diff options
Diffstat (limited to 'drivers')
220 files changed, 22418 insertions, 6671 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 80f0ec91e2c..59f33fa6af3 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -84,6 +84,8 @@ source "drivers/memstick/Kconfig" source "drivers/leds/Kconfig" +source "drivers/accessibility/Kconfig" + source "drivers/infiniband/Kconfig" source "drivers/edac/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index e5e394a7e6c..f65deda72d6 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_WATCHDOG) += watchdog/ obj-$(CONFIG_PHONE) += telephony/ obj-$(CONFIG_MD) += md/ obj-$(CONFIG_BT) += bluetooth/ +obj-$(CONFIG_ACCESSIBILITY) += accessibility/ obj-$(CONFIG_ISDN) += isdn/ obj-$(CONFIG_EDAC) += edac/ obj-$(CONFIG_MCA) += mca/ diff --git a/drivers/accessibility/Kconfig b/drivers/accessibility/Kconfig new file mode 100644 index 00000000000..1264c4b9809 --- /dev/null +++ b/drivers/accessibility/Kconfig @@ -0,0 +1,23 @@ +menuconfig ACCESSIBILITY + bool "Accessibility support" + ---help--- + Enable a submenu where accessibility items may be enabled. + + If unsure, say N. + +if ACCESSIBILITY +config A11Y_BRAILLE_CONSOLE + bool "Console on braille device" + depends on VT + depends on SERIAL_CORE_CONSOLE + ---help--- + Enables console output on a braille device connected to a 8250 + serial port. For now only the VisioBraille device is supported. + + To actually enable it, you need to pass option + console=brl,ttyS0 + to the kernel. Options are the same as for serial console. + + If unsure, say N. + +endif # ACCESSIBILITY diff --git a/drivers/accessibility/Makefile b/drivers/accessibility/Makefile new file mode 100644 index 00000000000..72b01a46546 --- /dev/null +++ b/drivers/accessibility/Makefile @@ -0,0 +1 @@ +obj-y += braille/ diff --git a/drivers/accessibility/braille/Makefile b/drivers/accessibility/braille/Makefile new file mode 100644 index 00000000000..2e9f16c9134 --- /dev/null +++ b/drivers/accessibility/braille/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_A11Y_BRAILLE_CONSOLE) += braille_console.o diff --git a/drivers/accessibility/braille/braille_console.c b/drivers/accessibility/braille/braille_console.c new file mode 100644 index 00000000000..0a5f6b2114c --- /dev/null +++ b/drivers/accessibility/braille/braille_console.c @@ -0,0 +1,397 @@ +/* + * Minimalistic braille device kernel support. + * + * By default, shows console messages on the braille device. + * Pressing Insert switches to VC browsing. + * + * Copyright (C) Samuel Thibault <samuel.thibault@ens-lyon.org> + * + * 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 the program ; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/autoconf.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/console.h> +#include <linux/notifier.h> + +#include <linux/selection.h> +#include <linux/vt_kern.h> +#include <linux/consolemap.h> + +#include <linux/keyboard.h> +#include <linux/kbd_kern.h> +#include <linux/input.h> + +MODULE_AUTHOR("samuel.thibault@ens-lyon.org"); +MODULE_DESCRIPTION("braille device"); +MODULE_LICENSE("GPL"); + +/* + * Braille device support part. + */ + +/* Emit various sounds */ +static int sound; +module_param(sound, bool, 0); +MODULE_PARM_DESC(sound, "emit sounds"); + +static void beep(unsigned int freq) +{ + if (sound) + kd_mksound(freq, HZ/10); +} + +/* mini console */ +#define WIDTH 40 +#define BRAILLE_KEY KEY_INSERT +static u16 console_buf[WIDTH]; +static int console_cursor; + +/* mini view of VC */ +static int vc_x, vc_y, lastvc_x, lastvc_y; + +/* show console ? (or show VC) */ +static int console_show = 1; +/* pending newline ? */ +static int console_newline = 1; +static int lastVC = -1; + +static struct console *braille_co; + +/* Very VisioBraille-specific */ +static void braille_write(u16 *buf) +{ + static u16 lastwrite[WIDTH]; + unsigned char data[1 + 1 + 2*WIDTH + 2 + 1], csum = 0, *c; + u16 out; + int i; + + if (!braille_co) + return; + + if (!memcmp(lastwrite, buf, WIDTH * sizeof(*buf))) + return; + memcpy(lastwrite, buf, WIDTH * sizeof(*buf)); + +#define SOH 1 +#define STX 2 +#define ETX 2 +#define EOT 4 +#define ENQ 5 + data[0] = STX; + data[1] = '>'; + csum ^= '>'; + c = &data[2]; + for (i = 0; i < WIDTH; i++) { + out = buf[i]; + if (out >= 0x100) + out = '?'; + else if (out == 0x00) + out = ' '; + csum ^= out; + if (out <= 0x05) { + *c++ = SOH; + out |= 0x40; + } + *c++ = out; + } + + if (csum <= 0x05) { + *c++ = SOH; + csum |= 0x40; + } + *c++ = csum; + *c++ = ETX; + + braille_co->write(braille_co, data, c - data); +} + +/* Follow the VC cursor*/ +static void vc_follow_cursor(struct vc_data *vc) +{ + vc_x = vc->vc_x - (vc->vc_x % WIDTH); + vc_y = vc->vc_y; + lastvc_x = vc->vc_x; + lastvc_y = vc->vc_y; +} + +/* Maybe the VC cursor moved, if so follow it */ +static void vc_maybe_cursor_moved(struct vc_data *vc) +{ + if (vc->vc_x != lastvc_x || vc->vc_y != lastvc_y) + vc_follow_cursor(vc); +} + +/* Show portion of VC at vc_x, vc_y */ +static void vc_refresh(struct vc_data *vc) +{ + u16 buf[WIDTH]; + int i; + + for (i = 0; i < WIDTH; i++) { + u16 glyph = screen_glyph(vc, + 2 * (vc_x + i) + vc_y * vc->vc_size_row); + buf[i] = inverse_translate(vc, glyph, 1); + } + braille_write(buf); +} + +/* + * Link to keyboard + */ + +static int keyboard_notifier_call(struct notifier_block *blk, + unsigned long code, void *_param) +{ + struct keyboard_notifier_param *param = _param; + struct vc_data *vc = param->vc; + int ret = NOTIFY_OK; + + if (!param->down) + return ret; + + switch (code) { + case KBD_KEYCODE: + if (console_show) { + if (param->value == BRAILLE_KEY) { + console_show = 0; + beep(880); + vc_maybe_cursor_moved(vc); + vc_refresh(vc); + ret = NOTIFY_STOP; + } + } else { + ret = NOTIFY_STOP; + switch (param->value) { + case KEY_INSERT: + beep(440); + console_show = 1; + lastVC = -1; + braille_write(console_buf); + break; + case KEY_LEFT: + if (vc_x > 0) { + vc_x -= WIDTH; + if (vc_x < 0) + vc_x = 0; + } else if (vc_y >= 1) { + beep(880); + vc_y--; + vc_x = vc->vc_cols-WIDTH; + } else + beep(220); + break; + case KEY_RIGHT: + if (vc_x + WIDTH < vc->vc_cols) { + vc_x += WIDTH; + } else if (vc_y + 1 < vc->vc_rows) { + beep(880); + vc_y++; + vc_x = 0; + } else + beep(220); + break; + case KEY_DOWN: + if (vc_y + 1 < vc->vc_rows) + vc_y++; + else + beep(220); + break; + case KEY_UP: + if (vc_y >= 1) + vc_y--; + else + beep(220); + break; + case KEY_HOME: + vc_follow_cursor(vc); + break; + case KEY_PAGEUP: + vc_x = 0; + vc_y = 0; + break; + case KEY_PAGEDOWN: + vc_x = 0; + vc_y = vc->vc_rows-1; + break; + default: + ret = NOTIFY_OK; + break; + } + if (ret == NOTIFY_STOP) + vc_refresh(vc); + } + break; + case KBD_POST_KEYSYM: + { + unsigned char type = KTYP(param->value) - 0xf0; + if (type == KT_SPEC) { + unsigned char val = KVAL(param->value); + int on_off = -1; + + switch (val) { + case KVAL(K_CAPS): + on_off = vc_kbd_led(kbd_table + fg_console, + VC_CAPSLOCK); + break; + case KVAL(K_NUM): + on_off = vc_kbd_led(kbd_table + fg_console, + VC_NUMLOCK); + break; + case KVAL(K_HOLD): + on_off = vc_kbd_led(kbd_table + fg_console, + VC_SCROLLOCK); + break; + } + if (on_off == 1) + beep(880); + else if (on_off == 0) + beep(440); + } + } + case KBD_UNBOUND_KEYCODE: + case KBD_UNICODE: + case KBD_KEYSYM: + /* Unused */ + break; + } + return ret; +} + +static struct notifier_block keyboard_notifier_block = { + .notifier_call = keyboard_notifier_call, +}; + +static int vt_notifier_call(struct notifier_block *blk, + unsigned long code, void *_param) +{ + struct vt_notifier_param *param = _param; + struct vc_data *vc = param->vc; + switch (code) { + case VT_ALLOCATE: + break; + case VT_DEALLOCATE: + break; + case VT_WRITE: + { + unsigned char c = param->c; + if (vc->vc_num != fg_console) + break; + switch (c) { + case '\b': + case 127: + if (console_cursor > 0) { + console_cursor--; + console_buf[console_cursor] = ' '; + } + break; + case '\n': + case '\v': + case '\f': + case '\r': + console_newline = 1; + break; + case '\t': + c = ' '; + /* Fallthrough */ + default: + if (c < 32) + /* Ignore other control sequences */ + break; + if (console_newline) { + memset(console_buf, 0, sizeof(console_buf)); + console_cursor = 0; + console_newline = 0; + } + if (console_cursor == WIDTH) + memmove(console_buf, &console_buf[1], + (WIDTH-1) * sizeof(*console_buf)); + else + console_cursor++; + console_buf[console_cursor-1] = c; + break; + } + if (console_show) + braille_write(console_buf); + else { + vc_maybe_cursor_moved(vc); + vc_refresh(vc); + } + break; + } + case VT_UPDATE: + /* Maybe a VT switch, flush */ + if (console_show) { + if (vc->vc_num != lastVC) { + lastVC = vc->vc_num; + memset(console_buf, 0, sizeof(console_buf)); + console_cursor = 0; + braille_write(console_buf); + } + } else { + vc_maybe_cursor_moved(vc); + vc_refresh(vc); + } + break; + } + return NOTIFY_OK; +} + +static struct notifier_block vt_notifier_block = { + .notifier_call = vt_notifier_call, +}; + +/* + * Called from printk.c when console=brl is given + */ + +int braille_register_console(struct console *console, int index, + char *console_options, char *braille_options) +{ + int ret; + if (!console_options) + /* Only support VisioBraille for now */ + console_options = "57600o8"; + if (braille_co) + return -ENODEV; + if (console->setup) { + ret = console->setup(console, console_options); + if (ret != 0) + return ret; + } + console->flags |= CON_ENABLED; + console->index = index; + braille_co = console; + return 0; +} + +int braille_unregister_console(struct console *console) +{ + if (braille_co != console) + return -EINVAL; + braille_co = NULL; + return 0; +} + +static int __init braille_init(void) +{ + register_keyboard_notifier(&keyboard_notifier_block); + register_vt_notifier(&vt_notifier_block); + return 0; +} + +console_initcall(braille_init); diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c index 5aa12b011a9..6adb72a2f87 100644 --- a/drivers/atm/ambassador.c +++ b/drivers/atm/ambassador.c @@ -33,6 +33,7 @@ #include <linux/interrupt.h> #include <linux/poison.h> #include <linux/bitrev.h> +#include <linux/mutex.h> #include <asm/atomic.h> #include <asm/io.h> @@ -1177,7 +1178,7 @@ static int amb_open (struct atm_vcc * atm_vcc) vcc->tx_frame_bits = tx_frame_bits; - down (&dev->vcc_sf); + mutex_lock(&dev->vcc_sf); if (dev->rxer[vci]) { // RXer on the channel already, just modify rate... cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE); @@ -1203,7 +1204,7 @@ static int amb_open (struct atm_vcc * atm_vcc) schedule(); } dev->txer[vci].tx_present = 1; - up (&dev->vcc_sf); + mutex_unlock(&dev->vcc_sf); } if (rxtp->traffic_class != ATM_NONE) { @@ -1211,7 +1212,7 @@ static int amb_open (struct atm_vcc * atm_vcc) vcc->rx_info.pool = pool; - down (&dev->vcc_sf); + mutex_lock(&dev->vcc_sf); /* grow RX buffer pool */ if (!dev->rxq[pool].buffers_wanted) dev->rxq[pool].buffers_wanted = rx_lats; @@ -1237,7 +1238,7 @@ static int amb_open (struct atm_vcc * atm_vcc) schedule(); // this link allows RX frames through dev->rxer[vci] = atm_vcc; - up (&dev->vcc_sf); + mutex_unlock(&dev->vcc_sf); } // indicate readiness @@ -1262,7 +1263,7 @@ static void amb_close (struct atm_vcc * atm_vcc) { if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) { command cmd; - down (&dev->vcc_sf); + mutex_lock(&dev->vcc_sf); if (dev->rxer[vci]) { // RXer still on the channel, just modify rate... XXX not really needed cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE); @@ -1277,7 +1278,7 @@ static void amb_close (struct atm_vcc * atm_vcc) { dev->txer[vci].tx_present = 0; while (command_do (dev, &cmd)) schedule(); - up (&dev->vcc_sf); + mutex_unlock(&dev->vcc_sf); } // disable RXing @@ -1287,7 +1288,7 @@ static void amb_close (struct atm_vcc * atm_vcc) { // this is (the?) one reason why we need the amb_vcc struct unsigned char pool = vcc->rx_info.pool; - down (&dev->vcc_sf); + mutex_lock(&dev->vcc_sf); if (dev->txer[vci].tx_present) { // TXer still on the channel, just go to pool zero XXX not really needed cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS); @@ -1314,7 +1315,7 @@ static void amb_close (struct atm_vcc * atm_vcc) { dev->rxq[pool].buffers_wanted = 0; drain_rx_pool (dev, pool); } - up (&dev->vcc_sf); + mutex_unlock(&dev->vcc_sf); } // free our structure @@ -2188,7 +2189,7 @@ static void setup_dev(amb_dev *dev, struct pci_dev *pci_dev) // semaphore for txer/rxer modifications - we cannot use a // spinlock as the critical region needs to switch processes - init_MUTEX (&dev->vcc_sf); + mutex_init(&dev->vcc_sf); // queue manipulation spinlocks; we want atomic reads and // writes to the queue descriptors (handles IRQ and SMP) // consider replacing "int pending" -> "atomic_t available" diff --git a/drivers/atm/ambassador.h b/drivers/atm/ambassador.h index ff2a303cbe0..df55fa8387d 100644 --- a/drivers/atm/ambassador.h +++ b/drivers/atm/ambassador.h @@ -638,7 +638,7 @@ struct amb_dev { amb_txq txq; amb_rxq rxq[NUM_RX_POOLS]; - struct semaphore vcc_sf; + struct mutex vcc_sf; amb_tx_info txer[NUM_VCS]; struct atm_vcc * rxer[NUM_VCS]; unsigned int tx_avail; diff --git a/drivers/base/node.c b/drivers/base/node.c index 12fde2d03d6..39f3d1b3a21 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -77,6 +77,7 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) "Node %d PageTables: %8lu kB\n" "Node %d NFS_Unstable: %8lu kB\n" "Node %d Bounce: %8lu kB\n" + "Node %d WritebackTmp: %8lu kB\n" "Node %d Slab: %8lu kB\n" "Node %d SReclaimable: %8lu kB\n" "Node %d SUnreclaim: %8lu kB\n", @@ -99,6 +100,7 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) nid, K(node_page_state(nid, NR_PAGETABLE)), nid, K(node_page_state(nid, NR_UNSTABLE_NFS)), nid, K(node_page_state(nid, NR_BOUNCE)), + nid, K(node_page_state(nid, NR_WRITEBACK_TEMP)), nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE) + node_page_state(nid, NR_SLAB_UNRECLAIMABLE)), nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE)), diff --git a/drivers/block/brd.c b/drivers/block/brd.c index e8e38faeafd..a196ef7f147 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -387,10 +387,14 @@ static struct block_device_operations brd_fops = { */ static int rd_nr; int rd_size = CONFIG_BLK_DEV_RAM_SIZE; +static int max_part; +static int part_shift; module_param(rd_nr, int, 0); MODULE_PARM_DESC(rd_nr, "Maximum number of brd devices"); module_param(rd_size, int, 0); MODULE_PARM_DESC(rd_size, "Size of each RAM disk in kbytes."); +module_param(max_part, int, 0); +MODULE_PARM_DESC(max_part, "Maximum number of partitions per RAM disk"); MODULE_LICENSE("GPL"); MODULE_ALIAS_BLOCKDEV_MAJOR(RAMDISK_MAJOR); @@ -435,11 +439,11 @@ static struct brd_device *brd_alloc(int i) blk_queue_max_sectors(brd->brd_queue, 1024); blk_queue_bounce_limit(brd->brd_queue, BLK_BOUNCE_ANY); - disk = brd->brd_disk = alloc_disk(1); + disk = brd->brd_disk = alloc_disk(1 << part_shift); if (!disk) goto out_free_queue; disk->major = RAMDISK_MAJOR; - disk->first_minor = i; + disk->first_minor = i << part_shift; disk->fops = &brd_fops; disk->private_data = brd; disk->queue = brd->brd_queue; @@ -523,7 +527,12 @@ static int __init brd_init(void) * themselves and have kernel automatically instantiate actual * device on-demand. */ - if (rd_nr > 1UL << MINORBITS) + + part_shift = 0; + if (max_part > 0) + part_shift = fls(max_part); + + if (rd_nr > 1UL << (MINORBITS - part_shift)) return -EINVAL; if (rd_nr) { @@ -531,7 +540,7 @@ static int __init brd_init(void) range = rd_nr; } else { nr = CONFIG_BLK_DEV_RAM_COUNT; - range = 1UL << MINORBITS; + range = 1UL << (MINORBITS - part_shift); } if (register_blkdev(RAMDISK_MAJOR, "ramdisk")) @@ -570,7 +579,7 @@ static void __exit brd_exit(void) unsigned long range; struct brd_device *brd, *next; - range = rd_nr ? rd_nr : 1UL << MINORBITS; + range = rd_nr ? rd_nr : 1UL << (MINORBITS - part_shift); list_for_each_entry_safe(brd, next, &brd_devices, brd_list) brd_del_one(brd); diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 7e31d5f1bc8..e5cd856a2fe 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -143,7 +143,7 @@ restart: int len; set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - len = tty->driver->write(tty, skb->data, skb->len); + len = tty->ops->write(tty, skb->data, skb->len); hdev->stat.byte_tx += len; skb_pull(skb, len); @@ -190,8 +190,7 @@ static int hci_uart_flush(struct hci_dev *hdev) /* Flush any pending characters in the driver and discipline. */ tty_ldisc_flush(tty); - if (tty->driver && tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty_driver_flush_buffer(tty); if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) hu->proto->flush(hu); @@ -285,9 +284,7 @@ static int hci_uart_tty_open(struct tty_struct *tty) if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); - - if (tty->driver && tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty_driver_flush_buffer(tty); return 0; } @@ -373,9 +370,7 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *f hu->hdev->stat.byte_rx += count; spin_unlock(&hu->rx_lock); - if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && - tty->driver->unthrottle) - tty->driver->unthrottle(tty); + tty_unthrottle(tty); } static int hci_uart_register_dev(struct hci_uart *hu) diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index c69f79598e4..99e6a406efb 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -35,7 +35,7 @@ //#define AGP_DEBUG 1 #ifdef AGP_DEBUG -#define DBG(x,y...) printk (KERN_DEBUG PFX "%s: " x "\n", __FUNCTION__ , ## y) +#define DBG(x,y...) printk (KERN_DEBUG PFX "%s: " x "\n", __func__ , ## y) #else #define DBG(x,y...) do { } while (0) #endif diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index 3d468f502d2..37457e5a4f2 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -832,33 +832,34 @@ static void change_speed(struct async_struct *info, local_irq_restore(flags); } -static void rs_put_char(struct tty_struct *tty, unsigned char ch) +static int rs_put_char(struct tty_struct *tty, unsigned char ch) { struct async_struct *info; unsigned long flags; if (!tty) - return; + return 0; info = tty->driver_data; if (serial_paranoia_check(info, tty->name, "rs_put_char")) - return; + return 0; if (!info->xmit.buf) - return; + return 0; local_irq_save(flags); if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) { local_irq_restore(flags); - return; + return 0; } info->xmit.buf[info->xmit.head++] = ch; info->xmit.head &= SERIAL_XMIT_SIZE-1; local_irq_restore(flags); + return 1; } static void rs_flush_chars(struct tty_struct *tty) @@ -1074,6 +1075,7 @@ static int get_serial_info(struct async_struct * info, if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); + lock_kernel(); tmp.type = state->type; tmp.line = state->line; tmp.port = state->port; @@ -1084,6 +1086,7 @@ static int get_serial_info(struct async_struct * info, tmp.close_delay = state->close_delay; tmp.closing_wait = state->closing_wait; tmp.custom_divisor = state->custom_divisor; + unlock_kernel(); if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; return 0; @@ -1099,13 +1102,17 @@ static int set_serial_info(struct async_struct * info, if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; + + lock_kernel(); state = info->state; old_state = *state; change_irq = new_serial.irq != state->irq; change_port = (new_serial.port != state->port); - if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size)) + if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size)) { + unlock_kernel(); return -EINVAL; + } if (!serial_isroot()) { if ((new_serial.baud_base != state->baud_base) || @@ -1122,8 +1129,10 @@ static int set_serial_info(struct async_struct * info, goto check_and_exit; } - if (new_serial.baud_base < 9600) + if (new_serial.baud_base < 9600) { + unlock_kernel(); return -EINVAL; + } /* * OK, past this point, all the error checking has been done. @@ -1157,6 +1166,7 @@ check_and_exit: } } else retval = startup(info); + unlock_kernel(); return retval; } @@ -1496,8 +1506,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) rs_wait_until_sent(tty, info->timeout); } shutdown(info); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + rs_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; @@ -1530,6 +1539,8 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) return; /* Just in case.... */ orig_jiffies = jiffies; + + lock_kernel(); /* * Set the check interval to be 1/5 of the estimated time to * send a single character, and make it at least 1. The check @@ -1570,6 +1581,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) break; } __set_current_state(TASK_RUNNING); + unlock_kernel(); #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); #endif diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index a7c4990b5b6..31d08b641f5 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -199,7 +199,7 @@ static int __init applicom_init(void) if (pci_enable_device(dev)) return -EIO; - RamIO = ioremap(pci_resource_start(dev, 0), LEN_RAM_IO); + RamIO = ioremap_nocache(pci_resource_start(dev, 0), LEN_RAM_IO); if (!RamIO) { printk(KERN_INFO "ac.o: Failed to ioremap PCI memory " @@ -254,7 +254,7 @@ static int __init applicom_init(void) /* Now try the specified ISA cards */ for (i = 0; i < MAX_ISA_BOARD; i++) { - RamIO = ioremap(mem + (LEN_RAM_IO * i), LEN_RAM_IO); + RamIO = ioremap_nocache(mem + (LEN_RAM_IO * i), LEN_RAM_IO); if (!RamIO) { printk(KERN_INFO "ac.o: Failed to ioremap the ISA card's memory space (slot #%d)\n", i + 1); diff --git a/drivers/char/consolemap.c b/drivers/char/consolemap.c index 6b104e45a32..4246b8e36cb 100644 --- a/drivers/char/consolemap.c +++ b/drivers/char/consolemap.c @@ -277,6 +277,7 @@ u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode) return p->inverse_translations[m][glyph]; } } +EXPORT_SYMBOL_GPL(inverse_translate); static void update_user_maps(void) { diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index e4f579c3e24..ef73e72daed 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -21,7 +21,6 @@ * * This version supports shared IRQ's (only for PCI boards). * - * $Log: cyclades.c,v $ * Prevent users from opening non-existing Z ports. * * Revision 2.3.2.8 2000/07/06 18:14:16 ivan @@ -62,7 +61,7 @@ * Driver now makes sure that the constant SERIAL_XMIT_SIZE is defined; * * Revision 2.3.2.2 1999/10/01 11:27:43 ivan - * Fixed bug in cyz_poll that would make all ports but port 0 + * Fixed bug in cyz_poll that would make all ports but port 0 * unable to transmit/receive data (Cyclades-Z only); * Implemented logic to prevent the RX buffer from being stuck with data * due to a driver / firmware race condition in interrupt op mode @@ -83,25 +82,25 @@ * Revision 2.3.1.1 1999/07/15 16:45:53 ivan * Removed CY_PROC conditional compilation; * Implemented SMP-awareness for the driver; - * Implemented a new ISA IRQ autoprobe that uses the irq_probe_[on|off] + * Implemented a new ISA IRQ autoprobe that uses the irq_probe_[on|off] * functions; * The driver now accepts memory addresses (maddr=0xMMMMM) and IRQs * (irq=NN) as parameters (only for ISA boards); - * Fixed bug in set_line_char that would prevent the Cyclades-Z + * Fixed bug in set_line_char that would prevent the Cyclades-Z * ports from being configured at speeds above 115.2Kbps; * Fixed bug in cy_set_termios that would prevent XON/XOFF flow control * switching from working properly; - * The driver now only prints IRQ info for the Cyclades-Z if it's + * The driver now only prints IRQ info for the Cyclades-Z if it's * configured to work in interrupt mode; * * Revision 2.2.2.3 1999/06/28 11:13:29 ivan * Added support for interrupt mode operation for the Z cards; * Removed the driver inactivity control for the Z; - * Added a missing MOD_DEC_USE_COUNT in the cy_open function for when + * Added a missing MOD_DEC_USE_COUNT in the cy_open function for when * the Z firmware is not loaded yet; - * Replaced the "manual" Z Tx flush buffer by a call to a FW command of + * Replaced the "manual" Z Tx flush buffer by a call to a FW command of * same functionality; - * Implemented workaround for IRQ setting loss on the PCI configuration + * Implemented workaround for IRQ setting loss on the PCI configuration * registers after a PCI bridge EEPROM reload (affects PLX9060 only); * * Revision 2.2.2.2 1999/05/14 17:18:15 ivan @@ -112,22 +111,22 @@ * BREAK implementation changed in order to make use of the 'break_ctl' * TTY facility; * Fixed typo in TTY structure field 'driver_name'; - * Included a PCI bridge reset and EEPROM reload in the board + * Included a PCI bridge reset and EEPROM reload in the board * initialization code (for both Y and Z series). * * Revision 2.2.2.1 1999/04/08 16:17:43 ivan - * Fixed a bug in cy_wait_until_sent that was preventing the port to be + * Fixed a bug in cy_wait_until_sent that was preventing the port to be * closed properly after a SIGINT; * Module usage counter scheme revisited; * Added support to the upcoming Y PCI boards (i.e., support to additional * PCI Device ID's). - * + * * Revision 2.2.1.10 1999/01/20 16:14:29 ivan * Removed all unnecessary page-alignement operations in ioremap calls * (ioremap is currently safe for these operations). * * Revision 2.2.1.9 1998/12/30 18:18:30 ivan - * Changed access to PLX PCI bridge registers from I/O to MMIO, in + * Changed access to PLX PCI bridge registers from I/O to MMIO, in * order to make PLX9050-based boards work with certain motherboards. * * Revision 2.2.1.8 1998/11/13 12:46:20 ivan @@ -148,7 +147,7 @@ * Fixed Cyclom-4Yo hardware detection bug. * * Revision 2.2.1.4 1998/08/04 11:02:50 ivan - * /proc/cyclades implementation with great collaboration of + * /proc/cyclades implementation with great collaboration of * Marc Lewis <marc@blarg.net>; * cyy_interrupt was changed to avoid occurrence of kernel oopses * during PPP operation. @@ -157,7 +156,7 @@ * General code review in order to comply with 2.1 kernel standards; * data loss prevention for slow devices revisited (cy_wait_until_sent * was created); - * removed conditional compilation for new/old PCI structure support + * removed conditional compilation for new/old PCI structure support * (now the driver only supports the new PCI structure). * * Revision 2.2.1.1 1998/03/19 16:43:12 ivan @@ -168,7 +167,7 @@ * cleaned up the data loss fix; * fixed XON/XOFF handling once more (Cyclades-Z); * general review of the driver routines; - * introduction of a mechanism to prevent data loss with slow + * introduction of a mechanism to prevent data loss with slow * printers, by forcing a delay before closing the port. * * Revision 2.1.1.2 1998/02/17 16:50:00 ivan @@ -182,12 +181,12 @@ * Code review for the module cleanup routine; * fixed RTS and DTR status report for new CD1400's in get_modem_info; * includes anonymous changes regarding signal_pending. - * + * * Revision 2.1 1997/11/01 17:42:41 ivan * Changes in the driver to support Alpha systems (except 8Zo V_1); * BREAK fix for the Cyclades-Z boards; * driver inactivity control by FW implemented; - * introduction of flag that allows driver to take advantage of + * introduction of flag that allows driver to take advantage of * a special CD1400 feature related to HW flow control; * added support for the CD1400 rev. J (Cyclom-Y boards); * introduction of ioctls to: @@ -196,17 +195,17 @@ * - adjust the polling interval (Cyclades-Z); * * Revision 1.36.4.33 1997/06/27 19:00:00 ivan - * Fixes related to kernel version conditional + * Fixes related to kernel version conditional * compilation. - * + * * Revision 1.36.4.32 1997/06/14 19:30:00 ivan - * Compatibility issues between kernels 2.0.x and + * Compatibility issues between kernels 2.0.x and * 2.1.x (mainly related to clear_bit function). - * + * * Revision 1.36.4.31 1997/06/03 15:30:00 ivan - * Changes to define the memory window according to the + * Changes to define the memory window according to the * board type. - * + * * Revision 1.36.4.30 1997/05/16 15:30:00 daniel * Changes to support new cycladesZ boards. * @@ -624,7 +623,7 @@ #undef CY_PCI_DEBUG /* - * Include section + * Include section */ #include <linux/module.h> #include <linux/errno.h> @@ -649,9 +648,9 @@ #include <linux/firmware.h> #include <asm/system.h> -#include <asm/io.h> +#include <linux/io.h> #include <asm/irq.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/kernel.h> #include <linux/pci.h> @@ -668,10 +667,10 @@ static void cy_send_xchar(struct tty_struct *tty, char ch); ((readl(&((struct RUNTIME_9060 __iomem *) \ ((card).ctl_addr))->init_ctrl) & (1<<17)) != 0) -#define ISZLOADED(card) (((ZO_V1==readl(&((struct RUNTIME_9060 __iomem *) \ +#define ISZLOADED(card) (((ZO_V1 == readl(&((struct RUNTIME_9060 __iomem *) \ ((card).ctl_addr))->mail_box_0)) || \ Z_FPGA_CHECK(card)) && \ - (ZFIRM_ID==readl(&((struct FIRM_ID __iomem *) \ + (ZFIRM_ID == readl(&((struct FIRM_ID __iomem *) \ ((card).base_addr+ID_ADDRESS))->signature))) #ifndef SERIAL_XMIT_SIZE @@ -809,12 +808,12 @@ static char baud_cor3[] = { /* receive threshold */ /* * 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 + * 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. */ @@ -841,14 +840,22 @@ static int cy_chip_offset[] = { 0x0000, #ifdef CONFIG_PCI static struct pci_device_id cy_pci_dev_id[] __devinitdata = { - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) }, /* PCI < 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Hi) }, /* PCI > 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Lo) }, /* 4Y PCI < 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Hi) }, /* 4Y PCI > 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Lo) }, /* 8Y PCI < 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Hi) }, /* 8Y PCI > 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Lo) }, /* Z PCI < 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Hi) }, /* Z PCI > 1Mb */ + /* PCI < 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) }, + /* PCI > 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Hi) }, + /* 4Y PCI < 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Lo) }, + /* 4Y PCI > 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Hi) }, + /* 8Y PCI < 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Lo) }, + /* 8Y PCI > 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Hi) }, + /* Z PCI < 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Lo) }, + /* Z PCI > 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Hi) }, { } /* end of table */ }; MODULE_DEVICE_TABLE(pci, cy_pci_dev_id); @@ -905,15 +912,14 @@ static inline int serial_paranoia_check(struct cyclades_port *info, This function is only called from inside spinlock-protected code. */ -static int cyy_issue_cmd(void __iomem * base_addr, u_char cmd, int index) +static int cyy_issue_cmd(void __iomem *base_addr, u_char cmd, int index) { unsigned int i; /* Check to see that the previous command has completed */ for (i = 0; i < 100; i++) { - if (readb(base_addr + (CyCCR << index)) == 0) { + if (readb(base_addr + (CyCCR << index)) == 0) break; - } udelay(10L); } /* if the CCR never cleared, the previous command @@ -929,7 +935,7 @@ static int cyy_issue_cmd(void __iomem * base_addr, u_char cmd, int index) #ifdef CONFIG_ISA /* ISA interrupt detection code */ -static unsigned detect_isa_irq(void __iomem * address) +static unsigned detect_isa_irq(void __iomem *address) { int irq; unsigned long irqs, flags; @@ -1038,7 +1044,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, if (info->flags & ASYNC_SAK) do_SAK(tty); } else if (data & CyFRAME) { - tty_insert_flip_char( tty, + tty_insert_flip_char(tty, readb(base_addr + (CyRDSR << index)), TTY_FRAME); info->icount.rx++; @@ -1320,7 +1326,8 @@ static irqreturn_t cyy_interrupt(int irq, void *dev_id) if (unlikely(cinfo == NULL)) { #ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n",irq); + printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n", + irq); #endif return IRQ_NONE; /* spurious interrupt */ } @@ -1375,12 +1382,12 @@ static irqreturn_t cyy_interrupt(int irq, void *dev_id) /***********************************************************/ /********* End of block of Cyclom-Y specific code **********/ -/******** Start of block of Cyclades-Z specific code *********/ +/******** Start of block of Cyclades-Z specific code *******/ /***********************************************************/ static int cyz_fetch_msg(struct cyclades_card *cinfo, - __u32 * channel, __u8 * cmd, __u32 * param) + __u32 *channel, __u8 *cmd, __u32 *param) { struct FIRM_ID __iomem *firm_id; struct ZFW_CTRL __iomem *zfw_ctrl; @@ -1388,9 +1395,8 @@ cyz_fetch_msg(struct cyclades_card *cinfo, unsigned long loc_doorbell; firm_id = cinfo->base_addr + ID_ADDRESS; - if (!ISZLOADED(*cinfo)) { + if (!ISZLOADED(*cinfo)) return -1; - } zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); board_ctrl = &zfw_ctrl->board_ctrl; @@ -1418,9 +1424,9 @@ cyz_issue_cmd(struct cyclades_card *cinfo, unsigned int index; firm_id = cinfo->base_addr + ID_ADDRESS; - if (!ISZLOADED(*cinfo)) { + if (!ISZLOADED(*cinfo)) return -1; - } + zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); board_ctrl = &zfw_ctrl->board_ctrl; @@ -1428,9 +1434,8 @@ cyz_issue_cmd(struct cyclades_card *cinfo, pci_doorbell = &((struct RUNTIME_9060 __iomem *)(cinfo->ctl_addr))->pci_doorbell; while ((readl(pci_doorbell) & 0xff) != 0) { - if (index++ == 1000) { + if (index++ == 1000) return (int)(readl(pci_doorbell) & 0xff); - } udelay(50L); } cy_writel(&board_ctrl->hcmd_channel, channel); @@ -1504,7 +1509,8 @@ static void cyz_handle_rx(struct cyclades_port *info, while (len--) { data = readb(cinfo->base_addr + rx_bufaddr + new_rx_get); - new_rx_get = (new_rx_get + 1)& (rx_bufsize - 1); + new_rx_get = (new_rx_get + 1) & + (rx_bufsize - 1); tty_insert_flip_char(tty, data, TTY_NORMAL); info->idle_stats.recv_bytes++; info->icount.rx++; @@ -1636,7 +1642,8 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) special_count = 0; delta_count = 0; info = &cinfo->ports[channel]; - if ((tty = info->tty) == NULL) + tty = info->tty; + if (tty == NULL) continue; ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); @@ -1732,7 +1739,8 @@ static irqreturn_t cyz_interrupt(int irq, void *dev_id) if (unlikely(cinfo == NULL)) { #ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyz_interrupt: spurious interrupt %d\n",irq); + printk(KERN_DEBUG "cyz_interrupt: spurious interrupt %d\n", + irq); #endif return IRQ_NONE; /* spurious interrupt */ } @@ -1851,9 +1859,8 @@ static int startup(struct cyclades_port *info) } if (!info->type) { - if (info->tty) { + if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); - } free_page(page); goto errout; } @@ -1904,9 +1911,8 @@ static int startup(struct cyclades_port *info) readb(base_addr + (CySRER << index)) | CyRxData); info->flags |= ASYNC_INITIALIZED; - if (info->tty) { + if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); - } info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; info->breakon = info->breakoff = 0; memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); @@ -1925,9 +1931,8 @@ static int startup(struct cyclades_port *info) base_addr = card->base_addr; firm_id = base_addr + ID_ADDRESS; - if (!ISZLOADED(*card)) { + if (!ISZLOADED(*card)) return -ENODEV; - } zfw_ctrl = card->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); @@ -1990,9 +1995,8 @@ static int startup(struct cyclades_port *info) /* enable send, recv, modem !!! */ info->flags |= ASYNC_INITIALIZED; - if (info->tty) { + if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); - } info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; info->breakon = info->breakoff = 0; memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); @@ -2061,9 +2065,8 @@ static void shutdown(struct cyclades_port *info) void __iomem *base_addr; int chip, channel, index; - if (!(info->flags & ASYNC_INITIALIZED)) { + if (!(info->flags & ASYNC_INITIALIZED)) return; - } card = info->card; channel = info->line - card->first_line; @@ -2105,9 +2108,8 @@ static void shutdown(struct cyclades_port *info) /* it may be appropriate to clear _XMIT at some later date (after testing)!!! */ - if (info->tty) { + if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); - } info->flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); } else { @@ -2124,9 +2126,8 @@ static void shutdown(struct cyclades_port *info) #endif firm_id = base_addr + ID_ADDRESS; - if (!ISZLOADED(*card)) { + if (!ISZLOADED(*card)) return; - } zfw_ctrl = card->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); @@ -2157,9 +2158,8 @@ static void shutdown(struct cyclades_port *info) #endif } - if (info->tty) { + if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); - } info->flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); @@ -2204,7 +2204,8 @@ block_til_ready(struct tty_struct *tty, struct file *filp, * If non-blocking mode is set, then make the check up front * and then exit. */ - if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { info->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -2301,7 +2302,8 @@ block_til_ready(struct tty_struct *tty, struct file *filp, return -EINVAL; } - zfw_ctrl = base_addr + (readl(&firm_id->zfwctrl_addr)& 0xfffff); + zfw_ctrl = base_addr + (readl(&firm_id->zfwctrl_addr) + & 0xfffff); board_ctrl = &zfw_ctrl->board_ctrl; ch_ctrl = zfw_ctrl->ch_ctrl; @@ -2378,9 +2380,9 @@ static int cy_open(struct tty_struct *tty, struct file *filp) int retval; line = tty->index; - if ((tty->index < 0) || (NR_PORTS <= line)) { + if (tty->index < 0 || NR_PORTS <= line) return -ENODEV; - } + for (i = 0; i < NR_CARDS; i++) if (line < cy_card[i].first_line + cy_card[i].nports && line >= cy_card[i].first_line) @@ -2388,9 +2390,8 @@ static int cy_open(struct tty_struct *tty, struct file *filp) if (i >= NR_CARDS) return -ENODEV; info = &cy_card[i].ports[line - cy_card[i].first_line]; - if (info->line < 0) { + if (info->line < 0) return -ENODEV; - } /* If the card's firmware hasn't been loaded, treat it as absent from the system. This @@ -2456,9 +2457,9 @@ static int cy_open(struct tty_struct *tty, struct file *filp) #endif tty->driver_data = info; info->tty = tty; - if (serial_paranoia_check(info, tty->name, "cy_open")) { + if (serial_paranoia_check(info, tty->name, "cy_open")) return -ENODEV; - } + #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc:cy_open ttyC%d, count = %d\n", info->line, info->count); @@ -2482,9 +2483,8 @@ static int cy_open(struct tty_struct *tty, struct file *filp) * Start up serial port */ retval = startup(info); - if (retval) { + if (retval) return retval; - } retval = block_til_ready(tty, filp, info); if (retval) { @@ -2522,6 +2522,7 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) return; /* Just in case.... */ orig_jiffies = jiffies; + lock_kernel(); /* * Set the check interval to be 1/5 of the estimated time to * send a single character, and make it at least 1. The check @@ -2573,11 +2574,47 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) } /* Run one more char cycle */ msleep_interruptible(jiffies_to_msecs(char_time * 5)); + unlock_kernel(); #ifdef CY_DEBUG_WAIT_UNTIL_SENT printk(KERN_DEBUG "Clean (jiff=%lu)...done\n", jiffies); #endif } +static void cy_flush_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + struct cyclades_card *card; + int channel, retval; + unsigned long flags; + +#ifdef CY_DEBUG_IO + printk(KERN_DEBUG "cyc:cy_flush_buffer ttyC%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_flush_buffer")) + return; + + card = info->card; + channel = info->line - card->first_line; + + spin_lock_irqsave(&card->card_lock, flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + spin_unlock_irqrestore(&card->card_lock, flags); + + if (IS_CYC_Z(*card)) { /* If it is a Z card, flush the on-board + buffers as well */ + spin_lock_irqsave(&card->card_lock, flags); + retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_TX, 0L); + if (retval != 0) { + printk(KERN_ERR "cyc: flush_buffer retval on ttyC%d " + "was %x\n", info->line, retval); + } + spin_unlock_irqrestore(&card->card_lock, flags); + } + tty_wakeup(tty); +} /* cy_flush_buffer */ + + /* * This routine is called when a particular tty device is closed. */ @@ -2591,9 +2628,8 @@ static void cy_close(struct tty_struct *tty, struct file *filp) printk(KERN_DEBUG "cyc:cy_close ttyC%d\n", info->line); #endif - if (!info || serial_paranoia_check(info, tty->name, "cy_close")) { + if (!info || serial_paranoia_check(info, tty->name, "cy_close")) return; - } card = info->card; @@ -2641,9 +2677,9 @@ static void cy_close(struct tty_struct *tty, struct file *filp) */ tty->closing = 1; spin_unlock_irqrestore(&card->card_lock, flags); - if (info->closing_wait != CY_CLOSING_WAIT_NONE) { + if (info->closing_wait != CY_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, info->closing_wait); - } + spin_lock_irqsave(&card->card_lock, flags); if (!IS_CYC_Z(*card)) { @@ -2657,15 +2693,16 @@ static void cy_close(struct tty_struct *tty, struct file *filp) cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyRxData); if (info->flags & ASYNC_INITIALIZED) { - /* Waiting for on-board buffers to be empty before closing - the port */ + /* Waiting for on-board buffers to be empty before + closing the port */ spin_unlock_irqrestore(&card->card_lock, flags); cy_wait_until_sent(tty, info->timeout); spin_lock_irqsave(&card->card_lock, flags); } } else { #ifdef Z_WAKE - /* Waiting for on-board buffers to be empty before closing the port */ + /* Waiting for on-board buffers to be empty before closing + the port */ void __iomem *base_addr = card->base_addr; struct FIRM_ID __iomem *firm_id = base_addr + ID_ADDRESS; struct ZFW_CTRL __iomem *zfw_ctrl = @@ -2689,8 +2726,7 @@ static void cy_close(struct tty_struct *tty, struct file *filp) spin_unlock_irqrestore(&card->card_lock, flags); shutdown(info); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + cy_flush_buffer(tty); tty_ldisc_flush(tty); spin_lock_irqsave(&card->card_lock, flags); @@ -2738,17 +2774,16 @@ static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) printk(KERN_DEBUG "cyc:cy_write ttyC%d\n", info->line); #endif - if (serial_paranoia_check(info, tty->name, "cy_write")) { + if (serial_paranoia_check(info, tty->name, "cy_write")) return 0; - } if (!info->xmit_buf) return 0; spin_lock_irqsave(&info->card->card_lock, flags); while (1) { - c = min(count, min((int)(SERIAL_XMIT_SIZE - info->xmit_cnt - 1), - (int)(SERIAL_XMIT_SIZE - info->xmit_head))); + c = min(count, (int)(SERIAL_XMIT_SIZE - info->xmit_cnt - 1)); + c = min(c, (int)(SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) break; @@ -2766,9 +2801,9 @@ static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) info->idle_stats.xmit_bytes += ret; info->idle_stats.xmit_idle = jiffies; - if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) start_xmit(info); - } + return ret; } /* cy_write */ @@ -2779,7 +2814,7 @@ static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) * done stuffing characters into the driver. If there is no room * in the queue, the character is ignored. */ -static void cy_put_char(struct tty_struct *tty, unsigned char ch) +static int cy_put_char(struct tty_struct *tty, unsigned char ch) { struct cyclades_port *info = tty->driver_data; unsigned long flags; @@ -2789,15 +2824,15 @@ static void cy_put_char(struct tty_struct *tty, unsigned char ch) #endif if (serial_paranoia_check(info, tty->name, "cy_put_char")) - return; + return 0; if (!info->xmit_buf) - return; + return 0; spin_lock_irqsave(&info->card->card_lock, flags); if (info->xmit_cnt >= (int)(SERIAL_XMIT_SIZE - 1)) { spin_unlock_irqrestore(&info->card->card_lock, flags); - return; + return 0; } info->xmit_buf[info->xmit_head++] = ch; @@ -2806,11 +2841,12 @@ static void cy_put_char(struct tty_struct *tty, unsigned char ch) info->idle_stats.xmit_bytes++; info->idle_stats.xmit_idle = jiffies; spin_unlock_irqrestore(&info->card->card_lock, flags); + return 1; } /* cy_put_char */ /* * This routine is called by the kernel after it has written a - * series of characters to the tty device using put_char(). + * series of characters to the tty device using put_char(). */ static void cy_flush_chars(struct tty_struct *tty) { @@ -2882,6 +2918,7 @@ static int cy_chars_in_buffer(struct tty_struct *tty) int char_count; __u32 tx_put, tx_get, tx_bufsize; + lock_kernel(); firm_id = card->base_addr + ID_ADDRESS; zfw_ctrl = card->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); @@ -2899,6 +2936,7 @@ static int cy_chars_in_buffer(struct tty_struct *tty) printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n", info->line, info->xmit_cnt + char_count); #endif + unlock_kernel(); return info->xmit_cnt + char_count; } #endif /* Z_EXT_CHARS_IN_BUFFER */ @@ -2950,12 +2988,12 @@ static void set_line_char(struct cyclades_port *info) int baud, baud_rate = 0; int i; - if (!info->tty || !info->tty->termios) { + if (!info->tty || !info->tty->termios) return; - } - if (info->line == -1) { + + if (info->line == -1) return; - } + cflag = info->tty->termios->c_cflag; iflag = info->tty->termios->c_iflag; @@ -2994,13 +3032,11 @@ static void set_line_char(struct cyclades_port *info) } /* find the baud index */ for (i = 0; i < 20; i++) { - if (baud == baud_table[i]) { + if (baud == baud_table[i]) break; - } } - if (i == 20) { + if (i == 20) i = 19; /* CD1400_MAX_SPEED */ - } if (baud == 38400 && (info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { @@ -3059,18 +3095,16 @@ static void set_line_char(struct cyclades_port *info) info->cor1 = Cy_8_BITS; break; } - if (cflag & CSTOPB) { + if (cflag & CSTOPB) info->cor1 |= Cy_2_STOP; - } + if (cflag & PARENB) { - if (cflag & PARODD) { + if (cflag & PARODD) info->cor1 |= CyPARITY_O; - } else { + else info->cor1 |= CyPARITY_E; - } - } else { + } else info->cor1 |= CyPARITY_NONE; - } /* CTS flow control flag */ if (cflag & CRTSCTS) { @@ -3123,7 +3157,8 @@ static void set_line_char(struct cyclades_port *info) cyy_issue_cmd(base_addr, CyCOR_CHANGE | CyCOR1ch | CyCOR2ch | CyCOR3ch, index); - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); /* !!! Is this needed? */ + /* !!! Is this needed? */ + cy_writeb(base_addr + (CyCAR << index), (u_char) channel); cy_writeb(base_addr + (CyRTPR << index), (info->default_timeout ? info->default_timeout : 0x02)); /* 10ms rx timeout */ @@ -3191,9 +3226,8 @@ static void set_line_char(struct cyclades_port *info) #endif } - if (info->tty) { + if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); - } spin_unlock_irqrestore(&card->card_lock, flags); } else { @@ -3206,9 +3240,8 @@ static void set_line_char(struct cyclades_port *info) int retval; firm_id = card->base_addr + ID_ADDRESS; - if (!ISZLOADED(*card)) { + if (!ISZLOADED(*card)) return; - } zfw_ctrl = card->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); @@ -3268,14 +3301,12 @@ static void set_line_char(struct cyclades_port *info) readl(&ch_ctrl->comm_data_l) | C_DL_1STOP); } if (cflag & PARENB) { - if (cflag & PARODD) { + if (cflag & PARODD) cy_writel(&ch_ctrl->comm_parity, C_PR_ODD); - } else { + else cy_writel(&ch_ctrl->comm_parity, C_PR_EVEN); - } - } else { + } else cy_writel(&ch_ctrl->comm_parity, C_PR_NONE); - } /* CTS flow control flag */ if (cflag & CRTSCTS) { @@ -3305,11 +3336,10 @@ static void set_line_char(struct cyclades_port *info) } /* CD sensitivity */ - if (cflag & CLOCAL) { + if (cflag & CLOCAL) info->flags &= ~ASYNC_CHECK_CD; - } else { + else info->flags |= ASYNC_CHECK_CD; - } if (baud == 0) { /* baud rate is zero, turn off line */ cy_writel(&ch_ctrl->rs_control, @@ -3325,21 +3355,20 @@ static void set_line_char(struct cyclades_port *info) #endif } - retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM,0L); + retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); if (retval != 0) { printk(KERN_ERR "cyc:set_line_char(2) retval on ttyC%d " "was %x\n", info->line, retval); } - if (info->tty) { + if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); - } } } /* set_line_char */ static int get_serial_info(struct cyclades_port *info, - struct serial_struct __user * retinfo) + struct serial_struct __user *retinfo) { struct serial_struct tmp; struct cyclades_card *cinfo = info->card; @@ -3363,7 +3392,7 @@ get_serial_info(struct cyclades_port *info, static int set_serial_info(struct cyclades_port *info, - struct serial_struct __user * new_info) + struct serial_struct __user *new_info) { struct serial_struct new_serial; struct cyclades_port old_info; @@ -3417,7 +3446,7 @@ check_and_exit: * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space. */ -static int get_lsr_info(struct cyclades_port *info, unsigned int __user * value) +static int get_lsr_info(struct cyclades_port *info, unsigned int __user *value) { struct cyclades_card *card; int chip, channel, index; @@ -3461,9 +3490,11 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file) struct BOARD_CTRL __iomem *board_ctrl; struct CH_CTRL __iomem *ch_ctrl; - if (serial_paranoia_check(info, tty->name, __FUNCTION__)) + if (serial_paranoia_check(info, tty->name, __func__)) return -ENODEV; + lock_kernel(); + card = info->card; channel = info->line - card->first_line; if (!IS_CYC_Z(*card)) { @@ -3506,10 +3537,12 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file) ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); } else { result = 0; + unlock_kernel(); return -ENODEV; } } + unlock_kernel(); return result; } /* cy_tiomget */ @@ -3528,7 +3561,7 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, struct CH_CTRL __iomem *ch_ctrl; int retval; - if (serial_paranoia_check(info, tty->name, __FUNCTION__)) + if (serial_paranoia_check(info, tty->name, __func__)) return -ENODEV; card = info->card; @@ -3727,8 +3760,8 @@ static void cy_break(struct tty_struct *tty, int break_state) spin_unlock_irqrestore(&card->card_lock, flags); } /* cy_break */ -static int -get_mon_info(struct cyclades_port *info, struct cyclades_monitor __user * mon) +static int get_mon_info(struct cyclades_port *info, + struct cyclades_monitor __user *mon) { if (copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor))) @@ -3767,8 +3800,8 @@ static int set_threshold(struct cyclades_port *info, unsigned long value) return 0; } /* set_threshold */ -static int -get_threshold(struct cyclades_port *info, unsigned long __user * value) +static int get_threshold(struct cyclades_port *info, + unsigned long __user *value) { struct cyclades_card *card; void __iomem *base_addr; @@ -3789,15 +3822,15 @@ get_threshold(struct cyclades_port *info, unsigned long __user * value) return 0; } /* get_threshold */ -static int -set_default_threshold(struct cyclades_port *info, unsigned long value) +static int set_default_threshold(struct cyclades_port *info, + unsigned long value) { info->default_threshold = value & 0x0f; return 0; } /* set_default_threshold */ -static int -get_default_threshold(struct cyclades_port *info, unsigned long __user * value) +static int get_default_threshold(struct cyclades_port *info, + unsigned long __user *value) { return put_user(info->default_threshold, value); } /* get_default_threshold */ @@ -3824,7 +3857,8 @@ static int set_timeout(struct cyclades_port *info, unsigned long value) return 0; } /* set_timeout */ -static int get_timeout(struct cyclades_port *info, unsigned long __user * value) +static int get_timeout(struct cyclades_port *info, + unsigned long __user *value) { struct cyclades_card *card; void __iomem *base_addr; @@ -3851,8 +3885,8 @@ static int set_default_timeout(struct cyclades_port *info, unsigned long value) return 0; } /* set_default_timeout */ -static int -get_default_timeout(struct cyclades_port *info, unsigned long __user * value) +static int get_default_timeout(struct cyclades_port *info, + unsigned long __user *value) { return put_user(info->default_timeout, value); } /* get_default_timeout */ @@ -3880,6 +3914,7 @@ cy_ioctl(struct tty_struct *tty, struct file *file, printk(KERN_DEBUG "cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n", info->line, cmd, arg); #endif + lock_kernel(); switch (cmd) { case CYGETMON: @@ -3936,7 +3971,7 @@ cy_ioctl(struct tty_struct *tty, struct file *file, break; #endif /* CONFIG_CYZ_INTR */ case CYSETWAIT: - info->closing_wait = (unsigned short)arg *HZ / 100; + info->closing_wait = (unsigned short)arg * HZ / 100; ret_val = 0; break; case CYGETWAIT: @@ -3988,47 +4023,47 @@ cy_ioctl(struct tty_struct *tty, struct file *file, p_cuser = argp; ret_val = put_user(cnow.cts, &p_cuser->cts); if (ret_val) - return ret_val; + break; ret_val = put_user(cnow.dsr, &p_cuser->dsr); if (ret_val) - return ret_val; + break; ret_val = put_user(cnow.rng, &p_cuser->rng); if (ret_val) - return ret_val; + break; ret_val = put_user(cnow.dcd, &p_cuser->dcd); if (ret_val) - return ret_val; + break; ret_val = put_user(cnow.rx, &p_cuser->rx); if (ret_val) - return ret_val; + break; ret_val = put_user(cnow.tx, &p_cuser->tx); if (ret_val) - return ret_val; + break; ret_val = put_user(cnow.frame, &p_cuser->frame); if (ret_val) - return ret_val; + break; ret_val = put_user(cnow.overrun, &p_cuser->overrun); if (ret_val) - return ret_val; + break; ret_val = put_user(cnow.parity, &p_cuser->parity); if (ret_val) - return ret_val; + break; ret_val = put_user(cnow.brk, &p_cuser->brk); if (ret_val) - return ret_val; + break; ret_val = put_user(cnow.buf_overrun, &p_cuser->buf_overrun); if (ret_val) - return ret_val; + break; ret_val = 0; break; default: ret_val = -ENOIOCTLCMD; } + unlock_kernel(); #ifdef CY_DEBUG_OTHER printk(KERN_DEBUG "cyc:cy_ioctl done\n"); #endif - return ret_val; } /* cy_ioctl */ @@ -4113,9 +4148,8 @@ static void cy_throttle(struct tty_struct *tty) tty->ldisc.chars_in_buffer(tty), info->line); #endif - if (serial_paranoia_check(info, tty->name, "cy_throttle")) { + if (serial_paranoia_check(info, tty->name, "cy_throttle")) return; - } card = info->card; @@ -4169,12 +4203,11 @@ static void cy_unthrottle(struct tty_struct *tty) char buf[64]; printk(KERN_DEBUG "cyc:unthrottle %s: %ld...ttyC%d\n", - tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty),info->line); + tty_name(tty, buf), tty_chars_in_buffer(tty), info->line); #endif - if (serial_paranoia_check(info, tty->name, "cy_unthrottle")) { + if (serial_paranoia_check(info, tty->name, "cy_unthrottle")) return; - } if (I_IXOFF(tty)) { if (info->x_char) @@ -4269,47 +4302,14 @@ static void cy_start(struct tty_struct *tty) base_addr = cinfo->base_addr + (cy_chip_offset[chip] << index); spin_lock_irqsave(&cinfo->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), (u_char) (channel & 0x0003)); /* index channel */ + cy_writeb(base_addr + (CyCAR << index), + (u_char) (channel & 0x0003)); /* index channel */ cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyTxRdy); spin_unlock_irqrestore(&cinfo->card_lock, flags); } } /* cy_start */ -static void cy_flush_buffer(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - int channel, retval; - unsigned long flags; - -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_flush_buffer ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_flush_buffer")) - return; - - card = info->card; - channel = info->line - card->first_line; - - spin_lock_irqsave(&card->card_lock, flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - spin_unlock_irqrestore(&card->card_lock, flags); - - if (IS_CYC_Z(*card)) { /* If it is a Z card, flush the on-board - buffers as well */ - spin_lock_irqsave(&card->card_lock, flags); - retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_TX, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc: flush_buffer retval on ttyC%d " - "was %x\n", info->line, retval); - } - spin_unlock_irqrestore(&card->card_lock, flags); - } - tty_wakeup(tty); -} /* cy_flush_buffer */ - /* * cy_hangup() --- called by tty_hangup() when a hangup is signaled. */ @@ -4406,10 +4406,11 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) info->cor3 = 0x08; /* _very_ small rcv threshold */ chip_number = (port - cinfo->first_line) / 4; - if ((info->chip_rev = readb(cinfo->base_addr + - (cy_chip_offset[chip_number] << - index) + (CyGFRCR << index))) >= - CD1400_REV_J) { + info->chip_rev = readb(cinfo->base_addr + + (cy_chip_offset[chip_number] << index) + + (CyGFRCR << index)); + + if (info->chip_rev >= CD1400_REV_J) { /* It is a CD1400 rev. J or later */ info->tbpr = baud_bpr_60[13]; /* Tx BPR */ info->tco = baud_co_60[13]; /* Tx CO */ @@ -4454,7 +4455,8 @@ static unsigned short __devinit cyy_init_card(void __iomem *true_base_addr, /* Cy_ClrIntr is 0x1800 */ udelay(500L); - for (chip_number = 0; chip_number < CyMAX_CHIPS_PER_CARD; chip_number++) { + for (chip_number = 0; chip_number < CyMAX_CHIPS_PER_CARD; + chip_number++) { base_addr = true_base_addr + (cy_chip_offset[chip_number] << index); mdelay(1); @@ -4555,12 +4557,11 @@ static int __init cy_detect_isa(void) /* scan the address table probing for Cyclom-Y/ISA boards */ for (i = 0; i < NR_ISA_ADDRS; i++) { unsigned int isa_address = cy_isa_addresses[i]; - if (isa_address == 0x0000) { + if (isa_address == 0x0000) return nboard; - } /* probe for CD1400... */ - cy_isa_address = ioremap(isa_address, CyISA_Ywin); + cy_isa_address = ioremap_nocache(isa_address, CyISA_Ywin); if (cy_isa_address == NULL) { printk(KERN_ERR "Cyclom-Y/ISA: can't remap base " "address\n"); @@ -4847,12 +4848,10 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr, if (mailbox != 0) { /* set window to last 512K of RAM */ cy_writel(&ctl_addr->loc_addr_base, WIN_RAM + RAM_SIZE); - //sleep(1); for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++) cy_writeb(tmp, 255); /* set window to beginning of RAM */ cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); - //sleep(1); } retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, base_addr, NULL); @@ -5382,7 +5381,8 @@ static void __exit cy_cleanup_module(void) del_timer_sync(&cyz_timerlist); #endif /* CONFIG_CYZ_INTR */ - if ((e1 = tty_unregister_driver(cy_serial_driver))) + e1 = tty_unregister_driver(cy_serial_driver); + if (e1) printk(KERN_ERR "failed to unregister Cyclades serial " "driver(%d)\n", e1); diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index ecee3547a13..213b3ca3468 100644 --- a/drivers/char/drm/drmP.h +++ b/drivers/char/drm/drmP.h @@ -160,7 +160,7 @@ struct drm_device; * \param arg arguments */ #define DRM_ERROR(fmt, arg...) \ - printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* " fmt , __FUNCTION__ , ##arg) + printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* " fmt , __func__ , ##arg) /** * Memory error output. @@ -170,7 +170,7 @@ struct drm_device; * \param arg arguments */ #define DRM_MEM_ERROR(area, fmt, arg...) \ - printk(KERN_ERR "[" DRM_NAME ":%s:%s] *ERROR* " fmt , __FUNCTION__, \ + printk(KERN_ERR "[" DRM_NAME ":%s:%s] *ERROR* " fmt , __func__, \ drm_mem_stats[area].name , ##arg) #define DRM_INFO(fmt, arg...) printk(KERN_INFO "[" DRM_NAME "] " fmt , ##arg) @@ -187,7 +187,7 @@ struct drm_device; if ( drm_debug ) \ printk(KERN_DEBUG \ "[" DRM_NAME ":%s] " fmt , \ - __FUNCTION__ , ##arg); \ + __func__ , ##arg); \ } while (0) #else #define DRM_DEBUG(fmt, arg...) do { } while (0) @@ -238,7 +238,7 @@ do { \ if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || \ dev->lock.file_priv != file_priv ) { \ DRM_ERROR( "%s called without lock held, held %d owner %p %p\n",\ - __FUNCTION__, _DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ),\ + __func__, _DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ),\ dev->lock.file_priv, file_priv ); \ return -EINVAL; \ } \ diff --git a/drivers/char/drm/drm_sysfs.c b/drivers/char/drm/drm_sysfs.c index 7a1d9a782dd..9a32169e88f 100644 --- a/drivers/char/drm/drm_sysfs.c +++ b/drivers/char/drm/drm_sysfs.c @@ -34,7 +34,7 @@ static int drm_sysfs_suspend(struct device *dev, pm_message_t state) struct drm_minor *drm_minor = to_drm_minor(dev); struct drm_device *drm_dev = drm_minor->dev; - printk(KERN_ERR "%s\n", __FUNCTION__); + printk(KERN_ERR "%s\n", __func__); if (drm_dev->driver->suspend) return drm_dev->driver->suspend(drm_dev, state); diff --git a/drivers/char/drm/i830_dma.c b/drivers/char/drm/i830_dma.c index 60c9376be48..a86ab30b462 100644 --- a/drivers/char/drm/i830_dma.c +++ b/drivers/char/drm/i830_dma.c @@ -692,7 +692,7 @@ static void i830EmitState(struct drm_device * dev) drm_i830_sarea_t *sarea_priv = dev_priv->sarea_priv; unsigned int dirty = sarea_priv->dirty; - DRM_DEBUG("%s %x\n", __FUNCTION__, dirty); + DRM_DEBUG("%s %x\n", __func__, dirty); if (dirty & I830_UPLOAD_BUFFERS) { i830EmitDestVerified(dev, sarea_priv->BufferState); @@ -1043,7 +1043,7 @@ static void i830_dma_dispatch_flip(struct drm_device * dev) RING_LOCALS; DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n", - __FUNCTION__, + __func__, dev_priv->current_page, dev_priv->sarea_priv->pf_current_page); @@ -1206,7 +1206,7 @@ static void i830_dma_quiescent(struct drm_device * dev) OUT_RING(0); ADVANCE_LP_RING(); - i830_wait_ring(dev, dev_priv->ring.Size - 8, __FUNCTION__); + i830_wait_ring(dev, dev_priv->ring.Size - 8, __func__); } static int i830_flush_queue(struct drm_device * dev) @@ -1223,7 +1223,7 @@ static int i830_flush_queue(struct drm_device * dev) OUT_RING(0); ADVANCE_LP_RING(); - i830_wait_ring(dev, dev_priv->ring.Size - 8, __FUNCTION__); + i830_wait_ring(dev, dev_priv->ring.Size - 8, __func__); for (i = 0; i < dma->buf_count; i++) { struct drm_buf *buf = dma->buflist[i]; @@ -1344,7 +1344,7 @@ static void i830_do_init_pageflip(struct drm_device * dev) { drm_i830_private_t *dev_priv = dev->dev_private; - DRM_DEBUG("%s\n", __FUNCTION__); + DRM_DEBUG("%s\n", __func__); dev_priv->page_flipping = 1; dev_priv->current_page = 0; dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; @@ -1354,7 +1354,7 @@ static int i830_do_cleanup_pageflip(struct drm_device * dev) { drm_i830_private_t *dev_priv = dev->dev_private; - DRM_DEBUG("%s\n", __FUNCTION__); + DRM_DEBUG("%s\n", __func__); if (dev_priv->current_page != 0) i830_dma_dispatch_flip(dev); @@ -1367,7 +1367,7 @@ static int i830_flip_bufs(struct drm_device *dev, void *data, { drm_i830_private_t *dev_priv = dev->dev_private; - DRM_DEBUG("%s\n", __FUNCTION__); + DRM_DEBUG("%s\n", __func__); LOCK_TEST_WITH_RETURN(dev, file_priv); @@ -1437,7 +1437,7 @@ static int i830_getparam(struct drm_device *dev, void *data, int value; if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + DRM_ERROR("%s called with no initialization\n", __func__); return -EINVAL; } @@ -1464,7 +1464,7 @@ static int i830_setparam(struct drm_device *dev, void *data, drm_i830_setparam_t *param = data; if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + DRM_ERROR("%s called with no initialization\n", __func__); return -EINVAL; } diff --git a/drivers/char/drm/i830_drv.h b/drivers/char/drm/i830_drv.h index 4caba8c5445..b5bf8cc0fda 100644 --- a/drivers/char/drm/i830_drv.h +++ b/drivers/char/drm/i830_drv.h @@ -158,7 +158,7 @@ extern int i830_driver_device_is_agp(struct drm_device * dev); if (I830_VERBOSE) \ printk("BEGIN_LP_RING(%d)\n", (n)); \ if (dev_priv->ring.space < n*4) \ - i830_wait_ring(dev, n*4, __FUNCTION__); \ + i830_wait_ring(dev, n*4, __func__); \ outcount = 0; \ outring = dev_priv->ring.tail; \ ringmask = dev_priv->ring.tail_mask; \ diff --git a/drivers/char/drm/i830_irq.c b/drivers/char/drm/i830_irq.c index a33db5f0967..91ec2bb497e 100644 --- a/drivers/char/drm/i830_irq.c +++ b/drivers/char/drm/i830_irq.c @@ -58,7 +58,7 @@ static int i830_emit_irq(struct drm_device * dev) drm_i830_private_t *dev_priv = dev->dev_private; RING_LOCALS; - DRM_DEBUG("%s\n", __FUNCTION__); + DRM_DEBUG("%s\n", __func__); atomic_inc(&dev_priv->irq_emitted); @@ -77,7 +77,7 @@ static int i830_wait_irq(struct drm_device * dev, int irq_nr) unsigned long end = jiffies + HZ * 3; int ret = 0; - DRM_DEBUG("%s\n", __FUNCTION__); + DRM_DEBUG("%s\n", __func__); if (atomic_read(&dev_priv->irq_received) >= irq_nr) return 0; @@ -124,7 +124,7 @@ int i830_irq_emit(struct drm_device *dev, void *data, LOCK_TEST_WITH_RETURN(dev, file_priv); if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + DRM_ERROR("%s called with no initialization\n", __func__); return -EINVAL; } @@ -147,7 +147,7 @@ int i830_irq_wait(struct drm_device *dev, void *data, drm_i830_irq_wait_t *irqwait = data; if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + DRM_ERROR("%s called with no initialization\n", __func__); return -EINVAL; } diff --git a/drivers/char/drm/i915_dma.c b/drivers/char/drm/i915_dma.c index ef7bf143a80..f47e46e3529 100644 --- a/drivers/char/drm/i915_dma.c +++ b/drivers/char/drm/i915_dma.c @@ -194,7 +194,7 @@ static int i915_dma_resume(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - DRM_DEBUG("%s\n", __FUNCTION__); + DRM_DEBUG("%s\n", __func__); if (!dev_priv->sarea) { DRM_ERROR("can not find sarea!\n"); @@ -609,7 +609,7 @@ static int i915_quiescent(struct drm_device * dev) drm_i915_private_t *dev_priv = dev->dev_private; i915_kernel_lost_context(dev); - return i915_wait_ring(dev, dev_priv->ring.Size - 8, __FUNCTION__); + return i915_wait_ring(dev, dev_priv->ring.Size - 8, __func__); } static int i915_flush_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/char/drm/i915_drv.h b/drivers/char/drm/i915_drv.h index c614d78b3df..db7001f2256 100644 --- a/drivers/char/drm/i915_drv.h +++ b/drivers/char/drm/i915_drv.h @@ -272,7 +272,7 @@ extern void i915_mem_release(struct drm_device * dev, if (I915_VERBOSE) \ DRM_DEBUG("BEGIN_LP_RING(%d)\n", (n)); \ if (dev_priv->ring.space < (n)*4) \ - i915_wait_ring(dev, (n)*4, __FUNCTION__); \ + i915_wait_ring(dev, (n)*4, __func__); \ outcount = 0; \ outring = dev_priv->ring.tail; \ ringmask = dev_priv->ring.tail_mask; \ diff --git a/drivers/char/drm/radeon_cp.c b/drivers/char/drm/radeon_cp.c index 9072e4a1894..f6f6c92bf77 100644 --- a/drivers/char/drm/radeon_cp.c +++ b/drivers/char/drm/radeon_cp.c @@ -894,7 +894,7 @@ static u32 RADEON_READ_IGPGART(drm_radeon_private_t *dev_priv, int addr) #if RADEON_FIFO_DEBUG static void radeon_status(drm_radeon_private_t * dev_priv) { - printk("%s:\n", __FUNCTION__); + printk("%s:\n", __func__); printk("RBBM_STATUS = 0x%08x\n", (unsigned int)RADEON_READ(RADEON_RBBM_STATUS)); printk("CP_RB_RTPR = 0x%08x\n", diff --git a/drivers/char/ds1286.c b/drivers/char/ds1286.c index 59146e3365b..ea35ab2c990 100644 --- a/drivers/char/ds1286.c +++ b/drivers/char/ds1286.c @@ -39,6 +39,7 @@ #include <linux/spinlock.h> #include <linux/bcd.h> #include <linux/proc_fs.h> +#include <linux/jiffies.h> #include <asm/uaccess.h> #include <asm/system.h> @@ -451,7 +452,7 @@ static void ds1286_get_time(struct rtc_time *rtc_tm) */ if (ds1286_is_updating() != 0) - while (jiffies - uip_watchdog < 2*HZ/100) + while (time_before(jiffies, uip_watchdog + 2*HZ/100)) barrier(); /* diff --git a/drivers/char/epca.c b/drivers/char/epca.c index ffd747c5dff..60a4df7dac1 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -38,8 +38,8 @@ #include <linux/slab.h> #include <linux/ioport.h> #include <linux/interrupt.h> -#include <asm/uaccess.h> -#include <asm/io.h> +#include <linux/uaccess.h> +#include <linux/io.h> #include <linux/spinlock.h> #include <linux/pci.h> #include "digiPCI.h" @@ -73,7 +73,8 @@ static int invalid_lilo_config; */ static DEFINE_SPINLOCK(epca_lock); -/* MAXBOARDS is typically 12, but ISA and EISA cards are restricted to 7 below. */ +/* MAXBOARDS is typically 12, but ISA and EISA cards are restricted + to 7 below. */ static struct board_info boards[MAXBOARDS]; static struct tty_driver *pc_driver; @@ -157,13 +158,12 @@ static void epca_error(int, char *); static void pc_close(struct tty_struct *, struct file *); static void shutdown(struct channel *); static void pc_hangup(struct tty_struct *); -static void pc_put_char(struct tty_struct *, unsigned char); static int pc_write_room(struct tty_struct *); static int pc_chars_in_buffer(struct tty_struct *); static void pc_flush_buffer(struct tty_struct *); static void pc_flush_chars(struct tty_struct *); static int block_til_ready(struct tty_struct *, struct file *, - struct channel *); + struct channel *); static int pc_open(struct tty_struct *, struct file *); static void post_fep_init(unsigned int crd); static void epcapoll(unsigned long); @@ -175,18 +175,18 @@ static unsigned termios2digi_c(struct channel *ch, unsigned); static void epcaparam(struct tty_struct *, struct channel *); static void receive_data(struct channel *); static int pc_ioctl(struct tty_struct *, struct file *, - unsigned int, unsigned long); + unsigned int, unsigned long); static int info_ioctl(struct tty_struct *, struct file *, - unsigned int, unsigned long); + unsigned int, unsigned long); static void pc_set_termios(struct tty_struct *, struct ktermios *); static void do_softint(struct work_struct *work); static void pc_stop(struct tty_struct *); static void pc_start(struct tty_struct *); -static void pc_throttle(struct tty_struct * tty); +static void pc_throttle(struct tty_struct *tty); static void pc_unthrottle(struct tty_struct *tty); static void digi_send_break(struct channel *ch, int msec); static void setup_empty_event(struct tty_struct *tty, struct channel *ch); -void epca_setup(char *, int *); +static void epca_setup(char *, int *); static int pc_write(struct tty_struct *, const unsigned char *, int); static int pc_init(void); @@ -243,7 +243,7 @@ static void assertmemoff(struct channel *ch) /* PCXEM windowing is the same as that used in the PCXR and CX series cards. */ static void pcxem_memwinon(struct board_info *b, unsigned int win) { - outb_p(FEPWIN|win, b->port + 1); + outb_p(FEPWIN | win, b->port + 1); } static void pcxem_memwinoff(struct board_info *b, unsigned int win) @@ -253,7 +253,7 @@ static void pcxem_memwinoff(struct board_info *b, unsigned int win) static void pcxem_globalwinon(struct channel *ch) { - outb_p( FEPWIN, (int)ch->board->port + 1); + outb_p(FEPWIN, (int)ch->board->port + 1); } static void pcxem_rxwinon(struct channel *ch) @@ -394,7 +394,7 @@ static struct channel *verifyChannel(struct tty_struct *tty) */ if (tty) { struct channel *ch = (struct channel *)tty->driver_data; - if ((ch >= &digi_channels[0]) && (ch < &digi_channels[nbdevs])) { + if (ch >= &digi_channels[0] && ch < &digi_channels[nbdevs]) { if (ch->magic == EPCA_MAGIC) return ch; } @@ -414,7 +414,7 @@ static void pc_sched_event(struct channel *ch, int event) static void epca_error(int line, char *msg) { - printk(KERN_ERR "epca_error (Digi): line = %d %s\n",line,msg); + printk(KERN_ERR "epca_error (Digi): line = %d %s\n", line, msg); } static void pc_close(struct tty_struct *tty, struct file *filp) @@ -425,7 +425,8 @@ static void pc_close(struct tty_struct *tty, struct file *filp) * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ - if ((ch = verifyChannel(tty)) != NULL) { + ch = verifyChannel(tty); + if (ch != NULL) { spin_lock_irqsave(&epca_lock, flags); if (tty_hung_up_p(filp)) { spin_unlock_irqrestore(&epca_lock, flags); @@ -440,7 +441,6 @@ static void pc_close(struct tty_struct *tty, struct file *filp) spin_unlock_irqrestore(&epca_lock, flags); return; } - /* Port open only once go ahead with shutdown & reset */ BUG_ON(ch->count < 0); @@ -455,12 +455,13 @@ static void pc_close(struct tty_struct *tty, struct file *filp) spin_unlock_irqrestore(&epca_lock, flags); if (ch->asyncflags & ASYNC_INITIALIZED) { - /* Setup an event to indicate when the transmit buffer empties */ + /* Setup an event to indicate when the + transmit buffer empties */ setup_empty_event(tty, ch); - tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ + /* 30 seconds timeout */ + tty_wait_until_sent(tty, 3000); } - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + pc_flush_buffer(tty); tty_ldisc_flush(tty); shutdown(ch); @@ -477,7 +478,7 @@ static void pc_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&ch->open_wait); } ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED | - ASYNC_CLOSING); + ASYNC_CLOSING); wake_up_interruptible(&ch->close_wait); } } @@ -524,16 +525,15 @@ static void shutdown(struct channel *ch) static void pc_hangup(struct tty_struct *tty) { struct channel *ch; - /* * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ - if ((ch = verifyChannel(tty)) != NULL) { + ch = verifyChannel(tty); + if (ch != NULL) { unsigned long flags; - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + pc_flush_buffer(tty); tty_ldisc_flush(tty); shutdown(ch); @@ -548,7 +548,7 @@ static void pc_hangup(struct tty_struct *tty) } static int pc_write(struct tty_struct *tty, - const unsigned char *buf, int bytesAvailable) + const unsigned char *buf, int bytesAvailable) { unsigned int head, tail; int dataLen; @@ -572,7 +572,8 @@ static int pc_write(struct tty_struct *tty, * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ - if ((ch = verifyChannel(tty)) == NULL) + ch = verifyChannel(tty); + if (ch == NULL) return 0; /* Make a pointer to the channel data structure found on the board. */ @@ -645,26 +646,19 @@ static int pc_write(struct tty_struct *tty, return amountCopied; } -static void pc_put_char(struct tty_struct *tty, unsigned char c) -{ - pc_write(tty, &c, 1); -} - static int pc_write_room(struct tty_struct *tty) { - int remain; + int remain = 0; struct channel *ch; unsigned long flags; unsigned int head, tail; struct board_chan __iomem *bc; - - remain = 0; - /* * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ - if ((ch = verifyChannel(tty)) != NULL) { + ch = verifyChannel(tty); + if (ch != NULL) { spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); @@ -676,8 +670,8 @@ static int pc_write_room(struct tty_struct *tty) tail = readw(&bc->tout); /* Wrap tail if necessary */ tail &= (ch->txbufsize - 1); - - if ((remain = tail - head - 1) < 0 ) + remain = tail - head - 1; + if (remain < 0) remain += ch->txbufsize; if (remain && (ch->statusflags & LOWWAIT) == 0) { @@ -699,12 +693,12 @@ static int pc_chars_in_buffer(struct tty_struct *tty) unsigned long flags; struct channel *ch; struct board_chan __iomem *bc; - /* * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ - if ((ch = verifyChannel(tty)) == NULL) + ch = verifyChannel(tty); + if (ch == NULL) return 0; spin_lock_irqsave(&epca_lock, flags); @@ -715,7 +709,8 @@ static int pc_chars_in_buffer(struct tty_struct *tty) head = readw(&bc->tin); ctail = readw(&ch->mailbox->cout); - if (tail == head && readw(&ch->mailbox->cin) == ctail && readb(&bc->tbusy) == 0) + if (tail == head && readw(&ch->mailbox->cin) == ctail && + readb(&bc->tbusy) == 0) chars = 0; else { /* Begin if some space on the card has been used */ head = readw(&bc->tin) & (ch->txbufsize - 1); @@ -725,7 +720,8 @@ static int pc_chars_in_buffer(struct tty_struct *tty) * pc_write_room here we are finding the amount of bytes in the * buffer filled. Not the amount of bytes empty. */ - if ((remain = tail - head - 1) < 0 ) + remain = tail - head - 1; + if (remain < 0) remain += ch->txbufsize; chars = (int)(ch->txbufsize - remain); /* @@ -736,7 +732,7 @@ static int pc_chars_in_buffer(struct tty_struct *tty) * transmit buffer empties. */ if (!(ch->statusflags & EMPTYWAIT)) - setup_empty_event(tty,ch); + setup_empty_event(tty, ch); } /* End if some space on the card has been used */ memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); @@ -754,7 +750,8 @@ static void pc_flush_buffer(struct tty_struct *tty) * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ - if ((ch = verifyChannel(tty)) == NULL) + ch = verifyChannel(tty); + if (ch == NULL) return; spin_lock_irqsave(&epca_lock, flags); @@ -775,23 +772,25 @@ static void pc_flush_chars(struct tty_struct *tty) * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ - if ((ch = verifyChannel(tty)) != NULL) { + ch = verifyChannel(tty); + if (ch != NULL) { unsigned long flags; spin_lock_irqsave(&epca_lock, flags); /* * If not already set and the transmitter is busy setup an * event to indicate when the transmit empties. */ - if ((ch->statusflags & TXBUSY) && !(ch->statusflags & EMPTYWAIT)) - setup_empty_event(tty,ch); + if ((ch->statusflags & TXBUSY) && + !(ch->statusflags & EMPTYWAIT)) + setup_empty_event(tty, ch); spin_unlock_irqrestore(&epca_lock, flags); } } static int block_til_ready(struct tty_struct *tty, - struct file *filp, struct channel *ch) + struct file *filp, struct channel *ch) { - DECLARE_WAITQUEUE(wait,current); + DECLARE_WAITQUEUE(wait, current); int retval, do_clocal = 0; unsigned long flags; @@ -839,8 +838,7 @@ static int block_til_ready(struct tty_struct *tty, while (1) { set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(ch->asyncflags & ASYNC_INITIALIZED)) - { + !(ch->asyncflags & ASYNC_INITIALIZED)) { if (ch->asyncflags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else @@ -880,7 +878,7 @@ static int block_til_ready(struct tty_struct *tty, return 0; } -static int pc_open(struct tty_struct *tty, struct file * filp) +static int pc_open(struct tty_struct *tty, struct file *filp) { struct channel *ch; unsigned long flags; @@ -923,7 +921,8 @@ static int pc_open(struct tty_struct *tty, struct file * filp) return(-ENODEV); } - if ((bc = ch->brdchan) == 0) { + bc = ch->brdchan; + if (bc == NULL) { tty->driver_data = NULL; return -ENODEV; } @@ -964,7 +963,7 @@ static int pc_open(struct tty_struct *tty, struct file * filp) * The below routine generally sets up parity, baud, flow control * issues, etc.... It effect both control flags and input flags. */ - epcaparam(tty,ch); + epcaparam(tty, ch); ch->asyncflags |= ASYNC_INITIALIZED; memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); @@ -1002,8 +1001,8 @@ static void __exit epca_module_exit(void) del_timer_sync(&epca_timer); - if (tty_unregister_driver(pc_driver) || tty_unregister_driver(pc_info)) - { + if (tty_unregister_driver(pc_driver) || + tty_unregister_driver(pc_info)) { printk(KERN_WARNING "epca: cleanup_module failed to un-register tty driver\n"); return; } @@ -1034,7 +1033,6 @@ static const struct tty_operations pc_ops = { .flush_buffer = pc_flush_buffer, .chars_in_buffer = pc_chars_in_buffer, .flush_chars = pc_flush_chars, - .put_char = pc_put_char, .ioctl = pc_ioctl, .set_termios = pc_set_termios, .stop = pc_stop, @@ -1044,7 +1042,7 @@ static const struct tty_operations pc_ops = { .hangup = pc_hangup, }; -static int info_open(struct tty_struct *tty, struct file * filp) +static int info_open(struct tty_struct *tty, struct file *filp) { return 0; } @@ -1099,7 +1097,7 @@ static int __init pc_init(void) * Set up interrupt, we will worry about memory allocation in * post_fep_init. */ - printk(KERN_INFO "DIGI epca driver version %s loaded.\n",VERSION); + printk(KERN_INFO "DIGI epca driver version %s loaded.\n", VERSION); /* * NOTE : This code assumes that the number of ports found in the @@ -1252,7 +1250,7 @@ static int __init pc_init(void) if ((board_id & 0x30) == 0x30) bd->memory_seg = 0x8000; } else - printk(KERN_ERR "epca: Board at 0x%x doesn't appear to be an XI\n",(int)bd->port); + printk(KERN_ERR "epca: Board at 0x%x doesn't appear to be an XI\n", (int)bd->port); break; } } @@ -1326,12 +1324,12 @@ static void post_fep_init(unsigned int crd) */ /* PCI cards are already remapped at this point ISA are not */ bd->numports = readw(bd->re_map_membase + XEMPORTS); - epcaassert(bd->numports <= 64,"PCI returned a invalid number of ports"); + epcaassert(bd->numports <= 64, "PCI returned a invalid number of ports"); nbdevs += (bd->numports); } else { /* Fix up the mappings for ISA/EISA etc */ /* FIXME: 64K - can we be smarter ? */ - bd->re_map_membase = ioremap(bd->membase, 0x10000); + bd->re_map_membase = ioremap_nocache(bd->membase, 0x10000); } if (crd != 0) @@ -1362,7 +1360,8 @@ static void post_fep_init(unsigned int crd) * XEPORTS (address 0xc22) points at the number of channels the card * supports. (For 64XE, XI, XEM, and XR use 0xc02) */ - if ((bd->type == PCXEVE || bd->type == PCXE) && (readw(memaddr + XEPORTS) < 3)) + if ((bd->type == PCXEVE || bd->type == PCXE) && + (readw(memaddr + XEPORTS) < 3)) shrinkmem = 1; if (bd->type < PCIXEM) if (!request_region((int)bd->port, 4, board_desc[bd->type])) @@ -1461,10 +1460,12 @@ static void post_fep_init(unsigned int crd) case PCXEVE: case PCXE: - ch->txptr = memaddr + (((tseg - bd->memory_seg) << 4) & 0x1fff); + ch->txptr = memaddr + (((tseg - bd->memory_seg) << 4) + & 0x1fff); ch->txwin = FEPWIN | ((tseg - bd->memory_seg) >> 9); - ch->rxptr = memaddr + (((rseg - bd->memory_seg) << 4) & 0x1fff); - ch->rxwin = FEPWIN | ((rseg - bd->memory_seg) >>9 ); + ch->rxptr = memaddr + (((rseg - bd->memory_seg) << 4) + & 0x1fff); + ch->rxwin = FEPWIN | ((rseg - bd->memory_seg) >> 9); break; case PCXI: @@ -1518,8 +1519,9 @@ static void post_fep_init(unsigned int crd) } printk(KERN_INFO - "Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n", - VERSION, board_desc[bd->type], (long)bd->port, (long)bd->membase, bd->numports); + "Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n", + VERSION, board_desc[bd->type], (long)bd->port, + (long)bd->membase, bd->numports); memwinoff(bd, 0); } @@ -1527,7 +1529,7 @@ static void epcapoll(unsigned long ignored) { unsigned long flags; int crd; - volatile unsigned int head, tail; + unsigned int head, tail; struct channel *ch; struct board_info *bd; @@ -1593,7 +1595,9 @@ static void doevent(int crd) chan0 = card_ptr[crd]; epcaassert(chan0 <= &digi_channels[nbdevs - 1], "ch out of range"); assertgwinon(chan0); - while ((tail = readw(&chan0->mailbox->eout)) != (head = readw(&chan0->mailbox->ein))) { /* Begin while something in event queue */ + while ((tail = readw(&chan0->mailbox->eout)) != + (head = readw(&chan0->mailbox->ein))) { + /* Begin while something in event queue */ assertgwinon(chan0); eventbuf = bd->re_map_membase + tail + ISTART; /* Get the channel the event occurred on */ @@ -1617,7 +1621,8 @@ static void doevent(int crd) goto next; } - if ((bc = ch->brdchan) == NULL) + bc = ch->brdchan; + if (bc == NULL) goto next; if (event & DATA_IND) { /* Begin DATA_IND */ @@ -1629,10 +1634,11 @@ static void doevent(int crd) /* A modem signal change has been indicated */ ch->imodem = mstat; if (ch->asyncflags & ASYNC_CHECK_CD) { - if (mstat & ch->dcd) /* We are now receiving dcd */ + /* We are now receiving dcd */ + if (mstat & ch->dcd) wake_up_interruptible(&ch->open_wait); - else - pc_sched_event(ch, EPCA_EVENT_HANGUP); /* No dcd; hangup */ + else /* No dcd; hangup */ + pc_sched_event(ch, EPCA_EVENT_HANGUP); } } tty = ch->tty; @@ -1647,7 +1653,8 @@ static void doevent(int crd) tty_wakeup(tty); } } else if (event & EMPTYTX_IND) { - /* This event is generated by setup_empty_event */ + /* This event is generated by + setup_empty_event */ ch->statusflags &= ~TXBUSY; if (ch->statusflags & EMPTYWAIT) { ch->statusflags &= ~EMPTYWAIT; @@ -1655,7 +1662,7 @@ static void doevent(int crd) } } } - next: +next: globalwinon(ch); BUG_ON(!bc); writew(1, &bc->idata); @@ -1665,7 +1672,7 @@ static void doevent(int crd) } static void fepcmd(struct channel *ch, int cmd, int word_or_byte, - int byte2, int ncmds, int bytecmd) + int byte2, int ncmds, int bytecmd) { unchar __iomem *memaddr; unsigned int head, cmdTail, cmdStart, cmdMax; @@ -1690,8 +1697,10 @@ static void fepcmd(struct channel *ch, int cmd, int word_or_byte, memaddr = ch->board->re_map_membase; if (head >= (cmdMax - cmdStart) || (head & 03)) { - printk(KERN_ERR "line %d: Out of range, cmd = %x, head = %x\n", __LINE__, cmd, head); - printk(KERN_ERR "line %d: Out of range, cmdMax = %x, cmdStart = %x\n", __LINE__, cmdMax, cmdStart); + printk(KERN_ERR "line %d: Out of range, cmd = %x, head = %x\n", + __LINE__, cmd, head); + printk(KERN_ERR "line %d: Out of range, cmdMax = %x, cmdStart = %x\n", + __LINE__, cmdMax, cmdStart); return; } if (bytecmd) { @@ -1770,7 +1779,7 @@ static unsigned termios2digi_h(struct channel *ch, unsigned cflag) static unsigned termios2digi_i(struct channel *ch, unsigned iflag) { unsigned res = iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | - INPCK | ISTRIP|IXON|IXANY|IXOFF); + INPCK | ISTRIP | IXON | IXANY | IXOFF); if (ch->digiext.digi_flags & DIGI_AIXON) res |= IAIXON; return res; @@ -1838,7 +1847,7 @@ static void epcaparam(struct tty_struct *tty, struct channel *ch) unsigned mval, hflow, cflag, iflag; bc = ch->brdchan; - epcaassert(bc !=0, "bc out of range"); + epcaassert(bc != NULL, "bc out of range"); assertgwinon(ch); ts = tty->termios; @@ -1884,8 +1893,10 @@ static void epcaparam(struct tty_struct *tty, struct channel *ch) * Command sets channels iflag structure on the board. Such * things as input soft flow control, handling of parity * errors, and break handling are all set here. + * + * break handling, parity handling, input stripping, + * flow control chars */ - /* break handling, parity handling, input stripping, flow control chars */ fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0); } /* @@ -1981,7 +1992,7 @@ static void receive_data(struct channel *ch) return; /* If CREAD bit is off or device not open, set TX tail to head */ - if (!tty || !ts || !(ts->c_cflag & CREAD)) { + if (!tty || !ts || !(ts->c_cflag & CREAD)) { writew(head, &bc->rout); return; } @@ -1991,18 +2002,21 @@ static void receive_data(struct channel *ch) if (readb(&bc->orun)) { writeb(0, &bc->orun); - printk(KERN_WARNING "epca; overrun! DigiBoard device %s\n",tty->name); + printk(KERN_WARNING "epca; overrun! DigiBoard device %s\n", + tty->name); tty_insert_flip_char(tty, 0, TTY_OVERRUN); } rxwinon(ch); - while (bytesAvailable > 0) { /* Begin while there is data on the card */ + while (bytesAvailable > 0) { + /* Begin while there is data on the card */ wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail; /* * Even if head has wrapped around only report the amount of * data to be equal to the size - tail. Remember memcpy can't * automaticly wrap around the receive buffer. */ - dataToRead = (wrapgap < bytesAvailable) ? wrapgap : bytesAvailable; + dataToRead = (wrapgap < bytesAvailable) ? wrapgap + : bytesAvailable; /* Make sure we don't overflow the buffer */ dataToRead = tty_prepare_flip_string(tty, &rptr, dataToRead); if (dataToRead == 0) @@ -2153,14 +2167,14 @@ static int pc_tiocmset(struct tty_struct *tty, struct file *file, * The below routine generally sets up parity, baud, flow control * issues, etc.... It effect both control flags and input flags. */ - epcaparam(tty,ch); + epcaparam(tty, ch); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); return 0; } -static int pc_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) +static int pc_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { digiflow_t dflow; int retval; @@ -2175,7 +2189,6 @@ static int pc_ioctl(struct tty_struct *tty, struct file * file, bc = ch->brdchan; else return -EINVAL; - /* * For POSIX compliance we need to add more ioctls. See tty_ioctl.c in * /usr/src/linux/drivers/char for a good example. In particular think @@ -2186,9 +2199,10 @@ static int pc_ioctl(struct tty_struct *tty, struct file * file, retval = tty_check_change(tty); if (retval) return retval; - /* Setup an event to indicate when the transmit buffer empties */ + /* Setup an event to indicate when the transmit + buffer empties */ spin_lock_irqsave(&epca_lock, flags); - setup_empty_event(tty,ch); + setup_empty_event(tty, ch); spin_unlock_irqrestore(&epca_lock, flags); tty_wait_until_sent(tty, 0); if (!arg) @@ -2198,29 +2212,14 @@ static int pc_ioctl(struct tty_struct *tty, struct file * file, retval = tty_check_change(tty); if (retval) return retval; - - /* Setup an event to indicate when the transmit buffer empties */ + /* Setup an event to indicate when the transmit buffer + empties */ spin_lock_irqsave(&epca_lock, flags); - setup_empty_event(tty,ch); + setup_empty_event(tty, ch); spin_unlock_irqrestore(&epca_lock, flags); tty_wait_until_sent(tty, 0); digi_send_break(ch, arg ? arg*(HZ/10) : HZ/4); return 0; - case TIOCGSOFTCAR: - if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)arg)) - return -EFAULT; - return 0; - case TIOCSSOFTCAR: - { - unsigned int value; - - if (get_user(value, (unsigned __user *)argp)) - return -EFAULT; - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (value ? CLOCAL : 0)); - return 0; - } case TIOCMODG: mflag = pc_tiocmget(tty, file); if (put_user(mflag, (unsigned long __user *)argp)) @@ -2253,10 +2252,12 @@ static int pc_ioctl(struct tty_struct *tty, struct file * file, break; case DIGI_SETAW: case DIGI_SETAF: + lock_kernel(); if (cmd == DIGI_SETAW) { - /* Setup an event to indicate when the transmit buffer empties */ + /* Setup an event to indicate when the transmit + buffer empties */ spin_lock_irqsave(&epca_lock, flags); - setup_empty_event(tty,ch); + setup_empty_event(tty, ch); spin_unlock_irqrestore(&epca_lock, flags); tty_wait_until_sent(tty, 0); } else { @@ -2264,6 +2265,7 @@ static int pc_ioctl(struct tty_struct *tty, struct file * file, if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); } + unlock_kernel(); /* Fall Thru */ case DIGI_SETA: if (copy_from_user(&ch->digiext, argp, sizeof(digi_t))) @@ -2285,7 +2287,7 @@ static int pc_ioctl(struct tty_struct *tty, struct file * file, * control issues, etc.... It effect both control flags and * input flags. */ - epcaparam(tty,ch); + epcaparam(tty, ch); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); break; @@ -2321,18 +2323,21 @@ static int pc_ioctl(struct tty_struct *tty, struct file * file, if (copy_from_user(&dflow, argp, sizeof(dflow))) return -EFAULT; - if (dflow.startc != startc || dflow.stopc != stopc) { /* Begin if setflow toggled */ + if (dflow.startc != startc || dflow.stopc != stopc) { + /* Begin if setflow toggled */ spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); if (cmd == DIGI_SETFLOW) { ch->fepstartc = ch->startc = dflow.startc; ch->fepstopc = ch->stopc = dflow.stopc; - fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1); + fepcmd(ch, SONOFFC, ch->fepstartc, + ch->fepstopc, 0, 1); } else { ch->fepstartca = ch->startca = dflow.startc; ch->fepstopca = ch->stopca = dflow.stopc; - fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); + fepcmd(ch, SAUXONOFFC, ch->fepstartca, + ch->fepstopca, 0, 1); } if (ch->statusflags & TXSTOPPED) @@ -2356,7 +2361,9 @@ static void pc_set_termios(struct tty_struct *tty, struct ktermios *old_termios) * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ - if ((ch = verifyChannel(tty)) != NULL) { /* Begin if channel valid */ + ch = verifyChannel(tty); + + if (ch != NULL) { /* Begin if channel valid */ spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); epcaparam(tty, ch); @@ -2383,7 +2390,7 @@ static void do_softint(struct work_struct *work) if (tty && tty->driver_data) { if (test_and_clear_bit(EPCA_EVENT_HANGUP, &ch->event)) { - tty_hangup(tty); /* FIXME: module removal race here - AKPM */ + tty_hangup(tty); wake_up_interruptible(&ch->open_wait); ch->asyncflags &= ~ASYNC_NORMAL_ACTIVE; } @@ -2403,9 +2410,11 @@ static void pc_stop(struct tty_struct *tty) * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ - if ((ch = verifyChannel(tty)) != NULL) { + ch = verifyChannel(tty); + if (ch != NULL) { spin_lock_irqsave(&epca_lock, flags); - if ((ch->statusflags & TXSTOPPED) == 0) { /* Begin if transmit stop requested */ + if ((ch->statusflags & TXSTOPPED) == 0) { + /* Begin if transmit stop requested */ globalwinon(ch); /* STOP transmitting now !! */ fepcmd(ch, PAUSETX, 0, 0, 0, 0); @@ -2423,11 +2432,14 @@ static void pc_start(struct tty_struct *tty) * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ - if ((ch = verifyChannel(tty)) != NULL) { + ch = verifyChannel(tty); + if (ch != NULL) { unsigned long flags; spin_lock_irqsave(&epca_lock, flags); - /* Just in case output was resumed because of a change in Digi-flow */ - if (ch->statusflags & TXSTOPPED) { /* Begin transmit resume requested */ + /* Just in case output was resumed because of a change + in Digi-flow */ + if (ch->statusflags & TXSTOPPED) { + /* Begin transmit resume requested */ struct board_chan __iomem *bc; globalwinon(ch); bc = ch->brdchan; @@ -2457,7 +2469,8 @@ static void pc_throttle(struct tty_struct *tty) * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ - if ((ch = verifyChannel(tty)) != NULL) { + ch = verifyChannel(tty); + if (ch != NULL) { spin_lock_irqsave(&epca_lock, flags); if ((ch->statusflags & RXSTOPPED) == 0) { globalwinon(ch); @@ -2477,8 +2490,10 @@ static void pc_unthrottle(struct tty_struct *tty) * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ - if ((ch = verifyChannel(tty)) != NULL) { - /* Just in case output was resumed because of a change in Digi-flow */ + ch = verifyChannel(tty); + if (ch != NULL) { + /* Just in case output was resumed because of a change + in Digi-flow */ spin_lock_irqsave(&epca_lock, flags); if (ch->statusflags & RXSTOPPED) { globalwinon(ch); @@ -2490,7 +2505,7 @@ static void pc_unthrottle(struct tty_struct *tty) } } -void digi_send_break(struct channel *ch, int msec) +static void digi_send_break(struct channel *ch, int msec) { unsigned long flags; @@ -2523,7 +2538,7 @@ static void setup_empty_event(struct tty_struct *tty, struct channel *ch) memoff(ch); } -void epca_setup(char *str, int *ints) +static void epca_setup(char *str, int *ints) { struct board_info board; int index, loop, last; @@ -2552,14 +2567,16 @@ void epca_setup(char *str, int *ints) * instructing the driver to ignore epcaconfig.) For * this reason we check for 2. */ - if (board.status == 2) { /* Begin ignore epcaconfig as well as lilo cmd line */ + if (board.status == 2) { + /* Begin ignore epcaconfig as well as lilo cmd line */ nbdevs = 0; num_cards = 0; return; } /* End ignore epcaconfig as well as lilo cmd line */ if (board.status > 2) { - printk(KERN_ERR "epca_setup: Invalid board status 0x%x\n", board.status); + printk(KERN_ERR "epca_setup: Invalid board status 0x%x\n", + board.status); invalid_lilo_config = 1; setup_error_code |= INVALID_BOARD_STATUS; return; @@ -2613,7 +2630,8 @@ void epca_setup(char *str, int *ints) case 6: board.membase = ints[index]; if (ints[index] <= 0) { - printk(KERN_ERR "epca_setup: Invalid memory base 0x%x\n",(unsigned int)board.membase); + printk(KERN_ERR "epca_setup: Invalid memory base 0x%x\n", + (unsigned int)board.membase); invalid_lilo_config = 1; setup_error_code |= INVALID_MEM_BASE; return; @@ -2744,7 +2762,7 @@ void epca_setup(char *str, int *ints) t2++; if (*t2) { - printk(KERN_ERR "epca_setup: Invalid memory base %s\n",str); + printk(KERN_ERR "epca_setup: Invalid memory base %s\n", str); invalid_lilo_config = 1; setup_error_code |= INVALID_MEM_BASE; return; @@ -2766,7 +2784,7 @@ void epca_setup(char *str, int *ints) /* I should REALLY validate the stuff here */ /* Copies our local copy of board into boards */ - memcpy((void *)&boards[num_cards],(void *)&board, sizeof(board)); + memcpy((void *)&boards[num_cards], (void *)&board, sizeof(board)); /* Does this get called once per lilo arg are what ? */ printk(KERN_INFO "PC/Xx: Added board %i, %s %i ports at 0x%4.4X base 0x%6.6X\n", num_cards, board_desc[board.type], @@ -2807,9 +2825,9 @@ static int __devinit epca_init_one(struct pci_dev *pdev, if (board_idx >= MAXBOARDS) goto err_out; - addr = pci_resource_start (pdev, epca_info_tbl[info_idx].bar_idx); + addr = pci_resource_start(pdev, epca_info_tbl[info_idx].bar_idx); if (!addr) { - printk (KERN_ERR PFX "PCI region #%d not available (size 0)\n", + printk(KERN_ERR PFX "PCI region #%d not available (size 0)\n", epca_info_tbl[info_idx].bar_idx); goto err_out; } @@ -2820,28 +2838,29 @@ static int __devinit epca_init_one(struct pci_dev *pdev, boards[board_idx].port = addr + PCI_IO_OFFSET; boards[board_idx].membase = addr; - if (!request_mem_region (addr + PCI_IO_OFFSET, 0x200000, "epca")) { - printk (KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n", + if (!request_mem_region(addr + PCI_IO_OFFSET, 0x200000, "epca")) { + printk(KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n", 0x200000, addr + PCI_IO_OFFSET); goto err_out; } - boards[board_idx].re_map_port = ioremap(addr + PCI_IO_OFFSET, 0x200000); + boards[board_idx].re_map_port = ioremap_nocache(addr + PCI_IO_OFFSET, + 0x200000); if (!boards[board_idx].re_map_port) { - printk (KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n", + printk(KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n", 0x200000, addr + PCI_IO_OFFSET); goto err_out_free_pciio; } - if (!request_mem_region (addr, 0x200000, "epca")) { - printk (KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n", + if (!request_mem_region(addr, 0x200000, "epca")) { + printk(KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n", 0x200000, addr); goto err_out_free_iounmap; } - boards[board_idx].re_map_membase = ioremap(addr, 0x200000); + boards[board_idx].re_map_membase = ioremap_nocache(addr, 0x200000); if (!boards[board_idx].re_map_membase) { - printk (KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n", + printk(KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n", 0x200000, addr + PCI_IO_OFFSET); goto err_out_free_memregion; } @@ -2858,11 +2877,11 @@ static int __devinit epca_init_one(struct pci_dev *pdev, return 0; err_out_free_memregion: - release_mem_region (addr, 0x200000); + release_mem_region(addr, 0x200000); err_out_free_iounmap: - iounmap (boards[board_idx].re_map_port); + iounmap(boards[board_idx].re_map_port); err_out_free_pciio: - release_mem_region (addr + PCI_IO_OFFSET, 0x200000); + release_mem_region(addr + PCI_IO_OFFSET, 0x200000); err_out: return -ENODEV; } @@ -2878,9 +2897,9 @@ static struct pci_device_id epca_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, epca_pci_tbl); -int __init init_PCI (void) +static int __init init_PCI(void) { - memset (&epca_driver, 0, sizeof (epca_driver)); + memset(&epca_driver, 0, sizeof(epca_driver)); epca_driver.name = "epca"; epca_driver.id_table = epca_pci_tbl; epca_driver.probe = epca_init_one; diff --git a/drivers/char/esp.c b/drivers/char/esp.c index f3fe6206734..84840ba13ff 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -8,7 +8,7 @@ * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now * much more extensible to support other serial cards based on the * 16450/16550A UART's. Added support for the AST FourPort and the - * Accent Async board. + * Accent Async board. * * set_serial_info fixed to set the flags, custom divisor, and uart * type fields. Fix suggested by Michael K. Johnson 12/12/92. @@ -61,11 +61,11 @@ #include <linux/bitops.h> #include <asm/system.h> -#include <asm/io.h> +#include <linux/io.h> #include <asm/dma.h> #include <linux/slab.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/hayesesp.h> @@ -127,8 +127,10 @@ static struct tty_driver *esp_driver; #undef SERIAL_DEBUG_FLOW #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) -#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ - tty->name, (info->flags), serial_driver.refcount,info->count,tty->count,s) +#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ + tty->name, info->flags, \ + serial_driver.refcount, \ + info->count, tty->count, s) #else #define DBG_CNT(s) #endif @@ -189,7 +191,7 @@ static inline void serial_out(struct esp_struct *info, int offset, */ static void rs_stop(struct tty_struct *tty) { - struct esp_struct *info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "rs_stop")) @@ -206,12 +208,12 @@ static void rs_stop(struct tty_struct *tty) static void rs_start(struct tty_struct *tty) { - struct esp_struct *info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned long flags; - + if (serial_paranoia_check(info, tty->name, "rs_start")) return; - + spin_lock_irqsave(&info->lock, flags); if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; @@ -233,7 +235,7 @@ static void rs_start(struct tty_struct *tty) * rs_interrupt() should try to keep the interrupt handler as fast as * possible. After you are done making modifications, it is not a bad * idea to do: - * + * * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c * * and look at the resulting assemble code in serial.s. @@ -290,7 +292,7 @@ static inline void receive_chars_pio(struct esp_struct *info, int num_bytes) } status_mask = (info->read_status_mask >> 2) & 0x07; - + for (i = 0; i < num_bytes - 1; i += 2) { *((unsigned short *)(pio_buf->data + i)) = inw(info->port + UART_ESI_RX); @@ -325,8 +327,7 @@ static inline void receive_chars_pio(struct esp_struct *info, int num_bytes) flag = TTY_BREAK; if (info->flags & ASYNC_SAK) do_SAK(tty); - } - else if (err_buf->data[i] & 0x02) + } else if (err_buf->data[i] & 0x02) flag = TTY_FRAME; else if (err_buf->data[i] & 0x01) flag = TTY_PARITY; @@ -341,23 +342,29 @@ static inline void receive_chars_pio(struct esp_struct *info, int num_bytes) release_pio_buffer(err_buf); } -static inline void receive_chars_dma(struct esp_struct *info, int num_bytes) +static void program_isa_dma(int dma, int dir, unsigned long addr, int len) { unsigned long flags; + + flags = claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma, dir); + set_dma_addr(dma, addr); + set_dma_count(dma, len); + enable_dma(dma); + release_dma_lock(flags); +} + +static void receive_chars_dma(struct esp_struct *info, int num_bytes) +{ info->stat_flags &= ~ESP_STAT_RX_TIMEOUT; dma_bytes = num_bytes; info->stat_flags |= ESP_STAT_DMA_RX; - - flags=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma, DMA_MODE_READ); - set_dma_addr(dma, isa_virt_to_bus(dma_buffer)); - set_dma_count(dma, dma_bytes); - enable_dma(dma); - release_dma_lock(flags); - - serial_out(info, UART_ESI_CMD1, ESI_START_DMA_RX); + + program_isa_dma(dma, DMA_MODE_READ, isa_virt_to_bus(dma_buffer), + dma_bytes); + serial_out(info, UART_ESI_CMD1, ESI_START_DMA_RX); } static inline void receive_chars_dma_done(struct esp_struct *info, @@ -366,22 +373,22 @@ static inline void receive_chars_dma_done(struct esp_struct *info, struct tty_struct *tty = info->tty; int num_bytes; unsigned long flags; - - flags=claim_dma_lock(); + + flags = claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); info->stat_flags &= ~ESP_STAT_DMA_RX; num_bytes = dma_bytes - get_dma_residue(dma); release_dma_lock(flags); - + info->icount.rx += num_bytes; if (num_bytes > 0) { tty_insert_flip_string(tty, dma_buffer, num_bytes - 1); status &= (0x1c & info->read_status_mask); - + /* Is the status significant or do we throw the last byte ? */ if (!(status & info->ignore_status_mask)) { int statflag = 0; @@ -393,13 +400,13 @@ static inline void receive_chars_dma_done(struct esp_struct *info, do_SAK(tty); } else if (status & 0x08) { statflag = TTY_FRAME; - (info->icount.frame)++; - } - else if (status & 0x04) { + info->icount.frame++; + } else if (status & 0x04) { statflag = TTY_PARITY; - (info->icount.parity)++; + info->icount.parity++; } - tty_insert_flip_char(tty, dma_buffer[num_bytes - 1], statflag); + tty_insert_flip_char(tty, dma_buffer[num_bytes - 1], + statflag); } tty_schedule_flip(tty); } @@ -484,8 +491,6 @@ static inline void transmit_chars_pio(struct esp_struct *info, /* Caller must hold info->lock */ static inline void transmit_chars_dma(struct esp_struct *info, int num_bytes) { - unsigned long flags; - dma_bytes = num_bytes; if (info->xmit_tail + dma_bytes <= ESP_XMIT_SIZE) { @@ -517,26 +522,18 @@ static inline void transmit_chars_dma(struct esp_struct *info, int num_bytes) } info->stat_flags |= ESP_STAT_DMA_TX; - - flags=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma, DMA_MODE_WRITE); - set_dma_addr(dma, isa_virt_to_bus(dma_buffer)); - set_dma_count(dma, dma_bytes); - enable_dma(dma); - release_dma_lock(flags); - - serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX); + + program_isa_dma(dma, DMA_MODE_WRITE, isa_virt_to_bus(dma_buffer), + dma_bytes); + serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX); } static inline void transmit_chars_dma_done(struct esp_struct *info) { int num_bytes; unsigned long flags; - - flags=claim_dma_lock(); + flags = claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); @@ -547,27 +544,21 @@ static inline void transmit_chars_dma_done(struct esp_struct *info) if (dma_bytes != num_bytes) { dma_bytes -= num_bytes; memmove(dma_buffer, dma_buffer + num_bytes, dma_bytes); - - flags=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma, DMA_MODE_WRITE); - set_dma_addr(dma, isa_virt_to_bus(dma_buffer)); - set_dma_count(dma, dma_bytes); - enable_dma(dma); - release_dma_lock(flags); - - serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX); + + program_isa_dma(dma, DMA_MODE_WRITE, + isa_virt_to_bus(dma_buffer), dma_bytes); + + serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX); } else { dma_bytes = 0; info->stat_flags &= ~ESP_STAT_DMA_TX; } } -static inline void check_modem_status(struct esp_struct *info) +static void check_modem_status(struct esp_struct *info) { int status; - + serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT); status = serial_in(info, UART_ESI_STAT2); @@ -588,7 +579,7 @@ static inline void check_modem_status(struct esp_struct *info) #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) printk("ttys%d CD now %s...", info->line, (status & UART_MSR_DCD) ? "on" : "off"); -#endif +#endif if (status & UART_MSR_DCD) wake_up_interruptible(&info->open_wait); else { @@ -605,7 +596,7 @@ static inline void check_modem_status(struct esp_struct *info) */ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) { - struct esp_struct * info; + struct esp_struct *info; unsigned err_status; unsigned int scratch; @@ -617,7 +608,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) scratch = serial_in(info, UART_ESI_SID); spin_lock(&info->lock); - + if (!info->tty) { spin_unlock(&info->lock); return IRQ_NONE; @@ -637,7 +628,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) if (err_status & 0x80) /* Start break */ wake_up_interruptible(&info->break_wait); } - + if ((scratch & 0x88) || /* DMA completed or timed out */ (err_status & 0x1c) /* receive error */) { if (info->stat_flags & ESP_STAT_DMA_RX) @@ -667,7 +658,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) receive_chars_dma(info, num_bytes); } } - + if (!(info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) && (scratch & 0x02) && (info->IER & UART_IER_THRI)) { if ((info->xmit_cnt <= 0) || info->tty->stopped) { @@ -722,11 +713,11 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) * --------------------------------------------------------------- */ -static inline void esp_basic_init(struct esp_struct * info) +static void esp_basic_init(struct esp_struct *info) { /* put ESPC in enhanced mode */ serial_out(info, UART_ESI_CMD1, ESI_SET_MODE); - + if (info->stat_flags & ESP_STAT_NEVER_DMA) serial_out(info, UART_ESI_CMD2, 0x01); else @@ -783,13 +774,13 @@ static inline void esp_basic_init(struct esp_struct * info) serial_out(info, UART_ESI_CMD2, 0xff); } -static int startup(struct esp_struct * info) +static int startup(struct esp_struct *info) { unsigned long flags; - int retval=0; - unsigned int num_chars; + int retval = 0; + unsigned int num_chars; - spin_lock_irqsave(&info->lock, flags); + spin_lock_irqsave(&info->lock, flags); if (info->flags & ASYNC_INITIALIZED) goto out; @@ -802,7 +793,8 @@ static int startup(struct esp_struct * info) } #ifdef SERIAL_DEBUG_OPEN - printk("starting up ttys%d (irq %d)...", info->line, info->irq); + printk(KERN_DEBUG "starting up ttys%d (irq %d)...", + info->line, info->irq); #endif /* Flush the RX buffer. Using the ESI flush command may cause */ @@ -863,7 +855,7 @@ static int startup(struct esp_struct * info) dma_buffer = NULL; info->stat_flags |= ESP_STAT_USE_PIO; } - + } info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; @@ -872,7 +864,7 @@ static int startup(struct esp_struct * info) serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART); serial_out(info, UART_ESI_CMD2, UART_MCR); serial_out(info, UART_ESI_CMD2, info->MCR); - + /* * Finally, enable interrupts */ @@ -881,7 +873,7 @@ static int startup(struct esp_struct * info) UART_IER_DMA_TC; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); - + if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; @@ -900,7 +892,7 @@ static int startup(struct esp_struct * info) if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; } - + /* * set the speed of the serial port */ @@ -918,7 +910,7 @@ out_unlocked: * 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 esp_struct * info) +static void shutdown(struct esp_struct *info) { unsigned long flags, f; @@ -929,7 +921,7 @@ static void shutdown(struct esp_struct * info) printk("Shutting down serial port %d (irq %d)....", info->line, info->irq); #endif - + spin_lock_irqsave(&info->lock, flags); /* * clear delta_msr_wait queue to avoid mem leaks: we may free the irq @@ -941,14 +933,14 @@ static void shutdown(struct esp_struct * info) /* stop a DMA transfer on the port being closed */ /* DMA lock is higher priority always */ if (info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) { - f=claim_dma_lock(); + f = claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); release_dma_lock(f); - + dma_bytes = 0; } - + /* * Free the IRQ */ @@ -970,7 +962,7 @@ static void shutdown(struct esp_struct * info) free_pages((unsigned long)dma_buffer, get_order(DMA_BUFFER_SZ)); dma_buffer = NULL; - } + } } if (info->xmit_buf) { @@ -992,7 +984,7 @@ static void shutdown(struct esp_struct * info) if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); - + info->flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&info->lock, flags); } @@ -1005,7 +997,7 @@ static void change_speed(struct esp_struct *info) { unsigned short port; int quot = 0; - unsigned cflag,cval; + unsigned cflag, cval; int baud, bits; unsigned char flow1 = 0, flow2 = 0; unsigned long flags; @@ -1014,14 +1006,14 @@ static void change_speed(struct esp_struct *info) return; cflag = info->tty->termios->c_cflag; port = info->port; - + /* byte size and parity */ switch (cflag & CSIZE) { - case CS5: cval = 0x00; bits = 7; break; - case CS6: cval = 0x01; bits = 8; break; - case CS7: cval = 0x02; bits = 9; break; - case CS8: cval = 0x03; bits = 10; break; - default: cval = 0x00; bits = 7; break; + case CS5: cval = 0x00; bits = 7; break; + case CS6: cval = 0x01; bits = 8; break; + case CS7: cval = 0x02; bits = 9; break; + case CS8: cval = 0x03; bits = 10; break; + default: cval = 0x00; bits = 7; break; } if (cflag & CSTOPB) { cval |= 0x04; @@ -1037,14 +1029,12 @@ static void change_speed(struct esp_struct *info) if (cflag & CMSPAR) cval |= UART_LCR_SPAR; #endif - baud = tty_get_baud_rate(info->tty); if (baud == 38400 && - ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) quot = info->custom_divisor; else { - if (baud == 134) - /* Special case since 134 is really 134.5 */ + if (baud == 134) /* Special case since 134 is really 134.5 */ quot = (2*BASE_BAUD / 269); else if (baud) quot = BASE_BAUD / baud; @@ -1052,7 +1042,12 @@ static void change_speed(struct esp_struct *info) /* If the quotient is ever zero, default to 9600 bps */ if (!quot) quot = BASE_BAUD / 9600; - + + if (baud) { + /* Actual rate */ + baud = BASE_BAUD/quot; + tty_encode_baud_rate(info->tty, baud, baud); + } info->timeout = ((1024 * HZ * bits * quot) / BASE_BAUD) + (HZ / 50); /* CTS flow control flag and modem status interrupts */ @@ -1066,10 +1061,8 @@ static void change_speed(struct esp_struct *info) info->flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) info->flags &= ~ASYNC_CHECK_CD; - else { + else info->flags |= ASYNC_CHECK_CD; - /* info->IER |= UART_IER_MSI; */ - } /* * Set up parity check flag @@ -1079,7 +1072,7 @@ static void change_speed(struct esp_struct *info) info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) info->read_status_mask |= UART_LSR_BI; - + info->ignore_status_mask = 0; #if 0 /* This should be safe, but for some broken bits of hardware... */ @@ -1092,7 +1085,7 @@ static void change_speed(struct esp_struct *info) info->ignore_status_mask |= UART_LSR_BI; info->read_status_mask |= UART_LSR_BI; /* - * If we're ignore parity and break indicators, ignore + * If we're ignore parity and break indicators, ignore * overruns too. (For real raw support). */ if (I_IGNPAR(info->tty)) { @@ -1130,19 +1123,19 @@ static void change_speed(struct esp_struct *info) serial_out(info, UART_ESI_CMD2, 0x10); serial_out(info, UART_ESI_CMD2, 0x21); switch (cflag & CSIZE) { - case CS5: - serial_out(info, UART_ESI_CMD2, 0x1f); - break; - case CS6: - serial_out(info, UART_ESI_CMD2, 0x3f); - break; - case CS7: - case CS8: - serial_out(info, UART_ESI_CMD2, 0x7f); - break; - default: - serial_out(info, UART_ESI_CMD2, 0xff); - break; + case CS5: + serial_out(info, UART_ESI_CMD2, 0x1f); + break; + case CS6: + serial_out(info, UART_ESI_CMD2, 0x3f); + break; + case CS7: + case CS8: + serial_out(info, UART_ESI_CMD2, 0x7f); + break; + default: + serial_out(info, UART_ESI_CMD2, 0xff); + break; } } @@ -1156,31 +1149,34 @@ static void change_speed(struct esp_struct *info) spin_unlock_irqrestore(&info->lock, flags); } -static void rs_put_char(struct tty_struct *tty, unsigned char ch) +static int rs_put_char(struct tty_struct *tty, unsigned char ch) { - struct esp_struct *info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned long flags; + int ret = 0; if (serial_paranoia_check(info, tty->name, "rs_put_char")) - return; + return 0; if (!info->xmit_buf) - return; + return 0; spin_lock_irqsave(&info->lock, flags); if (info->xmit_cnt < ESP_XMIT_SIZE - 1) { info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= ESP_XMIT_SIZE-1; info->xmit_cnt++; + ret = 1; } spin_unlock_irqrestore(&info->lock, flags); + return ret; } static void rs_flush_chars(struct tty_struct *tty) { - struct esp_struct *info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned long flags; - + if (serial_paranoia_check(info, tty->name, "rs_flush_chars")) return; @@ -1198,11 +1194,11 @@ out: spin_unlock_irqrestore(&info->lock, flags); } -static int rs_write(struct tty_struct * tty, +static int rs_write(struct tty_struct *tty, const unsigned char *buf, int count) { int c, t, ret = 0; - struct esp_struct *info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "rs_write")) @@ -1210,19 +1206,19 @@ static int rs_write(struct tty_struct * tty, if (!info->xmit_buf) return 0; - + while (1) { /* Thanks to R. Wolff for suggesting how to do this with */ /* interrupts enabled */ c = count; t = ESP_XMIT_SIZE - info->xmit_cnt - 1; - + if (t < c) c = t; t = ESP_XMIT_SIZE - info->xmit_head; - + if (t < c) c = t; @@ -1252,10 +1248,10 @@ static int rs_write(struct tty_struct * tty, static int rs_write_room(struct tty_struct *tty) { - struct esp_struct *info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; int ret; unsigned long flags; - + if (serial_paranoia_check(info, tty->name, "rs_write_room")) return 0; @@ -1270,8 +1266,8 @@ static int rs_write_room(struct tty_struct *tty) static int rs_chars_in_buffer(struct tty_struct *tty) { - struct esp_struct *info = (struct esp_struct *)tty->driver_data; - + struct esp_struct *info = tty->driver_data; + if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer")) return 0; return info->xmit_cnt; @@ -1279,9 +1275,9 @@ static int rs_chars_in_buffer(struct tty_struct *tty) static void rs_flush_buffer(struct tty_struct *tty) { - struct esp_struct *info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned long flags; - + if (serial_paranoia_check(info, tty->name, "rs_flush_buffer")) return; spin_lock_irqsave(&info->lock, flags); @@ -1293,20 +1289,20 @@ static void rs_flush_buffer(struct tty_struct *tty) /* * ------------------------------------------------------------ * rs_throttle() - * + * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */ -static void rs_throttle(struct tty_struct * tty) +static void rs_throttle(struct tty_struct *tty) { - struct esp_struct *info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - + printk("throttle %s: %d....\n", tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); + tty_chars_in_buffer(tty)); #endif if (serial_paranoia_check(info, tty->name, "rs_throttle")) @@ -1321,20 +1317,20 @@ static void rs_throttle(struct tty_struct * tty) spin_unlock_irqrestore(&info->lock, flags); } -static void rs_unthrottle(struct tty_struct * tty) +static void rs_unthrottle(struct tty_struct *tty) { - struct esp_struct *info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - - printk("unthrottle %s: %d....\n", tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); + + printk(KERN_DEBUG "unthrottle %s: %d....\n", tty_name(tty, buf), + tty_chars_in_buffer(tty)); #endif if (serial_paranoia_check(info, tty->name, "rs_unthrottle")) return; - + spin_lock_irqsave(&info->lock, flags); info->IER |= UART_IER_RDI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); @@ -1350,11 +1346,12 @@ static void rs_unthrottle(struct tty_struct * tty) * ------------------------------------------------------------ */ -static int get_serial_info(struct esp_struct * info, +static int get_serial_info(struct esp_struct *info, struct serial_struct __user *retinfo) { struct serial_struct tmp; - + + lock_kernel(); memset(&tmp, 0, sizeof(tmp)); tmp.type = PORT_16550A; tmp.line = info->line; @@ -1367,20 +1364,22 @@ static int get_serial_info(struct esp_struct * info, tmp.closing_wait = info->closing_wait; tmp.custom_divisor = info->custom_divisor; tmp.hub6 = 0; - if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + unlock_kernel(); + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; return 0; } -static int get_esp_config(struct esp_struct * info, +static int get_esp_config(struct esp_struct *info, struct hayes_esp_config __user *retinfo) { struct hayes_esp_config tmp; - + if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); + lock_kernel(); tmp.rx_timeout = info->config.rx_timeout; tmp.rx_trigger = info->config.rx_trigger; tmp.tx_trigger = info->config.tx_trigger; @@ -1388,11 +1387,12 @@ static int get_esp_config(struct esp_struct * info, tmp.flow_on = info->config.flow_on; tmp.pio_threshold = info->config.pio_threshold; tmp.dma_channel = (info->stat_flags & ESP_STAT_NEVER_DMA ? 0 : dma); + unlock_kernel(); return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; } -static int set_serial_info(struct esp_struct * info, +static int set_serial_info(struct esp_struct *info, struct serial_struct __user *new_info) { struct serial_struct new_serial; @@ -1401,7 +1401,7 @@ static int set_serial_info(struct esp_struct * info, int retval = 0; struct esp_struct *current_async; - if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) return -EFAULT; old_info = *info; @@ -1422,7 +1422,7 @@ static int set_serial_info(struct esp_struct * info, return -EINVAL; if (!capable(CAP_SYS_ADMIN)) { - if (change_irq || + if (change_irq || (new_serial.close_delay != info->close_delay) || ((new_serial.flags & ~ASYNC_USR_MASK) != (info->flags & ~ASYNC_USR_MASK))) @@ -1507,8 +1507,8 @@ static int set_serial_info(struct esp_struct * info, return retval; } -static int set_esp_config(struct esp_struct * info, - struct hayes_esp_config __user * new_info) +static int set_esp_config(struct esp_struct *info, + struct hayes_esp_config __user *new_info) { struct hayes_esp_config new_config; unsigned int change_dma; @@ -1550,7 +1550,6 @@ static int set_esp_config(struct esp_struct * info, if (new_config.dma_channel) { /* PIO mode to DMA mode transition OR */ /* change current DMA channel */ - current_async = ports; while (current_async) { @@ -1559,16 +1558,15 @@ static int set_esp_config(struct esp_struct * info, return -EBUSY; } else if (current_async->count) return -EBUSY; - - current_async = - current_async->next_port; + + current_async = current_async->next_port; } shutdown(info); dma = new_config.dma_channel; info->stat_flags &= ~ESP_STAT_NEVER_DMA; - - /* all ports must use the same DMA channel */ + + /* all ports must use the same DMA channel */ spin_lock_irqsave(&info->lock, flags); current_async = ports; @@ -1580,7 +1578,6 @@ static int set_esp_config(struct esp_struct * info, spin_unlock_irqrestore(&info->lock, flags); } else { /* DMA mode to PIO mode only */ - if (info->count > 1) return -EBUSY; @@ -1596,8 +1593,6 @@ static int set_esp_config(struct esp_struct * info, if ((new_config.flow_off != info->config.flow_off) || (new_config.flow_on != info->config.flow_on)) { - unsigned long flags; - info->config.flow_off = new_config.flow_off; info->config.flow_on = new_config.flow_on; @@ -1612,8 +1607,6 @@ static int set_esp_config(struct esp_struct * info, if ((new_config.rx_trigger != info->config.rx_trigger) || (new_config.tx_trigger != info->config.tx_trigger)) { - unsigned long flags; - info->config.rx_trigger = new_config.rx_trigger; info->config.tx_trigger = new_config.tx_trigger; spin_lock_irqsave(&info->lock, flags); @@ -1628,8 +1621,6 @@ static int set_esp_config(struct esp_struct * info, } if (new_config.rx_timeout != info->config.rx_timeout) { - unsigned long flags; - info->config.rx_timeout = new_config.rx_timeout; spin_lock_irqsave(&info->lock, flags); @@ -1657,9 +1648,9 @@ static int set_esp_config(struct esp_struct * info, * 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. + * allows an RS485 driver to be written in user space. */ -static int get_lsr_info(struct esp_struct * info, unsigned int __user *value) +static int get_lsr_info(struct esp_struct *info, unsigned int __user *value) { unsigned char status; unsigned int result; @@ -1670,17 +1661,17 @@ static int get_lsr_info(struct esp_struct * info, unsigned int __user *value) status = serial_in(info, UART_ESI_STAT1); spin_unlock_irqrestore(&info->lock, flags); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); - return put_user(result,value); + return put_user(result, value); } static int esp_tiocmget(struct tty_struct *tty, struct file *file) { - struct esp_struct * info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned char control, status; unsigned long flags; - if (serial_paranoia_check(info, tty->name, __FUNCTION__)) + if (serial_paranoia_check(info, tty->name, __func__)) return -ENODEV; if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; @@ -1703,10 +1694,10 @@ static int esp_tiocmget(struct tty_struct *tty, struct file *file) static int esp_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { - struct esp_struct * info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned long flags; - if (serial_paranoia_check(info, tty->name, __FUNCTION__)) + if (serial_paranoia_check(info, tty->name, __func__)) return -ENODEV; if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; @@ -1736,9 +1727,9 @@ static int esp_tiocmset(struct tty_struct *tty, struct file *file, */ static void esp_break(struct tty_struct *tty, int break_state) { - struct esp_struct * info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned long flags; - + if (serial_paranoia_check(info, tty->name, "esp_break")) return; @@ -1758,14 +1749,15 @@ static void esp_break(struct tty_struct *tty, int break_state) } } -static int rs_ioctl(struct tty_struct *tty, struct file * file, +static int rs_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { - struct esp_struct * info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct __user *p_cuser; /* user space */ void __user *argp = (void __user *)arg; unsigned long flags; + int ret; if (serial_paranoia_check(info, tty->name, "rs_ioctl")) return -ENODEV; @@ -1778,97 +1770,93 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; } - - switch (cmd) { - case TIOCGSERIAL: - return get_serial_info(info, argp); - case TIOCSSERIAL: - return set_serial_info(info, argp); - case TIOCSERCONFIG: - /* do not reconfigure after initial configuration */ - return 0; - - case TIOCSERGWILD: - return put_user(0L, (unsigned long __user *)argp); - case TIOCSERGETLSR: /* Get line status register */ - return get_lsr_info(info, argp); - - case TIOCSERSWILD: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - return 0; - - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ - case TIOCMIWAIT: + switch (cmd) { + case TIOCGSERIAL: + return get_serial_info(info, argp); + case TIOCSSERIAL: + lock_kernel(); + ret = set_serial_info(info, argp); + unlock_kernel(); + return ret; + case TIOCSERGWILD: + return put_user(0L, (unsigned long __user *)argp); + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, argp); + case TIOCSERSWILD: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return 0; + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + spin_lock_irqsave(&info->lock, flags); + cprev = info->icount; /* note the counters on entry */ + spin_unlock_irqrestore(&info->lock, flags); + while (1) { + /* FIXME: convert to new style wakeup */ + interruptible_sleep_on(&info->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; spin_lock_irqsave(&info->lock, flags); - cprev = info->icount; /* note the counters on entry */ + cnow = info->icount; /* atomic copy */ spin_unlock_irqrestore(&info->lock, flags); - while (1) { - /* FIXME: convert to new style wakeup */ - interruptible_sleep_on(&info->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - spin_lock_irqsave(&info->lock, flags); - cnow = info->icount; /* atomic copy */ - spin_unlock_irqrestore(&info->lock, flags); - if (cnow.rng == cprev.rng && - cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && - cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - if (((arg & TIOCM_RNG) && - (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && - (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && - (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && - (cnow.cts != cprev.cts)) ) { - return 0; - } - cprev = cnow; + if (cnow.rng == cprev.rng && + cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && + cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if (((arg & TIOCM_RNG) && + (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && + (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && + (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && + (cnow.cts != cprev.cts))) { + return 0; } - /* NOTREACHED */ - - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ - case TIOCGICOUNT: - spin_lock_irqsave(&info->lock, flags); - cnow = info->icount; - spin_unlock_irqrestore(&info->lock, flags); - p_cuser = argp; - if (put_user(cnow.cts, &p_cuser->cts) || - put_user(cnow.dsr, &p_cuser->dsr) || - put_user(cnow.rng, &p_cuser->rng) || - put_user(cnow.dcd, &p_cuser->dcd)) - return -EFAULT; - + cprev = cnow; + } + /* NOTREACHED */ + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + spin_lock_irqsave(&info->lock, flags); + cnow = info->icount; + spin_unlock_irqrestore(&info->lock, flags); + p_cuser = argp; + if (put_user(cnow.cts, &p_cuser->cts) || + put_user(cnow.dsr, &p_cuser->dsr) || + put_user(cnow.rng, &p_cuser->rng) || + put_user(cnow.dcd, &p_cuser->dcd)) + return -EFAULT; return 0; case TIOCGHAYESESP: return get_esp_config(info, argp); case TIOCSHAYESESP: - return set_esp_config(info, argp); - - default: - return -ENOIOCTLCMD; - } + lock_kernel(); + ret = set_esp_config(info, argp); + unlock_kernel(); + return ret; + default: + return -ENOIOCTLCMD; + } return 0; } static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { - struct esp_struct *info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned long flags; change_speed(info); @@ -1905,32 +1893,33 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* * ------------------------------------------------------------ * rs_close() - * + * * This routine is called when the serial port gets closed. First, we * wait for the last remaining data to be sent. Then, we unlink its * async structure from the interrupt chain if necessary, and we free * that IRQ if nothing is left in the chain. * ------------------------------------------------------------ */ -static void rs_close(struct tty_struct *tty, struct file * filp) +static void rs_close(struct tty_struct *tty, struct file *filp) { - struct esp_struct * info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned long flags; if (!info || serial_paranoia_check(info, tty->name, "rs_close")) return; - + spin_lock_irqsave(&info->lock, flags); - + if (tty_hung_up_p(filp)) { DBG_CNT("before DEC-hung"); goto out; } - + #ifdef SERIAL_DEBUG_OPEN - printk("rs_close ttys%d, count = %d\n", info->line, info->count); + printk(KERN_DEBUG "rs_close ttys%d, count = %d\n", + info->line, info->count); #endif - if ((tty->count == 1) && (info->count != 1)) { + 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 @@ -1938,12 +1927,11 @@ static void rs_close(struct tty_struct *tty, struct file * filp) * one, we've got real problems, since it means the * serial port won't be shutdown. */ - printk("rs_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); + printk(KERN_DEBUG "rs_close: bad serial port count; tty->count is 1, info->count is %d\n", info->count); info->count = 1; } if (--info->count < 0) { - printk("rs_close: bad serial port count for ttys%d: %d\n", + printk(KERN_ERR "rs_close: bad serial port count for ttys%d: %d\n", info->line, info->count); info->count = 0; } @@ -1955,7 +1943,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) spin_unlock_irqrestore(&info->lock, flags); /* - * Now we wait for the transmit buffer to clear; and we notify + * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; @@ -1990,16 +1978,14 @@ static void rs_close(struct tty_struct *tty, struct file * filp) rs_wait_until_sent(tty, info->timeout); } shutdown(info); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + rs_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; info->tty = NULL; if (info->blocked_open) { - if (info->close_delay) { + if (info->close_delay) msleep_interruptible(jiffies_to_msecs(info->close_delay)); - } wake_up_interruptible(&info->open_wait); } info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); @@ -2012,7 +1998,7 @@ out: static void rs_wait_until_sent(struct tty_struct *tty, int timeout) { - struct esp_struct *info = (struct esp_struct *)tty->driver_data; + struct esp_struct *info = tty->driver_data; unsigned long orig_jiffies, char_time; unsigned long flags; @@ -2036,10 +2022,10 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) msleep_interruptible(jiffies_to_msecs(char_time)); if (signal_pending(current)) - break; + return; if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; + return; spin_lock_irqsave(&info->lock, flags); serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND); @@ -2054,11 +2040,11 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) */ static void esp_hangup(struct tty_struct *tty) { - struct esp_struct * info = (struct esp_struct *)tty->driver_data; - + struct esp_struct *info = tty->driver_data; + if (serial_paranoia_check(info, tty->name, "esp_hangup")) return; - + rs_flush_buffer(tty); shutdown(info); info->count = 0; @@ -2072,7 +2058,7 @@ static void esp_hangup(struct tty_struct *tty) * esp_open() and friends * ------------------------------------------------------------ */ -static int block_til_ready(struct tty_struct *tty, struct file * filp, +static int block_til_ready(struct tty_struct *tty, struct file *filp, struct esp_struct *info) { DECLARE_WAITQUEUE(wait, current); @@ -2121,11 +2107,11 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, retval = 0; add_wait_queue(&info->open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready before block: ttys%d, count = %d\n", + printk(KERN_DEBUG "block_til_ready before block: ttys%d, count = %d\n", info->line, info->count); #endif spin_lock_irqsave(&info->lock, flags); - if (!tty_hung_up_p(filp)) + if (!tty_hung_up_p(filp)) info->count--; info->blocked_open++; while (1) { @@ -2147,7 +2133,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, if (info->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else - retval = -ERESTARTSYS; + retval = -ERESTARTSYS; #else retval = -EAGAIN; #endif @@ -2166,7 +2152,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, break; } #ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready blocking: ttys%d, count = %d\n", + printk(KERN_DEBUG "block_til_ready blocking: ttys%d, count = %d\n", info->line, info->count); #endif spin_unlock_irqrestore(&info->lock, flags); @@ -2180,14 +2166,14 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, info->blocked_open--; spin_unlock_irqrestore(&info->lock, flags); #ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: ttys%d, count = %d\n", + printk(KERN_DEBUG "block_til_ready after blocking: ttys%d, count = %d\n", info->line, info->count); #endif if (retval) return retval; info->flags |= ASYNC_NORMAL_ACTIVE; return 0; -} +} /* * This routine is called whenever a serial port is opened. It @@ -2195,7 +2181,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, * the IRQ chain. It also performs the serial-specific * initialization for the tty structure. */ -static int esp_open(struct tty_struct *tty, struct file * filp) +static int esp_open(struct tty_struct *tty, struct file *filp) { struct esp_struct *info; int retval, line; @@ -2218,7 +2204,7 @@ static int esp_open(struct tty_struct *tty, struct file * filp) } #ifdef SERIAL_DEBUG_OPEN - printk("esp_open %s, count = %d\n", tty->name, info->count); + printk(KERN_DEBUG "esp_open %s, count = %d\n", tty->name, info->count); #endif spin_lock_irqsave(&info->lock, flags); info->count++; @@ -2226,7 +2212,7 @@ static int esp_open(struct tty_struct *tty, struct file * filp) info->tty = tty; spin_unlock_irqrestore(&info->lock, flags); - + /* * Start up serial port */ @@ -2237,14 +2223,13 @@ static int esp_open(struct tty_struct *tty, struct file * filp) retval = block_til_ready(tty, filp, info); if (retval) { #ifdef SERIAL_DEBUG_OPEN - printk("esp_open returning after block_til_ready with %d\n", + printk(KERN_DEBUG "esp_open returning after block_til_ready with %d\n", retval); #endif return retval; } - #ifdef SERIAL_DEBUG_OPEN - printk("esp_open %s successful...", tty->name); + printk(KERN_DEBUG "esp_open %s successful...", tty->name); #endif return 0; } @@ -2262,10 +2247,10 @@ static int esp_open(struct tty_struct *tty, struct file * filp) * number, and identifies which options were configured into this * driver. */ - -static inline void show_serial_version(void) + +static void show_serial_version(void) { - printk(KERN_INFO "%s version %s (DMA %u)\n", + printk(KERN_INFO "%s version %s (DMA %u)\n", serial_name, serial_version, dma); } @@ -2273,7 +2258,7 @@ static inline void show_serial_version(void) * This routine is called by espserial_init() to initialize a specific serial * port. */ -static inline int autoconfig(struct esp_struct * info) +static int autoconfig(struct esp_struct *info) { int port_detected = 0; unsigned long flags; @@ -2349,14 +2334,14 @@ static const struct tty_operations esp_ops = { static int __init espserial_init(void) { int i, offset; - struct esp_struct * info; + struct esp_struct *info; struct esp_struct *last_primary = NULL; - int esp[] = {0x100,0x140,0x180,0x200,0x240,0x280,0x300,0x380}; + int esp[] = { 0x100, 0x140, 0x180, 0x200, 0x240, 0x280, 0x300, 0x380 }; esp_driver = alloc_tty_driver(NR_PORTS); if (!esp_driver) return -ENOMEM; - + for (i = 0; i < NR_PRIMARY; i++) { if (irq[i] != 0) { if ((irq[i] < 2) || (irq[i] > 15) || (irq[i] == 6) || @@ -2378,20 +2363,20 @@ static int __init espserial_init(void) if ((flow_off < 1) || (flow_off > 1023)) flow_off = 1016; - + if ((flow_on < 1) || (flow_on > 1023)) flow_on = 944; if ((rx_timeout < 0) || (rx_timeout > 255)) rx_timeout = 128; - + if (flow_on >= flow_off) flow_on = flow_off - 1; show_serial_version(); /* Initialize the tty_driver structure */ - + esp_driver->owner = THIS_MODULE; esp_driver->name = "ttyP"; esp_driver->major = ESP_IN_MAJOR; @@ -2401,10 +2386,11 @@ static int __init espserial_init(void) esp_driver->init_termios = tty_std_termios; esp_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + esp_driver->init_termios.c_ispeed = 9600; + esp_driver->init_termios.c_ospeed = 9600; esp_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(esp_driver, &esp_ops); - if (tty_register_driver(esp_driver)) - { + if (tty_register_driver(esp_driver)) { printk(KERN_ERR "Couldn't register esp serial driver"); put_tty_driver(esp_driver); return 1; @@ -2412,8 +2398,7 @@ static int __init espserial_init(void) info = kzalloc(sizeof(struct esp_struct), GFP_KERNEL); - if (!info) - { + if (!info) { printk(KERN_ERR "Couldn't allocate memory for esp serial device information\n"); tty_unregister_driver(esp_driver); put_tty_driver(esp_driver); @@ -2476,10 +2461,8 @@ static int __init espserial_init(void) info->stat_flags |= ESP_STAT_NEVER_DMA; info = kzalloc(sizeof(struct esp_struct), GFP_KERNEL); - if (!info) - { - printk(KERN_ERR "Couldn't allocate memory for esp serial device information\n"); - + if (!info) { + printk(KERN_ERR "Couldn't allocate memory for esp serial device information\n"); /* allow use of the already detected ports */ return 0; } @@ -2503,22 +2486,20 @@ static int __init espserial_init(void) return 0; } -static void __exit espserial_exit(void) +static void __exit espserial_exit(void) { int e1; struct esp_struct *temp_async; struct esp_pio_buffer *pio_buf; - /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ - if ((e1 = tty_unregister_driver(esp_driver))) - printk("SERIAL: failed to unregister serial driver (%d)\n", - e1); + e1 = tty_unregister_driver(esp_driver); + if (e1) + printk(KERN_ERR "esp: failed to unregister driver (%d)\n", e1); put_tty_driver(esp_driver); while (ports) { - if (ports->port) { + if (ports->port) release_region(ports->port, REGION_SIZE); - } temp_async = ports->next_port; kfree(ports); ports = temp_async; diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 7ed7da1d99c..252f73e4859 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -40,27 +40,27 @@ static int gs_debug; #define gs_dprintk(f, str...) /* nothing */ #endif -#define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter %s\n", __FUNCTION__) -#define func_exit() gs_dprintk (GS_DEBUG_FLOW, "gs: exit %s\n", __FUNCTION__) +#define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter %s\n", __func__) +#define func_exit() gs_dprintk (GS_DEBUG_FLOW, "gs: exit %s\n", __func__) #define RS_EVENT_WRITE_WAKEUP 1 module_param(gs_debug, int, 0644); -void gs_put_char(struct tty_struct * tty, unsigned char ch) +int gs_put_char(struct tty_struct * tty, unsigned char ch) { struct gs_port *port; func_enter (); - if (!tty) return; + if (!tty) return 0; port = tty->driver_data; - if (!port) return; + if (!port) return 0; - if (! (port->flags & ASYNC_INITIALIZED)) return; + if (! (port->flags & ASYNC_INITIALIZED)) return 0; /* Take a lock on the serial tranmit buffer! */ mutex_lock(& port->port_write_mutex); @@ -68,7 +68,7 @@ void gs_put_char(struct tty_struct * tty, unsigned char ch) if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { /* Sorry, buffer is full, drop character. Update statistics???? -- REW */ mutex_unlock(&port->port_write_mutex); - return; + return 0; } port->xmit_buf[port->xmit_head++] = ch; @@ -77,6 +77,7 @@ void gs_put_char(struct tty_struct * tty, unsigned char ch) mutex_unlock(&port->port_write_mutex); func_exit (); + return 1; } @@ -586,8 +587,7 @@ void gs_close(struct tty_struct * tty, struct file * filp) port->flags &= ~GS_ACTIVE; - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + gs_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 1399971be68..e7fb0bca366 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -308,7 +308,7 @@ static int hpet_mmap(struct file *file, struct vm_area_struct *vma) if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, PAGE_SIZE, vma->vm_page_prot)) { printk(KERN_ERR "%s: io_remap_pfn_range failed\n", - __FUNCTION__); + __func__); return -EAGAIN; } @@ -748,7 +748,7 @@ int hpet_alloc(struct hpet_data *hdp) */ if (hpet_is_known(hdp)) { printk(KERN_DEBUG "%s: duplicate HPET ignored\n", - __FUNCTION__); + __func__); return 0; } @@ -869,7 +869,7 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) if (hpet_is_known(hdp)) { printk(KERN_DEBUG "%s: 0x%lx is busy\n", - __FUNCTION__, hdp->hd_phys_address); + __func__, hdp->hd_phys_address); iounmap(hdp->hd_address); return AE_ALREADY_EXISTS; } @@ -886,7 +886,7 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) if (hpet_is_known(hdp)) { printk(KERN_DEBUG "%s: 0x%lx is busy\n", - __FUNCTION__, hdp->hd_phys_address); + __func__, hdp->hd_phys_address); iounmap(hdp->hd_address); return AE_ALREADY_EXISTS; } @@ -925,7 +925,7 @@ static int hpet_acpi_add(struct acpi_device *device) return -ENODEV; if (!data.hd_address || !data.hd_nirqs) { - printk("%s: no address or irqs in _CRS\n", __FUNCTION__); + printk("%s: no address or irqs in _CRS\n", __func__); return -ENODEV; } diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c index d5a752da322..59c6f9ab94e 100644 --- a/drivers/char/hvsi.c +++ b/drivers/char/hvsi.c @@ -246,7 +246,7 @@ static void compact_inbuf(struct hvsi_struct *hp, uint8_t *read_to) { int remaining = (int)(hp->inbuf_end - read_to); - pr_debug("%s: %i chars remain\n", __FUNCTION__, remaining); + pr_debug("%s: %i chars remain\n", __func__, remaining); if (read_to != hp->inbuf) memmove(hp->inbuf, read_to, remaining); @@ -365,7 +365,7 @@ static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) packet.u.version = HVSI_VERSION; packet.query_seqno = query_seqno+1; - pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); + pr_debug("%s: sending %i bytes\n", __func__, packet.len); dbg_dump_hex((uint8_t*)&packet, packet.len); wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); @@ -437,7 +437,7 @@ static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, return NULL; if (overflow > 0) { - pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __FUNCTION__); + pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __func__); datalen = TTY_THRESHOLD_THROTTLE; } @@ -448,7 +448,7 @@ static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, * we still have more data to deliver, so we need to save off the * overflow and send it later */ - pr_debug("%s: deferring overflow\n", __FUNCTION__); + pr_debug("%s: deferring overflow\n", __func__); memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow); hp->n_throttle = overflow; } @@ -474,11 +474,11 @@ static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); if (chunklen == 0) { - pr_debug("%s: 0-length read\n", __FUNCTION__); + pr_debug("%s: 0-length read\n", __func__); return 0; } - pr_debug("%s: got %i bytes\n", __FUNCTION__, chunklen); + pr_debug("%s: got %i bytes\n", __func__, chunklen); dbg_dump_hex(hp->inbuf_end, chunklen); hp->inbuf_end += chunklen; @@ -495,7 +495,7 @@ static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, continue; } - pr_debug("%s: handling %i-byte packet\n", __FUNCTION__, + pr_debug("%s: handling %i-byte packet\n", __func__, len_packet(packet)); dbg_dump_packet(packet); @@ -526,7 +526,7 @@ static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, packet += len_packet(packet); if (*hangup || *handshake) { - pr_debug("%s: hangup or handshake\n", __FUNCTION__); + pr_debug("%s: hangup or handshake\n", __func__); /* * we need to send the hangup now before receiving any more data. * If we get "data, hangup, data", we can't deliver the second @@ -543,7 +543,7 @@ static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, static void hvsi_send_overflow(struct hvsi_struct *hp) { - pr_debug("%s: delivering %i bytes overflow\n", __FUNCTION__, + pr_debug("%s: delivering %i bytes overflow\n", __func__, hp->n_throttle); hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); @@ -563,7 +563,7 @@ static irqreturn_t hvsi_interrupt(int irq, void *arg) unsigned long flags; int again = 1; - pr_debug("%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); while (again) { spin_lock_irqsave(&hp->lock, flags); @@ -647,7 +647,7 @@ static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) packet.seqno = atomic_inc_return(&hp->seqno); packet.verb = verb; - pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); + pr_debug("%s: sending %i bytes\n", __func__, packet.len); dbg_dump_hex((uint8_t*)&packet, packet.len); wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); @@ -674,7 +674,7 @@ static int hvsi_get_mctrl(struct hvsi_struct *hp) return ret; } - pr_debug("%s: mctrl 0x%x\n", __FUNCTION__, hp->mctrl); + pr_debug("%s: mctrl 0x%x\n", __func__, hp->mctrl); return 0; } @@ -694,7 +694,7 @@ static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) if (mctrl & TIOCM_DTR) packet.word = HVSI_TSDTR; - pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); + pr_debug("%s: sending %i bytes\n", __func__, packet.len); dbg_dump_hex((uint8_t*)&packet, packet.len); wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); @@ -790,7 +790,7 @@ static void hvsi_close_protocol(struct hvsi_struct *hp) packet.len = 6; packet.verb = VSV_CLOSE_PROTOCOL; - pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); + pr_debug("%s: sending %i bytes\n", __func__, packet.len); dbg_dump_hex((uint8_t*)&packet, packet.len); hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); @@ -803,7 +803,7 @@ static int hvsi_open(struct tty_struct *tty, struct file *filp) int line = tty->index; int ret; - pr_debug("%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); if (line < 0 || line >= hvsi_count) return -ENODEV; @@ -868,7 +868,7 @@ static void hvsi_close(struct tty_struct *tty, struct file *filp) struct hvsi_struct *hp = tty->driver_data; unsigned long flags; - pr_debug("%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); if (tty_hung_up_p(filp)) return; @@ -920,7 +920,7 @@ static void hvsi_hangup(struct tty_struct *tty) struct hvsi_struct *hp = tty->driver_data; unsigned long flags; - pr_debug("%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); spin_lock_irqsave(&hp->lock, flags); @@ -942,7 +942,7 @@ static void hvsi_push(struct hvsi_struct *hp) n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf); if (n > 0) { /* success */ - pr_debug("%s: wrote %i chars\n", __FUNCTION__, n); + pr_debug("%s: wrote %i chars\n", __func__, n); hp->n_outbuf = 0; } else if (n == -EIO) { __set_state(hp, HVSI_FSP_DIED); @@ -965,7 +965,7 @@ static void hvsi_write_worker(struct work_struct *work) spin_lock_irqsave(&hp->lock, flags); - pr_debug("%s: %i chars in buffer\n", __FUNCTION__, hp->n_outbuf); + pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); if (!is_open(hp)) { /* @@ -983,7 +983,7 @@ static void hvsi_write_worker(struct work_struct *work) schedule_delayed_work(&hp->writer, 10); else { #ifdef DEBUG - pr_debug("%s: outbuf emptied after %li jiffies\n", __FUNCTION__, + pr_debug("%s: outbuf emptied after %li jiffies\n", __func__, jiffies - start_j); start_j = 0; #endif /* DEBUG */ @@ -1020,11 +1020,11 @@ static int hvsi_write(struct tty_struct *tty, spin_lock_irqsave(&hp->lock, flags); - pr_debug("%s: %i chars in buffer\n", __FUNCTION__, hp->n_outbuf); + pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); if (!is_open(hp)) { /* we're either closing or not yet open; don't accept data */ - pr_debug("%s: not open\n", __FUNCTION__); + pr_debug("%s: not open\n", __func__); goto out; } @@ -1058,7 +1058,7 @@ out: spin_unlock_irqrestore(&hp->lock, flags); if (total != origcount) - pr_debug("%s: wanted %i, only wrote %i\n", __FUNCTION__, origcount, + pr_debug("%s: wanted %i, only wrote %i\n", __func__, origcount, total); return total; @@ -1072,7 +1072,7 @@ static void hvsi_throttle(struct tty_struct *tty) { struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; - pr_debug("%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); } @@ -1083,7 +1083,7 @@ static void hvsi_unthrottle(struct tty_struct *tty) unsigned long flags; int shouldflip = 0; - pr_debug("%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); spin_lock_irqsave(&hp->lock, flags); if (hp->n_throttle) { @@ -1302,7 +1302,7 @@ static int __init hvsi_console_init(void) hp->virq = irq_create_mapping(NULL, irq[0]); if (hp->virq == NO_IRQ) { printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", - __FUNCTION__, irq[0]); + __func__, irq[0]); continue; } diff --git a/drivers/char/ip2/i2ellis.c b/drivers/char/ip2/i2ellis.c index 61ef013b844..3601017f58c 100644 --- a/drivers/char/ip2/i2ellis.c +++ b/drivers/char/ip2/i2ellis.c @@ -53,7 +53,7 @@ static int ii2Safe; // Safe I/O address for delay routine static int iiDelayed; // Set when the iiResetDelay function is // called. Cleared when ANY board is reset. -static rwlock_t Dl_spinlock; +static DEFINE_RWLOCK(Dl_spinlock); //******** //* Code * @@ -82,7 +82,6 @@ static rwlock_t Dl_spinlock; static void iiEllisInit(void) { - LOCK_INIT(&Dl_spinlock); } //****************************************************************************** @@ -132,7 +131,7 @@ iiSetAddress( i2eBordStrPtr pB, int address, delayFunc_t delay ) || (address & 0x7) ) { - COMPLETE(pB,I2EE_BADADDR); + I2_COMPLETE(pB, I2EE_BADADDR); } // Initialize accelerators @@ -152,7 +151,7 @@ iiSetAddress( i2eBordStrPtr pB, int address, delayFunc_t delay ) pB->i2eValid = I2E_MAGIC; pB->i2eState = II_STATE_COLD; - COMPLETE(pB, I2EE_GOOD); + I2_COMPLETE(pB, I2EE_GOOD); } //****************************************************************************** @@ -177,12 +176,12 @@ iiReset(i2eBordStrPtr pB) // Magic number should be set, else even the address is suspect if (pB->i2eValid != I2E_MAGIC) { - COMPLETE(pB, I2EE_BADMAGIC); + I2_COMPLETE(pB, I2EE_BADMAGIC); } - OUTB(pB->i2eBase + FIFO_RESET, 0); // Any data will do + outb(0, pB->i2eBase + FIFO_RESET); /* Any data will do */ iiDelay(pB, 50); // Pause between resets - OUTB(pB->i2eBase + FIFO_RESET, 0); // Second reset + outb(0, pB->i2eBase + FIFO_RESET); /* Second reset */ // We must wait before even attempting to read anything from the FIFO: the // board's P.O.S.T may actually attempt to read and write its end of the @@ -203,7 +202,7 @@ iiReset(i2eBordStrPtr pB) // Ensure anything which would have been of use to standard loadware is // blanked out, since board has now forgotten everything!. - pB->i2eUsingIrq = IRQ_UNDEFINED; // Not set up to use an interrupt yet + pB->i2eUsingIrq = I2_IRQ_UNDEFINED; /* to not use an interrupt so far */ pB->i2eWaitingForEmptyFifo = 0; pB->i2eOutMailWaiting = 0; pB->i2eChannelPtr = NULL; @@ -215,7 +214,7 @@ iiReset(i2eBordStrPtr pB) pB->i2eFatalTrap = NULL; pB->i2eFatal = 0; - COMPLETE(pB, I2EE_GOOD); + I2_COMPLETE(pB, I2EE_GOOD); } //****************************************************************************** @@ -235,14 +234,14 @@ static int iiResetDelay(i2eBordStrPtr pB) { if (pB->i2eValid != I2E_MAGIC) { - COMPLETE(pB, I2EE_BADMAGIC); + I2_COMPLETE(pB, I2EE_BADMAGIC); } if (pB->i2eState != II_STATE_RESET) { - COMPLETE(pB, I2EE_BADSTATE); + I2_COMPLETE(pB, I2EE_BADSTATE); } iiDelay(pB,2000); /* Now we wait for two seconds. */ iiDelayed = 1; /* Delay has been called: ok to initialize */ - COMPLETE(pB, I2EE_GOOD); + I2_COMPLETE(pB, I2EE_GOOD); } //****************************************************************************** @@ -273,12 +272,12 @@ iiInitialize(i2eBordStrPtr pB) if (pB->i2eValid != I2E_MAGIC) { - COMPLETE(pB, I2EE_BADMAGIC); + I2_COMPLETE(pB, I2EE_BADMAGIC); } if (pB->i2eState != II_STATE_RESET || !iiDelayed) { - COMPLETE(pB, I2EE_BADSTATE); + I2_COMPLETE(pB, I2EE_BADSTATE); } // In case there is a failure short of our completely reading the power-up @@ -291,13 +290,12 @@ iiInitialize(i2eBordStrPtr pB) for (itemp = 0; itemp < sizeof(porStr); itemp++) { // We expect the entire message is ready. - if (HAS_NO_INPUT(pB)) - { + if (!I2_HAS_INPUT(pB)) { pB->i2ePomSize = itemp; - COMPLETE(pB, I2EE_PORM_SHORT); + I2_COMPLETE(pB, I2EE_PORM_SHORT); } - pB->i2ePom.c[itemp] = c = BYTE_FROM(pB); + pB->i2ePom.c[itemp] = c = inb(pB->i2eData); // We check the magic numbers as soon as they are supposed to be read // (rather than after) to minimize effect of reading something we @@ -306,22 +304,22 @@ iiInitialize(i2eBordStrPtr pB) (itemp == POR_2_INDEX && c != POR_MAGIC_2)) { pB->i2ePomSize = itemp+1; - COMPLETE(pB, I2EE_BADMAGIC); + I2_COMPLETE(pB, I2EE_BADMAGIC); } } pB->i2ePomSize = itemp; // Ensure that this was all the data... - if (HAS_INPUT(pB)) - COMPLETE(pB, I2EE_PORM_LONG); + if (I2_HAS_INPUT(pB)) + I2_COMPLETE(pB, I2EE_PORM_LONG); // For now, we'll fail to initialize if P.O.S.T reports bad chip mapper: // Implying we will not be able to download any code either: That's ok: the // condition is pretty explicit. if (pB->i2ePom.e.porDiag1 & POR_BAD_MAPPER) { - COMPLETE(pB, I2EE_POSTERR); + I2_COMPLETE(pB, I2EE_POSTERR); } // Determine anything which must be done differently depending on the family @@ -332,7 +330,7 @@ iiInitialize(i2eBordStrPtr pB) pB->i2eFifoStyle = FIFO_II; pB->i2eFifoSize = 512; // 512 bytes, always - pB->i2eDataWidth16 = NO; + pB->i2eDataWidth16 = false; pB->i2eMaxIrq = 15; // Because board cannot tell us it is in an 8-bit // slot, we do allow it to be done (documentation!) @@ -354,7 +352,7 @@ iiInitialize(i2eBordStrPtr pB) // should always be consistent for IntelliPort-II. Ditto below... if (pB->i2ePom.e.porPorts1 != 4) { - COMPLETE(pB, I2EE_INCONSIST); + I2_COMPLETE(pB, I2EE_INCONSIST); } break; @@ -364,7 +362,7 @@ iiInitialize(i2eBordStrPtr pB) pB->i2eChannelMap[0] = 0xff; // Eight port if (pB->i2ePom.e.porPorts1 != 8) { - COMPLETE(pB, I2EE_INCONSIST); + I2_COMPLETE(pB, I2EE_INCONSIST); } break; @@ -373,7 +371,7 @@ iiInitialize(i2eBordStrPtr pB) pB->i2eChannelMap[0] = 0x3f; // Six Port if (pB->i2ePom.e.porPorts1 != 6) { - COMPLETE(pB, I2EE_INCONSIST); + I2_COMPLETE(pB, I2EE_INCONSIST); } break; } @@ -402,7 +400,7 @@ iiInitialize(i2eBordStrPtr pB) if (itemp < 8 || itemp > 15) { - COMPLETE(pB, I2EE_INCONSIST); + I2_COMPLETE(pB, I2EE_INCONSIST); } pB->i2eFifoSize = (1 << itemp); @@ -450,26 +448,26 @@ iiInitialize(i2eBordStrPtr pB) switch (pB->i2ePom.e.porBus & (POR_BUS_SLOT16 | POR_BUS_DIP16) ) { case POR_BUS_SLOT16 | POR_BUS_DIP16: - pB->i2eDataWidth16 = YES; + pB->i2eDataWidth16 = true; pB->i2eMaxIrq = 15; break; case POR_BUS_SLOT16: - pB->i2eDataWidth16 = NO; + pB->i2eDataWidth16 = false; pB->i2eMaxIrq = 15; break; case 0: case POR_BUS_DIP16: // In an 8-bit slot, DIP switch don't care. default: - pB->i2eDataWidth16 = NO; + pB->i2eDataWidth16 = false; pB->i2eMaxIrq = 7; break; } break; // POR_ID_FIIEX case default: // Unknown type of board - COMPLETE(pB, I2EE_BAD_FAMILY); + I2_COMPLETE(pB, I2EE_BAD_FAMILY); break; } // End the switch based on family @@ -483,17 +481,14 @@ iiInitialize(i2eBordStrPtr pB) { case POR_BUS_T_ISA: case POR_BUS_T_UNK: // If the type of bus is undeclared, assume ok. - pB->i2eChangeIrq = YES; - break; case POR_BUS_T_MCA: case POR_BUS_T_EISA: - pB->i2eChangeIrq = NO; break; default: - COMPLETE(pB, I2EE_BADBUS); + I2_COMPLETE(pB, I2EE_BADBUS); } - if (pB->i2eDataWidth16 == YES) + if (pB->i2eDataWidth16) { pB->i2eWriteBuf = iiWriteBuf16; pB->i2eReadBuf = iiReadBuf16; @@ -529,7 +524,7 @@ iiInitialize(i2eBordStrPtr pB) break; default: - COMPLETE(pB, I2EE_INCONSIST); + I2_COMPLETE(pB, I2EE_INCONSIST); } // Initialize state information. @@ -549,7 +544,7 @@ iiInitialize(i2eBordStrPtr pB) // Everything is ok now, return with good status/ pB->i2eValid = I2E_MAGIC; - COMPLETE(pB, I2EE_GOOD); + I2_COMPLETE(pB, I2EE_GOOD); } //****************************************************************************** @@ -658,7 +653,7 @@ ii2DelayIO(unsigned int mseconds) while(mseconds--) { int i = ii2DelValue; while ( i-- ) { - INB ( ii2Safe ); + inb(ii2Safe); } } } @@ -709,11 +704,11 @@ iiWriteBuf16(i2eBordStrPtr pB, unsigned char *address, int count) { // Rudimentary sanity checking here. if (pB->i2eValid != I2E_MAGIC) - COMPLETE(pB, I2EE_INVALID); + I2_COMPLETE(pB, I2EE_INVALID); - OUTSW ( pB->i2eData, address, count); + I2_OUTSW(pB->i2eData, address, count); - COMPLETE(pB, I2EE_GOOD); + I2_COMPLETE(pB, I2EE_GOOD); } //****************************************************************************** @@ -738,11 +733,11 @@ iiWriteBuf8(i2eBordStrPtr pB, unsigned char *address, int count) { /* Rudimentary sanity checking here */ if (pB->i2eValid != I2E_MAGIC) - COMPLETE(pB, I2EE_INVALID); + I2_COMPLETE(pB, I2EE_INVALID); - OUTSB ( pB->i2eData, address, count ); + I2_OUTSB(pB->i2eData, address, count); - COMPLETE(pB, I2EE_GOOD); + I2_COMPLETE(pB, I2EE_GOOD); } //****************************************************************************** @@ -767,11 +762,11 @@ iiReadBuf16(i2eBordStrPtr pB, unsigned char *address, int count) { // Rudimentary sanity checking here. if (pB->i2eValid != I2E_MAGIC) - COMPLETE(pB, I2EE_INVALID); + I2_COMPLETE(pB, I2EE_INVALID); - INSW ( pB->i2eData, address, count); + I2_INSW(pB->i2eData, address, count); - COMPLETE(pB, I2EE_GOOD); + I2_COMPLETE(pB, I2EE_GOOD); } //****************************************************************************** @@ -796,11 +791,11 @@ iiReadBuf8(i2eBordStrPtr pB, unsigned char *address, int count) { // Rudimentary sanity checking here. if (pB->i2eValid != I2E_MAGIC) - COMPLETE(pB, I2EE_INVALID); + I2_COMPLETE(pB, I2EE_INVALID); - INSB ( pB->i2eData, address, count); + I2_INSB(pB->i2eData, address, count); - COMPLETE(pB, I2EE_GOOD); + I2_COMPLETE(pB, I2EE_GOOD); } //****************************************************************************** @@ -820,7 +815,7 @@ iiReadBuf8(i2eBordStrPtr pB, unsigned char *address, int count) static unsigned short iiReadWord16(i2eBordStrPtr pB) { - return (unsigned short)( INW(pB->i2eData) ); + return inw(pB->i2eData); } //****************************************************************************** @@ -842,9 +837,9 @@ iiReadWord8(i2eBordStrPtr pB) { unsigned short urs; - urs = INB ( pB->i2eData ); + urs = inb(pB->i2eData); - return ( ( INB ( pB->i2eData ) << 8 ) | urs ); + return (inb(pB->i2eData) << 8) | urs; } //****************************************************************************** @@ -865,7 +860,7 @@ iiReadWord8(i2eBordStrPtr pB) static void iiWriteWord16(i2eBordStrPtr pB, unsigned short value) { - WORD_TO(pB, (int)value); + outw((int)value, pB->i2eData); } //****************************************************************************** @@ -886,8 +881,8 @@ iiWriteWord16(i2eBordStrPtr pB, unsigned short value) static void iiWriteWord8(i2eBordStrPtr pB, unsigned short value) { - BYTE_TO(pB, (char)value); - BYTE_TO(pB, (char)(value >> 8) ); + outb((char)value, pB->i2eData); + outb((char)(value >> 8), pB->i2eData); } //****************************************************************************** @@ -939,30 +934,30 @@ iiWaitForTxEmptyII(i2eBordStrPtr pB, int mSdelay) // interrupts of any kind. - WRITE_LOCK_IRQSAVE(&Dl_spinlock,flags) - OUTB(pB->i2ePointer, SEL_COMMAND); - OUTB(pB->i2ePointer, SEL_CMD_SH); + write_lock_irqsave(&Dl_spinlock, flags); + outb(SEL_COMMAND, pB->i2ePointer); + outb(SEL_CMD_SH, pB->i2ePointer); - itemp = INB(pB->i2eStatus); + itemp = inb(pB->i2eStatus); - OUTB(pB->i2ePointer, SEL_COMMAND); - OUTB(pB->i2ePointer, SEL_CMD_UNSH); + outb(SEL_COMMAND, pB->i2ePointer); + outb(SEL_CMD_UNSH, pB->i2ePointer); if (itemp & ST_IN_EMPTY) { - UPDATE_FIFO_ROOM(pB); - WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) - COMPLETE(pB, I2EE_GOOD); + I2_UPDATE_FIFO_ROOM(pB); + write_unlock_irqrestore(&Dl_spinlock, flags); + I2_COMPLETE(pB, I2EE_GOOD); } - WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + write_unlock_irqrestore(&Dl_spinlock, flags); if (mSdelay-- == 0) break; iiDelay(pB, 1); /* 1 mS granularity on checking condition */ } - COMPLETE(pB, I2EE_TXE_TIME); + I2_COMPLETE(pB, I2EE_TXE_TIME); } //****************************************************************************** @@ -1002,21 +997,21 @@ iiWaitForTxEmptyIIEX(i2eBordStrPtr pB, int mSdelay) // you will generally not want to service interrupts or in any way // disrupt the assumptions implicit in the larger context. - WRITE_LOCK_IRQSAVE(&Dl_spinlock,flags) + write_lock_irqsave(&Dl_spinlock, flags); - if (INB(pB->i2eStatus) & STE_OUT_MT) { - UPDATE_FIFO_ROOM(pB); - WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) - COMPLETE(pB, I2EE_GOOD); + if (inb(pB->i2eStatus) & STE_OUT_MT) { + I2_UPDATE_FIFO_ROOM(pB); + write_unlock_irqrestore(&Dl_spinlock, flags); + I2_COMPLETE(pB, I2EE_GOOD); } - WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + write_unlock_irqrestore(&Dl_spinlock, flags); if (mSdelay-- == 0) break; iiDelay(pB, 1); // 1 mS granularity on checking condition } - COMPLETE(pB, I2EE_TXE_TIME); + I2_COMPLETE(pB, I2EE_TXE_TIME); } //****************************************************************************** @@ -1038,8 +1033,8 @@ static int iiTxMailEmptyII(i2eBordStrPtr pB) { int port = pB->i2ePointer; - OUTB ( port, SEL_OUTMAIL ); - return ( INB(port) == 0 ); + outb(SEL_OUTMAIL, port); + return inb(port) == 0; } //****************************************************************************** @@ -1060,7 +1055,7 @@ iiTxMailEmptyII(i2eBordStrPtr pB) static int iiTxMailEmptyIIEX(i2eBordStrPtr pB) { - return !(INB(pB->i2eStatus) & STE_OUT_MAIL); + return !(inb(pB->i2eStatus) & STE_OUT_MAIL); } //****************************************************************************** @@ -1084,10 +1079,10 @@ iiTrySendMailII(i2eBordStrPtr pB, unsigned char mail) { int port = pB->i2ePointer; - OUTB(port, SEL_OUTMAIL); - if (INB(port) == 0) { - OUTB(port, SEL_OUTMAIL); - OUTB(port, mail); + outb(SEL_OUTMAIL, port); + if (inb(port) == 0) { + outb(SEL_OUTMAIL, port); + outb(mail, port); return 1; } return 0; @@ -1112,10 +1107,9 @@ iiTrySendMailII(i2eBordStrPtr pB, unsigned char mail) static int iiTrySendMailIIEX(i2eBordStrPtr pB, unsigned char mail) { - if(INB(pB->i2eStatus) & STE_OUT_MAIL) { + if (inb(pB->i2eStatus) & STE_OUT_MAIL) return 0; - } - OUTB(pB->i2eXMail, mail); + outb(mail, pB->i2eXMail); return 1; } @@ -1136,9 +1130,9 @@ iiTrySendMailIIEX(i2eBordStrPtr pB, unsigned char mail) static unsigned short iiGetMailII(i2eBordStrPtr pB) { - if (HAS_MAIL(pB)) { - OUTB(pB->i2ePointer, SEL_INMAIL); - return INB(pB->i2ePointer); + if (I2_HAS_MAIL(pB)) { + outb(SEL_INMAIL, pB->i2ePointer); + return inb(pB->i2ePointer); } else { return NO_MAIL_HERE; } @@ -1161,11 +1155,10 @@ iiGetMailII(i2eBordStrPtr pB) static unsigned short iiGetMailIIEX(i2eBordStrPtr pB) { - if (HAS_MAIL(pB)) { - return INB(pB->i2eXMail); - } else { + if (I2_HAS_MAIL(pB)) + return inb(pB->i2eXMail); + else return NO_MAIL_HERE; - } } //****************************************************************************** @@ -1184,8 +1177,8 @@ iiGetMailIIEX(i2eBordStrPtr pB) static void iiEnableMailIrqII(i2eBordStrPtr pB) { - OUTB(pB->i2ePointer, SEL_MASK); - OUTB(pB->i2ePointer, ST_IN_MAIL); + outb(SEL_MASK, pB->i2ePointer); + outb(ST_IN_MAIL, pB->i2ePointer); } //****************************************************************************** @@ -1204,7 +1197,7 @@ iiEnableMailIrqII(i2eBordStrPtr pB) static void iiEnableMailIrqIIEX(i2eBordStrPtr pB) { - OUTB(pB->i2eXMask, MX_IN_MAIL); + outb(MX_IN_MAIL, pB->i2eXMask); } //****************************************************************************** @@ -1223,8 +1216,8 @@ iiEnableMailIrqIIEX(i2eBordStrPtr pB) static void iiWriteMaskII(i2eBordStrPtr pB, unsigned char value) { - OUTB(pB->i2ePointer, SEL_MASK); - OUTB(pB->i2ePointer, value); + outb(SEL_MASK, pB->i2ePointer); + outb(value, pB->i2ePointer); } //****************************************************************************** @@ -1243,7 +1236,7 @@ iiWriteMaskII(i2eBordStrPtr pB, unsigned char value) static void iiWriteMaskIIEX(i2eBordStrPtr pB, unsigned char value) { - OUTB(pB->i2eXMask, value); + outb(value, pB->i2eXMask); } //****************************************************************************** @@ -1354,9 +1347,8 @@ iiDownloadBlock ( i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard) // immediately and be harmless, though not strictly necessary. itemp = MAX_DLOAD_ACK_TIME/10; while (--itemp) { - if (HAS_INPUT(pB)) { - switch(BYTE_FROM(pB)) - { + if (I2_HAS_INPUT(pB)) { + switch (inb(pB->i2eData)) { case LOADWARE_OK: pB->i2eState = isStandard ? II_STATE_STDLOADED :II_STATE_LOADED; diff --git a/drivers/char/ip2/i2ellis.h b/drivers/char/ip2/i2ellis.h index 433305062fb..c88a64e527a 100644 --- a/drivers/char/ip2/i2ellis.h +++ b/drivers/char/ip2/i2ellis.h @@ -185,10 +185,6 @@ typedef struct _i2eBordStr // The highest allowable IRQ, based on the // slot size. - unsigned char i2eChangeIrq; - // Whether tis valid to change IRQ's - // ISA = ok, EISA, MicroChannel, no - // Accelerators for various addresses on the board int i2eBase; // I/O Address of the Board int i2eData; // From here data transfers happen @@ -431,12 +427,6 @@ typedef struct _i2eBordStr // Manifests for i2eBordStr: //------------------------------------------- -#define YES 1 -#define NO 0 - -#define NULLFUNC (void (*)(void))0 -#define NULLPTR (void *)0 - typedef void (*delayFunc_t)(unsigned int); // i2eValid @@ -494,8 +484,8 @@ typedef void (*delayFunc_t)(unsigned int); // i2eUsingIrq // -#define IRQ_UNDEFINED 0x1352 // No valid irq (or polling = 0) can ever - // promote to this! +#define I2_IRQ_UNDEFINED 0x1352 /* No valid irq (or polling = 0) can + * ever promote to this! */ //------------------------------------------ // Handy Macros for i2ellis.c and others // Note these are common to -II and -IIEX @@ -504,41 +494,14 @@ typedef void (*delayFunc_t)(unsigned int); // Given a pointer to the board structure, does the input FIFO have any data or // not? // -#define HAS_INPUT(pB) !(INB(pB->i2eStatus) & ST_IN_EMPTY) -#define HAS_NO_INPUT(pB) (INB(pB->i2eStatus) & ST_IN_EMPTY) - -// Given a pointer to board structure, read a byte or word from the fifo -// -#define BYTE_FROM(pB) (unsigned char)INB(pB->i2eData) -#define WORD_FROM(pB) (unsigned short)INW(pB->i2eData) - -// Given a pointer to board structure, is there room for any data to be written -// to the data fifo? -// -#define HAS_OUTROOM(pB) !(INB(pB->i2eStatus) & ST_OUT_FULL) -#define HAS_NO_OUTROOM(pB) (INB(pB->i2eStatus) & ST_OUT_FULL) - -// Given a pointer to board structure, write a single byte to the fifo -// structure. Note that for 16-bit interfaces, the high order byte is undefined -// and unknown. -// -#define BYTE_TO(pB, c) OUTB(pB->i2eData,(c)) - -// Write a word to the fifo structure. For 8-bit interfaces, this may have -// unknown results. -// -#define WORD_TO(pB, c) OUTW(pB->i2eData,(c)) +#define I2_HAS_INPUT(pB) !(inb(pB->i2eStatus) & ST_IN_EMPTY) // Given a pointer to the board structure, is there anything in the incoming // mailbox? // -#define HAS_MAIL(pB) (INB(pB->i2eStatus) & ST_IN_MAIL) +#define I2_HAS_MAIL(pB) (inb(pB->i2eStatus) & ST_IN_MAIL) -#define UPDATE_FIFO_ROOM(pB) (pB)->i2eFifoRemains=(pB)->i2eFifoSize - -// Handy macro to round up a number (like the buffer write and read routines do) -// -#define ROUNDUP(number) (((number)+1) & (~1)) +#define I2_UPDATE_FIFO_ROOM(pB) ((pB)->i2eFifoRemains = (pB)->i2eFifoSize) //------------------------------------------ // Function Declarations for i2ellis.c @@ -593,20 +556,11 @@ static int iiDownloadBlock(i2eBordStrPtr, loadHdrStrPtr, int); // static int iiDownloadAll(i2eBordStrPtr, loadHdrStrPtr, int, int); -// Called indirectly always. Needed externally so the routine might be -// SPECIFIED as an argument to iiReset() -// -//static void ii2DelayIO(unsigned int); // N-millisecond delay using - //hardware spin -//static void ii2DelayTimer(unsigned int); // N-millisecond delay using Linux - //timer - // Many functions defined here return True if good, False otherwise, with an // error code in i2eError field. Here is a handy macro for setting the error // code and returning. // -#define COMPLETE(pB,code) \ - do { \ +#define I2_COMPLETE(pB,code) do { \ pB->i2eError = code; \ return (code == I2EE_GOOD);\ } while (0) diff --git a/drivers/char/ip2/i2hw.h b/drivers/char/ip2/i2hw.h index 15fe04e748f..8aa6e7ab8d5 100644 --- a/drivers/char/ip2/i2hw.h +++ b/drivers/char/ip2/i2hw.h @@ -129,7 +129,6 @@ registers, use byte operations only. //------------------------------------------------ // #include "ip2types.h" -#include "i2os.h" /* For any o.s., compiler, or host-related issues */ //------------------------------------------------------------------------- // Manifests for the I/O map: @@ -644,5 +643,10 @@ typedef union _loadHdrStr #define ABS_BIGGEST_BOX 16 // Absolute the most ports per box #define ABS_MOST_PORTS (ABS_MAX_BOXES * ABS_BIGGEST_BOX) +#define I2_OUTSW(port, addr, count) outsw((port), (addr), (((count)+1)/2)) +#define I2_OUTSB(port, addr, count) outsb((port), (addr), (((count)+1))&-2) +#define I2_INSW(port, addr, count) insw((port), (addr), (((count)+1)/2)) +#define I2_INSB(port, addr, count) insb((port), (addr), (((count)+1))&-2) + #endif // I2HW_H diff --git a/drivers/char/ip2/i2lib.c b/drivers/char/ip2/i2lib.c index 9c25320121e..938879cc7bc 100644 --- a/drivers/char/ip2/i2lib.c +++ b/drivers/char/ip2/i2lib.c @@ -227,17 +227,17 @@ i2InitChannels ( i2eBordStrPtr pB, int nChannels, i2ChanStrPtr pCh) i2ChanStrPtr *ppCh; if (pB->i2eValid != I2E_MAGIC) { - COMPLETE(pB, I2EE_BADMAGIC); + I2_COMPLETE(pB, I2EE_BADMAGIC); } if (pB->i2eState != II_STATE_STDLOADED) { - COMPLETE(pB, I2EE_BADSTATE); + I2_COMPLETE(pB, I2EE_BADSTATE); } - LOCK_INIT(&pB->read_fifo_spinlock); - LOCK_INIT(&pB->write_fifo_spinlock); - LOCK_INIT(&pB->Dbuf_spinlock); - LOCK_INIT(&pB->Bbuf_spinlock); - LOCK_INIT(&pB->Fbuf_spinlock); + rwlock_init(&pB->read_fifo_spinlock); + rwlock_init(&pB->write_fifo_spinlock); + rwlock_init(&pB->Dbuf_spinlock); + rwlock_init(&pB->Bbuf_spinlock); + rwlock_init(&pB->Fbuf_spinlock); // NO LOCK needed yet - this is init @@ -259,10 +259,10 @@ i2InitChannels ( i2eBordStrPtr pB, int nChannels, i2ChanStrPtr pCh) if ( !(pB->i2eChannelMap[index >> 4] & (1 << (index & 0xf)) ) ) { continue; } - LOCK_INIT(&pCh->Ibuf_spinlock); - LOCK_INIT(&pCh->Obuf_spinlock); - LOCK_INIT(&pCh->Cbuf_spinlock); - LOCK_INIT(&pCh->Pbuf_spinlock); + rwlock_init(&pCh->Ibuf_spinlock); + rwlock_init(&pCh->Obuf_spinlock); + rwlock_init(&pCh->Cbuf_spinlock); + rwlock_init(&pCh->Pbuf_spinlock); // NO LOCK needed yet - this is init // Set up validity flag according to support level if (pB->i2eGoodMap[index >> 4] & (1 << (index & 0xf)) ) { @@ -347,7 +347,7 @@ i2InitChannels ( i2eBordStrPtr pB, int nChannels, i2ChanStrPtr pCh) } // No need to check for wrap here; this is initialization. pB->i2Fbuf_stuff = stuffIndex; - COMPLETE(pB, I2EE_GOOD); + I2_COMPLETE(pB, I2EE_GOOD); } @@ -374,7 +374,7 @@ i2DeQueueNeeds(i2eBordStrPtr pB, int type) case NEED_INLINE: - WRITE_LOCK_IRQSAVE(&pB->Dbuf_spinlock,flags); + write_lock_irqsave(&pB->Dbuf_spinlock, flags); if ( pB->i2Dbuf_stuff != pB->i2Dbuf_strip) { queueIndex = pB->i2Dbuf_strip; @@ -386,12 +386,12 @@ i2DeQueueNeeds(i2eBordStrPtr pB, int type) pB->i2Dbuf_strip = queueIndex; pCh->channelNeeds &= ~NEED_INLINE; } - WRITE_UNLOCK_IRQRESTORE(&pB->Dbuf_spinlock,flags); + write_unlock_irqrestore(&pB->Dbuf_spinlock, flags); break; case NEED_BYPASS: - WRITE_LOCK_IRQSAVE(&pB->Bbuf_spinlock,flags); + write_lock_irqsave(&pB->Bbuf_spinlock, flags); if (pB->i2Bbuf_stuff != pB->i2Bbuf_strip) { queueIndex = pB->i2Bbuf_strip; @@ -403,12 +403,12 @@ i2DeQueueNeeds(i2eBordStrPtr pB, int type) pB->i2Bbuf_strip = queueIndex; pCh->channelNeeds &= ~NEED_BYPASS; } - WRITE_UNLOCK_IRQRESTORE(&pB->Bbuf_spinlock,flags); + write_unlock_irqrestore(&pB->Bbuf_spinlock, flags); break; case NEED_FLOW: - WRITE_LOCK_IRQSAVE(&pB->Fbuf_spinlock,flags); + write_lock_irqsave(&pB->Fbuf_spinlock, flags); if (pB->i2Fbuf_stuff != pB->i2Fbuf_strip) { queueIndex = pB->i2Fbuf_strip; @@ -420,7 +420,7 @@ i2DeQueueNeeds(i2eBordStrPtr pB, int type) pB->i2Fbuf_strip = queueIndex; pCh->channelNeeds &= ~NEED_FLOW; } - WRITE_UNLOCK_IRQRESTORE(&pB->Fbuf_spinlock,flags); + write_unlock_irqrestore(&pB->Fbuf_spinlock, flags); break; default: printk(KERN_ERR "i2DeQueueNeeds called with bad type:%x\n",type); @@ -453,7 +453,7 @@ i2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type) case NEED_INLINE: - WRITE_LOCK_IRQSAVE(&pB->Dbuf_spinlock,flags); + write_lock_irqsave(&pB->Dbuf_spinlock, flags); if ( !(pCh->channelNeeds & NEED_INLINE) ) { pCh->channelNeeds |= NEED_INLINE; @@ -463,12 +463,12 @@ i2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type) queueIndex = 0; pB->i2Dbuf_stuff = queueIndex; } - WRITE_UNLOCK_IRQRESTORE(&pB->Dbuf_spinlock,flags); + write_unlock_irqrestore(&pB->Dbuf_spinlock, flags); break; case NEED_BYPASS: - WRITE_LOCK_IRQSAVE(&pB->Bbuf_spinlock,flags); + write_lock_irqsave(&pB->Bbuf_spinlock, flags); if ((type & NEED_BYPASS) && !(pCh->channelNeeds & NEED_BYPASS)) { pCh->channelNeeds |= NEED_BYPASS; @@ -478,12 +478,12 @@ i2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type) queueIndex = 0; pB->i2Bbuf_stuff = queueIndex; } - WRITE_UNLOCK_IRQRESTORE(&pB->Bbuf_spinlock,flags); + write_unlock_irqrestore(&pB->Bbuf_spinlock, flags); break; case NEED_FLOW: - WRITE_LOCK_IRQSAVE(&pB->Fbuf_spinlock,flags); + write_lock_irqsave(&pB->Fbuf_spinlock, flags); if ((type & NEED_FLOW) && !(pCh->channelNeeds & NEED_FLOW)) { pCh->channelNeeds |= NEED_FLOW; @@ -493,7 +493,7 @@ i2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type) queueIndex = 0; pB->i2Fbuf_stuff = queueIndex; } - WRITE_UNLOCK_IRQRESTORE(&pB->Fbuf_spinlock,flags); + write_unlock_irqrestore(&pB->Fbuf_spinlock, flags); break; case NEED_CREDIT: @@ -562,9 +562,8 @@ i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands, pB = pCh->pMyBord; // Board must also exist, and THE INTERRUPT COMMAND ALREADY SENT - if (pB->i2eValid != I2E_MAGIC || pB->i2eUsingIrq == IRQ_UNDEFINED) { + if (pB->i2eValid != I2E_MAGIC || pB->i2eUsingIrq == I2_IRQ_UNDEFINED) return -2; - } // If the board has gone fatal, return bad, and also hit the trap routine if // it exists. if (pB->i2eFatal) { @@ -620,13 +619,13 @@ i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands, switch(type) { case PTYPE_INLINE: lock_var_p = &pCh->Obuf_spinlock; - WRITE_LOCK_IRQSAVE(lock_var_p,flags); + write_lock_irqsave(lock_var_p, flags); stuffIndex = pCh->Obuf_stuff; bufroom = pCh->Obuf_strip - stuffIndex; break; case PTYPE_BYPASS: lock_var_p = &pCh->Cbuf_spinlock; - WRITE_LOCK_IRQSAVE(lock_var_p,flags); + write_lock_irqsave(lock_var_p, flags); stuffIndex = pCh->Cbuf_stuff; bufroom = pCh->Cbuf_strip - stuffIndex; break; @@ -645,7 +644,7 @@ i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands, break; /* from for()- Enough room: goto proceed */ } ip2trace(CHANN, ITRC_QUEUE, 3, 1, totalsize); - WRITE_UNLOCK_IRQRESTORE(lock_var_p, flags); + write_unlock_irqrestore(lock_var_p, flags); } else ip2trace(CHANN, ITRC_QUEUE, 3, 1, totalsize); @@ -747,7 +746,7 @@ i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands, { case PTYPE_INLINE: pCh->Obuf_stuff = stuffIndex; // Store buffer pointer - WRITE_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Obuf_spinlock, flags); pB->debugInlineQueued++; // Add the channel pointer to list of channels needing service (first @@ -757,7 +756,7 @@ i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands, case PTYPE_BYPASS: pCh->Cbuf_stuff = stuffIndex; // Store buffer pointer - WRITE_UNLOCK_IRQRESTORE(&pCh->Cbuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Cbuf_spinlock, flags); pB->debugBypassQueued++; // Add the channel pointer to list of channels needing service (first @@ -840,7 +839,7 @@ i2Input(i2ChanStrPtr pCh) count = -1; goto i2Input_exit; } - WRITE_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags); + write_lock_irqsave(&pCh->Ibuf_spinlock, flags); // initialize some accelerators and private copies stripIndex = pCh->Ibuf_strip; @@ -850,7 +849,7 @@ i2Input(i2ChanStrPtr pCh) // If buffer is empty or requested data count was 0, (trivial case) return // without any further thought. if ( count == 0 ) { - WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); goto i2Input_exit; } // Adjust for buffer wrap @@ -891,10 +890,10 @@ i2Input(i2ChanStrPtr pCh) if ((pCh->sinceLastFlow += count) >= pCh->whenSendFlow) { pCh->sinceLastFlow -= pCh->whenSendFlow; - WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW); } else { - WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); } i2Input_exit: @@ -926,7 +925,7 @@ i2InputFlush(i2ChanStrPtr pCh) ip2trace (CHANN, ITRC_INPUT, 10, 0); - WRITE_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags); + write_lock_irqsave(&pCh->Ibuf_spinlock, flags); count = pCh->Ibuf_stuff - pCh->Ibuf_strip; // Adjust for buffer wrap @@ -947,10 +946,10 @@ i2InputFlush(i2ChanStrPtr pCh) if ( (pCh->sinceLastFlow += count) >= pCh->whenSendFlow ) { pCh->sinceLastFlow -= pCh->whenSendFlow; - WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW); } else { - WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); } ip2trace (CHANN, ITRC_INPUT, 19, 1, count); @@ -979,9 +978,9 @@ i2InputAvailable(i2ChanStrPtr pCh) // initialize some accelerators and private copies - READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags); + read_lock_irqsave(&pCh->Ibuf_spinlock, flags); count = pCh->Ibuf_stuff - pCh->Ibuf_strip; - READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); // Adjust for buffer wrap if (count < 0) @@ -1045,9 +1044,9 @@ i2Output(i2ChanStrPtr pCh, const char *pSource, int count) while ( count > 0 ) { // How much room in output buffer is there? - READ_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + read_lock_irqsave(&pCh->Obuf_spinlock, flags); amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1; - READ_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + read_unlock_irqrestore(&pCh->Obuf_spinlock, flags); if (amountToMove < 0) { amountToMove += OBUF_SIZE; } @@ -1075,7 +1074,7 @@ i2Output(i2ChanStrPtr pCh, const char *pSource, int count) if ( !(pCh->flush_flags && i2RetryFlushOutput(pCh) ) && amountToMove > 0 ) { - WRITE_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + write_lock_irqsave(&pCh->Obuf_spinlock, flags); stuffIndex = pCh->Obuf_stuff; // Had room to move some data: don't know whether the block size, @@ -1102,7 +1101,7 @@ i2Output(i2ChanStrPtr pCh, const char *pSource, int count) } pCh->Obuf_stuff = stuffIndex; - WRITE_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Obuf_spinlock, flags); ip2trace (CHANN, ITRC_OUTPUT, 13, 1, stuffIndex ); @@ -1352,9 +1351,9 @@ i2OutputFree(i2ChanStrPtr pCh) if ( !i2Validate ( pCh ) ) { return -1; } - READ_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + read_lock_irqsave(&pCh->Obuf_spinlock, flags); amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1; - READ_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + read_unlock_irqrestore(&pCh->Obuf_spinlock, flags); if (amountToMove < 0) { amountToMove += OBUF_SIZE; @@ -1464,11 +1463,11 @@ i2StripFifo(i2eBordStrPtr pB) // ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_ENTER, 0 ); - while (HAS_INPUT(pB)) { + while (I2_HAS_INPUT(pB)) { // ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 2, 0 ); // Process packet from fifo a one atomic unit - WRITE_LOCK_IRQSAVE(&pB->read_fifo_spinlock,bflags); + write_lock_irqsave(&pB->read_fifo_spinlock, bflags); // The first word (or two bytes) will have channel number and type of // packet, possibly other information @@ -1490,7 +1489,8 @@ i2StripFifo(i2eBordStrPtr pB) // sick! if ( ((unsigned int)count) > IBUF_SIZE ) { pB->i2eFatal = 2; - WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + write_unlock_irqrestore(&pB->read_fifo_spinlock, + bflags); return; /* Bail out ASAP */ } // Channel is illegally big ? @@ -1498,7 +1498,8 @@ i2StripFifo(i2eBordStrPtr pB) (NULL==(pCh = ((i2ChanStrPtr*)pB->i2eChannelPtr)[channel]))) { iiReadBuf(pB, junkBuffer, count); - WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + write_unlock_irqrestore(&pB->read_fifo_spinlock, + bflags); break; /* From switch: ready for next packet */ } @@ -1512,14 +1513,15 @@ i2StripFifo(i2eBordStrPtr pB) if(ID_OF(pB->i2eLeadoffWord) == ID_HOT_KEY) { pCh->hotKeyIn = iiReadWord(pB) & 0xff; - WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + write_unlock_irqrestore(&pB->read_fifo_spinlock, + bflags); i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_HOTACK); break; /* From the switch: ready for next packet */ } // Normal data! We crudely assume there is room for the data in our // buffer because the board wouldn't have exceeded his credit limit. - WRITE_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,cflags); + write_lock_irqsave(&pCh->Ibuf_spinlock, cflags); // We have 2 locks now stuffIndex = pCh->Ibuf_stuff; amountToRead = IBUF_SIZE - stuffIndex; @@ -1562,8 +1564,9 @@ i2StripFifo(i2eBordStrPtr pB) // Update stuff index pCh->Ibuf_stuff = stuffIndex; - WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,cflags); - WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + write_unlock_irqrestore(&pCh->Ibuf_spinlock, cflags); + write_unlock_irqrestore(&pB->read_fifo_spinlock, + bflags); #ifdef USE_IQ schedule_work(&pCh->tqueue_input); @@ -1585,7 +1588,8 @@ i2StripFifo(i2eBordStrPtr pB) iiReadBuf(pB, cmdBuffer, count); // We can release early with buffer grab - WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + write_unlock_irqrestore(&pB->read_fifo_spinlock, + bflags); pc = cmdBuffer; pcLimit = &(cmdBuffer[count]); @@ -1830,12 +1834,12 @@ i2StripFifo(i2eBordStrPtr pB) default: // Neither packet? should be impossible ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 5, 1, PTYPE_OF(pB->i2eLeadoffWord) ); - WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock, + write_unlock_irqrestore(&pB->read_fifo_spinlock, bflags); break; } // End of switch on type of packets - } //while(board HAS_INPUT) + } /*while(board I2_HAS_INPUT)*/ ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_RETURN, 0 ); @@ -1858,7 +1862,7 @@ i2Write2Fifo(i2eBordStrPtr pB, unsigned char *source, int count,int reserve) { int rc = 0; unsigned long flags; - WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + write_lock_irqsave(&pB->write_fifo_spinlock, flags); if (!pB->i2eWaitingForEmptyFifo) { if (pB->i2eFifoRemains > (count+reserve)) { pB->i2eFifoRemains -= count; @@ -1867,7 +1871,7 @@ i2Write2Fifo(i2eBordStrPtr pB, unsigned char *source, int count,int reserve) rc = count; } } - WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + write_unlock_irqrestore(&pB->write_fifo_spinlock, flags); return rc; } //****************************************************************************** @@ -1898,7 +1902,7 @@ i2StuffFifoBypass(i2eBordStrPtr pB) while ( --bailout && notClogged && (NULL != (pCh = i2DeQueueNeeds(pB,NEED_BYPASS)))) { - WRITE_LOCK_IRQSAVE(&pCh->Cbuf_spinlock,flags); + write_lock_irqsave(&pCh->Cbuf_spinlock, flags); stripIndex = pCh->Cbuf_strip; // as long as there are packets for this channel... @@ -1906,7 +1910,7 @@ i2StuffFifoBypass(i2eBordStrPtr pB) while (stripIndex != pCh->Cbuf_stuff) { pRemove = &(pCh->Cbuf[stripIndex]); packetSize = CMD_COUNT_OF(pRemove) + sizeof(i2CmdHeader); - paddedSize = ROUNDUP(packetSize); + paddedSize = roundup(packetSize, 2); if (paddedSize > 0) { if ( 0 == i2Write2Fifo(pB, pRemove, paddedSize,0)) { @@ -1930,7 +1934,7 @@ WriteDBGBuf("BYPS", pRemove, paddedSize); // Done with this channel. Move to next, removing this one from // the queue of channels if we cleaned it out (i.e., didn't get clogged. pCh->Cbuf_strip = stripIndex; - WRITE_UNLOCK_IRQRESTORE(&pCh->Cbuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Cbuf_spinlock, flags); } // Either clogged or finished all the work #ifdef IP2DEBUG_TRACE @@ -1954,7 +1958,7 @@ static inline void i2StuffFifoFlow(i2eBordStrPtr pB) { i2ChanStrPtr pCh; - unsigned short paddedSize = ROUNDUP(sizeof(flowIn)); + unsigned short paddedSize = roundup(sizeof(flowIn), 2); ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_ENTER, 2, pB->i2eFifoRemains, paddedSize ); @@ -2010,7 +2014,7 @@ i2StuffFifoInline(i2eBordStrPtr pB) while ( --bailout && notClogged && (NULL != (pCh = i2DeQueueNeeds(pB,NEED_INLINE))) ) { - WRITE_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + write_lock_irqsave(&pCh->Obuf_spinlock, flags); stripIndex = pCh->Obuf_strip; ip2trace (CHANN, ITRC_SICMD, 3, 2, stripIndex, pCh->Obuf_stuff ); @@ -2031,7 +2035,7 @@ i2StuffFifoInline(i2eBordStrPtr pB) packetSize = flowsize + sizeof(i2CmdHeader); } flowsize = CREDIT_USAGE(flowsize); - paddedSize = ROUNDUP(packetSize); + paddedSize = roundup(packetSize, 2); ip2trace (CHANN, ITRC_SICMD, 4, 2, pB->i2eFifoRemains, paddedSize ); @@ -2086,7 +2090,7 @@ WriteDBGBuf("DATA", pRemove, paddedSize); // Done with this channel. Move to next, removing this one from the // queue of channels if we cleaned it out (i.e., didn't get clogged. pCh->Obuf_strip = stripIndex; - WRITE_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Obuf_spinlock, flags); if ( notClogged ) { @@ -2190,10 +2194,11 @@ i2ServiceBoard ( i2eBordStrPtr pB ) if (inmail & MB_OUT_STRIPPED) { pB->i2eFifoOutInts++; - WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + write_lock_irqsave(&pB->write_fifo_spinlock, flags); pB->i2eFifoRemains = pB->i2eFifoSize; pB->i2eWaitingForEmptyFifo = 0; - WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + write_unlock_irqrestore(&pB->write_fifo_spinlock, + flags); ip2trace (ITRC_NO_PORT, ITRC_INTR, 30, 1, pB->i2eFifoRemains ); diff --git a/drivers/char/ip2/i2os.h b/drivers/char/ip2/i2os.h deleted file mode 100644 index eff9b542d69..00000000000 --- a/drivers/char/ip2/i2os.h +++ /dev/null @@ -1,127 +0,0 @@ -/******************************************************************************* -* -* (c) 1999 by Computone Corporation -* -******************************************************************************** -* -* -* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport -* serial I/O controllers. -* -* DESCRIPTION: Defines, definitions and includes which are heavily dependent -* on O/S, host, compiler, etc. This file is tailored for: -* Linux v2.0.0 and later -* Gnu gcc c2.7.2 -* 80x86 architecture -* -*******************************************************************************/ - -#ifndef I2OS_H /* To prevent multiple includes */ -#define I2OS_H 1 - -//------------------------------------------------- -// Required Includes -//------------------------------------------------- - -#include "ip2types.h" -#include <asm/io.h> /* For inb, etc */ - -//------------------------------------ -// Defines for I/O instructions: -//------------------------------------ - -#define INB(port) inb(port) -#define OUTB(port,value) outb((value),(port)) -#define INW(port) inw(port) -#define OUTW(port,value) outw((value),(port)) -#define OUTSW(port,addr,count) outsw((port),(addr),(((count)+1)/2)) -#define OUTSB(port,addr,count) outsb((port),(addr),(((count)+1))&-2) -#define INSW(port,addr,count) insw((port),(addr),(((count)+1)/2)) -#define INSB(port,addr,count) insb((port),(addr),(((count)+1))&-2) - -//-------------------------------------------- -// Interrupt control -//-------------------------------------------- - -#define LOCK_INIT(a) rwlock_init(a) - -#define SAVE_AND_DISABLE_INTS(a,b) { \ - /* printk("get_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ - spin_lock_irqsave(a,b); \ -} - -#define RESTORE_INTS(a,b) { \ - /* printk("rel_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ - spin_unlock_irqrestore(a,b); \ -} - -#define READ_LOCK_IRQSAVE(a,b) { \ - /* printk("get_read_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ - read_lock_irqsave(a,b); \ -} - -#define READ_UNLOCK_IRQRESTORE(a,b) { \ - /* printk("rel_read_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ - read_unlock_irqrestore(a,b); \ -} - -#define WRITE_LOCK_IRQSAVE(a,b) { \ - /* printk("get_write_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ - write_lock_irqsave(a,b); \ -} - -#define WRITE_UNLOCK_IRQRESTORE(a,b) { \ - /* printk("rel_write_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ - write_unlock_irqrestore(a,b); \ -} - - -//------------------------------------------------------------------------------ -// Hardware-delay loop -// -// Probably used in only one place (see i2ellis.c) but this helps keep things -// together. Note we have unwound the IN instructions. On machines with a -// reasonable cache, the eight instructions (1 byte each) should fit in cache -// nicely, and on un-cached machines, the code-fetch would tend not to dominate. -// Note that cx is shifted so that "count" still reflects the total number of -// iterations assuming no unwinding. -//------------------------------------------------------------------------------ - -//#define DELAY1MS(port,count,label) - -//------------------------------------------------------------------------------ -// Macros to switch to a new stack, saving stack pointers, and to restore the -// old stack (Used, for example, in i2lib.c) "heap" is the address of some -// buffer which will become the new stack (working down from highest address). -// The two words at the two lowest addresses in this stack are for storing the -// SS and SP. -//------------------------------------------------------------------------------ - -//#define TO_NEW_STACK(heap,size) -//#define TO_OLD_STACK(heap) - -//------------------------------------------------------------------------------ -// Macros to save the original IRQ vectors and masks, and to patch in new ones. -//------------------------------------------------------------------------------ - -//#define SAVE_IRQ_MASKS(dest) -//#define WRITE_IRQ_MASKS(src) -//#define SAVE_IRQ_VECTOR(value,dest) -//#define WRITE_IRQ_VECTOR(value,src) - -//------------------------------------------------------------------------------ -// Macro to copy data from one far pointer to another. -//------------------------------------------------------------------------------ - -#define I2_MOVE_DATA(fpSource,fpDest,count) memmove(fpDest,fpSource,count); - -//------------------------------------------------------------------------------ -// Macros to issue eoi's to host interrupt control (IBM AT 8259-style). -//------------------------------------------------------------------------------ - -//#define MASTER_EOI -//#define SLAVE_EOI - -#endif /* I2OS_H */ - - diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 0a61856c631..70957acaa96 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -169,7 +169,7 @@ static int Fip_firmware_size; static int ip2_open(PTTY, struct file *); static void ip2_close(PTTY, struct file *); static int ip2_write(PTTY, const unsigned char *, int); -static void ip2_putchar(PTTY, unsigned char); +static int ip2_putchar(PTTY, unsigned char); static void ip2_flush_chars(PTTY); static int ip2_write_room(PTTY); static int ip2_chars_in_buf(PTTY); @@ -355,14 +355,15 @@ have_requested_irq( char irq ) /* the driver initialisation function and returns what it returns. */ /******************************************************************************/ #ifdef MODULE -int -init_module(void) +static int __init +ip2_init_module(void) { #ifdef IP2DEBUG_INIT printk (KERN_DEBUG "Loading module ...\n" ); #endif return 0; } +module_init(ip2_init_module); #endif /* MODULE */ /******************************************************************************/ @@ -381,8 +382,8 @@ init_module(void) /* driver should be returned since it may be unloaded from memory. */ /******************************************************************************/ #ifdef MODULE -void -cleanup_module(void) +void __exit +ip2_cleanup_module(void) { int err; int i; @@ -452,6 +453,7 @@ cleanup_module(void) printk (KERN_DEBUG "IP2 Unloaded\n" ); #endif } +module_exit(ip2_cleanup_module); #endif /* MODULE */ static const struct tty_operations ip2_ops = { @@ -1050,9 +1052,9 @@ set_irq( int boardnum, int boardIrq ) * Write to FIFO; don't bother to adjust fifo capacity for this, since * board will respond almost immediately after SendMail hit. */ - WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + write_lock_irqsave(&pB->write_fifo_spinlock, flags); iiWriteBuf(pB, tempCommand, 4); - WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + write_unlock_irqrestore(&pB->write_fifo_spinlock, flags); pB->i2eUsingIrq = boardIrq; pB->i2eOutMailWaiting |= MB_OUT_STUFFED; @@ -1070,9 +1072,9 @@ set_irq( int boardnum, int boardIrq ) (CMD_OF(tempCommand))[4] = 64; // chars (CMD_OF(tempCommand))[5] = 87; // HW_TEST - WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + write_lock_irqsave(&pB->write_fifo_spinlock, flags); iiWriteBuf(pB, tempCommand, 8); - WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + write_unlock_irqrestore(&pB->write_fifo_spinlock, flags); CHANNEL_OF(tempCommand) = 0; PTYPE_OF(tempCommand) = PTYPE_BYPASS; @@ -1087,9 +1089,9 @@ set_irq( int boardnum, int boardIrq ) CMD_COUNT_OF(tempCommand) = 2; (CMD_OF(tempCommand))[0] = 44; /* get ping */ (CMD_OF(tempCommand))[1] = 200; /* 200 ms */ - WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + write_lock_irqsave(&pB->write_fifo_spinlock, flags); iiWriteBuf(pB, tempCommand, 4); - WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + write_unlock_irqrestore(&pB->write_fifo_spinlock, flags); #endif iiEnableMailIrq(pB); @@ -1268,12 +1270,12 @@ static void do_input(struct work_struct *work) // Data input if ( pCh->pTTY != NULL ) { - READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags) + read_lock_irqsave(&pCh->Ibuf_spinlock, flags); if (!pCh->throttled && (pCh->Ibuf_stuff != pCh->Ibuf_strip)) { - READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) + read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); i2Input( pCh ); } else - READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) + read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); } else { ip2trace(CHANN, ITRC_INPUT, 22, 0 ); @@ -1614,10 +1616,8 @@ ip2_close( PTTY tty, struct file *pFile ) serviceOutgoingFifo ( pCh->pMyBord ); - if ( tty->driver->flush_buffer ) - tty->driver->flush_buffer(tty); - if ( tty->ldisc.flush_buffer ) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); + tty_driver_flush_buffer(tty); tty->closing = 0; pCh->pTTY = NULL; @@ -1717,9 +1717,9 @@ ip2_write( PTTY tty, const unsigned char *pData, int count) ip2_flush_chars( tty ); /* This is the actual move bit. Make sure it does what we need!!!!! */ - WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + write_lock_irqsave(&pCh->Pbuf_spinlock, flags); bytesSent = i2Output( pCh, pData, count); - WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); ip2trace (CHANN, ITRC_WRITE, ITRC_RETURN, 1, bytesSent ); @@ -1736,7 +1736,7 @@ ip2_write( PTTY tty, const unsigned char *pData, int count) /* */ /* */ /******************************************************************************/ -static void +static int ip2_putchar( PTTY tty, unsigned char ch ) { i2ChanStrPtr pCh = tty->driver_data; @@ -1744,13 +1744,14 @@ ip2_putchar( PTTY tty, unsigned char ch ) // ip2trace (CHANN, ITRC_PUTC, ITRC_ENTER, 1, ch ); - WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + write_lock_irqsave(&pCh->Pbuf_spinlock, flags); pCh->Pbuf[pCh->Pbuf_stuff++] = ch; if ( pCh->Pbuf_stuff == sizeof pCh->Pbuf ) { - WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); ip2_flush_chars( tty ); } else - WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); + return 1; // ip2trace (CHANN, ITRC_PUTC, ITRC_RETURN, 1, ch ); } @@ -1770,7 +1771,7 @@ ip2_flush_chars( PTTY tty ) i2ChanStrPtr pCh = tty->driver_data; unsigned long flags; - WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + write_lock_irqsave(&pCh->Pbuf_spinlock, flags); if ( pCh->Pbuf_stuff ) { // ip2trace (CHANN, ITRC_PUTC, 10, 1, strip ); @@ -1784,7 +1785,7 @@ ip2_flush_chars( PTTY tty ) } pCh->Pbuf_stuff -= strip; } - WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); } /******************************************************************************/ @@ -1802,9 +1803,9 @@ ip2_write_room ( PTTY tty ) i2ChanStrPtr pCh = tty->driver_data; unsigned long flags; - READ_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + read_lock_irqsave(&pCh->Pbuf_spinlock, flags); bytesFree = i2OutputFree( pCh ) - pCh->Pbuf_stuff; - READ_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + read_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); ip2trace (CHANN, ITRC_WRITE, 11, 1, bytesFree ); @@ -1834,12 +1835,12 @@ ip2_chars_in_buf ( PTTY tty ) pCh->Obuf_char_count + pCh->Pbuf_stuff, pCh->Obuf_char_count, pCh->Pbuf_stuff ); #endif - READ_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + read_lock_irqsave(&pCh->Obuf_spinlock, flags); rc = pCh->Obuf_char_count; - READ_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); - READ_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + read_unlock_irqrestore(&pCh->Obuf_spinlock, flags); + read_lock_irqsave(&pCh->Pbuf_spinlock, flags); rc += pCh->Pbuf_stuff; - READ_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + read_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); return rc; } @@ -1863,9 +1864,9 @@ ip2_flush_buffer( PTTY tty ) #ifdef IP2DEBUG_WRITE printk (KERN_DEBUG "IP2: flush buffer\n" ); #endif - WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + write_lock_irqsave(&pCh->Pbuf_spinlock, flags); pCh->Pbuf_stuff = 0; - WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags); i2FlushOutput( pCh ); ip2_owake(tty); @@ -1951,15 +1952,15 @@ ip2_unthrottle ( PTTY tty ) pCh->throttled = 0; i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME); serviceOutgoingFifo( pCh->pMyBord ); - READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags) + read_lock_irqsave(&pCh->Ibuf_spinlock, flags); if ( pCh->Ibuf_stuff != pCh->Ibuf_strip ) { - READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) + read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); #ifdef IP2DEBUG_READ printk (KERN_DEBUG "i2Input called from unthrottle\n" ); #endif i2Input( pCh ); } else - READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) + read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags); } static void @@ -2202,9 +2203,9 @@ ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg ) * for masking). Caller should use TIOCGICOUNT to see which one it was */ case TIOCMIWAIT: - WRITE_LOCK_IRQSAVE(&pB->read_fifo_spinlock, flags); + write_lock_irqsave(&pB->read_fifo_spinlock, flags); cprev = pCh->icount; /* note the counters on entry */ - WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock, flags); + write_unlock_irqrestore(&pB->read_fifo_spinlock, flags); i2QueueCommands(PTYPE_BYPASS, pCh, 100, 4, CMD_DCD_REP, CMD_CTS_REP, CMD_DSR_REP, CMD_RI_REP); init_waitqueue_entry(&wait, current); @@ -2224,9 +2225,9 @@ ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg ) rc = -ERESTARTSYS; break; } - WRITE_LOCK_IRQSAVE(&pB->read_fifo_spinlock, flags); + write_lock_irqsave(&pB->read_fifo_spinlock, flags); cnow = pCh->icount; /* atomic copy */ - WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock, flags); + write_unlock_irqrestore(&pB->read_fifo_spinlock, flags); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { rc = -EIO; /* no change => rc */ @@ -2264,9 +2265,9 @@ ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg ) case TIOCGICOUNT: ip2trace (CHANN, ITRC_IOCTL, 11, 1, rc ); - WRITE_LOCK_IRQSAVE(&pB->read_fifo_spinlock, flags); + write_lock_irqsave(&pB->read_fifo_spinlock, flags); cnow = pCh->icount; - WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock, flags); + write_unlock_irqrestore(&pB->read_fifo_spinlock, flags); p_cuser = argp; rc = put_user(cnow.cts, &p_cuser->cts); rc = put_user(cnow.dsr, &p_cuser->dsr); @@ -2872,7 +2873,7 @@ ip2_ipl_ioctl ( struct inode *pInode, struct file *pFile, UINT cmd, ULONG arg ) case 65: /* Board - ip2stat */ if ( pB ) { rc = copy_to_user(argp, pB, sizeof(i2eBordStr)); - rc = put_user(INB(pB->i2eStatus), + rc = put_user(inb(pB->i2eStatus), (ULONG __user *)(arg + (ULONG)(&pB->i2eStatus) - (ULONG)pB ) ); } else { rc = -ENODEV; diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index eba2883b630..4f3cefa8eb0 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -126,8 +126,8 @@ #include <linux/delay.h> #include <linux/ioport.h> -#include <asm/uaccess.h> -#include <asm/io.h> +#include <linux/uaccess.h> +#include <linux/io.h> #include <asm/system.h> #include <linux/pci.h> @@ -189,7 +189,7 @@ struct isi_board { unsigned short status; unsigned short port_status; /* each bit for each port */ unsigned short shift_count; - struct isi_port * ports; + struct isi_port *ports; signed char count; spinlock_t card_lock; /* Card wide lock 11/5/00 -sameer */ unsigned long flags; @@ -205,11 +205,11 @@ struct isi_port { u16 channel; u16 status; u16 closing_wait; - struct isi_board * card; - struct tty_struct * tty; + struct isi_board *card; + struct tty_struct *tty; wait_queue_head_t close_wait; wait_queue_head_t open_wait; - unsigned char * xmit_buf; + unsigned char *xmit_buf; int xmit_head; int xmit_tail; int xmit_cnt; @@ -405,7 +405,7 @@ static void isicom_tx(unsigned long _data) /* find next active board */ card = (prev_card + 1) & 0x0003; - while(count-- > 0) { + while (count-- > 0) { if (isi_card[card].status & BOARD_ACTIVE) break; card = (card + 1) & 0x0003; @@ -428,7 +428,7 @@ static void isicom_tx(unsigned long _data) if (retries >= 100) goto unlock; - for (;count > 0;count--, port++) { + for (; count > 0; count--, port++) { /* port not active or tx disabled to force flow control */ if (!(port->flags & ASYNC_INITIALIZED) || !(port->status & ISI_TXOK)) @@ -471,9 +471,10 @@ static void isicom_tx(unsigned long _data) break; } } - if (cnt <= 0) break; + if (cnt <= 0) + break; word_count = cnt >> 1; - outsw(base, port->xmit_buf+port->xmit_tail,word_count); + outsw(base, port->xmit_buf+port->xmit_tail, word_count); port->xmit_tail = (port->xmit_tail + (word_count << 1)) & (SERIAL_XMIT_SIZE - 1); txcount -= (word_count << 1); @@ -556,7 +557,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) tty = port->tty; if (tty == NULL) { word_count = byte_count >> 1; - while(byte_count > 1) { + while (byte_count > 1) { inw(base); byte_count -= 2; } @@ -569,7 +570,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) if (header & 0x8000) { /* Status Packet */ header = inw(base); - switch(header & 0xff) { + switch (header & 0xff) { case 0: /* Change in EIA signals */ if (port->flags & ASYNC_CHECK_CD) { if (port->status & ISI_DCD) { @@ -656,7 +657,8 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) if (byte_count > 0) { pr_dbg("Intr(0x%lx:%d): Flip buffer overflow! dropping " "bytes...\n", base, channel + 1); - while(byte_count > 0) { /* drain out unread xtra data */ + /* drain out unread xtra data */ + while (byte_count > 0) { inw(base); byte_count -= 2; } @@ -679,8 +681,11 @@ static void isicom_config_port(struct isi_port *port) shift_count = card->shift_count; unsigned char flow_ctrl; - if (!(tty = port->tty) || !tty->termios) + tty = port->tty; + + if (tty == NULL) return; + /* FIXME: Switch to new tty baud API */ baud = C_BAUD(tty); if (baud & CBAUDEX) { baud &= ~CBAUDEX; @@ -706,7 +711,7 @@ static void isicom_config_port(struct isi_port *port) if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baud++; /* 57.6 Kbps */ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - baud +=2; /* 115 Kbps */ + baud += 2; /* 115 Kbps */ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) baud += 3; /* 230 kbps*/ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) @@ -716,15 +721,14 @@ static void isicom_config_port(struct isi_port *port) /* hang up */ drop_dtr(port); return; - } - else + } else raise_dtr(port); if (WaitTillCardIsFree(base) == 0) { - outw(0x8000 | (channel << shift_count) |0x03, base); + outw(0x8000 | (channel << shift_count) | 0x03, base); outw(linuxb_to_isib[baud] << 8 | 0x03, base); channel_setup = 0; - switch(C_CSIZE(tty)) { + switch (C_CSIZE(tty)) { case CS5: channel_setup |= ISICOM_CS5; break; @@ -767,7 +771,7 @@ static void isicom_config_port(struct isi_port *port) flow_ctrl |= ISICOM_INITIATE_XONXOFF; if (WaitTillCardIsFree(base) == 0) { - outw(0x8000 | (channel << shift_count) |0x04, base); + outw(0x8000 | (channel << shift_count) | 0x04, base); outw(flow_ctrl << 8 | 0x05, base); outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base); InterruptTheCard(base); @@ -805,20 +809,17 @@ static int isicom_setup_port(struct isi_port *port) struct isi_board *card = port->card; unsigned long flags; - if (port->flags & ASYNC_INITIALIZED) { + if (port->flags & ASYNC_INITIALIZED) return 0; - } if (!port->xmit_buf) { - unsigned long page; - - if (!(page = get_zeroed_page(GFP_KERNEL))) + /* Relies on BKL */ + unsigned long page = get_zeroed_page(GFP_KERNEL); + if (page == 0) return -ENOMEM; - - if (port->xmit_buf) { + if (port->xmit_buf) free_page(page); - return -ERESTARTSYS; - } - port->xmit_buf = (unsigned char *) page; + else + port->xmit_buf = (unsigned char *) page; } spin_lock_irqsave(&card->card_lock, flags); @@ -949,21 +950,18 @@ static int isicom_open(struct tty_struct *tty, struct file *filp) port->count++; tty->driver_data = port; port->tty = tty; - if ((error = isicom_setup_port(port))!=0) - return error; - if ((error = block_til_ready(tty, filp, port))!=0) - return error; - - return 0; + error = isicom_setup_port(port); + if (error == 0) + error = block_til_ready(tty, filp, port); + return error; } /* close et all */ static inline void isicom_shutdown_board(struct isi_board *bp) { - if (bp->status & BOARD_ACTIVE) { + if (bp->status & BOARD_ACTIVE) bp->status &= ~BOARD_ACTIVE; - } } /* card->lock HAS to be held */ @@ -1012,6 +1010,22 @@ static void isicom_shutdown_port(struct isi_port *port) } } +static void isicom_flush_buffer(struct tty_struct *tty) +{ + struct isi_port *port = tty->driver_data; + struct isi_board *card = port->card; + unsigned long flags; + + if (isicom_paranoia_check(port, tty->name, "isicom_flush_buffer")) + return; + + spin_lock_irqsave(&card->card_lock, flags); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + spin_unlock_irqrestore(&card->card_lock, flags); + + tty_wakeup(tty); +} + static void isicom_close(struct tty_struct *tty, struct file *filp) { struct isi_port *port = tty->driver_data; @@ -1065,8 +1079,7 @@ static void isicom_close(struct tty_struct *tty, struct file *filp) isicom_shutdown_port(port); spin_unlock_irqrestore(&card->card_lock, flags); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + isicom_flush_buffer(tty); tty_ldisc_flush(tty); spin_lock_irqsave(&card->card_lock, flags); @@ -1104,7 +1117,7 @@ static int isicom_write(struct tty_struct *tty, const unsigned char *buf, spin_lock_irqsave(&card->card_lock, flags); - while(1) { + while (1) { cnt = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, SERIAL_XMIT_SIZE - port->xmit_head)); if (cnt <= 0) @@ -1125,28 +1138,29 @@ static int isicom_write(struct tty_struct *tty, const unsigned char *buf, } /* put_char et all */ -static void isicom_put_char(struct tty_struct *tty, unsigned char ch) +static int isicom_put_char(struct tty_struct *tty, unsigned char ch) { struct isi_port *port = tty->driver_data; struct isi_board *card = port->card; unsigned long flags; if (isicom_paranoia_check(port, tty->name, "isicom_put_char")) - return; + return 0; if (!port->xmit_buf) - return; + return 0; spin_lock_irqsave(&card->card_lock, flags); if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { spin_unlock_irqrestore(&card->card_lock, flags); - return; + return 0; } port->xmit_buf[port->xmit_head++] = ch; port->xmit_head &= (SERIAL_XMIT_SIZE - 1); port->xmit_cnt++; spin_unlock_irqrestore(&card->card_lock, flags); + return 1; } /* flush_chars et all */ @@ -1258,6 +1272,8 @@ static int isicom_set_serial_info(struct isi_port *port, if (copy_from_user(&newinfo, info, sizeof(newinfo))) return -EFAULT; + lock_kernel(); + reconfig_port = ((port->flags & ASYNC_SPD_MASK) != (newinfo.flags & ASYNC_SPD_MASK)); @@ -1265,12 +1281,13 @@ static int isicom_set_serial_info(struct isi_port *port, if ((newinfo.close_delay != port->close_delay) || (newinfo.closing_wait != port->closing_wait) || ((newinfo.flags & ~ASYNC_USR_MASK) != - (port->flags & ~ASYNC_USR_MASK))) + (port->flags & ~ASYNC_USR_MASK))) { + unlock_kernel(); return -EPERM; - port->flags = ((port->flags & ~ ASYNC_USR_MASK) | + } + port->flags = ((port->flags & ~ASYNC_USR_MASK) | (newinfo.flags & ASYNC_USR_MASK)); - } - else { + } else { port->close_delay = newinfo.close_delay; port->closing_wait = newinfo.closing_wait; port->flags = ((port->flags & ~ASYNC_FLAGS) | @@ -1282,6 +1299,7 @@ static int isicom_set_serial_info(struct isi_port *port, isicom_config_port(port); spin_unlock_irqrestore(&port->card->card_lock, flags); } + unlock_kernel(); return 0; } @@ -1290,6 +1308,7 @@ static int isicom_get_serial_info(struct isi_port *port, { struct serial_struct out_info; + lock_kernel(); memset(&out_info, 0, sizeof(out_info)); /* out_info.type = ? */ out_info.line = port - isi_ports; @@ -1299,6 +1318,7 @@ static int isicom_get_serial_info(struct isi_port *port, /* out_info.baud_base = ? */ out_info.close_delay = port->close_delay; out_info.closing_wait = port->closing_wait; + unlock_kernel(); if (copy_to_user(info, &out_info, sizeof(out_info))) return -EFAULT; return 0; @@ -1314,7 +1334,7 @@ static int isicom_ioctl(struct tty_struct *tty, struct file *filp, if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) return -ENODEV; - switch(cmd) { + switch (cmd) { case TCSBRK: retval = tty_check_change(tty); if (retval) @@ -1331,19 +1351,6 @@ static int isicom_ioctl(struct tty_struct *tty, struct file *filp, tty_wait_until_sent(tty, 0); isicom_send_break(port, arg ? arg * (HZ/10) : HZ/4); return 0; - - case TIOCGSOFTCAR: - return put_user(C_CLOCAL(tty) ? 1 : 0, - (unsigned long __user *)argp); - - case TIOCSSOFTCAR: - if (get_user(arg, (unsigned long __user *) argp)) - return -EFAULT; - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - return 0; - case TIOCGSERIAL: return isicom_get_serial_info(port, argp); @@ -1453,22 +1460,6 @@ static void isicom_hangup(struct tty_struct *tty) wake_up_interruptible(&port->open_wait); } -/* flush_buffer et all */ -static void isicom_flush_buffer(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - unsigned long flags; - - if (isicom_paranoia_check(port, tty->name, "isicom_flush_buffer")) - return; - - spin_lock_irqsave(&card->card_lock, flags); - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - spin_unlock_irqrestore(&card->card_lock, flags); - - tty_wakeup(tty); -} /* * Driver init and deinit functions @@ -1592,7 +1583,7 @@ static int __devinit load_firmware(struct pci_dev *pdev, default: dev_err(&pdev->dev, "Unknown signature.\n"); goto end; - } + } retval = request_firmware(&fw, name, &pdev->dev); if (retval) @@ -1620,7 +1611,8 @@ static int __devinit load_firmware(struct pci_dev *pdev, if (WaitTillCardIsFree(base)) goto errrelfw; - if ((status = inw(base + 0x4)) != 0) { + status = inw(base + 0x4); + if (status != 0) { dev_warn(&pdev->dev, "Card%d rejected load header:\n" KERN_WARNING "Address:0x%x\n" KERN_WARNING "Count:0x%x\n" @@ -1637,12 +1629,13 @@ static int __devinit load_firmware(struct pci_dev *pdev, if (WaitTillCardIsFree(base)) goto errrelfw; - if ((status = inw(base + 0x4)) != 0) { + status = inw(base + 0x4); + if (status != 0) { dev_err(&pdev->dev, "Card%d got out of sync.Card " "Status:0x%x\n", index + 1, status); goto errrelfw; } - } + } /* XXX: should we test it by reading it back and comparing with original like * in load firmware package? */ @@ -1666,7 +1659,8 @@ static int __devinit load_firmware(struct pci_dev *pdev, if (WaitTillCardIsFree(base)) goto errrelfw; - if ((status = inw(base + 0x4)) != 0) { + status = inw(base + 0x4); + if (status != 0) { dev_warn(&pdev->dev, "Card%d rejected verify header:\n" KERN_WARNING "Address:0x%x\n" KERN_WARNING "Count:0x%x\n" @@ -1699,7 +1693,8 @@ static int __devinit load_firmware(struct pci_dev *pdev, if (WaitTillCardIsFree(base)) goto errrelfw; - if ((status = inw(base + 0x4)) != 0) { + status = inw(base + 0x4); + if (status != 0) { dev_err(&pdev->dev, "Card%d verify got out of sync. " "Card Status:0x%x\n", index + 1, status); goto errrelfw; @@ -1764,7 +1759,7 @@ static int __devinit isicom_probe(struct pci_dev *pdev, index + 1); retval = -EBUSY; goto errdec; - } + } retval = request_irq(board->irq, isicom_interrupt, IRQF_SHARED | IRQF_DISABLED, ISICOM_NAME, board); @@ -1818,7 +1813,7 @@ static int __init isicom_init(void) int retval, idx, channel; struct isi_port *port; - for(idx = 0; idx < BOARD_COUNT; idx++) { + for (idx = 0; idx < BOARD_COUNT; idx++) { port = &isi_ports[idx * 16]; isi_card[idx].ports = port; spin_lock_init(&isi_card[idx].card_lock); @@ -1832,7 +1827,7 @@ static int __init isicom_init(void) init_waitqueue_head(&port->open_wait); init_waitqueue_head(&port->close_wait); /* . . . */ - } + } isi_card[idx].base = 0; isi_card[idx].irq = 0; } diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index c645455c3fd..7c8b62f162b 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -1682,16 +1682,6 @@ static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cm rc = 0; switch (cmd) { - case TIOCGSOFTCAR: - rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), - (unsigned __user *) arg); - break; - case TIOCSSOFTCAR: - if ((rc = get_user(ival, (unsigned __user *) arg)) == 0) - tty->termios->c_cflag = - (tty->termios->c_cflag & ~CLOCAL) | - (ival ? CLOCAL : 0); - break; case TIOCGSERIAL: rc = stli_getserial(portp, argp); break; @@ -3267,7 +3257,7 @@ static int stli_initecp(struct stlibrd *brdp) */ EBRDINIT(brdp); - brdp->membase = ioremap(brdp->memaddr, brdp->memsize); + brdp->membase = ioremap_nocache(brdp->memaddr, brdp->memsize); if (brdp->membase == NULL) { retval = -ENOMEM; goto err_reg; @@ -3424,7 +3414,7 @@ static int stli_initonb(struct stlibrd *brdp) */ EBRDINIT(brdp); - brdp->membase = ioremap(brdp->memaddr, brdp->memsize); + brdp->membase = ioremap_nocache(brdp->memaddr, brdp->memsize); if (brdp->membase == NULL) { retval = -ENOMEM; goto err_reg; @@ -3675,7 +3665,7 @@ static int stli_eisamemprobe(struct stlibrd *brdp) */ for (i = 0; (i < stli_eisamempsize); i++) { brdp->memaddr = stli_eisamemprobeaddrs[i]; - brdp->membase = ioremap(brdp->memaddr, brdp->memsize); + brdp->membase = ioremap_nocache(brdp->memaddr, brdp->memsize); if (brdp->membase == NULL) continue; @@ -4433,6 +4423,8 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un done = 0; rc = 0; + lock_kernel(); + switch (cmd) { case COM_GETPORTSTATS: rc = stli_getportstats(NULL, argp); @@ -4455,6 +4447,7 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un done++; break; } + unlock_kernel(); if (done) return rc; @@ -4472,6 +4465,8 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un if (brdp->state == 0) return -ENODEV; + lock_kernel(); + switch (cmd) { case STL_BINTR: EBRDINTR(brdp); @@ -4494,6 +4489,7 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un rc = -ENOIOCTLCMD; break; } + unlock_kernel(); return rc; } diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 60b934adea6..7f7e798c138 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -110,6 +110,7 @@ const int max_vals[] = { const int NR_TYPES = ARRAY_SIZE(max_vals); struct kbd_struct kbd_table[MAX_NR_CONSOLES]; +EXPORT_SYMBOL_GPL(kbd_table); static struct kbd_struct *kbd = kbd_table; struct vt_spawn_console vt_spawn_con = { @@ -260,6 +261,7 @@ void kd_mksound(unsigned int hz, unsigned int ticks) } else kd_nosound(0); } +EXPORT_SYMBOL(kd_mksound); /* * Setting the keyboard rate. @@ -1230,7 +1232,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) if (rep && (!vc_kbd_mode(kbd, VC_REPEAT) || - (tty && !L_ECHO(tty) && tty->driver->chars_in_buffer(tty)))) { + (tty && !L_ECHO(tty) && tty_chars_in_buffer(tty)))) { /* * Don't repeat a key if the input buffers are not empty and the * characters get aren't echoed locally. This makes key repeat diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index e60a74c66e3..d83db5d880e 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -74,9 +74,8 @@ static const struct file_operations mmtimer_fops = { * We only have comparison registers RTC1-4 currently available per * node. RTC0 is used by SAL. */ -#define NUM_COMPARATORS 3 /* Check for an RTC interrupt pending */ -static int inline mmtimer_int_pending(int comparator) +static int mmtimer_int_pending(int comparator) { if (HUB_L((unsigned long *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED)) & SH_EVENT_OCCURRED_RTC1_INT_MASK << comparator) @@ -84,15 +83,16 @@ static int inline mmtimer_int_pending(int comparator) else return 0; } + /* Clear the RTC interrupt pending bit */ -static void inline mmtimer_clr_int_pending(int comparator) +static void mmtimer_clr_int_pending(int comparator) { HUB_S((u64 *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED_ALIAS), SH_EVENT_OCCURRED_RTC1_INT_MASK << comparator); } /* Setup timer on comparator RTC1 */ -static void inline mmtimer_setup_int_0(u64 expires) +static void mmtimer_setup_int_0(int cpu, u64 expires) { u64 val; @@ -106,7 +106,7 @@ static void inline mmtimer_setup_int_0(u64 expires) mmtimer_clr_int_pending(0); val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC1_INT_CONFIG_IDX_SHFT) | - ((u64)cpu_physical_id(smp_processor_id()) << + ((u64)cpu_physical_id(cpu) << SH_RTC1_INT_CONFIG_PID_SHFT); /* Set configuration */ @@ -122,7 +122,7 @@ static void inline mmtimer_setup_int_0(u64 expires) } /* Setup timer on comparator RTC2 */ -static void inline mmtimer_setup_int_1(u64 expires) +static void mmtimer_setup_int_1(int cpu, u64 expires) { u64 val; @@ -133,7 +133,7 @@ static void inline mmtimer_setup_int_1(u64 expires) mmtimer_clr_int_pending(1); val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC2_INT_CONFIG_IDX_SHFT) | - ((u64)cpu_physical_id(smp_processor_id()) << + ((u64)cpu_physical_id(cpu) << SH_RTC2_INT_CONFIG_PID_SHFT); HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_CONFIG), val); @@ -144,7 +144,7 @@ static void inline mmtimer_setup_int_1(u64 expires) } /* Setup timer on comparator RTC3 */ -static void inline mmtimer_setup_int_2(u64 expires) +static void mmtimer_setup_int_2(int cpu, u64 expires) { u64 val; @@ -155,7 +155,7 @@ static void inline mmtimer_setup_int_2(u64 expires) mmtimer_clr_int_pending(2); val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC3_INT_CONFIG_IDX_SHFT) | - ((u64)cpu_physical_id(smp_processor_id()) << + ((u64)cpu_physical_id(cpu) << SH_RTC3_INT_CONFIG_PID_SHFT); HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_CONFIG), val); @@ -170,22 +170,22 @@ static void inline mmtimer_setup_int_2(u64 expires) * in order to insure that the setup succeeds in a deterministic time frame. * It will check if the interrupt setup succeeded. */ -static int inline mmtimer_setup(int comparator, unsigned long expires) +static int mmtimer_setup(int cpu, int comparator, unsigned long expires) { switch (comparator) { case 0: - mmtimer_setup_int_0(expires); + mmtimer_setup_int_0(cpu, expires); break; case 1: - mmtimer_setup_int_1(expires); + mmtimer_setup_int_1(cpu, expires); break; case 2: - mmtimer_setup_int_2(expires); + mmtimer_setup_int_2(cpu, expires); break; } /* We might've missed our expiration time */ - if (rtc_time() < expires) + if (rtc_time() <= expires) return 1; /* @@ -195,7 +195,7 @@ static int inline mmtimer_setup(int comparator, unsigned long expires) return mmtimer_int_pending(comparator); } -static int inline mmtimer_disable_int(long nasid, int comparator) +static int mmtimer_disable_int(long nasid, int comparator) { switch (comparator) { case 0: @@ -216,18 +216,124 @@ static int inline mmtimer_disable_int(long nasid, int comparator) return 0; } -#define TIMER_OFF 0xbadcabLL +#define COMPARATOR 1 /* The comparator to use */ -/* There is one of these for each comparator */ -typedef struct mmtimer { - spinlock_t lock ____cacheline_aligned; +#define TIMER_OFF 0xbadcabLL /* Timer is not setup */ +#define TIMER_SET 0 /* Comparator is set for this timer */ + +/* There is one of these for each timer */ +struct mmtimer { + struct rb_node list; struct k_itimer *timer; - int i; int cpu; +}; + +struct mmtimer_node { + spinlock_t lock ____cacheline_aligned; + struct rb_root timer_head; + struct rb_node *next; struct tasklet_struct tasklet; -} mmtimer_t; +}; +static struct mmtimer_node *timers; + + +/* + * Add a new mmtimer struct to the node's mmtimer list. + * This function assumes the struct mmtimer_node is locked. + */ +static void mmtimer_add_list(struct mmtimer *n) +{ + int nodeid = n->timer->it.mmtimer.node; + unsigned long expires = n->timer->it.mmtimer.expires; + struct rb_node **link = &timers[nodeid].timer_head.rb_node; + struct rb_node *parent = NULL; + struct mmtimer *x; + + /* + * Find the right place in the rbtree: + */ + while (*link) { + parent = *link; + x = rb_entry(parent, struct mmtimer, list); + + if (expires < x->timer->it.mmtimer.expires) + link = &(*link)->rb_left; + else + link = &(*link)->rb_right; + } + + /* + * Insert the timer to the rbtree and check whether it + * replaces the first pending timer + */ + rb_link_node(&n->list, parent, link); + rb_insert_color(&n->list, &timers[nodeid].timer_head); + + if (!timers[nodeid].next || expires < rb_entry(timers[nodeid].next, + struct mmtimer, list)->timer->it.mmtimer.expires) + timers[nodeid].next = &n->list; +} + +/* + * Set the comparator for the next timer. + * This function assumes the struct mmtimer_node is locked. + */ +static void mmtimer_set_next_timer(int nodeid) +{ + struct mmtimer_node *n = &timers[nodeid]; + struct mmtimer *x; + struct k_itimer *t; + int o; + +restart: + if (n->next == NULL) + return; -static mmtimer_t ** timers; + x = rb_entry(n->next, struct mmtimer, list); + t = x->timer; + if (!t->it.mmtimer.incr) { + /* Not an interval timer */ + if (!mmtimer_setup(x->cpu, COMPARATOR, + t->it.mmtimer.expires)) { + /* Late setup, fire now */ + tasklet_schedule(&n->tasklet); + } + return; + } + + /* Interval timer */ + o = 0; + while (!mmtimer_setup(x->cpu, COMPARATOR, t->it.mmtimer.expires)) { + unsigned long e, e1; + struct rb_node *next; + t->it.mmtimer.expires += t->it.mmtimer.incr << o; + t->it_overrun += 1 << o; + o++; + if (o > 20) { + printk(KERN_ALERT "mmtimer: cannot reschedule timer\n"); + t->it.mmtimer.clock = TIMER_OFF; + n->next = rb_next(&x->list); + rb_erase(&x->list, &n->timer_head); + kfree(x); + goto restart; + } + + e = t->it.mmtimer.expires; + next = rb_next(&x->list); + + if (next == NULL) + continue; + + e1 = rb_entry(next, struct mmtimer, list)-> + timer->it.mmtimer.expires; + if (e > e1) { + n->next = next; + rb_erase(&x->list, &n->timer_head); + mmtimer_add_list(x); + goto restart; + } + } +} /** * mmtimer_ioctl - ioctl interface for /dev/mmtimer @@ -390,35 +496,6 @@ static int sgi_clock_set(clockid_t clockid, struct timespec *tp) return 0; } -/* - * Schedule the next periodic interrupt. This function will attempt - * to schedule a periodic interrupt later if necessary. If the scheduling - * of an interrupt fails then the time to skip is lengthened - * exponentially in order to ensure that the next interrupt - * can be properly scheduled.. - */ -static int inline reschedule_periodic_timer(mmtimer_t *x) -{ - int n; - struct k_itimer *t = x->timer; - - t->it.mmtimer.clock = x->i; - t->it_overrun--; - - n = 0; - do { - - t->it.mmtimer.expires += t->it.mmtimer.incr << n; - t->it_overrun += 1 << n; - n++; - if (n > 20) - return 1; - - } while (!mmtimer_setup(x->i, t->it.mmtimer.expires)); - - return 0; -} - /** * mmtimer_interrupt - timer interrupt handler * @irq: irq received @@ -435,71 +512,75 @@ static int inline reschedule_periodic_timer(mmtimer_t *x) static irqreturn_t mmtimer_interrupt(int irq, void *dev_id) { - int i; unsigned long expires = 0; int result = IRQ_NONE; unsigned indx = cpu_to_node(smp_processor_id()); + struct mmtimer *base; - /* - * Do this once for each comparison register - */ - for (i = 0; i < NUM_COMPARATORS; i++) { - mmtimer_t *base = timers[indx] + i; - /* Make sure this doesn't get reused before tasklet_sched */ - spin_lock(&base->lock); - if (base->cpu == smp_processor_id()) { - if (base->timer) - expires = base->timer->it.mmtimer.expires; - /* expires test won't work with shared irqs */ - if ((mmtimer_int_pending(i) > 0) || - (expires && (expires < rtc_time()))) { - mmtimer_clr_int_pending(i); - tasklet_schedule(&base->tasklet); - result = IRQ_HANDLED; - } + spin_lock(&timers[indx].lock); + base = rb_entry(timers[indx].next, struct mmtimer, list); + if (base == NULL) { + spin_unlock(&timers[indx].lock); + return result; + } + + if (base->cpu == smp_processor_id()) { + if (base->timer) + expires = base->timer->it.mmtimer.expires; + /* expires test won't work with shared irqs */ + if ((mmtimer_int_pending(COMPARATOR) > 0) || + (expires && (expires <= rtc_time()))) { + mmtimer_clr_int_pending(COMPARATOR); + tasklet_schedule(&timers[indx].tasklet); + result = IRQ_HANDLED; } - spin_unlock(&base->lock); - expires = 0; } + spin_unlock(&timers[indx].lock); return result; } -void mmtimer_tasklet(unsigned long data) { - mmtimer_t *x = (mmtimer_t *)data; - struct k_itimer *t = x->timer; +static void mmtimer_tasklet(unsigned long data) +{ + int nodeid = data; + struct mmtimer_node *mn = &timers[nodeid]; + struct mmtimer *x = rb_entry(mn->next, struct mmtimer, list); + struct k_itimer *t; unsigned long flags; - if (t == NULL) - return; - /* Send signal and deal with periodic signals */ - spin_lock_irqsave(&t->it_lock, flags); - spin_lock(&x->lock); - /* If timer was deleted between interrupt and here, leave */ - if (t != x->timer) + spin_lock_irqsave(&mn->lock, flags); + if (!mn->next) goto out; - t->it_overrun = 0; - if (posix_timer_event(t, 0) != 0) { + x = rb_entry(mn->next, struct mmtimer, list); + t = x->timer; + + if (t->it.mmtimer.clock == TIMER_OFF) + goto out; + + t->it_overrun = 0; - // printk(KERN_WARNING "mmtimer: cannot deliver signal.\n"); + mn->next = rb_next(&x->list); + rb_erase(&x->list, &mn->timer_head); + if (posix_timer_event(t, 0) != 0) t->it_overrun++; - } + if(t->it.mmtimer.incr) { - /* Periodic timer */ - if (reschedule_periodic_timer(x)) { - printk(KERN_WARNING "mmtimer: unable to reschedule\n"); - x->timer = NULL; - } + t->it.mmtimer.expires += t->it.mmtimer.incr; + mmtimer_add_list(x); } else { /* Ensure we don't false trigger in mmtimer_interrupt */ + t->it.mmtimer.clock = TIMER_OFF; t->it.mmtimer.expires = 0; + kfree(x); } + /* Set comparator for next timer, if there is one */ + mmtimer_set_next_timer(nodeid); + t->it_overrun_last = t->it_overrun; out: - spin_unlock(&x->lock); - spin_unlock_irqrestore(&t->it_lock, flags); + spin_unlock_irqrestore(&mn->lock, flags); } static int sgi_timer_create(struct k_itimer *timer) @@ -516,19 +597,50 @@ static int sgi_timer_create(struct k_itimer *timer) */ static int sgi_timer_del(struct k_itimer *timr) { - int i = timr->it.mmtimer.clock; cnodeid_t nodeid = timr->it.mmtimer.node; - mmtimer_t *t = timers[nodeid] + i; unsigned long irqflags; - if (i != TIMER_OFF) { - spin_lock_irqsave(&t->lock, irqflags); - mmtimer_disable_int(cnodeid_to_nasid(nodeid),i); - t->timer = NULL; + spin_lock_irqsave(&timers[nodeid].lock, irqflags); + if (timr->it.mmtimer.clock != TIMER_OFF) { + unsigned long expires = timr->it.mmtimer.expires; + struct rb_node *n = timers[nodeid].timer_head.rb_node; + struct mmtimer *uninitialized_var(t); + int r = 0; + timr->it.mmtimer.clock = TIMER_OFF; timr->it.mmtimer.expires = 0; - spin_unlock_irqrestore(&t->lock, irqflags); + + while (n) { + t = rb_entry(n, struct mmtimer, list); + if (t->timer == timr) + break; + + if (expires < t->timer->it.mmtimer.expires) + n = n->rb_left; + else + n = n->rb_right; + } + + if (!n) { + spin_unlock_irqrestore(&timers[nodeid].lock, irqflags); + return 0; + } + + if (timers[nodeid].next == n) { + timers[nodeid].next = rb_next(n); + r = 1; + } + + rb_erase(n, &timers[nodeid].timer_head); + kfree(t); + + if (r) { + mmtimer_disable_int(cnodeid_to_nasid(nodeid), + COMPARATOR); + mmtimer_set_next_timer(nodeid); + } } + spin_unlock_irqrestore(&timers[nodeid].lock, irqflags); return 0; } @@ -557,12 +669,11 @@ static int sgi_timer_set(struct k_itimer *timr, int flags, struct itimerspec * new_setting, struct itimerspec * old_setting) { - - int i; unsigned long when, period, irqflags; int err = 0; cnodeid_t nodeid; - mmtimer_t *base; + struct mmtimer *base; + struct rb_node *n; if (old_setting) sgi_timer_get(timr, old_setting); @@ -575,6 +686,10 @@ static int sgi_timer_set(struct k_itimer *timr, int flags, /* Clear timer */ return 0; + base = kmalloc(sizeof(struct mmtimer), GFP_KERNEL); + if (base == NULL) + return -ENOMEM; + if (flags & TIMER_ABSTIME) { struct timespec n; unsigned long now; @@ -604,47 +719,38 @@ static int sgi_timer_set(struct k_itimer *timr, int flags, preempt_disable(); nodeid = cpu_to_node(smp_processor_id()); -retry: - /* Don't use an allocated timer, or a deleted one that's pending */ - for(i = 0; i< NUM_COMPARATORS; i++) { - base = timers[nodeid] + i; - if (!base->timer && !base->tasklet.state) { - break; - } - } - - if (i == NUM_COMPARATORS) { - preempt_enable(); - return -EBUSY; - } - spin_lock_irqsave(&base->lock, irqflags); + /* Lock the node timer structure */ + spin_lock_irqsave(&timers[nodeid].lock, irqflags); - if (base->timer || base->tasklet.state != 0) { - spin_unlock_irqrestore(&base->lock, irqflags); - goto retry; - } base->timer = timr; base->cpu = smp_processor_id(); - timr->it.mmtimer.clock = i; + timr->it.mmtimer.clock = TIMER_SET; timr->it.mmtimer.node = nodeid; timr->it.mmtimer.incr = period; timr->it.mmtimer.expires = when; - if (period == 0) { - if (!mmtimer_setup(i, when)) { - mmtimer_disable_int(-1, i); - posix_timer_event(timr, 0); - timr->it.mmtimer.expires = 0; - } - } else { - timr->it.mmtimer.expires -= period; - if (reschedule_periodic_timer(base)) - err = -EINVAL; + n = timers[nodeid].next; + + /* Add the new struct mmtimer to node's timer list */ + mmtimer_add_list(base); + + if (timers[nodeid].next == n) { + /* No need to reprogram comparator for now */ + spin_unlock_irqrestore(&timers[nodeid].lock, irqflags); + preempt_enable(); + return err; } - spin_unlock_irqrestore(&base->lock, irqflags); + /* We need to reprogram the comparator */ + if (n) + mmtimer_disable_int(cnodeid_to_nasid(nodeid), COMPARATOR); + + mmtimer_set_next_timer(nodeid); + + /* Unlock the node timer structure */ + spin_unlock_irqrestore(&timers[nodeid].lock, irqflags); preempt_enable(); @@ -669,7 +775,6 @@ static struct k_clock sgi_clock = { */ static int __init mmtimer_init(void) { - unsigned i; cnodeid_t node, maxn = -1; if (!ia64_platform_is("sn2")) @@ -706,31 +811,18 @@ static int __init mmtimer_init(void) maxn++; /* Allocate list of node ptrs to mmtimer_t's */ - timers = kzalloc(sizeof(mmtimer_t *)*maxn, GFP_KERNEL); + timers = kzalloc(sizeof(struct mmtimer_node)*maxn, GFP_KERNEL); if (timers == NULL) { printk(KERN_ERR "%s: failed to allocate memory for device\n", MMTIMER_NAME); goto out3; } - /* Allocate mmtimer_t's for each online node */ + /* Initialize struct mmtimer's for each online node */ for_each_online_node(node) { - timers[node] = kmalloc_node(sizeof(mmtimer_t)*NUM_COMPARATORS, GFP_KERNEL, node); - if (timers[node] == NULL) { - printk(KERN_ERR "%s: failed to allocate memory for device\n", - MMTIMER_NAME); - goto out4; - } - for (i=0; i< NUM_COMPARATORS; i++) { - mmtimer_t * base = timers[node] + i; - - spin_lock_init(&base->lock); - base->timer = NULL; - base->cpu = 0; - base->i = i; - tasklet_init(&base->tasklet, mmtimer_tasklet, - (unsigned long) (base)); - } + spin_lock_init(&timers[node].lock); + tasklet_init(&timers[node].tasklet, mmtimer_tasklet, + (unsigned long) node); } sgi_clock_period = sgi_clock.res = NSEC_PER_SEC / sn_rtc_cycles_per_second; @@ -741,11 +833,8 @@ static int __init mmtimer_init(void) return 0; -out4: - for_each_online_node(node) { - kfree(timers[node]); - } out3: + kfree(timers); misc_deregister(&mmtimer_miscdev); out2: free_irq(SGI_MMTIMER_VECTOR, NULL); @@ -754,4 +843,3 @@ out1: } module_init(mmtimer_init); - diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 64b7b2b1835..d57d3a61919 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -2,7 +2,8 @@ /* * moxa.c -- MOXA Intellio family multiport serial driver. * - * Copyright (C) 1999-2000 Moxa Technologies (support@moxa.com.tw). + * Copyright (C) 1999-2000 Moxa Technologies (support@moxa.com). + * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com> * * This code is loosely based on the Linux serial driver, written by * Linus Torvalds, Theodore T'so and others. @@ -25,6 +26,7 @@ #include <linux/mm.h> #include <linux/ioport.h> #include <linux/errno.h> +#include <linux/firmware.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/timer.h> @@ -41,21 +43,26 @@ #include <linux/pci.h> #include <linux/init.h> #include <linux/bitops.h> -#include <linux/completion.h> #include <asm/system.h> #include <asm/io.h> #include <asm/uaccess.h> -#define MOXA_VERSION "5.1k" +#include "moxa.h" + +#define MOXA_VERSION "6.0k" + +#define MOXA_FW_HDRLEN 32 #define MOXAMAJOR 172 -#define MOXACUMAJOR 173 #define MAX_BOARDS 4 /* Don't change this value */ #define MAX_PORTS_PER_BOARD 32 /* Don't change this value */ #define MAX_PORTS (MAX_BOARDS * MAX_PORTS_PER_BOARD) +#define MOXA_IS_320(brd) ((brd)->boardType == MOXA_BOARD_C320_ISA || \ + (brd)->boardType == MOXA_BOARD_C320_PCI) + /* * Define the Moxa PCI vendor and device IDs. */ @@ -92,24 +99,16 @@ static struct pci_device_id moxa_pcibrds[] = { MODULE_DEVICE_TABLE(pci, moxa_pcibrds); #endif /* CONFIG_PCI */ -struct moxa_isa_board_conf { - int boardType; - int numPorts; - unsigned long baseAddr; -}; - -static struct moxa_isa_board_conf moxa_isa_boards[] = -{ -/* {MOXA_BOARD_C218_ISA,8,0xDC000}, */ -}; +struct moxa_port; static struct moxa_board_conf { int boardType; int numPorts; - unsigned long baseAddr; int busType; - int loadstat; + unsigned int ready; + + struct moxa_port *ports; void __iomem *basemem; void __iomem *intNdx; @@ -131,30 +130,27 @@ struct moxaq_str { }; struct moxa_port { + struct moxa_board_conf *board; + struct tty_struct *tty; + void __iomem *tableAddr; + int type; - int port; int close_delay; - unsigned short closing_wait; - int count; - int blocked_open; - long event; /* long req'd for set_bit --RR */ + unsigned int count; int asyncflags; - unsigned long statusflags; - struct tty_struct *tty; int cflag; + unsigned long statusflags; wait_queue_head_t open_wait; - struct completion close_wait; - - struct timer_list emptyTimer; - char chkPort; - char lineCtrl; - void __iomem *tableAddr; - long curBaud; - char DCDState; - char lowChkFlag; + u8 DCDState; + u8 lineCtrl; + u8 lowChkFlag; +}; - ushort breakCnt; +struct mon_str { + int tick; + int rxcnt[MAX_PORTS]; + int txcnt[MAX_PORTS]; }; /* statusflags */ @@ -168,20 +164,27 @@ struct moxa_port { #define WAKEUP_CHARS 256 static int ttymajor = MOXAMAJOR; +static struct mon_str moxaLog; +static unsigned int moxaFuncTout = HZ / 2; +static unsigned int moxaLowWaterChk; +static DEFINE_MUTEX(moxa_openlock); /* Variables for insmod */ #ifdef MODULE -static int baseaddr[4]; -static int type[4]; -static int numports[4]; +static unsigned long baseaddr[MAX_BOARDS]; +static unsigned int type[MAX_BOARDS]; +static unsigned int numports[MAX_BOARDS]; #endif MODULE_AUTHOR("William Chen"); MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver"); MODULE_LICENSE("GPL"); #ifdef MODULE -module_param_array(type, int, NULL, 0); -module_param_array(baseaddr, int, NULL, 0); -module_param_array(numports, int, NULL, 0); +module_param_array(type, uint, NULL, 0); +MODULE_PARM_DESC(type, "card type: C218=2, C320=4"); +module_param_array(baseaddr, ulong, NULL, 0); +MODULE_PARM_DESC(baseaddr, "base address"); +module_param_array(numports, uint, NULL, 0); +MODULE_PARM_DESC(numports, "numports (ignored for C218)"); #endif module_param(ttymajor, int, 0); @@ -194,9 +197,6 @@ static int moxa_write(struct tty_struct *, const unsigned char *, int); static int moxa_write_room(struct tty_struct *); static void moxa_flush_buffer(struct tty_struct *); static int moxa_chars_in_buffer(struct tty_struct *); -static void moxa_flush_chars(struct tty_struct *); -static void moxa_put_char(struct tty_struct *, unsigned char); -static int moxa_ioctl(struct tty_struct *, struct file *, unsigned int, unsigned long); static void moxa_throttle(struct tty_struct *); static void moxa_unthrottle(struct tty_struct *); static void moxa_set_termios(struct tty_struct *, struct ktermios *); @@ -208,44 +208,183 @@ static int moxa_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static void moxa_poll(unsigned long); static void moxa_set_tty_param(struct tty_struct *, struct ktermios *); -static int moxa_block_till_ready(struct tty_struct *, struct file *, - struct moxa_port *); static void moxa_setup_empty_event(struct tty_struct *); -static void moxa_check_xmit_empty(unsigned long); static void moxa_shut_down(struct moxa_port *); -static void moxa_receive_data(struct moxa_port *); /* * moxa board interface functions: */ -static void MoxaDriverInit(void); -static int MoxaDriverIoctl(unsigned int, unsigned long, int); -static int MoxaDriverPoll(void); -static int MoxaPortsOfCard(int); -static int MoxaPortIsValid(int); -static void MoxaPortEnable(int); -static void MoxaPortDisable(int); -static long MoxaPortGetMaxBaud(int); -static long MoxaPortSetBaud(int, long); -static int MoxaPortSetTermio(int, struct ktermios *, speed_t); -static int MoxaPortGetLineOut(int, int *, int *); -static void MoxaPortLineCtrl(int, int, int); -static void MoxaPortFlowCtrl(int, int, int, int, int, int); -static int MoxaPortLineStatus(int); -static int MoxaPortDCDChange(int); -static int MoxaPortDCDON(int); -static void MoxaPortFlushData(int, int); -static int MoxaPortWriteData(int, unsigned char *, int); -static int MoxaPortReadData(int, struct tty_struct *tty); -static int MoxaPortTxQueue(int); -static int MoxaPortRxQueue(int); -static int MoxaPortTxFree(int); -static void MoxaPortTxDisable(int); -static void MoxaPortTxEnable(int); -static int MoxaPortResetBrkCnt(int); -static void MoxaPortSendBreak(int, int); +static void MoxaPortEnable(struct moxa_port *); +static void MoxaPortDisable(struct moxa_port *); +static int MoxaPortSetTermio(struct moxa_port *, struct ktermios *, speed_t); +static int MoxaPortGetLineOut(struct moxa_port *, int *, int *); +static void MoxaPortLineCtrl(struct moxa_port *, int, int); +static void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int); +static int MoxaPortLineStatus(struct moxa_port *); +static void MoxaPortFlushData(struct moxa_port *, int); +static int MoxaPortWriteData(struct moxa_port *, const unsigned char *, int); +static int MoxaPortReadData(struct moxa_port *); +static int MoxaPortTxQueue(struct moxa_port *); +static int MoxaPortRxQueue(struct moxa_port *); +static int MoxaPortTxFree(struct moxa_port *); +static void MoxaPortTxDisable(struct moxa_port *); +static void MoxaPortTxEnable(struct moxa_port *); static int moxa_get_serial_info(struct moxa_port *, struct serial_struct __user *); static int moxa_set_serial_info(struct moxa_port *, struct serial_struct __user *); -static void MoxaSetFifo(int port, int enable); +static void MoxaSetFifo(struct moxa_port *port, int enable); + +/* + * I/O functions + */ + +static void moxa_wait_finish(void __iomem *ofsAddr) +{ + unsigned long end = jiffies + moxaFuncTout; + + while (readw(ofsAddr + FuncCode) != 0) + if (time_after(jiffies, end)) + return; + if (readw(ofsAddr + FuncCode) != 0 && printk_ratelimit()) + printk(KERN_WARNING "moxa function expired\n"); +} + +static void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg) +{ + writew(arg, ofsAddr + FuncArg); + writew(cmd, ofsAddr + FuncCode); + moxa_wait_finish(ofsAddr); +} + +static void moxa_low_water_check(void __iomem *ofsAddr) +{ + u16 rptr, wptr, mask, len; + + if (readb(ofsAddr + FlagStat) & Xoff_state) { + rptr = readw(ofsAddr + RXrptr); + wptr = readw(ofsAddr + RXwptr); + mask = readw(ofsAddr + RX_mask); + len = (wptr - rptr) & mask; + if (len <= Low_water) + moxafunc(ofsAddr, FC_SendXon, 0); + } +} + +/* + * TTY operations + */ + +static int moxa_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct moxa_port *ch = tty->driver_data; + void __user *argp = (void __user *)arg; + int status, ret = 0; + + if (tty->index == MAX_PORTS) { + if (cmd != MOXA_GETDATACOUNT && cmd != MOXA_GET_IOQUEUE && + cmd != MOXA_GETMSTATUS) + return -EINVAL; + } else if (!ch) + return -ENODEV; + + switch (cmd) { + case MOXA_GETDATACOUNT: + moxaLog.tick = jiffies; + if (copy_to_user(argp, &moxaLog, sizeof(moxaLog))) + ret = -EFAULT; + break; + case MOXA_FLUSH_QUEUE: + MoxaPortFlushData(ch, arg); + break; + case MOXA_GET_IOQUEUE: { + struct moxaq_str __user *argm = argp; + struct moxaq_str tmp; + struct moxa_port *p; + unsigned int i, j; + + mutex_lock(&moxa_openlock); + for (i = 0; i < MAX_BOARDS; i++) { + p = moxa_boards[i].ports; + for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) { + memset(&tmp, 0, sizeof(tmp)); + if (moxa_boards[i].ready) { + tmp.inq = MoxaPortRxQueue(p); + tmp.outq = MoxaPortTxQueue(p); + } + if (copy_to_user(argm, &tmp, sizeof(tmp))) { + mutex_unlock(&moxa_openlock); + return -EFAULT; + } + } + } + mutex_unlock(&moxa_openlock); + break; + } case MOXA_GET_OQUEUE: + status = MoxaPortTxQueue(ch); + ret = put_user(status, (unsigned long __user *)argp); + break; + case MOXA_GET_IQUEUE: + status = MoxaPortRxQueue(ch); + ret = put_user(status, (unsigned long __user *)argp); + break; + case MOXA_GETMSTATUS: { + struct mxser_mstatus __user *argm = argp; + struct mxser_mstatus tmp; + struct moxa_port *p; + unsigned int i, j; + + mutex_lock(&moxa_openlock); + for (i = 0; i < MAX_BOARDS; i++) { + p = moxa_boards[i].ports; + for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) { + memset(&tmp, 0, sizeof(tmp)); + if (!moxa_boards[i].ready) + goto copy; + + status = MoxaPortLineStatus(p); + if (status & 1) + tmp.cts = 1; + if (status & 2) + tmp.dsr = 1; + if (status & 4) + tmp.dcd = 1; + + if (!p->tty || !p->tty->termios) + tmp.cflag = p->cflag; + else + tmp.cflag = p->tty->termios->c_cflag; +copy: + if (copy_to_user(argm, &tmp, sizeof(tmp))) { + mutex_unlock(&moxa_openlock); + return -EFAULT; + } + } + } + mutex_unlock(&moxa_openlock); + break; + } + case TIOCGSERIAL: + mutex_lock(&moxa_openlock); + ret = moxa_get_serial_info(ch, argp); + mutex_unlock(&moxa_openlock); + break; + case TIOCSSERIAL: + mutex_lock(&moxa_openlock); + ret = moxa_set_serial_info(ch, argp); + mutex_unlock(&moxa_openlock); + break; + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +static void moxa_break_ctl(struct tty_struct *tty, int state) +{ + struct moxa_port *port = tty->driver_data; + + moxafunc(port->tableAddr, state ? FC_SendBreak : FC_StopBreak, + Magic_code); +} static const struct tty_operations moxa_ops = { .open = moxa_open, @@ -254,8 +393,6 @@ static const struct tty_operations moxa_ops = { .write_room = moxa_write_room, .flush_buffer = moxa_flush_buffer, .chars_in_buffer = moxa_chars_in_buffer, - .flush_chars = moxa_flush_chars, - .put_char = moxa_put_char, .ioctl = moxa_ioctl, .throttle = moxa_throttle, .unthrottle = moxa_unthrottle, @@ -263,15 +400,509 @@ static const struct tty_operations moxa_ops = { .stop = moxa_stop, .start = moxa_start, .hangup = moxa_hangup, + .break_ctl = moxa_break_ctl, .tiocmget = moxa_tiocmget, .tiocmset = moxa_tiocmset, }; static struct tty_driver *moxaDriver; -static struct moxa_port moxa_ports[MAX_PORTS]; static DEFINE_TIMER(moxaTimer, moxa_poll, 0, 0); static DEFINE_SPINLOCK(moxa_lock); +/* + * HW init + */ + +static int moxa_check_fw_model(struct moxa_board_conf *brd, u8 model) +{ + switch (brd->boardType) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + if (model != 1) + goto err; + break; + case MOXA_BOARD_CP204J: + if (model != 3) + goto err; + break; + default: + if (model != 2) + goto err; + break; + } + return 0; +err: + return -EINVAL; +} + +static int moxa_check_fw(const void *ptr) +{ + const __le16 *lptr = ptr; + + if (*lptr != cpu_to_le16(0x7980)) + return -EINVAL; + + return 0; +} + +static int moxa_load_bios(struct moxa_board_conf *brd, const u8 *buf, + size_t len) +{ + void __iomem *baseAddr = brd->basemem; + u16 tmp; + + writeb(HW_reset, baseAddr + Control_reg); /* reset */ + msleep(10); + memset_io(baseAddr, 0, 4096); + memcpy_toio(baseAddr, buf, len); /* download BIOS */ + writeb(0, baseAddr + Control_reg); /* restart */ + + msleep(2000); + + switch (brd->boardType) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + tmp = readw(baseAddr + C218_key); + if (tmp != C218_KeyCode) + goto err; + break; + case MOXA_BOARD_CP204J: + tmp = readw(baseAddr + C218_key); + if (tmp != CP204J_KeyCode) + goto err; + break; + default: + tmp = readw(baseAddr + C320_key); + if (tmp != C320_KeyCode) + goto err; + tmp = readw(baseAddr + C320_status); + if (tmp != STS_init) { + printk(KERN_ERR "MOXA: bios upload failed -- CPU/Basic " + "module not found\n"); + return -EIO; + } + break; + } + + return 0; +err: + printk(KERN_ERR "MOXA: bios upload failed -- board not found\n"); + return -EIO; +} + +static int moxa_load_320b(struct moxa_board_conf *brd, const u8 *ptr, + size_t len) +{ + void __iomem *baseAddr = brd->basemem; + + if (len < 7168) { + printk(KERN_ERR "MOXA: invalid 320 bios -- too short\n"); + return -EINVAL; + } + + writew(len - 7168 - 2, baseAddr + C320bapi_len); + writeb(1, baseAddr + Control_reg); /* Select Page 1 */ + memcpy_toio(baseAddr + DynPage_addr, ptr, 7168); + writeb(2, baseAddr + Control_reg); /* Select Page 2 */ + memcpy_toio(baseAddr + DynPage_addr, ptr + 7168, len - 7168); + + return 0; +} + +static int moxa_real_load_code(struct moxa_board_conf *brd, const void *ptr, + size_t len) +{ + void __iomem *baseAddr = brd->basemem; + const u16 *uptr = ptr; + size_t wlen, len2, j; + unsigned long key, loadbuf, loadlen, checksum, checksum_ok; + unsigned int i, retry; + u16 usum, keycode; + + keycode = (brd->boardType == MOXA_BOARD_CP204J) ? CP204J_KeyCode : + C218_KeyCode; + + switch (brd->boardType) { + case MOXA_BOARD_CP204J: + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + key = C218_key; + loadbuf = C218_LoadBuf; + loadlen = C218DLoad_len; + checksum = C218check_sum; + checksum_ok = C218chksum_ok; + break; + default: + key = C320_key; + keycode = C320_KeyCode; + loadbuf = C320_LoadBuf; + loadlen = C320DLoad_len; + checksum = C320check_sum; + checksum_ok = C320chksum_ok; + break; + } + + usum = 0; + wlen = len >> 1; + for (i = 0; i < wlen; i++) + usum += le16_to_cpu(uptr[i]); + retry = 0; + do { + wlen = len >> 1; + j = 0; + while (wlen) { + len2 = (wlen > 2048) ? 2048 : wlen; + wlen -= len2; + memcpy_toio(baseAddr + loadbuf, ptr + j, len2 << 1); + j += len2 << 1; + + writew(len2, baseAddr + loadlen); + writew(0, baseAddr + key); + for (i = 0; i < 100; i++) { + if (readw(baseAddr + key) == keycode) + break; + msleep(10); + } + if (readw(baseAddr + key) != keycode) + return -EIO; + } + writew(0, baseAddr + loadlen); + writew(usum, baseAddr + checksum); + writew(0, baseAddr + key); + for (i = 0; i < 100; i++) { + if (readw(baseAddr + key) == keycode) + break; + msleep(10); + } + retry++; + } while ((readb(baseAddr + checksum_ok) != 1) && (retry < 3)); + if (readb(baseAddr + checksum_ok) != 1) + return -EIO; + + writew(0, baseAddr + key); + for (i = 0; i < 600; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + msleep(10); + } + if (readw(baseAddr + Magic_no) != Magic_code) + return -EIO; + + if (MOXA_IS_320(brd)) { + if (brd->busType == MOXA_BUS_TYPE_PCI) { /* ASIC board */ + writew(0x3800, baseAddr + TMS320_PORT1); + writew(0x3900, baseAddr + TMS320_PORT2); + writew(28499, baseAddr + TMS320_CLOCK); + } else { + writew(0x3200, baseAddr + TMS320_PORT1); + writew(0x3400, baseAddr + TMS320_PORT2); + writew(19999, baseAddr + TMS320_CLOCK); + } + } + writew(1, baseAddr + Disable_IRQ); + writew(0, baseAddr + Magic_no); + for (i = 0; i < 500; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + msleep(10); + } + if (readw(baseAddr + Magic_no) != Magic_code) + return -EIO; + + if (MOXA_IS_320(brd)) { + j = readw(baseAddr + Module_cnt); + if (j <= 0) + return -EIO; + brd->numPorts = j * 8; + writew(j, baseAddr + Module_no); + writew(0, baseAddr + Magic_no); + for (i = 0; i < 600; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + msleep(10); + } + if (readw(baseAddr + Magic_no) != Magic_code) + return -EIO; + } + brd->intNdx = baseAddr + IRQindex; + brd->intPend = baseAddr + IRQpending; + brd->intTable = baseAddr + IRQtable; + + return 0; +} + +static int moxa_load_code(struct moxa_board_conf *brd, const void *ptr, + size_t len) +{ + void __iomem *ofsAddr, *baseAddr = brd->basemem; + struct moxa_port *port; + int retval, i; + + if (len % 2) { + printk(KERN_ERR "MOXA: bios length is not even\n"); + return -EINVAL; + } + + retval = moxa_real_load_code(brd, ptr, len); /* may change numPorts */ + if (retval) + return retval; + + switch (brd->boardType) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + case MOXA_BOARD_CP204J: + port = brd->ports; + for (i = 0; i < brd->numPorts; i++, port++) { + port->board = brd; + port->DCDState = 0; + port->tableAddr = baseAddr + Extern_table + + Extern_size * i; + ofsAddr = port->tableAddr; + writew(C218rx_mask, ofsAddr + RX_mask); + writew(C218tx_mask, ofsAddr + TX_mask); + writew(C218rx_spage + i * C218buf_pageno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C218rx_pageno, ofsAddr + EndPage_rxb); + + writew(C218tx_spage + i * C218buf_pageno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb) + C218tx_pageno, ofsAddr + EndPage_txb); + + } + break; + default: + port = brd->ports; + for (i = 0; i < brd->numPorts; i++, port++) { + port->board = brd; + port->DCDState = 0; + port->tableAddr = baseAddr + Extern_table + + Extern_size * i; + ofsAddr = port->tableAddr; + switch (brd->numPorts) { + case 8: + writew(C320p8rx_mask, ofsAddr + RX_mask); + writew(C320p8tx_mask, ofsAddr + TX_mask); + writew(C320p8rx_spage + i * C320p8buf_pgno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C320p8rx_pgno, ofsAddr + EndPage_rxb); + writew(C320p8tx_spage + i * C320p8buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb) + C320p8tx_pgno, ofsAddr + EndPage_txb); + + break; + case 16: + writew(C320p16rx_mask, ofsAddr + RX_mask); + writew(C320p16tx_mask, ofsAddr + TX_mask); + writew(C320p16rx_spage + i * C320p16buf_pgno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C320p16rx_pgno, ofsAddr + EndPage_rxb); + writew(C320p16tx_spage + i * C320p16buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb) + C320p16tx_pgno, ofsAddr + EndPage_txb); + break; + + case 24: + writew(C320p24rx_mask, ofsAddr + RX_mask); + writew(C320p24tx_mask, ofsAddr + TX_mask); + writew(C320p24rx_spage + i * C320p24buf_pgno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C320p24rx_pgno, ofsAddr + EndPage_rxb); + writew(C320p24tx_spage + i * C320p24buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb); + break; + case 32: + writew(C320p32rx_mask, ofsAddr + RX_mask); + writew(C320p32tx_mask, ofsAddr + TX_mask); + writew(C320p32tx_ofs, ofsAddr + Ofs_txb); + writew(C320p32rx_spage + i * C320p32buf_pgno, ofsAddr + Page_rxb); + writew(readb(ofsAddr + Page_rxb), ofsAddr + EndPage_rxb); + writew(C320p32tx_spage + i * C320p32buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb); + break; + } + } + break; + } + return 0; +} + +static int moxa_load_fw(struct moxa_board_conf *brd, const struct firmware *fw) +{ + void *ptr = fw->data; + char rsn[64]; + u16 lens[5]; + size_t len; + unsigned int a, lenp, lencnt; + int ret = -EINVAL; + struct { + __le32 magic; /* 0x34303430 */ + u8 reserved1[2]; + u8 type; /* UNIX = 3 */ + u8 model; /* C218T=1, C320T=2, CP204=3 */ + u8 reserved2[8]; + __le16 len[5]; + } *hdr = ptr; + + BUILD_BUG_ON(ARRAY_SIZE(hdr->len) != ARRAY_SIZE(lens)); + + if (fw->size < MOXA_FW_HDRLEN) { + strcpy(rsn, "too short (even header won't fit)"); + goto err; + } + if (hdr->magic != cpu_to_le32(0x30343034)) { + sprintf(rsn, "bad magic: %.8x", le32_to_cpu(hdr->magic)); + goto err; + } + if (hdr->type != 3) { + sprintf(rsn, "not for linux, type is %u", hdr->type); + goto err; + } + if (moxa_check_fw_model(brd, hdr->model)) { + sprintf(rsn, "not for this card, model is %u", hdr->model); + goto err; + } + + len = MOXA_FW_HDRLEN; + lencnt = hdr->model == 2 ? 5 : 3; + for (a = 0; a < ARRAY_SIZE(lens); a++) { + lens[a] = le16_to_cpu(hdr->len[a]); + if (lens[a] && len + lens[a] <= fw->size && + moxa_check_fw(&fw->data[len])) + printk(KERN_WARNING "MOXA firmware: unexpected input " + "at offset %u, but going on\n", (u32)len); + if (!lens[a] && a < lencnt) { + sprintf(rsn, "too few entries in fw file"); + goto err; + } + len += lens[a]; + } + + if (len != fw->size) { + sprintf(rsn, "bad length: %u (should be %u)", (u32)fw->size, + (u32)len); + goto err; + } + + ptr += MOXA_FW_HDRLEN; + lenp = 0; /* bios */ + + strcpy(rsn, "read above"); + + ret = moxa_load_bios(brd, ptr, lens[lenp]); + if (ret) + goto err; + + /* we skip the tty section (lens[1]), since we don't need it */ + ptr += lens[lenp] + lens[lenp + 1]; + lenp += 2; /* comm */ + + if (hdr->model == 2) { + ret = moxa_load_320b(brd, ptr, lens[lenp]); + if (ret) + goto err; + /* skip another tty */ + ptr += lens[lenp] + lens[lenp + 1]; + lenp += 2; + } + + ret = moxa_load_code(brd, ptr, lens[lenp]); + if (ret) + goto err; + + return 0; +err: + printk(KERN_ERR "firmware failed to load, reason: %s\n", rsn); + return ret; +} + +static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev) +{ + const struct firmware *fw; + const char *file; + struct moxa_port *p; + unsigned int i; + int ret; + + brd->ports = kcalloc(MAX_PORTS_PER_BOARD, sizeof(*brd->ports), + GFP_KERNEL); + if (brd->ports == NULL) { + printk(KERN_ERR "cannot allocate memory for ports\n"); + ret = -ENOMEM; + goto err; + } + + for (i = 0, p = brd->ports; i < MAX_PORTS_PER_BOARD; i++, p++) { + p->type = PORT_16550A; + p->close_delay = 5 * HZ / 10; + p->cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; + init_waitqueue_head(&p->open_wait); + } + + switch (brd->boardType) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + file = "c218tunx.cod"; + break; + case MOXA_BOARD_CP204J: + file = "cp204unx.cod"; + break; + default: + file = "c320tunx.cod"; + break; + } + + ret = request_firmware(&fw, file, dev); + if (ret) { + printk(KERN_ERR "MOXA: request_firmware failed. Make sure " + "you've placed '%s' file into your firmware " + "loader directory (e.g. /lib/firmware)\n", + file); + goto err_free; + } + + ret = moxa_load_fw(brd, fw); + + release_firmware(fw); + + if (ret) + goto err_free; + + spin_lock_bh(&moxa_lock); + brd->ready = 1; + if (!timer_pending(&moxaTimer)) + mod_timer(&moxaTimer, jiffies + HZ / 50); + spin_unlock_bh(&moxa_lock); + + return 0; +err_free: + kfree(brd->ports); +err: + return ret; +} + +static void moxa_board_deinit(struct moxa_board_conf *brd) +{ + unsigned int a, opened; + + mutex_lock(&moxa_openlock); + spin_lock_bh(&moxa_lock); + brd->ready = 0; + spin_unlock_bh(&moxa_lock); + + /* pci hot-un-plug support */ + for (a = 0; a < brd->numPorts; a++) + if (brd->ports[a].asyncflags & ASYNC_INITIALIZED) + tty_hangup(brd->ports[a].tty); + while (1) { + opened = 0; + for (a = 0; a < brd->numPorts; a++) + if (brd->ports[a].asyncflags & ASYNC_INITIALIZED) + opened++; + mutex_unlock(&moxa_openlock); + if (!opened) + break; + msleep(50); + mutex_lock(&moxa_openlock); + } + + iounmap(brd->basemem); + brd->basemem = NULL; + kfree(brd->ports); +} + #ifdef CONFIG_PCI static int __devinit moxa_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -299,10 +930,17 @@ static int __devinit moxa_pci_probe(struct pci_dev *pdev, } board = &moxa_boards[i]; - board->basemem = pci_iomap(pdev, 2, 0x4000); + + retval = pci_request_region(pdev, 2, "moxa-base"); + if (retval) { + dev_err(&pdev->dev, "can't request pci region 2\n"); + goto err; + } + + board->basemem = ioremap_nocache(pci_resource_start(pdev, 2), 0x4000); if (board->basemem == NULL) { dev_err(&pdev->dev, "can't remap io space 2\n"); - goto err; + goto err_reg; } board->boardType = board_type; @@ -321,9 +959,21 @@ static int __devinit moxa_pci_probe(struct pci_dev *pdev, } board->busType = MOXA_BUS_TYPE_PCI; + retval = moxa_init_board(board, &pdev->dev); + if (retval) + goto err_base; + pci_set_drvdata(pdev, board); - return (0); + dev_info(&pdev->dev, "board '%s' ready (%u ports, firmware loaded)\n", + moxa_brdname[board_type - 1], board->numPorts); + + return 0; +err_base: + iounmap(board->basemem); + board->basemem = NULL; +err_reg: + pci_release_region(pdev, 2); err: return retval; } @@ -332,8 +982,9 @@ static void __devexit moxa_pci_remove(struct pci_dev *pdev) { struct moxa_board_conf *brd = pci_get_drvdata(pdev); - pci_iounmap(pdev, brd->basemem); - brd->basemem = NULL; + moxa_board_deinit(brd); + + pci_release_region(pdev, 2); } static struct pci_driver moxa_pci_driver = { @@ -346,8 +997,8 @@ static struct pci_driver moxa_pci_driver = { static int __init moxa_init(void) { - int i, numBoards, retval = 0; - struct moxa_port *ch; + unsigned int isabrds = 0; + int retval = 0; printk(KERN_INFO "MOXA Intellio family driver version %s\n", MOXA_VERSION); @@ -368,154 +1019,176 @@ static int __init moxa_init(void) moxaDriver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(moxaDriver, &moxa_ops); - for (i = 0, ch = moxa_ports; i < MAX_PORTS; i++, ch++) { - ch->type = PORT_16550A; - ch->port = i; - ch->close_delay = 5 * HZ / 10; - ch->closing_wait = 30 * HZ; - ch->cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; - init_waitqueue_head(&ch->open_wait); - init_completion(&ch->close_wait); - - setup_timer(&ch->emptyTimer, moxa_check_xmit_empty, - (unsigned long)ch); - } - - pr_debug("Moxa tty devices major number = %d\n", ttymajor); - if (tty_register_driver(moxaDriver)) { - printk(KERN_ERR "Couldn't install MOXA Smartio family driver !\n"); + printk(KERN_ERR "can't register MOXA Smartio tty driver!\n"); put_tty_driver(moxaDriver); return -1; } - mod_timer(&moxaTimer, jiffies + HZ / 50); - - /* Find the boards defined in source code */ - numBoards = 0; - for (i = 0; i < MAX_BOARDS; i++) { - if ((moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA) || - (moxa_isa_boards[i].boardType == MOXA_BOARD_C320_ISA)) { - moxa_boards[numBoards].boardType = moxa_isa_boards[i].boardType; - if (moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA) - moxa_boards[numBoards].numPorts = 8; - else - moxa_boards[numBoards].numPorts = moxa_isa_boards[i].numPorts; - moxa_boards[numBoards].busType = MOXA_BUS_TYPE_ISA; - moxa_boards[numBoards].baseAddr = moxa_isa_boards[i].baseAddr; - pr_debug("Moxa board %2d: %s board(baseAddr=%lx)\n", - numBoards + 1, - moxa_brdname[moxa_boards[numBoards].boardType-1], - moxa_boards[numBoards].baseAddr); - numBoards++; - } - } - /* Find the boards defined form module args. */ + /* Find the boards defined from module args. */ #ifdef MODULE + { + struct moxa_board_conf *brd = moxa_boards; + unsigned int i; for (i = 0; i < MAX_BOARDS; i++) { - if ((type[i] == MOXA_BOARD_C218_ISA) || - (type[i] == MOXA_BOARD_C320_ISA)) { + if (!baseaddr[i]) + break; + if (type[i] == MOXA_BOARD_C218_ISA || + type[i] == MOXA_BOARD_C320_ISA) { pr_debug("Moxa board %2d: %s board(baseAddr=%lx)\n", - numBoards + 1, moxa_brdname[type[i] - 1], - (unsigned long)baseaddr[i]); - if (numBoards >= MAX_BOARDS) { - printk(KERN_WARNING "More than %d MOXA " - "Intellio family boards found. Board " - "is ignored.\n", MAX_BOARDS); + isabrds + 1, moxa_brdname[type[i] - 1], + baseaddr[i]); + brd->boardType = type[i]; + brd->numPorts = type[i] == MOXA_BOARD_C218_ISA ? 8 : + numports[i]; + brd->busType = MOXA_BUS_TYPE_ISA; + brd->basemem = ioremap_nocache(baseaddr[i], 0x4000); + if (!brd->basemem) { + printk(KERN_ERR "MOXA: can't remap %lx\n", + baseaddr[i]); continue; } - moxa_boards[numBoards].boardType = type[i]; - if (moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA) - moxa_boards[numBoards].numPorts = 8; - else - moxa_boards[numBoards].numPorts = numports[i]; - moxa_boards[numBoards].busType = MOXA_BUS_TYPE_ISA; - moxa_boards[numBoards].baseAddr = baseaddr[i]; - numBoards++; + if (moxa_init_board(brd, NULL)) { + iounmap(brd->basemem); + brd->basemem = NULL; + continue; + } + + printk(KERN_INFO "MOXA isa board found at 0x%.8lu and " + "ready (%u ports, firmware loaded)\n", + baseaddr[i], brd->numPorts); + + brd++; + isabrds++; } } + } #endif #ifdef CONFIG_PCI retval = pci_register_driver(&moxa_pci_driver); if (retval) { - printk(KERN_ERR "Can't register moxa pci driver!\n"); - if (numBoards) + printk(KERN_ERR "Can't register MOXA pci driver!\n"); + if (isabrds) retval = 0; } #endif - for (i = 0; i < numBoards; i++) { - moxa_boards[i].basemem = ioremap(moxa_boards[i].baseAddr, - 0x4000); - } - return retval; } static void __exit moxa_exit(void) { - int i; + unsigned int i; - del_timer_sync(&moxaTimer); +#ifdef CONFIG_PCI + pci_unregister_driver(&moxa_pci_driver); +#endif + + for (i = 0; i < MAX_BOARDS; i++) /* ISA boards */ + if (moxa_boards[i].ready) + moxa_board_deinit(&moxa_boards[i]); - for (i = 0; i < MAX_PORTS; i++) - del_timer_sync(&moxa_ports[i].emptyTimer); + del_timer_sync(&moxaTimer); if (tty_unregister_driver(moxaDriver)) printk(KERN_ERR "Couldn't unregister MOXA Intellio family " "serial driver\n"); put_tty_driver(moxaDriver); - -#ifdef CONFIG_PCI - pci_unregister_driver(&moxa_pci_driver); -#endif - - for (i = 0; i < MAX_BOARDS; i++) - if (moxa_boards[i].basemem) - iounmap(moxa_boards[i].basemem); } module_init(moxa_init); module_exit(moxa_exit); +static void moxa_close_port(struct moxa_port *ch) +{ + moxa_shut_down(ch); + MoxaPortFlushData(ch, 2); + ch->asyncflags &= ~ASYNC_NORMAL_ACTIVE; + ch->tty->driver_data = NULL; + ch->tty = NULL; +} + +static int moxa_block_till_ready(struct tty_struct *tty, struct file *filp, + struct moxa_port *ch) +{ + DEFINE_WAIT(wait); + int retval = 0; + u8 dcd; + + while (1) { + prepare_to_wait(&ch->open_wait, &wait, TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp)) { +#ifdef SERIAL_DO_RESTART + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + spin_lock_bh(&moxa_lock); + dcd = ch->DCDState; + spin_unlock_bh(&moxa_lock); + if (dcd) + break; + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + finish_wait(&ch->open_wait, &wait); + + return retval; +} + static int moxa_open(struct tty_struct *tty, struct file *filp) { + struct moxa_board_conf *brd; struct moxa_port *ch; int port; int retval; port = tty->index; if (port == MAX_PORTS) { - return (0); + return capable(CAP_SYS_ADMIN) ? 0 : -EPERM; } - if (!MoxaPortIsValid(port)) { - tty->driver_data = NULL; - return (-ENODEV); + if (mutex_lock_interruptible(&moxa_openlock)) + return -ERESTARTSYS; + brd = &moxa_boards[port / MAX_PORTS_PER_BOARD]; + if (!brd->ready) { + mutex_unlock(&moxa_openlock); + return -ENODEV; } - ch = &moxa_ports[port]; + ch = &brd->ports[port % MAX_PORTS_PER_BOARD]; ch->count++; tty->driver_data = ch; ch->tty = tty; if (!(ch->asyncflags & ASYNC_INITIALIZED)) { ch->statusflags = 0; moxa_set_tty_param(tty, tty->termios); - MoxaPortLineCtrl(ch->port, 1, 1); - MoxaPortEnable(ch->port); + MoxaPortLineCtrl(ch, 1, 1); + MoxaPortEnable(ch); + MoxaSetFifo(ch, ch->type == PORT_16550A); ch->asyncflags |= ASYNC_INITIALIZED; } - retval = moxa_block_till_ready(tty, filp, ch); + mutex_unlock(&moxa_openlock); - moxa_unthrottle(tty); - - if (ch->type == PORT_16550A) { - MoxaSetFifo(ch->port, 1); - } else { - MoxaSetFifo(ch->port, 0); - } + retval = 0; + if (!(filp->f_flags & O_NONBLOCK) && !C_CLOCAL(tty)) + retval = moxa_block_till_ready(tty, filp, ch); + mutex_lock(&moxa_openlock); + if (retval) { + if (ch->count) /* 0 means already hung up... */ + if (--ch->count == 0) + moxa_close_port(ch); + } else + ch->asyncflags |= ASYNC_NORMAL_ACTIVE; + mutex_unlock(&moxa_openlock); - return (retval); + return retval; } static void moxa_close(struct tty_struct *tty, struct file *filp) @@ -524,23 +1197,14 @@ static void moxa_close(struct tty_struct *tty, struct file *filp) int port; port = tty->index; - if (port == MAX_PORTS) { - return; - } - if (!MoxaPortIsValid(port)) { - pr_debug("Invalid portno in moxa_close\n"); - tty->driver_data = NULL; - return; - } - if (tty->driver_data == NULL) { + if (port == MAX_PORTS || tty_hung_up_p(filp)) return; - } - if (tty_hung_up_p(filp)) { - return; - } - ch = (struct moxa_port *) tty->driver_data; - if ((tty->count == 1) && (ch->count != 1)) { + mutex_lock(&moxa_openlock); + ch = tty->driver_data; + if (ch == NULL) + goto unlock; + if (tty->count == 1 && ch->count != 1) { printk(KERN_WARNING "moxa_close: bad serial port count; " "tty->count is 1, ch->count is %d\n", ch->count); ch->count = 1; @@ -550,59 +1214,35 @@ static void moxa_close(struct tty_struct *tty, struct file *filp) "device=%s\n", tty->name); ch->count = 0; } - if (ch->count) { - return; - } - ch->asyncflags |= ASYNC_CLOSING; + if (ch->count) + goto unlock; ch->cflag = tty->termios->c_cflag; if (ch->asyncflags & ASYNC_INITIALIZED) { moxa_setup_empty_event(tty); tty_wait_until_sent(tty, 30 * HZ); /* 30 seconds timeout */ - del_timer_sync(&moxa_ports[ch->port].emptyTimer); - } - moxa_shut_down(ch); - MoxaPortFlushData(port, 2); - - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); - tty_ldisc_flush(tty); - - tty->closing = 0; - ch->event = 0; - ch->tty = NULL; - if (ch->blocked_open) { - if (ch->close_delay) { - msleep_interruptible(jiffies_to_msecs(ch->close_delay)); - } - wake_up_interruptible(&ch->open_wait); } - ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); - complete_all(&ch->close_wait); + + moxa_close_port(ch); +unlock: + mutex_unlock(&moxa_openlock); } static int moxa_write(struct tty_struct *tty, const unsigned char *buf, int count) { - struct moxa_port *ch; - int len, port; - unsigned long flags; + struct moxa_port *ch = tty->driver_data; + int len; - ch = (struct moxa_port *) tty->driver_data; if (ch == NULL) - return (0); - port = ch->port; + return 0; - spin_lock_irqsave(&moxa_lock, flags); - len = MoxaPortWriteData(port, (unsigned char *) buf, count); - spin_unlock_irqrestore(&moxa_lock, flags); + spin_lock_bh(&moxa_lock); + len = MoxaPortWriteData(ch, buf, count); + spin_unlock_bh(&moxa_lock); - /********************************************* - if ( !(ch->statusflags & LOWWAIT) && - ((len != count) || (MoxaPortTxFree(port) <= 100)) ) - ************************************************/ ch->statusflags |= LOWWAIT; - return (len); + return len; } static int moxa_write_room(struct tty_struct *tty) @@ -610,27 +1250,27 @@ static int moxa_write_room(struct tty_struct *tty) struct moxa_port *ch; if (tty->stopped) - return (0); - ch = (struct moxa_port *) tty->driver_data; + return 0; + ch = tty->driver_data; if (ch == NULL) - return (0); - return (MoxaPortTxFree(ch->port)); + return 0; + return MoxaPortTxFree(ch); } static void moxa_flush_buffer(struct tty_struct *tty) { - struct moxa_port *ch = (struct moxa_port *) tty->driver_data; + struct moxa_port *ch = tty->driver_data; if (ch == NULL) return; - MoxaPortFlushData(ch->port, 1); + MoxaPortFlushData(ch, 1); tty_wakeup(tty); } static int moxa_chars_in_buffer(struct tty_struct *tty) { + struct moxa_port *ch = tty->driver_data; int chars; - struct moxa_port *ch = (struct moxa_port *) tty->driver_data; /* * Sigh...I have to check if driver_data is NULL here, because @@ -639,8 +1279,9 @@ static int moxa_chars_in_buffer(struct tty_struct *tty) * routine. And since the open() failed, we return 0 here. TDJ */ if (ch == NULL) - return (0); - chars = MoxaPortTxQueue(ch->port); + return 0; + lock_kernel(); + chars = MoxaPortTxQueue(ch); if (chars) { /* * Make it possible to wakeup anything waiting for output @@ -649,73 +1290,54 @@ static int moxa_chars_in_buffer(struct tty_struct *tty) if (!(ch->statusflags & EMPTYWAIT)) moxa_setup_empty_event(tty); } - return (chars); -} - -static void moxa_flush_chars(struct tty_struct *tty) -{ - /* - * Don't think I need this, because this is called to empty the TX - * buffer for the 16450, 16550, etc. - */ -} - -static void moxa_put_char(struct tty_struct *tty, unsigned char c) -{ - struct moxa_port *ch; - int port; - unsigned long flags; - - ch = (struct moxa_port *) tty->driver_data; - if (ch == NULL) - return; - port = ch->port; - spin_lock_irqsave(&moxa_lock, flags); - MoxaPortWriteData(port, &c, 1); - spin_unlock_irqrestore(&moxa_lock, flags); - /************************************************ - if ( !(ch->statusflags & LOWWAIT) && (MoxaPortTxFree(port) <= 100) ) - *************************************************/ - ch->statusflags |= LOWWAIT; + unlock_kernel(); + return chars; } static int moxa_tiocmget(struct tty_struct *tty, struct file *file) { - struct moxa_port *ch = (struct moxa_port *) tty->driver_data; - int port; + struct moxa_port *ch; int flag = 0, dtr, rts; - port = tty->index; - if ((port != MAX_PORTS) && (!ch)) - return (-EINVAL); + mutex_lock(&moxa_openlock); + ch = tty->driver_data; + if (!ch) { + mutex_unlock(&moxa_openlock); + return -EINVAL; + } - MoxaPortGetLineOut(ch->port, &dtr, &rts); + MoxaPortGetLineOut(ch, &dtr, &rts); if (dtr) flag |= TIOCM_DTR; if (rts) flag |= TIOCM_RTS; - dtr = MoxaPortLineStatus(ch->port); + dtr = MoxaPortLineStatus(ch); if (dtr & 1) flag |= TIOCM_CTS; if (dtr & 2) flag |= TIOCM_DSR; if (dtr & 4) flag |= TIOCM_CD; + mutex_unlock(&moxa_openlock); return flag; } static int moxa_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { - struct moxa_port *ch = (struct moxa_port *) tty->driver_data; + struct moxa_port *ch; int port; int dtr, rts; port = tty->index; - if ((port != MAX_PORTS) && (!ch)) - return (-EINVAL); + mutex_lock(&moxa_openlock); + ch = tty->driver_data; + if (!ch) { + mutex_unlock(&moxa_openlock); + return -EINVAL; + } - MoxaPortGetLineOut(ch->port, &dtr, &rts); + MoxaPortGetLineOut(ch, &dtr, &rts); if (set & TIOCM_RTS) rts = 1; if (set & TIOCM_DTR) @@ -724,105 +1346,51 @@ static int moxa_tiocmset(struct tty_struct *tty, struct file *file, rts = 0; if (clear & TIOCM_DTR) dtr = 0; - MoxaPortLineCtrl(ch->port, dtr, rts); + MoxaPortLineCtrl(ch, dtr, rts); + mutex_unlock(&moxa_openlock); return 0; } -static int moxa_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct moxa_port *ch = (struct moxa_port *) tty->driver_data; - register int port; - void __user *argp = (void __user *)arg; - int retval; - - port = tty->index; - if ((port != MAX_PORTS) && (!ch)) - return (-EINVAL); - - switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return (retval); - moxa_setup_empty_event(tty); - tty_wait_until_sent(tty, 0); - if (!arg) - MoxaPortSendBreak(ch->port, 0); - return (0); - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return (retval); - moxa_setup_empty_event(tty); - tty_wait_until_sent(tty, 0); - MoxaPortSendBreak(ch->port, arg); - return (0); - case TIOCGSOFTCAR: - return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *) argp); - case TIOCSSOFTCAR: - if(get_user(retval, (unsigned long __user *) argp)) - return -EFAULT; - arg = retval; - tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - if (C_CLOCAL(tty)) - ch->asyncflags &= ~ASYNC_CHECK_CD; - else - ch->asyncflags |= ASYNC_CHECK_CD; - return (0); - case TIOCGSERIAL: - return moxa_get_serial_info(ch, argp); - - case TIOCSSERIAL: - return moxa_set_serial_info(ch, argp); - default: - retval = MoxaDriverIoctl(cmd, arg, port); - } - return (retval); -} - static void moxa_throttle(struct tty_struct *tty) { - struct moxa_port *ch = (struct moxa_port *) tty->driver_data; + struct moxa_port *ch = tty->driver_data; ch->statusflags |= THROTTLE; } static void moxa_unthrottle(struct tty_struct *tty) { - struct moxa_port *ch = (struct moxa_port *) tty->driver_data; + struct moxa_port *ch = tty->driver_data; ch->statusflags &= ~THROTTLE; } static void moxa_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) + struct ktermios *old_termios) { - struct moxa_port *ch = (struct moxa_port *) tty->driver_data; + struct moxa_port *ch = tty->driver_data; if (ch == NULL) return; moxa_set_tty_param(tty, old_termios); - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) + if (!(old_termios->c_cflag & CLOCAL) && C_CLOCAL(tty)) wake_up_interruptible(&ch->open_wait); } static void moxa_stop(struct tty_struct *tty) { - struct moxa_port *ch = (struct moxa_port *) tty->driver_data; + struct moxa_port *ch = tty->driver_data; if (ch == NULL) return; - MoxaPortTxDisable(ch->port); + MoxaPortTxDisable(ch); ch->statusflags |= TXSTOPPED; } static void moxa_start(struct tty_struct *tty) { - struct moxa_port *ch = (struct moxa_port *) tty->driver_data; + struct moxa_port *ch = tty->driver_data; if (ch == NULL) return; @@ -830,91 +1398,143 @@ static void moxa_start(struct tty_struct *tty) if (!(ch->statusflags & TXSTOPPED)) return; - MoxaPortTxEnable(ch->port); + MoxaPortTxEnable(ch); ch->statusflags &= ~TXSTOPPED; } static void moxa_hangup(struct tty_struct *tty) { - struct moxa_port *ch = (struct moxa_port *) tty->driver_data; + struct moxa_port *ch; - moxa_flush_buffer(tty); - moxa_shut_down(ch); - ch->event = 0; + mutex_lock(&moxa_openlock); + ch = tty->driver_data; + if (ch == NULL) { + mutex_unlock(&moxa_openlock); + return; + } ch->count = 0; - ch->asyncflags &= ~ASYNC_NORMAL_ACTIVE; - ch->tty = NULL; + moxa_close_port(ch); + mutex_unlock(&moxa_openlock); + wake_up_interruptible(&ch->open_wait); } -static void moxa_poll(unsigned long ignored) +static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) { - register int card; - struct moxa_port *ch; - struct tty_struct *tp; - int i, ports; + dcd = !!dcd; - del_timer(&moxaTimer); + if (dcd != p->DCDState && p->tty && C_CLOCAL(p->tty)) { + if (!dcd) + tty_hangup(p->tty); + } + p->DCDState = dcd; +} - if (MoxaDriverPoll() < 0) { - mod_timer(&moxaTimer, jiffies + HZ / 50); - return; +static int moxa_poll_port(struct moxa_port *p, unsigned int handle, + u16 __iomem *ip) +{ + struct tty_struct *tty = p->tty; + void __iomem *ofsAddr; + unsigned int inited = p->asyncflags & ASYNC_INITIALIZED; + u16 intr; + + if (tty) { + if ((p->statusflags & EMPTYWAIT) && + MoxaPortTxQueue(p) == 0) { + p->statusflags &= ~EMPTYWAIT; + tty_wakeup(tty); + } + if ((p->statusflags & LOWWAIT) && !tty->stopped && + MoxaPortTxQueue(p) <= WAKEUP_CHARS) { + p->statusflags &= ~LOWWAIT; + tty_wakeup(tty); + } + + if (inited && !(p->statusflags & THROTTLE) && + MoxaPortRxQueue(p) > 0) { /* RX */ + MoxaPortReadData(p); + tty_schedule_flip(tty); + } + } else { + p->statusflags &= ~EMPTYWAIT; + MoxaPortFlushData(p, 0); /* flush RX */ } + + if (!handle) /* nothing else to do */ + return 0; + + intr = readw(ip); /* port irq status */ + if (intr == 0) + return 0; + + writew(0, ip); /* ACK port */ + ofsAddr = p->tableAddr; + if (intr & IntrTx) /* disable tx intr */ + writew(readw(ofsAddr + HostStat) & ~WakeupTx, + ofsAddr + HostStat); + + if (!inited) + return 0; + + if (tty && (intr & IntrBreak) && !I_IGNBRK(tty)) { /* BREAK */ + tty_insert_flip_char(tty, 0, TTY_BREAK); + tty_schedule_flip(tty); + } + + if (intr & IntrLine) + moxa_new_dcdstate(p, readb(ofsAddr + FlagStat) & DCD_state); + + return 0; +} + +static void moxa_poll(unsigned long ignored) +{ + struct moxa_board_conf *brd; + u16 __iomem *ip; + unsigned int card, port, served = 0; + + spin_lock(&moxa_lock); for (card = 0; card < MAX_BOARDS; card++) { - if ((ports = MoxaPortsOfCard(card)) <= 0) + brd = &moxa_boards[card]; + if (!brd->ready) continue; - ch = &moxa_ports[card * MAX_PORTS_PER_BOARD]; - for (i = 0; i < ports; i++, ch++) { - if ((ch->asyncflags & ASYNC_INITIALIZED) == 0) - continue; - if (!(ch->statusflags & THROTTLE) && - (MoxaPortRxQueue(ch->port) > 0)) - moxa_receive_data(ch); - if ((tp = ch->tty) == 0) - continue; - if (ch->statusflags & LOWWAIT) { - if (MoxaPortTxQueue(ch->port) <= WAKEUP_CHARS) { - if (!tp->stopped) { - ch->statusflags &= ~LOWWAIT; - tty_wakeup(tp); - } - } - } - if (!I_IGNBRK(tp) && (MoxaPortResetBrkCnt(ch->port) > 0)) { - tty_insert_flip_char(tp, 0, TTY_BREAK); - tty_schedule_flip(tp); - } - if (MoxaPortDCDChange(ch->port)) { - if (ch->asyncflags & ASYNC_CHECK_CD) { - if (MoxaPortDCDON(ch->port)) - wake_up_interruptible(&ch->open_wait); - else { - tty_hangup(tp); - wake_up_interruptible(&ch->open_wait); - ch->asyncflags &= ~ASYNC_NORMAL_ACTIVE; - } + + served++; + + ip = NULL; + if (readb(brd->intPend) == 0xff) + ip = brd->intTable + readb(brd->intNdx); + + for (port = 0; port < brd->numPorts; port++) + moxa_poll_port(&brd->ports[port], !!ip, ip + port); + + if (ip) + writeb(0, brd->intPend); /* ACK */ + + if (moxaLowWaterChk) { + struct moxa_port *p = brd->ports; + for (port = 0; port < brd->numPorts; port++, p++) + if (p->lowChkFlag) { + p->lowChkFlag = 0; + moxa_low_water_check(p->tableAddr); } - } } } + moxaLowWaterChk = 0; - mod_timer(&moxaTimer, jiffies + HZ / 50); + if (served) + mod_timer(&moxaTimer, jiffies + HZ / 50); + spin_unlock(&moxa_lock); } /******************************************************************************/ static void moxa_set_tty_param(struct tty_struct *tty, struct ktermios *old_termios) { - register struct ktermios *ts; - struct moxa_port *ch; + register struct ktermios *ts = tty->termios; + struct moxa_port *ch = tty->driver_data; int rts, cts, txflow, rxflow, xany, baud; - ch = (struct moxa_port *) tty->driver_data; - ts = tty->termios; - if (ts->c_cflag & CLOCAL) - ch->asyncflags &= ~ASYNC_CHECK_CD; - else - ch->asyncflags |= ASYNC_CHECK_CD; rts = cts = txflow = rxflow = xany = 0; if (ts->c_cflag & CRTSCTS) rts = cts = 1; @@ -927,776 +1547,60 @@ static void moxa_set_tty_param(struct tty_struct *tty, struct ktermios *old_term /* Clear the features we don't support */ ts->c_cflag &= ~CMSPAR; - MoxaPortFlowCtrl(ch->port, rts, cts, txflow, rxflow, xany); - baud = MoxaPortSetTermio(ch->port, ts, tty_get_baud_rate(tty)); + MoxaPortFlowCtrl(ch, rts, cts, txflow, rxflow, xany); + baud = MoxaPortSetTermio(ch, ts, tty_get_baud_rate(tty)); if (baud == -1) baud = tty_termios_baud_rate(old_termios); /* Not put the baud rate into the termios data */ tty_encode_baud_rate(tty, baud, baud); } -static int moxa_block_till_ready(struct tty_struct *tty, struct file *filp, - struct moxa_port *ch) -{ - DECLARE_WAITQUEUE(wait,current); - unsigned long flags; - int retval; - int do_clocal = C_CLOCAL(tty); - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (tty_hung_up_p(filp) || (ch->asyncflags & ASYNC_CLOSING)) { - if (ch->asyncflags & ASYNC_CLOSING) - wait_for_completion_interruptible(&ch->close_wait); -#ifdef SERIAL_DO_RESTART - if (ch->asyncflags & ASYNC_HUP_NOTIFY) - return (-EAGAIN); - else - return (-ERESTARTSYS); -#else - return (-EAGAIN); -#endif - } - /* - * If non-blocking mode is set, then make the check up front - * and then exit. - */ - if (filp->f_flags & O_NONBLOCK) { - ch->asyncflags |= ASYNC_NORMAL_ACTIVE; - return (0); - } - /* - * Block waiting for the carrier detect and the line to become free - */ - retval = 0; - add_wait_queue(&ch->open_wait, &wait); - pr_debug("block_til_ready before block: ttys%d, count = %d\n", - ch->port, ch->count); - spin_lock_irqsave(&moxa_lock, flags); - if (!tty_hung_up_p(filp)) - ch->count--; - ch->blocked_open++; - spin_unlock_irqrestore(&moxa_lock, flags); - - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(ch->asyncflags & ASYNC_INITIALIZED)) { -#ifdef SERIAL_DO_RESTART - if (ch->asyncflags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; -#else - retval = -EAGAIN; -#endif - break; - } - if (!(ch->asyncflags & ASYNC_CLOSING) && (do_clocal || - MoxaPortDCDON(ch->port))) - break; - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&ch->open_wait, &wait); - - spin_lock_irqsave(&moxa_lock, flags); - if (!tty_hung_up_p(filp)) - ch->count++; - ch->blocked_open--; - spin_unlock_irqrestore(&moxa_lock, flags); - pr_debug("block_til_ready after blocking: ttys%d, count = %d\n", - ch->port, ch->count); - if (retval) - return (retval); - /* FIXME: review to see if we need to use set_bit on these */ - ch->asyncflags |= ASYNC_NORMAL_ACTIVE; - return 0; -} - static void moxa_setup_empty_event(struct tty_struct *tty) { struct moxa_port *ch = tty->driver_data; - unsigned long flags; - spin_lock_irqsave(&moxa_lock, flags); + spin_lock_bh(&moxa_lock); ch->statusflags |= EMPTYWAIT; - mod_timer(&moxa_ports[ch->port].emptyTimer, jiffies + HZ); - spin_unlock_irqrestore(&moxa_lock, flags); -} - -static void moxa_check_xmit_empty(unsigned long data) -{ - struct moxa_port *ch; - - ch = (struct moxa_port *) data; - if (ch->tty && (ch->statusflags & EMPTYWAIT)) { - if (MoxaPortTxQueue(ch->port) == 0) { - ch->statusflags &= ~EMPTYWAIT; - tty_wakeup(ch->tty); - return; - } - mod_timer(&moxa_ports[ch->port].emptyTimer, - round_jiffies(jiffies + HZ)); - } else - ch->statusflags &= ~EMPTYWAIT; + spin_unlock_bh(&moxa_lock); } static void moxa_shut_down(struct moxa_port *ch) { - struct tty_struct *tp; + struct tty_struct *tp = ch->tty; if (!(ch->asyncflags & ASYNC_INITIALIZED)) return; - tp = ch->tty; - - MoxaPortDisable(ch->port); + MoxaPortDisable(ch); /* * If we're a modem control device and HUPCL is on, drop RTS & DTR. */ - if (tp->termios->c_cflag & HUPCL) - MoxaPortLineCtrl(ch->port, 0, 0); + if (C_HUPCL(tp)) + MoxaPortLineCtrl(ch, 0, 0); + spin_lock_bh(&moxa_lock); ch->asyncflags &= ~ASYNC_INITIALIZED; + spin_unlock_bh(&moxa_lock); } -static void moxa_receive_data(struct moxa_port *ch) -{ - struct tty_struct *tp; - struct ktermios *ts; - unsigned long flags; - - ts = NULL; - tp = ch->tty; - if (tp) - ts = tp->termios; - /************************************************** - if ( !tp || !ts || !(ts->c_cflag & CREAD) ) { - *****************************************************/ - if (!tp || !ts) { - MoxaPortFlushData(ch->port, 0); - return; - } - spin_lock_irqsave(&moxa_lock, flags); - MoxaPortReadData(ch->port, tp); - spin_unlock_irqrestore(&moxa_lock, flags); - tty_schedule_flip(tp); -} - -#define Magic_code 0x404 - -/* - * System Configuration - */ -/* - * for C218 BIOS initialization - */ -#define C218_ConfBase 0x800 -#define C218_status (C218_ConfBase + 0) /* BIOS running status */ -#define C218_diag (C218_ConfBase + 2) /* diagnostic status */ -#define C218_key (C218_ConfBase + 4) /* WORD (0x218 for C218) */ -#define C218DLoad_len (C218_ConfBase + 6) /* WORD */ -#define C218check_sum (C218_ConfBase + 8) /* BYTE */ -#define C218chksum_ok (C218_ConfBase + 0x0a) /* BYTE (1:ok) */ -#define C218_TestRx (C218_ConfBase + 0x10) /* 8 bytes for 8 ports */ -#define C218_TestTx (C218_ConfBase + 0x18) /* 8 bytes for 8 ports */ -#define C218_RXerr (C218_ConfBase + 0x20) /* 8 bytes for 8 ports */ -#define C218_ErrFlag (C218_ConfBase + 0x28) /* 8 bytes for 8 ports */ - -#define C218_LoadBuf 0x0F00 -#define C218_KeyCode 0x218 -#define CP204J_KeyCode 0x204 - -/* - * for C320 BIOS initialization - */ -#define C320_ConfBase 0x800 -#define C320_LoadBuf 0x0f00 -#define STS_init 0x05 /* for C320_status */ - -#define C320_status C320_ConfBase + 0 /* BIOS running status */ -#define C320_diag C320_ConfBase + 2 /* diagnostic status */ -#define C320_key C320_ConfBase + 4 /* WORD (0320H for C320) */ -#define C320DLoad_len C320_ConfBase + 6 /* WORD */ -#define C320check_sum C320_ConfBase + 8 /* WORD */ -#define C320chksum_ok C320_ConfBase + 0x0a /* WORD (1:ok) */ -#define C320bapi_len C320_ConfBase + 0x0c /* WORD */ -#define C320UART_no C320_ConfBase + 0x0e /* WORD */ - -#define C320_KeyCode 0x320 - -#define FixPage_addr 0x0000 /* starting addr of static page */ -#define DynPage_addr 0x2000 /* starting addr of dynamic page */ -#define C218_start 0x3000 /* starting addr of C218 BIOS prg */ -#define Control_reg 0x1ff0 /* select page and reset control */ -#define HW_reset 0x80 - -/* - * Function Codes - */ -#define FC_CardReset 0x80 -#define FC_ChannelReset 1 /* C320 firmware not supported */ -#define FC_EnableCH 2 -#define FC_DisableCH 3 -#define FC_SetParam 4 -#define FC_SetMode 5 -#define FC_SetRate 6 -#define FC_LineControl 7 -#define FC_LineStatus 8 -#define FC_XmitControl 9 -#define FC_FlushQueue 10 -#define FC_SendBreak 11 -#define FC_StopBreak 12 -#define FC_LoopbackON 13 -#define FC_LoopbackOFF 14 -#define FC_ClrIrqTable 15 -#define FC_SendXon 16 -#define FC_SetTermIrq 17 /* C320 firmware not supported */ -#define FC_SetCntIrq 18 /* C320 firmware not supported */ -#define FC_SetBreakIrq 19 -#define FC_SetLineIrq 20 -#define FC_SetFlowCtl 21 -#define FC_GenIrq 22 -#define FC_InCD180 23 -#define FC_OutCD180 24 -#define FC_InUARTreg 23 -#define FC_OutUARTreg 24 -#define FC_SetXonXoff 25 -#define FC_OutCD180CCR 26 -#define FC_ExtIQueue 27 -#define FC_ExtOQueue 28 -#define FC_ClrLineIrq 29 -#define FC_HWFlowCtl 30 -#define FC_GetClockRate 35 -#define FC_SetBaud 36 -#define FC_SetDataMode 41 -#define FC_GetCCSR 43 -#define FC_GetDataError 45 -#define FC_RxControl 50 -#define FC_ImmSend 51 -#define FC_SetXonState 52 -#define FC_SetXoffState 53 -#define FC_SetRxFIFOTrig 54 -#define FC_SetTxFIFOCnt 55 -#define FC_UnixRate 56 -#define FC_UnixResetTimer 57 - -#define RxFIFOTrig1 0 -#define RxFIFOTrig4 1 -#define RxFIFOTrig8 2 -#define RxFIFOTrig14 3 - -/* - * Dual-Ported RAM - */ -#define DRAM_global 0 -#define INT_data (DRAM_global + 0) -#define Config_base (DRAM_global + 0x108) - -#define IRQindex (INT_data + 0) -#define IRQpending (INT_data + 4) -#define IRQtable (INT_data + 8) - -/* - * Interrupt Status - */ -#define IntrRx 0x01 /* receiver data O.K. */ -#define IntrTx 0x02 /* transmit buffer empty */ -#define IntrFunc 0x04 /* function complete */ -#define IntrBreak 0x08 /* received break */ -#define IntrLine 0x10 /* line status change - for transmitter */ -#define IntrIntr 0x20 /* received INTR code */ -#define IntrQuit 0x40 /* received QUIT code */ -#define IntrEOF 0x80 /* received EOF code */ - -#define IntrRxTrigger 0x100 /* rx data count reach tigger value */ -#define IntrTxTrigger 0x200 /* tx data count below trigger value */ - -#define Magic_no (Config_base + 0) -#define Card_model_no (Config_base + 2) -#define Total_ports (Config_base + 4) -#define Module_cnt (Config_base + 8) -#define Module_no (Config_base + 10) -#define Timer_10ms (Config_base + 14) -#define Disable_IRQ (Config_base + 20) -#define TMS320_PORT1 (Config_base + 22) -#define TMS320_PORT2 (Config_base + 24) -#define TMS320_CLOCK (Config_base + 26) - -/* - * DATA BUFFER in DRAM - */ -#define Extern_table 0x400 /* Base address of the external table - (24 words * 64) total 3K bytes - (24 words * 128) total 6K bytes */ -#define Extern_size 0x60 /* 96 bytes */ -#define RXrptr 0x00 /* read pointer for RX buffer */ -#define RXwptr 0x02 /* write pointer for RX buffer */ -#define TXrptr 0x04 /* read pointer for TX buffer */ -#define TXwptr 0x06 /* write pointer for TX buffer */ -#define HostStat 0x08 /* IRQ flag and general flag */ -#define FlagStat 0x0A -#define FlowControl 0x0C /* B7 B6 B5 B4 B3 B2 B1 B0 */ - /* x x x x | | | | */ - /* | | | + CTS flow */ - /* | | +--- RTS flow */ - /* | +------ TX Xon/Xoff */ - /* +--------- RX Xon/Xoff */ -#define Break_cnt 0x0E /* received break count */ -#define CD180TXirq 0x10 /* if non-0: enable TX irq */ -#define RX_mask 0x12 -#define TX_mask 0x14 -#define Ofs_rxb 0x16 -#define Ofs_txb 0x18 -#define Page_rxb 0x1A -#define Page_txb 0x1C -#define EndPage_rxb 0x1E -#define EndPage_txb 0x20 -#define Data_error 0x22 -#define RxTrigger 0x28 -#define TxTrigger 0x2a - -#define rRXwptr 0x34 -#define Low_water 0x36 - -#define FuncCode 0x40 -#define FuncArg 0x42 -#define FuncArg1 0x44 - -#define C218rx_size 0x2000 /* 8K bytes */ -#define C218tx_size 0x8000 /* 32K bytes */ - -#define C218rx_mask (C218rx_size - 1) -#define C218tx_mask (C218tx_size - 1) - -#define C320p8rx_size 0x2000 -#define C320p8tx_size 0x8000 -#define C320p8rx_mask (C320p8rx_size - 1) -#define C320p8tx_mask (C320p8tx_size - 1) - -#define C320p16rx_size 0x2000 -#define C320p16tx_size 0x4000 -#define C320p16rx_mask (C320p16rx_size - 1) -#define C320p16tx_mask (C320p16tx_size - 1) - -#define C320p24rx_size 0x2000 -#define C320p24tx_size 0x2000 -#define C320p24rx_mask (C320p24rx_size - 1) -#define C320p24tx_mask (C320p24tx_size - 1) - -#define C320p32rx_size 0x1000 -#define C320p32tx_size 0x1000 -#define C320p32rx_mask (C320p32rx_size - 1) -#define C320p32tx_mask (C320p32tx_size - 1) - -#define Page_size 0x2000 -#define Page_mask (Page_size - 1) -#define C218rx_spage 3 -#define C218tx_spage 4 -#define C218rx_pageno 1 -#define C218tx_pageno 4 -#define C218buf_pageno 5 - -#define C320p8rx_spage 3 -#define C320p8tx_spage 4 -#define C320p8rx_pgno 1 -#define C320p8tx_pgno 4 -#define C320p8buf_pgno 5 - -#define C320p16rx_spage 3 -#define C320p16tx_spage 4 -#define C320p16rx_pgno 1 -#define C320p16tx_pgno 2 -#define C320p16buf_pgno 3 - -#define C320p24rx_spage 3 -#define C320p24tx_spage 4 -#define C320p24rx_pgno 1 -#define C320p24tx_pgno 1 -#define C320p24buf_pgno 2 - -#define C320p32rx_spage 3 -#define C320p32tx_ofs C320p32rx_size -#define C320p32tx_spage 3 -#define C320p32buf_pgno 1 - -/* - * Host Status - */ -#define WakeupRx 0x01 -#define WakeupTx 0x02 -#define WakeupBreak 0x08 -#define WakeupLine 0x10 -#define WakeupIntr 0x20 -#define WakeupQuit 0x40 -#define WakeupEOF 0x80 /* used in VTIME control */ -#define WakeupRxTrigger 0x100 -#define WakeupTxTrigger 0x200 -/* - * Flag status - */ -#define Rx_over 0x01 -#define Xoff_state 0x02 -#define Tx_flowOff 0x04 -#define Tx_enable 0x08 -#define CTS_state 0x10 -#define DSR_state 0x20 -#define DCD_state 0x80 -/* - * FlowControl - */ -#define CTS_FlowCtl 1 -#define RTS_FlowCtl 2 -#define Tx_FlowCtl 4 -#define Rx_FlowCtl 8 -#define IXM_IXANY 0x10 - -#define LowWater 128 - -#define DTR_ON 1 -#define RTS_ON 2 -#define CTS_ON 1 -#define DSR_ON 2 -#define DCD_ON 8 - -/* mode definition */ -#define MX_CS8 0x03 -#define MX_CS7 0x02 -#define MX_CS6 0x01 -#define MX_CS5 0x00 - -#define MX_STOP1 0x00 -#define MX_STOP15 0x04 -#define MX_STOP2 0x08 - -#define MX_PARNONE 0x00 -#define MX_PAREVEN 0x40 -#define MX_PARODD 0xC0 - -/* - * Query - */ - -struct mon_str { - int tick; - int rxcnt[MAX_PORTS]; - int txcnt[MAX_PORTS]; -}; - -#define DCD_changed 0x01 -#define DCD_oldstate 0x80 - -static unsigned char moxaBuff[10240]; -static int moxaLowWaterChk; -static int moxaCard; -static struct mon_str moxaLog; -static int moxaFuncTout = HZ / 2; - -static void moxafunc(void __iomem *, int, ushort); -static void moxa_wait_finish(void __iomem *); -static void moxa_low_water_check(void __iomem *); -static int moxaloadbios(int, unsigned char __user *, int); -static int moxafindcard(int); -static int moxaload320b(int, unsigned char __user *, int); -static int moxaloadcode(int, unsigned char __user *, int); -static int moxaloadc218(int, void __iomem *, int); -static int moxaloadc320(int, void __iomem *, int, int *); - /***************************************************************************** * Driver level functions: * - * 1. MoxaDriverInit(void); * - * 2. MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port); * - * 3. MoxaDriverPoll(void); * *****************************************************************************/ -void MoxaDriverInit(void) -{ - struct moxa_port *p; - unsigned int i; - moxaFuncTout = HZ / 2; /* 500 mini-seconds */ - moxaCard = 0; - moxaLog.tick = 0; - moxaLowWaterChk = 0; - for (i = 0; i < MAX_PORTS; i++) { - p = &moxa_ports[i]; - p->chkPort = 0; - p->lowChkFlag = 0; - p->lineCtrl = 0; - moxaLog.rxcnt[i] = 0; - moxaLog.txcnt[i] = 0; - } -} - -#define MOXA 0x400 -#define MOXA_GET_IQUEUE (MOXA + 1) /* get input buffered count */ -#define MOXA_GET_OQUEUE (MOXA + 2) /* get output buffered count */ -#define MOXA_INIT_DRIVER (MOXA + 6) /* moxaCard=0 */ -#define MOXA_LOAD_BIOS (MOXA + 9) /* download BIOS */ -#define MOXA_FIND_BOARD (MOXA + 10) /* Check if MOXA card exist? */ -#define MOXA_LOAD_C320B (MOXA + 11) /* download 320B firmware */ -#define MOXA_LOAD_CODE (MOXA + 12) /* download firmware */ -#define MOXA_GETDATACOUNT (MOXA + 23) -#define MOXA_GET_IOQUEUE (MOXA + 27) -#define MOXA_FLUSH_QUEUE (MOXA + 28) -#define MOXA_GET_CONF (MOXA + 35) /* configuration */ -#define MOXA_GET_MAJOR (MOXA + 63) -#define MOXA_GET_CUMAJOR (MOXA + 64) -#define MOXA_GETMSTATUS (MOXA + 65) - -struct dl_str { - char __user *buf; - int len; - int cardno; -}; - -static struct dl_str dltmp; - -void MoxaPortFlushData(int port, int mode) +static void MoxaPortFlushData(struct moxa_port *port, int mode) { void __iomem *ofsAddr; - if ((mode < 0) || (mode > 2)) + if (mode < 0 || mode > 2) return; - ofsAddr = moxa_ports[port].tableAddr; + ofsAddr = port->tableAddr; moxafunc(ofsAddr, FC_FlushQueue, mode); if (mode != 1) { - moxa_ports[port].lowChkFlag = 0; + port->lowChkFlag = 0; moxa_low_water_check(ofsAddr); } } -int MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port) -{ - int i; - int status; - int MoxaPortTxQueue(int), MoxaPortRxQueue(int); - void __user *argp = (void __user *)arg; - - if (port == MAX_PORTS) { - if ((cmd != MOXA_GET_CONF) && (cmd != MOXA_INIT_DRIVER) && - (cmd != MOXA_LOAD_BIOS) && (cmd != MOXA_FIND_BOARD) && (cmd != MOXA_LOAD_C320B) && - (cmd != MOXA_LOAD_CODE) && (cmd != MOXA_GETDATACOUNT) && - (cmd != MOXA_GET_IOQUEUE) && (cmd != MOXA_GET_MAJOR) && - (cmd != MOXA_GET_CUMAJOR) && (cmd != MOXA_GETMSTATUS)) - return (-EINVAL); - } - switch (cmd) { - case MOXA_GET_CONF: - if(copy_to_user(argp, &moxa_boards, MAX_BOARDS * - sizeof(struct moxa_board_conf))) - return -EFAULT; - return (0); - case MOXA_INIT_DRIVER: - if ((int) arg == 0x404) - MoxaDriverInit(); - return (0); - case MOXA_GETDATACOUNT: - moxaLog.tick = jiffies; - if(copy_to_user(argp, &moxaLog, sizeof(struct mon_str))) - return -EFAULT; - return (0); - case MOXA_FLUSH_QUEUE: - MoxaPortFlushData(port, arg); - return (0); - case MOXA_GET_IOQUEUE: { - struct moxaq_str __user *argm = argp; - struct moxaq_str tmp; - - for (i = 0; i < MAX_PORTS; i++, argm++) { - memset(&tmp, 0, sizeof(tmp)); - if (moxa_ports[i].chkPort) { - tmp.inq = MoxaPortRxQueue(i); - tmp.outq = MoxaPortTxQueue(i); - } - if (copy_to_user(argm, &tmp, sizeof(tmp))) - return -EFAULT; - } - return (0); - } case MOXA_GET_OQUEUE: - i = MoxaPortTxQueue(port); - return put_user(i, (unsigned long __user *)argp); - case MOXA_GET_IQUEUE: - i = MoxaPortRxQueue(port); - return put_user(i, (unsigned long __user *)argp); - case MOXA_GET_MAJOR: - if(copy_to_user(argp, &ttymajor, sizeof(int))) - return -EFAULT; - return 0; - case MOXA_GET_CUMAJOR: - i = 0; - if(copy_to_user(argp, &i, sizeof(int))) - return -EFAULT; - return 0; - case MOXA_GETMSTATUS: { - struct mxser_mstatus __user *argm = argp; - struct mxser_mstatus tmp; - struct moxa_port *p; - - for (i = 0; i < MAX_PORTS; i++, argm++) { - p = &moxa_ports[i]; - memset(&tmp, 0, sizeof(tmp)); - if (!p->chkPort) { - goto copy; - } else { - status = MoxaPortLineStatus(p->port); - if (status & 1) - tmp.cts = 1; - if (status & 2) - tmp.dsr = 1; - if (status & 4) - tmp.dcd = 1; - } - - if (!p->tty || !p->tty->termios) - tmp.cflag = p->cflag; - else - tmp.cflag = p->tty->termios->c_cflag; -copy: - if (copy_to_user(argm, &tmp, sizeof(tmp))) - return -EFAULT; - } - return 0; - } default: - return (-ENOIOCTLCMD); - case MOXA_LOAD_BIOS: - case MOXA_FIND_BOARD: - case MOXA_LOAD_C320B: - case MOXA_LOAD_CODE: - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - break; - } - - if(copy_from_user(&dltmp, argp, sizeof(struct dl_str))) - return -EFAULT; - if(dltmp.cardno < 0 || dltmp.cardno >= MAX_BOARDS || dltmp.len < 0) - return -EINVAL; - - switch(cmd) - { - case MOXA_LOAD_BIOS: - i = moxaloadbios(dltmp.cardno, dltmp.buf, dltmp.len); - return (i); - case MOXA_FIND_BOARD: - return moxafindcard(dltmp.cardno); - case MOXA_LOAD_C320B: - moxaload320b(dltmp.cardno, dltmp.buf, dltmp.len); - default: /* to keep gcc happy */ - return (0); - case MOXA_LOAD_CODE: - i = moxaloadcode(dltmp.cardno, dltmp.buf, dltmp.len); - if (i == -1) - return (-EFAULT); - return (i); - - } -} - -int MoxaDriverPoll(void) -{ - struct moxa_board_conf *brd; - register ushort temp; - register int card; - void __iomem *ofsAddr; - void __iomem *ip; - int port, p, ports; - - if (moxaCard == 0) - return (-1); - for (card = 0; card < MAX_BOARDS; card++) { - brd = &moxa_boards[card]; - if (brd->loadstat == 0) - continue; - if ((ports = brd->numPorts) == 0) - continue; - if (readb(brd->intPend) == 0xff) { - ip = brd->intTable + readb(brd->intNdx); - p = card * MAX_PORTS_PER_BOARD; - ports <<= 1; - for (port = 0; port < ports; port += 2, p++) { - if ((temp = readw(ip + port)) != 0) { - writew(0, ip + port); - ofsAddr = moxa_ports[p].tableAddr; - if (temp & IntrTx) - writew(readw(ofsAddr + HostStat) & ~WakeupTx, ofsAddr + HostStat); - if (temp & IntrBreak) { - moxa_ports[p].breakCnt++; - } - if (temp & IntrLine) { - if (readb(ofsAddr + FlagStat) & DCD_state) { - if ((moxa_ports[p].DCDState & DCD_oldstate) == 0) - moxa_ports[p].DCDState = (DCD_oldstate | - DCD_changed); - } else { - if (moxa_ports[p].DCDState & DCD_oldstate) - moxa_ports[p].DCDState = DCD_changed; - } - } - } - } - writeb(0, brd->intPend); - } - if (moxaLowWaterChk) { - p = card * MAX_PORTS_PER_BOARD; - for (port = 0; port < ports; port++, p++) { - if (moxa_ports[p].lowChkFlag) { - moxa_ports[p].lowChkFlag = 0; - ofsAddr = moxa_ports[p].tableAddr; - moxa_low_water_check(ofsAddr); - } - } - } - } - moxaLowWaterChk = 0; - return (0); -} - -/***************************************************************************** - * Card level function: * - * 1. MoxaPortsOfCard(int cardno); * - *****************************************************************************/ -int MoxaPortsOfCard(int cardno) -{ - - if (moxa_boards[cardno].boardType == 0) - return (0); - return (moxa_boards[cardno].numPorts); -} - -/***************************************************************************** - * Port level functions: * - * 1. MoxaPortIsValid(int port); * - * 2. MoxaPortEnable(int port); * - * 3. MoxaPortDisable(int port); * - * 4. MoxaPortGetMaxBaud(int port); * - * 6. MoxaPortSetBaud(int port, long baud); * - * 8. MoxaPortSetTermio(int port, unsigned char *termio); * - * 9. MoxaPortGetLineOut(int port, int *dtrState, int *rtsState); * - * 10. MoxaPortLineCtrl(int port, int dtrState, int rtsState); * - * 11. MoxaPortFlowCtrl(int port, int rts, int cts, int rx, int tx,int xany); * - * 12. MoxaPortLineStatus(int port); * - * 13. MoxaPortDCDChange(int port); * - * 14. MoxaPortDCDON(int port); * - * 15. MoxaPortFlushData(int port, int mode); * - * 16. MoxaPortWriteData(int port, unsigned char * buffer, int length); * - * 17. MoxaPortReadData(int port, struct tty_struct *tty); * - * 20. MoxaPortTxQueue(int port); * - * 21. MoxaPortTxFree(int port); * - * 22. MoxaPortRxQueue(int port); * - * 24. MoxaPortTxDisable(int port); * - * 25. MoxaPortTxEnable(int port); * - * 27. MoxaPortResetBrkCnt(int port); * - * 30. MoxaPortSendBreak(int port, int ticks); * - *****************************************************************************/ /* * Moxa Port Number Description: * @@ -1733,33 +1637,6 @@ int MoxaPortsOfCard(int cardno) * -ENOIOCTLCMD * * - * Function 3: Moxa driver polling process routine. - * Syntax: - * int MoxaDriverPoll(void); - * - * return: 0 ; polling O.K. - * -1 : no any Moxa card. - * - * - * Function 4: Get the ports of this card. - * Syntax: - * int MoxaPortsOfCard(int cardno); - * - * int cardno : card number (0 - 3) - * - * return: 0 : this card is invalid - * 8/16/24/32 - * - * - * Function 5: Check this port is valid or invalid - * Syntax: - * int MoxaPortIsValid(int port); - * int port : port number (0 - 127, ref port description) - * - * return: 0 : this port is invalid - * 1 : this port is valid - * - * * Function 6: Enable this port to start Tx/Rx data. * Syntax: * void MoxaPortEnable(int port); @@ -1772,18 +1649,9 @@ int MoxaPortsOfCard(int cardno) * int port : port number (0 - 127) * * - * Function 8: Get the maximun available baud rate of this port. - * Syntax: - * long MoxaPortGetMaxBaud(int port); - * int port : port number (0 - 127) - * - * return: 0 : this port is invalid - * 38400/57600/115200 bps - * - * * Function 10: Setting baud rate of this port. * Syntax: - * long MoxaPortSetBaud(int port, long baud); + * speed_t MoxaPortSetBaud(int port, speed_t baud); * int port : port number (0 - 127) * long baud : baud rate (50 - 115200) * @@ -1850,25 +1718,6 @@ int MoxaPortsOfCard(int cardno) * Bit 2 - DCD state (0: off, 1: on) * * - * Function 17: Check the DCD state has changed since the last read - * of this function. - * Syntax: - * int MoxaPortDCDChange(int port); - * int port : port number (0 - 127) - * - * return: 0 : no changed - * 1 : DCD has changed - * - * - * Function 18: Check ths current DCD state is ON or not. - * Syntax: - * int MoxaPortDCDON(int port); - * int port : port number (0 - 127) - * - * return: 0 : DCD off - * 1 : DCD on - * - * * Function 19: Flush the Rx/Tx buffer data of this port. * Syntax: * void MoxaPortFlushData(int port, int mode); @@ -1942,40 +1791,20 @@ int MoxaPortsOfCard(int cardno) * return: 0 - .. : BREAK signal count * * - * Function 34: Send out a BREAK signal. - * Syntax: - * void MoxaPortSendBreak(int port, int ms100); - * int port : port number (0 - 127) - * int ms100 : break signal time interval. - * unit: 100 mini-second. if ms100 == 0, it will - * send out a about 250 ms BREAK signal. - * */ -int MoxaPortIsValid(int port) -{ - - if (moxaCard == 0) - return (0); - if (moxa_ports[port].chkPort == 0) - return (0); - return (1); -} -void MoxaPortEnable(int port) +static void MoxaPortEnable(struct moxa_port *port) { void __iomem *ofsAddr; - int MoxaPortLineStatus(int); - short lowwater = 512; + u16 lowwater = 512; - ofsAddr = moxa_ports[port].tableAddr; + ofsAddr = port->tableAddr; writew(lowwater, ofsAddr + Low_water); - moxa_ports[port].breakCnt = 0; - if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || - (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) { + if (MOXA_IS_320(port->board)) moxafunc(ofsAddr, FC_SetBreakIrq, 0); - } else { - writew(readw(ofsAddr + HostStat) | WakeupBreak, ofsAddr + HostStat); - } + else + writew(readw(ofsAddr + HostStat) | WakeupBreak, + ofsAddr + HostStat); moxafunc(ofsAddr, FC_SetLineIrq, Magic_code); moxafunc(ofsAddr, FC_FlushQueue, 2); @@ -1984,9 +1813,9 @@ void MoxaPortEnable(int port) MoxaPortLineStatus(port); } -void MoxaPortDisable(int port) +static void MoxaPortDisable(struct moxa_port *port) { - void __iomem *ofsAddr = moxa_ports[port].tableAddr; + void __iomem *ofsAddr = port->tableAddr; moxafunc(ofsAddr, FC_SetFlowCtl, 0); /* disable flow control */ moxafunc(ofsAddr, FC_ClrLineIrq, Magic_code); @@ -1994,49 +1823,32 @@ void MoxaPortDisable(int port) moxafunc(ofsAddr, FC_DisableCH, Magic_code); } -long MoxaPortGetMaxBaud(int port) -{ - if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || - (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) - return (460800L); - else - return (921600L); -} - - -long MoxaPortSetBaud(int port, long baud) +static speed_t MoxaPortSetBaud(struct moxa_port *port, speed_t baud) { - void __iomem *ofsAddr; - long max, clock; - unsigned int val; + void __iomem *ofsAddr = port->tableAddr; + unsigned int clock, val; + speed_t max; - if ((baud < 50L) || ((max = MoxaPortGetMaxBaud(port)) == 0)) - return (0); - ofsAddr = moxa_ports[port].tableAddr; + max = MOXA_IS_320(port->board) ? 460800 : 921600; + if (baud < 50) + return 0; if (baud > max) baud = max; - if (max == 38400L) - clock = 614400L; /* for 9.8304 Mhz : max. 38400 bps */ - else if (max == 57600L) - clock = 691200L; /* for 11.0592 Mhz : max. 57600 bps */ - else - clock = 921600L; /* for 14.7456 Mhz : max. 115200 bps */ + clock = 921600; val = clock / baud; moxafunc(ofsAddr, FC_SetBaud, val); baud = clock / val; - moxa_ports[port].curBaud = baud; - return (baud); + return baud; } -int MoxaPortSetTermio(int port, struct ktermios *termio, speed_t baud) +static int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio, + speed_t baud) { void __iomem *ofsAddr; tcflag_t cflag; tcflag_t mode = 0; - if (moxa_ports[port].chkPort == 0 || termio == 0) - return (-1); - ofsAddr = moxa_ports[port].tableAddr; + ofsAddr = port->tableAddr; cflag = termio->c_cflag; /* termio->c_cflag */ mode = termio->c_cflag & CSIZE; @@ -2065,13 +1877,11 @@ int MoxaPortSetTermio(int port, struct ktermios *termio, speed_t baud) } else mode |= MX_PARNONE; - moxafunc(ofsAddr, FC_SetDataMode, (ushort) mode); + moxafunc(ofsAddr, FC_SetDataMode, (u16)mode); + + if (MOXA_IS_320(port->board) && baud >= 921600) + return -1; - if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || - (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) { - if (baud >= 921600L) - return (-1); - } baud = MoxaPortSetBaud(port, baud); if (termio->c_iflag & (IXON | IXOFF | IXANY)) { @@ -2081,51 +1891,37 @@ int MoxaPortSetTermio(int port, struct ktermios *termio, speed_t baud) moxa_wait_finish(ofsAddr); } - return (baud); + return baud; } -int MoxaPortGetLineOut(int port, int *dtrState, int *rtsState) +static int MoxaPortGetLineOut(struct moxa_port *port, int *dtrState, + int *rtsState) { + if (dtrState) + *dtrState = !!(port->lineCtrl & DTR_ON); + if (rtsState) + *rtsState = !!(port->lineCtrl & RTS_ON); - if (!MoxaPortIsValid(port)) - return (-1); - if (dtrState) { - if (moxa_ports[port].lineCtrl & DTR_ON) - *dtrState = 1; - else - *dtrState = 0; - } - if (rtsState) { - if (moxa_ports[port].lineCtrl & RTS_ON) - *rtsState = 1; - else - *rtsState = 0; - } - return (0); + return 0; } -void MoxaPortLineCtrl(int port, int dtr, int rts) +static void MoxaPortLineCtrl(struct moxa_port *port, int dtr, int rts) { - void __iomem *ofsAddr; - int mode; + u8 mode = 0; - ofsAddr = moxa_ports[port].tableAddr; - mode = 0; if (dtr) mode |= DTR_ON; if (rts) mode |= RTS_ON; - moxa_ports[port].lineCtrl = mode; - moxafunc(ofsAddr, FC_LineControl, mode); + port->lineCtrl = mode; + moxafunc(port->tableAddr, FC_LineControl, mode); } -void MoxaPortFlowCtrl(int port, int rts, int cts, int txflow, int rxflow, int txany) +static void MoxaPortFlowCtrl(struct moxa_port *port, int rts, int cts, + int txflow, int rxflow, int txany) { - void __iomem *ofsAddr; - int mode; + int mode = 0; - ofsAddr = moxa_ports[port].tableAddr; - mode = 0; if (rts) mode |= RTS_FlowCtl; if (cts) @@ -2136,81 +1932,50 @@ void MoxaPortFlowCtrl(int port, int rts, int cts, int txflow, int rxflow, int tx mode |= Rx_FlowCtl; if (txany) mode |= IXM_IXANY; - moxafunc(ofsAddr, FC_SetFlowCtl, mode); + moxafunc(port->tableAddr, FC_SetFlowCtl, mode); } -int MoxaPortLineStatus(int port) +static int MoxaPortLineStatus(struct moxa_port *port) { void __iomem *ofsAddr; int val; - ofsAddr = moxa_ports[port].tableAddr; - if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || - (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) { + ofsAddr = port->tableAddr; + if (MOXA_IS_320(port->board)) { moxafunc(ofsAddr, FC_LineStatus, 0); val = readw(ofsAddr + FuncArg); } else { val = readw(ofsAddr + FlagStat) >> 4; } val &= 0x0B; - if (val & 8) { + if (val & 8) val |= 4; - if ((moxa_ports[port].DCDState & DCD_oldstate) == 0) - moxa_ports[port].DCDState = (DCD_oldstate | DCD_changed); - } else { - if (moxa_ports[port].DCDState & DCD_oldstate) - moxa_ports[port].DCDState = DCD_changed; - } + spin_lock_bh(&moxa_lock); + moxa_new_dcdstate(port, val & 8); + spin_unlock_bh(&moxa_lock); val &= 7; - return (val); -} - -int MoxaPortDCDChange(int port) -{ - int n; - - if (moxa_ports[port].chkPort == 0) - return (0); - n = moxa_ports[port].DCDState; - moxa_ports[port].DCDState &= ~DCD_changed; - n &= DCD_changed; - return (n); -} - -int MoxaPortDCDON(int port) -{ - int n; - - if (moxa_ports[port].chkPort == 0) - return (0); - if (moxa_ports[port].DCDState & DCD_oldstate) - n = 1; - else - n = 0; - return (n); + return val; } -int MoxaPortWriteData(int port, unsigned char * buffer, int len) +static int MoxaPortWriteData(struct moxa_port *port, + const unsigned char *buffer, int len) { - int c, total, i; - ushort tail; - int cnt; - ushort head, tx_mask, spage, epage; - ushort pageno, pageofs, bufhead; void __iomem *baseAddr, *ofsAddr, *ofs; + unsigned int c, total; + u16 head, tail, tx_mask, spage, epage; + u16 pageno, pageofs, bufhead; - ofsAddr = moxa_ports[port].tableAddr; - baseAddr = moxa_boards[port / MAX_PORTS_PER_BOARD].basemem; + ofsAddr = port->tableAddr; + baseAddr = port->board->basemem; tx_mask = readw(ofsAddr + TX_mask); spage = readw(ofsAddr + Page_txb); epage = readw(ofsAddr + EndPage_txb); tail = readw(ofsAddr + TXwptr); head = readw(ofsAddr + TXrptr); - c = (head > tail) ? (head - tail - 1) - : (head - tail + tx_mask); + c = (head > tail) ? (head - tail - 1) : (head - tail + tx_mask); if (c > len) c = len; - moxaLog.txcnt[port] += c; + moxaLog.txcnt[port->tty->index] += c; total = c; if (spage == epage) { bufhead = readw(ofsAddr + Ofs_txb); @@ -2222,249 +1987,179 @@ int MoxaPortWriteData(int port, unsigned char * buffer, int len) len = tx_mask + 1 - tail; len = (c > len) ? len : c; ofs = baseAddr + DynPage_addr + bufhead + tail; - for (i = 0; i < len; i++) - writeb(*buffer++, ofs + i); + memcpy_toio(ofs, buffer, len); + buffer += len; tail = (tail + len) & tx_mask; c -= len; } - writew(tail, ofsAddr + TXwptr); } else { - len = c; pageno = spage + (tail >> 13); pageofs = tail & Page_mask; - do { - cnt = Page_size - pageofs; - if (cnt > c) - cnt = c; - c -= cnt; + while (c > 0) { + len = Page_size - pageofs; + if (len > c) + len = c; writeb(pageno, baseAddr + Control_reg); ofs = baseAddr + DynPage_addr + pageofs; - for (i = 0; i < cnt; i++) - writeb(*buffer++, ofs + i); - if (c == 0) { - writew((tail + len) & tx_mask, ofsAddr + TXwptr); - break; - } + memcpy_toio(ofs, buffer, len); + buffer += len; if (++pageno == epage) pageno = spage; pageofs = 0; - } while (1); + c -= len; + } + tail = (tail + total) & tx_mask; } + writew(tail, ofsAddr + TXwptr); writeb(1, ofsAddr + CD180TXirq); /* start to send */ - return (total); + return total; } -int MoxaPortReadData(int port, struct tty_struct *tty) +static int MoxaPortReadData(struct moxa_port *port) { - register ushort head, pageofs; - int i, count, cnt, len, total, remain; - ushort tail, rx_mask, spage, epage; - ushort pageno, bufhead; + struct tty_struct *tty = port->tty; + unsigned char *dst; void __iomem *baseAddr, *ofsAddr, *ofs; + unsigned int count, len, total; + u16 tail, rx_mask, spage, epage; + u16 pageno, pageofs, bufhead, head; - ofsAddr = moxa_ports[port].tableAddr; - baseAddr = moxa_boards[port / MAX_PORTS_PER_BOARD].basemem; + ofsAddr = port->tableAddr; + baseAddr = port->board->basemem; head = readw(ofsAddr + RXrptr); tail = readw(ofsAddr + RXwptr); rx_mask = readw(ofsAddr + RX_mask); spage = readw(ofsAddr + Page_rxb); epage = readw(ofsAddr + EndPage_rxb); - count = (tail >= head) ? (tail - head) - : (tail - head + rx_mask + 1); + count = (tail >= head) ? (tail - head) : (tail - head + rx_mask + 1); if (count == 0) return 0; total = count; - remain = count - total; - moxaLog.rxcnt[port] += total; - count = total; + moxaLog.rxcnt[tty->index] += total; if (spage == epage) { bufhead = readw(ofsAddr + Ofs_rxb); writew(spage, baseAddr + Control_reg); while (count > 0) { - if (tail >= head) - len = tail - head; - else - len = rx_mask + 1 - head; - len = (count > len) ? len : count; ofs = baseAddr + DynPage_addr + bufhead + head; - for (i = 0; i < len; i++) - tty_insert_flip_char(tty, readb(ofs + i), TTY_NORMAL); + len = (tail >= head) ? (tail - head) : + (rx_mask + 1 - head); + len = tty_prepare_flip_string(tty, &dst, + min(len, count)); + memcpy_fromio(dst, ofs, len); head = (head + len) & rx_mask; count -= len; } - writew(head, ofsAddr + RXrptr); } else { - len = count; pageno = spage + (head >> 13); pageofs = head & Page_mask; - do { - cnt = Page_size - pageofs; - if (cnt > count) - cnt = count; - count -= cnt; + while (count > 0) { writew(pageno, baseAddr + Control_reg); ofs = baseAddr + DynPage_addr + pageofs; - for (i = 0; i < cnt; i++) - tty_insert_flip_char(tty, readb(ofs + i), TTY_NORMAL); - if (count == 0) { - writew((head + len) & rx_mask, ofsAddr + RXrptr); - break; - } - if (++pageno == epage) + len = tty_prepare_flip_string(tty, &dst, + min(Page_size - pageofs, count)); + memcpy_fromio(dst, ofs, len); + + count -= len; + pageofs = (pageofs + len) & Page_mask; + if (pageofs == 0 && ++pageno == epage) pageno = spage; - pageofs = 0; - } while (1); + } + head = (head + total) & rx_mask; } - if ((readb(ofsAddr + FlagStat) & Xoff_state) && (remain < LowWater)) { + writew(head, ofsAddr + RXrptr); + if (readb(ofsAddr + FlagStat) & Xoff_state) { moxaLowWaterChk = 1; - moxa_ports[port].lowChkFlag = 1; + port->lowChkFlag = 1; } - return (total); + return total; } -int MoxaPortTxQueue(int port) +static int MoxaPortTxQueue(struct moxa_port *port) { - void __iomem *ofsAddr; - ushort rptr, wptr, mask; - int len; + void __iomem *ofsAddr = port->tableAddr; + u16 rptr, wptr, mask; - ofsAddr = moxa_ports[port].tableAddr; rptr = readw(ofsAddr + TXrptr); wptr = readw(ofsAddr + TXwptr); mask = readw(ofsAddr + TX_mask); - len = (wptr - rptr) & mask; - return (len); + return (wptr - rptr) & mask; } -int MoxaPortTxFree(int port) +static int MoxaPortTxFree(struct moxa_port *port) { - void __iomem *ofsAddr; - ushort rptr, wptr, mask; - int len; + void __iomem *ofsAddr = port->tableAddr; + u16 rptr, wptr, mask; - ofsAddr = moxa_ports[port].tableAddr; rptr = readw(ofsAddr + TXrptr); wptr = readw(ofsAddr + TXwptr); mask = readw(ofsAddr + TX_mask); - len = mask - ((wptr - rptr) & mask); - return (len); + return mask - ((wptr - rptr) & mask); } -int MoxaPortRxQueue(int port) +static int MoxaPortRxQueue(struct moxa_port *port) { - void __iomem *ofsAddr; - ushort rptr, wptr, mask; - int len; + void __iomem *ofsAddr = port->tableAddr; + u16 rptr, wptr, mask; - ofsAddr = moxa_ports[port].tableAddr; rptr = readw(ofsAddr + RXrptr); wptr = readw(ofsAddr + RXwptr); mask = readw(ofsAddr + RX_mask); - len = (wptr - rptr) & mask; - return (len); + return (wptr - rptr) & mask; } - -void MoxaPortTxDisable(int port) +static void MoxaPortTxDisable(struct moxa_port *port) { - void __iomem *ofsAddr; - - ofsAddr = moxa_ports[port].tableAddr; - moxafunc(ofsAddr, FC_SetXoffState, Magic_code); + moxafunc(port->tableAddr, FC_SetXoffState, Magic_code); } -void MoxaPortTxEnable(int port) +static void MoxaPortTxEnable(struct moxa_port *port) { - void __iomem *ofsAddr; - - ofsAddr = moxa_ports[port].tableAddr; - moxafunc(ofsAddr, FC_SetXonState, Magic_code); -} - - -int MoxaPortResetBrkCnt(int port) -{ - ushort cnt; - cnt = moxa_ports[port].breakCnt; - moxa_ports[port].breakCnt = 0; - return (cnt); -} - - -void MoxaPortSendBreak(int port, int ms100) -{ - void __iomem *ofsAddr; - - ofsAddr = moxa_ports[port].tableAddr; - if (ms100) { - moxafunc(ofsAddr, FC_SendBreak, Magic_code); - msleep(ms100 * 10); - } else { - moxafunc(ofsAddr, FC_SendBreak, Magic_code); - msleep(250); - } - moxafunc(ofsAddr, FC_StopBreak, Magic_code); + moxafunc(port->tableAddr, FC_SetXonState, Magic_code); } static int moxa_get_serial_info(struct moxa_port *info, - struct serial_struct __user *retinfo) + struct serial_struct __user *retinfo) { - struct serial_struct tmp; - - memset(&tmp, 0, sizeof(tmp)); - tmp.type = info->type; - tmp.line = info->port; - tmp.port = 0; - tmp.irq = 0; - tmp.flags = info->asyncflags; - tmp.baud_base = 921600; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; - tmp.custom_divisor = 0; - tmp.hub6 = 0; - if(copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - return (0); + struct serial_struct tmp = { + .type = info->type, + .line = info->tty->index, + .flags = info->asyncflags, + .baud_base = 921600, + .close_delay = info->close_delay + }; + return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; } static int moxa_set_serial_info(struct moxa_port *info, - struct serial_struct __user *new_info) + struct serial_struct __user *new_info) { struct serial_struct new_serial; - if(copy_from_user(&new_serial, new_info, sizeof(new_serial))) + if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) return -EFAULT; - if ((new_serial.irq != 0) || - (new_serial.port != 0) || -// (new_serial.type != info->type) || - (new_serial.custom_divisor != 0) || - (new_serial.baud_base != 921600)) - return (-EPERM); + if (new_serial.irq != 0 || new_serial.port != 0 || + new_serial.custom_divisor != 0 || + new_serial.baud_base != 921600) + return -EPERM; if (!capable(CAP_SYS_ADMIN)) { if (((new_serial.flags & ~ASYNC_USR_MASK) != (info->asyncflags & ~ASYNC_USR_MASK))) - return (-EPERM); - } else { + return -EPERM; + } else info->close_delay = new_serial.close_delay * HZ / 100; - info->closing_wait = new_serial.closing_wait * HZ / 100; - } new_serial.flags = (new_serial.flags & ~ASYNC_FLAGS); new_serial.flags |= (info->asyncflags & ASYNC_FLAGS); - if (new_serial.type == PORT_16550A) { - MoxaSetFifo(info->port, 1); - } else { - MoxaSetFifo(info->port, 0); - } + MoxaSetFifo(info, new_serial.type == PORT_16550A); info->type = new_serial.type; - return (0); + return 0; } @@ -2472,374 +2167,10 @@ static int moxa_set_serial_info(struct moxa_port *info, /***************************************************************************** * Static local functions: * *****************************************************************************/ -static void moxafunc(void __iomem *ofsAddr, int cmd, ushort arg) -{ - - writew(arg, ofsAddr + FuncArg); - writew(cmd, ofsAddr + FuncCode); - moxa_wait_finish(ofsAddr); -} - -static void moxa_wait_finish(void __iomem *ofsAddr) -{ - unsigned long i, j; - - i = jiffies; - while (readw(ofsAddr + FuncCode) != 0) { - j = jiffies; - if ((j - i) > moxaFuncTout) { - return; - } - } -} - -static void moxa_low_water_check(void __iomem *ofsAddr) -{ - int len; - ushort rptr, wptr, mask; - - if (readb(ofsAddr + FlagStat) & Xoff_state) { - rptr = readw(ofsAddr + RXrptr); - wptr = readw(ofsAddr + RXwptr); - mask = readw(ofsAddr + RX_mask); - len = (wptr - rptr) & mask; - if (len <= Low_water) - moxafunc(ofsAddr, FC_SendXon, 0); - } -} - -static int moxaloadbios(int cardno, unsigned char __user *tmp, int len) -{ - void __iomem *baseAddr; - int i; - - if(len < 0 || len > sizeof(moxaBuff)) - return -EINVAL; - if(copy_from_user(moxaBuff, tmp, len)) - return -EFAULT; - baseAddr = moxa_boards[cardno].basemem; - writeb(HW_reset, baseAddr + Control_reg); /* reset */ - msleep(10); - for (i = 0; i < 4096; i++) - writeb(0, baseAddr + i); /* clear fix page */ - for (i = 0; i < len; i++) - writeb(moxaBuff[i], baseAddr + i); /* download BIOS */ - writeb(0, baseAddr + Control_reg); /* restart */ - return (0); -} - -static int moxafindcard(int cardno) -{ - void __iomem *baseAddr; - ushort tmp; - - baseAddr = moxa_boards[cardno].basemem; - switch (moxa_boards[cardno].boardType) { - case MOXA_BOARD_C218_ISA: - case MOXA_BOARD_C218_PCI: - if ((tmp = readw(baseAddr + C218_key)) != C218_KeyCode) { - return (-1); - } - break; - case MOXA_BOARD_CP204J: - if ((tmp = readw(baseAddr + C218_key)) != CP204J_KeyCode) { - return (-1); - } - break; - default: - if ((tmp = readw(baseAddr + C320_key)) != C320_KeyCode) { - return (-1); - } - if ((tmp = readw(baseAddr + C320_status)) != STS_init) { - return (-2); - } - } - return (0); -} - -static int moxaload320b(int cardno, unsigned char __user *tmp, int len) -{ - void __iomem *baseAddr; - int i; - - if(len < 0 || len > sizeof(moxaBuff)) - return -EINVAL; - if(copy_from_user(moxaBuff, tmp, len)) - return -EFAULT; - baseAddr = moxa_boards[cardno].basemem; - writew(len - 7168 - 2, baseAddr + C320bapi_len); - writeb(1, baseAddr + Control_reg); /* Select Page 1 */ - for (i = 0; i < 7168; i++) - writeb(moxaBuff[i], baseAddr + DynPage_addr + i); - writeb(2, baseAddr + Control_reg); /* Select Page 2 */ - for (i = 0; i < (len - 7168); i++) - writeb(moxaBuff[i + 7168], baseAddr + DynPage_addr + i); - return (0); -} - -static int moxaloadcode(int cardno, unsigned char __user *tmp, int len) -{ - void __iomem *baseAddr, *ofsAddr; - int retval, port, i; - - if(len < 0 || len > sizeof(moxaBuff)) - return -EINVAL; - if(copy_from_user(moxaBuff, tmp, len)) - return -EFAULT; - baseAddr = moxa_boards[cardno].basemem; - switch (moxa_boards[cardno].boardType) { - case MOXA_BOARD_C218_ISA: - case MOXA_BOARD_C218_PCI: - case MOXA_BOARD_CP204J: - retval = moxaloadc218(cardno, baseAddr, len); - if (retval) - return (retval); - port = cardno * MAX_PORTS_PER_BOARD; - for (i = 0; i < moxa_boards[cardno].numPorts; i++, port++) { - struct moxa_port *p = &moxa_ports[port]; - - p->chkPort = 1; - p->curBaud = 9600L; - p->DCDState = 0; - p->tableAddr = baseAddr + Extern_table + Extern_size * i; - ofsAddr = p->tableAddr; - writew(C218rx_mask, ofsAddr + RX_mask); - writew(C218tx_mask, ofsAddr + TX_mask); - writew(C218rx_spage + i * C218buf_pageno, ofsAddr + Page_rxb); - writew(readw(ofsAddr + Page_rxb) + C218rx_pageno, ofsAddr + EndPage_rxb); - - writew(C218tx_spage + i * C218buf_pageno, ofsAddr + Page_txb); - writew(readw(ofsAddr + Page_txb) + C218tx_pageno, ofsAddr + EndPage_txb); - - } - break; - default: - retval = moxaloadc320(cardno, baseAddr, len, - &moxa_boards[cardno].numPorts); - if (retval) - return (retval); - port = cardno * MAX_PORTS_PER_BOARD; - for (i = 0; i < moxa_boards[cardno].numPorts; i++, port++) { - struct moxa_port *p = &moxa_ports[port]; - - p->chkPort = 1; - p->curBaud = 9600L; - p->DCDState = 0; - p->tableAddr = baseAddr + Extern_table + Extern_size * i; - ofsAddr = p->tableAddr; - if (moxa_boards[cardno].numPorts == 8) { - writew(C320p8rx_mask, ofsAddr + RX_mask); - writew(C320p8tx_mask, ofsAddr + TX_mask); - writew(C320p8rx_spage + i * C320p8buf_pgno, ofsAddr + Page_rxb); - writew(readw(ofsAddr + Page_rxb) + C320p8rx_pgno, ofsAddr + EndPage_rxb); - writew(C320p8tx_spage + i * C320p8buf_pgno, ofsAddr + Page_txb); - writew(readw(ofsAddr + Page_txb) + C320p8tx_pgno, ofsAddr + EndPage_txb); - - } else if (moxa_boards[cardno].numPorts == 16) { - writew(C320p16rx_mask, ofsAddr + RX_mask); - writew(C320p16tx_mask, ofsAddr + TX_mask); - writew(C320p16rx_spage + i * C320p16buf_pgno, ofsAddr + Page_rxb); - writew(readw(ofsAddr + Page_rxb) + C320p16rx_pgno, ofsAddr + EndPage_rxb); - writew(C320p16tx_spage + i * C320p16buf_pgno, ofsAddr + Page_txb); - writew(readw(ofsAddr + Page_txb) + C320p16tx_pgno, ofsAddr + EndPage_txb); - - } else if (moxa_boards[cardno].numPorts == 24) { - writew(C320p24rx_mask, ofsAddr + RX_mask); - writew(C320p24tx_mask, ofsAddr + TX_mask); - writew(C320p24rx_spage + i * C320p24buf_pgno, ofsAddr + Page_rxb); - writew(readw(ofsAddr + Page_rxb) + C320p24rx_pgno, ofsAddr + EndPage_rxb); - writew(C320p24tx_spage + i * C320p24buf_pgno, ofsAddr + Page_txb); - writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb); - } else if (moxa_boards[cardno].numPorts == 32) { - writew(C320p32rx_mask, ofsAddr + RX_mask); - writew(C320p32tx_mask, ofsAddr + TX_mask); - writew(C320p32tx_ofs, ofsAddr + Ofs_txb); - writew(C320p32rx_spage + i * C320p32buf_pgno, ofsAddr + Page_rxb); - writew(readb(ofsAddr + Page_rxb), ofsAddr + EndPage_rxb); - writew(C320p32tx_spage + i * C320p32buf_pgno, ofsAddr + Page_txb); - writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb); - } - } - break; - } - moxa_boards[cardno].loadstat = 1; - return (0); -} - -static int moxaloadc218(int cardno, void __iomem *baseAddr, int len) -{ - char retry; - int i, j, len1, len2; - ushort usum, *ptr, keycode; - - if (moxa_boards[cardno].boardType == MOXA_BOARD_CP204J) - keycode = CP204J_KeyCode; - else - keycode = C218_KeyCode; - usum = 0; - len1 = len >> 1; - ptr = (ushort *) moxaBuff; - for (i = 0; i < len1; i++) - usum += le16_to_cpu(*(ptr + i)); - retry = 0; - do { - len1 = len >> 1; - j = 0; - while (len1) { - len2 = (len1 > 2048) ? 2048 : len1; - len1 -= len2; - for (i = 0; i < len2 << 1; i++) - writeb(moxaBuff[i + j], baseAddr + C218_LoadBuf + i); - j += i; - - writew(len2, baseAddr + C218DLoad_len); - writew(0, baseAddr + C218_key); - for (i = 0; i < 100; i++) { - if (readw(baseAddr + C218_key) == keycode) - break; - msleep(10); - } - if (readw(baseAddr + C218_key) != keycode) { - return (-1); - } - } - writew(0, baseAddr + C218DLoad_len); - writew(usum, baseAddr + C218check_sum); - writew(0, baseAddr + C218_key); - for (i = 0; i < 100; i++) { - if (readw(baseAddr + C218_key) == keycode) - break; - msleep(10); - } - retry++; - } while ((readb(baseAddr + C218chksum_ok) != 1) && (retry < 3)); - if (readb(baseAddr + C218chksum_ok) != 1) { - return (-1); - } - writew(0, baseAddr + C218_key); - for (i = 0; i < 100; i++) { - if (readw(baseAddr + Magic_no) == Magic_code) - break; - msleep(10); - } - if (readw(baseAddr + Magic_no) != Magic_code) { - return (-1); - } - writew(1, baseAddr + Disable_IRQ); - writew(0, baseAddr + Magic_no); - for (i = 0; i < 100; i++) { - if (readw(baseAddr + Magic_no) == Magic_code) - break; - msleep(10); - } - if (readw(baseAddr + Magic_no) != Magic_code) { - return (-1); - } - moxaCard = 1; - moxa_boards[cardno].intNdx = baseAddr + IRQindex; - moxa_boards[cardno].intPend = baseAddr + IRQpending; - moxa_boards[cardno].intTable = baseAddr + IRQtable; - return (0); -} - -static int moxaloadc320(int cardno, void __iomem *baseAddr, int len, int *numPorts) -{ - ushort usum; - int i, j, wlen, len2, retry; - ushort *uptr; - - usum = 0; - wlen = len >> 1; - uptr = (ushort *) moxaBuff; - for (i = 0; i < wlen; i++) - usum += le16_to_cpu(uptr[i]); - retry = 0; - j = 0; - do { - while (wlen) { - if (wlen > 2048) - len2 = 2048; - else - len2 = wlen; - wlen -= len2; - len2 <<= 1; - for (i = 0; i < len2; i++) - writeb(moxaBuff[j + i], baseAddr + C320_LoadBuf + i); - len2 >>= 1; - j += i; - writew(len2, baseAddr + C320DLoad_len); - writew(0, baseAddr + C320_key); - for (i = 0; i < 10; i++) { - if (readw(baseAddr + C320_key) == C320_KeyCode) - break; - msleep(10); - } - if (readw(baseAddr + C320_key) != C320_KeyCode) - return (-1); - } - writew(0, baseAddr + C320DLoad_len); - writew(usum, baseAddr + C320check_sum); - writew(0, baseAddr + C320_key); - for (i = 0; i < 10; i++) { - if (readw(baseAddr + C320_key) == C320_KeyCode) - break; - msleep(10); - } - retry++; - } while ((readb(baseAddr + C320chksum_ok) != 1) && (retry < 3)); - if (readb(baseAddr + C320chksum_ok) != 1) - return (-1); - writew(0, baseAddr + C320_key); - for (i = 0; i < 600; i++) { - if (readw(baseAddr + Magic_no) == Magic_code) - break; - msleep(10); - } - if (readw(baseAddr + Magic_no) != Magic_code) - return (-100); - - if (moxa_boards[cardno].busType == MOXA_BUS_TYPE_PCI) { /* ASIC board */ - writew(0x3800, baseAddr + TMS320_PORT1); - writew(0x3900, baseAddr + TMS320_PORT2); - writew(28499, baseAddr + TMS320_CLOCK); - } else { - writew(0x3200, baseAddr + TMS320_PORT1); - writew(0x3400, baseAddr + TMS320_PORT2); - writew(19999, baseAddr + TMS320_CLOCK); - } - writew(1, baseAddr + Disable_IRQ); - writew(0, baseAddr + Magic_no); - for (i = 0; i < 500; i++) { - if (readw(baseAddr + Magic_no) == Magic_code) - break; - msleep(10); - } - if (readw(baseAddr + Magic_no) != Magic_code) - return (-102); - - j = readw(baseAddr + Module_cnt); - if (j <= 0) - return (-101); - *numPorts = j * 8; - writew(j, baseAddr + Module_no); - writew(0, baseAddr + Magic_no); - for (i = 0; i < 600; i++) { - if (readw(baseAddr + Magic_no) == Magic_code) - break; - msleep(10); - } - if (readw(baseAddr + Magic_no) != Magic_code) - return (-102); - moxaCard = 1; - moxa_boards[cardno].intNdx = baseAddr + IRQindex; - moxa_boards[cardno].intPend = baseAddr + IRQpending; - moxa_boards[cardno].intTable = baseAddr + IRQtable; - return (0); -} -static void MoxaSetFifo(int port, int enable) +static void MoxaSetFifo(struct moxa_port *port, int enable) { - void __iomem *ofsAddr = moxa_ports[port].tableAddr; + void __iomem *ofsAddr = port->tableAddr; if (!enable) { moxafunc(ofsAddr, FC_SetRxFIFOTrig, 0); diff --git a/drivers/char/moxa.h b/drivers/char/moxa.h new file mode 100644 index 00000000000..87d16ce57be --- /dev/null +++ b/drivers/char/moxa.h @@ -0,0 +1,304 @@ +#ifndef MOXA_H_FILE +#define MOXA_H_FILE + +#define MOXA 0x400 +#define MOXA_GET_IQUEUE (MOXA + 1) /* get input buffered count */ +#define MOXA_GET_OQUEUE (MOXA + 2) /* get output buffered count */ +#define MOXA_GETDATACOUNT (MOXA + 23) +#define MOXA_GET_IOQUEUE (MOXA + 27) +#define MOXA_FLUSH_QUEUE (MOXA + 28) +#define MOXA_GETMSTATUS (MOXA + 65) + +/* + * System Configuration + */ + +#define Magic_code 0x404 + +/* + * for C218 BIOS initialization + */ +#define C218_ConfBase 0x800 +#define C218_status (C218_ConfBase + 0) /* BIOS running status */ +#define C218_diag (C218_ConfBase + 2) /* diagnostic status */ +#define C218_key (C218_ConfBase + 4) /* WORD (0x218 for C218) */ +#define C218DLoad_len (C218_ConfBase + 6) /* WORD */ +#define C218check_sum (C218_ConfBase + 8) /* BYTE */ +#define C218chksum_ok (C218_ConfBase + 0x0a) /* BYTE (1:ok) */ +#define C218_TestRx (C218_ConfBase + 0x10) /* 8 bytes for 8 ports */ +#define C218_TestTx (C218_ConfBase + 0x18) /* 8 bytes for 8 ports */ +#define C218_RXerr (C218_ConfBase + 0x20) /* 8 bytes for 8 ports */ +#define C218_ErrFlag (C218_ConfBase + 0x28) /* 8 bytes for 8 ports */ + +#define C218_LoadBuf 0x0F00 +#define C218_KeyCode 0x218 +#define CP204J_KeyCode 0x204 + +/* + * for C320 BIOS initialization + */ +#define C320_ConfBase 0x800 +#define C320_LoadBuf 0x0f00 +#define STS_init 0x05 /* for C320_status */ + +#define C320_status C320_ConfBase + 0 /* BIOS running status */ +#define C320_diag C320_ConfBase + 2 /* diagnostic status */ +#define C320_key C320_ConfBase + 4 /* WORD (0320H for C320) */ +#define C320DLoad_len C320_ConfBase + 6 /* WORD */ +#define C320check_sum C320_ConfBase + 8 /* WORD */ +#define C320chksum_ok C320_ConfBase + 0x0a /* WORD (1:ok) */ +#define C320bapi_len C320_ConfBase + 0x0c /* WORD */ +#define C320UART_no C320_ConfBase + 0x0e /* WORD */ + +#define C320_KeyCode 0x320 + +#define FixPage_addr 0x0000 /* starting addr of static page */ +#define DynPage_addr 0x2000 /* starting addr of dynamic page */ +#define C218_start 0x3000 /* starting addr of C218 BIOS prg */ +#define Control_reg 0x1ff0 /* select page and reset control */ +#define HW_reset 0x80 + +/* + * Function Codes + */ +#define FC_CardReset 0x80 +#define FC_ChannelReset 1 /* C320 firmware not supported */ +#define FC_EnableCH 2 +#define FC_DisableCH 3 +#define FC_SetParam 4 +#define FC_SetMode 5 +#define FC_SetRate 6 +#define FC_LineControl 7 +#define FC_LineStatus 8 +#define FC_XmitControl 9 +#define FC_FlushQueue 10 +#define FC_SendBreak 11 +#define FC_StopBreak 12 +#define FC_LoopbackON 13 +#define FC_LoopbackOFF 14 +#define FC_ClrIrqTable 15 +#define FC_SendXon 16 +#define FC_SetTermIrq 17 /* C320 firmware not supported */ +#define FC_SetCntIrq 18 /* C320 firmware not supported */ +#define FC_SetBreakIrq 19 +#define FC_SetLineIrq 20 +#define FC_SetFlowCtl 21 +#define FC_GenIrq 22 +#define FC_InCD180 23 +#define FC_OutCD180 24 +#define FC_InUARTreg 23 +#define FC_OutUARTreg 24 +#define FC_SetXonXoff 25 +#define FC_OutCD180CCR 26 +#define FC_ExtIQueue 27 +#define FC_ExtOQueue 28 +#define FC_ClrLineIrq 29 +#define FC_HWFlowCtl 30 +#define FC_GetClockRate 35 +#define FC_SetBaud 36 +#define FC_SetDataMode 41 +#define FC_GetCCSR 43 +#define FC_GetDataError 45 +#define FC_RxControl 50 +#define FC_ImmSend 51 +#define FC_SetXonState 52 +#define FC_SetXoffState 53 +#define FC_SetRxFIFOTrig 54 +#define FC_SetTxFIFOCnt 55 +#define FC_UnixRate 56 +#define FC_UnixResetTimer 57 + +#define RxFIFOTrig1 0 +#define RxFIFOTrig4 1 +#define RxFIFOTrig8 2 +#define RxFIFOTrig14 3 + +/* + * Dual-Ported RAM + */ +#define DRAM_global 0 +#define INT_data (DRAM_global + 0) +#define Config_base (DRAM_global + 0x108) + +#define IRQindex (INT_data + 0) +#define IRQpending (INT_data + 4) +#define IRQtable (INT_data + 8) + +/* + * Interrupt Status + */ +#define IntrRx 0x01 /* receiver data O.K. */ +#define IntrTx 0x02 /* transmit buffer empty */ +#define IntrFunc 0x04 /* function complete */ +#define IntrBreak 0x08 /* received break */ +#define IntrLine 0x10 /* line status change + for transmitter */ +#define IntrIntr 0x20 /* received INTR code */ +#define IntrQuit 0x40 /* received QUIT code */ +#define IntrEOF 0x80 /* received EOF code */ + +#define IntrRxTrigger 0x100 /* rx data count reach tigger value */ +#define IntrTxTrigger 0x200 /* tx data count below trigger value */ + +#define Magic_no (Config_base + 0) +#define Card_model_no (Config_base + 2) +#define Total_ports (Config_base + 4) +#define Module_cnt (Config_base + 8) +#define Module_no (Config_base + 10) +#define Timer_10ms (Config_base + 14) +#define Disable_IRQ (Config_base + 20) +#define TMS320_PORT1 (Config_base + 22) +#define TMS320_PORT2 (Config_base + 24) +#define TMS320_CLOCK (Config_base + 26) + +/* + * DATA BUFFER in DRAM + */ +#define Extern_table 0x400 /* Base address of the external table + (24 words * 64) total 3K bytes + (24 words * 128) total 6K bytes */ +#define Extern_size 0x60 /* 96 bytes */ +#define RXrptr 0x00 /* read pointer for RX buffer */ +#define RXwptr 0x02 /* write pointer for RX buffer */ +#define TXrptr 0x04 /* read pointer for TX buffer */ +#define TXwptr 0x06 /* write pointer for TX buffer */ +#define HostStat 0x08 /* IRQ flag and general flag */ +#define FlagStat 0x0A +#define FlowControl 0x0C /* B7 B6 B5 B4 B3 B2 B1 B0 */ + /* x x x x | | | | */ + /* | | | + CTS flow */ + /* | | +--- RTS flow */ + /* | +------ TX Xon/Xoff */ + /* +--------- RX Xon/Xoff */ +#define Break_cnt 0x0E /* received break count */ +#define CD180TXirq 0x10 /* if non-0: enable TX irq */ +#define RX_mask 0x12 +#define TX_mask 0x14 +#define Ofs_rxb 0x16 +#define Ofs_txb 0x18 +#define Page_rxb 0x1A +#define Page_txb 0x1C +#define EndPage_rxb 0x1E +#define EndPage_txb 0x20 +#define Data_error 0x22 +#define RxTrigger 0x28 +#define TxTrigger 0x2a + +#define rRXwptr 0x34 +#define Low_water 0x36 + +#define FuncCode 0x40 +#define FuncArg 0x42 +#define FuncArg1 0x44 + +#define C218rx_size 0x2000 /* 8K bytes */ +#define C218tx_size 0x8000 /* 32K bytes */ + +#define C218rx_mask (C218rx_size - 1) +#define C218tx_mask (C218tx_size - 1) + +#define C320p8rx_size 0x2000 +#define C320p8tx_size 0x8000 +#define C320p8rx_mask (C320p8rx_size - 1) +#define C320p8tx_mask (C320p8tx_size - 1) + +#define C320p16rx_size 0x2000 +#define C320p16tx_size 0x4000 +#define C320p16rx_mask (C320p16rx_size - 1) +#define C320p16tx_mask (C320p16tx_size - 1) + +#define C320p24rx_size 0x2000 +#define C320p24tx_size 0x2000 +#define C320p24rx_mask (C320p24rx_size - 1) +#define C320p24tx_mask (C320p24tx_size - 1) + +#define C320p32rx_size 0x1000 +#define C320p32tx_size 0x1000 +#define C320p32rx_mask (C320p32rx_size - 1) +#define C320p32tx_mask (C320p32tx_size - 1) + +#define Page_size 0x2000U +#define Page_mask (Page_size - 1) +#define C218rx_spage 3 +#define C218tx_spage 4 +#define C218rx_pageno 1 +#define C218tx_pageno 4 +#define C218buf_pageno 5 + +#define C320p8rx_spage 3 +#define C320p8tx_spage 4 +#define C320p8rx_pgno 1 +#define C320p8tx_pgno 4 +#define C320p8buf_pgno 5 + +#define C320p16rx_spage 3 +#define C320p16tx_spage 4 +#define C320p16rx_pgno 1 +#define C320p16tx_pgno 2 +#define C320p16buf_pgno 3 + +#define C320p24rx_spage 3 +#define C320p24tx_spage 4 +#define C320p24rx_pgno 1 +#define C320p24tx_pgno 1 +#define C320p24buf_pgno 2 + +#define C320p32rx_spage 3 +#define C320p32tx_ofs C320p32rx_size +#define C320p32tx_spage 3 +#define C320p32buf_pgno 1 + +/* + * Host Status + */ +#define WakeupRx 0x01 +#define WakeupTx 0x02 +#define WakeupBreak 0x08 +#define WakeupLine 0x10 +#define WakeupIntr 0x20 +#define WakeupQuit 0x40 +#define WakeupEOF 0x80 /* used in VTIME control */ +#define WakeupRxTrigger 0x100 +#define WakeupTxTrigger 0x200 +/* + * Flag status + */ +#define Rx_over 0x01 +#define Xoff_state 0x02 +#define Tx_flowOff 0x04 +#define Tx_enable 0x08 +#define CTS_state 0x10 +#define DSR_state 0x20 +#define DCD_state 0x80 +/* + * FlowControl + */ +#define CTS_FlowCtl 1 +#define RTS_FlowCtl 2 +#define Tx_FlowCtl 4 +#define Rx_FlowCtl 8 +#define IXM_IXANY 0x10 + +#define LowWater 128 + +#define DTR_ON 1 +#define RTS_ON 2 +#define CTS_ON 1 +#define DSR_ON 2 +#define DCD_ON 8 + +/* mode definition */ +#define MX_CS8 0x03 +#define MX_CS7 0x02 +#define MX_CS6 0x01 +#define MX_CS5 0x00 + +#define MX_STOP1 0x00 +#define MX_STOP15 0x04 +#define MX_STOP2 0x08 + +#define MX_PARNONE 0x00 +#define MX_PAREVEN 0x40 +#define MX_PARODD 0xC0 + +#endif diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 68c2e923469..4b81a85c5b5 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -307,6 +307,200 @@ static unsigned char mxser_msr[MXSER_PORTS + 1]; static struct mxser_mon_ext mon_data_ext; static int mxser_set_baud_method[MXSER_PORTS + 1]; +static void mxser_enable_must_enchance_mode(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr |= MOXA_MUST_EFR_EFRB_ENABLE; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_disable_must_enchance_mode(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_EFRB_ENABLE; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_set_must_xon1_value(unsigned long baseio, u8 value) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_BANK_MASK; + efr |= MOXA_MUST_EFR_BANK0; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(value, baseio + MOXA_MUST_XON1_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_set_must_xoff1_value(unsigned long baseio, u8 value) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_BANK_MASK; + efr |= MOXA_MUST_EFR_BANK0; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(value, baseio + MOXA_MUST_XOFF1_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_set_must_fifo_value(struct mxser_port *info) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(info->ioaddr + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, info->ioaddr + UART_LCR); + + efr = inb(info->ioaddr + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_BANK_MASK; + efr |= MOXA_MUST_EFR_BANK1; + + outb(efr, info->ioaddr + MOXA_MUST_EFR_REGISTER); + outb((u8)info->rx_high_water, info->ioaddr + MOXA_MUST_RBRTH_REGISTER); + outb((u8)info->rx_trigger, info->ioaddr + MOXA_MUST_RBRTI_REGISTER); + outb((u8)info->rx_low_water, info->ioaddr + MOXA_MUST_RBRTL_REGISTER); + outb(oldlcr, info->ioaddr + UART_LCR); +} + +static void mxser_set_must_enum_value(unsigned long baseio, u8 value) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_BANK_MASK; + efr |= MOXA_MUST_EFR_BANK2; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(value, baseio + MOXA_MUST_ENUM_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_get_must_hardware_id(unsigned long baseio, u8 *pId) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_BANK_MASK; + efr |= MOXA_MUST_EFR_BANK2; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + *pId = inb(baseio + MOXA_MUST_HWID_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_SF_MASK; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_enable_must_tx_software_flow_control(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_SF_TX_MASK; + efr |= MOXA_MUST_EFR_SF_TX1; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_disable_must_tx_software_flow_control(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_SF_TX_MASK; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_enable_must_rx_software_flow_control(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_SF_RX_MASK; + efr |= MOXA_MUST_EFR_SF_RX1; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_disable_must_rx_software_flow_control(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_SF_RX_MASK; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + #ifdef CONFIG_PCI static int __devinit CheckIsMoxaMust(unsigned long io) { @@ -314,16 +508,16 @@ static int __devinit CheckIsMoxaMust(unsigned long io) int i; outb(0, io + UART_LCR); - DISABLE_MOXA_MUST_ENCHANCE_MODE(io); + mxser_disable_must_enchance_mode(io); oldmcr = inb(io + UART_MCR); outb(0, io + UART_MCR); - SET_MOXA_MUST_XON1_VALUE(io, 0x11); + mxser_set_must_xon1_value(io, 0x11); if ((hwid = inb(io + UART_MCR)) != 0) { outb(oldmcr, io + UART_MCR); return MOXA_OTHER_UART; } - GET_MOXA_MUST_HARDWARE_ID(io, &hwid); + mxser_get_must_hardware_id(io, &hwid); for (i = 1; i < UART_INFO_NUM; i++) { /* 0 = OTHER_UART */ if (hwid == Gpci_uart_info[i].type) return (int)hwid; @@ -494,10 +688,10 @@ static int mxser_set_baud(struct mxser_port *info, long newspd) } else quot /= newspd; - SET_MOXA_MUST_ENUM_VALUE(info->ioaddr, quot); + mxser_set_must_enum_value(info->ioaddr, quot); } else #endif - SET_MOXA_MUST_ENUM_VALUE(info->ioaddr, 0); + mxser_set_must_enum_value(info->ioaddr, 0); return 0; } @@ -553,14 +747,14 @@ static int mxser_change_speed(struct mxser_port *info, if (info->board->chip_flag) { fcr = UART_FCR_ENABLE_FIFO; fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE; - SET_MOXA_MUST_FIFO_VALUE(info); + mxser_set_must_fifo_value(info); } else fcr = 0; } else { fcr = UART_FCR_ENABLE_FIFO; if (info->board->chip_flag) { fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE; - SET_MOXA_MUST_FIFO_VALUE(info); + mxser_set_must_fifo_value(info); } else { switch (info->rx_trigger) { case 1: @@ -657,17 +851,21 @@ static int mxser_change_speed(struct mxser_port *info, } } if (info->board->chip_flag) { - SET_MOXA_MUST_XON1_VALUE(info->ioaddr, START_CHAR(info->tty)); - SET_MOXA_MUST_XOFF1_VALUE(info->ioaddr, STOP_CHAR(info->tty)); + mxser_set_must_xon1_value(info->ioaddr, START_CHAR(info->tty)); + mxser_set_must_xoff1_value(info->ioaddr, STOP_CHAR(info->tty)); if (I_IXON(info->tty)) { - ENABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->ioaddr); + mxser_enable_must_rx_software_flow_control( + info->ioaddr); } else { - DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->ioaddr); + mxser_disable_must_rx_software_flow_control( + info->ioaddr); } if (I_IXOFF(info->tty)) { - ENABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(info->ioaddr); + mxser_enable_must_tx_software_flow_control( + info->ioaddr); } else { - DISABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(info->ioaddr); + mxser_disable_must_tx_software_flow_control( + info->ioaddr); } } @@ -927,6 +1125,27 @@ static int mxser_open(struct tty_struct *tty, struct file *filp) return 0; } +static void mxser_flush_buffer(struct tty_struct *tty) +{ + struct mxser_port *info = tty->driver_data; + char fcr; + unsigned long flags; + + + spin_lock_irqsave(&info->slock, flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + fcr = inb(info->ioaddr + UART_FCR); + outb((fcr | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), + info->ioaddr + UART_FCR); + outb(fcr, info->ioaddr + UART_FCR); + + spin_unlock_irqrestore(&info->slock, flags); + + tty_wakeup(tty); +} + + /* * This routine is called when the serial port gets closed. First, we * wait for the last remaining data to be sent. Then, we unlink its @@ -1013,9 +1232,7 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) } mxser_shutdown(info); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); - + mxser_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; @@ -1072,16 +1289,16 @@ static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int cou return total; } -static void mxser_put_char(struct tty_struct *tty, unsigned char ch) +static int mxser_put_char(struct tty_struct *tty, unsigned char ch) { struct mxser_port *info = tty->driver_data; unsigned long flags; if (!info->xmit_buf) - return; + return 0; if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) - return; + return 0; spin_lock_irqsave(&info->slock, flags); info->xmit_buf[info->xmit_head++] = ch; @@ -1099,6 +1316,7 @@ static void mxser_put_char(struct tty_struct *tty, unsigned char ch) spin_unlock_irqrestore(&info->slock, flags); } } + return 1; } @@ -1142,26 +1360,6 @@ static int mxser_chars_in_buffer(struct tty_struct *tty) return info->xmit_cnt; } -static void mxser_flush_buffer(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - char fcr; - unsigned long flags; - - - spin_lock_irqsave(&info->slock, flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - fcr = inb(info->ioaddr + UART_FCR); - outb((fcr | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), - info->ioaddr + UART_FCR); - outb(fcr, info->ioaddr + UART_FCR); - - spin_unlock_irqrestore(&info->slock, flags); - - tty_wakeup(tty); -} - /* * ------------------------------------------------------------ * friends of mxser_ioctl() @@ -1460,6 +1658,7 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) struct mxser_port *port; int result, status; unsigned int i, j; + int ret = 0; switch (cmd) { case MOXA_GET_MAJOR: @@ -1467,18 +1666,21 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) case MOXA_CHKPORTENABLE: result = 0; - + lock_kernel(); for (i = 0; i < MXSER_BOARDS; i++) for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) if (mxser_boards[i].ports[j].ioaddr) result |= (1 << i); - + unlock_kernel(); return put_user(result, (unsigned long __user *)argp); case MOXA_GETDATACOUNT: + lock_kernel(); if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log))) - return -EFAULT; - return 0; + ret = -EFAULT; + unlock_kernel(); + return ret; case MOXA_GETMSTATUS: + lock_kernel(); for (i = 0; i < MXSER_BOARDS; i++) for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) { port = &mxser_boards[i].ports[j]; @@ -1515,6 +1717,7 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) else GMStatus[i].cts = 0; } + unlock_kernel(); if (copy_to_user(argp, GMStatus, sizeof(struct mxser_mstatus) * MXSER_PORTS)) return -EFAULT; @@ -1524,7 +1727,8 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) unsigned long opmode; unsigned cflag, iflag; - for (i = 0; i < MXSER_BOARDS; i++) + lock_kernel(); + for (i = 0; i < MXSER_BOARDS; i++) { for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) { port = &mxser_boards[i].ports[j]; if (!port->ioaddr) @@ -1589,13 +1793,14 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) mon_data_ext.iftype[i] = opmode; } - if (copy_to_user(argp, &mon_data_ext, - sizeof(mon_data_ext))) - return -EFAULT; - - return 0; - - } default: + } + unlock_kernel(); + if (copy_to_user(argp, &mon_data_ext, + sizeof(mon_data_ext))) + return -EFAULT; + return 0; + } + default: return -ENOIOCTLCMD; } return 0; @@ -1651,16 +1856,20 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, opmode != RS422_MODE && opmode != RS485_4WIRE_MODE) return -EFAULT; + lock_kernel(); mask = ModeMask[p]; shiftbit = p * 2; val = inb(info->opmode_ioaddr); val &= mask; val |= (opmode << shiftbit); outb(val, info->opmode_ioaddr); + unlock_kernel(); } else { + lock_kernel(); shiftbit = p * 2; opmode = inb(info->opmode_ioaddr) >> shiftbit; opmode &= OP_MODE_MASK; + unlock_kernel(); if (put_user(opmode, (int __user *)argp)) return -EFAULT; } @@ -1687,19 +1896,18 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, tty_wait_until_sent(tty, 0); mxser_send_break(info, arg ? arg * (HZ / 10) : HZ / 4); return 0; - case TIOCGSOFTCAR: - return put_user(!!C_CLOCAL(tty), (unsigned long __user *)argp); - case TIOCSSOFTCAR: - if (get_user(arg, (unsigned long __user *)argp)) - return -EFAULT; - tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); - return 0; case TIOCGSERIAL: - return mxser_get_serial_info(info, argp); + lock_kernel(); + retval = mxser_get_serial_info(info, argp); + unlock_kernel(); + return retval; case TIOCSSERIAL: - return mxser_set_serial_info(info, argp); + lock_kernel(); + retval = mxser_set_serial_info(info, argp); + unlock_kernel(); + return retval; case TIOCSERGETLSR: /* Get line status register */ - return mxser_get_lsr_info(info, argp); + return mxser_get_lsr_info(info, argp); /* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change * - mask passed in arg for lines of interest @@ -1746,24 +1954,27 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, case MOXA_HighSpeedOn: return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp); case MOXA_SDS_RSTICOUNTER: + lock_kernel(); info->mon_data.rxcnt = 0; info->mon_data.txcnt = 0; + unlock_kernel(); return 0; case MOXA_ASPP_OQUEUE:{ int len, lsr; + lock_kernel(); len = mxser_chars_in_buffer(tty); - lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT; - len += (lsr ? 0 : 1); + unlock_kernel(); return put_user(len, (int __user *)argp); } case MOXA_ASPP_MON: { int mcr, status; + lock_kernel(); status = mxser_get_msr(info->ioaddr, 1, tty->index); mxser_check_modem_status(info, status); @@ -1782,7 +1993,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD; else info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD; - + unlock_kernel(); if (copy_to_user(argp, &info->mon_data, sizeof(struct mxser_mon))) return -EFAULT; @@ -1925,7 +2136,8 @@ static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termi if (info->board->chip_flag) { spin_lock_irqsave(&info->slock, flags); - DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->ioaddr); + mxser_disable_must_rx_software_flow_control( + info->ioaddr); spin_unlock_irqrestore(&info->slock, flags); } @@ -1979,6 +2191,7 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout) timeout, char_time); printk("jiff=%lu...", jiffies); #endif + lock_kernel(); while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) { #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...", lsr, jiffies); @@ -1990,6 +2203,7 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout) break; } set_current_state(TASK_RUNNING); + unlock_kernel(); #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); @@ -2342,7 +2556,7 @@ static int __devinit mxser_initbrd(struct mxser_board *brd, /* Enhance mode enabled here */ if (brd->chip_flag != MOXA_OTHER_UART) - ENABLE_MOXA_MUST_ENCHANCE_MODE(info->ioaddr); + mxser_enable_must_enchance_mode(info->ioaddr); info->flags = ASYNC_SHARE_IRQ; info->type = brd->uart_type; diff --git a/drivers/char/mxser.h b/drivers/char/mxser.h index 84417111595..41878a69203 100644 --- a/drivers/char/mxser.h +++ b/drivers/char/mxser.h @@ -147,141 +147,4 @@ /* Rx software flow control mask */ #define MOXA_MUST_EFR_SF_RX_MASK 0x03 -#define ENABLE_MOXA_MUST_ENCHANCE_MODE(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr |= MOXA_MUST_EFR_EFRB_ENABLE; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define DISABLE_MOXA_MUST_ENCHANCE_MODE(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_EFRB_ENABLE; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define SET_MOXA_MUST_XON1_VALUE(baseio, Value) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK0; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb((u8)(Value), (baseio)+MOXA_MUST_XON1_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define SET_MOXA_MUST_XOFF1_VALUE(baseio, Value) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK0; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb((u8)(Value), (baseio)+MOXA_MUST_XOFF1_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define SET_MOXA_MUST_FIFO_VALUE(info) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((info)->ioaddr+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (info)->ioaddr+UART_LCR);\ - __efr = inb((info)->ioaddr+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK1; \ - outb(__efr, (info)->ioaddr+MOXA_MUST_EFR_REGISTER); \ - outb((u8)((info)->rx_high_water), (info)->ioaddr+ \ - MOXA_MUST_RBRTH_REGISTER); \ - outb((u8)((info)->rx_trigger), (info)->ioaddr+ \ - MOXA_MUST_RBRTI_REGISTER); \ - outb((u8)((info)->rx_low_water), (info)->ioaddr+ \ - MOXA_MUST_RBRTL_REGISTER); \ - outb(__oldlcr, (info)->ioaddr+UART_LCR); \ -} while (0) - -#define SET_MOXA_MUST_ENUM_VALUE(baseio, Value) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK2; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb((u8)(Value), (baseio)+MOXA_MUST_ENUM_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define GET_MOXA_MUST_HARDWARE_ID(baseio, pId) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK2; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - *pId = inb((baseio)+MOXA_MUST_HWID_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_MASK; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define ENABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_TX_MASK; \ - __efr |= MOXA_MUST_EFR_SF_TX1; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define DISABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_TX_MASK; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define ENABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_RX_MASK; \ - __efr |= MOXA_MUST_EFR_SF_RX1; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_RX_MASK; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - #endif diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index 06803ed5568..a35bfd7ee80 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -342,12 +342,10 @@ static int n_hdlc_tty_open (struct tty_struct *tty) #endif /* Flush any pending characters in the driver and discipline. */ - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer (tty); + tty->ldisc.flush_buffer(tty); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer (tty); + tty_driver_flush_buffer(tty); if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d)n_hdlc_tty_open() success\n",__FILE__,__LINE__); @@ -399,7 +397,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) /* Send the next block of data to device */ tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - actual = tty->driver->write(tty, tbuf->buf, tbuf->count); + actual = tty->ops->write(tty, tbuf->buf, tbuf->count); /* rollback was possible and has been done */ if (actual == -ERESTARTSYS) { @@ -578,26 +576,36 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, return -EFAULT; } + lock_kernel(); + for (;;) { - if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { + unlock_kernel(); return -EIO; + } n_hdlc = tty2n_hdlc (tty); if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || - tty != n_hdlc->tty) + tty != n_hdlc->tty) { + unlock_kernel(); return 0; + } rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list); if (rbuf) break; /* no data */ - if (file->f_flags & O_NONBLOCK) + if (file->f_flags & O_NONBLOCK) { + unlock_kernel(); return -EAGAIN; + } interruptible_sleep_on (&tty->read_wait); - if (signal_pending(current)) + if (signal_pending(current)) { + unlock_kernel(); return -EINTR; + } } if (rbuf->count > nr) @@ -618,7 +626,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, kfree(rbuf); else n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf); - + unlock_kernel(); return ret; } /* end of n_hdlc_tty_read() */ @@ -661,6 +669,8 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, count = maxframe; } + lock_kernel(); + add_wait_queue(&tty->write_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); @@ -695,7 +705,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf); n_hdlc_send_frames(n_hdlc,tty); } - + unlock_kernel(); return error; } /* end of n_hdlc_tty_write() */ @@ -740,8 +750,7 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, case TIOCOUTQ: /* get the pending tx byte count in the driver */ - count = tty->driver->chars_in_buffer ? - tty->driver->chars_in_buffer(tty) : 0; + count = tty_chars_in_buffer(tty); /* add size of next output frame in queue */ spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags); if (n_hdlc->tx_buf_list.head) diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c index 6b918b80f73..90216906233 100644 --- a/drivers/char/n_r3964.c +++ b/drivers/char/n_r3964.c @@ -376,8 +376,9 @@ static void put_char(struct r3964_info *pInfo, unsigned char ch) if (tty == NULL) return; - if (tty->driver->put_char) { - tty->driver->put_char(tty, ch); + /* FIXME: put_char should not be called from an IRQ */ + if (tty->ops->put_char) { + tty->ops->put_char(tty, ch); } pInfo->bcc ^= ch; } @@ -386,12 +387,9 @@ static void flush(struct r3964_info *pInfo) { struct tty_struct *tty = pInfo->tty; - if (tty == NULL) + if (tty == NULL || tty->ops->flush_chars == NULL) return; - - if (tty->driver->flush_chars) { - tty->driver->flush_chars(tty); - } + tty->ops->flush_chars(tty); } static void trigger_transmit(struct r3964_info *pInfo) @@ -449,12 +447,11 @@ static void transmit_block(struct r3964_info *pInfo) struct r3964_block_header *pBlock = pInfo->tx_first; int room = 0; - if ((tty == NULL) || (pBlock == NULL)) { + if (tty == NULL || pBlock == NULL) { return; } - if (tty->driver->write_room) - room = tty->driver->write_room(tty); + room = tty_write_room(tty); TRACE_PS("transmit_block %p, room %d, length %d", pBlock, room, pBlock->length); @@ -1075,12 +1072,15 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, TRACE_L("read()"); + lock_kernel(); + pClient = findClient(pInfo, task_pid(current)); if (pClient) { pMsg = remove_msg(pInfo, pClient); if (pMsg == NULL) { /* no messages available. */ if (file->f_flags & O_NONBLOCK) { + unlock_kernel(); return -EAGAIN; } /* block until there is a message: */ @@ -1090,8 +1090,10 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, /* If we still haven't got a message, we must have been signalled */ - if (!pMsg) + if (!pMsg) { + unlock_kernel(); return -EINTR; + } /* deliver msg to client process: */ theMsg.msg_id = pMsg->msg_id; @@ -1102,12 +1104,15 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, kfree(pMsg); TRACE_M("r3964_read - msg kfree %p", pMsg); - if (copy_to_user(buf, &theMsg, count)) + if (copy_to_user(buf, &theMsg, count)) { + unlock_kernel(); return -EFAULT; + } TRACE_PS("read - return %d", count); return count; } + unlock_kernel(); return -EPERM; } @@ -1156,6 +1161,8 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, pHeader->locks = 0; pHeader->owner = NULL; + lock_kernel(); + pClient = findClient(pInfo, task_pid(current)); if (pClient) { pHeader->owner = pClient; @@ -1173,6 +1180,8 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, add_tx_queue(pInfo, pHeader); trigger_transmit(pInfo); + unlock_kernel(); + return 0; } diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 0c09409fa45..19105ec203f 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -147,10 +147,8 @@ static void put_tty_queue(unsigned char c, struct tty_struct *tty) static void check_unthrottle(struct tty_struct *tty) { - if (tty->count && - test_and_clear_bit(TTY_THROTTLED, &tty->flags) && - tty->driver->unthrottle) - tty->driver->unthrottle(tty); + if (tty->count) + tty_unthrottle(tty); } /** @@ -183,22 +181,24 @@ static void reset_buffer_flags(struct tty_struct *tty) * at hangup) or when the N_TTY line discipline internally has to * clean the pending queue (for example some signals). * - * FIXME: tty->ctrl_status is not spinlocked and relies on - * lock_kernel() still. + * Locking: ctrl_lock */ static void n_tty_flush_buffer(struct tty_struct *tty) { + unsigned long flags; /* clear everything and unthrottle the driver */ reset_buffer_flags(tty); if (!tty->link) return; + spin_lock_irqsave(&tty->ctrl_lock, flags); if (tty->link->packet) { tty->ctrl_status |= TIOCPKT_FLUSHREAD; wake_up_interruptible(&tty->link->read_wait); } + spin_unlock_irqrestore(&tty->ctrl_lock, flags); } /** @@ -264,17 +264,18 @@ static inline int is_continuation(unsigned char c, struct tty_struct *tty) * relevant in the world today. If you ever need them, add them here. * * Called from both the receive and transmit sides and can be called - * re-entrantly. Relies on lock_kernel() still. + * re-entrantly. Relies on lock_kernel() for tty->column state. */ static int opost(unsigned char c, struct tty_struct *tty) { int space, spaces; - space = tty->driver->write_room(tty); + space = tty_write_room(tty); if (!space) return -1; + lock_kernel(); if (O_OPOST(tty)) { switch (c) { case '\n': @@ -283,7 +284,7 @@ static int opost(unsigned char c, struct tty_struct *tty) if (O_ONLCR(tty)) { if (space < 2) return -1; - tty->driver->put_char(tty, '\r'); + tty_put_char(tty, '\r'); tty->column = 0; } tty->canon_column = tty->column; @@ -305,7 +306,7 @@ static int opost(unsigned char c, struct tty_struct *tty) if (space < spaces) return -1; tty->column += spaces; - tty->driver->write(tty, " ", spaces); + tty->ops->write(tty, " ", spaces); return 0; } tty->column += spaces; @@ -322,7 +323,8 @@ static int opost(unsigned char c, struct tty_struct *tty) break; } } - tty->driver->put_char(tty, c); + tty_put_char(tty, c); + unlock_kernel(); return 0; } @@ -337,7 +339,8 @@ static int opost(unsigned char c, struct tty_struct *tty) * the simple cases normally found and helps to generate blocks of * symbols for the console driver and thus improve performance. * - * Called from write_chan under the tty layer write lock. + * Called from write_chan under the tty layer write lock. Relies + * on lock_kernel for the tty->column state. */ static ssize_t opost_block(struct tty_struct *tty, @@ -347,12 +350,13 @@ static ssize_t opost_block(struct tty_struct *tty, int i; const unsigned char *cp; - space = tty->driver->write_room(tty); + space = tty_write_room(tty); if (!space) return 0; if (nr > space) nr = space; + lock_kernel(); for (i = 0, cp = buf; i < nr; i++, cp++) { switch (*cp) { case '\n': @@ -384,27 +388,15 @@ static ssize_t opost_block(struct tty_struct *tty, } } break_out: - if (tty->driver->flush_chars) - tty->driver->flush_chars(tty); - i = tty->driver->write(tty, buf, i); + if (tty->ops->flush_chars) + tty->ops->flush_chars(tty); + i = tty->ops->write(tty, buf, i); + unlock_kernel(); return i; } /** - * put_char - write character to driver - * @c: character (or part of unicode symbol) - * @tty: terminal device - * - * Queue a byte to the driver layer for output - */ - -static inline void put_char(unsigned char c, struct tty_struct *tty) -{ - tty->driver->put_char(tty, c); -} - -/** * echo_char - echo characters * @c: unicode byte to echo * @tty: terminal device @@ -416,8 +408,8 @@ static inline void put_char(unsigned char c, struct tty_struct *tty) static void echo_char(unsigned char c, struct tty_struct *tty) { if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') { - put_char('^', tty); - put_char(c ^ 0100, tty); + tty_put_char(tty, '^'); + tty_put_char(tty, c ^ 0100); tty->column += 2; } else opost(c, tty); @@ -426,7 +418,7 @@ static void echo_char(unsigned char c, struct tty_struct *tty) static inline void finish_erasing(struct tty_struct *tty) { if (tty->erasing) { - put_char('/', tty); + tty_put_char(tty, '/'); tty->column++; tty->erasing = 0; } @@ -510,7 +502,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { if (!tty->erasing) { - put_char('\\', tty); + tty_put_char(tty, '\\'); tty->column++; tty->erasing = 1; } @@ -518,7 +510,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) echo_char(c, tty); while (--cnt > 0) { head = (head+1) & (N_TTY_BUF_SIZE-1); - put_char(tty->read_buf[head], tty); + tty_put_char(tty, tty->read_buf[head]); } } else if (kill_type == ERASE && !L_ECHOE(tty)) { echo_char(ERASE_CHAR(tty), tty); @@ -546,22 +538,22 @@ static void eraser(unsigned char c, struct tty_struct *tty) /* Now backup to that column. */ while (tty->column > col) { /* Can't use opost here. */ - put_char('\b', tty); + tty_put_char(tty, '\b'); if (tty->column > 0) tty->column--; } } else { if (iscntrl(c) && L_ECHOCTL(tty)) { - put_char('\b', tty); - put_char(' ', tty); - put_char('\b', tty); + tty_put_char(tty, '\b'); + tty_put_char(tty, ' '); + tty_put_char(tty, '\b'); if (tty->column > 0) tty->column--; } if (!iscntrl(c) || L_ECHOCTL(tty)) { - put_char('\b', tty); - put_char(' ', tty); - put_char('\b', tty); + tty_put_char(tty, '\b'); + tty_put_char(tty, ' '); + tty_put_char(tty, '\b'); if (tty->column > 0) tty->column--; } @@ -592,8 +584,7 @@ static inline void isig(int sig, struct tty_struct *tty, int flush) kill_pgrp(tty->pgrp, sig, 1); if (flush || !L_NOFLSH(tty)) { n_tty_flush_buffer(tty); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty_driver_flush_buffer(tty); } } @@ -701,7 +692,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && ((I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty)) || - c == INTR_CHAR(tty) || c == QUIT_CHAR(tty))) + c == INTR_CHAR(tty) || c == QUIT_CHAR(tty) || c == SUSP_CHAR(tty))) start_tty(tty); if (tty->closing) { @@ -725,7 +716,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) tty->lnext = 0; if (L_ECHO(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { - put_char('\a', tty); /* beep if no space */ + tty_put_char(tty, '\a'); /* beep if no space */ return; } /* Record the column of first canon char. */ @@ -739,13 +730,6 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) return; } - if (c == '\r') { - if (I_IGNCR(tty)) - return; - if (I_ICRNL(tty)) - c = '\n'; - } else if (c == '\n' && I_INLCR(tty)) - c = '\r'; if (I_IXON(tty)) { if (c == START_CHAR(tty)) { start_tty(tty); @@ -756,6 +740,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) return; } } + if (L_ISIG(tty)) { int signal; signal = SIGINT; @@ -775,8 +760,7 @@ send_signal: */ if (!L_NOFLSH(tty)) { n_tty_flush_buffer(tty); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty_driver_flush_buffer(tty); } if (L_ECHO(tty)) echo_char(c, tty); @@ -785,6 +769,15 @@ send_signal: return; } } + + if (c == '\r') { + if (I_IGNCR(tty)) + return; + if (I_ICRNL(tty)) + c = '\n'; + } else if (c == '\n' && I_INLCR(tty)) + c = '\r'; + if (tty->icanon) { if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { @@ -796,8 +789,8 @@ send_signal: if (L_ECHO(tty)) { finish_erasing(tty); if (L_ECHOCTL(tty)) { - put_char('^', tty); - put_char('\b', tty); + tty_put_char(tty, '^'); + tty_put_char(tty, '\b'); } } return; @@ -818,7 +811,7 @@ send_signal: if (c == '\n') { if (L_ECHO(tty) || L_ECHONL(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) - put_char('\a', tty); + tty_put_char(tty, '\a'); opost('\n', tty); } goto handle_newline; @@ -836,7 +829,7 @@ send_signal: */ if (L_ECHO(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) - put_char('\a', tty); + tty_put_char(tty, '\a'); /* Record the column of first canon char. */ if (tty->canon_head == tty->read_head) tty->canon_column = tty->column; @@ -866,7 +859,7 @@ handle_newline: finish_erasing(tty); if (L_ECHO(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { - put_char('\a', tty); /* beep if no space */ + tty_put_char(tty, '\a'); /* beep if no space */ return; } if (c == '\n') @@ -970,8 +963,8 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, break; } } - if (tty->driver->flush_chars) - tty->driver->flush_chars(tty); + if (tty->ops->flush_chars) + tty->ops->flush_chars(tty); } n_tty_set_room(tty); @@ -987,12 +980,8 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, * mode. We don't want to throttle the driver if we're in * canonical mode and don't have a newline yet! */ - if (tty->receive_room < TTY_THRESHOLD_THROTTLE) { - /* check TTY_THROTTLED first so it indicates our state */ - if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && - tty->driver->throttle) - tty->driver->throttle(tty); - } + if (tty->receive_room < TTY_THRESHOLD_THROTTLE) + tty_throttle(tty); } int is_ignored(int sig) @@ -1076,6 +1065,9 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) tty->real_raw = 0; } n_tty_set_room(tty); + /* The termios change make the tty ready for I/O */ + wake_up_interruptible(&tty->write_wait); + wake_up_interruptible(&tty->read_wait); } /** @@ -1194,6 +1186,11 @@ extern ssize_t redirected_tty_write(struct file *, const char __user *, * Perform job control management checks on this file/tty descriptor * and if appropriate send any needed signals and return a negative * error code if action should be taken. + * + * FIXME: + * Locking: None - redirected write test is safe, testing + * current->signal should possibly lock current->sighand + * pgrp locking ? */ static int job_control(struct tty_struct *tty, struct file *file) @@ -1246,6 +1243,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file, ssize_t size; long timeout; unsigned long flags; + int packet; do_it_again: @@ -1289,16 +1287,19 @@ do_it_again: if (mutex_lock_interruptible(&tty->atomic_read_lock)) return -ERESTARTSYS; } + packet = tty->packet; add_wait_queue(&tty->read_wait, &wait); while (nr) { /* First test for status change. */ - if (tty->packet && tty->link->ctrl_status) { + if (packet && tty->link->ctrl_status) { unsigned char cs; if (b != buf) break; + spin_lock_irqsave(&tty->link->ctrl_lock, flags); cs = tty->link->ctrl_status; tty->link->ctrl_status = 0; + spin_unlock_irqrestore(&tty->link->ctrl_lock, flags); if (tty_put_user(tty, cs, b++)) { retval = -EFAULT; b--; @@ -1333,6 +1334,7 @@ do_it_again: retval = -ERESTARTSYS; break; } + /* FIXME: does n_tty_set_room need locking ? */ n_tty_set_room(tty); timeout = schedule_timeout(timeout); continue; @@ -1340,7 +1342,7 @@ do_it_again: __set_current_state(TASK_RUNNING); /* Deal with packet mode. */ - if (tty->packet && b == buf) { + if (packet && b == buf) { if (tty_put_user(tty, TIOCPKT_DATA, b++)) { retval = -EFAULT; b--; @@ -1388,6 +1390,8 @@ do_it_again: break; } else { int uncopied; + /* The copy function takes the read lock and handles + locking internally for this case */ uncopied = copy_from_read_buf(tty, &b, &nr); uncopied += copy_from_read_buf(tty, &b, &nr); if (uncopied) { @@ -1429,7 +1433,6 @@ do_it_again: goto do_it_again; n_tty_set_room(tty); - return retval; } @@ -1492,11 +1495,11 @@ static ssize_t write_chan(struct tty_struct *tty, struct file *file, break; b++; nr--; } - if (tty->driver->flush_chars) - tty->driver->flush_chars(tty); + if (tty->ops->flush_chars) + tty->ops->flush_chars(tty); } else { while (nr > 0) { - c = tty->driver->write(tty, b, nr); + c = tty->ops->write(tty, b, nr); if (c < 0) { retval = c; goto break_out; @@ -1533,11 +1536,6 @@ break_out: * * This code must be sure never to sleep through a hangup. * Called without the kernel lock held - fine - * - * FIXME: if someone changes the VMIN or discipline settings for the - * terminal while another process is in poll() the poll does not - * recompute the new limits. Possibly set_termios should issue - * a read wakeup to fix this bug. */ static unsigned int normal_poll(struct tty_struct *tty, struct file *file, @@ -1561,9 +1559,9 @@ static unsigned int normal_poll(struct tty_struct *tty, struct file *file, else tty->minimum_to_wake = 1; } - if (!tty_is_writelocked(tty) && - tty->driver->chars_in_buffer(tty) < WAKEUP_CHARS && - tty->driver->write_room(tty) > 0) + if (tty->ops->write && !tty_is_writelocked(tty) && + tty_chars_in_buffer(tty) < WAKEUP_CHARS && + tty_write_room(tty) > 0) mask |= POLLOUT | POLLWRNORM; return mask; } diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index 6a6843a0a67..66a0f931c66 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -73,7 +73,7 @@ do { \ char tmp[P_BUF_SIZE]; \ snprintf(tmp, sizeof(tmp), ##args); \ printk(_err_flag_ "[%d] %s(): %s\n", __LINE__, \ - __FUNCTION__, tmp); \ + __func__, tmp); \ } while (0) #define DBG1(args...) D_(0x01, ##args) @@ -1407,7 +1407,7 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev, /* Find out what card type it is */ nozomi_get_card_type(dc); - dc->base_addr = ioremap(start, dc->card_type); + dc->base_addr = ioremap_nocache(start, dc->card_type); if (!dc->base_addr) { dev_err(&pdev->dev, "Unable to map card MMIO\n"); ret = -ENODEV; @@ -1724,6 +1724,8 @@ static int ntty_tiocmget(struct tty_struct *tty, struct file *file) const struct ctrl_dl *ctrl_dl = &port->ctrl_dl; const struct ctrl_ul *ctrl_ul = &port->ctrl_ul; + /* Note: these could change under us but it is not clear this + matters if so */ return (ctrl_ul->RTS ? TIOCM_RTS : 0) | (ctrl_ul->DTR ? TIOCM_DTR : 0) | (ctrl_dl->DCD ? TIOCM_CAR : 0) | @@ -1849,16 +1851,6 @@ static void ntty_throttle(struct tty_struct *tty) spin_unlock_irqrestore(&dc->spin_mutex, flags); } -/* just to discard single character writes */ -static void ntty_put_char(struct tty_struct *tty, unsigned char c) -{ - /* - * card does not react correct when we write single chars - * to the card, so we discard them - */ - DBG2("PUT CHAR Function: %c", c); -} - /* Returns number of chars in buffer, called by tty layer */ static s32 ntty_chars_in_buffer(struct tty_struct *tty) { @@ -1892,7 +1884,6 @@ static const struct tty_operations tty_ops = { .unthrottle = ntty_unthrottle, .throttle = ntty_throttle, .chars_in_buffer = ntty_chars_in_buffer, - .put_char = ntty_put_char, .tiocmget = ntty_tiocmget, .tiocmset = ntty_tiocmset, }; diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 454d7324ba4..4a933d41342 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -53,7 +53,7 @@ module_param(pc_debug, int, 0600); #define DEBUGP(n, rdr, x, args...) do { \ if (pc_debug >= (n)) \ dev_printk(KERN_DEBUG, reader_to_dev(rdr), "%s:" x, \ - __FUNCTION__ , ## args); \ + __func__ , ## args); \ } while (0) #else #define DEBUGP(n, rdr, x, args...) diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index 5f291bf739a..035084c0732 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -47,7 +47,7 @@ module_param(pc_debug, int, 0600); #define DEBUGP(n, rdr, x, args...) do { \ if (pc_debug >= (n)) \ dev_printk(KERN_DEBUG, reader_to_dev(rdr), "%s:" x, \ - __FUNCTION__ , ##args); \ + __func__ , ##args); \ } while (0) #else #define DEBUGP(n, rdr, x, args...) diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 583356426df..1dd0e992c83 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -503,20 +503,9 @@ static void* mgslpc_get_text_ptr(void) * The wrappers maintain line discipline references * while calling into the line discipline. * - * ldisc_flush_buffer - flush line discipline receive buffers * ldisc_receive_buf - pass receive data to line discipline */ -static void ldisc_flush_buffer(struct tty_struct *tty) -{ - struct tty_ldisc *ld = tty_ldisc_ref(tty); - if (ld) { - if (ld->flush_buffer) - ld->flush_buffer(tty); - tty_ldisc_deref(ld); - } -} - static void ldisc_receive_buf(struct tty_struct *tty, const __u8 *data, char *flags, int count) { @@ -1556,7 +1545,7 @@ static void mgslpc_change_params(MGSLPC_INFO *info) /* Add a character to the transmit buffer */ -static void mgslpc_put_char(struct tty_struct *tty, unsigned char ch) +static int mgslpc_put_char(struct tty_struct *tty, unsigned char ch) { MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; unsigned long flags; @@ -1567,10 +1556,10 @@ static void mgslpc_put_char(struct tty_struct *tty, unsigned char ch) } if (mgslpc_paranoia_check(info, tty->name, "mgslpc_put_char")) - return; + return 0; if (!info->tx_buf) - return; + return 0; spin_lock_irqsave(&info->lock,flags); @@ -1583,6 +1572,7 @@ static void mgslpc_put_char(struct tty_struct *tty, unsigned char ch) } spin_unlock_irqrestore(&info->lock,flags); + return 1; } /* Enable transmitter so remaining characters in the @@ -2467,10 +2457,9 @@ static void mgslpc_close(struct tty_struct *tty, struct file * filp) if (info->flags & ASYNC_INITIALIZED) mgslpc_wait_until_sent(tty, info->timeout); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + mgslpc_flush_buffer(tty); - ldisc_flush_buffer(tty); + tty_ldisc_flush(tty); shutdown(info); diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 706ff34728f..0a05c038ae6 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -181,6 +181,7 @@ static int pty_set_lock(struct tty_struct *tty, int __user * arg) static void pty_flush_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; + unsigned long flags; if (!to) return; @@ -189,8 +190,10 @@ static void pty_flush_buffer(struct tty_struct *tty) to->ldisc.flush_buffer(to); if (to->packet) { + spin_lock_irqsave(&tty->ctrl_lock, flags); tty->ctrl_status |= TIOCPKT_FLUSHWRITE; wake_up_interruptible(&to->read_wait); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); } } @@ -251,6 +254,18 @@ static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file, static int legacy_count = CONFIG_LEGACY_PTY_COUNT; module_param(legacy_count, int, 0); +static const struct tty_operations pty_ops_bsd = { + .open = pty_open, + .close = pty_close, + .write = pty_write, + .write_room = pty_write_room, + .flush_buffer = pty_flush_buffer, + .chars_in_buffer = pty_chars_in_buffer, + .unthrottle = pty_unthrottle, + .set_termios = pty_set_termios, + .ioctl = pty_bsd_ioctl, +}; + static void __init legacy_pty_init(void) { if (legacy_count <= 0) @@ -281,7 +296,6 @@ static void __init legacy_pty_init(void) pty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW; pty_driver->other = pty_slave_driver; tty_set_operations(pty_driver, &pty_ops); - pty_driver->ioctl = pty_bsd_ioctl; pty_slave_driver->owner = THIS_MODULE; pty_slave_driver->driver_name = "pty_slave"; @@ -374,6 +388,19 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file, return -ENOIOCTLCMD; } +static const struct tty_operations pty_unix98_ops = { + .open = pty_open, + .close = pty_close, + .write = pty_write, + .write_room = pty_write_room, + .flush_buffer = pty_flush_buffer, + .chars_in_buffer = pty_chars_in_buffer, + .unthrottle = pty_unthrottle, + .set_termios = pty_set_termios, + .ioctl = pty_unix98_ioctl +}; + + static void __init unix98_pty_init(void) { ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX); @@ -400,8 +427,7 @@ static void __init unix98_pty_init(void) ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; ptm_driver->other = pts_driver; - tty_set_operations(ptm_driver, &pty_ops); - ptm_driver->ioctl = pty_unix98_ioctl; + tty_set_operations(ptm_driver, &pty_unix98_ops); pts_driver->owner = THIS_MODULE; pts_driver->driver_name = "pty_slave"; diff --git a/drivers/char/rio/cirrus.h b/drivers/char/rio/cirrus.h index f4f837f8682..a03a538a3ef 100644 --- a/drivers/char/rio/cirrus.h +++ b/drivers/char/rio/cirrus.h @@ -43,83 +43,83 @@ /* Bit fields for particular registers shared with driver */ /* COR1 - driver and RTA */ -#define COR1_ODD 0x80 /* Odd parity */ -#define COR1_EVEN 0x00 /* Even parity */ -#define COR1_NOP 0x00 /* No parity */ -#define COR1_FORCE 0x20 /* Force parity */ -#define COR1_NORMAL 0x40 /* With parity */ -#define COR1_1STOP 0x00 /* 1 stop bit */ -#define COR1_15STOP 0x04 /* 1.5 stop bits */ -#define COR1_2STOP 0x08 /* 2 stop bits */ -#define COR1_5BITS 0x00 /* 5 data bits */ -#define COR1_6BITS 0x01 /* 6 data bits */ -#define COR1_7BITS 0x02 /* 7 data bits */ -#define COR1_8BITS 0x03 /* 8 data bits */ - -#define COR1_HOST 0xef /* Safe host bits */ +#define RIOC_COR1_ODD 0x80 /* Odd parity */ +#define RIOC_COR1_EVEN 0x00 /* Even parity */ +#define RIOC_COR1_NOP 0x00 /* No parity */ +#define RIOC_COR1_FORCE 0x20 /* Force parity */ +#define RIOC_COR1_NORMAL 0x40 /* With parity */ +#define RIOC_COR1_1STOP 0x00 /* 1 stop bit */ +#define RIOC_COR1_15STOP 0x04 /* 1.5 stop bits */ +#define RIOC_COR1_2STOP 0x08 /* 2 stop bits */ +#define RIOC_COR1_5BITS 0x00 /* 5 data bits */ +#define RIOC_COR1_6BITS 0x01 /* 6 data bits */ +#define RIOC_COR1_7BITS 0x02 /* 7 data bits */ +#define RIOC_COR1_8BITS 0x03 /* 8 data bits */ + +#define RIOC_COR1_HOST 0xef /* Safe host bits */ /* RTA only */ -#define COR1_CINPCK 0x00 /* Check parity of received characters */ -#define COR1_CNINPCK 0x10 /* Don't check parity */ +#define RIOC_COR1_CINPCK 0x00 /* Check parity of received characters */ +#define RIOC_COR1_CNINPCK 0x10 /* Don't check parity */ /* COR2 bits for both RTA and driver use */ -#define COR2_IXANY 0x80 /* IXANY - any character is XON */ -#define COR2_IXON 0x40 /* IXON - enable tx soft flowcontrol */ -#define COR2_RTSFLOW 0x02 /* Enable tx hardware flow control */ +#define RIOC_COR2_IXANY 0x80 /* IXANY - any character is XON */ +#define RIOC_COR2_IXON 0x40 /* IXON - enable tx soft flowcontrol */ +#define RIOC_COR2_RTSFLOW 0x02 /* Enable tx hardware flow control */ /* Additional driver bits */ -#define COR2_HUPCL 0x20 /* Hang up on close */ -#define COR2_CTSFLOW 0x04 /* Enable rx hardware flow control */ -#define COR2_IXOFF 0x01 /* Enable rx software flow control */ -#define COR2_DTRFLOW 0x08 /* Enable tx hardware flow control */ +#define RIOC_COR2_HUPCL 0x20 /* Hang up on close */ +#define RIOC_COR2_CTSFLOW 0x04 /* Enable rx hardware flow control */ +#define RIOC_COR2_IXOFF 0x01 /* Enable rx software flow control */ +#define RIOC_COR2_DTRFLOW 0x08 /* Enable tx hardware flow control */ /* RTA use only */ -#define COR2_ETC 0x20 /* Embedded transmit options */ -#define COR2_LOCAL 0x10 /* Local loopback mode */ -#define COR2_REMOTE 0x08 /* Remote loopback mode */ -#define COR2_HOST 0xc2 /* Safe host bits */ +#define RIOC_COR2_ETC 0x20 /* Embedded transmit options */ +#define RIOC_COR2_LOCAL 0x10 /* Local loopback mode */ +#define RIOC_COR2_REMOTE 0x08 /* Remote loopback mode */ +#define RIOC_COR2_HOST 0xc2 /* Safe host bits */ /* COR3 - RTA use only */ -#define COR3_SCDRNG 0x80 /* Enable special char detect for range */ -#define COR3_SCD34 0x40 /* Special character detect for SCHR's 3 + 4 */ -#define COR3_FCT 0x20 /* Flow control transparency */ -#define COR3_SCD12 0x10 /* Special character detect for SCHR's 1 + 2 */ -#define COR3_FIFO12 0x0c /* 12 chars for receive FIFO threshold */ -#define COR3_FIFO10 0x0a /* 10 chars for receive FIFO threshold */ -#define COR3_FIFO8 0x08 /* 8 chars for receive FIFO threshold */ -#define COR3_FIFO6 0x06 /* 6 chars for receive FIFO threshold */ - -#define COR3_THRESHOLD COR3_FIFO8 /* MUST BE LESS THAN MCOR_THRESHOLD */ - -#define COR3_DEFAULT (COR3_FCT | COR3_THRESHOLD) +#define RIOC_COR3_SCDRNG 0x80 /* Enable special char detect for range */ +#define RIOC_COR3_SCD34 0x40 /* Special character detect for SCHR's 3 + 4 */ +#define RIOC_COR3_FCT 0x20 /* Flow control transparency */ +#define RIOC_COR3_SCD12 0x10 /* Special character detect for SCHR's 1 + 2 */ +#define RIOC_COR3_FIFO12 0x0c /* 12 chars for receive FIFO threshold */ +#define RIOC_COR3_FIFO10 0x0a /* 10 chars for receive FIFO threshold */ +#define RIOC_COR3_FIFO8 0x08 /* 8 chars for receive FIFO threshold */ +#define RIOC_COR3_FIFO6 0x06 /* 6 chars for receive FIFO threshold */ + +#define RIOC_COR3_THRESHOLD RIOC_COR3_FIFO8 /* MUST BE LESS THAN MCOR_THRESHOLD */ + +#define RIOC_COR3_DEFAULT (RIOC_COR3_FCT | RIOC_COR3_THRESHOLD) /* Default bits for COR3 */ /* COR4 driver and RTA use */ -#define COR4_IGNCR 0x80 /* Throw away CR's on input */ -#define COR4_ICRNL 0x40 /* Map CR -> NL on input */ -#define COR4_INLCR 0x20 /* Map NL -> CR on input */ -#define COR4_IGNBRK 0x10 /* Ignore Break */ -#define COR4_NBRKINT 0x08 /* No interrupt on break (-BRKINT) */ -#define COR4_RAISEMOD 0x01 /* Raise modem output lines on non-zero baud */ +#define RIOC_COR4_IGNCR 0x80 /* Throw away CR's on input */ +#define RIOC_COR4_ICRNL 0x40 /* Map CR -> NL on input */ +#define RIOC_COR4_INLCR 0x20 /* Map NL -> CR on input */ +#define RIOC_COR4_IGNBRK 0x10 /* Ignore Break */ +#define RIOC_COR4_NBRKINT 0x08 /* No interrupt on break (-BRKINT) */ +#define RIOC_COR4_RAISEMOD 0x01 /* Raise modem output lines on non-zero baud */ /* COR4 driver only */ -#define COR4_IGNPAR 0x04 /* IGNPAR (ignore characters with errors) */ -#define COR4_PARMRK 0x02 /* PARMRK */ +#define RIOC_COR4_IGNPAR 0x04 /* IGNPAR (ignore characters with errors) */ +#define RIOC_COR4_PARMRK 0x02 /* PARMRK */ -#define COR4_HOST 0xf8 /* Safe host bits */ +#define RIOC_COR4_HOST 0xf8 /* Safe host bits */ /* COR4 RTA only */ -#define COR4_CIGNPAR 0x02 /* Thrown away bad characters */ -#define COR4_CPARMRK 0x04 /* PARMRK characters */ -#define COR4_CNPARMRK 0x03 /* Don't PARMRK */ +#define RIOC_COR4_CIGNPAR 0x02 /* Thrown away bad characters */ +#define RIOC_COR4_CPARMRK 0x04 /* PARMRK characters */ +#define RIOC_COR4_CNPARMRK 0x03 /* Don't PARMRK */ /* COR5 driver and RTA use */ -#define COR5_ISTRIP 0x80 /* Strip input chars to 7 bits */ -#define COR5_LNE 0x40 /* Enable LNEXT processing */ -#define COR5_CMOE 0x20 /* Match good and errored characters */ -#define COR5_ONLCR 0x02 /* NL -> CR NL on output */ -#define COR5_OCRNL 0x01 /* CR -> NL on output */ +#define RIOC_COR5_ISTRIP 0x80 /* Strip input chars to 7 bits */ +#define RIOC_COR5_LNE 0x40 /* Enable LNEXT processing */ +#define RIOC_COR5_CMOE 0x20 /* Match good and errored characters */ +#define RIOC_COR5_ONLCR 0x02 /* NL -> CR NL on output */ +#define RIOC_COR5_OCRNL 0x01 /* CR -> NL on output */ /* ** Spare bits - these are not used in the CIRRUS registers, so we use @@ -128,86 +128,86 @@ /* ** tstop and tbusy indication */ -#define COR5_TSTATE_ON 0x08 /* Turn on monitoring of tbusy and tstop */ -#define COR5_TSTATE_OFF 0x04 /* Turn off monitoring of tbusy and tstop */ +#define RIOC_COR5_TSTATE_ON 0x08 /* Turn on monitoring of tbusy and tstop */ +#define RIOC_COR5_TSTATE_OFF 0x04 /* Turn off monitoring of tbusy and tstop */ /* ** TAB3 */ -#define COR5_TAB3 0x10 /* TAB3 mode */ +#define RIOC_COR5_TAB3 0x10 /* TAB3 mode */ -#define COR5_HOST 0xc3 /* Safe host bits */ +#define RIOC_COR5_HOST 0xc3 /* Safe host bits */ /* CCSR */ -#define CCSR_TXFLOFF 0x04 /* Tx is xoffed */ +#define RIOC_CCSR_TXFLOFF 0x04 /* Tx is xoffed */ /* MSVR1 */ /* NB. DTR / CD swapped from Cirrus spec as the pins are also reversed on the RTA. This is because otherwise DCD would get lost on the 1 parallel / 3 serial option. */ -#define MSVR1_CD 0x80 /* CD (DSR on Cirrus) */ -#define MSVR1_RTS 0x40 /* RTS (CTS on Cirrus) */ -#define MSVR1_RI 0x20 /* RI */ -#define MSVR1_DTR 0x10 /* DTR (CD on Cirrus) */ -#define MSVR1_CTS 0x01 /* CTS output pin (RTS on Cirrus) */ +#define RIOC_MSVR1_CD 0x80 /* CD (DSR on Cirrus) */ +#define RIOC_MSVR1_RTS 0x40 /* RTS (CTS on Cirrus) */ +#define RIOC_MSVR1_RI 0x20 /* RI */ +#define RIOC_MSVR1_DTR 0x10 /* DTR (CD on Cirrus) */ +#define RIOC_MSVR1_CTS 0x01 /* CTS output pin (RTS on Cirrus) */ /* Next two used to indicate state of tbusy and tstop to driver */ -#define MSVR1_TSTOP 0x08 /* Set if port flow controlled */ -#define MSVR1_TEMPTY 0x04 /* Set if port tx buffer empty */ +#define RIOC_MSVR1_TSTOP 0x08 /* Set if port flow controlled */ +#define RIOC_MSVR1_TEMPTY 0x04 /* Set if port tx buffer empty */ -#define MSVR1_HOST 0xf3 /* The bits the host wants */ +#define RIOC_MSVR1_HOST 0xf3 /* The bits the host wants */ /* Defines for the subscripts of a CONFIG packet */ -#define CONFIG_COR1 1 /* Option register 1 */ -#define CONFIG_COR2 2 /* Option register 2 */ -#define CONFIG_COR4 3 /* Option register 4 */ -#define CONFIG_COR5 4 /* Option register 5 */ -#define CONFIG_TXXON 5 /* Tx XON character */ -#define CONFIG_TXXOFF 6 /* Tx XOFF character */ -#define CONFIG_RXXON 7 /* Rx XON character */ -#define CONFIG_RXXOFF 8 /* Rx XOFF character */ -#define CONFIG_LNEXT 9 /* LNEXT character */ -#define CONFIG_TXBAUD 10 /* Tx baud rate */ -#define CONFIG_RXBAUD 11 /* Rx baud rate */ - -#define PRE_EMPTIVE 0x80 /* Pre-emptive bit in command field */ +#define RIOC_CONFIG_COR1 1 /* Option register 1 */ +#define RIOC_CONFIG_COR2 2 /* Option register 2 */ +#define RIOC_CONFIG_COR4 3 /* Option register 4 */ +#define RIOC_CONFIG_COR5 4 /* Option register 5 */ +#define RIOC_CONFIG_TXXON 5 /* Tx XON character */ +#define RIOC_CONFIG_TXXOFF 6 /* Tx XOFF character */ +#define RIOC_CONFIG_RXXON 7 /* Rx XON character */ +#define RIOC_CONFIG_RXXOFF 8 /* Rx XOFF character */ +#define RIOC_CONFIG_LNEXT 9 /* LNEXT character */ +#define RIOC_CONFIG_TXBAUD 10 /* Tx baud rate */ +#define RIOC_CONFIG_RXBAUD 11 /* Rx baud rate */ + +#define RIOC_PRE_EMPTIVE 0x80 /* Pre-emptive bit in command field */ /* Packet types going from Host to remote - with the exception of OPEN, MOPEN, CONFIG, SBREAK and MEMDUMP the remaining bytes of the data array will not be used */ -#define OPEN 0x00 /* Open a port */ -#define CONFIG 0x01 /* Configure a port */ -#define MOPEN 0x02 /* Modem open (block for DCD) */ -#define CLOSE 0x03 /* Close a port */ -#define WFLUSH (0x04 | PRE_EMPTIVE) /* Write flush */ -#define RFLUSH (0x05 | PRE_EMPTIVE) /* Read flush */ -#define RESUME (0x06 | PRE_EMPTIVE) /* Resume if xoffed */ -#define SBREAK 0x07 /* Start break */ -#define EBREAK 0x08 /* End break */ -#define SUSPEND (0x09 | PRE_EMPTIVE) /* Susp op (behave as tho xoffed) */ -#define FCLOSE (0x0a | PRE_EMPTIVE) /* Force close */ -#define XPRINT 0x0b /* Xprint packet */ -#define MBIS (0x0c | PRE_EMPTIVE) /* Set modem lines */ -#define MBIC (0x0d | PRE_EMPTIVE) /* Clear modem lines */ -#define MSET (0x0e | PRE_EMPTIVE) /* Set modem lines */ -#define PCLOSE 0x0f /* Pseudo close - Leaves rx/tx enabled */ -#define MGET (0x10 | PRE_EMPTIVE) /* Force update of modem status */ -#define MEMDUMP (0x11 | PRE_EMPTIVE) /* Send back mem from addr supplied */ -#define READ_REGISTER (0x12 | PRE_EMPTIVE) /* Read CD1400 register (debug) */ +#define RIOC_OPEN 0x00 /* Open a port */ +#define RIOC_CONFIG 0x01 /* Configure a port */ +#define RIOC_MOPEN 0x02 /* Modem open (block for DCD) */ +#define RIOC_CLOSE 0x03 /* Close a port */ +#define RIOC_WFLUSH (0x04 | RIOC_PRE_EMPTIVE) /* Write flush */ +#define RIOC_RFLUSH (0x05 | RIOC_PRE_EMPTIVE) /* Read flush */ +#define RIOC_RESUME (0x06 | RIOC_PRE_EMPTIVE) /* Resume if xoffed */ +#define RIOC_SBREAK 0x07 /* Start break */ +#define RIOC_EBREAK 0x08 /* End break */ +#define RIOC_SUSPEND (0x09 | RIOC_PRE_EMPTIVE) /* Susp op (behave as tho xoffed) */ +#define RIOC_FCLOSE (0x0a | RIOC_PRE_EMPTIVE) /* Force close */ +#define RIOC_XPRINT 0x0b /* Xprint packet */ +#define RIOC_MBIS (0x0c | RIOC_PRE_EMPTIVE) /* Set modem lines */ +#define RIOC_MBIC (0x0d | RIOC_PRE_EMPTIVE) /* Clear modem lines */ +#define RIOC_MSET (0x0e | RIOC_PRE_EMPTIVE) /* Set modem lines */ +#define RIOC_PCLOSE 0x0f /* Pseudo close - Leaves rx/tx enabled */ +#define RIOC_MGET (0x10 | RIOC_PRE_EMPTIVE) /* Force update of modem status */ +#define RIOC_MEMDUMP (0x11 | RIOC_PRE_EMPTIVE) /* Send back mem from addr supplied */ +#define RIOC_READ_REGISTER (0x12 | RIOC_PRE_EMPTIVE) /* Read CD1400 register (debug) */ /* "Command" packets going from remote to host COMPLETE and MODEM_STATUS use data[4] / data[3] to indicate current state and modem status respectively */ -#define COMPLETE (0x20 | PRE_EMPTIVE) +#define RIOC_COMPLETE (0x20 | RIOC_PRE_EMPTIVE) /* Command complete */ -#define BREAK_RECEIVED (0x21 | PRE_EMPTIVE) +#define RIOC_BREAK_RECEIVED (0x21 | RIOC_PRE_EMPTIVE) /* Break received */ -#define MODEM_STATUS (0x22 | PRE_EMPTIVE) +#define RIOC_MODEM_STATUS (0x22 | RIOC_PRE_EMPTIVE) /* Change in modem status */ /* "Command" packet that could go either way - handshake wake-up */ -#define HANDSHAKE (0x23 | PRE_EMPTIVE) +#define RIOC_HANDSHAKE (0x23 | RIOC_PRE_EMPTIVE) /* Wake-up to HOST / RTA */ #endif diff --git a/drivers/char/rio/rio_linux.c b/drivers/char/rio/rio_linux.c index 0ce96670f97..412777cd1e6 100644 --- a/drivers/char/rio/rio_linux.c +++ b/drivers/char/rio/rio_linux.c @@ -344,7 +344,7 @@ int rio_minor(struct tty_struct *tty) static int rio_set_real_termios(void *ptr) { - return RIOParam((struct Port *) ptr, CONFIG, 1, 1); + return RIOParam((struct Port *) ptr, RIOC_CONFIG, 1, 1); } @@ -487,7 +487,7 @@ static int rio_get_CD(void *ptr) int rv; func_enter(); - rv = (PortP->ModemState & MSVR1_CD) != 0; + rv = (PortP->ModemState & RIOC_MSVR1_CD) != 0; rio_dprintk(RIO_DEBUG_INIT, "Getting CD status: %d\n", rv); @@ -607,7 +607,8 @@ static int rio_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd rio_dprintk(RIO_DEBUG_TTY, "BREAK on deleted RTA\n"); rc = -EIO; } else { - if (RIOShortCommand(p, PortP, SBREAK, 2, 250) == RIO_FAIL) { + if (RIOShortCommand(p, PortP, RIOC_SBREAK, 2, 250) == + RIO_FAIL) { rio_dprintk(RIO_DEBUG_INTR, "SBREAK RIOShortCommand failed\n"); rc = -EIO; } @@ -622,7 +623,8 @@ static int rio_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd l = arg ? arg * 100 : 250; if (l > 255) l = 255; - if (RIOShortCommand(p, PortP, SBREAK, 2, arg ? arg * 100 : 250) == RIO_FAIL) { + if (RIOShortCommand(p, PortP, RIOC_SBREAK, 2, + arg ? arg * 100 : 250) == RIO_FAIL) { rio_dprintk(RIO_DEBUG_INTR, "SBREAK RIOShortCommand failed\n"); rc = -EIO; } diff --git a/drivers/char/rio/rio_linux.h b/drivers/char/rio/rio_linux.h index dc3f005614a..7f26cd7c815 100644 --- a/drivers/char/rio/rio_linux.h +++ b/drivers/char/rio/rio_linux.h @@ -186,9 +186,9 @@ static inline void *rio_memcpy_fromio(void *dest, void __iomem *source, int n) #ifdef DEBUG #define rio_dprintk(f, str...) do { if (rio_debug & f) printk (str);} while (0) -#define func_enter() rio_dprintk (RIO_DEBUG_FLOW, "rio: enter %s\n", __FUNCTION__) -#define func_exit() rio_dprintk (RIO_DEBUG_FLOW, "rio: exit %s\n", __FUNCTION__) -#define func_enter2() rio_dprintk (RIO_DEBUG_FLOW, "rio: enter %s (port %d)\n",__FUNCTION__, port->line) +#define func_enter() rio_dprintk (RIO_DEBUG_FLOW, "rio: enter %s\n", __func__) +#define func_exit() rio_dprintk (RIO_DEBUG_FLOW, "rio: exit %s\n", __func__) +#define func_enter2() rio_dprintk (RIO_DEBUG_FLOW, "rio: enter %s (port %d)\n",__func__, port->line) #else #define rio_dprintk(f, str...) /* nothing */ #define func_enter() diff --git a/drivers/char/rio/riocmd.c b/drivers/char/rio/riocmd.c index bf36959fc12..7b96e081488 100644 --- a/drivers/char/rio/riocmd.c +++ b/drivers/char/rio/riocmd.c @@ -417,7 +417,7 @@ static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struc PortP = p->RIOPortp[SysPort]; rio_spin_lock_irqsave(&PortP->portSem, flags); switch (readb(&PktCmdP->Command)) { - case BREAK_RECEIVED: + case RIOC_BREAK_RECEIVED: rio_dprintk(RIO_DEBUG_CMD, "Received a break!\n"); /* If the current line disc. is not multi-threading and the current processor is not the default, reset rup_intr @@ -428,16 +428,16 @@ static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struc gs_got_break(&PortP->gs); break; - case COMPLETE: + case RIOC_COMPLETE: rio_dprintk(RIO_DEBUG_CMD, "Command complete on phb %d host %Zd\n", readb(&PktCmdP->PhbNum), HostP - p->RIOHosts); subCommand = 1; switch (readb(&PktCmdP->SubCommand)) { - case MEMDUMP: + case RIOC_MEMDUMP: rio_dprintk(RIO_DEBUG_CMD, "Memory dump cmd (0x%x) from addr 0x%x\n", readb(&PktCmdP->SubCommand), readw(&PktCmdP->SubAddr)); break; - case READ_REGISTER: + case RIOC_READ_REGISTER: rio_dprintk(RIO_DEBUG_CMD, "Read register (0x%x)\n", readw(&PktCmdP->SubAddr)); - p->CdRegister = (readb(&PktCmdP->ModemStatus) & MSVR1_HOST); + p->CdRegister = (readb(&PktCmdP->ModemStatus) & RIOC_MSVR1_HOST); break; default: subCommand = 0; @@ -456,14 +456,15 @@ static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struc rio_dprintk(RIO_DEBUG_CMD, "No change\n"); /* FALLTHROUGH */ - case MODEM_STATUS: + case RIOC_MODEM_STATUS: /* ** Knock out the tbusy and tstop bits, as these are not relevant ** to the check for modem status change (they're just there because ** it's a convenient place to put them!). */ ReportedModemStatus = readb(&PktCmdP->ModemStatus); - if ((PortP->ModemState & MSVR1_HOST) == (ReportedModemStatus & MSVR1_HOST)) { + if ((PortP->ModemState & RIOC_MSVR1_HOST) == + (ReportedModemStatus & RIOC_MSVR1_HOST)) { rio_dprintk(RIO_DEBUG_CMD, "Modem status unchanged 0x%x\n", PortP->ModemState); /* ** Update ModemState just in case tbusy or tstop states have @@ -497,7 +498,7 @@ static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struc /* ** Is there a carrier? */ - if (PortP->ModemState & MSVR1_CD) { + if (PortP->ModemState & RIOC_MSVR1_CD) { /* ** Has carrier just appeared? */ @@ -691,7 +692,7 @@ void RIOPollHostCommands(struct rio_info *p, struct Host *HostP) */ rio_spin_unlock_irqrestore(&UnixRupP->RupLock, flags); FreeMe = RIOCommandRup(p, Rup, HostP, PacketP); - if (readb(&PacketP->data[5]) == MEMDUMP) { + if (readb(&PacketP->data[5]) == RIOC_MEMDUMP) { rio_dprintk(RIO_DEBUG_CMD, "Memdump from 0x%x complete\n", readw(&(PacketP->data[6]))); rio_memcpy_fromio(p->RIOMemDump, &(PacketP->data[8]), 32); } diff --git a/drivers/char/rio/rioctrl.c b/drivers/char/rio/rioctrl.c index d8eb2bcbe01..d65ceb9a434 100644 --- a/drivers/char/rio/rioctrl.c +++ b/drivers/char/rio/rioctrl.c @@ -422,7 +422,8 @@ int riocontrol(struct rio_info *p, dev_t dev, int cmd, unsigned long arg, int su } rio_spin_lock_irqsave(&PortP->portSem, flags); - if (RIOPreemptiveCmd(p, (p->RIOPortp[port]), RESUME) == RIO_FAIL) { + if (RIOPreemptiveCmd(p, (p->RIOPortp[port]), RIOC_RESUME) == + RIO_FAIL) { rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESUME failed\n"); rio_spin_unlock_irqrestore(&PortP->portSem, flags); return -EBUSY; @@ -636,7 +637,8 @@ int riocontrol(struct rio_info *p, dev_t dev, int cmd, unsigned long arg, int su return -ENXIO; } PortP = (p->RIOPortp[PortTty.port]); - RIOParam(PortP, CONFIG, PortP->State & RIO_MODEM, OK_TO_SLEEP); + RIOParam(PortP, RIOC_CONFIG, PortP->State & RIO_MODEM, + OK_TO_SLEEP); return retval; case RIO_SET_PORT_PARAMS: @@ -1247,7 +1249,7 @@ int riocontrol(struct rio_info *p, dev_t dev, int cmd, unsigned long arg, int su rio_spin_lock_irqsave(&PortP->portSem, flags); - if (RIOPreemptiveCmd(p, PortP, MEMDUMP) == RIO_FAIL) { + if (RIOPreemptiveCmd(p, PortP, RIOC_MEMDUMP) == RIO_FAIL) { rio_dprintk(RIO_DEBUG_CTRL, "RIO_MEM_DUMP failed\n"); rio_spin_unlock_irqrestore(&PortP->portSem, flags); return -EBUSY; @@ -1313,7 +1315,8 @@ int riocontrol(struct rio_info *p, dev_t dev, int cmd, unsigned long arg, int su rio_spin_lock_irqsave(&PortP->portSem, flags); - if (RIOPreemptiveCmd(p, PortP, READ_REGISTER) == RIO_FAIL) { + if (RIOPreemptiveCmd(p, PortP, RIOC_READ_REGISTER) == + RIO_FAIL) { rio_dprintk(RIO_DEBUG_CTRL, "RIO_READ_REGISTER failed\n"); rio_spin_unlock_irqrestore(&PortP->portSem, flags); return -EBUSY; @@ -1434,50 +1437,50 @@ int RIOPreemptiveCmd(struct rio_info *p, struct Port *PortP, u8 Cmd) PktCmdP->PhbNum = port; switch (Cmd) { - case MEMDUMP: + case RIOC_MEMDUMP: rio_dprintk(RIO_DEBUG_CTRL, "Queue MEMDUMP command blk %p " "(addr 0x%x)\n", CmdBlkP, (int) SubCmd.Addr); - PktCmdP->SubCommand = MEMDUMP; + PktCmdP->SubCommand = RIOC_MEMDUMP; PktCmdP->SubAddr = SubCmd.Addr; break; - case FCLOSE: + case RIOC_FCLOSE: rio_dprintk(RIO_DEBUG_CTRL, "Queue FCLOSE command blk %p\n", CmdBlkP); break; - case READ_REGISTER: + case RIOC_READ_REGISTER: rio_dprintk(RIO_DEBUG_CTRL, "Queue READ_REGISTER (0x%x) " "command blk %p\n", (int) SubCmd.Addr, CmdBlkP); - PktCmdP->SubCommand = READ_REGISTER; + PktCmdP->SubCommand = RIOC_READ_REGISTER; PktCmdP->SubAddr = SubCmd.Addr; break; - case RESUME: + case RIOC_RESUME: rio_dprintk(RIO_DEBUG_CTRL, "Queue RESUME command blk %p\n", CmdBlkP); break; - case RFLUSH: + case RIOC_RFLUSH: rio_dprintk(RIO_DEBUG_CTRL, "Queue RFLUSH command blk %p\n", CmdBlkP); CmdBlkP->PostFuncP = RIORFlushEnable; break; - case SUSPEND: + case RIOC_SUSPEND: rio_dprintk(RIO_DEBUG_CTRL, "Queue SUSPEND command blk %p\n", CmdBlkP); break; - case MGET: + case RIOC_MGET: rio_dprintk(RIO_DEBUG_CTRL, "Queue MGET command blk %p\n", CmdBlkP); break; - case MSET: - case MBIC: - case MBIS: + case RIOC_MSET: + case RIOC_MBIC: + case RIOC_MBIS: CmdBlkP->Packet.data[4] = (char) PortP->ModemLines; rio_dprintk(RIO_DEBUG_CTRL, "Queue MSET/MBIC/MBIS command " "blk %p\n", CmdBlkP); break; - case WFLUSH: + case RIOC_WFLUSH: /* ** If we have queued up the maximum number of Write flushes ** allowed then we should not bother sending any more to the diff --git a/drivers/char/rio/riointr.c b/drivers/char/rio/riointr.c index 4734e26e1cc..ea21686c69a 100644 --- a/drivers/char/rio/riointr.c +++ b/drivers/char/rio/riointr.c @@ -401,9 +401,8 @@ void RIOServiceHost(struct rio_info *p, struct Host *HostP) PortP->InUse = NOT_INUSE; rio_spin_unlock(&PortP->portSem); - if (RIOParam(PortP, OPEN, ((PortP->Cor2Copy & (COR2_RTSFLOW | COR2_CTSFLOW)) == (COR2_RTSFLOW | COR2_CTSFLOW)) ? 1 : 0, DONT_SLEEP) == RIO_FAIL) { + if (RIOParam(PortP, RIOC_OPEN, ((PortP->Cor2Copy & (RIOC_COR2_RTSFLOW | RIOC_COR2_CTSFLOW)) == (RIOC_COR2_RTSFLOW | RIOC_COR2_CTSFLOW)) ? 1 : 0, DONT_SLEEP) == RIO_FAIL) continue; /* with next port */ - } rio_spin_lock(&PortP->portSem); PortP->MagicFlags &= ~MAGIC_REBOOT; } @@ -429,7 +428,7 @@ void RIOServiceHost(struct rio_info *p, struct Host *HostP) */ PktCmdP = (struct PktCmd __iomem *) &PacketP->data[0]; - writeb(WFLUSH, &PktCmdP->Command); + writeb(RIOC_WFLUSH, &PktCmdP->Command); p = PortP->HostPort % (u16) PORTS_PER_RTA; diff --git a/drivers/char/rio/rioparam.c b/drivers/char/rio/rioparam.c index da276ed57b3..4810b845cc2 100644 --- a/drivers/char/rio/rioparam.c +++ b/drivers/char/rio/rioparam.c @@ -177,7 +177,7 @@ int RIOParam(struct Port *PortP, int cmd, int Modem, int SleepFlag) } rio_spin_lock_irqsave(&PortP->portSem, flags); - if (cmd == OPEN) { + if (cmd == RIOC_OPEN) { /* ** If the port is set to store or lock the parameters, and it is ** paramed with OPEN, we want to restore the saved port termio, but @@ -241,50 +241,50 @@ int RIOParam(struct Port *PortP, int cmd, int Modem, int SleepFlag) case CS5: { rio_dprintk(RIO_DEBUG_PARAM, "5 bit data\n"); - Cor1 |= COR1_5BITS; + Cor1 |= RIOC_COR1_5BITS; break; } case CS6: { rio_dprintk(RIO_DEBUG_PARAM, "6 bit data\n"); - Cor1 |= COR1_6BITS; + Cor1 |= RIOC_COR1_6BITS; break; } case CS7: { rio_dprintk(RIO_DEBUG_PARAM, "7 bit data\n"); - Cor1 |= COR1_7BITS; + Cor1 |= RIOC_COR1_7BITS; break; } case CS8: { rio_dprintk(RIO_DEBUG_PARAM, "8 bit data\n"); - Cor1 |= COR1_8BITS; + Cor1 |= RIOC_COR1_8BITS; break; } } if (TtyP->termios->c_cflag & CSTOPB) { rio_dprintk(RIO_DEBUG_PARAM, "2 stop bits\n"); - Cor1 |= COR1_2STOP; + Cor1 |= RIOC_COR1_2STOP; } else { rio_dprintk(RIO_DEBUG_PARAM, "1 stop bit\n"); - Cor1 |= COR1_1STOP; + Cor1 |= RIOC_COR1_1STOP; } if (TtyP->termios->c_cflag & PARENB) { rio_dprintk(RIO_DEBUG_PARAM, "Enable parity\n"); - Cor1 |= COR1_NORMAL; + Cor1 |= RIOC_COR1_NORMAL; } else { rio_dprintk(RIO_DEBUG_PARAM, "Disable parity\n"); - Cor1 |= COR1_NOP; + Cor1 |= RIOC_COR1_NOP; } if (TtyP->termios->c_cflag & PARODD) { rio_dprintk(RIO_DEBUG_PARAM, "Odd parity\n"); - Cor1 |= COR1_ODD; + Cor1 |= RIOC_COR1_ODD; } else { rio_dprintk(RIO_DEBUG_PARAM, "Even parity\n"); - Cor1 |= COR1_EVEN; + Cor1 |= RIOC_COR1_EVEN; } /* @@ -292,11 +292,11 @@ int RIOParam(struct Port *PortP, int cmd, int Modem, int SleepFlag) */ if (TtyP->termios->c_iflag & IXON) { rio_dprintk(RIO_DEBUG_PARAM, "Enable start/stop output control\n"); - Cor2 |= COR2_IXON; + Cor2 |= RIOC_COR2_IXON; } else { if (PortP->Config & RIO_IXON) { rio_dprintk(RIO_DEBUG_PARAM, "Force enable start/stop output control\n"); - Cor2 |= COR2_IXON; + Cor2 |= RIOC_COR2_IXON; } else rio_dprintk(RIO_DEBUG_PARAM, "IXON has been disabled.\n"); } @@ -304,29 +304,29 @@ int RIOParam(struct Port *PortP, int cmd, int Modem, int SleepFlag) if (TtyP->termios->c_iflag & IXANY) { if (PortP->Config & RIO_IXANY) { rio_dprintk(RIO_DEBUG_PARAM, "Enable any key to restart output\n"); - Cor2 |= COR2_IXANY; + Cor2 |= RIOC_COR2_IXANY; } else rio_dprintk(RIO_DEBUG_PARAM, "IXANY has been disabled due to sanity reasons.\n"); } if (TtyP->termios->c_iflag & IXOFF) { rio_dprintk(RIO_DEBUG_PARAM, "Enable start/stop input control 2\n"); - Cor2 |= COR2_IXOFF; + Cor2 |= RIOC_COR2_IXOFF; } if (TtyP->termios->c_cflag & HUPCL) { rio_dprintk(RIO_DEBUG_PARAM, "Hangup on last close\n"); - Cor2 |= COR2_HUPCL; + Cor2 |= RIOC_COR2_HUPCL; } if (C_CRTSCTS(TtyP)) { rio_dprintk(RIO_DEBUG_PARAM, "Rx hardware flow control enabled\n"); - Cor2 |= COR2_CTSFLOW; - Cor2 |= COR2_RTSFLOW; + Cor2 |= RIOC_COR2_CTSFLOW; + Cor2 |= RIOC_COR2_RTSFLOW; } else { rio_dprintk(RIO_DEBUG_PARAM, "Rx hardware flow control disabled\n"); - Cor2 &= ~COR2_CTSFLOW; - Cor2 &= ~COR2_RTSFLOW; + Cor2 &= ~RIOC_COR2_CTSFLOW; + Cor2 &= ~RIOC_COR2_RTSFLOW; } @@ -341,36 +341,36 @@ int RIOParam(struct Port *PortP, int cmd, int Modem, int SleepFlag) */ if (TtyP->termios->c_iflag & IGNBRK) { rio_dprintk(RIO_DEBUG_PARAM, "Ignore break condition\n"); - Cor4 |= COR4_IGNBRK; + Cor4 |= RIOC_COR4_IGNBRK; } if (!(TtyP->termios->c_iflag & BRKINT)) { rio_dprintk(RIO_DEBUG_PARAM, "Break generates NULL condition\n"); - Cor4 |= COR4_NBRKINT; + Cor4 |= RIOC_COR4_NBRKINT; } else { rio_dprintk(RIO_DEBUG_PARAM, "Interrupt on break condition\n"); } if (TtyP->termios->c_iflag & INLCR) { rio_dprintk(RIO_DEBUG_PARAM, "Map newline to carriage return on input\n"); - Cor4 |= COR4_INLCR; + Cor4 |= RIOC_COR4_INLCR; } if (TtyP->termios->c_iflag & IGNCR) { rio_dprintk(RIO_DEBUG_PARAM, "Ignore carriage return on input\n"); - Cor4 |= COR4_IGNCR; + Cor4 |= RIOC_COR4_IGNCR; } if (TtyP->termios->c_iflag & ICRNL) { rio_dprintk(RIO_DEBUG_PARAM, "Map carriage return to newline on input\n"); - Cor4 |= COR4_ICRNL; + Cor4 |= RIOC_COR4_ICRNL; } if (TtyP->termios->c_iflag & IGNPAR) { rio_dprintk(RIO_DEBUG_PARAM, "Ignore characters with parity errors\n"); - Cor4 |= COR4_IGNPAR; + Cor4 |= RIOC_COR4_IGNPAR; } if (TtyP->termios->c_iflag & PARMRK) { rio_dprintk(RIO_DEBUG_PARAM, "Mark parity errors\n"); - Cor4 |= COR4_PARMRK; + Cor4 |= RIOC_COR4_PARMRK; } /* @@ -378,22 +378,22 @@ int RIOParam(struct Port *PortP, int cmd, int Modem, int SleepFlag) ** on reception of a config packet. ** The download code handles the zero baud condition. */ - Cor4 |= COR4_RAISEMOD; + Cor4 |= RIOC_COR4_RAISEMOD; /* ** COR 5 */ - Cor5 = COR5_CMOE; + Cor5 = RIOC_COR5_CMOE; /* ** Set to monitor tbusy/tstop (or not). */ if (PortP->MonitorTstate) - Cor5 |= COR5_TSTATE_ON; + Cor5 |= RIOC_COR5_TSTATE_ON; else - Cor5 |= COR5_TSTATE_OFF; + Cor5 |= RIOC_COR5_TSTATE_OFF; /* ** Could set LNE here if you wanted LNext processing. SVR4 will use it. @@ -401,24 +401,24 @@ int RIOParam(struct Port *PortP, int cmd, int Modem, int SleepFlag) if (TtyP->termios->c_iflag & ISTRIP) { rio_dprintk(RIO_DEBUG_PARAM, "Strip input characters\n"); if (!(PortP->State & RIO_TRIAD_MODE)) { - Cor5 |= COR5_ISTRIP; + Cor5 |= RIOC_COR5_ISTRIP; } } if (TtyP->termios->c_oflag & ONLCR) { rio_dprintk(RIO_DEBUG_PARAM, "Map newline to carriage-return, newline on output\n"); if (PortP->CookMode == COOK_MEDIUM) - Cor5 |= COR5_ONLCR; + Cor5 |= RIOC_COR5_ONLCR; } if (TtyP->termios->c_oflag & OCRNL) { rio_dprintk(RIO_DEBUG_PARAM, "Map carriage return to newline on output\n"); if (PortP->CookMode == COOK_MEDIUM) - Cor5 |= COR5_OCRNL; + Cor5 |= RIOC_COR5_OCRNL; } if ((TtyP->termios->c_oflag & TABDLY) == TAB3) { rio_dprintk(RIO_DEBUG_PARAM, "Tab delay 3 set\n"); if (PortP->CookMode == COOK_MEDIUM) - Cor5 |= COR5_TAB3; + Cor5 |= RIOC_COR5_TAB3; } /* diff --git a/drivers/char/rio/riotty.c b/drivers/char/rio/riotty.c index 1cb8580a161..c99354843be 100644 --- a/drivers/char/rio/riotty.c +++ b/drivers/char/rio/riotty.c @@ -211,7 +211,7 @@ int riotopen(struct tty_struct *tty, struct file *filp) rio_dprintk(RIO_DEBUG_TTY, "Waiting for RIO_CLOSING to go away\n"); if (repeat_this-- <= 0) { rio_dprintk(RIO_DEBUG_TTY, "Waiting for not idle closed broken by signal\n"); - RIOPreemptiveCmd(p, PortP, FCLOSE); + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); retval = -EINTR; goto bombout; } @@ -264,7 +264,7 @@ int riotopen(struct tty_struct *tty, struct file *filp) here. If I read the docs correctly the "open" command piggybacks the parameters immediately. -- REW */ - RIOParam(PortP, OPEN, 1, OK_TO_SLEEP); /* Open the port */ + RIOParam(PortP, RIOC_OPEN, 1, OK_TO_SLEEP); /* Open the port */ rio_spin_lock_irqsave(&PortP->portSem, flags); /* @@ -275,7 +275,7 @@ int riotopen(struct tty_struct *tty, struct file *filp) rio_spin_unlock_irqrestore(&PortP->portSem, flags); if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) { rio_dprintk(RIO_DEBUG_TTY, "Waiting for open to finish broken by signal\n"); - RIOPreemptiveCmd(p, PortP, FCLOSE); + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); func_exit(); return -EINTR; } @@ -297,7 +297,8 @@ int riotopen(struct tty_struct *tty, struct file *filp) ** insert test for carrier here. -- ??? ** I already see that test here. What's the deal? -- REW */ - if ((PortP->gs.tty->termios->c_cflag & CLOCAL) || (PortP->ModemState & MSVR1_CD)) { + if ((PortP->gs.tty->termios->c_cflag & CLOCAL) || + (PortP->ModemState & RIOC_MSVR1_CD)) { rio_dprintk(RIO_DEBUG_TTY, "open(%d) Modem carr on\n", SysPort); /* tp->tm.c_state |= CARR_ON; @@ -325,7 +326,7 @@ int riotopen(struct tty_struct *tty, struct file *filp) ** I think it's OK. -- REW */ rio_dprintk(RIO_DEBUG_TTY, "open(%d) sleeping for carr broken by signal\n", SysPort); - RIOPreemptiveCmd(p, PortP, FCLOSE); + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); /* tp->tm.c_state &= ~WOPEN; */ @@ -416,7 +417,7 @@ int riotclose(void *ptr) */ PortP->State &= ~RIO_MOPEN; PortP->State &= ~RIO_CARR_ON; - PortP->ModemState &= ~MSVR1_CD; + PortP->ModemState &= ~RIOC_MSVR1_CD; /* ** If the device was open as both a Modem and a tty line ** then we need to wimp out here, as the port has not really @@ -453,7 +454,7 @@ int riotclose(void *ptr) if (repeat_this-- <= 0) { rv = -EINTR; rio_dprintk(RIO_DEBUG_TTY, "Waiting for not idle closed broken by signal\n"); - RIOPreemptiveCmd(p, PortP, FCLOSE); + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); goto close_end; } rio_dprintk(RIO_DEBUG_TTY, "Calling timeout to flush in closing\n"); @@ -492,8 +493,8 @@ int riotclose(void *ptr) /* Can't call RIOShortCommand with the port locked. */ rio_spin_unlock_irqrestore(&PortP->portSem, flags); - if (RIOShortCommand(p, PortP, CLOSE, 1, 0) == RIO_FAIL) { - RIOPreemptiveCmd(p, PortP, FCLOSE); + if (RIOShortCommand(p, PortP, RIOC_CLOSE, 1, 0) == RIO_FAIL) { + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); rio_spin_lock_irqsave(&PortP->portSem, flags); goto close_end; } @@ -503,7 +504,7 @@ int riotclose(void *ptr) try--; if (time_after(jiffies, end_time)) { rio_dprintk(RIO_DEBUG_TTY, "Run out of tries - force the bugger shut!\n"); - RIOPreemptiveCmd(p, PortP, FCLOSE); + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); break; } rio_dprintk(RIO_DEBUG_TTY, "Close: PortState:ISOPEN is %d\n", PortP->PortState & PORT_ISOPEN); @@ -515,14 +516,14 @@ int riotclose(void *ptr) } if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) { rio_dprintk(RIO_DEBUG_TTY, "RTA EINTR in delay \n"); - RIOPreemptiveCmd(p, PortP, FCLOSE); + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); break; } } rio_spin_lock_irqsave(&PortP->portSem, flags); rio_dprintk(RIO_DEBUG_TTY, "Close: try was %d on completion\n", try); - /* RIOPreemptiveCmd(p, PortP, FCLOSE); */ + /* RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); */ /* ** 15.10.1998 ARG - ESIL 0761 part fix diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 3f9d0a9ac36..f073c710ab8 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -4,9 +4,9 @@ * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) * * This code is loosely based on the Linux serial driver, written by - * Linus Torvalds, Theodore T'so and others. The RISCom/8 card - * programming info was obtained from various drivers for other OSes - * (FreeBSD, ISC, etc), but no source code from those drivers were + * Linus Torvalds, Theodore T'so and others. The RISCom/8 card + * programming info was obtained from various drivers for other OSes + * (FreeBSD, ISC, etc), but no source code from those drivers were * directly included in this driver. * * @@ -33,7 +33,7 @@ #include <linux/module.h> -#include <asm/io.h> +#include <linux/io.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/ioport.h> @@ -49,7 +49,7 @@ #include <linux/tty_flip.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include "riscom8.h" #include "riscom8_reg.h" @@ -57,15 +57,15 @@ /* Am I paranoid or not ? ;-) */ #define RISCOM_PARANOIA_CHECK -/* - * Crazy InteliCom/8 boards sometimes has swapped CTS & DSR signals. +/* + * Crazy InteliCom/8 boards sometimes have swapped CTS & DSR signals. * You can slightly speed up things by #undefing the following option, - * if you are REALLY sure that your board is correct one. + * if you are REALLY sure that your board is correct one. */ #define RISCOM_BRAIN_DAMAGED_CTS -/* +/* * The following defines are mostly for testing purposes. But if you need * some nice reporting in your syslog, you can define them also. */ @@ -112,7 +112,7 @@ static unsigned short rc_ioport[] = { #define RC_NIOPORT ARRAY_SIZE(rc_ioport) -static inline int rc_paranoia_check(struct riscom_port const * port, +static int rc_paranoia_check(struct riscom_port const *port, char *name, const char *routine) { #ifdef RISCOM_PARANOIA_CHECK @@ -134,52 +134,53 @@ static inline int rc_paranoia_check(struct riscom_port const * port, } /* - * + * * Service functions for RISCom/8 driver. - * + * */ /* Get board number from pointer */ -static inline int board_No (struct riscom_board const * bp) +static inline int board_No(struct riscom_board const *bp) { return bp - rc_board; } /* Get port number from pointer */ -static inline int port_No (struct riscom_port const * port) +static inline int port_No(struct riscom_port const *port) { - return RC_PORT(port - rc_port); + return RC_PORT(port - rc_port); } /* Get pointer to board from pointer to port */ -static inline struct riscom_board * port_Board(struct riscom_port const * port) +static inline struct riscom_board *port_Board(struct riscom_port const *port) { return &rc_board[RC_BOARD(port - rc_port)]; } /* Input Byte from CL CD180 register */ -static inline unsigned char rc_in(struct riscom_board const * bp, unsigned short reg) +static inline unsigned char rc_in(struct riscom_board const *bp, + unsigned short reg) { return inb(bp->base + RC_TO_ISA(reg)); } /* Output Byte to CL CD180 register */ -static inline void rc_out(struct riscom_board const * bp, unsigned short reg, +static inline void rc_out(struct riscom_board const *bp, unsigned short reg, unsigned char val) { outb(val, bp->base + RC_TO_ISA(reg)); } /* Wait for Channel Command Register ready */ -static inline void rc_wait_CCR(struct riscom_board const * bp) +static void rc_wait_CCR(struct riscom_board const *bp) { unsigned long delay; /* FIXME: need something more descriptive then 100000 :) */ - for (delay = 100000; delay; delay--) + for (delay = 100000; delay; delay--) if (!rc_in(bp, CD180_CCR)) return; - + printk(KERN_INFO "rc%d: Timeout waiting for CCR.\n", board_No(bp)); } @@ -187,11 +188,11 @@ static inline void rc_wait_CCR(struct riscom_board const * bp) * RISCom/8 probe functions. */ -static inline int rc_request_io_range(struct riscom_board * const bp) +static int rc_request_io_range(struct riscom_board * const bp) { int i; - - for (i = 0; i < RC_NIOPORT; i++) + + for (i = 0; i < RC_NIOPORT; i++) if (!request_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1, "RISCom/8")) { goto out_release; @@ -200,42 +201,42 @@ static inline int rc_request_io_range(struct riscom_board * const bp) out_release: printk(KERN_INFO "rc%d: Skipping probe at 0x%03x. IO address in use.\n", board_No(bp), bp->base); - while(--i >= 0) + while (--i >= 0) release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1); return 1; } -static inline void rc_release_io_range(struct riscom_board * const bp) +static void rc_release_io_range(struct riscom_board * const bp) { int i; - - for (i = 0; i < RC_NIOPORT; i++) + + for (i = 0; i < RC_NIOPORT; i++) release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1); } - + /* Reset and setup CD180 chip */ -static void __init rc_init_CD180(struct riscom_board const * bp) +static void __init rc_init_CD180(struct riscom_board const *bp) { unsigned long flags; - + spin_lock_irqsave(&riscom_lock, flags); - rc_out(bp, RC_CTOUT, 0); /* Clear timeout */ - rc_wait_CCR(bp); /* Wait for CCR ready */ - rc_out(bp, CD180_CCR, CCR_HARDRESET); /* Reset CD180 chip */ + rc_out(bp, RC_CTOUT, 0); /* Clear timeout */ + rc_wait_CCR(bp); /* Wait for CCR ready */ + rc_out(bp, CD180_CCR, CCR_HARDRESET); /* Reset CD180 chip */ spin_unlock_irqrestore(&riscom_lock, flags); - msleep(50); /* Delay 0.05 sec */ + msleep(50); /* Delay 0.05 sec */ spin_lock_irqsave(&riscom_lock, flags); - rc_out(bp, CD180_GIVR, RC_ID); /* Set ID for this chip */ - rc_out(bp, CD180_GICR, 0); /* Clear all bits */ - rc_out(bp, CD180_PILR1, RC_ACK_MINT); /* Prio for modem intr */ - rc_out(bp, CD180_PILR2, RC_ACK_TINT); /* Prio for transmitter intr */ - rc_out(bp, CD180_PILR3, RC_ACK_RINT); /* Prio for receiver intr */ - + rc_out(bp, CD180_GIVR, RC_ID); /* Set ID for this chip */ + rc_out(bp, CD180_GICR, 0); /* Clear all bits */ + rc_out(bp, CD180_PILR1, RC_ACK_MINT); /* Prio for modem intr */ + rc_out(bp, CD180_PILR2, RC_ACK_TINT); /* Prio for tx intr */ + rc_out(bp, CD180_PILR3, RC_ACK_RINT); /* Prio for rx intr */ + /* Setting up prescaler. We need 4 ticks per 1 ms */ rc_out(bp, CD180_PPRH, (RC_OSCFREQ/(1000000/RISCOM_TPS)) >> 8); rc_out(bp, CD180_PPRL, (RC_OSCFREQ/(1000000/RISCOM_TPS)) & 0xff); - + spin_unlock_irqrestore(&riscom_lock, flags); } @@ -245,12 +246,12 @@ static int __init rc_probe(struct riscom_board *bp) unsigned char val1, val2; int irqs = 0; int retries; - + bp->irq = 0; if (rc_request_io_range(bp)) return 1; - + /* Are the I/O ports here ? */ rc_out(bp, CD180_PPRL, 0x5a); outb(0xff, 0x80); @@ -258,34 +259,34 @@ static int __init rc_probe(struct riscom_board *bp) rc_out(bp, CD180_PPRL, 0xa5); outb(0x00, 0x80); val2 = rc_in(bp, CD180_PPRL); - + if ((val1 != 0x5a) || (val2 != 0xa5)) { printk(KERN_ERR "rc%d: RISCom/8 Board at 0x%03x not found.\n", board_No(bp), bp->base); goto out_release; } - + /* It's time to find IRQ for this board */ - for (retries = 0; retries < 5 && irqs <= 0; retries++) { + for (retries = 0; retries < 5 && irqs <= 0; retries++) { irqs = probe_irq_on(); - rc_init_CD180(bp); /* Reset CD180 chip */ - rc_out(bp, CD180_CAR, 2); /* Select port 2 */ + rc_init_CD180(bp); /* Reset CD180 chip */ + rc_out(bp, CD180_CAR, 2); /* Select port 2 */ rc_wait_CCR(bp); - rc_out(bp, CD180_CCR, CCR_TXEN); /* Enable transmitter */ - rc_out(bp, CD180_IER, IER_TXRDY); /* Enable tx empty intr */ + rc_out(bp, CD180_CCR, CCR_TXEN); /* Enable transmitter */ + rc_out(bp, CD180_IER, IER_TXRDY);/* Enable tx empty intr */ msleep(50); irqs = probe_irq_off(irqs); - val1 = rc_in(bp, RC_BSR); /* Get Board Status reg */ - val2 = rc_in(bp, RC_ACK_TINT); /* ACK interrupt */ - rc_init_CD180(bp); /* Reset CD180 again */ - + val1 = rc_in(bp, RC_BSR); /* Get Board Status reg */ + val2 = rc_in(bp, RC_ACK_TINT); /* ACK interrupt */ + rc_init_CD180(bp); /* Reset CD180 again */ + if ((val1 & RC_BSR_TINT) || (val2 != (RC_ID | GIVR_IT_TX))) { printk(KERN_ERR "rc%d: RISCom/8 Board at 0x%03x not " "found.\n", board_No(bp), bp->base); goto out_release; } } - + if (irqs <= 0) { printk(KERN_ERR "rc%d: Can't find IRQ for RISCom/8 board " "at 0x%03x.\n", board_No(bp), bp->base); @@ -293,113 +294,112 @@ static int __init rc_probe(struct riscom_board *bp) } bp->irq = irqs; bp->flags |= RC_BOARD_PRESENT; - + printk(KERN_INFO "rc%d: RISCom/8 Rev. %c board detected at " "0x%03x, IRQ %d.\n", board_No(bp), (rc_in(bp, CD180_GFRCR) & 0x0f) + 'A', /* Board revision */ bp->base, bp->irq); - + return 0; out_release: rc_release_io_range(bp); return 1; } -/* - * +/* + * * Interrupt processing routines. - * + * */ -static inline struct riscom_port * rc_get_port(struct riscom_board const * bp, - unsigned char const * what) +static struct riscom_port *rc_get_port(struct riscom_board const *bp, + unsigned char const *what) { unsigned char channel; - struct riscom_port * port; - + struct riscom_port *port; + channel = rc_in(bp, CD180_GICR) >> GICR_CHAN_OFF; if (channel < CD180_NCH) { port = &rc_port[board_No(bp) * RC_NPORT + channel]; - if (port->flags & ASYNC_INITIALIZED) { + if (port->flags & ASYNC_INITIALIZED) return port; - } } - printk(KERN_ERR "rc%d: %s interrupt from invalid port %d\n", + printk(KERN_ERR "rc%d: %s interrupt from invalid port %d\n", board_No(bp), what, channel); return NULL; } -static inline void rc_receive_exc(struct riscom_board const * bp) +static void rc_receive_exc(struct riscom_board const *bp) { struct riscom_port *port; struct tty_struct *tty; unsigned char status; unsigned char ch, flag; - - if (!(port = rc_get_port(bp, "Receive"))) + + port = rc_get_port(bp, "Receive"); + if (port == NULL) return; tty = port->tty; - -#ifdef RC_REPORT_OVERRUN + +#ifdef RC_REPORT_OVERRUN status = rc_in(bp, CD180_RCSR); if (status & RCSR_OE) port->overrun++; status &= port->mark_mask; -#else +#else status = rc_in(bp, CD180_RCSR) & port->mark_mask; -#endif +#endif ch = rc_in(bp, CD180_RDR); - if (!status) { + if (!status) return; - } if (status & RCSR_TOUT) { printk(KERN_WARNING "rc%d: port %d: Receiver timeout. " - "Hardware problems ?\n", + "Hardware problems ?\n", board_No(bp), port_No(port)); return; - + } else if (status & RCSR_BREAK) { printk(KERN_INFO "rc%d: port %d: Handling break...\n", board_No(bp), port_No(port)); flag = TTY_BREAK; if (port->flags & ASYNC_SAK) do_SAK(tty); - - } else if (status & RCSR_PE) + + } else if (status & RCSR_PE) flag = TTY_PARITY; - - else if (status & RCSR_FE) + + else if (status & RCSR_FE) flag = TTY_FRAME; - - else if (status & RCSR_OE) + + else if (status & RCSR_OE) flag = TTY_OVERRUN; - else flag = TTY_NORMAL; - + tty_insert_flip_char(tty, ch, flag); tty_flip_buffer_push(tty); } -static inline void rc_receive(struct riscom_board const * bp) +static void rc_receive(struct riscom_board const *bp) { struct riscom_port *port; struct tty_struct *tty; unsigned char count; - - if (!(port = rc_get_port(bp, "Receive"))) + + port = rc_get_port(bp, "Receive"); + if (port == NULL) return; - + tty = port->tty; - + count = rc_in(bp, CD180_RDCR); - + #ifdef RC_REPORT_FIFO port->hits[count > 8 ? 9 : count]++; -#endif - +#endif + while (count--) { if (tty_buffer_request_room(tty, 1) == 0) { printk(KERN_WARNING "rc%d: port %d: Working around " @@ -412,26 +412,26 @@ static inline void rc_receive(struct riscom_board const * bp) tty_flip_buffer_push(tty); } -static inline void rc_transmit(struct riscom_board const * bp) +static void rc_transmit(struct riscom_board const *bp) { struct riscom_port *port; struct tty_struct *tty; unsigned char count; - - - if (!(port = rc_get_port(bp, "Transmit"))) + + port = rc_get_port(bp, "Transmit"); + if (port == NULL) return; - + tty = port->tty; - - if (port->IER & IER_TXEMPTY) { + + if (port->IER & IER_TXEMPTY) { /* FIFO drained */ rc_out(bp, CD180_CAR, port_No(port)); port->IER &= ~IER_TXEMPTY; rc_out(bp, CD180_IER, port->IER); return; } - + if ((port->xmit_cnt <= 0 && !port->break_length) || tty->stopped || tty->hw_stopped) { rc_out(bp, CD180_CAR, port_No(port)); @@ -439,7 +439,7 @@ static inline void rc_transmit(struct riscom_board const * bp) rc_out(bp, CD180_IER, port->IER); return; } - + if (port->break_length) { if (port->break_length > 0) { if (port->COR2 & COR2_ETC) { @@ -451,7 +451,8 @@ static inline void rc_transmit(struct riscom_board const * bp) rc_out(bp, CD180_TDR, CD180_C_ESC); rc_out(bp, CD180_TDR, CD180_C_DELAY); rc_out(bp, CD180_TDR, count); - if (!(port->break_length -= count)) + port->break_length -= count; + if (port->break_length == 0) port->break_length--; } else { rc_out(bp, CD180_TDR, CD180_C_ESC); @@ -463,7 +464,7 @@ static inline void rc_transmit(struct riscom_board const * bp) } return; } - + count = CD180_NFIFO; do { rc_out(bp, CD180_TDR, port->xmit_buf[port->xmit_tail++]); @@ -471,7 +472,7 @@ static inline void rc_transmit(struct riscom_board const * bp) if (--port->xmit_cnt <= 0) break; } while (--count > 0); - + if (port->xmit_cnt <= 0) { rc_out(bp, CD180_CAR, port_No(port)); port->IER &= ~IER_TXRDY; @@ -481,25 +482,26 @@ static inline void rc_transmit(struct riscom_board const * bp) tty_wakeup(tty); } -static inline void rc_check_modem(struct riscom_board const * bp) +static void rc_check_modem(struct riscom_board const *bp) { struct riscom_port *port; struct tty_struct *tty; unsigned char mcr; - - if (!(port = rc_get_port(bp, "Modem"))) + + port = rc_get_port(bp, "Modem"); + if (port == NULL) return; - + tty = port->tty; - + mcr = rc_in(bp, CD180_MCR); - if (mcr & MCR_CDCHG) { - if (rc_in(bp, CD180_MSVR) & MSVR_CD) + if (mcr & MCR_CDCHG) { + if (rc_in(bp, CD180_MSVR) & MSVR_CD) wake_up_interruptible(&port->open_wait); else tty_hangup(tty); } - + #ifdef RISCOM_BRAIN_DAMAGED_CTS if (mcr & MCR_CTSCHG) { if (rc_in(bp, CD180_MSVR) & MSVR_CTS) { @@ -526,13 +528,13 @@ static inline void rc_check_modem(struct riscom_board const * bp) rc_out(bp, CD180_IER, port->IER); } #endif /* RISCOM_BRAIN_DAMAGED_CTS */ - + /* Clear change bits */ rc_out(bp, CD180_MCR, 0); } /* The main interrupt processing routine */ -static irqreturn_t rc_interrupt(int dummy, void * dev_id) +static irqreturn_t rc_interrupt(int dummy, void *dev_id) { unsigned char status; unsigned char ack; @@ -547,13 +549,11 @@ static irqreturn_t rc_interrupt(int dummy, void * dev_id) (RC_BSR_TOUT | RC_BSR_TINT | RC_BSR_MINT | RC_BSR_RINT))) { handled = 1; - if (status & RC_BSR_TOUT) + if (status & RC_BSR_TOUT) printk(KERN_WARNING "rc%d: Got timeout. Hardware " "error?\n", board_No(bp)); - else if (status & RC_BSR_RINT) { ack = rc_in(bp, RC_ACK_RINT); - if (ack == (RC_ID | GIVR_IT_RCV)) rc_receive(bp); else if (ack == (RC_ID | GIVR_IT_REXC)) @@ -562,29 +562,23 @@ static irqreturn_t rc_interrupt(int dummy, void * dev_id) printk(KERN_WARNING "rc%d: Bad receive ack " "0x%02x.\n", board_No(bp), ack); - } else if (status & RC_BSR_TINT) { ack = rc_in(bp, RC_ACK_TINT); - if (ack == (RC_ID | GIVR_IT_TX)) rc_transmit(bp); else printk(KERN_WARNING "rc%d: Bad transmit ack " "0x%02x.\n", board_No(bp), ack); - } else /* if (status & RC_BSR_MINT) */ { ack = rc_in(bp, RC_ACK_MINT); - - if (ack == (RC_ID | GIVR_IT_MODEM)) + if (ack == (RC_ID | GIVR_IT_MODEM)) rc_check_modem(bp); else printk(KERN_WARNING "rc%d: Bad modem ack " "0x%02x.\n", board_No(bp), ack); - - } - + } rc_out(bp, CD180_EOIR, 0); /* Mark end of interrupt */ rc_out(bp, RC_CTOUT, 0); /* Clear timeout flag */ } @@ -596,24 +590,24 @@ static irqreturn_t rc_interrupt(int dummy, void * dev_id) */ /* Called with disabled interrupts */ -static int rc_setup_board(struct riscom_board * bp) +static int rc_setup_board(struct riscom_board *bp) { int error; - if (bp->flags & RC_BOARD_ACTIVE) + if (bp->flags & RC_BOARD_ACTIVE) return 0; - + error = request_irq(bp->irq, rc_interrupt, IRQF_DISABLED, "RISCom/8", bp); - if (error) + if (error) return error; - + rc_out(bp, RC_CTOUT, 0); /* Just in case */ bp->DTR = ~0; rc_out(bp, RC_DTR, bp->DTR); /* Drop DTR on all ports */ - + bp->flags |= RC_BOARD_ACTIVE; - + return 0; } @@ -622,40 +616,40 @@ static void rc_shutdown_board(struct riscom_board *bp) { if (!(bp->flags & RC_BOARD_ACTIVE)) return; - + bp->flags &= ~RC_BOARD_ACTIVE; - + free_irq(bp->irq, NULL); - + bp->DTR = ~0; rc_out(bp, RC_DTR, bp->DTR); /* Drop DTR on all ports */ - + } /* - * Setting up port characteristics. + * Setting up port characteristics. * Must be called with disabled interrupts */ static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port) { - struct tty_struct *tty; + struct tty_struct *tty = port->tty; unsigned long baud; long tmp; unsigned char cor1 = 0, cor3 = 0; unsigned char mcor1 = 0, mcor2 = 0; - - if (!(tty = port->tty) || !tty->termios) + + if (tty == NULL || tty->termios == NULL) return; port->IER = 0; port->COR2 = 0; port->MSVR = MSVR_RTS; - + baud = tty_get_baud_rate(tty); - + /* Select port on the board */ rc_out(bp, CD180_CAR, port_No(port)); - + if (!baud) { /* Drop DTR & exit */ bp->DTR |= (1u << port_No(port)); @@ -666,69 +660,68 @@ static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port) bp->DTR &= ~(1u << port_No(port)); rc_out(bp, RC_DTR, bp->DTR); } - + /* - * Now we must calculate some speed depended things + * Now we must calculate some speed depended things */ - + /* Set baud rate for port */ tmp = (((RC_OSCFREQ + baud/2) / baud + CD180_TPC/2) / CD180_TPC); - rc_out(bp, CD180_RBPRH, (tmp >> 8) & 0xff); - rc_out(bp, CD180_TBPRH, (tmp >> 8) & 0xff); - rc_out(bp, CD180_RBPRL, tmp & 0xff); + rc_out(bp, CD180_RBPRH, (tmp >> 8) & 0xff); + rc_out(bp, CD180_TBPRH, (tmp >> 8) & 0xff); + rc_out(bp, CD180_RBPRL, tmp & 0xff); rc_out(bp, CD180_TBPRL, tmp & 0xff); - + baud = (baud + 5) / 10; /* Estimated CPS */ - + /* Two timer ticks seems enough to wakeup something like SLIP driver */ - tmp = ((baud + HZ/2) / HZ) * 2 - CD180_NFIFO; + tmp = ((baud + HZ/2) / HZ) * 2 - CD180_NFIFO; port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? SERIAL_XMIT_SIZE - 1 : tmp); - + /* Receiver timeout will be transmission time for 1.5 chars */ tmp = (RISCOM_TPS + RISCOM_TPS/2 + baud/2) / baud; tmp = (tmp > 0xff) ? 0xff : tmp; rc_out(bp, CD180_RTPR, tmp); - - switch (C_CSIZE(tty)) { - case CS5: + + switch (C_CSIZE(tty)) { + case CS5: cor1 |= COR1_5BITS; break; - case CS6: + case CS6: cor1 |= COR1_6BITS; break; - case CS7: + case CS7: cor1 |= COR1_7BITS; break; - case CS8: + case CS8: cor1 |= COR1_8BITS; break; } - - if (C_CSTOPB(tty)) + if (C_CSTOPB(tty)) cor1 |= COR1_2SB; - + cor1 |= COR1_IGNORE; - if (C_PARENB(tty)) { + if (C_PARENB(tty)) { cor1 |= COR1_NORMPAR; - if (C_PARODD(tty)) + if (C_PARODD(tty)) cor1 |= COR1_ODDP; - if (I_INPCK(tty)) + if (I_INPCK(tty)) cor1 &= ~COR1_IGNORE; } /* Set marking of some errors */ port->mark_mask = RCSR_OE | RCSR_TOUT; - if (I_INPCK(tty)) + if (I_INPCK(tty)) port->mark_mask |= RCSR_FE | RCSR_PE; - if (I_BRKINT(tty) || I_PARMRK(tty)) + if (I_BRKINT(tty) || I_PARMRK(tty)) port->mark_mask |= RCSR_BREAK; - if (I_IGNPAR(tty)) + if (I_IGNPAR(tty)) port->mark_mask &= ~(RCSR_FE | RCSR_PE); - if (I_IGNBRK(tty)) { + if (I_IGNBRK(tty)) { port->mark_mask &= ~RCSR_BREAK; - if (I_IGNPAR(tty)) + if (I_IGNPAR(tty)) /* Real raw mode. Ignore all */ port->mark_mask &= ~RCSR_OE; } @@ -738,7 +731,8 @@ static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port) port->IER |= IER_DSR | IER_CTS; mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; - tty->hw_stopped = !(rc_in(bp, CD180_MSVR) & (MSVR_CTS|MSVR_DSR)); + tty->hw_stopped = !(rc_in(bp, CD180_MSVR) & + (MSVR_CTS|MSVR_DSR)); #else port->COR2 |= COR2_CTSAE; #endif @@ -761,13 +755,13 @@ static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port) mcor1 |= MCOR1_CDZD; mcor2 |= MCOR2_CDOD; } - - if (C_CREAD(tty)) + + if (C_CREAD(tty)) /* Enable receiver */ port->IER |= IER_RXD; - + /* Set input FIFO size (1-8 bytes) */ - cor3 |= RISCOM_RXFIFO; + cor3 |= RISCOM_RXFIFO; /* Setting up CD180 channel registers */ rc_out(bp, CD180_COR1, cor1); rc_out(bp, CD180_COR2, port->COR2); @@ -791,36 +785,30 @@ static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port) static int rc_setup_port(struct riscom_board *bp, struct riscom_port *port) { unsigned long flags; - + if (port->flags & ASYNC_INITIALIZED) return 0; - + if (!port->xmit_buf) { /* We may sleep in get_zeroed_page() */ - unsigned long tmp; - - if (!(tmp = get_zeroed_page(GFP_KERNEL))) + unsigned long tmp = get_zeroed_page(GFP_KERNEL); + if (tmp == 0) return -ENOMEM; - - if (port->xmit_buf) { + if (port->xmit_buf) free_page(tmp); - return -ERESTARTSYS; - } - port->xmit_buf = (unsigned char *) tmp; + else + port->xmit_buf = (unsigned char *) tmp; } - spin_lock_irqsave(&riscom_lock, flags); - if (port->tty) + if (port->tty) clear_bit(TTY_IO_ERROR, &port->tty->flags); - - if (port->count == 1) + if (port->count == 1) bp->count++; - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; rc_change_speed(bp, port); port->flags |= ASYNC_INITIALIZED; - + spin_unlock_irqrestore(&riscom_lock, flags); return 0; } @@ -829,38 +817,39 @@ static int rc_setup_port(struct riscom_board *bp, struct riscom_port *port) static void rc_shutdown_port(struct riscom_board *bp, struct riscom_port *port) { struct tty_struct *tty; - - if (!(port->flags & ASYNC_INITIALIZED)) + + if (!(port->flags & ASYNC_INITIALIZED)) return; - + #ifdef RC_REPORT_OVERRUN printk(KERN_INFO "rc%d: port %d: Total %ld overruns were detected.\n", board_No(bp), port_No(port), port->overrun); -#endif +#endif #ifdef RC_REPORT_FIFO { int i; - + printk(KERN_INFO "rc%d: port %d: FIFO hits [ ", board_No(bp), port_No(port)); - for (i = 0; i < 10; i++) { + for (i = 0; i < 10; i++) printk("%ld ", port->hits[i]); - } printk("].\n"); } -#endif +#endif if (port->xmit_buf) { free_page((unsigned long) port->xmit_buf); port->xmit_buf = NULL; } - if (!(tty = port->tty) || C_HUPCL(tty)) { + tty = port->tty; + + if (tty == NULL || C_HUPCL(tty)) { /* Drop DTR */ bp->DTR |= (1u << port_No(port)); rc_out(bp, RC_DTR, bp->DTR); } - - /* Select port */ + + /* Select port */ rc_out(bp, CD180_CAR, port_No(port)); /* Reset port */ rc_wait_CCR(bp); @@ -868,28 +857,26 @@ static void rc_shutdown_port(struct riscom_board *bp, struct riscom_port *port) /* Disable all interrupts from this port */ port->IER = 0; rc_out(bp, CD180_IER, port->IER); - - if (tty) + + if (tty) set_bit(TTY_IO_ERROR, &tty->flags); port->flags &= ~ASYNC_INITIALIZED; - + if (--bp->count < 0) { printk(KERN_INFO "rc%d: rc_shutdown_port: " "bad board count: %d\n", board_No(bp), bp->count); bp->count = 0; } - /* * If this is the last opened port on the board * shutdown whole board */ - if (!bp->count) + if (!bp->count) rc_shutdown_board(bp); } - -static int block_til_ready(struct tty_struct *tty, struct file * filp, +static int block_til_ready(struct tty_struct *tty, struct file *filp, struct riscom_port *port) { DECLARE_WAITQUEUE(wait, current); @@ -921,7 +908,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, return 0; } - if (C_CLOCAL(tty)) + if (C_CLOCAL(tty)) do_clocal = 1; /* @@ -959,7 +946,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, if (port->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else - retval = -ERESTARTSYS; + retval = -ERESTARTSYS; break; } if (!(port->flags & ASYNC_CLOSING) && @@ -978,50 +965,63 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, port->blocked_open--; if (retval) return retval; - + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; -} +} -static int rc_open(struct tty_struct * tty, struct file * filp) +static int rc_open(struct tty_struct *tty, struct file *filp) { int board; int error; - struct riscom_port * port; - struct riscom_board * bp; - + struct riscom_port *port; + struct riscom_board *bp; + board = RC_BOARD(tty->index); if (board >= RC_NBOARD || !(rc_board[board].flags & RC_BOARD_PRESENT)) return -ENODEV; - + bp = &rc_board[board]; port = rc_port + board * RC_NPORT + RC_PORT(tty->index); if (rc_paranoia_check(port, tty->name, "rc_open")) return -ENODEV; - - if ((error = rc_setup_board(bp))) + + error = rc_setup_board(bp); + if (error) return error; - + port->count++; tty->driver_data = port; port->tty = tty; - - if ((error = rc_setup_port(bp, port))) - return error; - - if ((error = block_til_ready(tty, filp, port))) - return error; - - return 0; + + error = rc_setup_port(bp, port); + if (error == 0) + error = block_til_ready(tty, filp, port); + return error; } -static void rc_close(struct tty_struct * tty, struct file * filp) +static void rc_flush_buffer(struct tty_struct *tty) +{ + struct riscom_port *port = (struct riscom_port *)tty->driver_data; + unsigned long flags; + + if (rc_paranoia_check(port, tty->name, "rc_flush_buffer")) + return; + + spin_lock_irqsave(&riscom_lock, flags); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + spin_unlock_irqrestore(&riscom_lock, flags); + + tty_wakeup(tty); +} + +static void rc_close(struct tty_struct *tty, struct file *filp) { struct riscom_port *port = (struct riscom_port *) tty->driver_data; struct riscom_board *bp; unsigned long flags; unsigned long timeout; - + if (!port || rc_paranoia_check(port, tty->name, "close")) return; @@ -1029,7 +1029,7 @@ static void rc_close(struct tty_struct * tty, struct file * filp) if (tty_hung_up_p(filp)) goto out; - + bp = port_Board(port); if ((tty->count == 1) && (port->count != 1)) { printk(KERN_INFO "rc%d: rc_close: bad port count;" @@ -1047,7 +1047,7 @@ static void rc_close(struct tty_struct * tty, struct file * filp) goto out; port->flags |= ASYNC_CLOSING; /* - * Now we wait for the transmit buffer to clear; and we notify + * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; @@ -1070,24 +1070,22 @@ static void rc_close(struct tty_struct * tty, struct file * filp) * has completely drained; this is especially * important if there is a transmit FIFO! */ - timeout = jiffies+HZ; - while(port->IER & IER_TXEMPTY) { + timeout = jiffies + HZ; + while (port->IER & IER_TXEMPTY) { msleep_interruptible(jiffies_to_msecs(port->timeout)); if (time_after(jiffies, timeout)) break; } } rc_shutdown_port(bp, port); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + rc_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; port->tty = NULL; if (port->blocked_open) { - if (port->close_delay) { + if (port->close_delay) msleep_interruptible(jiffies_to_msecs(port->close_delay)); - } wake_up_interruptible(&port->open_wait); } port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); @@ -1097,17 +1095,17 @@ out: spin_unlock_irqrestore(&riscom_lock, flags); } -static int rc_write(struct tty_struct * tty, +static int rc_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct riscom_port *port = (struct riscom_port *)tty->driver_data; struct riscom_board *bp; int c, total = 0; unsigned long flags; - + if (rc_paranoia_check(port, tty->name, "rc_write")) return 0; - + bp = port_Board(port); if (!tty || !port->xmit_buf) @@ -1144,38 +1142,41 @@ static int rc_write(struct tty_struct * tty, return total; } -static void rc_put_char(struct tty_struct * tty, unsigned char ch) +static int rc_put_char(struct tty_struct *tty, unsigned char ch) { struct riscom_port *port = (struct riscom_port *)tty->driver_data; unsigned long flags; + int ret = 0; if (rc_paranoia_check(port, tty->name, "rc_put_char")) - return; + return 0; if (!tty || !port->xmit_buf) - return; + return 0; spin_lock_irqsave(&riscom_lock, flags); - + if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) goto out; port->xmit_buf[port->xmit_head++] = ch; port->xmit_head &= SERIAL_XMIT_SIZE - 1; port->xmit_cnt++; + ret = 1; out: spin_unlock_irqrestore(&riscom_lock, flags); + return ret; } -static void rc_flush_chars(struct tty_struct * tty) +static void rc_flush_chars(struct tty_struct *tty) { struct riscom_port *port = (struct riscom_port *)tty->driver_data; unsigned long flags; - + if (rc_paranoia_check(port, tty->name, "rc_flush_chars")) return; - + if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || !port->xmit_buf) return; @@ -1189,11 +1190,11 @@ static void rc_flush_chars(struct tty_struct * tty) spin_unlock_irqrestore(&riscom_lock, flags); } -static int rc_write_room(struct tty_struct * tty) +static int rc_write_room(struct tty_struct *tty) { struct riscom_port *port = (struct riscom_port *)tty->driver_data; int ret; - + if (rc_paranoia_check(port, tty->name, "rc_write_room")) return 0; @@ -1206,39 +1207,22 @@ static int rc_write_room(struct tty_struct * tty) static int rc_chars_in_buffer(struct tty_struct *tty) { struct riscom_port *port = (struct riscom_port *)tty->driver_data; - + if (rc_paranoia_check(port, tty->name, "rc_chars_in_buffer")) return 0; - - return port->xmit_cnt; -} - -static void rc_flush_buffer(struct tty_struct *tty) -{ - struct riscom_port *port = (struct riscom_port *)tty->driver_data; - unsigned long flags; - - if (rc_paranoia_check(port, tty->name, "rc_flush_buffer")) - return; - - spin_lock_irqsave(&riscom_lock, flags); - - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - spin_unlock_irqrestore(&riscom_lock, flags); - - tty_wakeup(tty); + return port->xmit_cnt; } static int rc_tiocmget(struct tty_struct *tty, struct file *file) { struct riscom_port *port = (struct riscom_port *)tty->driver_data; - struct riscom_board * bp; + struct riscom_board *bp; unsigned char status; unsigned int result; unsigned long flags; - if (rc_paranoia_check(port, tty->name, __FUNCTION__)) + if (rc_paranoia_check(port, tty->name, __func__)) return -ENODEV; bp = port_Board(port); @@ -1266,7 +1250,7 @@ static int rc_tiocmset(struct tty_struct *tty, struct file *file, unsigned long flags; struct riscom_board *bp; - if (rc_paranoia_check(port, tty->name, __FUNCTION__)) + if (rc_paranoia_check(port, tty->name, __func__)) return -ENODEV; bp = port_Board(port); @@ -1292,11 +1276,11 @@ static int rc_tiocmset(struct tty_struct *tty, struct file *file, return 0; } -static inline void rc_send_break(struct riscom_port * port, unsigned long length) +static void rc_send_break(struct riscom_port *port, unsigned long length) { struct riscom_board *bp = port_Board(port); unsigned long flags; - + spin_lock_irqsave(&riscom_lock, flags); port->break_length = RISCOM_TPS / HZ * length; @@ -1312,17 +1296,17 @@ static inline void rc_send_break(struct riscom_port * port, unsigned long length spin_unlock_irqrestore(&riscom_lock, flags); } -static inline int rc_set_serial_info(struct riscom_port * port, - struct serial_struct __user * newinfo) +static int rc_set_serial_info(struct riscom_port *port, + struct serial_struct __user *newinfo) { struct serial_struct tmp; struct riscom_board *bp = port_Board(port); int change_speed; - + if (copy_from_user(&tmp, newinfo, sizeof(tmp))) return -EFAULT; - -#if 0 + +#if 0 if ((tmp.irq != bp->irq) || (tmp.port != bp->base) || (tmp.type != PORT_CIRRUS) || @@ -1331,16 +1315,16 @@ static inline int rc_set_serial_info(struct riscom_port * port, (tmp.xmit_fifo_size != CD180_NFIFO) || (tmp.flags & ~RISCOM_LEGAL_FLAGS)) return -EINVAL; -#endif - +#endif + change_speed = ((port->flags & ASYNC_SPD_MASK) != (tmp.flags & ASYNC_SPD_MASK)); - + if (!capable(CAP_SYS_ADMIN)) { if ((tmp.close_delay != port->close_delay) || (tmp.closing_wait != port->closing_wait) || ((tmp.flags & ~ASYNC_USR_MASK) != - (port->flags & ~ASYNC_USR_MASK))) + (port->flags & ~ASYNC_USR_MASK))) return -EPERM; port->flags = ((port->flags & ~ASYNC_USR_MASK) | (tmp.flags & ASYNC_USR_MASK)); @@ -1360,12 +1344,12 @@ static inline int rc_set_serial_info(struct riscom_port * port, return 0; } -static inline int rc_get_serial_info(struct riscom_port * port, +static int rc_get_serial_info(struct riscom_port *port, struct serial_struct __user *retinfo) { struct serial_struct tmp; struct riscom_board *bp = port_Board(port); - + memset(&tmp, 0, sizeof(tmp)); tmp.type = PORT_CIRRUS; tmp.line = port - rc_port; @@ -1379,19 +1363,18 @@ static inline int rc_get_serial_info(struct riscom_port * port, return copy_to_user(retinfo, &tmp, sizeof(tmp)) ? -EFAULT : 0; } -static int rc_ioctl(struct tty_struct * tty, struct file * filp, +static int rc_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg) - { struct riscom_port *port = (struct riscom_port *)tty->driver_data; void __user *argp = (void __user *)arg; - int retval; - + int retval = 0; + if (rc_paranoia_check(port, tty->name, "rc_ioctl")) return -ENODEV; - + switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ + case TCSBRK: /* SVID version: non-zero arg --> no break */ retval = tty_check_change(tty); if (retval) return retval; @@ -1399,45 +1382,40 @@ static int rc_ioctl(struct tty_struct * tty, struct file * filp, if (!arg) rc_send_break(port, HZ/4); /* 1/4 second */ break; - case TCSBRKP: /* support for POSIX tcsendbreak() */ + case TCSBRKP: /* support for POSIX tcsendbreak() */ retval = tty_check_change(tty); if (retval) return retval; tty_wait_until_sent(tty, 0); rc_send_break(port, arg ? arg*(HZ/10) : HZ/4); break; - case TIOCGSOFTCAR: - return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned __user *)argp); - case TIOCSSOFTCAR: - if (get_user(arg,(unsigned __user *) argp)) - return -EFAULT; - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); + case TIOCGSERIAL: + lock_kernel(); + retval = rc_get_serial_info(port, argp); + unlock_kernel(); break; - case TIOCGSERIAL: - return rc_get_serial_info(port, argp); - case TIOCSSERIAL: - return rc_set_serial_info(port, argp); - default: - return -ENOIOCTLCMD; + case TIOCSSERIAL: + lock_kernel(); + retval = rc_set_serial_info(port, argp); + unlock_kernel(); + break; + default: + retval = -ENOIOCTLCMD; } - return 0; + return retval; } -static void rc_throttle(struct tty_struct * tty) +static void rc_throttle(struct tty_struct *tty) { struct riscom_port *port = (struct riscom_port *)tty->driver_data; struct riscom_board *bp; unsigned long flags; - + if (rc_paranoia_check(port, tty->name, "rc_throttle")) return; - bp = port_Board(port); spin_lock_irqsave(&riscom_lock, flags); - port->MSVR &= ~MSVR_RTS; rc_out(bp, CD180_CAR, port_No(port)); if (I_IXOFF(tty)) { @@ -1446,23 +1424,20 @@ static void rc_throttle(struct tty_struct * tty) rc_wait_CCR(bp); } rc_out(bp, CD180_MSVR, port->MSVR); - spin_unlock_irqrestore(&riscom_lock, flags); } -static void rc_unthrottle(struct tty_struct * tty) +static void rc_unthrottle(struct tty_struct *tty) { struct riscom_port *port = (struct riscom_port *)tty->driver_data; struct riscom_board *bp; unsigned long flags; - + if (rc_paranoia_check(port, tty->name, "rc_unthrottle")) return; - bp = port_Board(port); - - spin_lock_irqsave(&riscom_lock, flags); + spin_lock_irqsave(&riscom_lock, flags); port->MSVR |= MSVR_RTS; rc_out(bp, CD180_CAR, port_No(port)); if (I_IXOFF(tty)) { @@ -1471,62 +1446,58 @@ static void rc_unthrottle(struct tty_struct * tty) rc_wait_CCR(bp); } rc_out(bp, CD180_MSVR, port->MSVR); - spin_unlock_irqrestore(&riscom_lock, flags); } -static void rc_stop(struct tty_struct * tty) +static void rc_stop(struct tty_struct *tty) { struct riscom_port *port = (struct riscom_port *)tty->driver_data; struct riscom_board *bp; unsigned long flags; - + if (rc_paranoia_check(port, tty->name, "rc_stop")) return; - + bp = port_Board(port); - - spin_lock_irqsave(&riscom_lock, flags); + spin_lock_irqsave(&riscom_lock, flags); port->IER &= ~IER_TXRDY; rc_out(bp, CD180_CAR, port_No(port)); rc_out(bp, CD180_IER, port->IER); - spin_unlock_irqrestore(&riscom_lock, flags); } -static void rc_start(struct tty_struct * tty) +static void rc_start(struct tty_struct *tty) { struct riscom_port *port = (struct riscom_port *)tty->driver_data; struct riscom_board *bp; unsigned long flags; - + if (rc_paranoia_check(port, tty->name, "rc_start")) return; - + bp = port_Board(port); - + spin_lock_irqsave(&riscom_lock, flags); - if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) { + if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) { port->IER |= IER_TXRDY; rc_out(bp, CD180_CAR, port_No(port)); rc_out(bp, CD180_IER, port->IER); } - spin_unlock_irqrestore(&riscom_lock, flags); } -static void rc_hangup(struct tty_struct * tty) +static void rc_hangup(struct tty_struct *tty) { struct riscom_port *port = (struct riscom_port *)tty->driver_data; struct riscom_board *bp; - + if (rc_paranoia_check(port, tty->name, "rc_hangup")) return; - + bp = port_Board(port); - + rc_shutdown_port(bp, port); port->count = 0; port->flags &= ~ASYNC_NORMAL_ACTIVE; @@ -1534,17 +1505,14 @@ static void rc_hangup(struct tty_struct * tty) wake_up_interruptible(&port->open_wait); } -static void rc_set_termios(struct tty_struct * tty, struct ktermios * old_termios) +static void rc_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) { struct riscom_port *port = (struct riscom_port *)tty->driver_data; unsigned long flags; - + if (rc_paranoia_check(port, tty->name, "rc_set_termios")) return; - - if (tty->termios->c_cflag == old_termios->c_cflag && - tty->termios->c_iflag == old_termios->c_iflag) - return; spin_lock_irqsave(&riscom_lock, flags); rc_change_speed(port_Board(port), port); @@ -1583,9 +1551,9 @@ static int __init rc_init_drivers(void) int i; riscom_driver = alloc_tty_driver(RC_NBOARD * RC_NPORT); - if (!riscom_driver) + if (!riscom_driver) return -ENOMEM; - + riscom_driver->owner = THIS_MODULE; riscom_driver->name = "ttyL"; riscom_driver->major = RISCOM8_NORMAL_MAJOR; @@ -1598,23 +1566,21 @@ static int __init rc_init_drivers(void) riscom_driver->init_termios.c_ospeed = 9600; riscom_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(riscom_driver, &riscom_ops); - if ((error = tty_register_driver(riscom_driver))) { + error = tty_register_driver(riscom_driver); + if (error != 0) { put_tty_driver(riscom_driver); printk(KERN_ERR "rc: Couldn't register RISCom/8 driver, " - "error = %d\n", - error); + "error = %d\n", error); return 1; } - memset(rc_port, 0, sizeof(rc_port)); for (i = 0; i < RC_NPORT * RC_NBOARD; i++) { rc_port[i].magic = RISCOM8_MAGIC; - rc_port[i].close_delay = 50 * HZ/100; - rc_port[i].closing_wait = 3000 * HZ/100; + rc_port[i].close_delay = 50 * HZ / 100; + rc_port[i].closing_wait = 3000 * HZ / 100; init_waitqueue_head(&rc_port[i].open_wait); init_waitqueue_head(&rc_port[i].close_wait); } - return 0; } @@ -1627,13 +1593,13 @@ static void rc_release_drivers(void) #ifndef MODULE /* * Called at boot time. - * + * * You can specify IO base for up to RC_NBOARD cards, * using line "riscom8=0xiobase1,0xiobase2,.." at LILO prompt. * Note that there will be no probing at default * addresses in this case. * - */ + */ static int __init riscom8_setup(char *str) { int ints[RC_NBOARD]; @@ -1644,7 +1610,7 @@ static int __init riscom8_setup(char *str) for (i = 0; i < RC_NBOARD; i++) { if (i < ints[0]) rc_board[i].base = ints[i+1]; - else + else rc_board[i].base = 0; } return 1; @@ -1659,8 +1625,8 @@ static char banner[] __initdata = static char no_boards_msg[] __initdata = KERN_INFO "rc: No RISCom/8 boards detected.\n"; -/* - * This routine must be called by kernel at boot time +/* + * This routine must be called by kernel at boot time */ static int __init riscom8_init(void) { @@ -1669,13 +1635,12 @@ static int __init riscom8_init(void) printk(banner); - if (rc_init_drivers()) + if (rc_init_drivers()) return -EIO; - for (i = 0; i < RC_NBOARD; i++) - if (rc_board[i].base && !rc_probe(&rc_board[i])) + for (i = 0; i < RC_NBOARD; i++) + if (rc_board[i].base && !rc_probe(&rc_board[i])) found++; - if (!found) { rc_release_drivers(); printk(no_boards_msg); @@ -1702,13 +1667,13 @@ MODULE_LICENSE("GPL"); * by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter. * */ -static int __init riscom8_init_module (void) +static int __init riscom8_init_module(void) { #ifdef MODULE int i; if (iobase || iobase1 || iobase2 || iobase3) { - for(i = 0; i < RC_NBOARD; i++) + for (i = 0; i < RC_NBOARD; i++) rc_board[i].base = 0; } @@ -1724,18 +1689,17 @@ static int __init riscom8_init_module (void) return riscom8_init(); } - -static void __exit riscom8_exit_module (void) + +static void __exit riscom8_exit_module(void) { int i; - + rc_release_drivers(); - for (i = 0; i < RC_NBOARD; i++) - if (rc_board[i].flags & RC_BOARD_PRESENT) + for (i = 0; i < RC_NBOARD; i++) + if (rc_board[i].flags & RC_BOARD_PRESENT) rc_release_io_range(&rc_board[i]); - + } module_init(riscom8_init_module); module_exit(riscom8_exit_module); - diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index f585bc8579e..743dc80a932 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -449,7 +449,8 @@ static void rp_do_transmit(struct r_port *info) while (1) { if (tty->stopped || tty->hw_stopped) break; - c = min(info->xmit_fifo_room, min(info->xmit_cnt, XMIT_BUF_SIZE - info->xmit_tail)); + c = min(info->xmit_fifo_room, info->xmit_cnt); + c = min(c, XMIT_BUF_SIZE - info->xmit_tail); if (c <= 0 || info->xmit_fifo_room <= 0) break; sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) (info->xmit_buf + info->xmit_tail), c / 2); @@ -1433,29 +1434,38 @@ static int rp_ioctl(struct tty_struct *tty, struct file *file, { struct r_port *info = (struct r_port *) tty->driver_data; void __user *argp = (void __user *)arg; + int ret = 0; if (cmd != RCKP_GET_PORTS && rocket_paranoia_check(info, "rp_ioctl")) return -ENXIO; + lock_kernel(); + switch (cmd) { case RCKP_GET_STRUCT: if (copy_to_user(argp, info, sizeof (struct r_port))) - return -EFAULT; - return 0; + ret = -EFAULT; + break; case RCKP_GET_CONFIG: - return get_config(info, argp); + ret = get_config(info, argp); + break; case RCKP_SET_CONFIG: - return set_config(info, argp); + ret = set_config(info, argp); + break; case RCKP_GET_PORTS: - return get_ports(info, argp); + ret = get_ports(info, argp); + break; case RCKP_RESET_RM2: - return reset_rm2(info, argp); + ret = reset_rm2(info, argp); + break; case RCKP_GET_VERSION: - return get_version(info, argp); + ret = get_version(info, argp); + break; default: - return -ENOIOCTLCMD; + ret = -ENOIOCTLCMD; } - return 0; + unlock_kernel(); + return ret; } static void rp_send_xchar(struct tty_struct *tty, char ch) @@ -1575,6 +1585,7 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout) jiffies); printk(KERN_INFO "cps=%d...\n", info->cps); #endif + lock_kernel(); while (1) { txcnt = sGetTxCnt(cp); if (!txcnt) { @@ -1602,6 +1613,7 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout) break; } __set_current_state(TASK_RUNNING); + unlock_kernel(); #ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT printk(KERN_INFO "txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies); #endif @@ -1651,14 +1663,14 @@ static void rp_hangup(struct tty_struct *tty) * writing routines will write directly to transmit FIFO. * Write buffer and counters protected by spinlocks */ -static void rp_put_char(struct tty_struct *tty, unsigned char ch) +static int rp_put_char(struct tty_struct *tty, unsigned char ch) { struct r_port *info = (struct r_port *) tty->driver_data; CHANNEL_t *cp; unsigned long flags; if (rocket_paranoia_check(info, "rp_put_char")) - return; + return 0; /* * Grab the port write mutex, locking out other processes that try to @@ -1687,6 +1699,7 @@ static void rp_put_char(struct tty_struct *tty, unsigned char ch) } spin_unlock_irqrestore(&info->slock, flags); mutex_unlock(&info->write_mtx); + return 1; } /* @@ -1749,10 +1762,10 @@ static int rp_write(struct tty_struct *tty, /* Write remaining data into the port's xmit_buf */ while (1) { - if (!info->tty) /* Seemingly obligatory check... */ + if (!info->tty) /* Seemingly obligatory check... */ goto end; - - c = min(count, min(XMIT_BUF_SIZE - info->xmit_cnt - 1, XMIT_BUF_SIZE - info->xmit_head)); + c = min(count, XMIT_BUF_SIZE - info->xmit_cnt - 1); + c = min(c, XMIT_BUF_SIZE - info->xmit_head); if (c <= 0) break; diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index df8cd0ca97e..fd2db07a50f 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -1060,7 +1060,7 @@ static void config_setup(struct cyclades_port *info) } /* config_setup */ -static void cy_put_char(struct tty_struct *tty, unsigned char ch) +static int cy_put_char(struct tty_struct *tty, unsigned char ch) { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; unsigned long flags; @@ -1070,7 +1070,7 @@ static void cy_put_char(struct tty_struct *tty, unsigned char ch) #endif if (serial_paranoia_check(info, tty->name, "cy_put_char")) - return; + return 0; if (!info->xmit_buf) return; @@ -1078,13 +1078,14 @@ static void cy_put_char(struct tty_struct *tty, unsigned char ch) local_irq_save(flags); if (info->xmit_cnt >= PAGE_SIZE - 1) { local_irq_restore(flags); - return; + return 0; } info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= PAGE_SIZE - 1; info->xmit_cnt++; local_irq_restore(flags); + return 1; } /* cy_put_char */ static void cy_flush_chars(struct tty_struct *tty) @@ -1539,6 +1540,8 @@ cy_ioctl(struct tty_struct *tty, struct file *file, printk("cy_ioctl %s, cmd = %x arg = %lx\n", tty->name, cmd, arg); /* */ #endif + lock_kernel(); + switch (cmd) { case CYGETMON: ret_val = get_mon_info(info, argp); @@ -1584,18 +1587,6 @@ cy_ioctl(struct tty_struct *tty, struct file *file, break; /* The following commands are incompletely implemented!!! */ - case TIOCGSOFTCAR: - ret_val = - put_user(C_CLOCAL(tty) ? 1 : 0, - (unsigned long __user *)argp); - break; - case TIOCSSOFTCAR: - ret_val = get_user(val, (unsigned long __user *)argp); - if (ret_val) - break; - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | (val ? CLOCAL : 0)); - break; case TIOCGSERIAL: ret_val = get_serial_info(info, argp); break; @@ -1605,6 +1596,7 @@ cy_ioctl(struct tty_struct *tty, struct file *file, default: ret_val = -ENOIOCTLCMD; } + unlock_kernel(); #ifdef SERIAL_DEBUG_OTHER printk("cy_ioctl done\n"); @@ -1683,8 +1675,7 @@ static void cy_close(struct tty_struct *tty, struct file *filp) if (info->flags & ASYNC_INITIALIZED) tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ shutdown(info); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + cy_flush_buffer(tty); tty_ldisc_flush(tty); info->tty = NULL; if (info->blocked_open) { diff --git a/drivers/char/snsc.c b/drivers/char/snsc.c index b9c1dba6bd0..8fe099a4106 100644 --- a/drivers/char/snsc.c +++ b/drivers/char/snsc.c @@ -80,7 +80,7 @@ scdrv_open(struct inode *inode, struct file *file) sd = kzalloc(sizeof (struct subch_data_s), GFP_KERNEL); if (sd == NULL) { printk("%s: couldn't allocate subchannel data\n", - __FUNCTION__); + __func__); return -ENOMEM; } @@ -90,7 +90,7 @@ scdrv_open(struct inode *inode, struct file *file) if (sd->sd_subch < 0) { kfree(sd); - printk("%s: couldn't allocate subchannel\n", __FUNCTION__); + printk("%s: couldn't allocate subchannel\n", __func__); return -EBUSY; } @@ -110,7 +110,7 @@ scdrv_open(struct inode *inode, struct file *file) if (rv) { ia64_sn_irtr_close(sd->sd_nasid, sd->sd_subch); kfree(sd); - printk("%s: irq request failed (%d)\n", __FUNCTION__, rv); + printk("%s: irq request failed (%d)\n", __func__, rv); return -EBUSY; } @@ -215,7 +215,7 @@ scdrv_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos) */ if (count < len) { pr_debug("%s: only accepting %d of %d bytes\n", - __FUNCTION__, (int) count, len); + __func__, (int) count, len); } len = min((int) count, len); if (copy_to_user(buf, sd->sd_rb, len)) @@ -384,7 +384,7 @@ scdrv_init(void) if (alloc_chrdev_region(&first_dev, 0, num_cnodes, SYSCTL_BASENAME) < 0) { printk("%s: failed to register SN system controller device\n", - __FUNCTION__); + __func__); return -ENODEV; } snsc_class = class_create(THIS_MODULE, SYSCTL_BASENAME); @@ -403,7 +403,7 @@ scdrv_init(void) GFP_KERNEL); if (!scd) { printk("%s: failed to allocate device info" - "for %s/%s\n", __FUNCTION__, + "for %s/%s\n", __func__, SYSCTL_BASENAME, devname); continue; } @@ -412,7 +412,7 @@ scdrv_init(void) scd->scd_nasid = cnodeid_to_nasid(cnode); if (!(salbuf = kmalloc(SCDRV_BUFSZ, GFP_KERNEL))) { printk("%s: failed to allocate driver buffer" - "(%s%s)\n", __FUNCTION__, + "(%s%s)\n", __func__, SYSCTL_BASENAME, devname); kfree(scd); continue; @@ -424,7 +424,7 @@ scdrv_init(void) ("%s: failed to initialize SAL for" " system controller communication" " (%s/%s): outdated PROM?\n", - __FUNCTION__, SYSCTL_BASENAME, devname); + __func__, SYSCTL_BASENAME, devname); kfree(scd); kfree(salbuf); continue; @@ -435,7 +435,7 @@ scdrv_init(void) if (cdev_add(&scd->scd_cdev, dev, 1)) { printk("%s: failed to register system" " controller device (%s%s)\n", - __FUNCTION__, SYSCTL_BASENAME, devname); + __func__, SYSCTL_BASENAME, devname); kfree(scd); kfree(salbuf); continue; diff --git a/drivers/char/snsc_event.c b/drivers/char/snsc_event.c index 31a7765eaf7..53b3d44f8c0 100644 --- a/drivers/char/snsc_event.c +++ b/drivers/char/snsc_event.c @@ -271,7 +271,7 @@ scdrv_event_init(struct sysctl_data_s *scd) event_sd = kzalloc(sizeof (struct subch_data_s), GFP_KERNEL); if (event_sd == NULL) { printk(KERN_WARNING "%s: couldn't allocate subchannel info" - " for event monitoring\n", __FUNCTION__); + " for event monitoring\n", __func__); return; } @@ -285,7 +285,7 @@ scdrv_event_init(struct sysctl_data_s *scd) if (event_sd->sd_subch < 0) { kfree(event_sd); printk(KERN_WARNING "%s: couldn't open event subchannel\n", - __FUNCTION__); + __func__); return; } @@ -295,7 +295,7 @@ scdrv_event_init(struct sysctl_data_s *scd) "system controller events", event_sd); if (rv) { printk(KERN_WARNING "%s: irq request failed (%d)\n", - __FUNCTION__, rv); + __func__, rv); ia64_sn_irtr_close(event_sd->sd_nasid, event_sd->sd_subch); kfree(event_sd); return; diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index c03ad164c39..58533de5902 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -506,7 +506,7 @@ static struct sonypi_device { while (--n && (command)) \ udelay(1); \ if (!n && (verbose || !quiet)) \ - printk(KERN_WARNING "sonypi command failed at %s : %s (line %d)\n", __FILE__, __FUNCTION__, __LINE__); \ + printk(KERN_WARNING "sonypi command failed at %s : %s (line %d)\n", __FILE__, __func__, __LINE__); \ } #ifdef CONFIG_ACPI diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index 4b5b5b78acb..2ee4d989375 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -131,8 +131,8 @@ static int sx_rxfifo = SPECIALIX_RXFIFO; #define SX_DEBUG_FIFO 0x0800 -#define func_enter() dprintk (SX_DEBUG_FLOW, "io8: enter %s\n",__FUNCTION__) -#define func_exit() dprintk (SX_DEBUG_FLOW, "io8: exit %s\n", __FUNCTION__) +#define func_enter() dprintk (SX_DEBUG_FLOW, "io8: enter %s\n",__func__) +#define func_exit() dprintk (SX_DEBUG_FLOW, "io8: exit %s\n", __func__) #define jiffies_from_ms(a) ((((a) * HZ)/1000)+1) @@ -874,7 +874,7 @@ static irqreturn_t sx_interrupt(int dummy, void *dev_id) spin_lock_irqsave(&bp->lock, flags); - dprintk (SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __FUNCTION__, port_No(sx_get_port(bp, "INT")), SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1); + dprintk (SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __func__, port_No(sx_get_port(bp, "INT")), SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1); if (!(bp->flags & SX_BOARD_ACTIVE)) { dprintk (SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", bp->irq); spin_unlock_irqrestore(&bp->lock, flags); @@ -1504,6 +1504,27 @@ static int sx_open(struct tty_struct * tty, struct file * filp) return 0; } +static void sx_flush_buffer(struct tty_struct *tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + struct specialix_board * bp; + + func_enter(); + + if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) { + func_exit(); + return; + } + + bp = port_Board(port); + spin_lock_irqsave(&port->lock, flags); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + spin_unlock_irqrestore(&port->lock, flags); + tty_wakeup(tty); + + func_exit(); +} static void sx_close(struct tty_struct * tty, struct file * filp) { @@ -1597,8 +1618,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp) } sx_shutdown_port(bp, port); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + sx_flush_buffer(tty); tty_ldisc_flush(tty); spin_lock_irqsave(&port->lock, flags); tty->closing = 0; @@ -1670,7 +1690,7 @@ static int sx_write(struct tty_struct * tty, } -static void sx_put_char(struct tty_struct * tty, unsigned char ch) +static int sx_put_char(struct tty_struct * tty, unsigned char ch) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; unsigned long flags; @@ -1680,12 +1700,12 @@ static void sx_put_char(struct tty_struct * tty, unsigned char ch) if (sx_paranoia_check(port, tty->name, "sx_put_char")) { func_exit(); - return; + return 0; } dprintk (SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf); if (!port->xmit_buf) { func_exit(); - return; + return 0; } bp = port_Board(port); spin_lock_irqsave(&port->lock, flags); @@ -1695,7 +1715,7 @@ static void sx_put_char(struct tty_struct * tty, unsigned char ch) spin_unlock_irqrestore(&port->lock, flags); dprintk (SX_DEBUG_TX, "Exit size\n"); func_exit(); - return; + return 0; } dprintk (SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf); port->xmit_buf[port->xmit_head++] = ch; @@ -1704,6 +1724,7 @@ static void sx_put_char(struct tty_struct * tty, unsigned char ch) spin_unlock_irqrestore(&port->lock, flags); func_exit(); + return 1; } @@ -1770,28 +1791,6 @@ static int sx_chars_in_buffer(struct tty_struct *tty) } -static void sx_flush_buffer(struct tty_struct *tty) -{ - struct specialix_port *port = (struct specialix_port *)tty->driver_data; - unsigned long flags; - struct specialix_board * bp; - - func_enter(); - - if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) { - func_exit(); - return; - } - - bp = port_Board(port); - spin_lock_irqsave(&port->lock, flags); - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - spin_unlock_irqrestore(&port->lock, flags); - tty_wakeup(tty); - - func_exit(); -} - static int sx_tiocmget(struct tty_struct *tty, struct file *file) { @@ -1803,7 +1802,7 @@ static int sx_tiocmget(struct tty_struct *tty, struct file *file) func_enter(); - if (sx_paranoia_check(port, tty->name, __FUNCTION__)) { + if (sx_paranoia_check(port, tty->name, __func__)) { func_exit(); return -ENODEV; } @@ -1845,7 +1844,7 @@ static int sx_tiocmset(struct tty_struct *tty, struct file *file, func_enter(); - if (sx_paranoia_check(port, tty->name, __FUNCTION__)) { + if (sx_paranoia_check(port, tty->name, __func__)) { func_exit(); return -ENODEV; } @@ -1922,29 +1921,13 @@ static inline int sx_set_serial_info(struct specialix_port * port, int change_speed; func_enter(); - /* - if (!access_ok(VERIFY_READ, (void *) newinfo, sizeof(tmp))) { - func_exit(); - return -EFAULT; - } - */ + if (copy_from_user(&tmp, newinfo, sizeof(tmp))) { func_enter(); return -EFAULT; } -#if 0 - if ((tmp.irq != bp->irq) || - (tmp.port != bp->base) || - (tmp.type != PORT_CIRRUS) || - (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) || - (tmp.custom_divisor != 0) || - (tmp.xmit_fifo_size != CD186x_NFIFO) || - (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) { - func_exit(); - return -EINVAL; - } -#endif + lock_kernel(); change_speed = ((port->flags & ASYNC_SPD_MASK) != (tmp.flags & ASYNC_SPD_MASK)); @@ -1956,6 +1939,7 @@ static inline int sx_set_serial_info(struct specialix_port * port, ((tmp.flags & ~ASYNC_USR_MASK) != (port->flags & ~ASYNC_USR_MASK))) { func_exit(); + unlock_kernel(); return -EPERM; } port->flags = ((port->flags & ~ASYNC_USR_MASK) | @@ -1972,6 +1956,7 @@ static inline int sx_set_serial_info(struct specialix_port * port, sx_change_speed(bp, port); } func_exit(); + unlock_kernel(); return 0; } @@ -1984,12 +1969,8 @@ static inline int sx_get_serial_info(struct specialix_port * port, func_enter(); - /* - if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp))) - return -EFAULT; - */ - memset(&tmp, 0, sizeof(tmp)); + lock_kernel(); tmp.type = PORT_CIRRUS; tmp.line = port - sx_port; tmp.port = bp->base; @@ -2000,6 +1981,7 @@ static inline int sx_get_serial_info(struct specialix_port * port, tmp.closing_wait = port->closing_wait * HZ/100; tmp.custom_divisor = port->custom_divisor; tmp.xmit_fifo_size = CD186x_NFIFO; + unlock_kernel(); if (copy_to_user(retinfo, &tmp, sizeof(tmp))) { func_exit(); return -EFAULT; @@ -2045,23 +2027,6 @@ static int sx_ioctl(struct tty_struct * tty, struct file * filp, sx_send_break(port, arg ? arg*(HZ/10) : HZ/4); func_exit(); return 0; - case TIOCGSOFTCAR: - if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp)) { - func_exit(); - return -EFAULT; - } - func_exit(); - return 0; - case TIOCSSOFTCAR: - if (get_user(arg, (unsigned long __user *) argp)) { - func_exit(); - return -EFAULT; - } - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - func_exit(); - return 0; case TIOCGSERIAL: func_exit(); return sx_get_serial_info(port, argp); diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 874aaa08e95..d17be10c5d2 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -875,6 +875,7 @@ static void stl_waituntilsent(struct tty_struct *tty, int timeout) timeout = HZ; tend = jiffies + timeout; + lock_kernel(); while (stl_datastate(portp)) { if (signal_pending(current)) break; @@ -882,6 +883,7 @@ static void stl_waituntilsent(struct tty_struct *tty, int timeout) if (time_after_eq(jiffies, tend)) break; } + unlock_kernel(); } /*****************************************************************************/ @@ -1273,18 +1275,9 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd rc = 0; + lock_kernel(); + switch (cmd) { - case TIOCGSOFTCAR: - rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), - (unsigned __user *) argp); - break; - case TIOCSSOFTCAR: - if (get_user(ival, (unsigned int __user *) arg)) - return -EFAULT; - tty->termios->c_cflag = - (tty->termios->c_cflag & ~CLOCAL) | - (ival ? CLOCAL : 0); - break; case TIOCGSERIAL: rc = stl_getserial(portp, argp); break; @@ -1308,7 +1301,7 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd rc = -ENOIOCTLCMD; break; } - + unlock_kernel(); return rc; } diff --git a/drivers/char/sx.c b/drivers/char/sx.c index a6e1c9ba121..f39f6fd8935 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -384,11 +384,11 @@ static struct real_driver sx_real_driver = { #define sx_dprintk(f, str...) /* nothing */ #endif -#define func_enter() sx_dprintk(SX_DEBUG_FLOW, "sx: enter %s\n",__FUNCTION__) -#define func_exit() sx_dprintk(SX_DEBUG_FLOW, "sx: exit %s\n",__FUNCTION__) +#define func_enter() sx_dprintk(SX_DEBUG_FLOW, "sx: enter %s\n",__func__) +#define func_exit() sx_dprintk(SX_DEBUG_FLOW, "sx: exit %s\n",__func__) #define func_enter2() sx_dprintk(SX_DEBUG_FLOW, "sx: enter %s (port %d)\n", \ - __FUNCTION__, port->line) + __func__, port->line) /* * Firmware loader driver specific routines @@ -1574,7 +1574,7 @@ static void sx_close(void *ptr) sx_dprintk(SX_DEBUG_CLOSE, "WARNING port count:%d\n", port->gs.count); /*printk("%s SETTING port count to zero: %p count: %d\n", - __FUNCTION__, port, port->gs.count); + __func__, port, port->gs.count); port->gs.count = 0;*/ } @@ -1844,6 +1844,7 @@ static void sx_break(struct tty_struct *tty, int flag) int rv; func_enter(); + lock_kernel(); if (flag) rv = sx_send_command(port, HS_START, -1, HS_IDLE_BREAK); @@ -1852,7 +1853,7 @@ static void sx_break(struct tty_struct *tty, int flag) if (rv != 1) printk(KERN_ERR "sx: couldn't send break (%x).\n", read_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat))); - + unlock_kernel(); func_exit(); } @@ -1888,23 +1889,12 @@ static int sx_ioctl(struct tty_struct *tty, struct file *filp, int rc; struct sx_port *port = tty->driver_data; void __user *argp = (void __user *)arg; - int ival; /* func_enter2(); */ rc = 0; + lock_kernel(); switch (cmd) { - case TIOCGSOFTCAR: - rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), - (unsigned __user *)argp); - break; - case TIOCSSOFTCAR: - if ((rc = get_user(ival, (unsigned __user *)argp)) == 0) { - tty->termios->c_cflag = - (tty->termios->c_cflag & ~CLOCAL) | - (ival ? CLOCAL : 0); - } - break; case TIOCGSERIAL: rc = gs_getserial(&port->gs, argp); break; @@ -1915,6 +1905,7 @@ static int sx_ioctl(struct tty_struct *tty, struct file *filp, rc = -ENOIOCTLCMD; break; } + unlock_kernel(); /* func_exit(); */ return rc; @@ -2549,7 +2540,7 @@ static int __devinit sx_eisa_probe(struct device *dev) goto err_flag; } board->base2 = - board->base = ioremap(board->hw_base, SI2_EISA_WINDOW_LEN); + board->base = ioremap_nocache(board->hw_base, SI2_EISA_WINDOW_LEN); if (!board->base) { dev_err(dev, "can't remap memory\n"); goto err_reg; @@ -2626,7 +2617,7 @@ static void __devinit fix_sx_pci(struct pci_dev *pdev, struct sx_board *board) pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &hwbase); hwbase &= PCI_BASE_ADDRESS_MEM_MASK; - rebase = ioremap(hwbase, 0x80); + rebase = ioremap_nocache(hwbase, 0x80); t = readl(rebase + CNTRL_REG_OFFSET); if (t != CNTRL_REG_GOODVALUE) { printk(KERN_DEBUG "sx: performing cntrl reg fix: %08x -> " @@ -2770,7 +2761,7 @@ static int __init sx_init(void) if (!request_region(board->hw_base, board->hw_len, "sx")) continue; board->base2 = - board->base = ioremap(board->hw_base, board->hw_len); + board->base = ioremap_nocache(board->hw_base, board->hw_len); if (!board->base) goto err_sx_reg; board->flags &= ~SX_BOARD_TYPE; @@ -2794,7 +2785,7 @@ err_sx_reg: if (!request_region(board->hw_base, board->hw_len, "sx")) continue; board->base2 = - board->base = ioremap(board->hw_base, board->hw_len); + board->base = ioremap_nocache(board->hw_base, board->hw_len); if (!board->base) goto err_si_reg; board->flags &= ~SX_BOARD_TYPE; @@ -2817,7 +2808,7 @@ err_si_reg: if (!request_region(board->hw_base, board->hw_len, "sx")) continue; board->base2 = - board->base = ioremap(board->hw_base, board->hw_len); + board->base = ioremap_nocache(board->hw_base, board->hw_len); if (!board->base) goto err_si1_reg; board->flags &= ~SX_BOARD_TYPE; diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index fadab1d9510..513b7c2f3e2 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -2026,10 +2026,11 @@ static void mgsl_change_params(struct mgsl_struct *info) * * Return Value: None */ -static void mgsl_put_char(struct tty_struct *tty, unsigned char ch) +static int mgsl_put_char(struct tty_struct *tty, unsigned char ch) { struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; unsigned long flags; + int ret; if ( debug_level >= DEBUG_LEVEL_INFO ) { printk( "%s(%d):mgsl_put_char(%d) on %s\n", @@ -2037,23 +2038,23 @@ static void mgsl_put_char(struct tty_struct *tty, unsigned char ch) } if (mgsl_paranoia_check(info, tty->name, "mgsl_put_char")) - return; + return 0; if (!tty || !info->xmit_buf) - return; + return 0; spin_lock_irqsave(&info->irq_spinlock,flags); if ( (info->params.mode == MGSL_MODE_ASYNC ) || !info->tx_active ) { - if (info->xmit_cnt < SERIAL_XMIT_SIZE - 1) { info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= SERIAL_XMIT_SIZE-1; info->xmit_cnt++; + ret = 1; } } - spin_unlock_irqrestore(&info->irq_spinlock,flags); + return ret; } /* end of mgsl_put_char() */ @@ -2942,6 +2943,7 @@ static int mgsl_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; + int ret; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__, @@ -2956,7 +2958,10 @@ static int mgsl_ioctl(struct tty_struct *tty, struct file * file, return -EIO; } - return mgsl_ioctl_common(info, cmd, arg); + lock_kernel(); + ret = mgsl_ioctl_common(info, cmd, arg); + unlock_kernel(); + return ret; } static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg) @@ -3153,8 +3158,7 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp) if (info->flags & ASYNC_INITIALIZED) mgsl_wait_until_sent(tty, info->timeout); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + mgsl_flush_buffer(tty); tty_ldisc_flush(tty); @@ -3217,7 +3221,8 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout) * interval should also be less than the timeout. * Note: use tight timings here to satisfy the NIST-PCTS. */ - + + lock_kernel(); if ( info->params.data_rate ) { char_time = info->timeout/(32 * 5); if (!char_time) @@ -3247,6 +3252,7 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout) break; } } + unlock_kernel(); exit: if (debug_level >= DEBUG_LEVEL_INFO) @@ -4144,7 +4150,8 @@ static int mgsl_claim_resources(struct mgsl_struct *info) } info->lcr_mem_requested = true; - info->memory_base = ioremap(info->phys_memory_base,0x40000); + info->memory_base = ioremap_nocache(info->phys_memory_base, + 0x40000); if (!info->memory_base) { printk( "%s(%d):Cant map shared memory on device %s MemAddr=%08X\n", __FILE__,__LINE__,info->device_name, info->phys_memory_base ); @@ -4157,12 +4164,14 @@ static int mgsl_claim_resources(struct mgsl_struct *info) goto errout; } - info->lcr_base = ioremap(info->phys_lcr_base,PAGE_SIZE) + info->lcr_offset; + info->lcr_base = ioremap_nocache(info->phys_lcr_base, + PAGE_SIZE); if (!info->lcr_base) { printk( "%s(%d):Cant map LCR memory on device %s MemAddr=%08X\n", __FILE__,__LINE__,info->device_name, info->phys_lcr_base ); goto errout; } + info->lcr_base += info->lcr_offset; } else { /* claim DMA channel */ diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index f3d8d72e5ea..2001b0e52dc 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -151,7 +151,7 @@ static void hangup(struct tty_struct *tty); static void set_termios(struct tty_struct *tty, struct ktermios *old_termios); static int write(struct tty_struct *tty, const unsigned char *buf, int count); -static void put_char(struct tty_struct *tty, unsigned char ch); +static int put_char(struct tty_struct *tty, unsigned char ch); static void send_xchar(struct tty_struct *tty, char ch); static void wait_until_sent(struct tty_struct *tty, int timeout); static int write_room(struct tty_struct *tty); @@ -771,8 +771,7 @@ static void close(struct tty_struct *tty, struct file *filp) if (info->flags & ASYNC_INITIALIZED) wait_until_sent(tty, info->timeout); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + flush_buffer(tty); tty_ldisc_flush(tty); shutdown(info); @@ -913,20 +912,24 @@ cleanup: return ret; } -static void put_char(struct tty_struct *tty, unsigned char ch) +static int put_char(struct tty_struct *tty, unsigned char ch) { struct slgt_info *info = tty->driver_data; unsigned long flags; + int ret; if (sanity_check(info, tty->name, "put_char")) - return; + return 0; DBGINFO(("%s put_char(%d)\n", info->device_name, ch)); if (!info->tx_buf) - return; + return 0; spin_lock_irqsave(&info->lock,flags); - if (!info->tx_active && (info->tx_count < info->max_frame_size)) + if (!info->tx_active && (info->tx_count < info->max_frame_size)) { info->tx_buf[info->tx_count++] = ch; + ret = 1; + } spin_unlock_irqrestore(&info->lock,flags); + return ret; } static void send_xchar(struct tty_struct *tty, char ch) @@ -967,6 +970,8 @@ static void wait_until_sent(struct tty_struct *tty, int timeout) * Note: use tight timings here to satisfy the NIST-PCTS. */ + lock_kernel(); + if (info->params.data_rate) { char_time = info->timeout/(32 * 5); if (!char_time) @@ -984,6 +989,7 @@ static void wait_until_sent(struct tty_struct *tty, int timeout) if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; } + unlock_kernel(); exit: DBGINFO(("%s wait_until_sent exit\n", info->device_name)); @@ -1097,6 +1103,7 @@ static int ioctl(struct tty_struct *tty, struct file *file, struct serial_icounter_struct __user *p_cuser; /* user space */ unsigned long flags; void __user *argp = (void __user *)arg; + int ret; if (sanity_check(info, tty->name, "ioctl")) return -ENODEV; @@ -1108,37 +1115,54 @@ static int ioctl(struct tty_struct *tty, struct file *file, return -EIO; } + lock_kernel(); + switch (cmd) { case MGSL_IOCGPARAMS: - return get_params(info, argp); + ret = get_params(info, argp); + break; case MGSL_IOCSPARAMS: - return set_params(info, argp); + ret = set_params(info, argp); + break; case MGSL_IOCGTXIDLE: - return get_txidle(info, argp); + ret = get_txidle(info, argp); + break; case MGSL_IOCSTXIDLE: - return set_txidle(info, (int)arg); + ret = set_txidle(info, (int)arg); + break; case MGSL_IOCTXENABLE: - return tx_enable(info, (int)arg); + ret = tx_enable(info, (int)arg); + break; case MGSL_IOCRXENABLE: - return rx_enable(info, (int)arg); + ret = rx_enable(info, (int)arg); + break; case MGSL_IOCTXABORT: - return tx_abort(info); + ret = tx_abort(info); + break; case MGSL_IOCGSTATS: - return get_stats(info, argp); + ret = get_stats(info, argp); + break; case MGSL_IOCWAITEVENT: - return wait_mgsl_event(info, argp); + ret = wait_mgsl_event(info, argp); + break; case TIOCMIWAIT: - return modem_input_wait(info,(int)arg); + ret = modem_input_wait(info,(int)arg); + break; case MGSL_IOCGIF: - return get_interface(info, argp); + ret = get_interface(info, argp); + break; case MGSL_IOCSIF: - return set_interface(info,(int)arg); + ret = set_interface(info,(int)arg); + break; case MGSL_IOCSGPIO: - return set_gpio(info, argp); + ret = set_gpio(info, argp); + break; case MGSL_IOCGGPIO: - return get_gpio(info, argp); + ret = get_gpio(info, argp); + break; case MGSL_IOCWAITGPIO: - return wait_gpio(info, argp); + ret = wait_gpio(info, argp); + break; case TIOCGICOUNT: spin_lock_irqsave(&info->lock,flags); cnow = info->icount; @@ -1155,12 +1179,14 @@ static int ioctl(struct tty_struct *tty, struct file *file, put_user(cnow.parity, &p_cuser->parity) || put_user(cnow.brk, &p_cuser->brk) || put_user(cnow.buf_overrun, &p_cuser->buf_overrun)) - return -EFAULT; - return 0; + ret = -EFAULT; + ret = 0; + break; default: - return -ENOIOCTLCMD; + ret = -ENOIOCTLCMD; } - return 0; + unlock_kernel(); + return ret; } /* @@ -3324,7 +3350,7 @@ static int claim_resources(struct slgt_info *info) else info->reg_addr_requested = true; - info->reg_addr = ioremap(info->phys_reg_addr, SLGT_REG_SIZE); + info->reg_addr = ioremap_nocache(info->phys_reg_addr, SLGT_REG_SIZE); if (!info->reg_addr) { DBGERR(("%s cant map device registers, addr=%08X\n", info->device_name, info->phys_reg_addr)); diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index e98c3e6f821..bec54866e0b 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -519,7 +519,7 @@ static void hangup(struct tty_struct *tty); static void set_termios(struct tty_struct *tty, struct ktermios *old_termios); static int write(struct tty_struct *tty, const unsigned char *buf, int count); -static void put_char(struct tty_struct *tty, unsigned char ch); +static int put_char(struct tty_struct *tty, unsigned char ch); static void send_xchar(struct tty_struct *tty, char ch); static void wait_until_sent(struct tty_struct *tty, int timeout); static int write_room(struct tty_struct *tty); @@ -862,8 +862,7 @@ static void close(struct tty_struct *tty, struct file *filp) if (info->flags & ASYNC_INITIALIZED) wait_until_sent(tty, info->timeout); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + flush_buffer(tty); tty_ldisc_flush(tty); @@ -1046,10 +1045,11 @@ cleanup: /* Add a character to the transmit buffer. */ -static void put_char(struct tty_struct *tty, unsigned char ch) +static int put_char(struct tty_struct *tty, unsigned char ch) { SLMP_INFO *info = (SLMP_INFO *)tty->driver_data; unsigned long flags; + int ret = 0; if ( debug_level >= DEBUG_LEVEL_INFO ) { printk( "%s(%d):%s put_char(%d)\n", @@ -1057,10 +1057,10 @@ static void put_char(struct tty_struct *tty, unsigned char ch) } if (sanity_check(info, tty->name, "put_char")) - return; + return 0; if (!info->tx_buf) - return; + return 0; spin_lock_irqsave(&info->lock,flags); @@ -1072,10 +1072,12 @@ static void put_char(struct tty_struct *tty, unsigned char ch) if (info->tx_put >= info->max_frame_size) info->tx_put -= info->max_frame_size; info->tx_count++; + ret = 1; } } spin_unlock_irqrestore(&info->lock,flags); + return ret; } /* Send a high-priority XON/XOFF character @@ -1119,6 +1121,8 @@ static void wait_until_sent(struct tty_struct *tty, int timeout) if (sanity_check(info, tty->name, "wait_until_sent")) return; + lock_kernel(); + if (!(info->flags & ASYNC_INITIALIZED)) goto exit; @@ -1161,6 +1165,7 @@ static void wait_until_sent(struct tty_struct *tty, int timeout) } exit: + unlock_kernel(); if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):%s wait_until_sent() exit\n", __FILE__,__LINE__, info->device_name ); @@ -1176,6 +1181,7 @@ static int write_room(struct tty_struct *tty) if (sanity_check(info, tty->name, "write_room")) return 0; + lock_kernel(); if (info->params.mode == MGSL_MODE_HDLC) { ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE; } else { @@ -1183,6 +1189,7 @@ static int write_room(struct tty_struct *tty) if (ret < 0) ret = 0; } + unlock_kernel(); if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):%s write_room()=%d\n", @@ -1303,7 +1310,7 @@ static void tx_release(struct tty_struct *tty) * * Return Value: 0 if success, otherwise error code */ -static int ioctl(struct tty_struct *tty, struct file *file, +static int do_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { SLMP_INFO *info = (SLMP_INFO *)tty->driver_data; @@ -1393,6 +1400,16 @@ static int ioctl(struct tty_struct *tty, struct file *file, return 0; } +static int ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + lock_kernel(); + ret = do_ioctl(tty, file, cmd, arg); + unlock_kernel(); + return ret; +} + /* * /proc fs routines.... */ @@ -3626,7 +3643,8 @@ static int claim_resources(SLMP_INFO *info) else info->sca_statctrl_requested = true; - info->memory_base = ioremap(info->phys_memory_base,SCA_MEM_SIZE); + info->memory_base = ioremap_nocache(info->phys_memory_base, + SCA_MEM_SIZE); if (!info->memory_base) { printk( "%s(%d):%s Cant map shared memory, MemAddr=%08X\n", __FILE__,__LINE__,info->device_name, info->phys_memory_base ); @@ -3634,7 +3652,7 @@ static int claim_resources(SLMP_INFO *info) goto errout; } - info->lcr_base = ioremap(info->phys_lcr_base,PAGE_SIZE); + info->lcr_base = ioremap_nocache(info->phys_lcr_base, PAGE_SIZE); if (!info->lcr_base) { printk( "%s(%d):%s Cant map LCR memory, MemAddr=%08X\n", __FILE__,__LINE__,info->device_name, info->phys_lcr_base ); @@ -3643,7 +3661,7 @@ static int claim_resources(SLMP_INFO *info) } info->lcr_base += info->lcr_offset; - info->sca_base = ioremap(info->phys_sca_base,PAGE_SIZE); + info->sca_base = ioremap_nocache(info->phys_sca_base, PAGE_SIZE); if (!info->sca_base) { printk( "%s(%d):%s Cant map SCA memory, MemAddr=%08X\n", __FILE__,__LINE__,info->device_name, info->phys_sca_base ); @@ -3652,7 +3670,8 @@ static int claim_resources(SLMP_INFO *info) } info->sca_base += info->sca_offset; - info->statctrl_base = ioremap(info->phys_statctrl_base,PAGE_SIZE); + info->statctrl_base = ioremap_nocache(info->phys_statctrl_base, + PAGE_SIZE); if (!info->statctrl_base) { printk( "%s(%d):%s Cant map SCA Status/Control memory, MemAddr=%08X\n", __FILE__,__LINE__,info->device_name, info->phys_statctrl_base ); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 2fa6856706a..1d298c2cf93 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -91,7 +91,6 @@ #include <linux/module.h> #include <linux/smp_lock.h> #include <linux/device.h> -#include <linux/idr.h> #include <linux/wait.h> #include <linux/bitops.h> #include <linux/delay.h> @@ -137,9 +136,6 @@ EXPORT_SYMBOL(tty_mutex); #ifdef CONFIG_UNIX98_PTYS extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */ -extern int pty_limit; /* Config limit on Unix98 ptys */ -static DEFINE_IDR(allocated_ptys); -static DEFINE_MUTEX(allocated_ptys_lock); static int ptmx_open(struct inode *, struct file *); #endif @@ -152,8 +148,7 @@ ssize_t redirected_tty_write(struct file *, const char __user *, static unsigned int tty_poll(struct file *, poll_table *); static int tty_open(struct inode *, struct file *); static int tty_release(struct inode *, struct file *); -int tty_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); +long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg); #ifdef CONFIG_COMPAT static long tty_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); @@ -1109,8 +1104,8 @@ restart: a reference to the old ldisc. If we ended up flipping back to the existing ldisc we have two references to it */ - if (tty->ldisc.num != o_ldisc.num && tty->driver->set_ldisc) - tty->driver->set_ldisc(tty); + if (tty->ldisc.num != o_ldisc.num && tty->ops->set_ldisc) + tty->ops->set_ldisc(tty); tty_ldisc_put(o_ldisc.num); @@ -1182,9 +1177,8 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line) if (*str == '\0') str = NULL; - if (tty_line >= 0 && tty_line <= p->num && p->poll_init && - !p->poll_init(p, tty_line, str)) { - + if (tty_line >= 0 && tty_line <= p->num && p->ops && + p->ops->poll_init && !p->ops->poll_init(p, tty_line, str)) { res = p; *line = tty_line; break; @@ -1205,26 +1199,37 @@ EXPORT_SYMBOL_GPL(tty_find_polling_driver); * not in the foreground, send a SIGTTOU. If the signal is blocked or * ignored, go ahead and perform the operation. (POSIX 7.2) * - * Locking: none + * Locking: ctrl_lock */ int tty_check_change(struct tty_struct *tty) { + unsigned long flags; + int ret = 0; + if (current->signal->tty != tty) return 0; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (!tty->pgrp) { printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n"); - return 0; + goto out; } if (task_pgrp(current) == tty->pgrp) - return 0; + goto out; if (is_ignored(SIGTTOU)) - return 0; - if (is_current_pgrp_orphaned()) - return -EIO; + goto out; + if (is_current_pgrp_orphaned()) { + ret = -EIO; + goto out; + } kill_pgrp(task_pgrp(current), SIGTTOU, 1); set_thread_flag(TIF_SIGPENDING); - return -ERESTARTSYS; + ret = -ERESTARTSYS; +out: + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + return ret; } EXPORT_SYMBOL(tty_check_change); @@ -1247,8 +1252,8 @@ static unsigned int hung_up_tty_poll(struct file *filp, poll_table *wait) return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM; } -static int hung_up_tty_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long hung_up_tty_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { return cmd == TIOCSPGRP ? -ENOTTY : -EIO; } @@ -1264,7 +1269,7 @@ static const struct file_operations tty_fops = { .read = tty_read, .write = tty_write, .poll = tty_poll, - .ioctl = tty_ioctl, + .unlocked_ioctl = tty_ioctl, .compat_ioctl = tty_compat_ioctl, .open = tty_open, .release = tty_release, @@ -1277,7 +1282,7 @@ static const struct file_operations ptmx_fops = { .read = tty_read, .write = tty_write, .poll = tty_poll, - .ioctl = tty_ioctl, + .unlocked_ioctl = tty_ioctl, .compat_ioctl = tty_compat_ioctl, .open = ptmx_open, .release = tty_release, @@ -1290,7 +1295,7 @@ static const struct file_operations console_fops = { .read = tty_read, .write = redirected_tty_write, .poll = tty_poll, - .ioctl = tty_ioctl, + .unlocked_ioctl = tty_ioctl, .compat_ioctl = tty_compat_ioctl, .open = tty_open, .release = tty_release, @@ -1302,7 +1307,7 @@ static const struct file_operations hung_up_tty_fops = { .read = hung_up_tty_read, .write = hung_up_tty_write, .poll = hung_up_tty_poll, - .ioctl = hung_up_tty_ioctl, + .unlocked_ioctl = hung_up_tty_ioctl, .compat_ioctl = hung_up_tty_compat_ioctl, .release = tty_release, }; @@ -1404,6 +1409,7 @@ static void do_tty_hangup(struct work_struct *work) struct task_struct *p; struct tty_ldisc *ld; int closecount = 0, n; + unsigned long flags; if (!tty) return; @@ -1441,8 +1447,7 @@ static void do_tty_hangup(struct work_struct *work) /* We may have no line discipline at this point */ if (ld->flush_buffer) ld->flush_buffer(tty); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty_driver_flush_buffer(tty); if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && ld->write_wakeup) ld->write_wakeup(tty); @@ -1480,19 +1485,24 @@ static void do_tty_hangup(struct work_struct *work) __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); put_pid(p->signal->tty_old_pgrp); /* A noop */ + spin_lock_irqsave(&tty->ctrl_lock, flags); if (tty->pgrp) p->signal->tty_old_pgrp = get_pid(tty->pgrp); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); spin_unlock_irq(&p->sighand->siglock); } while_each_pid_task(tty->session, PIDTYPE_SID, p); } read_unlock(&tasklist_lock); + spin_lock_irqsave(&tty->ctrl_lock, flags); tty->flags = 0; put_pid(tty->session); put_pid(tty->pgrp); tty->session = NULL; tty->pgrp = NULL; tty->ctrl_status = 0; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + /* * If one of the devices matches a console pointer, we * cannot just call hangup() because that will cause @@ -1500,11 +1510,11 @@ static void do_tty_hangup(struct work_struct *work) * So we just call close() the right number of times. */ if (cons_filp) { - if (tty->driver->close) + if (tty->ops->close) for (n = 0; n < closecount; n++) - tty->driver->close(tty, cons_filp); - } else if (tty->driver->hangup) - (tty->driver->hangup)(tty); + tty->ops->close(tty, cons_filp); + } else if (tty->ops->hangup) + (tty->ops->hangup)(tty); /* * We don't want to have driver/ldisc interactions beyond * the ones we did here. The driver layer expects no @@ -1626,16 +1636,17 @@ void disassociate_ctty(int on_exit) struct tty_struct *tty; struct pid *tty_pgrp = NULL; - lock_kernel(); mutex_lock(&tty_mutex); tty = get_current_tty(); if (tty) { tty_pgrp = get_pid(tty->pgrp); mutex_unlock(&tty_mutex); + lock_kernel(); /* XXX: here we race, there is nothing protecting tty */ if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) tty_vhangup(tty); + unlock_kernel(); } else if (on_exit) { struct pid *old_pgrp; spin_lock_irq(¤t->sighand->siglock); @@ -1648,7 +1659,6 @@ void disassociate_ctty(int on_exit) put_pid(old_pgrp); } mutex_unlock(&tty_mutex); - unlock_kernel(); return; } if (tty_pgrp) { @@ -1667,10 +1677,13 @@ void disassociate_ctty(int on_exit) /* It is possible that do_tty_hangup has free'd this tty */ tty = get_current_tty(); if (tty) { + unsigned long flags; + spin_lock_irqsave(&tty->ctrl_lock, flags); put_pid(tty->session); put_pid(tty->pgrp); tty->session = NULL; tty->pgrp = NULL; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); } else { #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "error attempted to write to tty [0x%p]" @@ -1683,7 +1696,6 @@ void disassociate_ctty(int on_exit) read_lock(&tasklist_lock); session_clear_tty(task_session(current)); read_unlock(&tasklist_lock); - unlock_kernel(); } /** @@ -1693,8 +1705,10 @@ void disassociate_ctty(int on_exit) void no_tty(void) { struct task_struct *tsk = current; + lock_kernel(); if (tsk->signal->leader) disassociate_ctty(0); + unlock_kernel(); proc_clear_tty(tsk); } @@ -1714,21 +1728,26 @@ void no_tty(void) * but not always. * * Locking: - * Broken. Relies on BKL which is unsafe here. + * Uses the tty control lock internally */ void stop_tty(struct tty_struct *tty) { - if (tty->stopped) + unsigned long flags; + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (tty->stopped) { + spin_unlock_irqrestore(&tty->ctrl_lock, flags); return; + } tty->stopped = 1; if (tty->link && tty->link->packet) { tty->ctrl_status &= ~TIOCPKT_START; tty->ctrl_status |= TIOCPKT_STOP; wake_up_interruptible(&tty->link->read_wait); } - if (tty->driver->stop) - (tty->driver->stop)(tty); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + if (tty->ops->stop) + (tty->ops->stop)(tty); } EXPORT_SYMBOL(stop_tty); @@ -1743,21 +1762,26 @@ EXPORT_SYMBOL(stop_tty); * driver start method is invoked and the line discipline woken. * * Locking: - * Broken. Relies on BKL which is unsafe here. + * ctrl_lock */ void start_tty(struct tty_struct *tty) { - if (!tty->stopped || tty->flow_stopped) + unsigned long flags; + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (!tty->stopped || tty->flow_stopped) { + spin_unlock_irqrestore(&tty->ctrl_lock, flags); return; + } tty->stopped = 0; if (tty->link && tty->link->packet) { tty->ctrl_status &= ~TIOCPKT_STOP; tty->ctrl_status |= TIOCPKT_START; wake_up_interruptible(&tty->link->read_wait); } - if (tty->driver->start) - (tty->driver->start)(tty); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + if (tty->ops->start) + (tty->ops->start)(tty); /* If we have a running line discipline it may need kicking */ tty_wakeup(tty); } @@ -1775,10 +1799,8 @@ EXPORT_SYMBOL(start_tty); * for hung up devices before calling the line discipline method. * * Locking: - * Locks the line discipline internally while needed - * For historical reasons the line discipline read method is - * invoked under the BKL. This will go away in time so do not rely on it - * in new code. Multiple read calls may be outstanding in parallel. + * Locks the line discipline internally while needed. Multiple + * read calls may be outstanding in parallel. */ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, @@ -1799,13 +1821,11 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, /* We want to wait for the line discipline to sort out in this situation */ ld = tty_ldisc_ref_wait(tty); - lock_kernel(); if (ld->read) i = (ld->read)(tty, file, buf, count); else i = -EIO; tty_ldisc_deref(ld); - unlock_kernel(); if (i > 0) inode->i_atime = current_fs_time(inode->i_sb); return i; @@ -1893,9 +1913,7 @@ static inline ssize_t do_tty_write( ret = -EFAULT; if (copy_from_user(tty->write_buf, buf, size)) break; - lock_kernel(); ret = write(tty, file, tty->write_buf, size); - unlock_kernel(); if (ret <= 0) break; written += ret; @@ -1948,10 +1966,13 @@ static ssize_t tty_write(struct file *file, const char __user *buf, tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode, "tty_write")) return -EIO; - if (!tty || !tty->driver->write || + if (!tty || !tty->ops->write || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; - + /* Short term debug to catch buggy drivers */ + if (tty->ops->write_room == NULL) + printk(KERN_ERR "tty driver %s lacks a write_room method.\n", + tty->driver->name); ld = tty_ldisc_ref_wait(tty); if (!ld->write) ret = -EIO; @@ -2098,6 +2119,7 @@ static int init_dev(struct tty_driver *driver, int idx, goto fail_no_mem; initialize_tty_struct(tty); tty->driver = driver; + tty->ops = driver->ops; tty->index = idx; tty_line_name(driver, idx, tty->name); @@ -2128,6 +2150,7 @@ static int init_dev(struct tty_driver *driver, int idx, goto free_mem_out; initialize_tty_struct(o_tty); o_tty->driver = driver->other; + o_tty->ops = driver->ops; o_tty->index = idx; tty_line_name(driver->other, idx, o_tty->name); @@ -2432,8 +2455,8 @@ static void release_dev(struct file *filp) } } #endif - if (tty->driver->close) - tty->driver->close(tty, filp); + if (tty->ops->close) + tty->ops->close(tty, filp); /* * Sanity check: if tty->count is going to zero, there shouldn't be @@ -2612,15 +2635,9 @@ static void release_dev(struct file *filp) */ release_tty(tty, idx); -#ifdef CONFIG_UNIX98_PTYS /* Make this pty number available for reallocation */ - if (devpts) { - mutex_lock(&allocated_ptys_lock); - idr_remove(&allocated_ptys, idx); - mutex_unlock(&allocated_ptys_lock); - } -#endif - + if (devpts) + devpts_kill_index(idx); } /** @@ -2716,8 +2733,8 @@ got_driver: printk(KERN_DEBUG "opening %s...", tty->name); #endif if (!retval) { - if (tty->driver->open) - retval = tty->driver->open(tty, filp); + if (tty->ops->open) + retval = tty->ops->open(tty, filp); else retval = -ENODEV; } @@ -2776,29 +2793,13 @@ static int ptmx_open(struct inode *inode, struct file *filp) struct tty_struct *tty; int retval; int index; - int idr_ret; nonseekable_open(inode, filp); /* find a device that is not in use. */ - mutex_lock(&allocated_ptys_lock); - if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) { - mutex_unlock(&allocated_ptys_lock); - return -ENOMEM; - } - idr_ret = idr_get_new(&allocated_ptys, NULL, &index); - if (idr_ret < 0) { - mutex_unlock(&allocated_ptys_lock); - if (idr_ret == -EAGAIN) - return -ENOMEM; - return -EIO; - } - if (index >= pty_limit) { - idr_remove(&allocated_ptys, index); - mutex_unlock(&allocated_ptys_lock); - return -EIO; - } - mutex_unlock(&allocated_ptys_lock); + index = devpts_new_index(); + if (index < 0) + return index; mutex_lock(&tty_mutex); retval = init_dev(ptm_driver, index, &tty); @@ -2811,21 +2812,19 @@ static int ptmx_open(struct inode *inode, struct file *filp) filp->private_data = tty; file_move(filp, &tty->tty_files); - retval = -ENOMEM; - if (devpts_pty_new(tty->link)) + retval = devpts_pty_new(tty->link); + if (retval) goto out1; - check_tty_count(tty, "tty_open"); - retval = ptm_driver->open(tty, filp); + check_tty_count(tty, "ptmx_open"); + retval = ptm_driver->ops->open(tty, filp); if (!retval) return 0; out1: release_dev(filp); return retval; out: - mutex_lock(&allocated_ptys_lock); - idr_remove(&allocated_ptys, index); - mutex_unlock(&allocated_ptys_lock); + devpts_kill_index(index); return retval; } #endif @@ -2882,6 +2881,7 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait) static int tty_fasync(int fd, struct file *filp, int on) { struct tty_struct *tty; + unsigned long flags; int retval; tty = (struct tty_struct *)filp->private_data; @@ -2897,6 +2897,7 @@ static int tty_fasync(int fd, struct file *filp, int on) struct pid *pid; if (!waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = 1; + spin_lock_irqsave(&tty->ctrl_lock, flags); if (tty->pgrp) { pid = tty->pgrp; type = PIDTYPE_PGID; @@ -2904,6 +2905,7 @@ static int tty_fasync(int fd, struct file *filp, int on) pid = task_pid(current); type = PIDTYPE_PID; } + spin_unlock_irqrestore(&tty->ctrl_lock, flags); retval = __f_setown(filp, pid, type, 0); if (retval) return retval; @@ -2989,6 +2991,8 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, struct winsize __user *arg) { struct winsize tmp_ws; + struct pid *pgrp, *rpgrp; + unsigned long flags; if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) return -EFAULT; @@ -3006,10 +3010,21 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, } } #endif - if (tty->pgrp) - kill_pgrp(tty->pgrp, SIGWINCH, 1); - if ((real_tty->pgrp != tty->pgrp) && real_tty->pgrp) - kill_pgrp(real_tty->pgrp, SIGWINCH, 1); + /* Get the PID values and reference them so we can + avoid holding the tty ctrl lock while sending signals */ + spin_lock_irqsave(&tty->ctrl_lock, flags); + pgrp = get_pid(tty->pgrp); + rpgrp = get_pid(real_tty->pgrp); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + + if (pgrp) + kill_pgrp(pgrp, SIGWINCH, 1); + if (rpgrp != pgrp && rpgrp) + kill_pgrp(rpgrp, SIGWINCH, 1); + + put_pid(pgrp); + put_pid(rpgrp); + tty->winsize = tmp_ws; real_tty->winsize = tmp_ws; done: @@ -3070,10 +3085,13 @@ static int fionbio(struct file *file, int __user *p) if (get_user(nonblock, p)) return -EFAULT; + /* file->f_flags is still BKL protected in the fs layer - vomit */ + lock_kernel(); if (nonblock) file->f_flags |= O_NONBLOCK; else file->f_flags &= ~O_NONBLOCK; + unlock_kernel(); return 0; } @@ -3131,6 +3149,27 @@ unlock: } /** + * tty_get_pgrp - return a ref counted pgrp pid + * @tty: tty to read + * + * Returns a refcounted instance of the pid struct for the process + * group controlling the tty. + */ + +struct pid *tty_get_pgrp(struct tty_struct *tty) +{ + unsigned long flags; + struct pid *pgrp; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + pgrp = get_pid(tty->pgrp); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + + return pgrp; +} +EXPORT_SYMBOL_GPL(tty_get_pgrp); + +/** * tiocgpgrp - get process group * @tty: tty passed by user * @real_tty: tty side of the tty pased by the user if a pty else the tty @@ -3144,13 +3183,18 @@ unlock: static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) { + struct pid *pid; + int ret; /* * (tty == real_tty) is a cheap way of * testing if the tty is NOT a master pty. */ if (tty == real_tty && current->signal->tty != real_tty) return -ENOTTY; - return put_user(pid_vnr(real_tty->pgrp), p); + pid = tty_get_pgrp(real_tty); + ret = put_user(pid_vnr(pid), p); + put_pid(pid); + return ret; } /** @@ -3162,7 +3206,7 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t * Set the process group of the tty to the session passed. Only * permitted where the tty session is our session. * - * Locking: None + * Locking: RCU, ctrl lock */ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) @@ -3170,6 +3214,7 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t struct pid *pgrp; pid_t pgrp_nr; int retval = tty_check_change(real_tty); + unsigned long flags; if (retval == -EIO) return -ENOTTY; @@ -3192,8 +3237,10 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t if (session_of_pgrp(pgrp) != task_session(current)) goto out_unlock; retval = 0; + spin_lock_irqsave(&tty->ctrl_lock, flags); put_pid(real_tty->pgrp); real_tty->pgrp = get_pid(pgrp); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); out_unlock: rcu_read_unlock(); return retval; @@ -3237,10 +3284,16 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _ static int tiocsetd(struct tty_struct *tty, int __user *p) { int ldisc; + int ret; if (get_user(ldisc, p)) return -EFAULT; - return tty_set_ldisc(tty, ldisc); + + lock_kernel(); + ret = tty_set_ldisc(tty, ldisc); + unlock_kernel(); + + return ret; } /** @@ -3260,18 +3313,18 @@ static int send_break(struct tty_struct *tty, unsigned int duration) { if (tty_write_lock(tty, 0) < 0) return -EINTR; - tty->driver->break_ctl(tty, -1); + tty->ops->break_ctl(tty, -1); if (!signal_pending(current)) msleep_interruptible(duration); - tty->driver->break_ctl(tty, 0); + tty->ops->break_ctl(tty, 0); tty_write_unlock(tty); - if (signal_pending(current)) + if (!signal_pending(current)) return -EINTR; return 0; } /** - * tiocmget - get modem status + * tty_tiocmget - get modem status * @tty: tty device * @file: user file pointer * @p: pointer to result @@ -3286,8 +3339,8 @@ static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p { int retval = -EINVAL; - if (tty->driver->tiocmget) { - retval = tty->driver->tiocmget(tty, file); + if (tty->ops->tiocmget) { + retval = tty->ops->tiocmget(tty, file); if (retval >= 0) retval = put_user(retval, p); @@ -3296,7 +3349,7 @@ static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p } /** - * tiocmset - set modem status + * tty_tiocmset - set modem status * @tty: tty device * @file: user file pointer * @cmd: command - clear bits, set bits or set all @@ -3313,7 +3366,7 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int { int retval = -EINVAL; - if (tty->driver->tiocmset) { + if (tty->ops->tiocmset) { unsigned int set, clear, val; retval = get_user(val, p); @@ -3337,7 +3390,7 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; - retval = tty->driver->tiocmset(tty, file, set, clear); + retval = tty->ops->tiocmset(tty, file, set, clear); } return retval; } @@ -3345,20 +3398,18 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int /* * Split this up, as gcc can choke on it otherwise.. */ -int tty_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct tty_struct *tty, *real_tty; void __user *p = (void __user *)arg; int retval; struct tty_ldisc *ld; + struct inode *inode = file->f_dentry->d_inode; tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode, "tty_ioctl")) return -EINVAL; - /* CHECKME: is this safe as one end closes ? */ - real_tty = tty; if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) @@ -3367,21 +3418,28 @@ int tty_ioctl(struct inode *inode, struct file *file, /* * Break handling by driver */ - if (!tty->driver->break_ctl) { + + retval = -EINVAL; + + if (!tty->ops->break_ctl) { switch (cmd) { case TIOCSBRK: case TIOCCBRK: - if (tty->driver->ioctl) - return tty->driver->ioctl(tty, file, cmd, arg); - return -EINVAL; + if (tty->ops->ioctl) + retval = tty->ops->ioctl(tty, file, cmd, arg); + if (retval != -EINVAL && retval != -ENOIOCTLCMD) + printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name); + return retval; /* These two ioctl's always return success; even if */ /* the driver doesn't support them. */ case TCSBRK: case TCSBRKP: - if (!tty->driver->ioctl) + if (!tty->ops->ioctl) return 0; - retval = tty->driver->ioctl(tty, file, cmd, arg); + retval = tty->ops->ioctl(tty, file, cmd, arg); + if (retval != -EINVAL && retval != -ENOIOCTLCMD) + printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name); if (retval == -ENOIOCTLCMD) retval = 0; return retval; @@ -3439,7 +3497,6 @@ int tty_ioctl(struct inode *inode, struct file *file, case TIOCGSID: return tiocgsid(tty, real_tty, p); case TIOCGETD: - /* FIXME: check this is ok */ return put_user(tty->ldisc.num, (int __user *)p); case TIOCSETD: return tiocsetd(tty, p); @@ -3451,11 +3508,13 @@ int tty_ioctl(struct inode *inode, struct file *file, * Break handling */ case TIOCSBRK: /* Turn break on, unconditionally */ - tty->driver->break_ctl(tty, -1); + if (tty->ops->break_ctl) + tty->ops->break_ctl(tty, -1); return 0; case TIOCCBRK: /* Turn break off, unconditionally */ - tty->driver->break_ctl(tty, 0); + if (tty->ops->break_ctl) + tty->ops->break_ctl(tty, 0); return 0; case TCSBRK: /* SVID version: non-zero arg --> no break */ /* non-zero arg means wait for all output data @@ -3484,8 +3543,8 @@ int tty_ioctl(struct inode *inode, struct file *file, } break; } - if (tty->driver->ioctl) { - retval = (tty->driver->ioctl)(tty, file, cmd, arg); + if (tty->ops->ioctl) { + retval = (tty->ops->ioctl)(tty, file, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } @@ -3512,8 +3571,8 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, if (tty_paranoia_check(tty, inode, "tty_ioctl")) return -EINVAL; - if (tty->driver->compat_ioctl) { - retval = (tty->driver->compat_ioctl)(tty, file, cmd, arg); + if (tty->ops->compat_ioctl) { + retval = (tty->ops->compat_ioctl)(tty, file, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } @@ -3563,8 +3622,7 @@ void __do_SAK(struct tty_struct *tty) tty_ldisc_flush(tty); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty_driver_flush_buffer(tty); read_lock(&tasklist_lock); /* Kill the entire session */ @@ -3770,19 +3828,32 @@ static void initialize_tty_struct(struct tty_struct *tty) mutex_init(&tty->atomic_read_lock); mutex_init(&tty->atomic_write_lock); spin_lock_init(&tty->read_lock); + spin_lock_init(&tty->ctrl_lock); INIT_LIST_HEAD(&tty->tty_files); INIT_WORK(&tty->SAK_work, do_SAK_work); } -/* - * The default put_char routine if the driver did not define one. +/** + * tty_put_char - write one character to a tty + * @tty: tty + * @ch: character + * + * Write one byte to the tty using the provided put_char method + * if present. Returns the number of characters successfully output. + * + * Note: the specific put_char operation in the driver layer may go + * away soon. Don't call it directly, use this method */ -static void tty_default_put_char(struct tty_struct *tty, unsigned char ch) +int tty_put_char(struct tty_struct *tty, unsigned char ch) { - tty->driver->write(tty, &ch, 1); + if (tty->ops->put_char) + return tty->ops->put_char(tty, ch); + return tty->ops->write(tty, &ch, 1); } +EXPORT_SYMBOL_GPL(tty_put_char); + static struct class *tty_class; /** @@ -3865,37 +3936,8 @@ void put_tty_driver(struct tty_driver *driver) void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op) { - driver->open = op->open; - driver->close = op->close; - driver->write = op->write; - driver->put_char = op->put_char; - driver->flush_chars = op->flush_chars; - driver->write_room = op->write_room; - driver->chars_in_buffer = op->chars_in_buffer; - driver->ioctl = op->ioctl; - driver->compat_ioctl = op->compat_ioctl; - driver->set_termios = op->set_termios; - driver->throttle = op->throttle; - driver->unthrottle = op->unthrottle; - driver->stop = op->stop; - driver->start = op->start; - driver->hangup = op->hangup; - driver->break_ctl = op->break_ctl; - driver->flush_buffer = op->flush_buffer; - driver->set_ldisc = op->set_ldisc; - driver->wait_until_sent = op->wait_until_sent; - driver->send_xchar = op->send_xchar; - driver->read_proc = op->read_proc; - driver->write_proc = op->write_proc; - driver->tiocmget = op->tiocmget; - driver->tiocmset = op->tiocmset; -#ifdef CONFIG_CONSOLE_POLL - driver->poll_init = op->poll_init; - driver->poll_get_char = op->poll_get_char; - driver->poll_put_char = op->poll_put_char; -#endif -} - + driver->ops = op; +}; EXPORT_SYMBOL(alloc_tty_driver); EXPORT_SYMBOL(put_tty_driver); @@ -3958,9 +4000,6 @@ int tty_register_driver(struct tty_driver *driver) return error; } - if (!driver->put_char) - driver->put_char = tty_default_put_char; - mutex_lock(&tty_mutex); list_add(&driver->tty_drivers, &tty_drivers); mutex_unlock(&tty_mutex); @@ -4036,14 +4075,19 @@ void proc_clear_tty(struct task_struct *p) } EXPORT_SYMBOL(proc_clear_tty); +/* Called under the sighand lock */ + static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) { if (tty) { - /* We should not have a session or pgrp to here but.... */ + unsigned long flags; + /* We should not have a session or pgrp to put here but.... */ + spin_lock_irqsave(&tty->ctrl_lock, flags); put_pid(tty->session); put_pid(tty->pgrp); - tty->session = get_pid(task_session(tsk)); tty->pgrp = get_pid(task_pgrp(tsk)); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + tty->session = get_pid(task_session(tsk)); } put_pid(tsk->signal->tty_old_pgrp); tsk->signal->tty = tty; diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index f95a80b2265..b1a757a5ee2 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -21,6 +21,7 @@ #include <linux/module.h> #include <linux/bitops.h> #include <linux/mutex.h> +#include <linux/smp_lock.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -39,6 +40,50 @@ #define TERMIOS_OLD 8 +int tty_chars_in_buffer(struct tty_struct *tty) +{ + if (tty->ops->chars_in_buffer) + return tty->ops->chars_in_buffer(tty); + else + return 0; +} + +EXPORT_SYMBOL(tty_chars_in_buffer); + +int tty_write_room(struct tty_struct *tty) +{ + if (tty->ops->write_room) + return tty->ops->write_room(tty); + return 2048; +} + +EXPORT_SYMBOL(tty_write_room); + +void tty_driver_flush_buffer(struct tty_struct *tty) +{ + if (tty->ops->flush_buffer) + tty->ops->flush_buffer(tty); +} + +EXPORT_SYMBOL(tty_driver_flush_buffer); + +void tty_throttle(struct tty_struct *tty) +{ + /* check TTY_THROTTLED first so it indicates our state */ + if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && + tty->ops->throttle) + tty->ops->throttle(tty); +} +EXPORT_SYMBOL(tty_throttle); + +void tty_unthrottle(struct tty_struct *tty) +{ + if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && + tty->ops->unthrottle) + tty->ops->unthrottle(tty); +} +EXPORT_SYMBOL(tty_unthrottle); + /** * tty_wait_until_sent - wait for I/O to finish * @tty: tty we are waiting for @@ -57,15 +102,13 @@ void tty_wait_until_sent(struct tty_struct *tty, long timeout) printk(KERN_DEBUG "%s wait until sent...\n", tty_name(tty, buf)); #endif - if (!tty->driver->chars_in_buffer) - return; if (!timeout) timeout = MAX_SCHEDULE_TIMEOUT; if (wait_event_interruptible_timeout(tty->write_wait, - !tty->driver->chars_in_buffer(tty), timeout) < 0) - return; - if (tty->driver->wait_until_sent) - tty->driver->wait_until_sent(tty, timeout); + !tty_chars_in_buffer(tty), timeout) >= 0) { + if (tty->ops->wait_until_sent) + tty->ops->wait_until_sent(tty, timeout); + } } EXPORT_SYMBOL(tty_wait_until_sent); @@ -393,8 +436,9 @@ EXPORT_SYMBOL(tty_termios_hw_change); static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) { int canon_change; - struct ktermios old_termios = *tty->termios; + struct ktermios old_termios; struct tty_ldisc *ld; + unsigned long flags; /* * Perform the actual termios internal changes under lock. @@ -404,7 +448,7 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) /* FIXME: we need to decide on some locking/ordering semantics for the set_termios notification eventually */ mutex_lock(&tty->termios_mutex); - + old_termios = *tty->termios; *tty->termios = *new_termios; unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON; @@ -429,17 +473,19 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) STOP_CHAR(tty) == '\023' && START_CHAR(tty) == '\021'); if (old_flow != new_flow) { + spin_lock_irqsave(&tty->ctrl_lock, flags); tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP); if (new_flow) tty->ctrl_status |= TIOCPKT_DOSTOP; else tty->ctrl_status |= TIOCPKT_NOSTOP; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); wake_up_interruptible(&tty->link->read_wait); } } - if (tty->driver->set_termios) - (*tty->driver->set_termios)(tty, &old_termios); + if (tty->ops->set_termios) + (*tty->ops->set_termios)(tty, &old_termios); else tty_termios_copy_hw(tty->termios, &old_termios); @@ -474,7 +520,9 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) if (retval) return retval; + mutex_lock(&tty->termios_mutex); memcpy(&tmp_termios, tty->termios, sizeof(struct ktermios)); + mutex_unlock(&tty->termios_mutex); if (opt & TERMIOS_TERMIO) { if (user_termio_to_kernel_termios(&tmp_termios, @@ -660,12 +708,14 @@ static int get_tchars(struct tty_struct *tty, struct tchars __user *tchars) { struct tchars tmp; + mutex_lock(&tty->termios_mutex); tmp.t_intrc = tty->termios->c_cc[VINTR]; tmp.t_quitc = tty->termios->c_cc[VQUIT]; tmp.t_startc = tty->termios->c_cc[VSTART]; tmp.t_stopc = tty->termios->c_cc[VSTOP]; tmp.t_eofc = tty->termios->c_cc[VEOF]; tmp.t_brkc = tty->termios->c_cc[VEOL2]; /* what is brkc anyway? */ + mutex_unlock(&tty->termios_mutex); return copy_to_user(tchars, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -675,12 +725,14 @@ static int set_tchars(struct tty_struct *tty, struct tchars __user *tchars) if (copy_from_user(&tmp, tchars, sizeof(tmp))) return -EFAULT; + mutex_lock(&tty->termios_mutex); tty->termios->c_cc[VINTR] = tmp.t_intrc; tty->termios->c_cc[VQUIT] = tmp.t_quitc; tty->termios->c_cc[VSTART] = tmp.t_startc; tty->termios->c_cc[VSTOP] = tmp.t_stopc; tty->termios->c_cc[VEOF] = tmp.t_eofc; tty->termios->c_cc[VEOL2] = tmp.t_brkc; /* what is brkc anyway? */ + mutex_unlock(&tty->termios_mutex); return 0; } #endif @@ -690,6 +742,7 @@ static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) { struct ltchars tmp; + mutex_lock(&tty->termios_mutex); tmp.t_suspc = tty->termios->c_cc[VSUSP]; /* what is dsuspc anyway? */ tmp.t_dsuspc = tty->termios->c_cc[VSUSP]; @@ -698,6 +751,7 @@ static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) tmp.t_flushc = tty->termios->c_cc[VEOL2]; tmp.t_werasc = tty->termios->c_cc[VWERASE]; tmp.t_lnextc = tty->termios->c_cc[VLNEXT]; + mutex_unlock(&tty->termios_mutex); return copy_to_user(ltchars, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -708,6 +762,7 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) if (copy_from_user(&tmp, ltchars, sizeof(tmp))) return -EFAULT; + mutex_lock(&tty->termios_mutex); tty->termios->c_cc[VSUSP] = tmp.t_suspc; /* what is dsuspc anyway? */ tty->termios->c_cc[VEOL2] = tmp.t_dsuspc; @@ -716,6 +771,7 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) tty->termios->c_cc[VEOL2] = tmp.t_flushc; tty->termios->c_cc[VWERASE] = tmp.t_werasc; tty->termios->c_cc[VLNEXT] = tmp.t_lnextc; + mutex_unlock(&tty->termios_mutex); return 0; } #endif @@ -732,8 +788,8 @@ static int send_prio_char(struct tty_struct *tty, char ch) { int was_stopped = tty->stopped; - if (tty->driver->send_xchar) { - tty->driver->send_xchar(tty, ch); + if (tty->ops->send_xchar) { + tty->ops->send_xchar(tty, ch); return 0; } @@ -742,7 +798,7 @@ static int send_prio_char(struct tty_struct *tty, char ch) if (was_stopped) start_tty(tty); - tty->driver->write(tty, &ch, 1); + tty->ops->write(tty, &ch, 1); if (was_stopped) stop_tty(tty); tty_write_unlock(tty); @@ -750,6 +806,33 @@ static int send_prio_char(struct tty_struct *tty, char ch) } /** + * tty_change_softcar - carrier change ioctl helper + * @tty: tty to update + * @arg: enable/disable CLOCAL + * + * Perform a change to the CLOCAL state and call into the driver + * layer to make it visible. All done with the termios mutex + */ + +static int tty_change_softcar(struct tty_struct *tty, int arg) +{ + int ret = 0; + int bit = arg ? CLOCAL : 0; + struct ktermios old; + + mutex_lock(&tty->termios_mutex); + old = *tty->termios; + tty->termios->c_cflag &= ~CLOCAL; + tty->termios->c_cflag |= bit; + if (tty->ops->set_termios) + tty->ops->set_termios(tty, &old); + if ((tty->termios->c_cflag & CLOCAL) != bit) + ret = -EINVAL; + mutex_unlock(&tty->termios_mutex); + return ret; +} + +/** * tty_mode_ioctl - mode related ioctls * @tty: tty for the ioctl * @file: file pointer for the tty @@ -859,12 +942,7 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, case TIOCSSOFTCAR: if (get_user(arg, (unsigned int __user *) arg)) return -EFAULT; - mutex_lock(&tty->termios_mutex); - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - mutex_unlock(&tty->termios_mutex); - return 0; + return tty_change_softcar(tty, arg); default: return -ENOIOCTLCMD; } @@ -889,8 +967,7 @@ int tty_perform_flush(struct tty_struct *tty, unsigned long arg) ld->flush_buffer(tty); /* fall through */ case TCOFLUSH: - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty_driver_flush_buffer(tty); break; default: tty_ldisc_deref(ld); @@ -905,6 +982,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { struct tty_struct *real_tty; + unsigned long flags; int retval; if (tty->driver->type == TTY_DRIVER_TYPE_PTY && @@ -946,9 +1024,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, case TCFLSH: return tty_perform_flush(tty, arg); case TIOCOUTQ: - return put_user(tty->driver->chars_in_buffer ? - tty->driver->chars_in_buffer(tty) : 0, - (int __user *) arg); + return put_user(tty_chars_in_buffer(tty), (int __user *) arg); case TIOCINQ: retval = tty->read_cnt; if (L_ICANON(tty)) @@ -963,6 +1039,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, return -ENOTTY; if (get_user(pktmode, (int __user *) arg)) return -EFAULT; + spin_lock_irqsave(&tty->ctrl_lock, flags); if (pktmode) { if (!tty->packet) { tty->packet = 1; @@ -970,6 +1047,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, } } else tty->packet = 0; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); return 0; } default: diff --git a/drivers/char/viocons.c b/drivers/char/viocons.c index 8de6b95aeb8..3d3e1c2b310 100644 --- a/drivers/char/viocons.c +++ b/drivers/char/viocons.c @@ -628,13 +628,13 @@ static int viotty_write(struct tty_struct *tty, const unsigned char *buf, /* * TTY put_char method */ -static void viotty_put_char(struct tty_struct *tty, unsigned char ch) +static int viotty_put_char(struct tty_struct *tty, unsigned char ch) { struct port_info *pi; pi = get_port_data(tty); if (pi == NULL) - return; + return 0; /* This will append '\r' as well if the char is '\n' */ if (viochar_is_console(pi)) @@ -642,6 +642,7 @@ static void viotty_put_char(struct tty_struct *tty, unsigned char ch) if (viopath_isactive(pi->lp)) internal_write(pi, &ch, 1); + return 1; } /* @@ -704,8 +705,11 @@ static int viotty_ioctl(struct tty_struct *tty, struct file *file, case KDSKBLED: return 0; } - - return n_tty_ioctl(tty, file, cmd, arg); + /* FIXME: WTF is this being called for ??? */ + lock_kernel(); + ret = n_tty_ioctl(tty, file, cmd, arg); + unlock_kernel(); + return ret; } /* diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 1c266047713..e458b08139a 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -909,15 +909,21 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) if (vc->vc_tty) { struct winsize ws, *cws = &vc->vc_tty->winsize; + unsigned long flags; memset(&ws, 0, sizeof(ws)); ws.ws_row = vc->vc_rows; ws.ws_col = vc->vc_cols; ws.ws_ypixel = vc->vc_scan_lines; + + mutex_lock(&vc->vc_tty->termios_mutex); + spin_lock_irqsave(&vc->vc_tty->ctrl_lock, flags); if ((ws.ws_row != cws->ws_row || ws.ws_col != cws->ws_col) && vc->vc_tty->pgrp) kill_pgrp(vc->vc_tty->pgrp, SIGWINCH, 1); + spin_unlock_irqrestore(&vc->vc_tty->ctrl_lock, flags); *cws = ws; + mutex_unlock(&vc->vc_tty->termios_mutex); } if (CON_IS_VISIBLE(vc)) @@ -2541,6 +2547,9 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) if (get_user(type, p)) return -EFAULT; ret = 0; + + lock_kernel(); + switch (type) { case TIOCL_SETSEL: @@ -2560,7 +2569,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) ret = sel_loadlut(p); break; case TIOCL_GETSHIFTSTATE: - + /* * Make it possible to react to Shift+Mousebutton. * Note that 'shift_state' is an undocumented @@ -2615,6 +2624,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) ret = -EINVAL; break; } + unlock_kernel(); return ret; } @@ -2632,11 +2642,11 @@ static int con_write(struct tty_struct *tty, const unsigned char *buf, int count return retval; } -static void con_put_char(struct tty_struct *tty, unsigned char ch) +static int con_put_char(struct tty_struct *tty, unsigned char ch) { if (in_interrupt()) - return; /* n_r3964 calls put_char() from interrupt context */ - do_con_write(tty, &ch, 1); + return 0; /* n_r3964 calls put_char() from interrupt context */ + return do_con_write(tty, &ch, 1); } static int con_write_room(struct tty_struct *tty) @@ -3829,7 +3839,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op) goto out; c = (font.width+7)/8 * 32 * font.charcount; - + if (op->data && font.charcount > op->charcount) rc = -ENOSPC; if (!(op->flags & KD_FONT_FLAG_OLD)) { @@ -3994,6 +4004,7 @@ u16 screen_glyph(struct vc_data *vc, int offset) c |= 0x100; return c; } +EXPORT_SYMBOL_GPL(screen_glyph); /* used by vcs - note the word offset */ unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed) diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index e6f89e8b925..3211afd9d57 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -373,11 +373,17 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, unsigned char ucval; void __user *up = (void __user *)arg; int i, perm; - + int ret = 0; + console = vc->vc_num; - if (!vc_cons_allocated(console)) /* impossible? */ - return -ENOIOCTLCMD; + lock_kernel(); + + if (!vc_cons_allocated(console)) { /* impossible? */ + ret = -ENOIOCTLCMD; + goto out; + } + /* * To have permissions to do most of the vt ioctls, we either have @@ -391,15 +397,15 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, switch (cmd) { case KIOCSOUND: if (!perm) - return -EPERM; + goto eperm; if (arg) arg = CLOCK_TICK_RATE / arg; kd_mksound(arg, 0); - return 0; + break; case KDMKTONE: if (!perm) - return -EPERM; + goto eperm; { unsigned int ticks, count; @@ -412,7 +418,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, if (count) count = CLOCK_TICK_RATE / count; kd_mksound(count, ticks); - return 0; + break; } case KDGKBTYPE: @@ -435,14 +441,18 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, * KDADDIO and KDDELIO may be able to add ports beyond what * we reject here, but to be safe... */ - if (arg < GPFIRST || arg > GPLAST) - return -EINVAL; - return sys_ioperm(arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0; + if (arg < GPFIRST || arg > GPLAST) { + ret = -EINVAL; + break; + } + ret = sys_ioperm(arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0; + break; case KDENABIO: case KDDISABIO: - return sys_ioperm(GPFIRST, GPNUM, + ret = sys_ioperm(GPFIRST, GPNUM, (cmd == KDENABIO)) ? -ENXIO : 0; + break; #endif /* Linux m68k/i386 interface for setting the keyboard delay/repeat rate */ @@ -450,19 +460,20 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, case KDKBDREP: { struct kbd_repeat kbrep; - int err; if (!capable(CAP_SYS_TTY_CONFIG)) - return -EPERM; + goto eperm; - if (copy_from_user(&kbrep, up, sizeof(struct kbd_repeat))) - return -EFAULT; - err = kbd_rate(&kbrep); - if (err) - return err; + if (copy_from_user(&kbrep, up, sizeof(struct kbd_repeat))) { + ret = -EFAULT; + break; + } + ret = kbd_rate(&kbrep); + if (ret) + break; if (copy_to_user(up, &kbrep, sizeof(struct kbd_repeat))) - return -EFAULT; - return 0; + ret = -EFAULT; + break; } case KDSETMODE: @@ -475,7 +486,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, * need to restore their engine state. --BenH */ if (!perm) - return -EPERM; + goto eperm; switch (arg) { case KD_GRAPHICS: break; @@ -485,13 +496,14 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, case KD_TEXT: break; default: - return -EINVAL; + ret = -EINVAL; + goto out; } if (vc->vc_mode == (unsigned char) arg) - return 0; + break; vc->vc_mode = (unsigned char) arg; if (console != fg_console) - return 0; + break; /* * explicitly blank/unblank the screen if switching modes */ @@ -501,7 +513,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, else do_blank_screen(1); release_console_sem(); - return 0; + break; case KDGETMODE: ucval = vc->vc_mode; @@ -513,11 +525,12 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, * these work like a combination of mmap and KDENABIO. * this could be easily finished. */ - return -EINVAL; + ret = -EINVAL; + break; case KDSKBMODE: if (!perm) - return -EPERM; + goto eperm; switch(arg) { case K_RAW: kbd->kbdmode = VC_RAW; @@ -534,10 +547,11 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, compute_shiftstate(); break; default: - return -EINVAL; + ret = -EINVAL; + goto out; } tty_ldisc_flush(tty); - return 0; + break; case KDGKBMODE: ucval = ((kbd->kbdmode == VC_RAW) ? K_RAW : @@ -557,28 +571,32 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, set_vc_kbd_mode(kbd, VC_META); break; default: - return -EINVAL; + ret = -EINVAL; } - return 0; + break; case KDGKBMETA: ucval = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT); setint: - return put_user(ucval, (int __user *)arg); + ret = put_user(ucval, (int __user *)arg); + break; case KDGETKEYCODE: case KDSETKEYCODE: if(!capable(CAP_SYS_TTY_CONFIG)) - perm=0; - return do_kbkeycode_ioctl(cmd, up, perm); + perm = 0; + ret = do_kbkeycode_ioctl(cmd, up, perm); + break; case KDGKBENT: case KDSKBENT: - return do_kdsk_ioctl(cmd, up, perm, kbd); + ret = do_kdsk_ioctl(cmd, up, perm, kbd); + break; case KDGKBSENT: case KDSKBSENT: - return do_kdgkb_ioctl(cmd, up, perm); + ret = do_kdgkb_ioctl(cmd, up, perm); + break; case KDGKBDIACR: { @@ -586,26 +604,31 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, struct kbdiacr diacr; int i; - if (put_user(accent_table_size, &a->kb_cnt)) - return -EFAULT; + if (put_user(accent_table_size, &a->kb_cnt)) { + ret = -EFAULT; + break; + } for (i = 0; i < accent_table_size; i++) { diacr.diacr = conv_uni_to_8bit(accent_table[i].diacr); diacr.base = conv_uni_to_8bit(accent_table[i].base); diacr.result = conv_uni_to_8bit(accent_table[i].result); - if (copy_to_user(a->kbdiacr + i, &diacr, sizeof(struct kbdiacr))) - return -EFAULT; + if (copy_to_user(a->kbdiacr + i, &diacr, sizeof(struct kbdiacr))) { + ret = -EFAULT; + break; + } } - return 0; + break; } case KDGKBDIACRUC: { struct kbdiacrsuc __user *a = up; if (put_user(accent_table_size, &a->kb_cnt)) - return -EFAULT; - if (copy_to_user(a->kbdiacruc, accent_table, accent_table_size*sizeof(struct kbdiacruc))) - return -EFAULT; - return 0; + ret = -EFAULT; + else if (copy_to_user(a->kbdiacruc, accent_table, + accent_table_size*sizeof(struct kbdiacruc))) + ret = -EFAULT; + break; } case KDSKBDIACR: @@ -616,20 +639,26 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, int i; if (!perm) - return -EPERM; - if (get_user(ct,&a->kb_cnt)) - return -EFAULT; - if (ct >= MAX_DIACR) - return -EINVAL; + goto eperm; + if (get_user(ct,&a->kb_cnt)) { + ret = -EFAULT; + break; + } + if (ct >= MAX_DIACR) { + ret = -EINVAL; + break; + } accent_table_size = ct; for (i = 0; i < ct; i++) { - if (copy_from_user(&diacr, a->kbdiacr + i, sizeof(struct kbdiacr))) - return -EFAULT; + if (copy_from_user(&diacr, a->kbdiacr + i, sizeof(struct kbdiacr))) { + ret = -EFAULT; + break; + } accent_table[i].diacr = conv_8bit_to_uni(diacr.diacr); accent_table[i].base = conv_8bit_to_uni(diacr.base); accent_table[i].result = conv_8bit_to_uni(diacr.result); } - return 0; + break; } case KDSKBDIACRUC: @@ -638,15 +667,19 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, unsigned int ct; if (!perm) - return -EPERM; - if (get_user(ct,&a->kb_cnt)) - return -EFAULT; - if (ct >= MAX_DIACR) - return -EINVAL; + goto eperm; + if (get_user(ct,&a->kb_cnt)) { + ret = -EFAULT; + break; + } + if (ct >= MAX_DIACR) { + ret = -EINVAL; + break; + } accent_table_size = ct; if (copy_from_user(accent_table, a->kbdiacruc, ct*sizeof(struct kbdiacruc))) - return -EFAULT; - return 0; + ret = -EFAULT; + break; } /* the ioctls below read/set the flags usually shown in the leds */ @@ -657,26 +690,29 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, case KDSKBLED: if (!perm) - return -EPERM; - if (arg & ~0x77) - return -EINVAL; + goto eperm; + if (arg & ~0x77) { + ret = -EINVAL; + break; + } kbd->ledflagstate = (arg & 7); kbd->default_ledflagstate = ((arg >> 4) & 7); set_leds(); - return 0; + break; /* the ioctls below only set the lights, not the functions */ /* for those, see KDGKBLED and KDSKBLED above */ case KDGETLED: ucval = getledstate(); setchar: - return put_user(ucval, (char __user *)arg); + ret = put_user(ucval, (char __user *)arg); + break; case KDSETLED: if (!perm) - return -EPERM; + goto eperm; setledstate(kbd, arg); - return 0; + break; /* * A process can indicate its willingness to accept signals @@ -688,16 +724,17 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, case KDSIGACCEPT: { if (!perm || !capable(CAP_KILL)) - return -EPERM; + goto eperm; if (!valid_signal(arg) || arg < 1 || arg == SIGKILL) - return -EINVAL; - - spin_lock_irq(&vt_spawn_con.lock); - put_pid(vt_spawn_con.pid); - vt_spawn_con.pid = get_pid(task_pid(current)); - vt_spawn_con.sig = arg; - spin_unlock_irq(&vt_spawn_con.lock); - return 0; + ret = -EINVAL; + else { + spin_lock_irq(&vt_spawn_con.lock); + put_pid(vt_spawn_con.pid); + vt_spawn_con.pid = get_pid(task_pid(current)); + vt_spawn_con.sig = arg; + spin_unlock_irq(&vt_spawn_con.lock); + } + break; } case VT_SETMODE: @@ -705,11 +742,15 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, struct vt_mode tmp; if (!perm) - return -EPERM; - if (copy_from_user(&tmp, up, sizeof(struct vt_mode))) - return -EFAULT; - if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS) - return -EINVAL; + goto eperm; + if (copy_from_user(&tmp, up, sizeof(struct vt_mode))) { + ret = -EFAULT; + goto out; + } + if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS) { + ret = -EINVAL; + goto out; + } acquire_console_sem(); vc->vt_mode = tmp; /* the frsig is ignored, so we set it to 0 */ @@ -719,7 +760,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, /* no switch is required -- saw@shade.msu.ru */ vc->vt_newvt = -1; release_console_sem(); - return 0; + break; } case VT_GETMODE: @@ -732,7 +773,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, release_console_sem(); rc = copy_to_user(up, &tmp, sizeof(struct vt_mode)); - return rc ? -EFAULT : 0; + if (rc) + ret = -EFAULT; + break; } /* @@ -746,12 +789,16 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, unsigned short state, mask; if (put_user(fg_console + 1, &vtstat->v_active)) - return -EFAULT; - state = 1; /* /dev/tty0 is always open */ - for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask; ++i, mask <<= 1) - if (VT_IS_IN_USE(i)) - state |= mask; - return put_user(state, &vtstat->v_state); + ret = -EFAULT; + else { + state = 1; /* /dev/tty0 is always open */ + for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask; + ++i, mask <<= 1) + if (VT_IS_IN_USE(i)) + state |= mask; + ret = put_user(state, &vtstat->v_state); + } + break; } /* @@ -771,27 +818,31 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, */ case VT_ACTIVATE: if (!perm) - return -EPERM; + goto eperm; if (arg == 0 || arg > MAX_NR_CONSOLES) - return -ENXIO; - arg--; - acquire_console_sem(); - i = vc_allocate(arg); - release_console_sem(); - if (i) - return i; - set_console(arg); - return 0; + ret = -ENXIO; + else { + arg--; + acquire_console_sem(); + ret = vc_allocate(arg); + release_console_sem(); + if (ret) + break; + set_console(arg); + } + break; /* * wait until the specified VT has been activated */ case VT_WAITACTIVE: if (!perm) - return -EPERM; + goto eperm; if (arg == 0 || arg > MAX_NR_CONSOLES) - return -ENXIO; - return vt_waitactive(arg-1); + ret = -ENXIO; + else + ret = vt_waitactive(arg - 1); + break; /* * If a vt is under process control, the kernel will not switch to it @@ -805,10 +856,12 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, */ case VT_RELDISP: if (!perm) - return -EPERM; - if (vc->vt_mode.mode != VT_PROCESS) - return -EINVAL; + goto eperm; + if (vc->vt_mode.mode != VT_PROCESS) { + ret = -EINVAL; + break; + } /* * Switching-from response */ @@ -829,10 +882,10 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, int newvt; newvt = vc->vt_newvt; vc->vt_newvt = -1; - i = vc_allocate(newvt); - if (i) { + ret = vc_allocate(newvt); + if (ret) { release_console_sem(); - return i; + break; } /* * When we actually do the console switch, @@ -841,31 +894,27 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, */ complete_change_console(vc_cons[newvt].d); } - } - - /* - * Switched-to response - */ - else - { + } else { + /* + * Switched-to response + */ /* * If it's just an ACK, ignore it */ - if (arg != VT_ACKACQ) { - release_console_sem(); - return -EINVAL; - } + if (arg != VT_ACKACQ) + ret = -EINVAL; } release_console_sem(); - - return 0; + break; /* * Disallocate memory associated to VT (but leave VT1) */ case VT_DISALLOCATE: - if (arg > MAX_NR_CONSOLES) - return -ENXIO; + if (arg > MAX_NR_CONSOLES) { + ret = -ENXIO; + break; + } if (arg == 0) { /* deallocate all unused consoles, but leave 0 */ acquire_console_sem(); @@ -877,14 +926,14 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, /* deallocate a single console, if possible */ arg--; if (VT_BUSY(arg)) - return -EBUSY; - if (arg) { /* leave 0 */ + ret = -EBUSY; + else if (arg) { /* leave 0 */ acquire_console_sem(); vc_deallocate(arg); release_console_sem(); } } - return 0; + break; case VT_RESIZE: { @@ -893,21 +942,21 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, ushort ll,cc; if (!perm) - return -EPERM; + goto eperm; if (get_user(ll, &vtsizes->v_rows) || get_user(cc, &vtsizes->v_cols)) - return -EFAULT; - - for (i = 0; i < MAX_NR_CONSOLES; i++) { - vc = vc_cons[i].d; + ret = -EFAULT; + else { + for (i = 0; i < MAX_NR_CONSOLES; i++) { + vc = vc_cons[i].d; - if (vc) { - vc->vc_resize_user = 1; - vc_lock_resize(vc_cons[i].d, cc, ll); + if (vc) { + vc->vc_resize_user = 1; + vc_lock_resize(vc_cons[i].d, cc, ll); + } } } - - return 0; + break; } case VT_RESIZEX: @@ -915,10 +964,13 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, struct vt_consize __user *vtconsize = up; ushort ll,cc,vlin,clin,vcol,ccol; if (!perm) - return -EPERM; + goto eperm; if (!access_ok(VERIFY_READ, vtconsize, - sizeof(struct vt_consize))) - return -EFAULT; + sizeof(struct vt_consize))) { + ret = -EFAULT; + break; + } + /* FIXME: Should check the copies properly */ __get_user(ll, &vtconsize->v_rows); __get_user(cc, &vtconsize->v_cols); __get_user(vlin, &vtconsize->v_vlin); @@ -928,21 +980,28 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, vlin = vlin ? vlin : vc->vc_scan_lines; if (clin) { if (ll) { - if (ll != vlin/clin) - return -EINVAL; /* Parameters don't add up */ + if (ll != vlin/clin) { + /* Parameters don't add up */ + ret = -EINVAL; + break; + } } else ll = vlin/clin; } if (vcol && ccol) { if (cc) { - if (cc != vcol/ccol) - return -EINVAL; + if (cc != vcol/ccol) { + ret = -EINVAL; + break; + } } else cc = vcol/ccol; } - if (clin > 32) - return -EINVAL; + if (clin > 32) { + ret = -EINVAL; + break; + } for (i = 0; i < MAX_NR_CONSOLES; i++) { if (!vc_cons[i].d) @@ -956,19 +1015,20 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, vc_resize(vc_cons[i].d, cc, ll); release_console_sem(); } - return 0; + break; } case PIO_FONT: { if (!perm) - return -EPERM; + goto eperm; op.op = KD_FONT_OP_SET; op.flags = KD_FONT_FLAG_OLD | KD_FONT_FLAG_DONT_RECALC; /* Compatibility */ op.width = 8; op.height = 0; op.charcount = 256; op.data = up; - return con_font_op(vc_cons[fg_console].d, &op); + ret = con_font_op(vc_cons[fg_console].d, &op); + break; } case GIO_FONT: { @@ -978,100 +1038,124 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, op.height = 32; op.charcount = 256; op.data = up; - return con_font_op(vc_cons[fg_console].d, &op); + ret = con_font_op(vc_cons[fg_console].d, &op); + break; } case PIO_CMAP: if (!perm) - return -EPERM; - return con_set_cmap(up); + ret = -EPERM; + else + ret = con_set_cmap(up); + break; case GIO_CMAP: - return con_get_cmap(up); + ret = con_get_cmap(up); + break; case PIO_FONTX: case GIO_FONTX: - return do_fontx_ioctl(cmd, up, perm, &op); + ret = do_fontx_ioctl(cmd, up, perm, &op); + break; case PIO_FONTRESET: { if (!perm) - return -EPERM; + goto eperm; #ifdef BROKEN_GRAPHICS_PROGRAMS /* With BROKEN_GRAPHICS_PROGRAMS defined, the default font is not saved. */ - return -ENOSYS; + ret = -ENOSYS; + break; #else { op.op = KD_FONT_OP_SET_DEFAULT; op.data = NULL; - i = con_font_op(vc_cons[fg_console].d, &op); - if (i) - return i; + ret = con_font_op(vc_cons[fg_console].d, &op); + if (ret) + break; con_set_default_unimap(vc_cons[fg_console].d); - return 0; + break; } #endif } case KDFONTOP: { - if (copy_from_user(&op, up, sizeof(op))) - return -EFAULT; + if (copy_from_user(&op, up, sizeof(op))) { + ret = -EFAULT; + break; + } if (!perm && op.op != KD_FONT_OP_GET) - return -EPERM; - i = con_font_op(vc, &op); - if (i) return i; + goto eperm; + ret = con_font_op(vc, &op); + if (ret) + break; if (copy_to_user(up, &op, sizeof(op))) - return -EFAULT; - return 0; + ret = -EFAULT; + break; } case PIO_SCRNMAP: if (!perm) - return -EPERM; - return con_set_trans_old(up); + ret = -EPERM; + else + ret = con_set_trans_old(up); + break; case GIO_SCRNMAP: - return con_get_trans_old(up); + ret = con_get_trans_old(up); + break; case PIO_UNISCRNMAP: if (!perm) - return -EPERM; - return con_set_trans_new(up); + ret = -EPERM; + else + ret = con_set_trans_new(up); + break; case GIO_UNISCRNMAP: - return con_get_trans_new(up); + ret = con_get_trans_new(up); + break; case PIO_UNIMAPCLR: { struct unimapinit ui; if (!perm) - return -EPERM; - i = copy_from_user(&ui, up, sizeof(struct unimapinit)); - if (i) return -EFAULT; - con_clear_unimap(vc, &ui); - return 0; + goto eperm; + ret = copy_from_user(&ui, up, sizeof(struct unimapinit)); + if (!ret) + con_clear_unimap(vc, &ui); + break; } case PIO_UNIMAP: case GIO_UNIMAP: - return do_unimap_ioctl(cmd, up, perm, vc); + ret = do_unimap_ioctl(cmd, up, perm, vc); + break; case VT_LOCKSWITCH: if (!capable(CAP_SYS_TTY_CONFIG)) - return -EPERM; + goto eperm; vt_dont_switch = 1; - return 0; + break; case VT_UNLOCKSWITCH: if (!capable(CAP_SYS_TTY_CONFIG)) - return -EPERM; + goto eperm; vt_dont_switch = 0; - return 0; + break; case VT_GETHIFONTMASK: - return put_user(vc->vc_hi_font_mask, (unsigned short __user *)arg); + ret = put_user(vc->vc_hi_font_mask, + (unsigned short __user *)arg); + break; default: - return -ENOIOCTLCMD; + ret = -ENOIOCTLCMD; } +out: + unlock_kernel(); + return ret; +eperm: + ret = -EPERM; + goto out; } /* diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 24c62b848bf..7f138c6195f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -382,7 +382,7 @@ fail: spin_unlock_irqrestore(&gpio_lock, flags); if (status) pr_debug("%s: gpio-%d status %d\n", - __FUNCTION__, gpio, status); + __func__, gpio, status); return status; } EXPORT_SYMBOL_GPL(gpio_direction_input); @@ -420,7 +420,7 @@ fail: spin_unlock_irqrestore(&gpio_lock, flags); if (status) pr_debug("%s: gpio-%d status %d\n", - __FUNCTION__, gpio, status); + __func__, gpio, status); return status; } EXPORT_SYMBOL_GPL(gpio_direction_output); diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index ed71a8bc70d..5c8b6e0ff47 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -224,7 +224,7 @@ static int ads7828_detect(struct i2c_adapter *adapter, int address, int kind) if (in_data & 0xF000) { printk(KERN_DEBUG "%s : Doesn't look like an ads7828 device\n", - __FUNCTION__); + __func__); goto exit_free; } } diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index e1a3a79ab3f..7ff71ba7b7c 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -46,7 +46,7 @@ struct serport { static int serport_serio_write(struct serio *serio, unsigned char data) { struct serport *serport = serio->port_data; - return -(serport->tty->driver->write(serport->tty, &data, 1) != 1); + return -(serport->tty->ops->write(serport->tty, &data, 1) != 1); } static int serport_serio_open(struct serio *serio) diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 24c6b7ca62b..6ca0bb949ad 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -1111,11 +1111,12 @@ static int capinc_tty_write(struct tty_struct * tty, return count; } -static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch) +static int capinc_tty_put_char(struct tty_struct *tty, unsigned char ch) { struct capiminor *mp = (struct capiminor *)tty->driver_data; struct sk_buff *skb; unsigned long flags; + int ret = 1; #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_put_char(%u)\n", ch); @@ -1125,7 +1126,7 @@ static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch) #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_tty_put_char: mp or mp->ncci NULL\n"); #endif - return; + return 0; } spin_lock_irqsave(&workaround_lock, flags); @@ -1134,7 +1135,7 @@ static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch) if (skb_tailroom(skb) > 0) { *(skb_put(skb, 1)) = ch; spin_unlock_irqrestore(&workaround_lock, flags); - return; + return 1; } mp->ttyskb = NULL; skb_queue_tail(&mp->outqueue, skb); @@ -1148,8 +1149,10 @@ static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch) mp->ttyskb = skb; } else { printk(KERN_ERR "capinc_put_char: char %u lost\n", ch); + ret = 0; } spin_unlock_irqrestore(&workaround_lock, flags); + return ret; } static void capinc_tty_flush_chars(struct tty_struct *tty) diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index fceeb1d5768..45d1ee93cd3 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -68,10 +68,10 @@ static int write_modem(struct cardstate *cs) struct tty_struct *tty = cs->hw.ser->tty; struct bc_state *bcs = &cs->bcs[0]; /* only one channel */ struct sk_buff *skb = bcs->tx_skb; - int sent; + int sent = -EOPNOTSUPP; if (!tty || !tty->driver || !skb) - return -EFAULT; + return -EINVAL; if (!skb->len) { dev_kfree_skb_any(skb); @@ -80,7 +80,8 @@ static int write_modem(struct cardstate *cs) } set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - sent = tty->driver->write(tty, skb->data, skb->len); + if (tty->ops->write) + sent = tty->ops->write(tty, skb->data, skb->len); gig_dbg(DEBUG_OUTPUT, "write_modem: sent %d", sent); if (sent < 0) { /* error */ @@ -120,7 +121,7 @@ static int send_cb(struct cardstate *cs) if (cb->len) { set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - sent = tty->driver->write(tty, cb->buf + cb->offset, cb->len); + sent = tty->ops->write(tty, cb->buf + cb->offset, cb->len); if (sent < 0) { /* error */ gig_dbg(DEBUG_OUTPUT, "send_cb: write error %d", sent); @@ -440,14 +441,14 @@ static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state, unsi struct tty_struct *tty = cs->hw.ser->tty; unsigned int set, clear; - if (!tty || !tty->driver || !tty->driver->tiocmset) - return -EFAULT; + if (!tty || !tty->driver || !tty->ops->tiocmset) + return -EINVAL; set = new_state & ~old_state; clear = old_state & ~new_state; if (!set && !clear) return 0; gig_dbg(DEBUG_IF, "tiocmset set %x clear %x", set, clear); - return tty->driver->tiocmset(tty, NULL, set, clear); + return tty->ops->tiocmset(tty, NULL, set, clear); } static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag) diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 8af0df1d5b8..1a2222cbb80 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1352,12 +1352,14 @@ isdn_tty_tiocmget(struct tty_struct *tty, struct file *file) if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; + lock_kernel(); #ifdef ISDN_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line); #endif control = info->mcr; status = info->msr; + unlock_kernel(); return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) @@ -1381,6 +1383,7 @@ isdn_tty_tiocmset(struct tty_struct *tty, struct file *file, printk(KERN_DEBUG "ttyI%d ioctl TIOCMxxx: %x %x\n", info->line, set, clear); #endif + lock_kernel(); if (set & TIOCM_RTS) info->mcr |= UART_MCR_RTS; if (set & TIOCM_DTR) { @@ -1402,6 +1405,7 @@ isdn_tty_tiocmset(struct tty_struct *tty, struct file *file, isdn_tty_modem_hup(info, 1); } } + unlock_kernel(); return 0; } @@ -1435,21 +1439,6 @@ isdn_tty_ioctl(struct tty_struct *tty, struct file *file, return retval; tty_wait_until_sent(tty, 0); return 0; - case TIOCGSOFTCAR: -#ifdef ISDN_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line); -#endif - return put_user(C_CLOCAL(tty) ? 1 : 0, (ulong __user *) arg); - case TIOCSSOFTCAR: -#ifdef ISDN_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line); -#endif - if (get_user(arg, (ulong __user *) arg)) - return -EFAULT; - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - return 0; case TIOCSERGETLSR: /* Get line status register */ #ifdef ISDN_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line); @@ -1472,13 +1461,14 @@ isdn_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) if (!old_termios) isdn_tty_change_speed(info); else { - if (tty->termios->c_cflag == old_termios->c_cflag) + if (tty->termios->c_cflag == old_termios->c_cflag && + tty->termios->c_ispeed == old_termios->c_ispeed && + tty->termios->c_ospeed == old_termios->c_ospeed) return; isdn_tty_change_speed(info); if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios->c_cflag & CRTSCTS)) tty->hw_stopped = 0; - } } } @@ -1718,9 +1708,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) } dev->modempoll--; isdn_tty_shutdown(info); - - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + isdn_tty_flush_buffer(tty); tty_ldisc_flush(tty); info->tty = NULL; info->ncarrier = 0; diff --git a/drivers/md/md.c b/drivers/md/md.c index bb3e4b1cb77..83eb78b0013 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -276,6 +276,7 @@ static mddev_t * mddev_find(dev_t unit) init_waitqueue_head(&new->sb_wait); new->reshape_position = MaxSector; new->resync_max = MaxSector; + new->level = LEVEL_NONE; new->queue = blk_alloc_queue(GFP_KERNEL); if (!new->queue) { @@ -1369,6 +1370,11 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) MD_BUG(); return -EINVAL; } + + /* prevent duplicates */ + if (find_rdev(mddev, rdev->bdev->bd_dev)) + return -EEXIST; + /* make sure rdev->size exceeds mddev->size */ if (rdev->size && (mddev->size == 0 || rdev->size < mddev->size)) { if (mddev->pers) { @@ -1652,6 +1658,8 @@ static void md_update_sb(mddev_t * mddev, int force_change) int sync_req; int nospares = 0; + if (mddev->external) + return; repeat: spin_lock_irq(&mddev->write_lock); @@ -1820,6 +1828,10 @@ state_show(mdk_rdev_t *rdev, char *page) len += sprintf(page+len, "%swrite_mostly",sep); sep = ","; } + if (test_bit(Blocked, &rdev->flags)) { + len += sprintf(page+len, "%sblocked", sep); + sep = ","; + } if (!test_bit(Faulty, &rdev->flags) && !test_bit(In_sync, &rdev->flags)) { len += sprintf(page+len, "%sspare", sep); @@ -1836,6 +1848,8 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len) * remove - disconnects the device * writemostly - sets write_mostly * -writemostly - clears write_mostly + * blocked - sets the Blocked flag + * -blocked - clears the Blocked flag */ int err = -EINVAL; if (cmd_match(buf, "faulty") && rdev->mddev->pers) { @@ -1858,6 +1872,16 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len) } else if (cmd_match(buf, "-writemostly")) { clear_bit(WriteMostly, &rdev->flags); err = 0; + } else if (cmd_match(buf, "blocked")) { + set_bit(Blocked, &rdev->flags); + err = 0; + } else if (cmd_match(buf, "-blocked")) { + clear_bit(Blocked, &rdev->flags); + wake_up(&rdev->blocked_wait); + set_bit(MD_RECOVERY_NEEDED, &rdev->mddev->recovery); + md_wakeup_thread(rdev->mddev->thread); + + err = 0; } return err ? err : len; } @@ -2097,7 +2121,7 @@ rdev_attr_store(struct kobject *kobj, struct attribute *attr, rv = -EBUSY; else rv = entry->store(rdev, page, length); - mddev_unlock(rdev->mddev); + mddev_unlock(mddev); } return rv; } @@ -2186,7 +2210,9 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi goto abort_free; } } + INIT_LIST_HEAD(&rdev->same_set); + init_waitqueue_head(&rdev->blocked_wait); return rdev; @@ -2457,7 +2483,6 @@ resync_start_show(mddev_t *mddev, char *page) static ssize_t resync_start_store(mddev_t *mddev, const char *buf, size_t len) { - /* can only set chunk_size if array is not yet active */ char *e; unsigned long long n = simple_strtoull(buf, &e, 10); @@ -2591,15 +2616,20 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) err = do_md_stop(mddev, 1); else { mddev->ro = 1; + set_disk_ro(mddev->gendisk, 1); err = do_md_run(mddev); } break; case read_auto: - /* stopping an active array */ if (mddev->pers) { - err = do_md_stop(mddev, 1); - if (err == 0) - mddev->ro = 2; /* FIXME mark devices writable */ + if (mddev->ro != 1) + err = do_md_stop(mddev, 1); + else + err = restart_array(mddev); + if (err == 0) { + mddev->ro = 2; + set_disk_ro(mddev->gendisk, 0); + } } else { mddev->ro = 2; err = do_md_run(mddev); @@ -2612,6 +2642,8 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) if (atomic_read(&mddev->writes_pending) == 0) { if (mddev->in_sync == 0) { mddev->in_sync = 1; + if (mddev->safemode == 1) + mddev->safemode = 0; if (mddev->persistent) set_bit(MD_CHANGE_CLEAN, &mddev->flags); @@ -2635,6 +2667,7 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) err = 0; } else { mddev->ro = 0; + set_disk_ro(mddev->gendisk, 0); err = do_md_run(mddev); } break; @@ -3712,6 +3745,30 @@ static int do_md_stop(mddev_t * mddev, int mode) mddev->reshape_position = MaxSector; mddev->external = 0; mddev->persistent = 0; + mddev->level = LEVEL_NONE; + mddev->clevel[0] = 0; + mddev->flags = 0; + mddev->ro = 0; + mddev->metadata_type[0] = 0; + mddev->chunk_size = 0; + mddev->ctime = mddev->utime = 0; + mddev->layout = 0; + mddev->max_disks = 0; + mddev->events = 0; + mddev->delta_disks = 0; + mddev->new_level = LEVEL_NONE; + mddev->new_layout = 0; + mddev->new_chunk = 0; + mddev->curr_resync = 0; + mddev->resync_mismatches = 0; + mddev->suspend_lo = mddev->suspend_hi = 0; + mddev->sync_speed_min = mddev->sync_speed_max = 0; + mddev->recovery = 0; + mddev->in_sync = 0; + mddev->changed = 0; + mddev->degraded = 0; + mddev->barriers_work = 0; + mddev->safemode = 0; } else if (mddev->pers) printk(KERN_INFO "md: %s switched to read-only mode.\n", @@ -4919,6 +4976,9 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev) if (!rdev || test_bit(Faulty, &rdev->flags)) return; + + if (mddev->external) + set_bit(Blocked, &rdev->flags); /* dprintk("md_error dev:%s, rdev:(%d:%d), (caller: %p,%p,%p,%p).\n", mdname(mddev), @@ -5365,6 +5425,8 @@ void md_write_start(mddev_t *mddev, struct bio *bi) md_wakeup_thread(mddev->sync_thread); } atomic_inc(&mddev->writes_pending); + if (mddev->safemode == 1) + mddev->safemode = 0; if (mddev->in_sync) { spin_lock_irq(&mddev->write_lock); if (mddev->in_sync) { @@ -5719,7 +5781,7 @@ static int remove_and_add_spares(mddev_t *mddev) rdev_for_each(rdev, rtmp, mddev) if (rdev->raid_disk >= 0 && - !mddev->external && + !test_bit(Blocked, &rdev->flags) && (test_bit(Faulty, &rdev->flags) || ! test_bit(In_sync, &rdev->flags)) && atomic_read(&rdev->nr_pending)==0) { @@ -5789,7 +5851,7 @@ void md_check_recovery(mddev_t *mddev) return; if (signal_pending(current)) { - if (mddev->pers->sync_request) { + if (mddev->pers->sync_request && !mddev->external) { printk(KERN_INFO "md: %s in immediate safe mode\n", mdname(mddev)); mddev->safemode = 2; @@ -5801,7 +5863,7 @@ void md_check_recovery(mddev_t *mddev) (mddev->flags && !mddev->external) || test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) || test_bit(MD_RECOVERY_DONE, &mddev->recovery) || - (mddev->safemode == 1) || + (mddev->external == 0 && mddev->safemode == 1) || (mddev->safemode == 2 && ! atomic_read(&mddev->writes_pending) && !mddev->in_sync && mddev->recovery_cp == MaxSector) )) @@ -5810,16 +5872,20 @@ void md_check_recovery(mddev_t *mddev) if (mddev_trylock(mddev)) { int spares = 0; - spin_lock_irq(&mddev->write_lock); - if (mddev->safemode && !atomic_read(&mddev->writes_pending) && - !mddev->in_sync && mddev->recovery_cp == MaxSector) { - mddev->in_sync = 1; - if (mddev->persistent) - set_bit(MD_CHANGE_CLEAN, &mddev->flags); + if (!mddev->external) { + spin_lock_irq(&mddev->write_lock); + if (mddev->safemode && + !atomic_read(&mddev->writes_pending) && + !mddev->in_sync && + mddev->recovery_cp == MaxSector) { + mddev->in_sync = 1; + if (mddev->persistent) + set_bit(MD_CHANGE_CLEAN, &mddev->flags); + } + if (mddev->safemode == 1) + mddev->safemode = 0; + spin_unlock_irq(&mddev->write_lock); } - if (mddev->safemode == 1) - mddev->safemode = 0; - spin_unlock_irq(&mddev->write_lock); if (mddev->flags) md_update_sb(mddev, 0); @@ -5914,6 +5980,16 @@ void md_check_recovery(mddev_t *mddev) } } +void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev) +{ + sysfs_notify(&rdev->kobj, NULL, "state"); + wait_event_timeout(rdev->blocked_wait, + !test_bit(Blocked, &rdev->flags), + msecs_to_jiffies(5000)); + rdev_dec_pending(rdev, mddev); +} +EXPORT_SYMBOL(md_wait_for_blocked_rdev); + static int md_notify_reboot(struct notifier_block *this, unsigned long code, void *x) { diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 9fd473a6dbf..6778b7cb39b 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -773,7 +773,6 @@ static int make_request(struct request_queue *q, struct bio * bio) r1bio_t *r1_bio; struct bio *read_bio; int i, targets = 0, disks; - mdk_rdev_t *rdev; struct bitmap *bitmap = mddev->bitmap; unsigned long flags; struct bio_list bl; @@ -781,6 +780,7 @@ static int make_request(struct request_queue *q, struct bio * bio) const int rw = bio_data_dir(bio); const int do_sync = bio_sync(bio); int do_barriers; + mdk_rdev_t *blocked_rdev; /* * Register the new request and wait if the reconstruction @@ -862,10 +862,17 @@ static int make_request(struct request_queue *q, struct bio * bio) first = 0; } #endif + retry_write: + blocked_rdev = NULL; rcu_read_lock(); for (i = 0; i < disks; i++) { - if ((rdev=rcu_dereference(conf->mirrors[i].rdev)) != NULL && - !test_bit(Faulty, &rdev->flags)) { + mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[i].rdev); + if (rdev && unlikely(test_bit(Blocked, &rdev->flags))) { + atomic_inc(&rdev->nr_pending); + blocked_rdev = rdev; + break; + } + if (rdev && !test_bit(Faulty, &rdev->flags)) { atomic_inc(&rdev->nr_pending); if (test_bit(Faulty, &rdev->flags)) { rdev_dec_pending(rdev, mddev); @@ -878,6 +885,20 @@ static int make_request(struct request_queue *q, struct bio * bio) } rcu_read_unlock(); + if (unlikely(blocked_rdev)) { + /* Wait for this device to become unblocked */ + int j; + + for (j = 0; j < i; j++) + if (r1_bio->bios[j]) + rdev_dec_pending(conf->mirrors[j].rdev, mddev); + + allow_barrier(conf); + md_wait_for_blocked_rdev(blocked_rdev, mddev); + wait_barrier(conf); + goto retry_write; + } + BUG_ON(targets == 0); /* we never fail the last device */ if (targets < conf->raid_disks) { diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 1e96aa3ff51..5938fa96292 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -790,6 +790,7 @@ static int make_request(struct request_queue *q, struct bio * bio) const int do_sync = bio_sync(bio); struct bio_list bl; unsigned long flags; + mdk_rdev_t *blocked_rdev; if (unlikely(bio_barrier(bio))) { bio_endio(bio, -EOPNOTSUPP); @@ -879,17 +880,23 @@ static int make_request(struct request_queue *q, struct bio * bio) /* * WRITE: */ - /* first select target devices under spinlock and + /* first select target devices under rcu_lock and * inc refcount on their rdev. Record them by setting * bios[x] to bio */ raid10_find_phys(conf, r10_bio); + retry_write: + blocked_rdev = 0; rcu_read_lock(); for (i = 0; i < conf->copies; i++) { int d = r10_bio->devs[i].devnum; mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[d].rdev); - if (rdev && - !test_bit(Faulty, &rdev->flags)) { + if (rdev && unlikely(test_bit(Blocked, &rdev->flags))) { + atomic_inc(&rdev->nr_pending); + blocked_rdev = rdev; + break; + } + if (rdev && !test_bit(Faulty, &rdev->flags)) { atomic_inc(&rdev->nr_pending); r10_bio->devs[i].bio = bio; } else { @@ -899,6 +906,22 @@ static int make_request(struct request_queue *q, struct bio * bio) } rcu_read_unlock(); + if (unlikely(blocked_rdev)) { + /* Have to wait for this device to get unblocked, then retry */ + int j; + int d; + + for (j = 0; j < i; j++) + if (r10_bio->devs[j].bio) { + d = r10_bio->devs[j].devnum; + rdev_dec_pending(conf->mirrors[d].rdev, mddev); + } + allow_barrier(conf); + md_wait_for_blocked_rdev(blocked_rdev, mddev); + wait_barrier(conf); + goto retry_write; + } + atomic_set(&r10_bio->remaining, 0); bio_list_init(&bl); diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 968dacaced6..087eee0cb80 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -2607,6 +2607,7 @@ static void handle_stripe_expansion(raid5_conf_t *conf, struct stripe_head *sh, } } + /* * handle_stripe - do things to a stripe. * @@ -2632,6 +2633,7 @@ static void handle_stripe5(struct stripe_head *sh) struct stripe_head_state s; struct r5dev *dev; unsigned long pending = 0; + mdk_rdev_t *blocked_rdev = NULL; memset(&s, 0, sizeof(s)); pr_debug("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d " @@ -2691,6 +2693,11 @@ static void handle_stripe5(struct stripe_head *sh) if (dev->written) s.written++; rdev = rcu_dereference(conf->disks[i].rdev); + if (rdev && unlikely(test_bit(Blocked, &rdev->flags))) { + blocked_rdev = rdev; + atomic_inc(&rdev->nr_pending); + break; + } if (!rdev || !test_bit(In_sync, &rdev->flags)) { /* The ReadError flag will just be confusing now */ clear_bit(R5_ReadError, &dev->flags); @@ -2705,6 +2712,11 @@ static void handle_stripe5(struct stripe_head *sh) } rcu_read_unlock(); + if (unlikely(blocked_rdev)) { + set_bit(STRIPE_HANDLE, &sh->state); + goto unlock; + } + if (s.to_fill && !test_and_set_bit(STRIPE_OP_BIOFILL, &sh->ops.pending)) sh->ops.count++; @@ -2894,8 +2906,13 @@ static void handle_stripe5(struct stripe_head *sh) if (sh->ops.count) pending = get_stripe_work(sh); + unlock: spin_unlock(&sh->lock); + /* wait for this device to become unblocked */ + if (unlikely(blocked_rdev)) + md_wait_for_blocked_rdev(blocked_rdev, conf->mddev); + if (pending) raid5_run_ops(sh, pending); @@ -2912,6 +2929,7 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) struct stripe_head_state s; struct r6_state r6s; struct r5dev *dev, *pdev, *qdev; + mdk_rdev_t *blocked_rdev = NULL; r6s.qd_idx = raid6_next_disk(pd_idx, disks); pr_debug("handling stripe %llu, state=%#lx cnt=%d, " @@ -2975,6 +2993,11 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) if (dev->written) s.written++; rdev = rcu_dereference(conf->disks[i].rdev); + if (rdev && unlikely(test_bit(Blocked, &rdev->flags))) { + blocked_rdev = rdev; + atomic_inc(&rdev->nr_pending); + break; + } if (!rdev || !test_bit(In_sync, &rdev->flags)) { /* The ReadError flag will just be confusing now */ clear_bit(R5_ReadError, &dev->flags); @@ -2989,6 +3012,11 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) set_bit(R5_Insync, &dev->flags); } rcu_read_unlock(); + + if (unlikely(blocked_rdev)) { + set_bit(STRIPE_HANDLE, &sh->state); + goto unlock; + } pr_debug("locked=%d uptodate=%d to_read=%d" " to_write=%d failed=%d failed_num=%d,%d\n", s.locked, s.uptodate, s.to_read, s.to_write, s.failed, @@ -3094,8 +3122,13 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending)) handle_stripe_expansion(conf, sh, &r6s); + unlock: spin_unlock(&sh->lock); + /* wait for this device to become unblocked */ + if (unlikely(blocked_rdev)) + md_wait_for_blocked_rdev(blocked_rdev, conf->mddev); + return_io(return_bi); for (i=disks; i-- ;) { diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index 8e744823064..351b98b9b30 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -2,6 +2,7 @@ saa7146-objs := saa7146_i2c.o saa7146_core.o saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o ir-common-objs := ir-functions.o ir-keymaps.o +obj-y += tuners/ obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o obj-$(CONFIG_VIDEO_IR) += ir-common.o diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h index 03816b73f84..27da7b42327 100644 --- a/drivers/media/video/bt8xx/bttvp.h +++ b/drivers/media/video/bt8xx/bttvp.h @@ -81,8 +81,6 @@ /* Limits scaled width, which must be a multiple of 4. */ #define MAX_HACTIVE (0x3FF & -4) -#define clamp(x, low, high) min (max (low, x), high) - #define BTTV_NORMS (\ V4L2_STD_PAL | V4L2_STD_PAL_N | \ V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \ diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c index 64819353276..17f542dfb36 100644 --- a/drivers/media/video/usbvideo/vicam.c +++ b/drivers/media/video/usbvideo/vicam.c @@ -70,12 +70,6 @@ #define VICAM_HEADER_SIZE 64 -#define clamp( x, l, h ) max_t( __typeof__( x ), \ - ( l ), \ - min_t( __typeof__( x ), \ - ( h ), \ - ( x ) ) ) - /* Not sure what all the bytes in these char * arrays do, but they're necessary to make * the camera work. diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index f6f2d960cad..ef8a492766a 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -132,7 +132,7 @@ static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) if (iter >= MAX_ASIC_ISR_LOOPS) printk(KERN_ERR "%s: interrupt processing overrun\n", - __FUNCTION__); + __func__); } static inline int asic3_irq_to_bank(struct asic3 *asic, int irq) @@ -409,7 +409,7 @@ int asic3_gpio_get_value(struct asic3 *asic, unsigned gpio) return asic3_get_gpio_d(asic, Status) & mask; default: printk(KERN_ERR "%s: invalid GPIO value 0x%x", - __FUNCTION__, gpio); + __func__, gpio); return -EINVAL; } } @@ -437,7 +437,7 @@ void asic3_gpio_set_value(struct asic3 *asic, unsigned gpio, int val) return; default: printk(KERN_ERR "%s: invalid GPIO value 0x%x", - __FUNCTION__, gpio); + __func__, gpio); return; } } diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 6e655b4c668..2fe64734d8a 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -349,11 +349,11 @@ int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to) mode &= 3; /* get current power mode */ if (unit >= ARRAY_SIZE(sm->unit_power)) { - dev_err(dev, "%s: bad unit %d\n", __FUNCTION__, unit); + dev_err(dev, "%s: bad unit %d\n", __func__, unit); goto already; } - dev_dbg(sm->dev, "%s: unit %d, cur %d, to %d\n", __FUNCTION__, unit, + dev_dbg(sm->dev, "%s: unit %d, cur %d, to %d\n", __func__, unit, sm->unit_power[unit], to); if (to == 0 && sm->unit_power[unit] == 0) { diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index 6d6286c4eea..30a1af857c7 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -132,7 +132,7 @@ static int send_ack; static int final_ack; static int hw_break_val; static int hw_break_val2; -#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) +#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_SPARC) static int arch_needs_sstep_emulation = 1; #else static int arch_needs_sstep_emulation; diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c index 9c6573419f5..fdfb2b2cb73 100644 --- a/drivers/net/3c505.c +++ b/drivers/net/3c505.c @@ -670,7 +670,7 @@ static irqreturn_t elp_interrupt(int irq, void *dev_id) memcpy(adapter->current_dma.target, adapter->dma_buffer, adapter->current_dma.length); } skb->protocol = eth_type_trans(skb,dev); - adapter->stats.rx_bytes += skb->len; + dev->stats.rx_bytes += skb->len; netif_rx(skb); dev->last_rx = jiffies; } @@ -773,12 +773,12 @@ static irqreturn_t elp_interrupt(int irq, void *dev_id) * received board statistics */ case CMD_NETWORK_STATISTICS_RESPONSE: - adapter->stats.rx_packets += adapter->irx_pcb.data.netstat.tot_recv; - adapter->stats.tx_packets += adapter->irx_pcb.data.netstat.tot_xmit; - adapter->stats.rx_crc_errors += adapter->irx_pcb.data.netstat.err_CRC; - adapter->stats.rx_frame_errors += adapter->irx_pcb.data.netstat.err_align; - adapter->stats.rx_fifo_errors += adapter->irx_pcb.data.netstat.err_ovrrun; - adapter->stats.rx_over_errors += adapter->irx_pcb.data.netstat.err_res; + dev->stats.rx_packets += adapter->irx_pcb.data.netstat.tot_recv; + dev->stats.tx_packets += adapter->irx_pcb.data.netstat.tot_xmit; + dev->stats.rx_crc_errors += adapter->irx_pcb.data.netstat.err_CRC; + dev->stats.rx_frame_errors += adapter->irx_pcb.data.netstat.err_align; + dev->stats.rx_fifo_errors += adapter->irx_pcb.data.netstat.err_ovrrun; + dev->stats.rx_over_errors += adapter->irx_pcb.data.netstat.err_res; adapter->got[CMD_NETWORK_STATISTICS] = 1; if (elp_debug >= 3) printk(KERN_DEBUG "%s: interrupt - statistics response received\n", dev->name); @@ -794,11 +794,11 @@ static irqreturn_t elp_interrupt(int irq, void *dev_id) break; switch (adapter->irx_pcb.data.xmit_resp.c_stat) { case 0xffff: - adapter->stats.tx_aborted_errors++; + dev->stats.tx_aborted_errors++; printk(KERN_INFO "%s: transmit timed out, network cable problem?\n", dev->name); break; case 0xfffe: - adapter->stats.tx_fifo_errors++; + dev->stats.tx_fifo_errors++; printk(KERN_INFO "%s: transmit timed out, FIFO underrun\n", dev->name); break; } @@ -986,7 +986,7 @@ static bool send_packet(struct net_device *dev, struct sk_buff *skb) return false; } - adapter->stats.tx_bytes += nlen; + dev->stats.tx_bytes += nlen; /* * send the adapter a transmit packet command. Ignore segment and offset @@ -1041,7 +1041,6 @@ static bool send_packet(struct net_device *dev, struct sk_buff *skb) static void elp_timeout(struct net_device *dev) { - elp_device *adapter = dev->priv; int stat; stat = inb_status(dev->base_addr); @@ -1049,7 +1048,7 @@ static void elp_timeout(struct net_device *dev) if (elp_debug >= 1) printk(KERN_DEBUG "%s: status %#02x\n", dev->name, stat); dev->trans_start = jiffies; - adapter->stats.tx_dropped++; + dev->stats.tx_dropped++; netif_wake_queue(dev); } @@ -1113,7 +1112,7 @@ static struct net_device_stats *elp_get_stats(struct net_device *dev) /* If the device is closed, just return the latest stats we have, - we cannot ask from the adapter without interrupts */ if (!netif_running(dev)) - return &adapter->stats; + return &dev->stats; /* send a get statistics command to the board */ adapter->tx_pcb.command = CMD_NETWORK_STATISTICS; @@ -1126,12 +1125,12 @@ static struct net_device_stats *elp_get_stats(struct net_device *dev) while (adapter->got[CMD_NETWORK_STATISTICS] == 0 && time_before(jiffies, timeout)); if (time_after_eq(jiffies, timeout)) { TIMEOUT_MSG(__LINE__); - return &adapter->stats; + return &dev->stats; } } /* statistics are now up to date */ - return &adapter->stats; + return &dev->stats; } @@ -1571,7 +1570,6 @@ static int __init elplus_setup(struct net_device *dev) dev->set_multicast_list = elp_set_mc_list; /* local */ dev->ethtool_ops = &netdev_ethtool_ops; /* local */ - memset(&(adapter->stats), 0, sizeof(struct net_device_stats)); dev->mem_start = dev->mem_end = 0; err = register_netdev(dev); diff --git a/drivers/net/3c505.h b/drivers/net/3c505.h index 1910cb1dc78..04df2a9002b 100644 --- a/drivers/net/3c505.h +++ b/drivers/net/3c505.h @@ -264,7 +264,6 @@ typedef struct { pcb_struct rx_pcb; /* PCB for foreground receiving */ pcb_struct itx_pcb; /* PCB for background sending */ pcb_struct irx_pcb; /* PCB for background receiving */ - struct net_device_stats stats; void *dma_buffer; diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c index 54dac0696d9..e6c545fe5f5 100644 --- a/drivers/net/3c509.c +++ b/drivers/net/3c509.c @@ -167,7 +167,6 @@ enum RxFilter { enum el3_cardtype { EL3_ISA, EL3_PNP, EL3_MCA, EL3_EISA }; struct el3_private { - struct net_device_stats stats; spinlock_t lock; /* skb send-queue */ int head, size; @@ -794,7 +793,6 @@ el3_open(struct net_device *dev) static void el3_tx_timeout (struct net_device *dev) { - struct el3_private *lp = netdev_priv(dev); int ioaddr = dev->base_addr; /* Transmitter timeout, serious problems. */ @@ -802,7 +800,7 @@ el3_tx_timeout (struct net_device *dev) "Tx FIFO room %d.\n", dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS), inw(ioaddr + TX_FREE)); - lp->stats.tx_errors++; + dev->stats.tx_errors++; dev->trans_start = jiffies; /* Issue TX_RESET and TX_START commands. */ outw(TxReset, ioaddr + EL3_CMD); @@ -820,7 +818,7 @@ el3_start_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue (dev); - lp->stats.tx_bytes += skb->len; + dev->stats.tx_bytes += skb->len; if (el3_debug > 4) { printk(KERN_DEBUG "%s: el3_start_xmit(length = %u) called, status %4.4x.\n", @@ -881,7 +879,7 @@ el3_start_xmit(struct sk_buff *skb, struct net_device *dev) int i = 4; while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { - if (tx_status & 0x38) lp->stats.tx_aborted_errors++; + if (tx_status & 0x38) dev->stats.tx_aborted_errors++; if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ @@ -931,12 +929,11 @@ el3_interrupt(int irq, void *dev_id) outw(AckIntr | RxEarly, ioaddr + EL3_CMD); } if (status & TxComplete) { /* Really Tx error. */ - struct el3_private *lp = netdev_priv(dev); short tx_status; int i = 4; while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { - if (tx_status & 0x38) lp->stats.tx_aborted_errors++; + if (tx_status & 0x38) dev->stats.tx_aborted_errors++; if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ @@ -1002,7 +999,7 @@ el3_get_stats(struct net_device *dev) spin_lock_irqsave(&lp->lock, flags); update_stats(dev); spin_unlock_irqrestore(&lp->lock, flags); - return &lp->stats; + return &dev->stats; } /* Update statistics. We change to register window 6, so this should be run @@ -1012,7 +1009,6 @@ el3_get_stats(struct net_device *dev) */ static void update_stats(struct net_device *dev) { - struct el3_private *lp = netdev_priv(dev); int ioaddr = dev->base_addr; if (el3_debug > 5) @@ -1021,13 +1017,13 @@ static void update_stats(struct net_device *dev) outw(StatsDisable, ioaddr + EL3_CMD); /* Switch to the stats window, and read everything. */ EL3WINDOW(6); - lp->stats.tx_carrier_errors += inb(ioaddr + 0); - lp->stats.tx_heartbeat_errors += inb(ioaddr + 1); + dev->stats.tx_carrier_errors += inb(ioaddr + 0); + dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); /* Multiple collisions. */ inb(ioaddr + 2); - lp->stats.collisions += inb(ioaddr + 3); - lp->stats.tx_window_errors += inb(ioaddr + 4); - lp->stats.rx_fifo_errors += inb(ioaddr + 5); - lp->stats.tx_packets += inb(ioaddr + 6); + dev->stats.collisions += inb(ioaddr + 3); + dev->stats.tx_window_errors += inb(ioaddr + 4); + dev->stats.rx_fifo_errors += inb(ioaddr + 5); + dev->stats.tx_packets += inb(ioaddr + 6); /* Rx packets */ inb(ioaddr + 7); /* Tx deferrals */ inb(ioaddr + 8); inw(ioaddr + 10); /* Total Rx and Tx octets. */ @@ -1042,7 +1038,6 @@ static void update_stats(struct net_device *dev) static int el3_rx(struct net_device *dev) { - struct el3_private *lp = netdev_priv(dev); int ioaddr = dev->base_addr; short rx_status; @@ -1054,21 +1049,21 @@ el3_rx(struct net_device *dev) short error = rx_status & 0x3800; outw(RxDiscard, ioaddr + EL3_CMD); - lp->stats.rx_errors++; + dev->stats.rx_errors++; switch (error) { - case 0x0000: lp->stats.rx_over_errors++; break; - case 0x0800: lp->stats.rx_length_errors++; break; - case 0x1000: lp->stats.rx_frame_errors++; break; - case 0x1800: lp->stats.rx_length_errors++; break; - case 0x2000: lp->stats.rx_frame_errors++; break; - case 0x2800: lp->stats.rx_crc_errors++; break; + case 0x0000: dev->stats.rx_over_errors++; break; + case 0x0800: dev->stats.rx_length_errors++; break; + case 0x1000: dev->stats.rx_frame_errors++; break; + case 0x1800: dev->stats.rx_length_errors++; break; + case 0x2000: dev->stats.rx_frame_errors++; break; + case 0x2800: dev->stats.rx_crc_errors++; break; } } else { short pkt_len = rx_status & 0x7ff; struct sk_buff *skb; skb = dev_alloc_skb(pkt_len+5); - lp->stats.rx_bytes += pkt_len; + dev->stats.rx_bytes += pkt_len; if (el3_debug > 4) printk("Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); @@ -1083,11 +1078,11 @@ el3_rx(struct net_device *dev) skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); dev->last_rx = jiffies; - lp->stats.rx_packets++; + dev->stats.rx_packets++; continue; } outw(RxDiscard, ioaddr + EL3_CMD); - lp->stats.rx_dropped++; + dev->stats.rx_dropped++; if (el3_debug) printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); diff --git a/drivers/net/3c515.c b/drivers/net/3c515.c index 6ab84b661d7..105a8c7ca7e 100644 --- a/drivers/net/3c515.c +++ b/drivers/net/3c515.c @@ -310,7 +310,6 @@ struct corkscrew_private { struct sk_buff *tx_skbuff[TX_RING_SIZE]; unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx;/* The ring entries to be free()ed. */ - struct net_device_stats stats; struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */ struct timer_list timer; /* Media selection timer. */ int capabilities ; /* Adapter capabilities word. */ @@ -983,8 +982,8 @@ static void corkscrew_timeout(struct net_device *dev) break; outw(TxEnable, ioaddr + EL3_CMD); dev->trans_start = jiffies; - vp->stats.tx_errors++; - vp->stats.tx_dropped++; + dev->stats.tx_errors++; + dev->stats.tx_dropped++; netif_wake_queue(dev); } @@ -1050,7 +1049,7 @@ static int corkscrew_start_xmit(struct sk_buff *skb, } /* Put out the doubleword header... */ outl(skb->len, ioaddr + TX_FIFO); - vp->stats.tx_bytes += skb->len; + dev->stats.tx_bytes += skb->len; #ifdef VORTEX_BUS_MASTER if (vp->bus_master) { /* Set the bus-master controller to transfer the packet. */ @@ -1094,9 +1093,9 @@ static int corkscrew_start_xmit(struct sk_buff *skb, printk("%s: Tx error, status %2.2x.\n", dev->name, tx_status); if (tx_status & 0x04) - vp->stats.tx_fifo_errors++; + dev->stats.tx_fifo_errors++; if (tx_status & 0x38) - vp->stats.tx_aborted_errors++; + dev->stats.tx_aborted_errors++; if (tx_status & 0x30) { int j; outw(TxReset, ioaddr + EL3_CMD); @@ -1257,7 +1256,6 @@ static irqreturn_t corkscrew_interrupt(int irq, void *dev_id) static int corkscrew_rx(struct net_device *dev) { - struct corkscrew_private *vp = netdev_priv(dev); int ioaddr = dev->base_addr; int i; short rx_status; @@ -1271,17 +1269,17 @@ static int corkscrew_rx(struct net_device *dev) if (corkscrew_debug > 2) printk(" Rx error: status %2.2x.\n", rx_error); - vp->stats.rx_errors++; + dev->stats.rx_errors++; if (rx_error & 0x01) - vp->stats.rx_over_errors++; + dev->stats.rx_over_errors++; if (rx_error & 0x02) - vp->stats.rx_length_errors++; + dev->stats.rx_length_errors++; if (rx_error & 0x04) - vp->stats.rx_frame_errors++; + dev->stats.rx_frame_errors++; if (rx_error & 0x08) - vp->stats.rx_crc_errors++; + dev->stats.rx_crc_errors++; if (rx_error & 0x10) - vp->stats.rx_length_errors++; + dev->stats.rx_length_errors++; } else { /* The packet length: up to 4.5K!. */ short pkt_len = rx_status & 0x1fff; @@ -1301,8 +1299,8 @@ static int corkscrew_rx(struct net_device *dev) skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; - vp->stats.rx_packets++; - vp->stats.rx_bytes += pkt_len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; /* Wait a limited time to go to next packet. */ for (i = 200; i >= 0; i--) if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) @@ -1312,7 +1310,7 @@ static int corkscrew_rx(struct net_device *dev) printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); } outw(RxDiscard, ioaddr + EL3_CMD); - vp->stats.rx_dropped++; + dev->stats.rx_dropped++; /* Wait a limited time to skip this packet. */ for (i = 200; i >= 0; i--) if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) @@ -1337,23 +1335,23 @@ static int boomerang_rx(struct net_device *dev) if (corkscrew_debug > 2) printk(" Rx error: status %2.2x.\n", rx_error); - vp->stats.rx_errors++; + dev->stats.rx_errors++; if (rx_error & 0x01) - vp->stats.rx_over_errors++; + dev->stats.rx_over_errors++; if (rx_error & 0x02) - vp->stats.rx_length_errors++; + dev->stats.rx_length_errors++; if (rx_error & 0x04) - vp->stats.rx_frame_errors++; + dev->stats.rx_frame_errors++; if (rx_error & 0x08) - vp->stats.rx_crc_errors++; + dev->stats.rx_crc_errors++; if (rx_error & 0x10) - vp->stats.rx_length_errors++; + dev->stats.rx_length_errors++; } else { /* The packet length: up to 4.5K!. */ short pkt_len = rx_status & 0x1fff; struct sk_buff *skb; - vp->stats.rx_bytes += pkt_len; + dev->stats.rx_bytes += pkt_len; if (corkscrew_debug > 4) printk("Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); @@ -1388,7 +1386,7 @@ static int boomerang_rx(struct net_device *dev) skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; - vp->stats.rx_packets++; + dev->stats.rx_packets++; } entry = (++vp->cur_rx) % RX_RING_SIZE; } @@ -1475,7 +1473,7 @@ static struct net_device_stats *corkscrew_get_stats(struct net_device *dev) update_stats(dev->base_addr, dev); spin_unlock_irqrestore(&vp->lock, flags); } - return &vp->stats; + return &dev->stats; } /* Update statistics. @@ -1487,19 +1485,17 @@ static struct net_device_stats *corkscrew_get_stats(struct net_device *dev) */ static void update_stats(int ioaddr, struct net_device *dev) { - struct corkscrew_private *vp = netdev_priv(dev); - /* Unlike the 3c5x9 we need not turn off stats updates while reading. */ /* Switch to the stats window, and read everything. */ EL3WINDOW(6); - vp->stats.tx_carrier_errors += inb(ioaddr + 0); - vp->stats.tx_heartbeat_errors += inb(ioaddr + 1); + dev->stats.tx_carrier_errors += inb(ioaddr + 0); + dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); /* Multiple collisions. */ inb(ioaddr + 2); - vp->stats.collisions += inb(ioaddr + 3); - vp->stats.tx_window_errors += inb(ioaddr + 4); - vp->stats.rx_fifo_errors += inb(ioaddr + 5); - vp->stats.tx_packets += inb(ioaddr + 6); - vp->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4; + dev->stats.collisions += inb(ioaddr + 3); + dev->stats.tx_window_errors += inb(ioaddr + 4); + dev->stats.rx_fifo_errors += inb(ioaddr + 5); + dev->stats.tx_packets += inb(ioaddr + 6); + dev->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4; /* Rx packets */ inb(ioaddr + 7); /* Must read to clear */ /* Tx deferrals */ inb(ioaddr + 8); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index f90a86ba7e2..af46341827f 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2593,6 +2593,7 @@ config BNX2X To compile this driver as a module, choose M here: the module will be called bnx2x. This is recommended. +source "drivers/net/sfc/Kconfig" endif # NETDEV_10000 diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 2f1f3f2739f..dcbfe842115 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -253,3 +253,5 @@ obj-$(CONFIG_FS_ENET) += fs_enet/ obj-$(CONFIG_NETXEN_NIC) += netxen/ obj-$(CONFIG_NIU) += niu.o obj-$(CONFIG_VIRTIO_NET) += virtio_net.o +obj-$(CONFIG_SFC) += sfc/ + diff --git a/drivers/net/arm/Kconfig b/drivers/net/arm/Kconfig index f9cc2b621fe..8eda6eeb43b 100644 --- a/drivers/net/arm/Kconfig +++ b/drivers/net/arm/Kconfig @@ -47,3 +47,11 @@ config EP93XX_ETH help This is a driver for the ethernet hardware included in EP93xx CPUs. Say Y if you are building a kernel for EP93xx based devices. + +config IXP4XX_ETH + tristate "Intel IXP4xx Ethernet support" + depends on ARM && ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR + select MII + help + Say Y here if you want to use built-in Ethernet ports + on IXP4xx processor. diff --git a/drivers/net/arm/Makefile b/drivers/net/arm/Makefile index a4c868278e1..7c812ac2b6a 100644 --- a/drivers/net/arm/Makefile +++ b/drivers/net/arm/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_ARM_ETHER3) += ether3.o obj-$(CONFIG_ARM_ETHER1) += ether1.o obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o +obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c new file mode 100644 index 00000000000..c617b64c288 --- /dev/null +++ b/drivers/net/arm/ixp4xx_eth.c @@ -0,0 +1,1265 @@ +/* + * Intel IXP4xx Ethernet driver for Linux + * + * Copyright (C) 2007 Krzysztof Halasa <khc@pm.waw.pl> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * Ethernet port config (0x00 is not present on IXP42X): + * + * logical port 0x00 0x10 0x20 + * NPE 0 (NPE-A) 1 (NPE-B) 2 (NPE-C) + * physical PortId 2 0 1 + * TX queue 23 24 25 + * RX-free queue 26 27 28 + * TX-done queue is always 31, per-port RX and TX-ready queues are configurable + * + * + * Queue entries: + * bits 0 -> 1 - NPE ID (RX and TX-done) + * bits 0 -> 2 - priority (TX, per 802.1D) + * bits 3 -> 4 - port ID (user-set?) + * bits 5 -> 31 - physical descriptor address + */ + +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/etherdevice.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mii.h> +#include <linux/platform_device.h> +#include <asm/arch/npe.h> +#include <asm/arch/qmgr.h> + +#define DEBUG_QUEUES 0 +#define DEBUG_DESC 0 +#define DEBUG_RX 0 +#define DEBUG_TX 0 +#define DEBUG_PKT_BYTES 0 +#define DEBUG_MDIO 0 +#define DEBUG_CLOSE 0 + +#define DRV_NAME "ixp4xx_eth" + +#define MAX_NPES 3 + +#define RX_DESCS 64 /* also length of all RX queues */ +#define TX_DESCS 16 /* also length of all TX queues */ +#define TXDONE_QUEUE_LEN 64 /* dwords */ + +#define POOL_ALLOC_SIZE (sizeof(struct desc) * (RX_DESCS + TX_DESCS)) +#define REGS_SIZE 0x1000 +#define MAX_MRU 1536 /* 0x600 */ +#define RX_BUFF_SIZE ALIGN((NET_IP_ALIGN) + MAX_MRU, 4) + +#define NAPI_WEIGHT 16 +#define MDIO_INTERVAL (3 * HZ) +#define MAX_MDIO_RETRIES 100 /* microseconds, typically 30 cycles */ +#define MAX_MII_RESET_RETRIES 100 /* mdio_read() cycles, typically 4 */ +#define MAX_CLOSE_WAIT 1000 /* microseconds, typically 2-3 cycles */ + +#define NPE_ID(port_id) ((port_id) >> 4) +#define PHYSICAL_ID(port_id) ((NPE_ID(port_id) + 2) % 3) +#define TX_QUEUE(port_id) (NPE_ID(port_id) + 23) +#define RXFREE_QUEUE(port_id) (NPE_ID(port_id) + 26) +#define TXDONE_QUEUE 31 + +/* TX Control Registers */ +#define TX_CNTRL0_TX_EN 0x01 +#define TX_CNTRL0_HALFDUPLEX 0x02 +#define TX_CNTRL0_RETRY 0x04 +#define TX_CNTRL0_PAD_EN 0x08 +#define TX_CNTRL0_APPEND_FCS 0x10 +#define TX_CNTRL0_2DEFER 0x20 +#define TX_CNTRL0_RMII 0x40 /* reduced MII */ +#define TX_CNTRL1_RETRIES 0x0F /* 4 bits */ + +/* RX Control Registers */ +#define RX_CNTRL0_RX_EN 0x01 +#define RX_CNTRL0_PADSTRIP_EN 0x02 +#define RX_CNTRL0_SEND_FCS 0x04 +#define RX_CNTRL0_PAUSE_EN 0x08 +#define RX_CNTRL0_LOOP_EN 0x10 +#define RX_CNTRL0_ADDR_FLTR_EN 0x20 +#define RX_CNTRL0_RX_RUNT_EN 0x40 +#define RX_CNTRL0_BCAST_DIS 0x80 +#define RX_CNTRL1_DEFER_EN 0x01 + +/* Core Control Register */ +#define CORE_RESET 0x01 +#define CORE_RX_FIFO_FLUSH 0x02 +#define CORE_TX_FIFO_FLUSH 0x04 +#define CORE_SEND_JAM 0x08 +#define CORE_MDC_EN 0x10 /* MDIO using NPE-B ETH-0 only */ + +#define DEFAULT_TX_CNTRL0 (TX_CNTRL0_TX_EN | TX_CNTRL0_RETRY | \ + TX_CNTRL0_PAD_EN | TX_CNTRL0_APPEND_FCS | \ + TX_CNTRL0_2DEFER) +#define DEFAULT_RX_CNTRL0 RX_CNTRL0_RX_EN +#define DEFAULT_CORE_CNTRL CORE_MDC_EN + + +/* NPE message codes */ +#define NPE_GETSTATUS 0x00 +#define NPE_EDB_SETPORTADDRESS 0x01 +#define NPE_EDB_GETMACADDRESSDATABASE 0x02 +#define NPE_EDB_SETMACADDRESSSDATABASE 0x03 +#define NPE_GETSTATS 0x04 +#define NPE_RESETSTATS 0x05 +#define NPE_SETMAXFRAMELENGTHS 0x06 +#define NPE_VLAN_SETRXTAGMODE 0x07 +#define NPE_VLAN_SETDEFAULTRXVID 0x08 +#define NPE_VLAN_SETPORTVLANTABLEENTRY 0x09 +#define NPE_VLAN_SETPORTVLANTABLERANGE 0x0A +#define NPE_VLAN_SETRXQOSENTRY 0x0B +#define NPE_VLAN_SETPORTIDEXTRACTIONMODE 0x0C +#define NPE_STP_SETBLOCKINGSTATE 0x0D +#define NPE_FW_SETFIREWALLMODE 0x0E +#define NPE_PC_SETFRAMECONTROLDURATIONID 0x0F +#define NPE_PC_SETAPMACTABLE 0x11 +#define NPE_SETLOOPBACK_MODE 0x12 +#define NPE_PC_SETBSSIDTABLE 0x13 +#define NPE_ADDRESS_FILTER_CONFIG 0x14 +#define NPE_APPENDFCSCONFIG 0x15 +#define NPE_NOTIFY_MAC_RECOVERY_DONE 0x16 +#define NPE_MAC_RECOVERY_START 0x17 + + +#ifdef __ARMEB__ +typedef struct sk_buff buffer_t; +#define free_buffer dev_kfree_skb +#define free_buffer_irq dev_kfree_skb_irq +#else +typedef void buffer_t; +#define free_buffer kfree +#define free_buffer_irq kfree +#endif + +struct eth_regs { + u32 tx_control[2], __res1[2]; /* 000 */ + u32 rx_control[2], __res2[2]; /* 010 */ + u32 random_seed, __res3[3]; /* 020 */ + u32 partial_empty_threshold, __res4; /* 030 */ + u32 partial_full_threshold, __res5; /* 038 */ + u32 tx_start_bytes, __res6[3]; /* 040 */ + u32 tx_deferral, rx_deferral, __res7[2];/* 050 */ + u32 tx_2part_deferral[2], __res8[2]; /* 060 */ + u32 slot_time, __res9[3]; /* 070 */ + u32 mdio_command[4]; /* 080 */ + u32 mdio_status[4]; /* 090 */ + u32 mcast_mask[6], __res10[2]; /* 0A0 */ + u32 mcast_addr[6], __res11[2]; /* 0C0 */ + u32 int_clock_threshold, __res12[3]; /* 0E0 */ + u32 hw_addr[6], __res13[61]; /* 0F0 */ + u32 core_control; /* 1FC */ +}; + +struct port { + struct resource *mem_res; + struct eth_regs __iomem *regs; + struct npe *npe; + struct net_device *netdev; + struct napi_struct napi; + struct net_device_stats stat; + struct mii_if_info mii; + struct delayed_work mdio_thread; + struct eth_plat_info *plat; + buffer_t *rx_buff_tab[RX_DESCS], *tx_buff_tab[TX_DESCS]; + struct desc *desc_tab; /* coherent */ + u32 desc_tab_phys; + int id; /* logical port ID */ + u16 mii_bmcr; +}; + +/* NPE message structure */ +struct msg { +#ifdef __ARMEB__ + u8 cmd, eth_id, byte2, byte3; + u8 byte4, byte5, byte6, byte7; +#else + u8 byte3, byte2, eth_id, cmd; + u8 byte7, byte6, byte5, byte4; +#endif +}; + +/* Ethernet packet descriptor */ +struct desc { + u32 next; /* pointer to next buffer, unused */ + +#ifdef __ARMEB__ + u16 buf_len; /* buffer length */ + u16 pkt_len; /* packet length */ + u32 data; /* pointer to data buffer in RAM */ + u8 dest_id; + u8 src_id; + u16 flags; + u8 qos; + u8 padlen; + u16 vlan_tci; +#else + u16 pkt_len; /* packet length */ + u16 buf_len; /* buffer length */ + u32 data; /* pointer to data buffer in RAM */ + u16 flags; + u8 src_id; + u8 dest_id; + u16 vlan_tci; + u8 padlen; + u8 qos; +#endif + +#ifdef __ARMEB__ + u8 dst_mac_0, dst_mac_1, dst_mac_2, dst_mac_3; + u8 dst_mac_4, dst_mac_5, src_mac_0, src_mac_1; + u8 src_mac_2, src_mac_3, src_mac_4, src_mac_5; +#else + u8 dst_mac_3, dst_mac_2, dst_mac_1, dst_mac_0; + u8 src_mac_1, src_mac_0, dst_mac_5, dst_mac_4; + u8 src_mac_5, src_mac_4, src_mac_3, src_mac_2; +#endif +}; + + +#define rx_desc_phys(port, n) ((port)->desc_tab_phys + \ + (n) * sizeof(struct desc)) +#define rx_desc_ptr(port, n) (&(port)->desc_tab[n]) + +#define tx_desc_phys(port, n) ((port)->desc_tab_phys + \ + ((n) + RX_DESCS) * sizeof(struct desc)) +#define tx_desc_ptr(port, n) (&(port)->desc_tab[(n) + RX_DESCS]) + +#ifndef __ARMEB__ +static inline void memcpy_swab32(u32 *dest, u32 *src, int cnt) +{ + int i; + for (i = 0; i < cnt; i++) + dest[i] = swab32(src[i]); +} +#endif + +static spinlock_t mdio_lock; +static struct eth_regs __iomem *mdio_regs; /* mdio command and status only */ +static int ports_open; +static struct port *npe_port_tab[MAX_NPES]; +static struct dma_pool *dma_pool; + + +static u16 mdio_cmd(struct net_device *dev, int phy_id, int location, + int write, u16 cmd) +{ + int cycles = 0; + + if (__raw_readl(&mdio_regs->mdio_command[3]) & 0x80) { + printk(KERN_ERR "%s: MII not ready to transmit\n", dev->name); + return 0; + } + + if (write) { + __raw_writel(cmd & 0xFF, &mdio_regs->mdio_command[0]); + __raw_writel(cmd >> 8, &mdio_regs->mdio_command[1]); + } + __raw_writel(((phy_id << 5) | location) & 0xFF, + &mdio_regs->mdio_command[2]); + __raw_writel((phy_id >> 3) | (write << 2) | 0x80 /* GO */, + &mdio_regs->mdio_command[3]); + + while ((cycles < MAX_MDIO_RETRIES) && + (__raw_readl(&mdio_regs->mdio_command[3]) & 0x80)) { + udelay(1); + cycles++; + } + + if (cycles == MAX_MDIO_RETRIES) { + printk(KERN_ERR "%s: MII write failed\n", dev->name); + return 0; + } + +#if DEBUG_MDIO + printk(KERN_DEBUG "%s: mdio_cmd() took %i cycles\n", dev->name, + cycles); +#endif + + if (write) + return 0; + + if (__raw_readl(&mdio_regs->mdio_status[3]) & 0x80) { + printk(KERN_ERR "%s: MII read failed\n", dev->name); + return 0; + } + + return (__raw_readl(&mdio_regs->mdio_status[0]) & 0xFF) | + (__raw_readl(&mdio_regs->mdio_status[1]) << 8); +} + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ + unsigned long flags; + u16 val; + + spin_lock_irqsave(&mdio_lock, flags); + val = mdio_cmd(dev, phy_id, location, 0, 0); + spin_unlock_irqrestore(&mdio_lock, flags); + return val; +} + +static void mdio_write(struct net_device *dev, int phy_id, int location, + int val) +{ + unsigned long flags; + + spin_lock_irqsave(&mdio_lock, flags); + mdio_cmd(dev, phy_id, location, 1, val); + spin_unlock_irqrestore(&mdio_lock, flags); +} + +static void phy_reset(struct net_device *dev, int phy_id) +{ + struct port *port = netdev_priv(dev); + int cycles = 0; + + mdio_write(dev, phy_id, MII_BMCR, port->mii_bmcr | BMCR_RESET); + + while (cycles < MAX_MII_RESET_RETRIES) { + if (!(mdio_read(dev, phy_id, MII_BMCR) & BMCR_RESET)) { +#if DEBUG_MDIO + printk(KERN_DEBUG "%s: phy_reset() took %i cycles\n", + dev->name, cycles); +#endif + return; + } + udelay(1); + cycles++; + } + + printk(KERN_ERR "%s: MII reset failed\n", dev->name); +} + +static void eth_set_duplex(struct port *port) +{ + if (port->mii.full_duplex) + __raw_writel(DEFAULT_TX_CNTRL0 & ~TX_CNTRL0_HALFDUPLEX, + &port->regs->tx_control[0]); + else + __raw_writel(DEFAULT_TX_CNTRL0 | TX_CNTRL0_HALFDUPLEX, + &port->regs->tx_control[0]); +} + + +static void phy_check_media(struct port *port, int init) +{ + if (mii_check_media(&port->mii, 1, init)) + eth_set_duplex(port); + if (port->mii.force_media) { /* mii_check_media() doesn't work */ + struct net_device *dev = port->netdev; + int cur_link = mii_link_ok(&port->mii); + int prev_link = netif_carrier_ok(dev); + + if (!prev_link && cur_link) { + printk(KERN_INFO "%s: link up\n", dev->name); + netif_carrier_on(dev); + } else if (prev_link && !cur_link) { + printk(KERN_INFO "%s: link down\n", dev->name); + netif_carrier_off(dev); + } + } +} + + +static void mdio_thread(struct work_struct *work) +{ + struct port *port = container_of(work, struct port, mdio_thread.work); + + phy_check_media(port, 0); + schedule_delayed_work(&port->mdio_thread, MDIO_INTERVAL); +} + + +static inline void debug_pkt(struct net_device *dev, const char *func, + u8 *data, int len) +{ +#if DEBUG_PKT_BYTES + int i; + + printk(KERN_DEBUG "%s: %s(%i) ", dev->name, func, len); + for (i = 0; i < len; i++) { + if (i >= DEBUG_PKT_BYTES) + break; + printk("%s%02X", + ((i == 6) || (i == 12) || (i >= 14)) ? " " : "", + data[i]); + } + printk("\n"); +#endif +} + + +static inline void debug_desc(u32 phys, struct desc *desc) +{ +#if DEBUG_DESC + printk(KERN_DEBUG "%X: %X %3X %3X %08X %2X < %2X %4X %X" + " %X %X %02X%02X%02X%02X%02X%02X < %02X%02X%02X%02X%02X%02X\n", + phys, desc->next, desc->buf_len, desc->pkt_len, + desc->data, desc->dest_id, desc->src_id, desc->flags, + desc->qos, desc->padlen, desc->vlan_tci, + desc->dst_mac_0, desc->dst_mac_1, desc->dst_mac_2, + desc->dst_mac_3, desc->dst_mac_4, desc->dst_mac_5, + desc->src_mac_0, desc->src_mac_1, desc->src_mac_2, + desc->src_mac_3, desc->src_mac_4, desc->src_mac_5); +#endif +} + +static inline void debug_queue(unsigned int queue, int is_get, u32 phys) +{ +#if DEBUG_QUEUES + static struct { + int queue; + char *name; + } names[] = { + { TX_QUEUE(0x10), "TX#0 " }, + { TX_QUEUE(0x20), "TX#1 " }, + { TX_QUEUE(0x00), "TX#2 " }, + { RXFREE_QUEUE(0x10), "RX-free#0 " }, + { RXFREE_QUEUE(0x20), "RX-free#1 " }, + { RXFREE_QUEUE(0x00), "RX-free#2 " }, + { TXDONE_QUEUE, "TX-done " }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(names); i++) + if (names[i].queue == queue) + break; + + printk(KERN_DEBUG "Queue %i %s%s %X\n", queue, + i < ARRAY_SIZE(names) ? names[i].name : "", + is_get ? "->" : "<-", phys); +#endif +} + +static inline u32 queue_get_entry(unsigned int queue) +{ + u32 phys = qmgr_get_entry(queue); + debug_queue(queue, 1, phys); + return phys; +} + +static inline int queue_get_desc(unsigned int queue, struct port *port, + int is_tx) +{ + u32 phys, tab_phys, n_desc; + struct desc *tab; + + if (!(phys = queue_get_entry(queue))) + return -1; + + phys &= ~0x1F; /* mask out non-address bits */ + tab_phys = is_tx ? tx_desc_phys(port, 0) : rx_desc_phys(port, 0); + tab = is_tx ? tx_desc_ptr(port, 0) : rx_desc_ptr(port, 0); + n_desc = (phys - tab_phys) / sizeof(struct desc); + BUG_ON(n_desc >= (is_tx ? TX_DESCS : RX_DESCS)); + debug_desc(phys, &tab[n_desc]); + BUG_ON(tab[n_desc].next); + return n_desc; +} + +static inline void queue_put_desc(unsigned int queue, u32 phys, + struct desc *desc) +{ + debug_queue(queue, 0, phys); + debug_desc(phys, desc); + BUG_ON(phys & 0x1F); + qmgr_put_entry(queue, phys); + BUG_ON(qmgr_stat_overflow(queue)); +} + + +static inline void dma_unmap_tx(struct port *port, struct desc *desc) +{ +#ifdef __ARMEB__ + dma_unmap_single(&port->netdev->dev, desc->data, + desc->buf_len, DMA_TO_DEVICE); +#else + dma_unmap_single(&port->netdev->dev, desc->data & ~3, + ALIGN((desc->data & 3) + desc->buf_len, 4), + DMA_TO_DEVICE); +#endif +} + + +static void eth_rx_irq(void *pdev) +{ + struct net_device *dev = pdev; + struct port *port = netdev_priv(dev); + +#if DEBUG_RX + printk(KERN_DEBUG "%s: eth_rx_irq\n", dev->name); +#endif + qmgr_disable_irq(port->plat->rxq); + netif_rx_schedule(dev, &port->napi); +} + +static int eth_poll(struct napi_struct *napi, int budget) +{ + struct port *port = container_of(napi, struct port, napi); + struct net_device *dev = port->netdev; + unsigned int rxq = port->plat->rxq, rxfreeq = RXFREE_QUEUE(port->id); + int received = 0; + +#if DEBUG_RX + printk(KERN_DEBUG "%s: eth_poll\n", dev->name); +#endif + + while (received < budget) { + struct sk_buff *skb; + struct desc *desc; + int n; +#ifdef __ARMEB__ + struct sk_buff *temp; + u32 phys; +#endif + + if ((n = queue_get_desc(rxq, port, 0)) < 0) { + received = 0; /* No packet received */ +#if DEBUG_RX + printk(KERN_DEBUG "%s: eth_poll netif_rx_complete\n", + dev->name); +#endif + netif_rx_complete(dev, napi); + qmgr_enable_irq(rxq); + if (!qmgr_stat_empty(rxq) && + netif_rx_reschedule(dev, napi)) { +#if DEBUG_RX + printk(KERN_DEBUG "%s: eth_poll" + " netif_rx_reschedule successed\n", + dev->name); +#endif + qmgr_disable_irq(rxq); + continue; + } +#if DEBUG_RX + printk(KERN_DEBUG "%s: eth_poll all done\n", + dev->name); +#endif + return 0; /* all work done */ + } + + desc = rx_desc_ptr(port, n); + +#ifdef __ARMEB__ + if ((skb = netdev_alloc_skb(dev, RX_BUFF_SIZE))) { + phys = dma_map_single(&dev->dev, skb->data, + RX_BUFF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(phys)) { + dev_kfree_skb(skb); + skb = NULL; + } + } +#else + skb = netdev_alloc_skb(dev, + ALIGN(NET_IP_ALIGN + desc->pkt_len, 4)); +#endif + + if (!skb) { + port->stat.rx_dropped++; + /* put the desc back on RX-ready queue */ + desc->buf_len = MAX_MRU; + desc->pkt_len = 0; + queue_put_desc(rxfreeq, rx_desc_phys(port, n), desc); + continue; + } + + /* process received frame */ +#ifdef __ARMEB__ + temp = skb; + skb = port->rx_buff_tab[n]; + dma_unmap_single(&dev->dev, desc->data - NET_IP_ALIGN, + RX_BUFF_SIZE, DMA_FROM_DEVICE); +#else + dma_sync_single(&dev->dev, desc->data - NET_IP_ALIGN, + RX_BUFF_SIZE, DMA_FROM_DEVICE); + memcpy_swab32((u32 *)skb->data, (u32 *)port->rx_buff_tab[n], + ALIGN(NET_IP_ALIGN + desc->pkt_len, 4) / 4); +#endif + skb_reserve(skb, NET_IP_ALIGN); + skb_put(skb, desc->pkt_len); + + debug_pkt(dev, "eth_poll", skb->data, skb->len); + + skb->protocol = eth_type_trans(skb, dev); + dev->last_rx = jiffies; + port->stat.rx_packets++; + port->stat.rx_bytes += skb->len; + netif_receive_skb(skb); + + /* put the new buffer on RX-free queue */ +#ifdef __ARMEB__ + port->rx_buff_tab[n] = temp; + desc->data = phys + NET_IP_ALIGN; +#endif + desc->buf_len = MAX_MRU; + desc->pkt_len = 0; + queue_put_desc(rxfreeq, rx_desc_phys(port, n), desc); + received++; + } + +#if DEBUG_RX + printk(KERN_DEBUG "eth_poll(): end, not all work done\n"); +#endif + return received; /* not all work done */ +} + + +static void eth_txdone_irq(void *unused) +{ + u32 phys; + +#if DEBUG_TX + printk(KERN_DEBUG DRV_NAME ": eth_txdone_irq\n"); +#endif + while ((phys = queue_get_entry(TXDONE_QUEUE)) != 0) { + u32 npe_id, n_desc; + struct port *port; + struct desc *desc; + int start; + + npe_id = phys & 3; + BUG_ON(npe_id >= MAX_NPES); + port = npe_port_tab[npe_id]; + BUG_ON(!port); + phys &= ~0x1F; /* mask out non-address bits */ + n_desc = (phys - tx_desc_phys(port, 0)) / sizeof(struct desc); + BUG_ON(n_desc >= TX_DESCS); + desc = tx_desc_ptr(port, n_desc); + debug_desc(phys, desc); + + if (port->tx_buff_tab[n_desc]) { /* not the draining packet */ + port->stat.tx_packets++; + port->stat.tx_bytes += desc->pkt_len; + + dma_unmap_tx(port, desc); +#if DEBUG_TX + printk(KERN_DEBUG "%s: eth_txdone_irq free %p\n", + port->netdev->name, port->tx_buff_tab[n_desc]); +#endif + free_buffer_irq(port->tx_buff_tab[n_desc]); + port->tx_buff_tab[n_desc] = NULL; + } + + start = qmgr_stat_empty(port->plat->txreadyq); + queue_put_desc(port->plat->txreadyq, phys, desc); + if (start) { +#if DEBUG_TX + printk(KERN_DEBUG "%s: eth_txdone_irq xmit ready\n", + port->netdev->name); +#endif + netif_wake_queue(port->netdev); + } + } +} + +static int eth_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct port *port = netdev_priv(dev); + unsigned int txreadyq = port->plat->txreadyq; + int len, offset, bytes, n; + void *mem; + u32 phys; + struct desc *desc; + +#if DEBUG_TX + printk(KERN_DEBUG "%s: eth_xmit\n", dev->name); +#endif + + if (unlikely(skb->len > MAX_MRU)) { + dev_kfree_skb(skb); + port->stat.tx_errors++; + return NETDEV_TX_OK; + } + + debug_pkt(dev, "eth_xmit", skb->data, skb->len); + + len = skb->len; +#ifdef __ARMEB__ + offset = 0; /* no need to keep alignment */ + bytes = len; + mem = skb->data; +#else + offset = (int)skb->data & 3; /* keep 32-bit alignment */ + bytes = ALIGN(offset + len, 4); + if (!(mem = kmalloc(bytes, GFP_ATOMIC))) { + dev_kfree_skb(skb); + port->stat.tx_dropped++; + return NETDEV_TX_OK; + } + memcpy_swab32(mem, (u32 *)((int)skb->data & ~3), bytes / 4); + dev_kfree_skb(skb); +#endif + + phys = dma_map_single(&dev->dev, mem, bytes, DMA_TO_DEVICE); + if (dma_mapping_error(phys)) { +#ifdef __ARMEB__ + dev_kfree_skb(skb); +#else + kfree(mem); +#endif + port->stat.tx_dropped++; + return NETDEV_TX_OK; + } + + n = queue_get_desc(txreadyq, port, 1); + BUG_ON(n < 0); + desc = tx_desc_ptr(port, n); + +#ifdef __ARMEB__ + port->tx_buff_tab[n] = skb; +#else + port->tx_buff_tab[n] = mem; +#endif + desc->data = phys + offset; + desc->buf_len = desc->pkt_len = len; + + /* NPE firmware pads short frames with zeros internally */ + wmb(); + queue_put_desc(TX_QUEUE(port->id), tx_desc_phys(port, n), desc); + dev->trans_start = jiffies; + + if (qmgr_stat_empty(txreadyq)) { +#if DEBUG_TX + printk(KERN_DEBUG "%s: eth_xmit queue full\n", dev->name); +#endif + netif_stop_queue(dev); + /* we could miss TX ready interrupt */ + if (!qmgr_stat_empty(txreadyq)) { +#if DEBUG_TX + printk(KERN_DEBUG "%s: eth_xmit ready again\n", + dev->name); +#endif + netif_wake_queue(dev); + } + } + +#if DEBUG_TX + printk(KERN_DEBUG "%s: eth_xmit end\n", dev->name); +#endif + return NETDEV_TX_OK; +} + + +static struct net_device_stats *eth_stats(struct net_device *dev) +{ + struct port *port = netdev_priv(dev); + return &port->stat; +} + +static void eth_set_mcast_list(struct net_device *dev) +{ + struct port *port = netdev_priv(dev); + struct dev_mc_list *mclist = dev->mc_list; + u8 diffs[ETH_ALEN], *addr; + int cnt = dev->mc_count, i; + + if ((dev->flags & IFF_PROMISC) || !mclist || !cnt) { + __raw_writel(DEFAULT_RX_CNTRL0 & ~RX_CNTRL0_ADDR_FLTR_EN, + &port->regs->rx_control[0]); + return; + } + + memset(diffs, 0, ETH_ALEN); + addr = mclist->dmi_addr; /* first MAC address */ + + while (--cnt && (mclist = mclist->next)) + for (i = 0; i < ETH_ALEN; i++) + diffs[i] |= addr[i] ^ mclist->dmi_addr[i]; + + for (i = 0; i < ETH_ALEN; i++) { + __raw_writel(addr[i], &port->regs->mcast_addr[i]); + __raw_writel(~diffs[i], &port->regs->mcast_mask[i]); + } + + __raw_writel(DEFAULT_RX_CNTRL0 | RX_CNTRL0_ADDR_FLTR_EN, + &port->regs->rx_control[0]); +} + + +static int eth_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + struct port *port = netdev_priv(dev); + unsigned int duplex_chg; + int err; + + if (!netif_running(dev)) + return -EINVAL; + err = generic_mii_ioctl(&port->mii, if_mii(req), cmd, &duplex_chg); + if (duplex_chg) + eth_set_duplex(port); + return err; +} + + +static int request_queues(struct port *port) +{ + int err; + + err = qmgr_request_queue(RXFREE_QUEUE(port->id), RX_DESCS, 0, 0); + if (err) + return err; + + err = qmgr_request_queue(port->plat->rxq, RX_DESCS, 0, 0); + if (err) + goto rel_rxfree; + + err = qmgr_request_queue(TX_QUEUE(port->id), TX_DESCS, 0, 0); + if (err) + goto rel_rx; + + err = qmgr_request_queue(port->plat->txreadyq, TX_DESCS, 0, 0); + if (err) + goto rel_tx; + + /* TX-done queue handles skbs sent out by the NPEs */ + if (!ports_open) { + err = qmgr_request_queue(TXDONE_QUEUE, TXDONE_QUEUE_LEN, 0, 0); + if (err) + goto rel_txready; + } + return 0; + +rel_txready: + qmgr_release_queue(port->plat->txreadyq); +rel_tx: + qmgr_release_queue(TX_QUEUE(port->id)); +rel_rx: + qmgr_release_queue(port->plat->rxq); +rel_rxfree: + qmgr_release_queue(RXFREE_QUEUE(port->id)); + printk(KERN_DEBUG "%s: unable to request hardware queues\n", + port->netdev->name); + return err; +} + +static void release_queues(struct port *port) +{ + qmgr_release_queue(RXFREE_QUEUE(port->id)); + qmgr_release_queue(port->plat->rxq); + qmgr_release_queue(TX_QUEUE(port->id)); + qmgr_release_queue(port->plat->txreadyq); + + if (!ports_open) + qmgr_release_queue(TXDONE_QUEUE); +} + +static int init_queues(struct port *port) +{ + int i; + + if (!ports_open) + if (!(dma_pool = dma_pool_create(DRV_NAME, NULL, + POOL_ALLOC_SIZE, 32, 0))) + return -ENOMEM; + + if (!(port->desc_tab = dma_pool_alloc(dma_pool, GFP_KERNEL, + &port->desc_tab_phys))) + return -ENOMEM; + memset(port->desc_tab, 0, POOL_ALLOC_SIZE); + memset(port->rx_buff_tab, 0, sizeof(port->rx_buff_tab)); /* tables */ + memset(port->tx_buff_tab, 0, sizeof(port->tx_buff_tab)); + + /* Setup RX buffers */ + for (i = 0; i < RX_DESCS; i++) { + struct desc *desc = rx_desc_ptr(port, i); + buffer_t *buff; /* skb or kmalloc()ated memory */ + void *data; +#ifdef __ARMEB__ + if (!(buff = netdev_alloc_skb(port->netdev, RX_BUFF_SIZE))) + return -ENOMEM; + data = buff->data; +#else + if (!(buff = kmalloc(RX_BUFF_SIZE, GFP_KERNEL))) + return -ENOMEM; + data = buff; +#endif + desc->buf_len = MAX_MRU; + desc->data = dma_map_single(&port->netdev->dev, data, + RX_BUFF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(desc->data)) { + free_buffer(buff); + return -EIO; + } + desc->data += NET_IP_ALIGN; + port->rx_buff_tab[i] = buff; + } + + return 0; +} + +static void destroy_queues(struct port *port) +{ + int i; + + if (port->desc_tab) { + for (i = 0; i < RX_DESCS; i++) { + struct desc *desc = rx_desc_ptr(port, i); + buffer_t *buff = port->rx_buff_tab[i]; + if (buff) { + dma_unmap_single(&port->netdev->dev, + desc->data - NET_IP_ALIGN, + RX_BUFF_SIZE, DMA_FROM_DEVICE); + free_buffer(buff); + } + } + for (i = 0; i < TX_DESCS; i++) { + struct desc *desc = tx_desc_ptr(port, i); + buffer_t *buff = port->tx_buff_tab[i]; + if (buff) { + dma_unmap_tx(port, desc); + free_buffer(buff); + } + } + dma_pool_free(dma_pool, port->desc_tab, port->desc_tab_phys); + port->desc_tab = NULL; + } + + if (!ports_open && dma_pool) { + dma_pool_destroy(dma_pool); + dma_pool = NULL; + } +} + +static int eth_open(struct net_device *dev) +{ + struct port *port = netdev_priv(dev); + struct npe *npe = port->npe; + struct msg msg; + int i, err; + + if (!npe_running(npe)) { + err = npe_load_firmware(npe, npe_name(npe), &dev->dev); + if (err) + return err; + + if (npe_recv_message(npe, &msg, "ETH_GET_STATUS")) { + printk(KERN_ERR "%s: %s not responding\n", dev->name, + npe_name(npe)); + return -EIO; + } + } + + mdio_write(dev, port->plat->phy, MII_BMCR, port->mii_bmcr); + + memset(&msg, 0, sizeof(msg)); + msg.cmd = NPE_VLAN_SETRXQOSENTRY; + msg.eth_id = port->id; + msg.byte5 = port->plat->rxq | 0x80; + msg.byte7 = port->plat->rxq << 4; + for (i = 0; i < 8; i++) { + msg.byte3 = i; + if (npe_send_recv_message(port->npe, &msg, "ETH_SET_RXQ")) + return -EIO; + } + + msg.cmd = NPE_EDB_SETPORTADDRESS; + msg.eth_id = PHYSICAL_ID(port->id); + msg.byte2 = dev->dev_addr[0]; + msg.byte3 = dev->dev_addr[1]; + msg.byte4 = dev->dev_addr[2]; + msg.byte5 = dev->dev_addr[3]; + msg.byte6 = dev->dev_addr[4]; + msg.byte7 = dev->dev_addr[5]; + if (npe_send_recv_message(port->npe, &msg, "ETH_SET_MAC")) + return -EIO; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = NPE_FW_SETFIREWALLMODE; + msg.eth_id = port->id; + if (npe_send_recv_message(port->npe, &msg, "ETH_SET_FIREWALL_MODE")) + return -EIO; + + if ((err = request_queues(port)) != 0) + return err; + + if ((err = init_queues(port)) != 0) { + destroy_queues(port); + release_queues(port); + return err; + } + + for (i = 0; i < ETH_ALEN; i++) + __raw_writel(dev->dev_addr[i], &port->regs->hw_addr[i]); + __raw_writel(0x08, &port->regs->random_seed); + __raw_writel(0x12, &port->regs->partial_empty_threshold); + __raw_writel(0x30, &port->regs->partial_full_threshold); + __raw_writel(0x08, &port->regs->tx_start_bytes); + __raw_writel(0x15, &port->regs->tx_deferral); + __raw_writel(0x08, &port->regs->tx_2part_deferral[0]); + __raw_writel(0x07, &port->regs->tx_2part_deferral[1]); + __raw_writel(0x80, &port->regs->slot_time); + __raw_writel(0x01, &port->regs->int_clock_threshold); + + /* Populate queues with buffers, no failure after this point */ + for (i = 0; i < TX_DESCS; i++) + queue_put_desc(port->plat->txreadyq, + tx_desc_phys(port, i), tx_desc_ptr(port, i)); + + for (i = 0; i < RX_DESCS; i++) + queue_put_desc(RXFREE_QUEUE(port->id), + rx_desc_phys(port, i), rx_desc_ptr(port, i)); + + __raw_writel(TX_CNTRL1_RETRIES, &port->regs->tx_control[1]); + __raw_writel(DEFAULT_TX_CNTRL0, &port->regs->tx_control[0]); + __raw_writel(0, &port->regs->rx_control[1]); + __raw_writel(DEFAULT_RX_CNTRL0, &port->regs->rx_control[0]); + + napi_enable(&port->napi); + phy_check_media(port, 1); + eth_set_mcast_list(dev); + netif_start_queue(dev); + schedule_delayed_work(&port->mdio_thread, MDIO_INTERVAL); + + qmgr_set_irq(port->plat->rxq, QUEUE_IRQ_SRC_NOT_EMPTY, + eth_rx_irq, dev); + if (!ports_open) { + qmgr_set_irq(TXDONE_QUEUE, QUEUE_IRQ_SRC_NOT_EMPTY, + eth_txdone_irq, NULL); + qmgr_enable_irq(TXDONE_QUEUE); + } + ports_open++; + /* we may already have RX data, enables IRQ */ + netif_rx_schedule(dev, &port->napi); + return 0; +} + +static int eth_close(struct net_device *dev) +{ + struct port *port = netdev_priv(dev); + struct msg msg; + int buffs = RX_DESCS; /* allocated RX buffers */ + int i; + + ports_open--; + qmgr_disable_irq(port->plat->rxq); + napi_disable(&port->napi); + netif_stop_queue(dev); + + while (queue_get_desc(RXFREE_QUEUE(port->id), port, 0) >= 0) + buffs--; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = NPE_SETLOOPBACK_MODE; + msg.eth_id = port->id; + msg.byte3 = 1; + if (npe_send_recv_message(port->npe, &msg, "ETH_ENABLE_LOOPBACK")) + printk(KERN_CRIT "%s: unable to enable loopback\n", dev->name); + + i = 0; + do { /* drain RX buffers */ + while (queue_get_desc(port->plat->rxq, port, 0) >= 0) + buffs--; + if (!buffs) + break; + if (qmgr_stat_empty(TX_QUEUE(port->id))) { + /* we have to inject some packet */ + struct desc *desc; + u32 phys; + int n = queue_get_desc(port->plat->txreadyq, port, 1); + BUG_ON(n < 0); + desc = tx_desc_ptr(port, n); + phys = tx_desc_phys(port, n); + desc->buf_len = desc->pkt_len = 1; + wmb(); + queue_put_desc(TX_QUEUE(port->id), phys, desc); + } + udelay(1); + } while (++i < MAX_CLOSE_WAIT); + + if (buffs) + printk(KERN_CRIT "%s: unable to drain RX queue, %i buffer(s)" + " left in NPE\n", dev->name, buffs); +#if DEBUG_CLOSE + if (!buffs) + printk(KERN_DEBUG "Draining RX queue took %i cycles\n", i); +#endif + + buffs = TX_DESCS; + while (queue_get_desc(TX_QUEUE(port->id), port, 1) >= 0) + buffs--; /* cancel TX */ + + i = 0; + do { + while (queue_get_desc(port->plat->txreadyq, port, 1) >= 0) + buffs--; + if (!buffs) + break; + } while (++i < MAX_CLOSE_WAIT); + + if (buffs) + printk(KERN_CRIT "%s: unable to drain TX queue, %i buffer(s) " + "left in NPE\n", dev->name, buffs); +#if DEBUG_CLOSE + if (!buffs) + printk(KERN_DEBUG "Draining TX queues took %i cycles\n", i); +#endif + + msg.byte3 = 0; + if (npe_send_recv_message(port->npe, &msg, "ETH_DISABLE_LOOPBACK")) + printk(KERN_CRIT "%s: unable to disable loopback\n", + dev->name); + + port->mii_bmcr = mdio_read(dev, port->plat->phy, MII_BMCR) & + ~(BMCR_RESET | BMCR_PDOWN); /* may have been altered */ + mdio_write(dev, port->plat->phy, MII_BMCR, + port->mii_bmcr | BMCR_PDOWN); + + if (!ports_open) + qmgr_disable_irq(TXDONE_QUEUE); + cancel_rearming_delayed_work(&port->mdio_thread); + destroy_queues(port); + release_queues(port); + return 0; +} + +static int __devinit eth_init_one(struct platform_device *pdev) +{ + struct port *port; + struct net_device *dev; + struct eth_plat_info *plat = pdev->dev.platform_data; + u32 regs_phys; + int err; + + if (!(dev = alloc_etherdev(sizeof(struct port)))) + return -ENOMEM; + + SET_NETDEV_DEV(dev, &pdev->dev); + port = netdev_priv(dev); + port->netdev = dev; + port->id = pdev->id; + + switch (port->id) { + case IXP4XX_ETH_NPEA: + port->regs = (struct eth_regs __iomem *)IXP4XX_EthA_BASE_VIRT; + regs_phys = IXP4XX_EthA_BASE_PHYS; + break; + case IXP4XX_ETH_NPEB: + port->regs = (struct eth_regs __iomem *)IXP4XX_EthB_BASE_VIRT; + regs_phys = IXP4XX_EthB_BASE_PHYS; + break; + case IXP4XX_ETH_NPEC: + port->regs = (struct eth_regs __iomem *)IXP4XX_EthC_BASE_VIRT; + regs_phys = IXP4XX_EthC_BASE_PHYS; + break; + default: + err = -ENOSYS; + goto err_free; + } + + dev->open = eth_open; + dev->hard_start_xmit = eth_xmit; + dev->stop = eth_close; + dev->get_stats = eth_stats; + dev->do_ioctl = eth_ioctl; + dev->set_multicast_list = eth_set_mcast_list; + dev->tx_queue_len = 100; + + netif_napi_add(dev, &port->napi, eth_poll, NAPI_WEIGHT); + + if (!(port->npe = npe_request(NPE_ID(port->id)))) { + err = -EIO; + goto err_free; + } + + if (register_netdev(dev)) { + err = -EIO; + goto err_npe_rel; + } + + port->mem_res = request_mem_region(regs_phys, REGS_SIZE, dev->name); + if (!port->mem_res) { + err = -EBUSY; + goto err_unreg; + } + + port->plat = plat; + npe_port_tab[NPE_ID(port->id)] = port; + memcpy(dev->dev_addr, plat->hwaddr, ETH_ALEN); + + platform_set_drvdata(pdev, dev); + + __raw_writel(DEFAULT_CORE_CNTRL | CORE_RESET, + &port->regs->core_control); + udelay(50); + __raw_writel(DEFAULT_CORE_CNTRL, &port->regs->core_control); + udelay(50); + + port->mii.dev = dev; + port->mii.mdio_read = mdio_read; + port->mii.mdio_write = mdio_write; + port->mii.phy_id = plat->phy; + port->mii.phy_id_mask = 0x1F; + port->mii.reg_num_mask = 0x1F; + + printk(KERN_INFO "%s: MII PHY %i on %s\n", dev->name, plat->phy, + npe_name(port->npe)); + + phy_reset(dev, plat->phy); + port->mii_bmcr = mdio_read(dev, plat->phy, MII_BMCR) & + ~(BMCR_RESET | BMCR_PDOWN); + mdio_write(dev, plat->phy, MII_BMCR, port->mii_bmcr | BMCR_PDOWN); + + INIT_DELAYED_WORK(&port->mdio_thread, mdio_thread); + return 0; + +err_unreg: + unregister_netdev(dev); +err_npe_rel: + npe_release(port->npe); +err_free: + free_netdev(dev); + return err; +} + +static int __devexit eth_remove_one(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct port *port = netdev_priv(dev); + + unregister_netdev(dev); + npe_port_tab[NPE_ID(port->id)] = NULL; + platform_set_drvdata(pdev, NULL); + npe_release(port->npe); + release_resource(port->mem_res); + free_netdev(dev); + return 0; +} + +static struct platform_driver drv = { + .driver.name = DRV_NAME, + .probe = eth_init_one, + .remove = eth_remove_one, +}; + +static int __init eth_init_module(void) +{ + if (!(ixp4xx_read_feature_bits() & IXP4XX_FEATURE_NPEB_ETH0)) + return -ENOSYS; + + /* All MII PHY accesses use NPE-B Ethernet registers */ + spin_lock_init(&mdio_lock); + mdio_regs = (struct eth_regs __iomem *)IXP4XX_EthB_BASE_VIRT; + __raw_writel(DEFAULT_CORE_CNTRL, &mdio_regs->core_control); + + return platform_driver_register(&drv); +} + +static void __exit eth_cleanup_module(void) +{ + platform_driver_unregister(&drv); +} + +MODULE_AUTHOR("Krzysztof Halasa"); +MODULE_DESCRIPTION("Intel IXP4xx Ethernet driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:ixp4xx_eth"); +module_init(eth_init_module); +module_exit(eth_cleanup_module); diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c index 4fec8581bfd..89c0018132e 100644 --- a/drivers/net/bfin_mac.c +++ b/drivers/net/bfin_mac.c @@ -27,6 +27,7 @@ #include <linux/phy.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> +#include <linux/ethtool.h> #include <linux/skbuff.h> #include <linux/platform_device.h> @@ -42,7 +43,7 @@ #define DRV_NAME "bfin_mac" #define DRV_VERSION "1.1" #define DRV_AUTHOR "Bryan Wu, Luke Yang" -#define DRV_DESC "Blackfin BF53[67] BF527 on-chip Ethernet MAC driver" +#define DRV_DESC "Blackfin on-chip Ethernet MAC driver" MODULE_AUTHOR(DRV_AUTHOR); MODULE_LICENSE("GPL"); @@ -73,8 +74,14 @@ static struct net_dma_desc_tx *current_tx_ptr; static struct net_dma_desc_tx *tx_desc; static struct net_dma_desc_rx *rx_desc; -static void bf537mac_disable(void); -static void bf537mac_enable(void); +#if defined(CONFIG_BFIN_MAC_RMII) +static u16 pin_req[] = P_RMII0; +#else +static u16 pin_req[] = P_MII0; +#endif + +static void bfin_mac_disable(void); +static void bfin_mac_enable(void); static void desc_list_free(void) { @@ -243,27 +250,6 @@ init_error: /*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/ -/* Set FER regs to MUX in Ethernet pins */ -static int setup_pin_mux(int action) -{ -#if defined(CONFIG_BFIN_MAC_RMII) - u16 pin_req[] = P_RMII0; -#else - u16 pin_req[] = P_MII0; -#endif - - if (action) { - if (peripheral_request_list(pin_req, DRV_NAME)) { - printk(KERN_ERR DRV_NAME - ": Requesting Peripherals failed\n"); - return -EFAULT; - } - } else - peripheral_free_list(pin_req); - - return 0; -} - /* * MII operations */ @@ -322,9 +308,9 @@ static int mdiobus_reset(struct mii_bus *bus) return 0; } -static void bf537_adjust_link(struct net_device *dev) +static void bfin_mac_adjust_link(struct net_device *dev) { - struct bf537mac_local *lp = netdev_priv(dev); + struct bfin_mac_local *lp = netdev_priv(dev); struct phy_device *phydev = lp->phydev; unsigned long flags; int new_state = 0; @@ -395,7 +381,7 @@ static void bf537_adjust_link(struct net_device *dev) static int mii_probe(struct net_device *dev) { - struct bf537mac_local *lp = netdev_priv(dev); + struct bfin_mac_local *lp = netdev_priv(dev); struct phy_device *phydev = NULL; unsigned short sysctl; int i; @@ -431,10 +417,10 @@ static int mii_probe(struct net_device *dev) } #if defined(CONFIG_BFIN_MAC_RMII) - phydev = phy_connect(dev, phydev->dev.bus_id, &bf537_adjust_link, 0, + phydev = phy_connect(dev, phydev->dev.bus_id, &bfin_mac_adjust_link, 0, PHY_INTERFACE_MODE_RMII); #else - phydev = phy_connect(dev, phydev->dev.bus_id, &bf537_adjust_link, 0, + phydev = phy_connect(dev, phydev->dev.bus_id, &bfin_mac_adjust_link, 0, PHY_INTERFACE_MODE_MII); #endif @@ -469,6 +455,51 @@ static int mii_probe(struct net_device *dev) return 0; } +/* + * Ethtool support + */ + +static int +bfin_mac_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct bfin_mac_local *lp = netdev_priv(dev); + + if (lp->phydev) + return phy_ethtool_gset(lp->phydev, cmd); + + return -EINVAL; +} + +static int +bfin_mac_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct bfin_mac_local *lp = netdev_priv(dev); + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (lp->phydev) + return phy_ethtool_sset(lp->phydev, cmd); + + return -EINVAL; +} + +static void bfin_mac_ethtool_getdrvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->fw_version, "N/A"); + strcpy(info->bus_info, dev->dev.bus_id); +} + +static struct ethtool_ops bfin_mac_ethtool_ops = { + .get_settings = bfin_mac_ethtool_getsettings, + .set_settings = bfin_mac_ethtool_setsettings, + .get_link = ethtool_op_get_link, + .get_drvinfo = bfin_mac_ethtool_getdrvinfo, +}; + /**************************************************************************/ void setup_system_regs(struct net_device *dev) { @@ -511,7 +542,7 @@ static void setup_mac_addr(u8 *mac_addr) bfin_write_EMAC_ADDRHI(addr_hi); } -static int bf537mac_set_mac_address(struct net_device *dev, void *p) +static int bfin_mac_set_mac_address(struct net_device *dev, void *p) { struct sockaddr *addr = p; if (netif_running(dev)) @@ -573,7 +604,7 @@ adjust_head: } -static int bf537mac_hard_start_xmit(struct sk_buff *skb, +static int bfin_mac_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned int data; @@ -631,7 +662,7 @@ out: return 0; } -static void bf537mac_rx(struct net_device *dev) +static void bfin_mac_rx(struct net_device *dev) { struct sk_buff *skb, *new_skb; unsigned short len; @@ -680,7 +711,7 @@ out: } /* interrupt routine to handle rx and error signal */ -static irqreturn_t bf537mac_interrupt(int irq, void *dev_id) +static irqreturn_t bfin_mac_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; int number = 0; @@ -700,21 +731,21 @@ get_one_packet: } real_rx: - bf537mac_rx(dev); + bfin_mac_rx(dev); number++; goto get_one_packet; } #ifdef CONFIG_NET_POLL_CONTROLLER -static void bf537mac_poll(struct net_device *dev) +static void bfin_mac_poll(struct net_device *dev) { disable_irq(IRQ_MAC_RX); - bf537mac_interrupt(IRQ_MAC_RX, dev); + bfin_mac_interrupt(IRQ_MAC_RX, dev); enable_irq(IRQ_MAC_RX); } #endif /* CONFIG_NET_POLL_CONTROLLER */ -static void bf537mac_disable(void) +static void bfin_mac_disable(void) { unsigned int opmode; @@ -728,7 +759,7 @@ static void bf537mac_disable(void) /* * Enable Interrupts, Receive, and Transmit */ -static void bf537mac_enable(void) +static void bfin_mac_enable(void) { u32 opmode; @@ -766,23 +797,23 @@ static void bf537mac_enable(void) } /* Our watchdog timed out. Called by the networking layer */ -static void bf537mac_timeout(struct net_device *dev) +static void bfin_mac_timeout(struct net_device *dev) { pr_debug("%s: %s\n", dev->name, __FUNCTION__); - bf537mac_disable(); + bfin_mac_disable(); /* reset tx queue */ tx_list_tail = tx_list_head->next; - bf537mac_enable(); + bfin_mac_enable(); /* We can accept TX packets again */ dev->trans_start = jiffies; netif_wake_queue(dev); } -static void bf537mac_multicast_hash(struct net_device *dev) +static void bfin_mac_multicast_hash(struct net_device *dev) { u32 emac_hashhi, emac_hashlo; struct dev_mc_list *dmi = dev->mc_list; @@ -821,7 +852,7 @@ static void bf537mac_multicast_hash(struct net_device *dev) * promiscuous mode (for TCPDUMP and cousins) or accept * a select set of multicast packets */ -static void bf537mac_set_multicast_list(struct net_device *dev) +static void bfin_mac_set_multicast_list(struct net_device *dev) { u32 sysctl; @@ -840,7 +871,7 @@ static void bf537mac_set_multicast_list(struct net_device *dev) sysctl = bfin_read_EMAC_OPMODE(); sysctl |= HM; bfin_write_EMAC_OPMODE(sysctl); - bf537mac_multicast_hash(dev); + bfin_mac_multicast_hash(dev); } else { /* clear promisc or multicast mode */ sysctl = bfin_read_EMAC_OPMODE(); @@ -852,7 +883,7 @@ static void bf537mac_set_multicast_list(struct net_device *dev) /* * this puts the device in an inactive state */ -static void bf537mac_shutdown(struct net_device *dev) +static void bfin_mac_shutdown(struct net_device *dev) { /* Turn off the EMAC */ bfin_write_EMAC_OPMODE(0x00000000); @@ -866,9 +897,9 @@ static void bf537mac_shutdown(struct net_device *dev) * * Set up everything, reset the card, etc.. */ -static int bf537mac_open(struct net_device *dev) +static int bfin_mac_open(struct net_device *dev) { - struct bf537mac_local *lp = netdev_priv(dev); + struct bfin_mac_local *lp = netdev_priv(dev); int retval; pr_debug("%s: %s\n", dev->name, __FUNCTION__); @@ -891,8 +922,8 @@ static int bf537mac_open(struct net_device *dev) phy_start(lp->phydev); phy_write(lp->phydev, MII_BMCR, BMCR_RESET); setup_system_regs(dev); - bf537mac_disable(); - bf537mac_enable(); + bfin_mac_disable(); + bfin_mac_enable(); pr_debug("hardware init finished\n"); netif_start_queue(dev); netif_carrier_on(dev); @@ -906,9 +937,9 @@ static int bf537mac_open(struct net_device *dev) * and not talk to the outside world. Caused by * an 'ifconfig ethX down' */ -static int bf537mac_close(struct net_device *dev) +static int bfin_mac_close(struct net_device *dev) { - struct bf537mac_local *lp = netdev_priv(dev); + struct bfin_mac_local *lp = netdev_priv(dev); pr_debug("%s: %s\n", dev->name, __FUNCTION__); netif_stop_queue(dev); @@ -918,7 +949,7 @@ static int bf537mac_close(struct net_device *dev) phy_write(lp->phydev, MII_BMCR, BMCR_PDOWN); /* clear everything */ - bf537mac_shutdown(dev); + bfin_mac_shutdown(dev); /* free the rx/tx buffers */ desc_list_free(); @@ -926,46 +957,59 @@ static int bf537mac_close(struct net_device *dev) return 0; } -static int __init bf537mac_probe(struct net_device *dev) +static int __init bfin_mac_probe(struct platform_device *pdev) { - struct bf537mac_local *lp = netdev_priv(dev); - int retval; - int i; + struct net_device *ndev; + struct bfin_mac_local *lp; + int rc, i; + + ndev = alloc_etherdev(sizeof(struct bfin_mac_local)); + if (!ndev) { + dev_err(&pdev->dev, "Cannot allocate net device!\n"); + return -ENOMEM; + } + + SET_NETDEV_DEV(ndev, &pdev->dev); + platform_set_drvdata(pdev, ndev); + lp = netdev_priv(ndev); /* Grab the MAC address in the MAC */ - *(__le32 *) (&(dev->dev_addr[0])) = cpu_to_le32(bfin_read_EMAC_ADDRLO()); - *(__le16 *) (&(dev->dev_addr[4])) = cpu_to_le16((u16) bfin_read_EMAC_ADDRHI()); + *(__le32 *) (&(ndev->dev_addr[0])) = cpu_to_le32(bfin_read_EMAC_ADDRLO()); + *(__le16 *) (&(ndev->dev_addr[4])) = cpu_to_le16((u16) bfin_read_EMAC_ADDRHI()); /* probe mac */ /*todo: how to proble? which is revision_register */ bfin_write_EMAC_ADDRLO(0x12345678); if (bfin_read_EMAC_ADDRLO() != 0x12345678) { - pr_debug("can't detect bf537 mac!\n"); - retval = -ENODEV; - goto err_out; + dev_err(&pdev->dev, "Cannot detect Blackfin on-chip ethernet MAC controller!\n"); + rc = -ENODEV; + goto out_err_probe_mac; } /* set the GPIO pins to Ethernet mode */ - retval = setup_pin_mux(1); - if (retval) - return retval; - - /*Is it valid? (Did bootloader initialize it?) */ - if (!is_valid_ether_addr(dev->dev_addr)) { - /* Grab the MAC from the board somehow - this is done in the - arch/blackfin/mach-bf537/boards/eth_mac.c */ - bfin_get_ether_addr(dev->dev_addr); + rc = peripheral_request_list(pin_req, DRV_NAME); + if (rc) { + dev_err(&pdev->dev, "Requesting peripherals failed!\n"); + rc = -EFAULT; + goto out_err_setup_pin_mux; } + /* + * Is it valid? (Did bootloader initialize it?) + * Grab the MAC from the board somehow + * this is done in the arch/blackfin/mach-bfxxx/boards/eth_mac.c + */ + if (!is_valid_ether_addr(ndev->dev_addr)) + bfin_get_ether_addr(ndev->dev_addr); + /* If still not valid, get a random one */ - if (!is_valid_ether_addr(dev->dev_addr)) { - random_ether_addr(dev->dev_addr); - } + if (!is_valid_ether_addr(ndev->dev_addr)) + random_ether_addr(ndev->dev_addr); - setup_mac_addr(dev->dev_addr); + setup_mac_addr(ndev->dev_addr); /* MDIO bus initial */ - lp->mii_bus.priv = dev; + lp->mii_bus.priv = ndev; lp->mii_bus.read = mdiobus_read; lp->mii_bus.write = mdiobus_write; lp->mii_bus.reset = mdiobus_reset; @@ -975,86 +1019,86 @@ static int __init bf537mac_probe(struct net_device *dev) for (i = 0; i < PHY_MAX_ADDR; ++i) lp->mii_bus.irq[i] = PHY_POLL; - mdiobus_register(&lp->mii_bus); + rc = mdiobus_register(&lp->mii_bus); + if (rc) { + dev_err(&pdev->dev, "Cannot register MDIO bus!\n"); + goto out_err_mdiobus_register; + } - retval = mii_probe(dev); - if (retval) - return retval; + rc = mii_probe(ndev); + if (rc) { + dev_err(&pdev->dev, "MII Probe failed!\n"); + goto out_err_mii_probe; + } /* Fill in the fields of the device structure with ethernet values. */ - ether_setup(dev); - - dev->open = bf537mac_open; - dev->stop = bf537mac_close; - dev->hard_start_xmit = bf537mac_hard_start_xmit; - dev->set_mac_address = bf537mac_set_mac_address; - dev->tx_timeout = bf537mac_timeout; - dev->set_multicast_list = bf537mac_set_multicast_list; + ether_setup(ndev); + + ndev->open = bfin_mac_open; + ndev->stop = bfin_mac_close; + ndev->hard_start_xmit = bfin_mac_hard_start_xmit; + ndev->set_mac_address = bfin_mac_set_mac_address; + ndev->tx_timeout = bfin_mac_timeout; + ndev->set_multicast_list = bfin_mac_set_multicast_list; #ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = bf537mac_poll; + ndev->poll_controller = bfin_mac_poll; #endif + ndev->ethtool_ops = &bfin_mac_ethtool_ops; spin_lock_init(&lp->lock); /* now, enable interrupts */ /* register irq handler */ - if (request_irq - (IRQ_MAC_RX, bf537mac_interrupt, IRQF_DISABLED | IRQF_SHARED, - "EMAC_RX", dev)) { - printk(KERN_WARNING DRV_NAME - ": Unable to attach BlackFin MAC RX interrupt\n"); - return -EBUSY; + rc = request_irq(IRQ_MAC_RX, bfin_mac_interrupt, + IRQF_DISABLED | IRQF_SHARED, "EMAC_RX", ndev); + if (rc) { + dev_err(&pdev->dev, "Cannot request Blackfin MAC RX IRQ!\n"); + rc = -EBUSY; + goto out_err_request_irq; } - - retval = register_netdev(dev); - if (retval == 0) { - /* now, print out the card info, in a short format.. */ - printk(KERN_INFO "%s: Version %s, %s\n", - DRV_NAME, DRV_VERSION, DRV_DESC); - } - -err_out: - return retval; -} - -static int bfin_mac_probe(struct platform_device *pdev) -{ - struct net_device *ndev; - - ndev = alloc_etherdev(sizeof(struct bf537mac_local)); - if (!ndev) { - printk(KERN_WARNING DRV_NAME ": could not allocate device\n"); - return -ENOMEM; + rc = register_netdev(ndev); + if (rc) { + dev_err(&pdev->dev, "Cannot register net device!\n"); + goto out_err_reg_ndev; } - SET_NETDEV_DEV(ndev, &pdev->dev); + /* now, print out the card info, in a short format.. */ + dev_info(&pdev->dev, "%s, Version %s\n", DRV_DESC, DRV_VERSION); - platform_set_drvdata(pdev, ndev); + return 0; - if (bf537mac_probe(ndev) != 0) { - platform_set_drvdata(pdev, NULL); - free_netdev(ndev); - printk(KERN_WARNING DRV_NAME ": not found\n"); - return -ENODEV; - } +out_err_reg_ndev: + free_irq(IRQ_MAC_RX, ndev); +out_err_request_irq: +out_err_mii_probe: + mdiobus_unregister(&lp->mii_bus); +out_err_mdiobus_register: + peripheral_free_list(pin_req); +out_err_setup_pin_mux: +out_err_probe_mac: + platform_set_drvdata(pdev, NULL); + free_netdev(ndev); - return 0; + return rc; } static int bfin_mac_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); + struct bfin_mac_local *lp = netdev_priv(ndev); platform_set_drvdata(pdev, NULL); + mdiobus_unregister(&lp->mii_bus); + unregister_netdev(ndev); free_irq(IRQ_MAC_RX, ndev); free_netdev(ndev); - setup_pin_mux(0); + peripheral_free_list(pin_req); return 0; } @@ -1065,7 +1109,7 @@ static int bfin_mac_suspend(struct platform_device *pdev, pm_message_t mesg) struct net_device *net_dev = platform_get_drvdata(pdev); if (netif_running(net_dev)) - bf537mac_close(net_dev); + bfin_mac_close(net_dev); return 0; } @@ -1075,7 +1119,7 @@ static int bfin_mac_resume(struct platform_device *pdev) struct net_device *net_dev = platform_get_drvdata(pdev); if (netif_running(net_dev)) - bf537mac_open(net_dev); + bfin_mac_open(net_dev); return 0; } diff --git a/drivers/net/bfin_mac.h b/drivers/net/bfin_mac.h index f774d5a3694..beff51064ff 100644 --- a/drivers/net/bfin_mac.h +++ b/drivers/net/bfin_mac.h @@ -49,7 +49,7 @@ struct net_dma_desc_tx { struct status_area_tx status; }; -struct bf537mac_local { +struct bfin_mac_local { /* * these are things that the kernel wants me to keep, so users * can find out semi-useless statistics of how well the card is diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c index 83bda6ccde9..56f50491a45 100644 --- a/drivers/net/eepro.c +++ b/drivers/net/eepro.c @@ -633,7 +633,7 @@ static void __init printEEPROMInfo(struct net_device *dev) printk(KERN_DEBUG " PC: %d\n", GetBit(Word,ee_PC)); printk(KERN_DEBUG " TPE/AUI: %d\n", GetBit(Word,ee_TPE_AUI)); printk(KERN_DEBUG " Jabber: %d\n", GetBit(Word,ee_Jabber)); - printk(KERN_DEBUG " AutoPort: %d\n", GetBit(!Word,ee_Jabber)); + printk(KERN_DEBUG " AutoPort: %d\n", !GetBit(Word,ee_AutoPort)); printk(KERN_DEBUG " Duplex: %d\n", GetBit(Word,ee_Duplex)); } diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c index e5e6352556f..d21b7ab64bd 100644 --- a/drivers/net/fec_mpc52xx.c +++ b/drivers/net/fec_mpc52xx.c @@ -491,20 +491,23 @@ static irqreturn_t mpc52xx_fec_interrupt(int irq, void *dev_id) out_be32(&fec->ievent, ievent); /* clear pending events */ - if (ievent & ~(FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR)) { - if (ievent & ~FEC_IEVENT_TFINT) - dev_dbg(&dev->dev, "ievent: %08x\n", ievent); + /* on fifo error, soft-reset fec */ + if (ievent & (FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR)) { + + if (net_ratelimit() && (ievent & FEC_IEVENT_RFIFO_ERROR)) + dev_warn(&dev->dev, "FEC_IEVENT_RFIFO_ERROR\n"); + if (net_ratelimit() && (ievent & FEC_IEVENT_XFIFO_ERROR)) + dev_warn(&dev->dev, "FEC_IEVENT_XFIFO_ERROR\n"); + + mpc52xx_fec_reset(dev); + + netif_wake_queue(dev); return IRQ_HANDLED; } - if (net_ratelimit() && (ievent & FEC_IEVENT_RFIFO_ERROR)) - dev_warn(&dev->dev, "FEC_IEVENT_RFIFO_ERROR\n"); - if (net_ratelimit() && (ievent & FEC_IEVENT_XFIFO_ERROR)) - dev_warn(&dev->dev, "FEC_IEVENT_XFIFO_ERROR\n"); + if (ievent & ~FEC_IEVENT_TFINT) + dev_dbg(&dev->dev, "ievent: %08x\n", ievent); - mpc52xx_fec_reset(dev); - - netif_wake_queue(dev); return IRQ_HANDLED; } diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 99a4b990939..587afe7be68 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -131,8 +131,6 @@ static void free_skb_resources(struct gfar_private *priv); static void gfar_set_multi(struct net_device *dev); static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr); static void gfar_configure_serdes(struct net_device *dev); -extern int gfar_local_mdio_write(struct gfar_mii __iomem *regs, int mii_id, int regnum, u16 value); -extern int gfar_local_mdio_read(struct gfar_mii __iomem *regs, int mii_id, int regnum); #ifdef CONFIG_GFAR_NAPI static int gfar_poll(struct napi_struct *napi, int budget); #endif @@ -477,24 +475,30 @@ static int init_phy(struct net_device *dev) return 0; } +/* + * Initialize TBI PHY interface for communicating with the + * SERDES lynx PHY on the chip. We communicate with this PHY + * through the MDIO bus on each controller, treating it as a + * "normal" PHY at the address found in the TBIPA register. We assume + * that the TBIPA register is valid. Either the MDIO bus code will set + * it to a value that doesn't conflict with other PHYs on the bus, or the + * value doesn't matter, as there are no other PHYs on the bus. + */ static void gfar_configure_serdes(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); struct gfar_mii __iomem *regs = (void __iomem *)&priv->regs->gfar_mii_regs; + int tbipa = gfar_read(&priv->regs->tbipa); - /* Initialise TBI i/f to communicate with serdes (lynx phy) */ + /* Single clk mode, mii mode off(for serdes communication) */ + gfar_local_mdio_write(regs, tbipa, MII_TBICON, TBICON_CLK_SELECT); - /* Single clk mode, mii mode off(for aerdes communication) */ - gfar_local_mdio_write(regs, TBIPA_VALUE, MII_TBICON, TBICON_CLK_SELECT); - - /* Supported pause and full-duplex, no half-duplex */ - gfar_local_mdio_write(regs, TBIPA_VALUE, MII_ADVERTISE, + gfar_local_mdio_write(regs, tbipa, MII_ADVERTISE, ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM); - /* ANEG enable, restart ANEG, full duplex mode, speed[1] set */ - gfar_local_mdio_write(regs, TBIPA_VALUE, MII_BMCR, BMCR_ANENABLE | + gfar_local_mdio_write(regs, tbipa, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX | BMCR_SPEED1000); } @@ -541,9 +545,6 @@ static void init_registers(struct net_device *dev) /* Initialize the Minimum Frame Length Register */ gfar_write(&priv->regs->minflr, MINFLR_INIT_SETTINGS); - - /* Assign the TBI an address which won't conflict with the PHYs */ - gfar_write(&priv->regs->tbipa, TBIPA_VALUE); } diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index 0d088360946..fd487be3993 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -137,7 +137,6 @@ extern const char gfar_driver_version[]; #define DEFAULT_RXCOUNT 0 #endif /* CONFIG_GFAR_NAPI */ -#define TBIPA_VALUE 0x1f #define MIIMCFG_INIT_VALUE 0x00000007 #define MIIMCFG_RESET 0x80000000 #define MIIMIND_BUSY 0x00000001 diff --git a/drivers/net/gianfar_mii.c b/drivers/net/gianfar_mii.c index b8898927236..ebcfb27a904 100644 --- a/drivers/net/gianfar_mii.c +++ b/drivers/net/gianfar_mii.c @@ -78,7 +78,6 @@ int gfar_local_mdio_write(struct gfar_mii __iomem *regs, int mii_id, * same as system mdio bus, used for controlling the external PHYs, for eg. */ int gfar_local_mdio_read(struct gfar_mii __iomem *regs, int mii_id, int regnum) - { u16 value; @@ -122,7 +121,7 @@ int gfar_mdio_read(struct mii_bus *bus, int mii_id, int regnum) } /* Reset the MIIM registers, and wait for the bus to free */ -int gfar_mdio_reset(struct mii_bus *bus) +static int gfar_mdio_reset(struct mii_bus *bus) { struct gfar_mii __iomem *regs = (void __iomem *)bus->priv; unsigned int timeout = PHY_INIT_TIMEOUT; @@ -152,14 +151,15 @@ int gfar_mdio_reset(struct mii_bus *bus) } -int gfar_mdio_probe(struct device *dev) +static int gfar_mdio_probe(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct gianfar_mdio_data *pdata; struct gfar_mii __iomem *regs; + struct gfar __iomem *enet_regs; struct mii_bus *new_bus; struct resource *r; - int err = 0; + int i, err = 0; if (NULL == dev) return -EINVAL; @@ -199,6 +199,34 @@ int gfar_mdio_probe(struct device *dev) new_bus->dev = dev; dev_set_drvdata(dev, new_bus); + /* + * This is mildly evil, but so is our hardware for doing this. + * Also, we have to cast back to struct gfar_mii because of + * definition weirdness done in gianfar.h. + */ + enet_regs = (struct gfar __iomem *) + ((char *)regs - offsetof(struct gfar, gfar_mii_regs)); + + /* Scan the bus, looking for an empty spot for TBIPA */ + gfar_write(&enet_regs->tbipa, 0); + for (i = PHY_MAX_ADDR; i > 0; i--) { + u32 phy_id; + int r; + + r = get_phy_id(new_bus, i, &phy_id); + if (r) + return r; + + if (phy_id == 0xffffffff) + break; + } + + /* The bus is full. We don't support using 31 PHYs, sorry */ + if (i == 0) + return -EBUSY; + + gfar_write(&enet_regs->tbipa, i); + err = mdiobus_register(new_bus); if (0 != err) { @@ -218,7 +246,7 @@ reg_map_fail: } -int gfar_mdio_remove(struct device *dev) +static int gfar_mdio_remove(struct device *dev) { struct mii_bus *bus = dev_get_drvdata(dev); diff --git a/drivers/net/gianfar_mii.h b/drivers/net/gianfar_mii.h index b373091c703..2af28b16a0e 100644 --- a/drivers/net/gianfar_mii.h +++ b/drivers/net/gianfar_mii.h @@ -41,6 +41,9 @@ struct gfar_mii { int gfar_mdio_read(struct mii_bus *bus, int mii_id, int regnum); int gfar_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value); +int gfar_local_mdio_write(struct gfar_mii __iomem *regs, int mii_id, + int regnum, u16 value); +int gfar_local_mdio_read(struct gfar_mii __iomem *regs, int mii_id, int regnum); int __init gfar_mdio_init(void); void gfar_mdio_exit(void); #endif /* GIANFAR_PHY_H */ diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 1da55dd2a5a..9d5721287d6 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -148,13 +148,13 @@ static void sp_xmit_on_air(unsigned long channel) if (((sp->status1 & SIXP_DCD_MASK) == 0) && (random < sp->persistence)) { sp->led_state = 0x70; - sp->tty->driver->write(sp->tty, &sp->led_state, 1); + sp->tty->ops->write(sp->tty, &sp->led_state, 1); sp->tx_enable = 1; - actual = sp->tty->driver->write(sp->tty, sp->xbuff, sp->status2); + actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2); sp->xleft -= actual; sp->xhead += actual; sp->led_state = 0x60; - sp->tty->driver->write(sp->tty, &sp->led_state, 1); + sp->tty->ops->write(sp->tty, &sp->led_state, 1); sp->status2 = 0; } else mod_timer(&sp->tx_t, jiffies + ((when + 1) * HZ) / 100); @@ -220,13 +220,13 @@ static void sp_encaps(struct sixpack *sp, unsigned char *icp, int len) */ if (sp->duplex == 1) { sp->led_state = 0x70; - sp->tty->driver->write(sp->tty, &sp->led_state, 1); + sp->tty->ops->write(sp->tty, &sp->led_state, 1); sp->tx_enable = 1; - actual = sp->tty->driver->write(sp->tty, sp->xbuff, count); + actual = sp->tty->ops->write(sp->tty, sp->xbuff, count); sp->xleft = count - actual; sp->xhead = sp->xbuff + actual; sp->led_state = 0x60; - sp->tty->driver->write(sp->tty, &sp->led_state, 1); + sp->tty->ops->write(sp->tty, &sp->led_state, 1); } else { sp->xleft = count; sp->xhead = sp->xbuff; @@ -444,7 +444,7 @@ static void sixpack_write_wakeup(struct tty_struct *tty) } if (sp->tx_enable) { - actual = tty->driver->write(tty, sp->xhead, sp->xleft); + actual = tty->ops->write(tty, sp->xhead, sp->xleft); sp->xleft -= actual; sp->xhead += actual; } @@ -491,9 +491,7 @@ static void sixpack_receive_buf(struct tty_struct *tty, sixpack_decode(sp, buf, count1); sp_put(sp); - if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) - && tty->driver->unthrottle) - tty->driver->unthrottle(tty); + tty_unthrottle(tty); } /* @@ -554,8 +552,8 @@ static void resync_tnc(unsigned long channel) /* resync the TNC */ sp->led_state = 0x60; - sp->tty->driver->write(sp->tty, &sp->led_state, 1); - sp->tty->driver->write(sp->tty, &resync_cmd, 1); + sp->tty->ops->write(sp->tty, &sp->led_state, 1); + sp->tty->ops->write(sp->tty, &resync_cmd, 1); /* Start resync timer again -- the TNC might be still absent */ @@ -573,7 +571,7 @@ static inline int tnc_init(struct sixpack *sp) tnc_set_sync_state(sp, TNC_UNSYNC_STARTUP); - sp->tty->driver->write(sp->tty, &inbyte, 1); + sp->tty->ops->write(sp->tty, &inbyte, 1); del_timer(&sp->resync_t); sp->resync_t.data = (unsigned long) sp; @@ -601,6 +599,8 @@ static int sixpack_open(struct tty_struct *tty) if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (tty->ops->write == NULL) + return -EOPNOTSUPP; dev = alloc_netdev(sizeof(struct sixpack), "sp%d", sp_setup); if (!dev) { @@ -914,9 +914,9 @@ static void decode_prio_command(struct sixpack *sp, unsigned char cmd) } else { /* output watchdog char if idle */ if ((sp->status2 != 0) && (sp->duplex == 1)) { sp->led_state = 0x70; - sp->tty->driver->write(sp->tty, &sp->led_state, 1); + sp->tty->ops->write(sp->tty, &sp->led_state, 1); sp->tx_enable = 1; - actual = sp->tty->driver->write(sp->tty, sp->xbuff, sp->status2); + actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2); sp->xleft -= actual; sp->xhead += actual; sp->led_state = 0x60; @@ -926,7 +926,7 @@ static void decode_prio_command(struct sixpack *sp, unsigned char cmd) } /* needed to trigger the TNC watchdog */ - sp->tty->driver->write(sp->tty, &sp->led_state, 1); + sp->tty->ops->write(sp->tty, &sp->led_state, 1); /* if the state byte has been received, the TNC is present, so the resync timer can be reset. */ @@ -956,12 +956,12 @@ static void decode_std_command(struct sixpack *sp, unsigned char cmd) if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK) { sp->led_state = 0x68; - sp->tty->driver->write(sp->tty, &sp->led_state, 1); + sp->tty->ops->write(sp->tty, &sp->led_state, 1); } } else { sp->led_state = 0x60; /* fill trailing bytes with zeroes */ - sp->tty->driver->write(sp->tty, &sp->led_state, 1); + sp->tty->ops->write(sp->tty, &sp->led_state, 1); rest = sp->rx_count; if (rest != 0) for (i = rest; i <= 3; i++) diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 30c9b3b0d13..65166035aca 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -516,7 +516,7 @@ static void ax_encaps(struct net_device *dev, unsigned char *icp, int len) spin_unlock_bh(&ax->buflock); set_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); - actual = ax->tty->driver->write(ax->tty, ax->xbuff, count); + actual = ax->tty->ops->write(ax->tty, ax->xbuff, count); ax->stats.tx_packets++; ax->stats.tx_bytes += actual; @@ -546,7 +546,7 @@ static int ax_xmit(struct sk_buff *skb, struct net_device *dev) } printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name, - (ax->tty->driver->chars_in_buffer(ax->tty) || ax->xleft) ? + (ax->tty->ops->chars_in_buffer(ax->tty) || ax->xleft) ? "bad line quality" : "driver error"); ax->xleft = 0; @@ -736,6 +736,8 @@ static int mkiss_open(struct tty_struct *tty) if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (tty->ops->write == NULL) + return -EOPNOTSUPP; dev = alloc_netdev(sizeof(struct mkiss), "ax%d", ax_setup); if (!dev) { @@ -754,8 +756,7 @@ static int mkiss_open(struct tty_struct *tty) tty->disc_data = ax; tty->receive_room = 65535; - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty_driver_flush_buffer(tty); /* Restore default settings */ dev->type = ARPHRD_AX25; @@ -935,9 +936,7 @@ static void mkiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, } mkiss_put(ax); - if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) - && tty->driver->unthrottle) - tty->driver->unthrottle(tty); + tty_unthrottle(tty); } /* @@ -962,7 +961,7 @@ static void mkiss_write_wakeup(struct tty_struct *tty) goto out; } - actual = tty->driver->write(tty, ax->xhead, ax->xleft); + actual = tty->ops->write(tty, ax->xhead, ax->xleft); ax->xleft -= actual; ax->xhead += actual; diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index fc753d7f674..e6f40b7f904 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -64,7 +64,7 @@ static int irtty_chars_in_buffer(struct sir_dev *dev) IRDA_ASSERT(priv != NULL, return -1;); IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); - return priv->tty->driver->chars_in_buffer(priv->tty); + return tty_chars_in_buffer(priv->tty); } /* Wait (sleep) until underlaying hardware finished transmission @@ -93,10 +93,8 @@ static void irtty_wait_until_sent(struct sir_dev *dev) IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); tty = priv->tty; - if (tty->driver->wait_until_sent) { - lock_kernel(); - tty->driver->wait_until_sent(tty, msecs_to_jiffies(100)); - unlock_kernel(); + if (tty->ops->wait_until_sent) { + tty->ops->wait_until_sent(tty, msecs_to_jiffies(100)); } else { msleep(USBSERIAL_TX_DONE_DELAY); @@ -125,48 +123,14 @@ static int irtty_change_speed(struct sir_dev *dev, unsigned speed) tty = priv->tty; - lock_kernel(); + mutex_lock(&tty->termios_mutex); old_termios = *(tty->termios); cflag = tty->termios->c_cflag; - - cflag &= ~CBAUD; - - IRDA_DEBUG(2, "%s(), Setting speed to %d\n", __FUNCTION__, speed); - - switch (speed) { - case 1200: - cflag |= B1200; - break; - case 2400: - cflag |= B2400; - break; - case 4800: - cflag |= B4800; - break; - case 19200: - cflag |= B19200; - break; - case 38400: - cflag |= B38400; - break; - case 57600: - cflag |= B57600; - break; - case 115200: - cflag |= B115200; - break; - case 9600: - default: - cflag |= B9600; - break; - } - - tty->termios->c_cflag = cflag; - if (tty->driver->set_termios) - tty->driver->set_termios(tty, &old_termios); - unlock_kernel(); - + tty_encode_baud_rate(tty, speed, speed); + if (tty->ops->set_termios) + tty->ops->set_termios(tty, &old_termios); priv->io.speed = speed; + mutex_unlock(&tty->termios_mutex); return 0; } @@ -202,8 +166,8 @@ static int irtty_set_dtr_rts(struct sir_dev *dev, int dtr, int rts) * This function is not yet defined for all tty driver, so * let's be careful... Jean II */ - IRDA_ASSERT(priv->tty->driver->tiocmset != NULL, return -1;); - priv->tty->driver->tiocmset(priv->tty, NULL, set, clear); + IRDA_ASSERT(priv->tty->ops->tiocmset != NULL, return -1;); + priv->tty->ops->tiocmset(priv->tty, NULL, set, clear); return 0; } @@ -225,17 +189,13 @@ static int irtty_do_write(struct sir_dev *dev, const unsigned char *ptr, size_t IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); tty = priv->tty; - if (!tty->driver->write) + if (!tty->ops->write) return 0; tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - if (tty->driver->write_room) { - writelen = tty->driver->write_room(tty); - if (writelen > len) - writelen = len; - } - else + writelen = tty_write_room(tty); + if (writelen > len) writelen = len; - return tty->driver->write(tty, ptr, writelen); + return tty->ops->write(tty, ptr, writelen); } /* ------------------------------------------------------- */ @@ -321,7 +281,7 @@ static inline void irtty_stop_receiver(struct tty_struct *tty, int stop) struct ktermios old_termios; int cflag; - lock_kernel(); + mutex_lock(&tty->termios_mutex); old_termios = *(tty->termios); cflag = tty->termios->c_cflag; @@ -331,9 +291,9 @@ static inline void irtty_stop_receiver(struct tty_struct *tty, int stop) cflag |= CREAD; tty->termios->c_cflag = cflag; - if (tty->driver->set_termios) - tty->driver->set_termios(tty, &old_termios); - unlock_kernel(); + if (tty->ops->set_termios) + tty->ops->set_termios(tty, &old_termios); + mutex_unlock(&tty->termios_mutex); } /*****************************************************************/ @@ -359,8 +319,8 @@ static int irtty_start_dev(struct sir_dev *dev) tty = priv->tty; - if (tty->driver->start) - tty->driver->start(tty); + if (tty->ops->start) + tty->ops->start(tty); /* Make sure we can receive more data */ irtty_stop_receiver(tty, FALSE); @@ -388,8 +348,8 @@ static int irtty_stop_dev(struct sir_dev *dev) /* Make sure we don't receive more data */ irtty_stop_receiver(tty, TRUE); - if (tty->driver->stop) - tty->driver->stop(tty); + if (tty->ops->stop) + tty->ops->stop(tty); mutex_unlock(&irtty_mutex); @@ -483,11 +443,10 @@ static int irtty_open(struct tty_struct *tty) /* stop the underlying driver */ irtty_stop_receiver(tty, TRUE); - if (tty->driver->stop) - tty->driver->stop(tty); + if (tty->ops->stop) + tty->ops->stop(tty); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty_driver_flush_buffer(tty); /* apply mtt override */ sir_tty_drv.qos_mtt_bits = qos_mtt_bits; @@ -564,8 +523,8 @@ static void irtty_close(struct tty_struct *tty) /* Stop tty */ irtty_stop_receiver(tty, TRUE); tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); - if (tty->driver->stop) - tty->driver->stop(tty); + if (tty->ops->stop) + tty->ops->stop(tty); kfree(priv); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 3ac8529bb92..6bf9e76b0a0 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -48,7 +48,7 @@ config VITESSE_PHY config SMSC_PHY tristate "Drivers for SMSC PHYs" ---help--- - Currently supports the LAN83C185 PHY + Currently supports the LAN83C185, LAN8187 and LAN8700 PHYs config BROADCOM_PHY tristate "Drivers for Broadcom PHYs" diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index ddf8d51832a..ac3c01d28fd 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -256,7 +256,7 @@ void phy_prepare_link(struct phy_device *phydev, /** * phy_connect - connect an ethernet device to a PHY device * @dev: the network device to connect - * @phy_id: the PHY device to connect + * @bus_id: the id string of the PHY device to connect * @handler: callback function for state change notifications * @flags: PHY device's dev_flags * @interface: PHY device's interface diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index b1d8ed40ad9..73baa7a3bb0 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -12,6 +12,8 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * + * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@smsc.com + * */ #include <linux/kernel.h> @@ -38,7 +40,7 @@ (MII_LAN83C185_ISF_INT6 | MII_LAN83C185_ISF_INT4) -static int lan83c185_config_intr(struct phy_device *phydev) +static int smsc_phy_config_intr(struct phy_device *phydev) { int rc = phy_write (phydev, MII_LAN83C185_IM, ((PHY_INTERRUPT_ENABLED == phydev->interrupts) @@ -48,16 +50,16 @@ static int lan83c185_config_intr(struct phy_device *phydev) return rc < 0 ? rc : 0; } -static int lan83c185_ack_interrupt(struct phy_device *phydev) +static int smsc_phy_ack_interrupt(struct phy_device *phydev) { int rc = phy_read (phydev, MII_LAN83C185_ISF); return rc < 0 ? rc : 0; } -static int lan83c185_config_init(struct phy_device *phydev) +static int smsc_phy_config_init(struct phy_device *phydev) { - return lan83c185_ack_interrupt (phydev); + return smsc_phy_ack_interrupt (phydev); } @@ -73,22 +75,87 @@ static struct phy_driver lan83c185_driver = { /* basic functions */ .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .config_init = lan83c185_config_init, + .config_init = smsc_phy_config_init, /* IRQ related */ - .ack_interrupt = lan83c185_ack_interrupt, - .config_intr = lan83c185_config_intr, + .ack_interrupt = smsc_phy_ack_interrupt, + .config_intr = smsc_phy_config_intr, + + .driver = { .owner = THIS_MODULE, } +}; + +static struct phy_driver lan8187_driver = { + .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ + .phy_id_mask = 0xfffffff0, + .name = "SMSC LAN8187", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .config_init = smsc_phy_config_init, + + /* IRQ related */ + .ack_interrupt = smsc_phy_ack_interrupt, + .config_intr = smsc_phy_config_intr, + + .driver = { .owner = THIS_MODULE, } +}; + +static struct phy_driver lan8700_driver = { + .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ + .phy_id_mask = 0xfffffff0, + .name = "SMSC LAN8700", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .config_init = smsc_phy_config_init, + + /* IRQ related */ + .ack_interrupt = smsc_phy_ack_interrupt, + .config_intr = smsc_phy_config_intr, .driver = { .owner = THIS_MODULE, } }; static int __init smsc_init(void) { - return phy_driver_register (&lan83c185_driver); + int ret; + + ret = phy_driver_register (&lan83c185_driver); + if (ret) + goto err1; + + ret = phy_driver_register (&lan8187_driver); + if (ret) + goto err2; + + ret = phy_driver_register (&lan8700_driver); + if (ret) + goto err3; + + return 0; + +err3: + phy_driver_unregister (&lan8187_driver); +err2: + phy_driver_unregister (&lan83c185_driver); +err1: + return ret; } static void __exit smsc_exit(void) { + phy_driver_unregister (&lan8700_driver); + phy_driver_unregister (&lan8187_driver); phy_driver_unregister (&lan83c185_driver); } diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index f023d5b67e6..f1a52def124 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -158,6 +158,9 @@ ppp_asynctty_open(struct tty_struct *tty) struct asyncppp *ap; int err; + if (tty->ops->write == NULL) + return -EOPNOTSUPP; + err = -ENOMEM; ap = kzalloc(sizeof(*ap), GFP_KERNEL); if (!ap) @@ -358,9 +361,7 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, if (!skb_queue_empty(&ap->rqueue)) tasklet_schedule(&ap->tsk); ap_put(ap); - if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) - && tty->driver->unthrottle) - tty->driver->unthrottle(tty); + tty_unthrottle(tty); } static void @@ -676,7 +677,7 @@ ppp_async_push(struct asyncppp *ap) if (!tty_stuffed && ap->optr < ap->olim) { avail = ap->olim - ap->optr; set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - sent = tty->driver->write(tty, ap->optr, avail); + sent = tty->ops->write(tty, ap->optr, avail); if (sent < 0) goto flush; /* error, e.g. loss of CD */ ap->optr += sent; diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index 0d80fa54671..b8f0369a71e 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -207,6 +207,9 @@ ppp_sync_open(struct tty_struct *tty) struct syncppp *ap; int err; + if (tty->ops->write == NULL) + return -EOPNOTSUPP; + ap = kzalloc(sizeof(*ap), GFP_KERNEL); err = -ENOMEM; if (!ap) @@ -398,9 +401,7 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, if (!skb_queue_empty(&ap->rqueue)) tasklet_schedule(&ap->tsk); sp_put(ap); - if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) - && tty->driver->unthrottle) - tty->driver->unthrottle(tty); + tty_unthrottle(tty); } static void @@ -653,7 +654,7 @@ ppp_sync_push(struct syncppp *ap) tty_stuffed = 0; if (!tty_stuffed && ap->tpkt) { set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - sent = tty->driver->write(tty, ap->tpkt->data, ap->tpkt->len); + sent = tty->ops->write(tty, ap->tpkt->data, ap->tpkt->len); if (sent < 0) goto flush; /* error, e.g. loss of CD */ if (sent < ap->tpkt->len) { diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 3acfeeabdee..65724250462 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -1617,6 +1617,7 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) SET_NETDEV_DEV(dev, &pdev->dev); tp = netdev_priv(dev); tp->dev = dev; + tp->pci_dev = pdev; tp->msg_enable = netif_msg_init(debug.msg_enable, R8169_MSG_DEFAULT); /* enable device (incl. PCI PM wakeup and hotplug setup) */ @@ -1705,18 +1706,18 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) rtl8169_print_mac_version(tp); - for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--) { + for (i = 0; i < ARRAY_SIZE(rtl_chip_info); i++) { if (tp->mac_version == rtl_chip_info[i].mac_version) break; } - if (i < 0) { + if (i == ARRAY_SIZE(rtl_chip_info)) { /* Unknown chip: assume array element #0, original RTL-8169 */ if (netif_msg_probe(tp)) { dev_printk(KERN_DEBUG, &pdev->dev, "unknown chip version, assuming %s\n", rtl_chip_info[0].name); } - i++; + i = 0; } tp->chipset = i; @@ -1777,7 +1778,6 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) #endif tp->intr_mask = 0xffff; - tp->pci_dev = pdev; tp->mmio_addr = ioaddr; tp->align = cfg->align; tp->hw_start = cfg->hw_start; diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 157fd932e95..523478ebfd6 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -86,7 +86,7 @@ #include "s2io.h" #include "s2io-regs.h" -#define DRV_VERSION "2.0.26.22" +#define DRV_VERSION "2.0.26.23" /* S2io Driver name & version. */ static char s2io_driver_name[] = "Neterion"; @@ -809,6 +809,7 @@ static int init_shared_mem(struct s2io_nic *nic) config->rx_cfg[i].num_rxd - 1; mac_control->rings[i].nic = nic; mac_control->rings[i].ring_no = i; + mac_control->rings[i].lro = lro_enable; blk_cnt = config->rx_cfg[i].num_rxd / (rxd_count[nic->rxd_mode] + 1); @@ -1560,113 +1561,112 @@ static int init_nic(struct s2io_nic *nic) writeq(val64, &bar0->tx_fifo_partition_0); /* Filling the Rx round robin registers as per the - * number of Rings and steering based on QoS. - */ + * number of Rings and steering based on QoS with + * equal priority. + */ switch (config->rx_ring_num) { case 1: + val64 = 0x0; + writeq(val64, &bar0->rx_w_round_robin_0); + writeq(val64, &bar0->rx_w_round_robin_1); + writeq(val64, &bar0->rx_w_round_robin_2); + writeq(val64, &bar0->rx_w_round_robin_3); + writeq(val64, &bar0->rx_w_round_robin_4); + val64 = 0x8080808080808080ULL; writeq(val64, &bar0->rts_qos_steering); break; case 2: - val64 = 0x0000010000010000ULL; + val64 = 0x0001000100010001ULL; writeq(val64, &bar0->rx_w_round_robin_0); - val64 = 0x0100000100000100ULL; writeq(val64, &bar0->rx_w_round_robin_1); - val64 = 0x0001000001000001ULL; writeq(val64, &bar0->rx_w_round_robin_2); - val64 = 0x0000010000010000ULL; writeq(val64, &bar0->rx_w_round_robin_3); - val64 = 0x0100000000000000ULL; + val64 = 0x0001000100000000ULL; writeq(val64, &bar0->rx_w_round_robin_4); val64 = 0x8080808040404040ULL; writeq(val64, &bar0->rts_qos_steering); break; case 3: - val64 = 0x0001000102000001ULL; + val64 = 0x0001020001020001ULL; writeq(val64, &bar0->rx_w_round_robin_0); - val64 = 0x0001020000010001ULL; + val64 = 0x0200010200010200ULL; writeq(val64, &bar0->rx_w_round_robin_1); - val64 = 0x0200000100010200ULL; + val64 = 0x0102000102000102ULL; writeq(val64, &bar0->rx_w_round_robin_2); - val64 = 0x0001000102000001ULL; + val64 = 0x0001020001020001ULL; writeq(val64, &bar0->rx_w_round_robin_3); - val64 = 0x0001020000000000ULL; + val64 = 0x0200010200000000ULL; writeq(val64, &bar0->rx_w_round_robin_4); val64 = 0x8080804040402020ULL; writeq(val64, &bar0->rts_qos_steering); break; case 4: - val64 = 0x0001020300010200ULL; + val64 = 0x0001020300010203ULL; writeq(val64, &bar0->rx_w_round_robin_0); - val64 = 0x0100000102030001ULL; writeq(val64, &bar0->rx_w_round_robin_1); - val64 = 0x0200010000010203ULL; writeq(val64, &bar0->rx_w_round_robin_2); - val64 = 0x0001020001000001ULL; writeq(val64, &bar0->rx_w_round_robin_3); - val64 = 0x0203000100000000ULL; + val64 = 0x0001020300000000ULL; writeq(val64, &bar0->rx_w_round_robin_4); val64 = 0x8080404020201010ULL; writeq(val64, &bar0->rts_qos_steering); break; case 5: - val64 = 0x0001000203000102ULL; + val64 = 0x0001020304000102ULL; writeq(val64, &bar0->rx_w_round_robin_0); - val64 = 0x0001020001030004ULL; + val64 = 0x0304000102030400ULL; writeq(val64, &bar0->rx_w_round_robin_1); - val64 = 0x0001000203000102ULL; + val64 = 0x0102030400010203ULL; writeq(val64, &bar0->rx_w_round_robin_2); - val64 = 0x0001020001030004ULL; + val64 = 0x0400010203040001ULL; writeq(val64, &bar0->rx_w_round_robin_3); - val64 = 0x0001000000000000ULL; + val64 = 0x0203040000000000ULL; writeq(val64, &bar0->rx_w_round_robin_4); val64 = 0x8080404020201008ULL; writeq(val64, &bar0->rts_qos_steering); break; case 6: - val64 = 0x0001020304000102ULL; + val64 = 0x0001020304050001ULL; writeq(val64, &bar0->rx_w_round_robin_0); - val64 = 0x0304050001020001ULL; + val64 = 0x0203040500010203ULL; writeq(val64, &bar0->rx_w_round_robin_1); - val64 = 0x0203000100000102ULL; + val64 = 0x0405000102030405ULL; writeq(val64, &bar0->rx_w_round_robin_2); - val64 = 0x0304000102030405ULL; + val64 = 0x0001020304050001ULL; writeq(val64, &bar0->rx_w_round_robin_3); - val64 = 0x0001000200000000ULL; + val64 = 0x0203040500000000ULL; writeq(val64, &bar0->rx_w_round_robin_4); val64 = 0x8080404020100804ULL; writeq(val64, &bar0->rts_qos_steering); break; case 7: - val64 = 0x0001020001020300ULL; + val64 = 0x0001020304050600ULL; writeq(val64, &bar0->rx_w_round_robin_0); - val64 = 0x0102030400010203ULL; + val64 = 0x0102030405060001ULL; writeq(val64, &bar0->rx_w_round_robin_1); - val64 = 0x0405060001020001ULL; + val64 = 0x0203040506000102ULL; writeq(val64, &bar0->rx_w_round_robin_2); - val64 = 0x0304050000010200ULL; + val64 = 0x0304050600010203ULL; writeq(val64, &bar0->rx_w_round_robin_3); - val64 = 0x0102030000000000ULL; + val64 = 0x0405060000000000ULL; writeq(val64, &bar0->rx_w_round_robin_4); val64 = 0x8080402010080402ULL; writeq(val64, &bar0->rts_qos_steering); break; case 8: - val64 = 0x0001020300040105ULL; + val64 = 0x0001020304050607ULL; writeq(val64, &bar0->rx_w_round_robin_0); - val64 = 0x0200030106000204ULL; writeq(val64, &bar0->rx_w_round_robin_1); - val64 = 0x0103000502010007ULL; writeq(val64, &bar0->rx_w_round_robin_2); - val64 = 0x0304010002060500ULL; writeq(val64, &bar0->rx_w_round_robin_3); - val64 = 0x0103020400000000ULL; + val64 = 0x0001020300000000ULL; writeq(val64, &bar0->rx_w_round_robin_4); val64 = 0x8040201008040201ULL; @@ -2499,8 +2499,7 @@ static void stop_nic(struct s2io_nic *nic) /** * fill_rx_buffers - Allocates the Rx side skbs - * @nic: device private variable - * @ring_no: ring number + * @ring_info: per ring structure * Description: * The function allocates Rx side skbs and puts the physical * address of these buffers into the RxD buffer pointers, so that the NIC @@ -2518,103 +2517,94 @@ static void stop_nic(struct s2io_nic *nic) * SUCCESS on success or an appropriate -ve value on failure. */ -static int fill_rx_buffers(struct s2io_nic *nic, int ring_no) +static int fill_rx_buffers(struct ring_info *ring) { - struct net_device *dev = nic->dev; struct sk_buff *skb; struct RxD_t *rxdp; - int off, off1, size, block_no, block_no1; + int off, size, block_no, block_no1; u32 alloc_tab = 0; u32 alloc_cnt; - struct mac_info *mac_control; - struct config_param *config; u64 tmp; struct buffAdd *ba; struct RxD_t *first_rxdp = NULL; u64 Buffer0_ptr = 0, Buffer1_ptr = 0; + int rxd_index = 0; struct RxD1 *rxdp1; struct RxD3 *rxdp3; - struct swStat *stats = &nic->mac_control.stats_info->sw_stat; + struct swStat *stats = &ring->nic->mac_control.stats_info->sw_stat; - mac_control = &nic->mac_control; - config = &nic->config; - alloc_cnt = mac_control->rings[ring_no].pkt_cnt - - atomic_read(&nic->rx_bufs_left[ring_no]); + alloc_cnt = ring->pkt_cnt - ring->rx_bufs_left; - block_no1 = mac_control->rings[ring_no].rx_curr_get_info.block_index; - off1 = mac_control->rings[ring_no].rx_curr_get_info.offset; + block_no1 = ring->rx_curr_get_info.block_index; while (alloc_tab < alloc_cnt) { - block_no = mac_control->rings[ring_no].rx_curr_put_info. - block_index; - off = mac_control->rings[ring_no].rx_curr_put_info.offset; + block_no = ring->rx_curr_put_info.block_index; - rxdp = mac_control->rings[ring_no]. - rx_blocks[block_no].rxds[off].virt_addr; + off = ring->rx_curr_put_info.offset; + + rxdp = ring->rx_blocks[block_no].rxds[off].virt_addr; + + rxd_index = off + 1; + if (block_no) + rxd_index += (block_no * ring->rxd_count); - if ((block_no == block_no1) && (off == off1) && - (rxdp->Host_Control)) { + if ((block_no == block_no1) && + (off == ring->rx_curr_get_info.offset) && + (rxdp->Host_Control)) { DBG_PRINT(INTR_DBG, "%s: Get and Put", - dev->name); + ring->dev->name); DBG_PRINT(INTR_DBG, " info equated\n"); goto end; } - if (off && (off == rxd_count[nic->rxd_mode])) { - mac_control->rings[ring_no].rx_curr_put_info. - block_index++; - if (mac_control->rings[ring_no].rx_curr_put_info. - block_index == mac_control->rings[ring_no]. - block_count) - mac_control->rings[ring_no].rx_curr_put_info. - block_index = 0; - block_no = mac_control->rings[ring_no]. - rx_curr_put_info.block_index; - if (off == rxd_count[nic->rxd_mode]) - off = 0; - mac_control->rings[ring_no].rx_curr_put_info. - offset = off; - rxdp = mac_control->rings[ring_no]. - rx_blocks[block_no].block_virt_addr; + if (off && (off == ring->rxd_count)) { + ring->rx_curr_put_info.block_index++; + if (ring->rx_curr_put_info.block_index == + ring->block_count) + ring->rx_curr_put_info.block_index = 0; + block_no = ring->rx_curr_put_info.block_index; + off = 0; + ring->rx_curr_put_info.offset = off; + rxdp = ring->rx_blocks[block_no].block_virt_addr; DBG_PRINT(INTR_DBG, "%s: Next block at: %p\n", - dev->name, rxdp); + ring->dev->name, rxdp); + } if ((rxdp->Control_1 & RXD_OWN_XENA) && - ((nic->rxd_mode == RXD_MODE_3B) && + ((ring->rxd_mode == RXD_MODE_3B) && (rxdp->Control_2 & s2BIT(0)))) { - mac_control->rings[ring_no].rx_curr_put_info. - offset = off; + ring->rx_curr_put_info.offset = off; goto end; } /* calculate size of skb based on ring mode */ - size = dev->mtu + HEADER_ETHERNET_II_802_3_SIZE + + size = ring->mtu + HEADER_ETHERNET_II_802_3_SIZE + HEADER_802_2_SIZE + HEADER_SNAP_SIZE; - if (nic->rxd_mode == RXD_MODE_1) + if (ring->rxd_mode == RXD_MODE_1) size += NET_IP_ALIGN; else - size = dev->mtu + ALIGN_SIZE + BUF0_LEN + 4; + size = ring->mtu + ALIGN_SIZE + BUF0_LEN + 4; /* allocate skb */ skb = dev_alloc_skb(size); if(!skb) { - DBG_PRINT(INFO_DBG, "%s: Out of ", dev->name); + DBG_PRINT(INFO_DBG, "%s: Out of ", ring->dev->name); DBG_PRINT(INFO_DBG, "memory to allocate SKBs\n"); if (first_rxdp) { wmb(); first_rxdp->Control_1 |= RXD_OWN_XENA; } - nic->mac_control.stats_info->sw_stat. \ - mem_alloc_fail_cnt++; + stats->mem_alloc_fail_cnt++; + return -ENOMEM ; } - nic->mac_control.stats_info->sw_stat.mem_allocated - += skb->truesize; - if (nic->rxd_mode == RXD_MODE_1) { + stats->mem_allocated += skb->truesize; + + if (ring->rxd_mode == RXD_MODE_1) { /* 1 buffer mode - normal operation mode */ rxdp1 = (struct RxD1*)rxdp; memset(rxdp, 0, sizeof(struct RxD1)); skb_reserve(skb, NET_IP_ALIGN); rxdp1->Buffer0_ptr = pci_map_single - (nic->pdev, skb->data, size - NET_IP_ALIGN, + (ring->pdev, skb->data, size - NET_IP_ALIGN, PCI_DMA_FROMDEVICE); if( (rxdp1->Buffer0_ptr == 0) || (rxdp1->Buffer0_ptr == @@ -2623,8 +2613,8 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no) rxdp->Control_2 = SET_BUFFER0_SIZE_1(size - NET_IP_ALIGN); - - } else if (nic->rxd_mode == RXD_MODE_3B) { + rxdp->Host_Control = (unsigned long) (skb); + } else if (ring->rxd_mode == RXD_MODE_3B) { /* * 2 buffer mode - * 2 buffer mode provides 128 @@ -2640,7 +2630,7 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no) rxdp3->Buffer0_ptr = Buffer0_ptr; rxdp3->Buffer1_ptr = Buffer1_ptr; - ba = &mac_control->rings[ring_no].ba[block_no][off]; + ba = &ring->ba[block_no][off]; skb_reserve(skb, BUF0_LEN); tmp = (u64)(unsigned long) skb->data; tmp += ALIGN_SIZE; @@ -2650,10 +2640,10 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no) if (!(rxdp3->Buffer0_ptr)) rxdp3->Buffer0_ptr = - pci_map_single(nic->pdev, ba->ba_0, BUF0_LEN, - PCI_DMA_FROMDEVICE); + pci_map_single(ring->pdev, ba->ba_0, + BUF0_LEN, PCI_DMA_FROMDEVICE); else - pci_dma_sync_single_for_device(nic->pdev, + pci_dma_sync_single_for_device(ring->pdev, (dma_addr_t) rxdp3->Buffer0_ptr, BUF0_LEN, PCI_DMA_FROMDEVICE); if( (rxdp3->Buffer0_ptr == 0) || @@ -2661,7 +2651,7 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no) goto pci_map_failed; rxdp->Control_2 = SET_BUFFER0_SIZE_3(BUF0_LEN); - if (nic->rxd_mode == RXD_MODE_3B) { + if (ring->rxd_mode == RXD_MODE_3B) { /* Two buffer mode */ /* @@ -2669,39 +2659,42 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no) * L4 payload */ rxdp3->Buffer2_ptr = pci_map_single - (nic->pdev, skb->data, dev->mtu + 4, + (ring->pdev, skb->data, ring->mtu + 4, PCI_DMA_FROMDEVICE); if( (rxdp3->Buffer2_ptr == 0) || (rxdp3->Buffer2_ptr == DMA_ERROR_CODE)) goto pci_map_failed; - rxdp3->Buffer1_ptr = - pci_map_single(nic->pdev, + if (!rxdp3->Buffer1_ptr) + rxdp3->Buffer1_ptr = + pci_map_single(ring->pdev, ba->ba_1, BUF1_LEN, PCI_DMA_FROMDEVICE); + if( (rxdp3->Buffer1_ptr == 0) || (rxdp3->Buffer1_ptr == DMA_ERROR_CODE)) { pci_unmap_single - (nic->pdev, - (dma_addr_t)rxdp3->Buffer2_ptr, - dev->mtu + 4, + (ring->pdev, + (dma_addr_t)(unsigned long) + skb->data, + ring->mtu + 4, PCI_DMA_FROMDEVICE); goto pci_map_failed; } rxdp->Control_2 |= SET_BUFFER1_SIZE_3(1); rxdp->Control_2 |= SET_BUFFER2_SIZE_3 - (dev->mtu + 4); + (ring->mtu + 4); } rxdp->Control_2 |= s2BIT(0); + rxdp->Host_Control = (unsigned long) (skb); } - rxdp->Host_Control = (unsigned long) (skb); if (alloc_tab & ((1 << rxsync_frequency) - 1)) rxdp->Control_1 |= RXD_OWN_XENA; off++; - if (off == (rxd_count[nic->rxd_mode] + 1)) + if (off == (ring->rxd_count + 1)) off = 0; - mac_control->rings[ring_no].rx_curr_put_info.offset = off; + ring->rx_curr_put_info.offset = off; rxdp->Control_2 |= SET_RXD_MARKER; if (!(alloc_tab & ((1 << rxsync_frequency) - 1))) { @@ -2711,7 +2704,7 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no) } first_rxdp = rxdp; } - atomic_inc(&nic->rx_bufs_left[ring_no]); + ring->rx_bufs_left += 1; alloc_tab++; } @@ -2783,7 +2776,7 @@ static void free_rxd_blk(struct s2io_nic *sp, int ring_no, int blk) } sp->mac_control.stats_info->sw_stat.mem_freed += skb->truesize; dev_kfree_skb(skb); - atomic_dec(&sp->rx_bufs_left[ring_no]); + mac_control->rings[ring_no].rx_bufs_left -= 1; } } @@ -2814,7 +2807,7 @@ static void free_rx_buffers(struct s2io_nic *sp) mac_control->rings[i].rx_curr_get_info.block_index = 0; mac_control->rings[i].rx_curr_put_info.offset = 0; mac_control->rings[i].rx_curr_get_info.offset = 0; - atomic_set(&sp->rx_bufs_left[i], 0); + mac_control->rings[i].rx_bufs_left = 0; DBG_PRINT(INIT_DBG, "%s:Freed 0x%x Rx Buffers on ring%d\n", dev->name, buf_cnt, i); } @@ -2864,7 +2857,7 @@ static int s2io_poll(struct napi_struct *napi, int budget) netif_rx_complete(dev, napi); for (i = 0; i < config->rx_ring_num; i++) { - if (fill_rx_buffers(nic, i) == -ENOMEM) { + if (fill_rx_buffers(&mac_control->rings[i]) == -ENOMEM) { DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name); DBG_PRINT(INFO_DBG, " in Rx Poll!!\n"); break; @@ -2877,7 +2870,7 @@ static int s2io_poll(struct napi_struct *napi, int budget) no_rx: for (i = 0; i < config->rx_ring_num; i++) { - if (fill_rx_buffers(nic, i) == -ENOMEM) { + if (fill_rx_buffers(&mac_control->rings[i]) == -ENOMEM) { DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name); DBG_PRINT(INFO_DBG, " in Rx Poll!!\n"); break; @@ -2928,7 +2921,7 @@ static void s2io_netpoll(struct net_device *dev) rx_intr_handler(&mac_control->rings[i]); for (i = 0; i < config->rx_ring_num; i++) { - if (fill_rx_buffers(nic, i) == -ENOMEM) { + if (fill_rx_buffers(&mac_control->rings[i]) == -ENOMEM) { DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name); DBG_PRINT(INFO_DBG, " in Rx Netpoll!!\n"); break; @@ -2953,8 +2946,6 @@ static void s2io_netpoll(struct net_device *dev) */ static void rx_intr_handler(struct ring_info *ring_data) { - struct s2io_nic *nic = ring_data->nic; - struct net_device *dev = (struct net_device *) nic->dev; int get_block, put_block; struct rx_curr_get_info get_info, put_info; struct RxD_t *rxdp; @@ -2977,33 +2968,34 @@ static void rx_intr_handler(struct ring_info *ring_data) */ if ((get_block == put_block) && (get_info.offset + 1) == put_info.offset) { - DBG_PRINT(INTR_DBG, "%s: Ring Full\n",dev->name); + DBG_PRINT(INTR_DBG, "%s: Ring Full\n", + ring_data->dev->name); break; } skb = (struct sk_buff *) ((unsigned long)rxdp->Host_Control); if (skb == NULL) { DBG_PRINT(ERR_DBG, "%s: The skb is ", - dev->name); + ring_data->dev->name); DBG_PRINT(ERR_DBG, "Null in Rx Intr\n"); return; } - if (nic->rxd_mode == RXD_MODE_1) { + if (ring_data->rxd_mode == RXD_MODE_1) { rxdp1 = (struct RxD1*)rxdp; - pci_unmap_single(nic->pdev, (dma_addr_t) + pci_unmap_single(ring_data->pdev, (dma_addr_t) rxdp1->Buffer0_ptr, - dev->mtu + + ring_data->mtu + HEADER_ETHERNET_II_802_3_SIZE + HEADER_802_2_SIZE + HEADER_SNAP_SIZE, PCI_DMA_FROMDEVICE); - } else if (nic->rxd_mode == RXD_MODE_3B) { + } else if (ring_data->rxd_mode == RXD_MODE_3B) { rxdp3 = (struct RxD3*)rxdp; - pci_dma_sync_single_for_cpu(nic->pdev, (dma_addr_t) + pci_dma_sync_single_for_cpu(ring_data->pdev, (dma_addr_t) rxdp3->Buffer0_ptr, BUF0_LEN, PCI_DMA_FROMDEVICE); - pci_unmap_single(nic->pdev, (dma_addr_t) + pci_unmap_single(ring_data->pdev, (dma_addr_t) rxdp3->Buffer2_ptr, - dev->mtu + 4, + ring_data->mtu + 4, PCI_DMA_FROMDEVICE); } prefetch(skb->data); @@ -3012,7 +3004,7 @@ static void rx_intr_handler(struct ring_info *ring_data) ring_data->rx_curr_get_info.offset = get_info.offset; rxdp = ring_data->rx_blocks[get_block]. rxds[get_info.offset].virt_addr; - if (get_info.offset == rxd_count[nic->rxd_mode]) { + if (get_info.offset == rxd_count[ring_data->rxd_mode]) { get_info.offset = 0; ring_data->rx_curr_get_info.offset = get_info.offset; get_block++; @@ -3022,19 +3014,21 @@ static void rx_intr_handler(struct ring_info *ring_data) rxdp = ring_data->rx_blocks[get_block].block_virt_addr; } - nic->pkts_to_process -= 1; - if ((napi) && (!nic->pkts_to_process)) - break; + if(ring_data->nic->config.napi){ + ring_data->nic->pkts_to_process -= 1; + if (!ring_data->nic->pkts_to_process) + break; + } pkt_cnt++; if ((indicate_max_pkts) && (pkt_cnt > indicate_max_pkts)) break; } - if (nic->lro) { + if (ring_data->lro) { /* Clear all LRO sessions before exiting */ for (i=0; i<MAX_LRO_SESSIONS; i++) { - struct lro *lro = &nic->lro0_n[i]; + struct lro *lro = &ring_data->lro0_n[i]; if (lro->in_use) { - update_L3L4_header(nic, lro); + update_L3L4_header(ring_data->nic, lro); queue_rx_frame(lro->parent, lro->vlan_tag); clear_lro_session(lro); } @@ -4333,10 +4327,10 @@ s2io_alarm_handle(unsigned long data) mod_timer(&sp->alarm_timer, jiffies + HZ / 2); } -static int s2io_chk_rx_buffers(struct s2io_nic *sp, int rng_n) +static int s2io_chk_rx_buffers(struct ring_info *ring) { - if (fill_rx_buffers(sp, rng_n) == -ENOMEM) { - DBG_PRINT(INFO_DBG, "%s:Out of memory", sp->dev->name); + if (fill_rx_buffers(ring) == -ENOMEM) { + DBG_PRINT(INFO_DBG, "%s:Out of memory", ring->dev->name); DBG_PRINT(INFO_DBG, " in Rx Intr!!\n"); } return 0; @@ -4351,7 +4345,7 @@ static irqreturn_t s2io_msix_ring_handle(int irq, void *dev_id) return IRQ_HANDLED; rx_intr_handler(ring); - s2io_chk_rx_buffers(sp, ring->ring_no); + s2io_chk_rx_buffers(ring); return IRQ_HANDLED; } @@ -4809,7 +4803,7 @@ static irqreturn_t s2io_isr(int irq, void *dev_id) */ if (!config->napi) { for (i = 0; i < config->rx_ring_num; i++) - s2io_chk_rx_buffers(sp, i); + s2io_chk_rx_buffers(&mac_control->rings[i]); } writeq(sp->general_int_mask, &bar0->general_int_mask); readl(&bar0->general_int_status); @@ -4866,6 +4860,7 @@ static struct net_device_stats *s2io_get_stats(struct net_device *dev) struct s2io_nic *sp = dev->priv; struct mac_info *mac_control; struct config_param *config; + int i; mac_control = &sp->mac_control; @@ -4885,6 +4880,13 @@ static struct net_device_stats *s2io_get_stats(struct net_device *dev) sp->stats.rx_length_errors = le64_to_cpu(mac_control->stats_info->rmac_long_frms); + /* collect per-ring rx_packets and rx_bytes */ + sp->stats.rx_packets = sp->stats.rx_bytes = 0; + for (i = 0; i < config->rx_ring_num; i++) { + sp->stats.rx_packets += mac_control->rings[i].rx_packets; + sp->stats.rx_bytes += mac_control->rings[i].rx_bytes; + } + return (&sp->stats); } @@ -7157,7 +7159,9 @@ static int s2io_card_up(struct s2io_nic * sp) config = &sp->config; for (i = 0; i < config->rx_ring_num; i++) { - if ((ret = fill_rx_buffers(sp, i))) { + mac_control->rings[i].mtu = dev->mtu; + ret = fill_rx_buffers(&mac_control->rings[i]); + if (ret) { DBG_PRINT(ERR_DBG, "%s: Out of memory in Open\n", dev->name); s2io_reset(sp); @@ -7165,7 +7169,7 @@ static int s2io_card_up(struct s2io_nic * sp) return -ENOMEM; } DBG_PRINT(INFO_DBG, "Buf in ring:%d is %d:\n", i, - atomic_read(&sp->rx_bufs_left[i])); + mac_control->rings[i].rx_bufs_left); } /* Initialise napi */ @@ -7300,7 +7304,7 @@ static void s2io_tx_watchdog(struct net_device *dev) static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp) { struct s2io_nic *sp = ring_data->nic; - struct net_device *dev = (struct net_device *) sp->dev; + struct net_device *dev = (struct net_device *) ring_data->dev; struct sk_buff *skb = (struct sk_buff *) ((unsigned long) rxdp->Host_Control); int ring_no = ring_data->ring_no; @@ -7377,19 +7381,19 @@ static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp) sp->mac_control.stats_info->sw_stat.mem_freed += skb->truesize; dev_kfree_skb(skb); - atomic_dec(&sp->rx_bufs_left[ring_no]); + ring_data->rx_bufs_left -= 1; rxdp->Host_Control = 0; return 0; } } /* Updating statistics */ - sp->stats.rx_packets++; + ring_data->rx_packets++; rxdp->Host_Control = 0; if (sp->rxd_mode == RXD_MODE_1) { int len = RXD_GET_BUFFER0_SIZE_1(rxdp->Control_2); - sp->stats.rx_bytes += len; + ring_data->rx_bytes += len; skb_put(skb, len); } else if (sp->rxd_mode == RXD_MODE_3B) { @@ -7400,13 +7404,13 @@ static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp) unsigned char *buff = skb_push(skb, buf0_len); struct buffAdd *ba = &ring_data->ba[get_block][get_off]; - sp->stats.rx_bytes += buf0_len + buf2_len; + ring_data->rx_bytes += buf0_len + buf2_len; memcpy(buff, ba->ba_0, buf0_len); skb_put(skb, buf2_len); } - if ((rxdp->Control_1 & TCP_OR_UDP_FRAME) && ((!sp->lro) || - (sp->lro && (!(rxdp->Control_1 & RXD_FRAME_IP_FRAG)))) && + if ((rxdp->Control_1 & TCP_OR_UDP_FRAME) && ((!ring_data->lro) || + (ring_data->lro && (!(rxdp->Control_1 & RXD_FRAME_IP_FRAG)))) && (sp->rx_csum)) { l3_csum = RXD_GET_L3_CKSUM(rxdp->Control_1); l4_csum = RXD_GET_L4_CKSUM(rxdp->Control_1); @@ -7417,14 +7421,14 @@ static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp) * a flag in the RxD. */ skb->ip_summed = CHECKSUM_UNNECESSARY; - if (sp->lro) { + if (ring_data->lro) { u32 tcp_len; u8 *tcp; int ret = 0; - ret = s2io_club_tcp_session(skb->data, &tcp, - &tcp_len, &lro, - rxdp, sp); + ret = s2io_club_tcp_session(ring_data, + skb->data, &tcp, &tcp_len, &lro, + rxdp, sp); switch (ret) { case 3: /* Begin anew */ lro->parent = skb; @@ -7486,7 +7490,7 @@ send_up: queue_rx_frame(skb, RXD_GET_VLAN_TAG(rxdp->Control_2)); dev->last_rx = jiffies; aggregate: - atomic_dec(&sp->rx_bufs_left[ring_no]); + sp->mac_control.rings[ring_no].rx_bufs_left -= 1; return SUCCESS; } @@ -7603,12 +7607,14 @@ static int s2io_verify_parm(struct pci_dev *pdev, u8 *dev_intr_type, tx_steering_type = NO_STEERING; } - if ( rx_ring_num > 8) { - DBG_PRINT(ERR_DBG, "s2io: Requested number of Rx rings not " + if (rx_ring_num > MAX_RX_RINGS) { + DBG_PRINT(ERR_DBG, "s2io: Requested number of rx rings not " "supported\n"); - DBG_PRINT(ERR_DBG, "s2io: Default to 8 Rx rings\n"); - rx_ring_num = 8; + DBG_PRINT(ERR_DBG, "s2io: Default to %d rx rings\n", + MAX_RX_RINGS); + rx_ring_num = MAX_RX_RINGS; } + if (*dev_intr_type != INTA) napi = 0; @@ -7836,10 +7842,15 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) /* Rx side parameters. */ config->rx_ring_num = rx_ring_num; - for (i = 0; i < MAX_RX_RINGS; i++) { + for (i = 0; i < config->rx_ring_num; i++) { config->rx_cfg[i].num_rxd = rx_ring_sz[i] * (rxd_count[sp->rxd_mode] + 1); config->rx_cfg[i].ring_priority = i; + mac_control->rings[i].rx_bufs_left = 0; + mac_control->rings[i].rxd_mode = sp->rxd_mode; + mac_control->rings[i].rxd_count = rxd_count[sp->rxd_mode]; + mac_control->rings[i].pdev = sp->pdev; + mac_control->rings[i].dev = sp->dev; } for (i = 0; i < rx_ring_num; i++) { @@ -7854,10 +7865,6 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) mac_control->mc_pause_threshold_q4q7 = mc_pause_threshold_q4q7; - /* Initialize Ring buffer parameters. */ - for (i = 0; i < config->rx_ring_num; i++) - atomic_set(&sp->rx_bufs_left[i], 0); - /* initialize the shared memory used by the NIC and the host */ if (init_shared_mem(sp)) { DBG_PRINT(ERR_DBG, "%s: Memory allocation failed\n", @@ -8077,6 +8084,9 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) DBG_PRINT(ERR_DBG, "%s: Using %d Tx fifo(s)\n", dev->name, sp->config.tx_fifo_num); + DBG_PRINT(ERR_DBG, "%s: Using %d Rx ring(s)\n", dev->name, + sp->config.rx_ring_num); + switch(sp->config.intr_type) { case INTA: DBG_PRINT(ERR_DBG, "%s: Interrupt type INTA\n", dev->name); @@ -8391,8 +8401,9 @@ static int verify_l3_l4_lro_capable(struct lro *l_lro, struct iphdr *ip, } static int -s2io_club_tcp_session(u8 *buffer, u8 **tcp, u32 *tcp_len, struct lro **lro, - struct RxD_t *rxdp, struct s2io_nic *sp) +s2io_club_tcp_session(struct ring_info *ring_data, u8 *buffer, u8 **tcp, + u32 *tcp_len, struct lro **lro, struct RxD_t *rxdp, + struct s2io_nic *sp) { struct iphdr *ip; struct tcphdr *tcph; @@ -8410,7 +8421,7 @@ s2io_club_tcp_session(u8 *buffer, u8 **tcp, u32 *tcp_len, struct lro **lro, tcph = (struct tcphdr *)*tcp; *tcp_len = get_l4_pyld_length(ip, tcph); for (i=0; i<MAX_LRO_SESSIONS; i++) { - struct lro *l_lro = &sp->lro0_n[i]; + struct lro *l_lro = &ring_data->lro0_n[i]; if (l_lro->in_use) { if (check_for_socket_match(l_lro, ip, tcph)) continue; @@ -8448,7 +8459,7 @@ s2io_club_tcp_session(u8 *buffer, u8 **tcp, u32 *tcp_len, struct lro **lro, } for (i=0; i<MAX_LRO_SESSIONS; i++) { - struct lro *l_lro = &sp->lro0_n[i]; + struct lro *l_lro = &ring_data->lro0_n[i]; if (!(l_lro->in_use)) { *lro = l_lro; ret = 3; /* Begin anew */ diff --git a/drivers/net/s2io.h b/drivers/net/s2io.h index ce53a02105f..0709ebae913 100644 --- a/drivers/net/s2io.h +++ b/drivers/net/s2io.h @@ -678,11 +678,53 @@ struct rx_block_info { struct rxd_info *rxds; }; +/* Data structure to represent a LRO session */ +struct lro { + struct sk_buff *parent; + struct sk_buff *last_frag; + u8 *l2h; + struct iphdr *iph; + struct tcphdr *tcph; + u32 tcp_next_seq; + __be32 tcp_ack; + int total_len; + int frags_len; + int sg_num; + int in_use; + __be16 window; + u16 vlan_tag; + u32 cur_tsval; + __be32 cur_tsecr; + u8 saw_ts; +} ____cacheline_aligned; + /* Ring specific structure */ struct ring_info { /* The ring number */ int ring_no; + /* per-ring buffer counter */ + u32 rx_bufs_left; + + #define MAX_LRO_SESSIONS 32 + struct lro lro0_n[MAX_LRO_SESSIONS]; + u8 lro; + + /* copy of sp->rxd_mode flag */ + int rxd_mode; + + /* Number of rxds per block for the rxd_mode */ + int rxd_count; + + /* copy of sp pointer */ + struct s2io_nic *nic; + + /* copy of sp->dev pointer */ + struct net_device *dev; + + /* copy of sp->pdev pointer */ + struct pci_dev *pdev; + /* * Place holders for the virtual and physical addresses of * all the Rx Blocks @@ -703,10 +745,16 @@ struct ring_info { */ struct rx_curr_get_info rx_curr_get_info; + /* interface MTU value */ + unsigned mtu; + /* Buffer Address store. */ struct buffAdd **ba; - struct s2io_nic *nic; -}; + + /* per-Ring statistics */ + unsigned long rx_packets; + unsigned long rx_bytes; +} ____cacheline_aligned; /* Fifo specific structure */ struct fifo_info { @@ -813,26 +861,6 @@ struct msix_info_st { u64 data; }; -/* Data structure to represent a LRO session */ -struct lro { - struct sk_buff *parent; - struct sk_buff *last_frag; - u8 *l2h; - struct iphdr *iph; - struct tcphdr *tcph; - u32 tcp_next_seq; - __be32 tcp_ack; - int total_len; - int frags_len; - int sg_num; - int in_use; - __be16 window; - u16 vlan_tag; - u32 cur_tsval; - __be32 cur_tsecr; - u8 saw_ts; -} ____cacheline_aligned; - /* These flags represent the devices temporary state */ enum s2io_device_state_t { @@ -872,8 +900,6 @@ struct s2io_nic { /* Space to back up the PCI config space */ u32 config_space[256 / sizeof(u32)]; - atomic_t rx_bufs_left[MAX_RX_RINGS]; - #define PROMISC 1 #define ALL_MULTI 2 @@ -950,8 +976,6 @@ struct s2io_nic { #define XFRAME_II_DEVICE 2 u8 device_type; -#define MAX_LRO_SESSIONS 32 - struct lro lro0_n[MAX_LRO_SESSIONS]; unsigned long clubbed_frms_cnt; unsigned long sending_both; u8 lro; @@ -1118,9 +1142,9 @@ static int do_s2io_add_mc(struct s2io_nic *sp, u8 *addr); static int do_s2io_add_mac(struct s2io_nic *sp, u64 addr, int offset); static int do_s2io_delete_unicast_mc(struct s2io_nic *sp, u64 addr); -static int -s2io_club_tcp_session(u8 *buffer, u8 **tcp, u32 *tcp_len, struct lro **lro, - struct RxD_t *rxdp, struct s2io_nic *sp); +static int s2io_club_tcp_session(struct ring_info *ring_data, u8 *buffer, + u8 **tcp, u32 *tcp_len, struct lro **lro, struct RxD_t *rxdp, + struct s2io_nic *sp); static void clear_lro_session(struct lro *lro); static void queue_rx_frame(struct sk_buff *skb, u16 vlan_tag); static void update_L3L4_header(struct s2io_nic *sp, struct lro *lro); diff --git a/drivers/net/sfc/Kconfig b/drivers/net/sfc/Kconfig new file mode 100644 index 00000000000..dbad95c295b --- /dev/null +++ b/drivers/net/sfc/Kconfig @@ -0,0 +1,12 @@ +config SFC + tristate "Solarflare Solarstorm SFC4000 support" + depends on PCI && INET + select MII + select INET_LRO + select CRC32 + help + This driver supports 10-gigabit Ethernet cards based on + the Solarflare Communications Solarstorm SFC4000 controller. + + To compile this driver as a module, choose M here. The module + will be called sfc. diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile new file mode 100644 index 00000000000..0f023447eaf --- /dev/null +++ b/drivers/net/sfc/Makefile @@ -0,0 +1,5 @@ +sfc-y += efx.o falcon.o tx.o rx.o falcon_xmac.o \ + i2c-direct.o ethtool.o xfp_phy.o mdio_10g.o \ + tenxpress.o boards.o sfe4001.o + +obj-$(CONFIG_SFC) += sfc.o diff --git a/drivers/net/sfc/bitfield.h b/drivers/net/sfc/bitfield.h new file mode 100644 index 00000000000..2806201644c --- /dev/null +++ b/drivers/net/sfc/bitfield.h @@ -0,0 +1,508 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_BITFIELD_H +#define EFX_BITFIELD_H + +/* + * Efx bitfield access + * + * Efx NICs make extensive use of bitfields up to 128 bits + * wide. Since there is no native 128-bit datatype on most systems, + * and since 64-bit datatypes are inefficient on 32-bit systems and + * vice versa, we wrap accesses in a way that uses the most efficient + * datatype. + * + * The NICs are PCI devices and therefore little-endian. Since most + * of the quantities that we deal with are DMAed to/from host memory, + * we define our datatypes (efx_oword_t, efx_qword_t and + * efx_dword_t) to be little-endian. + */ + +/* Lowest bit numbers and widths */ +#define EFX_DUMMY_FIELD_LBN 0 +#define EFX_DUMMY_FIELD_WIDTH 0 +#define EFX_DWORD_0_LBN 0 +#define EFX_DWORD_0_WIDTH 32 +#define EFX_DWORD_1_LBN 32 +#define EFX_DWORD_1_WIDTH 32 +#define EFX_DWORD_2_LBN 64 +#define EFX_DWORD_2_WIDTH 32 +#define EFX_DWORD_3_LBN 96 +#define EFX_DWORD_3_WIDTH 32 + +/* Specified attribute (e.g. LBN) of the specified field */ +#define EFX_VAL(field, attribute) field ## _ ## attribute +/* Low bit number of the specified field */ +#define EFX_LOW_BIT(field) EFX_VAL(field, LBN) +/* Bit width of the specified field */ +#define EFX_WIDTH(field) EFX_VAL(field, WIDTH) +/* High bit number of the specified field */ +#define EFX_HIGH_BIT(field) (EFX_LOW_BIT(field) + EFX_WIDTH(field) - 1) +/* Mask equal in width to the specified field. + * + * For example, a field with width 5 would have a mask of 0x1f. + * + * The maximum width mask that can be generated is 64 bits. + */ +#define EFX_MASK64(field) \ + (EFX_WIDTH(field) == 64 ? ~((u64) 0) : \ + (((((u64) 1) << EFX_WIDTH(field))) - 1)) + +/* Mask equal in width to the specified field. + * + * For example, a field with width 5 would have a mask of 0x1f. + * + * The maximum width mask that can be generated is 32 bits. Use + * EFX_MASK64 for higher width fields. + */ +#define EFX_MASK32(field) \ + (EFX_WIDTH(field) == 32 ? ~((u32) 0) : \ + (((((u32) 1) << EFX_WIDTH(field))) - 1)) + +/* A doubleword (i.e. 4 byte) datatype - little-endian in HW */ +typedef union efx_dword { + __le32 u32[1]; +} efx_dword_t; + +/* A quadword (i.e. 8 byte) datatype - little-endian in HW */ +typedef union efx_qword { + __le64 u64[1]; + __le32 u32[2]; + efx_dword_t dword[2]; +} efx_qword_t; + +/* An octword (eight-word, i.e. 16 byte) datatype - little-endian in HW */ +typedef union efx_oword { + __le64 u64[2]; + efx_qword_t qword[2]; + __le32 u32[4]; + efx_dword_t dword[4]; +} efx_oword_t; + +/* Format string and value expanders for printk */ +#define EFX_DWORD_FMT "%08x" +#define EFX_QWORD_FMT "%08x:%08x" +#define EFX_OWORD_FMT "%08x:%08x:%08x:%08x" +#define EFX_DWORD_VAL(dword) \ + ((unsigned int) le32_to_cpu((dword).u32[0])) +#define EFX_QWORD_VAL(qword) \ + ((unsigned int) le32_to_cpu((qword).u32[1])), \ + ((unsigned int) le32_to_cpu((qword).u32[0])) +#define EFX_OWORD_VAL(oword) \ + ((unsigned int) le32_to_cpu((oword).u32[3])), \ + ((unsigned int) le32_to_cpu((oword).u32[2])), \ + ((unsigned int) le32_to_cpu((oword).u32[1])), \ + ((unsigned int) le32_to_cpu((oword).u32[0])) + +/* + * Extract bit field portion [low,high) from the native-endian element + * which contains bits [min,max). + * + * For example, suppose "element" represents the high 32 bits of a + * 64-bit value, and we wish to extract the bits belonging to the bit + * field occupying bits 28-45 of this 64-bit value. + * + * Then EFX_EXTRACT ( element, 32, 63, 28, 45 ) would give + * + * ( element ) << 4 + * + * The result will contain the relevant bits filled in in the range + * [0,high-low), with garbage in bits [high-low+1,...). + */ +#define EFX_EXTRACT_NATIVE(native_element, min, max, low, high) \ + (((low > max) || (high < min)) ? 0 : \ + ((low > min) ? \ + ((native_element) >> (low - min)) : \ + ((native_element) << (min - low)))) + +/* + * Extract bit field portion [low,high) from the 64-bit little-endian + * element which contains bits [min,max) + */ +#define EFX_EXTRACT64(element, min, max, low, high) \ + EFX_EXTRACT_NATIVE(le64_to_cpu(element), min, max, low, high) + +/* + * Extract bit field portion [low,high) from the 32-bit little-endian + * element which contains bits [min,max) + */ +#define EFX_EXTRACT32(element, min, max, low, high) \ + EFX_EXTRACT_NATIVE(le32_to_cpu(element), min, max, low, high) + +#define EFX_EXTRACT_OWORD64(oword, low, high) \ + (EFX_EXTRACT64((oword).u64[0], 0, 63, low, high) | \ + EFX_EXTRACT64((oword).u64[1], 64, 127, low, high)) + +#define EFX_EXTRACT_QWORD64(qword, low, high) \ + EFX_EXTRACT64((qword).u64[0], 0, 63, low, high) + +#define EFX_EXTRACT_OWORD32(oword, low, high) \ + (EFX_EXTRACT32((oword).u32[0], 0, 31, low, high) | \ + EFX_EXTRACT32((oword).u32[1], 32, 63, low, high) | \ + EFX_EXTRACT32((oword).u32[2], 64, 95, low, high) | \ + EFX_EXTRACT32((oword).u32[3], 96, 127, low, high)) + +#define EFX_EXTRACT_QWORD32(qword, low, high) \ + (EFX_EXTRACT32((qword).u32[0], 0, 31, low, high) | \ + EFX_EXTRACT32((qword).u32[1], 32, 63, low, high)) + +#define EFX_EXTRACT_DWORD(dword, low, high) \ + EFX_EXTRACT32((dword).u32[0], 0, 31, low, high) + +#define EFX_OWORD_FIELD64(oword, field) \ + (EFX_EXTRACT_OWORD64(oword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ + & EFX_MASK64(field)) + +#define EFX_QWORD_FIELD64(qword, field) \ + (EFX_EXTRACT_QWORD64(qword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ + & EFX_MASK64(field)) + +#define EFX_OWORD_FIELD32(oword, field) \ + (EFX_EXTRACT_OWORD32(oword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ + & EFX_MASK32(field)) + +#define EFX_QWORD_FIELD32(qword, field) \ + (EFX_EXTRACT_QWORD32(qword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ + & EFX_MASK32(field)) + +#define EFX_DWORD_FIELD(dword, field) \ + (EFX_EXTRACT_DWORD(dword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ + & EFX_MASK32(field)) + +#define EFX_OWORD_IS_ZERO64(oword) \ + (((oword).u64[0] | (oword).u64[1]) == (__force __le64) 0) + +#define EFX_QWORD_IS_ZERO64(qword) \ + (((qword).u64[0]) == (__force __le64) 0) + +#define EFX_OWORD_IS_ZERO32(oword) \ + (((oword).u32[0] | (oword).u32[1] | (oword).u32[2] | (oword).u32[3]) \ + == (__force __le32) 0) + +#define EFX_QWORD_IS_ZERO32(qword) \ + (((qword).u32[0] | (qword).u32[1]) == (__force __le32) 0) + +#define EFX_DWORD_IS_ZERO(dword) \ + (((dword).u32[0]) == (__force __le32) 0) + +#define EFX_OWORD_IS_ALL_ONES64(oword) \ + (((oword).u64[0] & (oword).u64[1]) == ~((__force __le64) 0)) + +#define EFX_QWORD_IS_ALL_ONES64(qword) \ + ((qword).u64[0] == ~((__force __le64) 0)) + +#define EFX_OWORD_IS_ALL_ONES32(oword) \ + (((oword).u32[0] & (oword).u32[1] & (oword).u32[2] & (oword).u32[3]) \ + == ~((__force __le32) 0)) + +#define EFX_QWORD_IS_ALL_ONES32(qword) \ + (((qword).u32[0] & (qword).u32[1]) == ~((__force __le32) 0)) + +#define EFX_DWORD_IS_ALL_ONES(dword) \ + ((dword).u32[0] == ~((__force __le32) 0)) + +#if BITS_PER_LONG == 64 +#define EFX_OWORD_FIELD EFX_OWORD_FIELD64 +#define EFX_QWORD_FIELD EFX_QWORD_FIELD64 +#define EFX_OWORD_IS_ZERO EFX_OWORD_IS_ZERO64 +#define EFX_QWORD_IS_ZERO EFX_QWORD_IS_ZERO64 +#define EFX_OWORD_IS_ALL_ONES EFX_OWORD_IS_ALL_ONES64 +#define EFX_QWORD_IS_ALL_ONES EFX_QWORD_IS_ALL_ONES64 +#else +#define EFX_OWORD_FIELD EFX_OWORD_FIELD32 +#define EFX_QWORD_FIELD EFX_QWORD_FIELD32 +#define EFX_OWORD_IS_ZERO EFX_OWORD_IS_ZERO32 +#define EFX_QWORD_IS_ZERO EFX_QWORD_IS_ZERO32 +#define EFX_OWORD_IS_ALL_ONES EFX_OWORD_IS_ALL_ONES32 +#define EFX_QWORD_IS_ALL_ONES EFX_QWORD_IS_ALL_ONES32 +#endif + +/* + * Construct bit field portion + * + * Creates the portion of the bit field [low,high) that lies within + * the range [min,max). + */ +#define EFX_INSERT_NATIVE64(min, max, low, high, value) \ + (((low > max) || (high < min)) ? 0 : \ + ((low > min) ? \ + (((u64) (value)) << (low - min)) : \ + (((u64) (value)) >> (min - low)))) + +#define EFX_INSERT_NATIVE32(min, max, low, high, value) \ + (((low > max) || (high < min)) ? 0 : \ + ((low > min) ? \ + (((u32) (value)) << (low - min)) : \ + (((u32) (value)) >> (min - low)))) + +#define EFX_INSERT_NATIVE(min, max, low, high, value) \ + ((((max - min) >= 32) || ((high - low) >= 32)) ? \ + EFX_INSERT_NATIVE64(min, max, low, high, value) : \ + EFX_INSERT_NATIVE32(min, max, low, high, value)) + +/* + * Construct bit field portion + * + * Creates the portion of the named bit field that lies within the + * range [min,max). + */ +#define EFX_INSERT_FIELD_NATIVE(min, max, field, value) \ + EFX_INSERT_NATIVE(min, max, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +/* + * Construct bit field + * + * Creates the portion of the named bit fields that lie within the + * range [min,max). + */ +#define EFX_INSERT_FIELDS_NATIVE(min, max, \ + field1, value1, \ + field2, value2, \ + field3, value3, \ + field4, value4, \ + field5, value5, \ + field6, value6, \ + field7, value7, \ + field8, value8, \ + field9, value9, \ + field10, value10) \ + (EFX_INSERT_FIELD_NATIVE((min), (max), field1, (value1)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field2, (value2)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field3, (value3)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field4, (value4)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field5, (value5)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field6, (value6)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field7, (value7)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field8, (value8)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field9, (value9)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field10, (value10))) + +#define EFX_INSERT_FIELDS64(...) \ + cpu_to_le64(EFX_INSERT_FIELDS_NATIVE(__VA_ARGS__)) + +#define EFX_INSERT_FIELDS32(...) \ + cpu_to_le32(EFX_INSERT_FIELDS_NATIVE(__VA_ARGS__)) + +#define EFX_POPULATE_OWORD64(oword, ...) do { \ + (oword).u64[0] = EFX_INSERT_FIELDS64(0, 63, __VA_ARGS__); \ + (oword).u64[1] = EFX_INSERT_FIELDS64(64, 127, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_QWORD64(qword, ...) do { \ + (qword).u64[0] = EFX_INSERT_FIELDS64(0, 63, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_OWORD32(oword, ...) do { \ + (oword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + (oword).u32[1] = EFX_INSERT_FIELDS32(32, 63, __VA_ARGS__); \ + (oword).u32[2] = EFX_INSERT_FIELDS32(64, 95, __VA_ARGS__); \ + (oword).u32[3] = EFX_INSERT_FIELDS32(96, 127, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_QWORD32(qword, ...) do { \ + (qword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + (qword).u32[1] = EFX_INSERT_FIELDS32(32, 63, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_DWORD(dword, ...) do { \ + (dword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + } while (0) + +#if BITS_PER_LONG == 64 +#define EFX_POPULATE_OWORD EFX_POPULATE_OWORD64 +#define EFX_POPULATE_QWORD EFX_POPULATE_QWORD64 +#else +#define EFX_POPULATE_OWORD EFX_POPULATE_OWORD32 +#define EFX_POPULATE_QWORD EFX_POPULATE_QWORD32 +#endif + +/* Populate an octword field with various numbers of arguments */ +#define EFX_POPULATE_OWORD_10 EFX_POPULATE_OWORD +#define EFX_POPULATE_OWORD_9(oword, ...) \ + EFX_POPULATE_OWORD_10(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_8(oword, ...) \ + EFX_POPULATE_OWORD_9(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_7(oword, ...) \ + EFX_POPULATE_OWORD_8(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_6(oword, ...) \ + EFX_POPULATE_OWORD_7(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_5(oword, ...) \ + EFX_POPULATE_OWORD_6(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_4(oword, ...) \ + EFX_POPULATE_OWORD_5(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_3(oword, ...) \ + EFX_POPULATE_OWORD_4(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_2(oword, ...) \ + EFX_POPULATE_OWORD_3(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_1(oword, ...) \ + EFX_POPULATE_OWORD_2(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_ZERO_OWORD(oword) \ + EFX_POPULATE_OWORD_1(oword, EFX_DUMMY_FIELD, 0) +#define EFX_SET_OWORD(oword) \ + EFX_POPULATE_OWORD_4(oword, \ + EFX_DWORD_0, 0xffffffff, \ + EFX_DWORD_1, 0xffffffff, \ + EFX_DWORD_2, 0xffffffff, \ + EFX_DWORD_3, 0xffffffff) + +/* Populate a quadword field with various numbers of arguments */ +#define EFX_POPULATE_QWORD_10 EFX_POPULATE_QWORD +#define EFX_POPULATE_QWORD_9(qword, ...) \ + EFX_POPULATE_QWORD_10(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_8(qword, ...) \ + EFX_POPULATE_QWORD_9(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_7(qword, ...) \ + EFX_POPULATE_QWORD_8(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_6(qword, ...) \ + EFX_POPULATE_QWORD_7(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_5(qword, ...) \ + EFX_POPULATE_QWORD_6(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_4(qword, ...) \ + EFX_POPULATE_QWORD_5(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_3(qword, ...) \ + EFX_POPULATE_QWORD_4(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_2(qword, ...) \ + EFX_POPULATE_QWORD_3(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_1(qword, ...) \ + EFX_POPULATE_QWORD_2(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_ZERO_QWORD(qword) \ + EFX_POPULATE_QWORD_1(qword, EFX_DUMMY_FIELD, 0) +#define EFX_SET_QWORD(qword) \ + EFX_POPULATE_QWORD_2(qword, \ + EFX_DWORD_0, 0xffffffff, \ + EFX_DWORD_1, 0xffffffff) + +/* Populate a dword field with various numbers of arguments */ +#define EFX_POPULATE_DWORD_10 EFX_POPULATE_DWORD +#define EFX_POPULATE_DWORD_9(dword, ...) \ + EFX_POPULATE_DWORD_10(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_8(dword, ...) \ + EFX_POPULATE_DWORD_9(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_7(dword, ...) \ + EFX_POPULATE_DWORD_8(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_6(dword, ...) \ + EFX_POPULATE_DWORD_7(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_5(dword, ...) \ + EFX_POPULATE_DWORD_6(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_4(dword, ...) \ + EFX_POPULATE_DWORD_5(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_3(dword, ...) \ + EFX_POPULATE_DWORD_4(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_2(dword, ...) \ + EFX_POPULATE_DWORD_3(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_1(dword, ...) \ + EFX_POPULATE_DWORD_2(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_ZERO_DWORD(dword) \ + EFX_POPULATE_DWORD_1(dword, EFX_DUMMY_FIELD, 0) +#define EFX_SET_DWORD(dword) \ + EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xffffffff) + +/* + * Modify a named field within an already-populated structure. Used + * for read-modify-write operations. + * + */ + +#define EFX_INVERT_OWORD(oword) do { \ + (oword).u64[0] = ~((oword).u64[0]); \ + (oword).u64[1] = ~((oword).u64[1]); \ + } while (0) + +#define EFX_INSERT_FIELD64(...) \ + cpu_to_le64(EFX_INSERT_FIELD_NATIVE(__VA_ARGS__)) + +#define EFX_INSERT_FIELD32(...) \ + cpu_to_le32(EFX_INSERT_FIELD_NATIVE(__VA_ARGS__)) + +#define EFX_INPLACE_MASK64(min, max, field) \ + EFX_INSERT_FIELD64(min, max, field, EFX_MASK64(field)) + +#define EFX_INPLACE_MASK32(min, max, field) \ + EFX_INSERT_FIELD32(min, max, field, EFX_MASK32(field)) + +#define EFX_SET_OWORD_FIELD64(oword, field, value) do { \ + (oword).u64[0] = (((oword).u64[0] \ + & ~EFX_INPLACE_MASK64(0, 63, field)) \ + | EFX_INSERT_FIELD64(0, 63, field, value)); \ + (oword).u64[1] = (((oword).u64[1] \ + & ~EFX_INPLACE_MASK64(64, 127, field)) \ + | EFX_INSERT_FIELD64(64, 127, field, value)); \ + } while (0) + +#define EFX_SET_QWORD_FIELD64(qword, field, value) do { \ + (qword).u64[0] = (((qword).u64[0] \ + & ~EFX_INPLACE_MASK64(0, 63, field)) \ + | EFX_INSERT_FIELD64(0, 63, field, value)); \ + } while (0) + +#define EFX_SET_OWORD_FIELD32(oword, field, value) do { \ + (oword).u32[0] = (((oword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, field)) \ + | EFX_INSERT_FIELD32(0, 31, field, value)); \ + (oword).u32[1] = (((oword).u32[1] \ + & ~EFX_INPLACE_MASK32(32, 63, field)) \ + | EFX_INSERT_FIELD32(32, 63, field, value)); \ + (oword).u32[2] = (((oword).u32[2] \ + & ~EFX_INPLACE_MASK32(64, 95, field)) \ + | EFX_INSERT_FIELD32(64, 95, field, value)); \ + (oword).u32[3] = (((oword).u32[3] \ + & ~EFX_INPLACE_MASK32(96, 127, field)) \ + | EFX_INSERT_FIELD32(96, 127, field, value)); \ + } while (0) + +#define EFX_SET_QWORD_FIELD32(qword, field, value) do { \ + (qword).u32[0] = (((qword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, field)) \ + | EFX_INSERT_FIELD32(0, 31, field, value)); \ + (qword).u32[1] = (((qword).u32[1] \ + & ~EFX_INPLACE_MASK32(32, 63, field)) \ + | EFX_INSERT_FIELD32(32, 63, field, value)); \ + } while (0) + +#define EFX_SET_DWORD_FIELD(dword, field, value) do { \ + (dword).u32[0] = (((dword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, field)) \ + | EFX_INSERT_FIELD32(0, 31, field, value)); \ + } while (0) + +#if BITS_PER_LONG == 64 +#define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD64 +#define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD64 +#else +#define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD32 +#define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD32 +#endif + +#define EFX_SET_OWORD_FIELD_VER(efx, oword, field, value) do { \ + if (FALCON_REV(efx) >= FALCON_REV_B0) { \ + EFX_SET_OWORD_FIELD((oword), field##_B0, (value)); \ + } else { \ + EFX_SET_OWORD_FIELD((oword), field##_A1, (value)); \ + } \ +} while (0) + +#define EFX_QWORD_FIELD_VER(efx, qword, field) \ + (FALCON_REV(efx) >= FALCON_REV_B0 ? \ + EFX_QWORD_FIELD((qword), field##_B0) : \ + EFX_QWORD_FIELD((qword), field##_A1)) + +/* Used to avoid compiler warnings about shift range exceeding width + * of the data types when dma_addr_t is only 32 bits wide. + */ +#define DMA_ADDR_T_WIDTH (8 * sizeof(dma_addr_t)) +#define EFX_DMA_TYPE_WIDTH(width) \ + (((width) < DMA_ADDR_T_WIDTH) ? (width) : DMA_ADDR_T_WIDTH) +#define EFX_DMA_MAX_MASK ((DMA_ADDR_T_WIDTH == 64) ? \ + ~((u64) 0) : ~((u32) 0)) +#define EFX_DMA_MASK(mask) ((mask) & EFX_DMA_MAX_MASK) + +#endif /* EFX_BITFIELD_H */ diff --git a/drivers/net/sfc/boards.c b/drivers/net/sfc/boards.c new file mode 100644 index 00000000000..eecaa6d5858 --- /dev/null +++ b/drivers/net/sfc/boards.c @@ -0,0 +1,167 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2007 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "net_driver.h" +#include "phy.h" +#include "boards.h" +#include "efx.h" + +/* Macros for unpacking the board revision */ +/* The revision info is in host byte order. */ +#define BOARD_TYPE(_rev) (_rev >> 8) +#define BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf) +#define BOARD_MINOR(_rev) (_rev & 0xf) + +/* Blink support. If the PHY has no auto-blink mode so we hang it off a timer */ +#define BLINK_INTERVAL (HZ/2) + +static void blink_led_timer(unsigned long context) +{ + struct efx_nic *efx = (struct efx_nic *)context; + struct efx_blinker *bl = &efx->board_info.blinker; + efx->board_info.set_fault_led(efx, bl->state); + bl->state = !bl->state; + if (bl->resubmit) { + bl->timer.expires = jiffies + BLINK_INTERVAL; + add_timer(&bl->timer); + } +} + +static void board_blink(struct efx_nic *efx, int blink) +{ + struct efx_blinker *blinker = &efx->board_info.blinker; + + /* The rtnl mutex serialises all ethtool ioctls, so + * nothing special needs doing here. */ + if (blink) { + blinker->resubmit = 1; + blinker->state = 0; + setup_timer(&blinker->timer, blink_led_timer, + (unsigned long)efx); + blinker->timer.expires = jiffies + BLINK_INTERVAL; + add_timer(&blinker->timer); + } else { + blinker->resubmit = 0; + if (blinker->timer.function) + del_timer_sync(&blinker->timer); + efx->board_info.set_fault_led(efx, 0); + } +} + +/***************************************************************************** + * Support for the SFE4002 + * + */ +/****************************************************************************/ +/* LED allocations. Note that on rev A0 boards the schematic and the reality + * differ: red and green are swapped. Below is the fixed (A1) layout (there + * are only 3 A0 boards in existence, so no real reason to make this + * conditional). + */ +#define SFE4002_FAULT_LED (2) /* Red */ +#define SFE4002_RX_LED (0) /* Green */ +#define SFE4002_TX_LED (1) /* Amber */ + +static int sfe4002_init_leds(struct efx_nic *efx) +{ + /* Set the TX and RX LEDs to reflect status and activity, and the + * fault LED off */ + xfp_set_led(efx, SFE4002_TX_LED, + QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT); + xfp_set_led(efx, SFE4002_RX_LED, + QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT); + xfp_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF); + efx->board_info.blinker.led_num = SFE4002_FAULT_LED; + return 0; +} + +static void sfe4002_fault_led(struct efx_nic *efx, int state) +{ + xfp_set_led(efx, SFE4002_FAULT_LED, state ? QUAKE_LED_ON : + QUAKE_LED_OFF); +} + +static int sfe4002_init(struct efx_nic *efx) +{ + efx->board_info.init_leds = sfe4002_init_leds; + efx->board_info.set_fault_led = sfe4002_fault_led; + efx->board_info.blink = board_blink; + return 0; +} + +/* This will get expanded as board-specific details get moved out of the + * PHY drivers. */ +struct efx_board_data { + const char *ref_model; + const char *gen_type; + int (*init) (struct efx_nic *nic); +}; + +static int dummy_init(struct efx_nic *nic) +{ + return 0; +} + +static struct efx_board_data board_data[] = { + [EFX_BOARD_INVALID] = + {NULL, NULL, dummy_init}, + [EFX_BOARD_SFE4001] = + {"SFE4001", "10GBASE-T adapter", sfe4001_poweron}, + [EFX_BOARD_SFE4002] = + {"SFE4002", "XFP adapter", sfe4002_init}, +}; + +int efx_set_board_info(struct efx_nic *efx, u16 revision_info) +{ + int rc = 0; + struct efx_board_data *data; + + if (BOARD_TYPE(revision_info) >= EFX_BOARD_MAX) { + EFX_ERR(efx, "squashing unknown board type %d\n", + BOARD_TYPE(revision_info)); + revision_info = 0; + } + + if (BOARD_TYPE(revision_info) == 0) { + efx->board_info.major = 0; + efx->board_info.minor = 0; + /* For early boards that don't have revision info. there is + * only 1 board for each PHY type, so we can work it out, with + * the exception of the PHY-less boards. */ + switch (efx->phy_type) { + case PHY_TYPE_10XPRESS: + efx->board_info.type = EFX_BOARD_SFE4001; + break; + case PHY_TYPE_XFP: + efx->board_info.type = EFX_BOARD_SFE4002; + break; + default: + efx->board_info.type = 0; + break; + } + } else { + efx->board_info.type = BOARD_TYPE(revision_info); + efx->board_info.major = BOARD_MAJOR(revision_info); + efx->board_info.minor = BOARD_MINOR(revision_info); + } + + data = &board_data[efx->board_info.type]; + + /* Report the board model number or generic type for recognisable + * boards. */ + if (efx->board_info.type != 0) + EFX_INFO(efx, "board is %s rev %c%d\n", + (efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC) + ? data->ref_model : data->gen_type, + 'A' + efx->board_info.major, efx->board_info.minor); + + efx->board_info.init = data->init; + + return rc; +} diff --git a/drivers/net/sfc/boards.h b/drivers/net/sfc/boards.h new file mode 100644 index 00000000000..f56341d428e --- /dev/null +++ b/drivers/net/sfc/boards.h @@ -0,0 +1,26 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2007 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_BOARDS_H +#define EFX_BOARDS_H + +/* Board IDs (must fit in 8 bits) */ +enum efx_board_type { + EFX_BOARD_INVALID = 0, + EFX_BOARD_SFE4001 = 1, /* SFE4001 (10GBASE-T) */ + EFX_BOARD_SFE4002 = 2, + /* Insert new types before here */ + EFX_BOARD_MAX +}; + +extern int efx_set_board_info(struct efx_nic *efx, u16 revision_info); +extern int sfe4001_poweron(struct efx_nic *efx); +extern void sfe4001_poweroff(struct efx_nic *efx); + +#endif diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c new file mode 100644 index 00000000000..59edcf793c1 --- /dev/null +++ b/drivers/net/sfc/efx.c @@ -0,0 +1,2208 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/delay.h> +#include <linux/notifier.h> +#include <linux/ip.h> +#include <linux/tcp.h> +#include <linux/in.h> +#include <linux/crc32.h> +#include <linux/ethtool.h> +#include "net_driver.h" +#include "gmii.h" +#include "ethtool.h" +#include "tx.h" +#include "rx.h" +#include "efx.h" +#include "mdio_10g.h" +#include "falcon.h" +#include "workarounds.h" +#include "mac.h" + +#define EFX_MAX_MTU (9 * 1024) + +/* RX slow fill workqueue. If memory allocation fails in the fast path, + * a work item is pushed onto this work queue to retry the allocation later, + * to avoid the NIC being starved of RX buffers. Since this is a per cpu + * workqueue, there is nothing to be gained in making it per NIC + */ +static struct workqueue_struct *refill_workqueue; + +/************************************************************************** + * + * Configurable values + * + *************************************************************************/ + +/* + * Enable large receive offload (LRO) aka soft segment reassembly (SSR) + * + * This sets the default for new devices. It can be controlled later + * using ethtool. + */ +static int lro = 1; +module_param(lro, int, 0644); +MODULE_PARM_DESC(lro, "Large receive offload acceleration"); + +/* + * Use separate channels for TX and RX events + * + * Set this to 1 to use separate channels for TX and RX. It allows us to + * apply a higher level of interrupt moderation to TX events. + * + * This is forced to 0 for MSI interrupt mode as the interrupt vector + * is not written + */ +static unsigned int separate_tx_and_rx_channels = 1; + +/* This is the weight assigned to each of the (per-channel) virtual + * NAPI devices. + */ +static int napi_weight = 64; + +/* This is the time (in jiffies) between invocations of the hardware + * monitor, which checks for known hardware bugs and resets the + * hardware and driver as necessary. + */ +unsigned int efx_monitor_interval = 1 * HZ; + +/* This controls whether or not the hardware monitor will trigger a + * reset when it detects an error condition. + */ +static unsigned int monitor_reset = 1; + +/* This controls whether or not the driver will initialise devices + * with invalid MAC addresses stored in the EEPROM or flash. If true, + * such devices will be initialised with a random locally-generated + * MAC address. This allows for loading the sfc_mtd driver to + * reprogram the flash, even if the flash contents (including the MAC + * address) have previously been erased. + */ +static unsigned int allow_bad_hwaddr; + +/* Initial interrupt moderation settings. They can be modified after + * module load with ethtool. + * + * The default for RX should strike a balance between increasing the + * round-trip latency and reducing overhead. + */ +static unsigned int rx_irq_mod_usec = 60; + +/* Initial interrupt moderation settings. They can be modified after + * module load with ethtool. + * + * This default is chosen to ensure that a 10G link does not go idle + * while a TX queue is stopped after it has become full. A queue is + * restarted when it drops below half full. The time this takes (assuming + * worst case 3 descriptors per packet and 1024 descriptors) is + * 512 / 3 * 1.2 = 205 usec. + */ +static unsigned int tx_irq_mod_usec = 150; + +/* This is the first interrupt mode to try out of: + * 0 => MSI-X + * 1 => MSI + * 2 => legacy + */ +static unsigned int interrupt_mode; + +/* This is the requested number of CPUs to use for Receive-Side Scaling (RSS), + * i.e. the number of CPUs among which we may distribute simultaneous + * interrupt handling. + * + * Cards without MSI-X will only target one CPU via legacy or MSI interrupt. + * The default (0) means to assign an interrupt to each package (level II cache) + */ +static unsigned int rss_cpus; +module_param(rss_cpus, uint, 0444); +MODULE_PARM_DESC(rss_cpus, "Number of CPUs to use for Receive-Side Scaling"); + +/************************************************************************** + * + * Utility functions and prototypes + * + *************************************************************************/ +static void efx_remove_channel(struct efx_channel *channel); +static void efx_remove_port(struct efx_nic *efx); +static void efx_fini_napi(struct efx_nic *efx); +static void efx_fini_channels(struct efx_nic *efx); + +#define EFX_ASSERT_RESET_SERIALISED(efx) \ + do { \ + if ((efx->state == STATE_RUNNING) || \ + (efx->state == STATE_RESETTING)) \ + ASSERT_RTNL(); \ + } while (0) + +/************************************************************************** + * + * Event queue processing + * + *************************************************************************/ + +/* Process channel's event queue + * + * This function is responsible for processing the event queue of a + * single channel. The caller must guarantee that this function will + * never be concurrently called more than once on the same channel, + * though different channels may be being processed concurrently. + */ +static inline int efx_process_channel(struct efx_channel *channel, int rx_quota) +{ + int rxdmaqs; + struct efx_rx_queue *rx_queue; + + if (unlikely(channel->efx->reset_pending != RESET_TYPE_NONE || + !channel->enabled)) + return rx_quota; + + rxdmaqs = falcon_process_eventq(channel, &rx_quota); + + /* Deliver last RX packet. */ + if (channel->rx_pkt) { + __efx_rx_packet(channel, channel->rx_pkt, + channel->rx_pkt_csummed); + channel->rx_pkt = NULL; + } + + efx_flush_lro(channel); + efx_rx_strategy(channel); + + /* Refill descriptor rings as necessary */ + rx_queue = &channel->efx->rx_queue[0]; + while (rxdmaqs) { + if (rxdmaqs & 0x01) + efx_fast_push_rx_descriptors(rx_queue); + rx_queue++; + rxdmaqs >>= 1; + } + + return rx_quota; +} + +/* Mark channel as finished processing + * + * Note that since we will not receive further interrupts for this + * channel before we finish processing and call the eventq_read_ack() + * method, there is no need to use the interrupt hold-off timers. + */ +static inline void efx_channel_processed(struct efx_channel *channel) +{ + /* Write to EVQ_RPTR_REG. If a new event arrived in a race + * with finishing processing, a new interrupt will be raised. + */ + channel->work_pending = 0; + smp_wmb(); /* Ensure channel updated before any new interrupt. */ + falcon_eventq_read_ack(channel); +} + +/* NAPI poll handler + * + * NAPI guarantees serialisation of polls of the same device, which + * provides the guarantee required by efx_process_channel(). + */ +static int efx_poll(struct napi_struct *napi, int budget) +{ + struct efx_channel *channel = + container_of(napi, struct efx_channel, napi_str); + struct net_device *napi_dev = channel->napi_dev; + int unused; + int rx_packets; + + EFX_TRACE(channel->efx, "channel %d NAPI poll executing on CPU %d\n", + channel->channel, raw_smp_processor_id()); + + unused = efx_process_channel(channel, budget); + rx_packets = (budget - unused); + + if (rx_packets < budget) { + /* There is no race here; although napi_disable() will + * only wait for netif_rx_complete(), this isn't a problem + * since efx_channel_processed() will have no effect if + * interrupts have already been disabled. + */ + netif_rx_complete(napi_dev, napi); + efx_channel_processed(channel); + } + + return rx_packets; +} + +/* Process the eventq of the specified channel immediately on this CPU + * + * Disable hardware generated interrupts, wait for any existing + * processing to finish, then directly poll (and ack ) the eventq. + * Finally reenable NAPI and interrupts. + * + * Since we are touching interrupts the caller should hold the suspend lock + */ +void efx_process_channel_now(struct efx_channel *channel) +{ + struct efx_nic *efx = channel->efx; + + BUG_ON(!channel->used_flags); + BUG_ON(!channel->enabled); + + /* Disable interrupts and wait for ISRs to complete */ + falcon_disable_interrupts(efx); + if (efx->legacy_irq) + synchronize_irq(efx->legacy_irq); + if (channel->has_interrupt && channel->irq) + synchronize_irq(channel->irq); + + /* Wait for any NAPI processing to complete */ + napi_disable(&channel->napi_str); + + /* Poll the channel */ + (void) efx_process_channel(channel, efx->type->evq_size); + + /* Ack the eventq. This may cause an interrupt to be generated + * when they are reenabled */ + efx_channel_processed(channel); + + napi_enable(&channel->napi_str); + falcon_enable_interrupts(efx); +} + +/* Create event queue + * Event queue memory allocations are done only once. If the channel + * is reset, the memory buffer will be reused; this guards against + * errors during channel reset and also simplifies interrupt handling. + */ +static int efx_probe_eventq(struct efx_channel *channel) +{ + EFX_LOG(channel->efx, "chan %d create event queue\n", channel->channel); + + return falcon_probe_eventq(channel); +} + +/* Prepare channel's event queue */ +static int efx_init_eventq(struct efx_channel *channel) +{ + EFX_LOG(channel->efx, "chan %d init event queue\n", channel->channel); + + channel->eventq_read_ptr = 0; + + return falcon_init_eventq(channel); +} + +static void efx_fini_eventq(struct efx_channel *channel) +{ + EFX_LOG(channel->efx, "chan %d fini event queue\n", channel->channel); + + falcon_fini_eventq(channel); +} + +static void efx_remove_eventq(struct efx_channel *channel) +{ + EFX_LOG(channel->efx, "chan %d remove event queue\n", channel->channel); + + falcon_remove_eventq(channel); +} + +/************************************************************************** + * + * Channel handling + * + *************************************************************************/ + +/* Setup per-NIC RX buffer parameters. + * Calculate the rx buffer allocation parameters required to support + * the current MTU, including padding for header alignment and overruns. + */ +static void efx_calc_rx_buffer_params(struct efx_nic *efx) +{ + unsigned int order, len; + + len = (max(EFX_PAGE_IP_ALIGN, NET_IP_ALIGN) + + EFX_MAX_FRAME_LEN(efx->net_dev->mtu) + + efx->type->rx_buffer_padding); + + /* Calculate page-order */ + for (order = 0; ((1u << order) * PAGE_SIZE) < len; ++order) + ; + + efx->rx_buffer_len = len; + efx->rx_buffer_order = order; +} + +static int efx_probe_channel(struct efx_channel *channel) +{ + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + int rc; + + EFX_LOG(channel->efx, "creating channel %d\n", channel->channel); + + rc = efx_probe_eventq(channel); + if (rc) + goto fail1; + + efx_for_each_channel_tx_queue(tx_queue, channel) { + rc = efx_probe_tx_queue(tx_queue); + if (rc) + goto fail2; + } + + efx_for_each_channel_rx_queue(rx_queue, channel) { + rc = efx_probe_rx_queue(rx_queue); + if (rc) + goto fail3; + } + + channel->n_rx_frm_trunc = 0; + + return 0; + + fail3: + efx_for_each_channel_rx_queue(rx_queue, channel) + efx_remove_rx_queue(rx_queue); + fail2: + efx_for_each_channel_tx_queue(tx_queue, channel) + efx_remove_tx_queue(tx_queue); + fail1: + return rc; +} + + +/* Channels are shutdown and reinitialised whilst the NIC is running + * to propagate configuration changes (mtu, checksum offload), or + * to clear hardware error conditions + */ +static int efx_init_channels(struct efx_nic *efx) +{ + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + struct efx_channel *channel; + int rc = 0; + + efx_calc_rx_buffer_params(efx); + + /* Initialise the channels */ + efx_for_each_channel(channel, efx) { + EFX_LOG(channel->efx, "init chan %d\n", channel->channel); + + rc = efx_init_eventq(channel); + if (rc) + goto err; + + efx_for_each_channel_tx_queue(tx_queue, channel) { + rc = efx_init_tx_queue(tx_queue); + if (rc) + goto err; + } + + /* The rx buffer allocation strategy is MTU dependent */ + efx_rx_strategy(channel); + + efx_for_each_channel_rx_queue(rx_queue, channel) { + rc = efx_init_rx_queue(rx_queue); + if (rc) + goto err; + } + + WARN_ON(channel->rx_pkt != NULL); + efx_rx_strategy(channel); + } + + return 0; + + err: + EFX_ERR(efx, "failed to initialise channel %d\n", + channel ? channel->channel : -1); + efx_fini_channels(efx); + return rc; +} + +/* This enables event queue processing and packet transmission. + * + * Note that this function is not allowed to fail, since that would + * introduce too much complexity into the suspend/resume path. + */ +static void efx_start_channel(struct efx_channel *channel) +{ + struct efx_rx_queue *rx_queue; + + EFX_LOG(channel->efx, "starting chan %d\n", channel->channel); + + if (!(channel->efx->net_dev->flags & IFF_UP)) + netif_napi_add(channel->napi_dev, &channel->napi_str, + efx_poll, napi_weight); + + channel->work_pending = 0; + channel->enabled = 1; + smp_wmb(); /* ensure channel updated before first interrupt */ + + napi_enable(&channel->napi_str); + + /* Load up RX descriptors */ + efx_for_each_channel_rx_queue(rx_queue, channel) + efx_fast_push_rx_descriptors(rx_queue); +} + +/* This disables event queue processing and packet transmission. + * This function does not guarantee that all queue processing + * (e.g. RX refill) is complete. + */ +static void efx_stop_channel(struct efx_channel *channel) +{ + struct efx_rx_queue *rx_queue; + + if (!channel->enabled) + return; + + EFX_LOG(channel->efx, "stop chan %d\n", channel->channel); + + channel->enabled = 0; + napi_disable(&channel->napi_str); + + /* Ensure that any worker threads have exited or will be no-ops */ + efx_for_each_channel_rx_queue(rx_queue, channel) { + spin_lock_bh(&rx_queue->add_lock); + spin_unlock_bh(&rx_queue->add_lock); + } +} + +static void efx_fini_channels(struct efx_nic *efx) +{ + struct efx_channel *channel; + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + + EFX_ASSERT_RESET_SERIALISED(efx); + BUG_ON(efx->port_enabled); + + efx_for_each_channel(channel, efx) { + EFX_LOG(channel->efx, "shut down chan %d\n", channel->channel); + + efx_for_each_channel_rx_queue(rx_queue, channel) + efx_fini_rx_queue(rx_queue); + efx_for_each_channel_tx_queue(tx_queue, channel) + efx_fini_tx_queue(tx_queue); + } + + /* Do the event queues last so that we can handle flush events + * for all DMA queues. */ + efx_for_each_channel(channel, efx) { + EFX_LOG(channel->efx, "shut down evq %d\n", channel->channel); + + efx_fini_eventq(channel); + } +} + +static void efx_remove_channel(struct efx_channel *channel) +{ + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + + EFX_LOG(channel->efx, "destroy chan %d\n", channel->channel); + + efx_for_each_channel_rx_queue(rx_queue, channel) + efx_remove_rx_queue(rx_queue); + efx_for_each_channel_tx_queue(tx_queue, channel) + efx_remove_tx_queue(tx_queue); + efx_remove_eventq(channel); + + channel->used_flags = 0; +} + +void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay) +{ + queue_delayed_work(refill_workqueue, &rx_queue->work, delay); +} + +/************************************************************************** + * + * Port handling + * + **************************************************************************/ + +/* This ensures that the kernel is kept informed (via + * netif_carrier_on/off) of the link status, and also maintains the + * link status's stop on the port's TX queue. + */ +static void efx_link_status_changed(struct efx_nic *efx) +{ + int carrier_ok; + + /* SFC Bug 5356: A net_dev notifier is registered, so we must ensure + * that no events are triggered between unregister_netdev() and the + * driver unloading. A more general condition is that NETDEV_CHANGE + * can only be generated between NETDEV_UP and NETDEV_DOWN */ + if (!netif_running(efx->net_dev)) + return; + + carrier_ok = netif_carrier_ok(efx->net_dev) ? 1 : 0; + if (efx->link_up != carrier_ok) { + efx->n_link_state_changes++; + + if (efx->link_up) + netif_carrier_on(efx->net_dev); + else + netif_carrier_off(efx->net_dev); + } + + /* Status message for kernel log */ + if (efx->link_up) { + struct mii_if_info *gmii = &efx->mii; + unsigned adv, lpa; + /* NONE here means direct XAUI from the controller, with no + * MDIO-attached device we can query. */ + if (efx->phy_type != PHY_TYPE_NONE) { + adv = gmii_advertised(gmii); + lpa = gmii_lpa(gmii); + } else { + lpa = GM_LPA_10000 | LPA_DUPLEX; + adv = lpa; + } + EFX_INFO(efx, "link up at %dMbps %s-duplex " + "(adv %04x lpa %04x) (MTU %d)%s\n", + (efx->link_options & GM_LPA_10000 ? 10000 : + (efx->link_options & GM_LPA_1000 ? 1000 : + (efx->link_options & GM_LPA_100 ? 100 : + 10))), + (efx->link_options & GM_LPA_DUPLEX ? + "full" : "half"), + adv, lpa, + efx->net_dev->mtu, + (efx->promiscuous ? " [PROMISC]" : "")); + } else { + EFX_INFO(efx, "link down\n"); + } + +} + +/* This call reinitialises the MAC to pick up new PHY settings. The + * caller must hold the mac_lock */ +static void __efx_reconfigure_port(struct efx_nic *efx) +{ + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + + EFX_LOG(efx, "reconfiguring MAC from PHY settings on CPU %d\n", + raw_smp_processor_id()); + + falcon_reconfigure_xmac(efx); + + /* Inform kernel of loss/gain of carrier */ + efx_link_status_changed(efx); +} + +/* Reinitialise the MAC to pick up new PHY settings, even if the port is + * disabled. */ +void efx_reconfigure_port(struct efx_nic *efx) +{ + EFX_ASSERT_RESET_SERIALISED(efx); + + mutex_lock(&efx->mac_lock); + __efx_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); +} + +/* Asynchronous efx_reconfigure_port work item. To speed up efx_flush_all() + * we don't efx_reconfigure_port() if the port is disabled. Care is taken + * in efx_stop_all() and efx_start_port() to prevent PHY events being lost */ +static void efx_reconfigure_work(struct work_struct *data) +{ + struct efx_nic *efx = container_of(data, struct efx_nic, + reconfigure_work); + + mutex_lock(&efx->mac_lock); + if (efx->port_enabled) + __efx_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); +} + +static int efx_probe_port(struct efx_nic *efx) +{ + int rc; + + EFX_LOG(efx, "create port\n"); + + /* Connect up MAC/PHY operations table and read MAC address */ + rc = falcon_probe_port(efx); + if (rc) + goto err; + + /* Sanity check MAC address */ + if (is_valid_ether_addr(efx->mac_address)) { + memcpy(efx->net_dev->dev_addr, efx->mac_address, ETH_ALEN); + } else { + DECLARE_MAC_BUF(mac); + + EFX_ERR(efx, "invalid MAC address %s\n", + print_mac(mac, efx->mac_address)); + if (!allow_bad_hwaddr) { + rc = -EINVAL; + goto err; + } + random_ether_addr(efx->net_dev->dev_addr); + EFX_INFO(efx, "using locally-generated MAC %s\n", + print_mac(mac, efx->net_dev->dev_addr)); + } + + return 0; + + err: + efx_remove_port(efx); + return rc; +} + +static int efx_init_port(struct efx_nic *efx) +{ + int rc; + + EFX_LOG(efx, "init port\n"); + + /* Initialise the MAC and PHY */ + rc = falcon_init_xmac(efx); + if (rc) + return rc; + + efx->port_initialized = 1; + + /* Reconfigure port to program MAC registers */ + falcon_reconfigure_xmac(efx); + + return 0; +} + +/* Allow efx_reconfigure_port() to be scheduled, and close the window + * between efx_stop_port and efx_flush_all whereby a previously scheduled + * efx_reconfigure_port() may have been cancelled */ +static void efx_start_port(struct efx_nic *efx) +{ + EFX_LOG(efx, "start port\n"); + BUG_ON(efx->port_enabled); + + mutex_lock(&efx->mac_lock); + efx->port_enabled = 1; + __efx_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); +} + +/* Prevent efx_reconfigure_work and efx_monitor() from executing, and + * efx_set_multicast_list() from scheduling efx_reconfigure_work. + * efx_reconfigure_work can still be scheduled via NAPI processing + * until efx_flush_all() is called */ +static void efx_stop_port(struct efx_nic *efx) +{ + EFX_LOG(efx, "stop port\n"); + + mutex_lock(&efx->mac_lock); + efx->port_enabled = 0; + mutex_unlock(&efx->mac_lock); + + /* Serialise against efx_set_multicast_list() */ + if (NET_DEV_REGISTERED(efx)) { + netif_tx_lock_bh(efx->net_dev); + netif_tx_unlock_bh(efx->net_dev); + } +} + +static void efx_fini_port(struct efx_nic *efx) +{ + EFX_LOG(efx, "shut down port\n"); + + if (!efx->port_initialized) + return; + + falcon_fini_xmac(efx); + efx->port_initialized = 0; + + efx->link_up = 0; + efx_link_status_changed(efx); +} + +static void efx_remove_port(struct efx_nic *efx) +{ + EFX_LOG(efx, "destroying port\n"); + + falcon_remove_port(efx); +} + +/************************************************************************** + * + * NIC handling + * + **************************************************************************/ + +/* This configures the PCI device to enable I/O and DMA. */ +static int efx_init_io(struct efx_nic *efx) +{ + struct pci_dev *pci_dev = efx->pci_dev; + dma_addr_t dma_mask = efx->type->max_dma_mask; + int rc; + + EFX_LOG(efx, "initialising I/O\n"); + + rc = pci_enable_device(pci_dev); + if (rc) { + EFX_ERR(efx, "failed to enable PCI device\n"); + goto fail1; + } + + pci_set_master(pci_dev); + + /* Set the PCI DMA mask. Try all possibilities from our + * genuine mask down to 32 bits, because some architectures + * (e.g. x86_64 with iommu_sac_force set) will allow 40 bit + * masks event though they reject 46 bit masks. + */ + while (dma_mask > 0x7fffffffUL) { + if (pci_dma_supported(pci_dev, dma_mask) && + ((rc = pci_set_dma_mask(pci_dev, dma_mask)) == 0)) + break; + dma_mask >>= 1; + } + if (rc) { + EFX_ERR(efx, "could not find a suitable DMA mask\n"); + goto fail2; + } + EFX_LOG(efx, "using DMA mask %llx\n", (unsigned long long) dma_mask); + rc = pci_set_consistent_dma_mask(pci_dev, dma_mask); + if (rc) { + /* pci_set_consistent_dma_mask() is not *allowed* to + * fail with a mask that pci_set_dma_mask() accepted, + * but just in case... + */ + EFX_ERR(efx, "failed to set consistent DMA mask\n"); + goto fail2; + } + + efx->membase_phys = pci_resource_start(efx->pci_dev, + efx->type->mem_bar); + rc = pci_request_region(pci_dev, efx->type->mem_bar, "sfc"); + if (rc) { + EFX_ERR(efx, "request for memory BAR failed\n"); + rc = -EIO; + goto fail3; + } + efx->membase = ioremap_nocache(efx->membase_phys, + efx->type->mem_map_size); + if (!efx->membase) { + EFX_ERR(efx, "could not map memory BAR %d at %lx+%x\n", + efx->type->mem_bar, efx->membase_phys, + efx->type->mem_map_size); + rc = -ENOMEM; + goto fail4; + } + EFX_LOG(efx, "memory BAR %u at %lx+%x (virtual %p)\n", + efx->type->mem_bar, efx->membase_phys, efx->type->mem_map_size, + efx->membase); + + return 0; + + fail4: + release_mem_region(efx->membase_phys, efx->type->mem_map_size); + fail3: + efx->membase_phys = 0UL; + fail2: + pci_disable_device(efx->pci_dev); + fail1: + return rc; +} + +static void efx_fini_io(struct efx_nic *efx) +{ + EFX_LOG(efx, "shutting down I/O\n"); + + if (efx->membase) { + iounmap(efx->membase); + efx->membase = NULL; + } + + if (efx->membase_phys) { + pci_release_region(efx->pci_dev, efx->type->mem_bar); + efx->membase_phys = 0UL; + } + + pci_disable_device(efx->pci_dev); +} + +/* Probe the number and type of interrupts we are able to obtain. */ +static void efx_probe_interrupts(struct efx_nic *efx) +{ + int max_channel = efx->type->phys_addr_channels - 1; + struct msix_entry xentries[EFX_MAX_CHANNELS]; + int rc, i; + + if (efx->interrupt_mode == EFX_INT_MODE_MSIX) { + BUG_ON(!pci_find_capability(efx->pci_dev, PCI_CAP_ID_MSIX)); + + efx->rss_queues = rss_cpus ? rss_cpus : num_online_cpus(); + efx->rss_queues = min(efx->rss_queues, max_channel + 1); + efx->rss_queues = min(efx->rss_queues, EFX_MAX_CHANNELS); + + /* Request maximum number of MSI interrupts, and fill out + * the channel interrupt information the allowed allocation */ + for (i = 0; i < efx->rss_queues; i++) + xentries[i].entry = i; + rc = pci_enable_msix(efx->pci_dev, xentries, efx->rss_queues); + if (rc > 0) { + EFX_BUG_ON_PARANOID(rc >= efx->rss_queues); + efx->rss_queues = rc; + rc = pci_enable_msix(efx->pci_dev, xentries, + efx->rss_queues); + } + + if (rc == 0) { + for (i = 0; i < efx->rss_queues; i++) { + efx->channel[i].has_interrupt = 1; + efx->channel[i].irq = xentries[i].vector; + } + } else { + /* Fall back to single channel MSI */ + efx->interrupt_mode = EFX_INT_MODE_MSI; + EFX_ERR(efx, "could not enable MSI-X\n"); + } + } + + /* Try single interrupt MSI */ + if (efx->interrupt_mode == EFX_INT_MODE_MSI) { + efx->rss_queues = 1; + rc = pci_enable_msi(efx->pci_dev); + if (rc == 0) { + efx->channel[0].irq = efx->pci_dev->irq; + efx->channel[0].has_interrupt = 1; + } else { + EFX_ERR(efx, "could not enable MSI\n"); + efx->interrupt_mode = EFX_INT_MODE_LEGACY; + } + } + + /* Assume legacy interrupts */ + if (efx->interrupt_mode == EFX_INT_MODE_LEGACY) { + efx->rss_queues = 1; + /* Every channel is interruptible */ + for (i = 0; i < EFX_MAX_CHANNELS; i++) + efx->channel[i].has_interrupt = 1; + efx->legacy_irq = efx->pci_dev->irq; + } +} + +static void efx_remove_interrupts(struct efx_nic *efx) +{ + struct efx_channel *channel; + + /* Remove MSI/MSI-X interrupts */ + efx_for_each_channel_with_interrupt(channel, efx) + channel->irq = 0; + pci_disable_msi(efx->pci_dev); + pci_disable_msix(efx->pci_dev); + + /* Remove legacy interrupt */ + efx->legacy_irq = 0; +} + +/* Select number of used resources + * Should be called after probe_interrupts() + */ +static void efx_select_used(struct efx_nic *efx) +{ + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + int i; + + /* TX queues. One per port per channel with TX capability + * (more than one per port won't work on Linux, due to out + * of order issues... but will be fine on Solaris) + */ + tx_queue = &efx->tx_queue[0]; + + /* Perform this for each channel with TX capabilities. + * At the moment, we only support a single TX queue + */ + tx_queue->used = 1; + if ((!EFX_INT_MODE_USE_MSI(efx)) && separate_tx_and_rx_channels) + tx_queue->channel = &efx->channel[1]; + else + tx_queue->channel = &efx->channel[0]; + tx_queue->channel->used_flags |= EFX_USED_BY_TX; + tx_queue++; + + /* RX queues. Each has a dedicated channel. */ + for (i = 0; i < EFX_MAX_RX_QUEUES; i++) { + rx_queue = &efx->rx_queue[i]; + + if (i < efx->rss_queues) { + rx_queue->used = 1; + /* If we allow multiple RX queues per channel + * we need to decide that here + */ + rx_queue->channel = &efx->channel[rx_queue->queue]; + rx_queue->channel->used_flags |= EFX_USED_BY_RX; + rx_queue++; + } + } +} + +static int efx_probe_nic(struct efx_nic *efx) +{ + int rc; + + EFX_LOG(efx, "creating NIC\n"); + + /* Carry out hardware-type specific initialisation */ + rc = falcon_probe_nic(efx); + if (rc) + return rc; + + /* Determine the number of channels and RX queues by trying to hook + * in MSI-X interrupts. */ + efx_probe_interrupts(efx); + + /* Determine number of RX queues and TX queues */ + efx_select_used(efx); + + /* Initialise the interrupt moderation settings */ + efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec); + + return 0; +} + +static void efx_remove_nic(struct efx_nic *efx) +{ + EFX_LOG(efx, "destroying NIC\n"); + + efx_remove_interrupts(efx); + falcon_remove_nic(efx); +} + +/************************************************************************** + * + * NIC startup/shutdown + * + *************************************************************************/ + +static int efx_probe_all(struct efx_nic *efx) +{ + struct efx_channel *channel; + int rc; + + /* Create NIC */ + rc = efx_probe_nic(efx); + if (rc) { + EFX_ERR(efx, "failed to create NIC\n"); + goto fail1; + } + + /* Create port */ + rc = efx_probe_port(efx); + if (rc) { + EFX_ERR(efx, "failed to create port\n"); + goto fail2; + } + + /* Create channels */ + efx_for_each_channel(channel, efx) { + rc = efx_probe_channel(channel); + if (rc) { + EFX_ERR(efx, "failed to create channel %d\n", + channel->channel); + goto fail3; + } + } + + return 0; + + fail3: + efx_for_each_channel(channel, efx) + efx_remove_channel(channel); + efx_remove_port(efx); + fail2: + efx_remove_nic(efx); + fail1: + return rc; +} + +/* Called after previous invocation(s) of efx_stop_all, restarts the + * port, kernel transmit queue, NAPI processing and hardware interrupts, + * and ensures that the port is scheduled to be reconfigured. + * This function is safe to call multiple times when the NIC is in any + * state. */ +static void efx_start_all(struct efx_nic *efx) +{ + struct efx_channel *channel; + + EFX_ASSERT_RESET_SERIALISED(efx); + + /* Check that it is appropriate to restart the interface. All + * of these flags are safe to read under just the rtnl lock */ + if (efx->port_enabled) + return; + if ((efx->state != STATE_RUNNING) && (efx->state != STATE_INIT)) + return; + if (NET_DEV_REGISTERED(efx) && !netif_running(efx->net_dev)) + return; + + /* Mark the port as enabled so port reconfigurations can start, then + * restart the transmit interface early so the watchdog timer stops */ + efx_start_port(efx); + efx_wake_queue(efx); + + efx_for_each_channel(channel, efx) + efx_start_channel(channel); + + falcon_enable_interrupts(efx); + + /* Start hardware monitor if we're in RUNNING */ + if (efx->state == STATE_RUNNING) + queue_delayed_work(efx->workqueue, &efx->monitor_work, + efx_monitor_interval); +} + +/* Flush all delayed work. Should only be called when no more delayed work + * will be scheduled. This doesn't flush pending online resets (efx_reset), + * since we're holding the rtnl_lock at this point. */ +static void efx_flush_all(struct efx_nic *efx) +{ + struct efx_rx_queue *rx_queue; + + /* Make sure the hardware monitor is stopped */ + cancel_delayed_work_sync(&efx->monitor_work); + + /* Ensure that all RX slow refills are complete. */ + efx_for_each_rx_queue(rx_queue, efx) { + cancel_delayed_work_sync(&rx_queue->work); + } + + /* Stop scheduled port reconfigurations */ + cancel_work_sync(&efx->reconfigure_work); + +} + +/* Quiesce hardware and software without bringing the link down. + * Safe to call multiple times, when the nic and interface is in any + * state. The caller is guaranteed to subsequently be in a position + * to modify any hardware and software state they see fit without + * taking locks. */ +static void efx_stop_all(struct efx_nic *efx) +{ + struct efx_channel *channel; + + EFX_ASSERT_RESET_SERIALISED(efx); + + /* port_enabled can be read safely under the rtnl lock */ + if (!efx->port_enabled) + return; + + /* Disable interrupts and wait for ISR to complete */ + falcon_disable_interrupts(efx); + if (efx->legacy_irq) + synchronize_irq(efx->legacy_irq); + efx_for_each_channel_with_interrupt(channel, efx) + if (channel->irq) + synchronize_irq(channel->irq); + + /* Stop all NAPI processing and synchronous rx refills */ + efx_for_each_channel(channel, efx) + efx_stop_channel(channel); + + /* Stop all asynchronous port reconfigurations. Since all + * event processing has already been stopped, there is no + * window to loose phy events */ + efx_stop_port(efx); + + /* Flush reconfigure_work, refill_workqueue, monitor_work */ + efx_flush_all(efx); + + /* Isolate the MAC from the TX and RX engines, so that queue + * flushes will complete in a timely fashion. */ + falcon_deconfigure_mac_wrapper(efx); + falcon_drain_tx_fifo(efx); + + /* Stop the kernel transmit interface late, so the watchdog + * timer isn't ticking over the flush */ + efx_stop_queue(efx); + if (NET_DEV_REGISTERED(efx)) { + netif_tx_lock_bh(efx->net_dev); + netif_tx_unlock_bh(efx->net_dev); + } +} + +static void efx_remove_all(struct efx_nic *efx) +{ + struct efx_channel *channel; + + efx_for_each_channel(channel, efx) + efx_remove_channel(channel); + efx_remove_port(efx); + efx_remove_nic(efx); +} + +/* A convinience function to safely flush all the queues */ +int efx_flush_queues(struct efx_nic *efx) +{ + int rc; + + EFX_ASSERT_RESET_SERIALISED(efx); + + efx_stop_all(efx); + + efx_fini_channels(efx); + rc = efx_init_channels(efx); + if (rc) { + efx_schedule_reset(efx, RESET_TYPE_DISABLE); + return rc; + } + + efx_start_all(efx); + + return 0; +} + +/************************************************************************** + * + * Interrupt moderation + * + **************************************************************************/ + +/* Set interrupt moderation parameters */ +void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs) +{ + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + + EFX_ASSERT_RESET_SERIALISED(efx); + + efx_for_each_tx_queue(tx_queue, efx) + tx_queue->channel->irq_moderation = tx_usecs; + + efx_for_each_rx_queue(rx_queue, efx) + rx_queue->channel->irq_moderation = rx_usecs; +} + +/************************************************************************** + * + * Hardware monitor + * + **************************************************************************/ + +/* Run periodically off the general workqueue. Serialised against + * efx_reconfigure_port via the mac_lock */ +static void efx_monitor(struct work_struct *data) +{ + struct efx_nic *efx = container_of(data, struct efx_nic, + monitor_work.work); + int rc = 0; + + EFX_TRACE(efx, "hardware monitor executing on CPU %d\n", + raw_smp_processor_id()); + + + /* If the mac_lock is already held then it is likely a port + * reconfiguration is already in place, which will likely do + * most of the work of check_hw() anyway. */ + if (!mutex_trylock(&efx->mac_lock)) { + queue_delayed_work(efx->workqueue, &efx->monitor_work, + efx_monitor_interval); + return; + } + + if (efx->port_enabled) + rc = falcon_check_xmac(efx); + mutex_unlock(&efx->mac_lock); + + if (rc) { + if (monitor_reset) { + EFX_ERR(efx, "hardware monitor detected a fault: " + "triggering reset\n"); + efx_schedule_reset(efx, RESET_TYPE_MONITOR); + } else { + EFX_ERR(efx, "hardware monitor detected a fault, " + "skipping reset\n"); + } + } + + queue_delayed_work(efx->workqueue, &efx->monitor_work, + efx_monitor_interval); +} + +/************************************************************************** + * + * ioctls + * + *************************************************************************/ + +/* Net device ioctl + * Context: process, rtnl_lock() held. + */ +static int efx_ioctl(struct net_device *net_dev, struct ifreq *ifr, int cmd) +{ + struct efx_nic *efx = net_dev->priv; + + EFX_ASSERT_RESET_SERIALISED(efx); + + return generic_mii_ioctl(&efx->mii, if_mii(ifr), cmd, NULL); +} + +/************************************************************************** + * + * NAPI interface + * + **************************************************************************/ + +static int efx_init_napi(struct efx_nic *efx) +{ + struct efx_channel *channel; + int rc; + + efx_for_each_channel(channel, efx) { + channel->napi_dev = efx->net_dev; + rc = efx_lro_init(&channel->lro_mgr, efx); + if (rc) + goto err; + } + return 0; + err: + efx_fini_napi(efx); + return rc; +} + +static void efx_fini_napi(struct efx_nic *efx) +{ + struct efx_channel *channel; + + efx_for_each_channel(channel, efx) { + efx_lro_fini(&channel->lro_mgr); + channel->napi_dev = NULL; + } +} + +/************************************************************************** + * + * Kernel netpoll interface + * + *************************************************************************/ + +#ifdef CONFIG_NET_POLL_CONTROLLER + +/* Although in the common case interrupts will be disabled, this is not + * guaranteed. However, all our work happens inside the NAPI callback, + * so no locking is required. + */ +static void efx_netpoll(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + struct efx_channel *channel; + + efx_for_each_channel_with_interrupt(channel, efx) + efx_schedule_channel(channel); +} + +#endif + +/************************************************************************** + * + * Kernel net device interface + * + *************************************************************************/ + +/* Context: process, rtnl_lock() held. */ +static int efx_net_open(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + EFX_ASSERT_RESET_SERIALISED(efx); + + EFX_LOG(efx, "opening device %s on CPU %d\n", net_dev->name, + raw_smp_processor_id()); + + efx_start_all(efx); + return 0; +} + +/* Context: process, rtnl_lock() held. + * Note that the kernel will ignore our return code; this method + * should really be a void. + */ +static int efx_net_stop(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + int rc; + + EFX_LOG(efx, "closing %s on CPU %d\n", net_dev->name, + raw_smp_processor_id()); + + /* Stop the device and flush all the channels */ + efx_stop_all(efx); + efx_fini_channels(efx); + rc = efx_init_channels(efx); + if (rc) + efx_schedule_reset(efx, RESET_TYPE_DISABLE); + + return 0; +} + +/* Context: process, dev_base_lock held, non-blocking. */ +static struct net_device_stats *efx_net_stats(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + struct efx_mac_stats *mac_stats = &efx->mac_stats; + struct net_device_stats *stats = &net_dev->stats; + + if (!spin_trylock(&efx->stats_lock)) + return stats; + if (efx->state == STATE_RUNNING) { + falcon_update_stats_xmac(efx); + falcon_update_nic_stats(efx); + } + spin_unlock(&efx->stats_lock); + + stats->rx_packets = mac_stats->rx_packets; + stats->tx_packets = mac_stats->tx_packets; + stats->rx_bytes = mac_stats->rx_bytes; + stats->tx_bytes = mac_stats->tx_bytes; + stats->multicast = mac_stats->rx_multicast; + stats->collisions = mac_stats->tx_collision; + stats->rx_length_errors = (mac_stats->rx_gtjumbo + + mac_stats->rx_length_error); + stats->rx_over_errors = efx->n_rx_nodesc_drop_cnt; + stats->rx_crc_errors = mac_stats->rx_bad; + stats->rx_frame_errors = mac_stats->rx_align_error; + stats->rx_fifo_errors = mac_stats->rx_overflow; + stats->rx_missed_errors = mac_stats->rx_missed; + stats->tx_window_errors = mac_stats->tx_late_collision; + + stats->rx_errors = (stats->rx_length_errors + + stats->rx_over_errors + + stats->rx_crc_errors + + stats->rx_frame_errors + + stats->rx_fifo_errors + + stats->rx_missed_errors + + mac_stats->rx_symbol_error); + stats->tx_errors = (stats->tx_window_errors + + mac_stats->tx_bad); + + return stats; +} + +/* Context: netif_tx_lock held, BHs disabled. */ +static void efx_watchdog(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + + EFX_ERR(efx, "TX stuck with stop_count=%d port_enabled=%d: %s\n", + atomic_read(&efx->netif_stop_count), efx->port_enabled, + monitor_reset ? "resetting channels" : "skipping reset"); + + if (monitor_reset) + efx_schedule_reset(efx, RESET_TYPE_MONITOR); +} + + +/* Context: process, rtnl_lock() held. */ +static int efx_change_mtu(struct net_device *net_dev, int new_mtu) +{ + struct efx_nic *efx = net_dev->priv; + int rc = 0; + + EFX_ASSERT_RESET_SERIALISED(efx); + + if (new_mtu > EFX_MAX_MTU) + return -EINVAL; + + efx_stop_all(efx); + + EFX_LOG(efx, "changing MTU to %d\n", new_mtu); + + efx_fini_channels(efx); + net_dev->mtu = new_mtu; + rc = efx_init_channels(efx); + if (rc) + goto fail; + + efx_start_all(efx); + return rc; + + fail: + efx_schedule_reset(efx, RESET_TYPE_DISABLE); + return rc; +} + +static int efx_set_mac_address(struct net_device *net_dev, void *data) +{ + struct efx_nic *efx = net_dev->priv; + struct sockaddr *addr = data; + char *new_addr = addr->sa_data; + + EFX_ASSERT_RESET_SERIALISED(efx); + + if (!is_valid_ether_addr(new_addr)) { + DECLARE_MAC_BUF(mac); + EFX_ERR(efx, "invalid ethernet MAC address requested: %s\n", + print_mac(mac, new_addr)); + return -EINVAL; + } + + memcpy(net_dev->dev_addr, new_addr, net_dev->addr_len); + + /* Reconfigure the MAC */ + efx_reconfigure_port(efx); + + return 0; +} + +/* Context: netif_tx_lock held, BHs disabled. */ +static void efx_set_multicast_list(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + struct dev_mc_list *mc_list = net_dev->mc_list; + union efx_multicast_hash *mc_hash = &efx->multicast_hash; + int promiscuous; + u32 crc; + int bit; + int i; + + /* Set per-MAC promiscuity flag and reconfigure MAC if necessary */ + promiscuous = (net_dev->flags & IFF_PROMISC) ? 1 : 0; + if (efx->promiscuous != promiscuous) { + efx->promiscuous = promiscuous; + /* Close the window between efx_stop_port() and efx_flush_all() + * by only queuing work when the port is enabled. */ + if (efx->port_enabled) + queue_work(efx->workqueue, &efx->reconfigure_work); + } + + /* Build multicast hash table */ + if (promiscuous || (net_dev->flags & IFF_ALLMULTI)) { + memset(mc_hash, 0xff, sizeof(*mc_hash)); + } else { + memset(mc_hash, 0x00, sizeof(*mc_hash)); + for (i = 0; i < net_dev->mc_count; i++) { + crc = ether_crc_le(ETH_ALEN, mc_list->dmi_addr); + bit = crc & (EFX_MCAST_HASH_ENTRIES - 1); + set_bit_le(bit, mc_hash->byte); + mc_list = mc_list->next; + } + } + + /* Create and activate new global multicast hash table */ + falcon_set_multicast_hash(efx); +} + +static int efx_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *net_dev = (struct net_device *)ptr; + + if (net_dev->open == efx_net_open && event == NETDEV_CHANGENAME) { + struct efx_nic *efx = net_dev->priv; + + strcpy(efx->name, net_dev->name); + } + + return NOTIFY_DONE; +} + +static struct notifier_block efx_netdev_notifier = { + .notifier_call = efx_netdev_event, +}; + +static int efx_register_netdev(struct efx_nic *efx) +{ + struct net_device *net_dev = efx->net_dev; + int rc; + + net_dev->watchdog_timeo = 5 * HZ; + net_dev->irq = efx->pci_dev->irq; + net_dev->open = efx_net_open; + net_dev->stop = efx_net_stop; + net_dev->get_stats = efx_net_stats; + net_dev->tx_timeout = &efx_watchdog; + net_dev->hard_start_xmit = efx_hard_start_xmit; + net_dev->do_ioctl = efx_ioctl; + net_dev->change_mtu = efx_change_mtu; + net_dev->set_mac_address = efx_set_mac_address; + net_dev->set_multicast_list = efx_set_multicast_list; +#ifdef CONFIG_NET_POLL_CONTROLLER + net_dev->poll_controller = efx_netpoll; +#endif + SET_NETDEV_DEV(net_dev, &efx->pci_dev->dev); + SET_ETHTOOL_OPS(net_dev, &efx_ethtool_ops); + + /* Always start with carrier off; PHY events will detect the link */ + netif_carrier_off(efx->net_dev); + + /* Clear MAC statistics */ + falcon_update_stats_xmac(efx); + memset(&efx->mac_stats, 0, sizeof(efx->mac_stats)); + + rc = register_netdev(net_dev); + if (rc) { + EFX_ERR(efx, "could not register net dev\n"); + return rc; + } + strcpy(efx->name, net_dev->name); + + return 0; +} + +static void efx_unregister_netdev(struct efx_nic *efx) +{ + struct efx_tx_queue *tx_queue; + + if (!efx->net_dev) + return; + + BUG_ON(efx->net_dev->priv != efx); + + /* Free up any skbs still remaining. This has to happen before + * we try to unregister the netdev as running their destructors + * may be needed to get the device ref. count to 0. */ + efx_for_each_tx_queue(tx_queue, efx) + efx_release_tx_buffers(tx_queue); + + if (NET_DEV_REGISTERED(efx)) { + strlcpy(efx->name, pci_name(efx->pci_dev), sizeof(efx->name)); + unregister_netdev(efx->net_dev); + } +} + +/************************************************************************** + * + * Device reset and suspend + * + **************************************************************************/ + +/* The final hardware and software finalisation before reset. */ +static int efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + int rc; + + EFX_ASSERT_RESET_SERIALISED(efx); + + rc = falcon_xmac_get_settings(efx, ecmd); + if (rc) { + EFX_ERR(efx, "could not back up PHY settings\n"); + goto fail; + } + + efx_fini_channels(efx); + return 0; + + fail: + return rc; +} + +/* The first part of software initialisation after a hardware reset + * This function does not handle serialisation with the kernel, it + * assumes the caller has done this */ +static int efx_reset_up(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + int rc; + + rc = efx_init_channels(efx); + if (rc) + goto fail1; + + /* Restore MAC and PHY settings. */ + rc = falcon_xmac_set_settings(efx, ecmd); + if (rc) { + EFX_ERR(efx, "could not restore PHY settings\n"); + goto fail2; + } + + return 0; + + fail2: + efx_fini_channels(efx); + fail1: + return rc; +} + +/* Reset the NIC as transparently as possible. Do not reset the PHY + * Note that the reset may fail, in which case the card will be left + * in a most-probably-unusable state. + * + * This function will sleep. You cannot reset from within an atomic + * state; use efx_schedule_reset() instead. + * + * Grabs the rtnl_lock. + */ +static int efx_reset(struct efx_nic *efx) +{ + struct ethtool_cmd ecmd; + enum reset_type method = efx->reset_pending; + int rc; + + /* Serialise with kernel interfaces */ + rtnl_lock(); + + /* If we're not RUNNING then don't reset. Leave the reset_pending + * flag set so that efx_pci_probe_main will be retried */ + if (efx->state != STATE_RUNNING) { + EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n"); + goto unlock_rtnl; + } + + efx->state = STATE_RESETTING; + EFX_INFO(efx, "resetting (%d)\n", method); + + /* The net_dev->get_stats handler is quite slow, and will fail + * if a fetch is pending over reset. Serialise against it. */ + spin_lock(&efx->stats_lock); + spin_unlock(&efx->stats_lock); + + efx_stop_all(efx); + mutex_lock(&efx->mac_lock); + + rc = efx_reset_down(efx, &ecmd); + if (rc) + goto fail1; + + rc = falcon_reset_hw(efx, method); + if (rc) { + EFX_ERR(efx, "failed to reset hardware\n"); + goto fail2; + } + + /* Allow resets to be rescheduled. */ + efx->reset_pending = RESET_TYPE_NONE; + + /* Reinitialise bus-mastering, which may have been turned off before + * the reset was scheduled. This is still appropriate, even in the + * RESET_TYPE_DISABLE since this driver generally assumes the hardware + * can respond to requests. */ + pci_set_master(efx->pci_dev); + + /* Reinitialise device. This is appropriate in the RESET_TYPE_DISABLE + * case so the driver can talk to external SRAM */ + rc = falcon_init_nic(efx); + if (rc) { + EFX_ERR(efx, "failed to initialise NIC\n"); + goto fail3; + } + + /* Leave device stopped if necessary */ + if (method == RESET_TYPE_DISABLE) { + /* Reinitialise the device anyway so the driver unload sequence + * can talk to the external SRAM */ + (void) falcon_init_nic(efx); + rc = -EIO; + goto fail4; + } + + rc = efx_reset_up(efx, &ecmd); + if (rc) + goto fail5; + + mutex_unlock(&efx->mac_lock); + EFX_LOG(efx, "reset complete\n"); + + efx->state = STATE_RUNNING; + efx_start_all(efx); + + unlock_rtnl: + rtnl_unlock(); + return 0; + + fail5: + fail4: + fail3: + fail2: + fail1: + EFX_ERR(efx, "has been disabled\n"); + efx->state = STATE_DISABLED; + + mutex_unlock(&efx->mac_lock); + rtnl_unlock(); + efx_unregister_netdev(efx); + efx_fini_port(efx); + return rc; +} + +/* The worker thread exists so that code that cannot sleep can + * schedule a reset for later. + */ +static void efx_reset_work(struct work_struct *data) +{ + struct efx_nic *nic = container_of(data, struct efx_nic, reset_work); + + efx_reset(nic); +} + +void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) +{ + enum reset_type method; + + if (efx->reset_pending != RESET_TYPE_NONE) { + EFX_INFO(efx, "quenching already scheduled reset\n"); + return; + } + + switch (type) { + case RESET_TYPE_INVISIBLE: + case RESET_TYPE_ALL: + case RESET_TYPE_WORLD: + case RESET_TYPE_DISABLE: + method = type; + break; + case RESET_TYPE_RX_RECOVERY: + case RESET_TYPE_RX_DESC_FETCH: + case RESET_TYPE_TX_DESC_FETCH: + case RESET_TYPE_TX_SKIP: + method = RESET_TYPE_INVISIBLE; + break; + default: + method = RESET_TYPE_ALL; + break; + } + + if (method != type) + EFX_LOG(efx, "scheduling reset (%d:%d)\n", type, method); + else + EFX_LOG(efx, "scheduling reset (%d)\n", method); + + efx->reset_pending = method; + + queue_work(efx->workqueue, &efx->reset_work); +} + +/************************************************************************** + * + * List of NICs we support + * + **************************************************************************/ + +/* PCI device ID table */ +static struct pci_device_id efx_pci_table[] __devinitdata = { + {PCI_DEVICE(EFX_VENDID_SFC, FALCON_A_P_DEVID), + .driver_data = (unsigned long) &falcon_a_nic_type}, + {PCI_DEVICE(EFX_VENDID_SFC, FALCON_B_P_DEVID), + .driver_data = (unsigned long) &falcon_b_nic_type}, + {0} /* end of list */ +}; + +/************************************************************************** + * + * Dummy PHY/MAC/Board operations + * + * Can be used where the MAC does not implement this operation + * Needed so all function pointers are valid and do not have to be tested + * before use + * + **************************************************************************/ +int efx_port_dummy_op_int(struct efx_nic *efx) +{ + return 0; +} +void efx_port_dummy_op_void(struct efx_nic *efx) {} +void efx_port_dummy_op_blink(struct efx_nic *efx, int blink) {} + +static struct efx_phy_operations efx_dummy_phy_operations = { + .init = efx_port_dummy_op_int, + .reconfigure = efx_port_dummy_op_void, + .check_hw = efx_port_dummy_op_int, + .fini = efx_port_dummy_op_void, + .clear_interrupt = efx_port_dummy_op_void, + .reset_xaui = efx_port_dummy_op_void, +}; + +/* Dummy board operations */ +static int efx_nic_dummy_op_int(struct efx_nic *nic) +{ + return 0; +} + +static struct efx_board efx_dummy_board_info = { + .init = efx_nic_dummy_op_int, + .init_leds = efx_port_dummy_op_int, + .set_fault_led = efx_port_dummy_op_blink, +}; + +/************************************************************************** + * + * Data housekeeping + * + **************************************************************************/ + +/* This zeroes out and then fills in the invariants in a struct + * efx_nic (including all sub-structures). + */ +static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, + struct pci_dev *pci_dev, struct net_device *net_dev) +{ + struct efx_channel *channel; + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + int i, rc; + + /* Initialise common structures */ + memset(efx, 0, sizeof(*efx)); + spin_lock_init(&efx->biu_lock); + spin_lock_init(&efx->phy_lock); + INIT_WORK(&efx->reset_work, efx_reset_work); + INIT_DELAYED_WORK(&efx->monitor_work, efx_monitor); + efx->pci_dev = pci_dev; + efx->state = STATE_INIT; + efx->reset_pending = RESET_TYPE_NONE; + strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name)); + efx->board_info = efx_dummy_board_info; + + efx->net_dev = net_dev; + efx->rx_checksum_enabled = 1; + spin_lock_init(&efx->netif_stop_lock); + spin_lock_init(&efx->stats_lock); + mutex_init(&efx->mac_lock); + efx->phy_op = &efx_dummy_phy_operations; + efx->mii.dev = net_dev; + INIT_WORK(&efx->reconfigure_work, efx_reconfigure_work); + atomic_set(&efx->netif_stop_count, 1); + + for (i = 0; i < EFX_MAX_CHANNELS; i++) { + channel = &efx->channel[i]; + channel->efx = efx; + channel->channel = i; + channel->evqnum = i; + channel->work_pending = 0; + } + for (i = 0; i < EFX_MAX_TX_QUEUES; i++) { + tx_queue = &efx->tx_queue[i]; + tx_queue->efx = efx; + tx_queue->queue = i; + tx_queue->buffer = NULL; + tx_queue->channel = &efx->channel[0]; /* for safety */ + } + for (i = 0; i < EFX_MAX_RX_QUEUES; i++) { + rx_queue = &efx->rx_queue[i]; + rx_queue->efx = efx; + rx_queue->queue = i; + rx_queue->channel = &efx->channel[0]; /* for safety */ + rx_queue->buffer = NULL; + spin_lock_init(&rx_queue->add_lock); + INIT_DELAYED_WORK(&rx_queue->work, efx_rx_work); + } + + efx->type = type; + + /* Sanity-check NIC type */ + EFX_BUG_ON_PARANOID(efx->type->txd_ring_mask & + (efx->type->txd_ring_mask + 1)); + EFX_BUG_ON_PARANOID(efx->type->rxd_ring_mask & + (efx->type->rxd_ring_mask + 1)); + EFX_BUG_ON_PARANOID(efx->type->evq_size & + (efx->type->evq_size - 1)); + /* As close as we can get to guaranteeing that we don't overflow */ + EFX_BUG_ON_PARANOID(efx->type->evq_size < + (efx->type->txd_ring_mask + 1 + + efx->type->rxd_ring_mask + 1)); + EFX_BUG_ON_PARANOID(efx->type->phys_addr_channels > EFX_MAX_CHANNELS); + + /* Higher numbered interrupt modes are less capable! */ + efx->interrupt_mode = max(efx->type->max_interrupt_mode, + interrupt_mode); + + efx->workqueue = create_singlethread_workqueue("sfc_work"); + if (!efx->workqueue) { + rc = -ENOMEM; + goto fail1; + } + + return 0; + + fail1: + return rc; +} + +static void efx_fini_struct(struct efx_nic *efx) +{ + if (efx->workqueue) { + destroy_workqueue(efx->workqueue); + efx->workqueue = NULL; + } +} + +/************************************************************************** + * + * PCI interface + * + **************************************************************************/ + +/* Main body of final NIC shutdown code + * This is called only at module unload (or hotplug removal). + */ +static void efx_pci_remove_main(struct efx_nic *efx) +{ + EFX_ASSERT_RESET_SERIALISED(efx); + + /* Skip everything if we never obtained a valid membase */ + if (!efx->membase) + return; + + efx_fini_channels(efx); + efx_fini_port(efx); + + /* Shutdown the board, then the NIC and board state */ + falcon_fini_interrupt(efx); + + efx_fini_napi(efx); + efx_remove_all(efx); +} + +/* Final NIC shutdown + * This is called only at module unload (or hotplug removal). + */ +static void efx_pci_remove(struct pci_dev *pci_dev) +{ + struct efx_nic *efx; + + efx = pci_get_drvdata(pci_dev); + if (!efx) + return; + + /* Mark the NIC as fini, then stop the interface */ + rtnl_lock(); + efx->state = STATE_FINI; + dev_close(efx->net_dev); + + /* Allow any queued efx_resets() to complete */ + rtnl_unlock(); + + if (efx->membase == NULL) + goto out; + + efx_unregister_netdev(efx); + + /* Wait for any scheduled resets to complete. No more will be + * scheduled from this point because efx_stop_all() has been + * called, we are no longer registered with driverlink, and + * the net_device's have been removed. */ + flush_workqueue(efx->workqueue); + + efx_pci_remove_main(efx); + +out: + efx_fini_io(efx); + EFX_LOG(efx, "shutdown successful\n"); + + pci_set_drvdata(pci_dev, NULL); + efx_fini_struct(efx); + free_netdev(efx->net_dev); +}; + +/* Main body of NIC initialisation + * This is called at module load (or hotplug insertion, theoretically). + */ +static int efx_pci_probe_main(struct efx_nic *efx) +{ + int rc; + + /* Do start-of-day initialisation */ + rc = efx_probe_all(efx); + if (rc) + goto fail1; + + rc = efx_init_napi(efx); + if (rc) + goto fail2; + + /* Initialise the board */ + rc = efx->board_info.init(efx); + if (rc) { + EFX_ERR(efx, "failed to initialise board\n"); + goto fail3; + } + + rc = falcon_init_nic(efx); + if (rc) { + EFX_ERR(efx, "failed to initialise NIC\n"); + goto fail4; + } + + rc = efx_init_port(efx); + if (rc) { + EFX_ERR(efx, "failed to initialise port\n"); + goto fail5; + } + + rc = efx_init_channels(efx); + if (rc) + goto fail6; + + rc = falcon_init_interrupt(efx); + if (rc) + goto fail7; + + return 0; + + fail7: + efx_fini_channels(efx); + fail6: + efx_fini_port(efx); + fail5: + fail4: + fail3: + efx_fini_napi(efx); + fail2: + efx_remove_all(efx); + fail1: + return rc; +} + +/* NIC initialisation + * + * This is called at module load (or hotplug insertion, + * theoretically). It sets up PCI mappings, tests and resets the NIC, + * sets up and registers the network devices with the kernel and hooks + * the interrupt service routine. It does not prepare the device for + * transmission; this is left to the first time one of the network + * interfaces is brought up (i.e. efx_net_open). + */ +static int __devinit efx_pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *entry) +{ + struct efx_nic_type *type = (struct efx_nic_type *) entry->driver_data; + struct net_device *net_dev; + struct efx_nic *efx; + int i, rc; + + /* Allocate and initialise a struct net_device and struct efx_nic */ + net_dev = alloc_etherdev(sizeof(*efx)); + if (!net_dev) + return -ENOMEM; + net_dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA; + if (lro) + net_dev->features |= NETIF_F_LRO; + efx = net_dev->priv; + pci_set_drvdata(pci_dev, efx); + rc = efx_init_struct(efx, type, pci_dev, net_dev); + if (rc) + goto fail1; + + EFX_INFO(efx, "Solarflare Communications NIC detected\n"); + + /* Set up basic I/O (BAR mappings etc) */ + rc = efx_init_io(efx); + if (rc) + goto fail2; + + /* No serialisation is required with the reset path because + * we're in STATE_INIT. */ + for (i = 0; i < 5; i++) { + rc = efx_pci_probe_main(efx); + if (rc == 0) + break; + + /* Serialise against efx_reset(). No more resets will be + * scheduled since efx_stop_all() has been called, and we + * have not and never have been registered with either + * the rtnetlink or driverlink layers. */ + cancel_work_sync(&efx->reset_work); + + /* Retry if a recoverably reset event has been scheduled */ + if ((efx->reset_pending != RESET_TYPE_INVISIBLE) && + (efx->reset_pending != RESET_TYPE_ALL)) + goto fail3; + + efx->reset_pending = RESET_TYPE_NONE; + } + + if (rc) { + EFX_ERR(efx, "Could not reset NIC\n"); + goto fail4; + } + + /* Switch to the running state before we expose the device to + * the OS. This is to ensure that the initial gathering of + * MAC stats succeeds. */ + rtnl_lock(); + efx->state = STATE_RUNNING; + rtnl_unlock(); + + rc = efx_register_netdev(efx); + if (rc) + goto fail5; + + EFX_LOG(efx, "initialisation successful\n"); + + return 0; + + fail5: + efx_pci_remove_main(efx); + fail4: + fail3: + efx_fini_io(efx); + fail2: + efx_fini_struct(efx); + fail1: + EFX_LOG(efx, "initialisation failed. rc=%d\n", rc); + free_netdev(net_dev); + return rc; +} + +static struct pci_driver efx_pci_driver = { + .name = EFX_DRIVER_NAME, + .id_table = efx_pci_table, + .probe = efx_pci_probe, + .remove = efx_pci_remove, +}; + +/************************************************************************** + * + * Kernel module interface + * + *************************************************************************/ + +module_param(interrupt_mode, uint, 0444); +MODULE_PARM_DESC(interrupt_mode, + "Interrupt mode (0=>MSIX 1=>MSI 2=>legacy)"); + +static int __init efx_init_module(void) +{ + int rc; + + printk(KERN_INFO "Solarflare NET driver v" EFX_DRIVER_VERSION "\n"); + + rc = register_netdevice_notifier(&efx_netdev_notifier); + if (rc) + goto err_notifier; + + refill_workqueue = create_workqueue("sfc_refill"); + if (!refill_workqueue) { + rc = -ENOMEM; + goto err_refill; + } + + rc = pci_register_driver(&efx_pci_driver); + if (rc < 0) + goto err_pci; + + return 0; + + err_pci: + destroy_workqueue(refill_workqueue); + err_refill: + unregister_netdevice_notifier(&efx_netdev_notifier); + err_notifier: + return rc; +} + +static void __exit efx_exit_module(void) +{ + printk(KERN_INFO "Solarflare NET driver unloading\n"); + + pci_unregister_driver(&efx_pci_driver); + destroy_workqueue(refill_workqueue); + unregister_netdevice_notifier(&efx_netdev_notifier); + +} + +module_init(efx_init_module); +module_exit(efx_exit_module); + +MODULE_AUTHOR("Michael Brown <mbrown@fensystems.co.uk> and " + "Solarflare Communications"); +MODULE_DESCRIPTION("Solarflare Communications network driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, efx_pci_table); diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h new file mode 100644 index 00000000000..3b2f69f4a9a --- /dev/null +++ b/drivers/net/sfc/efx.h @@ -0,0 +1,67 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_EFX_H +#define EFX_EFX_H + +#include "net_driver.h" + +/* PCI IDs */ +#define EFX_VENDID_SFC 0x1924 +#define FALCON_A_P_DEVID 0x0703 +#define FALCON_A_S_DEVID 0x6703 +#define FALCON_B_P_DEVID 0x0710 + +/* TX */ +extern int efx_xmit(struct efx_nic *efx, + struct efx_tx_queue *tx_queue, struct sk_buff *skb); +extern void efx_stop_queue(struct efx_nic *efx); +extern void efx_wake_queue(struct efx_nic *efx); + +/* RX */ +extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); +extern void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, + unsigned int len, int checksummed, int discard); +extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay); + +/* Channels */ +extern void efx_process_channel_now(struct efx_channel *channel); +extern int efx_flush_queues(struct efx_nic *efx); + +/* Ports */ +extern void efx_reconfigure_port(struct efx_nic *efx); + +/* Global */ +extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); +extern void efx_suspend(struct efx_nic *efx); +extern void efx_resume(struct efx_nic *efx); +extern void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, + int rx_usecs); +extern int efx_request_power(struct efx_nic *efx, int mw, const char *name); +extern void efx_hex_dump(const u8 *, unsigned int, const char *); + +/* Dummy PHY ops for PHY drivers */ +extern int efx_port_dummy_op_int(struct efx_nic *efx); +extern void efx_port_dummy_op_void(struct efx_nic *efx); +extern void efx_port_dummy_op_blink(struct efx_nic *efx, int blink); + + +extern unsigned int efx_monitor_interval; + +static inline void efx_schedule_channel(struct efx_channel *channel) +{ + EFX_TRACE(channel->efx, "channel %d scheduling NAPI poll on CPU%d\n", + channel->channel, raw_smp_processor_id()); + channel->work_pending = 1; + + netif_rx_schedule(channel->napi_dev, &channel->napi_str); +} + +#endif /* EFX_EFX_H */ diff --git a/drivers/net/sfc/enum.h b/drivers/net/sfc/enum.h new file mode 100644 index 00000000000..43663a4619d --- /dev/null +++ b/drivers/net/sfc/enum.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2007 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_ENUM_H +#define EFX_ENUM_H + +/*****************************************************************************/ + +/** + * enum reset_type - reset types + * + * %RESET_TYPE_INVSIBLE, %RESET_TYPE_ALL, %RESET_TYPE_WORLD and + * %RESET_TYPE_DISABLE specify the method/scope of the reset. The + * other valuesspecify reasons, which efx_schedule_reset() will choose + * a method for. + * + * @RESET_TYPE_INVISIBLE: don't reset the PHYs or interrupts + * @RESET_TYPE_ALL: reset everything but PCI core blocks + * @RESET_TYPE_WORLD: reset everything, save & restore PCI config + * @RESET_TYPE_DISABLE: disable NIC + * @RESET_TYPE_MONITOR: reset due to hardware monitor + * @RESET_TYPE_INT_ERROR: reset due to internal error + * @RESET_TYPE_RX_RECOVERY: reset to recover from RX datapath errors + * @RESET_TYPE_RX_DESC_FETCH: pcie error during rx descriptor fetch + * @RESET_TYPE_TX_DESC_FETCH: pcie error during tx descriptor fetch + * @RESET_TYPE_TX_SKIP: hardware completed empty tx descriptors + */ +enum reset_type { + RESET_TYPE_NONE = -1, + RESET_TYPE_INVISIBLE = 0, + RESET_TYPE_ALL = 1, + RESET_TYPE_WORLD = 2, + RESET_TYPE_DISABLE = 3, + RESET_TYPE_MAX_METHOD, + RESET_TYPE_MONITOR, + RESET_TYPE_INT_ERROR, + RESET_TYPE_RX_RECOVERY, + RESET_TYPE_RX_DESC_FETCH, + RESET_TYPE_TX_DESC_FETCH, + RESET_TYPE_TX_SKIP, + RESET_TYPE_MAX, +}; + +#endif /* EFX_ENUM_H */ diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c new file mode 100644 index 00000000000..ad541badbd9 --- /dev/null +++ b/drivers/net/sfc/ethtool.c @@ -0,0 +1,460 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include <linux/netdevice.h> +#include <linux/ethtool.h> +#include <linux/rtnetlink.h> +#include "net_driver.h" +#include "efx.h" +#include "ethtool.h" +#include "falcon.h" +#include "gmii.h" +#include "mac.h" + +static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable); + +struct ethtool_string { + char name[ETH_GSTRING_LEN]; +}; + +struct efx_ethtool_stat { + const char *name; + enum { + EFX_ETHTOOL_STAT_SOURCE_mac_stats, + EFX_ETHTOOL_STAT_SOURCE_nic, + EFX_ETHTOOL_STAT_SOURCE_channel + } source; + unsigned offset; + u64(*get_stat) (void *field); /* Reader function */ +}; + +/* Initialiser for a struct #efx_ethtool_stat with type-checking */ +#define EFX_ETHTOOL_STAT(stat_name, source_name, field, field_type, \ + get_stat_function) { \ + .name = #stat_name, \ + .source = EFX_ETHTOOL_STAT_SOURCE_##source_name, \ + .offset = ((((field_type *) 0) == \ + &((struct efx_##source_name *)0)->field) ? \ + offsetof(struct efx_##source_name, field) : \ + offsetof(struct efx_##source_name, field)), \ + .get_stat = get_stat_function, \ +} + +static u64 efx_get_uint_stat(void *field) +{ + return *(unsigned int *)field; +} + +static u64 efx_get_ulong_stat(void *field) +{ + return *(unsigned long *)field; +} + +static u64 efx_get_u64_stat(void *field) +{ + return *(u64 *) field; +} + +static u64 efx_get_atomic_stat(void *field) +{ + return atomic_read((atomic_t *) field); +} + +#define EFX_ETHTOOL_ULONG_MAC_STAT(field) \ + EFX_ETHTOOL_STAT(field, mac_stats, field, \ + unsigned long, efx_get_ulong_stat) + +#define EFX_ETHTOOL_U64_MAC_STAT(field) \ + EFX_ETHTOOL_STAT(field, mac_stats, field, \ + u64, efx_get_u64_stat) + +#define EFX_ETHTOOL_UINT_NIC_STAT(name) \ + EFX_ETHTOOL_STAT(name, nic, n_##name, \ + unsigned int, efx_get_uint_stat) + +#define EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(field) \ + EFX_ETHTOOL_STAT(field, nic, field, \ + atomic_t, efx_get_atomic_stat) + +#define EFX_ETHTOOL_UINT_CHANNEL_STAT(field) \ + EFX_ETHTOOL_STAT(field, channel, n_##field, \ + unsigned int, efx_get_uint_stat) + +static struct efx_ethtool_stat efx_ethtool_stats[] = { + EFX_ETHTOOL_U64_MAC_STAT(tx_bytes), + EFX_ETHTOOL_U64_MAC_STAT(tx_good_bytes), + EFX_ETHTOOL_U64_MAC_STAT(tx_bad_bytes), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_packets), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_bad), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_pause), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_control), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_unicast), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_multicast), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_broadcast), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_lt64), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_64), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_65_to_127), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_128_to_255), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_256_to_511), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_512_to_1023), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_1024_to_15xx), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_15xx_to_jumbo), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_gtjumbo), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_collision), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_single_collision), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_multiple_collision), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_excessive_collision), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_deferred), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_late_collision), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_excessive_deferred), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_non_tcpudp), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_mac_src_error), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_ip_src_error), + EFX_ETHTOOL_U64_MAC_STAT(rx_bytes), + EFX_ETHTOOL_U64_MAC_STAT(rx_good_bytes), + EFX_ETHTOOL_U64_MAC_STAT(rx_bad_bytes), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_packets), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_good), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_pause), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_control), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_unicast), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_multicast), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_broadcast), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_lt64), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_64), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_65_to_127), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_128_to_255), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_256_to_511), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_512_to_1023), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_1024_to_15xx), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_15xx_to_jumbo), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_gtjumbo), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_lt64), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_64_to_15xx), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_15xx_to_jumbo), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_gtjumbo), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_overflow), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_missed), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_false_carrier), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_symbol_error), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_align_error), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_length_error), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_internal_error), + EFX_ETHTOOL_UINT_NIC_STAT(rx_nodesc_drop_cnt), + EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(rx_reset), + EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc), + EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err), + EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tcp_udp_chksum_err), + EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_frm_trunc), +}; + +/* Number of ethtool statistics */ +#define EFX_ETHTOOL_NUM_STATS ARRAY_SIZE(efx_ethtool_stats) + +/************************************************************************** + * + * Ethtool operations + * + ************************************************************************** + */ + +/* Identify device by flashing LEDs */ +static int efx_ethtool_phys_id(struct net_device *net_dev, u32 seconds) +{ + struct efx_nic *efx = net_dev->priv; + + efx->board_info.blink(efx, 1); + schedule_timeout_interruptible(seconds * HZ); + efx->board_info.blink(efx, 0); + return 0; +} + +/* This must be called with rtnl_lock held. */ +int efx_ethtool_get_settings(struct net_device *net_dev, + struct ethtool_cmd *ecmd) +{ + struct efx_nic *efx = net_dev->priv; + int rc; + + mutex_lock(&efx->mac_lock); + rc = falcon_xmac_get_settings(efx, ecmd); + mutex_unlock(&efx->mac_lock); + + return rc; +} + +/* This must be called with rtnl_lock held. */ +int efx_ethtool_set_settings(struct net_device *net_dev, + struct ethtool_cmd *ecmd) +{ + struct efx_nic *efx = net_dev->priv; + int rc; + + mutex_lock(&efx->mac_lock); + rc = falcon_xmac_set_settings(efx, ecmd); + mutex_unlock(&efx->mac_lock); + if (!rc) + efx_reconfigure_port(efx); + + return rc; +} + +static void efx_ethtool_get_drvinfo(struct net_device *net_dev, + struct ethtool_drvinfo *info) +{ + struct efx_nic *efx = net_dev->priv; + + strlcpy(info->driver, EFX_DRIVER_NAME, sizeof(info->driver)); + strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); +} + +static int efx_ethtool_get_stats_count(struct net_device *net_dev) +{ + return EFX_ETHTOOL_NUM_STATS; +} + +static void efx_ethtool_get_strings(struct net_device *net_dev, + u32 string_set, u8 *strings) +{ + struct ethtool_string *ethtool_strings = + (struct ethtool_string *)strings; + int i; + + if (string_set == ETH_SS_STATS) + for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) + strncpy(ethtool_strings[i].name, + efx_ethtool_stats[i].name, + sizeof(ethtool_strings[i].name)); +} + +static void efx_ethtool_get_stats(struct net_device *net_dev, + struct ethtool_stats *stats, + u64 *data) +{ + struct efx_nic *efx = net_dev->priv; + struct efx_mac_stats *mac_stats = &efx->mac_stats; + struct efx_ethtool_stat *stat; + struct efx_channel *channel; + int i; + + EFX_BUG_ON_PARANOID(stats->n_stats != EFX_ETHTOOL_NUM_STATS); + + /* Update MAC and NIC statistics */ + net_dev->get_stats(net_dev); + + /* Fill detailed statistics buffer */ + for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) { + stat = &efx_ethtool_stats[i]; + switch (stat->source) { + case EFX_ETHTOOL_STAT_SOURCE_mac_stats: + data[i] = stat->get_stat((void *)mac_stats + + stat->offset); + break; + case EFX_ETHTOOL_STAT_SOURCE_nic: + data[i] = stat->get_stat((void *)efx + stat->offset); + break; + case EFX_ETHTOOL_STAT_SOURCE_channel: + data[i] = 0; + efx_for_each_channel(channel, efx) + data[i] += stat->get_stat((void *)channel + + stat->offset); + break; + } + } +} + +static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) +{ + struct efx_nic *efx = net_dev->priv; + int rc; + + rc = ethtool_op_set_tx_csum(net_dev, enable); + if (rc) + return rc; + + efx_flush_queues(efx); + + return 0; +} + +static int efx_ethtool_set_rx_csum(struct net_device *net_dev, u32 enable) +{ + struct efx_nic *efx = net_dev->priv; + + /* No way to stop the hardware doing the checks; we just + * ignore the result. + */ + efx->rx_checksum_enabled = (enable ? 1 : 0); + + return 0; +} + +static u32 efx_ethtool_get_rx_csum(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + + return efx->rx_checksum_enabled; +} + +/* Restart autonegotiation */ +static int efx_ethtool_nway_reset(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + + return mii_nway_restart(&efx->mii); +} + +static u32 efx_ethtool_get_link(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + + return efx->link_up; +} + +static int efx_ethtool_get_coalesce(struct net_device *net_dev, + struct ethtool_coalesce *coalesce) +{ + struct efx_nic *efx = net_dev->priv; + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + struct efx_channel *channel; + + memset(coalesce, 0, sizeof(*coalesce)); + + /* Find lowest IRQ moderation across all used TX queues */ + coalesce->tx_coalesce_usecs_irq = ~((u32) 0); + efx_for_each_tx_queue(tx_queue, efx) { + channel = tx_queue->channel; + if (channel->irq_moderation < coalesce->tx_coalesce_usecs_irq) { + if (channel->used_flags != EFX_USED_BY_RX_TX) + coalesce->tx_coalesce_usecs_irq = + channel->irq_moderation; + else + coalesce->tx_coalesce_usecs_irq = 0; + } + } + + /* Find lowest IRQ moderation across all used RX queues */ + coalesce->rx_coalesce_usecs_irq = ~((u32) 0); + efx_for_each_rx_queue(rx_queue, efx) { + channel = rx_queue->channel; + if (channel->irq_moderation < coalesce->rx_coalesce_usecs_irq) + coalesce->rx_coalesce_usecs_irq = + channel->irq_moderation; + } + + return 0; +} + +/* Set coalescing parameters + * The difficulties occur for shared channels + */ +static int efx_ethtool_set_coalesce(struct net_device *net_dev, + struct ethtool_coalesce *coalesce) +{ + struct efx_nic *efx = net_dev->priv; + struct efx_channel *channel; + struct efx_tx_queue *tx_queue; + unsigned tx_usecs, rx_usecs; + + if (coalesce->use_adaptive_rx_coalesce || + coalesce->use_adaptive_tx_coalesce) + return -EOPNOTSUPP; + + if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) { + EFX_ERR(efx, "invalid coalescing setting. " + "Only rx/tx_coalesce_usecs_irq are supported\n"); + return -EOPNOTSUPP; + } + + rx_usecs = coalesce->rx_coalesce_usecs_irq; + tx_usecs = coalesce->tx_coalesce_usecs_irq; + + /* If the channel is shared only allow RX parameters to be set */ + efx_for_each_tx_queue(tx_queue, efx) { + if ((tx_queue->channel->used_flags == EFX_USED_BY_RX_TX) && + tx_usecs) { + EFX_ERR(efx, "Channel is shared. " + "Only RX coalescing may be set\n"); + return -EOPNOTSUPP; + } + } + + efx_init_irq_moderation(efx, tx_usecs, rx_usecs); + + /* Reset channel to pick up new moderation value. Note that + * this may change the value of the irq_moderation field + * (e.g. to allow for hardware timer granularity). + */ + efx_for_each_channel(channel, efx) + falcon_set_int_moderation(channel); + + return 0; +} + +static int efx_ethtool_set_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *pause) +{ + struct efx_nic *efx = net_dev->priv; + enum efx_fc_type flow_control = efx->flow_control; + int rc; + + flow_control &= ~(EFX_FC_RX | EFX_FC_TX | EFX_FC_AUTO); + flow_control |= pause->rx_pause ? EFX_FC_RX : 0; + flow_control |= pause->tx_pause ? EFX_FC_TX : 0; + flow_control |= pause->autoneg ? EFX_FC_AUTO : 0; + + /* Try to push the pause parameters */ + mutex_lock(&efx->mac_lock); + rc = falcon_xmac_set_pause(efx, flow_control); + mutex_unlock(&efx->mac_lock); + + if (!rc) + efx_reconfigure_port(efx); + + return rc; +} + +static void efx_ethtool_get_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *pause) +{ + struct efx_nic *efx = net_dev->priv; + + pause->rx_pause = (efx->flow_control & EFX_FC_RX) ? 1 : 0; + pause->tx_pause = (efx->flow_control & EFX_FC_TX) ? 1 : 0; + pause->autoneg = (efx->flow_control & EFX_FC_AUTO) ? 1 : 0; +} + + +struct ethtool_ops efx_ethtool_ops = { + .get_settings = efx_ethtool_get_settings, + .set_settings = efx_ethtool_set_settings, + .get_drvinfo = efx_ethtool_get_drvinfo, + .nway_reset = efx_ethtool_nway_reset, + .get_link = efx_ethtool_get_link, + .get_coalesce = efx_ethtool_get_coalesce, + .set_coalesce = efx_ethtool_set_coalesce, + .get_pauseparam = efx_ethtool_get_pauseparam, + .set_pauseparam = efx_ethtool_set_pauseparam, + .get_rx_csum = efx_ethtool_get_rx_csum, + .set_rx_csum = efx_ethtool_set_rx_csum, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = efx_ethtool_set_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, + .get_flags = ethtool_op_get_flags, + .set_flags = ethtool_op_set_flags, + .get_strings = efx_ethtool_get_strings, + .phys_id = efx_ethtool_phys_id, + .get_stats_count = efx_ethtool_get_stats_count, + .get_ethtool_stats = efx_ethtool_get_stats, +}; diff --git a/drivers/net/sfc/ethtool.h b/drivers/net/sfc/ethtool.h new file mode 100644 index 00000000000..3628e43df14 --- /dev/null +++ b/drivers/net/sfc/ethtool.h @@ -0,0 +1,27 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005 Fen Systems Ltd. + * Copyright 2006 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_ETHTOOL_H +#define EFX_ETHTOOL_H + +#include "net_driver.h" + +/* + * Ethtool support + */ + +extern int efx_ethtool_get_settings(struct net_device *net_dev, + struct ethtool_cmd *ecmd); +extern int efx_ethtool_set_settings(struct net_device *net_dev, + struct ethtool_cmd *ecmd); + +extern struct ethtool_ops efx_ethtool_ops; + +#endif /* EFX_ETHTOOL_H */ diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c new file mode 100644 index 00000000000..46db549ce58 --- /dev/null +++ b/drivers/net/sfc/falcon.c @@ -0,0 +1,2722 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/module.h> +#include <linux/seq_file.h> +#include "net_driver.h" +#include "bitfield.h" +#include "efx.h" +#include "mac.h" +#include "gmii.h" +#include "spi.h" +#include "falcon.h" +#include "falcon_hwdefs.h" +#include "falcon_io.h" +#include "mdio_10g.h" +#include "phy.h" +#include "boards.h" +#include "workarounds.h" + +/* Falcon hardware control. + * Falcon is the internal codename for the SFC4000 controller that is + * present in SFE400X evaluation boards + */ + +/** + * struct falcon_nic_data - Falcon NIC state + * @next_buffer_table: First available buffer table id + * @pci_dev2: The secondary PCI device if present + */ +struct falcon_nic_data { + unsigned next_buffer_table; + struct pci_dev *pci_dev2; +}; + +/************************************************************************** + * + * Configurable values + * + ************************************************************************** + */ + +static int disable_dma_stats; + +/* This is set to 16 for a good reason. In summary, if larger than + * 16, the descriptor cache holds more than a default socket + * buffer's worth of packets (for UDP we can only have at most one + * socket buffer's worth outstanding). This combined with the fact + * that we only get 1 TX event per descriptor cache means the NIC + * goes idle. + */ +#define TX_DC_ENTRIES 16 +#define TX_DC_ENTRIES_ORDER 0 +#define TX_DC_BASE 0x130000 + +#define RX_DC_ENTRIES 64 +#define RX_DC_ENTRIES_ORDER 2 +#define RX_DC_BASE 0x100000 + +/* RX FIFO XOFF watermark + * + * When the amount of the RX FIFO increases used increases past this + * watermark send XOFF. Only used if RX flow control is enabled (ethtool -A) + * This also has an effect on RX/TX arbitration + */ +static int rx_xoff_thresh_bytes = -1; +module_param(rx_xoff_thresh_bytes, int, 0644); +MODULE_PARM_DESC(rx_xoff_thresh_bytes, "RX fifo XOFF threshold"); + +/* RX FIFO XON watermark + * + * When the amount of the RX FIFO used decreases below this + * watermark send XON. Only used if TX flow control is enabled (ethtool -A) + * This also has an effect on RX/TX arbitration + */ +static int rx_xon_thresh_bytes = -1; +module_param(rx_xon_thresh_bytes, int, 0644); +MODULE_PARM_DESC(rx_xon_thresh_bytes, "RX fifo XON threshold"); + +/* TX descriptor ring size - min 512 max 4k */ +#define FALCON_TXD_RING_ORDER TX_DESCQ_SIZE_1K +#define FALCON_TXD_RING_SIZE 1024 +#define FALCON_TXD_RING_MASK (FALCON_TXD_RING_SIZE - 1) + +/* RX descriptor ring size - min 512 max 4k */ +#define FALCON_RXD_RING_ORDER RX_DESCQ_SIZE_1K +#define FALCON_RXD_RING_SIZE 1024 +#define FALCON_RXD_RING_MASK (FALCON_RXD_RING_SIZE - 1) + +/* Event queue size - max 32k */ +#define FALCON_EVQ_ORDER EVQ_SIZE_4K +#define FALCON_EVQ_SIZE 4096 +#define FALCON_EVQ_MASK (FALCON_EVQ_SIZE - 1) + +/* Max number of internal errors. After this resets will not be performed */ +#define FALCON_MAX_INT_ERRORS 4 + +/* Maximum period that we wait for flush events. If the flush event + * doesn't arrive in this period of time then we check if the queue + * was disabled anyway. */ +#define FALCON_FLUSH_TIMEOUT 10 /* 10ms */ + +/************************************************************************** + * + * Falcon constants + * + ************************************************************************** + */ + +/* DMA address mask (up to 46-bit, avoiding compiler warnings) + * + * Note that it is possible to have a platform with 64-bit longs and + * 32-bit DMA addresses, or vice versa. EFX_DMA_MASK takes care of the + * platform DMA mask. + */ +#if BITS_PER_LONG == 64 +#define FALCON_DMA_MASK EFX_DMA_MASK(0x00003fffffffffffUL) +#else +#define FALCON_DMA_MASK EFX_DMA_MASK(0x00003fffffffffffULL) +#endif + +/* TX DMA length mask (13-bit) */ +#define FALCON_TX_DMA_MASK (4096 - 1) + +/* Size and alignment of special buffers (4KB) */ +#define FALCON_BUF_SIZE 4096 + +/* Dummy SRAM size code */ +#define SRM_NB_BSZ_ONCHIP_ONLY (-1) + +/* Be nice if these (or equiv.) were in linux/pci_regs.h, but they're not. */ +#define PCI_EXP_DEVCAP_PWR_VAL_LBN 18 +#define PCI_EXP_DEVCAP_PWR_SCL_LBN 26 +#define PCI_EXP_DEVCTL_PAYLOAD_LBN 5 +#define PCI_EXP_LNKSTA_LNK_WID 0x3f0 +#define PCI_EXP_LNKSTA_LNK_WID_LBN 4 + +#define FALCON_IS_DUAL_FUNC(efx) \ + (FALCON_REV(efx) < FALCON_REV_B0) + +/************************************************************************** + * + * Falcon hardware access + * + **************************************************************************/ + +/* Read the current event from the event queue */ +static inline efx_qword_t *falcon_event(struct efx_channel *channel, + unsigned int index) +{ + return (((efx_qword_t *) (channel->eventq.addr)) + index); +} + +/* See if an event is present + * + * We check both the high and low dword of the event for all ones. We + * wrote all ones when we cleared the event, and no valid event can + * have all ones in either its high or low dwords. This approach is + * robust against reordering. + * + * Note that using a single 64-bit comparison is incorrect; even + * though the CPU read will be atomic, the DMA write may not be. + */ +static inline int falcon_event_present(efx_qword_t *event) +{ + return (!(EFX_DWORD_IS_ALL_ONES(event->dword[0]) | + EFX_DWORD_IS_ALL_ONES(event->dword[1]))); +} + +/************************************************************************** + * + * I2C bus - this is a bit-bashing interface using GPIO pins + * Note that it uses the output enables to tristate the outputs + * SDA is the data pin and SCL is the clock + * + ************************************************************************** + */ +static void falcon_setsdascl(struct efx_i2c_interface *i2c) +{ + efx_oword_t reg; + + falcon_read(i2c->efx, ®, GPIO_CTL_REG_KER); + EFX_SET_OWORD_FIELD(reg, GPIO0_OEN, (i2c->scl ? 0 : 1)); + EFX_SET_OWORD_FIELD(reg, GPIO3_OEN, (i2c->sda ? 0 : 1)); + falcon_write(i2c->efx, ®, GPIO_CTL_REG_KER); +} + +static int falcon_getsda(struct efx_i2c_interface *i2c) +{ + efx_oword_t reg; + + falcon_read(i2c->efx, ®, GPIO_CTL_REG_KER); + return EFX_OWORD_FIELD(reg, GPIO3_IN); +} + +static int falcon_getscl(struct efx_i2c_interface *i2c) +{ + efx_oword_t reg; + + falcon_read(i2c->efx, ®, GPIO_CTL_REG_KER); + return EFX_DWORD_FIELD(reg, GPIO0_IN); +} + +static struct efx_i2c_bit_operations falcon_i2c_bit_operations = { + .setsda = falcon_setsdascl, + .setscl = falcon_setsdascl, + .getsda = falcon_getsda, + .getscl = falcon_getscl, + .udelay = 100, + .mdelay = 10, +}; + +/************************************************************************** + * + * Falcon special buffer handling + * Special buffers are used for event queues and the TX and RX + * descriptor rings. + * + *************************************************************************/ + +/* + * Initialise a Falcon special buffer + * + * This will define a buffer (previously allocated via + * falcon_alloc_special_buffer()) in Falcon's buffer table, allowing + * it to be used for event queues, descriptor rings etc. + */ +static int +falcon_init_special_buffer(struct efx_nic *efx, + struct efx_special_buffer *buffer) +{ + efx_qword_t buf_desc; + int index; + dma_addr_t dma_addr; + int i; + + EFX_BUG_ON_PARANOID(!buffer->addr); + + /* Write buffer descriptors to NIC */ + for (i = 0; i < buffer->entries; i++) { + index = buffer->index + i; + dma_addr = buffer->dma_addr + (i * 4096); + EFX_LOG(efx, "mapping special buffer %d at %llx\n", + index, (unsigned long long)dma_addr); + EFX_POPULATE_QWORD_4(buf_desc, + IP_DAT_BUF_SIZE, IP_DAT_BUF_SIZE_4K, + BUF_ADR_REGION, 0, + BUF_ADR_FBUF, (dma_addr >> 12), + BUF_OWNER_ID_FBUF, 0); + falcon_write_sram(efx, &buf_desc, index); + } + + return 0; +} + +/* Unmaps a buffer from Falcon and clears the buffer table entries */ +static void +falcon_fini_special_buffer(struct efx_nic *efx, + struct efx_special_buffer *buffer) +{ + efx_oword_t buf_tbl_upd; + unsigned int start = buffer->index; + unsigned int end = (buffer->index + buffer->entries - 1); + + if (!buffer->entries) + return; + + EFX_LOG(efx, "unmapping special buffers %d-%d\n", + buffer->index, buffer->index + buffer->entries - 1); + + EFX_POPULATE_OWORD_4(buf_tbl_upd, + BUF_UPD_CMD, 0, + BUF_CLR_CMD, 1, + BUF_CLR_END_ID, end, + BUF_CLR_START_ID, start); + falcon_write(efx, &buf_tbl_upd, BUF_TBL_UPD_REG_KER); +} + +/* + * Allocate a new Falcon special buffer + * + * This allocates memory for a new buffer, clears it and allocates a + * new buffer ID range. It does not write into Falcon's buffer table. + * + * This call will allocate 4KB buffers, since Falcon can't use 8KB + * buffers for event queues and descriptor rings. + */ +static int falcon_alloc_special_buffer(struct efx_nic *efx, + struct efx_special_buffer *buffer, + unsigned int len) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + + len = ALIGN(len, FALCON_BUF_SIZE); + + buffer->addr = pci_alloc_consistent(efx->pci_dev, len, + &buffer->dma_addr); + if (!buffer->addr) + return -ENOMEM; + buffer->len = len; + buffer->entries = len / FALCON_BUF_SIZE; + BUG_ON(buffer->dma_addr & (FALCON_BUF_SIZE - 1)); + + /* All zeros is a potentially valid event so memset to 0xff */ + memset(buffer->addr, 0xff, len); + + /* Select new buffer ID */ + buffer->index = nic_data->next_buffer_table; + nic_data->next_buffer_table += buffer->entries; + + EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x " + "(virt %p phys %lx)\n", buffer->index, + buffer->index + buffer->entries - 1, + (unsigned long long)buffer->dma_addr, len, + buffer->addr, virt_to_phys(buffer->addr)); + + return 0; +} + +static void falcon_free_special_buffer(struct efx_nic *efx, + struct efx_special_buffer *buffer) +{ + if (!buffer->addr) + return; + + EFX_LOG(efx, "deallocating special buffers %d-%d at %llx+%x " + "(virt %p phys %lx)\n", buffer->index, + buffer->index + buffer->entries - 1, + (unsigned long long)buffer->dma_addr, buffer->len, + buffer->addr, virt_to_phys(buffer->addr)); + + pci_free_consistent(efx->pci_dev, buffer->len, buffer->addr, + buffer->dma_addr); + buffer->addr = NULL; + buffer->entries = 0; +} + +/************************************************************************** + * + * Falcon generic buffer handling + * These buffers are used for interrupt status and MAC stats + * + **************************************************************************/ + +static int falcon_alloc_buffer(struct efx_nic *efx, + struct efx_buffer *buffer, unsigned int len) +{ + buffer->addr = pci_alloc_consistent(efx->pci_dev, len, + &buffer->dma_addr); + if (!buffer->addr) + return -ENOMEM; + buffer->len = len; + memset(buffer->addr, 0, len); + return 0; +} + +static void falcon_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer) +{ + if (buffer->addr) { + pci_free_consistent(efx->pci_dev, buffer->len, + buffer->addr, buffer->dma_addr); + buffer->addr = NULL; + } +} + +/************************************************************************** + * + * Falcon TX path + * + **************************************************************************/ + +/* Returns a pointer to the specified transmit descriptor in the TX + * descriptor queue belonging to the specified channel. + */ +static inline efx_qword_t *falcon_tx_desc(struct efx_tx_queue *tx_queue, + unsigned int index) +{ + return (((efx_qword_t *) (tx_queue->txd.addr)) + index); +} + +/* This writes to the TX_DESC_WPTR; write pointer for TX descriptor ring */ +static inline void falcon_notify_tx_desc(struct efx_tx_queue *tx_queue) +{ + unsigned write_ptr; + efx_dword_t reg; + + write_ptr = tx_queue->write_count & FALCON_TXD_RING_MASK; + EFX_POPULATE_DWORD_1(reg, TX_DESC_WPTR_DWORD, write_ptr); + falcon_writel_page(tx_queue->efx, ®, + TX_DESC_UPD_REG_KER_DWORD, tx_queue->queue); +} + + +/* For each entry inserted into the software descriptor ring, create a + * descriptor in the hardware TX descriptor ring (in host memory), and + * write a doorbell. + */ +void falcon_push_buffers(struct efx_tx_queue *tx_queue) +{ + + struct efx_tx_buffer *buffer; + efx_qword_t *txd; + unsigned write_ptr; + + BUG_ON(tx_queue->write_count == tx_queue->insert_count); + + do { + write_ptr = tx_queue->write_count & FALCON_TXD_RING_MASK; + buffer = &tx_queue->buffer[write_ptr]; + txd = falcon_tx_desc(tx_queue, write_ptr); + ++tx_queue->write_count; + + /* Create TX descriptor ring entry */ + EFX_POPULATE_QWORD_5(*txd, + TX_KER_PORT, 0, + TX_KER_CONT, buffer->continuation, + TX_KER_BYTE_CNT, buffer->len, + TX_KER_BUF_REGION, 0, + TX_KER_BUF_ADR, buffer->dma_addr); + } while (tx_queue->write_count != tx_queue->insert_count); + + wmb(); /* Ensure descriptors are written before they are fetched */ + falcon_notify_tx_desc(tx_queue); +} + +/* Allocate hardware resources for a TX queue */ +int falcon_probe_tx(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + return falcon_alloc_special_buffer(efx, &tx_queue->txd, + FALCON_TXD_RING_SIZE * + sizeof(efx_qword_t)); +} + +int falcon_init_tx(struct efx_tx_queue *tx_queue) +{ + efx_oword_t tx_desc_ptr; + struct efx_nic *efx = tx_queue->efx; + int rc; + + /* Pin TX descriptor ring */ + rc = falcon_init_special_buffer(efx, &tx_queue->txd); + if (rc) + return rc; + + /* Push TX descriptor ring to card */ + EFX_POPULATE_OWORD_10(tx_desc_ptr, + TX_DESCQ_EN, 1, + TX_ISCSI_DDIG_EN, 0, + TX_ISCSI_HDIG_EN, 0, + TX_DESCQ_BUF_BASE_ID, tx_queue->txd.index, + TX_DESCQ_EVQ_ID, tx_queue->channel->evqnum, + TX_DESCQ_OWNER_ID, 0, + TX_DESCQ_LABEL, tx_queue->queue, + TX_DESCQ_SIZE, FALCON_TXD_RING_ORDER, + TX_DESCQ_TYPE, 0, + TX_NON_IP_DROP_DIS_B0, 1); + + if (FALCON_REV(efx) >= FALCON_REV_B0) { + int csum = !(efx->net_dev->features & NETIF_F_IP_CSUM); + EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_IP_CHKSM_DIS_B0, csum); + EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_TCP_CHKSM_DIS_B0, csum); + } + + falcon_write_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, + tx_queue->queue); + + if (FALCON_REV(efx) < FALCON_REV_B0) { + efx_oword_t reg; + + BUG_ON(tx_queue->queue >= 128); /* HW limit */ + + falcon_read(efx, ®, TX_CHKSM_CFG_REG_KER_A1); + if (efx->net_dev->features & NETIF_F_IP_CSUM) + clear_bit_le(tx_queue->queue, (void *)®); + else + set_bit_le(tx_queue->queue, (void *)®); + falcon_write(efx, ®, TX_CHKSM_CFG_REG_KER_A1); + } + + return 0; +} + +static int falcon_flush_tx_queue(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + struct efx_channel *channel = &efx->channel[0]; + efx_oword_t tx_flush_descq; + unsigned int read_ptr, i; + + /* Post a flush command */ + EFX_POPULATE_OWORD_2(tx_flush_descq, + TX_FLUSH_DESCQ_CMD, 1, + TX_FLUSH_DESCQ, tx_queue->queue); + falcon_write(efx, &tx_flush_descq, TX_FLUSH_DESCQ_REG_KER); + msleep(FALCON_FLUSH_TIMEOUT); + + if (EFX_WORKAROUND_7803(efx)) + return 0; + + /* Look for a flush completed event */ + read_ptr = channel->eventq_read_ptr; + for (i = 0; i < FALCON_EVQ_SIZE; ++i) { + efx_qword_t *event = falcon_event(channel, read_ptr); + int ev_code, ev_sub_code, ev_queue; + if (!falcon_event_present(event)) + break; + + ev_code = EFX_QWORD_FIELD(*event, EV_CODE); + ev_sub_code = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_CODE); + ev_queue = EFX_QWORD_FIELD(*event, DRIVER_EV_TX_DESCQ_ID); + if ((ev_sub_code == TX_DESCQ_FLS_DONE_EV_DECODE) && + (ev_queue == tx_queue->queue)) { + EFX_LOG(efx, "tx queue %d flush command succesful\n", + tx_queue->queue); + return 0; + } + + read_ptr = (read_ptr + 1) & FALCON_EVQ_MASK; + } + + if (EFX_WORKAROUND_11557(efx)) { + efx_oword_t reg; + int enabled; + + falcon_read_table(efx, ®, efx->type->txd_ptr_tbl_base, + tx_queue->queue); + enabled = EFX_OWORD_FIELD(reg, TX_DESCQ_EN); + if (!enabled) { + EFX_LOG(efx, "tx queue %d disabled without a " + "flush event seen\n", tx_queue->queue); + return 0; + } + } + + EFX_ERR(efx, "tx queue %d flush command timed out\n", tx_queue->queue); + return -ETIMEDOUT; +} + +void falcon_fini_tx(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + efx_oword_t tx_desc_ptr; + + /* Stop the hardware using the queue */ + if (falcon_flush_tx_queue(tx_queue)) + EFX_ERR(efx, "failed to flush tx queue %d\n", tx_queue->queue); + + /* Remove TX descriptor ring from card */ + EFX_ZERO_OWORD(tx_desc_ptr); + falcon_write_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, + tx_queue->queue); + + /* Unpin TX descriptor ring */ + falcon_fini_special_buffer(efx, &tx_queue->txd); +} + +/* Free buffers backing TX queue */ +void falcon_remove_tx(struct efx_tx_queue *tx_queue) +{ + falcon_free_special_buffer(tx_queue->efx, &tx_queue->txd); +} + +/************************************************************************** + * + * Falcon RX path + * + **************************************************************************/ + +/* Returns a pointer to the specified descriptor in the RX descriptor queue */ +static inline efx_qword_t *falcon_rx_desc(struct efx_rx_queue *rx_queue, + unsigned int index) +{ + return (((efx_qword_t *) (rx_queue->rxd.addr)) + index); +} + +/* This creates an entry in the RX descriptor queue */ +static inline void falcon_build_rx_desc(struct efx_rx_queue *rx_queue, + unsigned index) +{ + struct efx_rx_buffer *rx_buf; + efx_qword_t *rxd; + + rxd = falcon_rx_desc(rx_queue, index); + rx_buf = efx_rx_buffer(rx_queue, index); + EFX_POPULATE_QWORD_3(*rxd, + RX_KER_BUF_SIZE, + rx_buf->len - + rx_queue->efx->type->rx_buffer_padding, + RX_KER_BUF_REGION, 0, + RX_KER_BUF_ADR, rx_buf->dma_addr); +} + +/* This writes to the RX_DESC_WPTR register for the specified receive + * descriptor ring. + */ +void falcon_notify_rx_desc(struct efx_rx_queue *rx_queue) +{ + efx_dword_t reg; + unsigned write_ptr; + + while (rx_queue->notified_count != rx_queue->added_count) { + falcon_build_rx_desc(rx_queue, + rx_queue->notified_count & + FALCON_RXD_RING_MASK); + ++rx_queue->notified_count; + } + + wmb(); + write_ptr = rx_queue->added_count & FALCON_RXD_RING_MASK; + EFX_POPULATE_DWORD_1(reg, RX_DESC_WPTR_DWORD, write_ptr); + falcon_writel_page(rx_queue->efx, ®, + RX_DESC_UPD_REG_KER_DWORD, rx_queue->queue); +} + +int falcon_probe_rx(struct efx_rx_queue *rx_queue) +{ + struct efx_nic *efx = rx_queue->efx; + return falcon_alloc_special_buffer(efx, &rx_queue->rxd, + FALCON_RXD_RING_SIZE * + sizeof(efx_qword_t)); +} + +int falcon_init_rx(struct efx_rx_queue *rx_queue) +{ + efx_oword_t rx_desc_ptr; + struct efx_nic *efx = rx_queue->efx; + int rc; + int is_b0 = FALCON_REV(efx) >= FALCON_REV_B0; + int iscsi_digest_en = is_b0; + + EFX_LOG(efx, "RX queue %d ring in special buffers %d-%d\n", + rx_queue->queue, rx_queue->rxd.index, + rx_queue->rxd.index + rx_queue->rxd.entries - 1); + + /* Pin RX descriptor ring */ + rc = falcon_init_special_buffer(efx, &rx_queue->rxd); + if (rc) + return rc; + + /* Push RX descriptor ring to card */ + EFX_POPULATE_OWORD_10(rx_desc_ptr, + RX_ISCSI_DDIG_EN, iscsi_digest_en, + RX_ISCSI_HDIG_EN, iscsi_digest_en, + RX_DESCQ_BUF_BASE_ID, rx_queue->rxd.index, + RX_DESCQ_EVQ_ID, rx_queue->channel->evqnum, + RX_DESCQ_OWNER_ID, 0, + RX_DESCQ_LABEL, rx_queue->queue, + RX_DESCQ_SIZE, FALCON_RXD_RING_ORDER, + RX_DESCQ_TYPE, 0 /* kernel queue */ , + /* For >=B0 this is scatter so disable */ + RX_DESCQ_JUMBO, !is_b0, + RX_DESCQ_EN, 1); + falcon_write_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, + rx_queue->queue); + return 0; +} + +static int falcon_flush_rx_queue(struct efx_rx_queue *rx_queue) +{ + struct efx_nic *efx = rx_queue->efx; + struct efx_channel *channel = &efx->channel[0]; + unsigned int read_ptr, i; + efx_oword_t rx_flush_descq; + + /* Post a flush command */ + EFX_POPULATE_OWORD_2(rx_flush_descq, + RX_FLUSH_DESCQ_CMD, 1, + RX_FLUSH_DESCQ, rx_queue->queue); + falcon_write(efx, &rx_flush_descq, RX_FLUSH_DESCQ_REG_KER); + msleep(FALCON_FLUSH_TIMEOUT); + + if (EFX_WORKAROUND_7803(efx)) + return 0; + + /* Look for a flush completed event */ + read_ptr = channel->eventq_read_ptr; + for (i = 0; i < FALCON_EVQ_SIZE; ++i) { + efx_qword_t *event = falcon_event(channel, read_ptr); + int ev_code, ev_sub_code, ev_queue, ev_failed; + if (!falcon_event_present(event)) + break; + + ev_code = EFX_QWORD_FIELD(*event, EV_CODE); + ev_sub_code = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_CODE); + ev_queue = EFX_QWORD_FIELD(*event, DRIVER_EV_RX_DESCQ_ID); + ev_failed = EFX_QWORD_FIELD(*event, DRIVER_EV_RX_FLUSH_FAIL); + + if ((ev_sub_code == RX_DESCQ_FLS_DONE_EV_DECODE) && + (ev_queue == rx_queue->queue)) { + if (ev_failed) { + EFX_INFO(efx, "rx queue %d flush command " + "failed\n", rx_queue->queue); + return -EAGAIN; + } else { + EFX_LOG(efx, "rx queue %d flush command " + "succesful\n", rx_queue->queue); + return 0; + } + } + + read_ptr = (read_ptr + 1) & FALCON_EVQ_MASK; + } + + if (EFX_WORKAROUND_11557(efx)) { + efx_oword_t reg; + int enabled; + + falcon_read_table(efx, ®, efx->type->rxd_ptr_tbl_base, + rx_queue->queue); + enabled = EFX_OWORD_FIELD(reg, RX_DESCQ_EN); + if (!enabled) { + EFX_LOG(efx, "rx queue %d disabled without a " + "flush event seen\n", rx_queue->queue); + return 0; + } + } + + EFX_ERR(efx, "rx queue %d flush command timed out\n", rx_queue->queue); + return -ETIMEDOUT; +} + +void falcon_fini_rx(struct efx_rx_queue *rx_queue) +{ + efx_oword_t rx_desc_ptr; + struct efx_nic *efx = rx_queue->efx; + int i, rc; + + /* Try and flush the rx queue. This may need to be repeated */ + for (i = 0; i < 5; i++) { + rc = falcon_flush_rx_queue(rx_queue); + if (rc == -EAGAIN) + continue; + break; + } + if (rc) + EFX_ERR(efx, "failed to flush rx queue %d\n", rx_queue->queue); + + /* Remove RX descriptor ring from card */ + EFX_ZERO_OWORD(rx_desc_ptr); + falcon_write_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, + rx_queue->queue); + + /* Unpin RX descriptor ring */ + falcon_fini_special_buffer(efx, &rx_queue->rxd); +} + +/* Free buffers backing RX queue */ +void falcon_remove_rx(struct efx_rx_queue *rx_queue) +{ + falcon_free_special_buffer(rx_queue->efx, &rx_queue->rxd); +} + +/************************************************************************** + * + * Falcon event queue processing + * Event queues are processed by per-channel tasklets. + * + **************************************************************************/ + +/* Update a channel's event queue's read pointer (RPTR) register + * + * This writes the EVQ_RPTR_REG register for the specified channel's + * event queue. + * + * Note that EVQ_RPTR_REG contains the index of the "last read" event, + * whereas channel->eventq_read_ptr contains the index of the "next to + * read" event. + */ +void falcon_eventq_read_ack(struct efx_channel *channel) +{ + efx_dword_t reg; + struct efx_nic *efx = channel->efx; + + EFX_POPULATE_DWORD_1(reg, EVQ_RPTR_DWORD, channel->eventq_read_ptr); + falcon_writel_table(efx, ®, efx->type->evq_rptr_tbl_base, + channel->evqnum); +} + +/* Use HW to insert a SW defined event */ +void falcon_generate_event(struct efx_channel *channel, efx_qword_t *event) +{ + efx_oword_t drv_ev_reg; + + EFX_POPULATE_OWORD_2(drv_ev_reg, + DRV_EV_QID, channel->evqnum, + DRV_EV_DATA, + EFX_QWORD_FIELD64(*event, WHOLE_EVENT)); + falcon_write(channel->efx, &drv_ev_reg, DRV_EV_REG_KER); +} + +/* Handle a transmit completion event + * + * Falcon batches TX completion events; the message we receive is of + * the form "complete all TX events up to this index". + */ +static inline void falcon_handle_tx_event(struct efx_channel *channel, + efx_qword_t *event) +{ + unsigned int tx_ev_desc_ptr; + unsigned int tx_ev_q_label; + struct efx_tx_queue *tx_queue; + struct efx_nic *efx = channel->efx; + + if (likely(EFX_QWORD_FIELD(*event, TX_EV_COMP))) { + /* Transmit completion */ + tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, TX_EV_DESC_PTR); + tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL); + tx_queue = &efx->tx_queue[tx_ev_q_label]; + efx_xmit_done(tx_queue, tx_ev_desc_ptr); + } else if (EFX_QWORD_FIELD(*event, TX_EV_WQ_FF_FULL)) { + /* Rewrite the FIFO write pointer */ + tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL); + tx_queue = &efx->tx_queue[tx_ev_q_label]; + + if (NET_DEV_REGISTERED(efx)) + netif_tx_lock(efx->net_dev); + falcon_notify_tx_desc(tx_queue); + if (NET_DEV_REGISTERED(efx)) + netif_tx_unlock(efx->net_dev); + } else if (EFX_QWORD_FIELD(*event, TX_EV_PKT_ERR) && + EFX_WORKAROUND_10727(efx)) { + efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); + } else { + EFX_ERR(efx, "channel %d unexpected TX event " + EFX_QWORD_FMT"\n", channel->channel, + EFX_QWORD_VAL(*event)); + } +} + +/* Check received packet's destination MAC address. */ +static int check_dest_mac(struct efx_rx_queue *rx_queue, + const efx_qword_t *event) +{ + struct efx_rx_buffer *rx_buf; + struct efx_nic *efx = rx_queue->efx; + int rx_ev_desc_ptr; + struct ethhdr *eh; + + if (efx->promiscuous) + return 1; + + rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, RX_EV_DESC_PTR); + rx_buf = efx_rx_buffer(rx_queue, rx_ev_desc_ptr); + eh = (struct ethhdr *)rx_buf->data; + if (memcmp(eh->h_dest, efx->net_dev->dev_addr, ETH_ALEN)) + return 0; + return 1; +} + +/* Detect errors included in the rx_evt_pkt_ok bit. */ +static void falcon_handle_rx_not_ok(struct efx_rx_queue *rx_queue, + const efx_qword_t *event, + unsigned *rx_ev_pkt_ok, + int *discard, int byte_count) +{ + struct efx_nic *efx = rx_queue->efx; + unsigned rx_ev_buf_owner_id_err, rx_ev_ip_hdr_chksum_err; + unsigned rx_ev_tcp_udp_chksum_err, rx_ev_eth_crc_err; + unsigned rx_ev_frm_trunc, rx_ev_drib_nib, rx_ev_tobe_disc; + unsigned rx_ev_pkt_type, rx_ev_other_err, rx_ev_pause_frm; + unsigned rx_ev_ip_frag_err, rx_ev_hdr_type, rx_ev_mcast_pkt; + int snap, non_ip; + + rx_ev_hdr_type = EFX_QWORD_FIELD(*event, RX_EV_HDR_TYPE); + rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, RX_EV_MCAST_PKT); + rx_ev_tobe_disc = EFX_QWORD_FIELD(*event, RX_EV_TOBE_DISC); + rx_ev_pkt_type = EFX_QWORD_FIELD(*event, RX_EV_PKT_TYPE); + rx_ev_buf_owner_id_err = EFX_QWORD_FIELD(*event, + RX_EV_BUF_OWNER_ID_ERR); + rx_ev_ip_frag_err = EFX_QWORD_FIELD(*event, RX_EV_IF_FRAG_ERR); + rx_ev_ip_hdr_chksum_err = EFX_QWORD_FIELD(*event, + RX_EV_IP_HDR_CHKSUM_ERR); + rx_ev_tcp_udp_chksum_err = EFX_QWORD_FIELD(*event, + RX_EV_TCP_UDP_CHKSUM_ERR); + rx_ev_eth_crc_err = EFX_QWORD_FIELD(*event, RX_EV_ETH_CRC_ERR); + rx_ev_frm_trunc = EFX_QWORD_FIELD(*event, RX_EV_FRM_TRUNC); + rx_ev_drib_nib = ((FALCON_REV(efx) >= FALCON_REV_B0) ? + 0 : EFX_QWORD_FIELD(*event, RX_EV_DRIB_NIB)); + rx_ev_pause_frm = EFX_QWORD_FIELD(*event, RX_EV_PAUSE_FRM_ERR); + + /* Every error apart from tobe_disc and pause_frm */ + rx_ev_other_err = (rx_ev_drib_nib | rx_ev_tcp_udp_chksum_err | + rx_ev_buf_owner_id_err | rx_ev_eth_crc_err | + rx_ev_frm_trunc | rx_ev_ip_hdr_chksum_err); + + snap = (rx_ev_pkt_type == RX_EV_PKT_TYPE_LLC_DECODE) || + (rx_ev_pkt_type == RX_EV_PKT_TYPE_VLAN_LLC_DECODE); + non_ip = (rx_ev_hdr_type == RX_EV_HDR_TYPE_NON_IP_DECODE); + + /* SFC bug 5475/8970: The Falcon XMAC incorrectly calculates the + * length field of an LLC frame, which sets TOBE_DISC. We could set + * PASS_LEN_ERR, but we want the MAC to filter out short frames (to + * protect the RX block). + * + * bug5475 - LLC/SNAP: Falcon identifies SNAP packets. + * bug8970 - LLC/noSNAP: Falcon does not provide an LLC flag. + * LLC can't encapsulate IP, so by definition + * these packets are NON_IP. + * + * Unicast mismatch will also cause TOBE_DISC, so the driver needs + * to check this. + */ + if (EFX_WORKAROUND_5475(efx) && rx_ev_tobe_disc && (snap || non_ip)) { + /* If all the other flags are zero then we can state the + * entire packet is ok, which will flag to the kernel not + * to recalculate checksums. + */ + if (!(non_ip | rx_ev_other_err | rx_ev_pause_frm)) + *rx_ev_pkt_ok = 1; + + rx_ev_tobe_disc = 0; + + /* TOBE_DISC is set for unicast mismatch. But given that + * we can't trust TOBE_DISC here, we must validate the dest + * MAC address ourselves. + */ + if (!rx_ev_mcast_pkt && !check_dest_mac(rx_queue, event)) + rx_ev_tobe_disc = 1; + } + + /* Count errors that are not in MAC stats. */ + if (rx_ev_frm_trunc) + ++rx_queue->channel->n_rx_frm_trunc; + else if (rx_ev_tobe_disc) + ++rx_queue->channel->n_rx_tobe_disc; + else if (rx_ev_ip_hdr_chksum_err) + ++rx_queue->channel->n_rx_ip_hdr_chksum_err; + else if (rx_ev_tcp_udp_chksum_err) + ++rx_queue->channel->n_rx_tcp_udp_chksum_err; + if (rx_ev_ip_frag_err) + ++rx_queue->channel->n_rx_ip_frag_err; + + /* The frame must be discarded if any of these are true. */ + *discard = (rx_ev_eth_crc_err | rx_ev_frm_trunc | rx_ev_drib_nib | + rx_ev_tobe_disc | rx_ev_pause_frm); + + /* TOBE_DISC is expected on unicast mismatches; don't print out an + * error message. FRM_TRUNC indicates RXDP dropped the packet due + * to a FIFO overflow. + */ +#ifdef EFX_ENABLE_DEBUG + if (rx_ev_other_err) { + EFX_INFO_RL(efx, " RX queue %d unexpected RX event " + EFX_QWORD_FMT "%s%s%s%s%s%s%s%s%s\n", + rx_queue->queue, EFX_QWORD_VAL(*event), + rx_ev_buf_owner_id_err ? " [OWNER_ID_ERR]" : "", + rx_ev_ip_hdr_chksum_err ? + " [IP_HDR_CHKSUM_ERR]" : "", + rx_ev_tcp_udp_chksum_err ? + " [TCP_UDP_CHKSUM_ERR]" : "", + rx_ev_eth_crc_err ? " [ETH_CRC_ERR]" : "", + rx_ev_frm_trunc ? " [FRM_TRUNC]" : "", + rx_ev_drib_nib ? " [DRIB_NIB]" : "", + rx_ev_tobe_disc ? " [TOBE_DISC]" : "", + rx_ev_pause_frm ? " [PAUSE]" : "", + snap ? " [SNAP/LLC]" : ""); + } +#endif + + if (unlikely(rx_ev_eth_crc_err && EFX_WORKAROUND_10750(efx) && + efx->phy_type == PHY_TYPE_10XPRESS)) + tenxpress_crc_err(efx); +} + +/* Handle receive events that are not in-order. */ +static void falcon_handle_rx_bad_index(struct efx_rx_queue *rx_queue, + unsigned index) +{ + struct efx_nic *efx = rx_queue->efx; + unsigned expected, dropped; + + expected = rx_queue->removed_count & FALCON_RXD_RING_MASK; + dropped = ((index + FALCON_RXD_RING_SIZE - expected) & + FALCON_RXD_RING_MASK); + EFX_INFO(efx, "dropped %d events (index=%d expected=%d)\n", + dropped, index, expected); + + efx_schedule_reset(efx, EFX_WORKAROUND_5676(efx) ? + RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); +} + +/* Handle a packet received event + * + * Falcon silicon gives a "discard" flag if it's a unicast packet with the + * wrong destination address + * Also "is multicast" and "matches multicast filter" flags can be used to + * discard non-matching multicast packets. + */ +static inline int falcon_handle_rx_event(struct efx_channel *channel, + const efx_qword_t *event) +{ + unsigned int rx_ev_q_label, rx_ev_desc_ptr, rx_ev_byte_cnt; + unsigned int rx_ev_pkt_ok, rx_ev_hdr_type, rx_ev_mcast_pkt; + unsigned expected_ptr; + int discard = 0, checksummed; + struct efx_rx_queue *rx_queue; + struct efx_nic *efx = channel->efx; + + /* Basic packet information */ + rx_ev_byte_cnt = EFX_QWORD_FIELD(*event, RX_EV_BYTE_CNT); + rx_ev_pkt_ok = EFX_QWORD_FIELD(*event, RX_EV_PKT_OK); + rx_ev_hdr_type = EFX_QWORD_FIELD(*event, RX_EV_HDR_TYPE); + WARN_ON(EFX_QWORD_FIELD(*event, RX_EV_JUMBO_CONT)); + WARN_ON(EFX_QWORD_FIELD(*event, RX_EV_SOP) != 1); + + rx_ev_q_label = EFX_QWORD_FIELD(*event, RX_EV_Q_LABEL); + rx_queue = &efx->rx_queue[rx_ev_q_label]; + + rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, RX_EV_DESC_PTR); + expected_ptr = rx_queue->removed_count & FALCON_RXD_RING_MASK; + if (unlikely(rx_ev_desc_ptr != expected_ptr)) { + falcon_handle_rx_bad_index(rx_queue, rx_ev_desc_ptr); + return rx_ev_q_label; + } + + if (likely(rx_ev_pkt_ok)) { + /* If packet is marked as OK and packet type is TCP/IPv4 or + * UDP/IPv4, then we can rely on the hardware checksum. + */ + checksummed = RX_EV_HDR_TYPE_HAS_CHECKSUMS(rx_ev_hdr_type); + } else { + falcon_handle_rx_not_ok(rx_queue, event, &rx_ev_pkt_ok, + &discard, rx_ev_byte_cnt); + checksummed = 0; + } + + /* Detect multicast packets that didn't match the filter */ + rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, RX_EV_MCAST_PKT); + if (rx_ev_mcast_pkt) { + unsigned int rx_ev_mcast_hash_match = + EFX_QWORD_FIELD(*event, RX_EV_MCAST_HASH_MATCH); + + if (unlikely(!rx_ev_mcast_hash_match)) + discard = 1; + } + + /* Handle received packet */ + efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt, + checksummed, discard); + + return rx_ev_q_label; +} + +/* Global events are basically PHY events */ +static void falcon_handle_global_event(struct efx_channel *channel, + efx_qword_t *event) +{ + struct efx_nic *efx = channel->efx; + int is_phy_event = 0, handled = 0; + + /* Check for interrupt on either port. Some boards have a + * single PHY wired to the interrupt line for port 1. */ + if (EFX_QWORD_FIELD(*event, G_PHY0_INTR) || + EFX_QWORD_FIELD(*event, G_PHY1_INTR) || + EFX_QWORD_FIELD(*event, XG_PHY_INTR)) + is_phy_event = 1; + + if ((FALCON_REV(efx) >= FALCON_REV_B0) && + EFX_OWORD_FIELD(*event, XG_MNT_INTR_B0)) + is_phy_event = 1; + + if (is_phy_event) { + efx->phy_op->clear_interrupt(efx); + queue_work(efx->workqueue, &efx->reconfigure_work); + handled = 1; + } + + if (EFX_QWORD_FIELD_VER(efx, *event, RX_RECOVERY)) { + EFX_ERR(efx, "channel %d seen global RX_RESET " + "event. Resetting.\n", channel->channel); + + atomic_inc(&efx->rx_reset); + efx_schedule_reset(efx, EFX_WORKAROUND_6555(efx) ? + RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); + handled = 1; + } + + if (!handled) + EFX_ERR(efx, "channel %d unknown global event " + EFX_QWORD_FMT "\n", channel->channel, + EFX_QWORD_VAL(*event)); +} + +static void falcon_handle_driver_event(struct efx_channel *channel, + efx_qword_t *event) +{ + struct efx_nic *efx = channel->efx; + unsigned int ev_sub_code; + unsigned int ev_sub_data; + + ev_sub_code = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_CODE); + ev_sub_data = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_DATA); + + switch (ev_sub_code) { + case TX_DESCQ_FLS_DONE_EV_DECODE: + EFX_TRACE(efx, "channel %d TXQ %d flushed\n", + channel->channel, ev_sub_data); + break; + case RX_DESCQ_FLS_DONE_EV_DECODE: + EFX_TRACE(efx, "channel %d RXQ %d flushed\n", + channel->channel, ev_sub_data); + break; + case EVQ_INIT_DONE_EV_DECODE: + EFX_LOG(efx, "channel %d EVQ %d initialised\n", + channel->channel, ev_sub_data); + break; + case SRM_UPD_DONE_EV_DECODE: + EFX_TRACE(efx, "channel %d SRAM update done\n", + channel->channel); + break; + case WAKE_UP_EV_DECODE: + EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n", + channel->channel, ev_sub_data); + break; + case TIMER_EV_DECODE: + EFX_TRACE(efx, "channel %d RX queue %d timer expired\n", + channel->channel, ev_sub_data); + break; + case RX_RECOVERY_EV_DECODE: + EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. " + "Resetting.\n", channel->channel); + efx_schedule_reset(efx, + EFX_WORKAROUND_6555(efx) ? + RESET_TYPE_RX_RECOVERY : + RESET_TYPE_DISABLE); + break; + case RX_DSC_ERROR_EV_DECODE: + EFX_ERR(efx, "RX DMA Q %d reports descriptor fetch error." + " RX Q %d is disabled.\n", ev_sub_data, ev_sub_data); + efx_schedule_reset(efx, RESET_TYPE_RX_DESC_FETCH); + break; + case TX_DSC_ERROR_EV_DECODE: + EFX_ERR(efx, "TX DMA Q %d reports descriptor fetch error." + " TX Q %d is disabled.\n", ev_sub_data, ev_sub_data); + efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); + break; + default: + EFX_TRACE(efx, "channel %d unknown driver event code %d " + "data %04x\n", channel->channel, ev_sub_code, + ev_sub_data); + break; + } +} + +int falcon_process_eventq(struct efx_channel *channel, int *rx_quota) +{ + unsigned int read_ptr; + efx_qword_t event, *p_event; + int ev_code; + int rxq; + int rxdmaqs = 0; + + read_ptr = channel->eventq_read_ptr; + + do { + p_event = falcon_event(channel, read_ptr); + event = *p_event; + + if (!falcon_event_present(&event)) + /* End of events */ + break; + + EFX_TRACE(channel->efx, "channel %d event is "EFX_QWORD_FMT"\n", + channel->channel, EFX_QWORD_VAL(event)); + + /* Clear this event by marking it all ones */ + EFX_SET_QWORD(*p_event); + + ev_code = EFX_QWORD_FIELD(event, EV_CODE); + + switch (ev_code) { + case RX_IP_EV_DECODE: + rxq = falcon_handle_rx_event(channel, &event); + rxdmaqs |= (1 << rxq); + (*rx_quota)--; + break; + case TX_IP_EV_DECODE: + falcon_handle_tx_event(channel, &event); + break; + case DRV_GEN_EV_DECODE: + channel->eventq_magic + = EFX_QWORD_FIELD(event, EVQ_MAGIC); + EFX_LOG(channel->efx, "channel %d received generated " + "event "EFX_QWORD_FMT"\n", channel->channel, + EFX_QWORD_VAL(event)); + break; + case GLOBAL_EV_DECODE: + falcon_handle_global_event(channel, &event); + break; + case DRIVER_EV_DECODE: + falcon_handle_driver_event(channel, &event); + break; + default: + EFX_ERR(channel->efx, "channel %d unknown event type %d" + " (data " EFX_QWORD_FMT ")\n", channel->channel, + ev_code, EFX_QWORD_VAL(event)); + } + + /* Increment read pointer */ + read_ptr = (read_ptr + 1) & FALCON_EVQ_MASK; + + } while (*rx_quota); + + channel->eventq_read_ptr = read_ptr; + return rxdmaqs; +} + +void falcon_set_int_moderation(struct efx_channel *channel) +{ + efx_dword_t timer_cmd; + struct efx_nic *efx = channel->efx; + + /* Set timer register */ + if (channel->irq_moderation) { + /* Round to resolution supported by hardware. The value we + * program is based at 0. So actual interrupt moderation + * achieved is ((x + 1) * res). + */ + unsigned int res = 5; + channel->irq_moderation -= (channel->irq_moderation % res); + if (channel->irq_moderation < res) + channel->irq_moderation = res; + EFX_POPULATE_DWORD_2(timer_cmd, + TIMER_MODE, TIMER_MODE_INT_HLDOFF, + TIMER_VAL, + (channel->irq_moderation / res) - 1); + } else { + EFX_POPULATE_DWORD_2(timer_cmd, + TIMER_MODE, TIMER_MODE_DIS, + TIMER_VAL, 0); + } + falcon_writel_page_locked(efx, &timer_cmd, TIMER_CMD_REG_KER, + channel->evqnum); + +} + +/* Allocate buffer table entries for event queue */ +int falcon_probe_eventq(struct efx_channel *channel) +{ + struct efx_nic *efx = channel->efx; + unsigned int evq_size; + + evq_size = FALCON_EVQ_SIZE * sizeof(efx_qword_t); + return falcon_alloc_special_buffer(efx, &channel->eventq, evq_size); +} + +int falcon_init_eventq(struct efx_channel *channel) +{ + efx_oword_t evq_ptr; + struct efx_nic *efx = channel->efx; + int rc; + + EFX_LOG(efx, "channel %d event queue in special buffers %d-%d\n", + channel->channel, channel->eventq.index, + channel->eventq.index + channel->eventq.entries - 1); + + /* Pin event queue buffer */ + rc = falcon_init_special_buffer(efx, &channel->eventq); + if (rc) + return rc; + + /* Fill event queue with all ones (i.e. empty events) */ + memset(channel->eventq.addr, 0xff, channel->eventq.len); + + /* Push event queue to card */ + EFX_POPULATE_OWORD_3(evq_ptr, + EVQ_EN, 1, + EVQ_SIZE, FALCON_EVQ_ORDER, + EVQ_BUF_BASE_ID, channel->eventq.index); + falcon_write_table(efx, &evq_ptr, efx->type->evq_ptr_tbl_base, + channel->evqnum); + + falcon_set_int_moderation(channel); + + return 0; +} + +void falcon_fini_eventq(struct efx_channel *channel) +{ + efx_oword_t eventq_ptr; + struct efx_nic *efx = channel->efx; + + /* Remove event queue from card */ + EFX_ZERO_OWORD(eventq_ptr); + falcon_write_table(efx, &eventq_ptr, efx->type->evq_ptr_tbl_base, + channel->evqnum); + + /* Unpin event queue */ + falcon_fini_special_buffer(efx, &channel->eventq); +} + +/* Free buffers backing event queue */ +void falcon_remove_eventq(struct efx_channel *channel) +{ + falcon_free_special_buffer(channel->efx, &channel->eventq); +} + + +/* Generates a test event on the event queue. A subsequent call to + * process_eventq() should pick up the event and place the value of + * "magic" into channel->eventq_magic; + */ +void falcon_generate_test_event(struct efx_channel *channel, unsigned int magic) +{ + efx_qword_t test_event; + + EFX_POPULATE_QWORD_2(test_event, + EV_CODE, DRV_GEN_EV_DECODE, + EVQ_MAGIC, magic); + falcon_generate_event(channel, &test_event); +} + + +/************************************************************************** + * + * Falcon hardware interrupts + * The hardware interrupt handler does very little work; all the event + * queue processing is carried out by per-channel tasklets. + * + **************************************************************************/ + +/* Enable/disable/generate Falcon interrupts */ +static inline void falcon_interrupts(struct efx_nic *efx, int enabled, + int force) +{ + efx_oword_t int_en_reg_ker; + + EFX_POPULATE_OWORD_2(int_en_reg_ker, + KER_INT_KER, force, + DRV_INT_EN_KER, enabled); + falcon_write(efx, &int_en_reg_ker, INT_EN_REG_KER); +} + +void falcon_enable_interrupts(struct efx_nic *efx) +{ + efx_oword_t int_adr_reg_ker; + struct efx_channel *channel; + + EFX_ZERO_OWORD(*((efx_oword_t *) efx->irq_status.addr)); + wmb(); /* Ensure interrupt vector is clear before interrupts enabled */ + + /* Program address */ + EFX_POPULATE_OWORD_2(int_adr_reg_ker, + NORM_INT_VEC_DIS_KER, EFX_INT_MODE_USE_MSI(efx), + INT_ADR_KER, efx->irq_status.dma_addr); + falcon_write(efx, &int_adr_reg_ker, INT_ADR_REG_KER); + + /* Enable interrupts */ + falcon_interrupts(efx, 1, 0); + + /* Force processing of all the channels to get the EVQ RPTRs up to + date */ + efx_for_each_channel_with_interrupt(channel, efx) + efx_schedule_channel(channel); +} + +void falcon_disable_interrupts(struct efx_nic *efx) +{ + /* Disable interrupts */ + falcon_interrupts(efx, 0, 0); +} + +/* Generate a Falcon test interrupt + * Interrupt must already have been enabled, otherwise nasty things + * may happen. + */ +void falcon_generate_interrupt(struct efx_nic *efx) +{ + falcon_interrupts(efx, 1, 1); +} + +/* Acknowledge a legacy interrupt from Falcon + * + * This acknowledges a legacy (not MSI) interrupt via INT_ACK_KER_REG. + * + * Due to SFC bug 3706 (silicon revision <=A1) reads can be duplicated in the + * BIU. Interrupt acknowledge is read sensitive so must write instead + * (then read to ensure the BIU collector is flushed) + * + * NB most hardware supports MSI interrupts + */ +static inline void falcon_irq_ack_a1(struct efx_nic *efx) +{ + efx_dword_t reg; + + EFX_POPULATE_DWORD_1(reg, INT_ACK_DUMMY_DATA, 0xb7eb7e); + falcon_writel(efx, ®, INT_ACK_REG_KER_A1); + falcon_readl(efx, ®, WORK_AROUND_BROKEN_PCI_READS_REG_KER_A1); +} + +/* Process a fatal interrupt + * Disable bus mastering ASAP and schedule a reset + */ +static irqreturn_t falcon_fatal_interrupt(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + efx_oword_t *int_ker = (efx_oword_t *) efx->irq_status.addr; + efx_oword_t fatal_intr; + int error, mem_perr; + static int n_int_errors; + + falcon_read(efx, &fatal_intr, FATAL_INTR_REG_KER); + error = EFX_OWORD_FIELD(fatal_intr, INT_KER_ERROR); + + EFX_ERR(efx, "SYSTEM ERROR " EFX_OWORD_FMT " status " + EFX_OWORD_FMT ": %s\n", EFX_OWORD_VAL(*int_ker), + EFX_OWORD_VAL(fatal_intr), + error ? "disabling bus mastering" : "no recognised error"); + if (error == 0) + goto out; + + /* If this is a memory parity error dump which blocks are offending */ + mem_perr = EFX_OWORD_FIELD(fatal_intr, MEM_PERR_INT_KER); + if (mem_perr) { + efx_oword_t reg; + falcon_read(efx, ®, MEM_STAT_REG_KER); + EFX_ERR(efx, "SYSTEM ERROR: memory parity error " + EFX_OWORD_FMT "\n", EFX_OWORD_VAL(reg)); + } + + /* Disable DMA bus mastering on both devices */ + pci_disable_device(efx->pci_dev); + if (FALCON_IS_DUAL_FUNC(efx)) + pci_disable_device(nic_data->pci_dev2); + + if (++n_int_errors < FALCON_MAX_INT_ERRORS) { + EFX_ERR(efx, "SYSTEM ERROR - reset scheduled\n"); + efx_schedule_reset(efx, RESET_TYPE_INT_ERROR); + } else { + EFX_ERR(efx, "SYSTEM ERROR - max number of errors seen." + "NIC will be disabled\n"); + efx_schedule_reset(efx, RESET_TYPE_DISABLE); + } +out: + return IRQ_HANDLED; +} + +/* Handle a legacy interrupt from Falcon + * Acknowledges the interrupt and schedule event queue processing. + */ +static irqreturn_t falcon_legacy_interrupt_b0(int irq, void *dev_id) +{ + struct efx_nic *efx = (struct efx_nic *)dev_id; + efx_oword_t *int_ker = (efx_oword_t *) efx->irq_status.addr; + struct efx_channel *channel; + efx_dword_t reg; + u32 queues; + int syserr; + + /* Read the ISR which also ACKs the interrupts */ + falcon_readl(efx, ®, INT_ISR0_B0); + queues = EFX_EXTRACT_DWORD(reg, 0, 31); + + /* Check to see if we have a serious error condition */ + syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT); + if (unlikely(syserr)) + return falcon_fatal_interrupt(efx); + + if (queues == 0) + return IRQ_NONE; + + efx->last_irq_cpu = raw_smp_processor_id(); + EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_DWORD_FMT "\n", + irq, raw_smp_processor_id(), EFX_DWORD_VAL(reg)); + + /* Schedule processing of any interrupting queues */ + channel = &efx->channel[0]; + while (queues) { + if (queues & 0x01) + efx_schedule_channel(channel); + channel++; + queues >>= 1; + } + + return IRQ_HANDLED; +} + + +static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) +{ + struct efx_nic *efx = (struct efx_nic *)dev_id; + efx_oword_t *int_ker = (efx_oword_t *) efx->irq_status.addr; + struct efx_channel *channel; + int syserr; + int queues; + + /* Check to see if this is our interrupt. If it isn't, we + * exit without having touched the hardware. + */ + if (unlikely(EFX_OWORD_IS_ZERO(*int_ker))) { + EFX_TRACE(efx, "IRQ %d on CPU %d not for me\n", irq, + raw_smp_processor_id()); + return IRQ_NONE; + } + efx->last_irq_cpu = raw_smp_processor_id(); + EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_OWORD_FMT "\n", + irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker)); + + /* Check to see if we have a serious error condition */ + syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT); + if (unlikely(syserr)) + return falcon_fatal_interrupt(efx); + + /* Determine interrupting queues, clear interrupt status + * register and acknowledge the device interrupt. + */ + BUILD_BUG_ON(INT_EVQS_WIDTH > EFX_MAX_CHANNELS); + queues = EFX_OWORD_FIELD(*int_ker, INT_EVQS); + EFX_ZERO_OWORD(*int_ker); + wmb(); /* Ensure the vector is cleared before interrupt ack */ + falcon_irq_ack_a1(efx); + + /* Schedule processing of any interrupting queues */ + channel = &efx->channel[0]; + while (queues) { + if (queues & 0x01) + efx_schedule_channel(channel); + channel++; + queues >>= 1; + } + + return IRQ_HANDLED; +} + +/* Handle an MSI interrupt from Falcon + * + * Handle an MSI hardware interrupt. This routine schedules event + * queue processing. No interrupt acknowledgement cycle is necessary. + * Also, we never need to check that the interrupt is for us, since + * MSI interrupts cannot be shared. + */ +static irqreturn_t falcon_msi_interrupt(int irq, void *dev_id) +{ + struct efx_channel *channel = (struct efx_channel *)dev_id; + struct efx_nic *efx = channel->efx; + efx_oword_t *int_ker = (efx_oword_t *) efx->irq_status.addr; + int syserr; + + efx->last_irq_cpu = raw_smp_processor_id(); + EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_OWORD_FMT "\n", + irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker)); + + /* Check to see if we have a serious error condition */ + syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT); + if (unlikely(syserr)) + return falcon_fatal_interrupt(efx); + + /* Schedule processing of the channel */ + efx_schedule_channel(channel); + + return IRQ_HANDLED; +} + + +/* Setup RSS indirection table. + * This maps from the hash value of the packet to RXQ + */ +static void falcon_setup_rss_indir_table(struct efx_nic *efx) +{ + int i = 0; + unsigned long offset; + efx_dword_t dword; + + if (FALCON_REV(efx) < FALCON_REV_B0) + return; + + for (offset = RX_RSS_INDIR_TBL_B0; + offset < RX_RSS_INDIR_TBL_B0 + 0x800; + offset += 0x10) { + EFX_POPULATE_DWORD_1(dword, RX_RSS_INDIR_ENT_B0, + i % efx->rss_queues); + falcon_writel(efx, &dword, offset); + i++; + } +} + +/* Hook interrupt handler(s) + * Try MSI and then legacy interrupts. + */ +int falcon_init_interrupt(struct efx_nic *efx) +{ + struct efx_channel *channel; + int rc; + + if (!EFX_INT_MODE_USE_MSI(efx)) { + irq_handler_t handler; + if (FALCON_REV(efx) >= FALCON_REV_B0) + handler = falcon_legacy_interrupt_b0; + else + handler = falcon_legacy_interrupt_a1; + + rc = request_irq(efx->legacy_irq, handler, IRQF_SHARED, + efx->name, efx); + if (rc) { + EFX_ERR(efx, "failed to hook legacy IRQ %d\n", + efx->pci_dev->irq); + goto fail1; + } + return 0; + } + + /* Hook MSI or MSI-X interrupt */ + efx_for_each_channel_with_interrupt(channel, efx) { + rc = request_irq(channel->irq, falcon_msi_interrupt, + IRQF_PROBE_SHARED, /* Not shared */ + efx->name, channel); + if (rc) { + EFX_ERR(efx, "failed to hook IRQ %d\n", channel->irq); + goto fail2; + } + } + + return 0; + + fail2: + efx_for_each_channel_with_interrupt(channel, efx) + free_irq(channel->irq, channel); + fail1: + return rc; +} + +void falcon_fini_interrupt(struct efx_nic *efx) +{ + struct efx_channel *channel; + efx_oword_t reg; + + /* Disable MSI/MSI-X interrupts */ + efx_for_each_channel_with_interrupt(channel, efx) + if (channel->irq) + free_irq(channel->irq, channel); + + /* ACK legacy interrupt */ + if (FALCON_REV(efx) >= FALCON_REV_B0) + falcon_read(efx, ®, INT_ISR0_B0); + else + falcon_irq_ack_a1(efx); + + /* Disable legacy interrupt */ + if (efx->legacy_irq) + free_irq(efx->legacy_irq, efx); +} + +/************************************************************************** + * + * EEPROM/flash + * + ************************************************************************** + */ + +#define FALCON_SPI_MAX_LEN sizeof(efx_oword_t) + +/* Wait for SPI command completion */ +static int falcon_spi_wait(struct efx_nic *efx) +{ + efx_oword_t reg; + int cmd_en, timer_active; + int count; + + count = 0; + do { + falcon_read(efx, ®, EE_SPI_HCMD_REG_KER); + cmd_en = EFX_OWORD_FIELD(reg, EE_SPI_HCMD_CMD_EN); + timer_active = EFX_OWORD_FIELD(reg, EE_WR_TIMER_ACTIVE); + if (!cmd_en && !timer_active) + return 0; + udelay(10); + } while (++count < 10000); /* wait upto 100msec */ + EFX_ERR(efx, "timed out waiting for SPI\n"); + return -ETIMEDOUT; +} + +static int +falcon_spi_read(struct efx_nic *efx, int device_id, unsigned int command, + unsigned int address, unsigned int addr_len, + void *data, unsigned int len) +{ + efx_oword_t reg; + int rc; + + BUG_ON(len > FALCON_SPI_MAX_LEN); + + /* Check SPI not currently being accessed */ + rc = falcon_spi_wait(efx); + if (rc) + return rc; + + /* Program address register */ + EFX_POPULATE_OWORD_1(reg, EE_SPI_HADR_ADR, address); + falcon_write(efx, ®, EE_SPI_HADR_REG_KER); + + /* Issue read command */ + EFX_POPULATE_OWORD_7(reg, + EE_SPI_HCMD_CMD_EN, 1, + EE_SPI_HCMD_SF_SEL, device_id, + EE_SPI_HCMD_DABCNT, len, + EE_SPI_HCMD_READ, EE_SPI_READ, + EE_SPI_HCMD_DUBCNT, 0, + EE_SPI_HCMD_ADBCNT, addr_len, + EE_SPI_HCMD_ENC, command); + falcon_write(efx, ®, EE_SPI_HCMD_REG_KER); + + /* Wait for read to complete */ + rc = falcon_spi_wait(efx); + if (rc) + return rc; + + /* Read data */ + falcon_read(efx, ®, EE_SPI_HDATA_REG_KER); + memcpy(data, ®, len); + return 0; +} + +/************************************************************************** + * + * MAC wrapper + * + ************************************************************************** + */ +void falcon_drain_tx_fifo(struct efx_nic *efx) +{ + efx_oword_t temp; + int count; + + if (FALCON_REV(efx) < FALCON_REV_B0) + return; + + falcon_read(efx, &temp, MAC0_CTRL_REG_KER); + /* There is no point in draining more than once */ + if (EFX_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0)) + return; + + /* MAC stats will fail whilst the TX fifo is draining. Serialise + * the drain sequence with the statistics fetch */ + spin_lock(&efx->stats_lock); + + EFX_SET_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0, 1); + falcon_write(efx, &temp, MAC0_CTRL_REG_KER); + + /* Reset the MAC and EM block. */ + falcon_read(efx, &temp, GLB_CTL_REG_KER); + EFX_SET_OWORD_FIELD(temp, RST_XGTX, 1); + EFX_SET_OWORD_FIELD(temp, RST_XGRX, 1); + EFX_SET_OWORD_FIELD(temp, RST_EM, 1); + falcon_write(efx, &temp, GLB_CTL_REG_KER); + + count = 0; + while (1) { + falcon_read(efx, &temp, GLB_CTL_REG_KER); + if (!EFX_OWORD_FIELD(temp, RST_XGTX) && + !EFX_OWORD_FIELD(temp, RST_XGRX) && + !EFX_OWORD_FIELD(temp, RST_EM)) { + EFX_LOG(efx, "Completed MAC reset after %d loops\n", + count); + break; + } + if (count > 20) { + EFX_ERR(efx, "MAC reset failed\n"); + break; + } + count++; + udelay(10); + } + + spin_unlock(&efx->stats_lock); + + /* If we've reset the EM block and the link is up, then + * we'll have to kick the XAUI link so the PHY can recover */ + if (efx->link_up && EFX_WORKAROUND_5147(efx)) + falcon_reset_xaui(efx); +} + +void falcon_deconfigure_mac_wrapper(struct efx_nic *efx) +{ + efx_oword_t temp; + + if (FALCON_REV(efx) < FALCON_REV_B0) + return; + + /* Isolate the MAC -> RX */ + falcon_read(efx, &temp, RX_CFG_REG_KER); + EFX_SET_OWORD_FIELD(temp, RX_INGR_EN_B0, 0); + falcon_write(efx, &temp, RX_CFG_REG_KER); + + if (!efx->link_up) + falcon_drain_tx_fifo(efx); +} + +void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) +{ + efx_oword_t reg; + int link_speed; + unsigned int tx_fc; + + if (efx->link_options & GM_LPA_10000) + link_speed = 0x3; + else if (efx->link_options & GM_LPA_1000) + link_speed = 0x2; + else if (efx->link_options & GM_LPA_100) + link_speed = 0x1; + else + link_speed = 0x0; + /* MAC_LINK_STATUS controls MAC backpressure but doesn't work + * as advertised. Disable to ensure packets are not + * indefinitely held and TX queue can be flushed at any point + * while the link is down. */ + EFX_POPULATE_OWORD_5(reg, + MAC_XOFF_VAL, 0xffff /* max pause time */, + MAC_BCAD_ACPT, 1, + MAC_UC_PROM, efx->promiscuous, + MAC_LINK_STATUS, 1, /* always set */ + MAC_SPEED, link_speed); + /* On B0, MAC backpressure can be disabled and packets get + * discarded. */ + if (FALCON_REV(efx) >= FALCON_REV_B0) { + EFX_SET_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0, + !efx->link_up); + } + + falcon_write(efx, ®, MAC0_CTRL_REG_KER); + + /* Restore the multicast hash registers. */ + falcon_set_multicast_hash(efx); + + /* Transmission of pause frames when RX crosses the threshold is + * covered by RX_XOFF_MAC_EN and XM_TX_CFG_REG:XM_FCNTL. + * Action on receipt of pause frames is controller by XM_DIS_FCNTL */ + tx_fc = (efx->flow_control & EFX_FC_TX) ? 1 : 0; + falcon_read(efx, ®, RX_CFG_REG_KER); + EFX_SET_OWORD_FIELD_VER(efx, reg, RX_XOFF_MAC_EN, tx_fc); + + /* Unisolate the MAC -> RX */ + if (FALCON_REV(efx) >= FALCON_REV_B0) + EFX_SET_OWORD_FIELD(reg, RX_INGR_EN_B0, 1); + falcon_write(efx, ®, RX_CFG_REG_KER); +} + +int falcon_dma_stats(struct efx_nic *efx, unsigned int done_offset) +{ + efx_oword_t reg; + u32 *dma_done; + int i; + + if (disable_dma_stats) + return 0; + + /* Statistics fetch will fail if the MAC is in TX drain */ + if (FALCON_REV(efx) >= FALCON_REV_B0) { + efx_oword_t temp; + falcon_read(efx, &temp, MAC0_CTRL_REG_KER); + if (EFX_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0)) + return 0; + } + + dma_done = (efx->stats_buffer.addr + done_offset); + *dma_done = FALCON_STATS_NOT_DONE; + wmb(); /* ensure done flag is clear */ + + /* Initiate DMA transfer of stats */ + EFX_POPULATE_OWORD_2(reg, + MAC_STAT_DMA_CMD, 1, + MAC_STAT_DMA_ADR, + efx->stats_buffer.dma_addr); + falcon_write(efx, ®, MAC0_STAT_DMA_REG_KER); + + /* Wait for transfer to complete */ + for (i = 0; i < 400; i++) { + if (*(volatile u32 *)dma_done == FALCON_STATS_DONE) + return 0; + udelay(10); + } + + EFX_ERR(efx, "timed out waiting for statistics\n"); + return -ETIMEDOUT; +} + +/************************************************************************** + * + * PHY access via GMII + * + ************************************************************************** + */ + +/* Use the top bit of the MII PHY id to indicate the PHY type + * (1G/10G), with the remaining bits as the actual PHY id. + * + * This allows us to avoid leaking information from the mii_if_info + * structure into other data structures. + */ +#define FALCON_PHY_ID_ID_WIDTH EFX_WIDTH(MD_PRT_DEV_ADR) +#define FALCON_PHY_ID_ID_MASK ((1 << FALCON_PHY_ID_ID_WIDTH) - 1) +#define FALCON_PHY_ID_WIDTH (FALCON_PHY_ID_ID_WIDTH + 1) +#define FALCON_PHY_ID_MASK ((1 << FALCON_PHY_ID_WIDTH) - 1) +#define FALCON_PHY_ID_10G (1 << (FALCON_PHY_ID_WIDTH - 1)) + + +/* Packing the clause 45 port and device fields into a single value */ +#define MD_PRT_ADR_COMP_LBN (MD_PRT_ADR_LBN - MD_DEV_ADR_LBN) +#define MD_PRT_ADR_COMP_WIDTH MD_PRT_ADR_WIDTH +#define MD_DEV_ADR_COMP_LBN 0 +#define MD_DEV_ADR_COMP_WIDTH MD_DEV_ADR_WIDTH + + +/* Wait for GMII access to complete */ +static int falcon_gmii_wait(struct efx_nic *efx) +{ + efx_dword_t md_stat; + int count; + + for (count = 0; count < 1000; count++) { /* wait upto 10ms */ + falcon_readl(efx, &md_stat, MD_STAT_REG_KER); + if (EFX_DWORD_FIELD(md_stat, MD_BSY) == 0) { + if (EFX_DWORD_FIELD(md_stat, MD_LNFL) != 0 || + EFX_DWORD_FIELD(md_stat, MD_BSERR) != 0) { + EFX_ERR(efx, "error from GMII access " + EFX_DWORD_FMT"\n", + EFX_DWORD_VAL(md_stat)); + return -EIO; + } + return 0; + } + udelay(10); + } + EFX_ERR(efx, "timed out waiting for GMII\n"); + return -ETIMEDOUT; +} + +/* Writes a GMII register of a PHY connected to Falcon using MDIO. */ +static void falcon_mdio_write(struct net_device *net_dev, int phy_id, + int addr, int value) +{ + struct efx_nic *efx = (struct efx_nic *)net_dev->priv; + unsigned int phy_id2 = phy_id & FALCON_PHY_ID_ID_MASK; + efx_oword_t reg; + + /* The 'generic' prt/dev packing in mdio_10g.h is conveniently + * chosen so that the only current user, Falcon, can take the + * packed value and use them directly. + * Fail to build if this assumption is broken. + */ + BUILD_BUG_ON(FALCON_PHY_ID_10G != MDIO45_XPRT_ID_IS10G); + BUILD_BUG_ON(FALCON_PHY_ID_ID_WIDTH != MDIO45_PRT_DEV_WIDTH); + BUILD_BUG_ON(MD_PRT_ADR_COMP_LBN != MDIO45_PRT_ID_COMP_LBN); + BUILD_BUG_ON(MD_DEV_ADR_COMP_LBN != MDIO45_DEV_ID_COMP_LBN); + + if (phy_id2 == PHY_ADDR_INVALID) + return; + + /* See falcon_mdio_read for an explanation. */ + if (!(phy_id & FALCON_PHY_ID_10G)) { + int mmd = ffs(efx->phy_op->mmds) - 1; + EFX_TRACE(efx, "Fixing erroneous clause22 write\n"); + phy_id2 = mdio_clause45_pack(phy_id2, mmd) + & FALCON_PHY_ID_ID_MASK; + } + + EFX_REGDUMP(efx, "writing GMII %d register %02x with %04x\n", phy_id, + addr, value); + + spin_lock_bh(&efx->phy_lock); + + /* Check MII not currently being accessed */ + if (falcon_gmii_wait(efx) != 0) + goto out; + + /* Write the address/ID register */ + EFX_POPULATE_OWORD_1(reg, MD_PHY_ADR, addr); + falcon_write(efx, ®, MD_PHY_ADR_REG_KER); + + EFX_POPULATE_OWORD_1(reg, MD_PRT_DEV_ADR, phy_id2); + falcon_write(efx, ®, MD_ID_REG_KER); + + /* Write data */ + EFX_POPULATE_OWORD_1(reg, MD_TXD, value); + falcon_write(efx, ®, MD_TXD_REG_KER); + + EFX_POPULATE_OWORD_2(reg, + MD_WRC, 1, + MD_GC, 0); + falcon_write(efx, ®, MD_CS_REG_KER); + + /* Wait for data to be written */ + if (falcon_gmii_wait(efx) != 0) { + /* Abort the write operation */ + EFX_POPULATE_OWORD_2(reg, + MD_WRC, 0, + MD_GC, 1); + falcon_write(efx, ®, MD_CS_REG_KER); + udelay(10); + } + + out: + spin_unlock_bh(&efx->phy_lock); +} + +/* Reads a GMII register from a PHY connected to Falcon. If no value + * could be read, -1 will be returned. */ +static int falcon_mdio_read(struct net_device *net_dev, int phy_id, int addr) +{ + struct efx_nic *efx = (struct efx_nic *)net_dev->priv; + unsigned int phy_addr = phy_id & FALCON_PHY_ID_ID_MASK; + efx_oword_t reg; + int value = -1; + + if (phy_addr == PHY_ADDR_INVALID) + return -1; + + /* Our PHY code knows whether it needs to talk clause 22(1G) or 45(10G) + * but the generic Linux code does not make any distinction or have + * any state for this. + * We spot the case where someone tried to talk 22 to a 45 PHY and + * redirect the request to the lowest numbered MMD as a clause45 + * request. This is enough to allow simple queries like id and link + * state to succeed. TODO: We may need to do more in future. + */ + if (!(phy_id & FALCON_PHY_ID_10G)) { + int mmd = ffs(efx->phy_op->mmds) - 1; + EFX_TRACE(efx, "Fixing erroneous clause22 read\n"); + phy_addr = mdio_clause45_pack(phy_addr, mmd) + & FALCON_PHY_ID_ID_MASK; + } + + spin_lock_bh(&efx->phy_lock); + + /* Check MII not currently being accessed */ + if (falcon_gmii_wait(efx) != 0) + goto out; + + EFX_POPULATE_OWORD_1(reg, MD_PHY_ADR, addr); + falcon_write(efx, ®, MD_PHY_ADR_REG_KER); + + EFX_POPULATE_OWORD_1(reg, MD_PRT_DEV_ADR, phy_addr); + falcon_write(efx, ®, MD_ID_REG_KER); + + /* Request data to be read */ + EFX_POPULATE_OWORD_2(reg, MD_RDC, 1, MD_GC, 0); + falcon_write(efx, ®, MD_CS_REG_KER); + + /* Wait for data to become available */ + value = falcon_gmii_wait(efx); + if (value == 0) { + falcon_read(efx, ®, MD_RXD_REG_KER); + value = EFX_OWORD_FIELD(reg, MD_RXD); + EFX_REGDUMP(efx, "read from GMII %d register %02x, got %04x\n", + phy_id, addr, value); + } else { + /* Abort the read operation */ + EFX_POPULATE_OWORD_2(reg, + MD_RIC, 0, + MD_GC, 1); + falcon_write(efx, ®, MD_CS_REG_KER); + + EFX_LOG(efx, "read from GMII 0x%x register %02x, got " + "error %d\n", phy_id, addr, value); + } + + out: + spin_unlock_bh(&efx->phy_lock); + + return value; +} + +static void falcon_init_mdio(struct mii_if_info *gmii) +{ + gmii->mdio_read = falcon_mdio_read; + gmii->mdio_write = falcon_mdio_write; + gmii->phy_id_mask = FALCON_PHY_ID_MASK; + gmii->reg_num_mask = ((1 << EFX_WIDTH(MD_PHY_ADR)) - 1); +} + +static int falcon_probe_phy(struct efx_nic *efx) +{ + switch (efx->phy_type) { + case PHY_TYPE_10XPRESS: + efx->phy_op = &falcon_tenxpress_phy_ops; + break; + case PHY_TYPE_XFP: + efx->phy_op = &falcon_xfp_phy_ops; + break; + default: + EFX_ERR(efx, "Unknown PHY type %d\n", + efx->phy_type); + return -1; + } + return 0; +} + +/* This call is responsible for hooking in the MAC and PHY operations */ +int falcon_probe_port(struct efx_nic *efx) +{ + int rc; + + /* Hook in PHY operations table */ + rc = falcon_probe_phy(efx); + if (rc) + return rc; + + /* Set up GMII structure for PHY */ + efx->mii.supports_gmii = 1; + falcon_init_mdio(&efx->mii); + + /* Hardware flow ctrl. FalconA RX FIFO too small for pause generation */ + if (FALCON_REV(efx) >= FALCON_REV_B0) + efx->flow_control = EFX_FC_RX | EFX_FC_TX; + else + efx->flow_control = EFX_FC_RX; + + /* Allocate buffer for stats */ + rc = falcon_alloc_buffer(efx, &efx->stats_buffer, + FALCON_MAC_STATS_SIZE); + if (rc) + return rc; + EFX_LOG(efx, "stats buffer at %llx (virt %p phys %lx)\n", + (unsigned long long)efx->stats_buffer.dma_addr, + efx->stats_buffer.addr, + virt_to_phys(efx->stats_buffer.addr)); + + return 0; +} + +void falcon_remove_port(struct efx_nic *efx) +{ + falcon_free_buffer(efx, &efx->stats_buffer); +} + +/************************************************************************** + * + * Multicast filtering + * + ************************************************************************** + */ + +void falcon_set_multicast_hash(struct efx_nic *efx) +{ + union efx_multicast_hash *mc_hash = &efx->multicast_hash; + + /* Broadcast packets go through the multicast hash filter. + * ether_crc_le() of the broadcast address is 0xbe2612ff + * so we always add bit 0xff to the mask. + */ + set_bit_le(0xff, mc_hash->byte); + + falcon_write(efx, &mc_hash->oword[0], MAC_MCAST_HASH_REG0_KER); + falcon_write(efx, &mc_hash->oword[1], MAC_MCAST_HASH_REG1_KER); +} + +/************************************************************************** + * + * Device reset + * + ************************************************************************** + */ + +/* Resets NIC to known state. This routine must be called in process + * context and is allowed to sleep. */ +int falcon_reset_hw(struct efx_nic *efx, enum reset_type method) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + efx_oword_t glb_ctl_reg_ker; + int rc; + + EFX_LOG(efx, "performing hardware reset (%d)\n", method); + + /* Initiate device reset */ + if (method == RESET_TYPE_WORLD) { + rc = pci_save_state(efx->pci_dev); + if (rc) { + EFX_ERR(efx, "failed to backup PCI state of primary " + "function prior to hardware reset\n"); + goto fail1; + } + if (FALCON_IS_DUAL_FUNC(efx)) { + rc = pci_save_state(nic_data->pci_dev2); + if (rc) { + EFX_ERR(efx, "failed to backup PCI state of " + "secondary function prior to " + "hardware reset\n"); + goto fail2; + } + } + + EFX_POPULATE_OWORD_2(glb_ctl_reg_ker, + EXT_PHY_RST_DUR, 0x7, + SWRST, 1); + } else { + int reset_phy = (method == RESET_TYPE_INVISIBLE ? + EXCLUDE_FROM_RESET : 0); + + EFX_POPULATE_OWORD_7(glb_ctl_reg_ker, + EXT_PHY_RST_CTL, reset_phy, + PCIE_CORE_RST_CTL, EXCLUDE_FROM_RESET, + PCIE_NSTCK_RST_CTL, EXCLUDE_FROM_RESET, + PCIE_SD_RST_CTL, EXCLUDE_FROM_RESET, + EE_RST_CTL, EXCLUDE_FROM_RESET, + EXT_PHY_RST_DUR, 0x7 /* 10ms */, + SWRST, 1); + } + falcon_write(efx, &glb_ctl_reg_ker, GLB_CTL_REG_KER); + + EFX_LOG(efx, "waiting for hardware reset\n"); + schedule_timeout_uninterruptible(HZ / 20); + + /* Restore PCI configuration if needed */ + if (method == RESET_TYPE_WORLD) { + if (FALCON_IS_DUAL_FUNC(efx)) { + rc = pci_restore_state(nic_data->pci_dev2); + if (rc) { + EFX_ERR(efx, "failed to restore PCI config for " + "the secondary function\n"); + goto fail3; + } + } + rc = pci_restore_state(efx->pci_dev); + if (rc) { + EFX_ERR(efx, "failed to restore PCI config for the " + "primary function\n"); + goto fail4; + } + EFX_LOG(efx, "successfully restored PCI config\n"); + } + + /* Assert that reset complete */ + falcon_read(efx, &glb_ctl_reg_ker, GLB_CTL_REG_KER); + if (EFX_OWORD_FIELD(glb_ctl_reg_ker, SWRST) != 0) { + rc = -ETIMEDOUT; + EFX_ERR(efx, "timed out waiting for hardware reset\n"); + goto fail5; + } + EFX_LOG(efx, "hardware reset complete\n"); + + return 0; + + /* pci_save_state() and pci_restore_state() MUST be called in pairs */ +fail2: +fail3: + pci_restore_state(efx->pci_dev); +fail1: +fail4: +fail5: + return rc; +} + +/* Zeroes out the SRAM contents. This routine must be called in + * process context and is allowed to sleep. + */ +static int falcon_reset_sram(struct efx_nic *efx) +{ + efx_oword_t srm_cfg_reg_ker, gpio_cfg_reg_ker; + int count; + + /* Set the SRAM wake/sleep GPIO appropriately. */ + falcon_read(efx, &gpio_cfg_reg_ker, GPIO_CTL_REG_KER); + EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, GPIO1_OEN, 1); + EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, GPIO1_OUT, 1); + falcon_write(efx, &gpio_cfg_reg_ker, GPIO_CTL_REG_KER); + + /* Initiate SRAM reset */ + EFX_POPULATE_OWORD_2(srm_cfg_reg_ker, + SRAM_OOB_BT_INIT_EN, 1, + SRM_NUM_BANKS_AND_BANK_SIZE, 0); + falcon_write(efx, &srm_cfg_reg_ker, SRM_CFG_REG_KER); + + /* Wait for SRAM reset to complete */ + count = 0; + do { + EFX_LOG(efx, "waiting for SRAM reset (attempt %d)...\n", count); + + /* SRAM reset is slow; expect around 16ms */ + schedule_timeout_uninterruptible(HZ / 50); + + /* Check for reset complete */ + falcon_read(efx, &srm_cfg_reg_ker, SRM_CFG_REG_KER); + if (!EFX_OWORD_FIELD(srm_cfg_reg_ker, SRAM_OOB_BT_INIT_EN)) { + EFX_LOG(efx, "SRAM reset complete\n"); + + return 0; + } + } while (++count < 20); /* wait upto 0.4 sec */ + + EFX_ERR(efx, "timed out waiting for SRAM reset\n"); + return -ETIMEDOUT; +} + +/* Extract non-volatile configuration */ +static int falcon_probe_nvconfig(struct efx_nic *efx) +{ + struct falcon_nvconfig *nvconfig; + efx_oword_t nic_stat; + int device_id; + unsigned addr_len; + size_t offset, len; + int magic_num, struct_ver, board_rev; + int rc; + + /* Find the boot device. */ + falcon_read(efx, &nic_stat, NIC_STAT_REG); + if (EFX_OWORD_FIELD(nic_stat, SF_PRST)) { + device_id = EE_SPI_FLASH; + addr_len = 3; + } else if (EFX_OWORD_FIELD(nic_stat, EE_PRST)) { + device_id = EE_SPI_EEPROM; + addr_len = 2; + } else { + return -ENODEV; + } + + nvconfig = kmalloc(sizeof(*nvconfig), GFP_KERNEL); + + /* Read the whole configuration structure into memory. */ + for (offset = 0; offset < sizeof(*nvconfig); offset += len) { + len = min(sizeof(*nvconfig) - offset, + (size_t) FALCON_SPI_MAX_LEN); + rc = falcon_spi_read(efx, device_id, SPI_READ, + NVCONFIG_BASE + offset, addr_len, + (char *)nvconfig + offset, len); + if (rc) + goto out; + } + + /* Read the MAC addresses */ + memcpy(efx->mac_address, nvconfig->mac_address[0], ETH_ALEN); + + /* Read the board configuration. */ + magic_num = le16_to_cpu(nvconfig->board_magic_num); + struct_ver = le16_to_cpu(nvconfig->board_struct_ver); + + if (magic_num != NVCONFIG_BOARD_MAGIC_NUM || struct_ver < 2) { + EFX_ERR(efx, "Non volatile memory bad magic=%x ver=%x " + "therefore using defaults\n", magic_num, struct_ver); + efx->phy_type = PHY_TYPE_NONE; + efx->mii.phy_id = PHY_ADDR_INVALID; + board_rev = 0; + } else { + struct falcon_nvconfig_board_v2 *v2 = &nvconfig->board_v2; + + efx->phy_type = v2->port0_phy_type; + efx->mii.phy_id = v2->port0_phy_addr; + board_rev = le16_to_cpu(v2->board_revision); + } + + EFX_LOG(efx, "PHY is %d phy_id %d\n", efx->phy_type, efx->mii.phy_id); + + efx_set_board_info(efx, board_rev); + + out: + kfree(nvconfig); + return rc; +} + +/* Probe the NIC variant (revision, ASIC vs FPGA, function count, port + * count, port speed). Set workaround and feature flags accordingly. + */ +static int falcon_probe_nic_variant(struct efx_nic *efx) +{ + efx_oword_t altera_build; + + falcon_read(efx, &altera_build, ALTERA_BUILD_REG_KER); + if (EFX_OWORD_FIELD(altera_build, VER_ALL)) { + EFX_ERR(efx, "Falcon FPGA not supported\n"); + return -ENODEV; + } + + switch (FALCON_REV(efx)) { + case FALCON_REV_A0: + case 0xff: + EFX_ERR(efx, "Falcon rev A0 not supported\n"); + return -ENODEV; + + case FALCON_REV_A1:{ + efx_oword_t nic_stat; + + falcon_read(efx, &nic_stat, NIC_STAT_REG); + + if (EFX_OWORD_FIELD(nic_stat, STRAP_PCIE) == 0) { + EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n"); + return -ENODEV; + } + if (!EFX_OWORD_FIELD(nic_stat, STRAP_10G)) { + EFX_ERR(efx, "1G mode not supported\n"); + return -ENODEV; + } + break; + } + + case FALCON_REV_B0: + break; + + default: + EFX_ERR(efx, "Unknown Falcon rev %d\n", FALCON_REV(efx)); + return -ENODEV; + } + + return 0; +} + +int falcon_probe_nic(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data; + int rc; + + /* Initialise I2C interface state */ + efx->i2c.efx = efx; + efx->i2c.op = &falcon_i2c_bit_operations; + efx->i2c.sda = 1; + efx->i2c.scl = 1; + + /* Allocate storage for hardware specific data */ + nic_data = kzalloc(sizeof(*nic_data), GFP_KERNEL); + efx->nic_data = (void *) nic_data; + + /* Determine number of ports etc. */ + rc = falcon_probe_nic_variant(efx); + if (rc) + goto fail1; + + /* Probe secondary function if expected */ + if (FALCON_IS_DUAL_FUNC(efx)) { + struct pci_dev *dev = pci_dev_get(efx->pci_dev); + + while ((dev = pci_get_device(EFX_VENDID_SFC, FALCON_A_S_DEVID, + dev))) { + if (dev->bus == efx->pci_dev->bus && + dev->devfn == efx->pci_dev->devfn + 1) { + nic_data->pci_dev2 = dev; + break; + } + } + if (!nic_data->pci_dev2) { + EFX_ERR(efx, "failed to find secondary function\n"); + rc = -ENODEV; + goto fail2; + } + } + + /* Now we can reset the NIC */ + rc = falcon_reset_hw(efx, RESET_TYPE_ALL); + if (rc) { + EFX_ERR(efx, "failed to reset NIC\n"); + goto fail3; + } + + /* Allocate memory for INT_KER */ + rc = falcon_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t)); + if (rc) + goto fail4; + BUG_ON(efx->irq_status.dma_addr & 0x0f); + + EFX_LOG(efx, "INT_KER at %llx (virt %p phys %lx)\n", + (unsigned long long)efx->irq_status.dma_addr, + efx->irq_status.addr, virt_to_phys(efx->irq_status.addr)); + + /* Read in the non-volatile configuration */ + rc = falcon_probe_nvconfig(efx); + if (rc) + goto fail5; + + return 0; + + fail5: + falcon_free_buffer(efx, &efx->irq_status); + fail4: + /* fall-thru */ + fail3: + if (nic_data->pci_dev2) { + pci_dev_put(nic_data->pci_dev2); + nic_data->pci_dev2 = NULL; + } + fail2: + /* fall-thru */ + fail1: + kfree(efx->nic_data); + return rc; +} + +/* This call performs hardware-specific global initialisation, such as + * defining the descriptor cache sizes and number of RSS channels. + * It does not set up any buffers, descriptor rings or event queues. + */ +int falcon_init_nic(struct efx_nic *efx) +{ + struct falcon_nic_data *data; + efx_oword_t temp; + unsigned thresh; + int rc; + + data = (struct falcon_nic_data *)efx->nic_data; + + /* Set up the address region register. This is only needed + * for the B0 FPGA, but since we are just pushing in the + * reset defaults this may as well be unconditional. */ + EFX_POPULATE_OWORD_4(temp, ADR_REGION0, 0, + ADR_REGION1, (1 << 16), + ADR_REGION2, (2 << 16), + ADR_REGION3, (3 << 16)); + falcon_write(efx, &temp, ADR_REGION_REG_KER); + + /* Use on-chip SRAM */ + falcon_read(efx, &temp, NIC_STAT_REG); + EFX_SET_OWORD_FIELD(temp, ONCHIP_SRAM, 1); + falcon_write(efx, &temp, NIC_STAT_REG); + + /* Set buffer table mode */ + EFX_POPULATE_OWORD_1(temp, BUF_TBL_MODE, BUF_TBL_MODE_FULL); + falcon_write(efx, &temp, BUF_TBL_CFG_REG_KER); + + rc = falcon_reset_sram(efx); + if (rc) + return rc; + + /* Set positions of descriptor caches in SRAM. */ + EFX_POPULATE_OWORD_1(temp, SRM_TX_DC_BASE_ADR, TX_DC_BASE / 8); + falcon_write(efx, &temp, SRM_TX_DC_CFG_REG_KER); + EFX_POPULATE_OWORD_1(temp, SRM_RX_DC_BASE_ADR, RX_DC_BASE / 8); + falcon_write(efx, &temp, SRM_RX_DC_CFG_REG_KER); + + /* Set TX descriptor cache size. */ + BUILD_BUG_ON(TX_DC_ENTRIES != (16 << TX_DC_ENTRIES_ORDER)); + EFX_POPULATE_OWORD_1(temp, TX_DC_SIZE, TX_DC_ENTRIES_ORDER); + falcon_write(efx, &temp, TX_DC_CFG_REG_KER); + + /* Set RX descriptor cache size. Set low watermark to size-8, as + * this allows most efficient prefetching. + */ + BUILD_BUG_ON(RX_DC_ENTRIES != (16 << RX_DC_ENTRIES_ORDER)); + EFX_POPULATE_OWORD_1(temp, RX_DC_SIZE, RX_DC_ENTRIES_ORDER); + falcon_write(efx, &temp, RX_DC_CFG_REG_KER); + EFX_POPULATE_OWORD_1(temp, RX_DC_PF_LWM, RX_DC_ENTRIES - 8); + falcon_write(efx, &temp, RX_DC_PF_WM_REG_KER); + + /* Clear the parity enables on the TX data fifos as + * they produce false parity errors because of timing issues + */ + if (EFX_WORKAROUND_5129(efx)) { + falcon_read(efx, &temp, SPARE_REG_KER); + EFX_SET_OWORD_FIELD(temp, MEM_PERR_EN_TX_DATA, 0); + falcon_write(efx, &temp, SPARE_REG_KER); + } + + /* Enable all the genuinely fatal interrupts. (They are still + * masked by the overall interrupt mask, controlled by + * falcon_interrupts()). + * + * Note: All other fatal interrupts are enabled + */ + EFX_POPULATE_OWORD_3(temp, + ILL_ADR_INT_KER_EN, 1, + RBUF_OWN_INT_KER_EN, 1, + TBUF_OWN_INT_KER_EN, 1); + EFX_INVERT_OWORD(temp); + falcon_write(efx, &temp, FATAL_INTR_REG_KER); + + /* Set number of RSS queues for receive path. */ + falcon_read(efx, &temp, RX_FILTER_CTL_REG); + if (FALCON_REV(efx) >= FALCON_REV_B0) + EFX_SET_OWORD_FIELD(temp, NUM_KER, 0); + else + EFX_SET_OWORD_FIELD(temp, NUM_KER, efx->rss_queues - 1); + if (EFX_WORKAROUND_7244(efx)) { + EFX_SET_OWORD_FIELD(temp, UDP_FULL_SRCH_LIMIT, 8); + EFX_SET_OWORD_FIELD(temp, UDP_WILD_SRCH_LIMIT, 8); + EFX_SET_OWORD_FIELD(temp, TCP_FULL_SRCH_LIMIT, 8); + EFX_SET_OWORD_FIELD(temp, TCP_WILD_SRCH_LIMIT, 8); + } + falcon_write(efx, &temp, RX_FILTER_CTL_REG); + + falcon_setup_rss_indir_table(efx); + + /* Setup RX. Wait for descriptor is broken and must + * be disabled. RXDP recovery shouldn't be needed, but is. + */ + falcon_read(efx, &temp, RX_SELF_RST_REG_KER); + EFX_SET_OWORD_FIELD(temp, RX_NODESC_WAIT_DIS, 1); + EFX_SET_OWORD_FIELD(temp, RX_RECOVERY_EN, 1); + if (EFX_WORKAROUND_5583(efx)) + EFX_SET_OWORD_FIELD(temp, RX_ISCSI_DIS, 1); + falcon_write(efx, &temp, RX_SELF_RST_REG_KER); + + /* Disable the ugly timer-based TX DMA backoff and allow TX DMA to be + * controlled by the RX FIFO fill level. Set arbitration to one pkt/Q. + */ + falcon_read(efx, &temp, TX_CFG2_REG_KER); + EFX_SET_OWORD_FIELD(temp, TX_RX_SPACER, 0xfe); + EFX_SET_OWORD_FIELD(temp, TX_RX_SPACER_EN, 1); + EFX_SET_OWORD_FIELD(temp, TX_ONE_PKT_PER_Q, 1); + EFX_SET_OWORD_FIELD(temp, TX_CSR_PUSH_EN, 0); + EFX_SET_OWORD_FIELD(temp, TX_DIS_NON_IP_EV, 1); + /* Enable SW_EV to inherit in char driver - assume harmless here */ + EFX_SET_OWORD_FIELD(temp, TX_SW_EV_EN, 1); + /* Prefetch threshold 2 => fetch when descriptor cache half empty */ + EFX_SET_OWORD_FIELD(temp, TX_PREF_THRESHOLD, 2); + /* Squash TX of packets of 16 bytes or less */ + if (FALCON_REV(efx) >= FALCON_REV_B0 && EFX_WORKAROUND_9141(efx)) + EFX_SET_OWORD_FIELD(temp, TX_FLUSH_MIN_LEN_EN_B0, 1); + falcon_write(efx, &temp, TX_CFG2_REG_KER); + + /* Do not enable TX_NO_EOP_DISC_EN, since it limits packets to 16 + * descriptors (which is bad). + */ + falcon_read(efx, &temp, TX_CFG_REG_KER); + EFX_SET_OWORD_FIELD(temp, TX_NO_EOP_DISC_EN, 0); + falcon_write(efx, &temp, TX_CFG_REG_KER); + + /* RX config */ + falcon_read(efx, &temp, RX_CFG_REG_KER); + EFX_SET_OWORD_FIELD_VER(efx, temp, RX_DESC_PUSH_EN, 0); + if (EFX_WORKAROUND_7575(efx)) + EFX_SET_OWORD_FIELD_VER(efx, temp, RX_USR_BUF_SIZE, + (3 * 4096) / 32); + if (FALCON_REV(efx) >= FALCON_REV_B0) + EFX_SET_OWORD_FIELD(temp, RX_INGR_EN_B0, 1); + + /* RX FIFO flow control thresholds */ + thresh = ((rx_xon_thresh_bytes >= 0) ? + rx_xon_thresh_bytes : efx->type->rx_xon_thresh); + EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XON_MAC_TH, thresh / 256); + thresh = ((rx_xoff_thresh_bytes >= 0) ? + rx_xoff_thresh_bytes : efx->type->rx_xoff_thresh); + EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XOFF_MAC_TH, thresh / 256); + /* RX control FIFO thresholds [32 entries] */ + EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XON_TX_TH, 25); + EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XOFF_TX_TH, 20); + falcon_write(efx, &temp, RX_CFG_REG_KER); + + /* Set destination of both TX and RX Flush events */ + if (FALCON_REV(efx) >= FALCON_REV_B0) { + EFX_POPULATE_OWORD_1(temp, FLS_EVQ_ID, 0); + falcon_write(efx, &temp, DP_CTRL_REG); + } + + return 0; +} + +void falcon_remove_nic(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + + falcon_free_buffer(efx, &efx->irq_status); + + (void) falcon_reset_hw(efx, RESET_TYPE_ALL); + + /* Release the second function after the reset */ + if (nic_data->pci_dev2) { + pci_dev_put(nic_data->pci_dev2); + nic_data->pci_dev2 = NULL; + } + + /* Tear down the private nic state */ + kfree(efx->nic_data); + efx->nic_data = NULL; +} + +void falcon_update_nic_stats(struct efx_nic *efx) +{ + efx_oword_t cnt; + + falcon_read(efx, &cnt, RX_NODESC_DROP_REG_KER); + efx->n_rx_nodesc_drop_cnt += EFX_OWORD_FIELD(cnt, RX_NODESC_DROP_CNT); +} + +/************************************************************************** + * + * Revision-dependent attributes used by efx.c + * + ************************************************************************** + */ + +struct efx_nic_type falcon_a_nic_type = { + .mem_bar = 2, + .mem_map_size = 0x20000, + .txd_ptr_tbl_base = TX_DESC_PTR_TBL_KER_A1, + .rxd_ptr_tbl_base = RX_DESC_PTR_TBL_KER_A1, + .buf_tbl_base = BUF_TBL_KER_A1, + .evq_ptr_tbl_base = EVQ_PTR_TBL_KER_A1, + .evq_rptr_tbl_base = EVQ_RPTR_REG_KER_A1, + .txd_ring_mask = FALCON_TXD_RING_MASK, + .rxd_ring_mask = FALCON_RXD_RING_MASK, + .evq_size = FALCON_EVQ_SIZE, + .max_dma_mask = FALCON_DMA_MASK, + .tx_dma_mask = FALCON_TX_DMA_MASK, + .bug5391_mask = 0xf, + .rx_xoff_thresh = 2048, + .rx_xon_thresh = 512, + .rx_buffer_padding = 0x24, + .max_interrupt_mode = EFX_INT_MODE_MSI, + .phys_addr_channels = 4, +}; + +struct efx_nic_type falcon_b_nic_type = { + .mem_bar = 2, + /* Map everything up to and including the RSS indirection + * table. Don't map MSI-X table, MSI-X PBA since Linux + * requires that they not be mapped. */ + .mem_map_size = RX_RSS_INDIR_TBL_B0 + 0x800, + .txd_ptr_tbl_base = TX_DESC_PTR_TBL_KER_B0, + .rxd_ptr_tbl_base = RX_DESC_PTR_TBL_KER_B0, + .buf_tbl_base = BUF_TBL_KER_B0, + .evq_ptr_tbl_base = EVQ_PTR_TBL_KER_B0, + .evq_rptr_tbl_base = EVQ_RPTR_REG_KER_B0, + .txd_ring_mask = FALCON_TXD_RING_MASK, + .rxd_ring_mask = FALCON_RXD_RING_MASK, + .evq_size = FALCON_EVQ_SIZE, + .max_dma_mask = FALCON_DMA_MASK, + .tx_dma_mask = FALCON_TX_DMA_MASK, + .bug5391_mask = 0, + .rx_xoff_thresh = 54272, /* ~80Kb - 3*max MTU */ + .rx_xon_thresh = 27648, /* ~3*max MTU */ + .rx_buffer_padding = 0, + .max_interrupt_mode = EFX_INT_MODE_MSIX, + .phys_addr_channels = 32, /* Hardware limit is 64, but the legacy + * interrupt handler only supports 32 + * channels */ +}; + diff --git a/drivers/net/sfc/falcon.h b/drivers/net/sfc/falcon.h new file mode 100644 index 00000000000..6117403b0c0 --- /dev/null +++ b/drivers/net/sfc/falcon.h @@ -0,0 +1,130 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_FALCON_H +#define EFX_FALCON_H + +#include "net_driver.h" + +/* + * Falcon hardware control + */ + +enum falcon_revision { + FALCON_REV_A0 = 0, + FALCON_REV_A1 = 1, + FALCON_REV_B0 = 2, +}; + +#define FALCON_REV(efx) ((efx)->pci_dev->revision) + +extern struct efx_nic_type falcon_a_nic_type; +extern struct efx_nic_type falcon_b_nic_type; + +/************************************************************************** + * + * Externs + * + ************************************************************************** + */ + +/* TX data path */ +extern int falcon_probe_tx(struct efx_tx_queue *tx_queue); +extern int falcon_init_tx(struct efx_tx_queue *tx_queue); +extern void falcon_fini_tx(struct efx_tx_queue *tx_queue); +extern void falcon_remove_tx(struct efx_tx_queue *tx_queue); +extern void falcon_push_buffers(struct efx_tx_queue *tx_queue); + +/* RX data path */ +extern int falcon_probe_rx(struct efx_rx_queue *rx_queue); +extern int falcon_init_rx(struct efx_rx_queue *rx_queue); +extern void falcon_fini_rx(struct efx_rx_queue *rx_queue); +extern void falcon_remove_rx(struct efx_rx_queue *rx_queue); +extern void falcon_notify_rx_desc(struct efx_rx_queue *rx_queue); + +/* Event data path */ +extern int falcon_probe_eventq(struct efx_channel *channel); +extern int falcon_init_eventq(struct efx_channel *channel); +extern void falcon_fini_eventq(struct efx_channel *channel); +extern void falcon_remove_eventq(struct efx_channel *channel); +extern int falcon_process_eventq(struct efx_channel *channel, int *rx_quota); +extern void falcon_eventq_read_ack(struct efx_channel *channel); + +/* Ports */ +extern int falcon_probe_port(struct efx_nic *efx); +extern void falcon_remove_port(struct efx_nic *efx); + +/* MAC/PHY */ +extern int falcon_xaui_link_ok(struct efx_nic *efx); +extern int falcon_dma_stats(struct efx_nic *efx, + unsigned int done_offset); +extern void falcon_drain_tx_fifo(struct efx_nic *efx); +extern void falcon_deconfigure_mac_wrapper(struct efx_nic *efx); +extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx); + +/* Interrupts and test events */ +extern int falcon_init_interrupt(struct efx_nic *efx); +extern void falcon_enable_interrupts(struct efx_nic *efx); +extern void falcon_generate_test_event(struct efx_channel *channel, + unsigned int magic); +extern void falcon_generate_interrupt(struct efx_nic *efx); +extern void falcon_set_int_moderation(struct efx_channel *channel); +extern void falcon_disable_interrupts(struct efx_nic *efx); +extern void falcon_fini_interrupt(struct efx_nic *efx); + +/* Global Resources */ +extern int falcon_probe_nic(struct efx_nic *efx); +extern int falcon_probe_resources(struct efx_nic *efx); +extern int falcon_init_nic(struct efx_nic *efx); +extern int falcon_reset_hw(struct efx_nic *efx, enum reset_type method); +extern void falcon_remove_resources(struct efx_nic *efx); +extern void falcon_remove_nic(struct efx_nic *efx); +extern void falcon_update_nic_stats(struct efx_nic *efx); +extern void falcon_set_multicast_hash(struct efx_nic *efx); +extern int falcon_reset_xaui(struct efx_nic *efx); + +/************************************************************************** + * + * Falcon MAC stats + * + ************************************************************************** + */ + +#define FALCON_STAT_OFFSET(falcon_stat) EFX_VAL(falcon_stat, offset) +#define FALCON_STAT_WIDTH(falcon_stat) EFX_VAL(falcon_stat, WIDTH) + +/* Retrieve statistic from statistics block */ +#define FALCON_STAT(efx, falcon_stat, efx_stat) do { \ + if (FALCON_STAT_WIDTH(falcon_stat) == 16) \ + (efx)->mac_stats.efx_stat += le16_to_cpu( \ + *((__force __le16 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + else if (FALCON_STAT_WIDTH(falcon_stat) == 32) \ + (efx)->mac_stats.efx_stat += le32_to_cpu( \ + *((__force __le32 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + else \ + (efx)->mac_stats.efx_stat += le64_to_cpu( \ + *((__force __le64 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + } while (0) + +#define FALCON_MAC_STATS_SIZE 0x100 + +#define MAC_DATA_LBN 0 +#define MAC_DATA_WIDTH 32 + +extern void falcon_generate_event(struct efx_channel *channel, + efx_qword_t *event); + +#endif /* EFX_FALCON_H */ diff --git a/drivers/net/sfc/falcon_hwdefs.h b/drivers/net/sfc/falcon_hwdefs.h new file mode 100644 index 00000000000..0485a63eaff --- /dev/null +++ b/drivers/net/sfc/falcon_hwdefs.h @@ -0,0 +1,1135 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_FALCON_HWDEFS_H +#define EFX_FALCON_HWDEFS_H + +/* + * Falcon hardware value definitions. + * Falcon is the internal codename for the SFC4000 controller that is + * present in SFE400X evaluation boards + */ + +/************************************************************************** + * + * Falcon registers + * + ************************************************************************** + */ + +/* Address region register */ +#define ADR_REGION_REG_KER 0x00 +#define ADR_REGION0_LBN 0 +#define ADR_REGION0_WIDTH 18 +#define ADR_REGION1_LBN 32 +#define ADR_REGION1_WIDTH 18 +#define ADR_REGION2_LBN 64 +#define ADR_REGION2_WIDTH 18 +#define ADR_REGION3_LBN 96 +#define ADR_REGION3_WIDTH 18 + +/* Interrupt enable register */ +#define INT_EN_REG_KER 0x0010 +#define KER_INT_KER_LBN 3 +#define KER_INT_KER_WIDTH 1 +#define DRV_INT_EN_KER_LBN 0 +#define DRV_INT_EN_KER_WIDTH 1 + +/* Interrupt status address register */ +#define INT_ADR_REG_KER 0x0030 +#define NORM_INT_VEC_DIS_KER_LBN 64 +#define NORM_INT_VEC_DIS_KER_WIDTH 1 +#define INT_ADR_KER_LBN 0 +#define INT_ADR_KER_WIDTH EFX_DMA_TYPE_WIDTH(64) /* not 46 for this one */ + +/* Interrupt status register (B0 only) */ +#define INT_ISR0_B0 0x90 +#define INT_ISR1_B0 0xA0 + +/* Interrupt acknowledge register (A0/A1 only) */ +#define INT_ACK_REG_KER_A1 0x0050 +#define INT_ACK_DUMMY_DATA_LBN 0 +#define INT_ACK_DUMMY_DATA_WIDTH 32 + +/* Interrupt acknowledge work-around register (A0/A1 only )*/ +#define WORK_AROUND_BROKEN_PCI_READS_REG_KER_A1 0x0070 + +/* SPI host command register */ +#define EE_SPI_HCMD_REG_KER 0x0100 +#define EE_SPI_HCMD_CMD_EN_LBN 31 +#define EE_SPI_HCMD_CMD_EN_WIDTH 1 +#define EE_WR_TIMER_ACTIVE_LBN 28 +#define EE_WR_TIMER_ACTIVE_WIDTH 1 +#define EE_SPI_HCMD_SF_SEL_LBN 24 +#define EE_SPI_HCMD_SF_SEL_WIDTH 1 +#define EE_SPI_EEPROM 0 +#define EE_SPI_FLASH 1 +#define EE_SPI_HCMD_DABCNT_LBN 16 +#define EE_SPI_HCMD_DABCNT_WIDTH 5 +#define EE_SPI_HCMD_READ_LBN 15 +#define EE_SPI_HCMD_READ_WIDTH 1 +#define EE_SPI_READ 1 +#define EE_SPI_WRITE 0 +#define EE_SPI_HCMD_DUBCNT_LBN 12 +#define EE_SPI_HCMD_DUBCNT_WIDTH 2 +#define EE_SPI_HCMD_ADBCNT_LBN 8 +#define EE_SPI_HCMD_ADBCNT_WIDTH 2 +#define EE_SPI_HCMD_ENC_LBN 0 +#define EE_SPI_HCMD_ENC_WIDTH 8 + +/* SPI host address register */ +#define EE_SPI_HADR_REG_KER 0x0110 +#define EE_SPI_HADR_ADR_LBN 0 +#define EE_SPI_HADR_ADR_WIDTH 24 + +/* SPI host data register */ +#define EE_SPI_HDATA_REG_KER 0x0120 + +/* PCIE CORE ACCESS REG */ +#define PCIE_CORE_ADDR_PCIE_DEVICE_CTRL_STAT 0x68 +#define PCIE_CORE_ADDR_PCIE_LINK_CTRL_STAT 0x70 +#define PCIE_CORE_ADDR_ACK_RPL_TIMER 0x700 +#define PCIE_CORE_ADDR_ACK_FREQ 0x70C + +/* NIC status register */ +#define NIC_STAT_REG 0x0200 +#define ONCHIP_SRAM_LBN 16 +#define ONCHIP_SRAM_WIDTH 1 +#define SF_PRST_LBN 9 +#define SF_PRST_WIDTH 1 +#define EE_PRST_LBN 8 +#define EE_PRST_WIDTH 1 +/* See pic_mode_t for decoding of this field */ +/* These bit definitions are extrapolated from the list of numerical + * values for STRAP_PINS. + */ +#define STRAP_10G_LBN 2 +#define STRAP_10G_WIDTH 1 +#define STRAP_PCIE_LBN 0 +#define STRAP_PCIE_WIDTH 1 + +/* GPIO control register */ +#define GPIO_CTL_REG_KER 0x0210 +#define GPIO_OUTPUTS_LBN (16) +#define GPIO_OUTPUTS_WIDTH (4) +#define GPIO_INPUTS_LBN (8) +#define GPIO_DIRECTION_LBN (24) +#define GPIO_DIRECTION_WIDTH (4) +#define GPIO_DIRECTION_OUT (1) +#define GPIO_SRAM_SLEEP (1 << 1) + +#define GPIO3_OEN_LBN (GPIO_DIRECTION_LBN + 3) +#define GPIO3_OEN_WIDTH 1 +#define GPIO2_OEN_LBN (GPIO_DIRECTION_LBN + 2) +#define GPIO2_OEN_WIDTH 1 +#define GPIO1_OEN_LBN (GPIO_DIRECTION_LBN + 1) +#define GPIO1_OEN_WIDTH 1 +#define GPIO0_OEN_LBN (GPIO_DIRECTION_LBN + 0) +#define GPIO0_OEN_WIDTH 1 + +#define GPIO3_OUT_LBN (GPIO_OUTPUTS_LBN + 3) +#define GPIO3_OUT_WIDTH 1 +#define GPIO2_OUT_LBN (GPIO_OUTPUTS_LBN + 2) +#define GPIO2_OUT_WIDTH 1 +#define GPIO1_OUT_LBN (GPIO_OUTPUTS_LBN + 1) +#define GPIO1_OUT_WIDTH 1 +#define GPIO0_OUT_LBN (GPIO_OUTPUTS_LBN + 0) +#define GPIO0_OUT_WIDTH 1 + +#define GPIO3_IN_LBN (GPIO_INPUTS_LBN + 3) +#define GPIO3_IN_WIDTH 1 +#define GPIO2_IN_WIDTH 1 +#define GPIO1_IN_WIDTH 1 +#define GPIO0_IN_LBN (GPIO_INPUTS_LBN + 0) +#define GPIO0_IN_WIDTH 1 + +/* Global control register */ +#define GLB_CTL_REG_KER 0x0220 +#define EXT_PHY_RST_CTL_LBN 63 +#define EXT_PHY_RST_CTL_WIDTH 1 +#define PCIE_SD_RST_CTL_LBN 61 +#define PCIE_SD_RST_CTL_WIDTH 1 + +#define PCIE_NSTCK_RST_CTL_LBN 58 +#define PCIE_NSTCK_RST_CTL_WIDTH 1 +#define PCIE_CORE_RST_CTL_LBN 57 +#define PCIE_CORE_RST_CTL_WIDTH 1 +#define EE_RST_CTL_LBN 49 +#define EE_RST_CTL_WIDTH 1 +#define RST_XGRX_LBN 24 +#define RST_XGRX_WIDTH 1 +#define RST_XGTX_LBN 23 +#define RST_XGTX_WIDTH 1 +#define RST_EM_LBN 22 +#define RST_EM_WIDTH 1 +#define EXT_PHY_RST_DUR_LBN 1 +#define EXT_PHY_RST_DUR_WIDTH 3 +#define SWRST_LBN 0 +#define SWRST_WIDTH 1 +#define INCLUDE_IN_RESET 0 +#define EXCLUDE_FROM_RESET 1 + +/* Fatal interrupt register */ +#define FATAL_INTR_REG_KER 0x0230 +#define RBUF_OWN_INT_KER_EN_LBN 39 +#define RBUF_OWN_INT_KER_EN_WIDTH 1 +#define TBUF_OWN_INT_KER_EN_LBN 38 +#define TBUF_OWN_INT_KER_EN_WIDTH 1 +#define ILL_ADR_INT_KER_EN_LBN 33 +#define ILL_ADR_INT_KER_EN_WIDTH 1 +#define MEM_PERR_INT_KER_LBN 8 +#define MEM_PERR_INT_KER_WIDTH 1 +#define INT_KER_ERROR_LBN 0 +#define INT_KER_ERROR_WIDTH 12 + +#define DP_CTRL_REG 0x250 +#define FLS_EVQ_ID_LBN 0 +#define FLS_EVQ_ID_WIDTH 11 + +#define MEM_STAT_REG_KER 0x260 + +/* Debug probe register */ +#define DEBUG_BLK_SEL_MISC 7 +#define DEBUG_BLK_SEL_SERDES 6 +#define DEBUG_BLK_SEL_EM 5 +#define DEBUG_BLK_SEL_SR 4 +#define DEBUG_BLK_SEL_EV 3 +#define DEBUG_BLK_SEL_RX 2 +#define DEBUG_BLK_SEL_TX 1 +#define DEBUG_BLK_SEL_BIU 0 + +/* FPGA build version */ +#define ALTERA_BUILD_REG_KER 0x0300 +#define VER_ALL_LBN 0 +#define VER_ALL_WIDTH 32 + +/* Spare EEPROM bits register (flash 0x390) */ +#define SPARE_REG_KER 0x310 +#define MEM_PERR_EN_TX_DATA_LBN 72 +#define MEM_PERR_EN_TX_DATA_WIDTH 2 + +/* Timer table for kernel access */ +#define TIMER_CMD_REG_KER 0x420 +#define TIMER_MODE_LBN 12 +#define TIMER_MODE_WIDTH 2 +#define TIMER_MODE_DIS 0 +#define TIMER_MODE_INT_HLDOFF 2 +#define TIMER_VAL_LBN 0 +#define TIMER_VAL_WIDTH 12 + +/* Driver generated event register */ +#define DRV_EV_REG_KER 0x440 +#define DRV_EV_QID_LBN 64 +#define DRV_EV_QID_WIDTH 12 +#define DRV_EV_DATA_LBN 0 +#define DRV_EV_DATA_WIDTH 64 + +/* Buffer table configuration register */ +#define BUF_TBL_CFG_REG_KER 0x600 +#define BUF_TBL_MODE_LBN 3 +#define BUF_TBL_MODE_WIDTH 1 +#define BUF_TBL_MODE_HALF 0 +#define BUF_TBL_MODE_FULL 1 + +/* SRAM receive descriptor cache configuration register */ +#define SRM_RX_DC_CFG_REG_KER 0x610 +#define SRM_RX_DC_BASE_ADR_LBN 0 +#define SRM_RX_DC_BASE_ADR_WIDTH 21 + +/* SRAM transmit descriptor cache configuration register */ +#define SRM_TX_DC_CFG_REG_KER 0x620 +#define SRM_TX_DC_BASE_ADR_LBN 0 +#define SRM_TX_DC_BASE_ADR_WIDTH 21 + +/* SRAM configuration register */ +#define SRM_CFG_REG_KER 0x630 +#define SRAM_OOB_BT_INIT_EN_LBN 3 +#define SRAM_OOB_BT_INIT_EN_WIDTH 1 +#define SRM_NUM_BANKS_AND_BANK_SIZE_LBN 0 +#define SRM_NUM_BANKS_AND_BANK_SIZE_WIDTH 3 +#define SRM_NB_BSZ_1BANKS_2M 0 +#define SRM_NB_BSZ_1BANKS_4M 1 +#define SRM_NB_BSZ_1BANKS_8M 2 +#define SRM_NB_BSZ_DEFAULT 3 /* char driver will set the default */ +#define SRM_NB_BSZ_2BANKS_4M 4 +#define SRM_NB_BSZ_2BANKS_8M 5 +#define SRM_NB_BSZ_2BANKS_16M 6 +#define SRM_NB_BSZ_RESERVED 7 + +/* Special buffer table update register */ +#define BUF_TBL_UPD_REG_KER 0x0650 +#define BUF_UPD_CMD_LBN 63 +#define BUF_UPD_CMD_WIDTH 1 +#define BUF_CLR_CMD_LBN 62 +#define BUF_CLR_CMD_WIDTH 1 +#define BUF_CLR_END_ID_LBN 32 +#define BUF_CLR_END_ID_WIDTH 20 +#define BUF_CLR_START_ID_LBN 0 +#define BUF_CLR_START_ID_WIDTH 20 + +/* Receive configuration register */ +#define RX_CFG_REG_KER 0x800 + +/* B0 */ +#define RX_INGR_EN_B0_LBN 47 +#define RX_INGR_EN_B0_WIDTH 1 +#define RX_DESC_PUSH_EN_B0_LBN 43 +#define RX_DESC_PUSH_EN_B0_WIDTH 1 +#define RX_XON_TX_TH_B0_LBN 33 +#define RX_XON_TX_TH_B0_WIDTH 5 +#define RX_XOFF_TX_TH_B0_LBN 28 +#define RX_XOFF_TX_TH_B0_WIDTH 5 +#define RX_USR_BUF_SIZE_B0_LBN 19 +#define RX_USR_BUF_SIZE_B0_WIDTH 9 +#define RX_XON_MAC_TH_B0_LBN 10 +#define RX_XON_MAC_TH_B0_WIDTH 9 +#define RX_XOFF_MAC_TH_B0_LBN 1 +#define RX_XOFF_MAC_TH_B0_WIDTH 9 +#define RX_XOFF_MAC_EN_B0_LBN 0 +#define RX_XOFF_MAC_EN_B0_WIDTH 1 + +/* A1 */ +#define RX_DESC_PUSH_EN_A1_LBN 35 +#define RX_DESC_PUSH_EN_A1_WIDTH 1 +#define RX_XON_TX_TH_A1_LBN 25 +#define RX_XON_TX_TH_A1_WIDTH 5 +#define RX_XOFF_TX_TH_A1_LBN 20 +#define RX_XOFF_TX_TH_A1_WIDTH 5 +#define RX_USR_BUF_SIZE_A1_LBN 11 +#define RX_USR_BUF_SIZE_A1_WIDTH 9 +#define RX_XON_MAC_TH_A1_LBN 6 +#define RX_XON_MAC_TH_A1_WIDTH 5 +#define RX_XOFF_MAC_TH_A1_LBN 1 +#define RX_XOFF_MAC_TH_A1_WIDTH 5 +#define RX_XOFF_MAC_EN_A1_LBN 0 +#define RX_XOFF_MAC_EN_A1_WIDTH 1 + +/* Receive filter control register */ +#define RX_FILTER_CTL_REG 0x810 +#define UDP_FULL_SRCH_LIMIT_LBN 32 +#define UDP_FULL_SRCH_LIMIT_WIDTH 8 +#define NUM_KER_LBN 24 +#define NUM_KER_WIDTH 2 +#define UDP_WILD_SRCH_LIMIT_LBN 16 +#define UDP_WILD_SRCH_LIMIT_WIDTH 8 +#define TCP_WILD_SRCH_LIMIT_LBN 8 +#define TCP_WILD_SRCH_LIMIT_WIDTH 8 +#define TCP_FULL_SRCH_LIMIT_LBN 0 +#define TCP_FULL_SRCH_LIMIT_WIDTH 8 + +/* RX queue flush register */ +#define RX_FLUSH_DESCQ_REG_KER 0x0820 +#define RX_FLUSH_DESCQ_CMD_LBN 24 +#define RX_FLUSH_DESCQ_CMD_WIDTH 1 +#define RX_FLUSH_DESCQ_LBN 0 +#define RX_FLUSH_DESCQ_WIDTH 12 + +/* Receive descriptor update register */ +#define RX_DESC_UPD_REG_KER_DWORD (0x830 + 12) +#define RX_DESC_WPTR_DWORD_LBN 0 +#define RX_DESC_WPTR_DWORD_WIDTH 12 + +/* Receive descriptor cache configuration register */ +#define RX_DC_CFG_REG_KER 0x840 +#define RX_DC_SIZE_LBN 0 +#define RX_DC_SIZE_WIDTH 2 + +#define RX_DC_PF_WM_REG_KER 0x850 +#define RX_DC_PF_LWM_LBN 0 +#define RX_DC_PF_LWM_WIDTH 6 + +/* RX no descriptor drop counter */ +#define RX_NODESC_DROP_REG_KER 0x880 +#define RX_NODESC_DROP_CNT_LBN 0 +#define RX_NODESC_DROP_CNT_WIDTH 16 + +/* RX black magic register */ +#define RX_SELF_RST_REG_KER 0x890 +#define RX_ISCSI_DIS_LBN 17 +#define RX_ISCSI_DIS_WIDTH 1 +#define RX_NODESC_WAIT_DIS_LBN 9 +#define RX_NODESC_WAIT_DIS_WIDTH 1 +#define RX_RECOVERY_EN_LBN 8 +#define RX_RECOVERY_EN_WIDTH 1 + +/* TX queue flush register */ +#define TX_FLUSH_DESCQ_REG_KER 0x0a00 +#define TX_FLUSH_DESCQ_CMD_LBN 12 +#define TX_FLUSH_DESCQ_CMD_WIDTH 1 +#define TX_FLUSH_DESCQ_LBN 0 +#define TX_FLUSH_DESCQ_WIDTH 12 + +/* Transmit descriptor update register */ +#define TX_DESC_UPD_REG_KER_DWORD (0xa10 + 12) +#define TX_DESC_WPTR_DWORD_LBN 0 +#define TX_DESC_WPTR_DWORD_WIDTH 12 + +/* Transmit descriptor cache configuration register */ +#define TX_DC_CFG_REG_KER 0xa20 +#define TX_DC_SIZE_LBN 0 +#define TX_DC_SIZE_WIDTH 2 + +/* Transmit checksum configuration register (A0/A1 only) */ +#define TX_CHKSM_CFG_REG_KER_A1 0xa30 + +/* Transmit configuration register */ +#define TX_CFG_REG_KER 0xa50 +#define TX_NO_EOP_DISC_EN_LBN 5 +#define TX_NO_EOP_DISC_EN_WIDTH 1 + +/* Transmit configuration register 2 */ +#define TX_CFG2_REG_KER 0xa80 +#define TX_CSR_PUSH_EN_LBN 89 +#define TX_CSR_PUSH_EN_WIDTH 1 +#define TX_RX_SPACER_LBN 64 +#define TX_RX_SPACER_WIDTH 8 +#define TX_SW_EV_EN_LBN 59 +#define TX_SW_EV_EN_WIDTH 1 +#define TX_RX_SPACER_EN_LBN 57 +#define TX_RX_SPACER_EN_WIDTH 1 +#define TX_PREF_THRESHOLD_LBN 19 +#define TX_PREF_THRESHOLD_WIDTH 2 +#define TX_ONE_PKT_PER_Q_LBN 18 +#define TX_ONE_PKT_PER_Q_WIDTH 1 +#define TX_DIS_NON_IP_EV_LBN 17 +#define TX_DIS_NON_IP_EV_WIDTH 1 +#define TX_FLUSH_MIN_LEN_EN_B0_LBN 7 +#define TX_FLUSH_MIN_LEN_EN_B0_WIDTH 1 + +/* PHY management transmit data register */ +#define MD_TXD_REG_KER 0xc00 +#define MD_TXD_LBN 0 +#define MD_TXD_WIDTH 16 + +/* PHY management receive data register */ +#define MD_RXD_REG_KER 0xc10 +#define MD_RXD_LBN 0 +#define MD_RXD_WIDTH 16 + +/* PHY management configuration & status register */ +#define MD_CS_REG_KER 0xc20 +#define MD_GC_LBN 4 +#define MD_GC_WIDTH 1 +#define MD_RIC_LBN 2 +#define MD_RIC_WIDTH 1 +#define MD_RDC_LBN 1 +#define MD_RDC_WIDTH 1 +#define MD_WRC_LBN 0 +#define MD_WRC_WIDTH 1 + +/* PHY management PHY address register */ +#define MD_PHY_ADR_REG_KER 0xc30 +#define MD_PHY_ADR_LBN 0 +#define MD_PHY_ADR_WIDTH 16 + +/* PHY management ID register */ +#define MD_ID_REG_KER 0xc40 +#define MD_PRT_ADR_LBN 11 +#define MD_PRT_ADR_WIDTH 5 +#define MD_DEV_ADR_LBN 6 +#define MD_DEV_ADR_WIDTH 5 +/* Used for writing both at once */ +#define MD_PRT_DEV_ADR_LBN 6 +#define MD_PRT_DEV_ADR_WIDTH 10 + +/* PHY management status & mask register (DWORD read only) */ +#define MD_STAT_REG_KER 0xc50 +#define MD_BSERR_LBN 2 +#define MD_BSERR_WIDTH 1 +#define MD_LNFL_LBN 1 +#define MD_LNFL_WIDTH 1 +#define MD_BSY_LBN 0 +#define MD_BSY_WIDTH 1 + +/* Port 0 and 1 MAC stats registers */ +#define MAC0_STAT_DMA_REG_KER 0xc60 +#define MAC_STAT_DMA_CMD_LBN 48 +#define MAC_STAT_DMA_CMD_WIDTH 1 +#define MAC_STAT_DMA_ADR_LBN 0 +#define MAC_STAT_DMA_ADR_WIDTH EFX_DMA_TYPE_WIDTH(46) + +/* Port 0 and 1 MAC control registers */ +#define MAC0_CTRL_REG_KER 0xc80 +#define MAC_XOFF_VAL_LBN 16 +#define MAC_XOFF_VAL_WIDTH 16 +#define TXFIFO_DRAIN_EN_B0_LBN 7 +#define TXFIFO_DRAIN_EN_B0_WIDTH 1 +#define MAC_BCAD_ACPT_LBN 4 +#define MAC_BCAD_ACPT_WIDTH 1 +#define MAC_UC_PROM_LBN 3 +#define MAC_UC_PROM_WIDTH 1 +#define MAC_LINK_STATUS_LBN 2 +#define MAC_LINK_STATUS_WIDTH 1 +#define MAC_SPEED_LBN 0 +#define MAC_SPEED_WIDTH 2 + +/* 10G XAUI XGXS default values */ +#define XX_TXDRV_DEQ_DEFAULT 0xe /* deq=.6 */ +#define XX_TXDRV_DTX_DEFAULT 0x5 /* 1.25 */ +#define XX_SD_CTL_DRV_DEFAULT 0 /* 20mA */ + +/* Multicast address hash table */ +#define MAC_MCAST_HASH_REG0_KER 0xca0 +#define MAC_MCAST_HASH_REG1_KER 0xcb0 + +/* GMAC registers */ +#define FALCON_GMAC_REGBANK 0xe00 +#define FALCON_GMAC_REGBANK_SIZE 0x200 +#define FALCON_GMAC_REG_SIZE 0x10 + +/* XMAC registers */ +#define FALCON_XMAC_REGBANK 0x1200 +#define FALCON_XMAC_REGBANK_SIZE 0x200 +#define FALCON_XMAC_REG_SIZE 0x10 + +/* XGMAC address register low */ +#define XM_ADR_LO_REG_MAC 0x00 +#define XM_ADR_3_LBN 24 +#define XM_ADR_3_WIDTH 8 +#define XM_ADR_2_LBN 16 +#define XM_ADR_2_WIDTH 8 +#define XM_ADR_1_LBN 8 +#define XM_ADR_1_WIDTH 8 +#define XM_ADR_0_LBN 0 +#define XM_ADR_0_WIDTH 8 + +/* XGMAC address register high */ +#define XM_ADR_HI_REG_MAC 0x01 +#define XM_ADR_5_LBN 8 +#define XM_ADR_5_WIDTH 8 +#define XM_ADR_4_LBN 0 +#define XM_ADR_4_WIDTH 8 + +/* XGMAC global configuration */ +#define XM_GLB_CFG_REG_MAC 0x02 +#define XM_RX_STAT_EN_LBN 11 +#define XM_RX_STAT_EN_WIDTH 1 +#define XM_TX_STAT_EN_LBN 10 +#define XM_TX_STAT_EN_WIDTH 1 +#define XM_RX_JUMBO_MODE_LBN 6 +#define XM_RX_JUMBO_MODE_WIDTH 1 +#define XM_INTCLR_MODE_LBN 3 +#define XM_INTCLR_MODE_WIDTH 1 +#define XM_CORE_RST_LBN 0 +#define XM_CORE_RST_WIDTH 1 + +/* XGMAC transmit configuration */ +#define XM_TX_CFG_REG_MAC 0x03 +#define XM_IPG_LBN 16 +#define XM_IPG_WIDTH 4 +#define XM_FCNTL_LBN 10 +#define XM_FCNTL_WIDTH 1 +#define XM_TXCRC_LBN 8 +#define XM_TXCRC_WIDTH 1 +#define XM_AUTO_PAD_LBN 5 +#define XM_AUTO_PAD_WIDTH 1 +#define XM_TX_PRMBL_LBN 2 +#define XM_TX_PRMBL_WIDTH 1 +#define XM_TXEN_LBN 1 +#define XM_TXEN_WIDTH 1 + +/* XGMAC receive configuration */ +#define XM_RX_CFG_REG_MAC 0x04 +#define XM_PASS_CRC_ERR_LBN 25 +#define XM_PASS_CRC_ERR_WIDTH 1 +#define XM_ACPT_ALL_MCAST_LBN 11 +#define XM_ACPT_ALL_MCAST_WIDTH 1 +#define XM_ACPT_ALL_UCAST_LBN 9 +#define XM_ACPT_ALL_UCAST_WIDTH 1 +#define XM_AUTO_DEPAD_LBN 8 +#define XM_AUTO_DEPAD_WIDTH 1 +#define XM_RXEN_LBN 1 +#define XM_RXEN_WIDTH 1 + +/* XGMAC management interrupt mask register */ +#define XM_MGT_INT_MSK_REG_MAC_B0 0x5 +#define XM_MSK_PRMBLE_ERR_LBN 2 +#define XM_MSK_PRMBLE_ERR_WIDTH 1 +#define XM_MSK_RMTFLT_LBN 1 +#define XM_MSK_RMTFLT_WIDTH 1 +#define XM_MSK_LCLFLT_LBN 0 +#define XM_MSK_LCLFLT_WIDTH 1 + +/* XGMAC flow control register */ +#define XM_FC_REG_MAC 0x7 +#define XM_PAUSE_TIME_LBN 16 +#define XM_PAUSE_TIME_WIDTH 16 +#define XM_DIS_FCNTL_LBN 0 +#define XM_DIS_FCNTL_WIDTH 1 + +/* XGMAC pause time count register */ +#define XM_PAUSE_TIME_REG_MAC 0x9 + +/* XGMAC transmit parameter register */ +#define XM_TX_PARAM_REG_MAC 0x0d +#define XM_TX_JUMBO_MODE_LBN 31 +#define XM_TX_JUMBO_MODE_WIDTH 1 +#define XM_MAX_TX_FRM_SIZE_LBN 16 +#define XM_MAX_TX_FRM_SIZE_WIDTH 14 + +/* XGMAC receive parameter register */ +#define XM_RX_PARAM_REG_MAC 0x0e +#define XM_MAX_RX_FRM_SIZE_LBN 0 +#define XM_MAX_RX_FRM_SIZE_WIDTH 14 + +/* XGMAC management interrupt status register */ +#define XM_MGT_INT_REG_MAC_B0 0x0f +#define XM_PRMBLE_ERR 2 +#define XM_PRMBLE_WIDTH 1 +#define XM_RMTFLT_LBN 1 +#define XM_RMTFLT_WIDTH 1 +#define XM_LCLFLT_LBN 0 +#define XM_LCLFLT_WIDTH 1 + +/* XGXS/XAUI powerdown/reset register */ +#define XX_PWR_RST_REG_MAC 0x10 + +#define XX_PWRDND_EN_LBN 15 +#define XX_PWRDND_EN_WIDTH 1 +#define XX_PWRDNC_EN_LBN 14 +#define XX_PWRDNC_EN_WIDTH 1 +#define XX_PWRDNB_EN_LBN 13 +#define XX_PWRDNB_EN_WIDTH 1 +#define XX_PWRDNA_EN_LBN 12 +#define XX_PWRDNA_EN_WIDTH 1 +#define XX_RSTPLLCD_EN_LBN 9 +#define XX_RSTPLLCD_EN_WIDTH 1 +#define XX_RSTPLLAB_EN_LBN 8 +#define XX_RSTPLLAB_EN_WIDTH 1 +#define XX_RESETD_EN_LBN 7 +#define XX_RESETD_EN_WIDTH 1 +#define XX_RESETC_EN_LBN 6 +#define XX_RESETC_EN_WIDTH 1 +#define XX_RESETB_EN_LBN 5 +#define XX_RESETB_EN_WIDTH 1 +#define XX_RESETA_EN_LBN 4 +#define XX_RESETA_EN_WIDTH 1 +#define XX_RSTXGXSRX_EN_LBN 2 +#define XX_RSTXGXSRX_EN_WIDTH 1 +#define XX_RSTXGXSTX_EN_LBN 1 +#define XX_RSTXGXSTX_EN_WIDTH 1 +#define XX_RST_XX_EN_LBN 0 +#define XX_RST_XX_EN_WIDTH 1 + +/* XGXS/XAUI powerdown/reset control register */ +#define XX_SD_CTL_REG_MAC 0x11 +#define XX_HIDRVD_LBN 15 +#define XX_HIDRVD_WIDTH 1 +#define XX_LODRVD_LBN 14 +#define XX_LODRVD_WIDTH 1 +#define XX_HIDRVC_LBN 13 +#define XX_HIDRVC_WIDTH 1 +#define XX_LODRVC_LBN 12 +#define XX_LODRVC_WIDTH 1 +#define XX_HIDRVB_LBN 11 +#define XX_HIDRVB_WIDTH 1 +#define XX_LODRVB_LBN 10 +#define XX_LODRVB_WIDTH 1 +#define XX_HIDRVA_LBN 9 +#define XX_HIDRVA_WIDTH 1 +#define XX_LODRVA_LBN 8 +#define XX_LODRVA_WIDTH 1 + +#define XX_TXDRV_CTL_REG_MAC 0x12 +#define XX_DEQD_LBN 28 +#define XX_DEQD_WIDTH 4 +#define XX_DEQC_LBN 24 +#define XX_DEQC_WIDTH 4 +#define XX_DEQB_LBN 20 +#define XX_DEQB_WIDTH 4 +#define XX_DEQA_LBN 16 +#define XX_DEQA_WIDTH 4 +#define XX_DTXD_LBN 12 +#define XX_DTXD_WIDTH 4 +#define XX_DTXC_LBN 8 +#define XX_DTXC_WIDTH 4 +#define XX_DTXB_LBN 4 +#define XX_DTXB_WIDTH 4 +#define XX_DTXA_LBN 0 +#define XX_DTXA_WIDTH 4 + +/* XAUI XGXS core status register */ +#define XX_FORCE_SIG_DECODE_FORCED 0xff +#define XX_CORE_STAT_REG_MAC 0x16 +#define XX_ALIGN_DONE_LBN 20 +#define XX_ALIGN_DONE_WIDTH 1 +#define XX_SYNC_STAT_LBN 16 +#define XX_SYNC_STAT_WIDTH 4 +#define XX_SYNC_STAT_DECODE_SYNCED 0xf +#define XX_COMMA_DET_LBN 12 +#define XX_COMMA_DET_WIDTH 4 +#define XX_COMMA_DET_DECODE_DETECTED 0xf +#define XX_COMMA_DET_RESET 0xf +#define XX_CHARERR_LBN 4 +#define XX_CHARERR_WIDTH 4 +#define XX_CHARERR_RESET 0xf +#define XX_DISPERR_LBN 0 +#define XX_DISPERR_WIDTH 4 +#define XX_DISPERR_RESET 0xf + +/* Receive filter table */ +#define RX_FILTER_TBL0 0xF00000 + +/* Receive descriptor pointer table */ +#define RX_DESC_PTR_TBL_KER_A1 0x11800 +#define RX_DESC_PTR_TBL_KER_B0 0xF40000 +#define RX_DESC_PTR_TBL_KER_P0 0x900 +#define RX_ISCSI_DDIG_EN_LBN 88 +#define RX_ISCSI_DDIG_EN_WIDTH 1 +#define RX_ISCSI_HDIG_EN_LBN 87 +#define RX_ISCSI_HDIG_EN_WIDTH 1 +#define RX_DESCQ_BUF_BASE_ID_LBN 36 +#define RX_DESCQ_BUF_BASE_ID_WIDTH 20 +#define RX_DESCQ_EVQ_ID_LBN 24 +#define RX_DESCQ_EVQ_ID_WIDTH 12 +#define RX_DESCQ_OWNER_ID_LBN 10 +#define RX_DESCQ_OWNER_ID_WIDTH 14 +#define RX_DESCQ_LABEL_LBN 5 +#define RX_DESCQ_LABEL_WIDTH 5 +#define RX_DESCQ_SIZE_LBN 3 +#define RX_DESCQ_SIZE_WIDTH 2 +#define RX_DESCQ_SIZE_4K 3 +#define RX_DESCQ_SIZE_2K 2 +#define RX_DESCQ_SIZE_1K 1 +#define RX_DESCQ_SIZE_512 0 +#define RX_DESCQ_TYPE_LBN 2 +#define RX_DESCQ_TYPE_WIDTH 1 +#define RX_DESCQ_JUMBO_LBN 1 +#define RX_DESCQ_JUMBO_WIDTH 1 +#define RX_DESCQ_EN_LBN 0 +#define RX_DESCQ_EN_WIDTH 1 + +/* Transmit descriptor pointer table */ +#define TX_DESC_PTR_TBL_KER_A1 0x11900 +#define TX_DESC_PTR_TBL_KER_B0 0xF50000 +#define TX_DESC_PTR_TBL_KER_P0 0xa40 +#define TX_NON_IP_DROP_DIS_B0_LBN 91 +#define TX_NON_IP_DROP_DIS_B0_WIDTH 1 +#define TX_IP_CHKSM_DIS_B0_LBN 90 +#define TX_IP_CHKSM_DIS_B0_WIDTH 1 +#define TX_TCP_CHKSM_DIS_B0_LBN 89 +#define TX_TCP_CHKSM_DIS_B0_WIDTH 1 +#define TX_DESCQ_EN_LBN 88 +#define TX_DESCQ_EN_WIDTH 1 +#define TX_ISCSI_DDIG_EN_LBN 87 +#define TX_ISCSI_DDIG_EN_WIDTH 1 +#define TX_ISCSI_HDIG_EN_LBN 86 +#define TX_ISCSI_HDIG_EN_WIDTH 1 +#define TX_DESCQ_BUF_BASE_ID_LBN 36 +#define TX_DESCQ_BUF_BASE_ID_WIDTH 20 +#define TX_DESCQ_EVQ_ID_LBN 24 +#define TX_DESCQ_EVQ_ID_WIDTH 12 +#define TX_DESCQ_OWNER_ID_LBN 10 +#define TX_DESCQ_OWNER_ID_WIDTH 14 +#define TX_DESCQ_LABEL_LBN 5 +#define TX_DESCQ_LABEL_WIDTH 5 +#define TX_DESCQ_SIZE_LBN 3 +#define TX_DESCQ_SIZE_WIDTH 2 +#define TX_DESCQ_SIZE_4K 3 +#define TX_DESCQ_SIZE_2K 2 +#define TX_DESCQ_SIZE_1K 1 +#define TX_DESCQ_SIZE_512 0 +#define TX_DESCQ_TYPE_LBN 1 +#define TX_DESCQ_TYPE_WIDTH 2 + +/* Event queue pointer */ +#define EVQ_PTR_TBL_KER_A1 0x11a00 +#define EVQ_PTR_TBL_KER_B0 0xf60000 +#define EVQ_PTR_TBL_KER_P0 0x500 +#define EVQ_EN_LBN 23 +#define EVQ_EN_WIDTH 1 +#define EVQ_SIZE_LBN 20 +#define EVQ_SIZE_WIDTH 3 +#define EVQ_SIZE_32K 6 +#define EVQ_SIZE_16K 5 +#define EVQ_SIZE_8K 4 +#define EVQ_SIZE_4K 3 +#define EVQ_SIZE_2K 2 +#define EVQ_SIZE_1K 1 +#define EVQ_SIZE_512 0 +#define EVQ_BUF_BASE_ID_LBN 0 +#define EVQ_BUF_BASE_ID_WIDTH 20 + +/* Event queue read pointer */ +#define EVQ_RPTR_REG_KER_A1 0x11b00 +#define EVQ_RPTR_REG_KER_B0 0xfa0000 +#define EVQ_RPTR_REG_KER_DWORD (EVQ_RPTR_REG_KER + 0) +#define EVQ_RPTR_DWORD_LBN 0 +#define EVQ_RPTR_DWORD_WIDTH 14 + +/* RSS indirection table */ +#define RX_RSS_INDIR_TBL_B0 0xFB0000 +#define RX_RSS_INDIR_ENT_B0_LBN 0 +#define RX_RSS_INDIR_ENT_B0_WIDTH 6 + +/* Special buffer descriptors (full-mode) */ +#define BUF_FULL_TBL_KER_A1 0x8000 +#define BUF_FULL_TBL_KER_B0 0x800000 +#define IP_DAT_BUF_SIZE_LBN 50 +#define IP_DAT_BUF_SIZE_WIDTH 1 +#define IP_DAT_BUF_SIZE_8K 1 +#define IP_DAT_BUF_SIZE_4K 0 +#define BUF_ADR_REGION_LBN 48 +#define BUF_ADR_REGION_WIDTH 2 +#define BUF_ADR_FBUF_LBN 14 +#define BUF_ADR_FBUF_WIDTH 34 +#define BUF_OWNER_ID_FBUF_LBN 0 +#define BUF_OWNER_ID_FBUF_WIDTH 14 + +/* Transmit descriptor */ +#define TX_KER_PORT_LBN 63 +#define TX_KER_PORT_WIDTH 1 +#define TX_KER_CONT_LBN 62 +#define TX_KER_CONT_WIDTH 1 +#define TX_KER_BYTE_CNT_LBN 48 +#define TX_KER_BYTE_CNT_WIDTH 14 +#define TX_KER_BUF_REGION_LBN 46 +#define TX_KER_BUF_REGION_WIDTH 2 +#define TX_KER_BUF_REGION0_DECODE 0 +#define TX_KER_BUF_REGION1_DECODE 1 +#define TX_KER_BUF_REGION2_DECODE 2 +#define TX_KER_BUF_REGION3_DECODE 3 +#define TX_KER_BUF_ADR_LBN 0 +#define TX_KER_BUF_ADR_WIDTH EFX_DMA_TYPE_WIDTH(46) + +/* Receive descriptor */ +#define RX_KER_BUF_SIZE_LBN 48 +#define RX_KER_BUF_SIZE_WIDTH 14 +#define RX_KER_BUF_REGION_LBN 46 +#define RX_KER_BUF_REGION_WIDTH 2 +#define RX_KER_BUF_REGION0_DECODE 0 +#define RX_KER_BUF_REGION1_DECODE 1 +#define RX_KER_BUF_REGION2_DECODE 2 +#define RX_KER_BUF_REGION3_DECODE 3 +#define RX_KER_BUF_ADR_LBN 0 +#define RX_KER_BUF_ADR_WIDTH EFX_DMA_TYPE_WIDTH(46) + +/************************************************************************** + * + * Falcon events + * + ************************************************************************** + */ + +/* Event queue entries */ +#define EV_CODE_LBN 60 +#define EV_CODE_WIDTH 4 +#define RX_IP_EV_DECODE 0 +#define TX_IP_EV_DECODE 2 +#define DRIVER_EV_DECODE 5 +#define GLOBAL_EV_DECODE 6 +#define DRV_GEN_EV_DECODE 7 +#define WHOLE_EVENT_LBN 0 +#define WHOLE_EVENT_WIDTH 64 + +/* Receive events */ +#define RX_EV_PKT_OK_LBN 56 +#define RX_EV_PKT_OK_WIDTH 1 +#define RX_EV_PAUSE_FRM_ERR_LBN 55 +#define RX_EV_PAUSE_FRM_ERR_WIDTH 1 +#define RX_EV_BUF_OWNER_ID_ERR_LBN 54 +#define RX_EV_BUF_OWNER_ID_ERR_WIDTH 1 +#define RX_EV_IF_FRAG_ERR_LBN 53 +#define RX_EV_IF_FRAG_ERR_WIDTH 1 +#define RX_EV_IP_HDR_CHKSUM_ERR_LBN 52 +#define RX_EV_IP_HDR_CHKSUM_ERR_WIDTH 1 +#define RX_EV_TCP_UDP_CHKSUM_ERR_LBN 51 +#define RX_EV_TCP_UDP_CHKSUM_ERR_WIDTH 1 +#define RX_EV_ETH_CRC_ERR_LBN 50 +#define RX_EV_ETH_CRC_ERR_WIDTH 1 +#define RX_EV_FRM_TRUNC_LBN 49 +#define RX_EV_FRM_TRUNC_WIDTH 1 +#define RX_EV_DRIB_NIB_LBN 48 +#define RX_EV_DRIB_NIB_WIDTH 1 +#define RX_EV_TOBE_DISC_LBN 47 +#define RX_EV_TOBE_DISC_WIDTH 1 +#define RX_EV_PKT_TYPE_LBN 44 +#define RX_EV_PKT_TYPE_WIDTH 3 +#define RX_EV_PKT_TYPE_ETH_DECODE 0 +#define RX_EV_PKT_TYPE_LLC_DECODE 1 +#define RX_EV_PKT_TYPE_JUMBO_DECODE 2 +#define RX_EV_PKT_TYPE_VLAN_DECODE 3 +#define RX_EV_PKT_TYPE_VLAN_LLC_DECODE 4 +#define RX_EV_PKT_TYPE_VLAN_JUMBO_DECODE 5 +#define RX_EV_HDR_TYPE_LBN 42 +#define RX_EV_HDR_TYPE_WIDTH 2 +#define RX_EV_HDR_TYPE_TCP_IPV4_DECODE 0 +#define RX_EV_HDR_TYPE_UDP_IPV4_DECODE 1 +#define RX_EV_HDR_TYPE_OTHER_IP_DECODE 2 +#define RX_EV_HDR_TYPE_NON_IP_DECODE 3 +#define RX_EV_HDR_TYPE_HAS_CHECKSUMS(hdr_type) \ + ((hdr_type) <= RX_EV_HDR_TYPE_UDP_IPV4_DECODE) +#define RX_EV_MCAST_HASH_MATCH_LBN 40 +#define RX_EV_MCAST_HASH_MATCH_WIDTH 1 +#define RX_EV_MCAST_PKT_LBN 39 +#define RX_EV_MCAST_PKT_WIDTH 1 +#define RX_EV_Q_LABEL_LBN 32 +#define RX_EV_Q_LABEL_WIDTH 5 +#define RX_EV_JUMBO_CONT_LBN 31 +#define RX_EV_JUMBO_CONT_WIDTH 1 +#define RX_EV_BYTE_CNT_LBN 16 +#define RX_EV_BYTE_CNT_WIDTH 14 +#define RX_EV_SOP_LBN 15 +#define RX_EV_SOP_WIDTH 1 +#define RX_EV_DESC_PTR_LBN 0 +#define RX_EV_DESC_PTR_WIDTH 12 + +/* Transmit events */ +#define TX_EV_PKT_ERR_LBN 38 +#define TX_EV_PKT_ERR_WIDTH 1 +#define TX_EV_Q_LABEL_LBN 32 +#define TX_EV_Q_LABEL_WIDTH 5 +#define TX_EV_WQ_FF_FULL_LBN 15 +#define TX_EV_WQ_FF_FULL_WIDTH 1 +#define TX_EV_COMP_LBN 12 +#define TX_EV_COMP_WIDTH 1 +#define TX_EV_DESC_PTR_LBN 0 +#define TX_EV_DESC_PTR_WIDTH 12 + +/* Driver events */ +#define DRIVER_EV_SUB_CODE_LBN 56 +#define DRIVER_EV_SUB_CODE_WIDTH 4 +#define DRIVER_EV_SUB_DATA_LBN 0 +#define DRIVER_EV_SUB_DATA_WIDTH 14 +#define TX_DESCQ_FLS_DONE_EV_DECODE 0 +#define RX_DESCQ_FLS_DONE_EV_DECODE 1 +#define EVQ_INIT_DONE_EV_DECODE 2 +#define EVQ_NOT_EN_EV_DECODE 3 +#define RX_DESCQ_FLSFF_OVFL_EV_DECODE 4 +#define SRM_UPD_DONE_EV_DECODE 5 +#define WAKE_UP_EV_DECODE 6 +#define TX_PKT_NON_TCP_UDP_DECODE 9 +#define TIMER_EV_DECODE 10 +#define RX_RECOVERY_EV_DECODE 11 +#define RX_DSC_ERROR_EV_DECODE 14 +#define TX_DSC_ERROR_EV_DECODE 15 +#define DRIVER_EV_TX_DESCQ_ID_LBN 0 +#define DRIVER_EV_TX_DESCQ_ID_WIDTH 12 +#define DRIVER_EV_RX_FLUSH_FAIL_LBN 12 +#define DRIVER_EV_RX_FLUSH_FAIL_WIDTH 1 +#define DRIVER_EV_RX_DESCQ_ID_LBN 0 +#define DRIVER_EV_RX_DESCQ_ID_WIDTH 12 +#define SRM_CLR_EV_DECODE 0 +#define SRM_UPD_EV_DECODE 1 +#define SRM_ILLCLR_EV_DECODE 2 + +/* Global events */ +#define RX_RECOVERY_B0_LBN 12 +#define RX_RECOVERY_B0_WIDTH 1 +#define XG_MNT_INTR_B0_LBN 11 +#define XG_MNT_INTR_B0_WIDTH 1 +#define RX_RECOVERY_A1_LBN 11 +#define RX_RECOVERY_A1_WIDTH 1 +#define XG_PHY_INTR_LBN 9 +#define XG_PHY_INTR_WIDTH 1 +#define G_PHY1_INTR_LBN 8 +#define G_PHY1_INTR_WIDTH 1 +#define G_PHY0_INTR_LBN 7 +#define G_PHY0_INTR_WIDTH 1 + +/* Driver-generated test events */ +#define EVQ_MAGIC_LBN 0 +#define EVQ_MAGIC_WIDTH 32 + +/************************************************************************** + * + * Falcon MAC stats + * + ************************************************************************** + * + */ +#define GRxGoodOct_offset 0x0 +#define GRxBadOct_offset 0x8 +#define GRxMissPkt_offset 0x10 +#define GRxFalseCRS_offset 0x14 +#define GRxPausePkt_offset 0x18 +#define GRxBadPkt_offset 0x1C +#define GRxUcastPkt_offset 0x20 +#define GRxMcastPkt_offset 0x24 +#define GRxBcastPkt_offset 0x28 +#define GRxGoodLt64Pkt_offset 0x2C +#define GRxBadLt64Pkt_offset 0x30 +#define GRx64Pkt_offset 0x34 +#define GRx65to127Pkt_offset 0x38 +#define GRx128to255Pkt_offset 0x3C +#define GRx256to511Pkt_offset 0x40 +#define GRx512to1023Pkt_offset 0x44 +#define GRx1024to15xxPkt_offset 0x48 +#define GRx15xxtoJumboPkt_offset 0x4C +#define GRxGtJumboPkt_offset 0x50 +#define GRxFcsErr64to15xxPkt_offset 0x54 +#define GRxFcsErr15xxtoJumboPkt_offset 0x58 +#define GRxFcsErrGtJumboPkt_offset 0x5C +#define GTxGoodBadOct_offset 0x80 +#define GTxGoodOct_offset 0x88 +#define GTxSglColPkt_offset 0x90 +#define GTxMultColPkt_offset 0x94 +#define GTxExColPkt_offset 0x98 +#define GTxDefPkt_offset 0x9C +#define GTxLateCol_offset 0xA0 +#define GTxExDefPkt_offset 0xA4 +#define GTxPausePkt_offset 0xA8 +#define GTxBadPkt_offset 0xAC +#define GTxUcastPkt_offset 0xB0 +#define GTxMcastPkt_offset 0xB4 +#define GTxBcastPkt_offset 0xB8 +#define GTxLt64Pkt_offset 0xBC +#define GTx64Pkt_offset 0xC0 +#define GTx65to127Pkt_offset 0xC4 +#define GTx128to255Pkt_offset 0xC8 +#define GTx256to511Pkt_offset 0xCC +#define GTx512to1023Pkt_offset 0xD0 +#define GTx1024to15xxPkt_offset 0xD4 +#define GTx15xxtoJumboPkt_offset 0xD8 +#define GTxGtJumboPkt_offset 0xDC +#define GTxNonTcpUdpPkt_offset 0xE0 +#define GTxMacSrcErrPkt_offset 0xE4 +#define GTxIpSrcErrPkt_offset 0xE8 +#define GDmaDone_offset 0xEC + +#define XgRxOctets_offset 0x0 +#define XgRxOctets_WIDTH 48 +#define XgRxOctetsOK_offset 0x8 +#define XgRxOctetsOK_WIDTH 48 +#define XgRxPkts_offset 0x10 +#define XgRxPkts_WIDTH 32 +#define XgRxPktsOK_offset 0x14 +#define XgRxPktsOK_WIDTH 32 +#define XgRxBroadcastPkts_offset 0x18 +#define XgRxBroadcastPkts_WIDTH 32 +#define XgRxMulticastPkts_offset 0x1C +#define XgRxMulticastPkts_WIDTH 32 +#define XgRxUnicastPkts_offset 0x20 +#define XgRxUnicastPkts_WIDTH 32 +#define XgRxUndersizePkts_offset 0x24 +#define XgRxUndersizePkts_WIDTH 32 +#define XgRxOversizePkts_offset 0x28 +#define XgRxOversizePkts_WIDTH 32 +#define XgRxJabberPkts_offset 0x2C +#define XgRxJabberPkts_WIDTH 32 +#define XgRxUndersizeFCSerrorPkts_offset 0x30 +#define XgRxUndersizeFCSerrorPkts_WIDTH 32 +#define XgRxDropEvents_offset 0x34 +#define XgRxDropEvents_WIDTH 32 +#define XgRxFCSerrorPkts_offset 0x38 +#define XgRxFCSerrorPkts_WIDTH 32 +#define XgRxAlignError_offset 0x3C +#define XgRxAlignError_WIDTH 32 +#define XgRxSymbolError_offset 0x40 +#define XgRxSymbolError_WIDTH 32 +#define XgRxInternalMACError_offset 0x44 +#define XgRxInternalMACError_WIDTH 32 +#define XgRxControlPkts_offset 0x48 +#define XgRxControlPkts_WIDTH 32 +#define XgRxPausePkts_offset 0x4C +#define XgRxPausePkts_WIDTH 32 +#define XgRxPkts64Octets_offset 0x50 +#define XgRxPkts64Octets_WIDTH 32 +#define XgRxPkts65to127Octets_offset 0x54 +#define XgRxPkts65to127Octets_WIDTH 32 +#define XgRxPkts128to255Octets_offset 0x58 +#define XgRxPkts128to255Octets_WIDTH 32 +#define XgRxPkts256to511Octets_offset 0x5C +#define XgRxPkts256to511Octets_WIDTH 32 +#define XgRxPkts512to1023Octets_offset 0x60 +#define XgRxPkts512to1023Octets_WIDTH 32 +#define XgRxPkts1024to15xxOctets_offset 0x64 +#define XgRxPkts1024to15xxOctets_WIDTH 32 +#define XgRxPkts15xxtoMaxOctets_offset 0x68 +#define XgRxPkts15xxtoMaxOctets_WIDTH 32 +#define XgRxLengthError_offset 0x6C +#define XgRxLengthError_WIDTH 32 +#define XgTxPkts_offset 0x80 +#define XgTxPkts_WIDTH 32 +#define XgTxOctets_offset 0x88 +#define XgTxOctets_WIDTH 48 +#define XgTxMulticastPkts_offset 0x90 +#define XgTxMulticastPkts_WIDTH 32 +#define XgTxBroadcastPkts_offset 0x94 +#define XgTxBroadcastPkts_WIDTH 32 +#define XgTxUnicastPkts_offset 0x98 +#define XgTxUnicastPkts_WIDTH 32 +#define XgTxControlPkts_offset 0x9C +#define XgTxControlPkts_WIDTH 32 +#define XgTxPausePkts_offset 0xA0 +#define XgTxPausePkts_WIDTH 32 +#define XgTxPkts64Octets_offset 0xA4 +#define XgTxPkts64Octets_WIDTH 32 +#define XgTxPkts65to127Octets_offset 0xA8 +#define XgTxPkts65to127Octets_WIDTH 32 +#define XgTxPkts128to255Octets_offset 0xAC +#define XgTxPkts128to255Octets_WIDTH 32 +#define XgTxPkts256to511Octets_offset 0xB0 +#define XgTxPkts256to511Octets_WIDTH 32 +#define XgTxPkts512to1023Octets_offset 0xB4 +#define XgTxPkts512to1023Octets_WIDTH 32 +#define XgTxPkts1024to15xxOctets_offset 0xB8 +#define XgTxPkts1024to15xxOctets_WIDTH 32 +#define XgTxPkts1519toMaxOctets_offset 0xBC +#define XgTxPkts1519toMaxOctets_WIDTH 32 +#define XgTxUndersizePkts_offset 0xC0 +#define XgTxUndersizePkts_WIDTH 32 +#define XgTxOversizePkts_offset 0xC4 +#define XgTxOversizePkts_WIDTH 32 +#define XgTxNonTcpUdpPkt_offset 0xC8 +#define XgTxNonTcpUdpPkt_WIDTH 16 +#define XgTxMacSrcErrPkt_offset 0xCC +#define XgTxMacSrcErrPkt_WIDTH 16 +#define XgTxIpSrcErrPkt_offset 0xD0 +#define XgTxIpSrcErrPkt_WIDTH 16 +#define XgDmaDone_offset 0xD4 + +#define FALCON_STATS_NOT_DONE 0x00000000 +#define FALCON_STATS_DONE 0xffffffff + +/* Interrupt status register bits */ +#define FATAL_INT_LBN 64 +#define FATAL_INT_WIDTH 1 +#define INT_EVQS_LBN 40 +#define INT_EVQS_WIDTH 4 + +/************************************************************************** + * + * Falcon non-volatile configuration + * + ************************************************************************** + */ + +/* Board configuration v2 (v1 is obsolete; later versions are compatible) */ +struct falcon_nvconfig_board_v2 { + __le16 nports; + u8 port0_phy_addr; + u8 port0_phy_type; + u8 port1_phy_addr; + u8 port1_phy_type; + __le16 asic_sub_revision; + __le16 board_revision; +} __attribute__ ((packed)); + +#define NVCONFIG_BASE 0x300 +#define NVCONFIG_BOARD_MAGIC_NUM 0xFA1C +struct falcon_nvconfig { + efx_oword_t ee_vpd_cfg_reg; /* 0x300 */ + u8 mac_address[2][8]; /* 0x310 */ + efx_oword_t pcie_sd_ctl0123_reg; /* 0x320 */ + efx_oword_t pcie_sd_ctl45_reg; /* 0x330 */ + efx_oword_t pcie_pcs_ctl_stat_reg; /* 0x340 */ + efx_oword_t hw_init_reg; /* 0x350 */ + efx_oword_t nic_stat_reg; /* 0x360 */ + efx_oword_t glb_ctl_reg; /* 0x370 */ + efx_oword_t srm_cfg_reg; /* 0x380 */ + efx_oword_t spare_reg; /* 0x390 */ + __le16 board_magic_num; /* 0x3A0 */ + __le16 board_struct_ver; + __le16 board_checksum; + struct falcon_nvconfig_board_v2 board_v2; +} __attribute__ ((packed)); + +#endif /* EFX_FALCON_HWDEFS_H */ diff --git a/drivers/net/sfc/falcon_io.h b/drivers/net/sfc/falcon_io.h new file mode 100644 index 00000000000..ea08184ddfa --- /dev/null +++ b/drivers/net/sfc/falcon_io.h @@ -0,0 +1,243 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_FALCON_IO_H +#define EFX_FALCON_IO_H + +#include <linux/io.h> +#include <linux/spinlock.h> +#include "net_driver.h" + +/************************************************************************** + * + * Falcon hardware access + * + ************************************************************************** + * + * Notes on locking strategy: + * + * Most Falcon registers require 16-byte (or 8-byte, for SRAM + * registers) atomic writes which necessitates locking. + * Under normal operation few writes to the Falcon BAR are made and these + * registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and TX_DESC_UPD_REG) are special + * cased to allow 4-byte (hence lockless) accesses. + * + * It *is* safe to write to these 4-byte registers in the middle of an + * access to an 8-byte or 16-byte register. We therefore use a + * spinlock to protect accesses to the larger registers, but no locks + * for the 4-byte registers. + * + * A write barrier is needed to ensure that DW3 is written after DW0/1/2 + * due to the way the 16byte registers are "collected" in the Falcon BIU + * + * We also lock when carrying out reads, to ensure consistency of the + * data (made possible since the BIU reads all 128 bits into a cache). + * Reads are very rare, so this isn't a significant performance + * impact. (Most data transferred from NIC to host is DMAed directly + * into host memory). + * + * I/O BAR access uses locks for both reads and writes (but is only provided + * for testing purposes). + */ + +/* Special buffer descriptors (Falcon SRAM) */ +#define BUF_TBL_KER_A1 0x18000 +#define BUF_TBL_KER_B0 0x800000 + + +#if BITS_PER_LONG == 64 +#define FALCON_USE_QWORD_IO 1 +#endif + +#define _falcon_writeq(efx, value, reg) \ + __raw_writeq((__force u64) (value), (efx)->membase + (reg)) +#define _falcon_writel(efx, value, reg) \ + __raw_writel((__force u32) (value), (efx)->membase + (reg)) +#define _falcon_readq(efx, reg) \ + ((__force __le64) __raw_readq((efx)->membase + (reg))) +#define _falcon_readl(efx, reg) \ + ((__force __le32) __raw_readl((efx)->membase + (reg))) + +/* Writes to a normal 16-byte Falcon register, locking as appropriate. */ +static inline void falcon_write(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg) +{ + unsigned long flags; + + EFX_REGDUMP(efx, "writing register %x with " EFX_OWORD_FMT "\n", reg, + EFX_OWORD_VAL(*value)); + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef FALCON_USE_QWORD_IO + _falcon_writeq(efx, value->u64[0], reg + 0); + wmb(); + _falcon_writeq(efx, value->u64[1], reg + 8); +#else + _falcon_writel(efx, value->u32[0], reg + 0); + _falcon_writel(efx, value->u32[1], reg + 4); + _falcon_writel(efx, value->u32[2], reg + 8); + wmb(); + _falcon_writel(efx, value->u32[3], reg + 12); +#endif + mmiowb(); + spin_unlock_irqrestore(&efx->biu_lock, flags); +} + +/* Writes to an 8-byte Falcon SRAM register, locking as appropriate. */ +static inline void falcon_write_sram(struct efx_nic *efx, efx_qword_t *value, + unsigned int index) +{ + unsigned int reg = efx->type->buf_tbl_base + (index * sizeof(*value)); + unsigned long flags; + + EFX_REGDUMP(efx, "writing SRAM register %x with " EFX_QWORD_FMT "\n", + reg, EFX_QWORD_VAL(*value)); + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef FALCON_USE_QWORD_IO + _falcon_writeq(efx, value->u64[0], reg + 0); +#else + _falcon_writel(efx, value->u32[0], reg + 0); + wmb(); + _falcon_writel(efx, value->u32[1], reg + 4); +#endif + mmiowb(); + spin_unlock_irqrestore(&efx->biu_lock, flags); +} + +/* Write dword to Falcon register that allows partial writes + * + * Some Falcon registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and + * TX_DESC_UPD_REG) can be written to as a single dword. This allows + * for lockless writes. + */ +static inline void falcon_writel(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg) +{ + EFX_REGDUMP(efx, "writing partial register %x with "EFX_DWORD_FMT"\n", + reg, EFX_DWORD_VAL(*value)); + + /* No lock required */ + _falcon_writel(efx, value->u32[0], reg); +} + +/* Read from a Falcon register + * + * This reads an entire 16-byte Falcon register in one go, locking as + * appropriate. It is essential to read the first dword first, as this + * prompts Falcon to load the current value into the shadow register. + */ +static inline void falcon_read(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg) +{ + unsigned long flags; + + spin_lock_irqsave(&efx->biu_lock, flags); + value->u32[0] = _falcon_readl(efx, reg + 0); + rmb(); + value->u32[1] = _falcon_readl(efx, reg + 4); + value->u32[2] = _falcon_readl(efx, reg + 8); + value->u32[3] = _falcon_readl(efx, reg + 12); + spin_unlock_irqrestore(&efx->biu_lock, flags); + + EFX_REGDUMP(efx, "read from register %x, got " EFX_OWORD_FMT "\n", reg, + EFX_OWORD_VAL(*value)); +} + +/* This reads an 8-byte Falcon SRAM entry in one go. */ +static inline void falcon_read_sram(struct efx_nic *efx, efx_qword_t *value, + unsigned int index) +{ + unsigned int reg = efx->type->buf_tbl_base + (index * sizeof(*value)); + unsigned long flags; + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef FALCON_USE_QWORD_IO + value->u64[0] = _falcon_readq(efx, reg + 0); +#else + value->u32[0] = _falcon_readl(efx, reg + 0); + rmb(); + value->u32[1] = _falcon_readl(efx, reg + 4); +#endif + spin_unlock_irqrestore(&efx->biu_lock, flags); + + EFX_REGDUMP(efx, "read from SRAM register %x, got "EFX_QWORD_FMT"\n", + reg, EFX_QWORD_VAL(*value)); +} + +/* Read dword from Falcon register that allows partial writes (sic) */ +static inline void falcon_readl(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg) +{ + value->u32[0] = _falcon_readl(efx, reg); + EFX_REGDUMP(efx, "read from register %x, got "EFX_DWORD_FMT"\n", + reg, EFX_DWORD_VAL(*value)); +} + +/* Write to a register forming part of a table */ +static inline void falcon_write_table(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg, unsigned int index) +{ + falcon_write(efx, value, reg + index * sizeof(efx_oword_t)); +} + +/* Read to a register forming part of a table */ +static inline void falcon_read_table(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg, unsigned int index) +{ + falcon_read(efx, value, reg + index * sizeof(efx_oword_t)); +} + +/* Write to a dword register forming part of a table */ +static inline void falcon_writel_table(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg, unsigned int index) +{ + falcon_writel(efx, value, reg + index * sizeof(efx_oword_t)); +} + +/* Page-mapped register block size */ +#define FALCON_PAGE_BLOCK_SIZE 0x2000 + +/* Calculate offset to page-mapped register block */ +#define FALCON_PAGED_REG(page, reg) \ + ((page) * FALCON_PAGE_BLOCK_SIZE + (reg)) + +/* As for falcon_write(), but for a page-mapped register. */ +static inline void falcon_write_page(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg, unsigned int page) +{ + falcon_write(efx, value, FALCON_PAGED_REG(page, reg)); +} + +/* As for falcon_writel(), but for a page-mapped register. */ +static inline void falcon_writel_page(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg, unsigned int page) +{ + falcon_writel(efx, value, FALCON_PAGED_REG(page, reg)); +} + +/* Write dword to Falcon page-mapped register with an extra lock. + * + * As for falcon_writel_page(), but for a register that suffers from + * SFC bug 3181. Take out a lock so the BIU collector cannot be + * confused. */ +static inline void falcon_writel_page_locked(struct efx_nic *efx, + efx_dword_t *value, + unsigned int reg, + unsigned int page) +{ + unsigned long flags; + + spin_lock_irqsave(&efx->biu_lock, flags); + falcon_writel(efx, value, FALCON_PAGED_REG(page, reg)); + spin_unlock_irqrestore(&efx->biu_lock, flags); +} + +#endif /* EFX_FALCON_IO_H */ diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/sfc/falcon_xmac.c new file mode 100644 index 00000000000..aa7521b24a5 --- /dev/null +++ b/drivers/net/sfc/falcon_xmac.c @@ -0,0 +1,585 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include <linux/delay.h> +#include "net_driver.h" +#include "efx.h" +#include "falcon.h" +#include "falcon_hwdefs.h" +#include "falcon_io.h" +#include "mac.h" +#include "gmii.h" +#include "mdio_10g.h" +#include "phy.h" +#include "boards.h" +#include "workarounds.h" + +/************************************************************************** + * + * MAC register access + * + **************************************************************************/ + +/* Offset of an XMAC register within Falcon */ +#define FALCON_XMAC_REG(mac_reg) \ + (FALCON_XMAC_REGBANK + ((mac_reg) * FALCON_XMAC_REG_SIZE)) + +void falcon_xmac_writel(struct efx_nic *efx, + efx_dword_t *value, unsigned int mac_reg) +{ + efx_oword_t temp; + + EFX_POPULATE_OWORD_1(temp, MAC_DATA, EFX_DWORD_FIELD(*value, MAC_DATA)); + falcon_write(efx, &temp, FALCON_XMAC_REG(mac_reg)); +} + +void falcon_xmac_readl(struct efx_nic *efx, + efx_dword_t *value, unsigned int mac_reg) +{ + efx_oword_t temp; + + falcon_read(efx, &temp, FALCON_XMAC_REG(mac_reg)); + EFX_POPULATE_DWORD_1(*value, MAC_DATA, EFX_OWORD_FIELD(temp, MAC_DATA)); +} + +/************************************************************************** + * + * MAC operations + * + *************************************************************************/ +static int falcon_reset_xmac(struct efx_nic *efx) +{ + efx_dword_t reg; + int count; + + EFX_POPULATE_DWORD_1(reg, XM_CORE_RST, 1); + falcon_xmac_writel(efx, ®, XM_GLB_CFG_REG_MAC); + + for (count = 0; count < 10000; count++) { /* wait upto 100ms */ + falcon_xmac_readl(efx, ®, XM_GLB_CFG_REG_MAC); + if (EFX_DWORD_FIELD(reg, XM_CORE_RST) == 0) + return 0; + udelay(10); + } + + EFX_ERR(efx, "timed out waiting for XMAC core reset\n"); + return -ETIMEDOUT; +} + +/* Configure the XAUI driver that is an output from Falcon */ +static void falcon_setup_xaui(struct efx_nic *efx) +{ + efx_dword_t sdctl, txdrv; + + /* Move the XAUI into low power, unless there is no PHY, in + * which case the XAUI will have to drive a cable. */ + if (efx->phy_type == PHY_TYPE_NONE) + return; + + falcon_xmac_readl(efx, &sdctl, XX_SD_CTL_REG_MAC); + EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVD, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_LODRVD, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVC, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_LODRVC, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVB, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_LODRVB, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVA, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_LODRVA, XX_SD_CTL_DRV_DEFAULT); + falcon_xmac_writel(efx, &sdctl, XX_SD_CTL_REG_MAC); + + EFX_POPULATE_DWORD_8(txdrv, + XX_DEQD, XX_TXDRV_DEQ_DEFAULT, + XX_DEQC, XX_TXDRV_DEQ_DEFAULT, + XX_DEQB, XX_TXDRV_DEQ_DEFAULT, + XX_DEQA, XX_TXDRV_DEQ_DEFAULT, + XX_DTXD, XX_TXDRV_DTX_DEFAULT, + XX_DTXC, XX_TXDRV_DTX_DEFAULT, + XX_DTXB, XX_TXDRV_DTX_DEFAULT, + XX_DTXA, XX_TXDRV_DTX_DEFAULT); + falcon_xmac_writel(efx, &txdrv, XX_TXDRV_CTL_REG_MAC); +} + +static void falcon_hold_xaui_in_rst(struct efx_nic *efx) +{ + efx_dword_t reg; + + EFX_ZERO_DWORD(reg); + EFX_SET_DWORD_FIELD(reg, XX_PWRDNA_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_PWRDNB_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_PWRDNC_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_PWRDND_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RSTPLLAB_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RSTPLLCD_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RESETA_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RESETB_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RESETC_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RESETD_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSRX_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSTX_EN, 1); + falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); +} + +static int _falcon_reset_xaui_a(struct efx_nic *efx) +{ + efx_dword_t reg; + + falcon_hold_xaui_in_rst(efx); + falcon_xmac_readl(efx, ®, XX_PWR_RST_REG_MAC); + + /* Follow the RAMBUS XAUI data reset sequencing + * Channels A and B first: power down, reset PLL, reset, clear + */ + EFX_SET_DWORD_FIELD(reg, XX_PWRDNA_EN, 0); + EFX_SET_DWORD_FIELD(reg, XX_PWRDNB_EN, 0); + falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + EFX_SET_DWORD_FIELD(reg, XX_RSTPLLAB_EN, 0); + falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + EFX_SET_DWORD_FIELD(reg, XX_RESETA_EN, 0); + EFX_SET_DWORD_FIELD(reg, XX_RESETB_EN, 0); + falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + /* Channels C and D: power down, reset PLL, reset, clear */ + EFX_SET_DWORD_FIELD(reg, XX_PWRDNC_EN, 0); + EFX_SET_DWORD_FIELD(reg, XX_PWRDND_EN, 0); + falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + EFX_SET_DWORD_FIELD(reg, XX_RSTPLLCD_EN, 0); + falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + EFX_SET_DWORD_FIELD(reg, XX_RESETC_EN, 0); + EFX_SET_DWORD_FIELD(reg, XX_RESETD_EN, 0); + falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + /* Setup XAUI */ + falcon_setup_xaui(efx); + udelay(10); + + /* Take XGXS out of reset */ + EFX_ZERO_DWORD(reg); + falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + return 0; +} + +static int _falcon_reset_xaui_b(struct efx_nic *efx) +{ + efx_dword_t reg; + int count; + + EFX_POPULATE_DWORD_1(reg, XX_RST_XX_EN, 1); + falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC); + + /* Give some time for the link to establish */ + for (count = 0; count < 1000; count++) { /* wait upto 10ms */ + falcon_xmac_readl(efx, ®, XX_PWR_RST_REG_MAC); + if (EFX_DWORD_FIELD(reg, XX_RST_XX_EN) == 0) { + falcon_setup_xaui(efx); + return 0; + } + udelay(10); + } + EFX_ERR(efx, "timed out waiting for XAUI/XGXS reset\n"); + return -ETIMEDOUT; +} + +int falcon_reset_xaui(struct efx_nic *efx) +{ + int rc; + + if (EFX_WORKAROUND_9388(efx)) { + falcon_hold_xaui_in_rst(efx); + efx->phy_op->reset_xaui(efx); + rc = _falcon_reset_xaui_a(efx); + } else { + rc = _falcon_reset_xaui_b(efx); + } + return rc; +} + +static int falcon_xgmii_status(struct efx_nic *efx) +{ + efx_dword_t reg; + + if (FALCON_REV(efx) < FALCON_REV_B0) + return 1; + + /* The ISR latches, so clear it and re-read */ + falcon_xmac_readl(efx, ®, XM_MGT_INT_REG_MAC_B0); + falcon_xmac_readl(efx, ®, XM_MGT_INT_REG_MAC_B0); + + if (EFX_DWORD_FIELD(reg, XM_LCLFLT) || + EFX_DWORD_FIELD(reg, XM_RMTFLT)) { + EFX_INFO(efx, "MGT_INT: "EFX_DWORD_FMT"\n", EFX_DWORD_VAL(reg)); + return 0; + } + + return 1; +} + +static void falcon_mask_status_intr(struct efx_nic *efx, int enable) +{ + efx_dword_t reg; + + if (FALCON_REV(efx) < FALCON_REV_B0) + return; + + /* Flush the ISR */ + if (enable) + falcon_xmac_readl(efx, ®, XM_MGT_INT_REG_MAC_B0); + + EFX_POPULATE_DWORD_2(reg, + XM_MSK_RMTFLT, !enable, + XM_MSK_LCLFLT, !enable); + falcon_xmac_writel(efx, ®, XM_MGT_INT_MSK_REG_MAC_B0); +} + +int falcon_init_xmac(struct efx_nic *efx) +{ + int rc; + + /* Initialize the PHY first so the clock is around */ + rc = efx->phy_op->init(efx); + if (rc) + goto fail1; + + rc = falcon_reset_xaui(efx); + if (rc) + goto fail2; + + /* Wait again. Give the PHY and MAC time to come back */ + schedule_timeout_uninterruptible(HZ / 10); + + rc = falcon_reset_xmac(efx); + if (rc) + goto fail2; + + falcon_mask_status_intr(efx, 1); + return 0; + + fail2: + efx->phy_op->fini(efx); + fail1: + return rc; +} + +int falcon_xaui_link_ok(struct efx_nic *efx) +{ + efx_dword_t reg; + int align_done, sync_status, link_ok = 0; + + /* Read link status */ + falcon_xmac_readl(efx, ®, XX_CORE_STAT_REG_MAC); + + align_done = EFX_DWORD_FIELD(reg, XX_ALIGN_DONE); + sync_status = EFX_DWORD_FIELD(reg, XX_SYNC_STAT); + if (align_done && (sync_status == XX_SYNC_STAT_DECODE_SYNCED)) + link_ok = 1; + + /* Clear link status ready for next read */ + EFX_SET_DWORD_FIELD(reg, XX_COMMA_DET, XX_COMMA_DET_RESET); + EFX_SET_DWORD_FIELD(reg, XX_CHARERR, XX_CHARERR_RESET); + EFX_SET_DWORD_FIELD(reg, XX_DISPERR, XX_DISPERR_RESET); + falcon_xmac_writel(efx, ®, XX_CORE_STAT_REG_MAC); + + /* If the link is up, then check the phy side of the xaui link + * (error conditions from the wire side propoagate back through + * the phy to the xaui side). */ + if (efx->link_up && link_ok) { + int has_phyxs = efx->phy_op->mmds & (1 << MDIO_MMD_PHYXS); + if (has_phyxs) + link_ok = mdio_clause45_phyxgxs_lane_sync(efx); + } + + /* If the PHY and XAUI links are up, then check the mac's xgmii + * fault state */ + if (efx->link_up && link_ok) + link_ok = falcon_xgmii_status(efx); + + return link_ok; +} + +static void falcon_reconfigure_xmac_core(struct efx_nic *efx) +{ + unsigned int max_frame_len; + efx_dword_t reg; + int rx_fc = (efx->flow_control & EFX_FC_RX) ? 1 : 0; + + /* Configure MAC - cut-thru mode is hard wired on */ + EFX_POPULATE_DWORD_3(reg, + XM_RX_JUMBO_MODE, 1, + XM_TX_STAT_EN, 1, + XM_RX_STAT_EN, 1); + falcon_xmac_writel(efx, ®, XM_GLB_CFG_REG_MAC); + + /* Configure TX */ + EFX_POPULATE_DWORD_6(reg, + XM_TXEN, 1, + XM_TX_PRMBL, 1, + XM_AUTO_PAD, 1, + XM_TXCRC, 1, + XM_FCNTL, 1, + XM_IPG, 0x3); + falcon_xmac_writel(efx, ®, XM_TX_CFG_REG_MAC); + + /* Configure RX */ + EFX_POPULATE_DWORD_5(reg, + XM_RXEN, 1, + XM_AUTO_DEPAD, 0, + XM_ACPT_ALL_MCAST, 1, + XM_ACPT_ALL_UCAST, efx->promiscuous, + XM_PASS_CRC_ERR, 1); + falcon_xmac_writel(efx, ®, XM_RX_CFG_REG_MAC); + + /* Set frame length */ + max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu); + EFX_POPULATE_DWORD_1(reg, XM_MAX_RX_FRM_SIZE, max_frame_len); + falcon_xmac_writel(efx, ®, XM_RX_PARAM_REG_MAC); + EFX_POPULATE_DWORD_2(reg, + XM_MAX_TX_FRM_SIZE, max_frame_len, + XM_TX_JUMBO_MODE, 1); + falcon_xmac_writel(efx, ®, XM_TX_PARAM_REG_MAC); + + EFX_POPULATE_DWORD_2(reg, + XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */ + XM_DIS_FCNTL, rx_fc ? 0 : 1); + falcon_xmac_writel(efx, ®, XM_FC_REG_MAC); + + /* Set MAC address */ + EFX_POPULATE_DWORD_4(reg, + XM_ADR_0, efx->net_dev->dev_addr[0], + XM_ADR_1, efx->net_dev->dev_addr[1], + XM_ADR_2, efx->net_dev->dev_addr[2], + XM_ADR_3, efx->net_dev->dev_addr[3]); + falcon_xmac_writel(efx, ®, XM_ADR_LO_REG_MAC); + EFX_POPULATE_DWORD_2(reg, + XM_ADR_4, efx->net_dev->dev_addr[4], + XM_ADR_5, efx->net_dev->dev_addr[5]); + falcon_xmac_writel(efx, ®, XM_ADR_HI_REG_MAC); +} + +/* Try and bring the Falcon side of the Falcon-Phy XAUI link fails + * to come back up. Bash it until it comes back up */ +static int falcon_check_xaui_link_up(struct efx_nic *efx) +{ + int max_tries, tries; + tries = EFX_WORKAROUND_5147(efx) ? 5 : 1; + max_tries = tries; + + if (efx->phy_type == PHY_TYPE_NONE) + return 0; + + while (tries) { + if (falcon_xaui_link_ok(efx)) + return 1; + + EFX_LOG(efx, "%s Clobbering XAUI (%d tries left).\n", + __func__, tries); + (void) falcon_reset_xaui(efx); + udelay(200); + tries--; + } + + EFX_ERR(efx, "Failed to bring XAUI link back up in %d tries!\n", + max_tries); + return 0; +} + +void falcon_reconfigure_xmac(struct efx_nic *efx) +{ + int xaui_link_ok; + + falcon_mask_status_intr(efx, 0); + + falcon_deconfigure_mac_wrapper(efx); + efx->phy_op->reconfigure(efx); + falcon_reconfigure_xmac_core(efx); + falcon_reconfigure_mac_wrapper(efx); + + /* Ensure XAUI link is up */ + xaui_link_ok = falcon_check_xaui_link_up(efx); + + if (xaui_link_ok && efx->link_up) + falcon_mask_status_intr(efx, 1); +} + +void falcon_fini_xmac(struct efx_nic *efx) +{ + /* Isolate the MAC - PHY */ + falcon_deconfigure_mac_wrapper(efx); + + /* Potentially power down the PHY */ + efx->phy_op->fini(efx); +} + +void falcon_update_stats_xmac(struct efx_nic *efx) +{ + struct efx_mac_stats *mac_stats = &efx->mac_stats; + int rc; + + rc = falcon_dma_stats(efx, XgDmaDone_offset); + if (rc) + return; + + /* Update MAC stats from DMAed values */ + FALCON_STAT(efx, XgRxOctets, rx_bytes); + FALCON_STAT(efx, XgRxOctetsOK, rx_good_bytes); + FALCON_STAT(efx, XgRxPkts, rx_packets); + FALCON_STAT(efx, XgRxPktsOK, rx_good); + FALCON_STAT(efx, XgRxBroadcastPkts, rx_broadcast); + FALCON_STAT(efx, XgRxMulticastPkts, rx_multicast); + FALCON_STAT(efx, XgRxUnicastPkts, rx_unicast); + FALCON_STAT(efx, XgRxUndersizePkts, rx_lt64); + FALCON_STAT(efx, XgRxOversizePkts, rx_gtjumbo); + FALCON_STAT(efx, XgRxJabberPkts, rx_bad_gtjumbo); + FALCON_STAT(efx, XgRxUndersizeFCSerrorPkts, rx_bad_lt64); + FALCON_STAT(efx, XgRxDropEvents, rx_overflow); + FALCON_STAT(efx, XgRxFCSerrorPkts, rx_bad); + FALCON_STAT(efx, XgRxAlignError, rx_align_error); + FALCON_STAT(efx, XgRxSymbolError, rx_symbol_error); + FALCON_STAT(efx, XgRxInternalMACError, rx_internal_error); + FALCON_STAT(efx, XgRxControlPkts, rx_control); + FALCON_STAT(efx, XgRxPausePkts, rx_pause); + FALCON_STAT(efx, XgRxPkts64Octets, rx_64); + FALCON_STAT(efx, XgRxPkts65to127Octets, rx_65_to_127); + FALCON_STAT(efx, XgRxPkts128to255Octets, rx_128_to_255); + FALCON_STAT(efx, XgRxPkts256to511Octets, rx_256_to_511); + FALCON_STAT(efx, XgRxPkts512to1023Octets, rx_512_to_1023); + FALCON_STAT(efx, XgRxPkts1024to15xxOctets, rx_1024_to_15xx); + FALCON_STAT(efx, XgRxPkts15xxtoMaxOctets, rx_15xx_to_jumbo); + FALCON_STAT(efx, XgRxLengthError, rx_length_error); + FALCON_STAT(efx, XgTxPkts, tx_packets); + FALCON_STAT(efx, XgTxOctets, tx_bytes); + FALCON_STAT(efx, XgTxMulticastPkts, tx_multicast); + FALCON_STAT(efx, XgTxBroadcastPkts, tx_broadcast); + FALCON_STAT(efx, XgTxUnicastPkts, tx_unicast); + FALCON_STAT(efx, XgTxControlPkts, tx_control); + FALCON_STAT(efx, XgTxPausePkts, tx_pause); + FALCON_STAT(efx, XgTxPkts64Octets, tx_64); + FALCON_STAT(efx, XgTxPkts65to127Octets, tx_65_to_127); + FALCON_STAT(efx, XgTxPkts128to255Octets, tx_128_to_255); + FALCON_STAT(efx, XgTxPkts256to511Octets, tx_256_to_511); + FALCON_STAT(efx, XgTxPkts512to1023Octets, tx_512_to_1023); + FALCON_STAT(efx, XgTxPkts1024to15xxOctets, tx_1024_to_15xx); + FALCON_STAT(efx, XgTxPkts1519toMaxOctets, tx_15xx_to_jumbo); + FALCON_STAT(efx, XgTxUndersizePkts, tx_lt64); + FALCON_STAT(efx, XgTxOversizePkts, tx_gtjumbo); + FALCON_STAT(efx, XgTxNonTcpUdpPkt, tx_non_tcpudp); + FALCON_STAT(efx, XgTxMacSrcErrPkt, tx_mac_src_error); + FALCON_STAT(efx, XgTxIpSrcErrPkt, tx_ip_src_error); + + /* Update derived statistics */ + mac_stats->tx_good_bytes = + (mac_stats->tx_bytes - mac_stats->tx_bad_bytes); + mac_stats->rx_bad_bytes = + (mac_stats->rx_bytes - mac_stats->rx_good_bytes); +} + +#define EFX_XAUI_RETRAIN_MAX 8 + +int falcon_check_xmac(struct efx_nic *efx) +{ + unsigned xaui_link_ok; + int rc; + + falcon_mask_status_intr(efx, 0); + xaui_link_ok = falcon_xaui_link_ok(efx); + + if (EFX_WORKAROUND_5147(efx) && !xaui_link_ok) + (void) falcon_reset_xaui(efx); + + /* Call the PHY check_hw routine */ + rc = efx->phy_op->check_hw(efx); + + /* Unmask interrupt if everything was (and still is) ok */ + if (xaui_link_ok && efx->link_up) + falcon_mask_status_intr(efx, 1); + + return rc; +} + +/* Simulate a PHY event */ +void falcon_xmac_sim_phy_event(struct efx_nic *efx) +{ + efx_qword_t phy_event; + + EFX_POPULATE_QWORD_2(phy_event, + EV_CODE, GLOBAL_EV_DECODE, + XG_PHY_INTR, 1); + falcon_generate_event(&efx->channel[0], &phy_event); +} + +int falcon_xmac_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + mdio_clause45_get_settings(efx, ecmd); + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = efx->mii.phy_id; + ecmd->autoneg = AUTONEG_DISABLE; + ecmd->duplex = DUPLEX_FULL; + return 0; +} + +int falcon_xmac_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + if (ecmd->transceiver != XCVR_INTERNAL) + return -EINVAL; + if (ecmd->autoneg != AUTONEG_DISABLE) + return -EINVAL; + if (ecmd->duplex != DUPLEX_FULL) + return -EINVAL; + + return mdio_clause45_set_settings(efx, ecmd); +} + + +int falcon_xmac_set_pause(struct efx_nic *efx, enum efx_fc_type flow_control) +{ + int reset; + + if (flow_control & EFX_FC_AUTO) { + EFX_LOG(efx, "10G does not support flow control " + "autonegotiation\n"); + return -EINVAL; + } + + if ((flow_control & EFX_FC_TX) && !(flow_control & EFX_FC_RX)) + return -EINVAL; + + /* TX flow control may automatically turn itself off if the + * link partner (intermittently) stops responding to pause + * frames. There isn't any indication that this has happened, + * so the best we do is leave it up to the user to spot this + * and fix it be cycling transmit flow control on this end. */ + reset = ((flow_control & EFX_FC_TX) && + !(efx->flow_control & EFX_FC_TX)); + if (EFX_WORKAROUND_11482(efx) && reset) { + if (FALCON_REV(efx) >= FALCON_REV_B0) { + /* Recover by resetting the EM block */ + if (efx->link_up) + falcon_drain_tx_fifo(efx); + } else { + /* Schedule a reset to recover */ + efx_schedule_reset(efx, RESET_TYPE_INVISIBLE); + } + } + + efx->flow_control = flow_control; + + return 0; +} diff --git a/drivers/net/sfc/gmii.h b/drivers/net/sfc/gmii.h new file mode 100644 index 00000000000..d25bbd1297f --- /dev/null +++ b/drivers/net/sfc/gmii.h @@ -0,0 +1,195 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_GMII_H +#define EFX_GMII_H + +/* + * GMII interface + */ + +#include <linux/mii.h> + +/* GMII registers, excluding registers already defined as MII + * registers in mii.h + */ +#define GMII_IER 0x12 /* Interrupt enable register */ +#define GMII_ISR 0x13 /* Interrupt status register */ + +/* Interrupt enable register */ +#define IER_ANEG_ERR 0x8000 /* Bit 15 - autonegotiation error */ +#define IER_SPEED_CHG 0x4000 /* Bit 14 - speed changed */ +#define IER_DUPLEX_CHG 0x2000 /* Bit 13 - duplex changed */ +#define IER_PAGE_RCVD 0x1000 /* Bit 12 - page received */ +#define IER_ANEG_DONE 0x0800 /* Bit 11 - autonegotiation complete */ +#define IER_LINK_CHG 0x0400 /* Bit 10 - link status changed */ +#define IER_SYM_ERR 0x0200 /* Bit 9 - symbol error */ +#define IER_FALSE_CARRIER 0x0100 /* Bit 8 - false carrier */ +#define IER_FIFO_ERR 0x0080 /* Bit 7 - FIFO over/underflow */ +#define IER_MDIX_CHG 0x0040 /* Bit 6 - MDI crossover changed */ +#define IER_DOWNSHIFT 0x0020 /* Bit 5 - downshift */ +#define IER_ENERGY 0x0010 /* Bit 4 - energy detect */ +#define IER_DTE_POWER 0x0004 /* Bit 2 - DTE power detect */ +#define IER_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */ +#define IER_JABBER 0x0001 /* Bit 0 - jabber */ + +/* Interrupt status register */ +#define ISR_ANEG_ERR 0x8000 /* Bit 15 - autonegotiation error */ +#define ISR_SPEED_CHG 0x4000 /* Bit 14 - speed changed */ +#define ISR_DUPLEX_CHG 0x2000 /* Bit 13 - duplex changed */ +#define ISR_PAGE_RCVD 0x1000 /* Bit 12 - page received */ +#define ISR_ANEG_DONE 0x0800 /* Bit 11 - autonegotiation complete */ +#define ISR_LINK_CHG 0x0400 /* Bit 10 - link status changed */ +#define ISR_SYM_ERR 0x0200 /* Bit 9 - symbol error */ +#define ISR_FALSE_CARRIER 0x0100 /* Bit 8 - false carrier */ +#define ISR_FIFO_ERR 0x0080 /* Bit 7 - FIFO over/underflow */ +#define ISR_MDIX_CHG 0x0040 /* Bit 6 - MDI crossover changed */ +#define ISR_DOWNSHIFT 0x0020 /* Bit 5 - downshift */ +#define ISR_ENERGY 0x0010 /* Bit 4 - energy detect */ +#define ISR_DTE_POWER 0x0004 /* Bit 2 - DTE power detect */ +#define ISR_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */ +#define ISR_JABBER 0x0001 /* Bit 0 - jabber */ + +/* Logically extended advertisement register */ +#define GM_ADVERTISE_SLCT ADVERTISE_SLCT +#define GM_ADVERTISE_CSMA ADVERTISE_CSMA +#define GM_ADVERTISE_10HALF ADVERTISE_10HALF +#define GM_ADVERTISE_1000XFULL ADVERTISE_1000XFULL +#define GM_ADVERTISE_10FULL ADVERTISE_10FULL +#define GM_ADVERTISE_1000XHALF ADVERTISE_1000XHALF +#define GM_ADVERTISE_100HALF ADVERTISE_100HALF +#define GM_ADVERTISE_1000XPAUSE ADVERTISE_1000XPAUSE +#define GM_ADVERTISE_100FULL ADVERTISE_100FULL +#define GM_ADVERTISE_1000XPSE_ASYM ADVERTISE_1000XPSE_ASYM +#define GM_ADVERTISE_100BASE4 ADVERTISE_100BASE4 +#define GM_ADVERTISE_PAUSE_CAP ADVERTISE_PAUSE_CAP +#define GM_ADVERTISE_PAUSE_ASYM ADVERTISE_PAUSE_ASYM +#define GM_ADVERTISE_RESV ADVERTISE_RESV +#define GM_ADVERTISE_RFAULT ADVERTISE_RFAULT +#define GM_ADVERTISE_LPACK ADVERTISE_LPACK +#define GM_ADVERTISE_NPAGE ADVERTISE_NPAGE +#define GM_ADVERTISE_1000FULL (ADVERTISE_1000FULL << 8) +#define GM_ADVERTISE_1000HALF (ADVERTISE_1000HALF << 8) +#define GM_ADVERTISE_1000 (GM_ADVERTISE_1000FULL | \ + GM_ADVERTISE_1000HALF) +#define GM_ADVERTISE_FULL (GM_ADVERTISE_1000FULL | \ + ADVERTISE_FULL) +#define GM_ADVERTISE_ALL (GM_ADVERTISE_1000FULL | \ + GM_ADVERTISE_1000HALF | \ + ADVERTISE_ALL) + +/* Logically extended link partner ability register */ +#define GM_LPA_SLCT LPA_SLCT +#define GM_LPA_10HALF LPA_10HALF +#define GM_LPA_1000XFULL LPA_1000XFULL +#define GM_LPA_10FULL LPA_10FULL +#define GM_LPA_1000XHALF LPA_1000XHALF +#define GM_LPA_100HALF LPA_100HALF +#define GM_LPA_1000XPAUSE LPA_1000XPAUSE +#define GM_LPA_100FULL LPA_100FULL +#define GM_LPA_1000XPAUSE_ASYM LPA_1000XPAUSE_ASYM +#define GM_LPA_100BASE4 LPA_100BASE4 +#define GM_LPA_PAUSE_CAP LPA_PAUSE_CAP +#define GM_LPA_PAUSE_ASYM LPA_PAUSE_ASYM +#define GM_LPA_RESV LPA_RESV +#define GM_LPA_RFAULT LPA_RFAULT +#define GM_LPA_LPACK LPA_LPACK +#define GM_LPA_NPAGE LPA_NPAGE +#define GM_LPA_1000FULL (LPA_1000FULL << 6) +#define GM_LPA_1000HALF (LPA_1000HALF << 6) +#define GM_LPA_10000FULL 0x00040000 +#define GM_LPA_10000HALF 0x00080000 +#define GM_LPA_DUPLEX (GM_LPA_1000FULL | GM_LPA_10000FULL \ + | LPA_DUPLEX) +#define GM_LPA_10 (LPA_10FULL | LPA_10HALF) +#define GM_LPA_100 LPA_100 +#define GM_LPA_1000 (GM_LPA_1000FULL | GM_LPA_1000HALF) +#define GM_LPA_10000 (GM_LPA_10000FULL | GM_LPA_10000HALF) + +/* Retrieve GMII autonegotiation advertised abilities + * + * The MII advertisment register (MII_ADVERTISE) is logically extended + * to include advertisement bits ADVERTISE_1000FULL and + * ADVERTISE_1000HALF from MII_CTRL1000. The result can be tested + * against the GM_ADVERTISE_xxx constants. + */ +static inline unsigned int gmii_advertised(struct mii_if_info *gmii) +{ + unsigned int advertise; + unsigned int ctrl1000; + + advertise = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_ADVERTISE); + ctrl1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_CTRL1000); + return (((ctrl1000 << 8) & GM_ADVERTISE_1000) | advertise); +} + +/* Retrieve GMII autonegotiation link partner abilities + * + * The MII link partner ability register (MII_LPA) is logically + * extended by adding bits LPA_1000HALF and LPA_1000FULL from + * MII_STAT1000. The result can be tested against the GM_LPA_xxx + * constants. + */ +static inline unsigned int gmii_lpa(struct mii_if_info *gmii) +{ + unsigned int lpa; + unsigned int stat1000; + + lpa = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_LPA); + stat1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_STAT1000); + return (((stat1000 << 6) & GM_LPA_1000) | lpa); +} + +/* Calculate GMII autonegotiated link technology + * + * "negotiated" should be the result of gmii_advertised() logically + * ANDed with the result of gmii_lpa(). + * + * "tech" will be negotiated with the unused bits masked out. For + * example, if both ends of the link are capable of both + * GM_LPA_1000FULL and GM_LPA_100FULL, GM_LPA_100FULL will be masked + * out. + */ +static inline unsigned int gmii_nway_result(unsigned int negotiated) +{ + unsigned int other_bits; + + /* Mask out the speed and duplexity bits */ + other_bits = negotiated & ~(GM_LPA_10 | GM_LPA_100 | GM_LPA_1000); + + if (negotiated & GM_LPA_1000FULL) + return (other_bits | GM_LPA_1000FULL); + else if (negotiated & GM_LPA_1000HALF) + return (other_bits | GM_LPA_1000HALF); + else + return (other_bits | mii_nway_result(negotiated)); +} + +/* Calculate GMII non-autonegotiated link technology + * + * This provides an equivalent to gmii_nway_result for the case when + * autonegotiation is disabled. + */ +static inline unsigned int gmii_forced_result(unsigned int bmcr) +{ + unsigned int result; + int full_duplex; + + full_duplex = bmcr & BMCR_FULLDPLX; + if (bmcr & BMCR_SPEED1000) + result = full_duplex ? GM_LPA_1000FULL : GM_LPA_1000HALF; + else if (bmcr & BMCR_SPEED100) + result = full_duplex ? GM_LPA_100FULL : GM_LPA_100HALF; + else + result = full_duplex ? GM_LPA_10FULL : GM_LPA_10HALF; + return result; +} + +#endif /* EFX_GMII_H */ diff --git a/drivers/net/sfc/i2c-direct.c b/drivers/net/sfc/i2c-direct.c new file mode 100644 index 00000000000..b6c62d0ed9c --- /dev/null +++ b/drivers/net/sfc/i2c-direct.c @@ -0,0 +1,381 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include <linux/delay.h> +#include "net_driver.h" +#include "i2c-direct.h" + +/* + * I2C data (SDA) and clock (SCL) line read/writes with appropriate + * delays. + */ + +static inline void setsda(struct efx_i2c_interface *i2c, int state) +{ + udelay(i2c->op->udelay); + i2c->sda = state; + i2c->op->setsda(i2c); + udelay(i2c->op->udelay); +} + +static inline void setscl(struct efx_i2c_interface *i2c, int state) +{ + udelay(i2c->op->udelay); + i2c->scl = state; + i2c->op->setscl(i2c); + udelay(i2c->op->udelay); +} + +static inline int getsda(struct efx_i2c_interface *i2c) +{ + int sda; + + udelay(i2c->op->udelay); + sda = i2c->op->getsda(i2c); + udelay(i2c->op->udelay); + return sda; +} + +static inline int getscl(struct efx_i2c_interface *i2c) +{ + int scl; + + udelay(i2c->op->udelay); + scl = i2c->op->getscl(i2c); + udelay(i2c->op->udelay); + return scl; +} + +/* + * I2C low-level protocol operations + * + */ + +static inline void i2c_release(struct efx_i2c_interface *i2c) +{ + EFX_WARN_ON_PARANOID(!i2c->scl); + EFX_WARN_ON_PARANOID(!i2c->sda); + /* Devices may time out if operations do not end */ + setscl(i2c, 1); + setsda(i2c, 1); + EFX_BUG_ON_PARANOID(getsda(i2c) != 1); + EFX_BUG_ON_PARANOID(getscl(i2c) != 1); +} + +static inline void i2c_start(struct efx_i2c_interface *i2c) +{ + /* We may be restarting immediately after a {send,recv}_bit, + * so SCL will not necessarily already be high. + */ + EFX_WARN_ON_PARANOID(!i2c->sda); + setscl(i2c, 1); + setsda(i2c, 0); + setscl(i2c, 0); + setsda(i2c, 1); +} + +static inline void i2c_send_bit(struct efx_i2c_interface *i2c, int bit) +{ + EFX_WARN_ON_PARANOID(i2c->scl != 0); + setsda(i2c, bit); + setscl(i2c, 1); + setscl(i2c, 0); + setsda(i2c, 1); +} + +static inline int i2c_recv_bit(struct efx_i2c_interface *i2c) +{ + int bit; + + EFX_WARN_ON_PARANOID(i2c->scl != 0); + EFX_WARN_ON_PARANOID(!i2c->sda); + setscl(i2c, 1); + bit = getsda(i2c); + setscl(i2c, 0); + return bit; +} + +static inline void i2c_stop(struct efx_i2c_interface *i2c) +{ + EFX_WARN_ON_PARANOID(i2c->scl != 0); + setsda(i2c, 0); + setscl(i2c, 1); + setsda(i2c, 1); +} + +/* + * I2C mid-level protocol operations + * + */ + +/* Sends a byte via the I2C bus and checks for an acknowledgement from + * the slave device. + */ +static int i2c_send_byte(struct efx_i2c_interface *i2c, u8 byte) +{ + int i; + + /* Send byte */ + for (i = 0; i < 8; i++) { + i2c_send_bit(i2c, !!(byte & 0x80)); + byte <<= 1; + } + + /* Check for acknowledgement from slave */ + return (i2c_recv_bit(i2c) == 0 ? 0 : -EIO); +} + +/* Receives a byte via the I2C bus and sends ACK/NACK to the slave device. */ +static u8 i2c_recv_byte(struct efx_i2c_interface *i2c, int ack) +{ + u8 value = 0; + int i; + + /* Receive byte */ + for (i = 0; i < 8; i++) + value = (value << 1) | i2c_recv_bit(i2c); + + /* Send ACK/NACK */ + i2c_send_bit(i2c, (ack ? 0 : 1)); + + return value; +} + +/* Calculate command byte for a read operation */ +static inline u8 i2c_read_cmd(u8 device_id) +{ + return ((device_id << 1) | 1); +} + +/* Calculate command byte for a write operation */ +static inline u8 i2c_write_cmd(u8 device_id) +{ + return ((device_id << 1) | 0); +} + +int efx_i2c_check_presence(struct efx_i2c_interface *i2c, u8 device_id) +{ + int rc; + + /* If someone is driving the bus low we just give up. */ + if (getsda(i2c) == 0 || getscl(i2c) == 0) { + EFX_ERR(i2c->efx, "%s someone is holding the I2C bus low." + " Giving up.\n", __func__); + return -EFAULT; + } + + /* Pretend to initiate a device write */ + i2c_start(i2c); + rc = i2c_send_byte(i2c, i2c_write_cmd(device_id)); + if (rc) + goto out; + + out: + i2c_stop(i2c); + i2c_release(i2c); + + return rc; +} + +/* This performs a fast read of one or more consecutive bytes from an + * I2C device. Not all devices support consecutive reads of more than + * one byte; for these devices use efx_i2c_read() instead. + */ +int efx_i2c_fast_read(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, u8 *data, unsigned int len) +{ + int i; + int rc; + + EFX_WARN_ON_PARANOID(getsda(i2c) != 1); + EFX_WARN_ON_PARANOID(getscl(i2c) != 1); + EFX_WARN_ON_PARANOID(data == NULL); + EFX_WARN_ON_PARANOID(len < 1); + + /* Select device and starting offset */ + i2c_start(i2c); + rc = i2c_send_byte(i2c, i2c_write_cmd(device_id)); + if (rc) + goto out; + rc = i2c_send_byte(i2c, offset); + if (rc) + goto out; + + /* Read data from device */ + i2c_start(i2c); + rc = i2c_send_byte(i2c, i2c_read_cmd(device_id)); + if (rc) + goto out; + for (i = 0; i < (len - 1); i++) + /* Read and acknowledge all but the last byte */ + data[i] = i2c_recv_byte(i2c, 1); + /* Read last byte with no acknowledgement */ + data[i] = i2c_recv_byte(i2c, 0); + + out: + i2c_stop(i2c); + i2c_release(i2c); + + return rc; +} + +/* This performs a fast write of one or more consecutive bytes to an + * I2C device. Not all devices support consecutive writes of more + * than one byte; for these devices use efx_i2c_write() instead. + */ +int efx_i2c_fast_write(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, + const u8 *data, unsigned int len) +{ + int i; + int rc; + + EFX_WARN_ON_PARANOID(getsda(i2c) != 1); + EFX_WARN_ON_PARANOID(getscl(i2c) != 1); + EFX_WARN_ON_PARANOID(len < 1); + + /* Select device and starting offset */ + i2c_start(i2c); + rc = i2c_send_byte(i2c, i2c_write_cmd(device_id)); + if (rc) + goto out; + rc = i2c_send_byte(i2c, offset); + if (rc) + goto out; + + /* Write data to device */ + for (i = 0; i < len; i++) { + rc = i2c_send_byte(i2c, data[i]); + if (rc) + goto out; + } + + out: + i2c_stop(i2c); + i2c_release(i2c); + + return rc; +} + +/* I2C byte-by-byte read */ +int efx_i2c_read(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, u8 *data, unsigned int len) +{ + int rc; + + /* i2c_fast_read with length 1 is a single byte read */ + for (; len > 0; offset++, data++, len--) { + rc = efx_i2c_fast_read(i2c, device_id, offset, data, 1); + if (rc) + return rc; + } + + return 0; +} + +/* I2C byte-by-byte write */ +int efx_i2c_write(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, const u8 *data, unsigned int len) +{ + int rc; + + /* i2c_fast_write with length 1 is a single byte write */ + for (; len > 0; offset++, data++, len--) { + rc = efx_i2c_fast_write(i2c, device_id, offset, data, 1); + if (rc) + return rc; + mdelay(i2c->op->mdelay); + } + + return 0; +} + + +/* This is just a slightly neater wrapper round efx_i2c_fast_write + * in the case where the target doesn't take an offset + */ +int efx_i2c_send_bytes(struct efx_i2c_interface *i2c, + u8 device_id, const u8 *data, unsigned int len) +{ + return efx_i2c_fast_write(i2c, device_id, data[0], data + 1, len - 1); +} + +/* I2C receiving of bytes - does not send an offset byte */ +int efx_i2c_recv_bytes(struct efx_i2c_interface *i2c, u8 device_id, + u8 *bytes, unsigned int len) +{ + int i; + int rc; + + EFX_WARN_ON_PARANOID(getsda(i2c) != 1); + EFX_WARN_ON_PARANOID(getscl(i2c) != 1); + EFX_WARN_ON_PARANOID(len < 1); + + /* Select device */ + i2c_start(i2c); + + /* Read data from device */ + rc = i2c_send_byte(i2c, i2c_read_cmd(device_id)); + if (rc) + goto out; + + for (i = 0; i < (len - 1); i++) + /* Read and acknowledge all but the last byte */ + bytes[i] = i2c_recv_byte(i2c, 1); + /* Read last byte with no acknowledgement */ + bytes[i] = i2c_recv_byte(i2c, 0); + + out: + i2c_stop(i2c); + i2c_release(i2c); + + return rc; +} + +/* SMBus and some I2C devices will time out if the I2C clock is + * held low for too long. This is most likely to happen in virtualised + * systems (when the entire domain is descheduled) but could in + * principle happen due to preemption on any busy system (and given the + * potential length of an I2C operation turning preemption off is not + * a sensible option). The following functions deal with the failure by + * retrying up to a fixed number of times. + */ + +#define I2C_MAX_RETRIES (10) + +/* The timeout problem will result in -EIO. If the wrapped function + * returns any other error, pass this up and do not retry. */ +#define RETRY_WRAPPER(_f) \ + int retries = I2C_MAX_RETRIES; \ + int rc; \ + while (retries) { \ + rc = _f; \ + if (rc != -EIO) \ + return rc; \ + retries--; \ + } \ + return rc; \ + +int efx_i2c_check_presence_retry(struct efx_i2c_interface *i2c, u8 device_id) +{ + RETRY_WRAPPER(efx_i2c_check_presence(i2c, device_id)) +} + +int efx_i2c_read_retry(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, u8 *data, unsigned int len) +{ + RETRY_WRAPPER(efx_i2c_read(i2c, device_id, offset, data, len)) +} + +int efx_i2c_write_retry(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, const u8 *data, unsigned int len) +{ + RETRY_WRAPPER(efx_i2c_write(i2c, device_id, offset, data, len)) +} diff --git a/drivers/net/sfc/i2c-direct.h b/drivers/net/sfc/i2c-direct.h new file mode 100644 index 00000000000..291e561071f --- /dev/null +++ b/drivers/net/sfc/i2c-direct.h @@ -0,0 +1,91 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005 Fen Systems Ltd. + * Copyright 2006 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_I2C_DIRECT_H +#define EFX_I2C_DIRECT_H + +#include "net_driver.h" + +/* + * Direct control of an I2C bus + */ + +struct efx_i2c_interface; + +/** + * struct efx_i2c_bit_operations - I2C bus direct control methods + * + * I2C bus direct control methods. + * + * @setsda: Set state of SDA line + * @setscl: Set state of SCL line + * @getsda: Get state of SDA line + * @getscl: Get state of SCL line + * @udelay: Delay between each bit operation + * @mdelay: Delay between each byte write + */ +struct efx_i2c_bit_operations { + void (*setsda) (struct efx_i2c_interface *i2c); + void (*setscl) (struct efx_i2c_interface *i2c); + int (*getsda) (struct efx_i2c_interface *i2c); + int (*getscl) (struct efx_i2c_interface *i2c); + unsigned int udelay; + unsigned int mdelay; +}; + +/** + * struct efx_i2c_interface - an I2C interface + * + * An I2C interface. + * + * @efx: Attached Efx NIC + * @op: I2C bus control methods + * @sda: Current output state of SDA line + * @scl: Current output state of SCL line + */ +struct efx_i2c_interface { + struct efx_nic *efx; + struct efx_i2c_bit_operations *op; + unsigned int sda:1; + unsigned int scl:1; +}; + +extern int efx_i2c_check_presence(struct efx_i2c_interface *i2c, u8 device_id); +extern int efx_i2c_fast_read(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, + u8 *data, unsigned int len); +extern int efx_i2c_fast_write(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, + const u8 *data, unsigned int len); +extern int efx_i2c_read(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, u8 *data, unsigned int len); +extern int efx_i2c_write(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, + const u8 *data, unsigned int len); + +extern int efx_i2c_send_bytes(struct efx_i2c_interface *i2c, u8 device_id, + const u8 *bytes, unsigned int len); + +extern int efx_i2c_recv_bytes(struct efx_i2c_interface *i2c, u8 device_id, + u8 *bytes, unsigned int len); + + +/* Versions of the API that retry on failure. */ +extern int efx_i2c_check_presence_retry(struct efx_i2c_interface *i2c, + u8 device_id); + +extern int efx_i2c_read_retry(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, u8 *data, unsigned int len); + +extern int efx_i2c_write_retry(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, + const u8 *data, unsigned int len); + +#endif /* EFX_I2C_DIRECT_H */ diff --git a/drivers/net/sfc/mac.h b/drivers/net/sfc/mac.h new file mode 100644 index 00000000000..edd07d4dee1 --- /dev/null +++ b/drivers/net/sfc/mac.h @@ -0,0 +1,33 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2007 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_MAC_H +#define EFX_MAC_H + +#include "net_driver.h" + +extern void falcon_xmac_writel(struct efx_nic *efx, + efx_dword_t *value, unsigned int mac_reg); +extern void falcon_xmac_readl(struct efx_nic *efx, + efx_dword_t *value, unsigned int mac_reg); +extern int falcon_init_xmac(struct efx_nic *efx); +extern void falcon_reconfigure_xmac(struct efx_nic *efx); +extern void falcon_update_stats_xmac(struct efx_nic *efx); +extern void falcon_fini_xmac(struct efx_nic *efx); +extern int falcon_check_xmac(struct efx_nic *efx); +extern void falcon_xmac_sim_phy_event(struct efx_nic *efx); +extern int falcon_xmac_get_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd); +extern int falcon_xmac_set_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd); +extern int falcon_xmac_set_pause(struct efx_nic *efx, + enum efx_fc_type pause_params); + +#endif diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c new file mode 100644 index 00000000000..dc06bb0aa57 --- /dev/null +++ b/drivers/net/sfc/mdio_10g.c @@ -0,0 +1,282 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ +/* + * Useful functions for working with MDIO clause 45 PHYs + */ +#include <linux/types.h> +#include <linux/ethtool.h> +#include <linux/delay.h> +#include "net_driver.h" +#include "mdio_10g.h" +#include "boards.h" + +int mdio_clause45_reset_mmd(struct efx_nic *port, int mmd, + int spins, int spintime) +{ + u32 ctrl; + int phy_id = port->mii.phy_id; + + /* Catch callers passing values in the wrong units (or just silly) */ + EFX_BUG_ON_PARANOID(spins * spintime >= 5000); + + mdio_clause45_write(port, phy_id, mmd, MDIO_MMDREG_CTRL1, + (1 << MDIO_MMDREG_CTRL1_RESET_LBN)); + /* Wait for the reset bit to clear. */ + do { + msleep(spintime); + ctrl = mdio_clause45_read(port, phy_id, mmd, MDIO_MMDREG_CTRL1); + spins--; + + } while (spins && (ctrl & (1 << MDIO_MMDREG_CTRL1_RESET_LBN))); + + return spins ? spins : -ETIMEDOUT; +} + +static int mdio_clause45_check_mmd(struct efx_nic *efx, int mmd, + int fault_fatal) +{ + int status; + int phy_id = efx->mii.phy_id; + + /* Read MMD STATUS2 to check it is responding. */ + status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT2); + if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) & + ((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) != + MDIO_MMDREG_STAT2_PRESENT_VAL) { + EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd); + return -EIO; + } + + /* Read MMD STATUS 1 to check for fault. */ + status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT1); + if ((status & (1 << MDIO_MMDREG_STAT1_FAULT_LBN)) != 0) { + if (fault_fatal) { + EFX_ERR(efx, "PHY MMD %d reporting fatal" + " fault: status %x\n", mmd, status); + return -EIO; + } else { + EFX_LOG(efx, "PHY MMD %d reporting status" + " %x (expected)\n", mmd, status); + } + } + return 0; +} + +/* This ought to be ridiculous overkill. We expect it to fail rarely */ +#define MDIO45_RESET_TIME 1000 /* ms */ +#define MDIO45_RESET_ITERS 100 + +int mdio_clause45_wait_reset_mmds(struct efx_nic *efx, + unsigned int mmd_mask) +{ + const int spintime = MDIO45_RESET_TIME / MDIO45_RESET_ITERS; + int tries = MDIO45_RESET_ITERS; + int rc = 0; + int in_reset; + + while (tries) { + int mask = mmd_mask; + int mmd = 0; + int stat; + in_reset = 0; + while (mask) { + if (mask & 1) { + stat = mdio_clause45_read(efx, + efx->mii.phy_id, + mmd, + MDIO_MMDREG_CTRL1); + if (stat < 0) { + EFX_ERR(efx, "failed to read status of" + " MMD %d\n", mmd); + return -EIO; + } + if (stat & (1 << MDIO_MMDREG_CTRL1_RESET_LBN)) + in_reset |= (1 << mmd); + } + mask = mask >> 1; + mmd++; + } + if (!in_reset) + break; + tries--; + msleep(spintime); + } + if (in_reset != 0) { + EFX_ERR(efx, "not all MMDs came out of reset in time." + " MMDs still in reset: %x\n", in_reset); + rc = -ETIMEDOUT; + } + return rc; +} + +int mdio_clause45_check_mmds(struct efx_nic *efx, + unsigned int mmd_mask, unsigned int fatal_mask) +{ + int devices, mmd = 0; + int probe_mmd; + + /* Historically we have probed the PHYXS to find out what devices are + * present,but that doesn't work so well if the PHYXS isn't expected + * to exist, if so just find the first item in the list supplied. */ + probe_mmd = (mmd_mask & MDIO_MMDREG_DEVS0_PHYXS) ? MDIO_MMD_PHYXS : + __ffs(mmd_mask); + devices = mdio_clause45_read(efx, efx->mii.phy_id, + probe_mmd, MDIO_MMDREG_DEVS0); + + /* Check all the expected MMDs are present */ + if (devices < 0) { + EFX_ERR(efx, "failed to read devices present\n"); + return -EIO; + } + if ((devices & mmd_mask) != mmd_mask) { + EFX_ERR(efx, "required MMDs not present: got %x, " + "wanted %x\n", devices, mmd_mask); + return -ENODEV; + } + EFX_TRACE(efx, "Devices present: %x\n", devices); + + /* Check all required MMDs are responding and happy. */ + while (mmd_mask) { + if (mmd_mask & 1) { + int fault_fatal = fatal_mask & 1; + if (mdio_clause45_check_mmd(efx, mmd, fault_fatal)) + return -EIO; + } + mmd_mask = mmd_mask >> 1; + fatal_mask = fatal_mask >> 1; + mmd++; + } + + return 0; +} + +int mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask) +{ + int phy_id = efx->mii.phy_id; + int status; + int ok = 1; + int mmd = 0; + int good; + + while (mmd_mask) { + if (mmd_mask & 1) { + /* Double reads because link state is latched, and a + * read moves the current state into the register */ + status = mdio_clause45_read(efx, phy_id, + mmd, MDIO_MMDREG_STAT1); + status = mdio_clause45_read(efx, phy_id, + mmd, MDIO_MMDREG_STAT1); + + good = status & (1 << MDIO_MMDREG_STAT1_LINK_LBN); + ok = ok && good; + } + mmd_mask = (mmd_mask >> 1); + mmd++; + } + return ok; +} + +/** + * mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO. + * @efx: Efx NIC + * @ecmd: Buffer for settings + * + * On return the 'port', 'speed', 'supported' and 'advertising' fields of + * ecmd have been filled out based on the PMA type. + */ +void mdio_clause45_get_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd) +{ + int pma_type; + + /* If no PMA is present we are presumably talking something XAUI-ish + * like CX4. Which we report as FIBRE (see below) */ + if ((efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)) == 0) { + ecmd->speed = SPEED_10000; + ecmd->port = PORT_FIBRE; + ecmd->supported = SUPPORTED_FIBRE; + ecmd->advertising = ADVERTISED_FIBRE; + return; + } + + pma_type = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL2); + pma_type &= MDIO_PMAPMD_CTRL2_TYPE_MASK; + + switch (pma_type) { + /* We represent CX4 as fibre in the absence of anything + better. */ + case MDIO_PMAPMD_CTRL2_10G_CX4: + ecmd->speed = SPEED_10000; + ecmd->port = PORT_FIBRE; + ecmd->supported = SUPPORTED_FIBRE; + ecmd->advertising = ADVERTISED_FIBRE; + break; + /* 10G Base-T */ + case MDIO_PMAPMD_CTRL2_10G_BT: + ecmd->speed = SPEED_10000; + ecmd->port = PORT_TP; + ecmd->supported = SUPPORTED_TP | SUPPORTED_10000baseT_Full; + ecmd->advertising = (ADVERTISED_FIBRE + | ADVERTISED_10000baseT_Full); + break; + case MDIO_PMAPMD_CTRL2_1G_BT: + ecmd->speed = SPEED_1000; + ecmd->port = PORT_TP; + ecmd->supported = SUPPORTED_TP | SUPPORTED_1000baseT_Full; + ecmd->advertising = (ADVERTISED_FIBRE + | ADVERTISED_1000baseT_Full); + break; + case MDIO_PMAPMD_CTRL2_100_BT: + ecmd->speed = SPEED_100; + ecmd->port = PORT_TP; + ecmd->supported = SUPPORTED_TP | SUPPORTED_100baseT_Full; + ecmd->advertising = (ADVERTISED_FIBRE + | ADVERTISED_100baseT_Full); + break; + case MDIO_PMAPMD_CTRL2_10_BT: + ecmd->speed = SPEED_10; + ecmd->port = PORT_TP; + ecmd->supported = SUPPORTED_TP | SUPPORTED_10baseT_Full; + ecmd->advertising = ADVERTISED_FIBRE | ADVERTISED_10baseT_Full; + break; + /* All the other defined modes are flavours of + * 10G optical */ + default: + ecmd->speed = SPEED_10000; + ecmd->port = PORT_FIBRE; + ecmd->supported = SUPPORTED_FIBRE; + ecmd->advertising = ADVERTISED_FIBRE; + break; + } +} + +/** + * mdio_clause45_set_settings - Set (some of) the PHY settings over MDIO. + * @efx: Efx NIC + * @ecmd: New settings + * + * Currently this just enforces that we are _not_ changing the + * 'port', 'speed', 'supported' or 'advertising' settings as these + * cannot be changed on any currently supported PHY. + */ +int mdio_clause45_set_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd) +{ + struct ethtool_cmd tmpcmd; + mdio_clause45_get_settings(efx, &tmpcmd); + /* None of the current PHYs support more than one mode + * of operation (and only 10GBT ever will), so keep things + * simple for now */ + if ((ecmd->speed == tmpcmd.speed) && (ecmd->port == tmpcmd.port) && + (ecmd->supported == tmpcmd.supported) && + (ecmd->advertising == tmpcmd.advertising)) + return 0; + return -EOPNOTSUPP; +} diff --git a/drivers/net/sfc/mdio_10g.h b/drivers/net/sfc/mdio_10g.h new file mode 100644 index 00000000000..2214b6d820a --- /dev/null +++ b/drivers/net/sfc/mdio_10g.h @@ -0,0 +1,232 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_MDIO_10G_H +#define EFX_MDIO_10G_H + +/* + * Definitions needed for doing 10G MDIO as specified in clause 45 + * MDIO, which do not appear in Linux yet. Also some helper functions. + */ + +#include "efx.h" +#include "boards.h" + +/* Numbering of the MDIO Manageable Devices (MMDs) */ +/* Physical Medium Attachment/ Physical Medium Dependent sublayer */ +#define MDIO_MMD_PMAPMD (1) +/* WAN Interface Sublayer */ +#define MDIO_MMD_WIS (2) +/* Physical Coding Sublayer */ +#define MDIO_MMD_PCS (3) +/* PHY Extender Sublayer */ +#define MDIO_MMD_PHYXS (4) +/* Extender Sublayer */ +#define MDIO_MMD_DTEXS (5) +/* Transmission convergence */ +#define MDIO_MMD_TC (6) +/* Auto negotiation */ +#define MDIO_MMD_AN (7) + +/* Generic register locations */ +#define MDIO_MMDREG_CTRL1 (0) +#define MDIO_MMDREG_STAT1 (1) +#define MDIO_MMDREG_IDHI (2) +#define MDIO_MMDREG_IDLOW (3) +#define MDIO_MMDREG_SPEED (4) +#define MDIO_MMDREG_DEVS0 (5) +#define MDIO_MMDREG_DEVS1 (6) +#define MDIO_MMDREG_CTRL2 (7) +#define MDIO_MMDREG_STAT2 (8) + +/* Bits in MMDREG_CTRL1 */ +/* Reset */ +#define MDIO_MMDREG_CTRL1_RESET_LBN (15) +#define MDIO_MMDREG_CTRL1_RESET_WIDTH (1) + +/* Bits in MMDREG_STAT1 */ +#define MDIO_MMDREG_STAT1_FAULT_LBN (7) +#define MDIO_MMDREG_STAT1_FAULT_WIDTH (1) +/* Link state */ +#define MDIO_MMDREG_STAT1_LINK_LBN (2) +#define MDIO_MMDREG_STAT1_LINK_WIDTH (1) + +/* Bits in ID reg */ +#define MDIO_ID_REV(_id32) (_id32 & 0xf) +#define MDIO_ID_MODEL(_id32) ((_id32 >> 4) & 0x3f) +#define MDIO_ID_OUI(_id32) (_id32 >> 10) + +/* Bits in MMDREG_DEVS0. Someone thoughtfully layed things out + * so the 'bit present' bit number of an MMD is the number of + * that MMD */ +#define DEV_PRESENT_BIT(_b) (1 << _b) + +#define MDIO_MMDREG_DEVS0_PHYXS DEV_PRESENT_BIT(MDIO_MMD_PHYXS) +#define MDIO_MMDREG_DEVS0_PCS DEV_PRESENT_BIT(MDIO_MMD_PCS) +#define MDIO_MMDREG_DEVS0_PMAPMD DEV_PRESENT_BIT(MDIO_MMD_PMAPMD) + +/* Bits in MMDREG_STAT2 */ +#define MDIO_MMDREG_STAT2_PRESENT_VAL (2) +#define MDIO_MMDREG_STAT2_PRESENT_LBN (14) +#define MDIO_MMDREG_STAT2_PRESENT_WIDTH (2) + +/* PMA type (4 bits) */ +#define MDIO_PMAPMD_CTRL2_10G_CX4 (0x0) +#define MDIO_PMAPMD_CTRL2_10G_EW (0x1) +#define MDIO_PMAPMD_CTRL2_10G_LW (0x2) +#define MDIO_PMAPMD_CTRL2_10G_SW (0x3) +#define MDIO_PMAPMD_CTRL2_10G_LX4 (0x4) +#define MDIO_PMAPMD_CTRL2_10G_ER (0x5) +#define MDIO_PMAPMD_CTRL2_10G_LR (0x6) +#define MDIO_PMAPMD_CTRL2_10G_SR (0x7) +/* Reserved */ +#define MDIO_PMAPMD_CTRL2_10G_BT (0x9) +/* Reserved */ +/* Reserved */ +#define MDIO_PMAPMD_CTRL2_1G_BT (0xc) +/* Reserved */ +#define MDIO_PMAPMD_CTRL2_100_BT (0xe) +#define MDIO_PMAPMD_CTRL2_10_BT (0xf) +#define MDIO_PMAPMD_CTRL2_TYPE_MASK (0xf) + +/* /\* PHY XGXS lane state *\/ */ +#define MDIO_PHYXS_LANE_STATE (0x18) +#define MDIO_PHYXS_LANE_ALIGNED_LBN (12) + +/* AN registers */ +#define MDIO_AN_STATUS (1) +#define MDIO_AN_STATUS_XNP_LBN (7) +#define MDIO_AN_STATUS_PAGE_LBN (6) +#define MDIO_AN_STATUS_AN_DONE_LBN (5) +#define MDIO_AN_STATUS_LP_AN_CAP_LBN (0) + +#define MDIO_AN_10GBT_STATUS (33) +#define MDIO_AN_10GBT_STATUS_MS_FLT_LBN (15) /* MASTER/SLAVE config fault */ +#define MDIO_AN_10GBT_STATUS_MS_LBN (14) /* MASTER/SLAVE config */ +#define MDIO_AN_10GBT_STATUS_LOC_OK_LBN (13) /* Local OK */ +#define MDIO_AN_10GBT_STATUS_REM_OK_LBN (12) /* Remote OK */ +#define MDIO_AN_10GBT_STATUS_LP_10G_LBN (11) /* Link partner is 10GBT capable */ +#define MDIO_AN_10GBT_STATUS_LP_LTA_LBN (10) /* LP loop timing ability */ +#define MDIO_AN_10GBT_STATUS_LP_TRR_LBN (9) /* LP Training Reset Request */ + + +/* Packing of the prt and dev arguments of clause 45 style MDIO into a + * single int so they can be passed into the mdio_read/write functions + * that currently exist. Note that as Falcon is the only current user, + * the packed form is chosen to match what Falcon needs to write into + * a register. This is checked at compile-time so do not change it. If + * your target chip needs things layed out differently you will need + * to unpack the arguments in your chip-specific mdio functions. + */ + /* These are defined by the standard. */ +#define MDIO45_PRT_ID_WIDTH (5) +#define MDIO45_DEV_ID_WIDTH (5) + +/* The prt ID is just packed in immediately to the left of the dev ID */ +#define MDIO45_PRT_DEV_WIDTH (MDIO45_PRT_ID_WIDTH + MDIO45_DEV_ID_WIDTH) + +#define MDIO45_PRT_ID_MASK ((1 << MDIO45_PRT_DEV_WIDTH) - 1) +/* This is the prt + dev extended by 1 bit to hold the 'is clause 45' flag. */ +#define MDIO45_XPRT_ID_WIDTH (MDIO45_PRT_DEV_WIDTH + 1) +#define MDIO45_XPRT_ID_MASK ((1 << MDIO45_XPRT_ID_WIDTH) - 1) +#define MDIO45_XPRT_ID_IS10G (1 << (MDIO45_XPRT_ID_WIDTH - 1)) + + +#define MDIO45_PRT_ID_COMP_LBN MDIO45_DEV_ID_WIDTH +#define MDIO45_PRT_ID_COMP_WIDTH MDIO45_PRT_ID_WIDTH +#define MDIO45_DEV_ID_COMP_LBN 0 +#define MDIO45_DEV_ID_COMP_WIDTH MDIO45_DEV_ID_WIDTH + +/* Compose port and device into a phy_id */ +static inline int mdio_clause45_pack(u8 prt, u8 dev) +{ + efx_dword_t phy_id; + EFX_POPULATE_DWORD_2(phy_id, MDIO45_PRT_ID_COMP, prt, + MDIO45_DEV_ID_COMP, dev); + return MDIO45_XPRT_ID_IS10G | EFX_DWORD_VAL(phy_id); +} + +static inline void mdio_clause45_unpack(u32 val, u8 *prt, u8 *dev) +{ + efx_dword_t phy_id; + EFX_POPULATE_DWORD_1(phy_id, EFX_DWORD_0, val); + *prt = EFX_DWORD_FIELD(phy_id, MDIO45_PRT_ID_COMP); + *dev = EFX_DWORD_FIELD(phy_id, MDIO45_DEV_ID_COMP); +} + +static inline int mdio_clause45_read(struct efx_nic *efx, + u8 prt, u8 dev, u16 addr) +{ + return efx->mii.mdio_read(efx->net_dev, + mdio_clause45_pack(prt, dev), addr); +} + +static inline void mdio_clause45_write(struct efx_nic *efx, + u8 prt, u8 dev, u16 addr, int value) +{ + efx->mii.mdio_write(efx->net_dev, + mdio_clause45_pack(prt, dev), addr, value); +} + + +static inline u32 mdio_clause45_read_id(struct efx_nic *efx, int mmd) +{ + int phy_id = efx->mii.phy_id; + u16 id_low = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_IDLOW); + u16 id_hi = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_IDHI); + return (id_hi << 16) | (id_low); +} + +static inline int mdio_clause45_phyxgxs_lane_sync(struct efx_nic *efx) +{ + int i, sync, lane_status; + + for (i = 0; i < 2; ++i) + lane_status = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PHYXS, + MDIO_PHYXS_LANE_STATE); + + sync = (lane_status & (1 << MDIO_PHYXS_LANE_ALIGNED_LBN)) != 0; + if (!sync) + EFX_INFO(efx, "XGXS lane status: %x\n", lane_status); + return sync; +} + +extern const char *mdio_clause45_mmd_name(int mmd); + +/* + * Reset a specific MMD and wait for reset to clear. + * Return number of spins left (>0) on success, -%ETIMEDOUT on failure. + * + * This function will sleep + */ +extern int mdio_clause45_reset_mmd(struct efx_nic *efx, int mmd, + int spins, int spintime); + +/* As mdio_clause45_check_mmd but for multiple MMDs */ +int mdio_clause45_check_mmds(struct efx_nic *efx, + unsigned int mmd_mask, unsigned int fatal_mask); + +/* Check the link status of specified mmds in bit mask */ +extern int mdio_clause45_links_ok(struct efx_nic *efx, + unsigned int mmd_mask); + +/* Read (some of) the PHY settings over MDIO */ +extern void mdio_clause45_get_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd); + +/* Set (some of) the PHY settings over MDIO */ +extern int mdio_clause45_set_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd); + +/* Wait for specified MMDs to exit reset within a timeout */ +extern int mdio_clause45_wait_reset_mmds(struct efx_nic *efx, + unsigned int mmd_mask); + +#endif /* EFX_MDIO_10G_H */ diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h new file mode 100644 index 00000000000..c505482c252 --- /dev/null +++ b/drivers/net/sfc/net_driver.h @@ -0,0 +1,883 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +/* Common definitions for all Efx net driver code */ + +#ifndef EFX_NET_DRIVER_H +#define EFX_NET_DRIVER_H + +#include <linux/version.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/if_vlan.h> +#include <linux/timer.h> +#include <linux/mii.h> +#include <linux/list.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/highmem.h> +#include <linux/workqueue.h> +#include <linux/inet_lro.h> + +#include "enum.h" +#include "bitfield.h" +#include "i2c-direct.h" + +#define EFX_MAX_LRO_DESCRIPTORS 8 +#define EFX_MAX_LRO_AGGR MAX_SKB_FRAGS + +/************************************************************************** + * + * Build definitions + * + **************************************************************************/ +#ifndef EFX_DRIVER_NAME +#define EFX_DRIVER_NAME "sfc" +#endif +#define EFX_DRIVER_VERSION "2.2.0136" + +#ifdef EFX_ENABLE_DEBUG +#define EFX_BUG_ON_PARANOID(x) BUG_ON(x) +#define EFX_WARN_ON_PARANOID(x) WARN_ON(x) +#else +#define EFX_BUG_ON_PARANOID(x) do {} while (0) +#define EFX_WARN_ON_PARANOID(x) do {} while (0) +#endif + +#define NET_DEV_REGISTERED(efx) \ + ((efx)->net_dev->reg_state == NETREG_REGISTERED) + +/* Include net device name in log messages if it has been registered. + * Use efx->name not efx->net_dev->name so that races with (un)registration + * are harmless. + */ +#define NET_DEV_NAME(efx) (NET_DEV_REGISTERED(efx) ? (efx)->name : "") + +/* Un-rate-limited logging */ +#define EFX_ERR(efx, fmt, args...) \ +dev_err(&((efx)->pci_dev->dev), "ERR: %s " fmt, NET_DEV_NAME(efx), ##args) + +#define EFX_INFO(efx, fmt, args...) \ +dev_info(&((efx)->pci_dev->dev), "INFO: %s " fmt, NET_DEV_NAME(efx), ##args) + +#ifdef EFX_ENABLE_DEBUG +#define EFX_LOG(efx, fmt, args...) \ +dev_info(&((efx)->pci_dev->dev), "DBG: %s " fmt, NET_DEV_NAME(efx), ##args) +#else +#define EFX_LOG(efx, fmt, args...) \ +dev_dbg(&((efx)->pci_dev->dev), "DBG: %s " fmt, NET_DEV_NAME(efx), ##args) +#endif + +#define EFX_TRACE(efx, fmt, args...) do {} while (0) + +#define EFX_REGDUMP(efx, fmt, args...) do {} while (0) + +/* Rate-limited logging */ +#define EFX_ERR_RL(efx, fmt, args...) \ +do {if (net_ratelimit()) EFX_ERR(efx, fmt, ##args); } while (0) + +#define EFX_INFO_RL(efx, fmt, args...) \ +do {if (net_ratelimit()) EFX_INFO(efx, fmt, ##args); } while (0) + +#define EFX_LOG_RL(efx, fmt, args...) \ +do {if (net_ratelimit()) EFX_LOG(efx, fmt, ##args); } while (0) + +/* Kernel headers may redefine inline anyway */ +#ifndef inline +#define inline inline __attribute__ ((always_inline)) +#endif + +/************************************************************************** + * + * Efx data structures + * + **************************************************************************/ + +#define EFX_MAX_CHANNELS 32 +#define EFX_MAX_TX_QUEUES 1 +#define EFX_MAX_RX_QUEUES EFX_MAX_CHANNELS + +/** + * struct efx_special_buffer - An Efx special buffer + * @addr: CPU base address of the buffer + * @dma_addr: DMA base address of the buffer + * @len: Buffer length, in bytes + * @index: Buffer index within controller;s buffer table + * @entries: Number of buffer table entries + * + * Special buffers are used for the event queues and the TX and RX + * descriptor queues for each channel. They are *not* used for the + * actual transmit and receive buffers. + * + * Note that for Falcon, TX and RX descriptor queues live in host memory. + * Allocation and freeing procedures must take this into account. + */ +struct efx_special_buffer { + void *addr; + dma_addr_t dma_addr; + unsigned int len; + int index; + int entries; +}; + +/** + * struct efx_tx_buffer - An Efx TX buffer + * @skb: The associated socket buffer. + * Set only on the final fragment of a packet; %NULL for all other + * fragments. When this fragment completes, then we can free this + * skb. + * @dma_addr: DMA address of the fragment. + * @len: Length of this fragment. + * This field is zero when the queue slot is empty. + * @continuation: True if this fragment is not the end of a packet. + * @unmap_single: True if pci_unmap_single should be used. + * @unmap_addr: DMA address to unmap + * @unmap_len: Length of this fragment to unmap + */ +struct efx_tx_buffer { + const struct sk_buff *skb; + dma_addr_t dma_addr; + unsigned short len; + unsigned char continuation; + unsigned char unmap_single; + dma_addr_t unmap_addr; + unsigned short unmap_len; +}; + +/** + * struct efx_tx_queue - An Efx TX queue + * + * This is a ring buffer of TX fragments. + * Since the TX completion path always executes on the same + * CPU and the xmit path can operate on different CPUs, + * performance is increased by ensuring that the completion + * path and the xmit path operate on different cache lines. + * This is particularly important if the xmit path is always + * executing on one CPU which is different from the completion + * path. There is also a cache line for members which are + * read but not written on the fast path. + * + * @efx: The associated Efx NIC + * @queue: DMA queue number + * @used: Queue is used by net driver + * @channel: The associated channel + * @buffer: The software buffer ring + * @txd: The hardware descriptor ring + * @read_count: Current read pointer. + * This is the number of buffers that have been removed from both rings. + * @stopped: Stopped flag. + * Set if this TX queue is currently stopping its port. + * @insert_count: Current insert pointer + * This is the number of buffers that have been added to the + * software ring. + * @write_count: Current write pointer + * This is the number of buffers that have been added to the + * hardware ring. + * @old_read_count: The value of read_count when last checked. + * This is here for performance reasons. The xmit path will + * only get the up-to-date value of read_count if this + * variable indicates that the queue is full. This is to + * avoid cache-line ping-pong between the xmit path and the + * completion path. + */ +struct efx_tx_queue { + /* Members which don't change on the fast path */ + struct efx_nic *efx ____cacheline_aligned_in_smp; + int queue; + int used; + struct efx_channel *channel; + struct efx_nic *nic; + struct efx_tx_buffer *buffer; + struct efx_special_buffer txd; + + /* Members used mainly on the completion path */ + unsigned int read_count ____cacheline_aligned_in_smp; + int stopped; + + /* Members used only on the xmit path */ + unsigned int insert_count ____cacheline_aligned_in_smp; + unsigned int write_count; + unsigned int old_read_count; +}; + +/** + * struct efx_rx_buffer - An Efx RX data buffer + * @dma_addr: DMA base address of the buffer + * @skb: The associated socket buffer, if any. + * If both this and page are %NULL, the buffer slot is currently free. + * @page: The associated page buffer, if any. + * If both this and skb are %NULL, the buffer slot is currently free. + * @data: Pointer to ethernet header + * @len: Buffer length, in bytes. + * @unmap_addr: DMA address to unmap + */ +struct efx_rx_buffer { + dma_addr_t dma_addr; + struct sk_buff *skb; + struct page *page; + char *data; + unsigned int len; + dma_addr_t unmap_addr; +}; + +/** + * struct efx_rx_queue - An Efx RX queue + * @efx: The associated Efx NIC + * @queue: DMA queue number + * @used: Queue is used by net driver + * @channel: The associated channel + * @buffer: The software buffer ring + * @rxd: The hardware descriptor ring + * @added_count: Number of buffers added to the receive queue. + * @notified_count: Number of buffers given to NIC (<= @added_count). + * @removed_count: Number of buffers removed from the receive queue. + * @add_lock: Receive queue descriptor add spin lock. + * This lock must be held in order to add buffers to the RX + * descriptor ring (rxd and buffer) and to update added_count (but + * not removed_count). + * @max_fill: RX descriptor maximum fill level (<= ring size) + * @fast_fill_trigger: RX descriptor fill level that will trigger a fast fill + * (<= @max_fill) + * @fast_fill_limit: The level to which a fast fill will fill + * (@fast_fill_trigger <= @fast_fill_limit <= @max_fill) + * @min_fill: RX descriptor minimum non-zero fill level. + * This records the minimum fill level observed when a ring + * refill was triggered. + * @min_overfill: RX descriptor minimum overflow fill level. + * This records the minimum fill level at which RX queue + * overflow was observed. It should never be set. + * @alloc_page_count: RX allocation strategy counter. + * @alloc_skb_count: RX allocation strategy counter. + * @work: Descriptor push work thread + * @buf_page: Page for next RX buffer. + * We can use a single page for multiple RX buffers. This tracks + * the remaining space in the allocation. + * @buf_dma_addr: Page's DMA address. + * @buf_data: Page's host address. + */ +struct efx_rx_queue { + struct efx_nic *efx; + int queue; + int used; + struct efx_channel *channel; + struct efx_rx_buffer *buffer; + struct efx_special_buffer rxd; + + int added_count; + int notified_count; + int removed_count; + spinlock_t add_lock; + unsigned int max_fill; + unsigned int fast_fill_trigger; + unsigned int fast_fill_limit; + unsigned int min_fill; + unsigned int min_overfill; + unsigned int alloc_page_count; + unsigned int alloc_skb_count; + struct delayed_work work; + unsigned int slow_fill_count; + + struct page *buf_page; + dma_addr_t buf_dma_addr; + char *buf_data; +}; + +/** + * struct efx_buffer - An Efx general-purpose buffer + * @addr: host base address of the buffer + * @dma_addr: DMA base address of the buffer + * @len: Buffer length, in bytes + * + * Falcon uses these buffers for its interrupt status registers and + * MAC stats dumps. + */ +struct efx_buffer { + void *addr; + dma_addr_t dma_addr; + unsigned int len; +}; + + +/* Flags for channel->used_flags */ +#define EFX_USED_BY_RX 1 +#define EFX_USED_BY_TX 2 +#define EFX_USED_BY_RX_TX (EFX_USED_BY_RX | EFX_USED_BY_TX) + +enum efx_rx_alloc_method { + RX_ALLOC_METHOD_AUTO = 0, + RX_ALLOC_METHOD_SKB = 1, + RX_ALLOC_METHOD_PAGE = 2, +}; + +/** + * struct efx_channel - An Efx channel + * + * A channel comprises an event queue, at least one TX queue, at least + * one RX queue, and an associated tasklet for processing the event + * queue. + * + * @efx: Associated Efx NIC + * @evqnum: Event queue number + * @channel: Channel instance number + * @used_flags: Channel is used by net driver + * @enabled: Channel enabled indicator + * @irq: IRQ number (MSI and MSI-X only) + * @has_interrupt: Channel has an interrupt + * @irq_moderation: IRQ moderation value (in us) + * @napi_dev: Net device used with NAPI + * @napi_str: NAPI control structure + * @reset_work: Scheduled reset work thread + * @work_pending: Is work pending via NAPI? + * @eventq: Event queue buffer + * @eventq_read_ptr: Event queue read pointer + * @last_eventq_read_ptr: Last event queue read pointer value. + * @eventq_magic: Event queue magic value for driver-generated test events + * @lro_mgr: LRO state + * @rx_alloc_level: Watermark based heuristic counter for pushing descriptors + * and diagnostic counters + * @rx_alloc_push_pages: RX allocation method currently in use for pushing + * descriptors + * @rx_alloc_pop_pages: RX allocation method currently in use for popping + * descriptors + * @n_rx_tobe_disc: Count of RX_TOBE_DISC errors + * @n_rx_ip_frag_err: Count of RX IP fragment errors + * @n_rx_ip_hdr_chksum_err: Count of RX IP header checksum errors + * @n_rx_tcp_udp_chksum_err: Count of RX TCP and UDP checksum errors + * @n_rx_frm_trunc: Count of RX_FRM_TRUNC errors + * @n_rx_overlength: Count of RX_OVERLENGTH errors + * @n_skbuff_leaks: Count of skbuffs leaked due to RX overrun + */ +struct efx_channel { + struct efx_nic *efx; + int evqnum; + int channel; + int used_flags; + int enabled; + int irq; + unsigned int has_interrupt; + unsigned int irq_moderation; + struct net_device *napi_dev; + struct napi_struct napi_str; + struct work_struct reset_work; + int work_pending; + struct efx_special_buffer eventq; + unsigned int eventq_read_ptr; + unsigned int last_eventq_read_ptr; + unsigned int eventq_magic; + + struct net_lro_mgr lro_mgr; + int rx_alloc_level; + int rx_alloc_push_pages; + int rx_alloc_pop_pages; + + unsigned n_rx_tobe_disc; + unsigned n_rx_ip_frag_err; + unsigned n_rx_ip_hdr_chksum_err; + unsigned n_rx_tcp_udp_chksum_err; + unsigned n_rx_frm_trunc; + unsigned n_rx_overlength; + unsigned n_skbuff_leaks; + + /* Used to pipeline received packets in order to optimise memory + * access with prefetches. + */ + struct efx_rx_buffer *rx_pkt; + int rx_pkt_csummed; + +}; + +/** + * struct efx_blinker - S/W LED blinking context + * @led_num: LED ID (board-specific meaning) + * @state: Current state - on or off + * @resubmit: Timer resubmission flag + * @timer: Control timer for blinking + */ +struct efx_blinker { + int led_num; + int state; + int resubmit; + struct timer_list timer; +}; + + +/** + * struct efx_board - board information + * @type: Board model type + * @major: Major rev. ('A', 'B' ...) + * @minor: Minor rev. (0, 1, ...) + * @init: Initialisation function + * @init_leds: Sets up board LEDs + * @set_fault_led: Turns the fault LED on or off + * @blink: Starts/stops blinking + * @blinker: used to blink LEDs in software + */ +struct efx_board { + int type; + int major; + int minor; + int (*init) (struct efx_nic *nic); + /* As the LEDs are typically attached to the PHY, LEDs + * have a separate init callback that happens later than + * board init. */ + int (*init_leds)(struct efx_nic *efx); + void (*set_fault_led) (struct efx_nic *efx, int state); + void (*blink) (struct efx_nic *efx, int start); + struct efx_blinker blinker; +}; + +enum efx_int_mode { + /* Be careful if altering to correct macro below */ + EFX_INT_MODE_MSIX = 0, + EFX_INT_MODE_MSI = 1, + EFX_INT_MODE_LEGACY = 2, + EFX_INT_MODE_MAX /* Insert any new items before this */ +}; +#define EFX_INT_MODE_USE_MSI(x) (((x)->interrupt_mode) <= EFX_INT_MODE_MSI) + +enum phy_type { + PHY_TYPE_NONE = 0, + PHY_TYPE_CX4_RTMR = 1, + PHY_TYPE_1G_ALASKA = 2, + PHY_TYPE_10XPRESS = 3, + PHY_TYPE_XFP = 4, + PHY_TYPE_PM8358 = 6, + PHY_TYPE_MAX /* Insert any new items before this */ +}; + +#define PHY_ADDR_INVALID 0xff + +enum nic_state { + STATE_INIT = 0, + STATE_RUNNING = 1, + STATE_FINI = 2, + STATE_RESETTING = 3, /* rtnl_lock always held */ + STATE_DISABLED = 4, + STATE_MAX, +}; + +/* + * Alignment of page-allocated RX buffers + * + * Controls the number of bytes inserted at the start of an RX buffer. + * This is the equivalent of NET_IP_ALIGN [which controls the alignment + * of the skb->head for hardware DMA]. + */ +#if defined(__i386__) || defined(__x86_64__) +#define EFX_PAGE_IP_ALIGN 0 +#else +#define EFX_PAGE_IP_ALIGN NET_IP_ALIGN +#endif + +/* + * Alignment of the skb->head which wraps a page-allocated RX buffer + * + * The skb allocated to wrap an rx_buffer can have this alignment. Since + * the data is memcpy'd from the rx_buf, it does not need to be equal to + * EFX_PAGE_IP_ALIGN. + */ +#define EFX_PAGE_SKB_ALIGN 2 + +/* Forward declaration */ +struct efx_nic; + +/* Pseudo bit-mask flow control field */ +enum efx_fc_type { + EFX_FC_RX = 1, + EFX_FC_TX = 2, + EFX_FC_AUTO = 4, +}; + +/** + * struct efx_phy_operations - Efx PHY operations table + * @init: Initialise PHY + * @fini: Shut down PHY + * @reconfigure: Reconfigure PHY (e.g. for new link parameters) + * @clear_interrupt: Clear down interrupt + * @blink: Blink LEDs + * @check_hw: Check hardware + * @reset_xaui: Reset XAUI side of PHY for (software sequenced reset) + * @mmds: MMD presence mask + */ +struct efx_phy_operations { + int (*init) (struct efx_nic *efx); + void (*fini) (struct efx_nic *efx); + void (*reconfigure) (struct efx_nic *efx); + void (*clear_interrupt) (struct efx_nic *efx); + int (*check_hw) (struct efx_nic *efx); + void (*reset_xaui) (struct efx_nic *efx); + int mmds; +}; + +/* + * Efx extended statistics + * + * Not all statistics are provided by all supported MACs. The purpose + * is this structure is to contain the raw statistics provided by each + * MAC. + */ +struct efx_mac_stats { + u64 tx_bytes; + u64 tx_good_bytes; + u64 tx_bad_bytes; + unsigned long tx_packets; + unsigned long tx_bad; + unsigned long tx_pause; + unsigned long tx_control; + unsigned long tx_unicast; + unsigned long tx_multicast; + unsigned long tx_broadcast; + unsigned long tx_lt64; + unsigned long tx_64; + unsigned long tx_65_to_127; + unsigned long tx_128_to_255; + unsigned long tx_256_to_511; + unsigned long tx_512_to_1023; + unsigned long tx_1024_to_15xx; + unsigned long tx_15xx_to_jumbo; + unsigned long tx_gtjumbo; + unsigned long tx_collision; + unsigned long tx_single_collision; + unsigned long tx_multiple_collision; + unsigned long tx_excessive_collision; + unsigned long tx_deferred; + unsigned long tx_late_collision; + unsigned long tx_excessive_deferred; + unsigned long tx_non_tcpudp; + unsigned long tx_mac_src_error; + unsigned long tx_ip_src_error; + u64 rx_bytes; + u64 rx_good_bytes; + u64 rx_bad_bytes; + unsigned long rx_packets; + unsigned long rx_good; + unsigned long rx_bad; + unsigned long rx_pause; + unsigned long rx_control; + unsigned long rx_unicast; + unsigned long rx_multicast; + unsigned long rx_broadcast; + unsigned long rx_lt64; + unsigned long rx_64; + unsigned long rx_65_to_127; + unsigned long rx_128_to_255; + unsigned long rx_256_to_511; + unsigned long rx_512_to_1023; + unsigned long rx_1024_to_15xx; + unsigned long rx_15xx_to_jumbo; + unsigned long rx_gtjumbo; + unsigned long rx_bad_lt64; + unsigned long rx_bad_64_to_15xx; + unsigned long rx_bad_15xx_to_jumbo; + unsigned long rx_bad_gtjumbo; + unsigned long rx_overflow; + unsigned long rx_missed; + unsigned long rx_false_carrier; + unsigned long rx_symbol_error; + unsigned long rx_align_error; + unsigned long rx_length_error; + unsigned long rx_internal_error; + unsigned long rx_good_lt64; +}; + +/* Number of bits used in a multicast filter hash address */ +#define EFX_MCAST_HASH_BITS 8 + +/* Number of (single-bit) entries in a multicast filter hash */ +#define EFX_MCAST_HASH_ENTRIES (1 << EFX_MCAST_HASH_BITS) + +/* An Efx multicast filter hash */ +union efx_multicast_hash { + u8 byte[EFX_MCAST_HASH_ENTRIES / 8]; + efx_oword_t oword[EFX_MCAST_HASH_ENTRIES / sizeof(efx_oword_t) / 8]; +}; + +/** + * struct efx_nic - an Efx NIC + * @name: Device name (net device name or bus id before net device registered) + * @pci_dev: The PCI device + * @type: Controller type attributes + * @legacy_irq: IRQ number + * @workqueue: Workqueue for resets, port reconfigures and the HW monitor + * @reset_work: Scheduled reset workitem + * @monitor_work: Hardware monitor workitem + * @membase_phys: Memory BAR value as physical address + * @membase: Memory BAR value + * @biu_lock: BIU (bus interface unit) lock + * @interrupt_mode: Interrupt mode + * @i2c: I2C interface + * @board_info: Board-level information + * @state: Device state flag. Serialised by the rtnl_lock. + * @reset_pending: Pending reset method (normally RESET_TYPE_NONE) + * @tx_queue: TX DMA queues + * @rx_queue: RX DMA queues + * @channel: Channels + * @rss_queues: Number of RSS queues + * @rx_buffer_len: RX buffer length + * @rx_buffer_order: Order (log2) of number of pages for each RX buffer + * @irq_status: Interrupt status buffer + * @last_irq_cpu: Last CPU to handle interrupt. + * This register is written with the SMP processor ID whenever an + * interrupt is handled. It is used by falcon_test_interrupt() + * to verify that an interrupt has occurred. + * @n_rx_nodesc_drop_cnt: RX no descriptor drop count + * @nic_data: Hardware dependant state + * @mac_lock: MAC access lock. Protects @port_enabled, efx_monitor() and + * efx_reconfigure_port() + * @port_enabled: Port enabled indicator. + * Serialises efx_stop_all(), efx_start_all() and efx_monitor() and + * efx_reconfigure_work with kernel interfaces. Safe to read under any + * one of the rtnl_lock, mac_lock, or netif_tx_lock, but all three must + * be held to modify it. + * @port_initialized: Port initialized? + * @net_dev: Operating system network device. Consider holding the rtnl lock + * @rx_checksum_enabled: RX checksumming enabled + * @netif_stop_count: Port stop count + * @netif_stop_lock: Port stop lock + * @mac_stats: MAC statistics. These include all statistics the MACs + * can provide. Generic code converts these into a standard + * &struct net_device_stats. + * @stats_buffer: DMA buffer for statistics + * @stats_lock: Statistics update lock + * @mac_address: Permanent MAC address + * @phy_type: PHY type + * @phy_lock: PHY access lock + * @phy_op: PHY interface + * @phy_data: PHY private data (including PHY-specific stats) + * @mii: PHY interface + * @phy_powered: PHY power state + * @tx_disabled: PHY transmitter turned off + * @link_up: Link status + * @link_options: Link options (MII/GMII format) + * @n_link_state_changes: Number of times the link has changed state + * @promiscuous: Promiscuous flag. Protected by netif_tx_lock. + * @multicast_hash: Multicast hash table + * @flow_control: Flow control flags - separate RX/TX so can't use link_options + * @reconfigure_work: work item for dealing with PHY events + * + * The @priv field of the corresponding &struct net_device points to + * this. + */ +struct efx_nic { + char name[IFNAMSIZ]; + struct pci_dev *pci_dev; + const struct efx_nic_type *type; + int legacy_irq; + struct workqueue_struct *workqueue; + struct work_struct reset_work; + struct delayed_work monitor_work; + unsigned long membase_phys; + void __iomem *membase; + spinlock_t biu_lock; + enum efx_int_mode interrupt_mode; + + struct efx_i2c_interface i2c; + struct efx_board board_info; + + enum nic_state state; + enum reset_type reset_pending; + + struct efx_tx_queue tx_queue[EFX_MAX_TX_QUEUES]; + struct efx_rx_queue rx_queue[EFX_MAX_RX_QUEUES]; + struct efx_channel channel[EFX_MAX_CHANNELS]; + + int rss_queues; + unsigned int rx_buffer_len; + unsigned int rx_buffer_order; + + struct efx_buffer irq_status; + volatile signed int last_irq_cpu; + + unsigned n_rx_nodesc_drop_cnt; + + void *nic_data; + + struct mutex mac_lock; + int port_enabled; + + int port_initialized; + struct net_device *net_dev; + int rx_checksum_enabled; + + atomic_t netif_stop_count; + spinlock_t netif_stop_lock; + + struct efx_mac_stats mac_stats; + struct efx_buffer stats_buffer; + spinlock_t stats_lock; + + unsigned char mac_address[ETH_ALEN]; + + enum phy_type phy_type; + spinlock_t phy_lock; + struct efx_phy_operations *phy_op; + void *phy_data; + struct mii_if_info mii; + + int link_up; + unsigned int link_options; + unsigned int n_link_state_changes; + + int promiscuous; + union efx_multicast_hash multicast_hash; + enum efx_fc_type flow_control; + struct work_struct reconfigure_work; + + atomic_t rx_reset; +}; + +/** + * struct efx_nic_type - Efx device type definition + * @mem_bar: Memory BAR number + * @mem_map_size: Memory BAR mapped size + * @txd_ptr_tbl_base: TX descriptor ring base address + * @rxd_ptr_tbl_base: RX descriptor ring base address + * @buf_tbl_base: Buffer table base address + * @evq_ptr_tbl_base: Event queue pointer table base address + * @evq_rptr_tbl_base: Event queue read-pointer table base address + * @txd_ring_mask: TX descriptor ring size - 1 (must be a power of two - 1) + * @rxd_ring_mask: RX descriptor ring size - 1 (must be a power of two - 1) + * @evq_size: Event queue size (must be a power of two) + * @max_dma_mask: Maximum possible DMA mask + * @tx_dma_mask: TX DMA mask + * @bug5391_mask: Address mask for bug 5391 workaround + * @rx_xoff_thresh: RX FIFO XOFF watermark (bytes) + * @rx_xon_thresh: RX FIFO XON watermark (bytes) + * @rx_buffer_padding: Padding added to each RX buffer + * @max_interrupt_mode: Highest capability interrupt mode supported + * from &enum efx_init_mode. + * @phys_addr_channels: Number of channels with physically addressed + * descriptors + */ +struct efx_nic_type { + unsigned int mem_bar; + unsigned int mem_map_size; + unsigned int txd_ptr_tbl_base; + unsigned int rxd_ptr_tbl_base; + unsigned int buf_tbl_base; + unsigned int evq_ptr_tbl_base; + unsigned int evq_rptr_tbl_base; + + unsigned int txd_ring_mask; + unsigned int rxd_ring_mask; + unsigned int evq_size; + dma_addr_t max_dma_mask; + unsigned int tx_dma_mask; + unsigned bug5391_mask; + + int rx_xoff_thresh; + int rx_xon_thresh; + unsigned int rx_buffer_padding; + unsigned int max_interrupt_mode; + unsigned int phys_addr_channels; +}; + +/************************************************************************** + * + * Prototypes and inline functions + * + *************************************************************************/ + +/* Iterate over all used channels */ +#define efx_for_each_channel(_channel, _efx) \ + for (_channel = &_efx->channel[0]; \ + _channel < &_efx->channel[EFX_MAX_CHANNELS]; \ + _channel++) \ + if (!_channel->used_flags) \ + continue; \ + else + +/* Iterate over all used channels with interrupts */ +#define efx_for_each_channel_with_interrupt(_channel, _efx) \ + for (_channel = &_efx->channel[0]; \ + _channel < &_efx->channel[EFX_MAX_CHANNELS]; \ + _channel++) \ + if (!(_channel->used_flags && _channel->has_interrupt)) \ + continue; \ + else + +/* Iterate over all used TX queues */ +#define efx_for_each_tx_queue(_tx_queue, _efx) \ + for (_tx_queue = &_efx->tx_queue[0]; \ + _tx_queue < &_efx->tx_queue[EFX_MAX_TX_QUEUES]; \ + _tx_queue++) \ + if (!_tx_queue->used) \ + continue; \ + else + +/* Iterate over all TX queues belonging to a channel */ +#define efx_for_each_channel_tx_queue(_tx_queue, _channel) \ + for (_tx_queue = &_channel->efx->tx_queue[0]; \ + _tx_queue < &_channel->efx->tx_queue[EFX_MAX_TX_QUEUES]; \ + _tx_queue++) \ + if ((!_tx_queue->used) || \ + (_tx_queue->channel != _channel)) \ + continue; \ + else + +/* Iterate over all used RX queues */ +#define efx_for_each_rx_queue(_rx_queue, _efx) \ + for (_rx_queue = &_efx->rx_queue[0]; \ + _rx_queue < &_efx->rx_queue[EFX_MAX_RX_QUEUES]; \ + _rx_queue++) \ + if (!_rx_queue->used) \ + continue; \ + else + +/* Iterate over all RX queues belonging to a channel */ +#define efx_for_each_channel_rx_queue(_rx_queue, _channel) \ + for (_rx_queue = &_channel->efx->rx_queue[0]; \ + _rx_queue < &_channel->efx->rx_queue[EFX_MAX_RX_QUEUES]; \ + _rx_queue++) \ + if ((!_rx_queue->used) || \ + (_rx_queue->channel != _channel)) \ + continue; \ + else + +/* Returns a pointer to the specified receive buffer in the RX + * descriptor queue. + */ +static inline struct efx_rx_buffer *efx_rx_buffer(struct efx_rx_queue *rx_queue, + unsigned int index) +{ + return (&rx_queue->buffer[index]); +} + +/* Set bit in a little-endian bitfield */ +static inline void set_bit_le(int nr, unsigned char *addr) +{ + addr[nr / 8] |= (1 << (nr % 8)); +} + +/* Clear bit in a little-endian bitfield */ +static inline void clear_bit_le(int nr, unsigned char *addr) +{ + addr[nr / 8] &= ~(1 << (nr % 8)); +} + + +/** + * EFX_MAX_FRAME_LEN - calculate maximum frame length + * + * This calculates the maximum frame length that will be used for a + * given MTU. The frame length will be equal to the MTU plus a + * constant amount of header space and padding. This is the quantity + * that the net driver will program into the MAC as the maximum frame + * length. + * + * The 10G MAC used in Falcon requires 8-byte alignment on the frame + * length, so we round up to the nearest 8. + */ +#define EFX_MAX_FRAME_LEN(mtu) \ + ((((mtu) + ETH_HLEN + VLAN_HLEN + 4/* FCS */) + 7) & ~7) + + +#endif /* EFX_NET_DRIVER_H */ diff --git a/drivers/net/sfc/phy.h b/drivers/net/sfc/phy.h new file mode 100644 index 00000000000..9d02c84e6b2 --- /dev/null +++ b/drivers/net/sfc/phy.h @@ -0,0 +1,48 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2007 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_PHY_H +#define EFX_PHY_H + +/**************************************************************************** + * 10Xpress (SFX7101) PHY + */ +extern struct efx_phy_operations falcon_tenxpress_phy_ops; + +enum tenxpress_state { + TENXPRESS_STATUS_OFF = 0, + TENXPRESS_STATUS_OTEMP = 1, + TENXPRESS_STATUS_NORMAL = 2, +}; + +extern void tenxpress_set_state(struct efx_nic *efx, + enum tenxpress_state state); +extern void tenxpress_phy_blink(struct efx_nic *efx, int blink); +extern void tenxpress_crc_err(struct efx_nic *efx); + +/**************************************************************************** + * Exported functions from the driver for XFP optical PHYs + */ +extern struct efx_phy_operations falcon_xfp_phy_ops; + +/* The QUAKE XFP PHY provides various H/W control states for LEDs */ +#define QUAKE_LED_LINK_INVAL (0) +#define QUAKE_LED_LINK_STAT (1) +#define QUAKE_LED_LINK_ACT (2) +#define QUAKE_LED_LINK_ACTSTAT (3) +#define QUAKE_LED_OFF (4) +#define QUAKE_LED_ON (5) +#define QUAKE_LED_LINK_INPUT (6) /* Pin is an input. */ +/* What link the LED tracks */ +#define QUAKE_LED_TXLINK (0) +#define QUAKE_LED_RXLINK (8) + +extern void xfp_set_led(struct efx_nic *p, int led, int state); + +#endif diff --git a/drivers/net/sfc/rx.c b/drivers/net/sfc/rx.c new file mode 100644 index 00000000000..551299b462a --- /dev/null +++ b/drivers/net/sfc/rx.c @@ -0,0 +1,875 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/tcp.h> +#include <linux/udp.h> +#include <net/ip.h> +#include <net/checksum.h> +#include "net_driver.h" +#include "rx.h" +#include "efx.h" +#include "falcon.h" +#include "workarounds.h" + +/* Number of RX descriptors pushed at once. */ +#define EFX_RX_BATCH 8 + +/* Size of buffer allocated for skb header area. */ +#define EFX_SKB_HEADERS 64u + +/* + * rx_alloc_method - RX buffer allocation method + * + * This driver supports two methods for allocating and using RX buffers: + * each RX buffer may be backed by an skb or by an order-n page. + * + * When LRO is in use then the second method has a lower overhead, + * since we don't have to allocate then free skbs on reassembled frames. + * + * Values: + * - RX_ALLOC_METHOD_AUTO = 0 + * - RX_ALLOC_METHOD_SKB = 1 + * - RX_ALLOC_METHOD_PAGE = 2 + * + * The heuristic for %RX_ALLOC_METHOD_AUTO is a simple hysteresis count + * controlled by the parameters below. + * + * - Since pushing and popping descriptors are separated by the rx_queue + * size, so the watermarks should be ~rxd_size. + * - The performance win by using page-based allocation for LRO is less + * than the performance hit of using page-based allocation of non-LRO, + * so the watermarks should reflect this. + * + * Per channel we maintain a single variable, updated by each channel: + * + * rx_alloc_level += (lro_performed ? RX_ALLOC_FACTOR_LRO : + * RX_ALLOC_FACTOR_SKB) + * Per NAPI poll interval, we constrain rx_alloc_level to 0..MAX (which + * limits the hysteresis), and update the allocation strategy: + * + * rx_alloc_method = (rx_alloc_level > RX_ALLOC_LEVEL_LRO ? + * RX_ALLOC_METHOD_PAGE : RX_ALLOC_METHOD_SKB) + */ +static int rx_alloc_method = RX_ALLOC_METHOD_PAGE; + +#define RX_ALLOC_LEVEL_LRO 0x2000 +#define RX_ALLOC_LEVEL_MAX 0x3000 +#define RX_ALLOC_FACTOR_LRO 1 +#define RX_ALLOC_FACTOR_SKB (-2) + +/* This is the percentage fill level below which new RX descriptors + * will be added to the RX descriptor ring. + */ +static unsigned int rx_refill_threshold = 90; + +/* This is the percentage fill level to which an RX queue will be refilled + * when the "RX refill threshold" is reached. + */ +static unsigned int rx_refill_limit = 95; + +/* + * RX maximum head room required. + * + * This must be at least 1 to prevent overflow and at least 2 to allow + * pipelined receives. + */ +#define EFX_RXD_HEAD_ROOM 2 + +/* Macros for zero-order pages (potentially) containing multiple RX buffers */ +#define RX_DATA_OFFSET(_data) \ + (((unsigned long) (_data)) & (PAGE_SIZE-1)) +#define RX_BUF_OFFSET(_rx_buf) \ + RX_DATA_OFFSET((_rx_buf)->data) + +#define RX_PAGE_SIZE(_efx) \ + (PAGE_SIZE * (1u << (_efx)->rx_buffer_order)) + + +/************************************************************************** + * + * Linux generic LRO handling + * + ************************************************************************** + */ + +static int efx_lro_get_skb_hdr(struct sk_buff *skb, void **ip_hdr, + void **tcpudp_hdr, u64 *hdr_flags, void *priv) +{ + struct efx_channel *channel = (struct efx_channel *)priv; + struct iphdr *iph; + struct tcphdr *th; + + iph = (struct iphdr *)skb->data; + if (skb->protocol != htons(ETH_P_IP) || iph->protocol != IPPROTO_TCP) + goto fail; + + th = (struct tcphdr *)(skb->data + iph->ihl * 4); + + *tcpudp_hdr = th; + *ip_hdr = iph; + *hdr_flags = LRO_IPV4 | LRO_TCP; + + channel->rx_alloc_level += RX_ALLOC_FACTOR_LRO; + return 0; +fail: + channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB; + return -1; +} + +static int efx_get_frag_hdr(struct skb_frag_struct *frag, void **mac_hdr, + void **ip_hdr, void **tcpudp_hdr, u64 *hdr_flags, + void *priv) +{ + struct efx_channel *channel = (struct efx_channel *)priv; + struct ethhdr *eh; + struct iphdr *iph; + + /* We support EtherII and VLAN encapsulated IPv4 */ + eh = (struct ethhdr *)(page_address(frag->page) + frag->page_offset); + *mac_hdr = eh; + + if (eh->h_proto == htons(ETH_P_IP)) { + iph = (struct iphdr *)(eh + 1); + } else { + struct vlan_ethhdr *veh = (struct vlan_ethhdr *)eh; + if (veh->h_vlan_encapsulated_proto != htons(ETH_P_IP)) + goto fail; + + iph = (struct iphdr *)(veh + 1); + } + *ip_hdr = iph; + + /* We can only do LRO over TCP */ + if (iph->protocol != IPPROTO_TCP) + goto fail; + + *hdr_flags = LRO_IPV4 | LRO_TCP; + *tcpudp_hdr = (struct tcphdr *)((u8 *) iph + iph->ihl * 4); + + channel->rx_alloc_level += RX_ALLOC_FACTOR_LRO; + return 0; + fail: + channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB; + return -1; +} + +int efx_lro_init(struct net_lro_mgr *lro_mgr, struct efx_nic *efx) +{ + size_t s = sizeof(struct net_lro_desc) * EFX_MAX_LRO_DESCRIPTORS; + struct net_lro_desc *lro_arr; + + /* Allocate the LRO descriptors structure */ + lro_arr = kzalloc(s, GFP_KERNEL); + if (lro_arr == NULL) + return -ENOMEM; + + lro_mgr->lro_arr = lro_arr; + lro_mgr->max_desc = EFX_MAX_LRO_DESCRIPTORS; + lro_mgr->max_aggr = EFX_MAX_LRO_AGGR; + lro_mgr->frag_align_pad = EFX_PAGE_SKB_ALIGN; + + lro_mgr->get_skb_header = efx_lro_get_skb_hdr; + lro_mgr->get_frag_header = efx_get_frag_hdr; + lro_mgr->dev = efx->net_dev; + + lro_mgr->features = LRO_F_NAPI; + + /* We can pass packets up with the checksum intact */ + lro_mgr->ip_summed = CHECKSUM_UNNECESSARY; + + lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY; + + return 0; +} + +void efx_lro_fini(struct net_lro_mgr *lro_mgr) +{ + kfree(lro_mgr->lro_arr); + lro_mgr->lro_arr = NULL; +} + +/** + * efx_init_rx_buffer_skb - create new RX buffer using skb-based allocation + * + * @rx_queue: Efx RX queue + * @rx_buf: RX buffer structure to populate + * + * This allocates memory for a new receive buffer, maps it for DMA, + * and populates a struct efx_rx_buffer with the relevant + * information. Return a negative error code or 0 on success. + */ +static inline int efx_init_rx_buffer_skb(struct efx_rx_queue *rx_queue, + struct efx_rx_buffer *rx_buf) +{ + struct efx_nic *efx = rx_queue->efx; + struct net_device *net_dev = efx->net_dev; + int skb_len = efx->rx_buffer_len; + + rx_buf->skb = netdev_alloc_skb(net_dev, skb_len); + if (unlikely(!rx_buf->skb)) + return -ENOMEM; + + /* Adjust the SKB for padding and checksum */ + skb_reserve(rx_buf->skb, NET_IP_ALIGN); + rx_buf->len = skb_len - NET_IP_ALIGN; + rx_buf->data = (char *)rx_buf->skb->data; + rx_buf->skb->ip_summed = CHECKSUM_UNNECESSARY; + + rx_buf->dma_addr = pci_map_single(efx->pci_dev, + rx_buf->data, rx_buf->len, + PCI_DMA_FROMDEVICE); + + if (unlikely(pci_dma_mapping_error(rx_buf->dma_addr))) { + dev_kfree_skb_any(rx_buf->skb); + rx_buf->skb = NULL; + return -EIO; + } + + return 0; +} + +/** + * efx_init_rx_buffer_page - create new RX buffer using page-based allocation + * + * @rx_queue: Efx RX queue + * @rx_buf: RX buffer structure to populate + * + * This allocates memory for a new receive buffer, maps it for DMA, + * and populates a struct efx_rx_buffer with the relevant + * information. Return a negative error code or 0 on success. + */ +static inline int efx_init_rx_buffer_page(struct efx_rx_queue *rx_queue, + struct efx_rx_buffer *rx_buf) +{ + struct efx_nic *efx = rx_queue->efx; + int bytes, space, offset; + + bytes = efx->rx_buffer_len - EFX_PAGE_IP_ALIGN; + + /* If there is space left in the previously allocated page, + * then use it. Otherwise allocate a new one */ + rx_buf->page = rx_queue->buf_page; + if (rx_buf->page == NULL) { + dma_addr_t dma_addr; + + rx_buf->page = alloc_pages(__GFP_COLD | __GFP_COMP | GFP_ATOMIC, + efx->rx_buffer_order); + if (unlikely(rx_buf->page == NULL)) + return -ENOMEM; + + dma_addr = pci_map_page(efx->pci_dev, rx_buf->page, + 0, RX_PAGE_SIZE(efx), + PCI_DMA_FROMDEVICE); + + if (unlikely(pci_dma_mapping_error(dma_addr))) { + __free_pages(rx_buf->page, efx->rx_buffer_order); + rx_buf->page = NULL; + return -EIO; + } + + rx_queue->buf_page = rx_buf->page; + rx_queue->buf_dma_addr = dma_addr; + rx_queue->buf_data = ((char *) page_address(rx_buf->page) + + EFX_PAGE_IP_ALIGN); + } + + offset = RX_DATA_OFFSET(rx_queue->buf_data); + rx_buf->len = bytes; + rx_buf->dma_addr = rx_queue->buf_dma_addr + offset; + rx_buf->data = rx_queue->buf_data; + + /* Try to pack multiple buffers per page */ + if (efx->rx_buffer_order == 0) { + /* The next buffer starts on the next 512 byte boundary */ + rx_queue->buf_data += ((bytes + 0x1ff) & ~0x1ff); + offset += ((bytes + 0x1ff) & ~0x1ff); + + space = RX_PAGE_SIZE(efx) - offset; + if (space >= bytes) { + /* Refs dropped on kernel releasing each skb */ + get_page(rx_queue->buf_page); + goto out; + } + } + + /* This is the final RX buffer for this page, so mark it for + * unmapping */ + rx_queue->buf_page = NULL; + rx_buf->unmap_addr = rx_queue->buf_dma_addr; + + out: + return 0; +} + +/* This allocates memory for a new receive buffer, maps it for DMA, + * and populates a struct efx_rx_buffer with the relevant + * information. + */ +static inline int efx_init_rx_buffer(struct efx_rx_queue *rx_queue, + struct efx_rx_buffer *new_rx_buf) +{ + int rc = 0; + + if (rx_queue->channel->rx_alloc_push_pages) { + new_rx_buf->skb = NULL; + rc = efx_init_rx_buffer_page(rx_queue, new_rx_buf); + rx_queue->alloc_page_count++; + } else { + new_rx_buf->page = NULL; + rc = efx_init_rx_buffer_skb(rx_queue, new_rx_buf); + rx_queue->alloc_skb_count++; + } + + if (unlikely(rc < 0)) + EFX_LOG_RL(rx_queue->efx, "%s RXQ[%d] =%d\n", __func__, + rx_queue->queue, rc); + return rc; +} + +static inline void efx_unmap_rx_buffer(struct efx_nic *efx, + struct efx_rx_buffer *rx_buf) +{ + if (rx_buf->page) { + EFX_BUG_ON_PARANOID(rx_buf->skb); + if (rx_buf->unmap_addr) { + pci_unmap_page(efx->pci_dev, rx_buf->unmap_addr, + RX_PAGE_SIZE(efx), PCI_DMA_FROMDEVICE); + rx_buf->unmap_addr = 0; + } + } else if (likely(rx_buf->skb)) { + pci_unmap_single(efx->pci_dev, rx_buf->dma_addr, + rx_buf->len, PCI_DMA_FROMDEVICE); + } +} + +static inline void efx_free_rx_buffer(struct efx_nic *efx, + struct efx_rx_buffer *rx_buf) +{ + if (rx_buf->page) { + __free_pages(rx_buf->page, efx->rx_buffer_order); + rx_buf->page = NULL; + } else if (likely(rx_buf->skb)) { + dev_kfree_skb_any(rx_buf->skb); + rx_buf->skb = NULL; + } +} + +static inline void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue, + struct efx_rx_buffer *rx_buf) +{ + efx_unmap_rx_buffer(rx_queue->efx, rx_buf); + efx_free_rx_buffer(rx_queue->efx, rx_buf); +} + +/** + * efx_fast_push_rx_descriptors - push new RX descriptors quickly + * @rx_queue: RX descriptor queue + * @retry: Recheck the fill level + * This will aim to fill the RX descriptor queue up to + * @rx_queue->@fast_fill_limit. If there is insufficient atomic + * memory to do so, the caller should retry. + */ +static int __efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue, + int retry) +{ + struct efx_rx_buffer *rx_buf; + unsigned fill_level, index; + int i, space, rc = 0; + + /* Calculate current fill level. Do this outside the lock, + * because most of the time we'll end up not wanting to do the + * fill anyway. + */ + fill_level = (rx_queue->added_count - rx_queue->removed_count); + EFX_BUG_ON_PARANOID(fill_level > + rx_queue->efx->type->rxd_ring_mask + 1); + + /* Don't fill if we don't need to */ + if (fill_level >= rx_queue->fast_fill_trigger) + return 0; + + /* Record minimum fill level */ + if (unlikely(fill_level < rx_queue->min_fill)) + if (fill_level) + rx_queue->min_fill = fill_level; + + /* Acquire RX add lock. If this lock is contended, then a fast + * fill must already be in progress (e.g. in the refill + * tasklet), so we don't need to do anything + */ + if (!spin_trylock_bh(&rx_queue->add_lock)) + return -1; + + retry: + /* Recalculate current fill level now that we have the lock */ + fill_level = (rx_queue->added_count - rx_queue->removed_count); + EFX_BUG_ON_PARANOID(fill_level > + rx_queue->efx->type->rxd_ring_mask + 1); + space = rx_queue->fast_fill_limit - fill_level; + if (space < EFX_RX_BATCH) + goto out_unlock; + + EFX_TRACE(rx_queue->efx, "RX queue %d fast-filling descriptor ring from" + " level %d to level %d using %s allocation\n", + rx_queue->queue, fill_level, rx_queue->fast_fill_limit, + rx_queue->channel->rx_alloc_push_pages ? "page" : "skb"); + + do { + for (i = 0; i < EFX_RX_BATCH; ++i) { + index = (rx_queue->added_count & + rx_queue->efx->type->rxd_ring_mask); + rx_buf = efx_rx_buffer(rx_queue, index); + rc = efx_init_rx_buffer(rx_queue, rx_buf); + if (unlikely(rc)) + goto out; + ++rx_queue->added_count; + } + } while ((space -= EFX_RX_BATCH) >= EFX_RX_BATCH); + + EFX_TRACE(rx_queue->efx, "RX queue %d fast-filled descriptor ring " + "to level %d\n", rx_queue->queue, + rx_queue->added_count - rx_queue->removed_count); + + out: + /* Send write pointer to card. */ + falcon_notify_rx_desc(rx_queue); + + /* If the fast fill is running inside from the refill tasklet, then + * for SMP systems it may be running on a different CPU to + * RX event processing, which means that the fill level may now be + * out of date. */ + if (unlikely(retry && (rc == 0))) + goto retry; + + out_unlock: + spin_unlock_bh(&rx_queue->add_lock); + + return rc; +} + +/** + * efx_fast_push_rx_descriptors - push new RX descriptors quickly + * @rx_queue: RX descriptor queue + * + * This will aim to fill the RX descriptor queue up to + * @rx_queue->@fast_fill_limit. If there is insufficient memory to do so, + * it will schedule a work item to immediately continue the fast fill + */ +void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue) +{ + int rc; + + rc = __efx_fast_push_rx_descriptors(rx_queue, 0); + if (unlikely(rc)) { + /* Schedule the work item to run immediately. The hope is + * that work is immediately pending to free some memory + * (e.g. an RX event or TX completion) + */ + efx_schedule_slow_fill(rx_queue, 0); + } +} + +void efx_rx_work(struct work_struct *data) +{ + struct efx_rx_queue *rx_queue; + int rc; + + rx_queue = container_of(data, struct efx_rx_queue, work.work); + + if (unlikely(!rx_queue->channel->enabled)) + return; + + EFX_TRACE(rx_queue->efx, "RX queue %d worker thread executing on CPU " + "%d\n", rx_queue->queue, raw_smp_processor_id()); + + ++rx_queue->slow_fill_count; + /* Push new RX descriptors, allowing at least 1 jiffy for + * the kernel to free some more memory. */ + rc = __efx_fast_push_rx_descriptors(rx_queue, 1); + if (rc) + efx_schedule_slow_fill(rx_queue, 1); +} + +static inline void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue, + struct efx_rx_buffer *rx_buf, + int len, int *discard, + int *leak_packet) +{ + struct efx_nic *efx = rx_queue->efx; + unsigned max_len = rx_buf->len - efx->type->rx_buffer_padding; + + if (likely(len <= max_len)) + return; + + /* The packet must be discarded, but this is only a fatal error + * if the caller indicated it was + */ + *discard = 1; + + if ((len > rx_buf->len) && EFX_WORKAROUND_8071(efx)) { + EFX_ERR_RL(efx, " RX queue %d seriously overlength " + "RX event (0x%x > 0x%x+0x%x). Leaking\n", + rx_queue->queue, len, max_len, + efx->type->rx_buffer_padding); + /* If this buffer was skb-allocated, then the meta + * data at the end of the skb will be trashed. So + * we have no choice but to leak the fragment. + */ + *leak_packet = (rx_buf->skb != NULL); + efx_schedule_reset(efx, RESET_TYPE_RX_RECOVERY); + } else { + EFX_ERR_RL(efx, " RX queue %d overlength RX event " + "(0x%x > 0x%x)\n", rx_queue->queue, len, max_len); + } + + rx_queue->channel->n_rx_overlength++; +} + +/* Pass a received packet up through the generic LRO stack + * + * Handles driverlink veto, and passes the fragment up via + * the appropriate LRO method + */ +static inline void efx_rx_packet_lro(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf) +{ + struct net_lro_mgr *lro_mgr = &channel->lro_mgr; + void *priv = channel; + + /* Pass the skb/page into the LRO engine */ + if (rx_buf->page) { + struct skb_frag_struct frags; + + frags.page = rx_buf->page; + frags.page_offset = RX_BUF_OFFSET(rx_buf); + frags.size = rx_buf->len; + + lro_receive_frags(lro_mgr, &frags, rx_buf->len, + rx_buf->len, priv, 0); + + EFX_BUG_ON_PARANOID(rx_buf->skb); + rx_buf->page = NULL; + } else { + EFX_BUG_ON_PARANOID(!rx_buf->skb); + + lro_receive_skb(lro_mgr, rx_buf->skb, priv); + rx_buf->skb = NULL; + } +} + +/* Allocate and construct an SKB around a struct page.*/ +static inline struct sk_buff *efx_rx_mk_skb(struct efx_rx_buffer *rx_buf, + struct efx_nic *efx, + int hdr_len) +{ + struct sk_buff *skb; + + /* Allocate an SKB to store the headers */ + skb = netdev_alloc_skb(efx->net_dev, hdr_len + EFX_PAGE_SKB_ALIGN); + if (unlikely(skb == NULL)) { + EFX_ERR_RL(efx, "RX out of memory for skb\n"); + return NULL; + } + + EFX_BUG_ON_PARANOID(skb_shinfo(skb)->nr_frags); + EFX_BUG_ON_PARANOID(rx_buf->len < hdr_len); + + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb_reserve(skb, EFX_PAGE_SKB_ALIGN); + + skb->len = rx_buf->len; + skb->truesize = rx_buf->len + sizeof(struct sk_buff); + memcpy(skb->data, rx_buf->data, hdr_len); + skb->tail += hdr_len; + + /* Append the remaining page onto the frag list */ + if (unlikely(rx_buf->len > hdr_len)) { + struct skb_frag_struct *frag = skb_shinfo(skb)->frags; + frag->page = rx_buf->page; + frag->page_offset = RX_BUF_OFFSET(rx_buf) + hdr_len; + frag->size = skb->len - hdr_len; + skb_shinfo(skb)->nr_frags = 1; + skb->data_len = frag->size; + } else { + __free_pages(rx_buf->page, efx->rx_buffer_order); + skb->data_len = 0; + } + + /* Ownership has transferred from the rx_buf to skb */ + rx_buf->page = NULL; + + /* Move past the ethernet header */ + skb->protocol = eth_type_trans(skb, efx->net_dev); + + return skb; +} + +void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, + unsigned int len, int checksummed, int discard) +{ + struct efx_nic *efx = rx_queue->efx; + struct efx_rx_buffer *rx_buf; + int leak_packet = 0; + + rx_buf = efx_rx_buffer(rx_queue, index); + EFX_BUG_ON_PARANOID(!rx_buf->data); + EFX_BUG_ON_PARANOID(rx_buf->skb && rx_buf->page); + EFX_BUG_ON_PARANOID(!(rx_buf->skb || rx_buf->page)); + + /* This allows the refill path to post another buffer. + * EFX_RXD_HEAD_ROOM ensures that the slot we are using + * isn't overwritten yet. + */ + rx_queue->removed_count++; + + /* Validate the length encoded in the event vs the descriptor pushed */ + efx_rx_packet__check_len(rx_queue, rx_buf, len, + &discard, &leak_packet); + + EFX_TRACE(efx, "RX queue %d received id %x at %llx+%x %s%s\n", + rx_queue->queue, index, + (unsigned long long)rx_buf->dma_addr, len, + (checksummed ? " [SUMMED]" : ""), + (discard ? " [DISCARD]" : "")); + + /* Discard packet, if instructed to do so */ + if (unlikely(discard)) { + if (unlikely(leak_packet)) + rx_queue->channel->n_skbuff_leaks++; + else + /* We haven't called efx_unmap_rx_buffer yet, + * so fini the entire rx_buffer here */ + efx_fini_rx_buffer(rx_queue, rx_buf); + return; + } + + /* Release card resources - assumes all RX buffers consumed in-order + * per RX queue + */ + efx_unmap_rx_buffer(efx, rx_buf); + + /* Prefetch nice and early so data will (hopefully) be in cache by + * the time we look at it. + */ + prefetch(rx_buf->data); + + /* Pipeline receives so that we give time for packet headers to be + * prefetched into cache. + */ + rx_buf->len = len; + if (rx_queue->channel->rx_pkt) + __efx_rx_packet(rx_queue->channel, + rx_queue->channel->rx_pkt, + rx_queue->channel->rx_pkt_csummed); + rx_queue->channel->rx_pkt = rx_buf; + rx_queue->channel->rx_pkt_csummed = checksummed; +} + +/* Handle a received packet. Second half: Touches packet payload. */ +void __efx_rx_packet(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf, int checksummed) +{ + struct efx_nic *efx = channel->efx; + struct sk_buff *skb; + int lro = efx->net_dev->features & NETIF_F_LRO; + + if (rx_buf->skb) { + prefetch(skb_shinfo(rx_buf->skb)); + + skb_put(rx_buf->skb, rx_buf->len); + + /* Move past the ethernet header. rx_buf->data still points + * at the ethernet header */ + rx_buf->skb->protocol = eth_type_trans(rx_buf->skb, + efx->net_dev); + } + + /* Both our generic-LRO and SFC-SSR support skb and page based + * allocation, but neither support switching from one to the + * other on the fly. If we spot that the allocation mode has + * changed, then flush the LRO state. + */ + if (unlikely(channel->rx_alloc_pop_pages != (rx_buf->page != NULL))) { + efx_flush_lro(channel); + channel->rx_alloc_pop_pages = (rx_buf->page != NULL); + } + if (likely(checksummed && lro)) { + efx_rx_packet_lro(channel, rx_buf); + goto done; + } + + /* Form an skb if required */ + if (rx_buf->page) { + int hdr_len = min(rx_buf->len, EFX_SKB_HEADERS); + skb = efx_rx_mk_skb(rx_buf, efx, hdr_len); + if (unlikely(skb == NULL)) { + efx_free_rx_buffer(efx, rx_buf); + goto done; + } + } else { + /* We now own the SKB */ + skb = rx_buf->skb; + rx_buf->skb = NULL; + } + + EFX_BUG_ON_PARANOID(rx_buf->page); + EFX_BUG_ON_PARANOID(rx_buf->skb); + EFX_BUG_ON_PARANOID(!skb); + + /* Set the SKB flags */ + if (unlikely(!checksummed || !efx->rx_checksum_enabled)) + skb->ip_summed = CHECKSUM_NONE; + + /* Pass the packet up */ + netif_receive_skb(skb); + + /* Update allocation strategy method */ + channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB; + + /* fall-thru */ +done: + efx->net_dev->last_rx = jiffies; +} + +void efx_rx_strategy(struct efx_channel *channel) +{ + enum efx_rx_alloc_method method = rx_alloc_method; + + /* Only makes sense to use page based allocation if LRO is enabled */ + if (!(channel->efx->net_dev->features & NETIF_F_LRO)) { + method = RX_ALLOC_METHOD_SKB; + } else if (method == RX_ALLOC_METHOD_AUTO) { + /* Constrain the rx_alloc_level */ + if (channel->rx_alloc_level < 0) + channel->rx_alloc_level = 0; + else if (channel->rx_alloc_level > RX_ALLOC_LEVEL_MAX) + channel->rx_alloc_level = RX_ALLOC_LEVEL_MAX; + + /* Decide on the allocation method */ + method = ((channel->rx_alloc_level > RX_ALLOC_LEVEL_LRO) ? + RX_ALLOC_METHOD_PAGE : RX_ALLOC_METHOD_SKB); + } + + /* Push the option */ + channel->rx_alloc_push_pages = (method == RX_ALLOC_METHOD_PAGE); +} + +int efx_probe_rx_queue(struct efx_rx_queue *rx_queue) +{ + struct efx_nic *efx = rx_queue->efx; + unsigned int rxq_size; + int rc; + + EFX_LOG(efx, "creating RX queue %d\n", rx_queue->queue); + + /* Allocate RX buffers */ + rxq_size = (efx->type->rxd_ring_mask + 1) * sizeof(*rx_queue->buffer); + rx_queue->buffer = kzalloc(rxq_size, GFP_KERNEL); + if (!rx_queue->buffer) { + rc = -ENOMEM; + goto fail1; + } + + rc = falcon_probe_rx(rx_queue); + if (rc) + goto fail2; + + return 0; + + fail2: + kfree(rx_queue->buffer); + rx_queue->buffer = NULL; + fail1: + rx_queue->used = 0; + + return rc; +} + +int efx_init_rx_queue(struct efx_rx_queue *rx_queue) +{ + struct efx_nic *efx = rx_queue->efx; + unsigned int max_fill, trigger, limit; + + EFX_LOG(rx_queue->efx, "initialising RX queue %d\n", rx_queue->queue); + + /* Initialise ptr fields */ + rx_queue->added_count = 0; + rx_queue->notified_count = 0; + rx_queue->removed_count = 0; + rx_queue->min_fill = -1U; + rx_queue->min_overfill = -1U; + + /* Initialise limit fields */ + max_fill = efx->type->rxd_ring_mask + 1 - EFX_RXD_HEAD_ROOM; + trigger = max_fill * min(rx_refill_threshold, 100U) / 100U; + limit = max_fill * min(rx_refill_limit, 100U) / 100U; + + rx_queue->max_fill = max_fill; + rx_queue->fast_fill_trigger = trigger; + rx_queue->fast_fill_limit = limit; + + /* Set up RX descriptor ring */ + return falcon_init_rx(rx_queue); +} + +void efx_fini_rx_queue(struct efx_rx_queue *rx_queue) +{ + int i; + struct efx_rx_buffer *rx_buf; + + EFX_LOG(rx_queue->efx, "shutting down RX queue %d\n", rx_queue->queue); + + falcon_fini_rx(rx_queue); + + /* Release RX buffers NB start at index 0 not current HW ptr */ + if (rx_queue->buffer) { + for (i = 0; i <= rx_queue->efx->type->rxd_ring_mask; i++) { + rx_buf = efx_rx_buffer(rx_queue, i); + efx_fini_rx_buffer(rx_queue, rx_buf); + } + } + + /* For a page that is part-way through splitting into RX buffers */ + if (rx_queue->buf_page != NULL) { + pci_unmap_page(rx_queue->efx->pci_dev, rx_queue->buf_dma_addr, + RX_PAGE_SIZE(rx_queue->efx), PCI_DMA_FROMDEVICE); + __free_pages(rx_queue->buf_page, + rx_queue->efx->rx_buffer_order); + rx_queue->buf_page = NULL; + } +} + +void efx_remove_rx_queue(struct efx_rx_queue *rx_queue) +{ + EFX_LOG(rx_queue->efx, "destroying RX queue %d\n", rx_queue->queue); + + falcon_remove_rx(rx_queue); + + kfree(rx_queue->buffer); + rx_queue->buffer = NULL; + rx_queue->used = 0; +} + +void efx_flush_lro(struct efx_channel *channel) +{ + lro_flush_all(&channel->lro_mgr); +} + + +module_param(rx_alloc_method, int, 0644); +MODULE_PARM_DESC(rx_alloc_method, "Allocation method used for RX buffers"); + +module_param(rx_refill_threshold, uint, 0444); +MODULE_PARM_DESC(rx_refill_threshold, + "RX descriptor ring fast/slow fill threshold (%)"); + diff --git a/drivers/net/sfc/rx.h b/drivers/net/sfc/rx.h new file mode 100644 index 00000000000..f35e377bfc5 --- /dev/null +++ b/drivers/net/sfc/rx.h @@ -0,0 +1,29 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2006 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_RX_H +#define EFX_RX_H + +#include "net_driver.h" + +int efx_probe_rx_queue(struct efx_rx_queue *rx_queue); +void efx_remove_rx_queue(struct efx_rx_queue *rx_queue); +int efx_init_rx_queue(struct efx_rx_queue *rx_queue); +void efx_fini_rx_queue(struct efx_rx_queue *rx_queue); + +int efx_lro_init(struct net_lro_mgr *lro_mgr, struct efx_nic *efx); +void efx_lro_fini(struct net_lro_mgr *lro_mgr); +void efx_flush_lro(struct efx_channel *channel); +void efx_rx_strategy(struct efx_channel *channel); +void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue); +void efx_rx_work(struct work_struct *data); +void __efx_rx_packet(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf, int checksummed); + +#endif /* EFX_RX_H */ diff --git a/drivers/net/sfc/sfe4001.c b/drivers/net/sfc/sfe4001.c new file mode 100644 index 00000000000..11fa9fb8f48 --- /dev/null +++ b/drivers/net/sfc/sfe4001.c @@ -0,0 +1,252 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2007 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +/***************************************************************************** + * Support for the SFE4001 NIC: driver code for the PCA9539 I/O expander that + * controls the PHY power rails, and for the MAX6647 temp. sensor used to check + * the PHY + */ +#include <linux/delay.h> +#include "efx.h" +#include "phy.h" +#include "boards.h" +#include "falcon.h" +#include "falcon_hwdefs.h" +#include "mac.h" + +/************************************************************************** + * + * I2C IO Expander device + * + **************************************************************************/ +#define PCA9539 0x74 + +#define P0_IN 0x00 +#define P0_OUT 0x02 +#define P0_INVERT 0x04 +#define P0_CONFIG 0x06 + +#define P0_EN_1V0X_LBN 0 +#define P0_EN_1V0X_WIDTH 1 +#define P0_EN_1V2_LBN 1 +#define P0_EN_1V2_WIDTH 1 +#define P0_EN_2V5_LBN 2 +#define P0_EN_2V5_WIDTH 1 +#define P0_EN_3V3X_LBN 3 +#define P0_EN_3V3X_WIDTH 1 +#define P0_EN_5V_LBN 4 +#define P0_EN_5V_WIDTH 1 +#define P0_SHORTEN_JTAG_LBN 5 +#define P0_SHORTEN_JTAG_WIDTH 1 +#define P0_X_TRST_LBN 6 +#define P0_X_TRST_WIDTH 1 +#define P0_DSP_RESET_LBN 7 +#define P0_DSP_RESET_WIDTH 1 + +#define P1_IN 0x01 +#define P1_OUT 0x03 +#define P1_INVERT 0x05 +#define P1_CONFIG 0x07 + +#define P1_AFE_PWD_LBN 0 +#define P1_AFE_PWD_WIDTH 1 +#define P1_DSP_PWD25_LBN 1 +#define P1_DSP_PWD25_WIDTH 1 +#define P1_RESERVED_LBN 2 +#define P1_RESERVED_WIDTH 2 +#define P1_SPARE_LBN 4 +#define P1_SPARE_WIDTH 4 + + +/************************************************************************** + * + * Temperature Sensor + * + **************************************************************************/ +#define MAX6647 0x4e + +#define RLTS 0x00 +#define RLTE 0x01 +#define RSL 0x02 +#define RCL 0x03 +#define RCRA 0x04 +#define RLHN 0x05 +#define RLLI 0x06 +#define RRHI 0x07 +#define RRLS 0x08 +#define WCRW 0x0a +#define WLHO 0x0b +#define WRHA 0x0c +#define WRLN 0x0e +#define OSHT 0x0f +#define REET 0x10 +#define RIET 0x11 +#define RWOE 0x19 +#define RWOI 0x20 +#define HYS 0x21 +#define QUEUE 0x22 +#define MFID 0xfe +#define REVID 0xff + +/* Status bits */ +#define MAX6647_BUSY (1 << 7) /* ADC is converting */ +#define MAX6647_LHIGH (1 << 6) /* Local high temp. alarm */ +#define MAX6647_LLOW (1 << 5) /* Local low temp. alarm */ +#define MAX6647_RHIGH (1 << 4) /* Remote high temp. alarm */ +#define MAX6647_RLOW (1 << 3) /* Remote low temp. alarm */ +#define MAX6647_FAULT (1 << 2) /* DXN/DXP short/open circuit */ +#define MAX6647_EOT (1 << 1) /* Remote junction overtemp. */ +#define MAX6647_IOT (1 << 0) /* Local junction overtemp. */ + +static const u8 xgphy_max_temperature = 90; + +void sfe4001_poweroff(struct efx_nic *efx) +{ + struct efx_i2c_interface *i2c = &efx->i2c; + + u8 cfg, out, in; + + EFX_INFO(efx, "%s\n", __func__); + + /* Turn off all power rails */ + out = 0xff; + (void) efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1); + + /* Disable port 1 outputs on IO expander */ + cfg = 0xff; + (void) efx_i2c_write(i2c, PCA9539, P1_CONFIG, &cfg, 1); + + /* Disable port 0 outputs on IO expander */ + cfg = 0xff; + (void) efx_i2c_write(i2c, PCA9539, P0_CONFIG, &cfg, 1); + + /* Clear any over-temperature alert */ + (void) efx_i2c_read(i2c, MAX6647, RSL, &in, 1); +} + +/* This board uses an I2C expander to provider power to the PHY, which needs to + * be turned on before the PHY can be used. + * Context: Process context, rtnl lock held + */ +int sfe4001_poweron(struct efx_nic *efx) +{ + struct efx_i2c_interface *i2c = &efx->i2c; + unsigned int count; + int rc; + u8 out, in, cfg; + efx_dword_t reg; + + /* 10Xpress has fixed-function LED pins, so there is no board-specific + * blink code. */ + efx->board_info.blink = tenxpress_phy_blink; + + /* Ensure that XGXS and XAUI SerDes are held in reset */ + EFX_POPULATE_DWORD_7(reg, XX_PWRDNA_EN, 1, + XX_PWRDNB_EN, 1, + XX_RSTPLLAB_EN, 1, + XX_RESETA_EN, 1, + XX_RESETB_EN, 1, + XX_RSTXGXSRX_EN, 1, + XX_RSTXGXSTX_EN, 1); + falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + /* Set DSP over-temperature alert threshold */ + EFX_INFO(efx, "DSP cut-out at %dC\n", xgphy_max_temperature); + rc = efx_i2c_write(i2c, MAX6647, WLHO, + &xgphy_max_temperature, 1); + if (rc) + goto fail1; + + /* Read it back and verify */ + rc = efx_i2c_read(i2c, MAX6647, RLHN, &in, 1); + if (rc) + goto fail1; + if (in != xgphy_max_temperature) { + rc = -EFAULT; + goto fail1; + } + + /* Clear any previous over-temperature alert */ + rc = efx_i2c_read(i2c, MAX6647, RSL, &in, 1); + if (rc) + goto fail1; + + /* Enable port 0 and port 1 outputs on IO expander */ + cfg = 0x00; + rc = efx_i2c_write(i2c, PCA9539, P0_CONFIG, &cfg, 1); + if (rc) + goto fail1; + cfg = 0xff & ~(1 << P1_SPARE_LBN); + rc = efx_i2c_write(i2c, PCA9539, P1_CONFIG, &cfg, 1); + if (rc) + goto fail2; + + /* Turn all power off then wait 1 sec. This ensures PHY is reset */ + out = 0xff & ~((0 << P0_EN_1V2_LBN) | (0 << P0_EN_2V5_LBN) | + (0 << P0_EN_3V3X_LBN) | (0 << P0_EN_5V_LBN) | + (0 << P0_EN_1V0X_LBN)); + rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1); + if (rc) + goto fail3; + + schedule_timeout_uninterruptible(HZ); + count = 0; + do { + /* Turn on 1.2V, 2.5V, 3.3V and 5V power rails */ + out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) | + (1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) | + (1 << P0_X_TRST_LBN)); + + rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1); + if (rc) + goto fail3; + msleep(10); + + /* Turn on 1V power rail */ + out &= ~(1 << P0_EN_1V0X_LBN); + rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1); + if (rc) + goto fail3; + + EFX_INFO(efx, "waiting for power (attempt %d)...\n", count); + + schedule_timeout_uninterruptible(HZ); + + /* Check DSP is powered */ + rc = efx_i2c_read(i2c, PCA9539, P1_IN, &in, 1); + if (rc) + goto fail3; + if (in & (1 << P1_AFE_PWD_LBN)) + goto done; + + } while (++count < 20); + + EFX_INFO(efx, "timed out waiting for power\n"); + rc = -ETIMEDOUT; + goto fail3; + +done: + EFX_INFO(efx, "PHY is powered on\n"); + return 0; + +fail3: + /* Turn off all power rails */ + out = 0xff; + (void) efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1); + /* Disable port 1 outputs on IO expander */ + out = 0xff; + (void) efx_i2c_write(i2c, PCA9539, P1_CONFIG, &out, 1); +fail2: + /* Disable port 0 outputs on IO expander */ + out = 0xff; + (void) efx_i2c_write(i2c, PCA9539, P0_CONFIG, &out, 1); +fail1: + return rc; +} diff --git a/drivers/net/sfc/spi.h b/drivers/net/sfc/spi.h new file mode 100644 index 00000000000..34412f3d41c --- /dev/null +++ b/drivers/net/sfc/spi.h @@ -0,0 +1,71 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005 Fen Systems Ltd. + * Copyright 2006 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_SPI_H +#define EFX_SPI_H + +#include "net_driver.h" + +/************************************************************************** + * + * Basic SPI command set and bit definitions + * + *************************************************************************/ + +/* + * Commands common to all known devices. + * + */ + +/* Write status register */ +#define SPI_WRSR 0x01 + +/* Write data to memory array */ +#define SPI_WRITE 0x02 + +/* Read data from memory array */ +#define SPI_READ 0x03 + +/* Reset write enable latch */ +#define SPI_WRDI 0x04 + +/* Read status register */ +#define SPI_RDSR 0x05 + +/* Set write enable latch */ +#define SPI_WREN 0x06 + +/* SST: Enable write to status register */ +#define SPI_SST_EWSR 0x50 + +/* + * Status register bits. Not all bits are supported on all devices. + * + */ + +/* Write-protect pin enabled */ +#define SPI_STATUS_WPEN 0x80 + +/* Block protection bit 2 */ +#define SPI_STATUS_BP2 0x10 + +/* Block protection bit 1 */ +#define SPI_STATUS_BP1 0x08 + +/* Block protection bit 0 */ +#define SPI_STATUS_BP0 0x04 + +/* State of the write enable latch */ +#define SPI_STATUS_WEN 0x02 + +/* Device busy flag */ +#define SPI_STATUS_NRDY 0x01 + +#endif /* EFX_SPI_H */ diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c new file mode 100644 index 00000000000..a2e9f79e47b --- /dev/null +++ b/drivers/net/sfc/tenxpress.c @@ -0,0 +1,434 @@ +/**************************************************************************** + * Driver for Solarflare 802.3an compliant PHY + * Copyright 2007 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include <linux/delay.h> +#include <linux/seq_file.h> +#include "efx.h" +#include "gmii.h" +#include "mdio_10g.h" +#include "falcon.h" +#include "phy.h" +#include "falcon_hwdefs.h" +#include "boards.h" +#include "mac.h" + +/* We expect these MMDs to be in the package */ +/* AN not here as mdio_check_mmds() requires STAT2 support */ +#define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PMAPMD | \ + MDIO_MMDREG_DEVS0_PCS | \ + MDIO_MMDREG_DEVS0_PHYXS) + +/* We complain if we fail to see the link partner as 10G capable this many + * times in a row (must be > 1 as sampling the autoneg. registers is racy) + */ +#define MAX_BAD_LP_TRIES (5) + +/* Extended control register */ +#define PMA_PMD_XCONTROL_REG 0xc000 +#define PMA_PMD_LNPGA_POWERDOWN_LBN 8 +#define PMA_PMD_LNPGA_POWERDOWN_WIDTH 1 + +/* extended status register */ +#define PMA_PMD_XSTATUS_REG 0xc001 +#define PMA_PMD_XSTAT_FLP_LBN (12) + +/* LED control register */ +#define PMA_PMD_LED_CTRL_REG (0xc007) +#define PMA_PMA_LED_ACTIVITY_LBN (3) + +/* LED function override register */ +#define PMA_PMD_LED_OVERR_REG (0xc009) +/* Bit positions for different LEDs (there are more but not wired on SFE4001)*/ +#define PMA_PMD_LED_LINK_LBN (0) +#define PMA_PMD_LED_SPEED_LBN (2) +#define PMA_PMD_LED_TX_LBN (4) +#define PMA_PMD_LED_RX_LBN (6) +/* Override settings */ +#define PMA_PMD_LED_AUTO (0) /* H/W control */ +#define PMA_PMD_LED_ON (1) +#define PMA_PMD_LED_OFF (2) +#define PMA_PMD_LED_FLASH (3) +/* All LEDs under hardware control */ +#define PMA_PMD_LED_FULL_AUTO (0) +/* Green and Amber under hardware control, Red off */ +#define PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN) + + +/* Self test (BIST) control register */ +#define PMA_PMD_BIST_CTRL_REG (0xc014) +#define PMA_PMD_BIST_BER_LBN (2) /* Run BER test */ +#define PMA_PMD_BIST_CONT_LBN (1) /* Run continuous BIST until cleared */ +#define PMA_PMD_BIST_SINGLE_LBN (0) /* Run 1 BIST iteration (self clears) */ +/* Self test status register */ +#define PMA_PMD_BIST_STAT_REG (0xc015) +#define PMA_PMD_BIST_ENX_LBN (3) +#define PMA_PMD_BIST_PMA_LBN (2) +#define PMA_PMD_BIST_RXD_LBN (1) +#define PMA_PMD_BIST_AFE_LBN (0) + +#define BIST_MAX_DELAY (1000) +#define BIST_POLL_DELAY (10) + +/* Misc register defines */ +#define PCS_CLOCK_CTRL_REG 0xd801 +#define PLL312_RST_N_LBN 2 + +#define PCS_SOFT_RST2_REG 0xd806 +#define SERDES_RST_N_LBN 13 +#define XGXS_RST_N_LBN 12 + +#define PCS_TEST_SELECT_REG 0xd807 /* PRM 10.5.8 */ +#define CLK312_EN_LBN 3 + +/* Boot status register */ +#define PCS_BOOT_STATUS_REG (0xd000) +#define PCS_BOOT_FATAL_ERR_LBN (0) +#define PCS_BOOT_PROGRESS_LBN (1) +#define PCS_BOOT_PROGRESS_WIDTH (2) +#define PCS_BOOT_COMPLETE_LBN (3) +#define PCS_BOOT_MAX_DELAY (100) +#define PCS_BOOT_POLL_DELAY (10) + +/* Time to wait between powering down the LNPGA and turning off the power + * rails */ +#define LNPGA_PDOWN_WAIT (HZ / 5) + +static int crc_error_reset_threshold = 100; +module_param(crc_error_reset_threshold, int, 0644); +MODULE_PARM_DESC(crc_error_reset_threshold, + "Max number of CRC errors before XAUI reset"); + +struct tenxpress_phy_data { + enum tenxpress_state state; + atomic_t bad_crc_count; + int bad_lp_tries; +}; + +static int tenxpress_state_is(struct efx_nic *efx, int state) +{ + struct tenxpress_phy_data *phy_data = efx->phy_data; + return (phy_data != NULL) && (state == phy_data->state); +} + +void tenxpress_set_state(struct efx_nic *efx, + enum tenxpress_state state) +{ + struct tenxpress_phy_data *phy_data = efx->phy_data; + if (phy_data != NULL) + phy_data->state = state; +} + +void tenxpress_crc_err(struct efx_nic *efx) +{ + struct tenxpress_phy_data *phy_data = efx->phy_data; + if (phy_data != NULL) + atomic_inc(&phy_data->bad_crc_count); +} + +/* Check that the C166 has booted successfully */ +static int tenxpress_phy_check(struct efx_nic *efx) +{ + int phy_id = efx->mii.phy_id; + int count = PCS_BOOT_MAX_DELAY / PCS_BOOT_POLL_DELAY; + int boot_stat; + + /* Wait for the boot to complete (or not) */ + while (count) { + boot_stat = mdio_clause45_read(efx, phy_id, + MDIO_MMD_PCS, + PCS_BOOT_STATUS_REG); + if (boot_stat & (1 << PCS_BOOT_COMPLETE_LBN)) + break; + count--; + udelay(PCS_BOOT_POLL_DELAY); + } + + if (!count) { + EFX_ERR(efx, "%s: PHY boot timed out. Last status " + "%x\n", __func__, + (boot_stat >> PCS_BOOT_PROGRESS_LBN) & + ((1 << PCS_BOOT_PROGRESS_WIDTH) - 1)); + return -ETIMEDOUT; + } + + return 0; +} + +static void tenxpress_reset_xaui(struct efx_nic *efx); + +static int tenxpress_init(struct efx_nic *efx) +{ + int rc, reg; + + /* Turn on the clock */ + reg = (1 << CLK312_EN_LBN); + mdio_clause45_write(efx, efx->mii.phy_id, + MDIO_MMD_PCS, PCS_TEST_SELECT_REG, reg); + + rc = tenxpress_phy_check(efx); + if (rc < 0) + return rc; + + /* Set the LEDs up as: Green = Link, Amber = Link/Act, Red = Off */ + reg = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PMAPMD, PMA_PMD_LED_CTRL_REG); + reg |= (1 << PMA_PMA_LED_ACTIVITY_LBN); + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + PMA_PMD_LED_CTRL_REG, reg); + + reg = PMA_PMD_LED_DEFAULT; + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + PMA_PMD_LED_OVERR_REG, reg); + + return rc; +} + +static int tenxpress_phy_init(struct efx_nic *efx) +{ + struct tenxpress_phy_data *phy_data; + int rc = 0; + + phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); + efx->phy_data = phy_data; + + tenxpress_set_state(efx, TENXPRESS_STATUS_NORMAL); + + rc = mdio_clause45_wait_reset_mmds(efx, + TENXPRESS_REQUIRED_DEVS); + if (rc < 0) + goto fail; + + rc = mdio_clause45_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0); + if (rc < 0) + goto fail; + + rc = tenxpress_init(efx); + if (rc < 0) + goto fail; + + schedule_timeout_uninterruptible(HZ / 5); /* 200ms */ + + /* Let XGXS and SerDes out of reset and resets 10XPress */ + falcon_reset_xaui(efx); + + return 0; + + fail: + kfree(efx->phy_data); + efx->phy_data = NULL; + return rc; +} + +static void tenxpress_set_bad_lp(struct efx_nic *efx, int bad_lp) +{ + struct tenxpress_phy_data *pd = efx->phy_data; + int reg; + + /* Nothing to do if all is well and was previously so. */ + if (!(bad_lp || pd->bad_lp_tries)) + return; + + reg = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG); + + if (bad_lp) + pd->bad_lp_tries++; + else + pd->bad_lp_tries = 0; + + if (pd->bad_lp_tries == MAX_BAD_LP_TRIES) { + pd->bad_lp_tries = 0; /* Restart count */ + reg &= ~(PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN); + reg |= (PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN); + EFX_ERR(efx, "This NIC appears to be plugged into" + " a port that is not 10GBASE-T capable.\n" + " This PHY is 10GBASE-T ONLY, so no link can" + " be established.\n"); + } else { + reg |= (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN); + } + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + PMA_PMD_LED_OVERR_REG, reg); +} + +/* Check link status and return a boolean OK value. If the link is NOT + * OK we have a quick rummage round to see if we appear to be plugged + * into a non-10GBT port and if so warn the user that they won't get + * link any time soon as we are 10GBT only, unless caller specified + * not to do this check (it isn't useful in loopback) */ +static int tenxpress_link_ok(struct efx_nic *efx, int check_lp) +{ + int ok = mdio_clause45_links_ok(efx, TENXPRESS_REQUIRED_DEVS); + + if (ok) { + tenxpress_set_bad_lp(efx, 0); + } else if (check_lp) { + /* Are we plugged into the wrong sort of link? */ + int bad_lp = 0; + int phy_id = efx->mii.phy_id; + int an_stat = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, + MDIO_AN_STATUS); + int xphy_stat = mdio_clause45_read(efx, phy_id, + MDIO_MMD_PMAPMD, + PMA_PMD_XSTATUS_REG); + /* Are we plugged into anything that sends FLPs? If + * not we can't distinguish between not being plugged + * in and being plugged into a non-AN antique. The FLP + * bit has the advantage of not clearing when autoneg + * restarts. */ + if (!(xphy_stat & (1 << PMA_PMD_XSTAT_FLP_LBN))) { + tenxpress_set_bad_lp(efx, 0); + return ok; + } + + /* If it can do 10GBT it must be XNP capable */ + bad_lp = !(an_stat & (1 << MDIO_AN_STATUS_XNP_LBN)); + if (!bad_lp && (an_stat & (1 << MDIO_AN_STATUS_PAGE_LBN))) { + bad_lp = !(mdio_clause45_read(efx, phy_id, + MDIO_MMD_AN, MDIO_AN_10GBT_STATUS) & + (1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN)); + } + tenxpress_set_bad_lp(efx, bad_lp); + } + return ok; +} + +static void tenxpress_phy_reconfigure(struct efx_nic *efx) +{ + if (!tenxpress_state_is(efx, TENXPRESS_STATUS_NORMAL)) + return; + + efx->link_up = tenxpress_link_ok(efx, 0); + efx->link_options = GM_LPA_10000FULL; +} + +static void tenxpress_phy_clear_interrupt(struct efx_nic *efx) +{ + /* Nothing done here - LASI interrupts aren't reliable so poll */ +} + + +/* Poll PHY for interrupt */ +static int tenxpress_phy_check_hw(struct efx_nic *efx) +{ + struct tenxpress_phy_data *phy_data = efx->phy_data; + int phy_up = tenxpress_state_is(efx, TENXPRESS_STATUS_NORMAL); + int link_ok; + + link_ok = phy_up && tenxpress_link_ok(efx, 1); + + if (link_ok != efx->link_up) + falcon_xmac_sim_phy_event(efx); + + /* Nothing to check if we've already shut down the PHY */ + if (!phy_up) + return 0; + + if (atomic_read(&phy_data->bad_crc_count) > crc_error_reset_threshold) { + EFX_ERR(efx, "Resetting XAUI due to too many CRC errors\n"); + falcon_reset_xaui(efx); + atomic_set(&phy_data->bad_crc_count, 0); + } + + return 0; +} + +static void tenxpress_phy_fini(struct efx_nic *efx) +{ + int reg; + + /* Power down the LNPGA */ + reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN); + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + PMA_PMD_XCONTROL_REG, reg); + + /* Waiting here ensures that the board fini, which can turn off the + * power to the PHY, won't get run until the LNPGA powerdown has been + * given long enough to complete. */ + schedule_timeout_uninterruptible(LNPGA_PDOWN_WAIT); /* 200 ms */ + + kfree(efx->phy_data); + efx->phy_data = NULL; +} + + +/* Set the RX and TX LEDs and Link LED flashing. The other LEDs + * (which probably aren't wired anyway) are left in AUTO mode */ +void tenxpress_phy_blink(struct efx_nic *efx, int blink) +{ + int reg; + + if (blink) + reg = (PMA_PMD_LED_FLASH << PMA_PMD_LED_TX_LBN) | + (PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN) | + (PMA_PMD_LED_FLASH << PMA_PMD_LED_LINK_LBN); + else + reg = PMA_PMD_LED_DEFAULT; + + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + PMA_PMD_LED_OVERR_REG, reg); +} + +static void tenxpress_reset_xaui(struct efx_nic *efx) +{ + int phy = efx->mii.phy_id; + int clk_ctrl, test_select, soft_rst2; + + /* Real work is done on clock_ctrl other resets are thought to be + * optional but make the reset more reliable + */ + + /* Read */ + clk_ctrl = mdio_clause45_read(efx, phy, MDIO_MMD_PCS, + PCS_CLOCK_CTRL_REG); + test_select = mdio_clause45_read(efx, phy, MDIO_MMD_PCS, + PCS_TEST_SELECT_REG); + soft_rst2 = mdio_clause45_read(efx, phy, MDIO_MMD_PCS, + PCS_SOFT_RST2_REG); + + /* Put in reset */ + test_select &= ~(1 << CLK312_EN_LBN); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + PCS_TEST_SELECT_REG, test_select); + + soft_rst2 &= ~((1 << XGXS_RST_N_LBN) | (1 << SERDES_RST_N_LBN)); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + PCS_SOFT_RST2_REG, soft_rst2); + + clk_ctrl &= ~(1 << PLL312_RST_N_LBN); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + PCS_CLOCK_CTRL_REG, clk_ctrl); + udelay(10); + + /* Remove reset */ + clk_ctrl |= (1 << PLL312_RST_N_LBN); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + PCS_CLOCK_CTRL_REG, clk_ctrl); + udelay(10); + + soft_rst2 |= ((1 << XGXS_RST_N_LBN) | (1 << SERDES_RST_N_LBN)); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + PCS_SOFT_RST2_REG, soft_rst2); + udelay(10); + + test_select |= (1 << CLK312_EN_LBN); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + PCS_TEST_SELECT_REG, test_select); + udelay(10); +} + +struct efx_phy_operations falcon_tenxpress_phy_ops = { + .init = tenxpress_phy_init, + .reconfigure = tenxpress_phy_reconfigure, + .check_hw = tenxpress_phy_check_hw, + .fini = tenxpress_phy_fini, + .clear_interrupt = tenxpress_phy_clear_interrupt, + .reset_xaui = tenxpress_reset_xaui, + .mmds = TENXPRESS_REQUIRED_DEVS, +}; diff --git a/drivers/net/sfc/tx.c b/drivers/net/sfc/tx.c new file mode 100644 index 00000000000..fbb866b2185 --- /dev/null +++ b/drivers/net/sfc/tx.c @@ -0,0 +1,452 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include <linux/pci.h> +#include <linux/tcp.h> +#include <linux/ip.h> +#include <linux/in.h> +#include <linux/if_ether.h> +#include <linux/highmem.h> +#include "net_driver.h" +#include "tx.h" +#include "efx.h" +#include "falcon.h" +#include "workarounds.h" + +/* + * TX descriptor ring full threshold + * + * The tx_queue descriptor ring fill-level must fall below this value + * before we restart the netif queue + */ +#define EFX_NETDEV_TX_THRESHOLD(_tx_queue) \ + (_tx_queue->efx->type->txd_ring_mask / 2u) + +/* We want to be able to nest calls to netif_stop_queue(), since each + * channel can have an individual stop on the queue. + */ +void efx_stop_queue(struct efx_nic *efx) +{ + spin_lock_bh(&efx->netif_stop_lock); + EFX_TRACE(efx, "stop TX queue\n"); + + atomic_inc(&efx->netif_stop_count); + netif_stop_queue(efx->net_dev); + + spin_unlock_bh(&efx->netif_stop_lock); +} + +/* Wake netif's TX queue + * We want to be able to nest calls to netif_stop_queue(), since each + * channel can have an individual stop on the queue. + */ +inline void efx_wake_queue(struct efx_nic *efx) +{ + local_bh_disable(); + if (atomic_dec_and_lock(&efx->netif_stop_count, + &efx->netif_stop_lock)) { + EFX_TRACE(efx, "waking TX queue\n"); + netif_wake_queue(efx->net_dev); + spin_unlock(&efx->netif_stop_lock); + } + local_bh_enable(); +} + +static inline void efx_dequeue_buffer(struct efx_tx_queue *tx_queue, + struct efx_tx_buffer *buffer) +{ + if (buffer->unmap_len) { + struct pci_dev *pci_dev = tx_queue->efx->pci_dev; + if (buffer->unmap_single) + pci_unmap_single(pci_dev, buffer->unmap_addr, + buffer->unmap_len, PCI_DMA_TODEVICE); + else + pci_unmap_page(pci_dev, buffer->unmap_addr, + buffer->unmap_len, PCI_DMA_TODEVICE); + buffer->unmap_len = 0; + buffer->unmap_single = 0; + } + + if (buffer->skb) { + dev_kfree_skb_any((struct sk_buff *) buffer->skb); + buffer->skb = NULL; + EFX_TRACE(tx_queue->efx, "TX queue %d transmission id %x " + "complete\n", tx_queue->queue, read_ptr); + } +} + + +/* + * Add a socket buffer to a TX queue + * + * This maps all fragments of a socket buffer for DMA and adds them to + * the TX queue. The queue's insert pointer will be incremented by + * the number of fragments in the socket buffer. + * + * If any DMA mapping fails, any mapped fragments will be unmapped, + * the queue's insert pointer will be restored to its original value. + * + * Returns NETDEV_TX_OK or NETDEV_TX_BUSY + * You must hold netif_tx_lock() to call this function. + */ +static inline int efx_enqueue_skb(struct efx_tx_queue *tx_queue, + const struct sk_buff *skb) +{ + struct efx_nic *efx = tx_queue->efx; + struct pci_dev *pci_dev = efx->pci_dev; + struct efx_tx_buffer *buffer; + skb_frag_t *fragment; + struct page *page; + int page_offset; + unsigned int len, unmap_len = 0, fill_level, insert_ptr, misalign; + dma_addr_t dma_addr, unmap_addr = 0; + unsigned int dma_len; + unsigned unmap_single; + int q_space, i = 0; + int rc = NETDEV_TX_OK; + + EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count); + + /* Get size of the initial fragment */ + len = skb_headlen(skb); + + fill_level = tx_queue->insert_count - tx_queue->old_read_count; + q_space = efx->type->txd_ring_mask - 1 - fill_level; + + /* Map for DMA. Use pci_map_single rather than pci_map_page + * since this is more efficient on machines with sparse + * memory. + */ + unmap_single = 1; + dma_addr = pci_map_single(pci_dev, skb->data, len, PCI_DMA_TODEVICE); + + /* Process all fragments */ + while (1) { + if (unlikely(pci_dma_mapping_error(dma_addr))) + goto pci_err; + + /* Store fields for marking in the per-fragment final + * descriptor */ + unmap_len = len; + unmap_addr = dma_addr; + + /* Add to TX queue, splitting across DMA boundaries */ + do { + if (unlikely(q_space-- <= 0)) { + /* It might be that completions have + * happened since the xmit path last + * checked. Update the xmit path's + * copy of read_count. + */ + ++tx_queue->stopped; + /* This memory barrier protects the + * change of stopped from the access + * of read_count. */ + smp_mb(); + tx_queue->old_read_count = + *(volatile unsigned *) + &tx_queue->read_count; + fill_level = (tx_queue->insert_count + - tx_queue->old_read_count); + q_space = (efx->type->txd_ring_mask - 1 - + fill_level); + if (unlikely(q_space-- <= 0)) + goto stop; + smp_mb(); + --tx_queue->stopped; + } + + insert_ptr = (tx_queue->insert_count & + efx->type->txd_ring_mask); + buffer = &tx_queue->buffer[insert_ptr]; + EFX_BUG_ON_PARANOID(buffer->skb); + EFX_BUG_ON_PARANOID(buffer->len); + EFX_BUG_ON_PARANOID(buffer->continuation != 1); + EFX_BUG_ON_PARANOID(buffer->unmap_len); + + dma_len = (((~dma_addr) & efx->type->tx_dma_mask) + 1); + if (likely(dma_len > len)) + dma_len = len; + + misalign = (unsigned)dma_addr & efx->type->bug5391_mask; + if (misalign && dma_len + misalign > 512) + dma_len = 512 - misalign; + + /* Fill out per descriptor fields */ + buffer->len = dma_len; + buffer->dma_addr = dma_addr; + len -= dma_len; + dma_addr += dma_len; + ++tx_queue->insert_count; + } while (len); + + /* Transfer ownership of the unmapping to the final buffer */ + buffer->unmap_addr = unmap_addr; + buffer->unmap_single = unmap_single; + buffer->unmap_len = unmap_len; + unmap_len = 0; + + /* Get address and size of next fragment */ + if (i >= skb_shinfo(skb)->nr_frags) + break; + fragment = &skb_shinfo(skb)->frags[i]; + len = fragment->size; + page = fragment->page; + page_offset = fragment->page_offset; + i++; + /* Map for DMA */ + unmap_single = 0; + dma_addr = pci_map_page(pci_dev, page, page_offset, len, + PCI_DMA_TODEVICE); + } + + /* Transfer ownership of the skb to the final buffer */ + buffer->skb = skb; + buffer->continuation = 0; + + /* Pass off to hardware */ + falcon_push_buffers(tx_queue); + + return NETDEV_TX_OK; + + pci_err: + EFX_ERR_RL(efx, " TX queue %d could not map skb with %d bytes %d " + "fragments for DMA\n", tx_queue->queue, skb->len, + skb_shinfo(skb)->nr_frags + 1); + + /* Mark the packet as transmitted, and free the SKB ourselves */ + dev_kfree_skb_any((struct sk_buff *)skb); + goto unwind; + + stop: + rc = NETDEV_TX_BUSY; + + if (tx_queue->stopped == 1) + efx_stop_queue(efx); + + unwind: + /* Work backwards until we hit the original insert pointer value */ + while (tx_queue->insert_count != tx_queue->write_count) { + --tx_queue->insert_count; + insert_ptr = tx_queue->insert_count & efx->type->txd_ring_mask; + buffer = &tx_queue->buffer[insert_ptr]; + efx_dequeue_buffer(tx_queue, buffer); + buffer->len = 0; + } + + /* Free the fragment we were mid-way through pushing */ + if (unmap_len) + pci_unmap_page(pci_dev, unmap_addr, unmap_len, + PCI_DMA_TODEVICE); + + return rc; +} + +/* Remove packets from the TX queue + * + * This removes packets from the TX queue, up to and including the + * specified index. + */ +static inline void efx_dequeue_buffers(struct efx_tx_queue *tx_queue, + unsigned int index) +{ + struct efx_nic *efx = tx_queue->efx; + unsigned int stop_index, read_ptr; + unsigned int mask = tx_queue->efx->type->txd_ring_mask; + + stop_index = (index + 1) & mask; + read_ptr = tx_queue->read_count & mask; + + while (read_ptr != stop_index) { + struct efx_tx_buffer *buffer = &tx_queue->buffer[read_ptr]; + if (unlikely(buffer->len == 0)) { + EFX_ERR(tx_queue->efx, "TX queue %d spurious TX " + "completion id %x\n", tx_queue->queue, + read_ptr); + efx_schedule_reset(efx, RESET_TYPE_TX_SKIP); + return; + } + + efx_dequeue_buffer(tx_queue, buffer); + buffer->continuation = 1; + buffer->len = 0; + + ++tx_queue->read_count; + read_ptr = tx_queue->read_count & mask; + } +} + +/* Initiate a packet transmission on the specified TX queue. + * Note that returning anything other than NETDEV_TX_OK will cause the + * OS to free the skb. + * + * This function is split out from efx_hard_start_xmit to allow the + * loopback test to direct packets via specific TX queues. It is + * therefore a non-static inline, so as not to penalise performance + * for non-loopback transmissions. + * + * Context: netif_tx_lock held + */ +inline int efx_xmit(struct efx_nic *efx, + struct efx_tx_queue *tx_queue, struct sk_buff *skb) +{ + int rc; + + /* Map fragments for DMA and add to TX queue */ + rc = efx_enqueue_skb(tx_queue, skb); + if (unlikely(rc != NETDEV_TX_OK)) + goto out; + + /* Update last TX timer */ + efx->net_dev->trans_start = jiffies; + + out: + return rc; +} + +/* Initiate a packet transmission. We use one channel per CPU + * (sharing when we have more CPUs than channels). On Falcon, the TX + * completion events will be directed back to the CPU that transmitted + * the packet, which should be cache-efficient. + * + * Context: non-blocking. + * Note that returning anything other than NETDEV_TX_OK will cause the + * OS to free the skb. + */ +int efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + return efx_xmit(efx, &efx->tx_queue[0], skb); +} + +void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) +{ + unsigned fill_level; + struct efx_nic *efx = tx_queue->efx; + + EFX_BUG_ON_PARANOID(index > efx->type->txd_ring_mask); + + efx_dequeue_buffers(tx_queue, index); + + /* See if we need to restart the netif queue. This barrier + * separates the update of read_count from the test of + * stopped. */ + smp_mb(); + if (unlikely(tx_queue->stopped)) { + fill_level = tx_queue->insert_count - tx_queue->read_count; + if (fill_level < EFX_NETDEV_TX_THRESHOLD(tx_queue)) { + EFX_BUG_ON_PARANOID(!NET_DEV_REGISTERED(efx)); + + /* Do this under netif_tx_lock(), to avoid racing + * with efx_xmit(). */ + netif_tx_lock(efx->net_dev); + if (tx_queue->stopped) { + tx_queue->stopped = 0; + efx_wake_queue(efx); + } + netif_tx_unlock(efx->net_dev); + } + } +} + +int efx_probe_tx_queue(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + unsigned int txq_size; + int i, rc; + + EFX_LOG(efx, "creating TX queue %d\n", tx_queue->queue); + + /* Allocate software ring */ + txq_size = (efx->type->txd_ring_mask + 1) * sizeof(*tx_queue->buffer); + tx_queue->buffer = kzalloc(txq_size, GFP_KERNEL); + if (!tx_queue->buffer) { + rc = -ENOMEM; + goto fail1; + } + for (i = 0; i <= efx->type->txd_ring_mask; ++i) + tx_queue->buffer[i].continuation = 1; + + /* Allocate hardware ring */ + rc = falcon_probe_tx(tx_queue); + if (rc) + goto fail2; + + return 0; + + fail2: + kfree(tx_queue->buffer); + tx_queue->buffer = NULL; + fail1: + tx_queue->used = 0; + + return rc; +} + +int efx_init_tx_queue(struct efx_tx_queue *tx_queue) +{ + EFX_LOG(tx_queue->efx, "initialising TX queue %d\n", tx_queue->queue); + + tx_queue->insert_count = 0; + tx_queue->write_count = 0; + tx_queue->read_count = 0; + tx_queue->old_read_count = 0; + BUG_ON(tx_queue->stopped); + + /* Set up TX descriptor ring */ + return falcon_init_tx(tx_queue); +} + +void efx_release_tx_buffers(struct efx_tx_queue *tx_queue) +{ + struct efx_tx_buffer *buffer; + + if (!tx_queue->buffer) + return; + + /* Free any buffers left in the ring */ + while (tx_queue->read_count != tx_queue->write_count) { + buffer = &tx_queue->buffer[tx_queue->read_count & + tx_queue->efx->type->txd_ring_mask]; + efx_dequeue_buffer(tx_queue, buffer); + buffer->continuation = 1; + buffer->len = 0; + + ++tx_queue->read_count; + } +} + +void efx_fini_tx_queue(struct efx_tx_queue *tx_queue) +{ + EFX_LOG(tx_queue->efx, "shutting down TX queue %d\n", tx_queue->queue); + + /* Flush TX queue, remove descriptor ring */ + falcon_fini_tx(tx_queue); + + efx_release_tx_buffers(tx_queue); + + /* Release queue's stop on port, if any */ + if (tx_queue->stopped) { + tx_queue->stopped = 0; + efx_wake_queue(tx_queue->efx); + } +} + +void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) +{ + EFX_LOG(tx_queue->efx, "destroying TX queue %d\n", tx_queue->queue); + falcon_remove_tx(tx_queue); + + kfree(tx_queue->buffer); + tx_queue->buffer = NULL; + tx_queue->used = 0; +} + + diff --git a/drivers/net/sfc/tx.h b/drivers/net/sfc/tx.h new file mode 100644 index 00000000000..1526a73b4b5 --- /dev/null +++ b/drivers/net/sfc/tx.h @@ -0,0 +1,24 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2006 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_TX_H +#define EFX_TX_H + +#include "net_driver.h" + +int efx_probe_tx_queue(struct efx_tx_queue *tx_queue); +void efx_remove_tx_queue(struct efx_tx_queue *tx_queue); +int efx_init_tx_queue(struct efx_tx_queue *tx_queue); +void efx_fini_tx_queue(struct efx_tx_queue *tx_queue); + +int efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev); +void efx_release_tx_buffers(struct efx_tx_queue *tx_queue); + +#endif /* EFX_TX_H */ diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h new file mode 100644 index 00000000000..dca62f19019 --- /dev/null +++ b/drivers/net/sfc/workarounds.h @@ -0,0 +1,56 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_WORKAROUNDS_H +#define EFX_WORKAROUNDS_H + +/* + * Hardware workarounds. + * Bug numbers are from Solarflare's Bugzilla. + */ + +#define EFX_WORKAROUND_ALWAYS(efx) 1 +#define EFX_WORKAROUND_FALCON_A(efx) (FALCON_REV(efx) <= FALCON_REV_A1) + +/* XAUI resets if link not detected */ +#define EFX_WORKAROUND_5147 EFX_WORKAROUND_ALWAYS +/* SNAP frames have TOBE_DISC set */ +#define EFX_WORKAROUND_5475 EFX_WORKAROUND_ALWAYS +/* RX PCIe double split performance issue */ +#define EFX_WORKAROUND_7575 EFX_WORKAROUND_ALWAYS +/* TX pkt parser problem with <= 16 byte TXes */ +#define EFX_WORKAROUND_9141 EFX_WORKAROUND_ALWAYS +/* XGXS and XAUI reset sequencing in SW */ +#define EFX_WORKAROUND_9388 EFX_WORKAROUND_ALWAYS +/* Low rate CRC errors require XAUI reset */ +#define EFX_WORKAROUND_10750 EFX_WORKAROUND_ALWAYS +/* TX_EV_PKT_ERR can be caused by a dangling TX descriptor + * or a PCIe error (bug 11028) */ +#define EFX_WORKAROUND_10727 EFX_WORKAROUND_ALWAYS +/* Transmit flow control may get disabled */ +#define EFX_WORKAROUND_11482 EFX_WORKAROUND_ALWAYS +/* Flush events can take a very long time to appear */ +#define EFX_WORKAROUND_11557 EFX_WORKAROUND_ALWAYS + +/* Spurious parity errors in TSORT buffers */ +#define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A +/* iSCSI parsing errors */ +#define EFX_WORKAROUND_5583 EFX_WORKAROUND_FALCON_A +/* RX events go missing */ +#define EFX_WORKAROUND_5676 EFX_WORKAROUND_FALCON_A +/* RX_RESET on A1 */ +#define EFX_WORKAROUND_6555 EFX_WORKAROUND_FALCON_A +/* Increase filter depth to avoid RX_RESET */ +#define EFX_WORKAROUND_7244 EFX_WORKAROUND_FALCON_A +/* Flushes may never complete */ +#define EFX_WORKAROUND_7803 EFX_WORKAROUND_FALCON_A +/* Leak overlength packets rather than free */ +#define EFX_WORKAROUND_8071 EFX_WORKAROUND_FALCON_A + +#endif /* EFX_WORKAROUNDS_H */ diff --git a/drivers/net/sfc/xenpack.h b/drivers/net/sfc/xenpack.h new file mode 100644 index 00000000000..b0d1f225b70 --- /dev/null +++ b/drivers/net/sfc/xenpack.h @@ -0,0 +1,62 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2006 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_XENPACK_H +#define EFX_XENPACK_H + +/* Exported functions from Xenpack standard PHY control */ + +#include "mdio_10g.h" + +/****************************************************************************/ +/* XENPACK MDIO register extensions */ +#define MDIO_XP_LASI_RX_CTRL (0x9000) +#define MDIO_XP_LASI_TX_CTRL (0x9001) +#define MDIO_XP_LASI_CTRL (0x9002) +#define MDIO_XP_LASI_RX_STAT (0x9003) +#define MDIO_XP_LASI_TX_STAT (0x9004) +#define MDIO_XP_LASI_STAT (0x9005) + +/* Control/Status bits */ +#define XP_LASI_LS_ALARM (1 << 0) +#define XP_LASI_TX_ALARM (1 << 1) +#define XP_LASI_RX_ALARM (1 << 2) +/* These two are Quake vendor extensions to the standard XENPACK defines */ +#define XP_LASI_LS_INTB (1 << 3) +#define XP_LASI_TEST (1 << 7) + +/* Enable LASI interrupts for PHY */ +static inline void xenpack_enable_lasi_irqs(struct efx_nic *efx) +{ + int reg; + int phy_id = efx->mii.phy_id; + /* Read to clear LASI status register */ + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_XP_LASI_STAT); + + mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_XP_LASI_CTRL, XP_LASI_LS_ALARM); +} + +/* Read the LASI interrupt status to clear the interrupt. */ +static inline int xenpack_clear_lasi_irqs(struct efx_nic *efx) +{ + /* Read to clear link status alarm */ + return mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PMAPMD, MDIO_XP_LASI_STAT); +} + +/* Turn off LASI interrupts */ +static inline void xenpack_disable_lasi_irqs(struct efx_nic *efx) +{ + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + MDIO_XP_LASI_CTRL, 0); +} + +#endif /* EFX_XENPACK_H */ diff --git a/drivers/net/sfc/xfp_phy.c b/drivers/net/sfc/xfp_phy.c new file mode 100644 index 00000000000..66dd5bf1eaa --- /dev/null +++ b/drivers/net/sfc/xfp_phy.c @@ -0,0 +1,132 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ +/* + * Driver for XFP optical PHYs (plus some support specific to the Quake 2032) + * See www.amcc.com for details (search for qt2032) + */ + +#include <linux/timer.h> +#include <linux/delay.h> +#include "efx.h" +#include "gmii.h" +#include "mdio_10g.h" +#include "xenpack.h" +#include "phy.h" +#include "mac.h" + +#define XFP_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PCS | \ + MDIO_MMDREG_DEVS0_PMAPMD | \ + MDIO_MMDREG_DEVS0_PHYXS) + +/****************************************************************************/ +/* Quake-specific MDIO registers */ +#define MDIO_QUAKE_LED0_REG (0xD006) + +void xfp_set_led(struct efx_nic *p, int led, int mode) +{ + int addr = MDIO_QUAKE_LED0_REG + led; + mdio_clause45_write(p, p->mii.phy_id, MDIO_MMD_PMAPMD, addr, + mode); +} + +#define XFP_MAX_RESET_TIME 500 +#define XFP_RESET_WAIT 10 + +/* Reset the PHYXS MMD. This is documented (for the Quake PHY) as doing + * a complete soft reset. + */ +static int xfp_reset_phy(struct efx_nic *efx) +{ + int rc; + + rc = mdio_clause45_reset_mmd(efx, MDIO_MMD_PHYXS, + XFP_MAX_RESET_TIME / XFP_RESET_WAIT, + XFP_RESET_WAIT); + if (rc < 0) + goto fail; + + /* Wait 250ms for the PHY to complete bootup */ + msleep(250); + + /* Check that all the MMDs we expect are present and responding. We + * expect faults on some if the link is down, but not on the PHY XS */ + rc = mdio_clause45_check_mmds(efx, XFP_REQUIRED_DEVS, + MDIO_MMDREG_DEVS0_PHYXS); + if (rc < 0) + goto fail; + + efx->board_info.init_leds(efx); + + return rc; + + fail: + EFX_ERR(efx, "XFP: reset timed out!\n"); + return rc; +} + +static int xfp_phy_init(struct efx_nic *efx) +{ + u32 devid = mdio_clause45_read_id(efx, MDIO_MMD_PHYXS); + int rc; + + EFX_INFO(efx, "XFP: PHY ID reg %x (OUI %x model %x revision" + " %x)\n", devid, MDIO_ID_OUI(devid), MDIO_ID_MODEL(devid), + MDIO_ID_REV(devid)); + + rc = xfp_reset_phy(efx); + + EFX_INFO(efx, "XFP: PHY init %s.\n", + rc ? "failed" : "successful"); + + return rc; +} + +static void xfp_phy_clear_interrupt(struct efx_nic *efx) +{ + xenpack_clear_lasi_irqs(efx); +} + +static int xfp_link_ok(struct efx_nic *efx) +{ + return mdio_clause45_links_ok(efx, XFP_REQUIRED_DEVS); +} + +static int xfp_phy_check_hw(struct efx_nic *efx) +{ + int rc = 0; + int link_up = xfp_link_ok(efx); + /* Simulate a PHY event if link state has changed */ + if (link_up != efx->link_up) + falcon_xmac_sim_phy_event(efx); + + return rc; +} + +static void xfp_phy_reconfigure(struct efx_nic *efx) +{ + efx->link_up = xfp_link_ok(efx); + efx->link_options = GM_LPA_10000FULL; +} + + +static void xfp_phy_fini(struct efx_nic *efx) +{ + /* Clobber the LED if it was blinking */ + efx->board_info.blink(efx, 0); +} + +struct efx_phy_operations falcon_xfp_phy_ops = { + .init = xfp_phy_init, + .reconfigure = xfp_phy_reconfigure, + .check_hw = xfp_phy_check_hw, + .fini = xfp_phy_fini, + .clear_interrupt = xfp_phy_clear_interrupt, + .reset_xaui = efx_port_dummy_op_void, + .mmds = XFP_REQUIRED_DEVS, +}; diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c index 20745fd4e97..abc63b0663b 100644 --- a/drivers/net/sis190.c +++ b/drivers/net/sis190.c @@ -212,6 +212,12 @@ enum _DescStatusBit { THOL2 = 0x20000000, THOL1 = 0x10000000, THOL0 = 0x00000000, + + WND = 0x00080000, + TABRT = 0x00040000, + FIFO = 0x00020000, + LINK = 0x00010000, + ColCountMask = 0x0000ffff, /* RxDesc.status */ IPON = 0x20000000, TCPON = 0x10000000, @@ -480,30 +486,23 @@ static inline void sis190_make_unusable_by_asic(struct RxDesc *desc) desc->status = 0x0; } -static int sis190_alloc_rx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff, - struct RxDesc *desc, u32 rx_buf_sz) +static struct sk_buff *sis190_alloc_rx_skb(struct sis190_private *tp, + struct RxDesc *desc) { + u32 rx_buf_sz = tp->rx_buf_sz; struct sk_buff *skb; - dma_addr_t mapping; - int ret = 0; - - skb = dev_alloc_skb(rx_buf_sz); - if (!skb) - goto err_out; - - *sk_buff = skb; - mapping = pci_map_single(pdev, skb->data, rx_buf_sz, - PCI_DMA_FROMDEVICE); + skb = netdev_alloc_skb(tp->dev, rx_buf_sz); + if (likely(skb)) { + dma_addr_t mapping; - sis190_map_to_asic(desc, mapping, rx_buf_sz); -out: - return ret; + mapping = pci_map_single(tp->pci_dev, skb->data, tp->rx_buf_sz, + PCI_DMA_FROMDEVICE); + sis190_map_to_asic(desc, mapping, rx_buf_sz); + } else + sis190_make_unusable_by_asic(desc); -err_out: - ret = -ENOMEM; - sis190_make_unusable_by_asic(desc); - goto out; + return skb; } static u32 sis190_rx_fill(struct sis190_private *tp, struct net_device *dev, @@ -512,37 +511,41 @@ static u32 sis190_rx_fill(struct sis190_private *tp, struct net_device *dev, u32 cur; for (cur = start; cur < end; cur++) { - int ret, i = cur % NUM_RX_DESC; + unsigned int i = cur % NUM_RX_DESC; if (tp->Rx_skbuff[i]) continue; - ret = sis190_alloc_rx_skb(tp->pci_dev, tp->Rx_skbuff + i, - tp->RxDescRing + i, tp->rx_buf_sz); - if (ret < 0) + tp->Rx_skbuff[i] = sis190_alloc_rx_skb(tp, tp->RxDescRing + i); + + if (!tp->Rx_skbuff[i]) break; } return cur - start; } -static inline int sis190_try_rx_copy(struct sk_buff **sk_buff, int pkt_size, - struct RxDesc *desc, int rx_buf_sz) +static bool sis190_try_rx_copy(struct sis190_private *tp, + struct sk_buff **sk_buff, int pkt_size, + dma_addr_t addr) { - int ret = -1; + struct sk_buff *skb; + bool done = false; - if (pkt_size < rx_copybreak) { - struct sk_buff *skb; + if (pkt_size >= rx_copybreak) + goto out; - skb = dev_alloc_skb(pkt_size + NET_IP_ALIGN); - if (skb) { - skb_reserve(skb, NET_IP_ALIGN); - skb_copy_to_linear_data(skb, sk_buff[0]->data, pkt_size); - *sk_buff = skb; - sis190_give_to_asic(desc, rx_buf_sz); - ret = 0; - } - } - return ret; + skb = netdev_alloc_skb(tp->dev, pkt_size + 2); + if (!skb) + goto out; + + pci_dma_sync_single_for_device(tp->pci_dev, addr, pkt_size, + PCI_DMA_FROMDEVICE); + skb_reserve(skb, 2); + skb_copy_to_linear_data(skb, sk_buff[0]->data, pkt_size); + *sk_buff = skb; + done = true; +out: + return done; } static inline int sis190_rx_pkt_err(u32 status, struct net_device_stats *stats) @@ -592,9 +595,9 @@ static int sis190_rx_interrupt(struct net_device *dev, sis190_give_to_asic(desc, tp->rx_buf_sz); else { struct sk_buff *skb = tp->Rx_skbuff[entry]; + dma_addr_t addr = le32_to_cpu(desc->addr); int pkt_size = (status & RxSizeMask) - 4; - void (*pci_action)(struct pci_dev *, dma_addr_t, - size_t, int) = pci_dma_sync_single_for_device; + struct pci_dev *pdev = tp->pci_dev; if (unlikely(pkt_size > tp->rx_buf_sz)) { net_intr(tp, KERN_INFO @@ -606,20 +609,18 @@ static int sis190_rx_interrupt(struct net_device *dev, continue; } - pci_dma_sync_single_for_cpu(tp->pci_dev, - le32_to_cpu(desc->addr), tp->rx_buf_sz, - PCI_DMA_FROMDEVICE); - if (sis190_try_rx_copy(&skb, pkt_size, desc, - tp->rx_buf_sz)) { - pci_action = pci_unmap_single; + if (sis190_try_rx_copy(tp, &skb, pkt_size, addr)) { + pci_dma_sync_single_for_device(pdev, addr, + tp->rx_buf_sz, PCI_DMA_FROMDEVICE); + sis190_give_to_asic(desc, tp->rx_buf_sz); + } else { + pci_unmap_single(pdev, addr, tp->rx_buf_sz, + PCI_DMA_FROMDEVICE); tp->Rx_skbuff[entry] = NULL; sis190_make_unusable_by_asic(desc); } - pci_action(tp->pci_dev, le32_to_cpu(desc->addr), - tp->rx_buf_sz, PCI_DMA_FROMDEVICE); - skb_put(skb, pkt_size); skb->protocol = eth_type_trans(skb, dev); @@ -658,9 +659,31 @@ static void sis190_unmap_tx_skb(struct pci_dev *pdev, struct sk_buff *skb, memset(desc, 0x00, sizeof(*desc)); } +static inline int sis190_tx_pkt_err(u32 status, struct net_device_stats *stats) +{ +#define TxErrMask (WND | TABRT | FIFO | LINK) + + if (!unlikely(status & TxErrMask)) + return 0; + + if (status & WND) + stats->tx_window_errors++; + if (status & TABRT) + stats->tx_aborted_errors++; + if (status & FIFO) + stats->tx_fifo_errors++; + if (status & LINK) + stats->tx_carrier_errors++; + + stats->tx_errors++; + + return -1; +} + static void sis190_tx_interrupt(struct net_device *dev, struct sis190_private *tp, void __iomem *ioaddr) { + struct net_device_stats *stats = &dev->stats; u32 pending, dirty_tx = tp->dirty_tx; /* * It would not be needed if queueing was allowed to be enabled @@ -675,15 +698,19 @@ static void sis190_tx_interrupt(struct net_device *dev, for (; pending; pending--, dirty_tx++) { unsigned int entry = dirty_tx % NUM_TX_DESC; struct TxDesc *txd = tp->TxDescRing + entry; + u32 status = le32_to_cpu(txd->status); struct sk_buff *skb; - if (le32_to_cpu(txd->status) & OWNbit) + if (status & OWNbit) break; skb = tp->Tx_skbuff[entry]; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; + if (likely(sis190_tx_pkt_err(status, stats) == 0)) { + stats->tx_packets++; + stats->tx_bytes += skb->len; + stats->collisions += ((status & ColCountMask) - 1); + } sis190_unmap_tx_skb(tp->pci_dev, skb, txd); tp->Tx_skbuff[entry] = NULL; @@ -904,10 +931,9 @@ static void sis190_phy_task(struct work_struct *work) mod_timer(&tp->timer, jiffies + HZ/10); } else if (!(mdio_read_latched(ioaddr, phy_id, MII_BMSR) & BMSR_ANEGCOMPLETE)) { - net_link(tp, KERN_WARNING "%s: PHY reset until link up.\n", - dev->name); netif_carrier_off(dev); - mdio_write(ioaddr, phy_id, MII_BMCR, val | BMCR_RESET); + net_link(tp, KERN_WARNING "%s: auto-negotiating...\n", + dev->name); mod_timer(&tp->timer, jiffies + SIS190_PHY_TIMEOUT); } else { /* Rejoice ! */ diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 5a55ede352f..84af68fdb6c 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -396,14 +396,14 @@ static void sl_encaps(struct slip *sl, unsigned char *icp, int len) /* Order of next two lines is *very* important. * When we are sending a little amount of data, - * the transfer may be completed inside driver.write() + * the transfer may be completed inside the ops->write() * routine, because it's running with interrupts enabled. * In this case we *never* got WRITE_WAKEUP event, * if we did not request it before write operation. * 14 Oct 1994 Dmitry Gorodchanin. */ sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - actual = sl->tty->driver->write(sl->tty, sl->xbuff, count); + actual = sl->tty->ops->write(sl->tty, sl->xbuff, count); #ifdef SL_CHECK_TRANSMIT sl->dev->trans_start = jiffies; #endif @@ -437,7 +437,7 @@ static void slip_write_wakeup(struct tty_struct *tty) return; } - actual = tty->driver->write(tty, sl->xhead, sl->xleft); + actual = tty->ops->write(tty, sl->xhead, sl->xleft); sl->xleft -= actual; sl->xhead += actual; } @@ -462,7 +462,7 @@ static void sl_tx_timeout(struct net_device *dev) } printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, - (sl->tty->driver->chars_in_buffer(sl->tty) || sl->xleft) ? + (tty_chars_in_buffer(sl->tty) || sl->xleft) ? "bad line quality" : "driver error"); sl->xleft = 0; sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); @@ -830,6 +830,9 @@ static int slip_open(struct tty_struct *tty) if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (tty->ops->write == NULL) + return -EOPNOTSUPP; + /* RTnetlink lock is misused here to serialize concurrent opens of slip channels. There are better ways, but it is the simplest one. @@ -1432,7 +1435,7 @@ static void sl_outfill(unsigned long sls) /* put END into tty queue. Is it right ??? */ if (!netif_queue_stopped(sl->dev)) { /* if device busy no outfill */ - sl->tty->driver->write(sl->tty, &s, 1); + sl->tty->ops->write(sl->tty, &s, 1); } } else set_bit(SLF_OUTWAIT, &sl->flags); diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c index 63abfd72542..e03eef2f228 100644 --- a/drivers/net/wan/pc300_tty.c +++ b/drivers/net/wan/pc300_tty.c @@ -178,6 +178,20 @@ static void cpc_tty_signal_on(pc300dev_t *pc300dev, unsigned char signal) CPC_TTY_UNLOCK(card,flags); } + +static const struct tty_operations pc300_ops = { + .open = cpc_tty_open, + .close = cpc_tty_close, + .write = cpc_tty_write, + .write_room = cpc_tty_write_room, + .chars_in_buffer = cpc_tty_chars_in_buffer, + .tiocmset = pc300_tiocmset, + .tiocmget = pc300_tiocmget, + .flush_buffer = cpc_tty_flush_buffer, + .hangup = cpc_tty_hangup, +}; + + /* * PC300 TTY initialization routine * @@ -225,15 +239,7 @@ void cpc_tty_init(pc300dev_t *pc300dev) serial_drv.flags = TTY_DRIVER_REAL_RAW; /* interface routines from the upper tty layer to the tty driver */ - serial_drv.open = cpc_tty_open; - serial_drv.close = cpc_tty_close; - serial_drv.write = cpc_tty_write; - serial_drv.write_room = cpc_tty_write_room; - serial_drv.chars_in_buffer = cpc_tty_chars_in_buffer; - serial_drv.tiocmset = pc300_tiocmset; - serial_drv.tiocmget = pc300_tiocmget; - serial_drv.flush_buffer = cpc_tty_flush_buffer; - serial_drv.hangup = cpc_tty_hangup; + tty_set_operations(&serial_drv, &pc300_ops); /* register the TTY driver */ if (tty_register_driver(&serial_drv)) { diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 0f8aca8a4d4..249e18053d5 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -17,7 +17,7 @@ #include <linux/module.h> #include <asm/system.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/bitops.h> #include <linux/string.h> #include <linux/mm.h> @@ -95,7 +95,7 @@ static struct x25_asy *x25_asy_alloc(void) x25_asy_devs[i] = dev; return sl; } else { - printk("x25_asy_alloc() - register_netdev() failure.\n"); + printk(KERN_WARNING "x25_asy_alloc() - register_netdev() failure.\n"); free_netdev(dev); } } @@ -112,23 +112,22 @@ static void x25_asy_free(struct x25_asy *sl) kfree(sl->xbuff); sl->xbuff = NULL; - if (!test_and_clear_bit(SLF_INUSE, &sl->flags)) { - printk("%s: x25_asy_free for already free unit.\n", sl->dev->name); - } + if (!test_and_clear_bit(SLF_INUSE, &sl->flags)) + printk(KERN_ERR "%s: x25_asy_free for already free unit.\n", + sl->dev->name); } static int x25_asy_change_mtu(struct net_device *dev, int newmtu) { struct x25_asy *sl = dev->priv; unsigned char *xbuff, *rbuff; - int len = 2* newmtu; + int len = 2 * newmtu; xbuff = kmalloc(len + 4, GFP_ATOMIC); rbuff = kmalloc(len + 4, GFP_ATOMIC); - if (xbuff == NULL || rbuff == NULL) - { - printk("%s: unable to grow X.25 buffers, MTU change cancelled.\n", + if (xbuff == NULL || rbuff == NULL) { + printk(KERN_WARNING "%s: unable to grow X.25 buffers, MTU change cancelled.\n", dev->name); kfree(xbuff); kfree(rbuff); @@ -193,25 +192,23 @@ static void x25_asy_bump(struct x25_asy *sl) int err; count = sl->rcount; - sl->stats.rx_bytes+=count; - + sl->stats.rx_bytes += count; + skb = dev_alloc_skb(count+1); - if (skb == NULL) - { - printk("%s: memory squeeze, dropping packet.\n", sl->dev->name); + if (skb == NULL) { + printk(KERN_WARNING "%s: memory squeeze, dropping packet.\n", + sl->dev->name); sl->stats.rx_dropped++; return; } - skb_push(skb,1); /* LAPB internal control */ - memcpy(skb_put(skb,count), sl->rbuff, count); + skb_push(skb, 1); /* LAPB internal control */ + memcpy(skb_put(skb, count), sl->rbuff, count); skb->protocol = x25_type_trans(skb, sl->dev); - if((err=lapb_data_received(skb->dev, skb))!=LAPB_OK) - { + err = lapb_data_received(skb->dev, skb); + if (err != LAPB_OK) { kfree_skb(skb); - printk(KERN_DEBUG "x25_asy: data received err - %d\n",err); - } - else - { + printk(KERN_DEBUG "x25_asy: data received err - %d\n", err); + } else { netif_rx(skb); sl->dev->last_rx = jiffies; sl->stats.rx_packets++; @@ -224,10 +221,11 @@ static void x25_asy_encaps(struct x25_asy *sl, unsigned char *icp, int len) unsigned char *p; int actual, count, mtu = sl->dev->mtu; - if (len > mtu) - { /* Sigh, shouldn't occur BUT ... */ + if (len > mtu) { + /* Sigh, shouldn't occur BUT ... */ len = mtu; - printk ("%s: truncating oversized transmit packet!\n", sl->dev->name); + printk(KERN_DEBUG "%s: truncating oversized transmit packet!\n", + sl->dev->name); sl->stats.tx_dropped++; x25_asy_unlock(sl); return; @@ -245,7 +243,7 @@ static void x25_asy_encaps(struct x25_asy *sl, unsigned char *icp, int len) * 14 Oct 1994 Dmitry Gorodchanin. */ sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - actual = sl->tty->driver->write(sl->tty, sl->xbuff, count); + actual = sl->tty->ops->write(sl->tty, sl->xbuff, count); sl->xleft = count - actual; sl->xhead = sl->xbuff + actual; /* VSV */ @@ -265,8 +263,7 @@ static void x25_asy_write_wakeup(struct tty_struct *tty) if (!sl || sl->magic != X25_ASY_MAGIC || !netif_running(sl->dev)) return; - if (sl->xleft <= 0) - { + if (sl->xleft <= 0) { /* Now serial buffer is almost free & we can start * transmission of another packet */ sl->stats.tx_packets++; @@ -275,14 +272,14 @@ static void x25_asy_write_wakeup(struct tty_struct *tty) return; } - actual = tty->driver->write(tty, sl->xhead, sl->xleft); + actual = tty->ops->write(tty, sl->xhead, sl->xleft); sl->xleft -= actual; sl->xhead += actual; } static void x25_asy_timeout(struct net_device *dev) { - struct x25_asy *sl = (struct x25_asy*)(dev->priv); + struct x25_asy *sl = dev->priv; spin_lock(&sl->lock); if (netif_queue_stopped(dev)) { @@ -290,7 +287,7 @@ static void x25_asy_timeout(struct net_device *dev) * 14 Oct 1994 Dmitry Gorodchanin. */ printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, - (sl->tty->driver->chars_in_buffer(sl->tty) || sl->xleft) ? + (tty_chars_in_buffer(sl->tty) || sl->xleft) ? "bad line quality" : "driver error"); sl->xleft = 0; sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); @@ -303,31 +300,34 @@ static void x25_asy_timeout(struct net_device *dev) static int x25_asy_xmit(struct sk_buff *skb, struct net_device *dev) { - struct x25_asy *sl = (struct x25_asy*)(dev->priv); + struct x25_asy *sl = dev->priv; int err; if (!netif_running(sl->dev)) { - printk("%s: xmit call when iface is down\n", dev->name); + printk(KERN_ERR "%s: xmit call when iface is down\n", + dev->name); kfree_skb(skb); return 0; } - - switch(skb->data[0]) - { - case 0x00:break; - case 0x01: /* Connection request .. do nothing */ - if((err=lapb_connect_request(dev))!=LAPB_OK) - printk(KERN_ERR "x25_asy: lapb_connect_request error - %d\n", err); - kfree_skb(skb); - return 0; - case 0x02: /* Disconnect request .. do nothing - hang up ?? */ - if((err=lapb_disconnect_request(dev))!=LAPB_OK) - printk(KERN_ERR "x25_asy: lapb_disconnect_request error - %d\n", err); - default: - kfree_skb(skb); - return 0; + + switch (skb->data[0]) { + case 0x00: + break; + case 0x01: /* Connection request .. do nothing */ + err = lapb_connect_request(dev); + if (err != LAPB_OK) + printk(KERN_ERR "x25_asy: lapb_connect_request error - %d\n", err); + kfree_skb(skb); + return 0; + case 0x02: /* Disconnect request .. do nothing - hang up ?? */ + err = lapb_disconnect_request(dev); + if (err != LAPB_OK) + printk(KERN_ERR "x25_asy: lapb_disconnect_request error - %d\n", err); + default: + kfree_skb(skb); + return 0; } - skb_pull(skb,1); /* Remove control byte */ + skb_pull(skb, 1); /* Remove control byte */ /* * If we are busy already- too bad. We ought to be able * to queue things at this point, to allow for a little @@ -338,10 +338,10 @@ static int x25_asy_xmit(struct sk_buff *skb, struct net_device *dev) * So, no queues ! * 14 Oct 1994 Dmitry Gorodchanin. */ - - if((err=lapb_data_request(dev,skb))!=LAPB_OK) - { - printk(KERN_ERR "lapbeth: lapb_data_request error - %d\n", err); + + err = lapb_data_request(dev, skb); + if (err != LAPB_OK) { + printk(KERN_ERR "x25_asy: lapb_data_request error - %d\n", err); kfree_skb(skb); return 0; } @@ -357,7 +357,7 @@ static int x25_asy_xmit(struct sk_buff *skb, struct net_device *dev) * Called when I frame data arrives. We did the work above - throw it * at the net layer. */ - + static int x25_asy_data_indication(struct net_device *dev, struct sk_buff *skb) { skb->dev->last_rx = jiffies; @@ -369,24 +369,22 @@ static int x25_asy_data_indication(struct net_device *dev, struct sk_buff *skb) * busy cases too well. Its tricky to see how to do this nicely - * perhaps lapb should allow us to bounce this ? */ - + static void x25_asy_data_transmit(struct net_device *dev, struct sk_buff *skb) { - struct x25_asy *sl=dev->priv; - + struct x25_asy *sl = dev->priv; + spin_lock(&sl->lock); - if (netif_queue_stopped(sl->dev) || sl->tty == NULL) - { + if (netif_queue_stopped(sl->dev) || sl->tty == NULL) { spin_unlock(&sl->lock); printk(KERN_ERR "x25_asy: tbusy drop\n"); kfree_skb(skb); return; } /* We were not busy, so we are now... :-) */ - if (skb != NULL) - { + if (skb != NULL) { x25_asy_lock(sl); - sl->stats.tx_bytes+=skb->len; + sl->stats.tx_bytes += skb->len; x25_asy_encaps(sl, skb->data, skb->len); dev_kfree_skb(skb); } @@ -396,15 +394,16 @@ static void x25_asy_data_transmit(struct net_device *dev, struct sk_buff *skb) /* * LAPB connection establish/down information. */ - + static void x25_asy_connected(struct net_device *dev, int reason) { struct x25_asy *sl = dev->priv; struct sk_buff *skb; unsigned char *ptr; - if ((skb = dev_alloc_skb(1)) == NULL) { - printk(KERN_ERR "lapbeth: out of memory\n"); + skb = dev_alloc_skb(1); + if (skb == NULL) { + printk(KERN_ERR "x25_asy: out of memory\n"); return; } @@ -422,7 +421,8 @@ static void x25_asy_disconnected(struct net_device *dev, int reason) struct sk_buff *skb; unsigned char *ptr; - if ((skb = dev_alloc_skb(1)) == NULL) { + skb = dev_alloc_skb(1); + if (skb == NULL) { printk(KERN_ERR "x25_asy: out of memory\n"); return; } @@ -449,7 +449,7 @@ static struct lapb_register_struct x25_asy_callbacks = { /* Open the low-level part of the X.25 channel. Easy! */ static int x25_asy_open(struct net_device *dev) { - struct x25_asy *sl = (struct x25_asy*)(dev->priv); + struct x25_asy *sl = dev->priv; unsigned long len; int err; @@ -466,13 +466,11 @@ static int x25_asy_open(struct net_device *dev) len = dev->mtu * 2; sl->rbuff = kmalloc(len + 4, GFP_KERNEL); - if (sl->rbuff == NULL) { + if (sl->rbuff == NULL) goto norbuff; - } sl->xbuff = kmalloc(len + 4, GFP_KERNEL); - if (sl->xbuff == NULL) { + if (sl->xbuff == NULL) goto noxbuff; - } sl->buffsize = len; sl->rcount = 0; @@ -480,11 +478,12 @@ static int x25_asy_open(struct net_device *dev) sl->flags &= (1 << SLF_INUSE); /* Clear ESCAPE & ERROR flags */ netif_start_queue(dev); - + /* * Now attach LAPB */ - if((err=lapb_register(dev, &x25_asy_callbacks))==LAPB_OK) + err = lapb_register(dev, &x25_asy_callbacks); + if (err == LAPB_OK) return 0; /* Cleanup */ @@ -499,18 +498,20 @@ norbuff: /* Close the low-level part of the X.25 channel. Easy! */ static int x25_asy_close(struct net_device *dev) { - struct x25_asy *sl = (struct x25_asy*)(dev->priv); + struct x25_asy *sl = dev->priv; int err; spin_lock(&sl->lock); - if (sl->tty) + if (sl->tty) sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); netif_stop_queue(dev); sl->rcount = 0; sl->xleft = 0; - if((err=lapb_unregister(dev))!=LAPB_OK) - printk(KERN_ERR "x25_asy_close: lapb_unregister error -%d\n",err); + err = lapb_unregister(dev); + if (err != LAPB_OK) + printk(KERN_ERR "x25_asy_close: lapb_unregister error -%d\n", + err); spin_unlock(&sl->lock); return 0; } @@ -521,8 +522,9 @@ static int x25_asy_close(struct net_device *dev) * a block of X.25 data has been received, which can now be decapsulated * and sent on to some IP layer for further processing. */ - -static void x25_asy_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) + +static void x25_asy_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { struct x25_asy *sl = (struct x25_asy *) tty->disc_data; @@ -533,9 +535,8 @@ static void x25_asy_receive_buf(struct tty_struct *tty, const unsigned char *cp, /* Read the characters out of the buffer */ while (count--) { if (fp && *fp++) { - if (!test_and_set_bit(SLF_ERROR, &sl->flags)) { + if (!test_and_set_bit(SLF_ERROR, &sl->flags)) sl->stats.rx_errors++; - } cp++; continue; } @@ -556,31 +557,31 @@ static int x25_asy_open_tty(struct tty_struct *tty) struct x25_asy *sl = (struct x25_asy *) tty->disc_data; int err; + if (tty->ops->write == NULL) + return -EOPNOTSUPP; + /* First make sure we're not already connected. */ - if (sl && sl->magic == X25_ASY_MAGIC) { + if (sl && sl->magic == X25_ASY_MAGIC) return -EEXIST; - } /* OK. Find a free X.25 channel to use. */ - if ((sl = x25_asy_alloc()) == NULL) { + sl = x25_asy_alloc(); + if (sl == NULL) return -ENFILE; - } sl->tty = tty; tty->disc_data = sl; tty->receive_room = 65536; - if (tty->driver->flush_buffer) { - tty->driver->flush_buffer(tty); - } + tty_driver_flush_buffer(tty); tty_ldisc_flush(tty); /* Restore default settings */ sl->dev->type = ARPHRD_X25; - + /* Perform the low-level X.25 async init */ - if ((err = x25_asy_open(sl->dev))) + err = x25_asy_open(sl->dev); + if (err) return err; - /* Done. We have linked the TTY line to a channel. */ return sl->dev->base_addr; } @@ -601,9 +602,7 @@ static void x25_asy_close_tty(struct tty_struct *tty) return; if (sl->dev->flags & IFF_UP) - { - (void) dev_close(sl->dev); - } + dev_close(sl->dev); tty->disc_data = NULL; sl->tty = NULL; @@ -613,8 +612,7 @@ static void x25_asy_close_tty(struct tty_struct *tty) static struct net_device_stats *x25_asy_get_stats(struct net_device *dev) { - struct x25_asy *sl = (struct x25_asy*)(dev->priv); - + struct x25_asy *sl = dev->priv; return &sl->stats; } @@ -641,21 +639,19 @@ int x25_asy_esc(unsigned char *s, unsigned char *d, int len) * character sequence, according to the X.25 protocol. */ - while (len-- > 0) - { - switch(c = *s++) - { - case X25_END: - *ptr++ = X25_ESC; - *ptr++ = X25_ESCAPE(X25_END); - break; - case X25_ESC: - *ptr++ = X25_ESC; - *ptr++ = X25_ESCAPE(X25_ESC); - break; - default: - *ptr++ = c; - break; + while (len-- > 0) { + switch (c = *s++) { + case X25_END: + *ptr++ = X25_ESC; + *ptr++ = X25_ESCAPE(X25_END); + break; + case X25_ESC: + *ptr++ = X25_ESC; + *ptr++ = X25_ESCAPE(X25_ESC); + break; + default: + *ptr++ = c; + break; } } *ptr++ = X25_END; @@ -665,31 +661,25 @@ int x25_asy_esc(unsigned char *s, unsigned char *d, int len) static void x25_asy_unesc(struct x25_asy *sl, unsigned char s) { - switch(s) - { - case X25_END: - if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 2)) - { - x25_asy_bump(sl); - } - clear_bit(SLF_ESCAPE, &sl->flags); - sl->rcount = 0; - return; - - case X25_ESC: - set_bit(SLF_ESCAPE, &sl->flags); - return; - - case X25_ESCAPE(X25_ESC): - case X25_ESCAPE(X25_END): - if (test_and_clear_bit(SLF_ESCAPE, &sl->flags)) - s = X25_UNESCAPE(s); - break; - } - if (!test_bit(SLF_ERROR, &sl->flags)) - { - if (sl->rcount < sl->buffsize) - { + switch (s) { + case X25_END: + if (!test_and_clear_bit(SLF_ERROR, &sl->flags) + && sl->rcount > 2) + x25_asy_bump(sl); + clear_bit(SLF_ESCAPE, &sl->flags); + sl->rcount = 0; + return; + case X25_ESC: + set_bit(SLF_ESCAPE, &sl->flags); + return; + case X25_ESCAPE(X25_ESC): + case X25_ESCAPE(X25_END): + if (test_and_clear_bit(SLF_ESCAPE, &sl->flags)) + s = X25_UNESCAPE(s); + break; + } + if (!test_bit(SLF_ERROR, &sl->flags)) { + if (sl->rcount < sl->buffsize) { sl->rbuff[sl->rcount++] = s; return; } @@ -709,7 +699,7 @@ static int x25_asy_ioctl(struct tty_struct *tty, struct file *file, if (!sl || sl->magic != X25_ASY_MAGIC) return -EINVAL; - switch(cmd) { + switch (cmd) { case SIOCGIFNAME: if (copy_to_user((void __user *)arg, sl->dev->name, strlen(sl->dev->name) + 1)) @@ -724,8 +714,8 @@ static int x25_asy_ioctl(struct tty_struct *tty, struct file *file, static int x25_asy_open_dev(struct net_device *dev) { - struct x25_asy *sl = (struct x25_asy*)(dev->priv); - if(sl->tty==NULL) + struct x25_asy *sl = dev->priv; + if (sl->tty == NULL) return -ENODEV; return 0; } @@ -741,9 +731,9 @@ static void x25_asy_setup(struct net_device *dev) set_bit(SLF_INUSE, &sl->flags); /* - * Finish setting up the DEVICE info. + * Finish setting up the DEVICE info. */ - + dev->mtu = SL_MTU; dev->hard_start_xmit = x25_asy_xmit; dev->tx_timeout = x25_asy_timeout; @@ -778,9 +768,10 @@ static int __init init_x25_asy(void) x25_asy_maxdev = 4; /* Sanity */ printk(KERN_INFO "X.25 async: version 0.00 ALPHA " - "(dynamic channels, max=%d).\n", x25_asy_maxdev ); + "(dynamic channels, max=%d).\n", x25_asy_maxdev); - x25_asy_devs = kcalloc(x25_asy_maxdev, sizeof(struct net_device*), GFP_KERNEL); + x25_asy_devs = kcalloc(x25_asy_maxdev, sizeof(struct net_device *), + GFP_KERNEL); if (!x25_asy_devs) { printk(KERN_WARNING "X25 async: Can't allocate x25_asy_ctrls[] " "array! Uaargh! (-> No X.25 available)\n"); @@ -802,7 +793,7 @@ static void __exit exit_x25_asy(void) struct x25_asy *sl = dev->priv; spin_lock_bh(&sl->lock); - if (sl->tty) + if (sl->tty) tty_hangup(sl->tty); spin_unlock_bh(&sl->lock); diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 9a25f550fd1..d5b7a76fcaa 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -6,6 +6,10 @@ config IWLCORE tristate "Intel Wireless Wifi Core" depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL select IWLWIFI + select MAC80211_LEDS if IWLWIFI_LEDS + select LEDS_CLASS if IWLWIFI_LEDS + select RFKILL if IWLWIFI_RFKILL + select RFKILL_INPUT if IWLWIFI_RFKILL config IWLWIFI_LEDS bool @@ -14,8 +18,6 @@ config IWLWIFI_LEDS config IWLWIFI_RFKILL boolean "IWLWIFI RF kill support" depends on IWLCORE - select RFKILL - select RFKILL_INPUT config IWL4965 tristate "Intel Wireless WiFi 4965AGN" @@ -55,8 +57,6 @@ config IWL4965_HT config IWL4965_LEDS bool "Enable LEDS features in iwl4965 driver" depends on IWL4965 - select MAC80211_LEDS - select LEDS_CLASS select IWLWIFI_LEDS ---help--- This option enables LEDS for the iwlwifi drivers @@ -112,6 +112,8 @@ config IWL3945 depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL select FW_LOADER select IWLWIFI + select MAC80211_LEDS if IWL3945_LEDS + select LEDS_CLASS if IWL3945_LEDS ---help--- Select to build the driver supporting the: @@ -143,8 +145,6 @@ config IWL3945_SPECTRUM_MEASUREMENT config IWL3945_LEDS bool "Enable LEDS features in iwl3945 driver" depends on IWL3945 - select MAC80211_LEDS - select LEDS_CLASS ---help--- This option enables LEDS for the iwl3945 driver. diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c index bced3fe1cf8..5dd23c93497 100644 --- a/drivers/net/wireless/strip.c +++ b/drivers/net/wireless/strip.c @@ -768,41 +768,17 @@ static __u8 *UnStuffData(__u8 * src, __u8 * end, __u8 * dst, /* General routines for STRIP */ /* - * get_baud returns the current baud rate, as one of the constants defined in - * termbits.h - * If the user has issued a baud rate override using the 'setserial' command - * and the logical current rate is set to 38.4, then the true baud rate - * currently in effect (57.6 or 115.2) is returned. - */ -static unsigned int get_baud(struct tty_struct *tty) -{ - if (!tty || !tty->termios) - return (0); - if ((tty->termios->c_cflag & CBAUD) == B38400 && tty->driver_data) { - struct async_struct *info = - (struct async_struct *) tty->driver_data; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - return (B57600); - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - return (B115200); - } - return (tty->termios->c_cflag & CBAUD); -} - -/* * set_baud sets the baud rate to the rate defined by baudcode - * Note: The rate B38400 should be avoided, because the user may have - * issued a 'setserial' speed override to map that to a different speed. - * We could achieve a true rate of 38400 if we needed to by cancelling - * any user speed override that is in place, but that might annoy the - * user, so it is simplest to just avoid using 38400. */ -static void set_baud(struct tty_struct *tty, unsigned int baudcode) +static void set_baud(struct tty_struct *tty, speed_t baudrate) { - struct ktermios old_termios = *(tty->termios); - tty->termios->c_cflag &= ~CBAUD; /* Clear the old baud setting */ - tty->termios->c_cflag |= baudcode; /* Set the new baud setting */ - tty->driver->set_termios(tty, &old_termios); + struct ktermios old_termios; + + mutex_lock(&tty->termios_mutex); + old_termios =*(tty->termios); + tty_encode_baud_rate(tty, baudrate, baudrate); + tty->ops->set_termios(tty, &old_termios); + mutex_unlock(&tty->termios_mutex); } /* @@ -1217,7 +1193,7 @@ static void ResetRadio(struct strip *strip_info) strip_info->watchdog_doreset = jiffies + 1 * HZ; /* If the user has selected a baud rate above 38.4 see what magic we have to do */ - if (strip_info->user_baud > B38400) { + if (strip_info->user_baud > 38400) { /* * Subtle stuff: Pay attention :-) * If the serial port is currently at the user's selected (>38.4) rate, @@ -1227,17 +1203,17 @@ static void ResetRadio(struct strip *strip_info) * issued the ATS304 command last time through, so this time we restore * the user's selected rate and issue the normal starmode reset string. */ - if (strip_info->user_baud == get_baud(tty)) { + if (strip_info->user_baud == tty_get_baud_rate(tty)) { static const char b0[] = "ate0q1s304=57600\r"; static const char b1[] = "ate0q1s304=115200\r"; static const StringDescriptor baudstring[2] = { {b0, sizeof(b0) - 1} , {b1, sizeof(b1) - 1} }; - set_baud(tty, B19200); - if (strip_info->user_baud == B57600) + set_baud(tty, 19200); + if (strip_info->user_baud == 57600) s = baudstring[0]; - else if (strip_info->user_baud == B115200) + else if (strip_info->user_baud == 115200) s = baudstring[1]; else s = baudstring[1]; /* For now */ @@ -1245,7 +1221,7 @@ static void ResetRadio(struct strip *strip_info) set_baud(tty, strip_info->user_baud); } - tty->driver->write(tty, s.string, s.length); + tty->ops->write(tty, s.string, s.length); #ifdef EXT_COUNTERS strip_info->tx_ebytes += s.length; #endif @@ -1267,7 +1243,7 @@ static void strip_write_some_more(struct tty_struct *tty) if (strip_info->tx_left > 0) { int num_written = - tty->driver->write(tty, strip_info->tx_head, + tty->ops->write(tty, strip_info->tx_head, strip_info->tx_left); strip_info->tx_left -= num_written; strip_info->tx_head += num_written; @@ -2457,7 +2433,7 @@ static int strip_open_low(struct net_device *dev) strip_info->working = FALSE; strip_info->firmware_level = NoStructure; strip_info->next_command = CompatibilityCommand; - strip_info->user_baud = get_baud(strip_info->tty); + strip_info->user_baud = tty_get_baud_rate(strip_info->tty); printk(KERN_INFO "%s: Initializing Radio.\n", strip_info->dev->name); @@ -2632,6 +2608,13 @@ static int strip_open(struct tty_struct *tty) return -EEXIST; /* + * We need a write method. + */ + + if (tty->ops->write == NULL) + return -EOPNOTSUPP; + + /* * OK. Find a free STRIP channel to use. */ if ((strip_info = strip_alloc()) == NULL) @@ -2652,8 +2635,7 @@ static int strip_open(struct tty_struct *tty) tty->disc_data = strip_info; tty->receive_room = 65536; - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty_driver_flush_buffer(tty); /* * Restore default settings diff --git a/drivers/parport/ieee1284.c b/drivers/parport/ieee1284.c index 54a6ef72906..0338b091267 100644 --- a/drivers/parport/ieee1284.c +++ b/drivers/parport/ieee1284.c @@ -76,7 +76,7 @@ int parport_wait_event (struct parport *port, signed long timeout) semaphore. */ return 1; - init_timer (&timer); + init_timer_on_stack(&timer); timer.expires = jiffies + timeout; timer.function = timeout_waiting_on_port; port_from_cookie[port->number % PARPORT_MAX] = port; @@ -88,6 +88,8 @@ int parport_wait_event (struct parport *port, signed long timeout) /* Timed out. */ ret = 1; + destroy_timer_on_stack(&timer); + return ret; } diff --git a/drivers/parport/parport_gsc.c b/drivers/parport/parport_gsc.c index 0e77ae2b71a..e6a7e847ee8 100644 --- a/drivers/parport/parport_gsc.c +++ b/drivers/parport/parport_gsc.c @@ -365,11 +365,11 @@ static int __devinit parport_init_chip(struct parisc_device *dev) if (boot_cpu_data.cpu_type > pcxt && !pdc_add_valid(port+4)) { /* Initialize bidirectional-mode (0x10) & data-tranfer-mode #1 (0x20) */ - printk("%s: initialize bidirectional-mode.\n", __FUNCTION__); + printk("%s: initialize bidirectional-mode.\n", __func__); parport_writeb ( (0x10 + 0x20), port + 4); } else { - printk("%s: enhanced parport-modes not supported.\n", __FUNCTION__); + printk("%s: enhanced parport-modes not supported.\n", __func__); } p = parport_gsc_probe_port(port, 0, dev->irq, diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index e71092e8028..e0c2a4584ec 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -1415,7 +1415,7 @@ static void __devinit winbond_check(int io, int key) { int devid,devrev,oldid,x_devid,x_devrev,x_oldid; - if (!request_region(io, 3, __FUNCTION__)) + if (!request_region(io, 3, __func__)) return; /* First probe without key */ @@ -1449,7 +1449,7 @@ static void __devinit winbond_check2(int io,int key) { int devid,devrev,oldid,x_devid,x_devrev,x_oldid; - if (!request_region(io, 3, __FUNCTION__)) + if (!request_region(io, 3, __func__)) return; /* First probe without the key */ @@ -1482,7 +1482,7 @@ static void __devinit smsc_check(int io, int key) { int id,rev,oldid,oldrev,x_id,x_rev,x_oldid,x_oldrev; - if (!request_region(io, 3, __FUNCTION__)) + if (!request_region(io, 3, __func__)) return; /* First probe without the key */ @@ -1547,7 +1547,7 @@ static void __devinit detect_and_report_it87(void) u8 r; if (verbose_probing) printk(KERN_DEBUG "IT8705 Super-IO detection, now testing port 2E ...\n"); - if (!request_region(0x2e, 1, __FUNCTION__)) + if (!request_region(0x2e, 1, __func__)) return; outb(0x87, 0x2e); outb(0x01, 0x2e); diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 0e1f35c9ed9..3e5653c92f4 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -982,15 +982,16 @@ tty3215_write(struct tty_struct * tty, /* * Put character routine for 3215 ttys */ -static void +static int tty3215_put_char(struct tty_struct *tty, unsigned char ch) { struct raw3215_info *raw; if (!tty) - return; + return 0; raw = (struct raw3215_info *) tty->driver_data; raw3215_putchar(raw, ch); + return 1; } static void diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c index b8f35bc52b7..9e784d5f7f5 100644 --- a/drivers/s390/char/sclp_config.c +++ b/drivers/s390/char/sclp_config.c @@ -10,6 +10,7 @@ #include <linux/cpu.h> #include <linux/sysdev.h> #include <linux/workqueue.h> +#include <asm/smp.h> #include "sclp.h" #define TAG "sclp_config: " @@ -19,9 +20,11 @@ struct conf_mgm_data { u8 ev_qualifier; } __attribute__((packed)); +#define EV_QUAL_CPU_CHANGE 1 #define EV_QUAL_CAP_CHANGE 3 static struct work_struct sclp_cpu_capability_work; +static struct work_struct sclp_cpu_change_work; static void sclp_cpu_capability_notify(struct work_struct *work) { @@ -37,13 +40,24 @@ static void sclp_cpu_capability_notify(struct work_struct *work) put_online_cpus(); } +static void sclp_cpu_change_notify(struct work_struct *work) +{ + smp_rescan_cpus(); +} + static void sclp_conf_receiver_fn(struct evbuf_header *evbuf) { struct conf_mgm_data *cdata; cdata = (struct conf_mgm_data *)(evbuf + 1); - if (cdata->ev_qualifier == EV_QUAL_CAP_CHANGE) + switch (cdata->ev_qualifier) { + case EV_QUAL_CPU_CHANGE: + schedule_work(&sclp_cpu_change_work); + break; + case EV_QUAL_CAP_CHANGE: schedule_work(&sclp_cpu_capability_work); + break; + } } static struct sclp_register sclp_conf_register = @@ -57,6 +71,7 @@ static int __init sclp_conf_init(void) int rc; INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify); + INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify); rc = sclp_register(&sclp_conf_register); if (rc) { diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index e3b3d390b4a..40b11521cd2 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -412,14 +412,14 @@ sclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) * - including previous characters from sclp_tty_put_char() and strings from * sclp_write() without final '\n' - will be written. */ -static void +static int sclp_tty_put_char(struct tty_struct *tty, unsigned char ch) { sclp_tty_chars[sclp_tty_chars_count++] = ch; if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) { sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); sclp_tty_chars_count = 0; - } + } return 1; } /* diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index ed507594e62..35707c04e61 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -524,11 +524,15 @@ sclp_vt220_close(struct tty_struct *tty, struct file *filp) * NOTE: include/linux/tty_driver.h specifies that a character should be * ignored if there is no room in the queue. This driver implements a different * semantic in that it will block when there is no more room left. + * + * FIXME: putchar can currently be called from BH and other non blocking + * handlers so this semantic isn't a good idea. */ -static void +static int sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch) { __sclp_vt220_write(&ch, 1, 0, 0, 1); + return 1; } /* diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 70b1980a08b..c1f2adefad4 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -965,7 +965,7 @@ tty3270_write_room(struct tty_struct *tty) * Insert character into the screen at the current position with the * current color and highlight. This function does NOT do cursor movement. */ -static void +static int tty3270_put_character(struct tty3270 *tp, char ch) { struct tty3270_line *line; @@ -986,6 +986,7 @@ tty3270_put_character(struct tty3270 *tp, char ch) cell->character = tp->view.ascebc[(unsigned int) ch]; cell->highlight = tp->highlight; cell->f_color = tp->f_color; + return 1; } /* diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index fe1ad172215..26a930e832b 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -152,44 +152,89 @@ __ccwgroup_create_symlinks(struct ccwgroup_device *gdev) return 0; } +static int __get_next_bus_id(const char **buf, char *bus_id) +{ + int rc, len; + char *start, *end; + + start = (char *)*buf; + end = strchr(start, ','); + if (!end) { + /* Last entry. Strip trailing newline, if applicable. */ + end = strchr(start, '\n'); + if (end) + *end = '\0'; + len = strlen(start) + 1; + } else { + len = end - start + 1; + end++; + } + if (len < BUS_ID_SIZE) { + strlcpy(bus_id, start, len); + rc = 0; + } else + rc = -EINVAL; + *buf = end; + return rc; +} + +static int __is_valid_bus_id(char bus_id[BUS_ID_SIZE]) +{ + int cssid, ssid, devno; + + /* Must be of form %x.%x.%04x */ + if (sscanf(bus_id, "%x.%1x.%04x", &cssid, &ssid, &devno) != 3) + return 0; + return 1; +} + /** - * ccwgroup_create() - create and register a ccw group device + * ccwgroup_create_from_string() - create and register a ccw group device * @root: parent device for the new device * @creator_id: identifier of creating driver * @cdrv: ccw driver of slave devices - * @argc: number of slave devices - * @argv: bus ids of slave devices + * @num_devices: number of slave devices + * @buf: buffer containing comma separated bus ids of slave devices * * Create and register a new ccw group device as a child of @root. Slave - * devices are obtained from the list of bus ids given in @argv[] and must all + * devices are obtained from the list of bus ids given in @buf and must all * belong to @cdrv. * Returns: * %0 on success and an error code on failure. * Context: * non-atomic */ -int ccwgroup_create(struct device *root, unsigned int creator_id, - struct ccw_driver *cdrv, int argc, char *argv[]) +int ccwgroup_create_from_string(struct device *root, unsigned int creator_id, + struct ccw_driver *cdrv, int num_devices, + const char *buf) { struct ccwgroup_device *gdev; - int i; - int rc; + int rc, i; + char tmp_bus_id[BUS_ID_SIZE]; + const char *curr_buf; - if (argc > 256) /* disallow dumb users */ - return -EINVAL; - - gdev = kzalloc(sizeof(*gdev) + argc*sizeof(gdev->cdev[0]), GFP_KERNEL); + gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]), + GFP_KERNEL); if (!gdev) return -ENOMEM; atomic_set(&gdev->onoff, 0); mutex_init(&gdev->reg_mutex); mutex_lock(&gdev->reg_mutex); - for (i = 0; i < argc; i++) { - gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]); - - /* all devices have to be of the same type in - * order to be grouped */ + curr_buf = buf; + for (i = 0; i < num_devices && curr_buf; i++) { + rc = __get_next_bus_id(&curr_buf, tmp_bus_id); + if (rc != 0) + goto error; + if (!__is_valid_bus_id(tmp_bus_id)) { + rc = -EINVAL; + goto error; + } + gdev->cdev[i] = get_ccwdev_by_busid(cdrv, tmp_bus_id); + /* + * All devices have to be of the same type in + * order to be grouped. + */ if (!gdev->cdev[i] || gdev->cdev[i]->id.driver_info != gdev->cdev[0]->id.driver_info) { @@ -203,9 +248,18 @@ int ccwgroup_create(struct device *root, unsigned int creator_id, } dev_set_drvdata(&gdev->cdev[i]->dev, gdev); } - + /* Check for sufficient number of bus ids. */ + if (i < num_devices && !curr_buf) { + rc = -EINVAL; + goto error; + } + /* Check for trailing stuff. */ + if (i == num_devices && strlen(curr_buf) > 0) { + rc = -EINVAL; + goto error; + } gdev->creator_id = creator_id; - gdev->count = argc; + gdev->count = num_devices; gdev->dev.bus = &ccwgroup_bus_type; gdev->dev.parent = root; gdev->dev.release = ccwgroup_release; @@ -233,7 +287,7 @@ int ccwgroup_create(struct device *root, unsigned int creator_id, device_remove_file(&gdev->dev, &dev_attr_ungroup); device_unregister(&gdev->dev); error: - for (i = 0; i < argc; i++) + for (i = 0; i < num_devices; i++) if (gdev->cdev[i]) { if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev) dev_set_drvdata(&gdev->cdev[i]->dev, NULL); @@ -243,6 +297,7 @@ error: put_device(&gdev->dev); return rc; } +EXPORT_SYMBOL(ccwgroup_create_from_string); static int __init init_ccwgroup (void) @@ -318,7 +373,7 @@ ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const { struct ccwgroup_device *gdev; struct ccwgroup_driver *gdrv; - unsigned int value; + unsigned long value; int ret; gdev = to_ccwgroupdev(dev); @@ -329,7 +384,9 @@ ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const if (!try_module_get(gdrv->owner)) return -EINVAL; - value = simple_strtoul(buf, NULL, 0); + ret = strict_strtoul(buf, 0, &value); + if (ret) + goto out; ret = count; if (value == 1) ccwgroup_set_online(gdev); @@ -337,6 +394,7 @@ ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const ccwgroup_set_offline(gdev); else ret = -EINVAL; +out: module_put(gdrv->owner); return ret; } @@ -518,6 +576,5 @@ void ccwgroup_remove_ccwdev(struct ccw_device *cdev) MODULE_LICENSE("GPL"); EXPORT_SYMBOL(ccwgroup_driver_register); EXPORT_SYMBOL(ccwgroup_driver_unregister); -EXPORT_SYMBOL(ccwgroup_create); EXPORT_SYMBOL(ccwgroup_probe_ccwdev); EXPORT_SYMBOL(ccwgroup_remove_ccwdev); diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 23ffcc4768a..08a57816130 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -407,8 +407,7 @@ cio_modify (struct subchannel *sch) /* * Enable subchannel. */ -int cio_enable_subchannel(struct subchannel *sch, unsigned int isc, - u32 intparm) +int cio_enable_subchannel(struct subchannel *sch, u32 intparm) { char dbf_txt[15]; int ccode; @@ -426,7 +425,7 @@ int cio_enable_subchannel(struct subchannel *sch, unsigned int isc, for (retry = 5, ret = 0; retry > 0; retry--) { sch->schib.pmcw.ena = 1; - sch->schib.pmcw.isc = isc; + sch->schib.pmcw.isc = sch->isc; sch->schib.pmcw.intparm = intparm; ret = cio_modify(sch); if (ret == -ENODEV) @@ -600,6 +599,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) else sch->opm = chp_get_sch_opm(sch); sch->lpm = sch->schib.pmcw.pam & sch->opm; + sch->isc = 3; CIO_DEBUG(KERN_INFO, 0, "Detected device %04x on subchannel 0.%x.%04X" @@ -610,13 +610,11 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) /* * We now have to initially ... - * ... set "interruption subclass" * ... enable "concurrent sense" * ... enable "multipath mode" if more than one * CHPID is available. This is done regardless * whether multiple paths are available for us. */ - sch->schib.pmcw.isc = 3; /* could be smth. else */ sch->schib.pmcw.csense = 1; /* concurrent sense */ sch->schib.pmcw.ena = 0; if ((sch->lpm & (sch->lpm - 1)) != 0) @@ -812,6 +810,7 @@ cio_probe_console(void) * enable console I/O-interrupt subclass 7 */ ctl_set_bit(6, 24); + console_subchannel.isc = 7; console_subchannel.schib.pmcw.isc = 7; console_subchannel.schib.pmcw.intparm = (u32)(addr_t)&console_subchannel; diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 08f2235c5a6..3c75412904d 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -74,6 +74,7 @@ struct subchannel { __u8 lpm; /* logical path mask */ __u8 opm; /* operational path mask */ struct schib schib; /* subchannel information block */ + int isc; /* desired interruption subclass */ struct chsc_ssd_info ssd_info; /* subchannel description */ struct device dev; /* entry in device tree */ struct css_driver *driver; @@ -85,7 +86,7 @@ struct subchannel { #define to_subchannel(n) container_of(n, struct subchannel, dev) extern int cio_validate_subchannel (struct subchannel *, struct subchannel_id); -extern int cio_enable_subchannel(struct subchannel *, unsigned int, u32); +extern int cio_enable_subchannel(struct subchannel *, u32); extern int cio_disable_subchannel (struct subchannel *); extern int cio_cancel (struct subchannel *); extern int cio_clear (struct subchannel *); diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index f4c132ab39e..2808b6833b9 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -1219,16 +1219,21 @@ static ssize_t cmb_enable_store(struct device *dev, { struct ccw_device *cdev; int ret; + unsigned long val; + + ret = strict_strtoul(buf, 16, &val); + if (ret) + return ret; cdev = to_ccwdev(dev); - switch (buf[0]) { - case '0': + switch (val) { + case 0: ret = disable_cmf(cdev); if (ret) dev_info(&cdev->dev, "disable_cmf failed (%d)\n", ret); break; - case '1': + case 1: ret = enable_cmf(cdev); if (ret && ret != -EBUSY) dev_info(&cdev->dev, "enable_cmf failed (%d)\n", ret); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index c1afab5f72d..595e327d2f7 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -705,13 +705,17 @@ css_cm_enable_store(struct device *dev, struct device_attribute *attr, { struct channel_subsystem *css = to_css(dev); int ret; + unsigned long val; + ret = strict_strtoul(buf, 16, &val); + if (ret) + return ret; mutex_lock(&css->mutex); - switch (buf[0]) { - case '0': + switch (val) { + case 0: ret = css->cm_enabled ? chsc_secm(css, 0) : 0; break; - case '1': + case 1: ret = css->cm_enabled ? 0 : chsc_secm(css, 1); break; default: diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e0c7adb8958..abfd601d237 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -512,8 +512,8 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ccw_device *cdev = to_ccwdev(dev); - int i, force; - char *tmp; + int force, ret; + unsigned long i; if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) return -EAGAIN; @@ -525,25 +525,30 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, if (!strncmp(buf, "force\n", count)) { force = 1; i = 1; + ret = 0; } else { force = 0; - i = simple_strtoul(buf, &tmp, 16); + ret = strict_strtoul(buf, 16, &i); } - + if (ret) + goto out; switch (i) { case 0: online_store_handle_offline(cdev); + ret = count; break; case 1: online_store_handle_online(cdev, force); + ret = count; break; default: - count = -EINVAL; + ret = -EINVAL; } +out: if (cdev->drv) module_put(cdev->drv->owner); atomic_set(&cdev->private->onoff, 0); - return count; + return ret; } static ssize_t diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 4b92c84fb43..99403b0a97a 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -555,8 +555,7 @@ ccw_device_recognition(struct ccw_device *cdev) (cdev->private->state != DEV_STATE_BOXED)) return -EINVAL; sch = to_subchannel(cdev->dev.parent); - ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc, - (u32)(addr_t)sch); + ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); if (ret != 0) /* Couldn't enable the subchannel for i/o. Sick device. */ return ret; @@ -667,8 +666,7 @@ ccw_device_online(struct ccw_device *cdev) sch = to_subchannel(cdev->dev.parent); if (css_init_done && !get_device(&cdev->dev)) return -ENODEV; - ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc, - (u32)(addr_t)sch); + ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); if (ret != 0) { /* Couldn't enable the subchannel for i/o. Sick device. */ if (ret == -ENODEV) @@ -1048,8 +1046,7 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event) struct subchannel *sch; sch = to_subchannel(cdev->dev.parent); - if (cio_enable_subchannel(sch, sch->schib.pmcw.isc, - (u32)(addr_t)sch) != 0) + if (cio_enable_subchannel(sch, (u32)(addr_t)sch) != 0) /* Couldn't enable the subchannel for i/o. Sick device. */ return; @@ -1082,7 +1079,6 @@ device_trigger_reprobe(struct subchannel *sch) */ sch->lpm = sch->schib.pmcw.pam & sch->opm; /* Re-set some bits in the pmcw that were lost. */ - sch->schib.pmcw.isc = 3; sch->schib.pmcw.csense = 1; sch->schib.pmcw.ena = 0; if ((sch->lpm & (sch->lpm - 1)) != 0) diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index a1718a0aa53..f308ad55a6d 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -508,7 +508,7 @@ ccw_device_stlck(struct ccw_device *cdev) return -ENOMEM; } spin_lock_irqsave(sch->lock, flags); - ret = cio_enable_subchannel(sch, 3, (u32)(addr_t)sch); + ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); if (ret) goto out_unlock; /* diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index 43876e28737..445cf364e46 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -3663,11 +3663,11 @@ qdio_performance_stats_show(struct bus_type *bus, char *buf) static ssize_t qdio_performance_stats_store(struct bus_type *bus, const char *buf, size_t count) { - char *tmp; - int i; + unsigned long i; + int ret; - i = simple_strtoul(buf, &tmp, 16); - if ((i == 0) || (i == 1)) { + ret = strict_strtoul(buf, 16, &i); + if (!ret && ((i == 0) || (i == 1))) { if (i == qdio_performance_stats) return count; qdio_performance_stats = i; diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index bbef3764fbf..47a7e6200b2 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -17,6 +17,7 @@ #include <linux/virtio_config.h> #include <linux/interrupt.h> #include <linux/virtio_ring.h> +#include <linux/pfn.h> #include <asm/io.h> #include <asm/kvm_para.h> #include <asm/kvm_virtio.h> @@ -180,11 +181,10 @@ static struct virtqueue *kvm_find_vq(struct virtio_device *vdev, config = kvm_vq_config(kdev->desc)+index; - if (add_shared_memory(config->address, - vring_size(config->num, PAGE_SIZE))) { - err = -ENOMEM; + err = vmem_add_mapping(config->address, + vring_size(config->num, PAGE_SIZE)); + if (err) goto out; - } vq = vring_new_virtqueue(config->num, vdev, (void *) config->address, kvm_notify, callback); @@ -202,8 +202,8 @@ static struct virtqueue *kvm_find_vq(struct virtio_device *vdev, vq->priv = config; return vq; unmap: - remove_shared_memory(config->address, vring_size(config->num, - PAGE_SIZE)); + vmem_remove_mapping(config->address, + vring_size(config->num, PAGE_SIZE)); out: return ERR_PTR(err); } @@ -213,8 +213,8 @@ static void kvm_del_vq(struct virtqueue *vq) struct kvm_vqconfig *config = vq->priv; vring_del_virtqueue(vq); - remove_shared_memory(config->address, - vring_size(config->num, PAGE_SIZE)); + vmem_remove_mapping(config->address, + vring_size(config->num, PAGE_SIZE)); } /* @@ -318,12 +318,13 @@ static int __init kvm_devices_init(void) return rc; } - if (add_shared_memory((max_pfn) << PAGE_SHIFT, PAGE_SIZE)) { + rc = vmem_add_mapping(PFN_PHYS(max_pfn), PAGE_SIZE); + if (rc) { device_unregister(&kvm_root); - return -ENOMEM; + return rc; } - kvm_devices = (void *) (max_pfn << PAGE_SHIFT); + kvm_devices = (void *) PFN_PHYS(max_pfn); ctl_set_bit(0, 9); register_external_interrupt(0x2603, kvm_extint_handler); diff --git a/drivers/s390/net/cu3088.c b/drivers/s390/net/cu3088.c index 76728ae4b84..8e7697305a4 100644 --- a/drivers/s390/net/cu3088.c +++ b/drivers/s390/net/cu3088.c @@ -62,30 +62,14 @@ static struct device *cu3088_root_dev; static ssize_t group_write(struct device_driver *drv, const char *buf, size_t count) { - const char *start, *end; - char bus_ids[2][BUS_ID_SIZE], *argv[2]; - int i; int ret; struct ccwgroup_driver *cdrv; cdrv = to_ccwgroupdrv(drv); if (!cdrv) return -EINVAL; - start = buf; - for (i=0; i<2; i++) { - static const char delim[] = {',', '\n'}; - int len; - - if (!(end = strchr(start, delim[i]))) - return -EINVAL; - len = min_t(ptrdiff_t, BUS_ID_SIZE, end - start + 1); - strlcpy (bus_ids[i], start, len); - argv[i] = bus_ids[i]; - start = end + 1; - } - - ret = ccwgroup_create(cu3088_root_dev, cdrv->driver_id, - &cu3088_driver, 2, argv); + ret = ccwgroup_create_from_string(cu3088_root_dev, cdrv->driver_id, + &cu3088_driver, 2, buf); return (ret == 0) ? count : ret; } diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index f51ed997258..dd22f4b3703 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -1793,7 +1793,8 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len) skb->protocol = card->lan_type_trans(skb, card->dev); card->stats.rx_bytes += skb_len; card->stats.rx_packets++; - *((__u32 *)skb->cb) = ++card->pkt_seq; + if (skb->protocol == htons(ETH_P_802_2)) + *((__u32 *)skb->cb) = ++card->pkt_seq; netif_rx(skb); } diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 8f876f6ab36..e4ba6a0372a 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -1313,8 +1313,6 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev) * and throw away packet. */ if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) { - if (!in_atomic()) - fsm_event(privptr->fsm, DEV_EVENT_START, dev); dev_kfree_skb(skb); privptr->stats.tx_dropped++; privptr->stats.tx_errors++; @@ -2147,6 +2145,7 @@ static int __init netiucv_init(void) if (rc) goto out_dbf; IUCV_DBF_TEXT(trace, 3, __func__); + netiucv_driver.groups = netiucv_drv_attr_groups; rc = driver_register(&netiucv_driver); if (rc) { PRINT_ERR("NETIUCV: failed to register driver.\n"); diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 66f4f12503c..699ac11debd 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -72,22 +72,7 @@ struct qeth_dbf_info { debug_sprintf_event(qeth_dbf[QETH_DBF_MSG].id, level, text) #define QETH_DBF_TEXT_(name, level, text...) \ - do { \ - if (qeth_dbf_passes(qeth_dbf[QETH_DBF_##name].id, level)) { \ - char *dbf_txt_buf = \ - get_cpu_var(QETH_DBF_TXT_BUF); \ - sprintf(dbf_txt_buf, text); \ - debug_text_event(qeth_dbf[QETH_DBF_##name].id, \ - level, dbf_txt_buf); \ - put_cpu_var(QETH_DBF_TXT_BUF); \ - } \ - } while (0) - -/* Allow to sort out low debug levels early to avoid wasted sprints */ -static inline int qeth_dbf_passes(debug_info_t *dbf_grp, int level) -{ - return (level <= dbf_grp->level); -} + qeth_dbf_longtext(QETH_DBF_##name, level, text) /** * some more debug stuff @@ -773,27 +758,6 @@ static inline int qeth_get_micros(void) return (int) (get_clock() >> 12); } -static inline void *qeth_push_skb(struct qeth_card *card, struct sk_buff *skb, - int size) -{ - void *hdr; - - hdr = (void *) skb_push(skb, size); - /* - * sanity check, the Linux memory allocation scheme should - * never present us cases like this one (the qdio header size plus - * the first 40 bytes of the paket cross a 4k boundary) - */ - if ((((unsigned long) hdr) & (~(PAGE_SIZE - 1))) != - (((unsigned long) hdr + size + - QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) { - PRINT_ERR("Misaligned packet on interface %s. Discarded.", - QETH_CARD_IFNAME(card)); - return NULL; - } - return hdr; -} - static inline int qeth_get_ip_version(struct sk_buff *skb) { switch (skb->protocol) { @@ -806,6 +770,12 @@ static inline int qeth_get_ip_version(struct sk_buff *skb) } } +static inline void qeth_put_buffer_pool_entry(struct qeth_card *card, + struct qeth_buffer_pool_entry *entry) +{ + list_add_tail(&entry->list, &card->qdio.in_buf_pool.entry_list); +} + struct qeth_eddp_context; extern struct ccwgroup_driver qeth_l2_ccwgroup_driver; extern struct ccwgroup_driver qeth_l3_ccwgroup_driver; @@ -843,8 +813,6 @@ struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *, int qeth_query_setadapterparms(struct qeth_card *); int qeth_check_qdio_errors(struct qdio_buffer *, unsigned int, unsigned int, const char *); -void qeth_put_buffer_pool_entry(struct qeth_card *, - struct qeth_buffer_pool_entry *); void qeth_queue_input_buffer(struct qeth_card *, int); struct sk_buff *qeth_core_get_next_skb(struct qeth_card *, struct qdio_buffer *, struct qdio_buffer_element **, int *, @@ -880,8 +848,6 @@ int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *, void *reply_param); int qeth_get_cast_type(struct qeth_card *, struct sk_buff *); int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); -struct sk_buff *qeth_prepare_skb(struct qeth_card *, struct sk_buff *, - struct qeth_hdr **); int qeth_get_elements_no(struct qeth_card *, void *, struct sk_buff *, int); int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *, struct sk_buff *, struct qeth_hdr *, int, @@ -894,6 +860,8 @@ void qeth_core_get_ethtool_stats(struct net_device *, struct ethtool_stats *, u64 *); void qeth_core_get_strings(struct net_device *, u32, u8 *); void qeth_core_get_drvinfo(struct net_device *, struct ethtool_drvinfo *); +void qeth_dbf_longtext(enum qeth_dbf_names dbf_nix, int level, char *text, ...); +int qeth_core_ethtool_get_settings(struct net_device *, struct ethtool_cmd *); /* exports for OSN */ int qeth_osn_assist(struct net_device *, void *, int); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 055f5c3e7b5..436bf1f6d4a 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -26,9 +26,6 @@ #include "qeth_core.h" #include "qeth_core_offl.h" -static DEFINE_PER_CPU(char[256], qeth_core_dbf_txt_buf); -#define QETH_DBF_TXT_BUF qeth_core_dbf_txt_buf - struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = { /* define dbf - Name, Pages, Areas, Maxlen, Level, View, Handle */ /* N P A M L V H */ @@ -2255,14 +2252,6 @@ void qeth_print_status_message(struct qeth_card *card) } EXPORT_SYMBOL_GPL(qeth_print_status_message); -void qeth_put_buffer_pool_entry(struct qeth_card *card, - struct qeth_buffer_pool_entry *entry) -{ - QETH_DBF_TEXT(TRACE, 6, "ptbfplen"); - list_add_tail(&entry->list, &card->qdio.in_buf_pool.entry_list); -} -EXPORT_SYMBOL_GPL(qeth_put_buffer_pool_entry); - static void qeth_initialize_working_pool_list(struct qeth_card *card) { struct qeth_buffer_pool_entry *entry; @@ -2603,7 +2592,6 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index) int rc; int newcount = 0; - QETH_DBF_TEXT(TRACE, 6, "queinbuf"); count = (index < queue->next_buf_to_init)? card->qdio.in_buf_pool.buf_count - (queue->next_buf_to_init - index) : @@ -2792,8 +2780,6 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int, int i; unsigned int qdio_flags; - QETH_DBF_TEXT(TRACE, 6, "flushbuf"); - for (i = index; i < index + count; ++i) { buf = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]; buf->buffer->element[buf->next_element_to_fill - 1].flags |= @@ -3037,49 +3023,6 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, } EXPORT_SYMBOL_GPL(qeth_get_priority_queue); -static void __qeth_free_new_skb(struct sk_buff *orig_skb, - struct sk_buff *new_skb) -{ - if (orig_skb != new_skb) - dev_kfree_skb_any(new_skb); -} - -static inline struct sk_buff *qeth_realloc_headroom(struct qeth_card *card, - struct sk_buff *skb, int size) -{ - struct sk_buff *new_skb = skb; - - if (skb_headroom(skb) >= size) - return skb; - new_skb = skb_realloc_headroom(skb, size); - if (!new_skb) - PRINT_ERR("Could not realloc headroom for qeth_hdr " - "on interface %s", QETH_CARD_IFNAME(card)); - return new_skb; -} - -struct sk_buff *qeth_prepare_skb(struct qeth_card *card, struct sk_buff *skb, - struct qeth_hdr **hdr) -{ - struct sk_buff *new_skb; - - QETH_DBF_TEXT(TRACE, 6, "prepskb"); - - new_skb = qeth_realloc_headroom(card, skb, - sizeof(struct qeth_hdr)); - if (!new_skb) - return NULL; - - *hdr = ((struct qeth_hdr *)qeth_push_skb(card, new_skb, - sizeof(struct qeth_hdr))); - if (*hdr == NULL) { - __qeth_free_new_skb(skb, new_skb); - return NULL; - } - return new_skb; -} -EXPORT_SYMBOL_GPL(qeth_prepare_skb); - int qeth_get_elements_no(struct qeth_card *card, void *hdr, struct sk_buff *skb, int elems) { @@ -3100,8 +3043,8 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr, } EXPORT_SYMBOL_GPL(qeth_get_elements_no); -static void __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer, - int is_tso, int *next_element_to_fill) +static inline void __qeth_fill_buffer(struct sk_buff *skb, + struct qdio_buffer *buffer, int is_tso, int *next_element_to_fill) { int length = skb->len; int length_here; @@ -3143,15 +3086,13 @@ static void __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer, *next_element_to_fill = element; } -static int qeth_fill_buffer(struct qeth_qdio_out_q *queue, +static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf, struct sk_buff *skb) { struct qdio_buffer *buffer; struct qeth_hdr_tso *hdr; int flush_cnt = 0, hdr_len, large_send = 0; - QETH_DBF_TEXT(TRACE, 6, "qdfillbf"); - buffer = buf->buffer; atomic_inc(&skb->users); skb_queue_tail(&buf->skb_list, skb); @@ -3210,8 +3151,6 @@ int qeth_do_send_packet_fast(struct qeth_card *card, int flush_cnt = 0; int index; - QETH_DBF_TEXT(TRACE, 6, "dosndpfa"); - /* spin until we get the queue ... */ while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED, QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED); @@ -3263,8 +3202,6 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, int tmp; int rc = 0; - QETH_DBF_TEXT(TRACE, 6, "dosndpkt"); - /* spin until we get the queue ... */ while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED, QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED); @@ -3827,27 +3764,8 @@ static struct ccw_driver qeth_ccw_driver = { static int qeth_core_driver_group(const char *buf, struct device *root_dev, unsigned long driver_id) { - const char *start, *end; - char bus_ids[3][BUS_ID_SIZE], *argv[3]; - int i; - - start = buf; - for (i = 0; i < 3; i++) { - static const char delim[] = { ',', ',', '\n' }; - int len; - - end = strchr(start, delim[i]); - if (!end) - return -EINVAL; - len = min_t(ptrdiff_t, BUS_ID_SIZE, end - start); - strncpy(bus_ids[i], start, len); - bus_ids[i][len] = '\0'; - start = end + 1; - argv[i] = bus_ids[i]; - } - - return (ccwgroup_create(root_dev, driver_id, - &qeth_ccw_driver, 3, argv)); + return ccwgroup_create_from_string(root_dev, driver_id, + &qeth_ccw_driver, 3, buf); } int qeth_core_hardsetup_card(struct qeth_card *card) @@ -3885,8 +3803,9 @@ retry: QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); return rc; } - - mpno = QETH_MAX_PORTNO; + mpno = qdio_get_ssqd_pct(CARD_DDEV(card)); + if (mpno) + mpno = min(mpno - 1, QETH_MAX_PORTNO); if (card->info.portno > mpno) { PRINT_ERR("Device %s does not offer port number %d \n.", CARD_BUS_ID(card), card->info.portno); @@ -3980,7 +3899,6 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, int use_rx_sg = 0; int frag = 0; - QETH_DBF_TEXT(TRACE, 6, "nextskb"); /* qeth_hdr must not cross element boundaries */ if (element->length < offset + sizeof(struct qeth_hdr)) { if (qeth_is_last_sbale(element)) @@ -4086,6 +4004,18 @@ static void qeth_unregister_dbf_views(void) } } +void qeth_dbf_longtext(enum qeth_dbf_names dbf_nix, int level, char *text, ...) +{ + char dbf_txt_buf[32]; + + if (level > (qeth_dbf[dbf_nix].id)->level) + return; + snprintf(dbf_txt_buf, sizeof(dbf_txt_buf), text); + debug_text_event(qeth_dbf[dbf_nix].id, level, dbf_txt_buf); + +} +EXPORT_SYMBOL_GPL(qeth_dbf_longtext); + static int qeth_register_dbf_views(void) { int ret; @@ -4433,6 +4363,96 @@ void qeth_core_get_drvinfo(struct net_device *dev, } EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo); +int qeth_core_ethtool_get_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct qeth_card *card = netdev_priv(netdev); + enum qeth_link_types link_type; + + if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan)) + link_type = QETH_LINK_TYPE_10GBIT_ETH; + else + link_type = card->info.link_type; + + ecmd->transceiver = XCVR_INTERNAL; + ecmd->supported = SUPPORTED_Autoneg; + ecmd->advertising = ADVERTISED_Autoneg; + ecmd->duplex = DUPLEX_FULL; + ecmd->autoneg = AUTONEG_ENABLE; + + switch (link_type) { + case QETH_LINK_TYPE_FAST_ETH: + case QETH_LINK_TYPE_LANE_ETH100: + ecmd->supported |= SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_TP; + ecmd->advertising |= ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_TP; + ecmd->speed = SPEED_100; + ecmd->port = PORT_TP; + break; + + case QETH_LINK_TYPE_GBIT_ETH: + case QETH_LINK_TYPE_LANE_ETH1000: + ecmd->supported |= SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full | + SUPPORTED_FIBRE; + ecmd->advertising |= ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full | + ADVERTISED_FIBRE; + ecmd->speed = SPEED_1000; + ecmd->port = PORT_FIBRE; + break; + + case QETH_LINK_TYPE_10GBIT_ETH: + ecmd->supported |= SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full | + SUPPORTED_10000baseT_Full | + SUPPORTED_FIBRE; + ecmd->advertising |= ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full | + ADVERTISED_10000baseT_Full | + ADVERTISED_FIBRE; + ecmd->speed = SPEED_10000; + ecmd->port = PORT_FIBRE; + break; + + default: + ecmd->supported |= SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_TP; + ecmd->advertising |= ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_TP; + ecmd->speed = SPEED_10; + ecmd->port = PORT_TP; + } + + return 0; +} +EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings); + static int __init qeth_core_init(void) { int rc; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 3921d1631a7..86ec50ddae1 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -22,9 +22,6 @@ #include "qeth_core.h" #include "qeth_core_offl.h" -#define QETH_DBF_TXT_BUF qeth_l2_dbf_txt_buf -static DEFINE_PER_CPU(char[256], qeth_l2_dbf_txt_buf); - static int qeth_l2_set_offline(struct ccwgroup_device *); static int qeth_l2_stop(struct net_device *); static int qeth_l2_send_delmac(struct qeth_card *, __u8 *); @@ -635,8 +632,6 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO; struct qeth_eddp_context *ctx = NULL; - QETH_DBF_TEXT(TRACE, 6, "l2xmit"); - if ((card->state != CARD_STATE_UP) || !card->lan_online) { card->stats.tx_carrier_errors++; goto tx_drop; @@ -658,9 +653,12 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (card->info.type == QETH_CARD_TYPE_OSN) hdr = (struct qeth_hdr *)skb->data; else { - new_skb = qeth_prepare_skb(card, skb, &hdr); + /* create a clone with writeable headroom */ + new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr)); if (!new_skb) goto tx_drop; + hdr = (struct qeth_hdr *)skb_push(new_skb, + sizeof(struct qeth_hdr)); qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type); } @@ -747,7 +745,6 @@ static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev, int index; int i; - QETH_DBF_TEXT(TRACE, 6, "qdinput"); card = (struct qeth_card *) card_ptr; net_dev = card->dev; if (card->options.performance_stats) { @@ -852,6 +849,22 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev) return; } +static int qeth_l2_ethtool_set_tso(struct net_device *dev, u32 data) +{ + struct qeth_card *card = netdev_priv(dev); + + if (data) { + if (card->options.large_send == QETH_LARGE_SEND_NO) { + card->options.large_send = QETH_LARGE_SEND_EDDP; + dev->features |= NETIF_F_TSO; + } + } else { + dev->features &= ~NETIF_F_TSO; + card->options.large_send = QETH_LARGE_SEND_NO; + } + return 0; +} + static struct ethtool_ops qeth_l2_ethtool_ops = { .get_link = ethtool_op_get_link, .get_tx_csum = ethtool_op_get_tx_csum, @@ -859,11 +872,12 @@ static struct ethtool_ops qeth_l2_ethtool_ops = { .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, .get_tso = ethtool_op_get_tso, - .set_tso = ethtool_op_set_tso, + .set_tso = qeth_l2_ethtool_set_tso, .get_strings = qeth_core_get_strings, .get_ethtool_stats = qeth_core_get_ethtool_stats, .get_stats_count = qeth_core_get_stats_count, .get_drvinfo = qeth_core_get_drvinfo, + .get_settings = qeth_core_ethtool_get_settings, }; static struct ethtool_ops qeth_l2_osn_ops = { diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index 1be353593a5..9f143c83bba 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -13,9 +13,6 @@ #include "qeth_core.h" -#define QETH_DBF_TXT_BUF qeth_l3_dbf_txt_buf -DECLARE_PER_CPU(char[256], qeth_l3_dbf_txt_buf); - struct qeth_ipaddr { struct list_head entry; enum qeth_ip_types type; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index e1bfe56087d..94a8ead64ed 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -28,8 +28,6 @@ #include "qeth_l3.h" #include "qeth_core_offl.h" -DEFINE_PER_CPU(char[256], qeth_l3_dbf_txt_buf); - static int qeth_l3_set_offline(struct ccwgroup_device *); static int qeth_l3_recover(void *); static int qeth_l3_stop(struct net_device *); @@ -2093,6 +2091,11 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode) (card->state == CARD_STATE_UP)) { if (recovery_mode) qeth_l3_stop(card->dev); + else { + rtnl_lock(); + dev_close(card->dev); + rtnl_unlock(); + } if (!card->use_hard_stop) { rc = qeth_send_stoplan(card); if (rc) @@ -2559,8 +2562,6 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, struct sk_buff *skb, int ipv, int cast_type) { - QETH_DBF_TEXT(TRACE, 6, "fillhdr"); - memset(hdr, 0, sizeof(struct qeth_hdr)); hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3; hdr->hdr.l3.ext_flags = 0; @@ -2570,9 +2571,10 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, * v6 uses passthrough, v4 sets the tag in the QDIO header. */ if (card->vlangrp && vlan_tx_tag_present(skb)) { - hdr->hdr.l3.ext_flags = (ipv == 4) ? - QETH_HDR_EXT_VLAN_FRAME : - QETH_HDR_EXT_INCLUDE_VLAN_TAG; + if ((ipv == 4) || (card->info.type == QETH_CARD_TYPE_IQD)) + hdr->hdr.l3.ext_flags = QETH_HDR_EXT_VLAN_FRAME; + else + hdr->hdr.l3.ext_flags = QETH_HDR_EXT_INCLUDE_VLAN_TAG; hdr->hdr.l3.vlan_id = vlan_tx_tag_get(skb); } @@ -2638,8 +2640,6 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO; struct qeth_eddp_context *ctx = NULL; - QETH_DBF_TEXT(TRACE, 6, "l3xmit"); - if ((card->info.type == QETH_CARD_TYPE_IQD) && (skb->protocol != htons(ETH_P_IPV6)) && (skb->protocol != htons(ETH_P_IP))) @@ -2890,6 +2890,7 @@ static struct ethtool_ops qeth_l3_ethtool_ops = { .get_ethtool_stats = qeth_core_get_ethtool_stats, .get_stats_count = qeth_core_get_stats_count, .get_drvinfo = qeth_core_get_drvinfo, + .get_settings = qeth_core_ethtool_get_settings, }; /* @@ -2982,7 +2983,6 @@ static void qeth_l3_qdio_input_handler(struct ccw_device *ccwdev, int index; int i; - QETH_DBF_TEXT(TRACE, 6, "qdinput"); card = (struct qeth_card *) card_ptr; net_dev = card->dev; if (card->options.performance_stats) { @@ -3140,9 +3140,15 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) netif_carrier_on(card->dev); qeth_set_allowed_threads(card, 0xffffffff, 0); - if ((recover_flag == CARD_STATE_RECOVER) && recovery_mode) { + if (recover_flag == CARD_STATE_RECOVER) { + if (recovery_mode) qeth_l3_open(card->dev); - qeth_l3_set_multicast_list(card->dev); + else { + rtnl_lock(); + dev_open(card->dev); + rtnl_unlock(); + } + qeth_l3_set_multicast_list(card->dev); } /* let user_space know that device is online */ kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE); diff --git a/drivers/sbus/char/cpwatchdog.c b/drivers/sbus/char/cpwatchdog.c index a4e75814366..23570341437 100644 --- a/drivers/sbus/char/cpwatchdog.c +++ b/drivers/sbus/char/cpwatchdog.c @@ -637,7 +637,7 @@ static int wd_inittimer(int whichdog) break; default: printk("%s: %s: invalid watchdog id: %i\n", - WD_OBPNAME, __FUNCTION__, whichdog); + WD_OBPNAME, __func__, whichdog); return(1); } if(0 != misc_register(whichmisc)) diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c index 44d2ef906ac..383f32c1d34 100644 --- a/drivers/sbus/char/uctrl.c +++ b/drivers/sbus/char/uctrl.c @@ -393,13 +393,13 @@ static int __init ts102_uctrl_init(void) err = request_irq(driver->irq, uctrl_interrupt, 0, "uctrl", driver); if (err) { printk("%s: unable to register irq %d\n", - __FUNCTION__, driver->irq); + __func__, driver->irq); return err; } if (misc_register(&uctrl_dev)) { printk("%s: unable to get misc minor %d\n", - __FUNCTION__, uctrl_dev.minor); + __func__, uctrl_dev.minor); free_irq(driver->irq, driver); return -ENODEV; } diff --git a/drivers/serial/68328serial.c b/drivers/serial/68328serial.c index 2b8a410e095..bbf5bc5892c 100644 --- a/drivers/serial/68328serial.c +++ b/drivers/serial/68328serial.c @@ -200,7 +200,7 @@ static void rs_stop(struct tty_struct *tty) local_irq_restore(flags); } -static void rs_put_char(char ch) +static int rs_put_char(char ch) { int flags, loops = 0; @@ -214,6 +214,7 @@ static void rs_put_char(char ch) UTX_TXDATA = ch; udelay(5); local_irq_restore(flags); + return 1; } static void rs_start(struct tty_struct *tty) @@ -1017,18 +1018,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, tty_wait_until_sent(tty, 0); send_break(info, arg ? arg*(100) : 250); return 0; - case TIOCGSOFTCAR: - error = put_user(C_CLOCAL(tty) ? 1 : 0, - (unsigned long *) arg); - if (error) - return error; - return 0; - case TIOCSSOFTCAR: - get_user(arg, (unsigned long *) arg); - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - return 0; case TIOCGSERIAL: if (access_ok(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) @@ -1061,9 +1050,6 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - if (tty->termios->c_cflag == old_termios->c_cflag) - return; - change_speed(info); if ((old_termios->c_cflag & CRTSCTS) && @@ -1140,8 +1126,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) uart->ustcnt &= ~(USTCNT_RXEN | USTCNT_RX_INTR_MASK); shutdown(info); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + rs_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; diff --git a/drivers/serial/68360serial.c b/drivers/serial/68360serial.c index f5946360187..d9d4e9552a4 100644 --- a/drivers/serial/68360serial.c +++ b/drivers/serial/68360serial.c @@ -995,10 +995,10 @@ static void rs_360_put_char(struct tty_struct *tty, unsigned char ch) volatile QUICC_BD *bdp; if (serial_paranoia_check(info, tty->name, "rs_put_char")) - return; + return 0; if (!tty) - return; + return 0; bdp = info->tx_cur; while (bdp->status & BD_SC_READY); @@ -1016,6 +1016,7 @@ static void rs_360_put_char(struct tty_struct *tty, unsigned char ch) bdp++; info->tx_cur = (QUICC_BD *)bdp; + return 1; } @@ -1246,7 +1247,7 @@ static int rs_360_tiocmget(struct tty_struct *tty, struct file *file) #ifdef modem_control unsigned char control, status; - if (serial_paranoia_check(info, tty->name, __FUNCTION__)) + if (serial_paranoia_check(info, tty->name, __func__)) return -ENODEV; if (tty->flags & (1 << TTY_IO_ERROR)) @@ -1277,12 +1278,12 @@ static int rs_360_tiocmset(struct tty_struct *tty, struct file *file, ser_info_t *info = (ser_info_t *)tty->driver_data; unsigned int arg; - if (serial_paranoia_check(info, tty->name, __FUNCTION__)) + if (serial_paranoia_check(info, tty->name, __func__)) return -ENODEV; if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; - + /* FIXME: locking on info->mcr */ if (set & TIOCM_RTS) info->mcr |= UART_MCR_RTS; if (set & TIOCM_DTR) @@ -1436,18 +1437,6 @@ static int rs_360_ioctl(struct tty_struct *tty, struct file * file, return retval; end_break(info); return 0; - case TIOCGSOFTCAR: - /* return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); */ - put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); - return 0; - case TIOCSSOFTCAR: - error = get_user(arg, (unsigned int *) arg); - if (error) - return error; - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - return 0; #ifdef maybe case TIOCSERGETLSR: /* Get line status register */ return get_lsr_info(info, (unsigned int *) arg); @@ -1665,8 +1654,7 @@ static void rs_360_close(struct tty_struct *tty, struct file * filp) rs_360_wait_until_sent(tty, info->timeout); } shutdown(info); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + rs_360_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; info->event = 0; @@ -1717,6 +1705,7 @@ static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout) printk("jiff=%lu...", jiffies); #endif + lock_kernel(); /* We go through the loop at least once because we can't tell * exactly when the last character exits the shifter. There can * be at least two characters waiting to be sent after the buffers @@ -1745,6 +1734,7 @@ static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout) bdp--; } while (bdp->status & BD_SC_READY); current->state = TASK_RUNNING; + unlock_kernel(); #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); #endif diff --git a/drivers/serial/8250_early.c b/drivers/serial/8250_early.c index 38776e8b064..cd898704ba4 100644 --- a/drivers/serial/8250_early.c +++ b/drivers/serial/8250_early.c @@ -156,7 +156,7 @@ static int __init parse_options(struct early_serial8250_device *device, port->membase = ioremap(port->mapbase, 64); if (!port->membase) { printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n", - __FUNCTION__, + __func__, (unsigned long long)port->mapbase); return -ENOMEM; } diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 34b809e3b59..36acbcca2d4 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1355,4 +1355,47 @@ config SERIAL_SC26XX_CONSOLE help Support for Console on SC2681/SC2692 serial ports. +config SERIAL_BFIN_SPORT + tristate "Blackfin SPORT emulate UART (EXPERIMENTAL)" + depends on BFIN && EXPERIMENTAL + select SERIAL_CORE + help + Enble support SPORT emulate UART on Blackfin series. + + To compile this driver as a module, choose M here: the + module will be called bfin_sport_uart. + +choice + prompt "Baud rate for Blackfin SPORT UART" + depends on SERIAL_BFIN_SPORT + default SERIAL_SPORT_BAUD_RATE_57600 + help + Choose a baud rate for the SPORT UART, other uart settings are + 8 bit, 1 stop bit, no parity, no flow control. + +config SERIAL_SPORT_BAUD_RATE_115200 + bool "115200" + +config SERIAL_SPORT_BAUD_RATE_57600 + bool "57600" + +config SERIAL_SPORT_BAUD_RATE_38400 + bool "38400" + +config SERIAL_SPORT_BAUD_RATE_19200 + bool "19200" + +config SERIAL_SPORT_BAUD_RATE_9600 + bool "9600" +endchoice + +config SPORT_BAUD_RATE + int + depends on SERIAL_BFIN_SPORT + default 115200 if (SERIAL_SPORT_BAUD_RATE_115200) + default 57600 if (SERIAL_SPORT_BAUD_RATE_57600) + default 38400 if (SERIAL_SPORT_BAUD_RATE_38400) + default 19200 if (SERIAL_SPORT_BAUD_RATE_19200) + default 9600 if (SERIAL_SPORT_BAUD_RATE_9600) + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index f02ff9fad01..0d9c09b1e83 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SERIAL_PXA) += pxa.o obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o obj-$(CONFIG_SERIAL_SA1100) += sa1100.o obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o +obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c index 5f55534a290..8a2f6a1baa7 100644 --- a/drivers/serial/bfin_5xx.c +++ b/drivers/serial/bfin_5xx.c @@ -762,7 +762,7 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, break; default: printk(KERN_ERR "%s: word lengh not supported\n", - __FUNCTION__); + __func__); } if (termios->c_cflag & CSTOPB) @@ -1029,7 +1029,7 @@ bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud, *baud = get_sclk() / (16*(dll | dlh << 8)); } - pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __FUNCTION__, *baud, *parity, *bits); + pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __func__, *baud, *parity, *bits); } #endif diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c new file mode 100644 index 00000000000..aca1240ad80 --- /dev/null +++ b/drivers/serial/bfin_sport_uart.c @@ -0,0 +1,614 @@ +/* + * File: linux/drivers/serial/bfin_sport_uart.c + * + * Based on: drivers/serial/bfin_5xx.c by Aubrey Li. + * Author: Roy Huang <roy.huang@analog.com> + * + * Created: Nov 22, 2006 + * Copyright: (c) 2006-2007 Analog Devices Inc. + * Description: this driver enable SPORTs on Blackfin emulate UART. + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * This driver and the hardware supported are in term of EE-191 of ADI. + * http://www.analog.com/UploadedFiles/Application_Notes/399447663EE191.pdf + * This application note describe how to implement a UART on a Sharc DSP, + * but this driver is implemented on Blackfin Processor. + */ + +/* After reset, there is a prelude of low level pulse when transmit data first + * time. No addtional pulse in following transmit. + * According to document: + * The SPORTs are ready to start transmitting or receiving data no later than + * three serial clock cycles after they are enabled in the SPORTx_TCR1 or + * SPORTx_RCR1 register. No serial clock cycles are lost from this point on. + * The first internal frame sync will occur one frame sync delay after the + * SPORTs are ready. External frame syncs can occur as soon as the SPORT is + * ready. + */ + +/* Thanks to Axel Alatalo <axel@rubico.se> for fixing sport rx bug. Sometimes + * sport receives data incorrectly. The following is Axel's words. + * As EE-191, sport rx samples 3 times of the UART baudrate and takes the + * middle smaple of every 3 samples as the data bit. For a 8-N-1 UART setting, + * 30 samples will be required for a byte. If transmitter sends a 1/3 bit short + * byte due to buadrate drift, then the 30th sample of a byte, this sample is + * also the third sample of the stop bit, will happens on the immediately + * following start bit which will be thrown away and missed. Thus since parts + * of the startbit will be missed and the receiver will begin to drift, the + * effect accumulates over time until synchronization is lost. + * If only require 2 samples of the stopbit (by sampling in total 29 samples), + * then a to short byte as in the case above will be tolerated. Then the 1/3 + * early startbit will trigger a framesync since the last read is complete + * after only 2/3 stopbit and framesync is active during the last 1/3 looking + * for a possible early startbit. */ + +//#define DEBUG + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/platform_device.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> + +#include <asm/delay.h> +#include <asm/portmux.h> + +#include "bfin_sport_uart.h" + +unsigned short bfin_uart_pin_req_sport0[] = + {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \ + P_SPORT0_DRPRI, P_SPORT0_RSCLK, P_SPORT0_DRSEC, P_SPORT0_DTSEC, 0}; + +unsigned short bfin_uart_pin_req_sport1[] = + {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \ + P_SPORT1_DRPRI, P_SPORT1_RSCLK, P_SPORT1_DRSEC, P_SPORT1_DTSEC, 0}; + +#define DRV_NAME "bfin-sport-uart" + +struct sport_uart_port { + struct uart_port port; + char *name; + + int tx_irq; + int rx_irq; + int err_irq; +}; + +static void sport_uart_tx_chars(struct sport_uart_port *up); +static void sport_stop_tx(struct uart_port *port); + +static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value) +{ + pr_debug("%s value:%x\n", __FUNCTION__, value); + /* Place a Start and Stop bit */ + __asm__ volatile ( + "R2 = b#01111111100;\n\t" + "R3 = b#10000000001;\n\t" + "%0 <<= 2;\n\t" + "%0 = %0 & R2;\n\t" + "%0 = %0 | R3;\n\t" + :"=r"(value) + :"0"(value) + :"R2", "R3"); + pr_debug("%s value:%x\n", __FUNCTION__, value); + + SPORT_PUT_TX(up, value); +} + +static inline unsigned int rx_one_byte(struct sport_uart_port *up) +{ + unsigned int value, extract; + + value = SPORT_GET_RX32(up); + pr_debug("%s value:%x\n", __FUNCTION__, value); + + /* Extract 8 bits data */ + __asm__ volatile ( + "R5 = 0;\n\t" + "P0 = 8;\n\t" + "R1 = 0x1801(Z);\n\t" + "R3 = 0x0300(Z);\n\t" + "R4 = 0;\n\t" + "LSETUP(loop_s, loop_e) LC0 = P0;\nloop_s:\t" + "R2 = extract(%1, R1.L)(Z);\n\t" + "R2 <<= R4;\n\t" + "R5 = R5 | R2;\n\t" + "R1 = R1 - R3;\nloop_e:\t" + "R4 += 1;\n\t" + "%0 = R5;\n\t" + :"=r"(extract) + :"r"(value) + :"P0", "R1", "R2","R3","R4", "R5"); + + pr_debug(" extract:%x\n", extract); + return extract; +} + +static int sport_uart_setup(struct sport_uart_port *up, int sclk, int baud_rate) +{ + int tclkdiv, tfsdiv, rclkdiv; + + /* Set TCR1 and TCR2 */ + SPORT_PUT_TCR1(up, (LTFS | ITFS | TFSR | TLSBIT | ITCLK)); + SPORT_PUT_TCR2(up, 10); + pr_debug("%s TCR1:%x, TCR2:%x\n", __FUNCTION__, SPORT_GET_TCR1(up), SPORT_GET_TCR2(up)); + + /* Set RCR1 and RCR2 */ + SPORT_PUT_RCR1(up, (RCKFE | LARFS | LRFS | RFSR | IRCLK)); + SPORT_PUT_RCR2(up, 28); + pr_debug("%s RCR1:%x, RCR2:%x\n", __FUNCTION__, SPORT_GET_RCR1(up), SPORT_GET_RCR2(up)); + + tclkdiv = sclk/(2 * baud_rate) - 1; + tfsdiv = 12; + rclkdiv = sclk/(2 * baud_rate * 3) - 1; + SPORT_PUT_TCLKDIV(up, tclkdiv); + SPORT_PUT_TFSDIV(up, tfsdiv); + SPORT_PUT_RCLKDIV(up, rclkdiv); + SSYNC(); + pr_debug("%s sclk:%d, baud_rate:%d, tclkdiv:%d, tfsdiv:%d, rclkdiv:%d\n", + __FUNCTION__, sclk, baud_rate, tclkdiv, tfsdiv, rclkdiv); + + return 0; +} + +static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id) +{ + struct sport_uart_port *up = dev_id; + struct tty_struct *tty = up->port.info->tty; + unsigned int ch; + + do { + ch = rx_one_byte(up); + up->port.icount.rx++; + + if (uart_handle_sysrq_char(&up->port, ch)) + ; + else + tty_insert_flip_char(tty, ch, TTY_NORMAL); + } while (SPORT_GET_STAT(up) & RXNE); + tty_flip_buffer_push(tty); + + return IRQ_HANDLED; +} + +static irqreturn_t sport_uart_tx_irq(int irq, void *dev_id) +{ + sport_uart_tx_chars(dev_id); + + return IRQ_HANDLED; +} + +static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) +{ + struct sport_uart_port *up = dev_id; + struct tty_struct *tty = up->port.info->tty; + unsigned int stat = SPORT_GET_STAT(up); + + /* Overflow in RX FIFO */ + if (stat & ROVF) { + up->port.icount.overrun++; + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + SPORT_PUT_STAT(up, ROVF); /* Clear ROVF bit */ + } + /* These should not happen */ + if (stat & (TOVF | TUVF | RUVF)) { + printk(KERN_ERR "SPORT Error:%s %s %s\n", + (stat & TOVF)?"TX overflow":"", + (stat & TUVF)?"TX underflow":"", + (stat & RUVF)?"RX underflow":""); + SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); + SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); + } + SSYNC(); + + return IRQ_HANDLED; +} + +/* Reqeust IRQ, Setup clock */ +static int sport_startup(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + char buffer[20]; + int retval; + + pr_debug("%s enter\n", __FUNCTION__); + memset(buffer, 20, '\0'); + snprintf(buffer, 20, "%s rx", up->name); + retval = request_irq(up->rx_irq, sport_uart_rx_irq, IRQF_SAMPLE_RANDOM, buffer, up); + if (retval) { + printk(KERN_ERR "Unable to request interrupt %s\n", buffer); + return retval; + } + + snprintf(buffer, 20, "%s tx", up->name); + retval = request_irq(up->tx_irq, sport_uart_tx_irq, IRQF_SAMPLE_RANDOM, buffer, up); + if (retval) { + printk(KERN_ERR "Unable to request interrupt %s\n", buffer); + goto fail1; + } + + snprintf(buffer, 20, "%s err", up->name); + retval = request_irq(up->err_irq, sport_uart_err_irq, IRQF_SAMPLE_RANDOM, buffer, up); + if (retval) { + printk(KERN_ERR "Unable to request interrupt %s\n", buffer); + goto fail2; + } + + if (port->line) { + if (peripheral_request_list(bfin_uart_pin_req_sport1, DRV_NAME)) + goto fail3; + } else { + if (peripheral_request_list(bfin_uart_pin_req_sport0, DRV_NAME)) + goto fail3; + } + + sport_uart_setup(up, get_sclk(), port->uartclk); + + /* Enable receive interrupt */ + SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) | RSPEN)); + SSYNC(); + + return 0; + + +fail3: + printk(KERN_ERR DRV_NAME + ": Requesting Peripherals failed\n"); + + free_irq(up->err_irq, up); +fail2: + free_irq(up->tx_irq, up); +fail1: + free_irq(up->rx_irq, up); + + return retval; + +} + +static void sport_uart_tx_chars(struct sport_uart_port *up) +{ + struct circ_buf *xmit = &up->port.info->xmit; + + if (SPORT_GET_STAT(up) & TXF) + return; + + if (up->port.x_char) { + tx_one_byte(up, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + sport_stop_tx(&up->port); + return; + } + + while(!(SPORT_GET_STAT(up) & TXF) && !uart_circ_empty(xmit)) { + tx_one_byte(up, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE -1); + up->port.icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); +} + +static unsigned int sport_tx_empty(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + unsigned int stat; + + stat = SPORT_GET_STAT(up); + pr_debug("%s stat:%04x\n", __FUNCTION__, stat); + if (stat & TXHRE) { + return TIOCSER_TEMT; + } else + return 0; +} + +static unsigned int sport_get_mctrl(struct uart_port *port) +{ + pr_debug("%s enter\n", __FUNCTION__); + return (TIOCM_CTS | TIOCM_CD | TIOCM_DSR); +} + +static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + pr_debug("%s enter\n", __FUNCTION__); +} + +static void sport_stop_tx(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + unsigned int stat; + + pr_debug("%s enter\n", __FUNCTION__); + + stat = SPORT_GET_STAT(up); + while(!(stat & TXHRE)) { + udelay(1); + stat = SPORT_GET_STAT(up); + } + /* Although the hold register is empty, last byte is still in shift + * register and not sent out yet. If baud rate is lower than default, + * delay should be longer. For example, if the baud rate is 9600, + * the delay must be at least 2ms by experience */ + udelay(500); + + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); + SSYNC(); + + return; +} + +static void sport_start_tx(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + pr_debug("%s enter\n", __FUNCTION__); + /* Write data into SPORT FIFO before enable SPROT to transmit */ + sport_uart_tx_chars(up); + + /* Enable transmit, then an interrupt will generated */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); + SSYNC(); + pr_debug("%s exit\n", __FUNCTION__); +} + +static void sport_stop_rx(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + pr_debug("%s enter\n", __FUNCTION__); + /* Disable sport to stop rx */ + SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN)); + SSYNC(); +} + +static void sport_enable_ms(struct uart_port *port) +{ + pr_debug("%s enter\n", __FUNCTION__); +} + +static void sport_break_ctl(struct uart_port *port, int break_state) +{ + pr_debug("%s enter\n", __FUNCTION__); +} + +static void sport_shutdown(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + pr_debug("%s enter\n", __FUNCTION__); + + /* Disable sport */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); + SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN)); + SSYNC(); + + if (port->line) { + peripheral_free_list(bfin_uart_pin_req_sport1); + } else { + peripheral_free_list(bfin_uart_pin_req_sport0); + } + + free_irq(up->rx_irq, up); + free_irq(up->tx_irq, up); + free_irq(up->err_irq, up); +} + +static void sport_set_termios(struct uart_port *port, + struct termios *termios, struct termios *old) +{ + pr_debug("%s enter, c_cflag:%08x\n", __FUNCTION__, termios->c_cflag); + uart_update_timeout(port, CS8 ,port->uartclk); +} + +static const char *sport_type(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + pr_debug("%s enter\n", __FUNCTION__); + return up->name; +} + +static void sport_release_port(struct uart_port *port) +{ + pr_debug("%s enter\n", __FUNCTION__); +} + +static int sport_request_port(struct uart_port *port) +{ + pr_debug("%s enter\n", __FUNCTION__); + return 0; +} + +static void sport_config_port(struct uart_port *port, int flags) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + pr_debug("%s enter\n", __FUNCTION__); + up->port.type = PORT_BFIN_SPORT; +} + +static int sport_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + pr_debug("%s enter\n", __FUNCTION__); + return 0; +} + +struct uart_ops sport_uart_ops = { + .tx_empty = sport_tx_empty, + .set_mctrl = sport_set_mctrl, + .get_mctrl = sport_get_mctrl, + .stop_tx = sport_stop_tx, + .start_tx = sport_start_tx, + .stop_rx = sport_stop_rx, + .enable_ms = sport_enable_ms, + .break_ctl = sport_break_ctl, + .startup = sport_startup, + .shutdown = sport_shutdown, + .set_termios = sport_set_termios, + .type = sport_type, + .release_port = sport_release_port, + .request_port = sport_request_port, + .config_port = sport_config_port, + .verify_port = sport_verify_port, +}; + +static struct sport_uart_port sport_uart_ports[] = { + { /* SPORT 0 */ + .name = "SPORT0", + .tx_irq = IRQ_SPORT0_TX, + .rx_irq = IRQ_SPORT0_RX, + .err_irq= IRQ_SPORT0_ERROR, + .port = { + .type = PORT_BFIN_SPORT, + .iotype = UPIO_MEM, + .membase = (void __iomem *)SPORT0_TCR1, + .mapbase = SPORT0_TCR1, + .irq = IRQ_SPORT0_RX, + .uartclk = CONFIG_SPORT_BAUD_RATE, + .fifosize = 8, + .ops = &sport_uart_ops, + .line = 0, + }, + }, { /* SPORT 1 */ + .name = "SPORT1", + .tx_irq = IRQ_SPORT1_TX, + .rx_irq = IRQ_SPORT1_RX, + .err_irq= IRQ_SPORT1_ERROR, + .port = { + .type = PORT_BFIN_SPORT, + .iotype = UPIO_MEM, + .membase = (void __iomem *)SPORT1_TCR1, + .mapbase = SPORT1_TCR1, + .irq = IRQ_SPORT1_RX, + .uartclk = CONFIG_SPORT_BAUD_RATE, + .fifosize = 8, + .ops = &sport_uart_ops, + .line = 1, + }, + } +}; + +static struct uart_driver sport_uart_reg = { + .owner = THIS_MODULE, + .driver_name = "SPORT-UART", + .dev_name = "ttySS", + .major = 204, + .minor = 84, + .nr = ARRAY_SIZE(sport_uart_ports), + .cons = NULL, +}; + +static int sport_uart_suspend(struct platform_device *dev, pm_message_t state) +{ + struct sport_uart_port *sport = platform_get_drvdata(dev); + + pr_debug("%s enter\n", __FUNCTION__); + if (sport) + uart_suspend_port(&sport_uart_reg, &sport->port); + + return 0; +} + +static int sport_uart_resume(struct platform_device *dev) +{ + struct sport_uart_port *sport = platform_get_drvdata(dev); + + pr_debug("%s enter\n", __FUNCTION__); + if (sport) + uart_resume_port(&sport_uart_reg, &sport->port); + + return 0; +} + +static int sport_uart_probe(struct platform_device *dev) +{ + pr_debug("%s enter\n", __FUNCTION__); + sport_uart_ports[dev->id].port.dev = &dev->dev; + uart_add_one_port(&sport_uart_reg, &sport_uart_ports[dev->id].port); + platform_set_drvdata(dev, &sport_uart_ports[dev->id]); + + return 0; +} + +static int sport_uart_remove(struct platform_device *dev) +{ + struct sport_uart_port *sport = platform_get_drvdata(dev); + + pr_debug("%s enter\n", __FUNCTION__); + platform_set_drvdata(dev, NULL); + + if (sport) + uart_remove_one_port(&sport_uart_reg, &sport->port); + + return 0; +} + +static struct platform_driver sport_uart_driver = { + .probe = sport_uart_probe, + .remove = sport_uart_remove, + .suspend = sport_uart_suspend, + .resume = sport_uart_resume, + .driver = { + .name = DRV_NAME, + }, +}; + +static int __init sport_uart_init(void) +{ + int ret; + + pr_debug("%s enter\n", __FUNCTION__); + ret = uart_register_driver(&sport_uart_reg); + if (ret != 0) { + printk(KERN_ERR "Failed to register %s:%d\n", + sport_uart_reg.driver_name, ret); + return ret; + } + + ret = platform_driver_register(&sport_uart_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register sport uart driver:%d\n", ret); + uart_unregister_driver(&sport_uart_reg); + } + + + pr_debug("%s exit\n", __FUNCTION__); + return ret; +} + +static void __exit sport_uart_exit(void) +{ + pr_debug("%s enter\n", __FUNCTION__); + platform_driver_unregister(&sport_uart_driver); + uart_unregister_driver(&sport_uart_reg); +} + +module_init(sport_uart_init); +module_exit(sport_uart_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/bfin_sport_uart.h b/drivers/serial/bfin_sport_uart.h new file mode 100644 index 00000000000..671d41cc1a3 --- /dev/null +++ b/drivers/serial/bfin_sport_uart.h @@ -0,0 +1,63 @@ +/* + * File: linux/drivers/serial/bfin_sport_uart.h + * + * Based on: include/asm-blackfin/mach-533/bfin_serial_5xx.h + * Author: Roy Huang <roy.huang>analog.com> + * + * Created: Nov 22, 2006 + * Copyright: (C) Analog Device Inc. + * Description: this driver enable SPORTs on Blackfin emulate UART. + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#define OFFSET_TCR1 0x00 /* Transmit Configuration 1 Register */ +#define OFFSET_TCR2 0x04 /* Transmit Configuration 2 Register */ +#define OFFSET_TCLKDIV 0x08 /* Transmit Serial Clock Divider Register */ +#define OFFSET_TFSDIV 0x0C /* Transmit Frame Sync Divider Register */ +#define OFFSET_TX 0x10 /* Transmit Data Register */ +#define OFFSET_RX 0x18 /* Receive Data Register */ +#define OFFSET_RCR1 0x20 /* Receive Configuration 1 Register */ +#define OFFSET_RCR2 0x24 /* Receive Configuration 2 Register */ +#define OFFSET_RCLKDIV 0x28 /* Receive Serial Clock Divider Register */ +#define OFFSET_RFSDIV 0x2c /* Receive Frame Sync Divider Register */ +#define OFFSET_STAT 0x30 /* Status Register */ + +#define SPORT_GET_TCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_TCR1)) +#define SPORT_GET_TCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_TCR2)) +#define SPORT_GET_TCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TCLKDIV)) +#define SPORT_GET_TFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TFSDIV)) +#define SPORT_GET_TX(sport) bfin_read16(((sport)->port.membase + OFFSET_TX)) +#define SPORT_GET_RX(sport) bfin_read16(((sport)->port.membase + OFFSET_RX)) +#define SPORT_GET_RX32(sport) bfin_read32(((sport)->port.membase + OFFSET_RX)) +#define SPORT_GET_RCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR1)) +#define SPORT_GET_RCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR2)) +#define SPORT_GET_RCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RCLKDIV)) +#define SPORT_GET_RFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RFSDIV)) +#define SPORT_GET_STAT(sport) bfin_read16(((sport)->port.membase + OFFSET_STAT)) + +#define SPORT_PUT_TCR1(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCR1), v) +#define SPORT_PUT_TCR2(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCR2), v) +#define SPORT_PUT_TCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCLKDIV), v) +#define SPORT_PUT_TFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TFSDIV), v) +#define SPORT_PUT_TX(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TX), v) +#define SPORT_PUT_RX(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RX), v) +#define SPORT_PUT_RCR1(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCR1), v) +#define SPORT_PUT_RCR2(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCR2), v) +#define SPORT_PUT_RCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCLKDIV), v) +#define SPORT_PUT_RFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RFSDIV), v) +#define SPORT_PUT_STAT(sport, v) bfin_write16(((sport)->port.membase + OFFSET_STAT), v) diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index a638ba0679a..a19dc7ef886 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -1117,7 +1117,7 @@ int cpm_uart_drv_get_platform_data(struct platform_device *pdev, int is_con) line = cpm_uart_id2nr(idx); if(line < 0) { - printk(KERN_ERR"%s(): port %d is not registered", __FUNCTION__, idx); + printk(KERN_ERR"%s(): port %d is not registered", __func__, idx); return -EINVAL; } diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c index 88e7c1d5b91..f9fa237aa94 100644 --- a/drivers/serial/crisv10.c +++ b/drivers/serial/crisv10.c @@ -1788,7 +1788,7 @@ static unsigned int handle_descr_data(struct e100_serial *info, if (info->recv_cnt + recvl > 65536) { printk(KERN_CRIT - "%s: Too much pending incoming serial data! Dropping %u bytes.\n", __FUNCTION__, recvl); + "%s: Too much pending incoming serial data! Dropping %u bytes.\n", __func__, recvl); return 0; } @@ -1801,7 +1801,7 @@ static unsigned int handle_descr_data(struct e100_serial *info, append_recv_buffer(info, buffer); if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE))) - panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__); + panic("%s: Failed to allocate memory for receive buffer!\n", __func__); descr->buf = virt_to_phys(buffer->buffer); @@ -1925,7 +1925,7 @@ static int start_recv_dma(struct e100_serial *info) /* Set up the receiving descriptors */ for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) { if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE))) - panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__); + panic("%s: Failed to allocate memory for receive buffer!\n", __func__); descr[i].ctrl = d_int; descr[i].buf = virt_to_phys(buffer->buffer); @@ -3581,8 +3581,9 @@ rs_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long flags; - lock_kernel(); + local_irq_save(flags); if (clear & TIOCM_RTS) e100_rts(info, 0); @@ -3604,7 +3605,7 @@ rs_tiocmset(struct tty_struct *tty, struct file *file, if (set & TIOCM_CD) e100_cd_out(info, 1); - unlock_kernel(); + local_irq_restore(flags); return 0; } @@ -3613,8 +3614,10 @@ rs_tiocmget(struct tty_struct *tty, struct file *file) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; unsigned int result; + unsigned long flags; + + local_irq_save(flags); - lock_kernel(); result = (!E100_RTS_GET(info) ? TIOCM_RTS : 0) | (!E100_DTR_GET(info) ? TIOCM_DTR : 0) @@ -3623,7 +3626,7 @@ rs_tiocmget(struct tty_struct *tty, struct file *file) | (!E100_CD_GET(info) ? TIOCM_CAR : 0) | (!E100_CTS_GET(info) ? TIOCM_CTS : 0); - unlock_kernel(); + local_irq_restore(flags); #ifdef SERIAL_DEBUG_IO printk(KERN_DEBUG "ser%i: modem state: %i 0x%08X\n", @@ -3702,10 +3705,6 @@ rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; - if (tty->termios->c_cflag == old_termios->c_cflag && - tty->termios->c_iflag == old_termios->c_iflag) - return; - change_speed(info); /* Handle turning off CRTSCTS */ @@ -3808,10 +3807,8 @@ rs_close(struct tty_struct *tty, struct file * filp) #endif shutdown(info); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + rs_flush_buffer(tty); + tty_ldisc_flush_buffer(tty); tty->closing = 0; info->event = 0; info->tty = 0; @@ -3885,6 +3882,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k) */ + lock_kernel(); orig_jiffies = jiffies; while (info->xmit.head != info->xmit.tail || /* More in send queue */ (*info->ostatusadr & 0x007f) || /* more in FIFO */ @@ -3901,6 +3899,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) curr_time_usec - info->last_tx_active_usec; } set_current_state(TASK_RUNNING); + unlock_kernel(); } /* @@ -4520,7 +4519,7 @@ rs_init(void) if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, IRQF_SHARED | IRQF_DISABLED, "serial ", driver)) - panic("%s: Failed to request irq8", __FUNCTION__); + panic("%s: Failed to request irq8", __func__); #endif #endif /* CONFIG_SVINTO_SIM */ diff --git a/drivers/serial/ioc3_serial.c b/drivers/serial/ioc3_serial.c index 168073f12ce..4f1af71e9a1 100644 --- a/drivers/serial/ioc3_serial.c +++ b/drivers/serial/ioc3_serial.c @@ -52,7 +52,7 @@ static unsigned int Submodule_slot; #define DPRINT_CONFIG(_x...) ; //#define DPRINT_CONFIG(_x...) printk _x #define NOT_PROGRESS() ; -//#define NOT_PROGRESS() printk("%s : fails %d\n", __FUNCTION__, __LINE__) +//#define NOT_PROGRESS() printk("%s : fails %d\n", __func__, __LINE__) /* number of characters we want to transmit to the lower level at a time */ #define MAX_CHARS 256 @@ -445,7 +445,7 @@ static int inline port_init(struct ioc3_port *port) sbbr_h = &idd->vma->sbbr_h; ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf; DPRINT_CONFIG(("%s: ring_pci_addr 0x%p\n", - __FUNCTION__, (void *)ring_pci_addr)); + __func__, (void *)ring_pci_addr)); writel((unsigned int)((uint64_t) ring_pci_addr >> 32), sbbr_h); writel((unsigned int)ring_pci_addr | BUF_SIZE_BIT, sbbr_l); @@ -593,7 +593,7 @@ config_port(struct ioc3_port *port, DPRINT_CONFIG(("%s: line %d baud %d byte_size %d stop %d parenb %d " "parodd %d\n", - __FUNCTION__, ((struct uart_port *)port->ip_port)->line, + __func__, ((struct uart_port *)port->ip_port)->line, baud, byte_size, stop_bits, parenb, parodd)); if (set_baud(port, baud)) @@ -871,14 +871,14 @@ static int ioc3_set_proto(struct ioc3_port *port, int proto) default: case PROTO_RS232: /* Clear the appropriate GIO pin */ - DPRINT_CONFIG(("%s: rs232\n", __FUNCTION__)); + DPRINT_CONFIG(("%s: rs232\n", __func__)); writel(0, (&port->ip_idd->vma->gppr[0] + hooks->rs422_select_pin)); break; case PROTO_RS422: /* Set the appropriate GIO pin */ - DPRINT_CONFIG(("%s: rs422\n", __FUNCTION__)); + DPRINT_CONFIG(("%s: rs422\n", __func__)); writel(1, (&port->ip_idd->vma->gppr[0] + hooks->rs422_select_pin)); break; @@ -988,7 +988,7 @@ ioc3_change_speed(struct uart_port *the_port, } baud = uart_get_baud_rate(the_port, new_termios, old_termios, MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED); - DPRINT_CONFIG(("%s: returned baud %d for line %d\n", __FUNCTION__, baud, + DPRINT_CONFIG(("%s: returned baud %d for line %d\n", __func__, baud, the_port->line)); if (!the_port->fifosize) @@ -1026,7 +1026,7 @@ ioc3_change_speed(struct uart_port *the_port, DPRINT_CONFIG(("%s : port 0x%p line %d cflag 0%o " "config_port(baud %d data %d stop %d penable %d " " parity %d), notification 0x%x\n", - __FUNCTION__, (void *)port, the_port->line, cflag, baud, + __func__, (void *)port, the_port->line, cflag, baud, new_data, new_stop, new_parity_enable, new_parity, the_port->ignore_status_mask)); @@ -1919,7 +1919,7 @@ static inline int ioc3_serial_core_attach( struct ioc3_submodule *is, struct pci_dev *pdev = idd->pdev; DPRINT_CONFIG(("%s: attach pdev 0x%p - card_ptr 0x%p\n", - __FUNCTION__, pdev, (void *)card_ptr)); + __func__, pdev, (void *)card_ptr)); if (!card_ptr) return -ENODEV; @@ -1933,7 +1933,7 @@ static inline int ioc3_serial_core_attach( struct ioc3_submodule *is, port->ip_port = the_port; DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p [%d/%d]\n", - __FUNCTION__, (void *)the_port, (void *)port, + __func__, (void *)the_port, (void *)port, phys_port, ii)); /* membase, iobase and mapbase just need to be non-0 */ @@ -1950,7 +1950,7 @@ static inline int ioc3_serial_core_attach( struct ioc3_submodule *is, if (uart_add_one_port(&ioc3_uart, the_port) < 0) { printk(KERN_WARNING "%s: unable to add port %d bus %d\n", - __FUNCTION__, the_port->line, pdev->bus->number); + __func__, the_port->line, pdev->bus->number); } else { DPRINT_CONFIG(("IOC3 serial port %d irq %d bus %d\n", the_port->line, the_port->irq, pdev->bus->number)); @@ -2017,7 +2017,7 @@ ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) struct ioc3_port *ports[PORTS_PER_CARD]; int phys_port; - DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __FUNCTION__, is, idd)); + DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, is, idd)); card_ptr = kzalloc(sizeof(struct ioc3_card), GFP_KERNEL); if (!card_ptr) { @@ -2067,7 +2067,7 @@ ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) DPRINT_CONFIG(("%s : Port A ip_serial_regs 0x%p " "ip_uart_regs 0x%p\n", - __FUNCTION__, + __func__, (void *)port->ip_serial_regs, (void *)port->ip_uart_regs)); @@ -2082,7 +2082,7 @@ ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) DPRINT_CONFIG(("%s : Port A ip_cpu_ringbuf 0x%p " "ip_dma_ringbuf 0x%p, ip_inring 0x%p " "ip_outring 0x%p\n", - __FUNCTION__, + __func__, (void *)port->ip_cpu_ringbuf, (void *)port->ip_dma_ringbuf, (void *)port->ip_inring, @@ -2094,7 +2094,7 @@ ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) DPRINT_CONFIG(("%s : Port B ip_serial_regs 0x%p " "ip_uart_regs 0x%p\n", - __FUNCTION__, + __func__, (void *)port->ip_serial_regs, (void *)port->ip_uart_regs)); @@ -2108,7 +2108,7 @@ ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) DPRINT_CONFIG(("%s : Port B ip_cpu_ringbuf 0x%p " "ip_dma_ringbuf 0x%p, ip_inring 0x%p " "ip_outring 0x%p\n", - __FUNCTION__, + __func__, (void *)port->ip_cpu_ringbuf, (void *)port->ip_dma_ringbuf, (void *)port->ip_inring, @@ -2116,7 +2116,7 @@ ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) } DPRINT_CONFIG(("%s : port %d [addr 0x%p] card_ptr 0x%p", - __FUNCTION__, + __func__, phys_port, (void *)port, (void *)card_ptr)); DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n", (void *)port->ip_serial_regs, @@ -2127,7 +2127,7 @@ ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) DPRINT_CONFIG(("%s: phys_port %d port 0x%p inring 0x%p " "outring 0x%p\n", - __FUNCTION__, + __func__, phys_port, (void *)port, (void *)port->ip_inring, (void *)port->ip_outring)); @@ -2170,7 +2170,7 @@ static int __devinit ioc3uart_init(void) if ((ret = uart_register_driver(&ioc3_uart)) < 0) { printk(KERN_WARNING "%s: Couldn't register IOC3 uart serial driver\n", - __FUNCTION__); + __func__); return ret; } ret = ioc3_register_submodule(&ioc3uart_submodule); diff --git a/drivers/serial/ioc4_serial.c b/drivers/serial/ioc4_serial.c index 0c179384fb0..49b8a82b7b9 100644 --- a/drivers/serial/ioc4_serial.c +++ b/drivers/serial/ioc4_serial.c @@ -889,7 +889,7 @@ static int inline port_init(struct ioc4_port *port) ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf; DPRINT_CONFIG(("%s: ring_pci_addr 0x%lx\n", - __FUNCTION__, ring_pci_addr)); + __func__, ring_pci_addr)); writel((unsigned int)((uint64_t)ring_pci_addr >> 32), sbbr_h); writel((unsigned int)ring_pci_addr | IOC4_BUF_SIZE_BIT, sbbr_l); @@ -1028,7 +1028,7 @@ static irqreturn_t ioc4_intr(int irq, void *arg) spin_lock_irqsave(&soft->is_ir_lock, flag); printk ("%s : %d : mem 0x%p sio_ir 0x%x sio_ies 0x%x " "other_ir 0x%x other_ies 0x%x mask 0x%x\n", - __FUNCTION__, __LINE__, + __func__, __LINE__, (void *)mem, readl(&mem->sio_ir.raw), readl(&mem->sio_ies.raw), readl(&mem->other_ir.raw), @@ -1155,14 +1155,14 @@ static int inline ioc4_attach_local(struct ioc4_driver_data *idd) (TOTAL_RING_BUF_SIZE - 1)) == 0)); DPRINT_CONFIG(("%s : ip_cpu_ringbuf 0x%p " "ip_dma_ringbuf 0x%p\n", - __FUNCTION__, + __func__, (void *)port->ip_cpu_ringbuf, (void *)port->ip_dma_ringbuf)); port->ip_inring = RING(port, RX_0_OR_2); port->ip_outring = RING(port, TX_0_OR_2); } DPRINT_CONFIG(("%s : port %d [addr 0x%p] control 0x%p", - __FUNCTION__, + __func__, port_number, (void *)port, (void *)control)); DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n", (void *)port->ip_serial_regs, @@ -1173,7 +1173,7 @@ static int inline ioc4_attach_local(struct ioc4_driver_data *idd) DPRINT_CONFIG(("%s: port_number %d port 0x%p inring 0x%p " "outring 0x%p\n", - __FUNCTION__, + __func__, port_number, (void *)port, (void *)port->ip_inring, (void *)port->ip_outring)); @@ -1317,7 +1317,7 @@ config_port(struct ioc4_port *port, int spiniter = 0; DPRINT_CONFIG(("%s: baud %d byte_size %d stop %d parenb %d parodd %d\n", - __FUNCTION__, baud, byte_size, stop_bits, parenb, parodd)); + __func__, baud, byte_size, stop_bits, parenb, parodd)); if (set_baud(port, baud)) return 1; @@ -1725,7 +1725,7 @@ ioc4_change_speed(struct uart_port *the_port, } baud = uart_get_baud_rate(the_port, new_termios, old_termios, MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED); - DPRINT_CONFIG(("%s: returned baud %d\n", __FUNCTION__, baud)); + DPRINT_CONFIG(("%s: returned baud %d\n", __func__, baud)); /* default is 9600 */ if (!baud) @@ -1765,7 +1765,7 @@ ioc4_change_speed(struct uart_port *the_port, DPRINT_CONFIG(("%s : port 0x%p cflag 0%o " "config_port(baud %d data %d stop %d p enable %d parity %d)," " notification 0x%x\n", - __FUNCTION__, (void *)port, cflag, baud, new_data, new_stop, + __func__, (void *)port, cflag, baud, new_data, new_stop, new_parity_enable, new_parity, the_port->ignore_status_mask)); if ((config_port(port, baud, /* baud */ @@ -2715,7 +2715,7 @@ ioc4_serial_core_attach(struct pci_dev *pdev, int port_type) DPRINT_CONFIG(("%s: attach pdev 0x%p - control 0x%p\n", - __FUNCTION__, pdev, (void *)control)); + __func__, pdev, (void *)control)); if (!control) return -ENODEV; @@ -2734,7 +2734,7 @@ ioc4_serial_core_attach(struct pci_dev *pdev, int port_type) port->ip_all_ports[port_type_idx] = the_port; DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p : type %s\n", - __FUNCTION__, (void *)the_port, + __func__, (void *)the_port, (void *)port, port_type == PROTO_RS232 ? "rs232" : "rs422")); @@ -2752,7 +2752,7 @@ ioc4_serial_core_attach(struct pci_dev *pdev, int port_type) if (uart_add_one_port(u_driver, the_port) < 0) { printk(KERN_WARNING "%s: unable to add port %d bus %d\n", - __FUNCTION__, the_port->line, pdev->bus->number); + __func__, the_port->line, pdev->bus->number); } else { DPRINT_CONFIG( ("IOC4 serial port %d irq = %d, bus %d\n", @@ -2777,7 +2777,7 @@ ioc4_serial_attach_one(struct ioc4_driver_data *idd) int ret = 0; - DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __FUNCTION__, idd->idd_pdev, + DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, idd->idd_pdev, idd->idd_pci_id)); /* PCI-RT does not bring out serial connections. @@ -2806,7 +2806,7 @@ ioc4_serial_attach_one(struct ioc4_driver_data *idd) goto out2; } DPRINT_CONFIG(("%s : mem 0x%p, serial 0x%p\n", - __FUNCTION__, (void *)idd->idd_misc_regs, + __func__, (void *)idd->idd_misc_regs, (void *)serial)); /* Get memory for the new card */ @@ -2858,7 +2858,7 @@ ioc4_serial_attach_one(struct ioc4_driver_data *idd) } else { printk(KERN_WARNING "%s : request_irq fails for IRQ 0x%x\n ", - __FUNCTION__, idd->idd_pdev->irq); + __func__, idd->idd_pdev->irq); } ret = ioc4_attach_local(idd); if (ret) @@ -2911,13 +2911,13 @@ int ioc4_serial_init(void) if ((ret = uart_register_driver(&ioc4_uart_rs232)) < 0) { printk(KERN_WARNING "%s: Couldn't register rs232 IOC4 serial driver\n", - __FUNCTION__); + __func__); return ret; } if ((ret = uart_register_driver(&ioc4_uart_rs422)) < 0) { printk(KERN_WARNING "%s: Couldn't register rs422 IOC4 serial driver\n", - __FUNCTION__); + __func__); return ret; } diff --git a/drivers/serial/kgdboc.c b/drivers/serial/kgdboc.c index 9cf03327386..eadc1ab6bbc 100644 --- a/drivers/serial/kgdboc.c +++ b/drivers/serial/kgdboc.c @@ -96,12 +96,14 @@ static void cleanup_kgdboc(void) static int kgdboc_get_char(void) { - return kgdb_tty_driver->poll_get_char(kgdb_tty_driver, kgdb_tty_line); + return kgdb_tty_driver->ops->poll_get_char(kgdb_tty_driver, + kgdb_tty_line); } static void kgdboc_put_char(u8 chr) { - kgdb_tty_driver->poll_put_char(kgdb_tty_driver, kgdb_tty_line, chr); + kgdb_tty_driver->ops->poll_put_char(kgdb_tty_driver, + kgdb_tty_line, chr); } static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp) diff --git a/drivers/serial/mcfserial.c b/drivers/serial/mcfserial.c index ddd3aa50d4a..43af40d59b8 100644 --- a/drivers/serial/mcfserial.c +++ b/drivers/serial/mcfserial.c @@ -1072,18 +1072,6 @@ static int mcfrs_ioctl(struct tty_struct *tty, struct file * file, tty_wait_until_sent(tty, 0); send_break(info, arg ? arg*(HZ/10) : HZ/4); return 0; - case TIOCGSOFTCAR: - error = put_user(C_CLOCAL(tty) ? 1 : 0, - (unsigned long *) arg); - if (error) - return error; - return 0; - case TIOCSSOFTCAR: - get_user(arg, (unsigned long *) arg); - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - return 0; case TIOCGSERIAL: if (access_ok(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) @@ -1222,8 +1210,7 @@ static void mcfrs_close(struct tty_struct *tty, struct file * filp) } else #endif shutdown(info); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + mcfrs_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; @@ -1276,6 +1263,8 @@ mcfrs_wait_until_sent(struct tty_struct *tty, int timeout) * Note: we have to use pretty tight timings here to satisfy * the NIST-PCTS. */ + lock_kernel(); + fifo_time = (MCF5272_FIFO_SIZE * HZ * 10) / info->baud; char_time = fifo_time / 5; if (char_time == 0) @@ -1312,6 +1301,7 @@ mcfrs_wait_until_sent(struct tty_struct *tty, int timeout) if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; } + unlock_kernel(); #else /* * For the other coldfire models, assume all data has been sent @@ -1907,7 +1897,7 @@ static struct tty_driver *mcfrs_console_device(struct console *c, int *index) * This is used for console output. */ -void mcfrs_put_char(char ch) +int mcfrs_put_char(char ch) { volatile unsigned char *uartp; unsigned long flags; @@ -1931,7 +1921,7 @@ void mcfrs_put_char(char ch) mcfrs_init_console(); /* try and get it back */ local_irq_restore(flags); - return; + return 1; } diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index d93b3578c5e..7a3625f52a0 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -1221,8 +1221,8 @@ static struct of_device_id mpc52xx_uart_of_match[] = { #endif #ifdef CONFIG_PPC_MPC512x { .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, }, - {}, #endif + {}, }; static int __devinit diff --git a/drivers/serial/netx-serial.c b/drivers/serial/netx-serial.c index 3123ffeac8a..81ac9bb4f39 100644 --- a/drivers/serial/netx-serial.c +++ b/drivers/serial/netx-serial.c @@ -287,6 +287,7 @@ static void netx_set_mctrl(struct uart_port *port, unsigned int mctrl) { unsigned int val; + /* FIXME: Locking needed ? */ if (mctrl & TIOCM_RTS) { val = readl(port->membase + UART_RTS_CR); writel(val | RTS_CR_RTS, port->membase + UART_RTS_CR); diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c index da5a02cb4f6..2b6a013639e 100644 --- a/drivers/serial/s3c2410.c +++ b/drivers/serial/s3c2410.c @@ -1096,13 +1096,13 @@ static int s3c24xx_serial_probe(struct platform_device *dev, ourport = &s3c24xx_serial_ports[probe_index]; probe_index++; - dbg("%s: initialising port %p...\n", __FUNCTION__, ourport); + dbg("%s: initialising port %p...\n", __func__, ourport); ret = s3c24xx_serial_init_port(ourport, info, dev); if (ret < 0) goto probe_err; - dbg("%s: adding port\n", __FUNCTION__); + dbg("%s: adding port\n", __func__); uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); platform_set_drvdata(dev, &ourport->port); @@ -1587,7 +1587,7 @@ static int s3c2412_serial_resetport(struct uart_port *port, unsigned long ucon = rd_regl(port, S3C2410_UCON); dbg("%s: port=%p (%08lx), cfg=%p\n", - __FUNCTION__, port, port->mapbase, cfg); + __func__, port, port->mapbase, cfg); /* ensure we don't change the clock settings... */ diff --git a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c index 67b2338913c..62b38582f5e 100644 --- a/drivers/serial/sa1100.c +++ b/drivers/serial/sa1100.c @@ -655,7 +655,7 @@ void __init sa1100_register_uart_fns(struct sa1100_port_fns *fns) void __init sa1100_register_uart(int idx, int port) { if (idx >= NR_PORTS) { - printk(KERN_ERR "%s: bad index number %d\n", __FUNCTION__, idx); + printk(KERN_ERR "%s: bad index number %d\n", __func__, idx); return; } @@ -682,7 +682,7 @@ void __init sa1100_register_uart(int idx, int port) break; default: - printk(KERN_ERR "%s: bad port number %d\n", __FUNCTION__, port); + printk(KERN_ERR "%s: bad port number %d\n", __func__, port); } } diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 977ce820ce3..1e2b9d826f6 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -422,6 +422,7 @@ uart_get_divisor(struct uart_port *port, unsigned int baud) EXPORT_SYMBOL(uart_get_divisor); +/* FIXME: Consistent locking policy */ static void uart_change_speed(struct uart_state *state, struct ktermios *old_termios) { @@ -454,27 +455,30 @@ uart_change_speed(struct uart_state *state, struct ktermios *old_termios) port->ops->set_termios(port, termios, old_termios); } -static inline void +static inline int __uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c) { unsigned long flags; + int ret = 0; if (!circ->buf) - return; + return 0; spin_lock_irqsave(&port->lock, flags); if (uart_circ_chars_free(circ) != 0) { circ->buf[circ->head] = c; circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); + ret = 1; } spin_unlock_irqrestore(&port->lock, flags); + return ret; } -static void uart_put_char(struct tty_struct *tty, unsigned char ch) +static int uart_put_char(struct tty_struct *tty, unsigned char ch) { struct uart_state *state = tty->driver_data; - __uart_put_char(state->port, &state->info->xmit, ch); + return __uart_put_char(state->port, &state->info->xmit, ch); } static void uart_flush_chars(struct tty_struct *tty) @@ -528,15 +532,25 @@ uart_write(struct tty_struct *tty, const unsigned char *buf, int count) static int uart_write_room(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; + unsigned long flags; + int ret; - return uart_circ_chars_free(&state->info->xmit); + spin_lock_irqsave(&state->port->lock, flags); + ret = uart_circ_chars_free(&state->info->xmit); + spin_unlock_irqrestore(&state->port->lock, flags); + return ret; } static int uart_chars_in_buffer(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; + unsigned long flags; + int ret; - return uart_circ_chars_pending(&state->info->xmit); + spin_lock_irqsave(&state->port->lock, flags); + ret = uart_circ_chars_pending(&state->info->xmit); + spin_unlock_irqrestore(&state->port->lock, flags); + return ret; } static void uart_flush_buffer(struct tty_struct *tty) @@ -618,6 +632,11 @@ static int uart_get_info(struct uart_state *state, struct serial_struct tmp; memset(&tmp, 0, sizeof(tmp)); + + /* Ensure the state we copy is consistent and no hardware changes + occur as we go */ + mutex_lock(&state->mutex); + tmp.type = port->type; tmp.line = port->line; tmp.port = port->iobase; @@ -637,6 +656,8 @@ static int uart_get_info(struct uart_state *state, tmp.iomem_reg_shift = port->regshift; tmp.iomem_base = (void *)(unsigned long)port->mapbase; + mutex_unlock(&state->mutex); + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; return 0; @@ -914,8 +935,6 @@ static void uart_break_ctl(struct tty_struct *tty, int break_state) struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; - BUG_ON(!kernel_locked()); - mutex_lock(&state->mutex); if (port->type != PORT_UNKNOWN) @@ -1059,7 +1078,7 @@ static int uart_get_count(struct uart_state *state, } /* - * Called via sys_ioctl under the BKL. We can use spin_lock_irq() here. + * Called via sys_ioctl. We can use spin_lock_irq() here. */ static int uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, @@ -1069,7 +1088,6 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, void __user *uarg = (void __user *)arg; int ret = -ENOIOCTLCMD; - BUG_ON(!kernel_locked()); /* * These ioctls don't rely on the hardware to be present. @@ -1140,9 +1158,9 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, break; } } - out_up: +out_up: mutex_unlock(&state->mutex); - out: +out: return ret; } @@ -1153,7 +1171,6 @@ static void uart_set_termios(struct tty_struct *tty, unsigned long flags; unsigned int cflag = tty->termios->c_cflag; - BUG_ON(!kernel_locked()); /* * These are the bits that are used to setup various @@ -1165,8 +1182,9 @@ static void uart_set_termios(struct tty_struct *tty, if ((cflag ^ old_termios->c_cflag) == 0 && tty->termios->c_ospeed == old_termios->c_ospeed && tty->termios->c_ispeed == old_termios->c_ispeed && - RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) + RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) { return; + } uart_change_speed(state, old_termios); @@ -1200,7 +1218,6 @@ static void uart_set_termios(struct tty_struct *tty, } spin_unlock_irqrestore(&state->port->lock, flags); } - #if 0 /* * No need to wake up processes in open wait, since they @@ -1316,11 +1333,11 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) struct uart_port *port = state->port; unsigned long char_time, expire; - BUG_ON(!kernel_locked()); - if (port->type == PORT_UNKNOWN || port->fifosize == 0) return; + lock_kernel(); + /* * Set the check interval to be 1/5 of the estimated time to * send a single character, and make it at least 1. The check @@ -1366,6 +1383,7 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) break; } set_current_state(TASK_RUNNING); /* might not be needed */ + unlock_kernel(); } /* @@ -2079,7 +2097,9 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port) int ret; uart_change_pm(state, 0); + spin_lock_irq(&port->lock); ops->set_mctrl(port, 0); + spin_unlock_irq(&port->lock); ret = ops->startup(port); if (ret == 0) { uart_change_speed(state, NULL); diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index c2ea5d4df44..96910618771 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -855,7 +855,7 @@ static int sci_notifier(struct notifier_block *self, printk(KERN_INFO "%s: got a postchange notification " "for cpu %d (old %d, new %d)\n", - __FUNCTION__, freqs->cpu, freqs->old, freqs->new); + __func__, freqs->cpu, freqs->old, freqs->new); } return NOTIFY_OK; diff --git a/drivers/serial/sn_console.c b/drivers/serial/sn_console.c index 41fc6126444..019da2e05f0 100644 --- a/drivers/serial/sn_console.c +++ b/drivers/serial/sn_console.c @@ -839,7 +839,7 @@ static int __init sn_sal_module_init(void) if (uart_add_one_port(&sal_console_uart, &sal_console_port.sc_port) < 0) { /* error - not sure what I'd do - so I'll do nothing */ - printk(KERN_ERR "%s: unable to add port\n", __FUNCTION__); + printk(KERN_ERR "%s: unable to add port\n", __func__); } /* when this driver is compiled in, the console initialization diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index b565d5a3749..b51c24245be 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -584,7 +584,7 @@ ulite_of_probe(struct of_device *op, const struct of_device_id *match) const unsigned int *id; int irq, rc; - dev_dbg(&op->dev, "%s(%p, %p)\n", __FUNCTION__, op, match); + dev_dbg(&op->dev, "%s(%p, %p)\n", __func__, op, match); rc = of_address_to_resource(op->node, 0, &res); if (rc) { diff --git a/drivers/serial/ucc_uart.c b/drivers/serial/ucc_uart.c index 5e4310ccd59..01917c433f1 100644 --- a/drivers/serial/ucc_uart.c +++ b/drivers/serial/ucc_uart.c @@ -215,7 +215,7 @@ static inline dma_addr_t cpu2qe_addr(void *addr, struct uart_qe_port *qe_port) return qe_port->bd_dma_addr + (addr - qe_port->bd_virt); /* something nasty happened */ - printk(KERN_ERR "%s: addr=%p\n", __FUNCTION__, addr); + printk(KERN_ERR "%s: addr=%p\n", __func__, addr); BUG(); return 0; } @@ -234,7 +234,7 @@ static inline void *qe2cpu_addr(dma_addr_t addr, struct uart_qe_port *qe_port) return qe_port->bd_virt + (addr - qe_port->bd_dma_addr); /* something nasty happened */ - printk(KERN_ERR "%s: addr=%x\n", __FUNCTION__, addr); + printk(KERN_ERR "%s: addr=%x\n", __func__, addr); BUG(); return NULL; } diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 02c8e305b14..e81d59d7891 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -497,7 +497,7 @@ static int atmel_spi_setup(struct spi_device *spi) struct atmel_spi *as; u32 scbr, csr; unsigned int bits = spi->bits_per_word; - unsigned long bus_hz, sck_hz; + unsigned long bus_hz; unsigned int npcs_pin; int ret; @@ -536,14 +536,25 @@ static int atmel_spi_setup(struct spi_device *spi) return -EINVAL; } - /* speed zero convention is used by some upper layers */ + /* + * Pre-new_1 chips start out at half the peripheral + * bus speed. + */ bus_hz = clk_get_rate(as->clk); + if (!as->new_1) + bus_hz /= 2; + if (spi->max_speed_hz) { - /* assume div32/fdiv/mbz == 0 */ - if (!as->new_1) - bus_hz /= 2; - scbr = ((bus_hz + spi->max_speed_hz - 1) - / spi->max_speed_hz); + /* + * Calculate the lowest divider that satisfies the + * constraint, assuming div32/fdiv/mbz == 0. + */ + scbr = DIV_ROUND_UP(bus_hz, spi->max_speed_hz); + + /* + * If the resulting divider doesn't fit into the + * register bitfield, we can't satisfy the constraint. + */ if (scbr >= (1 << SPI_SCBR_SIZE)) { dev_dbg(&spi->dev, "setup: %d Hz too slow, scbr %u; min %ld Hz\n", @@ -551,8 +562,8 @@ static int atmel_spi_setup(struct spi_device *spi) return -EINVAL; } } else + /* speed zero means "as slow as possible" */ scbr = 0xff; - sck_hz = bus_hz / scbr; csr = SPI_BF(SCBR, scbr) | SPI_BF(BITS, bits - 8); if (spi->mode & SPI_CPOL) @@ -589,7 +600,7 @@ static int atmel_spi_setup(struct spi_device *spi) dev_dbg(&spi->dev, "setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n", - sck_hz, bits, spi->mode, spi->chip_select, csr); + bus_hz / scbr, bits, spi->mode, spi->chip_select, csr); spi_writel(as, CSR0 + 4 * spi->chip_select, csr); diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 433b3f44f42..8d158e5640e 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -170,7 +170,7 @@ static int gs_open(struct tty_struct *tty, struct file *file); static void gs_close(struct tty_struct *tty, struct file *file); static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count); -static void gs_put_char(struct tty_struct *tty, unsigned char ch); +static int gs_put_char(struct tty_struct *tty, unsigned char ch); static void gs_flush_chars(struct tty_struct *tty); static int gs_write_room(struct tty_struct *tty); static int gs_chars_in_buffer(struct tty_struct *tty); @@ -883,14 +883,15 @@ exit: /* * gs_put_char */ -static void gs_put_char(struct tty_struct *tty, unsigned char ch) +static int gs_put_char(struct tty_struct *tty, unsigned char ch) { unsigned long flags; struct gs_port *port = tty->driver_data; + int ret = 0; if (port == NULL) { pr_err("gs_put_char: NULL port pointer\n"); - return; + return 0; } gs_debug("gs_put_char: (%d,%p) char=0x%x, called from %p\n", @@ -910,10 +911,11 @@ static void gs_put_char(struct tty_struct *tty, unsigned char ch) goto exit; } - gs_buf_put(port->port_write_buf, &ch, 1); + ret = gs_buf_put(port->port_write_buf, &ch, 1); exit: spin_unlock_irqrestore(&port->port_lock, flags); + return ret; } /* diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index d17d1645714..04a56f300ea 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1421,8 +1421,7 @@ static void digi_close(struct usb_serial_port *port, struct file *filp) tty_wait_until_sent(tty, DIGI_CLOSE_TIMEOUT); /* flush driver and line discipline buffers */ - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty_driver_flush_buffer(tty); tty_ldisc_flush(tty); if (port->serial->dev) { diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index a9934a3f984..0cb0d77dc42 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -296,16 +296,14 @@ static int serial_write (struct tty_struct * tty, const unsigned char *buf, int struct usb_serial_port *port = tty->driver_data; int retval = -ENODEV; - if (!port || port->serial->dev->state == USB_STATE_NOTATTACHED) + if (port->serial->dev->state == USB_STATE_NOTATTACHED) goto exit; dbg("%s - port %d, %d byte(s)", __func__, port->number, count); - if (!port->open_count) { - retval = -EINVAL; - dbg("%s - port not opened", __func__); - goto exit; - } + /* open_count is managed under the mutex lock for the tty so cannot + drop to zero until after the last close completes */ + WARN_ON(!port->open_count); /* pass on to the driver specific version of this function */ retval = port->serial->type->write(port, buf, count); @@ -317,61 +315,28 @@ exit: static int serial_write_room (struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - int retval = -ENODEV; - - if (!port) - goto exit; - dbg("%s - port %d", __func__, port->number); - - if (!port->open_count) { - dbg("%s - port not open", __func__); - goto exit; - } - + WARN_ON(!port->open_count); /* pass on to the driver specific version of this function */ - retval = port->serial->type->write_room(port); - -exit: - return retval; + return port->serial->type->write_room(port); } static int serial_chars_in_buffer (struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - int retval = -ENODEV; - - if (!port) - goto exit; - dbg("%s = port %d", __func__, port->number); - if (!port->open_count) { - dbg("%s - port not open", __func__); - goto exit; - } - + WARN_ON(!port->open_count); /* pass on to the driver specific version of this function */ - retval = port->serial->type->chars_in_buffer(port); - -exit: - return retval; + return port->serial->type->chars_in_buffer(port); } static void serial_throttle (struct tty_struct * tty) { struct usb_serial_port *port = tty->driver_data; - - if (!port) - return; - dbg("%s - port %d", __func__, port->number); - if (!port->open_count) { - dbg ("%s - port not open", __func__); - return; - } - + WARN_ON(!port->open_count); /* pass on to the driver specific version of this function */ if (port->serial->type->throttle) port->serial->type->throttle(port); @@ -380,17 +345,9 @@ static void serial_throttle (struct tty_struct * tty) static void serial_unthrottle (struct tty_struct * tty) { struct usb_serial_port *port = tty->driver_data; - - if (!port) - return; - dbg("%s - port %d", __func__, port->number); - if (!port->open_count) { - dbg("%s - port not open", __func__); - return; - } - + WARN_ON(!port->open_count); /* pass on to the driver specific version of this function */ if (port->serial->type->unthrottle) port->serial->type->unthrottle(port); @@ -401,42 +358,27 @@ static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned in struct usb_serial_port *port = tty->driver_data; int retval = -ENODEV; - lock_kernel(); - if (!port) - goto exit; - dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); - /* Caution - port->open_count is BKL protected */ - if (!port->open_count) { - dbg ("%s - port not open", __func__); - goto exit; - } + WARN_ON(!port->open_count); /* pass on to the driver specific version of this function if it is available */ - if (port->serial->type->ioctl) + if (port->serial->type->ioctl) { + lock_kernel(); retval = port->serial->type->ioctl(port, file, cmd, arg); + unlock_kernel(); + } else retval = -ENOIOCTLCMD; -exit: - unlock_kernel(); return retval; } static void serial_set_termios (struct tty_struct *tty, struct ktermios * old) { struct usb_serial_port *port = tty->driver_data; - - if (!port) - return; - dbg("%s - port %d", __func__, port->number); - if (!port->open_count) { - dbg("%s - port not open", __func__); - return; - } - + WARN_ON(!port->open_count); /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->set_termios) port->serial->type->set_termios(port, old); @@ -448,24 +390,15 @@ static void serial_break (struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; - lock_kernel(); - if (!port) { - unlock_kernel(); - return; - } - dbg("%s - port %d", __func__, port->number); - if (!port->open_count) { - dbg("%s - port not open", __func__); - unlock_kernel(); - return; - } - + WARN_ON(!port->open_count); /* pass on to the driver specific version of this function if it is available */ - if (port->serial->type->break_ctl) + if (port->serial->type->break_ctl) { + lock_kernel(); port->serial->type->break_ctl(port, break_state); - unlock_kernel(); + unlock_kernel(); + } } static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) @@ -519,19 +452,11 @@ static int serial_tiocmget (struct tty_struct *tty, struct file *file) { struct usb_serial_port *port = tty->driver_data; - if (!port) - return -ENODEV; - dbg("%s - port %d", __func__, port->number); - if (!port->open_count) { - dbg("%s - port not open", __func__); - return -ENODEV; - } - + WARN_ON(!port->open_count); if (port->serial->type->tiocmget) return port->serial->type->tiocmget(port, file); - return -EINVAL; } @@ -540,19 +465,11 @@ static int serial_tiocmset (struct tty_struct *tty, struct file *file, { struct usb_serial_port *port = tty->driver_data; - if (!port) - return -ENODEV; - dbg("%s - port %d", __func__, port->number); - if (!port->open_count) { - dbg("%s - port not open", __func__); - return -ENODEV; - } - + WARN_ON(!port->open_count); if (port->serial->type->tiocmset) return port->serial->type->tiocmset(port, file, set, clear); - return -EINVAL; } diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index e96bf8663ff..f07e8a4c1f3 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -673,15 +673,13 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) } */ - if (port->tty->driver->flush_buffer) - port->tty->driver->flush_buffer(port->tty); + tty_driver_flush_buffer(port->tty); tty_ldisc_flush(port->tty); firm_report_tx_done(port); firm_close(port); -printk(KERN_ERR"Before processing rx_urbs_submitted.\n"); /* shutdown our bulk reads and writes */ mutex_lock(&info->deathwarrant); spin_lock_irq(&info->lock); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index a576dc26173..bb1dadaa4a2 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1774,6 +1774,11 @@ config FB_PXA If unsure, say N. +config FB_PXA_SMARTPANEL + bool "PXA Smartpanel LCD support" + default n + depends on FB_PXA + config FB_PXA_PARAMETERS bool "PXA LCD command line parameters" default n diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 757651954e6..3ab6e3d973a 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -39,6 +39,9 @@ #include <linux/dma-mapping.h> #include <linux/clk.h> #include <linux/err.h> +#include <linux/completion.h> +#include <linux/kthread.h> +#include <linux/freezer.h> #include <asm/hardware.h> #include <asm/io.h> @@ -57,19 +60,31 @@ #include "pxafb.h" /* Bits which should not be set in machine configuration structures */ -#define LCCR0_INVALID_CONFIG_MASK (LCCR0_OUM|LCCR0_BM|LCCR0_QDM|LCCR0_DIS|LCCR0_EFM|LCCR0_IUM|LCCR0_SFM|LCCR0_LDM|LCCR0_ENB) -#define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP|LCCR3_VSP|LCCR3_PCD|LCCR3_BPP) +#define LCCR0_INVALID_CONFIG_MASK (LCCR0_OUM | LCCR0_BM | LCCR0_QDM |\ + LCCR0_DIS | LCCR0_EFM | LCCR0_IUM |\ + LCCR0_SFM | LCCR0_LDM | LCCR0_ENB) + +#define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP | LCCR3_VSP |\ + LCCR3_PCD | LCCR3_BPP) static void (*pxafb_backlight_power)(int); static void (*pxafb_lcd_power)(int, struct fb_var_screeninfo *); -static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); +static int pxafb_activate_var(struct fb_var_screeninfo *var, + struct pxafb_info *); static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); -#ifdef CONFIG_FB_PXA_PARAMETERS -#define PXAFB_OPTIONS_SIZE 256 -static char g_options[PXAFB_OPTIONS_SIZE] __devinitdata = ""; -#endif +static inline unsigned long +lcd_readl(struct pxafb_info *fbi, unsigned int off) +{ + return __raw_readl(fbi->mmio_base + off); +} + +static inline void +lcd_writel(struct pxafb_info *fbi, unsigned int off, unsigned long val) +{ + __raw_writel(val, fbi->mmio_base + off); +} static inline void pxafb_schedule_work(struct pxafb_info *fbi, u_int state) { @@ -79,10 +94,12 @@ static inline void pxafb_schedule_work(struct pxafb_info *fbi, u_int state) /* * We need to handle two requests being made at the same time. * There are two important cases: - * 1. When we are changing VT (C_REENABLE) while unblanking (C_ENABLE) - * We must perform the unblanking, which will do our REENABLE for us. - * 2. When we are blanking, but immediately unblank before we have - * blanked. We do the "REENABLE" thing here as well, just to be sure. + * 1. When we are changing VT (C_REENABLE) while unblanking + * (C_ENABLE) We must perform the unblanking, which will + * do our REENABLE for us. + * 2. When we are blanking, but immediately unblank before + * we have blanked. We do the "REENABLE" thing here as + * well, just to be sure. */ if (fbi->task_state == C_ENABLE && state == C_REENABLE) state = (u_int) -1; @@ -129,13 +146,13 @@ pxafb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, val = ((red << 8) & 0x00f80000); val |= ((green >> 0) & 0x0000fc00); val |= ((blue >> 8) & 0x000000f8); - ((u32*)(fbi->palette_cpu))[regno] = val; + ((u32 *)(fbi->palette_cpu))[regno] = val; break; case LCCR4_PAL_FOR_2: val = ((red << 8) & 0x00fc0000); val |= ((green >> 0) & 0x0000fc00); val |= ((blue >> 8) & 0x000000fc); - ((u32*)(fbi->palette_cpu))[regno] = val; + ((u32 *)(fbi->palette_cpu))[regno] = val; break; } @@ -203,15 +220,15 @@ pxafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, */ static int pxafb_bpp_to_lccr3(struct fb_var_screeninfo *var) { - int ret = 0; - switch (var->bits_per_pixel) { - case 1: ret = LCCR3_1BPP; break; - case 2: ret = LCCR3_2BPP; break; - case 4: ret = LCCR3_4BPP; break; - case 8: ret = LCCR3_8BPP; break; - case 16: ret = LCCR3_16BPP; break; - } - return ret; + int ret = 0; + switch (var->bits_per_pixel) { + case 1: ret = LCCR3_1BPP; break; + case 2: ret = LCCR3_2BPP; break; + case 4: ret = LCCR3_4BPP; break; + case 8: ret = LCCR3_8BPP; break; + case 16: ret = LCCR3_16BPP; break; + } + return ret; } #ifdef CONFIG_CPU_FREQ @@ -223,31 +240,32 @@ static int pxafb_bpp_to_lccr3(struct fb_var_screeninfo *var) */ static unsigned int pxafb_display_dma_period(struct fb_var_screeninfo *var) { - /* - * Period = pixclock * bits_per_byte * bytes_per_transfer - * / memory_bits_per_pixel; - */ - return var->pixclock * 8 * 16 / var->bits_per_pixel; + /* + * Period = pixclock * bits_per_byte * bytes_per_transfer + * / memory_bits_per_pixel; + */ + return var->pixclock * 8 * 16 / var->bits_per_pixel; } - -extern unsigned int get_clk_frequency_khz(int info); #endif /* * Select the smallest mode that allows the desired resolution to be * displayed. If desired parameters can be rounded up. */ -static struct pxafb_mode_info *pxafb_getmode(struct pxafb_mach_info *mach, struct fb_var_screeninfo *var) +static struct pxafb_mode_info *pxafb_getmode(struct pxafb_mach_info *mach, + struct fb_var_screeninfo *var) { struct pxafb_mode_info *mode = NULL; struct pxafb_mode_info *modelist = mach->modes; unsigned int best_x = 0xffffffff, best_y = 0xffffffff; unsigned int i; - for (i = 0 ; i < mach->num_modes ; i++) { - if (modelist[i].xres >= var->xres && modelist[i].yres >= var->yres && - modelist[i].xres < best_x && modelist[i].yres < best_y && - modelist[i].bpp >= var->bits_per_pixel ) { + for (i = 0; i < mach->num_modes; i++) { + if (modelist[i].xres >= var->xres && + modelist[i].yres >= var->yres && + modelist[i].xres < best_x && + modelist[i].yres < best_y && + modelist[i].bpp >= var->bits_per_pixel) { best_x = modelist[i].xres; best_y = modelist[i].yres; mode = &modelist[i]; @@ -257,7 +275,8 @@ static struct pxafb_mode_info *pxafb_getmode(struct pxafb_mach_info *mach, struc return mode; } -static void pxafb_setmode(struct fb_var_screeninfo *var, struct pxafb_mode_info *mode) +static void pxafb_setmode(struct fb_var_screeninfo *var, + struct pxafb_mode_info *mode) { var->xres = mode->xres; var->yres = mode->yres; @@ -315,19 +334,20 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->yres_virtual = max(var->yres_virtual, var->yres); - /* + /* * Setup the RGB parameters for this display. * * The pixel packing format is described on page 7-11 of the * PXA2XX Developer's Manual. - */ + */ if (var->bits_per_pixel == 16) { var->red.offset = 11; var->red.length = 5; var->green.offset = 5; var->green.length = 6; var->blue.offset = 0; var->blue.length = 5; var->transp.offset = var->transp.length = 0; } else { - var->red.offset = var->green.offset = var->blue.offset = var->transp.offset = 0; + var->red.offset = var->green.offset = 0; + var->blue.offset = var->transp.offset = 0; var->red.length = 8; var->green.length = 8; var->blue.length = 8; @@ -345,8 +365,7 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) static inline void pxafb_set_truecolor(u_int is_true_color) { - pr_debug("pxafb: true_color = %d\n", is_true_color); - // do your machine-specific setup if needed + /* do your machine-specific setup if needed */ } /* @@ -357,9 +376,6 @@ static int pxafb_set_par(struct fb_info *info) { struct pxafb_info *fbi = (struct pxafb_info *)info; struct fb_var_screeninfo *var = &info->var; - unsigned long palette_mem_size; - - pr_debug("pxafb: set_par\n"); if (var->bits_per_pixel == 16) fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; @@ -379,17 +395,10 @@ static int pxafb_set_par(struct fb_info *info) if (var->bits_per_pixel == 16) fbi->palette_size = 0; else - fbi->palette_size = var->bits_per_pixel == 1 ? 4 : 1 << var->bits_per_pixel; - - if ((fbi->lccr4 & LCCR4_PAL_FOR_MASK) == LCCR4_PAL_FOR_0) - palette_mem_size = fbi->palette_size * sizeof(u16); - else - palette_mem_size = fbi->palette_size * sizeof(u32); - - pr_debug("pxafb: palette_mem_size = 0x%08lx\n", palette_mem_size); + fbi->palette_size = var->bits_per_pixel == 1 ? + 4 : 1 << var->bits_per_pixel; - fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size); - fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size; + fbi->palette_cpu = (u16 *)&fbi->dma_buff->palette[0]; /* * Set (any) board control register to handle new color depth @@ -407,36 +416,6 @@ static int pxafb_set_par(struct fb_info *info) } /* - * Formal definition of the VESA spec: - * On - * This refers to the state of the display when it is in full operation - * Stand-By - * This defines an optional operating state of minimal power reduction with - * the shortest recovery time - * Suspend - * This refers to a level of power management in which substantial power - * reduction is achieved by the display. The display can have a longer - * recovery time from this state than from the Stand-by state - * Off - * This indicates that the display is consuming the lowest level of power - * and is non-operational. Recovery from this state may optionally require - * the user to manually power on the monitor - * - * Now, the fbdev driver adds an additional state, (blank), where they - * turn off the video (maybe by colormap tricks), but don't mess with the - * video itself: think of it semantically between on and Stand-By. - * - * So here's what we should do in our fbdev blank routine: - * - * VESA_NO_BLANKING (mode 0) Video on, front/back light on - * VESA_VSYNC_SUSPEND (mode 1) Video on, front/back light off - * VESA_HSYNC_SUSPEND (mode 2) Video on, front/back light off - * VESA_POWERDOWN (mode 3) Video off, front/back light off - * - * This will match the matrox implementation. - */ - -/* * pxafb_blank(): * Blank the display by setting all palette values to zero. Note, the * 16 bpp mode does not really use the palette, so this will not @@ -447,8 +426,6 @@ static int pxafb_blank(int blank, struct fb_info *info) struct pxafb_info *fbi = (struct pxafb_info *)info; int i; - pr_debug("pxafb: blank=%d\n", blank); - switch (blank) { case FB_BLANK_POWERDOWN: case FB_BLANK_VSYNC_SUSPEND: @@ -460,11 +437,11 @@ static int pxafb_blank(int blank, struct fb_info *info) pxafb_setpalettereg(i, 0, 0, 0, 0, info); pxafb_schedule_work(fbi, C_DISABLE); - //TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); + /* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */ break; case FB_BLANK_UNBLANK: - //TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); + /* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */ if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR || fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) fb_set_cmap(&fbi->fb.cmap, info); @@ -480,7 +457,7 @@ static int pxafb_mmap(struct fb_info *info, unsigned long off = vma->vm_pgoff << PAGE_SHIFT; if (off < info->fix.smem_len) { - vma->vm_pgoff += 1; + vma->vm_pgoff += fbi->video_offset / PAGE_SIZE; return dma_mmap_writecombine(fbi->dev, vma, fbi->map_cpu, fbi->map_dma, fbi->map_size); } @@ -529,7 +506,8 @@ static struct fb_ops pxafb_ops = { * * Factoring the 10^4 and 10^-12 out gives 10^-8 == 1 / 100000000 as used below. */ -static inline unsigned int get_pcd(struct pxafb_info *fbi, unsigned int pixclock) +static inline unsigned int get_pcd(struct pxafb_info *fbi, + unsigned int pixclock) { unsigned long long pcd; @@ -555,7 +533,7 @@ static inline void set_hsync_time(struct pxafb_info *fbi, unsigned int pcd) unsigned long htime; if ((pcd == 0) || (fbi->fb.var.hsync_len == 0)) { - fbi->hsync_time=0; + fbi->hsync_time = 0; return; } @@ -576,71 +554,231 @@ unsigned long pxafb_get_hsync_time(struct device *dev) } EXPORT_SYMBOL(pxafb_get_hsync_time); -/* - * pxafb_activate_var(): - * Configures LCD Controller based on entries in var parameter. Settings are - * only written to the controller if changes were made. - */ -static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *fbi) +static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, + unsigned int offset, size_t size) { - struct pxafb_lcd_reg new_regs; - u_long flags; - u_int lines_per_panel, pcd = get_pcd(fbi, var->pixclock); + struct pxafb_dma_descriptor *dma_desc, *pal_desc; + unsigned int dma_desc_off, pal_desc_off; - pr_debug("pxafb: Configuring PXA LCD\n"); + if (dma < 0 || dma >= DMA_MAX) + return -EINVAL; - pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n", - var->xres, var->hsync_len, - var->left_margin, var->right_margin); - pr_debug("var: yres=%d vslen=%d um=%d bm=%d\n", - var->yres, var->vsync_len, - var->upper_margin, var->lower_margin); - pr_debug("var: pixclock=%d pcd=%d\n", var->pixclock, pcd); + dma_desc = &fbi->dma_buff->dma_desc[dma]; + dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]); -#if DEBUG_VAR - if (var->xres < 16 || var->xres > 1024) - printk(KERN_ERR "%s: invalid xres %d\n", - fbi->fb.fix.id, var->xres); - switch(var->bits_per_pixel) { - case 1: - case 2: - case 4: - case 8: - case 16: - break; - default: - printk(KERN_ERR "%s: invalid bit depth %d\n", - fbi->fb.fix.id, var->bits_per_pixel); - break; + dma_desc->fsadr = fbi->screen_dma + offset; + dma_desc->fidr = 0; + dma_desc->ldcmd = size; + + if (pal < 0 || pal >= PAL_MAX) { + dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off; + fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off; + } else { + pal_desc = &fbi->dma_buff->pal_desc[dma]; + pal_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[pal]); + + pal_desc->fsadr = fbi->dma_buff_phys + pal * PALETTE_SIZE; + pal_desc->fidr = 0; + + if ((fbi->lccr4 & LCCR4_PAL_FOR_MASK) == LCCR4_PAL_FOR_0) + pal_desc->ldcmd = fbi->palette_size * sizeof(u16); + else + pal_desc->ldcmd = fbi->palette_size * sizeof(u32); + + pal_desc->ldcmd |= LDCMD_PAL; + + /* flip back and forth between palette and frame buffer */ + pal_desc->fdadr = fbi->dma_buff_phys + dma_desc_off; + dma_desc->fdadr = fbi->dma_buff_phys + pal_desc_off; + fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off; } - if (var->hsync_len < 1 || var->hsync_len > 64) - printk(KERN_ERR "%s: invalid hsync_len %d\n", - fbi->fb.fix.id, var->hsync_len); - if (var->left_margin < 1 || var->left_margin > 255) - printk(KERN_ERR "%s: invalid left_margin %d\n", - fbi->fb.fix.id, var->left_margin); - if (var->right_margin < 1 || var->right_margin > 255) - printk(KERN_ERR "%s: invalid right_margin %d\n", - fbi->fb.fix.id, var->right_margin); - if (var->yres < 1 || var->yres > 1024) - printk(KERN_ERR "%s: invalid yres %d\n", - fbi->fb.fix.id, var->yres); - if (var->vsync_len < 1 || var->vsync_len > 64) - printk(KERN_ERR "%s: invalid vsync_len %d\n", - fbi->fb.fix.id, var->vsync_len); - if (var->upper_margin < 0 || var->upper_margin > 255) - printk(KERN_ERR "%s: invalid upper_margin %d\n", - fbi->fb.fix.id, var->upper_margin); - if (var->lower_margin < 0 || var->lower_margin > 255) - printk(KERN_ERR "%s: invalid lower_margin %d\n", - fbi->fb.fix.id, var->lower_margin); -#endif - new_regs.lccr0 = fbi->lccr0 | - (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | - LCCR0_QDM | LCCR0_BM | LCCR0_OUM); + return 0; +} + +#ifdef CONFIG_FB_PXA_SMARTPANEL +static int setup_smart_dma(struct pxafb_info *fbi) +{ + struct pxafb_dma_descriptor *dma_desc; + unsigned long dma_desc_off, cmd_buff_off; + + dma_desc = &fbi->dma_buff->dma_desc[DMA_CMD]; + dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[DMA_CMD]); + cmd_buff_off = offsetof(struct pxafb_dma_buff, cmd_buff); + + dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off; + dma_desc->fsadr = fbi->dma_buff_phys + cmd_buff_off; + dma_desc->fidr = 0; + dma_desc->ldcmd = fbi->n_smart_cmds * sizeof(uint16_t); + + fbi->fdadr[DMA_CMD] = dma_desc->fdadr; + return 0; +} + +int pxafb_smart_flush(struct fb_info *info) +{ + struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); + uint32_t prsr; + int ret = 0; + + /* disable controller until all registers are set up */ + lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB); + + /* 1. make it an even number of commands to align on 32-bit boundary + * 2. add the interrupt command to the end of the chain so we can + * keep track of the end of the transfer + */ + + while (fbi->n_smart_cmds & 1) + fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_NOOP; + + fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_INTERRUPT; + fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_WAIT_FOR_VSYNC; + setup_smart_dma(fbi); + + /* continue to execute next command */ + prsr = lcd_readl(fbi, PRSR) | PRSR_ST_OK | PRSR_CON_NT; + lcd_writel(fbi, PRSR, prsr); + + /* stop the processor in case it executed "wait for sync" cmd */ + lcd_writel(fbi, CMDCR, 0x0001); + + /* don't send interrupts for fifo underruns on channel 6 */ + lcd_writel(fbi, LCCR5, LCCR5_IUM(6)); + + lcd_writel(fbi, LCCR1, fbi->reg_lccr1); + lcd_writel(fbi, LCCR2, fbi->reg_lccr2); + lcd_writel(fbi, LCCR3, fbi->reg_lccr3); + lcd_writel(fbi, FDADR0, fbi->fdadr[0]); + lcd_writel(fbi, FDADR6, fbi->fdadr[6]); + + /* begin sending */ + lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB); + + if (wait_for_completion_timeout(&fbi->command_done, HZ/2) == 0) { + pr_warning("%s: timeout waiting for command done\n", + __func__); + ret = -ETIMEDOUT; + } + + /* quick disable */ + prsr = lcd_readl(fbi, PRSR) & ~(PRSR_ST_OK | PRSR_CON_NT); + lcd_writel(fbi, PRSR, prsr); + lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB); + lcd_writel(fbi, FDADR6, 0); + fbi->n_smart_cmds = 0; + return ret; +} + +int pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds) +{ + int i; + struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); + + /* leave 2 commands for INTERRUPT and WAIT_FOR_SYNC */ + for (i = 0; i < n_cmds; i++) { + if (fbi->n_smart_cmds == CMD_BUFF_SIZE - 8) + pxafb_smart_flush(info); + + fbi->smart_cmds[fbi->n_smart_cmds++] = *cmds++; + } + + return 0; +} + +static unsigned int __smart_timing(unsigned time_ns, unsigned long lcd_clk) +{ + unsigned int t = (time_ns * (lcd_clk / 1000000) / 1000); + return (t == 0) ? 1 : t; +} + +static void setup_smart_timing(struct pxafb_info *fbi, + struct fb_var_screeninfo *var) +{ + struct pxafb_mach_info *inf = fbi->dev->platform_data; + struct pxafb_mode_info *mode = &inf->modes[0]; + unsigned long lclk = clk_get_rate(fbi->clk); + unsigned t1, t2, t3, t4; + + t1 = max(mode->a0csrd_set_hld, mode->a0cswr_set_hld); + t2 = max(mode->rd_pulse_width, mode->wr_pulse_width); + t3 = mode->op_hold_time; + t4 = mode->cmd_inh_time; + + fbi->reg_lccr1 = + LCCR1_DisWdth(var->xres) | + LCCR1_BegLnDel(__smart_timing(t1, lclk)) | + LCCR1_EndLnDel(__smart_timing(t2, lclk)) | + LCCR1_HorSnchWdth(__smart_timing(t3, lclk)); + + fbi->reg_lccr2 = LCCR2_DisHght(var->yres); + fbi->reg_lccr3 = LCCR3_PixClkDiv(__smart_timing(t4, lclk)); + + /* FIXME: make this configurable */ + fbi->reg_cmdcr = 1; +} + +static int pxafb_smart_thread(void *arg) +{ + struct pxafb_info *fbi = arg; + struct pxafb_mach_info *inf = fbi->dev->platform_data; + + if (!fbi || !inf->smart_update) { + pr_err("%s: not properly initialized, thread terminated\n", + __func__); + return -EINVAL; + } - new_regs.lccr1 = + pr_debug("%s(): task starting\n", __func__); + + set_freezable(); + while (!kthread_should_stop()) { + + if (try_to_freeze()) + continue; + + if (fbi->state == C_ENABLE) { + inf->smart_update(&fbi->fb); + complete(&fbi->refresh_done); + } + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(30 * HZ / 1000); + } + + pr_debug("%s(): task ending\n", __func__); + return 0; +} + +static int pxafb_smart_init(struct pxafb_info *fbi) +{ + fbi->smart_thread = kthread_run(pxafb_smart_thread, fbi, + "lcd_refresh"); + if (IS_ERR(fbi->smart_thread)) { + printk(KERN_ERR "%s: unable to create kernel thread\n", + __func__); + return PTR_ERR(fbi->smart_thread); + } + return 0; +} +#else +int pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds) +{ + return 0; +} + +int pxafb_smart_flush(struct fb_info *info) +{ + return 0; +} +#endif /* CONFIG_FB_SMART_PANEL */ + +static void setup_parallel_timing(struct pxafb_info *fbi, + struct fb_var_screeninfo *var) +{ + unsigned int lines_per_panel, pcd = get_pcd(fbi, var->pixclock); + + fbi->reg_lccr1 = LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(var->hsync_len) + LCCR1_BegLnDel(var->left_margin) + @@ -654,110 +792,118 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info * if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual) lines_per_panel /= 2; - new_regs.lccr2 = + fbi->reg_lccr2 = LCCR2_DisHght(lines_per_panel) + LCCR2_VrtSnchWdth(var->vsync_len) + LCCR2_BegFrmDel(var->upper_margin) + LCCR2_EndFrmDel(var->lower_margin); - new_regs.lccr3 = fbi->lccr3 | - pxafb_bpp_to_lccr3(var) | - (var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) | - (var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL); + fbi->reg_lccr3 = fbi->lccr3 | + (var->sync & FB_SYNC_HOR_HIGH_ACT ? + LCCR3_HorSnchH : LCCR3_HorSnchL) | + (var->sync & FB_SYNC_VERT_HIGH_ACT ? + LCCR3_VrtSnchH : LCCR3_VrtSnchL); + + if (pcd) { + fbi->reg_lccr3 |= LCCR3_PixClkDiv(pcd); + set_hsync_time(fbi, pcd); + } +} - if (pcd) - new_regs.lccr3 |= LCCR3_PixClkDiv(pcd); +/* + * pxafb_activate_var(): + * Configures LCD Controller based on entries in var parameter. + * Settings are only written to the controller if changes were made. + */ +static int pxafb_activate_var(struct fb_var_screeninfo *var, + struct pxafb_info *fbi) +{ + u_long flags; + size_t nbytes; - pr_debug("nlccr0 = 0x%08x\n", new_regs.lccr0); - pr_debug("nlccr1 = 0x%08x\n", new_regs.lccr1); - pr_debug("nlccr2 = 0x%08x\n", new_regs.lccr2); - pr_debug("nlccr3 = 0x%08x\n", new_regs.lccr3); +#if DEBUG_VAR + if (!(fbi->lccr0 & LCCR0_LCDT)) { + if (var->xres < 16 || var->xres > 1024) + printk(KERN_ERR "%s: invalid xres %d\n", + fbi->fb.fix.id, var->xres); + switch (var->bits_per_pixel) { + case 1: + case 2: + case 4: + case 8: + case 16: + break; + default: + printk(KERN_ERR "%s: invalid bit depth %d\n", + fbi->fb.fix.id, var->bits_per_pixel); + break; + } + if (var->hsync_len < 1 || var->hsync_len > 64) + printk(KERN_ERR "%s: invalid hsync_len %d\n", + fbi->fb.fix.id, var->hsync_len); + if (var->left_margin < 1 || var->left_margin > 255) + printk(KERN_ERR "%s: invalid left_margin %d\n", + fbi->fb.fix.id, var->left_margin); + if (var->right_margin < 1 || var->right_margin > 255) + printk(KERN_ERR "%s: invalid right_margin %d\n", + fbi->fb.fix.id, var->right_margin); + if (var->yres < 1 || var->yres > 1024) + printk(KERN_ERR "%s: invalid yres %d\n", + fbi->fb.fix.id, var->yres); + if (var->vsync_len < 1 || var->vsync_len > 64) + printk(KERN_ERR "%s: invalid vsync_len %d\n", + fbi->fb.fix.id, var->vsync_len); + if (var->upper_margin < 0 || var->upper_margin > 255) + printk(KERN_ERR "%s: invalid upper_margin %d\n", + fbi->fb.fix.id, var->upper_margin); + if (var->lower_margin < 0 || var->lower_margin > 255) + printk(KERN_ERR "%s: invalid lower_margin %d\n", + fbi->fb.fix.id, var->lower_margin); + } +#endif /* Update shadow copy atomically */ local_irq_save(flags); - /* setup dma descriptors */ - fbi->dmadesc_fblow_cpu = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette_cpu - 3*16); - fbi->dmadesc_fbhigh_cpu = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette_cpu - 2*16); - fbi->dmadesc_palette_cpu = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette_cpu - 1*16); - - fbi->dmadesc_fblow_dma = fbi->palette_dma - 3*16; - fbi->dmadesc_fbhigh_dma = fbi->palette_dma - 2*16; - fbi->dmadesc_palette_dma = fbi->palette_dma - 1*16; - -#define BYTES_PER_PANEL (lines_per_panel * fbi->fb.fix.line_length) - - /* populate descriptors */ - fbi->dmadesc_fblow_cpu->fdadr = fbi->dmadesc_fblow_dma; - fbi->dmadesc_fblow_cpu->fsadr = fbi->screen_dma + BYTES_PER_PANEL; - fbi->dmadesc_fblow_cpu->fidr = 0; - fbi->dmadesc_fblow_cpu->ldcmd = BYTES_PER_PANEL; +#ifdef CONFIG_FB_PXA_SMARTPANEL + if (fbi->lccr0 & LCCR0_LCDT) + setup_smart_timing(fbi, var); + else +#endif + setup_parallel_timing(fbi, var); - fbi->fdadr1 = fbi->dmadesc_fblow_dma; /* only used in dual-panel mode */ + fbi->reg_lccr0 = fbi->lccr0 | + (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | + LCCR0_QDM | LCCR0_BM | LCCR0_OUM); - fbi->dmadesc_fbhigh_cpu->fsadr = fbi->screen_dma; - fbi->dmadesc_fbhigh_cpu->fidr = 0; - fbi->dmadesc_fbhigh_cpu->ldcmd = BYTES_PER_PANEL; + fbi->reg_lccr3 |= pxafb_bpp_to_lccr3(var); - fbi->dmadesc_palette_cpu->fsadr = fbi->palette_dma; - fbi->dmadesc_palette_cpu->fidr = 0; - if ((fbi->lccr4 & LCCR4_PAL_FOR_MASK) == LCCR4_PAL_FOR_0) - fbi->dmadesc_palette_cpu->ldcmd = fbi->palette_size * - sizeof(u16); - else - fbi->dmadesc_palette_cpu->ldcmd = fbi->palette_size * - sizeof(u32); - fbi->dmadesc_palette_cpu->ldcmd |= LDCMD_PAL; + nbytes = var->yres * fbi->fb.fix.line_length; - if (var->bits_per_pixel == 16) { - /* palette shouldn't be loaded in true-color mode */ - fbi->dmadesc_fbhigh_cpu->fdadr = fbi->dmadesc_fbhigh_dma; - fbi->fdadr0 = fbi->dmadesc_fbhigh_dma; /* no pal just fbhigh */ - /* init it to something, even though we won't be using it */ - fbi->dmadesc_palette_cpu->fdadr = fbi->dmadesc_palette_dma; - } else { - fbi->dmadesc_palette_cpu->fdadr = fbi->dmadesc_fbhigh_dma; - fbi->dmadesc_fbhigh_cpu->fdadr = fbi->dmadesc_palette_dma; - fbi->fdadr0 = fbi->dmadesc_palette_dma; /* flips back and forth between pal and fbhigh */ + if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual) { + nbytes = nbytes / 2; + setup_frame_dma(fbi, DMA_LOWER, PAL_NONE, nbytes, nbytes); } -#if 0 - pr_debug("fbi->dmadesc_fblow_cpu = 0x%p\n", fbi->dmadesc_fblow_cpu); - pr_debug("fbi->dmadesc_fbhigh_cpu = 0x%p\n", fbi->dmadesc_fbhigh_cpu); - pr_debug("fbi->dmadesc_palette_cpu = 0x%p\n", fbi->dmadesc_palette_cpu); - pr_debug("fbi->dmadesc_fblow_dma = 0x%x\n", fbi->dmadesc_fblow_dma); - pr_debug("fbi->dmadesc_fbhigh_dma = 0x%x\n", fbi->dmadesc_fbhigh_dma); - pr_debug("fbi->dmadesc_palette_dma = 0x%x\n", fbi->dmadesc_palette_dma); - - pr_debug("fbi->dmadesc_fblow_cpu->fdadr = 0x%x\n", fbi->dmadesc_fblow_cpu->fdadr); - pr_debug("fbi->dmadesc_fbhigh_cpu->fdadr = 0x%x\n", fbi->dmadesc_fbhigh_cpu->fdadr); - pr_debug("fbi->dmadesc_palette_cpu->fdadr = 0x%x\n", fbi->dmadesc_palette_cpu->fdadr); - - pr_debug("fbi->dmadesc_fblow_cpu->fsadr = 0x%x\n", fbi->dmadesc_fblow_cpu->fsadr); - pr_debug("fbi->dmadesc_fbhigh_cpu->fsadr = 0x%x\n", fbi->dmadesc_fbhigh_cpu->fsadr); - pr_debug("fbi->dmadesc_palette_cpu->fsadr = 0x%x\n", fbi->dmadesc_palette_cpu->fsadr); - - pr_debug("fbi->dmadesc_fblow_cpu->ldcmd = 0x%x\n", fbi->dmadesc_fblow_cpu->ldcmd); - pr_debug("fbi->dmadesc_fbhigh_cpu->ldcmd = 0x%x\n", fbi->dmadesc_fbhigh_cpu->ldcmd); - pr_debug("fbi->dmadesc_palette_cpu->ldcmd = 0x%x\n", fbi->dmadesc_palette_cpu->ldcmd); -#endif + if ((var->bits_per_pixel >= 16) || (fbi->lccr0 & LCCR0_LCDT)) + setup_frame_dma(fbi, DMA_BASE, PAL_NONE, 0, nbytes); + else + setup_frame_dma(fbi, DMA_BASE, PAL_BASE, 0, nbytes); - fbi->reg_lccr0 = new_regs.lccr0; - fbi->reg_lccr1 = new_regs.lccr1; - fbi->reg_lccr2 = new_regs.lccr2; - fbi->reg_lccr3 = new_regs.lccr3; - fbi->reg_lccr4 = LCCR4 & (~LCCR4_PAL_FOR_MASK); + fbi->reg_lccr4 = lcd_readl(fbi, LCCR4) & ~LCCR4_PAL_FOR_MASK; fbi->reg_lccr4 |= (fbi->lccr4 & LCCR4_PAL_FOR_MASK); - set_hsync_time(fbi, pcd); local_irq_restore(flags); /* * Only update the registers if the controller is enabled * and something has changed. */ - if ((LCCR0 != fbi->reg_lccr0) || (LCCR1 != fbi->reg_lccr1) || - (LCCR2 != fbi->reg_lccr2) || (LCCR3 != fbi->reg_lccr3) || - (FDADR0 != fbi->fdadr0) || (FDADR1 != fbi->fdadr1)) + if ((lcd_readl(fbi, LCCR0) != fbi->reg_lccr0) || + (lcd_readl(fbi, LCCR1) != fbi->reg_lccr1) || + (lcd_readl(fbi, LCCR2) != fbi->reg_lccr2) || + (lcd_readl(fbi, LCCR3) != fbi->reg_lccr3) || + (lcd_readl(fbi, FDADR0) != fbi->fdadr[0]) || + (lcd_readl(fbi, FDADR1) != fbi->fdadr[1])) pxafb_schedule_work(fbi, C_REENABLE); return 0; @@ -773,8 +919,8 @@ static inline void __pxafb_backlight_power(struct pxafb_info *fbi, int on) { pr_debug("pxafb: backlight o%s\n", on ? "n" : "ff"); - if (pxafb_backlight_power) - pxafb_backlight_power(on); + if (pxafb_backlight_power) + pxafb_backlight_power(on); } static inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on) @@ -788,11 +934,11 @@ static inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on) static void pxafb_setup_gpio(struct pxafb_info *fbi) { int gpio, ldd_bits; - unsigned int lccr0 = fbi->lccr0; + unsigned int lccr0 = fbi->lccr0; /* * setup is based on type of panel supported - */ + */ /* 4 bit interface */ if ((lccr0 & LCCR0_CMS) == LCCR0_Mono && @@ -801,21 +947,25 @@ static void pxafb_setup_gpio(struct pxafb_info *fbi) ldd_bits = 4; /* 8 bit interface */ - else if (((lccr0 & LCCR0_CMS) == LCCR0_Mono && - ((lccr0 & LCCR0_SDS) == LCCR0_Dual || (lccr0 & LCCR0_DPD) == LCCR0_8PixMono)) || - ((lccr0 & LCCR0_CMS) == LCCR0_Color && - (lccr0 & LCCR0_PAS) == LCCR0_Pas && (lccr0 & LCCR0_SDS) == LCCR0_Sngl)) + else if (((lccr0 & LCCR0_CMS) == LCCR0_Mono && + ((lccr0 & LCCR0_SDS) == LCCR0_Dual || + (lccr0 & LCCR0_DPD) == LCCR0_8PixMono)) || + ((lccr0 & LCCR0_CMS) == LCCR0_Color && + (lccr0 & LCCR0_PAS) == LCCR0_Pas && + (lccr0 & LCCR0_SDS) == LCCR0_Sngl)) ldd_bits = 8; /* 16 bit interface */ else if ((lccr0 & LCCR0_CMS) == LCCR0_Color && - ((lccr0 & LCCR0_SDS) == LCCR0_Dual || (lccr0 & LCCR0_PAS) == LCCR0_Act)) + ((lccr0 & LCCR0_SDS) == LCCR0_Dual || + (lccr0 & LCCR0_PAS) == LCCR0_Act)) ldd_bits = 16; else { - printk(KERN_ERR "pxafb_setup_gpio: unable to determine bits per pixel\n"); + printk(KERN_ERR "pxafb_setup_gpio: unable to determine " + "bits per pixel\n"); return; - } + } for (gpio = 58; ldd_bits; gpio++, ldd_bits--) pxa_gpio_mode(gpio | GPIO_ALT_FN_2_OUT); @@ -828,8 +978,8 @@ static void pxafb_setup_gpio(struct pxafb_info *fbi) static void pxafb_enable_controller(struct pxafb_info *fbi) { pr_debug("pxafb: Enabling LCD controller\n"); - pr_debug("fdadr0 0x%08x\n", (unsigned int) fbi->fdadr0); - pr_debug("fdadr1 0x%08x\n", (unsigned int) fbi->fdadr1); + pr_debug("fdadr0 0x%08x\n", (unsigned int) fbi->fdadr[0]); + pr_debug("fdadr1 0x%08x\n", (unsigned int) fbi->fdadr[1]); pr_debug("reg_lccr0 0x%08x\n", (unsigned int) fbi->reg_lccr0); pr_debug("reg_lccr1 0x%08x\n", (unsigned int) fbi->reg_lccr1); pr_debug("reg_lccr2 0x%08x\n", (unsigned int) fbi->reg_lccr2); @@ -838,40 +988,40 @@ static void pxafb_enable_controller(struct pxafb_info *fbi) /* enable LCD controller clock */ clk_enable(fbi->clk); + if (fbi->lccr0 & LCCR0_LCDT) + return; + /* Sequence from 11.7.10 */ - LCCR3 = fbi->reg_lccr3; - LCCR2 = fbi->reg_lccr2; - LCCR1 = fbi->reg_lccr1; - LCCR0 = fbi->reg_lccr0 & ~LCCR0_ENB; - - FDADR0 = fbi->fdadr0; - FDADR1 = fbi->fdadr1; - LCCR0 |= LCCR0_ENB; - - pr_debug("FDADR0 0x%08x\n", (unsigned int) FDADR0); - pr_debug("FDADR1 0x%08x\n", (unsigned int) FDADR1); - pr_debug("LCCR0 0x%08x\n", (unsigned int) LCCR0); - pr_debug("LCCR1 0x%08x\n", (unsigned int) LCCR1); - pr_debug("LCCR2 0x%08x\n", (unsigned int) LCCR2); - pr_debug("LCCR3 0x%08x\n", (unsigned int) LCCR3); - pr_debug("LCCR4 0x%08x\n", (unsigned int) LCCR4); + lcd_writel(fbi, LCCR3, fbi->reg_lccr3); + lcd_writel(fbi, LCCR2, fbi->reg_lccr2); + lcd_writel(fbi, LCCR1, fbi->reg_lccr1); + lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB); + + lcd_writel(fbi, FDADR0, fbi->fdadr[0]); + lcd_writel(fbi, FDADR1, fbi->fdadr[1]); + lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB); } static void pxafb_disable_controller(struct pxafb_info *fbi) { - DECLARE_WAITQUEUE(wait, current); + uint32_t lccr0; - pr_debug("pxafb: disabling LCD controller\n"); +#ifdef CONFIG_FB_PXA_SMARTPANEL + if (fbi->lccr0 & LCCR0_LCDT) { + wait_for_completion_timeout(&fbi->refresh_done, + 200 * HZ / 1000); + return; + } +#endif - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&fbi->ctrlr_wait, &wait); + /* Clear LCD Status Register */ + lcd_writel(fbi, LCSR, 0xffffffff); - LCSR = 0xffffffff; /* Clear LCD Status Register */ - LCCR0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */ - LCCR0 |= LCCR0_DIS; /* Disable LCD Controller */ + lccr0 = lcd_readl(fbi, LCCR0) & ~LCCR0_LDM; + lcd_writel(fbi, LCCR0, lccr0); + lcd_writel(fbi, LCCR0, lccr0 | LCCR0_DIS); - schedule_timeout(200 * HZ / 1000); - remove_wait_queue(&fbi->ctrlr_wait, &wait); + wait_for_completion_timeout(&fbi->disable_done, 200 * HZ / 1000); /* disable LCD controller clock */ clk_disable(fbi->clk); @@ -883,14 +1033,20 @@ static void pxafb_disable_controller(struct pxafb_info *fbi) static irqreturn_t pxafb_handle_irq(int irq, void *dev_id) { struct pxafb_info *fbi = dev_id; - unsigned int lcsr = LCSR; + unsigned int lccr0, lcsr = lcd_readl(fbi, LCSR); if (lcsr & LCSR_LDD) { - LCCR0 |= LCCR0_LDM; - wake_up(&fbi->ctrlr_wait); + lccr0 = lcd_readl(fbi, LCCR0); + lcd_writel(fbi, LCCR0, lccr0 | LCCR0_LDM); + complete(&fbi->disable_done); } - LCSR = lcsr; +#ifdef CONFIG_FB_PXA_SMARTPANEL + if (lcsr & LCSR_CMD_INT) + complete(&fbi->command_done); +#endif + + lcd_writel(fbi, LCSR, lcsr); return IRQ_HANDLED; } @@ -921,7 +1077,7 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state) */ if (old_state != C_DISABLE && old_state != C_DISABLE_PM) { fbi->state = state; - //TODO __pxafb_lcd_power(fbi, 0); + /* TODO __pxafb_lcd_power(fbi, 0); */ pxafb_disable_controller(fbi); } break; @@ -948,7 +1104,7 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state) if (old_state == C_DISABLE_CLKCHANGE) { fbi->state = C_ENABLE; pxafb_enable_controller(fbi); - //TODO __pxafb_lcd_power(fbi, 1); + /* TODO __pxafb_lcd_power(fbi, 1); */ } break; @@ -1019,7 +1175,7 @@ static int pxafb_freq_transition(struct notifier_block *nb, unsigned long val, void *data) { struct pxafb_info *fbi = TO_INF(nb, freq_transition); - //TODO struct cpufreq_freqs *f = data; + /* TODO struct cpufreq_freqs *f = data; */ u_int pcd; switch (val) { @@ -1030,7 +1186,8 @@ pxafb_freq_transition(struct notifier_block *nb, unsigned long val, void *data) case CPUFREQ_POSTCHANGE: pcd = get_pcd(fbi, fbi->fb.var.pixclock); set_hsync_time(fbi, pcd); - fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd); + fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | + LCCR3_PixClkDiv(pcd); set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE); break; } @@ -1050,18 +1207,8 @@ pxafb_freq_policy(struct notifier_block *nb, unsigned long val, void *data) pr_debug("min dma period: %d ps, " "new clock %d kHz\n", pxafb_display_dma_period(var), policy->max); - // TODO: fill in min/max values - break; -#if 0 - case CPUFREQ_NOTIFY: - printk(KERN_ERR "%s: got CPUFREQ_NOTIFY\n", __FUNCTION__); - do {} while(0); - /* todo: panic if min/max values aren't fulfilled - * [can't really happen unless there's a bug in the - * CPU policy verification process * - */ + /* TODO: fill in min/max values */ break; -#endif } return 0; } @@ -1102,21 +1249,21 @@ static int pxafb_resume(struct platform_device *dev) */ static int __init pxafb_map_video_memory(struct pxafb_info *fbi) { - u_long palette_mem_size; - /* * We reserve one page for the palette, plus the size * of the framebuffer. */ - fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE); + fbi->video_offset = PAGE_ALIGN(sizeof(struct pxafb_dma_buff)); + fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + fbi->video_offset); fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size, &fbi->map_dma, GFP_KERNEL); if (fbi->map_cpu) { /* prevent initial garbage on screen */ memset(fbi->map_cpu, 0, fbi->map_size); - fbi->fb.screen_base = fbi->map_cpu + PAGE_SIZE; - fbi->screen_dma = fbi->map_dma + PAGE_SIZE; + fbi->fb.screen_base = fbi->map_cpu + fbi->video_offset; + fbi->screen_dma = fbi->map_dma + fbi->video_offset; + /* * FIXME: this is actually the wrong thing to place in * smem_start. But fbdev suffers from the problem that @@ -1126,27 +1273,86 @@ static int __init pxafb_map_video_memory(struct pxafb_info *fbi) fbi->fb.fix.smem_start = fbi->screen_dma; fbi->palette_size = fbi->fb.var.bits_per_pixel == 8 ? 256 : 16; - if ((fbi->lccr4 & LCCR4_PAL_FOR_MASK) == LCCR4_PAL_FOR_0) - palette_mem_size = fbi->palette_size * sizeof(u16); - else - palette_mem_size = fbi->palette_size * sizeof(u32); + fbi->dma_buff = (void *) fbi->map_cpu; + fbi->dma_buff_phys = fbi->map_dma; + fbi->palette_cpu = (u16 *) fbi->dma_buff->palette; - pr_debug("pxafb: palette_mem_size = 0x%08lx\n", palette_mem_size); - - fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size); - fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size; +#ifdef CONFIG_FB_PXA_SMARTPANEL + fbi->smart_cmds = (uint16_t *) fbi->dma_buff->cmd_buff; + fbi->n_smart_cmds = 0; +#endif } return fbi->map_cpu ? 0 : -ENOMEM; } +static void pxafb_decode_mode_info(struct pxafb_info *fbi, + struct pxafb_mode_info *modes, + unsigned int num_modes) +{ + unsigned int i, smemlen; + + pxafb_setmode(&fbi->fb.var, &modes[0]); + + for (i = 0; i < num_modes; i++) { + smemlen = modes[i].xres * modes[i].yres * modes[i].bpp / 8; + if (smemlen > fbi->fb.fix.smem_len) + fbi->fb.fix.smem_len = smemlen; + } +} + +static int pxafb_decode_mach_info(struct pxafb_info *fbi, + struct pxafb_mach_info *inf) +{ + unsigned int lcd_conn = inf->lcd_conn; + + fbi->cmap_inverse = inf->cmap_inverse; + fbi->cmap_static = inf->cmap_static; + + switch (lcd_conn & 0xf) { + case LCD_TYPE_MONO_STN: + fbi->lccr0 = LCCR0_CMS; + break; + case LCD_TYPE_MONO_DSTN: + fbi->lccr0 = LCCR0_CMS | LCCR0_SDS; + break; + case LCD_TYPE_COLOR_STN: + fbi->lccr0 = 0; + break; + case LCD_TYPE_COLOR_DSTN: + fbi->lccr0 = LCCR0_SDS; + break; + case LCD_TYPE_COLOR_TFT: + fbi->lccr0 = LCCR0_PAS; + break; + case LCD_TYPE_SMART_PANEL: + fbi->lccr0 = LCCR0_LCDT | LCCR0_PAS; + break; + default: + /* fall back to backward compatibility way */ + fbi->lccr0 = inf->lccr0; + fbi->lccr3 = inf->lccr3; + fbi->lccr4 = inf->lccr4; + return -EINVAL; + } + + if (lcd_conn == LCD_MONO_STN_8BPP) + fbi->lccr0 |= LCCR0_DPD; + + fbi->lccr3 = LCCR3_Acb((inf->lcd_conn >> 10) & 0xff); + fbi->lccr3 |= (lcd_conn & LCD_BIAS_ACTIVE_LOW) ? LCCR3_OEP : 0; + fbi->lccr3 |= (lcd_conn & LCD_PCLK_EDGE_FALL) ? LCCR3_PCP : 0; + + pxafb_decode_mode_info(fbi, inf->modes, inf->num_modes); + return 0; +} + static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev) { struct pxafb_info *fbi; void *addr; struct pxafb_mach_info *inf = dev->platform_data; struct pxafb_mode_info *mode = inf->modes; - int i, smemlen; /* Alloc the pxafb_info and pseudo_palette in one step */ fbi = kmalloc(sizeof(struct pxafb_info) + sizeof(u32) * 16, GFP_KERNEL); @@ -1186,187 +1392,233 @@ static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev) addr = addr + sizeof(struct pxafb_info); fbi->fb.pseudo_palette = addr; - pxafb_setmode(&fbi->fb.var, mode); + fbi->state = C_STARTUP; + fbi->task_state = (u_char)-1; - fbi->cmap_inverse = inf->cmap_inverse; - fbi->cmap_static = inf->cmap_static; - - fbi->lccr0 = inf->lccr0; - fbi->lccr3 = inf->lccr3; - fbi->lccr4 = inf->lccr4; - fbi->state = C_STARTUP; - fbi->task_state = (u_char)-1; - - for (i = 0; i < inf->num_modes; i++) { - smemlen = mode[i].xres * mode[i].yres * mode[i].bpp / 8; - if (smemlen > fbi->fb.fix.smem_len) - fbi->fb.fix.smem_len = smemlen; - } + pxafb_decode_mach_info(fbi, inf); init_waitqueue_head(&fbi->ctrlr_wait); INIT_WORK(&fbi->task, pxafb_task); init_MUTEX(&fbi->ctrlr_sem); + init_completion(&fbi->disable_done); +#ifdef CONFIG_FB_PXA_SMARTPANEL + init_completion(&fbi->command_done); + init_completion(&fbi->refresh_done); +#endif return fbi; } #ifdef CONFIG_FB_PXA_PARAMETERS -static int __init pxafb_parse_options(struct device *dev, char *options) +static int __init parse_opt_mode(struct device *dev, const char *this_opt) +{ + struct pxafb_mach_info *inf = dev->platform_data; + + const char *name = this_opt+5; + unsigned int namelen = strlen(name); + int res_specified = 0, bpp_specified = 0; + unsigned int xres = 0, yres = 0, bpp = 0; + int yres_specified = 0; + int i; + for (i = namelen-1; i >= 0; i--) { + switch (name[i]) { + case '-': + namelen = i; + if (!bpp_specified && !yres_specified) { + bpp = simple_strtoul(&name[i+1], NULL, 0); + bpp_specified = 1; + } else + goto done; + break; + case 'x': + if (!yres_specified) { + yres = simple_strtoul(&name[i+1], NULL, 0); + yres_specified = 1; + } else + goto done; + break; + case '0' ... '9': + break; + default: + goto done; + } + } + if (i < 0 && yres_specified) { + xres = simple_strtoul(name, NULL, 0); + res_specified = 1; + } +done: + if (res_specified) { + dev_info(dev, "overriding resolution: %dx%d\n", xres, yres); + inf->modes[0].xres = xres; inf->modes[0].yres = yres; + } + if (bpp_specified) + switch (bpp) { + case 1: + case 2: + case 4: + case 8: + case 16: + inf->modes[0].bpp = bpp; + dev_info(dev, "overriding bit depth: %d\n", bpp); + break; + default: + dev_err(dev, "Depth %d is not valid\n", bpp); + return -EINVAL; + } + return 0; +} + +static int __init parse_opt(struct device *dev, char *this_opt) { struct pxafb_mach_info *inf = dev->platform_data; + struct pxafb_mode_info *mode = &inf->modes[0]; + char s[64]; + + s[0] = '\0'; + + if (!strncmp(this_opt, "mode:", 5)) { + return parse_opt_mode(dev, this_opt); + } else if (!strncmp(this_opt, "pixclock:", 9)) { + mode->pixclock = simple_strtoul(this_opt+9, NULL, 0); + sprintf(s, "pixclock: %ld\n", mode->pixclock); + } else if (!strncmp(this_opt, "left:", 5)) { + mode->left_margin = simple_strtoul(this_opt+5, NULL, 0); + sprintf(s, "left: %u\n", mode->left_margin); + } else if (!strncmp(this_opt, "right:", 6)) { + mode->right_margin = simple_strtoul(this_opt+6, NULL, 0); + sprintf(s, "right: %u\n", mode->right_margin); + } else if (!strncmp(this_opt, "upper:", 6)) { + mode->upper_margin = simple_strtoul(this_opt+6, NULL, 0); + sprintf(s, "upper: %u\n", mode->upper_margin); + } else if (!strncmp(this_opt, "lower:", 6)) { + mode->lower_margin = simple_strtoul(this_opt+6, NULL, 0); + sprintf(s, "lower: %u\n", mode->lower_margin); + } else if (!strncmp(this_opt, "hsynclen:", 9)) { + mode->hsync_len = simple_strtoul(this_opt+9, NULL, 0); + sprintf(s, "hsynclen: %u\n", mode->hsync_len); + } else if (!strncmp(this_opt, "vsynclen:", 9)) { + mode->vsync_len = simple_strtoul(this_opt+9, NULL, 0); + sprintf(s, "vsynclen: %u\n", mode->vsync_len); + } else if (!strncmp(this_opt, "hsync:", 6)) { + if (simple_strtoul(this_opt+6, NULL, 0) == 0) { + sprintf(s, "hsync: Active Low\n"); + mode->sync &= ~FB_SYNC_HOR_HIGH_ACT; + } else { + sprintf(s, "hsync: Active High\n"); + mode->sync |= FB_SYNC_HOR_HIGH_ACT; + } + } else if (!strncmp(this_opt, "vsync:", 6)) { + if (simple_strtoul(this_opt+6, NULL, 0) == 0) { + sprintf(s, "vsync: Active Low\n"); + mode->sync &= ~FB_SYNC_VERT_HIGH_ACT; + } else { + sprintf(s, "vsync: Active High\n"); + mode->sync |= FB_SYNC_VERT_HIGH_ACT; + } + } else if (!strncmp(this_opt, "dpc:", 4)) { + if (simple_strtoul(this_opt+4, NULL, 0) == 0) { + sprintf(s, "double pixel clock: false\n"); + inf->lccr3 &= ~LCCR3_DPC; + } else { + sprintf(s, "double pixel clock: true\n"); + inf->lccr3 |= LCCR3_DPC; + } + } else if (!strncmp(this_opt, "outputen:", 9)) { + if (simple_strtoul(this_opt+9, NULL, 0) == 0) { + sprintf(s, "output enable: active low\n"); + inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnL; + } else { + sprintf(s, "output enable: active high\n"); + inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnH; + } + } else if (!strncmp(this_opt, "pixclockpol:", 12)) { + if (simple_strtoul(this_opt+12, NULL, 0) == 0) { + sprintf(s, "pixel clock polarity: falling edge\n"); + inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixFlEdg; + } else { + sprintf(s, "pixel clock polarity: rising edge\n"); + inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixRsEdg; + } + } else if (!strncmp(this_opt, "color", 5)) { + inf->lccr0 = (inf->lccr0 & ~LCCR0_CMS) | LCCR0_Color; + } else if (!strncmp(this_opt, "mono", 4)) { + inf->lccr0 = (inf->lccr0 & ~LCCR0_CMS) | LCCR0_Mono; + } else if (!strncmp(this_opt, "active", 6)) { + inf->lccr0 = (inf->lccr0 & ~LCCR0_PAS) | LCCR0_Act; + } else if (!strncmp(this_opt, "passive", 7)) { + inf->lccr0 = (inf->lccr0 & ~LCCR0_PAS) | LCCR0_Pas; + } else if (!strncmp(this_opt, "single", 6)) { + inf->lccr0 = (inf->lccr0 & ~LCCR0_SDS) | LCCR0_Sngl; + } else if (!strncmp(this_opt, "dual", 4)) { + inf->lccr0 = (inf->lccr0 & ~LCCR0_SDS) | LCCR0_Dual; + } else if (!strncmp(this_opt, "4pix", 4)) { + inf->lccr0 = (inf->lccr0 & ~LCCR0_DPD) | LCCR0_4PixMono; + } else if (!strncmp(this_opt, "8pix", 4)) { + inf->lccr0 = (inf->lccr0 & ~LCCR0_DPD) | LCCR0_8PixMono; + } else { + dev_err(dev, "unknown option: %s\n", this_opt); + return -EINVAL; + } + + if (s[0] != '\0') + dev_info(dev, "override %s", s); + + return 0; +} + +static int __init pxafb_parse_options(struct device *dev, char *options) +{ char *this_opt; + int ret; - if (!options || !*options) - return 0; + if (!options || !*options) + return 0; dev_dbg(dev, "options are \"%s\"\n", options ? options : "null"); /* could be made table driven or similar?... */ - while ((this_opt = strsep(&options, ",")) != NULL) { - if (!strncmp(this_opt, "mode:", 5)) { - const char *name = this_opt+5; - unsigned int namelen = strlen(name); - int res_specified = 0, bpp_specified = 0; - unsigned int xres = 0, yres = 0, bpp = 0; - int yres_specified = 0; - int i; - for (i = namelen-1; i >= 0; i--) { - switch (name[i]) { - case '-': - namelen = i; - if (!bpp_specified && !yres_specified) { - bpp = simple_strtoul(&name[i+1], NULL, 0); - bpp_specified = 1; - } else - goto done; - break; - case 'x': - if (!yres_specified) { - yres = simple_strtoul(&name[i+1], NULL, 0); - yres_specified = 1; - } else - goto done; - break; - case '0' ... '9': - break; - default: - goto done; - } - } - if (i < 0 && yres_specified) { - xres = simple_strtoul(name, NULL, 0); - res_specified = 1; - } - done: - if (res_specified) { - dev_info(dev, "overriding resolution: %dx%d\n", xres, yres); - inf->modes[0].xres = xres; inf->modes[0].yres = yres; - } - if (bpp_specified) - switch (bpp) { - case 1: - case 2: - case 4: - case 8: - case 16: - inf->modes[0].bpp = bpp; - dev_info(dev, "overriding bit depth: %d\n", bpp); - break; - default: - dev_err(dev, "Depth %d is not valid\n", bpp); - } - } else if (!strncmp(this_opt, "pixclock:", 9)) { - inf->modes[0].pixclock = simple_strtoul(this_opt+9, NULL, 0); - dev_info(dev, "override pixclock: %ld\n", inf->modes[0].pixclock); - } else if (!strncmp(this_opt, "left:", 5)) { - inf->modes[0].left_margin = simple_strtoul(this_opt+5, NULL, 0); - dev_info(dev, "override left: %u\n", inf->modes[0].left_margin); - } else if (!strncmp(this_opt, "right:", 6)) { - inf->modes[0].right_margin = simple_strtoul(this_opt+6, NULL, 0); - dev_info(dev, "override right: %u\n", inf->modes[0].right_margin); - } else if (!strncmp(this_opt, "upper:", 6)) { - inf->modes[0].upper_margin = simple_strtoul(this_opt+6, NULL, 0); - dev_info(dev, "override upper: %u\n", inf->modes[0].upper_margin); - } else if (!strncmp(this_opt, "lower:", 6)) { - inf->modes[0].lower_margin = simple_strtoul(this_opt+6, NULL, 0); - dev_info(dev, "override lower: %u\n", inf->modes[0].lower_margin); - } else if (!strncmp(this_opt, "hsynclen:", 9)) { - inf->modes[0].hsync_len = simple_strtoul(this_opt+9, NULL, 0); - dev_info(dev, "override hsynclen: %u\n", inf->modes[0].hsync_len); - } else if (!strncmp(this_opt, "vsynclen:", 9)) { - inf->modes[0].vsync_len = simple_strtoul(this_opt+9, NULL, 0); - dev_info(dev, "override vsynclen: %u\n", inf->modes[0].vsync_len); - } else if (!strncmp(this_opt, "hsync:", 6)) { - if (simple_strtoul(this_opt+6, NULL, 0) == 0) { - dev_info(dev, "override hsync: Active Low\n"); - inf->modes[0].sync &= ~FB_SYNC_HOR_HIGH_ACT; - } else { - dev_info(dev, "override hsync: Active High\n"); - inf->modes[0].sync |= FB_SYNC_HOR_HIGH_ACT; - } - } else if (!strncmp(this_opt, "vsync:", 6)) { - if (simple_strtoul(this_opt+6, NULL, 0) == 0) { - dev_info(dev, "override vsync: Active Low\n"); - inf->modes[0].sync &= ~FB_SYNC_VERT_HIGH_ACT; - } else { - dev_info(dev, "override vsync: Active High\n"); - inf->modes[0].sync |= FB_SYNC_VERT_HIGH_ACT; - } - } else if (!strncmp(this_opt, "dpc:", 4)) { - if (simple_strtoul(this_opt+4, NULL, 0) == 0) { - dev_info(dev, "override double pixel clock: false\n"); - inf->lccr3 &= ~LCCR3_DPC; - } else { - dev_info(dev, "override double pixel clock: true\n"); - inf->lccr3 |= LCCR3_DPC; - } - } else if (!strncmp(this_opt, "outputen:", 9)) { - if (simple_strtoul(this_opt+9, NULL, 0) == 0) { - dev_info(dev, "override output enable: active low\n"); - inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnL; - } else { - dev_info(dev, "override output enable: active high\n"); - inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnH; - } - } else if (!strncmp(this_opt, "pixclockpol:", 12)) { - if (simple_strtoul(this_opt+12, NULL, 0) == 0) { - dev_info(dev, "override pixel clock polarity: falling edge\n"); - inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixFlEdg; - } else { - dev_info(dev, "override pixel clock polarity: rising edge\n"); - inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixRsEdg; - } - } else if (!strncmp(this_opt, "color", 5)) { - inf->lccr0 = (inf->lccr0 & ~LCCR0_CMS) | LCCR0_Color; - } else if (!strncmp(this_opt, "mono", 4)) { - inf->lccr0 = (inf->lccr0 & ~LCCR0_CMS) | LCCR0_Mono; - } else if (!strncmp(this_opt, "active", 6)) { - inf->lccr0 = (inf->lccr0 & ~LCCR0_PAS) | LCCR0_Act; - } else if (!strncmp(this_opt, "passive", 7)) { - inf->lccr0 = (inf->lccr0 & ~LCCR0_PAS) | LCCR0_Pas; - } else if (!strncmp(this_opt, "single", 6)) { - inf->lccr0 = (inf->lccr0 & ~LCCR0_SDS) | LCCR0_Sngl; - } else if (!strncmp(this_opt, "dual", 4)) { - inf->lccr0 = (inf->lccr0 & ~LCCR0_SDS) | LCCR0_Dual; - } else if (!strncmp(this_opt, "4pix", 4)) { - inf->lccr0 = (inf->lccr0 & ~LCCR0_DPD) | LCCR0_4PixMono; - } else if (!strncmp(this_opt, "8pix", 4)) { - inf->lccr0 = (inf->lccr0 & ~LCCR0_DPD) | LCCR0_8PixMono; - } else { - dev_err(dev, "unknown option: %s\n", this_opt); - return -EINVAL; - } - } - return 0; + while ((this_opt = strsep(&options, ",")) != NULL) { + ret = parse_opt(dev, this_opt); + if (ret) + return ret; + } + return 0; +} + +static char g_options[256] __devinitdata = ""; +#ifndef CONFIG_MODULES +static int __devinit pxafb_setup_options(void) +{ + char *options = NULL; + + if (fb_get_options("pxafb", &options)) + return -ENODEV; + + if (options) + strlcpy(g_options, options, sizeof(g_options)); + + return 0; } +#else +#define pxafb_setup_options() (0) + +module_param_string(options, g_options, sizeof(g_options), 0); +MODULE_PARM_DESC(options, "LCD parameters (see Documentation/fb/pxafb.txt)"); +#endif + +#else +#define pxafb_parse_options(...) (0) +#define pxafb_setup_options() (0) #endif static int __init pxafb_probe(struct platform_device *dev) { struct pxafb_info *fbi; struct pxafb_mach_info *inf; - int ret; + struct resource *r; + int irq, ret; dev_dbg(&dev->dev, "pxafb_probe\n"); @@ -1376,38 +1628,45 @@ static int __init pxafb_probe(struct platform_device *dev) if (!inf) goto failed; -#ifdef CONFIG_FB_PXA_PARAMETERS ret = pxafb_parse_options(&dev->dev, g_options); if (ret < 0) goto failed; -#endif #ifdef DEBUG_VAR - /* Check for various illegal bit-combinations. Currently only + /* Check for various illegal bit-combinations. Currently only * a warning is given. */ - if (inf->lccr0 & LCCR0_INVALID_CONFIG_MASK) - dev_warn(&dev->dev, "machine LCCR0 setting contains illegal bits: %08x\n", - inf->lccr0 & LCCR0_INVALID_CONFIG_MASK); - if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK) - dev_warn(&dev->dev, "machine LCCR3 setting contains illegal bits: %08x\n", - inf->lccr3 & LCCR3_INVALID_CONFIG_MASK); - if (inf->lccr0 & LCCR0_DPD && + if (inf->lccr0 & LCCR0_INVALID_CONFIG_MASK) + dev_warn(&dev->dev, "machine LCCR0 setting contains " + "illegal bits: %08x\n", + inf->lccr0 & LCCR0_INVALID_CONFIG_MASK); + if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK) + dev_warn(&dev->dev, "machine LCCR3 setting contains " + "illegal bits: %08x\n", + inf->lccr3 & LCCR3_INVALID_CONFIG_MASK); + if (inf->lccr0 & LCCR0_DPD && ((inf->lccr0 & LCCR0_PAS) != LCCR0_Pas || (inf->lccr0 & LCCR0_SDS) != LCCR0_Sngl || (inf->lccr0 & LCCR0_CMS) != LCCR0_Mono)) - dev_warn(&dev->dev, "Double Pixel Data (DPD) mode is only valid in passive mono" - " single panel mode\n"); - if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Act && + dev_warn(&dev->dev, "Double Pixel Data (DPD) mode is " + "only valid in passive mono" + " single panel mode\n"); + if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Act && (inf->lccr0 & LCCR0_SDS) == LCCR0_Dual) - dev_warn(&dev->dev, "Dual panel only valid in passive mode\n"); - if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Pas && - (inf->modes->upper_margin || inf->modes->lower_margin)) - dev_warn(&dev->dev, "Upper and lower margins must be 0 in passive mode\n"); + dev_warn(&dev->dev, "Dual panel only valid in passive mode\n"); + if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Pas && + (inf->modes->upper_margin || inf->modes->lower_margin)) + dev_warn(&dev->dev, "Upper and lower margins must be 0 in " + "passive mode\n"); #endif - dev_dbg(&dev->dev, "got a %dx%dx%d LCD\n",inf->modes->xres, inf->modes->yres, inf->modes->bpp); - if (inf->modes->xres == 0 || inf->modes->yres == 0 || inf->modes->bpp == 0) { + dev_dbg(&dev->dev, "got a %dx%dx%d LCD\n", + inf->modes->xres, + inf->modes->yres, + inf->modes->bpp); + if (inf->modes->xres == 0 || + inf->modes->yres == 0 || + inf->modes->bpp == 0) { dev_err(&dev->dev, "Invalid resolution or bit depth\n"); ret = -EINVAL; goto failed; @@ -1416,26 +1675,62 @@ static int __init pxafb_probe(struct platform_device *dev) pxafb_lcd_power = inf->pxafb_lcd_power; fbi = pxafb_init_fbinfo(&dev->dev); if (!fbi) { + /* only reason for pxafb_init_fbinfo to fail is kmalloc */ dev_err(&dev->dev, "Failed to initialize framebuffer device\n"); - ret = -ENOMEM; // only reason for pxafb_init_fbinfo to fail is kmalloc + ret = -ENOMEM; goto failed; } + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&dev->dev, "no I/O memory resource defined\n"); + ret = -ENODEV; + goto failed; + } + + r = request_mem_region(r->start, r->end - r->start + 1, dev->name); + if (r == NULL) { + dev_err(&dev->dev, "failed to request I/O memory\n"); + ret = -EBUSY; + goto failed; + } + + fbi->mmio_base = ioremap(r->start, r->end - r->start + 1); + if (fbi->mmio_base == NULL) { + dev_err(&dev->dev, "failed to map I/O memory\n"); + ret = -EBUSY; + goto failed_free_res; + } + /* Initialize video memory */ ret = pxafb_map_video_memory(fbi); if (ret) { dev_err(&dev->dev, "Failed to allocate video RAM: %d\n", ret); ret = -ENOMEM; - goto failed; + goto failed_free_io; + } + + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "no IRQ defined\n"); + ret = -ENODEV; + goto failed_free_mem; } - ret = request_irq(IRQ_LCD, pxafb_handle_irq, IRQF_DISABLED, "LCD", fbi); + ret = request_irq(irq, pxafb_handle_irq, IRQF_DISABLED, "LCD", fbi); if (ret) { dev_err(&dev->dev, "request_irq failed: %d\n", ret); ret = -EBUSY; - goto failed; + goto failed_free_mem; } +#ifdef CONFIG_FB_PXA_SMARTPANEL + ret = pxafb_smart_init(fbi); + if (ret) { + dev_err(&dev->dev, "failed to initialize smartpanel\n"); + goto failed_free_irq; + } +#endif /* * This makes sure that our colour bitfield * descriptors are correctly initialised. @@ -1447,19 +1742,18 @@ static int __init pxafb_probe(struct platform_device *dev) ret = register_framebuffer(&fbi->fb); if (ret < 0) { - dev_err(&dev->dev, "Failed to register framebuffer device: %d\n", ret); - goto failed; + dev_err(&dev->dev, + "Failed to register framebuffer device: %d\n", ret); + goto failed_free_irq; } -#ifdef CONFIG_PM - // TODO -#endif - #ifdef CONFIG_CPU_FREQ fbi->freq_transition.notifier_call = pxafb_freq_transition; fbi->freq_policy.notifier_call = pxafb_freq_policy; - cpufreq_register_notifier(&fbi->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); - cpufreq_register_notifier(&fbi->freq_policy, CPUFREQ_POLICY_NOTIFIER); + cpufreq_register_notifier(&fbi->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); + cpufreq_register_notifier(&fbi->freq_policy, + CPUFREQ_POLICY_NOTIFIER); #endif /* @@ -1469,6 +1763,15 @@ static int __init pxafb_probe(struct platform_device *dev) return 0; +failed_free_irq: + free_irq(irq, fbi); +failed_free_res: + release_mem_region(r->start, r->end - r->start + 1); +failed_free_io: + iounmap(fbi->mmio_base); +failed_free_mem: + dma_free_writecombine(&dev->dev, fbi->map_size, + fbi->map_cpu, fbi->map_dma); failed: platform_set_drvdata(dev, NULL); kfree(fbi); @@ -1477,40 +1780,18 @@ failed: static struct platform_driver pxafb_driver = { .probe = pxafb_probe, -#ifdef CONFIG_PM .suspend = pxafb_suspend, .resume = pxafb_resume, -#endif .driver = { .name = "pxa2xx-fb", }, }; -#ifndef MODULE -static int __devinit pxafb_setup(char *options) -{ -# ifdef CONFIG_FB_PXA_PARAMETERS - if (options) - strlcpy(g_options, options, sizeof(g_options)); -# endif - return 0; -} -#else -# ifdef CONFIG_FB_PXA_PARAMETERS -module_param_string(options, g_options, sizeof(g_options), 0); -MODULE_PARM_DESC(options, "LCD parameters (see Documentation/fb/pxafb.txt)"); -# endif -#endif - static int __devinit pxafb_init(void) { -#ifndef MODULE - char *option = NULL; + if (pxafb_setup_options()) + return -EINVAL; - if (fb_get_options("pxafb", &option)) - return -ENODEV; - pxafb_setup(option); -#endif return platform_driver_register(&pxafb_driver); } diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h index d920b8a14c3..8238dc82642 100644 --- a/drivers/video/pxafb.h +++ b/drivers/video/pxafb.h @@ -21,14 +21,6 @@ * for more details. */ -/* Shadows for LCD controller registers */ -struct pxafb_lcd_reg { - unsigned int lccr0; - unsigned int lccr1; - unsigned int lccr2; - unsigned int lccr3; -}; - /* PXA LCD DMA descriptor */ struct pxafb_dma_descriptor { unsigned int fdadr; @@ -37,11 +29,49 @@ struct pxafb_dma_descriptor { unsigned int ldcmd; }; +enum { + PAL_NONE = -1, + PAL_BASE = 0, + PAL_OV1 = 1, + PAL_OV2 = 2, + PAL_MAX, +}; + +enum { + DMA_BASE = 0, + DMA_UPPER = 0, + DMA_LOWER = 1, + DMA_OV1 = 1, + DMA_OV2_Y = 2, + DMA_OV2_Cb = 3, + DMA_OV2_Cr = 4, + DMA_CURSOR = 5, + DMA_CMD = 6, + DMA_MAX, +}; + +/* maximum palette size - 256 entries, each 4 bytes long */ +#define PALETTE_SIZE (256 * 4) +#define CMD_BUFF_SIZE (1024 * 50) + +struct pxafb_dma_buff { + unsigned char palette[PAL_MAX * PALETTE_SIZE]; + uint16_t cmd_buff[CMD_BUFF_SIZE]; + struct pxafb_dma_descriptor pal_desc[PAL_MAX]; + struct pxafb_dma_descriptor dma_desc[DMA_MAX]; +}; + struct pxafb_info { struct fb_info fb; struct device *dev; struct clk *clk; + void __iomem *mmio_base; + + struct pxafb_dma_buff *dma_buff; + dma_addr_t dma_buff_phys; + dma_addr_t fdadr[DMA_MAX]; + /* * These are the addresses we mapped * the framebuffer memory region to. @@ -55,19 +85,8 @@ struct pxafb_info { u_char * screen_cpu; /* virtual address of frame buffer */ dma_addr_t screen_dma; /* physical address of frame buffer */ u16 * palette_cpu; /* virtual address of palette memory */ - dma_addr_t palette_dma; /* physical address of palette memory */ u_int palette_size; - - /* DMA descriptors */ - struct pxafb_dma_descriptor * dmadesc_fblow_cpu; - dma_addr_t dmadesc_fblow_dma; - struct pxafb_dma_descriptor * dmadesc_fbhigh_cpu; - dma_addr_t dmadesc_fbhigh_dma; - struct pxafb_dma_descriptor * dmadesc_palette_cpu; - dma_addr_t dmadesc_palette_dma; - - dma_addr_t fdadr0; - dma_addr_t fdadr1; + ssize_t video_offset; u_int lccr0; u_int lccr3; @@ -81,6 +100,7 @@ struct pxafb_info { u_int reg_lccr2; u_int reg_lccr3; u_int reg_lccr4; + u_int reg_cmdcr; unsigned long hsync_time; @@ -90,6 +110,16 @@ struct pxafb_info { wait_queue_head_t ctrlr_wait; struct work_struct task; + struct completion disable_done; + +#ifdef CONFIG_FB_PXA_SMARTPANEL + uint16_t *smart_cmds; + size_t n_smart_cmds; + struct completion command_done; + struct completion refresh_done; + struct task_struct *smart_thread; +#endif + #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; struct notifier_block freq_policy; diff --git a/drivers/w1/w1_log.h b/drivers/w1/w1_log.h index fe6bdf43380..e6ab7cf08f8 100644 --- a/drivers/w1/w1_log.h +++ b/drivers/w1/w1_log.h @@ -30,7 +30,7 @@ # define assert(expr) \ if(unlikely(!(expr))) { \ printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); \ + #expr, __FILE__, __func__, __LINE__); \ } #endif |