summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
commit545f435ebcfd94a1e7c20b46efe81b4d6ac4e698 (patch)
treee9ce4bc598d06374bda906f18365984bf22a526a /drivers
parent4291a610eef89d0d5c69d9a10ee6560e1aa36c74 (diff)
Merge with Linux 2.1.55. More bugfixes and goodies from my private
CVS archive.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile7
-rw-r--r--drivers/block/Makefile4
-rw-r--r--drivers/block/floppy.c9
-rw-r--r--drivers/block/genhd.c130
-rw-r--r--drivers/block/hd.c43
-rw-r--r--drivers/block/ide.c4
-rw-r--r--drivers/block/ll_rw_blk.c6
-rw-r--r--drivers/block/raid0.c32
-rw-r--r--drivers/block/rd.c2
-rw-r--r--drivers/block/swim3.c1011
-rw-r--r--drivers/char/.cvsignore1
-rw-r--r--drivers/char/Config.in10
-rw-r--r--drivers/char/Makefile39
-rw-r--r--drivers/char/acquirewdt.c221
-rw-r--r--drivers/char/apm_bios.c36
-rw-r--r--drivers/char/conmakehash.c29
-rw-r--r--drivers/char/consolemap.c5
-rw-r--r--drivers/char/esp.c212
-rw-r--r--drivers/char/h8.c1272
-rw-r--r--drivers/char/h8.h250
-rw-r--r--drivers/char/hfmodem/.cvsignore3
-rw-r--r--drivers/char/hfmodem/Makefile37
-rw-r--r--drivers/char/hfmodem/gentbl.c69
-rw-r--r--drivers/char/hfmodem/main.c736
-rw-r--r--drivers/char/hfmodem/modem.c792
-rw-r--r--drivers/char/hfmodem/refclock.c189
-rw-r--r--drivers/char/hfmodem/sbc.c741
-rw-r--r--drivers/char/hfmodem/wss.c437
-rw-r--r--drivers/char/lp.c99
-rw-r--r--drivers/char/mem.c14
-rw-r--r--drivers/char/misc.c14
-rw-r--r--drivers/char/psaux.c9
-rw-r--r--drivers/char/serial.c8
-rw-r--r--drivers/char/tty_ioctl.c13
-rw-r--r--drivers/char/vt.c5
-rw-r--r--drivers/isdn/avmb1/capi.c7
-rw-r--r--drivers/isdn/hisax/l3_1tr6.c9
-rw-r--r--drivers/isdn/hisax/l3dss1.c9
-rw-r--r--drivers/isdn/isdn_common.c9
-rw-r--r--drivers/isdn/isdn_ppp.c9
-rw-r--r--drivers/macintosh/.cvsignore1
-rw-r--r--drivers/macintosh/Makefile46
-rw-r--r--drivers/macintosh/adb.c169
-rw-r--r--drivers/macintosh/ati-gt.h149
-rw-r--r--drivers/macintosh/ati-vt.h147
-rw-r--r--drivers/macintosh/aty.c568
-rw-r--r--drivers/macintosh/aty.h614
-rw-r--r--drivers/macintosh/control.c520
-rw-r--r--drivers/macintosh/control.h17
-rw-r--r--drivers/macintosh/imstt.c506
-rw-r--r--drivers/macintosh/imstt.h18
-rw-r--r--drivers/macintosh/mac_keyb.c343
-rw-r--r--drivers/macintosh/mackeymap.c262
-rw-r--r--drivers/macintosh/mackeymap.map346
-rw-r--r--drivers/macintosh/macserial.c1923
-rw-r--r--drivers/macintosh/macserial.h407
-rw-r--r--drivers/macintosh/nvram.c87
-rw-r--r--drivers/macintosh/platinum.c623
-rw-r--r--drivers/macintosh/platinum.h17
-rw-r--r--drivers/macintosh/pmac-cons.c1312
-rw-r--r--drivers/macintosh/pmac-cons.h90
-rw-r--r--drivers/macintosh/valkyrie.c321
-rw-r--r--drivers/macintosh/valkyrie.h17
-rw-r--r--drivers/macintosh/via-cuda.c415
-rw-r--r--drivers/misc/BUGS-parport7
-rw-r--r--drivers/misc/parport_arc.c24
-rw-r--r--drivers/misc/parport_ieee1284.c5
-rw-r--r--drivers/misc/parport_init.c16
-rw-r--r--drivers/misc/parport_pc.c71
-rw-r--r--drivers/misc/parport_procfs.c3
-rw-r--r--drivers/misc/parport_share.c20
-rw-r--r--drivers/net/3c509.c85
-rw-r--r--drivers/net/8390.c4
-rw-r--r--drivers/net/Config.in45
-rw-r--r--drivers/net/Makefile57
-rw-r--r--drivers/net/Space.c36
-rw-r--r--drivers/net/arc-rimi.c851
-rw-r--r--drivers/net/arcnet.c3895
-rw-r--r--drivers/net/baycom.c232
-rw-r--r--drivers/net/com20020.c1097
-rw-r--r--drivers/net/com90io.c964
-rw-r--r--drivers/net/com90xx.c1264
-rw-r--r--drivers/net/defxx.c4
-rw-r--r--drivers/net/eepro100.c164
-rw-r--r--drivers/net/ethertap.c225
-rw-r--r--drivers/net/hdlcdrv.c4
-rw-r--r--drivers/net/ibmtr.c136
-rw-r--r--drivers/net/mace.c800
-rw-r--r--drivers/net/mace.h173
-rw-r--r--drivers/net/myri_sbus.c49
-rw-r--r--drivers/net/ne.c2
-rw-r--r--drivers/net/plip.c4
-rw-r--r--drivers/net/ppp.c8
-rw-r--r--drivers/net/slip.c64
-rw-r--r--drivers/net/soundmodem/Makefile3
-rw-r--r--drivers/net/soundmodem/gentbl.c74
-rw-r--r--drivers/net/soundmodem/sm.c3
-rw-r--r--drivers/net/soundmodem/sm_afsk2400_7.c1
-rw-r--r--drivers/net/soundmodem/sm_afsk2400_8.c1
-rw-r--r--drivers/net/soundmodem/sm_sbc.c5
-rw-r--r--drivers/net/soundmodem/sm_wss.c23
-rw-r--r--drivers/net/soundmodem/smdma.h7
-rw-r--r--drivers/net/sunhme.c852
-rw-r--r--drivers/net/sunhme.h68
-rw-r--r--drivers/net/sunlance.c24
-rw-r--r--drivers/net/sunqe.c42
-rw-r--r--drivers/net/tlan.c194
-rw-r--r--drivers/net/tlan.h29
-rw-r--r--drivers/pci/pci.c157
-rw-r--r--drivers/pnp/parport_probe.c4
-rw-r--r--drivers/sbus/char/Config.in1
-rw-r--r--drivers/sbus/char/Makefile22
-rw-r--r--drivers/sbus/char/cgfourteen.c4
-rw-r--r--drivers/sbus/char/cgsix.c8
-rw-r--r--drivers/sbus/char/cgthree.c4
-rw-r--r--drivers/sbus/char/creator.c231
-rw-r--r--drivers/sbus/char/fb.h26
-rw-r--r--drivers/sbus/char/leo.c16
-rw-r--r--drivers/sbus/char/mach64.c203
-rw-r--r--drivers/sbus/char/mach64.h587
-rw-r--r--drivers/sbus/char/pcicons.c750
-rw-r--r--drivers/sbus/char/pcicons.h79
-rw-r--r--drivers/sbus/char/pcikbd.c964
-rw-r--r--drivers/sbus/char/pcikbd.h123
-rw-r--r--drivers/sbus/char/sab82532.c2173
-rw-r--r--drivers/sbus/char/sbuscons.c1644
-rw-r--r--drivers/sbus/char/su.c678
-rw-r--r--drivers/sbus/char/suncons.c1778
-rw-r--r--drivers/sbus/char/sunfb.c14
-rw-r--r--drivers/sbus/char/sunkbd.c43
-rw-r--r--drivers/sbus/char/sunkbd.h27
-rw-r--r--drivers/sbus/char/sunmouse.c35
-rw-r--r--drivers/sbus/char/sunmouse.h13
-rw-r--r--drivers/sbus/char/sunserial.c2678
-rw-r--r--drivers/sbus/char/sunserial.h457
-rw-r--r--drivers/sbus/char/tcx.c10
-rw-r--r--drivers/sbus/char/zs.c2786
-rw-r--r--drivers/sbus/char/zs.h428
-rw-r--r--drivers/sbus/sbus.c118
-rw-r--r--drivers/scsi/53c7,8xx.c4
-rw-r--r--drivers/scsi/BusLogic.c84
-rw-r--r--drivers/scsi/ChangeLog.ncr53c8xx95
-rw-r--r--drivers/scsi/Config.in22
-rw-r--r--drivers/scsi/FlashPoint.c94
-rw-r--r--drivers/scsi/Makefile26
-rw-r--r--drivers/scsi/README.BusLogic21
-rw-r--r--drivers/scsi/README.ncr53c8xx536
-rw-r--r--drivers/scsi/aic7xxx.c27
-rw-r--r--drivers/scsi/aic7xxx_proc.c51
-rw-r--r--drivers/scsi/aic7xxx_reg.h14
-rw-r--r--drivers/scsi/esp.c80
-rw-r--r--drivers/scsi/g_NCR5380.c12
-rw-r--r--drivers/scsi/hosts.c16
-rw-r--r--drivers/scsi/mac53c94.c502
-rw-r--r--drivers/scsi/mac53c94.h246
-rw-r--r--drivers/scsi/mesh.c1319
-rw-r--r--drivers/scsi/mesh.h159
-rw-r--r--drivers/scsi/ncr53c8xx.c2648
-rw-r--r--drivers/scsi/ncr53c8xx.h126
-rw-r--r--drivers/scsi/qlogicpti.c111
-rw-r--r--drivers/scsi/scsi.h1
-rw-r--r--drivers/scsi/scsi_ioctl.c16
-rw-r--r--drivers/scsi/scsi_syms.c2
-rw-r--r--drivers/scsi/sd.c6
-rw-r--r--drivers/scsi/sr.c3
-rw-r--r--drivers/scsi/ultrastor.c7
-rw-r--r--drivers/sgi/char/cons_newport.c8
167 files changed, 40351 insertions, 9031 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index c152af2eb..f95448f18 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -9,7 +9,7 @@
SUB_DIRS := block char net misc #streams
MOD_SUB_DIRS := $(SUB_DIRS) sbus
-ALL_SUB_DIRS := $(SUB_DIRS) pci sgi scsi sound cdrom isdn pnp
+ALL_SUB_DIRS := $(SUB_DIRS) pci sgi scsi sound cdrom isdn pnp macintosh
ifdef CONFIG_PCI
SUB_DIRS += pci
@@ -19,6 +19,11 @@ ifdef CONFIG_SBUS
SUB_DIRS += sbus
endif
+ifdef CONFIG_PPC
+SUB_DIRS += macintosh
+MOD_SUB_DIRS += macintosh
+endif
+
ifdef CONFIG_SGI
SUB_DIRS += sgi
endif
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index f8ec1abd9..de13e072a 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -22,6 +22,10 @@ MOD_LIST_NAME := BLOCK_MODULES
LX_OBJS :=
MX_OBJS :=
+ifeq ($(CONFIG_MAC_FLOPPY),y)
+L_OBJS += swim3.o
+endif
+
ifeq ($(CONFIG_BLK_DEV_FD),y)
L_OBJS += floppy.o
else
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 2799eb04c..04ea60ff8 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -161,11 +161,6 @@ static inline int __get_order(unsigned long size);
#include <linux/blk.h>
#include <linux/cdrom.h> /* for the compatibility eject ioctl */
-
-#ifndef FLOPPY_MOTOR_MASK
-#define FLOPPY_MOTOR_MASK 0xf0
-#endif
-
#ifndef fd_get_dma_residue
#define fd_get_dma_residue() get_dma_residue(FLOPPY_DMA)
#endif
@@ -3968,6 +3963,10 @@ __initfunc(int floppy_init(void))
}
fdc_state[0].address = FDC1;
+ if (fdc_state[0].address == -1) {
+ unregister_blkdev(MAJOR_NR,"fd");
+ return -ENODEV;
+ }
#if N_FDC > 1
fdc_state[1].address = FDC2;
#endif
diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c
index 1ee709737..c6b0a02b6 100644
--- a/drivers/block/genhd.c
+++ b/drivers/block/genhd.c
@@ -740,6 +740,132 @@ rdb_done:
}
#endif /* CONFIG_AMIGA_PARTITION */
+#ifdef CONFIG_MAC_PARTITION
+#include <linux/ctype.h>
+
+/*
+ * Code to understand MacOS partition tables.
+ */
+
+#define MAC_PARTITION_MAGIC 0x504d
+
+/* type field value for A/UX or other Unix partitions */
+#define APPLE_AUX_TYPE "Apple_UNIX_SVR2"
+
+struct mac_partition {
+ __u16 signature; /* expected to be MAC_PARTITION_MAGIC */
+ __u16 res1;
+ __u32 map_count; /* # blocks in partition map */
+ __u32 start_block; /* absolute starting block # of partition */
+ __u32 block_count; /* number of blocks in partition */
+ char name[32]; /* partition name */
+ char type[32]; /* string type description */
+ __u32 data_start; /* rel block # of first data block */
+ __u32 data_count; /* number of data blocks */
+ __u32 status; /* partition status bits */
+ __u32 boot_start;
+ __u32 boot_size;
+ __u32 boot_load;
+ __u32 boot_load2;
+ __u32 boot_entry;
+ __u32 boot_entry2;
+ __u32 boot_cksum;
+ char processor[16]; /* identifies ISA of boot */
+ /* there is more stuff after this that we don't need */
+};
+
+#define MAC_STATUS_BOOTABLE 8 /* partition is bootable */
+
+#define MAC_DRIVER_MAGIC 0x4552
+
+/* Driver descriptor structure, in block 0 */
+struct mac_driver_desc {
+ __u16 signature; /* expected to be MAC_DRIVER_MAGIC */
+ __u16 block_size;
+ __u32 block_count;
+ /* ... more stuff */
+};
+
+static int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec)
+{
+ struct buffer_head *bh;
+ int blk, blocks_in_map;
+ int dev_bsize, dev_pos, pos;
+ unsigned secsize;
+ int first_bootable = 1;
+ struct mac_partition *part;
+ struct mac_driver_desc *md;
+
+ dev_bsize = get_ptable_blocksize(dev);
+ dev_pos = 0;
+ /* Get 0th block and look at the first partition map entry. */
+ if ((bh = bread(dev, 0, dev_bsize)) == 0) {
+ printk("%s: error reading partition table\n",
+ kdevname(dev));
+ return -1;
+ }
+ md = (struct mac_driver_desc *) bh->b_data;
+ if (be16_to_cpu(md->signature) != MAC_DRIVER_MAGIC) {
+ brelse(bh);
+ return 0;
+ }
+ secsize = be16_to_cpu(md->block_size);
+ if (secsize >= dev_bsize) {
+ brelse(bh);
+ dev_pos = secsize;
+ if ((bh = bread(dev, secsize/dev_bsize, dev_bsize)) == 0) {
+ printk("%s: error reading partition table\n",
+ kdevname(dev));
+ return -1;
+ }
+ }
+ part = (struct mac_partition *) (bh->b_data + secsize - dev_pos);
+ if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC) {
+ brelse(bh);
+ return 0; /* not a MacOS disk */
+ }
+ blocks_in_map = be32_to_cpu(part->map_count);
+ for (blk = 1; blk <= blocks_in_map; ++blk) {
+ pos = blk * secsize;
+ if (pos >= dev_pos + dev_bsize) {
+ brelse(bh);
+ dev_pos = pos;
+ if ((bh = bread(dev, pos/dev_bsize, dev_bsize)) == 0) {
+ printk("%s: error reading partition table\n",
+ kdevname(dev));
+ return -1;
+ }
+ }
+ part = (struct mac_partition *) (bh->b_data + pos - dev_pos);
+ if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC)
+ break;
+ blocks_in_map = be32_to_cpu(part->map_count);
+ add_partition(hd, current_minor,
+ fsec + be32_to_cpu(part->start_block) * (secsize/512),
+ be32_to_cpu(part->block_count) * (secsize/512));
+
+#ifdef CONFIG_PMAC
+ /*
+ * If this is the first bootable partition, tell the
+ * setup code, in case it wants to make this the root.
+ */
+ if (first_bootable
+ && (be32_to_cpu(part->status) & MAC_STATUS_BOOTABLE)
+ && strcasecmp(part->processor, "powerpc") == 0) {
+ note_bootable_part(dev, blk);
+ first_bootable = 0;
+ }
+#endif /* CONFIG_PMAC */
+
+ ++current_minor;
+ }
+ brelse(bh);
+ printk("\n");
+ return 1;
+}
+
+#endif /* CONFIG_MAC_PARTITION */
+
#ifdef CONFIG_ATARI_PARTITION
#include <asm/atari_rootsec.h>
@@ -916,6 +1042,10 @@ static void check_partition(struct gendisk *hd, kdev_t dev)
if(atari_partition(hd, dev, first_sector))
return;
#endif
+#ifdef CONFIG_MAC_PARTITION
+ if (mac_partition(hd, dev, first_sector))
+ return;
+#endif
#ifdef CONFIG_SGI_PARTITION
if(sgi_partition(hd, dev, first_sector))
return;
diff --git a/drivers/block/hd.c b/drivers/block/hd.c
index 62e2f8da3..558a1b2d2 100644
--- a/drivers/block/hd.c
+++ b/drivers/block/hd.c
@@ -584,7 +584,7 @@ static int hd_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
struct hd_geometry *loc = (struct hd_geometry *) arg;
- int dev, err;
+ int dev;
if ((!inode) || !(inode->i_rdev))
return -EINVAL;
@@ -593,19 +593,15 @@ static int hd_ioctl(struct inode * inode, struct file * file,
return -EINVAL;
switch (cmd) {
case HDIO_GETGEO:
+ {
+ struct hd_geometry g;
if (!loc) return -EINVAL;
- err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
- if (err)
- return err;
- put_user(hd_info[dev].head,
- (char *) &loc->heads);
- put_user(hd_info[dev].sect,
- (char *) &loc->sectors);
- put_user(hd_info[dev].cyl,
- (short *) &loc->cylinders);
- put_user(hd[MINOR(inode->i_rdev)].start_sect,
- (long *) &loc->start);
- return 0;
+ g.heads = hd_info[dev].head;
+ g.sectors = hd_info[dev].sect;
+ g.cylinders = hd_info[dev].cyl;
+ g.start = hd[MINOR(inode->i_rdev)].start_sect;
+ return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0;
+ }
case BLKRASET:
if(!suser()) return -EACCES;
if(arg > 0xff) return -EINVAL;
@@ -613,18 +609,12 @@ static int hd_ioctl(struct inode * inode, struct file * file,
return 0;
case BLKRAGET:
if (!arg) return -EINVAL;
- err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
- if (err)
- return err;
- put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
- return 0;
+ return put_user(read_ahead[MAJOR(inode->i_rdev)],
+ (long *) arg);
case BLKGETSIZE: /* Return device size */
if (!arg) return -EINVAL;
- err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
- if (err)
- return err;
- put_user(hd[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
- return 0;
+ return put_user(hd[MINOR(inode->i_rdev)].nr_sects,
+ (long *) arg);
case BLKFLSBUF:
if(!suser()) return -EACCES;
fsync_dev(inode->i_rdev);
@@ -817,7 +807,7 @@ __initfunc(int hd_init(void))
/*
* This routine is called to flush all partitions and partition tables
- * for a changed scsi disk, and then re-read the new partition table.
+ * for a changed disk, and then re-read the new partition table.
* If we are revalidating a disk because of a media change, then we
* enter with usage == 0. If we are using an ioctl, we automatically have
* usage == 1 (we need an open channel to use an ioctl :-), so this
@@ -850,8 +840,11 @@ static int revalidate_hddisk(kdev_t dev, int maxusage)
for (i=max_p - 1; i >=0 ; i--) {
int minor = start + i;
kdev_t devi = MKDEV(MAJOR_NR, minor);
+ struct super_block *sb = get_super(devi);
+
sync_dev(devi);
- invalidate_inodes(devi);
+ if (sb)
+ invalidate_inodes(sb);
invalidate_buffers(devi);
gdev->part[minor].start_sect = 0;
gdev->part[minor].nr_sects = 0;
diff --git a/drivers/block/ide.c b/drivers/block/ide.c
index 61b792f74..1a5fdc5a8 100644
--- a/drivers/block/ide.c
+++ b/drivers/block/ide.c
@@ -1611,8 +1611,10 @@ int ide_revalidate_disk(kdev_t i_rdev)
for (p = 0; p < (1<<PARTN_BITS); ++p) {
if (drive->part[p].nr_sects > 0) {
kdev_t devp = MKDEV(major, minor+p);
+ struct super_block * sb = get_super(devp);
fsync_dev (devp);
- invalidate_inodes (devp);
+ if (sb)
+ invalidate_inodes(sb);
invalidate_buffers (devp);
}
drive->part[p].start_sect = 0;
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
index 6e1d27eea..759785e85 100644
--- a/drivers/block/ll_rw_blk.c
+++ b/drivers/block/ll_rw_blk.c
@@ -681,10 +681,14 @@ __initfunc(int blk_dev_init(void))
#ifdef CONFIG_BLK_DEV_EZ
ez_init();
#endif
+#ifdef CONFIG_MAC_FLOPPY
+ swim3_init();
+#endif
#ifdef CONFIG_BLK_DEV_FD
floppy_init();
#else
-#if !defined(CONFIG_SGI) && !defined (__mc68000__)
+#if !defined(CONFIG_SGI) && !defined (__mc68000__) \
+ && !defined(CONFIG_PMAC) && !defined(__sparc__)
outb_p(0xc, 0x3f2);
#endif
#endif
diff --git a/drivers/block/raid0.c b/drivers/block/raid0.c
index dddf5dd8d..6d269745b 100644
--- a/drivers/block/raid0.c
+++ b/drivers/block/raid0.c
@@ -20,13 +20,13 @@
#include <linux/module.h>
#include <linux/md.h>
#include <linux/raid0.h>
-#include <linux/malloc.h>
+#include <linux/vmalloc.h>
#define MAJOR_NR MD_MAJOR
#define MD_DRIVER
#define MD_PERSONALITY
-static void create_strip_zones (int minor, struct md_dev *mddev)
+static int create_strip_zones (int minor, struct md_dev *mddev)
{
int i, j, c=0;
int current_offset=0;
@@ -50,8 +50,8 @@ static void create_strip_zones (int minor, struct md_dev *mddev)
c=0;
}
- data->strip_zone=kmalloc (sizeof(struct strip_zone)*data->nr_strip_zones,
- GFP_KERNEL);
+ if ((data->strip_zone=vmalloc(sizeof(struct strip_zone)*data->nr_strip_zones)) == NULL)
+ return 1;
data->smallest=NULL;
@@ -81,6 +81,7 @@ static void create_strip_zones (int minor, struct md_dev *mddev)
data->strip_zone[i-1].size) : 0;
current_offset=smallest_by_zone->size;
}
+ return 0;
}
static int raid0_run (int minor, struct md_dev *mddev)
@@ -90,17 +91,26 @@ static int raid0_run (int minor, struct md_dev *mddev)
MOD_INC_USE_COUNT;
- mddev->private=kmalloc (sizeof (struct raid0_data), GFP_KERNEL);
+ if ((mddev->private=vmalloc (sizeof (struct raid0_data))) == NULL) return 1;
data=(struct raid0_data *) mddev->private;
- create_strip_zones (minor, mddev);
+ if (create_strip_zones (minor, mddev))
+ {
+ vfree(data);
+ return 1;
+ }
nb_zone=data->nr_zones=
md_size[minor]/data->smallest->size +
(md_size[minor]%data->smallest->size ? 1 : 0);
-
- data->hash_table=kmalloc (sizeof (struct raid0_hash)*nb_zone, GFP_KERNEL);
+ printk ("raid0 : Allocating %d bytes for hash.\n",sizeof(struct raid0_hash)*nb_zone);
+ if ((data->hash_table=vmalloc (sizeof (struct raid0_hash)*nb_zone)) == NULL)
+ {
+ vfree(data->strip_zone);
+ vfree(data);
+ return 1;
+ }
size=data->strip_zone[cur].size;
i=0;
@@ -142,9 +152,9 @@ static int raid0_stop (int minor, struct md_dev *mddev)
{
struct raid0_data *data=(struct raid0_data *) mddev->private;
- kfree (data->hash_table);
- kfree (data->strip_zone);
- kfree (data);
+ vfree (data->hash_table);
+ vfree (data->strip_zone);
+ vfree (data);
MOD_DEC_USE_COUNT;
return 0;
diff --git a/drivers/block/rd.c b/drivers/block/rd.c
index 7d7a3931c..b8239df9b 100644
--- a/drivers/block/rd.c
+++ b/drivers/block/rd.c
@@ -447,7 +447,7 @@ __initfunc(static void rd_load_image(kdev_t device,int offset))
struct inode inode, out_inode;
struct file infile, outfile;
struct dentry in_dentry, out_dentry;
- unsigned short fs;
+ unsigned long fs;
kdev_t ram_device;
int nblocks, i;
char *buf;
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
new file mode 100644
index 000000000..4dc442673
--- /dev/null
+++ b/drivers/block/swim3.c
@@ -0,0 +1,1011 @@
+/*
+ * Driver for the SWIM3 (Super Woz Integrated Machine 3)
+ * floppy controller found on Power Macintoshes.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ * 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.
+ */
+
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/fd.h>
+#include <linux/ioctl.h>
+#include <asm/io.h>
+#include <asm/dbdma.h>
+#include <asm/prom.h>
+#include <asm/uaccess.h>
+
+#define MAJOR_NR FLOPPY_MAJOR
+#include <linux/blk.h>
+
+static int floppy_blocksizes[2] = {512};
+static int floppy_sizes[2] = {2880};
+
+enum swim_state {
+ idle,
+ locating,
+ seeking,
+ settling,
+ do_transfer,
+ jogging,
+ available,
+ revalidating,
+ ejecting
+};
+
+#define REG(x) unsigned char x; char x ## _pad[15];
+
+/*
+ * The names for these registers mostly represent speculation on my part.
+ * It will be interesting to see how close they are to the names Apple uses.
+ */
+struct swim3 {
+ REG(data);
+ REG(usecs); /* counts down at 1MHz */
+ REG(error);
+ REG(mode);
+ REG(select); /* controls CA0, CA1, CA2 and LSTRB signals */
+ REG(reg5);
+ REG(control); /* writing bits clears them */
+ REG(status); /* writing bits sets them in control */
+ REG(intr);
+ REG(nseek); /* # tracks to seek */
+ REG(ctrack); /* current track number */
+ REG(csect); /* current sector number */
+ REG(ssize); /* sector size code?? */
+ REG(sector); /* sector # to read or write */
+ REG(nsect); /* # sectors to read or write */
+ REG(intr_enable);
+};
+
+#define control_bic control
+#define control_bis status
+
+/* Bits in select register */
+#define CA_MASK 7
+#define LSTRB 8
+
+/* Bits in control register */
+#define DO_SEEK 0x80
+#define SELECT 0x20
+#define WRITE_SECTORS 0x10
+#define SCAN_TRACK 0x08
+#define DRIVE_ENABLE 0x02
+#define INTR_ENABLE 0x01
+
+/* Bits in status register */
+#define DATA 0x08
+
+/* Bits in intr and intr_enable registers */
+#define ERROR 0x20
+#define DATA_CHANGED 0x10
+#define TRANSFER_DONE 0x08
+#define SEEN_SECTOR 0x04
+#define SEEK_DONE 0x02
+
+/* Select values for swim3_action */
+#define SEEK_POSITIVE 0
+#define SEEK_NEGATIVE 4
+#define STEP 1
+#define MOTOR_ON 2
+#define MOTOR_OFF 6
+#define EJECT 7
+
+/* Select values for swim3_select and swim3_readbit */
+#define STEP_DIR 0
+#define STEPPING 1
+#define MOTOR_ON 2
+#define RELAX 3
+#define READ_DATA_0 4
+#define SINGLE_SIDED 6
+#define DRIVE_PRESENT 7
+#define DISK_IN 8
+#define WRITE_PROT 9
+#define TRACK_ZERO 10
+#define TACHO 11
+#define READ_DATA_1 12
+#define SEEK_COMPLETE 14
+
+struct floppy_state {
+ enum swim_state state;
+ volatile struct swim3 *swim3; /* hardware registers */
+ struct dbdma_regs *dma; /* DMA controller registers */
+ int swim3_intr; /* interrupt number for SWIM3 */
+ int dma_intr; /* interrupt number for DMA channel */
+ int cur_cyl; /* cylinder head is on, or -1 */
+ int cur_sector; /* last sector we saw go past */
+ int req_cyl; /* the cylinder for the current r/w request */
+ int head; /* head number ditto */
+ int req_sector; /* sector number ditto */
+ int scount; /* # sectors we're transferring at present */
+ int retries;
+ int secpercyl; /* disk geometry information */
+ int secpertrack;
+ int total_secs;
+ int write_prot; /* 1 if write-protected, 0 if not, -1 dunno */
+ struct dbdma_cmd *dma_cmd;
+ int ref_count;
+ int expect_cyl;
+ struct timer_list timeout;
+ int ejected;
+ struct wait_queue *wait;
+ int wanted;
+ char dbdma_cmd_space[5 * sizeof(struct dbdma_cmd)];
+};
+
+static struct floppy_state floppy_states[1];
+
+static unsigned short write_preamble[] = {
+ 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, /* gap field */
+ 0, 0, 0, 0, 0, 0, /* sync field */
+ 0x99a1, 0x99a1, 0x99a1, 0x99fb, /* data address mark */
+ 0x990f /* init CRC generator */
+};
+
+static unsigned short write_postamble[] = {
+ 0x9904, /* insert CRC */
+ 0x4e4e, 0x4e4e,
+ 0x9908, /* stop writing */
+ 0, 0, 0, 0, 0, 0
+};
+
+static void swim3_select(struct floppy_state *fs, int sel);
+static void swim3_action(struct floppy_state *fs, int action);
+static int swim3_readbit(struct floppy_state *fs, int bit);
+static void do_fd_request(void);
+static void start_request(struct floppy_state *fs);
+static void scan_track(struct floppy_state *fs);
+static void seek_track(struct floppy_state *fs, int n);
+static void init_dma(struct dbdma_cmd *cp, int cmd, void *buf, int count);
+static void setup_transfer(struct floppy_state *fs);
+static void act(struct floppy_state *fs);
+static void scan_timeout(unsigned long data);
+static void seek_timeout(unsigned long data);
+static void xfer_timeout(unsigned long data);
+static void swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void fd_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int grab_drive(struct floppy_state *fs, enum swim_state state,
+ int interruptible);
+static void release_drive(struct floppy_state *fs);
+static int fd_eject(struct floppy_state *fs);
+static int floppy_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long param);
+static int floppy_open(struct inode *inode, struct file *filp);
+static int floppy_release(struct inode *inode, struct file *filp);
+static long floppy_read(struct inode *inode, struct file *filp,
+ char *buf, unsigned long count);
+static long floppy_write(struct inode *inode, struct file *filp,
+ const char *buf, unsigned long count);
+static int floppy_check_change(kdev_t dev);
+static int floppy_revalidate(kdev_t dev);
+int swim3_init(void);
+
+#define IOCTL_MODE_BIT 8
+#define OPEN_WRITE_BIT 16
+
+static void swim3_select(struct floppy_state *fs, int sel)
+{
+ volatile struct swim3 *sw = fs->swim3;
+
+ out_8(&sw->select, RELAX);
+ if (sel & 8)
+ out_8(&sw->control_bis, SELECT);
+ else
+ out_8(&sw->control_bic, SELECT);
+ out_8(&sw->select, sel & CA_MASK);
+}
+
+static void swim3_action(struct floppy_state *fs, int action)
+{
+ volatile struct swim3 *sw = fs->swim3;
+
+ swim3_select(fs, action);
+ udelay(1);
+ sw->select |= LSTRB; eieio();
+ udelay(2);
+ sw->select &= ~LSTRB; eieio();
+ udelay(1);
+ out_8(&sw->select, RELAX);
+}
+
+static int swim3_readbit(struct floppy_state *fs, int bit)
+{
+ volatile struct swim3 *sw = fs->swim3;
+ int stat;
+
+ swim3_select(fs, bit);
+ udelay(1);
+ stat = in_8(&sw->status);
+ out_8(&sw->select, RELAX);
+ return (stat & DATA) == 0;
+}
+
+static void do_fd_request(void)
+{
+ start_request(&floppy_states[0]);
+ sti();
+}
+
+static void start_request(struct floppy_state *fs)
+{
+ int drive;
+ unsigned long x;
+
+ if (fs->state == idle && fs->wanted) {
+ fs->state = available;
+ wake_up(&fs->wait);
+ return;
+ }
+ while (CURRENT && fs->state == idle) {
+ if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
+ panic(DEVICE_NAME ": request list destroyed");
+ if (CURRENT->bh && !buffer_locked(CURRENT->bh))
+ panic(DEVICE_NAME ": block not locked");
+#if 0
+ printk("do_fd_req: dev=%x cmd=%d sec=%ld nr_sec=%ld buf=%p\n",
+ kdev_t_to_nr(CURRENT->rq_dev), CURRENT->cmd,
+ CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer);
+ printk(" rq_status=%d errors=%d current_nr_sectors=%ld\n",
+ CURRENT->rq_status, CURRENT->errors, CURRENT->current_nr_sectors);
+#endif
+
+ drive = MINOR(CURRENT->rq_dev);
+ if (drive != 0) {
+ end_request(0);
+ continue;
+ }
+ if (CURRENT->sector < 0 || CURRENT->sector >= fs->total_secs) {
+ end_request(0);
+ continue;
+ }
+ if (CURRENT->current_nr_sectors == 0) {
+ end_request(1);
+ continue;
+ }
+ if (fs->ejected) {
+ end_request(0);
+ continue;
+ }
+
+ if (CURRENT->cmd == WRITE) {
+ if (fs->write_prot < 0)
+ fs->write_prot = swim3_readbit(fs, WRITE_PROT);
+ if (fs->write_prot) {
+ end_request(0);
+ continue;
+ }
+ }
+
+ fs->req_cyl = CURRENT->sector / fs->secpercyl;
+ x = CURRENT->sector % fs->secpercyl;
+ fs->head = x / fs->secpertrack;
+ fs->req_sector = x % fs->secpertrack + 1;
+ fs->state = do_transfer;
+ fs->retries = 0;
+
+ act(fs);
+ }
+}
+
+static inline void scan_track(struct floppy_state *fs)
+{
+ volatile struct swim3 *sw = fs->swim3;
+ int xx;
+
+ swim3_select(fs, READ_DATA_0);
+ xx = sw->intr; /* clear SEEN_SECTOR bit */
+ out_8(&sw->control_bis, SCAN_TRACK);
+ /* enable intr when track found */
+ out_8(&sw->intr_enable, ERROR | SEEN_SECTOR);
+ /* enable timeout */
+ fs->timeout.expires = jiffies + HZ;
+ fs->timeout.function = scan_timeout;
+ fs->timeout.data = (unsigned long) fs;
+ add_timer(&fs->timeout);
+}
+
+static inline void seek_track(struct floppy_state *fs, int n)
+{
+ volatile struct swim3 *sw = fs->swim3;
+
+ if (n >= 0) {
+ swim3_action(fs, SEEK_POSITIVE);
+ sw->nseek = n;
+ } else {
+ swim3_action(fs, SEEK_NEGATIVE);
+ sw->nseek = -n;
+ }
+ fs->expect_cyl = (fs->cur_cyl > 0)? fs->cur_cyl + n: -1;
+ swim3_select(fs, STEP);
+ out_8(&sw->control_bis, DO_SEEK);
+ /* enable intr when seek finished */
+ out_8(&sw->intr_enable, ERROR | SEEK_DONE);
+ /* enable timeout */
+ fs->timeout.expires = jiffies + HZ/2;
+ fs->timeout.function = seek_timeout;
+ fs->timeout.data = (unsigned long) fs;
+ add_timer(&fs->timeout);
+}
+
+static inline void init_dma(struct dbdma_cmd *cp, int cmd,
+ void *buf, int count)
+{
+ st_le16(&cp->req_count, count);
+ st_le16(&cp->command, cmd);
+ st_le32(&cp->phy_addr, virt_to_bus(buf));
+ cp->xfer_status = 0;
+}
+
+static inline void setup_transfer(struct floppy_state *fs)
+{
+ int n;
+ volatile struct swim3 *sw = fs->swim3;
+ struct dbdma_cmd *cp = fs->dma_cmd;
+ struct dbdma_regs *dr = fs->dma;
+
+ if (CURRENT->current_nr_sectors <= 0) {
+ printk(KERN_ERR "swim3: transfer 0 sectors?\n");
+ return;
+ }
+ if (CURRENT->cmd == WRITE)
+ n = 1;
+ else {
+ n = fs->secpertrack - fs->req_sector + 1;
+ if (n > CURRENT->current_nr_sectors)
+ n = CURRENT->current_nr_sectors;
+ }
+ fs->scount = n;
+ swim3_select(fs, fs->head? READ_DATA_1: READ_DATA_0);
+ out_8(&sw->sector, fs->req_sector);
+ out_8(&sw->nsect, n);
+ out_8(&sw->ssize, 0);
+ st_le32(&dr->cmdptr, virt_to_bus(cp));
+ if (CURRENT->cmd == WRITE) {
+ /* Set up 3 dma commands: write preamble, data, postamble */
+ init_dma(cp, OUTPUT_MORE, write_preamble, sizeof(write_preamble));
+ ++cp;
+ init_dma(cp, OUTPUT_MORE, CURRENT->buffer, 512);
+ ++cp;
+ init_dma(cp, OUTPUT_MORE, write_postamble, sizeof(write_postamble));
+ } else {
+ init_dma(cp, INPUT_MORE, CURRENT->buffer, n * 512);
+ }
+ ++cp;
+ out_le16(&cp->command, DBDMA_STOP);
+ out_le32(&dr->control, (RUN << 16) | RUN);
+ out_8(&sw->control_bis,
+ (CURRENT->cmd == WRITE? WRITE_SECTORS: 0) | SCAN_TRACK);
+ /* enable intr when transfer complete */
+ out_8(&sw->intr_enable, ERROR | TRANSFER_DONE);
+ /* enable timeout */
+ fs->timeout.expires = jiffies + 2*HZ;
+ fs->timeout.function = xfer_timeout;
+ fs->timeout.data = (unsigned long) fs;
+ add_timer(&fs->timeout);
+}
+
+static void act(struct floppy_state *fs)
+{
+ volatile struct swim3 *sw = fs->swim3;
+
+ for (;;) {
+ switch (fs->state) {
+ case idle:
+ return; /* XXX shouldn't get here */
+
+ case locating:
+ if (swim3_readbit(fs, TRACK_ZERO)) {
+ fs->cur_cyl = 0;
+ if (fs->req_cyl == 0)
+ fs->state = do_transfer;
+ else
+ fs->state = seeking;
+ break;
+ }
+ scan_track(fs);
+ return;
+
+ case seeking:
+ if (fs->cur_cyl < 0) {
+ fs->expect_cyl = -1;
+ fs->state = locating;
+ break;
+ }
+ if (fs->req_cyl == fs->cur_cyl) {
+ printk("whoops, seeking 0\n");
+ fs->state = do_transfer;
+ break;
+ }
+ seek_track(fs, fs->req_cyl - fs->cur_cyl);
+ return;
+
+ case settling:
+ /* wait for SEEK_COMPLETE to become true */
+ swim3_select(fs, SEEK_COMPLETE);
+ udelay(1);
+ out_8(&sw->intr_enable, ERROR | DATA_CHANGED);
+ in_8(&sw->intr); /* clear DATA_CHANGED */
+ if (in_8(&sw->status) & DATA) {
+ /* seek_complete is not yet true */
+ fs->timeout.expires = jiffies + HZ/2;
+ fs->timeout.function = seek_timeout;
+ fs->timeout.data = (unsigned long) fs;
+ add_timer(&fs->timeout);
+ return;
+ }
+ out_8(&sw->intr_enable, 0);
+ in_8(&sw->intr);
+ fs->state = locating;
+ break;
+
+ case do_transfer:
+ if (fs->cur_cyl != fs->req_cyl) {
+ if (fs->retries > 5) {
+ end_request(0);
+ fs->state = idle;
+ return;
+ }
+ fs->state = seeking;
+ break;
+ }
+ setup_transfer(fs);
+ return;
+
+ case jogging:
+ seek_track(fs, -5);
+ return;
+
+ default:
+ printk(KERN_ERR"swim3: unknown state %d\n", fs->state);
+ return;
+ }
+ }
+}
+
+static void scan_timeout(unsigned long data)
+{
+ struct floppy_state *fs = (struct floppy_state *) data;
+ volatile struct swim3 *sw = fs->swim3;
+
+ out_8(&sw->control_bic, SCAN_TRACK);
+ out_8(&sw->select, RELAX);
+ out_8(&sw->intr_enable, 0);
+ fs->cur_cyl = -1;
+ if (fs->retries > 5) {
+ end_request(0);
+ fs->state = idle;
+ start_request(fs);
+ } else {
+ fs->state = jogging;
+ act(fs);
+ }
+}
+
+static void seek_timeout(unsigned long data)
+{
+ struct floppy_state *fs = (struct floppy_state *) data;
+ volatile struct swim3 *sw = fs->swim3;
+
+ if (fs->state == settling) {
+ printk(KERN_ERR "swim3: MSI sel=%x ctrl=%x stat=%x intr=%x ie=%x\n",
+ sw->select, sw->control, sw->status, sw->intr, sw->intr_enable);
+ }
+ out_8(&sw->control_bic, DO_SEEK);
+ out_8(&sw->select, RELAX);
+ out_8(&sw->intr_enable, 0);
+ if (fs->state == settling && swim3_readbit(fs, SEEK_COMPLETE)) {
+ /* printk(KERN_DEBUG "swim3: missed settling interrupt\n"); */
+ fs->state = locating;
+ act(fs);
+ return;
+ }
+ printk(KERN_ERR "swim3: seek timeout\n");
+ end_request(0);
+ fs->state = idle;
+ start_request(fs);
+}
+
+static void xfer_timeout(unsigned long data)
+{
+ struct floppy_state *fs = (struct floppy_state *) data;
+ volatile struct swim3 *sw = fs->swim3;
+ struct dbdma_regs *dr = fs->dma;
+ struct dbdma_cmd *cp = fs->dma_cmd;
+ unsigned long s;
+
+ st_le32(&dr->control, RUN << 16);
+ out_8(&sw->intr_enable, 0);
+ out_8(&sw->control_bic, WRITE_SECTORS | SCAN_TRACK);
+ out_8(&sw->select, RELAX);
+ if (CURRENT->cmd == WRITE)
+ ++cp;
+ if (ld_le16(&cp->xfer_status) != 0)
+ s = fs->scount - ((ld_le16(&cp->res_count) + 511) >> 9);
+ else
+ s = 0;
+ CURRENT->sector += s;
+ CURRENT->current_nr_sectors -= s;
+ printk(KERN_ERR "swim3: timeout %sing sector %ld\n",
+ (CURRENT->cmd==WRITE? "writ": "read"), CURRENT->sector);
+ end_request(0);
+ fs->state = idle;
+ start_request(fs);
+}
+
+static void swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct floppy_state *fs = (struct floppy_state *) dev_id;
+ volatile struct swim3 *sw = fs->swim3;
+ int intr, err, n;
+ int stat, resid;
+ struct dbdma_regs *dr;
+ struct dbdma_cmd *cp;
+
+ err = in_8(&sw->error);
+ intr = in_8(&sw->intr);
+ if ((intr & ERROR) && fs->state != do_transfer)
+ printk(KERN_ERR "swim3_interrupt, state=%d, cmd=%x, intr=%x, err=%x\n",
+ fs->state, CURRENT->cmd, intr, err);
+ switch (fs->state) {
+ case locating:
+ if (intr & SEEN_SECTOR) {
+ out_8(&sw->control_bic, SCAN_TRACK);
+ out_8(&sw->select, RELAX);
+ out_8(&sw->intr_enable, 0);
+ del_timer(&fs->timeout);
+ if (sw->ctrack == 0xff) {
+ printk(KERN_ERR "swim3: seen sector but cyl=ff?\n");
+ fs->cur_cyl = -1;
+ if (fs->retries > 5) {
+ end_request(0);
+ fs->state = idle;
+ start_request(fs);
+ } else {
+ fs->state = jogging;
+ act(fs);
+ }
+ break;
+ }
+ fs->cur_cyl = sw->ctrack;
+ fs->cur_sector = sw->csect;
+ if (fs->expect_cyl != -1 && fs->expect_cyl != fs->cur_cyl)
+ printk(KERN_ERR "swim3: expected cyl %d, got %d\n",
+ fs->expect_cyl, fs->cur_cyl);
+ fs->state = do_transfer;
+ act(fs);
+ }
+ break;
+ case seeking:
+ case jogging:
+ if (sw->nseek == 0) {
+ out_8(&sw->control_bic, DO_SEEK);
+ out_8(&sw->select, RELAX);
+ out_8(&sw->intr_enable, 0);
+ del_timer(&fs->timeout);
+ if (fs->state == seeking)
+ ++fs->retries;
+ fs->state = settling;
+ act(fs);
+ }
+ break;
+ case settling:
+ out_8(&sw->intr_enable, 0);
+ del_timer(&fs->timeout);
+ act(fs);
+ break;
+ case do_transfer:
+ if ((intr & (ERROR | TRANSFER_DONE)) == 0)
+ break;
+ dr = fs->dma;
+ cp = fs->dma_cmd;
+ st_le32(&dr->control, RUN << 16);
+ out_8(&sw->intr_enable, 0);
+ out_8(&sw->control_bic, WRITE_SECTORS | SCAN_TRACK);
+ out_8(&sw->select, RELAX);
+ del_timer(&fs->timeout);
+ if (CURRENT->cmd == WRITE)
+ ++cp;
+ stat = ld_le16(&cp->xfer_status);
+ resid = ld_le16(&cp->res_count);
+ if (intr & ERROR) {
+ n = fs->scount - 1 - resid / 512;
+ if (n > 0) {
+ CURRENT->sector += n;
+ CURRENT->current_nr_sectors -= n;
+ CURRENT->buffer += n * 512;
+ fs->req_sector += n;
+ }
+ if (fs->retries < 5) {
+ ++fs->retries;
+ act(fs);
+ } else {
+ printk("swim3: error %sing block %ld (err=%x)\n",
+ CURRENT->cmd == WRITE? "writ": "read",
+ CURRENT->sector, err);
+ end_request(0);
+ fs->state = idle;
+ }
+ } else {
+ if ((stat & ACTIVE) == 0 || resid != 0) {
+ /* musta been an error */
+ printk(KERN_ERR "swim3: fd dma: stat=%x resid=%d\n", stat, resid);
+ printk(KERN_ERR " state=%d, cmd=%x, intr=%x, err=%x\n",
+ fs->state, CURRENT->cmd, intr, err);
+ end_request(0);
+ fs->state = idle;
+ start_request(fs);
+ break;
+ }
+ CURRENT->sector += fs->scount;
+ CURRENT->current_nr_sectors -= fs->scount;
+ CURRENT->buffer += fs->scount * 512;
+ if (CURRENT->current_nr_sectors <= 0) {
+ end_request(1);
+ fs->state = idle;
+ } else {
+ fs->req_sector += fs->scount;
+ if (fs->req_sector > fs->secpertrack) {
+ fs->req_sector -= fs->secpertrack;
+ if (++fs->head > 1) {
+ fs->head = 0;
+ ++fs->req_cyl;
+ }
+ }
+ act(fs);
+ }
+ }
+ if (fs->state == idle)
+ start_request(fs);
+ break;
+ default:
+ printk(KERN_ERR "swim3: don't know what to do in state %d\n", fs->state);
+ }
+}
+
+static void fd_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+}
+
+static int grab_drive(struct floppy_state *fs, enum swim_state state,
+ int interruptible)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (fs->state != idle) {
+ ++fs->wanted;
+ while (fs->state != available) {
+ if (interruptible
+ && (current->signal & ~current->blocked)) {
+ --fs->wanted;
+ restore_flags(flags);
+ return -EINTR;
+ }
+ interruptible_sleep_on(&fs->wait);
+ }
+ --fs->wanted;
+ }
+ fs->state = state;
+ restore_flags(flags);
+ return 0;
+}
+
+static void release_drive(struct floppy_state *fs)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ fs->state = idle;
+ start_request(fs);
+ restore_flags(flags);
+}
+
+static int fd_eject(struct floppy_state *fs)
+{
+ int err, n;
+
+ err = grab_drive(fs, ejecting, 1);
+ if (err)
+ return err;
+ swim3_action(fs, EJECT);
+ for (n = 2*HZ; n > 0; --n) {
+ if (swim3_readbit(fs, RELAX))
+ break;
+ if ((current->signal & ~current->blocked) != 0) {
+ err = -EINTR;
+ break;
+ }
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + 1;
+ schedule();
+ }
+ fs->ejected = 1;
+ release_drive(fs);
+ return err;
+}
+
+static struct floppy_struct floppy_type =
+ { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }; /* 7 1.44MB 3.5" */
+
+static int floppy_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long param)
+{
+ struct floppy_state *fs;
+ int err;
+
+ if (((cmd & 0x80) && !suser())
+ || ((cmd & 0x40) && !(filp && (filp->f_mode & IOCTL_MODE_BIT))))
+ return -EPERM;
+
+ fs = &floppy_states[0];
+ switch (cmd) {
+ case FDEJECT:
+ if (fs->ref_count != 1)
+ return -EBUSY;
+ err = fd_eject(fs);
+ return err;
+ case FDGETPRM:
+ err = copy_to_user((void *) param, (void *) &floppy_type,
+ sizeof(struct floppy_struct));
+ return err;
+ }
+ return -ENOIOCTLCMD;
+}
+
+static int floppy_open(struct inode *inode, struct file *filp)
+{
+ struct floppy_state *fs;
+ volatile struct swim3 *sw;
+ int n, err;
+
+ if (MINOR(inode->i_rdev) != 0)
+ return -ENODEV;
+ fs = &floppy_states[0];
+ sw = fs->swim3;
+ err = 0;
+ if (fs->ref_count == 0) {
+ out_8(&sw->intr_enable, 0);
+ out_8(&sw->control_bis, DRIVE_ENABLE | INTR_ENABLE);
+ swim3_action(fs, MOTOR_ON);
+ fs->write_prot = -1;
+ fs->cur_cyl = -1;
+ for (n = HZ; n > 0; --n) {
+ if (swim3_readbit(fs, SEEK_COMPLETE))
+ break;
+ if ((current->signal & ~current->blocked) != 0) {
+ err = -EINTR;
+ break;
+ }
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + 1;
+ schedule();
+ }
+ if (err == 0 && (swim3_readbit(fs, SEEK_COMPLETE) == 0
+ || swim3_readbit(fs, DISK_IN) == 0))
+ err = -ENXIO;
+ swim3_action(fs, 9);
+
+ } else if (fs->ref_count == -1 || filp->f_flags & O_EXCL)
+ return -EBUSY;
+
+ if (err == 0 && filp && (filp->f_flags & O_NDELAY) == 0
+ && (filp->f_mode & 3)) {
+ check_disk_change(inode->i_rdev);
+ if (fs->ejected)
+ err = -ENXIO;
+ }
+
+ if (err == 0 && filp && (filp->f_flags & (O_WRONLY | O_RDWR))) {
+ if (fs->write_prot < 0)
+ fs->write_prot = swim3_readbit(fs, WRITE_PROT);
+ if (fs->write_prot)
+ err = -EROFS;
+ }
+
+ if (err) {
+ if (fs->ref_count == 0) {
+ swim3_action(fs, MOTOR_OFF);
+ out_8(&sw->control_bic, DRIVE_ENABLE | INTR_ENABLE);
+ }
+ return err;
+ }
+
+ if (filp->f_flags & O_EXCL)
+ fs->ref_count = -1;
+ else
+ ++fs->ref_count;
+
+ /* Allow ioctls if we have write-permissions even if read-only open */
+ if ((filp->f_mode & 2) || (permission(inode, 2) == 0))
+ filp->f_mode |= IOCTL_MODE_BIT;
+ if (filp->f_mode & 2)
+ filp->f_mode |= OPEN_WRITE_BIT;
+
+ return 0;
+}
+
+static int floppy_release(struct inode *inode, struct file *filp)
+{
+ struct floppy_state *fs;
+ volatile struct swim3 *sw;
+
+ if (MINOR(inode->i_rdev) != 0)
+ return -ENXIO;
+ fs = &floppy_states[0];
+ if (filp == 0 || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
+ block_fsync(inode, filp);
+ sw = fs->swim3;
+ if (fs->ref_count > 0 && --fs->ref_count == 0) {
+ swim3_action(fs, MOTOR_OFF);
+ out_8(&sw->control_bic, 0xff);
+ }
+ return 0;
+}
+
+static int floppy_check_change(kdev_t dev)
+{
+ struct floppy_state *fs;
+
+ if (MAJOR(dev) != MAJOR_NR || MINOR(dev) != 0)
+ return 0;
+ fs = &floppy_states[0];
+ return fs->ejected;
+}
+
+static int floppy_revalidate(kdev_t dev)
+{
+ struct floppy_state *fs;
+ volatile struct swim3 *sw;
+ int ret, n;
+
+ if (MAJOR(dev) != MAJOR_NR || MINOR(dev) != 0)
+ return 0;
+ fs = &floppy_states[0];
+ sw = fs->swim3;
+ grab_drive(fs, revalidating, 0);
+ out_8(&sw->intr_enable, 0);
+ out_8(&sw->control_bis, DRIVE_ENABLE | INTR_ENABLE);
+ swim3_action(fs, MOTOR_ON);
+ fs->write_prot = -1;
+ fs->cur_cyl = -1;
+ for (n = HZ; n > 0; --n) {
+ if (swim3_readbit(fs, SEEK_COMPLETE))
+ break;
+ if ((current->signal & ~current->blocked) != 0)
+ break;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + 1;
+ schedule();
+ }
+ ret = swim3_readbit(fs, SEEK_COMPLETE) == 0
+ || swim3_readbit(fs, DISK_IN) == 0;
+ if (ret)
+ swim3_action(fs, MOTOR_OFF);
+ else {
+ fs->ejected = 0;
+ swim3_action(fs, 9);
+ }
+
+ release_drive(fs);
+ return ret;
+}
+
+static long floppy_read(struct inode *inode, struct file *filp,
+ char *buf, unsigned long count)
+{
+ struct floppy_state *fs;
+
+ if (MINOR(inode->i_rdev) != 0)
+ return -ENODEV;
+ fs = &floppy_states[0];
+ if (fs->ejected)
+ return -ENXIO;
+ return block_read(inode, filp, buf, count);
+}
+
+static long floppy_write(struct inode *inode, struct file *filp,
+ const char *buf, unsigned long count)
+{
+ struct floppy_state *fs;
+
+ if (MINOR(inode->i_rdev) != 0)
+ return -ENODEV;
+ fs = &floppy_states[0];
+ if (fs->ejected)
+ return -ENXIO;
+ return block_write(inode, filp, buf, count);
+}
+
+static void floppy_off(unsigned int nr)
+{
+}
+
+static struct file_operations floppy_fops = {
+ NULL, /* lseek */
+ floppy_read, /* read */
+ floppy_write, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ floppy_ioctl, /* ioctl */
+ NULL, /* mmap */
+ floppy_open, /* open */
+ floppy_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ floppy_check_change, /* check_media_change */
+ floppy_revalidate, /* revalidate */
+};
+
+int swim3_init(void)
+{
+ struct device_node *swims;
+ struct floppy_state *fs = &floppy_states[0];
+ volatile struct swim3 *sw;
+
+ swims = find_devices("swim3");
+ if (swims == NULL)
+ return 0;
+
+ if (swims->next != NULL)
+ printk(KERN_ERR "Warning: only using first SWIM3 floppy controller\n");
+ if (swims->n_addrs != 2 || swims->n_intrs != 2) {
+ printk(KERN_ERR "swim3: expecting 2 addrs and 2 intrs! (%d, %d)\n",
+ swims->n_addrs, swims->n_intrs);
+ return -EINVAL;
+ }
+
+ if (register_blkdev(MAJOR_NR, "fd", &floppy_fops)) {
+ printk(KERN_ERR "Unable to get major %d for floppy\n",
+ MAJOR_NR);
+ return -EBUSY;
+ }
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ blksize_size[MAJOR_NR] = floppy_blocksizes;
+ blk_size[MAJOR_NR] = floppy_sizes;
+
+ memset(fs, 0, sizeof(*fs));
+ fs->state = idle;
+ fs->swim3 = (volatile struct swim3 *) swims->addrs[0].address;
+ fs->dma = (struct dbdma_regs *) swims->addrs[1].address;
+ fs->swim3_intr = swims->intrs[0];
+ fs->dma_intr = swims->intrs[1];
+ fs->cur_cyl = -1;
+ fs->cur_sector = -1;
+ fs->secpercyl = 36;
+ fs->secpertrack = 18;
+ fs->total_secs = 2880;
+
+ fs->dma_cmd = (struct dbdma_cmd *) DBDMA_ALIGN(fs->dbdma_cmd_space);
+ memset(fs->dma_cmd, 0, 2 * sizeof(struct dbdma_cmd));
+ st_le16(&fs->dma_cmd[1].command, DBDMA_STOP);
+
+ if (request_irq(fs->swim3_intr, swim3_interrupt, 0, "SWIM3", fs)) {
+ printk(KERN_ERR "Couldn't get irq %d for SWIM3\n", fs->swim3_intr);
+ return -EBUSY;
+ }
+ if (request_irq(fs->dma_intr, fd_dma_interrupt, 0, "SWIM3-dma", fs)) {
+ printk(KERN_ERR "Couldn't get irq %d for SWIM3 DMA",
+ fs->dma_intr);
+ return -EBUSY;
+ }
+
+ sw = fs->swim3;
+ out_8(&sw->mode, 0x95);
+ out_8(&sw->control_bic, 0xff);
+ out_8(&sw->reg5, 0x28);
+
+ do_floppy = NULL;
+
+ printk(KERN_INFO "fd0: SWIM3 floppy controller\n");
+
+ return 0;
+}
diff --git a/drivers/char/.cvsignore b/drivers/char/.cvsignore
index 1b9baceaa..764e0a482 100644
--- a/drivers/char/.cvsignore
+++ b/drivers/char/.cvsignore
@@ -1,3 +1,4 @@
.depend
conmakehash
+consolemap_deftbl.c
uni_hash.tbl
diff --git a/drivers/char/Config.in b/drivers/char/Config.in
index 65407caf6..eee723670 100644
--- a/drivers/char/Config.in
+++ b/drivers/char/Config.in
@@ -34,7 +34,11 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
tristate 'Hayes ESP serial port support' CONFIG_ESPSERIAL
if [ "$CONFIG_ESPSERIAL" = "y" -o "$CONFIG_ESPSERIAL" = "m" ]; then
int ' DMA channel' CONFIG_ESPSERIAL_DMA_CHANNEL 1
- int ' FIFO trigger level' CONFIG_ESPSERIAL_TRIGGER_LEVEL 768
+ int ' Receive FIFO trigger level' CONFIG_ESPSERIAL_RX_TRIGGER 768
+ int ' Transmit FIFO trigger level' CONFIG_ESPSERIAL_TX_TRIGGER 768
+ int ' Hardware flow off level' CONFIG_ESPSERIAL_FLOW_OFF 1016
+ int ' Hardware flow on level' CONFIG_ESPSERIAL_FLOW_ON 944
+ int ' Receiver timeout' CONFIG_ESPSERIAL_RX_TMOUT 128
fi
fi
if [ "$CONFIG_PARPORT" != "n" ]; then
@@ -97,8 +101,12 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then
fi
tristate ' Software Watchdog' CONFIG_SOFT_WATCHDOG
tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG
+ tristate ' Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT
fi
bool 'Enhanced Real Time Clock Support' CONFIG_RTC
+if [ "$CONFIG_PPC" = "y" ]; then
+ bool 'Tadpole ANA H8 Support' CONFIG_H8
+fi
tristate '/dev/nvram support' CONFIG_NVRAM
tristate 'PC joystick support' CONFIG_JOYSTICK
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index ad3664204..e0cb4dd44 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -23,7 +23,7 @@ M_OBJS :=
L_OBJS := tty_io.o n_tty.o tty_ioctl.o pty.o mem.o random.o
ifdef CONFIG_VT
-L_OBJS += console.o vt.o vc_screen.o consolemap.o
+L_OBJS += console.o vt.o vc_screen.o consolemap.o consolemap_deftbl.o
LX_OBJS += selection.o
endif
@@ -206,6 +206,16 @@ else
endif
endif
+ifeq ($(CONFIG_ACQUIRE_WDT),y)
+M = y
+L_OBJS += acquirewdt.o
+else
+ ifeq ($(CONFIG_ACQUIRE_WDT),m)
+ M_OBJS += acquirewdt.o
+ MM = m
+ endif
+endif
+
ifeq ($(CONFIG_AMIGAMOUSE),y)
M = y
L_OBJS += amigamouse.o
@@ -299,6 +309,11 @@ LX_OBJS += apm_bios.o
M = y
endif
+ifdef CONFIG_H8
+LX_OBJS += h8.o
+M = y
+endif
+
ifdef M
LX_OBJS += misc.o
else
@@ -321,17 +336,29 @@ ifdef CONFIG_VT
endif
endif
-include $(TOPDIR)/Rules.make
+ifeq ($(CONFIG_HFMODEM),y)
+ALL_SUB_DIRS += hfmodem
+SUB_DIRS += hfmodem
+L_OBJS += hfmodem/hfmodem.o
+else
+ ifeq ($(CONFIG_HFMODEM),m)
+ ALL_SUB_DIRS += hfmodem
+ MOD_SUB_DIRS += hfmodem
+ endif
+endif
-fastdep: uni_hash.tbl
+include $(TOPDIR)/Rules.make
-consolemap.o:
+fastdep:
conmakehash: conmakehash.c
$(HOSTCC) -o conmakehash conmakehash.c
-uni_hash.tbl: $(FONTMAPFILE) conmakehash
- ./conmakehash $(FONTMAPFILE) > uni_hash.tbl
+consolemap_deftbl.c: $(FONTMAPFILE) conmakehash
+ ./conmakehash $(FONTMAPFILE) > consolemap_deftbl.c
+
+consolemap_deftbl.o: consolemap_deftbl.c $(TOPDIR)/include/linux/types.h
defkeymap.c: defkeymap.map
loadkeys --mktable defkeymap.map > defkeymap.c
+
diff --git a/drivers/char/acquirewdt.c b/drivers/char/acquirewdt.c
new file mode 100644
index 000000000..aeb7b72fc
--- /dev/null
+++ b/drivers/char/acquirewdt.c
@@ -0,0 +1,221 @@
+/*
+ * Acquire Single Board Computer Watchdog Timer driver for Linux 2.1.x
+ *
+ * Based on wdt.c. Original copyright messages:
+ *
+ * (c) Copyright 1996 Alan Cox <alan@cymru.net>, All Rights Reserved.
+ * http://www.cymru.net
+ *
+ * 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.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+
+static int acq_is_open=0;
+
+/*
+ * You must set these - there is no sane way to probe for this board.
+ */
+
+#define WDT_STOP 0x43
+#define WDT_START 0x443
+#define WATCHDOG_MINOR 130
+
+#define WD_TIMO (100*60) /* 1 minute */
+
+
+/*
+ * Kernel methods.
+ */
+
+
+static void acq_ping(void)
+{
+ /* Write a watchdog value */
+ inb_p(WDT_START);
+}
+
+static long acq_write(struct inode *inode, struct file *file, const char *buf, unsigned long count)
+{
+ if(count)
+ {
+ acq_ping();
+ return 1;
+ }
+ return 0;
+}
+
+static long acq_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+{
+ return -EINVAL;
+}
+
+
+
+static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ static struct watchdog_info ident=
+ {
+ WDIOF_KEEPALIVEPING, 1, "Acquire WDT"
+ };
+
+ switch(cmd)
+ {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ if (copy_to_user((int *)arg, &acq_is_open, sizeof(int)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_KEEPALIVE:
+ acq_ping();
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int acq_open(struct inode *inode, struct file *file)
+{
+ switch(MINOR(inode->i_rdev))
+ {
+ case WATCHDOG_MINOR:
+ if(acq_is_open)
+ return -EBUSY;
+ MOD_INC_USE_COUNT;
+ /*
+ * Activate
+ */
+
+ acq_is_open=1;
+ inb_p(WDT_START);
+ return 0;
+ default:
+ return -ENODEV;
+ }
+}
+
+static int acq_close(struct inode *inode, struct file *file)
+{
+ if(MINOR(inode->i_rdev)==WATCHDOG_MINOR)
+ {
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ inb_p(WDT_STOP);
+#endif
+ acq_is_open=0;
+ }
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Notifier for system down
+ */
+
+static int acq_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if(code==SYS_DOWN || code==SYS_HALT)
+ {
+ /* Turn the card off */
+ inb_p(WDT_STOP);
+ }
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+
+static struct file_operations acq_fops = {
+ NULL,
+ acq_read,
+ acq_write,
+ NULL, /* No Readdir */
+ NULL, /* No Select */
+ acq_ioctl,
+ NULL, /* No mmap */
+ acq_open,
+ acq_close
+};
+
+static struct miscdevice acq_miscdev=
+{
+ WATCHDOG_MINOR,
+ "Acquire WDT",
+ &acq_fops
+};
+
+
+/*
+ * The WDT card needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block acq_notifier=
+{
+ acq_notify_sys,
+ NULL,
+ 0
+};
+
+#ifdef MODULE
+
+#define acq_init init_module
+
+void cleanup_module(void)
+{
+ misc_deregister(&acq_miscdev);
+ unregister_reboot_notifier(&acq_notifier);
+ release_region(WDT_STOP,1);
+ release_region(WDT_START,1);
+}
+
+#endif
+
+__initfunc(int acq_init(void))
+{
+ printk("WDT driver for Acquire single board computer initialising.\n");
+
+ misc_register(&acq_miscdev);
+ request_region(WDT_STOP, 1, "Acquire WDT");
+ request_region(WDT_START, 1, "Acquire WDT");
+ unregister_reboot_notifier(&acq_notifier);
+ return 0;
+}
+
diff --git a/drivers/char/apm_bios.c b/drivers/char/apm_bios.c
index bf7a04633..67ec582e4 100644
--- a/drivers/char/apm_bios.c
+++ b/drivers/char/apm_bios.c
@@ -13,7 +13,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
- * $Id: apm_bios.c,v 0.22 1995/03/09 14:12:02 sfr Exp $
+ * $Id: apm_bios.c,v 1.1.1.1 1997/06/01 03:17:29 ralf Exp $
*
* October 1995, Rik Faith (faith@cs.unc.edu):
* Minor enhancements and updates (to the patch set) for 1.3.x
@@ -40,6 +40,10 @@
* is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
* <jtoth@princeton.edu>); improve interaction between
* screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
+ * 1.2a:Simple change to stop mysterious bug reports with SMP also added
+ * levels to the printk calls. APM is not defined for SMP machines.
+ * The new replacment for it is, but Linux doesn't yet support this.
+ * Alan Cox Linux 2.1.55
*
* Reference:
*
@@ -492,9 +496,9 @@ static void apm_error(char *str, int err)
for (i = 0; i < ERROR_COUNT; i++)
if (error_table[i].key == err) break;
if (i < ERROR_COUNT)
- printk("apm_bios: %s: %s\n", str, error_table[i].msg);
+ printk(KERN_NOTICE "apm_bios: %s: %s\n", str, error_table[i].msg);
else
- printk("apm_bios: %s: unknown error code %#2.2x\n", str, err);
+ printk(KERN_NOTICE "apm_bios: %s: unknown error code %#2.2x\n", str, err);
}
/* Called from console driver -- must make sure apm_enabled. */
@@ -725,10 +729,10 @@ static void check_events(void)
}
#ifdef APM_DEBUG
if (event <= NR_APM_EVENT_NAME)
- printk("APM BIOS received %s notify\n",
+ printk(KERN_DEBUG "APM BIOS received %s notify\n",
apm_event_name[event - 1]);
else
- printk("APM BIOS received unknown event 0x%02x\n",
+ printk(KERN_DEBUG "APM BIOS received unknown event 0x%02x\n",
event);
#endif
}
@@ -802,7 +806,7 @@ void apm_do_busy(void)
static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
{
if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
- printk("apm_bios: %s passed bad filp", func);
+ printk(KERN_ERR "apm_bios: %s passed bad filp", func);
return 1;
}
return 0;
@@ -940,7 +944,7 @@ static int do_release(struct inode * inode, struct file * filp)
as1 = as1->next)
;
if (as1 == NULL)
- printk("apm_bios: filp not in user list");
+ printk(KERN_ERR "apm_bios: filp not in user list");
else
as1->next = as->next;
}
@@ -954,7 +958,7 @@ static int do_open(struct inode * inode, struct file * filp)
as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
if (as == NULL) {
- printk("apm_bios: cannot allocate struct of size %d bytes",
+ printk(KERN_ERR "apm_bios: cannot allocate struct of size %d bytes",
sizeof(*as));
return -ENOMEM;
}
@@ -1070,17 +1074,21 @@ __initfunc(void apm_bios_init(void))
char * bat_stat;
static struct proc_dir_entry *ent;
+#ifdef __SMP__
+ printk(KERN_NOTICE "APM disabled: APM is not SMP safe.\n");
+ return;
+#endif
if (apm_bios_info.version == 0) {
- printk("APM BIOS not found.\n");
+ printk(KERN_INFO "APM BIOS not found.\n");
return;
}
- printk("APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
+ printk(KERN_INFO "APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
((apm_bios_info.version >> 8) & 0xff) + '0',
(apm_bios_info.version & 0xff) + '0',
apm_bios_info.flags,
driver_version);
if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
- printk(" No 32 bit BIOS support\n");
+ printk(KERN_INFO " No 32 bit BIOS support\n");
return;
}
@@ -1091,7 +1099,7 @@ __initfunc(void apm_bios_init(void))
if (apm_bios_info.version == 0x001)
apm_bios_info.version = 0x100;
- printk(" Entry %x:%lx cseg16 %x dseg %x",
+ printk(KERN_INFO " Entry %x:%lx cseg16 %x dseg %x",
apm_bios_info.cseg, apm_bios_info.offset,
apm_bios_info.cseg_16, apm_bios_info.dseg);
if (apm_bios_info.version > 0x100)
@@ -1139,7 +1147,7 @@ __initfunc(void apm_bios_init(void))
error = apm_get_power_status(&bx, &cx, &dx);
if (error)
- printk(" Power status not available\n");
+ printk(KERN_INFO " Power status not available\n");
else {
switch ((bx >> 8) & 0xff) {
case 0: power_stat = "off line"; break;
@@ -1154,7 +1162,7 @@ __initfunc(void apm_bios_init(void))
case 3: bat_stat = "charging"; break;
default: bat_stat = "unknown"; break;
}
- printk(" AC %s, battery status %s, battery life ",
+ printk(KERN_INFO " AC %s, battery status %s, battery life ",
power_stat, bat_stat);
if ((cx & 0xff) == 0xff)
printk("unknown\n");
diff --git a/drivers/char/conmakehash.c b/drivers/char/conmakehash.c
index ce59eda4f..89215aa17 100644
--- a/drivers/char/conmakehash.c
+++ b/drivers/char/conmakehash.c
@@ -7,7 +7,7 @@
* memory not allocated by kmalloc(), and doing our own memory management
* just for this seems like massive overkill.
*
- * Copyright (C) 1995 H. Peter Anvin
+ * Copyright (C) 1995-1997 H. Peter Anvin
*
* This program is a part of the Linux kernel, and may be freely
* copied under the terms of the GNU General Public License (GPL),
@@ -248,18 +248,15 @@ int main(int argc, char *argv[])
printf("\
/*\n\
- * uni_hash.tbl\n\
- *\n\
* Do not edit this file; it was automatically generated by\n\
*\n\
- * conmakehash %s > uni_hash.tbl\n\
+ * conmakehash %s > [this file]\n\
*\n\
*/\n\
\n\
#include <linux/types.h>\n\
-#include <linux/kd.h>\n\
\n\
-static u8 dfont_unicount[%d] = \n\
+u8 dfont_unicount[%d] = \n\
{\n\t", argv[1], fontlen);
for ( i = 0 ; i < fontlen ; i++ )
@@ -272,20 +269,20 @@ static u8 dfont_unicount[%d] = \n\
else
printf(", ");
}
-
- printf("\nstatic u16 dfont_unitable[%d] = \n{\n\t", nuni);
-
+
+ printf("\nu16 dfont_unitable[%d] = \n{\n\t", nuni);
+
fp0 = 0;
nent = 0;
for ( i = 0 ; i < nuni ; i++ )
{
- while ( nent >= unicount[fp0] )
- {
- fp0++;
- nent = 0;
- }
- printf("0x%04x", unitable[fp0][nent++]);
- if ( i == nuni-1 )
+ while ( nent >= unicount[fp0] )
+ {
+ fp0++;
+ nent = 0;
+ }
+ printf("0x%04x", unitable[fp0][nent++]);
+ if ( i == nuni-1 )
printf("\n};");
else if ( i % 8 == 7 )
printf(",\n\t");
diff --git a/drivers/char/consolemap.c b/drivers/char/consolemap.c
index 5b0e61fea..5a9300bf8 100644
--- a/drivers/char/consolemap.c
+++ b/drivers/char/consolemap.c
@@ -307,9 +307,10 @@ int con_get_trans_new(ushort * arg)
* this 3-level paged table scheme to be comparable to a hash table.
*/
-#include "uni_hash.tbl" /* Include hash tables & parameters */
+extern u8 dfont_unicount[]; /* Defined in console_defmap.c */
+extern u16 dfont_unitable[];
-int hashtable_contents_valid = 0; /* Use ASCII-only mode for bootup*/
+int hashtable_contents_valid = 0; /* Use ASCII-only mode for bootup */
static u16 **uni_pagedir[32] =
{
diff --git a/drivers/char/esp.c b/drivers/char/esp.c
index 42ed91080..636c6369d 100644
--- a/drivers/char/esp.c
+++ b/drivers/char/esp.c
@@ -29,10 +29,9 @@
* by Chris Faylor.
*
* Most recent changes: (Andrew J. Robinson)
- * Don't cause a kernel panic if memory could not be allocated or if the
- * device couldn't be registered.
- * Always set RTS when transitioning away from B0 status (since the
- * flow is really being handled by the ESP card).
+ * Added rx_trigger, tx_trigger, flow_off, flow_on, and rx_timeout options.
+ * ESP enhanced mode configuration can be viewed/changed by a patched
+ * version of setserial.
*
* This module exports the following rs232 io functions:
*
@@ -76,13 +75,20 @@ static int irq[NR_PRIMARY] = {0,0,0,0,0,0,0,0}; /* IRQ for each base port */
static unsigned int divisor[NR_PRIMARY] = {0,0,0,0,0,0,0,0};
/* custom divisor for each port */
static unsigned int dma = CONFIG_ESPSERIAL_DMA_CHANNEL; /* DMA channel */
-static unsigned int trigger = CONFIG_ESPSERIAL_TRIGGER_LEVEL;
- /* FIFO trigger level */
+static unsigned int rx_trigger = CONFIG_ESPSERIAL_RX_TRIGGER;
+static unsigned int tx_trigger = CONFIG_ESPSERIAL_TX_TRIGGER;
+static unsigned int flow_off = CONFIG_ESPSERIAL_FLOW_OFF;
+static unsigned int flow_on = CONFIG_ESPSERIAL_FLOW_ON;
+static unsigned int rx_timeout = CONFIG_ESPSERIAL_RX_TMOUT;
MODULE_PARM(irq, "1-8i");
MODULE_PARM(divisor, "1-8i");
MODULE_PARM(dma, "i");
-MODULE_PARM(trigger, "i");
+MODULE_PARM(rx_trigger, "i");
+MODULE_PARM(tx_trigger, "i");
+MODULE_PARM(flow_off, "i");
+MODULE_PARM(flow_on, "i");
+MODULE_PARM(rx_timeout, "i");
/* END */
static char *dma_buffer;
@@ -93,9 +99,9 @@ static int dma_bytes;
#define WAKEUP_CHARS 1024
static char *serial_name = "ESP serial driver";
-static char *serial_version = "1.5";
+static char *serial_version = "1.6";
-DECLARE_TASK_QUEUE(tq_esp);
+static DECLARE_TASK_QUEUE(tq_esp);
static struct tty_driver esp_driver, esp_callout_driver;
static int serial_refcount;
@@ -200,7 +206,8 @@ static inline unsigned int serial_in(struct esp_struct *info, int offset)
return inb(info->port + offset);
}
-static inline void serial_out(struct esp_struct *info, int offset, int value)
+static inline void serial_out(struct esp_struct *info, int offset,
+ unsigned char value)
{
outb(value, info->port+offset);
}
@@ -382,6 +389,7 @@ static _INLINE_ void receive_chars_dma_done(struct esp_struct *info,
info->stat_flags &= ~ESP_STAT_DMA_RX;
bytes_left = num_bytes = dma_bytes - get_dma_residue(dma);
+ info->icount.rx += num_bytes;
buffer = &(tty->flip);
if (info->tty_buf->count && (tty->flip.count < TTY_FLIPBUF_SIZE))
@@ -420,13 +428,18 @@ static _INLINE_ void receive_chars_dma_done(struct esp_struct *info,
buffer->flag_buf_ptr--;
} else if (status & 0x10) {
*buffer->flag_buf_ptr = TTY_BREAK;
+ (info->icount.brk)++;
if (info->flags & ASYNC_SAK)
do_SAK(tty);
- } else if (status & 0x08)
+ } else if (status & 0x08) {
*buffer->flag_buf_ptr = TTY_FRAME;
- else if (status & 0x04)
+ (info->icount.frame)++;
+ }
+ else if (status & 0x04) {
*buffer->flag_buf_ptr = TTY_PARITY;
-
+ (info->icount.parity)++;
+ }
+
buffer->flag_buf_ptr++;
if (buffer == info->tty_buf)
@@ -512,6 +525,7 @@ static _INLINE_ void transmit_chars_dma_done(struct esp_struct *info)
clear_dma_ff(dma);
num_bytes = dma_bytes - get_dma_residue(dma);
+ info->icount.tx += dma_bytes;
if (dma_bytes != num_bytes) {
dma_bytes -= num_bytes;
@@ -759,10 +773,10 @@ static _INLINE_ void esp_basic_init(struct esp_struct * info)
/* set FIFO trigger levels */
serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER);
- serial_out(info, UART_ESI_CMD2, trigger / 256);
- serial_out(info, UART_ESI_CMD2, trigger % 256);
- serial_out(info, UART_ESI_CMD2, trigger / 256);
- serial_out(info, UART_ESI_CMD2, trigger % 256);
+ serial_out(info, UART_ESI_CMD2, rx_trigger >> 8);
+ serial_out(info, UART_ESI_CMD2, rx_trigger);
+ serial_out(info, UART_ESI_CMD2, tx_trigger >> 8);
+ serial_out(info, UART_ESI_CMD2, tx_trigger);
/* Set clock scaling */
serial_out(info, UART_ESI_CMD1, ESI_SET_PRESCALAR);
@@ -776,23 +790,21 @@ static _INLINE_ void esp_basic_init(struct esp_struct * info)
static int startup(struct esp_struct * info)
{
unsigned long flags;
- int retval;
+ int retval=0;
int next_irq;
struct esp_struct *next_info = 0;
unsigned int num_chars;
save_flags(flags); cli();
- if (info->flags & ASYNC_INITIALIZED) {
- restore_flags(flags);
- return 0;
- }
+ if (info->flags & ASYNC_INITIALIZED)
+ goto errout;
if (!info->xmit_buf) {
info->xmit_buf = (unsigned char *)get_free_page(GFP_KERNEL);
if (!info->xmit_buf) {
- restore_flags(flags);
- return -ENOMEM;
+ retval = -ENOMEM;
+ goto errout;
}
}
@@ -803,8 +815,8 @@ static int startup(struct esp_struct * info)
if (!info->tty_buf) {
free_page((unsigned long) info->xmit_buf);
info->xmit_buf = 0;
- restore_flags(flags);
- return -ENOMEM;
+ retval = -ENOMEM;
+ goto errout;
}
memset(info->tty_buf, 0, sizeof(struct tty_flip_buffer));
@@ -838,7 +850,7 @@ static int startup(struct esp_struct * info)
/* set receive character timeout */
serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
- serial_out(info, UART_ESI_CMD2, 0x80);
+ serial_out(info, UART_ESI_CMD2, rx_timeout);
info->stat_flags = 0;
@@ -870,14 +882,13 @@ static int startup(struct esp_struct * info)
}
if (retval) {
- restore_flags(flags);
if (suser()) {
if (info->tty)
set_bit(TTY_IO_ERROR,
&info->tty->flags);
- return 0;
- } else
- return retval;
+ retval = 0;
+ }
+ goto errout;
}
}
@@ -939,6 +950,10 @@ static int startup(struct esp_struct * info)
info->flags |= ASYNC_INITIALIZED;
restore_flags(flags);
return 0;
+
+errout:
+ restore_flags(flags);
+ return retval;
}
/*
@@ -1128,7 +1143,11 @@ static void change_speed(struct esp_struct *info)
}
if (!(cflag & PARODD))
cval |= UART_LCR_EPAR;
-
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ cval |= UART_LCR_SPAR;
+#endif
+
if (!quot) {
quot = quot_table[i];
@@ -1234,10 +1253,10 @@ static void change_speed(struct esp_struct *info)
/* Set high/low water */
serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_LVL);
- serial_out(info, UART_ESI_CMD2, 0x03);
- serial_out(info, UART_ESI_CMD2, 0xfc);
- serial_out(info, UART_ESI_CMD2, (trigger + 4) / 256);
- serial_out(info, UART_ESI_CMD2, (trigger + 4) % 256);
+ serial_out(info, UART_ESI_CMD2, flow_off >> 8);
+ serial_out(info, UART_ESI_CMD2, flow_off);
+ serial_out(info, UART_ESI_CMD2, flow_on >> 8);
+ serial_out(info, UART_ESI_CMD2, flow_on);
restore_flags(flags);
}
@@ -1288,7 +1307,7 @@ static void rs_flush_chars(struct tty_struct *tty)
static int rs_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
- int c, total = 0;
+ int c, ret = 0;
struct esp_struct *info = (struct esp_struct *)tty->driver_data;
unsigned long flags;
@@ -1309,7 +1328,12 @@ static int rs_write(struct tty_struct * tty, int from_user,
break;
if (from_user) {
- copy_from_user(tmp_buf, buf, c);
+ c -= copy_from_user(tmp_buf, buf, c);
+ if (!c) {
+ if (!ret)
+ ret = -EFAULT;
+ break;
+ }
c = MIN(c, MIN(ESP_XMIT_SIZE - info->xmit_cnt - 1,
ESP_XMIT_SIZE - info->xmit_head));
memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
@@ -1320,7 +1344,7 @@ static int rs_write(struct tty_struct * tty, int from_user,
restore_flags(flags);
buf += c;
count -= c;
- total += c;
+ ret += c;
}
if (from_user)
up(&tmp_buf_sem);
@@ -1330,7 +1354,7 @@ static int rs_write(struct tty_struct * tty, int from_user,
serial_out(info, UART_ESI_CMD2, info->IER);
}
restore_flags(flags);
- return total;
+ return ret;
}
static int rs_write_room(struct tty_struct *tty)
@@ -1418,7 +1442,7 @@ static void rs_unthrottle(struct tty_struct * tty)
serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
serial_out(info, UART_ESI_CMD2, info->IER);
serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
- serial_out(info, UART_ESI_CMD2, 0x80);
+ serial_out(info, UART_ESI_CMD2, rx_timeout);
sti();
}
@@ -1441,12 +1465,20 @@ static int get_serial_info(struct esp_struct * info,
tmp.port = info->port;
tmp.irq = info->irq;
tmp.flags = info->flags;
+ tmp.xmit_fifo_size = 1024;
tmp.baud_base = BASE_BAUD;
tmp.close_delay = info->close_delay;
tmp.closing_wait = info->closing_wait;
tmp.custom_divisor = info->custom_divisor;
tmp.hub6 = 0;
- copy_to_user(retinfo,&tmp,sizeof(*retinfo));
+ tmp.reserved_char[0] = 'E';
+ tmp.reserved_char[1] = rx_timeout;
+ tmp.reserved[0] = rx_trigger;
+ tmp.reserved[1] = tx_trigger;
+ tmp.reserved[2] = flow_off;
+ tmp.reserved[3] = flow_on;
+ if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+ return -EFAULT;
return 0;
}
@@ -1456,26 +1488,37 @@ static int set_serial_info(struct esp_struct * info,
struct serial_struct new_serial;
struct esp_struct old_info;
unsigned int change_irq;
+ unsigned int change_flow;
int i, retval = 0;
struct esp_struct *current_async;
- if (!new_info)
+ if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
return -EFAULT;
- copy_from_user(&new_serial,new_info,sizeof(new_serial));
old_info = *info;
if ((new_serial.type != PORT_16550A) ||
- (new_serial.hub6) ||
- (info->port != new_serial.port) ||
- (new_serial.baud_base != BASE_BAUD) ||
- (new_serial.irq > 15) ||
- (new_serial.irq < 2) ||
- (new_serial.irq == 6) ||
- (new_serial.irq == 8) ||
- (new_serial.irq == 13))
+ (new_serial.hub6) ||
+ (info->port != new_serial.port) ||
+ (new_serial.baud_base != BASE_BAUD) ||
+ (new_serial.irq > 15) ||
+ (new_serial.irq < 2) ||
+ (new_serial.irq == 6) ||
+ (new_serial.irq == 8) ||
+ (new_serial.irq == 13) ||
+ (new_serial.reserved[3] >= new_serial.reserved[2]) ||
+ (new_serial.reserved[0] < 1) ||
+ (new_serial.reserved[1] < 1) ||
+ (new_serial.reserved[2] < 1) ||
+ (new_serial.reserved[3] < 1) ||
+ (new_serial.reserved[0] > 1023) ||
+ (new_serial.reserved[1] > 1023) ||
+ (new_serial.reserved[2] > 1023) ||
+ (new_serial.reserved[3] > 1023))
return -EINVAL;
change_irq = new_serial.irq != info->irq;
+ change_flow = ((new_serial.reserved[2] != flow_off) ||
+ (new_serial.reserved[3] != flow_on));
if (change_irq && (info->line % 8))
return -EINVAL;
@@ -1529,6 +1572,37 @@ static int set_serial_info(struct esp_struct * info,
info->custom_divisor = new_serial.custom_divisor;
info->close_delay = new_serial.close_delay * HZ/100;
info->closing_wait = new_serial.closing_wait * HZ/100;
+ flow_off = new_serial.reserved[2];
+ flow_on = new_serial.reserved[3];
+
+ if ((new_serial.reserved[0] != rx_trigger) ||
+ (new_serial.reserved[1] != tx_trigger)) {
+ unsigned long flags;
+
+ rx_trigger = new_serial.reserved[0];
+ tx_trigger = new_serial.reserved[1];
+ save_flags(flags); cli();
+ serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER);
+ serial_out(info, UART_ESI_CMD2, rx_trigger >> 8);
+ serial_out(info, UART_ESI_CMD2, rx_trigger);
+ serial_out(info, UART_ESI_CMD2, tx_trigger >> 8);
+ serial_out(info, UART_ESI_CMD2, tx_trigger);
+ restore_flags(flags);
+ }
+
+ if (((unsigned char)(new_serial.reserved_char[1]) != rx_timeout)) {
+ unsigned long flags;
+
+ rx_timeout = (unsigned char)(new_serial.reserved_char[1]);
+ save_flags(flags); cli();
+
+ if (info->IER & UART_IER_RDI) {
+ serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
+ serial_out(info, UART_ESI_CMD2, rx_timeout);
+ }
+
+ restore_flags(flags);
+ }
if (change_irq) {
/*
@@ -1557,7 +1631,8 @@ check_and_exit:
if (info->flags & ASYNC_INITIALIZED) {
if (((old_info.flags & ASYNC_SPD_MASK) !=
(info->flags & ASYNC_SPD_MASK)) ||
- (old_info.custom_divisor != info->custom_divisor))
+ (old_info.custom_divisor != info->custom_divisor) ||
+ change_flow)
change_speed(info);
} else
retval = startup(info);
@@ -1737,17 +1812,9 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
case TIOCMSET:
return set_modem_info(info, cmd, (unsigned int *) arg);
case TIOCGSERIAL:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct serial_struct));
- if (error)
- return error;
return get_serial_info(info,
(struct serial_struct *) arg);
case TIOCSSERIAL:
- error = verify_area(VERIFY_READ, (void *) arg,
- sizeof(struct serial_struct));
- if (error)
- return error;
return set_serial_info(info,
(struct serial_struct *) arg);
case TIOCSERCONFIG:
@@ -2316,8 +2383,8 @@ static int esp_open(struct tty_struct *tty, struct file * filp)
static _INLINE_ void show_serial_version(void)
{
- printk(KERN_INFO "%s version %s (DMA %u, trigger level %u)\n",
- serial_name, serial_version, dma, trigger);
+ printk(KERN_INFO "%s version %s (DMA %u)\n",
+ serial_name, serial_version, dma);
}
/*
@@ -2406,9 +2473,24 @@ __initfunc(int espserial_init(void))
if ((dma != 1) && (dma != 3))
dma = 1;
- if ((trigger < 1) || (trigger > 1015))
- trigger = 768;
+ if ((rx_trigger < 1) || (rx_trigger > 1023))
+ rx_trigger = 768;
+
+ if ((tx_trigger < 1) || (tx_trigger > 1023))
+ tx_trigger = 768;
+
+ 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 */
diff --git a/drivers/char/h8.c b/drivers/char/h8.c
new file mode 100644
index 000000000..73941a059
--- /dev/null
+++ b/drivers/char/h8.c
@@ -0,0 +1,1272 @@
+/*
+ */
+/*
+ * Hitachi H8/337 Microcontroller driver
+ *
+ * The H8 is used to deal with the power and thermal environment
+ * of a system.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/fcntl.h>
+#include <linux/malloc.h>
+#include <linux/linkage.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#endif
+#include <linux/miscdevice.h>
+#include <linux/lists.h>
+#include <linux/ioport.h>
+
+#define __KERNEL_SYSCALLS__
+#include <asm/unistd.h>
+
+#include "h8.h"
+
+#define DEBUG_H8
+
+#ifdef DEBUG_H8
+#define Dprintk printk
+#else
+#define Dprintk
+#endif
+
+#define XDprintk if(h8_debug==-1)printk
+
+/*
+ * The h8 device is one of the misc char devices.
+ */
+#define H8_MINOR_DEV 140
+
+/*
+ * Forward declarations.
+ */
+int h8_init(void);
+int h8_display_blank(void);
+int h8_display_unblank(void);
+
+static int h8_open(struct inode *, struct file *);
+static void h8_release(struct inode *, struct file *);
+static long h8_read(struct inode *, struct file *, char *, u_long);
+static int h8_select(struct inode *, struct file *, int, select_table *);
+static int h8_ioctl(struct inode *, struct file *, u_int, u_long);
+
+static void h8_intr(int irq, void *dev_id, struct pt_regs *regs);
+
+#ifdef CONFIG_PROC_FS
+static int h8_get_info(char *, char **, off_t, int, int);
+#endif
+
+/*
+ * Support Routines.
+ */
+static void h8_hw_init(void);
+static void h8_start_new_cmd(void);
+static void h8_send_next_cmd_byte(void);
+static void h8_read_event_status(void);
+static void h8_sync(void);
+static void h8_q_cmd(u_char *, int, int);
+static void h8_cmd_done(h8_cmd_q_t *qp);
+static int h8_alloc_queues(void);
+
+static u_long h8_get_cpu_speed(void);
+static int h8_get_curr_temp(u_char curr_temp[]);
+static void h8_get_max_temp(void);
+static void h8_get_upper_therm_thold(void);
+static void h8_set_upper_therm_thold(int);
+static int h8_get_ext_status(u_char stat_word[]);
+
+static int h8_monitor_thread(void *);
+
+static int h8_manage_therm(void);
+static void h8_set_cpu_speed(int speed_divisor);
+
+static void h8_start_monitor_timer(unsigned long secs);
+static void h8_activate_monitor(unsigned long unused);
+
+/* in arch/alpha/kernel/lca.c */
+extern void lca_clock_print(void);
+extern int lca_get_clock(void);
+extern void lca_clock_fiddle(int);
+
+static void h8_set_event_mask(int);
+static void h8_clear_event_mask(int);
+
+/*
+ * Driver structures
+ */
+
+static struct timer_list h8_monitor_timer;
+static int h8_monitor_timer_active = 0;
+
+static char driver_version[] = "X0.0";/* no spaces */
+
+static struct file_operations h8_fops = {
+ NULL, /* lseek */
+ h8_read,
+ NULL, /* write */
+ NULL, /* readdir */
+ h8_select,
+ h8_ioctl,
+ NULL, /* mmap */
+ h8_open,
+ h8_release,
+ NULL, /* fsync */
+ NULL /* fasync */
+};
+
+static struct miscdevice h8_device = {
+ H8_MINOR_DEV,
+ "h8",
+ &h8_fops
+};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry h8_proc_entry = {
+ 0, 3, "h8", S_IFREG | S_IRUGO, 1, 0, 0, 0, 0, h8_get_info
+};
+#endif
+
+union intr_buf intrbuf;
+int intr_buf_ptr;
+union intr_buf xx;
+u_char last_temp;
+
+/*
+ * I/O Macros for register reads and writes.
+ */
+#define H8_READ(a) inb((a))
+#define H8_WRITE(d,a) outb((d),(a))
+
+#define H8_GET_STATUS H8_READ((h8_base) + H8_STATUS_REG_OFF)
+#define H8_READ_DATA H8_READ((h8_base) + H8_DATA_REG_OFF)
+#define WRITE_DATA(d) H8_WRITE((d), h8_base + H8_DATA_REG_OFF)
+#define WRITE_CMD(d) H8_WRITE((d), h8_base + H8_CMD_REG_OFF)
+
+unsigned int h8_base = H8_BASE_ADDR;
+unsigned int h8_irq = H8_IRQ;
+unsigned int h8_state = H8_IDLE;
+unsigned int h8_index = -1;
+unsigned int h8_enabled = 0;
+
+queue_head_t h8_actq, h8_cmdq, h8_freeq;
+
+/*
+ * Globals used in thermal control of Alphabook1.
+ */
+int cpu_speed_divisor = -1;
+int h8_event_mask = 0;
+struct wait_queue *h8_monitor_wait = NULL;
+unsigned int h8_command_mask = 0;
+int h8_uthermal_threshold = DEFAULT_UTHERMAL_THRESHOLD;
+int h8_uthermal_window = UTH_HYSTERESIS;
+int h8_debug = 0xfffffdfc;
+int h8_ldamp = MHZ_115;
+int h8_udamp = MHZ_57;
+u_char h8_current_temp = 0;
+u_char h8_system_temp = 0;
+int h8_sync_channel = 0;
+struct wait_queue *h8_sync_wait = NULL;
+int h8_init_performed;
+
+/* CPU speeds and clock divisor values */
+int speed_tab[6] = {230, 153, 115, 57, 28, 14};
+
+/*
+ * H8 interrupt handler
+ */
+static void h8_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u_char stat_reg, data_reg;
+ h8_cmd_q_t *qp = (h8_cmd_q_t *)QUEUE_FIRST(&h8_actq, link);
+
+ stat_reg = H8_GET_STATUS;
+ data_reg = H8_READ_DATA;
+
+ XDprintk("h8_intr: state %d status 0x%x data 0x%x\n", h8_state, stat_reg, data_reg);
+
+ switch (h8_state) {
+ /* Response to an asynchronous event. */
+ case H8_IDLE: { /* H8_IDLE */
+ if (stat_reg & H8_OFULL) {
+ if (data_reg == H8_INTR) {
+ h8_state = H8_INTR_MODE;
+ /* Executing a command to determine what happened. */
+ WRITE_CMD(H8_RD_EVENT_STATUS);
+ intr_buf_ptr = 1;
+ WRITE_CMD(H8_RD_EVENT_STATUS);
+ } else {
+ Dprintk("h8_intr: idle stat 0x%x data 0x%x\n",
+ stat_reg, data_reg);
+ }
+ } else {
+ Dprintk("h8_intr: bogus interrupt\n");
+ }
+ break;
+ }
+ case H8_INTR_MODE: { /* H8_INTR_MODE */
+ XDprintk("H8 intr/intr_mode\n");
+ if (data_reg == H8_BYTE_LEVEL_ACK) {
+ return;
+ } else if (data_reg == H8_CMD_ACK) {
+ return;
+ } else {
+ intrbuf.byte[intr_buf_ptr] = data_reg;
+ if(!intr_buf_ptr) {
+ h8_state = H8_IDLE;
+ h8_read_event_status();
+ }
+ intr_buf_ptr--;
+ }
+ break;
+ }
+ /* Placed in this state by h8_start_new_cmd(). */
+ case H8_XMIT: { /* H8_XMIT */
+ XDprintk("H8 intr/xmit\n");
+ /* If a byte level acknowledgement has been received */
+ if (data_reg == H8_BYTE_LEVEL_ACK) {
+ XDprintk("H8 intr/xmit BYTE ACK\n");
+ qp->nacks++;
+ if (qp->nacks > qp->ncmd)
+ if(h8_debug & 0x1)
+ Dprintk("h8intr: bogus # of acks!\n");
+ /*
+ * If the number of bytes sent is less than the total
+ * number of bytes in the command.
+ */
+ if (qp->cnt < qp->ncmd) {
+ h8_send_next_cmd_byte();
+ }
+ return;
+ /* If the complete command has produced an acknowledgement. */
+ } else if (data_reg == H8_CMD_ACK) {
+ XDprintk("H8 intr/xmit CMD ACK\n");
+ /* If there are response bytes */
+ if (qp->nrsp)
+ h8_state = H8_RCV;
+ else
+ h8_state = H8_IDLE;
+ qp->cnt = 0;
+ return;
+ /* Error, need to start over with a clean slate. */
+ } else if (data_reg == H8_NACK) {
+ XDprintk("h8_intr: NACK received restarting command\n");
+ qp->nacks = 0;
+ qp->cnt = 0;
+ h8_state = H8_IDLE;
+ WRITE_CMD(H8_SYNC);
+ return;
+ } else {
+ Dprintk ("h8intr: xmit unknown data 0x%x \n", data_reg);
+ return;
+ }
+ break;
+ }
+ case H8_RESYNC: { /* H8_RESYNC */
+ XDprintk("H8 intr/resync\n");
+ if (data_reg == H8_BYTE_LEVEL_ACK) {
+ return;
+ } else if (data_reg == H8_SYNC_BYTE) {
+ h8_state = H8_IDLE;
+ if (!QUEUE_EMPTY(&h8_actq, link))
+ h8_send_next_cmd_byte();
+ } else {
+ Dprintk ("h8_intr: resync unknown data 0x%x \n", data_reg);
+ return;
+ }
+ break;
+ }
+ case H8_RCV: { /* H8_RCV */
+ XDprintk("H8 intr/rcv\n");
+ if (qp->cnt < qp->nrsp) {
+ qp->rcvbuf[qp->cnt] = data_reg;
+ qp->cnt++;
+ /* If command reception finished. */
+ if (qp->cnt == qp->nrsp) {
+ h8_state = H8_IDLE;
+ QUEUE_REMOVE(&h8_actq, qp, link);
+ h8_cmd_done (qp);
+ /* More commands to send over? */
+ if (!QUEUE_EMPTY(&h8_cmdq, link))
+ h8_start_new_cmd();
+ }
+ return;
+ } else {
+ Dprintk ("h8intr: rcv overflow cmd 0x%x\n", qp->cmdbuf[0]);
+ }
+ break;
+ }
+ default: /* default */
+ Dprintk("H8 intr/unknown\n");
+ break;
+ }
+ return;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ printk("H8 module at %X(Interrupt %d)\n", h8_base, h8_irq);
+ if(request_irq(h8_irq, h8_intr, SA_INTERRUPT, "h8", NULL))
+ {
+ printk("H8: error: IRQ %d is not free.\n", h8_irq);
+ return -EIO;
+ }
+
+ misc_register(&h8_device);
+ request_region(h8_base, 8, "h8");
+
+#ifdef CONFIG_PROC_FS
+ proc_register_dynamic(&proc_root, &h8_proc_entry);
+#endif
+
+ QUEUE_INIT(&h8_actq, link, h8_cmd_q_t *);
+ QUEUE_INIT(&h8_cmdq, link, h8_cmd_q_t *);
+ QUEUE_INIT(&h8_freeq, link, h8_cmd_q_t *);
+ h8_alloc_queues();
+
+ h8_hw_init();
+
+ kernel_thread(h8_monitor_thread, NULL, 0);
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ misc_deregister(&h8_device);
+ release_region(h8_base, 8);
+ free_irq(h8_irq, NULL);
+}
+
+#else /* MODULE */
+
+int h8_init(void)
+{
+ if(request_irq(h8_irq, h8_intr, SA_INTERRUPT, "h8", NULL))
+ {
+ printk("H8: error: IRQ %d is not free\n", h8_irq);
+ return -EIO;
+ }
+ printk("H8 at 0x%x IRQ %d\n", h8_base, h8_irq);
+
+#ifdef CONFIG_PROC_FS
+ proc_register_dynamic(&proc_root, &h8_proc_entry);
+#endif
+
+ misc_register(&h8_device);
+ request_region(h8_base, 8, "h8");
+
+ QUEUE_INIT(&h8_actq, link, h8_cmd_q_t *);
+ QUEUE_INIT(&h8_cmdq, link, h8_cmd_q_t *);
+ QUEUE_INIT(&h8_freeq, link, h8_cmd_q_t *);
+ h8_alloc_queues();
+
+ h8_hw_init();
+
+ kernel_thread(h8_monitor_thread, NULL, 0);
+
+ return 0;
+}
+#endif /* MODULE */
+
+void h8_hw_init(void)
+{
+ u_char buf[H8_MAX_CMD_SIZE];
+
+ /* set CPU speed to max for booting */
+ h8_set_cpu_speed(MHZ_230);
+
+ /*
+ * Initialize the H8
+ */
+ h8_sync(); /* activate interrupts */
+
+ /* To clear conditions left by console */
+ h8_read_event_status();
+
+ /* Perform a conditioning read */
+ buf[0] = H8_DEVICE_CONTROL;
+ buf[1] = 0xff;
+ buf[2] = 0x0;
+ h8_q_cmd(buf, 3, 1);
+
+ /* Turn on built-in and external mice, capture power switch */
+ buf[0] = H8_DEVICE_CONTROL;
+ buf[1] = 0x0;
+ buf[2] = H8_ENAB_INT_PTR | H8_ENAB_EXT_PTR |
+ /*H8_DISAB_PWR_OFF_SW |*/ H8_ENAB_LOW_SPD_IND;
+ h8_q_cmd(buf, 3, 1);
+
+ h8_enabled = 1;
+ return;
+}
+
+#ifdef CONFIG_PROC_FS
+int h8_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
+{
+ char *p;
+
+ if (!h8_enabled)
+ return 0;
+ p = buf;
+
+
+ /*
+ 0) Linux driver version (this will change if format changes)
+ 1)
+ 2)
+ 3)
+ 4)
+ */
+
+ p += sprintf(p, "%s \n",
+ driver_version
+ );
+
+ return p - buf;
+}
+#endif
+
+static long h8_read(struct inode *inode, struct file *fp, char *buf,
+ u_long count)
+{
+ printk("h8_read: IMPDEL\n");
+ return 0;
+}
+
+static int h8_select(struct inode *inode, struct file *fp, int sel_type,
+ select_table * wait)
+{
+ printk("h8_select: IMPDEL\n");
+ return 0;
+}
+
+static int h8_ioctl(struct inode * inode, struct file *filp,
+ u_int cmd, u_long arg)
+{
+ printk("h8_ioctl: IMPDEL\n");
+ return 0;
+}
+
+static void h8_release(struct inode * inode, struct file * filp)
+{
+ printk("h8_release: IMPDEL\n");
+}
+
+static int h8_open(struct inode * inode, struct file * filp)
+{
+ printk("h8_open: IMPDEL\n");
+ return 0;
+}
+
+/* Called from console driver -- must make sure h8_enabled. */
+int h8_display_blank(void)
+{
+#ifdef CONFIG_H8_DISPLAY_BLANK
+ int error;
+
+ if (!h8_enabled)
+ return 0;
+ error = h8_set_display_power_state(H8_STATE_STANDBY);
+ if (error == H8_SUCCESS)
+ return 1;
+ h8_error("set display standby", error);
+#endif
+ return 0;
+}
+
+/* Called from console driver -- must make sure h8_enabled. */
+int h8_display_unblank(void)
+{
+#ifdef CONFIG_H8_DISPLAY_BLANK
+ int error;
+
+ if (!h8_enabled)
+ return 0;
+ error = h8_set_display_power_state(H8_STATE_READY);
+ if (error == H8_SUCCESS)
+ return 1;
+ h8_error("set display ready", error);
+#endif
+ return 0;
+}
+
+int
+h8_alloc_queues(void)
+{
+ h8_cmd_q_t *qp;
+ unsigned long flags;
+ int i;
+
+ qp = (h8_cmd_q_t *)kmalloc((sizeof (h8_cmd_q_t) * H8_Q_ALLOC_AMOUNT),
+ GFP_KERNEL);
+
+ if (!qp) {
+ printk("H8: could not allocate memory for command queue\n");
+ return(0);
+ }
+ /* add to the free queue */
+ save_flags(flags); cli();
+ for (i = 0; i < H8_Q_ALLOC_AMOUNT; i++) {
+ /* place each at front of freeq */
+ QUEUE_ENTER(&h8_freeq, &qp[i], link, h8_cmd_q_t *);
+ }
+ restore_flags(flags);
+ return (1);
+}
+
+/*
+ * Basic means by which commands are sent to the H8.
+ */
+void
+h8_q_cmd(u_char *cmd, int cmd_size, int resp_size)
+{
+ h8_cmd_q_t *qp;
+ unsigned long flags;
+ int i;
+
+ /* get cmd buf */
+ save_flags(flags); cli();
+ while (QUEUE_EMPTY(&h8_freeq, link)) {
+ Dprintk("H8: need to allocate more cmd buffers\n");
+ restore_flags(flags);
+ h8_alloc_queues();
+ save_flags(flags); cli();
+ }
+ /* get first element from queue */
+ qp = (h8_cmd_q_t *)QUEUE_FIRST(&h8_freeq, link);
+ QUEUE_REMOVE(&h8_freeq, qp, link);
+
+ restore_flags(flags);
+
+ /* fill it in */
+ for (i = 0; i < cmd_size; i++)
+ qp->cmdbuf[i] = cmd[i];
+ qp->ncmd = cmd_size;
+ qp->nrsp = resp_size;
+
+ /* queue it at the end of the cmd queue */
+ save_flags(flags); cli();
+
+ QUEUE_ENTER(&h8_cmdq, qp, link, h8_cmd_q_t *);
+
+ restore_flags(flags);
+
+ h8_start_new_cmd();
+}
+
+void
+h8_start_new_cmd(void)
+{
+ unsigned long flags;
+ h8_cmd_q_t *qp;
+
+ save_flags(flags); cli();
+ if (h8_state != H8_IDLE) {
+ if (h8_debug & 0x1)
+ Dprintk("h8_start_new_cmd: not idle\n");
+ restore_flags(flags);
+ return;
+ }
+
+ if (!QUEUE_EMPTY(&h8_actq, link)) {
+ Dprintk("h8_start_new_cmd: inconsistency: IDLE with non-empty active queue!\n");
+ restore_flags(flags);
+ return;
+ }
+
+ if (QUEUE_EMPTY(&h8_cmdq, link)) {
+ Dprintk("h8_start_new_cmd: no command to dequeue\n");
+ restore_flags(flags);
+ return;
+ }
+ /*
+ * Take first command off of the command queue and put
+ * it on the active queue.
+ */
+ qp = (h8_cmd_q_t *) QUEUE_FIRST(&h8_cmdq, link);
+ QUEUE_REMOVE(&h8_cmdq, qp, link);
+ QUEUE_ENTER(&h8_actq, qp, link, h8_cmd_q_t *);
+ h8_state = H8_XMIT;
+ if (h8_debug & 0x1)
+ Dprintk("h8_start_new_cmd: Starting a command\n");
+
+ qp->cnt = 1;
+ WRITE_CMD(qp->cmdbuf[0]); /* Kick it off */
+
+ restore_flags(flags);
+ return;
+}
+
+void
+h8_send_next_cmd_byte(void)
+{
+ h8_cmd_q_t *qp = (h8_cmd_q_t *)QUEUE_FIRST(&h8_actq, link);
+ int cnt;
+
+ cnt = qp->cnt;
+ qp->cnt++;
+
+ if (h8_debug & 0x1)
+ Dprintk("h8 sending next cmd byte 0x%x (0x%x)\n",
+ cnt, qp->cmdbuf[cnt]);
+
+ if (cnt) {
+ WRITE_DATA(qp->cmdbuf[cnt]);
+ } else {
+ WRITE_CMD(qp->cmdbuf[cnt]);
+ }
+ return;
+}
+
+/*
+ * Synchronize H8 communications channel for command transmission.
+ */
+void
+h8_sync(void)
+{
+ u_char buf[H8_MAX_CMD_SIZE];
+
+ buf[0] = H8_SYNC;
+ buf[1] = H8_SYNC_BYTE;
+ h8_q_cmd(buf, 2, 1);
+}
+
+/*
+ * Responds to external interrupt. Reads event status word and
+ * decodes type of interrupt.
+ */
+void
+h8_read_event_status(void)
+{
+
+ if(h8_debug & 0x200)
+ printk("h8_read_event_status: value 0x%x\n", intrbuf.word);
+
+ /*
+ * Power related items
+ */
+ if (intrbuf.word & H8_DC_CHANGE) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: DC_CHANGE\n");
+ /* see if dc added or removed, set batt/dc flag, send event */
+
+ h8_set_event_mask(H8_MANAGE_BATTERY);
+ wake_up(&h8_monitor_wait);
+ }
+
+ if (intrbuf.word & H8_POWER_BUTTON) {
+ printk("Power switch pressed - please wait - preparing to power
+off\n");
+ h8_set_event_mask(H8_POWER_BUTTON);
+ wake_up(&h8_monitor_wait);
+ }
+
+ /*
+ * Thermal related items
+ */
+ if (intrbuf.word & H8_THERMAL_THRESHOLD) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: THERMAL_THRESHOLD\n");
+ h8_set_event_mask(H8_MANAGE_UTHERM);
+ wake_up(&h8_monitor_wait);
+ }
+
+ /*
+ * nops -for now
+ */
+ if (intrbuf.word & H8_DOCKING_STATION_STATUS) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: DOCKING_STATION_STATUS\n");
+ /* read_ext_status */
+ }
+ if (intrbuf.word & H8_EXT_BATT_STATUS) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: EXT_BATT_STATUS\n");
+
+ }
+ if (intrbuf.word & H8_EXT_BATT_CHARGE_STATE) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: EXT_BATT_CHARGE_STATE\n");
+
+ }
+ if (intrbuf.word & H8_BATT_CHANGE_OVER) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: BATT_CHANGE_OVER\n");
+
+ }
+ if (intrbuf.word & H8_WATCHDOG) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: WATCHDOG\n");
+ /* nop */
+ }
+ if (intrbuf.word & H8_SHUTDOWN) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: SHUTDOWN\n");
+ /* nop */
+ }
+ if (intrbuf.word & H8_KEYBOARD) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: KEYBOARD\n");
+ /* nop */
+ }
+ if (intrbuf.word & H8_EXT_MOUSE_OR_CASE_SWITCH) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: EXT_MOUSE_OR_CASE_SWITCH\n");
+ /* read_ext_status*/
+ }
+ if (intrbuf.word & H8_INT_BATT_LOW) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: INT_BATT_LOW\n");
+ /* post event, warn user */
+ }
+ if (intrbuf.word & H8_INT_BATT_CHARGE_STATE) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: INT_BATT_CHARGE_STATE\n");
+ /* nop - happens often */
+ }
+ if (intrbuf.word & H8_INT_BATT_STATUS) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: INT_BATT_STATUS\n");
+
+ }
+ if (intrbuf.word & H8_INT_BATT_CHARGE_THRESHOLD) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: INT_BATT_CHARGE_THRESHOLD\n");
+ /* nop - happens often */
+ }
+ if (intrbuf.word & H8_EXT_BATT_LOW) {
+ if(h8_debug & 0x4)
+ printk("h8_read_event_status: EXT_BATT_LOW\n");
+ /*if no internal, post event, warn user */
+ /* else nop */
+ }
+
+ return;
+}
+
+/*
+ * Function called when H8 has performed requested command.
+ */
+void
+h8_cmd_done(h8_cmd_q_t *qp)
+{
+
+ /* what to do */
+ switch (qp->cmdbuf[0]) {
+ case H8_SYNC:
+ if (h8_debug & 0x40000)
+ printk("H8: Sync command done - byte returned was 0x%x\n",
+ qp->rcvbuf[0]);
+ QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ break;
+
+ case H8_RD_SN:
+ case H8_RD_ENET_ADDR:
+ printk("H8: Read ethernet addr - command done - address: %x - %x - %x - %x - %x - %x \n",
+ qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2],
+ qp->rcvbuf[3], qp->rcvbuf[4], qp->rcvbuf[5]);
+ QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ break;
+
+ case H8_RD_HW_VER:
+ case H8_RD_MIC_VER:
+ case H8_RD_MAX_TEMP:
+ printk("H8: Max recorded CPU temp %d, Sys temp %d\n",
+ qp->rcvbuf[0], qp->rcvbuf[1]);
+ QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ break;
+
+ case H8_RD_MIN_TEMP:
+ printk("H8: Min recorded CPU temp %d, Sys temp %d\n",
+ qp->rcvbuf[0], qp->rcvbuf[1]);
+ QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ break;
+
+ case H8_RD_CURR_TEMP:
+ h8_sync_channel |= H8_RD_CURR_TEMP;
+ xx.byte[0] = qp->rcvbuf[0];
+ xx.byte[1] = qp->rcvbuf[1];
+ wake_up(&h8_sync_wait);
+ QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ break;
+
+ case H8_RD_SYS_VARIENT:
+ case H8_RD_PWR_ON_CYCLES:
+ printk(" H8: RD_PWR_ON_CYCLES command done\n");
+ break;
+
+ case H8_RD_PWR_ON_SECS:
+ printk("H8: RD_PWR_ON_SECS command done\n");
+ break;
+
+ case H8_RD_RESET_STATUS:
+ case H8_RD_PWR_DN_STATUS:
+ case H8_RD_EVENT_STATUS:
+ case H8_RD_ROM_CKSM:
+ case H8_RD_EXT_STATUS:
+ xx.byte[1] = qp->rcvbuf[0];
+ xx.byte[0] = qp->rcvbuf[1];
+ h8_sync_channel |= H8_GET_EXT_STATUS;
+ wake_up(&h8_sync_wait);
+ QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ break;
+
+ case H8_RD_USER_CFG:
+ case H8_RD_INT_BATT_VOLT:
+ case H8_RD_DC_INPUT_VOLT:
+ case H8_RD_HORIZ_PTR_VOLT:
+ case H8_RD_VERT_PTR_VOLT:
+ case H8_RD_EEPROM_STATUS:
+ case H8_RD_ERR_STATUS:
+ case H8_RD_NEW_BUSY_SPEED:
+ case H8_RD_CONFIG_INTERFACE:
+ case H8_RD_INT_BATT_STATUS:
+ printk("H8: Read int batt status cmd done - returned was %x %x %x\n",
+ qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2]);
+ QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ break;
+
+ case H8_RD_EXT_BATT_STATUS:
+ case H8_RD_PWR_UP_STATUS:
+ case H8_RD_EVENT_STATUS_MASK:
+ case H8_CTL_EMU_BITPORT:
+ case H8_DEVICE_CONTROL:
+ if(h8_debug & 0x20000) {
+ printk("H8: Device control cmd done - byte returned was 0x%x\n",
+ qp->rcvbuf[0]);
+ }
+ QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ break;
+
+ case H8_CTL_TFT_BRT_DC:
+ case H8_CTL_WATCHDOG:
+ case H8_CTL_MIC_PROT:
+ case H8_CTL_INT_BATT_CHG:
+ case H8_CTL_EXT_BATT_CHG:
+ case H8_CTL_MARK_SPACE:
+ case H8_CTL_MOUSE_SENSITIVITY:
+ case H8_CTL_DIAG_MODE:
+ case H8_CTL_IDLE_AND_BUSY_SPDS:
+ printk("H8: Idle and busy speed command done\n");
+ break;
+
+ case H8_CTL_TFT_BRT_BATT:
+ case H8_CTL_UPPER_TEMP:
+ if(h8_debug & 0x10) {
+ XDprintk("H8: ctl upper thermal thresh cmd done - returned was %d\n",
+ qp->rcvbuf[0]);
+ }
+ QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ break;
+
+ case H8_CTL_LOWER_TEMP:
+ case H8_CTL_TEMP_CUTOUT:
+ case H8_CTL_WAKEUP:
+ case H8_CTL_CHG_THRESHOLD:
+ case H8_CTL_TURBO_MODE:
+ case H8_SET_DIAG_STATUS:
+ case H8_SOFTWARE_RESET:
+ case H8_RECAL_PTR:
+ case H8_SET_INT_BATT_PERCENT:
+ case H8_WRT_CFG_INTERFACE_REG:
+ case H8_WRT_EVENT_STATUS_MASK:
+ case H8_ENTER_POST_MODE:
+ case H8_EXIT_POST_MODE:
+ case H8_RD_EEPROM:
+ case H8_WRT_EEPROM:
+ case H8_WRT_TO_STATUS_DISP:
+ printk("H8: Write IO status display command done\n");
+ break;
+
+ case H8_DEFINE_SPC_CHAR:
+ case H8_DEFINE_TABLE_STRING_ENTRY:
+ case H8_PERFORM_EMU_CMD:
+ case H8_EMU_RD_REG:
+ case H8_EMU_WRT_REG:
+ case H8_EMU_RD_RAM:
+ case H8_EMU_WRT_RAM:
+ case H8_BQ_RD_REG:
+ case H8_BQ_WRT_REG:
+ case H8_PWR_OFF:
+ printk ("H8: misc command completed\n");
+ break;
+ }
+ return;
+}
+
+/*
+ * Retrieve the current cpu temperature and case temperature. Provides
+ * the feedback for the thermal control algorithm. Synchcronized via
+ * sleep() for priority so that no other actions in the process will take
+ * place before the data becomes available.
+ */
+int
+h8_get_curr_temp(u_char curr_temp[])
+{
+ u_char buf[H8_MAX_CMD_SIZE];
+ unsigned long flags;
+
+ memset(buf, 0, H8_MAX_CMD_SIZE);
+ buf[0] = H8_RD_CURR_TEMP;
+
+ h8_q_cmd(buf, 1, 2);
+
+ save_flags(flags); cli();
+
+ while((h8_sync_channel & H8_RD_CURR_TEMP) == 0)
+ sleep_on(&h8_sync_wait);
+
+ restore_flags(flags);
+
+ h8_sync_channel &= ~H8_RD_CURR_TEMP;
+ curr_temp[0] = xx.byte[0];
+ curr_temp[1] = xx.byte[1];
+ xx.word = 0;
+
+ if(h8_debug & 0x8)
+ printk("H8: curr CPU temp %d, Sys temp %d\n",
+ curr_temp[0], curr_temp[1]);
+ return 0;
+}
+
+static void
+h8_get_max_temp(void)
+{
+ u_char buf[H8_MAX_CMD_SIZE];
+
+ buf[0] = H8_RD_MAX_TEMP;
+ h8_q_cmd(buf, 1, 2);
+}
+
+/*
+ * Assigns an upper limit to the value of the H8 thermal interrupt.
+ * As an example setting a value of 115 F here will cause the
+ * interrupt to trigger when the cpu temperature reaches 115 F.
+ */
+static void
+h8_set_upper_therm_thold(int thold)
+{
+ u_char buf[H8_MAX_CMD_SIZE];
+
+ /* write 0 to reinitialize interrupt */
+ buf[0] = H8_CTL_UPPER_TEMP;
+ buf[1] = 0x0;
+ buf[2] = 0x0;
+ h8_q_cmd(buf, 3, 1);
+
+ /* Do it for real */
+ buf[0] = H8_CTL_UPPER_TEMP;
+ buf[1] = 0x0;
+ buf[2] = thold;
+ h8_q_cmd(buf, 3, 1);
+}
+
+static void
+h8_get_upper_therm_thold(void)
+{
+ u_char buf[H8_MAX_CMD_SIZE];
+
+ buf[0] = H8_CTL_UPPER_TEMP;
+ buf[1] = 0xff;
+ buf[2] = 0;
+ h8_q_cmd(buf, 3, 1);
+}
+
+/*
+ * The external status word contains information on keyboard controller,
+ * power button, changes in external batt status, change in DC state,
+ * docking station, etc. General purpose querying use.
+ */
+int
+h8_get_ext_status(u_char stat_word[])
+{
+ u_char buf[H8_MAX_CMD_SIZE];
+ unsigned long flags;
+
+ memset(buf, 0, H8_MAX_CMD_SIZE);
+ buf[0] = H8_RD_EXT_STATUS;
+
+ h8_q_cmd(buf, 1, 2);
+
+ save_flags(flags); cli();
+
+ while((h8_sync_channel & H8_GET_EXT_STATUS) == 0)
+ sleep_on(&h8_sync_wait);
+
+ restore_flags(flags);
+
+ h8_sync_channel &= ~H8_GET_EXT_STATUS;
+ stat_word[0] = xx.byte[0];
+ stat_word[1] = xx.byte[1];
+ xx.word = 0;
+
+ if(h8_debug & 0x8)
+ printk("H8: curr ext status %x, %x\n",
+ stat_word[0], stat_word[1]);
+
+ return 0;
+}
+
+/*
+ * Thread attached to task 0 manages thermal/physcial state of Alphabook.
+ * When a condition is detected by the interrupt service routine, the
+ * isr does a wakeup() on h8_monitor_wait. The mask value is then
+ * screened for the appropriate action.
+ */
+
+int
+h8_monitor_thread(void * unused)
+{
+ u_char curr_temp[2];
+
+ /*
+ * Need a logic based safety valve here. During boot when this thread is
+ * started and the thermal interrupt is not yet initialized this logic
+ * checks the temperature and acts accordingly. When this path is acted
+ * upon system boot is painfully slow, however, the priority associated
+ * with overheating is high enough to warrant this action.
+ */
+ h8_get_curr_temp(curr_temp);
+
+ printk("H8: Initial CPU temp: %d\n", curr_temp[0]);
+
+ if(curr_temp[0] >= h8_uthermal_threshold) {
+ h8_set_event_mask(H8_MANAGE_UTHERM);
+ h8_manage_therm();
+ } else {
+ /*
+ * Arm the upper thermal limit of the H8 so that any temp in
+ * excess will trigger the thermal control mechanism.
+ */
+ h8_set_upper_therm_thold(h8_uthermal_threshold);
+ }
+
+ for(;;) {
+ sleep_on(&h8_monitor_wait);
+
+ if(h8_debug & 0x2)
+ printk("h8_monitor_thread awakened, mask:%x\n",
+ h8_event_mask);
+
+ if (h8_event_mask & (H8_MANAGE_UTHERM|H8_MANAGE_LTHERM)) {
+ h8_manage_therm();
+ }
+
+#if 0
+ if (h8_event_mask & H8_POWER_BUTTON) {
+ h8_system_down();
+ }
+
+ /*
+ * If an external DC supply is removed or added make
+ * appropriate cpu speed adjustments.
+ */
+ if (h8_event_mask & H8_MANAGE_BATTERY) {
+ h8_run_level_3_manage(H8_RUN);
+ h8_clear_event_mask(H8_MANAGE_BATTERY);
+ }
+#endif
+ }
+}
+
+/*
+ * Function implements the following policy. When the machine is booted
+ * the system is set to run at full clock speed. When the upper thermal
+ * threshold is reached as a result of full clock a damping factor is
+ * applied to cool off the cpu. The default value is one quarter clock
+ * (57 Mhz). When as a result of this cooling a temperature lower by
+ * hmc_uthermal_window is reached, the machine is reset to a higher
+ * speed, one half clock (115 Mhz). One half clock is maintained until
+ * the upper thermal threshold is again reached restarting the cycle.
+ */
+
+int
+h8_manage_therm(void)
+{
+ u_char curr_temp[2];
+
+ if(h8_event_mask & H8_MANAGE_UTHERM) {
+ /* Upper thermal interrupt received, need to cool down. */
+ if(h8_debug & 0x10)
+ printk("H8: Thermal threshold %d F reached\n",
+ h8_uthermal_threshold);
+ h8_set_cpu_speed(h8_udamp);
+ h8_clear_event_mask(H8_MANAGE_UTHERM);
+ h8_set_event_mask(H8_MANAGE_LTHERM);
+ /* Check again in 30 seconds for cpu temperature */
+ h8_start_monitor_timer(H8_TIMEOUT_INTERVAL);
+ } else if (h8_event_mask & H8_MANAGE_LTHERM) {
+ /* See how cool the system has become as a result
+ of the reduction in speed. */
+ h8_get_curr_temp(curr_temp);
+ last_temp = curr_temp[0];
+ if (curr_temp[0] < (h8_uthermal_threshold - h8_uthermal_window))
+ {
+ /* System cooling has progressed to a point
+ that the cpu may be speeded up. */
+ h8_set_upper_therm_thold(h8_uthermal_threshold);
+ h8_set_cpu_speed(h8_ldamp); /* adjustable */
+ if(h8_debug & 0x10)
+ printk("H8: CPU cool, applying cpu_divisor: %d \n",
+ h8_ldamp);
+ h8_clear_event_mask(H8_MANAGE_LTHERM);
+ }
+ else /* Not cool enough yet, check again in 30 seconds. */
+ h8_start_monitor_timer(H8_TIMEOUT_INTERVAL);
+ } else {
+
+ }
+ return 0;
+}
+
+/*
+ * Function conditions the value of global_rpb_counter before
+ * calling the primitive which causes the actual speed change.
+ */
+void
+h8_set_cpu_speed(int speed_divisor)
+{
+
+#ifdef NOT_YET
+/*
+ * global_rpb_counter is consumed by alpha_delay() in determining just
+ * how much time to delay. It is necessary that the number of microseconds
+ * in DELAY(n) be kept consistent over a variety of cpu clock speeds.
+ * To that end global_rpb_counter is here adjusted.
+ */
+
+ switch (speed_divisor) {
+ case 0:
+ global_rpb_counter = rpb->rpb_counter * 2L;
+ break;
+ case 1:
+ global_rpb_counter = rpb->rpb_counter * 4L / 3L ;
+ break;
+ case 3:
+ global_rpb_counter = rpb->rpb_counter / 2L;
+ break;
+ case 4:
+ global_rpb_counter = rpb->rpb_counter / 4L;
+ break;
+ case 5:
+ global_rpb_counter = rpb->rpb_counter / 8L;
+ break;
+ /*
+ * This case most commonly needed for cpu_speed_divisor
+ * of 2 which is the value assigned by the firmware.
+ */
+ default:
+ global_rpb_counter = rpb->rpb_counter;
+ break;
+ }
+#endif /* NOT_YET */
+
+ if(h8_debug & 0x8)
+ printk("H8: Setting CPU speed to %d MHz\n",
+ speed_tab[speed_divisor]);
+
+ /* Make the actual speed change */
+ lca_clock_fiddle(speed_divisor);
+}
+
+/*
+ * Gets value stored in rpb representing cpu clock speed and adjusts this
+ * value based on the current clock speed divisor.
+ */
+u_long
+h8_get_cpu_speed(void)
+{
+ u_long speed = 0;
+ u_long counter;
+
+#ifdef NOT_YET
+ counter = rpb->rpb_counter / 1000000L;
+
+ switch (alphabook_get_clock()) {
+ case 0:
+ speed = counter * 2L;
+ break;
+ case 1:
+ speed = counter * 4L / 3L ;
+ break;
+ case 2:
+ speed = counter;
+ break;
+ case 3:
+ speed = counter / 2L;
+ break;
+ case 4:
+ speed = counter / 4L;
+ break;
+ case 5:
+ speed = counter / 8L;
+ break;
+ default:
+ break;
+ }
+ if(h8_debug & 0x8)
+ printk("H8: CPU speed current setting: %d MHz\n", speed);
+#endif /* NOT_YET */
+ return speed;
+}
+
+static void
+h8_activate_monitor(unsigned long unused)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ h8_monitor_timer_active = 0;
+ restore_flags(flags);
+
+ wake_up(&h8_monitor_wait);
+}
+
+static void
+h8_start_monitor_timer(unsigned long secs)
+{
+ unsigned long flags;
+
+ if (h8_monitor_timer_active)
+ return;
+
+ save_flags(flags); cli();
+ h8_monitor_timer_active = 1;
+ restore_flags(flags);
+
+ init_timer(&h8_monitor_timer);
+ h8_monitor_timer.function = h8_activate_monitor;
+ h8_monitor_timer.expires = secs * HZ + jiffies;
+ add_timer(&h8_monitor_timer);
+}
+
+static void h8_set_event_mask(int mask)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ h8_event_mask |= mask;
+ restore_flags(flags);
+}
+
+static void h8_clear_event_mask(int mask)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ h8_event_mask &= (~mask);
+ restore_flags(flags);
+}
diff --git a/drivers/char/h8.h b/drivers/char/h8.h
new file mode 100644
index 000000000..533c4ece1
--- /dev/null
+++ b/drivers/char/h8.h
@@ -0,0 +1,250 @@
+/*
+ */
+
+#ifndef __H8_H__
+#define __H8_H__
+
+/*
+ * Register address and offsets
+ */
+#define H8_BASE_ADDR 0x170 /* default */
+#define H8_IRQ 9 /* default */
+#define H8_STATUS_REG_OFF 0x4
+#define H8_CMD_REG_OFF 0x4
+#define H8_DATA_REG_OFF 0x0
+
+
+/* H8 register bit definitions */
+/* status register */
+#define H8_OFULL 0x1 /* output data register full */
+#define H8_IFULL 0x2 /* input data register full */
+#define H8_CMD 0x8 /* command / not data */
+
+#define H8_INTR 0xfa
+#define H8_NACK 0xfc
+#define H8_BYTE_LEVEL_ACK 0xfd
+#define H8_CMD_ACK 0xfe
+#define H8_SYNC_BYTE 0x99
+
+/*
+ * H8 command definitions
+ */
+/* System info commands */
+#define H8_SYNC 0x0
+#define H8_RD_SN 0x1
+#define H8_RD_ENET_ADDR 0x2
+#define H8_RD_HW_VER 0x3
+#define H8_RD_MIC_VER 0x4
+#define H8_RD_MAX_TEMP 0x5
+#define H8_RD_MIN_TEMP 0x6
+#define H8_RD_CURR_TEMP 0x7
+#define H8_RD_SYS_VARIENT 0x8
+#define H8_RD_PWR_ON_CYCLES 0x9
+#define H8_RD_PWR_ON_SECS 0xa
+#define H8_RD_RESET_STATUS 0xb
+#define H8_RD_PWR_DN_STATUS 0xc
+#define H8_RD_EVENT_STATUS 0xd
+#define H8_RD_ROM_CKSM 0xe
+#define H8_RD_EXT_STATUS 0xf
+#define H8_RD_USER_CFG 0x10
+#define H8_RD_INT_BATT_VOLT 0x11
+#define H8_RD_DC_INPUT_VOLT 0x12
+#define H8_RD_HORIZ_PTR_VOLT 0x13
+#define H8_RD_VERT_PTR_VOLT 0x14
+#define H8_RD_EEPROM_STATUS 0x15
+#define H8_RD_ERR_STATUS 0x16
+#define H8_RD_NEW_BUSY_SPEED 0x17
+#define H8_RD_CONFIG_INTERFACE 0x18
+#define H8_RD_INT_BATT_STATUS 0x19
+#define H8_RD_EXT_BATT_STATUS 0x1a
+#define H8_RD_PWR_UP_STATUS 0x1b
+#define H8_RD_EVENT_STATUS_MASK 0x56
+
+/* Read/write/modify commands */
+#define H8_CTL_EMU_BITPORT 0x32
+#define H8_DEVICE_CONTROL 0x21
+#define H8_CTL_TFT_BRT_DC 0x22
+#define H8_CTL_WATCHDOG 0x23
+#define H8_CTL_MIC_PROT 0x24
+#define H8_CTL_INT_BATT_CHG 0x25
+#define H8_CTL_EXT_BATT_CHG 0x26
+#define H8_CTL_MARK_SPACE 0x27
+#define H8_CTL_MOUSE_SENSITIVITY 0x28
+#define H8_CTL_DIAG_MODE 0x29
+#define H8_CTL_IDLE_AND_BUSY_SPDS 0x2a
+#define H8_CTL_TFT_BRT_BATT 0x2b
+#define H8_CTL_UPPER_TEMP 0x2c
+#define H8_CTL_LOWER_TEMP 0x2d
+#define H8_CTL_TEMP_CUTOUT 0x2e
+#define H8_CTL_WAKEUP 0x2f
+#define H8_CTL_CHG_THRESHOLD 0x30
+#define H8_CTL_TURBO_MODE 0x31
+#define H8_SET_DIAG_STATUS 0x40
+#define H8_SOFTWARE_RESET 0x41
+#define H8_RECAL_PTR 0x42
+#define H8_SET_INT_BATT_PERCENT 0x43
+#define H8_WRT_CFG_INTERFACE_REG 0x45
+#define H8_WRT_EVENT_STATUS_MASK 0x57
+#define H8_ENTER_POST_MODE 0x46
+#define H8_EXIT_POST_MODE 0x47
+
+/* Block transfer commands */
+#define H8_RD_EEPROM 0x50
+#define H8_WRT_EEPROM 0x51
+#define H8_WRT_TO_STATUS_DISP 0x52
+#define H8_DEFINE_SPC_CHAR 0x53
+
+/* Generic commands */
+#define H8_DEFINE_TABLE_STRING_ENTRY 0x60
+
+/* Battery control commands */
+#define H8_PERFORM_EMU_CMD 0x70
+#define H8_EMU_RD_REG 0x71
+#define H8_EMU_WRT_REG 0x72
+#define H8_EMU_RD_RAM 0x73
+#define H8_EMU_WRT_RAM 0x74
+#define H8_BQ_RD_REG 0x75
+#define H8_BQ_WRT_REG 0x76
+
+/* System admin commands */
+#define H8_PWR_OFF 0x80
+
+/*
+ * H8 command related definitions
+ */
+
+/* device control argument bits */
+#define H8_ENAB_EXTSMI 0x1
+#define H8_DISAB_IRQ 0x2
+#define H8_ENAB_FLASH_WRT 0x4
+#define H8_ENAB_THERM 0x8
+#define H8_ENAB_INT_PTR 0x10
+#define H8_ENAB_LOW_SPD_IND 0x20
+#define H8_ENAB_EXT_PTR 0x40
+#define H8_DISAB_PWR_OFF_SW 0x80
+#define H8_POWER_OFF 0x80
+
+/* H8 read event status bits */
+#define H8_DC_CHANGE 0x1
+#define H8_INT_BATT_LOW 0x2
+#define H8_INT_BATT_CHARGE_THRESHOLD 0x4
+#define H8_INT_BATT_CHARGE_STATE 0x8
+#define H8_INT_BATT_STATUS 0x10
+#define H8_EXT_BATT_CHARGE_STATE 0x20
+#define H8_EXT_BATT_LOW 0x40
+#define H8_EXT_BATT_STATUS 0x80
+#define H8_THERMAL_THRESHOLD 0x100
+#define H8_WATCHDOG 0x200
+#define H8_DOCKING_STATION_STATUS 0x400
+#define H8_EXT_MOUSE_OR_CASE_SWITCH 0x800
+#define H8_KEYBOARD 0x1000
+#define H8_BATT_CHANGE_OVER 0x2000
+#define H8_POWER_BUTTON 0x4000
+#define H8_SHUTDOWN 0x8000
+
+/* H8 control idle and busy speeds */
+#define H8_SPEED_LOW 0x1
+#define H8_SPEED_MED 0x2
+#define H8_SPEED_HI 0x3
+#define H8_SPEED_LOCKED 0x80
+
+#define H8_MAX_CMD_SIZE 18
+#define H8_Q_ALLOC_AMOUNT 10
+
+/* H8 state field values */
+#define H8_IDLE 1
+#define H8_XMIT 2
+#define H8_RCV 3
+#define H8_RESYNC 4
+#define H8_INTR_MODE 5
+
+/* Mask values for control functions */
+#define UTH_HYSTERESIS 5
+#define DEFAULT_UTHERMAL_THRESHOLD 115
+#define H8_TIMEOUT_INTERVAL 30
+#define H8_RUN 4
+
+#define H8_GET_MAX_TEMP 0x1
+#define H8_GET_CURR_TEMP 0x2
+#define H8_GET_UPPR_THRMAL_THOLD 0x4
+#define H8_GET_ETHERNET_ADDR 0x8
+#define H8_SYNC_OP 0x10
+#define H8_SET_UPPR_THRMAL_THOLD 0x20
+#define H8_GET_INT_BATT_STAT 0x40
+#define H8_GET_CPU_SPD 0x80
+#define H8_MANAGE_UTHERM 0x100
+#define H8_MANAGE_LTHERM 0x200
+#define H8_HALT 0x400
+#define H8_CRASH 0x800
+#define H8_GET_EXT_STATUS 0x10000
+#define H8_MANAGE_QUIET 0x20000
+#define H8_MANAGE_SPEEDUP 0x40000
+#define H8_MANAGE_BATTERY 0x80000
+#define H8_SYSTEM_DELAY_TEST 0x100000
+#define H8_POWER_SWITCH_TEST 0x200000
+
+/* cpu speeds and clock divisor values */
+#define MHZ_14 5
+#define MHZ_28 4
+#define MHZ_57 3
+#define MHZ_115 2
+#define MHZ_230 0
+
+/*
+ * H8 data
+ */
+struct h8_data {
+ u_int ser_num;
+ u_char ether_add[6];
+ u_short hw_ver;
+ u_short mic_ver;
+ u_short max_tmp;
+ u_short min_tmp;
+ u_short cur_tmp;
+ u_int sys_var;
+ u_int pow_on;
+ u_int pow_on_secs;
+ u_char reset_status;
+ u_char pwr_dn_status;
+ u_short event_status;
+ u_short rom_cksm;
+ u_short ext_status;
+ u_short u_cfg;
+ u_char ibatt_volt;
+ u_char dc_volt;
+ u_char ptr_horiz;
+ u_char ptr_vert;
+ u_char eeprom_status;
+ u_char error_status;
+ u_char new_busy_speed;
+ u_char cfg_interface;
+ u_short int_batt_status;
+ u_short ext_batt_status;
+ u_char pow_up_status;
+ u_char event_status_mask;
+};
+
+
+/*
+ * H8 command buffers
+ */
+typedef struct h8_cmd_q {
+ DLNODE(struct h8_cmd_q) link; /* double linked list */
+ int ncmd; /* number of bytes in command */
+ int nrsp; /* number of bytes in response */
+ int cnt; /* number of bytes sent/received */
+ int nacks; /* number of byte level acks */
+ u_char cmdbuf[H8_MAX_CMD_SIZE]; /* buffer to store command */
+ u_char rcvbuf[H8_MAX_CMD_SIZE]; /* buffer to store response */
+} h8_cmd_q_t;
+
+typedef struct __queue_head {
+ DLNODE(struct h8_cmd_q) link;
+} queue_head_t;
+
+union intr_buf {
+ u_char byte[2];
+ u_int word;
+};
+
+#endif /* __H8_H_ */
diff --git a/drivers/char/hfmodem/.cvsignore b/drivers/char/hfmodem/.cvsignore
new file mode 100644
index 000000000..add4bedde
--- /dev/null
+++ b/drivers/char/hfmodem/.cvsignore
@@ -0,0 +1,3 @@
+.depend
+tables.h
+gentbl
diff --git a/drivers/char/hfmodem/Makefile b/drivers/char/hfmodem/Makefile
new file mode 100644
index 000000000..42629693b
--- /dev/null
+++ b/drivers/char/hfmodem/Makefile
@@ -0,0 +1,37 @@
+#
+# Makefile for the hfmodem device driver.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+
+O_TARGET := hfmodem.o
+
+O_OBJS := refclock.o modem.o main.o
+ifeq ($(CONFIG_HFMODEM_SBC),y)
+O_OBJS += sbc.o
+endif
+ifeq ($(CONFIG_HFMODEM_WSS),y)
+O_OBJS += wss.o
+endif
+
+M_OBJS := $(O_TARGET)
+
+all: all_targets
+.PHONY: all
+
+gentbl: gentbl.c
+ $(HOSTCC) -Wall $< -o $@ -lm
+
+TBLHDR := tables.h
+
+tables.h: gentbl
+ ./gentbl > $@
+
+fastdep: $(TBLHDR)
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/char/hfmodem/gentbl.c b/drivers/char/hfmodem/gentbl.c
new file mode 100644
index 000000000..558b9a405
--- /dev/null
+++ b/drivers/char/hfmodem/gentbl.c
@@ -0,0 +1,69 @@
+/*****************************************************************************/
+
+/*
+ * gentbl.c -- Linux soundcard HF FSK driver,
+ * Table generator.
+ *
+ * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/hfmodem.h>
+#include <math.h>
+#include <stdio.h>
+
+/* --------------------------------------------------------------------- */
+
+#define SINTABBITS 9
+#define SINTABSIZE (1<<SINTABBITS)
+
+/* --------------------------------------------------------------------- */
+
+static void gensintbl(void)
+{
+ int i;
+
+ printf("#define SINTABBITS %d\n#define SINTABSIZE (1<<SINTABBITS)\n"
+ "\nstatic short isintab[SINTABSIZE+SINTABSIZE/4] = {\n\t", SINTABBITS);
+ for (i = 0; i < (SINTABSIZE+SINTABSIZE/4); i++) {
+ printf("%6d", (int)(32767.0 * sin(2.0 * M_PI / SINTABSIZE * i)));
+ if (i < (SINTABSIZE+SINTABSIZE/4)-1) {
+ if ((i & 7) == 7)
+ printf(",\n\t");
+ else
+ printf(",");
+ }
+ }
+ printf("\n};\n\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+int main(int argc, char *argv[])
+{
+ printf("/*\n * This file is automatically generated by %s, DO NOT EDIT!\n*/\n\n",
+ argv[0]);
+ gensintbl();
+ exit(0);
+}
+
+/* --------------------------------------------------------------------- */
+
diff --git a/drivers/char/hfmodem/main.c b/drivers/char/hfmodem/main.c
new file mode 100644
index 000000000..eb30cec3e
--- /dev/null
+++ b/drivers/char/hfmodem/main.c
@@ -0,0 +1,736 @@
+/*****************************************************************************/
+
+/*
+ * main.c -- Linux soundcard HF FSK driver.
+ *
+ * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Command line options (insmod command line)
+ *
+ * History:
+ * 0.1 15.04.97 Adapted from baycom.c and made network driver interface
+ * 0.2 05.07.97 All floating point stuff thrown out due to Linus' rantings :)
+ *
+ */
+
+/*****************************************************************************/
+
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/ioport.h>
+#include <linux/hfmodem.h>
+
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+ int i = verify_area(VERIFY_READ, from, n);
+ if (i)
+ return i;
+ memcpy_fromfs(to, from, n);
+ return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+ int i = verify_area(VERIFY_WRITE, to, n);
+ if (i)
+ return i;
+ memcpy_tofs(to, from, n);
+ return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*static*/ const char hfmodem_drvname[] = "hfmodem";
+static const char hfmodem_drvinfo[] = KERN_INFO "hfmodem: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "hfmodem: version 0.2 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+/*
+ * currently we support only one device
+ */
+
+struct hfmodem_state hfmodem_state[NR_DEVICE];
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== port checking routines ========================
+ */
+
+
+#define UART_RBR(iobase) (iobase+0)
+#define UART_THR(iobase) (iobase+0)
+#define UART_IER(iobase) (iobase+1)
+#define UART_IIR(iobase) (iobase+2)
+#define UART_FCR(iobase) (iobase+2)
+#define UART_LCR(iobase) (iobase+3)
+#define UART_MCR(iobase) (iobase+4)
+#define UART_LSR(iobase) (iobase+5)
+#define UART_MSR(iobase) (iobase+6)
+#define UART_SCR(iobase) (iobase+7)
+#define UART_DLL(iobase) (iobase+0)
+#define UART_DLM(iobase) (iobase+1)
+
+#define SER_EXTENT 8
+
+#define LPT_DATA(iobase) (iobase+0)
+#define LPT_STATUS(iobase) (iobase+1)
+#define LPT_CONTROL(iobase) (iobase+2)
+#define LPT_IRQ_ENABLE 0x10
+
+#define LPT_EXTENT 3
+
+#define MIDI_DATA(iobase) (iobase)
+#define MIDI_STATUS(iobase) (iobase+1)
+#define MIDI_READ_FULL 0x80 /* attention: negative logic!! */
+#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */
+
+#define MIDI_EXTENT 2
+
+#define SP_SER 1
+#define SP_PAR 2
+#define SP_MIDI 4
+
+/* ---------------------------------------------------------------------- */
+/*
+ * returns 0 if ok and != 0 on error;
+ * the same behaviour as par96_check_lpt in baycom.c
+ */
+
+__initfunc(static int check_lpt(unsigned int iobase))
+{
+ unsigned char b1,b2;
+ int i;
+
+ if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT)
+ return 0;
+ if (check_region(iobase, LPT_EXTENT))
+ return 0;
+ b1 = inb(LPT_DATA(iobase));
+ b2 = inb(LPT_CONTROL(iobase));
+ outb(0xaa, LPT_DATA(iobase));
+ i = inb(LPT_DATA(iobase)) == 0xaa;
+ outb(0x55, LPT_DATA(iobase));
+ i &= inb(LPT_DATA(iobase)) == 0x55;
+ outb(0x0a, LPT_CONTROL(iobase));
+ i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
+ outb(0x05, LPT_CONTROL(iobase));
+ i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
+ outb(b1, LPT_DATA(iobase));
+ outb(b2, LPT_CONTROL(iobase));
+ return !i;
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250, c_uart_16450, c_uart_16550, c_uart_16550A };
+static const char *uart_str[] __initdata = { "unknown", "8250", "16450", "16550", "16550A" };
+
+__initfunc(static enum uart check_uart(unsigned int iobase))
+{
+ unsigned char b1,b2,b3;
+ enum uart u;
+ enum uart uart_tab[] = { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+ if (iobase <= 0 || iobase > 0x1000-SER_EXTENT)
+ return c_uart_unknown;
+ if (check_region(iobase, SER_EXTENT))
+ return c_uart_unknown;
+ b1 = inb(UART_MCR(iobase));
+ outb(b1 | 0x10, UART_MCR(iobase)); /* loopback mode */
+ b2 = inb(UART_MSR(iobase));
+ outb(0x1a, UART_MCR(iobase));
+ b3 = inb(UART_MSR(iobase)) & 0xf0;
+ outb(b1, UART_MCR(iobase)); /* restore old values */
+ outb(b2, UART_MSR(iobase));
+ if (b3 != 0x90)
+ return c_uart_unknown;
+ inb(UART_RBR(iobase));
+ inb(UART_RBR(iobase));
+ outb(0x01, UART_FCR(iobase)); /* enable FIFOs */
+ u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3];
+ if (u == c_uart_16450) {
+ outb(0x5a, UART_SCR(iobase));
+ b1 = inb(UART_SCR(iobase));
+ outb(0xa5, UART_SCR(iobase));
+ b2 = inb(UART_SCR(iobase));
+ if ((b1 != 0x5a) || (b2 != 0xa5))
+ u = c_uart_8250;
+ }
+ return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(static int check_midi(unsigned int iobase))
+{
+ unsigned long timeout;
+ unsigned long flags;
+ unsigned char b;
+
+ if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT)
+ return 0;
+ if (check_region(iobase, MIDI_EXTENT))
+ return 0;
+ timeout = jiffies + (HZ / 100);
+ while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+ if ((signed)(jiffies - timeout) > 0)
+ return 0;
+ save_flags(flags);
+ cli();
+ outb(0xff, MIDI_DATA(iobase));
+ b = inb(MIDI_STATUS(iobase));
+ restore_flags(flags);
+ if (!(b & MIDI_WRITE_EMPTY))
+ return 0;
+ while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+ if ((signed)(jiffies - timeout) > 0)
+ return 0;
+ return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_status(struct hfmodem_state *dev, int ptt)
+{
+ int dcd = 0;
+
+ ptt = !!ptt;
+ if (dev->ptt_out.flags & SP_SER) {
+ outb(dcd | (ptt << 1), UART_MCR(dev->ptt_out.seriobase));
+ outb(0x40 & (-ptt), UART_LCR(dev->ptt_out.seriobase));
+ }
+ if (dev->ptt_out.flags & SP_PAR) {
+ outb(ptt | (dcd << 1), LPT_DATA(dev->ptt_out.pariobase));
+ }
+ if (dev->ptt_out.flags & SP_MIDI && ptt) {
+ outb(0, MIDI_DATA(dev->ptt_out.midiiobase));
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(static void output_check(struct hfmodem_state *dev))
+{
+ enum uart u = c_uart_unknown;
+
+ if (dev->ptt_out.seriobase > 0 && dev->ptt_out.seriobase <= 0x1000-SER_EXTENT &&
+ ((u = check_uart(dev->ptt_out.seriobase))) != c_uart_unknown)
+ printk(KERN_INFO "%s: PTT output: uart found at address 0x%x type %s\n",
+ hfmodem_drvname, dev->ptt_out.seriobase, uart_str[u]);
+ else {
+ if (dev->ptt_out.seriobase > 0)
+ printk(KERN_WARNING "%s: PTT output: no uart found at address 0x%x\n",
+ hfmodem_drvname, dev->ptt_out.seriobase);
+ dev->ptt_out.seriobase = 0;
+ }
+ if (dev->ptt_out.pariobase > 0 && dev->ptt_out.pariobase <= 0x1000-LPT_EXTENT &&
+ !check_lpt(dev->ptt_out.pariobase))
+ printk(KERN_INFO "%s: PTT output: parallel port found at address 0x%x\n",
+ hfmodem_drvname, dev->ptt_out.pariobase);
+ else {
+ if (dev->ptt_out.pariobase > 0)
+ printk(KERN_WARNING "%s: PTT output: no parallel port found at address 0x%x\n",
+ hfmodem_drvname, dev->ptt_out.pariobase);
+ dev->ptt_out.pariobase = 0;
+ }
+ if (dev->ptt_out.midiiobase > 0 && dev->ptt_out.midiiobase <= 0x1000-MIDI_EXTENT &&
+ check_midi(dev->ptt_out.midiiobase))
+ printk(KERN_INFO "%s: PTT output: midi port found at address 0x%x\n",
+ hfmodem_drvname, dev->ptt_out.midiiobase);
+ else {
+ if (dev->ptt_out.midiiobase > 0)
+ printk(KERN_WARNING "%s: PTT output: no midi port found at address 0x%x\n",
+ hfmodem_drvname, dev->ptt_out.midiiobase);
+ dev->ptt_out.midiiobase = 0;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_open(struct hfmodem_state *dev)
+{
+ dev->ptt_out.flags = 0;
+ if (dev->ptt_out.seriobase > 0) {
+ if (!check_region(dev->ptt_out.seriobase, SER_EXTENT)) {
+ request_region(dev->ptt_out.seriobase, SER_EXTENT, "hfmodem ser ptt");
+ dev->ptt_out.flags |= SP_SER;
+ outb(0, UART_IER(dev->ptt_out.seriobase));
+ /* 5 bits, 1 stop, no parity, no break, Div latch access */
+ outb(0x80, UART_LCR(dev->ptt_out.seriobase));
+ outb(0, UART_DLM(dev->ptt_out.seriobase));
+ outb(1, UART_DLL(dev->ptt_out.seriobase)); /* as fast as possible */
+ /* LCR and MCR set by output_status */
+ } else
+ printk(KERN_WARNING "%s: PTT output: serial port at 0x%x busy\n",
+ hfmodem_drvname, dev->ptt_out.seriobase);
+ }
+ if (dev->ptt_out.pariobase > 0) {
+ if (!check_region(dev->ptt_out.pariobase, LPT_EXTENT)) {
+ request_region(dev->ptt_out.pariobase, LPT_EXTENT, "hfmodem par ptt");
+ dev->ptt_out.flags |= SP_PAR;
+ } else
+ printk(KERN_WARNING "%s: PTT output: parallel port at 0x%x busy\n",
+ hfmodem_drvname, dev->ptt_out.pariobase);
+ }
+ if (dev->ptt_out.midiiobase > 0) {
+ if (!check_region(dev->ptt_out.midiiobase, MIDI_EXTENT)) {
+ request_region(dev->ptt_out.midiiobase, MIDI_EXTENT, "hfmodem midi ptt");
+ dev->ptt_out.flags |= SP_MIDI;
+ } else
+ printk(KERN_WARNING "%s: PTT output: midi port at 0x%x busy\n",
+ hfmodem_drvname, dev->ptt_out.midiiobase);
+ }
+ output_status(dev, 0);
+ printk(KERN_INFO "%s: PTT output:", hfmodem_drvname);
+ if (dev->ptt_out.flags & SP_SER)
+ printk(" serial interface at 0x%x", dev->ptt_out.seriobase);
+ if (dev->ptt_out.flags & SP_PAR)
+ printk(" parallel interface at 0x%x", dev->ptt_out.pariobase);
+ if (dev->ptt_out.flags & SP_MIDI)
+ printk(" mpu401 (midi) interface at 0x%x", dev->ptt_out.midiiobase);
+ if (!dev->ptt_out.flags)
+ printk(" none");
+ printk("\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_close(struct hfmodem_state *dev)
+{
+ /* release regions used for PTT output */
+ output_status(dev, 0);
+ if (dev->ptt_out.flags & SP_SER)
+ release_region(dev->ptt_out.seriobase, SER_EXTENT);
+ if (dev->ptt_out.flags & SP_PAR)
+ release_region(dev->ptt_out.pariobase, LPT_EXTENT);
+ if (dev->ptt_out.flags & SP_MIDI)
+ release_region(dev->ptt_out.midiiobase, MIDI_EXTENT);
+ dev->ptt_out.flags = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#define INC_SAMPLE (1000000/HFMODEM_SRATE)
+#define INC_FRAGMENT (HFMODEM_FRAGSAMPLES*1000000/HFMODEM_SRATE)
+#define SIZE (HFMODEM_FRAGSAMPLES*HFMODEM_NUMFRAGS)
+
+static void hfmodem_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct hfmodem_state *dev = (struct hfmodem_state *)dev_id;
+ unsigned int dmaptr;
+ __s16 *s;
+ unsigned int curfrag, nfrags;
+ int i;
+ hfmodem_time_t l1time;
+
+ dmaptr = dev->scops->intack(dev);
+ l1time = hfmodem_refclock_current(dev, ((SIZE+dmaptr-dev->dma.last_dmaptr) % SIZE) *
+ INC_SAMPLE, 1);
+ curfrag = (dev->dma.last_dmaptr = dmaptr) / HFMODEM_FRAGSAMPLES;
+ l1time -= INC_SAMPLE * (SIZE+dmaptr-dev->dma.fragptr*HFMODEM_FRAGSAMPLES) % SIZE;
+ sti();
+ /*
+ * handle receiving
+ */
+ if (dev->dma.ptt_frames <= 0) {
+ while (dev->dma.fragptr != curfrag) {
+ if (dev->dma.fragptr < HFMODEM_EXCESSFRAGS) {
+ s = dev->dma.buf + SIZE + HFMODEM_FRAGSAMPLES * dev->dma.fragptr;
+ memcpy(s, s - SIZE, HFMODEM_FRAGSIZE);
+ } else
+ s = dev->dma.buf + HFMODEM_FRAGSAMPLES * dev->dma.fragptr;
+ if (dev->sbuf.kbuf && dev->sbuf.kptr && dev->sbuf.rem > 0) {
+ i = HFMODEM_FRAGSAMPLES;
+ if (i > dev->sbuf.rem)
+ i = dev->sbuf.rem;
+ memcpy(dev->sbuf.kptr, s, i * sizeof(s[0]));
+ dev->sbuf.rem -= i;
+ dev->sbuf.kptr += i;
+ }
+ hfmodem_input_samples(dev, l1time, INC_SAMPLE, s);
+ l1time += INC_FRAGMENT;
+ dev->dma.fragptr++;
+ if (dev->dma.fragptr >= HFMODEM_NUMFRAGS)
+ dev->dma.fragptr = 0;
+ }
+ /*
+ * check for output
+ */
+ if (hfmodem_next_tx_event(dev, l1time) > (long)INC_FRAGMENT/2)
+ goto int_return;
+ /*
+ * start output
+ */
+ output_status(dev, 1);
+ dev->scops->prepare_output(dev);
+ dev->dma.last_dmaptr = 0;
+ /*
+ * clock adjust
+ */
+ l1time = hfmodem_refclock_current(dev, 0, 0);
+ /*
+ * fill first two fragments
+ */
+ dev->dma.ptt_frames = 1;
+ for (i = 0; i < 2 && i < HFMODEM_NUMFRAGS; i++)
+ if (hfmodem_output_samples(dev, l1time+i*INC_FRAGMENT, INC_SAMPLE,
+ dev->dma.buf+i*HFMODEM_FRAGSAMPLES))
+ dev->dma.ptt_frames = i + 1;
+ dev->dma.lastfrag = 0;
+ dev->scops->trigger_output(dev);
+ /*
+ * finish already pending rx requests
+ */
+ hfmodem_finish_pending_rx_requests(dev);
+ goto int_return;
+ }
+ /*
+ * handle transmitting
+ */
+ nfrags = HFMODEM_NUMFRAGS + curfrag - dev->dma.lastfrag;
+ dev->dma.lastfrag = curfrag;
+ if (nfrags >= HFMODEM_NUMFRAGS)
+ nfrags -= HFMODEM_NUMFRAGS;
+ dev->dma.ptt_frames -= nfrags;
+ if (dev->dma.ptt_frames < 0)
+ dev->dma.ptt_frames = 0;
+ while (dev->dma.ptt_frames < HFMODEM_NUMFRAGS && dev->dma.ptt_frames < 4 &&
+ hfmodem_output_samples(dev, l1time+dev->dma.ptt_frames*INC_FRAGMENT,
+ INC_SAMPLE, dev->dma.buf + HFMODEM_FRAGSAMPLES *
+ ((curfrag + dev->dma.ptt_frames) % HFMODEM_NUMFRAGS)))
+ dev->dma.ptt_frames++;
+ if (dev->dma.ptt_frames > 0)
+ goto int_return;
+ /*
+ * start receiving
+ */
+ output_status(dev, 0);
+ dev->dma.last_dmaptr = 0;
+ dev->dma.lastfrag = 0;
+ dev->dma.fragptr = 0;
+ dev->dma.ptt_frames = 0;
+ dev->scops->prepare_input(dev);
+ dev->scops->trigger_input(dev);
+ hfmodem_refclock_current(dev, 0, 0); /* needed to reset the time difference */
+int_return:
+ hfmodem_wakeup(dev);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hfmodem_close(struct inode *inode, struct file *file)
+{
+ struct hfmodem_state *dev = &hfmodem_state[0];
+
+ if (!dev->active)
+ return -EPERM;
+ dev->active = 0;
+ dev->scops->stop(dev);
+ free_irq(dev->io.irq, dev);
+ disable_dma(dev->io.dma);
+ free_dma(dev->io.dma);
+ release_region(dev->io.base_addr, dev->scops->extent);
+ kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS));
+ hfmodem_clear_rq(dev);
+ if (dev->sbuf.kbuf) {
+ kfree_s(dev->sbuf.kbuf, dev->sbuf.size);
+ dev->sbuf.kbuf = dev->sbuf.kptr = NULL;
+ dev->sbuf.size = dev->sbuf.rem = 0;
+ }
+ output_close(dev);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hfmodem_open(struct inode *inode, struct file *file)
+{
+ struct hfmodem_state *dev = &hfmodem_state[0];
+
+ if (dev->active)
+ return -EBUSY;
+ if (!dev->scops)
+ return -EPERM;
+ /*
+ * clear vars
+ */
+ memset(&dev->l1, 0, sizeof(dev->l1));
+ dev->dma.last_dmaptr = 0;
+ dev->dma.lastfrag = 0;
+ dev->dma.fragptr = 0;
+ dev->dma.ptt_frames = 0;
+ /*
+ * allocate memory
+ */
+ if (!(dev->dma.buf = kmalloc(HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS), GFP_KERNEL | GFP_DMA)))
+ return -ENOMEM;
+ /*
+ * allocate resources
+ */
+ if (request_dma(dev->io.dma, hfmodem_drvname)) {
+ kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS));
+ return -EBUSY;
+ }
+ if (request_irq(dev->io.irq, hfmodem_interrupt, SA_INTERRUPT, hfmodem_drvname, dev)) {
+ free_dma(dev->io.dma);
+ kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS));
+ return -EBUSY;
+ }
+ request_region(dev->io.base_addr, dev->scops->extent, hfmodem_drvname);
+
+ /* clear requests */
+ dev->active++;
+ MOD_INC_USE_COUNT;
+ hfmodem_refclock_init(dev);
+ output_open(dev);
+ dev->scops->init(dev);
+ dev->scops->prepare_input(dev);
+ dev->scops->trigger_input(dev);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct file_operations hfmodem_fops = {
+ NULL, /* hfmodem_seek */
+ NULL, /* hfmodem_read */
+ NULL, /* hfmodem_write */
+ NULL, /* hfmodem_readdir */
+#if LINUX_VERSION_CODE >= 0x20100
+ hfmodem_poll, /* hfmodem_poll */
+#else
+ hfmodem_select, /* hfmodem_select */
+#endif
+ hfmodem_ioctl, /* hfmodem_ioctl */
+ NULL, /* hfmodem_mmap */
+ hfmodem_open, /* hfmodem_open */
+ hfmodem_close, /* hfmodem_close */
+ NULL, /* hfmodem_fsync */
+ NULL, /* hfmodem_fasync */
+ NULL, /* hfmodem_check_media_change */
+ NULL /* hfmodem_revalidate */
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct miscdevice hfmodem_device = {
+ HFMODEM_MINOR, hfmodem_drvname, &hfmodem_fops
+};
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * Command line parameters
+ */
+
+static int hw = 0;
+static unsigned int iobase = 0x220;
+static unsigned int irq = 7;
+static unsigned int dma = 1;
+
+static unsigned int serio = 0;
+static unsigned int pario = 0;
+static unsigned int midiio = 0;
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(hw, "i");
+MODULE_PARM_DESC(hw, "hardware type: 0=SBC, 1=WSS");
+MODULE_PARM(iobase, "i");
+MODULE_PARM_DESC(iobase, "io base address");
+MODULE_PARM(irq, "i");
+MODULE_PARM_DESC(irq, "interrupt number");
+MODULE_PARM(dma, "i");
+MODULE_PARM_DESC(dma, "dma number (>=4 for SB16/32/64/etc, <=3 for the rest)");
+MODULE_PARM(serio, "i");
+MODULE_PARM_DESC(serio, "address of serial port to output PTT");
+MODULE_PARM(pario, "i");
+MODULE_PARM_DESC(pario, "address of parial port to output PTT");
+MODULE_PARM(midiio, "i");
+MODULE_PARM_DESC(midiio, "address of midi (MPU401) port to output PTT");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("HF FSK modem code");
+
+/* these are the module parameters from refclock.c */
+
+MODULE_PARM(scale_tvusec, "i");
+MODULE_PARM_DESC(scale_tvusec, "Scaling value for the tv_usec field (can be obta
+ined by refclock)");
+
+#ifdef __i386__
+MODULE_PARM(scale_rdtsc, "i");
+MODULE_PARM_DESC(scale_rdtsc, "Scaling value for the rdtsc counter (can be obtai
+ned by refclock)");
+MODULE_PARM(rdtsc_ok, "i");
+MODULE_PARM_DESC(rdtsc_ok, "Set to 0 to disable the use of the rdtsc instruction
+");
+#endif /* __i386__ */
+
+#endif
+
+__initfunc(int init_module(void))
+{
+ int i;
+
+ printk(hfmodem_drvinfo);
+ memset(hfmodem_state, 0, sizeof(hfmodem_state));
+ memset(hfmodem_correlator_cache, 0, sizeof(hfmodem_correlator_cache));
+ hfmodem_state[0].io.base_addr = iobase;
+ hfmodem_state[0].io.irq = irq;
+ hfmodem_state[0].io.dma = dma;
+ hfmodem_state[0].ptt_out.seriobase = serio;
+ hfmodem_state[0].ptt_out.pariobase = pario;
+ hfmodem_state[0].ptt_out.midiiobase = midiio;
+ hfmodem_refclock_probe();
+ output_check(&hfmodem_state[0]);
+#if defined(CONFIG_HFMODEM_WSS) && defined(CONFIG_HFMODEM_SBC)
+ if (hw)
+ i = hfmodem_wssprobe(&hfmodem_state[0]);
+ else
+ i = hfmodem_sbcprobe(&hfmodem_state[0]);
+#else
+ i = -EINVAL;
+#ifdef CONFIG_HFMODEM_WSS
+ i = hfmodem_wssprobe(&hfmodem_state[0]);
+#endif
+#ifdef CONFIG_HFMODEM_SBC
+ i = hfmodem_sbcprobe(&hfmodem_state[0]);
+#endif
+#endif
+ if (i)
+ return i;
+ if ((i = misc_register(&hfmodem_device))) {
+ printk(KERN_ERR "%s: cannot register misc device\n", hfmodem_drvname);
+ return i;
+ }
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ misc_deregister(&hfmodem_device);
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+
+static int hw = 0;
+
+__initfunc(void hfmodem_setup(char *str, int *ints))
+{
+ if (ints[0] < 7) {
+ printk(KERN_WARNING "%s: setup: too few parameters\n", hfmodem_drvname);
+ return;
+ }
+ memset(hfmodem_state, 0, sizeof(hfmodem_state));
+ memset(hfmodem_correlator_cache, 0, sizeof(hfmodem_correlator_cache));
+ hw = ints[1];
+ hfmodem_state[0].io.base_addr = ints[2];
+ hfmodem_state[0].io.irq = ints[3];
+ hfmodem_state[0].io.dma = ints[4];
+ if (ints[0] >= 8)
+ hfmodem_state[0].ptt_out.seriobase = ints[5];
+ if (ints[0] >= 9)
+ hfmodem_state[0].ptt_out.pariobase = ints[6];
+ if (ints[0] >= 10)
+ hfmodem_state[0].ptt_out.midiiobase = ints[7];
+ hfmodem_refclock_setscale(ints[ints[0]-2], ints[ints[0]-1], ints[ints[0]]);
+}
+
+__initfunc(void hfmodem_init(void))
+{
+ int i;
+
+ printk(hfmodem_drvinfo);
+ hfmodem_refclock_probe();
+ output_check(&hfmodem_state[0]);
+#if defined(CONFIG_HFMODEM_WSS) && defined(CONFIG_HFMODEM_SBC)
+ if (hw)
+ i = hfmodem_wssprobe(&hfmodem_state[0]);
+ else
+ i = hfmodem_sbcprobe(&hfmodem_state[0]);
+#else
+ i = -EINVAL;
+#ifdef CONFIG_HFMODEM_WSS
+ i = hfmodem_wssprobe(&hfmodem_state[0]);
+#endif
+#ifdef CONFIG_HFMODEM_SBC
+ i = hfmodem_sbcprobe(&hfmodem_state[0]);
+#endif
+#endif
+ if (i) {
+ printk(KERN_ERR "%s: soundcard probe failed\n", hfmodem_drvname);
+ return;
+ }
+ if ((i = misc_register(&hfmodem_device))) {
+ printk(KERN_ERR "%s: cannot register misc device\n", hfmodem_drvname);
+ return;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+#endif /* MODULE */
+
diff --git a/drivers/char/hfmodem/modem.c b/drivers/char/hfmodem/modem.c
new file mode 100644
index 000000000..4b21edd09
--- /dev/null
+++ b/drivers/char/hfmodem/modem.c
@@ -0,0 +1,792 @@
+/*****************************************************************************/
+
+/*
+ * modem.c -- Linux soundcard HF FSK driver,
+ * Modem code.
+ *
+ * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+/*****************************************************************************/
+
+
+#include <linux/wait.h>
+#include <linux/malloc.h>
+#include <linux/hfmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+ int i = verify_area(VERIFY_READ, from, n);
+ if (i)
+ return i;
+ memcpy_fromfs(to, from, n);
+ return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+ int i = verify_area(VERIFY_WRITE, to, n);
+ if (i)
+ return i;
+ memcpy_tofs(to, from, n);
+ return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+struct hfmodem_correlator_cache hfmodem_correlator_cache[HFMODEM_CORRELATOR_CACHE];
+
+/* --------------------------------------------------------------------- */
+
+#include "tables.h"
+
+#define M_PI 3.14159265358979323846 /* pi */
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int isimplecos(unsigned int arg)
+{
+ return isintab[((arg+0x4000) >> (16-SINTABBITS)) & (SINTABSIZE-1)];
+}
+
+extern __inline__ int isimplesin(unsigned int arg)
+{
+ return isintab[(arg >> (16-SINTABBITS)) & (SINTABSIZE-1)];
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int itblcos(unsigned int arg)
+{
+ unsigned int x;
+ int dx;
+ int s, c;
+
+ x = (arg + (0x8000 >> SINTABBITS)) & (0xffffu & (0xffffu << (16-SINTABBITS)));
+ dx = arg - x;
+ x >>= (16-SINTABBITS);
+ c = isintab[x+(0x4000 >> (16-SINTABBITS))];
+ s = isintab[x];
+ return c - ((s * dx * (int)(M_PI*64.0)) >> 21);
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void itblcossin(unsigned int arg, int *cos, int *sin)
+{
+ unsigned int x;
+ int dx;
+ int s, c;
+
+ x = (arg + (0x8000 >> SINTABBITS)) & (0xffffu & (0xffffu << (16-SINTABBITS)));
+ dx = arg - x;
+ x >>= (16-SINTABBITS);
+ c = isintab[x+(0x4000 >> (16-SINTABBITS))];
+ s = isintab[x];
+ *cos = c - ((s * dx * (int)(M_PI*64.0)) >> 21);
+ *sin = s + ((c * dx * (int)(M_PI*64.0)) >> 21);
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned short random_seed;
+
+extern __inline__ unsigned short random_num(void)
+{
+ random_seed = 28629 * random_seed + 157;
+ return random_seed;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * correlator cache routines
+ */
+
+extern __inline__ void cc_lock(unsigned int u)
+{
+ if (u >= HFMODEM_CORRELATOR_CACHE)
+ return;
+ hfmodem_correlator_cache[u].refcnt++;
+}
+
+extern __inline__ void cc_unlock(unsigned int u)
+{
+ if (u >= HFMODEM_CORRELATOR_CACHE)
+ return;
+ if ((--hfmodem_correlator_cache[u].refcnt) <= 0) {
+ unsigned int i;
+
+ for (i = 0; i < HFMODEM_CORRELATOR_CACHE; i++)
+ if (hfmodem_correlator_cache[i].lru < 32767)
+ hfmodem_correlator_cache[i].lru++;
+ hfmodem_correlator_cache[u].lru = 0;
+ hfmodem_correlator_cache[u].refcnt = 0;
+ }
+}
+
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int cc_lookup(unsigned short phinc0, unsigned short phinc1)
+{
+ unsigned int j;
+
+ /* find correlator cache entry */
+ for (j = 0; j < HFMODEM_CORRELATOR_CACHE; j++)
+ if (hfmodem_correlator_cache[j].phase_incs[0] == phinc0 &&
+ hfmodem_correlator_cache[j].phase_incs[1] == phinc1)
+ return j;
+ return ~0;
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int cc_replace(void)
+{
+ unsigned int j, k = HFMODEM_CORRELATOR_CACHE;
+ int l = -1;
+
+ for (j = 0; j < HFMODEM_CORRELATOR_CACHE; j++)
+ if (hfmodem_correlator_cache[j].refcnt <= 0 && hfmodem_correlator_cache[j].lru > l) {
+ k = j;
+ l = hfmodem_correlator_cache[j].lru;
+ }
+ if (k < HFMODEM_CORRELATOR_CACHE)
+ return k;
+ printk(KERN_ERR "%s: modem: out of filter coefficient cache entries\n", hfmodem_drvname);
+ return random_num() % HFMODEM_CORRELATOR_CACHE;
+}
+
+/* --------------------------------------------------------------------- */
+
+#define SH1 8 /* min. ceil(log2(L1CORR_LEN)) - (31-(2*15-1)) */
+#define SH2 (3*15-2*SH1)
+
+#ifdef __i386__
+
+extern __inline__ int icorr(int n, const int *coeff, const short *inp)
+{
+ int ret, rethi, tmp1 = 0, tmp2 = 0;
+
+ __asm__("\n1:\n\t"
+ "movswl (%0),%%eax\n\t"
+ "imull (%1)\n\t"
+ "subl $2,%0\n\t"
+ "addl $4,%1\n\t"
+ "addl %%eax,%3\n\t"
+ "adcl %%edx,%4\n\t"
+ "decl %2\n\t"
+ "jne 1b\n\t"
+ : "=&S" (inp), "=&D" (coeff), "=&c" (n), "=m" (tmp1), "=m" (tmp2)
+ : "0" (inp), "1" (coeff), "2" (n)
+ : "ax", "dx");
+ __asm__("shrdl %2,%1,%0\n\t"
+ "# sarl %2,%1\n\t"
+ : "=&r" (ret), "=&r" (rethi)
+ : "i" (SH1), "0" (tmp1), "1" (tmp2));
+
+
+ return ret;
+}
+
+#else /* __i386__ */
+
+extern __inline__ int icorr(int n, const int *coeff, const short *inp)
+{
+ long long sum = 0;
+ int i;
+
+ for (i = n; i > 0; i--, coeff++, inp--)
+ sum += (*coeff) * (*inp);
+ sum >>= SH1;
+ return sum;
+}
+
+#endif /* __i386__ */
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ long long isqr(int x) __attribute__ ((const));
+
+extern __inline__ long long isqr(int x)
+{
+ return ((long long)x) * ((long long)x);
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ hfmodem_soft_t do_filter(struct hfmodem_l1_rxslot *slot, short *s)
+{
+ unsigned int cc = slot->corr_cache;
+ long long ll;
+
+ if (cc >= HFMODEM_CORRELATOR_CACHE) {
+ printk(KERN_ERR "do_filter: correlator cache index overrange\n");
+ return 0;
+ }
+ ll = isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[1][0], s)) +
+ isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[1][1], s)) -
+ isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[0][0], s)) -
+ isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[0][1], s));
+ ll >>= SH2;
+ return (ll * slot->scale) >> 23;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void cc_prepare(struct hfmodem_l1_rxslot *slot, unsigned short phinc0, unsigned short phinc1)
+{
+ unsigned int j, k, l, ph, phinc;
+
+ slot->scale = (1<<23) / (slot->corrlen*slot->corrlen);
+
+ j = cc_lookup(phinc0, phinc1);
+ if (j >= HFMODEM_CORRELATOR_CACHE) {
+ j = cc_replace();
+ /* calculate the correlator values */
+ printk(KERN_DEBUG "%s: corr cache calc: %u phases: 0x%04x 0x%04x\n",
+ hfmodem_drvname, j, phinc0, phinc1);
+ hfmodem_correlator_cache[j].phase_incs[0] = phinc0;
+ hfmodem_correlator_cache[j].phase_incs[1] = phinc1;
+ for (k = 0; k < 2; k++) {
+ phinc = hfmodem_correlator_cache[j].phase_incs[k];
+ for (ph = l = 0; l < HFMODEM_MAXCORRLEN; l++, ph = (ph + phinc) & 0xffff)
+ itblcossin(ph, &hfmodem_correlator_cache[j].correlator[k][0][l],
+ &hfmodem_correlator_cache[j].correlator[k][1][l]);
+ }
+ hfmodem_correlator_cache[j].refcnt = 0;
+
+#if 0
+ printk(KERN_DEBUG "%s: corr: %u ph: 0x%04x 0x%04x\n", hfmodem_drvname, j,
+ hfmodem_correlator_cache[j].phase_incs[0],
+ hfmodem_correlator_cache[j].phase_incs[1]);
+ for (l = 0; l < HFMODEM_MAXCORRLEN; l++)
+ printk(KERN_DEBUG "%s: corr: %6d %6d %6d %6d\n", hfmodem_drvname,
+ hfmodem_correlator_cache[j].correlator[0][0][l],
+ hfmodem_correlator_cache[j].correlator[0][1][l],
+ hfmodem_correlator_cache[j].correlator[1][0][l],
+ hfmodem_correlator_cache[j].correlator[1][1][l]);
+#endif
+ }
+ slot->corr_cache = j;
+ cc_lock(j);
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_clear_rq(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+ unsigned int i;
+
+ save_flags(flags);
+ cli();
+ for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ if (dev->l1.rxslots[i].state == ss_unused)
+ continue;
+ dev->l1.rxslots[i].state = ss_unused;
+ kfree_s(dev->l1.rxslots[i].data, dev->l1.rxslots[i].nbits * sizeof(hfmodem_soft_t));
+ }
+ for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_unused)
+ continue;
+ dev->l1.txslots[i].state = ss_unused;
+ kfree_s(dev->l1.txslots[i].data, (dev->l1.txslots[i].nbits + 7) >> 3);
+ }
+ for (i = 0; i < HFMODEM_CORRELATOR_CACHE; i++)
+ hfmodem_correlator_cache[i].refcnt = 0;
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+int hfmodem_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct hfmodem_state *dev = &hfmodem_state[0];
+ struct hfmodem_ioctl_fsk_tx_request txrq;
+ struct hfmodem_ioctl_fsk_rx_request rxrq;
+ struct hfmodem_ioctl_mixer_params mix;
+ struct hfmodem_ioctl_sample_params spar;
+ unsigned long flags;
+ unsigned int len;
+ int ret, i, idx;
+ void *data, *userdata;
+ hfmodem_id_t id;
+ hfmodem_time_t tm = 0;
+
+ if (!dev->active)
+ return -EBUSY;
+ switch(cmd) {
+ default:
+ return -EINVAL;
+
+ case HFMODEM_IOCTL_FSKTXREQUEST:
+ if ((ret = copy_from_user(&txrq, (void *)arg, sizeof(txrq))))
+ return ret;
+ if (txrq.nbits > HFMODEM_MAXBITS)
+ return -EINVAL;
+ len = (txrq.nbits + 7) >> 3;
+ if (!(data = kmalloc(len, GFP_KERNEL)))
+ return -ENOMEM;
+ if (copy_from_user(data, txrq.data, len)) {
+ kfree_s(data, len);
+ return -EFAULT;
+ }
+ save_flags(flags);
+ cli();
+ for (i = 0; i < HFMODEM_NUMTXSLOTS && dev->l1.txslots[i].state != ss_unused; i++);
+ if (i >= HFMODEM_NUMTXSLOTS) {
+ restore_flags(flags);
+ kfree_s(data, len);
+ return -EBUSY;
+ }
+ dev->l1.txslots[i].state = ss_ready;
+ dev->l1.txslots[i].tstart = txrq.tstart;
+ dev->l1.txslots[i].tinc = txrq.tinc;
+ dev->l1.txslots[i].data = data;
+ dev->l1.txslots[i].nbits = txrq.nbits;
+ dev->l1.txslots[i].cntbits = 0;
+ dev->l1.txslots[i].inv = txrq.inv ? 0xff : 0;
+ dev->l1.txslots[i].id = txrq.id;
+ dev->l1.txslots[i].phase_incs[0] = ((txrq.freq[0]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE)
+ & 0xffff;
+ dev->l1.txslots[i].phase_incs[1] = ((txrq.freq[1]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE)
+ & 0xffff;
+ restore_flags(flags);
+ return 0;
+
+ case HFMODEM_IOCTL_FSKRXREQUEST:
+ if ((ret = copy_from_user(&rxrq, (void *)arg, sizeof(rxrq))))
+ return ret;
+ if (rxrq.nbits > HFMODEM_MAXBITS)
+ return -EINVAL;
+ if (rxrq.baud < HFMODEM_MINBAUD || rxrq.baud > HFMODEM_MAXBAUD)
+ return -EINVAL;
+ len = rxrq.nbits * sizeof(hfmodem_soft_t);
+ if (verify_area(VERIFY_WRITE, rxrq.data, len))
+ return -EFAULT;
+ if (!(data = kmalloc(len, GFP_KERNEL)))
+ return -ENOMEM;
+ save_flags(flags);
+ cli();
+ for (i = 0; i < HFMODEM_NUMRXSLOTS && dev->l1.rxslots[i].state != ss_unused; i++);
+ if (i >= HFMODEM_NUMRXSLOTS) {
+ restore_flags(flags);
+ kfree_s(data, len);
+ return -EBUSY;
+ }
+ dev->l1.rxslots[i].state = ss_ready;
+ dev->l1.rxslots[i].tstart = rxrq.tstart;
+ dev->l1.rxslots[i].tinc = rxrq.tinc;
+ dev->l1.rxslots[i].data = data;
+ dev->l1.rxslots[i].userdata = rxrq.data;
+ dev->l1.rxslots[i].nbits = rxrq.nbits;
+ dev->l1.rxslots[i].cntbits = 0;
+ dev->l1.rxslots[i].id = rxrq.id;
+ dev->l1.rxslots[i].corrlen = HFMODEM_SRATE/rxrq.baud;
+ cc_prepare(dev->l1.rxslots+i,
+ ((rxrq.freq[0]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) & 0xffff,
+ ((rxrq.freq[1]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) & 0xffff);
+ restore_flags(flags);
+ return 0;
+
+ case HFMODEM_IOCTL_CLEARRQ:
+ hfmodem_clear_rq(dev);
+ return 0;
+
+ case HFMODEM_IOCTL_GETCURTIME:
+ return put_user(dev->l1.last_time + 20000L, (hfmodem_time_t *)arg); /* heuristic */
+
+ case HFMODEM_IOCTL_WAITRQ:
+ save_flags(flags);
+ cli();
+ ret = 0;
+ for (idx = -1, i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ if (dev->l1.rxslots[i].state == ss_unused)
+ continue;
+ if (dev->l1.rxslots[i].state != ss_retired) {
+ ret++;
+ continue;
+ }
+ if (idx < 0 || (signed)(tm - dev->l1.rxslots[i].tstart) > 0) {
+ idx = i;
+ tm = dev->l1.rxslots[i].tstart;
+ }
+ }
+ if (idx >= 0) {
+ cc_unlock(dev->l1.rxslots[idx].corr_cache);
+ id = dev->l1.rxslots[idx].id;
+ data = dev->l1.rxslots[idx].data;
+ userdata = dev->l1.rxslots[idx].userdata;
+ len = dev->l1.rxslots[idx].nbits * sizeof(hfmodem_soft_t);
+ dev->l1.rxslots[idx].state = ss_unused;
+ restore_flags(flags);
+ ret = copy_to_user(userdata, data, len);
+ kfree_s(data, len);
+ return (put_user(id, (hfmodem_id_t *)arg)) ? -EFAULT : ret;
+ }
+ for (idx = -1, i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_unused)
+ continue;
+ if (dev->l1.txslots[i].state != ss_retired) {
+ ret++;
+ continue;
+ }
+ if (idx < 0 || (signed)(tm - dev->l1.txslots[i].tstart) > 0) {
+ idx = i;
+ tm = dev->l1.txslots[i].tstart;
+ }
+ }
+ if (idx >= 0) {
+ id = dev->l1.txslots[idx].id;
+ data = dev->l1.txslots[idx].data;
+ len = (dev->l1.txslots[idx].nbits + 7) >> 3;
+ dev->l1.txslots[idx].state = ss_unused;
+ restore_flags(flags);
+ kfree_s(data, len);
+ return put_user(id, (hfmodem_id_t *)arg);
+ }
+ restore_flags(flags);
+ return ret ? -EAGAIN : -EPIPE;
+
+ case HFMODEM_IOCTL_MIXERPARAMS:
+ if ((ret = copy_from_user(&mix, (void *)arg, sizeof(mix))))
+ return ret;
+ dev->scops->mixer(dev, mix.src, mix.igain, mix.ogain);
+ return 0;
+
+ case HFMODEM_IOCTL_SAMPLESTART:
+ save_flags(flags);
+ cli();
+ if (dev->sbuf.kbuf)
+ kfree_s(dev->sbuf.kbuf, dev->sbuf.size);
+ dev->sbuf.kbuf = dev->sbuf.kptr = NULL;
+ dev->sbuf.size = dev->sbuf.rem = 0;
+ restore_flags(flags);
+ if ((ret = copy_from_user(&spar, (void *)arg, sizeof(spar))))
+ return ret;
+ if (spar.len == 0)
+ return 0;
+ if (spar.len < 2 || spar.len > 8192)
+ return -EINVAL;
+ if (verify_area(VERIFY_WRITE, spar.data, spar.len * sizeof(__s16)))
+ return -EFAULT;
+ if (!(dev->sbuf.kbuf = kmalloc(spar.len * sizeof(__s16), GFP_KERNEL)))
+ return -ENOMEM;
+ save_flags(flags);
+ cli();
+ dev->sbuf.kptr = dev->sbuf.kbuf;
+ dev->sbuf.size = spar.len * sizeof(__s16);
+ dev->sbuf.rem = spar.len;
+ dev->sbuf.ubuf = spar.data;
+ restore_flags(flags);
+ return 0;
+
+ case HFMODEM_IOCTL_SAMPLEFINISHED:
+ save_flags(flags);
+ cli();
+ if (dev->sbuf.rem > 0) {
+ restore_flags(flags);
+ return -EAGAIN;
+ }
+ if (!dev->sbuf.kbuf || !dev->sbuf.size) {
+ restore_flags(flags);
+ return -EPIPE;
+ }
+ restore_flags(flags);
+ ret = copy_to_user(dev->sbuf.ubuf, dev->sbuf.kbuf, dev->sbuf.size);
+ kfree_s(dev->sbuf.kbuf, dev->sbuf.size);
+ dev->sbuf.kbuf = dev->sbuf.kptr = NULL;
+ dev->sbuf.size = dev->sbuf.rem = 0;
+ return ret;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= 0x20100
+
+unsigned int hfmodem_poll(struct file *file, poll_table *wait)
+{
+ struct hfmodem_state *dev = &hfmodem_state[0];
+ unsigned long flags;
+ int i, cnt1, cnt2;
+
+ save_flags(flags);
+ cli();
+ for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_retired)
+ cnt1++;
+ if (dev->l1.txslots[i].state != ss_unused)
+ cnt2++;
+ }
+ for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ if (dev->l1.rxslots[i].state == ss_retired)
+ cnt1++;
+ if (dev->l1.rxslots[i].state != ss_unused)
+ cnt2++;
+ }
+ restore_flags(flags);
+ poll_wait(&dev->wait, wait);
+ if (cnt1 || !cnt2)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+#else
+
+int hfmodem_select(struct inode *inode, struct file *file, int sel_type, select_table *wait)
+{
+ struct hfmodem_state *dev = &hfmodem_state[0];
+ unsigned long flags;
+ int i, cnt1, cnt2;
+
+ if (sel_type == SEL_IN) {
+ save_flags(flags);
+ cli();
+ for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_retired)
+ cnt1++;
+ if (dev->l1.txslots[i].state != ss_unused)
+ cnt2++;
+ }
+ for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ if (dev->l1.rxslots[i].state == ss_retired)
+ cnt1++;
+ if (dev->l1.rxslots[i].state != ss_unused)
+ cnt2++;
+ }
+ restore_flags(flags);
+ if (cnt1 || !cnt2)
+ return 1;
+ select_wait(&dev->wait, wait);
+ }
+ return 0;
+}
+
+#endif
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int l1fsk_phinc(struct hfmodem_l1_txslot *txs, unsigned int nbit)
+{
+ return txs->phase_incs[!!((txs->data[nbit >> 3] ^ txs->inv) & (1 << (nbit & 7)))];
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_input_samples(struct hfmodem_state *dev, hfmodem_time_t tstart,
+ hfmodem_time_t tinc, __s16 *samples)
+{
+ hfmodem_time_t tst, tend;
+ __s16 *s;
+ int i, j;
+ hfmodem_soft_t sample;
+
+ dev->l1.last_time = tstart + (HFMODEM_FRAGSAMPLES-1) * tinc;
+ for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ struct hfmodem_l1_rxslot *rxs = dev->l1.rxslots + i;
+
+ if (rxs->state == ss_unused || rxs->state == ss_retired)
+ continue;
+ tst = tstart - (rxs->corrlen-1) * tinc;
+ tend = tst + (HFMODEM_FRAGSAMPLES-1) * tinc;
+ if (rxs->state == ss_ready) {
+ if ((signed)(rxs->tstart - tend) > 0)
+ continue;
+ rxs->state = ss_oper;
+ }
+ for (s = samples, j = 0; j < HFMODEM_FRAGSAMPLES; j++, s++, tst += tinc)
+ if ((signed)(rxs->tstart - tst) <= 0) {
+ sample = do_filter(rxs, s);
+ while ((signed)(rxs->tstart - tst) <= 0 &&
+ rxs->cntbits < rxs->nbits) {
+ rxs->data[rxs->cntbits] = sample;
+ rxs->cntbits++;
+ rxs->tstart += rxs->tinc;
+ }
+ if (rxs->cntbits >= rxs->nbits) {
+ rxs->state = ss_retired;
+ break;
+ }
+ }
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int output_one_sample(struct hfmodem_state *dev, hfmodem_time_t tm)
+{
+ int i, j, k;
+ struct hfmodem_l1_txslot *txs;
+ /*
+ * first activate new output slots
+ */
+ for (j = -1, i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ txs = dev->l1.txslots + i;
+ if (txs->state == ss_ready && (signed)(txs->tstart - tm) <= 0) {
+ for (k = 0; k < HFMODEM_NUMTXSLOTS; k++) {
+ if (dev->l1.txslots[k].state != ss_oper)
+ continue;
+ dev->l1.txslots[k].state = ss_retired;
+ }
+ txs->state = ss_oper;
+ txs->tstart += txs->tinc;
+ txs->phinc = l1fsk_phinc(txs, 0);
+ txs->cntbits = 1;
+ };
+ if (txs->state != ss_oper)
+ continue;
+ j = i;
+ }
+ if (j < 0 || j >= HFMODEM_NUMTXSLOTS)
+ return 0;
+ /*
+ * calculate the current slot
+ */
+ txs = dev->l1.txslots + j;
+ while ((signed)(txs->tstart - tm) <= 0) {
+ if (txs->cntbits >= txs->nbits) {
+ txs->state = ss_retired;
+ return 0;
+ }
+ txs->tstart += txs->tinc;
+ txs->phinc = l1fsk_phinc(txs, txs->cntbits);
+ txs->cntbits++;
+ }
+ return txs->phinc;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hfmodem_output_samples(struct hfmodem_state *dev, hfmodem_time_t tstart,
+ hfmodem_time_t tinc, __s16 *samples)
+{
+ int i, j;
+ hfmodem_time_t tend = tstart + (HFMODEM_FRAGSAMPLES-1) * tinc;
+
+ for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_oper)
+ break;
+ if (dev->l1.txslots[i].state == ss_ready &&
+ (signed)(dev->l1.txslots[i].tstart - tend) <= 0)
+ break;
+ }
+ if (i >= HFMODEM_NUMTXSLOTS)
+ return 0;
+ for (j = 0; j < HFMODEM_FRAGSAMPLES; j++, tstart += tinc, samples++) {
+ *samples = isimplecos(dev->l1.tx_phase);
+ dev->l1.tx_phase += output_one_sample(dev, tstart);
+ }
+ return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+long hfmodem_next_tx_event(struct hfmodem_state *dev, hfmodem_time_t curr)
+{
+ long diff = LONG_MAX, t;
+ int i;
+
+ for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_oper)
+ if (diff > 0)
+ diff = 0;
+ if (dev->l1.txslots[i].state == ss_ready) {
+ t = dev->l1.txslots[i].tstart - curr;
+ if (t < diff)
+ diff = t;
+ }
+ }
+ return diff;
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_finish_pending_rx_requests(struct hfmodem_state *dev)
+{
+ int i;
+
+ for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ if (dev->l1.rxslots[i].state != ss_oper)
+ continue;
+ while (dev->l1.rxslots[i].cntbits < dev->l1.rxslots[i].nbits) {
+ dev->l1.rxslots[i].data[dev->l1.rxslots[i].cntbits] = 0;
+ dev->l1.rxslots[i].cntbits++;
+ }
+ dev->l1.rxslots[i].state = ss_retired;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_wakeup(struct hfmodem_state *dev)
+{
+ int i, cnt1, cnt2;
+
+ for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+ if (dev->l1.txslots[i].state == ss_retired)
+ cnt1++;
+ if (dev->l1.txslots[i].state != ss_unused)
+ cnt2++;
+ }
+ for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+ if (dev->l1.rxslots[i].state == ss_retired)
+ cnt1++;
+ if (dev->l1.rxslots[i].state != ss_unused)
+ cnt2++;
+ }
+ if (cnt1 || !cnt2)
+ wake_up_interruptible(&dev->wait);
+}
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/char/hfmodem/refclock.c b/drivers/char/hfmodem/refclock.c
new file mode 100644
index 000000000..6617673ca
--- /dev/null
+++ b/drivers/char/hfmodem/refclock.c
@@ -0,0 +1,189 @@
+/*****************************************************************************/
+
+/*
+ * refclock.c -- Linux soundcard HF FSK driver,
+ * Reference clock routines.
+ *
+ * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/hfmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+/*
+ * command line params
+ */
+
+static unsigned int scale_tvusec = 1UL<<24;
+
+#ifdef __i386__
+static unsigned int scale_rdtsc = 0;
+static int rdtsc_ok = 1;
+#endif /* __i386__ */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef __i386__
+
+__initfunc(static void i386_capability(void))
+{
+ unsigned long flags;
+ unsigned long fl1;
+ union {
+ struct {
+ unsigned int ebx, edx, ecx;
+ } r;
+ unsigned char s[13];
+ } id;
+ unsigned int eax;
+ unsigned int x86_capability;
+
+ save_flags(flags);
+ flags |= 0x200000;
+ restore_flags(flags);
+ save_flags(flags);
+ fl1 = flags;
+ flags &= ~0x200000;
+ restore_flags(flags);
+ save_flags(flags);
+ if (!(fl1 & 0x200000) || (flags & 0x200000)) {
+ printk(KERN_WARNING "%s: cpu does not support CPUID\n", hfmodem_drvname);
+ return;
+ }
+ __asm__ ("cpuid" : "=a" (eax), "=b" (id.r.ebx), "=c" (id.r.ecx), "=d" (id.r.edx) :
+ "0" (0));
+ id.s[12] = 0;
+ if (eax < 1) {
+ printk(KERN_WARNING "%s: cpu (vendor string %s) does not support capability "
+ "list\n", hfmodem_drvname, id.s);
+ return;
+ }
+ printk(KERN_INFO "%s: cpu: vendor string %s ", hfmodem_drvname, id.s);
+ __asm__ ("cpuid" : "=a" (eax), "=d" (x86_capability) : "0" (1) : "ebx", "ecx");
+ printk("fam %d mdl %d step %d cap 0x%x\n", (eax >> 8) & 15, (eax >> 4) & 15, eax & 15,
+ x86_capability);
+ if (x86_capability & 0x10)
+ rdtsc_ok = 1;
+ else
+ printk(KERN_INFO "%s: cpu does not support the rdtsc instruction\n", hfmodem_drvname);
+}
+#endif /* __i386__ */
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(void hfmodem_refclock_probe(void))
+{
+#ifdef __i386__
+ if (rdtsc_ok) {
+ rdtsc_ok = 0;
+ i386_capability();
+ if (rdtsc_ok) {
+ unsigned int tmp0, tmp1, tmp2, tmp3;
+ __asm__("rdtsc" : "=a" (tmp0), "=d" (tmp1));
+ __asm__("rdtsc" : "=a" (tmp2), "=d" (tmp3));
+ if (tmp0 == tmp2 && tmp1 == tmp3) {
+ rdtsc_ok = 0;
+ printk(KERN_WARNING "%s: rdtsc unusable, does not change\n",
+ hfmodem_drvname);
+ }
+ }
+ }
+ printk(KERN_INFO "%s: using %s as timing source\n", hfmodem_drvname,
+ rdtsc_ok ? "rdtsc" : "gettimeofday");
+#endif /* __i386__ */
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_refclock_init(struct hfmodem_state *dev)
+{
+ struct timeval tv;
+
+ dev->clk.lasttime = 0;
+#ifdef __i386__
+ if (rdtsc_ok) {
+ __asm__("rdtsc;" : "=&d" (dev->clk.starttime_hi), "=&a" (dev->clk.starttime_lo));
+ return;
+ }
+#endif /* __i386__ */
+ do_gettimeofday(&tv);
+ dev->clk.last_tvusec = tv.tv_usec;
+ dev->clk.time_cnt = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+hfmodem_time_t hfmodem_refclock_current(struct hfmodem_state *dev, hfmodem_time_t expected, int exp_valid)
+{
+ struct timeval tv;
+ hfmodem_time_t curtime;
+ long diff;
+
+#ifdef __i386__
+ if (rdtsc_ok) {
+ unsigned int tmp0, tmp1;
+ unsigned int tmp2, tmp3;
+
+ __asm__("rdtsc;\n\t"
+ "subl %2,%%eax\n\t"
+ "sbbl %3,%%edx\n\t" : "=&a" (tmp0), "=&d" (tmp1)
+ : "m" (dev->clk.starttime_lo), "m" (dev->clk.starttime_hi) : "ax", "dx");
+ __asm__("mull %1" : "=d" (tmp2) : "m" (scale_rdtsc), "a" (tmp0) : "ax");
+ __asm__("mull %1" : "=a" (tmp3) : "m" (scale_rdtsc), "a" (tmp1) : "dx");
+ curtime = tmp2 + tmp3;
+ goto time_known;
+ }
+#endif /* __i386__ */
+ do_gettimeofday(&tv);
+ dev->clk.time_cnt += (unsigned)(1000000 + tv.tv_usec - dev->clk.last_tvusec) % 1000000;
+ dev->clk.last_tvusec = tv.tv_usec;
+ curtime = (dev->clk.time_cnt * scale_tvusec) >> 24;
+ time_known:
+ if (exp_valid && abs(diff = (curtime - dev->clk.lasttime - expected)) >= 1000)
+ printk(KERN_DEBUG "%s: refclock adjustment %ld more than 1ms\n",
+ hfmodem_drvname, diff);
+ return (dev->clk.lasttime = curtime);
+}
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/char/hfmodem/sbc.c b/drivers/char/hfmodem/sbc.c
new file mode 100644
index 000000000..8e4010619
--- /dev/null
+++ b/drivers/char/hfmodem/sbc.c
@@ -0,0 +1,741 @@
+/*****************************************************************************/
+
+/*
+ * sbc.c -- Linux soundcard HF FSK driver,
+ * Soundblaster specific functions.
+ *
+ * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/hfmodem.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+/* --------------------------------------------------------------------- */
+/*
+ * the sbc converter's registers
+ */
+#define DSP_RESET(iobase) (iobase+0x6)
+#define DSP_READ_DATA(iobase) (iobase+0xa)
+#define DSP_WRITE_DATA(iobase) (iobase+0xc)
+#define DSP_WRITE_STATUS(iobase) (iobase+0xc)
+#define DSP_DATA_AVAIL(iobase) (iobase+0xe)
+#define DSP_MIXER_ADDR(iobase) (iobase+0x4)
+#define DSP_MIXER_DATA(iobase) (iobase+0x5)
+#define DSP_INTACK_16BIT(iobase) (iobase+0xf)
+#define SBC_EXTENT 16
+
+/* --------------------------------------------------------------------- */
+/*
+ * SBC commands
+ */
+
+#define SBC_OUTPUT 0x14
+#define SBC_INPUT 0x24
+#define SBC_BLOCKSIZE 0x48
+#define SBC_HI_OUTPUT 0x91
+#define SBC_HI_INPUT 0x99
+#define SBC_LO_OUTPUT_AUTOINIT 0x1c
+#define SBC_LO_INPUT_AUTOINIT 0x2c
+#define SBC_HI_OUTPUT_AUTOINIT 0x90
+#define SBC_HI_INPUT_AUTOINIT 0x98
+#define SBC_IMMED_INT 0xf2
+#define SBC_GET_REVISION 0xe1
+#define ESS_GET_REVISION 0xe7
+#define ESS_EXTENDED_MODE 0xc6
+#define SBC_SPEAKER_ON 0xd1
+#define SBC_SPEAKER_OFF 0xd3
+#define SBC_DMA_ON 0xd0
+#define SBC_DMA_OFF 0xd4
+#define SBC_SAMPLE_RATE 0x40
+#define SBC_SAMPLE_RATE_OUT 0x41
+#define SBC_SAMPLE_RATE_IN 0x42
+#define SBC_MONO_8BIT 0xa0
+#define SBC_MONO_16BIT 0xa4
+#define SBC_STEREO_8BIT 0xa8
+#define SBC_STEREO_16BIT 0xac
+
+#define SBC4_OUT8_AI 0xc6
+#define SBC4_IN8_AI 0xce
+#define SBC4_MODE_UNS_MONO 0x00
+#define SBC4_MODE_SIGN_MONO 0x10
+
+#define SBC4_OUT16_AI 0xb6
+#define SBC4_IN16_AI 0xbe
+#define SBC4_OUT16_AI_NO_FIFO 0xb4
+#define SBC4_IN16_AI_NO_FIFO 0xbc
+
+/* --------------------------------------------------------------------- */
+
+extern const struct hfmodem_scops sbc4_scops;
+extern const struct hfmodem_scops ess_scops;
+
+/* --------------------------------------------------------------------- */
+
+static int reset_dsp(struct hfmodem_state *dev)
+{
+ int i;
+
+ outb(1, DSP_RESET(dev->io.base_addr));
+ udelay(3);
+ outb(0, DSP_RESET(dev->io.base_addr));
+ for (i = 0; i < 0xffff; i++)
+ if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80)
+ if (inb(DSP_READ_DATA(dev->io.base_addr)) == 0xaa)
+ return 1;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void write_dsp(struct hfmodem_state *dev, unsigned char data)
+{
+ int i;
+
+ for (i = 0; i < 0xffff; i++)
+ if (!(inb(DSP_WRITE_STATUS(dev->io.base_addr)) & 0x80)) {
+ outb(data, DSP_WRITE_DATA(dev->io.base_addr));
+ return;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static int read_dsp(struct hfmodem_state *dev, unsigned char *data)
+{
+ int i;
+
+ if (!data)
+ return 0;
+ for (i = 0; i < 0xffff; i++)
+ if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80) {
+ *data = inb(DSP_READ_DATA(dev->io.base_addr));
+ return 1;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void write_ess(struct hfmodem_state *dev, unsigned char reg, unsigned char data)
+{
+ write_dsp(dev, reg);
+ write_dsp(dev, data);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int read_ess(struct hfmodem_state *dev, unsigned char reg, unsigned char *data)
+{
+ write_dsp(dev, 0xc0);
+ write_dsp(dev, reg);
+ return read_dsp(dev, data);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int reset_ess(struct hfmodem_state *dev)
+{
+ int i;
+
+ outb(3, DSP_RESET(dev->io.base_addr)); /* reset FIFOs too */
+ udelay(3);
+ outb(0, DSP_RESET(dev->io.base_addr));
+ for (i = 0; i < 0xffff; i++)
+ if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80)
+ if (inb(DSP_READ_DATA(dev->io.base_addr)) == 0xaa) {
+ write_dsp(dev, ESS_EXTENDED_MODE);
+ return 1;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int config_resources(struct hfmodem_state *dev)
+{
+ unsigned char irqreg = 0, dmareg = 0, realirq, realdma;
+ unsigned long flags;
+
+ switch (dev->io.irq) {
+ case 2:
+ case 9:
+ irqreg |= 0x01;
+ break;
+
+ case 5:
+ irqreg |= 0x02;
+ break;
+
+ case 7:
+ irqreg |= 0x04;
+ break;
+
+ case 10:
+ irqreg |= 0x08;
+ break;
+
+ default:
+ return -ENODEV;
+ }
+
+ switch (dev->io.dma) {
+ case 0:
+ dmareg |= 0x01;
+ break;
+
+ case 1:
+ dmareg |= 0x02;
+ break;
+
+ case 3:
+ dmareg |= 0x08;
+ break;
+
+ case 5:
+ dmareg |= 0x20;
+ break;
+
+ case 6:
+ dmareg |= 0x40;
+ break;
+
+ case 7:
+ dmareg |= 0x80;
+ break;
+
+ default:
+ return -ENODEV;
+ }
+ save_flags(flags);
+ cli();
+ outb(0x80, DSP_MIXER_ADDR(dev->io.base_addr));
+ outb(irqreg, DSP_MIXER_DATA(dev->io.base_addr));
+ realirq = inb(DSP_MIXER_DATA(dev->io.base_addr));
+ outb(0x81, DSP_MIXER_ADDR(dev->io.base_addr));
+ outb(dmareg, DSP_MIXER_DATA(dev->io.base_addr));
+ realdma = inb(DSP_MIXER_DATA(dev->io.base_addr));
+ restore_flags(flags);
+ if ((~realirq) & irqreg || (~realdma) & dmareg) {
+ printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device "
+ "and IRQ/DMA specified wrongly?\n", hfmodem_drvname);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void sbc_int_ack_8bit(struct hfmodem_state *dev)
+{
+ inb(DSP_DATA_AVAIL(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void sbc_int_ack_16bit(struct hfmodem_state *dev)
+{
+ inb(DSP_INTACK_16BIT(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void set_mixer(struct hfmodem_state *dev, unsigned char reg, unsigned char data)
+{
+ outb(reg, DSP_MIXER_ADDR(dev->io.base_addr));
+ outb(data, DSP_MIXER_DATA(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+int hfmodem_sbcprobe(struct hfmodem_state *dev)
+{
+ unsigned char revhi, revlo, essrevhi, essrevlo, tmp;
+ int ret;
+
+ if (dev->io.base_addr <= 0 || dev->io.base_addr > 0x1000-SBC_EXTENT ||
+ dev->io.irq < 2 || dev->io.irq > 15 || dev->io.dma > 7 || dev->io.dma == 2)
+ return -ENXIO;
+ if (check_region(dev->io.base_addr, SBC_EXTENT))
+ return -EACCES;
+ /*
+ * check if a card is available
+ */
+ if (!reset_dsp(dev)) {
+ printk(KERN_ERR "%s: sbc: no card at io address 0x%x\n",
+ hfmodem_drvname, dev->io.base_addr);
+ return -ENODEV;
+ }
+ set_mixer(dev, 0, 0); /* reset mixer */
+ write_dsp(dev, SBC_GET_REVISION);
+ if (!read_dsp(dev, &revhi) || !read_dsp(dev, &revlo))
+ return -ENODEV;
+ printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%02d\n", hfmodem_drvname, revhi, revlo);
+ if (revhi == 3 && revlo == 1) {
+ write_dsp(dev, ESS_GET_REVISION);
+ if (!read_dsp(dev, &essrevhi) || !read_dsp(dev, &essrevlo))
+ return -ENODEV;
+ if (essrevhi == 0x48 && (essrevlo & 0xf0) == 0x80) {
+ printk(KERN_INFO "%s: ESS ES488 AudioDrive (rev %d): unsupported.\n",
+ hfmodem_drvname, essrevlo & 0x0f);
+ return -ENODEV;
+ }
+ if (essrevhi == 0x68 && (essrevlo & 0xf0) == 0x80) {
+ printk(KERN_INFO "%s: ESS ES%s688 AudioDrive (rev %d)\n",
+ hfmodem_drvname, ((essrevlo & 0x0f) >= 8) ? "1" : "", essrevlo & 0x0f);
+ if (dev->io.dma > 3) {
+ printk(KERN_INFO "%s: DMA number out of range\n", hfmodem_drvname);
+ return -ENXIO;
+ }
+ printk(KERN_INFO "%s: ess: irq: ", hfmodem_drvname);
+ read_ess(dev, 0xb1, &tmp);
+ switch (tmp & 0xf) {
+ case 0:
+ printk("2, 9, \"all others\"");
+ break;
+
+ case 5:
+ printk("5");
+ break;
+
+ case 10:
+ printk("7");
+ break;
+
+ case 15:
+ printk("10");
+ break;
+
+ default:
+ printk("unknown (%d)", tmp & 0xf);
+ break;
+ }
+ printk(" dma: ");
+ read_ess(dev, 0xb2, &tmp);
+ switch (tmp & 0xf) {
+ case 0:
+ printk("\"all others\"");
+ break;
+
+ case 5:
+ printk("0");
+ break;
+
+ case 10:
+ printk("1");
+ break;
+
+ case 15:
+ printk("3");
+ break;
+
+ default:
+ printk("unknown (%d)", tmp & 0xf);
+ break;
+ }
+ printk("\n");
+ dev->scops = &ess_scops;
+ return 0;
+ }
+ }
+ if (revhi < 4) {
+ printk(KERN_INFO "%s: at least SB16 required\n", hfmodem_drvname);
+ return -ENODEV;
+ }
+ if (dev->io.dma < 4) {
+ printk(KERN_INFO "%s: DMA number out of range\n", hfmodem_drvname);
+ return -ENXIO;
+ }
+ if ((ret = config_resources(dev)))
+ return ret;
+ dev->scops = &sbc4_scops;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_init(struct hfmodem_state *dev)
+{
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_prepare_input(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ if (!reset_dsp(dev)) {
+ printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", hfmodem_drvname);
+ return;
+ }
+ save_flags(flags);
+ cli();
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ sbc_int_ack_16bit(dev);
+ write_dsp(dev, SBC_SAMPLE_RATE_IN); /* set sampling rate */
+ write_dsp(dev, HFMODEM_SRATE >> 8);
+ write_dsp(dev, HFMODEM_SRATE & 0xff);
+ write_dsp(dev, SBC_SPEAKER_OFF);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_trigger_input(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ write_dsp(dev, SBC4_IN16_AI_NO_FIFO);
+ write_dsp(dev, SBC4_MODE_UNS_MONO);
+ write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) & 0xff);
+ write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) >> 8);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_prepare_output(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ if (!reset_dsp(dev)) {
+ printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", hfmodem_drvname);
+ return;
+ }
+ save_flags(flags);
+ cli();
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ sbc_int_ack_16bit(dev);
+ write_dsp(dev, SBC_SAMPLE_RATE_OUT); /* set sampling rate */
+ write_dsp(dev, HFMODEM_SRATE >> 8);
+ write_dsp(dev, HFMODEM_SRATE & 0xff);
+ write_dsp(dev, SBC_SPEAKER_ON);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_trigger_output(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ write_dsp(dev, SBC4_OUT16_AI_NO_FIFO);
+ write_dsp(dev, SBC4_MODE_UNS_MONO);
+ write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) & 0xff);
+ write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) >> 8);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_stop(struct hfmodem_state *dev)
+{
+ reset_dsp(dev);
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned int sbc4_intack(struct hfmodem_state *dev)
+{
+ unsigned int dmaptr;
+ unsigned long flags;
+ unsigned char intsrc;
+
+ save_flags(flags);
+ cli();
+ outb(0x82, DSP_MIXER_ADDR(dev->io.base_addr));
+ intsrc = inb(DSP_MIXER_DATA(dev->io.base_addr));
+ if (intsrc & 0x01)
+ sbc_int_ack_8bit(dev);
+ if (intsrc & 0x02)
+ sbc_int_ack_16bit(dev);
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ dmaptr = get_dma_residue(dev->io.dma);
+ enable_dma(dev->io.dma);
+ restore_flags(flags);
+ if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE)
+ dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE;
+ return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_mixer(struct hfmodem_state *dev, int src, int igain, int ogain)
+{
+ unsigned long flags;
+ static const unsigned char srcbits[3] = { 0x18, 0x01, 0x06 };
+
+ save_flags(flags);
+ cli();
+ if (src >= 0 && src <= 2) {
+ set_mixer(dev, 0x3d, srcbits[src]);
+ set_mixer(dev, 0x3e, srcbits[src]);
+ }
+ if (ogain >= 0 && ogain <= 255) {
+ set_mixer(dev, 0x30, ogain);
+ set_mixer(dev, 0x31, ogain);
+ }
+ if (igain >= 0 && igain <= 255) {
+ set_mixer(dev, 0x36, igain);
+ set_mixer(dev, 0x37, igain);
+ set_mixer(dev, 0x38, igain);
+ set_mixer(dev, 0x39, igain);
+ set_mixer(dev, 0x3a, igain);
+ }
+ set_mixer(dev, 0x32, 0xff);
+ set_mixer(dev, 0x33, 0xff);
+ set_mixer(dev, 0x34, 0);
+ set_mixer(dev, 0x35, 0);
+ set_mixer(dev, 0x3b, 0); /* pc spkr vol */
+ set_mixer(dev, 0x3c, 0); /* output src */
+ set_mixer(dev, 0x3f, 0); /* inp gain */
+ set_mixer(dev, 0x40, 0);
+ set_mixer(dev, 0x41, 0); /* outp gain */
+ set_mixer(dev, 0x42, 0);
+ set_mixer(dev, 0x43, 1); /* mic agc off */
+ set_mixer(dev, 0x44, 8<<4); /* treble */
+ set_mixer(dev, 0x45, 8<<4);
+ set_mixer(dev, 0x46, 8<<4); /* bass */
+ set_mixer(dev, 0x47, 8<<4);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ess_prepare_input(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+ unsigned char tmp;
+
+ if (!reset_ess(dev)) {
+ printk(KERN_ERR "%s: sbc: cannot reset ess dsp\n", hfmodem_drvname);
+ return;
+ }
+ save_flags(flags);
+ cli();
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ sbc_int_ack_8bit(dev);
+ write_ess(dev, 0xa1, 128 - (397700 + HFMODEM_SRATE/2) / HFMODEM_SRATE);
+ /*
+ * Set filter divider register
+ * Rolloff at 90% of the half sampling rate
+ */
+ write_ess(dev, 0xa2, 256-(7160000 / (82 * (HFMODEM_SRATE * 9 / 20))));
+ write_dsp(dev, SBC_SPEAKER_OFF);
+ write_ess(dev, 0xb8, 0x0e); /* Auto init DMA mode */
+ read_ess(dev, 0xa8, &tmp);
+ write_ess(dev, 0xa8, (tmp & ~0x03) | 2); /* Mono */
+ write_ess(dev, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */
+ /* 16 bit mono */
+ write_ess(dev, 0xb7, 0x71);
+ write_ess(dev, 0xb7, 0xf4);
+
+ read_ess(dev, 0xb1, &tmp);
+ write_ess(dev, 0xb1, (tmp & 0x0f) | 0x50);
+ read_ess(dev, 0xb2, &tmp);
+ write_ess(dev, 0xb2, (tmp & 0x0f) | 0x50);
+
+ write_ess(dev, 0xa4, (unsigned char) ((-HFMODEM_FRAGSIZE) & 0xff));
+ write_ess(dev, 0xa5, (unsigned char) (((-HFMODEM_FRAGSIZE) >> 8) & 0xff));
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ess_trigger_input(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+ unsigned char tmp;
+
+ save_flags(flags);
+ cli();
+ read_ess(dev, 0xb8, &tmp);
+ write_ess(dev, 0xb8, tmp | 0x0f); /* Go */
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+void ess_prepare_output(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+ unsigned char tmp;
+
+ if (!reset_ess(dev)) {
+ printk(KERN_ERR "%s: sbc: cannot reset ess dsp\n", hfmodem_drvname);
+ return;
+ }
+ save_flags(flags);
+ cli();
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ sbc_int_ack_8bit(dev);
+ write_ess(dev, 0xa1, 128 - (397700 + HFMODEM_SRATE/2) / HFMODEM_SRATE);
+ /*
+ * Set filter divider register
+ * Rolloff at 90% of the half sampling rate
+ */
+ write_ess(dev, 0xa2, 256-(7160000 / (82 * (HFMODEM_SRATE * 9 / 20))));
+ write_ess(dev, 0xb8, 0x04); /* Auto init DMA mode */
+ read_ess(dev, 0xa8, &tmp);
+ write_ess(dev, 0xa8, (tmp & ~0x03) | 2); /* Mono */
+ write_ess(dev, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */
+ /* 16 bit mono */
+ write_ess(dev, 0xb6, 0x00);
+ write_ess(dev, 0xb7, 0x71);
+ write_ess(dev, 0xb7, 0xf4);
+
+ read_ess(dev, 0xb1, &tmp);
+ write_ess(dev, 0xb1, (tmp & 0x0f) | 0x50);
+ read_ess(dev, 0xb2, &tmp);
+ write_ess(dev, 0xb2, (tmp & 0x0f) | 0x50);
+
+ write_ess(dev, 0xa4, (unsigned char) ((-HFMODEM_FRAGSIZE) & 0xff));
+ write_ess(dev, 0xa5, (unsigned char) (((-HFMODEM_FRAGSIZE) >> 8) & 0xff));
+
+ write_dsp(dev, SBC_SPEAKER_ON);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+void ess_trigger_output(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+ unsigned char tmp;
+
+ save_flags(flags);
+ cli();
+ read_ess(dev, 0xb8, &tmp);
+ write_ess(dev, 0xb8, tmp | 0x05); /* Go */
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+unsigned int ess_intack(struct hfmodem_state *dev)
+{
+ unsigned int dmaptr;
+ unsigned long flags;
+ unsigned char st;
+#if 0
+ static unsigned int cnt = 0;
+#endif
+
+ save_flags(flags);
+ cli();
+ st = inb(DSP_WRITE_STATUS(dev->io.base_addr));
+ sbc_int_ack_8bit(dev);
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ dmaptr = get_dma_residue(dev->io.dma);
+ enable_dma(dev->io.dma);
+ restore_flags(flags);
+#if 0
+ cnt = (cnt + 1) & 0x3f;
+ if (!cnt)
+ printk(KERN_DEBUG "%s: ess: FIFO: full:%c empty:%c half empty:%c IRQ: cpu:%c half empty:%c DMA:%c\n",
+ hfmodem_drvname, '1'-!(st&0x20), '1'-!(st&0x10), '1'-!(st&0x8),
+ '1'-!(st&0x4), '1'-!(st&0x2), '1'-!(st&0x1));
+#endif
+ if (st & 0x20) /* FIFO full, 256 bytes */
+ dmaptr += 256;
+ else if (!(st & 0x10)) /* FIFO not empty, assume half full 128 bytes */
+ dmaptr += 128;
+ if (dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE)
+ dmaptr -= HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE;
+ if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE)
+ dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE;
+ return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ess_mixer(struct hfmodem_state *dev, int src, int igain, int ogain)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (src >= 0 && src <= 2)
+ set_mixer(dev, 0x0c, ((src+3) & 3) << 1);
+ if (ogain >= 0 && ogain <= 255)
+ set_mixer(dev, 0x22, (ogain & 0xf0) | ((ogain >> 4) & 0xf));
+ if (igain >= 0 && igain <= 255) {
+ set_mixer(dev, 0x36, igain);
+ set_mixer(dev, 0x37, igain);
+ set_mixer(dev, 0x38, igain);
+ set_mixer(dev, 0x39, igain);
+ set_mixer(dev, 0x3a, igain);
+ }
+ set_mixer(dev, 0x4, 0xff);
+ set_mixer(dev, 0xe, 0x0);
+ set_mixer(dev, 0x26, 0);
+ set_mixer(dev, 0x28, 0);
+ set_mixer(dev, 0x2e, 0);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const struct hfmodem_scops sbc4_scops = {
+ SBC_EXTENT, sbc4_init, sbc4_prepare_input, sbc4_trigger_input,
+ sbc4_prepare_output, sbc4_trigger_output, sbc4_stop, sbc4_intack, sbc4_mixer
+};
+
+static const struct hfmodem_scops ess_scops = {
+ SBC_EXTENT, sbc4_init, ess_prepare_input, ess_trigger_input,
+ ess_prepare_output, ess_trigger_output, sbc4_stop, ess_intack, ess_mixer
+};
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/char/hfmodem/wss.c b/drivers/char/hfmodem/wss.c
new file mode 100644
index 000000000..c54aeadee
--- /dev/null
+++ b/drivers/char/hfmodem/wss.c
@@ -0,0 +1,437 @@
+/*****************************************************************************/
+
+/*
+ * wss.c -- Linux soundcard HF FSK driver,
+ * WindowsSoundSystem specific functions.
+ *
+ * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/hfmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+#define WSS_CONFIG(iobase) (iobase+0)
+#define WSS_STATUS(iobase) (iobase+3)
+#define WSS_CODEC_IA(iobase) (iobase+4)
+#define WSS_CODEC_ID(iobase) (iobase+5)
+#define WSS_CODEC_STATUS(iobase) (iobase+6)
+#define WSS_CODEC_DATA(iobase) (iobase+7)
+
+#define WSS_EXTENT 8
+
+/* --------------------------------------------------------------------- */
+
+extern const struct hfmodem_scops wss_scops;
+
+/* --------------------------------------------------------------------- */
+
+static void write_codec(struct hfmodem_state *dev, unsigned char idx,
+ unsigned char data)
+{
+ int timeout = 900000;
+
+ /* wait until codec ready */
+ while (timeout > 0 && inb(WSS_CODEC_IA(dev->io.base_addr)) & 0x80)
+ timeout--;
+ outb(idx, WSS_CODEC_IA(dev->io.base_addr));
+ outb(data, WSS_CODEC_ID(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned char read_codec(struct hfmodem_state *dev, unsigned char idx)
+{
+ int timeout = 900000;
+
+ /* wait until codec ready */
+ while (timeout > 0 && inb(WSS_CODEC_IA(dev->io.base_addr)) & 0x80)
+ timeout--;
+ outb(idx & 0x1f, WSS_CODEC_IA(dev->io.base_addr));
+ return inb(WSS_CODEC_ID(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void wss_ack_int(struct hfmodem_state *dev)
+{
+ outb(0, WSS_CODEC_STATUS(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_srate_tab[16] = {
+ 8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050,
+ -1, 37800, -1, 44100, 48000, 33075, 9600, 6620
+};
+
+static int wss_srate_index(int srate)
+{
+ int i;
+
+ for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++)
+ if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0)
+ return i;
+ return -1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_set_codec_fmt(struct hfmodem_state *dev, unsigned char fmt)
+{
+ unsigned long time;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ /* Clock and data format register */
+ write_codec(dev, 0x48, fmt);
+ /* MCE and interface config reg */
+ write_codec(dev, 0x49, 0xc);
+ outb(0xb, WSS_CODEC_IA(dev->io.base_addr)); /* leave MCE */
+ /*
+ * wait for ACI start
+ */
+ time = 1000;
+ while (!(read_codec(dev, 0x0b) & 0x20))
+ if (!(--time)) {
+ printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n",
+ hfmodem_drvname);
+ restore_flags(flags);
+ return -1;
+ }
+ /*
+ * wait for ACI end
+ */
+ sti();
+ time = jiffies + HZ/4;
+ while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0));
+ restore_flags(flags);
+ if ((signed)(jiffies - time) >= 0) {
+ printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n",
+ hfmodem_drvname);
+ return -1;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_init_codec(struct hfmodem_state *dev)
+{
+ unsigned char tmp, revwss, revid;
+ static const signed char irqtab[16] = {
+ -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1, -1, -1
+ };
+ static const signed char dmatab[4] = { 1, 2, -1, 3 };
+ int fmt;
+
+ if ((fmt = wss_srate_index(HFMODEM_SRATE)) < 0) {
+ printk(KERN_ERR "%s: WSS: sampling rate not supported\n", hfmodem_drvname);
+ return -1;
+ }
+ fmt &= 0x0f;
+#ifdef __BIG_ENDIAN
+ fmt |= 0xc0;
+#else /* __BIG_ENDIAN */
+ fmt |= 0x40;
+#endif /* __BIG_ENDIAN */
+ tmp = inb(WSS_STATUS(dev->io.base_addr));
+ if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 &&
+ (tmp & 0x3f) != 0x0f) {
+ printk(KERN_WARNING "%s: WSS card id register not found, "
+ "address 0x%x, ID register 0x%02x\n", hfmodem_drvname,
+ dev->io.base_addr, (int)tmp);
+ /* return -1; */
+ revwss = 0;
+ } else {
+ if ((tmp & 0x80) && ((dev->io.dma == 0) || ((dev->io.irq >= 8) && (dev->io.irq != 9)))) {
+ printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 "
+ "(except IRQ9) cannot be used on an 8bit "
+ "card\n", hfmodem_drvname);
+ return -1;
+ }
+ if (dev->io.irq > 15 || irqtab[dev->io.irq] == -1) {
+ printk(KERN_ERR "%s: WSS: invalid interrupt %d\n",
+ hfmodem_drvname, (int)dev->io.irq);
+ return -1;
+ }
+ if (dev->io.dma > 3 || dmatab[dev->io.dma] == -1) {
+ printk(KERN_ERR "%s: WSS: invalid dma channel %d\n",
+ hfmodem_drvname, (int)dev->io.dma);
+ return -1;
+ }
+ tmp = irqtab[dev->io.irq] | dmatab[dev->io.dma];
+ /* irq probe */
+ outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->io.base_addr));
+ if (!(inb(WSS_STATUS(dev->io.base_addr)) & 0x40)) {
+ outb(0, WSS_CONFIG(dev->io.base_addr));
+ printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n",
+ hfmodem_drvname, dev->io.irq);
+ }
+ outb(tmp, WSS_CONFIG(dev->io.base_addr));
+ revwss = inb(WSS_STATUS(dev->io.base_addr)) & 0x3f;
+ }
+ /*
+ * initialize the codec
+ */
+ write_codec(dev, 9, 0);
+ write_codec(dev, 12, 0);
+ write_codec(dev, 0, 0x45);
+ if (read_codec(dev, 0) != 0x45)
+ goto codec_err;
+ write_codec(dev, 0, 0xaa);
+ if (read_codec(dev, 0) != 0xaa)
+ goto codec_err;
+ if (wss_set_codec_fmt(dev, fmt))
+ goto codec_err;
+ write_codec(dev, 0, 0x40); /* left input control */
+ write_codec(dev, 1, 0x40); /* right input control */
+ write_codec(dev, 2, 0x80); /* left aux#1 input control */
+ write_codec(dev, 3, 0x80); /* right aux#1 input control */
+ write_codec(dev, 4, 0x80); /* left aux#2 input control */
+ write_codec(dev, 5, 0x80); /* right aux#2 input control */
+ write_codec(dev, 6, 0x80); /* left dac control */
+ write_codec(dev, 7, 0x80); /* right dac control */
+ write_codec(dev, 0xa, 0x2); /* pin control register */
+ write_codec(dev, 0xd, 0x0); /* digital mix control */
+ revid = read_codec(dev, 0xc) & 0xf;
+ /*
+ * print revisions
+ */
+ printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n",
+ hfmodem_drvname, (int)revwss, (int)revid);
+ return 0;
+ codec_err:
+ outb(0, WSS_CONFIG(dev->io.base_addr));
+ printk(KERN_ERR "%s: no WSS soundcard found at address 0x%x\n",
+ hfmodem_drvname, dev->io.base_addr);
+ return -1;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hfmodem_wssprobe(struct hfmodem_state *dev)
+{
+ if (dev->io.base_addr <= 0 || dev->io.base_addr > 0x1000-WSS_EXTENT ||
+ dev->io.irq < 2 || dev->io.irq > 15 || dev->io.dma > 3 || dev->io.dma == 2)
+ return -ENXIO;
+ if (check_region(dev->io.base_addr, WSS_EXTENT))
+ return -EACCES;
+ /*
+ * check if a card is available
+ */
+ if (wss_init_codec(dev)) {
+ printk(KERN_ERR "%s: sbc: no card at io address 0x%x\n",
+ hfmodem_drvname, dev->io.base_addr);
+ return -ENODEV;
+ }
+ dev->scops = &wss_scops;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_init(struct hfmodem_state *dev)
+{
+ wss_init_codec(dev);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_stop(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+ unsigned char oldcodecmode;
+ long abrt;
+
+ save_flags(flags);
+ cli();
+ /*
+ * perform the final DMA sequence to disable the codec request
+ */
+ oldcodecmode = read_codec(dev, 9);
+ write_codec(dev, 9, 0xc); /* disable codec */
+ wss_ack_int(dev);
+ if (read_codec(dev, 11) & 0x10) {
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, (oldcodecmode & 1) ?
+ (DMA_MODE_WRITE | DMA_MODE_AUTOINIT) : (DMA_MODE_READ | DMA_MODE_AUTOINIT));
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ abrt = 0;
+ while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000));
+ }
+ disable_dma(dev->io.dma);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_prepare_input(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ wss_stop(dev);
+ save_flags(flags);
+ cli();
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ write_codec(dev, 15, (HFMODEM_FRAGSAMPLES-1) & 0xff);
+ write_codec(dev, 14, (HFMODEM_FRAGSAMPLES-1) >> 8);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_trigger_input(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ write_codec(dev, 9, 0x0e);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_prepare_output(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ wss_stop(dev);
+ save_flags(flags);
+ cli();
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+ set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+ enable_dma(dev->io.dma);
+ write_codec(dev, 15, (HFMODEM_FRAGSAMPLES-1) & 0xff);
+ write_codec(dev, 14, (HFMODEM_FRAGSAMPLES-1) >> 8);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_trigger_output(struct hfmodem_state *dev)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ write_codec(dev, 9, 0x0d);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned int wss_intack(struct hfmodem_state *dev)
+{
+ unsigned int dmaptr, nums;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ wss_ack_int(dev);
+ disable_dma(dev->io.dma);
+ clear_dma_ff(dev->io.dma);
+ dmaptr = get_dma_residue(dev->io.dma);
+ if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE)
+ dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE;
+ nums = (((dmaptr - 1) % HFMODEM_FRAGSIZE) - 1) / 2;
+ write_codec(dev, 15, nums & 0xff);
+ write_codec(dev, 14, nums >> 8);
+ enable_dma(dev->io.dma);
+ restore_flags(flags);
+ return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_mixer(struct hfmodem_state *dev, int src, int igain, int ogain)
+{
+ unsigned long flags;
+ static const unsigned char srctoreg[3] = { 1, 2, 0 };
+ static const unsigned char regtosrc[4] = { 2, 0, 1, 0 };
+ unsigned char tmp;
+
+ save_flags(flags);
+ cli();
+ tmp = read_codec(dev, 0x00);
+ if (src < 0 || src > 2)
+ src = regtosrc[(tmp >> 6) & 3];
+ if (igain < 0 || igain > 255) {
+ if (src == 1)
+ igain = ((tmp & 0xf) + ((tmp & 0x20) ? 13 : 0)) << 3;
+ else
+ igain = (tmp & 0xf) << 4;
+ }
+ if (src == 1) {
+ if (igain > (28<<3))
+ tmp = 0x2f;
+ else if (igain >= (13<<3))
+ tmp = 0x20 + (((igain >> 3) - 13) & 0xf);
+ else
+ tmp = (igain >> 3) & 0xf;
+ } else
+ tmp = (igain >> 4) & 0xf;
+ tmp |= srctoreg[src] << 6;
+ write_codec(dev, 0, tmp);
+ write_codec(dev, 1, tmp);
+ if (ogain > 0 && ogain <= 255) {
+ tmp = 63 - (ogain >> 2);
+ write_codec(dev, 6, tmp);
+ write_codec(dev, 7, tmp);
+ } else if (ogain == 0) {
+ write_codec(dev, 6, 0x80);
+ write_codec(dev, 7, 0x80);
+ }
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const struct hfmodem_scops wss_scops = {
+ WSS_EXTENT, wss_init, wss_prepare_input, wss_trigger_input,
+ wss_prepare_output, wss_trigger_output, wss_stop, wss_intack, wss_mixer
+};
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index 59bb0a0a2..770607df4 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -36,11 +36,11 @@
/* if you have more than 3 printers, remember to increase LP_NO */
struct lp_struct lp_table[] =
{
- {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
+ {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, 0,
{0}},
- {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
+ {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, 0,
{0}},
- {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
+ {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, 0,
{0}}
};
#define LP_NO 3
@@ -60,6 +60,41 @@ static char *dev_name = "lp";
#undef LP_DEBUG
#undef LP_READ_DEBUG
+static inline void lp_parport_release (int minor)
+{
+ parport_release (lp_table[minor].dev);
+ lp_table[minor].should_relinquish = 0;
+}
+
+static inline void lp_parport_claim (int minor)
+{
+ if (parport_claim (lp_table[minor].dev))
+ sleep_on (&lp_table[minor].lp_wait_q);
+}
+
+static inline void lp_schedule (int minor)
+{
+ if (lp_table[minor].should_relinquish) {
+ lp_parport_release (minor);
+ schedule ();
+ lp_parport_claim (minor);
+ }
+ else
+ schedule ();
+}
+
+
+static int lp_preempt (void *handle)
+{
+ struct lp_struct *lps = (struct lp_struct *)handle;
+
+ /* Just remember that someone wants the port */
+ lps->should_relinquish = 1;
+
+ /* Don't actually release the port now */
+ return 1;
+}
+
static int lp_reset(int minor)
{
w_ctr(minor, LP_PSELECP);
@@ -70,7 +105,8 @@ static int lp_reset(int minor)
static inline int lp_char_polled(char lpchar, int minor)
{
- int status, wait = 0;
+ int status;
+ unsigned int wait = 0;
unsigned long count = 0;
struct lp_stats *stats;
@@ -78,7 +114,7 @@ static inline int lp_char_polled(char lpchar, int minor)
status = r_str(minor);
count++;
if (need_resched)
- schedule();
+ lp_schedule (minor);
} while (!LP_READY(minor, status) && count < LP_CHAR(minor));
if (count == LP_CHAR(minor)) {
@@ -90,11 +126,11 @@ static inline int lp_char_polled(char lpchar, int minor)
stats->chars++;
/* must wait before taking strobe high, and after taking strobe
low, according spec. Some printers need it, others don't. */
- while (wait != LP_WAIT(minor))
+ while (wait != LP_WAIT(minor)) /* FIXME: should be a udelay() */
wait++;
/* control port takes strobe high */
w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
- while (wait)
+ while (wait) /* FIXME: should be a udelay() */
wait--;
/* take strobe low */
w_ctr(minor, LP_PSELECP | LP_PINITP);
@@ -116,14 +152,14 @@ static inline int lp_char_polled(char lpchar, int minor)
static inline int lp_char_interrupt(char lpchar, int minor)
{
- int wait;
+ unsigned int wait;
unsigned long count = 0;
unsigned char status;
struct lp_stats *stats;
do {
if(need_resched)
- schedule();
+ lp_schedule (minor);
if ((status = r_str(minor)) & LP_PBUSY) {
if (!LP_CAREFUL_READY(minor, status))
return 0;
@@ -133,12 +169,12 @@ static inline int lp_char_interrupt(char lpchar, int minor)
/* must wait before taking strobe high, and after taking strobe
low, according spec. Some printers need it, others don't. */
wait = 0;
- while (wait != LP_WAIT(minor))
- wait++;
+ while (wait != LP_WAIT(minor)) /* FIXME: should be */
+ wait++; /* a udelay () */
/* control port takes strobe high */
w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
while (wait)
- wait--;
+ wait--; /* FIXME: should be a udelay() */
/* take strobe low */
w_ctr(minor, LP_PSELECP | LP_PINITP);
/* update waittime statistics */
@@ -164,7 +200,7 @@ static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
struct pardevice *pd = pb->cad;
struct lp_struct *lp_dev = (struct lp_struct *) pd->private;
- if (lp_dev->lp_wait_q)
+ if (waitqueue_active (&lp_dev->lp_wait_q))
wake_up(&lp_dev->lp_wait_q);
}
@@ -211,7 +247,8 @@ static inline int lp_write_interrupt(unsigned int minor, const char *buf, int co
}
LP_STAT(minor).sleeps++;
cli();
- w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN);
+ enable_irq(lp->dev->port->irq);
+ w_ctr(minor, LP_PSELECP|LP_PINITP|LP_PINTEN);
status = r_str(minor);
if ((!(status & LP_PACK) || (status & LP_PBUSY))
&& LP_CAREFUL_READY(minor, status)) {
@@ -222,6 +259,7 @@ static inline int lp_write_interrupt(unsigned int minor, const char *buf, int co
lp_table[minor].runchars = 0;
current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
interruptible_sleep_on(&lp->lp_wait_q);
+
w_ctr(minor, LP_PSELECP | LP_PINITP);
sti();
if (current->signal & ~current->blocked) {
@@ -267,7 +305,7 @@ static inline int lp_write_polled(unsigned int minor, const char *buf, int count
return temp-buf?temp-buf:-ENOSPC;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
- schedule();
+ lp_schedule (minor);
} else
if (!(status & LP_PSELECD)) {
printk(KERN_INFO "lp%d off-line\n", minor);
@@ -275,7 +313,7 @@ static inline int lp_write_polled(unsigned int minor, const char *buf, int count
return temp-buf?temp-buf:-EIO;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
- schedule();
+ lp_schedule (minor);
} else
/* not offline or out of paper. on fire? */
if (!(status & LP_PERRORP)) {
@@ -284,7 +322,7 @@ static inline int lp_write_polled(unsigned int minor, const char *buf, int count
return temp-buf?temp-buf:-EIO;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
- schedule();
+ lp_schedule (minor);
}
/* check for signals before going to sleep */
@@ -302,7 +340,7 @@ static inline int lp_write_polled(unsigned int minor, const char *buf, int count
lp_table[minor].runchars=0;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIME(minor);
- schedule();
+ lp_schedule (minor);
}
}
return temp-buf;
@@ -322,16 +360,14 @@ static long lp_write(struct inode * inode, struct file * file,
/* Claim Parport or sleep until it becomes available
* (see lp_wakeup() for details)
*/
- if (parport_claim(lp_table[minor].dev)) {
- sleep_on(&lp_table[minor].lp_wait_q);
- lp_table[minor].lp_wait_q = NULL;
- }
+ lp_parport_claim (minor);
+
if (LP_IRQ(minor) > 0)
retv = lp_write_interrupt(minor, buf, count);
else
retv = lp_write_polled(minor, buf, count);
- parport_release(lp_table[minor].dev);
+ lp_parport_release (minor);
return retv;
}
@@ -370,10 +406,7 @@ static long lp_read(struct inode * inode, struct file * file,
/* Claim Parport or sleep until it becomes available
* (see lp_wakeup() for details)
*/
- if (parport_claim(lp_table[minor].dev)) {
- sleep_on(&lp_table[minor].lp_wait_q);
- lp_table[minor].lp_wait_q = NULL;
- }
+ lp_parport_claim (minor);
temp=buf;
#ifdef LP_READ_DEBUG
@@ -399,7 +432,7 @@ static long lp_read(struct inode * inode, struct file * file,
udelay(50);
counter++;
if (need_resched)
- schedule();
+ lp_schedule (minor);
} while ( (status == 0x40) && (counter < 20) );
if ( counter == 20 ) { /* Timeout */
#ifdef LP_READ_DEBUG
@@ -418,7 +451,7 @@ static long lp_read(struct inode * inode, struct file * file,
udelay(20);
counter++;
if (need_resched)
- schedule();
+ lp_schedule (minor);
} while ( (status == 0) && (counter < 20) );
if (counter == 20) { /* Timeout */
#ifdef LP_READ_DEBUG
@@ -434,7 +467,7 @@ static long lp_read(struct inode * inode, struct file * file,
}
current->state=TASK_INTERRUPTIBLE;
current->timeout=jiffies + LP_TIME(minor);
- schedule();
+ lp_schedule (minor);
}
counter=0;
if (( i & 1) != 0) {
@@ -656,7 +689,7 @@ void lp_wakeup(void *ref)
{
struct lp_struct *lp_dev = (struct lp_struct *) ref;
- if (!lp_dev->lp_wait_q)
+ if (!waitqueue_active (&lp_dev->lp_wait_q))
return; /* Wake up whom? */
/* Claim the Parport */
@@ -691,8 +724,8 @@ int lp_init(void)
(parport[0] == -3 &&
pb->probe_info.class == PARPORT_CLASS_PRINTER)) {
lp_table[count].dev =
- parport_register_device(pb, dev_name, NULL,
- lp_wakeup,
+ parport_register_device(pb, dev_name,
+ lp_preempt, lp_wakeup,
lp_interrupt, PARPORT_DEV_TRAN,
(void *) &lp_table[count]);
lp_table[count].flags |= LP_EXIST;
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 6e9eb5766..78df2e3be 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -339,13 +339,6 @@ static int mmap_zero(struct inode * inode, struct file * file, struct vm_area_st
return 0;
}
-static long read_full(struct inode * node, struct file * file,
- char * buf, unsigned long count)
-{
- file->f_pos += count;
- return count;
-}
-
static long write_full(struct inode * inode, struct file * file,
const char * buf, unsigned long count)
{
@@ -390,7 +383,9 @@ static long long memory_lseek(struct inode * inode, struct file * file,
#define mmap_kmem mmap_mem
#define zero_lseek null_lseek
+#define full_lseek null_lseek
#define write_zero write_null
+#define read_full read_zero
static struct file_operations mem_fops = {
memory_lseek,
@@ -457,7 +452,7 @@ static struct file_operations zero_fops = {
};
static struct file_operations full_fops = {
- memory_lseek,
+ full_lseek,
read_full,
write_full,
NULL, /* full_readdir */
@@ -530,7 +525,8 @@ __initfunc(int chr_dev_init(void))
defined (CONFIG_ATIXL_BUSMOUSE) || defined(CONFIG_SOFT_WATCHDOG) || \
defined (CONFIG_AMIGAMOUSE) || defined (CONFIG_ATARIMOUSE) || \
defined (CONFIG_PCWATCHDOG) || \
- defined (CONFIG_APM) || defined (CONFIG_RTC) || defined (CONFIG_SUN_MOUSE)
+ defined (CONFIG_APM) || defined (CONFIG_RTC) || \
+ defined (CONFIG_SUN_MOUSE) || defined (CONFIG_NVRAM)
misc_init();
#endif
#ifdef CONFIG_SOUND
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index 4495f881d..826863ec0 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -73,10 +73,12 @@ extern void gfx_register(void);
extern void streamable_init(void);
extern void watchdog_init(void);
extern void wdt_init(void);
+extern void acq_init(void);
extern void pcwatchdog_init(void);
extern int rtc_init(void);
extern int dsp56k_init(void);
extern int nvram_init(void);
+extern void hfmodem_init(void);
#ifdef CONFIG_PROC_FS
static int misc_read_proc(char *buf, char **start, off_t offset,
@@ -233,21 +235,33 @@ __initfunc(int misc_init(void))
#ifdef CONFIG_WDT
wdt_init();
#endif
+#ifdef CONFIG_ACQUIRE_WDT
+ acq_init();
+#endif
#ifdef CONFIG_SOFT_WATCHDOG
watchdog_init();
#endif
#ifdef CONFIG_APM
apm_bios_init();
#endif
+#ifdef CONFIG_H8
+ h8_init();
+#endif
#ifdef CONFIG_RTC
rtc_init();
#endif
#ifdef CONFIG_ATARI_DSP56K
dsp56k_init();
#endif
+#ifdef CONFIG_HFMODEM
+ hfmodem_init();
+#endif
#ifdef CONFIG_NVRAM
nvram_init();
#endif
+#ifdef CONFIG_HFMODEM
+ hfmodem_init();
+#endif
#ifdef CONFIG_SGI_GRAPHICS
gfx_register ();
streamable_init ();
diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c
index 78089e833..e07c8648d 100644
--- a/drivers/char/psaux.c
+++ b/drivers/char/psaux.c
@@ -642,29 +642,20 @@ __initfunc(int psaux_init(void))
queue->head = queue->tail = 0;
queue->proc_list = NULL;
if (!qp_found) {
- printk ("AUX--1\n");
aux_start_atomic();
#ifdef INITIALIZE_DEVICE
- printk ("AUX--2\n");
kbd_write_command(AUX_ENABLE); /* Enable Aux */
aux_write_ack(AUX_SET_SAMPLE);
- printk ("AUX--3\n");
aux_write_ack(100); /* 100 samples/sec */
aux_write_ack(AUX_SET_RES);
aux_write_ack(3); /* 8 counts per mm */
- printk ("AUX--4\n");
aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */
poll_aux_status();
- printk ("AUX--5\n");
#endif /* INITIALIZE_DEVICE */
- printk ("AUX--6\n");
kbd_write_command(KBD_CCMD_MOUSE_DISABLE); /* Disable Aux device */
poll_aux_status();
- printk ("AUX--7\n");
kbd_write_command(KBD_CCMD_WRITE_MODE); /* Disable controller interrupts */
- kbd_pause ();
poll_aux_status();
- printk ("AUX--8\n");
kbd_write_output (AUX_INTS_OFF);
kbd_pause ();
poll_aux_status();
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
index ca561c4e9..d9a8d5cea 100644
--- a/drivers/char/serial.c
+++ b/drivers/char/serial.c
@@ -3611,7 +3611,7 @@ static inline void wait_for_xmitr(struct serial_state *ser)
* Print a string to the serial port trying not to disturb any possible
* real use of the port...
*/
-static int serial_console_write(const char *s, unsigned count)
+static void serial_console_write(const char *s, unsigned count)
{
struct serial_state *ser;
int ier;
@@ -3646,14 +3646,12 @@ static int serial_console_write(const char *s, unsigned count)
*/
wait_for_xmitr(ser);
outb(ier, ser->port + UART_IER);
-
- return (0);
}
/*
* Receive character from the serial port
*/
-static int serial_console_wait_key(void)
+static void serial_console_wait_key(void)
{
struct serial_state *ser;
int ier;
@@ -3677,8 +3675,6 @@ static int serial_console_wait_key(void)
/* Restore the interrupts */
outb(ier, ser->port + UART_IER);
-
- return c;
}
static int serial_console_device(void)
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index c14babf6d..02f42ec87 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -374,7 +374,6 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file,
{
struct tty_struct * real_tty;
int retval;
- int opt = 0;
if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
tty->driver.subtype == PTY_TYPE_MASTER)
@@ -407,19 +406,19 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file,
return -EFAULT;
return 0;
case TCSETSF:
- opt |= TERMIOS_FLUSH;
+ return set_termios(real_tty, arg, TERMIOS_FLUSH);
case TCSETSW:
- opt |= TERMIOS_WAIT;
+ return set_termios(real_tty, arg, TERMIOS_WAIT);
case TCSETS:
- return set_termios(real_tty, arg, opt);
+ return set_termios(real_tty, arg, 0);
case TCGETA:
return get_termio(real_tty,(struct termio *) arg);
case TCSETAF:
- opt |= TERMIOS_FLUSH;
+ return set_termios(real_tty, arg, TERMIOS_FLUSH | TERMIOS_TERMIO);
case TCSETAW:
- opt |= TERMIOS_WAIT;
+ return set_termios(real_tty, arg, TERMIOS_WAIT | TERMIOS_TERMIO);
case TCSETA:
- return set_termios(real_tty, arg, opt|TERMIOS_TERMIO);
+ return set_termios(real_tty, arg, TERMIOS_TERMIO);
case TCXONC:
retval = tty_check_change(tty);
if (retval)
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 7b007f465..971426bf1 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -511,8 +511,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
* If the time is zero, turn off sound ourselves.
*/
ticks = HZ * ((arg >> 16) & 0xffff) / 1000;
- if ((arg & 0xffff) == 0 ) arg |= 1; /* jp: huh? */
- count = ticks ? (1193180 / (arg & 0xffff)) : 0;
+ count = ticks ? (arg & 0xffff) : 0;
+ if (count)
+ count = 1193180 / count;
kd_mksound(count, ticks);
return 0;
}
diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c
index 34fde7bd9..19202f515 100644
--- a/drivers/isdn/avmb1/capi.c
+++ b/drivers/isdn/avmb1/capi.c
@@ -1,11 +1,14 @@
/*
- * $Id: capi.c,v 1.4 1997/05/27 15:17:50 fritz Exp $
+ * $Id: capi.c,v 1.1 1997/06/08 14:58:39 ralf Exp $
*
* CAPI 2.0 Interface for Linux
*
* Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: capi.c,v $
+ * Revision 1.1 1997/06/08 14:58:39 ralf
+ * These files were missing in the 2.1.42 merge.
+ *
* Revision 1.4 1997/05/27 15:17:50 fritz
* Added changes for recent 2.1.x kernels:
* changed return type of isdn_close
@@ -253,7 +256,7 @@ static unsigned int
capi_poll(struct file *file, poll_table * wait)
{
unsigned int mask = 0;
- unsigned int minor = MINOR(file->f_inode->i_rdev);
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
struct capidev *cdev;
if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
diff --git a/drivers/isdn/hisax/l3_1tr6.c b/drivers/isdn/hisax/l3_1tr6.c
index 99e318ac1..b3479fbd2 100644
--- a/drivers/isdn/hisax/l3_1tr6.c
+++ b/drivers/isdn/hisax/l3_1tr6.c
@@ -1,4 +1,4 @@
-/* $Id: l3_1tr6.c,v 1.11 1997/04/06 22:54:18 keil Exp $
+/* $Id: l3_1tr6.c,v 1.2 1997/06/03 09:24:33 ralf Exp $
* German 1TR6 D-channel protocol
*
@@ -6,6 +6,9 @@
*
*
* $Log: l3_1tr6.c,v $
+ * Revision 1.2 1997/06/03 09:24:33 ralf
+ * Sync with Linux 2.1.42.
+ *
* Revision 1.11 1997/04/06 22:54:18 keil
* Using SKB's
*
@@ -49,7 +52,7 @@
#include <linux/ctype.h>
extern char *HiSax_getrev(const char *revision);
-const char *l3_1tr6_revision = "$Revision: 1.11 $";
+const char *l3_1tr6_revision = "$Revision: 1.2 $";
#define MsgHead(ptr, cref, mty, dis) \
*ptr++ = dis; \
@@ -700,7 +703,7 @@ up1tr6(struct PStack *st, int pr, void *arg)
if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) {
if (st->l3.debug & L3_DEB_PROTERR) {
- sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %ld state %d",
+ sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d state %d",
(pr == DL_DATA) ? " " : "(broadcast) ",
skb->data[0], skb->len, st->l3.state);
l3_debug(st, tmp);
diff --git a/drivers/isdn/hisax/l3dss1.c b/drivers/isdn/hisax/l3dss1.c
index 60493560f..ea1f7490e 100644
--- a/drivers/isdn/hisax/l3dss1.c
+++ b/drivers/isdn/hisax/l3dss1.c
@@ -1,4 +1,4 @@
-/* $Id: l3dss1.c,v 1.15 1997/04/17 11:50:48 keil Exp $
+/* $Id: l3dss1.c,v 1.2 1997/06/03 09:24:34 ralf Exp $
* EURO/DSS1 D-channel protocol
*
@@ -9,6 +9,9 @@
* Fritz Elfert
*
* $Log: l3dss1.c,v $
+ * Revision 1.2 1997/06/03 09:24:34 ralf
+ * Sync with Linux 2.1.42.
+ *
* Revision 1.15 1997/04/17 11:50:48 keil
* pa->loc was undefined, if it was not send by the exchange
*
@@ -64,7 +67,7 @@
#include <linux/ctype.h>
extern char *HiSax_getrev(const char *revision);
-const char *dss1_revision = "$Revision: 1.15 $";
+const char *dss1_revision = "$Revision: 1.2 $";
#define MsgHead(ptr, cref, mty) \
*ptr++ = 0x8; \
@@ -721,7 +724,7 @@ dss1up(struct PStack *st, int pr, void *arg)
if (skb->data[0] != PROTO_DIS_EURO) {
if (st->l3.debug & L3_DEB_PROTERR) {
- sprintf(tmp, "dss1up%sunexpected discriminator %x message len %ld state %d",
+ sprintf(tmp, "dss1up%sunexpected discriminator %x message len %d state %d",
(pr == DL_DATA) ? " " : "(broadcast) ",
skb->data[0], skb->len, st->l3.state);
l3_debug(st, tmp);
diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c
index dcd251a01..86d8a3432 100644
--- a/drivers/isdn/isdn_common.c
+++ b/drivers/isdn/isdn_common.c
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.c,v 1.44 1997/05/27 15:17:23 fritz Exp $
+/* $Id: isdn_common.c,v 1.2 1997/06/03 09:24:18 ralf Exp $
* Linux ISDN subsystem, common used functions (linklevel).
*
@@ -21,6 +21,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdn_common.c,v $
+ * Revision 1.2 1997/06/03 09:24:18 ralf
+ * Sync with Linux 2.1.42.
+ *
* Revision 1.44 1997/05/27 15:17:23 fritz
* Added changes for recent 2.1.x kernels:
* changed return type of isdn_close
@@ -218,7 +221,7 @@
isdn_dev *dev = (isdn_dev *) 0;
-static char *isdn_revision = "$Revision: 1.44 $";
+static char *isdn_revision = "$Revision: 1.2 $";
extern char *isdn_net_revision;
extern char *isdn_tty_revision;
@@ -1012,7 +1015,7 @@ static unsigned int
isdn_poll(struct file *file, poll_table * wait)
{
unsigned int mask = 0;
- unsigned int minor = MINOR(file->f_inode->i_rdev);
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
if (minor == ISDN_MINOR_STATUS) {
diff --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c
index a909c298f..f527a8e8e 100644
--- a/drivers/isdn/isdn_ppp.c
+++ b/drivers/isdn/isdn_ppp.c
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.27 1997/03/30 16:51:17 calle Exp $
+/* $Id: isdn_ppp.c,v 1.2 1997/06/03 09:24:20 ralf Exp $
*
* Linux ISDN subsystem, functions for synchronous PPP (linklevel).
*
@@ -19,6 +19,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdn_ppp.c,v $
+ * Revision 1.2 1997/06/03 09:24:20 ralf
+ * Sync with Linux 2.1.42.
+ *
* Revision 1.27 1997/03/30 16:51:17 calle
* changed calls to copy_from_user/copy_to_user and removed verify_area
* were possible.
@@ -160,7 +163,7 @@ static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb,
static void isdn_ppp_free_mpqueue(isdn_net_dev *);
#endif
-char *isdn_ppp_revision = "$Revision: 1.27 $";
+char *isdn_ppp_revision = "$Revision: 1.2 $";
static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
static struct isdn_ppp_compressor *ipc_head = NULL;
@@ -700,7 +703,7 @@ isdn_ppp_poll(struct file *file, poll_table * wait)
is = file->private_data;
if (is->debug & 0x2)
- printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", MINOR(file->f_inode->i_rdev));
+ printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", MINOR(file->f_dentry->d_inode->i_rdev));
poll_wait(&is->wq, wait);
diff --git a/drivers/macintosh/.cvsignore b/drivers/macintosh/.cvsignore
new file mode 100644
index 000000000..4671378ae
--- /dev/null
+++ b/drivers/macintosh/.cvsignore
@@ -0,0 +1 @@
+.depend
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
new file mode 100644
index 000000000..ed92d17bc
--- /dev/null
+++ b/drivers/macintosh/Makefile
@@ -0,0 +1,46 @@
+#
+# Makefile for the Macintosh-specific device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := macintosh.a
+M_OBJS :=
+L_OBJS := via-cuda.o adb.o nvram.o
+
+ifeq ($(CONFIG_SERIAL),y)
+ LX_OBJS += macserial.o
+else
+ ifeq ($(CONFIG_SERIAL),m)
+ MX_OBJS += macserial.o
+ endif
+endif
+
+ifdef CONFIG_MAC_KEYBOARD
+L_OBJS += mac_keyb.o mackeymap.o
+endif
+
+ifdef CONFIG_VT
+ ifdef CONFIG_PMAC_CONSOLE
+ L_OBJS += pmac-cons.o control.o platinum.o valkyrie.o
+ ifdef CONFIG_ATY_VIDEO
+ L_OBJS += aty.o
+ endif
+ ifdef CONFIG_IMSTT_VIDEO
+ L_OBJS += imstt.o
+ endif
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+mackeymap.c: mackeymap.map
+ loadkeys --mktable mackeymap.map > mackeymap.c
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
new file mode 100644
index 000000000..30a7100c0
--- /dev/null
+++ b/drivers/macintosh/adb.c
@@ -0,0 +1,169 @@
+/*
+ * Device driver for the /dev/adb device on macintoshes.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <asm/prom.h>
+#include <asm/cuda.h>
+#include <asm/uaccess.h>
+
+#define ADB_MAJOR 56 /* major number for /dev/adb */
+
+extern void adbdev_init(void);
+
+struct adbdev_state {
+ struct cuda_request req;
+};
+
+static struct wait_queue *adb_wait;
+
+static int adb_wait_reply(struct adbdev_state *state, struct file *file)
+{
+ int ret = 0;
+ struct wait_queue wait = { current, NULL };
+
+ add_wait_queue(&adb_wait, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+
+ while (!state->req.got_reply) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+ if (current->signal & ~current->blocked) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&adb_wait, &wait);
+
+ return ret;
+}
+
+static void adb_write_done(struct cuda_request *req)
+{
+ if (!req->got_reply) {
+ req->reply_len = 0;
+ req->got_reply = 1;
+ }
+ wake_up_interruptible(&adb_wait);
+}
+
+static int adb_open(struct inode *inode, struct file *file)
+{
+ struct adbdev_state *state;
+
+ if (MINOR(inode->i_rdev) > 0)
+ return -ENXIO;
+ state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);
+ if (state == 0)
+ return -ENOMEM;
+ file->private_data = state;
+ state->req.reply_expected = 0;
+ return 0;
+}
+
+static int adb_release(struct inode *inode, struct file *file)
+{
+ struct adbdev_state *state = file->private_data;
+
+ if (state) {
+ file->private_data = NULL;
+ if (state->req.reply_expected && !state->req.got_reply)
+ if (adb_wait_reply(state, file))
+ return 0;
+ kfree(state);
+ }
+ return 0;
+}
+
+static long long adb_lseek(struct inode *inode, struct file *file,
+ long long offset, int origin)
+{
+ return -ESPIPE;
+}
+
+static long adb_read(struct inode *inode, struct file *file,
+ char *buf, unsigned long count)
+{
+ int ret;
+ struct adbdev_state *state = file->private_data;
+
+ if (count < 2)
+ return -EINVAL;
+ if (count > sizeof(state->req.reply))
+ count = sizeof(state->req.reply);
+ ret = verify_area(VERIFY_WRITE, buf, count);
+ if (ret)
+ return ret;
+
+ if (!state->req.reply_expected)
+ return 0;
+
+ ret = adb_wait_reply(state, file);
+ if (ret)
+ return ret;
+
+ ret = state->req.reply_len;
+ copy_to_user(buf, state->req.reply, ret);
+ state->req.reply_expected = 0;
+
+ return ret;
+}
+
+static long adb_write(struct inode *inode, struct file *file,
+ const char *buf, unsigned long count)
+{
+ int ret;
+ struct adbdev_state *state = file->private_data;
+
+ if (count < 2 || count > sizeof(state->req.data))
+ return -EINVAL;
+ ret = verify_area(VERIFY_READ, buf, count);
+ if (ret)
+ return ret;
+
+ if (state->req.reply_expected && !state->req.got_reply) {
+ /* A previous request is still being processed.
+ Wait for it to finish. */
+ ret = adb_wait_reply(state, file);
+ if (ret)
+ return ret;
+ }
+
+ state->req.nbytes = count;
+ state->req.done = adb_write_done;
+ copy_from_user(state->req.data, buf, count);
+ state->req.reply_expected = 1;
+ state->req.got_reply = 0;
+ cuda_send_request(&state->req);
+
+ return count;
+}
+
+static struct file_operations adb_fops = {
+ adb_lseek,
+ adb_read,
+ adb_write,
+ NULL, /* no readdir */
+ NULL, /* no poll yet */
+ NULL, /* no ioctl yet */
+ NULL, /* no mmap */
+ adb_open,
+ adb_release
+};
+
+void adbdev_init()
+{
+ if (register_chrdev(ADB_MAJOR, "adb", &adb_fops))
+ printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);
+}
diff --git a/drivers/macintosh/ati-gt.h b/drivers/macintosh/ati-gt.h
new file mode 100644
index 000000000..792912192
--- /dev/null
+++ b/drivers/macintosh/ati-gt.h
@@ -0,0 +1,149 @@
+#if 0 /* not filled inaty_gt_reg_init yet */
+/* Register values for 1280x1024, 75Hz mode (20) */
+static struct aty_regvals aty_gt_reg_init_20 = {
+ { 0x10, 0x28, 0x3c },
+ { },
+ { } /* pixel clock = 134.61MHz for V=74.81Hz */
+};
+
+/* Register values for 1280x960, 75Hz mode (19) */
+static struct aty_regvals aty_gt_reg_init_19 = {
+ { 0x10, 0x28, 0x3c },
+ { },
+ { } /* pixel clock = 126.01MHz for V=75.01 Hz */
+};
+
+/* Register values for 1152x870, 75Hz mode (18) */
+static struct aty_regvals aty_gt_reg_init_18 = {
+ { 0x10, 0x28, 0x50 },
+ { },
+ { } /* pixel clock = 100.33MHz for V=75.31Hz */
+};
+
+/* Register values for 1024x768, 75Hz mode (17) */
+static struct aty_regvals aty_gt_reg_init_17 = {
+ { 0x10, 0x28, 0x50 },
+ { },
+ { } /* pixel clock = 79.55MHz for V=74.50Hz */
+};
+
+/* Register values for 1024x768, 72Hz mode (15) */
+static struct aty_regvals aty_gt_reg_init_15 = {
+ { 0x10, 0x28, 0x50 },
+ { },
+ { } /* pixel clock = 78.12MHz for V=72.12Hz */
+};
+
+#endif
+
+
+/* Register values for 1280x1024, 60Hz mode (20) */
+static struct aty_regvals aty_gt_reg_init_20 = {
+ { 0, 0, 0 },
+
+ { 0x310086, 0x310084, 0x310084 },
+ { 0x3070200, 0x30e0300, 0x30e0300 },
+ { 0x2002312, 0x3002312, 0x3002312 },
+
+ 0x7f00a5, 0x2ff0325, 0x260302, 0x20100000,
+ { 0x88, 0x7 }
+};
+
+/* Register values for 1024x768, 75Hz mode (17) */
+static struct aty_regvals aty_gt_reg_init_17 = {
+ { 0, 0, 0 },
+
+ { 0xc0085, 0xc0083, 0xc0083 },
+ { 0x3070200, 0x30e0300, 0x30e0300 },
+ { 0x2002312, 0x3002312, 0x3002312 },
+
+ 0x7f00a3, 0x2ff031f, 0x30300, 0x20100000,
+ { 0x41, 0x3 }
+};
+
+/* Register values for 1024x768, 72Hz mode (15) */
+static struct aty_regvals aty_gt_reg_init_15 = {
+ { 0, 0, 0 },
+
+ { 0x310086, 0x310084, 0x310084 },
+ { 0x3070200, 0x30e0300, 0x30e0300 },
+ { 0x2002312, 0x3002312, 0x3002312 },
+
+ 0x7f00a5, 0x2ff0325, 0x260302, 0x20100000,
+ { 0x88, 0x7 }
+};
+
+/* Register values for 1024x768, 60Hz mode (14) */
+static struct aty_regvals aty_gt_reg_init_14 = {
+ { 0, 0, 0 },
+
+ { 0x310086, 0x310084, 0x310084 },
+ { 0x3060200, 0x30d0300, 0x30d0300 },
+ { 0x2002312, 0x3002312, 0x3002312 },
+
+ 0x7f00a7, 0x2ff0325, 0x260302, 0x20100000,
+ { 0x6c, 0x6 }
+};
+
+/* Register values for 832x624, 75Hz mode (13) */
+static struct aty_regvals aty_gt_reg_init_13 = {
+ { 0x200, 0x200, 0x200 },
+
+ { 0x28006f, 0x28006d, 0x28006c },
+ { 0x3050200, 0x30b0300, 0x30e0600 },
+ { 0x2002312, 0x3002312, 0x6002312 },
+
+ 0x67008f, 0x26f029a, 0x230270, 0x1a100040,
+ { 0x4f, 0x05 }
+};
+
+#if 0 /* not filled in yet */
+/* Register values for 800x600, 75Hz mode (12) */
+static struct aty_regvals aty_gt_reg_init_12 = {
+ { 0x10, 0x28, 0x50 },
+ { },
+ { } /* pixel clock = 49.11MHz for V=74.40Hz */
+};
+
+/* Register values for 800x600, 72Hz mode (11) */
+static struct aty_regvals aty_gt_reg_init_11 = {
+ { 0x10, 0x28, 0x50 },
+ { },
+ { } /* pixel clock = 49.63MHz for V=71.66Hz */
+};
+
+/* Register values for 800x600, 60Hz mode (10) */
+static struct aty_regvals aty_gt_reg_init_10 = {
+ { 0x10, 0x28, 0x50 },
+ { },
+ { } /* pixel clock = 41.41MHz for V=59.78Hz */
+};
+
+/* Register values for 640x870, 75Hz Full Page Display (7) */
+static struct aty_regvals aty_gt_reg_init_7 = {
+ { 0x10, 0x30, 0x68 },
+ { },
+ { } /* pixel clock = 57.29MHz for V=75.01Hz */
+};
+#endif
+
+/* Register values for 640x480, 67Hz mode (6) */
+static struct aty_regvals aty_gt_reg_init_6 = {
+ { 0x200, 0x200, 0x200 },
+
+ { 0x28005b, 0x280059, 0x280058 },
+ { 0x3040200, 0x3060300, 0x30c0600 },
+ { 0x2002312, 0x3002312, 0x6002312 },
+
+ 0x4f006b, 0x1df020c, 0x2301e2, 0x14100040,
+ { 0x35, 0x07 }
+};
+
+#if 0 /* not filled in yet */
+/* Register values for 640x480, 60Hz mode (5) */
+static struct aty_regvals aty_gt_reg_init_5 = {
+ { 0x200, 0x200, 0x200 },
+ { },
+ { 0x35, 0x07 }
+};
+#endif
diff --git a/drivers/macintosh/ati-vt.h b/drivers/macintosh/ati-vt.h
new file mode 100644
index 000000000..b3489239b
--- /dev/null
+++ b/drivers/macintosh/ati-vt.h
@@ -0,0 +1,147 @@
+#if 0 /* not filled inaty_vt_reg_init yet */
+/* Register values for 640x870, 75Hz Full Page Display (7) */
+static struct aty_regvals aty_vt_reg_init_7 = {
+ { 0x10, 0x30, 0x68 },
+ { },
+ { } /* pixel clock = 57.29MHz for V=75.01Hz */
+};
+
+/* Register values for 1024x768, 72Hz mode (15) */
+static struct aty_regvals aty_vt_reg_init_15 = {
+ { 0x10, 0x28, 0x50 },
+ { },
+ { } /* pixel clock = 78.12MHz for V=72.12Hz */
+};
+
+#endif
+
+/* Register values for 1280x1024, 60Hz mode (20) */
+static struct aty_regvals aty_vt_reg_init_20 = {
+ { 0, 0, 0 },
+ { 0x2e02a7, 0x2e02a7, 0x2e02a7},
+ { 0x3070200, 0x3070200, 0x3070200},
+ { 0xa00cb22, 0xb00cb23, 0xe00cb24 },
+
+ 0x9f00d2, 0x3ff0429, 0x30400, 0x28000000,
+ { 0x00, 0xaa }
+};
+
+/* Register values for 1152x870, 75Hz mode (18) */
+static struct aty_regvals aty_vt_reg_init_18 = {
+ { 0, 0, 0 },
+ { 0x300295, 0x300194, 0x300194 },
+ { 0x3060200, 0x30e0300, 0x30e0300 },
+ { 0xa00cb21, 0xb00cb22, 0xe00cb23 },
+
+ 0x8f00b5, 0x3650392, 0x230368, 0x24000000,
+ { 0x00, 0x9d }
+ /* pixel clock = 100.33MHz for V=75.31Hz */
+};
+
+/* Register values for 1024x768, 75Hz mode (17) */
+static struct aty_regvals aty_vt_reg_init_17 = {
+ { 0, 0, 0 },
+
+ { 0x2c0283, 0x2c0182, 0x2c0182 },
+ { 0x3060200, 0x30a0300, 0x30a0300 },
+ { 0xa00cb21, 0xb00cb22, 0xe00cb23 },
+
+ 0x7f00a3, 0x2ff031f, 0x30300, 0x20000000,
+ { 0x01, 0xf7 }
+};
+
+/* Register values for 1024x768, 72Hz mode (15) */
+static struct aty_regvals aty_vt_reg_init_15 = {
+ { 0, 0, 0 },
+
+ { 0x310284, 0x310183, 0x310183 },
+ { 0x3060200, 0x3090300, 0x3090600 },
+ { 0xa00cb21, 0xb00cb22, 0xe00cb23 },
+
+ 0x7f00a5, 0x2ff0325, 0x260302, 0x20000000,
+ { 0x01, 0xeb }
+};
+
+/* Register values for 1024x768, 60Hz mode (14) */
+static struct aty_regvals aty_vt_reg_init_14 = {
+ { 0, 0, 0 },
+ { 0x310284, 0x310183, 0x310183 },
+ { 0x3060200, 0x3080300, 0x30f0600 },
+ { 0xa00cb21, 0xb00cb22, 0xe00cb23 },
+
+ 0x7f00a7, 0x2ff0325, 0x260302, 0x20000000,
+ { 0x01, 0xcc }
+};
+
+/* Register values for 832x624, 75Hz mode (13) */
+static struct aty_regvals aty_vt_reg_init_13 = {
+ { 0, 0, 0 },
+
+ { 0x28026d, 0x28016c, 0x28016c },
+ { 0x3060200, 0x3080300, 0x30f0600 },
+ { 0xa00cb21, 0xb00cb21, 0xe00cb22 },
+
+ 0x67008f, 0x26f029a, 0x230270, 0x1a000000,
+ { 0x01, 0xb4 }
+};
+
+/* Register values for 800x600, 75Hz mode (12) */
+static struct aty_regvals aty_vt_reg_init_12 = {
+ { 0, 0, 0 },
+ { 0x2a0267, 0x2a0166, 0x2a0565 },
+ { 0x3040200, 0x3060300, 0x30d0600 },
+ { 0xa00cb21, 0xb00cb21, 0xe00cb22 },
+
+ 0x630083, 0x2570270, 0x30258, 0x19000000,
+ { 0x01, 0x9c }
+ /* pixel clock = 49.11MHz for V=74.40Hz */
+};
+
+/* Register values for 800x600, 72Hz mode (11) */
+static struct aty_regvals aty_vt_reg_init_11 = {
+ { 0, 0, 0 },
+
+ { 0x2f026c, 0x2f016b, 0x2f056a },
+ { 0x3040200, 0x3060300, 0x30d0600 },
+ { 0xa00cb21, 0xb00cb21, 0xe00cb22 },
+
+ 0x630081, 0x257029b, 0x6027c, 0x19000000,
+ { 0x01, 0x9d }
+ /* pixel clock = 49.63MHz for V=71.66Hz */
+};
+
+/* Register values for 800x600, 60Hz mode (10) */
+static struct aty_regvals aty_vt_reg_init_10 = {
+ { 0, 0, 0 },
+ { 0x30026a, 0x300169, 0x300568 },
+ { 0x3030200, 0x3050300, 0x30b0600 },
+ { 0xa00cb21, 0xb00cb21, 0xe00cb22 },
+
+ 0x630083, 0x2570273, 0x40258, 0x19000000,
+ { 0x02, 0xfb }
+/* pixel clock = 41.41MHz for V=59.78Hz */
+};
+
+/* Register values for 640x480, 67Hz mode (6) */
+static struct aty_regvals aty_vt_reg_init_6 = {
+ { 0, 0, 0 },
+
+ { 0x280259, 0x280158, 0x280557 },
+ { 0x3030200, 0x3040300, 0x3080600 },
+ { 0xa00cb21, 0xb00cb21, 0xe00cb22 },
+
+ 0x4f006b, 0x1df020c, 0x2301e2, 0x14000000,
+ { 0x02, 0xbe }
+};
+
+/* Register values for 640x480, 60Hz mode (5) */
+static struct aty_regvals aty_vt_reg_init_5 = {
+ { 0, 0, 0 },
+
+ { 0x2c0253, 0x2c0152, 0x2c0551 },
+ { 0x3030200, 0x3040300, 0x3060600 },
+ { 0xa00cb21, 0xb00cb21, 0xe00cb22 },
+
+ 0x4f0063, 0x1df020c, 0x2201e9, 0x14000000,
+ { 0x02, 0x9e }
+};
diff --git a/drivers/macintosh/aty.c b/drivers/macintosh/aty.c
new file mode 100644
index 000000000..007fbd754
--- /dev/null
+++ b/drivers/macintosh/aty.c
@@ -0,0 +1,568 @@
+/*
+ * aty.c: Console support for ATI/mach64 display adaptor cards.
+ *
+ * Copyright (C) 1997 Michael AK Tesch
+ * written with much help from Jon Howell
+ * changes to support the vt chip set by harry ac eaton
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/vc_ioctl.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/nvram.h>
+#include <asm/prom.h>
+#include <asm/io.h>
+#include <asm/pci-bridge.h>
+#include <linux/selection.h>
+#include <linux/vt_kern.h>
+#include "pmac-cons.h"
+#include "aty.h"
+
+struct aty_cmap_regs {
+ unsigned char windex;
+ unsigned char lut;
+ unsigned char mask;
+ unsigned char rindex;
+ unsigned char cntl;
+};
+
+struct aty_regvals {
+ int offset[3]; /* first pixel address */
+
+ int crtc_h_sync_strt_wid[3]; /* depth dependant */
+ int crtc_gen_cntl[3];
+ int mem_cntl[3];
+
+ int crtc_h_tot_disp; /* mode dependant */
+ int crtc_v_tot_disp;
+ int crtc_v_sync_strt_wid;
+ int crtc_off_pitch;
+
+ unsigned char clock_val[2]; /* vals for 20 and 21 */
+};
+
+/*static void set_aty_clock(unsigned char *params);*/
+static int aty_vram_reqd(int vmode, int cmode);
+
+static unsigned char *frame_buffer;
+static int total_vram; /* total amount of video memory, bytes */
+static int is_vt_chip; /* whether a vt chip type was detected */
+
+static unsigned long aty_regbase;
+static struct aty_cmap_regs *aty_cmap_regs;
+
+/* this array contains the number of bytes/line for each mode and color depth */
+static int pitch[20][3] = {
+ {0,0,0}, /* mode 1?? */
+ {0,0,0}, /* mode 2?? */
+ {0,0,0}, /* mode 3??*/
+ {0,0,0}, /* mode 4?? */
+ {640, 1280, 2560}, /* mode 5 */
+ {640, 1280, 2560}, /* mode 6 */
+ {640, 1280, 2560}, /* mode 7 */
+ {800, 1600, 3200}, /* mode 8 */
+ {0,0,0}, /* mode 9 ??*/
+ {800, 1600, 3200}, /* mode 10 */
+ {800, 1600, 3200}, /* mode 11 */
+ {800, 1600, 3200}, /* mode 12 */
+ {832, 1664, 3328}, /* mode 13 */
+ {1024, 2048, 4096}, /* mode 14 */
+ {1024, 2048, 4096}, /* mode 15 */
+ {1024, 2048, 4096}, /* mode 16 */
+ {1024, 2048, 4096}, /* mode 17 */
+ {1152, 2304, 4608}, /* mode 18 */
+ {1280, 2560, 5120}, /* mode 19 */
+ {1280, 2560, 5120} /* mode 20 */
+};
+
+#include "ati-gt.h"
+#include "ati-vt.h"
+
+static struct aty_regvals *aty_gt_reg_init[20] = {
+ NULL, NULL, NULL, NULL,
+ &aty_gt_reg_init_6,
+ &aty_gt_reg_init_6,
+ NULL,
+ NULL, NULL,
+ NULL,
+ NULL,NULL,
+ &aty_gt_reg_init_13,
+ &aty_gt_reg_init_14,
+ &aty_gt_reg_init_15,
+ NULL,
+ &aty_gt_reg_init_17,
+ NULL,
+ NULL,
+ &aty_gt_reg_init_20
+};
+
+static struct aty_regvals *aty_vt_reg_init[20] = {
+ NULL, NULL, NULL, NULL,
+ &aty_vt_reg_init_5,
+ &aty_vt_reg_init_6,
+ NULL,
+ NULL, NULL,
+ &aty_vt_reg_init_10,
+ &aty_vt_reg_init_11,
+ &aty_vt_reg_init_12,
+ &aty_vt_reg_init_13,
+ &aty_vt_reg_init_14,
+ &aty_vt_reg_init_15,
+ NULL,
+ &aty_vt_reg_init_17,
+ &aty_vt_reg_init_18,
+ NULL,
+ &aty_vt_reg_init_20
+};
+
+static inline int aty_vram_reqd(int vmode, int cmode)
+{
+ return vmode_attrs[vmode-1].vres
+ * pitch[vmode-1][cmode];
+}
+
+extern inline unsigned aty_ld_rev(volatile unsigned long addr)
+{
+ unsigned val;
+
+ (long)addr += (long)aty_regbase;
+ asm volatile("lwbrx %0,0,%1" : "=r" (val) : "r" (addr));
+ return val;
+}
+
+extern inline void aty_st_rev(volatile unsigned long addr, unsigned val)
+{
+ (long)addr += (long)aty_regbase;
+ asm volatile("stwbrx %0,0,%1" : : "r" (val), "r" (addr) : "memory");
+}
+
+extern inline unsigned char aty_ld_byte(volatile unsigned long addr)
+{
+ unsigned char val;
+
+ val = *(char*)((long)addr+(long)aty_regbase);
+ return val;
+}
+
+extern inline void aty_st_byte(volatile unsigned long addr, unsigned char val)
+{
+ *(unsigned char*)(addr+(unsigned long)aty_regbase) = val;
+}
+
+void
+aty_st_514( int offset, char val )
+{
+ aty_WaitQueue(5);
+ aty_st_byte( DAC_CNTL, 1);
+ aty_st_byte( DAC_W_INDEX, offset & 0xff ); /* right addr byte */
+ aty_st_byte( DAC_DATA, (offset>>8) & 0xff ); /* left addr byte */
+ aty_st_byte( DAC_MASK, val );
+ aty_st_byte( DAC_CNTL, 0 );
+}
+
+void
+aty_st_pll( int offset, char val )
+{
+ aty_WaitQueue(3);
+ aty_st_byte( CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN ); /* write addr byte */
+ aty_st_byte( CLOCK_CNTL + 2, val); /* write the register value */
+ aty_st_byte( CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN);
+}
+
+#if 0
+unsigned char
+aty_ld_514( int offset )
+{
+/* do the same thing as aty_st_514, just read the DAC_MASK instead of writing*/
+}
+#endif
+
+void
+aty_set_palette(unsigned char red[], unsigned char green[],
+ unsigned char blue[], int index, int ncolors)
+{
+ int i,scale;
+
+ aty_WaitQueue(2);
+ aty_st_byte(DAC_CNTL, aty_ld_byte(DAC_CNTL) & 0xfc);
+ aty_st_byte(DAC_REGS + DAC_MASK, 0xff);
+ eieio();
+ scale = (is_vt_chip) ? ((color_mode == CMODE_16) ? 3 : 0) : 0;
+ for (i = 0; i < ncolors; ++i) {
+ aty_WaitQueue(4);
+ aty_cmap_regs->windex = (index + i) << scale; eieio();
+ aty_cmap_regs->lut = red[i]; eieio();
+ aty_cmap_regs->lut = green[i]; eieio();
+ aty_cmap_regs->lut = blue[i]; eieio();
+ }
+
+}
+
+void
+map_aty_display(struct device_node *dp)
+{
+ int i, sense;
+ unsigned long addr;
+ unsigned char bus, devfn;
+ unsigned short cmd;
+
+ if (dp->next != 0)
+ printk("Warning: only using first ATI card detected\n");
+ if (dp->n_addrs != 1)
+ panic("expecting 1 addresses for ATY (got %d)", dp->n_addrs);
+
+ aty_regbase = (int)ioremap((0x7ffc00 + dp->addrs[0].address), 0x1000);
+ aty_cmap_regs = (struct aty_cmap_regs *)(aty_regbase+0xC0);
+
+ /* enable memory-space accesses using config-space command register */
+ if (pci_device_loc(dp, &bus, &devfn) == 0) {
+ pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
+ if (cmd != 0xffff) {
+ cmd |= PCI_COMMAND_MEMORY;
+ pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
+ }
+ }
+
+ switch( aty_ld_rev(MEM_CNTL)&MEM_SIZE_ALIAS ) {
+ case MEM_SIZE_512K:
+ total_vram = 0x80000;
+ break;
+ case MEM_SIZE_1M:
+ total_vram = 0x100000;
+ break;
+ case MEM_SIZE_2M:
+ total_vram = 0x200000;
+ break;
+ case MEM_SIZE_4M:
+ total_vram = 0x400000;
+ break;
+ case MEM_SIZE_6M:
+ total_vram = 0x600000;
+ break;
+ case MEM_SIZE_8M:
+ total_vram = 0x800000;
+ break;
+ default:
+ total_vram = 0x80000;
+ }
+#if 1
+ printk("aty_display_init: node = %p, addrs = ", dp->node);
+ printk(" %x(%x)", dp->addrs[0].address, dp->addrs[0].size);
+ printk(", intrs =");
+ for (i = 0; i < dp->n_intrs; ++i)
+ printk(" %x", dp->intrs[i]);
+ printk( "\nregbase: %x pci loc: %x:%x total_vram: %x cregs: %x\n", (int)aty_regbase,
+ bus, devfn, total_vram, (int)aty_cmap_regs );
+#endif
+ is_vt_chip = ((aty_ld_rev(CONFIG_CHIP_ID) & CFG_CHIP_TYPE) == MACH64_VT_ID);
+ /* Map in frame buffer */
+ addr = dp->addrs[0].address;
+
+ /* use the big-endian aperture (??) */
+ addr += 0x800000;
+ frame_buffer = ioremap(addr, 0x800000);
+
+
+ /* sense = read_aty_sense(); XXX not yet, just give it mine */
+ sense = 0x62b;
+ if (video_mode == VMODE_NVRAM) {
+ video_mode = nvram_read_byte(NV_VMODE);
+ if (video_mode <= 0 || video_mode > VMODE_MAX
+ || ((is_vt_chip) ? aty_vt_reg_init[video_mode-1] : aty_gt_reg_init[video_mode-1]) == 0)
+ video_mode = VMODE_CHOOSE;
+ }
+ if (video_mode == VMODE_CHOOSE)
+ video_mode = map_monitor_sense(sense);
+ if (((is_vt_chip) ? aty_vt_reg_init[video_mode-1] : aty_gt_reg_init[video_mode-1]) == 0)
+ video_mode = VMODE_640_480_60;
+
+ /*
+ * Reduce the pixel size if we don't have enough VRAM.
+ */
+
+ if (color_mode == CMODE_NVRAM)
+ color_mode = nvram_read_byte(NV_CMODE);
+ if (color_mode < CMODE_8 || color_mode > CMODE_32)
+ color_mode = CMODE_8;
+ while (aty_vram_reqd(video_mode, color_mode) > total_vram) {
+ while (color_mode > CMODE_8
+ && aty_vram_reqd(video_mode, color_mode) > total_vram)
+ --color_mode;
+ /*
+ * adjust the video mode smaller if there still is not enough VRAM
+ */
+ if (aty_vram_reqd(video_mode, color_mode) > total_vram)
+ while ((((is_vt_chip) ? aty_vt_reg_init[--video_mode - 1]
+ : aty_gt_reg_init[--video_mode -1 ]) == 0) && (video_mode > VMODE_640_480_60)) ;
+ }
+}
+
+#if 0
+static void
+set_aty_clock(unsigned char *params)
+{
+ /* done in aty_init...probably need to change for different modes */
+ printk("tried to set ATY clock\n");
+}
+#endif
+
+void
+RGB514_Program(int cmode)
+{
+ typedef struct {
+ char pixel_dly;
+ char misc2_cntl;
+ char pixel_rep;
+ char pixel_cntl_index;
+ char pixel_cntl_v1;
+ } RGB514_DAC_Table;
+
+ static RGB514_DAC_Table RGB514DAC_Tab[8] = {
+ { 0, 0x41, 0x03, 0x71, 0x45 }, // 8bpp
+ { 0, 0x45, 0x04, 0x0c, 0x01 }, // 555
+ { 0, 0x45, 0x06, 0x0e, 0x00 }, // XRGB
+ };
+ RGB514_DAC_Table *pDacProgTab;
+
+ pDacProgTab = &RGB514DAC_Tab[cmode];
+
+ aty_st_514(0x90, 0x00);
+ aty_st_514(0x04, pDacProgTab->pixel_dly);
+ aty_st_514(0x05, 0x00);
+
+ aty_st_514(0x2, 0x1);
+ aty_st_514(0x71, pDacProgTab->misc2_cntl);
+ aty_st_514(0x0a, pDacProgTab->pixel_rep);
+
+ aty_st_514(pDacProgTab->pixel_cntl_index, pDacProgTab->pixel_cntl_v1);
+}
+
+/* The vt chipset seems to need a specialized color table for 15 bit mode */
+void
+VT_Program(int cmode)
+{
+#if 0
+ int i;
+ if (cmode != CMODE_8) {
+ aty_WaitQueue(2);
+ aty_cmap_regs->mask = 0xff; eieio();
+ aty_cmap_regs->windex = 0; eieio();
+ for (i = 0; i < 0x100; i++) {
+ aty_WaitQueue(3);
+ aty_cmap_regs->lut = i;
+ aty_cmap_regs->lut = i;
+ aty_cmap_regs->lut = i; eieio();
+ }
+ }
+#endif
+}
+
+void
+aty_init()
+{
+ int i, yoff, hres;
+ unsigned *p;
+ struct aty_regvals *init;
+
+ if (video_mode <= 0 || video_mode > VMODE_MAX
+ || (init = ((is_vt_chip) ? aty_vt_reg_init[video_mode-1] : aty_gt_reg_init[video_mode-1])) == 0)
+ panic("aty: display mode %d not supported", video_mode);
+ n_scanlines = vmode_attrs[video_mode-1].vres;
+ hres = vmode_attrs[video_mode-1].hres;
+ pixel_size = 1 << color_mode;
+ line_pitch = pitch[video_mode-1][color_mode];
+ row_pitch = line_pitch * 16;
+
+ aty_st_rev(BUS_CNTL, aty_ld_rev(BUS_CNTL) | BUS_HOST_ERR_ACK | BUS_FIFO_ERR_ACK);
+
+ /* Reset engine */
+ i = aty_ld_rev(GEN_TEST_CNTL);
+ aty_st_rev(GEN_TEST_CNTL, i & ~GUI_ENGINE_ENABLE);
+ eieio();
+ aty_WaitIdleEmpty();
+ aty_st_rev(GEN_TEST_CNTL, i | GUI_ENGINE_ENABLE);
+ aty_WaitIdleEmpty();
+
+ i = aty_ld_byte(CRTC_GEN_CNTL+3);
+ aty_st_byte(CRTC_GEN_CNTL+3, i | (CRTC_EXT_DISP_EN >> 24));
+
+ i = aty_ld_byte(GEN_TEST_CNTL);
+ aty_st_byte(GEN_TEST_CNTL, i | GEN_OVR_OUTPUT_EN );
+
+ if (!is_vt_chip) {
+ RGB514_Program(color_mode);
+
+ aty_WaitIdleEmpty();
+
+ aty_st_514(0x06, 0x02);
+ aty_st_514(0x10, 0x01);
+ aty_st_514(0x70, 0x01);
+ aty_st_514(0x8f, 0x1f);
+ aty_st_514(0x03, 0x00);
+ aty_st_514(0x05, 0x00);
+
+ aty_st_514(0x20, init->clock_val[0]);
+ aty_st_514(0x21, init->clock_val[1]);
+ } else {
+ VT_Program(color_mode);
+ aty_st_pll(PLL_MACRO_CNTL, 0xb5);
+ aty_st_pll(PLL_REF_DIV, 0x2d);
+ aty_st_pll(PLL_GEN_CNTL, 0x14);
+ aty_st_pll(MCLK_FB_DIV, 0xbd);
+ aty_st_pll(PLL_VCLK_CNTL, 0x0b);
+ aty_st_pll(VCLK_POST_DIV, init->clock_val[0]);
+ aty_st_pll(VCLK0_FB_DIV, init->clock_val[1]);
+ aty_st_pll(VCLK1_FB_DIV, 0xd6);
+ aty_st_pll(VCLK2_FB_DIV, 0xee);
+ aty_st_pll(VCLK3_FB_DIV, 0xf8);
+ aty_st_pll(PLL_XCLK_CNTL, 0x0);
+ aty_st_pll(PLL_TEST_CTRL, 0x0);
+ aty_st_pll(PLL_TEST_COUNT, 0x0);
+ }
+
+ aty_ld_byte( DAC_REGS ); /* clear counter */
+
+ aty_WaitIdleEmpty();
+
+ aty_st_rev(CRTC_H_TOTAL_DISP, init->crtc_h_tot_disp);
+ aty_st_rev(CRTC_H_SYNC_STRT_WID, init->crtc_h_sync_strt_wid[color_mode]);
+ aty_st_rev(CRTC_V_TOTAL_DISP, init->crtc_v_tot_disp);
+ aty_st_rev(CRTC_V_SYNC_STRT_WID, init->crtc_v_sync_strt_wid);
+
+ aty_st_byte(CLOCK_CNTL, 0);
+ aty_st_byte(CLOCK_CNTL, CLOCK_STROBE);
+
+ aty_st_rev(CRTC_OFF_PITCH, init->crtc_off_pitch);
+
+ aty_st_rev(CRTC_VLINE_CRNT_VLINE, 0x14e01d0);
+/* The magic constant below translates into:
+ * 5 = No RDY delay, 1 wait st for mem write, increment during burst transfer
+ * 9 = DAC access delayed, 1 wait state for DAC
+ * 0 = Disables interupts for FIFO errors
+ * e = Allows FIFO to generate 14 wait states before generating error
+ * 1 = DAC snooping disabled, ROM disabled
+ * 0 = ROM page at 0 (disabled so doesn't matter)
+ * f = 15 ROM wait states (disabled so doesn't matter)
+ * f = 15 BUS wait states (I'm not sure this applies to PCI bus types)
+ * at some point it would be good to experiment with bench marks to see if
+ * we can gain some speed by fooling with the wait states etc.
+ */
+ aty_st_rev(BUS_CNTL, 0x590e10ff);
+ i = aty_ld_rev(MEM_CNTL) & MEM_SIZE_ALIAS;
+ if (total_vram >= 0x400000)
+ aty_st_rev(MEM_CNTL, (init->mem_cntl[color_mode] &0xffff0000) | 0x0538 | i);
+ else
+ aty_st_rev(MEM_CNTL, (init->mem_cntl[color_mode] & ~MEM_SIZE_ALIAS) | i );
+ aty_st_rev(CRTC_INT_CNTL, 0x2);
+ aty_WaitIdleEmpty();
+/* These magic constants are harder to figure out
+ * on the vt chipset bit 3 set makes the screen brighter
+ * and bit 15 makes the screen black! But nothing else
+ * seems to matter for the vt DAC_CNTL
+ */
+ if (is_vt_chip)
+ aty_st_rev(DAC_CNTL, 0x47012104);
+ else
+ aty_st_rev(DAC_CNTL, 0x47012100);
+
+ aty_st_byte(DAC_MASK, 0xff);
+
+ aty_st_rev(CRTC_INT_CNTL, 0x00000002);
+
+ aty_st_byte(CRTC_FIFO, ((char*)&init->crtc_gen_cntl[color_mode])[1] );
+ aty_st_byte(CRTC_PIX_WIDTH, ((char*)&init->crtc_gen_cntl[color_mode])[2] );
+ aty_st_byte(CRTC_EXT_DISP, ((char*)&init->crtc_gen_cntl[color_mode])[0] );
+
+ aty_st_rev(GEN_TEST_CNTL, 0x300); /* gui_en block_en*/
+
+ pmac_init_palette(); /* Initialize colormap */
+ yoff = (n_scanlines % 16) / 2;
+ fb_start = frame_buffer + yoff * line_pitch + init->offset[color_mode];
+
+ /* Clear screen */
+ p = (unsigned *) fb_start;
+
+ for (i = n_scanlines * line_pitch * pixel_size / sizeof(unsigned); i != 0; --i)
+ *p++ = 0;
+ display_info.height = n_scanlines;
+ display_info.width = hres;
+ display_info.depth = pixel_size * 8;
+ display_info.pitch = line_pitch;
+ display_info.mode = video_mode;
+ strncpy(display_info.name, "ATY Mach64", sizeof(display_info.name));
+ display_info.fb_address = (unsigned long) frame_buffer + init->offset[color_mode];
+ display_info.cmap_adr_address = (unsigned long) &aty_cmap_regs->windex;
+ display_info.cmap_data_address = (unsigned long) &aty_cmap_regs->lut;
+ display_info.disp_reg_address = aty_regbase;
+}
+
+int
+aty_setmode(struct vc_mode *mode, int doit)
+{
+ int cmode;
+#if 0
+ if (mode->mode == 21) {
+ printk("hace: about to set 0x%x to 0x%x\n",mode->depth, mode->pitch & 0xff);
+ aty_st_byte(mode->depth, mode->pitch & 0xff);
+ return 0;
+ }
+
+ if (mode->mode == 0) {
+ printk("hace: 0x%x contains 0x%x\n",mode->depth, aty_ld_byte(mode->depth));
+ return 0;
+ }
+#endif
+
+ if (mode->mode <= 0 || mode->mode > VMODE_MAX
+ || ((is_vt_chip) ? aty_vt_reg_init[mode->mode-1] : aty_gt_reg_init[mode->mode-1]) == NULL)
+ return -EINVAL;
+ switch (mode->depth) {
+ case 24:
+ case 32:
+ cmode = CMODE_32;
+ break;
+ case 16:
+ cmode = CMODE_16;
+ break;
+ case 8:
+ case 0: /* (default) */
+ cmode = CMODE_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (aty_vram_reqd(mode->mode, cmode) > total_vram) {
+ return -EINVAL;
+ }
+ if (doit) {
+ video_mode = mode->mode;
+ color_mode = cmode;
+ aty_init();
+ }
+ return 0;
+}
+
+void
+aty_set_blanking(int blank_mode)
+{
+ char gen_cntl;
+
+ gen_cntl = aty_ld_byte(CRTC_GEN_CNTL);
+ if (blank_mode & VESA_VSYNC_SUSPEND)
+ gen_cntl |= 0x8;
+ if (blank_mode & VESA_HSYNC_SUSPEND)
+ gen_cntl |= 0x4;
+ if ((blank_mode & VESA_POWERDOWN) == VESA_POWERDOWN)
+ gen_cntl |= 0x40;
+ if (blank_mode == VESA_NO_BLANKING)
+ gen_cntl &= ~(0x4c);
+ aty_st_byte(CRTC_GEN_CNTL, gen_cntl);
+}
diff --git a/drivers/macintosh/aty.h b/drivers/macintosh/aty.h
new file mode 100644
index 000000000..27ba7bd45
--- /dev/null
+++ b/drivers/macintosh/aty.h
@@ -0,0 +1,614 @@
+/*
+ * Exported procedures for the ATI/mach64 display driver on PowerMacs.
+ *
+ * Copyright (C) 1997 Michael AK Tesch
+ * written with much help from Jon Howell
+ *
+ * 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.
+ */
+
+extern void map_aty_display(struct device_node *);
+extern void aty_init(void);
+extern int aty_setmode(struct vc_mode *mode, int doit);
+extern void aty_set_palette(unsigned char red[], unsigned char green[],
+ unsigned char blue[], int index, int ncolors);
+extern void aty_set_blanking(int blank_mode);
+
+/*
+ * most of the rest of this file comes from ATI sample code
+ */
+#ifndef REGMACH64_H
+#define REGMACH64_H
+
+/* NON-GUI MEMORY MAPPED Registers - expressed in BYTE offsets */
+
+#define CRTC_H_TOTAL_DISP 0x0000 /* Dword offset 00 */
+#define CRTC_H_SYNC_STRT_WID 0x0004 /* Dword offset 01 */
+#define CRTC_H_SYNC_STRT 0x0004
+#define CRTC_H_SYNC_DLY 0x0005
+#define CRTC_H_SYNC_WID 0x0006
+
+#define CRTC_V_TOTAL_DISP 0x0008 /* Dword offset 02 */
+#define CRTC_V_TOTAL 0x0008
+#define CRTC_V_DISP 0x000a
+#define CRTC_V_SYNC_STRT_WID 0x000C /* Dword offset 03 */
+#define CRTC_V_SYNC_STRT 0x000c
+#define CRTC_V_SYNC_WID 0x000e
+
+#define CRTC_VLINE_CRNT_VLINE 0x0010 /* Dword offset 04 */
+#define CRTC_OFF_PITCH 0x0014 /* Dword offset 05 */
+#define CRTC_OFFSET 0x0014
+#define CRTC_PITCH 0x0016
+
+#define CRTC_INT_CNTL 0x0018 /* Dword offset 06 */
+#define CRTC_GEN_CNTL 0x001C /* Dword offset 07 */
+#define CRTC_PIX_WIDTH 0x001d
+#define CRTC_FIFO 0x001e
+#define CRTC_EXT_DISP 0x001f
+
+#define OVR_CLR 0x0040 /* Dword offset 10 */
+#define OVR_WID_LEFT_RIGHT 0x0044 /* Dword offset 11 */
+#define OVR_WID_TOP_BOTTOM 0x0048 /* Dword offset 12 */
+
+#define CUR_CLR0 0x0060 /* Dword offset 18 */
+#define CUR_CLR1 0x0064 /* Dword offset 19 */
+#define CUR_OFFSET 0x0068 /* Dword offset 1A */
+#define CUR_HORZ_VERT_POSN 0x006C /* Dword offset 1B */
+#define CUR_HORZ_VERT_OFF 0x0070 /* Dword offset 1C */
+
+#define SCRATCH_REG0 0x0080 /* Dword offset 20 */
+#define SCRATCH_REG1 0x0084 /* Dword offset 21 */
+
+#define CLOCK_CNTL 0x0090 /* Dword offset 24 */
+#define CLOCK_SEL_CNTL 0x0090 // Dword offset 24
+
+#define BUS_CNTL 0x00A0 /* Dword offset 28 */
+
+#define MEM_CNTL 0x00B0 /* Dword offset 2C */
+
+#define MEM_VGA_WP_SEL 0x00B4 /* Dword offset 2D */
+#define MEM_VGA_RP_SEL 0x00B8 /* Dword offset 2E */
+
+#define DAC_REGS 0x00C0 /* Dword offset 30 */
+#define DAC_W_INDEX 0x00C0 /* Dword offset 30 */
+#define DAC_DATA 0x00C1 /* Dword offset 30 */
+#define DAC_MASK 0x00C2 /* Dword offset 30 */
+#define DAC_R_INDEX 0x00C3 /* Dword offset 30 */
+#define DAC_CNTL 0x00C4 /* Dword offset 31 */
+
+#define GEN_TEST_CNTL 0x00D0 /* Dword offset 34 */
+
+#define CONFIG_CNTL 0x00DC /* Dword offset 37 (CT, ET, VT) */
+#define CONFIG_CHIP_ID 0x00E0 /* Dword offset 38 */
+#define CONFIG_STAT0 0x00E4 /* Dword offset 39 */
+#define CONFIG_STAT1 0x00E8 /* Dword offset 3A */
+
+
+/* GUI MEMORY MAPPED Registers */
+
+#define DST_OFF_PITCH 0x0100 /* Dword offset 40 */
+#define DST_X 0x0104 /* Dword offset 41 */
+#define DST_Y 0x0108 /* Dword offset 42 */
+#define DST_Y_X 0x010C /* Dword offset 43 */
+#define DST_WIDTH 0x0110 /* Dword offset 44 */
+#define DST_HEIGHT 0x0114 /* Dword offset 45 */
+#define DST_HEIGHT_WIDTH 0x0118 /* Dword offset 46 */
+#define DST_X_WIDTH 0x011C /* Dword offset 47 */
+#define DST_BRES_LNTH 0x0120 /* Dword offset 48 */
+#define DST_BRES_ERR 0x0124 /* Dword offset 49 */
+#define DST_BRES_INC 0x0128 /* Dword offset 4A */
+#define DST_BRES_DEC 0x012C /* Dword offset 4B */
+#define DST_CNTL 0x0130 /* Dword offset 4C */
+
+#define SRC_OFF_PITCH 0x0180 /* Dword offset 60 */
+#define SRC_X 0x0184 /* Dword offset 61 */
+#define SRC_Y 0x0188 /* Dword offset 62 */
+#define SRC_Y_X 0x018C /* Dword offset 63 */
+#define SRC_WIDTH1 0x0190 /* Dword offset 64 */
+#define SRC_HEIGHT1 0x0194 /* Dword offset 65 */
+#define SRC_HEIGHT1_WIDTH1 0x0198 /* Dword offset 66 */
+#define SRC_X_START 0x019C /* Dword offset 67 */
+#define SRC_Y_START 0x01A0 /* Dword offset 68 */
+#define SRC_Y_X_START 0x01A4 /* Dword offset 69 */
+#define SRC_WIDTH2 0x01A8 /* Dword offset 6A */
+#define SRC_HEIGHT2 0x01AC /* Dword offset 6B */
+#define SRC_HEIGHT2_WIDTH2 0x01B0 /* Dword offset 6C */
+#define SRC_CNTL 0x01B4 /* Dword offset 6D */
+
+#define HOST_DATA0 0x0200 /* Dword offset 80 */
+#define HOST_DATA1 0x0204 /* Dword offset 81 */
+#define HOST_DATA2 0x0208 /* Dword offset 82 */
+#define HOST_DATA3 0x020C /* Dword offset 83 */
+#define HOST_DATA4 0x0210 /* Dword offset 84 */
+#define HOST_DATA5 0x0214 /* Dword offset 85 */
+#define HOST_DATA6 0x0218 /* Dword offset 86 */
+#define HOST_DATA7 0x021C /* Dword offset 87 */
+#define HOST_DATA8 0x0220 /* Dword offset 88 */
+#define HOST_DATA9 0x0224 /* Dword offset 89 */
+#define HOST_DATAA 0x0228 /* Dword offset 8A */
+#define HOST_DATAB 0x022C /* Dword offset 8B */
+#define HOST_DATAC 0x0230 /* Dword offset 8C */
+#define HOST_DATAD 0x0234 /* Dword offset 8D */
+#define HOST_DATAE 0x0238 /* Dword offset 8E */
+#define HOST_DATAF 0x023C /* Dword offset 8F */
+#define HOST_CNTL 0x0240 /* Dword offset 90 */
+
+#define PAT_REG0 0x0280 /* Dword offset A0 */
+#define PAT_REG1 0x0284 /* Dword offset A1 */
+#define PAT_CNTL 0x0288 /* Dword offset A2 */
+
+#define SC_LEFT 0x02A0 /* Dword offset A8 */
+#define SC_RIGHT 0x02A4 /* Dword offset A9 */
+#define SC_LEFT_RIGHT 0x02A8 /* Dword offset AA */
+#define SC_TOP 0x02AC /* Dword offset AB */
+#define SC_BOTTOM 0x02B0 /* Dword offset AC */
+#define SC_TOP_BOTTOM 0x02B4 /* Dword offset AD */
+
+#define DP_BKGD_CLR 0x02C0 /* Dword offset B0 */
+#define DP_FRGD_CLR 0x02C4 /* Dword offset B1 */
+#define DP_WRITE_MASK 0x02C8 /* Dword offset B2 */
+#define DP_CHAIN_MASK 0x02CC /* Dword offset B3 */
+#define DP_PIX_WIDTH 0x02D0 /* Dword offset B4 */
+#define DP_MIX 0x02D4 /* Dword offset B5 */
+#define DP_SRC 0x02D8 /* Dword offset B6 */
+
+#define CLR_CMP_CLR 0x0300 /* Dword offset C0 */
+#define CLR_CMP_MASK 0x0304 /* Dword offset C1 */
+#define CLR_CMP_CNTL 0x0308 /* Dword offset C2 */
+
+#define FIFO_STAT 0x0310 /* Dword offset C4 */
+
+#define CONTEXT_MASK 0x0320 /* Dword offset C8 */
+#define CONTEXT_LOAD_CNTL 0x032C /* Dword offset CB */
+
+#define GUI_TRAJ_CNTL 0x0330 /* Dword offset CC */
+#define GUI_STAT 0x0338 /* Dword offset CE */
+
+
+/* CRTC control values (mostly CRTC_GEN_CNTL) */
+
+#define CRTC_H_SYNC_NEG 0x00200000
+#define CRTC_V_SYNC_NEG 0x00200000
+
+#define CRTC_DBL_SCAN_EN 0x00000001
+#define CRTC_INTERLACE_EN 0x00000002
+#define CRTC_HSYNC_DIS 0x00000004
+#define CRTC_VSYNC_DIS 0x00000008
+#define CRTC_CSYNC_EN 0x00000010
+#define CRTC_PIX_BY_2_EN 0x00000020
+#define CRTC_BLANK 0x00000040
+
+#define CRTC_PIX_WIDTH_MASK 0x00000700
+#define CRTC_PIX_WIDTH_4BPP 0x00000100
+#define CRTC_PIX_WIDTH_8BPP 0x00000200
+#define CRTC_PIX_WIDTH_15BPP 0x00000300
+#define CRTC_PIX_WIDTH_16BPP 0x00000400
+#define CRTC_PIX_WIDTH_24BPP 0x00000500
+#define CRTC_PIX_WIDTH_32BPP 0x00000600
+
+#define CRTC_BYTE_PIX_ORDER 0x00000800
+#define CRTC_PIX_ORDER_MSN_LSN 0x00000000
+#define CRTC_PIX_ORDER_LSN_MSN 0x00000800
+
+#define CRTC_FIFO_LWM 0x000f0000
+#define CRTC_EXT_DISP_EN 0x01000000
+#define CRTC_EXT_EN 0x02000000
+
+#define CRTC_CRNT_VLINE 0x07f00000
+#define CRTC_VBLANK 0x00000001
+
+/* DAC control values */
+
+#define DAC_EXT_SEL_RS2 0x01
+#define DAC_EXT_SEL_RS3 0x02
+#define DAC_8BIT_EN 0x00000100
+#define DAC_PIX_DLY_MASK 0x00000600
+#define DAC_PIX_DLY_0NS 0x00000000
+#define DAC_PIX_DLY_2NS 0x00000200
+#define DAC_PIX_DLY_4NS 0x00000400
+#define DAC_BLANK_ADJ_MASK 0x00001800
+#define DAC_BLANK_ADJ_0 0x00000000
+#define DAC_BLANK_ADJ_1 0x00000800
+#define DAC_BLANK_ADJ_2 0x00001000
+
+
+/* Mix control values */
+
+#define MIX_NOT_DST 0x0000
+#define MIX_0 0x0001
+#define MIX_1 0x0002
+#define MIX_DST 0x0003
+#define MIX_NOT_SRC 0x0004
+#define MIX_XOR 0x0005
+#define MIX_XNOR 0x0006
+#define MIX_SRC 0x0007
+#define MIX_NAND 0x0008
+#define MIX_NOT_SRC_OR_DST 0x0009
+#define MIX_SRC_OR_NOT_DST 0x000a
+#define MIX_OR 0x000b
+#define MIX_AND 0x000c
+#define MIX_SRC_AND_NOT_DST 0x000d
+#define MIX_NOT_SRC_AND_DST 0x000e
+#define MIX_NOR 0x000f
+
+/* Maximum engine dimensions */
+#define ENGINE_MIN_X 0
+#define ENGINE_MIN_Y 0
+#define ENGINE_MAX_X 4095
+#define ENGINE_MAX_Y 16383
+
+/* Mach64 engine bit constants - these are typically ORed together */
+
+/* BUS_CNTL register constants */
+#define BUS_FIFO_ERR_ACK 0x00200000
+#define BUS_HOST_ERR_ACK 0x00800000
+
+/* GEN_TEST_CNTL register constants */
+#define GEN_OVR_OUTPUT_EN 0x20
+#define HWCURSOR_ENABLE 0x80
+#define GUI_ENGINE_ENABLE 0x100
+#define BLOCK_WRITE_ENABLE 0x200
+
+/* CLOCK_CNTL register constants */
+#define CLOCK_SEL 0x0f
+#define CLOCK_DIV 0x30
+#define CLOCK_DIV1 0x00
+#define CLOCK_DIV2 0x10
+#define CLOCK_DIV4 0x20
+#define CLOCK_STROBE 0x40
+#define PLL_WR_EN 0x02
+
+/* PLL registers */
+#define PLL_MACRO_CNTL 0x01
+#define PLL_REF_DIV 0x02
+#define PLL_GEN_CNTL 0x03
+#define MCLK_FB_DIV 0x04
+#define PLL_VCLK_CNTL 0x05
+#define VCLK_POST_DIV 0x06
+#define VCLK0_FB_DIV 0x07
+#define VCLK1_FB_DIV 0x08
+#define VCLK2_FB_DIV 0x09
+#define VCLK3_FB_DIV 0x0A
+#define PLL_XCLK_CNTL 0x0B
+#define PLL_TEST_CTRL 0x0E
+#define PLL_TEST_COUNT 0x0F
+
+/* Fields in PLL registers */
+#define PLL_PC_GAIN 0x07
+#define PLL_VC_GAIN 0x18
+#define PLL_DUTY_CYC 0xE0
+#define PLL_OVERRIDE 0x01
+#define PLL_MCLK_RST 0x02
+#define OSC_EN 0x04
+#define EXT_CLK_EN 0x08
+#define MCLK_SRC_SEL 0x70
+#define EXT_CLK_CNTL 0x80
+#define VCLK_SRC_SEL 0x03
+#define PLL_VCLK_RST 0x04
+#define VCLK_INVERT 0x08
+#define VCLK0_POST 0x03
+#define VCLK1_POST 0x0C
+#define VCLK2_POST 0x30
+#define VCLK3_POST 0xC0
+
+/* CONFIG_CNTL register constants */
+#define APERTURE_4M_ENABLE 1
+#define APERTURE_8M_ENABLE 2
+#define VGA_APERTURE_ENABLE 4
+
+/* CONFIG_STAT0 register constants (GX, CX) */
+#define CFG_BUS_TYPE 0x00000007
+#define CFG_MEM_TYPE 0x00000038
+#define CFG_INIT_DAC_TYPE 0x00000e00
+
+/* CONFIG_STAT0 register constants (CT, ET, VT) */
+#define CFG_MEM_TYPE_xT 0x00000007
+
+#define ISA 0
+#define EISA 1
+#define LOCAL_BUS 6
+#define PCI 7
+
+/* Memory types for GX, CX */
+#define DRAMx4 0
+#define VRAMx16 1
+#define VRAMx16ssr 2
+#define DRAMx16 3
+#define GraphicsDRAMx16 4
+#define EnhancedVRAMx16 5
+#define EnhancedVRAMx16ssr 6
+
+/* Memory types for CT, ET, VT, GT */
+#define DRAM 0
+#define EDO_DRAM 1
+#define PSEUDO_EDO 2
+#define SDRAM 3
+
+#define DAC_INTERNAL 0x00
+#define DAC_IBMRGB514 0x01
+#define DAC_ATI68875 0x02
+#define DAC_TVP3026_A 0x72
+#define DAC_BT476 0x03
+#define DAC_BT481 0x04
+#define DAC_ATT20C491 0x14
+#define DAC_SC15026 0x24
+#define DAC_MU9C1880 0x34
+#define DAC_IMSG174 0x44
+#define DAC_ATI68860_B 0x05
+#define DAC_ATI68860_C 0x15
+#define DAC_TVP3026_B 0x75
+#define DAC_STG1700 0x06
+#define DAC_ATT498 0x16
+#define DAC_STG1702 0x07
+#define DAC_SC15021 0x17
+#define DAC_ATT21C498 0x27
+#define DAC_STG1703 0x37
+#define DAC_CH8398 0x47
+#define DAC_ATT20C408 0x57
+
+#define CLK_ATI18818_0 0
+#define CLK_ATI18818_1 1
+#define CLK_STG1703 2
+#define CLK_CH8398 3
+#define CLK_INTERNAL 4
+#define CLK_ATT20C408 5
+#define CLK_IBMRGB514 6
+
+/* MEM_CNTL register constants */
+#define MEM_SIZE_ALIAS 0x00000007
+#define MEM_SIZE_512K 0x00000000
+#define MEM_SIZE_1M 0x00000001
+#define MEM_SIZE_2M 0x00000002
+#define MEM_SIZE_4M 0x00000003
+#define MEM_SIZE_6M 0x00000004
+#define MEM_SIZE_8M 0x00000005
+#define MEM_SIZE_ALIAS_GTB 0x0000000F
+#define MEM_SIZE_2M_GTB 0x00000003
+#define MEM_SIZE_4M_GTB 0x00000007
+#define MEM_SIZE_6M_GTB 0x00000009
+#define MEM_SIZE_8M_GTB 0x0000000B
+#define MEM_BNDRY 0x00030000
+#define MEM_BNDRY_0K 0x00000000
+#define MEM_BNDRY_256K 0x00010000
+#define MEM_BNDRY_512K 0x00020000
+#define MEM_BNDRY_1M 0x00030000
+#define MEM_BNDRY_EN 0x00040000
+
+/* ATI PCI constants */
+#define PCI_ATI_VENDOR_ID 0x1002
+#define PCI_MACH64_GX 0x4758
+#define PCI_MACH64_CX 0x4358
+#define PCI_MACH64_CT 0x4354
+#define PCI_MACH64_ET 0x4554
+#define PCI_MACH64_VT 0x5654
+#define PCI_MACH64_GT 0x4754
+
+/* CONFIG_CHIP_ID register constants */
+#define CFG_CHIP_TYPE 0x0000FFFF
+#define CFG_CHIP_CLASS 0x00FF0000
+#define CFG_CHIP_REV 0xFF000000
+#define CFG_CHIP_VERSION 0x07000000
+#define CFG_CHIP_FOUNDRY 0x38000000
+#define CFG_CHIP_REVISION 0xC0000000
+
+/* Chip IDs read from CONFIG_CHIP_ID */
+#define MACH64_GX_ID 0xD7
+#define MACH64_CX_ID 0x57
+#define MACH64_CT_ID 0x4354
+#define MACH64_ET_ID 0x4554
+#define MACH64_VT_ID 0x5654
+#define MACH64_GT_ID 0x4754
+
+/* Mach64 chip types */
+#define MACH64_UNKNOWN 0
+#define MACH64_GX 1
+#define MACH64_CX 2
+#define MACH64_CT 3
+#define MACH64_ET 4
+#define MACH64_VT 5
+#define MACH64_GT 6
+
+/* DST_CNTL register constants */
+#define DST_X_RIGHT_TO_LEFT 0
+#define DST_X_LEFT_TO_RIGHT 1
+#define DST_Y_BOTTOM_TO_TOP 0
+#define DST_Y_TOP_TO_BOTTOM 2
+#define DST_X_MAJOR 0
+#define DST_Y_MAJOR 4
+#define DST_X_TILE 8
+#define DST_Y_TILE 0x10
+#define DST_LAST_PEL 0x20
+#define DST_POLYGON_ENABLE 0x40
+#define DST_24_ROTATION_ENABLE 0x80
+
+/* SRC_CNTL register constants */
+#define SRC_PATTERN_ENABLE 1
+#define SRC_ROTATION_ENABLE 2
+#define SRC_LINEAR_ENABLE 4
+#define SRC_BYTE_ALIGN 8
+#define SRC_LINE_X_RIGHT_TO_LEFT 0
+#define SRC_LINE_X_LEFT_TO_RIGHT 0x10
+
+/* HOST_CNTL register constants */
+#define HOST_BYTE_ALIGN 1
+
+/* GUI_TRAJ_CNTL register constants */
+#define PAT_MONO_8x8_ENABLE 0x01000000
+#define PAT_CLR_4x2_ENABLE 0x02000000
+#define PAT_CLR_8x1_ENABLE 0x04000000
+
+/* DP_CHAIN_MASK register constants */
+#define DP_CHAIN_4BPP 0x8888
+#define DP_CHAIN_7BPP 0xD2D2
+#define DP_CHAIN_8BPP 0x8080
+#define DP_CHAIN_8BPP_RGB 0x9292
+#define DP_CHAIN_15BPP 0x4210
+#define DP_CHAIN_16BPP 0x8410
+#define DP_CHAIN_24BPP 0x8080
+#define DP_CHAIN_32BPP 0x8080
+
+/* DP_PIX_WIDTH register constants */
+#define DST_1BPP 0
+#define DST_4BPP 1
+#define DST_8BPP 2
+#define DST_15BPP 3
+#define DST_16BPP 4
+#define DST_32BPP 6
+#define SRC_1BPP 0
+#define SRC_4BPP 0x100
+#define SRC_8BPP 0x200
+#define SRC_15BPP 0x300
+#define SRC_16BPP 0x400
+#define SRC_32BPP 0x600
+#define HOST_1BPP 0
+#define HOST_4BPP 0x10000
+#define HOST_8BPP 0x20000
+#define HOST_15BPP 0x30000
+#define HOST_16BPP 0x40000
+#define HOST_32BPP 0x60000
+#define BYTE_ORDER_MSB_TO_LSB 0
+#define BYTE_ORDER_LSB_TO_MSB 0x1000000
+
+/* DP_MIX register constants */
+#define BKGD_MIX_NOT_D 0
+#define BKGD_MIX_ZERO 1
+#define BKGD_MIX_ONE 2
+#define BKGD_MIX_D 3
+#define BKGD_MIX_NOT_S 4
+#define BKGD_MIX_D_XOR_S 5
+#define BKGD_MIX_NOT_D_XOR_S 6
+#define BKGD_MIX_S 7
+#define BKGD_MIX_NOT_D_OR_NOT_S 8
+#define BKGD_MIX_D_OR_NOT_S 9
+#define BKGD_MIX_NOT_D_OR_S 10
+#define BKGD_MIX_D_OR_S 11
+#define BKGD_MIX_D_AND_S 12
+#define BKGD_MIX_NOT_D_AND_S 13
+#define BKGD_MIX_D_AND_NOT_S 14
+#define BKGD_MIX_NOT_D_AND_NOT_S 15
+#define BKGD_MIX_D_PLUS_S_DIV2 0x17
+#define FRGD_MIX_NOT_D 0
+#define FRGD_MIX_ZERO 0x10000
+#define FRGD_MIX_ONE 0x20000
+#define FRGD_MIX_D 0x30000
+#define FRGD_MIX_NOT_S 0x40000
+#define FRGD_MIX_D_XOR_S 0x50000
+#define FRGD_MIX_NOT_D_XOR_S 0x60000
+#define FRGD_MIX_S 0x70000
+#define FRGD_MIX_NOT_D_OR_NOT_S 0x80000
+#define FRGD_MIX_D_OR_NOT_S 0x90000
+#define FRGD_MIX_NOT_D_OR_S 0xa0000
+#define FRGD_MIX_D_OR_S 0xb0000
+#define FRGD_MIX_D_AND_S 0xc0000
+#define FRGD_MIX_NOT_D_AND_S 0xd0000
+#define FRGD_MIX_D_AND_NOT_S 0xe0000
+#define FRGD_MIX_NOT_D_AND_NOT_S 0xf0000
+#define FRGD_MIX_D_PLUS_S_DIV2 0x170000
+
+/* DP_SRC register constants */
+#define BKGD_SRC_BKGD_CLR 0
+#define BKGD_SRC_FRGD_CLR 1
+#define BKGD_SRC_HOST 2
+#define BKGD_SRC_BLIT 3
+#define BKGD_SRC_PATTERN 4
+#define FRGD_SRC_BKGD_CLR 0
+#define FRGD_SRC_FRGD_CLR 0x100
+#define FRGD_SRC_HOST 0x200
+#define FRGD_SRC_BLIT 0x300
+#define FRGD_SRC_PATTERN 0x400
+#define MONO_SRC_ONE 0
+#define MONO_SRC_PATTERN 0x10000
+#define MONO_SRC_HOST 0x20000
+#define MONO_SRC_BLIT 0x30000
+
+/* CLR_CMP_CNTL register constants */
+#define COMPARE_FALSE 0
+#define COMPARE_TRUE 1
+#define COMPARE_NOT_EQUAL 4
+#define COMPARE_EQUAL 5
+#define COMPARE_DESTINATION 0
+#define COMPARE_SOURCE 0x1000000
+
+/* FIFO_STAT register constants */
+#define FIFO_ERR 0x80000000
+
+/* CONTEXT_LOAD_CNTL constants */
+#define CONTEXT_NO_LOAD 0
+#define CONTEXT_LOAD 0x10000
+#define CONTEXT_LOAD_AND_DO_FILL 0x20000
+#define CONTEXT_LOAD_AND_DO_LINE 0x30000
+#define CONTEXT_EXECUTE 0
+#define CONTEXT_CMD_DISABLE 0x80000000
+
+/* GUI_STAT register constants */
+#define ENGINE_IDLE 0
+#define ENGINE_BUSY 1
+#define SCISSOR_LEFT_FLAG 0x10
+#define SCISSOR_RIGHT_FLAG 0x20
+#define SCISSOR_TOP_FLAG 0x40
+#define SCISSOR_BOTTOM_FLAG 0x80
+
+/* ATI VGA Extended Regsiters */
+#define sioATIEXT 0x1ce
+#define bioATIEXT 0x3ce
+
+#define ATI2E 0xae
+#define ATI32 0xb2
+#define ATI36 0xb6
+
+/* VGA Graphics Controller Registers */
+#define VGAGRA 0x3ce
+#define GRA06 0x06
+
+/* VGA Seququencer Registers */
+#define VGASEQ 0x3c4
+#define SEQ02 0x02
+#define SEQ04 0x04
+
+#define MACH64_MAX_X ENGINE_MAX_X
+#define MACH64_MAX_Y ENGINE_MAX_Y
+
+#define INC_X 0x0020
+#define INC_Y 0x0080
+
+#define RGB16_555 0x0000
+#define RGB16_565 0x0040
+#define RGB16_655 0x0080
+#define RGB16_664 0x00c0
+
+#define POLY_TEXT_TYPE 0x0001
+#define IMAGE_TEXT_TYPE 0x0002
+#define TEXT_TYPE_8_BIT 0x0004
+#define TEXT_TYPE_16_BIT 0x0008
+#define POLY_TEXT_TYPE_8 (POLY_TEXT_TYPE | TEXT_TYPE_8_BIT)
+#define IMAGE_TEXT_TYPE_8 (IMAGE_TEXT_TYPE | TEXT_TYPE_8_BIT)
+#define POLY_TEXT_TYPE_16 (POLY_TEXT_TYPE | TEXT_TYPE_16_BIT)
+#define IMAGE_TEXT_TYPE_16 (IMAGE_TEXT_TYPE | TEXT_TYPE_16_BIT)
+
+#define MACH64_NUM_CLOCKS 16
+#define MACH64_NUM_FREQS 50
+
+/* Wait until "v" queue entries are free */
+#define aty_WaitQueue(v) { while ((aty_ld_rev(FIFO_STAT) & 0xffff) > \
+ ((unsigned short)(0x8000 >> (v)))); }
+
+/* Wait until GP is idle and queue is empty */
+#define aty_WaitIdleEmpty() { aty_WaitQueue(16); \
+ while ((aty_ld_rev(GUI_STAT) & 1) != 0); }
+
+#define SKIP_2(_v) ((((_v)<<1)&0xfff8)|((_v)&0x3)|(((_v)&0x80)>>5))
+
+#define MACH64_BIT_BLT(_srcx, _srcy, _dstx, _dsty, _w, _h, _dir) \
+{ \
+ aty_WaitQueue(5); \
+ aty_st_rev(SRC_Y_X, (((_srcx) << 16) | ((_srcy) & 0x0000ffff))); \
+ aty_st_rev(SRC_WIDTH1, (_w)); \
+ aty_st_rev(DST_CNTL, (_dir)); \
+ aty_st_rev(DST_Y_X, (((_dstx) << 16) | ((_dsty) & 0x0000ffff))); \
+ aty_st_rev(DST_HEIGHT_WIDTH, (((_w) << 16) | ((_h) & 0x0000ffff))); \
+}
+#endif /* REGMACH64_H */
+
diff --git a/drivers/macintosh/control.c b/drivers/macintosh/control.c
new file mode 100644
index 000000000..fac8b0589
--- /dev/null
+++ b/drivers/macintosh/control.c
@@ -0,0 +1,520 @@
+/*
+ * control.c: Console support for PowerMac "control" display adaptor.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/vc_ioctl.h>
+#include <linux/nvram.h>
+#include <asm/prom.h>
+#include <asm/io.h>
+#include <asm/cuda.h>
+#include <linux/selection.h>
+#include "pmac-cons.h"
+#include "control.h"
+
+/*
+ * Structure of the registers for the RADACAL colormap device.
+ */
+struct cmap_regs {
+ unsigned char addr;
+ char pad1[15];
+ unsigned char d1;
+ char pad2[15];
+ unsigned char d2;
+ char pad3[15];
+ unsigned char lut;
+ char pad4[15];
+};
+
+/*
+ * Structure of the registers for the "control" display adaptor".
+ */
+#define PAD(x) char x[12]
+
+struct preg { /* padded register */
+ unsigned r;
+ char pad[12];
+};
+
+struct control_regs {
+ struct preg vcount; /* vertical counter */
+ /* Vertical parameters are in units of 1/2 scan line */
+ struct preg vswin; /* between vsblank and vssync */
+ struct preg vsblank; /* vert start blank */
+ struct preg veblank; /* vert end blank (display start) */
+ struct preg vewin; /* between vesync and veblank */
+ struct preg vesync; /* vert end sync */
+ struct preg vssync; /* vert start sync */
+ struct preg vperiod; /* vert period */
+ struct preg reg8;
+ /* Horizontal params are in units of 2 pixels */
+ struct preg hperiod; /* horiz period - 2 */
+ struct preg hsblank; /* horiz start blank */
+ struct preg heblank; /* horiz end blank */
+ struct preg hesync; /* horiz end sync */
+ struct preg hssync; /* horiz start sync */
+ struct preg rege;
+ struct preg regf;
+ struct preg reg10;
+ struct preg reg11;
+ struct preg ctrl; /* display control */
+ struct preg start_addr; /* start address: 5 lsbs zero */
+ struct preg pitch; /* addrs diff between scan lines */
+ struct preg mon_sense; /* monitor sense bits */
+ struct preg flags;
+ struct preg mode;
+ struct preg reg18;
+ struct preg reg19;
+ struct preg res[6];
+};
+
+static void set_control_clock(unsigned char *params);
+static int read_control_sense(void);
+static int control_vram_reqd(int vmode, int cmode);
+
+static int total_vram; /* total amount of video memory, bytes */
+static unsigned char *frame_buffer;
+static struct cmap_regs *cmap_regs;
+static struct control_regs *disp_regs;
+static int control_use_bank2;
+
+/*
+ * Register initialization tables for the control display.
+ *
+ * Dot clock rate is
+ * 3.9064MHz * 2**clock_params[2] * clock_params[1] / clock_params[0].
+ *
+ * The values for vertical frequency (V) in the comments below
+ * are the values measured using the modes under MacOS.
+ */
+struct control_regvals {
+ int pitch[3]; /* bytes/line, indexed by color_mode */
+ int offset[3]; /* first pixel address */
+ unsigned regs[16]; /* for vswin .. reg10 */
+ unsigned char mode[3]; /* indexed by color_mode */
+ unsigned char radacal_ctrl[3];
+ unsigned char clock_params[3];
+};
+
+/* Register values for 1280x1024, 75Hz mode (20) */
+static struct control_regvals control_reg_init_20 = {
+ { 1280, 2560, 0 },
+ { 0x10, 0x20, 0 },
+ { 2129, 2128, 80, 42, 4, 2130, 2132, 88,
+ 420, 411, 91, 35, 421, 18, 211, 386, },
+ { 1, 1, 1},
+ { 0x50, 0x64, 0x64 },
+ { 13, 56, 3 } /* pixel clock = 134.61MHz for V=74.81Hz */
+};
+
+/* Register values for 1280x960, 75Hz mode (19) */
+static struct control_regvals control_reg_init_19 = {
+ { 1280, 2560, 0 },
+ { 0x10, 0x20, 0 },
+ { 1997, 1996, 76, 40, 4, 1998, 2000, 86,
+ 418, 409, 89, 35, 419, 18, 210, 384, },
+ { 1, 1, 1 },
+ { 0x50, 0x64, 0x64 },
+ { 31, 125, 3 } /* pixel clock = 126.01MHz for V=75.01 Hz */
+};
+
+/* Register values for 1152x870, 75Hz mode (18) */
+static struct control_regvals control_reg_init_18 = {
+ { 1152, 2304, 4608 },
+ { 0x10, 0x28, 0x50 },
+ { 1825, 1822, 82, 43, 4, 1828, 1830, 120,
+ 726, 705, 129, 63, 727, 32, 364, 664 },
+ { 2, 1, 1 },
+ { 0x10, 0x14, 0x28 },
+ { 19, 61, 3 } /* pixel clock = 100.33MHz for V=75.31Hz */
+};
+
+/* Register values for 1024x768, 75Hz mode (17) */
+static struct control_regvals control_reg_init_17 = {
+ { 1024, 2048, 4096 },
+ { 0x10, 0x28, 0x50 },
+ { 1603, 1600, 64, 34, 4, 1606, 1608, 120,
+ 662, 641, 129, 47, 663, 24, 332, 616 },
+ { 2, 1, 1 },
+ { 0x10, 0x14, 0x28 },
+ { 11, 28, 3 } /* pixel clock = 79.55MHz for V=74.50Hz */
+};
+
+/* Register values for 1024x768, 72Hz mode (15) */
+static struct control_regvals control_reg_init_15 = {
+ { 1024, 2048, 4096 },
+ { 0x10, 0x28, 0x50 },
+ { 1607, 1604, 68, 39, 10, 1610, 1612, 132,
+ 670, 653, 141, 67, 671, 34, 336, 604, },
+ { 2, 1, 1 },
+ { 0x10, 0x14, 0x28 },
+ { 12, 30, 3 } /* pixel clock = 78.12MHz for V=72.12Hz */
+};
+
+/* Register values for 1024x768, 60Hz mode (14) */
+static struct control_regvals control_reg_init_14 = {
+ { 1024, 2048, 4096 },
+ { 0x10, 0x28, 0x50 },
+ { 1607, 1604, 68, 39, 10, 1610, 1612, 132,
+ 670, 653, 141, 67, 671, 34, 336, 604, },
+ { 2, 1, 1 },
+ { 0x10, 0x14, 0x28 },
+ { 15, 31, 3 } /* pixel clock = 64.58MHz for V=59.62Hz */
+};
+
+/* Register values for 832x624, 75Hz mode (13) */
+static struct control_regvals control_reg_init_13 = {
+ { 832, 1664, 3328 },
+ { 0x10, 0x28, 0x50 },
+ { 1331, 1330, 82, 43, 4, 1332, 1334, 128,
+ 574, 553, 137, 31, 575, 16, 288, 544 },
+ { 2, 1, 0 }, { 0x10, 0x14, 0x18 },
+ { 23, 42, 3 } /* pixel clock = 57.07MHz for V=74.27Hz */
+};
+
+/* Register values for 800x600, 75Hz mode (12) */
+static struct control_regvals control_reg_init_12 = {
+ { 800, 1600, 3200 },
+ { 0x10, 0x28, 0x50 },
+ { 1247, 1246, 46, 25, 4, 1248, 1250, 104,
+ 526, 513, 113, 39, 527, 20, 264, 488, },
+ { 2, 1, 0 }, { 0x10, 0x14, 0x18 },
+ { 7, 11, 3 } /* pixel clock = 49.11MHz for V=74.40Hz */
+};
+
+/* Register values for 800x600, 72Hz mode (11) */
+static struct control_regvals control_reg_init_11 = {
+ { 800, 1600, 3200 },
+ { 0x10, 0x28, 0x50 },
+ { 1293, 1256, 56, 33, 10, 1330, 1332, 76,
+ 518, 485, 85, 59, 519, 30, 260, 460, },
+ { 2, 1, 0 }, { 0x10, 0x14, 0x18 },
+ { 17, 27, 3 } /* pixel clock = 49.63MHz for V=71.66Hz */
+};
+
+/* Register values for 800x600, 60Hz mode (10) */
+static struct control_regvals control_reg_init_10 = {
+ { 800, 1600, 3200 },
+ { 0x10, 0x28, 0x50 },
+ { 1293, 1256, 56, 33, 10, 1330, 1332, 76,
+ 518, 485, 85, 59, 519, 30, 260, 460, },
+ { 2, 1, 0 }, { 0x10, 0x14, 0x18 },
+ { 20, 53, 2 } /* pixel clock = 41.41MHz for V=59.78Hz */
+};
+
+/* Register values for 640x870, 75Hz Full Page Display (7) */
+static struct control_regvals control_reg_init_7 = {
+ { 640, 1280, 2560 },
+ { 0x10, 0x30, 0x68 },
+ { 0x727, 0x724, 0x58, 0x2e, 0x4, 0x72a, 0x72c, 0x40,
+ 0x19e, 0x18c, 0x4c, 0x27, 0x19f, 0x14, 0xd0, 0x178 },
+ { 2, 1, 0 }, { 0x10, 0x14, 0x18 },
+ { 9, 33, 2 } /* pixel clock = 57.29MHz for V=75.01Hz */
+};
+
+/* Register values for 640x480, 67Hz mode (6) */
+static struct control_regvals control_reg_init_6 = {
+ { 640, 1280, 2560 },
+ { 0, 8, 0x10 },
+ { 1045, 1042, 82, 43, 4, 1048, 1050, 72,
+ 430, 393, 73, 31, 431, 16, 216, 400 },
+ { 2, 1, 0 }, { 0x10, 0x14, 0x18 },
+ { 14, 27, 2 } /* pixel clock = 30.13MHz for V=66.43Hz */
+};
+
+/* Register values for 640x480, 60Hz mode (5) */
+static struct control_regvals control_reg_init_5 = {
+ { 640, 1280, 2560 },
+ { 0x10, 0x28, 0x50 },
+ { 1037, 1026, 66, 34, 2, 1048, 1050, 56,
+ 398, 385, 65, 47, 399, 24, 200, 352, },
+ { 2, 1, 0 }, { 0x10, 0x14, 0x18 },
+ { 23, 37, 2 } /* pixel clock = 25.14MHz for V=59.85Hz */
+};
+
+static struct control_regvals *control_reg_init[20] = {
+ NULL, NULL, NULL, NULL,
+ &control_reg_init_5,
+ &control_reg_init_6,
+ &control_reg_init_7,
+ NULL, NULL,
+ &control_reg_init_10,
+ &control_reg_init_11,
+ &control_reg_init_12,
+ &control_reg_init_13,
+ &control_reg_init_14,
+ &control_reg_init_15,
+ NULL,
+ &control_reg_init_17,
+ &control_reg_init_18,
+ &control_reg_init_19,
+ &control_reg_init_20
+};
+
+/*
+ * Get the monitor sense value.
+ * Note that this can be called before calibrate_delay,
+ * so we can't use udelay.
+ */
+static int
+read_control_sense()
+{
+ int sense;
+
+ out_le32(&disp_regs->mon_sense.r, 7); /* drive all lines high */
+ __delay(200);
+ out_le32(&disp_regs->mon_sense.r, 077); /* turn off drivers */
+ __delay(2000);
+ sense = (in_le32(&disp_regs->mon_sense.r) & 0x1c0) << 2;
+
+ /* drive each sense line low in turn and collect the other 2 */
+ out_le32(&disp_regs->mon_sense.r, 033); /* drive A low */
+ __delay(2000);
+ sense |= (in_le32(&disp_regs->mon_sense.r) & 0xc0) >> 2;
+ out_le32(&disp_regs->mon_sense.r, 055); /* drive B low */
+ __delay(2000);
+ sense |= ((in_le32(&disp_regs->mon_sense.r) & 0x100) >> 5)
+ | ((in_le32(&disp_regs->mon_sense.r) & 0x40) >> 4);
+ out_le32(&disp_regs->mon_sense.r, 066); /* drive C low */
+ __delay(2000);
+ sense |= (in_le32(&disp_regs->mon_sense.r) & 0x180) >> 7;
+
+ out_le32(&disp_regs->mon_sense.r, 077); /* turn off drivers */
+ return sense;
+}
+
+static inline int control_vram_reqd(int vmode, int cmode)
+{
+ return vmode_attrs[vmode-1].vres
+ * control_reg_init[vmode-1]->pitch[cmode];
+}
+
+void
+map_control_display(struct device_node *dp)
+{
+ int i, sense;
+ unsigned long addr, size;
+ int bank1, bank2;
+
+ if (dp->next != 0)
+ printk("Warning: only using first control display device\n");
+ if (dp->n_addrs != 2)
+ panic("expecting 2 addresses for control (got %d)", dp->n_addrs);
+
+#if 0
+ printk("pmac_display_init: node = %p, addrs =", dp->node);
+ for (i = 0; i < dp->n_addrs; ++i)
+ printk(" %x(%x)", dp->addrs[i].address, dp->addrs[i].size);
+ printk(", intrs =");
+ for (i = 0; i < dp->n_intrs; ++i)
+ printk(" %x", dp->intrs[i]);
+ printk("\n");
+#endif
+
+ /* Map in frame buffer and registers */
+ for (i = 0; i < dp->n_addrs; ++i) {
+ addr = dp->addrs[i].address;
+ size = dp->addrs[i].size;
+ if (size >= 0x800000) {
+ /* use the big-endian aperture (??) */
+ addr += 0x800000;
+ /* map at most 8MB for the frame buffer */
+ frame_buffer = ioremap(addr, 0x800000);
+ } else {
+ disp_regs = ioremap(addr, size);
+ }
+ }
+ cmap_regs = ioremap(0xf301b000, 0x1000); /* XXX not in prom? */
+
+ /* Work out which banks of VRAM we have installed. */
+ frame_buffer[0] = 0x5a;
+ frame_buffer[1] = 0xc7;
+ bank1 = frame_buffer[0] == 0x5a && frame_buffer[1] == 0xc7;
+ frame_buffer[0x600000] = 0xa5;
+ frame_buffer[0x600001] = 0x38;
+ bank2 = frame_buffer[0x600000] == 0xa5 && frame_buffer[0x600001] == 0x38;
+ total_vram = (bank1 + bank2) * 0x200000;
+ /* If we don't have bank 1 installed, we hope we have bank 2 :-) */
+ control_use_bank2 = !bank1;
+ if (control_use_bank2)
+ frame_buffer += 0x600000;
+
+ sense = read_control_sense();
+ if (video_mode == VMODE_NVRAM) {
+ video_mode = nvram_read_byte(NV_VMODE);
+ if (video_mode <= 0 || video_mode > VMODE_MAX
+ || control_reg_init[video_mode-1] == 0)
+ video_mode = VMODE_CHOOSE;
+ }
+ if (video_mode == VMODE_CHOOSE)
+ video_mode = map_monitor_sense(sense);
+ if (control_reg_init[video_mode-1] == 0)
+ video_mode = VMODE_640_480_60;
+
+ /*
+ * Reduce the pixel size if we don't have enough VRAM.
+ */
+ if (color_mode == CMODE_NVRAM)
+ color_mode = nvram_read_byte(NV_CMODE);
+ if (color_mode < CMODE_8 || color_mode > CMODE_32)
+ color_mode = CMODE_8;
+ while (color_mode > CMODE_8
+ && control_vram_reqd(video_mode, color_mode) > total_vram)
+ --color_mode;
+
+ printk("Monitor sense value = 0x%x, ", sense);
+}
+
+static void
+set_control_clock(unsigned char *params)
+{
+ struct cuda_request req;
+ int i;
+
+ for (i = 0; i < 3; ++i) {
+ cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
+ 0x50, i + 1, params[i]);
+ while (!req.got_reply)
+ cuda_poll();
+ }
+}
+
+void
+control_init()
+{
+ struct preg *rp;
+ int i, yoff, hres;
+ int ctrl, flags;
+ unsigned *p;
+ struct control_regvals *init;
+
+ if (video_mode <= 0 || video_mode > VMODE_MAX
+ || (init = control_reg_init[video_mode-1]) == 0)
+ panic("control: display mode %d not supported", video_mode);
+ n_scanlines = vmode_attrs[video_mode-1].vres;
+ hres = vmode_attrs[video_mode-1].hres;
+ pixel_size = 1 << color_mode;
+ line_pitch = init->pitch[color_mode];
+ row_pitch = line_pitch * 16;
+
+ if (control_vram_reqd(video_mode, color_mode) > 0x200000)
+ flags = 0x51;
+ else if (control_use_bank2)
+ flags = 0x39;
+ else
+ flags = 0x31;
+ if (video_mode >= VMODE_1280_960_75 && color_mode >= CMODE_16)
+ ctrl = 0x7f;
+ else
+ ctrl = 0x3b;
+
+ /* Initialize display timing registers */
+ out_le32(&disp_regs->ctrl.r, 0x43b);
+ set_control_clock(init->clock_params);
+ cmap_regs->addr = 0x20; cmap_regs->d2 = init->radacal_ctrl[color_mode];
+ cmap_regs->addr = 0x21; cmap_regs->d2 = control_use_bank2? 0: 1;
+ cmap_regs->addr = 0x10; cmap_regs->d2 = 0;
+ cmap_regs->addr = 0x11; cmap_regs->d2 = 0;
+ rp = &disp_regs->vswin;
+ for (i = 0; i < 16; ++i, ++rp)
+ out_le32(&rp->r, init->regs[i]);
+ out_le32(&disp_regs->pitch.r, line_pitch);
+ out_le32(&disp_regs->mode.r, init->mode[color_mode]);
+ out_le32(&disp_regs->flags.r, flags);
+ out_le32(&disp_regs->start_addr.r, 0);
+ out_le32(&disp_regs->reg18.r, 0x1e5);
+ out_le32(&disp_regs->reg19.r, 0);
+
+ pmac_init_palette(); /* Initialize colormap */
+
+ /* Turn on display */
+ out_le32(&disp_regs->ctrl.r, ctrl);
+
+ yoff = (n_scanlines % 16) / 2;
+ fb_start = frame_buffer + yoff * line_pitch + init->offset[color_mode];
+
+ /* Clear screen */
+ p = (unsigned *) (frame_buffer + init->offset[color_mode]);
+ for (i = n_scanlines * line_pitch / sizeof(unsigned); i != 0; --i)
+ *p++ = 0;
+
+ display_info.height = n_scanlines;
+ display_info.width = hres;
+ display_info.depth = pixel_size * 8;
+ display_info.pitch = line_pitch;
+ display_info.mode = video_mode;
+ strncpy(display_info.name, "control", sizeof(display_info.name));
+ display_info.fb_address = (unsigned long) frame_buffer + init->offset[color_mode];
+ display_info.cmap_adr_address = (unsigned long) &cmap_regs->addr;
+ display_info.cmap_data_address = (unsigned long) &cmap_regs->lut;
+ display_info.disp_reg_address = (unsigned long) &disp_regs;
+}
+
+int
+control_setmode(struct vc_mode *mode, int doit)
+{
+ int cmode;
+
+ if (mode->mode <= 0 || mode->mode > VMODE_MAX
+ || control_reg_init[mode->mode-1] == 0)
+ return -EINVAL;
+ switch (mode->depth) {
+ case 24:
+ case 32:
+ cmode = CMODE_32;
+ break;
+ case 16:
+ cmode = CMODE_16;
+ break;
+ case 8:
+ case 0: /* (default) */
+ cmode = CMODE_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (control_vram_reqd(mode->mode, cmode) > total_vram)
+ return -EINVAL;
+ if (doit) {
+ video_mode = mode->mode;
+ color_mode = cmode;
+ control_init();
+ }
+ return 0;
+}
+
+void
+control_set_palette(unsigned char red[], unsigned char green[],
+ unsigned char blue[], int index, int ncolors)
+{
+ int i;
+
+ for (i = 0; i < ncolors; ++i) {
+ cmap_regs->addr = index + i; eieio();
+ cmap_regs->lut = red[i]; eieio();
+ cmap_regs->lut = green[i]; eieio();
+ cmap_regs->lut = blue[i]; eieio();
+ }
+}
+
+void
+control_set_blanking(int blank_mode)
+{
+ int ctrl;
+
+ ctrl = ld_le32(&disp_regs->ctrl.r) | 0x33;
+ if (blank_mode & VESA_VSYNC_SUSPEND)
+ ctrl &= ~3;
+ if (blank_mode & VESA_HSYNC_SUSPEND)
+ ctrl &= ~0x30;
+ out_le32(&disp_regs->ctrl.r, ctrl);
+}
diff --git a/drivers/macintosh/control.h b/drivers/macintosh/control.h
new file mode 100644
index 000000000..edf87fe5b
--- /dev/null
+++ b/drivers/macintosh/control.h
@@ -0,0 +1,17 @@
+/*
+ * Exported procedures for the "control" display driver on PowerMacs.
+ *
+ * Copyright (C) 1997 Paul Mackerras.
+ *
+ * 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.
+ */
+
+extern void map_control_display(struct device_node *);
+extern void control_init(void);
+extern int control_setmode(struct vc_mode *mode, int doit);
+extern void control_set_palette(unsigned char red[], unsigned char green[],
+ unsigned char blue[], int index, int ncolors);
+extern void control_set_blanking(int blank_mode);
diff --git a/drivers/macintosh/imstt.c b/drivers/macintosh/imstt.c
new file mode 100644
index 000000000..2f74a153c
--- /dev/null
+++ b/drivers/macintosh/imstt.c
@@ -0,0 +1,506 @@
+/*
+ * imstt.c: Console support for PowerMac "imstt" display adaptor.
+ *
+ * Copyright (C) 1997 Sigurdur Asgeirsson
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/vc_ioctl.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/nvram.h>
+#include <asm/prom.h>
+#include <asm/io.h>
+#include <asm/pci-bridge.h>
+#include <linux/selection.h>
+#include <linux/vt_kern.h>
+#include "pmac-cons.h"
+#include "imstt.h"
+
+enum
+{
+ S1SA = 0, /* 0x00 */
+ S2SA = 1, /* 0x04 */
+ SP = 2, /* 0x08 */
+ DSA = 3, /* 0x0C */
+ CNT = 4, /* 0x10 */
+ DP_OCTRL = 5, /* 0x14 */
+ BLTCTL = 10, /* 0x28 */
+
+ // Scan Timing Generator Registers
+ HES = 12, /* 0x30 */
+ HEB = 13, /* 0x34 */
+ HSB = 14, /* 0x38 */
+ HT = 15, /* 0x3C */
+ VES = 16, /* 0x40 */
+ VEB = 17, /* 0x44 */
+ VSB = 18, /* 0x48 */
+ VT = 19, /* 0x4C */
+ HCIV = 20, /* 0x50 */
+ VCIV = 21, /* 0x54 */
+ TCDR = 22, /* 0x58 */
+ VIL = 23, /* 0x5C */
+ STGCTL = 24, /* 0x60 */
+
+ // Screen Refresh Generator Registers
+ SSR = 25, /* 0x64 */
+ HRIR = 26, /* 0x68 */
+ SPR = 27, /* 0x6C */
+ CMR = 28, /* 0x70 */
+ SRGCTL = 29, /* 0x74 */
+
+ // RAM Refresh Generator Registers
+ RRCIV = 30, /* 0x78 */
+ RRSC = 31, /* 0x7C */
+ RRCR = 34, /* 0x88 */
+
+ // System Registers
+ GIOE = 32, /* 0x80 */
+ GIO = 33, /* 0x84 */
+ SCR = 35, /* 0x8C */
+ SSTATUS = 36, /* 0x90 */
+ PRC = 37, /* 0x94 */
+
+#if 0
+ // PCI Registers
+ DVID = 0x00000000L,
+ SC = 0x00000004L,
+ CCR = 0x00000008L,
+ OG = 0x0000000CL,
+ BARM = 0x00000010L,
+ BARER = 0x00000030L,
+#endif
+};
+
+enum
+{
+ PADDRW = 0x00,
+ PDATA = 0x04,
+ PPMASK = 0x08,
+ PADDRR = 0x0C,
+ PIDXLO = 0x10,
+ PIDXHI = 0x14,
+ PIDXDATA = 0x18,
+ PIDXCTL = 0x1C,
+
+ PPIXREP = 0x0A,
+ PM0 = 0x20,
+ PN0 = 0x21,
+ PP0 = 0x22,
+ PC0 = 0x23
+};
+
+struct initvalues
+{
+ unsigned char addr, value;
+};
+
+static struct initvalues initregs[] =
+{
+ { 0x02, 0x21 }, /* (0x01) Miscellaneous Clock Control */
+ { 0x03, 0x00 }, /* (0x00) Sync Control */
+ { 0x04, 0x00 }, /* (0x00) Horizontal Sync Position */
+ { 0x05, 0x00 }, /* (0x00) Power Management */
+ { 0x06, 0x0B }, /* (0x02) DAC Operation */
+ { 0x07, 0x00 }, /* (0x00) Palette Control */
+ { 0x08, 0x01 }, /* (0x01) System Clock Control */
+ { 0x0B, 0x00 }, /* (U) 8 BPP Control */
+ { 0x0C, 0xC4 }, /* (U) 16 BPP Control */
+ { 0x0D, 0x00 }, /* (U) 24 BPP Packed Control */
+ { 0x0E, 0x03 }, /* (U) 32 BPP Control */
+ { 0x10, 0x05 }, /* (0x00) Pixel PLL Control 1 */
+ { 0x11, 0x00 }, /* (0x00) Pixel PLL Control 2 */
+ { 0x15, 0x08 }, /* (0x08) SYSCLK N (System PLL Reference Divider) */
+ { 0x16, 0x57 }, /* (0x41) SYSCLK M (System PLL VCO Divider) */
+ { 0x17, 0x00 }, /* (U) SYSCLK P */
+ { 0x18, 0x00 }, /* (U) SYSCLK C */
+ { 0x30, 0x00 }, /* (0x00) Cursor Control */
+ { 0x60, 0xFF }, /* (U) Border Color Red */
+ { 0x61, 0xFF }, /* (U) Border Color Green */
+ { 0x62, 0xFF }, /* (U) Border Color Blue */
+ { 0x70, 0x01 }, /* (0x00) Miscellaneous Control 1 */
+ { 0x71, 0x45 }, /* (0x00) Miscellaneous Control 2 */
+ { 0x72, 0x00 }, /* (0x00) Miscellaneous Control 3 */
+ { 0x78, 0x00 }, /* (0x00) Key Control/DB Operation */
+};
+
+static void set_imstt_clock(unsigned char *params);
+static int read_imstt_sense(void);
+static int imstt_vram_reqd(int vmode, int cmode);
+
+static int total_vram = 2 * 1024 * 1024; /* total amount of video memory, bytes */
+static unsigned char *frame_buffer;
+static unsigned char *cmap_regs;
+static unsigned *dc_regs;
+
+
+/*
+ * Register initialization tables for the imstt display.
+ *
+ * Dot clock rate is 20MHz * (m + 1) / ((n + 1) * (p ? 2 * p : 1)
+ * where m = clk[0], n = clk[1], p = clk[2]
+ * clk[3] is c, charge pump bias which depends on the VCO frequency
+ */
+struct imstt_regvals {
+ unsigned short cfg[8];
+ unsigned char clk[4];
+ unsigned long pitch[3];
+} imsttmode;
+
+/* Register values for 1024x768, 75Hz mode (17) */
+static struct imstt_regvals imstt_reg_init_17 = {
+ { 0x0A, 0x1C, 0x9C, 0xA6, 0x0003, 0x0020, 0x0320, 0x0323 },
+ { 0x07, 0x00, 0x01, 0x02 },
+ { 0x0400, 0x0800, 0x1000 }
+};
+
+/* Register values for 832x624, 75Hz mode (13) */
+static struct imstt_regvals imstt_reg_init_13 = {
+ { 0x05, 0x20, 0x88, 0x90, 0x0003, 0x0028, 0x0298, 0x029B },
+ { 0x3E, 0x0A, 0x01, 0x02 },
+ { 832, 832 * 2, 832 * 4 }
+};
+
+/* Register values for 640x480, 67Hz mode (6) */
+static struct imstt_regvals imstt_reg_init_6 = {
+ { 0x08, 0x12, 0x62, 0x6C, 0x0003, 0x002A, 0x020A, 0x020C },
+ { 0x78, 0x13, 0x02, 0x02 },
+ { 640, 640 * 2, 640 * 4 }
+};
+
+static struct imstt_regvals *imstt_reg_init[20] = {
+ NULL, NULL, NULL, NULL,
+ &imstt_reg_init_6, // fake'm out
+ &imstt_reg_init_6,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ &imstt_reg_init_13,
+ NULL, NULL, NULL,
+ &imstt_reg_init_17,
+ NULL, NULL, NULL
+};
+
+/*
+ * Get the monitor sense value.
+ * Note that this can be called before calibrate_delay,
+ * so we can't use udelay.
+ */
+static int
+read_imstt_sense()
+{
+#if 0
+ int sense;
+ unsigned gio, gioe;
+
+ gio = ld_le32(dc_regs + GIO) & ~0x0038;
+ gioe = ld_le32(dc_
+
+ out_le32(dc_regs + GIOE, reg); /* drive all lines high */
+ __delay(200);
+ out_le32(dc_regs + GIOE, 077); /* turn off drivers */
+ __delay(2000);
+ sense = (in_le32(dc_regs + GIOE) & 0x1c0) << 2;
+
+ /* drive each sense line low in turn and collect the other 2 */
+ out_le32(dc_regs + GIOE, 033); /* drive A low */
+ __delay(2000);
+ sense |= (in_le32(dc_regs + GIOE) & 0xc0) >> 2;
+ out_le32(dc_regs + GIOE, 055); /* drive B low */
+ __delay(2000);
+ sense |= ((in_le32(dc_regs + GIOE) & 0x100) >> 5)
+ | ((in_le32(dc_regs + GIOE) & 0x40) >> 4);
+ out_le32(dc_regs + GIOE, 066); /* drive C low */
+ __delay(2000);
+ sense |= (in_le32(dc_regs + GIOE) & 0x180) >> 7;
+
+ out_le32(dc_regs + GIOE, 077); /* turn off drivers */
+ return sense;
+#else
+ return 0;
+#endif
+}
+
+static inline int imstt_vram_reqd(int vmode, int cmode)
+{
+ return vmode_attrs[vmode-1].vres
+ * imstt_reg_init[vmode-1]->pitch[cmode];
+}
+
+void
+map_imstt_display(struct device_node *dp)
+{
+ int i, sense;
+ unsigned long addr, size, tmp;
+ unsigned char bus, devfn;
+ unsigned short cmd;
+
+ if (dp->next != 0)
+ printk("Warning: only using first imstt display device\n");
+
+#if 1
+ printk("pmac_display_init: node = %p, addrs =", dp->node);
+ for (i = 0; i < dp->n_addrs; ++i)
+ printk(" %x(%x)", dp->addrs[i].address, dp->addrs[i].size);
+ printk(", intrs =");
+ for (i = 0; i < dp->n_intrs; ++i)
+ printk(" %x", dp->intrs[i]);
+ printk("\n");
+#endif
+
+ /* Map in frame buffer and registers */
+ for (i = 0; i < dp->n_addrs; ++i) {
+ addr = dp->addrs[i].address;
+ size = dp->addrs[i].size;
+ if (size >= 0x02000000) {
+ frame_buffer = ioremap(addr, size);
+ dc_regs = (unsigned*)(frame_buffer + 0x00800000);
+ cmap_regs = (unsigned char*)(frame_buffer + 0x00840000);
+
+ printk("mapped frame_buffer=%x(%x)", (unsigned)frame_buffer, (unsigned)size);
+ printk(" dc_regs=%x, cmap_regs=%x\n", (unsigned)dc_regs, (unsigned)cmap_regs);
+ }
+ }
+
+ /* enable memory-space accesses using config-space command register */
+ if (pci_device_loc(dp, &bus, &devfn) == 0) {
+ pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
+
+ printk("command word 0x%04X\n", cmd);
+
+ if (cmd != 0xffff) {
+ cmd |= PCI_COMMAND_MEMORY;
+ pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
+ }
+ }
+ else
+ printk("unable to find pci device\n");
+
+ tmp = in_le32(dc_regs + SSTATUS);
+ printk("chip version %ld, ", (tmp & 0x0F00) >> 8);
+
+ tmp = in_le32(dc_regs + PRC);
+ total_vram = (tmp & 0x0004) ? 0x000400000L : 0x000200000L;
+ printk("VRAM size %ldM\n", total_vram / 0x000100000L);
+
+ sense = read_imstt_sense();
+ printk("Monitor sense value = 0x%x, ", sense);
+#if 0
+ if (video_mode == VMODE_NVRAM) {
+ video_mode = nvram_read_byte(NV_VMODE);
+ if (video_mode <= 0 || video_mode > VMODE_MAX
+ || imstt_reg_init[video_mode-1] == 0)
+ video_mode = VMODE_CHOOSE;
+ }
+ if (video_mode == VMODE_CHOOSE)
+ video_mode = map_monitor_sense(sense);
+ if (imstt_reg_init[video_mode-1] == 0)
+ video_mode = VMODE_640_480_67;
+
+ /*
+ * Reduce the pixel size if we don't have enough VRAM.
+ */
+ if (color_mode == CMODE_NVRAM)
+ color_mode = nvram_read_byte(NV_CMODE);
+ if (color_mode < CMODE_8 || color_mode > CMODE_32)
+ color_mode = CMODE_8;
+ while (color_mode > CMODE_8
+ && imstt_vram_reqd(video_mode, color_mode) > total_vram)
+ --color_mode;
+
+#endif
+
+ video_mode = VMODE_640_480_67;
+ color_mode = CMODE_8;
+}
+
+static void
+set_imstt_clock(unsigned char *params)
+{
+ cmap_regs[PIDXHI] = 0; eieio();
+ cmap_regs[PIDXLO] = PM0; eieio();
+ cmap_regs[PIDXDATA] = params[0]; eieio();
+
+ cmap_regs[PIDXLO] = PN0; eieio();
+ cmap_regs[PIDXDATA] = params[1]; eieio();
+
+ cmap_regs[PIDXLO] = PP0; eieio();
+ cmap_regs[PIDXDATA] = params[2]; eieio();
+
+ cmap_regs[PIDXLO] = PC0; eieio();
+ cmap_regs[PIDXDATA] = params[3]; eieio();
+}
+
+void
+imstt_init()
+{
+ int i, yoff, hres;
+ unsigned long ctl, pitch, tmp;
+ unsigned char pformat;
+ unsigned *p;
+ struct imstt_regvals *init;
+
+ if (video_mode <= 0 || video_mode > VMODE_MAX
+ || (init = imstt_reg_init[video_mode-1]) == 0)
+ panic("imstt: display mode %d not supported", video_mode);
+
+ n_scanlines = vmode_attrs[video_mode-1].vres;
+ hres = vmode_attrs[video_mode-1].hres;
+ pixel_size = 1 << color_mode;
+ line_pitch = init->pitch[color_mode];
+ row_pitch = line_pitch * 16;
+
+ /* initialize the card */
+ tmp = in_le32(dc_regs + STGCTL);
+ out_le32(dc_regs + STGCTL, tmp & ~0x1);
+#if 0
+ out_le32(dc_regs + SCR, 0);
+#endif
+
+ cmap_regs[PPMASK] = 0xFF;
+ /* set default values for DAC registers */
+ cmap_regs[PIDXHI] = 0; eieio();
+ for(i = 0; i < sizeof(initregs) / sizeof(*initregs); i++) {
+ cmap_regs[PIDXLO] = initregs[i].addr; eieio();
+ cmap_regs[PIDXDATA] = initregs[i].value; eieio();
+ }
+ set_imstt_clock(init->clk);
+
+ switch(color_mode) {
+ case CMODE_32:
+ ctl = 0x17b5;
+ pitch = init->pitch[2] / 4;
+ pformat = 0x06;
+ break;
+ case CMODE_16:
+ ctl = 0x17b3;
+ pitch = init->pitch[1] / 4;
+ pformat = 0x04;
+ break;
+ case CMODE_8:
+ default:
+ ctl = 0x17b1;
+ pitch = init->pitch[0] / 4;
+ pformat = 0x03;
+ break;
+ }
+
+ out_le32(&dc_regs[HES], init->cfg[0]);
+ out_le32(&dc_regs[HEB], init->cfg[1]);
+ out_le32(&dc_regs[HSB], init->cfg[2]);
+ out_le32(&dc_regs[HT], init->cfg[3]);
+ out_le32(&dc_regs[VES], init->cfg[4]);
+ out_le32(&dc_regs[VEB], init->cfg[5]);
+ out_le32(&dc_regs[VSB], init->cfg[6]);
+ out_le32(&dc_regs[VT], init->cfg[7]);
+ out_le32(&dc_regs[HCIV], 1);
+ out_le32(&dc_regs[VCIV], 1);
+ out_le32(&dc_regs[TCDR], 4);
+ out_le32(&dc_regs[VIL], 0);
+
+ out_le32(&dc_regs[SSR], 0);
+ out_le32(&dc_regs[HRIR], 0x0200);
+ out_le32(&dc_regs[CMR], 0x01FF);
+ out_le32(&dc_regs[SRGCTL], 0x0003);
+ if(total_vram == 0x000200000)
+ out_le32(&dc_regs[SCR], 0x0059D);
+ else {
+ pitch /= 2;
+ out_le32(&dc_regs[SCR], 0x00D0DC);
+ }
+
+ out_le32(&dc_regs[SPR], pitch);
+
+ cmap_regs[PIDXLO] = PPIXREP; eieio();
+ cmap_regs[PIDXDATA] = pformat; eieio();
+
+
+ pmac_init_palette(); /* Initialize colormap */
+
+ out_le32(&dc_regs[STGCTL], ctl);
+
+ yoff = (n_scanlines % 16) / 2;
+ fb_start = frame_buffer + yoff * line_pitch;
+
+ /* Clear screen */
+ p = (unsigned *)frame_buffer;
+ for (i = n_scanlines * line_pitch / sizeof(unsigned); i != 0; --i)
+ *p++ = 0;
+
+ display_info.height = n_scanlines;
+ display_info.width = hres;
+ display_info.depth = pixel_size * 8;
+ display_info.pitch = line_pitch;
+ display_info.mode = video_mode;
+ strncpy(display_info.name, "IMS,tt128mb", sizeof(display_info.name));
+ display_info.fb_address = (unsigned long) frame_buffer;
+ display_info.cmap_adr_address = (unsigned long) &cmap_regs[PADDRW];
+ display_info.cmap_data_address = (unsigned long) &cmap_regs[PDATA];
+ display_info.disp_reg_address = (unsigned long) NULL;
+}
+
+int
+imstt_setmode(struct vc_mode *mode, int doit)
+{
+ int cmode;
+
+ if (mode->mode <= 0 || mode->mode > VMODE_MAX
+ || imstt_reg_init[mode->mode-1] == 0)
+ return -EINVAL;
+ switch (mode->depth) {
+ case 24:
+ case 32:
+ cmode = CMODE_32;
+ break;
+ case 16:
+ cmode = CMODE_16;
+ break;
+ case 8:
+ case 0: /* (default) */
+ cmode = CMODE_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (imstt_vram_reqd(mode->mode, cmode) > total_vram)
+ return -EINVAL;
+ if (doit) {
+ video_mode = mode->mode;
+ color_mode = cmode;
+ imstt_init();
+ }
+ return 0;
+}
+
+void
+imstt_set_palette(unsigned char red[], unsigned char green[],
+ unsigned char blue[], int index, int ncolors)
+{
+ int i;
+
+ for (i = 0; i < ncolors; ++i) {
+ cmap_regs[PADDRW] = index + i; eieio();
+ cmap_regs[PDATA] = red[i]; eieio();
+ cmap_regs[PDATA] = green[i]; eieio();
+ cmap_regs[PDATA] = blue[i]; eieio();
+ }
+}
+
+void
+imstt_set_blanking(int blank_mode)
+{
+ long ctrl;
+
+ ctrl = ld_le32(dc_regs + STGCTL) | 0x0030;
+ if (blank_mode & VESA_VSYNC_SUSPEND)
+ ctrl &= ~0x0020;
+ if (blank_mode & VESA_HSYNC_SUSPEND)
+ ctrl &= ~0x0010;
+ out_le32(dc_regs + STGCTL, ctrl);
+}
diff --git a/drivers/macintosh/imstt.h b/drivers/macintosh/imstt.h
new file mode 100644
index 000000000..853e8f00d
--- /dev/null
+++ b/drivers/macintosh/imstt.h
@@ -0,0 +1,18 @@
+/*
+ * Exported procedures for the "control" display driver on PowerMacs.
+ *
+ * Copyright (C) 1997 Paul Mackerras.
+ *
+ * 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.
+ */
+
+extern void map_imstt_display(struct device_node *);
+extern void imstt_init(void);
+extern int imstt_setmode(struct vc_mode *mode, int doit);
+extern void imstt_set_palette(unsigned char red[], unsigned char green[],
+ unsigned char blue[], int index, int ncolors);
+extern void imstt_set_blanking(int blank_mode);
+
diff --git a/drivers/macintosh/mac_keyb.c b/drivers/macintosh/mac_keyb.c
new file mode 100644
index 000000000..fbb39d76a
--- /dev/null
+++ b/drivers/macintosh/mac_keyb.c
@@ -0,0 +1,343 @@
+/*
+ * drivers/char/mac_keyb.c
+ *
+ * Keyboard driver for Power Macintosh computers.
+ *
+ * Adapted from drivers/char/keyboard.c by Paul Mackerras
+ * (see that file for its authors and contributors).
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/mm.h>
+#include <linux/signal.h>
+#include <linux/ioport.h>
+
+#include <asm/keyboard.h>
+#include <asm/bitops.h>
+#include <asm/cuda.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/kbd_ll.h>
+
+#define KEYB_KEYREG 0 /* register # for key up/down data */
+#define KEYB_LEDREG 2 /* register # for leds on ADB keyboard */
+#define MOUSE_DATAREG 0 /* reg# for movement/button codes from mouse */
+
+unsigned char kbd_read_mask = 0; /* XXX */
+
+static void kbd_repeat(unsigned long);
+static struct timer_list repeat_timer = { NULL, NULL, 0, 0, kbd_repeat };
+static int last_keycode;
+
+static void keyboard_input(unsigned char *, int, struct pt_regs *);
+static void input_keycode(int, int);
+static void leds_done(struct cuda_request *);
+
+extern struct kbd_struct kbd_table[];
+
+extern void handle_scancode(unsigned char);
+extern void put_queue(int);
+
+/* this map indicates which keys shouldn't autorepeat. */
+static unsigned char dont_repeat[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* esc...option */
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* num lock */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, /* scroll lock */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+int mackbd_setkeycode(unsigned int scancode, unsigned int keycode)
+{
+ return -EINVAL;
+}
+
+int mackbd_getkeycode(unsigned int scancode)
+{
+ return -EINVAL;
+}
+
+int mackbd_pretranslate(unsigned char scancode, char raw_mode)
+{
+ return 1;
+}
+
+int mackbd_translate(unsigned char keycode, unsigned char *keycodep,
+ char raw_mode)
+{
+ if (!raw_mode) {
+ /*
+ * Convert R-shift/control/option to L version.
+ * Remap keycode 0 (A) to the unused keycode 0x5a.
+ * Other parts of the system assume 0 is not a valid keycode.
+ */
+ switch (keycode) {
+ case 0x7b: keycode = 0x38; break; /* R-shift */
+ case 0x7c: keycode = 0x3a; break; /* R-option */
+ case 0x7d: keycode = 0x36; break; /* R-control */
+ case 0: keycode = 0x5a; break; /* A */
+ }
+ }
+ *keycodep = keycode;
+ return 1;
+}
+
+int mackbd_unexpected_up(unsigned char keycode)
+{
+ return 0x80;
+}
+
+static void
+keyboard_input(unsigned char *data, int nb, struct pt_regs *regs)
+{
+ /* first check this is from register 0 */
+ if (nb != 5 || (data[2] & 3) != KEYB_KEYREG)
+ return; /* ignore it */
+ kbd_pt_regs = regs;
+ input_keycode(data[3], 0);
+ if (!(data[4] == 0xff || (data[4] == 0x7f && data[3] == 0x7f)))
+ input_keycode(data[4], 0);
+}
+
+static void
+input_keycode(int keycode, int repeat)
+{
+ struct kbd_struct *kbd;
+ int up_flag;
+
+ kbd = kbd_table + fg_console;
+ up_flag = (keycode & 0x80);
+ keycode &= 0x7f;
+ if (!repeat)
+ del_timer(&repeat_timer);
+
+ if (kbd->kbdmode != VC_RAW) {
+ if (!up_flag && !dont_repeat[keycode]) {
+ last_keycode = keycode;
+ repeat_timer.expires = jiffies + (repeat? HZ/15: HZ/2);
+ add_timer(&repeat_timer);
+ }
+
+ /*
+ * XXX fix caps-lock behaviour by turning the key-up
+ * transition into a key-down transition.
+ */
+ if (keycode == 0x39 && up_flag && vc_kbd_led(kbd, VC_CAPSLOCK))
+ up_flag = 0;
+ }
+
+ handle_scancode(keycode + up_flag);
+}
+
+static void
+kbd_repeat(unsigned long xxx)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ input_keycode(last_keycode, 1);
+ restore_flags(flags);
+}
+
+static void
+mouse_input(unsigned char *data, int nb, struct pt_regs *regs)
+{
+ /* [ACA:23-Mar-97] Three button mouse support. This is designed to
+ function with MkLinux DR-2.1 style X servers. It only works with
+ three-button mice that conform to Apple's multi-button mouse
+ protocol. */
+
+ /*
+ The X server for MkLinux DR2.1 uses the following unused keycodes to
+ read the mouse:
+
+ 0x7e This indicates that the next two keycodes should be interpreted
+ as mouse information. The first following byte's high bit
+ represents the state of the left button. The lower seven bits
+ represent the x-axis acceleration. The lower seven bits of the
+ second byte represent y-axis acceleration.
+
+ 0x3f The x server interprets this keycode as a middle button
+ release.
+
+ 0xbf The x server interprets this keycode as a middle button
+ depress.
+
+ 0x40 The x server interprets this keycode as a right button
+ release.
+
+ 0xc0 The x server interprets this keycode as a right button
+ depress.
+
+ NOTES: There should be a better way of handling mice in the X server.
+ The MOUSE_ESCAPE code (0x7e) should be followed by three bytes instead
+ of two. The three mouse buttons should then, in the X server, be read
+ as the high-bits of all three bytes. The x and y motions can still be
+ in the first two bytes. Maybe I'll do this...
+ */
+
+ /*
+ Handler 4 -- Apple Extended mouse protocol.
+
+ For Apple's 3-button mouse protocol the data array will contain the
+ following values:
+
+ BITS COMMENTS
+ data[0] = 0000 0000 ADB packet identifer.
+ data[1] = 0100 0000 Extended protocol register.
+ Bits 6-7 are the device id, which should be 1.
+ Bits 4-5 are resolution which is in "units/inch".
+ The Logitech MouseMan returns these bits clear but it has
+ 200/300cpi resolution.
+ Bits 0-3 are unique vendor id.
+ data[2] = 0011 1100 Bits 0-1 should be zero for a mouse device.
+ Bits 2-3 should be 8 + 4.
+ Bits 4-7 should be 3 for a mouse device.
+ data[3] = bxxx xxxx Left button and x-axis motion.
+ data[4] = byyy yyyy Second button and y-axis motion.
+ data[5] = byyy bxxx Third button and fourth button. Y is additional
+ high bits of y-axis motion. XY is additional
+ high bits of x-axis motion.
+
+ NOTE: data[0] and data[2] are confirmed by the parent function and
+ need not be checked here.
+ */
+
+ /*
+ Handler 1 -- 100cpi original Apple mouse protocol.
+ Handler 2 -- 200cpi original Apple mouse protocol.
+
+ For Apple's standard one-button mouse protocol the data array will
+ contain the following values:
+
+ BITS COMMENTS
+ data[0] = 0000 0000 ADB packet identifer.
+ data[1] = ???? ???? (?)
+ data[2] = ???? ??00 Bits 0-1 should be zero for a mouse device.
+ data[3] = bxxx xxxx First button and x-axis motion.
+ data[4] = byyy yyyy Second button and y-axis motion.
+
+ NOTE: data[0] is confirmed by the parent function and need not be
+ checked here.
+ */
+ struct kbd_struct *kbd;
+
+ kbd = kbd_table + fg_console;
+
+ /* Only send mouse codes when keyboard is in raw mode. */
+ if (kbd->kbdmode == VC_RAW) {
+ static unsigned char uch_ButtonStateSecond = 0;
+ unsigned char uchButtonSecond;
+
+ /* Send first button, second button and movement. */
+ put_queue( 0x7e );
+ put_queue( data[3] );
+ put_queue( data[4] );
+
+ /* [ACA: Are there any two-button ADB mice that use handler 1 or 2?] */
+
+ /* Store the button state. */
+ uchButtonSecond = (data[4] & 0x80);
+
+ /* Send second button. */
+ if (uchButtonSecond != uch_ButtonStateSecond) {
+ put_queue( 0x3f | uchButtonSecond );
+ uch_ButtonStateSecond = uchButtonSecond;
+ }
+
+ /* Macintosh 3-button mouse (handler 4). */
+ if ((nb == 6) && (data[1] & 0x40)) {
+ static unsigned char uch_ButtonStateThird = 0;
+ unsigned char uchButtonThird;
+
+ /* Store the button state for speed. */
+ uchButtonThird = (data[5] & 0x80);
+
+ /* Send third button. */
+ if (uchButtonThird != uch_ButtonStateThird) {
+ put_queue( 0x40 | uchButtonThird );
+ uch_ButtonStateThird = uchButtonThird;
+ }
+ }
+ }
+}
+
+/* Map led flags as defined in kbd_kern.h to bits for Apple keyboard. */
+static unsigned char mac_ledmap[8] = {
+ 0, /* none */
+ 4, /* scroll lock */
+ 1, /* num lock */
+ 5, /* scroll + num lock */
+ 2, /* caps lock */
+ 6, /* caps + scroll lock */
+ 3, /* caps + num lock */
+ 7, /* caps + num + scroll lock */
+};
+
+static struct cuda_request led_request;
+static int leds_pending;
+
+void mackbd_leds(unsigned char leds)
+{
+ if (led_request.got_reply) {
+ cuda_request(&led_request, leds_done, 4, ADB_PACKET,
+ ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG),
+ 0xff, ~mac_ledmap[leds]);
+ } else
+ leds_pending = leds | 0x100;
+}
+
+static void leds_done(struct cuda_request *req)
+{
+ int leds;
+
+ if (leds_pending) {
+ leds = leds_pending & 0xff;
+ leds_pending = 0;
+ mackbd_leds(leds);
+ }
+}
+
+void mackbd_init_hw(void)
+{
+ struct cuda_request req;
+
+ adb_register(ADB_KEYBOARD, keyboard_input);
+ adb_register(ADB_MOUSE, mouse_input);
+
+ /* turn on ADB auto-polling in the CUDA */
+ cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1);
+ while (!req.got_reply)
+ cuda_poll();
+
+ /* turn off all leds */
+ cuda_request(&req, NULL, 4, ADB_PACKET,
+ ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG), 0xff, 0xff);
+ while (!req.got_reply)
+ cuda_poll();
+
+ /* get the keyboard to send separate codes for
+ left and right shift, control, option keys. */
+ cuda_request(&req, NULL, 4, ADB_PACKET,
+ ADB_WRITEREG(ADB_KEYBOARD, 3), 0, 3);
+ while (!req.got_reply)
+ cuda_poll();
+
+ led_request.got_reply = 1;
+
+ /* Try to switch the mouse (id 3) to handler 4, for three-button
+ mode. (0x20 is Service Request Enable, 0x03 is Device ID). */
+ cuda_request(&req, NULL, 4, ADB_PACKET,
+ ADB_WRITEREG(ADB_MOUSE, 3), 0x23, 4 );
+ while (!req.got_reply)
+ cuda_poll();
+}
+
diff --git a/drivers/macintosh/mackeymap.c b/drivers/macintosh/mackeymap.c
new file mode 100644
index 000000000..3f1eedc97
--- /dev/null
+++ b/drivers/macintosh/mackeymap.c
@@ -0,0 +1,262 @@
+/* Do not edit this file! It was automatically generated by */
+/* loadkeys --mktable defkeymap.map > defkeymap.c */
+
+#include <linux/types.h>
+#include <linux/keyboard.h>
+#include <linux/kd.h>
+
+u_short plain_map[NR_KEYS] = {
+ 0xf200, 0xfb73, 0xfb64, 0xfb66, 0xfb68, 0xfb67, 0xfb7a, 0xfb78,
+ 0xfb63, 0xfb76, 0xf200, 0xfb62, 0xfb71, 0xfb77, 0xfb65, 0xfb72,
+ 0xfb79, 0xfb74, 0xf031, 0xf032, 0xf033, 0xf034, 0xf036, 0xf035,
+ 0xf03d, 0xf039, 0xf037, 0xf02d, 0xf038, 0xf030, 0xf05d, 0xfb6f,
+ 0xfb75, 0xf05b, 0xfb69, 0xfb70, 0xf201, 0xfb6c, 0xfb6a, 0xf027,
+ 0xfb6b, 0xf03b, 0xf05c, 0xf02c, 0xf02f, 0xfb6e, 0xfb6d, 0xf02e,
+ 0xf009, 0xf020, 0xf060, 0xf07f, 0xf200, 0xf01b, 0xf702, 0xf703,
+ 0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
+ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+ 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+ 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
+ 0xf306, 0xf307, 0xfb61, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
+ 0xf104, 0xf105, 0xf106, 0xf102, 0xf107, 0xf108, 0xf200, 0xf10a,
+ 0xf200, 0xf10c, 0xf200, 0xf209, 0xf200, 0xf109, 0xf200, 0xf10b,
+ 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf103, 0xf117,
+ 0xf101, 0xf119, 0xf100, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short shift_map[NR_KEYS] = {
+ 0xf200, 0xfb53, 0xfb44, 0xfb46, 0xfb48, 0xfb47, 0xfb5a, 0xfb58,
+ 0xfb43, 0xfb56, 0xf200, 0xfb42, 0xfb51, 0xfb57, 0xfb45, 0xfb52,
+ 0xfb59, 0xfb54, 0xf021, 0xf040, 0xf023, 0xf024, 0xf05e, 0xf025,
+ 0xf02b, 0xf028, 0xf026, 0xf05f, 0xf02a, 0xf029, 0xf07d, 0xfb4f,
+ 0xfb55, 0xf07b, 0xfb49, 0xfb50, 0xf201, 0xfb4c, 0xfb4a, 0xf022,
+ 0xfb4b, 0xf03a, 0xf07c, 0xf03c, 0xf03f, 0xfb4e, 0xfb4d, 0xf03e,
+ 0xf009, 0xf020, 0xf07e, 0xf07f, 0xf200, 0xf01b, 0xf702, 0xf703,
+ 0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
+ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+ 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+ 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
+ 0xf306, 0xf307, 0xfb41, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
+ 0xf10e, 0xf10f, 0xf110, 0xf10c, 0xf111, 0xf112, 0xf200, 0xf10a,
+ 0xf200, 0xf10c, 0xf200, 0xf203, 0xf200, 0xf113, 0xf200, 0xf10b,
+ 0xf200, 0xf11d, 0xf115, 0xf114, 0xf20b, 0xf116, 0xf10d, 0xf117,
+ 0xf10b, 0xf20a, 0xf10a, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short altgr_map[NR_KEYS] = {
+ 0xf200, 0xfb73, 0xf917, 0xf919, 0xfb68, 0xfb67, 0xfb7a, 0xfb78,
+ 0xf916, 0xfb76, 0xf200, 0xf915, 0xfb71, 0xfb77, 0xf918, 0xfb72,
+ 0xfb79, 0xfb74, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200,
+ 0xf200, 0xf05d, 0xf07b, 0xf05c, 0xf05b, 0xf07d, 0xf07e, 0xfb6f,
+ 0xfb75, 0xf200, 0xfb69, 0xfb70, 0xf201, 0xfb6c, 0xfb6a, 0xf200,
+ 0xfb6b, 0xf200, 0xf200, 0xf200, 0xf200, 0xfb6e, 0xfb6d, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf703,
+ 0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
+ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+ 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+ 0xf200, 0xf200, 0xf90a, 0xf90b, 0xf90c, 0xf90d, 0xf90e, 0xf90f,
+ 0xf910, 0xf911, 0xf914, 0xf912, 0xf913, 0xf200, 0xf200, 0xf200,
+ 0xf510, 0xf511, 0xf512, 0xf50e, 0xf513, 0xf514, 0xf200, 0xf516,
+ 0xf200, 0xf10c, 0xf200, 0xf202, 0xf200, 0xf515, 0xf200, 0xf517,
+ 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf50f, 0xf117,
+ 0xf50d, 0xf119, 0xf50c, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short ctrl_map[NR_KEYS] = {
+ 0xf200, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018,
+ 0xf003, 0xf016, 0xf200, 0xf002, 0xf011, 0xf017, 0xf005, 0xf012,
+ 0xf019, 0xf014, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01e, 0xf01d,
+ 0xf200, 0xf200, 0xf01f, 0xf01f, 0xf07f, 0xf200, 0xf01d, 0xf00f,
+ 0xf015, 0xf01b, 0xf009, 0xf010, 0xf201, 0xf00c, 0xf00a, 0xf007,
+ 0xf00b, 0xf200, 0xf01c, 0xf200, 0xf07f, 0xf00e, 0xf00d, 0xf20e,
+ 0xf200, 0xf000, 0xf000, 0xf008, 0xf200, 0xf200, 0xf702, 0xf703,
+ 0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
+ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+ 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+ 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
+ 0xf306, 0xf307, 0xf001, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
+ 0xf104, 0xf105, 0xf106, 0xf102, 0xf107, 0xf108, 0xf200, 0xf10a,
+ 0xf200, 0xf10c, 0xf200, 0xf204, 0xf200, 0xf109, 0xf200, 0xf10b,
+ 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf103, 0xf117,
+ 0xf101, 0xf119, 0xf100, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short shift_ctrl_map[NR_KEYS] = {
+ 0xf200, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018,
+ 0xf003, 0xf016, 0xf200, 0xf002, 0xf011, 0xf017, 0xf005, 0xf012,
+ 0xf019, 0xf014, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, 0xf00f,
+ 0xf015, 0xf200, 0xf009, 0xf010, 0xf201, 0xf00c, 0xf00a, 0xf200,
+ 0xf00b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf00e, 0xf00d, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf703,
+ 0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
+ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+ 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+ 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
+ 0xf306, 0xf307, 0xf001, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf10c, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf200, 0xf117,
+ 0xf200, 0xf119, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf20c,
+};
+
+u_short alt_map[NR_KEYS] = {
+ 0xf200, 0xf873, 0xf864, 0xf866, 0xf868, 0xf867, 0xf87a, 0xf878,
+ 0xf863, 0xf876, 0xf200, 0xf862, 0xf871, 0xf877, 0xf865, 0xf872,
+ 0xf879, 0xf874, 0xf831, 0xf832, 0xf833, 0xf834, 0xf836, 0xf835,
+ 0xf83d, 0xf839, 0xf837, 0xf82d, 0xf838, 0xf830, 0xf85d, 0xf86f,
+ 0xf875, 0xf85b, 0xf869, 0xf870, 0xf80d, 0xf86c, 0xf86a, 0xf827,
+ 0xf86b, 0xf83b, 0xf85c, 0xf82c, 0xf82f, 0xf86e, 0xf86d, 0xf82e,
+ 0xf809, 0xf820, 0xf860, 0xf87f, 0xf200, 0xf81b, 0xf702, 0xf703,
+ 0xf700, 0xf207, 0xf701, 0xf210, 0xf211, 0xf600, 0xf603, 0xf200,
+ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+ 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+ 0xf200, 0xf200, 0xf900, 0xf901, 0xf902, 0xf903, 0xf904, 0xf905,
+ 0xf906, 0xf907, 0xf861, 0xf908, 0xf909, 0xf200, 0xf200, 0xf200,
+ 0xf504, 0xf505, 0xf506, 0xf502, 0xf507, 0xf508, 0xf200, 0xf50a,
+ 0xf200, 0xf10c, 0xf200, 0xf209, 0xf200, 0xf509, 0xf200, 0xf50b,
+ 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf503, 0xf117,
+ 0xf501, 0xf119, 0xf500, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short ctrl_alt_map[NR_KEYS] = {
+ 0xf200, 0xf813, 0xf804, 0xf806, 0xf808, 0xf807, 0xf81a, 0xf818,
+ 0xf803, 0xf816, 0xf200, 0xf802, 0xf811, 0xf817, 0xf805, 0xf812,
+ 0xf819, 0xf814, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf80f,
+ 0xf815, 0xf200, 0xf809, 0xf810, 0xf201, 0xf80c, 0xf80a, 0xf200,
+ 0xf80b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf80e, 0xf80d, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf703,
+ 0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
+ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+ 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+ 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
+ 0xf306, 0xf307, 0xf801, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
+ 0xf504, 0xf505, 0xf506, 0xf502, 0xf507, 0xf508, 0xf200, 0xf50a,
+ 0xf200, 0xf10c, 0xf200, 0xf200, 0xf200, 0xf509, 0xf200, 0xf50b,
+ 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf503, 0xf117,
+ 0xf501, 0xf119, 0xf500, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+ushort *key_maps[MAX_NR_KEYMAPS] = {
+ plain_map, shift_map, altgr_map, 0,
+ ctrl_map, shift_ctrl_map, 0, 0,
+ alt_map, 0, 0, 0,
+ ctrl_alt_map, 0
+};
+
+unsigned int keymap_count = 7;
+
+/*
+ * Philosophy: most people do not define more strings, but they who do
+ * often want quite a lot of string space. So, we statically allocate
+ * the default and allocate dynamically in chunks of 512 bytes.
+ */
+
+char func_buf[] = {
+ '\033', '[', '[', 'A', 0,
+ '\033', '[', '[', 'B', 0,
+ '\033', '[', '[', 'C', 0,
+ '\033', '[', '[', 'D', 0,
+ '\033', '[', '[', 'E', 0,
+ '\033', '[', '1', '7', '~', 0,
+ '\033', '[', '1', '8', '~', 0,
+ '\033', '[', '1', '9', '~', 0,
+ '\033', '[', '2', '0', '~', 0,
+ '\033', '[', '2', '1', '~', 0,
+ '\033', '[', '2', '3', '~', 0,
+ '\033', '[', '2', '4', '~', 0,
+ '\033', '[', '2', '5', '~', 0,
+ '\033', '[', '2', '6', '~', 0,
+ '\033', '[', '2', '8', '~', 0,
+ '\033', '[', '2', '9', '~', 0,
+ '\033', '[', '3', '1', '~', 0,
+ '\033', '[', '3', '2', '~', 0,
+ '\033', '[', '3', '3', '~', 0,
+ '\033', '[', '3', '4', '~', 0,
+ '\033', '[', '1', '~', 0,
+ '\033', '[', '2', '~', 0,
+ '\033', '[', '3', '~', 0,
+ '\033', '[', '4', '~', 0,
+ '\033', '[', '5', '~', 0,
+ '\033', '[', '6', '~', 0,
+ '\033', '[', 'M', 0,
+ '\033', '[', 'P', 0,
+};
+
+char *funcbufptr = func_buf;
+int funcbufsize = sizeof(func_buf);
+int funcbufleft = 0; /* space left */
+
+char *func_table[MAX_NR_FUNC] = {
+ func_buf + 0,
+ func_buf + 5,
+ func_buf + 10,
+ func_buf + 15,
+ func_buf + 20,
+ func_buf + 25,
+ func_buf + 31,
+ func_buf + 37,
+ func_buf + 43,
+ func_buf + 49,
+ func_buf + 55,
+ func_buf + 61,
+ func_buf + 67,
+ func_buf + 73,
+ func_buf + 79,
+ func_buf + 85,
+ func_buf + 91,
+ func_buf + 97,
+ func_buf + 103,
+ func_buf + 109,
+ func_buf + 115,
+ func_buf + 120,
+ func_buf + 125,
+ func_buf + 130,
+ func_buf + 135,
+ func_buf + 140,
+ func_buf + 145,
+ 0,
+ 0,
+ func_buf + 149,
+ 0,
+};
+
+struct kbdiacr accent_table[MAX_DIACR] = {
+ {'`', 'A', '\300'}, {'`', 'a', '\340'},
+ {'\'', 'A', '\301'}, {'\'', 'a', '\341'},
+ {'^', 'A', '\302'}, {'^', 'a', '\342'},
+ {'~', 'A', '\303'}, {'~', 'a', '\343'},
+ {'"', 'A', '\304'}, {'"', 'a', '\344'},
+ {'O', 'A', '\305'}, {'o', 'a', '\345'},
+ {'0', 'A', '\305'}, {'0', 'a', '\345'},
+ {'A', 'A', '\305'}, {'a', 'a', '\345'},
+ {'A', 'E', '\306'}, {'a', 'e', '\346'},
+ {',', 'C', '\307'}, {',', 'c', '\347'},
+ {'`', 'E', '\310'}, {'`', 'e', '\350'},
+ {'\'', 'E', '\311'}, {'\'', 'e', '\351'},
+ {'^', 'E', '\312'}, {'^', 'e', '\352'},
+ {'"', 'E', '\313'}, {'"', 'e', '\353'},
+ {'`', 'I', '\314'}, {'`', 'i', '\354'},
+ {'\'', 'I', '\315'}, {'\'', 'i', '\355'},
+ {'^', 'I', '\316'}, {'^', 'i', '\356'},
+ {'"', 'I', '\317'}, {'"', 'i', '\357'},
+ {'-', 'D', '\320'}, {'-', 'd', '\360'},
+ {'~', 'N', '\321'}, {'~', 'n', '\361'},
+ {'`', 'O', '\322'}, {'`', 'o', '\362'},
+ {'\'', 'O', '\323'}, {'\'', 'o', '\363'},
+ {'^', 'O', '\324'}, {'^', 'o', '\364'},
+ {'~', 'O', '\325'}, {'~', 'o', '\365'},
+ {'"', 'O', '\326'}, {'"', 'o', '\366'},
+ {'/', 'O', '\330'}, {'/', 'o', '\370'},
+ {'`', 'U', '\331'}, {'`', 'u', '\371'},
+ {'\'', 'U', '\332'}, {'\'', 'u', '\372'},
+ {'^', 'U', '\333'}, {'^', 'u', '\373'},
+ {'"', 'U', '\334'}, {'"', 'u', '\374'},
+ {'\'', 'Y', '\335'}, {'\'', 'y', '\375'},
+ {'T', 'H', '\336'}, {'t', 'h', '\376'},
+ {'s', 's', '\337'}, {'"', 'y', '\377'},
+ {'s', 'z', '\337'}, {'i', 'j', '\377'},
+};
+
+unsigned int accent_table_size = 68;
diff --git a/drivers/macintosh/mackeymap.map b/drivers/macintosh/mackeymap.map
new file mode 100644
index 000000000..714985e7a
--- /dev/null
+++ b/drivers/macintosh/mackeymap.map
@@ -0,0 +1,346 @@
+# Kernel keymap for Macintoshes. This uses 7 modifier combinations.
+keymaps 0-2,4-5,8,12
+# We use the Command (pretzel) key as Alt, and the Option key as AltGr.
+#
+keycode 0x00 =
+keycode 0x01 = s
+keycode 0x02 = d
+ altgr keycode 0x02 = Hex_D
+keycode 0x03 = f
+ altgr keycode 0x03 = Hex_F
+keycode 0x04 = h
+keycode 0x05 = g
+keycode 0x06 = z
+keycode 0x07 = x
+keycode 0x08 = c
+ altgr keycode 0x08 = Hex_C
+keycode 0x09 = v
+keycode 0x0a =
+keycode 0x0b = b
+ altgr keycode 0x0b = Hex_B
+keycode 0x0c = q
+keycode 0x0d = w
+keycode 0x0e = e
+ altgr keycode 0x0e = Hex_E
+keycode 0x0f = r
+keycode 0x10 = y
+keycode 0x11 = t
+keycode 0x12 = one exclam
+ alt keycode 0x12 = Meta_one
+keycode 0x13 = two at at
+ control keycode 0x13 = nul
+ shift control keycode 0x13 = nul
+ alt keycode 0x13 = Meta_two
+keycode 0x14 = three numbersign
+ control keycode 0x14 = Escape
+ alt keycode 0x14 = Meta_three
+keycode 0x15 = four dollar dollar
+ control keycode 0x15 = Control_backslash
+ alt keycode 0x15 = Meta_four
+keycode 0x16 = six asciicircum
+ control keycode 0x16 = Control_asciicircum
+ alt keycode 0x16 = Meta_six
+keycode 0x17 = five percent
+ control keycode 0x17 = Control_bracketright
+ alt keycode 0x17 = Meta_five
+keycode 0x18 = equal plus
+ alt keycode 0x18 = Meta_equal
+keycode 0x19 = nine parenleft bracketright
+ alt keycode 0x19 = Meta_nine
+keycode 0x1a = seven ampersand braceleft
+ control keycode 0x1a = Control_underscore
+ alt keycode 0x1a = Meta_seven
+keycode 0x1b = minus underscore backslash
+ control keycode 0x1b = Control_underscore
+ shift control keycode 0x1b = Control_underscore
+ alt keycode 0x1b = Meta_minus
+keycode 0x1c = eight asterisk bracketleft
+ control keycode 0x1c = Delete
+ alt keycode 0x1c = Meta_eight
+keycode 0x1d = zero parenright braceright
+ alt keycode 0x1d = Meta_zero
+keycode 0x1e = bracketright braceright asciitilde
+ control keycode 0x1e = Control_bracketright
+ alt keycode 0x1e = Meta_bracketright
+keycode 0x1f = o
+keycode 0x20 = u
+keycode 0x21 = bracketleft braceleft
+ control keycode 0x21 = Escape
+ alt keycode 0x21 = Meta_bracketleft
+keycode 0x22 = i
+keycode 0x23 = p
+keycode 0x24 = Return
+ alt keycode 0x24 = Meta_Control_m
+keycode 0x25 = l
+keycode 0x26 = j
+keycode 0x27 = apostrophe quotedbl
+ control keycode 0x27 = Control_g
+ alt keycode 0x27 = Meta_apostrophe
+keycode 0x28 = k
+keycode 0x29 = semicolon colon
+ alt keycode 0x29 = Meta_semicolon
+keycode 0x2a = backslash bar
+ control keycode 0x2a = Control_backslash
+ alt keycode 0x2a = Meta_backslash
+keycode 0x2b = comma less
+ alt keycode 0x2b = Meta_comma
+keycode 0x2c = slash question
+ control keycode 0x2c = Delete
+ alt keycode 0x2c = Meta_slash
+keycode 0x2d = n
+keycode 0x2e = m
+keycode 0x2f = period greater
+ control keycode 0x2f = Compose
+ alt keycode 0x2f = Meta_period
+keycode 0x30 = Tab Tab
+ alt keycode 0x30 = Meta_Tab
+keycode 0x31 = space space
+ control keycode 0x31 = nul
+ alt keycode 0x31 = Meta_space
+keycode 0x32 = grave asciitilde
+ control keycode 0x32 = nul
+ alt keycode 0x32 = Meta_grave
+keycode 0x33 = Delete Delete
+ control keycode 0x33 = BackSpace
+ alt keycode 0x33 = Meta_Delete
+keycode 0x34 =
+keycode 0x35 = Escape Escape
+ alt keycode 0x35 = Meta_Escape
+keycode 0x36 = Control
+keycode 0x37 = Alt
+keycode 0x38 = Shift
+keycode 0x39 = Caps_Lock
+keycode 0x3a = AltGr
+keycode 0x3b = Left
+ alt keycode 0x3b = Decr_Console
+keycode 0x3c = Right
+ alt keycode 0x3c = Incr_Console
+keycode 0x3d = Down
+keycode 0x3e = Up
+keycode 0x3f =
+keycode 0x40 =
+keycode 0x41 = KP_Period
+keycode 0x42 =
+keycode 0x43 = KP_Multiply
+keycode 0x44 =
+keycode 0x45 = KP_Add
+keycode 0x46 =
+keycode 0x47 = Num_Lock
+# shift keycode 0x47 = Bare_Num_Lock
+keycode 0x48 =
+keycode 0x49 =
+keycode 0x4a =
+keycode 0x4b = KP_Divide
+keycode 0x4c = KP_Enter
+keycode 0x4d =
+keycode 0x4e = KP_Subtract
+keycode 0x4f =
+keycode 0x50 =
+keycode 0x51 =
+#keycode 0x51 = KP_Equals
+keycode 0x52 = KP_0
+ alt keycode 0x52 = Ascii_0
+ altgr keycode 0x52 = Hex_0
+keycode 0x53 = KP_1
+ alt keycode 0x53 = Ascii_1
+ altgr keycode 0x53 = Hex_1
+keycode 0x54 = KP_2
+ alt keycode 0x54 = Ascii_2
+ altgr keycode 0x54 = Hex_2
+keycode 0x55 = KP_3
+ alt keycode 0x55 = Ascii_3
+ altgr keycode 0x55 = Hex_3
+keycode 0x56 = KP_4
+ alt keycode 0x56 = Ascii_4
+ altgr keycode 0x56 = Hex_4
+keycode 0x57 = KP_5
+ alt keycode 0x57 = Ascii_5
+ altgr keycode 0x57 = Hex_5
+keycode 0x58 = KP_6
+ alt keycode 0x58 = Ascii_6
+ altgr keycode 0x58 = Hex_6
+keycode 0x59 = KP_7
+ alt keycode 0x59 = Ascii_7
+ altgr keycode 0x59 = Hex_7
+# keycode 0 (A) is remapped to here
+keycode 0x5a = a
+ altgr keycode 0x00 = Hex_A
+keycode 0x5b = KP_8
+ alt keycode 0x5b = Ascii_8
+ altgr keycode 0x5b = Hex_8
+keycode 0x5c = KP_9
+ alt keycode 0x5c = Ascii_9
+ altgr keycode 0x5c = Hex_9
+keycode 0x5d =
+keycode 0x5e =
+keycode 0x5f =
+keycode 0x60 = F5 F15 Console_17
+ control keycode 0x60 = F5
+ alt keycode 0x60 = Console_5
+ control alt keycode 0x60 = Console_5
+keycode 0x61 = F6 F16 Console_18
+ control keycode 0x61 = F6
+ alt keycode 0x61 = Console_6
+ control alt keycode 0x61 = Console_6
+keycode 0x62 = F7 F17 Console_19
+ control keycode 0x62 = F7
+ alt keycode 0x62 = Console_7
+ control alt keycode 0x62 = Console_7
+keycode 0x63 = F3 F13 Console_15
+ control keycode 0x63 = F3
+ alt keycode 0x63 = Console_3
+ control alt keycode 0x63 = Console_3
+keycode 0x64 = F8 F18 Console_20
+ control keycode 0x64 = F8
+ alt keycode 0x64 = Console_8
+ control alt keycode 0x64 = Console_8
+keycode 0x65 = F9 F19 Console_21
+ control keycode 0x65 = F9
+ alt keycode 0x65 = Console_9
+ control alt keycode 0x65 = Console_9
+keycode 0x66 =
+keycode 0x67 = F11 F11 Console_23
+ control keycode 0x67 = F11
+ alt keycode 0x67 = Console_11
+ control alt keycode 0x67 = Console_11
+keycode 0x68 =
+keycode 0x69 = F13
+keycode 0x6a =
+keycode 0x6b = Scroll_Lock Show_Memory Show_Registers
+ control keycode 0x6b = Show_State
+ alt keycode 0x6b = Scroll_Lock
+keycode 0x6c =
+keycode 0x6d = F10 F20 Console_22
+ control keycode 0x6d = F10
+ alt keycode 0x6d = Console_10
+ control alt keycode 0x6d = Console_10
+keycode 0x6e =
+keycode 0x6f = F12 F12 Console_24
+ control keycode 0x6f = F12
+ alt keycode 0x6f = Console_12
+ control alt keycode 0x6f = Console_12
+keycode 0x70 =
+keycode 0x71 = Pause
+keycode 0x72 = Insert
+keycode 0x73 = Home
+keycode 0x74 = Prior
+ shift keycode 0x74 = Scroll_Backward
+keycode 0x75 = Remove
+keycode 0x76 = F4 F14 Console_16
+ control keycode 0x76 = F4
+ alt keycode 0x76 = Console_4
+ control alt keycode 0x76 = Console_4
+keycode 0x77 = End
+keycode 0x78 = F2 F12 Console_14
+ control keycode 0x78 = F2
+ alt keycode 0x78 = Console_2
+ control alt keycode 0x78 = Console_2
+keycode 0x79 = Next
+ shift keycode 0x79 = Scroll_Forward
+keycode 0x7a = F1 F11 Console_13
+ control keycode 0x7a = F1
+ alt keycode 0x7a = Console_1
+ control alt keycode 0x7a = Console_1
+keycode 0x7b = Shift
+keycode 0x7c = AltGr
+keycode 0x7d = Control
+keycode 0x7e =
+keycode 0x7f =
+#keycode 0x7f = Power
+ control shift keycode 0x7f = Boot
+string F1 = "\033[[A"
+string F2 = "\033[[B"
+string F3 = "\033[[C"
+string F4 = "\033[[D"
+string F5 = "\033[[E"
+string F6 = "\033[17~"
+string F7 = "\033[18~"
+string F8 = "\033[19~"
+string F9 = "\033[20~"
+string F10 = "\033[21~"
+string F11 = "\033[23~"
+string F12 = "\033[24~"
+string F13 = "\033[25~"
+string F14 = "\033[26~"
+string F15 = "\033[28~"
+string F16 = "\033[29~"
+string F17 = "\033[31~"
+string F18 = "\033[32~"
+string F19 = "\033[33~"
+string F20 = "\033[34~"
+string Find = "\033[1~"
+string Insert = "\033[2~"
+string Remove = "\033[3~"
+string Select = "\033[4~"
+string Prior = "\033[5~"
+string Next = "\033[6~"
+string Macro = "\033[M"
+string Pause = "\033[P"
+compose '`' 'A' to 'À'
+compose '`' 'a' to 'à'
+compose '\'' 'A' to 'Á'
+compose '\'' 'a' to 'á'
+compose '^' 'A' to 'Â'
+compose '^' 'a' to 'â'
+compose '~' 'A' to 'Ã'
+compose '~' 'a' to 'ã'
+compose '"' 'A' to 'Ä'
+compose '"' 'a' to 'ä'
+compose 'O' 'A' to 'Å'
+compose 'o' 'a' to 'å'
+compose '0' 'A' to 'Å'
+compose '0' 'a' to 'å'
+compose 'A' 'A' to 'Å'
+compose 'a' 'a' to 'å'
+compose 'A' 'E' to 'Æ'
+compose 'a' 'e' to 'æ'
+compose ',' 'C' to 'Ç'
+compose ',' 'c' to 'ç'
+compose '`' 'E' to 'È'
+compose '`' 'e' to 'è'
+compose '\'' 'E' to 'É'
+compose '\'' 'e' to 'é'
+compose '^' 'E' to 'Ê'
+compose '^' 'e' to 'ê'
+compose '"' 'E' to 'Ë'
+compose '"' 'e' to 'ë'
+compose '`' 'I' to 'Ì'
+compose '`' 'i' to 'ì'
+compose '\'' 'I' to 'Í'
+compose '\'' 'i' to 'í'
+compose '^' 'I' to 'Î'
+compose '^' 'i' to 'î'
+compose '"' 'I' to 'Ï'
+compose '"' 'i' to 'ï'
+compose '-' 'D' to 'Ð'
+compose '-' 'd' to 'ð'
+compose '~' 'N' to 'Ñ'
+compose '~' 'n' to 'ñ'
+compose '`' 'O' to 'Ò'
+compose '`' 'o' to 'ò'
+compose '\'' 'O' to 'Ó'
+compose '\'' 'o' to 'ó'
+compose '^' 'O' to 'Ô'
+compose '^' 'o' to 'ô'
+compose '~' 'O' to 'Õ'
+compose '~' 'o' to 'õ'
+compose '"' 'O' to 'Ö'
+compose '"' 'o' to 'ö'
+compose '/' 'O' to 'Ø'
+compose '/' 'o' to 'ø'
+compose '`' 'U' to 'Ù'
+compose '`' 'u' to 'ù'
+compose '\'' 'U' to 'Ú'
+compose '\'' 'u' to 'ú'
+compose '^' 'U' to 'Û'
+compose '^' 'u' to 'û'
+compose '"' 'U' to 'Ü'
+compose '"' 'u' to 'ü'
+compose '\'' 'Y' to 'Ý'
+compose '\'' 'y' to 'ý'
+compose 'T' 'H' to 'Þ'
+compose 't' 'h' to 'þ'
+compose 's' 's' to 'ß'
+compose '"' 'y' to 'ÿ'
+compose 's' 'z' to 'ß'
+compose 'i' 'j' to 'ÿ'
diff --git a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c
new file mode 100644
index 000000000..724950cfe
--- /dev/null
+++ b/drivers/macintosh/macserial.c
@@ -0,0 +1,1923 @@
+/*
+ * macserial.c: Serial port driver for Power Macintoshes.
+ *
+ * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras.
+ *
+ * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/prom.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+
+#include "macserial.h"
+
+/*
+ * It would be nice to dynamically allocate everything that
+ * depends on NUM_SERIAL, so we could support any number of
+ * Z8530s, but for now...
+ */
+#define NUM_SERIAL 2 /* Max number of ZS chips supported */
+#define NUM_CHANNELS (NUM_SERIAL * 2) /* 2 channels per chip */
+
+/* On PowerMacs, the hardware takes care of the SCC recovery time,
+ but we need the eieio to make sure that the accesses occur
+ in the order we want. */
+#define RECOVERY_DELAY eieio()
+
+struct mac_zschannel *zs_kgdbchan;
+struct mac_zschannel zs_channels[NUM_CHANNELS];
+
+struct mac_serial zs_soft[NUM_CHANNELS];
+int zs_channels_found;
+struct mac_serial *zs_chain; /* list of all channels */
+
+struct tty_struct zs_ttys[NUM_CHANNELS];
+/** struct tty_struct *zs_constty; **/
+
+/* Console hooks... */
+static int zs_cons_chan = 0;
+struct mac_serial *zs_consinfo = 0;
+struct mac_zschannel *zs_conschan;
+
+/*
+ * Initialization values for when a channel is used for
+ * kernel gdb support.
+ */
+static unsigned char kgdb_regs[16] = {
+ 0, 0, 0, /* write 0, 1, 2 */
+ (Rx8 | RxENABLE), /* write 3 */
+ (X16CLK | SB1), /* write 4 */
+ (Tx8 | TxENAB | RTS), /* write 5 */
+ 0, 0, 0, /* write 6, 7, 8 */
+ (NV), /* write 9 */
+ (NRZ), /* write 10 */
+ (TCBR | RCBR), /* write 11 */
+ 1, 0, /* 38400 baud divisor, write 12 + 13 */
+ (BRENABL), /* write 14 */
+ (DCDIE) /* write 15 */
+};
+
+#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */
+
+DECLARE_TASK_QUEUE(tq_serial);
+
+struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Debugging... DEBUG_INTR is bad to use when one of the zs
+ * lines is your console ;(
+ */
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+
+#define RS_STROBE_TIME 10
+#define RS_ISR_PASS_LIMIT 256
+
+#define _INLINE_ inline
+
+static void probe_sccs(void);
+static void change_speed(struct mac_serial *info);
+
+static struct tty_struct *serial_table[NUM_CHANNELS];
+static struct termios *serial_termios[NUM_CHANNELS];
+static struct termios *serial_termios_locked[NUM_CHANNELS];
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write. We need to
+ * lock it in case the copy_from_user blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char tmp_buf[4096]; /* This is cheating */
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int serial_paranoia_check(struct mac_serial *info,
+ dev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+ static const char *badmagic =
+ "Warning: bad magic number for serial struct (%d, %d) in %s\n";
+ static const char *badinfo =
+ "Warning: null mac_serial for (%d, %d) in %s\n";
+
+ if (!info) {
+ printk(badinfo, MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+ if (info->magic != SERIAL_MAGIC) {
+ printk(badmagic, MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 230400, 0 };
+
+/*
+ * Reading and writing Z8530 registers.
+ */
+static inline unsigned char read_zsreg(struct mac_zschannel *channel,
+ unsigned char reg)
+{
+ unsigned char retval;
+
+ if (reg != 0) {
+ *channel->control = reg;
+ RECOVERY_DELAY;
+ }
+ retval = *channel->control;
+ RECOVERY_DELAY;
+ return retval;
+}
+
+static inline void write_zsreg(struct mac_zschannel *channel,
+ unsigned char reg, unsigned char value)
+{
+ if (reg != 0) {
+ *channel->control = reg;
+ RECOVERY_DELAY;
+ }
+ *channel->control = value;
+ RECOVERY_DELAY;
+ return;
+}
+
+static inline unsigned char read_zsdata(struct mac_zschannel *channel)
+{
+ unsigned char retval;
+
+ retval = *channel->data;
+ RECOVERY_DELAY;
+ return retval;
+}
+
+static inline void write_zsdata(struct mac_zschannel *channel,
+ unsigned char value)
+{
+ *channel->data = value;
+ RECOVERY_DELAY;
+ return;
+}
+
+static inline void load_zsregs(struct mac_zschannel *channel,
+ unsigned char *regs)
+{
+ ZS_CLEARERR(channel);
+ ZS_CLEARFIFO(channel);
+ /* Load 'em up */
+ write_zsreg(channel, R4, regs[R4]);
+ write_zsreg(channel, R10, regs[R10]);
+ write_zsreg(channel, R3, regs[R3] & ~RxENABLE);
+ write_zsreg(channel, R5, regs[R5] & ~TxENAB);
+ write_zsreg(channel, R1, regs[R1]);
+ write_zsreg(channel, R9, regs[R9]);
+ write_zsreg(channel, R11, regs[R11]);
+ write_zsreg(channel, R12, regs[R12]);
+ write_zsreg(channel, R13, regs[R13]);
+ write_zsreg(channel, R14, regs[R14]);
+ write_zsreg(channel, R15, regs[R15]);
+ write_zsreg(channel, R3, regs[R3]);
+ write_zsreg(channel, R5, regs[R5]);
+ return;
+}
+
+/* Sets or clears DTR/RTS on the requested line */
+static inline void zs_rtsdtr(struct mac_serial *ss, int set)
+{
+ if (set)
+ ss->curregs[5] |= (RTS | DTR);
+ else
+ ss->curregs[5] &= ~(RTS | DTR);
+ write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
+ return;
+}
+
+static inline void kgdb_chaninit(struct mac_serial *ss, int intson, int bps)
+{
+ int brg;
+
+ if (intson) {
+ kgdb_regs[R1] = INT_ALL_Rx;
+ kgdb_regs[R9] |= MIE;
+ } else {
+ kgdb_regs[R1] = 0;
+ kgdb_regs[R9] &= ~MIE;
+ }
+ brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
+ kgdb_regs[R12] = brg;
+ kgdb_regs[R13] = brg >> 8;
+ load_zsregs(ss->zs_channel, kgdb_regs);
+}
+
+/* Utility routines for the Zilog */
+static inline int get_zsbaud(struct mac_serial *ss)
+{
+ struct mac_zschannel *channel = ss->zs_channel;
+ int brg;
+
+ if ((ss->curregs[R11] & TCBR) == 0) {
+ /* higher rates don't use the baud rate generator */
+ return (ss->curregs[R4] & X32CLK)? ZS_CLOCK/32: ZS_CLOCK/16;
+ }
+ /* The baud rate is split up between two 8-bit registers in
+ * what is termed 'BRG time constant' format in my docs for
+ * the chip, it is a function of the clk rate the chip is
+ * receiving which happens to be constant.
+ */
+ brg = (read_zsreg(channel, 13) << 8);
+ brg |= read_zsreg(channel, 12);
+ return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->clk_divisor)));
+}
+
+/* On receive, this clears errors and the receiver interrupts */
+static inline void rs_recv_clear(struct mac_zschannel *zsc)
+{
+ write_zsreg(zsc, 0, ERR_RES);
+ write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines. All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt(). They were separated out for readability's sake.
+ *
+ * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void rs_sched_event(struct mac_serial *info,
+ int event)
+{
+ info->event |= 1 << event;
+ queue_task(&info->tqueue, &tq_serial);
+ mark_bh(SERIAL_BH);
+}
+
+extern void breakpoint(void); /* For the KGDB frame character */
+
+static _INLINE_ void receive_chars(struct mac_serial *info,
+ struct pt_regs *regs)
+{
+ struct tty_struct *tty = info->tty;
+ unsigned char ch, stat, flag;
+
+ while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) != 0) {
+
+ stat = read_zsreg(info->zs_channel, R1);
+ ch = read_zsdata(info->zs_channel);
+
+#if 0 /* KGDB not yet supported */
+ /* Look for kgdb 'stop' character, consult the gdb documentation
+ * for remote target debugging and arch/sparc/kernel/sparc-stub.c
+ * to see how all this works.
+ */
+ if ((info->kgdb_channel) && (ch =='\003')) {
+ breakpoint();
+ continue;
+ }
+#endif
+
+ if (!tty)
+ continue;
+ queue_task(&tty->flip.tqueue, &tq_timer);
+
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ static int flip_buf_ovf;
+ ++flip_buf_ovf;
+ continue;
+ }
+ tty->flip.count++;
+ {
+ static int flip_max_cnt;
+ if (flip_max_cnt < tty->flip.count)
+ flip_max_cnt = tty->flip.count;
+ }
+ if (stat & Rx_OVR) {
+ flag = TTY_OVERRUN;
+ /* reset the error indication */
+ write_zsreg(info->zs_channel, 0, ERR_RES);
+ } else if (stat & FRM_ERR) {
+ /* this error is not sticky */
+ flag = TTY_FRAME;
+ } else if (stat & PAR_ERR) {
+ flag = TTY_PARITY;
+ /* reset the error indication */
+ write_zsreg(info->zs_channel, 0, ERR_RES);
+ } else
+ flag = 0;
+ *tty->flip.flag_buf_ptr++ = flag;
+ *tty->flip.char_buf_ptr++ = ch;
+ }
+}
+
+static void transmit_chars(struct mac_serial *info)
+{
+ if ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0)
+ return;
+ info->tx_active = 0;
+
+ if (info->x_char) {
+ /* Send next char */
+ write_zsdata(info->zs_channel, info->x_char);
+ info->x_char = 0;
+ info->tx_active = 1;
+ return;
+ }
+
+ if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tx_stopped) {
+ write_zsreg(info->zs_channel, 0, RES_Tx_P);
+ return;
+ }
+
+ /* Send char */
+ write_zsdata(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
+ info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt--;
+ info->tx_active = 1;
+
+ if (info->xmit_cnt < WAKEUP_CHARS)
+ rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+}
+
+static _INLINE_ void status_handle(struct mac_serial *info)
+{
+ unsigned char status;
+
+ /* Get status from Read Register 0 */
+ status = read_zsreg(info->zs_channel, 0);
+
+ /* Check for DCD transitions */
+ if (((status ^ info->read_reg_zero) & DCD) != 0
+ && info->tty && !C_CLOCAL(info->tty)) {
+ if (status & DCD) {
+ wake_up_interruptible(&info->open_wait);
+ } else if (!(info->flags & ZILOG_CALLOUT_ACTIVE)) {
+ queue_task(&info->tqueue_hangup, &tq_scheduler);
+ }
+ }
+
+ /* Check for CTS transitions */
+ if (info->tty && C_CRTSCTS(info->tty)) {
+ /*
+ * For some reason, on the Power Macintosh,
+ * it seems that the CTS bit is 1 when CTS is
+ * *negated* and 0 when it is asserted.
+ * The DCD bit doesn't seem to be inverted
+ * like this.
+ */
+ if ((status & CTS) == 0) {
+ if (info->tx_stopped) {
+ info->tx_stopped = 0;
+ if (!info->tx_active)
+ transmit_chars(info);
+ }
+ } else {
+ info->tx_stopped = 1;
+ }
+ }
+
+ /* Clear status condition... */
+ write_zsreg(info->zs_channel, 0, RES_EXT_INT);
+ info->read_reg_zero = status;
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct mac_serial *info = (struct mac_serial *) dev_id;
+ unsigned char zs_intreg;
+ int shift;
+
+ /* NOTE: The read register 3, which holds the irq status,
+ * does so for both channels on each chip. Although
+ * the status value itself must be read from the A
+ * channel and is only valid when read from channel A.
+ * Yes... broken hardware...
+ */
+#define CHAN_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT)
+
+ if (info->zs_chan_a == info->zs_channel)
+ shift = 3; /* Channel A */
+ else
+ shift = 0; /* Channel B */
+
+ for (;;) {
+ zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift;
+ if ((zs_intreg & CHAN_IRQMASK) == 0)
+ break;
+
+ if (zs_intreg & CHBRxIP)
+ receive_chars(info, regs);
+ if (zs_intreg & CHBTxIP)
+ transmit_chars(info);
+ if (zs_intreg & CHBEXT)
+ status_handle(info);
+ }
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+ struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "rs_stop"))
+ return;
+
+#if 0
+ save_flags(flags); cli();
+ if (info->curregs[5] & TxENAB) {
+ info->curregs[5] &= ~TxENAB;
+ info->pendregs[5] &= ~TxENAB;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ }
+ restore_flags(flags);
+#endif
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+ struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_start"))
+ return;
+
+ save_flags(flags); cli();
+#if 0
+ if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {
+ info->curregs[5] |= TxENAB;
+ info->pendregs[5] = info->curregs[5];
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ }
+#else
+ if (info->xmit_cnt && info->xmit_buf && !info->tx_active) {
+ transmit_chars(info);
+ }
+#endif
+ restore_flags(flags);
+}
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+ run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+ struct mac_serial *info = (struct mac_serial *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+}
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred. The path of
+ * hangup processing is:
+ *
+ * serial interrupt routine -> (scheduler tqueue) ->
+ * do_serial_hangup() -> tty->hangup() -> rs_hangup()
+ *
+ */
+static void do_serial_hangup(void *private_)
+{
+ struct mac_serial *info = (struct mac_serial *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ tty_hangup(tty);
+}
+
+static void rs_timer(void)
+{
+}
+
+static int startup(struct mac_serial * info)
+{
+ unsigned long flags;
+
+ if (info->flags & ZILOG_INITIALIZED)
+ return 0;
+
+ if (!info->xmit_buf) {
+ info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+ if (!info->xmit_buf)
+ return -ENOMEM;
+ }
+
+ save_flags(flags); cli();
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("starting up ttyS%d (irq %d)...", info->line, info->irq);
+#endif
+
+ /*
+ * Clear the receive FIFO.
+ */
+ ZS_CLEARFIFO(info->zs_channel);
+ info->xmit_fifo_size = 1;
+
+ /*
+ * Clear the interrupt registers.
+ */
+ write_zsreg(info->zs_channel, 0, ERR_RES);
+ write_zsreg(info->zs_channel, 0, RES_H_IUS);
+
+ /*
+ * Turn on RTS and DTR.
+ */
+ zs_rtsdtr(info, 1);
+
+ /*
+ * Finally, enable sequencing and interrupts
+ */
+ info->curregs[1] = (info->curregs[1] & ~0x18) | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
+ info->pendregs[1] = info->curregs[1];
+ info->curregs[3] |= (RxENABLE | Rx8);
+ info->pendregs[3] = info->curregs[3];
+ info->curregs[5] |= (TxENAB | Tx8);
+ info->pendregs[5] = info->curregs[5];
+ info->curregs[9] |= (NV | MIE);
+ info->pendregs[9] = info->curregs[9];
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ write_zsreg(info->zs_channel, 9, info->curregs[9]);
+
+ if (info->tty)
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+ /*
+ * Set the speed of the serial port
+ */
+ change_speed(info);
+
+ /* Save the current value of RR0 */
+ info->read_reg_zero = read_zsreg(info->zs_channel, 0);
+
+ info->flags |= ZILOG_INITIALIZED;
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * 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 mac_serial * info)
+{
+ unsigned long flags;
+
+ if (!(info->flags & ZILOG_INITIALIZED))
+ return;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Shutting down serial port %d (irq %d)....", info->line,
+ info->irq);
+#endif
+
+ save_flags(flags); cli(); /* Disable interrupts */
+
+ if (info->xmit_buf) {
+ free_page((unsigned long) info->xmit_buf);
+ info->xmit_buf = 0;
+ }
+
+ info->pendregs[1] = info->curregs[1] = 0;
+ write_zsreg(info->zs_channel, 1, 0); /* no interrupts */
+
+ info->curregs[3] &= ~RxENABLE;
+ info->pendregs[3] = info->curregs[3];
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+
+ info->curregs[5] &= ~TxENAB;
+ if (!info->tty || C_HUPCL(info->tty))
+ info->curregs[5] &= ~(DTR | RTS);
+ info->pendregs[5] = info->curregs[5];
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ZILOG_INITIALIZED;
+ restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct mac_serial *info)
+{
+ unsigned short port;
+ unsigned cflag;
+ int i;
+ int brg;
+ unsigned long flags;
+
+ if (!info->tty || !info->tty->termios)
+ return;
+ cflag = info->tty->termios->c_cflag;
+ if (!(port = info->port))
+ return;
+ i = cflag & CBAUD;
+
+ save_flags(flags); cli();
+ info->zs_baud = baud_table[i];
+ info->clk_divisor = 16;
+
+ switch (info->zs_baud) {
+ case ZS_CLOCK/16: /* 230400 */
+ info->curregs[4] = X16CLK;
+ info->curregs[11] = 0;
+ break;
+ case ZS_CLOCK/32: /* 115200 */
+ info->curregs[4] = X32CLK;
+ info->curregs[11] = 0;
+ break;
+ default:
+ info->curregs[4] = X16CLK;
+ info->curregs[11] = TCBR | RCBR;
+ brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
+ info->curregs[12] = (brg & 255);
+ info->curregs[13] = ((brg >> 8) & 255);
+ info->curregs[14] = BRENABL;
+ }
+
+ /* byte size and parity */
+ info->curregs[3] &= ~RxNBITS_MASK;
+ info->curregs[5] &= ~TxNBITS_MASK;
+ switch (cflag & CSIZE) {
+ case CS5:
+ info->curregs[3] |= Rx5;
+ info->curregs[5] |= Tx5;
+ break;
+ case CS6:
+ info->curregs[3] |= Rx6;
+ info->curregs[5] |= Tx6;
+ break;
+ case CS7:
+ info->curregs[3] |= Rx7;
+ info->curregs[5] |= Tx7;
+ break;
+ case CS8:
+ default: /* defaults to 8 bits */
+ info->curregs[3] |= Rx8;
+ info->curregs[5] |= Tx8;
+ break;
+ }
+ info->pendregs[3] = info->curregs[3];
+ info->pendregs[5] = info->curregs[5];
+
+ info->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN);
+ if (cflag & CSTOPB) {
+ info->curregs[4] |= SB2;
+ } else {
+ info->curregs[4] |= SB1;
+ }
+ if (cflag & PARENB) {
+ info->curregs[4] |= PAR_ENA;
+ }
+ if (!(cflag & PARODD)) {
+ info->curregs[4] |= PAR_EVEN;
+ }
+ info->pendregs[4] = info->curregs[4];
+
+ if (!(cflag & CLOCAL)) {
+ if (!(info->curregs[15] & DCDIE))
+ info->read_reg_zero = read_zsreg(info->zs_channel, 0);
+ info->curregs[15] |= DCDIE;
+ } else
+ info->curregs[15] &= ~DCDIE;
+ if (cflag & CRTSCTS) {
+ info->curregs[15] |= CTSIE;
+ if ((read_zsreg(info->zs_channel, 0) & CTS) != 0)
+ info->tx_stopped = 1;
+ } else {
+ info->curregs[15] &= ~CTSIE;
+ info->tx_stopped = 0;
+ }
+ info->pendregs[15] = info->curregs[15];
+
+ /* Load up the new values */
+ load_zsregs(info->zs_channel, info->curregs);
+
+ restore_flags(flags);
+}
+
+/* This is for console output over ttya/ttyb */
+static void rs_put_char(char ch)
+{
+ struct mac_zschannel *chan = zs_conschan;
+ int loops = 0;
+ unsigned long flags;
+
+ if(!chan)
+ return;
+
+ save_flags(flags); cli();
+ while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0)
+ if (++loops >= 1000000)
+ break;
+ write_zsdata(chan, ch);
+ restore_flags(flags);
+}
+
+/* These are for receiving and sending characters under the kgdb
+ * source level kernel debugger.
+ */
+void putDebugChar(char kgdb_char)
+{
+ struct mac_zschannel *chan = zs_kgdbchan;
+
+ while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0)
+ udelay(5);
+ write_zsdata(chan, kgdb_char);
+}
+
+char getDebugChar(void)
+{
+ struct mac_zschannel *chan = zs_kgdbchan;
+
+ while ((read_zsreg(chan, 0) & Rx_CH_AV) == 0)
+ udelay(5);
+ return read_zsdata(chan);
+}
+
+/*
+ * Fair output driver allows a process to speak.
+ */
+static void rs_fair_output(void)
+{
+ int left; /* Output no more than that */
+ unsigned long flags;
+ struct mac_serial *info = zs_consinfo;
+ char c;
+
+ if (info == 0) return;
+ if (info->xmit_buf == 0) return;
+
+ save_flags(flags); cli();
+ left = info->xmit_cnt;
+ while (left != 0) {
+ c = info->xmit_buf[info->xmit_tail];
+ info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt--;
+ restore_flags(flags);
+
+ rs_put_char(c);
+
+ save_flags(flags); cli();
+ left = MIN(info->xmit_cnt, left-1);
+ }
+
+ restore_flags(flags);
+ return;
+}
+
+/*
+ * zs_console_print is registered for printk.
+ */
+static void zs_console_print(const char *p)
+{
+ char c;
+
+ while ((c = *(p++)) != 0) {
+ if (c == '\n')
+ rs_put_char('\r');
+ rs_put_char(c);
+ }
+
+ /* Comment this if you want to have a strict interrupt-driven output */
+ rs_fair_output();
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+ struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
+ return;
+
+ if (info->xmit_cnt <= 0 || tty->stopped || info->tx_stopped ||
+ !info->xmit_buf)
+ return;
+
+ /* Enable transmitter */
+ save_flags(flags); cli();
+ transmit_chars(info);
+ restore_flags(flags);
+}
+
+static int rs_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ int c, total = 0;
+ struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_write"))
+ return 0;
+
+ if (!tty || !info->xmit_buf)
+ return 0;
+
+ save_flags(flags);
+ while (1) {
+ cli();
+ c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ if (c <= 0)
+ break;
+
+ if (from_user) {
+ down(&tmp_buf_sem);
+ copy_from_user(tmp_buf, buf, c);
+ c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+ up(&tmp_buf_sem);
+ } else
+ memcpy(info->xmit_buf + info->xmit_head, buf, c);
+ info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt += c;
+ restore_flags(flags);
+ buf += c;
+ count -= c;
+ total += c;
+ }
+ if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
+ && !info->tx_active)
+ transmit_chars(info);
+ restore_flags(flags);
+ return total;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+ struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+ int ret;
+
+ if (serial_paranoia_check(info, tty->device, "rs_write_room"))
+ return 0;
+ ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+ if (ret < 0)
+ ret = 0;
+ return ret;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+ struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
+ return 0;
+ return info->xmit_cnt;
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+ struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
+ return;
+ cli();
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ sti();
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(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)
+{
+ struct mac_serial *info = (struct mac_serial *)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));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "rs_throttle"))
+ return;
+
+ if (I_IXOFF(tty)) {
+ save_flags(flags); cli();
+ info->x_char = STOP_CHAR(tty);
+ if (!info->tx_active)
+ transmit_chars(info);
+ restore_flags(flags);
+ }
+
+ if (C_CRTSCTS(tty)) {
+ /*
+ * Here we want to turn off the RTS line. On Macintoshes,
+ * we only get the DTR line, which goes to both DTR and
+ * RTS on the modem. RTS doesn't go out to the serial
+ * port socket. So you should make sure your modem is
+ * set to ignore DTR if you're using CRTSCTS.
+ */
+ save_flags(flags); cli();
+ info->curregs[5] &= ~(DTR | RTS);
+ info->pendregs[5] &= ~(DTR | RTS);
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ restore_flags(flags);
+ }
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+ struct mac_serial *info = (struct mac_serial *)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));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
+ return;
+
+ if (I_IXOFF(tty)) {
+ save_flags(flags); cli();
+ if (info->x_char)
+ info->x_char = 0;
+ else {
+ info->x_char = START_CHAR(tty);
+ if (!info->tx_active)
+ transmit_chars(info);
+ }
+ restore_flags(flags);
+ }
+
+ if (C_CRTSCTS(tty)) {
+ /* Assert RTS and DTR lines */
+ save_flags(flags); cli();
+ info->curregs[5] |= DTR | RTS;
+ info->pendregs[5] |= DTR | RTS;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ restore_flags(flags);
+ }
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct mac_serial * info,
+ struct serial_struct * retinfo)
+{
+ struct serial_struct tmp;
+
+ if (!retinfo)
+ return -EFAULT;
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.type = info->type;
+ tmp.line = info->line;
+ tmp.port = info->port;
+ tmp.irq = info->irq;
+ tmp.flags = info->flags;
+ tmp.baud_base = info->baud_base;
+ tmp.close_delay = info->close_delay;
+ tmp.closing_wait = info->closing_wait;
+ tmp.custom_divisor = info->custom_divisor;
+ return copy_to_user(retinfo,&tmp,sizeof(*retinfo));
+}
+
+static int set_serial_info(struct mac_serial * info,
+ struct serial_struct * new_info)
+{
+ struct serial_struct new_serial;
+ struct mac_serial old_info;
+ int retval = 0;
+
+ if (!new_info)
+ return -EFAULT;
+ copy_from_user(&new_serial,new_info,sizeof(new_serial));
+ old_info = *info;
+
+ if (!suser()) {
+ if ((new_serial.baud_base != info->baud_base) ||
+ (new_serial.type != info->type) ||
+ (new_serial.close_delay != info->close_delay) ||
+ ((new_serial.flags & ~ZILOG_USR_MASK) !=
+ (info->flags & ~ZILOG_USR_MASK)))
+ return -EPERM;
+ info->flags = ((info->flags & ~ZILOG_USR_MASK) |
+ (new_serial.flags & ZILOG_USR_MASK));
+ info->custom_divisor = new_serial.custom_divisor;
+ goto check_and_exit;
+ }
+
+ if (info->count > 1)
+ return -EBUSY;
+
+ /*
+ * OK, past this point, all the error checking has been done.
+ * At this point, we start making changes.....
+ */
+
+ info->baud_base = new_serial.baud_base;
+ info->flags = ((info->flags & ~ZILOG_FLAGS) |
+ (new_serial.flags & ZILOG_FLAGS));
+ info->type = new_serial.type;
+ info->close_delay = new_serial.close_delay;
+ info->closing_wait = new_serial.closing_wait;
+
+check_and_exit:
+ retval = startup(info);
+ return retval;
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows an RS485 driver to be written in user space.
+ */
+static int get_lsr_info(struct mac_serial * info, unsigned int *value)
+{
+ unsigned char status;
+
+ cli();
+ status = read_zsreg(info->zs_channel, 0);
+ sti();
+ put_user(status,value);
+ return 0;
+}
+
+static int get_modem_info(struct mac_serial *info, unsigned int *value)
+{
+ unsigned char control, status;
+ unsigned int result;
+
+ cli();
+ control = info->curregs[5];
+ status = read_zsreg(info->zs_channel, 0);
+ sti();
+ result = ((control & RTS) ? TIOCM_RTS: 0)
+ | ((control & DTR) ? TIOCM_DTR: 0)
+ | ((status & DCD) ? TIOCM_CAR: 0)
+ | ((status & CTS) ? 0: TIOCM_CTS);
+ put_user(result,value);
+ return 0;
+}
+
+static int set_modem_info(struct mac_serial *info, unsigned int cmd,
+ unsigned int *value)
+{
+ int error;
+ unsigned int arg, bits;
+
+ error = verify_area(VERIFY_READ, value, sizeof(int));
+ if (error)
+ return error;
+ get_user(arg, value);
+ bits = (arg & TIOCM_RTS? RTS: 0) + (arg & TIOCM_DTR? DTR: 0);
+ cli();
+ switch (cmd) {
+ case TIOCMBIS:
+ info->curregs[5] |= bits;
+ break;
+ case TIOCMBIC:
+ info->curregs[5] &= ~bits;
+ break;
+ case TIOCMSET:
+ info->curregs[5] = (info->curregs[5] & ~(DTR | RTS)) | bits;
+ break;
+ default:
+ sti();
+ return -EINVAL;
+ }
+ info->pendregs[5] = info->curregs[5];
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ sti();
+ return 0;
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break( struct mac_serial * info, int duration)
+{
+ if (!info->port)
+ return;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + duration;
+ cli();
+ info->curregs[5] |= SND_BRK;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ schedule();
+ info->curregs[5] &= ~SND_BRK;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ sti();
+}
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ int error;
+ struct mac_serial * info = (struct mac_serial *)tty->driver_data;
+ int retval;
+
+ if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
+ return -ENODEV;
+
+ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
+ (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ }
+
+ switch (cmd) {
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (!arg)
+ send_break(info, HZ/4); /* 1/4 second */
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ send_break(info, arg ? arg*(HZ/10) : HZ/4);
+ return 0;
+ case TIOCGSOFTCAR:
+ return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
+ case TIOCSSOFTCAR:
+ error = get_user(arg, (int *) arg);
+ if (error)
+ return error;
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ return 0;
+ case TIOCMGET:
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(unsigned int));
+ if (error)
+ return error;
+ return get_modem_info(info, (unsigned int *) arg);
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return set_modem_info(info, cmd, (unsigned int *) arg);
+ case TIOCGSERIAL:
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(struct serial_struct));
+ if (error)
+ return error;
+ return get_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ return set_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSERGETLSR: /* Get line status register */
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(unsigned int));
+ if (error)
+ return error;
+ else
+ return get_lsr_info(info, (unsigned int *) arg);
+
+ case TIOCSERGSTRUCT:
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(struct mac_serial));
+ if (error)
+ return error;
+ copy_from_user((struct mac_serial *) arg,
+ info, sizeof(struct mac_serial));
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+ struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+ int was_stopped;
+
+ if (tty->termios->c_cflag == old_termios->c_cflag)
+ return;
+ was_stopped = info->tx_stopped;
+
+ change_speed(info);
+
+ if (was_stopped && !info->tx_stopped)
+ rs_start(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed.
+ * Wait for the last remaining data to be sent.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+ struct mac_serial * info = (struct mac_serial *)tty->driver_data;
+ unsigned long flags;
+ unsigned long timeout;
+
+ if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
+ return;
+
+ save_flags(flags); cli();
+
+ if (tty_hung_up_p(filp)) {
+ restore_flags(flags);
+ return;
+ }
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_close ttys%d, count = %d\n", info->line, info->count);
+#endif
+ if ((tty->count == 1) && (info->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. Info->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk("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",
+ info->line, info->count);
+ info->count = 0;
+ }
+ if (info->count) {
+ restore_flags(flags);
+ return;
+ }
+ info->flags |= ZILOG_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ZILOG_NORMAL_ACTIVE)
+ info->normal_termios = *tty->termios;
+ if (info->flags & ZILOG_CALLOUT_ACTIVE)
+ info->callout_termios = *tty->termios;
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, info->closing_wait);
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receiver and receive interrupts.
+ */
+ /** if (!info->iscons) ... **/
+ info->curregs[3] &= ~RxENABLE;
+ info->pendregs[3] = info->curregs[3];
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ info->curregs[1] &= ~(0x18); /* disable any rx ints */
+ info->pendregs[1] = info->curregs[1];
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ ZS_CLEARFIFO(info->zs_channel);
+ if (info->flags & ZILOG_INITIALIZED) {
+ /*
+ * Before we drop DTR, make sure the SCC transmitter
+ * has completely drained.
+ */
+ timeout = jiffies+HZ;
+ while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + info->timeout;
+ schedule();
+ if (jiffies > timeout)
+ break;
+ }
+ }
+
+ shutdown(info);
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ tty->closing = 0;
+ info->event = 0;
+ info->tty = 0;
+ if (tty->ldisc.num != ldiscs[N_TTY].num) {
+ if (tty->ldisc.close)
+ (tty->ldisc.close)(tty);
+ tty->ldisc = ldiscs[N_TTY];
+ tty->termios->c_line = N_TTY;
+ if (tty->ldisc.open)
+ (tty->ldisc.open)(tty);
+ }
+ if (info->blocked_open) {
+ if (info->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + info->close_delay;
+ schedule();
+ }
+ wake_up_interruptible(&info->open_wait);
+ }
+ info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
+ ZILOG_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+ restore_flags(flags);
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void rs_hangup(struct tty_struct *tty)
+{
+ struct mac_serial * info = (struct mac_serial *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "rs_hangup"))
+ return;
+
+ rs_flush_buffer(tty);
+ shutdown(info);
+ info->event = 0;
+ info->count = 0;
+ info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE);
+ info->tty = 0;
+ wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct mac_serial *info)
+{
+ struct wait_queue wait = { current, NULL };
+ int retval;
+ int do_clocal = 0;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (info->flags & ZILOG_CLOSING) {
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ZILOG_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+ if (info->flags & ZILOG_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
+ (info->flags & ZILOG_SESSION_LOCKOUT) &&
+ (info->session != current->session))
+ return -EBUSY;
+ if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
+ (info->flags & ZILOG_PGRP_LOCKOUT) &&
+ (info->pgrp != current->pgrp))
+ return -EBUSY;
+ info->flags |= ZILOG_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (info->flags & ZILOG_CALLOUT_ACTIVE)
+ return -EBUSY;
+ info->flags |= ZILOG_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (info->flags & ZILOG_CALLOUT_ACTIVE) {
+ if (info->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, info->count is dropped by one, so that
+ * rs_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready before block: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+ cli();
+ if (!tty_hung_up_p(filp))
+ info->count--;
+ sti();
+ info->blocked_open++;
+ while (1) {
+ cli();
+ if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
+ (tty->termios->c_cflag & CBAUD))
+ zs_rtsdtr(info, 1);
+ sti();
+ current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp) ||
+ !(info->flags & ZILOG_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ZILOG_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+ if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
+ !(info->flags & ZILOG_CLOSING) &&
+ (do_clocal || (read_zsreg(info->zs_channel, 0) & DCD)))
+ break;
+ if (current->signal & ~current->blocked) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready blocking: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+ if (!tty_hung_up_p(filp))
+ info->count++;
+ info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready after blocking: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+ if (retval)
+ return retval;
+ info->flags |= ZILOG_NORMAL_ACTIVE;
+ return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened. It
+ * enables interrupts for a serial port, linking in its ZILOG structure into
+ * the IRQ chain. It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+int rs_open(struct tty_struct *tty, struct file * filp)
+{
+ struct mac_serial *info;
+ int retval, line;
+
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ if ((line < 0) || (line >= zs_channels_found))
+ return -ENODEV;
+ info = zs_soft + line;
+
+ /* Is the kgdb running over this line? */
+ if (info->kgdb_channel)
+ return -ENODEV;
+ if (serial_paranoia_check(info, tty->device, "rs_open"))
+ return -ENODEV;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+ info->count);
+#endif
+
+ info->count++;
+ tty->driver_data = info;
+ info->tty = tty;
+
+ /*
+ * Start up serial port
+ */
+ retval = startup(info);
+ if (retval)
+ return retval;
+
+ retval = block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open returning after block_til_ready with %d\n",
+ retval);
+#endif
+ return retval;
+ }
+
+ if ((info->count == 1) && (info->flags & ZILOG_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = info->normal_termios;
+ else
+ *tty->termios = info->callout_termios;
+ change_speed(info);
+ }
+
+ info->session = current->session;
+ info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open ttys%d successful...", info->line);
+#endif
+ return 0;
+}
+
+/* Finally, routines used to initialize the serial driver. */
+
+static void show_serial_version(void)
+{
+ printk("PowerMac Z8530 serial driver version 1.00\n");
+}
+
+/* Ask the PROM how many Z8530s we have and initialize their zs_channels */
+static void
+probe_sccs()
+{
+ struct device_node *dev, *ch;
+ struct mac_serial **pp;
+ int n;
+
+ n = 0;
+ pp = &zs_chain;
+ for (dev = find_devices("escc"); dev != 0; dev = dev->next) {
+ if (n >= NUM_CHANNELS) {
+ printk("Sorry, can't use %s: no more channels\n",
+ dev->full_name);
+ continue;
+ }
+ for (ch = dev->child; ch != 0; ch = ch->sibling) {
+ if (ch->n_addrs < 1 || ch ->n_intrs < 1) {
+ printk("Can't use %s: %d addrs %d intrs\n",
+ ch->full_name, ch->n_addrs, ch->n_intrs);
+ continue;
+ }
+ zs_channels[n].control = (volatile unsigned char *)
+ ch->addrs[0].address;
+ zs_channels[n].data = zs_channels[n].control
+ + ch->addrs[0].size / 2;
+ zs_soft[n].zs_channel = &zs_channels[n];
+ zs_soft[n].irq = ch->intrs[0];
+ if (request_irq(ch->intrs[0], rs_interrupt, 0,
+ "SCC", &zs_soft[n]))
+ panic("macserial: can't get irq %d",
+ ch->intrs[0]);
+ /* XXX this assumes the prom puts chan A before B */
+ if (n & 1)
+ zs_soft[n].zs_chan_a = &zs_channels[n-1];
+ else
+ zs_soft[n].zs_chan_a = &zs_channels[n];
+
+ *pp = &zs_soft[n];
+ pp = &zs_soft[n].zs_next;
+ ++n;
+ }
+ }
+ *pp = 0;
+ zs_channels_found = n;
+}
+
+/* rs_init inits the driver */
+int rs_init(void)
+{
+ int channel, i;
+ unsigned long flags;
+ struct mac_serial *info;
+
+ /* Setup base handler, and timer table. */
+ init_bh(SERIAL_BH, do_serial_bh);
+ timer_table[RS_TIMER].fn = rs_timer;
+ timer_table[RS_TIMER].expires = 0;
+
+ /* Find out how many Z8530 SCCs we have */
+ if (zs_chain == 0)
+ probe_sccs();
+
+ show_serial_version();
+
+ /* Initialize the tty_driver structure */
+ /* Not all of this is exactly right for us. */
+
+ memset(&serial_driver, 0, sizeof(struct tty_driver));
+ serial_driver.magic = TTY_DRIVER_MAGIC;
+ serial_driver.name = "ttyS";
+ serial_driver.major = TTY_MAJOR;
+ serial_driver.minor_start = 64;
+ serial_driver.num = zs_channels_found;
+ serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ serial_driver.subtype = SERIAL_TYPE_NORMAL;
+ serial_driver.init_termios = tty_std_termios;
+
+ serial_driver.init_termios.c_cflag =
+ B38400 | CS8 | CREAD | HUPCL | CLOCAL;
+ serial_driver.flags = TTY_DRIVER_REAL_RAW;
+ serial_driver.refcount = &serial_refcount;
+ serial_driver.table = serial_table;
+ serial_driver.termios = serial_termios;
+ serial_driver.termios_locked = serial_termios_locked;
+
+ serial_driver.open = rs_open;
+ serial_driver.close = rs_close;
+ serial_driver.write = rs_write;
+ serial_driver.flush_chars = rs_flush_chars;
+ serial_driver.write_room = rs_write_room;
+ serial_driver.chars_in_buffer = rs_chars_in_buffer;
+ serial_driver.flush_buffer = rs_flush_buffer;
+ serial_driver.ioctl = rs_ioctl;
+ serial_driver.throttle = rs_throttle;
+ serial_driver.unthrottle = rs_unthrottle;
+ serial_driver.set_termios = rs_set_termios;
+ serial_driver.stop = rs_stop;
+ serial_driver.start = rs_start;
+ serial_driver.hangup = rs_hangup;
+
+ /*
+ * The callout device is just like normal device except for
+ * major number and the subtype code.
+ */
+ callout_driver = serial_driver;
+ callout_driver.name = "cua";
+ callout_driver.major = TTYAUX_MAJOR;
+ callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+ if (tty_register_driver(&serial_driver))
+ panic("Couldn't register serial driver\n");
+ if (tty_register_driver(&callout_driver))
+ panic("Couldn't register callout driver\n");
+
+ save_flags(flags); cli();
+
+ for (channel = 0; channel < zs_channels_found; ++channel) {
+ zs_soft[channel].clk_divisor = 16;
+ zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]);
+
+ /* If console serial line, then enable interrupts. */
+ if (zs_soft[channel].is_cons) {
+ write_zsreg(zs_soft[channel].zs_channel, R1,
+ (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB));
+ write_zsreg(zs_soft[channel].zs_channel, R9,
+ (NV | MIE));
+ }
+ /* If this is the kgdb line, enable interrupts because we
+ * now want to receive the 'control-c' character from the
+ * client attached to us asynchronously.
+ */
+ if (zs_soft[channel].kgdb_channel)
+ kgdb_chaninit(&zs_soft[channel], 1,
+ zs_soft[channel].zs_baud);
+ }
+
+ for (info = zs_chain, i = 0; info; info = info->zs_next, i++)
+ {
+ info->magic = SERIAL_MAGIC;
+ info->port = (int) info->zs_channel->control;
+ info->line = i;
+ info->tty = 0;
+ info->custom_divisor = 16;
+ info->close_delay = 50;
+ info->closing_wait = 3000;
+ info->x_char = 0;
+ info->event = 0;
+ info->count = 0;
+ info->blocked_open = 0;
+ info->tqueue.routine = do_softint;
+ info->tqueue.data = info;
+ info->tqueue_hangup.routine = do_serial_hangup;
+ info->tqueue_hangup.data = info;
+ info->callout_termios =callout_driver.init_termios;
+ info->normal_termios = serial_driver.init_termios;
+ info->open_wait = 0;
+ info->close_wait = 0;
+ printk("tty%02d at 0x%08x (irq = %d)", info->line,
+ info->port, info->irq);
+ printk(" is a Z8530 ESCC\n");
+ }
+
+ restore_flags(flags);
+
+ return 0;
+}
+
+/*
+ * register_serial and unregister_serial allows for serial ports to be
+ * configured at run-time, to support PCMCIA modems.
+ */
+/* PowerMac: Unused at this time, just here to make things link. */
+int register_serial(struct serial_struct *req)
+{
+ return -1;
+}
+
+void unregister_serial(int line)
+{
+ return;
+}
+
+extern void register_console(void (*proc)(const char *));
+
+/*
+ * Initialization values for when a channel is used for
+ * a serial console.
+ */
+static unsigned char cons_init_regs[16] = {
+ 0, 0, 0, /* write 0, 1, 2 */
+ (Rx8 | RxENABLE), /* write 3 */
+ (X16CLK | SB1), /* write 4 */
+ (Tx8 | TxENAB | RTS), /* write 5 */
+ 0, 0, 0, /* write 6, 7, 8 */
+ 0, /* write 9 */
+ (NRZ), /* write 10 */
+ (TCBR | RCBR), /* write 11 */
+ 1, 0, /* 38400 baud divisor, write 12 + 13 */
+ (BRENABL), /* write 14 */
+ 0 /* write 15 */
+};
+
+/*
+ * Hooks for running a serial console. con_init() calls this if the
+ * console is being run over one of the serial ports.
+ * 'channel' is decoded as 1=modem, 2=printer.
+ */
+void
+rs_cons_hook(int chip, int out, int channel)
+{
+ int brg;
+
+ if (!out)
+ return;
+ if (zs_consinfo != 0) {
+ printk("rs_cons_hook called twice?\n");
+ return;
+ }
+ if (zs_chain == 0)
+ probe_sccs();
+ --channel;
+ if (channel < 0 || channel >= zs_channels_found) {
+ printk("rs_cons_hook: channel = %d?\n", channel);
+ return;
+ }
+
+ zs_cons_chan = channel;
+ zs_consinfo = &zs_soft[channel];
+ zs_conschan = zs_consinfo->zs_channel;
+ zs_consinfo->clk_divisor = 16;
+ zs_consinfo->zs_baud = 38400;
+ zs_consinfo->is_cons = 1;
+
+ memcpy(zs_consinfo->curregs, cons_init_regs, sizeof(cons_init_regs));
+ brg = BPS_TO_BRG(zs_consinfo->zs_baud, ZS_CLOCK/16);
+ zs_consinfo->curregs[R12] = brg;
+ zs_consinfo->curregs[R13] = brg >> 8;
+ load_zsregs(zs_conschan, zs_consinfo->curregs);
+
+ register_console(zs_console_print);
+ printk("zs%d: console I/O\n", channel);
+}
+
+/* This is called at boot time to prime the kgdb serial debugging
+ * serial line. The 'tty_num' argument is 0 for /dev/ttyS0 and 1
+ * for /dev/ttyS1 which is determined in setup_arch() from the
+ * boot command line flags.
+ */
+void
+rs_kgdb_hook(int tty_num)
+{
+ if (zs_chain == 0)
+ probe_sccs();
+ zs_kgdbchan = zs_soft[tty_num].zs_channel;
+ zs_soft[tty_num].clk_divisor = 16;
+ zs_soft[tty_num].zs_baud = get_zsbaud(&zs_soft[tty_num]);
+ zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */
+ zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
+ /* Turn on transmitter/receiver at 8-bits/char */
+ kgdb_chaninit(&zs_soft[tty_num], 0, 9600);
+ ZS_CLEARERR(zs_kgdbchan);
+ ZS_CLEARFIFO(zs_kgdbchan);
+}
diff --git a/drivers/macintosh/macserial.h b/drivers/macintosh/macserial.h
new file mode 100644
index 000000000..83e6dc932
--- /dev/null
+++ b/drivers/macintosh/macserial.h
@@ -0,0 +1,407 @@
+/*
+ * macserial.h: Definitions for the Macintosh Z8530 serial driver.
+ *
+ * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras.
+ *
+ * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _MACSERIAL_H
+#define _MACSERIAL_H
+
+#define NUM_ZSREGS 16
+
+struct serial_struct {
+ int type;
+ int line;
+ int port;
+ int irq;
+ int flags;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int baud_base;
+ unsigned short close_delay;
+ char reserved_char[2];
+ int hub6;
+ unsigned short closing_wait; /* time to wait before closing */
+ unsigned short closing_wait2; /* no longer used... */
+ int reserved[4];
+};
+
+/*
+ * For the close wait times, 0 means wait forever for serial port to
+ * flush its output. 65535 means don't wait at all.
+ */
+#define ZILOG_CLOSING_WAIT_INF 0
+#define ZILOG_CLOSING_WAIT_NONE 65535
+
+/*
+ * Definitions for ZILOG_struct (and serial_struct) flags field
+ */
+#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
+ on the callout port */
+#define ZILOG_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
+#define ZILOG_SAK 0x0004 /* Secure Attention Key (Orange book) */
+#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define ZILOG_SPD_MASK 0x0030
+#define ZILOG_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
+
+#define ZILOG_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
+#define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */
+
+#define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
+#define ZILOG_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
+#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define ZILOG_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
+#define ZILOG_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
+
+#define ZILOG_FLAGS 0x0FFF /* Possible legal ZILOG flags */
+#define ZILOG_USR_MASK 0x0430 /* Legal flags that non-privileged
+ * users can set or reset */
+
+/* Internal flags used only by kernel/chr_drv/serial.c */
+#define ZILOG_INITIALIZED 0x80000000 /* Serial port was initialized */
+#define ZILOG_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
+#define ZILOG_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
+#define ZILOG_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
+#define ZILOG_CLOSING 0x08000000 /* Serial port is closing */
+#define ZILOG_CTS_FLOW 0x04000000 /* Do CTS flow control */
+#define ZILOG_CHECK_CD 0x02000000 /* i.e., CLOCAL */
+
+/* Software state per channel */
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct mac_zschannel {
+ volatile unsigned char *control;
+ volatile unsigned char *data;
+};
+
+struct mac_serial {
+ struct mac_serial *zs_next; /* For IRQ servicing chain */
+ struct mac_zschannel *zs_channel; /* Channel registers */
+ struct mac_zschannel *zs_chan_a; /* A side registers */
+ unsigned char read_reg_zero;
+
+ char soft_carrier; /* Use soft carrier on this channel */
+ char break_abort; /* Is serial console in, so process brk/abrt */
+ char kgdb_channel; /* Kgdb is running on this channel */
+ char is_cons; /* Is this our console. */
+ unsigned char tx_active; /* character is being xmitted */
+ unsigned char tx_stopped; /* output is suspended */
+
+ /* We need to know the current clock divisor
+ * to read the bps rate the chip has currently
+ * loaded.
+ */
+ unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */
+ int zs_baud;
+
+ /* Current write register values */
+ unsigned char curregs[NUM_ZSREGS];
+
+ /* Values we need to set next opportunity */
+ unsigned char pendregs[NUM_ZSREGS];
+
+ char change_needed;
+
+ int magic;
+ int baud_base;
+ int port;
+ int irq;
+ int flags; /* defined in tty.h */
+ int type; /* UART type */
+ struct tty_struct *tty;
+ int read_status_mask;
+ int ignore_status_mask;
+ int timeout;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int x_char; /* xon/xoff character */
+ int close_delay;
+ unsigned short closing_wait;
+ unsigned short closing_wait2;
+ unsigned long event;
+ unsigned long last_active;
+ int line;
+ int count; /* # of fd on device */
+ int blocked_open; /* # of blocked opens */
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ unsigned char *xmit_buf;
+ int xmit_head;
+ int xmit_tail;
+ int xmit_cnt;
+ struct tq_struct tqueue;
+ struct tq_struct tqueue_hangup;
+ struct termios normal_termios;
+ struct termios callout_termios;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+};
+
+
+#define SERIAL_MAGIC 0x5301
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP 0
+
+#endif /* __KERNEL__ */
+
+/* Conversion routines to/from brg time constants from/to bits
+ * per second.
+ */
+#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
+#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+/* The Zilog register set */
+
+#define FLAG 0x7e
+
+/* Write Register 0 */
+#define R0 0 /* Register selects */
+#define R1 1
+#define R2 2
+#define R3 3
+#define R4 4
+#define R5 5
+#define R6 6
+#define R7 7
+#define R8 8
+#define R9 9
+#define R10 10
+#define R11 11
+#define R12 12
+#define R13 13
+#define R14 14
+#define R15 15
+
+#define NULLCODE 0 /* Null Code */
+#define POINT_HIGH 0x8 /* Select upper half of registers */
+#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
+#define SEND_ABORT 0x18 /* HDLC Abort */
+#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
+#define RES_Tx_P 0x28 /* Reset TxINT Pending */
+#define ERR_RES 0x30 /* Error Reset */
+#define RES_H_IUS 0x38 /* Reset highest IUS */
+
+#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
+#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
+#define RES_EOM_L 0xC0 /* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
+#define TxINT_ENAB 0x2 /* Tx Int Enable */
+#define PAR_SPEC 0x4 /* Parity is special condition */
+
+#define RxINT_DISAB 0 /* Rx Int Disable */
+#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
+#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
+#define INT_ERR_Rx 0x18 /* Int on error only */
+
+#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
+#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
+#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define RxENABLE 0x1 /* Rx Enable */
+#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
+#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
+#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
+#define ENT_HM 0x10 /* Enter Hunt Mode */
+#define AUTO_ENAB 0x20 /* Auto Enables */
+#define Rx5 0x0 /* Rx 5 Bits/Character */
+#define Rx7 0x40 /* Rx 7 Bits/Character */
+#define Rx6 0x80 /* Rx 6 Bits/Character */
+#define Rx8 0xc0 /* Rx 8 Bits/Character */
+#define RxNBITS_MASK 0xc0
+
+/* Write Register 4 */
+
+#define PAR_ENA 0x1 /* Parity Enable */
+#define PAR_EVEN 0x2 /* Parity Even/Odd* */
+
+#define SYNC_ENAB 0 /* Sync Modes Enable */
+#define SB1 0x4 /* 1 stop bit/char */
+#define SB15 0x8 /* 1.5 stop bits/char */
+#define SB2 0xc /* 2 stop bits/char */
+#define SB_MASK 0xc
+
+#define MONSYNC 0 /* 8 Bit Sync character */
+#define BISYNC 0x10 /* 16 bit sync character */
+#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
+#define EXTSYNC 0x30 /* External Sync Mode */
+
+#define X1CLK 0x0 /* x1 clock mode */
+#define X16CLK 0x40 /* x16 clock mode */
+#define X32CLK 0x80 /* x32 clock mode */
+#define X64CLK 0xC0 /* x64 clock mode */
+#define XCLK_MASK 0xC0
+
+/* Write Register 5 */
+
+#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
+#define RTS 0x2 /* RTS */
+#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
+#define TxENAB 0x8 /* Tx Enable */
+#define SND_BRK 0x10 /* Send Break */
+#define Tx5 0x0 /* Tx 5 bits (or less)/character */
+#define Tx7 0x20 /* Tx 7 bits/character */
+#define Tx6 0x40 /* Tx 6 bits/character */
+#define Tx8 0x60 /* Tx 8 bits/character */
+#define TxNBITS_MASK 0x60
+#define DTR 0x80 /* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define VIS 1 /* Vector Includes Status */
+#define NV 2 /* No Vector */
+#define DLC 4 /* Disable Lower Chain */
+#define MIE 8 /* Master Interrupt Enable */
+#define STATHI 0x10 /* Status high */
+#define NORESET 0 /* No reset on write to R9 */
+#define CHRB 0x40 /* Reset channel B */
+#define CHRA 0x80 /* Reset channel A */
+#define FHWRES 0xc0 /* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define BIT6 1 /* 6 bit/8bit sync */
+#define LOOPMODE 2 /* SDLC Loop mode */
+#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
+#define MARKIDLE 8 /* Mark/flag on idle */
+#define GAOP 0x10 /* Go active on poll */
+#define NRZ 0 /* NRZ mode */
+#define NRZI 0x20 /* NRZI mode */
+#define FM1 0x40 /* FM1 (transition = 1) */
+#define FM0 0x60 /* FM0 (transition = 0) */
+#define CRCPS 0x80 /* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define TRxCXT 0 /* TRxC = Xtal output */
+#define TRxCTC 1 /* TRxC = Transmit clock */
+#define TRxCBR 2 /* TRxC = BR Generator Output */
+#define TRxCDP 3 /* TRxC = DPLL output */
+#define TRxCOI 4 /* TRxC O/I */
+#define TCRTxCP 0 /* Transmit clock = RTxC pin */
+#define TCTRxCP 8 /* Transmit clock = TRxC pin */
+#define TCBR 0x10 /* Transmit clock = BR Generator output */
+#define TCDPLL 0x18 /* Transmit clock = DPLL output */
+#define RCRTxCP 0 /* Receive clock = RTxC pin */
+#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
+#define RCBR 0x40 /* Receive clock = BR Generator output */
+#define RCDPLL 0x60 /* Receive clock = DPLL output */
+#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define BRENABL 1 /* Baud rate generator enable */
+#define BRSRC 2 /* Baud rate generator source */
+#define DTRREQ 4 /* DTR/Request function */
+#define AUTOECHO 8 /* Auto Echo */
+#define LOOPBAK 0x10 /* Local loopback */
+#define SEARCH 0x20 /* Enter search mode */
+#define RMC 0x40 /* Reset missing clock */
+#define DISDPLL 0x60 /* Disable DPLL */
+#define SSBR 0x80 /* Set DPLL source = BR generator */
+#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
+#define SFMM 0xc0 /* Set FM mode */
+#define SNRZI 0xe0 /* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define ZCIE 2 /* Zero count IE */
+#define DCDIE 8 /* DCD IE */
+#define SYNCIE 0x10 /* Sync/hunt IE */
+#define CTSIE 0x20 /* CTS IE */
+#define TxUIE 0x40 /* Tx Underrun/EOM IE */
+#define BRKIE 0x80 /* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define Rx_CH_AV 0x1 /* Rx Character Available */
+#define ZCOUNT 0x2 /* Zero count */
+#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
+#define DCD 0x8 /* DCD */
+#define SYNC_HUNT 0x10 /* Sync/hunt */
+#define CTS 0x20 /* CTS */
+#define TxEOM 0x40 /* Tx underrun */
+#define BRK_ABRT 0x80 /* Break/Abort */
+
+/* Read Register 1 */
+#define ALL_SNT 0x1 /* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define RES3 0x8 /* 0/3 */
+#define RES4 0x4 /* 0/4 */
+#define RES5 0xc /* 0/5 */
+#define RES6 0x2 /* 0/6 */
+#define RES7 0xa /* 0/7 */
+#define RES8 0x6 /* 0/8 */
+#define RES18 0xe /* 1/8 */
+#define RES28 0x0 /* 2/8 */
+/* Special Rx Condition Interrupts */
+#define PAR_ERR 0x10 /* Parity error */
+#define Rx_OVR 0x20 /* Rx Overrun Error */
+#define FRM_ERR 0x40 /* CRC/Framing Error */
+#define END_FR 0x80 /* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
+#define CHBTxIP 0x2 /* Channel B Tx IP */
+#define CHBRxIP 0x4 /* Channel B Rx IP */
+#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
+#define CHATxIP 0x10 /* Channel A Tx IP */
+#define CHARxIP 0x20 /* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10 (misc status bits) */
+#define ONLOOP 2 /* On loop */
+#define LOOPSEND 0x10 /* Loop sending */
+#define CLK2MIS 0x40 /* Two clocks missing */
+#define CLK1MIS 0x80 /* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* Misc macros */
+#define ZS_CLEARERR(channel) (write_zsreg(channel, 0, ERR_RES))
+#define ZS_CLEARFIFO(channel) do { volatile unsigned char garbage; \
+ garbage = read_zsdata(channel); \
+ garbage = read_zsdata(channel); \
+ garbage = read_zsdata(channel); \
+ } while(0)
+
+#endif /* !(_MACSERIAL_H) */
diff --git a/drivers/macintosh/nvram.c b/drivers/macintosh/nvram.c
new file mode 100644
index 000000000..9bb6166e4
--- /dev/null
+++ b/drivers/macintosh/nvram.c
@@ -0,0 +1,87 @@
+/*
+ * /dev/nvram driver for Power Macintosh.
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/fcntl.h>
+#include <linux/nvram.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#define NVRAM_SIZE 8192
+
+static long long nvram_llseek(struct inode *inode, struct file *file,
+ loff_t offset, int origin)
+{
+ switch (origin) {
+ case 1:
+ offset += file->f_pos;
+ break;
+ case 2:
+ offset += NVRAM_SIZE;
+ break;
+ }
+ if (offset < 0)
+ return -EINVAL;
+ file->f_pos = offset;
+ return file->f_pos;
+}
+
+static long read_nvram(struct inode *inode, struct file *file,
+ char *buf, unsigned long count)
+{
+ unsigned int i = file->f_pos;
+ char *p = buf;
+
+ for (; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count)
+ put_user(nvram_read_byte(i), p);
+ file->f_pos = i;
+ return p - buf;
+}
+
+static long write_nvram(struct inode *inode, struct file *file,
+ const char *buf, unsigned long count)
+{
+ unsigned int i = file->f_pos;
+ const char *p = buf;
+ char c;
+
+ for (; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) {
+ get_user(c, p);
+ nvram_write_byte(i, c);
+ }
+ file->f_pos = i;
+ return p - buf;
+}
+
+static int nvram_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+struct file_operations nvram_fops = {
+ nvram_llseek,
+ read_nvram,
+ write_nvram,
+ NULL, /* nvram_readdir */
+ NULL, /* nvram_select */
+ NULL, /* nvram_ioctl */
+ NULL, /* nvram_mmap */
+ nvram_open,
+ NULL, /* no special release code */
+ NULL /* fsync */
+};
+
+static struct miscdevice nvram_dev = {
+ NVRAM_MINOR,
+ "nvram",
+ &nvram_fops
+};
+
+__initfunc(int nvram_init(void))
+{
+ misc_register(&nvram_dev);
+ return 0;
+}
diff --git a/drivers/macintosh/platinum.c b/drivers/macintosh/platinum.c
new file mode 100644
index 000000000..34f314df7
--- /dev/null
+++ b/drivers/macintosh/platinum.c
@@ -0,0 +1,623 @@
+/*
+ * platinum.c: Console support for PowerMac "platinum" display adaptor.
+ *
+ * Copyright (C) 1996 Paul Mackerras and Mark Abene.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/vc_ioctl.h>
+#include <linux/nvram.h>
+#include <asm/prom.h>
+#include <asm/io.h>
+#include <linux/selection.h>
+#include "pmac-cons.h"
+#include "platinum.h"
+
+/*
+ * Structure of the registers for the DACula colormap device.
+ */
+struct cmap_regs {
+ unsigned char addr;
+ char pad1[15];
+ unsigned char d1;
+ char pad2[15];
+ unsigned char d2;
+ char pad3[15];
+ unsigned char lut;
+ char pad4[15];
+};
+
+/*
+ * Structure of the registers for the "platinum" display adaptor".
+ */
+#define PAD(x) char x[12]
+
+struct preg { /* padded register */
+ unsigned r;
+ char pad[12];
+};
+
+struct platinum_regs {
+ struct preg reg[128];
+};
+
+static void set_platinum_clock(unsigned char *clksel);
+static int read_platinum_sense(void);
+static int platinum_vram_reqd(int vmode, int cmode);
+
+static int total_vram; /* total amount of video memory, bytes */
+static unsigned char *frame_buffer;
+static unsigned char *base_frame_buffer;
+static struct cmap_regs *cmap_regs;
+static volatile struct platinum_regs *plat_regs;
+
+/*
+ * Register initialization tables for the platinum display.
+ *
+ * It seems that there are two different types of platinum display
+ * out there. Older ones use the values in clocksel[1], for which
+ * the formula for the clock frequency seems to be
+ * F = 14.3MHz * c0 / (c1 & 0x1f) / (1 << (c1 >> 5))
+ * Newer ones use the values in clocksel[0], for which the formula
+ * seems to be
+ * F = 15MHz * c0 / ((c1 & 0x1f) + 2) / (1 << (c1 >> 5))
+ */
+struct plat_regvals {
+ int fb_offset;
+ int pitch[3];
+ unsigned regs[26];
+ unsigned char plat_offset[3];
+ unsigned char mode[3];
+ unsigned char dacula_ctrl[3];
+ unsigned char clocksel[2][2];
+};
+
+#define DIV2 0x20
+#define DIV4 0x40
+#define DIV8 0x60
+#define DIV16 0x80
+
+/* 1280x1024, 75Hz (20) */
+static struct plat_regvals platinum_reg_init_20 = {
+ 0x5c00,
+ { 1312, 2592, 2592 },
+ { 0xffc, 4, 0, 0, 0, 0, 0x428, 0,
+ 0, 0xb3, 0xd3, 0x12, 0x1a5, 0x23, 0x28, 0x2d,
+ 0x5e, 0x19e, 0x1a4, 0x854, 0x852, 4, 9, 0x50,
+ 0x850, 0x851 }, { 0x58, 0x5d, 0x5d },
+ { 0, 0xff, 0xff }, { 0x51, 0x55, 0x55 },
+ {{ 45, 3 }, { 66, 7 }}
+};
+
+/* 1280x960, 75Hz (19) */
+static struct plat_regvals platinum_reg_init_19 = {
+ 0x5c00,
+ { 1312, 2592, 2592 },
+ { 0xffc, 4, 0, 0, 0, 0, 0x428, 0,
+ 0, 0xb2, 0xd2, 0x12, 0x1a3, 0x23, 0x28, 0x2d,
+ 0x5c, 0x19c, 0x1a2, 0x7d0, 0x7ce, 4, 9, 0x4c,
+ 0x7cc, 0x7cd }, { 0x56, 0x5b, 0x5b },
+ { 0, 0xff, 0xff }, { 0x51, 0x55, 0x55 },
+ {{ 42, 3 }, { 44, 5 }}
+};
+
+/* 1152x870, 75Hz (18) */
+static struct plat_regvals platinum_reg_init_18 = {
+ 0x11b0,
+ { 1184, 2336, 4640 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x38f, 0,
+ 0, 0x294, 0x16c, 0x20, 0x2d7, 0x3f, 0x49, 0x53,
+ 0x82, 0x2c2, 0x2d6, 0x726, 0x724, 4, 9, 0x52,
+ 0x71e, 0x722 }, { 0x74, 0x7c, 0x81 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 26, 0 + DIV2 }, { 42, 6 }}
+};
+
+/* 1024x768, 75Hz (17) */
+static struct plat_regvals platinum_reg_init_17 = {
+ 0x10b0,
+ { 1056, 2080, 4128 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0x254, 0x14b, 0x18, 0x295, 0x2f, 0x32, 0x3b,
+ 0x80, 0x280, 0x296, 0x648, 0x646, 4, 9, 0x40,
+ 0x640, 0x644 }, { 0x72, 0x7a, 0x7f },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 54, 3 + DIV2 }, { 67, 12 }}
+};
+
+/* 1024x768, 75Hz (16) */
+static struct plat_regvals platinum_reg_init_16 = {
+ 0x10b0,
+ { 1056, 2080, 4128 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0x250, 0x147, 0x17, 0x28f, 0x2f, 0x35, 0x47,
+ 0x82, 0x282, 0x28e, 0x640, 0x63e, 4, 9, 0x3c,
+ 0x63c, 0x63d }, { 0x74, 0x7c, 0x81 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 20, 0 + DIV2 }, { 11, 2 }}
+};
+
+/* 1024x768, 70Hz (15) */
+static struct plat_regvals platinum_reg_init_15 = {
+ 0x10b0,
+ { 1056, 2080, 4128 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0x254, 0x14b, 0x22, 0x297, 0x43, 0x49, 0x5b,
+ 0x86, 0x286, 0x296, 0x64c, 0x64a, 0xa, 0xf, 0x44,
+ 0x644, 0x646 }, { 0x78, 0x80, 0x85 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 19, 0 + DIV2 }, { 110, 21 }}
+};
+
+/* 1024x768, 60Hz (14) */
+static struct plat_regvals platinum_reg_init_14 = {
+ 0x10b0,
+ { 1056, 2080, 4128 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0x25a, 0x14f, 0x22, 0x29f, 0x43, 0x49, 0x5b,
+ 0x8e, 0x28e, 0x29e, 0x64c, 0x64a, 0xa, 0xf, 0x44,
+ 0x644, 0x646 }, { 0x80, 0x88, 0x8d },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 71, 6 + DIV2 }, { 118, 13 + DIV2 }}
+};
+
+/* 832x624, 75Hz (13) */
+static struct plat_regvals platinum_reg_init_13 = {
+ 0x70,
+ { 864, 1680, 3360 }, /* MacOS does 1680 instead of 1696 to fit 16bpp in 1MB */
+ { 0xff0, 4, 0, 0, 0, 0, 0x299, 0,
+ 0, 0x21e, 0x120, 0x10, 0x23f, 0x1f, 0x25, 0x37,
+ 0x8a, 0x22a, 0x23e, 0x536, 0x534, 4, 9, 0x52,
+ 0x532, 0x533 }, { 0x7c, 0x84, 0x89 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 30, 0 + DIV4 }, { 56, 7 + DIV2 }}
+};
+
+/* 800x600, 75Hz (12) */
+static struct plat_regvals platinum_reg_init_12 = {
+ 0x1010,
+ { 832, 1632, 3232 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0x1ce, 0x108, 0x14, 0x20f, 0x27, 0x30, 0x39,
+ 0x72, 0x202, 0x20e, 0x4e2, 0x4e0, 4, 9, 0x2e,
+ 0x4de, 0x4df }, { 0x64, 0x6c, 0x71 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 122, 7 + DIV4 }, { 62, 9 + DIV2 }}
+};
+
+/* 800x600, 72Hz (11) */
+static struct plat_regvals platinum_reg_init_11 = {
+ 0x1010,
+ { 832, 1632, 3232 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0x1ca, 0x104, 0x1e, 0x207, 0x3b, 0x44, 0x4d,
+ 0x56, 0x1e6, 0x206, 0x534, 0x532, 0xa, 0xe, 0x38,
+ 0x4e8, 0x4ec }, { 0x48, 0x50, 0x55 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 26, 0 + DIV4 }, { 42, 6 + DIV2 }}
+};
+
+/* 800x600, 60Hz (10) */
+static struct plat_regvals platinum_reg_init_10 = {
+ 0x1010,
+ { 832, 1632, 3232 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0x1ce, 0x108, 0x20, 0x20f, 0x3f, 0x45, 0x5d,
+ 0x66, 0x1f6, 0x20e, 0x4e8, 0x4e6, 6, 0xa, 0x34,
+ 0x4e4, 0x4e5 }, { 0x58, 0x60, 0x65 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 54, 3 + DIV4 }, { 95, 1 + DIV8 }}
+};
+
+/* 800x600, 56Hz (9) --unsupported? copy of mode 10 for now... */
+static struct plat_regvals platinum_reg_init_9 = {
+ 0x1010,
+ { 832, 1632, 3232 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0x1ce, 0x108, 0x20, 0x20f, 0x3f, 0x45, 0x5d,
+ 0x66, 0x1f6, 0x20e, 0x4e8, 0x4e6, 6, 0xa, 0x34,
+ 0x4e4, 0x4e5 }, { 0x58, 0x60, 0x65 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 54, 3 + DIV4 }, { 88, 1 + DIV8 }}
+};
+
+/* 768x576, 50Hz Interlaced-PAL (8) */
+static struct plat_regvals platinum_reg_init_8 = {
+ 0x1010,
+ { 800, 1568, 3104 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0xc8, 0xec, 0x11, 0x1d7, 0x22, 0x25, 0x36,
+ 0x47, 0x1c7, 0x1d6, 0x271, 0x270, 4, 9, 0x27,
+ 0x267, 0x26b }, { 0x39, 0x41, 0x46 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 31, 0 + DIV16 }, { 74, 9 + DIV8 }}
+};
+
+/* 640x870, 75Hz Portrait (7) */
+static struct plat_regvals platinum_reg_init_7 = {
+ 0xb10,
+ { 672, 1312, 2592 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0x176, 0xd0, 0x14, 0x19f, 0x27, 0x2d, 0x3f,
+ 0x4a, 0x18a, 0x19e, 0x72c, 0x72a, 4, 9, 0x58,
+ 0x724, 0x72a }, { 0x3c, 0x44, 0x49 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 30, 0 + DIV4 }, { 56, 7 + DIV2 }}
+};
+
+/* 640x480, 67Hz (6) */
+static struct plat_regvals platinum_reg_init_6 = {
+ 0x1010,
+ { 672, 1312, 2592 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x209, 0,
+ 0, 0x18e, 0xd8, 0x10, 0x1af, 0x1f, 0x25, 0x37,
+ 0x4a, 0x18a, 0x1ae, 0x41a, 0x418, 4, 9, 0x52,
+ 0x412, 0x416 }, { 0x3c, 0x44, 0x49 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 99, 4 + DIV8 }, { 42, 5 + DIV4 }}
+};
+
+/* 640x480, 60Hz (5) */
+static struct plat_regvals platinum_reg_init_5 = {
+ 0x1010,
+ { 672, 1312, 2592 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0x15e, 0xc8, 0x18, 0x18f, 0x2f, 0x35, 0x3e,
+ 0x42, 0x182, 0x18e, 0x41a, 0x418, 2, 7, 0x44,
+ 0x404, 0x408 }, { 0x34, 0x3c, 0x41 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 26, 0 + DIV8 }, { 14, 2 + DIV4 }}
+};
+
+/* 640x480, 60Hz Interlaced-NTSC (4) */
+static struct plat_regvals platinum_reg_init_4 = {
+ 0x1010,
+ { 672, 1312, 2592 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0xa5, 0xc3, 0xe, 0x185, 0x1c, 0x1f, 0x30,
+ 0x37, 0x177, 0x184, 0x20d, 0x20c, 5, 0xb, 0x23,
+ 0x203, 0x206 }, { 0x29, 0x31, 0x36 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 94, 5 + DIV16 }, { 48, 7 + DIV8 }}
+};
+
+/* 640x480, 50Hz Interlaced-PAL (3) */
+static struct plat_regvals platinum_reg_init_3 = {
+ 0x1010,
+ { 672, 1312, 2592 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0xc8, 0xec, 0x11, 0x1d7, 0x22, 0x25, 0x36,
+ 0x67, 0x1a7, 0x1d6, 0x271, 0x270, 4, 9, 0x57,
+ 0x237, 0x26b }, { 0x59, 0x61, 0x66 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 31, 0 + DIV16 }, { 74, 9 + DIV8 }}
+};
+
+/* 512x384, 60Hz (2) */
+static struct plat_regvals platinum_reg_init_2 = {
+ 0x1010,
+ { 544, 1056, 2080 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0x25c, 0x140, 0x10, 0x27f, 0x1f, 0x2b, 0x4f,
+ 0x68, 0x268, 0x27e, 0x32e, 0x32c, 4, 9, 0x2a,
+ 0x32a, 0x32b }, { 0x5a, 0x62, 0x67 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 33, 2 + DIV8 }, { 79, 9 + DIV8 }}
+};
+
+/* 512x384, 60Hz Interlaced-NTSC (1) */
+static struct plat_regvals platinum_reg_init_1 = {
+ 0x1010,
+ { 544, 1056, 2080 },
+ { 0xff0, 4, 0, 0, 0, 0, 0x320, 0,
+ 0, 0xa5, 0xc3, 0xe, 0x185, 0x1c, 0x1f, 0x30,
+ 0x57, 0x157, 0x184, 0x20d, 0x20c, 5, 0xb, 0x53,
+ 0x1d3, 0x206 }, { 0x49, 0x51, 0x56 },
+ { 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
+ {{ 94, 5 + DIV16 }, { 48, 7 + DIV8 }}
+};
+
+static struct plat_regvals *platinum_reg_init[VMODE_MAX] = {
+ &platinum_reg_init_1,
+ &platinum_reg_init_2,
+ &platinum_reg_init_3,
+ &platinum_reg_init_4,
+ &platinum_reg_init_5,
+ &platinum_reg_init_6,
+ &platinum_reg_init_7,
+ &platinum_reg_init_8,
+ &platinum_reg_init_9,
+ &platinum_reg_init_10,
+ &platinum_reg_init_11,
+ &platinum_reg_init_12,
+ &platinum_reg_init_13,
+ &platinum_reg_init_14,
+ &platinum_reg_init_15,
+ &platinum_reg_init_16,
+ &platinum_reg_init_17,
+ &platinum_reg_init_18,
+ &platinum_reg_init_19,
+ &platinum_reg_init_20
+};
+
+/*
+ * Get the monitor sense value.
+ */
+static int
+read_platinum_sense()
+{
+ int sense;
+
+ plat_regs->reg[23].r = 7; /* turn off drivers */
+ eieio(); __delay(2000);
+ sense = (~plat_regs->reg[23].r & 7) << 8;
+
+ /* drive each sense line low in turn and collect the other 2 */
+ plat_regs->reg[23].r = 3; /* drive A low */
+ eieio(); __delay(2000);
+ sense |= (~plat_regs->reg[23].r & 3) << 4;
+ eieio();
+ plat_regs->reg[23].r = 5; /* drive B low */
+ eieio(); __delay(2000);
+ sense |= (~plat_regs->reg[23].r & 4) << 1;
+ sense |= (~plat_regs->reg[23].r & 1) << 2;
+ eieio();
+ plat_regs->reg[23].r = 6; /* drive C low */
+ eieio(); __delay(2000);
+ sense |= (~plat_regs->reg[23].r & 6) >> 1;
+ eieio();
+
+ plat_regs->reg[23].r = 7; /* turn off drivers */
+ return sense;
+}
+
+static inline int platinum_vram_reqd(int vmode, int cmode)
+{
+ return vmode_attrs[vmode-1].vres
+ * platinum_reg_init[vmode-1]->pitch[cmode];
+}
+
+void
+map_platinum(struct device_node *dp)
+{
+ int i, sense;
+ unsigned long addr, size;
+ int bank0, bank1, bank2, bank3;
+
+ if (dp->next != 0)
+ printk("Warning: only using first platinum display device\n");
+ if (dp->n_addrs != 2)
+ panic("expecting 2 addresses for platinum (got %d)",
+ dp->n_addrs);
+
+ /* Map in frame buffer and registers */
+ for (i = 0; i < dp->n_addrs; ++i) {
+ addr = dp->addrs[i].address;
+ size = dp->addrs[i].size;
+ if (size >= 0x400000) {
+ /* frame buffer - map only 4MB */
+ frame_buffer = ioremap(addr, 0x400000);
+ base_frame_buffer = frame_buffer;
+ } else {
+ /* registers */
+ plat_regs = ioremap(addr, size);
+ }
+ }
+ cmap_regs = ioremap(0xf301b000, 0x1000); /* XXX not in prom? */
+
+ /* Grok total video ram */
+ plat_regs->reg[16].r = (unsigned)frame_buffer;
+ plat_regs->reg[20].r = 0x1011; /* select max vram */
+ plat_regs->reg[24].r = 0; /* switch in vram */
+ eieio();
+ frame_buffer[0x100000] = 0x34;
+ frame_buffer[0x200000] = 0x56;
+ frame_buffer[0x300000] = 0x78;
+ eieio();
+ bank0 = 1; /* builtin 1MB vram, always there */
+ bank1 = frame_buffer[0x100000] == 0x34;
+ bank2 = frame_buffer[0x200000] == 0x56;
+ bank3 = frame_buffer[0x300000] == 0x78;
+ total_vram = (bank0 + bank1 + bank2 + bank3) * 0x100000;
+ printk("Total VRAM = %dMB\n", total_vram / 1024 / 1024);
+
+ sense = read_platinum_sense();
+ if (video_mode == VMODE_NVRAM) {
+ video_mode = nvram_read_byte(NV_VMODE);
+ if (video_mode <= 0 || video_mode > VMODE_MAX
+ || platinum_reg_init[video_mode-1] == 0)
+ video_mode = VMODE_CHOOSE;
+ }
+ if (video_mode == VMODE_CHOOSE)
+ video_mode = map_monitor_sense(sense);
+ if (platinum_reg_init[video_mode-1] == 0)
+ video_mode = VMODE_640_480_60;
+ printk("Monitor sense value = 0x%x, ", sense);
+
+ if (color_mode == CMODE_NVRAM)
+ color_mode = nvram_read_byte(NV_CMODE);
+ if (color_mode < CMODE_8 || color_mode > CMODE_32)
+ color_mode = CMODE_8;
+ /*
+ * Reduce the pixel size if we don't have enough VRAM.
+ */
+ while (color_mode > CMODE_8
+ && platinum_vram_reqd(video_mode, color_mode) > total_vram)
+ --color_mode;
+ /*
+ * Reduce the video mode if we don't have enough VRAM.
+ */
+ while (platinum_vram_reqd(video_mode, color_mode) > total_vram)
+ --video_mode;
+}
+
+#define STORE_D2(a, v) { \
+ out_8(&cmap_regs->addr, (a+32)); \
+ out_8(&cmap_regs->d2, (v)); \
+}
+
+static void
+set_platinum_clock(unsigned char *clksel)
+{
+ STORE_D2(6, 0xc6);
+ out_8(&cmap_regs->addr, 3+32);
+ if (cmap_regs->d2 == 2) {
+ STORE_D2(7, clksel[0]);
+ STORE_D2(8, clksel[1]);
+ STORE_D2(3, 3);
+ } else {
+ STORE_D2(4, clksel[0]);
+ STORE_D2(5, clksel[1]);
+ STORE_D2(3, 2);
+ }
+ __delay(5000);
+ STORE_D2(9, 0xa6);
+}
+
+void
+platinum_init()
+{
+ int i, yoff, width, clkmode, dtype;
+ struct plat_regvals *init;
+ unsigned *p;
+ int one_mb = 0;
+
+ if (total_vram == 0x100000) one_mb=1;
+
+ if (video_mode <= 0 || video_mode > VMODE_MAX
+ || (init = platinum_reg_init[video_mode-1]) == 0)
+ panic("platinum: video mode %d not supported", video_mode);
+
+ frame_buffer = base_frame_buffer + init->fb_offset;
+ /* printk("Frame buffer start address is %p\n", frame_buffer); */
+
+ pixel_size = 1 << color_mode;
+ line_pitch = init->pitch[color_mode];
+ width = vmode_attrs[video_mode-1].hres;
+ n_scanlines = vmode_attrs[video_mode-1].vres;
+ row_pitch = line_pitch * 16;
+
+ /* Initialize display timing registers */
+ out_be32(&plat_regs->reg[24].r, 7); /* turn display off */
+
+ for (i = 0; i < 26; ++i)
+ plat_regs->reg[i+32].r = init->regs[i];
+ plat_regs->reg[26+32].r = (one_mb ? init->plat_offset[color_mode] + 4 - color_mode : init->plat_offset[color_mode]);
+ plat_regs->reg[16].r = (unsigned) frame_buffer;
+ plat_regs->reg[18].r = line_pitch;
+ plat_regs->reg[19].r = (one_mb ? init->mode[color_mode+1] : init->mode[color_mode]);
+ plat_regs->reg[20].r = (one_mb ? 0x11 : 0x1011);
+ plat_regs->reg[21].r = 0x100;
+ plat_regs->reg[22].r = 1;
+ plat_regs->reg[23].r = 1;
+ plat_regs->reg[26].r = 0xc00;
+ plat_regs->reg[27].r = 0x235;
+ /* plat_regs->reg[27].r = 0x2aa; */
+
+ STORE_D2(0, (one_mb ? init->dacula_ctrl[color_mode] & 0xf : init->dacula_ctrl[color_mode]));
+ STORE_D2(1, 4);
+ STORE_D2(2, 0);
+ /*
+ * Try to determine whether we have an old or a new DACula.
+ */
+ out_8(&cmap_regs->addr, 0x40);
+ dtype = cmap_regs->d2;
+ switch (dtype) {
+ case 0x3c:
+ clkmode = 1;
+ break;
+ case 0x84:
+ clkmode = 0;
+ break;
+ default:
+ clkmode = 0;
+ printk("Unknown DACula type: %x\n", cmap_regs->d2);
+ }
+ set_platinum_clock(init->clocksel[clkmode]);
+
+ out_be32(&plat_regs->reg[24].r, 0); /* turn display on */
+
+ pmac_init_palette();
+
+ yoff = (n_scanlines % 16) / 2;
+ fb_start = frame_buffer + yoff * line_pitch + 0x10;
+
+ /* Clear screen */
+ p = (unsigned *) (frame_buffer + 0x10);
+ for (i = n_scanlines * line_pitch / sizeof(unsigned); i != 0; --i)
+ *p++ = 0;
+
+ display_info.height = n_scanlines;
+ display_info.width = width;
+ display_info.depth = 8 * pixel_size;
+ display_info.pitch = line_pitch;
+ display_info.mode = video_mode;
+ strncpy(display_info.name, "platinum", sizeof(display_info.name));
+ display_info.fb_address = (unsigned long) frame_buffer + 0x10;
+ display_info.cmap_adr_address = (unsigned long) &cmap_regs->addr;
+ display_info.cmap_data_address = (unsigned long) &cmap_regs->lut;
+ display_info.disp_reg_address = (unsigned long) &plat_regs;
+}
+
+int
+platinum_setmode(struct vc_mode *mode, int doit)
+{
+ int cmode;
+
+ if (mode->mode <= 0 || mode->mode > VMODE_MAX
+ || platinum_reg_init[mode->mode-1] == 0)
+ return -EINVAL;
+ if (mode->depth != 8 && mode->depth != 16 && mode->depth != 24 && mode->depth != 32)
+ return -EINVAL;
+
+ switch (mode->depth) {
+ case 24:
+ case 32:
+ cmode = CMODE_32;
+ break;
+ case 16:
+ cmode = CMODE_16;
+ break;
+ case 8:
+ cmode = CMODE_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (platinum_vram_reqd(mode->mode, cmode) > total_vram)
+ return -EINVAL;
+
+ if (doit) {
+ video_mode = mode->mode;
+ color_mode = cmode;
+ platinum_init();
+ }
+ return 0;
+}
+
+void
+platinum_set_palette(unsigned char red[], unsigned char green[],
+ unsigned char blue[], int index, int ncolors)
+{
+ int i;
+
+ for (i = 0; i < ncolors; ++i) {
+ cmap_regs->addr = index + i; eieio();
+ cmap_regs->lut = red[i]; eieio();
+ cmap_regs->lut = green[i]; eieio();
+ cmap_regs->lut = blue[i]; eieio();
+ }
+}
+
+void
+platinum_set_blanking(int blank_mode)
+{
+}
diff --git a/drivers/macintosh/platinum.h b/drivers/macintosh/platinum.h
new file mode 100644
index 000000000..530a89c4c
--- /dev/null
+++ b/drivers/macintosh/platinum.h
@@ -0,0 +1,17 @@
+/*
+ * Exported procedures for the PowerMac "platinum" display adaptor.
+ *
+ * Copyright (C) 1996 Paul Mackerras and Mark Abene.
+ *
+ * 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.
+ */
+
+extern void map_platinum(struct device_node *);
+extern void platinum_init(void);
+extern int platinum_setmode(struct vc_mode *mode, int doit);
+extern void platinum_set_palette(unsigned char red[], unsigned char green[],
+ unsigned char blue[], int index, int ncolors);
+extern void platinum_set_blanking(int blank_mode);
diff --git a/drivers/macintosh/pmac-cons.c b/drivers/macintosh/pmac-cons.c
new file mode 100644
index 000000000..8b7f08328
--- /dev/null
+++ b/drivers/macintosh/pmac-cons.c
@@ -0,0 +1,1312 @@
+/*
+ * pmac-cons.c: Console support for PowerMac (PCI-based).
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ * 7200/Platinum code hacked by Mark Abene.
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/vc_ioctl.h>
+#include <linux/kd.h>
+#include <linux/version.h>
+#include <asm/processor.h>
+#include <asm/prom.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/cuda.h>
+#include <asm/linux_logo.h>
+#include <linux/selection.h>
+#include <linux/console_struct.h>
+#include <linux/vt_kern.h>
+#include "pmac-cons.h"
+#include "control.h"
+#include "platinum.h"
+#include "valkyrie.h"
+#ifdef CONFIG_ATY_VIDEO
+#include "aty.h"
+#endif
+#ifdef CONFIG_IMSTT_VIDEO
+#include "imstt.h"
+#endif
+
+int video_mode = VMODE_NVRAM;
+int color_mode = CMODE_NVRAM;
+
+/*
+ * The format of "screen_info" is strange, and due to early
+ * i386-setup code. This is just enough to make the console
+ * code think we're on a EGA+ colour display.
+ */
+struct screen_info screen_info = {
+ 0, 0, /* orig-x, orig-y */
+ {0, 0}, /* unused1 */
+ 0, /* orig-video-page */
+ 0, /* orig-video-mode */
+ 80, /* orig-video-cols */
+ 0, /* unused [short] */
+ 0, /* ega_bx */
+ 0, /* unused [short] */
+ 25, /* orig-video-lines */
+ 0, /* isVGA */
+ 16 /* video points */
+};
+
+/*
+ * We allocate enough character+attribute memory for the largest
+ * screen resolution supported.
+ */
+#define MAX_TEXT_COLS (1280/8)
+#define MAX_TEXT_ROWS (1024/16)
+
+/*
+ * We get a sense value from the monitor and use it to choose
+ * what resolution to use. This structure maps sense values
+ * to display mode values (which determine the resolution and
+ * frequencies).
+ */
+struct mon_map {
+ int sense;
+ int vmode;
+} monitor_map [] = {
+ {0x000, VMODE_1280_1024_75}, /* 21" RGB */
+ {0x114, VMODE_640_870_75P}, /* Portrait Monochrome */
+ {0x221, VMODE_512_384_60}, /* 12" RGB*/
+ {0x331, VMODE_1280_1024_75}, /* 21" RGB (Radius) */
+ {0x334, VMODE_1280_1024_75}, /* 21" mono (Radius) */
+ {0x335, VMODE_1280_1024_75}, /* 21" mono */
+ {0x40A, VMODE_640_480_60I}, /* NTSC */
+ {0x51E, VMODE_640_870_75P}, /* Portrait RGB */
+ {0x603, VMODE_832_624_75}, /* 12"-16" multiscan */
+ {0x60b, VMODE_1024_768_70}, /* 13"-19" multiscan */
+ {0x623, VMODE_1152_870_75}, /* 13"-21" multiscan */
+ {0x62b, VMODE_640_480_67}, /* 13"/14" RGB */
+ {0x700, VMODE_640_480_50I}, /* PAL */
+ {0x714, VMODE_640_480_60I}, /* NTSC */
+ {0x717, VMODE_800_600_75}, /* VGA */
+ {0x72d, VMODE_832_624_75}, /* 16" RGB (Goldfish) */
+ {0x730, VMODE_768_576_50I}, /* PAL (Alternate) */
+ {0x73a, VMODE_1152_870_75}, /* 3rd party 19" */
+ {-1, VMODE_640_480_60}, /* catch-all, must be last */
+};
+
+int
+map_monitor_sense(int sense)
+{
+ struct mon_map *map;
+
+ for (map = monitor_map; map->sense >= 0; ++map)
+ if (map->sense == sense)
+ break;
+ return map->vmode;
+}
+
+/*
+ * Horizontal and vertical resolution for each mode.
+ */
+struct vmode_attr vmode_attrs[VMODE_MAX] = {
+ {512, 384, 60, 1},
+ {512, 384, 60},
+ {640, 480, 50, 1},
+ {640, 480, 60, 1},
+ {640, 480, 60},
+ {640, 480, 67},
+ {640, 870, 75},
+ {768, 576, 50, 1},
+ {800, 600, 56},
+ {800, 600, 60},
+ {800, 600, 72},
+ {800, 600, 75},
+ {832, 624, 75},
+ {1024, 768, 60},
+ {1024, 768, 72},
+ {1024, 768, 75},
+ {1024, 768, 75},
+ {1152, 870, 75},
+ {1280, 960, 75},
+ {1280, 1024, 75}
+};
+
+static void invert_cursor(int);
+static int map_unknown(struct device_node *);
+static void unknown_init(void);
+
+struct display_interface {
+ char *name;
+ void (*map_interface)(struct device_node *);
+ void (*init_interface)(void);
+ int (*setmode)(struct vc_mode *, int);
+ void (*set_palette)(unsigned char red[], unsigned char green[],
+ unsigned char blue[], int index, int ncolors);
+ void (*set_blanking)(int blank_mode);
+} displays[] = {
+ { "control", map_control_display, control_init,
+ control_setmode, control_set_palette, control_set_blanking },
+ { "platinum", map_platinum, platinum_init,
+ platinum_setmode, platinum_set_palette, platinum_set_blanking },
+ { "valkyrie", map_valkyrie_display, valkyrie_init,
+ valkyrie_setmode, valkyrie_set_palette, valkyrie_set_blanking },
+#ifdef CONFIG_ATY_VIDEO
+ { "ATY,mach64", map_aty_display, aty_init,
+ aty_setmode, aty_set_palette, aty_set_blanking },
+ { "ATY,XCLAIM", map_aty_display, aty_init,
+ aty_setmode, aty_set_palette, aty_set_blanking },
+ { "ATY,264VT", map_aty_display, aty_init,
+ aty_setmode, aty_set_palette, aty_set_blanking },
+ { "ATY,mach64ii", map_aty_display, aty_init,
+ aty_setmode, aty_set_palette, aty_set_blanking },
+#if 0 /* not right for 3D mach64 yet */
+ { "ATY,264GT-B", map_aty_display, aty_init,
+ aty_setmode, aty_set_palette, aty_set_blanking },
+ { "ATY,mach64_3D_pcc", map_aty_display, aty_init,
+ aty_setmode, aty_set_palette },
+#endif
+#endif
+#ifdef CONFIG_IMSTT_VIDEO
+ { "IMS,tt128mb", map_imstt_display, imstt_init,
+ imstt_setmode, imstt_set_palette, imstt_set_blanking },
+#endif
+ { NULL }
+};
+
+struct display_interface unknown_display = {
+ "unknown", NULL, unknown_init, NULL, NULL
+};
+
+static struct display_interface *current_display;
+
+static int cursor_pos = -1;
+static unsigned *cursor_fb; /* address of cursor pos in frame buffer */
+static unsigned cursor_bits; /* bits changed to turn cursor on */
+
+int pixel_size; /* in bytes */
+int n_scanlines; /* # of scan lines */
+int line_pitch; /* # bytes in 1 scan line */
+int row_pitch; /* # bytes in 1 row of characters */
+unsigned char *fb_start; /* addr of top left pixel of top left char */
+struct vc_mode display_info;
+
+extern int screen_initialized; /* in arch/ppc/pmac/prom.c */
+
+#define cmapsz (16*256)
+extern unsigned char vga_font[cmapsz];
+
+static inline unsigned pixel32(int currcons, int cidx)
+{
+ cidx *= 3;
+ return (palette[cidx] << 16) | (palette[cidx + 1] << 8)
+ | palette[cidx + 2];
+}
+
+static inline unsigned pixel16(int currcons, int cidx)
+{
+ unsigned p;
+
+ p = ((cidx << 10) & 0x7c00) + ((cidx << 5) & 0x03e0)
+ + (cidx & 0x1f);
+ return (p << 16) | p;
+}
+
+void
+__set_origin(unsigned short offset)
+{
+}
+
+static void
+invert_cursor(int cpos)
+{
+ int row, col;
+ int l, c, nw;
+ unsigned *fb, mask;
+ int currcons = fg_console; /* for `color', which is a macro */
+
+ if (cpos == -1) {
+ /* turning cursor off */
+ fb = cursor_fb;
+ mask = cursor_bits;
+ } else {
+ row = cpos / video_num_columns;
+ col = cpos - row * video_num_columns;
+ fb = (unsigned *) (fb_start + row * row_pitch
+ + col * pixel_size * 8);
+ switch (color_mode) {
+ case CMODE_16:
+ mask = pixel16(currcons, foreground)
+ ^ pixel16(currcons, background >> 4);
+ break;
+ default:
+ mask = (color ^ (color >> 4)) & 0xf;
+ mask |= mask << 8;
+ mask |= mask << 16;
+ break;
+ }
+ cursor_fb = fb;
+ cursor_bits = mask;
+ }
+ nw = pixel_size * 2; /* pixel_size * 8 (char width) / 4 */
+ for (l = 0; l < 16; ++l) {
+ for (c = 0; c < nw; ++c)
+ fb[c] ^= mask;
+ fb = (unsigned *) ((char *)fb + line_pitch);
+ }
+}
+
+void
+hide_cursor()
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (cursor_pos != -1) {
+ invert_cursor(-1);
+ cursor_pos = -1;
+ }
+ restore_flags(flags);
+}
+
+void
+set_cursor(int currcons)
+{
+ unsigned long flags;
+ int old_cursor;
+
+ if (currcons != fg_console || console_blanked)
+ return;
+
+ save_flags(flags);
+ cli();
+ if (!deccm) {
+ hide_cursor();
+ } else {
+ old_cursor = cursor_pos;
+ cursor_pos = (pos - video_mem_base) >> 1;
+ if (old_cursor != -1)
+ invert_cursor(-1);
+ invert_cursor(cursor_pos);
+ }
+ restore_flags(flags);
+}
+
+/*
+ * NOTE: get_scrmem() and set_scrmem() are here only because
+ * the VGA version of set_scrmem() has some direct VGA references.
+ */
+void
+get_scrmem(int currcons)
+{
+ memcpyw((unsigned short *)vc_scrbuf[currcons],
+ (unsigned short *)origin, video_screen_size);
+ origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
+ scr_end = video_mem_end = video_mem_start + video_screen_size;
+ pos = origin + y*video_size_row + (x<<1);
+}
+
+void
+set_scrmem(int currcons, long offset)
+{
+ if (video_mem_term - video_mem_base < offset + video_screen_size)
+ offset = 0;
+ memcpyw((unsigned short *)(video_mem_base + offset),
+ (unsigned short *) origin, video_screen_size);
+ video_mem_start = video_mem_base;
+ video_mem_end = video_mem_term;
+ origin = video_mem_base + offset;
+ scr_end = origin + video_screen_size;
+ pos = origin + y*video_size_row + (x<<1);
+}
+
+int
+set_get_cmap(unsigned char *p, int set)
+{
+ int i, j, err;
+
+ err = verify_area(set? VERIFY_READ: VERIFY_WRITE, p, 48);
+ if (err)
+ return err;
+
+ for (i = 0; i < 16; ++i) {
+ if (set) {
+ get_user(default_red[i], p++);
+ get_user(default_grn[i], p++);
+ get_user(default_blu[i], p++);
+ } else {
+ put_user(default_red[i], p++);
+ put_user(default_grn[i], p++);
+ put_user(default_blu[i], p++);
+ }
+ }
+
+ if (set) {
+ for (j = 0; j < MAX_NR_CONSOLES; ++j) {
+ if (!vc_cons_allocated(j))
+ continue;
+ for (i = 0; i < 16; ++i) {
+ vc_cons[j].d->vc_palette[3*i+0] = default_red[i];
+ vc_cons[j].d->vc_palette[3*i+1] = default_grn[i];
+ vc_cons[j].d->vc_palette[3*i+2] = default_blu[i];
+ }
+ }
+ set_palette();
+ }
+
+ return 0;
+}
+
+int
+set_get_font(char *p, int set, int ch512)
+{
+ return 0;
+}
+
+void
+set_palette()
+{
+ int i, j, n;
+ unsigned char red[16], green[16], blue[16];
+
+ if (console_blanked || current_display == NULL
+ || current_display->set_palette == NULL
+ || vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+ return;
+
+ for (i = j = 0; i < 16; ++i) {
+ n = color_table[i] & 0xf;
+ red[n] = vc_cons[fg_console].d->vc_palette[j++];
+ green[n] = vc_cons[fg_console].d->vc_palette[j++];
+ blue[n] = vc_cons[fg_console].d->vc_palette[j++];
+ }
+ (*current_display->set_palette)(red, green, blue, 0, 16);
+}
+
+void
+pmac_init_palette()
+{
+ int i, n;
+ unsigned char red[16], green[16], blue[16];
+
+ for (i = 0; i < 16; ++i) {
+ n = color_table[i] & 0xf;
+ red[n] = default_red[i];
+ green[n] = default_grn[i];
+ blue[n] = default_blu[i];
+ }
+ (*current_display->set_palette)(red, green, blue, 0, 16);
+}
+
+static int vesa_blanking_mode;
+static int vesa_blanked;
+
+void
+vesa_blank()
+{
+ if (vesa_blanking_mode == 0 || vesa_blanked
+ || current_display == NULL
+ || current_display->set_blanking == NULL)
+ return;
+ (*current_display->set_blanking)(vesa_blanking_mode);
+ vesa_blanked = vesa_blanking_mode;
+}
+
+void
+vesa_unblank()
+{
+ if (vesa_blanked == 0
+ || current_display == NULL
+ || current_display->set_blanking == NULL)
+ return;
+ (*current_display->set_blanking)(VESA_NO_BLANKING);
+ vesa_blanked = VESA_NO_BLANKING;
+}
+
+void
+set_vesa_blanking(const unsigned long arg)
+{
+ unsigned char *argp = (unsigned char *)(arg + 1);
+ unsigned int mode;
+
+ if (verify_area(VERIFY_READ, argp, 1))
+ return;
+
+ get_user(mode, argp);
+ vesa_blanking_mode = (mode <= VESA_POWERDOWN)? mode: \
+ DEFAULT_VESA_BLANKING_MODE;
+}
+
+void
+vesa_powerdown()
+{
+ if (vesa_blanked == 0 || vesa_blanked == VESA_POWERDOWN
+ || current_display == NULL
+ || current_display->set_blanking == NULL)
+ return;
+ (*current_display->set_blanking)(VESA_POWERDOWN);
+ vesa_blanked = VESA_POWERDOWN;
+}
+
+void
+memsetw(unsigned short *p, unsigned short c, unsigned count)
+{
+ count /= 2;
+ if ((unsigned long)(p + count) > video_mem_base
+ && (unsigned long)p < video_mem_term) {
+ for (; p < (unsigned short *) video_mem_base && count != 0; --count)
+ *p++ = c;
+ for (; p < (unsigned short *) video_mem_term && count != 0; --count) {
+ if (*p != c) {
+ *p = c;
+ pmac_blitc(c, (unsigned long)p);
+ }
+ ++p;
+ }
+ }
+ for (; count != 0; --count)
+ *p++ = c;
+}
+
+void
+memcpyw(unsigned short *to, unsigned short *from, unsigned count)
+{
+ unsigned short c;
+
+ count /= 2;
+ if ((unsigned long)(to + count) > video_mem_base
+ && (unsigned long)to < video_mem_term) {
+ for (; to < (unsigned short *) video_mem_base && count != 0; --count)
+ *to++ = *from++;
+ for (; to < (unsigned short *) video_mem_term && count != 0; --count) {
+ c = *from++;
+ if (*to != c) {
+ *to = c;
+ pmac_blitc(c, (unsigned long)to);
+ }
+ ++to;
+ }
+ }
+ for (; count != 0; --count)
+ *to++ = *from++;
+}
+
+void
+pmac_find_display()
+{
+ struct display_interface *disp;
+ struct device_node *dp;
+ struct vmode_attr *ap;
+
+ current_display = NULL;
+ if (serial_console)
+ return;
+ for (disp = displays; disp->name != NULL; ++disp) {
+ dp = find_devices(disp->name);
+ if (dp == 0)
+ continue;
+ current_display = disp;
+ disp->map_interface(dp);
+ break;
+ }
+
+ if (current_display == NULL) {
+ /*
+ * We haven't found a display that we know about,
+ * but if there is a display with sufficient prom support,
+ * we may be able to use it in a limited fashion.
+ * If there is, it has already been opened in prom_init().
+ */
+ if (prom_display_path[0] != 0) {
+ dp = find_path_device(prom_display_path);
+ if (dp != 0 && map_unknown(dp))
+ current_display = &unknown_display;
+ else
+ printk(KERN_INFO "Can't use %s for display\n",
+ prom_display_path);
+ }
+ }
+
+ if (current_display == NULL
+ || video_mode <= 0 || video_mode > VMODE_MAX) {
+ printk(KERN_INFO "No usable display device found"
+ "- using serial console\n");
+ serial_console = 1; /* no screen - fall back to serial */
+ return;
+ }
+ ap = &vmode_attrs[video_mode - 1];
+ screen_info.orig_video_cols = ap->hres / 8;
+ screen_info.orig_video_lines = ap->vres / 16;
+ printk("using video mode %d (%dx%d at %dHz%s), %d bits/pixel\n",
+ video_mode, ap->hres, ap->vres, ap->vfreq,
+ ap->interlaced? " interlaced": "",
+ (color_mode + 1) * 8);
+}
+
+int
+pmac_display_supported(const char *name)
+{
+ struct display_interface *disp;
+
+ for (disp = displays; disp->name != NULL; ++disp)
+ if (strcmp(name, disp->name) == 0)
+ return 1;
+ return 0;
+}
+
+int
+console_getmode(struct vc_mode *mode)
+{
+ *mode = display_info;
+ return 0;
+}
+
+int
+console_setmode(struct vc_mode *mode, int doit)
+{
+ int err;
+
+ if (current_display == NULL || current_display->setmode == NULL)
+ return -EINVAL;
+ err = (*current_display->setmode)(mode, doit);
+ if (doit && err == 0)
+ memset((void *)video_mem_base, 0, video_screen_size);
+ return err;
+}
+
+int
+console_powermode(int mode)
+{
+ if (mode == VC_POWERMODE_INQUIRY)
+ return vesa_blanked;
+ if (mode < VESA_NO_BLANKING || mode > VESA_POWERDOWN)
+ return -EINVAL;
+ if (current_display == NULL || current_display->set_blanking == NULL)
+ return -ENXIO;
+ (*current_display->set_blanking)(mode);
+ vesa_blanked = mode;
+ return 0;
+}
+
+void
+pmac_cons_setup(char *str, int *ints)
+{
+ if (strcmp(str, "ttya") == 0 || strcmp(str, "modem") == 0)
+ serial_console = 1;
+ else if (strcmp(str, "ttyb") == 0 || strcmp(str, "printer") == 0)
+ serial_console = 2;
+}
+
+void
+pmac_vmode_setup(char *str, int *ints)
+{
+ if (ints[0] >= 1)
+ video_mode = ints[1];
+ if (ints[0] >= 2)
+ color_mode = ints[2];
+}
+
+unsigned long
+con_type_init(unsigned long mem_start, const char **type_p)
+{
+ if (current_display == NULL)
+ panic("no display available");
+ current_display->init_interface();
+ screen_initialized = 1; /* inhibits prom_print */
+ can_do_color = 1;
+ video_type = VIDEO_TYPE_PMAC;
+ *type_p = display_info.name;
+ video_mem_base = mem_start;
+ mem_start += MAX_TEXT_COLS * MAX_TEXT_ROWS * 2;
+ video_mem_term = mem_start;
+ memset((char *) video_mem_base, 0, video_screen_size);
+ return mem_start;
+}
+
+static __inline__ void
+draw_logo_8(void)
+{
+ unsigned char *fb = fb_start;
+ unsigned char *p = linux_logo;
+ int yy;
+
+ (*current_display->set_palette)
+ (linux_logo_red, linux_logo_green, linux_logo_blue,
+ 32, LINUX_LOGO_COLORS);
+
+ for (yy = 0; yy < LINUX_LOGO_HEIGHT; ++yy) {
+ memcpy(fb, p, LINUX_LOGO_WIDTH);
+ fb += line_pitch;
+ p += LINUX_LOGO_WIDTH;
+ }
+}
+
+static __inline__ void
+draw_logo_15(void)
+{
+ unsigned short *fb;
+ unsigned char *row = fb_start;
+ unsigned char *p = linux_logo;
+ int i, xx, yy;
+ unsigned char grey[16];
+
+ /*
+ * For 15-bit mode, treat the screen as a 4/4/4 TrueColor.
+ */
+ for (i = 0; i < 16; ++i)
+ grey[i] = i << 4;
+ (*current_display->set_palette)(grey, grey, grey, 16, 16);
+
+ for (yy = 0; yy < LINUX_LOGO_HEIGHT; ++yy) {
+ fb = (unsigned short *) row;
+ for (xx = 0; xx < LINUX_LOGO_WIDTH; ++xx) {
+ i = *p++ - 32;
+ *fb++ = 0x4210
+ + ((linux_logo_red[i] & 0xf0) << 6)
+ + ((linux_logo_green[i] & 0xf0) << 1)
+ + (linux_logo_blue[i] >> 4);
+ }
+ row += line_pitch;
+ }
+}
+
+static __inline__ void
+draw_logo_24(void)
+{
+ unsigned long *fb;
+ unsigned char *row = fb_start;
+ unsigned char *p = linux_logo;
+ int xx, yy, v;
+
+ (*current_display->set_palette)
+ (linux_logo_red, linux_logo_green, linux_logo_blue,
+ 32, LINUX_LOGO_COLORS);
+
+ for (yy = 0; yy < LINUX_LOGO_HEIGHT; ++yy) {
+ fb = (unsigned long *) row;
+ for (xx = 0; xx < LINUX_LOGO_WIDTH; ++xx) {
+ v = *p++;
+ v |= v << 8;
+ v |= v << 16;
+ *fb++ = v;
+ }
+ row += line_pitch;
+ }
+}
+
+void
+con_type_init_finish(void)
+{
+ char *p;
+ int c;
+ unsigned short *addr;
+ char xy[2];
+ int currcons = 0; /* for `attr', which is a macro */
+
+ if (current_display == NULL
+ || current_display->set_palette == NULL)
+ return;
+
+ switch (color_mode) {
+ case CMODE_8:
+ draw_logo_8();
+ break;
+
+ case CMODE_16:
+ draw_logo_15();
+ break;
+
+ case CMODE_32:
+ draw_logo_24();
+ break;
+ }
+ xy[0] = 0;
+ xy[1] = (LINUX_LOGO_HEIGHT + 16) / 16;
+ putconsxy(0, xy);
+
+ p = "PowerMac/Linux " UTS_RELEASE;
+ addr = (unsigned short *) video_mem_base + 2 * video_num_columns
+ + LINUX_LOGO_WIDTH / 8 + 8;
+ for (; *p; ++p) {
+ c = (attr << 8) + *p;
+ scr_writew(c, addr);
+ ++addr;
+ }
+}
+
+int
+con_adjust_height(unsigned long height)
+{
+ return -EINVAL;
+}
+
+static unsigned long expand_bits_8[16] = {
+ 0x00000000,
+ 0x000000ff,
+ 0x0000ff00,
+ 0x0000ffff,
+ 0x00ff0000,
+ 0x00ff00ff,
+ 0x00ffff00,
+ 0x00ffffff,
+ 0xff000000,
+ 0xff0000ff,
+ 0xff00ff00,
+ 0xff00ffff,
+ 0xffff0000,
+ 0xffff00ff,
+ 0xffffff00,
+ 0xffffffff
+};
+
+static unsigned long expand_bits_16[4] = {
+ 0x00000000,
+ 0x0000ffff,
+ 0xffff0000,
+ 0xffffffff
+};
+
+void
+pmac_blitc(unsigned charattr, unsigned long addr)
+{
+ int col, row, fg, bg, l, bits;
+ unsigned char *fp;
+ unsigned long *fb;
+ static int cached_row_start, cached_row_end = -1;
+ static unsigned char *cached_fb;
+ static int cached_attr = -1;
+ static unsigned long cached_fg, cached_bg;
+
+ col = (addr - video_mem_base) / 2;
+ if (cursor_pos == col)
+ cursor_pos = -1;
+ if (!(col >= cached_row_start && col < cached_row_end)) {
+ row = col / video_num_columns;
+ cached_row_start = row * video_num_columns;
+ cached_row_end = cached_row_start + video_num_columns;
+ cached_fb = fb_start + row * row_pitch;
+ }
+ fb = (unsigned long *)
+ (cached_fb + (col - cached_row_start) * pixel_size * 8);
+
+ if ((charattr & 0xff00) != cached_attr) {
+ fg = (charattr >> 8) & 0xf;
+ bg = (charattr >> 12) & 0xf;
+ switch (color_mode) {
+ case CMODE_16:
+ fg = pixel16(fg_console, fg);
+ bg = pixel16(fg_console, bg);
+ break;
+ default:
+ fg += fg << 8;
+ fg += fg << 16;
+ bg += bg << 8;
+ bg += bg << 16;
+ }
+ fg ^= bg;
+ cached_fg = fg;
+ cached_bg = bg;
+ } else {
+ fg = cached_fg;
+ bg = cached_bg;
+ }
+
+ fp = &vga_font[(charattr & 0xff) * 16];
+ switch (color_mode) {
+ case CMODE_32:
+ for (l = 0; l < 16; ++l) {
+ bits = *fp++;
+ fb[0] = (-(bits >> 7) & fg) ^ bg;
+ fb[1] = (-((bits >> 6) & 1) & fg) ^ bg;
+ fb[2] = (-((bits >> 5) & 1) & fg) ^ bg;
+ fb[3] = (-((bits >> 4) & 1) & fg) ^ bg;
+ fb[4] = (-((bits >> 3) & 1) & fg) ^ bg;
+ fb[5] = (-((bits >> 2) & 1) & fg) ^ bg;
+ fb[6] = (-((bits >> 1) & 1) & fg) ^ bg;
+ fb[7] = (-(bits & 1) & fg) ^ bg;
+ fb = (unsigned long *) ((char *)fb + line_pitch);
+ }
+ break;
+ case CMODE_16:
+ for (l = 0; l < 16; ++l) {
+ bits = *fp++;
+ fb[0] = (expand_bits_16[bits >> 6] & fg) ^ bg;
+ fb[1] = (expand_bits_16[(bits >> 4) & 3] & fg) ^ bg;
+ fb[2] = (expand_bits_16[(bits >> 2) & 3] & fg) ^ bg;
+ fb[3] = (expand_bits_16[bits & 3] & fg) ^ bg;
+ fb = (unsigned long *) ((char *)fb + line_pitch);
+ }
+ break;
+ default:
+ for (l = 0; l < 16; ++l) {
+ bits = *fp++;
+ fb[0] = (expand_bits_8[bits >> 4] & fg) ^ bg;
+ fb[1] = (expand_bits_8[bits & 0xf] & fg) ^ bg;
+ fb = (unsigned long *) ((char *)fb + line_pitch);
+ }
+ }
+}
+
+
+/*
+ * The following provides a very basic screen driver for displays
+ * that we basically don't know anything about, but which we can
+ * initialize by using their Open Firmware "open" method.
+ */
+
+static int unknown_modes[] = {
+ VMODE_512_384_60,
+ VMODE_640_480_60,
+ VMODE_800_600_60,
+ VMODE_832_624_75,
+ VMODE_1024_768_60,
+ VMODE_1152_870_75,
+ VMODE_1280_960_75,
+ VMODE_1280_1024_75,
+ 0
+};
+
+static unsigned char *frame_buffer;
+
+static int map_unknown(struct device_node *dp)
+{
+ int i, mode;
+ int width;
+ int *pp, len;
+ unsigned *up, address;
+
+ printk("map_unknown(%p), name = %s\n", dp, dp->full_name);
+
+ /* check the depth */
+ if ((pp = (int *) get_property(dp, "depth", &len)) != NULL
+ && len == sizeof(int) && *pp != 8) {
+ printk("%s: can't use depth = %d\n", dp->full_name, *pp);
+ return 0;
+ }
+
+ width = 640; /* default values */
+ n_scanlines = 480;
+ if ((pp = (int *) get_property(dp, "width", &len)) != NULL
+ && len == sizeof(int))
+ width = *pp;
+ if ((pp = (int *) get_property(dp, "height", &len)) != NULL
+ && len == sizeof(int))
+ n_scanlines = *pp;
+ line_pitch = width;
+ if ((pp = (int *) get_property(dp, "linebytes", &len)) != NULL
+ && len == sizeof(int))
+ line_pitch = *pp;
+ printk(KERN_INFO "width=%d height=%d pitch=%d\n",
+ width, n_scanlines, line_pitch);
+
+ len = n_scanlines * line_pitch;
+ if ((up = (unsigned *) get_property(dp, "address", &len)) != NULL
+ && len == sizeof(unsigned)) {
+ address = *up;
+ } else {
+ for (i = 0; i < dp->n_addrs; ++i)
+ if (dp->addrs[i].size >= len)
+ break;
+ if (i >= dp->n_addrs) {
+ printk("no framebuffer address found for %s\n",
+ dp->full_name);
+ return 0;
+ }
+ address = dp->addrs[i].address;
+ /* temporary kludge for valkyrie */
+ if (strcmp(dp->name, "valkyrie") == 0)
+ address += 0x1000;
+ }
+ printk(KERN_INFO "%s: using address %x\n", dp->full_name, address);
+ frame_buffer = ioremap(address, len);
+
+ video_mode = 0;
+ color_mode = CMODE_8;
+ pixel_size = 1;
+
+ for (i = 0; (mode = unknown_modes[i]) != 0; ++i) {
+ if (vmode_attrs[mode-1].hres <= width
+ && vmode_attrs[mode-1].vres <= n_scanlines)
+ video_mode = mode;
+ }
+ if (video_mode == 0) {
+ printk(KERN_INFO "%s: no mode found for %d x %d\n",
+ dp->full_name, width, n_scanlines);
+ return 0;
+ }
+
+ display_info.height = n_scanlines;
+ display_info.width = width;
+ display_info.depth = 8;
+ display_info.pitch = line_pitch;
+ display_info.mode = video_mode;
+ strncpy(display_info.name, dp->name, sizeof(display_info.name));
+ display_info.fb_address = (unsigned long) frame_buffer;
+ display_info.cmap_adr_address = 0;
+ display_info.cmap_data_address = 0;
+ display_info.disp_reg_address = 0;
+
+ return 1;
+}
+
+static void
+unknown_init()
+{
+ unsigned *p;
+ int i;
+
+ row_pitch = line_pitch * 16;
+ fb_start = frame_buffer;
+
+ /* Clear screen */
+ p = (unsigned *) frame_buffer;
+ for (i = n_scanlines * line_pitch / sizeof(unsigned); i != 0; --i)
+ *p++ = 0;
+}
+
+
+unsigned char vga_font[cmapsz] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd,
+0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff,
+0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe,
+0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c,
+0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd,
+0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e,
+0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30,
+0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63,
+0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8,
+0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0e,
+0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb,
+0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6,
+0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c,
+0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0,
+0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c,
+0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c,
+0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c,
+0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18,
+0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
+0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30,
+0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18,
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e,
+0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe,
+0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0,
+0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18,
+0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
+0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
+0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
+0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde,
+0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38,
+0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0,
+0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x6c,
+0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68,
+0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66,
+0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c,
+0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60,
+0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe7,
+0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66,
+0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c,
+0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c,
+0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3,
+0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18,
+0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3,
+0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30,
+0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60,
+0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc,
+0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, 0x00, 0x00, 0xe0, 0x60,
+0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06,
+0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60,
+0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb,
+0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66,
+0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60,
+0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30,
+0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3,
+0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x18,
+0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6,
+0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66,
+0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00,
+0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe,
+0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c,
+0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38,
+0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06,
+0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe,
+0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00,
+0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66,
+0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6,
+0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00,
+0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b,
+0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c,
+0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18,
+0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00,
+0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00,
+0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e,
+0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18,
+0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66,
+0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18,
+0xd8, 0x70, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30,
+0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc,
+0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c,
+0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0,
+0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06,
+0x0c, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30,
+0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
+0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36,
+0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x44, 0x11, 0x44,
+0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36,
+0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36,
+0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0,
+0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0,
+0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8,
+0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66,
+0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
+0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66,
+0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60,
+0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
+0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18,
+0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
+0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00,
+0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c,
+0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c,
+0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00,
+};
diff --git a/drivers/macintosh/pmac-cons.h b/drivers/macintosh/pmac-cons.h
new file mode 100644
index 000000000..18c2c3c7a
--- /dev/null
+++ b/drivers/macintosh/pmac-cons.h
@@ -0,0 +1,90 @@
+/*
+ * Definitions for display drivers for console use on PowerMacs.
+ *
+ * Copyright (C) 1997 Paul Mackerras.
+ *
+ * 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.
+ */
+
+extern int serial_console; /* set to use serial port as console */
+
+/*
+ * Video mode values.
+ * These are supposed to be the same as the values that
+ * Apple uses in MacOS.
+ */
+#define VMODE_NVRAM 0 /* use value stored in nvram */
+#define VMODE_512_384_60I 1 /* 512x384, 60Hz interlaced (NTSC) */
+#define VMODE_512_384_60 2 /* 512x384, 60Hz */
+#define VMODE_640_480_50I 3 /* 640x480, 50Hz interlaced (PAL) */
+#define VMODE_640_480_60I 4 /* 640x480, 60Hz interlaced (NTSC) */
+#define VMODE_640_480_60 5 /* 640x480, 60Hz (VGA) */
+#define VMODE_640_480_67 6 /* 640x480, 67Hz */
+#define VMODE_640_870_75P 7 /* 640x870, 75Hz (portrait) */
+#define VMODE_768_576_50I 8 /* 768x576, 50Hz (PAL full frame) */
+#define VMODE_800_600_56 9 /* 800x600, 56Hz */
+#define VMODE_800_600_60 10 /* 800x600, 60Hz */
+#define VMODE_800_600_72 11 /* 800x600, 72Hz */
+#define VMODE_800_600_75 12 /* 800x600, 75Hz */
+#define VMODE_832_624_75 13 /* 832x624, 75Hz */
+#define VMODE_1024_768_60 14 /* 1024x768, 60Hz */
+#define VMODE_1024_768_70 15 /* 1024x768, 70Hz (or 72Hz?) */
+#define VMODE_1024_768_75V 16 /* 1024x768, 75Hz (VESA) */
+#define VMODE_1024_768_75 17 /* 1024x768, 75Hz */
+#define VMODE_1152_870_75 18 /* 1152x870, 75Hz */
+#define VMODE_1280_960_75 19 /* 1280x960, 75Hz */
+#define VMODE_1280_1024_75 20 /* 1280x1024, 75Hz */
+#define VMODE_MAX 20
+#define VMODE_CHOOSE 99 /* choose based on monitor sense */
+
+/*
+ * Color mode values, used to select number of bits/pixel.
+ */
+#define CMODE_NVRAM -1 /* use value stored in nvram */
+#define CMODE_8 0 /* 8 bits/pixel */
+#define CMODE_16 1 /* 16 (actually 15) bits/pixel */
+#define CMODE_32 2 /* 32 (actually 24) bits/pixel */
+
+extern int video_mode;
+extern int color_mode;
+
+/*
+ * Addresses in NVRAM where video mode and pixel size are stored.
+ */
+#define NV_VMODE 0x140f
+#define NV_CMODE 0x1410
+
+/*
+ * Horizontal and vertical resolution information.
+ */
+extern struct vmode_attr {
+ int hres;
+ int vres;
+ int vfreq;
+ int interlaced;
+} vmode_attrs[VMODE_MAX];
+
+extern struct vc_mode display_info;
+
+#define DEFAULT_VESA_BLANKING_MODE VESA_NO_BLANKING
+
+extern int pixel_size; /* in bytes */
+extern int n_scanlines; /* # of scan lines */
+extern int line_pitch; /* # bytes in 1 scan line */
+extern int row_pitch; /* # bytes in 1 row of characters */
+extern unsigned char *fb_start; /* addr of top left pixel of top left char */
+
+/* map monitor sense value to video mode */
+extern int map_monitor_sense(int sense);
+
+void set_palette(void);
+void pmac_find_display(void);
+void vesa_blank(void);
+void vesa_unblank(void);
+void set_vesa_blanking(const unsigned long);
+void vesa_powerdown(void);
+void hide_cursor(void);
+void pmac_init_palette(void);
diff --git a/drivers/macintosh/valkyrie.c b/drivers/macintosh/valkyrie.c
new file mode 100644
index 000000000..3d1d877bf
--- /dev/null
+++ b/drivers/macintosh/valkyrie.c
@@ -0,0 +1,321 @@
+/*
+ * valkyrie.c: Console support for PowerMac "valkyrie" display adaptor.
+ *
+ * Copyright (C) 1997 Paul Mackerras.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/vc_ioctl.h>
+#include <linux/nvram.h>
+#include <asm/prom.h>
+#include <asm/io.h>
+#include <asm/cuda.h>
+#include <linux/selection.h>
+#include "pmac-cons.h"
+#include "valkyrie.h"
+
+/*
+ * Structure of the registers for the Valkyrie colormap registers.
+ */
+struct cmap_regs {
+ unsigned char addr;
+ char pad1[7];
+ unsigned char lut;
+};
+
+/*
+ * Structure of the registers for the "valkyrie" display adaptor.
+ */
+#define PAD(x) char x[7]
+
+struct valkyrie_regs {
+ unsigned char mode;
+ PAD(pad0);
+ unsigned char depth;
+ PAD(pad1);
+ unsigned char status;
+ PAD(pad2);
+ unsigned char reg3;
+ PAD(pad3);
+ unsigned char intr;
+ PAD(pad4);
+ unsigned char reg5;
+ PAD(pad5);
+ unsigned char intr_enb;
+ PAD(pad6);
+ unsigned char msense;
+ PAD(pad7);
+};
+
+static void set_valkyrie_clock(unsigned char *params);
+static int read_valkyrie_sense(void);
+
+static unsigned char *frame_buffer;
+static struct cmap_regs *cmap_regs;
+static struct valkyrie_regs *disp_regs;
+
+/*
+ * Register initialization tables for the valkyrie display.
+ *
+ * Dot clock rate is
+ * 3.9064MHz * 2**clock_params[2] * clock_params[1] / clock_params[0].
+ */
+struct valkyrie_regvals {
+ unsigned char mode;
+ unsigned char clock_params[3];
+ int pitch[2]; /* bytes/line, indexed by color_mode */
+};
+
+/* Register values for 1024x768, 72Hz mode (15) */
+static struct valkyrie_regvals valkyrie_reg_init_15 = {
+ 15,
+ { 12, 30, 3 }, /* pixel clock = 78.12MHz for V=72.12Hz */
+ { 1024, 0 }
+};
+
+/* Register values for 1024x768, 60Hz mode (14) */
+static struct valkyrie_regvals valkyrie_reg_init_14 = {
+ 14,
+ { 15, 31, 3 }, /* pixel clock = 64.58MHz for V=59.62Hz */
+ { 1024, 0 }
+};
+
+/* Register values for 832x624, 75Hz mode (13) */
+static struct valkyrie_regvals valkyrie_reg_init_13 = {
+ 9,
+ { 23, 42, 3 }, /* pixel clock = 57.07MHz for V=74.27Hz */
+ { 832, 0 }
+};
+
+/* Register values for 800x600, 72Hz mode (11) */
+static struct valkyrie_regvals valkyrie_reg_init_11 = {
+ 13,
+ { 17, 27, 3 }, /* pixel clock = 49.63MHz for V=71.66Hz */
+ { 800, 0 }
+};
+
+/* Register values for 800x600, 60Hz mode (10) */
+static struct valkyrie_regvals valkyrie_reg_init_10 = {
+ 12,
+ { 20, 53, 2 }, /* pixel clock = 41.41MHz for V=59.78Hz */
+ { 800, 0 }
+};
+
+/* Register values for 640x480, 67Hz mode (6) */
+static struct valkyrie_regvals valkyrie_reg_init_6 = {
+ 6,
+ { 14, 27, 2 }, /* pixel clock = 30.13MHz for V=66.43Hz */
+ { 640, 1280 }
+};
+
+/* Register values for 640x480, 60Hz mode (5) */
+static struct valkyrie_regvals valkyrie_reg_init_5 = {
+ 11,
+ { 23, 37, 2 }, /* pixel clock = 25.14MHz for V=59.85Hz */
+ { 640, 1280 }
+};
+
+static struct valkyrie_regvals *valkyrie_reg_init[20] = {
+ NULL, NULL, NULL, NULL,
+ &valkyrie_reg_init_5,
+ &valkyrie_reg_init_6,
+ NULL, NULL, NULL,
+ &valkyrie_reg_init_10,
+ &valkyrie_reg_init_11,
+ NULL,
+ &valkyrie_reg_init_13,
+ &valkyrie_reg_init_14,
+ &valkyrie_reg_init_15,
+ NULL, NULL, NULL, NULL, NULL
+};
+
+/*
+ * Get the monitor sense value.
+ */
+static int
+read_valkyrie_sense()
+{
+ int sense;
+
+ out_8(&disp_regs->msense, 0); /* release all lines */
+ __delay(20000);
+ sense = (in_8(&disp_regs->msense) & 0x70) << 4;
+
+ /* drive each sense line low in turn and collect the other 2 */
+ out_8(&disp_regs->msense, 4); /* drive A low */
+ __delay(20000);
+ sense |= in_8(&disp_regs->msense) & 0x30;
+ out_8(&disp_regs->msense, 2); /* drive B low */
+ __delay(20000);
+ sense |= ((in_8(&disp_regs->msense) & 0x40) >> 3)
+ | ((in_8(&disp_regs->msense) & 0x10) >> 2);
+ out_8(&disp_regs->msense, 1); /* drive C low */
+ __delay(20000);
+ sense |= (in_8(&disp_regs->msense) & 0x60) >> 5;
+
+ out_8(&disp_regs->msense, 7);
+ return sense;
+}
+
+void
+map_valkyrie_display(struct device_node *dp)
+{
+ int sense;
+ unsigned long addr;
+
+ if (dp->next != 0)
+ printk("Warning: only using first valkyrie display device\n");
+ if (dp->n_addrs != 1)
+ panic("expecting 1 address for valkyrie (got %d)", dp->n_addrs);
+
+ /* Map in frame buffer and registers */
+ addr = dp->addrs[0].address;
+ frame_buffer = ioremap(addr, 0x100000);
+ disp_regs = ioremap(addr + 0x30a000, 4096);
+ cmap_regs = ioremap(addr + 0x304000, 4096);
+
+ /* Read the monitor sense value and choose the video mode */
+ sense = read_valkyrie_sense();
+ if (video_mode == VMODE_NVRAM) {
+ video_mode = nvram_read_byte(NV_VMODE);
+ if (video_mode <= 0 || video_mode > VMODE_MAX
+ || valkyrie_reg_init[video_mode-1] == 0)
+ video_mode = VMODE_CHOOSE;
+ }
+ if (video_mode == VMODE_CHOOSE)
+ video_mode = map_monitor_sense(sense);
+ if (valkyrie_reg_init[video_mode-1] == 0)
+ video_mode = VMODE_640_480_60;
+
+ /*
+ * Reduce the pixel size if we don't have enough VRAM.
+ */
+ if (color_mode == CMODE_NVRAM)
+ color_mode = nvram_read_byte(NV_CMODE);
+ if (color_mode < CMODE_8 || color_mode > CMODE_16
+ || valkyrie_reg_init[video_mode-1]->pitch[color_mode] == 0)
+ color_mode = CMODE_8;
+
+ printk("Monitor sense value = 0x%x, ", sense);
+}
+
+static void
+set_valkyrie_clock(unsigned char *params)
+{
+ struct cuda_request req;
+ int i;
+
+ for (i = 0; i < 3; ++i) {
+ cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
+ 0x50, i + 1, params[i]);
+ while (!req.got_reply)
+ cuda_poll();
+ }
+}
+
+void
+valkyrie_init()
+{
+ int i, yoff, hres;
+ unsigned *p;
+ struct valkyrie_regvals *init;
+
+ if (video_mode <= 0 || video_mode > VMODE_MAX
+ || (init = valkyrie_reg_init[video_mode-1]) == 0)
+ panic("valkyrie: display mode %d not supported", video_mode);
+ n_scanlines = vmode_attrs[video_mode-1].vres;
+ hres = vmode_attrs[video_mode-1].hres;
+ pixel_size = 1 << color_mode;
+ line_pitch = init->pitch[color_mode];
+ row_pitch = line_pitch * 16;
+
+ /* Reset the valkyrie */
+ out_8(&disp_regs->status, 0);
+ udelay(100);
+
+ /* Initialize display timing registers */
+ out_8(&disp_regs->mode, init->mode | 0x80);
+ out_8(&disp_regs->depth, color_mode + 3);
+ set_valkyrie_clock(init->clock_params);
+ udelay(100);
+
+ pmac_init_palette(); /* Initialize colormap */
+
+ /* Turn on display */
+ out_8(&disp_regs->mode, init->mode);
+
+ yoff = (n_scanlines % 16) / 2;
+ fb_start = frame_buffer + yoff * line_pitch + 0x1000;
+
+ /* Clear screen */
+ p = (unsigned *) (frame_buffer + 0x1000);
+ for (i = n_scanlines * line_pitch / sizeof(unsigned); i != 0; --i)
+ *p++ = 0;
+
+ display_info.height = n_scanlines;
+ display_info.width = hres;
+ display_info.depth = pixel_size * 8;
+ display_info.pitch = line_pitch;
+ display_info.mode = video_mode;
+ strncpy(display_info.name, "valkyrie", sizeof(display_info.name));
+ display_info.fb_address = (unsigned long) frame_buffer + 0x1000;
+ display_info.cmap_adr_address = (unsigned long) &cmap_regs->addr;
+ display_info.cmap_data_address = (unsigned long) &cmap_regs->lut;
+ display_info.disp_reg_address = (unsigned long) &disp_regs;
+}
+
+int
+valkyrie_setmode(struct vc_mode *mode, int doit)
+{
+ int cmode;
+
+ switch (mode->depth) {
+ case 16:
+ cmode = CMODE_16;
+ break;
+ case 8:
+ case 0: /* (default) */
+ cmode = CMODE_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (mode->mode <= 0 || mode->mode > VMODE_MAX
+ || valkyrie_reg_init[mode->mode-1] == 0
+ || valkyrie_reg_init[mode->mode-1]->pitch[cmode] == 0)
+ return -EINVAL;
+ if (doit) {
+ video_mode = mode->mode;
+ color_mode = cmode;
+ valkyrie_init();
+ }
+ return 0;
+}
+
+void
+valkyrie_set_palette(unsigned char red[], unsigned char green[],
+ unsigned char blue[], int index, int ncolors)
+{
+ int i;
+
+ for (i = 0; i < ncolors; ++i) {
+ out_8(&cmap_regs->addr, index + i);
+ udelay(1);
+ out_8(&cmap_regs->lut, red[i]);
+ out_8(&cmap_regs->lut, green[i]);
+ out_8(&cmap_regs->lut, blue[i]);
+ }
+}
+
+void
+valkyrie_set_blanking(int blank_mode)
+{
+ /* don't know how to do this yet */
+}
diff --git a/drivers/macintosh/valkyrie.h b/drivers/macintosh/valkyrie.h
new file mode 100644
index 000000000..90521eb3e
--- /dev/null
+++ b/drivers/macintosh/valkyrie.h
@@ -0,0 +1,17 @@
+/*
+ * Exported procedures for the "valkyrie" display driver on PowerMacs.
+ *
+ * Copyright (C) 1997 Paul Mackerras.
+ *
+ * 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.
+ */
+
+extern void map_valkyrie_display(struct device_node *);
+extern void valkyrie_init(void);
+extern int valkyrie_setmode(struct vc_mode *mode, int doit);
+extern void valkyrie_set_palette(unsigned char red[], unsigned char green[],
+ unsigned char blue[], int index, int ncolors);
+extern void valkyrie_set_blanking(int blank_mode);
diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c
new file mode 100644
index 000000000..02b4a4a29
--- /dev/null
+++ b/drivers/macintosh/via-cuda.c
@@ -0,0 +1,415 @@
+/*
+ * Device driver for the via-cuda on Apple Powermacs.
+ *
+ * The VIA (versatile interface adapter) interfaces to the CUDA,
+ * a 6805 microprocessor core which controls the ADB (Apple Desktop
+ * Bus) which connects to the keyboard and mouse. The CUDA also
+ * controls system power and the RTC (real time clock) chip.
+ *
+ * This file also contains routines to support access to ADB
+ * devices via the /dev/adb interface.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/prom.h>
+#include <asm/cuda.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+static volatile unsigned char *via;
+
+/* VIA registers - spaced 0x200 bytes apart */
+#define RS 0x200 /* skip between registers */
+#define B 0 /* B-side data */
+#define A RS /* A-side data */
+#define DIRB (2*RS) /* B-side direction (1=output) */
+#define DIRA (3*RS) /* A-side direction (1=output) */
+#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */
+#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */
+#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */
+#define T2CL (8*RS) /* Timer 2 ctr/latch (low 8 bits) */
+#define T2CH (9*RS) /* Timer 2 counter (high 8 bits) */
+#define SR (10*RS) /* Shift register */
+#define ACR (11*RS) /* Auxiliary control register */
+#define PCR (12*RS) /* Peripheral control register */
+#define IFR (13*RS) /* Interrupt flag register */
+#define IER (14*RS) /* Interrupt enable register */
+#define ANH (15*RS) /* A-side data, no handshake */
+
+/* Bits in B data register: all active low */
+#define TREQ 0x08 /* Transfer request (input) */
+#define TACK 0x10 /* Transfer acknowledge (output) */
+#define TIP 0x20 /* Transfer in progress (output) */
+
+/* Bits in ACR */
+#define SR_CTRL 0x1c /* Shift register control bits */
+#define SR_EXT 0x0c /* Shift on external clock */
+#define SR_OUT 0x10 /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET 0x80 /* set bits in IER */
+#define IER_CLR 0 /* clear bits in IER */
+#define SR_INT 0x04 /* Shift register full/empty */
+
+static struct adb_handler {
+ void (*handler)(unsigned char *, int, struct pt_regs *);
+} adb_handler[16];
+
+static enum cuda_state {
+ idle,
+ sent_first_byte,
+ sending,
+ reading,
+ read_done,
+ awaiting_reply
+} cuda_state;
+
+static struct cuda_request *current_req;
+static struct cuda_request *last_req;
+static unsigned char cuda_rbuf[16];
+static unsigned char *reply_ptr;
+static int reading_reply;
+static int data_index;
+
+static int init_via(void);
+static void cuda_start(void);
+static void via_interrupt(int irq, void *arg, struct pt_regs *regs);
+static void cuda_input(unsigned char *buf, int nb, struct pt_regs *regs);
+
+void
+via_cuda_init()
+{
+ struct device_node *vias;
+
+ vias = find_devices("via-cuda");
+ if (vias == 0) {
+ printk(KERN_WARNING "Warning: no via-cuda\n");
+ vias = find_devices("via-pmu");
+ if (vias == 0)
+ return;
+ printk(KERN_WARNING "Found via-pmu, using it as via-cuda\n");
+ }
+ if (vias->next != 0)
+ printk("Warning: only using 1st via-cuda\n");
+
+#if 0
+ { int i;
+
+ printk("via_cuda_init: node = %p, addrs =", vias->node);
+ for (i = 0; i < vias->n_addrs; ++i)
+ printk(" %x(%x)", vias->addrs[i].address, vias->addrs[i].size);
+ printk(", intrs =");
+ for (i = 0; i < vias->n_intrs; ++i)
+ printk(" %x", vias->intrs[i]);
+ printk("\n"); }
+#endif
+
+ if (vias->n_addrs != 1 || vias->n_intrs != 1)
+ panic("via-cuda: expecting 1 address and 1 interrupt");
+ via = (volatile unsigned char *) vias->addrs->address;
+
+ if (!init_via())
+ panic("init_via failed");
+
+ cuda_state = idle;
+
+ if (request_irq(vias->intrs[0], via_interrupt, 0, "VIA", (void *)0))
+ panic("VIA: can't get irq %d\n", vias->intrs[0]);
+
+ /* Clear and enable interrupts */
+ via[IFR] = 0x7f; eieio(); /* clear interrupts by writing 1s */
+ via[IER] = IER_SET|SR_INT; eieio(); /* enable interrupt from SR */
+}
+
+#define WAIT_FOR(cond, what) \
+ do { \
+ for (x = 1000; !(cond); --x) { \
+ if (x == 0) { \
+ printk("Timeout waiting for " what); \
+ return 0; \
+ } \
+ udelay(100); \
+ } \
+ } while (0)
+
+static int
+init_via()
+{
+ int x;
+
+ via[DIRB] = (via[DIRB] | TACK | TIP) & ~TREQ; /* TACK & TIP out */
+ via[B] |= TACK | TIP; /* negate them */
+ via[ACR] = (via[ACR] & ~SR_CTRL) | SR_EXT; /* SR data in */
+ eieio();
+ x = via[SR]; eieio(); /* clear any left-over data */
+ via[IER] = 0x7f; eieio(); /* disable interrupts from VIA */
+ eieio();
+
+ /* delay 4ms and then clear any pending interrupt */
+ udelay(4000);
+ x = via[SR]; eieio();
+
+ /* sync with the CUDA - assert TACK without TIP */
+ via[B] &= ~TACK; eieio();
+
+ /* wait for the CUDA to assert TREQ in response */
+ WAIT_FOR((via[B] & TREQ) == 0, "CUDA response to sync");
+
+ /* wait for the interrupt and then clear it */
+ WAIT_FOR(via[IFR] & SR_INT, "CUDA response to sync (2)");
+ x = via[SR]; eieio();
+
+ /* finish the sync by negating TACK */
+ via[B] |= TACK; eieio();
+
+ /* wait for the CUDA to negate TREQ and the corresponding interrupt */
+ WAIT_FOR(via[B] & TREQ, "CUDA response to sync (3)");
+ WAIT_FOR(via[IFR] & SR_INT, "CUDA response to sync (4)");
+ x = via[SR]; eieio();
+ via[B] |= TIP; eieio(); /* should be unnecessary */
+
+ return 1;
+}
+
+/* Construct and send a cuda request */
+int
+cuda_request(struct cuda_request *req, void (*done)(struct cuda_request *),
+ int nbytes, ...)
+{
+ va_list list;
+ int i;
+
+ req->nbytes = nbytes;
+ req->done = done;
+ va_start(list, nbytes);
+ for (i = 0; i < nbytes; ++i)
+ req->data[i] = va_arg(list, int);
+ va_end(list);
+ req->reply_expected = 1;
+ return cuda_send_request(req);
+}
+
+int
+cuda_send_request(struct cuda_request *req)
+{
+ unsigned long flags;
+
+ req->next = 0;
+ req->sent = 0;
+ req->got_reply = 0;
+ req->reply_len = 0;
+ save_flags(flags); cli();
+
+ if (current_req != 0) {
+ last_req->next = req;
+ last_req = req;
+ } else {
+ current_req = req;
+ last_req = req;
+ if (cuda_state == idle)
+ cuda_start();
+ }
+
+ restore_flags(flags);
+ return 0;
+}
+
+static void
+cuda_start()
+{
+ unsigned long flags;
+ struct cuda_request *req;
+
+ /* assert cuda_state == idle */
+ /* get the packet to send */
+ req = current_req;
+ if (req == 0)
+ return;
+ save_flags(flags); cli();
+ if ((via[B] & TREQ) == 0) {
+ restore_flags(flags);
+ return; /* a byte is coming in from the CUDA */
+ }
+
+ /* set the shift register to shift out and send a byte */
+ via[ACR] |= SR_OUT; eieio();
+ via[SR] = req->data[0]; eieio();
+ via[B] &= ~TIP;
+ cuda_state = sent_first_byte;
+ restore_flags(flags);
+}
+
+void
+cuda_poll()
+{
+ int ie;
+
+ ie = _disable_interrupts();
+ if (via[IFR] & SR_INT)
+ via_interrupt(0, 0, 0);
+ _enable_interrupts(ie);
+}
+
+static void
+via_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+ int x, status;
+ struct cuda_request *req;
+
+ if ((via[IFR] & SR_INT) == 0)
+ return;
+
+ status = (~via[B] & (TIP|TREQ)) | (via[ACR] & SR_OUT); eieio();
+ /* printk("via_interrupt: state=%d status=%x\n", cuda_state, status); */
+ switch (cuda_state) {
+ case idle:
+ /* CUDA has sent us the first byte of data - unsolicited */
+ if (status != TREQ)
+ printk("cuda: state=idle, status=%x\n", status);
+ x = via[SR]; eieio();
+ via[B] &= ~TIP; eieio();
+ cuda_state = reading;
+ reply_ptr = cuda_rbuf;
+ reading_reply = 0;
+ break;
+
+ case awaiting_reply:
+ /* CUDA has sent us the first byte of data of a reply */
+ if (status != TREQ)
+ printk("cuda: state=awaiting_reply, status=%x\n", status);
+ x = via[SR]; eieio();
+ via[B] &= ~TIP; eieio();
+ cuda_state = reading;
+ reply_ptr = current_req->reply;
+ reading_reply = 1;
+ break;
+
+ case sent_first_byte:
+ if (status == TREQ + TIP + SR_OUT) {
+ /* collision */
+ via[ACR] &= ~SR_OUT; eieio();
+ x = via[SR]; eieio();
+ via[B] |= TIP | TACK; eieio();
+ cuda_state = idle;
+ } else {
+ /* assert status == TIP + SR_OUT */
+ if (status != TIP + SR_OUT)
+ printk("cuda: state=sent_first_byte status=%x\n", status);
+ via[SR] = current_req->data[1]; eieio();
+ via[B] ^= TACK; eieio();
+ data_index = 2;
+ cuda_state = sending;
+ }
+ break;
+
+ case sending:
+ req = current_req;
+ if (data_index >= req->nbytes) {
+ via[ACR] &= ~SR_OUT; eieio();
+ x = via[SR]; eieio();
+ via[B] |= TACK | TIP; eieio();
+ req->sent = 1;
+ if (req->reply_expected) {
+ cuda_state = awaiting_reply;
+ } else {
+ current_req = req->next;
+ if (req->done)
+ (*req->done)(req);
+ /* not sure about this */
+ cuda_state = idle;
+ cuda_start();
+ }
+ } else {
+ via[SR] = req->data[data_index++]; eieio();
+ via[B] ^= TACK; eieio();
+ }
+ break;
+
+ case reading:
+ *reply_ptr++ = via[SR]; eieio();
+ if (status == TIP) {
+ /* that's all folks */
+ via[B] |= TACK | TIP; eieio();
+ cuda_state = read_done;
+ } else {
+ /* assert status == TIP | TREQ */
+ if (status != TIP + TREQ)
+ printk("cuda: state=reading status=%x\n", status);
+ via[B] ^= TACK; eieio();
+ }
+ break;
+
+ case read_done:
+ x = via[SR]; eieio();
+ if (reading_reply) {
+ req = current_req;
+ req->reply_len = reply_ptr - req->reply;
+ req->got_reply = 1;
+ current_req = req->next;
+ if (req->done)
+ (*req->done)(req);
+ } else {
+ cuda_input(cuda_rbuf, reply_ptr - cuda_rbuf, regs);
+ }
+ if (status == TREQ) {
+ via[B] &= ~TIP; eieio();
+ cuda_state = reading;
+ reply_ptr = cuda_rbuf;
+ reading_reply = 0;
+ } else {
+ cuda_state = idle;
+ cuda_start();
+ }
+ break;
+
+ default:
+ printk("via_interrupt: unknown cuda_state %d?\n", cuda_state);
+ }
+}
+
+static void
+cuda_input(unsigned char *buf, int nb, struct pt_regs *regs)
+{
+ int i, id;
+ static int dump_cuda_input = 0;
+
+ switch (buf[0]) {
+ case ADB_PACKET:
+ id = buf[2] >> 4;
+ if (dump_cuda_input) {
+ printk(KERN_INFO "adb packet: ");
+ for (i = 0; i < nb; ++i)
+ printk(" %x", buf[i]);
+ printk(", id = %d\n", id);
+ }
+ if (adb_handler[id].handler != 0) {
+ (*adb_handler[id].handler)(buf, nb, regs);
+ }
+ break;
+
+ default:
+ printk("data from cuda (%d bytes):", nb);
+ for (i = 0; i < nb; ++i)
+ printk(" %.2x", buf[i]);
+ printk("\n");
+ }
+}
+
+/* Ultimately this should return the number of devices with
+ the given default id. */
+int
+adb_register(int default_id,
+ void (*handler)(unsigned char *, int, struct pt_regs *))
+{
+ if (adb_handler[default_id].handler != 0)
+ panic("Two handlers for ADB device %d\n", default_id);
+ adb_handler[default_id].handler = handler;
+ return 1;
+}
diff --git a/drivers/misc/BUGS-parport b/drivers/misc/BUGS-parport
index 6b8420a2c..cecbf703b 100644
--- a/drivers/misc/BUGS-parport
+++ b/drivers/misc/BUGS-parport
@@ -1,12 +1,5 @@
Currently known (or at least suspected) bugs in parport:
-o /proc/parport is untested under 2.0.XX
-
-o SCSI aborts for PPA under 2.0.29 [reported by jmr]. Under investigation.
-
-o make config (etc) allow you to select CONFIG_PNP_PARPORT=m, CONFIG_PPA=y -
- the resulting kernel won't link.
-
o IEEE1284 code does not do the terminating handshake after transfers, which
seems to upset some devices.
diff --git a/drivers/misc/parport_arc.c b/drivers/misc/parport_arc.c
index 932ecab8c..3a013b4cf 100644
--- a/drivers/misc/parport_arc.c
+++ b/drivers/misc/parport_arc.c
@@ -1,7 +1,6 @@
-/* $Id: parport_arc.c,v 1.1 1997/07/29 03:59:12 ralf Exp $
- * Parallel-port routines for ARC onboard hardware.
+/* Parallel-port routines for ARC onboard hardware.
*
- * Author: Phil Blundell <pjb27@cam.ac.uk>
+ * Author: Phil Blundell <Philip.Blundell@pobox.com>
*/
#include <linux/tasks.h>
@@ -37,6 +36,20 @@ static unsigned int arc_read_data(struct parport *p)
return data_copy;
}
+static void arc_inc_use_count(void)
+{
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+}
+
+static void arc_dec_use_count(void)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+}
+
static struct parport_operations arc_ops =
{
arc_write_data,
@@ -72,5 +85,8 @@ static struct parport_operations arc_ops =
arc_enable_irq,
arc_disable_irq,
- arc_examine_irq
+ arc_examine_irq,
+
+ arc_inc_use_count,
+ arc_dec_use_count
};
diff --git a/drivers/misc/parport_ieee1284.c b/drivers/misc/parport_ieee1284.c
index d1e8c44de..6fad49911 100644
--- a/drivers/misc/parport_ieee1284.c
+++ b/drivers/misc/parport_ieee1284.c
@@ -1,7 +1,6 @@
-/* $Id: parport_ieee1284.c,v 1.1 1997/07/25 01:53:13 ralf Exp $
- * IEEE-1284 implementation for parport.
+/* IEEE-1284 implementation for parport.
*
- * Authors: Philip Blundell <pjb27@cam.ac.uk>
+ * Authors: Phil Blundell <Philip.Blundell@pobox.com>
* Carsten Gross <carsten@sol.wohnheim.uni-ulm.de>
* Jose Renau <renau@acm.org>
*/
diff --git a/drivers/misc/parport_init.c b/drivers/misc/parport_init.c
index a0f280b2f..c6daf72cf 100644
--- a/drivers/misc/parport_init.c
+++ b/drivers/misc/parport_init.c
@@ -1,8 +1,7 @@
-/* $Id: parport_init.c,v 1.1 1997/07/29 03:59:13 ralf Exp $
- * Parallel-port initialisation code.
+/* Parallel-port initialisation code.
*
* Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
- * Tim Waugh <tmw20@cam.ac.uk>
+ * Tim Waugh <tim@cyberelk.demon.co.uk>
* Jose Renau <renau@acm.org>
*
* based on work by Grant Guenther <grant@torque.net>
@@ -62,17 +61,6 @@ int init_module(void)
void cleanup_module(void)
{
- struct parport *port, *next;
-
- for (port = parport_enumerate(); port; port = next) {
- next = port->next;
- if (!(port->flags & PARPORT_FLAG_COMA))
- parport_quiesce(port);
- parport_proc_unregister(port);
- kfree(port->name);
- kfree(port);
- }
-
parport_proc_cleanup();
}
#else
diff --git a/drivers/misc/parport_pc.c b/drivers/misc/parport_pc.c
index 6c6dbd033..ae32bc91c 100644
--- a/drivers/misc/parport_pc.c
+++ b/drivers/misc/parport_pc.c
@@ -1,13 +1,11 @@
-/* $Id: parport_pc.c,v 1.1 1997/07/29 03:59:13 ralf Exp $
- * Parallel-port routines for PC architecture
+/* Parallel-port routines for PC architecture
*
- * Authors: Phil Blundell <pjb27@cam.ac.uk>
- * Tim Waugh <tmw20@cam.ac.uk>
+ * Authors: Phil Blundell <Philip.Blundell@pobox.com>
+ * Tim Waugh <tim@cyberelk.demon.co.uk>
* Jose Renau <renau@acm.org>
* David Campbell <campbell@tirian.che.curtin.edu.au>
*
- * based on work by Grant Guenther <grant@torque.net>
- * and Philip Blundell <Philip.Blundell@pobox.com>
+ * based on work by Grant Guenther <grant@torque.net> and Phil Blundell.
*/
#include <linux/stddef.h>
@@ -44,10 +42,12 @@ static void pc_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
return;
}
+#if 0
static void pc_write_epp(struct parport *p, unsigned int d)
{
outb(d, p->base+EPPREG);
}
+#endif
static unsigned int pc_read_epp(struct parport *p)
{
@@ -151,7 +151,7 @@ static int pc_claim_resources(struct parport *p)
{
/* FIXME check that resources are free */
if (p->irq != PARPORT_IRQ_NONE)
- request_irq(p->irq, pc_null_intr_func, 0, p->name, NULL);
+ request_irq(p->irq, pc_null_intr_func, 0, p->name, p);
request_region(p->base, p->size, p->name);
if (p->modes & PARPORT_MODE_PCECR)
request_region(p->base+0x400, 3, p->name);
@@ -160,12 +160,14 @@ static int pc_claim_resources(struct parport *p)
static void pc_save_state(struct parport *p, struct parport_state *s)
{
- /* FIXME */
+ s->u.pc.ctr = pc_read_control(p);
+ s->u.pc.ecr = pc_read_econtrol(p);
}
static void pc_restore_state(struct parport *p, struct parport_state *s)
{
- /* FIXME */
+ pc_write_control(p, s->u.pc.ctr);
+ pc_write_econtrol(p, s->u.pc.ecr);
}
static unsigned int pc_epp_read_block(struct parport *p, void *buf, unsigned int length)
@@ -193,6 +195,20 @@ static int pc_examine_irq(struct parport *p)
return 0; /* FIXME */
}
+static void pc_inc_use_count(void)
+{
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+}
+
+static void pc_dec_use_count(void)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+}
+
static struct parport_operations pc_ops =
{
pc_write_data,
@@ -228,7 +244,10 @@ static struct parport_operations pc_ops =
pc_enable_irq,
pc_disable_irq,
- pc_examine_irq
+ pc_examine_irq,
+
+ pc_inc_use_count,
+ pc_dec_use_count
};
/******************************************************
@@ -385,6 +404,7 @@ static int parport_dma_probe(struct parport *pb)
return retv;
}
+
/******************************************************
* MODE detection section:
*/
@@ -444,18 +464,20 @@ static int parport_ECR_present(struct parport *pb)
oecr = pc_read_econtrol(pb);
r = pc_read_control(pb);
- if ((pc_read_econtrol(pb) & 0x03) == (r & 0x03)) {
- pc_write_control(pb, r ^ 0x03 ); /* Toggle bits 0-1 */
+ if ((pc_read_econtrol(pb) & 0x3) == (r & 0x3)) {
+ pc_write_control(pb, r ^ 0x2 ); /* Toggle bit 1 */
- r= pc_read_control(pb);
- if ((pc_read_econtrol(pb) & 0x03) == (r & 0x03))
+ r = pc_read_control(pb);
+ if ((pc_read_econtrol(pb) & 0x2) == (r & 0x2)) {
+ pc_write_control(pb, octr);
return 0; /* Sure that no ECR register exists */
+ }
}
- if ((pc_read_econtrol(pb) & 0x03 ) != 0x01)
+ if ((pc_read_econtrol(pb) & 0x3 ) != 0x1)
return 0;
- pc_write_econtrol(pb,0x34);
+ pc_write_econtrol(pb, 0x34);
if (pc_read_econtrol(pb) != 0x35)
return 0;
@@ -841,6 +863,11 @@ static int probe_one_port(unsigned long int base, int irq, int dma)
#undef printmode
printk("]\n");
parport_proc_register(p);
+ p->flags |= PARPORT_FLAG_COMA;
+
+ /* Done probing. Now put the port into a sensible start-up state. */
+ pc_write_control(p, 0xc);
+ pc_write_data(p, 0);
return 1;
}
@@ -855,11 +882,8 @@ int parport_pc_init(int *io, int *irq, int *dma)
} else {
/* Probe all the likely ports. */
count += probe_one_port(0x378, PARPORT_IRQ_AUTO, PARPORT_DMA_AUTO);
-
-#if defined(__i386__)
count += probe_one_port(0x278, PARPORT_IRQ_AUTO, PARPORT_DMA_AUTO);
count += probe_one_port(0x3bc, PARPORT_IRQ_AUTO, PARPORT_DMA_AUTO);
-#endif
}
return count;
}
@@ -872,22 +896,23 @@ MODULE_PARM(io, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
MODULE_PARM(irq, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
MODULE_PARM(dma, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
-static int init_module(void)
+int init_module(void)
{
return (parport_pc_init(io, irq, dma)?0:1);
}
-static void cleanup_module(void)
+void cleanup_module(void)
{
- struct parport *p = parport_enumerate();
+ struct parport *p = parport_enumerate(), *tmp;
while (p) {
+ tmp = p->next;
if (p->modes & PARPORT_MODE_PCSPP) {
if (!(p->flags & PARPORT_FLAG_COMA))
parport_quiesce(p);
parport_proc_unregister(p);
parport_unregister_port(p);
}
- p = p->next;
+ p = tmp;
}
}
#endif
diff --git a/drivers/misc/parport_procfs.c b/drivers/misc/parport_procfs.c
index 0fd03fddf..f50754829 100644
--- a/drivers/misc/parport_procfs.c
+++ b/drivers/misc/parport_procfs.c
@@ -1,5 +1,4 @@
-/* $Id: parport_procfs.c,v 1.1 1997/07/29 03:59:13 ralf Exp $
- * Parallel port /proc interface code.
+/* Parallel port /proc interface code.
*
* Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
* Tim Waugh <tmw20@cam.ac.uk>
diff --git a/drivers/misc/parport_share.c b/drivers/misc/parport_share.c
index 256cbcee6..e4c58370f 100644
--- a/drivers/misc/parport_share.c
+++ b/drivers/misc/parport_share.c
@@ -1,8 +1,7 @@
-/* $Id: parport_share.c,v 1.1 1997/07/29 03:59:13 ralf Exp $
- * Parallel-port resource manager code.
+/* Parallel-port resource manager code.
*
* Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
- * Tim Waugh <tmw20@cam.ac.uk>
+ * Tim Waugh <tim@cyberelk.demon.co.uk>
* Jose Renau <renau@acm.org>
*
* based on work by Grant Guenther <grant@torque.net>
@@ -19,6 +18,12 @@
#include <linux/kernel.h>
#include <linux/malloc.h>
+#include <linux/config.h>
+
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
#undef PARPORT_PARANOID
static struct parport *portlist = NULL, *portlist_tail = NULL;
@@ -27,6 +32,10 @@ static int portcount = 0;
/* Return a list of all the ports we know about. */
struct parport *parport_enumerate(void)
{
+#ifdef CONFIG_KERNELD
+ if (portlist == NULL)
+ request_module("parport_lowlevel");
+#endif
return portlist;
}
@@ -93,7 +102,8 @@ void parport_unregister_port(struct parport *port)
struct parport *p;
kfree(port->name);
if (portlist == port) {
- portlist = port->next;
+ if ((portlist = port->next) == NULL)
+ portlist_tail = NULL;
} else {
for (p = portlist; (p != NULL) && (p->next != port);
p=p->next);
@@ -184,6 +194,7 @@ struct pardevice *parport_register_device(struct parport *port, const char *name
port->lurker = tmp;
inc_parport_count();
+ port->ops->inc_use_count();
return tmp;
}
@@ -218,6 +229,7 @@ void parport_unregister_device(struct pardevice *dev)
kfree(dev);
dec_parport_count();
+ port->ops->dec_use_count();
/* If there are no more devices, put the port to sleep. */
if (!port->devices)
diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c
index af1be0848..676561504 100644
--- a/drivers/net/3c509.c
+++ b/drivers/net/3c509.c
@@ -27,6 +27,7 @@
FIXES:
Alan Cox: Removed the 'Unexpected interrupt' bug.
Michael Meskes: Upgraded to Donald Becker's version 1.07.
+ Phil Blundell: Media selection support.
*/
static char *version = "3c509.c:1.07 6/15/95 becker@cesdis.gsfc.nasa.gov\n";
@@ -127,6 +128,7 @@ static void update_stats(int addr, struct device *dev);
static struct net_device_stats *el3_get_stats(struct device *dev);
static int el3_rx(struct device *dev);
static int el3_close(struct device *dev);
+static int el3_set_config(struct device *dev, struct ifmap *map);
#ifdef HAVE_MULTICAST
static void set_multicast_list(struct device *dev);
#endif
@@ -136,9 +138,15 @@ static void set_multicast_list(struct device *dev);
__initfunc(int el3_probe(struct device *dev))
{
short lrs_state = 0xff, i;
- ushort ioaddr, irq, if_port;
+ ushort ioaddr, irq, port;
short *phys_addr = (short *)dev->dev_addr;
static int current_tag = 0;
+ static int el3_portmap[] = {
+ IF_PORT_10BASET,
+ IF_PORT_AUI,
+ IF_PORT_UNKNOWN,
+ IF_PORT_10BASE2
+ };
/* First check all slots of the EISA bus. The next slot address to
probe is kept in 'eisa_addr' to support multiple probe() calls. */
@@ -156,7 +164,7 @@ __initfunc(int el3_probe(struct device *dev))
outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);
irq = inw(ioaddr + WN0_IRQ) >> 12;
- if_port = inw(ioaddr + 6)>>14;
+ port = inw(ioaddr + 6)>>14;
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
@@ -179,7 +187,7 @@ __initfunc(int el3_probe(struct device *dev))
if ((mca_adaptor_id(i) | 1) == 0x627c) {
ioaddr = mca_pos_base_addr(i);
irq = inw(ioaddr + WN0_IRQ) >> 12;
- if_port = inw(ioaddr + 6)>>14;
+ port = inw(ioaddr + 6)>>14;
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
@@ -245,7 +253,7 @@ __initfunc(int el3_probe(struct device *dev))
{
unsigned short iobase = id_read_eeprom(8);
- if_port = iobase >> 14;
+ port = iobase >> 14;
ioaddr = 0x200 + ((iobase & 0x1f) << 4);
}
if (dev->irq > 1 && dev->irq < 16)
@@ -273,13 +281,13 @@ __initfunc(int el3_probe(struct device *dev))
found:
dev->base_addr = ioaddr;
dev->irq = irq;
- dev->if_port = if_port;
+ dev->if_port = el3_portmap[port];
request_region(dev->base_addr, EL3_IO_EXTENT, "3c509");
{
- const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
+ static const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
printk("%s: 3c509 at %#3.3lx tag %d, %s port, address ",
- dev->name, dev->base_addr, current_tag, if_names[dev->if_port]);
+ dev->name, dev->base_addr, current_tag, if_names[port]);
}
/* Read in the station address. */
@@ -301,12 +309,14 @@ __initfunc(int el3_probe(struct device *dev))
dev->hard_start_xmit = &el3_start_xmit;
dev->stop = &el3_close;
dev->get_stats = &el3_get_stats;
+ dev->set_config = &el3_set_config;
#ifdef HAVE_MULTICAST
dev->set_multicast_list = &set_multicast_list;
#endif
/* Fill in the generic fields of the device structure. */
ether_setup(dev);
+ dev->flags |= IFF_PORTSEL;
return 0;
}
@@ -345,6 +355,59 @@ __initfunc(static ushort id_read_eeprom(int index))
static int
+el3_set_config(struct device *dev, struct ifmap *map)
+{
+ int ioaddr = dev->base_addr;
+ if (map->port != dev->if_port) {
+ switch (map->port) {
+ case IF_PORT_10BASE2:
+ case IF_PORT_10BASET:
+ case IF_PORT_AUI:
+ if (dev->start) {
+ if (dev->if_port == IF_PORT_10BASE2)
+ /* Turn off thinnet power. */
+ outw(StopCoax, ioaddr + EL3_CMD);
+ else if (dev->if_port == IF_PORT_10BASET) {
+ /* Disable link beat and jabber */
+ EL3WINDOW(4);
+ outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
+ EL3WINDOW(1);
+ }
+ }
+ printk(KERN_INFO "%s: %s port selected.\n", dev->name,
+ if_port_text[map->port]);
+ dev->if_port = map->port;
+ if (dev->start) {
+ if (dev->if_port == IF_PORT_10BASE2)
+ /* Start the thinnet transceiver. We should really wait 50ms...*/
+ outw(StartCoax, ioaddr + EL3_CMD);
+ else if (dev->if_port == IF_PORT_10BASET) {
+ /* 10baseT interface, enabled link beat and jabber check. */
+ EL3WINDOW(4);
+ outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
+ EL3WINDOW(1);
+ }
+ }
+ break;
+ default:
+ printk(KERN_ERR "%s: %s port not supported.\n", dev->name,
+ if_port_text[map->port]);
+ return -EINVAL;
+ }
+ }
+ if (map->irq != dev->irq) {
+ printk(KERN_ERR "%s: cannot change interrupt.\n", dev->name);
+ return -EINVAL;
+ }
+ if (map->base_addr != dev->base_addr) {
+ printk(KERN_ERR "%s: cannot change base address.\n", dev->name);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static int
el3_open(struct device *dev)
{
int ioaddr = dev->base_addr;
@@ -376,10 +439,10 @@ el3_open(struct device *dev)
for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + i);
- if (dev->if_port == 3)
+ if (dev->if_port == IF_PORT_10BASE2)
/* Start the thinnet transceiver. We should really wait 50ms...*/
outw(StartCoax, ioaddr + EL3_CMD);
- else if (dev->if_port == 0) {
+ else if (dev->if_port == IF_PORT_10BASET) {
/* 10baseT interface, enabled link beat and jabber check. */
EL3WINDOW(4);
outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
@@ -731,10 +794,10 @@ el3_close(struct device *dev)
outw(RxDisable, ioaddr + EL3_CMD);
outw(TxDisable, ioaddr + EL3_CMD);
- if (dev->if_port == 3)
+ if (dev->if_port == IF_PORT_10BASE2)
/* Turn off thinnet power. Green! */
outw(StopCoax, ioaddr + EL3_CMD);
- else if (dev->if_port == 0) {
+ else if (dev->if_port == IF_PORT_10BASET) {
/* Disable link beat and jabber, if_port may change ere next open(). */
EL3WINDOW(4);
outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
diff --git a/drivers/net/8390.c b/drivers/net/8390.c
index 1150ddcf4..4623128a2 100644
--- a/drivers/net/8390.c
+++ b/drivers/net/8390.c
@@ -191,7 +191,8 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
printk("%s: Tx request while isr active.\n",dev->name);
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
ei_local->stat.tx_errors++;
- return 1;
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
}
ei_local->irqlock = 1;
@@ -305,6 +306,7 @@ void ei_interrupt(int irq, void *dev_id, struct pt_regs * regs)
}
dev->interrupt = 1;
+ sti();
/* Change to page 0 and read the intr status reg. */
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
diff --git a/drivers/net/Config.in b/drivers/net/Config.in
index 4c879ceca..2e721ba8d 100644
--- a/drivers/net/Config.in
+++ b/drivers/net/Config.in
@@ -6,15 +6,25 @@ tristate 'ARCnet support' CONFIG_ARCNET
if [ "$CONFIG_ARCNET" != "n" ]; then
bool ' Enable arc0e (ARCnet "Ether-Encap" packet format)' CONFIG_ARCNET_ETH
bool ' Enable arc0s (ARCnet RFC1051 packet format)' CONFIG_ARCNET_1051
+ dep_tristate ' ARCnet COM90xx (normal) chipset driver' CONFIG_ARCNET_COM90xx $CONFIG_ARCNET
+ dep_tristate ' ARCnet COM90xx (IO mapped) chipset driver' CONFIG_ARCNET_COM90xxIO $CONFIG_ARCNET
+ dep_tristate ' ARCnet COM90xx (RIM I) chipset driver' CONFIG_ARCNET_RIM_I $CONFIG_ARCNET
+ dep_tristate ' ARCnet COM20020 chipset driver' CONFIG_ARCNET_COM20020 $CONFIG_ARCNET
fi
+
tristate 'Dummy net driver support' CONFIG_DUMMY
tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
-
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'Ethertap network tap' CONFIG_ETHERTAP
+fi
#
# Ethernet
#
bool 'Ethernet (10 or 100Mbit)' CONFIG_NET_ETHERNET
if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
+ if [ "$CONFIG_PMAC" = "y" ]; then
+ bool 'MACE (Power Mac ethernet) support' CONFIG_MACE
+ fi
if [ "$CONFIG_MIPS_JAZZ" = "y" ]; then
tristate 'MIPS JAZZ onboard SONIC ethernet support' CONFIG_MIPS_JAZZ_SONIC
fi
@@ -43,6 +53,14 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
fi
tristate 'SMC 9194 support' CONFIG_SMC9194
fi
+ bool 'Racal-Interlan (Micom) NI cards' CONFIG_NET_VENDOR_RACAL
+ if [ "$CONFIG_NET_VENDOR_RACAL" = "y" ]; then
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'NI5010 support' CONFIG_NI5010
+ fi
+ tristate 'NI5210 support' CONFIG_NI52
+ tristate 'NI6510 support' CONFIG_NI65
+ fi
bool 'Other ISA cards' CONFIG_NET_ISA
if [ "$CONFIG_NET_ISA" = "y" ]; then
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
@@ -63,8 +81,6 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
tristate 'ICL EtherTeam 16i/32 support' CONFIG_ETH16I
fi
tristate 'NE2000/NE1000 support' CONFIG_NE2000
- tristate 'NI5210 support' CONFIG_NI52
- tristate 'NI6510 support' CONFIG_NI65
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool 'SEEQ8005 support (EXPERIMENTAL)' CONFIG_SEEQ8005
fi
@@ -123,8 +139,8 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
fi
fi
-if [ ! "$CONFIG_PNP_PARPORT" = "n" ]; then
- dep_tristate 'PLIP (parallel port) support' CONFIG_PLIP $CONFIG_PNP_PARPORT
+if [ ! "$CONFIG_PARPORT" = "n" ]; then
+ dep_tristate 'PLIP (parallel port) support' CONFIG_PLIP $CONFIG_PARPORT
fi
tristate 'PPP (point-to-point) support' CONFIG_PPP
@@ -152,14 +168,16 @@ if [ "$CONFIG_NET_RADIO" != "n" ]; then
bool 'Soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8
bool 'Soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800
bool 'Soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600
-# if [ -f doesn't work with xconfig. Enable it again when the drivers are really
-# included.
-# if [ -f drivers/net/soundmodem/sm_afsk2666.c ]; then
-# bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666
-# fi
-# if [ -f drivers/net/soundmodem/sm_psk4800.c ]; then
-# bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800
-# fi
+ fi
+ tristate 'Shortwave radio modem driver' CONFIG_HFMODEM
+ if [ "$CONFIG_HFMODEM" != "n" ]; then
+ bool 'HFmodem support for Soundblaster and compatible cards' CONFIG_HFMODEM_SBC
+ bool 'HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS
+ fi
+ tristate 'Shortwave radio modem driver' CONFIG_HFMODEM
+ if [ "$CONFIG_HFMODEM" != "n" ]; then
+ bool 'HFmodem support for Soundblaster and compatible cards' CONFIG_HFMODEM_SBC
+ bool 'HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS
fi
fi
tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
@@ -205,4 +223,3 @@ if [ "$CONFIG_LAPB" != "n" ]; then
dep_tristate 'X.25 async driver' CONFIG_X25_ASY $CONFIG_LAPB
fi
fi
-
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b149eac58..920c17dc2 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -53,6 +53,14 @@ else
endif
endif
+ifeq ($(CONFIG_ETHERTAP),y)
+L_OBJS += ethertap.o
+else
+ ifeq ($(CONFIG_ETHERTAP),m)
+ M_OBJS += ethertap.o
+ endif
+endif
+
ifeq ($(CONFIG_SHAPER),y)
L_OBJS += shaper.o
else
@@ -431,6 +439,14 @@ else
endif
endif
+ifeq ($(CONFIG_NI5010),y)
+L_OBJS += ni5010.o
+else
+ ifeq ($(CONFIG_NI5010),m)
+ M_OBJS += ni5010.o
+ endif
+endif
+
ifeq ($(CONFIG_NI52),y)
L_OBJS += ni52.o
else
@@ -482,10 +498,42 @@ else
endif
ifeq ($(CONFIG_ARCNET),y)
-L_OBJS += arcnet.o
+LX_OBJS += arcnet.o
else
ifeq ($(CONFIG_ARCNET),m)
- M_OBJS += arcnet.o
+ MX_OBJS += arcnet.o
+ endif
+endif
+
+ifeq ($(CONFIG_ARCNET_COM90xx),y)
+L_OBJS += com90xx.o
+else
+ ifeq ($(CONFIG_ARCNET_COM90xx),m)
+ M_OBJS += com90xx.o
+ endif
+endif
+
+ifeq ($(CONFIG_ARCNET_COM90xxIO),y)
+L_OBJS += com90io.o
+else
+ ifeq ($(CONFIG_ARCNET_COM90xxIO),m)
+ M_OBJS += com90io.o
+ endif
+endif
+
+ifeq ($(CONFIG_ARCNET_RIM_I),y)
+L_OBJS += arc-rimi.o
+else
+ ifeq ($(CONFIG_ARCNET_RIM_I),m)
+ M_OBJS += arc-rimi.o
+ endif
+endif
+
+ifeq ($(CONFIG_ARCNET_COM20020),y)
+L_OBJS += com20020.o
+else
+ ifeq ($(CONFIG_ARCNET_COM20020),m)
+ M_OBJS += com20020.o
endif
endif
@@ -692,6 +740,7 @@ endif
ifeq ($(CONFIG_SOUNDMODEM),y)
ALL_SUB_DIRS += soundmodem
SUB_DIRS += soundmodem
+L_OBJS += soundmodem/soundmodem.o
CONFIG_HDLCDRV_BUILTIN = y
else
ifeq ($(CONFIG_SOUNDMODEM),m)
@@ -701,6 +750,10 @@ else
endif
endif
+ifeq ($(CONFIG_MACE),y)
+L_OBJS += mace.o
+endif
+
# If anything built-in uses the hdlcdrv, then build it into the kernel also.
# If not, but a module uses it, build as a module.
ifdef CONFIG_HDLCDRV_BUILTIN
diff --git a/drivers/net/Space.c b/drivers/net/Space.c
index 64bc62c41..4fdfbb6b1 100644
--- a/drivers/net/Space.c
+++ b/drivers/net/Space.c
@@ -32,6 +32,8 @@
#include <linux/errno.h>
#include <linux/init.h>
+#include <net/netlink.h>
+
#define NEXT_DEV NULL
@@ -63,15 +65,14 @@ extern int apricot_probe(struct device *);
extern int ewrk3_probe(struct device *);
extern int de4x5_probe(struct device *);
extern int el1_probe(struct device *);
-#if defined(CONFIG_WAVELAN)
extern int wavelan_probe(struct device *);
-#endif /* defined(CONFIG_WAVELAN) */
extern int el16_probe(struct device *);
extern int elmc_probe(struct device *);
extern int elplus_probe(struct device *);
extern int ac3200_probe(struct device *);
extern int es_probe(struct device *);
extern int e2100_probe(struct device *);
+extern int ni5010_probe(struct device *);
extern int ni52_probe(struct device *);
extern int ni65_probe(struct device *);
extern int sonic_probe(struct device *);
@@ -90,7 +91,9 @@ extern int a2065_probe(struct device *);
extern int ariadne_probe(struct device *);
extern int hydra_probe(struct device *);
extern int tlan_probe(struct device *);
+extern int mace_probe(struct device *);
extern int cs89x0_probe(struct device *dev);
+extern int ethertap_probe(struct device *dev);
/* Detachable devices ("pocket adaptors") */
extern int atp_init(struct device *);
@@ -105,6 +108,12 @@ __initfunc(static int ethif_probe(struct device *dev))
return 1; /* ENXIO */
if (1
+#ifdef CONFIG_HAPPYMEAL
+ /* Please keep this one first, we'd like the on-board ethernet
+ * to be probed first before other PCI cards on Ultra/PCI. -DaveM
+ */
+ && happy_meal_probe(dev)
+#endif
#ifdef CONFIG_DGRS
&& dgrs_probe(dev)
#endif
@@ -222,6 +231,9 @@ __initfunc(static int ethif_probe(struct device *dev))
#if defined(CONFIG_SK_G16)
&& SK_init(dev)
#endif
+#ifdef CONFIG_NI5010
+ && ni5010_probe(dev)
+#endif
#ifdef CONFIG_NI52
&& ni52_probe(dev)
#endif
@@ -246,15 +258,15 @@ __initfunc(static int ethif_probe(struct device *dev))
#ifdef CONFIG_TLAN
&& tlan_probe(dev)
#endif
-#ifdef CONFIG_HAPPYMEAL
- && happy_meal_probe(dev)
-#endif
#ifdef CONFIG_SUNQE
&& qec_probe(dev)
#endif
#ifdef CONFIG_MYRI_SBUS
&& myri_sbus_probe(dev)
#endif
+#ifdef CONFIG_MACE
+ && mace_probe(dev)
+#endif
#ifdef CONFIG_SGISEEQ
&& sgiseeq_probe(dev)
#endif
@@ -270,6 +282,12 @@ __initfunc(static int ethif_probe(struct device *dev))
+#ifdef CONFIG_ETHERTAP
+ static struct device tap0_dev = { "tap0", 0, 0, 0, 0, NETLINK_TAPBASE, 0, 0, 0, 0, NEXT_DEV, ethertap_probe, };
+# undef NEXT_DEV
+# define NEXT_DEV (&tap0_dev)
+#endif
+
#ifdef CONFIG_SDLA
extern int sdla_init(struct device *);
static struct device sdla0_dev = { "sdla0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, sdla_init, };
@@ -286,14 +304,6 @@ static struct device atp_dev = {
# define NEXT_DEV (&atp_dev)
#endif
-#ifdef CONFIG_ARCNET
- extern int arcnet_probe(struct device *dev);
- static struct device arcnet_dev = {
- "arc0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, arcnet_probe, };
-# undef NEXT_DEV
-# define NEXT_DEV (&arcnet_dev)
-#endif
-
#if defined(CONFIG_LTPC)
extern int ltpc_probe(struct device *);
static struct device dev_ltpc = {
diff --git a/drivers/net/arc-rimi.c b/drivers/net/arc-rimi.c
new file mode 100644
index 000000000..c5ab22584
--- /dev/null
+++ b/drivers/net/arc-rimi.c
@@ -0,0 +1,851 @@
+/* $Id: arc-rimi.c,v 1.2 1997/09/05 08:57:51 mj Exp $
+
+ Derived from the original arcnet.c,
+ Written 1994-1996 by Avery Pennarun,
+ which was in turn derived from skeleton.c by Donald Becker.
+
+ Contact Avery at: apenwarr@bond.net or
+ RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
+
+ **********************
+
+ The original copyright of skeleton.c was as follows:
+
+ skeleton.c Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may only be used
+ and distributed according to the terms of the GNU Public License as
+ modified by SRC, incorporated herein by reference.
+
+ **********************
+
+ For more details, see drivers/net/arcnet.c
+
+ **********************
+*/
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/if_arcnet.h>
+#include <linux/arcdevice.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <net/arp.h>
+
+/**************************************************************************/
+
+/* On a fast computer, the buffer copy from memory to the ARCnet card during
+ * a transmit can hog the bus just a little too long. SLOW_XMIT_COPY
+ * replaces the fast memcpy() with a slower for() loop that seems to solve
+ * my problems with ftape.
+ *
+ * Probably a better solution would be to use memcpy_toio (more portable
+ * anyway) and modify that routine to support REALLY_SLOW_IO-style
+ * defines; ARCnet probably is not the only driver that can screw up an
+ * ftape DMA transfer.
+ *
+ * Turn this on if you have timing-sensitive DMA (ie. a tape drive) and
+ * would like to sacrifice a little bit of network speed to reduce tape
+ * write retries or some related problem.
+ */
+#undef SLOW_XMIT_COPY
+
+
+/* Internal function declarations */
+
+static int arcrimi_probe(struct device *dev);
+static void arcrimi_rx(struct device *dev,int recbuf);
+static int arcrimi_found(struct device *dev,int ioaddr,int airq,u_long shmem);
+static void arcrimi_inthandler (struct device *dev);
+static int arcrimi_reset (struct device *dev, int reset_delay);
+static void arcrimi_setmask (struct device *dev, u_char mask);
+static void arcrimi_command (struct device *dev, u_char command);
+static u_char arcrimi_status (struct device *dev);
+static void arcrimi_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+ char *data,int length,int daddr,int exceptA, int offset);
+static void arcrimi_openclose(int open);
+
+
+/* Module parameters */
+
+#ifdef MODULE
+static int shmem=0x0; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq=0; /* or use the insmod io= irq= shmem= options */
+static char *device; /* use eg. device="arc1" to change name */
+static int node=0; /* you must specify the node ID for RIM I cards */
+
+MODULE_PARM(shmem, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(device, "s");
+MODULE_PARM (node, "i");
+#else
+__initfunc(void arcrimi_setup (char *str, int *ints));
+extern struct device arcnet_devs[];
+extern char arcnet_dev_names[][10];
+extern int arcnet_num_devs;
+#endif
+
+/* Handy defines for ARCnet specific stuff */
+
+/* COM 9026 controller chip --> ARCnet register addresses */
+#define _INTMASK (ioaddr+0) /* writable */
+#define _STATUS (ioaddr+0) /* readable */
+#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */
+#define _RESET (ioaddr+8) /* software reset (on read) */
+#define _MEMDATA (ioaddr+12) /* Data port for IO-mapped memory */
+#define _ADDR_HI (ioaddr+15) /* Control registers for said */
+#define _ADDR_LO (ioaddr+14)
+#define _CONFIG (ioaddr+2) /* Configuration register */
+
+#define RDDATAflag 0x00 /* Next access is a read/~write */
+
+#define ARCSTATUS readb(_STATUS)
+#define ACOMMAND(cmd) writeb((cmd),_COMMAND)
+#define ARCRESET writeb(TESTvalue,ioaddr-0x800) /* fake reset */
+#define AINTMASK(msk) writeb((msk),_INTMASK)
+#define SETCONF writeb(lp->config,_CONFIG)
+
+static const char *version =
+"arc-rimi.c: v2.92 97/09/02 Avery Pennarun <apenwarr@bond.net> et al.\n";
+
+/****************************************************************************
+ * *
+ * Probe and initialization *
+ * *
+ ****************************************************************************/
+
+
+/* We cannot probe for a RIM I card; one reason is I don't know how to reset
+ * them. In fact, we can't even get their node ID automatically. So, we
+ * need to be passed a specific shmem address, IRQ, and node ID.
+ */
+__initfunc(int arcrimi_probe(struct device *dev))
+{
+ BUGLVL(D_NORMAL) printk(version);
+ BUGMSG(D_NORMAL,"Given: node %02Xh, shmem %lXh, irq %d\n",
+ dev->dev_addr[0],dev->mem_start,dev->irq);
+
+ if (dev->mem_start<=0 || dev->irq<=0)
+ {
+ BUGMSG(D_NORMAL,"No autoprobe for RIM I; you "
+ "must specify the shmem and irq!\n");
+ return -ENODEV;
+ }
+
+ if (dev->dev_addr[0]==0)
+ {
+ BUGMSG(D_NORMAL,"You need to specify your card's station "
+ "ID!\n");
+ return -ENODEV;
+ }
+
+ return arcrimi_found(dev,dev->dev_addr[0],dev->irq,dev->mem_start);
+}
+
+
+/* Set up the struct device associated with this card. Called after
+ * probing succeeds.
+ */
+__initfunc(int arcrimi_found(struct device *dev,int node,int airq, u_long shmem))
+{
+ struct arcnet_local *lp;
+ u_long first_mirror,last_mirror;
+ int mirror_size;
+
+ /* reserve the irq */
+ if (request_irq(airq,&arcnet_interrupt,0,"arcnet (RIM I)",NULL))
+ {
+ BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
+ return -ENODEV;
+ }
+ irq2dev_map[airq]=dev;
+ dev->irq=airq;
+
+ dev->base_addr=0;
+ writeb(TESTvalue,shmem);
+ writeb(node,shmem+1); /* actually the node ID */
+
+ /* find the real shared memory start/end points, including mirrors */
+#define BUFFER_SIZE (512)
+#define MIRROR_SIZE (BUFFER_SIZE*4)
+
+ /* guess the actual size of one "memory mirror" - the number of
+ * bytes between copies of the shared memory. On most cards, it's
+ * 2k (or there are no mirrors at all) but on some, it's 4k.
+ */
+ mirror_size=MIRROR_SIZE;
+ if (readb(shmem)==TESTvalue
+ && readb(shmem-mirror_size)!=TESTvalue
+ && readb(shmem-2*mirror_size)==TESTvalue)
+ mirror_size*=2;
+
+ first_mirror=last_mirror=shmem;
+ while (readb(first_mirror)==TESTvalue) first_mirror-=mirror_size;
+ first_mirror+=mirror_size;
+
+ while (readb(last_mirror)==TESTvalue) last_mirror+=mirror_size;
+ last_mirror-=mirror_size;
+
+ dev->mem_start=first_mirror;
+ dev->mem_end=last_mirror+MIRROR_SIZE-1;
+ dev->rmem_start=dev->mem_start+BUFFER_SIZE*0;
+ dev->rmem_end=dev->mem_start+BUFFER_SIZE*2-1;
+
+ /* Initialize the rest of the device structure. */
+
+ dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ {
+ irq2dev_map[airq] = NULL;
+ free_irq(airq,NULL);
+ return -ENOMEM;
+ }
+ memset(dev->priv,0,sizeof(struct arcnet_local));
+ lp=(struct arcnet_local *)(dev->priv);
+ lp->card_type = ARC_RIM_I;
+ lp->card_type_str = "RIM I";
+ lp->arcnet_reset=arcrimi_reset;
+ lp->asetmask=arcrimi_setmask;
+ lp->astatus=arcrimi_status;
+ lp->acommand=arcrimi_command;
+ lp->openclose_device=arcrimi_openclose;
+ lp->prepare_tx=arcrimi_prepare_tx;
+ lp->inthandler=arcrimi_inthandler;
+
+ /* Fill in the fields of the device structure with generic
+ * values.
+ */
+ arcnet_setup(dev);
+
+ /* And now fill particular fields with arcnet values */
+ dev->mtu=1500; /* completely arbitrary - agrees with ether, though */
+ dev->hard_header_len=sizeof(struct ClientData);
+ lp->sequence=1;
+ lp->recbuf=0;
+
+ BUGMSG(D_DURING,"ClientData header size is %d.\n",
+ sizeof(struct ClientData));
+ BUGMSG(D_DURING,"HardHeader size is %d.\n",
+ sizeof(struct archdr));
+
+ /* get and check the station ID from offset 1 in shmem */
+ lp->stationid = readb(first_mirror+1);
+
+ if (lp->stationid==0)
+ BUGMSG(D_NORMAL,"WARNING! Station address 00 is reserved "
+ "for broadcasts!\n");
+ else if (lp->stationid==255)
+ BUGMSG(D_NORMAL,"WARNING! Station address FF may confuse "
+ "DOS networking programs!\n");
+ dev->dev_addr[0]=lp->stationid;
+
+ BUGMSG(D_NORMAL,"ARCnet RIM I: station %02Xh found at IRQ %d, "
+ "ShMem %lXh (%ld*%d bytes).\n",
+ lp->stationid,
+ dev->irq, dev->mem_start,
+ (dev->mem_end-dev->mem_start+1)/mirror_size,mirror_size);
+
+ return 0;
+}
+
+
+/* Do a hardware reset on the card, and set up necessary registers.
+ *
+ * This should be called as little as possible, because it disrupts the
+ * token on the network (causes a RECON) and requires a significant delay.
+ *
+ * However, it does make sure the card is in a defined state.
+ */
+int arcrimi_reset(struct device *dev,int reset_delay)
+{
+ struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+ short ioaddr=dev->mem_start + 0x800;
+ int recbuf=lp->recbuf;
+
+ if (reset_delay==3)
+ {
+ ARCRESET;
+ return 0;
+ }
+
+ /* no IRQ's, please! */
+ lp->intmask=0;
+ SETMASK;
+
+ BUGMSG(D_INIT,"Resetting %s (status=%Xh)\n",
+ dev->name,ARCSTATUS);
+
+ ACOMMAND(CFLAGScmd|RESETclear); /* clear flags & end reset */
+ ACOMMAND(CFLAGScmd|CONFIGclear);
+
+ /* clear out status variables */
+ recbuf=lp->recbuf=0;
+ lp->txbuf=2;
+
+ /* enable extended (512-byte) packets */
+ ACOMMAND(CONFIGcmd|EXTconf);
+
+#ifndef SLOW_XMIT_COPY
+ /* clean out all the memory to make debugging make more sense :) */
+ BUGLVL(D_DURING)
+ memset_io(dev->mem_start,0x42,2048);
+#endif
+
+ /* and enable receive of our first packet to the first buffer */
+ EnableReceiver();
+
+ /* re-enable interrupts */
+ lp->intmask|=NORXflag;
+#ifdef DETECT_RECONFIGS
+ lp->intmask|=RECONflag;
+#endif
+ SETMASK;
+
+ /* done! return success. */
+ return 0;
+}
+
+
+static void arcrimi_openclose(int open)
+{
+ if (open)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+
+static void arcrimi_setmask(struct device *dev, u_char mask)
+{
+ int ioaddr=dev->mem_start+0x800;
+
+ AINTMASK(mask);
+}
+
+static u_char arcrimi_status(struct device *dev)
+{
+ int ioaddr=dev->mem_start+0x800;
+
+ return ARCSTATUS;
+}
+
+static void arcrimi_command(struct device *dev, u_char cmd)
+{
+ int ioaddr=dev->mem_start+0x800;
+
+ ACOMMAND(cmd);
+}
+
+
+/* The actual interrupt handler routine - handle various IRQ's generated
+ * by the card.
+ */
+static void
+arcrimi_inthandler(struct device *dev)
+{
+ struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+ int ioaddr=dev->mem_start+0x800, status, boguscount = 3, didsomething;
+
+ AINTMASK(0);
+
+ BUGMSG(D_DURING,"in arcrimi_inthandler (status=%Xh, intmask=%Xh)\n",
+ ARCSTATUS,lp->intmask);
+
+ do
+ {
+ status = ARCSTATUS;
+ didsomething=0;
+
+ /* RESET flag was enabled - card is resetting and if RX
+ * is disabled, it's NOT because we just got a packet.
+ */
+ if (status & RESETflag)
+ {
+ BUGMSG(D_NORMAL,"spurious reset (status=%Xh)\n",
+ status);
+ arcrimi_reset(dev,0);
+
+ /* all other flag values are just garbage */
+ break;
+ }
+
+ /* RX is inhibited - we must have received something. */
+ if (status & lp->intmask & NORXflag)
+ {
+ int recbuf=lp->recbuf=!lp->recbuf;
+
+ BUGMSG(D_DURING,"receive irq (status=%Xh)\n",
+ status);
+
+ /* enable receive of our next packet */
+ EnableReceiver();
+
+ /* Got a packet. */
+ arcrimi_rx(dev,!recbuf);
+
+ didsomething++;
+ }
+
+ /* it can only be an xmit-done irq if we're xmitting :) */
+ /*if (status&TXFREEflag && !lp->in_txhandler && lp->sending)*/
+ if (status & lp->intmask & TXFREEflag)
+ {
+ struct Outgoing *out=&(lp->outgoing);
+ int was_sending=lp->sending;
+
+ lp->intmask &= ~TXFREEflag;
+
+ lp->in_txhandler++;
+ if (was_sending) lp->sending--;
+
+ BUGMSG(D_DURING,"TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n",
+ status,out->numsegs,out->segnum,out->skb);
+
+ if (was_sending && !(status&TXACKflag))
+ {
+ if (lp->lasttrans_dest != 0)
+ {
+ BUGMSG(D_EXTRA,"transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n",
+ status,lp->lasttrans_dest);
+ lp->stats.tx_errors++;
+ lp->stats.tx_carrier_errors++;
+ }
+ else
+ {
+ BUGMSG(D_DURING,"broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n",
+ status,
+ lp->lasttrans_dest);
+ }
+ }
+
+ /* send packet if there is one */
+ arcnet_go_tx(dev,0);
+ didsomething++;
+
+ if (lp->intx)
+ {
+ BUGMSG(D_DURING,"TXDONE while intx! (status=%Xh, intx=%d)\n",
+ ARCSTATUS,lp->intx);
+ lp->in_txhandler--;
+ continue;
+ }
+
+ if (!lp->outgoing.skb)
+ {
+ BUGMSG(D_DURING,"TX IRQ done: no split to continue.\n");
+
+ /* inform upper layers */
+ if (!lp->txready) arcnet_tx_done(dev, lp);
+ lp->in_txhandler--;
+ continue;
+ }
+
+ /* if more than one segment, and not all segments
+ * are done, then continue xmit.
+ */
+ if (out->segnum<out->numsegs)
+ arcnetA_continue_tx(dev);
+ arcnet_go_tx(dev,0);
+
+ /* if segnum==numsegs, the transmission is finished;
+ * free the skb.
+ */
+ if (out->segnum>=out->numsegs)
+ {
+ /* transmit completed */
+ out->segnum++;
+ if (out->skb)
+ {
+ lp->stats.tx_bytes += out->skb->len;
+ dev_kfree_skb(out->skb,FREE_WRITE);
+ }
+ out->skb=NULL;
+
+ /* inform upper layers */
+ if (!lp->txready) arcnet_tx_done(dev, lp);
+ }
+ didsomething++;
+
+ lp->in_txhandler--;
+ }
+ else if (lp->txready && !lp->sending && !lp->intx)
+ {
+ BUGMSG(D_NORMAL,"recovery from silent TX (status=%Xh)\n",
+ status);
+ arcnet_go_tx(dev,0);
+ didsomething++;
+ }
+
+#ifdef DETECT_RECONFIGS
+ if (status & (lp->intmask) & RECONflag)
+ {
+ ACOMMAND(CFLAGScmd|CONFIGclear);
+ lp->stats.tx_carrier_errors++;
+
+#ifdef SHOW_RECONFIGS
+ BUGMSG(D_NORMAL,"Network reconfiguration detected (status=%Xh)\n",
+ status);
+#endif /* SHOW_RECONFIGS */
+
+#ifdef RECON_THRESHOLD
+ /* is the RECON info empty or old? */
+ if (!lp->first_recon || !lp->last_recon ||
+ jiffies-lp->last_recon > HZ*10)
+ {
+ if (lp->network_down)
+ BUGMSG(D_NORMAL,"reconfiguration detected: cabling restored?\n");
+ lp->first_recon=lp->last_recon=jiffies;
+ lp->num_recons=lp->network_down=0;
+
+ BUGMSG(D_DURING,"recon: clearing counters.\n");
+ }
+ else /* add to current RECON counter */
+ {
+ lp->last_recon=jiffies;
+ lp->num_recons++;
+
+ BUGMSG(D_DURING,"recon: counter=%d, time=%lds, net=%d\n",
+ lp->num_recons,
+ (lp->last_recon-lp->first_recon)/HZ,
+ lp->network_down);
+
+ /* if network is marked up;
+ * and first_recon and last_recon are 60+ sec
+ * apart;
+ * and the average no. of recons counted is
+ * > RECON_THRESHOLD/min;
+ * then print a warning message.
+ */
+ if (!lp->network_down
+ && (lp->last_recon-lp->first_recon)<=HZ*60
+ && lp->num_recons >= RECON_THRESHOLD)
+ {
+ lp->network_down=1;
+ BUGMSG(D_NORMAL,"many reconfigurations detected: cabling problem?\n");
+ }
+ else if (!lp->network_down
+ && lp->last_recon-lp->first_recon > HZ*60)
+ {
+ /* reset counters if we've gone for
+ * over a minute.
+ */
+ lp->first_recon=lp->last_recon;
+ lp->num_recons=1;
+ }
+ }
+ }
+ else if (lp->network_down && jiffies-lp->last_recon > HZ*10)
+ {
+ if (lp->network_down)
+ BUGMSG(D_NORMAL,"cabling restored?\n");
+ lp->first_recon=lp->last_recon=0;
+ lp->num_recons=lp->network_down=0;
+
+ BUGMSG(D_DURING,"not recon: clearing counters anyway.\n");
+#endif
+ }
+#endif /* DETECT_RECONFIGS */
+ } while (--boguscount && didsomething);
+
+ BUGMSG(D_DURING,"net_interrupt complete (status=%Xh, count=%d)\n",
+ ARCSTATUS,boguscount);
+ BUGMSG(D_DURING,"\n");
+
+ SETMASK; /* put back interrupt mask */
+}
+
+
+/* A packet has arrived; grab it from the buffers and pass it to the generic
+ * arcnet_rx routing to deal with it.
+ */
+
+static void
+arcrimi_rx(struct device *dev,int recbuf)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int ioaddr=dev->mem_start+0x800;
+ union ArcPacket *arcpacket=
+ (union ArcPacket *)phys_to_virt(dev->mem_start+recbuf*512);
+ u_char *arcsoft;
+ short length,offset;
+ u_char daddr,saddr;
+
+ lp->stats.rx_packets++;
+
+ saddr=arcpacket->hardheader.source;
+
+ /* if source is 0, it's a "used" packet! */
+ if (saddr==0)
+ {
+ BUGMSG(D_NORMAL,"discarding old packet. (status=%Xh)\n",
+ ARCSTATUS);
+ lp->stats.rx_errors++;
+ return;
+ }
+ /* Set source address to zero to mark it as old */
+
+ arcpacket->hardheader.source=0;
+
+ daddr=arcpacket->hardheader.destination;
+
+ if (arcpacket->hardheader.offset1) /* Normal Packet */
+ {
+ offset=arcpacket->hardheader.offset1;
+ arcsoft=&arcpacket->raw[offset];
+ length=256-offset;
+ }
+ else /* ExtendedPacket or ExceptionPacket */
+ {
+ offset=arcpacket->hardheader.offset2;
+ arcsoft=&arcpacket->raw[offset];
+
+ length=512-offset;
+ }
+
+ arcnet_rx(lp, arcsoft, length, saddr, daddr);
+
+ BUGLVL(D_RX) arcnet_dump_packet(lp->adev,arcpacket->raw,length>240,"rx");
+
+#ifndef SLOW_XMIT_COPY
+ /* clean out the page to make debugging make more sense :) */
+ BUGLVL(D_DURING)
+ memset((void *)arcpacket->raw,0x42,512);
+#endif
+}
+
+
+/* Given an skb, copy a packet into the ARCnet buffers for later transmission
+ * by arcnet_go_tx.
+ */
+static void
+arcrimi_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+ char *data,int length,int daddr,int exceptA, int offset)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ union ArcPacket *arcpacket =
+ (union ArcPacket *)phys_to_virt(dev->mem_start+512*(lp->txbuf^1));
+
+#ifdef SLOW_XMIT_COPY
+ char *iptr,*iend,*optr;
+#endif
+
+ lp->txbuf=lp->txbuf^1; /* XOR with 1 to alternate between 2 and 3 */
+
+ length+=hdrlen;
+
+ BUGMSG(D_TX,"arcnetAS_prep_tx: hdr:%ph, length:%d, data:%ph\n",
+ hdr,length,data);
+
+#ifndef SLOW_XMIT_COPY
+ /* clean out the page to make debugging make more sense :) */
+ BUGLVL(D_DURING)
+ memset_io(dev->mem_start+lp->txbuf*512,0x42,512);
+#endif
+
+ arcpacket->hardheader.destination=daddr;
+
+ /* load packet into shared memory */
+ if (length<=MTU) /* Normal (256-byte) Packet */
+ arcpacket->hardheader.offset1=offset=offset?offset:256-length;
+
+ else if (length>=MinTU || offset) /* Extended (512-byte) Packet */
+ {
+ arcpacket->hardheader.offset1=0;
+ arcpacket->hardheader.offset2=offset=offset?offset:512-length;
+ }
+ else if (exceptA) /* RFC1201 Exception Packet */
+ {
+ arcpacket->hardheader.offset1=0;
+ arcpacket->hardheader.offset2=offset=512-length-4;
+
+ /* exception-specific stuff - these four bytes
+ * make the packet long enough to fit in a 512-byte
+ * frame.
+ */
+
+ arcpacket->raw[offset+0]=hdr[0];
+ arcpacket->raw[offset+1]=0xFF; /* FF flag */
+ arcpacket->raw[offset+2]=0xFF; /* FF padding */
+ arcpacket->raw[offset+3]=0xFF; /* FF padding */
+ offset+=4;
+ }
+ else /* "other" Exception packet */
+ {
+ /* RFC1051 - set 4 trailing bytes to 0 */
+ memset(&arcpacket->raw[508],0,4);
+
+ /* now round up to MinTU */
+ arcpacket->hardheader.offset1=0;
+ arcpacket->hardheader.offset2=offset=512-MinTU;
+ }
+
+
+ /* copy the packet into ARCnet shmem
+ * - the first bytes of ClientData header are skipped
+ */
+
+ memcpy((u_char*)arcpacket+offset, (u_char*)hdr,hdrlen);
+#ifdef SLOW_XMIT_COPY
+ for (iptr=data,iend=iptr+length-hdrlen,optr=(char *)arcpacket+offset+hdrlen;
+ iptr<iend; iptr++,optr++)
+ {
+ *optr=*iptr;
+ /*udelay(5);*/
+ }
+#else
+ memcpy((u_char*)arcpacket+offset+hdrlen, data,length-hdrlen);
+#endif
+
+ BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
+ daddr,length);
+
+ BUGLVL(D_TX) arcnet_dump_packet(dev,arcpacket->raw,length>MTU,"tx");
+
+ lp->lastload_dest=daddr;
+ lp->txready=lp->txbuf; /* packet is ready for sending */
+}
+
+
+/****************************************************************************
+ * *
+ * Kernel Loadable Module Support *
+ * *
+ ****************************************************************************/
+
+
+#ifdef MODULE
+
+static char devicename[9] = "";
+static struct device thiscard = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0, /* I/O address, IRQ */
+ 0, 0, 0, NULL, arcrimi_probe
+};
+
+
+int init_module(void)
+{
+ struct device *dev=&thiscard;
+ if (device)
+ strcpy(dev->name,device);
+ else arcnet_makename(dev->name);
+
+ if (node && node != 0xff)
+ dev->dev_addr[0]=node;
+
+ dev->irq=irq;
+ if (dev->irq==2) dev->irq=9;
+
+ if (shmem)
+ {
+ dev->mem_start=shmem;
+ dev->mem_end=thiscard.mem_start+512*4-1;
+ dev->rmem_start=thiscard.mem_start+512*0;
+ dev->rmem_end=thiscard.mem_start+512*2-1;
+ }
+
+ if (register_netdev(dev) != 0)
+ return -EIO;
+ arcnet_use_count(1);
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ struct device *dev=&thiscard;
+ int ioaddr=dev->mem_start;
+
+ if (dev->start) (*dev->stop)(dev);
+
+ /* Flush TX and disable RX */
+ if (ioaddr)
+ {
+ AINTMASK(0); /* disable IRQ's */
+ ACOMMAND(NOTXcmd); /* stop transmit */
+ ACOMMAND(NORXcmd); /* disable receive */
+ }
+
+ if (dev->irq)
+ {
+ irq2dev_map[dev->irq] = NULL;
+ free_irq(dev->irq,NULL);
+ }
+
+ unregister_netdev(dev);
+ kfree(dev->priv);
+ dev->priv = NULL;
+ arcnet_use_count(0);
+}
+
+#else
+
+__initfunc(void arcrimi_setup (char *str, int *ints))
+{
+ struct device *dev;
+
+ if (arcnet_num_devs == MAX_ARCNET_DEVS)
+ {
+ printk("ARCnet RIM I: Too many ARCnet devices registered (max %d).\n",
+ MAX_ARCNET_DEVS);
+ return;
+ }
+
+ dev=&arcnet_devs[arcnet_num_devs];
+
+ if (ints[0] < 3)
+ {
+ printk("ARCnet RIM I: You must give address, IRQ and node ID.\n");
+ return;
+ }
+
+ dev->init=arcrimi_probe;
+
+ switch(ints[0])
+ {
+ case 4: /* ERROR */
+ printk("ARCnet RIM I: Too many arguments.\n");
+
+ case 3: /* Node ID */
+ dev->dev_addr[0]=(u_char)ints[3];
+
+ case 2: /* IRQ */
+ dev->irq=ints[2];
+
+ case 1: /* Mem address */
+ dev->mem_start=ints[1];
+ }
+
+ dev->name = (char *)&arcnet_dev_names[arcnet_num_devs];
+
+ if (str)
+ strncpy(dev->name, str, 9);
+
+ arcnet_num_devs++;
+}
+
+#endif /* MODULE */
diff --git a/drivers/net/arcnet.c b/drivers/net/arcnet.c
index c9f13150c..7669b59a8 100644
--- a/drivers/net/arcnet.c
+++ b/drivers/net/arcnet.c
@@ -1,8 +1,9 @@
-/* arcnet.c:
+/* $Id: arcnet.c,v 1.30 1997/09/05 08:57:46 mj Exp $
+
Written 1994-1996 by Avery Pennarun,
derived from skeleton.c by Donald Becker.
- Contact Avery at: apenwarr@foxnet.net or
+ Contact Avery at: apenwarr@bond.net or
RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
**********************
@@ -17,6 +18,32 @@
**********************
+ v2.92 ALPHA (97/02/09)
+ - Code cleanup [Martin Mares <mj@atrey.karlin.mff.cuni.cz>]
+ - Better probing for the COM90xx chipset, although only as
+ a temporary solution until we implement adding of all found
+ devices at once. [mj]
+
+ v2.91 ALPHA (97/19/08)
+ - Add counting of octets in/out.
+
+ v2.90 ALPHA (97/08/08)
+ - Add support for kernel command line parsing so that chipset
+ drivers are usable when compiled in.
+
+ v2.80 ALPHA (97/08/01)
+ - Split source into multiple files; generic arcnet support and
+ individual chipset drivers. <dwmw2@cam.ac.uk>
+
+ v2.61 ALPHA (97/07/30) by David Woodhouse (dwmw2@cam.ac.uk) for
+ Nortel (Northern Telecom).
+ - Added support for IO-mapped modes and for SMC COM20020 chipset.
+ - Fixed (avoided) race condition in send_packet routines which was
+ discovered when the buffer copy routines got slow (?).
+ - Fixed support for device naming at load time.
+ - Added backplane, clock and timeout options for COM20020.
+ - Added support for promiscuous mode.
+
v2.60 ALPHA (96/11/23)
- Added patch from Vojtech Pavlik <vojtech@atrey.karlin.mff.cuni.cz>
and Martin Mares <mj@k332.feld.cvut.cz> to make the driver work
@@ -95,30 +122,27 @@
This is half-done in ARCnet 2.60, but still uses some
undocumented i386 stuff. (We shouldn't call phys_to_virt,
for example.)
+ - Allow use of RFC1051 or Ether devices without RFC1201.
+ - Keep separate stats for each device.
- Support "arpless" mode like NetBSD does, and as recommended
by the (obsoleted) RFC1051.
- - Some way to make RIM_I_MODE runtime switchable? Yuck...
- Smarter recovery from RECON-during-transmit conditions. (ie.
retransmit immediately)
- - Make arcnetE_send_packet use arcnet_prepare_tx for loading the
- packet into ARCnet memory.
- - Probe for multiple devices in one shot (trying to decide whether
- to do it the "ugly" way or not).
- Add support for the new 1.3.x IP header cache, and other features.
- - Debug level should be changed with a system call, not a hack to
- the "metric" flag.
+ - Replace setting of debug level with the "metric" flag hack by
+ something better. SIOCDEVPRIVATE is a good candidate, but it would
+ require an extra user-level utility.
+
- What about cards with shared memory that can be "turned off?"
(or that have none at all, like the SMC PC500longboard)
+ Does this work now, with IO_MAPPED_BUFFERS?
+
- Autoconfigure PDI5xxPlus cards. (I now have a PDI508Plus to play
with temporarily.) Update: yes, the Pure Data config program
for DOS works fine, but the PDI508Plus I have doesn't! :)
- - Try to implement promiscuous (receive-all-packets) mode available
- on some newer cards with COM20020 and similar chips. I don't have
- one, but SMC sent me the specs.
- ATA protocol support??
- VINES TCP/IP encapsulation?? (info needed)
-
Sources:
- Crynwr arcnet.com/arcether.com packet drivers.
- arcnet.c v0.00 dated 1/1/94 and apparently by
@@ -128,6 +152,7 @@
- RFC's 1201 and 1051 - re: TCP/IP over ARCnet
- The official ARCnet COM9026 data sheets (!) thanks to Ken
Cornetet <kcornete@nyx10.cs.du.edu>
+ - The official ARCnet COM20020 data sheets.
- Information on some more obscure ARCnet controller chips, thanks
to the nice people at SMC.
- net/inet/eth.c (from kernel 1.1.50) for header-building info.
@@ -137,9 +162,7 @@
*/
static const char *version =
- "arcnet.c: v2.60 96/11/23 Avery Pennarun <apenwarr@foxnet.net>\n";
-
-
+ "arcnet.c: v2.92 97/09/02 Avery Pennarun <apenwarr@bond.net> et al.\n";
#include <linux/module.h>
#include <linux/config.h>
@@ -164,6 +187,9 @@ static const char *version =
#include <linux/skbuff.h>
#include <linux/init.h>
+#include <linux/if_arcnet.h>
+#include <linux/arcdevice.h>
+
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
@@ -171,95 +197,6 @@ static const char *version =
#include <net/arp.h>
-/**************************************************************************/
-
-/* Define this if you have a really ancient "RIM I" ARCnet card with no I/O
- * port at all and _only_ shared memory; this option MAY work for you. It's
- * untested, though, so good luck and write to me with any results!
- */
-#undef RIM_I_MODE
-
-/* Normally, the ARCnet device needs to be assigned a name (default arc0).
- * Ethernet devices have a function to automatically try eth0, eth1, etc
- * until a free name is found. To name the ARCnet device using an "eth?"
- * device name, define this option.
- */
-#undef CONFIG_ARCNET_ETHNAME
-
-/* On a fast computer, the buffer copy from memory to the ARCnet card during
- * a transmit can hog the bus just a little too long. SLOW_XMIT_COPY
- * replaces the fast memcpy() with a slower for() loop that seems to solve
- * my problems with ftape.
- *
- * Probably a better solution would be to use memcpy_toio (more portable
- * anyway) and modify that routine to support REALLY_SLOW_IO-style
- * defines; ARCnet probably is not the only driver that can screw up an
- * ftape DMA transfer.
- *
- * Turn this on if you have timing-sensitive DMA (ie. a tape drive) and
- * would like to sacrifice a little bit of network speed to reduce tape
- * write retries or some related problem.
- */
-#undef SLOW_XMIT_COPY
-
-/* The card sends the reconfiguration signal when it loses the connection to
- * the rest of its network. It is a 'Hello, is anybody there?' cry. This
- * usually happens when a new computer on the network is powered on or when
- * the cable is broken.
- *
- * Define DETECT_RECONFIGS if you want to detect network reconfigurations.
- * Recons may be a real nuisance on a larger ARCnet network; if you are a
- * network administrator you probably would like to count them.
- * Reconfigurations will be recorded in stats.tx_carrier_errors (the last
- * field of the /proc/net/dev file).
- *
- * Define SHOW_RECONFIGS if you really want to see a log message whenever
- * a RECON occurs.
- */
-#define DETECT_RECONFIGS
-#undef SHOW_RECONFIGS
-
-/* RECON_THRESHOLD is the maximum number of RECON messages to receive within
- * one minute before printing a "cabling problem" warning. You must have
- * DETECT_RECONFIGS enabled if you want to use this. The default value
- * should be fine.
- *
- * After that, a "cabling restored" message will be printed on the next IRQ
- * if no RECON messages have been received for 10 seconds.
- *
- * Do not define RECON_THRESHOLD at all if you want to disable this feature.
- */
-#define RECON_THRESHOLD 30
-
-/* Define this to the minimum "timeout" value. If a transmit takes longer
- * than TX_TIMEOUT jiffies, Linux will abort the TX and retry. On a large
- * network, or one with heavy network traffic, this timeout may need to be
- * increased. The larger it is, though, the longer it will be between
- * necessary transmits - don't set this too large.
- */
-#define TX_TIMEOUT 20
-
-/* Define this to speed up the autoprobe by assuming if only one io port and
- * shmem are left in the list at Stage 5, they must correspond to each
- * other.
- *
- * This is undefined by default because it might not always be true, and the
- * extra check makes the autoprobe even more careful. Speed demons can turn
- * it on - I think it should be fine if you only have one ARCnet card
- * installed.
- *
- * If no ARCnet cards are installed, this delay never happens anyway and thus
- * the option has no effect.
- */
-#undef FAST_PROBE
-
-/* Define this to speed up "ifconfig up" by moving the card reset command
- * around. This is a new option in 2.41 ALPHA. If it causes problems,
- * undefine this to get the old behaviour; then send me email, because if
- * there are no problems, this option will go away very soon.
- */
-#define FAST_IFCONFIG
-
/* Define this if you want to make it easier to use the "call trace" when
* a kernel NULL pointer assignment occurs. Hopefully unnecessary, most of
* the time. It will make all the function names (and other things) show
@@ -269,353 +206,80 @@ static const char *version =
/**************************************************************************/
-/* New debugging bitflags: each option can be enabled individually.
- *
- * These can be set while the driver is running by typing:
- * ifconfig arc0 down metric 1xxx HOSTNAME
- * where 1xxx is 1000 + the debug level you want
- * and HOSTNAME is your hostname/ip address
- * and then resetting your routes.
- *
- * An ioctl() should be used for this instead, someday.
- *
- * Note: only debug flags included in the ARCNET_DEBUG_MAX define will
- * actually be available. GCC will (at least, GCC 2.7.0 will) notice
- * lines using a BUGLVL not in ARCNET_DEBUG_MAX and automatically optimize
- * them out.
+/* These are now provided by the chipset driver. There's a performance
+ * overhead in using them.
*/
-#define D_NORMAL 1 /* important operational info */
-#define D_EXTRA 2 /* useful, but non-vital information */
-#define D_INIT 4 /* show init/probe messages */
-#define D_INIT_REASONS 8 /* show reasons for discarding probes */
-/* debug levels below give LOTS of output during normal operation! */
-#define D_DURING 16 /* trace operations (including irq's) */
-#define D_TX 32 /* show tx packets */
-#define D_RX 64 /* show rx packets */
-#define D_SKB 128 /* show skb's */
-
-#ifndef ARCNET_DEBUG_MAX
-#define ARCNET_DEBUG_MAX (~0) /* enable ALL debug messages */
-/*#define ARCNET_DEBUG_MAX (D_NORMAL|D_EXTRA|D_INIT|D_INIT_REASONS) */
-/*#define ARCNET_DEBUG_MAX 0 */ /* enable NO messages (bad idea) */
-#endif
-
-#ifndef ARCNET_DEBUG
-#define ARCNET_DEBUG (D_NORMAL|D_EXTRA)
-#endif
-int arcnet_debug = ARCNET_DEBUG;
-
-/* macros to simplify debug checking */
-#define BUGLVL(x) if ((ARCNET_DEBUG_MAX)&arcnet_debug&(x))
-#define BUGMSG2(x,msg,args...) BUGLVL(x) printk(msg, ## args)
-#define BUGMSG(x,msg,args...) BUGMSG2(x,"%s%6s: " msg, \
- x==D_NORMAL ? KERN_WARNING : \
- x<=D_INIT_REASONS ? KERN_INFO : KERN_DEBUG , \
- dev->name , ## args)
-
-/* Some useful multiprotocol macros. The idea here is that GCC will
- * optimize away multiple tests or assignments to lp->adev. Relying on this
- * results in the cleanest mess possible.
- */
-#define ADEV lp->adev
-
-#ifdef CONFIG_ARCNET_ETH
- #define EDEV lp->edev
-#else
- #define EDEV lp->adev
-#endif
-
-#ifdef CONFIG_ARCNET_1051
- #define SDEV lp->sdev
-#else
- #define SDEV lp->adev
-#endif
-#define TBUSY ADEV->tbusy=EDEV->tbusy=SDEV->tbusy
-#define IF_TBUSY (ADEV->tbusy||EDEV->tbusy||SDEV->tbusy)
+#define AINTMASK(x) ((*lp->asetmask)(dev, x))
+#define ARCSTATUS ((*lp->astatus)(dev))
+#define ACOMMAND(x) ((*lp->acommand)(dev, x))
-#define INTERRUPT ADEV->interrupt=EDEV->interrupt=SDEV->interrupt
-#define IF_INTERRUPT (ADEV->interrupt||EDEV->interrupt||SDEV->interrupt)
+int arcnet_debug=ARCNET_DEBUG;
-#define START ADEV->start=EDEV->start=SDEV->start
+/* Exported function prototypes */
-
-/* The number of low I/O ports used by the ethercard. */
-#define ARCNET_TOTAL_SIZE 16
-
-/* Handy defines for ARCnet specific stuff */
- /* COM 9026 controller chip --> ARCnet register addresses */
-#define _INTMASK (ioaddr+0) /* writable */
-#define _STATUS (ioaddr+0) /* readable */
-#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */
-#define _RESET (ioaddr+8) /* software reset (on read) */
-
-/* RIM I (command/status is memory mapped) versus RIM III (standard I/O
- * mapped) macros. These make things a bit cleaner.
- */
-#ifdef RIM_I_MODE
- #define IOADDR (dev->mem_start+0x800)
- #define ARCSTATUS readb(_STATUS)
- #define ACOMMAND(cmd) writeb((cmd),_COMMAND)
- #define ARCRESET writeb(TESTvalue,ioaddr-0x800) /* fake reset */
- #define AINTMASK(msk) writeb((msk),_INTMASK)
- #define RELEASE_REGION(x,y) /* nothing */
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
#else
- #define IOADDR (dev->base_addr)
- #define ARCSTATUS inb(_STATUS)
- #define ACOMMAND(cmd) outb((cmd),_COMMAND)
- #define AINTMASK(msk) outb((msk),_INTMASK)
- #define ARCRESET inb(_RESET)
- #define RELEASE_REGION(x,y) release_region((x),(y))
+void arcnet_init(void);
+static int init_module(void);
+#ifdef CONFIG_ARCNET_COM90xx
+extern char com90xx_explicit;
+extern int arc90xx_probe(struct device *dev);
#endif
-
-#define SETMASK AINTMASK(lp->intmask)
-
- /* Time needed to reset the card - in jiffies. This works on my SMC
- * PC100. I can't find a reference that tells me just how long I
- * should wait.
- */
-#define RESETtime (HZ * 3 / 10) /* reset */
-
- /* these are the max/min lengths of packet data. (including
- * ClientData header)
- * note: packet sizes 250, 251, 252 are impossible (God knows why)
- * so exception packets become necessary.
- *
- * These numbers are compared with the length of the full packet,
- * including ClientData header.
- */
-#define MTU 253 /* normal packet max size */
-#define MinTU 257 /* extended packet min size */
-#define XMTU 508 /* extended packet max size */
-
- /* status/interrupt mask bit fields */
-#define TXFREEflag 0x01 /* transmitter available */
-#define TXACKflag 0x02 /* transmitted msg. ackd */
-#define RECONflag 0x04 /* system reconfigured */
-#define TESTflag 0x08 /* test flag */
-#define RESETflag 0x10 /* power-on-reset */
-#define RES1flag 0x20 /* reserved - usually set by jumper */
-#define RES2flag 0x40 /* reserved - usually set by jumper */
-#define NORXflag 0x80 /* receiver inhibited */
-
- /* in the command register, the following bits have these meanings:
- * 0-2 command
- * 3-4 page number (for enable rcv/xmt command)
- * 7 receive broadcasts
- */
-#define NOTXcmd 0x01 /* disable transmitter */
-#define NORXcmd 0x02 /* disable receiver */
-#define TXcmd 0x03 /* enable transmitter */
-#define RXcmd 0x04 /* enable receiver */
-#define CONFIGcmd 0x05 /* define configuration */
-#define CFLAGScmd 0x06 /* clear flags */
-#define TESTcmd 0x07 /* load test flags */
-
- /* flags for "clear flags" command */
-#define RESETclear 0x08 /* power-on-reset */
-#define CONFIGclear 0x10 /* system reconfigured */
-
- /* flags for "load test flags" command */
-#define TESTload 0x08 /* test flag (diagnostic) */
-
- /* byte deposited into first address of buffers on reset */
-#define TESTvalue 0321 /* that's octal for 0xD1 :) */
-
- /* for "enable receiver" command */
-#define RXbcasts 0x80 /* receive broadcasts */
-
- /* flags for "define configuration" command */
-#define NORMALconf 0x00 /* 1-249 byte packets */
-#define EXTconf 0x08 /* 250-504 byte packets */
-
- /* Starts receiving packets into recbuf.
- */
-#define EnableReceiver() ACOMMAND(RXcmd|(recbuf<<3)|RXbcasts)
-
- /* RFC1201 Protocol ID's */
-#define ARC_P_IP 212 /* 0xD4 */
-#define ARC_P_ARP 213 /* 0xD5 */
-#define ARC_P_RARP 214 /* 0xD6 */
-#define ARC_P_IPX 250 /* 0xFA */
-#define ARC_P_NOVELL_EC 236 /* 0xEC */
-
- /* Old RFC1051 Protocol ID's */
-#define ARC_P_IP_RFC1051 240 /* 0xF0 */
-#define ARC_P_ARP_RFC1051 241 /* 0xF1 */
-
- /* MS LanMan/WfWg protocol */
-#define ARC_P_ETHER 0xE8
-
- /* Unsupported/indirectly supported protocols */
-#define ARC_P_DATAPOINT_BOOT 0 /* very old Datapoint equipment */
-#define ARC_P_DATAPOINT_MOUNT 1
-#define ARC_P_POWERLAN_BEACON 8 /* Probably ATA-Netbios related */
-#define ARC_P_POWERLAN_BEACON2 243
-#define ARC_P_LANSOFT 251 /* 0xFB - what is this? */
-#define ARC_P_ATALK 0xDD
-
- /* the header required by the card itself */
-struct HardHeader
-{
- u_char source, /* source ARCnet - filled in automagically */
- destination, /* destination ARCnet - 0 for broadcast */
- offset1, /* offset of ClientData (256-byte packets) */
- offset2; /* offset of ClientData (512-byte packets) */
-};
-
- /* a complete ARCnet packet */
-union ArcPacket
-{
- struct HardHeader hardheader; /* the hardware header */
- u_char raw[512]; /* raw packet info, incl ClientData */
-};
-
- /* the "client data" header - RFC1201 information
- * notice that this screws up if it's not an even number of bytes
- * <sigh>
- */
-struct ClientData
-{
- /* data that's NOT part of real packet - we MUST get rid of it before
- * actually sending!!
- */
- u_char saddr, /* Source address - needed for IPX */
- daddr; /* Destination address */
-
- /* data that IS part of real packet */
- u_char protocol_id, /* ARC_P_IP, ARC_P_ARP, etc */
- split_flag; /* for use with split packets */
- u_short sequence; /* sequence number */
-};
-#define EXTRA_CLIENTDATA (sizeof(struct ClientData)-4)
-
-
- /* the "client data" header - RFC1051 information
- * this also screws up if it's not an even number of bytes
- * <sigh again>
- */
-struct S_ClientData
-{
- /* data that's NOT part of real packet - we MUST get rid of it before
- * actually sending!!
- */
- u_char saddr, /* Source address - needed for IPX */
- daddr, /* Destination address */
- junk; /* padding to make an even length */
-
- /* data that IS part of real packet */
- u_char protocol_id; /* ARC_P_IP, ARC_P_ARP, etc */
-};
-#define S_EXTRA_CLIENTDATA (sizeof(struct S_ClientData)-1)
-
-
-/* "Incoming" is information needed for each address that could be sending
- * to us. Mostly for partially-received split packets.
- */
-struct Incoming
-{
- struct sk_buff *skb; /* packet data buffer */
- unsigned char lastpacket, /* number of last packet (from 1) */
- numpackets; /* number of packets in split */
- u_short sequence; /* sequence number of assembly */
-};
-
-struct Outgoing
-{
- struct sk_buff *skb; /* buffer from upper levels */
- struct ClientData *hdr; /* clientdata of last packet */
- u_char *data; /* pointer to data in packet */
- short length, /* bytes total */
- dataleft, /* bytes left */
- segnum, /* segment being sent */
- numsegs, /* number of segments */
- seglen; /* length of segment */
-};
-
-
-/* Information that needs to be kept for each board. */
-struct arcnet_local {
- struct net_device_stats stats;
- u_short sequence; /* sequence number (incs with each packet) */
- u_char stationid, /* our 8-bit station address */
- recbuf, /* receive buffer # (0 or 1) */
- txbuf, /* transmit buffer # (2 or 3) */
- txready, /* buffer where a packet is ready to send */
- intmask; /* current value of INTMASK register */
- short intx, /* in TX routine? */
- in_txhandler, /* in TX_IRQ handler? */
- sending, /* transmit in progress? */
- lastload_dest, /* can last loaded packet be acked? */
- lasttrans_dest; /* can last TX'd packet be acked? */
-
-#if defined(DETECT_RECONFIGS) && defined(RECON_THRESHOLD)
- time_t first_recon, /* time of "first" RECON message to count */
- last_recon; /* time of most recent RECON */
- int num_recons, /* number of RECONs between first and last. */
- network_down; /* do we think the network is down? */
#endif
- struct timer_list timer; /* the timer interrupt struct */
- struct Incoming incoming[256]; /* one from each address */
- struct Outgoing outgoing; /* packet currently being sent */
-
- struct device *adev; /* RFC1201 protocol device */
-
-#ifdef CONFIG_ARCNET_ETH
- struct device *edev; /* Ethernet-Encap device */
-#endif
-
-#ifdef CONFIG_ARCNET_1051
- struct device *sdev; /* RFC1051 protocol device */
-#endif
-};
-
-
-/* Index to functions, as function prototypes. */
+void arcnet_tx_done(struct device *dev, struct arcnet_local *lp);
+void arcnet_use_count (int open);
+void arcnet_setup(struct device *dev);
+void arcnet_makename(char *device);
+void arcnetA_continue_tx(struct device *dev);
+int arcnet_go_tx(struct device *dev,int enable_irq);
+void arcnet_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+void arcnet_rx(struct arcnet_local *lp, u_char *arcsoft, short length, int saddr, int daddr);
+
+EXPORT_SYMBOL(arcnet_debug);
+EXPORT_SYMBOL(arcnet_tx_done);
+EXPORT_SYMBOL(arcnet_use_count);
+EXPORT_SYMBOL(arcnet_setup);
+EXPORT_SYMBOL(arcnet_makename);
+EXPORT_SYMBOL(arcnetA_continue_tx);
+EXPORT_SYMBOL(arcnet_go_tx);
+EXPORT_SYMBOL(arcnet_interrupt);
+EXPORT_SYMBOL(arcnet_rx);
#if ARCNET_DEBUG_MAX & D_SKB
-static void arcnet_dump_skb(struct device *dev,struct sk_buff *skb,
+void arcnet_dump_skb(struct device *dev,struct sk_buff *skb,
char *desc);
+EXPORT_SYMBOL(arcnet_dump_skb);
#else
# define arcnet_dump_skb(dev,skb,desc) ;
#endif
#if (ARCNET_DEBUG_MAX & D_RX) || (ARCNET_DEBUG_MAX & D_TX)
-static void arcnet_dump_packet(struct device *dev,u_char *buffer,int ext,
+void arcnet_dump_packet(struct device *dev,u_char *buffer,int ext,
char *desc);
+EXPORT_SYMBOL(arcnet_dump_packet);
#else
# define arcnet_dump_packet(dev,buffer,ext,desc) ;
#endif
-extern int arcnet_probe(struct device *dev);
-static int arcnet_found(struct device *dev,int port,int airq,u_long shmem);
+/* Internal function prototypes */
-static void arcnet_setup(struct device *dev);
static int arcnet_open(struct device *dev);
static int arcnet_close(struct device *dev);
-static int arcnet_reset(struct device *dev,int reset_delay);
-
+static int arcnetA_header(struct sk_buff *skb,struct device *dev,
+ unsigned short type,void *daddr,void *saddr,unsigned len);
+static int arcnetA_rebuild_header(struct sk_buff *skb);
static int arcnet_send_packet_bad(struct sk_buff *skb,struct device *dev);
static int arcnetA_send_packet(struct sk_buff *skb, struct device *dev);
-static void arcnetA_continue_tx(struct device *dev);
-static void arcnetAS_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
- char *data,int length,int daddr,int exceptA);
-static int arcnet_go_tx(struct device *dev,int enable_irq);
-
-static void arcnet_interrupt(int irq,void *dev_id,struct pt_regs *regs);
-static void arcnet_inthandler(struct device *dev);
-
-static void arcnet_rx(struct device *dev,int recbuf);
static void arcnetA_rx(struct device *dev,u_char *buf,
int length,u_char saddr, u_char daddr);
-
static struct net_device_stats *arcnet_get_stats(struct device *dev);
+static unsigned short arcnetA_type_trans(struct sk_buff *skb,
+ struct device *dev);
-int arcnetA_header(struct sk_buff *skb,struct device *dev,
- unsigned short type,void *daddr,void *saddr,unsigned len);
-int arcnetA_rebuild_header(struct sk_buff *skb);
-unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev);
#ifdef CONFIG_ARCNET_ETH
/* functions specific to Ethernet-Encap */
@@ -626,6 +290,7 @@ static void arcnetE_rx(struct device *dev,u_char *arcsoft,
int length,u_char saddr, u_char daddr);
#endif
+
#ifdef CONFIG_ARCNET_1051
/* functions specific to RFC1051 */
static int arcnetS_init(struct device *dev);
@@ -633,21 +298,12 @@ static int arcnetS_open_close(struct device *dev);
static int arcnetS_send_packet(struct sk_buff *skb, struct device *dev);
static void arcnetS_rx(struct device *dev,u_char *buf,
int length,u_char saddr, u_char daddr);
-int arcnetS_header(struct sk_buff *skb,struct device *dev,
+static int arcnetS_header(struct sk_buff *skb,struct device *dev,
unsigned short type,void *daddr,void *saddr,unsigned len);
-int arcnetS_rebuild_header(struct sk_buff *skb);
-unsigned short arcnetS_type_trans(struct sk_buff *skb,struct device *dev);
+static int arcnetS_rebuild_header(struct sk_buff *skb);
+static unsigned short arcnetS_type_trans(struct sk_buff *skb,struct device *dev);
#endif
-#ifdef MODULE
-int init_module(void);
-void cleanup_module(void);
-#endif
-
-#define tx_done(dev) 1
-
-#define JIFFER(time) for (delayval=jiffies+time; jiffies<delayval;) ;
-
/****************************************************************************
* *
@@ -677,6 +333,7 @@ void arcnet_dump_skb(struct device *dev,struct sk_buff *skb,char *desc)
}
#endif
+
/* Dump the contents of an ARCnet buffer
*/
#if (ARCNET_DEBUG_MAX & D_RX) || (ARCNET_DEBUG_MAX & D_TX)
@@ -699,609 +356,6 @@ void arcnet_dump_packet(struct device *dev,u_char *buffer,int ext,char *desc)
}
#endif
-/****************************************************************************
- * *
- * Probe and initialization *
- * *
- ****************************************************************************/
-
-#ifdef RIM_I_MODE
-
-/* We cannot probe for a RIM I card; one reason is I don't know how to reset
- * them. In fact, we can't even get their node ID automatically. So, we
- * need to be passed a specific shmem address, IRQ, and node ID (stored in
- * dev->base_addr)
- */
-__initfunc(int arcnet_probe(struct device *dev))
-{
- BUGLVL(D_NORMAL) printk(version);
- BUGMSG(D_NORMAL,"Compiled for ARCnet RIM I (autoprobe disabled)\n");
- BUGMSG(D_NORMAL,"Given: node %02lXh, shmem %lXh, irq %d\n",
- dev->base_addr,dev->mem_start,dev->irq);
-
- if (dev->mem_start<=0 || dev->irq<=0)
- {
- BUGMSG(D_NORMAL,"No autoprobe for RIM I; you "
- "must specify the shmem and irq!\n");
- return -ENODEV;
- }
-
- if (dev->base_addr<=0 || dev->base_addr>255)
- {
- BUGMSG(D_NORMAL,"You need to specify your card's station "
- "ID!\n");
- return -ENODEV;
- }
-
- return arcnet_found(dev,dev->base_addr,dev->irq,dev->mem_start);
-}
-
-#else /* not RIM_I_MODE, so use a real autoprobe */
-
-/* Check for an ARCnet network adaptor, and return '0' if one exists.
- * If dev->base_addr == 0, probe all likely locations.
- * If dev->base_addr == 1, always return failure.
- * If dev->base_addr == 2, allocate space for the device and return success
- * (detachable devices only).
- *
- * NOTE: the list of possible ports/shmems is static, so it is retained
- * across calls to arcnet_probe. So, if more than one ARCnet probe is made,
- * values that were discarded once will not even be tried again.
- *
- * FIXME: grab all devices in one shot and eliminate the big static array.
- */
-
-static int ports[(0x3f0 - 0x200) / 16 + 1] __initdata = { 0 };
-static u_long shmems[(0xFF800 - 0xA0000) / 2048 + 1] __initdata = { 0 };
-
-__initfunc(int arcnet_probe(struct device *dev))
-{
- static int init_once = 0;
- static int numports=sizeof(ports)/sizeof(ports[0]),
- numshmems=sizeof(shmems)/sizeof(shmems[0]);
-
- int count,status,delayval,ioaddr,numprint,airq,retval=-ENODEV,
- openparen=0;
- unsigned long airqmask;
- int *port;
- u_long *shmem;
-
- if (!init_once)
- {
- for (count=0x200; count<=0x3f0; count+=16)
- ports[(count-0x200)/16] = count;
- for (count=0xA0000; count<=0xFF800; count+=2048)
- shmems[(count-0xA0000)/2048] = count;
- init_once=1;
- }
-
- BUGLVL(D_NORMAL) printk(version);
-
- BUGMSG(D_DURING,"space used for probe buffers: %d+%d=%d bytes\n",
- sizeof(ports),sizeof(shmems),
- sizeof(ports)+sizeof(shmems));
-
-
-#if 1
- BUGLVL(D_EXTRA)
- {
- printk("arcnet: ***\n");
- printk("arcnet: * Read arcnet.txt for important release notes!\n");
- printk("arcnet: *\n");
- printk("arcnet: * This is an ALPHA version! (Last stable release: v2.56) E-mail me if\n");
- printk("arcnet: * you have any questions, comments, or bug reports.\n");
- printk("arcnet: ***\n");
- }
-#endif
-
- BUGMSG(D_INIT,"given: base %lXh, IRQ %d, shmem %lXh\n",
- dev->base_addr,dev->irq,dev->mem_start);
-
- if (dev->base_addr > 0x1ff) /* Check a single specified port */
- {
- ports[0]=dev->base_addr;
- numports=1;
- }
- else if (dev->base_addr > 0) /* Don't probe at all. */
- return -ENXIO;
-
- if (dev->mem_start)
- {
- shmems[0]=dev->mem_start;
- numshmems=1;
- }
-
-
- /* Stage 1: abandon any reserved ports, or ones with status==0xFF
- * (empty), and reset any others by reading the reset port.
- */
- BUGMSG(D_INIT,"Stage 1: ");
- numprint=0;
- for (port = &ports[0]; port-ports<numports; port++)
- {
- numprint++;
- if (numprint>8)
- {
- BUGMSG2(D_INIT,"\n");
- BUGMSG(D_INIT,"Stage 1: ");
- numprint=1;
- }
- BUGMSG2(D_INIT,"%Xh ",*port);
-
- ioaddr=*port;
-
- if (check_region(*port, ARCNET_TOTAL_SIZE))
- {
- BUGMSG2(D_INIT_REASONS,"(check_region)\n");
- BUGMSG(D_INIT_REASONS,"Stage 1: ");
- BUGLVL(D_INIT_REASONS) numprint=0;
- *port=ports[numports-1];
- numports--;
- port--;
- continue;
- }
-
- if (ARCSTATUS == 0xFF)
- {
- BUGMSG2(D_INIT_REASONS,"(empty)\n");
- BUGMSG(D_INIT_REASONS,"Stage 1: ");
- BUGLVL(D_INIT_REASONS) numprint=0;
- *port=ports[numports-1];
- numports--;
- port--;
- continue;
- }
-
- ARCRESET; /* begin resetting card */
-
- BUGMSG2(D_INIT_REASONS,"\n");
- BUGMSG(D_INIT_REASONS,"Stage 1: ");
- BUGLVL(D_INIT_REASONS) numprint=0;
- }
- BUGMSG2(D_INIT,"\n");
-
- if (!numports)
- {
- BUGMSG(D_NORMAL,"Stage 1: No ARCnet cards found.\n");
- return -ENODEV;
- }
-
-
- /* Stage 2: we have now reset any possible ARCnet cards, so we can't
- * do anything until they finish. If D_INIT, print the list of
- * cards that are left.
- */
- BUGMSG(D_INIT,"Stage 2: ");
- numprint=0;
- for (port = &ports[0]; port-ports<numports; port++)
- {
- numprint++;
- if (numprint>8)
- {
- BUGMSG2(D_INIT,"\n");
- BUGMSG(D_INIT,"Stage 2: ");
- numprint=1;
- }
- BUGMSG2(D_INIT,"%Xh ",*port);
- }
- BUGMSG2(D_INIT,"\n");
- JIFFER(RESETtime);
-
-
- /* Stage 3: abandon any shmem addresses that don't have the signature
- * 0xD1 byte in the right place, or are read-only.
- */
- BUGMSG(D_INIT,"Stage 3: ");
- numprint=0;
- for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
- {
- u_long ptr;
-
- numprint++;
- if (numprint>8)
- {
- BUGMSG2(D_INIT,"\n");
- BUGMSG(D_INIT,"Stage 3: ");
- numprint=1;
- }
- BUGMSG2(D_INIT,"%lXh ",*shmem);
-
- ptr=(u_long)(*shmem);
-
- if (readb(ptr) != TESTvalue)
- {
- BUGMSG2(D_INIT_REASONS,"(mem=%02Xh, not %02Xh)\n",
- readb(ptr),TESTvalue);
- BUGMSG(D_INIT_REASONS,"Stage 3: ");
- BUGLVL(D_INIT_REASONS) numprint=0;
- *shmem=shmems[numshmems-1];
- numshmems--;
- shmem--;
- continue;
- }
-
- /* By writing 0x42 to the TESTvalue location, we also make
- * sure no "mirror" shmem areas show up - if they occur
- * in another pass through this loop, they will be discarded
- * because *cptr != TESTvalue.
- */
- writeb(0x42,ptr);
- if (readb(ptr) != 0x42)
- {
- BUGMSG2(D_INIT_REASONS,"(read only)\n");
- BUGMSG(D_INIT_REASONS,"Stage 3: ");
- *shmem=shmems[numshmems-1];
- numshmems--;
- shmem--;
- continue;
- }
-
- BUGMSG2(D_INIT_REASONS,"\n");
- BUGMSG(D_INIT_REASONS,"Stage 3: ");
- BUGLVL(D_INIT_REASONS) numprint=0;
- }
- BUGMSG2(D_INIT,"\n");
-
- if (!numshmems)
- {
- BUGMSG(D_NORMAL,"Stage 3: No ARCnet cards found.\n");
- return -ENODEV;
- }
-
- /* Stage 4: something of a dummy, to report the shmems that are
- * still possible after stage 3.
- */
- BUGMSG(D_INIT,"Stage 4: ");
- numprint=0;
- for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
- {
- numprint++;
- if (numprint>8)
- {
- BUGMSG2(D_INIT,"\n");
- BUGMSG(D_INIT,"Stage 4: ");
- numprint=1;
- }
- BUGMSG2(D_INIT,"%lXh ",*shmem);
- }
- BUGMSG2(D_INIT,"\n");
-
-
- /* Stage 5: for any ports that have the correct status, can disable
- * the RESET flag, and (if no irq is given) generate an autoirq,
- * register an ARCnet device.
- *
- * Currently, we can only register one device per probe, so quit
- * after the first one is found.
- */
- BUGMSG(D_INIT,"Stage 5: ");
- numprint=0;
- for (port = &ports[0]; port-ports<numports; port++)
- {
- numprint++;
- if (numprint>8)
- {
- BUGMSG2(D_INIT,"\n");
- BUGMSG(D_INIT,"Stage 5: ");
- numprint=1;
- }
- BUGMSG2(D_INIT,"%Xh ",*port);
-
- ioaddr=*port;
- status=ARCSTATUS;
-
- if ((status & 0x9D)
- != (NORXflag|RECONflag|TXFREEflag|RESETflag))
- {
- BUGMSG2(D_INIT_REASONS,"(status=%Xh)\n",status);
- BUGMSG(D_INIT_REASONS,"Stage 5: ");
- BUGLVL(D_INIT_REASONS) numprint=0;
- *port=ports[numports-1];
- numports--;
- port--;
- continue;
- }
-
- ACOMMAND(CFLAGScmd|RESETclear|CONFIGclear);
- status=ARCSTATUS;
- if (status & RESETflag)
- {
- BUGMSG2(D_INIT_REASONS," (eternal reset, status=%Xh)\n",
- status);
- BUGMSG(D_INIT_REASONS,"Stage 5: ");
- BUGLVL(D_INIT_REASONS) numprint=0;
- *port=ports[numports-1];
- numports--;
- port--;
- continue;
- }
-
- /* skip this completely if an IRQ was given, because maybe
- * we're on a machine that locks during autoirq!
- */
- if (!dev->irq)
- {
- /* if we do this, we're sure to get an IRQ since the
- * card has just reset and the NORXflag is on until
- * we tell it to start receiving.
- */
- airqmask = probe_irq_on();
- AINTMASK(NORXflag);
- udelay(1);
- AINTMASK(0);
- airq = probe_irq_off(airqmask);
-
- if (airq<=0)
- {
- BUGMSG2(D_INIT_REASONS,"(airq=%d)\n",airq);
- BUGMSG(D_INIT_REASONS,"Stage 5: ");
- BUGLVL(D_INIT_REASONS) numprint=0;
- *port=ports[numports-1];
- numports--;
- port--;
- continue;
- }
- }
- else
- {
- airq=dev->irq;
- }
-
- BUGMSG2(D_INIT,"(%d,", airq);
- openparen=1;
-
- /* Everything seems okay. But which shmem, if any, puts
- * back its signature byte when the card is reset?
- *
- * If there are multiple cards installed, there might be
- * multiple shmems still in the list.
- */
-#ifdef FAST_PROBE
- if (numports>1 || numshmems>1)
- {
- ARCRESET;
- JIFFER(RESETtime);
- }
- else
- {
- /* just one shmem and port, assume they match */
- writeb(TESTvalue,shmems[0]);
- }
-#else
- ARCRESET;
- JIFFER(RESETtime);
-#endif
-
-
- for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
- {
- u_long ptr;
- ptr=(u_long)(*shmem);
-
- if (readb(ptr) == TESTvalue) /* found one */
- {
- BUGMSG2(D_INIT,"%lXh)\n", *shmem);
- openparen=0;
-
- /* register the card */
- retval=arcnet_found(dev,*port,airq,*shmem);
- if (retval) openparen=0;
-
- /* remove shmem from the list */
- *shmem=shmems[numshmems-1];
- numshmems--;
-
- break;
- }
- else
- {
- BUGMSG2(D_INIT_REASONS,"%Xh-", readb(ptr));
- }
- }
-
- if (openparen)
- {
- BUGMSG2(D_INIT,"no matching shmem)\n");
- BUGMSG(D_INIT_REASONS,"Stage 5: ");
- BUGLVL(D_INIT_REASONS) numprint=0;
- }
-
- *port=ports[numports-1];
- numports--;
- port--;
-
- if (!retval) break;
- }
- BUGMSG(D_INIT_REASONS,"\n");
-
- /* Now put back TESTvalue on all leftover shmems.
- */
- for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
- writeb(TESTvalue,*shmem);
-
- if (retval) BUGMSG(D_NORMAL,"Stage 5: No ARCnet cards found.\n");
- return retval;
-}
-
-#endif /* !RIM_I_MODE */
-
-
-/* Set up the struct device associated with this card. Called after
- * probing succeeds.
- */
-__initfunc(int arcnet_found(struct device *dev,int port,int airq, u_long shmem))
-{
- u_long first_mirror,last_mirror;
- struct arcnet_local *lp;
- int mirror_size;
-
- /* reserve the irq */
- if (request_irq(airq,&arcnet_interrupt,0,"arcnet",NULL))
- {
- BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
- return -ENODEV;
- }
- irq2dev_map[airq]=dev;
- dev->irq=airq;
-
-#ifdef RIM_I_MODE
- dev->base_addr=0;
- writeb(TESTvalue,shmem);
- writeb(port,shmem+1); /* actually the node ID */
-#else
- /* reserve the I/O region - guaranteed to work by check_region */
- request_region(port,ARCNET_TOTAL_SIZE,"arcnet");
- dev->base_addr=port;
-#endif
-
- /* find the real shared memory start/end points, including mirrors */
-
- #define BUFFER_SIZE (512)
- #define MIRROR_SIZE (BUFFER_SIZE*4)
-
- /* guess the actual size of one "memory mirror" - the number of
- * bytes between copies of the shared memory. On most cards, it's
- * 2k (or there are no mirrors at all) but on some, it's 4k.
- */
- mirror_size=MIRROR_SIZE;
- if (readb(shmem)==TESTvalue
- && readb(shmem-mirror_size)!=TESTvalue
- && readb(shmem-2*mirror_size)==TESTvalue)
- mirror_size*=2;
-
- first_mirror=last_mirror=shmem;
- while (readb(first_mirror)==TESTvalue) first_mirror-=mirror_size;
- first_mirror+=mirror_size;
-
- while (readb(last_mirror)==TESTvalue) last_mirror+=mirror_size;
- last_mirror-=mirror_size;
-
- dev->mem_start=first_mirror;
- dev->mem_end=last_mirror+MIRROR_SIZE-1;
- dev->rmem_start=dev->mem_start+BUFFER_SIZE*0;
- dev->rmem_end=dev->mem_start+BUFFER_SIZE*2-1;
-
- /* Initialize the rest of the device structure. */
-
- dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
- if (dev->priv == NULL)
- {
- irq2dev_map[airq] = NULL;
- free_irq(airq,NULL);
- RELEASE_REGION(port,ARCNET_TOTAL_SIZE);
- return -ENOMEM;
- }
- memset(dev->priv,0,sizeof(struct arcnet_local));
- lp=(struct arcnet_local *)(dev->priv);
-
- dev->open=arcnet_open;
- dev->stop=arcnet_close;
- dev->hard_start_xmit=arcnetA_send_packet;
- dev->get_stats=arcnet_get_stats;
- /*dev->set_multicast_list = &set_multicast_list;*/
-
- /* Fill in the fields of the device structure with generic
- * values.
- */
- arcnet_setup(dev);
-
- /* And now fill particular fields with arcnet values */
- dev->mtu=1500; /* completely arbitrary - agrees with ether, though */
- dev->hard_header_len=sizeof(struct ClientData);
- dev->hard_header=arcnetA_header;
- dev->rebuild_header=arcnetA_rebuild_header;
- lp->sequence=1;
- lp->recbuf=0;
-
- BUGMSG(D_DURING,"ClientData header size is %d.\n",
- sizeof(struct ClientData));
- BUGMSG(D_DURING,"HardHeader size is %d.\n",
- sizeof(struct HardHeader));
-
- /* get and check the station ID from offset 1 in shmem */
- lp->stationid = readb(first_mirror+1);
- if (lp->stationid==0)
- BUGMSG(D_NORMAL,"WARNING! Station address 00 is reserved "
- "for broadcasts!\n");
- else if (lp->stationid==255)
- BUGMSG(D_NORMAL,"WARNING! Station address FF may confuse "
- "DOS networking programs!\n");
- dev->dev_addr[0]=lp->stationid;
-
- BUGMSG(D_NORMAL,"ARCnet station %02Xh found at %03lXh, IRQ %d, "
- "ShMem %lXh (%ld*%d bytes).\n",
- lp->stationid,
- dev->base_addr,dev->irq,dev->mem_start,
- (dev->mem_end-dev->mem_start+1)/mirror_size,mirror_size);
-
- return 0;
-}
-
-
-/* Do a hardware reset on the card, and set up necessary registers.
- *
- * This should be called as little as possible, because it disrupts the
- * token on the network (causes a RECON) and requires a significant delay.
- *
- * However, it does make sure the card is in a defined state.
- */
-int arcnet_reset(struct device *dev,int reset_delay)
-{
- struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
- short ioaddr=IOADDR;
- int delayval,recbuf=lp->recbuf;
-
- /* no IRQ's, please! */
- lp->intmask=0;
- SETMASK;
-
- BUGMSG(D_INIT,"Resetting %s (status=%Xh)\n",
- dev->name,ARCSTATUS);
-
- if (reset_delay)
- {
- /* reset the card */
- ARCRESET;
- JIFFER(RESETtime);
- }
-
- ACOMMAND(CFLAGScmd|RESETclear); /* clear flags & end reset */
- ACOMMAND(CFLAGScmd|CONFIGclear);
-
- /* verify that the ARCnet signature byte is present */
- if (readb(dev->mem_start) != TESTvalue)
- {
- BUGMSG(D_NORMAL,"reset failed: TESTvalue not present.\n");
- return 1;
- }
-
- /* clear out status variables */
- recbuf=lp->recbuf=0;
- lp->txbuf=2;
-
- /* enable extended (512-byte) packets */
- ACOMMAND(CONFIGcmd|EXTconf);
-
-#ifndef SLOW_XMIT_COPY
- /* clean out all the memory to make debugging make more sense :) */
- BUGLVL(D_DURING)
- memset_io(dev->mem_start,0x42,2048);
-#endif
-
- /* and enable receive of our first packet to the first buffer */
- EnableReceiver();
-
- /* re-enable interrupts */
- lp->intmask|=NORXflag;
-#ifdef DETECT_RECONFIGS
- lp->intmask|=RECONflag;
-#endif
- SETMASK;
-
- /* done! return success. */
- return 0;
-}
-
/* Setup a struct device for ARCnet. This should really be in net_init.c
* but since there are three different ARCnet devices ANYWAY... <gargle>
@@ -1319,9 +373,7 @@ void arcnet_setup(struct device *dev)
dev->broadcast[0] = 0x00; /* for us, broadcasts are address 0 */
dev->addr_len = 1;
dev->type = ARPHRD_ARCNET;
- dev->tx_queue_len = 30; /* fairly long queue - arcnet is
- * quite speedy.
- */
+ dev->tx_queue_len = 30;
/* New-style flags. */
dev->flags = IFF_BROADCAST;
@@ -1330,6 +382,17 @@ void arcnet_setup(struct device *dev)
dev->pa_brdaddr = 0;
dev->pa_mask = 0;
dev->pa_alen = 4;
+
+ /* Put in this stuff here, so we don't have to export the symbols
+ * to the chipset drivers.
+ */
+
+ dev->open=arcnet_open;
+ dev->stop=arcnet_close;
+ dev->hard_start_xmit=arcnetA_send_packet;
+ dev->get_stats=arcnet_get_stats;
+ dev->hard_header=arcnetA_header;
+ dev->rebuild_header=arcnetA_rebuild_header;
}
@@ -1339,7 +402,6 @@ void arcnet_setup(struct device *dev)
* *
****************************************************************************/
-
/* Open/initialize the board. This is called sometime after booting when
* the 'ifconfig' program is run.
*
@@ -1350,88 +412,83 @@ void arcnet_setup(struct device *dev)
static int
arcnet_open(struct device *dev)
{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- int ioaddr=IOADDR;
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- if (dev->metric>=1000)
- {
- arcnet_debug=dev->metric-1000;
- printk(KERN_INFO "%6s: debug level set to %d\n",dev->name,arcnet_debug);
- dev->metric=1;
- }
+ if (dev->metric>=1000)
+ {
+ arcnet_debug=dev->metric-1000;
+ printk(KERN_INFO "%6s: debug level set to %d\n",dev->name,arcnet_debug);
+ dev->metric=1;
+ }
- BUGMSG(D_INIT,"arcnet_open: resetting card.\n");
+ BUGMSG(D_INIT,"arcnet_open: resetting card.\n");
-#ifdef FAST_IFCONFIG
- /* try to put the card in a defined state - if it fails the first
- * time, actually reset it.
- */
- if (arcnet_reset(dev,0) && arcnet_reset(dev,1))
- return -ENODEV;
-#else
- /* reset the card twice in case something goes wrong the first time.
- */
- if (arcnet_reset(dev,1) && arcnet_reset(dev,1))
- return -ENODEV;
-#endif
+ /* try to put the card in a defined state - if it fails the first
+ * time, actually reset it.
+ */
+ if ((*lp->arcnet_reset)(dev,0) && (*lp->arcnet_reset)(dev,1))
+ return -ENODEV;
- dev->tbusy=0;
- dev->interrupt=0;
- lp->intx=0;
- lp->in_txhandler=0;
+ dev->tbusy=0;
+ dev->interrupt=0;
+ lp->intx=0;
+ lp->in_txhandler=0;
- /* The RFC1201 driver is the default - just store */
- lp->adev=dev;
- BUGMSG(D_NORMAL,"ARCnet RFC1201 protocol initialized.\n");
+ /* The RFC1201 driver is the default - just store */
+ lp->adev=dev;
+
+ /* we're started */
+ dev->start=1;
#ifdef CONFIG_ARCNET_ETH
- /* Initialize the ethernet-encap protocol driver */
- lp->edev=(struct device *)kmalloc(sizeof(struct device),GFP_KERNEL);
- if (lp->edev == NULL)
- return -ENOMEM;
- memcpy(lp->edev,dev,sizeof(struct device));
- lp->edev->name=(char *)kmalloc(10,GFP_KERNEL);
- if (lp->edev->name == NULL) {
- kfree(lp->edev);
- lp->edev = NULL;
- return -ENOMEM;
- }
- sprintf(lp->edev->name,"%se",dev->name);
- lp->edev->init=arcnetE_init;
- register_netdev(lp->edev);
-#else
- BUGMSG(D_NORMAL,"Ethernet-Encap protocol not available (disabled).\n");
+ /* Initialize the ethernet-encap protocol driver */
+ lp->edev=(struct device *)kmalloc(sizeof(struct device),GFP_KERNEL);
+ if (lp->edev == NULL)
+ return -ENOMEM;
+ memcpy(lp->edev,dev,sizeof(struct device));
+ lp->edev->type=ARPHRD_ETHER;
+ lp->edev->name=(char *)kmalloc(10,GFP_KERNEL);
+ if (lp->edev->name == NULL) {
+ kfree(lp->edev);
+ lp->edev = NULL;
+ return -ENOMEM;
+ }
+ sprintf(lp->edev->name,"%se",dev->name);
+ lp->edev->init=arcnetE_init;
+ register_netdev(lp->edev);
#endif
#ifdef CONFIG_ARCNET_1051
- /* Initialize the RFC1051-encap protocol driver */
- lp->sdev=(struct device *)kmalloc(sizeof(struct device),GFP_KERNEL);
- memcpy(lp->sdev,dev,sizeof(struct device));
- lp->sdev->name=(char *)kmalloc(10,GFP_KERNEL);
- sprintf(lp->sdev->name,"%ss",dev->name);
- lp->sdev->init=arcnetS_init;
- register_netdev(lp->sdev);
-#else
- BUGMSG(D_NORMAL,"RFC1051 protocol not available (disabled).\n");
+ /* Initialize the RFC1051-encap protocol driver */
+ lp->sdev=(struct device *)kmalloc(sizeof(struct device),GFP_KERNEL);
+ memcpy(lp->sdev,dev,sizeof(struct device));
+ lp->sdev->name=(char *)kmalloc(10,GFP_KERNEL);
+ sprintf(lp->sdev->name,"%ss",dev->name);
+ lp->sdev->init=arcnetS_init;
+ register_netdev(lp->sdev);
#endif
- /* we're started */
- START=1;
+ /* Enable TX if we need to */
+ if (lp->en_dis_able_TX)
+ (*lp->en_dis_able_TX)(dev, 1);
- /* make sure we're ready to receive IRQ's.
- * arcnet_reset sets this for us, but if we receive one before
- * START is set to 1, it could be ignored. So, we turn IRQ's
- * off, then on again to clean out the IRQ controller.
- */
- AINTMASK(0);
- udelay(1); /* give it time to set the mask before
- * we reset it again. (may not even be
- * necessary)
- */
- SETMASK;
+ /* make sure we're ready to receive IRQ's.
+ * arcnet_reset sets this for us, but if we receive one before
+ * START is set to 1, it could be ignored. So, we turn IRQ's
+ * off, then on again to clean out the IRQ controller.
+ */
- MOD_INC_USE_COUNT;
- return 0;
+ AINTMASK(0);
+ udelay(1); /* give it time to set the mask before
+ * we reset it again. (may not even be
+ * necessary)
+ */
+ SETMASK;
+
+ /* Let it increase its use count */
+ (*lp->openclose_device)(1);
+
+ return 0;
}
@@ -1440,54 +497,74 @@ arcnet_open(struct device *dev)
static int
arcnet_close(struct device *dev)
{
- int ioaddr=IOADDR;
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- TBUSY=1;
- START=0;
+ if (test_and_set_bit(0, (int *)&dev->tbusy))
+ BUGMSG(D_NORMAL, "arcnet_close: tbusy already set!\n");
- /* Shut down the card */
-#ifdef FAST_IFCONFIG
- ARCRESET; /* reset IRQ won't run if START=0 */
-#else
- lp->intmask=0;
- SETMASK; /* no IRQ's (except RESET, of course) */
- ACOMMAND(NOTXcmd); /* stop transmit */
- ACOMMAND(NORXcmd); /* disable receive */
+ dev->start=0;
+#ifdef CONFIG_ARCNET_1051
+ lp->sdev->tbusy=1;
+ lp->sdev->start=0;
+#endif
+#ifdef CONFIG_ARCNET_ETH
+ lp->edev->tbusy=1;
+ lp->edev->start=0;
#endif
- /* reset more flags */
- INTERRUPT=0;
+ /* Shut down the card */
+
+ /* Disable TX if we need to */
+ if (lp->en_dis_able_TX)
+ (*lp->en_dis_able_TX)(dev, 0);
- /* do NOT free lp->adev!! It's static! */
- lp->adev=NULL;
+ (*lp->arcnet_reset)(dev, 3); /* reset IRQ won't run if START=0 */
+#if 0
+ lp->intmask=0;
+ SETMASK; /* no IRQ's (except RESET, of course) */
+ ACOMMAND(NOTXcmd); /* stop transmit */
+ ACOMMAND(NORXcmd); /* disable receive */
+#endif
+ /* reset more flags */
+ dev->interrupt=0;
#ifdef CONFIG_ARCNET_ETH
- /* free the ethernet-encap protocol device */
- lp->edev->priv=NULL;
- dev_close(lp->edev);
- unregister_netdev(lp->edev);
- kfree(lp->edev->name);
- kfree(lp->edev);
- lp->edev=NULL;
+ lp->edev->interrupt=0;
+#endif
+#ifdef CONFIG_ARCNET_1051
+ lp->sdev->interrupt=0;
+#endif
+
+ /* do NOT free lp->adev!! It's static! */
+ lp->adev=NULL;
+
+#ifdef CONFIG_ARCNET_ETH
+ /* free the ethernet-encap protocol device */
+ lp->edev->priv=NULL;
+ dev_close(lp->edev);
+ unregister_netdev(lp->edev);
+ kfree(lp->edev->name);
+ kfree(lp->edev);
+ lp->edev=NULL;
#endif
#ifdef CONFIG_ARCNET_1051
- /* free the RFC1051-encap protocol device */
- lp->sdev->priv=NULL;
- dev_close(lp->sdev);
- unregister_netdev(lp->sdev);
- kfree(lp->sdev->name);
- kfree(lp->sdev);
- lp->sdev=NULL;
+ /* free the RFC1051-encap protocol device */
+ lp->sdev->priv=NULL;
+ dev_close(lp->sdev);
+ unregister_netdev(lp->sdev);
+ kfree(lp->sdev->name);
+ kfree(lp->sdev);
+ lp->sdev=NULL;
#endif
- /* Update the statistics here. (not necessary in ARCnet) */
+ /* Update the statistics here. (not necessary in ARCnet) */
- MOD_DEC_USE_COUNT;
- return 0;
-}
+ /* Decrease the use count */
+ (*lp->openclose_device)(0);
+ return 0;
+}
/****************************************************************************
@@ -1501,103 +578,116 @@ arcnet_close(struct device *dev)
static int
arcnet_send_packet_bad(struct sk_buff *skb, struct device *dev)
{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- int ioaddr=IOADDR;
-
- BUGMSG(D_DURING,"transmit requested (status=%Xh, inTX=%d)\n",
- ARCSTATUS,lp->intx);
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- if (lp->in_txhandler)
- {
- BUGMSG(D_NORMAL,"send_packet called while in txhandler!\n");
- lp->stats.tx_dropped++;
- return 1;
- }
-
- if (lp->intx>1)
- {
- BUGMSG(D_NORMAL,"send_packet called while intx!\n");
- lp->stats.tx_dropped++;
- return 1;
- }
+ BUGMSG(D_DURING,"transmit requested (status=%Xh, inTX=%d)\n",
+ ARCSTATUS,lp->intx);
- if (IF_TBUSY)
- {
- /* If we get here, some higher level has decided we are broken.
- There should really be a "kick me" function call instead. */
- int tickssofar = jiffies - dev->trans_start;
- /*int recbuf=lp->recbuf;*/
- int status=ARCSTATUS;
+ if (lp->in_txhandler)
+ {
+ BUGMSG(D_NORMAL,"send_packet called while in txhandler!\n");
+ lp->stats.tx_dropped++;
+ return 1;
+ }
- if (tickssofar < TX_TIMEOUT)
- {
- BUGMSG(D_DURING,"premature kickme! (status=%Xh ticks=%d o.skb=%ph numsegs=%d segnum=%d\n",
- status,tickssofar,lp->outgoing.skb,
- lp->outgoing.numsegs,
- lp->outgoing.segnum);
- return 1;
- }
+ if (lp->intx>1)
+ {
+ BUGMSG(D_NORMAL,"send_packet called while intx!\n");
+ lp->stats.tx_dropped++;
+ return 1;
+ }
- lp->intmask &= ~TXFREEflag;
- SETMASK;
+ if (test_bit(0, (int *)&dev->tbusy))
+ {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
- if (status&TXFREEflag) /* transmit _DID_ finish */
- {
- BUGMSG(D_NORMAL,"tx timeout - missed IRQ? (status=%Xh, ticks=%d, mask=%Xh, dest=%02Xh)\n",
- status,tickssofar,lp->intmask,lp->lasttrans_dest);
- lp->stats.tx_errors++;
- }
- else
- {
- BUGMSG(D_EXTRA,"tx timed out (status=%Xh, tickssofar=%d, intmask=%Xh, dest=%02Xh)\n",
- status,tickssofar,lp->intmask,lp->lasttrans_dest);
- lp->stats.tx_errors++;
- lp->stats.tx_aborted_errors++;
+ int status=ARCSTATUS;
- ACOMMAND(NOTXcmd);
- }
+ if (tickssofar < TX_TIMEOUT)
+ {
+ BUGMSG(D_DURING,"premature kickme! (status=%Xh ticks=%d o.skb=%ph numsegs=%d segnum=%d\n",
+ status,tickssofar,lp->outgoing.skb,
+ lp->outgoing.numsegs,
+ lp->outgoing.segnum);
+ return 1;
+ }
- if (lp->outgoing.skb)
- {
- dev_kfree_skb(lp->outgoing.skb,FREE_WRITE);
- lp->stats.tx_dropped++;
- }
- lp->outgoing.skb=NULL;
+ lp->intmask &= ~TXFREEflag;
+ SETMASK;
- TBUSY=0;
- lp->txready=0;
- lp->sending=0;
+ if (status&TXFREEflag) /* transmit _DID_ finish */
+ {
+ BUGMSG(D_NORMAL,"tx timeout - missed IRQ? (status=%Xh, ticks=%d, mask=%Xh, dest=%02Xh)\n",
+ status,tickssofar,lp->intmask,lp->lasttrans_dest);
+ lp->stats.tx_errors++;
+ }
+ else
+ {
+ BUGMSG(D_EXTRA,"tx timed out (status=%Xh, tickssofar=%d, intmask=%Xh, dest=%02Xh)\n",
+ status,tickssofar,lp->intmask,lp->lasttrans_dest);
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
- return 1;
+ ACOMMAND(NOTXcmd);
}
- if (lp->txready) /* transmit already in progress! */
+ if (lp->outgoing.skb)
{
- BUGMSG(D_NORMAL,"trying to start new packet while busy! (status=%Xh)\n",
- ARCSTATUS);
- lp->intmask &= ~TXFREEflag;
- SETMASK;
- ACOMMAND(NOTXcmd); /* abort current send */
- arcnet_inthandler(dev); /* fake an interrupt */
- lp->stats.tx_errors++;
- lp->stats.tx_fifo_errors++;
- lp->txready=0; /* we definitely need this line! */
-
- return 1;
+ dev_kfree_skb(lp->outgoing.skb,FREE_WRITE);
+ lp->stats.tx_dropped++;
}
+ lp->outgoing.skb=NULL;
- /* Block a timer-based transmit from overlapping. This could better be
- done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
- if (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
- {
- BUGMSG(D_NORMAL,"transmitter called with busy bit set! (status=%Xh, inTX=%d, tickssofar=%ld)\n",
- ARCSTATUS,lp->intx,jiffies-dev->trans_start);
- lp->stats.tx_errors++;
- lp->stats.tx_fifo_errors++;
- return -EBUSY;
- }
+#ifdef CONFIG_ARCNET_ETH
+ lp->edev->tbusy=0;
+#endif
+#ifdef CONFIG_ARCNET_1051
+ lp->sdev->tbusy=0;
+#endif
+ if (!test_and_clear_bit(0,(int *)&dev->tbusy))
+ BUGMSG(D_EXTRA, "after timing out, tbusy was clear!\n");
+
+ lp->txready=0;
+ lp->sending=0;
+
+ return 1;
+ }
+
+ if (lp->txready) /* transmit already in progress! */
+ {
+ BUGMSG(D_NORMAL,"trying to start new packet while busy! (status=%Xh)\n",
+ ARCSTATUS);
+ lp->intmask &= ~TXFREEflag;
+ SETMASK;
+ ACOMMAND(NOTXcmd); /* abort current send */
+ (*lp->inthandler)(dev); /* fake an interrupt */
+ lp->stats.tx_errors++;
+ lp->stats.tx_fifo_errors++;
+ lp->txready=0; /* we definitely need this line! */
+
+ return 1;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (test_and_set_bit(0, (int *)&lp->adev->tbusy))
+ {
+ BUGMSG(D_NORMAL,"transmitter called with busy bit set! (status=%Xh, inTX=%d, tickssofar=%ld)\n",
+ ARCSTATUS,lp->intx,jiffies-dev->trans_start);
+ lp->stats.tx_errors++;
+ lp->stats.tx_fifo_errors++;
+ return -EBUSY;
+ }
+#ifdef CONFIG_ARCNET_1051
+ lp->sdev->tbusy=1;
+#endif
+#ifdef CONFIG_ARCNET_ETH
+ lp->edev->tbusy=1;
+#endif
- return 0;
+ return 0;
}
@@ -1606,257 +696,170 @@ arcnet_send_packet_bad(struct sk_buff *skb, struct device *dev)
static int
arcnetA_send_packet(struct sk_buff *skb, struct device *dev)
{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- int ioaddr=IOADDR,bad;
- struct Outgoing *out=&(lp->outgoing);
-
- lp->intx++;
-
- bad=arcnet_send_packet_bad(skb,dev);
- if (bad)
- {
- lp->intx--;
- return bad;
- }
-
- TBUSY=1;
-
- out->length = 1 < skb->len ? skb->len : 1;
- out->hdr=(struct ClientData*)skb->data;
- out->skb=skb;
-
- BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"tx");
-
- out->hdr->sequence=(lp->sequence++);
-
- /* fits in one packet? */
- if (out->length-EXTRA_CLIENTDATA<=XMTU)
- {
- BUGMSG(D_DURING,"not splitting %d-byte packet. (split_flag=%d)\n",
- out->length,out->hdr->split_flag);
- if (out->hdr->split_flag)
- BUGMSG(D_NORMAL,"short packet has split_flag set?! (split_flag=%d)\n",
- out->hdr->split_flag);
- out->numsegs=1;
- out->segnum=1;
- arcnetAS_prepare_tx(dev,
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int bad,oldmask=0;
+ struct Outgoing *out=&(lp->outgoing);
+
+ lp->intx++;
+
+ oldmask |= lp->intmask;
+ lp->intmask=0;
+ SETMASK;
+
+ bad=arcnet_send_packet_bad(skb,dev);
+ if (bad)
+ {
+ lp->intx--;
+ lp->intmask=oldmask;
+ SETMASK;
+ return bad;
+ }
+
+ /* arcnet_send_packet_pad has already set tbusy - don't bother here. */
+
+ lp->intmask = oldmask & ~TXFREEflag;
+ SETMASK;
+
+ out->length = 1 < skb->len ? skb->len : 1;
+ out->hdr=(struct ClientData*)skb->data;
+ out->skb=skb;
+
+ BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"tx");
+
+ out->hdr->sequence=(lp->sequence++);
+
+ /* fits in one packet? */
+ if (out->length-EXTRA_CLIENTDATA<=XMTU)
+ {
+ BUGMSG(D_DURING,"not splitting %d-byte packet. (split_flag=%d)\n",
+ out->length,out->hdr->split_flag);
+ if (out->hdr->split_flag)
+ BUGMSG(D_NORMAL,"short packet has split_flag set?! (split_flag=%d)\n",
+ out->hdr->split_flag);
+ out->numsegs=1;
+ out->segnum=1;
+ (*lp->prepare_tx)(dev,
((char *)out->hdr)+EXTRA_CLIENTDATA,
sizeof(struct ClientData)-EXTRA_CLIENTDATA,
((char *)skb->data)+sizeof(struct ClientData),
out->length-sizeof(struct ClientData),
- out->hdr->daddr,1);
+ out->hdr->daddr,1,0);
- /* done right away */
- dev_kfree_skb(out->skb,FREE_WRITE);
- out->skb=NULL;
+ /* done right away */
+ lp->stats.tx_bytes += out->skb->len;
+ dev_kfree_skb(out->skb,FREE_WRITE);
+ out->skb=NULL;
- if (arcnet_go_tx(dev,1))
- {
- /* inform upper layers */
- TBUSY=0;
- mark_bh(NET_BH);
- }
- }
- else /* too big for one - split it */
+ if (arcnet_go_tx(dev,1))
{
- int maxsegsize=XMTU-4;
-
- out->data=(u_char *)skb->data
- + sizeof(struct ClientData);
- out->dataleft=out->length-sizeof(struct ClientData);
- out->numsegs=(out->dataleft+maxsegsize-1)/maxsegsize;
- out->segnum=0;
-
- BUGMSG(D_TX,"packet (%d bytes) split into %d fragments:\n",
- out->length,out->numsegs);
-
- /* if a packet waiting, launch it */
- arcnet_go_tx(dev,1);
-
- if (!lp->txready)
- {
- /* prepare a packet, launch it and prepare
- * another.
- */
- arcnetA_continue_tx(dev);
- if (arcnet_go_tx(dev,1))
- {
- arcnetA_continue_tx(dev);
- arcnet_go_tx(dev,1);
- }
- }
-
- /* if segnum==numsegs, the transmission is finished;
- * free the skb right away.
- */
- if (out->segnum==out->numsegs)
- {
- /* transmit completed */
- out->segnum++;
- if (out->skb)
- dev_kfree_skb(out->skb,FREE_WRITE);
- out->skb=NULL;
- }
+ /* inform upper layers */
+ arcnet_tx_done(dev, lp);
}
+ }
+ else /* too big for one - split it */
+ {
+ int maxsegsize=XMTU-4;
- dev->trans_start=jiffies;
- lp->intx--;
+ out->data=(u_char *)skb->data
+ + sizeof(struct ClientData);
+ out->dataleft=out->length-sizeof(struct ClientData);
+ out->numsegs=(out->dataleft+maxsegsize-1)/maxsegsize;
+ out->segnum=0;
- /* make sure we didn't ignore a TX IRQ while we were in here */
- lp->intmask |= TXFREEflag;
- SETMASK;
-
- return 0;
-}
+ BUGMSG(D_TX,"packet (%d bytes) split into %d fragments:\n",
+ out->length,out->numsegs);
+ /* if a packet waiting, launch it */
+ arcnet_go_tx(dev,1);
-/* After an RFC1201 split packet has been set up, this function calls
- * arcnetAS_prepare_tx to load the next segment into the card. This function
- * does NOT automatically call arcnet_go_tx.
- */
-static void arcnetA_continue_tx(struct device *dev)
-{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- int ioaddr=IOADDR,maxsegsize=XMTU-4;
- struct Outgoing *out=&(lp->outgoing);
-
- BUGMSG(D_DURING,"continue_tx called (status=%Xh, intx=%d, intxh=%d, intmask=%Xh\n",
- ARCSTATUS,lp->intx,lp->in_txhandler,lp->intmask);
-
- if (lp->txready)
+ if (!lp->txready)
{
- BUGMSG(D_NORMAL,"continue_tx: called with packet in buffer!\n");
- return;
+ /* prepare a packet, launch it and prepare
+ * another.
+ */
+ arcnetA_continue_tx(dev);
+ if (arcnet_go_tx(dev,1))
+ {
+ arcnetA_continue_tx(dev);
+ arcnet_go_tx(dev,1);
+ }
}
- if (out->segnum>=out->numsegs)
+ /* if segnum==numsegs, the transmission is finished;
+ * free the skb right away.
+ */
+
+ if (out->segnum==out->numsegs)
{
- BUGMSG(D_NORMAL,"continue_tx: building segment %d of %d!\n",
- out->segnum+1,out->numsegs);
+ /* transmit completed */
+ out->segnum++;
+ if (out->skb)
+ {
+ lp->stats.tx_bytes += skb->len;
+ dev_kfree_skb(out->skb,FREE_WRITE);
+ }
+ out->skb=NULL;
}
+ }
- if (!out->segnum) /* first packet */
- out->hdr->split_flag=((out->numsegs-2)<<1)+1;
- else
- out->hdr->split_flag=out->segnum<<1;
+ dev->trans_start=jiffies;
+ lp->intx--;
- out->seglen=maxsegsize;
- if (out->seglen>out->dataleft) out->seglen=out->dataleft;
+ /* make sure we didn't ignore a TX IRQ while we were in here */
+ lp->intmask |= TXFREEflag;
+ SETMASK;
- BUGMSG(D_TX,"building packet #%d (%d bytes) of %d (%d total), splitflag=%d\n",
- out->segnum+1,out->seglen,out->numsegs,
- out->length,out->hdr->split_flag);
-
- arcnetAS_prepare_tx(dev,((char *)out->hdr)+EXTRA_CLIENTDATA,
- sizeof(struct ClientData)-EXTRA_CLIENTDATA,
- out->data,out->seglen,out->hdr->daddr,1);
-
- out->dataleft-=out->seglen;
- out->data+=out->seglen;
- out->segnum++;
+ return 0;
}
-/* Given an skb, copy a packet into the ARCnet buffers for later transmission
- * by arcnet_go_tx.
+/* After an RFC1201 split packet has been set up, this function calls
+ * arcnetAS_prepare_tx to load the next segment into the card. This function
+ * does NOT automatically call arcnet_go_tx.
*/
-static void
-arcnetAS_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
- char *data,int length,int daddr,int exceptA)
+void arcnetA_continue_tx(struct device *dev)
{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- struct ClientData *arcsoft;
- union ArcPacket *arcpacket =
- (union ArcPacket *)phys_to_virt(dev->mem_start+512*(lp->txbuf^1));
- int offset;
-
-#ifdef SLOW_XMIT_COPY
- char *iptr,*iend,*optr;
-#endif
-
- lp->txbuf=lp->txbuf^1; /* XOR with 1 to alternate between 2 and 3 */
-
- length+=hdrlen;
-
- BUGMSG(D_TX,"arcnetAS_prep_tx: hdr:%ph, length:%d, data:%ph\n",
- hdr,length,data);
-
-#ifndef SLOW_XMIT_COPY
- /* clean out the page to make debugging make more sense :) */
- BUGLVL(D_DURING)
- memset_io(dev->mem_start+lp->txbuf*512,0x42,512);
-#endif
-
- arcpacket->hardheader.destination=daddr;
-
- /* load packet into shared memory */
- if (length<=MTU) /* Normal (256-byte) Packet */
- {
- arcpacket->hardheader.offset1=offset=256-length;
- arcsoft=(struct ClientData *)
- (&arcpacket->raw[offset]);
- }
- else if (length>=MinTU) /* Extended (512-byte) Packet */
- {
- arcpacket->hardheader.offset1=0;
- arcpacket->hardheader.offset2=offset=512-length;
-
- arcsoft=(struct ClientData *)
- (&arcpacket->raw[offset]);
- }
- else if (exceptA) /* RFC1201 Exception Packet */
- {
- arcpacket->hardheader.offset1=0;
- arcpacket->hardheader.offset2=offset=512-length-4;
- arcsoft=(struct ClientData *)
- (&arcpacket->raw[offset+4]);
-
- /* exception-specific stuff - these four bytes
- * make the packet long enough to fit in a 512-byte
- * frame.
- */
- arcpacket->raw[offset+0]=hdr[0];
- arcpacket->raw[offset+1]=0xFF; /* FF flag */
- arcpacket->raw[offset+2]=0xFF; /* FF padding */
- arcpacket->raw[offset+3]=0xFF; /* FF padding */
- }
- else /* "other" Exception packet */
- {
- /* RFC1051 - set 4 trailing bytes to 0 */
- memset(&arcpacket->raw[508],0,4);
-
- /* now round up to MinTU */
- arcpacket->hardheader.offset1=0;
- arcpacket->hardheader.offset2=offset=512-MinTU;
- arcsoft=(struct ClientData *)(&arcpacket->raw[offset]);
- }
-
-
- /* copy the packet into ARCnet shmem
- * - the first bytes of ClientData header are skipped
- */
- memcpy((u_char*)arcsoft,
- (u_char*)hdr,hdrlen);
-#ifdef SLOW_XMIT_COPY
- for (iptr=data,iend=iptr+length-hdrlen,optr=(char *)arcsoft+hdrlen;
- iptr<iend; iptr++,optr++)
- {
- *optr=*iptr;
- /*udelay(5);*/
- }
-#else
- memcpy((u_char*)arcsoft+hdrlen,
- data,length-hdrlen);
-#endif
-
- BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
- daddr,length);
-
- BUGLVL(D_TX) arcnet_dump_packet(dev,arcpacket->raw,length>MTU,"tx");
- lp->lastload_dest=daddr;
- lp->txready=lp->txbuf; /* packet is ready for sending */
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int maxsegsize=XMTU-4;
+ struct Outgoing *out=&(lp->outgoing);
+
+ BUGMSG(D_DURING,"continue_tx called (status=%Xh, intx=%d, intxh=%d, intmask=%Xh\n",
+ ARCSTATUS,lp->intx,lp->in_txhandler,lp->intmask);
+
+ if (lp->txready)
+ {
+ BUGMSG(D_NORMAL,"continue_tx: called with packet in buffer!\n");
+ return;
+ }
+
+ if (out->segnum>=out->numsegs)
+ {
+ BUGMSG(D_NORMAL,"continue_tx: building segment %d of %d!\n",
+ out->segnum+1,out->numsegs);
+ }
+
+ if (!out->segnum) /* first packet */
+ out->hdr->split_flag=((out->numsegs-2)<<1)+1;
+ else
+ out->hdr->split_flag=out->segnum<<1;
+
+ out->seglen=maxsegsize;
+ if (out->seglen>out->dataleft) out->seglen=out->dataleft;
+
+ BUGMSG(D_TX,"building packet #%d (%d bytes) of %d (%d total), splitflag=%d\n",
+ out->segnum+1,out->seglen,out->numsegs,
+ out->length,out->hdr->split_flag);
+
+ (*lp->prepare_tx)(dev,((char *)out->hdr)+EXTRA_CLIENTDATA,
+ sizeof(struct ClientData)-EXTRA_CLIENTDATA,
+ out->data,out->seglen,out->hdr->daddr,1,0);
+
+ out->dataleft-=out->seglen;
+ out->data+=out->seglen;
+ out->segnum++;
}
+
/* Actually start transmitting a packet that was placed in the card's
* buffer by arcnetAS_prepare_tx. Returns 1 if a Tx is really started.
*
@@ -1867,11 +870,9 @@ arcnetAS_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
* to the card; TXFREEflag is always OR'ed into the memory variable either
* way.
*/
-static int
-arcnet_go_tx(struct device *dev,int enable_irq)
+int arcnet_go_tx(struct device *dev,int enable_irq)
{
struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
- int ioaddr=IOADDR;
BUGMSG(D_DURING,"go_tx: status=%Xh, intmask=%Xh, txready=%d, sending=%d\n",
ARCSTATUS,lp->intmask,lp->txready,lp->sending);
@@ -1912,273 +913,80 @@ arcnet_go_tx(struct device *dev,int enable_irq)
/* The typical workload of the driver: Handle the network interface
- * interrupts. This doesn't do much right now except call arcnet_inthandler,
- * which takes different parameters but may be called from other places
- * as well.
+ * interrupts. Establish which device needs attention, and call the correct
+ * chipset interrupt handler.
*/
-static void
+void
arcnet_interrupt(int irq,void *dev_id,struct pt_regs *regs)
{
struct device *dev = (struct device *)(irq2dev_map[irq]);
- int ioaddr;
+ struct arcnet_local *lp;
if (dev==NULL)
- {
- BUGLVL(D_DURING)
- printk(KERN_DEBUG "arcnet: irq %d for unknown device.\n", irq);
- return;
- }
+ {
+ BUGLVL(D_DURING)
+ printk(KERN_DEBUG "arcnet: irq %d for unknown device.\n", irq);
+ return;
+ }
BUGMSG(D_DURING,"in arcnet_interrupt\n");
+ lp=(struct arcnet_local *)dev->priv;
+
/* RESET flag was enabled - if !dev->start, we must clear it right
* away (but nothing else) since inthandler() is never called.
*/
- ioaddr=IOADDR; /* can't do this before checking dev!=NULL */
+
if (!dev->start)
- {
- if (ARCSTATUS & RESETflag)
- ACOMMAND(CFLAGScmd|RESETclear);
- return;
- }
+ {
+ if (ARCSTATUS & RESETflag)
+ ACOMMAND(CFLAGScmd|RESETclear);
+ return;
+ }
+
+
+ if (test_and_set_bit(0, (int *)&dev->interrupt))
+ {
+ BUGMSG(D_NORMAL,"DRIVER PROBLEM! Nested arcnet interrupts!\n");
+ return; /* don't even try. */
+ }
+#ifdef CONFIG_ARCNET_1051
+ lp->sdev->interrupt=1;
+#endif
+#ifdef CONFIG_ARCNET_ETH
+ lp->edev->interrupt=1;
+#endif
/* Call the "real" interrupt handler. */
- arcnet_inthandler(dev);
-}
-
-
-/* The actual interrupt handler routine - handle various IRQ's generated
- * by the card.
- */
-static void
-arcnet_inthandler(struct device *dev)
-{
- struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
- int ioaddr=IOADDR, status, boguscount = 3, didsomething;
-
- if (IF_INTERRUPT)
- {
- BUGMSG(D_NORMAL,"DRIVER PROBLEM! Nested arcnet interrupts!\n");
- return; /* don't even try. */
- }
-
- AINTMASK(0);
- INTERRUPT = 1;
-
- BUGMSG(D_DURING,"in arcnet_inthandler (status=%Xh, intmask=%Xh)\n",
- ARCSTATUS,lp->intmask);
-
- do
- {
- status = ARCSTATUS;
- didsomething=0;
-
-
- /* RESET flag was enabled - card is resetting and if RX
- * is disabled, it's NOT because we just got a packet.
- */
- if (status & RESETflag)
- {
- BUGMSG(D_NORMAL,"spurious reset (status=%Xh)\n",
- status);
- arcnet_reset(dev,0);
-
- /* all other flag values are just garbage */
- break;
- }
-
+ (*lp->inthandler)(dev);
- /* RX is inhibited - we must have received something. */
- if (status & lp->intmask & NORXflag)
- {
- int recbuf=lp->recbuf=!lp->recbuf;
-
- BUGMSG(D_DURING,"receive irq (status=%Xh)\n",
- status);
-
- /* enable receive of our next packet */
- EnableReceiver();
-
- /* Got a packet. */
- arcnet_rx(dev,!recbuf);
- didsomething++;
- }
-
- /* it can only be an xmit-done irq if we're xmitting :) */
- /*if (status&TXFREEflag && !lp->in_txhandler && lp->sending)*/
- if (status & lp->intmask & TXFREEflag)
- {
- struct Outgoing *out=&(lp->outgoing);
- int was_sending=lp->sending;
-
- lp->intmask &= ~TXFREEflag;
-
- lp->in_txhandler++;
- if (was_sending) lp->sending--;
-
- BUGMSG(D_DURING,"TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n",
- status,out->numsegs,out->segnum,out->skb);
-
- if (was_sending && !(status&TXACKflag))
- {
- if (lp->lasttrans_dest != 0)
- {
- BUGMSG(D_EXTRA,"transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n",
- status,lp->lasttrans_dest);
- lp->stats.tx_errors++;
- lp->stats.tx_carrier_errors++;
- }
- else
- {
- BUGMSG(D_DURING,"broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n",
- status,
- lp->lasttrans_dest);
- }
- }
-
- /* send packet if there is one */
- arcnet_go_tx(dev,0);
- didsomething++;
-
- if (lp->intx)
- {
- BUGMSG(D_DURING,"TXDONE while intx! (status=%Xh, intx=%d)\n",
- ARCSTATUS,lp->intx);
- lp->in_txhandler--;
- continue;
- }
-
- if (!lp->outgoing.skb)
- {
- BUGMSG(D_DURING,"TX IRQ done: no split to continue.\n");
-
- /* inform upper layers */
- if (!lp->txready && IF_TBUSY)
- {
- TBUSY=0;
- mark_bh(NET_BH);
- }
- lp->in_txhandler--;
- continue;
- }
-
- /* if more than one segment, and not all segments
- * are done, then continue xmit.
- */
- if (out->segnum<out->numsegs)
- arcnetA_continue_tx(dev);
- arcnet_go_tx(dev,0);
-
- /* if segnum==numsegs, the transmission is finished;
- * free the skb.
- */
- if (out->segnum>=out->numsegs)
- {
- /* transmit completed */
- out->segnum++;
- if (out->skb)
- dev_kfree_skb(out->skb,FREE_WRITE);
- out->skb=NULL;
-
- /* inform upper layers */
- if (!lp->txready && IF_TBUSY)
- {
- TBUSY=0;
- mark_bh(NET_BH);
- }
- }
- didsomething++;
-
- lp->in_txhandler--;
- }
- else if (lp->txready && !lp->sending && !lp->intx)
- {
- BUGMSG(D_NORMAL,"recovery from silent TX (status=%Xh)\n",
- status);
- arcnet_go_tx(dev,0);
- didsomething++;
- }
-
-#ifdef DETECT_RECONFIGS
- if (status & (lp->intmask) & RECONflag)
- {
- ACOMMAND(CFLAGScmd|CONFIGclear);
- lp->stats.tx_carrier_errors++;
-
- #ifdef SHOW_RECONFIGS
- BUGMSG(D_NORMAL,"Network reconfiguration detected (status=%Xh)\n",
- status);
- #endif /* SHOW_RECONFIGS */
-
- #ifdef RECON_THRESHOLD
- /* is the RECON info empty or old? */
- if (!lp->first_recon || !lp->last_recon ||
- jiffies-lp->last_recon > HZ*10)
- {
- if (lp->network_down)
- BUGMSG(D_NORMAL,"reconfiguration detected: cabling restored?\n");
- lp->first_recon=lp->last_recon=jiffies;
- lp->num_recons=lp->network_down=0;
-
- BUGMSG(D_DURING,"recon: clearing counters.\n");
- }
- else /* add to current RECON counter */
- {
- lp->last_recon=jiffies;
- lp->num_recons++;
-
- BUGMSG(D_DURING,"recon: counter=%d, time=%lds, net=%d\n",
- lp->num_recons,
- (lp->last_recon-lp->first_recon)/HZ,
- lp->network_down);
-
- /* if network is marked up;
- * and first_recon and last_recon are 60+ sec
- * apart;
- * and the average no. of recons counted is
- * > RECON_THRESHOLD/min;
- * then print a warning message.
- */
- if (!lp->network_down
- && (lp->last_recon-lp->first_recon)<=HZ*60
- && lp->num_recons >= RECON_THRESHOLD)
- {
- lp->network_down=1;
- BUGMSG(D_NORMAL,"many reconfigurations detected: cabling problem?\n");
- }
- else if (!lp->network_down
- && lp->last_recon-lp->first_recon > HZ*60)
- {
- /* reset counters if we've gone for
- * over a minute.
- */
- lp->first_recon=lp->last_recon;
- lp->num_recons=1;
- }
- }
- #endif
- }
- #ifdef RECON_THRESHOLD
- else if (lp->network_down && jiffies-lp->last_recon > HZ*10)
- {
- if (lp->network_down)
- BUGMSG(D_NORMAL,"cabling restored?\n");
- lp->first_recon=lp->last_recon=0;
- lp->num_recons=lp->network_down=0;
-
- BUGMSG(D_DURING,"not recon: clearing counters anyway.\n");
- }
- #endif
-#endif /* DETECT_RECONFIGS */
- } while (--boguscount && didsomething);
+#ifdef CONFIG_ARCNET_ETH
+ lp->edev->interrupt=0;
+#endif
+#ifdef CONFIG_ARCNET_1051
+ lp->sdev->interrupt=0;
+#endif
+ if (!test_and_clear_bit(0, (int *)&dev->interrupt))
+ BUGMSG(D_NORMAL, "Someone cleared our dev->interrupt flag!\n");
- BUGMSG(D_DURING,"net_interrupt complete (status=%Xh, count=%d)\n",
- ARCSTATUS,boguscount);
- BUGMSG(D_DURING,"\n");
+}
- SETMASK; /* put back interrupt mask */
+void arcnet_tx_done(struct device *dev, struct arcnet_local *lp)
+{
+ if (dev->tbusy)
+ {
+#ifdef CONFIG_ARCNET_ETH
+ lp->edev->tbusy=0;
+#endif
+#ifdef CONFIG_ARCNET_1051
+ lp->sdev->tbusy=0;
+#endif
+ if (!test_and_clear_bit(0, (int *)&dev->tbusy))
+ BUGMSG(D_NORMAL, "In arcnet_tx_done: Someone cleared our dev->tbusy"
+ " flag!\n");
- INTERRUPT=0;
+ mark_bh(NET_BH);
+ }
}
@@ -2188,377 +996,336 @@ arcnet_inthandler(struct device *dev)
* *
****************************************************************************/
-
-/* A packet has arrived; grab it from the buffers and possibly unsplit it.
+/*
* This is a generic packet receiver that calls arcnet??_rx depending on the
* protocol ID found.
*/
-static void
-arcnet_rx(struct device *dev,int recbuf)
-{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- int ioaddr=IOADDR;
- union ArcPacket *arcpacket=
- (union ArcPacket *)phys_to_virt(dev->mem_start+recbuf*512);
- u_char *arcsoft;
- short length,offset;
- u_char daddr,saddr;
-
- lp->stats.rx_packets++;
-
- saddr=arcpacket->hardheader.source;
- daddr=arcpacket->hardheader.destination;
-
- /* if source is 0, it's a "used" packet! */
- if (saddr==0)
- {
- BUGMSG(D_NORMAL,"discarding old packet. (status=%Xh)\n",
- ARCSTATUS);
- lp->stats.rx_errors++;
- return;
- }
- arcpacket->hardheader.source=0;
- if (arcpacket->hardheader.offset1) /* Normal Packet */
- {
- offset=arcpacket->hardheader.offset1;
- arcsoft=&arcpacket->raw[offset];
- length=256-offset;
- }
- else /* ExtendedPacket or ExceptionPacket */
- {
- offset=arcpacket->hardheader.offset2;
- arcsoft=&arcpacket->raw[offset];
-
- length=512-offset;
- }
-
- BUGMSG(D_DURING,"received packet from %02Xh to %02Xh (%d bytes)\n",
- saddr,daddr,length);
-
- /* call the right receiver for the protocol */
- switch (arcsoft[0])
- {
- case ARC_P_IP:
- case ARC_P_ARP:
- case ARC_P_RARP:
- case ARC_P_IPX:
- case ARC_P_NOVELL_EC:
- arcnetA_rx(lp->adev,arcsoft,length,saddr,daddr);
- break;
+void arcnet_rx(struct arcnet_local *lp, u_char *arcsoft, short length, int saddr, int daddr)
+{
+ struct device *dev=lp->adev;
+
+ BUGMSG(D_DURING,"received packet from %02Xh to %02Xh (%d bytes)\n",
+ saddr,daddr,length);
+
+ /* call the right receiver for the protocol */
+ switch (arcsoft[0])
+ {
+ case ARC_P_IP:
+ case ARC_P_ARP:
+ case ARC_P_RARP:
+ case ARC_P_IPX:
+ case ARC_P_NOVELL_EC:
+ arcnetA_rx(lp->adev,arcsoft,length,saddr,daddr);
+ break;
#ifdef CONFIG_ARCNET_ETH
- case ARC_P_ETHER:
- arcnetE_rx(lp->edev,arcsoft,length,saddr,daddr);
- break;
+ case ARC_P_ETHER:
+ arcnetE_rx(lp->edev,arcsoft,length,saddr,daddr);
+ break;
#endif
#ifdef CONFIG_ARCNET_1051
- case ARC_P_IP_RFC1051:
- case ARC_P_ARP_RFC1051:
- arcnetS_rx(lp->sdev,arcsoft,length,saddr,daddr);
- break;
-#endif
- case ARC_P_DATAPOINT_BOOT:
- case ARC_P_DATAPOINT_MOUNT:
- break;
- case ARC_P_POWERLAN_BEACON:
- case ARC_P_POWERLAN_BEACON2:
- break;
- case ARC_P_LANSOFT: /* don't understand. fall through. */
- default:
- BUGMSG(D_EXTRA,"received unknown protocol %d (%Xh) from station %d.\n",
- arcsoft[0],arcsoft[0],saddr);
- lp->stats.rx_errors++;
- lp->stats.rx_crc_errors++;
- break;
- }
-
- BUGLVL(D_RX) arcnet_dump_packet(dev,arcpacket->raw,length>240,"rx");
-
-
-#ifndef SLOW_XMIT_COPY
- /* clean out the page to make debugging make more sense :) */
- BUGLVL(D_DURING)
- memset((void *)arcpacket->raw,0x42,512);
+ case ARC_P_IP_RFC1051:
+ case ARC_P_ARP_RFC1051:
+ arcnetS_rx(lp->sdev,arcsoft,length,saddr,daddr);
+ break;
#endif
+ case ARC_P_DATAPOINT_BOOT:
+ case ARC_P_DATAPOINT_MOUNT:
+ break;
+ case ARC_P_POWERLAN_BEACON:
+ case ARC_P_POWERLAN_BEACON2:
+ break;
+ case ARC_P_LANSOFT: /* don't understand. fall through. */
+ default:
+ BUGMSG(D_EXTRA,"received unknown protocol %d (%Xh) from station %d.\n",
+ arcsoft[0],arcsoft[0],saddr);
+ lp->stats.rx_errors++;
+ lp->stats.rx_crc_errors++;
+ break;
+ }
+
+ /* If any worth-while packets have been received, a mark_bh(NET_BH)
+ * has been done by netif_rx and Linux will handle them after we
+ * return.
+ */
- /* If any worth-while packets have been received, a mark_bh(NET_BH)
- * has been done by netif_rx and Linux will handle them after we
- * return.
- */
}
+
/* Packet receiver for "standard" RFC1201-style packets
*/
static void
arcnetA_rx(struct device *dev,u_char *buf,
- int length,u_char saddr, u_char daddr)
+ int length, u_char saddr, u_char daddr)
{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- struct sk_buff *skb;
- struct ClientData *arcsoft,*soft;
-
- BUGMSG(D_DURING,"it's an RFC1201 packet (length=%d)\n",
- length);
-
- /* compensate for EXTRA_CLIENTDATA (which isn't actually in the
- * packet)
- */
- arcsoft=(struct ClientData *)(buf-EXTRA_CLIENTDATA);
- length+=EXTRA_CLIENTDATA;
-
- if (arcsoft->split_flag==0xFF) /* Exception Packet */
- {
- BUGMSG(D_DURING,"compensating for exception packet\n");
-
- /* skip over 4-byte junkola */
- arcsoft=(struct ClientData *)
- ((u_char *)arcsoft + 4);
- length-=4;
- }
-
- if (!arcsoft->split_flag) /* not split */
- {
- struct Incoming *in=&lp->incoming[saddr];
-
- BUGMSG(D_RX,"incoming is not split (splitflag=%d)\n",
- arcsoft->split_flag);
-
- if (in->skb) /* already assembling one! */
- {
- BUGMSG(D_EXTRA,"aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n",
- in->sequence,arcsoft->split_flag,
- arcsoft->sequence);
- kfree_skb(in->skb,FREE_WRITE);
- lp->stats.rx_errors++;
- lp->stats.rx_missed_errors++;
- in->skb=NULL;
- }
-
- in->sequence=arcsoft->sequence;
-
- skb = alloc_skb(length, GFP_ATOMIC);
- if (skb == NULL) {
- BUGMSG(D_NORMAL,"Memory squeeze, dropping packet.\n");
- lp->stats.rx_dropped++;
- return;
- }
- soft=(struct ClientData *)skb->data;
-
- skb->len = length;
- skb->dev = dev;
-
- memcpy((u_char *)soft+EXTRA_CLIENTDATA,
- (u_char *)arcsoft+EXTRA_CLIENTDATA,
- length-EXTRA_CLIENTDATA);
- soft->daddr=daddr;
- soft->saddr=saddr;
-
- /* ARP packets have problems when sent from DOS.
- * source address is always 0 on some systems! So we take
- * the hardware source addr (which is impossible to fumble)
- * and insert it ourselves.
- */
- if (soft->protocol_id == ARC_P_ARP)
- {
- struct arphdr *arp=(struct arphdr *)
- ((char *)soft+sizeof(struct ClientData));
-
- /* make sure addresses are the right length */
- if (arp->ar_hln==1 && arp->ar_pln==4)
- {
- char *cptr=(char *)(arp)+sizeof(struct arphdr);
-
- if (!*cptr) /* is saddr = 00? */
- {
- BUGMSG(D_EXTRA,"ARP source address was 00h, set to %02Xh.\n",
- saddr);
- lp->stats.rx_crc_errors++;
- *cptr=saddr;
- }
- else
- {
- BUGMSG(D_DURING,"ARP source address (%Xh) is fine.\n",
- *cptr);
- }
- }
- else
- {
- BUGMSG(D_NORMAL,"funny-shaped ARP packet. (%Xh, %Xh)\n",
- arp->ar_hln,arp->ar_pln);
- lp->stats.rx_errors++;
- lp->stats.rx_crc_errors++;
- }
- }
-
- BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
-
- skb->protocol=arcnetA_type_trans(skb,dev);
-
- netif_rx(skb);
- }
- else /* split packet */
- {
- /* NOTE: MSDOS ARP packet correction should only need to
- * apply to unsplit packets, since ARP packets are so short.
- *
- * My interpretation of the RFC1201 (ARCnet) document is that
- * if a packet is received out of order, the entire assembly
- * process should be aborted.
- *
- * The RFC also mentions "it is possible for successfully
- * received packets to be retransmitted." As of 0.40 all
- * previously received packets are allowed, not just the
- * most recent one.
- *
- * We allow multiple assembly processes, one for each
- * ARCnet card possible on the network. Seems rather like
- * a waste of memory. Necessary?
- */
-
- struct Incoming *in=&lp->incoming[saddr];
-
- BUGMSG(D_RX,"packet is split (splitflag=%d, seq=%d)\n",
- arcsoft->split_flag,in->sequence);
-
- if (in->skb && in->sequence!=arcsoft->sequence)
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ struct sk_buff *skb;
+ struct ClientData *arcsoft,*soft;
+
+ BUGMSG(D_DURING,"it's an RFC1201 packet (length=%d)\n",
+ length);
+
+ /* compensate for EXTRA_CLIENTDATA (which isn't actually in the
+ * packet)
+ */
+ arcsoft=(struct ClientData *)(buf-EXTRA_CLIENTDATA);
+ length+=EXTRA_CLIENTDATA;
+
+ if (arcsoft->split_flag==0xFF) /* Exception Packet */
+ {
+ BUGMSG(D_DURING,"compensating for exception packet\n");
+
+ /* skip over 4-byte junkola */
+ arcsoft=(struct ClientData *)
+ ((u_char *)arcsoft + 4);
+ length-=4;
+ }
+
+ if (!arcsoft->split_flag) /* not split */
+ {
+ struct Incoming *in=&lp->incoming[saddr];
+
+ BUGMSG(D_RX,"incoming is not split (splitflag=%d)\n",
+ arcsoft->split_flag);
+
+ if (in->skb) /* already assembling one! */
+ {
+ BUGMSG(D_EXTRA,"aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n",
+ in->sequence,arcsoft->split_flag,
+ arcsoft->sequence);
+ lp->aborted_seq=arcsoft->sequence;
+ kfree_skb(in->skb,FREE_WRITE);
+ lp->stats.rx_errors++;
+ lp->stats.rx_missed_errors++;
+ in->skb=NULL;
+ }
+
+ in->sequence=arcsoft->sequence;
+
+ skb = alloc_skb(length, GFP_ATOMIC);
+ if (skb == NULL) {
+ BUGMSG(D_NORMAL,"Memory squeeze, dropping packet.\n");
+ lp->stats.rx_dropped++;
+ return;
+ }
+ soft=(struct ClientData *)skb->data;
+
+ skb_put(skb,length);
+ skb->dev = dev;
+
+ memcpy((u_char *)soft+EXTRA_CLIENTDATA,
+ (u_char *)arcsoft+EXTRA_CLIENTDATA,
+ length-EXTRA_CLIENTDATA);
+ soft->daddr=daddr;
+ soft->saddr=saddr;
+
+ /* ARP packets have problems when sent from DOS.
+ * source address is always 0 on some systems! So we take
+ * the hardware source addr (which is impossible to fumble)
+ * and insert it ourselves.
+ */
+ if (soft->protocol_id == ARC_P_ARP)
+ {
+ struct arphdr *arp=(struct arphdr *)
+ ((char *)soft+sizeof(struct ClientData));
+
+ /* make sure addresses are the right length */
+ if (arp->ar_hln==1 && arp->ar_pln==4)
+ {
+ char *cptr=(char *)(arp)+sizeof(struct arphdr);
+
+ if (!*cptr) /* is saddr = 00? */
{
- BUGMSG(D_EXTRA,"wrong seq number (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n",
- saddr,in->sequence,arcsoft->sequence,
- arcsoft->split_flag);
- kfree_skb(in->skb,FREE_WRITE);
- in->skb=NULL;
- lp->stats.rx_errors++;
- lp->stats.rx_missed_errors++;
- in->lastpacket=in->numpackets=0;
- }
-
- if (arcsoft->split_flag & 1) /* first packet in split */
- {
- BUGMSG(D_RX,"brand new splitpacket (splitflag=%d)\n",
- arcsoft->split_flag);
- if (in->skb) /* already assembling one! */
- {
- BUGMSG(D_EXTRA,"aborting previous (seq=%d) assembly (splitflag=%d, seq=%d)\n",
- in->sequence,arcsoft->split_flag,
- arcsoft->sequence);
- lp->stats.rx_errors++;
- lp->stats.rx_missed_errors++;
- kfree_skb(in->skb,FREE_WRITE);
- }
-
- in->sequence=arcsoft->sequence;
- in->numpackets=((unsigned)arcsoft->split_flag>>1)+2;
- in->lastpacket=1;
-
- if (in->numpackets>16)
- {
- BUGMSG(D_EXTRA,"incoming packet more than 16 segments; dropping. (splitflag=%d)\n",
- arcsoft->split_flag);
- lp->stats.rx_errors++;
- lp->stats.rx_length_errors++;
- return;
- }
-
- in->skb=skb=alloc_skb(508*in->numpackets
- + sizeof(struct ClientData),
- GFP_ATOMIC);
- if (skb == NULL) {
- BUGMSG(D_NORMAL,"(split) memory squeeze, dropping packet.\n");
- lp->stats.rx_dropped++;
- return;
- }
-
- soft=(struct ClientData *)skb->data;
-
- skb->len=sizeof(struct ClientData);
- skb->dev=dev;
-
- memcpy((u_char *)soft+EXTRA_CLIENTDATA,
- (u_char *)arcsoft+EXTRA_CLIENTDATA,
- sizeof(struct ClientData)-EXTRA_CLIENTDATA);
- soft->split_flag=0; /* final packet won't be split */
- }
- else /* not first packet */
- {
- int packetnum=((unsigned)arcsoft->split_flag>>1) + 1;
-
- /* if we're not assembling, there's no point
- * trying to continue.
- */
- if (!in->skb)
- {
- BUGMSG(D_EXTRA,"can't continue split without starting first! (splitflag=%d, seq=%d)\n",
- arcsoft->split_flag,arcsoft->sequence);
- lp->stats.rx_errors++;
- lp->stats.rx_missed_errors++;
- return;
- }
-
- in->lastpacket++;
- if (packetnum!=in->lastpacket) /* not the right flag! */
- {
- /* harmless duplicate? ignore. */
- if (packetnum<=in->lastpacket-1)
- {
- BUGMSG(D_EXTRA,"duplicate splitpacket ignored! (splitflag=%d)\n",
- arcsoft->split_flag);
- lp->stats.rx_errors++;
- lp->stats.rx_frame_errors++;
- return;
- }
-
- /* "bad" duplicate, kill reassembly */
- BUGMSG(D_EXTRA,"out-of-order splitpacket, reassembly (seq=%d) aborted (splitflag=%d, seq=%d)\n",
- in->sequence,arcsoft->split_flag,
- arcsoft->sequence);
- kfree_skb(in->skb,FREE_WRITE);
- in->skb=NULL;
- lp->stats.rx_errors++;
- lp->stats.rx_missed_errors++;
- in->lastpacket=in->numpackets=0;
- return;
- }
-
- soft=(struct ClientData *)in->skb->data;
- }
-
- skb=in->skb;
-
- memcpy(skb->data+skb->len,
- (u_char *)arcsoft+sizeof(struct ClientData),
- length-sizeof(struct ClientData));
-
- skb->len+=length-sizeof(struct ClientData);
-
- soft->daddr=daddr;
- soft->saddr=saddr;
-
- /* are we done? */
- if (in->lastpacket == in->numpackets)
- {
- if (!skb || !in->skb)
- {
- BUGMSG(D_NORMAL,"?!? done reassembling packet, no skb? (skb=%ph, in->skb=%ph)\n",
- skb,in->skb);
- }
- else
- {
- in->skb=NULL;
- in->lastpacket=in->numpackets=0;
-
- BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
-
- skb->protocol=arcnetA_type_trans(skb,dev);
-
- netif_rx(skb);
- }
- }
- }
-}
-
+ BUGMSG(D_EXTRA,"ARP source address was 00h, set to %02Xh.\n",
+ saddr);
+ lp->stats.rx_crc_errors++;
+ *cptr=saddr;
+ }
+ else
+ {
+ BUGMSG(D_DURING,"ARP source address (%Xh) is fine.\n",
+ *cptr);
+ }
+ }
+ else
+ {
+ BUGMSG(D_NORMAL,"funny-shaped ARP packet. (%Xh, %Xh)\n",
+ arp->ar_hln,arp->ar_pln);
+ lp->stats.rx_errors++;
+ lp->stats.rx_crc_errors++;
+ }
+ }
+
+ BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
+
+ lp->stats.rx_bytes += skb->len;
+ skb->protocol=arcnetA_type_trans(skb,dev);
+ netif_rx(skb);
+ }
+ else /* split packet */
+ {
+ /* NOTE: MSDOS ARP packet correction should only need to
+ * apply to unsplit packets, since ARP packets are so short.
+ *
+ * My interpretation of the RFC1201 (ARCnet) document is that
+ * if a packet is received out of order, the entire assembly
+ * process should be aborted.
+ *
+ * The RFC also mentions "it is possible for successfully
+ * received packets to be retransmitted." As of 0.40 all
+ * previously received packets are allowed, not just the
+ * most recent one.
+ *
+ * We allow multiple assembly processes, one for each
+ * ARCnet card possible on the network. Seems rather like
+ * a waste of memory. Necessary?
+ */
+
+ struct Incoming *in=&lp->incoming[saddr];
+
+ BUGMSG(D_RX,"packet is split (splitflag=%d, seq=%d)\n",
+ arcsoft->split_flag,in->sequence);
+
+ if (in->skb && in->sequence!=arcsoft->sequence)
+ {
+ BUGMSG(D_EXTRA,"wrong seq number (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n",
+ saddr,in->sequence,arcsoft->sequence,
+ arcsoft->split_flag);
+ kfree_skb(in->skb,FREE_WRITE);
+ in->skb=NULL;
+ lp->stats.rx_errors++;
+ lp->stats.rx_missed_errors++;
+ in->lastpacket=in->numpackets=0;
+ }
+
+ if (arcsoft->split_flag & 1) /* first packet in split */
+ {
+ BUGMSG(D_RX,"brand new splitpacket (splitflag=%d)\n",
+ arcsoft->split_flag);
+ if (in->skb) /* already assembling one! */
+ {
+ BUGMSG(D_EXTRA,"aborting previous (seq=%d) assembly (splitflag=%d, seq=%d)\n",
+ in->sequence,arcsoft->split_flag,
+ arcsoft->sequence);
+ lp->stats.rx_errors++;
+ lp->stats.rx_missed_errors++;
+ kfree_skb(in->skb,FREE_WRITE);
+ }
+
+ in->sequence=arcsoft->sequence;
+ in->numpackets=((unsigned)arcsoft->split_flag>>1)+2;
+ in->lastpacket=1;
+
+ if (in->numpackets>16)
+ {
+ BUGMSG(D_EXTRA,"incoming packet more than 16 segments; dropping. (splitflag=%d)\n",
+ arcsoft->split_flag);
+ lp->stats.rx_errors++;
+ lp->stats.rx_length_errors++;
+ return;
+ }
+
+ in->skb=skb=alloc_skb(508*in->numpackets
+ + sizeof(struct ClientData),
+ GFP_ATOMIC);
+ if (skb == NULL) {
+ BUGMSG(D_NORMAL,"(split) memory squeeze, dropping packet.\n");
+ lp->stats.rx_dropped++;
+ return;
+ }
+
+ soft=(struct ClientData *)skb->data;
+
+ skb_put(skb,sizeof(struct ClientData));
+ skb->dev=dev;
+
+ memcpy((u_char *)soft+EXTRA_CLIENTDATA,
+ (u_char *)arcsoft+EXTRA_CLIENTDATA,
+ sizeof(struct ClientData)-EXTRA_CLIENTDATA);
+ soft->split_flag=0; /* final packet won't be split */
+ }
+ else /* not first packet */
+ {
+ int packetnum=((unsigned)arcsoft->split_flag>>1) + 1;
+
+ /* if we're not assembling, there's no point
+ * trying to continue.
+ */
+ if (!in->skb)
+ {
+ if (lp->aborted_seq != arcsoft->sequence)
+ {
+ BUGMSG(D_EXTRA,"can't continue split without starting first! (splitflag=%d, seq=%d, aborted=%d)\n",
+ arcsoft->split_flag,arcsoft->sequence, lp->aborted_seq);
+ lp->stats.rx_errors++;
+ lp->stats.rx_missed_errors++;
+ }
+ return;
+ }
+
+ in->lastpacket++;
+ if (packetnum!=in->lastpacket) /* not the right flag! */
+ {
+ /* harmless duplicate? ignore. */
+ if (packetnum<=in->lastpacket-1)
+ {
+ BUGMSG(D_EXTRA,"duplicate splitpacket ignored! (splitflag=%d)\n",
+ arcsoft->split_flag);
+ lp->stats.rx_errors++;
+ lp->stats.rx_frame_errors++;
+ return;
+ }
+ /* "bad" duplicate, kill reassembly */
+ BUGMSG(D_EXTRA,"out-of-order splitpacket, reassembly (seq=%d) aborted (splitflag=%d, seq=%d)\n",
+ in->sequence,arcsoft->split_flag,
+ arcsoft->sequence);
+ lp->aborted_seq=arcsoft->sequence;
+ kfree_skb(in->skb,FREE_WRITE);
+ in->skb=NULL;
+ lp->stats.rx_errors++;
+ lp->stats.rx_missed_errors++;
+ in->lastpacket=in->numpackets=0;
+ return;
+ }
+
+ soft=(struct ClientData *)in->skb->data;
+ }
+
+ skb=in->skb;
+
+ memcpy(skb->data+skb->len,
+ (u_char *)arcsoft+sizeof(struct ClientData),
+ length-sizeof(struct ClientData));
+ skb_put(skb,length-sizeof(struct ClientData));
+
+ soft->daddr=daddr;
+ soft->saddr=saddr;
+
+ /* are we done? */
+ if (in->lastpacket == in->numpackets)
+ {
+ if (!skb || !in->skb)
+ {
+ BUGMSG(D_NORMAL,"?!? done reassembling packet, no skb? (skb=%ph, in->skb=%ph)\n",
+ skb,in->skb);
+ }
+ else
+ {
+ in->skb=NULL;
+ in->lastpacket=in->numpackets=0;
+
+ BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
+
+ lp->stats.rx_bytes += skb->len;
+ skb->protocol=arcnetA_type_trans(skb,dev);
+ netif_rx(skb);
+ }
+ }
+ }
+}
/****************************************************************************
@@ -2567,150 +1334,132 @@ arcnetA_rx(struct device *dev,u_char *buf,
* *
****************************************************************************/
-
/* Get the current statistics. This may be called with the card open or
* closed.
*/
static struct net_device_stats *arcnet_get_stats(struct device *dev)
{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- return &lp->stats;
+ return &lp->stats;
}
-#if 0 /* standard ARCnet cards have no promiscuous mode */
-/* Set or clear the multicast filter for this adaptor.
- * num_addrs == -1 Promiscuous mode, receive all packets
- * num_addrs == 0 Normal mode, clear multicast list
- * num_addrs > 0 Multicast mode, receive normal and MC packets, and do
- * best-effort filtering.
- */
-static void
-set_multicast_list(struct device *dev)
-{
- if (num_addrs) {
- /* Enable promiscuous mode */
- } else
- /* Disable promiscuous mode, use normal mode */
-}
-#endif
/* Create the ARCnet ClientData header for an arbitrary protocol layer
*
* saddr=NULL means use device source address (always will anyway)
* daddr=NULL means leave destination address (eg unresolved arp)
*/
-int arcnetA_header(struct sk_buff *skb,struct device *dev,
- unsigned short type,void *daddr,void *saddr,unsigned len)
+static int arcnetA_header(struct sk_buff *skb,struct device *dev,
+ unsigned short type,void *daddr,void *saddr,unsigned len)
{
- struct ClientData *head = (struct ClientData *)
- skb_push(skb,dev->hard_header_len);
- struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
-
- BUGMSG(D_DURING,"create header from %d to %d; protocol %d (%Xh); size %u.\n",
- saddr ? *(u_char*)saddr : -1,
- daddr ? *(u_char*)daddr : -1,
- type,type,len);
-
- /* set the protocol ID according to RFC1201 */
- switch(type)
- {
- case ETH_P_IP:
- head->protocol_id=ARC_P_IP;
- break;
- case ETH_P_ARP:
- head->protocol_id=ARC_P_ARP;
- break;
- case ETH_P_RARP:
- head->protocol_id=ARC_P_RARP;
- break;
- case ETH_P_IPX:
- case ETH_P_802_3:
- case ETH_P_802_2:
- head->protocol_id=ARC_P_IPX;
- break;
- case ETH_P_ATALK:
- head->protocol_id=ARC_P_ATALK;
- break;
- default:
- BUGMSG(D_NORMAL,"I don't understand protocol %d (%Xh)\n",
- type,type);
- lp->stats.tx_errors++;
- lp->stats.tx_aborted_errors++;
- return 0;
- }
-
- /*
- * Set the source hardware address.
- *
- * This is pretty pointless for most purposes, but it can help
- * in debugging. saddr is stored in the ClientData header and
- * removed before sending the packet (since ARCnet does not allow
- * us to change the source address in the actual packet sent)
- */
- if(saddr)
- head->saddr=((u_char*)saddr)[0];
- else
- head->saddr=((u_char*)(dev->dev_addr))[0];
-
- head->split_flag=0; /* split packets are done elsewhere */
- head->sequence=0; /* so are sequence numbers */
-
- /* supposedly if daddr is NULL, we should ignore it... */
- if(daddr)
- {
- head->daddr=((u_char*)daddr)[0];
- return dev->hard_header_len;
- }
- else
- head->daddr=0; /* better fill one in anyway */
-
- return -dev->hard_header_len;
+ struct ClientData *head = (struct ClientData *)
+ skb_push(skb,dev->hard_header_len);
+ struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
+
+ BUGMSG(D_DURING,"create header from %d to %d; protocol %d (%Xh); size %u.\n",
+ saddr ? *(u_char*)saddr : -1,
+ daddr ? *(u_char*)daddr : -1,
+ type,type,len);
+
+ /* set the protocol ID according to RFC1201 */
+ switch(type)
+ {
+ case ETH_P_IP:
+ head->protocol_id=ARC_P_IP;
+ break;
+ case ETH_P_ARP:
+ head->protocol_id=ARC_P_ARP;
+ break;
+ case ETH_P_RARP:
+ head->protocol_id=ARC_P_RARP;
+ break;
+ case ETH_P_IPX:
+ case ETH_P_802_3:
+ case ETH_P_802_2:
+ head->protocol_id=ARC_P_IPX;
+ break;
+ case ETH_P_ATALK:
+ head->protocol_id=ARC_P_ATALK;
+ break;
+ default:
+ BUGMSG(D_NORMAL,"I don't understand protocol %d (%Xh)\n",
+ type,type);
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+ return 0;
+ }
+
+ /*
+ * Set the source hardware address.
+ *
+ * This is pretty pointless for most purposes, but it can help
+ * in debugging. saddr is stored in the ClientData header and
+ * removed before sending the packet (since ARCnet does not allow
+ * us to change the source address in the actual packet sent)
+ */
+ if(saddr)
+ head->saddr=((u_char*)saddr)[0];
+ else
+ head->saddr=((u_char*)(dev->dev_addr))[0];
+
+ head->split_flag=0; /* split packets are done elsewhere */
+ head->sequence=0; /* so are sequence numbers */
+
+ /* supposedly if daddr is NULL, we should ignore it... */
+ if(daddr)
+ {
+ head->daddr=((u_char*)daddr)[0];
+ return dev->hard_header_len;
+ }
+ else
+ head->daddr=0; /* better fill one in anyway */
+
+ return -dev->hard_header_len;
}
-
/* Rebuild the ARCnet ClientData header. This is called after an ARP
* (or in future other address resolution) has completed on this
* sk_buff. We now let ARP fill in the other fields.
*/
-int arcnetA_rebuild_header(struct sk_buff *skb)
+static int arcnetA_rebuild_header(struct sk_buff *skb)
{
- struct ClientData *head = (struct ClientData *)skb->data;
- struct device *dev=skb->dev;
- struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
- int status;
-
- /*
- * Only ARP and IP are currently supported
- *
- * FIXME: Anyone want to spec IPv6 over ARCnet ?
- */
-
- if(head->protocol_id != ARC_P_IP)
- {
- BUGMSG(D_NORMAL,"I don't understand protocol type %d (%Xh) addresses!\n",
- head->protocol_id,head->protocol_id);
- lp->stats.tx_errors++;
- lp->stats.tx_aborted_errors++;
- head->daddr=0;
- /*memcpy(eth->h_source, dev->dev_addr, dev->addr_len);*/
- return 0;
- }
-
- /*
- * Try to get ARP to resolve the header.
- */
+ struct ClientData *head = (struct ClientData *)skb->data;
+ struct device *dev=skb->dev;
+ struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
+ int status;
+
+ /*
+ * Only ARP and IP are currently supported
+ *
+ * FIXME: Anyone want to spec IPv6 over ARCnet ?
+ */
+
+ if(head->protocol_id != ARC_P_IP)
+ {
+ BUGMSG(D_NORMAL,"I don't understand protocol type %d (%Xh) addresses!\n",
+ head->protocol_id,head->protocol_id);
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+ head->daddr=0;
+ /*memcpy(eth->h_source, dev->dev_addr, dev->addr_len);*/
+ return 0;
+ }
+
+ /*
+ * Try to get ARP to resolve the header.
+ */
#ifdef CONFIG_INET
- BUGMSG(D_DURING,"rebuild header from %d to %d; protocol %Xh\n",
- head->saddr,head->daddr,head->protocol_id);
- status=arp_find(&(head->daddr),skb)? 1 : 0;
- BUGMSG(D_DURING," rebuilt: from %d to %d; protocol %Xh\n",
- head->saddr,head->daddr,head->protocol_id);
- return status;
+ BUGMSG(D_DURING,"rebuild header from %d to %d; protocol %Xh\n",
+ head->saddr,head->daddr,head->protocol_id);
+ status=arp_find(&(head->daddr),skb)? 1 : 0;
+ BUGMSG(D_DURING," rebuilt: from %d to %d; protocol %Xh\n",
+ head->saddr,head->daddr,head->protocol_id);
+ return status;
#else
- return 0;
+ return 0;
#endif
}
@@ -2719,42 +1468,42 @@ int arcnetA_rebuild_header(struct sk_buff *skb)
*
* With ARCnet we have to convert everything to Ethernet-style stuff.
*/
-unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev)
+static unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev)
{
- struct ClientData *head;
- struct arcnet_local *lp=(struct arcnet_local *) (dev->priv);
-
- /* Pull off the arcnet header. */
- skb->mac.raw=skb->data;
- skb_pull(skb,dev->hard_header_len);
- head=(struct ClientData *)skb->mac.raw;
-
- if (head->daddr==0)
- skb->pkt_type=PACKET_BROADCAST;
- else if (dev->flags&IFF_PROMISC)
- {
- /* if we're not sending to ourselves :) */
- if (head->daddr != dev->dev_addr[0])
- skb->pkt_type=PACKET_OTHERHOST;
- }
-
- /* now return the protocol number */
- switch (head->protocol_id)
- {
- case ARC_P_IP: return htons(ETH_P_IP);
- case ARC_P_ARP: return htons(ETH_P_ARP);
- case ARC_P_RARP: return htons(ETH_P_RARP);
-
- case ARC_P_IPX:
- case ARC_P_NOVELL_EC:
- return htons(ETH_P_802_3);
- default:
- lp->stats.rx_errors++;
- lp->stats.rx_crc_errors++;
- return 0;
- }
-
- return htons(ETH_P_IP);
+ struct ClientData *head;
+ struct arcnet_local *lp=(struct arcnet_local *) (dev->priv);
+
+ /* Pull off the arcnet header. */
+ skb->mac.raw=skb->data;
+ skb_pull(skb,dev->hard_header_len);
+ head=(struct ClientData *)skb->mac.raw;
+
+ if (head->daddr==0)
+ skb->pkt_type=PACKET_BROADCAST;
+ else if (dev->flags&IFF_PROMISC)
+ {
+ /* if we're not sending to ourselves :) */
+ if (head->daddr != dev->dev_addr[0])
+ skb->pkt_type=PACKET_OTHERHOST;
+ }
+
+ /* now return the protocol number */
+ switch (head->protocol_id)
+ {
+ case ARC_P_IP: return htons(ETH_P_IP);
+ case ARC_P_ARP: return htons(ETH_P_ARP);
+ case ARC_P_RARP: return htons(ETH_P_RARP);
+
+ case ARC_P_IPX:
+ case ARC_P_NOVELL_EC:
+ return htons(ETH_P_802_3);
+ default:
+ lp->stats.rx_errors++;
+ lp->stats.rx_crc_errors++;
+ return 0;
+ }
+
+ return htons(ETH_P_IP);
}
@@ -2769,19 +1518,17 @@ unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev)
*/
static int arcnetE_init(struct device *dev)
{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- ether_setup(dev); /* we're emulating ether here, not ARCnet */
- dev->dev_addr[0]=0;
- dev->dev_addr[5]=lp->stationid;
- dev->mtu=512-sizeof(struct HardHeader)-dev->hard_header_len-1;
- dev->open=arcnetE_open_close;
- dev->stop=arcnetE_open_close;
- dev->hard_start_xmit=arcnetE_send_packet;
+ ether_setup(dev); /* we're emulating ether here, not ARCnet */
+ dev->dev_addr[0]=0;
+ dev->dev_addr[5]=lp->stationid;
+ dev->mtu=512-sizeof(struct archdr)-dev->hard_header_len-1;
+ dev->open=arcnetE_open_close;
+ dev->stop=arcnetE_open_close;
+ dev->hard_start_xmit=arcnetE_send_packet;
- BUGMSG(D_NORMAL,"ARCnet Ethernet-Encap protocol initialized.\n");
-
- return 0;
+ return 0;
}
@@ -2790,7 +1537,7 @@ static int arcnetE_init(struct device *dev)
*/
static int arcnetE_open_close(struct device *dev)
{
- return 0;
+ return 0;
}
@@ -2799,96 +1546,75 @@ static int arcnetE_open_close(struct device *dev)
static int
arcnetE_send_packet(struct sk_buff *skb, struct device *dev)
{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- int ioaddr=IOADDR,bad;
- union ArcPacket *arcpacket =
- (union ArcPacket *)phys_to_virt(dev->mem_start+512*(lp->txbuf^1));
- u_char *arcsoft,daddr;
- short offset,length=skb->len+1;
-
- lp->intx++;
-
- bad=arcnet_send_packet_bad(skb,dev);
- if (bad)
- {
- lp->intx--;
- return bad;
- }
-
- TBUSY=1;
-
- if (length>XMTU)
- {
- BUGMSG(D_NORMAL,"MTU must be <= 493 for ethernet encap (length=%d).\n",
- length);
- BUGMSG(D_NORMAL,"transmit aborted.\n");
-
- dev_kfree_skb(skb,FREE_WRITE);
- lp->intx--;
- return 0;
- }
-
- BUGMSG(D_DURING,"starting tx sequence...\n");
-
- lp->txbuf=lp->txbuf^1; /* XOR with 1 to alternate btw 2 & 3 */
-
-#ifndef SLOW_XMIT_COPY
- /* clean out the page to make debugging make more sense :) */
- BUGLVL(D_DURING)
- memset_io(dev->mem_start+lp->txbuf*512,0x42,512);
-#endif
-
- /* broadcasts have address FF:FF:FF:FF:FF:FF in etherspeak */
- if (((struct ethhdr*)(skb->data))->h_dest[0] == 0xFF)
- daddr=arcpacket->hardheader.destination=0;
- else
- daddr=arcpacket->hardheader.destination=
- ((struct ethhdr*)(skb->data))->h_dest[5];
-
- /* load packet into shared memory */
- offset=512-length;
- if (length>MTU) /* long/exception packet */
- {
- if (length<MinTU) offset-=3;
- arcpacket->hardheader.offset1=0;
- arcpacket->hardheader.offset2=offset;
- }
- else /* short packet */
- {
- arcpacket->hardheader.offset1=(offset-=256);
- }
-
- BUGMSG(D_DURING," length=%Xh, offset=%Xh, offset1=%Xh, offset2=%Xh\n",
- length,offset,arcpacket->hardheader.offset1,
- arcpacket->hardheader.offset2);
-
- arcsoft=&arcpacket->raw[offset];
- arcsoft[0]=ARC_P_ETHER;
- arcsoft++;
-
- /* copy the packet into ARCnet shmem
- * - the first bytes of ClientData header are skipped
- */
- BUGMSG(D_DURING,"ready to memcpy\n");
-
- memcpy(arcsoft,skb->data,skb->len);
-
- BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
- daddr,length);
-
- BUGLVL(D_TX) arcnet_dump_packet(dev,arcpacket->raw,length>=240,"tx");
-
- lp->lastload_dest=daddr;
- lp->txready=lp->txbuf; /* packet is ready for sending */
-
- dev_kfree_skb(skb,FREE_WRITE);
-
- if (arcnet_go_tx(dev,1))
- {
- /* inform upper layers */
- TBUSY=0;
- mark_bh(NET_BH);
- }
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int bad,oldmask=0;
+ u_char daddr;
+ short offset,length=skb->len+1;
+ u_char proto=ARC_P_ETHER;
+
+ lp->intx++;
+
+ oldmask |= lp->intmask;
+ lp->intmask=0;
+ SETMASK;
+
+ bad=arcnet_send_packet_bad(skb,dev);
+ if (bad)
+ {
+ lp->intx--;
+ lp->intmask=oldmask;
+ SETMASK;
+ return bad;
+ }
+
+ /* arcnet_send_packet_pad has already set tbusy - don't bother here. */
+
+ lp->intmask=oldmask;
+ SETMASK;
+
+ if (length>XMTU)
+ {
+ BUGMSG(D_NORMAL,"MTU must be <= 493 for ethernet encap (length=%d).\n",
+ length);
+ BUGMSG(D_NORMAL,"transmit aborted.\n");
+
+ dev_kfree_skb(skb,FREE_WRITE);
+ lp->intx--;
+ return 0;
+ }
+
+ BUGMSG(D_DURING,"starting tx sequence...\n");
+
+ /* broadcasts have address FF:FF:FF:FF:FF:FF in etherspeak */
+ if (((struct ethhdr*)(skb->data))->h_dest[0] == 0xFF)
+ daddr=0;
+ else
+ daddr=((struct ethhdr*)(skb->data))->h_dest[5];
+
+ /* load packet into shared memory */
+ offset=512-length;
+ if (length>MTU) /* long/exception packet */
+ {
+ if (length<MinTU) offset-=3;
+ }
+ else /* short packet */
+ {
+ offset-=256;
+ }
+
+ BUGMSG(D_DURING," length=%Xh, offset=%Xh\n",
+ length,offset);
+
+ (*lp->prepare_tx)(dev, &proto, 1, skb->data, length-1, daddr, 0,
+ offset);
+
+ dev_kfree_skb(skb,FREE_WRITE);
+
+ if (arcnet_go_tx(dev,1))
+ {
+ /* inform upper layers */
+ arcnet_tx_done(lp->adev, lp);
+ }
dev->trans_start=jiffies;
lp->intx--;
@@ -2920,15 +1646,16 @@ arcnetE_rx(struct device *dev,u_char *arcsoft,
return;
}
- skb->len = length;
+ skb_put(skb,length);
+
skb->dev = dev;
memcpy(skb->data,(u_char *)arcsoft+1,length-1);
BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
+ lp->stats.rx_bytes += skb->len;
skb->protocol=eth_type_trans(skb,dev);
-
netif_rx(skb);
}
@@ -2945,23 +1672,22 @@ arcnetE_rx(struct device *dev,u_char *arcsoft,
*/
static int arcnetS_init(struct device *dev)
{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-
- arcnet_setup(dev);
-
- /* And now fill particular fields with arcnet values */
- dev->dev_addr[0]=lp->stationid;
- dev->hard_header_len=sizeof(struct S_ClientData);
- dev->mtu=512-sizeof(struct HardHeader)-dev->hard_header_len
- + S_EXTRA_CLIENTDATA;
- dev->open=arcnetS_open_close;
- dev->stop=arcnetS_open_close;
- dev->hard_start_xmit=arcnetS_send_packet;
- dev->hard_header=arcnetS_header;
- dev->rebuild_header=arcnetS_rebuild_header;
- BUGMSG(D_NORMAL,"ARCnet RFC1051 (NetBSD, AmiTCP) protocol initialized.\n");
-
- return 0;
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+
+ arcnet_setup(dev);
+
+ /* And now fill particular fields with arcnet values */
+ dev->dev_addr[0]=lp->stationid;
+ dev->hard_header_len=sizeof(struct S_ClientData);
+ dev->mtu=512-sizeof(struct archdr)-dev->hard_header_len
+ + S_EXTRA_CLIENTDATA;
+ dev->open=arcnetS_open_close;
+ dev->stop=arcnetS_open_close;
+ dev->hard_start_xmit=arcnetS_send_packet;
+ dev->hard_header=arcnetS_header;
+ dev->rebuild_header=arcnetS_rebuild_header;
+
+ return 0;
}
@@ -2970,7 +1696,7 @@ static int arcnetS_init(struct device *dev)
*/
static int arcnetS_open_close(struct device *dev)
{
- return 0;
+ return 0;
}
@@ -2979,63 +1705,61 @@ static int arcnetS_open_close(struct device *dev)
static int
arcnetS_send_packet(struct sk_buff *skb, struct device *dev)
{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- int ioaddr=IOADDR,bad,length;
- struct S_ClientData *hdr=(struct S_ClientData *)skb->data;
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int bad,length;
+ struct S_ClientData *hdr=(struct S_ClientData *)skb->data;
- lp->intx++;
+ lp->intx++;
- bad=arcnet_send_packet_bad(skb,dev);
- if (bad)
- {
- lp->intx--;
- return bad;
- }
+ bad=arcnet_send_packet_bad(skb,dev);
+ if (bad)
+ {
+ lp->intx--;
+ return bad;
+ }
- TBUSY=1;
+ /* arcnet_send_packet_pad has already set tbusy - don't bother here. */
- length = 1 < skb->len ? skb->len : 1;
+ length = 1 < skb->len ? skb->len : 1;
- BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"tx");
+ BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"tx");
- /* fits in one packet? */
- if (length-S_EXTRA_CLIENTDATA<=XMTU)
- {
- arcnetAS_prepare_tx(dev,
+ /* fits in one packet? */
+ if (length-S_EXTRA_CLIENTDATA<=XMTU)
+ {
+ (*lp->prepare_tx)(dev,
skb->data+S_EXTRA_CLIENTDATA,
sizeof(struct S_ClientData)-S_EXTRA_CLIENTDATA,
skb->data+sizeof(struct S_ClientData),
length-sizeof(struct S_ClientData),
- hdr->daddr,0);
+ hdr->daddr,0,0);
- /* done right away */
- dev_kfree_skb(skb,FREE_WRITE);
+ /* done right away */
+ dev_kfree_skb(skb,FREE_WRITE);
- if (arcnet_go_tx(dev,1))
- {
- /* inform upper layers */
- TBUSY=0;
- mark_bh(NET_BH);
- }
- }
- else /* too big for one - not accepted */
+ if (arcnet_go_tx(dev,1))
{
- BUGMSG(D_NORMAL,"packet too long (length=%d)\n",
- length);
- dev_kfree_skb(skb,FREE_WRITE);
- lp->stats.tx_dropped++;
- TBUSY=0;
- mark_bh(NET_BH);
+ /* inform upper layers */
+ arcnet_tx_done(lp->adev, lp);
}
+ }
+ else /* too big for one - not accepted */
+ {
+ BUGMSG(D_NORMAL,"packet too long (length=%d)\n",
+ length);
+ dev_kfree_skb(skb,FREE_WRITE);
+ lp->stats.tx_dropped++;
+ arcnet_tx_done(lp->adev, lp);
+ }
- dev->trans_start=jiffies;
- lp->intx--;
+ dev->trans_start=jiffies;
+ lp->intx--;
- /* make sure we didn't ignore a TX IRQ while we were in here */
- lp->intmask |= TXFREEflag;
- SETMASK;
+ /* make sure we didn't ignore a TX IRQ while we were in here */
+ lp->intmask |= TXFREEflag;
+ SETMASK;
- return 0;
+ return 0;
}
@@ -3043,47 +1767,43 @@ arcnetS_send_packet(struct sk_buff *skb, struct device *dev)
*/
static void
arcnetS_rx(struct device *dev,u_char *buf,
- int length,u_char saddr, u_char daddr)
+ int length,u_char saddr, u_char daddr)
{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- struct sk_buff *skb;
- struct S_ClientData *arcsoft,*soft;
-
- arcsoft=(struct S_ClientData *)(buf-S_EXTRA_CLIENTDATA);
- length+=S_EXTRA_CLIENTDATA;
-
- BUGMSG(D_DURING,"it's an RFC1051 packet (length=%d)\n",
- length);
-
-
-
- { /* was "if not split" in A protocol, S is never split */
-
- skb = alloc_skb(length, GFP_ATOMIC);
- if (skb == NULL) {
- BUGMSG(D_NORMAL,"Memory squeeze, dropping packet.\n");
- lp->stats.rx_dropped++;
- return;
- }
- soft=(struct S_ClientData *)skb->data;
- skb->len = length;
- memcpy((u_char *)soft + sizeof(struct S_ClientData)
- - S_EXTRA_CLIENTDATA,
- (u_char *)arcsoft + sizeof(struct S_ClientData)
- - S_EXTRA_CLIENTDATA,
- length - sizeof(struct S_ClientData)
- + S_EXTRA_CLIENTDATA);
- soft->protocol_id=arcsoft->protocol_id;
- soft->daddr=daddr;
- soft->saddr=saddr;
- skb->dev = dev; /* is already lp->sdev */
-
- BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
-
- skb->protocol=arcnetS_type_trans(skb,dev);
-
- netif_rx(skb);
- }
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ struct sk_buff *skb;
+ struct S_ClientData *arcsoft,*soft;
+
+ arcsoft=(struct S_ClientData *)(buf-S_EXTRA_CLIENTDATA);
+ length+=S_EXTRA_CLIENTDATA;
+
+ BUGMSG(D_DURING,"it's an RFC1051 packet (length=%d)\n",
+ length);
+
+ { /* was "if not split" in A protocol, S is never split */
+
+ skb = alloc_skb(length, GFP_ATOMIC);
+ if (skb == NULL) {
+ BUGMSG(D_NORMAL,"Memory squeeze, dropping packet.\n");
+ lp->stats.rx_dropped++;
+ return;
+ }
+ soft=(struct S_ClientData *)skb->data;
+ skb_put(skb,length);
+
+ memcpy((u_char *)soft + sizeof(struct S_ClientData) - S_EXTRA_CLIENTDATA,
+ (u_char *)arcsoft + sizeof(struct S_ClientData) -S_EXTRA_CLIENTDATA,
+ length - sizeof(struct S_ClientData) + S_EXTRA_CLIENTDATA);
+ soft->protocol_id=arcsoft->protocol_id;
+ soft->daddr=daddr;
+ soft->saddr=saddr;
+ skb->dev = dev; /* is already lp->sdev */
+
+ BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
+
+ lp->stats.rx_bytes += skb->len;
+ skb->protocol=arcnetS_type_trans(skb,dev);
+ netif_rx(skb);
+ }
}
@@ -3092,55 +1812,55 @@ arcnetS_rx(struct device *dev,u_char *buf,
* saddr=NULL means use device source address (always will anyway)
* daddr=NULL means leave destination address (eg unresolved arp)
*/
-int arcnetS_header(struct sk_buff *skb,struct device *dev,
- unsigned short type,void *daddr,void *saddr,unsigned len)
+static int arcnetS_header(struct sk_buff *skb,struct device *dev,
+ unsigned short type,void *daddr,void *saddr,unsigned len)
{
- struct S_ClientData *head = (struct S_ClientData *)
- skb_push(skb,dev->hard_header_len);
- struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
-
- /* set the protocol ID according to RFC1051 */
- switch(type)
- {
- case ETH_P_IP:
- head->protocol_id=ARC_P_IP_RFC1051;
- BUGMSG(D_DURING,"S_header: IP_RFC1051 packet.\n");
- break;
- case ETH_P_ARP:
- head->protocol_id=ARC_P_ARP_RFC1051;
- BUGMSG(D_DURING,"S_header: ARP_RFC1051 packet.\n");
- break;
- default:
- BUGMSG(D_NORMAL,"I don't understand protocol %d (%Xh)\n",
- type,type);
- lp->stats.tx_errors++;
- lp->stats.tx_aborted_errors++;
- return 0;
- }
-
- /*
- * Set the source hardware address.
- *
- * This is pretty pointless for most purposes, but it can help
- * in debugging. saddr is stored in the ClientData header and
- * removed before sending the packet (since ARCnet does not allow
- * us to change the source address in the actual packet sent)
- */
- if(saddr)
- head->saddr=((u_char*)saddr)[0];
- else
- head->saddr=((u_char*)(dev->dev_addr))[0];
-
- /* supposedly if daddr is NULL, we should ignore it... */
- if(daddr)
- {
+ struct S_ClientData *head = (struct S_ClientData *)
+ skb_push(skb,dev->hard_header_len);
+ struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
+
+ /* set the protocol ID according to RFC1051 */
+ switch(type)
+ {
+ case ETH_P_IP:
+ head->protocol_id=ARC_P_IP_RFC1051;
+ BUGMSG(D_DURING,"S_header: IP_RFC1051 packet.\n");
+ break;
+ case ETH_P_ARP:
+ head->protocol_id=ARC_P_ARP_RFC1051;
+ BUGMSG(D_DURING,"S_header: ARP_RFC1051 packet.\n");
+ break;
+ default:
+ BUGMSG(D_NORMAL,"I don't understand protocol %d (%Xh)\n",
+ type,type);
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+ return 0;
+ }
+
+ /*
+ * Set the source hardware address.
+ *
+ * This is pretty pointless for most purposes, but it can help
+ * in debugging. saddr is stored in the ClientData header and
+ * removed before sending the packet (since ARCnet does not allow
+ * us to change the source address in the actual packet sent)
+ */
+ if(saddr)
+ head->saddr=((u_char*)saddr)[0];
+ else
+ head->saddr=((u_char*)(dev->dev_addr))[0];
+
+ /* supposedly if daddr is NULL, we should ignore it... */
+ if(daddr)
+ {
head->daddr=((u_char*)daddr)[0];
return dev->hard_header_len;
- }
- else
- head->daddr=0; /* better fill one in anyway */
+ }
+ else
+ head->daddr=0; /* better fill one in anyway */
- return -dev->hard_header_len;
+ return -dev->hard_header_len;
}
@@ -3148,34 +1868,34 @@ int arcnetS_header(struct sk_buff *skb,struct device *dev,
* (or in future other address resolution) has completed on this
* sk_buff. We now let ARP fill in the other fields.
*/
-int arcnetS_rebuild_header(struct sk_buff *skb)
+static int arcnetS_rebuild_header(struct sk_buff *skb)
{
- struct device *dev=skb->dev;
- struct S_ClientData *head = (struct S_ClientData *)skb->data;
- struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
-
- /*
- * Only ARP and IP are currently supported
- */
-
- if(head->protocol_id != ARC_P_IP_RFC1051)
- {
- BUGMSG(D_NORMAL,"I don't understand protocol type %d (%Xh) addresses!\n",
- head->protocol_id,head->protocol_id);
- lp->stats.tx_errors++;
- lp->stats.tx_aborted_errors++;
- head->daddr=0;
- /*memcpy(eth->h_source, dev->dev_addr, dev->addr_len);*/
- return 0;
- }
-
- /*
- * Try to get ARP to resolve the header.
- */
+ struct device *dev=skb->dev;
+ struct S_ClientData *head = (struct S_ClientData *)skb->data;
+ struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
+
+ /*
+ * Only ARP and IP are currently supported
+ */
+
+ if(head->protocol_id != ARC_P_IP_RFC1051)
+ {
+ BUGMSG(D_NORMAL,"I don't understand protocol type %d (%Xh) addresses!\n",
+ head->protocol_id,head->protocol_id);
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+ head->daddr=0;
+ /*memcpy(eth->h_source, dev->dev_addr, dev->addr_len);*/
+ return 0;
+ }
+
+ /*
+ * Try to get ARP to resolve the header.
+ */
#ifdef CONFIG_INET
- return arp_find(&(head->daddr),skb)? 1 : 0;
+ return arp_find(&(head->daddr),skb)? 1 : 0;
#else
- return 0;
+ return 0;
#endif
}
@@ -3186,129 +1906,164 @@ int arcnetS_rebuild_header(struct sk_buff *skb)
*/
unsigned short arcnetS_type_trans(struct sk_buff *skb,struct device *dev)
{
- struct S_ClientData *head;
- struct arcnet_local *lp=(struct arcnet_local *) (dev->priv);
-
- /* Pull off the arcnet header. */
- skb->mac.raw=skb->data;
- skb_pull(skb,dev->hard_header_len);
- head=(struct S_ClientData *)skb->mac.raw;
-
- if (head->daddr==0)
- skb->pkt_type=PACKET_BROADCAST;
- else if (dev->flags&IFF_PROMISC)
- {
- /* if we're not sending to ourselves :) */
- if (head->daddr != dev->dev_addr[0])
- skb->pkt_type=PACKET_OTHERHOST;
- }
-
- /* now return the protocol number */
- switch (head->protocol_id)
- {
- case ARC_P_IP_RFC1051: return htons(ETH_P_IP);
- case ARC_P_ARP_RFC1051: return htons(ETH_P_ARP);
- case ARC_P_ATALK: return htons(ETH_P_ATALK); /* untested appletalk */
- default:
- lp->stats.rx_errors++;
- lp->stats.rx_crc_errors++;
- return 0;
- }
-
- return htons(ETH_P_IP);
+ struct S_ClientData *head;
+ struct arcnet_local *lp=(struct arcnet_local *) (dev->priv);
+
+ /* Pull off the arcnet header. */
+ skb->mac.raw=skb->data;
+ skb_pull(skb,dev->hard_header_len);
+ head=(struct S_ClientData *)skb->mac.raw;
+
+ if (head->daddr==0)
+ skb->pkt_type=PACKET_BROADCAST;
+ else if (dev->flags&IFF_PROMISC)
+ {
+ /* if we're not sending to ourselves :) */
+ if (head->daddr != dev->dev_addr[0])
+ skb->pkt_type=PACKET_OTHERHOST;
+ }
+
+ /* now return the protocol number */
+ switch (head->protocol_id)
+ {
+ case ARC_P_IP_RFC1051: return htons(ETH_P_IP);
+ case ARC_P_ARP_RFC1051: return htons(ETH_P_ARP);
+ case ARC_P_ATALK: return htons(ETH_P_ATALK); /* untested appletalk */
+ default:
+ lp->stats.rx_errors++;
+ lp->stats.rx_crc_errors++;
+ return 0;
+ }
+
+ return htons(ETH_P_IP);
}
#endif /* CONFIG_ARCNET_1051 */
+
/****************************************************************************
* *
* Kernel Loadable Module Support *
* *
****************************************************************************/
-
#ifdef MODULE
-static char devicename[9] = "";
-static struct device thiscard = {
- devicename, /* device name is inserted by linux/drivers/net/net_init.c */
- 0, 0, 0, 0,
- 0, 0, /* I/O address, IRQ */
- 0, 0, 0, NULL, arcnet_probe
-};
+void cleanup_module(void)
+{
+ printk("Generic arcnet support removed.\n");
+}
+void arcnet_use_count(int open)
+{
+ if (open)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
-static int io=0x0; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
-static int irqnum=0; /* or use the insmod io= irq= shmem= options */
-static int irq=0;
-static int shmem=0;
-static char *device = NULL; /* use eg. device="arc1" to change name */
+#else
-MODULE_PARM(io, "i");
-MODULE_PARM(irqnum, "i");
-MODULE_PARM(shmem, "i");
+void arcnet_use_count(int open)
+{
+}
-#ifdef RIM_I_MODE
- static int node=0; /* you must specify the node ID for RIM I cards */
-#endif
+struct device arcnet_devs[MAX_ARCNET_DEVS];
+int arcnet_num_devs=0;
+char arcnet_dev_names[MAX_ARCNET_DEVS][10];
-int init_module(void)
+__initfunc(void arcnet_init(void))
{
- struct device *dev=&thiscard;
- if (device)
- strcpy(dev->name,device);
- #ifndef CONFIG_ARCNET_ETHNAME
- else if (!dev->name[0]) strcpy(dev->name,"arc0");
- #endif
+ int c;
- #ifdef RIM_I_MODE
- if (node) io=node;
- #endif
+ init_module();
- dev->base_addr=io;
+ /* Don't register_netdev here. The chain hasn't been initialised. */
- if (irq) irqnum=irq;
- dev->irq=irqnum;
- if (dev->irq==2) dev->irq=9;
+#ifdef CONFIG_ARCNET_COM90xx
+ if ((!com90xx_explicit) && arcnet_num_devs < MAX_ARCNET_DEVS)
+ {
+ arcnet_devs[arcnet_num_devs].init=arc90xx_probe;
+ arcnet_devs[arcnet_num_devs].name=
+ (char *)&arcnet_dev_names[arcnet_num_devs];
+ arcnet_num_devs++;
+ }
+#endif
- if (shmem)
- {
- dev->mem_start=shmem;
- dev->mem_end=thiscard.mem_start+512*4-1;
- dev->rmem_start=thiscard.mem_start+512*0;
- dev->rmem_end=thiscard.mem_start+512*2-1;
- }
+ if (!arcnet_num_devs)
+ {
+ printk("Don't forget to load the chipset driver.\n");
+ return;
+ }
- if (register_netdev(dev) != 0)
- return -EIO;
- return 0;
-}
+ /* Link into the device chain */
-void cleanup_module(void)
-{
- struct device *dev=&thiscard;
- int ioaddr=IOADDR;
+ /* Q: Should we put ourselves at the beginning or the end of the chain? */
+ /* Probably the end, because we're not so fast, but... */
- if (dev->start) arcnet_close(dev);
+ for (c=0; c< (arcnet_num_devs-1); c++)
+ arcnet_devs[c].next=&arcnet_devs[c+1];
- /* Flush TX and disable RX */
- if (ioaddr)
- {
- AINTMASK(0); /* disable IRQ's */
- ACOMMAND(NOTXcmd); /* stop transmit */
- ACOMMAND(NORXcmd); /* disable receive */
- }
+ arcnet_devs[c].next=dev_base;
+ dev_base=&arcnet_devs[0];
- if (dev->irq)
- {
- irq2dev_map[dev->irq] = NULL;
- free_irq(dev->irq,NULL);
- }
+ /* Give names to those without them */
- if (dev->base_addr) RELEASE_REGION(dev->base_addr,ARCNET_TOTAL_SIZE);
- unregister_netdev(dev);
- kfree(dev->priv);
- dev->priv = NULL;
+ for (c=0; c< arcnet_num_devs; c++)
+ if (!arcnet_dev_names[c][0])
+ arcnet_makename((char *)&arcnet_dev_names[c]);
}
#endif /* MODULE */
+
+
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(static int init_module(void))
+#endif
+{
+#ifdef ALPHA_WARNING
+ BUGLVL(D_EXTRA)
+ {
+ printk("arcnet: ***\n");
+ printk("arcnet: * Read arcnet.txt for important release notes!\n");
+ printk("arcnet: *\n");
+ printk("arcnet: * This is an ALPHA version! (Last stable release: v2.56) E-mail me if\n");
+ printk("arcnet: * you have any questions, comments, or bug reports.\n");
+ printk("arcnet: ***\n");
+ }
+#endif
+
+ printk("%sAvailable protocols: ARCnet RFC1201"
+#ifdef CONFIG_ARCNET_ETH
+ ", Ethernet-Encap"
+#endif
+#ifdef CONFIG_ARCNET_1051
+ ", ARCnet RFC1051"
+#endif
+#ifdef MODULE
+ ".\nDon't forget to load the chipset driver"
+#endif
+ ".\n",version);
+ return 0;
+}
+
+
+void arcnet_makename(char *device)
+{
+ struct device *dev;
+ int arcnum;
+
+ arcnum = 0;
+ for (;;)
+ {
+ sprintf(device, "arc%d", arcnum);
+ for (dev = dev_base; dev; dev=dev->next)
+ if (dev->name != device && !strcmp(dev->name, device))
+ break;
+ if (!dev)
+ return;
+ arcnum++;
+ }
+}
diff --git a/drivers/net/baycom.c b/drivers/net/baycom.c
index 74c041342..ef99046dc 100644
--- a/drivers/net/baycom.c
+++ b/drivers/net/baycom.c
@@ -68,7 +68,8 @@
* History:
* 0.1 26.06.96 Adapted from baycom.c and made network driver interface
* 18.10.96 Changed to new user space access routines (copy_{to,from}_user)
- * 0.3 26.04.96 init code/data tagged
+ * 0.3 26.04.97 init code/data tagged
+ * 0.4 08.07.97 alternative ser12 decoding algorithm (uses delta CTS ints)
*/
/*****************************************************************************/
@@ -149,12 +150,13 @@ extern inline int copy_to_user(void *to, const void *from, unsigned long n)
* modem options; bit mask
*/
#define BAYCOM_OPTIONS_SOFTDCD 1
+#define BAYCOM_ALT_SER12
/* --------------------------------------------------------------------- */
static const char bc_drvname[] = "baycom";
static const char bc_drvinfo[] = KERN_INFO "baycom: (C) 1996 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "baycom: version 0.3 compiled " __TIME__ " " __DATE__ "\n";
+KERN_INFO "baycom: version 0.4 compiled " __TIME__ " " __DATE__ "\n";
/* --------------------------------------------------------------------- */
@@ -214,14 +216,20 @@ struct baycom_state {
unsigned char flags;
unsigned int shreg;
struct modem_state_ser12 {
+ unsigned char tx_bit;
+ int dcd_sum0, dcd_sum1, dcd_sum2;
unsigned char last_sample;
- unsigned char interm_sample;
- unsigned int bit_pll;
+ unsigned char last_rxbit;
unsigned int dcd_shreg;
- int dcd_sum0, dcd_sum1, dcd_sum2;
unsigned int dcd_time;
- unsigned char last_rxbit;
- unsigned char tx_bit;
+ unsigned int bit_pll;
+#ifdef BAYCOM_ALT_SER12
+ unsigned long last_jiffies;
+ unsigned int pll_time;
+ unsigned int txshreg;
+#else /* BAYCOM_ALT_SER12 */
+ unsigned char interm_sample;
+#endif /* BAYCOM_ALT_SER12 */
} ser12;
struct modem_state_par96 {
int dcd_count;
@@ -272,6 +280,183 @@ static void inline baycom_int_freq(struct baycom_state *bc)
* ===================== SER12 specific routines =========================
*/
+#ifdef BAYCOM_ALT_SER12
+
+#define SER12_BAUD 1200
+
+/* --------------------------------------------------------------------- */
+
+extern inline unsigned int hweight16(unsigned short w)
+ __attribute__ ((unused));
+extern inline unsigned int hweight8(unsigned char w)
+ __attribute__ ((unused));
+
+extern inline unsigned int hweight16(unsigned short w)
+{
+ unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
+ res = (res & 0x3333) + ((res >> 2) & 0x3333);
+ res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
+ return (res & 0x00FF) + ((res >> 8) & 0x00FF);
+}
+
+extern inline unsigned int hweight8(unsigned char w)
+{
+ unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
+ res = (res & 0x33) + ((res >> 2) & 0x33);
+ return (res & 0x0F) + ((res >> 4) & 0x0F);
+}
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void ser12_rxsample(struct device *dev, struct baycom_state *bc, unsigned char news)
+{
+ bc->modem.ser12.dcd_shreg <<= 1;
+ bc->modem.ser12.bit_pll += 0x2000;
+ if (bc->modem.ser12.last_sample != news) {
+ bc->modem.ser12.last_sample = news;
+ bc->modem.ser12.dcd_shreg |= 1;
+ if (bc->modem.ser12.bit_pll < 0x9000)
+ bc->modem.ser12.bit_pll += 0x1000;
+ else
+ bc->modem.ser12.bit_pll -= 0x1000;
+ bc->modem.ser12.dcd_sum0 += 4 * hweight8(bc->modem.ser12.dcd_shreg & 0x38)
+ - hweight16(bc->modem.ser12.dcd_shreg & 0x7c0);
+ }
+ hdlcdrv_channelbit(&bc->hdrv, !!bc->modem.ser12.last_sample);
+ if ((--bc->modem.ser12.dcd_time) <= 0) {
+ hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
+ bc->modem.ser12.dcd_sum1 +
+ bc->modem.ser12.dcd_sum2) < 0);
+ bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+ bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+ bc->modem.ser12.dcd_sum0 = 2; /* slight bias */
+ bc->modem.ser12.dcd_time = 120;
+ }
+ if (bc->modem.ser12.bit_pll >= 0x10000) {
+ bc->modem.ser12.bit_pll &= 0xffff;
+ bc->modem.shreg >>= 1;
+ if (bc->modem.ser12.last_rxbit == bc->modem.ser12.last_sample)
+ bc->modem.shreg |= 0x10000;
+ bc->modem.ser12.last_rxbit = bc->modem.ser12.last_sample;
+ if (bc->modem.shreg & 1) {
+ hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
+ bc->modem.shreg = 0x10000;
+ }
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void ser12_rx(struct device *dev, struct baycom_state *bc, unsigned char curs)
+{
+ unsigned long curjiff;
+ struct timeval tv;
+ unsigned int timediff;
+
+ /*
+ * get current time
+ */
+ curjiff = jiffies;
+ do_gettimeofday(&tv);
+ if ((signed)(curjiff - bc->modem.ser12.last_jiffies) >= HZ/4) {
+ /* long inactivity; clear HDLC and DCD */
+ bc->modem.ser12.dcd_sum1 = 0;
+ bc->modem.ser12.dcd_sum2 = 0;
+ bc->modem.ser12.dcd_sum0 = 2;
+ bc->modem.ser12.dcd_time = 120;
+ hdlcdrv_setdcd(&bc->hdrv, 0);
+ hdlcdrv_putbits(&bc->hdrv, 0xffff);
+ bc->modem.ser12.last_jiffies = curjiff;
+ bc->modem.ser12.pll_time = tv.tv_usec;
+ }
+ bc->modem.ser12.last_jiffies = curjiff;
+ timediff = tv.tv_usec + 1000000 - bc->modem.ser12.pll_time;
+ timediff %= 1000000;
+ timediff /= 125000/SER12_BAUD;
+ bc->modem.ser12.pll_time = (bc->modem.ser12.pll_time + timediff * (125000/SER12_BAUD)) % 1000000;
+ for (; timediff > 1; timediff--)
+ ser12_rxsample(dev, bc, bc->modem.ser12.last_sample);
+ if (timediff >= 1)
+ ser12_rxsample(dev, bc, curs);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)dev_id;
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+ unsigned char iir, msr = 0;
+ unsigned int txcount = 0;
+ unsigned int rxcount = 0;
+
+ if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+ return;
+
+ for (;;) {
+ iir = inb(IIR(dev->base_addr));
+ if (iir & 1)
+ break;
+ switch (iir & 6) {
+ case 6:
+ inb(LSR(dev->base_addr));
+ continue;
+
+ case 4:
+ inb(RBR(dev->base_addr));
+ continue;
+
+ case 2:
+ /*
+ * make sure the next interrupt is generated;
+ * 0 must be used to power the modem; the modem draws its
+ * power from the TxD line
+ */
+ outb(0x00, THR(dev->base_addr));
+ bc->modem.arb_divider--;
+ baycom_int_freq(bc);
+ if (hdlcdrv_ptt(&bc->hdrv)) {
+ /*
+ * first output the last bit (!) then call HDLC transmitter,
+ * since this may take quite long
+ */
+ outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
+ txcount++;
+ } else
+ outb(0x0d, MCR(dev->base_addr)); /* transmitter off */
+ continue;
+
+ default:
+ msr = inb(MSR(dev->base_addr));
+ if (msr & 1) /* delta CTS interrupt */
+ rxcount++;
+ continue;
+ }
+ }
+ if (rxcount)
+ ser12_rx(dev, bc, msr & 0x10);
+ if (txcount) {
+#ifdef BAYCOM_DEBUG
+ if (bc->debug_vals.cur_pllcorr < txcount)
+ bc->debug_vals.cur_pllcorr = txcount;
+#endif /* BAYCOM_DEBUG */
+ if (bc->modem.ser12.txshreg <= 1)
+ bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
+ bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1));
+ bc->modem.ser12.txshreg >>= 1;
+ }
+ sti();
+ if (bc->modem.arb_divider <= 0) {
+ bc->modem.arb_divider = SER12_BAUD/100;
+ hdlcdrv_arbitrate(dev, &bc->hdrv);
+ }
+ hdlcdrv_transmitter(dev, &bc->hdrv);
+ hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+#else /* BAYCOM_ALT_SER12 */
+
static void inline ser12_set_divisor(struct device *dev,
unsigned char divisor)
{
@@ -503,13 +688,15 @@ static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
hdlcdrv_transmitter(dev, &bc->hdrv);
hdlcdrv_receiver(dev, &bc->hdrv);
}
+#endif /* BAYCOM_ALT_SER12 */
/* --------------------------------------------------------------------- */
enum uart { c_uart_unknown, c_uart_8250,
- c_uart_16450, c_uart_16550, c_uart_16550A};
-static const char *uart_str[] =
- { "unknown", "8250", "16450", "16550", "16550A" };
+ c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] = {
+ "unknown", "8250", "16450", "16550", "16550A"
+};
static enum uart ser12_check_uart(unsigned int iobase)
{
@@ -568,6 +755,30 @@ static int ser12_open(struct device *dev)
"baycom_ser12", dev))
return -EBUSY;
request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12");
+#ifdef BAYCOM_ALT_SER12
+ bc->hdrv.par.bitrate = SER12_BAUD;
+ /*
+ * set the SIO to 6 Bits/character and 19600 baud, so that
+ * we get exactly (hopefully) one interrupt per radio symbol
+ */
+ outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */
+ outb(115200/8/SER12_BAUD, DLL(dev->base_addr));
+ outb(0, DLM(dev->base_addr));
+ outb(0x01, LCR(dev->base_addr)); /* word length = 6 */
+ /*
+ * enable transmitter empty interrupt and modem status interrupt
+ */
+ outb(0x0a, IER(dev->base_addr));
+ /*
+ * make sure the next interrupt is generated;
+ * 0 must be used to power the modem; the modem draws its
+ * power from the TxD line
+ */
+ outb(0x00, THR(dev->base_addr));
+ printk(KERN_INFO "%s: ser12(alt modem) at iobase 0x%lx irq %u options "
+ "0x%x uart %s\n", bc_drvname, dev->base_addr, dev->irq,
+ bc->options, uart_str[u]);
+#else /* BAYCOM_ALT_SER12 */
/*
* enable transmitter empty interrupt
*/
@@ -581,6 +792,7 @@ static int ser12_open(struct device *dev)
printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u options "
"0x%x uart %s\n", bc_drvname, dev->base_addr, dev->irq,
bc->options, uart_str[u]);
+#endif /* BAYCOM_ALT_SER12 */
MOD_INC_USE_COUNT;
return 0;
}
diff --git a/drivers/net/com20020.c b/drivers/net/com20020.c
new file mode 100644
index 000000000..1fb3a0836
--- /dev/null
+++ b/drivers/net/com20020.c
@@ -0,0 +1,1097 @@
+/* $Id: com20020.c,v 1.2 1997/09/05 08:57:50 mj Exp $
+
+ Written 1997 by David Woodhouse <dwmw2@cam.ac.uk>
+
+ Derived from the original arcnet.c,
+ Written 1994-1996 by Avery Pennarun,
+ which was in turn derived from skeleton.c by Donald Becker.
+
+ Contact Avery at: apenwarr@bond.net or
+ RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
+
+ **********************
+
+ The original copyright of skeleton.c was as follows:
+
+ skeleton.c Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may only be used
+ and distributed according to the terms of the GNU Public License as
+ modified by SRC, incorporated herein by reference.
+
+ **********************
+
+ For more details, see drivers/net/arcnet.c
+
+ **********************
+*/
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/if_arcnet.h>
+#include <linux/arcdevice.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <net/arp.h>
+
+
+/* Internal function declarations */
+
+static int arc20020_probe(struct device *dev);
+static void arc20020_rx(struct device *dev,int recbuf);
+static int arc20020_found(struct device *dev,int ioaddr,int airq);
+static void arc20020_inthandler (struct device *dev);
+static int arc20020_reset (struct device *dev, int reset_delay);
+static void arc20020_setmask (struct device *dev, u_char mask);
+static void arc20020_command (struct device *dev, u_char command);
+static u_char arc20020_status (struct device *dev);
+static void arc20020_en_dis_able_TX (struct device *dev, int enable);
+static void arc20020_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+ char *data,int length,int daddr,int exceptA, int offset);
+static void arc20020_openclose(int open);
+static void arc20020_set_mc_list(struct device *dev);
+static u_char get_buffer_byte (struct device *dev, unsigned offset);
+static void put_buffer_byte (struct device *dev, unsigned offset, u_char datum);
+static void get_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest);
+static void put_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest);
+
+
+/* Module parameters */
+
+#ifdef MODULE
+static int node=0;
+static int io=0x0; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq=0; /* or use the insmod io= irq= shmem= options */
+static char *device; /* use eg. device="arc1" to change name */
+static int timeout=3;
+static int backplane=0;
+static int clock=0;
+
+MODULE_PARM(node,"i");
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(device, "s");
+MODULE_PARM(timeout,"i");
+MODULE_PARM(backplane,"i");
+MODULE_PARM(clock,"i");
+#else
+__initfunc(void com20020_setup (char *str, int *ints));
+extern struct device arcnet_devs[];
+extern char arcnet_dev_names[][10];
+extern int arcnet_num_devs;
+#endif
+
+
+/* Handy defines for ARCnet specific stuff */
+
+static char *clockrates[]={"2.5 Mb/s","1.25Mb/s","625 Kb/s","312.5 Kb/s",
+ "156.25 Kb/s", "Reserved", "Reserved",
+ "Reserved"};
+
+
+/* The number of low I/O ports used by the card. */
+#define ARCNET_TOTAL_SIZE 9
+
+#define _INTMASK (ioaddr+0) /* writable */
+#define _STATUS (ioaddr+0) /* readable */
+#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */
+#define _CONFIG (ioaddr+6) /* Configuration register */
+#define _DIAGSTAT (ioaddr+1) /* Diagnostic status register */
+#define _MEMDATA (ioaddr+4) /* Data port for IO-mapped memory */
+#define _ADDR_HI (ioaddr+2) /* Control registers for said */
+#define _ADDR_LO (ioaddr+3)
+
+#define RDDATAflag 0x80 /* Next access is a read/~write */
+#define NEWNXTIDflag 0x02 /* ID to which token is passed has changed */
+
+#define TXENflag 0x20 /* Enable TX (in CONFIG register) */
+
+#define PROMISCflag 0x10 /* Enable RCV_ALL (in SETUP register) */
+
+#define REGTENTID (lp->config &= ~3);
+#define REGNID (lp->config = (lp->config&~2)|1);
+#define REGSETUP (lp->config = (lp->config&~1)|2);
+#define REGNXTID (lp->config |= 3);
+
+#define ARCRESET { outb(lp->config | 0x80, _CONFIG); \
+ udelay(5); \
+ outb(lp->config , _CONFIG); \
+ }
+#define ARCRESET0 { outb(0x18 | 0x80, _CONFIG); \
+ udelay(5); \
+ outb(0x18 , _CONFIG); \
+ }
+
+#define ARCSTATUS inb(_STATUS)
+#define ACOMMAND(cmd) outb((cmd),_COMMAND)
+#define AINTMASK(msk) outb((msk),_INTMASK)
+#define SETCONF outb((lp->config),_CONFIG)
+
+
+/****************************************************************************
+ * *
+ * IO-mapped operation routines *
+ * *
+ ****************************************************************************/
+
+u_char get_buffer_byte (struct device *dev, unsigned offset)
+{
+ int ioaddr=dev->base_addr;
+
+ outb(offset >> 8 | RDDATAflag, _ADDR_HI);
+ outb(offset & 0xff, _ADDR_LO);
+
+ return inb(_MEMDATA);
+}
+
+void put_buffer_byte (struct device *dev, unsigned offset, u_char datum)
+{
+ int ioaddr=dev->base_addr;
+
+ outb(offset >> 8, _ADDR_HI);
+ outb(offset & 0xff, _ADDR_LO);
+
+ outb(datum, _MEMDATA);
+}
+
+
+#undef ONE_AT_A_TIME_TX
+#undef ONE_AT_A_TIME_RX
+
+void get_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest)
+{
+ int ioaddr=dev->base_addr;
+
+ outb( (offset >> 8) | AUTOINCflag | RDDATAflag, _ADDR_HI);
+ outb( offset & 0xff, _ADDR_LO);
+
+ while (length--)
+#ifdef ONE_AT_A_TIME_RX
+ *(dest++) = get_buffer_byte(dev,offset++);
+#else
+ *(dest++) = inb (_MEMDATA);
+#endif
+}
+
+void put_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest)
+{
+ int ioaddr=dev->base_addr;
+
+ outb( (offset >> 8) | AUTOINCflag, _ADDR_HI);
+ outb( offset & 0xff, _ADDR_LO);
+
+ while (length--)
+#ifdef ONE_AT_A_TIME_TX
+ put_buffer_byte(dev,offset++,*(dest++));
+#else
+ outb (*(dest++), _MEMDATA);
+#endif
+}
+
+
+static const char *version =
+ "com20020.c: v2.92 97/09/02 Avery Pennarun <apenwarr@bond.net> et al.\n";
+
+/****************************************************************************
+ * *
+ * Probe and initialization *
+ * *
+ ****************************************************************************/
+
+
+/* We cannot probe for an IO mapped card either, although we can check that
+ * it's where we were told it was, and even autoirq
+ */
+
+__initfunc(int arc20020_probe(struct device *dev))
+{
+ int ioaddr=dev->base_addr,status,delayval;
+ unsigned long airqmask;
+
+ BUGLVL(D_NORMAL) printk(version);
+
+ if (ioaddr<0x200)
+ {
+ BUGMSG(D_NORMAL,"No autoprobe for IO mapped cards; you "
+ "must specify the base address!\n");
+ return -ENODEV;
+ }
+
+ if (check_region(ioaddr, ARCNET_TOTAL_SIZE))
+ {
+ BUGMSG(D_NORMAL,"IO region %xh-%xh already allocated.\n",
+ ioaddr,ioaddr+ARCNET_TOTAL_SIZE-1);
+ return -ENXIO;
+ }
+
+ if (ARCSTATUS == 0xFF)
+ {
+ BUGMSG(D_NORMAL,"IO address %x empty\n",ioaddr);
+ return -ENODEV;
+ }
+
+ ARCRESET0;
+ JIFFER(RESETtime);
+
+ status=ARCSTATUS;
+
+ if ((status & 0x99)
+ != (NORXflag|TXFREEflag|RESETflag))
+ {
+ BUGMSG(D_NORMAL,"Status invalid (%Xh).\n",status);
+ return -ENODEV;
+ }
+
+ BUGMSG(D_INIT_REASONS,"Status after reset: %X\n",status);
+
+ /* Enable TX */
+ outb(0x39,_CONFIG);
+ outb(inb(ioaddr+8),ioaddr+7);
+
+ ACOMMAND(CFLAGScmd|RESETclear|CONFIGclear);
+
+ BUGMSG(D_INIT_REASONS,"Status after reset acknowledged: %X\n",status);
+
+ /* Reset card. */
+
+ outb(0x98,_CONFIG);
+ udelay(5);
+ outb(0x18,_CONFIG);
+
+ /* Read first loc'n of memory */
+
+ outb(0 | RDDATAflag | AUTOINCflag ,_ADDR_HI);
+ outb(0,_ADDR_LO);
+
+ if ((status=inb(_MEMDATA)) != 0xd1)
+ {
+ BUGMSG(D_NORMAL,"Signature byte not found.\n");
+ return -ENODEV;
+ }
+
+ if (!dev->irq)
+ {
+ /* if we do this, we're sure to get an IRQ since the
+ * card has just reset and the NORXflag is on until
+ * we tell it to start receiving.
+ */
+ BUGMSG(D_INIT_REASONS, "intmask was %d:\n",inb(_INTMASK));
+ outb(0, _INTMASK);
+ airqmask = probe_irq_on();
+ outb(NORXflag,_INTMASK);
+ udelay(1);
+ outb(0,_INTMASK);
+ dev->irq = probe_irq_off(airqmask);
+
+ if (dev->irq<=0)
+ {
+ BUGMSG(D_INIT_REASONS,"Autoprobe IRQ failed first time\n");
+ airqmask = probe_irq_on();
+ outb(NORXflag,_INTMASK);
+ udelay(5);
+ outb(0,_INTMASK);
+ dev->irq = probe_irq_off(airqmask);
+ if (dev->irq<=0)
+ {
+ BUGMSG(D_NORMAL,"Autoprobe IRQ failed.\n");
+ return -ENODEV;
+ }
+ }
+ }
+
+ return arc20020_found(dev,dev->base_addr,dev->irq);
+}
+
+
+/* Set up the struct device associated with this card. Called after
+ * probing succeeds.
+ */
+__initfunc(int arc20020_found(struct device *dev,int ioaddr,int airq))
+{
+ struct arcnet_local *lp;
+
+ /* reserve the irq */
+ if (request_irq(airq,&arcnet_interrupt,0,"arcnet (COM20020)",NULL))
+ {
+ BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
+ return -ENODEV;
+ }
+ irq2dev_map[airq]=dev;
+ dev->irq=airq;
+
+ /* reserve the I/O region - guaranteed to work by check_region */
+ request_region(ioaddr,ARCNET_TOTAL_SIZE,"arcnet (COM20020)");
+ dev->base_addr=ioaddr;
+
+ dev->mem_start=dev->mem_end=dev->rmem_start=dev->rmem_end=(long)NULL;
+
+ /* Initialize the rest of the device structure. */
+
+ dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ {
+ irq2dev_map[airq] = NULL;
+ free_irq(airq,NULL);
+ release_region(ioaddr,ARCNET_TOTAL_SIZE);
+ return -ENOMEM;
+ }
+
+ memset(dev->priv,0,sizeof(struct arcnet_local));
+ lp=(struct arcnet_local *)(dev->priv);
+ lp->card_type = ARC_20020;
+ lp->card_type_str = "COM 20020";
+
+ lp->arcnet_reset=arc20020_reset;
+ lp->asetmask=arc20020_setmask;
+ lp->astatus=arc20020_status;
+ lp->acommand=arc20020_command;
+ lp->en_dis_able_TX=arc20020_en_dis_able_TX;
+ lp->openclose_device=arc20020_openclose;
+ lp->prepare_tx=arc20020_prepare_tx;
+ lp->inthandler=arc20020_inthandler;
+
+ dev->set_multicast_list = arc20020_set_mc_list;
+
+ /* Fill in the fields of the device structure with generic
+ * values.
+ */
+ arcnet_setup(dev);
+
+ /* And now fill particular fields with arcnet values */
+ dev->mtu=1500; /* completely arbitrary - agrees with ether, though */
+ dev->hard_header_len=sizeof(struct ClientData);
+ lp->sequence=1;
+ lp->recbuf=0;
+
+ BUGMSG(D_DURING,"ClientData header size is %d.\n",
+ sizeof(struct ClientData));
+ BUGMSG(D_DURING,"HardHeader size is %d.\n",
+ sizeof(struct archdr));
+
+ /* get and check the station ID from offset 1 in shmem */
+ lp->timeout = dev->dev_addr[3] & 3; dev->dev_addr[3]=0;
+ lp->backplane =dev->dev_addr[1] & 1; dev->dev_addr[1]=0;
+ lp->setup = (dev->dev_addr[2] & 7) << 1; dev->dev_addr[2]=0;
+
+ if (dev->dev_addr[0])
+ lp->stationid=dev->dev_addr[0];
+ else
+ lp->stationid=inb(ioaddr+8); /* FIX ME - We should check that
+ this is valid before using it */
+ lp->config = 0x21 | (lp->timeout << 3) | (lp->backplane << 2);
+ /* Default 0x38 + register: Node ID */
+ SETCONF;
+ outb(lp->stationid, ioaddr+7);
+
+ REGSETUP;
+ SETCONF;
+ outb(lp->setup, ioaddr+7);
+
+ if (!lp->stationid)
+ BUGMSG(D_NORMAL,"WARNING! Station address 00 is reserved "
+ "for broadcasts!\n");
+ else if (lp->stationid==255)
+ BUGMSG(D_NORMAL,"WARNING! Station address FF may confuse "
+ "DOS networking programs!\n");
+ dev->dev_addr[0]=lp->stationid;
+
+ BUGMSG(D_NORMAL,"ARCnet COM20020: station %02Xh found at %03lXh, IRQ %d.\n",
+ lp->stationid, dev->base_addr,dev->irq);
+
+ if (lp->backplane)
+ BUGMSG (D_NORMAL, "Using backplane mode.\n");
+
+ if (lp->timeout != 3)
+ BUGMSG (D_NORMAL, "Using Extended Timeout value of %d.\n",lp->timeout);
+ if (lp->setup)
+ {
+ BUGMSG (D_NORMAL, "Using CKP %d - Data rate %s.\n",
+ lp->setup >>1,clockrates[lp->setup >> 1] );
+ }
+ return 0;
+}
+
+
+/****************************************************************************
+ * *
+ * Utility routines *
+ * *
+ ****************************************************************************/
+
+/* Do a hardware reset on the card, and set up necessary registers.
+ *
+ * This should be called as little as possible, because it disrupts the
+ * token on the network (causes a RECON) and requires a significant delay.
+ *
+ * However, it does make sure the card is in a defined state.
+ */
+int arc20020_reset(struct device *dev,int reset_delay)
+{
+ struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+ short ioaddr=dev->base_addr;
+ int delayval,recbuf=lp->recbuf;
+
+ if (reset_delay==3)
+ {
+ ARCRESET;
+ return 0;
+ }
+
+ /* no IRQ's, please! */
+ lp->intmask=0;
+ SETMASK;
+
+ BUGMSG(D_INIT,"Resetting %s (status=%Xh)\n",
+ dev->name,ARCSTATUS);
+
+ lp->config = 0x20 | (lp->timeout<<3) | (lp->backplane<<2);
+ /* power-up defaults */
+ SETCONF;
+
+ if (reset_delay)
+ {
+ /* reset the card */
+ ARCRESET;
+ JIFFER(RESETtime);
+ }
+
+ ACOMMAND(CFLAGScmd|RESETclear); /* clear flags & end reset */
+ ACOMMAND(CFLAGScmd|CONFIGclear);
+
+ /* verify that the ARCnet signature byte is present */
+
+ if (get_buffer_byte(dev,0) != TESTvalue)
+ {
+ BUGMSG(D_NORMAL,"reset failed: TESTvalue not present.\n");
+ return 1;
+ }
+
+ /* clear out status variables */
+ recbuf=lp->recbuf=0;
+ lp->txbuf=2;
+
+ /* enable extended (512-byte) packets */
+ ACOMMAND(CONFIGcmd|EXTconf);
+
+ /* and enable receive of our first packet to the first buffer */
+ EnableReceiver();
+
+ /* re-enable interrupts */
+ lp->intmask|=NORXflag;
+#ifdef DETECT_RECONFIGS
+ lp->intmask|=RECONflag;
+#endif
+ SETMASK;
+
+ /* done! return success. */
+ return 0;
+}
+
+
+/* Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ * num_addrs == 0 Normal mode, clear multicast list
+ * num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ * best-effort filtering.
+ * FIX ME - do multicast stuff, not just promiscuous.
+ */
+static void
+arc20020_set_mc_list(struct device *dev)
+{
+ struct arcnet_local *lp=dev->priv;
+ int ioaddr=dev->base_addr;
+
+ if ((dev->flags & IFF_PROMISC) && (dev->flags & IFF_UP))
+ { /* Enable promiscuous mode */
+ if (!(lp->setup & PROMISCflag))
+ BUGMSG(D_NORMAL, "Setting promiscuous flag...\n");
+ REGSETUP;
+ SETCONF;
+ lp->setup|=PROMISCflag;
+ outb(lp->setup,ioaddr+7);
+ } else
+ /* Disable promiscuous mode, use normal mode */
+ {
+ if ((lp->setup & PROMISCflag))
+ BUGMSG(D_NORMAL, "Resetting promiscuous flag...\n");
+ REGSETUP;
+ SETCONF;
+ lp->setup &= ~PROMISCflag;
+ outb(lp->setup,ioaddr+7);
+ }
+}
+
+
+static void arc20020_openclose(int open)
+{
+ if (open)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+
+
+static void arc20020_en_dis_able_TX(struct device *dev, int enable)
+{
+ struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+ int ioaddr=dev->base_addr;
+
+ lp->config=enable?(lp->config | TXENflag):(lp->config & ~TXENflag);
+ SETCONF;
+}
+
+
+static void arc20020_setmask(struct device *dev, u_char mask)
+{
+ short ioaddr=dev->base_addr;
+
+ AINTMASK(mask);
+}
+
+
+static u_char arc20020_status(struct device *dev)
+{
+ short ioaddr=dev->base_addr;
+
+ return ARCSTATUS;
+}
+
+
+static void arc20020_command(struct device *dev, u_char cmd)
+{
+ short ioaddr=dev->base_addr;
+
+ ACOMMAND(cmd);
+}
+
+
+/* The actual interrupt handler routine - handle various IRQ's generated
+ * by the card.
+ */
+static void
+arc20020_inthandler(struct device *dev)
+{
+ struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+ int ioaddr=dev->base_addr, status, boguscount = 3, didsomething,
+ dstatus;
+
+ AINTMASK(0);
+
+ BUGMSG(D_DURING,"in arc20020_inthandler (status=%Xh, intmask=%Xh)\n",
+ ARCSTATUS,lp->intmask);
+
+ do
+ {
+ status = ARCSTATUS;
+ didsomething=0;
+
+
+ /* RESET flag was enabled - card is resetting and if RX
+ * is disabled, it's NOT because we just got a packet.
+ */
+ if (status & RESETflag)
+ {
+ BUGMSG(D_NORMAL,"spurious reset (status=%Xh)\n",
+ status);
+ arc20020_reset(dev,0);
+
+ /* all other flag values are just garbage */
+ break;
+ }
+
+ /* RX is inhibited - we must have received something. */
+ if (status & lp->intmask & NORXflag)
+ {
+ int recbuf=lp->recbuf=!lp->recbuf;
+ int oldaddr=0;
+
+ BUGMSG(D_DURING,"receive irq (status=%Xh)\n",
+ status);
+
+ /* enable receive of our next packet */
+ EnableReceiver();
+
+ if (lp->intx)
+ oldaddr=(inb(_ADDR_HI)<<8) | inb(_ADDR_LO);
+
+ /* Got a packet. */
+ arc20020_rx(dev,!recbuf);
+
+ if (lp->intx)
+ {
+ outb( (oldaddr >> 8), _ADDR_HI);
+ outb( oldaddr & 0xff, _ADDR_LO);
+ }
+
+ didsomething++;
+ }
+
+ /* it can only be an xmit-done irq if we're xmitting :) */
+ /*if (status&TXFREEflag && !lp->in_txhandler && lp->sending)*/
+ if (status & lp->intmask & TXFREEflag)
+ {
+ struct Outgoing *out=&(lp->outgoing);
+ int was_sending=lp->sending;
+
+ lp->intmask &= ~TXFREEflag;
+
+ lp->in_txhandler++;
+ if (was_sending) lp->sending--;
+
+ BUGMSG(D_DURING,"TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n",
+ status,out->numsegs,out->segnum,out->skb);
+
+ if (was_sending && !(status&TXACKflag))
+ {
+ if (lp->lasttrans_dest != 0)
+ {
+ BUGMSG(D_EXTRA,"transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n",
+ status,lp->lasttrans_dest);
+ lp->stats.tx_errors++;
+ lp->stats.tx_carrier_errors++;
+ }
+ else
+ {
+ BUGMSG(D_DURING,"broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n",
+ status,
+ lp->lasttrans_dest);
+ }
+ }
+
+ /* send packet if there is one */
+ arcnet_go_tx(dev,0);
+ didsomething++;
+
+ if (lp->intx)
+ {
+ BUGMSG(D_DURING,"TXDONE while intx! (status=%Xh, intx=%d)\n",
+ ARCSTATUS,lp->intx);
+ lp->in_txhandler--;
+ continue;
+ }
+
+ if (!lp->outgoing.skb)
+ {
+ BUGMSG(D_DURING,"TX IRQ done: no split to continue.\n");
+
+ /* inform upper layers */
+ if (!lp->txready) arcnet_tx_done(dev, lp);
+ lp->in_txhandler--;
+ continue;
+ }
+
+ /* if more than one segment, and not all segments
+ * are done, then continue xmit.
+ */
+ if (out->segnum<out->numsegs)
+ arcnetA_continue_tx(dev);
+ arcnet_go_tx(dev,0);
+
+ /* if segnum==numsegs, the transmission is finished;
+ * free the skb.
+ */
+ if (out->segnum>=out->numsegs)
+ {
+ /* transmit completed */
+ out->segnum++;
+ if (out->skb)
+ {
+ lp->stats.tx_bytes += out->skb->len;
+ dev_kfree_skb(out->skb,FREE_WRITE);
+ }
+ out->skb=NULL;
+
+ /* inform upper layers */
+ if (!lp->txready) arcnet_tx_done(dev, lp);
+ }
+ didsomething++;
+
+ lp->in_txhandler--;
+ }
+ else if (lp->txready && !lp->sending && !lp->intx)
+ {
+ BUGMSG(D_NORMAL,"recovery from silent TX (status=%Xh)\n",
+ status);
+ arcnet_go_tx(dev,0);
+ didsomething++;
+ }
+
+ if ((dstatus=inb(_DIAGSTAT)) & NEWNXTIDflag)
+ {
+ REGNXTID;
+ SETCONF;
+ BUGMSG(D_EXTRA,"New NextID detected: %X\n",inb(ioaddr+7));
+ }
+
+
+#ifdef DETECT_RECONFIGS
+ if (status & (lp->intmask) & RECONflag)
+ {
+ ACOMMAND(CFLAGScmd|CONFIGclear);
+ lp->stats.tx_carrier_errors++;
+
+#ifdef SHOW_RECONFIGS
+ BUGMSG(D_NORMAL,"Network reconfiguration detected (status=%Xh, diag status=%Xh, config=%X)\n",
+ status,dstatus,lp->config);
+#endif /* SHOW_RECONFIGS */
+
+#ifdef RECON_THRESHOLD
+ /* is the RECON info empty or old? */
+ if (!lp->first_recon || !lp->last_recon ||
+ jiffies-lp->last_recon > HZ*10)
+ {
+ if (lp->network_down)
+ BUGMSG(D_NORMAL,"reconfiguration detected: cabling restored?\n");
+ lp->first_recon=lp->last_recon=jiffies;
+ lp->num_recons=lp->network_down=0;
+
+ BUGMSG(D_DURING,"recon: clearing counters.\n");
+ }
+ else /* add to current RECON counter */
+ {
+ lp->last_recon=jiffies;
+ lp->num_recons++;
+
+ BUGMSG(D_DURING,"recon: counter=%d, time=%lds, net=%d\n",
+ lp->num_recons,
+ (lp->last_recon-lp->first_recon)/HZ,
+ lp->network_down);
+
+ /* if network is marked up;
+ * and first_recon and last_recon are 60+ sec
+ * apart;
+ * and the average no. of recons counted is
+ * > RECON_THRESHOLD/min;
+ * then print a warning message.
+ */
+ if (!lp->network_down
+ && (lp->last_recon-lp->first_recon)<=HZ*60
+ && lp->num_recons >= RECON_THRESHOLD)
+ {
+ lp->network_down=1;
+ BUGMSG(D_NORMAL,"many reconfigurations detected: cabling problem?\n");
+ }
+ else if (!lp->network_down
+ && lp->last_recon-lp->first_recon > HZ*60)
+ {
+ /* reset counters if we've gone for
+ * over a minute.
+ */
+ lp->first_recon=lp->last_recon;
+ lp->num_recons=1;
+ }
+ }
+ }
+ else if (lp->network_down && jiffies-lp->last_recon > HZ*10)
+ {
+ if (lp->network_down)
+ BUGMSG(D_NORMAL,"cabling restored?\n");
+ lp->first_recon=lp->last_recon=0;
+ lp->num_recons=lp->network_down=0;
+
+ BUGMSG(D_DURING,"not recon: clearing counters anyway.\n");
+#endif
+ }
+#endif /* DETECT_RECONFIGS */
+ } while (--boguscount && didsomething);
+
+ BUGMSG(D_DURING,"net_interrupt complete (status=%Xh, count=%d)\n",
+ ARCSTATUS,boguscount);
+ BUGMSG(D_DURING,"\n");
+
+ SETMASK; /* put back interrupt mask */
+
+}
+
+
+/* A packet has arrived; grab it from the buffers and pass it to the generic
+ * arcnet_rx routing to deal with it.
+ */
+
+static void
+arc20020_rx(struct device *dev,int recbuf)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int ioaddr=dev->base_addr;
+ union ArcPacket packetbuf;
+ union ArcPacket *arcpacket=&packetbuf;
+ u_char *arcsoft;
+ short length,offset;
+ u_char daddr,saddr;
+
+ lp->stats.rx_packets++;
+
+ get_whole_buffer(dev,recbuf*512,4,(char *)arcpacket);
+
+ saddr=arcpacket->hardheader.source;
+
+ /* if source is 0, it's a "used" packet! */
+ if (saddr==0)
+ {
+ BUGMSG(D_NORMAL,"discarding old packet. (status=%Xh)\n",
+ ARCSTATUS);
+ lp->stats.rx_errors++;
+ return;
+ }
+ /* Set source address to zero to mark it as old */
+
+ put_buffer_byte(dev,recbuf*512,0);
+
+ arcpacket->hardheader.source=0;
+
+ daddr=arcpacket->hardheader.destination;
+
+ if (arcpacket->hardheader.offset1) /* Normal Packet */
+ {
+ offset=arcpacket->hardheader.offset1;
+ arcsoft=&arcpacket->raw[offset];
+ length=256-offset;
+ }
+ else /* ExtendedPacket or ExceptionPacket */
+ {
+ offset=arcpacket->hardheader.offset2;
+ arcsoft=&arcpacket->raw[offset];
+ length=512-offset;
+ }
+
+ get_whole_buffer(dev,recbuf*512+offset,length,(char *)arcpacket+offset);
+
+ arcnet_rx(lp, arcsoft, length, saddr, daddr);
+
+ BUGLVL(D_RX) arcnet_dump_packet(lp->adev,arcpacket->raw,length>240,"rx");
+}
+
+
+/* Given an skb, copy a packet into the ARCnet buffers for later transmission
+ * by arcnet_go_tx.
+ */
+static void
+arc20020_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+ char *data,int length,int daddr,int exceptA, int offset)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+
+ lp->txbuf=lp->txbuf^1; /* XOR with 1 to alternate between 2 and 3 */
+
+ length+=hdrlen;
+
+ BUGMSG(D_TX,"arcnetAS_prep_tx: hdr:%ph, length:%d, data:%ph\n",
+ hdr,length,data);
+
+ put_buffer_byte(dev, lp->txbuf*512+1, daddr);
+
+ /* load packet into shared memory */
+ if (length<=MTU) /* Normal (256-byte) Packet */
+ put_buffer_byte(dev, lp->txbuf*512+2, offset=offset?offset:256-length);
+
+ else if (length>=MinTU || offset) /* Extended (512-byte) Packet */
+ {
+ put_buffer_byte(dev, lp->txbuf*512+2, 0);
+ put_buffer_byte(dev, lp->txbuf*512+3, offset=offset?offset:512-length);
+ }
+ else if (exceptA) /* RFC1201 Exception Packet */
+ {
+ put_buffer_byte(dev, lp->txbuf*512+2, 0);
+ put_buffer_byte(dev, lp->txbuf*512+3, offset=512-length-4);
+
+ /* exception-specific stuff - these four bytes
+ * make the packet long enough to fit in a 512-byte
+ * frame.
+ */
+
+ put_whole_buffer(dev, lp->txbuf*512+offset,4,"\0\0xff\0xff\0xff");
+ offset+=4;
+ }
+ else /* "other" Exception packet */
+ {
+ /* RFC1051 - set 4 trailing bytes to 0 */
+
+ put_whole_buffer(dev,lp->txbuf*512+508,4,"\0\0\0\0");
+
+ /* now round up to MinTU */
+ put_buffer_byte(dev, lp->txbuf*512+2, 0);
+ put_buffer_byte(dev, lp->txbuf*512+3, offset=512-MinTU);
+ }
+
+ /* copy the packet into ARCnet shmem
+ * - the first bytes of ClientData header are skipped
+ */
+
+ put_whole_buffer(dev, 512*lp->txbuf+offset, hdrlen,(u_char *)hdr);
+ put_whole_buffer(dev, 512*lp->txbuf+offset+hdrlen,length-hdrlen,data);
+
+ BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
+ daddr,length);
+
+ lp->lastload_dest=daddr;
+ lp->txready=lp->txbuf; /* packet is ready for sending */
+}
+
+
+/****************************************************************************
+ * *
+ * Kernel Loadable Module Support *
+ * *
+ ****************************************************************************/
+
+
+#ifdef MODULE
+
+static struct device *cards[16]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
+
+
+int init_module(void)
+{
+ struct device *dev;
+
+ cards[0]=dev=(struct device *)kmalloc(sizeof(struct device), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ memset(dev, 0, sizeof(struct device));
+
+ dev->name=(char *)kmalloc(9, GFP_KERNEL);
+ if (!dev->name)
+ {
+ kfree(dev);
+ return -ENOMEM;
+ }
+ dev->init=arc20020_probe;
+
+ if (device)
+ strcpy(dev->name,device);
+ else arcnet_makename(dev->name);
+
+ if (node && node != 0xff)
+ dev->dev_addr[0]=node;
+
+ if (backplane) dev->dev_addr[1]=backplane?1:0;
+ if (clock) dev->dev_addr[2]=clock&7;
+ dev->dev_addr[3]=timeout&3;
+
+ dev->base_addr=io;
+ dev->irq=irq;
+
+ if (dev->irq==2) dev->irq=9;
+
+ if (register_netdev(dev) != 0)
+ return -EIO;
+
+ /* Increase use count of arcnet.o */
+ arcnet_use_count(1);
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ struct device *dev=cards[0];
+ int ioaddr=dev->base_addr;
+
+ if (dev->start) (*dev->stop)(dev);
+
+ /* Flush TX and disable RX */
+ if (ioaddr)
+ {
+ AINTMASK(0); /* disable IRQ's */
+ ACOMMAND(NOTXcmd); /* stop transmit */
+ ACOMMAND(NORXcmd); /* disable receive */
+ }
+
+ if (dev->irq)
+ {
+ irq2dev_map[dev->irq] = NULL;
+ free_irq(dev->irq,NULL);
+ }
+
+ if (dev->base_addr) release_region(dev->base_addr,ARCNET_TOTAL_SIZE);
+ unregister_netdev(dev);
+ kfree(dev->priv);
+ dev->priv = NULL;
+
+ /* Decrease use count of arcnet.o */
+ arcnet_use_count(0);
+}
+
+#else
+
+__initfunc(void com20020_setup (char *str, int *ints))
+{
+ struct device *dev;
+
+ if (arcnet_num_devs == MAX_ARCNET_DEVS)
+ {
+ printk("com20020: Too many ARCnet devices registered (max %d).\n",
+ MAX_ARCNET_DEVS);
+ return;
+ }
+
+ dev=&arcnet_devs[arcnet_num_devs];
+
+ if (ints[0] < 1)
+ {
+ printk("com20020: You must give an IO address.\n");
+ return;
+ }
+
+ dev->dev_addr[3]=3;
+ dev->init=arc20020_probe;
+
+ switch(ints[0])
+ {
+ case 7: /* ERROR */
+ printk("com20020: Too many arguments.\n");
+
+ case 6: /* Timeout */
+ dev->dev_addr[3]=(u_char)ints[6];
+
+ case 5: /* CKP value */
+ dev->dev_addr[2]=(u_char)ints[5];
+
+ case 4: /* Backplane flag */
+ dev->dev_addr[1]=(u_char)ints[4];
+
+ case 3: /* Node ID */
+ dev->dev_addr[0]=(u_char)ints[3];
+
+ case 2: /* IRQ */
+ dev->irq=ints[2];
+
+ case 1: /* IO address */
+ dev->base_addr=ints[1];
+ }
+
+ dev->name = (char *)&arcnet_dev_names[arcnet_num_devs];
+
+ if (str)
+ strncpy(dev->name, str, 9);
+
+ arcnet_num_devs++;
+}
+#endif /* MODULE */
diff --git a/drivers/net/com90io.c b/drivers/net/com90io.c
new file mode 100644
index 000000000..9bd902151
--- /dev/null
+++ b/drivers/net/com90io.c
@@ -0,0 +1,964 @@
+/* $Id: com90io.c,v 1.2 1997/09/05 08:57:52 mj Exp $
+
+ Written 1997 by David Woodhouse <dwmw2@cam.ac.uk>
+
+ Derived from the original arcnet.c,
+ Written 1994-1996 by Avery Pennarun,
+ which was in turn derived from skeleton.c by Donald Becker.
+
+ Contact Avery at: apenwarr@bond.net or
+ RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
+
+ **********************
+
+ The original copyright of skeleton.c was as follows:
+
+ skeleton.c Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may only be used
+ and distributed according to the terms of the GNU Public License as
+ modified by SRC, incorporated herein by reference.
+
+ **********************
+
+ For more details, see drivers/net/arcnet.c
+
+ **********************
+*/
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/if_arcnet.h>
+#include <linux/arcdevice.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <net/arp.h>
+
+
+/* Internal function declarations */
+
+static int arc90io_probe(struct device *dev);
+static void arc90io_rx(struct device *dev,int recbuf);
+static int arc90io_found(struct device *dev,int ioaddr,int airq);
+static void arc90io_inthandler (struct device *dev);
+static int arc90io_reset (struct device *dev, int reset_delay);
+static void arc90io_setmask (struct device *dev, u_char mask);
+static void arc90io_command (struct device *dev, u_char command);
+static u_char arc90io_status (struct device *dev);
+static void arc90io_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+ char *data,int length,int daddr,int exceptA, int offset);
+static void arc90io_openclose(int open);
+
+static u_char get_buffer_byte (struct device *dev, unsigned offset);
+static void put_buffer_byte (struct device *dev, unsigned offset, u_char datum);
+static void get_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest);
+static void put_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest);
+
+
+/* Module parameters */
+
+#ifdef MODULE
+static int io=0x0; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq=0; /* or use the insmod io= irq= shmem= options */
+static char *device; /* use eg. device="arc1" to change name */
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(device, "s");
+#else
+__initfunc(void com90io_setup (char *str, int *ints));
+extern struct device arcnet_devs[];
+extern char arcnet_dev_names[][10];
+extern int arcnet_num_devs;
+#endif
+
+
+/* Handy defines for ARCnet specific stuff */
+
+/* The number of low I/O ports used by the card. */
+#define ARCNET_TOTAL_SIZE 16
+
+/* COM 9026 controller chip --> ARCnet register addresses */
+#define _INTMASK (ioaddr+0) /* writable */
+#define _STATUS (ioaddr+0) /* readable */
+#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */
+#define _RESET (ioaddr+8) /* software reset (on read) */
+#define _MEMDATA (ioaddr+12) /* Data port for IO-mapped memory */
+#define _ADDR_HI (ioaddr+15) /* Control registers for said */
+#define _ADDR_LO (ioaddr+14)
+#define _CONFIG (ioaddr+2) /* Configuration register */
+
+#define ARCSTATUS inb(_STATUS)
+#define ACOMMAND(cmd) outb((cmd),_COMMAND)
+#define AINTMASK(msk) outb((msk),_INTMASK)
+
+#define ARCRESET inb(_RESET)
+
+#define SETCONF outb((lp->config),_CONFIG)
+
+
+/****************************************************************************
+ * *
+ * IO-mapped operation routines *
+ * *
+ ****************************************************************************/
+
+u_char get_buffer_byte (struct device *dev, unsigned offset)
+{
+ int ioaddr=dev->base_addr;
+
+ outb(offset >> 8, _ADDR_HI);
+ outb(offset & 0xff, _ADDR_LO);
+
+ return inb(_MEMDATA);
+}
+
+void put_buffer_byte (struct device *dev, unsigned offset, u_char datum)
+{
+ int ioaddr=dev->base_addr;
+
+ outb(offset >> 8, _ADDR_HI);
+ outb(offset & 0xff, _ADDR_LO);
+
+ outb(datum, _MEMDATA);
+}
+
+
+#undef ONE_AT_A_TIME_TX
+#undef ONE_AT_A_TIME_RX
+
+void get_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest)
+{
+ int ioaddr=dev->base_addr;
+
+ outb( (offset >> 8) | AUTOINCflag, _ADDR_HI);
+ outb( offset & 0xff, _ADDR_LO);
+
+ while (length--)
+#ifdef ONE_AT_A_TIME_RX
+ *(dest++) = get_buffer_byte(dev,offset++);
+#else
+ *(dest++) = inb (_MEMDATA);
+#endif
+}
+
+void put_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest)
+{
+ int ioaddr=dev->base_addr;
+
+ outb( (offset >> 8) | AUTOINCflag, _ADDR_HI);
+ outb( offset & 0xff, _ADDR_LO);
+
+ while (length--)
+#ifdef ONE_AT_A_TIME_TX
+ put_buffer_byte(dev,offset++,*(dest++));
+#else
+ outb (*(dest++), _MEMDATA);
+#endif
+}
+
+
+static const char *version =
+ "com90io.c: v2.91 97/08/19 Avery Pennarun <apenwarr@bond.net> et al.\n";
+
+
+/****************************************************************************
+ * *
+ * Probe and initialization *
+ * *
+ ****************************************************************************/
+
+/* We cannot probe for an IO mapped card either, although we can check that
+ * it's where we were told it was, and even autoirq
+ */
+
+__initfunc(int arc90io_probe(struct device *dev))
+{
+ int ioaddr=dev->base_addr,status,delayval;
+ unsigned long airqmask;
+
+ BUGLVL(D_NORMAL) printk(version);
+
+ if (ioaddr<0x200)
+ {
+ BUGMSG(D_NORMAL,"No autoprobe for IO mapped cards; you "
+ "must specify the base address!\n");
+ return -ENODEV;
+ }
+
+ if (check_region(ioaddr, ARCNET_TOTAL_SIZE))
+ {
+ BUGMSG(D_INIT_REASONS,"IO check_region %x-%x failed.\n",
+ ioaddr,ioaddr+ARCNET_TOTAL_SIZE-1);
+ return -ENXIO;
+ }
+
+ if (ARCSTATUS == 0xFF)
+ {
+ BUGMSG(D_INIT_REASONS,"IO address %x empty\n",ioaddr);
+ return -ENODEV;
+ }
+
+ ARCRESET;
+ JIFFER(RESETtime);
+
+ status=ARCSTATUS;
+
+ if ((status & 0x9D)
+ != (NORXflag|RECONflag|TXFREEflag|RESETflag))
+ {
+ BUGMSG(D_INIT_REASONS,"Status invalid (%Xh).\n",status);
+ return -ENODEV;
+ }
+
+ BUGMSG(D_INIT_REASONS,"Status after reset: %X\n",status);
+
+ ACOMMAND(CFLAGScmd|RESETclear|CONFIGclear);
+
+ BUGMSG(D_INIT_REASONS,"Status after reset acknowledged: %X\n",status);
+
+ status=ARCSTATUS;
+
+ if (status & RESETflag)
+ {
+ BUGMSG(D_INIT_REASONS,"Eternal reset (status=%Xh)\n",status);
+ return -ENODEV;
+ }
+
+ outb((0x16 | IOMAPflag) &~ENABLE16flag, _CONFIG);
+
+ /* Read first loc'n of memory */
+
+ outb(AUTOINCflag ,_ADDR_HI);
+ outb(0,_ADDR_LO);
+
+ if ((status=inb(_MEMDATA)) != 0xd1)
+ {
+ BUGMSG(D_INIT_REASONS,"Signature byte not found"
+ " (%Xh instead).\n", status);
+ return -ENODEV;
+ }
+
+ if (!dev->irq)
+ {
+ /* if we do this, we're sure to get an IRQ since the
+ * card has just reset and the NORXflag is on until
+ * we tell it to start receiving.
+ */
+
+ airqmask = probe_irq_on();
+ outb(NORXflag,_INTMASK);
+ udelay(1);
+ outb(0,_INTMASK);
+ dev->irq = probe_irq_off(airqmask);
+
+ if (dev->irq<=0)
+ {
+ BUGMSG(D_INIT_REASONS,"Autoprobe IRQ failed\n");
+ return -ENODEV;
+ }
+ }
+
+ return arc90io_found(dev,dev->base_addr,dev->irq);
+}
+
+
+/* Set up the struct device associated with this card. Called after
+ * probing succeeds.
+ */
+__initfunc(int arc90io_found(struct device *dev,int ioaddr,int airq))
+{
+ struct arcnet_local *lp;
+
+ /* reserve the irq */
+ if (request_irq(airq,&arcnet_interrupt,0,"arcnet (COM90xx-IO)",NULL))
+ {
+ BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
+ return -ENODEV;
+ }
+ irq2dev_map[airq]=dev;
+ dev->irq=airq;
+
+ /* reserve the I/O region - guaranteed to work by check_region */
+ request_region(ioaddr,ARCNET_TOTAL_SIZE,"arcnet (COM90xx-IO)");
+ dev->base_addr=ioaddr;
+
+ dev->mem_start=dev->mem_end=dev->rmem_start=dev->rmem_end=(long)NULL;
+
+ /* Initialize the rest of the device structure. */
+
+ dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ {
+ irq2dev_map[airq] = NULL;
+ free_irq(airq,NULL);
+ release_region(ioaddr,ARCNET_TOTAL_SIZE);
+ return -ENOMEM;
+ }
+
+ memset(dev->priv,0,sizeof(struct arcnet_local));
+ lp=(struct arcnet_local *)(dev->priv);
+ lp->card_type = ARC_90xx_IO;
+ lp->card_type_str = "COM 90xx (IO)";
+
+ lp->arcnet_reset=arc90io_reset;
+ lp->asetmask=arc90io_setmask;
+ lp->astatus=arc90io_status;
+ lp->acommand=arc90io_command;
+ lp->openclose_device=arc90io_openclose;
+ lp->prepare_tx=arc90io_prepare_tx;
+ lp->inthandler=arc90io_inthandler;
+
+ /* Fill in the fields of the device structure with generic
+ * values.
+ */
+ arcnet_setup(dev);
+
+ /* And now fill particular fields with arcnet values */
+ dev->mtu=1500; /* completely arbitrary - agrees with ether, though */
+ dev->hard_header_len=sizeof(struct ClientData);
+ lp->sequence=1;
+ lp->recbuf=0;
+
+ BUGMSG(D_DURING,"ClientData header size is %d.\n",
+ sizeof(struct ClientData));
+ BUGMSG(D_DURING,"HardHeader size is %d.\n",
+ sizeof(struct archdr));
+
+ lp->config = (0x16 | IOMAPflag) & ~ENABLE16flag;
+ SETCONF;
+
+ /* get and check the station ID from offset 1 in shmem */
+
+ lp->stationid = get_buffer_byte(dev,1);
+
+ if (!lp->stationid)
+ BUGMSG(D_NORMAL,"WARNING! Station address 00 is reserved "
+ "for broadcasts!\n");
+ else if (lp->stationid==255)
+ BUGMSG(D_NORMAL,"WARNING! Station address FF may confuse "
+ "DOS networking programs!\n");
+ dev->dev_addr[0]=lp->stationid;
+
+ BUGMSG(D_NORMAL,"ARCnet COM90xx in IO-mapped mode: "
+ "station %02Xh found at %03lXh, IRQ %d.\n",
+ lp->stationid,
+ dev->base_addr,dev->irq);
+
+ return 0;
+}
+
+
+/****************************************************************************
+ * *
+ * Utility routines *
+ * *
+ ****************************************************************************/
+
+/* Do a hardware reset on the card, and set up necessary registers.
+ *
+ * This should be called as little as possible, because it disrupts the
+ * token on the network (causes a RECON) and requires a significant delay.
+ *
+ * However, it does make sure the card is in a defined state.
+ */
+int arc90io_reset(struct device *dev,int reset_delay)
+{
+ struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+ short ioaddr=dev->base_addr;
+ int delayval,recbuf=lp->recbuf;
+
+ if (reset_delay==3)
+ {
+ ARCRESET;
+ return 0;
+ }
+
+ /* no IRQ's, please! */
+ lp->intmask=0;
+ SETMASK;
+
+ BUGMSG(D_INIT,"Resetting %s (status=%Xh)\n",
+ dev->name,ARCSTATUS);
+
+ /* Set the thing to IO-mapped, 8-bit mode */
+ lp->config = (0x1C|IOMAPflag) & ~ENABLE16flag;
+ SETCONF;
+
+ if (reset_delay)
+ {
+ /* reset the card */
+ ARCRESET;
+ JIFFER(RESETtime);
+ }
+
+ ACOMMAND(CFLAGScmd|RESETclear); /* clear flags & end reset */
+ ACOMMAND(CFLAGScmd|CONFIGclear);
+
+ /* verify that the ARCnet signature byte is present */
+
+ if (get_buffer_byte(dev,0) != TESTvalue)
+ {
+ BUGMSG(D_NORMAL,"reset failed: TESTvalue not present.\n");
+ return 1;
+ }
+
+ /* clear out status variables */
+ recbuf=lp->recbuf=0;
+ lp->txbuf=2;
+
+ /* enable extended (512-byte) packets */
+ ACOMMAND(CONFIGcmd|EXTconf);
+
+ /* and enable receive of our first packet to the first buffer */
+ EnableReceiver();
+
+ /* re-enable interrupts */
+ lp->intmask|=NORXflag;
+#ifdef DETECT_RECONFIGS
+ lp->intmask|=RECONflag;
+#endif
+ SETMASK;
+
+ /* done! return success. */
+ return 0;
+}
+
+
+static void arc90io_openclose(int open)
+{
+ if (open)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+
+
+static void arc90io_setmask(struct device *dev, u_char mask)
+{
+ short ioaddr=dev->base_addr;
+
+ AINTMASK(mask);
+}
+
+static u_char arc90io_status(struct device *dev)
+{
+ short ioaddr=dev->base_addr;
+
+ return ARCSTATUS;
+}
+
+static void arc90io_command(struct device *dev, u_char cmd)
+{
+ short ioaddr=dev->base_addr;
+
+ ACOMMAND(cmd);
+}
+
+
+/* The actual interrupt handler routine - handle various IRQ's generated
+ * by the card.
+ */
+static void
+arc90io_inthandler(struct device *dev)
+{
+ struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+ int ioaddr=dev->base_addr, status, boguscount = 3, didsomething;
+
+ AINTMASK(0);
+
+ BUGMSG(D_DURING,"in arc90io_inthandler (status=%Xh, intmask=%Xh)\n",
+ ARCSTATUS,lp->intmask);
+
+ do
+ {
+ status = ARCSTATUS;
+ didsomething=0;
+
+ /* RESET flag was enabled - card is resetting and if RX
+ * is disabled, it's NOT because we just got a packet.
+ */
+ if (status & RESETflag)
+ {
+ BUGMSG(D_NORMAL,"spurious reset (status=%Xh)\n",
+ status);
+ arc90io_reset(dev,0);
+
+ /* all other flag values are just garbage */
+ break;
+ }
+
+ /* RX is inhibited - we must have received something. */
+ if (status & lp->intmask & NORXflag)
+ {
+ int recbuf=lp->recbuf=!lp->recbuf;
+ int oldaddr=0;
+
+ BUGMSG(D_DURING,"receive irq (status=%Xh)\n",
+ status);
+
+ /* enable receive of our next packet */
+ EnableReceiver();
+
+ if (lp->intx)
+ oldaddr=(inb(_ADDR_HI)<<8) | inb(_ADDR_LO);
+
+
+ /* Got a packet. */
+ arc90io_rx(dev,!recbuf);
+
+
+ if (lp->intx)
+ {
+ outb( (oldaddr >> 8), _ADDR_HI);
+ outb( oldaddr & 0xff, _ADDR_LO);
+ }
+
+ didsomething++;
+ }
+
+ /* it can only be an xmit-done irq if we're xmitting :) */
+ /*if (status&TXFREEflag && !lp->in_txhandler && lp->sending)*/
+ if (status & lp->intmask & TXFREEflag)
+ {
+ struct Outgoing *out=&(lp->outgoing);
+ int was_sending=lp->sending;
+
+ lp->intmask &= ~TXFREEflag;
+
+ lp->in_txhandler++;
+ if (was_sending) lp->sending--;
+
+ BUGMSG(D_DURING,"TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n",
+ status,out->numsegs,out->segnum,out->skb);
+
+ if (was_sending && !(status&TXACKflag))
+ {
+ if (lp->lasttrans_dest != 0)
+ {
+ BUGMSG(D_EXTRA,"transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n",
+ status,lp->lasttrans_dest);
+ lp->stats.tx_errors++;
+ lp->stats.tx_carrier_errors++;
+ }
+ else
+ {
+ BUGMSG(D_DURING,"broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n",
+ status,
+ lp->lasttrans_dest);
+ }
+ }
+
+ /* send packet if there is one */
+ arcnet_go_tx(dev,0);
+ didsomething++;
+
+ if (lp->intx)
+ {
+ BUGMSG(D_DURING,"TXDONE while intx! (status=%Xh, intx=%d)\n",
+ ARCSTATUS,lp->intx);
+ lp->in_txhandler--;
+ continue;
+ }
+
+ if (!lp->outgoing.skb)
+ {
+ BUGMSG(D_DURING,"TX IRQ done: no split to continue.\n");
+
+ /* inform upper layers */
+ if (!lp->txready) arcnet_tx_done(dev, lp);
+ lp->in_txhandler--;
+ continue;
+ }
+
+ /* if more than one segment, and not all segments
+ * are done, then continue xmit.
+ */
+ if (out->segnum<out->numsegs)
+ arcnetA_continue_tx(dev);
+ arcnet_go_tx(dev,0);
+
+ /* if segnum==numsegs, the transmission is finished;
+ * free the skb.
+ */
+ if (out->segnum>=out->numsegs)
+ {
+ /* transmit completed */
+ out->segnum++;
+ if (out->skb)
+ {
+ lp->stats.tx_bytes += out->skb->len;
+ dev_kfree_skb(out->skb,FREE_WRITE);
+ }
+ out->skb=NULL;
+
+ /* inform upper layers */
+ if (!lp->txready) arcnet_tx_done(dev, lp);
+ }
+ didsomething++;
+
+ lp->in_txhandler--;
+ }
+ else if (lp->txready && !lp->sending && !lp->intx)
+ {
+ BUGMSG(D_NORMAL,"recovery from silent TX (status=%Xh)\n",
+ status);
+ arcnet_go_tx(dev,0);
+ didsomething++;
+ }
+
+#ifdef DETECT_RECONFIGS
+ if (status & (lp->intmask) & RECONflag)
+ {
+ ACOMMAND(CFLAGScmd|CONFIGclear);
+ lp->stats.tx_carrier_errors++;
+
+#ifdef SHOW_RECONFIGS
+ BUGMSG(D_NORMAL,"Network reconfiguration detected"
+ " (status=%Xh, config=%X)\n",
+ status,lp->config);
+#endif /* SHOW_RECONFIGS */
+
+#ifdef RECON_THRESHOLD
+ /* is the RECON info empty or old? */
+ if (!lp->first_recon || !lp->last_recon ||
+ jiffies-lp->last_recon > HZ*10)
+ {
+ if (lp->network_down)
+ BUGMSG(D_NORMAL,"reconfiguration detected: cabling restored?\n");
+ lp->first_recon=lp->last_recon=jiffies;
+ lp->num_recons=lp->network_down=0;
+
+ BUGMSG(D_DURING,"recon: clearing counters.\n");
+ }
+ else /* add to current RECON counter */
+ {
+ lp->last_recon=jiffies;
+ lp->num_recons++;
+
+ BUGMSG(D_DURING,"recon: counter=%d, time=%lds, net=%d\n",
+ lp->num_recons,
+ (lp->last_recon-lp->first_recon)/HZ,
+ lp->network_down);
+
+ /* if network is marked up;
+ * and first_recon and last_recon are 60+ sec
+ * apart;
+ * and the average no. of recons counted is
+ * > RECON_THRESHOLD/min;
+ * then print a warning message.
+ */
+ if (!lp->network_down
+ && (lp->last_recon-lp->first_recon)<=HZ*60
+ && lp->num_recons >= RECON_THRESHOLD)
+ {
+ lp->network_down=1;
+ BUGMSG(D_NORMAL,"many reconfigurations detected: cabling problem?\n");
+ }
+ else if (!lp->network_down
+ && lp->last_recon-lp->first_recon > HZ*60)
+ {
+ /* reset counters if we've gone for
+ * over a minute.
+ */
+ lp->first_recon=lp->last_recon;
+ lp->num_recons=1;
+ }
+ }
+ }
+ else if (lp->network_down && jiffies-lp->last_recon > HZ*10)
+ {
+ if (lp->network_down)
+ BUGMSG(D_NORMAL,"cabling restored?\n");
+ lp->first_recon=lp->last_recon=0;
+ lp->num_recons=lp->network_down=0;
+
+ BUGMSG(D_DURING,"not recon: clearing counters anyway.\n");
+#endif
+ }
+#endif /* DETECT_RECONFIGS */
+ } while (--boguscount && didsomething);
+
+ BUGMSG(D_DURING,"net_interrupt complete (status=%Xh, count=%d)\n",
+ ARCSTATUS,boguscount);
+ BUGMSG(D_DURING,"\n");
+
+ SETMASK; /* put back interrupt mask */
+}
+
+
+/* A packet has arrived; grab it from the buffers and pass it to the generic
+ * arcnet_rx routing to deal with it.
+ */
+
+static void
+arc90io_rx(struct device *dev,int recbuf)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int ioaddr=dev->base_addr;
+ union ArcPacket packetbuf;
+ union ArcPacket *arcpacket=&packetbuf;
+ u_char *arcsoft;
+ short length,offset;
+ u_char daddr,saddr;
+
+ lp->stats.rx_packets++;
+
+ get_whole_buffer(dev,recbuf*512,4,(char *)arcpacket);
+
+ saddr=arcpacket->hardheader.source;
+
+ /* if source is 0, it's a "used" packet! */
+ if (saddr==0)
+ {
+ BUGMSG(D_NORMAL,"discarding old packet. (status=%Xh)\n",
+ ARCSTATUS);
+ lp->stats.rx_errors++;
+ return;
+ }
+ /* Set source address to zero to mark it as old */
+
+ put_buffer_byte(dev,recbuf*512,0);
+
+ arcpacket->hardheader.source=0;
+
+ daddr=arcpacket->hardheader.destination;
+
+ if (arcpacket->hardheader.offset1) /* Normal Packet */
+ {
+ offset=arcpacket->hardheader.offset1;
+ arcsoft=&arcpacket->raw[offset];
+ length=256-offset;
+ }
+ else /* ExtendedPacket or ExceptionPacket */
+ {
+ offset=arcpacket->hardheader.offset2;
+ arcsoft=&arcpacket->raw[offset];
+ length=512-offset;
+ }
+
+ get_whole_buffer(dev,recbuf*512+offset,length,(char *)arcpacket+offset);
+
+ arcnet_rx(lp, arcsoft, length, saddr, daddr);
+
+ BUGLVL(D_RX) arcnet_dump_packet(lp->adev,arcpacket->raw,length>240,"rx");
+}
+
+
+/* Given an skb, copy a packet into the ARCnet buffers for later transmission
+ * by arcnet_go_tx.
+ */
+static void
+arc90io_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+ char *data,int length,int daddr,int exceptA, int offset)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+
+ lp->txbuf=lp->txbuf^1; /* XOR with 1 to alternate between 2 and 3 */
+
+ length+=hdrlen;
+
+ BUGMSG(D_TX,"arcnetAS_prep_tx: hdr:%ph, length:%d, data:%ph\n",
+ hdr,length,data);
+
+ put_buffer_byte(dev, lp->txbuf*512+1, daddr);
+
+ /* load packet into shared memory */
+ if (length<=MTU) /* Normal (256-byte) Packet */
+ put_buffer_byte(dev, lp->txbuf*512+2, offset=offset?offset:256-length);
+
+ else if (length>=MinTU || offset) /* Extended (512-byte) Packet */
+ {
+ put_buffer_byte(dev, lp->txbuf*512+2, 0);
+ put_buffer_byte(dev, lp->txbuf*512+3, offset=offset?offset:512-length);
+ }
+ else if (exceptA) /* RFC1201 Exception Packet */
+ {
+ put_buffer_byte(dev, lp->txbuf*512+2, 0);
+ put_buffer_byte(dev, lp->txbuf*512+3, offset=512-length-4);
+
+ /* exception-specific stuff - these four bytes
+ * make the packet long enough to fit in a 512-byte
+ * frame.
+ */
+
+ put_whole_buffer(dev, lp->txbuf*512+offset,4,"\0\0xff\0xff\0xff");
+ offset+=4;
+ }
+ else /* "other" Exception packet */
+ {
+ /* RFC1051 - set 4 trailing bytes to 0 */
+
+ put_whole_buffer(dev,lp->txbuf*512+508,4,"\0\0\0\0");
+
+ /* now round up to MinTU */
+ put_buffer_byte(dev, lp->txbuf*512+2, 0);
+ put_buffer_byte(dev, lp->txbuf*512+3, offset=512-MinTU);
+ }
+
+ /* copy the packet into ARCnet shmem
+ * - the first bytes of ClientData header are skipped
+ */
+
+ put_whole_buffer(dev, 512*lp->txbuf+offset, hdrlen,(u_char *)hdr);
+ put_whole_buffer(dev, 512*lp->txbuf+offset+hdrlen,length-hdrlen,data);
+
+ BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
+ daddr,length);
+
+ lp->lastload_dest=daddr;
+ lp->txready=lp->txbuf; /* packet is ready for sending */
+}
+
+
+/****************************************************************************
+ * *
+ * Kernel Loadable Module Support *
+ * *
+ ****************************************************************************/
+
+
+#ifdef MODULE
+
+static struct device *cards[16]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
+
+
+int init_module(void)
+{
+ struct device *dev=cards[0];
+
+ cards[0]=dev=(struct device *)kmalloc(sizeof(struct device), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ memset(dev, 0, sizeof(struct device));
+
+ dev->name=(char *)kmalloc(9, GFP_KERNEL);
+ if (!dev->name)
+ {
+ kfree(dev);
+ return -ENOMEM;
+ }
+ dev->init=arc90io_probe;
+
+ if (device)
+ strcpy(dev->name,device);
+ else arcnet_makename(dev->name);
+
+ dev->base_addr=io;
+ dev->irq=irq;
+
+ if (dev->irq==2) dev->irq=9;
+
+ if (register_netdev(dev) != 0)
+ return -EIO;
+
+ /* Increase use count of arcnet.o */
+ arcnet_use_count(1);
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ struct device *dev=cards[0];
+ int ioaddr=dev->base_addr;
+
+ if (dev->start) (*dev->stop)(dev);
+
+ /* Flush TX and disable RX */
+ if (ioaddr)
+ {
+ AINTMASK(0); /* disable IRQ's */
+ ACOMMAND(NOTXcmd); /* stop transmit */
+ ACOMMAND(NORXcmd); /* disable receive */
+
+ /* Set the thing back to MMAP mode, in case the old
+ driver is loaded later */
+ outb( (inb(_CONFIG)&~IOMAPflag),_CONFIG);
+ }
+
+ if (dev->irq)
+ {
+ irq2dev_map[dev->irq] = NULL;
+ free_irq(dev->irq,NULL);
+ }
+
+ if (dev->base_addr) release_region(dev->base_addr,ARCNET_TOTAL_SIZE);
+ unregister_netdev(dev);
+ kfree(dev->priv);
+ dev->priv = NULL;
+
+ /* Decrease use count of arcnet.o */
+ arcnet_use_count(0);
+}
+
+#else
+
+__initfunc(void com90io_setup (char *str, int *ints))
+{
+ struct device *dev;
+
+ if (arcnet_num_devs == MAX_ARCNET_DEVS)
+ {
+ printk("com90xx IO-MAP: Too many ARCnet devices registered (max %d).\n",
+ MAX_ARCNET_DEVS);
+ return;
+ }
+
+ dev=&arcnet_devs[arcnet_num_devs];
+
+ if (ints[0] < 1)
+ {
+ printk("com90xx IO-MAP: You must give an IO address.\n");
+ return;
+ }
+
+ dev->init=arc90io_probe;
+
+ switch(ints[0])
+ {
+ case 3: /* ERROR */
+ printk("com90xx IO-MAP: Too many arguments.\n");
+
+ case 2: /* IRQ */
+ dev->irq=ints[2];
+
+ case 1: /* IO address */
+ dev->base_addr=ints[1];
+ }
+
+ dev->name = (char *)&arcnet_dev_names[arcnet_num_devs];
+
+ if (str)
+ strncpy(dev->name, str, 9);
+
+ arcnet_num_devs++;
+}
+
+#endif /* MODULE */
diff --git a/drivers/net/com90xx.c b/drivers/net/com90xx.c
new file mode 100644
index 000000000..ec88b7354
--- /dev/null
+++ b/drivers/net/com90xx.c
@@ -0,0 +1,1264 @@
+/* $Id: com90xx.c,v 1.3 1997/09/05 18:27:23 mj Exp $
+
+ Derived from the original arcnet.c,
+ Written 1994-1996 by Avery Pennarun,
+ which was in turn derived from skeleton.c by Donald Becker.
+
+ Contact Avery at: apenwarr@bond.net or
+ RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
+
+ **********************
+
+ The original copyright of skeleton.c was as follows:
+
+ skeleton.c Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may only be used
+ and distributed according to the terms of the GNU Public License as
+ modified by SRC, incorporated herein by reference.
+
+ **********************
+
+ For more details, see drivers/net/arcnet.c
+
+ **********************
+*/
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/if_arcnet.h>
+#include <linux/arcdevice.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <net/arp.h>
+
+/**************************************************************************/
+
+/* On a fast computer, the buffer copy from memory to the ARCnet card during
+ * a transmit can hog the bus just a little too long. SLOW_XMIT_COPY
+ * replaces the fast memcpy() with a slower for() loop that seems to solve
+ * my problems with ftape.
+ *
+ * Probably a better solution would be to use memcpy_toio (more portable
+ * anyway) and modify that routine to support REALLY_SLOW_IO-style
+ * defines; ARCnet probably is not the only driver that can screw up an
+ * ftape DMA transfer.
+ *
+ * Turn this on if you have timing-sensitive DMA (ie. a tape drive) and
+ * would like to sacrifice a little bit of network speed to reduce tape
+ * write retries or some related problem.
+ */
+#undef SLOW_XMIT_COPY
+
+
+/* Define this to speed up the autoprobe by assuming if only one io port and
+ * shmem are left in the list at Stage 5, they must correspond to each
+ * other.
+ *
+ * This is undefined by default because it might not always be true, and the
+ * extra check makes the autoprobe even more careful. Speed demons can turn
+ * it on - I think it should be fine if you only have one ARCnet card
+ * installed.
+ *
+ * If no ARCnet cards are installed, this delay never happens anyway and thus
+ * the option has no effect.
+ */
+#undef FAST_PROBE
+
+
+/* Internal function declarations */
+#ifdef MODULE
+static
+#endif
+ int arc90xx_probe(struct device *dev);
+static void arc90xx_rx(struct device *dev,int recbuf);
+static int arc90xx_found(struct device *dev,int ioaddr,int airq,u_long shmem,int more);
+static void arc90xx_inthandler (struct device *dev);
+static int arc90xx_reset (struct device *dev, int reset_delay);
+static void arc90xx_setmask (struct device *dev, u_char mask);
+static void arc90xx_command (struct device *dev, u_char command);
+static u_char arc90xx_status (struct device *dev);
+static void arc90xx_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+ char *data,int length,int daddr,int exceptA, int offset);
+static void arc90xx_openclose(int open);
+
+
+/* Module parameters */
+
+#ifdef MODULE
+static int io=0x0; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq=0; /* or use the insmod io= irq= shmem= options */
+static int shmem=0;
+static char *device; /* use eg. device="arc1" to change name */
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(shmem, "i");
+MODULE_PARM(device, "s");
+#else
+__initfunc(void com90xx_setup (char *str, int *ints));
+char __initdata com90xx_explicit=0;
+
+extern struct device arcnet_devs[];
+extern char arcnet_dev_names[][10];
+extern int arcnet_num_devs;
+#endif
+
+
+/* Handy defines for ARCnet specific stuff */
+
+/* The number of low I/O ports used by the card. */
+#define ARCNET_TOTAL_SIZE 16
+
+/* COM 9026 controller chip --> ARCnet register addresses */
+#define _INTMASK (ioaddr+0) /* writable */
+#define _STATUS (ioaddr+0) /* readable */
+#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */
+#define _RESET (ioaddr+8) /* software reset (on read) */
+#define _MEMDATA (ioaddr+12) /* Data port for IO-mapped memory */
+#define _ADDR_HI (ioaddr+15) /* Control registers for said */
+#define _ADDR_LO (ioaddr+14)
+#define _CONFIG (ioaddr+2) /* Configuration register */
+
+#define RDDATAflag 0x00 /* Next access is a read/~write */
+
+#define ARCSTATUS inb(_STATUS)
+#define ACOMMAND(cmd) outb((cmd),_COMMAND)
+#define AINTMASK(msk) outb((msk),_INTMASK)
+#define SETCONF outb(lp->config,_CONFIG)
+#define ARCRESET inb(_RESET)
+
+static const char *version =
+ "com90xx.c: v2.92 97/09/02 Avery Pennarun <apenwarr@bond.net> et al.\n";
+
+
+/****************************************************************************
+ * *
+ * Probe and initialization *
+ * *
+ ****************************************************************************/
+
+/* Check for an ARCnet network adaptor, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ *
+ * NOTE: the list of possible ports/shmems is static, so it is retained
+ * across calls to arcnet_probe. So, if more than one ARCnet probe is made,
+ * values that were discarded once will not even be tried again.
+ *
+ * FIXME: grab all devices in one shot and eliminate the big static array.
+ */
+
+static int ports[(0x3f0 - 0x200) / 16 + 1] __initdata = { 0 };
+static u_long shmems[(0xFF800 - 0xA0000) / 2048 + 1] __initdata = { 0 };
+
+__initfunc(int arc90xx_probe(struct device *dev))
+{
+ static int init_once = 0;
+ static int numports=sizeof(ports)/sizeof(ports[0]),
+ numshmems=sizeof(shmems)/sizeof(shmems[0]);
+ int count,status,delayval,ioaddr,numprint,airq,retval=-ENODEV,
+ openparen=0;
+ unsigned long airqmask;
+ int *port;
+ u_long *shmem;
+
+ if (!init_once)
+ {
+ for (count=0x200; count<=0x3f0; count+=16)
+ ports[(count-0x200)/16] = count;
+ for (count=0xA0000; count<=0xFF800; count+=2048)
+ shmems[(count-0xA0000)/2048] = count;
+ BUGLVL(D_NORMAL) printk(version);
+ BUGMSG(D_DURING,"space used for probe buffers: %d+%d=%d bytes\n",
+ sizeof(ports),sizeof(shmems),
+ sizeof(ports)+sizeof(shmems));
+ }
+ init_once++;
+
+ BUGMSG(D_INIT,"given: base %lXh, IRQ %d, shmem %lXh\n",
+ dev->base_addr,dev->irq,dev->mem_start);
+
+ if (dev->base_addr > 0x1ff) /* Check a single specified port */
+ {
+ ports[0]=dev->base_addr;
+ numports=1;
+ }
+ else if (dev->base_addr > 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ if (dev->mem_start)
+ {
+ shmems[0]=dev->mem_start;
+ numshmems=1;
+ }
+
+ /* Stage 1: abandon any reserved ports, or ones with status==0xFF
+ * (empty), and reset any others by reading the reset port.
+ */
+ BUGMSG(D_INIT,"Stage 1: ");
+ numprint=0;
+ for (port = &ports[0]; port-ports<numports; port++)
+ {
+ numprint++;
+ if (numprint>8)
+ {
+ BUGMSG2(D_INIT,"\n");
+ BUGMSG(D_INIT,"Stage 1: ");
+ numprint=1;
+ }
+ BUGMSG2(D_INIT,"%Xh ",*port);
+
+ ioaddr=*port;
+
+ if (check_region(*port, ARCNET_TOTAL_SIZE))
+ {
+ BUGMSG2(D_INIT_REASONS,"(check_region)\n");
+ BUGMSG(D_INIT_REASONS,"Stage 1: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ *port=ports[numports-1];
+ numports--;
+ port--;
+ continue;
+ }
+
+ if (ARCSTATUS == 0xFF)
+ {
+ BUGMSG2(D_INIT_REASONS,"(empty)\n");
+ BUGMSG(D_INIT_REASONS,"Stage 1: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ *port=ports[numports-1];
+ numports--;
+ port--;
+ continue;
+ }
+
+ ARCRESET; /* begin resetting card */
+
+ BUGMSG2(D_INIT_REASONS,"\n");
+ BUGMSG(D_INIT_REASONS,"Stage 1: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ }
+ BUGMSG2(D_INIT,"\n");
+
+ if (!numports)
+ {
+ BUGMSG(D_NORMAL,"Stage 1: No ARCnet cards found.\n");
+ return -ENODEV;
+ }
+
+ /* Stage 2: we have now reset any possible ARCnet cards, so we can't
+ * do anything until they finish. If D_INIT, print the list of
+ * cards that are left.
+ */
+ BUGMSG(D_INIT,"Stage 2: ");
+ numprint=0;
+ for (port = &ports[0]; port-ports<numports; port++)
+ {
+ numprint++;
+ if (numprint>8)
+ {
+ BUGMSG2(D_INIT,"\n");
+ BUGMSG(D_INIT,"Stage 2: ");
+ numprint=1;
+ }
+ BUGMSG2(D_INIT,"%Xh ",*port);
+ }
+ BUGMSG2(D_INIT,"\n");
+ JIFFER(RESETtime);
+
+ /* Stage 3: abandon any shmem addresses that don't have the signature
+ * 0xD1 byte in the right place, or are read-only.
+ */
+ BUGMSG(D_INIT,"Stage 3: ");
+ numprint=0;
+ for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
+ {
+ u_long ptr;
+
+ numprint++;
+ if (numprint>8)
+ {
+ BUGMSG2(D_INIT,"\n");
+ BUGMSG(D_INIT,"Stage 3: ");
+ numprint=1;
+ }
+ BUGMSG2(D_INIT,"%lXh ",*shmem);
+
+ ptr=(u_long)(*shmem);
+
+ if (readb(ptr) != TESTvalue)
+ {
+ BUGMSG2(D_INIT_REASONS,"(mem=%02Xh, not %02Xh)\n",
+ readb(ptr),TESTvalue);
+ BUGMSG(D_INIT_REASONS,"Stage 3: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ *shmem=shmems[numshmems-1];
+ numshmems--;
+ shmem--;
+ continue;
+ }
+
+ /* By writing 0x42 to the TESTvalue location, we also make
+ * sure no "mirror" shmem areas show up - if they occur
+ * in another pass through this loop, they will be discarded
+ * because *cptr != TESTvalue.
+ */
+ writeb(0x42,ptr);
+ if (readb(ptr) != 0x42)
+ {
+ BUGMSG2(D_INIT_REASONS,"(read only)\n");
+ BUGMSG(D_INIT_REASONS,"Stage 3: ");
+ *shmem=shmems[numshmems-1];
+ numshmems--;
+ shmem--;
+ continue;
+ }
+
+ BUGMSG2(D_INIT_REASONS,"\n");
+ BUGMSG(D_INIT_REASONS,"Stage 3: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ }
+ BUGMSG2(D_INIT,"\n");
+
+ if (!numshmems)
+ {
+ BUGMSG(D_NORMAL,"Stage 3: No ARCnet cards found.\n");
+ return -ENODEV;
+ }
+
+ /* Stage 4: something of a dummy, to report the shmems that are
+ * still possible after stage 3.
+ */
+ BUGMSG(D_INIT,"Stage 4: ");
+ numprint=0;
+ for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
+ {
+ numprint++;
+ if (numprint>8)
+ {
+ BUGMSG2(D_INIT,"\n");
+ BUGMSG(D_INIT,"Stage 4: ");
+ numprint=1;
+ }
+ BUGMSG2(D_INIT,"%lXh ",*shmem);
+ }
+ BUGMSG2(D_INIT,"\n");
+
+ /* Stage 5: for any ports that have the correct status, can disable
+ * the RESET flag, and (if no irq is given) generate an autoirq,
+ * register an ARCnet device.
+ *
+ * Currently, we can only register one device per probe, so quit
+ * after the first one is found.
+ */
+ BUGMSG(D_INIT,"Stage 5: ");
+ numprint=0;
+ for (port = &ports[0]; port-ports<numports; port++)
+ {
+ numprint++;
+ if (numprint>8)
+ {
+ BUGMSG2(D_INIT,"\n");
+ BUGMSG(D_INIT,"Stage 5: ");
+ numprint=1;
+ }
+ BUGMSG2(D_INIT,"%Xh ",*port);
+
+ ioaddr=*port;
+ status=ARCSTATUS;
+
+ if ((status & 0x9D)
+ != (NORXflag|RECONflag|TXFREEflag|RESETflag))
+ {
+ BUGMSG2(D_INIT_REASONS,"(status=%Xh)\n",status);
+ BUGMSG(D_INIT_REASONS,"Stage 5: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ *port=ports[numports-1];
+ numports--;
+ port--;
+ continue;
+ }
+
+ ACOMMAND(CFLAGScmd|RESETclear|CONFIGclear);
+ status=ARCSTATUS;
+ if (status & RESETflag)
+ {
+ BUGMSG2(D_INIT_REASONS," (eternal reset, status=%Xh)\n",
+ status);
+ BUGMSG(D_INIT_REASONS,"Stage 5: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ *port=ports[numports-1];
+ numports--;
+ port--;
+ continue;
+ }
+
+ /* skip this completely if an IRQ was given, because maybe
+ * we're on a machine that locks during autoirq!
+ */
+ if (!dev->irq)
+ {
+ /* if we do this, we're sure to get an IRQ since the
+ * card has just reset and the NORXflag is on until
+ * we tell it to start receiving.
+ */
+ airqmask = probe_irq_on();
+ AINTMASK(NORXflag);
+ udelay(1);
+ AINTMASK(0);
+ airq = probe_irq_off(airqmask);
+
+ if (airq<=0)
+ {
+ BUGMSG2(D_INIT_REASONS,"(airq=%d)\n",airq);
+ BUGMSG(D_INIT_REASONS,"Stage 5: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ *port=ports[numports-1];
+ numports--;
+ port--;
+ continue;
+ }
+ }
+ else
+ {
+ airq=dev->irq;
+ }
+
+ BUGMSG2(D_INIT,"(%d,", airq);
+ openparen=1;
+
+ /* Everything seems okay. But which shmem, if any, puts
+ * back its signature byte when the card is reset?
+ *
+ * If there are multiple cards installed, there might be
+ * multiple shmems still in the list.
+ */
+#ifdef FAST_PROBE
+ if (numports>1 || numshmems>1)
+ {
+ ARCRESET;
+ JIFFER(RESETtime);
+ }
+ else
+ {
+ /* just one shmem and port, assume they match */
+ writeb(TESTvalue,shmems[0]);
+ }
+#else
+ ARCRESET;
+ JIFFER(RESETtime);
+#endif
+
+ for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
+ {
+ u_long ptr;
+ ptr=(u_long)(*shmem);
+
+ if (readb(ptr) == TESTvalue) /* found one */
+ {
+ int probe_more;
+ BUGMSG2(D_INIT,"%lXh)\n", *shmem);
+ openparen=0;
+
+ /* register the card */
+ if (init_once == 1 && numshmems > 1)
+ probe_more = numshmems - 1;
+ else
+ probe_more = 0;
+ retval=arc90xx_found(dev,*port,airq,*shmem,probe_more);
+ if (retval) openparen=0;
+
+ /* remove shmem from the list */
+ *shmem=shmems[numshmems-1];
+ numshmems--;
+
+ break;
+ }
+ else
+ {
+ BUGMSG2(D_INIT_REASONS,"%Xh-", readb(ptr));
+ }
+ }
+
+ if (openparen)
+ {
+ BUGMSG2(D_INIT,"no matching shmem)\n");
+ BUGMSG(D_INIT_REASONS,"Stage 5: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ }
+
+ *port=ports[numports-1];
+ numports--;
+ port--;
+
+ if (!retval) break;
+ }
+ BUGMSG(D_INIT_REASONS,"\n");
+
+ /* Now put back TESTvalue on all leftover shmems.
+ */
+ for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
+ writeb(TESTvalue,*shmem);
+
+ if (retval) BUGMSG(D_NORMAL,"Stage 5: No ARCnet cards found.\n");
+ return retval;
+}
+
+/* Set up the struct device associated with this card. Called after
+ * probing succeeds.
+ */
+__initfunc(static int arc90xx_found(struct device *dev,int ioaddr,int airq, u_long shmem, int more))
+{
+ struct arcnet_local *lp;
+ u_long first_mirror,last_mirror;
+ int mirror_size;
+
+ /* reserve the irq */
+ if (request_irq(airq,&arcnet_interrupt,0,"arcnet (90xx)",NULL))
+ {
+ BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
+ return -ENODEV;
+ }
+ irq2dev_map[airq]=dev;
+ dev->irq=airq;
+
+ /* reserve the I/O region - guaranteed to work by check_region */
+ request_region(ioaddr,ARCNET_TOTAL_SIZE,"arcnet (90xx)");
+ dev->base_addr=ioaddr;
+
+ /* find the real shared memory start/end points, including mirrors */
+#define BUFFER_SIZE (512)
+#define MIRROR_SIZE (BUFFER_SIZE*4)
+
+ /* guess the actual size of one "memory mirror" - the number of
+ * bytes between copies of the shared memory. On most cards, it's
+ * 2k (or there are no mirrors at all) but on some, it's 4k.
+ */
+ mirror_size=MIRROR_SIZE;
+ if (readb(shmem)==TESTvalue
+ && readb(shmem-mirror_size)!=TESTvalue
+ && readb(shmem-2*mirror_size)==TESTvalue)
+ mirror_size*=2;
+
+ first_mirror=last_mirror=shmem;
+ while (readb(first_mirror)==TESTvalue) first_mirror-=mirror_size;
+ first_mirror+=mirror_size;
+
+ while (readb(last_mirror)==TESTvalue) last_mirror+=mirror_size;
+ last_mirror-=mirror_size;
+
+ dev->mem_start=first_mirror;
+ dev->mem_end=last_mirror+MIRROR_SIZE-1;
+ dev->rmem_start=dev->mem_start+BUFFER_SIZE*0;
+ dev->rmem_end=dev->mem_start+BUFFER_SIZE*2-1;
+
+ /* Initialize the rest of the device structure. */
+
+ dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ {
+ irq2dev_map[airq] = NULL;
+ free_irq(airq,NULL);
+ release_region(ioaddr,ARCNET_TOTAL_SIZE);
+ return -ENOMEM;
+ }
+ memset(dev->priv,0,sizeof(struct arcnet_local));
+ lp=(struct arcnet_local *)(dev->priv);
+ lp->card_type = ARC_90xx;
+ lp->card_type_str = "COM 90xx";
+ lp->arcnet_reset=arc90xx_reset;
+ lp->asetmask=arc90xx_setmask;
+ lp->astatus=arc90xx_status;
+ lp->acommand=arc90xx_command;
+ lp->openclose_device=arc90xx_openclose;
+ lp->prepare_tx=arc90xx_prepare_tx;
+ lp->inthandler=arc90xx_inthandler;
+
+ /* Fill in the fields of the device structure with generic
+ * values.
+ */
+ arcnet_setup(dev);
+
+ /* And now fill particular fields with arcnet values */
+ dev->mtu=1500; /* completely arbitrary - agrees with ether, though */
+ dev->hard_header_len=sizeof(struct ClientData);
+ lp->sequence=1;
+ lp->recbuf=0;
+
+ BUGMSG(D_DURING,"ClientData header size is %d.\n",
+ sizeof(struct ClientData));
+ BUGMSG(D_DURING,"HardHeader size is %d.\n",
+ sizeof(struct archdr));
+
+ /* get and check the station ID from offset 1 in shmem */
+ lp->stationid = readb(first_mirror+1);
+
+ if (lp->stationid==0)
+ BUGMSG(D_NORMAL,"WARNING! Station address 00 is reserved "
+ "for broadcasts!\n");
+ else if (lp->stationid==255)
+ BUGMSG(D_NORMAL,"WARNING! Station address FF may confuse "
+ "DOS networking programs!\n");
+ dev->dev_addr[0]=lp->stationid;
+
+ BUGMSG(D_NORMAL,"ARCnet COM90xx: station %02Xh found at %03lXh, IRQ %d, "
+ "ShMem %lXh (%ld*%xh).\n",
+ lp->stationid,
+ dev->base_addr, dev->irq, dev->mem_start,
+ (dev->mem_end-dev->mem_start+1)/mirror_size,mirror_size);
+
+ /* OK. We're finished. If there are probably other cards, add other
+ * COM90xx drivers to the device chain, so they get probed later.
+ */
+
+#ifndef MODULE
+ while (!com90xx_explicit && more--)
+ {
+ if (arcnet_num_devs < MAX_ARCNET_DEVS)
+ {
+ arcnet_devs[arcnet_num_devs].next=dev->next;
+ dev->next=&arcnet_devs[arcnet_num_devs];
+ dev=dev->next;
+ dev->name=(char *)&arcnet_dev_names[arcnet_num_devs];
+ arcnet_num_devs++;
+ }
+ else
+ {
+ BUGMSG(D_NORMAL, "Too many arcnet devices - no more will be probed for.\n");
+ return 0;
+ }
+ arcnet_makename(dev->name);
+ dev->init=arc90xx_probe;
+ }
+#endif
+
+ return 0;
+}
+
+
+/* Do a hardware reset on the card, and set up necessary registers.
+ *
+ * This should be called as little as possible, because it disrupts the
+ * token on the network (causes a RECON) and requires a significant delay.
+ *
+ * However, it does make sure the card is in a defined state.
+ */
+int arc90xx_reset(struct device *dev,int reset_delay)
+{
+ struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+ short ioaddr=dev->base_addr;
+ int delayval,recbuf=lp->recbuf;
+
+ if (reset_delay==3)
+ {
+ ARCRESET;
+ return 0;
+ }
+
+ /* no IRQ's, please! */
+ lp->intmask=0;
+ SETMASK;
+
+ BUGMSG(D_INIT,"Resetting %s (status=%Xh)\n",
+ dev->name,ARCSTATUS);
+
+ if (reset_delay)
+ {
+ /* reset the card */
+ ARCRESET;
+ JIFFER(RESETtime);
+ }
+
+ ACOMMAND(CFLAGScmd|RESETclear); /* clear flags & end reset */
+ ACOMMAND(CFLAGScmd|CONFIGclear);
+
+ /* verify that the ARCnet signature byte is present */
+ if (readb(dev->mem_start) != TESTvalue)
+ {
+ BUGMSG(D_NORMAL,"reset failed: TESTvalue not present.\n");
+ return 1;
+ }
+
+ /* clear out status variables */
+ recbuf=lp->recbuf=0;
+ lp->txbuf=2;
+
+ /* enable extended (512-byte) packets */
+ ACOMMAND(CONFIGcmd|EXTconf);
+
+#ifndef SLOW_XMIT_COPY
+ /* clean out all the memory to make debugging make more sense :) */
+ BUGLVL(D_DURING)
+ memset_io(dev->mem_start,0x42,2048);
+#endif
+
+ /* and enable receive of our first packet to the first buffer */
+ EnableReceiver();
+
+ /* re-enable interrupts */
+ lp->intmask|=NORXflag;
+#ifdef DETECT_RECONFIGS
+ lp->intmask|=RECONflag;
+#endif
+ SETMASK;
+
+ /* done! return success. */
+ return 0;
+}
+
+
+static void arc90xx_openclose(int open)
+{
+ if (open)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+
+
+static void arc90xx_setmask(struct device *dev, u_char mask)
+{
+ short ioaddr=dev->base_addr;
+
+ AINTMASK(mask);
+}
+
+
+static u_char arc90xx_status(struct device *dev)
+{
+ short ioaddr=dev->base_addr;
+
+ return ARCSTATUS;
+}
+
+
+static void arc90xx_command(struct device *dev, u_char cmd)
+{
+ short ioaddr=dev->base_addr;
+
+ ACOMMAND(cmd);
+}
+
+
+/* The actual interrupt handler routine - handle various IRQ's generated
+ * by the card.
+ */
+static void
+arc90xx_inthandler(struct device *dev)
+{
+ struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+ int ioaddr=dev->base_addr, status, boguscount = 3, didsomething;
+
+ AINTMASK(0);
+
+ BUGMSG(D_DURING,"in arcnet_inthandler (status=%Xh, intmask=%Xh)\n",
+ ARCSTATUS,lp->intmask);
+
+ do
+ {
+ status = ARCSTATUS;
+ didsomething=0;
+
+ /* RESET flag was enabled - card is resetting and if RX
+ * is disabled, it's NOT because we just got a packet.
+ */
+ if (status & RESETflag)
+ {
+ BUGMSG(D_NORMAL,"spurious reset (status=%Xh)\n",
+ status);
+ arc90xx_reset(dev,0);
+
+ /* all other flag values are just garbage */
+ break;
+ }
+
+ /* RX is inhibited - we must have received something. */
+ if (status & lp->intmask & NORXflag)
+ {
+ int recbuf=lp->recbuf=!lp->recbuf;
+
+ BUGMSG(D_DURING,"receive irq (status=%Xh)\n",
+ status);
+
+ /* enable receive of our next packet */
+ EnableReceiver();
+
+ /* Got a packet. */
+ arc90xx_rx(dev,!recbuf);
+
+ didsomething++;
+ }
+
+ /* it can only be an xmit-done irq if we're xmitting :) */
+ /*if (status&TXFREEflag && !lp->in_txhandler && lp->sending)*/
+ if (status & lp->intmask & TXFREEflag)
+ {
+ struct Outgoing *out=&(lp->outgoing);
+ int was_sending=lp->sending;
+
+ lp->intmask &= ~TXFREEflag;
+
+ lp->in_txhandler++;
+ if (was_sending) lp->sending--;
+
+ BUGMSG(D_DURING,"TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n",
+ status,out->numsegs,out->segnum,out->skb);
+
+ if (was_sending && !(status&TXACKflag))
+ {
+ if (lp->lasttrans_dest != 0)
+ {
+ BUGMSG(D_EXTRA,"transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n",
+ status,lp->lasttrans_dest);
+ lp->stats.tx_errors++;
+ lp->stats.tx_carrier_errors++;
+ }
+ else
+ {
+ BUGMSG(D_DURING,"broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n",
+ status,
+ lp->lasttrans_dest);
+ }
+ }
+
+ /* send packet if there is one */
+ arcnet_go_tx(dev,0);
+ didsomething++;
+
+ if (lp->intx)
+ {
+ BUGMSG(D_DURING,"TXDONE while intx! (status=%Xh, intx=%d)\n",
+ ARCSTATUS,lp->intx);
+ lp->in_txhandler--;
+ continue;
+ }
+
+ if (!lp->outgoing.skb)
+ {
+ BUGMSG(D_DURING,"TX IRQ done: no split to continue.\n");
+
+ /* inform upper layers */
+ if (!lp->txready) arcnet_tx_done(dev, lp);
+ lp->in_txhandler--;
+ continue;
+ }
+
+ /* if more than one segment, and not all segments
+ * are done, then continue xmit.
+ */
+ if (out->segnum<out->numsegs)
+ arcnetA_continue_tx(dev);
+ arcnet_go_tx(dev,0);
+
+ /* if segnum==numsegs, the transmission is finished;
+ * free the skb.
+ */
+ if (out->segnum>=out->numsegs)
+ {
+ /* transmit completed */
+ out->segnum++;
+ if (out->skb)
+ {
+ lp->stats.tx_bytes += out->skb->len;
+ dev_kfree_skb(out->skb,FREE_WRITE);
+ }
+ out->skb=NULL;
+
+ /* inform upper layers */
+ if (!lp->txready) arcnet_tx_done(dev, lp);
+ }
+ didsomething++;
+
+ lp->in_txhandler--;
+ }
+ else if (lp->txready && !lp->sending && !lp->intx)
+ {
+ BUGMSG(D_NORMAL,"recovery from silent TX (status=%Xh)\n",
+ status);
+ arcnet_go_tx(dev,0);
+ didsomething++;
+ }
+
+#ifdef DETECT_RECONFIGS
+ if (status & (lp->intmask) & RECONflag)
+ {
+ ACOMMAND(CFLAGScmd|CONFIGclear);
+ lp->stats.tx_carrier_errors++;
+
+#ifdef SHOW_RECONFIGS
+ BUGMSG(D_NORMAL,"Network reconfiguration detected (status=%Xh)\n",
+ status);
+#endif /* SHOW_RECONFIGS */
+
+#ifdef RECON_THRESHOLD
+ /* is the RECON info empty or old? */
+ if (!lp->first_recon || !lp->last_recon ||
+ jiffies-lp->last_recon > HZ*10)
+ {
+ if (lp->network_down)
+ BUGMSG(D_NORMAL,"reconfiguration detected: cabling restored?\n");
+ lp->first_recon=lp->last_recon=jiffies;
+ lp->num_recons=lp->network_down=0;
+
+ BUGMSG(D_DURING,"recon: clearing counters.\n");
+ }
+ else /* add to current RECON counter */
+ {
+ lp->last_recon=jiffies;
+ lp->num_recons++;
+
+ BUGMSG(D_DURING,"recon: counter=%d, time=%lds, net=%d\n",
+ lp->num_recons,
+ (lp->last_recon-lp->first_recon)/HZ,
+ lp->network_down);
+
+ /* if network is marked up;
+ * and first_recon and last_recon are 60+ sec
+ * apart;
+ * and the average no. of recons counted is
+ * > RECON_THRESHOLD/min;
+ * then print a warning message.
+ */
+ if (!lp->network_down
+ && (lp->last_recon-lp->first_recon)<=HZ*60
+ && lp->num_recons >= RECON_THRESHOLD)
+ {
+ lp->network_down=1;
+ BUGMSG(D_NORMAL,"many reconfigurations detected: cabling problem?\n");
+ }
+ else if (!lp->network_down
+ && lp->last_recon-lp->first_recon > HZ*60)
+ {
+ /* reset counters if we've gone for
+ * over a minute.
+ */
+ lp->first_recon=lp->last_recon;
+ lp->num_recons=1;
+ }
+ }
+ }
+ else if (lp->network_down && jiffies-lp->last_recon > HZ*10)
+ {
+ if (lp->network_down)
+ BUGMSG(D_NORMAL,"cabling restored?\n");
+ lp->first_recon=lp->last_recon=0;
+ lp->num_recons=lp->network_down=0;
+
+ BUGMSG(D_DURING,"not recon: clearing counters anyway.\n");
+#endif
+ }
+#endif /* DETECT_RECONFIGS */
+ } while (--boguscount && didsomething);
+
+ BUGMSG(D_DURING,"net_interrupt complete (status=%Xh, count=%d)\n",
+ ARCSTATUS,boguscount);
+ BUGMSG(D_DURING,"\n");
+
+ SETMASK; /* put back interrupt mask */
+}
+
+
+/* A packet has arrived; grab it from the buffers and pass it to the generic
+ * arcnet_rx routing to deal with it.
+ */
+
+static void
+arc90xx_rx(struct device *dev,int recbuf)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int ioaddr=dev->base_addr;
+ union ArcPacket *arcpacket=
+ (union ArcPacket *)phys_to_virt(dev->mem_start+recbuf*512);
+ u_char *arcsoft;
+ short length,offset;
+ u_char daddr,saddr;
+
+ lp->stats.rx_packets++;
+
+ saddr=arcpacket->hardheader.source;
+
+ /* if source is 0, it's a "used" packet! */
+ if (saddr==0)
+ {
+ BUGMSG(D_NORMAL,"discarding old packet. (status=%Xh)\n",
+ ARCSTATUS);
+ lp->stats.rx_errors++;
+ return;
+ }
+ /* Set source address to zero to mark it as old */
+
+ arcpacket->hardheader.source=0;
+
+ daddr=arcpacket->hardheader.destination;
+
+ if (arcpacket->hardheader.offset1) /* Normal Packet */
+ {
+ offset=arcpacket->hardheader.offset1;
+ arcsoft=&arcpacket->raw[offset];
+ length=256-offset;
+ }
+ else /* ExtendedPacket or ExceptionPacket */
+ {
+ offset=arcpacket->hardheader.offset2;
+ arcsoft=&arcpacket->raw[offset];
+
+ length=512-offset;
+ }
+
+ arcnet_rx(lp, arcsoft, length, saddr, daddr);
+
+ BUGLVL(D_RX) arcnet_dump_packet(lp->adev,arcpacket->raw,length>240,"rx");
+
+#ifndef SLOW_XMIT_COPY
+ /* clean out the page to make debugging make more sense :) */
+ BUGLVL(D_DURING)
+ memset((void *)arcpacket->raw,0x42,512);
+#endif
+}
+
+
+/* Given an skb, copy a packet into the ARCnet buffers for later transmission
+ * by arcnet_go_tx.
+ */
+static void
+arc90xx_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+ char *data,int length,int daddr,int exceptA, int offset)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ union ArcPacket *arcpacket =
+ (union ArcPacket *)phys_to_virt(dev->mem_start+512*(lp->txbuf^1));
+
+#ifdef SLOW_XMIT_COPY
+ char *iptr,*iend,*optr;
+#endif
+
+ lp->txbuf=lp->txbuf^1; /* XOR with 1 to alternate between 2 and 3 */
+
+ length+=hdrlen;
+
+ BUGMSG(D_TX,"arcnetAS_prep_tx: hdr:%ph, length:%d, data:%ph\n",
+ hdr,length,data);
+
+#ifndef SLOW_XMIT_COPY
+ /* clean out the page to make debugging make more sense :) */
+ BUGLVL(D_DURING)
+ memset_io(dev->mem_start+lp->txbuf*512,0x42,512);
+#endif
+
+ arcpacket->hardheader.destination=daddr;
+
+ /* load packet into shared memory */
+ if (length<=MTU) /* Normal (256-byte) Packet */
+ arcpacket->hardheader.offset1=offset=offset?offset:256-length;
+
+ else if (length>=MinTU || offset) /* Extended (512-byte) Packet */
+ {
+ arcpacket->hardheader.offset1=0;
+ arcpacket->hardheader.offset2=offset=offset?offset:512-length;
+ }
+ else if (exceptA) /* RFC1201 Exception Packet */
+ {
+ arcpacket->hardheader.offset1=0;
+ arcpacket->hardheader.offset2=offset=512-length-4;
+
+ /* exception-specific stuff - these four bytes
+ * make the packet long enough to fit in a 512-byte
+ * frame.
+ */
+
+ arcpacket->raw[offset+0]=hdr[0];
+ arcpacket->raw[offset+1]=0xFF; /* FF flag */
+ arcpacket->raw[offset+2]=0xFF; /* FF padding */
+ arcpacket->raw[offset+3]=0xFF; /* FF padding */
+ offset+=4;
+ }
+ else /* "other" Exception packet */
+ {
+ /* RFC1051 - set 4 trailing bytes to 0 */
+ memset(&arcpacket->raw[508],0,4);
+
+ /* now round up to MinTU */
+ arcpacket->hardheader.offset1=0;
+ arcpacket->hardheader.offset2=offset=512-MinTU;
+ }
+
+ /* copy the packet into ARCnet shmem
+ * - the first bytes of ClientData header are skipped
+ */
+
+ memcpy((u_char*)arcpacket+offset, (u_char*)hdr,hdrlen);
+#ifdef SLOW_XMIT_COPY
+ for (iptr=data,iend=iptr+length-hdrlen,optr=(char *)arcpacket+offset+hdrlen;
+ iptr<iend; iptr++,optr++)
+ {
+ *optr=*iptr;
+ /*udelay(5);*/
+ }
+#else
+ memcpy((u_char*)arcpacket+offset+hdrlen, data,length-hdrlen);
+#endif
+
+ BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
+ daddr,length);
+
+ BUGLVL(D_TX) arcnet_dump_packet(dev,arcpacket->raw,length>MTU,"tx");
+
+ lp->lastload_dest=daddr;
+ lp->txready=lp->txbuf; /* packet is ready for sending */
+}
+
+
+/****************************************************************************
+ * *
+ * Kernel Loadable Module Support *
+ * *
+ ****************************************************************************/
+
+
+#ifdef MODULE
+
+static char devicename[9] = "";
+static struct device thiscard = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0, /* I/O address, IRQ */
+ 0, 0, 0, NULL, arc90xx_probe
+};
+
+
+int init_module(void)
+{
+ struct device *dev=&thiscard;
+ if (device)
+ strcpy(dev->name,device);
+ else arcnet_makename(dev->name);
+
+ dev->base_addr=io;
+
+ dev->irq=irq;
+ if (dev->irq==2) dev->irq=9;
+
+ if (shmem)
+ {
+ dev->mem_start=shmem;
+ dev->mem_end=thiscard.mem_start+512*4-1;
+ dev->rmem_start=thiscard.mem_start+512*0;
+ dev->rmem_end=thiscard.mem_start+512*2-1;
+ }
+
+ if (register_netdev(dev) != 0)
+ return -EIO;
+ arcnet_use_count(1);
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ struct device *dev=&thiscard;
+ int ioaddr=dev->mem_start;
+
+ if (dev->start) (*dev->stop)(dev);
+
+ /* Flush TX and disable RX */
+ if (ioaddr)
+ {
+ AINTMASK(0); /* disable IRQ's */
+ ACOMMAND(NOTXcmd); /* stop transmit */
+ ACOMMAND(NORXcmd); /* disable receive */
+
+#if defined(IO_MAPPED_BUFFERS) && !defined(COM20020)
+ /* Set the thing back to MMAP mode, in case the old
+ driver is loaded later */
+ outb( (inb(_CONFIG)&~IOMAPflag),_CONFIG);
+#endif
+ }
+
+ if (dev->irq)
+ {
+ irq2dev_map[dev->irq] = NULL;
+ free_irq(dev->irq,NULL);
+ }
+
+ if (dev->base_addr) release_region(dev->base_addr,ARCNET_TOTAL_SIZE);
+ unregister_netdev(dev);
+ kfree(dev->priv);
+ dev->priv = NULL;
+ arcnet_use_count(0);
+}
+
+#else
+
+__initfunc(void com90xx_setup (char *str, int *ints))
+{
+ struct device *dev;
+
+ if (arcnet_num_devs == MAX_ARCNET_DEVS)
+ {
+ printk("com90xx: Too many ARCnet devices registered (max %d).\n",
+ MAX_ARCNET_DEVS);
+ return;
+ }
+
+ if (!ints[0] && (!str || !*str))
+ {
+ printk("com90xx: Disabled.\n");
+ com90xx_explicit++;
+ return;
+ }
+
+ dev=&arcnet_devs[arcnet_num_devs];
+
+ dev->dev_addr[3]=3;
+ dev->init=arc90xx_probe;
+
+ switch(ints[0])
+ {
+ case 4: /* ERROR */
+ printk("com20020: Too many arguments.\n");
+
+ case 3: /* Mem address */
+ dev->mem_start=ints[3];
+
+ case 2: /* IRQ */
+ dev->irq=ints[2];
+
+ case 1: /* IO address */
+ dev->base_addr=ints[1];
+ }
+
+ dev->name = (char *)&arcnet_dev_names[arcnet_num_devs];
+
+ if (str)
+ strncpy(dev->name, str, 9);
+
+ arcnet_num_devs++;
+}
+#endif /* MODULE */
diff --git a/drivers/net/defxx.c b/drivers/net/defxx.c
index b841a01bb..03250092b 100644
--- a/drivers/net/defxx.c
+++ b/drivers/net/defxx.c
@@ -2953,7 +2953,7 @@ void dfx_rcv_init(
* the old EISA boards.
*/
newskb->data = (char *)((unsigned long)
- (newskb->data+127) & ~128);
+ (newskb->data+127) & ~127);
bp->descr_block_virt->rcv_data[i+j].long_1 = virt_to_bus(newskb->data);
/*
* p_rcv_buff_va is only used inside the
@@ -3065,7 +3065,7 @@ void dfx_rcv_queue_process(
if (newskb){
rx_in_place = 1;
- newskb->data = (char *)((unsigned long)(newskb->data+127) & ~128);
+ newskb->data = (char *)((unsigned long)(newskb->data+127) & ~127);
skb = (struct sk_buff *)bp->p_rcv_buff_va[entry];
skb->data += RCV_BUFF_K_PADDING;
bp->p_rcv_buff_va[entry] = (char *)newskb;
diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c
index af117e223..ce89c7b47 100644
--- a/drivers/net/eepro100.c
+++ b/drivers/net/eepro100.c
@@ -1,7 +1,7 @@
/* drivers/net/eepro100.c: An Intel i82557 ethernet driver for linux. */
/*
NOTICE: this version tested with kernels 1.3.72 and later only!
- Written 1996 by Donald Becker.
+ Written 1996-1997 by Donald Becker.
This software may be used and distributed according to the terms
of the GNU Public License, incorporated herein by reference.
@@ -19,7 +19,7 @@
*/
static const char *version =
-"eepro100.c:v0.31 3/29/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
+"eepro100.c:v0.34 8/30/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
/* A few user-configurable values that apply to all boards.
First set are undocumented and spelled per Intel recommendations. */
@@ -36,14 +36,17 @@ static int rxdmacount = 0; /* Rx DMA length, 0 means no preemption. */
#define SKBUFF_RX_COPYBREAK 256
#include <linux/config.h>
-#include <linux/version.h>
#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
#include <linux/module.h>
#else
#define MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#endif
+#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
@@ -55,7 +58,6 @@ static int rxdmacount = 0; /* Rx DMA length, 0 means no preemption. */
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/bios32.h>
-#include <linux/init.h>
#include <asm/processor.h> /* Processor type for cache alignment. */
#include <asm/bitops.h>
#include <asm/io.h>
@@ -113,6 +115,10 @@ struct device *init_etherdev(struct device *dev, int sizeof_priv,
#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
#endif
+#if (LINUX_VERSION_CODE < 0x20123)
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
+
/* The total I/O port extent of the board. Nominally 0x18, but rounded up
for PCI allocation. */
#define SPEEDO3_TOTAL_SIZE 0x20
@@ -143,7 +149,7 @@ PCI bus devices are configured by the system at boot time, so no jumpers
need to be set on the board. The system BIOS should be set to assign the
PCI INTA signal to an otherwise unused system IRQ line. While it's
possible to share PCI interrupt lines, it negatively impacts performance and
-only recent kernels support it.
+only recent kernels support it.
III. Driver operation
@@ -421,13 +427,17 @@ const char basic_config_cmd[22] = {
0x3f, 0x05, };
/* PHY media interface chips. */
-static const char *phys[] = { "None", "i82553-A/B", "i82553-C", "i82503",
- "DP83840", "80c240", "80c24", "unknown" };
+static const char *phys[] = {
+ "None", "i82553-A/B", "i82553-C", "i82503",
+ "DP83840", "80c240", "80c24", "i82555",
+ "unknown-8", "unknown-9", "DP83840A", "unknown-11",
+ "unknown-12", "unknown-13", "unknown-14", "unknown-15", };
enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
- S80C24, PhyUndefined, };
+ S80C24, I82555, DP83840A=10, };
static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
-static void speedo_found1(struct device *dev, int ioaddr, int irq, int options);
+static void speedo_found1(struct device *dev, int ioaddr, int irq,
+ int options, int card_idx);
static int read_eeprom(int ioaddr, int location);
static int mdio_read(int ioaddr, int phy_id, int location);
@@ -448,18 +458,22 @@ static void set_rx_mode(struct device *dev);
-#ifdef MODULE
/* The parameters that may be passed in... */
/* 'options' is used to pass a transceiver override or full-duplex flag
e.g. "options=16" for FD, "options=32" for 100mbps-only. */
+static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+#ifdef MODULE
+
static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
static int debug = -1; /* The debug level */
/* A list of all installed Speedo devices, for removing the driver module. */
static struct device *root_speedo_dev = NULL;
+
#endif
-__initfunc(int eepro100_init(struct device *dev))
+int eepro100_init(struct device *dev)
{
int cards_found = 0;
@@ -510,11 +524,13 @@ __initfunc(int eepro100_init(struct device *dev))
printk(" PCI latency timer (CFLT) is %#x.\n", pci_latency);
#ifdef MODULE
- speedo_found1(dev, pci_ioaddr, pci_irq_line, options[cards_found]);
+ speedo_found1(dev, pci_ioaddr, pci_irq_line, options[cards_found],
+ cards_found);
#else
speedo_found1(dev, pci_ioaddr, pci_irq_line,
- dev ? dev->mem_start : 0);
+ dev ? dev->mem_start : 0, -1);
#endif
+ dev = NULL;
cards_found++;
}
}
@@ -522,7 +538,8 @@ __initfunc(int eepro100_init(struct device *dev))
return cards_found;
}
-__initfunc(static void speedo_found1(struct device *dev, int ioaddr, int irq, int options))
+static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
+ int card_idx)
{
static int did_version = 0; /* Already printed version info. */
struct speedo_private *sp;
@@ -591,26 +608,27 @@ __initfunc(static void speedo_found1(struct device *dev, int ioaddr, int irq, in
if (eeprom[5] & (1<<i))
printk(connectors[i]);
printk("\n"KERN_INFO" Primary interface chip %s PHY #%d.\n",
- phys[(eeprom[6]>>8)&7], eeprom[6] & 0x1f);
+ phys[(eeprom[6]>>8)&15], eeprom[6] & 0x1f);
if (eeprom[7] & 0x0700)
printk(KERN_INFO " Secondary interface chip %s.\n",
phys[(eeprom[7]>>8)&7]);
#if defined(notdef)
/* ToDo: Read and set PHY registers through MDIO port. */
for (i = 0; i < 2; i++)
- printk(" MDIO register %d is %4.4x.\n",
+ printk(KERN_INFO" MDIO register %d is %4.4x.\n",
i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
for (i = 5; i < 7; i++)
- printk(" MDIO register %d is %4.4x.\n",
+ printk(KERN_INFO" MDIO register %d is %4.4x.\n",
i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
- printk(" MDIO register %d is %4.4x.\n",
+ printk(KERN_INFO" MDIO register %d is %4.4x.\n",
25, mdio_read(ioaddr, eeprom[6] & 0x1f, 25));
#endif
- if (((eeprom[6]>>8) & 0x3f) == DP83840) {
+ if (((eeprom[6]>>8) & 0x3f) == DP83840
+ || ((eeprom[6]>>8) & 0x3f) == DP83840A) {
int mdi_reg23 = mdio_read(ioaddr, eeprom[6] & 0x1f, 23) | 0x0422;
if (congenb)
mdi_reg23 |= 0x0100;
- printk(" DP83840 specific setup, setting register 23 to %4.4x.\n",
+ printk(KERN_INFO" DP83840 specific setup, setting register 23 to %4.4x.\n",
mdi_reg23);
mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23);
}
@@ -642,7 +660,7 @@ __initfunc(static void speedo_found1(struct device *dev, int ioaddr, int irq, in
KERN_ERR " Verify that the card is a bus-master"
" capable slot.\n",
self_test_results[1]);
- } else
+ } else
printk(KERN_INFO " General self-test: %s.\n"
KERN_INFO " Serial sub-system self-test: %s.\n"
KERN_INFO " Internal registers self-test: %s.\n"
@@ -670,7 +688,11 @@ __initfunc(static void speedo_found1(struct device *dev, int ioaddr, int irq, in
root_speedo_dev = dev;
#endif
- sp->full_duplex = options >= 0 && (options & 0x10) ? 1 : 0;
+ if (card_idx >= 0) {
+ if (full_duplex[card_idx] >= 0)
+ sp->full_duplex = full_duplex[card_idx];
+ } else
+ sp->full_duplex = options >= 0 && (options & 0x10) ? 1 : 0;
sp->default_port = options >= 0 ? (options & 0x0f) : 0;
sp->phy[0] = eeprom[6];
@@ -715,16 +737,16 @@ __initfunc(static void speedo_found1(struct device *dev, int ioaddr, int irq, in
#define EE_READ_CMD (6 << 6)
#define EE_ERASE_CMD (7 << 6)
-__initfunc(static int read_eeprom(int ioaddr, int location))
+static int read_eeprom(int ioaddr, int location)
{
int i;
unsigned short retval = 0;
int ee_addr = ioaddr + SCBeeprom;
int read_cmd = location | EE_READ_CMD;
-
+
outw(EE_ENB & ~EE_CS, ee_addr);
outw(EE_ENB, ee_addr);
-
+
/* Shift the read command bits out. */
for (i = 10; i >= 0; i--) {
short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
@@ -736,7 +758,7 @@ __initfunc(static int read_eeprom(int ioaddr, int location))
eeprom_delay(250);
}
outw(EE_ENB, ee_addr);
-
+
for (i = 15; i >= 0; i--) {
outw(EE_ENB | EE_SHIFT_CLK, ee_addr);
eeprom_delay(100);
@@ -830,22 +852,19 @@ speedo_open(struct device *dev)
/* Load the statistics block address. */
outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd);
- synchronize_irq();
sp->lstats.done_marker = 0;
speedo_init_rx_ring(dev);
outl(0, ioaddr + SCBPointer);
outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
- synchronize_irq();
/* Todo: verify that we must wait for previous command completion. */
wait_for_cmd_done(ioaddr + SCBCmd);
outl(virt_to_bus(sp->rx_ringp[0]), ioaddr + SCBPointer);
outw(INT_MASK | RX_START, ioaddr + SCBCmd);
- synchronize_irq();
/* Fill the first command with our physical address. */
- {
+ {
unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
unsigned short *setup_frm = (short *)&(sp->tx_ring[0].tx_desc_addr);
@@ -863,7 +882,6 @@ speedo_open(struct device *dev)
outl(0, ioaddr + SCBPointer);
outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd);
- synchronize_irq();
dev->if_port = sp->default_port;
@@ -988,29 +1006,33 @@ static void speedo_tx_timeout(struct device *dev)
"command %4.4x.\n",
dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd));
#ifndef final_version
- printk("%s: Tx timeout fill index %d scavenge index %d.\n",
+ printk(KERN_WARNING "%s: Tx timeout fill index %d scavenge index %d.\n",
dev->name, sp->cur_tx, sp->dirty_tx);
- printk(" Tx queue ");
+ printk(KERN_WARNING " Tx queue ");
for (i = 0; i < TX_RING_SIZE; i++)
printk(" %8.8x", (int)sp->tx_ring[i].status);
- printk(".\n Rx ring ");
+ printk(".\n" KERN_WARNING " Rx ring ");
for (i = 0; i < RX_RING_SIZE; i++)
printk(" %8.8x", (int)sp->rx_ringp[i]->status);
printk(".\n");
-
+
#else
dev->if_port ^= 1;
- printk(" (Media type switching not yet implemented.)\n");
+ printk(KERN_WARNING " (Media type switching not yet implemented.)\n");
/* Do not do 'dev->tbusy = 0;' there -- it is incorrect. */
#endif
if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) {
- printk("%s: Trying to restart the transmitter...\n", dev->name);
+ printk(KERN_WARNING "%s: Trying to restart the transmitter...\n",
+ dev->name);
outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]),
ioaddr + SCBPointer);
outw(CU_START, ioaddr + SCBCmd);
} else {
outw(DRVR_INT, ioaddr + SCBCmd);
}
+ /* Reset the MII transceiver. */
+ if ((sp->phy[0] & 0x8000) == 0)
+ mdio_write(ioaddr, sp->phy[0] & 0x1f, 0, 0x8000);
sp->stats.tx_errors++;
dev->trans_start = jiffies;
return;
@@ -1151,7 +1173,7 @@ static void speedo_interrupt(int irq, struct pt_regs *regs)
printk(KERN_WARNING"%s: The EEPro100 receiver left the ready"
" state -- %4.4x! Index %d (%d).\n", dev->name, status,
sp->cur_rx, sp->cur_rx % RX_RING_SIZE);
- printk(" Rx ring:\n ");
+ printk(KERN_WARNING " Rx ring:\n ");
for (i = 0; i < RX_RING_SIZE; i++)
printk(" %d %8.8x %8.8x %8.8x %d %d.\n",
i, sp->rx_ringp[i]->status, sp->rx_ringp[i]->link,
@@ -1194,7 +1216,8 @@ static void speedo_interrupt(int irq, struct pt_regs *regs)
#ifndef final_version
if (sp->cur_tx - dirty_tx > TX_RING_SIZE) {
- printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+ printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d,"
+ " full=%d.\n",
dirty_tx, sp->cur_tx, sp->tx_full);
dirty_tx += TX_RING_SIZE;
}
@@ -1212,7 +1235,7 @@ static void speedo_interrupt(int irq, struct pt_regs *regs)
}
if (--boguscnt < 0) {
- printk("%s: Too much work at interrupt, status=0x%4.4x.\n",
+ printk(KERN_ERR "%s: Too much work at interrupt, status=0x%4.4x.\n",
dev->name, status);
/* Clear all interrupt sources. */
outl(0xfc00, ioaddr + SCBStatus);
@@ -1250,7 +1273,7 @@ speedo_rx(struct device *dev)
struct speedo_private *sp = (struct speedo_private *)dev->priv;
int entry = sp->cur_rx % RX_RING_SIZE;
int status;
-
+
if (speedo_debug > 4)
printk(KERN_DEBUG " In speedo_rx().\n");
/* If we own the next entry, it's a new packet. Send it up. */
@@ -1260,12 +1283,12 @@ speedo_rx(struct device *dev)
printk(KERN_DEBUG " speedo_rx() status %8.8x len %d.\n", status,
sp->rx_ringp[entry]->count & 0x3fff);
if (status & 0x0200) {
- printk("%s: Ethernet frame overran the Rx buffer, status %8.8x!\n",
- dev->name, status);
+ printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, "
+ "status %8.8x!\n", dev->name, status);
} else if ( ! (status & 0x2000)) {
/* There was a fatal error. This *should* be impossible. */
sp->stats.rx_errors++;
- printk("%s: Anomalous event in speedo_rx(), status %8.8x.\n",
+ printk(KERN_ERR "%s: Anomalous event in speedo_rx(), status %8.8x.\n",
dev->name, status);
} else {
/* Malloc up new buffer, compatible with net-2e. */
@@ -1284,7 +1307,7 @@ speedo_rx(struct device *dev)
#ifdef KERNEL_1_2
temp = skb->data;
if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
- printk("%s: Warning -- the skbuff addresses do not match"
+ printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match"
" in speedo_rx: %p vs. %p / %p.\n", dev->name,
bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
temp, skb->data);
@@ -1293,7 +1316,7 @@ speedo_rx(struct device *dev)
#else
temp = skb_put(skb, pkt_len);
if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
- printk("%s: Warning -- the skbuff addresses do not match"
+ printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match"
" in speedo_rx: %8.8x vs. %p / %p.\n", dev->name,
sp->rx_ringp[entry]->rx_buf_addr, skb->head, temp);
/* Get a fresh skbuff to replace the filled one. */
@@ -1330,7 +1353,7 @@ speedo_rx(struct device *dev)
#endif
if (skb == NULL) {
int i;
- printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+ printk(KERN_ERR "%s: Memory squeeze, deferring packet.\n", dev->name);
/* Check that at least two ring entries are free.
If not, free one and mark stats->rx_dropped++. */
/* ToDo: This is not correct!!!! We should count the number
@@ -1377,7 +1400,7 @@ speedo_rx(struct device *dev)
}
/* ToDo: This is better than before, but should be checked. */
- {
+ {
struct RxFD *rxf = sp->rx_ringp[entry];
rxf->status = 0xC0000003; /* '3' for verification only */
rxf->link = 0; /* None yet. */
@@ -1414,7 +1437,6 @@ speedo_close(struct device *dev)
/* Disable interrupts, and stop the chip's Rx process. */
outw(INT_MASK, ioaddr + SCBCmd);
outw(INT_MASK | RX_ABORT, ioaddr + SCBCmd);
- synchronize_irq();
#ifdef SA_SHIRQ
free_irq(dev->irq, dev);
@@ -1446,19 +1468,20 @@ speedo_close(struct device *dev)
/* Print a few items for debugging. */
if (speedo_debug > 3) {
- printk("%s:Printing Rx ring (next to receive into %d).\n",
+ int phy_num = sp->phy[0] & 0x1f;
+ printk(KERN_DEBUG "%s:Printing Rx ring (next to receive into %d).\n",
dev->name, sp->cur_rx);
for (i = 0; i < RX_RING_SIZE; i++)
- printk(" Rx ring entry %d %8.8x.\n",
+ printk(KERN_DEBUG " Rx ring entry %d %8.8x.\n",
i, (int)sp->rx_ringp[i]->status);
for (i = 0; i < 5; i++)
- printk(" PHY index %d register %d is %4.4x.\n",
- 1, i, mdio_read(ioaddr, 1, i));
+ printk(KERN_DEBUG " PHY index %d register %d is %4.4x.\n",
+ phy_num, i, mdio_read(ioaddr, phy_num, i));
for (i = 21; i < 26; i++)
- printk(" PHY index %d register %d is %4.4x.\n",
- 1, i, mdio_read(ioaddr, 1, i));
+ printk(KERN_DEBUG " PHY index %d register %d is %4.4x.\n",
+ phy_num, i, mdio_read(ioaddr, phy_num, i));
}
MOD_DEC_USE_COUNT;
@@ -1529,7 +1552,7 @@ set_rx_mode(struct device *dev)
if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) {
/* The Tx ring is full -- don't add anything! Presumably the new mode
- is in config_cmd_data and will be added anyway. */
+ is in config_cmd_data and will be added anyway. */
sp->rx_mode = -1;
return;
}
@@ -1565,7 +1588,7 @@ set_rx_mode(struct device *dev)
restore_flags(flags);
if (speedo_debug > 5) {
int i;
- printk(" CmdConfig frame in entry %d.\n", entry);
+ printk(KERN_DEBUG " CmdConfig frame in entry %d.\n", entry);
for(i = 0; i < 32; i++)
printk(" %2.2x", ((unsigned char *)&sp->config_cmd)[i]);
printk(".\n");
@@ -1618,20 +1641,19 @@ set_rx_mode(struct device *dev)
if (sp->mc_setup_frm)
kfree(sp->mc_setup_frm);
sp->mc_setup_frm_len = 10 + dev->mc_count*6 + 24;
- printk("%s: Allocating a setup frame of size %d.\n",
- dev->name, sp->mc_setup_frm_len);
- sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len,
- in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len, GFP_ATOMIC);
if (sp->mc_setup_frm == NULL) {
- printk("%s: Failed to allocate a setup frame.\n", dev->name);
+ printk(KERN_ERR "%s: Failed to allocate a setup frame.\n", dev->name);
sp->rx_mode = -1; /* We failed, try again. */
return;
}
}
mc_setup_frm = sp->mc_setup_frm;
/* Construct the new setup frame. */
- printk("%s: Constructing a setup frame at %p, %d bytes.\n",
- dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len);
+ if (speedo_debug > 1)
+ printk(KERN_DEBUG "%s: Constructing a setup frame at %p, "
+ "%d bytes.\n",
+ dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len);
mc_setup_frm->status = 0;
mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList;
/* Link set below. */
@@ -1645,7 +1667,7 @@ set_rx_mode(struct device *dev)
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
}
-
+
/* Disable interrupts while playing with the Tx Cmd list. */
save_flags(flags);
cli();
@@ -1669,8 +1691,9 @@ set_rx_mode(struct device *dev)
outw(CU_RESUME, ioaddr + SCBCmd);
sp->last_cmd = mc_setup_frm;
restore_flags(flags);
- printk("%s: Last command at %p is %4.4x.\n",
- dev->name, sp->last_cmd, sp->last_cmd->command);
+ if (speedo_debug > 1)
+ printk(KERN_DEBUG "%s: Last command at %p is %4.4x.\n",
+ dev->name, sp->last_cmd, sp->last_cmd->command);
}
sp->rx_mode = new_rx_mode;
@@ -1693,7 +1716,7 @@ init_module(void)
root_speedo_dev = NULL;
cards_found = eepro100_init(NULL);
- return cards_found < 0 ? cards_found : 0;
+ return cards_found ? 0 : -ENODEV;
}
void
@@ -1711,7 +1734,7 @@ cleanup_module(void)
}
}
#else /* not MODULE */
-__initfunc(int eepro100_probe(struct device *dev))
+int eepro100_probe(struct device *dev)
{
int cards_found = 0;
@@ -1726,8 +1749,9 @@ __initfunc(int eepro100_probe(struct device *dev))
/*
* Local variables:
- * compile-command: "gcc -DCONFIG_MODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c"
+ * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c"
* c-indent-level: 4
+ * c-basic-offset: 4
* tab-width: 4
* End:
*/
diff --git a/drivers/net/ethertap.c b/drivers/net/ethertap.c
new file mode 100644
index 000000000..1ae6a636e
--- /dev/null
+++ b/drivers/net/ethertap.c
@@ -0,0 +1,225 @@
+/*
+ * Ethertap: A network device for bouncing packets via user space
+ *
+ * This is a very simple ethernet driver. It bounces ethernet frames
+ * to user space on /dev/tap0->/dev/tap15 and expects ethernet frames
+ * to be written back to it. By default it does not ARP. If you turn ARP
+ * on it will attempt to ARP the user space and reply to ARPS from the
+ * user space.
+ *
+ * As this is an ethernet device you cau use it for appletalk, IPX etc
+ * even for building bridging tunnels.
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+
+#include <net/netlink.h>
+
+/*
+ * Index to functions.
+ */
+
+int ethertap_probe(struct device *dev);
+static int ethertap_open(struct device *dev);
+static int ethertap_start_xmit(struct sk_buff *skb, struct device *dev);
+static int ethertap_close(struct device *dev);
+static struct net_device_stats *ethertap_get_stats(struct device *dev);
+static int ethertap_rx(int id, struct sk_buff *skb);
+
+static int ethertap_debug = 0;
+
+static struct device *tap_map[32]; /* Returns the tap device for a given netlink */
+
+/*
+ * Board-specific info in dev->priv.
+ */
+
+struct net_local
+{
+ struct net_device_stats stats;
+};
+
+/*
+ * To call this a probe is a bit misleading, however for real
+ * hardware it would have to check what was present.
+ */
+
+__initfunc(int ethertap_probe(struct device *dev))
+{
+ memcpy(dev->dev_addr, "\xFD\xFD\x00\x00\x00\x00", 6);
+ if (dev->mem_start & 0xf)
+ ethertap_debug = dev->mem_start & 0x7;
+
+ /*
+ * Initialize the device structure.
+ */
+
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+ /*
+ * The tap specific entries in the device structure.
+ */
+
+ dev->open = ethertap_open;
+ dev->hard_start_xmit = ethertap_start_xmit;
+ dev->stop = ethertap_close;
+ dev->get_stats = ethertap_get_stats;
+
+ /*
+ * Setup the generic properties
+ */
+
+ ether_setup(dev);
+
+ dev->flags|=IFF_NOARP; /* Need to set ARP - looks like there is a bug
+ in the 2.1.x hard header code currently */
+ tap_map[dev->base_addr]=dev;
+
+ return 0;
+}
+
+/*
+ * Open/initialize the board.
+ */
+
+static int ethertap_open(struct device *dev)
+{
+ if (ethertap_debug > 2)
+ printk("%s: Doing ethertap_open()...", dev->name);
+ netlink_attach(dev->base_addr, ethertap_rx);
+ dev->start = 1;
+ dev->tbusy = 0;
+ /* Fill in the MAC based on the IP address. We do the same thing
+ here as PLIP does */
+ memcpy(dev->dev_addr+2,&dev->pa_addr,4);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * We transmit by throwing the packet at netlink. We have to clone
+ * it for 2.0 so that we dev_kfree_skb() the locked original.
+ */
+
+static int ethertap_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ struct sk_buff *tmp;
+ /* copy buffer to tap */
+ tmp=skb_clone(skb, GFP_ATOMIC);
+ if(tmp)
+ {
+ if(netlink_post(dev->base_addr, tmp)<0)
+ kfree_skb(tmp, FREE_WRITE);
+ lp->stats.tx_bytes+=skb->len;
+ lp->stats.tx_packets++;
+ }
+ dev_kfree_skb (skb, FREE_WRITE);
+ return 0;
+}
+
+/*
+ * The typical workload of the driver:
+ * Handle the ether interface interrupts.
+ *
+ * (In this case handle the packets posted from user space..)
+ */
+
+static int ethertap_rx(int id, struct sk_buff *skb)
+{
+ struct device *dev = (struct device *)(tap_map[id]);
+ struct net_local *lp;
+ int len=skb->len;
+
+ if(dev==NULL)
+ {
+ printk("%s: bad unit!\n",dev->name);
+ kfree_skb(skb, FREE_WRITE);
+ return -ENXIO;
+ }
+ lp = (struct net_local *)dev->priv;
+
+ if (ethertap_debug > 3)
+ printk("%s: ethertap_rx()\n", dev->name);
+ skb->dev = dev;
+ skb->protocol=eth_type_trans(skb,dev);
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes+=len;
+ netif_rx(skb);
+ return len;
+}
+
+static int ethertap_close(struct device *dev)
+{
+ if (ethertap_debug > 2)
+ printk("%s: Shutting down tap %ld.\n", dev->name, dev->base_addr);
+
+ dev->tbusy = 1;
+ dev->start = 0;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct net_device_stats *ethertap_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ return &lp->stats;
+}
+
+#ifdef MODULE
+
+int unit;
+MODULE_PARM(unit,"i");
+
+static char devicename[9] = { 0, };
+
+static struct device dev_ethertap =
+{
+ devicename,
+ 0, 0, 0, 0,
+ 1, 5,
+ 0, 0, 0, NULL, ethertap_probe
+};
+
+int init_module(void)
+{
+ dev_ethertap.base_addr=unit+NETLINK_TAPBASE;
+ sprintf(devicename,"tap%d",unit);
+ if (dev_get(devicename))
+ {
+ printk(KERN_INFO "ethertap: tap %d already loaded.\n", unit);
+ return -EBUSY;
+ }
+ if (register_netdev(&dev_ethertap) != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ tap_map[dev_ethertap.base_addr]=NULL;
+ unregister_netdev(&dev_ethertap);
+
+ /*
+ * Free up the private structure.
+ */
+
+ kfree(dev_ethertap.priv);
+ dev_ethertap.priv = NULL; /* gets re-allocated by ethertap_probe */
+}
+
+#endif /* MODULE */
diff --git a/drivers/net/hdlcdrv.c b/drivers/net/hdlcdrv.c
index 666bf3858..59c72f914 100644
--- a/drivers/net/hdlcdrv.c
+++ b/drivers/net/hdlcdrv.c
@@ -33,6 +33,8 @@
* 0.2 21.11.96 various small changes
* 0.3 03.03.97 fixed (hopefully) IP not working with ax.25 as a module
* 0.4 16.04.97 init code/data tagged
+ * 0.5 30.07.97 made HDLC buffers bigger (solves a problem with the
+ * soundmodem driver)
*/
/*****************************************************************************/
@@ -1019,7 +1021,7 @@ MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder");
__initfunc(int init_module(void))
{
printk(KERN_INFO "hdlcdrv: (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
- printk(KERN_INFO "hdlcdrv: version 0.4 compiled " __TIME__ " " __DATE__ "\n");
+ printk(KERN_INFO "hdlcdrv: version 0.5 compiled " __TIME__ " " __DATE__ "\n");
#if LINUX_VERSION_CODE < 0x20115
register_symtab(&hdlcdrv_syms);
#endif
diff --git a/drivers/net/ibmtr.c b/drivers/net/ibmtr.c
index ad3a6eafd..2beae46e0 100644
--- a/drivers/net/ibmtr.c
+++ b/drivers/net/ibmtr.c
@@ -58,6 +58,10 @@
*
* Changes by Christopher Turcksin <wabbit@rtfc.demon.co.uk>
* + Now compiles ok as a module again.
+ *
+ * Changes by Paul Norton (pnorton@cts.com) :
+ * + moved the header manipulation code in tr_tx and tr_rx to
+ * net/802/tr.c. (July 12 1997)
*/
#ifdef PCMCIA
@@ -87,7 +91,7 @@
/* version and credits */
static char *version =
"ibmtr.c: v1.3.57 8/ 7/94 Peter De Schrijver and Mark Swanson\n"
-" v2.1.35 5/ 1/97 Paul Norton <pnorton@cts.com>\n";
+" v2.1.42 7/12/97 Paul Norton <pnorton@cts.com>\n";
static char pcchannelid[] = {
0x05, 0x00, 0x04, 0x09,
@@ -168,8 +172,8 @@ int ibmtr_probe(struct device *dev);
static int ibmtr_probe1(struct device *dev, int ioaddr);
static unsigned char get_sram_size(struct tok_info *adapt_info);
static int tok_init_card(struct device *dev);
-static int trdev_init(struct device *dev);
void tok_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int trdev_init(struct device *dev);
static void initial_tok_int(struct device *dev);
static void open_sap(unsigned char type,struct device *dev);
void tok_open_adapter(unsigned long dev_addr);
@@ -1249,7 +1253,15 @@ static void tr_tx(struct device *dev)
effective address where we will place data.*/
dhb=ti->sram
+ntohs(readw(ti->arb + offsetof(struct arb_xmit_req, dhb_address)));
- llc = (struct trllc *) &(ti->current_skb->data[sizeof(struct trh_hdr)]);
+
+ /* Figure out the size of the 802.5 header */
+ if (!(trhdr->saddr[0] & 0x80)) /* RIF present? */
+ hdr_len=sizeof(struct trh_hdr)-18;
+ else
+ hdr_len=((ntohs(trhdr->rcf) & TR_RCF_LEN_MASK)>>8)
+ +sizeof(struct trh_hdr)-18;
+
+ llc = (struct trllc *)(ti->current_skb->data + hdr_len);
xmit_command = readb(ti->srb + offsetof(struct srb_xmit, command));
@@ -1277,39 +1289,15 @@ static void tr_tx(struct device *dev)
}
- /* the token ring packet is copied from sk_buff to the adapter
- buffer identified in the command data received with the
- interrupt. The sk_buff area was set up with a maximum
- sized route information field so here we must compress
- out the extra (all) rif fields. */
- /* nb/dwm .... I re-arranged code here to avoid copy of extra
- bytes, ended up with fewer statements as well. */
-
- /* TR arch. identifies if RIF present by high bit of source
- address. So here we check if RIF present */
-
- if (!(trhdr->saddr[0] & 0x80)) { /* RIF present : preserve it */
- hdr_len=sizeof(struct trh_hdr)-18;
-
-#if TR_VERBOSE
- DPRINTK("hdr_length: %d, frame length: %ld\n", hdr_len,
- ti->current_skb->len-18);
-#endif
- } else hdr_len=((ntohs(trhdr->rcf) & TR_RCF_LEN_MASK)>>8)
- +sizeof(struct trh_hdr)-18;
-
- /* header length including rif is computed above, now move the data
- and set fields appropriately. */
- memcpy_toio(dhb, ti->current_skb->data, hdr_len);
-
+ /*
+ * the token ring packet is copied from sk_buff to the adapter
+ * buffer identified in the command data received with the interrupt.
+ */
writeb(hdr_len, ti->asb + offsetof(struct asb_xmit_resp, hdr_length));
- writew(htons(ti->current_skb->len-sizeof(struct trh_hdr)+hdr_len),
+ writew(htons(ti->current_skb->len),
ti->asb + offsetof(struct asb_xmit_resp, frame_length));
- /* now copy the actual packet data next to hdr */
- memcpy_toio(dhb + hdr_len,
- ti->current_skb->data + sizeof(struct trh_hdr),
- ti->current_skb->len - sizeof(struct trh_hdr));
+ memcpy_toio(dhb, ti->current_skb->data, ti->current_skb->len);
writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD);
dev->tbusy=0;
@@ -1328,7 +1316,7 @@ static void tr_rx(struct device *dev)
unsigned int rbuffer_len, lan_hdr_len, hdr_len, ip_len, length;
struct sk_buff *skb;
unsigned int skb_size = 0;
- int is8022 = 0;
+ int IPv4_p = 0;
unsigned int chksum = 0;
struct iphdr *iph;
@@ -1347,7 +1335,7 @@ static void tr_rx(struct device *dev)
lan_hdr_len=readb(ti->arb + offsetof(struct arb_rec_req, lan_hdr_len));
- llc=(rbuffer+offsetof(struct rec_buf, data) + lan_hdr_len);
+ llc=(rbuffer + offsetof(struct rec_buf, data) + lan_hdr_len);
#if TR_VERBOSE
DPRINTK("offsetof data: %02X lan_hdr_len: %02X\n",
@@ -1366,19 +1354,19 @@ static void tr_rx(struct device *dev)
(int)readw(llc + offsetof(struct trllc, ethertype)));
#endif
if (readb(llc + offsetof(struct trllc, llc))!=UI_CMD) {
- writeb(DATA_LOST, ti->asb + offsetof(struct asb_rec, ret_code));
- ti->tr_stats.rx_dropped++;
- writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD);
- return;
+ writeb(DATA_LOST, ti->asb + offsetof(struct asb_rec, ret_code));
+ ti->tr_stats.rx_dropped++;
+ writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD);
+ return;
}
- if ((readb(llc + offsetof(struct trllc, dsap))!=0xAA) ||
- (readb(llc + offsetof(struct trllc, ssap))!=0xAA)) {
- is8022 = 1;
+ if ((readb(llc + offsetof(struct trllc, dsap))==0xAA) &&
+ (readb(llc + offsetof(struct trllc, ssap))==0xAA)) {
+ IPv4_p = 1;
}
#if TR_VERBOSE
- if (is8022){
+ if (!IPv4_p){
__u32 trhhdr;
@@ -1405,11 +1393,8 @@ static void tr_rx(struct device *dev)
#endif
length = ntohs(readw(ti->arb+offsetof(struct arb_rec_req, frame_len)));
- skb_size = length-lan_hdr_len+sizeof(struct trh_hdr);
- if (is8022) {
- skb_size += sizeof(struct trllc);
- }
-
+ skb_size = length-lan_hdr_len+sizeof(struct trh_hdr)+sizeof(struct trllc);
+
if (!(skb=dev_alloc_skb(skb_size))) {
DPRINTK("out of memory. frame dropped.\n");
ti->tr_stats.rx_dropped++;
@@ -1418,53 +1403,37 @@ static void tr_rx(struct device *dev)
return;
}
- skb_put(skb, skb_size);
+ skb_put(skb, length);
+ skb_reserve(skb, sizeof(struct trh_hdr)-lan_hdr_len+sizeof(struct trllc));
skb->dev=dev;
-
data=skb->data;
+ rbuffer_len=ntohs(readw(rbuffer + offsetof(struct rec_buf, buf_len)));
+ rbufdata = rbuffer + offsetof(struct rec_buf,data);
- /* Copy the 802.5 MAC header */
- memcpy_fromio(data, rbuffer + offsetof(struct rec_buf, data), lan_hdr_len);
-
- if (lan_hdr_len<sizeof(struct trh_hdr))
- memset(data+lan_hdr_len, 0, sizeof(struct trh_hdr)-lan_hdr_len);
-
- data+=sizeof(struct trh_hdr);
- rbuffer_len=ntohs(readw(rbuffer + offsetof(struct rec_buf, buf_len)))-lan_hdr_len;
-
- if (is8022) {
- /* create whitewashed LLC header in sk buffer */
- struct trllc *local_llc = (struct trllc *)data;
- memset(local_llc, 0, sizeof(*local_llc));
- local_llc->ethertype = htons(ETH_P_TR_802_2);
- hdr_len = sizeof(struct trllc);
- /* copy the real LLC header to the sk buffer */
- data += hdr_len;
- memcpy_fromio(data, rbuffer+offsetof(struct rec_buf, data)+lan_hdr_len,hdr_len);
- } else {
- /* Copy the LLC header and the IPv4 header */
- hdr_len = sizeof(struct trllc) + sizeof(struct iphdr);
- memcpy_fromio(data, rbuffer+offsetof(struct rec_buf, data)+lan_hdr_len,hdr_len);
+ if (IPv4_p) {
+ /* Copy the headers without checksumming */
+ hdr_len = lan_hdr_len + sizeof(struct trllc) + sizeof(struct iphdr);
+ memcpy_fromio(data, rbufdata, hdr_len);
/* Watch for padded packets and bogons */
- iph=(struct iphdr*)(data+sizeof(struct trllc));
+ iph=(struct iphdr*)(data + lan_hdr_len + sizeof(struct trllc));
ip_len = ntohs(iph->tot_len) - sizeof(struct iphdr);
- length -= lan_hdr_len + hdr_len;
+ length -= hdr_len;
if ((ip_len <= length) && (ip_len > 7))
length = ip_len;
+ data += hdr_len;
+ rbuffer_len -= hdr_len;
+ rbufdata += hdr_len;
}
- data += hdr_len;
- rbuffer_len -= hdr_len;
- rbufdata = rbuffer + offsetof(struct rec_buf,data) + lan_hdr_len + hdr_len;
/* Copy the payload... */
for (;;) {
- if (is8022)
- memcpy_fromio(data, rbufdata, rbuffer_len);
- else
+ if (IPv4_p)
chksum = csum_partial_copy(bus_to_virt(rbufdata), data,
length < rbuffer_len ? length : rbuffer_len,
chksum);
+ else
+ memcpy_fromio(data, rbufdata, rbuffer_len);
rbuffer = ntohs(readw(rbuffer));
if (!rbuffer)
break;
@@ -1481,11 +1450,14 @@ static void tr_rx(struct device *dev)
ti->tr_stats.rx_packets++;
- skb->protocol = tr_type_trans(skb,dev);
- if (!is8022){
+ tr_reformat(skb, lan_hdr_len);
+ skb->protocol = tr_type_trans(skb,dev);
+
+ if (IPv4_p){
skb->csum = chksum;
skb->ip_summed = 1;
}
+
netif_rx(skb);
}
diff --git a/drivers/net/mace.c b/drivers/net/mace.c
new file mode 100644
index 000000000..bcc630f4c
--- /dev/null
+++ b/drivers/net/mace.c
@@ -0,0 +1,800 @@
+/*
+ * Network device driver for the MACE ethernet controller on
+ * Apple Powermacs. Assumes it's under a DBDMA controller.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <asm/prom.h>
+#include <asm/dbdma.h>
+#include <asm/io.h>
+#include "mace.h"
+
+#define N_RX_RING 8
+#define N_TX_RING 6
+#define MAX_TX_ACTIVE 1
+#define NCMDS_TX 1 /* dma commands per element in tx ring */
+#define RX_BUFLEN (ETH_FRAME_LEN + 8)
+#define TX_TIMEOUT HZ /* 1 second */
+
+/* Bits in transmit DMA status */
+#define TX_DMA_ERR 0x80
+
+struct mace_data {
+ volatile struct mace *mace;
+ volatile struct dbdma_regs *tx_dma;
+ int tx_dma_intr;
+ volatile struct dbdma_regs *rx_dma;
+ int rx_dma_intr;
+ volatile struct dbdma_cmd *tx_cmds; /* xmit dma command list */
+ volatile struct dbdma_cmd *rx_cmds; /* recv dma command list */
+ struct sk_buff *rx_bufs[N_RX_RING];
+ int rx_fill;
+ int rx_empty;
+ struct sk_buff *tx_bufs[N_TX_RING];
+ int tx_fill;
+ int tx_empty;
+ unsigned char maccc;
+ unsigned char tx_fullup;
+ unsigned char tx_active;
+ unsigned char tx_bad_runt;
+ struct net_device_stats stats;
+ struct timer_list tx_timeout;
+};
+
+/*
+ * Number of bytes of private data per MACE: allow enough for
+ * the rx and tx dma commands plus a branch dma command each,
+ * and another 16 bytes to allow us to align the dma command
+ * buffers on a 16 byte boundary.
+ */
+#define PRIV_BYTES (sizeof(struct mace_data) \
+ + (N_RX_RING + NCMDS_TX * N_TX_RING + 3) * sizeof(struct dbdma_cmd))
+
+static int bitrev(int);
+static int mace_open(struct device *dev);
+static int mace_close(struct device *dev);
+static int mace_xmit_start(struct sk_buff *skb, struct device *dev);
+static struct net_device_stats *mace_stats(struct device *dev);
+static void mace_set_multicast(struct device *dev);
+static void mace_reset(struct device *dev);
+static int mace_set_address(struct device *dev, void *addr);
+static void mace_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void mace_txdma_intr(int irq, void *dev_id, struct pt_regs *regs);
+static void mace_rxdma_intr(int irq, void *dev_id, struct pt_regs *regs);
+static void mace_set_timeout(struct device *dev);
+static void mace_tx_timeout(unsigned long data);
+
+/*
+ * If we can't get a skbuff when we need it, we use this area for DMA.
+ */
+static unsigned char dummy_buf[RX_BUFLEN+2];
+
+/* Bit-reverse one byte of an ethernet hardware address. */
+static int
+bitrev(int b)
+{
+ int d = 0, i;
+
+ for (i = 0; i < 8; ++i, b >>= 1)
+ d = (d << 1) | (b & 1);
+ return d;
+}
+
+int
+mace_probe(struct device *dev)
+{
+ int j, rev;
+ struct mace_data *mp;
+ struct device_node *maces;
+ unsigned char *addr;
+
+ maces = find_devices("mace");
+ if (maces == 0)
+ return ENODEV;
+
+ do {
+ if (maces->n_addrs != 3 || maces->n_intrs != 3) {
+ printk(KERN_ERR "can't use MACE %s: expect 3 addrs and 3 intrs\n",
+ maces->full_name);
+ continue;
+ }
+
+ if (dev == NULL)
+ dev = init_etherdev(0, PRIV_BYTES);
+ else {
+ /* XXX this doesn't look right (but it's never used :-) */
+ dev->priv = kmalloc(PRIV_BYTES, GFP_KERNEL);
+ if (dev->priv == 0)
+ return -ENOMEM;
+ }
+
+ mp = (struct mace_data *) dev->priv;
+ dev->base_addr = maces->addrs[0].address;
+ mp->mace = (volatile struct mace *) maces->addrs[0].address;
+ dev->irq = maces->intrs[0];
+
+ if (request_irq(dev->irq, mace_interrupt, 0, "MACE", dev)) {
+ printk(KERN_ERR "MACE: can't get irq %d\n", dev->irq);
+ return -EAGAIN;
+ }
+ if (request_irq(maces->intrs[1], mace_txdma_intr, 0, "MACE-txdma",
+ dev)) {
+ printk(KERN_ERR "MACE: can't get irq %d\n", maces->intrs[1]);
+ return -EAGAIN;
+ }
+ if (request_irq(maces->intrs[2], mace_rxdma_intr, 0, "MACE-rxdma",
+ dev)) {
+ printk(KERN_ERR "MACE: can't get irq %d\n", maces->intrs[2]);
+ return -EAGAIN;
+ }
+
+ addr = get_property(maces, "mac-address", NULL);
+ if (addr == NULL) {
+ addr = get_property(maces, "local-mac-address", NULL);
+ if (addr == NULL) {
+ printk(KERN_ERR "Can't get mac-address for MACE at %lx\n",
+ dev->base_addr);
+ return -EAGAIN;
+ }
+ }
+
+ printk(KERN_INFO "%s: MACE at", dev->name);
+ rev = addr[0] == 0 && addr[1] == 0xA0;
+ for (j = 0; j < 6; ++j) {
+ dev->dev_addr[j] = rev? bitrev(addr[j]): addr[j];
+ printk("%c%.2x", (j? ':': ' '), dev->dev_addr[j]);
+ }
+ printk("\n");
+
+ mp = (struct mace_data *) dev->priv;
+ mp->maccc = ENXMT | ENRCV;
+ mp->tx_dma = (volatile struct dbdma_regs *) maces->addrs[1].address;
+ mp->tx_dma_intr = maces->intrs[1];
+ mp->rx_dma = (volatile struct dbdma_regs *) maces->addrs[2].address;
+ mp->rx_dma_intr = maces->intrs[2];
+
+ mp->tx_cmds = (volatile struct dbdma_cmd *) DBDMA_ALIGN(mp + 1);
+ mp->rx_cmds = mp->tx_cmds + NCMDS_TX * N_TX_RING + 1;
+
+ memset(&mp->stats, 0, sizeof(mp->stats));
+ memset((char *) mp->tx_cmds, 0,
+ (NCMDS_TX*N_TX_RING + N_RX_RING + 2) * sizeof(struct dbdma_cmd));
+
+ mace_reset(dev);
+
+ dev->open = mace_open;
+ dev->stop = mace_close;
+ dev->hard_start_xmit = mace_xmit_start;
+ dev->get_stats = mace_stats;
+ dev->set_multicast_list = mace_set_multicast;
+ dev->set_mac_address = mace_set_address;
+
+ ether_setup(dev);
+
+ } while ((maces = maces->next) != 0);
+
+ return 0;
+}
+
+static void mace_reset(struct device *dev)
+{
+ struct mace_data *mp = (struct mace_data *) dev->priv;
+ volatile struct mace *mb = mp->mace;
+ int i;
+
+ /* soft-reset the chip */
+ mb->biucc = SWRST; eieio();
+ udelay(100);
+
+ mb->biucc = XMTSP_64;
+ mb->imr = 0xff; /* disable all intrs for now */
+ i = mb->ir;
+ mb->maccc = 0; /* turn off tx, rx */
+ mb->utr = RTRD;
+ mb->fifocc = RCVFW_64;
+ mb->xmtfc = AUTO_PAD_XMIT; /* auto-pad short frames */
+
+ /* load up the hardware address */
+ mb->iac = ADDRCHG | PHYADDR; eieio();
+ while ((mb->iac & ADDRCHG) != 0)
+ eieio();
+ for (i = 0; i < 6; ++i) {
+ mb->padr = dev->dev_addr[i];
+ eieio();
+ }
+
+ /* clear the multicast filter */
+ mb->iac = ADDRCHG | LOGADDR; eieio();
+ while ((mb->iac & ADDRCHG) != 0)
+ eieio();
+ for (i = 0; i < 8; ++i) {
+ mb->ladrf = 0;
+ eieio();
+ }
+
+ mb->plscc = PORTSEL_GPSI + ENPLSIO;
+}
+
+static int mace_set_address(struct device *dev, void *addr)
+{
+ unsigned char *p = addr;
+ struct mace_data *mp = (struct mace_data *) dev->priv;
+ volatile struct mace *mb = mp->mace;
+ int i;
+ unsigned long flags;
+
+ save_flags(flags); cli();
+
+ /* load up the hardware address */
+ mb->iac = ADDRCHG | PHYADDR; eieio();
+ while ((mb->iac & ADDRCHG) != 0)
+ eieio();
+ for (i = 0; i < 6; ++i) {
+ mb->padr = dev->dev_addr[i] = p[i];
+ eieio();
+ }
+ /* note: setting ADDRCHG clears ENRCV */
+ mb->maccc = mp->maccc; eieio();
+
+ restore_flags(flags);
+ return 0;
+}
+
+static int mace_open(struct device *dev)
+{
+ struct mace_data *mp = (struct mace_data *) dev->priv;
+ volatile struct mace *mb = mp->mace;
+ volatile struct dbdma_regs *rd = mp->rx_dma;
+ volatile struct dbdma_regs *td = mp->tx_dma;
+ volatile struct dbdma_cmd *cp;
+ int i;
+ struct sk_buff *skb;
+ unsigned char *data;
+
+ /* initialize list of sk_buffs for receiving and set up recv dma */
+ memset((char *)mp->rx_cmds, 0, N_RX_RING * sizeof(struct dbdma_cmd));
+ cp = mp->rx_cmds;
+ for (i = 0; i < N_RX_RING - 1; ++i) {
+ skb = dev_alloc_skb(RX_BUFLEN + 2);
+ if (skb == 0) {
+ data = dummy_buf;
+ } else {
+ skb_reserve(skb, 2); /* so IP header lands on 4-byte bdry */
+ data = skb->data;
+ }
+ mp->rx_bufs[i] = skb;
+ st_le16(&cp->req_count, RX_BUFLEN);
+ st_le16(&cp->command, INPUT_LAST + INTR_ALWAYS);
+ st_le32(&cp->phy_addr, virt_to_bus(data));
+ cp->xfer_status = 0;
+ ++cp;
+ }
+ mp->rx_bufs[i] = 0;
+ st_le16(&cp->command, DBDMA_STOP);
+ mp->rx_fill = i;
+ mp->rx_empty = 0;
+
+ /* Put a branch back to the beginning of the receive command list */
+ ++cp;
+ st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
+ st_le32(&cp->cmd_dep, virt_to_bus(mp->rx_cmds));
+
+ /* start rx dma */
+ out_le32(&rd->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */
+ out_le32(&rd->cmdptr, virt_to_bus(mp->rx_cmds));
+ out_le32(&rd->control, (RUN << 16) | RUN);
+
+ /* put a branch at the end of the tx command list */
+ cp = mp->tx_cmds + NCMDS_TX * N_TX_RING;
+ st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
+ st_le32(&cp->cmd_dep, virt_to_bus(mp->tx_cmds));
+
+ /* reset tx dma */
+ out_le32(&td->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+ out_le32(&td->cmdptr, virt_to_bus(mp->tx_cmds));
+ mp->tx_fill = 0;
+ mp->tx_empty = 0;
+ mp->tx_fullup = 0;
+ mp->tx_active = 0;
+ mp->tx_bad_runt = 0;
+
+ /* turn it on! */
+ mb->maccc = mp->maccc; eieio();
+ /* enable all interrupts except receive interrupts */
+ mb->imr = RCVINT; eieio();
+ return 0;
+}
+
+static int mace_close(struct device *dev)
+{
+ struct mace_data *mp = (struct mace_data *) dev->priv;
+ volatile struct mace *mb = mp->mace;
+ volatile struct dbdma_regs *rd = mp->rx_dma;
+ volatile struct dbdma_regs *td = mp->tx_dma;
+ int i;
+
+ /* disable rx and tx */
+ mb->maccc = 0;
+ mb->imr = 0xff; /* disable all intrs */
+
+ /* disable rx and tx dma */
+ st_le32(&rd->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */
+ st_le32(&td->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */
+
+ /* free some skb's */
+ for (i = 0; i < N_RX_RING; ++i) {
+ if (mp->rx_bufs[i] != 0) {
+ dev_kfree_skb(mp->rx_bufs[i], FREE_READ);
+ mp->rx_bufs[i] = 0;
+ }
+ }
+ for (i = mp->tx_empty; i != mp->tx_fill; ) {
+ dev_kfree_skb(mp->tx_bufs[i], FREE_WRITE);
+ if (++i >= N_TX_RING)
+ i = 0;
+ }
+
+ return 0;
+}
+
+static inline void mace_set_timeout(struct device *dev)
+{
+ struct mace_data *mp = (struct mace_data *) dev->priv;
+
+ mp->tx_timeout.expires = jiffies + TX_TIMEOUT;
+ mp->tx_timeout.function = mace_tx_timeout;
+ mp->tx_timeout.data = (unsigned long) dev;
+ add_timer(&mp->tx_timeout);
+}
+
+static int mace_xmit_start(struct sk_buff *skb, struct device *dev)
+{
+ struct mace_data *mp = (struct mace_data *) dev->priv;
+ volatile struct dbdma_regs *td = mp->tx_dma;
+ volatile struct dbdma_cmd *cp, *np;
+ unsigned long flags;
+ int fill, next, len;
+
+ /* see if there's a free slot in the tx ring */
+ save_flags(flags); cli();
+ fill = mp->tx_fill;
+ next = fill + 1;
+ if (next >= N_TX_RING)
+ next = 0;
+ if (next == mp->tx_empty) {
+ dev->tbusy = 1;
+ mp->tx_fullup = 1;
+ restore_flags(flags);
+ return -1; /* can't take it at the moment */
+ }
+ restore_flags(flags);
+
+ /* partially fill in the dma command block */
+ len = skb->len;
+ if (len > ETH_FRAME_LEN) {
+ printk(KERN_DEBUG "mace: xmit frame too long (%d)\n", len);
+ len = ETH_FRAME_LEN;
+ }
+ mp->tx_bufs[fill] = skb;
+ cp = mp->tx_cmds + NCMDS_TX * fill;
+ st_le16(&cp->req_count, len);
+ st_le32(&cp->phy_addr, virt_to_bus(skb->data));
+
+ np = mp->tx_cmds + NCMDS_TX * next;
+ out_le16(&np->command, DBDMA_STOP);
+
+ /* poke the tx dma channel */
+ save_flags(flags);
+ cli();
+ mp->tx_fill = next;
+ if (!mp->tx_bad_runt && mp->tx_active < MAX_TX_ACTIVE) {
+ out_le16(&cp->xfer_status, 0);
+ out_le16(&cp->command, OUTPUT_LAST);
+ out_le32(&td->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
+ ++mp->tx_active;
+ mace_set_timeout(dev);
+ }
+ restore_flags(flags);
+
+ return 0;
+}
+
+static struct net_device_stats *mace_stats(struct device *dev)
+{
+ struct mace_data *p = (struct mace_data *) dev->priv;
+
+ return &p->stats;
+}
+
+/*
+ * CRC polynomial - used in working out multicast filter bits.
+ */
+#define CRC_POLY 0xedb88320
+
+static void mace_set_multicast(struct device *dev)
+{
+ struct mace_data *mp = (struct mace_data *) dev->priv;
+ volatile struct mace *mb = mp->mace;
+ int i, j, k, b;
+ unsigned long crc;
+
+ mp->maccc &= ~PROM;
+ if (dev->flags & IFF_PROMISC) {
+ mp->maccc |= PROM;
+ } else {
+ unsigned char multicast_filter[8];
+ struct dev_mc_list *dmi = dev->mc_list;
+
+ if (dev->flags & IFF_ALLMULTI) {
+ for (i = 0; i < 8; i++)
+ multicast_filter[i] = 0xff;
+ } else {
+ for (i = 0; i < 8; i++)
+ multicast_filter[i] = 0;
+ for (i = 0; i < dev->mc_count; i++) {
+ crc = ~0;
+ for (j = 0; j < 6; ++j) {
+ b = dmi->dmi_addr[j];
+ for (k = 0; k < 8; ++k) {
+ if ((crc ^ b) & 1)
+ crc = (crc >> 1) ^ CRC_POLY;
+ else
+ crc >>= 1;
+ b >>= 1;
+ }
+ }
+ j = crc >> 26; /* bit number in multicast_filter */
+ multicast_filter[j >> 3] |= 1 << (j & 7);
+ dmi = dmi->next;
+ }
+ }
+#if 0
+ printk("Multicast filter :");
+ for (i = 0; i < 8; i++)
+ printk("%02x ", multicast_filter[i]);
+ printk("\n");
+#endif
+
+ mb->iac = ADDRCHG | LOGADDR; eieio();
+ while ((mb->iac & ADDRCHG) != 0)
+ eieio();
+ for (i = 0; i < 8; ++i) {
+ mb->ladrf = multicast_filter[i];
+ eieio();
+ }
+ }
+ /* reset maccc */
+ mb->maccc = mp->maccc; eieio();
+}
+
+static void mace_handle_misc_intrs(struct mace_data *mp, int intr)
+{
+ volatile struct mace *mb = mp->mace;
+ static int mace_babbles, mace_jabbers;
+
+ if (intr & MPCO)
+ mp->stats.rx_missed_errors += 256;
+ mp->stats.rx_missed_errors += mb->mpc; /* reading clears it */
+ if (intr & RNTPCO)
+ mp->stats.rx_length_errors += 256;
+ mp->stats.rx_length_errors += mb->rntpc; /* reading clears it */
+ if (intr & CERR)
+ ++mp->stats.tx_heartbeat_errors;
+ if (intr & BABBLE)
+ if (mace_babbles++ < 4)
+ printk(KERN_DEBUG "mace: babbling transmitter\n");
+ if (intr & JABBER)
+ if (mace_jabbers++ < 4)
+ printk(KERN_DEBUG "mace: jabbering transceiver\n");
+}
+
+static void mace_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *) dev_id;
+ struct mace_data *mp = (struct mace_data *) dev->priv;
+ volatile struct mace *mb = mp->mace;
+ volatile struct dbdma_regs *td = mp->tx_dma;
+ volatile struct dbdma_cmd *cp;
+ int intr, fs, i, stat, x;
+ int xcount, dstat;
+ static int mace_last_fs, mace_last_xcount;
+
+ intr = mb->ir; /* read interrupt register */
+ mace_handle_misc_intrs(mp, intr);
+
+ i = mp->tx_empty;
+ while (mb->pr & XMTSV) {
+ /*
+ * Clear any interrupt indication associated with this status
+ * word. This appears to unlatch any error indication from
+ * the DMA controller.
+ */
+ intr = mb->ir;
+ if (intr != 0)
+ mace_handle_misc_intrs(mp, intr);
+ if (mp->tx_bad_runt) {
+ fs = mb->xmtfs;
+ eieio();
+ mp->tx_bad_runt = 0;
+ mb->xmtfc = AUTO_PAD_XMIT;
+ del_timer(&mp->tx_timeout);
+ continue;
+ }
+ dstat = ld_le32(&td->status);
+ /* stop DMA controller */
+ out_le32(&td->control, RUN << 16);
+ /*
+ * xcount is the number of complete frames which have been
+ * written to the fifo but for which status has not been read.
+ */
+ xcount = (mb->fifofc >> XMTFC_SH) & XMTFC_MASK;
+ if (xcount == 0 || (dstat & DEAD)) {
+ /*
+ * If a packet was aborted before the DMA controller has
+ * finished transferring it, it seems that there are 2 bytes
+ * which are stuck in some buffer somewhere. These will get
+ * transmitted as soon as we read the frame status (which
+ * reenables the transmit data transfer request). Turning
+ * off the DMA controller and/or resetting the MACE doesn't
+ * help. So we disable auto-padding and FCS transmission
+ * so the two bytes will only be a runt packet which should
+ * be ignored by other stations.
+ */
+ mb->xmtfc = DXMTFCS;
+ eieio();
+ }
+ fs = mb->xmtfs;
+ if ((fs & XMTSV) == 0) {
+ printk(KERN_ERR "mace: xmtfs not valid! (fs=%x xc=%d ds=%x)\n", fs, xcount, dstat);
+ }
+ cp = mp->tx_cmds + NCMDS_TX * i;
+ stat = ld_le16(&cp->xfer_status);
+ if ((fs & (UFLO|LCOL|LCAR|RTRY)) || (dstat & DEAD) || xcount == 0) {
+ /*
+ * Check whether there were in fact 2 bytes written to
+ * the transmit FIFO.
+ */
+ x = (mb->fifofc >> XMTFC_SH) & XMTFC_MASK;
+ if (x != 0) {
+ /* there were two bytes with an end-of-packet indication */
+ mp->tx_bad_runt = 1;
+ mace_set_timeout(dev);
+ } else {
+ /*
+ * Either there weren't the two bytes buffered up, or they
+ * didn't have an end-of-packet indication. Maybe we ought
+ * to flush the transmit FIFO just in case (by setting the
+ * XMTFWU bit with the transmitter disabled).
+ */
+ mb->xmtfc = AUTO_PAD_XMIT;
+ eieio();
+ }
+ }
+ /* dma should have finished */
+ if (i == mp->tx_fill) {
+ printk(KERN_DEBUG "mace: tx ring ran out? (fs=%x xc=%d ds=%x)\n", fs, xcount, dstat);
+ continue;
+ }
+ /* Update stats */
+ if (fs & (UFLO|LCOL|LCAR|RTRY)) {
+ ++mp->stats.tx_errors;
+ if (fs & LCAR)
+ ++mp->stats.tx_carrier_errors;
+ if (fs & (UFLO|LCOL|RTRY))
+ ++mp->stats.tx_aborted_errors;
+ } else
+ ++mp->stats.tx_packets;
+ dev_kfree_skb(mp->tx_bufs[i], FREE_WRITE);
+ --mp->tx_active;
+ if (++i >= N_TX_RING)
+ i = 0;
+ mace_last_fs = fs;
+ mace_last_xcount = xcount;
+ del_timer(&mp->tx_timeout);
+ }
+
+ mp->tx_empty = i;
+ i += mp->tx_active;
+ if (i >= N_TX_RING)
+ i -= N_TX_RING;
+ if (i != mp->tx_fill && mp->tx_fullup) {
+ mp->tx_fullup = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ if (!mp->tx_bad_runt && i != mp->tx_fill && mp->tx_active < MAX_TX_ACTIVE) {
+ do {
+ /* set up the next one */
+ cp = mp->tx_cmds + NCMDS_TX * i;
+ out_le16(&cp->xfer_status, 0);
+ out_le16(&cp->command, OUTPUT_LAST);
+ ++mp->tx_active;
+ if (++i >= N_TX_RING)
+ i = 0;
+ } while (i != mp->tx_fill && mp->tx_active < MAX_TX_ACTIVE);
+ out_le32(&td->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
+ mace_set_timeout(dev);
+ }
+}
+
+static void mace_tx_timeout(unsigned long data)
+{
+ struct device *dev = (struct device *) data;
+ struct mace_data *mp = (struct mace_data *) dev->priv;
+ volatile struct mace *mb = mp->mace;
+ volatile struct dbdma_regs *td = mp->tx_dma;
+ volatile struct dbdma_regs *rd = mp->rx_dma;
+ volatile struct dbdma_cmd *cp;
+ unsigned long flags;
+ int i;
+
+ save_flags(flags);
+ cli();
+ if (mp->tx_active == 0 && !mp->tx_bad_runt)
+ goto out;
+
+ /* update various counters */
+ mace_handle_misc_intrs(mp, mb->ir);
+
+ cp = mp->tx_cmds + NCMDS_TX * mp->tx_empty;
+ printk(KERN_DEBUG "mace: tx dmastat=%x %x bad_runt=%d pr=%x fs=%x fc=%x\n",
+ ld_le32(&td->status), ld_le16(&cp->xfer_status), mp->tx_bad_runt,
+ mb->pr, mb->xmtfs, mb->fifofc);
+
+ /* turn off both tx and rx and reset the chip */
+ mb->maccc = 0;
+ out_le32(&td->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+ printk(KERN_ERR "mace: transmit timeout - resetting\n");
+ mace_reset(dev);
+
+ /* restart rx dma */
+ cp = bus_to_virt(ld_le32(&rd->cmdptr));
+ out_le32(&rd->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+ out_le16(&cp->xfer_status, 0);
+ out_le32(&rd->cmdptr, virt_to_bus(cp));
+ out_le32(&rd->control, (RUN << 16) | RUN);
+
+ /* fix up the transmit side */
+ i = mp->tx_empty;
+ mp->tx_active = 0;
+ ++mp->stats.tx_errors;
+ if (mp->tx_bad_runt) {
+ mp->tx_bad_runt = 0;
+ } else if (i != mp->tx_fill) {
+ dev_kfree_skb(mp->tx_bufs[i], FREE_WRITE);
+ if (++i >= N_TX_RING)
+ i = 0;
+ mp->tx_empty = i;
+ }
+ if (mp->tx_fullup) {
+ mp->tx_fullup = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ if (i != mp->tx_fill) {
+ cp = mp->tx_cmds + NCMDS_TX * i;
+ out_le16(&cp->xfer_status, 0);
+ out_le16(&cp->command, OUTPUT_LAST);
+ out_le32(&td->cmdptr, virt_to_bus(cp));
+ out_le32(&td->control, (RUN << 16) | RUN);
+ ++mp->tx_active;
+ mace_set_timeout(dev);
+ }
+
+ /* turn it back on */
+ out_8(&mb->imr, RCVINT);
+ out_8(&mb->maccc, mp->maccc);
+
+out:
+ restore_flags(flags);
+}
+
+static void mace_txdma_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+}
+
+static void mace_rxdma_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *) dev_id;
+ struct mace_data *mp = (struct mace_data *) dev->priv;
+ volatile struct dbdma_regs *rd = mp->rx_dma;
+ volatile struct dbdma_cmd *cp, *np;
+ int i, nb, stat, next;
+ struct sk_buff *skb;
+ unsigned frame_status;
+ static int mace_lost_status;
+ unsigned char *data;
+
+ for (i = mp->rx_empty; i != mp->rx_fill; ) {
+ cp = mp->rx_cmds + i;
+ stat = ld_le16(&cp->xfer_status);
+ if ((stat & ACTIVE) == 0) {
+ next = i + 1;
+ if (next >= N_RX_RING)
+ next = 0;
+ np = mp->rx_cmds + next;
+ if (next != mp->rx_fill
+ && (ld_le16(&np->xfer_status) & ACTIVE) != 0) {
+ printk(KERN_DEBUG "mace: lost a status word\n");
+ ++mace_lost_status;
+ } else
+ break;
+ }
+ nb = ld_le16(&cp->req_count) - ld_le16(&cp->res_count);
+ out_le16(&cp->command, DBDMA_STOP);
+ /* got a packet, have a look at it */
+ skb = mp->rx_bufs[i];
+ if (skb == 0) {
+ ++mp->stats.rx_dropped;
+ } else if (nb > 8) {
+ data = skb->data;
+ frame_status = (data[nb-3] << 8) + data[nb-4];
+ if (frame_status & (RS_OFLO|RS_CLSN|RS_FRAMERR|RS_FCSERR)) {
+ ++mp->stats.rx_errors;
+ if (frame_status & RS_OFLO)
+ ++mp->stats.rx_over_errors;
+ if (frame_status & RS_FRAMERR)
+ ++mp->stats.rx_frame_errors;
+ if (frame_status & RS_FCSERR)
+ ++mp->stats.rx_crc_errors;
+ } else {
+ nb -= 8;
+ skb_put(skb, nb);
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ mp->rx_bufs[i] = 0;
+ ++mp->stats.rx_packets;
+ }
+ } else {
+ ++mp->stats.rx_errors;
+ ++mp->stats.rx_length_errors;
+ }
+
+ /* advance to next */
+ if (++i >= N_RX_RING)
+ i = 0;
+ }
+ mp->rx_empty = i;
+
+ i = mp->rx_fill;
+ for (;;) {
+ next = i + 1;
+ if (next >= N_RX_RING)
+ next = 0;
+ if (next == mp->rx_empty)
+ break;
+ cp = mp->rx_cmds + i;
+ skb = mp->rx_bufs[i];
+ if (skb == 0) {
+ skb = dev_alloc_skb(RX_BUFLEN + 2);
+ if (skb != 0) {
+ skb_reserve(skb, 2);
+ mp->rx_bufs[i] = skb;
+ }
+ }
+ st_le16(&cp->req_count, RX_BUFLEN);
+ data = skb? skb->data: dummy_buf;
+ st_le32(&cp->phy_addr, virt_to_bus(data));
+ out_le16(&cp->xfer_status, 0);
+ out_le16(&cp->command, INPUT_LAST + INTR_ALWAYS);
+#if 0
+ if ((ld_le32(&rd->status) & ACTIVE) != 0) {
+ out_le32(&rd->control, (PAUSE << 16) | PAUSE);
+ while ((in_le32(&rd->status) & ACTIVE) != 0)
+ ;
+ }
+#endif
+ i = next;
+ }
+ if (i != mp->rx_fill) {
+ out_le32(&rd->control, ((RUN|WAKE) << 16) | (RUN|WAKE));
+ mp->rx_fill = i;
+ }
+}
diff --git a/drivers/net/mace.h b/drivers/net/mace.h
new file mode 100644
index 000000000..a397c83b4
--- /dev/null
+++ b/drivers/net/mace.h
@@ -0,0 +1,173 @@
+/*
+ * mace.h - definitions for the registers in the Am79C940 MACE
+ * (Medium Access Control for Ethernet) controller.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ * 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.
+ */
+
+#define REG(x) volatile unsigned char x; char x ## _pad[15]
+
+struct mace {
+ REG(rcvfifo); /* receive FIFO */
+ REG(xmtfifo); /* transmit FIFO */
+ REG(xmtfc); /* transmit frame control */
+ REG(xmtfs); /* transmit frame status */
+ REG(xmtrc); /* transmit retry count */
+ REG(rcvfc); /* receive frame control */
+ REG(rcvfs); /* receive frame status (4 bytes) */
+ REG(fifofc); /* FIFO frame count */
+ REG(ir); /* interrupt register */
+ REG(imr); /* interrupt mask register */
+ REG(pr); /* poll register */
+ REG(biucc); /* bus interface unit config control */
+ REG(fifocc); /* FIFO configuration control */
+ REG(maccc); /* medium access control config control */
+ REG(plscc); /* phys layer signalling config control */
+ REG(phycc); /* physical configuration control */
+ REG(chipid_lo); /* chip ID, lsb */
+ REG(chipid_hi); /* chip ID, msb */
+ REG(iac); /* internal address config */
+ REG(reg19);
+ REG(ladrf); /* logical address filter (8 bytes) */
+ REG(padr); /* physical address (6 bytes) */
+ REG(reg22);
+ REG(reg23);
+ REG(mpc); /* missed packet count (clears when read) */
+ REG(reg25);
+ REG(rntpc); /* runt packet count (clears when read) */
+ REG(rcvcc); /* recv collision count (clears when read) */
+ REG(reg28);
+ REG(utr); /* user test reg */
+ REG(reg30);
+ REG(reg31);
+};
+
+/* Bits in XMTFC */
+#define DRTRY 0x80 /* don't retry transmission after collision */
+#define DXMTFCS 0x08 /* don't append FCS to transmitted frame */
+#define AUTO_PAD_XMIT 0x01 /* auto-pad short packets on transmission */
+
+/* Bits in XMTFS: only valid when XMTSV is set in PR and XMTFS */
+#define XMTSV 0x80 /* transmit status (i.e. XMTFS) valid */
+#define UFLO 0x40 /* underflow - xmit fifo ran dry */
+#define LCOL 0x20 /* late collision (transmission aborted) */
+#define MORE 0x10 /* 2 or more retries needed to xmit frame */
+#define ONE 0x08 /* 1 retry needed to xmit frame */
+#define DEFER 0x04 /* MACE had to defer xmission (enet busy) */
+#define LCAR 0x02 /* loss of carrier (transmission aborted) */
+#define RTRY 0x01 /* too many retries (transmission aborted) */
+
+/* Bits in XMTRC: only valid when XMTSV is set in PR (and XMTFS) */
+#define EXDEF 0x80 /* had to defer for excessive time */
+#define RETRY_MASK 0x0f /* number of retries (0 - 15) */
+
+/* Bits in RCVFC */
+#define LLRCV 0x08 /* low latency receive: early DMA request */
+#define M_RBAR 0x04 /* sets function of EAM/R pin */
+#define AUTO_STRIP_RCV 0x01 /* auto-strip short LLC frames on recv */
+
+/*
+ * Bits in RCVFS. After a frame is received, four bytes of status
+ * are automatically read from this register and appended to the frame
+ * data in memory. These are:
+ * Byte 0 and 1: message byte count and frame status
+ * Byte 2: runt packet count
+ * Byte 3: receive collision count
+ */
+#define RS_OFLO 0x8000 /* receive FIFO overflowed */
+#define RS_CLSN 0x4000 /* received frame suffered (late) collision */
+#define RS_FRAMERR 0x2000 /* framing error flag */
+#define RS_FCSERR 0x1000 /* frame had FCS error */
+#define RS_COUNT 0x0fff /* mask for byte count field */
+
+/* Bits (fields) in FIFOFC */
+#define RCVFC_SH 4 /* receive frame count in FIFO */
+#define RCVFC_MASK 0x0f
+#define XMTFC_SH 0 /* transmit frame count in FIFO */
+#define XMTFC_MASK 0x0f
+
+/*
+ * Bits in IR and IMR. The IR clears itself when read.
+ * Setting a bit in the IMR will disable the corresponding interrupt.
+ */
+#define JABBER 0x80 /* jabber error - 10baseT xmission too long */
+#define BABBLE 0x40 /* babble - xmitter xmitting for too long */
+#define CERR 0x20 /* collision err - no SQE test (heartbeat) */
+#define RCVCCO 0x10 /* RCVCC overflow */
+#define RNTPCO 0x08 /* RNTPC overflow */
+#define MPCO 0x04 /* MPC overflow */
+#define RCVINT 0x02 /* receive interrupt */
+#define XMTINT 0x01 /* transmitter interrupt */
+
+/* Bits in PR */
+#define XMTSV 0x80 /* XMTFS valid (same as in XMTFS) */
+#define TDTREQ 0x40 /* set when xmit fifo is requesting data */
+#define RDTREQ 0x20 /* set when recv fifo requests data xfer */
+
+/* Bits in BIUCC */
+#define BSWP 0x40 /* byte swap, i.e. big-endian bus */
+#define XMTSP_4 0x00 /* start xmitting when 4 bytes in FIFO */
+#define XMTSP_16 0x10 /* start xmitting when 16 bytes in FIFO */
+#define XMTSP_64 0x20 /* start xmitting when 64 bytes in FIFO */
+#define XMTSP_112 0x30 /* start xmitting when 112 bytes in FIFO */
+#define SWRST 0x01 /* software reset */
+
+/* Bits in FIFOCC */
+#define XMTFW_8 0x00 /* xmit fifo watermark = 8 words free */
+#define XMTFW_16 0x40 /* 16 words free */
+#define XMTFW_32 0x80 /* 32 words free */
+#define RCVFW_16 0x00 /* recv fifo watermark = 16 bytes avail */
+#define RCVFW_32 0x10 /* 32 bytes avail */
+#define RCVFW_64 0x20 /* 64 bytes avail */
+#define XMTFWU 0x08 /* xmit fifo watermark update enable */
+#define RCVFWU 0x04 /* recv fifo watermark update enable */
+#define XMTBRST 0x02 /* enable transmit burst mode */
+#define RCVBRST 0x01 /* enable receive burst mode */
+
+/* Bits in MACCC */
+#define PROM 0x80 /* promiscuous mode */
+#define DXMT2PD 0x40 /* disable xmit two-part deferral algorithm */
+#define EMBA 0x20 /* enable modified backoff algorithm */
+#define DRCVPA 0x08 /* disable receiving physical address */
+#define DRCVBC 0x04 /* disable receiving broadcasts */
+#define ENXMT 0x02 /* enable transmitter */
+#define ENRCV 0x01 /* enable receiver */
+
+/* Bits in PLSCC */
+#define XMTSEL 0x08 /* select DO+/DO- state when idle */
+#define PORTSEL_AUI 0x00 /* select AUI port */
+#define PORTSEL_10T 0x02 /* select 10Base-T port */
+#define PORTSEL_DAI 0x04 /* select DAI port */
+#define PORTSEL_GPSI 0x06 /* select GPSI port */
+#define ENPLSIO 0x01 /* enable optional PLS I/O pins */
+
+/* Bits in PHYCC */
+#define LNKFL 0x80 /* reports 10Base-T link failure */
+#define DLNKTST 0x40 /* disable 10Base-T link test */
+#define REVPOL 0x20 /* 10Base-T receiver polarity reversed */
+#define DAPC 0x10 /* disable auto receiver polarity correction */
+#define LRT 0x08 /* low receive threshold for long links */
+#define ASEL 0x04 /* auto-select AUI or 10Base-T port */
+#define RWAKE 0x02 /* remote wake function */
+#define AWAKE 0x01 /* auto wake function */
+
+/* Bits in IAC */
+#define ADDRCHG 0x80 /* request address change */
+#define PHYADDR 0x04 /* access physical address */
+#define LOGADDR 0x02 /* access multicast filter */
+
+/* Bits in UTR */
+#define RTRE 0x80 /* reserved test register enable. DON'T SET. */
+#define RTRD 0x40 /* reserved test register disable. Sticky */
+#define RPA 0x20 /* accept runt packets */
+#define FCOLL 0x10 /* force collision */
+#define RCVFCSE 0x08 /* receive FCS enable */
+#define LOOP_NONE 0x00 /* no loopback */
+#define LOOP_EXT 0x02 /* external loopback */
+#define LOOP_INT 0x04 /* internal loopback, excludes MENDEC */
+#define LOOP_MENDEC 0x06 /* internal loopback, includes MENDEC */
diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c
index 4647f0fea..e37b3c783 100644
--- a/drivers/net/myri_sbus.c
+++ b/drivers/net/myri_sbus.c
@@ -19,7 +19,9 @@ static char *version =
#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/string.h>
+#include <linux/delay.h>
#include <linux/init.h>
+
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
@@ -34,6 +36,7 @@ static char *version =
#include <asm/auxio.h>
#include <asm/system.h>
#include <asm/pgtable.h>
+#include <asm/irq.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -275,7 +278,7 @@ static void myri_init_rings(struct myri_eth *mp, int from_irq)
mp->rx_skbs[i] = skb;
skb->dev = dev;
skb_put(skb, RX_ALLOC_SIZE);
- rxd[i].myri_scatters[0].addr = (unsigned long) skb->data;
+ rxd[i].myri_scatters[0].addr = (u32) ((unsigned long)skb->data);
rxd[i].myri_scatters[0].len = RX_ALLOC_SIZE;
rxd[i].ctx = i;
rxd[i].num_sg = 1;
@@ -338,6 +341,11 @@ static inline void myri_tx(struct myri_eth *mp, struct device *dev)
mp->tx_skbs[entry] = NULL;
mp->enet_stats.tx_packets++;
+#ifdef NEED_DMA_SYNCHRONIZATION
+ mmu_sync_dma(((u32)((unsigned long)skb->data)),
+ skb->len, mp->myri_sbus_dev->my_bus);
+#endif
+
entry = NEXT_TX(entry);
}
mp->tx_old = entry;
@@ -429,7 +437,8 @@ static inline void myri_rx(struct myri_eth *mp, struct device *dev)
drops++;
DRX(("DROP "));
mp->enet_stats.rx_dropped++;
- rxd->myri_scatters[0].addr = (unsigned long) skb->data;
+ rxd->myri_scatters[0].addr =
+ (u32) ((unsigned long)skb->data);
rxd->myri_scatters[0].len = RX_ALLOC_SIZE;
rxd->ctx = index;
rxd->num_sg = 1;
@@ -437,6 +446,11 @@ static inline void myri_rx(struct myri_eth *mp, struct device *dev)
goto next;
}
+#ifdef NEED_DMA_SYNCHRONIZATION
+ mmu_sync_dma(((u32)((unsigned long)skb->data)),
+ skb->len, mp->myri_sbus_dev->my_bus);
+#endif
+
DRX(("len[%d] ", len));
if(len > RX_COPY_THRESHOLD) {
struct sk_buff *new_skb;
@@ -450,7 +464,8 @@ static inline void myri_rx(struct myri_eth *mp, struct device *dev)
mp->rx_skbs[index] = new_skb;
new_skb->dev = dev;
skb_put(new_skb, RX_ALLOC_SIZE);
- rxd->myri_scatters[0].addr = (unsigned long) new_skb->data;
+ rxd->myri_scatters[0].addr =
+ (u32) ((unsigned long)new_skb->data);
rxd->myri_scatters[0].len = RX_ALLOC_SIZE;
rxd->ctx = index;
rxd->num_sg = 1;
@@ -474,7 +489,8 @@ static inline void myri_rx(struct myri_eth *mp, struct device *dev)
/* Reuse original ring buffer. */
DRX(("reuse "));
- rxd->myri_scatters[0].addr = (unsigned long) skb->data;
+ rxd->myri_scatters[0].addr =
+ (u32) ((unsigned long)skb->data);
rxd->myri_scatters[0].len = RX_ALLOC_SIZE;
rxd->ctx = index;
rxd->num_sg = 1;
@@ -618,7 +634,8 @@ static int myri_start_xmit(struct sk_buff *skb, struct device *dev)
txd = &sq->myri_txd[entry];
mp->tx_skbs[entry] = skb;
- txd->myri_gathers[0].addr = (unsigned long) skb->data;
+ txd->myri_gathers[0].addr =
+ (unsigned int) ((unsigned long)skb->data);
txd->myri_gathers[0].len = len;
txd->num_sg = 1;
txd->chan = KERNEL_CHANNEL;
@@ -837,12 +854,12 @@ static inline void determine_reg_space_size(struct myri_eth *mp)
case CPUVERS_3_0:
case CPUVERS_3_1:
case CPUVERS_3_2:
- mp->reg_size = (3 * 128 * 1024) + PAGE_SIZE;
+ mp->reg_size = (3 * 128 * 1024) + 4096;
break;
case CPUVERS_4_0:
case CPUVERS_4_1:
- mp->reg_size = ((PAGE_SIZE<<1) + mp->eeprom.ramsz);
+ mp->reg_size = ((4096<<1) + mp->eeprom.ramsz);
break;
case CPUVERS_4_2:
@@ -850,7 +867,7 @@ static inline void determine_reg_space_size(struct myri_eth *mp)
default:
printk("myricom: AIEEE weird cpu version %04x assuming pre4.0\n",
mp->eeprom.cpuvers);
- mp->reg_size = (3 * 128 * 1024) + PAGE_SIZE;
+ mp->reg_size = (3 * 128 * 1024) + 4096;
};
}
@@ -1077,6 +1094,22 @@ static inline int myri_ether_init(struct device *dev, struct linux_sbus_device *
/* Register interrupt handler now. */
DET(("Requesting MYRIcom IRQ line.\n"));
+#ifdef __sparc_v9__
+ if(sparc_cpu_model == sun4u) {
+ struct devid_cookie dcookie;
+
+ dcookie.real_dev_id = dev;
+ dcookie.imap = dcookie.iclr = 0;
+ dcookie.pil = -1;
+ dcookie.bus_cookie = sdev->my_bus;
+ if(request_irq(dev->irq, &myri_interrupt,
+ (SA_SHIRQ | SA_SBUS | SA_DCOOKIE),
+ "MyriCOM Ethernet", &dcookie)) {
+ printk("MyriCOM: Cannot register interrupt handler.\n");
+ return ENODEV;
+ }
+ } else
+#endif
if(request_irq(dev->irq, &myri_interrupt,
SA_SHIRQ, "MyriCOM Ethernet", (void *) dev)) {
printk("MyriCOM: Cannot register interrupt handler.\n");
diff --git a/drivers/net/ne.c b/drivers/net/ne.c
index 7e02bf4f8..c989d1841 100644
--- a/drivers/net/ne.c
+++ b/drivers/net/ne.c
@@ -425,7 +425,7 @@ __initfunc(static int ne_probe1(struct device *dev, int ioaddr))
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
{
- int irqval = request_irq(dev->irq, ei_interrupt, 0, name, NULL);
+ int irqval = request_irq(dev->irq, ei_interrupt, SA_INTERRUPT, name, NULL);
if (irqval) {
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
return EAGAIN;
diff --git a/drivers/net/plip.c b/drivers/net/plip.c
index 2e2f3f3e2..72b8a3314 100644
--- a/drivers/net/plip.c
+++ b/drivers/net/plip.c
@@ -1,4 +1,4 @@
-/* $Id: plip.c,v 1.1.1.1 1997/06/01 03:17:24 ralf Exp $ */
+/* $Id: plip.c,v 1.2 1997/08/06 19:15:51 miguel Exp $ */
/* PLIP: A parallel port "network" driver for Linux. */
/* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
/*
@@ -150,7 +150,7 @@ static struct net_device_stats *plip_get_stats(struct device *dev);
static int plip_config(struct device *dev, struct ifmap *map);
static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
static int plip_preempt(void *handle);
-static int plip_wakeup(void *handle);
+static void plip_wakeup(void *handle);
enum plip_connection_state {
PLIP_CN_NONE=0,
diff --git a/drivers/net/ppp.c b/drivers/net/ppp.c
index e3615534d..4d2cb31f1 100644
--- a/drivers/net/ppp.c
+++ b/drivers/net/ppp.c
@@ -51,7 +51,7 @@
#define PPP_MAX_DEV 256
#endif
-/* $Id: ppp.c,v 1.27 1997/01/26 07:13:29 davem Exp $
+/* $Id: ppp.c,v 1.1.1.1 1997/06/01 03:17:18 ralf Exp $
* Added dynamic allocation of channels to eliminate
* compiled-in limits on the number of channels.
*
@@ -521,7 +521,7 @@ ppp_free_buf (struct ppp_buffer *ptr)
extern inline int
lock_buffer (register struct ppp_buffer *buf)
{
- register int state;
+ register long state;
int flags;
/*
* Save the current state and if free then set it to the "busy" state
@@ -1071,6 +1071,7 @@ ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
*/
if (ppp->flags & SC_LOG_RAWIN)
ppp_print_buffer ("receive buffer", data, count);
+
/*
* Collect the character and error condition for the character. Set the toss
* flag for the first character error.
@@ -1083,6 +1084,7 @@ ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
ppp->toss = *flags;
++flags;
}
+
/*
* Set the flags for 8 data bits and no parity.
*
@@ -1467,6 +1469,7 @@ static void ppp_doframe_lower (struct ppp *ppp, __u8 *data, int count)
{
__u16 proto = PPP_PROTOCOL (data);
ppp_proto_type *proto_ptr;
+
/*
* Ignore empty frames
*/
@@ -1507,6 +1510,7 @@ ppp_doframe (struct ppp *ppp)
int addr, ctrl, proto;
int new_count;
__u8 *new_data;
+
/*
* If there is a pending error from the receiver then log it and discard
* the damaged frame.
diff --git a/drivers/net/slip.c b/drivers/net/slip.c
index 057d3c9d2..435cd629a 100644
--- a/drivers/net/slip.c
+++ b/drivers/net/slip.c
@@ -106,6 +106,7 @@ static void slip_unesc6(struct slip *sl, unsigned char c);
#ifdef CONFIG_SLIP_SMART
static void sl_keepalive(unsigned long sls);
static void sl_outfill(unsigned long sls);
+static int sl_ioctl(struct device *dev,struct ifreq *rq,int cmd);
#endif
/* Find a free SLIP channel, and link in this `tty' line. */
@@ -1015,8 +1016,6 @@ slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
#ifdef CONFIG_SLIP_SMART
/* VSV changes start here */
case SIOCSKEEPALIVE:
- if (sl->keepalive)
- (void)del_timer (&sl->keepalive_timer);
err = verify_area(VERIFY_READ, arg, sizeof(int));
if (err) {
return -err;
@@ -1024,6 +1023,8 @@ slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
get_user(tmp,(int *)arg);
if (tmp > 255) /* max for unchar */
return -EINVAL;
+ if (sl->keepalive)
+ (void)del_timer (&sl->keepalive_timer);
if ((sl->keepalive = (unchar) tmp) != 0) {
sl->keepalive_timer.expires=jiffies+sl->keepalive*HZ;
add_timer(&sl->keepalive_timer);
@@ -1040,8 +1041,6 @@ slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
return 0;
case SIOCSOUTFILL:
- if (sl->outfill)
- (void)del_timer (&sl->outfill_timer);
err = verify_area(VERIFY_READ, arg, sizeof(int));
if (err) {
return -err;
@@ -1049,6 +1048,8 @@ slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
get_user(tmp,(int *)arg);
if (tmp > 255) /* max for unchar */
return -EINVAL;
+ if (sl->outfill)
+ (void)del_timer (&sl->outfill_timer);
if ((sl->outfill = (unchar) tmp) != 0){
sl->outfill_timer.expires=jiffies+sl->outfill*HZ;
add_timer(&sl->outfill_timer);
@@ -1076,6 +1077,58 @@ slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
}
}
+/* VSV changes start here */
+#ifdef CONFIG_SLIP_SMART
+/* function do_ioctl called from net/core/dev.c
+ to allow get/set outfill/keepalive parameter
+ by ifconfig */
+
+static int sl_ioctl(struct device *dev,struct ifreq *rq,int cmd)
+{
+ struct slip *sl = (struct slip*)(dev->priv);
+
+ if (sl == NULL) /* Allocation failed ?? */
+ return -ENODEV;
+
+ switch(cmd){
+ case SIOCSKEEPALIVE:
+ /* max for unchar */
+ if (((unsigned int)((unsigned long)rq->ifr_data)) > 255)
+ return -EINVAL;
+ if (sl->keepalive)
+ (void)del_timer (&sl->keepalive_timer);
+ sl->keepalive = (unchar) ((unsigned long)rq->ifr_data);
+ if (sl->keepalive != 0) {
+ sl->keepalive_timer.expires=jiffies+sl->keepalive*HZ;
+ add_timer(&sl->keepalive_timer);
+ set_bit(SLF_KEEPTEST, &sl->flags);
+ }
+ break;
+
+ case SIOCGKEEPALIVE:
+ rq->ifr_data=(caddr_t)((unsigned long)sl->keepalive);
+ break;
+
+ case SIOCSOUTFILL:
+ if (((unsigned)((unsigned long)rq->ifr_data)) > 255) /* max for unchar */
+ return -EINVAL;
+ if (sl->outfill)
+ del_timer (&sl->outfill_timer);
+ if ((sl->outfill = (unchar)((unsigned long) rq->ifr_data)) != 0){
+ sl->outfill_timer.expires=jiffies+sl->outfill*HZ;
+ add_timer(&sl->outfill_timer);
+ set_bit(SLF_OUTWAIT, &sl->flags);
+ }
+ break;
+
+ case SIOCGOUTFILL:
+ rq->ifr_data=(caddr_t)((unsigned long)sl->outfill);
+ };
+ return 0;
+}
+#endif
+/* VSV changes end */
+
static int sl_open_dev(struct device *dev)
{
struct slip *sl = (struct slip*)(dev->priv);
@@ -1173,6 +1226,9 @@ int slip_init(struct device *dev)
dev->open = sl_open_dev;
dev->stop = sl_close;
dev->get_stats = sl_get_stats;
+#ifdef CONFIG_SLIP_SMART
+ dev->do_ioctl = sl_ioctl;
+#endif
dev->hard_header_len = 0;
dev->addr_len = 0;
dev->type = ARPHRD_SLIP + SL_MODE_DEFAULT;
diff --git a/drivers/net/soundmodem/Makefile b/drivers/net/soundmodem/Makefile
index 9997190fc..381eb35cd 100644
--- a/drivers/net/soundmodem/Makefile
+++ b/drivers/net/soundmodem/Makefile
@@ -42,6 +42,9 @@ endif
M_OBJS := $(O_TARGET)
+all: all_targets
+.PHONY: all
+
gentbl: gentbl.c
$(HOSTCC) $< -o $@ -lm
diff --git a/drivers/net/soundmodem/gentbl.c b/drivers/net/soundmodem/gentbl.c
index 5eff27167..c35d0e466 100644
--- a/drivers/net/soundmodem/gentbl.c
+++ b/drivers/net/soundmodem/gentbl.c
@@ -81,7 +81,6 @@ static void gentbl_costab(FILE *f, unsigned int nbits)
static void gentbl_afsk1200(FILE *f)
{
int i, v, sum;
- float fv, fsum;
#define ARGLO(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_LO/(double)AFSK12_SAMPLE_RATE
#define ARGHI(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_HI/(double)AFSK12_SAMPLE_RATE
@@ -93,34 +92,7 @@ static void gentbl_afsk1200(FILE *f)
"#define AFSK12_CORRLEN %u\n\n",
AFSK12_SAMPLE_RATE, AFSK12_TX_FREQ_LO,
AFSK12_TX_FREQ_HI, AFSK12_CORRLEN);
- fprintf(f, "#if defined(CONFIG_SOUNDMODEM_FLOAT) && "
- "(defined(CONFIG_M586) || defined(CONFIG_M686))\n\n"
- "static const float afsk12_tx_lo_i_f[] = {\n\t");
- for(fsum = i = 0; i < AFSK12_CORRLEN; i++) {
- fsum += (fv = cos(ARGLO(i)));
- fprintf(f, " %7f%c", fv, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
- }
- fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_Q %7f\n\n"
- "static const float afsk12_tx_lo_q_f[] = {\n\t", fsum);
- for(fsum = i = 0; i < AFSK12_CORRLEN; i++) {
- fsum += (fv = sin(ARGLO(i)));
- fprintf(f, " %7f%c", fv, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
- }
- fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_Q %7f\n\n"
- "static const float afsk12_tx_hi_i_f[] = {\n\t", fsum);
- for(fsum = i = 0; i < AFSK12_CORRLEN; i++) {
- fsum += (fv = cos(ARGHI(i)));
- fprintf(f, " %7f%c", fv, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
- }
- fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_I %7f\n\n"
- "static const float afsk12_tx_hi_q_f[] = {\n\t", fsum);
- for(fsum = i = 0; i < AFSK12_CORRLEN; i++) {
- fsum += (fv = sin(ARGHI(i)));
- fprintf(f, " %7f%c", fv, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
- }
- fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %7f\n\n"
- "#else /* CONFIG_SOUNDMODEM_FLOAT */\n\n"
- "static const int afsk12_tx_lo_i[] = {\n\t", fsum);
+ fprintf(f, "static const int afsk12_tx_lo_i[] = {\n\t");
for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
sum += (v = 127.0*cos(ARGLO(i)));
fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
@@ -143,8 +115,7 @@ static void gentbl_afsk1200(FILE *f)
sum += (v = 127.0*sin(ARGHI(i)));
fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
}
- fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %d\n\n"
- "#endif /* CONFIG_SOUNDMODEM_FLOAT */\n\n", sum);
+ fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %d\n\n", sum);
#undef ARGLO
#undef ARGHI
}
@@ -594,7 +565,6 @@ static void gentbl_hapn4800(FILE *f)
static void gentbl_afsk2400(FILE *f, float tcm3105clk)
{
int i, sum, v;
- float fsum, fv;
fprintf(f, "\n/*\n * afsk2400 specific tables (tcm3105 clk %7fHz)\n */\n"
"#define AFSK24_TX_FREQ_LO %d\n"
@@ -608,34 +578,7 @@ static void gentbl_afsk2400(FILE *f, float tcm3105clk)
#define ARGHI(x) 2.0*M_PI*(double)x*(tcm3105clk/2015.0)/(double)AFSK24_SAMPLERATE
#define WINDOW(x) hamming((float)(x)/(AFSK24_CORRLEN-1.0))
- fprintf(f, "#if defined(CONFIG_SOUNDMODEM_FLOAT) && "
- "(defined(CONFIG_M586) || defined(CONFIG_M686))\n\n"
- "static const float afsk24_tx_lo_i_f[] = {\n\t");
- for(fsum = i = 0; i < AFSK24_CORRLEN; i++) {
- fsum += (fv = cos(ARGLO(i))*WINDOW(i));
- fprintf(f, " %7f%c", fv, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
- }
- fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_Q %7f\n\n"
- "static const float afsk24_tx_lo_q_f[] = {\n\t", fsum);
- for(fsum = i = 0; i < AFSK24_CORRLEN; i++) {
- fsum += (fv = sin(ARGLO(i))*WINDOW(i));
- fprintf(f, " %7f%c", fv, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
- }
- fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_Q %7f\n\n"
- "static const float afsk24_tx_hi_i_f[] = {\n\t", fsum);
- for(fsum = i = 0; i < AFSK24_CORRLEN; i++) {
- fsum += (fv = cos(ARGHI(i))*WINDOW(i));
- fprintf(f, " %7f%c", fv, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
- }
- fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_I %7f\n\n"
- "static const float afsk24_tx_hi_q_f[] = {\n\t", fsum);
- for(fsum = i = 0; i < AFSK24_CORRLEN; i++) {
- fsum += (fv = sin(ARGHI(i))*WINDOW(i));
- fprintf(f, " %7f%c", fv, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
- }
- fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %7f\n\n"
- "#else /* CONFIG_SOUNDMODEM_FLOAT */\n\n"
- "static const int afsk24_tx_lo_i[] = {\n\t", fsum);
+ fprintf(f, "static const int afsk24_tx_lo_i[] = {\n\t");
for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
sum += (v = 127.0*cos(ARGLO(i))*WINDOW(i));
fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
@@ -658,8 +601,7 @@ static void gentbl_afsk2400(FILE *f, float tcm3105clk)
sum += (v = 127.0*sin(ARGHI(i))*WINDOW(i));
fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
}
- fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %d\n\n"
- "#endif /* CONFIG_SOUNDMODEM_FLOAT */\n\n", sum);
+ fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %d\n\n", sum);
#undef ARGLO
#undef ARGHI
#undef WINDOW
@@ -675,11 +617,6 @@ static void gentbl_banner(FILE *f)
"DO NOT EDIT!\n */\n\n", progname);
}
-static void gentbl_needs_config(FILE *f)
-{
- fprintf(f, "\n#include <linux/config.h>\n\n");
-}
-
/* -------------------------------------------------------------------- */
void main(int argc, char *argv[])
@@ -690,7 +627,6 @@ void main(int argc, char *argv[])
if (!(f = fopen("sm_tbl_afsk1200.h", "w")))
exit(1);
gentbl_banner(f);
- gentbl_needs_config(f);
gentbl_offscostab(f, 6);
gentbl_costab(f, 6);
gentbl_afsk1200(f);
@@ -722,7 +658,6 @@ void main(int argc, char *argv[])
if (!(f = fopen("sm_tbl_afsk2400_8.h", "w")))
exit(1);
gentbl_banner(f);
- gentbl_needs_config(f);
gentbl_offscostab(f, 6);
gentbl_costab(f, 6);
gentbl_afsk2400(f, 8000000);
@@ -730,7 +665,6 @@ void main(int argc, char *argv[])
if (!(f = fopen("sm_tbl_afsk2400_7.h", "w")))
exit(1);
gentbl_banner(f);
- gentbl_needs_config(f);
gentbl_offscostab(f, 6);
gentbl_costab(f, 6);
gentbl_afsk2400(f, 7372800);
diff --git a/drivers/net/soundmodem/sm.c b/drivers/net/soundmodem/sm.c
index ac1fbac48..171aecfc7 100644
--- a/drivers/net/soundmodem/sm.c
+++ b/drivers/net/soundmodem/sm.c
@@ -39,6 +39,7 @@
* 0.4 21.01.97 Separately compileable soundcard/modem modules
* 0.5 03.03.97 fixed LPT probing (check_lpt result was interpreted the wrong way round)
* 0.6 16.04.97 init code/data tagged
+ * 0.7 30.07.97 fixed halfduplex interrupt handlers/hotfix for CS423X
*/
/*****************************************************************************/
@@ -113,7 +114,7 @@ extern inline int copy_to_user(void *to, const void *from, unsigned long n)
/*static*/ const char sm_drvname[] = "soundmodem";
static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-1997 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "soundmodem: version 0.6 compiled " __TIME__ " " __DATE__ "\n";
+KERN_INFO "soundmodem: version 0.7 compiled " __TIME__ " " __DATE__ "\n";
/* --------------------------------------------------------------------- */
diff --git a/drivers/net/soundmodem/sm_afsk2400_7.c b/drivers/net/soundmodem/sm_afsk2400_7.c
index 36e0f328f..d217936ab 100644
--- a/drivers/net/soundmodem/sm_afsk2400_7.c
+++ b/drivers/net/soundmodem/sm_afsk2400_7.c
@@ -34,7 +34,6 @@
* a "legacy" link.
*/
-#include <linux/config.h>
#include "sm.h"
#include "sm_tbl_afsk2400_7.h"
diff --git a/drivers/net/soundmodem/sm_afsk2400_8.c b/drivers/net/soundmodem/sm_afsk2400_8.c
index 8234815d6..23d233746 100644
--- a/drivers/net/soundmodem/sm_afsk2400_8.c
+++ b/drivers/net/soundmodem/sm_afsk2400_8.c
@@ -34,7 +34,6 @@
* a "legacy" link.
*/
-#include <linux/config.h>
#include "sm.h"
#include "sm_tbl_afsk2400_8.h"
diff --git a/drivers/net/soundmodem/sm_sbc.c b/drivers/net/soundmodem/sm_sbc.c
index fc26b57bf..bbef05a5d 100644
--- a/drivers/net/soundmodem/sm_sbc.c
+++ b/drivers/net/soundmodem/sm_sbc.c
@@ -343,6 +343,7 @@ static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
if (hdlcdrv_ptt(&sm->hdrv)) {
/* starting to transmit */
disable_dma(dev->dma);
+ hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
dma_start_transmit(sm);
setup_dma_dsp(dev, sm, 1);
dma_transmit(sm);
@@ -416,11 +417,7 @@ static int sbc_open(struct device *dev, struct sm_state *sm)
*/
dma_init_receive(sm);
dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
- if (sm->dma.i16bit)
- dmasz <<= 1;
u = NUM_FRAGMENTS * sm->dma.ofragsz;
- if (sm->dma.o16bit)
- u <<= 1;
if (u > dmasz)
dmasz = u;
if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
diff --git a/drivers/net/soundmodem/sm_wss.c b/drivers/net/soundmodem/sm_wss.c
index ef129930a..a089544d2 100644
--- a/drivers/net/soundmodem/sm_wss.c
+++ b/drivers/net/soundmodem/sm_wss.c
@@ -98,6 +98,8 @@ struct sc_state_wss {
#define WSS_EXTENT 8
+#define CS423X_HOTFIX
+
/* --------------------------------------------------------------------- */
static void write_codec(struct device *dev, unsigned char idx,
@@ -170,6 +172,8 @@ static int wss_set_codec_fmt(struct device *dev, struct sm_state *sm, unsigned c
/* MCE and interface config reg */
write_codec(dev, 0x49, fdx ? 0x8 : 0xc);
outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */
+ if (SCSTATE->crystal && !fullcalib)
+ return 0;
/*
* wait for ACI start
*/
@@ -361,15 +365,16 @@ static void setup_dma_wss(struct device *dev, struct sm_state *sm, int send)
abrt = 0;
while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000));
}
+#ifdef CS423X_HOTFIX
+ if (read_codec(dev, 0x8) != fmt || SCSTATE->crystal)
+ wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
+#else /* CS423X_HOTFIX */
if (read_codec(dev, 0x8) != fmt)
wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
+#endif /* CS423X_HOTFIX */
numsamps = dma_setup(sm, send, dev->dma) - 1;
write_codec(dev, 15, numsamps & 0xff);
write_codec(dev, 14, numsamps >> 8);
- if (SCSTATE->crystal) {
- write_codec(dev, 31, numsamps & 0xff);
- write_codec(dev, 30, numsamps >> 8);
- }
write_codec(dev, 9, codecmode[send]);
restore_flags(flags);
}
@@ -393,10 +398,6 @@ static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs)
nums = dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag) - 1;
write_codec(dev, 15, nums & 0xff);
write_codec(dev, 14, nums >> 8);
- if (SCSTATE->crystal) {
- write_codec(dev, 31, nums & 0xff);
- write_codec(dev, 30, nums >> 8);
- }
enable_dma(dev->dma);
sm_int_freq(sm);
sti();
@@ -406,6 +407,7 @@ static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs)
if (hdlcdrv_ptt(&sm->hdrv)) {
/* starting to transmit */
disable_dma(dev->dma);
+ hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
dma_start_transmit(sm);
setup_dma_wss(dev, sm, 1);
dma_transmit(sm);
@@ -413,7 +415,6 @@ static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs)
} else if (dma_end_transmit(sm, curfrag)) {
/* stopping transmission */
disable_dma(dev->dma);
- sti();
dma_init_receive(sm);
setup_dma_wss(dev, sm, 0);
} else
@@ -451,11 +452,7 @@ static int wss_open(struct device *dev, struct sm_state *sm)
*/
dma_init_receive(sm);
dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
- if (sm->dma.i16bit)
- dmasz <<= 1;
u = NUM_FRAGMENTS * sm->dma.ofragsz;
- if (sm->dma.o16bit)
- u <<= 1;
if (u > dmasz)
dmasz = u;
if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
diff --git a/drivers/net/soundmodem/smdma.h b/drivers/net/soundmodem/smdma.h
index 27cea09e7..44e457a7a 100644
--- a/drivers/net/soundmodem/smdma.h
+++ b/drivers/net/soundmodem/smdma.h
@@ -37,6 +37,13 @@
#define DMA_MODE_AUTOINIT 0x10
#define NUM_FRAGMENTS 4
+/*
+ * NOTE: make sure that hdlcdrv_hdlcbuffer contains enough space
+ * for the modulator to fill the whole DMA buffer without underrun
+ * at the highest possible baud rate, otherwise the TX state machine will
+ * not work correctly. That is (9k6 FSK): HDLCDRV_HDLCBUFFER > 6*NUM_FRAGMENTS
+ */
+
/* --------------------------------------------------------------------- */
/*
* ===================== DMA buffer management ===========================
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index dca243c4f..8411595a0 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -10,6 +10,7 @@ static char *version =
#include <linux/module.h>
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
@@ -20,6 +21,7 @@ static char *version =
#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/string.h>
+#include <linux/delay.h>
#include <linux/init.h>
#include <asm/system.h>
#include <asm/bitops.h>
@@ -35,11 +37,18 @@ static char *version =
#include <asm/auxio.h>
#include <asm/system.h>
#include <asm/pgtable.h>
+#include <asm/irq.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/pbm.h>
+#endif
+
#include "sunhme.h"
#ifdef MODULE
@@ -68,30 +77,33 @@ static struct happy_meal *root_happy_dev = NULL;
#define DEFAULT_JAMSIZE 4 /* Toe jam */
/* Oh yes, the MIF BitBang is mighty fun to program. BitBucket is more like it. */
-#define BB_PUT_BIT(tregs, bit) \
-do { (tregs)->bb_data = (bit); (tregs)->bb_clock = 0; (tregs)->bb_clock = 1; } while(0)
-
-#define BB_GET_BIT(tregs, internal) \
-({ \
- (tregs)->bb_clock = 0; \
- (tregs)->bb_clock = 1; \
- if(internal) \
- ((tregs)->cfg & TCV_CFG_MDIO0); \
- else \
- ((tregs)->cfg & TCV_CFG_MDIO1); \
+#define BB_PUT_BIT(hp, tregs, bit) \
+do { hme_write32(hp, &(tregs)->bb_data, (bit)); \
+ hme_write32(hp, &(tregs)->bb_clock, 0); \
+ hme_write32(hp, &(tregs)->bb_clock, 1); \
+} while(0)
+
+#define BB_GET_BIT(hp, tregs, internal) \
+({ \
+ hme_write32(hp, &(tregs)->bb_clock, 0); \
+ hme_write32(hp, &(tregs)->bb_clock, 1); \
+ if(internal) \
+ hme_read32(hp, &(tregs)->cfg) & TCV_CFG_MDIO0; \
+ else \
+ hme_read32(hp, &(tregs)->cfg) & TCV_CFG_MDIO1; \
})
-#define BB_GET_BIT2(tregs, internal) \
-({ \
- int retval; \
- (tregs)->bb_clock = 0; \
- udelay(1); \
- if(internal) \
- retval = ((tregs)->cfg & TCV_CFG_MDIO0); \
- else \
- retval = ((tregs)->cfg & TCV_CFG_MDIO1); \
- (tregs)->bb_clock = 1; \
- retval; \
+#define BB_GET_BIT2(hp, tregs, internal) \
+({ \
+ int retval; \
+ hme_write32(hp, &(tregs)->bb_clock, 0); \
+ udelay(1); \
+ if(internal) \
+ retval = hme_read32(hp, &(tregs)->cfg) & TCV_CFG_MDIO0; \
+ else \
+ retval = hme_read32(hp, &(tregs)->cfg) & TCV_CFG_MDIO1; \
+ hme_write32(hp, &(tregs)->bb_clock, 1); \
+ retval; \
})
#define TCVR_FAILURE 0x80000000 /* Impossible MIF read value */
@@ -107,38 +119,38 @@ static inline int happy_meal_bb_read(struct happy_meal *hp,
ASD(("happy_meal_bb_read: reg=%d ", reg));
/* Enable the MIF BitBang outputs. */
- tregs->bb_oenab = 1;
+ hme_write32(hp, &tregs->bb_oenab, 1);
/* Force BitBang into the idle state. */
for(i = 0; i < 32; i++)
- BB_PUT_BIT(tregs, 1);
+ BB_PUT_BIT(hp, tregs, 1);
/* Give it the read sequence. */
- BB_PUT_BIT(tregs, 0);
- BB_PUT_BIT(tregs, 1);
- BB_PUT_BIT(tregs, 1);
- BB_PUT_BIT(tregs, 0);
+ BB_PUT_BIT(hp, tregs, 0);
+ BB_PUT_BIT(hp, tregs, 1);
+ BB_PUT_BIT(hp, tregs, 1);
+ BB_PUT_BIT(hp, tregs, 0);
/* Give it the PHY address. */
tmp = hp->paddr & 0xff;
for(i = 4; i >= 0; i--)
- BB_PUT_BIT(tregs, ((tmp >> i) & 1));
+ BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));
/* Tell it what register we want to read. */
tmp = (reg & 0xff);
for(i = 4; i >= 0; i--)
- BB_PUT_BIT(tregs, ((tmp >> i) & 1));
+ BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));
/* Close down the MIF BitBang outputs. */
- tregs->bb_oenab = 0;
+ hme_write32(hp, &tregs->bb_oenab, 0);
/* Now read in the value. */
- unused = BB_GET_BIT2(tregs, (hp->tcvr_type == internal));
+ unused = BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
for(i = 15; i >= 0; i--)
- retval |= BB_GET_BIT2(tregs, (hp->tcvr_type == internal));
- unused = BB_GET_BIT2(tregs, (hp->tcvr_type == internal));
- unused = BB_GET_BIT2(tregs, (hp->tcvr_type == internal));
- unused = BB_GET_BIT2(tregs, (hp->tcvr_type == internal));
+ retval |= BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
+ unused = BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
+ unused = BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
+ unused = BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
ASD(("value=%x\n", retval));
return retval;
}
@@ -153,37 +165,37 @@ static inline void happy_meal_bb_write(struct happy_meal *hp,
ASD(("happy_meal_bb_write: reg=%d value=%x\n", reg, value));
/* Enable the MIF BitBang outputs. */
- tregs->bb_oenab = 1;
+ hme_write32(hp, &tregs->bb_oenab, 1);
/* Force BitBang into the idle state. */
for(i = 0; i < 32; i++)
- BB_PUT_BIT(tregs, 1);
+ BB_PUT_BIT(hp, tregs, 1);
/* Give it write sequence. */
- BB_PUT_BIT(tregs, 0);
- BB_PUT_BIT(tregs, 1);
- BB_PUT_BIT(tregs, 0);
- BB_PUT_BIT(tregs, 1);
+ BB_PUT_BIT(hp, tregs, 0);
+ BB_PUT_BIT(hp, tregs, 1);
+ BB_PUT_BIT(hp, tregs, 0);
+ BB_PUT_BIT(hp, tregs, 1);
/* Give it the PHY address. */
tmp = (hp->paddr & 0xff);
for(i = 4; i >= 0; i--)
- BB_PUT_BIT(tregs, ((tmp >> i) & 1));
+ BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));
/* Tell it what register we will be writing. */
tmp = (reg & 0xff);
for(i = 4; i >= 0; i--)
- BB_PUT_BIT(tregs, ((tmp >> i) & 1));
+ BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));
/* Tell it to become ready for the bits. */
- BB_PUT_BIT(tregs, 1);
- BB_PUT_BIT(tregs, 0);
+ BB_PUT_BIT(hp, tregs, 1);
+ BB_PUT_BIT(hp, tregs, 0);
for(i = 15; i >= 0; i--)
- BB_PUT_BIT(tregs, ((value >> i) & 1));
+ BB_PUT_BIT(hp, tregs, ((value >> i) & 1));
/* Close down the MIF BitBang outputs. */
- tregs->bb_oenab = 0;
+ hme_write32(hp, &tregs->bb_oenab, 0);
}
#define TCVR_READ_TRIES 16
@@ -205,14 +217,15 @@ static inline int happy_meal_tcvr_read(struct happy_meal *hp,
return happy_meal_bb_read(hp, tregs, reg);
}
- tregs->frame = (FRAME_READ | (hp->paddr << 23) | ((reg & 0xff) << 18));
- while(!(tregs->frame & 0x10000) && --tries)
+ hme_write32(hp, &tregs->frame,
+ (FRAME_READ | (hp->paddr << 23) | ((reg & 0xff) << 18)));
+ while(!(hme_read32(hp, &tregs->frame) & 0x10000) && --tries)
udelay(20);
if(!tries) {
printk("happy meal: Aieee, transceiver MIF read bolixed\n");
return TCVR_FAILURE;
}
- retval = tregs->frame & 0xffff;
+ retval = hme_read32(hp, &tregs->frame) & 0xffff;
ASD(("value=%04x\n", retval));
return retval;
}
@@ -232,9 +245,10 @@ static inline void happy_meal_tcvr_write(struct happy_meal *hp,
return happy_meal_bb_write(hp, tregs, reg, value);
/* Would you like fries with that? */
- tregs->frame = (FRAME_WRITE | (hp->paddr << 23) |
- ((reg & 0xff) << 18) | (value & 0xffff));
- while(!(tregs->frame & 0x10000) && --tries)
+ hme_write32(hp, &tregs->frame,
+ (FRAME_WRITE | (hp->paddr << 23) |
+ ((reg & 0xff) << 18) | (value & 0xffff)));
+ while(!(hme_read32(hp, &tregs->frame) & 0x10000) && --tries)
udelay(20);
/* Anything else? */
@@ -364,9 +378,13 @@ static int set_happy_link_modes(struct happy_meal *hp, struct hmeal_tcvregs *tre
* XXX Happy Meal front end for this to work every time.
*/
if(full)
- hp->bigmacregs->tx_cfg |= BIGMAC_TXCFG_FULLDPLX;
+ hme_write32(hp, &hp->bigmacregs->tx_cfg,
+ hme_read32(hp, &hp->bigmacregs->tx_cfg) |
+ BIGMAC_TXCFG_FULLDPLX);
else
- hp->bigmacregs->tx_cfg &= ~(BIGMAC_TXCFG_FULLDPLX);
+ hme_write32(hp, &hp->bigmacregs->tx_cfg,
+ hme_read32(hp, &hp->bigmacregs->tx_cfg) &
+ ~(BIGMAC_TXCFG_FULLDPLX));
return 0;
no_response:
@@ -541,15 +559,16 @@ static void happy_meal_timer(unsigned long data)
#define TX_RESET_TRIES 32
#define RX_RESET_TRIES 32
-static inline void happy_meal_tx_reset(struct hmeal_bigmacregs *bregs)
+static inline void happy_meal_tx_reset(struct happy_meal *hp,
+ struct hmeal_bigmacregs *bregs)
{
int tries = TX_RESET_TRIES;
HMD(("happy_meal_tx_reset: reset, "));
/* Would you like to try our SMCC Delux? */
- bregs->tx_swreset = 0;
- while((bregs->tx_swreset & 1) && --tries)
+ hme_write32(hp, &bregs->tx_swreset, 0);
+ while((hme_read32(hp, &bregs->tx_swreset) & 1) && --tries)
udelay(20);
/* Lettuce, tomato, buggy hardware (no extra charge)? */
@@ -560,15 +579,16 @@ static inline void happy_meal_tx_reset(struct hmeal_bigmacregs *bregs)
HMD(("done\n"));
}
-static inline void happy_meal_rx_reset(struct hmeal_bigmacregs *bregs)
+static inline void happy_meal_rx_reset(struct happy_meal *hp,
+ struct hmeal_bigmacregs *bregs)
{
int tries = RX_RESET_TRIES;
HMD(("happy_meal_rx_reset: reset, "));
/* We have a special on GNU/Viking hardware bugs today. */
- bregs->rx_swreset = 0;
- while((bregs->rx_swreset & 1) && --tries)
+ hme_write32(hp, &bregs->rx_swreset, 0);
+ while((hme_read32(hp, &bregs->rx_swreset) & 1) && --tries)
udelay(20);
/* Will that be all? */
@@ -581,15 +601,16 @@ static inline void happy_meal_rx_reset(struct hmeal_bigmacregs *bregs)
#define STOP_TRIES 16
-static inline void happy_meal_stop(struct hmeal_gregs *gregs)
+static inline void happy_meal_stop(struct happy_meal *hp,
+ struct hmeal_gregs *gregs)
{
int tries = STOP_TRIES;
HMD(("happy_meal_stop: reset, "));
/* We're consolidating our STB products, it's your lucky day. */
- gregs->sw_reset = GREG_RESET_ALL;
- while(gregs->sw_reset && --tries)
+ hme_write32(hp, &gregs->sw_reset, GREG_RESET_ALL);
+ while(hme_read32(hp, &gregs->sw_reset) && --tries)
udelay(20);
/* Come back next week when we are "Sun Microelectronics". */
@@ -605,19 +626,22 @@ static void happy_meal_get_counters(struct happy_meal *hp,
{
struct net_device_stats *stats = &hp->net_stats;
- stats->rx_crc_errors += bregs->rcrce_ctr;
- bregs->rcrce_ctr = 0;
+ stats->rx_crc_errors += hme_read32(hp, &bregs->rcrce_ctr);
+ hme_write32(hp, &bregs->rcrce_ctr, 0);
- stats->rx_frame_errors += bregs->unale_ctr;
- bregs->unale_ctr = 0;
+ stats->rx_frame_errors += hme_read32(hp, &bregs->unale_ctr);
+ hme_write32(hp, &bregs->unale_ctr, 0);
- stats->rx_length_errors += bregs->gle_ctr;
- bregs->gle_ctr = 0;
+ stats->rx_length_errors += hme_read32(hp, &bregs->gle_ctr);
+ hme_write32(hp, &bregs->gle_ctr, 0);
- stats->tx_aborted_errors += bregs->ex_ctr;
+ stats->tx_aborted_errors += hme_read32(hp, &bregs->ex_ctr);
- stats->collisions += (bregs->ex_ctr + bregs->lt_ctr);
- bregs->ex_ctr = bregs->lt_ctr = 0;
+ stats->collisions +=
+ (hme_read32(hp, &bregs->ex_ctr) +
+ hme_read32(hp, &bregs->lt_ctr));
+ hme_write32(hp, &bregs->ex_ctr, 0);
+ hme_write32(hp, &bregs->lt_ctr, 0);
}
static inline void happy_meal_poll_start(struct happy_meal *hp,
@@ -634,12 +658,12 @@ static inline void happy_meal_poll_start(struct happy_meal *hp,
/* Start the MIF polling on the external transceiver. */
ASD(("polling on, "));
- tmp = tregs->cfg;
+ tmp = hme_read32(hp, &tregs->cfg);
tmp &= ~(TCV_CFG_PDADDR | TCV_CFG_PREGADDR);
tmp |= ((hp->paddr & 0x1f) << 10);
tmp |= (TCV_PADDR_ETX << 3);
tmp |= TCV_CFG_PENABLE;
- tregs->cfg = tmp;
+ hme_write32(hp, &tregs->cfg, tmp);
/* Let the bits set. */
udelay(200);
@@ -660,9 +684,9 @@ static inline void happy_meal_poll_start(struct happy_meal *hp,
/* Listen only for the MIF interrupts we want to hear. */
ASD(("mif ints on, "));
if(speed == 100)
- tregs->int_mask = 0xfffb;
+ hme_write32(hp, &tregs->int_mask, 0xfffb);
else
- tregs->int_mask = 0xfff9;
+ hme_write32(hp, &tregs->int_mask, 0xfff9);
ASD(("done\n"));
}
@@ -680,11 +704,12 @@ static inline void happy_meal_poll_stop(struct happy_meal *hp,
/* Shut up the MIF. */
ASD(("were polling, mif ints off, "));
- tregs->int_mask = 0xffff;
+ hme_write32(hp, &tregs->int_mask, 0xffff);
/* Turn off polling. */
ASD(("polling off, "));
- tregs->cfg &= ~(TCV_CFG_PENABLE);
+ hme_write32(hp, &tregs->cfg,
+ hme_read32(hp, &tregs->cfg) & ~(TCV_CFG_PENABLE));
/* We are no longer polling. */
hp->happy_flags &= ~(HFLAG_POLL);
@@ -706,11 +731,11 @@ static int happy_meal_tcvr_reset(struct happy_meal *hp,
unsigned long tconfig;
int result, tries = TCVR_RESET_TRIES;
- tconfig = tregs->cfg;
+ tconfig = hme_read32(hp, &tregs->cfg);
ASD(("happy_meal_tcvr_reset: tcfg<%08lx> ", tconfig));
if(hp->tcvr_type == external) {
ASD(("external<"));
- tregs->cfg = tconfig & ~(TCV_CFG_PSELECT);
+ hme_write32(hp, &tregs->cfg, tconfig & ~(TCV_CFG_PSELECT));
hp->tcvr_type = internal;
hp->paddr = TCV_PADDR_ITX;
ASD(("ISOLATE,"));
@@ -722,13 +747,13 @@ static int happy_meal_tcvr_reset(struct happy_meal *hp,
return -1;
}
ASD(("phyread_ok,PSELECT>"));
- tregs->cfg = tconfig | TCV_CFG_PSELECT;
+ hme_write32(hp, &tregs->cfg, tconfig | TCV_CFG_PSELECT);
hp->tcvr_type = external;
hp->paddr = TCV_PADDR_ETX;
} else {
if(tconfig & TCV_CFG_MDIO1) {
ASD(("internal<PSELECT,"));
- tregs->cfg = (tconfig | TCV_CFG_PSELECT);
+ hme_write32(hp, &tregs->cfg, (tconfig | TCV_CFG_PSELECT));
ASD(("ISOLATE,"));
happy_meal_tcvr_write(hp, tregs, DP83840_BMCR,
(BMCR_LOOPBACK|BMCR_PDOWN|BMCR_ISOLATE));
@@ -738,7 +763,7 @@ static int happy_meal_tcvr_reset(struct happy_meal *hp,
return -1;
}
ASD(("phyread_ok,~PSELECT>"));
- tregs->cfg = (tconfig & ~(TCV_CFG_PSELECT));
+ hme_write32(hp, &tregs->cfg, (tconfig & ~(TCV_CFG_PSELECT)));
hp->tcvr_type = internal;
hp->paddr = TCV_PADDR_ITX;
}
@@ -795,7 +820,7 @@ static int happy_meal_tcvr_reset(struct happy_meal *hp,
static void happy_meal_transceiver_check(struct happy_meal *hp,
struct hmeal_tcvregs *tregs)
{
- unsigned long tconfig = tregs->cfg;
+ unsigned long tconfig = hme_read32(hp, &tregs->cfg);
ASD(("happy_meal_transceiver_check: tcfg=%08lx ", tconfig));
if(hp->happy_flags & HFLAG_POLL) {
@@ -810,18 +835,20 @@ static void happy_meal_transceiver_check(struct happy_meal *hp,
ASD(("<external>\n"));
tconfig &= ~(TCV_CFG_PENABLE);
tconfig |= TCV_CFG_PSELECT;
- tregs->cfg = tconfig;
+ hme_write32(hp, &tregs->cfg, tconfig);
}
} else {
if(hp->tcvr_type == external) {
ASD(("<external> "));
- if(!(tregs->status >> 16)) {
+ if(!(hme_read32(hp, &tregs->status) >> 16)) {
ASD(("<poll stop> "));
happy_meal_poll_stop(hp, tregs);
hp->paddr = TCV_PADDR_ITX;
hp->tcvr_type = internal;
ASD(("<internal>\n"));
- tregs->cfg &= ~(TCV_CFG_PSELECT);
+ hme_write32(hp, &tregs->cfg,
+ hme_read32(hp, &tregs->cfg) &
+ ~(TCV_CFG_PSELECT));
}
ASD(("\n"));
} else {
@@ -829,18 +856,19 @@ static void happy_meal_transceiver_check(struct happy_meal *hp,
}
}
} else {
- unsigned long reread = tregs->cfg;
+ unsigned long reread = hme_read32(hp, &tregs->cfg);
/* Else we can just work off of the MDIO bits. */
ASD(("<not polling> "));
if(reread & TCV_CFG_MDIO1) {
- tregs->cfg = tconfig | TCV_CFG_PSELECT;
+ hme_write32(hp, &tregs->cfg, tconfig | TCV_CFG_PSELECT);
hp->paddr = TCV_PADDR_ETX;
hp->tcvr_type = external;
ASD(("<external>\n"));
} else {
if(reread & TCV_CFG_MDIO0) {
- tregs->cfg = tconfig & ~(TCV_CFG_PSELECT);
+ hme_write32(hp, &tregs->cfg,
+ tconfig & ~(TCV_CFG_PSELECT));
hp->paddr = TCV_PADDR_ITX;
hp->tcvr_type = internal;
ASD(("<internal>\n"));
@@ -946,11 +974,17 @@ static void happy_meal_init_rings(struct happy_meal *hp, int from_irq)
/* Because we reserve afterwards. */
skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET));
- hb->happy_meal_rxd[i].rx_addr =
- (u32) ((unsigned long)skb->data);
+ if(hp->happy_flags & HFLAG_PCI) {
+ pcihme_write_rxd(&hb->happy_meal_rxd[i],
+ (RXFLAG_OWN |
+ ((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
+ (u32)virt_to_bus((volatile void *)skb->data));
+ } else {
+ hb->happy_meal_rxd[i].rx_addr = (u32)((unsigned long) skb->data);
+ hb->happy_meal_rxd[i].rx_flags =
+ (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
+ }
skb_reserve(skb, RX_OFFSET);
- hb->happy_meal_rxd[i].rx_flags =
- (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
}
HMD(("init txring, "));
@@ -1097,7 +1131,8 @@ static int happy_meal_init(struct happy_meal *hp, int from_irq)
unsigned long regtmp;
unsigned char *e = &hp->dev->dev_addr[0];
- HMD(("happy_meal_init: "));
+ HMD(("happy_meal_init: happy_flags[%08x] ",
+ hp->happy_flags));
if(!(hp->happy_flags & HFLAG_INIT)) {
HMD(("set HFLAG_INIT, "));
hp->happy_flags |= HFLAG_INIT;
@@ -1110,7 +1145,7 @@ static int happy_meal_init(struct happy_meal *hp, int from_irq)
/* Stop transmitter and receiver. */
HMD(("happy_meal_init: to happy_meal_stop\n"));
- happy_meal_stop(gregs);
+ happy_meal_stop(hp, gregs);
/* Alloc and reset the tx/rx descriptor chains. */
HMD(("happy_meal_init: to happy_meal_init_rings\n"));
@@ -1120,16 +1155,21 @@ static int happy_meal_init(struct happy_meal *hp, int from_irq)
happy_meal_init_rings(hp, from_irq);
/* Shut up the MIF. */
- HMD(("happy_meal_init: Disable all MIF irqs, "));
- tregs->int_mask = 0xffff;
+ HMD(("happy_meal_init: Disable all MIF irqs (old[%08x]), ",
+ hme_read32(hp, &tregs->int_mask)));
+ hme_write32(hp, &tregs->int_mask, 0xffff);
/* See if we can enable the MIF frame on this card to speak to the DP83840. */
if(hp->happy_flags & HFLAG_FENABLE) {
- HMD(("use frame, "));
- tregs->cfg &= ~(TCV_CFG_BENABLE);
+ HMD(("use frame old[%08x], ",
+ hme_read32(hp, &tregs->cfg)));
+ hme_write32(hp, &tregs->cfg,
+ hme_read32(hp, &tregs->cfg) & ~(TCV_CFG_BENABLE));
} else {
- HMD(("use bitbang, "));
- tregs->cfg |= TCV_CFG_BENABLE;
+ HMD(("use bitbang old[%08x], ",
+ hme_read32(hp, &tregs->cfg)));
+ hme_write32(hp, &tregs->cfg,
+ hme_read32(hp, &tregs->cfg) | TCV_CFG_BENABLE);
}
/* Check the state of the transceiver. */
@@ -1147,13 +1187,13 @@ static int happy_meal_init(struct happy_meal *hp, int from_irq)
case internal:
/* Using the MII buffers. */
HMD(("internal, using MII, "));
- bregs->xif_cfg = 0;
+ hme_write32(hp, &bregs->xif_cfg, 0);
break;
case external:
/* Not using the MII, disable it. */
HMD(("external, disable MII, "));
- bregs->xif_cfg = BIGMAC_XCFG_MIIDISAB;
+ hme_write32(hp, &bregs->xif_cfg, BIGMAC_XCFG_MIIDISAB);
break;
};
@@ -1162,81 +1202,90 @@ static int happy_meal_init(struct happy_meal *hp, int from_irq)
/* Reset the Happy Meal Big Mac transceiver and the receiver. */
HMD(("tx/rx reset, "));
- happy_meal_tx_reset(bregs);
- happy_meal_rx_reset(bregs);
+ happy_meal_tx_reset(hp, bregs);
+ happy_meal_rx_reset(hp, bregs);
/* Set jam size and inter-packet gaps to reasonable defaults. */
HMD(("jsize/ipg1/ipg2, "));
- bregs->jsize = DEFAULT_JAMSIZE;
- bregs->ipkt_gap1 = DEFAULT_IPG1;
- bregs->ipkt_gap2 = DEFAULT_IPG2;
+ hme_write32(hp, &bregs->jsize, DEFAULT_JAMSIZE);
+ hme_write32(hp, &bregs->ipkt_gap1, DEFAULT_IPG1);
+ hme_write32(hp, &bregs->ipkt_gap2, DEFAULT_IPG2);
/* Load up the MAC address and random seed. */
HMD(("rseed/macaddr, "));
/* XXX use something less deterministic... */
- bregs->rand_seed = 0xbd;
+ hme_write32(hp, &bregs->rand_seed, 0xbd);
- bregs->mac_addr2 = ((e[4] << 8) | e[5]);
- bregs->mac_addr1 = ((e[2] << 8) | e[3]);
- bregs->mac_addr0 = ((e[0] << 8) | e[1]);
+ hme_write32(hp, &bregs->mac_addr2, ((e[4] << 8) | e[5]));
+ hme_write32(hp, &bregs->mac_addr1, ((e[2] << 8) | e[3]));
+ hme_write32(hp, &bregs->mac_addr0, ((e[0] << 8) | e[1]));
/* Ick, figure out how to properly program the hash table later... */
HMD(("htable, "));
- bregs->htable3 = 0;
- bregs->htable2 = 0;
- bregs->htable1 = 0;
- bregs->htable0 = 0;
+ hme_write32(hp, &bregs->htable3, 0);
+ hme_write32(hp, &bregs->htable2, 0);
+ hme_write32(hp, &bregs->htable1, 0);
+ hme_write32(hp, &bregs->htable0, 0);
/* Set the RX and TX ring ptrs. */
- HMD(("ring ptrs\n"));
- erxregs->rx_ring = hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0);
- etxregs->tx_ring = hp->hblock_dvma + hblock_offset(happy_meal_txd, 0);
-
- /* Set the supported SBUS burst sizes. */
- HMD(("happy_meal_init: bursts<"));
-
-#if 0 /* XXX take down huahaga and debug this... */
- /* Grrr... */
+ HMD(("ring ptrs rxr[%08x] txr[%08x]\n",
+ (hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)),
+ (hp->hblock_dvma + hblock_offset(happy_meal_txd, 0))));
+ hme_write32(hp, &erxregs->rx_ring,
+ (hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)));
+ hme_write32(hp, &etxregs->tx_ring,
+ (hp->hblock_dvma + hblock_offset(happy_meal_txd, 0)));
+
+ /* Set the supported burst sizes. */
+ HMD(("happy_meal_init: old[%08x] bursts<",
+ hme_read32(hp, &gregs->cfg)));
+
+#ifdef __sparc_v9__
if(hp->happy_bursts & DMA_BURST64) {
HMD(("64>"));
- gregs->cfg = GREG_CFG_BURST64;
+ hme_write32(hp, &gregs->cfg, GREG_CFG_BURST64);
} else
#endif
if(hp->happy_bursts & DMA_BURST32) {
HMD(("32>"));
- gregs->cfg = GREG_CFG_BURST32;
+ hme_write32(hp, &gregs->cfg, GREG_CFG_BURST32);
} else if(hp->happy_bursts & DMA_BURST16) {
HMD(("16>"));
- gregs->cfg = GREG_CFG_BURST16;
+ hme_write32(hp, &gregs->cfg, GREG_CFG_BURST16);
} else {
HMD(("XXX>"));
- gregs->cfg = 0;
+ hme_write32(hp, &gregs->cfg, 0);
}
/* Turn off interrupts we do not want to hear. */
HMD((", enable global interrupts, "));
- gregs->imask = (GREG_IMASK_GOTFRAME | GREG_IMASK_RCNTEXP |
- GREG_IMASK_SENTFRAME | GREG_IMASK_TXPERR);
+ hme_write32(hp, &gregs->imask,
+ (GREG_IMASK_GOTFRAME | GREG_IMASK_RCNTEXP |
+ GREG_IMASK_SENTFRAME | GREG_IMASK_TXPERR));
/* Set the transmit ring buffer size. */
- HMD(("tx rsize=%d, ", (int)TX_RING_SIZE));
- etxregs->tx_rsize = (TX_RING_SIZE >> ETX_RSIZE_SHIFT) - 1;
+ HMD(("tx rsize=%d oreg[%08x], ", (int)TX_RING_SIZE,
+ hme_read32(hp, &etxregs->tx_rsize)));
+ hme_write32(hp, &etxregs->tx_rsize, (TX_RING_SIZE >> ETX_RSIZE_SHIFT) - 1);
/* Enable transmitter DVMA. */
- HMD(("tx dma enable, "));
- etxregs->cfg |= ETX_CFG_DMAENABLE;
+ HMD(("tx dma enable old[%08x], ",
+ hme_read32(hp, &etxregs->cfg)));
+ hme_write32(hp, &etxregs->cfg,
+ hme_read32(hp, &etxregs->cfg) | ETX_CFG_DMAENABLE);
/* This chip really rots, for the receiver sometimes when you
* write to it's control registers not all the bits get there
* properly. I cannot think of a sane way to provide complete
* coverage for this hardware bug yet.
*/
- HMD(("erx regs bug\n"));
- erxregs->cfg = ERX_CFG_DEFAULT(RX_OFFSET);
- regtmp = erxregs->cfg;
- erxregs->cfg = ERX_CFG_DEFAULT(RX_OFFSET);
- if(erxregs->cfg != ERX_CFG_DEFAULT(RX_OFFSET)) {
+ HMD(("erx regs bug old[%08x]\n",
+ hme_read32(hp, &erxregs->cfg)));
+ hme_write32(hp, &erxregs->cfg, ERX_CFG_DEFAULT(RX_OFFSET));
+ regtmp = hme_read32(hp, &erxregs->cfg);
+ hme_write32(hp, &erxregs->cfg, ERX_CFG_DEFAULT(RX_OFFSET));
+ if(hme_read32(hp, &erxregs->cfg) != ERX_CFG_DEFAULT(RX_OFFSET)) {
printk("happy meal: Eieee, rx config register gets greasy fries.\n");
printk("happy meal: Trying to set %08x, reread gives %08lx\n",
ERX_CFG_DEFAULT(RX_OFFSET), regtmp);
@@ -1244,8 +1293,9 @@ static int happy_meal_init(struct happy_meal *hp, int from_irq)
}
/* Enable Big Mac hash table filter. */
- HMD(("happy_meal_init: enable hash, "));
- bregs->rx_cfg = BIGMAC_RXCFG_HENABLE;
+ HMD(("happy_meal_init: enable hash rx_cfg_old[%08x], ",
+ hme_read32(hp, &bregs->rx_cfg)));
+ hme_write32(hp, &bregs->rx_cfg, BIGMAC_RXCFG_HENABLE);
/* Let the bits settle in the chip. */
udelay(10);
@@ -1255,7 +1305,7 @@ static int happy_meal_init(struct happy_meal *hp, int from_irq)
regtmp = 0;
if(hp->happy_flags & HFLAG_FULL)
regtmp |= BIGMAC_TXCFG_FULLDPLX;
- bregs->tx_cfg = regtmp | BIGMAC_TXCFG_DGIVEUP;
+ hme_write32(hp, &bregs->tx_cfg, regtmp | BIGMAC_TXCFG_DGIVEUP);
/* Enable the output drivers no matter what. */
regtmp = BIGMAC_XCFG_ODENABLE;
@@ -1268,13 +1318,18 @@ static int happy_meal_init(struct happy_meal *hp, int from_irq)
if(hp->tcvr_type == external)
regtmp |= BIGMAC_XCFG_MIIDISAB;
- HMD(("XIF config, "));
- bregs->xif_cfg = regtmp;
+ HMD(("XIF config old[%08x], ",
+ hme_read32(hp, &bregs->xif_cfg)));
+ hme_write32(hp, &bregs->xif_cfg, regtmp);
/* Start things up. */
- HMD(("tx and rx ON!\n"));
- bregs->tx_cfg |= BIGMAC_TXCFG_ENABLE;
- bregs->rx_cfg |= BIGMAC_RXCFG_ENABLE;
+ HMD(("tx old[%08x] and rx [%08x] ON!\n",
+ hme_read32(hp, &bregs->tx_cfg),
+ hme_read32(hp, &bregs->rx_cfg)));
+ hme_write32(hp, &bregs->tx_cfg,
+ hme_read32(hp, &bregs->tx_cfg) | BIGMAC_TXCFG_ENABLE);
+ hme_write32(hp, &bregs->rx_cfg,
+ hme_read32(hp, &bregs->rx_cfg) | BIGMAC_RXCFG_ENABLE);
/* Get the autonegotiation started, and the watch timer ticking. */
happy_meal_begin_auto_negotiation(hp, tregs);
@@ -1289,21 +1344,23 @@ static void happy_meal_set_initial_advertisement(struct happy_meal *hp)
struct hmeal_bigmacregs *bregs = hp->bigmacregs;
struct hmeal_gregs *gregs = hp->gregs;
- happy_meal_stop(gregs);
- tregs->int_mask = 0xffff;
+ happy_meal_stop(hp, gregs);
+ hme_write32(hp, &tregs->int_mask, 0xffff);
if(hp->happy_flags & HFLAG_FENABLE)
- tregs->cfg &= ~(TCV_CFG_BENABLE);
+ hme_write32(hp, &tregs->cfg,
+ hme_read32(hp, &tregs->cfg) & ~(TCV_CFG_BENABLE));
else
- tregs->cfg |= TCV_CFG_BENABLE;
+ hme_write32(hp, &tregs->cfg,
+ hme_read32(hp, &tregs->cfg) | TCV_CFG_BENABLE);
happy_meal_transceiver_check(hp, tregs);
switch(hp->tcvr_type) {
case none:
return;
case internal:
- bregs->xif_cfg = 0;
+ hme_write32(hp, &bregs->xif_cfg, 0);
break;
case external:
- bregs->xif_cfg = BIGMAC_XCFG_MIIDISAB;
+ hme_write32(hp, &bregs->xif_cfg, BIGMAC_XCFG_MIIDISAB);
break;
};
if(happy_meal_tcvr_reset(hp, tregs))
@@ -1544,6 +1601,13 @@ static inline void happy_meal_tx(struct happy_meal *hp)
hp->tx_skbs[elem] = NULL;
hp->net_stats.tx_bytes+=skb->len;
+#ifdef NEED_DMA_SYNCHRONIZATION
+#ifdef CONFIG_PCI
+ if(!(hp->happy_flags & HFLAG_PCI))
+#endif
+ mmu_sync_dma(kva_to_hva(hp, skb->data),
+ skb->len, hp->happy_sbus_dev->my_bus);
+#endif
dev_kfree_skb(skb, FREE_WRITE);
hp->net_stats.tx_packets++;
@@ -1553,6 +1617,39 @@ static inline void happy_meal_tx(struct happy_meal *hp)
TXD((">"));
}
+#ifdef CONFIG_PCI
+static inline void pci_happy_meal_tx(struct happy_meal *hp)
+{
+ struct happy_meal_txd *txbase = &hp->happy_block->happy_meal_txd[0];
+ struct happy_meal_txd *this;
+ int elem = hp->tx_old;
+
+ TXD(("TX<"));
+ while(elem != hp->tx_new) {
+ struct sk_buff *skb;
+ unsigned int flags;
+
+ TXD(("[%d]", elem));
+ this = &txbase[elem];
+ __asm__ __volatile__("lduwa [%1] %2, %0"
+ : "=r" (flags)
+ : "r" (&this->tx_flags), "i" (ASI_PL));
+ if(flags & TXFLAG_OWN)
+ break;
+ skb = hp->tx_skbs[elem];
+ hp->tx_skbs[elem] = NULL;
+ hp->net_stats.tx_bytes+=skb->len;
+
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ hp->net_stats.tx_packets++;
+ elem = NEXT_TX(elem);
+ }
+ hp->tx_old = elem;
+ TXD((">"));
+}
+#endif
+
static inline void sun4c_happy_meal_tx(struct happy_meal *hp)
{
struct happy_meal_txd *txbase = &hp->happy_block->happy_meal_txd[0];
@@ -1616,13 +1713,19 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev,
/* Return it to the Happy meal. */
drop_it:
hp->net_stats.rx_dropped++;
- this->rx_addr =
- (u32) ((unsigned long)hp->rx_skbs[elem]->data);
+ this->rx_addr = kva_to_hva(hp, hp->rx_skbs[elem]->data);
this->rx_flags =
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
goto next;
}
skb = hp->rx_skbs[elem];
+#ifdef NEED_DMA_SYNCHRONIZATION
+#ifdef CONFIG_PCI
+ if(!(hp->happy_flags & HFLAG_PCI))
+#endif
+ mmu_sync_dma(kva_to_hva(hp, skb->data),
+ skb->len, hp->happy_sbus_dev->my_bus);
+#endif
if(len > RX_COPY_THRESHOLD) {
struct sk_buff *new_skb;
@@ -1636,8 +1739,7 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev,
hp->rx_skbs[elem] = new_skb;
new_skb->dev = dev;
skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET));
- rxbase[elem].rx_addr =
- (u32) ((unsigned long)new_skb->data);
+ rxbase[elem].rx_addr = kva_to_hva(hp, new_skb->data);
skb_reserve(new_skb, RX_OFFSET);
rxbase[elem].rx_flags =
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
@@ -1658,8 +1760,7 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev,
memcpy(copy_skb->data, skb->data, len);
/* Reuse original ring buffer. */
- rxbase[elem].rx_addr =
- (u32) ((unsigned long)skb->data);
+ rxbase[elem].rx_addr = kva_to_hva(hp, skb->data);
rxbase[elem].rx_flags =
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
@@ -1688,6 +1789,117 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev,
RXD((">"));
}
+#ifdef CONFIG_PCI
+static inline void pci_happy_meal_rx(struct happy_meal *hp, struct device *dev,
+ struct hmeal_gregs *gregs)
+{
+ struct happy_meal_rxd *rxbase = &hp->happy_block->happy_meal_rxd[0];
+ struct happy_meal_rxd *this;
+ unsigned int flags;
+ int elem = hp->rx_new, drops = 0;
+
+ RXD(("RX<"));
+ this = &rxbase[elem];
+ __asm__ __volatile__("lduwa [%1] %2, %0"
+ : "=r" (flags)
+ : "r" (&this->rx_flags), "i" (ASI_PL));
+ while(!(flags & RXFLAG_OWN)) {
+ struct sk_buff *skb;
+ int len;
+ u16 csum;
+
+ RXD(("[%d ", elem));
+
+ len = flags >> 16;
+ csum = flags & RXFLAG_CSUM;
+
+ /* Check for errors. */
+ if((len < ETH_ZLEN) || (flags & RXFLAG_OVERFLOW)) {
+ RXD(("ERR(%08lx)]", flags));
+ hp->net_stats.rx_errors++;
+ if(len < ETH_ZLEN)
+ hp->net_stats.rx_length_errors++;
+ if(len & (RXFLAG_OVERFLOW >> 16)) {
+ hp->net_stats.rx_over_errors++;
+ hp->net_stats.rx_fifo_errors++;
+ }
+
+ /* Return it to the Happy meal. */
+ drop_it:
+ hp->net_stats.rx_dropped++;
+ pcihme_write_rxd(this,
+ (RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
+ (u32) virt_to_bus((volatile void *)hp->rx_skbs[elem]->data));
+ goto next;
+ }
+ skb = hp->rx_skbs[elem];
+ if(len > RX_COPY_THRESHOLD) {
+ struct sk_buff *new_skb;
+
+ /* Now refill the entry, if we can. */
+ new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
+ if(!new_skb) {
+ drops++;
+ goto drop_it;
+ }
+
+ hp->rx_skbs[elem] = new_skb;
+ new_skb->dev = dev;
+ skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET));
+ pcihme_write_rxd(&rxbase[elem],
+ (RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
+ (u32)virt_to_bus((volatile void *)new_skb->data));
+ skb_reserve(new_skb, RX_OFFSET);
+
+ /* Trim the original skb for the netif. */
+ skb_trim(skb, len);
+ } else {
+ struct sk_buff *copy_skb = dev_alloc_skb(len+2);
+
+ if(!copy_skb) {
+ drops++;
+ goto drop_it;
+ }
+
+ copy_skb->dev = dev;
+ skb_reserve(copy_skb, 2);
+ skb_put(copy_skb, len);
+ memcpy(copy_skb->data, skb->data, len);
+
+ /* Reuse original ring buffer. */
+ pcihme_write_rxd(&rxbase[elem],
+ (RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
+ (u32)virt_to_bus((volatile void *)skb->data));
+
+ skb = copy_skb;
+ }
+
+ /* This card is _fucking_ hot... */
+ if(!~(csum))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb->ip_summed = CHECKSUM_NONE;
+
+ RXD(("len=%d csum=%4x]", len, csum));
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+
+ hp->net_stats.rx_packets++;
+ hp->net_stats.rx_bytes+=len;
+ next:
+ elem = NEXT_RX(elem);
+ this = &rxbase[elem];
+ __asm__ __volatile__("lduwa [%1] %2, %0"
+ : "=r" (flags)
+ : "r" (&this->rx_flags), "i" (ASI_PL));
+ }
+ hp->rx_new = elem;
+ if(drops)
+ printk("%s: Memory squeeze, deferring packet.\n", hp->dev->name);
+ RXD((">"));
+}
+#endif
+
static inline void sun4c_happy_meal_rx(struct happy_meal *hp, struct device *dev,
struct hmeal_gregs *gregs)
{
@@ -1757,7 +1969,7 @@ static void happy_meal_interrupt(int irq, void *dev_id, struct pt_regs *regs)
struct happy_meal *hp = (struct happy_meal *) dev->priv;
struct hmeal_gregs *gregs = hp->gregs;
struct hmeal_tcvregs *tregs = hp->tcvregs;
- unsigned long happy_status = gregs->stat;
+ unsigned int happy_status = hme_read32(hp, &gregs->stat);
HMD(("happy_meal_interrupt: status=%08lx ", happy_status));
@@ -1795,13 +2007,59 @@ static void happy_meal_interrupt(int irq, void *dev_id, struct pt_regs *regs)
HMD(("done\n"));
}
+#ifdef CONFIG_PCI
+static void pci_happy_meal_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *) dev_id;
+ struct happy_meal *hp = (struct happy_meal *) dev->priv;
+ struct hmeal_gregs *gregs = hp->gregs;
+ struct hmeal_tcvregs *tregs = hp->tcvregs;
+ unsigned int happy_status = readl((unsigned long)&gregs->stat);
+
+ HMD(("happy_meal_interrupt: status=%08lx ", happy_status));
+
+ dev->interrupt = 1;
+
+ if(happy_status & GREG_STAT_ERRORS) {
+ HMD(("ERRORS "));
+ if(happy_meal_is_not_so_happy(hp, gregs, /* un- */ happy_status)) {
+ dev->interrupt = 0;
+ return;
+ }
+ }
+
+ if(happy_status & GREG_STAT_MIFIRQ) {
+ HMD(("MIFIRQ "));
+ happy_meal_mif_interrupt(hp, gregs, tregs);
+ }
+
+ if(happy_status & GREG_STAT_TXALL) {
+ HMD(("TXALL "));
+ pci_happy_meal_tx(hp);
+ }
+
+ if(happy_status & GREG_STAT_RXTOHOST) {
+ HMD(("RXTOHOST "));
+ pci_happy_meal_rx(hp, dev, gregs);
+ }
+
+ if(dev->tbusy && (TX_BUFFS_AVAIL(hp) >= 0)) {
+ hp->dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ dev->interrupt = 0;
+ HMD(("done\n"));
+}
+#endif
+
static void sun4c_happy_meal_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct device *dev = (struct device *) dev_id;
struct happy_meal *hp = (struct happy_meal *) dev->priv;
struct hmeal_gregs *gregs = hp->gregs;
struct hmeal_tcvregs *tregs = hp->tcvregs;
- unsigned long happy_status = gregs->stat;
+ unsigned int happy_status = hme_read32(hp, &gregs->stat);
HMD(("happy_meal_interrupt: status=%08lx ", happy_status));
@@ -1852,7 +2110,41 @@ static int happy_meal_open(struct device *dev)
printk("happy meal: Can't order irq %d to go.\n", dev->irq);
return -EAGAIN;
}
- } else {
+ }
+#ifdef __sparc_v9__
+ else if(sparc_cpu_model == sun4u) {
+ struct devid_cookie dcookie;
+
+#ifdef CONFIG_PCI
+ if(hp->happy_flags & HFLAG_PCI) {
+ if(request_irq(dev->irq, &pci_happy_meal_interrupt,
+ SA_SHIRQ, "HAPPY MEAL (PCI)", dev)) {
+ HMD(("EAGAIN\n"));
+ printk("happy_meal(PCI: Can't order irq %d to go.\n",
+ dev->irq);
+ return -EAGAIN;
+ }
+ goto v9_done;
+ }
+#endif
+ dcookie.real_dev_id = dev;
+ dcookie.imap = dcookie.iclr = 0;
+ dcookie.pil = -1;
+ dcookie.bus_cookie = hp->happy_sbus_dev->my_bus;
+ if(request_irq(dev->irq, &happy_meal_interrupt,
+ (SA_SHIRQ | SA_SBUS | SA_DCOOKIE),
+ "HAPPY MEAL", &dcookie)) {
+ HMD(("EAGAIN\n"));
+ printk("happy_meal(SBUS): Can't order irq %d to go.\n",
+ dev->irq);
+ return -EAGAIN;
+ }
+#ifdef CONFIG_PCI
+ v9_done:
+#endif
+ }
+#endif
+ else {
if(request_irq(dev->irq, &happy_meal_interrupt,
SA_SHIRQ, "HAPPY MEAL", (void *) dev)) {
HMD(("EAGAIN\n"));
@@ -1874,8 +2166,12 @@ static int happy_meal_close(struct device *dev)
{
struct happy_meal *hp = (struct happy_meal *) dev->priv;
- happy_meal_stop(hp->gregs);
+ happy_meal_stop(hp, hp->gregs);
happy_meal_clean_rings(hp);
+
+ /* If auto-negotiation timer is running, kill it. */
+ del_timer(&hp->happy_timer);
+
free_irq(dev->irq, (void *)dev);
MOD_DEC_USE_COUNT;
return 0;
@@ -1917,15 +2213,14 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct device *dev)
SXD(("SX<l[%d]e[%d]>", len, entry));
hp->tx_skbs[entry] = skb;
- hp->happy_block->happy_meal_txd[entry].tx_addr =
- (u32) ((unsigned long)skb->data);
+ hp->happy_block->happy_meal_txd[entry].tx_addr = kva_to_hva(hp, skb->data);
hp->happy_block->happy_meal_txd[entry].tx_flags =
(TXFLAG_OWN | TXFLAG_SOP | TXFLAG_EOP | (len & TXFLAG_SIZE));
hp->tx_new = NEXT_TX(entry);
/* Get it going. */
dev->trans_start = jiffies;
- hp->etxregs->tx_pnding = ETX_TP_DMAWAKEUP;
+ hme_write32(hp, &hp->etxregs->tx_pnding, ETX_TP_DMAWAKEUP);
if(TX_BUFFS_AVAIL(hp))
dev->tbusy = 0;
@@ -1933,6 +2228,56 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct device *dev)
return 0;
}
+#ifdef CONFIG_PCI
+static int pci_happy_meal_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct happy_meal *hp = (struct happy_meal *) dev->priv;
+ int len, entry;
+
+ if(dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+
+ if (tickssofar < 40) {
+ return 1;
+ } else {
+ printk ("%s: transmit timed out, resetting\n", dev->name);
+ hp->net_stats.tx_errors++;
+ happy_meal_init(hp, 0);
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+ return 0;
+ }
+ }
+
+ if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
+ printk("happy meal: Transmitter access conflict.\n");
+ return 1;
+ }
+
+ if(!TX_BUFFS_AVAIL(hp))
+ return 1;
+
+ len = skb->len;
+ entry = hp->tx_new;
+
+ SXD(("SX<l[%d]e[%d]>", len, entry));
+ hp->tx_skbs[entry] = skb;
+ pcihme_write_txd(&hp->happy_block->happy_meal_txd[entry],
+ (TXFLAG_OWN|TXFLAG_SOP|TXFLAG_EOP|(len & TXFLAG_SIZE)),
+ (u32) virt_to_bus((volatile void *)skb->data));
+ hp->tx_new = NEXT_TX(entry);
+
+ /* Get it going. */
+ dev->trans_start = jiffies;
+ writel(ETX_TP_DMAWAKEUP, (unsigned long)&hp->etxregs->tx_pnding);
+
+ if(TX_BUFFS_AVAIL(hp))
+ dev->tbusy = 0;
+
+ return 0;
+}
+#endif
+
static int sun4c_happy_meal_start_xmit(struct sk_buff *skb, struct device *dev)
{
struct happy_meal *hp = (struct happy_meal *) dev->priv;
@@ -2023,12 +2368,13 @@ static void happy_meal_set_multicast(struct device *dev)
set_bit(0, (void *) &dev->tbusy);
if((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) {
- bregs->htable0 = 0xffff;
- bregs->htable1 = 0xffff;
- bregs->htable2 = 0xffff;
- bregs->htable3 = 0xffff;
+ hme_write32(hp, &bregs->htable0, 0xffff);
+ hme_write32(hp, &bregs->htable1, 0xffff);
+ hme_write32(hp, &bregs->htable2, 0xffff);
+ hme_write32(hp, &bregs->htable3, 0xffff);
} else if(dev->flags & IFF_PROMISC) {
- bregs->rx_cfg |= BIGMAC_RXCFG_PMISC;
+ hme_write32(hp, &bregs->rx_cfg,
+ hme_read32(hp, &bregs->rx_cfg) | BIGMAC_RXCFG_PMISC);
} else {
u16 hash_table[4];
@@ -2056,19 +2402,20 @@ static void happy_meal_set_multicast(struct device *dev)
crc >>= 26;
hash_table[crc >> 4] |= 1 << (crc & 0xf);
}
- bregs->htable0 = hash_table[0];
- bregs->htable1 = hash_table[1];
- bregs->htable2 = hash_table[2];
- bregs->htable3 = hash_table[3];
+ hme_write32(hp, &bregs->htable0, hash_table[0]);
+ hme_write32(hp, &bregs->htable1, hash_table[1]);
+ hme_write32(hp, &bregs->htable2, hash_table[2]);
+ hme_write32(hp, &bregs->htable3, hash_table[3]);
}
/* Let us get going again. */
dev->tbusy = 0;
}
+static unsigned hme_version_printed = 0;
+
static inline int happy_meal_ether_init(struct device *dev, struct linux_sbus_device *sdev)
{
- static unsigned version_printed = 0;
struct happy_meal *hp;
int i;
@@ -2079,10 +2426,10 @@ static inline int happy_meal_ether_init(struct device *dev, struct linux_sbus_de
if(dev->priv == NULL)
return -ENOMEM;
}
- if(version_printed++ == 0)
+ if(hme_version_printed++ == 0)
printk(version);
- printk("%s: HAPPY MEAL 10/100baseT Ethernet ", dev->name);
+ printk("%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ", dev->name);
dev->base_addr = (long) sdev;
for(i = 0; i < 6; i++)
@@ -2095,6 +2442,9 @@ static inline int happy_meal_ether_init(struct device *dev, struct linux_sbus_de
memset(hp, 0, sizeof(*hp));
hp->happy_sbus_dev = sdev;
+#ifdef CONFIG_PCI
+ hp->happy_pci_dev = NULL;
+#endif
if(sdev->num_registers != 5) {
printk("happymeal: Device does not have 5 regs, it has %d.\n",
@@ -2211,6 +2561,124 @@ static inline int happy_meal_ether_init(struct device *dev, struct linux_sbus_de
return 0;
}
+#ifdef CONFIG_PCI
+__initfunc(int happy_meal_pci_init(struct device *dev, struct pci_dev *pdev))
+{
+ struct pcidev_cookie *pcp;
+ struct happy_meal *hp;
+ unsigned long hpreg_base;
+ unsigned short pci_command;
+ int i, node;
+
+ if(dev == NULL) {
+ dev = init_etherdev(0, sizeof(struct happy_meal));
+ } else {
+ dev->priv = kmalloc(sizeof(struct happy_meal), GFP_KERNEL);
+ if(dev->priv == NULL)
+ return -ENOMEM;
+ }
+ if(hme_version_printed++ == 0)
+ printk(version);
+
+ printk("%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ", dev->name);
+
+ dev->base_addr = (long) pdev;
+ for(i = 0; i < 6; i++)
+ printk("%2.2x%c",
+ dev->dev_addr[i] = idprom->id_ethaddr[i],
+ i == 5 ? ' ' : ':');
+
+ printk("\n");
+
+ hp = (struct happy_meal *)dev->priv;
+ memset(hp, 0, sizeof(*hp));
+
+ hp->happy_sbus_dev = NULL;
+ hp->happy_pci_dev = pdev;
+
+ hpreg_base = pdev->base_address[0];
+ if((hpreg_base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
+ printk("happymeal(PCI): Cannot find proper PCI device base address.\n");
+ return ENODEV;
+ }
+ hpreg_base &= PCI_BASE_ADDRESS_MEM_MASK;
+
+ /* Now make sure pci_dev cookie is there. */
+ pcp = pdev->sysdata;
+ if(pcp == NULL || pcp->prom_node == -1) {
+ printk("happymeal(PCI): Some PCI device info missing\n");
+ return ENODEV;
+ }
+ node = pcp->prom_node;
+
+ /* Layout registers. */
+ hp->gregs = (struct hmeal_gregs *) (hpreg_base + 0x0000);
+ hp->etxregs = (struct hmeal_etxregs *) (hpreg_base + 0x2000);
+ hp->erxregs = (struct hmeal_erxregs *) (hpreg_base + 0x4000);
+ hp->bigmacregs = (struct hmeal_bigmacregs *) (hpreg_base + 0x6000);
+ hp->tcvregs = (struct hmeal_tcvregs *) (hpreg_base + 0x7000);
+
+ hp->hm_revision = prom_getintdefault(node, "hm-rev", 0xff);
+ if(hp->hm_revision == 0xff)
+ hp->hm_revision = 0xa0;
+
+ /* Now enable the feature flags we can. */
+ if(hp->hm_revision == 0x20 || hp->hm_revision == 0x21)
+ hp->happy_flags = HFLAG_20_21;
+ else if(hp->hm_revision != 0xa0)
+ hp->happy_flags = HFLAG_NOT_A0;
+
+ /* And of course, indicate this is PCI. */
+ hp->happy_flags |= HFLAG_PCI;
+
+ /* Assume PCI happy meals can handle all burst sizes. */
+ hp->happy_bursts = DMA_BURSTBITS;
+
+ hp->happy_block = (struct hmeal_init_block *) get_free_page(GFP_DMA);
+ if(!hp->happy_block) {
+ printk("happymeal(PCI): Cannot get hme init block.\n");
+ return ENODEV;
+ }
+
+ hp->hblock_dvma = (u32) virt_to_bus(hp->happy_block);
+ hp->sun4c_buffers = 0;
+
+ hp->linkcheck = 0;
+ hp->timer_state = asleep;
+ hp->timer_ticks = 0;
+ happy_meal_set_initial_advertisement(hp);
+
+ hp->dev = dev;
+ dev->open = &happy_meal_open;
+ dev->stop = &happy_meal_close;
+ dev->hard_start_xmit = &pci_happy_meal_start_xmit;
+ dev->get_stats = &happy_meal_get_stats;
+ dev->set_multicast_list = &happy_meal_set_multicast;
+ dev->irq = pdev->irq;
+ dev->dma = 0;
+ ether_setup(dev);
+
+ /* If we don't do this, nothing works. */
+ pcibios_read_config_word(pdev->bus->number,
+ pdev->devfn,
+ PCI_COMMAND, &pci_command);
+ pci_command |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pdev->bus->number,
+ pdev->devfn,
+ PCI_COMMAND, pci_command);
+
+#ifdef MODULE
+ /* We are home free at this point, link us in to the happy
+ * module device list.
+ */
+ dev->ifindex = dev_new_index();
+ hp->next_module = root_happy_dev;
+ root_happy_dev = hp;
+#endif
+ return 0;
+}
+#endif
+
__initfunc(int happy_meal_probe(struct device *dev))
{
struct linux_sbus *bus;
@@ -2224,7 +2692,8 @@ __initfunc(int happy_meal_probe(struct device *dev))
for_each_sbus(bus) {
for_each_sbusdev(sdev, bus) {
- if(cards) dev = NULL;
+ if(cards)
+ dev = NULL;
if(!strcmp(sdev->prom_name, "SUNW,hme")) {
cards++;
if((v = happy_meal_ether_init(dev, sdev)))
@@ -2232,6 +2701,23 @@ __initfunc(int happy_meal_probe(struct device *dev))
}
}
}
+#ifdef CONFIG_PCI
+ if(pcibios_present()) {
+ struct pci_dev *pdev;
+
+ for(pdev = pci_devices; pdev; pdev = pdev->next) {
+ if(cards)
+ dev = NULL;
+ if((pdev->vendor == PCI_VENDOR_ID_SUN) &&
+ (pdev->device == PCI_DEVICE_ID_SUN_HAPPYMEAL)) {
+ cards++;
+ if((v = happy_meal_pci_init(dev, pdev)))
+ return v;
+ }
+
+ }
+ }
+#endif
if(!cards)
return ENODEV;
return 0;
diff --git a/drivers/net/sunhme.h b/drivers/net/sunhme.h
index 4f23db4e6..38730b0d7 100644
--- a/drivers/net/sunhme.h
+++ b/drivers/net/sunhme.h
@@ -7,6 +7,8 @@
#ifndef _SUNHME_H
#define _SUNHME_H
+#include <linux/config.h>
+
/* Happy Meal global registers. */
struct hmeal_gregs {
volatile unsigned int sw_reset; /* Software Reset */
@@ -552,6 +554,9 @@ struct happy_meal {
struct net_device_stats net_stats; /* Statistical counters */
struct linux_sbus_device *happy_sbus_dev; /* ;-) */
+#ifdef CONFIG_PCI
+ struct pci_dev *happy_pci_dev;
+#endif
struct device *dev; /* Backpointer */
struct happy_meal *next_module;
};
@@ -568,6 +573,7 @@ struct happy_meal {
#define HFLAG_RXCV 0x00000100 /* XXX RXCV ENABLE */
#define HFLAG_INIT 0x00000200 /* Init called at least once */
#define HFLAG_LINKUP 0x00000400 /* 1 = Link is up */
+#define HFLAG_PCI 0x00000800 /* PCI based Happy Meal */
#define HFLAG_20_21 (HFLAG_POLLENABLE | HFLAG_FENABLE)
#define HFLAG_NOT_A0 (HFLAG_POLLENABLE | HFLAG_FENABLE | HFLAG_LANCE | HFLAG_RXCV)
@@ -589,4 +595,66 @@ static inline struct sk_buff *happy_meal_alloc_skb(unsigned int length, int gfp_
return skb;
}
+/* Register/DMA access stuff, used to cope with differences between
+ * PCI and SBUS happy meals.
+ */
+extern inline u32 kva_to_hva(struct happy_meal *hp, char *addr)
+{
+#ifdef CONFIG_PCI
+ if(hp->happy_flags & HFLAG_PCI)
+ return (u32) virt_to_bus((volatile void *)addr);
+ else
+#endif
+ return (u32) ((unsigned long)addr);
+}
+
+extern inline unsigned int hme_read32(struct happy_meal *hp,
+ volatile unsigned int *reg)
+{
+#ifdef CONFIG_PCI
+ if(hp->happy_flags & HFLAG_PCI)
+ return readl((unsigned long)reg);
+ else
+#endif
+ return *reg;
+}
+
+extern inline void hme_write32(struct happy_meal *hp,
+ volatile unsigned int *reg,
+ unsigned int val)
+{
+#ifdef CONFIG_PCI
+ if(hp->happy_flags & HFLAG_PCI)
+ writel(val, (unsigned long)reg);
+ else
+#endif
+ *reg = val;
+}
+
+#ifdef CONFIG_PCI
+extern inline void pcihme_write_rxd(struct happy_meal_rxd *rp,
+ unsigned int flags,
+ unsigned int addr)
+{
+ __asm__ __volatile__("
+ stwa %3, [%0] %2
+ stwa %4, [%1] %2
+" : /* no outputs */
+ : "r" (&rp->rx_addr), "r" (&rp->rx_flags),
+ "i" (ASI_PL), "r" (addr), "r" (flags));
+}
+
+extern inline void pcihme_write_txd(struct happy_meal_txd *tp,
+ unsigned int flags,
+ unsigned int addr)
+{
+ __asm__ __volatile__("
+ stwa %3, [%0] %2
+ stwa %4, [%1] %2
+" : /* no outputs */
+ : "r" (&tp->tx_addr), "r" (&tp->tx_flags),
+ "i" (ASI_PL), "r" (addr), "r" (flags));
+}
+#endif
+
#endif /* !(_SUNHME_H) */
diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c
index 4e7325016..52a3cad23 100644
--- a/drivers/net/sunlance.c
+++ b/drivers/net/sunlance.c
@@ -1,4 +1,4 @@
-/* $Id: sunlance.c,v 1.64 1997/05/14 20:46:40 davem Exp $
+/* $Id: sunlance.c,v 1.68 1997/08/15 06:44:36 davem Exp $
* lance.c: Linux/Sparc/Lance driver
*
* Written 1995, 1996 by Miguel de Icaza
@@ -75,11 +75,13 @@ static char *lancedma = "LANCE DMA";
#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/string.h>
+#include <linux/delay.h>
#include <linux/init.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
+#include <asm/pgtable.h>
#include <linux/errno.h>
#include <asm/byteorder.h> /* Used by the checksum routines */
@@ -92,6 +94,7 @@ static char *lancedma = "LANCE DMA";
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/auxio.h> /* For tpe-link-test? setting */
+#include <asm/irq.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -241,6 +244,7 @@ struct lance_private {
struct device *dev; /* Backpointer */
struct lance_private *next_module;
+ struct linux_sbus *sbus;
};
#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
@@ -661,6 +665,23 @@ static int lance_open (struct device *dev)
last_dev = dev;
+#ifdef __sparc_v9__
+ if (sparc_cpu_model == sun4u) {
+ struct devid_cookie dcookie;
+
+ dcookie.real_dev_id = dev;
+ dcookie.imap = dcookie.iclr = 0;
+ dcookie.pil = -1;
+ dcookie.bus_cookie = lp->sbus;
+ if(request_irq(dev->irq, &lance_interrupt,
+ (SA_SHIRQ | SA_SBUS | SA_DCOOKIE),
+ lancestr, &dcookie)) {
+ printk ("Lance: Can't get irq %d\n", dev->irq);
+ return -EAGAIN;
+ }
+
+ } else
+#endif
if (request_irq (dev->irq, &lance_interrupt, SA_SHIRQ,
lancestr, (void *) dev)) {
printk ("Lance: Can't get irq %d\n", dev->irq);
@@ -978,6 +999,7 @@ sparc_lance_init (struct device *dev, struct linux_sbus_device *sdev,
/* Make certain the data structures used by the LANCE are aligned. */
dev->priv = (void *)(((unsigned long)dev->priv + 7) & ~7);
lp = (struct lance_private *) dev->priv;
+ lp->sbus = sdev->my_bus;
if (lebuffer){
prom_apply_sbus_ranges (lebuffer->my_bus,
&lebuffer->reg_addrs [0],
diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c
index 6b417e34b..b17db5de0 100644
--- a/drivers/net/sunqe.c
+++ b/drivers/net/sunqe.c
@@ -21,7 +21,9 @@ static char *version =
#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/string.h>
+#include <linux/delay.h>
#include <linux/init.h>
+
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
@@ -36,6 +38,7 @@ static char *version =
#include <asm/auxio.h>
#include <asm/system.h>
#include <asm/pgtable.h>
+#include <asm/irq.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -153,7 +156,7 @@ static void qe_init_rings(struct sunqe *qep, int from_irq)
skb_reserve(skb, 34);
qb->qe_rxd[i].rx_addr =
- (unsigned int) ((unsigned long)skb->data);
+ (u32) ((unsigned long)skb->data);
qb->qe_rxd[i].rx_flags =
(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
}
@@ -444,6 +447,10 @@ static inline void qe_tx(struct sunqe *qep)
skb = qep->tx_skbs[elem];
qep->tx_skbs[elem] = NULL;
qep->net_stats.tx_bytes+=skb->len;
+#ifdef NEED_DMA_SYNCHRONIZATION
+ mmu_sync_dma(((u32)((unsigned long)skb->data)),
+ skb->len, qep->qe_sbusdev->my_bus);
+#endif
dev_kfree_skb(skb, FREE_WRITE);
qep->net_stats.tx_packets++;
@@ -492,12 +499,16 @@ static inline void qe_rx(struct sunqe *qep)
/* Return it to the QE. */
qep->net_stats.rx_dropped++;
this->rx_addr =
- (unsigned int) ((unsigned long)qep->rx_skbs[elem]->data);
+ (u32) ((unsigned long)qep->rx_skbs[elem]->data);
this->rx_flags =
(RXD_OWN | (RX_BUF_ALLOC_SIZE & RXD_LENGTH));
goto next;
}
skb = qep->rx_skbs[elem];
+#ifdef NEED_DMA_SYNCHRONIZATION
+ mmu_sync_dma(((u32)((unsigned long)skb->data)),
+ skb->len, qep->qe_sbusdev->my_bus);
+#endif
if(len > RX_COPY_THRESHOLD) {
struct sk_buff *new_skb;
@@ -514,7 +525,7 @@ static inline void qe_rx(struct sunqe *qep)
skb_reserve(new_skb, 34);
rxbase[elem].rx_addr =
- (unsigned int) ((unsigned long)new_skb->data);
+ (u32) ((unsigned long)new_skb->data);
rxbase[elem].rx_flags =
(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
@@ -535,7 +546,7 @@ static inline void qe_rx(struct sunqe *qep)
/* Reuse original ring buffer. */
rxbase[elem].rx_addr =
- (unsigned int) ((unsigned long)skb->data);
+ (u32) ((unsigned long)skb->data);
rxbase[elem].rx_flags =
(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
@@ -738,8 +749,7 @@ static int qe_start_xmit(struct sk_buff *skb, struct device *dev)
qep->tx_skbs[entry] = skb;
- /* FIX FOR ULTRA */
- qep->qe_block->qe_txd[entry].tx_addr = (unsigned long) skb->data;
+ qep->qe_block->qe_txd[entry].tx_addr = (u32) ((unsigned long) skb->data);
qep->qe_block->qe_txd[entry].tx_flags =
(TXD_OWN | TXD_SOP | TXD_EOP | (len & TXD_LENGTH));
qep->tx_new = NEXT_TX(entry);
@@ -1110,7 +1120,25 @@ static inline int qec_ether_init(struct device *dev, struct linux_sbus_device *s
res = EAGAIN;
goto qec_free_devs;
}
- } else {
+ }
+#ifdef __sparc_v9__
+ else if(sparc_cpu_model == sun4u) {
+ struct devid_cookie dcookie;
+
+ dcookie.real_dev_id = qecp;
+ dcookie.imap = dcookie.iclr = 0;
+ dcookie.pil = -1;
+ dcookie.bus_cookie = sdev->my_bus;
+ if(request_irq(sdev->irqs[0].pri, &qec_interrupt,
+ (SA_SHIRQ | SA_SBUS | SA_DCOOKIE),
+ "QuadEther", &dcookie)) {
+ printk("QuadEther: Can't register QEC master irq handler.\n");
+ res = EAGAIN;
+ goto qec_free_devs;
+ }
+ }
+#endif
+ else {
if(request_irq(sdev->irqs[0].pri, &qec_interrupt,
SA_SHIRQ, "QuadEther", (void *) qecp)) {
printk("QuadEther: Can't register QEC master irq handler.\n");
diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c
index d3d9d0e1c..556ca044b 100644
--- a/drivers/net/tlan.c
+++ b/drivers/net/tlan.c
@@ -35,6 +35,7 @@
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/etherdevice.h>
+#include <linux/delay.h>
@@ -44,15 +45,21 @@
static int TLanDevicesInstalled = 0;
#endif
static int debug = 0;
+ static int aui = 0;
static u8 *TLanPadBuffer;
static char TLanSignature[] = "TLAN";
static int TLanVersionMajor = 0;
- static int TLanVersionMinor = 27;
+ static int TLanVersionMinor = 32;
static TLanPciId TLanDeviceList[] = {
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10, "Compaq Netelligent 10" },
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100, "Compaq Netelligent 10/100" },
- { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3_INTEGRATED, "Compaq Integrated NetFlex-3" },
+ { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED, "Compaq Integrated NetFlex-3/P" },
+ { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3P, "Compaq NetFlex-3/P" },
+ { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3P_BNC, "Compaq NetFlex-3/P w/ BNC" },
+ { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT, "Compaq ProLiant Netelligent 10/100" },
+ { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL, "Compaq Dual Port Netelligent 10/100" },
+ { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_DESKPRO_4000_5233MMX, "Compaq Deskpro 4000 5233MMX" },
{ 0, 0, NULL } /* End of List */
};
@@ -100,6 +107,7 @@
u16 sio, tmp;
u32 i;
int err;
+ int minten;
err = FALSE;
outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
@@ -108,8 +116,10 @@
cli();
TLan_MiiSync(base_port);
-
- TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio); /* Disable PHY ints */
+
+ minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
+ if ( minten )
+ TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio);
TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */
TLan_MiiSendData( base_port, 0x2, 2 ); /* Read ( 10b ) */
@@ -145,7 +155,8 @@
TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */
TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
- TLan_SetBit(TLAN_NET_SIO_MINTEN, sio); /* Enable PHY ints */
+ if ( minten )
+ TLan_SetBit(TLAN_NET_SIO_MINTEN, sio);
*val = tmp;
@@ -250,6 +261,7 @@
void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val)
{
u16 sio;
+ int minten;
outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
@@ -258,7 +270,9 @@
TLan_MiiSync( base_port );
- TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio );
+ minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
+ if ( minten )
+ TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio );
TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */
TLan_MiiSendData( base_port, 0x1, 2 ); /* Write ( 01b ) */
@@ -271,7 +285,8 @@
TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); /* Idle cycle */
TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
- TLan_SetBit( TLAN_NET_SIO_MINTEN, sio );
+ if ( minten )
+ TLan_SetBit( TLAN_NET_SIO_MINTEN, sio );
sti();
@@ -447,11 +462,11 @@
int TLan_PhyInternalCheck( struct device *dev )
{
u16 gen_ctl;
- int i;
u32 io;
u16 phy;
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
u16 value;
+ u8 sio;
io = dev->base_addr;
phy = priv->phyAddr;
@@ -461,8 +476,7 @@
TLan_MiiSync( io );
TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE );
TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
- for ( i = 0; i < 500000; i++ )
- SLOW_DOWN_IO;
+ udelay(50000);
TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
TLan_MiiSync( io );
}
@@ -475,14 +489,20 @@
// TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
- for ( i = 0; i < 500000; i++ )
- SLOW_DOWN_IO;
+ udelay(500000);
+
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
+ if ( aui )
+ value |= TLAN_TC_AUISEL;
+ else
+ value &= ~TLAN_TC_AUISEL;
+ TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
// Read Possible Latched Link Status
TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
// Read Real Link Status
TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
- if ( value & MII_GS_LINK ) {
+ if ( ( value & MII_GS_LINK ) || aui ) {
priv->phyOnline = 1;
TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
} else {
@@ -494,6 +514,10 @@
TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
value |= TLAN_TC_INTEN;
TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
+
+ sio = TLan_DioRead8( io, TLAN_NET_SIO );
+ sio |= TLAN_NET_SIO_MINTEN;
+ TLan_DioWrite8( io, TLAN_NET_SIO, sio );
return 0;
@@ -530,7 +554,7 @@
TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts );
TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts );
TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp );
- if ( gen_sts & MII_GS_LINK ) {
+ if ( ( gen_sts & MII_GS_LINK ) || aui ) {
priv->phyOnline = 1;
TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
} else {
@@ -566,6 +590,7 @@
u16 phy;
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
u16 value;
+ u8 sio;
io = dev->base_addr;
phy = priv->phyAddr;
@@ -612,6 +637,9 @@
value |= TLAN_TC_INTEN;
TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
*/
+ sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO );
+ sio &= ~TLAN_NET_SIO_MINTEN;
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio );
priv->phyOnline = 1;
return 0;
@@ -741,7 +769,8 @@
printk( "TLAN: Forward = 0x%08x\n", list->forward );
printk( "TLAN: CSTAT = 0x%04hx\n", list->cStat );
printk( "TLAN: Frame Size = 0x%04hx\n", list->frameSize );
- for ( i = 0; i < 10; i++ ) {
+ // for ( i = 0; i < 10; i++ ) {
+ for ( i = 0; i < 2; i++ ) {
printk( "TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address );
}
@@ -874,14 +903,13 @@
TLan_SetBit( TLAN_NET_SIO_NMRST, dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO );
// 10a. Other.
// 12. Setup the NetMask register.
- TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, TLAN_ID_TX_EOC | TLAN_ID_RX_EOC );
+ if ( priv->tlanRev >= 0x30 )
+ TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, TLAN_ID_TX_EOC | TLAN_ID_RX_EOC );
//TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP | TLAN_NET_CMD_DUPLEX | TLAN_NET_CMD_CAF );
TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP );
// 11. Initialize PHYs.
TLan_PhySelect( dev );
(*priv->phyCheck)( dev );
- if ( debug >= 1 )
- TLan_PhyPrint( dev );
//data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK6 | TLAN_NET_MASK_MASK7;
data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK7;
@@ -1226,7 +1254,7 @@
TLanList *head_list;
u32 ack = 1;
- // printk( "TLAN: Handling Tx EOF\n" );
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOF (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
host_int = 0;
head_list = priv->txList + priv->txHead;
if ( head_list->cStat & TLAN_CSTAT_EOC )
@@ -1235,13 +1263,18 @@
printk( "TLAN: Received interrupt for uncompleted TX frame.\n" );
}
// printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat );
+
+#if LINUX_KERNEL_VERSION > 0x20100
+ priv->stats->tx_bytes += head_list->frameSize;
+#endif
+
head_list->cStat = TLAN_CSTAT_UNUSED;
dev->tbusy = 0;
priv->txHead++;
if ( priv->txHead >= TLAN_NUM_TX_LISTS )
priv->txHead = 0;
if ( eoc ) {
- // printk( "TLAN: Handling Tx EOC\n" );
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
head_list = priv->txList + priv->txHead;
if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) {
outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
@@ -1326,7 +1359,7 @@
TLanList *tail_list;
void *t;
- // printk( "TLAN: Handling Rx EOF Head=%d Tail=%d\n", priv->rxHead, priv->rxTail );
+ TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOF (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail );
host_int = 0;
head_list = priv->rxList + priv->rxHead;
tail_list = priv->rxList + priv->rxTail;
@@ -1344,6 +1377,11 @@
skb_reserve( skb, 2 );
t = (void *) skb_put( skb, head_list->frameSize );
// printk( " %hd %p %p\n", head_list->frameSize, skb->data, t );
+
+#if LINUX_KERNEL_VERSION > 0x20100
+ priv->stats->rx_bytes += head_list->frameSize;
+#endif
+
memcpy( t, head_buffer, head_list->frameSize );
skb->protocol = eth_type_trans( skb, dev );
netif_rx( skb );
@@ -1360,7 +1398,7 @@
if ( priv->rxTail >= TLAN_NUM_RX_LISTS )
priv->rxTail = 0;
if ( eoc ) {
- // printk( "TLAN: Handling Rx EOC Head=%d Tail=%d\n", priv->rxHead, priv->rxTail );
+ TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail );
head_list = priv->rxList + priv->rxHead;
outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
ack |= TLAN_HC_GO | TLAN_HC_RT;
@@ -1421,15 +1459,30 @@
*
* This driver is structured to determine EOC occurances by reading the
* CSTAT member of the list structure. Tx EOC interrupts are disabled
- * via the DIO INTDIS register.
+ * via the DIO INTDIS register. However, TLAN chips before revision 3.0
+ * didn't have this functionality, so process EOC events if this is the
+ * case.
*
************************************************************************/
u32 TLan_HandleTxEOC( struct device *dev, u16 host_int )
{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ TLanList *head_list;
+ u32 ack = 1;
+
host_int = 0;
- printk( "TLAN: Tx EOC interrupt on %s.\n", dev->name );
- return 1;
+ if ( priv->tlanRev < 0x30 ) {
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d) -- IRQ\n", priv->txHead, priv->txTail );
+ head_list = priv->txList + priv->txHead;
+ if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) {
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO;
+ } else {
+ priv->txInProgress = 0;
+ }
+ }
+ return ack;
} /* TLan_HandleTxEOC */
@@ -1485,10 +1538,13 @@
net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS );
if ( net_sts )
TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts );
- if ( net_sts & TLAN_NET_STS_MIRQ )
+ if ( net_sts & TLAN_NET_STS_MIRQ ) {
(*priv->phyService)( dev );
-
- TLAN_DBG( 1, "TLAN: Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts );
+ if (debug) {
+ TLan_PhyPrint( dev );
+ }
+ }
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts );
}
return ack;
@@ -1507,15 +1563,27 @@
*
* This driver is structured to determine EOC occurances by reading the
* CSTAT member of the list structure. Rx EOC interrupts are disabled
- * via the DIO INTDIS register.
+ * via the DIO INTDIS register. However, TLAN chips before revision 3.0
+ * didn't have this CSTAT member or a INTDIS register, so if this chip
+ * is pre-3.0, process EOC interrupts normally.
*
************************************************************************/
u32 TLan_HandleRxEOC( struct device *dev, u16 host_int )
{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ TLanList *head_list;
+ u32 ack = 1;
+
host_int = 0;
- printk( "TLAN: Rx EOC interrupt on %s.\n", dev->name );
- return 1;
+ if ( priv->tlanRev < 0x30 ) {
+ TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d) -- IRQ\n", priv->rxHead, priv->rxTail );
+ head_list = priv->rxList + priv->rxHead;
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO | TLAN_HC_RT;
+ priv->rxEocCount++;
+ }
+ return ack;
} /* TLan_HandleRxEOC */
@@ -1629,7 +1697,7 @@
static int TLan_StartTx(struct sk_buff *, struct device *);
static void TLan_HandleInterrupt(int, void *, struct pt_regs *);
static int TLan_Close(struct device *);
- static struct enet_statistics *TLan_GetStats( struct device * );
+ static struct net_device_stats *TLan_GetStats( struct device * );
static void TLan_SetMulticastList( struct device * );
@@ -1773,7 +1841,6 @@
u32 io_base, dl_ix;
found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix );
- TLAN_DBG( 1, "TLAN: Probing...\n" );
if ( found ) {
dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL );
if ( dev->priv == NULL ) {
@@ -1865,7 +1932,7 @@
pci_dfn
);
if ( ! not_found ) {
- TLAN_DBG( 1, "TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n",
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n",
TLanDeviceList[dl_index].vendorId,
TLanDeviceList[dl_index].deviceId
);
@@ -1878,19 +1945,19 @@
if (pci_latency < 0x10) {
pcibios_write_config_byte( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, 0xff);
- TLAN_DBG( 1, "TLAN: Setting latency timer to max.\n");
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Setting latency timer to max.\n");
}
if ((pci_command & PCI_COMMAND_IO) && (*pci_io_base & 0x3)) {
*pci_io_base &= PCI_BASE_ADDRESS_IO_MASK;
- TLAN_DBG( 1, "TLAN: IO mapping is available at %x.\n", *pci_io_base);
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: IO mapping is available at %x.\n", *pci_io_base);
} else {
*pci_io_base = 0;
- printk("TLAN: IO mapping not available, ignoring device.\n");
+ printk("TLAN: IO mapping not available, ignoring device.\n");
}
if (pci_command & PCI_COMMAND_MASTER) {
- TLAN_DBG( 1, "TLAN: Bus mastering is active.\n");
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Bus mastering is active.\n");
}
pci_index++;
@@ -1992,9 +2059,10 @@
int err;
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION );
err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
if ( err ) {
- printk( "TLAN: %s: IRQ %d already in use.\n", dev->name, dev->irq );
+ printk( "TLAN: Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq );
return -EAGAIN;
}
@@ -2010,6 +2078,7 @@
TLan_ResetLists( dev );
TLan_ReadAndClearStats( dev, TLAN_IGNORE );
TLan_Reset( dev );
+ TLan_Reset( dev );
TLan_SetMac( dev, 0, dev->dev_addr );
outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
if ( debug >= 1 )
@@ -2024,12 +2093,11 @@
priv->timerType = TLAN_TIMER_LINK;
add_timer( &priv->timer );
} else {
- TLAN_DBG( 1, "TLAN: RX GO\n" );
outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
}
- TLAN_DBG( 1, "TLAN: Device %s opened.\n", dev->name );
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s opened. Revision = %x\n", dev->name, priv->tlanRev );
return 0;
@@ -2063,17 +2131,16 @@
u8 *tail_buffer;
int pad;
- // printk( "Entering StartTx\n" );
if ( ! priv->phyOnline ) {
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s PHY is not ready\n", dev->name );
dev_kfree_skb( skb, FREE_WRITE );
return 0;
}
tail_list = priv->txList + priv->txTail;
if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) {
- // printk( "TLAN: %s TX is busy, Head=%d Tail=%d\n", dev->name, priv->txHead, priv->txTail );
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s is busy (Head=%d Tail=%d)\n", dev->name, priv->txHead, priv->txTail );
dev->tbusy = 1;
- // printk( "TLAN: Tx is busy.\n");
priv->txBusyCount++;
return 1;
}
@@ -2098,14 +2165,11 @@
if ( ! priv->txInProgress ) {
priv->txInProgress = 1;
outw( 0x4, dev->base_addr + TLAN_HOST_INT );
- // printk("TLAN: Sending GO for 0%d\n", priv->txTail );
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Starting TX on buffer %d\n", priv->txTail );
outl( virt_to_bus( tail_list ), dev->base_addr + TLAN_CH_PARM );
outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD );
} else {
- // printk("TLAN: Adding 0%d\n", priv->txTail );
- // Assign previous list to point to this one. If previous has
- // already been read, the EOC check in the TX EOF interrupt handler
- // will start another transfer.
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Adding buffer %d to TX channel\n", priv->txTail );
if ( priv->txTail == 0 )
( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list );
else
@@ -2119,7 +2183,6 @@
dev_kfree_skb( skb, FREE_WRITE );
dev->trans_start = jiffies;
- // printk( "Leaving StartTx\n" );
return 0;
} /* TLan_StartTx */
@@ -2154,7 +2217,7 @@
dev = (struct device *) dev_id;
if ( dev->interrupt )
- TLAN_DBG( 1, "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt );
+ printk( "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt );
dev->interrupt++;
cli();
@@ -2204,7 +2267,7 @@
if ( priv->timerSetAt != 0 )
del_timer( &priv->timer );
free_irq( dev->irq, dev );
- TLAN_DBG( 1, "TLAN: Device %s closed.\n", dev->name );
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s closed.\n", dev->name );
MOD_DEC_USE_COUNT;
@@ -2227,22 +2290,26 @@
*
************************************************************************/
- struct enet_statistics *TLan_GetStats( struct device *dev )
+ struct net_device_stats *TLan_GetStats( struct device *dev )
{
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
int i;
/* Should only read stats if open ? */
TLan_ReadAndClearStats( dev, TLAN_RECORD );
- printk( "TLAN: %s Rx EOC Count: %d\n", dev->name, priv->rxEocCount );
- printk( "TLAN: %s Tx Busy Count: %d\n", dev->name, priv->txBusyCount );
- // printk( "TLAN: Got stats for %s.\n", dev->name );
- TLan_PrintDio( dev->base_addr );
- TLan_PhyPrint( dev );
- for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ )
- TLan_PrintList( priv->rxList + i, "RX", i );
- for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ )
- TLan_PrintList( priv->txList + i, "TX", i );
+
+ TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: %s EOC count = %d\n", dev->name, priv->rxEocCount );
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s Busy count = %d\n", dev->name, priv->txBusyCount );
+ if ( debug & TLAN_DEBUG_GNRL ) {
+ TLan_PrintDio( dev->base_addr );
+ TLan_PhyPrint( dev );
+ }
+ if ( debug & TLAN_DEBUG_LIST ) {
+ for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ )
+ TLan_PrintList( priv->rxList + i, "RX", i );
+ for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ )
+ TLan_PrintList( priv->txList + i, "TX", i );
+ }
return ( &( (TLanPrivateInfo *) dev->priv )->stats );
@@ -2307,3 +2374,6 @@
}
} /* TLan_SetRxMode */
+
+
+
diff --git a/drivers/net/tlan.h b/drivers/net/tlan.h
index 076f6b71a..c66e70582 100644
--- a/drivers/net/tlan.h
+++ b/drivers/net/tlan.h
@@ -1,3 +1,6 @@
+#ifndef TLAN_H
+#define TLAN_H
+
/********************************************************************
*
* Linux ThunderLAN Driver
@@ -18,6 +21,11 @@
#include <asm/types.h>
#include <linux/netdevice.h>
+#if LINUX_VERSION_CODE <= 0x20100
+#define net_device_stats enet_statistics
+#endif
+
+
/*****************************************************************
@@ -37,7 +45,11 @@
#define TLAN_IGNORE 0
#define TLAN_RECORD 1
- #define TLAN_DBG(lvl, format, args...) if ( debug >= lvl ) printk( format, ##args );
+ #define TLAN_DBG(lvl, format, args...) if ( debug & lvl ) printk( format, ##args );
+ #define TLAN_DEBUG_GNRL 0x0001
+ #define TLAN_DEBUG_TX 0x0002
+ #define TLAN_DEBUG_RX 0x0004
+ #define TLAN_DEBUG_LIST 0x0008
@@ -50,7 +62,12 @@
/* NOTE: These should be moved to pci.h someday */
#define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34
#define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32
- #define PCI_DEVICE_ID_NETFLEX_3_INTEGRATED 0xAE35
+ #define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED 0xAE35
+ #define PCI_DEVICE_ID_NETFLEX_3P 0xF130
+ #define PCI_DEVICE_ID_NETFLEX_3P_BNC 0xF150
+ #define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT 0xAE43
+ #define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL 0xAE40
+ #define PCI_DEVICE_ID_DESKPRO_4000_5233MMX 0xB011
typedef struct tlan_pci_id {
@@ -152,11 +169,12 @@
u32 timerSetAt;
u32 timerType;
struct timer_list timer;
- struct enet_statistics stats;
+ struct net_device_stats stats;
u32 pciEntry;
u8 pciRevision;
u8 pciBus;
u8 pciDeviceFn;
+ u8 tlanRev;
char devName[8];
} TLanPrivateInfo;
@@ -478,3 +496,8 @@ inline u32 TLan_HashFunc( u8 *a )
return hash;
}
+
+
+
+
+#endif
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 16dcfafa0..4a8b247ca 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1,5 +1,5 @@
/*
- * drivers/pci/pci.c
+ * $Id: pci.c,v 1.44 1997/09/03 05:08:22 richard Exp $
*
* PCI services that are built on top of the BIOS32 service.
*
@@ -20,7 +20,6 @@
struct pci_bus pci_root;
struct pci_dev *pci_devices = 0;
-
/*
* The bridge_id field is an offset of an item into the array
* BRIDGE_MAPPING_TYPE. 0xff indicates that the device is not a PCI
@@ -43,6 +42,9 @@ struct pci_dev *pci_devices = 0;
*/
struct pci_dev_info dev_info[] = {
DEVICE( COMPAQ, COMPAQ_1280, "QVision 1280/p"),
+ DEVICE( COMPAQ, COMPAQ_NETELL100,"Netelligent 10/100"),
+ DEVICE( COMPAQ, COMPAQ_NETELL10,"Netelligent 10"),
+ DEVICE( COMPAQ, COMPAQ_NETFLEX3,"NetFlex 3"),
DEVICE( COMPAQ, COMPAQ_THUNDER, "ThunderLAN"),
DEVICE( NCR, NCR_53C810, "53c810"),
DEVICE( NCR, NCR_53C820, "53c820"),
@@ -63,7 +65,9 @@ struct pci_dev_info dev_info[] = {
DEVICE( VLSI, VLSI_82C593, "82C593-FC1"),
DEVICE( VLSI, VLSI_82C594, "82C594-AFC2"),
DEVICE( VLSI, VLSI_82C597, "82C597-AFC2"),
+ DEVICE( VLSI, VLSI_VAS96011, "VAS96011 PowerPC"),
DEVICE( ADL, ADL_2301, "2301"),
+ DEVICE( NS, NS_87415, "87415"),
DEVICE( NS, NS_87410, "87410"),
DEVICE( TSENG, TSENG_W32P_2, "ET4000W32P"),
DEVICE( TSENG, TSENG_W32P_b, "ET4000W32P rev B"),
@@ -74,8 +78,9 @@ struct pci_dev_info dev_info[] = {
DEVICE( WEITEK, WEITEK_P9100, "P9100"),
BRIDGE( DEC, DEC_BRD, "DC21050", 0x00),
DEVICE( DEC, DEC_TULIP, "DC21040"),
- DEVICE( DEC, DEC_TGA, "DC21030"),
+ DEVICE( DEC, DEC_TGA, "TGA"),
DEVICE( DEC, DEC_TULIP_FAST, "DC21140"),
+ DEVICE( DEC, DEC_TGA2, "TGA2"),
DEVICE( DEC, DEC_FDDI, "DEFPA"),
DEVICE( DEC, DEC_TULIP_PLUS, "DC21041"),
DEVICE( DEC, DEC_21142, "DC21142"),
@@ -87,11 +92,15 @@ struct pci_dev_info dev_info[] = {
DEVICE( CIRRUS, CIRRUS_5434_8, "GD 5434"),
DEVICE( CIRRUS, CIRRUS_5436, "GD 5436"),
DEVICE( CIRRUS, CIRRUS_5446, "GD 5446"),
+ DEVICE( CIRRUS, CIRRUS_5480, "GD 5480"),
DEVICE( CIRRUS, CIRRUS_5464, "GD 5464"),
+ DEVICE( CIRRUS, CIRRUS_5465, "GD 5465"),
DEVICE( CIRRUS, CIRRUS_6729, "CL 6729"),
+ DEVICE( CIRRUS, CIRRUS_6832, "PD 6832"),
DEVICE( CIRRUS, CIRRUS_7542, "CL 7542"),
DEVICE( CIRRUS, CIRRUS_7543, "CL 7543"),
DEVICE( CIRRUS, CIRRUS_7541, "CL 7541"),
+ DEVICE( IBM, IBM_FIRE_CORAL, "Fire Coral"),
DEVICE( IBM, IBM_82G2675, "82G2675"),
DEVICE( IBM, IBM_82351, "82351"),
DEVICE( WD, WD_7197, "WD 7197"),
@@ -104,6 +113,7 @@ struct pci_dev_info dev_info[] = {
DEVICE( MATROX, MATROX_MGA_2, "Atlas PX2085"),
DEVICE( MATROX, MATROX_MIL, "Millennium"),
DEVICE( MATROX, MATROX_MYS, "Mystique"),
+ DEVICE( MATROX, MATROX_MIL_2, "Millennium II"),
DEVICE( MATROX, MATROX_MGA_IMP, "MGA Impression"),
DEVICE( CT, CT_65545, "65545"),
DEVICE( CT, CT_65548, "65548"),
@@ -118,6 +128,7 @@ struct pci_dev_info dev_info[] = {
DEVICE( SI, SI_501, "85C501"),
DEVICE( SI, SI_496, "85C496"),
DEVICE( SI, SI_601, "85C601"),
+ DEVICE( SI, SI_5107, "5107"),
DEVICE( SI, SI_5511, "85C5511"),
DEVICE( SI, SI_5513, "85C5513"),
DEVICE( SI, SI_5571, "5571"),
@@ -137,12 +148,17 @@ struct pci_dev_info dev_info[] = {
DEVICE( BUSLOGIC, BUSLOGIC_MULTIMASTER_NC, "MultiMaster NC"),
DEVICE( BUSLOGIC, BUSLOGIC_MULTIMASTER, "MultiMaster"),
DEVICE( BUSLOGIC, BUSLOGIC_FLASHPOINT, "FlashPoint"),
+ DEVICE( TI, TI_PCI1130, "PCI1130"),
+ DEVICE( TI, TI_PCI1131, "PCI1131"),
DEVICE( OAK, OAK_OTI107, "OTI107"),
DEVICE( WINBOND2, WINBOND2_89C940,"NE2000-PCI"),
DEVICE( MOTOROLA, MOTOROLA_MPC105,"MPC105 Eagle"),
DEVICE( MOTOROLA, MOTOROLA_MPC106,"MPC106 Grackle"),
DEVICE( MOTOROLA, MOTOROLA_RAVEN, "Raven"),
DEVICE( PROMISE, PROMISE_5300, "DC5030"),
+ DEVICE( APPLE, APPLE_BANDIT, "Bandit"),
+ DEVICE( APPLE, APPLE_GC, "Grand Central"),
+ DEVICE( APPLE, APPLE_HYDRA, "Hydra"),
DEVICE( N9, N9_I128, "Imagine 128"),
DEVICE( N9, N9_I128_2, "Imagine 128v2"),
DEVICE( UMC, UMC_UM8673F, "UM8673F"),
@@ -160,8 +176,19 @@ struct pci_dev_info dev_info[] = {
DEVICE( QLOGIC, QLOGIC_ISP1022, "ISP1022"),
DEVICE( LEADTEK, LEADTEK_805, "S3 805"),
DEVICE( CONTAQ, CONTAQ_82C599, "82C599"),
+ DEVICE( OLICOM, OLICOM_OC3136, "OC-3136/3137"),
+ DEVICE( OLICOM, OLICOM_OC2315, "OC-2315"),
+ DEVICE( OLICOM, OLICOM_OC2325, "OC-2325"),
+ DEVICE( OLICOM, OLICOM_OC2183, "OC-2183/2185"),
+ DEVICE( OLICOM, OLICOM_OC2326, "OC-2326"),
+ DEVICE( OLICOM, OLICOM_OC6151, "OC-6151/6152"),
+ DEVICE( SUN, SUN_EBUS, "EBUS"),
+ DEVICE( SUN, SUN_HAPPYMEAL, "Happy Meal"),
+ BRIDGE( SUN, SUN_PBM, "PCI Bus Module", 0x02),
DEVICE( CMD, CMD_640, "640 (buggy)"),
+ DEVICE( CMD, CMD_643, "643"),
DEVICE( CMD, CMD_646, "646"),
+ DEVICE( CMD, CMD_670, "670"),
DEVICE( VISION, VISION_QD8500, "QD-8500"),
DEVICE( VISION, VISION_QD8580, "QD-8580"),
DEVICE( BROOKTREE, BT848, "Brooktree 848"),
@@ -170,6 +197,7 @@ struct pci_dev_info dev_info[] = {
DEVICE( WINBOND, WINBOND_83769, "W83769F"),
DEVICE( WINBOND, WINBOND_82C105, "SL82C105"),
DEVICE( WINBOND, WINBOND_83C553, "W83C553"),
+ DEVICE( DATABOOK, DATABOOK_87144, "DB87144"),
DEVICE( 3COM, 3COM_3C590, "3C590 10bT"),
DEVICE( 3COM, 3COM_3C595TX, "3C595 100bTX"),
DEVICE( 3COM, 3COM_3C595T4, "3C595 100bT4"),
@@ -186,6 +214,7 @@ struct pci_dev_info dev_info[] = {
DEVICE( AL, AL_M1513, "M1513"),
DEVICE( AL, AL_M4803, "M4803"),
DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_NM2070, "Magicgraph NM2070"),
+ DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_128V, "MagicGraph 128V"),
DEVICE( ASP, ASP_ABP940, "ABP940"),
DEVICE( ASP, ASP_ABP940U, "ABP940U"),
DEVICE( CERN, CERN_SPSB_PMC, "STAR/RD24 SCI-PCI (PMC)"),
@@ -198,6 +227,8 @@ struct pci_dev_info dev_info[] = {
DEVICE( INTERG, INTERG_1680, "IGA-1680"),
DEVICE( INTERG, INTERG_1682, "IGA-1682"),
DEVICE( REALTEK, REALTEK_8029, "8029"),
+ DEVICE( REALTEK, REALTEK_8129, "8129"),
+ DEVICE( TRUEVISION, TRUEVISION_T1000,"TARGA 1000"),
DEVICE( INIT, INIT_320P, "320 P"),
DEVICE( VIA, VIA_82C505, "VT 82C505"),
DEVICE( VIA, VIA_82C561, "VT 82C561"),
@@ -206,6 +237,7 @@ struct pci_dev_info dev_info[] = {
DEVICE( VIA, VIA_82C585, "VT 82C585VP Apollo VP-1"),
DEVICE( VIA, VIA_82C586, "VT 82C586 Apollo VP-1"),
DEVICE( VIA, VIA_82C416, "VT 82C416MV"),
+ DEVICE( VIA, VIA_82C926, "VT 82C926 Amazon"),
DEVICE( VORTEX, VORTEX_GDT60x0, "GDT 60x0"),
DEVICE( VORTEX, VORTEX_GDT6000B,"GDT 6000b"),
DEVICE( VORTEX, VORTEX_GDT6x10, "GDT 6110/6510"),
@@ -225,18 +257,26 @@ struct pci_dev_info dev_info[] = {
DEVICE( FORE, FORE_PCA200PC, "PCA-200PC"),
DEVICE( FORE, FORE_PCA200E, "PCA-200E"),
DEVICE( IMAGINGTECH, IMAGINGTECH_ICPCI, "MVC IC-PCI"),
+ DEVICE( PHILIPS, PHILIPS_SAA7146,"SAA7146"),
DEVICE( PLX, PLX_9060, "PCI9060 i960 bridge"),
DEVICE( ALLIANCE, ALLIANCE_PROMOTIO, "Promotion-6410"),
DEVICE( ALLIANCE, ALLIANCE_PROVIDEO, "Provideo"),
DEVICE( VMIC, VMIC_VME, "VMIVME-7587"),
DEVICE( DIGI, DIGI_RIGHTSWITCH, "RightSwitch SE-6"),
DEVICE( MUTECH, MUTECH_MV1000, "MV-1000"),
+ DEVICE( RENDITION, RENDITION_VERITE,"Verite 1000"),
DEVICE( TOSHIBA, TOSHIBA_601, "Laptop"),
+ DEVICE( RICOH, RICOH_RL5C466, "RL5C466"),
DEVICE( ZEITNET, ZEITNET_1221, "1221"),
DEVICE( ZEITNET, ZEITNET_1225, "1225"),
- DEVICE( OMEGA, OMEGA_PCMCIA, "PCMCIA"),
+ DEVICE( OMEGA, OMEGA_82C092G, "82C092G"),
+ BRIDGE( GALILEO, GALILEO_GT64011, "GT64011", 0x00),
+ DEVICE( NP, NP_PCI_FDDI, "NP-PCI"),
DEVICE( SPECIALIX, SPECIALIX_XIO, "XIO/SIO host"),
DEVICE( SPECIALIX, SPECIALIX_RIO, "RIO host"),
+ DEVICE( IKON, IKON_10115, "10115 Greensheet"),
+ DEVICE( IKON, IKON_10117, "10117 Greensheet"),
+ DEVICE( ZORAN, ZORAN_36057, "ZR36057"),
DEVICE( ZORAN, ZORAN_36120, "ZR36120"),
DEVICE( COMPEX, COMPEX_RL2000, "ReadyLink 2000"),
DEVICE( RP, RP8OCTA, "RocketPort 8 Oct"),
@@ -257,6 +297,7 @@ struct pci_dev_info dev_info[] = {
DEVICE( SYMPHONY, SYMPHONY_101, "82C101"),
DEVICE( TEKRAM, TEKRAM_DC290, "DC-290"),
DEVICE( 3DLABS, 3DLABS_300SX, "GLINT 300SX"),
+ DEVICE( 3DLABS, 3DLABS_500TX, "GLINT 500TX"),
DEVICE( 3DLABS, 3DLABS_DELTA, "GLINT Delta"),
DEVICE( 3DLABS, 3DLABS_PERMEDIA,"PERMEDIA"),
DEVICE( AVANCE, AVANCE_ALG2064, "ALG2064i"),
@@ -273,6 +314,9 @@ struct pci_dev_info dev_info[] = {
DEVICE( S3, S3_964_1, "Vision 964-P"),
DEVICE( S3, S3_964_2, "Vision 964-P"),
DEVICE( S3, S3_968, "Vision 968"),
+ DEVICE( S3, S3_TRIO64V2, "Trio64V2"),
+ DEVICE( S3, S3_PLATO_PXG, "Plato"),
+ DEVICE( S3, S3_ViRGE_DXGX, "ViRGE/DX"),
DEVICE( INTEL, INTEL_82375, "82375EB"),
BRIDGE( INTEL, INTEL_82424, "82424ZX Saturn", 0x00),
DEVICE( INTEL, INTEL_82378, "82378IB"),
@@ -539,13 +583,15 @@ const char *pci_strvendor(unsigned int vendor)
case PCI_VENDOR_ID_HP: return "Hewlett Packard";
case PCI_VENDOR_ID_PCTECH: return "PCTECH";
case PCI_VENDOR_ID_DPT: return "DPT";
- case PCI_VENDOR_ID_OPTI: return "OPTI";
+ case PCI_VENDOR_ID_OPTI: return "OPTi";
case PCI_VENDOR_ID_SGS: return "SGS Thomson";
case PCI_VENDOR_ID_BUSLOGIC: return "BusLogic";
+ case PCI_VENDOR_ID_TI: return "Texas Instruments";
case PCI_VENDOR_ID_OAK: return "OAK";
case PCI_VENDOR_ID_WINBOND2: return "Winbond";
case PCI_VENDOR_ID_MOTOROLA: return "Motorola";
case PCI_VENDOR_ID_PROMISE: return "Promise Technology";
+ case PCI_VENDOR_ID_APPLE: return "Apple";
case PCI_VENDOR_ID_N9: return "Number Nine";
case PCI_VENDOR_ID_UMC: return "UMC";
case PCI_VENDOR_ID_X: return "X TECHNOLOGY";
@@ -555,14 +601,18 @@ const char *pci_strvendor(unsigned int vendor)
case PCI_VENDOR_ID_CONTAQ: return "Contaq";
case PCI_VENDOR_ID_FOREX: return "Forex";
case PCI_VENDOR_ID_OLICOM: return "Olicom";
+ case PCI_VENDOR_ID_SUN: return "Sun Microsystems";
case PCI_VENDOR_ID_CMD: return "CMD";
case PCI_VENDOR_ID_VISION: return "Vision";
case PCI_VENDOR_ID_BROOKTREE: return "Brooktree";
case PCI_VENDOR_ID_SIERRA: return "Sierra";
case PCI_VENDOR_ID_ACC: return "ACC MICROELECTRONICS";
case PCI_VENDOR_ID_WINBOND: return "Winbond";
+ case PCI_VENDOR_ID_DATABOOK: return "Databook";
case PCI_VENDOR_ID_3COM: return "3Com";
+ case PCI_VENDOR_ID_SMC: return "SMC";
case PCI_VENDOR_ID_AL: return "Acer Labs";
+ case PCI_VENDOR_ID_MITSUBISHI: return "Mitsubishi";
case PCI_VENDOR_ID_NEOMAGIC: return "Neomagic";
case PCI_VENDOR_ID_ASP: return "Advanced System Products";
case PCI_VENDOR_ID_CERN: return "CERN";
@@ -572,21 +622,28 @@ const char *pci_strvendor(unsigned int vendor)
case PCI_VENDOR_ID_AMCC: return "AMCC";
case PCI_VENDOR_ID_INTERG: return "Intergraphics";
case PCI_VENDOR_ID_REALTEK: return "Realtek";
+ case PCI_VENDOR_ID_TRUEVISION: return "Truevision";
case PCI_VENDOR_ID_INIT: return "Initio Corp";
case PCI_VENDOR_ID_VIA: return "VIA Technologies";
case PCI_VENDOR_ID_VORTEX: return "VORTEX";
case PCI_VENDOR_ID_EF: return "Efficient Networks";
case PCI_VENDOR_ID_FORE: return "Fore Systems";
case PCI_VENDOR_ID_IMAGINGTECH: return "Imaging Technology";
+ case PCI_VENDOR_ID_PHILIPS: return "Philips";
case PCI_VENDOR_ID_PLX: return "PLX";
case PCI_VENDOR_ID_ALLIANCE: return "Alliance";
case PCI_VENDOR_ID_VMIC: return "VMIC";
case PCI_VENDOR_ID_DIGI: return "Digi Intl.";
case PCI_VENDOR_ID_MUTECH: return "Mutech";
+ case PCI_VENDOR_ID_RENDITION: return "Rendition";
case PCI_VENDOR_ID_TOSHIBA: return "Toshiba";
+ case PCI_VENDOR_ID_RICOH: return "Ricoh";
case PCI_VENDOR_ID_ZEITNET: return "ZeitNet";
case PCI_VENDOR_ID_OMEGA: return "Omega Micro";
+ case PCI_VENDOR_ID_GALILEO: return "Galileo Technology";
+ case PCI_VENDOR_ID_NP: return "Network Peripherals";
case PCI_VENDOR_ID_SPECIALIX: return "Specialix";
+ case PCI_VENDOR_ID_IKON: return "Ikon";
case PCI_VENDOR_ID_ZORAN: return "Zoran";
case PCI_VENDOR_ID_COMPEX: return "Compex";
case PCI_VENDOR_ID_RP: return "Comtrol";
@@ -618,6 +675,36 @@ const char *pci_strdev(unsigned int vendor, unsigned int device)
}
+const char *pcibios_strerror(int error)
+{
+ static char buf[32];
+
+ switch (error) {
+ case PCIBIOS_SUCCESSFUL:
+ case PCIBIOS_BAD_VENDOR_ID:
+ return "SUCCESSFUL";
+
+ case PCIBIOS_FUNC_NOT_SUPPORTED:
+ return "FUNC_NOT_SUPPORTED";
+
+ case PCIBIOS_DEVICE_NOT_FOUND:
+ return "DEVICE_NOT_FOUND";
+
+ case PCIBIOS_BAD_REGISTER_NUMBER:
+ return "BAD_REGISTER_NUMBER";
+
+ case PCIBIOS_SET_FAILED:
+ return "SET_FAILED";
+
+ case PCIBIOS_BUFFER_TOO_SMALL:
+ return "BUFFER_TOO_SMALL";
+
+ default:
+ sprintf (buf, "PCI ERROR 0x%x", error);
+ return buf;
+ }
+}
+
/*
* Turn on/off PCI bridge optimization. This should allow benchmarking.
@@ -743,7 +830,7 @@ static int sprint_dev_config(struct pci_dev *dev, char *buf, int size)
if (len + 40 > size) {
return -1;
}
- len += sprintf(buf + len, "IRQ %d. ", dev->irq);
+ len += sprintf(buf + len, "IRQ %x. ", dev->irq);
}
if (dev->master) {
@@ -761,20 +848,24 @@ static int sprint_dev_config(struct pci_dev *dev, char *buf, int size)
len += sprintf(buf + len, "Max Lat=%d.", max_lat);
}
- for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
+ for (reg = 0; reg < 6; reg++) {
if (len + 40 > size) {
return -1;
}
- pcibios_read_config_dword(bus, devfn, reg, &l);
- base = l;
- if (!base) {
+ pcibios_read_config_dword(bus, devfn,
+ PCI_BASE_ADDRESS_0 + (reg << 2), &l);
+ if (l == 0xffffffff)
+ base = 0;
+ else
+ base = l;
+ if (!base)
continue;
- }
if (base & PCI_BASE_ADDRESS_SPACE_IO) {
len += sprintf(buf + len,
- "\n I/O at 0x%lx.",
- base & PCI_BASE_ADDRESS_IO_MASK);
+ "\n I/O at 0x%lx [0x%lx].",
+ base & PCI_BASE_ADDRESS_IO_MASK,
+ dev->base_address[reg]);
} else {
const char *pref, *type = "unknown";
@@ -798,8 +889,9 @@ static int sprint_dev_config(struct pci_dev *dev, char *buf, int size)
}
len += sprintf(buf + len,
"\n %srefetchable %s memory at "
- "0x%lx.", pref, type,
- base & PCI_BASE_ADDRESS_MEM_MASK);
+ "0x%lx [0x%lx].", pref, type,
+ base & PCI_BASE_ADDRESS_MEM_MASK,
+ dev->base_address[reg]);
}
}
@@ -842,7 +934,7 @@ __initfunc(static void *pci_malloc(long size, unsigned long *mem_startp))
void *mem;
#ifdef DEBUG
- printk("...pci_malloc(size=%ld,mem=%p)", size, *mem_startp);
+ printk("...pci_malloc(size=%ld,mem=%p)", size, (void *)*mem_startp);
#endif
mem = (void*) *mem_startp;
*mem_startp += (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
@@ -851,16 +943,18 @@ __initfunc(static void *pci_malloc(long size, unsigned long *mem_startp))
}
-__initfunc(static unsigned int scan_bus(struct pci_bus *bus, unsigned long *mem_startp))
+unsigned int pci_scan_bus(struct pci_bus *bus, unsigned long *mem_startp)
{
unsigned int devfn, l, max;
- unsigned char cmd, tmp, hdr_type = 0;
+ unsigned char cmd, tmp, irq, hdr_type = 0;
struct pci_dev_info *info;
struct pci_dev *dev;
struct pci_bus *child;
+ int reg;
#ifdef DEBUG
- printk("...scan_bus(busno=%d,mem=%p)\n", bus->number, *mem_startp);
+ printk("...pci_scan_bus(busno=%d,mem=%p)\n", bus->number,
+ (void *)*mem_startp);
#endif
max = bus->secondary;
@@ -902,7 +996,7 @@ __initfunc(static unsigned int scan_bus(struct pci_bus *bus, unsigned long *mem_
*/
info = pci_lookup_dev(dev->vendor, dev->device);
if (!info) {
- printk("Warning : Unknown PCI device (%x:%x). Please read include/linux/pci.h \n",
+ printk("PCI: Warning: Unknown PCI device (%x:%x). Please read include/linux/pci.h\n",
dev->vendor, dev->device);
} else {
/* Some BIOS' are lazy. Let's do their job: */
@@ -925,7 +1019,20 @@ __initfunc(static unsigned int scan_bus(struct pci_bus *bus, unsigned long *mem_
/* read irq level (may be changed during pcibios_fixup()): */
pcibios_read_config_byte(bus->number, devfn,
- PCI_INTERRUPT_LINE, &dev->irq);
+ PCI_INTERRUPT_LINE, &irq);
+ dev->irq = irq;
+
+ /* read base address registers, again pcibios_fixup() can
+ * tweak these
+ */
+ for (reg = 0; reg < 6; reg++) {
+ pcibios_read_config_dword(bus->number, devfn,
+ PCI_BASE_ADDRESS_0 + (reg << 2), &l);
+ if (l == 0xffffffff)
+ dev->base_address[reg] = 0;
+ else
+ dev->base_address[reg] = l;
+ }
/* check to see if this device is a PCI-PCI bridge: */
pcibios_read_config_dword(bus->number, devfn,
@@ -983,7 +1090,7 @@ __initfunc(static unsigned int scan_bus(struct pci_bus *bus, unsigned long *mem_
child->secondary = (buses >> 8) & 0xFF;
child->subordinate = (buses >> 16) & 0xFF;
child->number = child->secondary;
- max = scan_bus(child, mem_startp);
+ max = pci_scan_bus(child, mem_startp);
}
else
{
@@ -1000,7 +1107,7 @@ __initfunc(static unsigned int scan_bus(struct pci_bus *bus, unsigned long *mem_
/*
* Now we can scan all subordinate buses:
*/
- max = scan_bus(child, mem_startp);
+ max = pci_scan_bus(child, mem_startp);
/*
* Set the subordinate bus number to its real
* value:
@@ -1031,14 +1138,14 @@ __initfunc(unsigned long pci_init (unsigned long mem_start, unsigned long mem_en
mem_start = pcibios_init(mem_start, mem_end);
if (!pcibios_present()) {
- printk("pci_init: no PCI BIOS detected\n");
+ printk("PCI: No PCI bus detected\n");
return mem_start;
}
printk("Probing PCI hardware.\n");
memset(&pci_root, 0, sizeof(pci_root));
- pci_root.subordinate = scan_bus(&pci_root, &mem_start);
+ pci_root.subordinate = pci_scan_bus(&pci_root, &mem_start);
/* give BIOS a chance to apply platform specific fixes: */
mem_start = pcibios_fixup(mem_start, mem_end);
diff --git a/drivers/pnp/parport_probe.c b/drivers/pnp/parport_probe.c
index 7ffde6f53..85f89e4c7 100644
--- a/drivers/pnp/parport_probe.c
+++ b/drivers/pnp/parport_probe.c
@@ -1,4 +1,4 @@
-/* $Id: parport_probe.c,v 1.2 1997/07/29 03:59:29 ralf Exp $
+/* $Id: parport_probe.c,v 1.3 1997/08/06 19:15:53 miguel Exp $
* Parallel port device probing code
*
* Authors: Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
@@ -52,7 +52,7 @@ static long read_polled(struct parport *port, char *buf,
{
int i;
char *temp=buf;
- int count = 0;
+ unsigned int count = 0;
unsigned char z=0;
unsigned char Byte=0;
diff --git a/drivers/sbus/char/Config.in b/drivers/sbus/char/Config.in
index 3f25aa828..90ecfc49a 100644
--- a/drivers/sbus/char/Config.in
+++ b/drivers/sbus/char/Config.in
@@ -41,6 +41,7 @@ fi
comment 'Misc Linux/SPARC drivers'
tristate '/dev/openprom device support' CONFIG_SUN_OPENPROMIO
tristate 'Mostek real time clock support' CONFIG_SUN_MOSTEK_RTC
+tristate 'Siemens SAB82532 serial support' CONFIG_SAB82532
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'Bidirectional parallel port support (EXPERIMENTAL)' CONFIG_SUN_BPP
diff --git a/drivers/sbus/char/Makefile b/drivers/sbus/char/Makefile
index a5d2b3016..dfcc7afe3 100644
--- a/drivers/sbus/char/Makefile
+++ b/drivers/sbus/char/Makefile
@@ -50,10 +50,14 @@ endif
#endif
O_TARGET := sunchar.o
-O_OBJ := ${FB_OBJS} suncons.o sunfb.o
-O_OBJS := ${O_OBJ} sunkbd.o sunkeymap.o sunmouse.o sunserial.o
+O_OBJ := ${FB_OBJS} suncons.o sbuscons.o pcicons.o sunfb.o
+O_OBJS := ${O_OBJ} sunkbd.o sunkeymap.o sunmouse.o sunserial.o zs.o
M_OBJS :=
+ifeq ($(ARCH),sparc64)
+O_OBJS += su.o pcikbd.o
+endif
+
ifeq ($(CONFIG_SUN_OPENPROMIO),y)
O_OBJS += openprom.o
else
@@ -86,6 +90,20 @@ else
endif
endif
+ifeq ($(CONFIG_SAB82532),y)
+O_OBJS += sab82532.o
+else
+ ifeq ($(CONFIG_SAB82532),m)
+ M_OBJS += sab82532.o
+ endif
+endif
+
+# Add PCI console/fb drivers here.
+#
+ifeq ($(CONFIG_PCI),y)
+O_OBJS += mach64.o
+endif
+
include $(TOPDIR)/Rules.make
vfc.o: vfc_dev.o vfc_i2c.o
diff --git a/drivers/sbus/char/cgfourteen.c b/drivers/sbus/char/cgfourteen.c
index 40fb9fd70..dbe071b98 100644
--- a/drivers/sbus/char/cgfourteen.c
+++ b/drivers/sbus/char/cgfourteen.c
@@ -1,4 +1,4 @@
-/* $Id: cgfourteen.c,v 1.24 1997/07/17 02:21:44 davem Exp $
+/* $Id: cgfourteen.c,v 1.25 1997/08/20 07:38:36 davem Exp $
* cgfourteen.c: Sun SparcStation console support.
*
* Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -456,7 +456,7 @@ __initfunc(void cg14_setup (fbinfo_t *fb, int slot, int con_node, u32 cg14, int
fb->setcurshape = cg14_setcurshape;
fb->ioctl = cg14_ioctl;
fb->switch_from_graph = cg14_switch_from_graph;
- fb->postsetup = sun_cg_postsetup;
+ fb->postsetup = cg_postsetup;
fb->reset = cg14_reset;
fb->blank = 0;
fb->unblank = 0;
diff --git a/drivers/sbus/char/cgsix.c b/drivers/sbus/char/cgsix.c
index 825d49b4a..4c24a8c0b 100644
--- a/drivers/sbus/char/cgsix.c
+++ b/drivers/sbus/char/cgsix.c
@@ -1,4 +1,4 @@
-/* $Id: cgsix.c,v 1.35 1997/07/17 02:21:45 davem Exp $
+/* $Id: cgsix.c,v 1.37 1997/08/22 15:55:20 jj Exp $
* cgsix.c: cgsix frame buffer driver
*
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -409,8 +409,8 @@ cg6_reset (fbinfo_t *fb)
struct cg6_info *cg6 = &(fb->info.cg6);
unsigned int rev, conf;
- if (fb->setcursor)
- sun_hw_hide_cursor ();
+ if (fb == &fbinfo[0])
+ sbus_hw_hide_cursor ();
/* Turn off stuff in the Transform Engine. */
cg6->tec->tec_matrix = 0;
cg6->tec->tec_clip = 0;
@@ -466,7 +466,7 @@ __initfunc(void cg6_setup (fbinfo_t *fb, int slot, u32 cg6, int cg6_io))
fb->setcursor = cg6_setcursor;
fb->setcursormap = cg6_setcursormap;
fb->setcurshape = cg6_setcurshape;
- fb->postsetup = sun_cg_postsetup;
+ fb->postsetup = cg_postsetup;
fb->blitc = cg6_blitc;
fb->setw = cg6_setw;
fb->cpyw = cg6_cpyw;
diff --git a/drivers/sbus/char/cgthree.c b/drivers/sbus/char/cgthree.c
index aea1bff32..95d8a07f4 100644
--- a/drivers/sbus/char/cgthree.c
+++ b/drivers/sbus/char/cgthree.c
@@ -1,4 +1,4 @@
-/* $Id: cgthree.c,v 1.23 1997/07/17 02:21:46 davem Exp $
+/* $Id: cgthree.c,v 1.24 1997/08/20 07:38:37 davem Exp $
* cgtree.c: cg3 frame buffer driver
*
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -213,7 +213,7 @@ __initfunc(void cg3_setup (fbinfo_t *fb, int slot, u32 cg3, int cg3_io,
fb->type.fb_cmsize = 256;
fb->mmap = cg3_mmap;
fb->loadcmap = cg3_loadcmap;
- fb->postsetup = sun_cg_postsetup;
+ fb->postsetup = cg_postsetup;
fb->ioctl = 0; /* no special ioctls */
fb->reset = 0;
fb->blank = cg3_blank;
diff --git a/drivers/sbus/char/creator.c b/drivers/sbus/char/creator.c
index 3ce90092a..815e397e8 100644
--- a/drivers/sbus/char/creator.c
+++ b/drivers/sbus/char/creator.c
@@ -1,4 +1,4 @@
-/* $Id: creator.c,v 1.8 1997/07/22 06:14:12 davem Exp $
+/* $Id: creator.c,v 1.12 1997/08/25 07:50:27 jj Exp $
* creator.c: Creator/Creator3D frame buffer driver
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -101,14 +101,24 @@
#define FFB_UCSR_RP_BUSY 0x02000000
struct ffb_fbc {
- u8 xxx1[0x200];
+ u8 xxx1[0x60];
+ volatile u32 by;
+ volatile u32 bx;
+ u32 xxx2;
+ u32 xxx3;
+ volatile u32 bh;
+ volatile u32 bw;
+ u8 xxx4[0x188];
volatile u32 ppc;
- u8 xxx2[0x50];
+ u32 xxx5;
+ volatile u32 fg;
+ volatile u32 bg;
+ u8 xxx6[0x44];
volatile u32 fbc;
volatile u32 rop;
- u8 xxx3[0x34];
+ u8 xxx7[0x34];
volatile u32 pmask;
- u8 xxx4[12];
+ u8 xxx8[12];
volatile u32 clip0min;
volatile u32 clip0max;
volatile u32 clip1min;
@@ -117,11 +127,17 @@ struct ffb_fbc {
volatile u32 clip2max;
volatile u32 clip3min;
volatile u32 clip3max;
- u8 xxx5[0x3c];
+ u8 xxx9[0x3c];
volatile u32 unk1;
- u8 xxx6[0x500];
volatile u32 unk2;
- u8 xxx7[0xfc];
+ u8 xxx10[0x10];
+ volatile u32 fontxy;
+ volatile u32 fontw;
+ volatile u32 fontinc;
+ volatile u32 font;
+ u8 xxx11[0x4dc];
+ volatile u32 unk3;
+ u8 xxx12[0xfc];
volatile u32 ucsr;
};
@@ -141,6 +157,7 @@ static void ffb_blitc(unsigned short, int, int);
static void ffb_setw(int, int, unsigned short, int);
static void ffb_cpyw(int, int, unsigned short *, int);
static void ffb_fill(int, int, int *);
+static void ffb_penguin(int,int,int);
static struct {
unsigned long voff;
@@ -176,6 +193,8 @@ ffb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
uint size, page, r, map_size;
unsigned long map_offset = 0;
int i;
+ int alignment;
+ struct vm_area_struct *vmm;
size = vma->vm_end - vma->vm_start;
if (vma->vm_offset & ~PAGE_MASK)
@@ -184,15 +203,18 @@ ffb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
/* Try to align RAM */
#define ALIGNMENT 0x400000
map_offset = vma->vm_offset + size;
+ alignment = 0;
if (vma->vm_offset < FFB_FBC_REGS_VOFF) {
- struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start);
- int alignment = ALIGNMENT - ((vma->vm_start - vma->vm_offset) & (ALIGNMENT - 1));
-
- if (alignment == ALIGNMENT) alignment = 0;
- if (alignment && (!vmm || vmm->vm_start >= vma->vm_end + alignment)) {
- vma->vm_start += alignment;
- vma->vm_end += alignment;
- }
+ vmm = find_vma(current->mm, vma->vm_start);
+ alignment = ALIGNMENT - ((vma->vm_start - vma->vm_offset) & (ALIGNMENT - 1));
+ } else if (vma->vm_offset >= FFB_DFB8R_VOFF && (vma->vm_offset & (ALIGNMENT - 1)) == 0x4000) {
+ vmm = find_vma(current->mm, vma->vm_start);
+ alignment = ALIGNMENT - (vma->vm_start & (ALIGNMENT - 1));
+ }
+ if (alignment == ALIGNMENT) alignment = 0;
+ if (alignment && (!vmm || vmm->vm_start >= vma->vm_end + alignment)) {
+ vma->vm_start += alignment;
+ vma->vm_end += alignment;
}
#undef ALIGNMENT
@@ -205,7 +227,7 @@ ffb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
for (i = 0; i < sizeof (ffbmmap) / sizeof (ffbmmap[0]); i++)
if (ffbmmap[i].voff == vma->vm_offset+page) {
map_size = ffbmmap[i].size;
- map_offset = fb->info.ffb.physbase + ffbmmap[i].poff;
+ map_offset = (fb->info.ffb.physbase + ffbmmap[i].poff) & _PAGE_PADDR;
}
if (!map_size){
@@ -380,8 +402,8 @@ ffb_setcursor (fbinfo_t *fb)
dac->type2 = 0x104;
/* Should this be just 0x7ff?? Should I do some margin handling and setcurshape
in that case? */
- dac->value2 = (((c->cpos.fbx - c->chot.fbx) & 0xffff) << 16)
- |((c->cpos.fby - c->chot.fby) & 0xffff);
+ dac->value2 = (((c->cpos.fby - c->chot.fby) & 0xffff) << 16)
+ |((c->cpos.fbx - c->chot.fbx) & 0xffff);
ffb_curs_enable (fb, fb->cursor.enable);
}
@@ -480,32 +502,14 @@ ffb_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long a
void
ffb_reset (fbinfo_t *fb)
{
- struct ffb_info *ffb = &(fb->info.ffb);
- int fifo;
-
- if (fb->setcursor)
- sun_hw_hide_cursor ();
-
- while ((fifo = (ffb->fbc->ucsr & FFB_UCSR_FIFO_MASK)) < 8);
- ffb->fbc->ppc = (FFB_PPC_ACE_DISABLE << FFB_PPC_ACE_SHIFT) |
- (FFB_PPC_DCE_DISABLE << FFB_PPC_DCE_SHIFT) |
- (FFB_PPC_ABE_DISABLE << FFB_PPC_ABE_SHIFT) |
- (FFB_PPC_VCE_DISABLE << FFB_PPC_VCE_SHIFT) |
- (FFB_PPC_APE_DISABLE << FFB_PPC_APE_SHIFT) |
- (FFB_PPC_CS_VARIABLE << FFB_PPC_CS_SHIFT);
- ffb->fbc->fbc = (FFB_FBC_WB_A << FFB_FBC_WB_SHIFT) |
- (FFB_FBC_PGE_MASK << FFB_FBC_BE_SHIFT) |
- (FFB_FBC_PGE_MASK << FFB_FBC_GE_SHIFT) |
- (FFB_FBC_PGE_MASK << FFB_FBC_RE_SHIFT);
- ffb->fbc->rop = (FFB_ROP_NEW << FFB_ROP_RGB_SHIFT);
- ffb->fbc->pmask = 0x00ffffff;
- while (ffb->fbc->ucsr & FFB_UCSR_RP_BUSY);
+ if (fb == &fbinfo[0])
+ sbus_hw_hide_cursor ();
}
__initfunc(static unsigned long ffb_postsetup (fbinfo_t *fb, unsigned long memory_start))
{
fb->info.ffb.clut = (u32 *)(memory_start);
- fb->color_map = (u8 *)(memory_start+256*4+256);
+ fb->color_map = (u8 *)(memory_start+256*4);
return memory_start + 256*4 + 256*3;
}
@@ -513,10 +517,11 @@ __initfunc(void creator_setup (fbinfo_t *fb, int slot, int ffb_node, unsigned lo
{
struct ffb_info *ffbinfo;
struct linux_prom64_registers regs[2*PROMREG_MAX];
-
+ int type;
+
if (prom_getproperty(ffb_node, "reg", (void *) regs, sizeof(regs)) <= 0)
return;
- ffb = regs[0].phys_addr;
+ ffb = (long)__va(regs[0].phys_addr);
printk ("creator%d at 0x%016lx ", slot, ffb);
fb->base = ffb; /* ??? */
@@ -536,9 +541,11 @@ __initfunc(void creator_setup (fbinfo_t *fb, int slot, int ffb_node, unsigned lo
fb->setw = ffb_setw;
fb->cpyw = ffb_cpyw;
fb->fill = ffb_fill;
+ fb->draw_penguin = ffb_penguin;
fb->ioctl = ffb_ioctl;
fb->cursor.hwsize.fbx = 64;
fb->cursor.hwsize.fby = 64;
+ fb->type.fb_depth = 24;
ffbinfo = (struct ffb_info *) &fb->info.ffb;
@@ -546,16 +553,18 @@ __initfunc(void creator_setup (fbinfo_t *fb, int slot, int ffb_node, unsigned lo
ffbinfo->fbc = (struct ffb_fbc *)(ffb + FFB_FBC_REGS_POFF);
ffbinfo->dac = (struct ffb_dac *)(ffb + FFB_DAC_POFF);
-
+
ffbinfo->dac->type = 0x8000;
ffbinfo->dac_rev = (ffbinfo->dac->value >> 0x1c);
if (slot == sun_prom_console_id)
fb_restore_palette = ffb_restore_palette;
+
+ type = prom_getintdefault (ffb_node, "board_type", 8);
/* Initialize Brooktree DAC */
- printk("DAC %d\n", ffbinfo->dac_rev);
+ printk("TYPE %d DAC %d\n", type, ffbinfo->dac_rev);
if (slot && sun_prom_console_id == slot)
return;
@@ -573,18 +582,152 @@ __initfunc(void creator_setup (fbinfo_t *fb, int slot, int ffb_node, unsigned lo
extern unsigned char vga_font[];
+#define FFB_BLITC_START(attr) \
+ { \
+ register struct ffb_fbc *ffb = fbinfo[0].info.ffb.fbc; \
+ register u32 *clut = fbinfo[0].info.ffb.clut; \
+ int i; \
+ ffb->ppc = 0x203; \
+ ffb->fg = clut[attr & 0xf]; \
+ ffb->fbc = 0x2000707f; \
+ ffb->rop = 0x83; \
+ ffb->pmask = 0xffffffff; \
+ ffb->bg = clut[attr>>4];
+#define FFB_BLITC_BODY4(count,x,y,start,action) \
+ while (count >= 4) { \
+ count -= 4; \
+ ffb->fontw = 32; \
+ ffb->fontinc = 0x10000; \
+ ffb->fontxy = (y << 16) + x; \
+ x += 32; \
+ start; \
+ for (i = 0; i < CHAR_HEIGHT; i++) { \
+ action; \
+ } \
+ }
+#define FFB_BLITC_BODY1(x,y,action) \
+ ffb->fontw = 8; \
+ ffb->fontinc = 0x10000; \
+ ffb->fontxy = (y << 16) + x; \
+ x += 8; \
+ for (i = 0; i < CHAR_HEIGHT; i++) { \
+ action; \
+ }
+#define FFB_BLITC_END \
+ }
+
static void ffb_blitc(unsigned short charattr, int xoff, int yoff)
{
+ unsigned char attrib = CHARATTR_TO_SUNCOLOR(charattr);
+ unsigned char *p = &vga_font[((unsigned char)charattr) << 4];
+ FFB_BLITC_START(attrib)
+ FFB_BLITC_BODY1(xoff, yoff, ffb->font=((*p++) << 24))
+ FFB_BLITC_END
}
static void ffb_setw(int xoff, int yoff, unsigned short c, int count)
{
+ unsigned char attrib = CHARATTR_TO_SUNCOLOR(c);
+ unsigned char *p = &vga_font[((unsigned char)c) << 4];
+ register unsigned char *q;
+ register uint l;
+ FFB_BLITC_START(attrib)
+ if (count >= 4) {
+ FFB_BLITC_BODY4(count, xoff, yoff, q = p,
+ l = *q++;
+ l |= l << 8;
+ l |= l << 16;
+ ffb->font=l)
+ }
+ while (count) {
+ count--;
+ q = p;
+ FFB_BLITC_BODY1(xoff, yoff, ffb->font=((*q++) << 24));
+ }
+ FFB_BLITC_END
}
static void ffb_cpyw(int xoff, int yoff, unsigned short *p, int count)
{
+ unsigned char attrib = CHARATTR_TO_SUNCOLOR(*p);
+ unsigned char *p1, *p2, *p3, *p4;
+ FFB_BLITC_START(attrib)
+ if (count >= 4) {
+ FFB_BLITC_BODY4(count, xoff, yoff,
+ p1 = &vga_font[((unsigned char)*p++) << 4];
+ p2 = &vga_font[((unsigned char)*p++) << 4];
+ p3 = &vga_font[((unsigned char)*p++) << 4];
+ p4 = &vga_font[((unsigned char)*p++) << 4],
+ ffb->font=((uint)*p4++) | ((((uint)*p3++) | ((((uint)*p2++) | (((uint)*p1++) << 8)) << 8)) << 8))
+ }
+ while (count) {
+ count--;
+ p1 = &vga_font[((unsigned char)*p++) << 4];
+ FFB_BLITC_BODY1(xoff, yoff, ffb->font=((*p1++) << 24));
+ }
+ FFB_BLITC_END
}
+#if 0
+#define FFB_FILL_START(attr) \
+ { \
+ register struct ffb_fbc *ffb = fbinfo[0].info.ffb.fbc; \
+ register u32 *clut = fbinfo[0].info.ffb.clut; \
+ ffb->ppc =0x1803; \
+ ffb->fg = clut[attr & 0xf]; \
+ ffb->fbc = 0x2000707f; \
+ ffb->rop = 0x83; \
+ ffb->pmask = 0xffffffff; \
+ ffb->unk2 = 8;
+#define FFB_FILL_END \
+ }
+#else
+#define FFB_FILL_START(attr) \
+ { \
+ register struct ffb_fbc *ffb = fbinfo[0].info.ffb.fbc; \
+ ffb->ppc = 0x1803; \
+ ffb->fg = 0; \
+ ffb->fbc = 0x2000707f; \
+ ffb->rop = 0x83; \
+ ffb->pmask = 0xffffffff; \
+ ffb->unk2 = 8;
+#define FFB_FILL_END \
+ }
+#endif
+
static void ffb_fill(int attrib, int count, int *boxes)
{
+ attrib = 5;
+ FFB_FILL_START(attrib)
+ while (count-- > 0) {
+ ffb->by = boxes[1];
+ ffb->bx = boxes[0];
+ ffb->bw = boxes[2];
+ ffb->bh = boxes[3];
+ boxes += 4;
+ }
+ FFB_FILL_END
+}
+
+__initfunc(void ffb_penguin(int x_margin, int y_margin, int ncpus))
+{
+ int i, j, k;
+ u32 *p, *q;
+ unsigned char *r;
+ unsigned char c;
+
+ p = (u32 *)(fbinfo[0].info.ffb.physbase + FFB_DFB24_POFF + y_margin*8192 + x_margin*4);
+ for (i = 0; i < 80; i++, p += 2048) {
+ q = p;
+ for (j = 0; j < ncpus; j++) {
+ r = linux_logo + 80 * i;
+ for (k = 0; k < 80; k++, r++) {
+ c = *r - 32;
+ *q++ = (linux_logo_red[c]) |
+ (linux_logo_green[c]<<8) |
+ (linux_logo_blue[c]<<16);
+ }
+ q += 8;
+ }
+ }
}
diff --git a/drivers/sbus/char/fb.h b/drivers/sbus/char/fb.h
index 0aa9f2b48..ea58a6346 100644
--- a/drivers/sbus/char/fb.h
+++ b/drivers/sbus/char/fb.h
@@ -1,4 +1,4 @@
-/* $Id: fb.h,v 1.29 1997/07/15 09:48:48 jj Exp $
+/* $Id: fb.h,v 1.33 1997/08/25 07:50:29 jj Exp $
* fb.h: contains the definitions of the structures that various sun
* frame buffer can use to do console driver stuff.
*
@@ -122,6 +122,7 @@ typedef struct fbinfo {
struct tcx_info tcx;
struct leo_info leo;
struct ffb_info ffb;
+ void *private;
} info; /* per frame information */
int space; /* I/O space this card resides in */
int blanked; /* true if video blanked */
@@ -134,6 +135,7 @@ typedef struct fbinfo {
int emulations[4]; /* possible emulations (-1 N/A) */
int prom_node; /* node of the device in prom tree */
int base_depth; /* depth of fb->base piece */
+ int linebytes; /* number of bytes in a row */
struct cg_cursor cursor; /* kernel state of hw cursor */
int (*mmap)(struct inode *, struct file *, struct vm_area_struct *,
long fb_base, struct fbinfo *);
@@ -149,16 +151,20 @@ typedef struct fbinfo {
void (*setcursormap)(struct fbinfo *, unsigned char *,
unsigned char *, unsigned char *);
unsigned long (*postsetup)(struct fbinfo *, unsigned long);
+ void (*clear_fb)(int);
+ void (*set_other_palette)(int);
void (*blitc)(unsigned short, int, int);
void (*setw)(int, int, unsigned short, int);
void (*cpyw)(int, int, unsigned short *, int);
void (*fill)(int, int, int *);
+ void (*draw_penguin)(int,int,int);
unsigned char *color_map;
struct openpromfs_dev proc_entry;
} fbinfo_t;
#define CM(i, j) [3*(i)+(j)]
+extern unsigned char sparc_color_table[];
extern unsigned char reverse_color_table[];
#define CHARATTR_TO_SUNCOLOR(attr) \
@@ -196,13 +202,13 @@ extern unsigned long get_phys (unsigned long addr);
extern int get_iospace (unsigned long addr);
extern void render_screen(void);
-extern void sun_hw_hide_cursor(void);
-extern void sun_hw_set_cursor(int, int);
-extern int sun_hw_scursor(struct fbcursor *,fbinfo_t *);
-extern int sun_hw_cursor_shown;
+extern void sbus_hw_hide_cursor(void);
+extern void sbus_hw_set_cursor(int, int);
+extern int sbus_hw_scursor(struct fbcursor *,fbinfo_t *);
+extern int sbus_hw_cursor_shown;
extern int sun_prom_console_id;
-extern unsigned long sun_cg_postsetup(fbinfo_t *, unsigned long);
+extern unsigned long cg_postsetup(fbinfo_t *, unsigned long);
#define FB_DEV(x) (MINOR(x) / 32)
@@ -216,4 +222,12 @@ extern void tcx_setup (fbinfo_t *, int, int, u32, struct linux_sbus_device *);
extern void creator_setup (fbinfo_t *, int, int, unsigned long, int);
extern int io_remap_page_range(unsigned long from, unsigned long offset, unsigned long size, pgprot_t prot, int space);
+extern unsigned char linux_logo_red[];
+extern unsigned char linux_logo_green[];
+extern unsigned char linux_logo_blue[];
+extern unsigned char linux_logo[];
+extern unsigned char linux_logo_bw[];
+extern unsigned int linux_logo_colors;
+extern char logo_banner[];
+
#endif __SPARC_FB_H_
diff --git a/drivers/sbus/char/leo.c b/drivers/sbus/char/leo.c
index 1b5d06e4b..e08cf1822 100644
--- a/drivers/sbus/char/leo.c
+++ b/drivers/sbus/char/leo.c
@@ -1,4 +1,4 @@
-/* $Id: leo.c,v 1.21 1997/07/17 02:21:48 davem Exp $
+/* $Id: leo.c,v 1.25 1997/08/22 17:33:58 jj Exp $
* leo.c: SUNW,leo 24/8bit frame buffer driver
*
* Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -9,12 +9,12 @@
#include <linux/tty.h>
#include <linux/malloc.h>
#include <linux/proc_fs.h>
+#include <linux/delay.h>
#include <asm/sbus.h>
#include <asm/io.h>
#include <asm/fbio.h>
#include <asm/pgtable.h>
-#include <asm/delay.h>
#include <asm/uaccess.h>
/* These must be included after asm/fbio.h */
@@ -129,6 +129,7 @@ static void leo_blitc(unsigned short, int, int);
static void leo_setw(int, int, unsigned short, int);
static void leo_cpyw(int, int, unsigned short *, int);
static void leo_fill(int, int, int *);
+static void leo_penguin(int,int,int);
static void
leo_restore_palette (fbinfo_t *fb)
@@ -496,8 +497,8 @@ leo_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long a
static void
leo_reset (fbinfo_t *fb)
{
- if (fb->setcursor)
- sun_hw_hide_cursor ();
+ if (fb == &fbinfo[0])
+ sbus_hw_hide_cursor ();
}
@@ -537,6 +538,7 @@ __initfunc(void leo_setup (fbinfo_t *fb, int slot, u32 leo, int leo_io))
fb->setw = leo_setw;
fb->cpyw = leo_cpyw;
fb->fill = leo_fill;
+ fb->draw_penguin = leo_penguin;
fb->base_depth = 0;
leoinfo = (struct leo_info *) &fb->info.leo;
@@ -692,3 +694,9 @@ static void leo_fill(int attrib, int count, int *boxes)
us->fill = (boxes[0] & 0x7ff) | ((boxes[1] & 0x7ff) << 11) | ((i & 3) << 29) | ((i & 8) ? 0x80000000 : 0);
}
}
+
+__initfunc(void leo_penguin(int x_margin, int y_margin, int ncpus))
+{
+ suncons_ops.clear_screen();
+ /* FIXME: Write this */
+}
diff --git a/drivers/sbus/char/mach64.c b/drivers/sbus/char/mach64.c
new file mode 100644
index 000000000..c3c562306
--- /dev/null
+++ b/drivers/sbus/char/mach64.c
@@ -0,0 +1,203 @@
+/* $Id: mach64.c,v 1.8 1997/08/25 07:50:34 jj Exp $
+ * mach64.c: Ultra/PCI Mach64 console driver.
+ *
+ * Just about all of this is from the PPC/mac driver, see that for
+ * author info. I'm only responsible for grafting it into working
+ * on PCI Ultra's. The two drivers should be merged.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/selection.h>
+#include <linux/proc_fs.h>
+
+#include <asm/oplib.h>
+#include <asm/pbm.h>
+#include <asm/fbio.h>
+#include <asm/sbus.h>
+
+#include "pcicons.h"
+#include "mach64.h"
+#include "fb.h"
+
+#define MACH64_REGOFF 0x7ffc00
+#define MACH64_FBOFF 0x800000
+
+static inline void mach64_waitq(int entries)
+{
+ unsigned short base = (0x8000 >> entries);
+
+ while((pcivga_readl(MACH64_REGOFF + FIFO_STAT) & 0xffff) > base)
+ barrier();
+}
+
+static inline void mach64_idle(void)
+{
+ mach64_waitq(16);
+ while(pcivga_readl(MACH64_REGOFF + GUI_STAT) & 1)
+ barrier();
+}
+
+#if 0 /* not used yet */
+static void mach64_st_514(int offset, char val)
+{
+ mach64_waitq(5);
+ pcivga_writeb(1, MACH64_REGOFF + DAC_CNTL);
+ pcivga_writeb((offset & 0xff), MACH64_REGOFF + DAC_W_INDEX);
+ pcivga_writeb(((offset>>8)&0xff), MACH64_REGOFF + DAC_DATA);
+ pcivga_writeb(val, MACH64_REGOFF + DAC_MASK);
+ pcivga_writeb(0, MACH64_REGOFF + DAC_CNTL);
+}
+
+static void mach64_st_pll(int offset, char val)
+{
+ mach64_waitq(3);
+ pcivga_writeb(((offset<<2)|PLL_WR_EN), MACH64_REGOFF + CLOCK_CNTL + 1);
+ pcivga_writeb(val, MACH64_REGOFF + CLOCK_CNTL + 2);
+ pcivga_writeb(((offset<<2)&~PLL_WR_EN), MACH64_REGOFF + CLOCK_CNTL + 1);
+}
+#endif
+
+static int
+mach64_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma,
+ long base, fbinfo_t *fb)
+{
+ return -ENOSYS;
+}
+
+static void
+mach64_loadcmap(fbinfo_t *fb, int index, int count)
+{
+ unsigned char tmp;
+ int i;
+
+ mach64_waitq(2);
+ tmp = pcivga_readb(MACH64_REGOFF + DAC_CNTL);
+ pcivga_writeb(tmp & 0xfc, MACH64_REGOFF + DAC_CNTL);
+ pcivga_writeb(0xff, MACH64_REGOFF + DAC_MASK);
+ for(i = index; count--; i++) {
+ mach64_waitq(4);
+ pcivga_writeb(i, MACH64_REGOFF + DAC_W_INDEX);
+ pcivga_writeb(fb->color_map CM(i, 0), MACH64_REGOFF + DAC_DATA);
+ pcivga_writeb(fb->color_map CM(i, 1), MACH64_REGOFF + DAC_DATA);
+ pcivga_writeb(fb->color_map CM(i, 2), MACH64_REGOFF + DAC_DATA);
+ }
+}
+
+static void
+mach64_blank(fbinfo_t *fb)
+{
+ unsigned char gen_cntl;
+
+ gen_cntl = pcivga_readb(MACH64_REGOFF + CRTC_GEN_CNTL);
+ gen_cntl |= 0x40;
+ pcivga_writeb(gen_cntl, MACH64_REGOFF + CRTC_GEN_CNTL);
+}
+
+static void
+mach64_unblank(fbinfo_t *fb)
+{
+ unsigned char gen_cntl;
+
+ gen_cntl = pcivga_readb(MACH64_REGOFF + CRTC_GEN_CNTL);
+ gen_cntl &= ~(0x4c);
+ pcivga_writeb(gen_cntl, MACH64_REGOFF + CRTC_GEN_CNTL);
+}
+
+static struct mach64_info mach64;
+
+int mach64_init(fbinfo_t *fb)
+{
+ struct pci_dev *pdev;
+ struct pcidev_cookie *cookie;
+ unsigned long addr;
+
+ memset(&mach64, 0, sizeof(mach64));
+ for(pdev = pci_devices; pdev; pdev = pdev->next) {
+ if((pdev->vendor == PCI_VENDOR_ID_ATI) &&
+ (pdev->device == PCI_DEVICE_ID_ATI_264VT))
+ break;
+ }
+ if(!pdev)
+ return -1;
+
+ addr = pdev->base_address[0];
+ pcivga_iobase = pcivga_membase = 0;
+ if((addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
+ pcivga_iobase = addr & PCI_BASE_ADDRESS_IO_MASK;
+ else
+ pcivga_membase = addr & PCI_BASE_ADDRESS_MEM_MASK;
+
+ addr = pdev->base_address[1];
+ if((addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
+ pcivga_iobase = addr & PCI_BASE_ADDRESS_IO_MASK;
+ else
+ pcivga_membase = addr & PCI_BASE_ADDRESS_MEM_MASK;
+
+ if(!pcivga_iobase || !pcivga_membase) {
+ prom_printf("mach64_init: I/O or MEM baseaddr is missing\n");
+ prom_printf("mach64_init: ba[0]=%016lx ba[1]=%016lx\n",
+ pdev->base_address[0], pdev->base_address[1]);
+ prom_halt();
+ }
+
+ printk("mach64_init: IOBASE[%016lx] MEMBASE[%016lx]\n",
+ pcivga_iobase, pcivga_membase);
+
+ cookie = (struct pcidev_cookie *)pdev->sysdata;
+ fb->prom_node = cookie->prom_node;
+ fb->proc_entry.node = cookie->pbm->prom_node;
+
+ fb->type.fb_type = FBTYPE_PCI_MACH64;
+ fb->type.fb_cmsize = 256;
+ fb->info.private = (void *)&mach64;
+ fb->base = pcivga_membase + MACH64_FBOFF;
+
+ switch(pcivga_readl(MACH64_REGOFF + MEM_CNTL) & MEM_SIZE_ALIAS) {
+ case MEM_SIZE_512K:
+ mach64.total_vram = 0x80000;
+ break;
+ case MEM_SIZE_1M:
+ mach64.total_vram = 0x100000;
+ break;
+ case MEM_SIZE_2M:
+ mach64.total_vram = 0x200000;
+ break;
+ case MEM_SIZE_4M:
+ mach64.total_vram = 0x400000;
+ break;
+ case MEM_SIZE_6M:
+ mach64.total_vram = 0x600000;
+ break;
+ case MEM_SIZE_8M:
+ mach64.total_vram = 0x800000;
+ break;
+ default:
+ mach64.total_vram = 0x80000;
+ break;
+ }
+
+ if ((pcivga_readl(MACH64_REGOFF + CONFIG_CHIP_ID)
+ & CFG_CHIP_TYPE) == MACH64_VT_ID)
+ mach64.flags |= MACH64_MASK_VT;
+
+ printk("mach64_init: total_vram[%08x] is_vt_chip[%d]\n",
+ mach64.total_vram, mach64.flags & MACH64_MASK_VT ? 1 : 0);
+
+ fb->mmap = mach64_mmap;
+ fb->loadcmap = mach64_loadcmap;
+ fb->ioctl = 0;
+ fb->reset = 0;
+ fb->blank = mach64_blank;
+ fb->unblank = mach64_unblank;
+ fb->setcursor = 0;
+
+ return 0;
+}
diff --git a/drivers/sbus/char/mach64.h b/drivers/sbus/char/mach64.h
new file mode 100644
index 000000000..8bf6be478
--- /dev/null
+++ b/drivers/sbus/char/mach64.h
@@ -0,0 +1,587 @@
+/* $Id: mach64.h,v 1.3 1997/08/24 12:13:07 ecd Exp $
+ * mach64.h: Ultra/PCI mach64 driver constants etc.
+ *
+ * Copyright 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _MACH64_H
+#define _MACH64_H 1
+
+struct mach64_info {
+ unsigned int color_mode;
+ unsigned int flags;
+ unsigned int total_vram;
+};
+
+/* The mach64_info flag bits. */
+#define MACH64_MASK_VT 0x00000001
+
+/* NON-GUI MEMORY MAPPED Registers - expressed in BYTE offsets */
+
+#define CRTC_H_TOTAL_DISP 0x0000 /* Dword offset 00 */
+#define CRTC_H_SYNC_STRT_WID 0x0004 /* Dword offset 01 */
+#define CRTC_H_SYNC_STRT 0x0004
+#define CRTC_H_SYNC_DLY 0x0005
+#define CRTC_H_SYNC_WID 0x0006
+
+#define CRTC_V_TOTAL_DISP 0x0008 /* Dword offset 02 */
+#define CRTC_V_TOTAL 0x0008
+#define CRTC_V_DISP 0x000a
+#define CRTC_V_SYNC_STRT_WID 0x000C /* Dword offset 03 */
+#define CRTC_V_SYNC_STRT 0x000c
+#define CRTC_V_SYNC_WID 0x000e
+
+#define CRTC_VLINE_CRNT_VLINE 0x0010 /* Dword offset 04 */
+#define CRTC_OFF_PITCH 0x0014 /* Dword offset 05 */
+#define CRTC_OFFSET 0x0014
+#define CRTC_PITCH 0x0016
+
+#define CRTC_INT_CNTL 0x0018 /* Dword offset 06 */
+#define CRTC_GEN_CNTL 0x001C /* Dword offset 07 */
+#define CRTC_PIX_WIDTH 0x001d
+#define CRTC_FIFO 0x001e
+#define CRTC_EXT_DISP 0x001f
+
+#define OVR_CLR 0x0040 /* Dword offset 10 */
+#define OVR_WID_LEFT_RIGHT 0x0044 /* Dword offset 11 */
+#define OVR_WID_TOP_BOTTOM 0x0048 /* Dword offset 12 */
+
+#define CUR_CLR0 0x0060 /* Dword offset 18 */
+#define CUR_CLR1 0x0064 /* Dword offset 19 */
+#define CUR_OFFSET 0x0068 /* Dword offset 1A */
+#define CUR_HORZ_VERT_POSN 0x006C /* Dword offset 1B */
+#define CUR_HORZ_VERT_OFF 0x0070 /* Dword offset 1C */
+
+#define SCRATCH_REG0 0x0080 /* Dword offset 20 */
+#define SCRATCH_REG1 0x0084 /* Dword offset 21 */
+
+#define CLOCK_CNTL 0x0090 /* Dword offset 24 */
+#define CLOCK_SEL_CNTL 0x0090 // Dword offset 24
+
+#define BUS_CNTL 0x00A0 /* Dword offset 28 */
+
+#define MEM_CNTL 0x00B0 /* Dword offset 2C */
+
+#define MEM_VGA_WP_SEL 0x00B4 /* Dword offset 2D */
+#define MEM_VGA_RP_SEL 0x00B8 /* Dword offset 2E */
+
+#define DAC_REGS 0x00C0 /* Dword offset 30 */
+#define DAC_W_INDEX 0x00C0 /* Dword offset 30 */
+#define DAC_DATA 0x00C1 /* Dword offset 30 */
+#define DAC_MASK 0x00C2 /* Dword offset 30 */
+#define DAC_R_INDEX 0x00C3 /* Dword offset 30 */
+#define DAC_CNTL 0x00C4 /* Dword offset 31 */
+
+#define GEN_TEST_CNTL 0x00D0 /* Dword offset 34 */
+
+#define CONFIG_CNTL 0x00DC /* Dword offset 37 (CT, ET, VT) */
+#define CONFIG_CHIP_ID 0x00E0 /* Dword offset 38 */
+#define CONFIG_STAT0 0x00E4 /* Dword offset 39 */
+#define CONFIG_STAT1 0x00E8 /* Dword offset 3A */
+
+
+/* GUI MEMORY MAPPED Registers */
+
+#define DST_OFF_PITCH 0x0100 /* Dword offset 40 */
+#define DST_X 0x0104 /* Dword offset 41 */
+#define DST_Y 0x0108 /* Dword offset 42 */
+#define DST_Y_X 0x010C /* Dword offset 43 */
+#define DST_WIDTH 0x0110 /* Dword offset 44 */
+#define DST_HEIGHT 0x0114 /* Dword offset 45 */
+#define DST_HEIGHT_WIDTH 0x0118 /* Dword offset 46 */
+#define DST_X_WIDTH 0x011C /* Dword offset 47 */
+#define DST_BRES_LNTH 0x0120 /* Dword offset 48 */
+#define DST_BRES_ERR 0x0124 /* Dword offset 49 */
+#define DST_BRES_INC 0x0128 /* Dword offset 4A */
+#define DST_BRES_DEC 0x012C /* Dword offset 4B */
+#define DST_CNTL 0x0130 /* Dword offset 4C */
+
+#define SRC_OFF_PITCH 0x0180 /* Dword offset 60 */
+#define SRC_X 0x0184 /* Dword offset 61 */
+#define SRC_Y 0x0188 /* Dword offset 62 */
+#define SRC_Y_X 0x018C /* Dword offset 63 */
+#define SRC_WIDTH1 0x0190 /* Dword offset 64 */
+#define SRC_HEIGHT1 0x0194 /* Dword offset 65 */
+#define SRC_HEIGHT1_WIDTH1 0x0198 /* Dword offset 66 */
+#define SRC_X_START 0x019C /* Dword offset 67 */
+#define SRC_Y_START 0x01A0 /* Dword offset 68 */
+#define SRC_Y_X_START 0x01A4 /* Dword offset 69 */
+#define SRC_WIDTH2 0x01A8 /* Dword offset 6A */
+#define SRC_HEIGHT2 0x01AC /* Dword offset 6B */
+#define SRC_HEIGHT2_WIDTH2 0x01B0 /* Dword offset 6C */
+#define SRC_CNTL 0x01B4 /* Dword offset 6D */
+
+#define HOST_DATA0 0x0200 /* Dword offset 80 */
+#define HOST_DATA1 0x0204 /* Dword offset 81 */
+#define HOST_DATA2 0x0208 /* Dword offset 82 */
+#define HOST_DATA3 0x020C /* Dword offset 83 */
+#define HOST_DATA4 0x0210 /* Dword offset 84 */
+#define HOST_DATA5 0x0214 /* Dword offset 85 */
+#define HOST_DATA6 0x0218 /* Dword offset 86 */
+#define HOST_DATA7 0x021C /* Dword offset 87 */
+#define HOST_DATA8 0x0220 /* Dword offset 88 */
+#define HOST_DATA9 0x0224 /* Dword offset 89 */
+#define HOST_DATAA 0x0228 /* Dword offset 8A */
+#define HOST_DATAB 0x022C /* Dword offset 8B */
+#define HOST_DATAC 0x0230 /* Dword offset 8C */
+#define HOST_DATAD 0x0234 /* Dword offset 8D */
+#define HOST_DATAE 0x0238 /* Dword offset 8E */
+#define HOST_DATAF 0x023C /* Dword offset 8F */
+#define HOST_CNTL 0x0240 /* Dword offset 90 */
+
+#define PAT_REG0 0x0280 /* Dword offset A0 */
+#define PAT_REG1 0x0284 /* Dword offset A1 */
+#define PAT_CNTL 0x0288 /* Dword offset A2 */
+
+#define SC_LEFT 0x02A0 /* Dword offset A8 */
+#define SC_RIGHT 0x02A4 /* Dword offset A9 */
+#define SC_LEFT_RIGHT 0x02A8 /* Dword offset AA */
+#define SC_TOP 0x02AC /* Dword offset AB */
+#define SC_BOTTOM 0x02B0 /* Dword offset AC */
+#define SC_TOP_BOTTOM 0x02B4 /* Dword offset AD */
+
+#define DP_BKGD_CLR 0x02C0 /* Dword offset B0 */
+#define DP_FRGD_CLR 0x02C4 /* Dword offset B1 */
+#define DP_WRITE_MASK 0x02C8 /* Dword offset B2 */
+#define DP_CHAIN_MASK 0x02CC /* Dword offset B3 */
+#define DP_PIX_WIDTH 0x02D0 /* Dword offset B4 */
+#define DP_MIX 0x02D4 /* Dword offset B5 */
+#define DP_SRC 0x02D8 /* Dword offset B6 */
+
+#define CLR_CMP_CLR 0x0300 /* Dword offset C0 */
+#define CLR_CMP_MASK 0x0304 /* Dword offset C1 */
+#define CLR_CMP_CNTL 0x0308 /* Dword offset C2 */
+
+#define FIFO_STAT 0x0310 /* Dword offset C4 */
+
+#define CONTEXT_MASK 0x0320 /* Dword offset C8 */
+#define CONTEXT_LOAD_CNTL 0x032C /* Dword offset CB */
+
+#define GUI_TRAJ_CNTL 0x0330 /* Dword offset CC */
+#define GUI_STAT 0x0338 /* Dword offset CE */
+
+
+/* CRTC control values (mostly CRTC_GEN_CNTL) */
+
+#define CRTC_H_SYNC_NEG 0x00200000
+#define CRTC_V_SYNC_NEG 0x00200000
+
+#define CRTC_DBL_SCAN_EN 0x00000001
+#define CRTC_INTERLACE_EN 0x00000002
+#define CRTC_HSYNC_DIS 0x00000004
+#define CRTC_VSYNC_DIS 0x00000008
+#define CRTC_CSYNC_EN 0x00000010
+#define CRTC_PIX_BY_2_EN 0x00000020
+#define CRTC_BLANK 0x00000040
+
+#define CRTC_PIX_WIDTH_MASK 0x00000700
+#define CRTC_PIX_WIDTH_4BPP 0x00000100
+#define CRTC_PIX_WIDTH_8BPP 0x00000200
+#define CRTC_PIX_WIDTH_15BPP 0x00000300
+#define CRTC_PIX_WIDTH_16BPP 0x00000400
+#define CRTC_PIX_WIDTH_24BPP 0x00000500
+#define CRTC_PIX_WIDTH_32BPP 0x00000600
+
+#define CRTC_BYTE_PIX_ORDER 0x00000800
+#define CRTC_PIX_ORDER_MSN_LSN 0x00000000
+#define CRTC_PIX_ORDER_LSN_MSN 0x00000800
+
+#define CRTC_FIFO_LWM 0x000f0000
+#define CRTC_EXT_DISP_EN 0x01000000
+#define CRTC_EXT_EN 0x02000000
+
+#define CRTC_CRNT_VLINE 0x07f00000
+#define CRTC_VBLANK 0x00000001
+
+/* DAC control values */
+
+#define DAC_EXT_SEL_RS2 0x01
+#define DAC_EXT_SEL_RS3 0x02
+#define DAC_8BIT_EN 0x00000100
+#define DAC_PIX_DLY_MASK 0x00000600
+#define DAC_PIX_DLY_0NS 0x00000000
+#define DAC_PIX_DLY_2NS 0x00000200
+#define DAC_PIX_DLY_4NS 0x00000400
+#define DAC_BLANK_ADJ_MASK 0x00001800
+#define DAC_BLANK_ADJ_0 0x00000000
+#define DAC_BLANK_ADJ_1 0x00000800
+#define DAC_BLANK_ADJ_2 0x00001000
+
+
+/* Mix control values */
+
+#define MIX_NOT_DST 0x0000
+#define MIX_0 0x0001
+#define MIX_1 0x0002
+#define MIX_DST 0x0003
+#define MIX_NOT_SRC 0x0004
+#define MIX_XOR 0x0005
+#define MIX_XNOR 0x0006
+#define MIX_SRC 0x0007
+#define MIX_NAND 0x0008
+#define MIX_NOT_SRC_OR_DST 0x0009
+#define MIX_SRC_OR_NOT_DST 0x000a
+#define MIX_OR 0x000b
+#define MIX_AND 0x000c
+#define MIX_SRC_AND_NOT_DST 0x000d
+#define MIX_NOT_SRC_AND_DST 0x000e
+#define MIX_NOR 0x000f
+
+/* Maximum engine dimensions */
+#define ENGINE_MIN_X 0
+#define ENGINE_MIN_Y 0
+#define ENGINE_MAX_X 4095
+#define ENGINE_MAX_Y 16383
+
+/* Mach64 engine bit constants - these are typically ORed together */
+
+/* BUS_CNTL register constants */
+#define BUS_FIFO_ERR_ACK 0x00200000
+#define BUS_HOST_ERR_ACK 0x00800000
+
+/* GEN_TEST_CNTL register constants */
+#define GEN_OVR_OUTPUT_EN 0x20
+#define HWCURSOR_ENABLE 0x80
+#define GUI_ENGINE_ENABLE 0x100
+#define BLOCK_WRITE_ENABLE 0x200
+
+/* CLOCK_CNTL register constants */
+#define CLOCK_SEL 0x0f
+#define CLOCK_DIV 0x30
+#define CLOCK_DIV1 0x00
+#define CLOCK_DIV2 0x10
+#define CLOCK_DIV4 0x20
+#define CLOCK_STROBE 0x40
+#define PLL_WR_EN 0x02
+
+/* PLL registers */
+#define PLL_MACRO_CNTL 0x01
+#define PLL_REF_DIV 0x02
+#define PLL_GEN_CNTL 0x03
+#define MCLK_FB_DIV 0x04
+#define PLL_VCLK_CNTL 0x05
+#define VCLK_POST_DIV 0x06
+#define VCLK0_FB_DIV 0x07
+#define VCLK1_FB_DIV 0x08
+#define VCLK2_FB_DIV 0x09
+#define VCLK3_FB_DIV 0x0A
+#define PLL_XCLK_CNTL 0x0B
+#define PLL_TEST_CTRL 0x0E
+#define PLL_TEST_COUNT 0x0F
+
+/* Fields in PLL registers */
+#define PLL_PC_GAIN 0x07
+#define PLL_VC_GAIN 0x18
+#define PLL_DUTY_CYC 0xE0
+#define PLL_OVERRIDE 0x01
+#define PLL_MCLK_RST 0x02
+#define OSC_EN 0x04
+#define EXT_CLK_EN 0x08
+#define MCLK_SRC_SEL 0x70
+#define EXT_CLK_CNTL 0x80
+#define VCLK_SRC_SEL 0x03
+#define PLL_VCLK_RST 0x04
+#define VCLK_INVERT 0x08
+#define VCLK0_POST 0x03
+#define VCLK1_POST 0x0C
+#define VCLK2_POST 0x30
+#define VCLK3_POST 0xC0
+
+/* CONFIG_CNTL register constants */
+#define APERTURE_4M_ENABLE 1
+#define APERTURE_8M_ENABLE 2
+#define VGA_APERTURE_ENABLE 4
+
+/* CONFIG_STAT0 register constants (GX, CX) */
+#define CFG_BUS_TYPE 0x00000007
+#define CFG_MEM_TYPE 0x00000038
+#define CFG_INIT_DAC_TYPE 0x00000e00
+
+/* CONFIG_STAT0 register constants (CT, ET, VT) */
+#define CFG_MEM_TYPE_xT 0x00000007
+
+#define ISA 0
+#define EISA 1
+#define LOCAL_BUS 6
+#define PCI 7
+
+/* Memory types for GX, CX */
+#define DRAMx4 0
+#define VRAMx16 1
+#define VRAMx16ssr 2
+#define DRAMx16 3
+#define GraphicsDRAMx16 4
+#define EnhancedVRAMx16 5
+#define EnhancedVRAMx16ssr 6
+
+/* Memory types for CT, ET, VT, GT */
+#define DRAM 0
+#define EDO_DRAM 1
+#define PSEUDO_EDO 2
+#define SDRAM 3
+
+#define DAC_INTERNAL 0x00
+#define DAC_IBMRGB514 0x01
+#define DAC_ATI68875 0x02
+#define DAC_TVP3026_A 0x72
+#define DAC_BT476 0x03
+#define DAC_BT481 0x04
+#define DAC_ATT20C491 0x14
+#define DAC_SC15026 0x24
+#define DAC_MU9C1880 0x34
+#define DAC_IMSG174 0x44
+#define DAC_ATI68860_B 0x05
+#define DAC_ATI68860_C 0x15
+#define DAC_TVP3026_B 0x75
+#define DAC_STG1700 0x06
+#define DAC_ATT498 0x16
+#define DAC_STG1702 0x07
+#define DAC_SC15021 0x17
+#define DAC_ATT21C498 0x27
+#define DAC_STG1703 0x37
+#define DAC_CH8398 0x47
+#define DAC_ATT20C408 0x57
+
+#define CLK_ATI18818_0 0
+#define CLK_ATI18818_1 1
+#define CLK_STG1703 2
+#define CLK_CH8398 3
+#define CLK_INTERNAL 4
+#define CLK_ATT20C408 5
+#define CLK_IBMRGB514 6
+
+/* MEM_CNTL register constants */
+#define MEM_SIZE_ALIAS 0x00000007
+#define MEM_SIZE_512K 0x00000000
+#define MEM_SIZE_1M 0x00000001
+#define MEM_SIZE_2M 0x00000002
+#define MEM_SIZE_4M 0x00000003
+#define MEM_SIZE_6M 0x00000004
+#define MEM_SIZE_8M 0x00000005
+#define MEM_SIZE_ALIAS_GTB 0x0000000F
+#define MEM_SIZE_2M_GTB 0x00000003
+#define MEM_SIZE_4M_GTB 0x00000007
+#define MEM_SIZE_6M_GTB 0x00000009
+#define MEM_SIZE_8M_GTB 0x0000000B
+#define MEM_BNDRY 0x00030000
+#define MEM_BNDRY_0K 0x00000000
+#define MEM_BNDRY_256K 0x00010000
+#define MEM_BNDRY_512K 0x00020000
+#define MEM_BNDRY_1M 0x00030000
+#define MEM_BNDRY_EN 0x00040000
+
+/* ATI PCI constants */
+#define PCI_ATI_VENDOR_ID 0x1002
+#define PCI_MACH64_GX 0x4758
+#define PCI_MACH64_CX 0x4358
+#define PCI_MACH64_CT 0x4354
+#define PCI_MACH64_ET 0x4554
+#define PCI_MACH64_VT 0x5654
+#define PCI_MACH64_GT 0x4754
+
+/* CONFIG_CHIP_ID register constants */
+#define CFG_CHIP_TYPE 0x0000FFFF
+#define CFG_CHIP_CLASS 0x00FF0000
+#define CFG_CHIP_REV 0xFF000000
+#define CFG_CHIP_VERSION 0x07000000
+#define CFG_CHIP_FOUNDRY 0x38000000
+#define CFG_CHIP_REVISION 0xC0000000
+
+/* Chip IDs read from CONFIG_CHIP_ID */
+#define MACH64_GX_ID 0xD7
+#define MACH64_CX_ID 0x57
+#define MACH64_CT_ID 0x4354
+#define MACH64_ET_ID 0x4554
+#define MACH64_VT_ID 0x5654
+#define MACH64_GT_ID 0x4754
+
+/* Mach64 chip types */
+#define MACH64_UNKNOWN 0
+#define MACH64_GX 1
+#define MACH64_CX 2
+#define MACH64_CT 3
+#define MACH64_ET 4
+#define MACH64_VT 5
+#define MACH64_GT 6
+
+/* DST_CNTL register constants */
+#define DST_X_RIGHT_TO_LEFT 0
+#define DST_X_LEFT_TO_RIGHT 1
+#define DST_Y_BOTTOM_TO_TOP 0
+#define DST_Y_TOP_TO_BOTTOM 2
+#define DST_X_MAJOR 0
+#define DST_Y_MAJOR 4
+#define DST_X_TILE 8
+#define DST_Y_TILE 0x10
+#define DST_LAST_PEL 0x20
+#define DST_POLYGON_ENABLE 0x40
+#define DST_24_ROTATION_ENABLE 0x80
+
+/* SRC_CNTL register constants */
+#define SRC_PATTERN_ENABLE 1
+#define SRC_ROTATION_ENABLE 2
+#define SRC_LINEAR_ENABLE 4
+#define SRC_BYTE_ALIGN 8
+#define SRC_LINE_X_RIGHT_TO_LEFT 0
+#define SRC_LINE_X_LEFT_TO_RIGHT 0x10
+
+/* HOST_CNTL register constants */
+#define HOST_BYTE_ALIGN 1
+
+/* GUI_TRAJ_CNTL register constants */
+#define PAT_MONO_8x8_ENABLE 0x01000000
+#define PAT_CLR_4x2_ENABLE 0x02000000
+#define PAT_CLR_8x1_ENABLE 0x04000000
+
+/* DP_CHAIN_MASK register constants */
+#define DP_CHAIN_4BPP 0x8888
+#define DP_CHAIN_7BPP 0xD2D2
+#define DP_CHAIN_8BPP 0x8080
+#define DP_CHAIN_8BPP_RGB 0x9292
+#define DP_CHAIN_15BPP 0x4210
+#define DP_CHAIN_16BPP 0x8410
+#define DP_CHAIN_24BPP 0x8080
+#define DP_CHAIN_32BPP 0x8080
+
+/* DP_PIX_WIDTH register constants */
+#define DST_1BPP 0
+#define DST_4BPP 1
+#define DST_8BPP 2
+#define DST_15BPP 3
+#define DST_16BPP 4
+#define DST_32BPP 6
+#define SRC_1BPP 0
+#define SRC_4BPP 0x100
+#define SRC_8BPP 0x200
+#define SRC_15BPP 0x300
+#define SRC_16BPP 0x400
+#define SRC_32BPP 0x600
+#define HOST_1BPP 0
+#define HOST_4BPP 0x10000
+#define HOST_8BPP 0x20000
+#define HOST_15BPP 0x30000
+#define HOST_16BPP 0x40000
+#define HOST_32BPP 0x60000
+#define BYTE_ORDER_MSB_TO_LSB 0
+#define BYTE_ORDER_LSB_TO_MSB 0x1000000
+
+/* DP_MIX register constants */
+#define BKGD_MIX_NOT_D 0
+#define BKGD_MIX_ZERO 1
+#define BKGD_MIX_ONE 2
+#define BKGD_MIX_D 3
+#define BKGD_MIX_NOT_S 4
+#define BKGD_MIX_D_XOR_S 5
+#define BKGD_MIX_NOT_D_XOR_S 6
+#define BKGD_MIX_S 7
+#define BKGD_MIX_NOT_D_OR_NOT_S 8
+#define BKGD_MIX_D_OR_NOT_S 9
+#define BKGD_MIX_NOT_D_OR_S 10
+#define BKGD_MIX_D_OR_S 11
+#define BKGD_MIX_D_AND_S 12
+#define BKGD_MIX_NOT_D_AND_S 13
+#define BKGD_MIX_D_AND_NOT_S 14
+#define BKGD_MIX_NOT_D_AND_NOT_S 15
+#define BKGD_MIX_D_PLUS_S_DIV2 0x17
+#define FRGD_MIX_NOT_D 0
+#define FRGD_MIX_ZERO 0x10000
+#define FRGD_MIX_ONE 0x20000
+#define FRGD_MIX_D 0x30000
+#define FRGD_MIX_NOT_S 0x40000
+#define FRGD_MIX_D_XOR_S 0x50000
+#define FRGD_MIX_NOT_D_XOR_S 0x60000
+#define FRGD_MIX_S 0x70000
+#define FRGD_MIX_NOT_D_OR_NOT_S 0x80000
+#define FRGD_MIX_D_OR_NOT_S 0x90000
+#define FRGD_MIX_NOT_D_OR_S 0xa0000
+#define FRGD_MIX_D_OR_S 0xb0000
+#define FRGD_MIX_D_AND_S 0xc0000
+#define FRGD_MIX_NOT_D_AND_S 0xd0000
+#define FRGD_MIX_D_AND_NOT_S 0xe0000
+#define FRGD_MIX_NOT_D_AND_NOT_S 0xf0000
+#define FRGD_MIX_D_PLUS_S_DIV2 0x170000
+
+/* DP_SRC register constants */
+#define BKGD_SRC_BKGD_CLR 0
+#define BKGD_SRC_FRGD_CLR 1
+#define BKGD_SRC_HOST 2
+#define BKGD_SRC_BLIT 3
+#define BKGD_SRC_PATTERN 4
+#define FRGD_SRC_BKGD_CLR 0
+#define FRGD_SRC_FRGD_CLR 0x100
+#define FRGD_SRC_HOST 0x200
+#define FRGD_SRC_BLIT 0x300
+#define FRGD_SRC_PATTERN 0x400
+#define MONO_SRC_ONE 0
+#define MONO_SRC_PATTERN 0x10000
+#define MONO_SRC_HOST 0x20000
+#define MONO_SRC_BLIT 0x30000
+
+/* CLR_CMP_CNTL register constants */
+#define COMPARE_FALSE 0
+#define COMPARE_TRUE 1
+#define COMPARE_NOT_EQUAL 4
+#define COMPARE_EQUAL 5
+#define COMPARE_DESTINATION 0
+#define COMPARE_SOURCE 0x1000000
+
+/* FIFO_STAT register constants */
+#define FIFO_ERR 0x80000000
+
+/* CONTEXT_LOAD_CNTL constants */
+#define CONTEXT_NO_LOAD 0
+#define CONTEXT_LOAD 0x10000
+#define CONTEXT_LOAD_AND_DO_FILL 0x20000
+#define CONTEXT_LOAD_AND_DO_LINE 0x30000
+#define CONTEXT_EXECUTE 0
+#define CONTEXT_CMD_DISABLE 0x80000000
+
+/* GUI_STAT register constants */
+#define ENGINE_IDLE 0
+#define ENGINE_BUSY 1
+#define SCISSOR_LEFT_FLAG 0x10
+#define SCISSOR_RIGHT_FLAG 0x20
+#define SCISSOR_TOP_FLAG 0x40
+#define SCISSOR_BOTTOM_FLAG 0x80
+
+/* ATI VGA Extended Regsiters */
+#define sioATIEXT 0x1ce
+#define bioATIEXT 0x3ce
+
+#define ATI2E 0xae
+#define ATI32 0xb2
+#define ATI36 0xb6
+
+/* VGA Graphics Controller Registers */
+#define VGAGRA 0x3ce
+#define GRA06 0x06
+
+/* VGA Seququencer Registers */
+#define VGASEQ 0x3c4
+#define SEQ02 0x02
+#define SEQ04 0x04
+
+#define MACH64_MAX_X ENGINE_MAX_X
+#define MACH64_MAX_Y ENGINE_MAX_Y
+
+#define INC_X 0x0020
+#define INC_Y 0x0080
+
+#define RGB16_555 0x0000
+#define RGB16_565 0x0040
+#define RGB16_655 0x0080
+#define RGB16_664 0x00c0
+
+#define POLY_TEXT_TYPE 0x0001
+#define IMAGE_TEXT_TYPE 0x0002
+#define TEXT_TYPE_8_BIT 0x0004
+#define TEXT_TYPE_16_BIT 0x0008
+#define POLY_TEXT_TYPE_8 (POLY_TEXT_TYPE | TEXT_TYPE_8_BIT)
+#define IMAGE_TEXT_TYPE_8 (IMAGE_TEXT_TYPE | TEXT_TYPE_8_BIT)
+#define POLY_TEXT_TYPE_16 (POLY_TEXT_TYPE | TEXT_TYPE_16_BIT)
+#define IMAGE_TEXT_TYPE_16 (IMAGE_TEXT_TYPE | TEXT_TYPE_16_BIT)
+
+#define MACH64_NUM_CLOCKS 16
+#define MACH64_NUM_FREQS 50
+
+#endif /* !(_MACH64_H) */
diff --git a/drivers/sbus/char/pcicons.c b/drivers/sbus/char/pcicons.c
new file mode 100644
index 000000000..82c0df9aa
--- /dev/null
+++ b/drivers/sbus/char/pcicons.c
@@ -0,0 +1,750 @@
+/* $Id: pcicons.c,v 1.9 1997/08/28 02:23:24 ecd Exp $
+ * pcicons.c: PCI specific probing and console operations layer.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_PCI
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/major.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+
+#include <asm/uaccess.h>
+#include <asm/oplib.h>
+#include <asm/sbus.h>
+#include <asm/pbm.h>
+#include <asm/fbio.h>
+#include <asm/smp.h>
+
+#include <linux/kd.h>
+#include <linux/console_struct.h>
+#include <linux/selection.h>
+#include <linux/vt_kern.h>
+
+#include "pcicons.h"
+#include "fb.h"
+
+static int x_margin = 0;
+static int y_margin = 0;
+static int skip_bytes;
+
+static void pci_cursor_blink(unsigned long);
+static __u32 *cursor_screen_pos;
+static __u32 cursor_bits;
+static int cursor_pos = -1;
+static int cursor_off = 1;
+static struct timer_list pci_cursor_timer = {
+ NULL, NULL, 0, 0, pci_cursor_blink
+};
+
+extern int serial_console;
+
+static void pci_install_consops(void);
+static int (*fbuf_offset)(int);
+
+static int color_fbuf_offset_1152_128(int cindex)
+{
+ register int i = (cindex >> 7);
+ /* (1152 * CHAR_HEIGHT) == 100.1000.0000.0000 */
+ return skip_bytes + (i << 14) + (i << 11) + ((cindex & 127) << 3);
+}
+
+static int color_fbuf_offset_1024_128(int cindex)
+{
+ register int i = (cindex >> 7);
+ /* (1024 * CHAR_HEIGHT) == 100.0000.0000.0000 */
+ return skip_bytes + (i << 14) + ((cindex & 127) << 3);
+}
+
+static __u32 expand_bits_8[16] = {
+ 0x00000000,
+ 0x000000ff,
+ 0x0000ff00,
+ 0x0000ffff,
+ 0x00ff0000,
+ 0x00ff00ff,
+ 0x00ffff00,
+ 0x00ffffff,
+ 0xff000000,
+ 0xff0000ff,
+ 0xff00ff00,
+ 0xff00ffff,
+ 0xffff0000,
+ 0xffff00ff,
+ 0xffffff00,
+ 0xffffffff
+};
+
+static void pci_blitc(unsigned int charattr, unsigned long addr)
+{
+ static int cached_attr = -1;
+ static __u32 fg, bg;
+ fbinfo_t *fb = &fbinfo[0];
+ __u32 *screen;
+ unsigned char attrib;
+ unsigned char *fp;
+ unsigned long flags;
+ int i, idx;
+
+ if ((charattr & 0xff00) != cached_attr) {
+ cached_attr = charattr;
+ attrib = CHARATTR_TO_SUNCOLOR(charattr);
+ fg = attrib & 0x0f;
+ fg |= fg << 8;
+ fg |= fg << 16;
+ bg = attrib >> 4;
+ bg |= bg << 8;
+ bg |= bg << 16;
+ fg ^= bg;
+ }
+
+ idx = (addr - video_mem_base) >> 1;
+ save_flags(flags); cli();
+ if (cursor_pos == idx)
+ cursor_pos = -1;
+ restore_flags(flags);
+
+ screen = (__u32 *)(fb->base + fbuf_offset(idx));
+ fp = &vga_font[(charattr & 0xff) << 4];
+
+ for(i = 0; i < 16; i++) {
+ int bits = *fp++;
+
+ screen[0] = (expand_bits_8[bits >> 4] & fg) ^ bg;
+ screen[1] = (expand_bits_8[bits & 0x0f] & fg) ^ bg;
+ screen = (__u32 *) (((unsigned long)screen) + fb->linebytes);
+ }
+}
+
+
+static void pci_memsetw(void *s, unsigned short c, unsigned int count)
+{
+ unsigned short *p = (unsigned short *)s;
+
+ count >>= 1;
+ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
+ while (count) {
+ --count;
+ *p++ = c;
+ }
+ return;
+ }
+ if ((unsigned long)(p + count) > video_mem_base &&
+ (unsigned long)p < video_mem_term) {
+ for (; p < (unsigned short *)video_mem_base && count; count--)
+ *p++ = c;
+ for (; p < (unsigned short *)video_mem_term && count; count--) {
+ if (*p != c) {
+ *p = c;
+ pci_blitc(c, (unsigned long)p);
+ }
+ ++p;
+ }
+ }
+ for (; count; count--)
+ *p++ = c;
+}
+
+static void pci_memcpyw(unsigned short *dst, unsigned short *src,
+ unsigned int count)
+{
+ unsigned short c;
+
+ count >>= 1;
+ if ((unsigned long)(dst + count) > video_mem_base &&
+ (unsigned long)dst < video_mem_term) {
+ for (; dst < (unsigned short *)video_mem_base && count; count--)
+ *dst++ = *src++;
+ for (; dst < (unsigned short *)video_mem_term && count;
+ count--) {
+ c = *src++;
+ if (*dst != c) {
+ *dst = c;
+ pci_blitc(c, (unsigned long)dst);
+ }
+ ++dst;
+ }
+ }
+ for (; count; count--)
+ *dst++ = *src++;
+}
+
+static void pci_scr_writew(unsigned short val, unsigned short *addr)
+{
+ if (*addr != val) {
+ *addr = val;
+ if ((unsigned long)addr < video_mem_term &&
+ (unsigned long)addr >= video_mem_base &&
+ vt_cons[fg_console]->vc_mode == KD_TEXT)
+ pci_blitc(val, (unsigned long) addr);
+ }
+}
+
+static unsigned short pci_scr_readw(unsigned short *addr)
+{
+ return *addr;
+}
+
+static void pci_get_scrmem(int currcons)
+{
+ memcpyw((unsigned short *)vc_scrbuf[currcons],
+ (unsigned short *)origin, video_screen_size);
+ origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
+ scr_end = video_mem_end = video_mem_start + video_screen_size;
+ pos = origin + y * video_size_row + (x << 1);
+}
+
+static void pci_set_scrmem(int currcons, long offset)
+{
+ if (video_mem_term - video_mem_base < offset + video_screen_size)
+ offset = 0;
+ memcpyw((unsigned short *)(video_mem_base + offset),
+ (unsigned short *) origin, video_screen_size);
+ video_mem_start = video_mem_base;
+ video_mem_end = video_mem_term;
+ origin = video_mem_base + offset;
+ scr_end = origin + video_screen_size;
+ pos = origin + y * video_size_row + (x << 1);
+}
+
+static void pci_invert_cursor(int cpos)
+{
+ fbinfo_t *fb = &fbinfo[0];
+ unsigned char color;
+ __u32 *screen, mask;
+ int i;
+
+ del_timer(&pci_cursor_timer);
+
+ if (cpos == -1) {
+ if (cursor_off)
+ return;
+ screen = cursor_screen_pos;
+ mask = cursor_bits;
+ } else {
+ screen = (__u32 *)(fb->base + fbuf_offset(cpos)
+ + 14 * fb->linebytes);
+
+ color = CHARATTR_TO_SUNCOLOR(
+ vc_cons[fg_console].d->vc_color << 8);
+
+ mask = (color ^ (color >> 4)) & 0x0f;
+ mask |= mask << 8;
+ mask |= mask << 16;
+
+ cursor_screen_pos = screen;
+ cursor_bits = mask;
+
+ pci_cursor_timer.expires = jiffies + (HZ >> 2);
+ add_timer(&pci_cursor_timer);
+ }
+
+ for (i = 0; i < 2; i++) {
+ screen[0] ^= mask;
+ screen[1] ^= mask;
+ screen = (__u32 *)((unsigned long)screen + fb->linebytes);
+ }
+}
+
+static void pci_cursor_blink(unsigned long ignored)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ if (cursor_pos != -1) {
+ pci_invert_cursor(cursor_pos);
+ cursor_off = 1 - cursor_off;
+ }
+ restore_flags(flags);
+}
+
+static void pci_hide_cursor(void)
+{
+ unsigned long flags;
+
+ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+ return;
+
+ save_flags(flags); cli();
+ if (cursor_pos != -1) {
+ pci_invert_cursor(-1);
+ cursor_pos = -1;
+ }
+ cursor_off = 1;
+ restore_flags(flags);
+}
+
+static void pci_set_cursor(int currcons)
+{
+ unsigned long flags;
+ int old_cursor;
+
+ if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
+ return;
+
+ save_flags(flags); cli();
+ if (!deccm) {
+ pci_hide_cursor();
+ } else {
+ old_cursor = cursor_pos;
+ cursor_pos = (pos - video_mem_base) >> 1;
+ if (old_cursor != -1)
+ pci_invert_cursor(-1);
+ pci_invert_cursor(cursor_pos);
+ cursor_off = 0;
+ }
+ restore_flags(flags);
+}
+
+static void pci_set_palette(void)
+{
+ fbinfo_t *fb = &fbinfo[0];
+
+ if (console_blanked || vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+ return;
+
+ if (fb->loadcmap) {
+ int i, j;
+
+ for (i = 0; i < 16; i++) {
+ j = sparc_color_table[i];
+ fb->color_map CM(i, 0) = default_red[j];
+ fb->color_map CM(i, 1) = default_grn[j];
+ fb->color_map CM(i, 2) = default_blu[j];
+ }
+ fb->loadcmap(fb, 0, 16);
+ }
+}
+
+static void pci_set_other_palette(int n)
+{
+ fbinfo_t *fb = &fbinfo[n];
+
+ if (!n) {
+ pci_set_palette();
+ return;
+ }
+
+ if (fb->loadcmap) {
+ fb->color_map CM(0, 0) = 0;
+ fb->color_map CM(0, 1) = 0;
+ fb->color_map CM(0, 2) = 0;
+ fb->loadcmap(fb, 0, 1);
+ }
+}
+
+static void pci_restore_palette(void)
+{
+ if (fb_restore_palette)
+ fb_restore_palette(&fbinfo[0]);
+}
+
+static int pci_set_get_font(char *arg, int set, int ch512)
+{
+ int i, line;
+
+ if (!arg)
+ return -EINVAL;
+
+ if (!set) {
+ if (clear_user(arg, sizeof(vga_font)))
+ return -EFAULT;
+ for (i = 0; i < 256; i++) {
+ for (line = 0; line < CHAR_HEIGHT; line++) {
+ unsigned char value;
+
+ value = vga_font[i * CHAR_HEIGHT + line];
+ __put_user_ret(value, (arg + (i * 32 + line)),
+ -EFAULT);
+ }
+ }
+ return 0;
+ }
+
+ if (verify_area(VERIFY_READ, arg, 256 * CHAR_HEIGHT))
+ return -EFAULT;
+ for (i = 0; i < 256; i++) {
+ for (line = 0; line < CHAR_HEIGHT; line++) {
+ unsigned char value;
+ __get_user_ret(value, (arg + (i + 32 + line)), -EFAULT);
+ vga_font[i * CHAR_HEIGHT + line] = value;
+ }
+ }
+ return 0;
+}
+
+static int pci_con_adjust_height(unsigned long fontheight)
+{
+ return -EINVAL;
+}
+
+static int pci_set_get_cmap(unsigned char *arg, int set)
+{
+ int i;
+
+ if (set)
+ i = VERIFY_READ;
+ else
+ i = VERIFY_WRITE;
+
+ if (verify_area(i, arg, (16 * 3 * sizeof(unsigned char))))
+ return -EFAULT;
+
+ for (i = 0; i < 16; i++) {
+ if (set) {
+ __get_user_ret(default_red[i], (arg + 0), -EFAULT);
+ __get_user_ret(default_grn[i], (arg + 1), -EFAULT);
+ __get_user_ret(default_blu[i], (arg + 2), -EFAULT);
+ } else {
+ __put_user_ret(default_red[i], (arg + 0), -EFAULT);
+ __put_user_ret(default_grn[i], (arg + 1), -EFAULT);
+ __put_user_ret(default_blu[i], (arg + 2), -EFAULT);
+ }
+ arg += 3;
+ }
+
+ if (set) {
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ if (vc_cons_allocated(i)) {
+ int j, k;
+
+ for (j = k = 0; j < 16; j++) {
+ vc_cons[i].d->vc_palette[k++] =
+ default_red[i];
+ vc_cons[i].d->vc_palette[k++] =
+ default_grn[i];
+ vc_cons[i].d->vc_palette[k++] =
+ default_blu[i];
+ }
+ }
+ }
+ pci_set_palette();
+ }
+ return -EINVAL;
+}
+
+static void pci_clear_screen(void)
+{
+ fbinfo_t *fb = &fbinfo[0];
+
+ if (fb->base)
+ memset((void *)fb->base,
+ (fb->type.fb_depth == 1) ?
+ ~(0) : reverse_color_table[0],
+ (fb->type.fb_depth * fb->type.fb_height
+ * fb->type.fb_width) / 8);
+ memset((char *)video_mem_base, 0, (video_mem_term - video_mem_base));
+}
+
+static void pci_clear_fb(int n)
+{
+ fbinfo_t *fb = &fbinfo[n];
+
+ if (!n) {
+ pci_clear_screen();
+ } else if (fb->base) {
+ memset((void *)fb->base,
+ (fb->type.fb_depth == 1) ?
+ ~(0) : reverse_color_table[0],
+ (fb->type.fb_depth * fb->type.fb_height
+ * fb->type.fb_width) / 8);
+ }
+}
+
+static void pci_render_screen(void)
+{
+ int count;
+ unsigned short *p;
+
+ count = video_num_columns * video_num_lines;
+ p = (unsigned short *)video_mem_base;
+
+ for (; count--; p++)
+ pci_blitc(*p, (unsigned long)p);
+}
+
+static void pci_clear_margin(void)
+{
+ fbinfo_t *fb = &fbinfo[0];
+ unsigned long p;
+ int h, he;
+
+ memset((void *)fb->base,
+ (fb->type.fb_depth == 1) ? ~(0) : reverse_color_table[0],
+ skip_bytes - (x_margin << 1));
+ memset((void *)(fb->base + fb->linebytes * fb->type.fb_height
+ - skip_bytes + (x_margin << 1)),
+ (fb->type.fb_depth == 1) ? ~(0) : reverse_color_table[0],
+ skip_bytes - (x_margin << 1));
+ he = fb->type.fb_height - 2 * y_margin;
+ if (fb->type.fb_depth == 1) {
+ for (p = fb->base + skip_bytes - (x_margin << 1), h = 0;
+ h < he; p += fb->linebytes, h++)
+ memset((void *)p, ~(0), (x_margin << 1));
+ } else {
+ for (p = fb->base + skip_bytes - (x_margin << 1), h = 0;
+ h < he; p += fb->linebytes, h++)
+ memset((void *)p, reverse_color_table[0],
+ (x_margin << 1));
+ }
+
+ if (fb->switch_from_graph)
+ fb->switch_from_graph();
+}
+
+static unsigned long
+pci_postsetup(fbinfo_t *fb, unsigned long memory_start)
+{
+ fb->color_map = (char *)memory_start;
+ pci_set_palette();
+ return memory_start + fb->type.fb_cmsize * 3;
+}
+
+__initfunc(static unsigned long
+pci_con_type_init(unsigned long kmem_start, const char **display_desc))
+{
+ can_do_color = 1;
+
+ video_type = VIDEO_TYPE_SUNPCI;
+ *display_desc = "SUNPCI";
+
+ if(!serial_console) {
+ /* If we fall back to PROM then our output
+ * have to remain readable.
+ */
+ prom_putchar('\033');
+ prom_putchar('[');
+ prom_putchar('H');
+
+ /*
+ * Fake the screen memory with some CPU memory
+ */
+ video_mem_base = kmem_start;
+ kmem_start += video_screen_size;
+ video_mem_term = kmem_start;
+ }
+
+ return kmem_start;
+}
+
+__initfunc(static void pci_con_type_init_finish(void))
+{
+ fbinfo_t *fb = &fbinfo[0];
+ unsigned char *p = (unsigned char *)fb->base + skip_bytes;
+ char q[2] = { 0, 5 };
+ unsigned short *ush;
+ int currcons = 0;
+ int cpu, ncpus;
+ int i;
+
+ if (serial_console)
+ return;
+
+ ncpus = linux_num_cpus;
+ if (ncpus > 4)
+ ncpus = 4;
+
+#if 0
+ if (fb->draw_penguin)
+ fb->draw_penguin(x_margin, y_margin, ncpus);
+ else
+#endif
+ if (fb->type.fb_depth == 8 && fb->loadcmap) {
+ for (i = 0; i < linux_logo_colors; i++) {
+ fb->color_map CM(i + 32, 0) = linux_logo_red[i];
+ fb->color_map CM(i + 32, 1) = linux_logo_green[i];
+ fb->color_map CM(i + 32, 2) = linux_logo_blue[i];
+ }
+ fb->loadcmap(fb, 32, linux_logo_colors);
+ for (i = 0; i < 80; i++, p += fb->linebytes) {
+ for (cpu = 0; cpu < ncpus; cpu++)
+ memcpy(p + (cpu * 88), linux_logo + 80 * i, 80);
+ }
+ } else if (fb->type.fb_depth == 1) {
+ for (i = 0; i < 80; i++, p += fb->linebytes) {
+ for (cpu = 0; cpu < ncpus; cpu++)
+ memcpy(p + (cpu * 11),
+ linux_logo_bw + 10 * i, 10);
+ }
+ }
+ putconsxy(0, q);
+
+ ush = (unsigned short *)video_mem_base + video_num_columns * 2 + 20
+ + 10 * (ncpus - 1);
+
+ for (p = logo_banner; *p; p++, ush++) {
+ *ush = (attr << 8) + *p;
+ pci_blitc(*ush, (unsigned long)ush);
+ }
+
+ for (i = 0; i < 5; i++) {
+ ush = (unsigned short *)video_mem_base + i * video_num_columns;
+ memset(ush, 0xff, 20);
+ }
+}
+
+unsigned long pcivga_iobase = 0;
+unsigned long pcivga_membase = 0;
+
+static struct {
+ int depth;
+ int resx, resy;
+ int x_margin, y_margin;
+} scr_def[] = {
+ { 8, 1152, 900, 64, 18 },
+ { 8, 1024, 768, 0, 0 },
+ { 0 }
+};
+
+extern int mach64_init(fbinfo_t *fb);
+
+__initfunc(int pci_console_probe(void))
+{
+ fbinfo_t *fb = &fbinfo[0];
+ char *p;
+ int i;
+
+ if (1
+#if 1
+ && mach64_init(fb)
+#endif
+ && 1) {
+ return -ENODEV;
+ }
+ fbinfos++;
+
+ fb->clear_fb = pci_clear_fb;
+ fb->set_other_palette = pci_set_other_palette;
+ fb->postsetup = pci_postsetup;
+ fb->blanked = 0;
+
+ fb->type.fb_height = prom_getintdefault(fb->prom_node, "height", 900);
+ fb->type.fb_width = prom_getintdefault(fb->prom_node, "width", 1152);
+ fb->type.fb_depth = prom_getintdefault(fb->prom_node, "depth", 8);
+ fb->linebytes = prom_getintdefault(fb->prom_node, "linebytes", 1152);
+ fb->type.fb_size = PAGE_ALIGN(fb->linebytes * fb->type.fb_height);
+
+ fb->proc_entry.rdev = MKDEV(GRAPHDEV_MAJOR, 0);
+ fb->proc_entry.mode = S_IFCHR | S_IRUSR | S_IWUSR;
+ prom_getname(fb->prom_node, fb->proc_entry.name, 32 - 3);
+ p = strchr(fb->proc_entry.name, 0);
+ sprintf(p, ":%d", 0);
+
+ for (i = 0; scr_def[i].depth; i++) {
+ if ((scr_def[i].resx != fb->type.fb_width) ||
+ (scr_def[i].resy != fb->type.fb_height) ||
+ (scr_def[i].depth != fb->type.fb_depth))
+ continue;
+ x_margin = scr_def[i].x_margin;
+ y_margin = scr_def[i].y_margin;
+ skip_bytes = y_margin * fb->linebytes + x_margin;
+ switch (fb->type.fb_width) {
+ case 1152:
+ fbuf_offset = color_fbuf_offset_1152_128;
+ break;
+ case 1024:
+ fbuf_offset = color_fbuf_offset_1024_128;
+ break;
+ default:
+ prom_printf("can't handle console width %d\n",
+ fb->type.fb_width);
+ prom_halt();
+ }
+ }
+
+ pci_install_consops();
+ return fb_init();
+}
+
+__initfunc(void pci_console_inithook(void))
+{
+ extern char *console_fb_path;
+ char prop[16];
+ int node = 0;
+ int width, height, depth, linebytes;
+ int x_margin, y_margin;
+ int i, len;
+
+ if (console_fb_path) {
+ char *p;
+ for (p = console_fb_path; *p && *p != ' '; p++) ;
+ *p = 0;
+ node = prom_pathtoinode(console_fb_path);
+ }
+ if (!node) {
+ node = prom_inst2pkg(prom_stdout);
+ if (!node) {
+ prom_printf("can't find output-device node\n");
+ prom_halt();
+ }
+ len = prom_getproperty(node, "device_type", prop, sizeof(prop));
+ if (len < 0) {
+ prom_printf("output-device doesn't have"
+ " device_type property\n");
+ prom_halt();
+ }
+ if (len != sizeof("display") ||
+ strncmp("display", prop, sizeof("display"))) {
+ prom_printf("output-device is %s"
+ " not \"display\"\n", prop);
+ prom_halt();
+ }
+ }
+
+ depth = prom_getintdefault(node, "depth", 8);
+ width = prom_getintdefault(node, "width", 1152);
+ height = prom_getintdefault(node, "height", 900);
+ linebytes = prom_getintdefault(node, "linebytes", 1152);
+
+ for (i = 0; scr_def[i].depth; i++) {
+ if ((scr_def[i].resx != width) ||
+ (scr_def[i].resy != height) ||
+ (scr_def[i].depth != depth))
+ continue;
+ x_margin = scr_def[i].x_margin;
+ y_margin = scr_def[i].y_margin;
+
+ ORIG_VIDEO_COLS = width / 8 - 2 * x_margin / depth;
+ ORIG_VIDEO_LINES = (height - 2 * y_margin) / 16;
+ }
+
+ suncons_ops.con_type_init = pci_con_type_init;
+}
+
+__initfunc(static void pci_install_consops(void))
+{
+ suncons_ops.memsetw = pci_memsetw;
+ suncons_ops.memcpyw = pci_memcpyw;
+ suncons_ops.scr_writew = pci_scr_writew;
+ suncons_ops.scr_readw = pci_scr_readw;
+
+ suncons_ops.get_scrmem = pci_get_scrmem;
+ suncons_ops.set_scrmem = pci_set_scrmem;
+
+ suncons_ops.hide_cursor = pci_hide_cursor;
+ suncons_ops.set_cursor = pci_set_cursor;
+ suncons_ops.set_get_font = pci_set_get_font;
+ suncons_ops.con_adjust_height = pci_con_adjust_height;
+ suncons_ops.set_get_cmap = pci_set_get_cmap;
+ suncons_ops.set_palette = pci_set_palette;
+ suncons_ops.set_other_palette = pci_set_other_palette;
+ suncons_ops.console_restore_palette = pci_restore_palette;
+
+ suncons_ops.con_type_init = pci_con_type_init;
+ suncons_ops.con_type_init_finish = pci_con_type_init_finish;
+
+ suncons_ops.clear_screen = pci_clear_screen;
+ suncons_ops.render_screen = pci_render_screen;
+ suncons_ops.clear_margin = pci_clear_margin;
+}
+
+#endif /* CONFIG_PCI */
diff --git a/drivers/sbus/char/pcicons.h b/drivers/sbus/char/pcicons.h
new file mode 100644
index 000000000..7a351a247
--- /dev/null
+++ b/drivers/sbus/char/pcicons.h
@@ -0,0 +1,79 @@
+/* $Id: pcicons.h,v 1.2 1997/08/24 12:13:11 ecd Exp $
+ * pcicons.h: Stuff which is generic across all PCI console drivers.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef PCICONS_H
+#define PCICONS_H
+
+#include <linux/pci.h>
+#include <asm/asi.h>
+#include <asm/io.h>
+
+extern unsigned long pcivga_iobase;
+extern unsigned long pcivga_membase;
+
+extern unsigned char vga_font[8192];
+
+extern __inline__ unsigned int pcivga_inb(unsigned long off)
+{
+ return inb(pcivga_iobase + off);
+}
+
+extern __inline__ unsigned int pcivga_inw(unsigned long off)
+{
+ return inw(pcivga_iobase + off);
+}
+
+extern __inline__ unsigned int pcivga_inl(unsigned long off)
+{
+ return inl(pcivga_iobase + off);
+}
+
+extern __inline__ void pcivga_outb(unsigned char val, unsigned long off)
+{
+ outb(val, pcivga_iobase + off);
+}
+
+extern __inline__ void pcivga_outw(unsigned short val, unsigned long off)
+{
+ outw(val, pcivga_iobase + off);
+}
+
+extern __inline__ void pcivga_outl(unsigned int val, unsigned long off)
+{
+ outl(val, pcivga_iobase + off);
+}
+
+extern __inline__ unsigned int pcivga_readb(unsigned long off)
+{
+ return readb(pcivga_membase + off);
+}
+
+extern __inline__ unsigned int pcivga_readw(unsigned long off)
+{
+ return readw(pcivga_membase + off);
+}
+
+extern __inline__ unsigned int pcivga_readl(unsigned long off)
+{
+ return readl(pcivga_membase + off);
+}
+
+extern __inline__ void pcivga_writeb(unsigned char val, unsigned long off)
+{
+ writeb(val, pcivga_membase + off);
+}
+
+extern __inline__ void pcivga_writew(unsigned short val, unsigned long off)
+{
+ writew(val, pcivga_membase + off);
+}
+
+extern __inline__ void pcivga_writel(unsigned int val, unsigned long off)
+{
+ writel(val, pcivga_membase + off);
+}
+
+#endif /* PCICONS_H */
diff --git a/drivers/sbus/char/pcikbd.c b/drivers/sbus/char/pcikbd.c
new file mode 100644
index 000000000..86f527027
--- /dev/null
+++ b/drivers/sbus/char/pcikbd.c
@@ -0,0 +1,964 @@
+/* $Id: pcikbd.c,v 1.4 1997/09/05 22:59:53 ecd Exp $
+ * pcikbd.c: Ultra/AX PC keyboard support.
+ *
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
+ *
+ * This code is mainly put together from various places in
+ * drivers/char, please refer to these sources for credits
+ * to the original authors.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/poll.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <linux/miscdevice.h>
+#include <linux/kbd_ll.h>
+#include <linux/init.h>
+
+#include <asm/ebus.h>
+#include <asm/oplib.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/keyboard.h>
+
+#include "pcikbd.h"
+#include "sunserial.h"
+
+static int kbd_node;
+static int beep_node;
+
+static unsigned long pcikbd_iobase = 0;
+static unsigned int pcikbd_irq;
+
+/* used only by send_data - set by keyboard_interrupt */
+static volatile unsigned char reply_expected = 0;
+static volatile unsigned char acknowledge = 0;
+static volatile unsigned char resend = 0;
+
+static inline void kb_wait(void)
+{
+ unsigned long start = jiffies;
+
+ do {
+ if(!(inb(pcikbd_iobase + KBD_STATUS_REG) & KBD_STAT_IBF))
+ return;
+ } while (jiffies - start < KBC_TIMEOUT);
+}
+
+/*
+ * Translation of escaped scancodes to keycodes.
+ * This is now user-settable.
+ * The keycodes 1-88,96-111,119 are fairly standard, and
+ * should probably not be changed - changing might confuse X.
+ * X also interprets scancode 0x5d (KEY_Begin).
+ *
+ * For 1-88 keycode equals scancode.
+ */
+
+#define E0_KPENTER 96
+#define E0_RCTRL 97
+#define E0_KPSLASH 98
+#define E0_PRSCR 99
+#define E0_RALT 100
+#define E0_BREAK 101 /* (control-pause) */
+#define E0_HOME 102
+#define E0_UP 103
+#define E0_PGUP 104
+#define E0_LEFT 105
+#define E0_RIGHT 106
+#define E0_END 107
+#define E0_DOWN 108
+#define E0_PGDN 109
+#define E0_INS 110
+#define E0_DEL 111
+
+#define E1_PAUSE 119
+
+/*
+ * The keycodes below are randomly located in 89-95,112-118,120-127.
+ * They could be thrown away (and all occurrences below replaced by 0),
+ * but that would force many users to use the `setkeycodes' utility, where
+ * they needed not before. It does not matter that there are duplicates, as
+ * long as no duplication occurs for any single keyboard.
+ */
+#define SC_LIM 89
+
+#define FOCUS_PF1 85 /* actual code! */
+#define FOCUS_PF2 89
+#define FOCUS_PF3 90
+#define FOCUS_PF4 91
+#define FOCUS_PF5 92
+#define FOCUS_PF6 93
+#define FOCUS_PF7 94
+#define FOCUS_PF8 95
+#define FOCUS_PF9 120
+#define FOCUS_PF10 121
+#define FOCUS_PF11 122
+#define FOCUS_PF12 123
+
+#define JAP_86 124
+/* tfj@olivia.ping.dk:
+ * The four keys are located over the numeric keypad, and are
+ * labelled A1-A4. It's an rc930 keyboard, from
+ * Regnecentralen/RC International, Now ICL.
+ * Scancodes: 59, 5a, 5b, 5c.
+ */
+#define RGN1 124
+#define RGN2 125
+#define RGN3 126
+#define RGN4 127
+
+static unsigned char high_keys[128 - SC_LIM] = {
+ RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */
+ 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */
+ 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */
+ FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */
+ FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */
+};
+
+/* BTC */
+#define E0_MACRO 112
+/* LK450 */
+#define E0_F13 113
+#define E0_F14 114
+#define E0_HELP 115
+#define E0_DO 116
+#define E0_F17 117
+#define E0_KPMINPLUS 118
+/*
+ * My OmniKey generates e0 4c for the "OMNI" key and the
+ * right alt key does nada. [kkoller@nyx10.cs.du.edu]
+ */
+#define E0_OK 124
+/*
+ * New microsoft keyboard is rumoured to have
+ * e0 5b (left window button), e0 5c (right window button),
+ * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU]
+ * [or: Windows_L, Windows_R, TaskMan]
+ */
+#define E0_MSLW 125
+#define E0_MSRW 126
+#define E0_MSTM 127
+
+static unsigned char e0_keys[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */
+ 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */
+ 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */
+ E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */
+ E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */
+ E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */
+ E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */
+ 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */
+ 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */
+ 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */
+};
+
+static unsigned int prev_scancode = 0;
+
+int pcikbd_setkeycode(unsigned int scancode, unsigned int keycode)
+{
+ if(scancode < SC_LIM || scancode > 255 || keycode > 127)
+ return -EINVAL;
+ if(scancode < 128)
+ high_keys[scancode - SC_LIM] = keycode;
+ else
+ e0_keys[scancode - 128] = keycode;
+ return 0;
+}
+
+int pcikbd_getkeycode(unsigned int scancode)
+{
+ return
+ (scancode < SC_LIM || scancode > 255) ? -EINVAL :
+ (scancode < 128) ? high_keys[scancode - SC_LIM] :
+ e0_keys[scancode - 128];
+}
+
+int do_acknowledge(unsigned char scancode)
+{
+ if(reply_expected) {
+ if(scancode == KBD_REPLY_ACK) {
+ acknowledge = 1;
+ reply_expected = 0;
+ return 0;
+ } else if(scancode == KBD_REPLY_RESEND) {
+ resend = 1;
+ reply_expected = 0;
+ return 0;
+ }
+ }
+ if(scancode == 0) {
+ prev_scancode = 0;
+ return 0;
+ }
+ return 1;
+}
+
+int pcikbd_pretranslate(unsigned char scancode, char raw_mode)
+{
+ if(scancode == 0xff) {
+ prev_scancode = 0;
+ return 0;
+ }
+ if(scancode == 0xe0 || scancode == 0xe1) {
+ prev_scancode = scancode;
+ return 0;
+ }
+ return 1;
+}
+
+int pcikbd_translate(unsigned char scancode, unsigned char *keycode,
+ char raw_mode)
+{
+ if(prev_scancode) {
+ if(prev_scancode != 0xe0) {
+ if(prev_scancode == 0xe1 && scancode == 0x1d) {
+ prev_scancode = 0x100;
+ return 0;
+ } else if(prev_scancode == 0x100 && scancode == 0x45) {
+ *keycode = E1_PAUSE;
+ prev_scancode = 0;
+ } else {
+ prev_scancode = 0;
+ return 0;
+ }
+ } else {
+ prev_scancode = 0;
+ if(scancode == 0x2a || scancode == 0x36)
+ return 0;
+ if(e0_keys[scancode])
+ *keycode = e0_keys[scancode];
+ else
+ return 0;
+ }
+ } else if(scancode >= SC_LIM) {
+ *keycode = high_keys[scancode - SC_LIM];
+ if(!*keycode)
+ return 0;
+
+ } else
+ *keycode = scancode;
+ return 1;
+}
+
+char pcikbd_unexpected_up(unsigned char keycode)
+{
+ if(keycode >= SC_LIM || keycode == 85)
+ return 0;
+ else
+ return 0200;
+}
+
+static void
+pcikbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned char status;
+
+ /*
+ * This IRQ might be shared with the 16550A serial chip,
+ * so we check dev_id to see if it was for us.
+ * (See also drivers/sbus/char/su.c).
+ */
+ if (dev_id)
+ return;
+
+ /* kbd_pt_regs = regs; */
+ status = inb(pcikbd_iobase + KBD_STATUS_REG);
+ do {
+ unsigned char scancode;
+
+ if(status & kbd_read_mask & KBD_STAT_MOUSE_OBF)
+ break;
+ scancode = inb(pcikbd_iobase + KBD_DATA_REG);
+ if((status & KBD_STAT_OBF) && do_acknowledge(scancode))
+ /* handle_scancode(scancode) */;
+ status = inb(pcikbd_iobase + KBD_STATUS_REG);
+ } while(status & KBD_STAT_OBF);
+ mark_bh(KEYBOARD_BH);
+}
+
+static int send_data(unsigned char data)
+{
+ int retries = 3;
+ unsigned long start;
+
+ do {
+ kb_wait();
+ acknowledge = resend = 0;
+ reply_expected = 1;
+ outb(data, pcikbd_iobase + KBD_DATA_REG);
+ start = jiffies;
+ do {
+ if(acknowledge)
+ return 1;
+ if(jiffies - start >= KBD_TIMEOUT)
+ return 0;
+ } while(!resend);
+ } while(retries-- > 0);
+ return 0;
+}
+
+void pcikbd_leds(unsigned char leds)
+{
+ if(!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))
+ send_data(KBD_CMD_ENABLE);
+
+}
+
+__initfunc(static int pcikbd_wait_for_input(void))
+{
+ int status, data;
+ unsigned long start = jiffies;
+
+ do {
+ status = inb(pcikbd_iobase + KBD_STATUS_REG);
+ if(!(status & KBD_STAT_OBF))
+ continue;
+ data = inb(pcikbd_iobase + KBD_DATA_REG);
+ if(status & (KBD_STAT_GTO | KBD_STAT_PERR))
+ continue;
+ return (data & 0xff);
+ } while(jiffies - start < KBD_INIT_TIMEOUT);
+ return -1;
+}
+
+__initfunc(static void pcikbd_write(int address, int data))
+{
+ int status;
+
+ do {
+ status = inb(pcikbd_iobase + KBD_STATUS_REG);
+ } while (status & KBD_STAT_IBF);
+ outb(data, pcikbd_iobase + address);
+}
+
+__initfunc(static char *do_pcikbd_hwinit(void))
+{
+ while(pcikbd_wait_for_input() != -1)
+ ;
+
+ pcikbd_write(KBD_CNTL_REG, KBD_CCMD_SELF_TEST);
+ if(pcikbd_wait_for_input() != 0xff)
+ return "Keyboard failed self test";
+
+ pcikbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_TEST);
+ if(pcikbd_wait_for_input() != 0x00)
+ return "Keyboard interface failed self test";
+
+ pcikbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_ENABLE);
+ pcikbd_write(KBD_DATA_REG, KBD_CMD_RESET);
+ if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+ return "Keyboard reset failed, no ACK";
+ if(pcikbd_wait_for_input() != KBD_REPLY_POR)
+ return "Keyboard reset failed, no ACK";
+
+ pcikbd_write(KBD_DATA_REG, KBD_CMD_DISABLE);
+ if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+ return "Disable keyboard: no ACK";
+
+ pcikbd_write(KBD_CNTL_REG, KBD_CCMD_WRITE_MODE);
+ pcikbd_write(KBD_DATA_REG,
+ (KBD_MODE_KBD_INT | KBD_MODE_SYS |
+ KBD_MODE_DISABLE_MOUSE | KBD_MODE_KCC));
+ pcikbd_write(KBD_DATA_REG, KBD_CMD_ENABLE);
+ if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+ return "Enable keyboard: no ACK";
+
+ pcikbd_write(KBD_DATA_REG, KBD_CMD_SET_RATE);
+ if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+ return "Set rate: no ACK";
+ pcikbd_write(KBD_DATA_REG, 0x00);
+ if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+ return "Set rate: no ACK";
+
+ return NULL; /* success */
+}
+
+__initfunc(void pcikbd_hwinit(void))
+{
+ char *msg;
+
+ disable_irq(pcikbd_irq);
+ msg = do_pcikbd_hwinit();
+ enable_irq(pcikbd_irq);
+
+ if(msg)
+ printk("8042: keyboard init failure [%s]\n", msg);
+}
+
+__initfunc(int pcikbd_probe(void))
+{
+ struct linux_ebus *ebus;
+ struct linux_ebus_device *edev;
+ struct linux_ebus_child *child;
+
+ for_all_ebusdev(edev, ebus) {
+ if(!strcmp(edev->prom_name, "8042")) {
+ for_each_edevchild(edev, child) {
+ if (!strcmp(child->prom_name, "kb_ps2"))
+ goto found;
+ }
+ }
+ }
+ printk("pcikbd_probe: no 8042 found\n");
+ return -ENODEV;
+
+found:
+ pcikbd_iobase = child->base_address[0];
+ if (check_region(pcikbd_iobase, sizeof(unsigned long))) {
+ printk("8042: can't get region %lx, %d\n",
+ pcikbd_iobase, (int)sizeof(unsigned long));
+ return -ENODEV;
+ }
+ request_region(pcikbd_iobase, sizeof(unsigned long), "8042 controller");
+
+ pcikbd_irq = child->irqs[0];
+ if (request_irq(pcikbd_irq, &pcikbd_interrupt,
+ SA_SHIRQ, "keyboard", NULL)) {
+ printk("8042: cannot register IRQ %x\n", pcikbd_irq);
+ return -ENODEV;
+ }
+
+ printk("8042(kbd): iobase[%016lx] irq[%x]\n", pcikbd_iobase, pcikbd_irq);
+
+ /* pcikbd_init(); */
+ kbd_read_mask = KBD_STAT_OBF;
+ return 0;
+}
+
+
+
+/*
+ * Here begins the Mouse Driver.
+ */
+
+static int ms_node;
+
+static unsigned long pcimouse_iobase = 0;
+static unsigned int pcimouse_irq;
+
+#define PSMOUSE_MINOR 1 /* Minor device # for this mouse */
+
+#define AUX_BUF_SIZE 2048
+
+struct aux_queue {
+ unsigned long head;
+ unsigned long tail;
+ struct wait_queue *proc_list;
+ struct fasync_struct *fasync;
+ unsigned char buf[AUX_BUF_SIZE];
+};
+
+static struct aux_queue *queue;
+static int aux_ready = 0;
+static int aux_count = 0;
+static int aux_present = 0;
+
+/*
+ * Shared subroutines
+ */
+
+static unsigned int get_from_queue(void)
+{
+ unsigned int result;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ result = queue->buf[queue->tail];
+ queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
+ restore_flags(flags);
+ return result;
+}
+
+
+static inline int queue_empty(void)
+{
+ return queue->head == queue->tail;
+}
+
+static int fasync_aux(struct inode *inode, struct file *filp, int on)
+{
+ int retval;
+
+ retval = fasync_helper(inode, filp, on, &queue->fasync);
+ if (retval < 0)
+ return retval;
+ return 0;
+}
+
+/*
+ * PS/2 Aux Device
+ */
+
+#define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | \
+ KBD_MODE_SYS | KBD_MODE_KBD_INT)
+
+#define AUX_INTS_ON (KBD_MODE_KCC | KBD_MODE_SYS | \
+ KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT)
+
+#define MAX_RETRIES 60 /* some aux operations take long time*/
+
+/*
+ * Status polling
+ */
+
+static int poll_aux_status(void)
+{
+ int retries=0;
+
+ while ((inb(pcimouse_iobase + KBD_STATUS_REG) &
+ (KBD_STAT_IBF | KBD_STAT_OBF)) && retries < MAX_RETRIES) {
+ if ((inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF)
+ == AUX_STAT_OBF)
+ inb(pcimouse_iobase + KBD_DATA_REG);
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + (5*HZ + 99) / 100;
+ schedule();
+ retries++;
+ }
+ return (retries < MAX_RETRIES);
+}
+
+/*
+ * Write to aux device
+ */
+
+static void aux_write_dev(int val)
+{
+ poll_aux_status();
+ outb(KBD_CCMD_WRITE_MOUSE, pcimouse_iobase + KBD_CNTL_REG);/* Write magic cookie */
+ poll_aux_status();
+ outb_p(val, pcimouse_iobase + KBD_DATA_REG); /* Write data */
+}
+
+/*
+ * Write to device & handle returned ack
+ */
+
+__initfunc(static int aux_write_ack(int val))
+{
+ aux_write_dev(val);
+ poll_aux_status();
+
+ if ((inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF)
+ return (inb(pcimouse_iobase + KBD_DATA_REG));
+ return 0;
+}
+
+/*
+ * Write aux device command
+ */
+
+static void aux_write_cmd(int val)
+{
+ poll_aux_status();
+ outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG);
+ poll_aux_status();
+ outb(val, pcimouse_iobase + KBD_DATA_REG);
+}
+
+/*
+ * AUX handler critical section start and end.
+ *
+ * Only one process can be in the critical section and all keyboard sends are
+ * deferred as long as we're inside. This is necessary as we may sleep when
+ * waiting for the keyboard controller and other processes / BH's can
+ * preempt us. Please note that the input buffer must be flushed when
+ * aux_end_atomic() is called and the interrupt is no longer enabled as not
+ * doing so might cause the keyboard driver to ignore all incoming keystrokes.
+ */
+
+static struct semaphore aux_sema4 = MUTEX;
+
+static inline void aux_start_atomic(void)
+{
+ down(&aux_sema4);
+ disable_bh(KEYBOARD_BH);
+}
+
+static inline void aux_end_atomic(void)
+{
+ enable_bh(KEYBOARD_BH);
+ up(&aux_sema4);
+}
+
+/*
+ * Interrupt from the auxiliary device: a character
+ * is waiting in the keyboard/aux controller.
+ */
+
+void pcimouse_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int head = queue->head;
+ int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
+
+ /*
+ * This IRQ might be shared with the 16550A serial chip,
+ * so we check dev_id to see if it was for us.
+ * (See also drivers/sbus/char/su.c).
+ */
+ if (dev_id)
+ return;
+
+ if ((inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) != AUX_STAT_OBF)
+ return;
+
+ add_mouse_randomness(queue->buf[head] = inb(pcimouse_iobase + KBD_DATA_REG));
+ if (head != maxhead) {
+ head++;
+ head &= AUX_BUF_SIZE-1;
+ }
+ queue->head = head;
+ aux_ready = 1;
+ if (queue->fasync)
+ kill_fasync(queue->fasync, SIGIO);
+ wake_up_interruptible(&queue->proc_list);
+}
+
+static int release_aux(struct inode * inode, struct file * file)
+{
+ fasync_aux(inode, file, 0);
+ if (--aux_count)
+ return 0;
+ aux_start_atomic();
+
+ /* Disable controller ints */
+ aux_write_cmd(AUX_INTS_OFF);
+ poll_aux_status();
+
+ /* Disable Aux device */
+ outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG);
+ poll_aux_status();
+ aux_end_atomic();
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Install interrupt handler.
+ * Enable auxiliary device.
+ */
+
+static int open_aux(struct inode * inode, struct file * file)
+{
+ if (!aux_present)
+ return -ENODEV;
+ aux_start_atomic();
+ if (aux_count++) {
+ aux_end_atomic();
+ return 0;
+ }
+ if (!poll_aux_status()) { /* FIXME: Race condition */
+ aux_count--;
+ aux_end_atomic();
+ return -EBUSY;
+ }
+ queue->head = queue->tail = 0; /* Flush input queue */
+
+ MOD_INC_USE_COUNT;
+
+ poll_aux_status();
+ outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase+KBD_CNTL_REG); /* Enable Aux */
+ aux_write_dev(AUX_ENABLE_DEV); /* Enable aux device */
+ aux_write_cmd(AUX_INTS_ON); /* Enable controller ints */
+ poll_aux_status();
+ aux_end_atomic();
+
+ aux_ready = 0;
+ return 0;
+}
+
+/*
+ * Write to the aux device.
+ */
+
+static long write_aux(struct inode * inode, struct file * file,
+ const char * buffer, unsigned long count)
+{
+ int retval = 0;
+
+ if (count) {
+ int written = 0;
+
+ aux_start_atomic();
+ do {
+ char c;
+ if (!poll_aux_status())
+ break;
+ outb(KBD_CCMD_WRITE_MOUSE, pcimouse_iobase + KBD_CNTL_REG);
+ if (!poll_aux_status())
+ break;
+ get_user(c, buffer++);
+ outb(c, pcimouse_iobase + KBD_DATA_REG);
+ written++;
+ } while (--count);
+ aux_end_atomic();
+ retval = -EIO;
+ if (written) {
+ retval = written;
+ inode->i_mtime = CURRENT_TIME;
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * Generic part continues...
+ */
+
+/*
+ * Put bytes from input queue to buffer.
+ */
+
+static long read_aux(struct inode * inode, struct file * file,
+ char * buffer, unsigned long count)
+{
+ struct wait_queue wait = { current, NULL };
+ int i = count;
+ unsigned char c;
+
+ if (queue_empty()) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ add_wait_queue(&queue->proc_list, &wait);
+repeat:
+ current->state = TASK_INTERRUPTIBLE;
+ if (queue_empty() && !(current->signal & ~current->blocked)) {
+ schedule();
+ goto repeat;
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&queue->proc_list, &wait);
+ }
+ while (i > 0 && !queue_empty()) {
+ c = get_from_queue();
+ put_user(c, buffer++);
+ i--;
+ }
+ aux_ready = !queue_empty();
+ if (count-i) {
+ inode->i_atime = CURRENT_TIME;
+ return count-i;
+ }
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ return 0;
+}
+
+static unsigned int aux_poll(struct file *file, poll_table * wait)
+{
+ poll_wait(&queue->proc_list, wait);
+ if (aux_ready)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+struct file_operations psaux_fops = {
+ NULL, /* seek */
+ read_aux,
+ write_aux,
+ NULL, /* readdir */
+ aux_poll,
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ open_aux,
+ release_aux,
+ NULL,
+ fasync_aux,
+};
+
+static struct miscdevice psaux_mouse = {
+ PSMOUSE_MINOR, "ps2aux", &psaux_fops
+};
+
+__initfunc(int pcimouse_init(void))
+{
+ struct linux_ebus *ebus;
+ struct linux_ebus_device *edev;
+ struct linux_ebus_child *child;
+
+ for_all_ebusdev(edev, ebus) {
+ if(!strcmp(edev->prom_name, "8042")) {
+ for_each_edevchild(edev, child) {
+ if (!strcmp(child->prom_name, "kdmouse"))
+ goto found;
+ }
+ }
+ }
+ printk("pcimouse_init: no 8042 found\n");
+ return -ENODEV;
+
+found:
+ pcimouse_iobase = child->base_address[0];
+ /*
+ * Just in case the iobases for kbd/mouse ever differ...
+ */
+ if (!check_region(pcimouse_iobase, sizeof(unsigned long)))
+ request_region(pcimouse_iobase, sizeof(unsigned long),
+ "8042 controller");
+
+ pcimouse_irq = child->irqs[0];
+ if (request_irq(pcimouse_irq, &pcimouse_interrupt,
+ SA_SHIRQ, "mouse", NULL)) {
+ printk("8042: Cannot register IRQ %x\n", pcimouse_irq);
+ return -ENODEV;
+ }
+
+ printk("8042(mouse): iobase[%016lx] irq[%x]\n",
+ pcimouse_iobase, pcimouse_irq);
+
+ printk("8042: PS/2 auxiliary pointing device detected.\n");
+ aux_present = 1;
+ kbd_read_mask = AUX_STAT_OBF;
+
+ misc_register(&psaux_mouse);
+ queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
+ memset(queue, 0, sizeof(*queue));
+ queue->head = queue->tail = 0;
+ queue->proc_list = NULL;
+ aux_start_atomic();
+ outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase + KBD_CNTL_REG);
+ aux_write_ack(AUX_SET_SAMPLE);
+ aux_write_ack(100);
+ aux_write_ack(AUX_SET_RES);
+ aux_write_ack(3);
+ aux_write_ack(AUX_SET_SCALE21);
+ poll_aux_status();
+ outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG);
+ poll_aux_status();
+ outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG);
+ poll_aux_status();
+ outb(AUX_INTS_OFF, pcimouse_iobase + KBD_DATA_REG);
+ poll_aux_status();
+ aux_end_atomic();
+
+ return 0;
+}
+
+
+__initfunc(static int ps2_init(void))
+{
+ int err;
+
+ err = pcikbd_probe();
+ if (err)
+ return err;
+
+ err = pcimouse_init();
+ if (err)
+ return err;
+
+ return 0;
+}
+
+__initfunc(int ps2kbd_probe(unsigned long *memory_start))
+{
+ int pnode, enode, node, dnode;
+ int kbnode = 0, msnode = 0, bnode = 0;
+ int devices = 0;
+ char prop[128];
+ int len;
+
+ /*
+ * Get the nodes for keyboard and mouse from 'aliases'...
+ */
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(node, "aliases");
+ if (!node)
+ return -ENODEV;
+
+ len = prom_getproperty(node, "keyboard", prop, sizeof(prop));
+ if (len > 0)
+ kbnode = prom_pathtoinode(prop);
+ if (!kbnode)
+ return -ENODEV;
+
+ len = prom_getproperty(node, "mouse", prop, sizeof(prop));
+ if (len > 0)
+ msnode = prom_pathtoinode(prop);
+ if (!msnode)
+ return -ENODEV;
+
+ /*
+ * Find matching EBus nodes...
+ */
+ node = prom_getchild(prom_root_node);
+ pnode = prom_searchsiblings(node, "pci");
+
+ /*
+ * For each PCI bus...
+ */
+ while (pnode) {
+ enode = prom_getchild(pnode);
+ enode = prom_searchsiblings(enode, "ebus");
+
+ /*
+ * For each EBus on this PCI...
+ */
+ while (enode) {
+ node = prom_getchild(enode);
+ bnode = prom_searchsiblings(node, "beeper");
+
+ node = prom_getchild(enode);
+ node = prom_searchsiblings(node, "8042");
+
+ /*
+ * For each '8042' on this EBus...
+ */
+ while (node) {
+ /*
+ * Does it match?
+ */
+ dnode = prom_getchild(node);
+ dnode = prom_searchsiblings(dnode, "kb_ps2");
+ if (dnode == kbnode) {
+ kbd_node = kbnode;
+ beep_node = bnode;
+ ++devices;
+ }
+
+ dnode = prom_getchild(node);
+ dnode = prom_searchsiblings(dnode, "kdmouse");
+ if (dnode == msnode) {
+ ms_node = msnode;
+ ++devices;
+ }
+
+ /*
+ * Found everything we need?
+ */
+ if (devices == 2)
+ goto found;
+
+ node = prom_getsibling(node);
+ node = prom_searchsiblings(node, "8042");
+ }
+ enode = prom_getsibling(enode);
+ enode = prom_searchsiblings(enode, "ebus");
+ }
+ pnode = prom_getsibling(pnode);
+ pnode = prom_searchsiblings(pnode, "pci");
+ }
+ return -ENODEV;
+
+found:
+ sunserial_setinitfunc(memory_start, ps2_init);
+ return 0;
+}
diff --git a/drivers/sbus/char/pcikbd.h b/drivers/sbus/char/pcikbd.h
new file mode 100644
index 000000000..234a5e980
--- /dev/null
+++ b/drivers/sbus/char/pcikbd.h
@@ -0,0 +1,123 @@
+/* $Id: pcikbd.h,v 1.1 1997/08/24 02:53:25 davem Exp $
+ * pcikbd.h: PCI/PC 8042 keyboard/mouse driver stuff. Mostly snarfed
+ * from the existing driver by Martin Mares.
+ *
+ * Copyright (C) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ * Ultra/AX specific hacks are:
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef __PCIKBD_H
+#define __PCIKBD_H
+
+/*
+ * Configuration Switches
+ */
+
+#define KBD_REPORT_ERR /* Report keyboard errors */
+#define KBD_REPORT_UNKN /* Report unknown scan codes */
+#define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */
+
+#define KBD_INIT_TIMEOUT HZ /* Timeout for initializing the keyboard */
+#define KBC_TIMEOUT (HZ/4) /* Timeout for sending to keyboard controller */
+#define KBD_TIMEOUT (HZ/4) /* Timeout for keyboard command acknowledge */
+
+/*
+ * Internal variables of the driver
+ */
+
+extern unsigned char kbd_read_mask;
+extern unsigned char aux_device_present;
+
+extern unsigned long pcikbd_iobase;
+extern unsigned int pcikbd_irq;
+
+/*
+ * Keyboard Controller Registers
+ *
+ * NOTE: These are offsets from pcikbd_iobase, not absolute.
+ */
+
+#define KBD_STATUS_REG 0x04
+#define KBD_CNTL_REG KBD_STATUS_REG
+#define KBD_DATA_REG 0x00
+
+/*
+ * Keyboard Controller Commands
+ */
+
+#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
+#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
+#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
+#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
+#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
+#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
+#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
+#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
+#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
+#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
+#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
+
+/*
+ * Keyboard Commands
+ */
+
+#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
+#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
+#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
+#define KBD_CMD_DISABLE 0xF5 /* Disable scanning */
+#define KBD_CMD_RESET 0xFF /* Reset */
+
+/*
+ * Keyboard Replies
+ */
+
+#define KBD_REPLY_POR 0xAA /* Power on reset */
+#define KBD_REPLY_ACK 0xFA /* Command ACK */
+#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
+
+/*
+ * Status Register Bits
+ */
+
+#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
+#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
+#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
+#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
+#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
+#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
+#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
+#define KBD_STAT_PERR 0x80 /* Parity error */
+
+#define AUX_STAT_OBF (KBD_STAT_OBF | KBD_STAT_MOUSE_OBF)
+
+/*
+ * Controller Mode Register Bits
+ */
+
+#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generage IRQ1 */
+#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
+#define KBD_MODE_SYS 0x04 /* The system flag (?) */
+#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
+#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
+#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
+#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
+#define KBD_MODE_RFU 0x80
+
+/*
+ * Mouse Commands
+ */
+
+#define AUX_SET_RES 0xE8 /* Set resolution */
+#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
+#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
+#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
+#define AUX_SET_STREAM 0xEA /* Set stream mode */
+#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
+#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
+#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
+#define AUX_RESET 0xFF /* Reset aux device */
+
+#endif /* __PCIKBD_H */
diff --git a/drivers/sbus/char/sab82532.c b/drivers/sbus/char/sab82532.c
new file mode 100644
index 000000000..775fbbe5a
--- /dev/null
+++ b/drivers/sbus/char/sab82532.c
@@ -0,0 +1,2173 @@
+/* $Id: sab82532.c,v 1.4 1997/09/03 17:04:21 ecd Exp $
+ * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
+ *
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <asm/sab82532.h>
+#include <asm/uaccess.h>
+#include <asm/ebus.h>
+
+#include "sunserial.h"
+
+static DECLARE_TASK_QUEUE(tq_serial);
+
+static struct tty_driver serial_driver, callout_driver;
+static int sab82532_refcount;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Set of debugging defines */
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_WAIT_UNTIL_SENT
+
+static void change_speed(struct sab82532 *info);
+static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout);
+
+/*
+ * This assumes you have a 29.4912 MHz clock for your UART.
+ */
+#define BASE_BAUD ( 29491200 / 16 )
+
+static struct sab82532 *sab82532_chain = 0;
+static struct tty_struct *sab82532_table[NR_PORTS];
+static struct termios *sab82532_termios[NR_PORTS];
+static struct termios *sab82532_termios_locked[NR_PORTS];
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by sab82532_write. We need to
+ * lock it in case the copy_from_user blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int serial_paranoia_check(struct sab82532 *info,
+ kdev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+ static const char *badmagic =
+ "Warning: bad magic number for serial struct (%s) in %s\n";
+ static const char *badinfo =
+ "Warning: null sab82532 for (%s) in %s\n";
+
+ if (!info) {
+ printk(badinfo, kdevname(device), routine);
+ return 1;
+ }
+ if (info->magic != SERIAL_MAGIC) {
+ printk(badmagic, kdevname(device), routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds.
+ *
+ * The formula is: Baud = BASE_BAUD / ((N + 1) * (1 << M)),
+ *
+ * with 0 <= N < 64 and 0 <= M < 16
+ *
+ * XXX: Speeds with M = 0 might not work properly for XTAL frequencies
+ * above 10 MHz.
+ */
+struct ebrg_struct {
+ int baud;
+ int n;
+ int m;
+};
+
+static struct ebrg_struct ebrg_table[] = {
+ { 0, 0, 0 },
+ { 50, 35, 10 },
+ { 75, 47, 9 },
+ { 110, 32, 9 },
+ { 134, 53, 8 },
+ { 150, 47, 8 },
+ { 200, 35, 8 },
+ { 300, 47, 7 },
+ { 600, 47, 6 },
+ { 1200, 47, 5 },
+ { 1800, 31, 5 },
+ { 2400, 47, 4 },
+ { 4800, 47, 3 },
+ { 9600, 47, 2 },
+ { 19200, 47, 1 },
+ { 38400, 23, 1 },
+ { 57600, 15, 1 },
+ { 115200, 7, 1 },
+ { 230400, 3, 1 },
+ { 460800, 1, 1 },
+ { 76800, 11, 1 },
+ { 153600, 5, 1 },
+ { 307200, 3, 1 },
+ { 614400, 3, 0 },
+ { 921600, 0, 1 },
+};
+
+#define NR_EBRG_VALUES (sizeof(ebrg_table)/sizeof(struct ebrg_struct))
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_stop() and sab82532_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void sab82532_stop(struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_stop"))
+ return;
+
+ save_flags(flags); cli();
+ info->interrupt_mask1 |= SAB82532_IMR1_XPR;
+ info->regs->w.imr1 = info->interrupt_mask1;
+ restore_flags(flags);
+}
+
+static void sab82532_start(struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_start"))
+ return;
+
+ save_flags(flags); cli();
+ info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+ info->regs->w.imr1 = info->interrupt_mask1;
+ restore_flags(flags);
+}
+
+static void batten_down_hatches(void)
+{
+ /* If we are doing kadb, we call the debugger
+ * else we just drop into the boot monitor.
+ * Note that we must flush the user windows
+ * first before giving up control.
+ */
+ printk("\n");
+ flush_user_windows();
+#ifndef __sparc_v9__
+ if ((((unsigned long)linux_dbvec) >= DEBUG_FIRSTVADDR) &&
+ (((unsigned long)linux_dbvec) <= DEBUG_LASTVADDR))
+ sp_enter_debugger();
+ else
+#endif
+ prom_cmdline();
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines. All of the following
+ * subroutines are declared as inline and are folded into
+ * sab82532_interrupt(). They were separated out for readability's sake.
+ *
+ * Note: sab82532_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off. People who may want to modify
+ * sab82532_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.
+ *
+ * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static inline void sab82532_sched_event(struct sab82532 *info, int event)
+{
+ info->event |= 1 << event;
+ queue_task(&info->tqueue, &tq_serial);
+ mark_bh(SERIAL_BH);
+}
+
+static inline void receive_chars(struct sab82532 *info,
+ union sab82532_irq_status *stat)
+{
+ struct tty_struct *tty = info->tty;
+ unsigned char buf[32];
+ unsigned char status;
+ int free_fifo = 0;
+ int i, count = 0;
+
+ /* Read number of BYTES (Character + Status) available. */
+ if (stat->sreg.isr0 & SAB82532_ISR0_RPF) {
+ count = info->recv_fifo_size;
+ free_fifo++;
+ }
+ if (stat->sreg.isr0 & SAB82532_ISR0_TCD) {
+ count = info->regs->r.rbcl & (info->recv_fifo_size - 1);
+ free_fifo++;
+ }
+ if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
+#if 1
+ printk("sab82532: receive_chars: RFO");
+#endif
+ free_fifo++;
+ }
+
+ /* Issue a FIFO read command in case we where idle. */
+ if (stat->sreg.isr0 & SAB82532_ISR0_TIME) {
+ if (info->regs->r.star & SAB82532_STAR_CEC)
+ udelay(1);
+ info->regs->w.cmdr = SAB82532_CMDR_RFRD;
+ }
+
+ /* Read the FIFO. */
+ for (i = 0; i < (count << 1); i++)
+ buf[i] = info->regs->r.rfifo[i];
+
+ /* Issue Receive Message Complete command. */
+ if (free_fifo) {
+ if (info->regs->r.star & SAB82532_STAR_CEC)
+ udelay(1);
+ info->regs->w.cmdr = SAB82532_CMDR_RMC;
+ }
+
+ for (i = 0; i < count; ) {
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+#if 1
+ printk("sab82532: receive_chars: tty overrun\n");
+#endif
+ info->icount.buf_overrun++;
+ break;
+ }
+
+ tty->flip.count++;
+ *tty->flip.char_buf_ptr++ = buf[i++];
+ status = buf[i++];
+ info->icount.rx++;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("DR%02x:%02x...", (unsigned char)*(tty->flip.char_buf_ptr - 1), status);
+#endif
+
+ if (status & SAB82532_RSTAT_PE) {
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+ info->icount.parity++;
+ } else if (status & SAB82532_RSTAT_FE) {
+ *tty->flip.flag_buf_ptr++ = TTY_FRAME;
+ info->icount.frame++;
+ }
+#ifdef CMSPAR
+ else if (status & SAB82532_RSTAT_PARITY)
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+#endif
+ else
+ *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+ }
+
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static inline void transmit_chars(struct sab82532 *info,
+ union sab82532_irq_status *stat)
+{
+ int i;
+
+ if ((info->xmit_cnt <= 0) || info->tty->stopped ||
+ info->tty->hw_stopped) {
+ if (stat->sreg.isr1 & SAB82532_ISR1_ALLS)
+ info->all_sent = 1;
+ info->interrupt_mask1 |= SAB82532_IMR1_XPR;
+ info->regs->w.imr1 = info->interrupt_mask1;
+ return;
+ }
+
+ /* Stuff 32 bytes into Transmit FIFO. */
+ info->all_sent = 0;
+ for (i = 0; i < info->xmit_fifo_size; i++) {
+ info->regs->w.xfifo[i] = info->xmit_buf[info->xmit_tail++];
+ info->xmit_tail &= (SERIAL_XMIT_SIZE - 1);
+ info->icount.tx++;
+ if (--info->xmit_cnt <= 0)
+ break;
+ }
+
+ /* Issue a Transmit Frame command. */
+ if (info->regs->r.star & SAB82532_STAR_CEC)
+ udelay(1);
+ info->regs->w.cmdr = SAB82532_CMDR_XF;
+
+ if (info->xmit_cnt < WAKEUP_CHARS)
+ sab82532_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("THRE...");
+#endif
+ if (info->xmit_cnt <= 0) {
+ info->interrupt_mask1 |= SAB82532_IMR1_XPR;
+ info->regs->w.imr1 = info->interrupt_mask1;
+ }
+}
+
+static inline void check_status(struct sab82532 *info,
+ union sab82532_irq_status *stat)
+{
+ struct tty_struct *tty = info->tty;
+ int modem_change = 0;
+
+ if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
+ if (info->is_console) {
+ batten_down_hatches();
+ return;
+ }
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ info->icount.buf_overrun++;
+ goto check_modem;
+ }
+ tty->flip.count++;
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+ *tty->flip.char_buf_ptr++ = 0;
+ info->icount.brk++;
+ }
+
+ if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ info->icount.buf_overrun++;
+ goto check_modem;
+ }
+ tty->flip.count++;
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+ *tty->flip.char_buf_ptr++ = 0;
+ info->icount.overrun++;
+ }
+
+ if (info->is_console)
+ return;
+
+check_modem:
+ if (stat->sreg.isr0 & SAB82532_ISR0_CDSC) {
+ info->dcd = (info->regs->r.vstr & SAB82532_VSTR_CD) ? 0 : 1;
+ info->icount.dcd++;
+ modem_change++;
+#if 0
+ printk("DCD change: %d\n", info->icount.dcd);
+#endif
+ }
+ if (stat->sreg.isr1 & SAB82532_ISR1_CSC) {
+ info->cts = info->regs->r.star & SAB82532_STAR_CTS;
+ info->icount.cts++;
+ modem_change++;
+#if 0
+ printk("CTS change: %d, CTS %s\n", info->icount.cts, info->cts ? "on" : "off");
+#endif
+ }
+ if ((info->regs->r.pvr & info->pvr_dsr_bit) ^ info->dsr) {
+ info->dsr = info->regs->r.pvr & info->pvr_dsr_bit;
+ info->icount.dsr++;
+ modem_change++;
+#if 0
+ printk("DSR change: %d\n", info->icount.dsr);
+#endif
+ }
+ if (modem_change)
+ wake_up_interruptible(&info->delta_msr_wait);
+
+ if ((info->flags & ASYNC_CHECK_CD) &&
+ (stat->sreg.isr0 & SAB82532_ISR0_CDSC)) {
+
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+ printk("ttys%d CD now %s...", info->line,
+ (info->dcd) ? "on" : "off");
+#endif
+
+ if (info->dcd)
+ wake_up_interruptible(&info->open_wait);
+ else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_CALLOUT_NOHUP))) {
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("scheduling hangup...");
+#endif
+
+ queue_task(&info->tqueue_hangup, &tq_scheduler);
+ }
+ }
+
+ if (info->flags & ASYNC_CTS_FLOW) {
+ if (info->tty->hw_stopped) {
+ if (info->cts) {
+
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx start...");
+#endif
+ info->tty->hw_stopped = 0;
+ sab82532_sched_event(info,
+ RS_EVENT_WRITE_WAKEUP);
+ info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+ info->regs->w.imr1 = info->interrupt_mask1;
+ }
+ } else {
+ if (!(info->cts)) {
+
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx stop...");
+#endif
+ info->tty->hw_stopped = 1;
+ }
+ }
+ }
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+static void sab82532_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct sab82532 *info = dev_id;
+ union sab82532_irq_status status;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("sab82532_interrupt(%d)...", irq);
+#endif
+
+ status.stat = 0;
+ if (info->regs->r.gis & SAB82532_GIS_ISA0)
+ status.sreg.isr0 = info->regs->r.isr0;
+ if (info->regs->r.gis & SAB82532_GIS_ISA1)
+ status.sreg.isr1 = info->regs->r.isr1;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("%d<%02x.%02x>", info->line,
+ status.sreg.isr0, status.sreg.isr1);
+#endif
+
+ if (!status.stat)
+ goto next;
+
+ if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+ SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
+ receive_chars(info, &status);
+ if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
+ (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
+ check_status(info, &status);
+ if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
+ transmit_chars(info, &status);
+
+next:
+ info = info->next;
+ status.stat = 0;
+ if (info->regs->r.gis & SAB82532_GIS_ISB0)
+ status.sreg.isr0 = info->regs->r.isr0;
+ if (info->regs->r.gis & SAB82532_GIS_ISB1)
+ status.sreg.isr1 = info->regs->r.isr1;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("%d<%02x.%02x>", info->line,
+ status.sreg.isr0, status.sreg.isr1);
+#endif
+
+ if (!status.stat)
+ goto done;
+
+ if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+ SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
+ receive_chars(info, &status);
+ if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
+ (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
+ check_status(info, &status);
+ if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
+ transmit_chars(info, &status);
+
+done:
+#ifdef SERIAL_DEBUG_INTR
+ printk("end.\n");
+#endif
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * sab82532_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using sab82532_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+ run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+ struct sab82532 *info = (struct sab82532 *)private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+}
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred. The path of
+ * hangup processing is:
+ *
+ * serial interrupt routine -> (scheduler tqueue) ->
+ * do_serial_hangup() -> tty->hangup() -> sab82532_hangup()
+ *
+ */
+static void do_serial_hangup(void *private_)
+{
+ struct sab82532 *info = (struct sab82532 *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ tty_hangup(tty);
+}
+
+
+static int startup(struct sab82532 *info)
+{
+ unsigned long flags;
+ unsigned long page;
+ unsigned char stat;
+
+ page = get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ save_flags(flags); cli();
+
+ if (info->flags & ASYNC_INITIALIZED) {
+ free_page(page);
+ goto errout;
+ }
+
+ if (!info->regs) {
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+ free_page(page);
+ goto errout;
+ }
+ if (info->xmit_buf)
+ free_page(page);
+ else
+ info->xmit_buf = (unsigned char *)page;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("starting up serial port %d...", info->line);
+#endif
+
+ /*
+ * Clear the FIFO buffers.
+ */
+ if (info->regs->r.star & SAB82532_STAR_CEC)
+ udelay(1);
+ info->regs->w.cmdr = SAB82532_CMDR_RRES;
+ if (info->regs->r.star & SAB82532_STAR_CEC)
+ udelay(1);
+ info->regs->w.cmdr = SAB82532_CMDR_XRES;
+
+ /*
+ * Clear the interrupt registers.
+ */
+ stat = info->regs->r.isr0;
+ stat = info->regs->r.isr1;
+
+ /*
+ * Now, initialize the UART
+ */
+ info->regs->w.ccr0 = 0; /* power-down */
+ info->regs->w.ccr0 = SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ |
+ SAB82532_CCR0_SM_ASYNC;
+ info->regs->w.ccr1 = SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7;
+ info->regs->w.ccr2 = SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL |
+ SAB82532_CCR2_TOE;
+ info->regs->w.ccr3 = 0;
+ info->regs->w.ccr4 = SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG;
+ info->regs->w.mode = SAB82532_MODE_RTS | SAB82532_MODE_FCTS |
+ SAB82532_MODE_RAC;
+ info->regs->w.rfc = SAB82532_RFC_DPS | SAB82532_RFC_RFDF;
+ switch (info->recv_fifo_size) {
+ case 1:
+ info->regs->w.rfc |= SAB82532_RFC_RFTH_1;
+ break;
+ case 4:
+ info->regs->w.rfc |= SAB82532_RFC_RFTH_4;
+ break;
+ case 16:
+ info->regs->w.rfc |= SAB82532_RFC_RFTH_16;
+ break;
+ default:
+ info->recv_fifo_size = 32;
+ /* fall through */
+ case 32:
+ info->regs->w.rfc |= SAB82532_RFC_RFTH_32;
+ break;
+ }
+ info->regs->rw.ccr0 |= SAB82532_CCR0_PU; /* power-up */
+
+ /*
+ * Finally, enable interrupts
+ */
+ info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+ SAB82532_IMR0_PLLA;
+ info->regs->w.imr0 = info->interrupt_mask0;
+ info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_XOFF |
+ SAB82532_IMR1_TIN | SAB82532_IMR1_XON |
+ SAB82532_IMR1_XPR;
+ info->regs->w.imr1 = info->interrupt_mask1;
+
+ if (info->tty)
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+ /*
+ * and set the speed of the serial port
+ */
+ change_speed(info);
+
+ info->flags |= ASYNC_INITIALIZED;
+ restore_flags(flags);
+ return 0;
+
+errout:
+ restore_flags(flags);
+ return -ENODEV;
+}
+
+/*
+ * 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 sab82532 *info)
+{
+ unsigned long flags;
+
+ if (!(info->flags & ASYNC_INITIALIZED))
+ return;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Shutting down serial port %d...", info->line);
+#endif
+
+ save_flags(flags); cli(); /* Disable interrupts */
+
+ /*
+ * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+ * here so the queue might never be waken up
+ */
+ wake_up_interruptible(&info->delta_msr_wait);
+
+ if (info->xmit_buf) {
+ free_page((unsigned long)info->xmit_buf);
+ info->xmit_buf = 0;
+ }
+
+ /* Disable Interrupts */
+ info->interrupt_mask0 = 0xff;
+ info->regs->w.imr0 = info->interrupt_mask0;
+ info->interrupt_mask1 = 0xff;
+ info->regs->w.imr1 = info->interrupt_mask1;
+
+ if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+ info->regs->rw.mode |= SAB82532_MODE_FRTS;
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+ info->regs->rw.pvr |= info->pvr_dtr_bit;
+ }
+
+ /* Disable break condition */
+ info->regs->rw.dafo &= ~(SAB82532_DAFO_XBRK);
+
+ /* Disable Receiver */
+ info->regs->rw.mode &= ~(SAB82532_MODE_RAC);
+
+ /* Power Down */
+ info->regs->rw.ccr0 &= ~(SAB82532_CCR0_PU);
+
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ASYNC_INITIALIZED;
+ restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct sab82532 *info)
+{
+ unsigned long flags;
+ unsigned int ebrg;
+ tcflag_t cflag;
+ unsigned char dafo;
+ int i;
+
+ if (!info->tty || !info->tty->termios)
+ return;
+ cflag = info->tty->termios->c_cflag;
+
+ /* Byte size and parity */
+ switch (cflag & CSIZE) {
+ case CS5: dafo = SAB82532_DAFO_CHL5; break;
+ case CS6: dafo = SAB82532_DAFO_CHL6; break;
+ case CS7: dafo = SAB82532_DAFO_CHL7; break;
+ case CS8: dafo = SAB82532_DAFO_CHL8; break;
+ /* Never happens, but GCC is too dumb to figure it out */
+ default: dafo = SAB82532_DAFO_CHL5; break;
+ }
+
+ if (cflag & CSTOPB)
+ dafo |= SAB82532_DAFO_STOP;
+
+ if (cflag & PARENB)
+ dafo |= SAB82532_DAFO_PARE;
+
+ if (cflag & PARODD) {
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ dafo |= SAB82532_DAFO_PAR_MARK;
+ else
+#endif
+ dafo |= SAB82532_DAFO_PAR_ODD;
+ } else {
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ dafo |= SAB82532_DAFO_PAR_SPACE;
+ else
+#endif
+ dafo |= SAB82532_DAFO_PAR_EVEN;
+ }
+
+ /* Determine EBRG values based on baud rate */
+ i = cflag & CBAUD;
+ if (i & CBAUDEX) {
+ i &= ~(CBAUDEX);
+ if ((i < 1) || ((i + 15) >= NR_EBRG_VALUES))
+ info->tty->termios->c_cflag &= ~CBAUDEX;
+ else
+ i += 15;
+ }
+ ebrg = ebrg_table[i].n;
+ ebrg |= (ebrg_table[i].m << 6);
+
+ /* CTS flow control flags */
+ if (cflag & CRTSCTS)
+ info->flags |= ASYNC_CTS_FLOW;
+ else
+ info->flags &= ~(ASYNC_CTS_FLOW);
+
+ if (cflag & CLOCAL)
+ info->flags &= ~(ASYNC_CHECK_CD);
+ else
+ info->flags |= ASYNC_CHECK_CD;
+ if (info->tty)
+ info->tty->hw_stopped = 0;
+
+ /*
+ * Set up parity check flag
+ * XXX: not implemented, yet.
+ */
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+ /*
+ * Characters to ignore
+ * XXX: not implemented, yet.
+ */
+
+ /*
+ * !!! ignore all characters if CREAD is not set
+ * XXX: not implemented, yet.
+ */
+ if ((cflag & CREAD) == 0)
+ info->ignore_status_mask |= SAB82532_ISR0_RPF |
+ SAB82532_ISR0_TCD |
+ SAB82532_ISR0_TIME;
+
+ save_flags(flags); cli();
+ info->regs->w.dafo = dafo;
+ info->regs->w.bgr = ebrg & 0xff;
+ info->regs->rw.ccr2 &= ~(0xc0);
+ info->regs->rw.ccr2 |= (ebrg >> 2) & 0xc0;
+ if (info->flags & ASYNC_CTS_FLOW) {
+ info->regs->rw.mode &= ~(SAB82532_MODE_RTS);
+ info->regs->rw.mode |= SAB82532_MODE_FRTS;
+ info->regs->rw.mode &= ~(SAB82532_MODE_FCTS);
+ } else {
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+ info->regs->rw.mode &= ~(SAB82532_MODE_FRTS);
+ info->regs->rw.mode |= SAB82532_MODE_FCTS;
+ }
+ info->regs->rw.mode |= SAB82532_MODE_RAC;
+ restore_flags(flags);
+}
+
+static void sab82532_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_put_char"))
+ return;
+
+ if (!tty || !info->xmit_buf)
+ return;
+
+ save_flags(flags); cli();
+ if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+ restore_flags(flags);
+ return;
+ }
+
+ info->xmit_buf[info->xmit_head++] = ch;
+ info->xmit_head &= SERIAL_XMIT_SIZE-1;
+ info->xmit_cnt++;
+ restore_flags(flags);
+}
+
+static void sab82532_flush_chars(struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_flush_chars"))
+ return;
+
+ if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+ !info->xmit_buf)
+ return;
+
+ save_flags(flags); cli();
+ info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+ info->regs->w.imr1 = info->interrupt_mask1;
+ restore_flags(flags);
+}
+
+static int sab82532_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ int c, ret = 0;
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_write"))
+ return 0;
+
+ if (!tty || !info->xmit_buf || !tmp_buf)
+ return 0;
+
+ if (from_user)
+ down(&tmp_buf_sem);
+ save_flags(flags);
+ while (1) {
+ cli();
+ c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ if (c <= 0)
+ break;
+
+ if (from_user) {
+ c -= copy_from_user(tmp_buf, buf, c);
+ if (!c) {
+ if (!ret)
+ ret = -EFAULT;
+ break;
+ }
+ c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+ } else
+ memcpy(info->xmit_buf + info->xmit_head, buf, c);
+ info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt += c;
+ restore_flags(flags);
+ buf += c;
+ count -= c;
+ ret += c;
+ }
+ if (from_user)
+ up(&tmp_buf_sem);
+
+ if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+ (info->interrupt_mask1 & SAB82532_IMR1_XPR)) {
+ info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+ info->regs->w.imr1 = info->interrupt_mask1;
+ }
+
+ restore_flags(flags);
+ return ret;
+}
+
+static int sab82532_write_room(struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ int ret;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_write_room"))
+ return 0;
+ ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+ if (ret < 0)
+ ret = 0;
+ return ret;
+}
+
+static int sab82532_chars_in_buffer(struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_chars_in_buffer"))
+ return 0;
+ return info->xmit_cnt;
+}
+
+static void sab82532_flush_buffer(struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_flush_buffer"))
+ return;
+ cli();
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ sti();
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void sab82532_send_xchar(struct tty_struct *tty, char ch)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_send_xchar"))
+ return;
+
+ if (info->regs->r.star & SAB82532_STAR_TEC)
+ udelay(1);
+ info->regs->w.tic = ch;
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void sab82532_throttle(struct tty_struct * tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("throttle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_throttle"))
+ return;
+
+ if (I_IXOFF(tty))
+ sab82532_send_xchar(tty, STOP_CHAR(tty));
+#if 0
+ if (tty->termios->c_cflag & CRTSCTS)
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+#endif
+}
+
+static void sab82532_unthrottle(struct tty_struct * tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_unthrottle"))
+ return;
+
+ if (I_IXOFF(tty)) {
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ sab82532_send_xchar(tty, START_CHAR(tty));
+ }
+
+#if 0
+ if (tty->termios->c_cflag & CRTSCTS)
+ info->regs->rw.mode &= ~(SAB82532_MODE_RTS);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct sab82532 *info,
+ struct serial_struct *retinfo)
+{
+ struct serial_struct tmp;
+
+ if (!retinfo)
+ return -EFAULT;
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.type = info->type;
+ tmp.line = info->line;
+ tmp.port = (unsigned long)info->regs;
+ tmp.irq = info->irq;
+ tmp.flags = info->flags;
+ tmp.xmit_fifo_size = info->xmit_fifo_size;
+ tmp.baud_base = info->baud_base;
+ tmp.close_delay = info->close_delay;
+ tmp.closing_wait = info->closing_wait;
+ tmp.custom_divisor = info->custom_divisor;
+ tmp.hub6 = 0;
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+ return 0;
+}
+
+static int set_serial_info(struct sab82532 *info,
+ struct serial_struct *new_info)
+{
+ return 0;
+}
+
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows an RS485 driver to be written in user space.
+ */
+static int get_lsr_info(struct sab82532 * info, unsigned int *value)
+{
+ unsigned int result;
+
+ result = info->all_sent ? TIOCSER_TEMT : 0;
+ return put_user(result, value);
+}
+
+
+static int get_modem_info(struct sab82532 * info, unsigned int *value)
+{
+ unsigned int result;
+
+ result = ((info->regs->r.mode & SAB82532_MODE_FRTS) ? 0 : TIOCM_RTS)
+ | ((info->regs->r.pvr & info->pvr_dtr_bit) ? 0 : TIOCM_DTR)
+ | ((info->regs->r.vstr & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR)
+ | ((info->regs->r.pvr & info->pvr_dsr_bit) ? 0 : TIOCM_DSR)
+ | ((info->regs->r.star & SAB82532_STAR_CTS) ? TIOCM_CTS : 0);
+ return put_user(result,value);
+}
+
+static int set_modem_info(struct sab82532 * info, unsigned int cmd,
+ unsigned int *value)
+{
+ int error;
+ unsigned int arg;
+
+ error = get_user(arg, value);
+ if (error)
+ return error;
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS) {
+ info->regs->rw.mode &= ~(SAB82532_MODE_FRTS);
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+ }
+ if (arg & TIOCM_DTR) {
+ info->regs->rw.pvr &= ~(info->pvr_dtr_bit);
+ }
+ break;
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS) {
+ info->regs->rw.mode |= SAB82532_MODE_FRTS;
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+ }
+ if (arg & TIOCM_DTR) {
+ info->regs->rw.pvr |= info->pvr_dtr_bit;
+ }
+ break;
+ case TIOCMSET:
+ if (arg & TIOCM_RTS) {
+ info->regs->rw.mode &= ~(SAB82532_MODE_FRTS);
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+ } else {
+ info->regs->rw.mode |= SAB82532_MODE_FRTS;
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+ }
+ if (arg & TIOCM_DTR) {
+ info->regs->rw.pvr &= ~(info->pvr_dtr_bit);
+ } else {
+ info->regs->rw.pvr |= info->pvr_dtr_bit;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break( struct sab82532 * info, int duration)
+{
+ if (!info->regs)
+ return;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + duration;
+#ifdef SERIAL_DEBUG_SEND_BREAK
+ printk("sab82532_send_break(%d) jiff=%lu...", duration, jiffies);
+#endif
+ cli();
+ info->regs->rw.dafo |= SAB82532_DAFO_XBRK;
+ schedule();
+ info->regs->rw.dafo &= ~(SAB82532_DAFO_XBRK);
+ sti();
+#ifdef SERIAL_DEBUG_SEND_BREAK
+ printk("done jiffies=%lu\n", jiffies);
+#endif
+}
+
+/*
+ * This routine sets the break condition on the serial port.
+ */
+static void begin_break(struct sab82532 * info)
+{
+ if (!info->regs)
+ return;
+ info->regs->rw.dafo |= SAB82532_DAFO_XBRK;
+}
+
+/*
+ * This routine clears the break condition on the serial port.
+ */
+static void end_break(struct sab82532 * info)
+{
+ if (!info->regs)
+ return;
+ info->regs->rw.dafo &= ~(SAB82532_DAFO_XBRK);
+}
+
+static int sab82532_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ int error;
+ struct sab82532 * info = (struct sab82532 *)tty->driver_data;
+ int retval;
+ struct async_icount cprev, cnow; /* kernel counter temps */
+ struct serial_icounter_struct *p_cuser; /* user space */
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_ioctl"))
+ return -ENODEV;
+
+ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
+ (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
+ (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ }
+
+ switch (cmd) {
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ if (!arg) {
+ send_break(info, HZ/4); /* 1/4 second */
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ }
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ send_break(info, arg ? arg*(HZ/10) : HZ/4);
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ return 0;
+ case TIOCSBRK:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ begin_break(info);
+ return 0;
+ case TIOCCBRK:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ end_break(info);
+ return 0;
+ case TIOCGSOFTCAR:
+ return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
+ 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;
+ case TIOCMGET:
+ return get_modem_info(info, (unsigned int *) arg);
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return set_modem_info(info, cmd, (unsigned int *) arg);
+ case TIOCGSERIAL:
+ return get_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ return set_serial_info(info,
+ (struct serial_struct *) arg);
+
+ case TIOCSERGETLSR: /* Get line status register */
+ return get_lsr_info(info, (unsigned int *) arg);
+
+ case TIOCSERGSTRUCT:
+ if (copy_to_user((struct sab82532 *) arg,
+ info, sizeof(struct sab82532)))
+ return -EFAULT;
+ 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:
+ cli();
+ /* note the counters on entry */
+ cprev = info->icount;
+ sti();
+ while (1) {
+ interruptible_sleep_on(&info->delta_msr_wait);
+ /* see if a signal did it */
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ cli();
+ cnow = info->icount; /* atomic copy */
+ sti();
+ 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;
+ }
+ /* 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:
+ cli();
+ cnow = info->icount;
+ sti();
+ p_cuser = (struct serial_icounter_struct *) arg;
+ error = put_user(cnow.cts, &p_cuser->cts);
+ if (error) return error;
+ error = put_user(cnow.dsr, &p_cuser->dsr);
+ if (error) return error;
+ error = put_user(cnow.rng, &p_cuser->rng);
+ if (error) return error;
+ error = put_user(cnow.dcd, &p_cuser->dcd);
+ if (error) return error;
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static void sab82532_set_termios(struct tty_struct *tty,
+ struct termios *old_termios)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+
+ if ( (tty->termios->c_cflag == old_termios->c_cflag)
+ && ( RELEVANT_IFLAG(tty->termios->c_iflag)
+ == RELEVANT_IFLAG(old_termios->c_iflag)))
+ return;
+
+ change_speed(info);
+
+ /* Handle transition to B0 status */
+ if ((old_termios->c_cflag & CBAUD) &&
+ !(tty->termios->c_cflag & CBAUD)) {
+ info->regs->w.mode |= SAB82532_MODE_FRTS;
+ info->regs->w.mode |= SAB82532_MODE_RTS;
+ info->regs->w.pvr |= info->pvr_dtr_bit;
+ }
+
+ /* Handle transition away from B0 status */
+ if (!(old_termios->c_cflag & CBAUD) &&
+ (tty->termios->c_cflag & CBAUD)) {
+ info->regs->w.pvr &= ~(info->pvr_dtr_bit);
+ if (!tty->hw_stopped ||
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ info->regs->w.mode &= ~(SAB82532_MODE_FRTS);
+ info->regs->w.mode |= SAB82532_MODE_RTS;
+ }
+ }
+
+ /* Handle turning off CRTSCTS */
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ sab82532_start(tty);
+ }
+
+#if 0
+ /*
+ * No need to wake up processes in open wait, since they
+ * sample the CLOCAL flag once, and don't recheck it.
+ * XXX It's not clear whether the current behavior is correct
+ * or not. Hence, this may change.....
+ */
+ if (!(old_termios->c_cflag & CLOCAL) &&
+ (tty->termios->c_cflag & CLOCAL))
+ wake_up_interruptible(&info->open_wait);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_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 sab82532_close(struct tty_struct *tty, struct file * filp)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long flags;
+
+ if (!info || serial_paranoia_check(info, tty->device, "sab82532_close"))
+ return;
+
+ save_flags(flags); cli();
+
+ if (tty_hung_up_p(filp)) {
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+ return;
+ }
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("sab82532_close ttys%d, count = %d\n", info->line, info->count);
+#endif
+ if ((tty->count == 1) && (info->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. info->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk("sab82532_close: bad serial port count; tty->count is 1,"
+ " info->count is %d\n", info->count);
+ info->count = 1;
+ }
+ if (--info->count < 0) {
+ printk("sab82532_close: bad serial port count for ttys%d: %d\n",
+ info->line, info->count);
+ info->count = 0;
+ }
+ if (info->count) {
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+ return;
+ }
+ info->flags |= ASYNC_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ info->normal_termios = *tty->termios;
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ info->callout_termios = *tty->termios;
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, info->closing_wait);
+
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receive line status interrupts, and turn off
+ * the receiver.
+ */
+ info->interrupt_mask0 |= SAB82532_IMR0_TCD;
+ info->regs->w.imr0 = info->interrupt_mask0;
+ info->regs->rw.mode &= ~(SAB82532_MODE_RAC);
+ if (info->flags & ASYNC_INITIALIZED) {
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ sab82532_wait_until_sent(tty, info->timeout);
+ }
+ shutdown(info);
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ tty->closing = 0;
+ info->event = 0;
+ info->tty = 0;
+ if (info->blocked_open) {
+ if (info->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + info->close_delay;
+ schedule();
+ }
+ wake_up_interruptible(&info->open_wait);
+ }
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+ ASYNC_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+}
+
+/*
+ * sab82532_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ unsigned long orig_jiffies, char_time;
+
+ if (serial_paranoia_check(info,tty->device,"sab82532_wait_until_sent"))
+ return;
+
+ orig_jiffies = jiffies;
+ /*
+ * 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
+ * interval should also be less than the timeout.
+ *
+ * Note: we have to use pretty tight timings here to satisfy
+ * the NIST-PCTS.
+ */
+ char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
+ char_time = char_time / 5;
+ if (char_time == 0)
+ char_time = 1;
+ if (timeout)
+ char_time = MIN(char_time, timeout);
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("In sab82532_wait_until_sent(%d) check=%lu...", timeout, char_time);
+ printk("jiff=%lu...", jiffies);
+#endif
+
+ /* XXX: Implement this... */
+
+ current->state = TASK_RUNNING;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+#endif
+}
+
+/*
+ * sab82532_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void sab82532_hangup(struct tty_struct *tty)
+{
+ struct sab82532 * info = (struct sab82532 *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "sab82532_hangup"))
+ return;
+
+ sab82532_flush_buffer(tty);
+ shutdown(info);
+ info->event = 0;
+ info->count = 0;
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+ info->tty = 0;
+ wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct sab82532 *info)
+{
+ struct wait_queue wait = { current, NULL };
+ int retval;
+ int do_clocal = 0;
+
+ /*
+ * 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) ||
+ (info->flags & ASYNC_CLOSING)) {
+ if (info->flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ASYNC_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_SESSION_LOCKOUT) &&
+ (info->session != current->session))
+ return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_PGRP_LOCKOUT) &&
+ (info->pgrp != current->pgrp))
+ return -EBUSY;
+ info->flags |= ASYNC_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ return -EBUSY;
+ info->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+ if (info->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, info->count is dropped by one, so that
+ * sab82532_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready before block: ttyS%d, count = %d\n",
+ info->line, info->count);
+#endif
+ cli();
+ if (!tty_hung_up_p(filp))
+ info->count--;
+ sti();
+ info->blocked_open++;
+ while (1) {
+ cli();
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (tty->termios->c_cflag & CBAUD)) {
+ info->regs->rw.pvr &= ~(info->pvr_dtr_bit);
+ info->regs->rw.mode |= SAB82532_MODE_FRTS;
+ info->regs->rw.mode &= ~(SAB82532_MODE_RTS);
+ }
+ sti();
+ current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp) ||
+ !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ !(info->flags & ASYNC_CLOSING) &&
+ (do_clocal || !(info->regs->r.vstr & SAB82532_VSTR_CD)))
+ break;
+ if (current->signal & ~current->blocked) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready blocking: ttyS%d, count = %d, flags = %x, clocal = %d, vstr = %02x\n",
+ info->line, info->count, info->flags, do_clocal, info->regs->r.vstr);
+#endif
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+ if (!tty_hung_up_p(filp))
+ info->count++;
+ info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("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
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain. It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int sab82532_open(struct tty_struct *tty, struct file * filp)
+{
+ struct sab82532 *info = sab82532_chain;
+ int retval, line;
+ unsigned long page;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("sab82532_open: count = %d\n", info->count);
+#endif
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ if ((line < 0) || (line >= NR_PORTS))
+ return -ENODEV;
+
+ while (info) {
+ if (info->line == line)
+ break;
+ info = info->next;
+ }
+ if (!info) {
+ printk("sab82532_open: can't find info for line %d\n",
+ line);
+ return -ENODEV;
+ }
+
+ info->count++;
+ if (serial_paranoia_check(info, tty->device, "sab82532_open"))
+ return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("sab82532_open %s%d, count = %d\n", tty->driver.name, info->line,
+ info->count);
+#endif
+ tty->driver_data = info;
+ info->tty = tty;
+
+ if (!tmp_buf) {
+ page = get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ if (tmp_buf)
+ free_page(page);
+ else
+ tmp_buf = (unsigned char *) page;
+ }
+
+ /*
+ * Start up serial port
+ */
+ retval = startup(info);
+ if (retval)
+ return retval;
+
+ MOD_INC_USE_COUNT;
+ retval = block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("sab82532_open returning after block_til_ready with %d\n",
+ retval);
+#endif
+ return retval;
+ }
+
+ if ((info->count == 1) &&
+ (info->flags & ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = info->normal_termios;
+ else
+ *tty->termios = info->callout_termios;
+ change_speed(info);
+ }
+
+ info->session = current->session;
+ info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("sab82532_open ttys%d successful... count %d", info->line, info->count);
+#endif
+ return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static int inline line_info(char *buf, struct tty_struct *tty)
+{
+ struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+ int ret;
+
+ ret = sprintf(buf, "%d: uart:SAB82532 ", info->line);
+ switch (info->type) {
+ case 0:
+ ret += sprintf(buf+ret, "V1.0 ");
+ break;
+ case 1:
+ ret += sprintf(buf+ret, "V2.0 ");
+ break;
+ case 2:
+ ret += sprintf(buf+ret, "V3.2 ");
+ break;
+ default:
+ ret += sprintf(buf+ret, "V?.? ");
+ break;
+ }
+ ret += sprintf(buf+ret, "port:%lX irq:%d",
+ (unsigned long)info->regs, info->irq);
+
+ if (!info->regs) {
+ ret += sprintf(buf+ret, "\n");
+ return ret;
+ }
+
+ /*
+ * Figure out the current RS-232 lines
+ */
+
+ ret += sprintf(buf+ret, "\n");
+ return ret;
+}
+
+int sab82532_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ int i, len = 0;
+ off_t begin = 0;
+
+ len += sprintf(page, "serinfo:1.0 driver:%s\n", "$Revision: 1.4 $");
+ for (i = 0; i < NR_PORTS && len < 4000; i++) {
+ len += line_info(page + len, sab82532_table[i]);
+ if (len+begin > off+count)
+ goto done;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+ *eof = 1;
+done:
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * sab82532_init() and friends
+ *
+ * sab82532_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+__initfunc(static int get_sab82532(void))
+{
+ struct linux_ebus *ebus;
+ struct linux_ebus_device *edev;
+ struct sab82532 *sab;
+ unsigned long regs, offset;
+ int i;
+
+ for_all_ebusdev(edev, ebus)
+ if (!strcmp(edev->prom_name, "se"))
+ break;
+ if (!edev)
+ return -ENODEV;
+
+ printk("%s: SAB82532 at 0x%lx IRQ %x\n", __FUNCTION__,
+ edev->base_address[0], edev->irqs[0]);
+
+ regs = edev->base_address[0];
+ offset = sizeof(union sab82532_async_regs);
+
+ for (i = 0; i < 2; i++) {
+ sab = (struct sab82532 *)kmalloc(sizeof(struct sab82532),
+ GFP_KERNEL);
+ if (!sab) {
+ printk("sab82532: can't alloc sab struct\n");
+ break;
+ }
+ memset(sab, 0, sizeof(struct sab82532));
+
+ sab->regs = (union sab82532_async_regs *)(regs + offset);
+ sab->irq = edev->irqs[0];
+
+ if (check_region((unsigned long)sab->regs,
+ sizeof(union sab82532_async_regs))) {
+ kfree(sab);
+ continue;
+ }
+ request_region((unsigned long)sab->regs,
+ sizeof(union sab82532_async_regs),
+ "serial(sab82532)");
+
+ sab->regs->w.ipc = SAB82532_IPC_IC_ACT_LOW;
+
+ sab->next = sab82532_chain;
+ sab82532_chain = sab;
+
+ offset -= sizeof(union sab82532_async_regs);
+ }
+ return 0;
+}
+
+/* Hooks for running a serial console. con_init() calls this if the
+ * console is run over one of the ttya/ttyb serial ports.
+ * 'chip' should be zero, as for now we only have one chip on board.
+ * 'line' is decoded as 0=ttya, 1=ttyb.
+ */
+void
+sab82532_cons_hook(int chip, int out, int line)
+{
+ prom_printf("sab82532: serial console is not implemented, yet\n");
+ prom_halt();
+}
+
+void
+sab82532_kgdb_hook(int line)
+{
+ prom_printf("sab82532: kgdb support is not implemented, yet\n");
+ prom_halt();
+}
+
+__initfunc(static inline void show_serial_version(void))
+{
+ char *revision = "$Revision: 1.4 $";
+ char *version, *p;
+
+ version = strchr(revision, ' ');
+ p = strchr(++version, ' ');
+ *p = '\0';
+ printk("SAB82532 serial driver version %s\n", version);
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+__initfunc(int sab82532_init(void))
+{
+ struct sab82532 *info;
+ int i;
+
+ if (!sab82532_chain)
+ get_sab82532();
+ if (!sab82532_chain)
+ return -ENODEV;
+
+ init_bh(SERIAL_BH, do_serial_bh);
+
+ show_serial_version();
+
+ /* Initialize the tty_driver structure */
+ memset(&serial_driver, 0, sizeof(struct tty_driver));
+ serial_driver.magic = TTY_DRIVER_MAGIC;
+ serial_driver.driver_name = "serial";
+ serial_driver.name = "ttyS";
+ serial_driver.major = TTY_MAJOR;
+ serial_driver.minor_start = 64;
+ serial_driver.num = NR_PORTS;
+ serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ serial_driver.subtype = SERIAL_TYPE_NORMAL;
+ serial_driver.init_termios = tty_std_termios;
+ serial_driver.init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ serial_driver.flags = TTY_DRIVER_REAL_RAW;
+ serial_driver.refcount = &sab82532_refcount;
+ serial_driver.table = sab82532_table;
+ serial_driver.termios = sab82532_termios;
+ serial_driver.termios_locked = sab82532_termios_locked;
+
+ serial_driver.open = sab82532_open;
+ serial_driver.close = sab82532_close;
+ serial_driver.write = sab82532_write;
+ serial_driver.put_char = sab82532_put_char;
+ serial_driver.flush_chars = sab82532_flush_chars;
+ serial_driver.write_room = sab82532_write_room;
+ serial_driver.chars_in_buffer = sab82532_chars_in_buffer;
+ serial_driver.flush_buffer = sab82532_flush_buffer;
+ serial_driver.ioctl = sab82532_ioctl;
+ serial_driver.throttle = sab82532_throttle;
+ serial_driver.unthrottle = sab82532_unthrottle;
+ serial_driver.send_xchar = sab82532_send_xchar;
+ serial_driver.set_termios = sab82532_set_termios;
+ serial_driver.stop = sab82532_stop;
+ serial_driver.start = sab82532_start;
+ serial_driver.hangup = sab82532_hangup;
+ serial_driver.wait_until_sent = sab82532_wait_until_sent;
+ serial_driver.read_proc = sab82532_read_proc;
+
+ /*
+ * The callout device is just like normal device except for
+ * major number and the subtype code.
+ */
+ callout_driver = serial_driver;
+ callout_driver.name = "cua";
+ callout_driver.major = TTYAUX_MAJOR;
+ callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+ callout_driver.read_proc = 0;
+ callout_driver.proc_entry = 0;
+
+ if (tty_register_driver(&serial_driver))
+ panic("Couldn't register serial driver\n");
+ if (tty_register_driver(&callout_driver))
+ panic("Couldn't register callout driver\n");
+
+ for (info = sab82532_chain, i = 0; info; info = info->next, i++) {
+ info->magic = SERIAL_MAGIC;
+ info->line = i;
+ info->tty = 0;
+ info->count = 0;
+
+ info->type = info->regs->r.vstr & 0x0f;
+ info->regs->w.pcr = ~((1 << 1) | (1 << 2) | (1 << 4));
+ info->regs->w.pim = 0xff;
+ if (info->line == 0) {
+ info->pvr_dsr_bit = (1 << 0);
+ info->pvr_dtr_bit = (1 << 1);
+ } else {
+ info->pvr_dsr_bit = (1 << 3);
+ info->pvr_dtr_bit = (1 << 2);
+ }
+ info->regs->w.pvr = (1 << 1) | (1 << 2) | (1 << 4);
+ info->regs->rw.mode |= SAB82532_MODE_FRTS;
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+
+ info->xmit_fifo_size = 32;
+ info->recv_fifo_size = 32;
+ info->custom_divisor = 16;
+ info->close_delay = 5*HZ/10;
+ info->closing_wait = 30*HZ;
+ info->x_char = 0;
+ info->event = 0;
+ info->count = 0;
+ info->blocked_open = 0;
+ info->tqueue.routine = do_softint;
+ info->tqueue.data = info;
+ info->tqueue_hangup.routine = do_serial_hangup;
+ info->tqueue_hangup.data = info;
+ info->callout_termios = callout_driver.init_termios;
+ info->normal_termios = serial_driver.init_termios;
+ info->open_wait = 0;
+ info->close_wait = 0;
+ info->delta_msr_wait = 0;
+ info->icount.cts = info->icount.dsr =
+ info->icount.rng = info->icount.dcd = 0;
+ info->icount.rx = info->icount.tx = 0;
+ info->icount.frame = info->icount.parity = 0;
+ info->icount.overrun = info->icount.brk = 0;
+
+ if (!(info->line & 0x01)) {
+ if (request_irq(info->irq, sab82532_interrupt, SA_SHIRQ,
+ "serial(sab82532)", info)) {
+ printk("sab82532: can't get IRQ %x\n",
+ info->irq);
+ panic("sab82532 initialization failed");
+ }
+ }
+
+ printk(KERN_INFO "ttyS%02d at 0x%lx (irq = %x) is a %s\n",
+ info->line, (unsigned long)info->regs, info->irq,
+ "SAB82532");
+ }
+ return 0;
+}
+
+__initfunc(int sab82532_probe(unsigned long *memory_start))
+{
+ int node, enode, snode;
+
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(node, "pci");
+
+ /*
+ * For each PCI bus...
+ */
+ while (node) {
+ enode = prom_getchild(node);
+ enode = prom_searchsiblings(enode, "ebus");
+
+ /*
+ * For each EBus on this PCI...
+ */
+ while (enode) {
+ snode = prom_getchild(enode);
+ snode = prom_searchsiblings(snode, "se");
+ if (snode)
+ goto found;
+
+ enode = prom_getsibling(enode);
+ enode = prom_searchsiblings(enode, "ebus");
+ }
+ node = prom_getsibling(node);
+ node = prom_searchsiblings(node, "pci");
+ }
+ return -ENODEV;
+
+found:
+ sunserial_setinitfunc(memory_start, sab82532_init);
+ rs_ops.rs_cons_hook = sab82532_cons_hook;
+ rs_ops.rs_kgdb_hook = sab82532_kgdb_hook;
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ if (get_sab82532())
+ return -ENODEV;
+
+ return sab82532_init();
+}
+
+void cleanup_module(void)
+{
+ unsigned long flags;
+ int e1, e2;
+ int i;
+
+ /* printk("Unloading %s: version %s\n", serial_name, serial_version); */
+ save_flags(flags);
+ cli();
+ timer_active &= ~(1 << RS_TIMER);
+ timer_table[RS_TIMER].fn = NULL;
+ timer_table[RS_TIMER].expires = 0;
+ remove_bh(SERIAL_BH);
+ if ((e1 = tty_unregister_driver(&serial_driver)))
+ printk("SERIAL: failed to unregister serial driver (%d)\n",
+ e1);
+ if ((e2 = tty_unregister_driver(&callout_driver)))
+ printk("SERIAL: failed to unregister callout driver (%d)\n",
+ e2);
+ restore_flags(flags);
+
+ for (i = 0; i < NR_PORTS; i++) {
+ if (sab82532_table[i].type != PORT_UNKNOWN)
+ release_region(sab82532_table[i].port, 8);
+ }
+ if (tmp_buf) {
+ free_page((unsigned long) tmp_buf);
+ tmp_buf = NULL;
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/sbus/char/sbuscons.c b/drivers/sbus/char/sbuscons.c
new file mode 100644
index 000000000..a0d47d79c
--- /dev/null
+++ b/drivers/sbus/char/sbuscons.c
@@ -0,0 +1,1644 @@
+/* $Id: sbuscons.c,v 1.7 1997/08/28 09:30:07 davem Exp $
+ * sbuscons.c: Routines specific to SBUS frame buffer consoles.
+ *
+ * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
+ * Copyright (C) 1995,1996,1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
+ * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ *
+ * Added font loading Nov/21, Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Added render_screen and faster scrolling Nov/27, miguel
+ * Added console palette code for cg6 Dec/13/95, miguel
+ * Added generic frame buffer support Dec/14/95, miguel
+ * Added cgsix and bwtwo drivers Jan/96, miguel
+ * Added 4m, and cg3 driver Feb/96, miguel
+ * Fixed the cursor on color displays Feb/96, miguel.
+ * Cleaned up the detection code, generic 8bit depth display
+ * code, Mar/96 miguel
+ * Hacked support for cg14 video cards -- Apr/96, miguel.
+ * Color support for cg14 video cards -- May/96, miguel.
+ * Code split, Dave Redman, May/96
+ * Be more VT change friendly, May/96, miguel.
+ * Support for hw cursor and graphics acceleration, Jun/96, jj.
+ * Added TurboGX+ detection (cgthree+), Aug/96, Iain Lea (iain@sbs.de)
+ * Added TCX support (8/24bit), Aug/96, jj.
+ * Support for multiple framebuffers, Sep/96, jj.
+ * Fix bwtwo inversion and handle inverse monochrome cells in
+ * sun_blitc, Nov/96, ecd.
+ * Fix sun_blitc and screen size on displays other than 1152x900,
+ * 128x54 chars, Nov/96, jj.
+ * Fix cursor spots left on some non-accelerated fbs, changed
+ * software cursor to be like the hw one, Nov/96, jj.
+ *
+ * Much of this driver is derived from the DEC TGA driver by
+ * Jay Estabrook who has done a nice job with the console
+ * driver abstraction btw.
+ *
+ * We try to make everything a power of two if possible to
+ * speed up the bit blit. Doing multiplies, divides, and
+ * remainder routines end up calling software library routines
+ * since not all Sparcs have the hardware to do it.
+ *
+ * TODO:
+ * do not blank the screen when frame buffer is mapped.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kd.h>
+#include <linux/malloc.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/proc_fs.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/bitops.h>
+#include <asm/oplib.h>
+#include <asm/sbus.h>
+#include <asm/fbio.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/consolemap.h>
+#include <linux/selection.h>
+#include <linux/console_struct.h>
+
+#include "fb.h"
+
+#define cmapsz 8192
+
+#include "suncons_font.h"
+
+#define ASM_BLITC
+
+int sbus_hw_cursor_shown = 0;
+
+void sbus_hw_hide_cursor(void);
+void sbus_hw_set_cursor(int,int);
+
+static int sbus_blitc(uint, unsigned long);
+
+static void sbus_install_consops(void);
+
+extern void register_console(void (*proc)(const char *));
+extern void console_print(const char *);
+extern void putconsxy(int, char *);
+extern unsigned char vga_font[];
+extern int serial_console;
+extern char *console_fb_path;
+
+/* The following variables describe a Sparc console. */
+
+/* Screen dimensions and color depth. */
+static int con_depth, con_width, con_height, con_type;
+
+/* Base address of first line. */
+static unsigned char *con_fb_base;
+
+/* Screen parameters: we compute those at startup to make the code faster */
+static int chars_per_line; /* number of bytes per line */
+static int ints_per_line; /* number of ints per line */
+static int ints_per_cursor; /* 14 * ints_per_line */
+static int skip_bytes; /* number of bytes we skip for the y margin + x_margin */
+static int x_margin, y_margin; /* the x and y margins */
+static int bytes_per_row; /* bytes used by one screen line (of 16 scan lines) */
+int sun_prom_console_id = 0;
+
+/* Functions used by the SPARC dependent console code
+ * to perform the fb_restore_palette function.
+ */
+extern void (*fb_restore_palette)(fbinfo_t *fbinfo);
+static void sbus_set_palette (void);
+
+ /* Our screen looks like at 1152 X 900:
+ *
+ * 0,0
+ * ------------------------------------------------------------------
+ * | ^^^^^^^^^^^ |
+ * | 18 y-pixels |
+ * | ^^^^^^^^^^^ |
+ * 13 | <-64 pixels->| <-- 128 8x16 characters --> | <-64 pixels-> |
+ * ....
+ * 54 chars from top to bottom
+ * ....
+ * 888 | <-64 pixels->| <-- 128 8x16 characters --> | <-64 pixels-> |
+ * | ^^^^^^^^^^^ |
+ * | 18 y-pixels |
+ * | ^^^^^^^^^^^ |
+ * ------------------------------------------------------------------
+ */
+/* First for MONO displays. */
+#define SCREEN_WIDTH 1152 /* Screen width in pixels */
+#define SCREEN_HEIGHT 900 /* Screen height in pixels */
+#define CHARS_PER_LINE 144 /* Make this empirical for speed */
+#define NICE_Y_MARGIN 18 /* We skip 18 y-pixels at top/bottom */
+#define NICE_X_MARGIN 8 /* We skip 64 x-pixels at left/right */
+#define FBUF_TOP_SKIP 2592 /* Empirical, (CHARS_PER_LINE * NICE_Y_MARGIN) */
+#define CHAR_HEIGHT 16
+#define ONE_ROW 2304 /* CHARS_PER_LINE * CHAR_HEIGHT */
+
+/* Now we have this, to compute the base frame buffer position
+ * for a new character to be rendered. 1 and 8 bit depth.
+ */
+#define FBUF_OFFSET(cindex) \
+ (((FBUF_TOP_SKIP) + (((cindex)>>7) * ONE_ROW)) + \
+ ((NICE_X_MARGIN) + (((cindex)&127))))
+
+
+#define COLOR_FBUF_OFFSET(cindex) (*color_fbuf_offset)(cindex)
+
+/* These four routines are optimizations for the _generic routine for
+ * the most common cases.
+ * I guess doing twice sll is much faster than doing .mul, sra faster
+ * than doing .div, and the disadvantage that someone has to call it
+ * (it cannot be inline) runs away, 'cause otherwise it would have to
+ * call .mul anyway.
+ * The shifting + addition only routines won't eat any stack frame :))
+ * Names come from width, screen_num_columns.
+ */
+static int color_fbuf_offset_1280_144 (int cindex)
+{
+ register int i = (cindex/144);
+ /* (1280 * CHAR_HEIGHT) == 101.0000.0000.0000 */
+ return skip_bytes + (i << 14) + (i << 12) + ((cindex % 144) << 3);
+}
+
+static int color_fbuf_offset_1152_128 (int cindex)
+{
+ register int i = (cindex>>7);
+ /* (1152 * CHAR_HEIGHT) == 100.1000.0000.0000 */
+ return skip_bytes + (i << 14) + (i << 11) + ((cindex & 127) << 3);
+}
+
+static int color_fbuf_offset_1024_128 (int cindex)
+{
+ register int i = (cindex>>7);
+ /* (1024 * CHAR_HEIGHT) == 100.0000.0000.0000 */
+ return skip_bytes + (i << 14) + ((cindex & 127) << 3);
+}
+
+static int color_fbuf_offset_800_96 (int cindex)
+{
+ register int i = (cindex / 96);
+ /* (800 * CHAR_HEIGHT) == 11.0010.0000.0000 */
+ return skip_bytes + (i<<13) + (i<<12) + (i<<9) + ((cindex % 96)<<3);
+}
+
+static int color_fbuf_offset_640_80 (int cindex)
+{
+ register int i = (cindex/80);
+ /* (640 * CHAR_HEIGHT) == 10.1000.0000.0000 */
+ return skip_bytes + (i << 13) + (i << 11) + ((cindex % 80) << 3);
+}
+
+static int color_fbuf_offset_generic (int cindex)
+{
+ return skip_bytes + (cindex / video_num_columns) * bytes_per_row + ((cindex % video_num_columns) << 3);
+}
+
+static int (*color_fbuf_offset)(int) = color_fbuf_offset_generic;
+
+static int do_accel = 0;
+
+/* For the cursor, we just invert the 8x16 block at the cursor
+ * location. Easy enough...
+ *
+ * Hide the cursor from view, during blanking, usually...
+ */
+static int cursor_pos = -1;
+
+static unsigned int under_cursor[4];
+
+static void sbus_hide_cursor(void)
+{
+ unsigned long flags;
+ int j;
+
+ if (fbinfo[0].setcursor) {
+ sbus_hw_hide_cursor();
+ return;
+ }
+
+ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+ return; /* Don't paint anything on fb which is not ours,
+ but turn off the hw cursor in such case */
+
+ __save_and_cli(flags);
+
+ if(cursor_pos == -1) {
+ __restore_flags (flags);
+ return;
+ }
+ switch (con_depth){
+ case 1: {
+ unsigned char *dst;
+ dst = (unsigned char *)((unsigned long)con_fb_base +
+ FBUF_OFFSET(cursor_pos));
+ for(j = 0; j < CHAR_HEIGHT; j++, dst += CHARS_PER_LINE)
+ *dst = ~(*dst);
+ break;
+ }
+ case 8: {
+ unsigned int *dst;
+
+ dst = (unsigned int *)((unsigned long)con_fb_base +
+ COLOR_FBUF_OFFSET(cursor_pos)) + ints_per_cursor;
+ dst[0] = under_cursor[0];
+ dst[1] = under_cursor[1];
+ dst[ints_per_line] = under_cursor[2];
+ dst[ints_per_line+1] = under_cursor[3];
+ break;
+ }
+ default:
+ break;
+ }
+ cursor_pos = -1;
+ __restore_flags(flags);
+}
+
+static void sbus_set_cursor(int currcons)
+{
+ int j, idx, oldpos;
+ unsigned long flags;
+
+ if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
+ return;
+
+ if (fbinfo[0].setcursor) {
+ if (!deccm)
+ sbus_hide_cursor();
+ else {
+ idx = (pos - video_mem_base) >> 1;
+
+ sbus_hw_set_cursor(x_margin + ((idx % video_num_columns) << 3), y_margin + ((idx / video_num_columns) * CHAR_HEIGHT));
+ }
+ return;
+ }
+
+ __save_and_cli(flags);
+
+ idx = (pos - video_mem_base) >> 1;
+ oldpos = cursor_pos;
+ if (!deccm) {
+ sbus_hide_cursor ();
+ __restore_flags (flags);
+ return;
+ }
+ cursor_pos = idx;
+ switch (con_depth){
+ case 1: {
+ unsigned char *dst, *opos;
+
+ dst = (unsigned char *)((unsigned long)con_fb_base + FBUF_OFFSET(idx));
+ opos = (unsigned char *)((unsigned long)con_fb_base + FBUF_OFFSET(oldpos));
+ if(oldpos != -1) {
+ /* Restore what was at the old position */
+ for(j=0; j < CHAR_HEIGHT; j++, opos += CHARS_PER_LINE) {
+ *opos = ~*opos;
+ }
+ }
+ for(j=0; j < 16; j++, dst+=CHARS_PER_LINE) {
+ *dst = ~*dst;
+ }
+ break;
+ }
+ case 8: {
+ unsigned int *dst, *opos;
+ dst = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(idx)) + ints_per_cursor;
+
+ if(oldpos != -1) {
+ opos = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(oldpos)) + ints_per_cursor;
+ opos[0] = under_cursor[0];
+ opos[1] = under_cursor[1];
+ opos[ints_per_line] = under_cursor[2];
+ opos[ints_per_line+1] = under_cursor[3];
+ }
+ under_cursor[0] = dst[0];
+ under_cursor[1] = dst[1];
+ under_cursor[2] = dst[ints_per_line];
+ under_cursor[3] = dst[ints_per_line+1];
+ dst[0] = 0x00000000;
+ dst[1] = 0x00000000;
+ dst[ints_per_line] = 0x00000000;
+ dst[ints_per_line+1] = 0x00000000;
+ break;
+ }
+ default:
+ }
+ __restore_flags(flags);
+}
+
+/*
+ * Render the current screen
+ * Only used at startup and when switching from KD_GRAPHICS to KD_TEXT
+ * to avoid the caching that is being done in selection.h
+ */
+
+static void sbus_render_screen(void)
+{
+ int count;
+ unsigned short *contents;
+
+ count = video_num_columns * video_num_lines;
+ contents = (unsigned short *) video_mem_base;
+
+ for (;count--; contents++)
+ sbus_blitc (*contents, (unsigned long) contents);
+}
+
+__initfunc(static unsigned long
+sbus_con_type_init(unsigned long kmem_start, const char **display_desc))
+{
+ can_do_color = (con_type != FBTYPE_SUN2BW);
+
+ video_type = VIDEO_TYPE_SUN;
+ *display_desc = "SUN";
+
+ if (!serial_console) {
+ /* If we fall back to PROM then our output have to remain readable. */
+ prom_putchar('\033'); prom_putchar('['); prom_putchar('H');
+
+ /*
+ * fake the screen memory with some CPU memory
+ */
+ video_mem_base = kmem_start;
+ kmem_start += video_screen_size;
+ video_mem_term = kmem_start;
+ }
+ return kmem_start;
+}
+
+__initfunc(static void sbus_con_type_init_finish(void))
+{
+ int i, cpu;
+ char *p = con_fb_base + skip_bytes;
+ char q[2] = {0,5};
+ int currcons = 0;
+ unsigned short *ush;
+ int ncpus;
+
+ if (serial_console)
+ return;
+ ncpus = linux_num_cpus;
+ if (ncpus > 4) ncpus = 4;
+ if (fbinfo[0].draw_penguin) {
+ (*fbinfo[0].draw_penguin)(x_margin, y_margin, ncpus);
+ } else if (con_depth == 8 && fbinfo[0].loadcmap) {
+ for (i = 0; i < linux_logo_colors; i++) {
+ fbinfo[0].color_map CM(i+32,0) = linux_logo_red [i];
+ fbinfo[0].color_map CM(i+32,1) = linux_logo_green [i];
+ fbinfo[0].color_map CM(i+32,2) = linux_logo_blue [i];
+ }
+ (*fbinfo [0].loadcmap)(&fbinfo [0], 0, linux_logo_colors + 32);
+ for (i = 0; i < 80; i++, p += chars_per_line){
+ for (cpu = 0; cpu < ncpus; cpu++){
+ memcpy (p + (cpu * 88), linux_logo + 80 * i, 80);
+ }
+ }
+ } else if (con_depth == 1) {
+ for (i = 0; i < 80; i++, p += chars_per_line)
+ memcpy (p, linux_logo_bw + 10 * i, 10);
+ }
+ putconsxy(0, q);
+ ush = (unsigned short *) video_mem_base + video_num_columns * 2 + 20 + 11 * (ncpus - 1);
+
+ p = logo_banner;
+ for (; *p; p++, ush++) {
+ *ush = (attr << 8) + *p;
+ sbus_blitc (*ush, (unsigned long) ush);
+ }
+ for (i = 0; i < 5; i++) {
+ ush = (unsigned short *) video_mem_base + i * video_num_columns;
+ memset (ush, 0, 20);
+ }
+}
+
+/*
+ * NOTE: get_scrmem() and set_scrmem() are here only because
+ * the VGA version of set_scrmem() has some direct VGA references.
+ */
+static void sbus_get_scrmem(int currcons)
+{
+ memcpyw((unsigned short *)vc_scrbuf[currcons],
+ (unsigned short *)origin, video_screen_size);
+ origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
+ scr_end = video_mem_end = video_mem_start + video_screen_size;
+ pos = origin + y*video_size_row + (x<<1);
+}
+
+static void sbus_set_scrmem(int currcons, long offset)
+{
+ if (video_mem_term - video_mem_base < offset + video_screen_size)
+ offset = 0;
+ memcpyw((unsigned short *)(video_mem_base + offset),
+ (unsigned short *) origin, video_screen_size);
+ video_mem_start = video_mem_base;
+ video_mem_end = video_mem_term;
+ origin = video_mem_base + offset;
+ scr_end = origin + video_screen_size;
+ pos = origin + y*video_size_row + (x<<1);
+}
+
+/*
+ * PIO_FONT support.
+ */
+static int sbus_set_get_font(char * arg, int set, int ch512)
+{
+ int i, line;
+
+ if (!arg)
+ return -EINVAL;
+
+ /* download the current font */
+ if (!set){
+ if(clear_user(arg, cmapsz))
+ return -EFAULT;
+ for (i = 0; i < 256; i++) {
+ for (line = 0; line < CHAR_HEIGHT; line++) {
+ unsigned char value = vga_font[i];
+
+ /* Access checked by the above clear_user */
+ __put_user_ret (value, (arg + (i * 32 + line)),
+ -EFAULT);
+ }
+ }
+ return 0;
+ }
+
+ /* set the font */
+
+ if (verify_area (VERIFY_READ, arg, 256 * CHAR_HEIGHT)) return -EFAULT;
+ for (i = 0; i < 256; i++) {
+ for (line = 0; line < CHAR_HEIGHT; line++){
+ unsigned char value;
+ __get_user_ret(value, (arg + (i * 32 + line)),-EFAULT);
+ vga_font [i*CHAR_HEIGHT + line] = value;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Adjust the screen to fit a font of a certain height
+ *
+ * Returns < 0 for error, 0 if nothing changed, and the number
+ * of lines on the adjusted console if changed.
+ *
+ * for now, we only support the built-in font...
+ */
+static int sbus_con_adjust_height(unsigned long fontheight)
+{
+ return -EINVAL;
+}
+
+static int sbus_set_get_cmap(unsigned char * arg, int set)
+{
+ int i;
+
+ if(set)
+ i = VERIFY_READ;
+ else
+ i = VERIFY_WRITE;
+ if(verify_area(i, arg, (16 * 3 * sizeof(unsigned char))))
+ return -EFAULT;
+ for (i=0; i<16; i++) {
+ if (set) {
+ __get_user_ret(default_red[i], (arg+0),-EFAULT);
+ __get_user_ret(default_grn[i], (arg+1),-EFAULT);
+ __get_user_ret(default_blu[i], (arg+2),-EFAULT);
+ } else {
+ __put_user_ret(default_red[i], (arg+0),-EFAULT);
+ __put_user_ret(default_grn[i], (arg+1),-EFAULT);
+ __put_user_ret(default_blu[i], (arg+2),-EFAULT);
+ }
+ arg += 3;
+ }
+ if (set) {
+ for (i=0; i<MAX_NR_CONSOLES; i++)
+ if (vc_cons_allocated(i)) {
+ int j, k ;
+ for (j=k=0; j<16; j++) {
+ vc_cons[i].d->vc_palette[k++] = default_red[j];
+ vc_cons[i].d->vc_palette[k++] = default_grn[j];
+ vc_cons[i].d->vc_palette[k++] = default_blu[j];
+ }
+ }
+ sbus_set_palette();
+ }
+
+ return 0;
+}
+
+static void sbus_clear_screen(void)
+{
+ if (fbinfo[0].fill) {
+ int rects [4];
+
+ rects [0] = 0;
+ rects [1] = 0;
+ rects [2] = con_width;
+ rects [3] = con_height;
+ (*fbinfo[0].fill)(reverse_color_table[0], 1, rects);
+ } else if (fbinfo[0].base && fbinfo[0].base_depth)
+ memset (con_fb_base,
+ (con_depth == 1) ? ~(0) : reverse_color_table[0],
+ (con_depth * con_height * con_width) / 8);
+ /* also clear out the "shadow" screen memory */
+ memset((char *)video_mem_base, 0, (video_mem_term - video_mem_base));
+ cursor_pos = -1;
+}
+
+static void sbus_clear_fb(int n)
+{
+ if (!n) {
+ sbus_clear_screen ();
+ } else if (fbinfo[n].base && fbinfo[n].base_depth) {
+ memset((void *)fbinfo[n].base,
+ (fbinfo[n].base_depth == 1) ?
+ ~(0) : reverse_color_table[0],
+ (fbinfo[n].base_depth * fbinfo[n].type.fb_height
+ * fbinfo[n].type.fb_width) / 8);
+ }
+}
+
+static void sbus_clear_margin(void)
+{
+ int h, he, i;
+ unsigned char *p;
+
+ if (fbinfo[0].fill) {
+ int rects [16];
+
+ rects [0] = 0;
+ rects [1] = 0;
+ rects [2] = con_width;
+ rects [3] = y_margin;
+ rects [4] = 0;
+ rects [5] = y_margin;
+ rects [6] = x_margin;
+ rects [7] = con_height;
+ rects [8] = con_width - x_margin;
+ rects [9] = y_margin;
+ rects [10] = con_width;
+ rects [11] = con_height;
+ rects [12] = x_margin;
+ rects [13] = con_height - y_margin;
+ rects [14] = con_width - x_margin;
+ rects [15] = con_height;
+ (*fbinfo[0].fill)(reverse_color_table[0], 4, rects);
+ } else {
+ memset (con_fb_base,
+ (con_depth == 1) ? ~(0) : reverse_color_table[0],
+ skip_bytes - (x_margin<<1));
+ memset (con_fb_base + chars_per_line * con_height
+ - skip_bytes + (x_margin<<1),
+ (con_depth == 1) ? ~(0) : reverse_color_table[0],
+ skip_bytes - (x_margin<<1));
+ he = con_height - 2 * y_margin;
+ i = 2 * x_margin;
+ if (con_depth == 1) {
+ for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0;
+ h <= he; p += chars_per_line, h++)
+ memset (p, ~(0), i);
+ } else {
+ for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0;
+ h <= he; p += chars_per_line, h++)
+ memset (p, reverse_color_table[0], i);
+ }
+ }
+ if (fbinfo [0].switch_from_graph)
+ (*fbinfo [0].switch_from_graph)();
+}
+
+/* Call the frame buffer routine for setting the palette */
+static void sbus_set_palette (void)
+{
+ if (console_blanked || vt_cons [fg_console]->vc_mode == KD_GRAPHICS)
+ return;
+
+ if (fbinfo [0].loadcmap){
+ int i, j;
+
+ /* First keep color_map with the palette colors */
+ for (i = 0; i < 16; i++){
+ j = sparc_color_table [i];
+ fbinfo[0].color_map CM(i,0) = default_red [j];
+ fbinfo[0].color_map CM(i,1) = default_grn [j];
+ fbinfo[0].color_map CM(i,2) = default_blu [j];
+ }
+ (*fbinfo [0].loadcmap)(&fbinfo [0], 0, 16);
+ }
+}
+
+static void sbus_set_other_palette (int n)
+{
+ if (!n) {
+ sbus_set_palette ();
+ return;
+ }
+ if (fbinfo [n].loadcmap){
+ fbinfo[n].color_map CM(0,0) = 0;
+ fbinfo[n].color_map CM(0,1) = 0;
+ fbinfo[n].color_map CM(0,2) = 0;
+ (*fbinfo [n].loadcmap)(&fbinfo [n], 0, 1);
+ }
+}
+
+/* Called when returning to prom */
+static void sbus_console_restore_palette (void)
+{
+ if (fb_restore_palette)
+ (*fb_restore_palette) (&fbinfo[0]);
+}
+
+__initfunc(unsigned long cg_postsetup(fbinfo_t *fb, unsigned long start_mem))
+{
+ fb->color_map = (char *)start_mem;
+ return start_mem + 256*3;
+}
+
+static char *known_cards [] __initdata = {
+ "cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", "SUNW,tcx",
+ "cgfourteen", "SUNW,leo", "SUNW,ffb", 0
+};
+static char *v0_known_cards [] __initdata = {
+ "cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", 0
+};
+
+__initfunc(static int known_card (char *name, char **known_cards))
+{
+ int i;
+
+ for (i = 0; known_cards [i]; i++)
+ if (strcmp (name, known_cards [i]) == 0)
+ return 1;
+ return 0;
+}
+
+static struct {
+ int depth;
+ int resx, resy;
+ int x_margin, y_margin;
+} scr_def [] = {
+ { 8, 1280, 1024, 64, 80 },
+ { 8, 1152, 1024, 64, 80 },
+ { 8, 1152, 900, 64, 18 },
+ { 8, 1024, 768, 0, 0 },
+ { 8, 800, 600, 16, 12 },
+ { 8, 640, 480, 0, 0 },
+ { 1, 1152, 900, 8, 18 },
+ { 0 },
+};
+
+__initfunc(static int cg14_present(void))
+{
+ int root, n;
+
+ root = prom_getchild (prom_root_node);
+ if ((n = prom_searchsiblings (root, "obio")) == 0)
+ return 0;
+
+ n = prom_getchild (n);
+ if ((n = prom_searchsiblings (n, "cgfourteen")) == 0)
+ return 0;
+ return n;
+}
+
+__initfunc(static int creator_present (void))
+{
+#ifdef __sparc_v9__
+ int root, n;
+
+ root = prom_getchild (prom_root_node);
+ if ((n = prom_searchsiblings (root, "SUNW,ffb")) == 0)
+ return 0;
+ return n;
+#else
+ return 0;
+#endif
+}
+
+__initfunc(static void
+ sparc_framebuffer_setup(int primary, int con_node,
+ int type, struct linux_sbus_device *sbdp,
+ uint base, unsigned long con_base, int prom_fb,
+ int parent_node))
+{
+ static int frame_buffers = 1;
+ int n, i;
+ int linebytes;
+ uint io = 0;
+ char *p;
+
+ if (primary)
+ n = 0;
+ else {
+ if (frame_buffers == FRAME_BUFFERS)
+ return; /* Silently ignore */
+ n = frame_buffers++;
+ }
+
+ if (prom_fb) sun_prom_console_id = n;
+
+ if (sbdp)
+ io = sbdp->reg_addrs [0].which_io;
+
+ /* Fill in common fb information */
+ fbinfo [n].clear_fb = sbus_clear_fb;
+ fbinfo [n].set_other_palette = sbus_set_other_palette;
+ fbinfo [n].type.fb_type = type;
+ fbinfo [n].real_type = type;
+ fbinfo [n].prom_node = con_node;
+ memset (&(fbinfo [n].emulations), 0xff, sizeof (fbinfo [n].emulations));
+ fbinfo [n].type.fb_height = prom_getintdefault(con_node, "height", 900);
+ fbinfo [n].type.fb_width = prom_getintdefault(con_node, "width", 1152);
+ fbinfo [n].type.fb_depth = (type == FBTYPE_SUN2BW) ? 1 : 8;
+ linebytes = prom_getint(con_node, "linebytes");
+ if (linebytes == -1) linebytes = fbinfo [n].type.fb_width;
+ fbinfo [n].type.fb_size = PAGE_ALIGN((linebytes) * (fbinfo [n].type.fb_height));
+ fbinfo [n].space = io;
+ fbinfo [n].blanked = 0;
+ if (con_base >= PAGE_OFFSET)
+ fbinfo [n].base = con_base;
+ else
+ fbinfo [n].base = 0;
+ fbinfo [n].cursor.hwsize.fbx = 32;
+ fbinfo [n].cursor.hwsize.fby = 32;
+ fbinfo [n].proc_entry.node = parent_node;
+ fbinfo [n].proc_entry.rdev = MKDEV(GRAPHDEV_MAJOR, n);
+ fbinfo [n].proc_entry.mode = S_IFCHR | S_IRUSR | S_IWUSR;
+ prom_getname (con_node, fbinfo [n].proc_entry.name, 32 - 3);
+ p = strchr (fbinfo [n].proc_entry.name, 0);
+ sprintf (p, ":%d", n);
+
+ /* Should be filled in for supported video cards */
+ fbinfo [n].mmap = 0;
+ fbinfo [n].loadcmap = 0;
+ fbinfo [n].ioctl = 0;
+ fbinfo [n].reset = 0;
+ fbinfo [n].blank = 0;
+ fbinfo [n].unblank = 0;
+ fbinfo [n].setcursor = 0;
+ fbinfo [n].base_depth = fbinfo [n].type.fb_depth;
+
+ /* Per card setup */
+ switch (fbinfo [n].type.fb_type){
+#ifdef SUN_FB_CGTHREE
+ case FBTYPE_SUN3COLOR:
+ cg3_setup (&fbinfo [n], n, base, io, sbdp);
+ break;
+#endif
+#ifdef SUN_FB_TCX
+ case FBTYPE_TCXCOLOR:
+ tcx_setup (&fbinfo [n], n, con_node, base, sbdp);
+ break;
+#endif
+#ifdef SUN_FB_CGSIX
+ case FBTYPE_SUNFAST_COLOR:
+ cg6_setup (&fbinfo [n], n, base, io);
+ break;
+#endif
+#ifdef SUN_FB_BWTWO
+ case FBTYPE_SUN2BW:
+ bwtwo_setup (&fbinfo [n], n, base, io, sbdp);
+ break;
+#endif
+#ifdef SUN_FB_CGFOURTEEN
+ case FBTYPE_MDICOLOR:
+ cg14_setup (&fbinfo [n], n, con_node, base, io);
+ break;
+#endif
+#ifdef SUN_FB_LEO
+ case FBTYPE_SUNLEO:
+ leo_setup (&fbinfo [n], n, base, io);
+ break;
+#endif
+#if defined(SUN_FB_CREATOR) && defined(__sparc_v9__)
+ case FBTYPE_CREATOR:
+ creator_setup (&fbinfo [n], n, con_node, base, io);
+ break;
+#endif
+ default:
+ fbinfo [n].type.fb_type = FBTYPE_NOTYPE;
+ return;
+ }
+
+ if (n)
+ return;
+
+ /* Code below here is just executed for the first frame buffer */
+ con_type = type;
+ con_height = fbinfo [n].type.fb_height;
+ con_width = fbinfo [n].type.fb_width;
+ con_depth = (type == FBTYPE_SUN2BW) ? 1 : 8;
+ for (i = 0; scr_def [i].depth; i++){
+ if ((scr_def [i].resx != con_width) ||
+ (scr_def [i].resy != con_height))
+ continue;
+ if (scr_def [i].depth != con_depth)
+ continue;
+ x_margin = scr_def [i].x_margin;
+ y_margin = scr_def [i].y_margin;
+ chars_per_line = (con_width * con_depth) / 8;
+ skip_bytes = chars_per_line * y_margin + x_margin;
+ ints_per_line = chars_per_line / 4;
+ ints_per_cursor = 14 * ints_per_line;
+ bytes_per_row = CHAR_HEIGHT * chars_per_line;
+ ORIG_VIDEO_COLS = con_width / 8 -
+ 2 * x_margin / con_depth;
+ ORIG_VIDEO_LINES = (con_height - 2 * y_margin) / 16;
+ switch (chars_per_line) {
+ case 1280:
+ if (ORIG_VIDEO_COLS == 144)
+ color_fbuf_offset =
+ color_fbuf_offset_1280_144;
+ break;
+ case 1152:
+ if (ORIG_VIDEO_COLS == 128)
+ color_fbuf_offset =
+ color_fbuf_offset_1152_128;
+ break;
+ case 1024:
+ if (ORIG_VIDEO_COLS == 128)
+ color_fbuf_offset =
+ color_fbuf_offset_1024_128;
+ break;
+ case 800:
+ if (ORIG_VIDEO_COLS == 96)
+ color_fbuf_offset =
+ color_fbuf_offset_800_96;
+ break;
+ case 640:
+ if (ORIG_VIDEO_COLS == 80)
+ color_fbuf_offset =
+ color_fbuf_offset_640_80;
+ break;
+ }
+ break;
+ }
+
+ if (!scr_def [i].depth){
+ x_margin = y_margin = 0;
+ prom_printf ("console: unknown video resolution %dx%d,"
+ " depth %d\n",
+ con_width, con_height, con_depth);
+ prom_halt ();
+ }
+
+ /* P3: I fear this strips 15inch 1024/768 PC-like
+ * monitors out. */
+ if ((linebytes*8) / con_depth != con_width) {
+ prom_printf("console: unusual video, linebytes=%d, "
+ "width=%d, height=%d depth=%d\n",
+ linebytes, con_width, con_height,
+ con_depth);
+ prom_halt ();
+ }
+}
+
+__initfunc(int sbus_console_probe(void))
+{
+ int propl, con_node, default_node = 0;
+ char prop[16];
+ struct linux_sbus_device *sbdp, *sbdprom;
+ struct linux_sbus *sbus;
+ int creator = 0, cg14 = 0;
+ char prom_name[40];
+ int type, card_found = 0;
+ unsigned long con_base;
+ u32 tmp;
+ u32 prom_console_node = 0;
+
+ if(SBus_chain == 0)
+ return -1;
+
+ sbdprom = 0;
+ switch(prom_vers) {
+ case PROM_V0:
+ /* V0 proms are at sun4c only. Can skip many checks. */
+ con_type = FBTYPE_NOTYPE;
+ for_each_sbusdev(sbdp, SBus_chain) {
+ /* If no "address" than it is not the PROM console. */
+ if(sbdp->num_vaddrs) {
+ if(known_card(sbdp->prom_name, v0_known_cards)) {
+ sbdprom = sbdp;
+ strncpy(prom_name, sbdp->prom_name, sizeof (prom_name));
+ break;
+ }
+ }
+ }
+ if(!sbdprom) return -1;
+ for_each_sbusdev(sbdp, SBus_chain) {
+ con_node = sbdp->prom_node;
+
+ if(!strncmp(sbdp->prom_name, "cgsix", 5) ||
+ !strncmp(sbdp->prom_name, "cgthree+", 8)) {
+ type = FBTYPE_SUNFAST_COLOR;
+ } else if(!strncmp(sbdp->prom_name, "cgthree", 7) ||
+ !strncmp(sbdp->prom_name, "cgRDI", 5)) {
+ type = FBTYPE_SUN3COLOR;
+ } else if (!strncmp(sbdp->prom_name, "bwtwo", 5)) {
+ type = FBTYPE_SUN2BW;
+ } else
+ continue;
+ sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp,
+ (uint)sbdp->reg_addrs [0].phys_addr, sbdp->sbus_vaddrs[0], 0,
+ sbdp->my_bus->prom_node);
+ /* XXX HACK */
+ if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5))
+ break;
+ }
+ break;
+ case PROM_V2:
+ case PROM_V3:
+ case PROM_P1275:
+ if (console_fb_path) {
+ char *q, c;
+
+ for (q = console_fb_path; *q && *q != ' '; q++);
+ c = *q;
+ *q = 0;
+ default_node = prom_pathtoinode(console_fb_path);
+ if (default_node) {
+ prom_printf ("Using %s for console\n", console_fb_path);
+ prom_console_node = prom_inst2pkg(prom_stdout);
+ if (prom_console_node == default_node)
+ prom_console_node = 0;
+ }
+ }
+ if (!default_node)
+ default_node = prom_inst2pkg(prom_stdout);
+ propl = prom_getproperty(default_node, "device_type",
+ prop, sizeof (prop));
+ if (propl < 0) {
+ prom_printf ("output-device doesn't have device_type property\n");
+ prom_halt ();
+ } else if (propl != sizeof("display") || strncmp("display", prop, sizeof("display"))) {
+ prop [propl] = 0;
+ prom_printf ("console_probe: output-device is %s"
+ " (not \"display\")\n", prop);
+ prom_halt ();
+ }
+ for_all_sbusdev(sbdp, sbus) {
+ if ((sbdp->prom_node == default_node)
+ && known_card (sbdp->prom_name, known_cards)) {
+ sbdprom = sbdp;
+ break;
+ }
+ }
+ if (sbdprom)
+ card_found = 1;
+ if (!card_found)
+ card_found = cg14 = cg14_present ();
+ if (!card_found){
+ card_found = creator = creator_present ();
+ }
+ if (!card_found){
+ prom_printf ("Could not find a known video card on this machine\n");
+ prom_halt ();
+ }
+
+ for_all_sbusdev(sbdp, sbus) {
+ if (!known_card (sbdp->prom_name, known_cards))
+ continue;
+ con_node = sbdp->prom_node;
+ prom_apply_sbus_ranges (sbdp->my_bus, &sbdp->reg_addrs [0],
+ sbdp->num_registers, sbdp);
+
+ propl = prom_getproperty(con_node, "address", (char *) &tmp, 4);
+ con_base = tmp;
+ if (propl != 4) con_base = 0;
+ propl = prom_getproperty(con_node, "emulation", prom_name, sizeof (prom_name));
+ if (propl < 0 || propl >= sizeof (prom_name)) {
+ /* Early cg3s had no "emulation". */
+ propl = prom_getproperty(con_node, "name", prom_name, sizeof (prom_name));
+ if (propl < 0) {
+ prom_printf("console: no device name!!\n");
+ return -1;
+ }
+ }
+ prom_name [sizeof (prom_name) - 1] = 0;
+ if(!strcmp(prom_name, "cgsix") ||
+ !strcmp(prom_name, "cgthree+")) {
+ type = FBTYPE_SUNFAST_COLOR;
+ } else if(!strcmp(prom_name, "cgthree") ||
+ !strcmp(prom_name, "cgRDI")) {
+ type = FBTYPE_SUN3COLOR;
+ } else if(!strcmp(prom_name, "cgfourteen")) {
+ type = FBTYPE_MDICOLOR;
+ } else if(!strcmp(prom_name, "SUNW,leo")) {
+ type = FBTYPE_SUNLEO;
+ } else if(!strcmp(prom_name, "bwtwo")) {
+ type = FBTYPE_SUN2BW;
+ } else if(!strcmp(prom_name,"SUNW,tcx")){
+ sparc_framebuffer_setup (sbdprom == sbdp, con_node, FBTYPE_TCXCOLOR, sbdp,
+ (uint)sbdp->reg_addrs [10].phys_addr, con_base,
+ prom_console_node == con_node, sbdp->my_bus->prom_node);
+ continue;
+ } else {
+ prom_printf("console: \"%s\" is unsupported\n", prom_name);
+ continue;
+ }
+ sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp,
+ (uint)sbdp->reg_addrs [0].phys_addr, con_base,
+ prom_console_node == con_node, sbdp->my_bus->prom_node);
+ /* XXX HACK */
+ if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5))
+ break;
+ }
+ if (cg14) {
+ sparc_framebuffer_setup (!sbdprom, cg14, FBTYPE_MDICOLOR,
+ 0, 0, 0, prom_console_node == cg14,
+ prom_searchsiblings (prom_getchild (prom_root_node), "obio"));
+ }
+ if (creator){
+ sparc_framebuffer_setup (!sbdprom, creator, FBTYPE_CREATOR,
+ 0, 0, 0, prom_console_node == creator,
+ prom_root_node);
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ if (fbinfo [0].type.fb_type == FBTYPE_NOTYPE) {
+ prom_printf ("Couldn't setup your primary frame buffer.\n");
+ prom_halt ();
+ }
+
+ if (fbinfo [0].blitc)
+ do_accel = 1;
+
+ con_fb_base = (unsigned char *)fbinfo[0].base;
+ if (!con_fb_base){
+ prom_printf ("PROM does not have an 'address' property for this\n"
+ "frame buffer and the Linux drivers do not know how\n"
+ "to map the video of this device\n");
+ prom_halt ();
+ }
+ sbus_install_consops();
+ return fb_init ();
+}
+
+/*
+ * sbus_blitc
+ *
+ * Displays an ASCII character at a specified character cell
+ * position.
+ *
+ * Called from scr_writew() when the destination is
+ * the "shadow" screen
+ */
+static uint
+fontmask_bits[16] = {
+ 0x00000000,
+ 0x000000ff,
+ 0x0000ff00,
+ 0x0000ffff,
+ 0x00ff0000,
+ 0x00ff00ff,
+ 0x00ffff00,
+ 0x00ffffff,
+ 0xff000000,
+ 0xff0000ff,
+ 0xff00ff00,
+ 0xff00ffff,
+ 0xffff0000,
+ 0xffff00ff,
+ 0xffffff00,
+ 0xffffffff
+};
+
+static int sbus_blitc(uint charattr, unsigned long addr)
+{
+ unsigned int fgmask, bgmask;
+ unsigned char attrib;
+ int j, idx;
+ unsigned char *font_row;
+
+ if (do_accel) {
+ (*fbinfo[0].blitc)(charattr,
+ x_margin + (((addr - video_mem_base) % video_size_row)<<2),
+ y_margin + CHAR_HEIGHT * ((addr - video_mem_base) / video_size_row));
+ return 0;
+ }
+
+ /* Invalidate the cursor position if necessary. */
+ idx = (addr - video_mem_base) >> 1;
+
+ attrib = CHARATTR_TO_SUNCOLOR(charattr);
+ font_row = &vga_font[(j = (charattr & 0xff)) << 4];
+
+ switch (con_depth){
+ case 1: {
+ register unsigned char *dst;
+ unsigned long flags;
+
+ dst = (unsigned char *)(((unsigned long)con_fb_base) + FBUF_OFFSET(idx));
+
+ __save_and_cli(flags);
+ if ((!(charattr & 0xf000)) ^ (idx == cursor_pos)) {
+ for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE)
+ *dst = ~(*font_row);
+ } else {
+ for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE)
+ *dst = *font_row;
+ }
+ __restore_flags(flags);
+ break;
+ }
+ case 8: {
+#ifdef ASM_BLITC
+ const int cpl = chars_per_line;
+ /* The register assignment is important here, do not modify without touching the assembly code as well */
+ register unsigned int x1 __asm__("g4"), x2 __asm__("g5"), x3 __asm__("g2"), x4 __asm__("g3"), flags __asm__("g7");
+ register unsigned int *dst __asm__("g1");
+#else
+ const int ipl = ints_per_line;
+ unsigned int data2, data3, data4;
+ unsigned int data, rowbits;
+ register unsigned int *dst;
+ unsigned long flags;
+#endif
+ const uint *fontm_bits = fontmask_bits;
+
+ dst = (unsigned int *)(((unsigned long)con_fb_base) + COLOR_FBUF_OFFSET(idx));
+ if (j == ' ') /* space is quite common, so we optimize a bit */ {
+#ifdef ASM_BLITC
+#define BLITC_SPACE \
+ "\n\t std %%g4, [%%g1]" \
+ "\n\t std %%g4, [%%g1 + %0]" \
+ "\n\t add %%g1, %1, %%g1"
+#define BLITC_SPC \
+ "\n\t std %0, [%1]" \
+ "\n\t std %0, [%1 + %2]"
+
+ x1 = attrib >> 4;
+ x1 |= x1 << 8;
+ x1 |= x1 << 16;
+ x3 = cpl << 1;
+
+ __asm__ __volatile__ (
+ "\n\t mov %2, %3"
+ BLITC_SPACE
+ BLITC_SPACE
+ BLITC_SPACE
+ BLITC_SPACE
+ BLITC_SPACE
+ BLITC_SPACE
+ BLITC_SPACE
+ : : "r" (cpl), "r" (x3), "r" (x1), "r" (x2));
+ __save_and_cli (flags);
+ if (idx != cursor_pos)
+ __asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (dst), "r" (cpl));
+ else
+ __asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (under_cursor), "i" (8));
+ __restore_flags (flags);
+#else
+ bgmask = attrib >> 4;
+ bgmask |= bgmask << 8;
+ bgmask |= bgmask << 16;
+
+ for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) {
+ *dst = bgmask;
+ *(dst+1) = bgmask;
+ }
+ /* Prevent cursor spots left on the screen */
+ __save_and_cli(flags);
+ if (idx != cursor_pos) {
+ *dst = bgmask;
+ *(dst+1) = bgmask;
+ dst += ipl;
+ *dst = bgmask;
+ *(dst+1) = bgmask;
+ } else {
+ under_cursor [0] = bgmask;
+ under_cursor [1] = bgmask;
+ under_cursor [2] = bgmask;
+ under_cursor [3] = bgmask;
+ }
+ __restore_flags(flags);
+#endif
+ } else /* non-space */ {
+ fgmask = attrib & 0x0f;
+ bgmask = attrib >> 4;
+ fgmask |= fgmask << 8;
+ fgmask |= fgmask << 16;
+ bgmask |= bgmask << 8;
+ bgmask |= bgmask << 16;
+
+#ifdef ASM_BLITC
+#define BLITC_INIT \
+ "\n\t ld [%0], %%g2"
+#define BLITC_BODY(ST1,SC1,ST2,SC2) \
+ "\n\t " #ST1 " %%g2, " #SC1 ", %%g7" \
+ "\n\t " #ST2 " %%g2, " #SC2 ", %7" \
+ "\n\t and %%g7, 0x3c, %%g7" \
+ "\n\t and %7, 0x3c, %7" \
+ "\n\t ld [%1 + %%g7], %6" \
+ "\n\t and %6, %2, %%g7" \
+ "\n\t andn %3, %6, %6" \
+ "\n\t or %%g7, %6, %6" \
+ "\n\t ld [%1 + %7], %7" \
+ "\n\t and %7, %2, %%g7" \
+ "\n\t andn %3, %7, %7" \
+ "\n\t or %%g7, %7, %7"
+#define BLITC_BODYEND \
+ "\n\t sll %3, 2, %%g7" \
+ "\n\t srl %3, 2, %3" \
+ "\n\t and %%g7, 0x3c, %%g7" \
+ "\n\t and %3, 0x3c, %3" \
+ "\n\t ld [%0 + %%g7], %4" \
+ "\n\t and %4, %1, %%g7" \
+ "\n\t andn %2, %4, %4" \
+ "\n\t or %%g7, %4, %4" \
+ "\n\t ld [%0 + %3], %3" \
+ "\n\t and %3, %1, %%g7" \
+ "\n\t andn %2, %3, %3" \
+ "\n\t or %%g7, %3, %3"
+#define BLITC_STOREIT \
+ "\n\t std %6, [%5]" \
+ "\n\t add %5, %4, %5" \
+ "\n\t"
+#define BLITC_STORE \
+ "\n\t std %%g4, [%0]" \
+ "\n\t std %%g2, [%0 + %1]"
+
+ for (j = 0; j < 3; j++, font_row+=4) {
+ __asm__ __volatile__ (BLITC_INIT
+ BLITC_BODY(srl, 26, srl, 22)
+ BLITC_STOREIT
+ BLITC_BODY(srl, 18, srl, 14)
+ BLITC_STOREIT
+ BLITC_BODY(srl, 10, srl, 6)
+ BLITC_STOREIT
+ BLITC_BODY(srl, 2, sll, 2)
+ BLITC_STOREIT
+ : : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst),
+ "r" (x1), "r" (x2));
+ }
+ __asm__ __volatile__ (BLITC_INIT
+ BLITC_BODY(srl, 26, srl, 22)
+ BLITC_STOREIT
+ BLITC_BODY(srl, 18, srl, 14)
+ BLITC_STOREIT
+ /* Now prepare date for the 15th line, but don't put it anywhere yet (leave it in g4,g5) */
+ BLITC_BODY(srl, 10, srl, 6)
+ : : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst),
+ "r" (x1), "r" (x2));
+ /* Prepare the data the bottom line (and put it into g2,g3) */
+ __asm__ __volatile__ (BLITC_BODYEND : : "r" (fontm_bits), "r" (fgmask), "r" (bgmask),
+ "r" (x3), "r" (x4));
+ __save_and_cli(flags);
+ if (idx != cursor_pos)
+ __asm__ __volatile__ (BLITC_STORE : : "r" (dst), "r" (cpl));
+ else
+ __asm__ __volatile__ (BLITC_STORE : : "r" (under_cursor), "i" (8));
+ __restore_flags (flags);
+#else
+ for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) {
+ rowbits = *font_row;
+ data = fontm_bits[(rowbits>>4)&0xf];
+ data = (data & fgmask) | (~data & bgmask);
+ *dst = data;
+ data = fontm_bits[rowbits&0xf];
+ data = (data & fgmask) | (~data & bgmask);
+ *(dst+1) = data;
+ }
+ rowbits = *font_row;
+ data = fontm_bits[(rowbits>>4)&0xf];
+ data = (data & fgmask) | (~data & bgmask);
+ data2 = fontm_bits[rowbits&0xf];
+ data2 = (data2 & fgmask) | (~data2 & bgmask);
+ rowbits = font_row[1];
+ data3 = fontm_bits[(rowbits>>4)&0xf];
+ data3 = (data3 & fgmask) | (~data3 & bgmask);
+ data4 = fontm_bits[rowbits&0xf];
+ data4 = (data4 & fgmask) | (~data4 & bgmask);
+
+ /* Prevent cursor spots left on the screen */
+ __save_and_cli(flags);
+
+ if (idx != cursor_pos) {
+ *dst = data;
+ *(dst+1) = data2;
+ dst += ipl;
+ *dst = data3;
+ *(dst+1) = data4;
+ } else {
+ under_cursor [0] = data;
+ under_cursor [1] = data2;
+ under_cursor [2] = data3;
+ under_cursor [3] = data4;
+ }
+
+ __restore_flags(flags);
+#endif
+ }
+ break;
+ } /* case */
+ } /* switch */
+ return (0);
+}
+
+static void sbus_scr_writew(unsigned short val, unsigned short * addr)
+{
+ /*
+ * always deposit the char/attr, then see if it was to "screen" mem.
+ * if so, then render the char/attr onto the real screen.
+ */
+ if (*addr != val) {
+ *addr = val;
+ if ((unsigned long)addr < video_mem_term &&
+ (unsigned long)addr >= video_mem_base &&
+ vt_cons [fg_console]->vc_mode == KD_TEXT)
+ sbus_blitc(val, (unsigned long) addr);
+ }
+}
+
+static unsigned short sbus_scr_readw(unsigned short * addr)
+{
+ return *addr;
+}
+
+static void sbus_memsetw(void * s, unsigned short c, unsigned int count)
+{
+ unsigned short * addr = (unsigned short *) s;
+
+ count >>= 1;
+ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
+ while (count) {
+ count--;
+ *addr++ = c;
+ }
+ return;
+ }
+ if ((unsigned long) addr + count > video_mem_term ||
+ (unsigned long) addr < video_mem_base) {
+ if ((unsigned long) addr + count <= video_mem_term ||
+ (unsigned long) addr > video_mem_base) {
+ while (count) {
+ count--;
+ *addr++ = c;
+ }
+ return;
+ } else {
+ while (count) {
+ count--;
+ scr_writew(c, addr++);
+ }
+ }
+#define GX_SETW (*fbinfo[0].setw)(x_margin + ((xoff - (addr - last)) << 3), y_margin + CHAR_HEIGHT * yoff, c, addr - last);
+ } else if (do_accel) {
+ int yoff = (((long)addr - (long)video_mem_base) >> 1) / video_num_columns;
+ int xoff = (((long)addr - (long)video_mem_base) >> 1) % video_num_columns;
+ unsigned short * last = addr;
+
+ while (count) {
+ count--;
+ if (*addr != c) {
+ if (xoff == video_num_columns) {
+ if (last != addr)
+ GX_SETW
+ xoff = 0;
+ yoff++;
+ last = addr;
+ }
+ *addr++ = c;
+ xoff++;
+ } else {
+ if (last != addr)
+ GX_SETW
+ if (xoff == video_num_columns) {
+ xoff = 0;
+ yoff++;
+ }
+ addr++;
+ xoff++;
+ last = addr;
+ }
+ }
+ if (last != addr)
+ GX_SETW
+ } else {
+ while (count) {
+ count--;
+ if (*addr != c) {
+ sbus_blitc(c, (unsigned long)addr);
+ *addr++ = c;
+ } else
+ addr++;
+ }
+ }
+}
+
+static void sbus_memcpyw(unsigned short *to, unsigned short *from, unsigned int count)
+{
+ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
+ memcpy(to, from, count);
+ return;
+ }
+ if ((unsigned long) to + count > video_mem_term ||
+ (unsigned long) to < video_mem_base) {
+ if ((unsigned long) to + count <= video_mem_term ||
+ (unsigned long) to > video_mem_base)
+ memcpy(to, from, count);
+ else {
+ count >>= 1;
+ while (count) {
+ count--;
+ scr_writew(scr_readw(from++), to++);
+ }
+ }
+#define GX_CPYW (*fbinfo[0].cpyw)(x_margin + ((xoff - (to - last)) << 3), y_margin + CHAR_HEIGHT * yoff, last, to - last);
+ } else if (do_accel) {
+ int yoff = (((long)to - (long)video_mem_base) >> 1) / video_num_columns;
+ int xoff = (((long)to - (long)video_mem_base) >> 1) % video_num_columns;
+ unsigned short * last = to;
+
+ count >>=1;
+ while (count) {
+ count--;
+ if (*to != *from) {
+ if (xoff == video_num_columns) {
+ if (last != to)
+ GX_CPYW
+ xoff = 0;
+ yoff++;
+ last = to;
+ } else if (last != to && (*last & 0xff00) != (*from & 0xff00)) {
+ GX_CPYW
+ last = to;
+ }
+ *to++ = *from++;
+ xoff++;
+ } else {
+ if (last != to)
+ GX_CPYW
+ if (xoff == video_num_columns) {
+ xoff = 0;
+ yoff++;
+ }
+ to++;
+ xoff++;
+ last = to;
+ from++;
+ }
+ }
+ if (last != to)
+ GX_CPYW
+ } else {
+ count >>= 1;
+ while (count) {
+ count--;
+ if (*to != *from) {
+ sbus_blitc(*from, (unsigned long)to);
+ *to++ = *from++;
+ } else {
+ from++;
+ to++;
+ }
+ }
+ }
+}
+
+#undef pos
+int sbus_hw_scursor (struct fbcursor *cursor, fbinfo_t *fb)
+{
+ int op;
+ int i, bytes = 0;
+ struct fbcursor f;
+ char red[2], green[2], blue[2];
+
+ if (copy_from_user (&f, cursor, sizeof(struct fbcursor)))
+ return -EFAULT;
+ op = f.set;
+ if (op & FB_CUR_SETSHAPE){
+ if ((uint) f.size.fbx > fb->cursor.hwsize.fbx)
+ return -EINVAL;
+ if ((uint) f.size.fby > fb->cursor.hwsize.fby)
+ return -EINVAL;
+ if (f.size.fbx > 32)
+ bytes = f.size.fby << 3;
+ else
+ bytes = f.size.fby << 2;
+ }
+ if (op & FB_CUR_SETCMAP){
+ if (f.cmap.index || f.cmap.count != 2)
+ return -EINVAL;
+ if (copy_from_user (red, f.cmap.red, 2) ||
+ copy_from_user (green, f.cmap.green, 2) ||
+ copy_from_user (blue, f.cmap.blue, 2))
+ return -EFAULT;
+ }
+ if (op & FB_CUR_SETCMAP)
+ (*fb->setcursormap) (fb, red, green, blue);
+ if (op & FB_CUR_SETSHAPE){
+ uint u;
+
+ fb->cursor.size = f.size;
+ memset ((void *)&fb->cursor.bits, 0, sizeof (fb->cursor.bits));
+ if (copy_from_user (fb->cursor.bits [0], f.mask, bytes) ||
+ copy_from_user (fb->cursor.bits [1], f.image, bytes))
+ return -EFAULT;
+ if (f.size.fbx <= 32) {
+ u = ~(0xffffffff >> f.size.fbx);
+ for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
+ fb->cursor.bits [0][i] &= u;
+ fb->cursor.bits [1][i] &= fb->cursor.bits [0][i];
+ }
+ } else {
+ u = ~(0xffffffff >> (f.size.fbx - 32));
+ for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
+ fb->cursor.bits [0][2*i+1] &= u;
+ fb->cursor.bits [1][2*i] &= fb->cursor.bits [0][2*i];
+ fb->cursor.bits [1][2*i+1] &= fb->cursor.bits [0][2*i+1];
+ }
+ }
+ (*fb->setcurshape) (fb);
+ }
+ if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){
+ if (op & FB_CUR_SETCUR)
+ fb->cursor.enable = f.enable;
+ if (op & FB_CUR_SETPOS)
+ fb->cursor.cpos = f.pos;
+ if (op & FB_CUR_SETHOT)
+ fb->cursor.chot = f.hot;
+ (*fb->setcursor) (fb);
+ }
+ return 0;
+}
+
+static unsigned char hw_cursor_cmap[2] = { 0, 0xff };
+
+void sbus_hw_hide_cursor (void)
+{
+ fbinfo[0].cursor.enable = 0;
+ (*fbinfo[0].setcursor)(&fbinfo[0]);
+ sbus_hw_cursor_shown = 0;
+}
+
+void sbus_hw_set_cursor (int xoff, int yoff)
+{
+ if (!sbus_hw_cursor_shown) {
+ fbinfo[0].cursor.size.fbx = CHAR_WIDTH;
+ fbinfo[0].cursor.size.fby = CHAR_HEIGHT;
+ fbinfo[0].cursor.chot.fbx = 0;
+ fbinfo[0].cursor.chot.fby = 0;
+ fbinfo[0].cursor.enable = 1;
+ memset (fbinfo[0].cursor.bits, 0, sizeof (fbinfo[0].cursor.bits));
+ fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 2] = 0xff000000;
+ fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 2] = 0xff000000;
+ fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 1] = 0xff000000;
+ fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 1] = 0xff000000;
+ (*fbinfo[0].setcursormap) (&fbinfo[0], hw_cursor_cmap, hw_cursor_cmap, hw_cursor_cmap);
+ (*fbinfo[0].setcurshape) (&fbinfo[0]);
+ sbus_hw_cursor_shown = 1;
+ }
+ fbinfo[0].cursor.cpos.fbx = xoff;
+ fbinfo[0].cursor.cpos.fby = yoff;
+ (*fbinfo[0].setcursor)(&fbinfo[0]);
+}
+
+__initfunc(static void sbus_install_consops(void))
+{
+ suncons_ops.memsetw = sbus_memsetw;
+ suncons_ops.memcpyw = sbus_memcpyw;
+ suncons_ops.scr_writew = sbus_scr_writew;
+ suncons_ops.scr_readw = sbus_scr_readw;
+
+ suncons_ops.get_scrmem = sbus_get_scrmem;
+ suncons_ops.set_scrmem = sbus_set_scrmem;
+
+ suncons_ops.hide_cursor = sbus_hide_cursor;
+ suncons_ops.set_cursor = sbus_set_cursor;
+ suncons_ops.set_get_font = sbus_set_get_font;
+ suncons_ops.con_adjust_height = sbus_con_adjust_height;
+ suncons_ops.set_get_cmap = sbus_set_get_cmap;
+ suncons_ops.set_palette = sbus_set_palette;
+ suncons_ops.set_other_palette = sbus_set_other_palette;
+ suncons_ops.console_restore_palette = sbus_console_restore_palette;
+
+ suncons_ops.con_type_init = sbus_con_type_init;
+ suncons_ops.con_type_init_finish = sbus_con_type_init_finish;
+
+ suncons_ops.clear_screen = sbus_clear_screen;
+ suncons_ops.render_screen = sbus_render_screen;
+ suncons_ops.clear_margin = sbus_clear_margin;
+}
diff --git a/drivers/sbus/char/su.c b/drivers/sbus/char/su.c
new file mode 100644
index 000000000..6cc5801cd
--- /dev/null
+++ b/drivers/sbus/char/su.c
@@ -0,0 +1,678 @@
+/* $Id: su.c,v 1.3 1997/09/03 11:54:56 ecd Exp $
+ * su.c: Small serial driver for keyboard/mouse interface on Ultra/AX
+ *
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
+ *
+ * This is mainly a very stripped down version of drivers/char/serial.c,
+ * credits go to authors mentioned therein.
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/interrupt.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+
+#include <asm/oplib.h>
+#include <asm/io.h>
+#include <asm/ebus.h>
+
+#include "sunserial.h"
+#include "sunkbd.h"
+#include "sunmouse.h"
+
+static char *serial_name = "kbd/mouse serial driver";
+static char *serial_version = "1.0";
+
+/* Set of debugging defines */
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+
+/* We are on a NS PC87303 clocked with 24.0 MHz, which results
+ * in a UART clock of 1.8462 MHz.
+ */
+#define BAUD_BASE (1846200 / 16)
+
+struct su_struct {
+ int magic;
+ unsigned long port;
+ int baud_base;
+ int type;
+ int irq;
+ int flags;
+ unsigned char IER;
+ unsigned char MCR;
+ int line;
+ int cflag;
+ int kbd_node;
+ int ms_node;
+ char name[16];
+};
+
+static struct su_struct su_table[] = {
+ { 0, 0, BAUD_BASE, PORT_UNKNOWN },
+ { 0, 0, BAUD_BASE, PORT_UNKNOWN }
+};
+
+#define NR_PORTS (sizeof(su_table) / sizeof(struct su_struct))
+
+static void autoconfig(struct su_struct * info);
+static void change_speed(struct su_struct *info);
+
+/*
+ * Here we define the default xmit fifo size used for each type of
+ * UART
+ */
+static struct serial_uart_config uart_config[] = {
+ { "unknown", 1, 0 },
+ { "8250", 1, 0 },
+ { "16450", 1, 0 },
+ { "16550", 1, 0 },
+ { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },
+ { "cirrus", 1, 0 },
+ { "ST16650", 1, UART_CLEAR_FIFO |UART_STARTECH },
+ { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO |
+ UART_STARTECH },
+ { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
+ { 0, 0}
+};
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
+
+static inline
+unsigned int su_inb(struct su_struct *info, unsigned long offset)
+{
+ return inb(info->port + offset);
+}
+
+static inline void
+su_outb(struct su_struct *info, unsigned long offset, int value)
+{
+ outb(value, info->port + offset);
+}
+
+static inline void
+receive_chars(struct su_struct *info, struct pt_regs *regs)
+{
+ unsigned char status = 0;
+ unsigned char ch;
+
+ do {
+ ch = su_inb(info, UART_RX);
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("DR%02x:%02x...", ch, status);
+#endif
+
+ if (info->kbd_node) {
+ if(ch == SUNKBD_RESET) {
+ l1a_state.kbd_id = 1;
+ l1a_state.l1_down = 0;
+ } else if(l1a_state.kbd_id) {
+ l1a_state.kbd_id = 0;
+ } else if(ch == SUNKBD_L1) {
+ l1a_state.l1_down = 1;
+ } else if(ch == (SUNKBD_L1|SUNKBD_UP)) {
+ l1a_state.l1_down = 0;
+ } else if(ch == SUNKBD_A && l1a_state.l1_down) {
+ /* whee... */
+ batten_down_hatches();
+ /* Continue execution... */
+ l1a_state.l1_down = 0;
+ l1a_state.kbd_id = 0;
+ return;
+ }
+ sunkbd_inchar(ch, regs);
+ } else {
+ sun_mouse_inbyte(ch);
+ }
+
+ status = su_inb(info, UART_LSR);
+ } while (status & UART_LSR_DR);
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+static void
+su_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct su_struct *info = (struct su_struct *)dev_id;
+ unsigned char status;
+
+ /*
+ * We might share interrupts with ps2kbd/ms driver,
+ * in case we want to use the 16550A as general serial
+ * driver in the presence of ps2 devices, so do a
+ * sanity check here, needs to be done in ps2kbd/ms
+ * driver, too.
+ */
+ if (!info || info->magic != SERIAL_MAGIC)
+ return;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("su_interrupt(%d)...", irq);
+#endif
+
+ if (su_inb(info, UART_IIR) & UART_IIR_NO_INT)
+ return;
+
+ status = su_inb(info, UART_LSR);
+#ifdef SERIAL_DEBUG_INTR
+ printk("status = %x...", status);
+#endif
+ if (status & UART_LSR_DR)
+ receive_chars(info, regs);
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("end.\n");
+#endif
+}
+
+static int
+startup(struct su_struct * info)
+{
+ unsigned long flags;
+ int retval=0;
+
+ save_flags(flags); cli();
+
+ if (info->flags & ASYNC_INITIALIZED) {
+ goto errout;
+ }
+
+ if (!info->port || !info->type) {
+ goto errout;
+ }
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("starting up su%d (irq %x)...", info->line, info->irq);
+#endif
+
+ if (info->type == PORT_16750)
+ su_outb(info, UART_IER, 0);
+
+ /*
+ * Clear the FIFO buffers and disable them
+ * (they will be reenabled in change_speed())
+ */
+ if (uart_config[info->type].flags & UART_CLEAR_FIFO)
+ su_outb(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT));
+
+ /*
+ * At this point there's no way the LSR could still be 0xFF;
+ * if it is, then bail out, because there's likely no UART
+ * here.
+ */
+ if (su_inb(info, UART_LSR) == 0xff) {
+ retval = -ENODEV;
+ goto errout;
+ }
+
+ /*
+ * Allocate the IRQ if necessary
+ */
+ retval = request_irq(info->irq, su_interrupt, SA_SHIRQ,
+ info->name, info);
+ if (retval)
+ goto errout;
+
+ /*
+ * Clear the interrupt registers.
+ */
+ su_inb(info, UART_RX);
+ su_inb(info, UART_IIR);
+ su_inb(info, UART_MSR);
+
+ /*
+ * Now, initialize the UART
+ */
+ su_outb(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
+
+ info->MCR = UART_MCR_OUT2;
+ su_outb(info, UART_MCR, info->MCR);
+
+ /*
+ * Finally, enable interrupts
+ */
+ info->IER = UART_IER_RLSI | UART_IER_RDI;
+ su_outb(info, UART_IER, info->IER); /* enable interrupts */
+
+ /*
+ * And clear the interrupt registers again for luck.
+ */
+ su_inb(info, UART_LSR);
+ su_inb(info, UART_RX);
+ su_inb(info, UART_IIR);
+ su_inb(info, UART_MSR);
+
+ /*
+ * and set the speed of the serial port
+ */
+ change_speed(info);
+
+ info->flags |= ASYNC_INITIALIZED;
+ restore_flags(flags);
+ return 0;
+
+errout:
+ restore_flags(flags);
+ return retval;
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void
+change_speed(struct su_struct *info)
+{
+ unsigned char cval, fcr = 0;
+ int quot = 0;
+ unsigned long flags;
+ int i;
+
+ /* byte size and parity */
+ switch (info->cflag & CSIZE) {
+ case CS5: cval = 0x00; break;
+ case CS6: cval = 0x01; break;
+ case CS7: cval = 0x02; break;
+ case CS8: cval = 0x03; break;
+ /* Never happens, but GCC is too dumb to figure it out */
+ default: cval = 0x00; break;
+ }
+ if (info->cflag & CSTOPB) {
+ cval |= 0x04;
+ }
+ if (info->cflag & PARENB) {
+ cval |= UART_LCR_PARITY;
+ }
+ if (!(info->cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+ if (info->cflag & CMSPAR)
+ cval |= UART_LCR_SPAR;
+#endif
+
+ /* Determine divisor based on baud rate */
+ i = info->cflag & CBAUD;
+ if (i & CBAUDEX) {
+ i &= ~CBAUDEX;
+ if (i < 1 || i > 4)
+ info->cflag &= ~CBAUDEX;
+ else
+ i += 15;
+ }
+ if (!quot) {
+ if (baud_table[i] == 134)
+ /* Special case since 134 is really 134.5 */
+ quot = (2 * info->baud_base / 269);
+ else if (baud_table[i])
+ quot = info->baud_base / baud_table[i];
+ /* If the quotient is ever zero, default to 1200 bps */
+ if (!quot)
+ quot = info->baud_base / 1200;
+ }
+
+ /* Set up FIFO's */
+ if (uart_config[info->type].flags & UART_USE_FIFO)
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+
+ su_outb(info, UART_IER, info->IER);
+
+ save_flags(flags); cli();
+ su_outb(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
+ su_outb(info, UART_DLL, quot & 0xff); /* LS of divisor */
+ su_outb(info, UART_DLM, quot >> 8); /* MS of divisor */
+ if (info->type == PORT_16750)
+ su_outb(info, UART_FCR, fcr); /* set fcr */
+ su_outb(info, UART_LCR, cval); /* reset DLAB */
+ if (info->type != PORT_16750)
+ su_outb(info, UART_FCR, fcr); /* set fcr */
+ restore_flags(flags);
+}
+
+static void
+su_put_char(unsigned char c)
+{
+ struct su_struct *info = su_table;
+ int lsr;
+
+ if (!info->kbd_node)
+ ++info;
+
+ do {
+ lsr = inb(info->port + UART_LSR);
+ } while (!(lsr & UART_LSR_THRE));
+
+ /* Send the character out. */
+ su_outb(info, UART_TX, c);
+}
+
+static void
+su_change_mouse_baud(int baud)
+{
+ struct su_struct *info = su_table;
+
+ if (!info->ms_node)
+ ++info;
+ if (!info)
+ return;
+
+ info->cflag &= ~(CBAUDEX | CBAUD);
+ switch(baud) {
+ case 1200:
+ info->cflag |= B1200;
+ break;
+ case 2400:
+ info->cflag |= B2400;
+ break;
+ case 4800:
+ info->cflag |= B4800;
+ break;
+ case 9600:
+ info->cflag |= B9600;
+ break;
+ default:
+ printk("su_change_mouse_baud: unknown baud rate %d, "
+ "defaulting to 1200\n", baud);
+ info->cflag |= 1200;
+ break;
+ }
+ change_speed(info);
+}
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static inline void
+show_su_version(void)
+{
+ printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
+}
+
+/*
+ * This routine is called by su_init() to initialize a specific serial
+ * port. It determines what type of UART chip this serial port is
+ * using: 8250, 16450, 16550, 16550A. The important question is
+ * whether or not this UART is a 16550A or not, since this will
+ * determine whether or not we can use its FIFO features or not.
+ */
+static void
+autoconfig(struct su_struct *info)
+{
+ unsigned char status1, status2, scratch, scratch2;
+ struct linux_ebus_device *dev;
+ struct linux_ebus *ebus;
+ unsigned long flags;
+
+ for_all_ebusdev(dev, ebus) {
+ if (!strncmp(dev->prom_name, "su", 2)) {
+ if (dev->prom_node == info->kbd_node)
+ break;
+ if (dev->prom_node == info->ms_node)
+ break;
+ }
+ }
+ if (!dev)
+ return;
+
+ info->port = dev->base_address[0];
+ if (check_region(info->port, 8))
+ return;
+
+ info->irq = dev->irqs[0];
+
+#ifdef DEBUG_SERIAL_OPEN
+ printk("Found 'su' at %016lx IRQ %08x\n",
+ dev->base_address[0], dev->irqs[0]);
+#endif
+
+ info->magic = SERIAL_MAGIC;
+
+ save_flags(flags); cli();
+
+ /*
+ * Do a simple existence test first; if we fail this, there's
+ * no point trying anything else.
+ *
+ * 0x80 is used as a nonsense port to prevent against false
+ * positives due to ISA bus float. The assumption is that
+ * 0x80 is a non-existent port; which should be safe since
+ * include/asm/io.h also makes this assumption.
+ */
+ scratch = su_inb(info, UART_IER);
+ su_outb(info, UART_IER, 0);
+ scratch2 = su_inb(info, UART_IER);
+ su_outb(info, UART_IER, scratch);
+ if (scratch2) {
+ restore_flags(flags);
+ return; /* We failed; there's nothing here */
+ }
+
+ scratch = su_inb(info, UART_MCR);
+ su_outb(info, UART_MCR, UART_MCR_LOOP | scratch);
+ scratch2 = su_inb(info, UART_MSR);
+ su_outb(info, UART_MCR, UART_MCR_LOOP | 0x0A);
+ status1 = su_inb(info, UART_MSR) & 0xF0;
+ su_outb(info, UART_MCR, scratch);
+ su_outb(info, UART_MSR, scratch2);
+ if (status1 != 0x90) {
+ restore_flags(flags);
+ return;
+ }
+
+ scratch2 = su_inb(info, UART_LCR);
+ su_outb(info, UART_LCR, 0xBF); /* set up for StarTech test */
+ su_outb(info, UART_EFR, 0); /* EFR is the same as FCR */
+ su_outb(info, UART_LCR, 0);
+ su_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+ scratch = su_inb(info, UART_IIR) >> 6;
+ switch (scratch) {
+ case 0:
+ info->type = PORT_16450;
+ break;
+ case 1:
+ info->type = PORT_UNKNOWN;
+ break;
+ case 2:
+ info->type = PORT_16550;
+ break;
+ case 3:
+ info->type = PORT_16550A;
+ break;
+ }
+ if (info->type == PORT_16550A) {
+ /* Check for Startech UART's */
+ su_outb(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+ if (su_inb(info, UART_EFR) == 0) {
+ info->type = PORT_16650;
+ } else {
+ su_outb(info, UART_LCR, 0xBF);
+ if (su_inb(info, UART_EFR) == 0)
+ info->type = PORT_16650V2;
+ }
+ }
+ if (info->type == PORT_16550A) {
+ /* Check for TI 16750 */
+ su_outb(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+ su_outb(info, UART_FCR,
+ UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+ scratch = su_inb(info, UART_IIR) >> 5;
+ if (scratch == 7) {
+ su_outb(info, UART_LCR, 0);
+ su_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+ scratch = su_inb(info, UART_IIR) >> 5;
+ if (scratch == 6)
+ info->type = PORT_16750;
+ }
+ su_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+ }
+ su_outb(info, UART_LCR, scratch2);
+ if (info->type == PORT_16450) {
+ scratch = su_inb(info, UART_SCR);
+ su_outb(info, UART_SCR, 0xa5);
+ status1 = su_inb(info, UART_SCR);
+ su_outb(info, UART_SCR, 0x5a);
+ status2 = su_inb(info, UART_SCR);
+ su_outb(info, UART_SCR, scratch);
+
+ if ((status1 != 0xa5) || (status2 != 0x5a))
+ info->type = PORT_8250;
+ }
+
+ if (info->type == PORT_UNKNOWN) {
+ restore_flags(flags);
+ return;
+ }
+
+ sprintf(info->name, "su(%s)", info->ms_node ? "mouse" : "kbd");
+ request_region(info->port, 8, info->name);
+
+ /*
+ * Reset the UART.
+ */
+ su_outb(info, UART_MCR, 0x00);
+ su_outb(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT));
+ su_inb(info, UART_RX);
+
+ restore_flags(flags);
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+__initfunc(int su_init(void))
+{
+ int i;
+ struct su_struct *info;
+
+ show_su_version();
+
+ for (i = 0, info = su_table; i < NR_PORTS; i++, info++) {
+ info->line = i;
+ if (info->kbd_node)
+ info->cflag = B1200 | CS8 | CREAD;
+ else
+ info->cflag = B4800 | CS8 | CREAD;
+
+ autoconfig(info);
+ if (info->type == PORT_UNKNOWN)
+ continue;
+
+ printk(KERN_INFO "%s at %16lx (irq = %08x) is a %s\n",
+ info->name, info->port, info->irq,
+ uart_config[info->type].name);
+
+ startup(info);
+ if (info->kbd_node)
+ keyboard_zsinit(su_put_char);
+ else
+ sun_mouse_zsinit();
+ }
+ return 0;
+}
+
+__initfunc(int su_probe (unsigned long *memory_start))
+{
+ struct su_struct *info = su_table;
+ int node, enode, sunode;
+ int kbnode = 0, msnode = 0;
+ int devices = 0;
+ char prop[128];
+ int len;
+
+ /*
+ * Get the nodes for keyboard and mouse from 'aliases'...
+ */
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(node, "aliases");
+ if (!node)
+ return -ENODEV;
+
+ len = prom_getproperty(node, "keyboard", prop, sizeof(prop));
+ if (len > 0)
+ kbnode = prom_pathtoinode(prop);
+ if (!kbnode)
+ return -ENODEV;
+
+ len = prom_getproperty(node, "mouse", prop, sizeof(prop));
+ if (len > 0)
+ msnode = prom_pathtoinode(prop);
+ if (!msnode)
+ return -ENODEV;
+
+ /*
+ * Find matching EBus nodes...
+ */
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(node, "pci");
+
+ /*
+ * For each PCI bus...
+ */
+ while (node) {
+ enode = prom_getchild(node);
+ enode = prom_searchsiblings(enode, "ebus");
+
+ /*
+ * For each EBus on this PCI...
+ */
+ while (enode) {
+ sunode = prom_getchild(enode);
+ sunode = prom_searchsiblings(sunode, "su");
+
+ /*
+ * For each 'su' on this EBus...
+ */
+ while (sunode) {
+ /*
+ * Does it match?
+ */
+ if (sunode == kbnode) {
+ info->kbd_node = kbnode;
+ ++info;
+ ++devices;
+ }
+ if (sunode == msnode) {
+ info->ms_node = msnode;
+ ++info;
+ ++devices;
+ }
+
+ /*
+ * Found everything we need?
+ */
+ if (devices == NR_PORTS)
+ goto found;
+
+ sunode = prom_getsibling(sunode);
+ sunode = prom_searchsiblings(sunode, "su");
+ }
+ enode = prom_getsibling(enode);
+ enode = prom_searchsiblings(enode, "ebus");
+ }
+ node = prom_getsibling(node);
+ node = prom_searchsiblings(node, "pci");
+ }
+ return -ENODEV;
+
+found:
+ sunserial_setinitfunc(memory_start, su_init);
+ rs_ops.rs_change_mouse_baud = su_change_mouse_baud;
+ return 0;
+}
diff --git a/drivers/sbus/char/suncons.c b/drivers/sbus/char/suncons.c
index 111f65f97..7e88b4688 100644
--- a/drivers/sbus/char/suncons.c
+++ b/drivers/sbus/char/suncons.c
@@ -1,706 +1,259 @@
-/* $Id: suncons.c,v 1.67 1997/07/20 05:59:42 davem Exp $
- *
- * suncons.c: Sun SparcStation console support.
- *
- * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
- * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- *
- * Added font loading Nov/21, Miguel de Icaza (miguel@nuclecu.unam.mx)
- * Added render_screen and faster scrolling Nov/27, miguel
- * Added console palette code for cg6 Dec/13/95, miguel
- * Added generic frame buffer support Dec/14/95, miguel
- * Added cgsix and bwtwo drivers Jan/96, miguel
- * Added 4m, and cg3 driver Feb/96, miguel
- * Fixed the cursor on color displays Feb/96, miguel.
- * Cleaned up the detection code, generic 8bit depth display
- * code, Mar/96 miguel
- * Hacked support for cg14 video cards -- Apr/96, miguel.
- * Color support for cg14 video cards -- May/96, miguel.
- * Code split, Dave Redman, May/96
- * Be more VT change friendly, May/96, miguel.
- * Support for hw cursor and graphics acceleration, Jun/96, jj.
- * Added TurboGX+ detection (cgthree+), Aug/96, Iain Lea (iain@sbs.de)
- * Added TCX support (8/24bit), Aug/96, jj.
- * Support for multiple framebuffers, Sep/96, jj.
- * Fix bwtwo inversion and handle inverse monochrome cells in
- * sun_blitc, Nov/96, ecd.
- * Fix sun_blitc and screen size on displays other than 1152x900,
- * 128x54 chars, Nov/96, jj.
- * Fix cursor spots left on some non-accelerated fbs, changed
- * software cursor to be like the hw one, Nov/96, jj.
- *
- * Much of this driver is derived from the DEC TGA driver by
- * Jay Estabrook who has done a nice job with the console
- * driver abstraction btw.
- *
- * We try to make everything a power of two if possible to
- * speed up the bit blit. Doing multiplies, divides, and
- * remainder routines end up calling software library routines
- * since not all Sparcs have the hardware to do it.
- *
- * TODO:
- * do not blank the screen when frame buffer is mapped.
+/* $Id: suncons.c,v 1.73 1997/08/25 07:50:33 jj Exp $
+ * suncons.c: Sparc platform console generic layer.
*
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
*/
-
#include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/kd.h>
-#include <linux/malloc.h>
-#include <linux/major.h>
-#include <linux/mm.h>
#include <linux/types.h>
-#include <linux/version.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/vt.h>
+#include <linux/selection.h>
#include <linux/proc_fs.h>
-#include <asm/system.h>
-#include <asm/uaccess.h>
#include <asm/page.h>
#include <asm/pgtable.h>
-#include <asm/bitops.h>
-#include <asm/oplib.h>
#include <asm/sbus.h>
#include <asm/fbio.h>
-#include <asm/io.h>
-#include <asm/smp.h>
-
-#include <linux/kbd_kern.h>
-#include <linux/vt_kern.h>
-#include <linux/consolemap.h>
-#include <linux/selection.h>
-#include <linux/console_struct.h>
#include "fb.h"
-#define cmapsz 8192
-
-#include "suncons_font.h"
#include <asm/linux_logo.h>
fbinfo_t *fbinfo;
int fbinfos;
+unsigned int linux_logo_colors __initdata = LINUX_LOGO_COLORS;
+char logo_banner[] __initdata = linux_logo_banner;
-#define ASM_BLITC
+extern struct console vt_console_driver;
-int sun_hw_cursor_shown = 0;
+/* Infrastructure. */
-void sun_hw_hide_cursor(void);
-void sun_hw_set_cursor(int,int);
+static void nop_memsetw(void *s, unsigned short c, unsigned int count)
+{
+}
-extern void register_console(void (*proc)(const char *));
-extern void console_print(const char *);
-extern void putconsxy(int, char *);
-extern unsigned char vga_font[];
-extern int serial_console;
-char *console_fb_path = NULL; /* Set in setup.c */
+static void nop_memcpyw(unsigned short *to, unsigned short *from, unsigned int count)
+{
+}
-/* The following variables describe a Sparc console. */
+static void nop_scr_writew(unsigned short val, unsigned short *addr)
+{
+}
-/* Screen dimensions and color depth. */
-static int con_depth, con_width, con_height, con_type;
+static unsigned short nop_scr_readw(unsigned short *addr)
+{
+ return 0;
+}
-/* Base address of first line. */
-static unsigned char *con_fb_base;
+static void nop_get_scrmem(int a)
+{
+}
-/* Screen parameters: we compute those at startup to make the code faster */
-static int chars_per_line; /* number of bytes per line */
-static int ints_per_line; /* number of ints per line */
-static int ints_per_cursor; /* 14 * ints_per_line */
-static int skip_bytes; /* number of bytes we skip for the y margin + x_margin */
-static int x_margin, y_margin; /* the x and y margins */
-static int bytes_per_row; /* bytes used by one screen line (of 16 scan lines) */
-int sun_prom_console_id = 0;
+static void nop_set_scrmem(int a, long b)
+{
+}
-/* Functions used by the SPARC dependent console code
- * to perform the fb_restore_palette function.
- */
-void (*fb_restore_palette)(fbinfo_t *fbinfo);
-void set_palette (void);
+static void nop_set_origin(unsigned short offset)
+{
+}
- /* Our screen looks like at 1152 X 900:
- *
- * 0,0
- * ------------------------------------------------------------------
- * | ^^^^^^^^^^^ |
- * | 18 y-pixels |
- * | ^^^^^^^^^^^ |
- * 13 | <-64 pixels->| <-- 128 8x16 characters --> | <-64 pixels-> |
- * ....
- * 54 chars from top to bottom
- * ....
- * 888 | <-64 pixels->| <-- 128 8x16 characters --> | <-64 pixels-> |
- * | ^^^^^^^^^^^ |
- * | 18 y-pixels |
- * | ^^^^^^^^^^^ |
- * ------------------------------------------------------------------
- */
-/* First for MONO displays. */
-#define SCREEN_WIDTH 1152 /* Screen width in pixels */
-#define SCREEN_HEIGHT 900 /* Screen height in pixels */
-#define CHARS_PER_LINE 144 /* Make this empirical for speed */
-#define NICE_Y_MARGIN 18 /* We skip 18 y-pixels at top/bottom */
-#define NICE_X_MARGIN 8 /* We skip 64 x-pixels at left/right */
-#define FBUF_TOP_SKIP 2592 /* Empirical, (CHARS_PER_LINE * NICE_Y_MARGIN) */
-#define CHAR_HEIGHT 16
-#define ONE_ROW 2304 /* CHARS_PER_LINE * CHAR_HEIGHT */
-
-/* Now we have this, to compute the base frame buffer position
- * for a new character to be rendered. 1 and 8 bit depth.
- */
-#define FBUF_OFFSET(cindex) \
- (((FBUF_TOP_SKIP) + (((cindex)>>7) * ONE_ROW)) + \
- ((NICE_X_MARGIN) + (((cindex)&127))))
+static void nop_hide_cursor(void)
+{
+}
+static void nop_set_cursor(int c)
+{
+}
-#define COLOR_FBUF_OFFSET(cindex) (*color_fbuf_offset)(cindex)
+static int nop_set_get_font(char *a, int b, int c)
+{
+ return 0;
+}
-/* These four routines are optimizations for the _generic routine for
- * the most common cases.
- * I guess doing twice sll is much faster than doing .mul, sra faster
- * than doing .div, and the disadvantage that someone has to call it
- * (it cannot be inline) runs away, 'cause otherwise it would have to
- * call .mul anyway.
- * The shifting + addition only routines won't eat any stack frame :))
- * Names come from width, screen_num_columns.
- */
-static int
-color_fbuf_offset_1280_144 (int cindex)
+static int nop_con_adjust_height(unsigned long arg)
{
- register int i = (cindex/144);
- /* (1280 * CHAR_HEIGHT) == 101.0000.0000.0000 */
- return skip_bytes + (i << 14) + (i << 12) + ((cindex % 144) << 3);
+ return -EINVAL;
}
-static int
-color_fbuf_offset_1152_128 (int cindex)
+static int nop_set_get_cmap(unsigned char *arg, int a)
{
- register int i = (cindex>>7);
- /* (1152 * CHAR_HEIGHT) == 100.1000.0000.0000 */
- return skip_bytes + (i << 14) + (i << 11) + ((cindex & 127) << 3);
+ return 0;
}
-static int
-color_fbuf_offset_1024_128 (int cindex)
+static void nop_set_palette(void)
{
- register int i = (cindex>>7);
- /* (1024 * CHAR_HEIGHT) == 100.0000.0000.0000 */
- return skip_bytes + (i << 14) + ((cindex & 127) << 3);
}
-static int
-color_fbuf_offset_800_96 (int cindex)
+static void nop_set_other_palette(int a)
{
- register int i = (cindex / 96);
- /* (800 * CHAR_HEIGHT) == 11.0010.0000.0000 */
- return skip_bytes + (i<<13) + (i<<12) + (i<<9) + ((cindex % 96)<<3);
}
-static int
-color_fbuf_offset_640_80 (int cindex)
+static void nop_console_restore_palette(void)
{
- register int i = (cindex/80);
- /* (640 * CHAR_HEIGHT) == 10.1000.0000.0000 */
- return skip_bytes + (i << 13) + (i << 11) + ((cindex % 80) << 3);
}
-
-static int
-color_fbuf_offset_generic (int cindex)
+
+static unsigned long nop_con_type_init(unsigned long mem_start,
+ const char **display_desc)
{
- return skip_bytes + (cindex / video_num_columns) * bytes_per_row + ((cindex % video_num_columns) << 3);
+ prom_printf("YIEEE: nop_con_type_init called!\n");
+ return mem_start;
}
-static int (*color_fbuf_offset)(int) = color_fbuf_offset_generic;
-
-static int do_accel = 0;
+static void nop_con_type_init_finish(void)
+{
+ prom_printf("YIEEE: nop_con_type_init_finish called!\n");
+}
-void
-__set_origin(unsigned short offset)
+static void nop_vesa_blank(void)
{
- /*
- * should not be called, but if so, do nothing...
- */
}
-/* For the cursor, we just invert the 8x16 block at the cursor
- * location. Easy enough...
- *
- * Hide the cursor from view, during blanking, usually...
- */
-static int cursor_pos = -1;
+static void nop_vesa_unblank(void)
+{
+}
-static unsigned int under_cursor[4];
+static void nop_set_vesa_blanking(const unsigned long arg)
+{
+}
-void
-hide_cursor(void)
+static void nop_vesa_powerdown(void)
{
- unsigned long flags;
- int j;
+}
- if (fbinfo[0].setcursor) {
- sun_hw_hide_cursor();
- return;
- }
-
- if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
- return; /* Don't paint anything on fb which is not ours,
- but turn off the hw cursor in such case */
-
- __save_and_cli(flags);
-
- if(cursor_pos == -1) {
- __restore_flags (flags);
- return;
- }
- switch (con_depth){
- case 1: {
- unsigned char *dst;
- dst = (unsigned char *)((unsigned long)con_fb_base +
- FBUF_OFFSET(cursor_pos));
- for(j = 0; j < CHAR_HEIGHT; j++, dst += CHARS_PER_LINE)
- *dst = ~(*dst);
- break;
- }
- case 8: {
- unsigned int *dst;
-
- dst = (unsigned int *)((unsigned long)con_fb_base +
- COLOR_FBUF_OFFSET(cursor_pos)) + ints_per_cursor;
- dst[0] = under_cursor[0];
- dst[1] = under_cursor[1];
- dst[ints_per_line] = under_cursor[2];
- dst[ints_per_line+1] = under_cursor[3];
- break;
- }
- default:
- break;
- }
- cursor_pos = -1;
- __restore_flags(flags);
+static void nop_clear_screen(void)
+{
}
-void
-set_cursor(int currcons)
+static void nop_render_screen(void)
{
- int j, idx, oldpos;
- unsigned long flags;
+}
- if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
- return;
+static void nop_clear_margin(void)
+{
+}
-#if 0
-/* This is a nop anyway */
- if (__real_origin != __origin)
- __set_origin(__real_origin);
-#endif
-
- if (fbinfo[0].setcursor) {
- if (!deccm)
- hide_cursor();
- else {
- idx = (pos - video_mem_base) >> 1;
-
- sun_hw_set_cursor(x_margin + ((idx % video_num_columns) << 3), y_margin + ((idx / video_num_columns) * CHAR_HEIGHT));
- }
- return;
- }
+struct suncons_operations suncons_ops = {
+ nop_memsetw,
+ nop_memcpyw,
+ nop_scr_writew,
+ nop_scr_readw,
+ nop_get_scrmem,
+ nop_set_scrmem,
+ nop_set_origin,
+ nop_hide_cursor,
+ nop_set_cursor,
+ nop_set_get_font,
+ nop_con_adjust_height,
+ nop_set_get_cmap,
+ nop_set_palette,
+ nop_set_other_palette,
+ nop_console_restore_palette,
+ nop_con_type_init,
+ nop_con_type_init_finish,
+ nop_vesa_blank,
+ nop_vesa_unblank,
+ nop_set_vesa_blanking,
+ nop_vesa_powerdown,
+ nop_clear_screen,
+ nop_render_screen,
+ nop_clear_margin
+};
- __save_and_cli(flags);
+/* Entry points. */
- idx = (pos - video_mem_base) >> 1;
- oldpos = cursor_pos;
- if (!deccm) {
- hide_cursor ();
- __restore_flags (flags);
- return;
- }
- cursor_pos = idx;
- switch (con_depth){
- case 1: {
- unsigned char *dst, *opos;
-
- dst = (unsigned char *)((unsigned long)con_fb_base + FBUF_OFFSET(idx));
- opos = (unsigned char *)((unsigned long)con_fb_base + FBUF_OFFSET(oldpos));
- if(oldpos != -1) {
- /* Restore what was at the old position */
- for(j=0; j < CHAR_HEIGHT; j++, opos += CHARS_PER_LINE) {
- *opos = ~*opos;
- }
- }
- for(j=0; j < 16; j++, dst+=CHARS_PER_LINE) {
- *dst = ~*dst;
- }
- break;
- }
- case 8: {
- unsigned int *dst, *opos;
- dst = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(idx)) + ints_per_cursor;
-
- if(oldpos != -1) {
- opos = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(oldpos)) + ints_per_cursor;
- opos[0] = under_cursor[0];
- opos[1] = under_cursor[1];
- opos[ints_per_line] = under_cursor[2];
- opos[ints_per_line+1] = under_cursor[3];
- }
- under_cursor[0] = dst[0];
- under_cursor[1] = dst[1];
- under_cursor[2] = dst[ints_per_line];
- under_cursor[3] = dst[ints_per_line+1];
- dst[0] = 0x00000000;
- dst[1] = 0x00000000;
- dst[ints_per_line] = 0x00000000;
- dst[ints_per_line+1] = 0x00000000;
- break;
- }
- default:
- }
- __restore_flags(flags);
+void get_scrmem(int a)
+{
+ suncons_ops.get_scrmem(a);
}
-/*
- * Render the current screen
- * Only used at startup and when switching from KD_GRAPHICS to KD_TEXT
- * to avoid the caching that is being done in selection.h
- */
-void
-render_screen(void)
+void set_scrmem(int a, long b)
{
- int count;
- unsigned short *contents;
-
- count = video_num_columns * video_num_lines;
- contents = (unsigned short *) video_mem_base;
-
- for (;count--; contents++)
- sun_blitc (*contents, (unsigned long) contents);
+ suncons_ops.set_scrmem(a, b);
}
-__initfunc(void serial_finish_init(void (*printfunc)(const char *, int)))
+void __set_origin(unsigned short offset)
{
- char buffer[2048];
-
- sprintf (buffer, linux_serial_image, UTS_RELEASE);
- (*printfunc)(buffer, strlen(buffer));
+ suncons_ops.set_origin(offset);
}
-__initfunc(void con_type_init_finish(void))
+void hide_cursor(void)
{
- int i, cpu;
- char *p = con_fb_base + skip_bytes;
- char q[2] = {0,5};
- int currcons = 0;
- unsigned short *ush;
-
- if (serial_console)
- return;
- if (con_type == FBTYPE_SUNLEO) {
- int rects [4];
-
- rects [0] = 0;
- rects [1] = 0;
- rects [2] = con_width;
- rects [3] = con_height;
- (*fbinfo[0].fill)(reverse_color_table[0], 1, rects);
- return; /* Dunno how to display logo on leo/zx yet */
- }
- if (con_depth == 8 && fbinfo[0].loadcmap) {
- for (i = 0; i < LINUX_LOGO_COLORS; i++) {
- fbinfo[0].color_map CM(i+32,0) = linux_logo_red [i];
- fbinfo[0].color_map CM(i+32,1) = linux_logo_green [i];
- fbinfo[0].color_map CM(i+32,2) = linux_logo_blue [i];
- }
- (*fbinfo [0].loadcmap)(&fbinfo [0], 0, LINUX_LOGO_COLORS + 32);
- for (i = 0; i < 80; i++, p += chars_per_line){
- for (cpu = 0; cpu < linux_num_cpus; cpu++){
- memcpy (p + (cpu * 88), linux_logo + 80 * i, 80);
- }
- }
- } else if (con_depth == 1) {
- for (i = 0; i < 80; i++, p += chars_per_line)
- memcpy (p, linux_logo_bw + 10 * i, 10);
- }
- putconsxy(0, q);
- ush = (unsigned short *) video_mem_base + video_num_columns * 2 + 20 + 11 * (linux_num_cpus - 1);
-
- p = linux_logo_banner;
- for (; *p; p++, ush++) {
- *ush = (attr << 8) + *p;
- sun_blitc (*ush, (unsigned long) ush);
- }
- for (i = 0; i < 5; i++) {
- ush = (unsigned short *) video_mem_base + i * video_num_columns;
- memset (ush, 0, 20);
- }
+ suncons_ops.hide_cursor();
}
-__initfunc(unsigned long
-con_type_init(unsigned long kmem_start, const char **display_desc))
+void set_cursor(int currcons)
{
- can_do_color = (con_type != FBTYPE_SUN2BW);
-
- video_type = VIDEO_TYPE_SUN;
- *display_desc = "SUN";
-
- if (!serial_console) {
- /* If we fall back to PROM then our output have to remain readable. */
- prom_putchar('\033'); prom_putchar('['); prom_putchar('H');
-
- /*
- * fake the screen memory with some CPU memory
- */
- video_mem_base = kmem_start;
- kmem_start += video_screen_size;
- video_mem_term = kmem_start;
- }
- return kmem_start;
+ suncons_ops.set_cursor(currcons);
}
-/*
- * NOTE: get_scrmem() and set_scrmem() are here only because
- * the VGA version of set_scrmem() has some direct VGA references.
- */
-void
-get_scrmem(int currcons)
+int set_get_font(char *arg, int set, int ch512)
{
- memcpyw((unsigned short *)vc_scrbuf[currcons],
- (unsigned short *)origin, video_screen_size);
- origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
- scr_end = video_mem_end = video_mem_start + video_screen_size;
- pos = origin + y*video_size_row + (x<<1);
+ return suncons_ops.set_get_font(arg, set, ch512);
}
-void
-set_scrmem(int currcons, long offset)
+int con_adjust_height(unsigned long fontheight)
{
- if (video_mem_term - video_mem_base < offset + video_screen_size)
- offset = 0;
- memcpyw((unsigned short *)(video_mem_base + offset),
- (unsigned short *) origin, video_screen_size);
- video_mem_start = video_mem_base;
- video_mem_end = video_mem_term;
- origin = video_mem_base + offset;
- scr_end = origin + video_screen_size;
- pos = origin + y*video_size_row + (x<<1);
+ return suncons_ops.con_adjust_height(fontheight);
}
-/*
- * PIO_FONT support.
- */
-int
-set_get_font(char * arg, int set, int ch512)
+int set_get_cmap(unsigned char *arg, int set)
{
- int i, line;
-
- if (!arg)
- return -EINVAL;
-
- /* download the current font */
- if (!set){
- if(clear_user(arg, cmapsz))
- return -EFAULT;
- for (i = 0; i < 256; i++) {
- for (line = 0; line < CHAR_HEIGHT; line++) {
- unsigned char value = vga_font[i];
-
- /* Access checked by the above clear_user */
- __put_user_ret (value, (arg + (i * 32 + line)),
- -EFAULT);
- }
- }
- return 0;
- }
-
- /* set the font */
-
- if (verify_area (VERIFY_READ, arg, 256 * CHAR_HEIGHT)) return -EFAULT;
- for (i = 0; i < 256; i++) {
- for (line = 0; line < CHAR_HEIGHT; line++){
- unsigned char value;
- __get_user_ret(value, (arg + (i * 32 + line)),-EFAULT);
- vga_font [i*CHAR_HEIGHT + line] = value;
- }
- }
- return 0;
+ return suncons_ops.set_get_cmap(arg, set);
}
-/*
- * Adjust the screen to fit a font of a certain height
- *
- * Returns < 0 for error, 0 if nothing changed, and the number
- * of lines on the adjusted console if changed.
- *
- * for now, we only support the built-in font...
- */
-int
-con_adjust_height(unsigned long fontheight)
+void set_palette(void)
{
- return -EINVAL;
+ suncons_ops.set_palette();
}
-int
-set_get_cmap(unsigned char * arg, int set)
+void set_other_palette(int n)
{
- int i;
-
- if(set)
- i = VERIFY_READ;
- else
- i = VERIFY_WRITE;
- if(verify_area(i, arg, (16 * 3 * sizeof(unsigned char))))
- return -EFAULT;
- for (i=0; i<16; i++) {
- if (set) {
- __get_user_ret(default_red[i], (arg+0),-EFAULT);
- __get_user_ret(default_grn[i], (arg+1),-EFAULT);
- __get_user_ret(default_blu[i], (arg+2),-EFAULT);
- } else {
- __put_user_ret(default_red[i], (arg+0),-EFAULT);
- __put_user_ret(default_grn[i], (arg+1),-EFAULT);
- __put_user_ret(default_blu[i], (arg+2),-EFAULT);
- }
- arg += 3;
- }
- if (set) {
- for (i=0; i<MAX_NR_CONSOLES; i++)
- if (vc_cons_allocated(i)) {
- int j, k ;
- for (j=k=0; j<16; j++) {
- vc_cons[i].d->vc_palette[k++] = default_red[j];
- vc_cons[i].d->vc_palette[k++] = default_grn[j];
- vc_cons[i].d->vc_palette[k++] = default_blu[j];
- }
- }
- set_palette();
- }
-
- return 0;
+ suncons_ops.set_other_palette(n);
}
-void
-sun_clear_screen(void)
+void console_restore_palette(void)
{
- if (fbinfo[0].fill) {
- int rects [4];
-
- rects [0] = 0;
- rects [1] = 0;
- rects [2] = con_width;
- rects [3] = con_height;
- (*fbinfo[0].fill)(reverse_color_table[0], 1, rects);
- } else if (fbinfo[0].base && fbinfo[0].base_depth)
- memset (con_fb_base,
- (con_depth == 1) ? ~(0) : reverse_color_table[0],
- (con_depth * con_height * con_width) / 8);
- /* also clear out the "shadow" screen memory */
- memset((char *)video_mem_base, 0, (video_mem_term - video_mem_base));
- cursor_pos = -1;
+ suncons_ops.console_restore_palette();
}
-void
-sun_clear_fb(int n)
+unsigned long con_type_init(unsigned long mem_start, const char **disp_desc)
{
- if (!n) sun_clear_screen ();
-#if 0
-/* This makes in some configurations serious problems.
- * Who cares if other screens are cleared?
- */
- else if (fbinfo[n].fill) {
- int rects [4];
-
- rects [0] = 0;
- rects [1] = 0;
- rects [2] = fbinfo[n].type.fb_width;
- rects [3] = fbinfo[n].type.fb_height;
- (*fbinfo[n].fill)(reverse_color_table[0], 1, rects);
- }
-#endif
- else if (fbinfo[n].base && fbinfo[n].base_depth) {
- memset((void *)fbinfo[n].base,
- (fbinfo[n].base_depth == 1) ?
- ~(0) : reverse_color_table[0],
- (fbinfo[n].base_depth * fbinfo[n].type.fb_height
- * fbinfo[n].type.fb_width) / 8);
- }
+ return suncons_ops.con_type_init(mem_start, disp_desc);
}
-void
-sun_clear_margin(void)
+void con_type_init_finish(void)
{
- int h, he, i;
- unsigned char *p;
-
- if (fbinfo[0].fill) {
- int rects [16];
-
- memset (rects, 0, sizeof (rects));
- rects [2] = con_width;
- rects [3] = y_margin;
- rects [5] = y_margin;
- rects [6] = x_margin;
- rects [7] = con_height;
- rects [8] = con_width - x_margin;
- rects [9] = y_margin;
- rects [10] = con_width;
- rects [11] = con_height;
- rects [12] = x_margin;
- rects [13] = con_height - y_margin;
- rects [14] = con_width - x_margin;
- rects [15] = con_height;
- (*fbinfo[0].fill)(reverse_color_table[0], 4, rects);
- } else {
- memset (con_fb_base,
- (con_depth == 1) ? ~(0) : reverse_color_table[0],
- skip_bytes - (x_margin<<1));
- memset (con_fb_base + chars_per_line * con_height
- - skip_bytes + (x_margin<<1),
- (con_depth == 1) ? ~(0) : reverse_color_table[0],
- skip_bytes - (x_margin<<1));
- he = con_height - 2 * y_margin;
- i = 2 * x_margin;
- if (con_depth == 1) {
- for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0;
- h <= he; p += chars_per_line, h++)
- memset (p, ~(0), i);
- } else {
- for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0;
- h <= he; p += chars_per_line, h++)
- memset (p, reverse_color_table[0], i);
- }
- }
- if (fbinfo [0].switch_from_graph)
- (*fbinfo [0].switch_from_graph)();
+ suncons_ops.con_type_init_finish();
}
-/*
- * dummy routines for the VESA blanking code, which is VGA only,
- * so we don't have to carry that stuff around for the Sparc...
- */
void vesa_blank(void)
{
+ suncons_ops.vesa_blank();
}
void vesa_unblank(void)
{
+ suncons_ops.vesa_unblank();
}
void set_vesa_blanking(const unsigned long arg)
{
+ suncons_ops.set_vesa_blanking(arg);
}
void vesa_powerdown(void)
{
+ suncons_ops.vesa_powerdown();
+}
+
+void render_screen(void)
+{
+ suncons_ops.render_screen();
}
/*
@@ -711,53 +264,14 @@ unsigned char reverse_color_table[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0
};
-static unsigned char sparc_color_table[] = {
+unsigned char sparc_color_table[] = {
15, 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11
};
-/* Call the frame buffer routine for setting the palette */
-void
-set_palette (void)
-{
- if (console_blanked || vt_cons [fg_console]->vc_mode == KD_GRAPHICS)
- return;
-
- if (fbinfo [0].loadcmap){
- int i, j;
-
- /* First keep color_map with the palette colors */
- for (i = 0; i < 16; i++){
- j = sparc_color_table [i];
- fbinfo[0].color_map CM(i,0) = default_red [j];
- fbinfo[0].color_map CM(i,1) = default_grn [j];
- fbinfo[0].color_map CM(i,2) = default_blu [j];
- }
- (*fbinfo [0].loadcmap)(&fbinfo [0], 0, 16);
- }
-}
-
-void
-set_other_palette (int n)
-{
- if (!n) {
- set_palette ();
- return;
- }
- if (fbinfo [n].loadcmap){
- fbinfo[n].color_map CM(0,0) = 0;
- fbinfo[n].color_map CM(0,1) = 0;
- fbinfo[n].color_map CM(0,2) = 0;
- (*fbinfo [n].loadcmap)(&fbinfo [n], 0, 1);
- }
-}
+/* Probing engine. */
-/* Called when returning to prom */
-void
-console_restore_palette (void)
-{
- if (fb_restore_palette)
- (*fb_restore_palette) (&fbinfo[0]);
-}
+char *console_fb_path = NULL;
+void (*fb_restore_palette)(fbinfo_t *fbinfo) = NULL;
unsigned long
get_phys (unsigned long addr)
@@ -765,978 +279,98 @@ get_phys (unsigned long addr)
return __get_phys(addr);
}
-int
-get_iospace (unsigned long addr)
-{
- return __get_iospace(addr);
-}
+extern int sbus_console_probe(void);
+extern int serial_console;
-__initfunc(unsigned long sun_cg_postsetup(fbinfo_t *fb, unsigned long start_mem))
+__initfunc(static unsigned long finish_console_init(unsigned long memory_start))
{
- fb->color_map = (char *)start_mem;
- return start_mem + 256*3;
-}
-
-static char *known_cards [] __initdata = {
- "cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", "SUNW,tcx",
- "cgfourteen", "SUNW,leo", "SUNW,ffb", 0
-};
-static char *v0_known_cards [] __initdata = {
- "cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", 0
-};
+ static int confinish_has_run = 0;
+ int i, j;
-__initfunc(static int known_card (char *name, char **known_cards))
-{
- int i;
+ if(confinish_has_run != 0) {
+ printk("finish_console_init: Someone tries to run me twice.\n");
+ return memory_start;
+ }
+ for(i = FRAME_BUFFERS; i > 1; i--)
+ if(fbinfo[i - 1].type.fb_type != FBTYPE_NOTYPE)
+ break;
+ fbinfos = i;
- for (i = 0; known_cards [i]; i++)
- if (strcmp (name, known_cards [i]) == 0)
- return 1;
- return 0;
-}
+ for(j = 0; j < i; j++)
+ if (fbinfo[j].postsetup)
+ memory_start = (*fbinfo[j].postsetup)(fbinfo+j, memory_start);
-static struct {
- int depth;
- int resx, resy;
- int x_margin, y_margin;
-} scr_def [] = {
- { 8, 1280, 1024, 64, 80 },
- { 8, 1152, 1024, 64, 80 },
- { 8, 1152, 900, 64, 18 },
- { 8, 1024, 768, 0, 0 },
- { 8, 800, 600, 16, 12 },
- { 8, 640, 480, 0, 0 },
- { 1, 1152, 900, 8, 18 },
- { 0 },
-};
+ suncons_ops.clear_screen();
-__initfunc(static int cg14_present(void))
-{
- int root, n;
+ for(j = 1; j < i; j++)
+ if(fbinfo[j].type.fb_type != FBTYPE_NOTYPE) {
+ fbinfo[j].clear_fb(j);
+ fbinfo[j].set_other_palette(j);
+ }
+#if defined(CONFIG_PROC_FS) && \
+ ( defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) )
+ for (j = 0; j < i; j++)
+ if (fbinfo[j].type.fb_type != FBTYPE_NOTYPE)
+ proc_openprom_regdev (&fbinfo[j].proc_entry);
+#endif
- root = prom_getchild (prom_root_node);
- if ((n = prom_searchsiblings (root, "obio")) == 0)
- return 0;
+ confinish_has_run = 1;
- n = prom_getchild (n);
- if ((n = prom_searchsiblings (n, "cgfourteen")) == 0)
- return 0;
- return n;
+ return memory_start;
}
-__initfunc(static int creator_present (void))
-{
- int root, n;
-
-#ifdef __sparc_v9__
- root = prom_getchild (prom_root_node);
- if ((n = prom_searchsiblings (root, "SUNW,ffb")) == 0)
- return 0;
- return n;
-#else
- return 0;
+#ifdef CONFIG_PCI
+extern void pci_console_inithook(void);
#endif
-}
-__initfunc(static void
- sparc_framebuffer_setup(int primary, int con_node,
- int type, struct linux_sbus_device *sbdp,
- uint base, unsigned long con_base, int prom_fb,
- int parent_node))
+__initfunc(unsigned long sun_console_init(unsigned long memory_start))
{
- static int frame_buffers = 1;
- int n, i;
- int linebytes;
- uint io = 0;
- char *p;
-
- if (primary)
- n = 0;
- else {
- if (frame_buffers == FRAME_BUFFERS)
- return; /* Silently ignore */
- n = frame_buffers++;
- }
-
- if (prom_fb) sun_prom_console_id = n;
-
- if (sbdp)
- io = sbdp->reg_addrs [0].which_io;
-
- /* Fill in common fb information */
- fbinfo [n].type.fb_type = type;
- fbinfo [n].real_type = type;
- fbinfo [n].prom_node = con_node;
- memset (&(fbinfo [n].emulations), 0xff, sizeof (fbinfo [n].emulations));
- fbinfo [n].type.fb_height = prom_getintdefault(con_node, "height", 900);
- fbinfo [n].type.fb_width = prom_getintdefault(con_node, "width", 1152);
- fbinfo [n].type.fb_depth = (type == FBTYPE_SUN2BW) ? 1 : 8;
- linebytes = prom_getint(con_node, "linebytes");
- if (linebytes == -1) linebytes = fbinfo [n].type.fb_width;
- fbinfo [n].type.fb_size = PAGE_ALIGN((linebytes) * (fbinfo [n].type.fb_height));
- fbinfo [n].space = io;
- fbinfo [n].blanked = 0;
- if (con_base >= PAGE_OFFSET)
- fbinfo [n].base = con_base;
- else
- fbinfo [n].base = 0;
- fbinfo [n].cursor.hwsize.fbx = 32;
- fbinfo [n].cursor.hwsize.fby = 32;
- fbinfo [n].proc_entry.node = parent_node;
- fbinfo [n].proc_entry.rdev = MKDEV(GRAPHDEV_MAJOR, n);
- fbinfo [n].proc_entry.mode = S_IFCHR | S_IRUSR | S_IWUSR;
- prom_getname (con_node, fbinfo [n].proc_entry.name, 32 - 3);
- p = strchr (fbinfo [n].proc_entry.name, 0);
- sprintf (p, ":%d", n);
-
- /* Should be filled in for supported video cards */
- fbinfo [n].mmap = 0;
- fbinfo [n].loadcmap = 0;
- fbinfo [n].ioctl = 0;
- fbinfo [n].reset = 0;
- fbinfo [n].blank = 0;
- fbinfo [n].unblank = 0;
- fbinfo [n].setcursor = 0;
- fbinfo [n].base_depth = fbinfo [n].type.fb_depth;
-
- /* Per card setup */
- switch (fbinfo [n].type.fb_type){
-#ifdef SUN_FB_CGTHREE
- case FBTYPE_SUN3COLOR:
- cg3_setup (&fbinfo [n], n, base, io, sbdp);
- break;
-#endif
-#ifdef SUN_FB_TCX
- case FBTYPE_TCXCOLOR:
- tcx_setup (&fbinfo [n], n, con_node, base, sbdp);
- break;
-#endif
-#ifdef SUN_FB_CGSIX
- case FBTYPE_SUNFAST_COLOR:
- cg6_setup (&fbinfo [n], n, base, io);
- break;
-#endif
-#ifdef SUN_FB_BWTWO
- case FBTYPE_SUN2BW:
- bwtwo_setup (&fbinfo [n], n, base, io, sbdp);
- break;
-#endif
-#ifdef SUN_FB_CGFOURTEEN
- case FBTYPE_MDICOLOR:
- cg14_setup (&fbinfo [n], n, con_node, base, io);
- break;
-#endif
-#ifdef SUN_FB_LEO
- case FBTYPE_SUNLEO:
- leo_setup (&fbinfo [n], n, base, io);
- break;
-#endif
-#if defined(SUN_FB_CREATOR) && defined(__sparc_v9__)
- case FBTYPE_CREATOR:
- creator_setup (&fbinfo [n], n, con_node, base, io);
- break;
-#endif
- default:
- fbinfo [n].type.fb_type = FBTYPE_NOTYPE;
- return;
- }
+ int i;
- if (n)
- return;
-
- /* Code below here is just executed for the first frame buffer */
- con_type = type;
- con_height = fbinfo [n].type.fb_height;
- con_width = fbinfo [n].type.fb_width;
- con_depth = (type == FBTYPE_SUN2BW) ? 1 : 8;
- for (i = 0; scr_def [i].depth; i++){
- if ((scr_def [i].resx != con_width) ||
- (scr_def [i].resy != con_height))
- continue;
- if (scr_def [i].depth != con_depth)
- continue;
- x_margin = scr_def [i].x_margin;
- y_margin = scr_def [i].y_margin;
- chars_per_line = (con_width * con_depth) / 8;
- skip_bytes = chars_per_line * y_margin + x_margin;
- ints_per_line = chars_per_line / 4;
- ints_per_cursor = 14 * ints_per_line;
- bytes_per_row = CHAR_HEIGHT * chars_per_line;
- ORIG_VIDEO_COLS = con_width / 8 -
- 2 * x_margin / con_depth;
- ORIG_VIDEO_LINES = (con_height - 2 * y_margin) / 16;
- switch (chars_per_line) {
- case 1280:
- if (ORIG_VIDEO_COLS == 144)
- color_fbuf_offset =
- color_fbuf_offset_1280_144;
- break;
- case 1152:
- if (ORIG_VIDEO_COLS == 128)
- color_fbuf_offset =
- color_fbuf_offset_1152_128;
- break;
- case 1024:
- if (ORIG_VIDEO_COLS == 128)
- color_fbuf_offset =
- color_fbuf_offset_1024_128;
- break;
- case 800:
- if (ORIG_VIDEO_COLS == 96)
- color_fbuf_offset =
- color_fbuf_offset_800_96;
- break;
- case 640:
- if (ORIG_VIDEO_COLS == 80)
- color_fbuf_offset =
- color_fbuf_offset_640_80;
- break;
- }
- break;
- }
-
- if (!scr_def [i].depth){
- x_margin = y_margin = 0;
- prom_printf ("console: unknown video resolution %dx%d,"
- " depth %d\n",
- con_width, con_height, con_depth);
- prom_halt ();
- }
-
- /* P3: I fear this strips 15inch 1024/768 PC-like
- * monitors out. */
- if ((linebytes*8) / con_depth != con_width) {
- prom_printf("console: unusual video, linebytes=%d, "
- "width=%d, height=%d depth=%d\n",
- linebytes, con_width, con_height,
- con_depth);
- prom_halt ();
- }
-}
+ /* Nothing to do in this case. */
+ if(serial_console)
+ return memory_start;
-__initfunc(static int sparc_console_probe(void))
-{
- int propl, con_node, default_node = 0, i;
- char prop[16];
- struct linux_sbus_device *sbdp, *sbdprom;
- struct linux_sbus *sbus;
- int creator = 0, cg14 = 0;
- char prom_name[40];
- int type, card_found = 0;
- unsigned long con_base;
- u32 tmp;
- u32 prom_console_node = 0;
+ fbinfo = (fbinfo_t *)memory_start;
+ memset(fbinfo, 0, FRAME_BUFFERS * sizeof(fbinfo_t));
+ memory_start += (FRAME_BUFFERS * sizeof(fbinfo_t));
+ fbinfos = 0;
for (i = 0; i < FRAME_BUFFERS; i++)
fbinfo [i].type.fb_type = FBTYPE_NOTYPE;
- sbdprom = 0;
- switch(prom_vers) {
- case PROM_V0:
- /* V0 proms are at sun4c only. Can skip many checks. */
- con_type = FBTYPE_NOTYPE;
- if(SBus_chain == 0) {
- prom_printf("SBUS chain is NULL, bailing out...\n");
- prom_halt();
- }
- for_each_sbusdev(sbdp, SBus_chain) {
- /* If no "address" than it is not the PROM console. */
- if(sbdp->num_vaddrs) {
- if(known_card(sbdp->prom_name, v0_known_cards)) {
- sbdprom = sbdp;
- strncpy(prom_name, sbdp->prom_name, sizeof (prom_name));
- break;
- }
- }
- }
- if(!sbdprom) return -1;
- for_each_sbusdev(sbdp, SBus_chain) {
- con_node = sbdp->prom_node;
-
- if(!strncmp(sbdp->prom_name, "cgsix", 5) ||
- !strncmp(sbdp->prom_name, "cgthree+", 8)) {
- type = FBTYPE_SUNFAST_COLOR;
- } else if(!strncmp(sbdp->prom_name, "cgthree", 7) ||
- !strncmp(sbdp->prom_name, "cgRDI", 5)) {
- type = FBTYPE_SUN3COLOR;
- } else if (!strncmp(sbdp->prom_name, "bwtwo", 5)) {
- type = FBTYPE_SUN2BW;
- } else
- continue;
- sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp,
- (uint)sbdp->reg_addrs [0].phys_addr, sbdp->sbus_vaddrs[0], 0,
- sbdp->my_bus->prom_node);
- /* XXX HACK */
- if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5))
- break;
- }
- break;
- case PROM_V2:
- case PROM_V3:
- case PROM_P1275:
- if (console_fb_path) {
- char *q, c;
-
- for (q = console_fb_path; *q && *q != ' '; q++);
- c = *q;
- *q = 0;
- default_node = prom_pathtoinode(console_fb_path);
- if (default_node) {
- prom_printf ("Using %s for console\n", console_fb_path);
- prom_console_node = prom_inst2pkg(prom_stdout);
- if (prom_console_node == default_node)
- prom_console_node = 0;
- }
- }
- if (!default_node)
- default_node = prom_inst2pkg(prom_stdout);
- propl = prom_getproperty(default_node, "device_type",
- prop, sizeof (prop));
- if (propl < 0) {
- prom_printf ("output-device doesn't have device_type property\n");
- prom_halt ();
- } else if (propl != sizeof("display") || strncmp("display", prop, sizeof("display"))) {
- prop [propl] = 0;
- prom_printf ("console_probe: output-device is %s"
- " (not \"display\")\n", prop);
- prom_halt ();
- }
- for_all_sbusdev(sbdp, sbus) {
- if ((sbdp->prom_node == default_node)
- && known_card (sbdp->prom_name, known_cards)) {
- sbdprom = sbdp;
- break;
- }
- }
- if (sbdprom)
- card_found = 1;
- if (!card_found)
- card_found = cg14 = cg14_present ();
- if (!card_found){
- card_found = creator = creator_present ();
- }
- if (!card_found){
- prom_printf ("Could not find a known video card on this machine\n");
- prom_halt ();
- }
-
- for_all_sbusdev(sbdp, sbus) {
- if (!known_card (sbdp->prom_name, known_cards))
- continue;
- con_node = sbdp->prom_node;
- prom_apply_sbus_ranges (sbdp->my_bus, &sbdp->reg_addrs [0],
- sbdp->num_registers, sbdp);
-
- propl = prom_getproperty(con_node, "address", (char *) &tmp, 4);
- con_base = tmp;
- if (propl != 4) con_base = 0;
- propl = prom_getproperty(con_node, "emulation", prom_name, sizeof (prom_name));
- if (propl < 0 || propl >= sizeof (prom_name)) {
- /* Early cg3s had no "emulation". */
- propl = prom_getproperty(con_node, "name", prom_name, sizeof (prom_name));
- if (propl < 0) {
- prom_printf("console: no device name!!\n");
- return -1;
- }
- }
- prom_name [sizeof (prom_name) - 1] = 0;
- if(!strcmp(prom_name, "cgsix") ||
- !strcmp(prom_name, "cgthree+")) {
- type = FBTYPE_SUNFAST_COLOR;
- } else if(!strcmp(prom_name, "cgthree") ||
- !strcmp(prom_name, "cgRDI")) {
- type = FBTYPE_SUN3COLOR;
- } else if(!strcmp(prom_name, "cgfourteen")) {
- type = FBTYPE_MDICOLOR;
- } else if(!strcmp(prom_name, "SUNW,leo")) {
- type = FBTYPE_SUNLEO;
- } else if(!strcmp(prom_name, "bwtwo")) {
- type = FBTYPE_SUN2BW;
- } else if(!strcmp(prom_name,"SUNW,tcx")){
- sparc_framebuffer_setup (sbdprom == sbdp, con_node, FBTYPE_TCXCOLOR, sbdp,
- (uint)sbdp->reg_addrs [10].phys_addr, con_base,
- prom_console_node == con_node, sbdp->my_bus->prom_node);
- continue;
- } else {
- prom_printf("console: \"%s\" is unsupported\n", prom_name);
- continue;
- }
- sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp,
- (uint)sbdp->reg_addrs [0].phys_addr, con_base,
- prom_console_node == con_node, sbdp->my_bus->prom_node);
- /* XXX HACK */
- if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5))
- break;
- }
- if (cg14) {
- sparc_framebuffer_setup (!sbdprom, cg14, FBTYPE_MDICOLOR,
- 0, 0, 0, prom_console_node == cg14,
- prom_searchsiblings (prom_getchild (prom_root_node), "obio"));
- }
- if (creator){
- sparc_framebuffer_setup (!sbdprom, creator, FBTYPE_CREATOR,
- 0, 0, 0, prom_console_node == creator,
- prom_root_node);
- }
- break;
- default:
- return -1;
- }
-
- if (fbinfo [0].type.fb_type == FBTYPE_NOTYPE) {
- prom_printf ("Couldn't setup your primary frame buffer.\n");
- prom_halt ();
- }
-
- if (fbinfo [0].blitc)
- do_accel = 1;
-
- con_fb_base = (unsigned char *)fbinfo[0].base;
- if (!con_fb_base){
- prom_printf ("PROM does not have an 'address' property for this\n"
- "frame buffer and the Linux drivers do not know how\n"
- "to map the video of this device\n");
- prom_halt ();
- }
- return fb_init ();
-}
-/* video init code, called from within the SBUS bus scanner at
- * boot time.
- */
-__initfunc(unsigned long sun_console_init(unsigned long memory_start))
-{
- int i, j;
- if(serial_console)
+ if(sbus_console_probe()) {
+#ifdef CONFIG_PCI
+ pci_console_inithook();
return memory_start;
-
- fbinfo = (fbinfo_t *)memory_start;
- memset (fbinfo, 0, FRAME_BUFFERS * sizeof (fbinfo_t));
- if(sparc_console_probe()) {
- prom_printf("Could not probe console, bailing out...\n");
+#else
+ /* XXX We need to write PROM console fallback driver... */
+ prom_printf("Could not probe SBUS console, bailing out...\n");
prom_halt();
- }
-
- sun_clear_screen();
- for (i = FRAME_BUFFERS; i > 1; i--)
- if (fbinfo[i - 1].type.fb_type != FBTYPE_NOTYPE) break;
- fbinfos = i;
- memory_start = memory_start + i * sizeof (fbinfo_t);
- for (j = 0; j < i; j++)
- if (fbinfo[j].postsetup)
- memory_start = (*fbinfo[j].postsetup)(fbinfo+j, memory_start);
- for (j = 1; j < i; j++)
- if (fbinfo[j].type.fb_type != FBTYPE_NOTYPE) {
- sun_clear_fb(j);
- set_other_palette(j);
- }
-#if defined(CONFIG_PROC_FS) && ( defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) )
- for (j = 0; j < i; j++)
- if (fbinfo[j].type.fb_type != FBTYPE_NOTYPE)
- proc_openprom_regdev (&fbinfo[j].proc_entry);
#endif
- return memory_start;
-}
-
-/*
- * sun_blitc
- *
- * Displays an ASCII character at a specified character cell
- * position.
- *
- * Called from scr_writew() when the destination is
- * the "shadow" screen
- */
-static uint
-fontmask_bits[16] = {
- 0x00000000,
- 0x000000ff,
- 0x0000ff00,
- 0x0000ffff,
- 0x00ff0000,
- 0x00ff00ff,
- 0x00ffff00,
- 0x00ffffff,
- 0xff000000,
- 0xff0000ff,
- 0xff00ff00,
- 0xff00ffff,
- 0xffff0000,
- 0xffff00ff,
- 0xffffff00,
- 0xffffffff
-};
-
-int
-sun_blitc(uint charattr, unsigned long addr)
-{
- unsigned int fgmask, bgmask;
- unsigned char attrib;
- int j, idx;
- unsigned char *font_row;
-
- if (do_accel) {
- (*fbinfo[0].blitc)(charattr,
- x_margin + (((addr - video_mem_base) % video_size_row)<<2),
- y_margin + CHAR_HEIGHT * ((addr - video_mem_base) / video_size_row));
- return 0;
- }
-
- /* Invalidate the cursor position if necessary. */
- idx = (addr - video_mem_base) >> 1;
-
- attrib = CHARATTR_TO_SUNCOLOR(charattr);
- font_row = &vga_font[(j = (charattr & 0xff)) << 4];
-
- switch (con_depth){
- case 1: {
- register unsigned char *dst;
- unsigned long flags;
-
- dst = (unsigned char *)(((unsigned long)con_fb_base) + FBUF_OFFSET(idx));
-
- __save_and_cli(flags);
- if ((!(charattr & 0xf000)) ^ (idx == cursor_pos)) {
- for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE)
- *dst = ~(*font_row);
- } else {
- for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE)
- *dst = *font_row;
- }
- __restore_flags(flags);
- break;
}
- case 8: {
-#ifdef ASM_BLITC
- const int cpl = chars_per_line;
- /* The register assignment is important here, do not modify without touching the assembly code as well */
- register unsigned int x1 __asm__("g4"), x2 __asm__("g5"), x3 __asm__("g2"), x4 __asm__("g3"), flags __asm__("g7");
- register unsigned int *dst __asm__("g1");
-#else
- const int ipl = ints_per_line;
- unsigned int data2, data3, data4;
- unsigned int data, rowbits;
- register unsigned int *dst;
- unsigned long flags;
-#endif
- const uint *fontm_bits = fontmask_bits;
-
- dst = (unsigned int *)(((unsigned long)con_fb_base) + COLOR_FBUF_OFFSET(idx));
- if (j == ' ') /* space is quite common, so we optimize a bit */ {
-#ifdef ASM_BLITC
-#define BLITC_SPACE \
- "\n\t std %%g4, [%%g1]" \
- "\n\t std %%g4, [%%g1 + %0]" \
- "\n\t add %%g1, %1, %%g1"
-#define BLITC_SPC \
- "\n\t std %0, [%1]" \
- "\n\t std %0, [%1 + %2]"
-
- x1 = attrib >> 4;
- x1 |= x1 << 8;
- x1 |= x1 << 16;
- x3 = cpl << 1;
-
- __asm__ __volatile__ (
- "\n\t mov %2, %3"
- BLITC_SPACE
- BLITC_SPACE
- BLITC_SPACE
- BLITC_SPACE
- BLITC_SPACE
- BLITC_SPACE
- BLITC_SPACE
- : : "r" (cpl), "r" (x3), "r" (x1), "r" (x2));
- __save_and_cli (flags);
- if (idx != cursor_pos)
- __asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (dst), "r" (cpl));
- else
- __asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (under_cursor), "i" (8));
- __restore_flags (flags);
-#else
- bgmask = attrib >> 4;
- bgmask |= bgmask << 8;
- bgmask |= bgmask << 16;
-
- for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) {
- *dst = bgmask;
- *(dst+1) = bgmask;
- }
- /* Prevent cursor spots left on the screen */
- __save_and_cli(flags);
- if (idx != cursor_pos) {
- *dst = bgmask;
- *(dst+1) = bgmask;
- dst += ipl;
- *dst = bgmask;
- *(dst+1) = bgmask;
- } else {
- under_cursor [0] = bgmask;
- under_cursor [1] = bgmask;
- under_cursor [2] = bgmask;
- under_cursor [3] = bgmask;
- }
- __restore_flags(flags);
-#endif
- } else /* non-space */ {
- fgmask = attrib & 0x0f;
- bgmask = attrib >> 4;
- fgmask |= fgmask << 8;
- fgmask |= fgmask << 16;
- bgmask |= bgmask << 8;
- bgmask |= bgmask << 16;
-
-#ifdef ASM_BLITC
-#define BLITC_INIT \
- "\n\t ld [%0], %%g2"
-#define BLITC_BODY(ST1,SC1,ST2,SC2) \
- "\n\t " #ST1 " %%g2, " #SC1 ", %%g7" \
- "\n\t " #ST2 " %%g2, " #SC2 ", %7" \
- "\n\t and %%g7, 0x3c, %%g7" \
- "\n\t and %7, 0x3c, %7" \
- "\n\t ld [%1 + %%g7], %6" \
- "\n\t and %6, %2, %%g7" \
- "\n\t andn %3, %6, %6" \
- "\n\t or %%g7, %6, %6" \
- "\n\t ld [%1 + %7], %7" \
- "\n\t and %7, %2, %%g7" \
- "\n\t andn %3, %7, %7" \
- "\n\t or %%g7, %7, %7"
-#define BLITC_BODYEND \
- "\n\t sll %3, 2, %%g7" \
- "\n\t srl %3, 2, %3" \
- "\n\t and %%g7, 0x3c, %%g7" \
- "\n\t and %3, 0x3c, %3" \
- "\n\t ld [%0 + %%g7], %4" \
- "\n\t and %4, %1, %%g7" \
- "\n\t andn %2, %4, %4" \
- "\n\t or %%g7, %4, %4" \
- "\n\t ld [%0 + %3], %3" \
- "\n\t and %3, %1, %%g7" \
- "\n\t andn %2, %3, %3" \
- "\n\t or %%g7, %3, %3"
-#define BLITC_STOREIT \
- "\n\t std %6, [%5]" \
- "\n\t add %5, %4, %5" \
- "\n\t"
-#define BLITC_STORE \
- "\n\t std %%g4, [%0]" \
- "\n\t std %%g2, [%0 + %1]"
-
- for (j = 0; j < 3; j++, font_row+=4) {
- __asm__ __volatile__ (BLITC_INIT
- BLITC_BODY(srl, 26, srl, 22)
- BLITC_STOREIT
- BLITC_BODY(srl, 18, srl, 14)
- BLITC_STOREIT
- BLITC_BODY(srl, 10, srl, 6)
- BLITC_STOREIT
- BLITC_BODY(srl, 2, sll, 2)
- BLITC_STOREIT
- : : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst),
- "r" (x1), "r" (x2));
- }
- __asm__ __volatile__ (BLITC_INIT
- BLITC_BODY(srl, 26, srl, 22)
- BLITC_STOREIT
- BLITC_BODY(srl, 18, srl, 14)
- BLITC_STOREIT
- /* Now prepare date for the 15th line, but don't put it anywhere yet (leave it in g4,g5) */
- BLITC_BODY(srl, 10, srl, 6)
- : : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst),
- "r" (x1), "r" (x2));
- /* Prepare the data the bottom line (and put it into g2,g3) */
- __asm__ __volatile__ (BLITC_BODYEND : : "r" (fontm_bits), "r" (fgmask), "r" (bgmask),
- "r" (x3), "r" (x4));
- __save_and_cli(flags);
- if (idx != cursor_pos)
- __asm__ __volatile__ (BLITC_STORE : : "r" (dst), "r" (cpl));
- else
- __asm__ __volatile__ (BLITC_STORE : : "r" (under_cursor), "i" (8));
- __restore_flags (flags);
-#else
- for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) {
- rowbits = *font_row;
- data = fontm_bits[(rowbits>>4)&0xf];
- data = (data & fgmask) | (~data & bgmask);
- *dst = data;
- data = fontm_bits[rowbits&0xf];
- data = (data & fgmask) | (~data & bgmask);
- *(dst+1) = data;
- }
- rowbits = *font_row;
- data = fontm_bits[(rowbits>>4)&0xf];
- data = (data & fgmask) | (~data & bgmask);
- data2 = fontm_bits[rowbits&0xf];
- data2 = (data2 & fgmask) | (~data2 & bgmask);
- rowbits = font_row[1];
- data3 = fontm_bits[(rowbits>>4)&0xf];
- data3 = (data3 & fgmask) | (~data3 & bgmask);
- data4 = fontm_bits[rowbits&0xf];
- data4 = (data4 & fgmask) | (~data4 & bgmask);
-
- /* Prevent cursor spots left on the screen */
- __save_and_cli(flags);
-
- if (idx != cursor_pos) {
- *dst = data;
- *(dst+1) = data2;
- dst += ipl;
- *dst = data3;
- *(dst+1) = data4;
- } else {
- under_cursor [0] = data;
- under_cursor [1] = data2;
- under_cursor [2] = data3;
- under_cursor [3] = data4;
- }
-
- __restore_flags(flags);
-#endif
- }
- break;
- } /* case */
- } /* switch */
- return (0);
+ return finish_console_init(memory_start);
}
-void memsetw(void * s, unsigned short c, unsigned int count)
-{
- unsigned short * addr = (unsigned short *) s;
+#ifdef CONFIG_PCI
+extern int pci_console_probe(void);
- count /= 2;
- if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
- while (count) {
- count--;
- *addr++ = c;
- }
- return;
- }
- if ((unsigned long) addr + count > video_mem_term ||
- (unsigned long) addr < video_mem_base) {
- if ((unsigned long) addr + count <= video_mem_term ||
- (unsigned long) addr > video_mem_base) {
- while (count) {
- count--;
- *addr++ = c;
- }
- return;
- } else {
- while (count) {
- count--;
- scr_writew(c, addr++);
- }
- }
-#define GX_SETW (*fbinfo[0].setw)(x_margin + ((xoff - (addr - last)) << 3), y_margin + CHAR_HEIGHT * yoff, c, addr - last);
- } else if (do_accel) {
- int yoff = (addr - (unsigned short *)video_mem_base) / video_num_columns;
- int xoff = (addr - (unsigned short *)video_mem_base) % video_num_columns;
- unsigned short * last = addr;
-
- while (count) {
- count--;
- if (*addr != c) {
- if (xoff == video_num_columns) {
- if (last != addr)
- GX_SETW
- xoff = 0;
- yoff++;
- last = addr;
- }
- *addr++ = c;
- xoff++;
- } else {
- if (last != addr)
- GX_SETW
- if (xoff == video_num_columns) {
- xoff = 0;
- yoff++;
- }
- addr++;
- xoff++;
- last = addr;
- }
- }
- if (last != addr)
- GX_SETW
- } else {
- while (count) {
- count--;
- if (*addr != c) {
- sun_blitc(c, (unsigned long)addr);
- *addr++ = c;
- } else
- addr++;
- }
- }
-}
-
-void memcpyw(unsigned short *to, unsigned short *from, unsigned int count)
+__initfunc(unsigned long pci_console_init(unsigned long memory_start))
{
- if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
- memcpy(to, from, count);
- return;
- }
- if ((unsigned long) to + count > video_mem_term ||
- (unsigned long) to < video_mem_base) {
- if ((unsigned long) to + count <= video_mem_term ||
- (unsigned long) to > video_mem_base)
- memcpy(to, from, count);
- else {
- count /= 2;
- while (count) {
- count--;
- scr_writew(scr_readw(from++), to++);
- }
- }
-#define GX_CPYW (*fbinfo[0].cpyw)(x_margin + ((xoff - (to - last)) << 3), y_margin + CHAR_HEIGHT * yoff, last, to - last);
- } else if (do_accel) {
- int yoff = (to - (unsigned short *)video_mem_base) / video_num_columns;
- int xoff = (to - (unsigned short *)video_mem_base) % video_num_columns;
- unsigned short * last = to;
-
- count /= 2;
- while (count) {
- count--;
- if (*to != *from) {
- if (xoff == video_num_columns) {
- if (last != to)
- GX_CPYW
- xoff = 0;
- yoff++;
- last = to;
- } else if (last != to && (*last & 0xff00) != (*from & 0xff00)) {
- GX_CPYW
- last = to;
- }
- *to++ = *from++;
- xoff++;
- } else {
- if (last != to)
- GX_CPYW
- if (xoff == video_num_columns) {
- xoff = 0;
- yoff++;
- }
- to++;
- xoff++;
- last = to;
- from++;
- }
- }
- if (last != to)
- GX_CPYW
- } else {
- count /= 2;
- while (count) {
- count--;
- if (*to != *from) {
- sun_blitc(*from, (unsigned long)to);
- *to++ = *from++;
- } else {
- from++;
- to++;
- }
- }
- }
-}
+ /* Nothing to do in this case. */
+ if(serial_console)
+ return memory_start;
-#undef pos
-int
-sun_hw_scursor (struct fbcursor *cursor, fbinfo_t *fb)
-{
- int op;
- int i, bytes = 0;
- struct fbcursor f;
- char red[2], green[2], blue[2];
-
- if (copy_from_user (&f, cursor, sizeof(struct fbcursor)))
- return -EFAULT;
- op = f.set;
- if (op & FB_CUR_SETSHAPE){
- if ((uint) f.size.fbx > fb->cursor.hwsize.fbx)
- return -EINVAL;
- if ((uint) f.size.fby > fb->cursor.hwsize.fby)
- return -EINVAL;
- if (f.size.fbx > 32)
- bytes = f.size.fby << 3;
- else
- bytes = f.size.fby << 2;
- }
- if (op & FB_CUR_SETCMAP){
- if (f.cmap.index || f.cmap.count != 2)
- return -EINVAL;
- if (copy_from_user (red, f.cmap.red, 2) ||
- copy_from_user (green, f.cmap.green, 2) ||
- copy_from_user (blue, f.cmap.blue, 2))
- return -EFAULT;
- }
- if (op & FB_CUR_SETCMAP)
- (*fb->setcursormap) (fb, red, green, blue);
- if (op & FB_CUR_SETSHAPE){
- uint u;
-
- fb->cursor.size = f.size;
- memset ((void *)&fb->cursor.bits, 0, sizeof (fb->cursor.bits));
- if (copy_from_user (fb->cursor.bits [0], f.mask, bytes) ||
- copy_from_user (fb->cursor.bits [1], f.image, bytes))
- return -EFAULT;
- if (f.size.fbx <= 32) {
- u = ~(0xffffffff >> f.size.fbx);
- for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
- fb->cursor.bits [0][i] &= u;
- fb->cursor.bits [1][i] &= fb->cursor.bits [0][i];
- }
- } else {
- u = ~(0xffffffff >> (f.size.fbx - 32));
- for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
- fb->cursor.bits [0][2*i+1] &= u;
- fb->cursor.bits [1][2*i] &= fb->cursor.bits [0][2*i];
- fb->cursor.bits [1][2*i+1] &= fb->cursor.bits [0][2*i+1];
- }
- }
- (*fb->setcurshape) (fb);
- }
- if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){
- if (op & FB_CUR_SETCUR)
- fb->cursor.enable = f.enable;
- if (op & FB_CUR_SETPOS)
- fb->cursor.cpos = f.pos;
- if (op & FB_CUR_SETHOT)
- fb->cursor.chot = f.hot;
- (*fb->setcursor) (fb);
+ if(pci_console_probe()) {
+ prom_printf("Could not probe PCI console, bailing out...\n");
+ prom_halt();
}
- return 0;
-}
-static unsigned char hw_cursor_cmap[2] = { 0, 0xff };
+ memory_start = finish_console_init(memory_start);
-void
-sun_hw_hide_cursor (void)
-{
- fbinfo[0].cursor.enable = 0;
- (*fbinfo[0].setcursor)(&fbinfo[0]);
- sun_hw_cursor_shown = 0;
-}
+ con_type_init_finish();
+ register_console(&vt_console_driver);
-void
-sun_hw_set_cursor (int xoff, int yoff)
-{
- if (!sun_hw_cursor_shown) {
- fbinfo[0].cursor.size.fbx = CHAR_WIDTH;
- fbinfo[0].cursor.size.fby = CHAR_HEIGHT;
- fbinfo[0].cursor.chot.fbx = 0;
- fbinfo[0].cursor.chot.fby = 0;
- fbinfo[0].cursor.enable = 1;
- memset (fbinfo[0].cursor.bits, 0, sizeof (fbinfo[0].cursor.bits));
- fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 2] = 0xff000000;
- fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 2] = 0xff000000;
- fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 1] = 0xff000000;
- fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 1] = 0xff000000;
- (*fbinfo[0].setcursormap) (&fbinfo[0], hw_cursor_cmap, hw_cursor_cmap, hw_cursor_cmap);
- (*fbinfo[0].setcurshape) (&fbinfo[0]);
- sun_hw_cursor_shown = 1;
- }
- fbinfo[0].cursor.cpos.fbx = xoff;
- fbinfo[0].cursor.cpos.fby = yoff;
- (*fbinfo[0].setcursor)(&fbinfo[0]);
+ return memory_start;
}
+#endif
diff --git a/drivers/sbus/char/sunfb.c b/drivers/sbus/char/sunfb.c
index 885581975..2ebd9cc5a 100644
--- a/drivers/sbus/char/sunfb.c
+++ b/drivers/sbus/char/sunfb.c
@@ -1,4 +1,4 @@
-/* $Id: sunfb.c,v 1.26 1997/07/17 02:21:48 davem Exp $
+/* $Id: sunfb.c,v 1.28 1997/08/22 15:55:23 jj Exp $
* sunfb.c: Sun generic frame buffer support.
*
* Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -42,7 +42,6 @@
#include "fb.h"
extern void set_other_palette (int);
-extern void sun_clear_fb(int);
extern void set_cursor (int);
#define FB_SETUP(err) \
@@ -197,9 +196,9 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg)
if (fb == fbinfo) {
if (vt_cons[fg_console]->vc_mode == KD_TEXT)
return -EINVAL; /* Don't let graphics programs hide our nice text cursor */
- sun_hw_cursor_shown = 0; /* Forget state of our text cursor */
+ sbus_hw_cursor_shown = 0; /* Forget state of our text cursor */
}
- return sun_hw_scursor ((struct fbcursor *) arg, fb);
+ return sbus_hw_scursor ((struct fbcursor *) arg, fb);
case FBIOSCURPOS:
if (!fb->setcursor) return -EINVAL;
@@ -242,8 +241,11 @@ fb_close (struct inode * inode, struct file *filp)
vt_cons [fb->vtconsole]->vc_mode = KD_TEXT;
/* Leaving graphics mode, turn off the cursor */
- if (fb->mmaped)
- sun_clear_fb (minor);
+ if (fb->mmaped) {
+ fb->clear_fb (minor);
+ if (!minor && suncons_ops.clear_margin)
+ suncons_ops.clear_margin();
+ }
cursor.set = FB_CUR_SETCUR;
cursor.enable = 0;
diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c
index 54b09cd76..dd4d933d9 100644
--- a/drivers/sbus/char/sunkbd.c
+++ b/drivers/sbus/char/sunkbd.c
@@ -1,10 +1,14 @@
/* keyboard.c: Sun keyboard driver.
*
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
+ *
* Added vuid event generation and /dev/kbd device for SunOS
* compatibility - Miguel (miguel@nuclecu.unam.mx)
+ *
+ * Added PCI 8042 controller support -DaveM
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tty.h>
@@ -16,11 +20,11 @@
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/random.h>
+#include <linux/delay.h>
#include <linux/init.h>
#include <asm/kbio.h>
#include <asm/vuid_event.h>
-#include <asm/delay.h>
#include <asm/bitops.h>
#include <asm/oplib.h>
#include <asm/uaccess.h>
@@ -29,6 +33,15 @@
#include <linux/kbd_diacr.h>
#include <linux/vt_kern.h>
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/pbm.h>
+#include <asm/ebus.h>
+#endif
+
+#include "sunkbd.h"
+
#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
/* Define this one if you are making a new frame buffer driver */
@@ -63,6 +76,7 @@ extern void reset_vc(unsigned int new_console);
extern void scrollback(int);
extern void scrollfront(int);
+struct l1a_kbd_state l1a_state = { 0, 0 };
unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */
unsigned char aux_device_present = 0x00; /* To make kernel/ksyms.c happy */
@@ -208,7 +222,23 @@ static unsigned char sunkbd_clickp;
#define KEY_ALT 0x86
#define KEY_L1 0x87
-extern void kbd_put_char(unsigned char ch);
+/* Do to kbd_init() being called before rs_init(), and kbd_init() doing:
+ *
+ * init_bh(KEYBOARD_BH, kbd_bh);
+ * mark_bh(KEYBOARD_BH);
+ *
+ * this might well be called before some driver has claimed interest in
+ * handling the keyboard input/output. So we need to assign an initial nop.
+ *
+ * Otherwise this would lead to the following (DaveM might want to look at):
+ *
+ * sparc64_dtlb_refbit_catch(),
+ * do_sparc64_fault(),
+ * kernel NULL pointer dereference at do_sparc64_fault + 0x2c0 ;-(
+ */
+static void nop_kbd_put_char(unsigned char c) { }
+static void (*kbd_put_char)(unsigned char) = nop_kbd_put_char;
+
static inline void send_cmd(unsigned char c)
{
kbd_put_char(c);
@@ -1425,13 +1455,18 @@ file_operations kbd_fops =
NULL, /* revalidate */
};
-__initfunc(void keyboard_zsinit(void))
+__initfunc(void keyboard_zsinit(void (*put_char)(unsigned char)))
{
int timeout = 0;
+ kbd_put_char = put_char;
+ if (!kbd_put_char)
+ panic("keyboard_zsinit: no put_char parameter");
+
/* Test out the leds */
sunkbd_type = 255;
send_cmd(SKBDCMD_RESET);
+ send_cmd(SKBDCMD_RESET);
while((sunkbd_type==255) && timeout < 500000) {
udelay(100);
timeout += 20;
diff --git a/drivers/sbus/char/sunkbd.h b/drivers/sbus/char/sunkbd.h
new file mode 100644
index 000000000..151c77457
--- /dev/null
+++ b/drivers/sbus/char/sunkbd.h
@@ -0,0 +1,27 @@
+/* $Id: sunkbd.h,v 1.1 1997/08/28 02:23:34 ecd Exp $
+ * sunkbd.h: Defines needed by SUN Keyboard drivers
+ *
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
+ */
+
+#ifndef _SPARC_SUNKBD_H
+#define _SPARC_SUNKBD_H 1
+
+/* Keyboard defines for L1-A processing... */
+#define SUNKBD_RESET 0xff
+#define SUNKBD_L1 0x01
+#define SUNKBD_UP 0x80
+#define SUNKBD_A 0x4d
+
+struct l1a_kbd_state {
+ int kbd_id;
+ int l1_down;
+};
+
+extern struct l1a_kbd_state l1a_state;
+
+extern void keyboard_zsinit(void (*kbd_put_char)(unsigned char));
+extern void sunkbd_inchar(unsigned char, struct pt_regs *);
+extern void batten_down_hatches(void);
+
+#endif /* !(_SPARC_SUNKBD_H) */
diff --git a/drivers/sbus/char/sunmouse.c b/drivers/sbus/char/sunmouse.c
index 789a332c0..9f5bbb5b6 100644
--- a/drivers/sbus/char/sunmouse.c
+++ b/drivers/sbus/char/sunmouse.c
@@ -1,6 +1,6 @@
/* sunmouse.c: Sun mouse driver for the Sparc
*
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
*
* Parts based on the psaux.c driver written by:
@@ -10,6 +10,7 @@
* Jan/5/96 Added VUID support, sigio support - miguel.
* Mar/5/96 Added proper mouse stream support - miguel.
* Sep/96 Allow more than one reader -miguel.
+ * Aug/97 Added PCI 8042 controller support -DaveM
*/
/* The mouse is run off of one of the Zilog serial ports. On
@@ -37,6 +38,7 @@
* FIXME: We need to support more than one mouse.
* */
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fcntl.h>
@@ -90,7 +92,7 @@ static struct sun_mouse sunmouse;
extern void mouse_put_char(char ch);
-/* #define SMOUSE_DEBUG */
+#undef SMOUSE_DEBUG
static void
push_event (Firm_event *ev)
@@ -125,6 +127,9 @@ push_char (char c)
int next = (sunmouse.head + 1) % STREAM_SIZE;
if (next != sunmouse.tail){
+#ifdef SMOUSE_DEBUG
+ printk("P<%02x>\n", (unsigned char)c);
+#endif
sunmouse.queue.stream [sunmouse.head] = c;
sunmouse.head = next;
}
@@ -142,14 +147,14 @@ static int mouse_baud = 4800; /* Initial rate set by zilog driver. */
/* Change the baud rate after receiving too many "bogon bytes". */
void sun_mouse_change_baud(void)
{
- extern void zs_change_mouse_baud(int newbaud);
+ extern void rs_change_mouse_baud(int newbaud);
if(mouse_baud == 1200)
mouse_baud = 4800;
else
mouse_baud = 1200;
- zs_change_mouse_baud(mouse_baud);
+ rs_change_mouse_baud(mouse_baud);
mouse_baud_changing = 1;
}
@@ -373,9 +378,25 @@ sun_mouse_read(struct inode *inode, struct file *file, char *buffer,
char *p = buffer, *end = buffer+count;
while (p < end && !queue_empty ()){
- copy_to_user_ret((Firm_event *)p, get_from_queue(),
- sizeof(Firm_event), -EFAULT);
- p += sizeof (Firm_event);
+#ifdef CONFIG_SPARC32_COMPAT
+ if (current->tss.flags & SPARC_FLAG_32BIT) {
+ Firm_event *q = get_from_queue();
+
+ copy_to_user_ret((Firm_event *)p, q,
+ sizeof(Firm_event)-sizeof(struct timeval),
+ -EFAULT);
+ p += sizeof(Firm_event)-sizeof(struct timeval);
+ __put_user_ret(q->time.tv_sec, (u32 *)p, -EFAULT);
+ p += sizeof(u32);
+ __put_user_ret(q->time.tv_usec, (u32 *)p, -EFAULT);
+ p += sizeof(u32);
+ } else
+#endif
+ {
+ copy_to_user_ret((Firm_event *)p, get_from_queue(),
+ sizeof(Firm_event), -EFAULT);
+ p += sizeof (Firm_event);
+ }
}
sunmouse.ready = !queue_empty ();
inode->i_atime = CURRENT_TIME;
diff --git a/drivers/sbus/char/sunmouse.h b/drivers/sbus/char/sunmouse.h
new file mode 100644
index 000000000..78d9eb73c
--- /dev/null
+++ b/drivers/sbus/char/sunmouse.h
@@ -0,0 +1,13 @@
+/* $Id: sunmouse.h,v 1.1 1997/08/28 02:23:38 ecd Exp $
+ * sunmouse.h: Interface to the SUN mouse driver.
+ *
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
+ */
+
+#ifndef _SPARC_SUNMOUSE_H
+#define _SPARC_SUNMOUSE_H 1
+
+extern void sun_mouse_zsinit(void);
+extern void sun_mouse_inbyte(unsigned char);
+
+#endif /* !(_SPARC_SUNMOUSE_H) */
diff --git a/drivers/sbus/char/sunserial.c b/drivers/sbus/char/sunserial.c
index c0e6ab71d..da90d5ade 100644
--- a/drivers/sbus/char/sunserial.c
+++ b/drivers/sbus/char/sunserial.c
@@ -1,2668 +1,134 @@
-/* $Id: sunserial.c,v 1.43 1997/07/05 09:53:23 davem Exp $
- * serial.c: Serial port driver for the Sparc.
+/* $Id: sunserial.c,v 1.50 1997/09/03 11:54:59 ecd Exp $
+ * serial.c: Serial port driver infrastructure for the Sparc.
*
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- * Fixes by Pete A. Zaitcev <zaitcev@ipmce.su>.
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
+#include <linux/config.h>
+#include <linux/module.h>
#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/config.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/kernel.h>
-#include <linux/keyboard.h>
-#include <linux/console.h>
-#include <linux/init.h>
+#include <linux/serial.h>
-#include <asm/io.h>
-#include <asm/irq.h>
#include <asm/oplib.h>
-#include <asm/system.h>
-#include <asm/uaccess.h>
-#include <asm/bitops.h>
-#include <asm/delay.h>
-#include <asm/kdebug.h>
#include "sunserial.h"
-static int num_serial = 2; /* sun4/sun4c/sun4m - Two chips on board. */
-#define NUM_SERIAL num_serial
-#define NUM_CHANNELS (NUM_SERIAL * 2)
-
-#define KEYBOARD_LINE 0x2
-#define MOUSE_LINE 0x3
-
-struct sun_zslayout **zs_chips;
-struct sun_zschannel **zs_channels;
-struct sun_zschannel *zs_conschan;
-struct sun_zschannel *zs_mousechan;
-struct sun_zschannel *zs_kbdchan;
-struct sun_zschannel *zs_kgdbchan;
-int *zs_nodes;
-
-struct sun_serial *zs_soft;
-struct sun_serial *zs_chain; /* IRQ servicing chain */
-int zilog_irq;
-
-struct tty_struct *zs_ttys;
-/** struct tty_struct *zs_constty; **/
-
-/* Console hooks... */
-static int zs_cons_chanout = 0;
-static int zs_cons_chanin = 0;
-static struct l1a_kbd_state l1a_state = { 0, 0 };
-struct sun_serial *zs_consinfo = 0;
-
-/* Keyboard defines for L1-A processing... */
-#define SUNKBD_RESET 0xff
-#define SUNKBD_L1 0x01
-#define SUNKBD_UP 0x80
-#define SUNKBD_A 0x4d
-
-extern void sunkbd_inchar(unsigned char ch, struct pt_regs *regs);
-extern void sun_mouse_inbyte(unsigned char byte);
-
-static unsigned char kgdb_regs[16] = {
- 0, 0, 0, /* write 0, 1, 2 */
- (Rx8 | RxENAB), /* write 3 */
- (X16CLK | SB1 | PAR_EVEN), /* write 4 */
- (DTR | Tx8 | TxENAB), /* write 5 */
- 0, 0, 0, /* write 6, 7, 8 */
- (NV), /* write 9 */
- (NRZ), /* write 10 */
- (TCBR | RCBR), /* write 11 */
- 0, 0, /* BRG time constant, write 12 + 13 */
- (BRSRC | BRENAB), /* write 14 */
- (DCDIE) /* write 15 */
-};
-
-static unsigned char zscons_regs[16] = {
- 0, /* write 0 */
- (EXT_INT_ENAB | INT_ALL_Rx), /* write 1 */
- 0, /* write 2 */
- (Rx8 | RxENAB), /* write 3 */
- (X16CLK), /* write 4 */
- (DTR | Tx8 | TxENAB), /* write 5 */
- 0, 0, 0, /* write 6, 7, 8 */
- (NV | MIE), /* write 9 */
- (NRZ), /* write 10 */
- (TCBR | RCBR), /* write 11 */
- 0, 0, /* BRG time constant, write 12 + 13 */
- (BRSRC | BRENAB), /* write 14 */
- (DCDIE | CTSIE | TxUIE | BRKIE) /* write 15 */
-};
-
-#define ZS_CLOCK 4915200 /* Zilog input clock rate */
-
-DECLARE_TASK_QUEUE(tq_serial);
-
-struct tty_driver serial_driver, callout_driver;
-static int serial_refcount;
-
-/* serial subtype definitions */
-#define SERIAL_TYPE_NORMAL 1
-#define SERIAL_TYPE_CALLOUT 2
-
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
-/* Debugging... DEBUG_INTR is bad to use when one of the zs
- * lines is your console ;(
- */
-#undef SERIAL_DEBUG_INTR
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
-
-#define RS_STROBE_TIME 10
-#define RS_ISR_PASS_LIMIT 256
-
-#define _INLINE_ inline
-
-static void change_speed(struct sun_serial *info);
-
-static struct tty_struct **serial_table;
-static struct termios **serial_termios;
-static struct termios **serial_termios_locked;
-
-#ifndef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
-/*
- * tmp_buf is used as a temporary buffer by serial_write. We need to
- * lock it in case the memcpy_fromfs blocks while swapping in a page,
- * and some other program tries to do a serial write at the same time.
- * Since the lock will only come under contention when the system is
- * swapping and available memory is low, it makes sense to share one
- * buffer across all the serial ports, since it significantly saves
- * memory if large numbers of serial ports are open.
- */
-static unsigned char tmp_buf[4096]; /* This is cheating */
-static struct semaphore tmp_buf_sem = MUTEX;
-
-static inline int serial_paranoia_check(struct sun_serial *info,
- dev_t device, const char *routine)
-{
-#ifdef SERIAL_PARANOIA_CHECK
- static const char *badmagic =
- "Warning: bad magic number for serial struct (%d, %d) in %s\n";
- static const char *badinfo =
- "Warning: null sun_serial for (%d, %d) in %s\n";
-
- if (!info) {
- printk(badinfo, MAJOR(device), MINOR(device), routine);
- return 1;
- }
- if (info->magic != SERIAL_MAGIC) {
- printk(badmagic, MAJOR(device), MINOR(device), routine);
- return 1;
- }
-#endif
- return 0;
-}
-
-/*
- * This is used to figure out the divisor speeds and the timeouts
- */
-static int baud_table[] = {
- 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- 9600, 19200, 38400, 76800, 0 };
-
-/*
- * Reading and writing Zilog8530 registers. The delays are to make this
- * driver work on the Sun4 which needs a settling delay after each chip
- * register access, other machines handle this in hardware via auxiliary
- * flip-flops which implement the settle time we do in software.
- */
-static inline unsigned char read_zsreg(struct sun_zschannel *channel,
- unsigned char reg)
-{
- unsigned char retval;
-
- channel->control = reg;
- udelay(5);
- retval = channel->control;
- udelay(5);
- return retval;
-}
-
-static inline void write_zsreg(struct sun_zschannel *channel,
- unsigned char reg, unsigned char value)
-{
- channel->control = reg;
- udelay(5);
- channel->control = value;
- udelay(5);
-}
-
-static inline void load_zsregs(struct sun_serial *info, unsigned char *regs)
-{
- unsigned long flags;
- struct sun_zschannel *channel = info->zs_channel;
- unsigned char stat;
- int i;
-
- for (i = 0; i < 1000; i++) {
- stat = read_zsreg(channel, R1);
- if (stat & ALL_SNT)
- break;
- udelay(100);
- }
- write_zsreg(channel, R3, 0);
- ZS_CLEARSTAT(channel);
- ZS_CLEARERR(channel);
- ZS_CLEARFIFO(channel);
-
- /* Load 'em up */
- save_flags(flags); cli();
- if (info->channelA)
- write_zsreg(channel, R9, CHRA);
- else
- write_zsreg(channel, R9, CHRB);
- udelay(20); /* wait for some old sun4's */
- write_zsreg(channel, R4, regs[R4]);
- write_zsreg(channel, R3, regs[R3] & ~RxENAB);
- write_zsreg(channel, R5, regs[R5] & ~TxENAB);
- write_zsreg(channel, R9, regs[R9] & ~MIE);
- write_zsreg(channel, R10, regs[R10]);
- write_zsreg(channel, R11, regs[R11]);
- write_zsreg(channel, R12, regs[R12]);
- write_zsreg(channel, R13, regs[R13]);
- write_zsreg(channel, R14, regs[R14] & ~BRENAB);
- write_zsreg(channel, R14, regs[R14]);
- write_zsreg(channel, R14, (regs[R14] & ~SNRZI) | BRENAB);
- write_zsreg(channel, R3, regs[R3]);
- write_zsreg(channel, R5, regs[R5]);
- write_zsreg(channel, R15, regs[R15]);
- write_zsreg(channel, R0, RES_EXT_INT);
- write_zsreg(channel, R0, ERR_RES);
- write_zsreg(channel, R1, regs[R1]);
- write_zsreg(channel, R9, regs[R9]);
- restore_flags(flags);
-}
-
-static inline void zs_put_char(struct sun_zschannel *channel, char ch)
+static void nop_rs_cons_hook(int chip, int out, int line)
{
- int loops = 0;
-
- while((channel->control & Tx_BUF_EMP) == 0 && loops < 10000) {
- loops++;
- udelay(5);
- }
- channel->data = ch;
- udelay(5);
+ printk("Oops: %s called\n", __FUNCTION__);
}
-/* Sets or clears DTR/RTS on the requested line */
-static inline void zs_rtsdtr(struct sun_serial *ss, int set)
+static void nop_rs_kgdb_hook(int channel)
{
- unsigned long flags;
-
- save_flags(flags); cli();
- if(set) {
- ss->curregs[5] |= (RTS | DTR);
- write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
- } else {
- ss->curregs[5] &= ~(RTS | DTR);
- write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
- }
- restore_flags(flags);
- return;
+ printk("Oops: %s called\n", __FUNCTION__);
}
-static inline void kgdb_chaninit(struct sun_serial *ss, int intson, int bps)
+static void nop_rs_change_mouse_baud(int baud)
{
- int brg;
-
- if(intson) {
- kgdb_regs[R1] = INT_ALL_Rx;
- kgdb_regs[R9] |= MIE;
- } else {
- kgdb_regs[R1] = 0;
- kgdb_regs[R9] &= ~MIE;
- }
- brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
- kgdb_regs[R12] = (brg & 255);
- kgdb_regs[R13] = ((brg >> 8) & 255);
- load_zsregs(ss, kgdb_regs);
+ printk("Oops: %s called\n", __FUNCTION__);
}
-/*
- * ------------------------------------------------------------
- * rs_stop() and rs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter interrupts, as necessary.
- * ------------------------------------------------------------
- */
-static void rs_stop(struct tty_struct *tty)
+static int nop_rs_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->device, "rs_stop"))
- return;
-
- save_flags(flags); cli();
- if (info->curregs[5] & TxENAB) {
- info->curregs[5] &= ~TxENAB;
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- }
- restore_flags(flags);
-}
-
-static void rs_start(struct tty_struct *tty)
-{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->device, "rs_start"))
- return;
-
- save_flags(flags); cli();
- if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {
- info->curregs[5] |= TxENAB;
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- }
- restore_flags(flags);
-}
-
-/* Drop into either the boot monitor or kadb upon receiving a break
- * from keyboard/console input.
- */
-static void batten_down_hatches(void)
-{
- /* If we are doing kadb, we call the debugger
- * else we just drop into the boot monitor.
- * Note that we must flush the user windows
- * first before giving up control.
- */
- printk("\n");
- flush_user_windows();
-#ifndef __sparc_v9__
- if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) &&
- (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR))
- sp_enter_debugger();
- else
-#endif
- prom_cmdline();
-
- /* XXX We want to notify the keyboard driver that all
- * XXX keys are in the up state or else weird things
- * XXX happen...
- */
-
- return;
-}
-
-
-/*
- * ----------------------------------------------------------------------
- *
- * Here starts the interrupt handling routines. All of the following
- * subroutines are declared as inline and are folded into
- * rs_interrupt(). They were separated out for readability's sake.
- *
- * Note: rs_interrupt() is a "fast" interrupt, which means that it
- * runs with interrupts turned off. People who may want to modify
- * 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.
- *
- * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
- * -----------------------------------------------------------------------
- */
-
-/*
- * This routine is used by the interrupt handler to schedule
- * processing in the software interrupt portion of the driver.
- */
-static _INLINE_ void rs_sched_event(struct sun_serial *info,
- int event)
-{
- info->event |= 1 << event;
- queue_task(&info->tqueue, &tq_serial);
- mark_bh(SERIAL_BH);
-}
-
-#ifndef __sparc_v9__
-extern void breakpoint(void); /* For the KGDB frame character */
-#endif
-
-static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs)
-{
- struct tty_struct *tty = info->tty;
- unsigned char ch, stat;
-
- do {
- ch = (info->zs_channel->data) & info->parity_mask;
- udelay(5);
-
- /* If this is the console keyboard, we need to handle
- * L1-A's here.
- */
- if(info->cons_keyb) {
- if(ch == SUNKBD_RESET) {
- l1a_state.kbd_id = 1;
- l1a_state.l1_down = 0;
- } else if(l1a_state.kbd_id) {
- l1a_state.kbd_id = 0;
- } else if(ch == SUNKBD_L1) {
- l1a_state.l1_down = 1;
- } else if(ch == (SUNKBD_L1|SUNKBD_UP)) {
- l1a_state.l1_down = 0;
- } else if(ch == SUNKBD_A && l1a_state.l1_down) {
- /* whee... */
- batten_down_hatches();
- /* Continue execution... */
- l1a_state.l1_down = 0;
- l1a_state.kbd_id = 0;
- return;
- }
- sunkbd_inchar(ch, regs);
- return;
- }
- if(info->cons_mouse) {
- sun_mouse_inbyte(ch);
- return;
- }
- if(info->is_cons) {
- if(ch==0) {
- /* whee, break received */
- batten_down_hatches();
- /* Continue execution... */
- return;
-#if 0
- } else if (ch == 1) {
- show_state();
- return;
- } else if (ch == 2) {
- show_buffers();
- return;
-#endif
- }
- /* It is a 'keyboard interrupt' ;-) */
- wake_up(&keypress_wait);
- }
-#ifndef __sparc_v9__
- /* Look for kgdb 'stop' character, consult the gdb
- * documentation for remote target debugging and
- * arch/sparc/kernel/sparc-stub.c to see how all this works.
- */
- if((info->kgdb_channel) && (ch =='\003')) {
- breakpoint();
- return;
- }
-#endif
- if(!tty)
- return;
-
- if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- break;
-
- tty->flip.count++;
- *tty->flip.flag_buf_ptr++ = 0;
- *tty->flip.char_buf_ptr++ = ch;
-
- /* Check if we have another character... */
- stat = info->zs_channel->control;
- udelay(5);
- if (!(stat & Rx_CH_AV))
- break;
-
- /* ... and see if it is clean. */
- stat = read_zsreg(info->zs_channel, R1);
- } while (!(stat & (PAR_ERR | Rx_OVR | CRC_ERR)));
-
- queue_task(&tty->flip.tqueue, &tq_timer);
-}
-
-static _INLINE_ void transmit_chars(struct sun_serial *info)
-{
- struct tty_struct *tty = info->tty;
-
- if (info->x_char) {
- /* Send next char */
- zs_put_char(info->zs_channel, info->x_char);
- info->x_char = 0;
- return;
- }
-
- if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
- /* That's peculiar... */
- info->zs_channel->control = RES_Tx_P;
- udelay(5);
- return;
- }
-
- /* Send char */
- zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
- info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt--;
-
- if (info->xmit_cnt < WAKEUP_CHARS)
- rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
-
- if(info->xmit_cnt <= 0) {
- info->zs_channel->control = RES_Tx_P;
- udelay(5);
- }
-}
-
-static _INLINE_ void status_handle(struct sun_serial *info)
-{
- unsigned char status;
-
- /* Get status from Read Register 0 */
- status = info->zs_channel->control;
- udelay(5);
- /* Clear status condition... */
- info->zs_channel->control = RES_EXT_INT;
- udelay(5);
-
-#if 0
- if(status & DCD) {
- if((info->tty->termios->c_cflag & CRTSCTS) &&
- ((info->curregs[3] & AUTO_ENAB)==0)) {
- info->curregs[3] |= AUTO_ENAB;
- write_zsreg(info->zs_channel, 3, info->curregs[3]);
- }
- } else {
- if((info->curregs[3] & AUTO_ENAB)) {
- info->curregs[3] &= ~AUTO_ENAB;
- write_zsreg(info->zs_channel, 3, info->curregs[3]);
- }
- }
-#endif
- /* Whee, if this is console input and this is a
- * 'break asserted' status change interrupt, call
- * the boot prom.
- */
- if((status & BRK_ABRT) && info->break_abort)
- batten_down_hatches();
-
- /* XXX Whee, put in a buffer somewhere, the status information
- * XXX whee whee whee... Where does the information go...
- */
- return;
-}
-
-static _INLINE_ void special_receive(struct sun_serial *info)
-{
- struct tty_struct *tty = info->tty;
- unsigned char ch, stat;
-
- stat = read_zsreg(info->zs_channel, R1);
- if (stat & (PAR_ERR | Rx_OVR | CRC_ERR)) {
- ch = info->zs_channel->data;
- udelay(5);
- }
-
- if (!tty)
- goto clear;
-
- if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- goto done;
-
- tty->flip.count++;
- if(stat & PAR_ERR)
- *tty->flip.flag_buf_ptr++ = TTY_PARITY;
- else if(stat & Rx_OVR)
- *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
- else if(stat & CRC_ERR)
- *tty->flip.flag_buf_ptr++ = TTY_FRAME;
-
-done:
- queue_task(&tty->flip.tqueue, &tq_timer);
-clear:
- info->zs_channel->control = ERR_RES;
- udelay(5);
-}
-
-
-/*
- * This is the serial driver's generic interrupt routine
- */
-void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
-{
- struct sun_serial *info;
- unsigned char zs_intreg;
- int i;
-
- info = (struct sun_serial *)dev_id;
- for (i = 0; i < NUM_SERIAL; i++) {
- zs_intreg = read_zsreg(info->zs_next->zs_channel, 2);
- zs_intreg &= STATUS_MASK;
-
- /* NOTE: The read register 2, which holds the irq status,
- * does so for both channels on each chip. Although
- * the status value itself must be read from the B
- * channel and is only valid when read from channel B.
- * When read from channel A, read register 2 contains
- * the value written to write register 2.
- */
-
- /* Channel A -- /dev/ttya or /dev/kbd, could be the console */
- if (zs_intreg == CHA_Rx_AVAIL) {
- receive_chars(info, regs);
- return;
- }
- if(zs_intreg == CHA_Tx_EMPTY) {
- transmit_chars(info);
- return;
- }
- if (zs_intreg == CHA_EXT_STAT) {
- status_handle(info);
- return;
- }
- if (zs_intreg == CHA_SPECIAL) {
- special_receive(info);
- return;
- }
-
- /* Channel B -- /dev/ttyb or /dev/mouse, could be the console */
- if(zs_intreg == CHB_Rx_AVAIL) {
- receive_chars(info->zs_next, regs);
- return;
- }
- if(zs_intreg == CHB_Tx_EMPTY) {
- transmit_chars(info->zs_next);
- return;
- }
- if (zs_intreg == CHB_EXT_STAT) {
- status_handle(info->zs_next);
- return;
- }
-
- /* NOTE: The default value for the IRQ status in read register
- * 2 in channel B is CHB_SPECIAL, so we need to look at
- * read register 3 in channel A to check if this is a
- * real interrupt, or just the default value.
- * Yes... broken hardware...
- */
-
- zs_intreg = read_zsreg(info->zs_channel, 3);
- if (zs_intreg & CHBRxIP) {
- special_receive(info->zs_next);
- return;
- }
- info = info->zs_next->zs_next;
- }
-}
-
-/*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
- */
-
-/*
- * This routine is used to handle the "bottom half" processing for the
- * serial driver, known also the "software interrupt" processing.
- * This processing is done at the kernel interrupt level, after the
- * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
- * is where time-consuming activities which can not be done in the
- * interrupt driver proper are done; the interrupt driver schedules
- * them using rs_sched_event(), and they get done here.
- */
-static void do_serial_bh(void)
-{
- run_task_queue(&tq_serial);
-}
-
-static void do_softint(void *private_)
-{
- struct sun_serial *info = (struct sun_serial *) private_;
- struct tty_struct *tty;
-
- tty = info->tty;
- if (!tty)
- return;
-
- if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
- wake_up_interruptible(&tty->write_wait);
- }
-}
-
-/*
- * This routine is called from the scheduler tqueue when the interrupt
- * routine has signalled that a hangup has occurred. The path of
- * hangup processing is:
- *
- * serial interrupt routine -> (scheduler tqueue) ->
- * do_serial_hangup() -> tty->hangup() -> rs_hangup()
- *
- */
-static void do_serial_hangup(void *private_)
-{
- struct sun_serial *info = (struct sun_serial *) private_;
- struct tty_struct *tty;
-
- tty = info->tty;
- if (!tty)
- return;
-#ifdef SERIAL_DEBUG_OPEN
- printk("do_serial_hangup<%p: tty-%d\n",
- __builtin_return_address(0), info->line);
-#endif
-
- tty_hangup(tty);
-}
-
-
-/*
- * This subroutine is called when the RS_TIMER goes off. It is used
- * by the serial driver to handle ports that do not have an interrupt
- * (irq=0). This doesn't work at all for 16450's, as a sun has a Z8530.
- */
-
-static void rs_timer(void)
-{
- printk("rs_timer called\n");
- prom_halt();
- return;
-}
-
-static int startup(struct sun_serial * info)
-{
- unsigned long flags;
-
- if (info->flags & ZILOG_INITIALIZED)
- return 0;
-
- if (!info->xmit_buf) {
- info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
- if (!info->xmit_buf)
- return -ENOMEM;
- }
-
- save_flags(flags); cli();
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("Starting up tty-%d (irq %d)...\n", info->line, info->irq);
-#endif
-
- /*
- * Clear the FIFO buffers and disable them
- * (they will be reenabled in change_speed())
- */
- ZS_CLEARFIFO(info->zs_channel);
- info->xmit_fifo_size = 1;
-
- /*
- * Clear the interrupt registers.
- */
- info->zs_channel->control = ERR_RES;
- udelay(5);
- info->zs_channel->control = RES_H_IUS;
- udelay(5);
-
- /*
- * Now, initialize the Zilog
- */
- zs_rtsdtr(info, 1);
-
- /*
- * Finally, enable sequencing and interrupts
- */
- info->curregs[1] |= (info->curregs[1] & ~(RxINT_MASK)) |
- (EXT_INT_ENAB | INT_ALL_Rx);
- info->curregs[3] |= (RxENAB | Rx8);
- /* We enable Tx interrupts as needed. */
- info->curregs[5] |= (TxENAB | Tx8);
- info->curregs[9] |= (NV | MIE);
- write_zsreg(info->zs_channel, 3, info->curregs[3]);
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- write_zsreg(info->zs_channel, 9, info->curregs[9]);
-
- /*
- * And clear the interrupt registers again for luck.
- */
- info->zs_channel->control = ERR_RES;
- udelay(5);
- info->zs_channel->control = RES_H_IUS;
- udelay(5);
-
- if (info->tty)
- clear_bit(TTY_IO_ERROR, &info->tty->flags);
- info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-
- /*
- * Set up serial timers...
- */
-#if 0 /* Works well and stops the machine. */
- timer_table[RS_TIMER].expires = jiffies + 2;
- timer_active |= 1 << RS_TIMER;
-#endif
-
- /*
- * and set the speed of the serial port
- */
- change_speed(info);
-
- info->flags |= ZILOG_INITIALIZED;
- restore_flags(flags);
+ printk("Oops: %s called\n", __FUNCTION__);
return 0;
}
-/*
- * 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 sun_serial * info)
-{
- unsigned long flags;
-
- if (!(info->flags & ZILOG_INITIALIZED))
- return;
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("Shutting down serial port %d (irq %d)....", info->line,
- info->irq);
-#endif
-
- save_flags(flags); cli(); /* Disable interrupts */
-
- if (info->xmit_buf) {
- free_page((unsigned long) info->xmit_buf);
- info->xmit_buf = 0;
- }
-
- if (info->tty)
- set_bit(TTY_IO_ERROR, &info->tty->flags);
-
- info->flags &= ~ZILOG_INITIALIZED;
- restore_flags(flags);
-}
-
-/*
- * This routine is called to set the UART divisor registers to match
- * the specified baud rate for a serial port.
- */
-static void change_speed(struct sun_serial *info)
-{
- unsigned short port;
- unsigned cflag;
- int quot = 0;
- int i;
- int brg;
-
- if (!info->tty || !info->tty->termios)
- return;
- cflag = info->tty->termios->c_cflag;
- if (!(port = info->port))
- return;
- i = cflag & CBAUD;
- if (cflag & CBAUDEX) {
- i &= ~CBAUDEX;
- if (i != 5)
- info->tty->termios->c_cflag &= ~CBAUDEX;
- else
- i = 16;
- }
- if (i == 15) {
- if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_HI)
- i += 1;
- if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_CUST)
- quot = info->custom_divisor;
- }
- if (quot) {
- info->zs_baud = info->baud_base / quot;
- info->clk_divisor = 16;
-
- info->curregs[4] = X16CLK;
- info->curregs[11] = TCBR | RCBR;
- brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
- info->curregs[12] = (brg & 255);
- info->curregs[13] = ((brg >> 8) & 255);
- info->curregs[14] = BRSRC | BRENAB;
- zs_rtsdtr(info, 1);
- } else if (baud_table[i]) {
- info->zs_baud = baud_table[i];
- info->clk_divisor = 16;
-
- info->curregs[4] = X16CLK;
- info->curregs[11] = TCBR | RCBR;
- brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
- info->curregs[12] = (brg & 255);
- info->curregs[13] = ((brg >> 8) & 255);
- info->curregs[14] = BRSRC | BRENAB;
- zs_rtsdtr(info, 1);
- } else {
- zs_rtsdtr(info, 0);
- return;
- }
-
- /* byte size and parity */
- switch (cflag & CSIZE) {
- case CS5:
- info->curregs[3] &= ~(RxN_MASK);
- info->curregs[3] |= Rx5;
- info->curregs[5] &= ~(TxN_MASK);
- info->curregs[5] |= Tx5;
- info->parity_mask = 0x1f;
- break;
- case CS6:
- info->curregs[3] &= ~(RxN_MASK);
- info->curregs[3] |= Rx6;
- info->curregs[5] &= ~(TxN_MASK);
- info->curregs[5] |= Tx6;
- info->parity_mask = 0x3f;
- break;
- case CS7:
- info->curregs[3] &= ~(RxN_MASK);
- info->curregs[3] |= Rx7;
- info->curregs[5] &= ~(TxN_MASK);
- info->curregs[5] |= Tx7;
- info->parity_mask = 0x7f;
- break;
- case CS8:
- default: /* defaults to 8 bits */
- info->curregs[3] &= ~(RxN_MASK);
- info->curregs[3] |= Rx8;
- info->curregs[5] &= ~(TxN_MASK);
- info->curregs[5] |= Tx8;
- info->parity_mask = 0xff;
- break;
- }
- info->curregs[4] &= ~(0x0c);
- if (cflag & CSTOPB) {
- info->curregs[4] |= SB2;
- } else {
- info->curregs[4] |= SB1;
- }
- if (cflag & PARENB) {
- info->curregs[4] |= PAR_ENAB;
- } else {
- info->curregs[4] &= ~PAR_ENAB;
- }
- if (!(cflag & PARODD)) {
- info->curregs[4] |= PAR_EVEN;
- } else {
- info->curregs[4] &= ~PAR_EVEN;
- }
-
- /* Load up the new values */
- load_zsregs(info, info->curregs);
-
- return;
-}
-
-/* This is for mouse/keyboard output.
- * XXX mouse output??? can we send it commands??? XXX
- */
-void kbd_put_char(unsigned char ch)
-{
- struct sun_zschannel *chan = zs_kbdchan;
- unsigned long flags;
-
- if(!chan)
- return;
-
- save_flags(flags); cli();
- zs_put_char(chan, ch);
- restore_flags(flags);
-}
-
-void mouse_put_char(char ch)
-{
- struct sun_zschannel *chan = zs_mousechan;
- unsigned long flags;
-
- if(!chan)
- return;
-
- save_flags(flags); cli();
- zs_put_char(chan, ch);
- restore_flags(flags);
-}
-
-
-/* This is for console output over ttya/ttyb */
-static void rs_put_char(char ch)
-{
- struct sun_zschannel *chan = zs_conschan;
- unsigned long flags;
-
- if(!chan)
- return;
-
- save_flags(flags); cli();
- zs_put_char(chan, ch);
- restore_flags(flags);
-}
-/* These are for receiving and sending characters under the kgdb
- * source level kernel debugger.
- */
-void putDebugChar(char kgdb_char)
-{
- struct sun_zschannel *chan = zs_kgdbchan;
-
- while((chan->control & Tx_BUF_EMP)==0)
- udelay(5);
- chan->data = kgdb_char;
-}
-
-char getDebugChar(void)
-{
- struct sun_zschannel *chan = zs_kgdbchan;
-
- while((chan->control & Rx_CH_AV)==0)
- barrier();
- return chan->data;
-}
-
-/*
- * Fair output driver allows a process to speak.
- */
-static void rs_fair_output(void)
-{
- int left; /* Output no more than that */
- unsigned long flags;
- struct sun_serial *info = zs_consinfo;
- char c;
-
- if (info == 0) return;
- if (info->xmit_buf == 0) return;
-
- save_flags(flags); cli();
- left = info->xmit_cnt;
- while (left != 0) {
- c = info->xmit_buf[info->xmit_tail];
- info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt--;
- restore_flags(flags);
-
- rs_put_char(c);
-
- cli();
- left = MIN(info->xmit_cnt, left-1);
- }
-
- /* Last character is being transmitted now (hopefully). */
- zs_conschan->control = RES_Tx_P;
- udelay(5);
-
- restore_flags(flags);
- return;
-}
-
-/*
- * zs_console_print is registered for printk.
- */
-static void zs_console_print(const char *s, unsigned count)
-{
- int i;
-
- for (i = 0; i < count; i++, s++) {
- if(*s == '\n')
- rs_put_char('\r');
- rs_put_char(*s);
- }
-
- /* Comment this if you want to have a strict interrupt-driven output */
- rs_fair_output();
-}
-
-static void zs_console_wait_key(void)
-{
- sleep_on(&keypress_wait);
-}
-
-static int zs_console_device(void)
-{
- extern int serial_console;
-
- return MKDEV(TTYAUX_MAJOR, 64 + serial_console - 1);
-}
-
-static void rs_flush_chars(struct tty_struct *tty)
-{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
- return;
-
- if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
- !info->xmit_buf)
- return;
-
- /* Enable transmitter */
- save_flags(flags); cli();
- info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
- write_zsreg(info->zs_channel, 1, info->curregs[1]);
- info->curregs[5] |= TxENAB;
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
-
- /*
- * Send a first (bootstrapping) character. A best solution is
- * to call transmit_chars() here which handles output in a
- * generic way. Current transmit_chars() not only transmits,
- * but resets interrupts also what we do not desire here.
- * XXX Discuss with David.
- */
- zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
- info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt--;
-
- restore_flags(flags);
-}
-
-static int rs_write(struct tty_struct * tty, int from_user,
- const unsigned char *buf, int count)
-{
- int c, total = 0;
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->device, "rs_write"))
- return 0;
-
- if (!info || !info->xmit_buf)
- return 0;
-
- save_flags(flags);
- while (1) {
- cli();
- c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
- SERIAL_XMIT_SIZE - info->xmit_head));
- if (c <= 0)
- break;
-
- if (from_user) {
- down(&tmp_buf_sem);
- copy_from_user(tmp_buf, buf, c);
- c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
- SERIAL_XMIT_SIZE - info->xmit_head));
- memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
- up(&tmp_buf_sem);
- } else
- memcpy(info->xmit_buf + info->xmit_head, buf, c);
- info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt += c;
- restore_flags(flags);
- buf += c;
- count -= c;
- total += c;
- }
-
- if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
- /* Enable transmitter */
- info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
- write_zsreg(info->zs_channel, 1, info->curregs[1]);
- info->curregs[5] |= TxENAB;
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- }
-#if 1
- if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
- zs_put_char(info->zs_channel,
- info->xmit_buf[info->xmit_tail++]);
- info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt--;
- }
-#endif
- restore_flags(flags);
- return total;
-}
-
-static int rs_write_room(struct tty_struct *tty)
-{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
- int ret;
-
- if (serial_paranoia_check(info, tty->device, "rs_write_room"))
- return 0;
- ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
- if (ret < 0)
- ret = 0;
- return ret;
-}
-
-static int rs_chars_in_buffer(struct tty_struct *tty)
-{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-
- if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
- return 0;
- return info->xmit_cnt;
-}
-
-static void rs_flush_buffer(struct tty_struct *tty)
-{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-
- if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
- return;
- cli();
- info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- sti();
- wake_up_interruptible(&tty->write_wait);
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(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)
-{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
-
- printk("throttle %s: %d....\n", _tty_name(tty, buf),
- tty->ldisc.chars_in_buffer(tty));
-#endif
-
- if (serial_paranoia_check(info, tty->device, "rs_throttle"))
- return;
-
- if (I_IXOFF(tty))
- info->x_char = STOP_CHAR(tty);
-
- /* Turn off RTS line */
- cli();
- info->curregs[5] &= ~RTS;
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- sti();
-}
-
-static void rs_unthrottle(struct tty_struct * tty)
-{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
-
- printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
- tty->ldisc.chars_in_buffer(tty));
-#endif
-
- if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
- return;
-
- if (I_IXOFF(tty)) {
- if (info->x_char)
- info->x_char = 0;
- else
- info->x_char = START_CHAR(tty);
- }
-
- /* Assert RTS line */
- cli();
- info->curregs[5] |= RTS;
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- sti();
-}
-
-/*
- * ------------------------------------------------------------
- * rs_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static int get_serial_info(struct sun_serial * info,
- struct serial_struct * retinfo)
-{
- struct serial_struct tmp;
-
- if (!retinfo)
- return -EFAULT;
- memset(&tmp, 0, sizeof(tmp));
- tmp.type = info->type;
- tmp.line = info->line;
- tmp.port = info->port;
- tmp.irq = info->irq;
- tmp.flags = info->flags;
- tmp.baud_base = info->baud_base;
- tmp.close_delay = info->close_delay;
- tmp.closing_wait = info->closing_wait;
- tmp.custom_divisor = info->custom_divisor;
- copy_to_user_ret(retinfo,&tmp,sizeof(*retinfo), -EFAULT);
- return 0;
-}
+struct sunserial_operations rs_ops = {
+ 0,
+ nop_rs_cons_hook,
+ nop_rs_kgdb_hook,
+ nop_rs_change_mouse_baud,
+ nop_rs_read_proc
+};
-static int set_serial_info(struct sun_serial * info,
- struct serial_struct * new_info)
+int rs_init(void)
{
- struct serial_struct new_serial;
- struct sun_serial old_info;
- int retval = 0;
-
- if (!new_info || copy_from_user(&new_serial,new_info,sizeof(new_serial)))
- return -EFAULT;
- old_info = *info;
+ struct rs_initfunc *init;
+ int err = -ENODEV;
- if (!suser()) {
- if ((new_serial.baud_base != info->baud_base) ||
- (new_serial.type != info->type) ||
- (new_serial.close_delay != info->close_delay) ||
- ((new_serial.flags & ~ZILOG_USR_MASK) !=
- (info->flags & ~ZILOG_USR_MASK)))
- return -EPERM;
- info->flags = ((info->flags & ~ZILOG_USR_MASK) |
- (new_serial.flags & ZILOG_USR_MASK));
- info->custom_divisor = new_serial.custom_divisor;
- goto check_and_exit;
+ init = rs_ops.rs_init;
+ while (init) {
+ err = init->rs_init();
+ init = init->next;
}
-
- if (info->count > 1)
- return -EBUSY;
-
- /*
- * OK, past this point, all the error checking has been done.
- * At this point, we start making changes.....
- */
-
- info->baud_base = new_serial.baud_base;
- info->flags = ((info->flags & ~ZILOG_FLAGS) |
- (new_serial.flags & ZILOG_FLAGS));
- info->custom_divisor = new_serial.custom_divisor;
- info->type = new_serial.type;
- info->close_delay = new_serial.close_delay;
- info->closing_wait = new_serial.closing_wait;
-
-check_and_exit:
- retval = startup(info);
- return retval;
+ return err;
}
-/*
- * get_lsr_info - get line status register info
- *
- * Purpose: Let user call ioctl() to get info when the UART physically
- * is emptied. On bus types like RS485, the transmitter must
- * release the bus after transmitting. This must be done when
- * the transmit shift register is empty, not be done when the
- * transmit holding register is empty. This functionality
- * allows an RS485 driver to be written in user space.
- */
-static int get_lsr_info(struct sun_serial * info, unsigned int *value)
+void rs_cons_hook(int chip, int out, int line)
{
- unsigned char status;
-
- cli();
- status = info->zs_channel->control;
- sti();
- put_user_ret(status,value, -EFAULT);
- return 0;
+ rs_ops.rs_cons_hook(chip, out, line);
}
-static int get_modem_info(struct sun_serial * info, unsigned int *value)
+void rs_kgdb_hook(int channel)
{
- unsigned char status;
- unsigned int result;
-
- cli();
- status = info->zs_channel->control;
- sti();
- result = ((info->curregs[5] & RTS) ? TIOCM_RTS : 0)
- | ((info->curregs[5] & DTR) ? TIOCM_DTR : 0)
- | ((status & DCD) ? TIOCM_CAR : 0)
- | ((status & SYNC) ? TIOCM_DSR : 0)
- | ((status & CTS) ? TIOCM_CTS : 0);
- put_user_ret(result, value, -EFAULT);
- return 0;
+ rs_ops.rs_kgdb_hook(channel);
}
-static int set_modem_info(struct sun_serial * info, unsigned int cmd,
- unsigned int *value)
+void rs_change_mouse_baud(int baud)
{
- unsigned int arg;
-
- get_user_ret(arg, value, -EFAULT);
- switch (cmd) {
- case TIOCMBIS:
- if (arg & TIOCM_RTS)
- info->curregs[5] |= RTS;
- if (arg & TIOCM_DTR)
- info->curregs[5] |= DTR;
- break;
- case TIOCMBIC:
- if (arg & TIOCM_RTS)
- info->curregs[5] &= ~RTS;
- if (arg & TIOCM_DTR)
- info->curregs[5] &= ~DTR;
- break;
- case TIOCMSET:
- info->curregs[5] = ((info->curregs[5] & ~(RTS | DTR))
- | ((arg & TIOCM_RTS) ? RTS : 0)
- | ((arg & TIOCM_DTR) ? DTR : 0));
- break;
- default:
- return -EINVAL;
- }
- cli();
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- sti();
- return 0;
+ rs_ops.rs_change_mouse_baud(baud);
}
-/*
- * This routine sends a break character out the serial port.
- */
-static void send_break( struct sun_serial * info, int duration)
+int rs_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
{
- if (!info->port)
- return;
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + duration;
- cli();
- write_zsreg(info->zs_channel, 5, (info->curregs[5] | SND_BRK));
- schedule();
- write_zsreg(info->zs_channel, 5, info->curregs[5]);
- sti();
+ return rs_ops.rs_read_proc(page, start, off, count, eof, data);
}
-static int rs_ioctl(struct tty_struct *tty, struct file * file,
- unsigned int cmd, unsigned long arg)
+int register_serial(struct serial_struct *req)
{
- struct sun_serial * info = (struct sun_serial *)tty->driver_data;
- int retval;
-
- if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
- return -ENODEV;
-
- if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
- (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
- }
-
- switch (cmd) {
- case TCSBRK: /* SVID version: non-zero arg --> no break */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (!arg)
- send_break(info, HZ/4); /* 1/4 second */
- return 0;
- case TCSBRKP: /* support for POSIX tcsendbreak() */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- send_break(info, arg ? arg*(HZ/10) : HZ/4);
- return 0;
- case TIOCGSOFTCAR:
- put_user_ret(C_CLOCAL(tty) ? 1 : 0,
- (unsigned long *) arg, -EFAULT);
- return 0;
- case TIOCSSOFTCAR:
- get_user_ret(arg, (unsigned long *) arg, -EFAULT);
- tty->termios->c_cflag =
- ((tty->termios->c_cflag & ~CLOCAL) |
- (arg ? CLOCAL : 0));
- return 0;
- case TIOCMGET:
- return get_modem_info(info, (unsigned int *) arg);
- case TIOCMBIS:
- case TIOCMBIC:
- case TIOCMSET:
- return set_modem_info(info, cmd, (unsigned int *) arg);
- case TIOCGSERIAL:
- return get_serial_info(info,
- (struct serial_struct *) arg);
- case TIOCSSERIAL:
- return set_serial_info(info,
- (struct serial_struct *) arg);
- case TIOCSERGETLSR: /* Get line status register */
- return get_lsr_info(info, (unsigned int *) arg);
-
- case TIOCSERGSTRUCT:
- copy_to_user_ret((struct sun_serial *) arg,
- info, sizeof(struct sun_serial), -EFAULT);
- return 0;
-
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
+ return -1;
}
-static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+void unregister_serial(int line)
{
- struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-
- if (tty->termios->c_cflag == old_termios->c_cflag)
- return;
-
- change_speed(info);
-
- if ((old_termios->c_cflag & CRTSCTS) &&
- !(tty->termios->c_cflag & CRTSCTS)) {
- tty->hw_stopped = 0;
- rs_start(tty);
- }
}
-/*
- * ------------------------------------------------------------
- * 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
- * ZILOG 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)
+void
+sunserial_setinitfunc(unsigned long *memory_start, int (*init) (void))
{
- struct sun_serial * info = (struct sun_serial *)tty->driver_data;
- unsigned long flags;
+ struct rs_initfunc *rs_init;
- if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
- return;
-
- save_flags(flags); cli();
-
- if (tty_hung_up_p(filp)) {
- restore_flags(flags);
- return;
- }
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_close tty-%d, count = %d\n", info->line, info->count);
-#endif
- if ((tty->count == 1) && (info->count != 1)) {
- /*
- * Uh, oh. tty->count is 1, which means that the tty
- * structure will be freed. Info->count should always
- * be one in these conditions. If it's greater than
- * one, we've got real problems, since it means the
- * serial port won't be shutdown.
- */
- printk("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",
- info->line, info->count);
- info->count = 0;
- }
- if (info->count) {
- restore_flags(flags);
- return;
- }
- info->flags |= ZILOG_CLOSING;
- /*
- * Save the termios structure, since this port may have
- * separate termios for callout and dialin.
- */
- if (info->flags & ZILOG_NORMAL_ACTIVE)
- info->normal_termios = *tty->termios;
- if (info->flags & ZILOG_CALLOUT_ACTIVE)
- info->callout_termios = *tty->termios;
- /*
- * Now we wait for the transmit buffer to clear; and we notify
- * the line discipline to only process XON/XOFF characters.
- */
- tty->closing = 1;
- if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
- tty_wait_until_sent(tty, info->closing_wait);
- /*
- * At this point we stop accepting input. To do this, we
- * disable the receive line status interrupts, and tell the
- * interrupt driver to stop checking the data ready bit in the
- * line status register.
- */
- /** if (!info->iscons) ... **/
- info->curregs[3] &= ~RxENAB;
- write_zsreg(info->zs_channel, 3, info->curregs[3]);
- info->curregs[1] &= ~(RxINT_MASK);
- write_zsreg(info->zs_channel, 1, info->curregs[1]);
- ZS_CLEARFIFO(info->zs_channel);
+ *memory_start = (*memory_start + 7) & ~(7);
+ rs_init = (struct rs_initfunc *) *memory_start;
+ *memory_start += sizeof(struct rs_initfunc);
- shutdown(info);
- if (tty->driver.flush_buffer)
- tty->driver.flush_buffer(tty);
- if (tty->ldisc.flush_buffer)
- tty->ldisc.flush_buffer(tty);
- tty->closing = 0;
- info->event = 0;
- info->tty = 0;
- if (tty->ldisc.num != ldiscs[N_TTY].num) {
- if (tty->ldisc.close)
- (tty->ldisc.close)(tty);
- tty->ldisc = ldiscs[N_TTY];
- tty->termios->c_line = N_TTY;
- if (tty->ldisc.open)
- (tty->ldisc.open)(tty);
- }
- if (info->blocked_open) {
- if (info->close_delay) {
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + info->close_delay;
- schedule();
- }
- wake_up_interruptible(&info->open_wait);
- }
- info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
- ZILOG_CLOSING);
- wake_up_interruptible(&info->close_wait);
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_close tty-%d exiting, count = %d\n", info->line, info->count);
-#endif
- restore_flags(flags);
+ rs_init->rs_init = init;
+ rs_init->next = rs_ops.rs_init;
+ rs_ops.rs_init = rs_init;
}
-/*
- * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-void rs_hangup(struct tty_struct *tty)
-{
- struct sun_serial * info = (struct sun_serial *)tty->driver_data;
-
- if (serial_paranoia_check(info, tty->device, "rs_hangup"))
- return;
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_hangup<%p: tty-%d, count = %d bye\n",
- __builtin_return_address(0), info->line, info->count);
+extern int zs_probe(unsigned long *);
+#ifdef CONFIG_SAB82532
+extern int sab82532_probe(unsigned long *);
#endif
-
- rs_flush_buffer(tty);
- shutdown(info);
- info->event = 0;
- info->count = 0;
- info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE);
- info->tty = 0;
- wake_up_interruptible(&info->open_wait);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_open() and friends
- * ------------------------------------------------------------
- */
-static int block_til_ready(struct tty_struct *tty, struct file * filp,
- struct sun_serial *info)
-{
- struct wait_queue wait = { current, NULL };
- int retval;
- int do_clocal = 0;
- unsigned char r0;
-
- /*
- * If the device is in the middle of being closed, then block
- * until it's done, and then try again.
- */
- if (info->flags & ZILOG_CLOSING) {
- interruptible_sleep_on(&info->close_wait);
-#ifdef SERIAL_DO_RESTART
- if (info->flags & ZILOG_HUP_NOTIFY)
- return -EAGAIN;
- else
- return -ERESTARTSYS;
-#else
- return -EAGAIN;
-#endif
- }
-
- /*
- * If this is a callout device, then just make sure the normal
- * device isn't being used.
- */
- if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
- if (info->flags & ZILOG_NORMAL_ACTIVE)
- return -EBUSY;
- if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
- (info->flags & ZILOG_SESSION_LOCKOUT) &&
- (info->session != current->session))
- return -EBUSY;
- if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
- (info->flags & ZILOG_PGRP_LOCKOUT) &&
- (info->pgrp != current->pgrp))
- return -EBUSY;
- info->flags |= ZILOG_CALLOUT_ACTIVE;
- return 0;
- }
-
- /*
- * If non-blocking mode is set, or the port is not enabled,
- * then make the check up front and then exit.
- */
- if ((filp->f_flags & O_NONBLOCK) ||
- (tty->flags & (1 << TTY_IO_ERROR))) {
- if (info->flags & ZILOG_CALLOUT_ACTIVE)
- return -EBUSY;
- info->flags |= ZILOG_NORMAL_ACTIVE;
- return 0;
- }
-
- if (info->flags & ZILOG_CALLOUT_ACTIVE) {
- if (info->normal_termios.c_cflag & CLOCAL)
- do_clocal = 1;
- } else {
- if (tty->termios->c_cflag & CLOCAL)
- do_clocal = 1;
- }
-
- /*
- * Block waiting for the carrier detect and the line to become
- * free (i.e., not in use by the callout). While we are in
- * this loop, info->count is dropped by one, so that
- * rs_close() knows when to free things. We restore it upon
- * exit, either normal or abnormal.
- */
- retval = 0;
- add_wait_queue(&info->open_wait, &wait);
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready before block: ttys%d, count = %d\n",
- info->line, info->count);
+#ifdef __sparc_v9__
+extern int ps2kbd_probe(unsigned long *);
+extern int su_probe(unsigned long *);
#endif
- cli();
- if(!tty_hung_up_p(filp))
- info->count--;
- sti();
- info->blocked_open++;
- while (1) {
- cli();
- if (!(info->flags & ZILOG_CALLOUT_ACTIVE))
- zs_rtsdtr(info, 1);
- sti();
- current->state = TASK_INTERRUPTIBLE;
- if (tty_hung_up_p(filp) ||
- !(info->flags & ZILOG_INITIALIZED)) {
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready hup-ed: ttys%d, count = %d\n",
- info->line, info->count);
-#endif
-#ifdef SERIAL_DO_RESTART
- if (info->flags & ZILOG_HUP_NOTIFY)
- retval = -EAGAIN;
- else
- retval = -ERESTARTSYS;
-#else
- retval = -EAGAIN;
-#endif
- break;
- }
-
- cli();
- r0 = read_zsreg(info->zs_channel, R0);
- sti();
- if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
- !(info->flags & ZILOG_CLOSING) &&
- (do_clocal || (DCD & r0)))
- break;
- if (current->signal & ~current->blocked) {
- retval = -ERESTARTSYS;
- break;
- }
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready blocking: ttys%d, count = %d\n",
- info->line, info->count);
-#endif
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&info->open_wait, &wait);
- if (!tty_hung_up_p(filp))
- info->count++;
- info->blocked_open--;
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready after blocking: ttys%d, count = %d\n",
- info->line, info->count);
-#endif
- if (retval)
- return retval;
- info->flags |= ZILOG_NORMAL_ACTIVE;
- return 0;
-}
-
-/*
- * This routine is called whenever a serial port is opened. It
- * enables interrupts for a serial port, linking in its ZILOG structure into
- * the IRQ chain. It also performs the serial-specific
- * initialization for the tty structure.
- */
-int rs_open(struct tty_struct *tty, struct file * filp)
-{
- struct sun_serial *info;
- int retval, line;
-
- line = MINOR(tty->device) - tty->driver.minor_start;
- /* The zilog lines for the mouse/keyboard must be
- * opened using their respective drivers.
- */
- if ((line < 0) || (line >= NUM_CHANNELS))
- return -ENODEV;
- if((line == KEYBOARD_LINE) || (line == MOUSE_LINE))
- return -ENODEV;
- info = zs_soft + line;
- /* Is the kgdb running over this line? */
- if (info->kgdb_channel)
- return -ENODEV;
- if (serial_paranoia_check(info, tty->device, "rs_open"))
- return -ENODEV;
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
- info->count);
-#endif
- if (info->tty != 0 && info->tty != tty) {
- /* Never happen? */
- printk("rs_open %s%d, tty overwrite.\n", tty->driver.name, info->line);
- return -EBUSY;
- }
- info->count++;
- tty->driver_data = info;
- info->tty = tty;
-
- /*
- * Start up serial port
- */
- retval = startup(info);
- if (retval)
- return retval;
-
- retval = block_til_ready(tty, filp, info);
- if (retval) {
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_open returning after block_til_ready with %d\n",
- retval);
-#endif
- return retval;
- }
-
- if ((info->count == 1) && (info->flags & ZILOG_SPLIT_TERMIOS)) {
- if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
- *tty->termios = info->normal_termios;
- else
- *tty->termios = info->callout_termios;
- change_speed(info);
- }
-
- info->session = current->session;
- info->pgrp = current->pgrp;
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_open ttys%d successful...", info->line);
-#endif
- return 0;
-}
-
-/* Finally, routines used to initialize the serial driver. */
-
-static void show_serial_version(void)
-{
- char *revision = "$Revision: 1.2 $";
- char *version, *p;
- version = strchr(revision, ' ');
- p = strchr(++version, ' ');
- *p = '\0';
- printk("Sparc Zilog8530 serial driver version %s\n", version);
- *p = ' ';
-}
-
-/* Probe the PROM for the request zs chip number.
- *
- * Note: The Sun Voyager shows two addresses and two intr for it's
- * Zilogs, what the second does, I don't know. It does work
- * with using only the first number of each property.
- */
-static struct sun_zslayout *get_zs(int chip)
+unsigned long
+sun_serial_setup(unsigned long memory_start)
{
- struct linux_prom_irqs tmp_irq[2];
- unsigned int paddr = 0;
- unsigned int vaddr[2] = { 0, 0 };
- int zsnode, tmpnode, iospace, slave, len, seen, sun4u_irq;
- static int irq = 0;
+ /* Probe for controllers. */
+ if (zs_probe(&memory_start) == 0)
+ return memory_start;
-#if CONFIG_AP1000
- printk("No zs chip\n");
- return NULL;
+#ifdef CONFIG_SAB82532
+ sab82532_probe(&memory_start);
#endif
-
- iospace = 0;
- if(chip < 0 || chip >= NUM_SERIAL)
- panic("get_zs bogon zs chip number");
-
- if(sparc_cpu_model == sun4) {
- /* Grrr, these have to be hardcoded aieee */
- switch(chip) {
- case 0:
- paddr = 0xf1000000;
- break;
- case 1:
- paddr = 0xf0000000;
- break;
- };
- iospace = 0;
- zs_nodes[chip] = 0;
- if(!irq)
- zilog_irq = irq = 12;
- vaddr[0] = (unsigned long)
- sparc_alloc_io(paddr, 0, 8,
- "Zilog Serial", iospace, 0);
- } else {
- /* Can use the prom for other machine types */
- zsnode = prom_getchild(prom_root_node);
- if (sparc_cpu_model == sun4d) {
- int board, node;
-
- tmpnode = zsnode;
- while (tmpnode && (tmpnode = prom_searchsiblings(tmpnode, "cpu-unit"))) {
- board = prom_getintdefault (tmpnode, "board#", -1);
- if (board == (chip >> 1)) {
- node = prom_getchild(tmpnode);
- if (node && (node = prom_searchsiblings(node, "bootbus"))) {
- zsnode = node;
- break;
- }
- }
- tmpnode = prom_getsibling(tmpnode);
- }
- if (!tmpnode)
- panic ("get_zs: couldn't find board%d's bootbus\n", chip >> 1);
- } else if (sparc_cpu_model == sun4u) {
- tmpnode = prom_searchsiblings(zsnode, "sbus");
- if(tmpnode)
- zsnode = prom_getchild(tmpnode);
- } else {
- tmpnode = prom_searchsiblings(zsnode, "obio");
- if(tmpnode)
- zsnode = prom_getchild(tmpnode);
- }
- if(!zsnode)
- panic("get_zs no zs serial prom node");
- seen = 0;
- while(zsnode) {
- zsnode = prom_searchsiblings(zsnode, "zs");
- slave = prom_getintdefault(zsnode, "slave", -1);
- if((slave == chip) ||
- (sparc_cpu_model == sun4u && seen == chip)) {
- /* The one we want */
- len = prom_getproperty(zsnode, "address",
- (void *) vaddr,
- sizeof(vaddr));
- if (len % sizeof(unsigned int)) {
- prom_printf("WHOOPS: proplen for %s "
- "was %d, need multiple of "
- "%d\n", "address", len,
- sizeof(unsigned int));
- panic("zilog: address property");
- }
- zs_nodes[chip] = zsnode;
- if(sparc_cpu_model == sun4u) {
- len = prom_getproperty(zsnode, "interrupts",
- (char *) &sun4u_irq,
- sizeof(tmp_irq));
- tmp_irq[0].pri = sun4u_irq;
- } else {
- len = prom_getproperty(zsnode, "intr",
- (char *) tmp_irq,
- sizeof(tmp_irq));
- if (len % sizeof(struct linux_prom_irqs)) {
- prom_printf(
- "WHOOPS: proplen for %s "
- "was %d, need multiple of "
- "%d\n", "address", len,
- sizeof(struct linux_prom_irqs));
- panic("zilog: address property");
- }
- }
- if(!irq) {
- irq = zilog_irq = tmp_irq[0].pri;
- } else {
- if(tmp_irq[0].pri != irq)
- panic("zilog: bogon irqs");
- }
- break;
- }
- zsnode = prom_getsibling(zsnode);
- seen++;
- }
- if(!zsnode)
- panic("get_zs whee chip not found");
- }
- if(!vaddr[0])
- panic("get_zs whee no serial chip mappable");
-
- return (struct sun_zslayout *)(unsigned long) vaddr[0];
-}
-
-static inline void
-init_zscons_termios(struct termios *termios)
-{
- char mode[16], buf[16];
- char *mode_prop = "ttyX-mode";
- char *cd_prop = "ttyX-ignore-cd";
- char *dtr_prop = "ttyX-rts-dtr-off";
- char *s;
- int baud, bits, cflag;
- char parity;
- int topnd, nd;
- int channel, stop;
- int carrier = 0;
- int rtsdtr = 1;
- extern int serial_console;
-
- if (!serial_console)
- return;
-
- if (serial_console == 1) {
- mode_prop[3] = 'a';
- cd_prop[3] = 'a';
- dtr_prop[3] = 'a';
- } else {
- mode_prop[3] = 'b';
- cd_prop[3] = 'b';
- dtr_prop[3] = 'b';
- }
-
- topnd = prom_getchild(prom_root_node);
- nd = prom_searchsiblings(topnd, "options");
- if (!nd) {
- strcpy(mode, "9600,8,n,1,-");
- goto no_options;
- }
-
- if (!prom_node_has_property(nd, mode_prop)) {
- strcpy(mode, "9600,8,n,1,-");
- goto no_options;
- }
-
- memset(mode, 0, sizeof(mode));
- prom_getstring(nd, mode_prop, mode, sizeof(mode));
-
- if (prom_node_has_property(nd, cd_prop)) {
- memset(buf, 0, sizeof(buf));
- prom_getstring(nd, cd_prop, buf, sizeof(buf));
- if (!strcmp(buf, "false"))
- carrier = 1;
-
- /* XXX this is unused below. */
- }
-
- if (prom_node_has_property(nd, cd_prop)) {
- memset(buf, 0, sizeof(buf));
- prom_getstring(nd, cd_prop, buf, sizeof(buf));
- if (!strcmp(buf, "false"))
- rtsdtr = 0;
-
- /* XXX this is unused below. */
- }
-
-no_options:
- cflag = CREAD | HUPCL | CLOCAL;
-
- s = mode;
- baud = simple_strtoul(s, 0, 0);
- s = strchr(s, ',');
- bits = simple_strtoul(++s, 0, 0);
- s = strchr(s, ',');
- parity = *(++s);
- s = strchr(s, ',');
- stop = simple_strtoul(++s, 0, 0);
- s = strchr(s, ',');
- /* XXX handshake is not handled here. */
-
- for (channel = 0; channel < NUM_CHANNELS; channel++)
- if (zs_soft[channel].is_cons)
- break;
-
- switch (baud) {
- case 150:
- cflag |= B150;
- break;
- case 300:
- cflag |= B300;
- break;
- case 600:
- cflag |= B600;
- break;
- case 1200:
- cflag |= B1200;
- break;
- case 2400:
- cflag |= B2400;
- break;
- case 4800:
- cflag |= B4800;
- break;
- default:
- baud = 9600;
- case 9600:
- cflag |= B9600;
- break;
- case 19200:
- cflag |= B19200;
- break;
- case 38400:
- cflag |= B38400;
- break;
- }
- zs_soft[channel].zs_baud = baud;
-
- switch (bits) {
- case 5:
- zscons_regs[3] = Rx5 | RxENAB;
- zscons_regs[5] = Tx5 | TxENAB;
- zs_soft[channel].parity_mask = 0x1f;
- cflag |= CS5;
- break;
- case 6:
- zscons_regs[3] = Rx6 | RxENAB;
- zscons_regs[5] = Tx6 | TxENAB;
- zs_soft[channel].parity_mask = 0x3f;
- cflag |= CS6;
- break;
- case 7:
- zscons_regs[3] = Rx7 | RxENAB;
- zscons_regs[5] = Tx7 | TxENAB;
- zs_soft[channel].parity_mask = 0x7f;
- cflag |= CS7;
- break;
- default:
- case 8:
- zscons_regs[3] = Rx8 | RxENAB;
- zscons_regs[5] = Tx8 | TxENAB;
- zs_soft[channel].parity_mask = 0xff;
- cflag |= CS8;
- break;
- }
- zscons_regs[5] |= DTR;
-
- switch (parity) {
- case 'o':
- zscons_regs[4] |= PAR_ENAB;
- cflag |= (PARENB | PARODD);
- break;
- case 'e':
- zscons_regs[4] |= (PAR_ENAB | PAR_EVEN);
- cflag |= PARENB;
- break;
- default:
- case 'n':
- break;
- }
-
- switch (stop) {
- default:
- case 1:
- zscons_regs[4] |= SB1;
- break;
- case 2:
- cflag |= CSTOPB;
- zscons_regs[4] |= SB2;
- break;
- }
-
- termios->c_cflag = cflag;
-}
-
-static inline void
-rs_cons_check(struct sun_serial *ss, int channel)
-{
- int i, o, io;
- static int consout_registered = 0;
- static int msg_printed = 0;
- static struct console console = {
- zs_console_print, 0,
- zs_console_wait_key, zs_console_device };
-
- i = o = io = 0;
-
- /* Is this one of the serial console lines? */
- if((zs_cons_chanout != channel) &&
- (zs_cons_chanin != channel))
- return;
- zs_conschan = ss->zs_channel;
- zs_consinfo = ss;
-
- /* Register the console output putchar, if necessary */
- if((zs_cons_chanout == channel)) {
- o = 1;
- /* double whee.. */
- if(!consout_registered) {
- extern void serial_finish_init (void (*)(const char *, unsigned count));
-
- serial_finish_init (zs_console_print);
- register_console(&console);
- consout_registered = 1;
- }
- }
-
- /* If this is console input, we handle the break received
- * status interrupt on this line to mean prom_halt().
- */
- if(zs_cons_chanin == channel) {
- ss->break_abort = 1;
- i = 1;
- }
- if(o && i)
- io = 1;
-
- /* Set flag variable for this port so that it cannot be
- * opened for other uses by accident.
- */
- ss->is_cons = 1;
-
- if(io) {
- if(!msg_printed) {
- printk("zs%d: console I/O\n", ((channel>>1)&1));
- msg_printed = 1;
- }
- } else {
- printk("zs%d: console %s\n", ((channel>>1)&1),
- (i==1 ? "input" : (o==1 ? "output" : "WEIRD")));
- }
-}
-
-extern void keyboard_zsinit(void);
-extern void sun_mouse_zsinit(void);
-
-/* This is for the auto baud rate detection in the mouse driver. */
-void zs_change_mouse_baud(int newbaud)
-{
- int channel = MOUSE_LINE;
- int brg;
-
- zs_soft[channel].zs_baud = newbaud;
- brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
- (ZS_CLOCK / zs_soft[channel].clk_divisor));
- write_zsreg(zs_soft[channel].zs_channel, R12, (brg & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R13, ((brg >> 8) & 0xff));
-}
-
-__initfunc(unsigned long sun_serial_setup (unsigned long memory_start))
-{
- char *p;
- int i;
-
- if (sparc_cpu_model == sun4d) {
- int node = prom_searchsiblings(prom_getchild(prom_root_node), "boards");
- NUM_SERIAL = 0;
- if (!node)
- panic ("Cannot find out count of boards");
- else
- node = prom_getchild(node);
- while (node && (node = prom_searchsiblings(node, "bif"))) {
- NUM_SERIAL += 2;
- node = prom_getsibling(node);
- }
- }
- p = (char *)((memory_start + 7) & ~7);
- zs_chips = (struct sun_zslayout **)(p);
- i = NUM_SERIAL * sizeof (struct sun_zslayout *);
- zs_channels = (struct sun_zschannel **)(p + i);
- i += NUM_CHANNELS * sizeof (struct sun_zschannel *);
- zs_nodes = (int *)(p + i);
- i += NUM_SERIAL * sizeof (int);
- zs_soft = (struct sun_serial *)(p + i);
- i += NUM_CHANNELS * sizeof (struct sun_serial);
- zs_ttys = (struct tty_struct *)(p + i);
- i += NUM_CHANNELS * sizeof (struct tty_struct);
- serial_table = (struct tty_struct **)(p + i);
- i += NUM_CHANNELS * sizeof (struct tty_struct *);
- serial_termios = (struct termios **)(p + i);
- i += NUM_CHANNELS * sizeof (struct termios *);
- serial_termios_locked = (struct termios **)(p + i);
- i += NUM_CHANNELS * sizeof (struct termios *);
- memset (p, 0, i);
- return (((unsigned long)p) + i + 7) & ~7;
-}
-
-__initfunc(int rs_init(void))
-{
- int chip, channel, brg, i;
- unsigned long flags;
- struct sun_serial *info;
- char dummy;
-
-#if CONFIG_AP1000
- printk("not doing rs_init()\n");
- return 0;
+#ifdef __sparc_v9__
+ if (ps2kbd_probe(&memory_start) == 0)
+ return memory_start;
+ if (su_probe(&memory_start) == 0)
+ return memory_start;
#endif
- /* Setup base handler, and timer table. */
- init_bh(SERIAL_BH, do_serial_bh);
- timer_table[RS_TIMER].fn = rs_timer;
- timer_table[RS_TIMER].expires = 0;
-
- show_serial_version();
-
- /* Initialize the tty_driver structure */
- /* SPARC: Not all of this is exactly right for us. */
-
- memset(&serial_driver, 0, sizeof(struct tty_driver));
- serial_driver.magic = TTY_DRIVER_MAGIC;
- serial_driver.driver_name = "serial";
- serial_driver.name = "ttyS";
- serial_driver.major = TTY_MAJOR;
- serial_driver.minor_start = 64;
- serial_driver.num = NUM_CHANNELS;
- serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
- serial_driver.subtype = SERIAL_TYPE_NORMAL;
- serial_driver.init_termios = tty_std_termios;
-
- serial_driver.init_termios.c_cflag =
- B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- serial_driver.flags = TTY_DRIVER_REAL_RAW;
- serial_driver.refcount = &serial_refcount;
- serial_driver.table = serial_table;
- serial_driver.termios = serial_termios;
- serial_driver.termios_locked = serial_termios_locked;
-
- serial_driver.open = rs_open;
- serial_driver.close = rs_close;
- serial_driver.write = rs_write;
- serial_driver.flush_chars = rs_flush_chars;
- serial_driver.write_room = rs_write_room;
- serial_driver.chars_in_buffer = rs_chars_in_buffer;
- serial_driver.flush_buffer = rs_flush_buffer;
- serial_driver.ioctl = rs_ioctl;
- serial_driver.throttle = rs_throttle;
- serial_driver.unthrottle = rs_unthrottle;
- serial_driver.set_termios = rs_set_termios;
- serial_driver.stop = rs_stop;
- serial_driver.start = rs_start;
- serial_driver.hangup = rs_hangup;
-
- /* I'm too lazy, someone write versions of this for us. -DaveM */
- serial_driver.read_proc = 0;
- serial_driver.proc_entry = 0;
-
- init_zscons_termios(&serial_driver.init_termios);
-
- /*
- * The callout device is just like normal device except for
- * major number and the subtype code.
- */
- callout_driver = serial_driver;
- callout_driver.name = "cua";
- callout_driver.major = TTYAUX_MAJOR;
- callout_driver.subtype = SERIAL_TYPE_CALLOUT;
-
- if (tty_register_driver(&serial_driver))
- panic("Couldn't register serial driver\n");
- if (tty_register_driver(&callout_driver))
- panic("Couldn't register callout driver\n");
-
- save_flags(flags); cli();
-
- /* Set up our interrupt linked list */
- zs_chain = &zs_soft[0];
- for(channel = 0; channel < NUM_CHANNELS - 1; channel++)
- zs_soft[channel].zs_next = &zs_soft[channel + 1];
- zs_soft[channel + 1].zs_next = 0;
-
- /* Initialize Softinfo */
- for(chip = 0; chip < NUM_SERIAL; chip++) {
- /* If we are doing kgdb over one of the channels on
- * chip zero, kgdb_channel will be set to 1 by the
- * rs_kgdb_hook() routine below.
- */
- if(!zs_chips[chip]) {
- zs_chips[chip] = get_zs(chip);
- /* Two channels per chip */
- zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
- zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
- zs_soft[(chip*2)].kgdb_channel = 0;
- zs_soft[(chip*2)+1].kgdb_channel = 0;
- }
-
- /* First, set up channel A on this chip. */
- channel = chip * 2;
- zs_soft[channel].zs_channel = zs_channels[channel];
- zs_soft[channel].change_needed = 0;
- zs_soft[channel].clk_divisor = 16;
- zs_soft[channel].cons_keyb = 0;
- zs_soft[channel].cons_mouse = 0;
- zs_soft[channel].channelA = 1;
-
- /* Now, channel B */
- channel++;
- zs_soft[channel].zs_channel = zs_channels[channel];
- zs_soft[channel].change_needed = 0;
- zs_soft[channel].clk_divisor = 16;
- zs_soft[channel].cons_keyb = 0;
- zs_soft[channel].cons_mouse = 0;
- zs_soft[channel].channelA = 0;
- }
-
- /* Initialize Hardware */
- for(channel = 0; channel < NUM_CHANNELS; channel++) {
-
- /* Hardware reset each chip */
- if (!(channel & 1)) {
- write_zsreg(zs_soft[channel].zs_channel, R9, FHWRES);
- udelay(20); /* wait for some old sun4's */
- dummy = read_zsreg(zs_soft[channel].zs_channel, R0);
- }
-
- if(channel == KEYBOARD_LINE) {
- zs_soft[channel].cons_keyb = 1;
- zs_soft[channel].parity_mask = 0xff;
- zs_kbdchan = zs_soft[channel].zs_channel;
-
- write_zsreg(zs_soft[channel].zs_channel, R4,
- (PAR_EVEN | X16CLK | SB1));
- write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
- write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
- write_zsreg(zs_soft[channel].zs_channel, R9, NV);
- write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
- write_zsreg(zs_soft[channel].zs_channel, R11,
- (TCBR | RCBR));
- zs_soft[channel].zs_baud = 1200;
- brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
- ZS_CLOCK/zs_soft[channel].clk_divisor);
- write_zsreg(zs_soft[channel].zs_channel, R12,
- (brg & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R13,
- ((brg >> 8) & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
-
- /* Enable Rx/Tx, IRQs, and inform kbd driver */
- write_zsreg(zs_soft[channel].zs_channel, R14,
- (BRSRC | BRENAB));
- write_zsreg(zs_soft[channel].zs_channel, R3,
- (Rx8 | RxENAB));
- write_zsreg(zs_soft[channel].zs_channel, R5,
- (Tx8 | TxENAB | DTR | RTS));
-
- write_zsreg(zs_soft[channel].zs_channel, R15,
- (DCDIE | CTSIE | TxUIE | BRKIE));
- write_zsreg(zs_soft[channel].zs_channel, R0,
- RES_EXT_INT);
- write_zsreg(zs_soft[channel].zs_channel, R0,
- RES_EXT_INT);
-
- write_zsreg(zs_soft[channel].zs_channel, R1,
- (EXT_INT_ENAB | INT_ALL_Rx));
- write_zsreg(zs_soft[channel].zs_channel, R9,
- (NV | MIE));
- ZS_CLEARERR(zs_soft[channel].zs_channel);
- ZS_CLEARFIFO(zs_soft[channel].zs_channel);
- } else if(channel == MOUSE_LINE) {
- zs_soft[channel].cons_mouse = 1;
- zs_soft[channel].parity_mask = 0xff;
- zs_mousechan = zs_soft[channel].zs_channel;
-
- write_zsreg(zs_soft[channel].zs_channel, R4,
- (PAR_EVEN | X16CLK | SB1));
- write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
- write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
- write_zsreg(zs_soft[channel].zs_channel, R9, NV);
- write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
- write_zsreg(zs_soft[channel].zs_channel, R11,
- (TCBR | RCBR));
-
- zs_soft[channel].zs_baud = 4800;
- brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
- ZS_CLOCK/zs_soft[channel].clk_divisor);
- write_zsreg(zs_soft[channel].zs_channel, R12,
- (brg & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R13,
- ((brg >> 8) & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
-
- /* Enable Rx, IRQs, and inform mouse driver */
- write_zsreg(zs_soft[channel].zs_channel, R14,
- (BRSRC | BRENAB));
- write_zsreg(zs_soft[channel].zs_channel, R3,
- (Rx8 | RxENAB));
- write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
-
- write_zsreg(zs_soft[channel].zs_channel, R15,
- (DCDIE | CTSIE | TxUIE | BRKIE));
- write_zsreg(zs_soft[channel].zs_channel, R0,
- RES_EXT_INT);
- write_zsreg(zs_soft[channel].zs_channel, R0,
- RES_EXT_INT);
-
- write_zsreg(zs_soft[channel].zs_channel, R1,
- (EXT_INT_ENAB | INT_ALL_Rx));
- write_zsreg(zs_soft[channel].zs_channel, R9,
- (NV | MIE));
-
- sun_mouse_zsinit();
- } else if (zs_soft[channel].is_cons) {
- brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
- ZS_CLOCK/zs_soft[channel].clk_divisor);
- zscons_regs[12] = brg & 0xff;
- zscons_regs[13] = (brg >> 8) & 0xff;
-
- memcpy(zs_soft[channel].curregs, zscons_regs, sizeof(zscons_regs));
- load_zsregs(&zs_soft[channel], zscons_regs);
-
- ZS_CLEARERR(zs_soft[channel].zs_channel);
- ZS_CLEARFIFO(zs_soft[channel].zs_channel);
- } else if (zs_soft[channel].kgdb_channel) {
- /* If this is the kgdb line, enable interrupts because
- * we now want to receive the 'control-c' character
- * from the client attached to us asynchronously.
- */
- zs_soft[channel].parity_mask = 0xff;
- kgdb_chaninit(&zs_soft[channel], 1,
- zs_soft[channel].zs_baud);
- } else {
- zs_soft[channel].parity_mask = 0xff;
- write_zsreg(zs_soft[channel].zs_channel, R4,
- (PAR_EVEN | X16CLK | SB1));
- write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
- write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
- write_zsreg(zs_soft[channel].zs_channel, R9, NV);
- write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
- write_zsreg(zs_soft[channel].zs_channel, R11,
- (RCBR | TCBR));
- zs_soft[channel].zs_baud = 9600;
- brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
- ZS_CLOCK/zs_soft[channel].clk_divisor);
- write_zsreg(zs_soft[channel].zs_channel, R12,
- (brg & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R13,
- ((brg >> 8) & 0xff));
- write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
- write_zsreg(zs_soft[channel].zs_channel, R14,
- (BRSRC | BRENAB));
- write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
- write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
- write_zsreg(zs_soft[channel].zs_channel, R15, DCDIE);
- write_zsreg(zs_soft[channel].zs_channel, R9, NV | MIE);
- write_zsreg(zs_soft[channel].zs_channel, R0,
- RES_EXT_INT);
- write_zsreg(zs_soft[channel].zs_channel, R0,
- RES_EXT_INT);
- }
- }
-
- for (info = zs_chain, i=0; info; info = info->zs_next, i++) {
- info->magic = SERIAL_MAGIC;
- info->port = (long) info->zs_channel;
- info->line = i;
- info->tty = 0;
- info->irq = zilog_irq;
- info->custom_divisor = 16;
- info->close_delay = 50;
- info->closing_wait = 3000;
- info->x_char = 0;
- info->event = 0;
- info->count = 0;
- info->blocked_open = 0;
- info->tqueue.routine = do_softint;
- info->tqueue.data = info;
- info->tqueue_hangup.routine = do_serial_hangup;
- info->tqueue_hangup.data = info;
- info->callout_termios = callout_driver.init_termios;
- info->normal_termios = serial_driver.init_termios;
- info->open_wait = 0;
- info->close_wait = 0;
- printk("tty%02d at 0x%04x (irq = %d)", info->line,
- info->port, info->irq);
- printk(" is a Zilog8530\n");
- }
-
- if (request_irq(zilog_irq, rs_interrupt,
- (SA_INTERRUPT | SA_STATIC_ALLOC),
- "Zilog8530", zs_chain))
- panic("Unable to attach zs intr\n");
- restore_flags(flags);
-
- keyboard_zsinit();
- return 0;
-}
-
-/*
- * register_serial and unregister_serial allows for serial ports to be
- * configured at run-time, to support PCMCIA modems.
- */
-/* SPARC: Unused at this time, just here to make things link. */
-int register_serial(struct serial_struct *req)
-{
- return -1;
-}
-
-void unregister_serial(int line)
-{
- return;
-}
-
-/* Hooks for running a serial console. con_init() calls this if the
- * console is being run over one of the ttya/ttyb serial ports.
- * 'chip' should be zero, as chip 1 drives the mouse/keyboard.
- * 'channel' is decoded as 0=TTYA 1=TTYB, note that the channels
- * are addressed backwards, channel B is first, then channel A.
- */
-void
-rs_cons_hook(int chip, int out, int line)
-{
- int channel;
-
- if(chip)
- panic("rs_cons_hook called with chip not zero");
- if(line != 1 && line != 2)
- panic("rs_cons_hook called with line not ttya or ttyb");
- channel = line - 1;
- if(!zs_chips[chip]) {
- zs_chips[chip] = get_zs(chip);
- /* Two channels per chip */
- zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
- zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
- }
- zs_soft[channel].zs_channel = zs_channels[channel];
- zs_soft[channel].change_needed = 0;
- zs_soft[channel].clk_divisor = 16;
- if(out)
- zs_cons_chanout = ((chip * 2) + channel);
- else
- zs_cons_chanin = ((chip * 2) + channel);
- rs_cons_check(&zs_soft[channel], channel);
-}
-
-/* This is called at boot time to prime the kgdb serial debugging
- * serial line. The 'tty_num' argument is 0 for /dev/ttya and 1
- * for /dev/ttyb which is determined in setup_arch() from the
- * boot command line flags.
- */
-void
-rs_kgdb_hook(int tty_num)
-{
- int chip = 0;
-
- if(!zs_chips[chip]) {
- zs_chips[chip] = get_zs(chip);
- /* Two channels per chip */
- zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
- zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
- }
- zs_soft[tty_num].zs_channel = zs_channels[tty_num];
- zs_kgdbchan = zs_soft[tty_num].zs_channel;
- zs_soft[tty_num].change_needed = 0;
- zs_soft[tty_num].clk_divisor = 16;
- zs_soft[tty_num].zs_baud = 9600;
- zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */
- zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
- /* Turn on transmitter/receiver at 8-bits/char */
- kgdb_chaninit(&zs_soft[tty_num], 0, 9600);
- ZS_CLEARERR(zs_kgdbchan);
- ZS_CLEARFIFO(zs_kgdbchan);
+ prom_printf("No serial devices found, bailing out.\n");
+ prom_halt();
+ return memory_start;
}
diff --git a/drivers/sbus/char/sunserial.h b/drivers/sbus/char/sunserial.h
index ae4260cad..8877d4b56 100644
--- a/drivers/sbus/char/sunserial.h
+++ b/drivers/sbus/char/sunserial.h
@@ -1,437 +1,48 @@
-/* $Id: sunserial.h,v 1.11 1997/07/08 10:17:23 davem Exp $
- * serial.h: Definitions for the Sparc Zilog serial driver.
+/* $Id: sunserial.h,v 1.13 1997/09/03 11:55:00 ecd Exp $
+ * sunserial.h: SUN serial driver infrastructure.
*
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
-#ifndef _SPARC_SERIAL_H
-#define _SPARC_SERIAL_H
-/* Just one channel */
-struct sun_zschannel {
- volatile unsigned char control;
- volatile unsigned char pad1;
- volatile unsigned char data;
- volatile unsigned char pad2;
-};
+#ifndef _SPARC_SUNSERIAL_H
+#define _SPARC_SUNSERIAL_H 1
-/* The address space layout for each zs chip. Yes they are
- * backwards.
- */
-struct sun_zslayout {
- struct sun_zschannel channelB;
- struct sun_zschannel channelA;
-};
+#include <linux/tty.h>
-/* We need to keep track of the keyboard state, *ahead* of what
- * the keyboard driver sees, which will be later on after the
- * interrupt via tqueue wait queues and/or base-handler processing.
- */
-struct l1a_kbd_state {
- unsigned char kbd_id;
- unsigned char l1_down;
+struct rs_initfunc {
+ int (*rs_init) (void);
+ struct rs_initfunc *next;
};
-#define NUM_ZSREGS 16
-
-struct serial_struct {
- int type;
- int line;
- int port;
- int irq;
- int flags;
- int xmit_fifo_size;
- int custom_divisor;
- int baud_base;
- unsigned short close_delay;
- char reserved_char[2];
- int hub6;
- unsigned short closing_wait; /* time to wait before closing */
- unsigned short closing_wait2; /* no longer used... */
- int reserved[4];
+struct sunserial_operations {
+ struct rs_initfunc *rs_init;
+ void (*rs_cons_hook) (int, int, int);
+ void (*rs_kgdb_hook) (int);
+ void (*rs_change_mouse_baud) (int);
+ int (*rs_read_proc) (char *, char **, off_t, int, int *, void *);
};
/*
- * For the close wait times, 0 means wait forever for serial port to
- * flush its output. 65535 means don't wait at all.
- */
-#define ZILOG_CLOSING_WAIT_INF 0
-#define ZILOG_CLOSING_WAIT_NONE 65535
-
-/*
- * Definitions for ZILOG_struct (and serial_struct) flags field
- */
-#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
- on the callout port */
-#define ZILOG_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
-#define ZILOG_SAK 0x0004 /* Secure Attention Key (Orange book) */
-#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
-
-#define ZILOG_SPD_MASK 0x0030
-#define ZILOG_SPD_HI 0x0010 /* Use 76800 instead of 38400 bps */
-#define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */
-
-#define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
-#define ZILOG_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
-#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
-#define ZILOG_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
-#define ZILOG_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
-
-#define ZILOG_FLAGS 0x0FFF /* Possible legal ZILOG flags */
-#define ZILOG_USR_MASK 0x0430 /* Legal flags that non-privileged
- * users can set or reset */
-
-/* Internal flags used only by kernel/chr_drv/serial.c */
-#define ZILOG_INITIALIZED 0x80000000 /* Serial port was initialized */
-#define ZILOG_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
-#define ZILOG_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
-#define ZILOG_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
-#define ZILOG_CLOSING 0x08000000 /* Serial port is closing */
-#define ZILOG_CTS_FLOW 0x04000000 /* Do CTS flow control */
-#define ZILOG_CHECK_CD 0x02000000 /* i.e., CLOCAL */
-
-/* Software state per channel */
-
-#ifdef __KERNEL__
-/*
- * This is our internal structure for each serial port's state.
+ * XXX: Work in progress, don't worry this will go away in a few days. (ecd)
*
- * Many fields are paralleled by the structure used by the serial_struct
- * structure.
- *
- * For definitions of the flags field, see tty.h
- */
-
-struct sun_serial {
- struct sun_serial *zs_next; /* For IRQ servicing chain */
- struct sun_zschannel *zs_channel; /* Channel registers */
- unsigned char read_reg_zero;
-
- char soft_carrier; /* Use soft carrier on this channel */
- char cons_keyb; /* Channel runs the keyboard */
- char cons_mouse; /* Channel runs the mouse */
- char break_abort; /* Is serial console in, so process brk/abrt */
- char kgdb_channel; /* Kgdb is running on this channel */
- char is_cons; /* Is this our console. */
-
- char channelA; /* This is channel A. */
- char parity_mask; /* Mask out parity bits in data register. */
-
- /* We need to know the current clock divisor
- * to read the bps rate the chip has currently
- * loaded.
- */
- unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */
- int zs_baud;
-
- /* Current write register values */
- unsigned char curregs[NUM_ZSREGS];
-
- char change_needed;
-
- int magic;
- int baud_base;
- int port;
- int irq;
- int flags; /* defined in tty.h */
- int type; /* UART type */
- struct tty_struct *tty;
- int read_status_mask;
- int ignore_status_mask;
- int timeout;
- int xmit_fifo_size;
- int custom_divisor;
- int x_char; /* xon/xoff character */
- int close_delay;
- unsigned short closing_wait;
- unsigned short closing_wait2;
- unsigned long event;
- unsigned long last_active;
- int line;
- int count; /* # of fd on device */
- int blocked_open; /* # of blocked opens */
- long session; /* Session of opening process */
- long pgrp; /* pgrp of opening process */
- unsigned char *xmit_buf;
- int xmit_head;
- int xmit_tail;
- int xmit_cnt;
- struct tq_struct tqueue;
- struct tq_struct tqueue_hangup;
- struct termios normal_termios;
- struct termios callout_termios;
- struct wait_queue *open_wait;
- struct wait_queue *close_wait;
-};
-
-
-#define SERIAL_MAGIC 0x5301
-
-/*
- * The size of the serial xmit buffer is 1 page, or 4096 bytes
- */
-#define SERIAL_XMIT_SIZE 4096
-
-/*
- * Events are used to schedule things to happen at timer-interrupt
- * time, instead of at rs interrupt time.
- */
-#define RS_EVENT_WRITE_WAKEUP 0
-
-#endif /* __KERNEL__ */
-
-/* Conversion routines to/from brg time constants from/to bits
- * per second.
+ * To support multiple keyboards in one binary we have to take care
+ * about (at least) the following:
+ *
+ * int shift_state;
+ *
+ * char *func_buf;
+ * char *func_bufptr;
+ * int funcbufsize;
+ * int funcbufleft;
+ * char **func_table;
+ *
+ * XXX: keymaps need to be handled...
+ *
+ * struct kbd_struct *kbd_table;
+ * int (*kbd_init)(void);
*/
-#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
-#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
-
-/* The Zilog register set */
-
-#define FLAG 0x7e
-
-/* Write Register 0 */
-#define R0 0 /* Register selects */
-#define R1 1
-#define R2 2
-#define R3 3
-#define R4 4
-#define R5 5
-#define R6 6
-#define R7 7
-#define R8 8
-#define R9 9
-#define R10 10
-#define R11 11
-#define R12 12
-#define R13 13
-#define R14 14
-#define R15 15
-
-#define NULLCODE 0 /* Null Code */
-#define POINT_HIGH 0x8 /* Select upper half of registers */
-#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
-#define SEND_ABORT 0x18 /* HDLC Abort */
-#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
-#define RES_Tx_P 0x28 /* Reset TxINT Pending */
-#define ERR_RES 0x30 /* Error Reset */
-#define RES_H_IUS 0x38 /* Reset highest IUS */
-
-#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
-#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
-#define RES_EOM_L 0xC0 /* Reset EOM latch */
-
-/* Write Register 1 */
-
-#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
-#define TxINT_ENAB 0x2 /* Tx Int Enable */
-#define PAR_SPEC 0x4 /* Parity is special condition */
-
-#define RxINT_DISAB 0 /* Rx Int Disable */
-#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
-#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
-#define INT_ERR_Rx 0x18 /* Int on error only */
-#define RxINT_MASK 0x18
-
-#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
-#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
-#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
-
-/* Write Register #2 (Interrupt Vector) */
-
-/* Write Register 3 */
-
-#define RxENAB 0x1 /* Rx Enable */
-#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
-#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
-#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
-#define ENT_HM 0x10 /* Enter Hunt Mode */
-#define AUTO_ENAB 0x20 /* Auto Enables */
-#define Rx5 0x0 /* Rx 5 Bits/Character */
-#define Rx7 0x40 /* Rx 7 Bits/Character */
-#define Rx6 0x80 /* Rx 6 Bits/Character */
-#define Rx8 0xc0 /* Rx 8 Bits/Character */
-#define RxN_MASK 0xc0
-
-/* Write Register 4 */
-
-#define PAR_ENAB 0x1 /* Parity Enable */
-#define PAR_EVEN 0x2 /* Parity Even/Odd* */
-
-#define SYNC_ENAB 0 /* Sync Modes Enable */
-#define SB1 0x4 /* 1 stop bit/char */
-#define SB15 0x8 /* 1.5 stop bits/char */
-#define SB2 0xc /* 2 stop bits/char */
-
-#define MONSYNC 0 /* 8 Bit Sync character */
-#define BISYNC 0x10 /* 16 bit sync character */
-#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
-#define EXTSYNC 0x30 /* External Sync Mode */
-
-#define X1CLK 0x0 /* x1 clock mode */
-#define X16CLK 0x40 /* x16 clock mode */
-#define X32CLK 0x80 /* x32 clock mode */
-#define X64CLK 0xC0 /* x64 clock mode */
-
-/* Write Register 5 */
-
-#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
-#define RTS 0x2 /* RTS */
-#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
-#define TxENAB 0x8 /* Tx Enable */
-#define SND_BRK 0x10 /* Send Break */
-#define Tx5 0x0 /* Tx 5 bits (or less)/character */
-#define Tx7 0x20 /* Tx 7 bits/character */
-#define Tx6 0x40 /* Tx 6 bits/character */
-#define Tx8 0x60 /* Tx 8 bits/character */
-#define TxN_MASK 0x60
-#define DTR 0x80 /* DTR */
-
-/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
-
-/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
-
-/* Write Register 8 (transmit buffer) */
-
-/* Write Register 9 (Master interrupt control) */
-#define VIS 1 /* Vector Includes Status */
-#define NV 2 /* No Vector */
-#define DLC 4 /* Disable Lower Chain */
-#define MIE 8 /* Master Interrupt Enable */
-#define STATHI 0x10 /* Status high */
-#define NORESET 0 /* No reset on write to R9 */
-#define CHRB 0x40 /* Reset channel B */
-#define CHRA 0x80 /* Reset channel A */
-#define FHWRES 0xc0 /* Force hardware reset */
-
-/* Write Register 10 (misc control bits) */
-#define BIT6 1 /* 6 bit/8bit sync */
-#define LOOPMODE 2 /* SDLC Loop mode */
-#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
-#define MARKIDLE 8 /* Mark/flag on idle */
-#define GAOP 0x10 /* Go active on poll */
-#define NRZ 0 /* NRZ mode */
-#define NRZI 0x20 /* NRZI mode */
-#define FM1 0x40 /* FM1 (transition = 1) */
-#define FM0 0x60 /* FM0 (transition = 0) */
-#define CRCPS 0x80 /* CRC Preset I/O */
-
-/* Write Register 11 (Clock Mode control) */
-#define TRxCXT 0 /* TRxC = Xtal output */
-#define TRxCTC 1 /* TRxC = Transmit clock */
-#define TRxCBR 2 /* TRxC = BR Generator Output */
-#define TRxCDP 3 /* TRxC = DPLL output */
-#define TRxCOI 4 /* TRxC O/I */
-#define TCRTxCP 0 /* Transmit clock = RTxC pin */
-#define TCTRxCP 8 /* Transmit clock = TRxC pin */
-#define TCBR 0x10 /* Transmit clock = BR Generator output */
-#define TCDPLL 0x18 /* Transmit clock = DPLL output */
-#define RCRTxCP 0 /* Receive clock = RTxC pin */
-#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
-#define RCBR 0x40 /* Receive clock = BR Generator output */
-#define RCDPLL 0x60 /* Receive clock = DPLL output */
-#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
-
-/* Write Register 12 (lower byte of baud rate generator time constant) */
-
-/* Write Register 13 (upper byte of baud rate generator time constant) */
-
-/* Write Register 14 (Misc control bits) */
-#define BRENAB 1 /* Baud rate generator enable */
-#define BRSRC 2 /* Baud rate generator source */
-#define DTRREQ 4 /* DTR/Request function */
-#define AUTOECHO 8 /* Auto Echo */
-#define LOOPBAK 0x10 /* Local loopback */
-#define SEARCH 0x20 /* Enter search mode */
-#define RMC 0x40 /* Reset missing clock */
-#define DISDPLL 0x60 /* Disable DPLL */
-#define SSBR 0x80 /* Set DPLL source = BR generator */
-#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
-#define SFMM 0xc0 /* Set FM mode */
-#define SNRZI 0xe0 /* Set NRZI mode */
-
-/* Write Register 15 (external/status interrupt control) */
-#define ZCIE 2 /* Zero count IE */
-#define DCDIE 8 /* DCD IE */
-#define SYNCIE 0x10 /* Sync/hunt IE */
-#define CTSIE 0x20 /* CTS IE */
-#define TxUIE 0x40 /* Tx Underrun/EOM IE */
-#define BRKIE 0x80 /* Break/Abort IE */
-
-
-/* Read Register 0 */
-#define Rx_CH_AV 0x1 /* Rx Character Available */
-#define ZCOUNT 0x2 /* Zero count */
-#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
-#define DCD 0x8 /* DCD */
-#define SYNC 0x10 /* Sync/hunt */
-#define CTS 0x20 /* CTS */
-#define TxEOM 0x40 /* Tx underrun */
-#define BRK_ABRT 0x80 /* Break/Abort */
-
-/* Read Register 1 */
-#define ALL_SNT 0x1 /* All sent */
-/* Residue Data for 8 Rx bits/char programmed */
-#define RES3 0x8 /* 0/3 */
-#define RES4 0x4 /* 0/4 */
-#define RES5 0xc /* 0/5 */
-#define RES6 0x2 /* 0/6 */
-#define RES7 0xa /* 0/7 */
-#define RES8 0x6 /* 0/8 */
-#define RES18 0xe /* 1/8 */
-#define RES28 0x0 /* 2/8 */
-/* Special Rx Condition Interrupts */
-#define PAR_ERR 0x10 /* Parity error */
-#define Rx_OVR 0x20 /* Rx Overrun Error */
-#define CRC_ERR 0x40 /* CRC/Framing Error */
-#define END_FR 0x80 /* End of Frame (SDLC) */
-
-/* Read Register 2 (channel b only) - Interrupt vector */
-#define CHB_Tx_EMPTY 0x00
-#define CHB_EXT_STAT 0x02
-#define CHB_Rx_AVAIL 0x04
-#define CHB_SPECIAL 0x06
-#define CHA_Tx_EMPTY 0x08
-#define CHA_EXT_STAT 0x0a
-#define CHA_Rx_AVAIL 0x0c
-#define CHA_SPECIAL 0x0e
-#define STATUS_MASK 0x0e
-
-/* Read Register 3 (interrupt pending register) ch a only */
-#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
-#define CHBTxIP 0x2 /* Channel B Tx IP */
-#define CHBRxIP 0x4 /* Channel B Rx IP */
-#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
-#define CHATxIP 0x10 /* Channel A Tx IP */
-#define CHARxIP 0x20 /* Channel A Rx IP */
-
-/* Read Register 8 (receive data register) */
-
-/* Read Register 10 (misc status bits) */
-#define ONLOOP 2 /* On loop */
-#define LOOPSEND 0x10 /* Loop sending */
-#define CLK2MIS 0x40 /* Two clocks missing */
-#define CLK1MIS 0x80 /* One clock missing */
-
-/* Read Register 12 (lower byte of baud rate generator constant) */
-
-/* Read Register 13 (upper byte of baud rate generator constant) */
-
-/* Read Register 15 (value of WR 15) */
-
-/* Misc macros */
-#define ZS_CLEARERR(channel) do { channel->control = ERR_RES; \
- udelay(5); } while(0)
-
-#define ZS_CLEARSTAT(channel) do { channel->control = RES_EXT_INT; \
- udelay(5); } while(0)
-#define ZS_CLEARFIFO(channel) do { volatile unsigned char garbage; \
- garbage = channel->data; \
- udelay(2); \
- garbage = channel->data; \
- udelay(2); \
- garbage = channel->data; \
- udelay(2); } while(0)
+extern struct sunserial_operations rs_ops;
+extern void sunserial_setinitfunc(unsigned long *, int (*) (void));
-#endif /* !(_SPARC_SERIAL_H) */
+#endif /* !(_SPARC_SUNSERIAL_H) */
diff --git a/drivers/sbus/char/tcx.c b/drivers/sbus/char/tcx.c
index 9eaad5bc7..ec0736ff0 100644
--- a/drivers/sbus/char/tcx.c
+++ b/drivers/sbus/char/tcx.c
@@ -1,4 +1,4 @@
-/* $Id: tcx.c,v 1.18 1997/07/22 06:14:09 davem Exp $
+/* $Id: tcx.c,v 1.20 1997/08/22 15:55:14 jj Exp $
* tcx.c: SUNW,tcx 24/8bit frame buffer driver
*
* Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -259,8 +259,8 @@ tcx_reset (fbinfo_t *fb)
{
struct tcx_info *tcx = &(fb->info.tcx);
- if (fb->setcursor)
- sun_hw_hide_cursor ();
+ if (fb->setcursor && fb == &fbinfo[0])
+ sbus_hw_hide_cursor ();
/* Reset control plane to 8bit mode if necessary */
if (fb->open && fb->mmaped)
tcx_set_control_plane (fb);
@@ -292,7 +292,7 @@ __initfunc(void tcx_setup (fbinfo_t *fb, int slot, int node, u32 tcx, struct lin
fb->emulations [1] = FBTYPE_SUN3COLOR;
fb->emulations [2] = FBTYPE_MEMCOLOR;
fb->switch_from_graph = tcx_switch_from_graph;
- fb->postsetup = sun_cg_postsetup;
+ fb->postsetup = cg_postsetup;
tcxinfo = (struct tcx_info *) &fb->info.tcx;
@@ -309,7 +309,7 @@ __initfunc(void tcx_setup (fbinfo_t *fb, int slot, int node, u32 tcx, struct lin
tcxinfo->tec = sparc_alloc_io((u32)tcxinfo->tcx_offsets [TCX_TEC_OFFSET], 0,
sizeof (struct tcx_tec), "tcx_tec", fb->space, 0);
if (!fb->base){
- fb->base = (unsigned long)
+ fb->base = (uint) (unsigned long)
sparc_alloc_io((u32)tcxinfo->tcx_offsets [TCX_RAM8BIT_OFFSET],
0, fb->type.fb_size, "tcx_ram", fb->space, 0);
}
diff --git a/drivers/sbus/char/zs.c b/drivers/sbus/char/zs.c
new file mode 100644
index 000000000..3d6759a7c
--- /dev/null
+++ b/drivers/sbus/char/zs.c
@@ -0,0 +1,2786 @@
+/* $Id: zs.c,v 1.3 1997/09/04 14:57:34 jj Exp $
+ * zs.c: Zilog serial port driver for the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Fixes by Pete A. Zaitcev <zaitcev@ipmce.su>.
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/config.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/keyboard.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/kdebug.h>
+
+#include <asm/sbus.h>
+#ifdef __sparc_v9__
+#include <asm/fhc.h>
+#endif
+
+#include "sunserial.h"
+#include "zs.h"
+#include "sunkbd.h"
+#include "sunmouse.h"
+
+static int num_serial = 2; /* sun4/sun4c/sun4m - Two chips on board. */
+#define NUM_SERIAL num_serial
+#define NUM_CHANNELS (NUM_SERIAL * 2)
+
+#define KEYBOARD_LINE 0x2
+#define MOUSE_LINE 0x3
+
+struct sun_zslayout **zs_chips;
+struct sun_zschannel **zs_channels;
+struct sun_zschannel *zs_conschan;
+struct sun_zschannel *zs_mousechan;
+struct sun_zschannel *zs_kbdchan;
+struct sun_zschannel *zs_kgdbchan;
+int *zs_nodes;
+
+struct sun_serial *zs_soft;
+struct sun_serial *zs_chain; /* IRQ servicing chain */
+int zilog_irq;
+
+struct tty_struct *zs_ttys;
+/** struct tty_struct *zs_constty; **/
+
+/* Console hooks... */
+static int zs_cons_chanout = 0;
+static int zs_cons_chanin = 0;
+struct sun_serial *zs_consinfo = 0;
+
+static unsigned char kgdb_regs[16] = {
+ 0, 0, 0, /* write 0, 1, 2 */
+ (Rx8 | RxENAB), /* write 3 */
+ (X16CLK | SB1 | PAR_EVEN), /* write 4 */
+ (DTR | Tx8 | TxENAB), /* write 5 */
+ 0, 0, 0, /* write 6, 7, 8 */
+ (NV), /* write 9 */
+ (NRZ), /* write 10 */
+ (TCBR | RCBR), /* write 11 */
+ 0, 0, /* BRG time constant, write 12 + 13 */
+ (BRSRC | BRENAB), /* write 14 */
+ (DCDIE) /* write 15 */
+};
+
+static unsigned char zscons_regs[16] = {
+ 0, /* write 0 */
+ (EXT_INT_ENAB | INT_ALL_Rx), /* write 1 */
+ 0, /* write 2 */
+ (Rx8 | RxENAB), /* write 3 */
+ (X16CLK), /* write 4 */
+ (DTR | Tx8 | TxENAB), /* write 5 */
+ 0, 0, 0, /* write 6, 7, 8 */
+ (NV | MIE), /* write 9 */
+ (NRZ), /* write 10 */
+ (TCBR | RCBR), /* write 11 */
+ 0, 0, /* BRG time constant, write 12 + 13 */
+ (BRSRC | BRENAB), /* write 14 */
+ (DCDIE | CTSIE | TxUIE | BRKIE) /* write 15 */
+};
+
+#define ZS_CLOCK 4915200 /* Zilog input clock rate */
+
+DECLARE_TASK_QUEUE(tq_serial);
+
+struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Debugging... DEBUG_INTR is bad to use when one of the zs
+ * lines is your console ;(
+ */
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+
+#define RS_STROBE_TIME 10
+#define RS_ISR_PASS_LIMIT 256
+
+#define _INLINE_ inline
+
+int zs_init(void);
+void zs_cons_hook(int, int, int);
+void zs_kgdb_hook(int);
+
+static void change_speed(struct sun_serial *info);
+
+static struct tty_struct **serial_table;
+static struct termios **serial_termios;
+static struct termios **serial_termios_locked;
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write. We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char tmp_buf[4096]; /* This is cheating */
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int serial_paranoia_check(struct sun_serial *info,
+ dev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+ static const char *badmagic =
+ "Warning: bad magic number for serial struct (%d, %d) in %s\n";
+ static const char *badinfo =
+ "Warning: null sun_serial for (%d, %d) in %s\n";
+
+ if (!info) {
+ printk(badinfo, MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+ if (info->magic != SERIAL_MAGIC) {
+ printk(badmagic, MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 76800, 0 };
+
+/*
+ * Reading and writing Zilog8530 registers. The delays are to make this
+ * driver work on the Sun4 which needs a settling delay after each chip
+ * register access, other machines handle this in hardware via auxiliary
+ * flip-flops which implement the settle time we do in software.
+ */
+static inline unsigned char read_zsreg(struct sun_zschannel *channel,
+ unsigned char reg)
+{
+ unsigned char retval;
+
+ channel->control = reg;
+ udelay(5);
+ retval = channel->control;
+ udelay(5);
+ return retval;
+}
+
+static inline void write_zsreg(struct sun_zschannel *channel,
+ unsigned char reg, unsigned char value)
+{
+ channel->control = reg;
+ udelay(5);
+ channel->control = value;
+ udelay(5);
+}
+
+static inline void load_zsregs(struct sun_serial *info, unsigned char *regs)
+{
+ unsigned long flags;
+ struct sun_zschannel *channel = info->zs_channel;
+ unsigned char stat;
+ int i;
+
+ for (i = 0; i < 1000; i++) {
+ stat = read_zsreg(channel, R1);
+ if (stat & ALL_SNT)
+ break;
+ udelay(100);
+ }
+ write_zsreg(channel, R3, 0);
+ ZS_CLEARSTAT(channel);
+ ZS_CLEARERR(channel);
+ ZS_CLEARFIFO(channel);
+
+ /* Load 'em up */
+ save_flags(flags); cli();
+ if (info->channelA)
+ write_zsreg(channel, R9, CHRA);
+ else
+ write_zsreg(channel, R9, CHRB);
+ udelay(20); /* wait for some old sun4's */
+ write_zsreg(channel, R4, regs[R4]);
+ write_zsreg(channel, R3, regs[R3] & ~RxENAB);
+ write_zsreg(channel, R5, regs[R5] & ~TxENAB);
+ write_zsreg(channel, R9, regs[R9] & ~MIE);
+ write_zsreg(channel, R10, regs[R10]);
+ write_zsreg(channel, R11, regs[R11]);
+ write_zsreg(channel, R12, regs[R12]);
+ write_zsreg(channel, R13, regs[R13]);
+ write_zsreg(channel, R14, regs[R14] & ~BRENAB);
+ write_zsreg(channel, R14, regs[R14]);
+ write_zsreg(channel, R14, (regs[R14] & ~SNRZI) | BRENAB);
+ write_zsreg(channel, R3, regs[R3]);
+ write_zsreg(channel, R5, regs[R5]);
+ write_zsreg(channel, R15, regs[R15]);
+ write_zsreg(channel, R0, RES_EXT_INT);
+ write_zsreg(channel, R0, ERR_RES);
+ write_zsreg(channel, R1, regs[R1]);
+ write_zsreg(channel, R9, regs[R9]);
+ restore_flags(flags);
+}
+
+static inline void zs_put_char(struct sun_zschannel *channel, char ch)
+{
+ int loops = 0;
+
+ while((channel->control & Tx_BUF_EMP) == 0 && loops < 10000) {
+ loops++;
+ udelay(5);
+ }
+ channel->data = ch;
+ udelay(5);
+}
+
+/* Sets or clears DTR/RTS on the requested line */
+static inline void zs_rtsdtr(struct sun_serial *ss, int set)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ if(set) {
+ ss->curregs[5] |= (RTS | DTR);
+ write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
+ } else {
+ ss->curregs[5] &= ~(RTS | DTR);
+ write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
+ }
+ restore_flags(flags);
+ return;
+}
+
+static inline void kgdb_chaninit(struct sun_serial *ss, int intson, int bps)
+{
+ int brg;
+
+ if(intson) {
+ kgdb_regs[R1] = INT_ALL_Rx;
+ kgdb_regs[R9] |= MIE;
+ } else {
+ kgdb_regs[R1] = 0;
+ kgdb_regs[R9] &= ~MIE;
+ }
+ brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
+ kgdb_regs[R12] = (brg & 255);
+ kgdb_regs[R13] = ((brg >> 8) & 255);
+ load_zsregs(ss, kgdb_regs);
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_stop() and zs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void zs_stop(struct tty_struct *tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "zs_stop"))
+ return;
+
+ save_flags(flags); cli();
+ if (info->curregs[5] & TxENAB) {
+ info->curregs[5] &= ~TxENAB;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ }
+ restore_flags(flags);
+}
+
+static void zs_start(struct tty_struct *tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "zs_start"))
+ return;
+
+ save_flags(flags); cli();
+ if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {
+ info->curregs[5] |= TxENAB;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ }
+ restore_flags(flags);
+}
+
+/* Drop into either the boot monitor or kadb upon receiving a break
+ * from keyboard/console input.
+ */
+void batten_down_hatches(void)
+{
+ /* If we are doing kadb, we call the debugger
+ * else we just drop into the boot monitor.
+ * Note that we must flush the user windows
+ * first before giving up control.
+ */
+ printk("\n");
+ flush_user_windows();
+#ifndef __sparc_v9__
+ if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) &&
+ (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR))
+ sp_enter_debugger();
+ else
+#endif
+ prom_cmdline();
+
+ /* XXX We want to notify the keyboard driver that all
+ * XXX keys are in the up state or else weird things
+ * XXX happen...
+ */
+
+ return;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines. All of the following
+ * subroutines are declared as inline and are folded into
+ * zs_interrupt(). They were separated out for readability's sake.
+ *
+ * Note: zs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off. People who may want to modify
+ * zs_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.
+ *
+ * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void zs_sched_event(struct sun_serial *info,
+ int event)
+{
+ info->event |= 1 << event;
+ queue_task(&info->tqueue, &tq_serial);
+ mark_bh(SERIAL_BH);
+}
+
+#ifndef __sparc_v9__
+extern void breakpoint(void); /* For the KGDB frame character */
+#endif
+
+static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs)
+{
+ struct tty_struct *tty = info->tty;
+ unsigned char ch, stat;
+
+ do {
+ ch = (info->zs_channel->data) & info->parity_mask;
+ udelay(5);
+
+ /* If this is the console keyboard, we need to handle
+ * L1-A's here.
+ */
+ if(info->cons_keyb) {
+ if(ch == SUNKBD_RESET) {
+ l1a_state.kbd_id = 1;
+ l1a_state.l1_down = 0;
+ } else if(l1a_state.kbd_id) {
+ l1a_state.kbd_id = 0;
+ } else if(ch == SUNKBD_L1) {
+ l1a_state.l1_down = 1;
+ } else if(ch == (SUNKBD_L1|SUNKBD_UP)) {
+ l1a_state.l1_down = 0;
+ } else if(ch == SUNKBD_A && l1a_state.l1_down) {
+ /* whee... */
+ batten_down_hatches();
+ /* Continue execution... */
+ l1a_state.l1_down = 0;
+ l1a_state.kbd_id = 0;
+ return;
+ }
+ sunkbd_inchar(ch, regs);
+ return;
+ }
+ if(info->cons_mouse) {
+ sun_mouse_inbyte(ch);
+ return;
+ }
+ if(info->is_cons) {
+ if(ch==0) {
+ /* whee, break received */
+ batten_down_hatches();
+ /* Continue execution... */
+ return;
+#if 0
+ } else if (ch == 1) {
+ show_state();
+ return;
+ } else if (ch == 2) {
+ show_buffers();
+ return;
+#endif
+ }
+ /* It is a 'keyboard interrupt' ;-) */
+ wake_up(&keypress_wait);
+ }
+#ifndef __sparc_v9__
+ /* Look for kgdb 'stop' character, consult the gdb
+ * documentation for remote target debugging and
+ * arch/sparc/kernel/sparc-stub.c to see how all this works.
+ */
+ if((info->kgdb_channel) && (ch =='\003')) {
+ breakpoint();
+ return;
+ }
+#endif
+ if(!tty)
+ return;
+
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ break;
+
+ tty->flip.count++;
+ *tty->flip.flag_buf_ptr++ = 0;
+ *tty->flip.char_buf_ptr++ = ch;
+
+ /* Check if we have another character... */
+ stat = info->zs_channel->control;
+ udelay(5);
+ if (!(stat & Rx_CH_AV))
+ break;
+
+ /* ... and see if it is clean. */
+ stat = read_zsreg(info->zs_channel, R1);
+ } while (!(stat & (PAR_ERR | Rx_OVR | CRC_ERR)));
+
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static _INLINE_ void transmit_chars(struct sun_serial *info)
+{
+ struct tty_struct *tty = info->tty;
+
+ if (info->x_char) {
+ /* Send next char */
+ zs_put_char(info->zs_channel, info->x_char);
+ info->x_char = 0;
+ return;
+ }
+
+ if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
+ /* That's peculiar... */
+ info->zs_channel->control = RES_Tx_P;
+ udelay(5);
+ return;
+ }
+
+ /* Send char */
+ zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
+ info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt--;
+
+ if (info->xmit_cnt < WAKEUP_CHARS)
+ zs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+ if(info->xmit_cnt <= 0) {
+ info->zs_channel->control = RES_Tx_P;
+ udelay(5);
+ }
+}
+
+static _INLINE_ void status_handle(struct sun_serial *info)
+{
+ unsigned char status;
+
+ /* Get status from Read Register 0 */
+ status = info->zs_channel->control;
+ udelay(5);
+ /* Clear status condition... */
+ info->zs_channel->control = RES_EXT_INT;
+ udelay(5);
+
+#if 0
+ if(status & DCD) {
+ if((info->tty->termios->c_cflag & CRTSCTS) &&
+ ((info->curregs[3] & AUTO_ENAB)==0)) {
+ info->curregs[3] |= AUTO_ENAB;
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ }
+ } else {
+ if((info->curregs[3] & AUTO_ENAB)) {
+ info->curregs[3] &= ~AUTO_ENAB;
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ }
+ }
+#endif
+ /* Whee, if this is console input and this is a
+ * 'break asserted' status change interrupt, call
+ * the boot prom.
+ */
+ if((status & BRK_ABRT) && info->break_abort)
+ batten_down_hatches();
+
+ /* XXX Whee, put in a buffer somewhere, the status information
+ * XXX whee whee whee... Where does the information go...
+ */
+ return;
+}
+
+static _INLINE_ void special_receive(struct sun_serial *info)
+{
+ struct tty_struct *tty = info->tty;
+ unsigned char ch, stat;
+
+ stat = read_zsreg(info->zs_channel, R1);
+ if (stat & (PAR_ERR | Rx_OVR | CRC_ERR)) {
+ ch = info->zs_channel->data;
+ udelay(5);
+ }
+
+ if (!tty)
+ goto clear;
+
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ goto done;
+
+ tty->flip.count++;
+ if(stat & PAR_ERR)
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+ else if(stat & Rx_OVR)
+ *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+ else if(stat & CRC_ERR)
+ *tty->flip.flag_buf_ptr++ = TTY_FRAME;
+
+done:
+ queue_task(&tty->flip.tqueue, &tq_timer);
+clear:
+ info->zs_channel->control = ERR_RES;
+ udelay(5);
+}
+
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+void zs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct sun_serial *info;
+ unsigned char zs_intreg;
+ int i;
+
+ info = (struct sun_serial *)dev_id;
+ for (i = 0; i < NUM_SERIAL; i++) {
+ zs_intreg = read_zsreg(info->zs_next->zs_channel, 2);
+ zs_intreg &= STATUS_MASK;
+
+ /* NOTE: The read register 2, which holds the irq status,
+ * does so for both channels on each chip. Although
+ * the status value itself must be read from the B
+ * channel and is only valid when read from channel B.
+ * When read from channel A, read register 2 contains
+ * the value written to write register 2.
+ */
+
+ /* Channel A -- /dev/ttya or /dev/kbd, could be the console */
+ if (zs_intreg == CHA_Rx_AVAIL) {
+ receive_chars(info, regs);
+ return;
+ }
+ if(zs_intreg == CHA_Tx_EMPTY) {
+ transmit_chars(info);
+ return;
+ }
+ if (zs_intreg == CHA_EXT_STAT) {
+ status_handle(info);
+ return;
+ }
+ if (zs_intreg == CHA_SPECIAL) {
+ special_receive(info);
+ return;
+ }
+
+ /* Channel B -- /dev/ttyb or /dev/mouse, could be the console */
+ if(zs_intreg == CHB_Rx_AVAIL) {
+ receive_chars(info->zs_next, regs);
+ return;
+ }
+ if(zs_intreg == CHB_Tx_EMPTY) {
+ transmit_chars(info->zs_next);
+ return;
+ }
+ if (zs_intreg == CHB_EXT_STAT) {
+ status_handle(info->zs_next);
+ return;
+ }
+
+ /* NOTE: The default value for the IRQ status in read register
+ * 2 in channel B is CHB_SPECIAL, so we need to look at
+ * read register 3 in channel A to check if this is a
+ * real interrupt, or just the default value.
+ * Yes... broken hardware...
+ */
+
+ zs_intreg = read_zsreg(info->zs_channel, 3);
+ if (zs_intreg & CHBRxIP) {
+ special_receive(info->zs_next);
+ return;
+ }
+ info = info->zs_next->zs_next;
+ }
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * zs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using zs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+ run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+ struct sun_serial *info = (struct sun_serial *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+}
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred. The path of
+ * hangup processing is:
+ *
+ * serial interrupt routine -> (scheduler tqueue) ->
+ * do_serial_hangup() -> tty->hangup() -> zs_hangup()
+ *
+ */
+static void do_serial_hangup(void *private_)
+{
+ struct sun_serial *info = (struct sun_serial *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("do_serial_hangup<%p: tty-%d\n",
+ __builtin_return_address(0), info->line);
+#endif
+
+ tty_hangup(tty);
+}
+
+
+/*
+ * This subroutine is called when the RS_TIMER goes off. It is used
+ * by the serial driver to handle ports that do not have an interrupt
+ * (irq=0). This doesn't work at all for 16450's, as a sun has a Z8530.
+ */
+
+static void zs_timer(void)
+{
+ printk("zs_timer called\n");
+ prom_halt();
+ return;
+}
+
+static int startup(struct sun_serial * info)
+{
+ unsigned long flags;
+
+ if (info->flags & ZILOG_INITIALIZED)
+ return 0;
+
+ if (!info->xmit_buf) {
+ info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+ if (!info->xmit_buf)
+ return -ENOMEM;
+ }
+
+ save_flags(flags); cli();
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Starting up tty-%d (irq %d)...\n", info->line, info->irq);
+#endif
+
+ /*
+ * Clear the FIFO buffers and disable them
+ * (they will be reenabled in change_speed())
+ */
+ ZS_CLEARFIFO(info->zs_channel);
+ info->xmit_fifo_size = 1;
+
+ /*
+ * Clear the interrupt registers.
+ */
+ info->zs_channel->control = ERR_RES;
+ udelay(5);
+ info->zs_channel->control = RES_H_IUS;
+ udelay(5);
+
+ /*
+ * Now, initialize the Zilog
+ */
+ zs_rtsdtr(info, 1);
+
+ /*
+ * Finally, enable sequencing and interrupts
+ */
+ info->curregs[1] |= (info->curregs[1] & ~(RxINT_MASK)) |
+ (EXT_INT_ENAB | INT_ALL_Rx);
+ info->curregs[3] |= (RxENAB | Rx8);
+ /* We enable Tx interrupts as needed. */
+ info->curregs[5] |= (TxENAB | Tx8);
+ info->curregs[9] |= (NV | MIE);
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ write_zsreg(info->zs_channel, 9, info->curregs[9]);
+
+ /*
+ * And clear the interrupt registers again for luck.
+ */
+ info->zs_channel->control = ERR_RES;
+ udelay(5);
+ info->zs_channel->control = RES_H_IUS;
+ udelay(5);
+
+ if (info->tty)
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+ /*
+ * Set up serial timers...
+ */
+#if 0 /* Works well and stops the machine. */
+ timer_table[RS_TIMER].expires = jiffies + 2;
+ timer_active |= 1 << RS_TIMER;
+#endif
+
+ /*
+ * and set the speed of the serial port
+ */
+ change_speed(info);
+
+ info->flags |= ZILOG_INITIALIZED;
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * 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 sun_serial * info)
+{
+ unsigned long flags;
+
+ if (!(info->flags & ZILOG_INITIALIZED))
+ return;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Shutting down serial port %d (irq %d)....", info->line,
+ info->irq);
+#endif
+
+ save_flags(flags); cli(); /* Disable interrupts */
+
+ if (info->xmit_buf) {
+ free_page((unsigned long) info->xmit_buf);
+ info->xmit_buf = 0;
+ }
+
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ZILOG_INITIALIZED;
+ restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct sun_serial *info)
+{
+ unsigned short port;
+ unsigned cflag;
+ int quot = 0;
+ int i;
+ int brg;
+
+ if (!info->tty || !info->tty->termios)
+ return;
+ cflag = info->tty->termios->c_cflag;
+ if (!(port = info->port))
+ return;
+ i = cflag & CBAUD;
+ if (cflag & CBAUDEX) {
+ i &= ~CBAUDEX;
+ if (i != 5)
+ info->tty->termios->c_cflag &= ~CBAUDEX;
+ else
+ i = 16;
+ }
+ if (i == 15) {
+ if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_HI)
+ i += 1;
+ if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_CUST)
+ quot = info->custom_divisor;
+ }
+ if (quot) {
+ info->zs_baud = info->baud_base / quot;
+ info->clk_divisor = 16;
+
+ info->curregs[4] = X16CLK;
+ info->curregs[11] = TCBR | RCBR;
+ brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
+ info->curregs[12] = (brg & 255);
+ info->curregs[13] = ((brg >> 8) & 255);
+ info->curregs[14] = BRSRC | BRENAB;
+ zs_rtsdtr(info, 1);
+ } else if (baud_table[i]) {
+ info->zs_baud = baud_table[i];
+ info->clk_divisor = 16;
+
+ info->curregs[4] = X16CLK;
+ info->curregs[11] = TCBR | RCBR;
+ brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
+ info->curregs[12] = (brg & 255);
+ info->curregs[13] = ((brg >> 8) & 255);
+ info->curregs[14] = BRSRC | BRENAB;
+ zs_rtsdtr(info, 1);
+ } else {
+ zs_rtsdtr(info, 0);
+ return;
+ }
+
+ /* byte size and parity */
+ switch (cflag & CSIZE) {
+ case CS5:
+ info->curregs[3] &= ~(RxN_MASK);
+ info->curregs[3] |= Rx5;
+ info->curregs[5] &= ~(TxN_MASK);
+ info->curregs[5] |= Tx5;
+ info->parity_mask = 0x1f;
+ break;
+ case CS6:
+ info->curregs[3] &= ~(RxN_MASK);
+ info->curregs[3] |= Rx6;
+ info->curregs[5] &= ~(TxN_MASK);
+ info->curregs[5] |= Tx6;
+ info->parity_mask = 0x3f;
+ break;
+ case CS7:
+ info->curregs[3] &= ~(RxN_MASK);
+ info->curregs[3] |= Rx7;
+ info->curregs[5] &= ~(TxN_MASK);
+ info->curregs[5] |= Tx7;
+ info->parity_mask = 0x7f;
+ break;
+ case CS8:
+ default: /* defaults to 8 bits */
+ info->curregs[3] &= ~(RxN_MASK);
+ info->curregs[3] |= Rx8;
+ info->curregs[5] &= ~(TxN_MASK);
+ info->curregs[5] |= Tx8;
+ info->parity_mask = 0xff;
+ break;
+ }
+ info->curregs[4] &= ~(0x0c);
+ if (cflag & CSTOPB) {
+ info->curregs[4] |= SB2;
+ } else {
+ info->curregs[4] |= SB1;
+ }
+ if (cflag & PARENB) {
+ info->curregs[4] |= PAR_ENAB;
+ } else {
+ info->curregs[4] &= ~PAR_ENAB;
+ }
+ if (!(cflag & PARODD)) {
+ info->curregs[4] |= PAR_EVEN;
+ } else {
+ info->curregs[4] &= ~PAR_EVEN;
+ }
+
+ /* Load up the new values */
+ load_zsregs(info, info->curregs);
+
+ return;
+}
+
+/* This is for mouse/keyboard output.
+ * XXX mouse output??? can we send it commands??? XXX
+ */
+static void kbd_put_char(unsigned char ch)
+{
+ struct sun_zschannel *chan = zs_kbdchan;
+ unsigned long flags;
+
+ if(!chan)
+ return;
+
+ save_flags(flags); cli();
+ zs_put_char(chan, ch);
+ restore_flags(flags);
+}
+
+void mouse_put_char(char ch)
+{
+ struct sun_zschannel *chan = zs_mousechan;
+ unsigned long flags;
+
+ if(!chan)
+ return;
+
+ save_flags(flags); cli();
+ zs_put_char(chan, ch);
+ restore_flags(flags);
+}
+
+
+/* This is for console output over ttya/ttyb */
+static void zs_cons_put_char(char ch)
+{
+ struct sun_zschannel *chan = zs_conschan;
+ unsigned long flags;
+
+ if(!chan)
+ return;
+
+ save_flags(flags); cli();
+ zs_put_char(chan, ch);
+ restore_flags(flags);
+}
+
+/* These are for receiving and sending characters under the kgdb
+ * source level kernel debugger.
+ */
+void putDebugChar(char kgdb_char)
+{
+ struct sun_zschannel *chan = zs_kgdbchan;
+
+ while((chan->control & Tx_BUF_EMP)==0)
+ udelay(5);
+ chan->data = kgdb_char;
+}
+
+char getDebugChar(void)
+{
+ struct sun_zschannel *chan = zs_kgdbchan;
+
+ while((chan->control & Rx_CH_AV)==0)
+ barrier();
+ return chan->data;
+}
+
+/*
+ * Fair output driver allows a process to speak.
+ */
+static void zs_fair_output(void)
+{
+ int left; /* Output no more than that */
+ unsigned long flags;
+ struct sun_serial *info = zs_consinfo;
+ char c;
+
+ if (info == 0) return;
+ if (info->xmit_buf == 0) return;
+
+ save_flags(flags); cli();
+ left = info->xmit_cnt;
+ while (left != 0) {
+ c = info->xmit_buf[info->xmit_tail];
+ info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt--;
+ restore_flags(flags);
+
+ zs_cons_put_char(c);
+
+ cli();
+ left = MIN(info->xmit_cnt, left-1);
+ }
+
+ /* Last character is being transmitted now (hopefully). */
+ zs_conschan->control = RES_Tx_P;
+ udelay(5);
+
+ restore_flags(flags);
+ return;
+}
+
+/*
+ * zs_console_print is registered for printk.
+ */
+static void zs_console_print(const char *s, unsigned count)
+{
+ int i;
+
+ for (i = 0; i < count; i++, s++) {
+ if(*s == '\n')
+ zs_cons_put_char('\r');
+ zs_cons_put_char(*s);
+ }
+
+ /* Comment this if you want to have a strict interrupt-driven output */
+ zs_fair_output();
+}
+
+static void zs_console_wait_key(void)
+{
+ sleep_on(&keypress_wait);
+}
+
+static int zs_console_device(void)
+{
+ extern int serial_console;
+
+ return MKDEV(TTYAUX_MAJOR, 64 + serial_console - 1);
+}
+
+static void zs_flush_chars(struct tty_struct *tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "zs_flush_chars"))
+ return;
+
+ if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+ !info->xmit_buf)
+ return;
+
+ /* Enable transmitter */
+ save_flags(flags); cli();
+ info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ info->curregs[5] |= TxENAB;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+
+ /*
+ * Send a first (bootstrapping) character. A best solution is
+ * to call transmit_chars() here which handles output in a
+ * generic way. Current transmit_chars() not only transmits,
+ * but resets interrupts also what we do not desire here.
+ * XXX Discuss with David.
+ */
+ zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
+ info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt--;
+
+ restore_flags(flags);
+}
+
+static int zs_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ int c, total = 0;
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "zs_write"))
+ return 0;
+
+ if (!info || !info->xmit_buf)
+ return 0;
+
+ save_flags(flags);
+ while (1) {
+ cli();
+ c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ if (c <= 0)
+ break;
+
+ if (from_user) {
+ down(&tmp_buf_sem);
+ copy_from_user(tmp_buf, buf, c);
+ c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+ up(&tmp_buf_sem);
+ } else
+ memcpy(info->xmit_buf + info->xmit_head, buf, c);
+ info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt += c;
+ restore_flags(flags);
+ buf += c;
+ count -= c;
+ total += c;
+ }
+
+ if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
+ /* Enable transmitter */
+ info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ info->curregs[5] |= TxENAB;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ }
+#if 1
+ if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
+ zs_put_char(info->zs_channel,
+ info->xmit_buf[info->xmit_tail++]);
+ info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt--;
+ }
+#endif
+ restore_flags(flags);
+ return total;
+}
+
+static int zs_write_room(struct tty_struct *tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+ int ret;
+
+ if (serial_paranoia_check(info, tty->device, "zs_write_room"))
+ return 0;
+ ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+ if (ret < 0)
+ ret = 0;
+ return ret;
+}
+
+static int zs_chars_in_buffer(struct tty_struct *tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "zs_chars_in_buffer"))
+ return 0;
+ return info->xmit_cnt;
+}
+
+static void zs_flush_buffer(struct tty_struct *tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "zs_flush_buffer"))
+ return;
+ cli();
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ sti();
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void zs_throttle(struct tty_struct * tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("throttle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "zs_throttle"))
+ return;
+
+ if (I_IXOFF(tty))
+ info->x_char = STOP_CHAR(tty);
+
+ /* Turn off RTS line */
+ cli();
+ info->curregs[5] &= ~RTS;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ sti();
+}
+
+static void zs_unthrottle(struct tty_struct * tty)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "zs_unthrottle"))
+ return;
+
+ if (I_IXOFF(tty)) {
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ info->x_char = START_CHAR(tty);
+ }
+
+ /* Assert RTS line */
+ cli();
+ info->curregs[5] |= RTS;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ sti();
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct sun_serial * info,
+ struct serial_struct * retinfo)
+{
+ struct serial_struct tmp;
+
+ if (!retinfo)
+ return -EFAULT;
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.type = info->type;
+ tmp.line = info->line;
+ tmp.port = info->port;
+ tmp.irq = info->irq;
+ tmp.flags = info->flags;
+ tmp.baud_base = info->baud_base;
+ tmp.close_delay = info->close_delay;
+ tmp.closing_wait = info->closing_wait;
+ tmp.custom_divisor = info->custom_divisor;
+ copy_to_user_ret(retinfo,&tmp,sizeof(*retinfo), -EFAULT);
+ return 0;
+}
+
+static int set_serial_info(struct sun_serial * info,
+ struct serial_struct * new_info)
+{
+ struct serial_struct new_serial;
+ struct sun_serial old_info;
+ int retval = 0;
+
+ if (!new_info || copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+ return -EFAULT;
+ old_info = *info;
+
+ if (!suser()) {
+ if ((new_serial.baud_base != info->baud_base) ||
+ (new_serial.type != info->type) ||
+ (new_serial.close_delay != info->close_delay) ||
+ ((new_serial.flags & ~ZILOG_USR_MASK) !=
+ (info->flags & ~ZILOG_USR_MASK)))
+ return -EPERM;
+ info->flags = ((info->flags & ~ZILOG_USR_MASK) |
+ (new_serial.flags & ZILOG_USR_MASK));
+ info->custom_divisor = new_serial.custom_divisor;
+ goto check_and_exit;
+ }
+
+ if (info->count > 1)
+ return -EBUSY;
+
+ /*
+ * OK, past this point, all the error checking has been done.
+ * At this point, we start making changes.....
+ */
+
+ info->baud_base = new_serial.baud_base;
+ info->flags = ((info->flags & ~ZILOG_FLAGS) |
+ (new_serial.flags & ZILOG_FLAGS));
+ info->custom_divisor = new_serial.custom_divisor;
+ info->type = new_serial.type;
+ info->close_delay = new_serial.close_delay;
+ info->closing_wait = new_serial.closing_wait;
+
+check_and_exit:
+ retval = startup(info);
+ return retval;
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows an RS485 driver to be written in user space.
+ */
+static int get_lsr_info(struct sun_serial * info, unsigned int *value)
+{
+ unsigned char status;
+
+ cli();
+ status = info->zs_channel->control;
+ sti();
+ put_user_ret(status,value, -EFAULT);
+ return 0;
+}
+
+static int get_modem_info(struct sun_serial * info, unsigned int *value)
+{
+ unsigned char status;
+ unsigned int result;
+
+ cli();
+ status = info->zs_channel->control;
+ sti();
+ result = ((info->curregs[5] & RTS) ? TIOCM_RTS : 0)
+ | ((info->curregs[5] & DTR) ? TIOCM_DTR : 0)
+ | ((status & DCD) ? TIOCM_CAR : 0)
+ | ((status & SYNC) ? TIOCM_DSR : 0)
+ | ((status & CTS) ? TIOCM_CTS : 0);
+ put_user_ret(result, value, -EFAULT);
+ return 0;
+}
+
+static int set_modem_info(struct sun_serial * info, unsigned int cmd,
+ unsigned int *value)
+{
+ unsigned int arg;
+
+ get_user_ret(arg, value, -EFAULT);
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS)
+ info->curregs[5] |= RTS;
+ if (arg & TIOCM_DTR)
+ info->curregs[5] |= DTR;
+ break;
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS)
+ info->curregs[5] &= ~RTS;
+ if (arg & TIOCM_DTR)
+ info->curregs[5] &= ~DTR;
+ break;
+ case TIOCMSET:
+ info->curregs[5] = ((info->curregs[5] & ~(RTS | DTR))
+ | ((arg & TIOCM_RTS) ? RTS : 0)
+ | ((arg & TIOCM_DTR) ? DTR : 0));
+ break;
+ default:
+ return -EINVAL;
+ }
+ cli();
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ sti();
+ return 0;
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break( struct sun_serial * info, int duration)
+{
+ if (!info->port)
+ return;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + duration;
+ cli();
+ write_zsreg(info->zs_channel, 5, (info->curregs[5] | SND_BRK));
+ schedule();
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ sti();
+}
+
+static int zs_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct sun_serial * info = (struct sun_serial *)tty->driver_data;
+ int retval;
+
+ if (serial_paranoia_check(info, tty->device, "zs_ioctl"))
+ return -ENODEV;
+
+ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
+ (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ }
+
+ switch (cmd) {
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (!arg)
+ send_break(info, HZ/4); /* 1/4 second */
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ send_break(info, arg ? arg*(HZ/10) : HZ/4);
+ return 0;
+ case TIOCGSOFTCAR:
+ put_user_ret(C_CLOCAL(tty) ? 1 : 0,
+ (unsigned long *) arg, -EFAULT);
+ return 0;
+ case TIOCSSOFTCAR:
+ get_user_ret(arg, (unsigned long *) arg, -EFAULT);
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ return 0;
+ case TIOCMGET:
+ return get_modem_info(info, (unsigned int *) arg);
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return set_modem_info(info, cmd, (unsigned int *) arg);
+ case TIOCGSERIAL:
+ return get_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ return set_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSERGETLSR: /* Get line status register */
+ return get_lsr_info(info, (unsigned int *) arg);
+
+ case TIOCSERGSTRUCT:
+ copy_to_user_ret((struct sun_serial *) arg,
+ info, sizeof(struct sun_serial), -EFAULT);
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static void zs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+ struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+
+ if (tty->termios->c_cflag == old_termios->c_cflag)
+ return;
+
+ change_speed(info);
+
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ zs_start(tty);
+ }
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_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
+ * ZILOG structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void zs_close(struct tty_struct *tty, struct file * filp)
+{
+ struct sun_serial * info = (struct sun_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (!info || serial_paranoia_check(info, tty->device, "zs_close"))
+ return;
+
+ save_flags(flags); cli();
+
+ if (tty_hung_up_p(filp)) {
+ restore_flags(flags);
+ return;
+ }
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("zs_close tty-%d, count = %d\n", info->line, info->count);
+#endif
+ if ((tty->count == 1) && (info->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. Info->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk("zs_close: bad serial port count; tty->count is 1, "
+ "info->count is %d\n", info->count);
+ info->count = 1;
+ }
+ if (--info->count < 0) {
+ printk("zs_close: bad serial port count for ttys%d: %d\n",
+ info->line, info->count);
+ info->count = 0;
+ }
+ if (info->count) {
+ restore_flags(flags);
+ return;
+ }
+ info->flags |= ZILOG_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ZILOG_NORMAL_ACTIVE)
+ info->normal_termios = *tty->termios;
+ if (info->flags & ZILOG_CALLOUT_ACTIVE)
+ info->callout_termios = *tty->termios;
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, info->closing_wait);
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receive line status interrupts, and tell the
+ * interrupt driver to stop checking the data ready bit in the
+ * line status register.
+ */
+ /** if (!info->iscons) ... **/
+ info->curregs[3] &= ~RxENAB;
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ info->curregs[1] &= ~(RxINT_MASK);
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ ZS_CLEARFIFO(info->zs_channel);
+
+ shutdown(info);
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ tty->closing = 0;
+ info->event = 0;
+ info->tty = 0;
+ if (tty->ldisc.num != ldiscs[N_TTY].num) {
+ if (tty->ldisc.close)
+ (tty->ldisc.close)(tty);
+ tty->ldisc = ldiscs[N_TTY];
+ tty->termios->c_line = N_TTY;
+ if (tty->ldisc.open)
+ (tty->ldisc.open)(tty);
+ }
+ if (info->blocked_open) {
+ if (info->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + info->close_delay;
+ schedule();
+ }
+ wake_up_interruptible(&info->open_wait);
+ }
+ info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
+ ZILOG_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+#ifdef SERIAL_DEBUG_OPEN
+ printk("zs_close tty-%d exiting, count = %d\n", info->line, info->count);
+#endif
+ restore_flags(flags);
+}
+
+/*
+ * zs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void zs_hangup(struct tty_struct *tty)
+{
+ struct sun_serial * info = (struct sun_serial *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "zs_hangup"))
+ return;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("zs_hangup<%p: tty-%d, count = %d bye\n",
+ __builtin_return_address(0), info->line, info->count);
+#endif
+
+ zs_flush_buffer(tty);
+ shutdown(info);
+ info->event = 0;
+ info->count = 0;
+ info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE);
+ info->tty = 0;
+ wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct sun_serial *info)
+{
+ struct wait_queue wait = { current, NULL };
+ int retval;
+ int do_clocal = 0;
+ unsigned char r0;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (info->flags & ZILOG_CLOSING) {
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ZILOG_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+ if (info->flags & ZILOG_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
+ (info->flags & ZILOG_SESSION_LOCKOUT) &&
+ (info->session != current->session))
+ return -EBUSY;
+ if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
+ (info->flags & ZILOG_PGRP_LOCKOUT) &&
+ (info->pgrp != current->pgrp))
+ return -EBUSY;
+ info->flags |= ZILOG_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (info->flags & ZILOG_CALLOUT_ACTIVE)
+ return -EBUSY;
+ info->flags |= ZILOG_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (info->flags & ZILOG_CALLOUT_ACTIVE) {
+ if (info->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, info->count is dropped by one, so that
+ * zs_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready before block: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+ cli();
+ if(!tty_hung_up_p(filp))
+ info->count--;
+ sti();
+ info->blocked_open++;
+ while (1) {
+ cli();
+ if (!(info->flags & ZILOG_CALLOUT_ACTIVE))
+ zs_rtsdtr(info, 1);
+ sti();
+ current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp) ||
+ !(info->flags & ZILOG_INITIALIZED)) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready hup-ed: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ZILOG_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+
+ cli();
+ r0 = read_zsreg(info->zs_channel, R0);
+ sti();
+ if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
+ !(info->flags & ZILOG_CLOSING) &&
+ (do_clocal || (DCD & r0)))
+ break;
+ if (current->signal & ~current->blocked) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready blocking: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+ if (!tty_hung_up_p(filp))
+ info->count++;
+ info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready after blocking: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+ if (retval)
+ return retval;
+ info->flags |= ZILOG_NORMAL_ACTIVE;
+ return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened. It
+ * enables interrupts for a serial port, linking in its ZILOG structure into
+ * the IRQ chain. It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+int zs_open(struct tty_struct *tty, struct file * filp)
+{
+ struct sun_serial *info;
+ int retval, line;
+
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ /* The zilog lines for the mouse/keyboard must be
+ * opened using their respective drivers.
+ */
+ if ((line < 0) || (line >= NUM_CHANNELS))
+ return -ENODEV;
+ if((line == KEYBOARD_LINE) || (line == MOUSE_LINE))
+ return -ENODEV;
+ info = zs_soft + line;
+ /* Is the kgdb running over this line? */
+ if (info->kgdb_channel)
+ return -ENODEV;
+ if (serial_paranoia_check(info, tty->device, "zs_open"))
+ return -ENODEV;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("zs_open %s%d, count = %d\n", tty->driver.name, info->line,
+ info->count);
+#endif
+ if (info->tty != 0 && info->tty != tty) {
+ /* Never happen? */
+ printk("zs_open %s%d, tty overwrite.\n", tty->driver.name, info->line);
+ return -EBUSY;
+ }
+ info->count++;
+ tty->driver_data = info;
+ info->tty = tty;
+
+ /*
+ * Start up serial port
+ */
+ retval = startup(info);
+ if (retval)
+ return retval;
+
+ retval = block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("zs_open returning after block_til_ready with %d\n",
+ retval);
+#endif
+ return retval;
+ }
+
+ if ((info->count == 1) && (info->flags & ZILOG_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = info->normal_termios;
+ else
+ *tty->termios = info->callout_termios;
+ change_speed(info);
+ }
+
+ info->session = current->session;
+ info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("zs_open ttys%d successful...", info->line);
+#endif
+ return 0;
+}
+
+/* Finally, routines used to initialize the serial driver. */
+
+static void show_serial_version(void)
+{
+ char *revision = "$Revision: 1.3 $";
+ char *version, *p;
+
+ version = strchr(revision, ' ');
+ p = strchr(++version, ' ');
+ *p = '\0';
+ printk("Sparc Zilog8530 serial driver version %s\n", version);
+ *p = ' ';
+}
+
+/* Probe the PROM for the request zs chip number.
+ *
+ * Note: The Sun Voyager shows two addresses and two intr for it's
+ * Zilogs, what the second does, I don't know. It does work
+ * with using only the first number of each property. Also
+ * we have a special version for sun4u.
+ */
+#ifdef __sparc_v9__
+static struct devid_cookie zs_dcookie;
+static unsigned long zs_irq_flags;
+static struct sun_zslayout *get_zs(int chip)
+{
+ unsigned int vaddr[2] = { 0, 0 };
+ int busnode, seen, zsnode, sun4u_ino;
+ static int irq = 0;
+
+ if(chip < 0 || chip >= NUM_SERIAL)
+ panic("get_zs bogon zs chip number");
+
+ if(central_bus)
+ busnode = central_bus->child->prom_node;
+ else
+ busnode = prom_searchsiblings(prom_getchild(prom_root_node), "sbus");
+ if(busnode == 0 || busnode == -1)
+ panic("get_zs: no zs bus to search");
+
+ zsnode = prom_getchild(busnode);
+ seen = 0;
+ while(zsnode) {
+ int slave;
+
+ zsnode = prom_searchsiblings(zsnode, "zs");
+ slave = prom_getintdefault(zsnode, "slave", -1);
+ if((slave == chip) || (seen == chip)) {
+ int len = prom_getproperty(zsnode, "address",
+ (void *) vaddr, sizeof(vaddr));
+
+ if(len % sizeof(unsigned int)) {
+ prom_printf("WHOOPS: proplen for %s "
+ "was %d, need multiple of "
+ "%d\n", "address", len,
+ sizeof(unsigned int));
+ panic("zilog: address property");
+ }
+ zs_nodes[chip] = zsnode;
+ len = prom_getproperty(zsnode, "interrupts",
+ (char *) &sun4u_ino,
+ (sizeof(sun4u_ino)));
+ if(!irq) {
+ irq = zilog_irq = sun4u_ino;
+
+ /* Construct dcookie. */
+ if(central_bus) {
+ zs_dcookie.imap =
+ &central_bus->child->fhc_regs.uregs->fhc_uart_imap;
+ zs_dcookie.iclr =
+ &central_bus->child->fhc_regs.uregs->fhc_uart_iclr;
+ zs_dcookie.pil = 12;
+ zs_dcookie.bus_cookie = NULL;
+ zs_irq_flags =
+ (SA_DCOOKIE|SA_INTERRUPT|SA_STATIC_ALLOC|SA_FHC);
+ } else {
+ zs_dcookie.imap = zs_dcookie.iclr = NULL;
+ zs_dcookie.pil = -1;
+ zs_dcookie.bus_cookie = SBus_chain;
+ zs_irq_flags =
+ (SA_DCOOKIE|SA_INTERRUPT|SA_STATIC_ALLOC|SA_SBUS);
+ }
+ } else if(irq != sun4u_ino) {
+ panic("zilog: bogon irqs");
+ }
+ break;
+ }
+ zsnode = prom_getsibling(zsnode);
+ seen++;
+ }
+ if(!zsnode)
+ panic("get_zs: whee chip not found");
+ if(!vaddr[0])
+ panic("get_zs: whee no serial chip mappable");
+ return (struct sun_zslayout *)(unsigned long) vaddr[0];
+}
+#else /* !(__sparc_v9__) */
+static struct sun_zslayout *get_zs(int chip)
+{
+ struct linux_prom_irqs tmp_irq[2];
+ unsigned int paddr = 0;
+ unsigned int vaddr[2] = { 0, 0 };
+ int zsnode, tmpnode, iospace, slave, len, seen, sun4u_irq;
+ static int irq = 0;
+
+#if CONFIG_AP1000
+ printk("No zs chip\n");
+ return NULL;
+#endif
+
+ iospace = 0;
+ if(chip < 0 || chip >= NUM_SERIAL)
+ panic("get_zs bogon zs chip number");
+
+ if(sparc_cpu_model == sun4) {
+ /* Grrr, these have to be hardcoded aieee */
+ switch(chip) {
+ case 0:
+ paddr = 0xf1000000;
+ break;
+ case 1:
+ paddr = 0xf0000000;
+ break;
+ };
+ iospace = 0;
+ zs_nodes[chip] = 0;
+ if(!irq)
+ zilog_irq = irq = 12;
+ vaddr[0] = (unsigned long)
+ sparc_alloc_io(paddr, 0, 8,
+ "Zilog Serial", iospace, 0);
+ } else {
+ /* Can use the prom for other machine types */
+ zsnode = prom_getchild(prom_root_node);
+ if (sparc_cpu_model == sun4d) {
+ int board, node;
+
+ tmpnode = zsnode;
+ while (tmpnode && (tmpnode = prom_searchsiblings(tmpnode, "cpu-unit"))) {
+ board = prom_getintdefault (tmpnode, "board#", -1);
+ if (board == (chip >> 1)) {
+ node = prom_getchild(tmpnode);
+ if (node && (node = prom_searchsiblings(node, "bootbus"))) {
+ zsnode = node;
+ break;
+ }
+ }
+ tmpnode = prom_getsibling(tmpnode);
+ }
+ if (!tmpnode)
+ panic ("get_zs: couldn't find board%d's bootbus\n", chip >> 1);
+ } else if (sparc_cpu_model == sun4u) {
+ tmpnode = prom_searchsiblings(zsnode, "sbus");
+ if(tmpnode)
+ zsnode = prom_getchild(tmpnode);
+ } else {
+ tmpnode = prom_searchsiblings(zsnode, "obio");
+ if(tmpnode)
+ zsnode = prom_getchild(tmpnode);
+ }
+ if(!zsnode)
+ panic("get_zs no zs serial prom node");
+ seen = 0;
+ while(zsnode) {
+ zsnode = prom_searchsiblings(zsnode, "zs");
+ slave = prom_getintdefault(zsnode, "slave", -1);
+ if((slave == chip) ||
+ (sparc_cpu_model == sun4u && seen == chip)) {
+ /* The one we want */
+ len = prom_getproperty(zsnode, "address",
+ (void *) vaddr,
+ sizeof(vaddr));
+ if (len % sizeof(unsigned int)) {
+ prom_printf("WHOOPS: proplen for %s "
+ "was %d, need multiple of "
+ "%d\n", "address", len,
+ sizeof(unsigned int));
+ panic("zilog: address property");
+ }
+ zs_nodes[chip] = zsnode;
+ if(sparc_cpu_model == sun4u) {
+ len = prom_getproperty(zsnode, "interrupts",
+ (char *) &sun4u_irq,
+ sizeof(tmp_irq));
+ tmp_irq[0].pri = sun4u_irq;
+ } else {
+ len = prom_getproperty(zsnode, "intr",
+ (char *) tmp_irq,
+ sizeof(tmp_irq));
+ if (len % sizeof(struct linux_prom_irqs)) {
+ prom_printf(
+ "WHOOPS: proplen for %s "
+ "was %d, need multiple of "
+ "%d\n", "address", len,
+ sizeof(struct linux_prom_irqs));
+ panic("zilog: address property");
+ }
+ }
+ if(!irq) {
+ irq = zilog_irq = tmp_irq[0].pri;
+ } else {
+ if(tmp_irq[0].pri != irq)
+ panic("zilog: bogon irqs");
+ }
+ break;
+ }
+ zsnode = prom_getsibling(zsnode);
+ seen++;
+ }
+ if(!zsnode)
+ panic("get_zs whee chip not found");
+ }
+ if(!vaddr[0])
+ panic("get_zs whee no serial chip mappable");
+
+ return (struct sun_zslayout *)(unsigned long) vaddr[0];
+}
+#endif
+
+static inline void
+init_zscons_termios(struct termios *termios)
+{
+ char mode[16], buf[16];
+ char *mode_prop = "ttyX-mode";
+ char *cd_prop = "ttyX-ignore-cd";
+ char *dtr_prop = "ttyX-rts-dtr-off";
+ char *s;
+ int baud, bits, cflag;
+ char parity;
+ int topnd, nd;
+ int channel, stop;
+ int carrier = 0;
+ int rtsdtr = 1;
+ extern int serial_console;
+
+ if (!serial_console)
+ return;
+
+ if (serial_console == 1) {
+ mode_prop[3] = 'a';
+ cd_prop[3] = 'a';
+ dtr_prop[3] = 'a';
+ } else {
+ mode_prop[3] = 'b';
+ cd_prop[3] = 'b';
+ dtr_prop[3] = 'b';
+ }
+
+ topnd = prom_getchild(prom_root_node);
+ nd = prom_searchsiblings(topnd, "options");
+ if (!nd) {
+ strcpy(mode, "9600,8,n,1,-");
+ goto no_options;
+ }
+
+ if (!prom_node_has_property(nd, mode_prop)) {
+ strcpy(mode, "9600,8,n,1,-");
+ goto no_options;
+ }
+
+ memset(mode, 0, sizeof(mode));
+ prom_getstring(nd, mode_prop, mode, sizeof(mode));
+
+ if (prom_node_has_property(nd, cd_prop)) {
+ memset(buf, 0, sizeof(buf));
+ prom_getstring(nd, cd_prop, buf, sizeof(buf));
+ if (!strcmp(buf, "false"))
+ carrier = 1;
+
+ /* XXX this is unused below. */
+ }
+
+ if (prom_node_has_property(nd, cd_prop)) {
+ memset(buf, 0, sizeof(buf));
+ prom_getstring(nd, cd_prop, buf, sizeof(buf));
+ if (!strcmp(buf, "false"))
+ rtsdtr = 0;
+
+ /* XXX this is unused below. */
+ }
+
+no_options:
+ cflag = CREAD | HUPCL | CLOCAL;
+
+ s = mode;
+ baud = simple_strtoul(s, 0, 0);
+ s = strchr(s, ',');
+ bits = simple_strtoul(++s, 0, 0);
+ s = strchr(s, ',');
+ parity = *(++s);
+ s = strchr(s, ',');
+ stop = simple_strtoul(++s, 0, 0);
+ s = strchr(s, ',');
+ /* XXX handshake is not handled here. */
+
+ for (channel = 0; channel < NUM_CHANNELS; channel++)
+ if (zs_soft[channel].is_cons)
+ break;
+
+ switch (baud) {
+ case 150:
+ cflag |= B150;
+ break;
+ case 300:
+ cflag |= B300;
+ break;
+ case 600:
+ cflag |= B600;
+ break;
+ case 1200:
+ cflag |= B1200;
+ break;
+ case 2400:
+ cflag |= B2400;
+ break;
+ case 4800:
+ cflag |= B4800;
+ break;
+ default:
+ baud = 9600;
+ case 9600:
+ cflag |= B9600;
+ break;
+ case 19200:
+ cflag |= B19200;
+ break;
+ case 38400:
+ cflag |= B38400;
+ break;
+ }
+ zs_soft[channel].zs_baud = baud;
+
+ switch (bits) {
+ case 5:
+ zscons_regs[3] = Rx5 | RxENAB;
+ zscons_regs[5] = Tx5 | TxENAB;
+ zs_soft[channel].parity_mask = 0x1f;
+ cflag |= CS5;
+ break;
+ case 6:
+ zscons_regs[3] = Rx6 | RxENAB;
+ zscons_regs[5] = Tx6 | TxENAB;
+ zs_soft[channel].parity_mask = 0x3f;
+ cflag |= CS6;
+ break;
+ case 7:
+ zscons_regs[3] = Rx7 | RxENAB;
+ zscons_regs[5] = Tx7 | TxENAB;
+ zs_soft[channel].parity_mask = 0x7f;
+ cflag |= CS7;
+ break;
+ default:
+ case 8:
+ zscons_regs[3] = Rx8 | RxENAB;
+ zscons_regs[5] = Tx8 | TxENAB;
+ zs_soft[channel].parity_mask = 0xff;
+ cflag |= CS8;
+ break;
+ }
+ zscons_regs[5] |= DTR;
+
+ switch (parity) {
+ case 'o':
+ zscons_regs[4] |= PAR_ENAB;
+ cflag |= (PARENB | PARODD);
+ break;
+ case 'e':
+ zscons_regs[4] |= (PAR_ENAB | PAR_EVEN);
+ cflag |= PARENB;
+ break;
+ default:
+ case 'n':
+ break;
+ }
+
+ switch (stop) {
+ default:
+ case 1:
+ zscons_regs[4] |= SB1;
+ break;
+ case 2:
+ cflag |= CSTOPB;
+ zscons_regs[4] |= SB2;
+ break;
+ }
+
+ termios->c_cflag = cflag;
+}
+
+__initfunc(static void serial_finish_init(void (*printfunc)(const char *, unsigned)))
+{
+ extern unsigned char *linux_serial_image;
+ char buffer[2048];
+
+ sprintf (buffer, linux_serial_image, UTS_RELEASE);
+ (*printfunc)(buffer, strlen(buffer));
+}
+
+static inline void
+zs_cons_check(struct sun_serial *ss, int channel)
+{
+ int i, o, io;
+ static int consout_registered = 0;
+ static int msg_printed = 0;
+ static struct console console = {
+ zs_console_print, 0,
+ zs_console_wait_key, zs_console_device };
+
+ i = o = io = 0;
+
+ /* Is this one of the serial console lines? */
+ if((zs_cons_chanout != channel) &&
+ (zs_cons_chanin != channel))
+ return;
+ zs_conschan = ss->zs_channel;
+ zs_consinfo = ss;
+
+ /* Register the console output putchar, if necessary */
+ if((zs_cons_chanout == channel)) {
+ o = 1;
+ /* double whee.. */
+ if(!consout_registered) {
+ serial_finish_init (zs_console_print);
+ register_console(&console);
+ consout_registered = 1;
+ }
+ }
+
+ /* If this is console input, we handle the break received
+ * status interrupt on this line to mean prom_halt().
+ */
+ if(zs_cons_chanin == channel) {
+ ss->break_abort = 1;
+ i = 1;
+ }
+ if(o && i)
+ io = 1;
+
+ /* Set flag variable for this port so that it cannot be
+ * opened for other uses by accident.
+ */
+ ss->is_cons = 1;
+
+ if(io) {
+ if(!msg_printed) {
+ printk("zs%d: console I/O\n", ((channel>>1)&1));
+ msg_printed = 1;
+ }
+ } else {
+ printk("zs%d: console %s\n", ((channel>>1)&1),
+ (i==1 ? "input" : (o==1 ? "output" : "WEIRD")));
+ }
+}
+
+/* This is for the auto baud rate detection in the mouse driver. */
+void zs_change_mouse_baud(int newbaud)
+{
+ int channel = MOUSE_LINE;
+ int brg;
+
+ zs_soft[channel].zs_baud = newbaud;
+ brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+ (ZS_CLOCK / zs_soft[channel].clk_divisor));
+ write_zsreg(zs_soft[channel].zs_channel, R12, (brg & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R13, ((brg >> 8) & 0xff));
+}
+
+__initfunc(int zs_probe (unsigned long *memory_start))
+{
+ char *p;
+ int node;
+ int i;
+
+ if(sparc_cpu_model == sun4)
+ goto no_probe;
+
+ node = prom_getchild(prom_root_node);
+ if (sparc_cpu_model == sun4d) {
+ node = prom_searchsiblings(node, "boards");
+ NUM_SERIAL = 0;
+ if (!node)
+ panic ("Cannot find out count of boards");
+ else
+ node = prom_getchild(node);
+ while (node && (node = prom_searchsiblings(node, "bif"))) {
+ NUM_SERIAL += 2;
+ node = prom_getsibling(node);
+ }
+ goto no_probe;
+ } else if (sparc_cpu_model == sun4u) {
+ node = prom_searchsiblings(node, "sbus");
+ if(node)
+ node = prom_getchild(node);
+ if(!node)
+ return -ENODEV;
+ } else {
+ node = prom_searchsiblings(node, "obio");
+ if(node)
+ node = prom_getchild(node);
+ goto no_probe;
+ }
+
+ node = prom_searchsiblings(node, "zs");
+ if (!node)
+ return -ENODEV;
+
+no_probe:
+ p = (char *)((*memory_start + 7) & ~7);
+ zs_chips = (struct sun_zslayout **)(p);
+ i = NUM_SERIAL * sizeof (struct sun_zslayout *);
+ zs_channels = (struct sun_zschannel **)(p + i);
+ i += NUM_CHANNELS * sizeof (struct sun_zschannel *);
+ zs_nodes = (int *)(p + i);
+ i += NUM_SERIAL * sizeof (int);
+ zs_soft = (struct sun_serial *)(p + i);
+ i += NUM_CHANNELS * sizeof (struct sun_serial);
+ zs_ttys = (struct tty_struct *)(p + i);
+ i += NUM_CHANNELS * sizeof (struct tty_struct);
+ serial_table = (struct tty_struct **)(p + i);
+ i += NUM_CHANNELS * sizeof (struct tty_struct *);
+ serial_termios = (struct termios **)(p + i);
+ i += NUM_CHANNELS * sizeof (struct termios *);
+ serial_termios_locked = (struct termios **)(p + i);
+ i += NUM_CHANNELS * sizeof (struct termios *);
+ memset (p, 0, i);
+ *memory_start = (((unsigned long)p) + i + 7) & ~7;
+
+ /* Fill in rs_ops struct... */
+ sunserial_setinitfunc(memory_start, zs_init);
+ rs_ops.rs_cons_hook = zs_cons_hook;
+ rs_ops.rs_kgdb_hook = zs_kgdb_hook;
+ rs_ops.rs_change_mouse_baud = zs_change_mouse_baud;
+
+ return 0;
+}
+
+__initfunc(int zs_init(void))
+{
+ int chip, channel, brg, i;
+ unsigned long flags;
+ struct sun_serial *info;
+ char dummy;
+
+#if CONFIG_AP1000
+ printk("not doing zs_init()\n");
+ return 0;
+#endif
+
+#ifdef CONFIG_PCI
+ if (prom_searchsiblings(prom_getchild(prom_root_node), "pci"))
+ return 0;
+#endif
+
+ /* Setup base handler, and timer table. */
+ init_bh(SERIAL_BH, do_serial_bh);
+ timer_table[RS_TIMER].fn = zs_timer;
+ timer_table[RS_TIMER].expires = 0;
+
+ show_serial_version();
+
+ /* Initialize the tty_driver structure */
+ /* SPARC: Not all of this is exactly right for us. */
+
+ memset(&serial_driver, 0, sizeof(struct tty_driver));
+ serial_driver.magic = TTY_DRIVER_MAGIC;
+ serial_driver.driver_name = "serial";
+ serial_driver.name = "ttyS";
+ serial_driver.major = TTY_MAJOR;
+ serial_driver.minor_start = 64;
+ serial_driver.num = NUM_CHANNELS;
+ serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ serial_driver.subtype = SERIAL_TYPE_NORMAL;
+ serial_driver.init_termios = tty_std_termios;
+
+ serial_driver.init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ serial_driver.flags = TTY_DRIVER_REAL_RAW;
+ serial_driver.refcount = &serial_refcount;
+ serial_driver.table = serial_table;
+ serial_driver.termios = serial_termios;
+ serial_driver.termios_locked = serial_termios_locked;
+
+ serial_driver.open = zs_open;
+ serial_driver.close = zs_close;
+ serial_driver.write = zs_write;
+ serial_driver.flush_chars = zs_flush_chars;
+ serial_driver.write_room = zs_write_room;
+ serial_driver.chars_in_buffer = zs_chars_in_buffer;
+ serial_driver.flush_buffer = zs_flush_buffer;
+ serial_driver.ioctl = zs_ioctl;
+ serial_driver.throttle = zs_throttle;
+ serial_driver.unthrottle = zs_unthrottle;
+ serial_driver.set_termios = zs_set_termios;
+ serial_driver.stop = zs_stop;
+ serial_driver.start = zs_start;
+ serial_driver.hangup = zs_hangup;
+
+ /* I'm too lazy, someone write versions of this for us. -DaveM */
+ serial_driver.read_proc = 0;
+ serial_driver.proc_entry = 0;
+
+ init_zscons_termios(&serial_driver.init_termios);
+
+ /*
+ * The callout device is just like normal device except for
+ * major number and the subtype code.
+ */
+ callout_driver = serial_driver;
+ callout_driver.name = "cua";
+ callout_driver.major = TTYAUX_MAJOR;
+ callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+ if (tty_register_driver(&serial_driver))
+ panic("Couldn't register serial driver\n");
+ if (tty_register_driver(&callout_driver))
+ panic("Couldn't register callout driver\n");
+
+ save_flags(flags); cli();
+
+ /* Set up our interrupt linked list */
+ zs_chain = &zs_soft[0];
+ for(channel = 0; channel < NUM_CHANNELS - 1; channel++)
+ zs_soft[channel].zs_next = &zs_soft[channel + 1];
+ zs_soft[channel + 1].zs_next = 0;
+
+ /* Initialize Softinfo */
+ for(chip = 0; chip < NUM_SERIAL; chip++) {
+ /* If we are doing kgdb over one of the channels on
+ * chip zero, kgdb_channel will be set to 1 by the
+ * zs_kgdb_hook() routine below.
+ */
+ if(!zs_chips[chip]) {
+ zs_chips[chip] = get_zs(chip);
+ /* Two channels per chip */
+ zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
+ zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
+ zs_soft[(chip*2)].kgdb_channel = 0;
+ zs_soft[(chip*2)+1].kgdb_channel = 0;
+ }
+
+ /* First, set up channel A on this chip. */
+ channel = chip * 2;
+ zs_soft[channel].zs_channel = zs_channels[channel];
+ zs_soft[channel].change_needed = 0;
+ zs_soft[channel].clk_divisor = 16;
+ zs_soft[channel].cons_keyb = 0;
+ zs_soft[channel].cons_mouse = 0;
+ zs_soft[channel].channelA = 1;
+
+ /* Now, channel B */
+ channel++;
+ zs_soft[channel].zs_channel = zs_channels[channel];
+ zs_soft[channel].change_needed = 0;
+ zs_soft[channel].clk_divisor = 16;
+ zs_soft[channel].cons_keyb = 0;
+ zs_soft[channel].cons_mouse = 0;
+ zs_soft[channel].channelA = 0;
+ }
+
+ /* Initialize Hardware */
+ for(channel = 0; channel < NUM_CHANNELS; channel++) {
+
+ /* Hardware reset each chip */
+ if (!(channel & 1)) {
+ write_zsreg(zs_soft[channel].zs_channel, R9, FHWRES);
+ udelay(20); /* wait for some old sun4's */
+ dummy = read_zsreg(zs_soft[channel].zs_channel, R0);
+ }
+
+ if(channel == KEYBOARD_LINE) {
+ zs_soft[channel].cons_keyb = 1;
+ zs_soft[channel].parity_mask = 0xff;
+ zs_kbdchan = zs_soft[channel].zs_channel;
+
+ write_zsreg(zs_soft[channel].zs_channel, R4,
+ (PAR_EVEN | X16CLK | SB1));
+ write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
+ write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+ write_zsreg(zs_soft[channel].zs_channel, R9, NV);
+ write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
+ write_zsreg(zs_soft[channel].zs_channel, R11,
+ (TCBR | RCBR));
+ zs_soft[channel].zs_baud = 1200;
+ brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+ ZS_CLOCK/zs_soft[channel].clk_divisor);
+ write_zsreg(zs_soft[channel].zs_channel, R12,
+ (brg & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R13,
+ ((brg >> 8) & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
+
+ /* Enable Rx/Tx, IRQs, and inform kbd driver */
+ write_zsreg(zs_soft[channel].zs_channel, R14,
+ (BRSRC | BRENAB));
+ write_zsreg(zs_soft[channel].zs_channel, R3,
+ (Rx8 | RxENAB));
+ write_zsreg(zs_soft[channel].zs_channel, R5,
+ (Tx8 | TxENAB | DTR | RTS));
+
+ write_zsreg(zs_soft[channel].zs_channel, R15,
+ (DCDIE | CTSIE | TxUIE | BRKIE));
+ write_zsreg(zs_soft[channel].zs_channel, R0,
+ RES_EXT_INT);
+ write_zsreg(zs_soft[channel].zs_channel, R0,
+ RES_EXT_INT);
+
+ write_zsreg(zs_soft[channel].zs_channel, R1,
+ (EXT_INT_ENAB | INT_ALL_Rx));
+ write_zsreg(zs_soft[channel].zs_channel, R9,
+ (NV | MIE));
+ ZS_CLEARERR(zs_soft[channel].zs_channel);
+ ZS_CLEARFIFO(zs_soft[channel].zs_channel);
+ } else if(channel == MOUSE_LINE) {
+ zs_soft[channel].cons_mouse = 1;
+ zs_soft[channel].parity_mask = 0xff;
+ zs_mousechan = zs_soft[channel].zs_channel;
+
+ write_zsreg(zs_soft[channel].zs_channel, R4,
+ (PAR_EVEN | X16CLK | SB1));
+ write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
+ write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+ write_zsreg(zs_soft[channel].zs_channel, R9, NV);
+ write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
+ write_zsreg(zs_soft[channel].zs_channel, R11,
+ (TCBR | RCBR));
+
+ zs_soft[channel].zs_baud = 4800;
+ brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+ ZS_CLOCK/zs_soft[channel].clk_divisor);
+ write_zsreg(zs_soft[channel].zs_channel, R12,
+ (brg & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R13,
+ ((brg >> 8) & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
+
+ /* Enable Rx, IRQs, and inform mouse driver */
+ write_zsreg(zs_soft[channel].zs_channel, R14,
+ (BRSRC | BRENAB));
+ write_zsreg(zs_soft[channel].zs_channel, R3,
+ (Rx8 | RxENAB));
+ write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+
+ write_zsreg(zs_soft[channel].zs_channel, R15,
+ (DCDIE | CTSIE | TxUIE | BRKIE));
+ write_zsreg(zs_soft[channel].zs_channel, R0,
+ RES_EXT_INT);
+ write_zsreg(zs_soft[channel].zs_channel, R0,
+ RES_EXT_INT);
+
+ write_zsreg(zs_soft[channel].zs_channel, R1,
+ (EXT_INT_ENAB | INT_ALL_Rx));
+ write_zsreg(zs_soft[channel].zs_channel, R9,
+ (NV | MIE));
+
+ sun_mouse_zsinit();
+ } else if (zs_soft[channel].is_cons) {
+ brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+ ZS_CLOCK/zs_soft[channel].clk_divisor);
+ zscons_regs[12] = brg & 0xff;
+ zscons_regs[13] = (brg >> 8) & 0xff;
+
+ memcpy(zs_soft[channel].curregs, zscons_regs, sizeof(zscons_regs));
+ load_zsregs(&zs_soft[channel], zscons_regs);
+
+ ZS_CLEARERR(zs_soft[channel].zs_channel);
+ ZS_CLEARFIFO(zs_soft[channel].zs_channel);
+ } else if (zs_soft[channel].kgdb_channel) {
+ /* If this is the kgdb line, enable interrupts because
+ * we now want to receive the 'control-c' character
+ * from the client attached to us asynchronously.
+ */
+ zs_soft[channel].parity_mask = 0xff;
+ kgdb_chaninit(&zs_soft[channel], 1,
+ zs_soft[channel].zs_baud);
+ } else {
+ zs_soft[channel].parity_mask = 0xff;
+ write_zsreg(zs_soft[channel].zs_channel, R4,
+ (PAR_EVEN | X16CLK | SB1));
+ write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
+ write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+ write_zsreg(zs_soft[channel].zs_channel, R9, NV);
+ write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
+ write_zsreg(zs_soft[channel].zs_channel, R11,
+ (RCBR | TCBR));
+ zs_soft[channel].zs_baud = 9600;
+ brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+ ZS_CLOCK/zs_soft[channel].clk_divisor);
+ write_zsreg(zs_soft[channel].zs_channel, R12,
+ (brg & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R13,
+ ((brg >> 8) & 0xff));
+ write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
+ write_zsreg(zs_soft[channel].zs_channel, R14,
+ (BRSRC | BRENAB));
+ write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
+ write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+ write_zsreg(zs_soft[channel].zs_channel, R15, DCDIE);
+ write_zsreg(zs_soft[channel].zs_channel, R9, NV | MIE);
+ write_zsreg(zs_soft[channel].zs_channel, R0,
+ RES_EXT_INT);
+ write_zsreg(zs_soft[channel].zs_channel, R0,
+ RES_EXT_INT);
+ }
+ }
+
+ for (info = zs_chain, i=0; info; info = info->zs_next, i++) {
+ info->magic = SERIAL_MAGIC;
+ info->port = (long) info->zs_channel;
+ info->line = i;
+ info->tty = 0;
+ info->irq = zilog_irq;
+ info->custom_divisor = 16;
+ info->close_delay = 50;
+ info->closing_wait = 3000;
+ info->x_char = 0;
+ info->event = 0;
+ info->count = 0;
+ info->blocked_open = 0;
+ info->tqueue.routine = do_softint;
+ info->tqueue.data = info;
+ info->tqueue_hangup.routine = do_serial_hangup;
+ info->tqueue_hangup.data = info;
+ info->callout_termios = callout_driver.init_termios;
+ info->normal_termios = serial_driver.init_termios;
+ info->open_wait = 0;
+ info->close_wait = 0;
+ printk("tty%02d at 0x%04x (irq = %d)", info->line,
+ info->port, info->irq);
+ printk(" is a Zilog8530\n");
+ }
+
+#ifndef __sparc_v9__
+ if (request_irq(zilog_irq, zs_interrupt,
+ (SA_INTERRUPT | SA_STATIC_ALLOC),
+ "Zilog8530", zs_chain))
+ panic("Unable to attach zs intr\n");
+#else
+ zs_dcookie.real_dev_id = zs_chain;
+ if (request_irq(zilog_irq, zs_interrupt,
+ zs_irq_flags, "Zilog8530", &zs_dcookie))
+ panic("Unable to attach zs intr\n");
+#endif
+ restore_flags(flags);
+
+ keyboard_zsinit(kbd_put_char);
+ return 0;
+}
+
+/* Hooks for running a serial console. con_init() calls this if the
+ * console is being run over one of the ttya/ttyb serial ports.
+ * 'chip' should be zero, as chip 1 drives the mouse/keyboard.
+ * 'channel' is decoded as 0=TTYA 1=TTYB, note that the channels
+ * are addressed backwards, channel B is first, then channel A.
+ */
+void
+zs_cons_hook(int chip, int out, int line)
+{
+ int channel;
+
+#ifdef CONFIG_PCI
+ if (prom_searchsiblings(prom_getchild(prom_root_node), "pci"))
+ return;
+#endif
+
+ if(chip)
+ panic("zs_cons_hook called with chip not zero");
+ if(line != 1 && line != 2)
+ panic("zs_cons_hook called with line not ttya or ttyb");
+ channel = line - 1;
+ if(!zs_chips[chip]) {
+ zs_chips[chip] = get_zs(chip);
+ /* Two channels per chip */
+ zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
+ zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
+ }
+ zs_soft[channel].zs_channel = zs_channels[channel];
+ zs_soft[channel].change_needed = 0;
+ zs_soft[channel].clk_divisor = 16;
+ if(out)
+ zs_cons_chanout = ((chip * 2) + channel);
+ else
+ zs_cons_chanin = ((chip * 2) + channel);
+ zs_cons_check(&zs_soft[channel], channel);
+}
+
+/* This is called at boot time to prime the kgdb serial debugging
+ * serial line. The 'tty_num' argument is 0 for /dev/ttya and 1
+ * for /dev/ttyb which is determined in setup_arch() from the
+ * boot command line flags.
+ */
+void
+zs_kgdb_hook(int tty_num)
+{
+ int chip = 0;
+
+ if(!zs_chips[chip]) {
+ zs_chips[chip] = get_zs(chip);
+ /* Two channels per chip */
+ zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
+ zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
+ }
+ zs_soft[tty_num].zs_channel = zs_channels[tty_num];
+ zs_kgdbchan = zs_soft[tty_num].zs_channel;
+ zs_soft[tty_num].change_needed = 0;
+ zs_soft[tty_num].clk_divisor = 16;
+ zs_soft[tty_num].zs_baud = 9600;
+ zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */
+ zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
+ /* Turn on transmitter/receiver at 8-bits/char */
+ kgdb_chaninit(&zs_soft[tty_num], 0, 9600);
+ ZS_CLEARERR(zs_kgdbchan);
+ ZS_CLEARFIFO(zs_kgdbchan);
+}
diff --git a/drivers/sbus/char/zs.h b/drivers/sbus/char/zs.h
new file mode 100644
index 000000000..fd28e4ced
--- /dev/null
+++ b/drivers/sbus/char/zs.h
@@ -0,0 +1,428 @@
+/* $Id: zs.h,v 1.1 1997/08/28 02:23:45 ecd Exp $
+ * zs.h: Definitions for the Sparc Zilog serial driver.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ */
+#ifndef _SPARC_SERIAL_H
+#define _SPARC_SERIAL_H
+
+/* Just one channel */
+struct sun_zschannel {
+ volatile unsigned char control;
+ volatile unsigned char pad1;
+ volatile unsigned char data;
+ volatile unsigned char pad2;
+};
+
+/* The address space layout for each zs chip. Yes they are
+ * backwards.
+ */
+struct sun_zslayout {
+ struct sun_zschannel channelB;
+ struct sun_zschannel channelA;
+};
+
+#define NUM_ZSREGS 16
+
+struct serial_struct {
+ int type;
+ int line;
+ int port;
+ int irq;
+ int flags;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int baud_base;
+ unsigned short close_delay;
+ char reserved_char[2];
+ int hub6;
+ unsigned short closing_wait; /* time to wait before closing */
+ unsigned short closing_wait2; /* no longer used... */
+ int reserved[4];
+};
+
+/*
+ * For the close wait times, 0 means wait forever for serial port to
+ * flush its output. 65535 means don't wait at all.
+ */
+#define ZILOG_CLOSING_WAIT_INF 0
+#define ZILOG_CLOSING_WAIT_NONE 65535
+
+/*
+ * Definitions for ZILOG_struct (and serial_struct) flags field
+ */
+#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
+ on the callout port */
+#define ZILOG_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
+#define ZILOG_SAK 0x0004 /* Secure Attention Key (Orange book) */
+#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define ZILOG_SPD_MASK 0x0030
+#define ZILOG_SPD_HI 0x0010 /* Use 76800 instead of 38400 bps */
+#define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */
+
+#define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
+#define ZILOG_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
+#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define ZILOG_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
+#define ZILOG_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
+
+#define ZILOG_FLAGS 0x0FFF /* Possible legal ZILOG flags */
+#define ZILOG_USR_MASK 0x0430 /* Legal flags that non-privileged
+ * users can set or reset */
+
+/* Internal flags used only by kernel/chr_drv/serial.c */
+#define ZILOG_INITIALIZED 0x80000000 /* Serial port was initialized */
+#define ZILOG_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
+#define ZILOG_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
+#define ZILOG_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
+#define ZILOG_CLOSING 0x08000000 /* Serial port is closing */
+#define ZILOG_CTS_FLOW 0x04000000 /* Do CTS flow control */
+#define ZILOG_CHECK_CD 0x02000000 /* i.e., CLOCAL */
+
+/* Software state per channel */
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct sun_serial {
+ struct sun_serial *zs_next; /* For IRQ servicing chain */
+ struct sun_zschannel *zs_channel; /* Channel registers */
+ unsigned char read_reg_zero;
+
+ char soft_carrier; /* Use soft carrier on this channel */
+ char cons_keyb; /* Channel runs the keyboard */
+ char cons_mouse; /* Channel runs the mouse */
+ char break_abort; /* Is serial console in, so process brk/abrt */
+ char kgdb_channel; /* Kgdb is running on this channel */
+ char is_cons; /* Is this our console. */
+
+ char channelA; /* This is channel A. */
+ char parity_mask; /* Mask out parity bits in data register. */
+
+ /* We need to know the current clock divisor
+ * to read the bps rate the chip has currently
+ * loaded.
+ */
+ unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */
+ int zs_baud;
+
+ /* Current write register values */
+ unsigned char curregs[NUM_ZSREGS];
+
+ char change_needed;
+
+ int magic;
+ int baud_base;
+ int port;
+ int irq;
+ int flags; /* defined in tty.h */
+ int type; /* UART type */
+ struct tty_struct *tty;
+ int read_status_mask;
+ int ignore_status_mask;
+ int timeout;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int x_char; /* xon/xoff character */
+ int close_delay;
+ unsigned short closing_wait;
+ unsigned short closing_wait2;
+ unsigned long event;
+ unsigned long last_active;
+ int line;
+ int count; /* # of fd on device */
+ int blocked_open; /* # of blocked opens */
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ unsigned char *xmit_buf;
+ int xmit_head;
+ int xmit_tail;
+ int xmit_cnt;
+ struct tq_struct tqueue;
+ struct tq_struct tqueue_hangup;
+ struct termios normal_termios;
+ struct termios callout_termios;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+};
+
+
+#define SERIAL_MAGIC 0x5301
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP 0
+
+#endif /* __KERNEL__ */
+
+/* Conversion routines to/from brg time constants from/to bits
+ * per second.
+ */
+#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
+#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+/* The Zilog register set */
+
+#define FLAG 0x7e
+
+/* Write Register 0 */
+#define R0 0 /* Register selects */
+#define R1 1
+#define R2 2
+#define R3 3
+#define R4 4
+#define R5 5
+#define R6 6
+#define R7 7
+#define R8 8
+#define R9 9
+#define R10 10
+#define R11 11
+#define R12 12
+#define R13 13
+#define R14 14
+#define R15 15
+
+#define NULLCODE 0 /* Null Code */
+#define POINT_HIGH 0x8 /* Select upper half of registers */
+#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
+#define SEND_ABORT 0x18 /* HDLC Abort */
+#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
+#define RES_Tx_P 0x28 /* Reset TxINT Pending */
+#define ERR_RES 0x30 /* Error Reset */
+#define RES_H_IUS 0x38 /* Reset highest IUS */
+
+#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
+#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
+#define RES_EOM_L 0xC0 /* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
+#define TxINT_ENAB 0x2 /* Tx Int Enable */
+#define PAR_SPEC 0x4 /* Parity is special condition */
+
+#define RxINT_DISAB 0 /* Rx Int Disable */
+#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
+#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
+#define INT_ERR_Rx 0x18 /* Int on error only */
+#define RxINT_MASK 0x18
+
+#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
+#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
+#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define RxENAB 0x1 /* Rx Enable */
+#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
+#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
+#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
+#define ENT_HM 0x10 /* Enter Hunt Mode */
+#define AUTO_ENAB 0x20 /* Auto Enables */
+#define Rx5 0x0 /* Rx 5 Bits/Character */
+#define Rx7 0x40 /* Rx 7 Bits/Character */
+#define Rx6 0x80 /* Rx 6 Bits/Character */
+#define Rx8 0xc0 /* Rx 8 Bits/Character */
+#define RxN_MASK 0xc0
+
+/* Write Register 4 */
+
+#define PAR_ENAB 0x1 /* Parity Enable */
+#define PAR_EVEN 0x2 /* Parity Even/Odd* */
+
+#define SYNC_ENAB 0 /* Sync Modes Enable */
+#define SB1 0x4 /* 1 stop bit/char */
+#define SB15 0x8 /* 1.5 stop bits/char */
+#define SB2 0xc /* 2 stop bits/char */
+
+#define MONSYNC 0 /* 8 Bit Sync character */
+#define BISYNC 0x10 /* 16 bit sync character */
+#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
+#define EXTSYNC 0x30 /* External Sync Mode */
+
+#define X1CLK 0x0 /* x1 clock mode */
+#define X16CLK 0x40 /* x16 clock mode */
+#define X32CLK 0x80 /* x32 clock mode */
+#define X64CLK 0xC0 /* x64 clock mode */
+
+/* Write Register 5 */
+
+#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
+#define RTS 0x2 /* RTS */
+#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
+#define TxENAB 0x8 /* Tx Enable */
+#define SND_BRK 0x10 /* Send Break */
+#define Tx5 0x0 /* Tx 5 bits (or less)/character */
+#define Tx7 0x20 /* Tx 7 bits/character */
+#define Tx6 0x40 /* Tx 6 bits/character */
+#define Tx8 0x60 /* Tx 8 bits/character */
+#define TxN_MASK 0x60
+#define DTR 0x80 /* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define VIS 1 /* Vector Includes Status */
+#define NV 2 /* No Vector */
+#define DLC 4 /* Disable Lower Chain */
+#define MIE 8 /* Master Interrupt Enable */
+#define STATHI 0x10 /* Status high */
+#define NORESET 0 /* No reset on write to R9 */
+#define CHRB 0x40 /* Reset channel B */
+#define CHRA 0x80 /* Reset channel A */
+#define FHWRES 0xc0 /* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define BIT6 1 /* 6 bit/8bit sync */
+#define LOOPMODE 2 /* SDLC Loop mode */
+#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
+#define MARKIDLE 8 /* Mark/flag on idle */
+#define GAOP 0x10 /* Go active on poll */
+#define NRZ 0 /* NRZ mode */
+#define NRZI 0x20 /* NRZI mode */
+#define FM1 0x40 /* FM1 (transition = 1) */
+#define FM0 0x60 /* FM0 (transition = 0) */
+#define CRCPS 0x80 /* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define TRxCXT 0 /* TRxC = Xtal output */
+#define TRxCTC 1 /* TRxC = Transmit clock */
+#define TRxCBR 2 /* TRxC = BR Generator Output */
+#define TRxCDP 3 /* TRxC = DPLL output */
+#define TRxCOI 4 /* TRxC O/I */
+#define TCRTxCP 0 /* Transmit clock = RTxC pin */
+#define TCTRxCP 8 /* Transmit clock = TRxC pin */
+#define TCBR 0x10 /* Transmit clock = BR Generator output */
+#define TCDPLL 0x18 /* Transmit clock = DPLL output */
+#define RCRTxCP 0 /* Receive clock = RTxC pin */
+#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
+#define RCBR 0x40 /* Receive clock = BR Generator output */
+#define RCDPLL 0x60 /* Receive clock = DPLL output */
+#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define BRENAB 1 /* Baud rate generator enable */
+#define BRSRC 2 /* Baud rate generator source */
+#define DTRREQ 4 /* DTR/Request function */
+#define AUTOECHO 8 /* Auto Echo */
+#define LOOPBAK 0x10 /* Local loopback */
+#define SEARCH 0x20 /* Enter search mode */
+#define RMC 0x40 /* Reset missing clock */
+#define DISDPLL 0x60 /* Disable DPLL */
+#define SSBR 0x80 /* Set DPLL source = BR generator */
+#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
+#define SFMM 0xc0 /* Set FM mode */
+#define SNRZI 0xe0 /* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define ZCIE 2 /* Zero count IE */
+#define DCDIE 8 /* DCD IE */
+#define SYNCIE 0x10 /* Sync/hunt IE */
+#define CTSIE 0x20 /* CTS IE */
+#define TxUIE 0x40 /* Tx Underrun/EOM IE */
+#define BRKIE 0x80 /* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define Rx_CH_AV 0x1 /* Rx Character Available */
+#define ZCOUNT 0x2 /* Zero count */
+#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
+#define DCD 0x8 /* DCD */
+#define SYNC 0x10 /* Sync/hunt */
+#define CTS 0x20 /* CTS */
+#define TxEOM 0x40 /* Tx underrun */
+#define BRK_ABRT 0x80 /* Break/Abort */
+
+/* Read Register 1 */
+#define ALL_SNT 0x1 /* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define RES3 0x8 /* 0/3 */
+#define RES4 0x4 /* 0/4 */
+#define RES5 0xc /* 0/5 */
+#define RES6 0x2 /* 0/6 */
+#define RES7 0xa /* 0/7 */
+#define RES8 0x6 /* 0/8 */
+#define RES18 0xe /* 1/8 */
+#define RES28 0x0 /* 2/8 */
+/* Special Rx Condition Interrupts */
+#define PAR_ERR 0x10 /* Parity error */
+#define Rx_OVR 0x20 /* Rx Overrun Error */
+#define CRC_ERR 0x40 /* CRC/Framing Error */
+#define END_FR 0x80 /* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+#define CHB_Tx_EMPTY 0x00
+#define CHB_EXT_STAT 0x02
+#define CHB_Rx_AVAIL 0x04
+#define CHB_SPECIAL 0x06
+#define CHA_Tx_EMPTY 0x08
+#define CHA_EXT_STAT 0x0a
+#define CHA_Rx_AVAIL 0x0c
+#define CHA_SPECIAL 0x0e
+#define STATUS_MASK 0x0e
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
+#define CHBTxIP 0x2 /* Channel B Tx IP */
+#define CHBRxIP 0x4 /* Channel B Rx IP */
+#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
+#define CHATxIP 0x10 /* Channel A Tx IP */
+#define CHARxIP 0x20 /* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10 (misc status bits) */
+#define ONLOOP 2 /* On loop */
+#define LOOPSEND 0x10 /* Loop sending */
+#define CLK2MIS 0x40 /* Two clocks missing */
+#define CLK1MIS 0x80 /* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* Misc macros */
+#define ZS_CLEARERR(channel) do { channel->control = ERR_RES; \
+ udelay(5); } while(0)
+
+#define ZS_CLEARSTAT(channel) do { channel->control = RES_EXT_INT; \
+ udelay(5); } while(0)
+
+#define ZS_CLEARFIFO(channel) do { volatile unsigned char garbage; \
+ garbage = channel->data; \
+ udelay(2); \
+ garbage = channel->data; \
+ udelay(2); \
+ garbage = channel->data; \
+ udelay(2); } while(0)
+
+#endif /* !(_SPARC_SERIAL_H) */
diff --git a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c
index cf06585ff..7b02fe60f 100644
--- a/drivers/sbus/sbus.c
+++ b/drivers/sbus/sbus.c
@@ -68,8 +68,14 @@ fill_sbus_device(int nd, struct linux_sbus_device *sbus_dev))
sparc_cpu_model == sun4m ||
sparc_cpu_model == sun4u) {
/* Ahh, we can determine the slot and offset */
- sbus_dev->slot = sbus_dev_slot(base);
- sbus_dev->offset = sbus_dev_offset(base);
+ if(sparc_cpu_model == sun4u) {
+ /* A bit tricky on the SYSIO. */
+ sbus_dev->slot = sbus_dev->reg_addrs[0].which_io;
+ sbus_dev->offset = sbus_dev_offset(base);
+ } else {
+ sbus_dev->slot = sbus_dev_slot(base);
+ sbus_dev->offset = sbus_dev_offset(base);
+ }
} else { /* Grrr, gotta do calculations to fix things up */
sbus_dev->slot = sbus_dev->reg_addrs[0].which_io;
sbus_dev->offset = base;
@@ -108,6 +114,18 @@ no_regs:
sbus_dev->num_irqs = 0;
} else {
sbus_dev->num_irqs = 1;
+ if(sbus_dev->irqs[0].pri < 0x20) {
+ int old_irq = sbus_dev->irqs[0].pri;
+
+ /* Need to do special SLOT fixups in this case. */
+#if 0 /* DEBUGGING */
+ printk("SBUS[%x:%lx]: INO fixup from [%x] to [%x]\n",
+ sbus_dev->slot, sbus_dev->offset,
+ old_irq, old_irq + (sbus_dev->slot * 8));
+#endif
+ sbus_dev->irqs[0].pri =
+ (old_irq + (sbus_dev->slot * 8));
+ }
}
} else {
len = prom_getproperty(nd, "intr", (void *)sbus_dev->irqs,
@@ -212,6 +230,8 @@ sbus_do_child_siblings(unsigned long memory_start, int start_node,
return memory_start;
}
+/* #define E3000_DEBUG */
+
__initfunc(unsigned long
sbus_init(unsigned long memory_start, unsigned long memory_end))
{
@@ -221,6 +241,9 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
struct linux_sbus_device *this_dev;
int num_sbus = 0; /* How many did we find? */
+#ifdef E3000_DEBUG
+ prom_printf("sbus_init: Radek, record following output for me. -DaveM\n");
+#endif
memory_start = ((memory_start + 7) & (~7));
topnd = prom_getchild(prom_root_node);
@@ -228,11 +251,15 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
/* Finding the first sbus is a special case... */
iommund = 0;
if(sparc_cpu_model == sun4u) {
- /* IOMMU "hides" inside SBUS/SYSIO node. */
- iommund = nd = prom_searchsiblings(topnd, "sbus");
+ nd = prom_searchsiblings(topnd, "sbus");
if(nd == 0) {
+#ifdef CONFIG_PCI
+ printk("SBUS: No SBUS's found.\n");
+ return sun_console_init(memory_start);
+#else
prom_printf("YEEE, UltraSparc sbus not found\n");
prom_halt();
+#endif
}
} else if(sparc_cpu_model == sun4d) {
if((iommund = prom_searchsiblings(topnd, "io-unit")) == 0 ||
@@ -249,6 +276,9 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
}
}
+#ifdef E3000_DEBUG
+ prom_printf("sbus_init: 1st sbus node(%x)\n", nd);
+#endif
/* Ok, we've found the first one, allocate first SBus struct
* and place in chain.
*/
@@ -257,16 +287,34 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
sbus->next = 0;
this_sbus=nd;
- /* Have IOMMU will travel. XXX grrr - this should be per sbus... */
- if(iommund) {
- if (sparc_cpu_model == sun4d)
- iommu_sun4d_init(this_sbus, sbus);
- else
- memory_start = iommu_init(iommund, memory_start, memory_end, sbus);
+ if(sparc_cpu_model != sun4u)
+ /* Have IOMMU will travel.
+ *
+ * XXX This should be per sbus on sun4d...
+ */
+ if(iommund) {
+ if (sparc_cpu_model == sun4d)
+ iommu_sun4d_init(this_sbus, sbus);
+ else
+ memory_start = iommu_init(iommund,
+ memory_start, memory_end,
+ sbus);
}
/* Loop until we find no more SBUS's */
while(this_sbus) {
+ /* IOMMU hides inside SBUS/SYSIO prom node on Ultra. */
+#ifdef E3000_DEBUG
+ prom_printf("sbus%d: [ii()", num_sbus);
+#endif
+ if(sparc_cpu_model == sun4u)
+ memory_start = iommu_init(this_sbus,
+ memory_start, memory_end,
+ sbus);
+
+#ifdef E3000_DEBUG
+ prom_printf("1");
+#endif
printk("sbus%d: ", num_sbus);
sbus_clock = prom_getint(this_sbus, "clock-frequency");
if(sbus_clock==-1) sbus_clock = (25*1000*1000);
@@ -279,9 +327,15 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
strcpy(sbus->prom_name, lbuf);
sbus->clock_freq = sbus_clock;
+#ifdef E3000_DEBUG
+ prom_printf("psri()");
+#endif
prom_sbus_ranges_init (iommund, sbus);
sbus_devs = prom_getchild(this_sbus);
+#ifdef E3000_DEBUG
+ prom_printf("chld(%x)", sbus_devs);
+#endif
sbus->devices = (struct linux_sbus_device *) memory_start;
memory_start += sizeof(struct linux_sbus_device);
@@ -289,6 +343,9 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
this_dev = sbus->devices;
this_dev->next = 0;
+#ifdef E3000_DEBUG
+ prom_printf("fsd()");
+#endif
fill_sbus_device(sbus_devs, this_dev);
this_dev->my_bus = sbus;
@@ -298,8 +355,14 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
this_dev->child = (struct linux_sbus_device *) memory_start;
memory_start += sizeof(struct linux_sbus_device);
/* Fill it */
+#ifdef E3000_DEBUG
+ prom_printf("fsd(chld)");
+#endif
fill_sbus_device(prom_getchild(sbus_devs), this_dev->child);
this_dev->child->my_bus = sbus;
+#ifdef E3000_DEBUG
+ prom_printf("sdcs()");
+#endif
memory_start = sbus_do_child_siblings(memory_start,
prom_getchild(sbus_devs),
this_dev->child,
@@ -308,6 +371,9 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
this_dev->child = 0;
}
+#ifdef E3000_DEBUG
+ prom_printf("2");
+#endif
while((sbus_devs = prom_getsibling(sbus_devs)) != 0) {
/* Allocate device node */
this_dev->next = (struct linux_sbus_device *) memory_start;
@@ -316,6 +382,9 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
this_dev->next=0;
/* Fill it */
+#ifdef E3000_DEBUG
+ prom_printf("fsd()");
+#endif
fill_sbus_device(sbus_devs, this_dev);
this_dev->my_bus = sbus;
@@ -327,9 +396,15 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
memory_start += sizeof(struct linux_sbus_device);
/* Fill it */
+#ifdef E3000_DEBUG
+ prom_printf("fsd()");
+#endif
fill_sbus_device(prom_getchild(sbus_devs),
this_dev->child);
this_dev->child->my_bus = sbus;
+#ifdef E3000_DEBUG
+ prom_printf("sdcs()");
+#endif
memory_start = sbus_do_child_siblings(
memory_start,
prom_getchild(sbus_devs),
@@ -340,14 +415,26 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
}
}
+#ifdef E3000_DEBUG
+ prom_printf("di()");
+#endif
memory_start = dvma_init(sbus, memory_start);
num_sbus++;
+#ifdef E3000_DEBUG
+ prom_printf("3, off to next sbus\n");
+#endif
if(sparc_cpu_model == sun4u) {
this_sbus = prom_getsibling(this_sbus);
+#ifdef E3000_DEBUG
+ prom_printf("sbus_init: sibling(%x), ", this_sbus);
+#endif
if(!this_sbus)
break;
this_sbus = prom_searchsiblings(this_sbus, "sbus");
+#ifdef E3000_DEBUG
+ prom_printf("next sbus node(%x),", this_sbus);
+#endif
} else if(sparc_cpu_model == sun4d) {
iommund = prom_getsibling(iommund);
if(!iommund) break;
@@ -360,6 +447,9 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
this_sbus = prom_searchsiblings(this_sbus, "sbus");
}
if(this_sbus) {
+#ifdef E3000_DEBUG
+ prom_printf(" scanning another sbus\n");
+#endif
sbus->next = (struct linux_sbus *) memory_start;
memory_start += sizeof(struct linux_sbus);
sbus = sbus->next;
@@ -368,7 +458,13 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
break;
}
} /* while(this_sbus) */
+#ifdef E3000_DEBUG
+ prom_printf("sbus_init: No more sbus's, calling sun_console_init()\n");
+#endif
memory_start = sun_console_init(memory_start); /* whee... */
+#ifdef E3000_DEBUG
+ prom_printf("sbus_init: back from sun_console_init()\n");
+#endif
#ifdef CONFIG_SUN_OPENPROMIO
openprom_init();
#endif
@@ -388,8 +484,10 @@ sbus_init(unsigned long memory_start, unsigned long memory_end))
#ifdef __sparc_v9__
if (sparc_cpu_model == sun4u) {
extern void sun4u_start_timers(void);
+ extern void clock_probe(void);
sun4u_start_timers();
+ clock_probe();
}
#endif
return memory_start;
diff --git a/drivers/scsi/53c7,8xx.c b/drivers/scsi/53c7,8xx.c
index 3d28dae2f..5f1a4d8da 100644
--- a/drivers/scsi/53c7,8xx.c
+++ b/drivers/scsi/53c7,8xx.c
@@ -678,6 +678,7 @@ static const unsigned char wdtr_message[] = {
* Returns : NULL on failure, pointer to host structure on success.
*/
+#if 0
static struct Scsi_Host *
find_host (int host) {
struct Scsi_Host *h;
@@ -767,6 +768,7 @@ request_disconnect (int host, int on_or_off) {
hostdata->options &= ~OPTION_DISCONNECT;
return 0;
}
+#endif
/*
* Function : static void NCR53c7x0_driver_init (struct Scsi_Host *host)
@@ -5468,6 +5470,7 @@ print_insn (struct Scsi_Host *host, const u32 *insn,
* Returns : char * representation of state, "unknown" on error.
*/
+#if 0
static const char *
ncr_state (int state) {
switch (state) {
@@ -5479,6 +5482,7 @@ ncr_state (int state) {
default: return "unknown";
}
}
+#endif
/*
* Function : int NCR53c7xx_abort (Scsi_Cmnd *cmd)
diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c
index 7e5038efd..9119be7f3 100644
--- a/drivers/scsi/BusLogic.c
+++ b/drivers/scsi/BusLogic.c
@@ -27,8 +27,8 @@
*/
-#define BusLogic_DriverVersion "2.0.9"
-#define BusLogic_DriverDate "29 March 1997"
+#define BusLogic_DriverVersion "2.0.10"
+#define BusLogic_DriverDate "11 August 1997"
#include <linux/module.h>
@@ -43,7 +43,6 @@
#include <linux/stat.h>
#include <linux/pci.h>
#include <linux/bios32.h>
-#include <linux/init.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/system.h>
@@ -223,8 +222,7 @@ static void BusLogic_UnregisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
Host Adapter.
*/
-__initfunc(static boolean
-BusLogic_CreateMailboxes(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_CreateMailboxes(BusLogic_HostAdapter_T *HostAdapter)
{
/*
FlashPoint Host Adapters do not use Outgoing and Incoming Mailboxes.
@@ -306,8 +304,7 @@ static boolean BusLogic_CreateCCB(BusLogic_HostAdapter_T *HostAdapter)
BusLogic_CreateInitialCCBs allocates the initial CCBs for Host Adapter.
*/
-__initfunc(static boolean
-BusLogic_CreateInitialCCBs(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_CreateInitialCCBs(BusLogic_HostAdapter_T *HostAdapter)
{
int Allocated;
for (Allocated = 0; Allocated < HostAdapter->InitialCCBs; Allocated++)
@@ -420,8 +417,8 @@ static void BusLogic_DeallocateCCB(BusLogic_CCB_T *CCB)
structure for Host Adapter.
*/
-__initfunc(static boolean
-BusLogic_CreateTargetDeviceStatistics(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_CreateTargetDeviceStatistics(BusLogic_HostAdapter_T
+ *HostAdapter)
{
HostAdapter->TargetDeviceStatistics =
(BusLogic_TargetDeviceStatistics_T *)
@@ -508,13 +505,14 @@ static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
Wait for the Host Adapter Ready bit to be set and the Command/Parameter
Register Busy bit to be reset in the Status Register.
*/
- TimeoutCounter = loops_per_sec >> 3;
+ TimeoutCounter = 10000;
while (--TimeoutCounter >= 0)
{
StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
if (StatusRegister.Bits.HostAdapterReady &&
!StatusRegister.Bits.CommandParameterRegisterBusy)
break;
+ udelay(100);
}
if (TimeoutCounter < 0)
{
@@ -590,11 +588,11 @@ static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
case BusLogic_InquireInstalledDevicesID8to15:
case BusLogic_InquireTargetDevices:
/* Approximately 60 seconds. */
- TimeoutCounter = loops_per_sec << 2;
+ TimeoutCounter = 60*10000;
break;
default:
/* Approximately 1 second. */
- TimeoutCounter = loops_per_sec >> 4;
+ TimeoutCounter = 10000;
break;
}
/*
@@ -614,6 +612,7 @@ static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
else BusLogic_ReadDataInRegister(HostAdapter);
if (OperationCode == BusLogic_FetchHostAdapterLocalRAM &&
StatusRegister.Bits.HostAdapterReady) break;
+ udelay(100);
}
if (TimeoutCounter < 0)
{
@@ -711,7 +710,7 @@ Done:
only from the list of standard BusLogic MultiMaster ISA I/O Addresses.
*/
-static inline void BusLogic_InitializeProbeInfoListISA(void)
+static void BusLogic_InitializeProbeInfoListISA(void)
{
int StandardAddressIndex;
/*
@@ -749,9 +748,8 @@ static inline void BusLogic_InitializeProbeInfoListISA(void)
of increasing PCI Bus and Device Number.
*/
-__initfunc(static void
-BusLogic_SortProbeInfo(BusLogic_ProbeInfo_T *ProbeInfoList,
- int ProbeInfoCount))
+static void BusLogic_SortProbeInfo(BusLogic_ProbeInfo_T *ProbeInfoList,
+ int ProbeInfoCount)
{
int LastInterchange = ProbeInfoCount-1, Bound, j;
while (LastInterchange > 0)
@@ -785,12 +783,12 @@ BusLogic_SortProbeInfo(BusLogic_ProbeInfo_T *ProbeInfoList,
I/O Addresses. It returns the number of PCI MultiMaster Host Adapters found.
*/
-__initfunc(static int BusLogic_InitializeMultiMasterProbeInfo(void))
+static int BusLogic_InitializeMultiMasterProbeInfo(void)
{
boolean StandardAddressSeen[BusLogic_ISA_StandardAddressesCount];
BusLogic_ProbeInfo_T *PrimaryProbeInfo =
&BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount];
- int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount;
+ int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount + 1;
int NonPrimaryPCIMultiMasterCount = 0, PCIMultiMasterCount = 0;
boolean ForceBusDeviceScanningOrder = false;
boolean ForceBusDeviceScanningOrderChecked = false;
@@ -1016,7 +1014,7 @@ __initfunc(static int BusLogic_InitializeMultiMasterProbeInfo(void))
number of FlashPoint Host Adapters found.
*/
-__initfunc(static int BusLogic_InitializeFlashPointProbeInfo(void))
+static int BusLogic_InitializeFlashPointProbeInfo(void)
{
int FlashPointIndex = BusLogic_ProbeInfoCount, FlashPointCount = 0;
unsigned char Bus, DeviceFunction, IRQ_Channel;
@@ -1123,7 +1121,7 @@ __initfunc(static int BusLogic_InitializeFlashPointProbeInfo(void))
particular probe order.
*/
-static inline void BusLogic_InitializeProbeInfoList(void)
+static void BusLogic_InitializeProbeInfoList(void)
{
/*
If BusLogic_Setup has provided an I/O Address probe list, do not override
@@ -1232,8 +1230,7 @@ static boolean BusLogic_Failure(BusLogic_HostAdapter_T *HostAdapter,
BusLogic_ProbeHostAdapter probes for a BusLogic Host Adapter.
*/
-__initfunc(static boolean
-BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
{
BusLogic_StatusRegister_T StatusRegister;
BusLogic_InterruptRegister_T InterruptRegister;
@@ -1245,13 +1242,16 @@ BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter))
{
FlashPoint_Info_T *FlashPointInfo = (FlashPoint_Info_T *)
scsi_init_malloc(sizeof(FlashPoint_Info_T), GFP_ATOMIC);
+ int Retries = 10;
if (FlashPointInfo == NULL)
return BusLogic_Failure(HostAdapter, "ALLOCATING FLASHPOINT INFO");
FlashPointInfo->BaseAddress = HostAdapter->IO_Address;
FlashPointInfo->IRQ_Channel = HostAdapter->IRQ_Channel;
FlashPointInfo->Present = false;
- if (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 &&
- FlashPointInfo->Present))
+ while (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 &&
+ FlashPointInfo->Present) &&
+ --Retries >= 0) ;
+ if (!FlashPointInfo->Present)
{
scsi_init_free((char *) FlashPointInfo, sizeof(FlashPoint_Info_T));
BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at "
@@ -1326,7 +1326,7 @@ static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
*HostAdapter)
{
BusLogic_StatusRegister_T StatusRegister;
- int TimeoutCounter = loops_per_sec;
+ int TimeoutCounter;
/*
FlashPoint Host Adapters are Hard Reset by the FlashPoint SCCB Manager.
*/
@@ -1349,10 +1349,12 @@ static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
/*
Wait until Diagnostic Active is set in the Status Register.
*/
+ TimeoutCounter = 5*10000;
while (--TimeoutCounter >= 0)
{
StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
if (StatusRegister.Bits.DiagnosticActive) break;
+ udelay(100);
}
if (BusLogic_GlobalOptions.Bits.TraceHardReset)
BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Active, "
@@ -1368,10 +1370,12 @@ static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
/*
Wait until Diagnostic Active is reset in the Status Register.
*/
+ TimeoutCounter = 10*10000;
while (--TimeoutCounter >= 0)
{
StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
if (!StatusRegister.Bits.DiagnosticActive) break;
+ udelay(100);
}
if (BusLogic_GlobalOptions.Bits.TraceHardReset)
BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Completed, "
@@ -1382,6 +1386,7 @@ static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
Wait until at least one of the Diagnostic Failure, Host Adapter Ready,
or Data In Register Ready bits is set in the Status Register.
*/
+ TimeoutCounter = 10000;
while (--TimeoutCounter >= 0)
{
StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
@@ -1389,6 +1394,7 @@ static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
StatusRegister.Bits.HostAdapterReady ||
StatusRegister.Bits.DataInRegisterReady)
break;
+ udelay(100);
}
if (BusLogic_GlobalOptions.Bits.TraceHardReset)
BusLogic_Notice("BusLogic_HardReset(0x%X): Host Adapter Ready, "
@@ -1427,8 +1433,7 @@ static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
Host Adapter. It also determines the IRQ Channel for non-PCI Host Adapters.
*/
-__initfunc(static boolean
-BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
{
BusLogic_Configuration_T Configuration;
BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
@@ -1491,8 +1496,8 @@ BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter))
from Host Adapter and initializes the Host Adapter structure.
*/
-__initfunc(static boolean
-BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
+ *HostAdapter)
{
BusLogic_BoardID_T BoardID;
BusLogic_Configuration_T Configuration;
@@ -2005,8 +2010,8 @@ Common:
Host Adapter.
*/
-__initfunc(static boolean
-BusLogic_ReportHostAdapterConfiguration(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_ReportHostAdapterConfiguration(BusLogic_HostAdapter_T
+ *HostAdapter)
{
unsigned short AllTargetsMask = (1 << HostAdapter->MaxTargetDevices) - 1;
unsigned short SynchronousPermitted, FastPermitted;
@@ -2231,8 +2236,7 @@ BusLogic_ReportHostAdapterConfiguration(BusLogic_HostAdapter_T *HostAdapter))
Host Adapter.
*/
-__initfunc(static boolean
-BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter)
{
BusLogic_HostAdapter_T *FirstHostAdapter =
BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel];
@@ -2318,8 +2322,7 @@ static void BusLogic_ReleaseResources(BusLogic_HostAdapter_T *HostAdapter)
interrupts do not get through as a result.
*/
-__initfunc(static boolean
-BusLogic_TestInterrupts(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_TestInterrupts(BusLogic_HostAdapter_T *HostAdapter)
{
unsigned int InitialInterruptCount, FinalInterruptCount;
int TestCount = 5, i;
@@ -2608,10 +2611,9 @@ static boolean BusLogic_TargetDeviceInquiry(BusLogic_HostAdapter_T
through explicit acquisition and release of the Host Adapter's Lock.
*/
-__initfunc(static void
-BusLogic_InitializeHostStructure(BusLogic_HostAdapter_T
- *HostAdapter,
- SCSI_Host_T *Host))
+static void BusLogic_InitializeHostStructure(BusLogic_HostAdapter_T
+ *HostAdapter,
+ SCSI_Host_T *Host)
{
Host->max_id = HostAdapter->MaxTargetDevices;
Host->max_lun = HostAdapter->MaxLogicalUnits;
@@ -2682,7 +2684,7 @@ static void BusLogic_SelectQueueDepths(SCSI_Host_T *Host,
registered.
*/
-__initfunc(int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate))
+int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)
{
int BusLogicHostAdapterCount = 0, CommandLineEntryIndex = 0, ProbeIndex;
char *MessageBuffer = NULL;
@@ -3007,8 +3009,6 @@ static void BusLogic_ScanIncomingMailboxes(BusLogic_HostAdapter_T *HostAdapter)
"Incoming Mailbox\n", HostAdapter,
CCB->SerialNumber, CCB->Status);
}
- else BusLogic_Warning("Aborted CCB #%ld to Target %d Not Found\n",
- HostAdapter, CCB->SerialNumber, CCB->TargetID);
NextIncomingMailbox->CompletionCode = BusLogic_IncomingMailboxFree;
if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox)
NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
diff --git a/drivers/scsi/ChangeLog.ncr53c8xx b/drivers/scsi/ChangeLog.ncr53c8xx
index edc8f395c..ecfd09676 100644
--- a/drivers/scsi/ChangeLog.ncr53c8xx
+++ b/drivers/scsi/ChangeLog.ncr53c8xx
@@ -1,3 +1,98 @@
+Thu Aug 23 23:43 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.5a
+ - Update Configure.help for inclusion in linux-2.1.51/2/3
+ - Use BASE_2 address from PCI config space instead of some
+ IO register for getting the on-board SRAM bus address.
+ - Remove error testing of pcibios_read/write functions.
+ These functions are intended to be used for successfully
+ detected PCI devices. Expecting error condition from them
+ is nothing but paranoia.
+
+Thu Aug 21 23:00 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.5
+ - 53C860 chip support fix.
+ - Move the 'host_status' to the last DWORD of the CCB header.
+ This header is copied back by the script processor. This
+ guarantees that the header is entirely copied back over
+ the PCI when the CPU completes a CCB.
+ - (re)read ISTAT prior to scanning CCBs for completion. This
+ ensure that any posted buffer are flushed prior CCBs scan.
+ - Support for BIG ENDIAN cpu. Added by Cort <cort@cs.nmt.edu>.
+ Initial patch did'nt support disconnections and tagged commands.
+ I've completed the patch and it seems that all is ok now.
+ Only some powerpc under 2.1.X is supported for the moment.
+ - Misc. trivial fixes and cleanups.
+
+Sat July 26 18:00 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.4
+ Several clean-ups:
+ - Asynchronous pre-scaler calculation.
+ Synchronous divisor calculation.
+ - Use FE_ as feature identifier prefix instead of _F_.
+ - Change 'ns_sync' identifier to "minsync".
+ - Some others.
+ Apply some SPI2-R12 recommendations.
+ - Use Slow, Fast-10, Fast-20, Fast-40 SCSI instead of SCSI-2,
+ FAST SCSI-2, ULTRA, ULTRA-2.
+ - Reset the SCSI on bus mode change.
+
+Wed July 02 22:58 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.3c
+ - Add define SCSI_NCR_PCI_FIX_UP_SUPPORT for conditionnal compilation
+ of the corresponding pci fix-up code when a small driver is needed.
+ - Use "ncr53c8xx" as driver name for both request_irq() and
+ request_region(). Using different names confused 'lsdev'.
+ (Suggestion sent by Henrik Storner).
+
+Wed June 24 22:08 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.3b
+ - Print an error message on unexpected boot command line option.
+ - Switch to asynchronous data transfer mode after SCSI wide
+ negotiation.
+
+Wed June 14 22:00 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.3a
+ - Add PCI LATENCY TIMER fixup code.
+ Increase it if necessary according to burst size.
+ Boot option bit : 'pcifix:4'
+ - On phase mismatch, calculate residual data size for all OUTPUT
+ phases. That's only required for interrupted DATA OUT phase, but
+ this information is usefull for problem solving.
+ - Add KERN_INFO to some messages printed to the log.
+ (Patch sent by Wolfram Kleff).
+
+Tue June 02 22:30 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.3
+ - NvRAM support code slightly improved (I think):
+ Use IO or MMIO according to driver setup for reading the NvRAM.
+ Use structures for NvRAM data instead of raw data.
+ - Prevent from queuing more than 1 command to the scsi SCRIPT with
+ negotiation attached when tagged command queueing is enabled.
+ - Fix-up for old 53C8XX chips that support PCI READ LINE but not
+ CACHE LINE SIZE. If the cache line size is unknown, set burst
+ to 8 dwords and disable READ LINE, otherwise set burst max to
+ the cache line size value.
+
+Sat May 24 12:30 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.2c (for linux-2.1.40)
+ - Remove reference to 'x86' symbol when MODULE is defined, since this
+ symbol is not exported for module loading.
+ The value of 'x86' is used for fixing up the PCI CACHE LINE SIZE
+ configuration register.
+ - Bytes/words read one bit at a time from the serial NVRAM were'nt
+ initialized with zero.
+ - Some comments added. Minor cosmetic changes.
+
+Mon May 19 20:30 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.2b
+ - Patch for NVRAM support by Richard Waltham applied.
+ The code detects Symbios NVRAM format and Tekram NVRAM format.
+ This enhancement allows to get hosts and devices user set up
+ from the NVRAM.
+ - Use the NVRAM contents when present to initialize user definable
+ target parameters.
+ - Update the README file.
+
Sun May 11 22:30 1997 Gerard Roudier (groudier@club-internet.fr)
* revision 2.1b
- Cosmetic changes.
diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in
index 3f24e681f..a137680cb 100644
--- a/drivers/scsi/Config.in
+++ b/drivers/scsi/Config.in
@@ -24,10 +24,13 @@ dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
bool ' Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y
- int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8
+ dep_tristate ' Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N
+ if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then
+ int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8
+ fi
bool ' Enable SCB paging' CONFIG_AIC7XXX_PAGE_ENABLE N
bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
- int ' delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15
+ int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15
fi
dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
@@ -62,10 +65,10 @@ if [ "$CONFIG_PCI" = "y" ]; then
bool ' allow DISCONNECT' CONFIG_SCSI_NCR53C7xx_DISCONNECT
fi
fi
-#if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then
-if [ "$CONFIG_SNI_RM200_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then
+if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then
dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI
if [ "$CONFIG_SCSI_NCR53C8XX" != "n" ]; then
+ bool ' detect and read serial NVRAMs' CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
bool ' enable tagged command queueing' CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
bool ' use normal IO' CONFIG_SCSI_NCR53C8XX_IOMAPPED
int ' maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 4
@@ -81,8 +84,8 @@ fi
if [ "$CONFIG_MCA" = "y" ]; then
dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI
fi
-if [ "$CONFIG_PNP_PARPORT" != "n" ]; then
- dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PNP_PARPORT
+if [ "$CONFIG_PARPORT" != "n" ]; then
+ dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PARPORT
if [ "$CONFIG_SCSI_PPA" != "n" ]; then
int ' Pedantic EPP-checking' CONFIG_SCSI_PPA_HAVE_PEDANTIC 2 0 3
int ' EPP timeout' CONFIG_SCSI_PPA_EPP_TIME 128
@@ -105,4 +108,11 @@ dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI
fi
dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR $CONFIG_SCSI
#dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI
+if [ "$CONFIG_PPC" = "y" ]; then
+ dep_tristate 'MESH (Power Mac internal SCSI) support' CONFIG_SCSI_MESH $CONFIG_SCSI
+ if [ "$CONFIG_SCSI_MESH" != "n" ]; then
+ int ' maximum synchronous transfer rate (MB/s) (0 = async)' CONFIG_SCSI_MESH_SYNC_RATE 5
+ fi
+ dep_tristate '53C94 (Power Mac external SCSI) support' CONFIG_SCSI_MAC53C94 $CONFIG_SCSI
+fi
endmenu
diff --git a/drivers/scsi/FlashPoint.c b/drivers/scsi/FlashPoint.c
index 7c74868a2..3b9696920 100644
--- a/drivers/scsi/FlashPoint.c
+++ b/drivers/scsi/FlashPoint.c
@@ -31,6 +31,18 @@
#endif
+/*
+ FlashPoint support is only available for the Intel x86 Architecture.
+*/
+
+#ifndef __i386__
+
+#undef CONFIG_SCSI_OMIT_FLASHPOINT
+#define CONFIG_SCSI_OMIT_FLASHPOINT
+
+#endif
+
+
#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
@@ -175,9 +187,9 @@
*
* Description: Common shared global defines.
*
- * $Date: 1996/09/04 01:26:13 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.11 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
#ifndef __GLOBALS_H__
@@ -414,9 +426,9 @@ extern void OS_OutPortLong(unsigned long ioport, unsigned long val);
* Description: Common shared SCCB Interface defines and SCCB
* Manager specifics defines.
*
- * $Date: 1996/10/24 23:09:33 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.14 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -732,9 +744,9 @@ typedef struct _SCCB {
* Description: This module contains SCCB/UCB Manager implementation
* specific stuff.
*
- * $Date: 1996/11/13 18:34:22 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.10 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -889,9 +901,9 @@ typedef struct _SCCB {
*
* Description: Definitions for Target related structures
*
- * $Date: 1996/12/11 22:06:20 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.9 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -1088,9 +1100,9 @@ typedef struct SCCBscam_info {
*
* Description: Register definitions for HARPOON ASIC.
*
- * $Date: 1996/11/13 18:32:57 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.4 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -1220,9 +1232,9 @@ typedef struct SCCBscam_info {
*
* Description: Definitions for EEPROM related structures
*
- * $Date: 1996/11/13 18:28:39 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.4 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -1305,9 +1317,9 @@ typedef struct SCCBscam_info {
*
* Description: Register definitions for HARPOON ASIC.
*
- * $Date: 1997/01/31 02:14:28 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.6 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -2327,7 +2339,7 @@ void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
extern unsigned int SccbGlobalFlags;
-#ident "$Id: sccb.c 1.17 1997/02/11 21:06:41 mohan Exp $"
+#ident "$Id: FlashPoint.c,v 1.1.1.1 1997/06/01 03:17:39 ralf Exp $"
/*----------------------------------------------------------------------
*
*
@@ -2341,9 +2353,9 @@ extern unsigned int SccbGlobalFlags;
* Description: Functions relating to handling of the SCCB interface
* between the device driver and the HARPOON.
*
- * $Date: 1997/02/11 21:06:41 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.17 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -5335,7 +5347,7 @@ void Debug_Load(UCHAR p_card, UCHAR p_bug_data)
}
#endif
-#ident "$Id: sccb_dat.c 1.9 1997/01/31 02:12:58 mohan Exp $"
+#ident "$Id: FlashPoint.c,v 1.1.1.1 1997/06/01 03:17:39 ralf Exp $"
/*----------------------------------------------------------------------
*
*
@@ -5349,9 +5361,9 @@ void Debug_Load(UCHAR p_card, UCHAR p_bug_data)
* Description: Functions relating to handling of the SCCB interface
* between the device driver and the HARPOON.
*
- * $Date: 1997/01/31 02:12:58 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.9 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -5406,7 +5418,7 @@ UCHAR debug_int[MAX_CARDS][debug_size];
UCHAR debug_index[MAX_CARDS];
UCHAR reserved_1[3];
#endif
-#ident "$Id: scsi.c 1.19 1997/01/31 02:08:14 mohan Exp $"
+#ident "$Id: FlashPoint.c,v 1.1.1.1 1997/06/01 03:17:39 ralf Exp $"
/*----------------------------------------------------------------------
*
*
@@ -5421,9 +5433,9 @@ UCHAR reserved_1[3];
* selection/reselection, sync negotiation, message-in
* decoding.
*
- * $Date: 1997/01/31 02:08:14 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.19 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -7377,7 +7389,7 @@ void sinits(PSCCB p_sccb, UCHAR p_card)
}
-#ident "$Id: phase.c 1.11 1997/01/31 02:08:49 mohan Exp $"
+#ident "$Id: FlashPoint.c,v 1.1.1.1 1997/06/01 03:17:39 ralf Exp $"
/*----------------------------------------------------------------------
*
*
@@ -7392,9 +7404,9 @@ void sinits(PSCCB p_sccb, UCHAR p_card)
* the target asserts request (and the automation is not
* enabled to handle the situation).
*
- * $Date: 1997/01/31 02:08:49 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.11 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -8126,7 +8138,7 @@ void phaseBusFree(ULONG port, UCHAR p_card)
-#ident "$Id: automate.c 1.14 1997/01/31 02:11:46 mohan Exp $"
+#ident "$Id: FlashPoint.c,v 1.1.1.1 1997/06/01 03:17:39 ralf Exp $"
/*----------------------------------------------------------------------
*
*
@@ -8140,9 +8152,9 @@ void phaseBusFree(ULONG port, UCHAR p_card)
* Description: Functions relating to programming the automation of
* the HARPOON.
*
- * $Date: 1997/01/31 02:11:46 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.14 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -8518,7 +8530,7 @@ void autoCmdCmplt(ULONG p_port, UCHAR p_card)
queueCmdComplete(&BL_Card[p_card], currSCCB, p_card);
}
-#ident "$Id: busmstr.c 1.8 1997/01/31 02:10:27 mohan Exp $"
+#ident "$Id: FlashPoint.c,v 1.1.1.1 1997/06/01 03:17:39 ralf Exp $"
/*----------------------------------------------------------------------
*
*
@@ -8531,9 +8543,9 @@ void autoCmdCmplt(ULONG p_port, UCHAR p_card)
*
* Description: Functions to start, stop, and abort BusMaster operations.
*
- * $Date: 1997/01/31 02:10:27 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.8 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -9210,7 +9222,7 @@ void hostDataXferRestart(PSCCB currSCCB)
currSCCB->Sccb_XferCnt = currSCCB->DataLength - currSCCB->Sccb_ATC;
}
}
-#ident "$Id: scam.c 1.16 1997/01/31 02:11:12 mohan Exp $"
+#ident "$Id: FlashPoint.c,v 1.1.1.1 1997/06/01 03:17:39 ralf Exp $"
/*----------------------------------------------------------------------
*
*
@@ -9225,9 +9237,9 @@ void hostDataXferRestart(PSCCB currSCCB)
* and the determination of the SCSI IDs to be assigned
* to all perspective SCSI targets.
*
- * $Date: 1997/01/31 02:11:12 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.16 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -10350,7 +10362,7 @@ void scsavdi(UCHAR p_card, ULONG p_port)
utilEEWrite(p_port, sum_data, EEPROM_CHECK_SUM/2);
utilEEWriteOnOff(p_port,0); /* Turn off write access */
}
-#ident "$Id: diagnose.c 1.9 1997/01/31 02:09:48 mohan Exp $"
+#ident "$Id: FlashPoint.c,v 1.1.1.1 1997/06/01 03:17:39 ralf Exp $"
/*----------------------------------------------------------------------
*
*
@@ -10364,9 +10376,9 @@ void scsavdi(UCHAR p_card, ULONG p_port)
* Description: Diagnostic funtions for testing the integrity of
* the HARPOON.
*
- * $Date: 1997/01/31 02:09:48 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.9 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
@@ -10795,7 +10807,7 @@ void DiagEEPROM(ULONG p_port)
}
-#ident "$Id: utility.c 1.22 1997/01/31 02:12:23 mohan Exp $"
+#ident "$Id: FlashPoint.c,v 1.1.1.1 1997/06/01 03:17:39 ralf Exp $"
/*----------------------------------------------------------------------
*
*
@@ -10809,9 +10821,9 @@ void DiagEEPROM(ULONG p_port)
* Description: Utility functions relating to queueing and EEPROM
* manipulation and any other garbage functions.
*
- * $Date: 1997/01/31 02:12:23 $
+ * $Date: 1997/06/01 03:17:39 $
*
- * $Revision: 1.22 $
+ * $Revision: 1.1.1.1 $
*
*----------------------------------------------------------------------*/
/*#include <globals.h>*/
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index dcb47e47e..9fb808309 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -281,6 +281,22 @@ else
endif
endif
+ifeq ($(CONFIG_SCSI_MESH),y)
+L_OBJS += mesh.o
+else
+ ifeq ($(CONFIG_SCSI_MESH),m)
+ M_OBJS += mesh.o
+ endif
+endif
+
+ifeq ($(CONFIG_SCSI_MAC53C94),y)
+L_OBJS += mac53c94.o
+else
+ ifeq ($(CONFIG_SCSI_MAC53C94),m)
+ M_OBJS += mac53c94.o
+ endif
+endif
+
ifeq ($(CONFIG_SCSI_DEBUG),y)
L_OBJS += scsi_debug.o
else
@@ -435,10 +451,6 @@ aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h
seagate.o: seagate.c
$(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -DPARITY -c seagate.c
-# For debugging, use the -g flag
-53c7,8xx.o : 53c7,8xx.c
- $(CC) $(CFLAGS) -g -c 53c7,8xx.c
-
53c8xx_d.h 53c8xx_u.h : 53c7,8xx.scr script_asm.pl
ln -sf 53c7,8xx.scr fake.c
$(CPP) -traditional -DCHIP=810 fake.c | grep -v '^#' | perl script_asm.pl
@@ -446,9 +458,6 @@ seagate.o: seagate.c
mv scriptu.h 53c8xx_u.h
rm fake.c
-53c7xx.o : 53c7xx_d.h 53c7xx.c
- $(CC) $(CFLAGS) -c 53c7xx.c
-
53c7xx_d.h 53c7xx_u.h : 53c7xx.scr script_asm.pl
ln -sf 53c7xx.scr fake.c
$(CPP) -traditional -DCHIP=710 fake.c | grep -v '^#' | perl -s script_asm.pl -ncr7x0_family
@@ -456,9 +465,6 @@ seagate.o: seagate.c
mv scriptu.h 53c7xx_u.h
rm fake.c
-ncr53c8xx.o : ncr53c8xx.c
- $(CC) $(CFLAGS) -c ncr53c8xx.c
-
scsi_mod.o: $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \
scsicam.o scsi_proc.o
$(LD) $(LD_RFLAG) -r -o $@ $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o
diff --git a/drivers/scsi/README.BusLogic b/drivers/scsi/README.BusLogic
index 6a66e4df6..a9962dbbc 100644
--- a/drivers/scsi/README.BusLogic
+++ b/drivers/scsi/README.BusLogic
@@ -1,10 +1,10 @@
BusLogic MultiMaster and FlashPoint SCSI Driver for Linux
- Version 2.0.9 for Linux 2.0
+ Version 2.0.10 for Linux 2.0
PRODUCTION RELEASE
- 29 March 1997
+ 11 August 1997
Leonard N. Zubkoff
Dandelion Digital
@@ -284,6 +284,14 @@ o PCI Slot Scanning Order
so as to recognize the host adapters in the same order as they are enumerated
by the host adapter's BIOS.
+o Mega-Transfers/Second
+
+ The driver reports on the synchronous transfer parameters negotiated between
+ the host adapter and target devices in units of "mega-transfers/second". For
+ wide devices, the unit of transfer is 16 bits if wide negotiation has been
+ successfully completed. Therefore, the total transfer rate to wide devices
+ will generally be twice the synchronous tranfer rate reported by the driver.
+
COMMAND LINE OPTIONS
@@ -372,16 +380,17 @@ substantially impact performance.
INSTALLATION
-This distribution was prepared for Linux kernel version 2.0.29, but should be
-compatible with 2.0.4 or any later 2.0 series kernel.
+This distribution was prepared for Linux kernel version 2.0.30, but should be
+compatible with 2.0.4 or any later 2.0 series kernel if BusLogic.patch is also
+applied.
To install the new BusLogic SCSI driver, you may use the following commands,
replacing "/usr/src" with wherever you keep your Linux kernel source tree:
cd /usr/src
- tar -xvzf BusLogic-2.0.9.tar.gz
+ tar -xvzf BusLogic-2.0.10.tar.gz
mv README.* LICENSE.* BusLogic.[ch] FlashPoint.c linux/drivers/scsi
- patch -p < BusLogic.patch
+ patch -p < BusLogic.patch # Only for kernels prior to 2.0.30
cd linux
make config
make depend
diff --git a/drivers/scsi/README.ncr53c8xx b/drivers/scsi/README.ncr53c8xx
index d93492a83..ab12ed995 100644
--- a/drivers/scsi/README.ncr53c8xx
+++ b/drivers/scsi/README.ncr53c8xx
@@ -4,7 +4,7 @@ Written by Gerard Roudier <groudier@club-internet.fr>
21 Rue Carnot
95170 DEUIL LA BARRE - FRANCE
-9 May 1997
+23 August 1997
===============================================================================
1. Introduction
@@ -28,6 +28,8 @@ Written by Gerard Roudier <groudier@club-internet.fr>
10.1 Syntax
10.2 Available arguments
10.3 Advised boot setup commands
+ 10.4 PCI configuration fix-up boot option
+ 10.5 Serial NVRAM support boot option
11. Some constants and flags of the ncr53c8xx.h header file
12. Installation
12.1 Provided files
@@ -40,6 +42,13 @@ Written by Gerard Roudier <groudier@club-internet.fr>
16. Synchonous transfer negotiation tables
16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
16.2 Synchronous timings for fast SCSI-2 53C8XX controllers
+17. Serial NVRAM support (by Richard Waltham)
+ 17.1 Features
+ 17.2 Symbios NVRAM layout
+ 17.3 Tekram NVRAM layout
+18. Support for Big Endian
+ 18.1 Big Endian CPU
+ 18.2 NCR chip in Big Endian mode of operations
===============================================================================
@@ -77,7 +86,7 @@ This short documentation only describes the features of the NCR53C8XX
driver, configuration parameters and control commands available
through the proc SCSI file system read / write operations.
-This driver has been tested OK with linux/i386 and Linux/Alpha.
+This driver has been tested OK with linux/i386, Linux/Alpha and Linux/PPC.
Latest driver version and patches are available at:
@@ -124,6 +133,7 @@ Chip SDMS BIOS Wide Ultra SCSI the driver the driver
Scatter / gather
Shared interrupt
Boot setup commands
+ Serial NVRAM: Symbios and Tekram formats
4. Memory mapped I/O versus normal I/O
@@ -412,35 +422,6 @@ Available commands:
If you select an error type, it will be triggered by the driver every
30 seconds.
-8.9 PCI configuration fix-up
-
- pcifix <option bits>
-
- Available option bits:
- 0x1: Set PCI cache-line size register if not set.
- 0x2: Set write and invalidate bit in PCI command register.
-
- Use 'pcifix:3' in order to allow the driver to fix both PCI features.
-
- These options only apply to new SYMBIOS chips 810A, 825A, 860 and 875
- and are only supported for Pentium and 486 class processors.
- Recent SYMBIOS 53C8XX scsi processors are able to use PCI read multiple
- and PCI write and invalidate commands. These features require the
- cache line size register to be properly set in the PCI configuration
- space of the chips. On the other hand, chips will use PCI write and
- invalidate commands only if the corresponding bit is set to 1 in the
- PCI command register.
-
- Not all PCI bioses set the PCI cache line register and the PCI write and
- invalidate bit in the PCI configuration space of 53C8XX chips.
- Optimized PCI accesses may be broken for some PCI/memory controllers or
- make problems with some PCI boards.
-
- This fix-up works flawlessly on my system.
- (MB Triton HX / 53C875 / 53C810A)
- I use these options at my own risks as you will do if you decide to
- use them too.
-
9. Configuration parameters
If the firmware of all your devices is perfect enough, all the
@@ -451,7 +432,10 @@ this feature after boot-up only for devices that support it safely.
CONFIG_SCSI_NCR53C8XX_IOMAPPED (default answer: n)
Answer "y" if you suspect your mother board to not allow memory mapped I/O.
- May slow down performance a little.
+ May slow down performance a little. This option is required by
+ Linux/PPC and is used no matter what you select here. Linux/PPC
+ suffers no performance loss with this option since all IO is memory
+ mapped anyway.
CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE (default answer: n)
Answer "y" if you are sure that all your SCSI devices that are able to
@@ -489,6 +473,18 @@ CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
GPIO wiring. So, this option must not be enabled if your system has
such a board installed.
+CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
+ Enable support for reading the serial NVRAM data on Symbios and
+ some Symbios compatible cards, and Tekram DC390W/U/F cards. Useful for
+ systems with more than one Symbios compatible controller where at least
+ one has a serial NVRAM, or for a system with a mixture of Symbios and
+ Tekram cards. Enables setting the boot order of host adaptors
+ to something other than the default order or "reverse probe" order.
+ Also enables Symbios and Tekram cards to be distinguished so
+ CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT may be set in a system with a
+ mixture of Symbios and Tekram cards so the Symbios cards can make use of
+ the full range of Symbios features, differential, led pin, without
+ causing problems for the Tekram card(s).
10. Boot setup commands
@@ -628,6 +624,20 @@ Reverse probe
810, 815, 820, 860, 875, 885, 895, 896
revprob:y probe chip ids in the reverse order.
+Fix up PCI configuration space
+ pcifix:<option bits>
+
+ Available option bits:
+ 0x1: Set PCI cache-line size register if not set.
+ 0x2: Set write and invalidate bit in PCI command register.
+ 0x4: Increase if necessary PCI latency timer according to burst max.
+
+ Use 'pcifix:7' in order to allow the driver to fix up all PCI features.
+
+Serial NVRAM
+ nvram:n do not look for serial NVRAM
+ nvram:y test controllers for onboard serial NVRAM
+
Boot fail safe
safe:y load the following assumed fail safe initial setup
@@ -638,6 +648,8 @@ Boot fail safe
ultra scsi disabled ultra:n
force sync negotiation disabled fsn:n
reverse probe disabled revprob:n
+ PCI fix up disabled pcifix:0
+ serial NVRAM enabled nvram:y
verbosity level 2 verb:2
tagged command queuing disabled tags:0
synchronous negotiation disabled sync:255
@@ -675,6 +687,83 @@ The driver prints its actual setup when verbosity level is 2. You can try
to your boot setup command in order to check the actual setup the driver is
using.
+10.4 PCI configuration fix-up boot option
+
+pcifix:<option bits>
+
+Available option bits:
+ 0x1: Set PCI cache-line size register if not set.
+ 0x2: Set write and invalidate bit in PCI command register.
+
+Use 'pcifix:3' in order to allow the driver to fix both PCI features.
+
+These options only apply to new SYMBIOS chips 810A, 825A, 860 and 875
+and are only supported for Pentium and 486 class processors.
+Recent SYMBIOS 53C8XX scsi processors are able to use PCI read multiple
+and PCI write and invalidate commands. These features require the
+cache line size register to be properly set in the PCI configuration
+space of the chips. On the other hand, chips will use PCI write and
+invalidate commands only if the corresponding bit is set to 1 in the
+PCI command register.
+
+Not all PCI bioses set the PCI cache line register and the PCI write and
+invalidate bit in the PCI configuration space of 53C8XX chips.
+Optimized PCI accesses may be broken for some PCI/memory controllers or
+make problems with some PCI boards.
+
+This fix-up works flawlessly on my system.
+(MB Triton HX / 53C875 / 53C810A)
+I use these options at my own risks as you will do if you decide to
+use them too.
+
+
+10.5 Serial NVRAM support boot option
+
+nvram:n do not look for serial NVRAM
+nvram:y test controllers for onboard serial NVRAM
+
+This option is described below (see 17. Serial NVRAM support).
+When this option is enabled, the driver tries to detect all boards using
+a Serial NVRAM. This memory is used to hold user set up parameters.
+
+The parameters the driver is able to get from the NVRAM depend on the
+data format used, as follow:
+
+ Tekram format Symbios format
+General and host parameters
+ Boot order N Y
+ Host SCSI ID Y Y
+ SCSI parity checking Y Y
+ Verbose boot messages N Y
+SCSI devices parameters
+ Synchronous transfer speed Y Y
+ Wide 16 / Narrow Y Y
+ Tagged Command Queuing enabled Y Y
+ Disconnections enabled Y Y
+ Scan at boot time N Y
+
+In order to speed up the system boot, for each device configured without
+the "scan at boot time" option, the driver forces an error on the
+first TEST UNIT READY command received for this device.
+
+Some SDMS BIOS revisions seem to be unable to boot cleanly with very fast
+hard disks. In such a situation you cannot configure the NVRAM with
+optimized parameters value.
+
+The 'nvram' boot option can be entered in hexadecimal form in order
+to ignore some options configured in the NVRAM, as follow:
+
+mvram=<bits options>
+ 0x01 look for NVRAM (equivalent to nvram=y)
+ 0x02 ignore NVRAM "Synchronous negotiation" parameters for all devices
+ 0x04 ignore NVRAM "Wide negotiation" parameter for all devices
+ 0x08 ignore NVRAM "Scan at boot time" parameter for all devices
+
+My Atlas Wide only boots cleanly in 8 bits asynchronous data transfer
+mode. However, it works flawlessly at 20 MB/second with the driver.
+Using "nvram=0x7" allows me to boot in 8 bits/async and to let the driver
+use its setup for synchronous and wide negotiations.
+
11. Some constants and flags of the ncr53c8xx.h header file
@@ -766,6 +855,7 @@ SCSI_NCR_SG_TABLESIZE (default: SCSI_NCR_MAX_SCATTER-1)
SCSI_NCR_MAX_LUN (default: 8)
Max number of LUNs per target.
+
12. Installation
12.1 Provided files
@@ -787,9 +877,9 @@ Driver and common files:
You must untar the distribution with the following command:
- tar zxvf ncrBsd2Linux-2.1b-src.tar.gz
+ tar zxvf ncrBsd2Linux-2.2b-src.tar.gz
-The sub-directory ncr53c8xx-2.1b will be created. Change to this directory.
+The sub-directory ncr53c8xx-2.2b will be created. Change to this directory.
12.2 Installation procedure
@@ -851,12 +941,11 @@ When you add a new NCR53C8XX chip based controller to a system that already
has one or more controllers of this family, it may happen that the order
the driver registers them to the kernel causes problems due to device
name changes.
-SDMS BIOS version 4 allows you to define the order the BIOS will scan the
-scsi boards and stores this information for next reboots. Unfortunately, the
-driver is not currently able to read this information and so may register
-controllers in a different order.
+When at least one controller uses NvRAM, SDMS BIOS version 4 allows you to
+define the order the BIOS will scan the scsi boards. The driver attaches
+controllers according to BIOS information if NvRAM detect option is set.
-If you have such a problem, you can:
+If your controllers do not have NvRAM, you can:
- Ask the driver to probe chip ids in reverse order from the boot command
line: ncr53c8xx=revprob:y
@@ -1003,8 +1092,373 @@ Wide16 SCSI.
47 188 5.319 200 5.000
48 192 5.208 200 5.000
49 196 5.102 200 5.000
-===============================================================================
-End of NCR53C8XX driver README file
+17. Serial NVRAM (added by Richard Waltham: dormouse@farsrobt.demon.co.uk)
+
+17.1 Features
+
+Enabling serial NVRAM support enables detection of the serial NVRAM included
+on Symbios and some Symbios compatible host adaptors, and Tekram boards. The
+serial NVRAM is used by Symbios and Tekram to hold set up parameters for the
+host adaptor and it's attached drives.
+
+The Symbios NVRAM also holds data on the boot order of host adaptors in a
+system with more than one host adaptor. This enables the order of scanning
+the cards for drives to be changed from the default used during host adaptor
+detection.
+
+This can be done to a limited extent at the moment using "reverse probe" but
+this only changes the order of detection of different types of cards. The
+NVRAM boot order settings can do this as well as change the order the same
+types of cards are scanned in, something "reverse probe" cannot do.
+
+Tekram boards using Symbios chips, DC390W/F/U, which have NVRAM are detected
+and this is used to distinguish between Symbios compatible and Tekram host
+adaptors. This is used to disable the Symbios compatible "diff" setting
+incorrectly set on Tekram boards if the CONFIG_SCSI_53C8XX_SYMBIOS_COMPAT
+configuration parameter is set enabling both Symbios and Tekram boards to be
+used together with the Symbios cards using all their features, including
+"diff" support. ("led pin" support for Symbios compatible cards can remain
+enabled when using Tekram cards. It does nothing useful for Tekram host
+adaptors but does not cause problems either.)
+
+
+17.2 Symbios NVRAM layout
+
+typical data at NVRAM address 0x100 (53c810a NVRAM)
+-----------------------------------------------------------
+00 00
+64 01
+8e 0b
+
+00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62
+04 00 03 00 00 10 00 58 00 00 01 00 00 63
+04 00 01 00 00 10 00 48 00 00 01 00 00 61
+00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+
+fe fe
+00 00
+00 00
+-----------------------------------------------------------
+NVRAM layout details
+
+NVRAM Address 0x000-0x0ff not used
+ 0x100-0x26f initialised data
+ 0x270-0x7ff not used
+
+general layout
+
+ header - 6 bytes,
+ data - 356 bytes (checksum is byte sum of this data)
+ trailer - 6 bytes
+ ---
+ total 368 bytes
+
+data area layout
+
+ controller set up - 20 bytes
+ boot configuration - 56 bytes (4x14 bytes)
+ device set up - 128 bytes (16x8 bytes)
+ unused (spare?) - 152 bytes (19x8 bytes)
+ ---
+ total 356 bytes
+
+-----------------------------------------------------------
+header
+
+00 00 - ?? start marker
+64 01 - byte count (lsb/msb excludes header/trailer)
+8e 0b - checksum (lsb/msb excludes header/trailer)
+-----------------------------------------------------------
+controller set up
+
+00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00
+ | | |
+ | | -- host ID
+ | --flag bits 2
+ | 0x00000001= scan order hi->low
+ | (default 0x00 - scan low->hi)
+ --flag bits 1
+ 0x00000001 scam enable
+ 0x00000010 parity enable
+ 0x00000100 verbose boot msgs
+
+remaining bytes unknown - they do not appear to change in my
+current set up for any of the controllers.
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+boot configuration
+
+boot order set by order of the devices in this table
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62 -- 1st controller
+04 00 03 00 00 10 00 58 00 00 01 00 00 63 2nd controller
+04 00 01 00 00 10 00 48 00 00 01 00 00 61 3rd controller
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 4th controller
+ | | | | | | | |
+ | | | | | | ---- PCI io port adr
+ | | | | | --0x01 init/scan at boot time
+ | | | | --PCI device/function number (0xdddddfff)
+ | | ----- ?? PCI vendor ID (lsb/msb)
+ ----PCI device ID (lsb/msb)
+
+?? use of this data is a guess but seems reasonable
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+device set up (up to 16 devices - includes controller)
+
+0f 00 08 08 64 00 0a 00 - id 0
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00 - id 15
+ | | | | | |
+ | | | | ----timeout (lsb/msb)
+ | | | --synch period (0x?? 40 Mtrans/sec- fast 40) (probably 0x28)
+ | | | (0x30 20 Mtrans/sec- fast 20)
+ | | | (0x64 10 Mtrans/sec- fast )
+ | | | (0xc8 5 Mtrans/sec)
+ | | | (0x00 asynchronous)
+ | | -- ?? max sync offset (0x08 in NVRAM on 53c810a)
+ | | (0x10 in NVRAM on 53c875)
+ | --device bus width (0x08 narrow)
+ | (0x10 16 bit wide)
+ --flag bits
+ 0x00000001 - disconnect enabled
+ 0x00000010 - scan at boot time
+ 0x00000100 - scan luns
+ 0x00001000 - queue tags enabled
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+?? use of this data is a guess but seems reasonable
+(but it could be max bus width)
+
+default set up for 53c810a NVRAM
+default set up for 53c875 NVRAM - bus width - 0x10
+ - sync offset ? - 0x10
+ - sync period - 0x30
+-----------------------------------------------------------
+?? spare device space (32 bit bus ??)
+
+00 00 00 00 00 00 00 00 (19x8bytes)
+.
+.
+00 00 00 00 00 00 00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+trailer
+
+fe fe - ? end marker ?
+00 00
+00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+
+
+
+17.3 Tekram NVRAM layout
+
+nvram 64x16 (1024 bit)
+
+Drive settings
+
+Drive ID 0-15 (addr 0x0yyyy0 = device setup, yyyy = ID)
+ (addr 0x0yyyy1 = 0x0000)
+
+ x x x x x x x x x x x x x x x x
+ | | | | | | | | |
+ | | | | | | | | ----- parity check 0 - off
+ | | | | | | | | 1 - on
+ | | | | | | | |
+ | | | | | | | ------- sync neg 0 - off
+ | | | | | | | 1 - on
+ | | | | | | |
+ | | | | | | --------- disconnect 0 - off
+ | | | | | | 1 - on
+ | | | | | |
+ | | | | | ----------- start cmd 0 - off
+ | | | | | 1 - on
+ | | | | |
+ | | | | -------------- tagged cmds 0 - off
+ | | | | 1 - on
+ | | | |
+ | | | ---------------- wide neg 0 - off
+ | | | 1 - on
+ | | |
+ --------------------------- sync rate 0 - 10.0 Mtrans/sec
+ 1 - 8.0
+ 2 - 6.6
+ 3 - 5.7
+ 4 - 5.0
+ 5 - 4.0
+ 6 - 3.0
+ 7 - 2.0
+ 7 - 2.0
+ 8 - 20.0
+ 9 - 16.7
+ a - 13.9
+ b - 11.9
+
+Global settings
+
+Host flags 0 (addr 0x100000, 32)
+
+ x x x x x x x x x x x x x x x x
+ | | | | | | | | | | | |
+ | | | | | | | | ----------- host ID 0x00 - 0x0f
+ | | | | | | | |
+ | | | | | | | ----------------------- support for 0 - off
+ | | | | | | | > 2 drives 1 - on
+ | | | | | | |
+ | | | | | | ------------------------- support drives 0 - off
+ | | | | | | > 1Gbytes 1 - on
+ | | | | | |
+ | | | | | --------------------------- bus reset on 0 - off
+ | | | | | power on 1 - on
+ | | | | |
+ | | | | ----------------------------- active neg 0 - off
+ | | | | 1 - on
+ | | | |
+ | | | -------------------------------- imm seek 0 - off
+ | | | 1 - on
+ | | |
+ | | ---------------------------------- scan luns 0 - off
+ | | 1 - on
+ | |
+ -------------------------------------- removable 0 - disable
+ as BIOS dev 1 - boot device
+ 2 - all
+
+Host flags 1 (addr 0x100001, 33)
+
+ x x x x x x x x x x x x x x x x
+ | | | | | |
+ | | | --------- boot delay 0 - 3 sec
+ | | | 1 - 5
+ | | | 2 - 10
+ | | | 3 - 20
+ | | | 4 - 30
+ | | | 5 - 60
+ | | | 6 - 120
+ | | |
+ --------------------------- max tag cmds 0 - 2
+ 1 - 4
+ 2 - 8
+ 3 - 16
+ 4 - 32
+
+Host flags 2 (addr 0x100010, 34)
+
+ x x x x x x x x x x x x x x x x
+ |
+ ----- F2/F6 enable 0 - off ???
+ 1 - on ???
+
+checksum (addr 0x111111)
+
+checksum = 0x1234 - (sum addr 0-63)
+
+----------------------------------------------------------------------------
+
+default nvram data:
+
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+
+0x0f07 0x0400 0x0001 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0xfbbc
+
+
+18. Support for Big Endian
+
+The PCI local bus has been primarily designed for x86 architecture.
+As a consequence, PCI devices generally expect DWORDS using little endian
+byte ordering.
+
+18.1 Big Endian CPU
+
+In order to support NCR chips on a Big Endian architecture the driver has to
+perform byte reordering each time it is needed. This feature has been
+added to the driver by Cort <cort@cs.nmt.edu> and is available in driver
+version 2.5 and later ones. For the moment Big Endian support has only
+been tested on Linux/PPC (PowerPC).
+
+18.2 NCR chip in Big Endian mode of operations
+
+It can be read in SYMBIOS documentation that some chips support a special
+Big Endian mode, on paper: 53C815, 53C825A, 53C875, 53C875N, 53C895.
+This mode of operations is not software-selectable, but needs pin named
+BigLit to be pulled-up. Using this mode, most of byte reorderings should
+be avoided when the driver is running on a Big Endian CPU.
+Driver version 2.5 is also, in theory, ready for this feature.
+===============================================================================
+End of NCR53C8XX driver README file
diff --git a/drivers/scsi/aic7xxx.c b/drivers/scsi/aic7xxx.c
index 005c889d7..612af9baa 100644
--- a/drivers/scsi/aic7xxx.c
+++ b/drivers/scsi/aic7xxx.c
@@ -73,7 +73,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aic7xxx.c,v 1.119 1997/06/27 19:39:18 gibbs Exp $
+ * $Id: aic7xxx.c,v 1.2 1997/08/06 19:16:00 miguel Exp $
*---------------------------------------------------------------------------
*
* Thanks also go to (in alphabetical order) the following:
@@ -93,7 +93,7 @@
*
* Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
*
- * $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
+ * $Id: aic7xxx.c,v 1.2 1997/08/06 19:16:00 miguel Exp $
*-M*************************************************************************/
#ifdef MODULE
@@ -139,7 +139,7 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
-#define AIC7XXX_C_VERSION "$Revision: 4.1 $"
+#define AIC7XXX_C_VERSION "$Revision: 1.2 $"
#define NUMBER(arr) (sizeof(arr) / sizeof(arr[0]))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
@@ -829,6 +829,7 @@ struct aic7xxx_host {
unsigned char pause; /* pause value for HCNTRL */
unsigned char qcntmask;
unsigned char qfullcount;
+ unsigned char cmdoutcnt;
unsigned char curqincnt;
struct Scsi_Host *next; /* allow for multiple IRQs */
unsigned char activescbs; /* active scbs */
@@ -3880,6 +3881,19 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
#endif
while (qoutcnt > 0)
{
+ if ((p->flags & PAGE_ENABLED) != 0)
+ {
+ p->cmdoutcnt += qoutcnt;
+ if (p->cmdoutcnt >= p->qfullcount)
+ {
+ /*
+ * Since paging only occurs on aic78x0 chips, we can use
+ * Auto Access Pause to clear the command count.
+ */
+ outb(0, p->base + CMDOUTCNT);
+ p->cmdoutcnt = 0;
+ }
+ }
for (i = 0; i < qoutcnt; i++)
{
scb_index = inb(p->base + QOUTFIFO);
@@ -4638,9 +4652,9 @@ configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1,
{
old_verbose = aic7xxx_verbose;
printk(KERN_INFO "aic7xxx: Warning - detected auto-termination. Please "
- "verify driver");
+ "verify driver\n");
printk(KERN_INFO " detected settings and use manual termination "
- "if necessary.");
+ "if necessary.\n");
/* Configure auto termination. */
outb(SEECS | SEEMS, p->base + SEECTL);
@@ -5291,6 +5305,9 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p)
*/
outb(p->qcntmask, p->base + QCNTMASK);
+ outb(p->qfullcount, p->base + FIFODEPTH);
+ outb(0, p->base + CMDOUTCNT);
+
/*
* We don't have any waiting selections or disconnected SCBs.
*/
diff --git a/drivers/scsi/aic7xxx_proc.c b/drivers/scsi/aic7xxx_proc.c
index 1092d4862..5c766c039 100644
--- a/drivers/scsi/aic7xxx_proc.c
+++ b/drivers/scsi/aic7xxx_proc.c
@@ -24,7 +24,7 @@
*
* Dean W. Gehnert, deang@teleport.com, 05/01/96
*
- * $Id: aic7xxx_proc.c,v 4.1 1997/06/97 08:23:42 deang Exp $
+ * $Id: aic7xxx_proc.c,v 1.2 1997/08/06 19:16:03 miguel Exp $
*-M*************************************************************************/
#define BLS buffer + len + size
@@ -76,7 +76,6 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
{
struct Scsi_Host *HBAptr;
struct aic7xxx_host *p;
- static u8 buff[512];
int i;
int found = FALSE;
int size = 0;
@@ -129,11 +128,6 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
return (aic7xxx_set_info(buffer, length, HBAptr));
}
- if (offset == 0)
- {
- memset(buff, 0, sizeof(buff));
- }
-
p = (struct aic7xxx_host *) HBAptr->hostdata;
size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
@@ -142,7 +136,14 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
#if 0
size += sprintf(BLS, "%s\n", rcs_version(AIC7XXX_SEQ_VER));
#endif
+ if (size > 512)
+ printk(KERN_CRIT "aic7xxx: possible overflow at first position\n");
len += size; pos = begin + len; size = 0;
+ if (pos < offset)
+ {
+ begin = pos;
+ len = 0;
+ }
size += sprintf(BLS, "\n");
size += sprintf(BLS, "Compile Options:\n");
@@ -167,7 +168,16 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
#else
size += sprintf(BLS, " AIC7XXX_PROC_STATS : Disabled\n");
#endif
+ if (size > 512)
+ printk(KERN_CRIT "aic7xxx: possible overflow at second position\n");
len += size; pos = begin + len; size = 0;
+ if (pos < offset)
+ {
+ begin = pos;
+ len = 0;
+ }
+ else if (pos >= offset + length)
+ goto stop_output;
size += sprintf(BLS, "\n");
size += sprintf(BLS, "Adapter Configuration:\n");
@@ -201,7 +211,16 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
(p->flags & ULTRA_ENABLED) ? "En" : "Dis");
size += sprintf(BLS, " Target Disconnect: %sabled\n",
p->discenable ? "En" : "Dis");
+ if (size > 512)
+ printk(KERN_CRIT "aic7xxx: possible overflow at third position\n");
len += size; pos = begin + len; size = 0;
+ if (pos < offset)
+ {
+ begin = pos;
+ len = 0;
+ }
+ else if (pos >= offset + length)
+ goto stop_output;
#ifdef AIC7XXX_PROC_STATS
{
@@ -210,6 +229,7 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
/*
* XXX: Need to fix this to avoid overflow...
+ * Fixed - gordo.
*/
size += sprintf(BLS, "\n");
size += sprintf(BLS, "Statistics:\n");
@@ -247,9 +267,18 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
sp->w_bins[9]);
size += sprintf(BLS, "\n");
}
+ if (size > 512)
+ printk(KERN_CRIT "aic7xxx: possible overflow at loop %d:%d\n", target, lun);
+ len += size; pos = begin + len; size = 0;
+ if (pos < offset)
+ {
+ begin = pos;
+ len = 0;
+ }
+ else if (pos >= offset + length)
+ goto stop_output;
}
}
- len += size; pos = begin + len; size = 0;
}
#endif /* AIC7XXX_PROC_STATS */
@@ -257,7 +286,11 @@ stop_output:
proc_debug("2pos: %ld offset: %ld len: %d\n", pos, offset, len);
*start = buffer + (offset - begin); /* Start of wanted data */
len -= (offset - begin); /* Start slop */
- if (len > length)
+ if (len < 0)
+ {
+ len = 0; /* off end of file */
+ }
+ else if (len > length)
{
len = length; /* Ending slop */
}
diff --git a/drivers/scsi/aic7xxx_reg.h b/drivers/scsi/aic7xxx_reg.h
index f3b8c349c..8fbf84fa6 100644
--- a/drivers/scsi/aic7xxx_reg.h
+++ b/drivers/scsi/aic7xxx_reg.h
@@ -252,15 +252,19 @@
#define CUR_SCBID 0x58
-#define ARG_1 0x59
-#define RETURN_1 0x59
-#define SEND_MSG 0x80
-#define SEND_SENSE 0x40
-#define SEND_REJ 0x20
+#define CMDOUTCNT 0x59
#define SCSICONF 0x5a
#define RESET_SCSI 0x40
+#define FIFODEPTH 0x5a
+
+#define ARG_1 0x5b
+#define RETURN_1 0x5b
+#define SEND_MSG 0x80
+#define SEND_SENSE 0x40
+#define SEND_REJ 0x20
+
#define HOSTCONF 0x5d
#define HA_274_BIOSCTRL 0x5f
diff --git a/drivers/scsi/esp.c b/drivers/scsi/esp.c
index b479cc9fc..f1be3e5b4 100644
--- a/drivers/scsi/esp.c
+++ b/drivers/scsi/esp.c
@@ -12,6 +12,7 @@
* 3) Add tagged queueing.
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/types.h>
@@ -34,6 +35,7 @@
#include <asm/pgtable.h>
#include <asm/oplib.h>
#include <asm/io.h>
+#include <asm/irq.h>
#include <asm/idprom.h>
#define DEBUG_ESP
@@ -642,6 +644,9 @@ static inline void esp_bootup_reset(struct Sparc_ESP *esp, struct Sparc_ESP_regs
*/
__initfunc(int esp_detect(Scsi_Host_Template *tpnt))
{
+#ifdef __sparc_v9__
+ struct devid_cookie dcookie;
+#endif
struct Sparc_ESP *esp, *elink;
struct Scsi_Host *esp_host;
struct linux_sbus *sbus;
@@ -655,8 +660,13 @@ __initfunc(int esp_detect(Scsi_Host_Template *tpnt))
int esp_node, i;
espchain = 0;
- if(!SBus_chain)
+ if(!SBus_chain) {
+#ifdef CONFIG_PCI
+ return 0;
+#else
panic("No SBUS in esp_detect()");
+#endif
+ }
for_each_sbus(sbus) {
for_each_sbusdev(sbdev_iter, sbus) {
struct linux_sbus_device *espdma = 0;
@@ -782,6 +792,7 @@ __initfunc(int esp_detect(Scsi_Host_Template *tpnt))
esp->edev->reg_addrs[0].reg_size;
esp->ehost->irq = esp->irq = esp->edev->irqs[0].pri;
+#ifndef __sparc_v9__
/* Allocate the irq only if necessary */
for_each_esp(elink) {
if((elink != esp) && (esp->irq == elink->irq)) {
@@ -793,6 +804,22 @@ __initfunc(int esp_detect(Scsi_Host_Template *tpnt))
panic("Cannot acquire ESP irq line");
esp_irq_acquired:
printk("esp%d: IRQ %d ", esp->esp_id, esp->ehost->irq);
+#else
+ /* On Ultra we must always call request_irq for each
+ * esp, so that imap registers get setup etc.
+ */
+ dcookie.real_dev_id = esp;
+ dcookie.imap = dcookie.iclr = 0;
+ dcookie.pil = -1;
+ dcookie.bus_cookie = sbus;
+ if(request_irq(esp->ehost->irq, esp_intr,
+ (SA_SHIRQ | SA_SBUS | SA_DCOOKIE),
+ "Sparc ESP SCSI", &dcookie))
+ panic("Cannot acquire ESP irq line");
+ esp->ehost->irq = esp->irq = dcookie.ret_ino;
+ printk("esp%d: INO[%x] IRQ %d ",
+ esp->esp_id, esp->ehost->irq, dcookie.ret_pil);
+#endif
/* Figure out our scsi ID on the bus */
esp->scsi_id = prom_getintdefault(esp->prom_node,
@@ -2323,11 +2350,6 @@ static inline int esp_do_data_finale(struct Sparc_ESP *esp,
/* Please go to msgout phase, please please please... */
ESPLOG(("esp%d: !BSERV after data, probably to msgout\n",
esp->esp_id));
-#ifdef __SMP__
- ESPLOG(("esp%d: local_irq_count[%x:%x:%x:%x]\n", esp->esp_id,
- local_irq_count[0], local_irq_count[1],
- local_irq_count[2], local_irq_count[3]));
-#endif
return esp_do_phase_determine(esp, eregs, dregs);
}
@@ -2410,11 +2432,6 @@ static inline int esp_do_data_finale(struct Sparc_ESP *esp,
ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n",
esp->esp_id,
SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual));
-#ifdef __SMP__
- ESPLOG(("esp%d: local_irq_count[%x:%x:%x:%x]\n", esp->esp_id,
- local_irq_count[0], local_irq_count[1],
- local_irq_count[2], local_irq_count[3]));
-#endif
bytes_sent = 0;
}
@@ -4021,6 +4038,8 @@ esp_handle_done:
return;
}
+#ifndef __sparc_v9__
+
#ifndef __SMP__
static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
{
@@ -4031,14 +4050,7 @@ static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
repeat:
again = 0;
for_each_esp(esp) {
- /* XXX Ultra: This is gross, what we really need
- * XXX is a sbusirq_to_sparc_pil() function, call
- * XXX that and stick the result in the esp soft
- * XXX state structure. -DaveM
- */
-#ifndef __sparc_v9__
if((esp->irq & 0xf) == irq) {
-#endif
if(DMA_IRQ_P(esp->dregs)) {
again = 1;
@@ -4050,23 +4062,12 @@ repeat:
DMA_INTSON(esp->dregs);
}
-#ifndef __sparc_v9__
}
-#endif
}
if(again)
goto repeat;
}
#else
-
-/* XXX Gross hack for sun4u SMP, fix it right later... -DaveM */
-#ifdef __sparc_v9__
-extern unsigned char ino_to_pil[];
-#define INO_TO_PIL(esp) (ino_to_pil[(esp)->irq])
-#else
-#define INO_TO_PIL(esp) ((esp)->irq & 0xf)
-#endif
-
/* For SMP we only service one ESP on the list list at our IRQ level! */
static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
{
@@ -4074,7 +4075,7 @@ static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
/* Handle all ESP interrupts showing at this IRQ level. */
for_each_esp(esp) {
- if(INO_TO_PIL(esp) == irq) {
+ if(((esp)->irq & 0xf) == irq) {
if(DMA_IRQ_P(esp->dregs)) {
DMA_INTSOFF(esp->dregs);
@@ -4090,3 +4091,22 @@ static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
}
}
#endif
+
+#else /* __sparc_v9__ */
+
+static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
+{
+ struct Sparc_ESP *esp = dev_id;
+
+ if(DMA_IRQ_P(esp->dregs)) {
+ DMA_INTSOFF(esp->dregs);
+
+ ESPIRQ(("I[%d:%d](", smp_processor_id(), esp->esp_id));
+ esp_handle(esp);
+ ESPIRQ((")"));
+
+ DMA_INTSON(esp->dregs);
+ }
+}
+
+#endif
diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c
index 1e398a152..aee77ba5c 100644
--- a/drivers/scsi/g_NCR5380.c
+++ b/drivers/scsi/g_NCR5380.c
@@ -70,7 +70,10 @@
*/
/*
- * $Log: generic_NCR5380.c,v $
+ * $Log: g_NCR5380.c,v $
+ * Revision 1.3 1997/06/17 13:25:29 ralf
+ * Merge with 2.1.43.
+ *
*/
#define AUTOPROBE_IRQ
@@ -736,4 +739,11 @@ Scsi_Host_Template driver_template = GENERIC_NCR5380;
#include <linux/module.h>
#include "scsi_module.c"
+
+MODULE_PARM(ncr_irq, "i");
+MODULE_PARM(ncr_dma, "i");
+MODULE_PARM(ncr_addr, "i");
+MODULE_PARM(ncr_5380, "i");
+MODULE_PARM(ncr_53c400, "i");
+
#endif
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 9bf8c5ec3..513ebf8ad 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -190,13 +190,21 @@
#include "ide-scsi.h"
#endif
+#ifdef CONFIG_SCSI_MESH
+#include "mesh.h"
+#endif
+
+#ifdef CONFIG_SCSI_MAC53C94
+#include "mac53c94.h"
+#endif
+
#ifdef CONFIG_SCSI_DEBUG
#include "scsi_debug.h"
#endif
/*
-static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/hosts.c,v 1.20 1996/12/12 19:18:32 davem Exp $";
+static const char RCSid[] = "$Header: /src/cvs/linux/drivers/scsi/hosts.c,v 1.1.1.1 1997/06/01 03:17:37 ralf Exp $";
*/
/*
@@ -343,6 +351,12 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
#ifdef CONFIG_BLK_DEV_IDESCSI
IDESCSI,
#endif
+#ifdef CONFIG_SCSI_MESH
+ SCSI_MESH,
+#endif
+#ifdef CONFIG_SCSI_MAC53C94
+ SCSI_MAC53C94,
+#endif
#ifdef CONFIG_SCSI_SGIWD93
SGIWD93_SCSI,
#endif
diff --git a/drivers/scsi/mac53c94.c b/drivers/scsi/mac53c94.c
new file mode 100644
index 000000000..e7d1470ae
--- /dev/null
+++ b/drivers/scsi/mac53c94.c
@@ -0,0 +1,502 @@
+/*
+ * SCSI low-level driver for the 53c94 SCSI bus adaptor found
+ * on Power Macintosh computers, controlling the external SCSI chain.
+ * We assume the 53c94 is connected to a DBDMA (descriptor-based DMA)
+ * controller.
+ *
+ * Paul Mackerras, August 1996.
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/blk.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <asm/dbdma.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/system.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "mac53c94.h"
+
+struct proc_dir_entry proc_scsi_mac53c94 = {
+ PROC_SCSI_53C94, 5, "53c94",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+enum fsc_phase {
+ idle,
+ selecting,
+ dataing,
+ completing,
+ busfreeing,
+};
+
+struct fsc_state {
+ volatile struct mac53c94_regs *regs;
+ int intr;
+ volatile struct dbdma_regs *dma;
+ int dmaintr;
+ int clk_freq;
+ struct Scsi_Host *host;
+ struct fsc_state *next;
+ Scsi_Cmnd *request_q;
+ Scsi_Cmnd *request_qtail;
+ Scsi_Cmnd *current_req; /* req we're currently working on */
+ enum fsc_phase phase; /* what we're currently trying to do */
+ struct dbdma_cmd *dma_cmds; /* space for dbdma commands, aligned */
+};
+
+static struct fsc_state *all_53c94s;
+
+static void mac53c94_init(struct fsc_state *);
+static void mac53c94_start(struct fsc_state *);
+static void mac53c94_interrupt(int, void *, struct pt_regs *);
+static void cmd_done(struct fsc_state *, int result);
+static void set_dma_cmds(struct fsc_state *, Scsi_Cmnd *);
+static int data_goes_out(Scsi_Cmnd *);
+
+int
+mac53c94_detect(Scsi_Host_Template *tp)
+{
+ struct device_node *node;
+ int nfscs;
+ struct fsc_state *state, **prev_statep;
+ struct Scsi_Host *host;
+ void *dma_cmd_space;
+ unsigned char *clkprop;
+ int proplen;
+
+ nfscs = 0;
+ prev_statep = &all_53c94s;
+ for (node = find_devices("53c94"); node != 0; node = node->next) {
+ if (node->n_addrs != 2 || node->n_intrs != 2)
+ panic("53c94: expected 2 addrs and intrs (got %d/%d)",
+ node->n_addrs, node->n_intrs);
+ host = scsi_register(tp, sizeof(struct fsc_state));
+ if (host == 0)
+ panic("couldn't register 53c94 host");
+ host->unique_id = nfscs;
+ note_scsi_host(node, host);
+
+ state = (struct fsc_state *) host->hostdata;
+ if (state == 0)
+ panic("no 53c94 state");
+ state->host = host;
+ state->regs = (volatile struct mac53c94_regs *)
+ node->addrs[0].address;
+ state->intr = node->intrs[0];
+ state->dma = (volatile struct dbdma_regs *)
+ node->addrs[1].address;
+ state->dmaintr = node->intrs[1];
+
+ clkprop = get_property(node, "clock-frequency", &proplen);
+ if (clkprop == NULL || proplen != sizeof(int)) {
+ printk(KERN_ERR "%s: can't get clock frequency\n",
+ node->full_name);
+ state->clk_freq = 25000000;
+ } else
+ state->clk_freq = *(int *)clkprop;
+
+ /* Space for dma command list: +1 for stop command,
+ +1 to allow for aligning. */
+ dma_cmd_space = kmalloc((host->sg_tablesize + 2) *
+ sizeof(struct dbdma_cmd), GFP_KERNEL);
+ if (dma_cmd_space == 0)
+ panic("53c94: couldn't allocate dma command space");
+ state->dma_cmds = (struct dbdma_cmd *)
+ DBDMA_ALIGN(dma_cmd_space);
+ memset(state->dma_cmds, 0, (host->sg_tablesize + 1)
+ * sizeof(struct dbdma_cmd));
+
+ *prev_statep = state;
+ prev_statep = &state->next;
+
+ if (request_irq(state->intr, mac53c94_interrupt, 0,
+ "53C94", state)) {
+ printk(KERN_ERR "mac53C94: can't get irq %d\n", state->intr);
+ }
+
+ mac53c94_init(state);
+
+ ++nfscs;
+ }
+ return nfscs;
+}
+
+int
+mac53c94_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+ unsigned long flags;
+ struct fsc_state *state;
+
+#if 0
+ if (data_goes_out(cmd)) {
+ int i;
+ printk(KERN_DEBUG "mac53c94_queue %p: command is", cmd);
+ for (i = 0; i < cmd->cmd_len; ++i)
+ printk(" %.2x", cmd->cmnd[i]);
+ printk("\n" KERN_DEBUG "use_sg=%d request_bufflen=%d request_buffer=%p\n",
+ cmd->use_sg, cmd->request_bufflen, cmd->request_buffer);
+ }
+#endif
+
+ cmd->scsi_done = done;
+ cmd->host_scribble = NULL;
+
+ state = (struct fsc_state *) cmd->host->hostdata;
+
+ save_flags(flags);
+ cli();
+ if (state->request_q == NULL)
+ state->request_q = cmd;
+ else
+ state->request_qtail->host_scribble = (void *) cmd;
+ state->request_qtail = cmd;
+
+ if (state->phase == idle)
+ mac53c94_start(state);
+
+ restore_flags(flags);
+ return 0;
+}
+
+int
+mac53c94_abort(Scsi_Cmnd *cmd)
+{
+ return SCSI_ABORT_SNOOZE;
+}
+
+int
+mac53c94_reset(Scsi_Cmnd *cmd, unsigned how)
+{
+ struct fsc_state *state = (struct fsc_state *) cmd->host->hostdata;
+ volatile struct mac53c94_regs *regs = state->regs;
+ volatile struct dbdma_regs *dma = state->dma;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ st_le32(&dma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+ regs->command = CMD_SCSI_RESET; /* assert RST */
+ eieio();
+ udelay(100); /* leave it on for a while (>= 25us) */
+ regs->command = CMD_RESET;
+ eieio();
+ udelay(20);
+ mac53c94_init(state);
+ regs->command = CMD_NOP;
+ eieio();
+ restore_flags(flags);
+ return SCSI_RESET_PENDING;
+}
+
+int
+mac53c94_command(Scsi_Cmnd *cmd)
+{
+ printk(KERN_DEBUG "whoops... mac53c94_command called\n");
+ return -1;
+}
+
+static void
+mac53c94_init(struct fsc_state *state)
+{
+ volatile struct mac53c94_regs *regs = state->regs;
+ volatile struct dbdma_regs *dma = state->dma;
+ int x;
+
+ regs->config1 = state->host->this_id | CF1_PAR_ENABLE;
+ regs->sel_timeout = TIMO_VAL(250); /* 250ms */
+ regs->clk_factor = CLKF_VAL(state->clk_freq);
+ regs->config2 = CF2_FEATURE_EN;
+ regs->config3 = 0;
+ regs->sync_period = 0;
+ regs->sync_offset = 0;
+ eieio();
+ x = regs->interrupt;
+ st_le32(&dma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+}
+
+/*
+ * Start the next command for a 53C94.
+ * Should be called with interrupts disabled.
+ */
+static void
+mac53c94_start(struct fsc_state *state)
+{
+ Scsi_Cmnd *cmd;
+ volatile struct mac53c94_regs *regs = state->regs;
+ int i;
+
+ if (state->phase != idle || state->current_req != NULL)
+ panic("inappropriate mac53c94_start (state=%p)", state);
+ if (state->request_q == NULL)
+ return;
+ state->current_req = cmd = state->request_q;
+ state->request_q = (Scsi_Cmnd *) cmd->host_scribble;
+
+ /* Off we go */
+ regs->count_lo = 0;
+ regs->count_mid = 0;
+ regs->count_hi = 0;
+ eieio();
+ regs->command = CMD_NOP + CMD_DMA_MODE;
+ udelay(1);
+ eieio();
+ regs->command = CMD_FLUSH;
+ udelay(1);
+ eieio();
+ regs->dest_id = cmd->target;
+ regs->sync_period = 0;
+ regs->sync_offset = 0;
+ eieio();
+
+ /* load the command into the FIFO */
+ for (i = 0; i < cmd->cmd_len; ++i) {
+ regs->fifo = cmd->cmnd[i];
+ eieio();
+ }
+
+ /* do select without ATN XXX */
+ regs->command = CMD_SELECT;
+ state->phase = selecting;
+
+ if (cmd->use_sg > 0 || cmd->request_bufflen != 0)
+ set_dma_cmds(state, cmd);
+}
+
+static void
+mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
+{
+ struct fsc_state *state = (struct fsc_state *) dev_id;
+ volatile struct mac53c94_regs *regs = state->regs;
+ volatile struct dbdma_regs *dma = state->dma;
+ Scsi_Cmnd *cmd = state->current_req;
+ int nb, stat, seq, intr;
+ static int mac53c94_errors;
+
+ /*
+ * Apparently, reading the interrupt register unlatches
+ * the status and sequence step registers.
+ */
+ seq = regs->seqstep;
+ stat = regs->status;
+ intr = regs->interrupt;
+
+#if 0
+ printk(KERN_DEBUG "mac53c94_intr, intr=%x stat=%x seq=%x phase=%d\n",
+ intr, stat, seq, state->phase);
+#endif
+
+ if (intr & INTR_RESET) {
+ /* SCSI bus was reset */
+ printk(KERN_INFO "external SCSI bus reset detected\n");
+ regs->command = CMD_NOP;
+ st_le32(&dma->control, RUN << 16); /* stop dma */
+ cmd_done(state, DID_RESET << 16);
+ return;
+ }
+ if (intr & INTR_ILL_CMD) {
+ printk(KERN_ERR "53c94: illegal cmd, intr=%x stat=%x seq=%x phase=%d\n",
+ intr, stat, seq, state->phase);
+ cmd_done(state, DID_ERROR << 16);
+ return;
+ }
+ if (stat & STAT_ERROR) {
+#if 0
+ /* XXX these seem to be harmless? */
+ printk("53c94: bad error, intr=%x stat=%x seq=%x phase=%d\n",
+ intr, stat, seq, state->phase);
+#endif
+ ++mac53c94_errors;
+ regs->command = CMD_NOP + CMD_DMA_MODE;
+ eieio();
+ }
+ if (cmd == 0) {
+ printk(KERN_DEBUG "53c94: interrupt with no command active?\n");
+ return;
+ }
+ if (stat & STAT_PARITY) {
+ printk(KERN_ERR "mac53c94: parity error\n");
+ cmd_done(state, DID_PARITY << 16);
+ return;
+ }
+ switch (state->phase) {
+ case selecting:
+ if (intr & INTR_DISCONNECT) {
+ /* selection timed out */
+ cmd_done(state, DID_BAD_TARGET << 16);
+ return;
+ }
+ if (intr != INTR_BUS_SERV + INTR_DONE) {
+ printk(KERN_DEBUG "got intr %x during selection\n", intr);
+ cmd_done(state, DID_ERROR << 16);
+ return;
+ }
+ if ((seq & SS_MASK) != SS_DONE) {
+ printk(KERN_DEBUG "seq step %x after command\n", seq);
+ cmd_done(state, DID_ERROR << 16);
+ return;
+ }
+ regs->command = CMD_NOP;
+ /* set DMA controller going if any data to transfer */
+ if ((stat & (STAT_MSG|STAT_CD)) == 0
+ && (cmd->use_sg > 0 || cmd->request_bufflen != 0)) {
+ nb = cmd->SCp.this_residual;
+ if (nb > 0xfff0)
+ nb = 0xfff0;
+ cmd->SCp.this_residual -= nb;
+ regs->count_lo = nb;
+ regs->count_mid = nb >> 8;
+ eieio();
+ regs->command = CMD_DMA_MODE + CMD_NOP;
+ eieio();
+ st_le32(&dma->cmdptr, virt_to_phys(state->dma_cmds));
+ st_le32(&dma->control, (RUN << 16) | RUN);
+ eieio();
+ regs->command = CMD_DMA_MODE + CMD_XFER_DATA;
+ state->phase = dataing;
+ break;
+ } else if ((stat & STAT_PHASE) == STAT_CD + STAT_IO) {
+ /* up to status phase already */
+ regs->command = CMD_I_COMPLETE;
+ state->phase = completing;
+ } else {
+ printk(KERN_DEBUG "in unexpected phase %x after cmd\n",
+ stat & STAT_PHASE);
+ cmd_done(state, DID_ERROR << 16);
+ return;
+ }
+ break;
+
+ case dataing:
+ if (intr != INTR_BUS_SERV) {
+ printk(KERN_DEBUG "got intr %x before status\n", intr);
+ cmd_done(state, DID_ERROR << 16);
+ return;
+ }
+ if (cmd->SCp.this_residual != 0
+ && (stat & (STAT_MSG|STAT_CD)) == 0) {
+ /* Set up the count regs to transfer more */
+ nb = cmd->SCp.this_residual;
+ if (nb > 0xfff0)
+ nb = 0xfff0;
+ cmd->SCp.this_residual -= nb;
+ regs->count_lo = nb;
+ regs->count_mid = nb >> 8;
+ eieio();
+ regs->command = CMD_DMA_MODE + CMD_NOP;
+ eieio();
+ regs->command = CMD_DMA_MODE + CMD_XFER_DATA;
+ break;
+ }
+ if ((stat & STAT_PHASE) != STAT_CD + STAT_IO) {
+ printk(KERN_DEBUG "intr %x before data xfer complete\n", intr);
+ }
+ st_le32(&dma->control, RUN << 16); /* stop dma */
+ /* should check dma status */
+ regs->command = CMD_I_COMPLETE;
+ state->phase = completing;
+ break;
+ case completing:
+ if (intr != INTR_DONE) {
+ printk(KERN_DEBUG "got intr %x on completion\n", intr);
+ cmd_done(state, DID_ERROR << 16);
+ return;
+ }
+ cmd->SCp.Status = regs->fifo; eieio();
+ cmd->SCp.Message = regs->fifo; eieio();
+ cmd->result =
+ regs->command = CMD_ACCEPT_MSG;
+ state->phase = busfreeing;
+ break;
+ case busfreeing:
+ if (intr != INTR_DISCONNECT) {
+ printk(KERN_DEBUG "got intr %x when expected disconnect\n", intr);
+ }
+ cmd_done(state, (DID_OK << 16) + (cmd->SCp.Message << 8)
+ + cmd->SCp.Status);
+ break;
+ default:
+ printk(KERN_DEBUG "don't know about phase %d\n", state->phase);
+ }
+}
+
+static void
+cmd_done(struct fsc_state *state, int result)
+{
+ Scsi_Cmnd *cmd;
+
+ cmd = state->current_req;
+ if (cmd != 0) {
+ cmd->result = result;
+ (*cmd->scsi_done)(cmd);
+ state->current_req = NULL;
+ }
+ state->phase = idle;
+ mac53c94_start(state);
+}
+
+/*
+ * Set up DMA commands for transferring data.
+ */
+static void
+set_dma_cmds(struct fsc_state *state, Scsi_Cmnd *cmd)
+{
+ int i, dma_cmd, total;
+ struct scatterlist *scl;
+ struct dbdma_cmd *dcmds;
+
+ dma_cmd = data_goes_out(cmd)? OUTPUT_MORE: INPUT_MORE;
+ dcmds = state->dma_cmds;
+ if (cmd->use_sg > 0) {
+ total = 0;
+ scl = (struct scatterlist *) cmd->buffer;
+ for (i = 0; i < cmd->use_sg; ++i) {
+ if (scl->length > 0xffff)
+ panic("mac53c94: scatterlist element >= 64k");
+ total += scl->length;
+ st_le16(&dcmds->req_count, scl->length);
+ st_le16(&dcmds->command, dma_cmd);
+ st_le32(&dcmds->phy_addr, virt_to_phys(scl->address));
+ dcmds->xfer_status = 0;
+ ++scl;
+ ++dcmds;
+ }
+ } else {
+ total = cmd->request_bufflen;
+ if (total > 0xffff)
+ panic("mac53c94: transfer size >= 64k");
+ st_le16(&dcmds->req_count, total);
+ st_le32(&dcmds->phy_addr, virt_to_phys(cmd->request_buffer));
+ dcmds->xfer_status = 0;
+ ++dcmds;
+ }
+ dma_cmd += OUTPUT_LAST - OUTPUT_MORE;
+ st_le16(&dcmds[-1].command, dma_cmd);
+ st_le16(&dcmds->command, DBDMA_STOP);
+ cmd->SCp.this_residual = total;
+}
+
+/*
+ * Work out whether data will be going out from the host adaptor or into it.
+ * (If this information is available from somewhere else in the scsi
+ * code, somebody please let me know :-)
+ */
+static int
+data_goes_out(Scsi_Cmnd *cmd)
+{
+ switch (cmd->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12: /* any others? */
+ return 1;
+ default:
+ return 0;
+ }
+}
diff --git a/drivers/scsi/mac53c94.h b/drivers/scsi/mac53c94.h
new file mode 100644
index 000000000..b423b0c9f
--- /dev/null
+++ b/drivers/scsi/mac53c94.h
@@ -0,0 +1,246 @@
+/*
+ * mac53c94.h: definitions for the driver for the 53c94 SCSI bus adaptor
+ * found on Power Macintosh computers, controlling the external SCSI chain.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#ifndef _MAC53C94_H
+#define _MAC53C94_H
+
+extern struct proc_dir_entry proc_scsi_mac53c94;
+
+int mac53c94_detect(Scsi_Host_Template *);
+int mac53c94_command(Scsi_Cmnd *);
+int mac53c94_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int mac53c94_abort(Scsi_Cmnd *);
+int mac53c94_reset(Scsi_Cmnd *, unsigned int);
+
+#define SCSI_MAC53C94 { \
+ NULL, /* next */ \
+ NULL, /* usage_count */ \
+ &proc_scsi_mac53c94, /* proc_dir */ \
+ NULL, /* proc_info */ \
+ "53C94", /* name */ \
+ mac53c94_detect, /* detect */ \
+ NULL, /* release */ \
+ NULL, /* info */ \
+ mac53c94_command, /* command */ \
+ mac53c94_queue, /* queuecommand */ \
+ mac53c94_abort, /* abort */ \
+ mac53c94_reset, /* reset */ \
+ NULL, /* slave_attach */ \
+ NULL, /* bios_param */ \
+ 1, /* can_queue */ \
+ 7, /* this_id */ \
+ SG_ALL, /* sg_tablesize */ \
+ 1, /* cmd_per_lun */ \
+ 0, /* present */ \
+ 0, /* unchecked_isa_dma */ \
+ DISABLE_CLUSTERING, /* use_clustering */ \
+}
+
+/*
+ * Registers in the 53C94 controller.
+ */
+
+struct mac53c94_regs {
+ unsigned char count_lo;
+ char pad0[15];
+ unsigned char count_mid;
+ char pad1[15];
+ unsigned char fifo;
+ char pad2[15];
+ unsigned char command;
+ char pad3[15];
+ unsigned char status;
+ char pad4[15];
+ unsigned char interrupt;
+ char pad5[15];
+ unsigned char seqstep;
+ char pad6[15];
+ unsigned char flags;
+ char pad7[15];
+ unsigned char config1;
+ char pad8[15];
+ unsigned char clk_factor;
+ char pad9[15];
+ unsigned char test;
+ char pad10[15];
+ unsigned char config2;
+ char pad11[15];
+ unsigned char config3;
+ char pad12[15];
+ unsigned char config4;
+ char pad13[15];
+ unsigned char count_hi;
+ char pad14[15];
+ unsigned char fifo_res;
+ char pad15[15];
+};
+
+/*
+ * Alternate functions for some registers.
+ */
+#define dest_id status
+#define sel_timeout interrupt
+#define sync_period seqstep
+#define sync_offset flags
+
+/*
+ * Bits in command register.
+ */
+#define CMD_DMA_MODE 0x80
+#define CMD_MODE_MASK 0x70
+#define CMD_MODE_INIT 0x10
+#define CMD_MODE_TARG 0x20
+#define CMD_MODE_DISC 0x40
+
+#define CMD_NOP 0
+#define CMD_FLUSH 1
+#define CMD_RESET 2
+#define CMD_SCSI_RESET 3
+
+#define CMD_XFER_DATA 0x10
+#define CMD_I_COMPLETE 0x11
+#define CMD_ACCEPT_MSG 0x12
+#define CMD_XFER_PAD 0x18
+#define CMD_SET_ATN 0x1a
+#define CMD_CLR_ATN 0x1b
+
+#define CMD_SEND_MSG 0x20
+#define CMD_SEND_STATUS 0x21
+#define CMD_SEND_DATA 0x22
+#define CMD_DISC_SEQ 0x23
+#define CMD_TERMINATE 0x24
+#define CMD_T_COMPLETE 0x25
+#define CMD_DISCONNECT 0x27
+#define CMD_RECV_MSG 0x28
+#define CMD_RECV_CDB 0x29
+#define CMD_RECV_DATA 0x2a
+#define CMD_RECV_CMD 0x2b
+#define CMD_ABORT_DMA 0x04
+
+#define CMD_RESELECT 0x40
+#define CMD_SELECT 0x41
+#define CMD_SELECT_ATN 0x42
+#define CMD_SELATN_STOP 0x43
+#define CMD_ENABLE_SEL 0x44
+#define CMD_DISABLE_SEL 0x45
+#define CMD_SEL_ATN3 0x46
+#define CMD_RESEL_ATN3 0x47
+
+/*
+ * Bits in status register.
+ */
+#define STAT_IRQ 0x80
+#define STAT_ERROR 0x40
+#define STAT_PARITY 0x20
+#define STAT_TC_ZERO 0x10
+#define STAT_DONE 0x08
+#define STAT_PHASE 0x07
+#define STAT_MSG 0x04
+#define STAT_CD 0x02
+#define STAT_IO 0x01
+
+/*
+ * Bits in interrupt register.
+ */
+#define INTR_RESET 0x80 /* SCSI bus was reset */
+#define INTR_ILL_CMD 0x40 /* illegal command */
+#define INTR_DISCONNECT 0x20 /* we got disconnected */
+#define INTR_BUS_SERV 0x10 /* bus service requested */
+#define INTR_DONE 0x08 /* function completed */
+#define INTR_RESELECTED 0x04 /* we were reselected */
+#define INTR_SEL_ATN 0x02 /* we were selected, ATN asserted */
+#define INTR_SELECT 0x01 /* we were selected, ATN negated */
+
+/*
+ * Encoding for the select timeout.
+ */
+#define TIMO_VAL(x) ((x) * 5000 / 7682)
+
+/*
+ * Bits in sequence step register.
+ */
+#define SS_MASK 7
+#define SS_ARB_SEL 0 /* Selection & arbitration complete */
+#define SS_MSG_SENT 1 /* One message byte sent */
+#define SS_NOT_CMD 2 /* Not in command phase */
+#define SS_PHASE_CHG 3 /* Early phase change, cmd bytes lost */
+#define SS_DONE 4 /* Command was sent OK */
+
+/*
+ * Encoding for sync transfer period.
+ */
+#define SYNCP_MASK 0x1f
+#define SYNCP_MIN 4
+#define SYNCP_MAX 31
+
+/*
+ * Bits in flags register.
+ */
+#define FLAGS_FIFO_LEV 0x1f
+#define FLAGS_SEQ_STEP 0xe0
+
+/*
+ * Encoding for sync offset.
+ */
+#define SYNCO_MASK 0x0f
+#define SYNCO_ASS_CTRL 0x30 /* REQ/ACK assertion control */
+#define SYNCO_NEG_CTRL 0xc0 /* REQ/ACK negation control */
+
+/*
+ * Bits in config1 register.
+ */
+#define CF1_SLOW_CABLE 0x80 /* Slow cable mode */
+#define CF1_NO_RES_REP 0x40 /* Disable SCSI reset reports */
+#define CF1_PAR_TEST 0x20 /* Parity test mode enable */
+#define CF1_PAR_ENABLE 0x10 /* Enable parity checks */
+#define CF1_TEST 0x08 /* Chip tests */
+#define CF1_MY_ID 0x07 /* Controller's address on bus */
+
+/*
+ * Encoding for clk_factor register.
+ */
+#define CLKF_MASK 7
+#define CLKF_VAL(freq) ((((freq) + 4999999) / 5000000) & CLKF_MASK)
+
+/*
+ * Bits in test mode register.
+ */
+#define TEST_TARGET 1 /* target test mode */
+#define TEST_INITIATOR 2 /* initiator test mode */
+#define TEST_TRISTATE 4 /* tristate (hi-z) test mode */
+
+/*
+ * Bits in config2 register.
+ */
+#define CF2_RFB 0x80
+#define CF2_FEATURE_EN 0x40 /* enable features / phase latch */
+#define CF2_BYTECTRL 0x20
+#define CF2_DREQ_HIZ 0x10
+#define CF2_SCSI2 0x08
+#define CF2_PAR_ABORT 0x04 /* bad parity target abort */
+#define CF2_REG_PARERR 0x02 /* register parity error */
+#define CF2_DMA_PARERR 0x01 /* DMA parity error */
+
+/*
+ * Bits in the config3 register.
+ */
+#define CF3_ID_MSG_CHK 0x80
+#define CF3_3B_MSGS 0x40
+#define CF3_CDB10 0x20
+#define CF3_FASTSCSI 0x10 /* enable fast SCSI support */
+#define CF3_FASTCLOCK 0x08
+#define CF3_SAVERESID 0x04
+#define CF3_ALT_DMA 0x02
+#define CF3_THRESH_8 0x01
+
+/*
+ * Bits in the config4 register.
+ */
+#define CF4_EAN 0x04
+#define CF4_TEST 0x02
+#define CF4_BBTE 0x01
+
+#endif /* _MAC53C94_H */
diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
new file mode 100644
index 000000000..501aff777
--- /dev/null
+++ b/drivers/scsi/mesh.c
@@ -0,0 +1,1319 @@
+/*
+ * SCSI low-level driver for the MESH (Macintosh Enhanced SCSI Hardware)
+ * bus adaptor found on Power Macintosh computers.
+ * We assume the MESH is connected to a DBDMA (descriptor-based DMA)
+ * controller.
+ *
+ * Paul Mackerras, August 1996.
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/blk.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
+#include <asm/dbdma.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/system.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "mesh.h"
+
+#if 0
+#undef KERN_DEBUG
+#define KERN_DEBUG KERN_WARNING
+#endif
+
+#if CONFIG_SCSI_MESH_SYNC_RATE == 0
+int mesh_sync_period = 100;
+int mesh_sync_offset = 0;
+#else
+int mesh_sync_period = 1000 / CONFIG_SCSI_MESH_SYNC_RATE; /* ns */
+int mesh_sync_offset = 15;
+#endif
+
+int mesh_sync_targets = 0xff; /* targets to set synchronous (bitmap) */
+int mesh_resel_targets = 0xff; /* targets that we let disconnect (bitmap) */
+int mesh_debug_targets = 0; /* print debug for these targets */
+
+#define ALLOW_SYNC(tgt) ((mesh_sync_targets >> (tgt)) & 1)
+#define ALLOW_RESEL(tgt) ((mesh_resel_targets >> (tgt)) & 1)
+#define ALLOW_DEBUG(tgt) ((mesh_debug_targets >> (tgt)) & 1)
+#define DEBUG_TARGET(cmd) ((cmd) && ALLOW_DEBUG((cmd)->target))
+
+struct proc_dir_entry proc_scsi_mesh = {
+ PROC_SCSI_MESH, 4, "mesh",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+enum mesh_phase {
+ idle,
+ arbitrating,
+ selecting,
+ commanding,
+ dataing,
+ statusing,
+ busfreeing,
+ disconnecting,
+ reselecting
+};
+
+enum msg_phase {
+ msg_none,
+ msg_out,
+ msg_out_xxx,
+ msg_out_last,
+ msg_in,
+};
+
+enum sdtr_phase {
+ do_sdtr,
+ sdtr_sent,
+ sdtr_done
+};
+
+struct mesh_target {
+ enum sdtr_phase sdtr_state;
+ enum mesh_phase phase;
+ int sync_params;
+ int data_goes_out;
+ Scsi_Cmnd *current_req;
+ u32 saved_ptr;
+};
+
+struct mesh_state {
+ volatile struct mesh_regs *mesh;
+ int meshintr;
+ volatile struct dbdma_regs *dma;
+ int dmaintr;
+ struct Scsi_Host *host;
+ struct mesh_state *next;
+ Scsi_Cmnd *request_q;
+ Scsi_Cmnd *request_qtail;
+ enum mesh_phase phase; /* what we're currently trying to do */
+ enum msg_phase msgphase;
+ int conn_tgt; /* target we're connected to */
+ Scsi_Cmnd *current_req; /* req we're currently working on */
+ int data_ptr;
+ int data_goes_out; /* guess as to data direction */
+ int dma_started;
+ int dma_count;
+ int expect_reply;
+ int n_msgin;
+ u8 msgin[16];
+ int n_msgout;
+ int last_n_msgout;
+ u8 msgout[16];
+ struct dbdma_cmd *dma_cmds; /* space for dbdma commands, aligned */
+ int clk_freq;
+ struct mesh_target tgts[8];
+ struct tq_struct tqueue;
+ Scsi_Cmnd *completed_q;
+ Scsi_Cmnd *completed_qtail;
+};
+
+static struct mesh_state *all_meshes;
+
+static void mesh_init(struct mesh_state *);
+static int mesh_notify_reboot(struct notifier_block *, unsigned long, void *);
+static void mesh_dump_regs(struct mesh_state *);
+static void mesh_start(struct mesh_state *);
+static void finish_cmds(void *);
+static void add_sdtr_msg(struct mesh_state *);
+static void set_sdtr(struct mesh_state *, int, int);
+static void start_phase(struct mesh_state *);
+static void get_msgin(struct mesh_state *);
+static int msgin_length(struct mesh_state *);
+static void cmd_complete(struct mesh_state *);
+static void phase_mismatch(struct mesh_state *);
+static void reselected(struct mesh_state *);
+static void handle_reset(struct mesh_state *);
+static void mesh_interrupt(int, void *, struct pt_regs *);
+static void handle_msgin(struct mesh_state *);
+static void mesh_done(struct mesh_state *);
+static void mesh_completed(struct mesh_state *, Scsi_Cmnd *);
+static void set_dma_cmds(struct mesh_state *, Scsi_Cmnd *);
+static void halt_dma(struct mesh_state *);
+static int data_goes_out(Scsi_Cmnd *);
+
+static struct notifier_block mesh_notifier = {
+ mesh_notify_reboot,
+ NULL,
+ 0
+};
+
+int
+mesh_detect(Scsi_Host_Template *tp)
+{
+ struct device_node *mesh;
+ int nmeshes, tgt, *cfp, minper;
+ struct mesh_state *ms, **prev_statep;
+ struct Scsi_Host *mesh_host;
+ void *dma_cmd_space;
+
+ nmeshes = 0;
+ prev_statep = &all_meshes;
+ for (mesh = find_devices("mesh"); mesh != 0; mesh = mesh->next) {
+ if (mesh->n_addrs != 2 || mesh->n_intrs != 2)
+ panic("mesh: expected 2 addrs and intrs (got %d/%d)",
+ mesh->n_addrs, mesh->n_intrs);
+ mesh_host = scsi_register(tp, sizeof(struct mesh_state));
+ if (mesh_host == 0)
+ panic("couldn't register mesh host");
+ mesh_host->unique_id = nmeshes;
+ note_scsi_host(mesh, mesh_host);
+
+ ms = (struct mesh_state *) mesh_host->hostdata;
+ if (ms == 0)
+ panic("no mesh state");
+ memset(ms, 0, sizeof(*ms));
+ ms->host = mesh_host;
+ ms->mesh = (volatile struct mesh_regs *)
+ mesh->addrs[0].address;
+ ms->meshintr = mesh->intrs[0];
+ ms->dma = (volatile struct dbdma_regs *)
+ mesh->addrs[1].address;
+ ms->dmaintr = mesh->intrs[1];
+
+ /* Space for dma command list: +1 for stop command,
+ +1 to allow for aligning. */
+ dma_cmd_space = kmalloc((mesh_host->sg_tablesize + 2) *
+ sizeof(struct dbdma_cmd), GFP_KERNEL);
+ if (dma_cmd_space == 0)
+ panic("mesh: couldn't allocate dma command space");
+ ms->dma_cmds = (struct dbdma_cmd *) DBDMA_ALIGN(dma_cmd_space);
+ memset(ms->dma_cmds, 0, (mesh_host->sg_tablesize + 1)
+ * sizeof(struct dbdma_cmd));
+
+ ms->current_req = 0;
+ for (tgt = 0; tgt < 8; ++tgt) {
+ ms->tgts[tgt].sdtr_state = do_sdtr;
+ ms->tgts[tgt].sync_params = ASYNC_PARAMS;
+ ms->tgts[tgt].current_req = 0;
+ }
+
+ ms->tqueue.routine = finish_cmds;
+ ms->tqueue.data = ms;
+
+ *prev_statep = ms;
+ prev_statep = &ms->next;
+
+ if (request_irq(ms->meshintr, mesh_interrupt, 0, "MESH", ms)) {
+ printk(KERN_ERR "MESH: can't get irq %d\n", ms->meshintr);
+ }
+
+ cfp = (int *) get_property(mesh, "clock-frequency", NULL);
+ if (cfp) {
+ ms->clk_freq = *cfp;
+ } else {
+ printk(KERN_INFO "mesh: assuming 50MHz clock frequency\n");
+ ms->clk_freq = 50000000;
+ }
+ /* The maximum sync rate is clock / 5; increase
+ mesh_sync_period if necessary. */
+ minper = 1000000000 / (ms->clk_freq / 5); /* ns */
+ if (mesh_sync_period < minper)
+ mesh_sync_period = minper;
+
+ mesh_init(ms);
+
+ ++nmeshes;
+ }
+ if (nmeshes > 0)
+ register_reboot_notifier(&mesh_notifier);
+
+ return nmeshes;
+}
+
+int
+mesh_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+ unsigned long flags;
+ struct mesh_state *ms;
+
+#if 0
+ if (data_goes_out(cmd)) {
+ printk(KERN_DEBUG "mesh_queue %p: command is", cmd);
+ for (i = 0; i < cmd->cmd_len; ++i)
+ printk(" %.2x", cmd->cmnd[i]);
+ printk("\n" KERN_DEBUG "use_sg=%d request_bufflen=%d request_buffer=%p\n",
+ cmd->use_sg, cmd->request_bufflen, cmd->request_buffer);
+ }
+#endif
+
+ cmd->scsi_done = done;
+ cmd->host_scribble = NULL;
+
+ ms = (struct mesh_state *) cmd->host->hostdata;
+
+ save_flags(flags);
+ cli();
+ if (ms->request_q == NULL)
+ ms->request_q = cmd;
+ else
+ ms->request_qtail->host_scribble = (void *) cmd;
+ ms->request_qtail = cmd;
+
+ if (ms->phase == idle)
+ mesh_start(ms);
+
+ restore_flags(flags);
+ return 0;
+}
+
+int
+mesh_abort(Scsi_Cmnd *cmd)
+{
+ printk(KERN_DEBUG "mesh_abort(%p)\n", cmd);
+ mesh_dump_regs((struct mesh_state *)(cmd->host->hostdata));
+ return SCSI_ABORT_SNOOZE;
+}
+
+static void
+mesh_dump_regs(struct mesh_state *ms)
+{
+ volatile struct mesh_regs *mr = ms->mesh;
+ volatile struct dbdma_regs *md = ms->dma;
+ int t;
+ struct mesh_target *tp;
+
+ printk(KERN_DEBUG "mesh: state at %p, regs at %p, dma at %p\n",
+ ms, mr, md);
+ printk(KERN_DEBUG " ct=%4x seq=%2x bs=%4x fc=%2x exc=%2x err=%2x sp=%2x\n",
+ (mr->count_hi << 8) + mr->count_lo, mr->sequence,
+ (mr->bus_status1 << 8) + mr->bus_status0, mr->fifo_count,
+ mr->exception, mr->error, mr->sync_params);
+ printk(KERN_DEBUG " dma stat=%x cmdptr=%x\n",
+ in_le32(&md->status), in_le32(&md->cmdptr));
+ printk(KERN_DEBUG " phase=%d msgphase=%d conn_tgt=%d data_ptr=%d\n",
+ ms->phase, ms->msgphase, ms->conn_tgt, ms->data_ptr);
+ printk(KERN_DEBUG " goes_out=%d dma_st=%d dma_ct=%d n_msgout=%d\n",
+ ms->data_goes_out, ms->dma_started, ms->dma_count, ms->n_msgout);
+ for (t = 0; t < 8; ++t) {
+ tp = &ms->tgts[t];
+ if (tp->current_req == NULL)
+ continue;
+ printk(KERN_DEBUG " target %d: req=%p phase=%d saved_ptr=%d\n",
+ t, tp->current_req, tp->phase, tp->saved_ptr);
+ }
+}
+
+int
+mesh_reset(Scsi_Cmnd *cmd, unsigned how)
+{
+ struct mesh_state *ms = (struct mesh_state *) cmd->host->hostdata;
+ volatile struct mesh_regs *mr = ms->mesh;
+ volatile struct dbdma_regs *md = ms->dma;
+ unsigned long flags;
+ int ret;
+
+ printk(KERN_DEBUG "mesh_reset %x\n", how);
+ ret = SCSI_RESET_BUS_RESET;
+ save_flags(flags);
+ cli();
+ out_8(&mr->exception, 0xff); /* clear all exception bits */
+ out_8(&mr->error, 0xff); /* clear all error bits */
+ out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+ if (how & SCSI_RESET_SUGGEST_HOST_RESET) {
+ out_8(&mr->sequence, SEQ_RESETMESH);
+ ret |= SCSI_RESET_HOST_RESET;
+ udelay(1);
+ out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ }
+ out_8(&mr->bus_status1, BS1_RST); /* assert RST */
+ udelay(30); /* leave it on for >= 25us */
+ out_8(&mr->bus_status1, 0); /* negate RST */
+#ifdef DO_ASYNC_RESET
+ if (how & SCSI_RESET_ASYNCHRONOUS) {
+ restore_flags(flags);
+ ret |= SCSI_RESET_PENDING;
+ } else
+#endif
+ {
+ out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ handle_reset(ms);
+ restore_flags(flags);
+ finish_cmds(ms);
+ ret |= SCSI_RESET_SUCCESS;
+ }
+ return ret;
+}
+
+/*
+ * If we leave drives set for synchronous transfers (especially
+ * CDROMs), and reboot to MacOS, it gets confused, poor thing.
+ * So, on reboot we reset the SCSI bus.
+ */
+static int
+mesh_notify_reboot(struct notifier_block *this, unsigned long code, void *x)
+{
+ struct mesh_state *ms;
+ volatile struct mesh_regs *mr;
+
+ if (code == SYS_DOWN || code == SYS_HALT) {
+ printk(KERN_INFO "resetting MESH scsi bus(es)\n");
+ for (ms = all_meshes; ms != 0; ms = ms->next) {
+ mr = ms->mesh;
+ out_8(&mr->intr_mask, 0);
+ out_8(&mr->interrupt,
+ INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ out_8(&mr->bus_status1, BS1_RST);
+ udelay(30);
+ out_8(&mr->bus_status1, 0);
+ }
+ }
+ return NOTIFY_DONE;
+}
+
+int
+mesh_command(Scsi_Cmnd *cmd)
+{
+ printk(KERN_WARNING "whoops... mesh_command called\n");
+ return -1;
+}
+
+static void
+mesh_init(struct mesh_state *ms)
+{
+ volatile struct mesh_regs *mr = ms->mesh;
+ volatile struct dbdma_regs *md = ms->dma;
+
+ out_8(&mr->interrupt, 0xff); /* clear all interrupt bits */
+ out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ out_8(&mr->source_id, ms->host->this_id);
+ out_8(&mr->sel_timeout, 25); /* 250ms */
+ out_8(&mr->sync_params, ASYNC_PARAMS); /* asynchronous initially */
+ out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+}
+
+/*
+ * Start the next command for a MESH.
+ * Should be called with interrupts disabled.
+ */
+static void
+mesh_start(struct mesh_state *ms)
+{
+ Scsi_Cmnd *cmd, *prev, *next;
+ volatile struct mesh_regs *mr = ms->mesh;
+
+ if (ms->phase != idle || ms->current_req != NULL)
+ panic("inappropriate mesh_start (ms=%p)", ms);
+
+ prev = NULL;
+ for (cmd = ms->request_q; ; cmd = (Scsi_Cmnd *) cmd->host_scribble) {
+ if (cmd == NULL)
+ return;
+ if (ms->tgts[cmd->target].current_req == NULL)
+ break;
+ prev = cmd;
+ }
+ next = (Scsi_Cmnd *) cmd->host_scribble;
+ if (prev == NULL)
+ ms->request_q = next;
+ else
+ prev->host_scribble = (void *) next;
+ if (next == NULL)
+ ms->request_qtail = prev;
+
+ ms->current_req = cmd;
+ ms->data_goes_out = data_goes_out(cmd);
+ ms->tgts[cmd->target].current_req = cmd;
+
+#if 1
+ if (DEBUG_TARGET(cmd)) {
+ int i;
+ printk(KERN_DEBUG "mesh_start: %p ser=%lu tgt=%d cmd=",
+ cmd, cmd->serial_number, cmd->target);
+ for (i = 0; i < cmd->cmd_len; ++i)
+ printk(" %x", cmd->cmnd[i]);
+ printk(" use_sg=%d buffer=%p bufflen=%u\n",
+ cmd->use_sg, cmd->request_buffer, cmd->request_bufflen);
+ }
+#endif
+
+ /* Off we go */
+ out_8(&mr->sequence, SEQ_ARBITRATE);
+
+ ms->phase = arbitrating;
+ ms->msgphase = msg_none;
+ ms->data_ptr = 0;
+ ms->dma_started = 0;
+ ms->n_msgout = 0;
+ ms->last_n_msgout = 0;
+ ms->expect_reply = 0;
+ ms->conn_tgt = cmd->target;
+ ms->tgts[cmd->target].saved_ptr = 0;
+}
+
+static void
+finish_cmds(void *data)
+{
+ struct mesh_state *ms = data;
+ Scsi_Cmnd *cmd;
+ unsigned long flags;
+
+ for (;;) {
+ save_flags(flags);
+ cli();
+ cmd = ms->completed_q;
+ if (cmd == NULL) {
+ restore_flags(flags);
+ break;
+ }
+ ms->completed_q = (Scsi_Cmnd *) cmd->host_scribble;
+ restore_flags(flags);
+ (*cmd->scsi_done)(cmd);
+ }
+}
+
+static inline void
+add_sdtr_msg(struct mesh_state *ms)
+{
+ int i = ms->n_msgout;
+
+ ms->msgout[i] = EXTENDED_MESSAGE;
+ ms->msgout[i+1] = 3;
+ ms->msgout[i+2] = EXTENDED_SDTR;
+ ms->msgout[i+3] = mesh_sync_period/4;
+ ms->msgout[i+4] = (ALLOW_SYNC(ms->conn_tgt)? mesh_sync_offset: 0);
+ ms->n_msgout = i + 5;
+}
+
+static void
+set_sdtr(struct mesh_state *ms, int period, int offset)
+{
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+ volatile struct mesh_regs *mr = ms->mesh;
+ int v, tr;
+
+ tp->sdtr_state = sdtr_done;
+ if (offset == 0) {
+ /* asynchronous */
+ if (SYNC_OFF(tp->sync_params))
+ printk(KERN_INFO "mesh: target %d now asynchronous\n",
+ ms->conn_tgt);
+ tp->sync_params = ASYNC_PARAMS;
+ out_8(&mr->sync_params, ASYNC_PARAMS);
+ return;
+ }
+ /*
+ * We need to compute ceil(clk_freq * period / 500e6) - 2
+ * without incurring overflow.
+ */
+ v = (ms->clk_freq / 5000) * period;
+ if (v <= 250000) {
+ /* special case: sync_period == 5 * clk_period */
+ v = 0;
+ /* units of tr are 100kB/s */
+ tr = (ms->clk_freq + 250000) / 500000;
+ } else {
+ /* sync_period == (v + 2) * 2 * clk_period */
+ v = (v + 99999) / 100000 - 2;
+ if (v > 15)
+ v = 15; /* oops */
+ tr = ((ms->clk_freq / (v + 2)) + 199999) / 200000;
+ }
+ if (offset > 15)
+ offset = 15; /* can't happen */
+ tp->sync_params = SYNC_PARAMS(offset, v);
+ out_8(&mr->sync_params, tp->sync_params);
+ printk(KERN_INFO "mesh: target %d synchronous at %d.%d MB/s\n",
+ ms->conn_tgt, tr/10, tr%10);
+}
+
+static void
+start_phase(struct mesh_state *ms)
+{
+ int i, seq, nb;
+ volatile struct mesh_regs *mr = ms->mesh;
+ volatile struct dbdma_regs *md = ms->dma;
+ Scsi_Cmnd *cmd = ms->current_req;
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+
+ if (cmd == 0) {
+ printk(KERN_ERR "mesh: start_phase but no cmd?\n");
+ return;
+ }
+ seq = SEQ_ACTIVE_NEG + (ms->n_msgout? SEQ_ATN: 0);
+ switch (ms->msgphase) {
+ case msg_none:
+ break;
+
+ case msg_in:
+ out_8(&mr->count_hi, 0);
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_MSGIN + seq);
+ ms->n_msgin = 0;
+ return;
+
+ case msg_out:
+ /*
+ * To make sure ATN drops before we assert ACK for
+ * the last byte of the message, we have to do the
+ * last byte specially.
+ */
+ if (DEBUG_TARGET(cmd)) {
+ printk(KERN_DEBUG "mesh: sending %d msg bytes:",
+ ms->n_msgout);
+ for (i = 0; i < ms->n_msgout; ++i)
+ printk(" %x", ms->msgout[i]);
+ printk("\n");
+ }
+ out_8(&mr->count_hi, 0);
+ if (ms->n_msgout == 1) {
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_MSGOUT + SEQ_ACTIVE_NEG);
+ udelay(1);
+ out_8(&mr->fifo, ms->msgout[0]);
+ ms->msgphase = msg_out_last;
+ } else {
+ out_8(&mr->count_lo, ms->n_msgout - 1);
+ out_8(&mr->sequence, SEQ_MSGOUT + seq);
+ for (i = 0; i < ms->n_msgout - 1; ++i)
+ out_8(&mr->fifo, ms->msgout[i]);
+ }
+ return;
+
+ default:
+ printk(KERN_ERR "mesh bug: start_phase msgphase=%d\n",
+ ms->msgphase);
+ }
+
+ switch (ms->phase) {
+ case selecting:
+ out_8(&mr->dest_id, cmd->target);
+ out_8(&mr->sequence, SEQ_SELECT + SEQ_ATN);
+ break;
+ case commanding:
+ out_8(&mr->sync_params, tp->sync_params);
+ out_8(&mr->count_hi, 0);
+ out_8(&mr->count_lo, cmd->cmd_len);
+ out_8(&mr->sequence, SEQ_COMMAND + seq);
+ for (i = 0; i < cmd->cmd_len; ++i)
+ out_8(&mr->fifo, cmd->cmnd[i]);
+ break;
+ case dataing:
+ /* transfer data, if any */
+ if (!ms->dma_started) {
+ set_dma_cmds(ms, cmd);
+ out_le32(&md->cmdptr, virt_to_phys(ms->dma_cmds));
+ out_le32(&md->control, (RUN << 16) | RUN);
+ ms->dma_started = 1;
+ }
+ nb = ms->dma_count;
+ if (nb > 0xfff0)
+ nb = 0xfff0;
+ ms->dma_count -= nb;
+ ms->data_ptr += nb;
+ out_8(&mr->count_lo, nb);
+ out_8(&mr->count_hi, nb >> 8);
+ out_8(&mr->sequence, (ms->data_goes_out?
+ SEQ_DATAOUT: SEQ_DATAIN) + SEQ_DMA_MODE + seq);
+ break;
+ case statusing:
+ out_8(&mr->count_hi, 0);
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_STATUS + seq);
+ break;
+ case busfreeing:
+ case disconnecting:
+ out_8(&mr->sequence, SEQ_ENBRESEL);
+ udelay(1);
+ out_8(&mr->sequence, SEQ_BUSFREE);
+ break;
+ default:
+ printk(KERN_ERR "mesh: start_phase called with phase=%d\n",
+ ms->phase);
+ }
+
+}
+
+static inline void
+get_msgin(struct mesh_state *ms)
+{
+ volatile struct mesh_regs *mr = ms->mesh;
+ int i, n;
+
+ n = mr->fifo_count;
+ if (n != 0) {
+ i = ms->n_msgin;
+ ms->n_msgin = i + n;
+ for (; n > 0; --n)
+ ms->msgin[i++] = in_8(&mr->fifo);
+ }
+}
+
+static inline int
+msgin_length(struct mesh_state *ms)
+{
+ int b, n;
+
+ n = 1;
+ if (ms->n_msgin > 0) {
+ b = ms->msgin[0];
+ if (b == 1) {
+ /* extended message */
+ n = ms->n_msgin < 2? 2: ms->msgin[1] + 2;
+ } else if (0x20 <= b && b <= 0x2f) {
+ /* 2-byte message */
+ n = 2;
+ }
+ }
+ return n;
+}
+
+static void
+cmd_complete(struct mesh_state *ms)
+{
+ volatile struct mesh_regs *mr = ms->mesh;
+ Scsi_Cmnd *cmd = ms->current_req;
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+ int seq, n, t;
+
+ seq = SEQ_ACTIVE_NEG + (ms->n_msgout? SEQ_ATN: 0);
+ switch (ms->msgphase) {
+ case msg_out_xxx:
+ /* huh? we expected a phase mismatch */
+ ms->n_msgin = 0;
+ ms->msgphase = msg_in;
+ /* fall through */
+
+ case msg_in:
+ /* should have some message bytes in fifo */
+ get_msgin(ms);
+ n = msgin_length(ms);
+ if (ms->n_msgin < n) {
+ out_8(&mr->count_lo, n - ms->n_msgin);
+ out_8(&mr->sequence, SEQ_MSGIN + seq);
+ } else {
+ ms->msgphase = msg_none;
+ handle_msgin(ms);
+ start_phase(ms);
+ }
+ break;
+
+ case msg_out:
+ /*
+ * To get the right timing on ATN wrt ACK, we have
+ * to get the MESH to drop ACK, wait until REQ gets
+ * asserted, then drop ATN. To do this we first
+ * issue a SEQ_MSGOUT with ATN and wait for REQ,
+ * then change the command to a SEQ_MSGOUT w/o ATN.
+ * If we don't see REQ in a reasonable time, we
+ * change the command to SEQ_MSGIN with ATN,
+ * wait for the phase mismatch interrupt, then
+ * issue the SEQ_MSGOUT without ATN.
+ */
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_MSGOUT + SEQ_ACTIVE_NEG + SEQ_ATN);
+ t = 30; /* wait up to 30us */
+ while ((mr->bus_status0 & BS0_REQ) == 0 && --t >= 0)
+ udelay(1);
+ if (mr->bus_status0 & BS0_REQ) {
+ out_8(&mr->sequence, SEQ_MSGOUT + SEQ_ACTIVE_NEG);
+ udelay(1);
+ out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);
+ ms->msgphase = msg_out_last;
+ } else {
+ out_8(&mr->sequence, SEQ_MSGIN + SEQ_ACTIVE_NEG + SEQ_ATN);
+ ms->msgphase = msg_out_xxx;
+ }
+ break;
+
+ case msg_out_last:
+ ms->last_n_msgout = ms->n_msgout;
+ ms->n_msgout = 0;
+ ms->msgphase = ms->expect_reply? msg_in: msg_none;
+ start_phase(ms);
+ break;
+
+ case msg_none:
+ switch (ms->phase) {
+ case selecting:
+ ms->msgout[0] = IDENTIFY(ALLOW_RESEL(cmd->target), cmd->lun);
+ ms->n_msgout = 1;
+ ms->expect_reply = 0;
+ if (tp->sdtr_state == do_sdtr) {
+ /* add SDTR message */
+ add_sdtr_msg(ms);
+ ms->expect_reply = 1;
+ tp->sdtr_state = sdtr_sent;
+ }
+ ms->msgphase = msg_out;
+ /*
+ * We need to wait for REQ before dropping ATN.
+ * We wait for at most 30us, then fall back to
+ * a scheme where we issue a SEQ_COMMAND with ATN,
+ * which will give us a phase mismatch interrupt
+ * when REQ does come, and then we send the message.
+ */
+ t = 30; /* wait up to 30us */
+ while ((mr->bus_status0 & BS0_REQ) == 0) {
+ if (--t < 0) {
+ ms->msgphase = msg_none;
+ break;
+ }
+ udelay(1);
+ }
+ break;
+ case dataing:
+ if (ms->dma_count != 0) {
+ start_phase(ms);
+ return;
+ }
+ halt_dma(ms);
+ break;
+ case statusing:
+ cmd->SCp.Status = mr->fifo;
+ cmd->result = (DID_OK << 16) + cmd->SCp.Status;
+ ms->msgphase = msg_in;
+ if (DEBUG_TARGET(cmd))
+ printk(KERN_DEBUG "mesh: status is %x\n",
+ cmd->SCp.Status);
+ break;
+ case busfreeing:
+ mesh_done(ms);
+ return;
+ case disconnecting:
+ ms->current_req = 0;
+ ms->phase = idle;
+ mesh_start(ms);
+ return;
+ default:
+ break;
+ }
+ ++ms->phase;
+ start_phase(ms);
+ break;
+ }
+}
+
+static void phase_mismatch(struct mesh_state *ms)
+{
+ volatile struct mesh_regs *mr = ms->mesh;
+ int phase;
+
+ phase = mr->bus_status0 & BS0_PHASE;
+ if (ms->msgphase == msg_out_xxx && phase == BP_MSGOUT) {
+ /* output the last byte of the message, without ATN */
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_MSGOUT + SEQ_ACTIVE_NEG);
+ udelay(1);
+ out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);
+ ms->msgphase = msg_out_last;
+ return;
+ }
+
+ if (ms->msgphase == msg_in) {
+ get_msgin(ms);
+ if (ms->n_msgin)
+ handle_msgin(ms);
+ }
+
+ if (ms->dma_started)
+ halt_dma(ms);
+ if (mr->fifo_count) {
+ out_8(&mr->sequence, SEQ_FLUSHFIFO);
+ udelay(1);
+ }
+
+ ms->msgphase = msg_none;
+ switch (phase) {
+ case BP_DATAIN:
+ ms->data_goes_out = 0;
+ ms->phase = dataing;
+ break;
+ case BP_DATAOUT:
+ ms->data_goes_out = 1;
+ ms->phase = dataing;
+ break;
+ case BP_COMMAND:
+ ms->phase = commanding;
+ break;
+ case BP_STATUS:
+ ms->phase = statusing;
+ break;
+ case BP_MSGIN:
+ ms->msgphase = msg_in;
+ ms->n_msgin = 0;
+ break;
+ case BP_MSGOUT:
+ ms->msgphase = msg_out;
+ if (ms->n_msgout == 0) {
+ if (ms->last_n_msgout == 0) {
+ printk(KERN_DEBUG "mesh: no msg to repeat\n");
+ ms->msgout[0] = NOP;
+ ms->last_n_msgout = 1;
+ }
+ ms->n_msgout = ms->last_n_msgout;
+ }
+ break;
+ default:
+ printk(KERN_DEBUG "mesh: unknown scsi phase %x\n", phase);
+ ms->current_req->result = DID_ERROR << 16;
+ mesh_done(ms);
+ return;
+ }
+
+ start_phase(ms);
+}
+
+static void
+reselected(struct mesh_state *ms)
+{
+ volatile struct mesh_regs *mr = ms->mesh;
+ Scsi_Cmnd *cmd = ms->current_req;
+ struct mesh_target *tp;
+ int b, t;
+
+ switch (ms->phase) {
+ case idle:
+ case arbitrating:
+ break;
+ case busfreeing:
+ ms->phase = reselecting;
+ mesh_done(ms);
+ cmd = NULL;
+ break;
+ case disconnecting:
+ cmd = NULL;
+ break;
+ default:
+ printk(KERN_ERR "mesh: reselected in phase %d/%d\n",
+ ms->msgphase, ms->phase);
+ }
+ if (cmd) {
+ /* put the command back on the queue */
+ cmd->host_scribble = (void *) ms->request_q;
+ if (ms->request_q == NULL)
+ ms->request_qtail = cmd;
+ ms->request_q = cmd;
+ tp = &ms->tgts[cmd->target];
+ tp->current_req = NULL;
+ ms->current_req = NULL;
+ }
+
+ /*
+ * Find out who reselected us.
+ */
+ if (mr->fifo_count == 0) {
+ printk(KERN_ERR "mesh: reselection but nothing in fifo?\n");
+ return;
+ }
+ /* get the last byte in the fifo */
+ do {
+ b = in_8(&mr->fifo);
+ } while (in_8(&mr->fifo_count));
+ for (t = 0; t < 8; ++t)
+ if ((b & (1 << t)) != 0 && t != ms->host->this_id)
+ break;
+ if (b != (1 << t) + (1 << ms->host->this_id)) {
+ printk(KERN_ERR "mesh: bad reselection data %x\n", b);
+ return;
+ }
+
+ /*
+ * Set up to continue with that target's transfer.
+ */
+ tp = &ms->tgts[t];
+ if (ALLOW_DEBUG(t)) {
+ printk(KERN_DEBUG "mesh: reselected by target %d\n", t);
+ printk(KERN_DEBUG "mesh: saved_ptr=%x phase=%d cmd=%p\n",
+ tp->saved_ptr, tp->phase, tp->current_req);
+ }
+ if (tp->current_req == NULL) {
+ printk(KERN_ERR "mesh: reselected by tgt %d but no cmd!\n", t);
+ return;
+ }
+ ms->current_req = tp->current_req;
+ ms->phase = tp->phase;
+ ms->msgphase = msg_in;
+ ms->data_goes_out = tp->data_goes_out;
+ ms->data_ptr = tp->saved_ptr;
+ ms->conn_tgt = t;
+ ms->dma_started = 0;
+ ms->n_msgout = 0;
+ ms->last_n_msgout = 0;
+ out_8(&mr->sync_params, tp->sync_params);
+ start_phase(ms);
+}
+
+static void
+handle_reset(struct mesh_state *ms)
+{
+ int tgt;
+ struct mesh_target *tp;
+ Scsi_Cmnd *cmd;
+ volatile struct mesh_regs *mr = ms->mesh;
+
+ for (tgt = 0; tgt < 8; ++tgt) {
+ tp = &ms->tgts[tgt];
+ if ((cmd = tp->current_req) != NULL) {
+ cmd->result = DID_RESET << 16;
+ tp->current_req = NULL;
+ mesh_completed(ms, cmd);
+ }
+ ms->tgts[tgt].sdtr_state = do_sdtr;
+ ms->tgts[tgt].sync_params = ASYNC_PARAMS;
+ }
+ ms->current_req = NULL;
+ while ((cmd = ms->request_q) != NULL) {
+ ms->request_q = (Scsi_Cmnd *) cmd->host_scribble;
+ cmd->result = DID_RESET << 16;
+ mesh_completed(ms, cmd);
+ }
+ ms->phase = idle;
+ out_8(&mr->sync_params, ASYNC_PARAMS);
+}
+
+static void
+mesh_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
+{
+ struct mesh_state *ms = (struct mesh_state *) dev_id;
+ volatile struct mesh_regs *mr = ms->mesh;
+ Scsi_Cmnd *cmd = ms->current_req;
+ int stat, exc, err, intr;
+
+#if 0
+ if (DEBUG_TARGET(cmd))
+ printk(KERN_DEBUG "mesh_intr, bs0=%x int=%x exc=%x err=%x phase=%d msgphase=%d\n",
+ mr->bus_status0, mr->interrupt, mr->exception, mr->error, ms->phase, ms->msgphase);
+#endif
+ while ((intr = in_8(&mr->interrupt)) != 0) {
+ if (intr & INT_ERROR) {
+ stat = DID_BAD_INTR << 16;
+ err = in_8(&mr->error);
+ exc = in_8(&mr->exception);
+ out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ if (err & ERR_SCSIRESET) {
+ /* SCSI bus was reset */
+ printk(KERN_INFO "mesh: SCSI bus reset detected: "
+ "waiting for end...");
+ while ((mr->bus_status1 & BS1_RST) != 0)
+ udelay(1);
+ printk("done\n");
+ handle_reset(ms);
+ /* request_q is empty, no point in mesh_start() */
+ continue;
+ } else if (err & ERR_UNEXPDISC) {
+ /* Unexpected disconnect */
+ printk(KERN_WARNING "mesh: target %d aborted\n",
+ ms->conn_tgt);
+ stat = DID_ABORT << 16;
+ } else if (err & ERR_PARITY) {
+ printk(KERN_ERR "mesh: parity error\n");
+ stat = DID_PARITY << 16;
+ } else if ((err & ERR_SEQERR) && (exc & EXC_RESELECTED)
+ && ms->phase == arbitrating) {
+ /* This can happen if we issue a command to
+ get the bus just after the target
+ reselects us. */
+ static int mesh_resel_seqerr;
+ mesh_resel_seqerr++;
+ reselected(ms);
+ continue;
+ } else {
+ printk(KERN_ERR "mesh: error %x (exc = %x)\n",
+ err, exc);
+ mesh_dump_regs(ms);
+ }
+ if (cmd != 0) {
+ cmd->result = stat;
+ mesh_done(ms);
+ }
+
+ } else if (intr & INT_EXCEPTION) {
+ exc = in_8(&mr->exception);
+ out_8(&mr->interrupt, INT_EXCEPTION | INT_CMDDONE);
+ if (exc & EXC_RESELECTED) {
+ static int mesh_resel_exc;
+ mesh_resel_exc++;
+ reselected(ms);
+ } else if (cmd && exc == EXC_ARBLOST
+ && ms->phase == arbitrating) {
+ printk(KERN_DEBUG "mesh: lost arbitration\n");
+ cmd->result = DID_BUS_BUSY << 16;
+ mesh_done(ms);
+ } else if (cmd && exc == EXC_SELTO && ms->phase == selecting) {
+ /* selection timed out */
+ cmd->result = DID_BAD_TARGET << 16;
+ mesh_done(ms);
+ } else if (cmd && exc == EXC_PHASEMM
+ && (mr->bus_status0 & BS0_REQ) != 0) {
+ /* target wants to do something different:
+ find out what it wants and do it. */
+ phase_mismatch(ms);
+ } else {
+ printk(KERN_ERR "mesh: can't cope with exception %x\n",
+ exc);
+ cmd->result = DID_ERROR << 16;
+ mesh_done(ms);
+ }
+
+ } else if (intr & INT_CMDDONE) {
+ out_8(&mr->interrupt, INT_CMDDONE);
+ cmd_complete(ms);
+ }
+ }
+}
+
+static void
+handle_msgin(struct mesh_state *ms)
+{
+ int i;
+ Scsi_Cmnd *cmd = ms->current_req;
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+
+ if (ms->n_msgin == 0)
+ return;
+ if (DEBUG_TARGET(cmd)) {
+ printk(KERN_DEBUG "got %d message bytes:", ms->n_msgin);
+ for (i = 0; i < ms->n_msgin; ++i)
+ printk(" %x", ms->msgin[i]);
+ printk("\n");
+ }
+
+ ms->expect_reply = 0;
+ ms->n_msgout = 0;
+ if (ms->n_msgin < msgin_length(ms))
+ goto reject;
+ if (cmd)
+ cmd->SCp.Message = ms->msgin[0];
+ switch (ms->msgin[0]) {
+ case COMMAND_COMPLETE:
+ break;
+ case EXTENDED_MESSAGE:
+ switch (ms->msgin[2]) {
+ case EXTENDED_MODIFY_DATA_POINTER:
+ ms->data_ptr += (ms->msgin[3] << 24) + ms->msgin[6]
+ + (ms->msgin[4] << 16) + (ms->msgin[5] << 8);
+ break;
+ case EXTENDED_SDTR:
+ if (tp->sdtr_state != sdtr_sent) {
+ /* reply with an SDTR */
+ add_sdtr_msg(ms);
+ /* limit period to at least his value,
+ offset to no more than his */
+ if (ms->msgout[3] < ms->msgin[3])
+ ms->msgout[3] = ms->msgin[3];
+ if (ms->msgout[4] > ms->msgin[4])
+ ms->msgout[4] = ms->msgin[4];
+ set_sdtr(ms, ms->msgout[3], ms->msgout[4]);
+ ms->msgphase = msg_out;
+ } else {
+ set_sdtr(ms, ms->msgin[3], ms->msgin[4]);
+ }
+ break;
+ default:
+ goto reject;
+ }
+ break;
+ case SAVE_POINTERS:
+ tp->saved_ptr = ms->data_ptr;
+ break;
+ case RESTORE_POINTERS:
+ ms->data_ptr = tp->saved_ptr;
+ break;
+ case DISCONNECT:
+ tp->phase = ms->phase;
+ tp->data_goes_out = ms->data_goes_out;
+ ms->phase = disconnecting;
+ break;
+ case ABORT:
+ break;
+ case MESSAGE_REJECT:
+ if (tp->sdtr_state == sdtr_sent)
+ set_sdtr(ms, 0, 0);
+ break;
+ case NOP:
+ break;
+ default:
+ if (cmd && IDENTIFY_BASE <= ms->msgin[0]
+ && ms->msgin[0] <= IDENTIFY_BASE + 7) {
+ i = ms->msgin[0] - IDENTIFY_BASE;
+ if (i != cmd->lun)
+ printk(KERN_WARNING "mesh: lun mismatch "
+ "(%d != %d) on reselection from "
+ "target %d\n", i, cmd->lun,
+ ms->conn_tgt);
+ break;
+ }
+ goto reject;
+ }
+ return;
+
+ reject:
+ printk(KERN_WARNING "mesh: rejecting message %x from target %d\n",
+ ms->msgin[0], ms->conn_tgt);
+ ms->msgout[0] = MESSAGE_REJECT;
+ ms->n_msgout = 1;
+ ms->msgphase = msg_out;
+}
+
+static void
+mesh_done(struct mesh_state *ms)
+{
+ Scsi_Cmnd *cmd;
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+
+ cmd = ms->current_req;
+ if (DEBUG_TARGET(cmd)) {
+ printk(KERN_DEBUG "mesh_done: result = %x, data_ptr=%d, buflen=%d\n",
+ cmd->result, ms->data_ptr, cmd->request_bufflen);
+ if ((cmd->cmnd[0] == 0 || cmd->cmnd[0] == 0x12 || cmd->cmnd[0] == 3)
+ && cmd->request_buffer != 0) {
+ unsigned char *b = cmd->request_buffer;
+ printk(KERN_DEBUG "buffer = %x %x %x %x %x %x %x %x\n",
+ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+ }
+ }
+ tp->current_req = 0;
+ cmd->SCp.this_residual -= ms->data_ptr;
+ ms->current_req = NULL;
+ mesh_completed(ms, cmd);
+ if (ms->phase != reselecting) {
+ ms->phase = idle;
+ mesh_start(ms);
+ }
+}
+
+static void
+mesh_completed(struct mesh_state *ms, Scsi_Cmnd *cmd)
+{
+ if (ms->completed_q == NULL)
+ ms->completed_q = cmd;
+ else
+ ms->completed_qtail->host_scribble = (void *) cmd;
+ ms->completed_qtail = cmd;
+ cmd->host_scribble = NULL;
+ queue_task(&ms->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+/*
+ * Set up DMA commands for transferring data.
+ */
+static void
+set_dma_cmds(struct mesh_state *ms, Scsi_Cmnd *cmd)
+{
+ int i, dma_cmd, total, off, dtot;
+ struct scatterlist *scl;
+ struct dbdma_cmd *dcmds;
+
+ dma_cmd = ms->data_goes_out? OUTPUT_MORE: INPUT_MORE;
+ dcmds = ms->dma_cmds;
+ dtot = 0;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ if (cmd->use_sg > 0) {
+ total = 0;
+ scl = (struct scatterlist *) cmd->buffer;
+ off = ms->data_ptr;
+ for (i = 0; i < cmd->use_sg; ++i, ++scl) {
+ total += scl->length;
+ if (off >= scl->length) {
+ off -= scl->length;
+ continue;
+ }
+ if (scl->length > 0xffff)
+ panic("mesh: scatterlist element >= 64k");
+ st_le16(&dcmds->req_count, scl->length - off);
+ st_le16(&dcmds->command, dma_cmd);
+ st_le32(&dcmds->phy_addr,
+ virt_to_phys(scl->address) + off);
+ dcmds->xfer_status = 0;
+ ++dcmds;
+ dtot += scl->length - off;
+ off = 0;
+ }
+ } else if (ms->data_ptr < cmd->request_bufflen) {
+ dtot = cmd->request_bufflen - ms->data_ptr;
+ if (dtot > 0xffff)
+ panic("mesh: transfer size >= 64k");
+ st_le16(&dcmds->req_count, dtot);
+ st_le32(&dcmds->phy_addr,
+ virt_to_phys(cmd->request_buffer) + ms->data_ptr);
+ dcmds->xfer_status = 0;
+ ++dcmds;
+ }
+ if (dtot == 0) {
+ /* Either the target has overrun our buffer,
+ or the caller didn't provide a buffer. */
+ static char mesh_extra_buf[64];
+
+ if (cmd->request_bufflen != 0)
+ printk(KERN_DEBUG "mesh: target %d overrun, "
+ "data_ptr=%x total=%x goes_out=%d\n",
+ ms->conn_tgt, ms->data_ptr,
+ cmd->request_bufflen, ms->data_goes_out);
+ dtot = sizeof(mesh_extra_buf);
+ st_le16(&dcmds->req_count, dtot);
+ st_le32(&dcmds->phy_addr, virt_to_phys(mesh_extra_buf));
+ dcmds->xfer_status = 0;
+ ++dcmds;
+ }
+ dma_cmd += OUTPUT_LAST - OUTPUT_MORE;
+ st_le16(&dcmds[-1].command, dma_cmd);
+ memset(dcmds, 0, sizeof(*dcmds));
+ st_le16(&dcmds->command, DBDMA_STOP);
+ ms->dma_count = dtot;
+}
+
+static void
+halt_dma(struct mesh_state *ms)
+{
+ volatile struct dbdma_regs *md = ms->dma;
+ volatile struct mesh_regs *mr = ms->mesh;
+ int t, nb;
+
+ if (!ms->data_goes_out) {
+ /* wait a little while until the fifo drains */
+ t = 50;
+ while (t > 0 && mr->fifo_count != 0
+ && (in_le32(&md->status) & ACTIVE) != 0) {
+ --t;
+ udelay(1);
+ }
+ }
+ out_le32(&md->control, RUN << 16); /* turn off RUN bit */
+ nb = (mr->count_hi << 8) + mr->count_lo;
+ if (ms->data_goes_out)
+ nb += mr->fifo_count;
+ /* nb is the number of bytes not yet transferred
+ to/from the target. */
+ ms->data_ptr -= nb;
+ if (ms->data_ptr < 0) {
+ printk(KERN_ERR "mesh: halt_dma: data_ptr=%d (nb=%d, ms=%p)\n",
+ ms->data_ptr, nb, ms);
+ ms->data_ptr = 0;
+ }
+ ms->dma_started = 0;
+}
+
+/*
+ * Work out whether we expect data to go out from the host adaptor or into it.
+ * (If this information is available from somewhere else in the scsi
+ * code, somebody please let me know :-)
+ */
+static int
+data_goes_out(Scsi_Cmnd *cmd)
+{
+ switch (cmd->cmnd[0]) {
+ case MODE_SELECT:
+ case MODE_SELECT_10:
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12: /* any others? */
+ return 1;
+ default:
+ return 0;
+ }
+}
diff --git a/drivers/scsi/mesh.h b/drivers/scsi/mesh.h
new file mode 100644
index 000000000..c808cc58a
--- /dev/null
+++ b/drivers/scsi/mesh.h
@@ -0,0 +1,159 @@
+/*
+ * mesh.h: definitions for the driver for the MESH SCSI bus adaptor
+ * (Macintosh Enhanced SCSI Hardware) found on Power Macintosh computers.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#ifndef _MESH_H
+#define _MESH_H
+
+extern struct proc_dir_entry proc_scsi_mesh;
+
+int mesh_detect(Scsi_Host_Template *);
+int mesh_command(Scsi_Cmnd *);
+int mesh_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int mesh_abort(Scsi_Cmnd *);
+int mesh_reset(Scsi_Cmnd *, unsigned int);
+
+#define SCSI_MESH { \
+ NULL, /* next */ \
+ NULL, /* usage_count */ \
+ &proc_scsi_mesh, /* proc_dir */ \
+ NULL, /* proc_info */ \
+ "MESH", /* name */ \
+ mesh_detect, /* detect */ \
+ NULL, /* release */ \
+ NULL, /* info */ \
+ mesh_command, /* command */ \
+ mesh_queue, /* queuecommand */ \
+ mesh_abort, /* abort */ \
+ mesh_reset, /* reset */ \
+ NULL, /* slave_attach */ \
+ NULL, /* bios_param */ \
+ 20, /* can_queue */ \
+ 7, /* this_id */ \
+ SG_ALL, /* sg_tablesize */ \
+ 2, /* cmd_per_lun */ \
+ 0, /* present */ \
+ 0, /* unchecked_isa_dma */ \
+ DISABLE_CLUSTERING, /* use_clustering */ \
+}
+
+/*
+ * Registers in the MESH controller.
+ */
+
+struct mesh_regs {
+ unsigned char count_lo;
+ char pad0[15];
+ unsigned char count_hi;
+ char pad1[15];
+ unsigned char fifo;
+ char pad2[15];
+ unsigned char sequence;
+ char pad3[15];
+ unsigned char bus_status0;
+ char pad4[15];
+ unsigned char bus_status1;
+ char pad5[15];
+ unsigned char fifo_count;
+ char pad6[15];
+ unsigned char exception;
+ char pad7[15];
+ unsigned char error;
+ char pad8[15];
+ unsigned char intr_mask;
+ char pad9[15];
+ unsigned char interrupt;
+ char pad10[15];
+ unsigned char source_id;
+ char pad11[15];
+ unsigned char dest_id;
+ char pad12[15];
+ unsigned char sync_params;
+ char pad13[15];
+ unsigned char mesh_id;
+ char pad14[15];
+ unsigned char sel_timeout;
+ char pad15[15];
+};
+
+/* Bits in the sequence register. */
+#define SEQ_DMA_MODE 0x80 /* use DMA for data transfer */
+#define SEQ_TARGET 0x40 /* put the controller into target mode */
+#define SEQ_ATN 0x20 /* assert ATN signal */
+#define SEQ_ACTIVE_NEG 0x10 /* use active negation on REQ/ACK */
+#define SEQ_CMD 0x0f /* command bits: */
+#define SEQ_ARBITRATE 1 /* get the bus */
+#define SEQ_SELECT 2 /* select a target */
+#define SEQ_COMMAND 3 /* send a command */
+#define SEQ_STATUS 4 /* receive status */
+#define SEQ_DATAOUT 5 /* send data */
+#define SEQ_DATAIN 6 /* receive data */
+#define SEQ_MSGOUT 7 /* send a message */
+#define SEQ_MSGIN 8 /* receive a message */
+#define SEQ_BUSFREE 9 /* look for bus free */
+#define SEQ_ENBPARITY 0x0a /* enable parity checking */
+#define SEQ_DISPARITY 0x0b /* disable parity checking */
+#define SEQ_ENBRESEL 0x0c /* enable reselection */
+#define SEQ_DISRESEL 0x0d /* disable reselection */
+#define SEQ_RESETMESH 0x0e /* reset the controller */
+#define SEQ_FLUSHFIFO 0x0f /* clear out the FIFO */
+
+/* Bits in the bus_status0 and bus_status1 registers:
+ these correspond directly to the SCSI bus control signals. */
+#define BS0_REQ 0x20
+#define BS0_ACK 0x10
+#define BS0_ATN 0x08
+#define BS0_MSG 0x04
+#define BS0_CD 0x02
+#define BS0_IO 0x01
+#define BS1_RST 0x80
+#define BS1_BSY 0x40
+#define BS1_SEL 0x20
+
+/* Bus phases defined by the bits in bus_status0 */
+#define BS0_PHASE (BS0_MSG+BS0_CD+BS0_IO)
+#define BP_DATAOUT 0
+#define BP_DATAIN BS0_IO
+#define BP_COMMAND BS0_CD
+#define BP_STATUS (BS0_CD+BS0_IO)
+#define BP_MSGOUT (BS0_MSG+BS0_CD)
+#define BP_MSGIN (BS0_MSG+BS0_CD+BS0_IO)
+
+/* Bits in the exception register. */
+#define EXC_SELWATN 0x20 /* (as target) we were selected with ATN */
+#define EXC_SELECTED 0x10 /* (as target) we were selected w/o ATN */
+#define EXC_RESELECTED 0x08 /* (as initiator) we were reselected */
+#define EXC_ARBLOST 0x04 /* we lost arbitration */
+#define EXC_PHASEMM 0x02 /* SCSI phase mismatch */
+#define EXC_SELTO 0x01 /* selection timeout */
+
+/* Bits in the error register */
+#define ERR_UNEXPDISC 0x40 /* target unexpectedly disconnected */
+#define ERR_SCSIRESET 0x20 /* SCSI bus got reset on us */
+#define ERR_SEQERR 0x10 /* we did something the chip didn't like */
+#define ERR_PARITY 0x01 /* parity error was detected */
+
+/* Bits in the interrupt and intr_mask registers */
+#define INT_ERROR 0x04 /* error interrupt */
+#define INT_EXCEPTION 0x02 /* exception interrupt */
+#define INT_CMDDONE 0x01 /* command done interrupt */
+
+/* Fields in the sync_params register */
+#define SYNC_OFF(x) ((x) >> 4) /* offset field */
+#define SYNC_PER(x) ((x) & 0xf) /* period field */
+#define SYNC_PARAMS(o, p) (((o) << 4) | (p))
+#define ASYNC_PARAMS 2 /* sync_params value for async xfers */
+
+/*
+ * Assuming a clock frequency of 50MHz:
+ *
+ * The transfer period with SYNC_PER(sync_params) == x
+ * is (x + 2) * 40ns, except that x == 0 gives 100ns.
+ *
+ * The units of the sel_timeout register are 10ms.
+ */
+
+
+#endif /* _MESH_H */
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
index e62795b11..9945ece31 100644
--- a/drivers/scsi/ncr53c8xx.c
+++ b/drivers/scsi/ncr53c8xx.c
@@ -36,11 +36,38 @@
** And has been ported to NetBSD by
** Charles M. Hannum <mycroft@gnu.ai.mit.edu>
**
+**-----------------------------------------------------------------------------
+**
+** Brief history
+**
+** December 10 1995 by Gerard Roudier:
+** Initial port to Linux.
+**
+** June 23 1996 by Gerard Roudier:
+** Support for 64 bits architectures (Alpha).
+**
+** November 30 1996 by Gerard Roudier:
+** Support for Fast-20 scsi.
+** Support for large DMA fifo and 128 dwords bursting.
+**
+** February 27 1997 by Gerard Roudier:
+** Support for Fast-40 scsi.
+** Support for on-Board RAM.
+**
+** May 3 1997 by Gerard Roudier:
+** Full support for scsi scripts instructions pre-fetching.
+**
+** May 19 1997 by Richard Waltham <dormouse@farsrobt.demon.co.uk>:
+** Support for NvRAM detection and reading.
+**
+** August 18 1997 by Cort <cort@cs.nmt.edu>:
+** Support for Power/PC (Big Endian).
+**
*******************************************************************************
*/
/*
-** 9 May 1997, version 2.1b
+** 23 August 1997, version 2.5a
**
** Supported SCSI-II features:
** Synchronous negotiation
@@ -51,13 +78,13 @@
** Etc...
**
** Supported NCR chips:
-** 53C810 (NCR BIOS in flash-bios required)
-** 53C815 (~53C810 with on board rom BIOS)
-** 53C820 (Wide, NCR BIOS in flash bios required)
-** 53C825 (Wide, ~53C820 with on board rom BIOS)
-** 53C860 (Narrow fast 20, BIOS required)
-** 53C875 (Wide fast 20 with on board rom BIOS)
-** 53C895 (Ultra2 80 MB/s with on board rom BIOS)
+** 53C810 (8 bits, Fast SCSI-2, no rom BIOS)
+** 53C815 (8 bits, Fast SCSI-2, on board rom BIOS)
+** 53C820 (Wide, Fast SCSI-2, no rom BIOS)
+** 53C825 (Wide, Fast SCSI-2, on board rom BIOS)
+** 53C860 (8 bits, Fast 20, no rom BIOS)
+** 53C875 (Wide, Fast 20, on board rom BIOS)
+** 53C895 (Wide, Fast 40, on board rom BIOS)
**
** Other features:
** Memory mapped IO (linux-1.3.X and above only)
@@ -67,10 +94,6 @@
#define SCSI_NCR_DEBUG_FLAGS (0)
-#define NCR_DATE "pl24 96/12/14"
-
-#define NCR_VERSION (2)
-
#define NCR_GETCC_WITHMSG
/*==========================================================
@@ -191,7 +214,14 @@ typedef u32 u_int32;
#else
#define MAX_LUN (1)
#endif
+
+/*
+** Asynchronous pre-scaler (ns). Shall be 40
+*/
+#ifndef SCSI_NCR_MIN_ASYNC
+#define SCSI_NCR_MIN_ASYNC (40)
+#endif
/*
** The maximum number of jobs scheduled for starting.
@@ -268,8 +298,8 @@ typedef int vm_size_t;
** In the original Bsd driver, vtophys() is called to translate
** data addresses to IO bus addresses. In order to minimize
** change, I decide to define vtophys() as virt_to_bus().
-*
-* FIXME: Bus addresses are _not_ physical addresses.
+**
+** FIXME: Bus addresses are _not_ physical addresses.
*/
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
@@ -286,7 +316,10 @@ typedef int vm_size_t;
** architecture.
*/
-static inline vm_offset_t remap_pci_mem(u_long base, u_long size)
+#ifndef NCR_IOMAPPED
+__initfunc(
+static vm_offset_t remap_pci_mem(u_long base, u_long size)
+)
{
u_long page_base = ((u_long) base) & PAGE_MASK;
u_long page_offs = ((u_long) base) - page_base;
@@ -299,7 +332,9 @@ static inline vm_offset_t remap_pci_mem(u_long base, u_long size)
return (vm_offset_t) (page_remapped ? (page_remapped + page_offs) : 0UL);
}
-static inline void unmap_pci_mem(vm_offset_t vaddr, u_long size)
+__initfunc(
+static void unmap_pci_mem(vm_offset_t vaddr, u_long size)
+)
{
if (vaddr)
#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
@@ -308,6 +343,7 @@ static inline void unmap_pci_mem(vm_offset_t vaddr, u_long size)
vfree((void *) (vaddr & PAGE_MASK));
#endif
}
+#endif /* !NCR_IOMAPPED */
#else /* linux-1.2.13 */
@@ -479,7 +515,8 @@ struct ncr_driver_setup {
unsigned ultra_scsi : 2;
unsigned force_sync_nego: 1;
unsigned reverse_probe: 1;
- unsigned pci_fix_up: 2;
+ unsigned pci_fix_up: 4;
+ u_char use_nvram;
u_char verbose;
u_char default_tags;
u_short default_sync;
@@ -518,9 +555,152 @@ static void ncr53c8xx_intr(int irq, struct pt_regs * regs);
static void ncr53c8xx_timeout(unsigned long np);
+#define initverbose (driver_setup.verbose)
//#define bootverbose (driver_setup.verbose)
#define bootverbose 2
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+/*
+** Symbios NvRAM data format
+*/
+#define SYMBIOS_NVRAM_SIZE 368
+#define SYMBIOS_NVRAM_ADDRESS 0x100
+
+struct Symbios_nvram {
+/* Header 6 bytes */
+ u_short start_marker; /* 0x0000 */
+ u_short byte_count; /* excluding header/trailer */
+ u_short checksum;
+
+/* Controller set up 20 bytes */
+ u_short word0; /* 0x3000 */
+ u_short word2; /* 0x0000 */
+ u_short word4; /* 0x0000 */
+ u_short flags;
+#define SYMBIOS_SCAM_ENABLE (1)
+#define SYMBIOS_PARITY_ENABLE (1<<1)
+#define SYMBIOS_VERBOSE_MSGS (1<<2)
+ u_short flags1;
+#define SYMBIOS_SCAN_HI_LO (1)
+ u_short word10; /* 0x00 */
+ u_short word12; /* 0x00 */
+ u_char host_id;
+ u_char byte15; /* 0x04 */
+ u_short word16; /* 0x0410 */
+ u_short word18; /* 0x0000 */
+
+/* Boot order 14 bytes * 4 */
+ struct Symbios_host{
+ u_char word0; /* 0x0004:ok / 0x0000:nok */
+ u_short device_id; /* PCI device id */
+ u_short vendor_id; /* PCI vendor id */
+ u_char byte6; /* 0x00 */
+ u_char device_fn; /* PCI device/function number << 3*/
+ u_short word8;
+ u_short flags;
+#define SYMBIOS_INIT_SCAN_AT_BOOT (1)
+ u_short io_port; /* PCI io_port address */
+ } host[4];
+
+/* Targets 8 bytes * 16 */
+ struct Symbios_target {
+ u_short flags;
+#define SYMBIOS_DISCONNECT_ENABLE (1)
+#define SYMBIOS_SCAN_AT_BOOT_TIME (1<<1)
+#define SYMBIOS_SCAN_LUNS (1<<2)
+#define SYMBIOS_QUEUE_TAGS_ENABLED (1<<3)
+ u_char bus_width; /* 0x08/0x10 */
+ u_char sync_offset;
+ u_char sync_period; /* 4*period factor */
+ u_char byte6; /* 0x00 */
+ u_short timeout;
+ } target[16];
+ u_char spare_devices[19*8];
+ u_char trailer[6]; /* 0xfe 0xfe 0x00 0x00 0x00 0x00 */
+};
+typedef struct Symbios_nvram Symbios_nvram;
+typedef struct Symbios_host Symbios_host;
+typedef struct Symbios_target Symbios_target;
+
+/*
+** Tekram NvRAM data format.
+*/
+#define TEKRAM_NVRAM_SIZE 64
+#define TEKRAM_NVRAM_ADDRESS 0
+
+struct Tekram_nvram {
+ struct Tekram_target {
+ u_char flags;
+#define TEKRAM_PARITY_CHECK (1)
+#define TEKRAM_SYNC_NEGO (1<<1)
+#define TEKRAM_DISCONNECT_ENABLE (1<<2)
+#define TEKRAM_START_CMD (1<<3)
+#define TEKRAM_TAGGED_COMMANDS (1<<4)
+#define TEKRAM_WIDE_NEGO (1<<5)
+ u_char sync_index;
+ u_short word2;
+ } target[16];
+ u_char host_id;
+ u_char flags;
+#define TEKRAM_MORE_THAN_2_DRIVES (1)
+#define TEKRAM_DRIVES_SUP_1GB (1<<1)
+#define TEKRAM_RESET_ON_POWER_ON (1<<2)
+#define TEKRAM_ACTIVE_NEGATION (1<<3)
+#define TEKRAM_IMMEDIATE_SEEK (1<<4)
+#define TEKRAM_SCAN_LUNS (1<<5)
+#define TEKRAM_REMOVABLE_FLAGS (3<<6) /* 0: disable; 1: boot device; 2:all */
+ u_char boot_delay_index;
+ u_char max_tags_index;
+ u_short flags1;
+#define TEKRAM_F2_F6_ENABLED (1)
+ u_short spare[29];
+};
+typedef struct Tekram_nvram Tekram_nvram;
+typedef struct Tekram_target Tekram_target;
+
+static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21};
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+/*
+** Structures used by ncr53c8xx_detect/ncr53c8xx_pci_init to
+** transmit device configuration to the ncr_attach() function.
+*/
+typedef struct {
+ int bus;
+ u_char device_fn;
+ u_int base;
+ u_int base_2;
+ u_int io_port;
+ int irq;
+/* port and reg fields to use INB, OUTB macros */
+ u_int port;
+ volatile struct ncr_reg *reg;
+} ncr_slot;
+
+typedef struct {
+ int type;
+#define SCSI_NCR_SYMBIOS_NVRAM (1)
+#define SCSI_NCR_TEKRAM_NVRAM (2)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ union {
+ Symbios_nvram Symbios;
+ Tekram_nvram Tekram;
+ } data;
+#endif
+} ncr_nvram;
+
+/*
+** Structure used by ncr53c8xx_detect/ncr53c8xx_pci_init
+** to save data on each detected board for ncr_attach().
+*/
+typedef struct {
+ ncr_slot slot;
+ ncr_chip chip;
+ ncr_nvram *nvram;
+ int attached;
+} ncr_device;
+
/*==========================================================
**
** Debugging tags
@@ -577,79 +757,159 @@ static void ncr53c8xx_timeout(unsigned long np);
/*==========================================================
**
-** Access to the controller chip.
-**
-** If NCR_IOMAPPED is defined, only IO are used by the driver.
+** Big/Little endian support.
**
**==========================================================
*/
/*
-** IO mapped input / ouput
+** If the NCR uses big endian addressing mode over the
+** PCI, actual io register addresses for byte and word
+** accesses must be changed according to lane routing.
+** Btw, ncr_offb() and ncr_offw() macros only apply to
+** constants and so donnot generate bloated code.
*/
-#define IOM_INB(r) inb (np->port + offsetof(struct ncr_reg, r))
-#define IOM_INB_OFF(o) inb (np->port + (o))
-#define IOM_INW(r) inw (np->port + offsetof(struct ncr_reg, r))
-#define IOM_INL(r) inl (np->port + offsetof(struct ncr_reg, r))
-#define IOM_INL_OFF(o) inl (np->port + (o))
+#if defined(SCSI_NCR_BIG_ENDIAN)
+
+#define ncr_offb(o) (((o)&~3)+((~((o)&3))&3))
+#define ncr_offw(o) (((o)&~3)+((~((o)&3))&2))
+
+#else
-#define IOM_OUTB(r, val) outb ((val), np->port+offsetof(struct ncr_reg,r))
-#define IOM_OUTW(r, val) outw ((val), np->port+offsetof(struct ncr_reg,r))
-#define IOM_OUTL(r, val) outl ((val), np->port+offsetof(struct ncr_reg,r))
-#define IOM_OUTL_OFF(o, val) outl ((val), np->port + (o))
+#define ncr_offb(o) (o)
+#define ncr_offw(o) (o)
+
+#endif
/*
-** MEMORY mapped IO input / output
+** If the CPU and the NCR use same endian-ness adressing,
+** no byte reordering is needed for script patching.
+** Macro cpu_to_scr() is to be used for script patching.
+** Macro scr_to_cpu() is to be used for getting a DWORD
+** from the script.
*/
-#define MMIO_INB(r) (*(volatile u8 *)(&np->reg->r))
-#define MMIO_INB_OFF(o) (*(volatile u8 *)((char *)np->reg + (o)))
-#define MMIO_INW(r) (*(volatile u16 *)(&np->reg->r))
-#define MMIO_INL(r) (*(volatile u32 *)(&np->reg->r))
-#define MMIO_INL_OFF(o) (*(volatile u32 *)((char *)np->reg + (o)))
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_le32(dw)
+#define scr_to_cpu(dw) le32_to_cpu(dw)
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
-#define MMIO_OUTB(r, val) (*(volatile u8 *) &np->reg->r = (val))
-#define MMIO_OUTW(r, val) (*(volatile u16 *) &np->reg->r = (val))
-#define MMIO_OUTL(r, val) (*(volatile u32 *) &np->reg->r = (val))
-#define MMIO_OUTL_OFF(o, val) (*(volatile u32 *) ((char *)np->reg + (o)) = (val))
+#define cpu_to_scr(dw) cpu_to_be32(dw)
+#define scr_to_cpu(dw) be32_to_cpu(dw)
+
+#else
+
+#define cpu_to_scr(dw) (dw)
+#define scr_to_cpu(dw) (dw)
+
+#endif
+
+/*==========================================================
+**
+** Access to the controller chip.
+**
+** If NCR_IOMAPPED is defined, only IO are used by the driver.
+**
+**==========================================================
+*/
/*
-** IO mapped input / output
+** If the CPU and the NCR use same endian-ness adressing,
+** no byte reordering is needed for accessing chip io
+** registers. Functions suffixed by '_raw' are assumed
+** to access the chip over the PCI without doing byte
+** reordering. Functions suffixed by '_l2b' are
+** assumed to perform little-endian to big-endian byte
+** reordering, those suffixed by '_b2l' blah, blah,
+** blah, ...
*/
#if defined(NCR_IOMAPPED)
-#define INB(r) IOM_INB(r)
-#define INB_OFF(o) IOM_INB_OFF(o)
-#define INW(r) IOM_INW(r)
-#define INL(r) IOM_INL(r)
-#define INL_OFF(o) IOM_INL_OFF(o)
+/*
+** IO mapped only input / ouput
+*/
+
+#define INB_OFF(o) inb (np->port + ncr_offb(o))
+#define OUTB_OFF(o, val) outb ((val), np->port + ncr_offb(o))
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
-#define OUTB(r, val) IOM_OUTB(r, val)
-#define OUTW(r, val) IOM_OUTW(r, val)
-#define OUTL(r, val) IOM_OUTL(r, val)
-#define OUTL_OFF(o, val) IOM_OUTL_OFF(o, val)
+#define INW_OFF(o) inw_l2b (np->port + ncr_offw(o))
+#define INL_OFF(o) inl_l2b (np->port + (o))
+
+#define OUTW_OFF(o, val) outw_b2l ((val), np->port + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_b2l ((val), np->port + (o))
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) inw_b2l (np->port + ncr_offw(o))
+#define INL_OFF(o) inl_b2l (np->port + (o))
+
+#define OUTW_OFF(o, val) outw_l2b ((val), np->port + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_l2b ((val), np->port + (o))
+
+#else
+
+#define INW_OFF(o) inw_raw (np->port + ncr_offw(o))
+#define INL_OFF(o) inl_raw (np->port + (o))
+
+#define OUTW_OFF(o, val) outw_raw ((val), np->port + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_raw ((val), np->port + (o))
+
+#endif /* ENDIANs */
+
+#else /* defined NCR_IOMAPPED */
/*
-** MEMORY mapped only input / output
+** MEMORY mapped IO input / output
+**
+** FIXME: The NCR code is broken in that it feeds {read,write}[bwl] not
+** with bus addresses.
*/
+#define INB_OFF(o) readb((char *)np->reg + ncr_offb(o))
+#define OUTB_OFF(o, val) writeb((val), (char *)np->reg + ncr_offb(o))
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) readw_l2b((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_l2b((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_b2l((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_b2l((val), (char *)np->reg + (o))
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) readw_b2l((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_b2l((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_l2b((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_l2b((val), (char *)np->reg + (o))
+
#else
-#define INB(r) MMIO_INB(r)
-#define INB_OFF(o) MMIO_INB_OFF(o)
-#define INW(r) MMIO_INW(r)
-#define INL(r) MMIO_INL(r)
-#define INL_OFF(o) MMIO_INL_OFF(o)
+#define INW_OFF(o) readw_raw((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_raw((char *)np->reg + (o))
-#define OUTB(r, val) MMIO_OUTB(r, val)
-#define OUTW(r, val) MMIO_OUTW(r, val)
-#define OUTL(r, val) MMIO_OUTL(r, val)
-#define OUTL_OFF(o, val) MMIO_OUTL_OFF(o, val)
+#define OUTW_OFF(o, val) writew_raw((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_raw((val), (char *)np->reg + (o))
#endif
+#endif /* defined NCR_IOMAPPED */
+
+#define INB(r) INB_OFF (offsetof(struct ncr_reg,r))
+#define INW(r) INW_OFF (offsetof(struct ncr_reg,r))
+#define INL(r) INL_OFF (offsetof(struct ncr_reg,r))
+
+#define OUTB(r, val) OUTB_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTW(r, val) OUTW_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTL(r, val) OUTL_OFF (offsetof(struct ncr_reg,r), (val))
+
/*
** Set bit field ON, OFF
*/
@@ -661,6 +921,7 @@ static void ncr53c8xx_timeout(unsigned long np);
#define OUTONL(r, m) OUTL(r, INL(r) | (m))
#define OUTOFFL(r, m) OUTL(r, INL(r) & ~(m))
+
/*==========================================================
**
** Command control block states.
@@ -808,6 +1069,7 @@ struct usrcmd {
#define UF_TRACE (0x01)
#define UF_NODISC (0x02)
+#define UF_NOSCAN (0x04)
/*---------------------------------------
**
@@ -905,6 +1167,14 @@ struct tcb {
ccb_p hold_cp;
/*
+ ** pointer to ccb used for negotiating.
+ ** Avoid to start a nego for all queued commands
+ ** when tagged command queuing is enabled.
+ */
+
+ ccb_p nego_cp;
+
+ /*
** statistical data
*/
@@ -914,13 +1184,15 @@ struct tcb {
/*
** user settable limits for sync transfer
** and tagged commands.
+ ** These limits are read from the NVRAM if present.
*/
u_char usrsync;
- u_char usrtags;
u_char usrwide;
+ u_char usrtags;
u_char usrflag;
+ u_char numtags;
u_char maxtags;
u_short num_good;
@@ -1087,27 +1359,36 @@ struct head {
** status fields.
*/
- u_char status[8];
+ u_char scr_st[4]; /* script status */
+ u_char status[4]; /* host status. Must be the last */
+ /* DWORD of the CCB header */
};
/*
** The status bytes are used by the host and the script processor.
**
-** The first four byte are copied to the scratchb register
+** The byte corresponding to the host_status must be stored in the
+** last DWORD of the CCB header since it is used for command
+** completion (ncr_wakeup()). Doing so, we are sure that the header
+** has been entirely copied back to the CCB when the host_status is
+** seen complete by the CPU.
+**
+** The last four bytes (status[4]) are copied to the scratchb register
** (declared as scr0..scr3 in ncr_reg.h) just after the select/reselect,
** and copied back just after disconnecting.
** Inside the script the XX_REG are used.
**
-** The last four bytes are used inside the script by "COPY" commands.
+** The first four bytes (scr_st[4]) are used inside the script by
+** "COPY" commands.
** Because source and destination must have the same alignment
-** in a longword, the fields HAVE to be at the choosen offsets.
-** xerr_st (4) 0 (0x34) scratcha
-** sync_st (5) 1 (0x05) sxfer
-** wide_st (7) 3 (0x03) scntl3
+** in a DWORD, the fields HAVE to be at the choosen offsets.
+** xerr_st 0 (0x34) scratcha
+** sync_st 1 (0x05) sxfer
+** wide_st 3 (0x03) scntl3
*/
/*
-** First four bytes (script)
+** Last four bytes (script)
*/
#define QU_REG scr0
#define HS_REG scr1
@@ -1116,7 +1397,7 @@ struct head {
#define PS_REG scr3
/*
-** First four bytes (host)
+** Last four bytes (host)
*/
#define actualquirks phys.header.status[0]
#define host_status phys.header.status[1]
@@ -1124,15 +1405,15 @@ struct head {
#define parity_status phys.header.status[3]
/*
-** Last four bytes (script)
+** First four bytes (script)
*/
-#define xerr_st header.status[4] /* MUST be ==0 mod 4 */
-#define sync_st header.status[5] /* MUST be ==1 mod 4 */
-#define nego_st header.status[6]
-#define wide_st header.status[7] /* MUST be ==3 mod 4 */
+#define xerr_st header.scr_st[0]
+#define sync_st header.scr_st[1]
+#define nego_st header.scr_st[2]
+#define wide_st header.scr_st[3]
/*
-** Last four bytes (host)
+** First four bytes (host)
*/
#define xerr_status phys.xerr_st
#define sync_status phys.sync_st
@@ -1349,13 +1630,13 @@ struct ncb {
int unit; /* Unit number */
char chip_name[8]; /* Chip name */
char inst_name[16]; /* Instance name */
- u_int features; /* Chip features map */
struct timer_list timer; /* Timer link header */
int ncr_cache; /* Cache test variable */
Scsi_Cmnd *waiting_list; /* Waiting list header for commands */
/* that we can't put into the squeue */
u_long settle_time; /* Reset in progess */
u_char release_stage; /* Synchronisation stage on release */
+ u_char verbose; /* Boot verbosity for this controller*/
#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
u_char debug_error_recovery;
u_char stalling;
@@ -1373,6 +1654,8 @@ struct ncb {
*/
u_short device_id;
u_char revision_id;
+
+ u_char sv_scntl0;
u_char sv_scntl3;
u_char sv_dmode;
u_char sv_dcntl;
@@ -1383,6 +1666,8 @@ struct ncb {
u_char sv_stest2;
u_char sv_stest4;
+ u_char rv_scntl0;
+ u_char rv_scntl3;
u_char rv_dmode;
u_char rv_dcntl;
u_char rv_ctest3;
@@ -1390,9 +1675,7 @@ struct ncb {
u_char rv_ctest5;
u_char rv_stest2;
- u_char maxburst;
u_char scsi_mode;
- u_char multiplier;
/*-----------------------------------------------
** Scripts ..
@@ -1451,21 +1734,21 @@ struct ncb {
u_char myaddr;
/*
- ** timing parameters
+ ** Max dwords burst supported by the adapter.
*/
- u_char ns_sync;
- u_char rv_scntl3;
+ u_char maxburst; /* log base 2 of dwords burst */
/*
- ** controller chip dependent maximal offset.
+ ** timing parameters
*/
- u_char maxoffs;
+ u_char minsync; /* Minimum sync period factor */
+ u_char maxsync; /* Maximum sync period factor */
+ u_char maxoffs; /* Max scsi offset */
+ u_char multiplier; /* Clock multiplier (1,2,4) */
+ u_char clock_divn; /* Number of clock divisors */
+ u_long clock_khz; /* SCSI clock frequency in KHz */
+ u_int features; /* Chip features map */
- /*
- ** controller scsi clock frequency and available divisors
- */
- u_long clock_khz;
- int clock_divn;
/*-----------------------------------------------
** Link to the generic SCSI driver
@@ -1498,9 +1781,11 @@ struct ncb {
/*
** Timeout handler
*/
+#if 0
u_long heartbeat;
u_short ticks;
u_short latetime;
+#endif
u_long lasttime;
/*-----------------------------------------------
@@ -1592,7 +1877,7 @@ struct ncb {
** of 825A, 875 and 895 chips.
*/
struct script {
- ncrcmd start [ 7];
+ ncrcmd start [ 4];
ncrcmd start0 [ 2];
ncrcmd start1 [ 3];
ncrcmd startpos [ 1];
@@ -1698,11 +1983,11 @@ static void ncr_script_copy_and_bind
(ncb_p np, ncrcmd *src, ncrcmd *dst, int len);
static void ncr_script_fill (struct script * scr, struct scripth * scripth);
static int ncr_scatter (ccb_p cp, Scsi_Cmnd *cmd);
-static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long usrtags);
-static int ncr_getsync (ncb_p np, u_char fac, u_char *fakp, u_char *scntl3p);
+static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags);
+static void ncr_getsync (ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p);
static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
static void ncr_settags (tcb_p tp, lcb_p lp);
-static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide);
+static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack);
static int ncr_show_msg (u_char * msg);
static int ncr_snooptest (ncb_p np);
static void ncr_timeout (ncb_p np);
@@ -1713,9 +1998,7 @@ static void ncr_start_reset (ncb_p np, int settle_delay);
static void ncr_usercmd (ncb_p np);
#endif
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit,
- ncr_chip *chip, u_int base, u_int io_port,
- int irq, int bus, u_char device_fn);
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device);
static void insert_into_waiting_list(ncb_p np, Scsi_Cmnd *cmd);
static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd);
@@ -1726,6 +2009,11 @@ static void process_waiting_list(ncb_p np, int sts);
#define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
#define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram);
+static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram);
+#endif
+
/*==========================================================
**
**
@@ -1792,13 +2080,14 @@ static void *script_kvars[] __initdata =
static struct script script0 __initdata = {
/*--------------------------< START >-----------------------*/ {
+#if 0
/*
** Claim to be still alive ...
*/
SCR_COPY (sizeof (((struct ncb *)0)->heartbeat)),
KVAR(SCRIPT_KVAR_JIFFIES),
NADDR (heartbeat),
-
+#endif
/*
** Make data structure address invalid.
** clear SIGP.
@@ -3411,7 +3700,8 @@ static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int le
while (src < end) {
- *dst++ = opcode = *src++;
+ opcode = *src++;
+ *dst++ = cpu_to_scr(opcode);
/*
** If we forget to change the length
@@ -3455,8 +3745,8 @@ static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int le
** If PREFETCH feature not enabled, remove
** the NO FLUSH bit if present.
*/
- if ((opcode & SCR_NO_FLUSH) && !(np->features & _F_PFEN)) {
- dst[-1] = (opcode & ~SCR_NO_FLUSH);
+ if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) {
+ dst[-1] = cpu_to_scr(opcode & ~SCR_NO_FLUSH);
++opchanged;
}
break;
@@ -3529,10 +3819,10 @@ static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int le
break;
}
- *dst++ = new;
+ *dst++ = cpu_to_scr(new);
}
} else
- *dst++ = *src++;
+ *dst++ = cpu_to_scr(*src++);
};
if (bootverbose > 1 && opchanged)
@@ -3578,7 +3868,7 @@ struct host_data {
*/
#define PRINT_LUN(np, target, lun) \
-printf("%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
+printf(KERN_INFO "%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
static void PRINT_ADDR(Scsi_Cmnd *cmd)
{
@@ -3587,6 +3877,20 @@ static void PRINT_ADDR(Scsi_Cmnd *cmd)
if (np) PRINT_LUN(np, cmd->target, cmd->lun);
}
+/*==========================================================
+**
+** NCR chip clock divisor table.
+** Divisors are multiplied by 10,000,000 in order to make
+** calculations more simple.
+**
+**==========================================================
+*/
+
+#define _5M 5000000
+static u_long div_10M[] =
+ {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M};
+
+
/*===============================================================
**
** Prepare io register values used by ncr_init() according
@@ -3630,16 +3934,79 @@ static inline void ncr_init_burst(ncb_p np, u_char bc)
}
}
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/*
+** Get target set-up from Symbios format NVRAM.
+*/
+
__initfunc(
-static int ncr_prepare_setting(ncb_p np)
+static void
+ ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram)
+)
+{
+ tcb_p tp = &np->target[target];
+ Symbios_target *tn = &nvram->target[target];
+
+ tp->usrsync = tn->sync_period ? (tn->sync_period + 3) / 4 : 255;
+ tp->usrwide = tn->bus_width == 0x10 ? 1 : 0;
+ tp->usrtags =
+ (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SCSI_NCR_MAX_TAGS : 0;
+
+ if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE))
+ tp->usrflag |= UF_NODISC;
+ if (!(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME))
+ tp->usrflag |= UF_NOSCAN;
+}
+
+/*
+** Get target set-up from Tekram format NVRAM.
+*/
+
+__initfunc(
+static void
+ ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram)
+)
+{
+ tcb_p tp = &np->target[target];
+ struct Tekram_target *tn = &nvram->target[target];
+ int i;
+
+ if (tn->flags & TEKRAM_SYNC_NEGO) {
+ i = tn->sync_index & 0xf;
+ tp->usrsync = i < 12 ? Tekram_sync[i] : 255;
+ }
+
+ tp->usrwide = (tn->flags & TEKRAM_WIDE_NEGO) ? 1 : 0;
+
+ if (tn->flags & TEKRAM_TAGGED_COMMANDS) {
+ tp->usrtags = 2 << nvram->max_tags_index;
+ if (tp->usrtags > SCSI_NCR_MAX_TAGS)
+ tp->usrtags = SCSI_NCR_MAX_TAGS;
+ }
+
+ if (!(tn->flags & TEKRAM_DISCONNECT_ENABLE))
+ tp->usrflag = UF_NODISC;
+
+ /* If any device does not support parity, we will not use this option */
+ if (!(tn->flags & TEKRAM_PARITY_CHECK))
+ np->rv_scntl0 &= ~0x0a; /* SCSI parity checking disabled */
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+__initfunc(
+static int ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
)
{
u_char burst_max;
+ u_long period;
+ int i;
/*
** Save assumed BIOS setting
*/
+ np->sv_scntl0 = INB(nc_scntl0) & 0x0a;
np->sv_scntl3 = INB(nc_scntl3) & 0x07;
np->sv_dmode = INB(nc_dmode) & 0xce;
np->sv_dcntl = INB(nc_dcntl) & 0xa8;
@@ -3651,44 +4018,74 @@ static int ncr_prepare_setting(ncb_p np)
np->sv_stest4 = INB(nc_stest4);
/*
+ ** Wide ?
+ */
+
+ np->maxwide = (np->features & FE_WIDE)? 1 : 0;
+
+ /*
** Get the frequency of the chip's clock.
** Find the right value for scntl3.
*/
- if (np->features & _F_QUAD)
+ if (np->features & FE_QUAD)
np->multiplier = 4;
- else if (np->features & _F_DBLR)
+ else if (np->features & FE_DBLR)
np->multiplier = 2;
else
np->multiplier = 1;
- np->clock_khz = (np->features & _F_CLK80)? 80000 : 40000;
+ np->clock_khz = (np->features & FE_CLK80)? 80000 : 40000;
np->clock_khz *= np->multiplier;
if (np->clock_khz != 40000)
ncr_getclock(np, np->multiplier);
- if (np->clock_khz <= 25000) np->rv_scntl3 = 0x01;
- else if (np->clock_khz <= 37500) np->rv_scntl3 = 0x02;
- else if (np->clock_khz <= 50000) np->rv_scntl3 = 0x03;
- else if (np->clock_khz <= 75000) np->rv_scntl3 = 0x04;
- else if (np->clock_khz <= 100000) np->rv_scntl3 = 0x05;
- else if (np->clock_khz <= 150000) np->rv_scntl3 = 0x06;
- else np->rv_scntl3 = 0x07;
-
/*
- ** Get on-board RAM bus address when supported
- */
- if (np->features & _F_RAM) {
- OUTONB(nc_ctest2, 0x8);
- np->paddr2 = INL(nc_scr0);
- OUTOFFB(nc_ctest2, 0x8);
+ * Divisor to be used for async (timer pre-scaler).
+ */
+ i = np->clock_divn - 1;
+ while (i >= 0) {
+ --i;
+ if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) {
+ ++i;
+ break;
+ }
}
+ np->rv_scntl3 = i+1;
+
+ /*
+ * Minimum synchronous period factor supported by the chip.
+ * Btw, 'period' is in tenths of nanoseconds.
+ */
+
+ period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz;
+ if (period <= 250) np->minsync = 10;
+ else if (period <= 303) np->minsync = 11;
+ else if (period <= 500) np->minsync = 12;
+ else np->minsync = (period + 40 - 1) / 40;
+
+ /*
+ * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2).
+ */
+
+ if (np->minsync < 25 && !(np->features & (FE_ULTRA|FE_ULTRA2)))
+ np->minsync = 25;
+ else if (np->minsync < 12 && !(np->features & FE_ULTRA2))
+ np->minsync = 12;
+
+ /*
+ * Maximum synchronous period factor supported by the chip.
+ */
+
+ period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz);
+ np->maxsync = period > 2540 ? 254 : period / 10;
/*
** Prepare initial value of other IO registers
*/
#if defined SCSI_NCR_TRUST_BIOS_SETTING
+ np->rv_scntl0 = np->sv_scntl0;
np->rv_dmode = np->sv_dmode;
np->rv_dcntl = np->sv_dcntl;
np->rv_ctest3 = np->sv_ctest3;
@@ -3696,12 +4093,6 @@ static int ncr_prepare_setting(ncb_p np)
np->rv_ctest5 = np->sv_ctest5;
burst_max = burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5);
#else
- np->rv_dmode = 0;
- np->rv_dcntl = 0;
- np->rv_ctest3 = 0;
- np->rv_ctest4 = 0;
- np->rv_ctest5 = 0;
- np->rv_stest2 = 0;
/*
** Select burst length (dwords)
@@ -3717,19 +4108,19 @@ static int ncr_prepare_setting(ncb_p np)
/*
** Select all supported special features
*/
- if (np->features & _F_ERL)
+ if (np->features & FE_ERL)
np->rv_dmode |= ERL; /* Enable Read Line */
- if (np->features & _F_BOF)
+ if (np->features & FE_BOF)
np->rv_dmode |= BOF; /* Burst Opcode Fetch */
- if (np->features & _F_ERMP)
+ if (np->features & FE_ERMP)
np->rv_dmode |= ERMP; /* Enable Read Multiple */
- if (np->features & _F_PFEN)
+ if (np->features & FE_PFEN)
np->rv_dcntl |= PFEN; /* Prefetch Enable */
- if (np->features & _F_CLSE)
+ if (np->features & FE_CLSE)
np->rv_dcntl |= CLSE; /* Cache Line Size Enable */
- if (np->features & _F_WRIE)
+ if (np->features & FE_WRIE)
np->rv_ctest3 |= WRIE; /* Write and Invalidate */
- if (np->features & _F_DFS)
+ if (np->features & FE_DFS)
np->rv_ctest5 |= DFS; /* Dma Fifo Size */
/*
@@ -3737,6 +4128,34 @@ static int ncr_prepare_setting(ncb_p np)
*/
if (driver_setup.master_parity)
np->rv_ctest4 |= MPEE; /* Master parity checking */
+ if (driver_setup.scsi_parity)
+ np->rv_scntl0 |= 0x0a; /* full arb., ena parity, par->ATN */
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ /*
+ ** Get parity checking, host ID and verbose mode from NVRAM
+ **/
+ if (nvram) {
+ switch(nvram->type) {
+ case SCSI_NCR_TEKRAM_NVRAM:
+ np->myaddr = nvram->data.Tekram.host_id & 0x0f;
+ break;
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ if (!(nvram->data.Symbios.flags & SYMBIOS_PARITY_ENABLE))
+ np->rv_scntl0 &= ~0x0a;
+ np->myaddr = nvram->data.Symbios.host_id & 0x0f;
+ if (nvram->data.Symbios.flags & SYMBIOS_VERBOSE_MSGS)
+ np->verbose += 1;
+ break;
+ }
+ }
+#endif
+ /*
+ ** Get SCSI addr of host adapter (set by bios?).
+ */
+ if (!np->myaddr) np->myaddr = INB(nc_scid) & 0x07;
+ if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
+
#endif /* SCSI_NCR_TRUST_BIOS_SETTING */
@@ -3746,21 +4165,31 @@ static int ncr_prepare_setting(ncb_p np)
ncr_init_burst(np, burst_max);
/*
- ** Set differential mode.
+ ** Set differential mode and LED support.
+ ** Ignore these features for boards known to use a
+ ** specific GPIO wiring (Tekram only for now).
+ ** Probe initial setting of GPREG and GPCNTL for
+ ** other ones.
*/
- switch(driver_setup.diff_support) {
- case 3:
- if (INB(nc_gpreg) & 0x08)
+ if (!nvram || nvram->type != SCSI_NCR_TEKRAM_NVRAM) {
+ switch(driver_setup.diff_support) {
+ case 3:
+ if (INB(nc_gpreg) & 0x08)
break;
- case 2:
- np->rv_stest2 |= 0x20;
- break;
- case 1:
- np->rv_stest2 |= (np->sv_stest2 & 0x20);
- break;
- default:
- break;
+ case 2:
+ np->rv_stest2 |= 0x20;
+ break;
+ case 1:
+ np->rv_stest2 |= (np->sv_stest2 & 0x20);
+ break;
+ default:
+ break;
+ }
}
+ if ((driver_setup.led_pin ||
+ (nvram && nvram->type == SCSI_NCR_SYMBIOS_NVRAM)) &&
+ !(np->sv_gpcntl & 0x01))
+ np->features |= FE_LED0;
/*
** Set irq mode.
@@ -3770,36 +4199,167 @@ static int ncr_prepare_setting(ncb_p np)
np->rv_dcntl |= IRQM;
break;
case 1:
- np->rv_stest2 |= (np->sv_dcntl & IRQM);
+ np->rv_dcntl |= (np->sv_dcntl & IRQM);
break;
default:
break;
}
/*
+ ** Configure targets according to driver setup.
+ ** If NVRAM present get targets setup from NVRAM.
+ ** Allow to override sync, wide and NOSCAN from
+ ** boot command line.
+ */
+ for (i = 0 ; i < MAX_TARGET ; i++) {
+ tcb_p tp = &np->target[i];
+
+ tp->usrsync = 255;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (nvram) {
+ switch(nvram->type) {
+ case SCSI_NCR_TEKRAM_NVRAM:
+ ncr_Tekram_setup_target(np, i, &nvram->data.Tekram);
+ break;
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ ncr_Symbios_setup_target(np, i, &nvram->data.Symbios);
+ break;
+ }
+ if (driver_setup.use_nvram & 0x2)
+ tp->usrsync = driver_setup.default_sync;
+ if (driver_setup.use_nvram & 0x4)
+ tp->usrwide = driver_setup.max_wide;
+ if (driver_setup.use_nvram & 0x8)
+ tp->usrflag &= ~UF_NOSCAN;
+ }
+ else {
+#else
+ if (1) {
+#endif
+ tp->usrsync = driver_setup.default_sync;
+ tp->usrwide = driver_setup.max_wide;
+ tp->usrtags = driver_setup.default_tags;
+ if (!driver_setup.disconnection)
+ np->target[i].usrflag = UF_NODISC;
+ }
+ }
+
+ /*
** Announce all that stuff to user.
*/
+
+ i = nvram ? nvram->type : 0;
+ printf(KERN_INFO "%s: %sID %d, Fast-%d%s%s\n", ncr_name(np),
+ i == SCSI_NCR_SYMBIOS_NVRAM ? "Symbios format NVRAM, " :
+ (i == SCSI_NCR_TEKRAM_NVRAM ? "Tekram format NVRAM, " : ""),
+ np->myaddr,
+ np->minsync < 12 ? 40 : (np->minsync < 25 ? 20 : 10),
+ (np->rv_scntl0 & 0xa) ? ", Parity Checking" : ", NO Parity",
+ (np->rv_stest2 & 0x20) ? ", Differential" : "");
+
if (bootverbose > 1) {
- printf ("%s: initial value of SCNTL3 = %02x, final = %02x\n",
- ncr_name(np), np->sv_scntl3, np->rv_scntl3);
- printf ("%s: initial value of dmode/dcntl/ctest3/4/5 = (hex) %02x/%02x/%02x/%02x/%02x\n",
- ncr_name(np), np->sv_dmode, np->sv_dcntl, np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
- printf ("%s: final value of dmode/dcntl/ctest3/4/5 = (hex) %02x/%02x/%02x/%02x/%02x\n",
- ncr_name(np), np->rv_dmode, np->rv_dcntl, np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
- if (np->rv_stest2 & 0x20)
- printf ("%s: DIFF mode set\n", ncr_name(np));
+ printf ("%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+ "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+ ncr_name(np), np->sv_scntl3, np->sv_dmode, np->sv_dcntl,
+ np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
+
+ printf ("%s: final SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+ "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+ ncr_name(np), np->rv_scntl3, np->rv_dmode, np->rv_dcntl,
+ np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
}
if (bootverbose && np->paddr2)
- printf ("%s: on-board RAM at 0x%lx\n", ncr_name(np), np->paddr2);
-
- if (bootverbose && np->ns_sync < 25)
- printf ("%s: Ultra%s SCSI support enabled\n", ncr_name(np),
- np->ns_sync < 12 ? "-2": "");
+ printf (KERN_INFO "%s: on-board RAM at 0x%lx\n",
+ ncr_name(np), np->paddr2);
return 0;
}
+
+#ifdef SCSI_NCR_DEBUG_NVRAM
+
+__initfunc(
+void ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram)
+)
+{
+ int i;
+
+ /* display Symbios nvram host data */
+ printf("%s: HOST ID=%d%s%s%s%s\n",
+ ncr_name(np), nvram->host_id & 0x0f,
+ (nvram->flags & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & SYMBIOS_PARITY_ENABLE) ? " PARITY" :"",
+ (nvram->flags & SYMBIOS_VERBOSE_MSGS) ? " VERSBOSE" :"",
+ (nvram->flags1 & SYMBIOS_SCAN_HI_LO) ? " HI_LO" :"");
+
+ /* display Symbios nvram drive data */
+ for (i = 0 ; i < 15 ; i++) {
+ struct Symbios_target *tn = &nvram->target[i];
+ printf("%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
+ ncr_name(np), i,
+ (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT" : "",
+ (tn->flags & SYMBIOS_SCAN_LUNS) ? " SCAN_LUNS" : "",
+ (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ" : "",
+ tn->bus_width,
+ tn->sync_period / 4,
+ tn->timeout);
+ }
+}
+
+static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
+
+__initfunc(
+void ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram)
+)
+{
+ int i, tags, boot_delay;
+ char *rem;
+
+ /* display Tekram nvram host data */
+ tags = 2 << nvram->max_tags_index;
+ boot_delay = 0;
+ if (nvram->boot_delay_index < 6)
+ boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
+ switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
+ default:
+ case 0: rem = ""; break;
+ case 1: rem = " REMOVABLE=boot device"; break;
+ case 2: rem = " REMOVABLE=all"; break;
+ }
+
+ printf("%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
+ ncr_name(np), nvram->host_id & 0x0f,
+ (nvram->flags1 & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES" :"",
+ (nvram->flags & TEKRAM_DRIVES_SUP_1GB) ? " >1GB" :"",
+ (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET" :"",
+ (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG" :"",
+ (nvram->flags & TEKRAM_IMMEDIATE_SEEK) ? " IMM_SEEK" :"",
+ (nvram->flags & TEKRAM_SCAN_LUNS) ? " SCAN_LUNS" :"",
+ (nvram->flags1 & TEKRAM_F2_F6_ENABLED) ? " F2_F6" :"",
+ rem, boot_delay, tags);
+
+ /* display Tekram nvram drive data */
+ for (i = 0; i <= 15; i++) {
+ int sync, j;
+ struct Tekram_target *tn = &nvram->target[i];
+ j = tn->sync_index & 0xf;
+ sync = j < 12 ? Tekram_sync[j] : 255;
+ printf("%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
+ ncr_name(np), i,
+ (tn->flags & TEKRAM_PARITY_CHECK) ? " PARITY" : "",
+ (tn->flags & TEKRAM_SYNC_NEGO) ? " SYNC" : "",
+ (tn->flags & TEKRAM_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & TEKRAM_START_CMD) ? " START" : "",
+ (tn->flags & TEKRAM_TAGGED_COMMANDS) ? " TCQ" : "",
+ (tn->flags & TEKRAM_WIDE_NEGO) ? " WIDE" : "",
+ sync);
+ }
+}
+#endif /* SCSI_NCR_DEBUG_NVRAM */
+
/*
** Host attach and initialisations.
**
@@ -3811,18 +4371,18 @@ static int ncr_prepare_setting(ncb_p np)
*/
__initfunc(
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit,
- ncr_chip *chip, u_int base, u_int io_port,
- int irq, int bus, u_char device_fn)
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
)
{
struct host_data *host_data;
ncb_p np;
struct Scsi_Host *instance = 0;
u_long flags = 0;
+ ncr_nvram *nvram = device->nvram;
-printf("ncr53c8xx: unit=%d chip=%s rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
- unit, chip->name, chip->revision_id, base, io_port, irq);
+printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
+ device->chip.name, unit, device->chip.revision_id, device->slot.base,
+ device->slot.io_port, device->slot.irq);
/*
** Allocate host_data structure
@@ -3833,7 +4393,6 @@ printf("ncr53c8xx: unit=%d chip=%s rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
/*
** Initialize structure.
*/
- instance->irq = irq;
host_data = (struct host_data *) instance->hostdata;
/*
@@ -3850,18 +4409,16 @@ printf("ncr53c8xx: unit=%d chip=%s rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
/*
** Store input informations in the host data structure.
*/
- strncpy(np->chip_name, chip->name, sizeof(np->chip_name) - 1);
+ strncpy(np->chip_name, device->chip.name, sizeof(np->chip_name) - 1);
np->unit = unit;
+ np->verbose = driver_setup.verbose;
sprintf(np->inst_name, "ncr53c%s-%d", np->chip_name, np->unit);
- np->device_id = chip->device_id;
- np->revision_id = chip->revision_id;
- np->features = chip->features;
- np->maxwide = (np->features & _F_WIDE)? 1 : 0;
- np->ns_sync = 25;
- np->clock_khz = 40000;
- np->clock_divn = chip->nr_divisor;
- np->maxoffs = chip->offset_max;
- np->maxburst = chip->burst_max;
+ np->device_id = device->chip.device_id;
+ np->revision_id = device->chip.revision_id;
+ np->features = device->chip.features;
+ np->clock_divn = device->chip.nr_divisor;
+ np->maxoffs = device->chip.offset_max;
+ np->maxburst = device->chip.burst_max;
np->script0 =
(struct script *) (((u_long) &host_data->script_data) & SCR_ALIGN_MASK);
@@ -3880,10 +4437,11 @@ printf("ncr53c8xx: unit=%d chip=%s rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
** virtual and physical memory.
*/
- np->paddr = base;
+ np->paddr = device->slot.base;
+ np->paddr2 = (np->features & FE_RAM)? device->slot.base_2 : 0;
#ifndef NCR_IOMAPPED
- np->vaddr = remap_pci_mem((u_long) base, (u_long) 128);
+ np->vaddr = remap_pci_mem((u_long) np->paddr, (u_long) 128);
if (!np->vaddr) {
printf("%s: can't map memory mapped IO region\n", ncr_name(np));
goto attach_error;
@@ -3906,13 +4464,35 @@ printf("ncr53c8xx: unit=%d chip=%s rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
** Try to map the controller chip into iospace.
*/
- request_region(io_port, 128, "ncr53c8xx");
- np->port = io_port;
+ request_region(device->slot.io_port, 128, "ncr53c8xx");
+ np->port = device->slot.io_port;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (nvram) {
+ switch(nvram->type) {
+ case SCSI_NCR_SYMBIOS_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ ncr_display_Symbios_nvram(np, &nvram->data.Symbios);
+#endif
+ break;
+ case SCSI_NCR_TEKRAM_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ ncr_display_Tekram_nvram(np, &nvram->data.Tekram);
+#endif
+ break;
+ default:
+ nvram = 0;
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ printf("%s: NVRAM: None or invalid data.\n", ncr_name(np));
+#endif
+ }
+ }
+#endif
/*
** Do chip dependent initialization.
*/
- (void)ncr_prepare_setting(np);
+ (void)ncr_prepare_setting(np, nvram);
#ifndef NCR_IOMAPPED
if (np->paddr2 && sizeof(struct script) <= 4096) {
@@ -3938,7 +4518,8 @@ printf("ncr53c8xx: unit=%d chip=%s rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
#ifndef NCR_IOMAPPED
instance->base = (char *) np->reg;
#endif
- instance->io_port = io_port;
+ instance->irq = device->slot.irq;
+ instance->io_port = device->slot.io_port;
instance->n_io_port = 128;
instance->dma_channel = 0;
#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
@@ -3964,34 +4545,26 @@ printf("ncr53c8xx: unit=%d chip=%s rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
** Patch the script for LED support.
*/
- if (driver_setup.led_pin & (~np->sv_gpcntl) & 0x01) {
- np->features |= _F_LED0;
- np->script0->reselect[0] = SCR_REG_REG(gpreg, SCR_OR, 0x01);
- np->script0->reselect1[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
- np->script0->reselect2[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
+ if (np->features & FE_LED0) {
+ np->script0->reselect[0] =
+ cpu_to_scr(SCR_REG_REG(gpreg, SCR_OR, 0x01));
+ np->script0->reselect1[0] =
+ cpu_to_scr(SCR_REG_REG(gpreg, SCR_AND, 0xfe));
+ np->script0->reselect2[0] =
+ cpu_to_scr(SCR_REG_REG(gpreg, SCR_AND, 0xfe));
}
/*
** init data structure
*/
- np->jump_tcb.l_cmd = SCR_JUMP;
- np->jump_tcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort);
-
- /*
- ** Get SCSI addr of host adapter (set by bios?).
- */
-
- np->myaddr = INB(nc_scid) & 0x07;
- if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
+ np->jump_tcb.l_cmd = cpu_to_scr(SCR_JUMP);
+ np->jump_tcb.l_paddr = cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
/*
** Reset chip.
*/
- OUTW (nc_sien , 0); /* Disable scsi interrupts */
- OUTB (nc_dien , 0); /* Disable dma interrupts */
-
OUTB (nc_istat, SRST);
DELAY (1000);
OUTB (nc_istat, 0 );
@@ -4009,31 +4582,24 @@ printf("ncr53c8xx: unit=%d chip=%s rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
** Install the interrupt handler.
*/
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-# ifdef SCSI_NCR_SHARE_IRQ
+#ifdef SCSI_NCR_SHARE_IRQ
if (bootverbose > 1)
printf("%s: requesting shared irq %d (dev_id=0x%lx)\n",
- ncr_name(np), irq, (u_long) np);
- if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT|SA_SHIRQ, "53c8xx", np)) {
-# else
- if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx", NULL)) {
-# endif
+ ncr_name(np), device->slot.irq, (u_long) np);
+ if (request_irq(device->slot.irq, ncr53c8xx_intr,
+ SA_INTERRUPT|SA_SHIRQ, "ncr53c8xx", np)) {
+#else
+ if (request_irq(device->slot.irq, ncr53c8xx_intr,
+ SA_INTERRUPT, "ncr53c8xx", NULL)) {
+#endif
#else
- if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx")) {
+ if (request_irq(device->slot.irq, ncr53c8xx_intr,
+ SA_INTERRUPT, "ncr53c8xx")) {
#endif
- printf("%s: request irq %d failure\n", ncr_name(np), irq);
+ printf("%s: request irq %d failure\n", ncr_name(np), device->slot.irq);
goto attach_error;
}
- np->irq = irq;
-
- /*
- ** Not allow disconnections for all targets if asked by config
- */
-
- if (!driver_setup.disconnection) {
- int i;
- for (i = 0 ; i < MAX_TARGET ; i++)
- np->target[i].usrflag |= UF_NODISC;
- }
+ np->irq = device->slot.irq;
/*
** After SCSI devices have been opened, we cannot
@@ -4160,6 +4726,19 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
return(DID_BAD_TARGET);
}
+ /*---------------------------------------------
+ **
+ ** Complete the 1st TEST UNIT READY command
+ ** with error condition if the device is
+ ** flagged NOSCAN, in order to speed up
+ ** the boot.
+ **
+ **---------------------------------------------
+ */
+ if (cmd->cmnd[0] == 0 && (tp->usrflag & UF_NOSCAN)) {
+ tp->usrflag &= ~UF_NOSCAN;
+ return DID_BAD_TARGET;
+ }
if (DEBUG_FLAGS & DEBUG_TINY) {
PRINT_ADDR(cmd);
@@ -4185,15 +4764,13 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
/*---------------------------------------------------
**
- ** Enable tagged queue if asked by user
+ ** Enable tagged queue if asked by scsi ioctl
**
**----------------------------------------------------
*/
- if (driver_setup.default_tags < SCSI_NCR_MAX_TAGS) {
- if (cmd->device && cmd->device->tagged_queue &&
- (lp = tp->lp[cmd->lun]) && (!lp->usetags)) {
- ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
- }
+ if (!tp->usrtags && cmd->device && cmd->device->tagged_queue) {
+ tp->usrtags = SCSI_NCR_MAX_TAGS;
+ ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
}
/*---------------------------------------------------
@@ -4240,7 +4817,8 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
nego = 0;
- if (cmd->lun == 0 && (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
+ if (cmd->lun == 0 && !tp->nego_cp &&
+ (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
/*
** negotiate wide transfers ?
*/
@@ -4270,6 +4848,15 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
printf ("asynchronous.\n");
};
};
+
+ /*
+ ** remember nego is pending for the target.
+ ** Avoid to start a nego for all queued commands
+ ** when tagged command queuing is enabled.
+ */
+
+ if (nego)
+ tp->nego_cp = cp;
};
/*---------------------------------------------------
@@ -4439,21 +5026,23 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
u_long endp;
default:
case XferBoth:
- cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_io);
+ cp->phys.header.savep =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, data_io));
cp->phys.header.goalp = cp->phys.header.savep;
break;
case XferIn:
endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16;
- cp->phys.header.goalp = endp + 8;
- cp->phys.header.savep = endp - segments*16;
+ cp->phys.header.goalp = cpu_to_scr(endp + 8);
+ cp->phys.header.savep = cpu_to_scr(endp - segments*16);
break;
case XferOut:
endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
- cp->phys.header.goalp = endp + 8;
- cp->phys.header.savep = endp - segments*16;
+ cp->phys.header.goalp = cpu_to_scr(endp + 8);
+ cp->phys.header.savep = cpu_to_scr(endp - segments*16);
break;
case XferNone:
- cp->phys.header.savep = NCB_SCRIPT_PHYS (np, no_data);
+ cp->phys.header.savep =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, no_data));
cp->phys.header.goalp = cp->phys.header.savep;
break;
}
@@ -4474,8 +5063,9 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
/*
** Startqueue
*/
- cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, select);
- cp->phys.header.launch.l_cmd = SCR_JUMP;
+ cp->phys.header.launch.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
+ cp->phys.header.launch.l_cmd = cpu_to_scr(SCR_JUMP);
/*
** select
*/
@@ -4485,21 +5075,21 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
/*
** message
*/
- cp->phys.smsg.addr = CCB_PHYS (cp, scsi_smsg);
- cp->phys.smsg.size = msglen;
+ cp->phys.smsg.addr = cpu_to_scr(CCB_PHYS (cp, scsi_smsg));
+ cp->phys.smsg.size = cpu_to_scr(msglen);
- cp->phys.smsg2.addr = CCB_PHYS (cp, scsi_smsg2);
- cp->phys.smsg2.size = msglen2;
+ cp->phys.smsg2.addr = cpu_to_scr(CCB_PHYS (cp, scsi_smsg2));
+ cp->phys.smsg2.size = cpu_to_scr(msglen2);
/*
** command
*/
- cp->phys.cmd.addr = vtophys (&cmd->cmnd[0]);
- cp->phys.cmd.size = cmd->cmd_len;
+ cp->phys.cmd.addr = cpu_to_scr(vtophys (&cmd->cmnd[0]));
+ cp->phys.cmd.size = cpu_to_scr(cmd->cmd_len);
/*
** sense command
*/
- cp->phys.scmd.addr = CCB_PHYS (cp, sensecmd);
- cp->phys.scmd.size = 6;
+ cp->phys.scmd.addr = cpu_to_scr(CCB_PHYS (cp, sensecmd));
+ cp->phys.scmd.size = cpu_to_scr(6);
/*
** patch requested size into sense command
*/
@@ -4509,8 +5099,9 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
/*
** sense data
*/
- cp->phys.sense.addr = vtophys (&cmd->sense_buffer[0]);
- cp->phys.sense.size = sizeof(cmd->sense_buffer);
+ cp->phys.sense.addr =
+ cpu_to_scr(vtophys (&cmd->sense_buffer[0]));
+ cp->phys.sense.size = cpu_to_scr(sizeof(cmd->sense_buffer));
/*
** status
*/
@@ -4535,8 +5126,10 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
** reselect pattern and activate this job.
*/
- cp->jump_ccb.l_cmd = (SCR_JUMP ^ IFFALSE (DATA (cp->tag)));
- /* Compute a time limit bigger than the middle-level driver one */
+ cp->jump_ccb.l_cmd =
+ cpu_to_scr((SCR_JUMP ^ IFFALSE (DATA (cp->tag))));
+
+ /* Compute a time limit greater than the middle-level driver one */
if (cmd->timeout_per_command > 0)
cp->tlimit = jiffies + cmd->timeout_per_command + NCR_TIMEOUT_INCREASE;
else
@@ -4549,14 +5142,14 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
qidx = np->squeueput + 1;
if (qidx >= MAX_START) qidx=0;
- np->squeue [qidx ] = NCB_SCRIPT_PHYS (np, idle);
- np->squeue [np->squeueput] = CCB_PHYS (cp, phys);
+ np->squeue [qidx ] = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
+ np->squeue [np->squeueput] = cpu_to_scr(CCB_PHYS (cp, phys));
np->squeueput = qidx;
if(DEBUG_FLAGS & DEBUG_QUEUE)
printf ("%s: queuepos=%d tryoffset=%d.\n", ncr_name (np),
np->squeueput,
- (unsigned)(np->script->startpos[0]-
+ (unsigned)(scr_to_cpu(np->script->startpos[0]) -
(NCB_SCRIPTH_PHYS (np, tryloop))));
/*
@@ -4762,10 +5355,12 @@ static int ncr_abort_command (Scsi_Cmnd *cmd)
** this condition in order to complete the canceled command
** after the script skipped the ccb, if necessary.
*/
- cp->jump_ccb.l_cmd = (SCR_JUMP);
- if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, select)) {
+ cp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
+ if (cp->phys.header.launch.l_paddr ==
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, select))) {
printf ("%s: abort ccb=%p (skip)\n", ncr_name (np), cp);
- cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, skip);
+ cp->phys.header.launch.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, skip));
}
cp->tlimit = 0;
@@ -4956,12 +5551,12 @@ void ncr_complete (ncb_p np, ccb_p cp)
/*
** No Reselect anymore.
*/
- cp->jump_ccb.l_cmd = (SCR_JUMP);
+ cp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
/*
** No starting.
*/
- cp->phys.header.launch.l_paddr= NCB_SCRIPT_PHYS (np, idle);
+ cp->phys.header.launch.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
/*
** timestamp
@@ -4981,6 +5576,15 @@ void ncr_complete (ncb_p np, ccb_p cp)
lp = tp->lp[cmd->lun];
/*
+ ** We donnot queue more than 1 ccb per target
+ ** with negotiation at any time. If this ccb was
+ ** used for negotiation, clear this info in the tcb.
+ */
+
+ if (cp == tp->nego_cp)
+ tp->nego_cp = 0;
+
+ /*
** Check for parity errors.
*/
@@ -5088,12 +5692,12 @@ void ncr_complete (ncb_p np, ccb_p cp)
** If tags was reduced due to queue full,
** increase tags if 100 good status received.
*/
- if (tp->usrtags < tp->maxtags) {
+ if (tp->numtags < tp->maxtags) {
++tp->num_good;
if (tp->num_good >= 100) {
tp->num_good = 0;
- ++tp->usrtags;
- if (tp->usrtags == 1) {
+ ++tp->numtags;
+ if (tp->numtags == 1) {
PRINT_ADDR(cmd);
printf("tagged command queueing resumed\n");
}
@@ -5136,10 +5740,10 @@ void ncr_complete (ncb_p np, ccb_p cp)
** Suspend tagged queuing and start good status counter.
** Announce changes to the generic driver.
*/
- if (tp->usrtags) {
+ if (tp->numtags) {
PRINT_ADDR(cmd);
printf("QUEUE FULL! suspending tagged command queueing\n");
- tp->usrtags = 0;
+ tp->numtags = 0;
tp->num_good = 0;
if (lp) {
ncr_settags (tp, lp);
@@ -5286,8 +5890,6 @@ void ncr_wakeup (ncb_p np, u_long code)
void ncr_init (ncb_p np, char * msg, u_long code)
{
int i;
- u_long usrsync;
- u_char usrwide;
/*
** Reset chip.
@@ -5300,20 +5902,20 @@ void ncr_init (ncb_p np, char * msg, u_long code)
** Message.
*/
- if (msg) printf ("%s: restart (%s).\n", ncr_name (np), msg);
+ if (msg) printf (KERN_INFO "%s: restart (%s).\n", ncr_name (np), msg);
/*
** Clear Start Queue
*/
for (i=0;i<MAX_START;i++)
- np -> squeue [i] = NCB_SCRIPT_PHYS (np, idle);
+ np -> squeue [i] = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
/*
** Start at first entry.
*/
np->squeueput = 0;
- np->script0->startpos[0] = NCB_SCRIPTH_PHYS (np, tryloop);
- np->script0->start0 [0] = SCR_INT ^ IFFALSE (0);
+ np->script0->startpos[0] = cpu_to_scr(NCB_SCRIPTH_PHYS (np, tryloop));
+ np->script0->start0 [0] = cpu_to_scr(SCR_INT ^ IFFALSE (0));
/*
** Wakeup all pending jobs.
@@ -5325,11 +5927,8 @@ void ncr_init (ncb_p np, char * msg, u_long code)
*/
OUTB (nc_istat, 0x00 ); /* Remove Reset, abort */
- if (driver_setup.scsi_parity)
- OUTB (nc_scntl0, 0xca); /* full arb., ena parity, par->ATN */
- else
- OUTB (nc_scntl0, 0xc0); /* full arb., (no parity) */
-
+ OUTB (nc_scntl0, np->rv_scntl0 | 0xc0);
+ /* full arb., ena parity, par->ATN */
OUTB (nc_scntl1, 0x00); /* odd parity, and remove CRST!! */
ncr_selectclock(np, np->rv_scntl3); /* Select SCSI clock */
@@ -5349,30 +5948,6 @@ void ncr_init (ncb_p np, char * msg, u_long code)
OUTB (nc_stime0, 0x0d ); /* HTH disabled STO 0.4 sec. */
/*
- ** Reinitialize usrsync.
- ** Have to renegotiate synch mode.
- */
-
- usrsync = driver_setup.default_sync;
- if (usrsync != 255) {
- if (4 * usrsync <= 11 * 50) {
- if (usrsync < np->ns_sync) {
- usrsync = np->ns_sync;
- }
- }
- else
- usrsync = 255;
- };
-
- /*
- ** Reinitialize usrwide.
- ** Have to renegotiate wide mode.
- */
-
- usrwide = driver_setup.max_wide;
- if (usrwide > np->maxwide) usrwide=np->maxwide;
-
- /*
** Disable disconnects.
*/
@@ -5382,7 +5957,7 @@ void ncr_init (ncb_p np, char * msg, u_long code)
** Enable GPIO0 pin for writing if LED support.
*/
- if (np->features & _F_LED0) {
+ if (np->features & FE_LED0) {
OUTOFFB (nc_gpcntl, 0x01);
}
@@ -5392,7 +5967,11 @@ void ncr_init (ncb_p np, char * msg, u_long code)
if (np->vaddr2) {
if (bootverbose)
printf ("%s: copying script fragments into the on-board RAM ...\n", ncr_name(np));
- bcopy(np->script0, np->script, sizeof(struct script));
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
+ memcpy_toio(np->script, np->script0, sizeof(struct script));
+#else
+ memcpy(np->script, np->script0, sizeof(struct script));
+#endif
}
/*
@@ -5405,13 +5984,15 @@ void ncr_init (ncb_p np, char * msg, u_long code)
/*
** For 895/6 enable SBMC interrupt and save current SCSI bus mode.
*/
- if (np->features & _F_ULTRA2) {
+ if (np->features & FE_ULTRA2) {
OUTONW (nc_sien, SBMC);
np->scsi_mode = INB (nc_stest4) & SMODE;
}
/*
** Fill in target structure.
+ ** Reinitialize usrsync.
+ ** Reinitialize usrwide.
** Prepare sync negotiation according to actual SCSI bus mode.
*/
@@ -5421,8 +6002,18 @@ void ncr_init (ncb_p np, char * msg, u_long code)
tp->sval = 0;
tp->wval = np->rv_scntl3;
- tp->usrsync = usrsync;
- tp->usrwide = usrwide;
+ if (tp->usrsync != 255) {
+ if (tp->usrsync <= np->maxsync) {
+ if (tp->usrsync < np->minsync) {
+ tp->usrsync = np->minsync;
+ }
+ }
+ else
+ tp->usrsync = 255;
+ };
+
+ if (tp->usrwide > np->maxwide)
+ tp->usrwide = np->maxwide;
ncr_negotiate (np, tp);
}
@@ -5470,14 +6061,14 @@ static void ncr_negotiate (struct ncb* np, struct tcb* tp)
** our limit ..
*/
- if (minsync < np->ns_sync)
- minsync = np->ns_sync;
+ if (minsync < np->minsync)
+ minsync = np->minsync;
/*
** divider limit
*/
- if (minsync > (11*50)/4)
+ if (minsync > np->maxsync)
minsync = 255;
tp->minsync = minsync;
@@ -5497,88 +6088,105 @@ static void ncr_negotiate (struct ncb* np, struct tcb* tp)
/*==========================================================
**
-** Get clock factor and sync divisor.
+** Get clock factor and sync divisor for a given
+** synchronous factor period.
+** Returns the clock factor (in sxfer) and scntl3
+** synchronous divisor field.
**
**==========================================================
*/
-#define SCSI_NCR_USE_ALL_DIVISORS
-
-/*
-** NCR chip clock divisor table.
-** Multiplied by 2x2000000 in order to avoid useless operations in
-** the code that gets clock factor and sync divisor from sync factor.
-*/
-#define _2M 2000000
-static u_long ncr_div2_2M[] = {2*_2M, 3*_2M, 4*_2M, 6*_2M, 8*_2M, 12*_2M, 16*_2M};
-
-/*
-** Get clock factor and sync divisor for a given sync factor period.
-** Returns the clock factor, scntl3 and resulting period.
-*/
-static int ncr_getsync(ncb_p np, u_char fac, u_char *fakp, u_char *scntl3p)
+static void ncr_getsync(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p)
{
- u_long clk = np->clock_khz; /* Clock in kHz */
- int idiv = np->clock_divn; /* # divisors supported */
- u_long fak, per, per_clk;
+ u_long clk = np->clock_khz; /* SCSI clock frequency in kHz */
+ int div = np->clock_divn; /* Number of divisors supported */
+ u_long fak; /* Sync factor in sxfer */
+ u_long per; /* Period in tenths of ns */
+ u_long kpc; /* (per * clk) */
/*
- ** Compute the synchronous period in nano-seconds
+ ** Compute the synchronous period in tenths of nano-seconds
*/
- if (fac <= 10) per = 25;
- else if (fac == 11) per = 30;
- else if (fac == 12) per = 50;
- else per = 4 * fac;
+ if (sfac <= 10) per = 250;
+ else if (sfac == 11) per = 303;
+ else if (sfac == 12) per = 500;
+ else per = 40 * sfac;
/*
- ** Find the greatest divisor that allows an input speed
- ** faster than the period.
+ ** Look for the greatest clock divisor that allows an
+ ** input speed faster than the period.
*/
- per_clk = per * clk;
- while (--idiv >= 0) {
-#ifndef SCSI_NCR_USE_ALL_DIVISORS
- if (idiv & 1) continue;
-#endif
- if (ncr_div2_2M[idiv] <= per_clk) break;
- }
- if (idiv < 0) idiv = 0; /* Should never happen */
+ kpc = per * clk;
+ while (--div >= 0)
+ if (kpc >= (div_10M[div] << 2)) break;
/*
** Calculate the lowest clock factor that allows an output
** speed not faster than the period.
*/
- fak = (4 * per_clk - 1) / ncr_div2_2M[idiv] + 1;
- per = (fak * ncr_div2_2M[idiv]) / (4 * clk);
+ fak = (kpc - 1) / div_10M[div] + 1;
+
+#if 0 /* This optimization does not seem very usefull */
+
+ per = (fak * div_10M[div]) / clk;
-#ifdef SCSI_NCR_USE_ALL_DIVISORS
/*
- ** Try the next divisor and choose the one that give
- ** the fastest output speed.
+ ** Why not to try the immediate lower divisor and to choose
+ ** the one that allows the fastest output speed ?
+ ** We dont want input speed too much greater than output speed.
*/
- if (idiv >= 1 && fak < 8) {
+ if (div >= 1 && fak < 8) {
u_long fak2, per2;
- fak2 = (4 * per_clk - 1) / ncr_div2_2M[idiv-1] + 1;
- per2 = (fak2 * ncr_div2_2M[idiv-1]) / (4 * clk);
+ fak2 = (kpc - 1) / div_10M[div-1] + 1;
+ per2 = (fak2 * div_10M[div-1]) / clk;
if (per2 < per && fak2 <= 8) {
fak = fak2;
per = per2;
- --idiv;
+ --div;
}
}
#endif
- if (fak < 4) fak = 4; /* Should never happen */
+
+ if (fak < 4) fak = 4; /* Should never happen, too bad ... */
/*
** Compute and return sync parameters for the ncr
*/
*fakp = fak - 4;
- *scntl3p = ((idiv+1) << 4) + (fac < 25 ? ULTRA : 0);
+ *scntl3p = ((div+1) << 4) + (sfac < 25 ? 0x80 : 0);
+}
-#ifdef DEBUG_NCR53C8XX
-printf("fac=%d idiv=%d per=%d fak=%x ", fac, idiv, per, *fakp);
-#endif
- return per;
+/*==========================================================
+**
+** Set actual values, sync status and patch all ccbs of
+** a target according to new sync/wide agreement.
+**
+**==========================================================
+*/
+
+static void ncr_set_sync_wide_status (ncb_p np, u_char target)
+{
+ ccb_p cp;
+ tcb_p tp = &np->target[target];
+
+ /*
+ ** set actual value and sync_status
+ */
+ OUTB (nc_sxfer, tp->sval);
+ np->sync_st = tp->sval;
+ OUTB (nc_scntl3, tp->wval);
+ np->wide_st = tp->wval;
+
+ /*
+ ** patch ALL ccbs of this target.
+ */
+ for (cp = np->ccb; cp; cp = cp->link_ccb) {
+ if (!cp->cmd) continue;
+ if (cp->cmd->target != target) continue;
+ cp->sync_status = tp->sval;
+ cp->wide_status = tp->wval;
+ };
}
/*==========================================================
@@ -5611,10 +6219,12 @@ static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
/*
** Deduce the value of controller sync period from scntl3.
+ ** period is in tenths of nano-seconds.
*/
+
idiv = ((scntl3 >> 4) & 0x7);
if ((sxfer & 0x1f) && idiv)
- tp->period = (((sxfer>>5)+4)*ncr_div2_2M[idiv-1])/(4*np->clock_khz);
+ tp->period = (((sxfer>>5)+4)*div_10M[idiv-1])/np->clock_khz;
else
tp->period = 0xffff;
@@ -5630,66 +6240,53 @@ static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
*/
PRINT_ADDR(cmd);
if (sxfer & 0x01f) {
- unsigned f10 = 10000 << (tp->widedone ? tp->widedone -1 : 0);
+ unsigned f10 = 100000 << (tp->widedone ? tp->widedone -1 : 0);
unsigned mb10 = (f10 + tp->period/2) / tp->period;
- char *msg;
+ char *scsi;
/*
** Disable extended Sreq/Sack filtering
*/
- if (tp->period <= 200) OUTOFFB (nc_stest2, EXT);
+ if (tp->period <= 2000) OUTOFFB (nc_stest2, EXT);
/*
** Bells and whistles ;-)
*/
- msg = "";
- if (tp->widedone > 1) {
- if (tp->period < 50) msg = "ULTRA-2 WIDE SCSI ";
- else if (tp->period < 100) msg = "ULTRA WIDE SCSI ";
- else if (tp->period < 200) msg = "FAST WIDE SCSI-2 ";
- }
- else {
- if (tp->period < 50) msg = "ULTRA-2 SCSI ";
- else if (tp->period < 100) msg = "ULTRA SCSI ";
- else if (tp->period < 200) msg = "FAST SCSI-2 ";
- }
+ if (tp->period < 500) scsi = "FAST-40";
+ else if (tp->period < 1000) scsi = "FAST-20";
+ else if (tp->period < 2000) scsi = "FAST-10";
+ else scsi = "SLOW";
- printf ("%s%d.%d MB/s (%d ns, offset %d)\n", msg,
- mb10 / 10, mb10 % 10, tp->period, sxfer & 0x1f);
- } else printf ("asynchronous.\n");
+ printf ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi,
+ tp->widedone > 1 ? "WIDE " : "",
+ mb10 / 10, mb10 % 10, tp->period / 10, sxfer & 0x1f);
+ } else
+ printf ("%sasynchronous.\n", tp->widedone > 1 ? "wide " : "");
/*
** set actual value and sync_status
- */
- OUTB (nc_sxfer, sxfer);
- np->sync_st = sxfer;
- OUTB (nc_scntl3, scntl3);
- np->wide_st = scntl3;
-
- /*
** patch ALL ccbs of this target.
*/
- for (cp = np->ccb; cp; cp = cp->link_ccb) {
- if (!cp->cmd) continue;
- if (cp->cmd->target != target) continue;
- cp->sync_status = sxfer;
- cp->wide_status = scntl3;
- };
+ ncr_set_sync_wide_status(np, target);
}
/*==========================================================
**
** Switch wide mode for current job and it's target
+** SCSI specs say: a SCSI device that accepts a WDTR
+** message shall reset the synchronous agreement to
+** asynchronous mode.
**
**==========================================================
*/
-static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide)
+static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack)
{
Scsi_Cmnd *cmd;
u_short target = INB (nc_ctest0) & 0x0f;
tcb_p tp;
u_char scntl3;
+ u_char sxfer;
assert (cp);
if (!cp) return;
@@ -5702,7 +6299,14 @@ static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide)
tp = &np->target[target];
tp->widedone = wide+1;
scntl3 = (tp->wval & (~EWS)) | (wide ? EWS : 0);
- if (tp->wval == scntl3) return;
+
+ sxfer = ack ? 0 : tp->sval;
+
+ /*
+ ** Stop there if sync/wide parameters are unchanged
+ */
+ if (tp->sval == sxfer && tp->wval == scntl3) return;
+ tp->sval = sxfer;
tp->wval = scntl3;
/*
@@ -5718,18 +6322,9 @@ static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide)
/*
** set actual value and sync_status
- */
- OUTB (nc_scntl3, scntl3);
- np->wide_st = scntl3;
-
- /*
** patch ALL ccbs of this target.
*/
- for (cp = np->ccb; cp; cp = cp->link_ccb) {
- if (!cp->cmd) continue;
- if (cp->cmd->target != target) continue;
- cp->wide_status = scntl3;
- };
+ ncr_set_sync_wide_status(np, target);
}
/*==========================================================
@@ -5739,11 +6334,13 @@ static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide)
**==========================================================
*/
-static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long usrtags)
+static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags)
{
int l;
- tp->usrtags = usrtags;
- tp->maxtags = usrtags;
+ if (numtags > tp->usrtags)
+ numtags = tp->usrtags;
+ tp->numtags = numtags;
+ tp->maxtags = numtags;
for (l=0; l<MAX_LUN; l++) {
lcb_p lp;
@@ -5756,11 +6353,11 @@ static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long usrtags)
wastags = lp->usetags;
ncr_settags (tp, lp);
- if (usrtags > 1 && lp->reqccbs > 1) {
+ if (numtags > 1 && lp->reqccbs > 1) {
PRINT_LUN(np, tp - np->target, l);
- printf("using tagged command queueing, up to %ld cmds/lun\n", usrtags);
+ printf("using tagged command queueing, up to %ld cmds/lun\n", numtags);
}
- else if (usrtags <= 1 && wastags) {
+ else if (numtags <= 1 && wastags) {
PRINT_LUN(np, tp - np->target, l);
printf("disabling tagged command queueing\n");
}
@@ -5781,8 +6378,8 @@ static void ncr_settags (tcb_p tp, lcb_p lp)
*/
if (( tp->inqdata[2] & 0x7) >= 2 &&
( tp->inqdata[7] & INQ7_QUEUE) && ((tp->inqdata[0] & 0x1f)==0x00)
- && tp->usrtags > 1) {
- reqtags = tp->usrtags;
+ && tp->numtags > 1) {
+ reqtags = tp->numtags;
if (lp->actlink <= 1)
lp->usetags=reqtags;
} else {
@@ -5838,6 +6435,7 @@ static void ncr_usercmd (ncb_p np)
np->user.data = SCSI_NCR_MAX_TAGS;
for (t=0; t<MAX_TARGET; t++) {
if (!((np->user.target>>t)&1)) continue;
+ np->target[t].usrtags = np->user.data;
ncr_setmaxtags (np, &np->target[t], np->user.data);
};
break;
@@ -5994,7 +6592,6 @@ static void ncr_timeout (ncb_p np)
{
u_long thistime = jiffies;
u_long count = 0;
- long signed t;
ccb_p cp;
u_long flags;
@@ -6068,10 +6665,12 @@ static void ncr_timeout (ncb_p np)
**
**----------------------------------------------------
*/
-
- t = (thistime - np->heartbeat) / HZ;
-
- if (t<2) np->latetime=0; else np->latetime++;
+#if 0
+ if (thistime < np->heartbeat + HZ + HZ)
+ np->latetime = 0;
+ else
+ np->latetime++;
+#endif
/*----------------------------------------------------
**
@@ -6114,7 +6713,7 @@ static void ncr_timeout (ncb_p np)
** still in start queue ?
*/
if (cp->phys.header.launch.l_paddr ==
- NCB_SCRIPT_PHYS (np, skip))
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, skip)))
continue;
/* fall through */
@@ -6263,15 +6862,18 @@ void ncr_exception (ncb_p np)
/*
** interrupt on the fly ?
- */
- while ((istat = INB (nc_istat)) & INTF) {
+ ** Since the global header may be copied back to a CCB
+ ** using a posted PCI memory write, the last operation on
+ ** the istat register is a READ in order to flush posted
+ ** PCI commands (Btw, the 'do' loop is probably useless).
+ */
+ istat = INB (nc_istat);
+ if (istat & INTF) {
+ do {
+ OUTB (nc_istat, (istat & SIGP) | INTF);
+ istat = INB (nc_istat);
+ } while (istat & INTF);
if (DEBUG_FLAGS & DEBUG_TINY) printf ("F ");
-#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
- if (np->stalling)
- OUTB (nc_istat, INTF);
- else
-#endif
- OUTB (nc_istat, (istat & SIGP) | INTF);
np->profile.num_fly++;
ncr_wakeup (np, 0);
};
@@ -6461,7 +7063,7 @@ void ncr_int_sto (ncb_p np)
/* assert ((diff <= MAX_START * 20) && !(diff % 20));*/
if ((diff <= MAX_START * 20) && !(diff % 20)) {
- np->script->startpos[0] = scratcha;
+ np->script->startpos[0] = cpu_to_scr(scratcha);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start));
return;
};
@@ -6475,11 +7077,13 @@ void ncr_int_sto (ncb_p np)
**
**==========================================================
**
-** I'm not quite sure of what is to be done in such a
-** situation.
-** For now,
-** Reset the bus if some devices use too fast sync transfers.
-** Otherwise, just try to renegotiate sync with targets.
+** spi2-r12 11.2.3 says a transceiver mode change must
+** generate a reset event and a device that detects a reset
+** event shall initiate a hard reset. It says also that a
+** device that detects a mode change shall set data transfer
+** mode to eight bit asynchronous, etc...
+** So, just resetting should be enough.
+**
**
**----------------------------------------------------------
*/
@@ -6487,32 +7091,14 @@ void ncr_int_sto (ncb_p np)
static int ncr_int_sbmc (ncb_p np)
{
u_char scsi_mode = INB (nc_stest4) & SMODE;
- int i;
- int oversync;
- printf("%s: SCSI bus mode change from %x to %x\n", ncr_name(np),
- np->scsi_mode, scsi_mode);
-
- if (scsi_mode == np->scsi_mode)
- return 0;
+ printf("%s: SCSI bus mode change from %x to %x, resetting ...\n",
+ ncr_name(np), np->scsi_mode, scsi_mode);
np->scsi_mode = scsi_mode;
- oversync = 0;
- for (i = 0; i < MAX_TARGET; i++) {
- tcb_p tp = &np->target[i];
-
- if (np->ns_sync < 12 && tp->maxoffs && tp->usrsync < 12) {
- if (scsi_mode != SMODE_SE)
- ncr_negotiate(np, tp);
- else
- ++oversync;
- }
- }
-
- if (oversync)
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, 2);
- return oversync;
+ return 1;
}
/*==========================================================
@@ -6570,10 +7156,10 @@ static void ncr_int_ma (ncb_p np)
/*
** Take into account dma fifo and various buffers and latches,
- ** only if the interrupted phase was DATA OUT.
+ ** only if the interrupted phase is an OUTPUT phase.
*/
- if ((cmd & 7) == 0) {
+ if ((cmd & 1) == 0) {
u_char ctest5, ss0, ss2;
u_short delta;
@@ -6669,20 +7255,20 @@ static void ncr_int_ma (ncb_p np)
** get old startaddress and old length.
*/
- oadr = vdsp[1];
+ oadr = scr_to_cpu(vdsp[1]);
if (cmd & 0x10) { /* Table indirect */
tblp = (u_int32 *) ((char*) &cp->phys + oadr);
- olen = tblp[0];
- oadr = tblp[1];
+ olen = scr_to_cpu(tblp[0]);
+ oadr = scr_to_cpu(tblp[1]);
} else {
tblp = (u_int32 *) 0;
- olen = vdsp[0] & 0xffffff;
+ olen = scr_to_cpu(vdsp[0]) & 0xffffff;
};
if (DEBUG_FLAGS & DEBUG_PHASE) {
printf ("OCMD=%x\nTBLP=%p OLEN=%x OADR=%x\n",
- (unsigned) (vdsp[0] >> 24),
+ (unsigned) (scr_to_cpu(vdsp[0]) >> 24),
tblp,
(unsigned) olen,
(unsigned) oadr);
@@ -6692,10 +7278,10 @@ static void ncr_int_ma (ncb_p np)
** check cmd against assumed interrupted script command.
*/
- if (cmd != (vdsp[0] >> 24)) {
+ if (cmd != (scr_to_cpu(vdsp[0]) >> 24)) {
PRINT_ADDR(cp->cmd);
printf ("internal error: cmd=%02x != %02x=(vdsp[0] >> 24)\n",
- (unsigned)cmd, (unsigned)vdsp[0] >> 24);
+ (unsigned)cmd, (unsigned)scr_to_cpu(vdsp[0]) >> 24);
return;
}
@@ -6727,25 +7313,25 @@ static void ncr_int_ma (ncb_p np)
*/
newcmd = cp->patch;
- if (cp->phys.header.savep == vtophys (newcmd)) newcmd+=4;
+ if (cp->phys.header.savep == cpu_to_scr(vtophys (newcmd))) newcmd+=4;
/*
** fillin the commands
*/
- newcmd[0] = ((cmd & 0x0f) << 24) | rest;
- newcmd[1] = oadr + olen - rest;
- newcmd[2] = SCR_JUMP;
- newcmd[3] = nxtdsp;
+ newcmd[0] = cpu_to_scr(((cmd & 0x0f) << 24) | rest);
+ newcmd[1] = cpu_to_scr(oadr + olen - rest);
+ newcmd[2] = cpu_to_scr(SCR_JUMP);
+ newcmd[3] = cpu_to_scr(nxtdsp);
if (DEBUG_FLAGS & DEBUG_PHASE) {
PRINT_ADDR(cp->cmd);
printf ("newcmd[%d] %x %x %x %x.\n",
(int) (newcmd - cp->patch),
- (unsigned)newcmd[0],
- (unsigned)newcmd[1],
- (unsigned)newcmd[2],
- (unsigned)newcmd[3]);
+ (unsigned)scr_to_cpu(newcmd[0]),
+ (unsigned)scr_to_cpu(newcmd[1]),
+ (unsigned)scr_to_cpu(newcmd[2]),
+ (unsigned)scr_to_cpu(newcmd[3]));
}
/*
** fake the return address (to the patch).
@@ -6832,20 +7418,23 @@ void ncr_int_sir (ncb_p np)
*/
if (num == SIR_DATA_IO_IS_OUT) {
endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
- cp->phys.header.goalp = endp + 8;
- cp->phys.header.savep = endp - cp->segments*16;
+ cp->phys.header.goalp = cpu_to_scr(endp + 8);
+ cp->phys.header.savep =
+ cpu_to_scr(endp - cp->segments*16);
} else {
endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16;
- cp->phys.header.goalp = endp + 8;
- cp->phys.header.savep = endp - cp->segments*16;
+ cp->phys.header.goalp = cpu_to_scr(endp + 8);
+ cp->phys.header.savep =
+ cpu_to_scr(endp - cp->segments*16);
}
cp->phys.header.lastp = cp->phys.header.savep;
np->header.savep = cp->phys.header.savep;
np->header.goalp = cp->phys.header.goalp;
np->header.lastp = cp->phys.header.lastp;
- OUTL (nc_temp, np->header.savep);
- OUTL (nc_dsp, np->header.savep);
+
+ OUTL (nc_temp, scr_to_cpu(np->header.savep));
+ OUTL (nc_dsp, scr_to_cpu(np->header.savep));
return;
/* break; */
@@ -6892,7 +7481,7 @@ void ncr_int_sir (ncb_p np)
** no job, resume normal processing
*/
if (DEBUG_FLAGS & DEBUG_RESTART) printf (" -- remove trap\n");
- np->script->start0[0] = SCR_INT ^ IFFALSE (0);
+ np->script->start0[0] = cpu_to_scr(SCR_INT ^ IFFALSE (0));
break;
case SIR_SENSE_FAILED:
@@ -6918,7 +7507,7 @@ void ncr_int_sir (ncb_p np)
/*
** And patch code to restart it.
*/
- np->script->start0[0] = SCR_INT;
+ np->script->start0[0] = cpu_to_scr(SCR_INT);
break;
/*-----------------------------------------------------------------------------
@@ -7019,7 +7608,7 @@ void ncr_int_sir (ncb_p np)
break;
case NS_WIDE:
- ncr_setwide (np, cp, 0);
+ ncr_setwide (np, cp, 0, 0);
break;
};
@@ -7062,8 +7651,8 @@ void ncr_int_sir (ncb_p np)
** check values against driver limits.
*/
- if (per < np->ns_sync)
- {chg = 1; per = np->ns_sync;}
+ if (per < np->minsync)
+ {chg = 1; per = np->minsync;}
if (per < tp->minsync)
{chg = 1; per = tp->minsync;}
if (ofs > tp->maxoffs)
@@ -7075,7 +7664,7 @@ void ncr_int_sir (ncb_p np)
fak = 7;
scntl3 = 0;
if (ofs != 0) {
- (void) ncr_getsync(np, per, &fak, &scntl3);
+ ncr_getsync(np, per, &fak, &scntl3);
if (fak > 7) {
chg = 1;
ofs = 0;
@@ -7118,7 +7707,7 @@ void ncr_int_sir (ncb_p np)
return;
case NS_WIDE:
- ncr_setwide (np, cp, 0);
+ ncr_setwide (np, cp, 0, 0);
break;
};
};
@@ -7217,13 +7806,13 @@ void ncr_int_sir (ncb_p np)
/*
** Answer wasn't acceptable.
*/
- ncr_setwide (np, cp, 0);
+ ncr_setwide (np, cp, 0, 1);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
} else {
/*
** Answer is ok.
*/
- ncr_setwide (np, cp, wide);
+ ncr_setwide (np, cp, wide, 1);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
};
return;
@@ -7239,7 +7828,7 @@ void ncr_int_sir (ncb_p np)
** prepare an answer message
*/
- ncr_setwide (np, cp, wide);
+ ncr_setwide (np, cp, wide, 1);
np->msgout[0] = M_EXTENDED;
np->msgout[1] = 2;
@@ -7275,7 +7864,7 @@ void ncr_int_sir (ncb_p np)
PRINT_ADDR(cp->cmd);
printf ("M_REJECT received (%x:%x).\n",
- (unsigned)np->lastmsg, np->msgout[0]);
+ (unsigned)scr_to_cpu(np->lastmsg), np->msgout[0]);
break;
case SIR_REJECT_SENT:
@@ -7325,8 +7914,8 @@ void ncr_int_sir (ncb_p np)
printf ("M_DISCONNECT received, but datapointer not saved: "
"data=%x save=%x goal=%x.\n",
(unsigned) INL (nc_temp),
- (unsigned) np->header.savep,
- (unsigned) np->header.goalp);
+ (unsigned) scr_to_cpu(np->header.savep),
+ (unsigned) scr_to_cpu(np->header.goalp));
break;
#if 0 /* This stuff does not work */
@@ -7357,7 +7946,7 @@ void ncr_int_sir (ncb_p np)
PRINT_ADDR(cp->cmd);
printf ("queue full.\n");
- np->script->start1[0] = SCR_INT;
+ np->script->start1[0] = cpu_to_scr(SCR_INT);
/*
** Try to disable tagged transfers.
@@ -7405,7 +7994,7 @@ void ncr_int_sir (ncb_p np)
*/
printf ("%s: queue empty.\n", ncr_name (np));
- np->script->start1[0] = SCR_INT ^ IFFALSE (0);
+ np->script->start1[0] = cpu_to_scr(SCR_INT ^ IFFALSE (0));
break;
#endif /* This stuff does not work */
};
@@ -7544,31 +8133,37 @@ static void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
tp=&np->target[target];
if (!tp->jump_tcb.l_cmd) {
-
/*
** initialize it.
*/
- tp->jump_tcb.l_cmd = (SCR_JUMP^IFFALSE (DATA (0x80 + target)));
+ tp->jump_tcb.l_cmd =
+ cpu_to_scr((SCR_JUMP^IFFALSE (DATA (0x80 + target))));
tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr;
- tp->getscr[0] = (np->features & _F_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
- tp->getscr[1] = vtophys (&tp->sval);
- tp->getscr[2] = np->paddr + offsetof (struct ncr_reg, nc_sxfer);
- tp->getscr[3] = (np->features & _F_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
- tp->getscr[4] = vtophys (&tp->wval);
- tp->getscr[5] = np->paddr + offsetof (struct ncr_reg, nc_scntl3);
+ tp->getscr[0] = (np->features & FE_PFEN) ?
+ cpu_to_scr(SCR_COPY(1)):cpu_to_scr(SCR_COPY_F(1));
+ tp->getscr[1] = cpu_to_scr(vtophys (&tp->sval));
+ tp->getscr[2] =
+ cpu_to_scr(np->paddr + offsetof (struct ncr_reg, nc_sxfer));
+
+ tp->getscr[3] = (np->features & FE_PFEN) ?
+ cpu_to_scr(SCR_COPY(1)):cpu_to_scr(SCR_COPY_F(1));
+ tp->getscr[4] = cpu_to_scr(vtophys (&tp->wval));
+ tp->getscr[5] =
+ cpu_to_scr(np->paddr + offsetof (struct ncr_reg, nc_scntl3));
assert (( (offsetof(struct ncr_reg, nc_sxfer) ^
offsetof(struct tcb , sval )) &3) == 0);
assert (( (offsetof(struct ncr_reg, nc_scntl3) ^
offsetof(struct tcb , wval )) &3) == 0);
- tp->call_lun.l_cmd = (SCR_CALL);
- tp->call_lun.l_paddr = NCB_SCRIPT_PHYS (np, resel_lun);
+ tp->call_lun.l_cmd = cpu_to_scr(SCR_CALL);
+ tp->call_lun.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_lun));
- tp->jump_lcb.l_cmd = (SCR_JUMP);
- tp->jump_lcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort);
- np->jump_tcb.l_paddr = vtophys (&tp->jump_tcb);
+ tp->jump_lcb.l_cmd = cpu_to_scr(SCR_JUMP);
+ tp->jump_lcb.l_paddr = cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
+ np->jump_tcb.l_paddr = cpu_to_scr(vtophys (&tp->jump_tcb));
}
/*
@@ -7591,14 +8186,17 @@ static void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
** Initialize it
*/
bzero (lp, sizeof (*lp));
- lp->jump_lcb.l_cmd = (SCR_JUMP ^ IFFALSE (DATA (lun)));
+ lp->jump_lcb.l_cmd =
+ cpu_to_scr(SCR_JUMP ^ IFFALSE (DATA (lun)));
lp->jump_lcb.l_paddr = tp->jump_lcb.l_paddr;
- lp->call_tag.l_cmd = (SCR_CALL);
- lp->call_tag.l_paddr = NCB_SCRIPT_PHYS (np, resel_tag);
+ lp->call_tag.l_cmd = cpu_to_scr(SCR_CALL);
+ lp->call_tag.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_tag));
- lp->jump_ccb.l_cmd = (SCR_JUMP);
- lp->jump_ccb.l_paddr = NCB_SCRIPTH_PHYS (np, aborttag);
+ lp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
+ lp->jump_ccb.l_paddr =
+ cpu_to_scr(NCB_SCRIPTH_PHYS (np, aborttag));
lp->actlink = 1;
@@ -7607,7 +8205,7 @@ static void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
/*
** Chain into LUN list
*/
- tp->jump_lcb.l_paddr = vtophys (&lp->jump_lcb);
+ tp->jump_lcb.l_paddr = cpu_to_scr(vtophys (&lp->jump_lcb));
tp->lp[lun] = lp;
ncr_setmaxtags (np, tp, driver_setup.default_tags);
@@ -7659,11 +8257,11 @@ static void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
/*
** Chain into reselect list
*/
- cp->jump_ccb.l_cmd = SCR_JUMP;
+ cp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
cp->jump_ccb.l_paddr = lp->jump_ccb.l_paddr;
- lp->jump_ccb.l_paddr = CCB_PHYS (cp, jump_ccb);
- cp->call_tmp.l_cmd = SCR_CALL;
- cp->call_tmp.l_paddr = NCB_SCRIPT_PHYS (np, resel_tmp);
+ lp->jump_ccb.l_paddr = cpu_to_scr(CCB_PHYS (cp, jump_ccb));
+ cp->call_tmp.l_cmd = cpu_to_scr(SCR_CALL);
+ cp->call_tmp.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_tmp));
/*
** Chain into wakeup list
@@ -7776,9 +8374,9 @@ static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
if (!use_sg) {
if (cmd->request_bufflen) {
data = &data[MAX_SCATTER - 1];
- data[0].addr = vtophys(cmd->request_buffer);
- data[0].size = cmd->request_bufflen;
- cp->data_len = data[0].size;
+ data[0].addr = cpu_to_scr(vtophys(cmd->request_buffer));
+ data[0].size = cpu_to_scr(cmd->request_bufflen);
+ cp->data_len = cmd->request_bufflen;
segment = 1;
}
}
@@ -7787,9 +8385,11 @@ static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
data = &data[MAX_SCATTER - use_sg];
while (segment < use_sg) {
- data[segment].addr = vtophys(scatter[segment].address);
- data[segment].size = scatter[segment].length;
- cp->data_len += data[segment].size;
+ data[segment].addr =
+ cpu_to_scr(vtophys(scatter[segment].address));
+ data[segment].size =
+ cpu_to_scr(scatter[segment].length);
+ cp->data_len += scatter[segment].length;
++segment;
}
}
@@ -7859,7 +8459,7 @@ static int ncr_snooptest (struct ncb* np)
/*
** Set memory and register.
*/
- np->ncr_cache = host_wr;
+ np->ncr_cache = cpu_to_scr(host_wr);
OUTL (nc_temp, ncr_wr);
/*
** Start script (exchange values)
@@ -7879,7 +8479,7 @@ flush_cache_all();
/*
** Read memory and register.
*/
- host_rd = np->ncr_cache;
+ host_rd = scr_to_cpu(np->ncr_cache);
ncr_rd = INL (nc_scratcha);
ncr_bk = INL (nc_temp);
/*
@@ -8215,15 +8815,6 @@ static void ncr_getclock (ncb_p np, int mult)
*/
f1 *= np->multiplier;
np->clock_khz = f1;
- np->ns_sync = 25;
-
- if (f1 >= 160000) {
- if (np->features & _F_ULTRA2) np->ns_sync = 10;
- else if (np->features & _F_ULTRA) np->ns_sync = 12;
- }
- else if (f1 >= 80000) {
- if (np->features & _F_ULTRA) np->ns_sync = 12;
- }
}
/*===================== LINUX ENTRY POINTS SECTION ==========================*/
@@ -8253,13 +8844,14 @@ void ncr53c8xx_setup(char *str, int *ints)
{
#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
char *cur = str;
- char *pv;
+ char *pc, *pv;
int val;
int base;
int c;
- while (cur != NULL && (pv = strchr(cur, ':')) != NULL) {
+ while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
val = 0;
+ pv = pc;
c = *++pv;
if (c == 'n')
val = 0;
@@ -8323,9 +8915,15 @@ void ncr53c8xx_setup(char *str, int *ints)
driver_setup.irqm = val;
else if (!strncmp(cur, "pcifix:", 7))
driver_setup.pci_fix_up = val;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ else if (!strncmp(cur, "nvram:", 6))
+ driver_setup.use_nvram = val;
+#endif
else if (!strncmp(cur, "safe:", 5) && val)
memcpy(&driver_setup, &driver_safe_setup, sizeof(driver_setup));
+ else
+ printf("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
if ((cur = strchr(cur, ',')) != NULL)
++cur;
@@ -8333,8 +8931,8 @@ void ncr53c8xx_setup(char *str, int *ints)
#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
}
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit,
- uchar bus, uchar device_fn);
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+ uchar bus, uchar device_fn, ncr_device *device);
/*
** Linux entry point for NCR53C8XX devices detection routine.
@@ -8345,6 +8943,9 @@ static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit,
** Read the PCI configuration and try to attach each
** detected NCR board.
**
+** If NVRAM is present, try to attach boards according to
+** the used defined boot order.
+**
** Returns the number of boards successfully attached.
*/
@@ -8381,6 +8982,91 @@ static void ncr_print_driver_setup(void)
static ncr_chip ncr_chip_table[] __initdata = SCSI_NCR_CHIP_TABLE;
static ushort ncr_chip_ids[] __initdata = SCSI_NCR_CHIP_IDS;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+__initfunc(
+static int
+ncr_attach_using_nvram(Scsi_Host_Template *tpnt, int nvram_index, int count, ncr_device device[])
+)
+{
+ int i, j;
+ int attach_count = 0;
+ ncr_nvram *nvram;
+ ncr_device *devp;
+
+ if (!nvram_index)
+ return 0;
+
+ /* find first Symbios NVRAM if there is one as we need to check it for host boot order */
+ for (i = 0, nvram_index = -1; i < count; i++) {
+ devp = &device[i];
+ nvram = devp->nvram;
+ if (!nvram)
+ continue;
+ if (nvram->type == SCSI_NCR_SYMBIOS_NVRAM) {
+ if (nvram_index == -1)
+ nvram_index = i;
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ printf("ncr53c8xx: NVRAM: Symbios format Boot Block, 53c%s, PCI bus %d, device %d, function %d\n",
+ devp->chip.name, devp->slot.bus,
+ (int) (devp->slot.device_fn & 0xf8) >> 3,
+ (int) devp->slot.device_fn & 7);
+ for (j = 0 ; j < 4 ; j++) {
+ Symbios_host *h = &nvram->data.Symbios.host[j];
+ printf("ncr53c8xx: BOOT[%d] device_id=%04x vendor_id=%04x device_fn=%02x io_port=%04x %s\n",
+ j, h->device_id, h->vendor_id,
+ h->device_fn, h->io_port,
+ (h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) ? "SCAN AT BOOT" : "");
+ }
+ }
+ else if (nvram->type == SCSI_NCR_TEKRAM_NVRAM) {
+ /* display Tekram nvram data */
+ printf("ncr53c8xx: NVRAM: Tekram format data, 53c%s, PCI bus %d, device %d, function %d\n",
+ devp->chip.name, devp->slot.bus,
+ (int) (devp->slot.device_fn & 0xf8) >> 3,
+ (int) devp->slot.device_fn & 7);
+#endif
+ }
+ }
+
+ if (nvram_index >= 0 && nvram_index < count)
+ nvram = device[nvram_index].nvram;
+ else
+ nvram = 0;
+
+ if (!nvram)
+ goto out;
+
+ /*
+ ** check devices in the boot record against devices detected.
+ ** attach devices if we find a match. boot table records that
+ ** do not match any detected devices will be ignored.
+ ** devices that do not match any boot table will not be attached
+ ** here but will attempt to be attached during the device table
+ ** rescan.
+ */
+ for (i = 0; i < 4; i++) {
+ Symbios_host *h = &nvram->data.Symbios.host[i];
+ for (j = 0 ; j < count ; j++) {
+ devp = &device[j];
+ if (h->device_fn == devp->slot.device_fn &&
+#if 0 /* bus number location in nvram ? */
+ h->bus == devp->slot.bus &&
+#endif
+ h->device_id == devp->chip.device_id)
+ break;
+ }
+ if (j < count && !devp->attached &&
+ !ncr_attach (tpnt, attach_count, devp)) {
+ attach_count++;
+ devp->attached = 1;
+ }
+ }
+
+out:
+ return attach_count;
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
__initfunc(
int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
)
@@ -8390,8 +9076,18 @@ int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
int count = 0;
uchar bus, device_fn;
short index;
+ int attach_count = 0;
+ ncr_device device[8];
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ ncr_nvram nvram[4];
+ int k, nvrams;
+#endif
+ int hosts;
- if (bootverbose >= 2)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ int nvram_index = 0;
+#endif
+ if (initverbose >= 2)
ncr_print_driver_setup();
#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
@@ -8405,137 +9101,379 @@ int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
# endif
#endif
+ /*
+ ** Detect all 53c8xx hosts and then attach them.
+ **
+ ** If we are using NVRAM, once all hosts are detected, we need to check
+ ** any NVRAM for boot order in case detect and boot order differ and
+ ** attach them using the order in the NVRAM.
+ **
+ ** If no NVRAM is found or data appears invalid attach boards in the
+ ** the order they are detected.
+ */
+
if (!pcibios_present())
return 0;
- chips = sizeof(ncr_chip_ids) / sizeof(ncr_chip_ids[0]);
+ chips = sizeof(ncr_chip_ids) / sizeof(ncr_chip_ids[0]);
+ hosts = sizeof(device) / sizeof(device[0]);
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ k = 0;
+ if (driver_setup.use_nvram & 0x1)
+ nvrams = sizeof(nvram) / sizeof(nvram[0]);
+ else
+ nvrams = 0;
+#endif
+
for (j = 0; j < chips ; ++j) {
i = driver_setup.reverse_probe ? chips-1 - j : j;
for (index = 0; ; index++) {
- if (pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
- index, &bus, &device_fn))
+ char *msg = "";
+ if ((pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+ index, &bus, &device_fn)) ||
+ (count == hosts))
break;
- if (!ncr53c8xx_pci_init(tpnt, count, bus, device_fn))
- ++count;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ device[count].nvram = k < nvrams ? &nvram[k] : 0;
+#else
+ device[count].nvram = 0;
+#endif
+ if (ncr53c8xx_pci_init(tpnt, bus, device_fn, &device[count])) {
+ device[count].nvram = 0;
+ continue;
+ }
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (device[count].nvram) {
+ ++k;
+ nvram_index |= device[count].nvram->type;
+ switch (device[count].nvram->type) {
+ case SCSI_NCR_TEKRAM_NVRAM:
+ msg = "with Tekram NVRAM";
+ break;
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ msg = "with Symbios NVRAM";
+ break;
+ default:
+ msg = "";
+ device[count].nvram = 0;
+ --k;
+ }
+ }
+#endif
+ printf(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
+ device[count].chip.name, msg);
+
+ device[count].attached = 0;
+ ++count;
+ }
+ }
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ attach_count = ncr_attach_using_nvram(tpnt, nvram_index, count, device);
+#endif
+ /*
+ ** rescan device list to make sure all boards attached.
+ ** devices without boot records will not be attached yet
+ ** so try to attach them here.
+ */
+ for (i= 0; i < count; i++) {
+ if ((!device[i].attached) && (!ncr_attach (tpnt, attach_count, &device[i]))) {
+ attach_count++;
+ device[i].attached = 1;
}
}
- return count;
-}
+ return attach_count;
+}
/*
-** Read the PCI configuration of a found NCR board and
-** try to attach it.
+** Read and check the PCI configuration for any detected NCR
+** boards and save data for attaching after all boards have
+** been detected.
*/
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit, int board, int chip,
- uchar bus, uchar device_fn, int options)
+__initfunc(
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+ uchar bus, uchar device_fn, ncr_device *device)
+)
{
- ushort vendor_id, device_id, command;
+ ushort vendor_id, device_id, command;
+ uchar cache_line_size, latency_timer;
+ uchar irq, revision;
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
- uint base, io_port;
+ uint base, base_2, io_port;
#else
- ulong base, io_port;
+ ulong base, base_2;
#endif
- uchar irq, revision;
- int error, expected_chip;
- int expected_id = -1, max_revision = -1, min_revision = -1;
- int i;
-
- printk("ncr53c8xx: at PCI bus %d, device %d, function %d\n",
- bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
+ int i;
- if (!pcibios_present()) {
- printk("ncr53c8xx: not initializing due to lack of PCI BIOS,\n");
- return -1;
- }
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ ncr_nvram *nvram = device->nvram;
+#endif
+ ncr_chip *chip;
- if ((error = pcibios_read_config_word( bus, device_fn, PCI_VENDOR_ID, &vendor_id)) ||
- (error = pcibios_read_config_word( bus, device_fn, PCI_DEVICE_ID, &device_id)) ||
- (error = pcibios_read_config_word( bus, device_fn, PCI_COMMAND, &command)) ||
- (error = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, &io_port)) ||
- (error = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base)) ||
- (error = pcibios_read_config_byte (bus, device_fn, PCI_CLASS_REVISION, &revision)) ||
- (error = pcibios_read_config_byte (bus, device_fn, PCI_INTERRUPT_LINE, &irq))) {
- printk("ncr53c8xx: error %s not initializing due to error reading configuration space\n",
- pcibios_strerror(error));
- return -1;
- }
+ printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
+ bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
+ /*
+ * Read info from the PCI config space.
+ * pcibios_read_config_xxx() functions are assumed to be used for
+ * successfully detected PCI devices.
+ * Expecting error conditions from them is just paranoia,
+ * thus void cast.
+ */
+ (void) pcibios_read_config_word(bus, device_fn,
+ PCI_VENDOR_ID, &vendor_id);
+ (void) pcibios_read_config_word(bus, device_fn,
+ PCI_DEVICE_ID, &device_id);
+ (void) pcibios_read_config_word(bus, device_fn,
+ PCI_COMMAND, &command);
+ (void) pcibios_read_config_dword(bus, device_fn,
+ PCI_BASE_ADDRESS_0, &io_port);
+ (void) pcibios_read_config_dword(bus, device_fn,
+ PCI_BASE_ADDRESS_1, &base);
+ (void) pcibios_read_config_dword(bus, device_fn,
+ PCI_BASE_ADDRESS_2, &base_2);
+ (void) pcibios_read_config_byte(bus, device_fn,
+ PCI_CLASS_REVISION,&revision);
+ (void) pcibios_read_config_byte(bus, device_fn,
+ PCI_INTERRUPT_LINE, &irq);
+ (void) pcibios_read_config_byte(bus, device_fn,
+ PCI_CACHE_LINE_SIZE, &cache_line_size);
+ (void) pcibios_read_config_byte(bus, device_fn,
+ PCI_LATENCY_TIMER, &latency_timer);
#ifdef CONFIG_SNI_RM200_PCI
- /*
- * The onboard NCR bypasses the normal PCI interrupt system.
- */
- if (mips_machgroup == MACH_GROUP_SNI_RM
- && mips_machtype == MACH_SNI_RM200_PCI
- && bus == 0 && device_fn == PCI_DEVFN(0, 1))
- irq = PCIMT_IRQ_SCSI;
+ /*
+ * The onboard NCR bypasses the normal PCI interrupt system.
+ */
+ if (mips_machgroup == MACH_GROUP_SNI_RM
+ && mips_machtype == MACH_SNI_RM200_PCI
+ && bus == 0 && device_fn == PCI_DEVFN(0, 1))
+ irq = PCIMT_IRQ_SCSI;
printk("ncr53c8xx_pci_init() #1: bus == %d, device_fn == %d\n", bus, device_fn);
#endif
+ /*
+ * Check if the chip is supported
+ */
+ chip = 0;
+ for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) {
+ if (device_id != ncr_chip_table[i].device_id)
+ continue;
+ if (revision > ncr_chip_table[i].revision_id)
+ continue;
+ chip = &device->chip;
+ memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
+ chip->revision_id = revision;
+ break;
+ }
+ if (!chip) {
+ printk("ncr53c8xx: not initializing, device not supported\n");
+ return -1;
+ }
- if (vendor_id != PCI_VENDOR_ID_NCR) {
- printk("ncr53c8xx: not initializing, 0x%04x is not NCR vendor ID\n", (int) vendor_id);
- return -1;
- }
-
+ /*
+ * Check availability of IO space, memory space and master capability.
+ */
+ if (command & PCI_COMMAND_IO) {
+ if ((io_port & 3) != 1) {
+ printk("ncr53c8xx: disabling I/O mapping since base address 0 (0x%x)\n"
+ " bits 0..1 indicate a non-IO mapping\n", (int) io_port);
+ io_port = 0;
+ }
+ else
+ io_port &= PCI_BASE_ADDRESS_IO_MASK;
+ }
+ else
+ io_port = 0;
- if (command & PCI_COMMAND_IO) {
- if ((io_port & 3) != 1) {
- printk("ncr53c8xx : disabling I/O mapping since base address 0 (0x%x)\n"
- " bits 0..1 indicate a non-IO mapping\n", (int) io_port);
- io_port = 0;
- }
- else
- io_port &= PCI_BASE_ADDRESS_IO_MASK;
- }
- else
- io_port = 0;
-
- if (command & PCI_COMMAND_MEMORY) {
- if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
- printk("ncr53c8xx: disabling memory mapping since base address 1\n"
- " contains a non-memory mapping\n");
- base = 0;
- }
- else
- base &= PCI_BASE_ADDRESS_MEM_MASK;
- }
- else
- base = 0;
+ if (command & PCI_COMMAND_MEMORY) {
+ if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
+ printk("ncr53c8xx: disabling memory mapping since base address 1\n"
+ " contains a non-memory mapping\n");
+ base = 0;
+ }
+ else
+ base &= PCI_BASE_ADDRESS_MEM_MASK;
+ }
+ else
+ base = 0;
- if (!io_port && !base) {
- printk("ncr53c8xx: not initializing, both I/O and memory mappings disabled\n");
- return -1;
- }
+ if (!io_port && !base) {
+ printk("ncr53c8xx: not initializing, both I/O and memory mappings disabled\n");
+ return -1;
+ }
+
+ base_2 &= PCI_BASE_ADDRESS_MEM_MASK;
+
+ if (io_port && check_region (io_port, 128)) {
+ printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n",
+ (int) io_port, (int) (io_port + 127));
+ return -1;
+ }
- if (!(command & PCI_COMMAND_MASTER)) {
- printk ("ncr53c8xx: not initializing, BUS MASTERING was disabled\n");
- return -1;
- }
+ if (!(command & PCI_COMMAND_MASTER)) {
+ printk("ncr53c8xx: not initializing, BUS MASTERING was disabled\n");
+ return -1;
+ }
- for (i = 0; i < NPCI_CHIP_IDS; ++i) {
- if (device_id == pci_chip_ids[i].pci_device_id) {
- max_revision = pci_chip_ids[i].max_revision;
- min_revision = pci_chip_ids[i].min_revision;
- expected_chip = pci_chip_ids[i].chip;
- }
- if (chip == pci_chip_ids[i].chip)
- expected_id = pci_chip_ids[i].pci_device_id;
- }
+ /*
+ * Fix some features according to driver setup.
+ */
+ if (!driver_setup.special_features)
+ chip->features &= ~FE_SPECIAL_SET;
+ if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
+ chip->features |= FE_ULTRA;
+ chip->features &= ~FE_ULTRA2;
+ }
+ if (driver_setup.ultra_scsi < 1)
+ chip->features &= ~FE_ULTRA;
+ if (!driver_setup.max_wide)
+ chip->features &= ~FE_WIDE;
- if (chip && device_id != expected_id)
- printk("ncr53c8xx: warning : device id of 0x%04x doesn't\n"
- " match expected 0x%04x\n",
- (unsigned int) device_id, (unsigned int) expected_id );
-
- if (io_port && check_region (io_port, 128)) {
- printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n",
- (int) io_port, (int) (io_port + 127));
- return -1;
- }
- return ncr_attach (tpnt, unit, device_id, revision, chip, base, io_port,
- (int) irq, bus, (uchar) device_fn);
+#ifdef SCSI_NCR_PCI_FIX_UP_SUPPORT
+
+ /*
+ * Try to fix up PCI config according to wished features.
+ */
+#if defined(__i386) && !defined(MODULE)
+ if ((driver_setup.pci_fix_up & 1) &&
+ (chip->features & FE_CLSE) && cache_line_size == 0) {
+ extern char x86;
+ switch(x86) {
+ case 4: cache_line_size = 4; break;
+ case 5: cache_line_size = 8; break;
+ }
+ if (cache_line_size)
+ (void) pcibios_write_config_byte(bus, device_fn,
+ PCI_CACHE_LINE_SIZE, cache_line_size);
+ if (initverbose)
+ printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size);
+ }
+
+ if ((driver_setup.pci_fix_up & 2) && cache_line_size &&
+ (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+ command |= PCI_COMMAND_INVALIDATE;
+ (void) pcibios_write_config_word(bus, device_fn,
+ PCI_COMMAND, command);
+ if (initverbose)
+ printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
+ }
+#endif
+ /*
+ * Fix up for old chips that support READ LINE but not CACHE LINE SIZE.
+ * - If CACHE LINE SIZE is unknown, set burst max to 32 bytes = 8 dwords
+ * and donnot enable READ LINE.
+ * - Otherwise set it to the CACHE LINE SIZE (power of 2 assumed).
+ */
+
+ if (!(chip->features & FE_CLSE)) {
+ int burst_max = chip->burst_max;
+ if (cache_line_size == 0) {
+ chip->features &= ~FE_ERL;
+ if (burst_max > 3)
+ burst_max = 3;
+ }
+ else {
+ while (cache_line_size < (1 << burst_max))
+ --burst_max;
+ }
+ chip->burst_max = burst_max;
+ }
+
+ /*
+ * Tune PCI LATENCY TIMER according to burst max length transfer.
+ * (latency timer >= burst length + 6, we add 10 to be quite sure)
+ * If current value is zero, the device has probably been configured
+ * for no bursting due to some broken hardware.
+ */
+
+ if (latency_timer == 0 && chip->burst_max)
+ printk("ncr53c8xx: PCI_LATENCY_TIMER=0, bursting should'nt be allowed.\n");
+
+ if ((driver_setup.pci_fix_up & 4) && chip->burst_max) {
+ uchar lt = (1 << chip->burst_max) + 6 + 10;
+ if (latency_timer < lt) {
+ latency_timer = lt;
+ if (initverbose)
+ printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer);
+ (void) pcibios_write_config_byte(bus, device_fn,
+ PCI_LATENCY_TIMER, latency_timer);
+ }
+ }
+
+ /*
+ * Fix up for recent chips that support CACHE LINE SIZE.
+ * If PCI config space is not OK, remove features that shall not be
+ * used by the chip. No need to trigger possible chip bugs.
+ */
+
+ if ((chip->features & FE_CLSE) && cache_line_size == 0) {
+ chip->features &= ~FE_CACHE_SET;
+ printk("ncr53c8xx: PCI_CACHE_LINE_SIZE not set, features based on CACHE LINE SIZE not used.\n");
+ }
+
+ if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+ chip->features &= ~FE_WRIE;
+ printk("ncr53c8xx: PCI_COMMAND_INVALIDATE not set, WRITE AND INVALIDATE not used\n");
+ }
+
+#endif /* SCSI_NCR_PCI_FIX_UP_SUPPORT */
+
+ /* initialise ncr_device structure with items required by ncr_attach */
+ device->slot.bus = bus;
+ device->slot.device_fn = device_fn;
+ device->slot.base = base;
+ device->slot.base_2 = base_2;
+ device->slot.io_port = io_port;
+ device->slot.irq = irq;
+ device->attached = 0;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (!nvram)
+ goto out;
+
+ /*
+ ** Get access to chip IO registers
+ */
+#ifdef NCR_IOMAPPED
+ request_region(io_port, 128, "ncr53c8xx");
+ device->slot.port = io_port;
+#else
+ device->slot.reg = (struct ncr_reg *) remap_pci_mem((ulong) base, 128);
+ if (!device->slot.reg)
+ goto out;
+#endif
+
+ /*
+ ** Try to read SYMBIOS nvram.
+ ** Data can be used to order booting of boards.
+ **
+ ** Data is saved in ncr_device structure if NVRAM found. This
+ ** is then used to find drive boot order for ncr_attach().
+ **
+ ** NVRAM data is passed to Scsi_Host_Template later during ncr_attach()
+ ** for any device set up.
+ **
+ ** Try to read TEKRAM nvram if Symbios nvram not found.
+ */
+
+ if (!ncr_get_Symbios_nvram(&device->slot, &nvram->data.Symbios))
+ nvram->type = SCSI_NCR_SYMBIOS_NVRAM;
+ else if (!ncr_get_Tekram_nvram(&device->slot, &nvram->data.Tekram))
+ nvram->type = SCSI_NCR_TEKRAM_NVRAM;
+ else
+ nvram->type = 0;
+out:
+ /*
+ ** Release access to chip IO registers
+ */
+#ifdef NCR_IOMAPPED
+ release_region(device->slot.port, 128);
+#else
+ unmap_pci_mem((vm_offset_t) device->slot.reg, (u_long) 128);
+#endif
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+ return 0;
}
#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
@@ -9188,7 +10126,7 @@ static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len)
copy_info(&info, " Using memory mapped IO at virtual address 0x%lx\n",
(u_long) np->reg);
#endif
- copy_info(&info, " Synchronous period factor %d, ", (int) np->ns_sync);
+ copy_info(&info, " Synchronous period factor %d, ", (int) np->minsync);
copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS);
if (driver_setup.debug || driver_setup.verbose > 1) {
@@ -9271,6 +10209,460 @@ printf("ncr53c8xx_proc_info: hostno=%d, func=%d\n", hostno, func);
*/
#endif
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/* ---------------------------------------------------------------------
+**
+** Try reading Symbios format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in/data out
+** GPIO1 - clock
+**
+** return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl);
+static void nvram_start(ncr_slot *np, u_char *gpreg);
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg);
+static void nvram_stop(ncr_slot *np, u_char *gpreg);
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode);
+
+__initfunc(
+static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
+)
+{
+ static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ u_short csum;
+ u_char ack_data;
+ int retv = 1;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB (nc_gpreg);
+ old_gpcntl = INB (nc_gpcntl);
+ gpcntl = old_gpcntl & 0xfc;
+
+ /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+ OUTB (nc_gpreg, old_gpreg);
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* this is to set NVRAM into a known state with GPIO0/1 both low */
+ gpreg = old_gpreg;
+ nvram_setBit(np, 0, &gpreg, CLR_CLK);
+ nvram_setBit(np, 0, &gpreg, CLR_BIT);
+
+ /* now set NVRAM inactive with GPIO0/1 both high */
+ nvram_stop(np, &gpreg);
+
+ /* activate NVRAM */
+ nvram_start(np, &gpreg);
+
+ /* write device code and random address MSB */
+ nvram_write_byte(np, &ack_data,
+ 0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* write random address LSB */
+ nvram_write_byte(np, &ack_data,
+ (SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* regenerate START state to set up for reading */
+ nvram_start(np, &gpreg);
+
+ /* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+ nvram_write_byte(np, &ack_data,
+ 0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* now set up GPIO0 for inputting data */
+ gpcntl |= 0x01;
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* input all active data - only part of total NVRAM */
+ csum = nvram_read_data(np,
+ (u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl);
+
+ /* finally put NVRAM back in inactive mode */
+ gpcntl &= 0xfe;
+ OUTB (nc_gpcntl, gpcntl);
+ nvram_stop(np, &gpreg);
+
+#ifdef SCSI_NCR_DEBUG_NVRAM
+printf("ncr53c8xx: NvRAM marker=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
+ nvram->start_marker,
+ nvram->trailer[0], nvram->trailer[1], nvram->trailer[2],
+ nvram->trailer[3], nvram->trailer[4], nvram->trailer[5],
+ nvram->byte_count, sizeof(*nvram) - 12,
+ nvram->checksum, csum);
+#endif
+
+ /* check valid NVRAM signature, verify byte count and checksum */
+ if (nvram->start_marker == 0 &&
+ !memcmp(nvram->trailer, Symbios_trailer, 6) &&
+ nvram->byte_count == sizeof(*nvram) - 12 &&
+ csum == nvram->checksum)
+ retv = 0;
+out:
+ /* return GPIO0/1 to original states after having accessed NVRAM */
+ OUTB (nc_gpcntl, old_gpcntl);
+ OUTB (nc_gpreg, old_gpreg);
+
+ return retv;
+}
+
+/*
+ * Read Symbios NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl)
+)
+{
+ int x;
+ u_short csum;
+
+ for (x = 0; x < len; x++)
+ nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl);
+
+ for (x = 6, csum = 0; x < len - 6; x++)
+ csum += data[x];
+
+ return csum;
+}
+
+/*
+ * Send START condition to NVRAM to wake it up.
+ */
+__initfunc(
+static void nvram_start(ncr_slot *np, u_char *gpreg)
+)
+{
+ nvram_setBit(np, 1, gpreg, SET_BIT);
+ nvram_setBit(np, 0, gpreg, SET_CLK);
+ nvram_setBit(np, 0, gpreg, CLR_BIT);
+ nvram_setBit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ * GPIO0 must already be set as an output
+ */
+__initfunc(
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+ int x;
+
+ for (x = 0; x < 8; x++)
+ nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+
+ nvram_readAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * READ a byte from the NVRAM and then send an ACK to say we have got it,
+ * GPIO0 must already be set as an input
+ */
+__initfunc(
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+ int x;
+ u_char read_bit;
+
+ *read_data = 0;
+ for (x = 0; x < 8; x++) {
+ nvram_doBit(np, &read_bit, 1, gpreg);
+ *read_data |= ((read_bit & 0x01) << (7 - x));
+ }
+
+ nvram_writeAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * Output an ACK to the NVRAM after reading,
+ * change GPIO0 to output and when done back to an input
+ */
+__initfunc(
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+ OUTB (nc_gpcntl, *gpcntl & 0xfe);
+ nvram_doBit(np, 0, write_bit, gpreg);
+ OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Input an ACK from NVRAM after writing,
+ * change GPIO0 to input and when done back to an output
+ */
+__initfunc(
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+ OUTB (nc_gpcntl, *gpcntl | 0x01);
+ nvram_doBit(np, read_bit, 1, gpreg);
+ OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Read or write a bit to the NVRAM,
+ * read if GPIO0 input else write if GPIO0 output
+ */
+__initfunc(
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
+)
+{
+ nvram_setBit(np, write_bit, gpreg, SET_BIT);
+ nvram_setBit(np, 0, gpreg, SET_CLK);
+ if (read_bit)
+ *read_bit = INB (nc_gpreg);
+ nvram_setBit(np, 0, gpreg, CLR_CLK);
+ nvram_setBit(np, 0, gpreg, CLR_BIT);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+__initfunc(
+static void nvram_stop(ncr_slot *np, u_char *gpreg)
+)
+{
+ nvram_setBit(np, 0, gpreg, SET_CLK);
+ nvram_setBit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ * Set/clear data/clock bit in GPIO0
+ */
+__initfunc(
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+)
+{
+ DELAY(5);
+ switch (bit_mode){
+ case SET_BIT:
+ *gpreg |= write_bit;
+ break;
+ case CLR_BIT:
+ *gpreg &= 0xfe;
+ break;
+ case SET_CLK:
+ *gpreg |= 0x02;
+ break;
+ case CLR_CLK:
+ *gpreg &= 0xfd;
+ break;
+
+ }
+ OUTB (nc_gpreg, *gpreg);
+ DELAY(5);
+}
+
+#undef SET_BIT 0
+#undef CLR_BIT 1
+#undef SET_CLK 2
+#undef CLR_CLK 3
+
+
+/* ---------------------------------------------------------------------
+**
+** Try reading Tekram format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in
+** GPIO1 - data out
+** GPIO2 - clock
+** GPIO4 - chip select
+**
+** return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg);
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg);
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg);
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg);
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg);
+
+__initfunc(
+static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram)
+)
+{
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ u_short csum;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB (nc_gpreg);
+ old_gpcntl = INB (nc_gpcntl);
+
+ /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
+ 1/2/4 out */
+ gpreg = old_gpreg & 0xe9;
+ OUTB (nc_gpreg, gpreg);
+ gpcntl = (old_gpcntl & 0xe9) | 0x09;
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* input all of NVRAM, 64 words */
+ csum = Tnvram_read_data(np, (u_short *) nvram,
+ sizeof(*nvram) / sizeof(short), &gpreg);
+
+ /* return GPIO0/1/2/4 to original states after having accessed NVRAM */
+ OUTB (nc_gpcntl, old_gpcntl);
+ OUTB (nc_gpreg, old_gpreg);
+
+ /* check data valid */
+ if (csum != 0x1234)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Read Tekram NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg)
+)
+{
+ u_char read_bit;
+ u_short csum;
+ int x;
+
+ for (x = 0, csum = 0; x < len; x++) {
+
+ /* output read command and address */
+ Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+ if (read_bit & 0x01)
+ return 0; /* Force bad checksum */
+
+ Tnvram_Read_Word(np, &data[x], gpreg);
+ csum += data[x];
+
+ Tnvram_Stop(np, gpreg);
+ }
+
+ return csum;
+}
+
+/*
+ * Send read command and address to NVRAM
+ */
+__initfunc(
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg)
+)
+{
+ int x;
+
+ /* send 9 bits, start bit (1), command (2), address (6) */
+ for (x = 0; x < 9; x++)
+ Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+ *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * READ a byte from the NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
+)
+{
+ int x;
+ u_char read_bit;
+
+ *nvram_data = 0;
+ for (x = 0; x < 16; x++) {
+ Tnvram_Read_Bit(np, &read_bit, gpreg);
+
+ if (read_bit & 0x01)
+ *nvram_data |= (0x01 << (15 - x));
+ else
+ *nvram_data &= ~(0x01 << (15 - x));
+ }
+}
+
+/*
+ * Read bit from NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
+)
+{
+ DELAY(2);
+ Tnvram_Clk(np, gpreg);
+ *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * Write bit to GPIO0
+ */
+__initfunc(
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+)
+{
+ if (write_bit & 0x01)
+ *gpreg |= 0x02;
+ else
+ *gpreg &= 0xfd;
+
+ *gpreg |= 0x10;
+
+ OUTB (nc_gpreg, *gpreg);
+ DELAY(2);
+
+ Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+__initfunc(
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg)
+)
+{
+ *gpreg &= 0xef;
+ OUTB (nc_gpreg, *gpreg);
+ DELAY(2);
+
+ Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Pulse clock bit in GPIO0
+ */
+__initfunc(
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg)
+)
+{
+ OUTB (nc_gpreg, *gpreg | 0x04);
+ DELAY(2);
+ OUTB (nc_gpreg, *gpreg);
+}
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
/*
** Module stuff
*/
diff --git a/drivers/scsi/ncr53c8xx.h b/drivers/scsi/ncr53c8xx.h
index 412c66a37..b22565b11 100644
--- a/drivers/scsi/ncr53c8xx.h
+++ b/drivers/scsi/ncr53c8xx.h
@@ -45,7 +45,7 @@
/*
** Name and revision of the driver
*/
-#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 2.1b"
+#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 2.5a"
/*
** Check supported Linux versions
@@ -95,6 +95,7 @@
#define SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
#define SCSI_NCR_DEBUG_INFO_SUPPORT
+#define SCSI_NCR_PCI_FIX_UP_SUPPORT
#ifdef SCSI_NCR_PROC_INFO_SUPPORT
# define SCSI_NCR_PROFILE_SUPPORT
# define SCSI_NCR_USER_COMMAND_SUPPORT
@@ -102,6 +103,18 @@
/* # define SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT */
#endif
+/*==========================================================
+**
+** nvram settings - #define SCSI_NCR_NVRAM_SUPPORT to enable
+**
+**==========================================================
+*/
+
+#ifdef CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
+#define SCSI_NCR_NVRAM_SUPPORT
+/* #define SCSI_NCR_DEBUG_NVRAM */
+#endif
+
/* ---------------------------------------------------------------------
** Take into account kernel configured parameters.
** Most of these options can be overridden at startup by a command line.
@@ -142,9 +155,11 @@
#endif
/*
- * Use normal IO if configured. Forced for alpha.
+ * Use normal IO if configured. Forced for alpha and ppc.
*/
-#if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED) || defined(__alpha__)
+#if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED)
+#define SCSI_NCR_IOMAPPED
+#elif defined(__alpha__) || defined(__powerpc__)
#define SCSI_NCR_IOMAPPED
#endif
@@ -303,6 +318,45 @@ int ncr53c8xx_release(struct Scsi_Host *);
#ifndef HOSTS_C
/*
+** IO functions definition for big/little endian support.
+** For now, the NCR is only supported in little endian addressing mode,
+** and big endian byte ordering is only supported for the PPC.
+** MMIO is not used on PPC.
+*/
+
+#ifdef __BIG_ENDIAN
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
+#error "BIG ENDIAN byte ordering needs kernel version >= 2.1.0"
+#endif
+
+#ifdef __powerpc__
+#define inw_l2b inw
+#define inl_l2b inl
+#define outw_b2l outw
+#define outl_b2l outl
+#else
+#error "Support for BIG ENDIAN is only available for the PowerPC"
+#endif
+
+#else /* Assumed x86 or alpha */
+
+#define inw_raw inw
+#define inl_raw inl
+#define outw_raw outw
+#define outl_raw outl
+#define readw_raw readw
+#define readl_raw readl
+#define writew_raw writew
+#define writel_raw writel
+
+#endif
+
+#ifdef SCSI_NCR_BIG_ENDIAN
+#error "The NCR in BIG ENDIAN adressing mode is not (yet) supported"
+#endif
+
+/*
** NCR53C8XX Device Ids
*/
@@ -361,67 +415,67 @@ typedef struct {
unsigned char offset_max;
unsigned char nr_divisor;
unsigned int features;
-#define _F_LED0 (1<<0)
-#define _F_WIDE (1<<1)
-#define _F_ULTRA (1<<2)
-#define _F_ULTRA2 (1<<3)
-#define _F_DBLR (1<<4)
-#define _F_QUAD (1<<5)
-#define _F_ERL (1<<6)
-#define _F_CLSE (1<<7)
-#define _F_WRIE (1<<8)
-#define _F_ERMP (1<<9)
-#define _F_BOF (1<<10)
-#define _F_DFS (1<<11)
-#define _F_PFEN (1<<12)
-#define _F_LDSTR (1<<13)
-#define _F_RAM (1<<14)
-#define _F_CLK80 (1<<15)
-#define _F_CACHE_SET (_F_ERL|_F_CLSE|_F_WRIE|_F_ERMP)
-#define _F_SCSI_SET (_F_WIDE|_F_ULTRA|_F_ULTRA2|_F_DBLR|_F_QUAD|F_CLK80)
-#define _F_SPECIAL_SET (_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM)
+#define FE_LED0 (1<<0)
+#define FE_WIDE (1<<1)
+#define FE_ULTRA (1<<2)
+#define FE_ULTRA2 (1<<3)
+#define FE_DBLR (1<<4)
+#define FE_QUAD (1<<5)
+#define FE_ERL (1<<6)
+#define FE_CLSE (1<<7)
+#define FE_WRIE (1<<8)
+#define FE_ERMP (1<<9)
+#define FE_BOF (1<<10)
+#define FE_DFS (1<<11)
+#define FE_PFEN (1<<12)
+#define FE_LDSTR (1<<13)
+#define FE_RAM (1<<14)
+#define FE_CLK80 (1<<15)
+#define FE_CACHE_SET (FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP)
+#define FE_SCSI_SET (FE_WIDE|FE_ULTRA|FE_ULTRA2|FE_DBLR|FE_QUAD|F_CLK80)
+#define FE_SPECIAL_SET (FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM)
} ncr_chip;
#define SCSI_NCR_CHIP_TABLE \
{ \
{PCI_DEVICE_ID_NCR_53C810, 0x0f, "810", 4, 8, 4, \
- _F_ERL} \
+ FE_ERL} \
, \
{PCI_DEVICE_ID_NCR_53C810, 0xff, "810a", 4, 8, 4, \
- _F_CACHE_SET|_F_LDSTR|_F_PFEN|_F_BOF} \
+ FE_CACHE_SET|FE_LDSTR|FE_PFEN|FE_BOF} \
, \
{PCI_DEVICE_ID_NCR_53C815, 0xff, "815", 4, 8, 4, \
- _F_ERL|_F_BOF} \
+ FE_ERL|FE_BOF} \
, \
{PCI_DEVICE_ID_NCR_53C820, 0xff, "820", 4, 8, 4, \
- _F_WIDE|_F_ERL} \
+ FE_WIDE|FE_ERL} \
, \
{PCI_DEVICE_ID_NCR_53C825, 0x0f, "825", 4, 8, 4, \
- _F_WIDE|_F_ERL|_F_BOF} \
+ FE_WIDE|FE_ERL|FE_BOF} \
, \
{PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 7, 8, 4, \
- _F_WIDE|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} \
, \
{PCI_DEVICE_ID_NCR_53C860, 0xff, "860", 4, 8, 5, \
- _F_WIDE|_F_ULTRA|_F_CLK80|_F_CACHE_SET|_F_BOF|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN} \
, \
{PCI_DEVICE_ID_NCR_53C875, 0x01, "875", 7, 16, 5, \
- _F_WIDE|_F_ULTRA|_F_CLK80|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
{PCI_DEVICE_ID_NCR_53C875, 0xff, "875", 7, 16, 5, \
- _F_WIDE|_F_ULTRA|_F_DBLR|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
{PCI_DEVICE_ID_NCR_53C875J, 0xff, "875J", 7, 16, 5, \
- _F_WIDE|_F_ULTRA|_F_DBLR|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
{PCI_DEVICE_ID_NCR_53C885, 0xff, "885", 7, 16, 5, \
- _F_WIDE|_F_ULTRA|_F_DBLR|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
{PCI_DEVICE_ID_NCR_53C895, 0xff, "895", 7, 31, 7, \
- _F_WIDE|_F_ULTRA|_F_ULTRA2|_F_QUAD|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
{PCI_DEVICE_ID_NCR_53C896, 0xff, "896", 7, 31, 7, \
- _F_WIDE|_F_ULTRA|_F_ULTRA2|_F_QUAD|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
}
/*
@@ -456,6 +510,7 @@ typedef struct {
0, \
0, \
1, \
+ 1, \
SCSI_NCR_SETUP_DEFAULT_TAGS, \
SCSI_NCR_SETUP_DEFAULT_SYNC, \
0x00, \
@@ -482,6 +537,7 @@ typedef struct {
0, \
0, \
0, \
+ 1, \
2, \
0, \
255, \
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index b8620dfdc..ab6c12f5c 100644
--- a/drivers/scsi/qlogicpti.c
+++ b/drivers/scsi/qlogicpti.c
@@ -37,6 +37,7 @@
#include <asm/oplib.h>
#include <asm/vaddrs.h>
#include <asm/io.h>
+#include <asm/irq.h>
#include <linux/module.h>
@@ -351,7 +352,7 @@ __initfunc(static int qlogicpti_load_firmware(struct qlogicpti *qpti))
unsigned short csum = 0;
unsigned short param[6];
unsigned long flags;
-#ifndef MODULE
+#if !defined(MODULE) && !defined(__sparc_v9__)
unsigned long dvma_addr;
#endif
int i, timeout;
@@ -419,8 +420,7 @@ __initfunc(static int qlogicpti_load_firmware(struct qlogicpti *qpti))
}
/* Load the firmware. */
-#ifndef MODULE
- /* XXX THIS SHIT DOES NOT WORK ON ULTRA... FIXME -DaveM */
+#if !defined(MODULE) && !defined(__sparc_v9__)
dvma_addr = (unsigned long) mmu_lockarea((char *)&risc_code01[0],
(sizeof(u_short) * risc_code_length01));
param[0] = MBOX_LOAD_RAM;
@@ -435,9 +435,9 @@ __initfunc(static int qlogicpti_load_firmware(struct qlogicpti *qpti))
restore_flags(flags);
return 1;
}
- /* XXX THIS SHIT DOES NOT WORK ON ULTRA... FIXME -DaveM */
mmu_unlockarea((char *)dvma_addr, (sizeof(u_short) * risc_code_length01));
#else
+ /* We need to do it this slow way always on Ultra. */
for(i = 0; i < risc_code_length01; i++) {
param[0] = MBOX_WRITE_RAM_WORD;
param[1] = risc_code_addr01 + i;
@@ -566,6 +566,9 @@ static void qlogicpti_intr_handler(int irq, void *dev_id, struct pt_regs *regs);
/* Detect all PTI Qlogic ISP's in the machine. */
__initfunc(int qlogicpti_detect(Scsi_Host_Template *tpnt))
{
+#ifdef __sparc_v9__
+ struct devid_cookie dcookie;
+#endif
struct qlogicpti *qpti, *qlink;
struct Scsi_Host *qpti_host;
struct linux_sbus *sbus;
@@ -643,7 +646,7 @@ __initfunc(int qlogicpti_detect(Scsi_Host_Template *tpnt))
/* Map this one read only. */
qpti->sreg = sreg = (volatile unsigned char *)
sparc_alloc_io((qpti->qdev->reg_addrs[0].phys_addr +
- (16 * PAGE_SIZE)), 0,
+ (16 * 4096)), 0,
sizeof(unsigned char),
"PTI Qlogic/ISP Status Reg",
qpti->qdev->reg_addrs[0].which_io, 1);
@@ -659,6 +662,7 @@ __initfunc(int qlogicpti_detect(Scsi_Host_Template *tpnt))
qpti_host->irq = qpti->irq = qpti->qdev->irqs[0].pri;
+#ifndef __sparc_v9__
/* Allocate the irq only if necessary. */
for_each_qlogicpti(qlink) {
if((qlink != qpti) && (qpti->irq == qlink->irq)) {
@@ -673,6 +677,25 @@ __initfunc(int qlogicpti_detect(Scsi_Host_Template *tpnt))
}
qpti_irq_acquired:
printk("qpti%d: IRQ %d ", qpti->qpti_id, qpti->qhost->irq);
+#else
+ /* On Ultra we must always call request_irq for each
+ * qpti, so that imap registers get setup etc.
+ */
+ dcookie.real_dev_id = qpti;
+ dcookie.imap = dcookie.iclr = 0;
+ dcookie.pil = -1;
+ dcookie.bus_cookie = sbus;
+ if(request_irq(qpti->qhost->irq, qlogicpti_intr_handler,
+ (SA_SHIRQ | SA_SBUS | SA_DCOOKIE),
+ "PTI Qlogic/ISP SCSI", &dcookie)) {
+ printk("Cannot acquire PTI Qlogic/ISP irq line\n");
+ /* XXX Unmap regs, unregister scsi host, free things. */
+ continue;
+ }
+ qpti->qhost->irq = qpti->irq = dcookie.ret_ino;
+ printk("qpti%d: INO[%x] IRQ %d ",
+ qpti->qpti_id, qpti->qhost->irq, dcookie.ret_pil);
+#endif
/* Figure out our scsi ID on the bus */
qpti->scsi_id = prom_getintdefault(qpti->prom_node,
@@ -805,7 +828,11 @@ static inline void cmd_frob(struct Command_Entry *cmd, Scsi_Cmnd *Cmnd,
memset(cmd, 0, sizeof(struct Command_Entry));
cmd->hdr.entry_cnt = 1;
cmd->hdr.entry_type = ENTRY_COMMAND;
- cmd->handle = (u_int) ((unsigned long)Cmnd); /* magic mushroom */
+#ifdef __sparc_v9__
+ cmd->handle = (u_int) (((unsigned long)Cmnd) - PAGE_OFFSET); /* magic mushroom */
+#else
+ cmd->handle = (u_int) ((unsigned long)Cmnd); /* magic mushroom */
+#endif
cmd->target_id = Cmnd->target;
cmd->target_lun = Cmnd->lun;
cmd->cdb_length = Cmnd->cmd_len;
@@ -1013,6 +1040,8 @@ static int qlogicpti_return_status(struct Status_Entry *sts)
return (sts->scsi_status & STATUS_MASK) | (host_status << 16);
}
+#ifndef __sparc_v9__
+
static void qlogicpti_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
{
static int running = 0;
@@ -1099,6 +1128,76 @@ repeat:
running--;
}
+#else /* __sparc_v9__ */
+
+static void qlogicpti_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct qlogicpti *qpti = dev_id;
+ Scsi_Cmnd *Cmnd;
+ struct Status_Entry *sts;
+ u_int in_ptr, out_ptr;
+
+ if(qpti->qregs->sbus_stat & SBUS_STAT_RINT) {
+ struct qlogicpti_regs *qregs = qpti->qregs;
+
+ in_ptr = qregs->mbox5;
+ qregs->hcctrl = HCCTRL_CRIRQ;
+ if(qregs->sbus_semaphore & SBUS_SEMAPHORE_LCK) {
+ switch(qregs->mbox0) {
+ case ASYNC_SCSI_BUS_RESET:
+ case EXECUTION_TIMEOUT_RESET:
+ qpti->send_marker = 1;
+ break;
+ case INVALID_COMMAND:
+ case HOST_INTERFACE_ERROR:
+ case COMMAND_ERROR:
+ case COMMAND_PARAM_ERROR:
+ break;
+ }
+ qregs->sbus_semaphore = 0;
+ }
+
+ /* This looks like a network driver! */
+ out_ptr = qpti->res_out_ptr;
+ while(out_ptr != in_ptr) {
+ sts = (struct Status_Entry *) &qpti->res_cpu[out_ptr];
+ out_ptr = NEXT_RES_PTR(out_ptr);
+ Cmnd = (Scsi_Cmnd *) (((unsigned long)sts->handle)+PAGE_OFFSET);
+
+ if(sts->completion_status == CS_RESET_OCCURRED ||
+ sts->completion_status == CS_ABORTED ||
+ (sts->status_flags & STF_BUS_RESET))
+ qpti->send_marker = 1;
+
+ if(sts->state_flags & SF_GOT_SENSE)
+ memcpy(Cmnd->sense_buffer, sts->req_sense_data,
+ sizeof(Cmnd->sense_buffer));
+
+ if(sts->hdr.entry_type == ENTRY_STATUS)
+ Cmnd->result = qlogicpti_return_status(sts);
+ else
+ Cmnd->result = DID_ERROR << 16;
+
+ if(Cmnd->use_sg)
+ mmu_release_scsi_sgl((struct mmu_sglist *)
+ Cmnd->buffer,
+ Cmnd->use_sg - 1,
+ qpti->qdev->my_bus);
+ else
+ mmu_release_scsi_one((__u32)((unsigned long)Cmnd->SCp.ptr),
+ Cmnd->request_bufflen,
+ qpti->qdev->my_bus);
+
+ qpti->cmd_count[Cmnd->target]--;
+ qregs->mbox5 = out_ptr;
+ Cmnd->scsi_done(Cmnd);
+ }
+ qpti->res_out_ptr = out_ptr;
+ }
+}
+
+#endif
+
int qlogicpti_abort(Scsi_Cmnd *Cmnd)
{
u_short param[6];
diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
index 023a1c448..6404809cc 100644
--- a/drivers/scsi/scsi.h
+++ b/drivers/scsi/scsi.h
@@ -37,6 +37,7 @@
#endif
#define MAX_SCSI_DEVICE_CODE 10
+extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
extern void scsi_make_blocked_list(void);
extern volatile int in_scan_scsis;
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
index 869fa4e2a..073de6095 100644
--- a/drivers/scsi/scsi_ioctl.c
+++ b/drivers/scsi/scsi_ioctl.c
@@ -9,6 +9,7 @@
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
+#include <asm/page.h>
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -23,7 +24,7 @@
#define MAX_RETRIES 5
#define MAX_TIMEOUT (9 * HZ)
-#define MAX_BUF 4096
+#define MAX_BUF PAGE_SIZE
#define max(a,b) (((a) > (b)) ? (a) : (b))
@@ -73,12 +74,9 @@ static int ioctl_probe(struct Scsi_Host * host, void *buffer)
*
* *(char *) ((int *) arg)[2] the actual command byte.
*
- * Note that no more than MAX_BUF data bytes will be transfered. Since
- * SCSI block device size is 512 bytes, I figured 1K was good.
- * but (WDE) changed it to 8192 to handle large bad track buffers.
- * ERY: I changed this to a dynamic allocation using scsi_malloc - we were
- * getting a kernel stack overflow which was crashing the system when we
- * were using 8192 bytes.
+ * Note that if more than MAX_BUF bytes are requested to be transfered,
+ * the ioctl will fail with error EINVAL. MAX_BUF can be increased in
+ * the future by increasing the size that scsi_malloc will accept.
*
* This size *does not* include the initial lengths that were passed.
*
@@ -201,8 +199,8 @@ static int ioctl_command(Scsi_Device *dev, Scsi_Ioctl_Command *sic)
* If the user needs to transfer more data than this, they
* should use scsi_generics instead.
*/
- if( inlen > MAX_BUF ) inlen = MAX_BUF;
- if( outlen > MAX_BUF ) outlen = MAX_BUF;
+ if( inlen > MAX_BUF ) return -EINVAL;
+ if( outlen > MAX_BUF ) return -EINVAL;
cmd_in = sic->data;
get_user(opcode, cmd_in);
diff --git a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c
index 17469967f..bf82a51cb 100644
--- a/drivers/scsi/scsi_syms.c
+++ b/drivers/scsi/scsi_syms.c
@@ -72,5 +72,7 @@ EXPORT_SYMBOL(scsi_hostlist);
EXPORT_SYMBOL(scsi_hosts);
EXPORT_SYMBOL(scsi_devicelist);
EXPORT_SYMBOL(scsi_devices);
+EXPORT_SYMBOL(scsi_device_types);
+
#endif /* CONFIG_MODULES */
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index eb68d383b..4ec0a82fa 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1509,8 +1509,9 @@ int revalidate_scsidisk(kdev_t dev, int maxusage){
for (i=max_p - 1; i >=0 ; i--) {
int minor = start+i;
kdev_t devi = MKDEV(MAJOR_NR, minor);
+ struct super_block *sb = get_super(devi);
sync_dev(devi);
- invalidate_inodes(devi);
+ if (sb) invalidate_inodes(sb);
invalidate_buffers(devi);
gdev->part[minor].start_sect = 0;
gdev->part[minor].nr_sects = 0;
@@ -1561,8 +1562,9 @@ static void sd_detach(Scsi_Device * SDp)
for (i=max_p - 1; i >=0 ; i--) {
int minor = start+i;
kdev_t devi = MKDEV(MAJOR_NR, minor);
+ struct super_block *sb = get_super(devi);
sync_dev(devi);
- invalidate_inodes(devi);
+ if (sb) invalidate_inodes(sb);
invalidate_buffers(devi);
sd_gendisk.part[minor].start_sect = 0;
sd_gendisk.part[minor].nr_sects = 0;
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 458335638..517568346 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -1007,12 +1007,13 @@ static void sr_detach(Scsi_Device * SDp)
for(cpnt = scsi_CDs, i=0; i<sr_template.dev_max; i++, cpnt++)
if(cpnt->device == SDp) {
kdev_t devi = MKDEV(MAJOR_NR, i);
+ struct super_block * sb = get_super(devi);
/*
* Since the cdrom is read-only, no need to sync the device.
* We should be kind to our buffer cache, however.
*/
- invalidate_inodes(devi);
+ if (sb) invalidate_inodes(sb);
invalidate_buffers(devi);
/*
diff --git a/drivers/scsi/ultrastor.c b/drivers/scsi/ultrastor.c
index 19b451b24..20f90e6e0 100644
--- a/drivers/scsi/ultrastor.c
+++ b/drivers/scsi/ultrastor.c
@@ -1144,8 +1144,13 @@ static void ultrastor_interrupt(int irq, void *dev_id, struct pt_regs *regs)
else
printk("US14F: interrupt: unexpected interrupt\n");
- if (config.slot ? inb(config.icm_address - 1) : (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+ if (config.slot ? inb(config.icm_address - 1) :
+ (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+#if (ULTRASTOR_DEBUG & UD_MULTI_CMD)
printk("Ux4F: multiple commands completed\n");
+#else
+ ;
+#endif
#if (ULTRASTOR_DEBUG & UD_INTERRUPT)
printk("USx4F: interrupt: returning\n");
diff --git a/drivers/sgi/char/cons_newport.c b/drivers/sgi/char/cons_newport.c
index 1fb02c0b5..38e86ded6 100644
--- a/drivers/sgi/char/cons_newport.c
+++ b/drivers/sgi/char/cons_newport.c
@@ -1,7 +1,9 @@
-/* $Id: cons_newport.c,v 1.3 1997/08/26 04:35:53 miguel Exp $
+/*
* cons_newport.c: Newport graphics console code for the SGI.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ *
+ * $Id:$
*/
#include <linux/kernel.h>
@@ -255,7 +257,7 @@ newport_hide_cursor(void)
if(vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
return;
- save_flags(flags); cli();
+ save_and_cli(flags);
idx = cursor_pos;
if(idx == -1) {
@@ -282,7 +284,7 @@ newport_set_cursor(int currcons)
if (__real_origin != __origin)
__set_origin(__real_origin);
- save_flags(flags); cli();
+ save_and_cli(flags);
idx = (pos - video_mem_base) >> 1;
sp = (unsigned short *) pos;