diff options
| author | James Simmons <jsimmons@maxwell.earthlink.net> | 2002-07-21 20:42:48 -0700 |
|---|---|---|
| committer | James Simmons <jsimmons@maxwell.earthlink.net> | 2002-07-21 20:42:48 -0700 |
| commit | 975f679b6b9e7321503694de9ea739280374f741 (patch) | |
| tree | 36b1f6247ba050ad7406166d0ff1ca499344cfd6 /drivers | |
| parent | 9a56acef9e6203b444a956d7e45a49a91cefbfb0 (diff) | |
| parent | 4872eaccd9c1926c2e047abd761a1076eb7c4d11 (diff) | |
Merge http://linus.bkbits.net/linux-2.5
into maxwell.earthlink.net:/tmp/linus-2.5
Diffstat (limited to 'drivers')
273 files changed, 19811 insertions, 19508 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index e858842760d3..3348f9765ba6 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_PCI) += pci/ obj-$(CONFIG_ACPI) += acpi/ obj-$(CONFIG_PARPORT) += parport/ -obj-y += base/ char/ block/ misc/ net/ media/ +obj-y += base/ serial/ char/ block/ misc/ net/ media/ obj-$(CONFIG_NUBUS) += nubus/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_IDE) += ide/ diff --git a/drivers/acorn/block/mfmhd.c b/drivers/acorn/block/mfmhd.c index fd41fe9d58b7..309fc7c4d47d 100644 --- a/drivers/acorn/block/mfmhd.c +++ b/drivers/acorn/block/mfmhd.c @@ -184,8 +184,6 @@ struct mfm_info { #define NEED_1_RECAL -2 #define NEED_2_RECAL -3 int cylinder; - unsigned int access_count; - unsigned int busy; struct { char recal; char report; @@ -197,7 +195,6 @@ struct mfm_info { static struct hd_struct mfm[MFM_MAXDRIVES << 6]; static int mfm_sizes[MFM_MAXDRIVES << 6]; -static DECLARE_WAIT_QUEUE_HEAD(mfm_wait_open); /* Stuff from the assembly routines */ extern unsigned int hdc63463_baseaddress; /* Controller base address */ @@ -1195,22 +1192,11 @@ static int mfm_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long a return -EFAULT; return 0; - case BLKSECTGET: - return put_user(max_sectors[major][minor], (long *) arg); - case BLKRRPART: if (!capable(CAP_SYS_ADMIN)) return -EACCES; return mfm_reread_partitions(dev); - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKFLSBUF: - case BLKROSET: - case BLKROGET: - case BLKPG: - return blk_ioctl(inode->i_bdev, cmd, arg); - default: return -EINVAL; } @@ -1219,24 +1205,8 @@ static int mfm_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long a static int mfm_open(struct inode *inode, struct file *file) { int dev = DEVICE_NR(minor(inode->i_rdev)); - if (dev >= mfm_drives) return -ENODEV; - - while (mfm_info[dev].busy) - sleep_on (&mfm_wait_open); - - mfm_info[dev].access_count++; - return 0; -} - -/* - * Releasing a block device means we sync() it, so that it can safely - * be forgotten about... - */ -static int mfm_release(struct inode *inode, struct file *file) -{ - mfm_info[DEVICE_NR(minor(inode->i_rdev))].access_count--; return 0; } @@ -1254,10 +1224,11 @@ void mfm_setup(char *str, int *ints) * since if there are any non-ADFS partitions on the disk, this won't work! * Hence, I want to get rid of this... */ -void xd_set_geometry(kdev_t dev, unsigned char secsptrack, unsigned char heads, - unsigned long discsize, unsigned int secsize) +void xd_set_geometry(struct block_device *bdev, unsigned char secsptrack, + unsigned char heads, unsigned int secsize) { - int drive = minor(dev) >> 6; + int drive = MINOR(bdev->bd_dev) >> 6; + unsigned long disksize = bdev->bd_inode->i_size; if (mfm_info[drive].cylinders == 1) { mfm_info[drive].sectors = secsptrack; @@ -1265,7 +1236,7 @@ void xd_set_geometry(kdev_t dev, unsigned char secsptrack, unsigned char heads, mfm_info[drive].cylinders = discsize / (secsptrack * heads * secsize); if ((heads < 1) || (mfm_info[drive].cylinders > 1024)) { - printk("mfm%c: Insane disc shape! Setting to 512/4/32\n",'a' + (dev >> 6)); + printk("mfm%c: Insane disc shape! Setting to 512/4/32\n",'a' + drive); /* These values are fairly arbitary, but are there so that if your * lucky you can pick apart your disc to find out what is going on - @@ -1295,7 +1266,6 @@ static struct block_device_operations mfm_fops = { owner: THIS_MODULE, open: mfm_open, - release: mfm_release, ioctl: mfm_ioctl, }; @@ -1424,38 +1394,19 @@ int mfm_init (void) /* * This routine is called to flush all partitions and partition tables * for a changed MFM disk, and then re-read the new partition table. - * If we are revalidating due to an ioctl, we have USAGE == 1. */ static int mfm_reread_partitions(kdev_t dev) { - unsigned int start, i, maxp, target = DEVICE_NR(minor(dev)); - unsigned long flags; - - local_irq_save(flags); - if (mfm_info[target].busy || mfm_info[target].access_count > 1) { - local_irq_restore (flags); - return -EBUSY; - } - mfm_info[target].busy = 1; - local_irq_restore (flags); - - maxp = 1 << mfm_gendisk.minor_shift; - start = target << mfm_gendisk.minor_shift; - - for (i = maxp - 1; i >= 0; i--) { - int minor = start + i; - invalidate_device (mk_kdev(MAJOR_NR, minor), 1); - mfm_gendisk.part[minor].start_sect = 0; - mfm_gendisk.part[minor].nr_sects = 0; - } - + unsigned int unit = DEVICE_NR(minor(dev)); + kdev_t device = mk_kdev(MAJOR_NR, unit << mfm_gendisk.minor_shift); + int err = dev_lock_part(device); + if (err) + return err; + wipe_partitions(device); /* Divide by 2, since sectors are 2 times smaller than usual ;-) */ - - grok_partitions(&mfm_gendisk, target, 1<<6, mfm_info[target].heads * - mfm_info[target].cylinders * mfm_info[target].sectors / 2); - - mfm_info[target].busy = 0; - wake_up (&mfm_wait_open); + grok_partitions(device, mfm_info[unit].heads * + mfm_info[unit].cylinders * mfm_info[unit].sectors / 2); + dev_unlock_part(device); return 0; } diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index 210449ad1715..dc372aa4dbb1 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -2055,29 +2055,20 @@ static void DAC960_ComputeGenericDiskInfo(DAC960_Controller_T *Controller) static void DAC960_RegisterDisk(DAC960_Controller_T *Controller, int LogicalDriveNumber) { - if (Controller->FirmwareType == DAC960_V1_Controller) - { + long size; + if (Controller->FirmwareType == DAC960_V1_Controller) { if (LogicalDriveNumber > Controller->LogicalDriveCount - 1) return; - register_disk(&Controller->GenericDiskInfo, - DAC960_KernelDevice(Controller->ControllerNumber, - LogicalDriveNumber, 0), - DAC960_MaxPartitions, - &DAC960_BlockDeviceOperations, - Controller->V1.LogicalDriveInformation - [LogicalDriveNumber].LogicalDriveSize); - } - else - { + size = Controller->V1.LogicalDriveInformation + [LogicalDriveNumber].LogicalDriveSize; + } else { DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo = Controller->V2.LogicalDeviceInformation[LogicalDriveNumber]; if (LogicalDeviceInfo == NULL) return; - register_disk(&Controller->GenericDiskInfo, - DAC960_KernelDevice(Controller->ControllerNumber, - LogicalDriveNumber, 0), - DAC960_MaxPartitions, - &DAC960_BlockDeviceOperations, - LogicalDeviceInfo->ConfigurableDeviceSize); - } + size = LogicalDeviceInfo->ConfigurableDeviceSize; + } + register_disk(&Controller->GenericDiskInfo, + DAC960_KernelDevice(Controller->ControllerNumber, LogicalDriveNumber, 0), + DAC960_MaxPartitions, &DAC960_BlockDeviceOperations, size); } @@ -5399,15 +5390,9 @@ static int DAC960_IOCTL(Inode_T *Inode, File_T *File, LogicalDeviceInfo->ConfigurableDeviceSize / (Geometry.heads * Geometry.sectors); } - Geometry.start = get_start_sect(Inode->i_rdev); + Geometry.start = get_start_sect(Inode->i_bdev); return (copy_to_user(UserGeometry, &Geometry, sizeof(DiskGeometry_T)) ? -EFAULT : 0); - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKFLSBUF: - case BLKBSZGET: - case BLKBSZSET: - return blk_ioctl(Inode->i_bdev, Request, Argument); case BLKRRPART: /* Re-Read Partition Table. */ diff --git a/drivers/block/acsi.c b/drivers/block/acsi.c index c35ef7f77e40..f616f01847b7 100644 --- a/drivers/block/acsi.c +++ b/drivers/block/acsi.c @@ -248,8 +248,6 @@ static int NDevices = 0; static int acsi_sizes[MAX_DEV<<4] = { 0, }; static struct hd_struct acsi_part[MAX_DEV<<4] = { {0,0}, }; static int access_count[MAX_DEV] = { 0, }; -static char busy[MAX_DEV] = { 0, }; -static DECLARE_WAIT_QUEUE_HEAD(busy_wait); static int CurrentNReq; static int CurrentNSect; @@ -1107,7 +1105,7 @@ static int acsi_ioctl( struct inode *inode, struct file *file, put_user( 64, &geo->heads ); put_user( 32, &geo->sectors ); put_user( acsi_info[dev].size >> 11, &geo->cylinders ); - put_user(get_start_sect(inode->i_rdev), &geo->start); + put_user(get_start_sect(inode->i_bdev), &geo->start); return 0; } @@ -1118,14 +1116,6 @@ static int acsi_ioctl( struct inode *inode, struct file *file, put_user( 0, &((Scsi_Idlun *) arg)->host_unique_id ); return 0; - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKROSET: - case BLKROGET: - case BLKFLSBUF: - case BLKPG: - return blk_ioctl(inode->i_bdev, cmd, arg); - case BLKRRPART: /* Re-read partition tables */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -1161,8 +1151,6 @@ static int acsi_open( struct inode * inode, struct file * filp ) if (device >= NDevices) return -ENXIO; aip = &acsi_info[device]; - while (busy[device]) - sleep_on(&busy_wait); if (access_count[device] == 0 && aip->removable) { #if 0 @@ -1803,10 +1791,6 @@ void cleanup_module(void) } #endif -#define DEVICE_BUSY busy[device] -#define USAGE access_count[device] -#define GENDISK_STRUCT acsi_gendisk - /* * This routine is called to flush all partitions and partition tables * for a changed scsi disk, and then re-read the new partition table. @@ -1828,24 +1812,15 @@ void cleanup_module(void) static int revalidate_acsidisk( int dev, int maxusage ) { - int device; - struct gendisk * gdev; - int res; - struct acsi_info_struct *aip; - - device = DEVICE_NR(minor(dev)); - aip = &acsi_info[device]; - gdev = &GENDISK_STRUCT; + int unit = DEVICE_NR(minor(dev)); + struct acsi_info_struct *aip = &acsi_info[unit]; + kdev_t device = mk_kdev(MAJOR_NR, unit<<4); + int res = dev_lock_part(device); - cli(); - if (DEVICE_BUSY || USAGE > maxusage) { - sti(); - return -EBUSY; - }; - DEVICE_BUSY = 1; - sti(); + if (res < 0) + return res; - res = wipe_partitions(dev); + res = wipe_partitions(device); stdma_lock( NULL, NULL ); @@ -1862,10 +1837,9 @@ static int revalidate_acsidisk( int dev, int maxusage ) stdma_release(); if (!res) - grok_partitions(dev, aip->size); + grok_partitions(device, aip->size); - DEVICE_BUSY = 0; - wake_up(&busy_wait); + dev_unlock_part(device); return res; } diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index af8f966f4eb3..04a8ce00c6a7 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -1559,12 +1559,6 @@ static int fd_ioctl(struct inode *inode, struct file *filp, struct floppy_struct setprm; device = inode->i_rdev; - switch (cmd) { - case BLKROSET: - case BLKROGET: - case BLKFLSBUF: - return blk_ioctl(inode->i_bdev, cmd, param); - } drive = minor (device); type = drive >> 2; drive &= 3; diff --git a/drivers/block/blkpg.c b/drivers/block/blkpg.c index 37e244777963..83826af84b6a 100644 --- a/drivers/block/blkpg.c +++ b/drivers/block/blkpg.c @@ -69,8 +69,9 @@ int add_partition(struct block_device *bdev, struct blkpg_partition *p) struct gendisk *g; long long ppstart, pplength; long pstart, plength; - int i, drive, first_minor, end_minor, minor; + int i; kdev_t dev = to_kdev_t(bdev->bd_dev); + struct hd_struct *part; /* convert bytes to sectors, check for fit in a hd_struct */ ppstart = (p->start >> 9); @@ -85,37 +86,32 @@ int add_partition(struct block_device *bdev, struct blkpg_partition *p) g = get_gendisk(dev); if (!g) return -ENXIO; + part = g->part + minor(dev); /* existing drive? */ - drive = (minor(dev) >> g->minor_shift); - first_minor = (drive << g->minor_shift); - end_minor = first_minor + (1 << g->minor_shift); - if (drive >= g->nr_real) - return -ENXIO; /* drive and partition number OK? */ - if (first_minor != minor(dev)) + if (bdev != bdev->bd_contains) return -EINVAL; if (p->pno <= 0 || p->pno >= (1 << g->minor_shift)) return -EINVAL; /* partition number in use? */ - minor = first_minor + p->pno; - if (g->part[minor].nr_sects != 0) + if (part[p->pno].nr_sects != 0) return -EBUSY; /* overlap? */ - for (i=first_minor+1; i<end_minor; i++) - if (!(pstart+plength <= g->part[i].start_sect || - pstart >= g->part[i].start_sect + g->part[i].nr_sects)) + for (i = 1; i < (1<<g->minor_shift); i++) + if (!(pstart+plength <= part[i].start_sect || + pstart >= part[i].start_sect + part[i].nr_sects)) return -EBUSY; /* all seems OK */ - g->part[minor].start_sect = pstart; - g->part[minor].nr_sects = plength; + part[p->pno].start_sect = pstart; + part[p->pno].nr_sects = plength; if (g->sizes) - g->sizes[minor] = (plength >> (BLOCK_SIZE_BITS - 9)); - devfs_register_partitions (g, first_minor, 0); + g->sizes[minor(dev)+p->pno] = (plength >> (BLOCK_SIZE_BITS-9)); + devfs_register_partitions (g, minor(dev), 0); return 0; } @@ -133,33 +129,27 @@ int del_partition(struct block_device *bdev, struct blkpg_partition *p) { kdev_t dev = to_kdev_t(bdev->bd_dev); struct gendisk *g; - kdev_t devp; struct block_device *bdevp; - int drive, first_minor, minor; + struct hd_struct *part; int holder; /* find the drive major */ g = get_gendisk(dev); if (!g) return -ENXIO; + part = g->part + minor(dev); - /* drive and partition number OK? */ - drive = (minor(dev) >> g->minor_shift); - first_minor = (drive << g->minor_shift); - - if (first_minor != minor(dev)) + if (bdev != bdev->bd_contains) return -EINVAL; if (p->pno <= 0 || p->pno >= (1 << g->minor_shift)) return -EINVAL; /* existing drive and partition? */ - minor = first_minor + p->pno; - if (drive >= g->nr_real || g->part[minor].nr_sects == 0) + if (part[p->pno].nr_sects == 0) return -ENXIO; /* partition in use? Incomplete check for now. */ - devp = mk_kdev(major(dev), minor); - bdevp = bdget(kdev_t_to_nr(devp)); + bdevp = bdget(MKDEV(major(dev), minor(dev) + p->pno)); if (!bdevp) return -ENOMEM; if (bd_claim(bdevp, &holder) < 0) { @@ -171,11 +161,11 @@ int del_partition(struct block_device *bdev, struct blkpg_partition *p) fsync_bdev(bdevp); invalidate_bdev(bdevp, 0); - g->part[minor].start_sect = 0; - g->part[minor].nr_sects = 0; + part[p->pno].start_sect = 0; + part[p->pno].nr_sects = 0; if (g->sizes) - g->sizes[minor] = 0; - devfs_register_partitions (g, first_minor, 0); + g->sizes[minor(dev) + p->pno] = 0; + devfs_register_partitions (g, minor(dev), 0); bd_release(bdevp); bdput(bdevp); @@ -223,10 +213,6 @@ int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg) int holder; struct backing_dev_info *bdi; - intval = block_ioctl(bdev, cmd, arg); - if (intval != -ENOTTY) - return intval; - switch (cmd) { case BLKROSET: if (!capable(CAP_SYS_ADMIN)) @@ -296,14 +282,6 @@ int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg) case BLKPG: return blkpg_ioctl(bdev, (struct blkpg_ioctl_arg *) arg); - - /* - * deprecated, use the /proc/iosched interface instead - */ - case BLKELVGET: - case BLKELVSET: - return -ENOTTY; - case BLKBSZGET: /* get the logical block size (cf. BLKSSZGET) */ intval = block_size(bdev); @@ -330,5 +308,3 @@ int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg) return -EINVAL; } } - -EXPORT_SYMBOL(blk_ioctl); diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index e06fd274b653..939d70269415 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -440,15 +440,6 @@ static int cciss_ioctl(struct inode *inode, struct file *filep, case BLKRRPART: return revalidate_logvol(inode->i_rdev, 1); - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKFLSBUF: - case BLKBSZSET: - case BLKBSZGET: - case BLKROSET: - case BLKROGET: - case BLKPG: - return blk_ioctl(inode->i_bdev, cmd, arg); case CCISS_GETPCIINFO: { cciss_pci_info_struct pciinfo; @@ -735,6 +726,10 @@ static int cciss_ioctl(struct inode *inode, struct file *filep, } /* Borrowed and adapted from sd.c */ +/* + * FIXME: we are missing the exclusion with ->open() here - it can happen + * just as we are rereading partition tables. + */ static int revalidate_logvol(kdev_t dev, int maxusage) { int ctlr, target; @@ -860,18 +855,9 @@ static int deregister_disk(int ctlr, int logvol) /* invalidate the devices and deregister the disk */ max_p = 1 << gdev->minor_shift; start = logvol << gdev->minor_shift; + wipe_partitions(mk_kdev(MAJOR_NR+ctlr, start)); for (i=max_p-1; i>=0; i--) - { - int minor = start+i; - kdev_t kdev = mk_kdev(MAJOR_NR+ctlr, minor); - // printk("invalidating( %d %d)\n", ctlr, minor); - invalidate_device(kdev, 1); - /* so open will now fail */ - h->sizes[minor] = 0; - /* so it will no longer appear in /proc/partitions */ - gdev->part[minor].start_sect = 0; - gdev->part[minor].nr_sects = 0; - } + h->sizes[start + i] = 0; /* check to see if it was the last disk */ if (logvol == h->highest_lun) { @@ -1313,19 +1299,12 @@ static int register_new_disk(kdev_t dev, int ctlr) hba[ctlr]->drv[logvol].usage_count = 0; max_p = 1 << gdev->minor_shift; start = logvol<< gdev->minor_shift; + kdev = mk_kdev(MAJOR_NR + ctlr, logvol<< gdev->minor_shift); - for(i=max_p-1; i>=0; i--) { - int minor = start+i; - kdev = mk_kdev(MAJOR_NR + ctlr, minor); - invalidate_device(kdev, 1); - gdev->part[minor].start_sect = 0; - gdev->part[minor].nr_sects = 0; - } - + wipe_partitions(kdev); ++hba[ctlr]->num_luns; gdev->nr_real = hba[ctlr]->highest_lun + 1; /* setup partitions per disk */ - kdev = mk_kdev(MAJOR_NR + ctlr, logvol<< gdev->minor_shift); grok_partitions(kdev, hba[ctlr]->drv[logvol].nr_blocks); kfree(ld_buff); diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index fccef1bb792c..31276ef0e87c 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -1116,7 +1116,7 @@ static int ida_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, put_user(diskinfo[0], &geo->heads); put_user(diskinfo[1], &geo->sectors); put_user(diskinfo[2], &geo->cylinders); - put_user(get_start_sect(inode->i_rdev), &geo->start); + put_user(get_start_sect(inode->i_bdev), &geo->start); return 0; case IDAGETDRVINFO: if (copy_to_user(&io->c.drv, &hba[ctlr]->drv[dsk], @@ -1157,16 +1157,6 @@ static int ida_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, return(0); } - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKFLSBUF: - case BLKBSZSET: - case BLKBSZGET: - case BLKROSET: - case BLKROGET: - case BLKPG: - return blk_ioctl(inode->i_bdev, cmd, arg); - default: return -EINVAL; } @@ -1533,6 +1523,9 @@ static int revalidate_allvol(kdev_t dev) } /* Borrowed and adapted from sd.c */ +/* + * FIXME: exclusion with ->open() + */ static int revalidate_logvol(kdev_t dev, int maxusage) { int ctlr, target; diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index aff8acff0ef3..fdc6e904464a 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -3468,12 +3468,6 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, const char *outparam; /* parameters passed back to user space */ device = inode->i_rdev; - switch (cmd) { - case BLKROSET: - case BLKROGET: - case BLKFLSBUF: - return blk_ioctl(inode->i_bdev, cmd, param); - } type = TYPE(device); drive = DRIVE(device); diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 7c30fbe37c16..0fc011f610d1 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -117,31 +117,6 @@ get_gendisk(kdev_t dev) EXPORT_SYMBOL(get_gendisk); - -unsigned long -get_start_sect(kdev_t dev) -{ - struct gendisk *gp; - - gp = get_gendisk(dev); - if (gp) - return gp->part[minor(dev)].start_sect; - return 0; -} - -EXPORT_SYMBOL(get_start_sect); - -unsigned long -get_nr_sects(kdev_t dev) -{ - struct gendisk *gp; - - gp = get_gendisk(dev); - if (gp) - return gp->part[minor(dev)].nr_sects; - return 0; -} - #ifdef CONFIG_PROC_FS /* iterator */ static void *part_start(struct seq_file *part, loff_t *pos) @@ -209,7 +184,6 @@ int __init device_init(void) { rwlock_init(&gendisk_lock); blk_dev_init(); - sti(); #ifdef CONFIG_I2O i2o_init(); #endif diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 48616f94f5ee..c23f57a0ed16 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -1573,16 +1573,10 @@ end_io: static inline void blk_partition_remap(struct bio *bio) { struct block_device *bdev = bio->bi_bdev; - struct gendisk *g; - if (bdev == bdev->bd_contains) return; - g = get_gendisk(to_kdev_t(bdev->bd_dev)); - if (!g) - BUG(); - - bio->bi_sector += g->part[minor(to_kdev_t((bdev->bd_dev)))].start_sect; + bio->bi_sector += bdev->bd_offset; bio->bi_bdev = bdev->bd_contains; /* lots of checks are possible */ } @@ -2047,9 +2041,6 @@ int __init blk_dev_init(void) #if defined(CONFIG_IDE) && defined(CONFIG_BLK_DEV_HD) hd_init(); #endif -#if defined(__i386__) /* Do we even need this? */ - outb_p(0xc, 0x3f2); -#endif return 0; }; diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 982604ff6bfd..50c1052cae74 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -912,10 +912,6 @@ static int lo_ioctl(struct inode * inode, struct file * file, } err = put_user((u64)loop_sizes[lo->lo_number] << 10, (u64*)arg); break; - case BLKBSZGET: - case BLKBSZSET: - err = blk_ioctl(inode->i_bdev, cmd, arg); - break; default: err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL; } diff --git a/drivers/block/paride/Makefile b/drivers/block/paride/Makefile index d62c39cb9b55..b1294e64f414 100644 --- a/drivers/block/paride/Makefile +++ b/drivers/block/paride/Makefile @@ -5,14 +5,9 @@ # Rewritten to use lists instead of if-statements. # -export-objs := paride.o bpck6.o +export-objs := paride.o obj-$(CONFIG_PARIDE) += paride.o -obj-$(CONFIG_PARIDE_PD) += pd.o -obj-$(CONFIG_PARIDE_PCD) += pcd.o -obj-$(CONFIG_PARIDE_PF) += pf.o -obj-$(CONFIG_PARIDE_PT) += pt.o -obj-$(CONFIG_PARIDE_PG) += pg.o obj-$(CONFIG_PARIDE_ATEN) += aten.o obj-$(CONFIG_PARIDE_BPCK) += bpck.o obj-$(CONFIG_PARIDE_COMM) += comm.o @@ -20,13 +15,18 @@ obj-$(CONFIG_PARIDE_DSTR) += dstr.o obj-$(CONFIG_PARIDE_KBIC) += kbic.o obj-$(CONFIG_PARIDE_EPAT) += epat.o obj-$(CONFIG_PARIDE_EPIA) += epia.o -obj-$(CONFIG_PARIDE_FIT2) += fit2.o -obj-$(CONFIG_PARIDE_FIT3) += fit3.o obj-$(CONFIG_PARIDE_FRPW) += frpw.o obj-$(CONFIG_PARIDE_FRIQ) += friq.o +obj-$(CONFIG_PARIDE_FIT2) += fit2.o +obj-$(CONFIG_PARIDE_FIT3) += fit3.o obj-$(CONFIG_PARIDE_ON20) += on20.o obj-$(CONFIG_PARIDE_ON26) += on26.o obj-$(CONFIG_PARIDE_KTTI) += ktti.o obj-$(CONFIG_PARIDE_BPCK6) += bpck6.o +obj-$(CONFIG_PARIDE_PD) += pd.o +obj-$(CONFIG_PARIDE_PCD) += pcd.o +obj-$(CONFIG_PARIDE_PF) += pf.o +obj-$(CONFIG_PARIDE_PT) += pt.o +obj-$(CONFIG_PARIDE_PG) += pg.o include $(TOPDIR)/Rules.make diff --git a/drivers/block/paride/aten.c b/drivers/block/paride/aten.c index b22d34da348a..763fc50d7f76 100644 --- a/drivers/block/paride/aten.c +++ b/drivers/block/paride/aten.c @@ -18,6 +18,7 @@ #define ATEN_VERSION "1.01" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/wait.h> @@ -140,35 +141,33 @@ static void aten_release_proto( PIA *pi ) { MOD_DEC_USE_COUNT; } -struct pi_protocol aten = {"aten",0,2,2,1,1, - aten_write_regr, - aten_read_regr, - aten_write_block, - aten_read_block, - aten_connect, - aten_disconnect, - 0, - 0, - 0, - aten_log_adapter, - aten_init_proto, - aten_release_proto - }; - - -#ifdef MODULE - -int init_module(void) - -{ return pi_register( &aten ) - 1; +static struct pi_protocol aten = { + .name = "aten", + .max_mode = 2, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = aten_write_regr, + .read_regr = aten_read_regr, + .write_block = aten_write_block, + .read_block = aten_read_block, + .connect = aten_connect, + .disconnect = aten_disconnect, + .log_adapter = aten_log_adapter, + .init_proto = aten_init_proto, + .release_proto = aten_release_proto, +}; + +static int __init aten_init(void) +{ + return pi_register(&aten)-1; } -void cleanup_module(void) - -{ pi_unregister( &aten ); +static void __exit aten_exit(void) +{ + pi_unregister( &aten ); } -#endif - -/* end of aten.c */ MODULE_LICENSE("GPL"); +module_init(aten_init) +module_exit(aten_exit) diff --git a/drivers/block/paride/bpck.c b/drivers/block/paride/bpck.c index 3e20e6b7054a..b15ff97fe7af 100644 --- a/drivers/block/paride/bpck.c +++ b/drivers/block/paride/bpck.c @@ -17,6 +17,7 @@ #define BPCK_VERSION "1.02" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/types.h> @@ -452,34 +453,36 @@ static void bpck_release_proto( PIA *pi) { MOD_DEC_USE_COUNT; } -struct pi_protocol bpck = { "bpck",0,5,2,4,256, - bpck_write_regr, - bpck_read_regr, - bpck_write_block, - bpck_read_block, - bpck_connect, - bpck_disconnect, - bpck_test_port, - bpck_probe_unit, - bpck_test_proto, - bpck_log_adapter, - bpck_init_proto, - bpck_release_proto - }; - -#ifdef MODULE - -int init_module(void) - -{ return pi_register(&bpck) - 1; +static struct pi_protocol bpck = { + .name = "bpck", + .max_mode = 5, + .epp_first = 2, + .default_delay = 4, + .max_units = 255, + .write_regr = bpck_write_regr, + .read_regr = bpck_read_regr, + .write_block = bpck_write_block, + .read_block = bpck_read_block, + .connect = bpck_connect, + .disconnect = bpck_disconnect, + .test_port = bpck_test_port, + .probe_unit = bpck_probe_unit, + .test_proto = bpck_test_proto, + .log_adapter = bpck_log_adapter, + .init_proto = bpck_init_proto, + .release_proto = bpck_release_proto, +}; + +static int __init bpck_init(void) +{ + return pi_register(&bpck)-1; } -void cleanup_module(void) - -{ pi_unregister(&bpck); +static void __exit bpck_exit(void) +{ + pi_unregister(&bpck); } -#endif - -/* end of bpck.c */ MODULE_LICENSE("GPL"); +module_init(bpck_init) +module_exit(bpck_exit) diff --git a/drivers/block/paride/bpck6.c b/drivers/block/paride/bpck6.c index f2febfcfd268..c1fcdd9f4e94 100644 --- a/drivers/block/paride/bpck6.c +++ b/drivers/block/paride/bpck6.c @@ -26,6 +26,7 @@ int verbose=0; /* set this to 1 to see debugging messages and whatnot */ #define BACKPACK_VERSION "2.0.2" #include <linux/module.h> +#include <linux/init.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/types.h> @@ -252,65 +253,41 @@ static void bpck6_release_proto(PIA *pi) kfree((void *)(pi->private)); } -struct pi_protocol bpck6 = { "bpck6", /* name for proto*/ - 0, /* index into proto table */ - 5, /* max mode =5 */ - 2, /* 2-5 use epp (need 8 ports) */ - 0, /* no delay (not used anyway) */ - 255, /* we can have units up to 255 */ - bpck6_write_regr, - bpck6_read_regr, - bpck6_write_block, - bpck6_read_block, - bpck6_connect, - bpck6_disconnect, - bpck6_test_port, - bpck6_probe_unit, - 0, - bpck6_log_adapter, - bpck6_init_proto, - bpck6_release_proto - }; - - -EXPORT_SYMBOL(bpck6_write_regr); -EXPORT_SYMBOL(bpck6_read_regr); -EXPORT_SYMBOL(bpck6_write_block); -EXPORT_SYMBOL(bpck6_read_block); -EXPORT_SYMBOL(bpck6_connect); -EXPORT_SYMBOL(bpck6_disconnect); -EXPORT_SYMBOL(bpck6_test_port); -EXPORT_SYMBOL(bpck6_probe_unit); -EXPORT_SYMBOL(bpck6_log_adapter); -EXPORT_SYMBOL(bpck6_init_proto); -EXPORT_SYMBOL(bpck6_release_proto); - -/*---------------------------MODULE STUFF-----------------------*/ - -#ifdef MODULE -/*module information*/ - -static int init_module(void) +static struct pi_protocol bpck6 = { + .name = "bpck6", + .max_mode = 5, + .epp_first = 2, /* 2-5 use epp (need 8 ports) */ + .max_units = 255, + .write_regr = bpck6_write_regr, + .read_regr = bpck6_read_regr, + .write_block = bpck6_write_block, + .read_block = bpck6_read_block, + .connect = bpck6_connect, + .disconnect = bpck6_disconnect, + .test_port = bpck6_test_port, + .probe_unit = bpck6_probe_unit, + .log_adapter = bpck6_log_adapter, + .init_proto = bpck6_init_proto, + .release_proto = bpck6_release_proto, +}; + +static int __init bpck6_init(void) { printk(KERN_INFO "bpck6: BACKPACK Protocol Driver V"BACKPACK_VERSION"\n"); printk(KERN_INFO "bpck6: Copyright 2001 by Micro Solutions, Inc., DeKalb IL. USA\n"); - if(verbose) - { printk(KERN_DEBUG "bpck6: verbose debug enabled.\n"); - } - return pi_register(&bpck6) - 1; } -void cleanup_module(void) +static void __exit bpck6_exit(void) { pi_unregister(&bpck6); } +MODULE_LICENSE("GPL"); MODULE_AUTHOR("Micro Solutions Inc."); MODULE_DESCRIPTION("BACKPACK Protocol module, compatible with PARIDE"); MODULE_PARM(verbose,"i"); - -#endif - +module_init(bpck6_init) +module_exit(bpck6_exit) diff --git a/drivers/block/paride/comm.c b/drivers/block/paride/comm.c index 058a7d68572c..f3010d6fc73b 100644 --- a/drivers/block/paride/comm.c +++ b/drivers/block/paride/comm.c @@ -17,6 +17,7 @@ #define COMM_VERSION "1.01" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/types.h> @@ -196,35 +197,33 @@ static void comm_release_proto(PIA *pi) { MOD_DEC_USE_COUNT; } -struct pi_protocol comm = {"comm",0,5,2,1,1, - comm_write_regr, - comm_read_regr, - comm_write_block, - comm_read_block, - comm_connect, - comm_disconnect, - 0, - 0, - 0, - comm_log_adapter, - comm_init_proto, - comm_release_proto - }; - - -#ifdef MODULE - -int init_module(void) - -{ return pi_register( &comm ) - 1; +static struct pi_protocol comm = { + .name = "comm", + .max_mode = 5, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = comm_write_regr, + .read_regr = comm_read_regr, + .write_block = comm_write_block, + .read_block = comm_read_block, + .connect = comm_connect, + .disconnect = comm_disconnect, + .log_adapter = comm_log_adapter, + .init_proto = comm_init_proto, + .release_proto = comm_release_proto, +}; + +static int __init comm_init(void) +{ + return pi_register(&comm)-1; } -void cleanup_module(void) - -{ pi_unregister( &comm ); +static void __exit comm_exit(void) +{ + pi_unregister(&comm); } -#endif - -/* end of comm.c */ MODULE_LICENSE("GPL"); +module_init(comm_init) +module_exit(comm_exit) diff --git a/drivers/block/paride/dstr.c b/drivers/block/paride/dstr.c index 438735b6ce10..204fe75df233 100644 --- a/drivers/block/paride/dstr.c +++ b/drivers/block/paride/dstr.c @@ -16,6 +16,7 @@ #define DSTR_VERSION "1.01" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/types.h> @@ -211,35 +212,33 @@ static void dstr_release_proto( PIA *pi) { MOD_DEC_USE_COUNT; } -struct pi_protocol dstr = {"dstr",0,5,2,1,1, - dstr_write_regr, - dstr_read_regr, - dstr_write_block, - dstr_read_block, - dstr_connect, - dstr_disconnect, - 0, - 0, - 0, - dstr_log_adapter, - dstr_init_proto, - dstr_release_proto - }; - - -#ifdef MODULE - -int init_module(void) - -{ return pi_register( &dstr ) - 1; +static struct pi_protocol dstr = { + .name = "dstr", + .max_mode = 5, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = dstr_write_regr, + .read_regr = dstr_read_regr, + .write_block = dstr_write_block, + .read_block = dstr_read_block, + .connect = dstr_connect, + .disconnect = dstr_disconnect, + .log_adapter = dstr_log_adapter, + .init_proto = dstr_init_proto, + .release_proto = dstr_release_proto, +}; + +static int __init dstr_init(void) +{ + return pi_register(&dstr)-1; } -void cleanup_module(void) - -{ pi_unregister( &dstr ); +static void __exit dstr_exit(void) +{ + pi_unregister(&dstr); } -#endif - -/* end of dstr.c */ MODULE_LICENSE("GPL"); +module_init(dstr_init) +module_exit(dstr_exit) diff --git a/drivers/block/paride/epat.c b/drivers/block/paride/epat.c index 624a5ba32cfd..0a7126756371 100644 --- a/drivers/block/paride/epat.c +++ b/drivers/block/paride/epat.c @@ -19,6 +19,7 @@ #define EPAT_VERSION "1.02" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/types.h> @@ -311,35 +312,34 @@ static void epat_release_proto( PIA *pi) { MOD_DEC_USE_COUNT; } -struct pi_protocol epat = {"epat",0,6,3,1,1, - epat_write_regr, - epat_read_regr, - epat_write_block, - epat_read_block, - epat_connect, - epat_disconnect, - 0, - 0, - epat_test_proto, - epat_log_adapter, - epat_init_proto, - epat_release_proto - }; - - -#ifdef MODULE - -int init_module(void) - -{ return pi_register( &epat) - 1; +static struct pi_protocol epat = { + .name = "epat", + .max_mode = 6, + .epp_first = 3, + .default_delay = 1, + .max_units = 1, + .write_regr = epat_write_regr, + .read_regr = epat_read_regr, + .write_block = epat_write_block, + .read_block = epat_read_block, + .connect = epat_connect, + .disconnect = epat_disconnect, + .test_proto = epat_test_proto, + .log_adapter = epat_log_adapter, + .init_proto = epat_init_proto, + .release_proto = epat_release_proto, +}; + +static int __init epat_init(void) +{ + return pi_register(&epat)-1; } -void cleanup_module(void) - -{ pi_unregister( &epat); +static void __exit epat_exit(void) +{ + pi_unregister(&epat); } -#endif - -/* end of epat.c */ MODULE_LICENSE("GPL"); +module_init(epat_init) +module_exit(epat_exit) diff --git a/drivers/block/paride/epia.c b/drivers/block/paride/epia.c index c27edd64ecc1..c2728af843dd 100644 --- a/drivers/block/paride/epia.c +++ b/drivers/block/paride/epia.c @@ -20,6 +20,7 @@ #define EPIA_VERSION "1.02" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/types.h> @@ -293,36 +294,34 @@ static void epia_release_proto( PIA *pi) { MOD_DEC_USE_COUNT; } -struct pi_protocol epia = {"epia",0,6,3,1,1, - epia_write_regr, - epia_read_regr, - epia_write_block, - epia_read_block, - epia_connect, - epia_disconnect, - 0, - 0, - epia_test_proto, - epia_log_adapter, - epia_init_proto, - epia_release_proto - }; - - -#ifdef MODULE - -int init_module(void) - -{ return pi_register( &epia ) - 1; +static struct pi_protocol epia = { + .name = "epia", + .max_mode = 6, + .epp_first = 3, + .default_delay = 1, + .max_units = 1, + .write_regr = epia_write_regr, + .read_regr = epia_read_regr, + .write_block = epia_write_block, + .read_block = epia_read_block, + .connect = epia_connect, + .disconnect = epia_disconnect, + .test_proto = epia_test_proto, + .log_adapter = epia_log_adapter, + .init_proto = epia_init_proto, + .release_proto = epia_release_proto, +}; + +static int __init epia_init(void) +{ + return pi_register(&epia)-1; } -void cleanup_module(void) - -{ pi_unregister( &epia ); +static void __exit epia_exit(void) +{ + pi_unregister(&epia); } -#endif - -/* end of epia.c */ - MODULE_LICENSE("GPL"); +module_init(epia_init) +module_exit(epia_exit) diff --git a/drivers/block/paride/fit2.c b/drivers/block/paride/fit2.c index 7f45529c80b1..42b2880a82ac 100644 --- a/drivers/block/paride/fit2.c +++ b/drivers/block/paride/fit2.c @@ -16,6 +16,7 @@ #define FIT2_VERSION "1.0" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/types.h> @@ -129,35 +130,33 @@ static void fit2_release_proto( PIA *pi) { MOD_DEC_USE_COUNT; } -struct pi_protocol fit2 = {"fit2",0,1,2,1,1, - fit2_write_regr, - fit2_read_regr, - fit2_write_block, - fit2_read_block, - fit2_connect, - fit2_disconnect, - 0, - 0, - 0, - fit2_log_adapter, - fit2_init_proto, - fit2_release_proto - }; - - -#ifdef MODULE - -int init_module(void) - -{ return pi_register( &fit2 ) - 1; +static struct pi_protocol fit2 = { + .name = "fit2", + .max_mode = 1, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = fit2_write_regr, + .read_regr = fit2_read_regr, + .write_block = fit2_write_block, + .read_block = fit2_read_block, + .connect = fit2_connect, + .disconnect = fit2_disconnect, + .log_adapter = fit2_log_adapter, + .init_proto = fit2_init_proto, + .release_proto = fit2_release_proto, +}; + +static int __init fit2_init(void) +{ + return pi_register(&fit2)-1; } -void cleanup_module(void) - -{ pi_unregister( &fit2 ); +static void __exit fit2_exit(void) +{ + pi_unregister(&fit2); } -#endif - -/* end of fit2.c */ MODULE_LICENSE("GPL"); +module_init(fit2_init) +module_exit(fit2_exit) diff --git a/drivers/block/paride/fit3.c b/drivers/block/paride/fit3.c index 8b5ad4bd5ad0..4d9286562c3c 100644 --- a/drivers/block/paride/fit3.c +++ b/drivers/block/paride/fit3.c @@ -20,6 +20,7 @@ #define FIT3_VERSION "1.0" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/types.h> @@ -189,35 +190,33 @@ static void fit3_release_proto(PIA *pi) { MOD_DEC_USE_COUNT; } -struct pi_protocol fit3 = {"fit3",0,3,2,1,1, - fit3_write_regr, - fit3_read_regr, - fit3_write_block, - fit3_read_block, - fit3_connect, - fit3_disconnect, - 0, - 0, - 0, - fit3_log_adapter, - fit3_init_proto, - fit3_release_proto - }; - - -#ifdef MODULE - -int init_module(void) - -{ return pi_register( &fit3 ) - 1; +static struct pi_protocol fit3 = { + .name = "fit3", + .max_mode = 3, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = fit3_write_regr, + .read_regr = fit3_read_regr, + .write_block = fit3_write_block, + .read_block = fit3_read_block, + .connect = fit3_connect, + .disconnect = fit3_disconnect, + .log_adapter = fit3_log_adapter, + .init_proto = fit3_init_proto, + .release_proto = fit3_release_proto, +}; + +static int __init fit3_init(void) +{ + return pi_register(&fit3)-1; } -void cleanup_module(void) - -{ pi_unregister( &fit3 ); +static void __exit fit3_exit(void) +{ + pi_unregister(&fit3); } -#endif - -/* end of fit3.c */ MODULE_LICENSE("GPL"); +module_init(fit3_init) +module_exit(fit3_exit) diff --git a/drivers/block/paride/friq.c b/drivers/block/paride/friq.c index 74b2eaab1c19..10b994a456da 100644 --- a/drivers/block/paride/friq.c +++ b/drivers/block/paride/friq.c @@ -28,6 +28,7 @@ #define FRIQ_VERSION "1.01" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/types.h> @@ -250,35 +251,34 @@ static void friq_release_proto( PIA *pi) MOD_DEC_USE_COUNT; } -struct pi_protocol friq = {"friq",0,5,2,1,1, - friq_write_regr, - friq_read_regr, - friq_write_block, - friq_read_block, - friq_connect, - friq_disconnect, - 0, - 0, - friq_test_proto, - friq_log_adapter, - friq_init_proto, - friq_release_proto - }; - - -#ifdef MODULE - -int init_module(void) - -{ return pi_register( &friq ) - 1; +static struct pi_protocol friq = { + .name = "friq", + .max_mode = 5, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = friq_write_regr, + .read_regr = friq_read_regr, + .write_block = friq_write_block, + .read_block = friq_read_block, + .connect = friq_connect, + .disconnect = friq_disconnect, + .test_proto = friq_test_proto, + .log_adapter = friq_log_adapter, + .init_proto = friq_init_proto, + .release_proto = friq_release_proto, +}; + +static int __init friq_init(void) +{ + return pi_register(&friq)-1; } -void cleanup_module(void) - -{ pi_unregister( &friq ); +static void __exit friq_exit(void) +{ + pi_unregister(&friq); } -#endif - -/* end of friq.c */ MODULE_LICENSE("GPL"); +module_init(friq_init) +module_exit(friq_exit) diff --git a/drivers/block/paride/frpw.c b/drivers/block/paride/frpw.c index 817004a3f7dc..ee0cad3807a1 100644 --- a/drivers/block/paride/frpw.c +++ b/drivers/block/paride/frpw.c @@ -26,6 +26,7 @@ #define FRPW_VERSION "1.03" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/types.h> @@ -291,35 +292,34 @@ static void frpw_release_proto( PIA *pi) { MOD_DEC_USE_COUNT; } -struct pi_protocol frpw = {"frpw",0,6,2,2,1, - frpw_write_regr, - frpw_read_regr, - frpw_write_block, - frpw_read_block, - frpw_connect, - frpw_disconnect, - 0, - 0, - frpw_test_proto, - frpw_log_adapter, - frpw_init_proto, - frpw_release_proto - }; - - -#ifdef MODULE - -int init_module(void) - -{ return pi_register( &frpw ) - 1; +static struct pi_protocol frpw = { + .name = "frpw", + .max_mode = 6, + .epp_first = 2, + .default_delay = 2, + .max_units = 1, + .write_regr = frpw_write_regr, + .read_regr = frpw_read_regr, + .write_block = frpw_write_block, + .read_block = frpw_read_block, + .connect = frpw_connect, + .disconnect = frpw_disconnect, + .test_proto = frpw_test_proto, + .log_adapter = frpw_log_adapter, + .init_proto = frpw_init_proto, + .release_proto = frpw_release_proto, +}; + +static int __init frpw_init(void) +{ + return pi_register(&frpw)-1; } -void cleanup_module(void) - -{ pi_unregister( &frpw ); +static void __exit frpw_exit(void) +{ + pi_unregister(&frpw); } -#endif - -/* end of frpw.c */ MODULE_LICENSE("GPL"); +module_init(frpw_init) +module_exit(frpw_exit) diff --git a/drivers/block/paride/kbic.c b/drivers/block/paride/kbic.c index 2f20c9b0fa4e..f97233b39b7b 100644 --- a/drivers/block/paride/kbic.c +++ b/drivers/block/paride/kbic.c @@ -21,6 +21,7 @@ #define KBIC_VERSION "1.01" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/types.h> @@ -258,56 +259,51 @@ static void kbic_release_proto( PIA *pi) { MOD_DEC_USE_COUNT; } -struct pi_protocol k951 = {"k951",0,6,3,1,1, - kbic_write_regr, - kbic_read_regr, - kbic_write_block, - kbic_read_block, - k951_connect, - k951_disconnect, - 0, - 0, - 0, - k951_log_adapter, - kbic_init_proto, - kbic_release_proto - }; - - -struct pi_protocol k971 = {"k971",0,6,3,1,1, - kbic_write_regr, - kbic_read_regr, - kbic_write_block, - kbic_read_block, - k971_connect, - k971_disconnect, - 0, - 0, - 0, - k971_log_adapter, - kbic_init_proto, - kbic_release_proto - }; - -#ifdef MODULE - -int init_module(void) - -{ int s5,s7; - - s5 = pi_register(&k951); - s7 = pi_register(&k971); - - return (s5 || s7) - 1; +static struct pi_protocol k951 = { + .name = "k951", + .max_mode = 6, + .epp_first = 3, + .default_delay = 1, + .max_units = 1, + .write_regr = kbic_write_regr, + .read_regr = kbic_read_regr, + .write_block = kbic_write_block, + .read_block = kbic_read_block, + .connect = k951_connect, + .disconnect = k951_disconnect, + .log_adapter = k951_log_adapter, + .init_proto = kbic_init_proto, + .release_proto = kbic_release_proto +}; + +static struct pi_protocol k971 = { + .name = "k971", + .max_mode = 6, + .epp_first = 3, + .default_delay = 1, + .max_units = 1, + .write_regr = kbic_write_regr, + .read_regr = kbic_read_regr, + .write_block = kbic_write_block, + .read_block = kbic_read_block, + .connect = k971_connect, + .disconnect = k971_disconnect, + .log_adapter = k971_log_adapter, + .init_proto = kbic_init_proto, + .release_proto = kbic_release_proto +}; + +static int __init kbic_init(void) +{ + return (pi_register(&k951)||pi_register(&k971))-1; } -void cleanup_module(void) - -{ pi_unregister( &k951 ); - pi_unregister( &k971 ); +static void __exit kbic_exit(void) +{ + pi_unregister(&k951); + pi_unregister(&k971); } -#endif - -/* end of kbic.c */ MODULE_LICENSE("GPL"); +module_init(kbic_init) +module_exit(kbic_exit) diff --git a/drivers/block/paride/ktti.c b/drivers/block/paride/ktti.c index 60778192e0c1..bee083b4da9c 100644 --- a/drivers/block/paride/ktti.c +++ b/drivers/block/paride/ktti.c @@ -12,6 +12,7 @@ #define KTTI_VERSION "1.0" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/types.h> @@ -106,35 +107,33 @@ static void ktti_release_proto( PIA *pi) { MOD_DEC_USE_COUNT; } -struct pi_protocol ktti = {"ktti",0,1,2,1,1, - ktti_write_regr, - ktti_read_regr, - ktti_write_block, - ktti_read_block, - ktti_connect, - ktti_disconnect, - 0, - 0, - 0, - ktti_log_adapter, - ktti_init_proto, - ktti_release_proto - }; - - -#ifdef MODULE - -int init_module(void) - -{ return pi_register( &ktti ) - 1; +static struct pi_protocol ktti = { + .name = "ktti", + .max_mode = 1, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = ktti_write_regr, + .read_regr = ktti_read_regr, + .write_block = ktti_write_block, + .read_block = ktti_read_block, + .connect = ktti_connect, + .disconnect = ktti_disconnect, + .log_adapter = ktti_log_adapter, + .init_proto = ktti_init_proto, + .release_proto = ktti_release_proto, +}; + +static int __init ktti_init(void) +{ + return pi_register(&ktti)-1; } -void cleanup_module(void) - -{ pi_unregister( &ktti ); +static void __exit ktti_exit(void) +{ + pi_unregister(&ktti); } -#endif - -/* end of ktti.c */ MODULE_LICENSE("GPL"); +module_init(ktti_init) +module_exit(ktti_exit) diff --git a/drivers/block/paride/on20.c b/drivers/block/paride/on20.c index b3c8d3d325e0..8528e0212591 100644 --- a/drivers/block/paride/on20.c +++ b/drivers/block/paride/on20.c @@ -15,6 +15,7 @@ #define ON20_VERSION "1.01" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/types.h> @@ -131,35 +132,33 @@ static void on20_release_proto( PIA *pi) { MOD_DEC_USE_COUNT; } -struct pi_protocol on20 = {"on20",0,2,2,1,1, - on20_write_regr, - on20_read_regr, - on20_write_block, - on20_read_block, - on20_connect, - on20_disconnect, - 0, - 0, - 0, - on20_log_adapter, - on20_init_proto, - on20_release_proto - }; - - -#ifdef MODULE - -int init_module(void) - -{ return pi_register( &on20 ) - 1; +static struct pi_protocol on20 = { + .name = "on20", + .max_mode = 2, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = on20_write_regr, + .read_regr = on20_read_regr, + .write_block = on20_write_block, + .read_block = on20_read_block, + .connect = on20_connect, + .disconnect = on20_disconnect, + .log_adapter = on20_log_adapter, + .init_proto = on20_init_proto, + .release_proto = on20_release_proto, +}; + +static int __init on20_init(void) +{ + return pi_register(&on20)-1; } -void cleanup_module(void) - -{ pi_unregister( &on20 ); +static void __exit on20_exit(void) +{ + pi_unregister(&on20); } -#endif - -/* end of on20.c */ MODULE_LICENSE("GPL"); +module_init(on20_init) +module_exit(on20_exit) diff --git a/drivers/block/paride/on26.c b/drivers/block/paride/on26.c index 0688c6317972..c08111db5a21 100644 --- a/drivers/block/paride/on26.c +++ b/drivers/block/paride/on26.c @@ -19,6 +19,7 @@ #define ON26_VERSION "1.04" #include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/types.h> @@ -296,36 +297,34 @@ static void on26_release_proto( PIA *pi) { MOD_DEC_USE_COUNT; } -struct pi_protocol on26 = {"on26",0,5,2,1,1, - on26_write_regr, - on26_read_regr, - on26_write_block, - on26_read_block, - on26_connect, - on26_disconnect, - on26_test_port, - 0, - 0, - on26_log_adapter, - on26_init_proto, - on26_release_proto - }; - - -#ifdef MODULE - -int init_module(void) - -{ return pi_register( &on26 ) - 1; +static struct pi_protocol on26 = { + .name = "on26", + .max_mode = 5, + .epp_first = 2, + .default_delay = 1, + .max_units = 1, + .write_regr = on26_write_regr, + .read_regr = on26_read_regr, + .write_block = on26_write_block, + .read_block = on26_read_block, + .connect = on26_connect, + .disconnect = on26_disconnect, + .test_port = on26_test_port, + .log_adapter = on26_log_adapter, + .init_proto = on26_init_proto, + .release_proto = on26_release_proto, +}; + +static int __init on26_init(void) +{ + return pi_register(&on26)-1; } -void cleanup_module(void) - -{ pi_unregister( &on26 ); +static void __exit on26_exit(void) +{ + pi_unregister(&on26); } -#endif - -/* end of on26.c */ - MODULE_LICENSE("GPL"); +module_init(on26_init) +module_exit(on26_exit) diff --git a/drivers/block/paride/paride.c b/drivers/block/paride/paride.c index e3a6150acff6..18bd7892269c 100644 --- a/drivers/block/paride/paride.c +++ b/drivers/block/paride/paride.c @@ -431,136 +431,3 @@ int pi_init(PIA *pi, int autoprobe, int port, int mode, } EXPORT_SYMBOL(pi_init); - -#ifdef MODULE - -int init_module(void) - -{ - int k; - const char *indicate_pp = ""; -#ifdef CONFIG_PARPORT - indicate_pp = " (parport)"; -#endif - - for (k=0;k<MAX_PROTOS;k++) protocols[k] = 0; - - printk("paride: version %s installed%s\n",PI_VERSION,indicate_pp); - return 0; -} - -void cleanup_module(void) - -{ -} - -#else - -void paride_init( void ) - -{ - -#ifdef CONFIG_PARIDE_ATEN - { extern struct pi_protocol aten; - pi_register(&aten); - }; -#endif -#ifdef CONFIG_PARIDE_BPCK - { extern struct pi_protocol bpck; - pi_register(&bpck); - }; -#endif -#ifdef CONFIG_PARIDE_COMM - { extern struct pi_protocol comm; - pi_register(&comm); - }; -#endif -#ifdef CONFIG_PARIDE_DSTR - { extern struct pi_protocol dstr; - pi_register(&dstr); - }; -#endif -#ifdef CONFIG_PARIDE_EPAT - { extern struct pi_protocol epat; - pi_register(&epat); - }; -#endif -#ifdef CONFIG_PARIDE_EPIA - { extern struct pi_protocol epia; - pi_register(&epia); - }; -#endif -#ifdef CONFIG_PARIDE_FRPW - { extern struct pi_protocol frpw; - pi_register(&frpw); - }; -#endif -#ifdef CONFIG_PARIDE_FRIQ - { extern struct pi_protocol friq; - pi_register(&friq); - }; -#endif -#ifdef CONFIG_PARIDE_FIT2 - { extern struct pi_protocol fit2; - pi_register(&fit2); - }; -#endif -#ifdef CONFIG_PARIDE_FIT3 - { extern struct pi_protocol fit3; - pi_register(&fit3); - }; -#endif -#ifdef CONFIG_PARIDE_KBIC - { extern struct pi_protocol k951; - extern struct pi_protocol k971; - pi_register(&k951); - pi_register(&k971); - }; -#endif -#ifdef CONFIG_PARIDE_KTTI - { extern struct pi_protocol ktti; - pi_register(&ktti); - }; -#endif -#ifdef CONFIG_PARIDE_ON20 - { extern struct pi_protocol on20; - pi_register(&on20); - }; -#endif -#ifdef CONFIG_PARIDE_ON26 - { extern struct pi_protocol on26; - pi_register(&on26); - }; -#endif - -#ifdef CONFIG_PARIDE_PD - { extern int pd_init(void); - pd_init(); - }; -#endif -#ifdef CONFIG_PARIDE_PCD - { extern int pcd_init(void); - pcd_init(); - }; -#endif -#ifdef CONFIG_PARIDE_PF - { extern int pf_init(void); - pf_init(); - }; -#endif -#ifdef CONFIG_PARIDE_PT - { extern int pt_init(void); - pt_init(); - }; -#endif -#ifdef CONFIG_PARIDE_PG - { extern int pg_init(void); - pg_init(); - }; -#endif -} - -#endif - -/* end of paride.c */ -MODULE_LICENSE("GPL"); diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c index e9839726b20e..25273d4ba1b8 100644 --- a/drivers/block/paride/pcd.c +++ b/drivers/block/paride/pcd.c @@ -137,6 +137,7 @@ static int pcd_drive_count; /* end of parameters */ #include <linux/module.h> +#include <linux/init.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/kernel.h> @@ -200,9 +201,6 @@ MODULE_PARM(drive3,"1-6i"); #define IDE_READY 0x40 #define IDE_BUSY 0x80 -int pcd_init(void); -void cleanup_module( void ); - static int pcd_open(struct cdrom_device_info *cdi, int purpose); static void pcd_release(struct cdrom_device_info *cdi); static int pcd_drive_status(struct cdrom_device_info *cdi, int slot_nr); @@ -327,84 +325,18 @@ static void pcd_init_units( void ) } } -int pcd_init (void) /* preliminary initialisation */ -{ - int unit; - - if (disable) return -1; - - pcd_init_units(); - - if (pcd_detect()) return -1; - - /* get the atapi capabilities page */ - pcd_probe_capabilities(); - - if (register_blkdev(MAJOR_NR,name,&pcd_bdops)) { - printk("pcd: unable to get major number %d\n",MAJOR_NR); - return -1; - } - - for (unit=0;unit<PCD_UNITS;unit++) { - if (PCD.present) { - register_cdrom(&PCD.info); - devfs_plain_cdrom(&PCD.info, &pcd_bdops); - } - } - - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_pcd_request, &pcd_lock); - - return 0; -} - static int pcd_open(struct cdrom_device_info *cdi, int purpose) - -{ int unit = DEVICE_NR(cdi->dev); - - if ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV; - +{ + int unit = DEVICE_NR(cdi->dev); + if ((unit >= PCD_UNITS) || (!PCD.present)) + return -ENODEV; return 0; } static void pcd_release(struct cdrom_device_info *cdi) - { } -#ifdef MODULE - -/* Glue for modules ... */ - -int init_module(void) - -{ int err; - -#ifdef PARIDE_JUMBO - { extern paride_init(); - paride_init(); - } -#endif - - err = pcd_init(); - - return err; -} - -void cleanup_module(void) - -{ int unit; - - for (unit=0;unit<PCD_UNITS;unit++) - if (PCD.present) { - pi_release(PI); - unregister_cdrom(&PCD.info); - } - - unregister_blkdev(MAJOR_NR,name); -} - -#endif - #define WR(c,r,v) pi_write_regr(PI,c,r,v) #define RR(c,r) (pi_read_regr(PI,c,r)) @@ -950,6 +882,50 @@ static int pcd_get_mcn (struct cdrom_device_info *cdi, struct cdrom_mcn *mcn) return 0; } -/* end of pcd.c */ + +static int __init pcd_init(void) +{ + int unit; + + if (disable) + return -1; + + pcd_init_units(); + + if (pcd_detect()) + return -1; + + /* get the atapi capabilities page */ + pcd_probe_capabilities(); + + if (register_blkdev(MAJOR_NR,name,&pcd_bdops)) { + printk("pcd: unable to get major number %d\n",MAJOR_NR); + return -1; + } + + for (unit=0;unit<PCD_UNITS;unit++) { + if (PCD.present) { + register_cdrom(&PCD.info); + devfs_plain_cdrom(&PCD.info, &pcd_bdops); + } + } + + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_pcd_request, &pcd_lock); + + return 0; +} + +static void __exit pcd_exit(void) +{ + int unit; + for (unit=0;unit<PCD_UNITS;unit++) + if (PCD.present) { + pi_release(PI); + unregister_cdrom(&PCD.info); + } + unregister_blkdev(MAJOR_NR,name); +} MODULE_LICENSE("GPL"); +module_init(pcd_init) +module_exit(pcd_exit) diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c index cb10da16d136..f9b681ac6b86 100644 --- a/drivers/block/paride/pd.c +++ b/drivers/block/paride/pd.c @@ -153,16 +153,12 @@ static int pd_drive_count; /* end of parameters */ +#include <linux/init.h> #include <linux/module.h> -#include <linux/errno.h> #include <linux/fs.h> -#include <linux/devfs_fs_kernel.h> -#include <linux/kernel.h> #include <linux/delay.h> -#include <linux/genhd.h> #include <linux/hdreg.h> #include <linux/cdrom.h> /* for the eject ioctl */ -#include <linux/spinlock.h> #include <asm/uaccess.h> @@ -181,8 +177,8 @@ static STT pd_stt[7] = {{"drive0",8,drive0}, {"nice",1,&nice}}; void pd_setup( char *str, int *ints) - -{ generic_setup(pd_stt,7,str); +{ + generic_setup(pd_stt,7,str); } #endif @@ -199,19 +195,15 @@ MODULE_PARM(drive3,"1-8i"); #include "paride.h" -#define PD_BITS 4 - -/* set up defines for blk.h, why don't all drivers do it this way ? */ - #define MAJOR_NR major -#define DEVICE_NR(device) (minor(device)>>PD_BITS) -#define DEVICE_OFF(device) #include <linux/blk.h> #include <linux/blkpg.h> #include "pseudo.h" +#define PD_BITS 4 +#define DEVICE_NR(device) (minor(device)>>PD_BITS) #define PD_PARTNS (1<<PD_BITS) #define PD_DEVS PD_PARTNS*PD_UNITS @@ -258,11 +250,7 @@ MODULE_PARM(drive3,"1-8i"); #define IDE_IDENTIFY 0xec #define IDE_EJECT 0xed -int pd_init(void); void pd_setup(char * str, int * ints); -#ifdef MODULE -void cleanup_module( void ); -#endif static int pd_open(struct inode *inode, struct file *file); static void do_pd_request(request_queue_t * q); static int pd_ioctl(struct inode *inode,struct file *file, @@ -296,6 +284,7 @@ struct pd_unit { int heads; /* physical geometry */ int sectors; int cylinders; + int can_lba; int drive; /* master=0 slave=1 */ int changed; /* Have we seen a disk change ? */ int removable; /* removable media device ? */ @@ -303,7 +292,7 @@ struct pd_unit { int alt_geom; int present; char name[PD_NAMELEN]; /* pda, pdb, etc ... */ - }; +}; struct pd_unit pd[PD_UNITS]; @@ -312,7 +301,6 @@ struct pd_unit pd[PD_UNITS]; #define PD pd[unit] #define PI PD.pi -static int pd_valid = 1; /* serialise partition checks */ static char pd_scratch[512]; /* scratch block buffer */ /* the variables below are used mainly in the I/O request engine, which @@ -379,47 +367,12 @@ void pd_init_units( void ) } } -int pd_init (void) -{ - request_queue_t * q; - - if (disable) return -1; - if (devfs_register_blkdev(MAJOR_NR,name,&pd_fops)) { - printk("%s: unable to get major number %d\n", - name,major); - return -1; - } - q = BLK_DEFAULT_QUEUE(MAJOR_NR); - blk_init_queue(q, do_pd_request, &pd_lock); - blk_queue_max_sectors(q, cluster); - - pd_gendisk.major = major; - pd_gendisk.major_name = name; - add_gendisk(&pd_gendisk); - - printk("%s: %s version %s, major %d, cluster %d, nice %d\n", - name,name,PD_VERSION,major,cluster,nice); - pd_init_units(); - pd_valid = 0; - pd_gendisk.nr_real = pd_detect(); - pd_valid = 1; - -#ifdef MODULE - if (!pd_gendisk.nr_real) { - cleanup_module(); - return -1; - } -#endif - return 0; -} - static int pd_open (struct inode *inode, struct file *file) +{ + int unit = DEVICE_NR(inode->i_rdev); -{ int unit = DEVICE_NR(inode->i_rdev); - - if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV; - - wait_event (pd_wait_open, pd_valid); + if ((unit >= PD_UNITS) || (!PD.present)) + return -ENODEV; PD.access++; @@ -434,11 +387,8 @@ static int pd_ioctl(struct inode *inode,struct file *file, unsigned int cmd, unsigned long arg) { struct hd_geometry *geo = (struct hd_geometry *) arg; - int err, unit; + int err, unit = DEVICE_NR(inode->i_rdev); - if (!inode || kdev_none(inode->i_rdev)) - return -EINVAL; - unit = DEVICE_NR(inode->i_rdev); if (!PD.present) return -ENODEV; @@ -461,50 +411,34 @@ static int pd_ioctl(struct inode *inode,struct file *file, put_user(PD.heads, (char *) &geo->heads); put_user(PD.sectors, (char *) &geo->sectors); } - put_user(get_start_sect(inode->i_rdev), (long *)&geo->start); + put_user(get_start_sect(inode->i_bdev), (long *)&geo->start); return 0; case BLKRRPART: if (!capable(CAP_SYS_ADMIN)) return -EACCES; return pd_revalidate(inode->i_rdev); - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKROSET: - case BLKROGET: - case BLKFLSBUF: - case BLKPG: - return blk_ioctl(inode->i_bdev, cmd, arg); default: return -EINVAL; } } static int pd_release (struct inode *inode, struct file *file) +{ + int unit = DEVICE_NR(inode->i_rdev); -{ kdev_t devp; - int unit; - - devp = inode->i_rdev; - unit = DEVICE_NR(devp); - - if ((unit >= PD_UNITS) || (PD.access <= 0)) - return -EINVAL; - - PD.access--; - - if (!PD.access && PD.removable) + if (!--PD.access && PD.removable) pd_doorlock(unit,IDE_DOORUNLOCK); return 0; } static int pd_check_media( kdev_t dev) - -{ int r, unit; - - unit = DEVICE_NR(dev); - if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV; - if (!PD.removable) return 0; +{ + int r, unit = DEVICE_NR(dev); + if ((unit >= PD_UNITS) || (!PD.present)) + return -ENODEV; + if (!PD.removable) + return 0; pd_media_check(unit); r = PD.changed; PD.changed = 0; @@ -513,64 +447,26 @@ static int pd_check_media( kdev_t dev) static int pd_revalidate(kdev_t dev) { - int unit, res; - long flags; + int unit = DEVICE_NR(dev); + kdev_t device = mk_kdev(MAJOR_NR, unit << PD_BITS); + int res; - unit = DEVICE_NR(dev); if ((unit >= PD_UNITS) || !PD.present) return -ENODEV; - save_flags(flags); - cli(); - if (PD.access > 1) { - restore_flags(flags); - return -EBUSY; - } - pd_valid = 0; - restore_flags(flags); - - res = wipe_partitions(dev); + res = dev_lock_part(device); + if (res < 0) + return res; + res = wipe_partitions(device); if (res == 0 && pd_identify(unit)) - grok_partitions(dev, PD.capacity); + grok_partitions(device, PD.capacity); - pd_valid = 1; - wake_up(&pd_wait_open); + dev_unlock_part(device); return res; } -#ifdef MODULE - -/* Glue for modules ... */ - -void cleanup_module(void); - -int init_module(void) - -{ - -#ifdef PARIDE_JUMBO - { extern paride_init(); - paride_init(); - } -#endif - return pd_init(); -} - -void cleanup_module(void) -{ - int unit; - - devfs_unregister_blkdev(MAJOR_NR, name); - del_gendisk(&pd_gendisk); - - for (unit=0; unit<PD_UNITS; unit++) - if (PD.present) - pi_release(PI); -} -#endif - #define WR(c,r,v) pi_write_regr(PI,c,r,v) #define RR(c,r) (pi_read_regr(PI,c,r)) @@ -619,7 +515,6 @@ static int pd_wait_for( int unit, int w, char * msg ) /* polled wait */ static void pd_send_command( int unit, int n, int s, int h, int c0, int c1, int func ) - { WR(0,6,DRIVE+h); WR(0,1,0); /* the IDE task file */ @@ -633,16 +528,20 @@ static void pd_send_command( int unit, int n, int s, int h, } static void pd_ide_command( int unit, int func, int block, int count ) - -/* Don't use this call if the capacity is zero. */ - -{ int c1, c0, h, s; - - s = ( block % PD.sectors) + 1; - h = ( block / PD.sectors) % PD.heads; - c0 = ( block / (PD.sectors*PD.heads)) % 256; - c1 = ( block / (PD.sectors*PD.heads*256)); - +{ + int c1, c0, h, s; + + if (PD.can_lba) { + s = block & 255; + c0 = (block >>= 8) & 255; + c1 = (block >>= 8) & 255; + h = ((block >>= 8) & 15) + 0x40; + } else { + s = ( block % PD.sectors) + 1; + h = ( block /= PD.sectors) % PD.heads; + c0 = ( block /= PD.heads) % 256; + c1 = (block >>= 8); + } pd_send_command(unit,count,s,h,c0,c1,func); } @@ -742,10 +641,14 @@ static int pd_identify( int unit ) } pi_read_block(PI,pd_scratch,512); pi_disconnect(PI); - PD.sectors = word_val(6); - PD.heads = word_val(3); - PD.cylinders = word_val(1); - PD.capacity = PD.sectors*PD.heads*PD.cylinders; + PD.can_lba = pd_scratch[99] & 2; + PD.sectors = le16_to_cpu(*(u16*)(pd_scratch+12)); + PD.heads = le16_to_cpu(*(u16*)(pd_scratch+6)); + PD.cylinders = le16_to_cpu(*(u16*)(pd_scratch+2)); + if (PD.can_lba) + PD.capacity = le32_to_cpu(*(u32*)(pd_scratch + 120)); + else + PD.capacity = PD.sectors*PD.heads*PD.cylinders; for(j=0;j<PD_ID_LEN;j++) id[j^1] = pd_scratch[j+PD_ID_OFF]; j = PD_ID_LEN-1; @@ -763,7 +666,7 @@ static int pd_identify( int unit ) if (PD.capacity) pd_init_dev_parms(unit); if (!PD.standby) pd_standby_off(unit); - + return 1; } @@ -1031,6 +934,50 @@ static void do_pd_write_done( void ) spin_unlock_irqrestore(&pd_lock,saved_flags); } -/* end of pd.c */ +static int __init pd_init(void) +{ + request_queue_t * q; + int unit; + + if (disable) return -1; + if (devfs_register_blkdev(MAJOR_NR,name,&pd_fops)) { + printk("%s: unable to get major number %d\n", + name,major); + return -1; + } + q = BLK_DEFAULT_QUEUE(MAJOR_NR); + blk_init_queue(q, do_pd_request, &pd_lock); + blk_queue_max_sectors(q, cluster); + + pd_gendisk.major = major; + pd_gendisk.major_name = name; + add_gendisk(&pd_gendisk); + + printk("%s: %s version %s, major %d, cluster %d, nice %d\n", + name,name,PD_VERSION,major,cluster,nice); + pd_init_units(); + pd_gendisk.nr_real = pd_detect(); + if (!pd_gendisk.nr_real) { + devfs_unregister_blkdev(MAJOR_NR, name); + del_gendisk(&pd_gendisk); + for (unit=0; unit<PD_UNITS; unit++) + if (PD.present) + pi_release(PI); + return -1; + } + return 0; +} + +static void __exit pd_exit(void) +{ + int unit; + devfs_unregister_blkdev(MAJOR_NR, name); + del_gendisk(&pd_gendisk); + for (unit=0; unit<PD_UNITS; unit++) + if (PD.present) + pi_release(PI); +} MODULE_LICENSE("GPL"); +module_init(pd_init) +module_exit(pd_exit) diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c index 578feaadbb38..a117aa6df61a 100644 --- a/drivers/block/paride/pf.c +++ b/drivers/block/paride/pf.c @@ -153,11 +153,9 @@ static int pf_drive_count; #include <linux/module.h> -#include <linux/errno.h> +#include <linux/init.h> #include <linux/fs.h> -#include <linux/kernel.h> #include <linux/delay.h> -#include <linux/genhd.h> #include <linux/hdreg.h> #include <linux/cdrom.h> #include <linux/spinlock.h> @@ -240,7 +238,6 @@ MODULE_PARM(drive3,"1-7i"); #define ATAPI_READ_10 0x28 #define ATAPI_WRITE_10 0x2a -int pf_init(void); #ifdef MODULE void cleanup_module( void ); #endif @@ -337,34 +334,6 @@ void pf_init_units( void ) } } -int pf_init (void) /* preliminary initialisation */ - -{ int i; - request_queue_t * q; - - if (disable) return -1; - - pf_init_units(); - - if (pf_detect()) return -1; - pf_busy = 0; - - if (register_blkdev(MAJOR_NR,name,&pf_fops)) { - printk("pf_init: unable to get major number %d\n", - major); - return -1; - } - q = BLK_DEFAULT_QUEUE(MAJOR_NR); - blk_init_queue(q, do_pf_request, &pf_spin_lock); - blk_queue_max_phys_segments(q, cluster); - blk_queue_max_hw_segments(q, cluster); - - for (i=0;i<PF_UNITS;i++) - register_disk(NULL, mk_kdev(MAJOR_NR, i), 1, &pf_fops, 0); - - return 0; -} - static int pf_open (struct inode *inode, struct file *file) { int unit = DEVICE_NR(inode->i_rdev); @@ -423,10 +392,6 @@ static int pf_ioctl(struct inode *inode,struct file *file, return put_user(PF.capacity,(long *) arg); case BLKGETSIZE64: return put_user((u64)PF.capacity << 9,(u64 *)arg); - case BLKROSET: - case BLKROGET: - case BLKFLSBUF: - return blk_ioctl(inode->i_bdev, cmd, arg); default: return -EINVAL; } @@ -458,39 +423,6 @@ static int pf_check_media( kdev_t dev) { return 1; } -#ifdef MODULE - -/* Glue for modules ... */ - -void cleanup_module(void); - -int init_module(void) - -{ int err; - -#ifdef PARIDE_JUMBO - { extern paride_init(); - paride_init(); - } -#endif - - err = pf_init(); - - return err; -} - -void cleanup_module(void) - -{ int unit; - - unregister_blkdev(MAJOR_NR,name); - - for (unit=0;unit<PF_UNITS;unit++) - if (PF.present) pi_release(PI); -} - -#endif - #define WR(c,r,v) pi_write_regr(PI,c,r,v) #define RR(c,r) (pi_read_regr(PI,c,r)) @@ -1040,6 +972,44 @@ static void do_pf_write_done( void ) spin_unlock_irqrestore(&pf_spin_lock,saved_flags); } -/* end of pf.c */ +static int __init pf_init(void) /* preliminary initialisation */ +{ + int i; + request_queue_t * q; + + if (disable) + return -1; + + pf_init_units(); + + if (pf_detect()) + return -1; + pf_busy = 0; + + if (register_blkdev(MAJOR_NR,name,&pf_fops)) { + printk("pf_init: unable to get major number %d\n", major); + return -1; + } + q = BLK_DEFAULT_QUEUE(MAJOR_NR); + blk_init_queue(q, do_pf_request, &pf_spin_lock); + blk_queue_max_phys_segments(q, cluster); + blk_queue_max_hw_segments(q, cluster); + + for (i=0;i<PF_UNITS;i++) + register_disk(NULL, mk_kdev(MAJOR_NR, i), 1, &pf_fops, 0); + return 0; +} + +static void __exit pf_exit(void) +{ + int unit; + unregister_blkdev(MAJOR_NR,name); + for (unit=0;unit<PF_UNITS;unit++) + if (PF.present) + pi_release(PI); +} + MODULE_LICENSE("GPL"); +module_init(pf_init) +module_exit(pf_exit) diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c index 5f5af68fc86c..0a43b4a5d61b 100644 --- a/drivers/block/paride/pg.c +++ b/drivers/block/paride/pg.c @@ -162,16 +162,13 @@ static int pg_drive_count; #include <linux/module.h> -#include <linux/errno.h> +#include <linux/init.h> #include <linux/fs.h> #include <linux/devfs_fs_kernel.h> -#include <linux/kernel.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/mtio.h> #include <linux/pg.h> -#include <linux/wait.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> @@ -218,11 +215,6 @@ MODULE_PARM(drive3,"1-6i"); #define ATAPI_IDENTIFY 0x12 -int pg_init(void); -#ifdef MODULE -void cleanup_module( void ); -#endif - static int pg_open(struct inode *inode, struct file *file); static int pg_release (struct inode *inode, struct file *file); static ssize_t pg_read(struct file * filp, char * buf, @@ -291,64 +283,6 @@ void pg_init_units( void ) static devfs_handle_t devfs_handle; -int pg_init (void) /* preliminary initialisation */ - -{ int unit; - - if (disable) return -1; - - pg_init_units(); - - if (pg_detect()) return -1; - - if (devfs_register_chrdev(major,name,&pg_fops)) { - printk("pg_init: unable to get major number %d\n", - major); - for (unit=0;unit<PG_UNITS;unit++) - if (PG.present) pi_release(PI); - return -1; - } - devfs_handle = devfs_mk_dir (NULL, "pg", NULL); - devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, - major, 0, S_IFCHR | S_IRUSR | S_IWUSR, - &pg_fops, NULL); - return 0; -} - -#ifdef MODULE - -/* Glue for modules ... */ - -void cleanup_module(void); - -int init_module(void) - -{ int err; - -#ifdef PARIDE_JUMBO - { extern paride_init(); - paride_init(); - } -#endif - - err = pg_init(); - - return err; -} - -void cleanup_module(void) - -{ int unit; - - devfs_unregister (devfs_handle); - devfs_unregister_chrdev(major,name); - - for (unit=0;unit<PG_UNITS;unit++) - if (PG.present) pi_release(PI); -} - -#endif - #define WR(c,r,v) pi_write_regr(PI,c,r,v) #define RR(c,r) (pi_read_regr(PI,c,r)) @@ -691,6 +625,43 @@ static ssize_t pg_read(struct file * filp, char * buf, return copy+hs; } -/* end of pg.c */ +static int __init pg_init(void) +{ + int unit; + + if (disable) + return -1; + + pg_init_units(); + + if (pg_detect()) + return -1; + + if (devfs_register_chrdev(major,name,&pg_fops)) { + printk("pg_init: unable to get major number %d\n", + major); + for (unit=0;unit<PG_UNITS;unit++) + if (PG.present) pi_release(PI); + return -1; + } + devfs_handle = devfs_mk_dir (NULL, "pg", NULL); + devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, + major, 0, S_IFCHR | S_IRUSR | S_IWUSR, + &pg_fops, NULL); + return 0; +} + +static void __exit pg_exit(void) +{ + int unit; + + devfs_unregister (devfs_handle); + devfs_unregister_chrdev(major,name); + + for (unit=0;unit<PG_UNITS;unit++) + if (PG.present) pi_release(PI); +} MODULE_LICENSE("GPL"); +module_init(pg_init) +module_exit(pg_exit) diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c index ccf3db7b1463..bc987791fd32 100644 --- a/drivers/block/paride/pt.c +++ b/drivers/block/paride/pt.c @@ -141,15 +141,12 @@ static int pt_drive_count; #include <linux/module.h> -#include <linux/errno.h> +#include <linux/init.h> #include <linux/fs.h> #include <linux/devfs_fs_kernel.h> -#include <linux/kernel.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/mtio.h> -#include <linux/wait.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> @@ -209,11 +206,6 @@ MODULE_PARM(drive3,"1-6i"); #define ATAPI_MODE_SENSE 0x1a #define ATAPI_LOG_SENSE 0x4d -int pt_init(void); -#ifdef MODULE -void cleanup_module( void ); -#endif - static int pt_open(struct inode *inode, struct file *file); static int pt_ioctl(struct inode *inode,struct file *file, unsigned int cmd, unsigned long arg); @@ -291,71 +283,9 @@ void pt_init_units( void ) PT.name[j] = 0; if (DU[D_PRT]) pt_drive_count++; } -} - -static devfs_handle_t devfs_handle; - -int pt_init (void) /* preliminary initialisation */ - -{ int unit; - - if (disable) return -1; - - pt_init_units(); - - if (pt_detect()) return -1; - - if (devfs_register_chrdev(major,name,&pt_fops)) { - printk("pt_init: unable to get major number %d\n", - major); - for (unit=0;unit<PT_UNITS;unit++) - if (PT.present) pi_release(PI); - return -1; - } - - devfs_handle = devfs_mk_dir (NULL, "pt", NULL); - devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, - major, 0, S_IFCHR | S_IRUSR | S_IWUSR, - &pt_fops, NULL); - devfs_register_series (devfs_handle, "%un", 4, DEVFS_FL_DEFAULT, - major, 128, S_IFCHR | S_IRUSR | S_IWUSR, - &pt_fops, NULL); - return 0; } -#ifdef MODULE - -/* Glue for modules ... */ - -void cleanup_module(void); - -int init_module(void) - -{ int err; - -#ifdef PARIDE_JUMBO - { extern paride_init(); - paride_init(); - } -#endif - - err = pt_init(); - - return err; -} - -void cleanup_module(void) - -{ int unit; - - devfs_unregister (devfs_handle); - devfs_unregister_chrdev(major,name); - - for (unit=0;unit<PT_UNITS;unit++) - if (PT.present) pi_release(PI); -} - -#endif +static devfs_handle_t devfs_handle; #define WR(c,r,v) pi_write_regr(PI,c,r,v) #define RR(c,r) (pi_read_regr(PI,c,r)) @@ -965,6 +895,46 @@ static ssize_t pt_write(struct file * filp, const char * buf, return t; } -/* end of pt.c */ +static int __init pt_init(void) +{ + int unit; + + if (disable) + return -1; + + pt_init_units(); + + if (pt_detect()) + return -1; + + if (devfs_register_chrdev(major,name,&pt_fops)) { + printk("pt_init: unable to get major number %d\n", + major); + for (unit=0;unit<PT_UNITS;unit++) + if (PT.present) pi_release(PI); + return -1; + } + + devfs_handle = devfs_mk_dir (NULL, "pt", NULL); + devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, + major, 0, S_IFCHR | S_IRUSR | S_IWUSR, + &pt_fops, NULL); + devfs_register_series (devfs_handle, "%un", 4, DEVFS_FL_DEFAULT, + major, 128, S_IFCHR | S_IRUSR | S_IWUSR, + &pt_fops, NULL); + return 0; +} + +static void __exit pt_exit(void) +{ + int unit; + devfs_unregister (devfs_handle); + devfs_unregister_chrdev(major,name); + for (unit=0;unit<PT_UNITS;unit++) + if (PT.present) + pi_release(PI); +} MODULE_LICENSE("GPL"); +module_init(pt_init) +module_exit(pt_exit) diff --git a/drivers/block/ps2esdi.c b/drivers/block/ps2esdi.c index bd604cee4663..1d734f15cab2 100644 --- a/drivers/block/ps2esdi.c +++ b/drivers/block/ps2esdi.c @@ -93,8 +93,6 @@ static void ps2esdi_geometry_int_handler(u_int); static int ps2esdi_open(struct inode *inode, struct file *file); -static int ps2esdi_release(struct inode *inode, struct file *file); - static int ps2esdi_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg); @@ -111,11 +109,8 @@ static void ps2esdi_reset_timer(unsigned long unused); static u_int dma_arb_level; /* DMA arbitration level */ static DECLARE_WAIT_QUEUE_HEAD(ps2esdi_int); -static DECLARE_WAIT_QUEUE_HEAD(ps2esdi_wait_open); static int no_int_yet; -static int access_count[MAX_HD]; -static char ps2esdi_valid[MAX_HD]; static int ps2esdi_sizes[MAX_HD << 6]; static int ps2esdi_drives; static struct hd_struct ps2esdi[MAX_HD << 6]; @@ -152,7 +147,6 @@ static struct block_device_operations ps2esdi_fops = { owner: THIS_MODULE, open: ps2esdi_open, - release: ps2esdi_release, ioctl: ps2esdi_ioctl, }; @@ -441,13 +435,11 @@ static int __init ps2esdi_geninit(void) } blk_queue_max_sectors(BLK_DEFAULT_QUEUE(MAJOR_NR), 128); - for (i = 0; i < ps2esdi_drives; i++) { + for (i = 0; i < ps2esdi_drives; i++) register_disk(&ps2esdi_gendisk,mk_kdev(MAJOR_NR,i<<6),1<<6, &ps2esdi_fops, ps2esdi_info[i].head * ps2esdi_info[i].sect * ps2esdi_info[i].cyl); - ps2esdi_valid[i] = 1; - } return 0; err_out3: @@ -1083,32 +1075,11 @@ static void dump_cmd_complete_status(u_int int_ret_code) static int ps2esdi_open(struct inode *inode, struct file *file) { int dev = DEVICE_NR(inode->i_rdev); - - if (dev < ps2esdi_drives) { - while (!ps2esdi_valid[dev]) - sleep_on(&ps2esdi_wait_open); - - access_count[dev]++; - - return (0); - } else - return (-ENODEV); -} - - - -static int ps2esdi_release(struct inode *inode, struct file *file) -{ - int dev = DEVICE_NR(inode->i_rdev); - - if (dev < ps2esdi_drives) { - access_count[dev]--; - } + if (dev >= ps2esdi_drives) + return -ENODEV; return 0; } - - static int ps2esdi_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) { @@ -1125,7 +1096,7 @@ static int ps2esdi_ioctl(struct inode *inode, put_user(ps2esdi_info[dev].head, (char *) &geometry->heads); put_user(ps2esdi_info[dev].sect, (char *) &geometry->sectors); put_user(ps2esdi_info[dev].cyl, (short *) &geometry->cylinders); - put_user(get_start_sect(inode->i_rdev), + put_user(get_start_sect(inode->b_rdev), (long *) &geometry->start); return 0; @@ -1136,16 +1107,6 @@ static int ps2esdi_ioctl(struct inode *inode, if (!capable(CAP_SYS_ADMIN)) return -EACCES; return (ps2esdi_reread_partitions(inode->i_rdev)); - - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKROSET: - case BLKROGET: - case BLKFLSBUF: - case BLKBSZGET: - case BLKBSZSET: - case BLKPG: - return blk_ioctl(inode->i_bdev, cmd, arg); } return (-EINVAL); } @@ -1155,24 +1116,20 @@ static int ps2esdi_ioctl(struct inode *inode, static int ps2esdi_reread_partitions(kdev_t dev) { int target = DEVICE_NR(dev); - int res; + kdev_t device = mk_kdev(MAJOR_NR, target << 6); + int res = dev_lock_part(device); - cli(); - ps2esdi_valid[target] = (access_count[target] != 1); - sti(); - if (ps2esdi_valid[target]) - return (-EBUSY); + if (res < 0) + return res; - res = wipe_partitions(dev); + res = wipe_partitions(device); if (res == 0) - grok_partitions(dev, ps2esdi_info[target].head + grok_partitions(device, ps2esdi_info[target].head * ps2esdi_info[target].cyl * ps2esdi_info[target].sect); - ps2esdi_valid[target] = 1; - wake_up(&ps2esdi_wait_open); - - return (res); + dev_unlock_part(device); + return res; } static void ps2esdi_reset_timer(unsigned long unused) diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 7b60e75d5584..662020429ba6 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -303,12 +303,6 @@ static int rd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un } up(&inode->i_bdev->bd_sem); break; - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKROSET: - case BLKROGET: - case BLKSSZGET: - error = blk_ioctl(inode->i_bdev, cmd, arg); } out: return error; diff --git a/drivers/block/umem.c b/drivers/block/umem.c index e046885bb67b..a1575eb830b0 100644 --- a/drivers/block/umem.c +++ b/drivers/block/umem.c @@ -65,7 +65,7 @@ #define MM_BLKSIZE 1024 /* 1k blocks */ #define MM_HARDSECT 512 /* 512-byte hardware sectors */ #define MM_SHIFT 6 /* max 64 partitions on 4 cards */ -#define DEVICE_NR(device) (MINOR(device)>>MM_SHIFT) +#define DEVICE_NR(device) (minor(device)>>MM_SHIFT) /* * Version Information @@ -150,7 +150,6 @@ struct cardinfo { unsigned long last_change; } battery[2]; - atomic_t usage; spinlock_t lock; int check_batteries; @@ -813,31 +812,21 @@ static void del_battery_timer(void) * Note no locks taken out here. In a worst case scenario, we could drop * a chunk of system memory. But that should never happen, since validation * happens at open or mount time, when locks are held. + * + * That's crap, since doing that while some partitions are opened + * or mounted will give you really nasty results. */ static int mm_revalidate(kdev_t i_rdev) { - int i; - - int card_number = DEVICE_NR(kdev_val(i_rdev)); - /* first partition, # of partitions */ - int part1 = (card_number << MM_SHIFT) + 1; - int npart = (1 << MM_SHIFT) -1; - - /* first clear old partition information */ - for (i=0; i<npart ;i++) { - mm_gendisk.sizes[part1+i]=0; - mm_gendisk.part[part1+i].start_sect = 0; - mm_gendisk.part[part1+i].nr_sects = 0; - } - - mm_gendisk.part[card_number << MM_SHIFT].nr_sects = - cards[card_number].mm_size << 1; - - - /* then fill new info */ + int card_number = DEVICE_NR(i_rdev); + kdev_t device = mk_mdev(MAJOR_NR, card_number << MM_SHIFT); + int res = dev_lock_part(device); + if (res < 0) + return res; + wipe_partitions(device); printk(KERN_INFO "mm partition check: (%d)\n", card_number); - grok_partitions(mk_kdev(major_nr,part1-1), - mm_gendisk.sizes[card_number<<MM_SHIFT]); + grok_partitions(device, cards[card_number].mm_size << 1); + dev_unlock_part(device); return 0; } /* @@ -859,16 +848,6 @@ static int mm_ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned switch(cmd) { - - case BLKGETSIZE: - /* Return the device size, expressed in sectors */ - err = ! access_ok (VERIFY_WRITE, arg, sizeof(long)); - if (err) return -EFAULT; - size = mm_gendisk.part[minor].nr_sects; - if (copy_to_user((long *) arg, &size, sizeof (long))) - return -EFAULT; - return 0; - case BLKRRPART: return (mm_revalidate(i->i_rdev)); @@ -883,16 +862,15 @@ static int mm_ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned size = cards[card_number].mm_size * (1024 / MM_HARDSECT); geo.heads = 64; geo.sectors = 32; - geo.start = mm_gendisk.part[minor].start_sect; + geo.start = get_start_sect(inode->i_bdev); geo.cylinders = size / (geo.heads * geo.sectors); if (copy_to_user((void *) arg, &geo, sizeof(geo))) return -EFAULT; return 0; - default: - return blk_ioctl(i->i_bdev, cmd, arg); + return -EINVAL; } return -ENOTTY; /* unknown command */ @@ -905,7 +883,7 @@ static int mm_ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned */ static int mm_check_change(kdev_t i_rdev) { - int card_number = DEVICE_NR(kdev_val(i_rdev)); + int card_number = DEVICE_NR(i_rdev); /* struct cardinfo *dev = cards + card_number; */ if (card_number >= num_cards) /* paranoid */ return 0; @@ -920,38 +898,8 @@ static int mm_check_change(kdev_t i_rdev) */ static int mm_open(struct inode *i, struct file *filp) { - int num; - struct cardinfo *card; - - num = DEVICE_NR(kdev_val(i->i_rdev)); - if (num >= num_cards) + if (DEVICE_NR(i->i_rdev) >= num_cards) return -ENXIO; - - card = cards + num; - - atomic_inc(&card->usage); - MOD_INC_USE_COUNT; - - return 0; -} -/* ------------------------------------------------------------------------------------ --- mm_do_release ------------------------------------------------------------------------------------ -*/ -static int mm_do_release(struct inode *i, struct file *filp) -{ - int num; - struct cardinfo *card; - - num = DEVICE_NR(kdev_val(i->i_rdev)); - - card = cards + num; - - if (atomic_dec_and_test(&card->usage)) - invalidate_device(i->i_rdev, 1); - - MOD_DEC_USE_COUNT; return 0; } /* @@ -962,7 +910,6 @@ static int mm_do_release(struct inode *i, struct file *filp) static struct block_device_operations mm_fops = { owner: THIS_MODULE, open: mm_open, - release: mm_do_release, ioctl: mm_ioctl, revalidate: mm_revalidate, check_media_change: mm_check_change, @@ -1243,7 +1190,7 @@ static struct pci_driver mm_pci_driver = { static request_queue_t * mm_queue_proc(kdev_t dev) { - int c = DEVICE_NR(kdev_val(dev)); + int c = DEVICE_NR(dev); if (c < MM_MAXCARDS) return &cards[c].queue; @@ -1293,7 +1240,6 @@ int __init mm_init(void) blk_dev[MAJOR_NR].queue = mm_queue_proc; add_gendisk(&mm_gendisk); - blk_size[MAJOR_NR] = mm_gendisk.sizes; for (i = 0; i < num_cards; i++) { register_disk(&mm_gendisk, mk_kdev(MAJOR_NR, i<<MM_SHIFT), MM_SHIFT, &mm_fops, cards[i].mm_size << 1); @@ -1325,9 +1271,6 @@ void __exit mm_cleanup(void) unregister_blkdev(MAJOR_NR, "umem"); - for (i = 0; i < (num_cards << MM_SHIFT); i++) - invalidate_device (mk_kdev(MAJOR_NR,i), 1); - blk_size [MAJOR_NR] = NULL; /* diff --git a/drivers/block/xd.c b/drivers/block/xd.c index bca8cd457e0e..a1166a4e1394 100644 --- a/drivers/block/xd.c +++ b/drivers/block/xd.c @@ -122,7 +122,7 @@ static unsigned int xd_bases[] __initdata = }; static struct hd_struct xd_struct[XD_MAXDRIVES << 6]; -static int xd_sizes[XD_MAXDRIVES << 6], xd_access[XD_MAXDRIVES]; +static int xd_sizes[XD_MAXDRIVES << 6]; static spinlock_t xd_lock = SPIN_LOCK_UNLOCKED; @@ -140,12 +140,9 @@ static struct gendisk xd_gendisk = { static struct block_device_operations xd_fops = { owner: THIS_MODULE, open: xd_open, - release: xd_release, ioctl: xd_ioctl, }; static DECLARE_WAIT_QUEUE_HEAD(xd_wait_int); -static DECLARE_WAIT_QUEUE_HEAD(xd_wait_open); -static u_char xd_valid[XD_MAXDRIVES] = { 0,0 }; static u_char xd_drives, xd_irq = 5, xd_dma = 3, xd_maxsectors; static u_char xd_override __initdata = 0, xd_type __initdata = 0; static u_short xd_iobase = 0x320; @@ -244,14 +241,11 @@ static void __init xd_geninit (void) /* xd_maxsectors depends on controller - so set after detection */ blk_queue_max_sectors(BLK_DEFAULT_QUEUE(MAJOR_NR), xd_maxsectors); - for (i = 0; i < xd_drives; i++) { - xd_valid[i] = 1; + for (i = 0; i < xd_drives; i++) register_disk(&xd_gendisk, mk_kdev(MAJOR_NR,i<<6), 1<<6, &xd_fops, xd_info[i].heads * xd_info[i].cylinders * xd_info[i].sectors); - } - xd_gendisk.nr_real = xd_drives; } @@ -259,17 +253,9 @@ static void __init xd_geninit (void) static int xd_open (struct inode *inode,struct file *file) { int dev = DEVICE_NR(inode->i_rdev); - - if (dev < xd_drives) { - while (!xd_valid[dev]) - sleep_on(&xd_wait_open); - - xd_access[dev]++; - - return (0); - } - - return -ENXIO; + if (dev >= xd_drives) + return -ENXIO; + return 0; } /* do_xd_request: handle an incoming request */ @@ -329,7 +315,7 @@ static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg) g.heads = xd_info[dev].heads; g.sectors = xd_info[dev].sectors; g.cylinders = xd_info[dev].cylinders; - g.start = get_start_sect(inode->i_rdev); + g.start = get_start_sect(inode->i_bdev); return copy_to_user(geometry, &g, sizeof g) ? -EFAULT : 0; } case HDIO_SET_DMA: @@ -351,50 +337,28 @@ static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg) return -EACCES; return xd_reread_partitions(inode->i_rdev); - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKFLSBUF: - case BLKROSET: - case BLKROGET: - case BLKPG: - return blk_ioctl(inode->i_bdev, cmd, arg); - default: return -EINVAL; } } -/* xd_release: release the device */ -static int xd_release (struct inode *inode, struct file *file) -{ - int target = DEVICE_NR(inode->i_rdev); - if (target < xd_drives) - xd_access[target]--; - return 0; -} - /* xd_reread_partitions: rereads the partition table from a drive */ static int xd_reread_partitions(kdev_t dev) { - int target; - int res; + int target = DEVICE_NR(dev); + kdev_t device = mk_kdev(MAJOR_NR, target << 6); + int res = dev_lock_part(device); - target = DEVICE_NR(dev); - - cli(); - xd_valid[target] = (xd_access[target] != 1); - sti(); - if (xd_valid[target]) - return -EBUSY; + if (res < 0) + return 0; - res = wipe_partitions(dev); + res = wipe_partitions(device); if (!res) - grok_partitions(dev, xd_info[target].heads + grok_partitions(device, xd_info[target].heads * xd_info[target].cylinders * xd_info[target].sectors); - xd_valid[target] = 1; - wake_up(&xd_wait_open); + dev_unlock_part(device); return res; } diff --git a/drivers/block/xd.h b/drivers/block/xd.h index 7babb59b96d0..3df2d4e98410 100644 --- a/drivers/block/xd.h +++ b/drivers/block/xd.h @@ -113,7 +113,6 @@ static void xd_geninit (void); static int xd_open (struct inode *inode,struct file *file); static void do_xd_request (request_queue_t * q); static int xd_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg); -static int xd_release (struct inode *inode,struct file *file); static int xd_reread_partitions (kdev_t dev); static int xd_readwrite (u_char operation,u_char drive,char *buffer,u_int block,u_int count); static void xd_recalibrate (u_char drive); diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 494aa03dc273..7b6dbbee9d95 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -1732,11 +1732,6 @@ int cdrom_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, because they fill up the sys log when CD players poll the drive. */ switch (cmd) { - case BLKROSET: - case BLKROGET: - case BLKFLSBUF: - case BLKSSZGET: - return blk_ioctl(ip->i_bdev, cmd, arg); case CDROMSUBCHNL: { struct cdrom_subchnl q; u_char requested, back; diff --git a/drivers/char/Config.in b/drivers/char/Config.in index ccce94f8fd26..a2489a9daf9b 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -8,25 +8,6 @@ bool 'Virtual terminal' CONFIG_VT if [ "$CONFIG_VT" = "y" ]; then bool ' Support for console on virtual terminal' CONFIG_VT_CONSOLE fi -tristate 'Standard/generic (8250/16550 and compatible UARTs) serial support' CONFIG_SERIAL -if [ "$CONFIG_SERIAL" = "y" ]; then - bool ' Support for console on serial port' CONFIG_SERIAL_CONSOLE - if [ "$CONFIG_ARCH_ACORN" = "y" ]; then - tristate ' Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL - tristate ' Dual serial port support' CONFIG_DUALSP_SERIAL - fi -fi -if [ "$CONFIG_ACPI" = "y" -a "$CONFIG_IA64" = "y" ]; then - bool ' Support for serial ports defined by ACPI tables' CONFIG_SERIAL_ACPI -fi -dep_mbool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED $CONFIG_SERIAL -if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then - bool ' Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS - bool ' Support for sharing serial interrupts' CONFIG_SERIAL_SHARE_IRQ - bool ' Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_DETECT_IRQ - bool ' Support special multiport boards' CONFIG_SERIAL_MULTIPORT - bool ' Support the Bell Technologies HUB6 card' CONFIG_HUB6 -fi bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then tristate ' Computone IntelliPort Plus serial support' CONFIG_COMPUTONE @@ -85,15 +66,9 @@ fi if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_ZORRO" = "y" ]; then tristate 'Commodore A2232 serial support (EXPERIMENTAL)' CONFIG_A2232 fi -if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then - bool 'DC21285 serial port support' CONFIG_SERIAL_21285 - if [ "$CONFIG_SERIAL_21285" = "y" ]; then - if [ "$CONFIG_OBSOLETE" = "y" ]; then - bool ' Use /dev/ttyS0 device (OBSOLETE)' CONFIG_SERIAL_21285_OLD - fi - bool ' Console on DC21285 serial port' CONFIG_SERIAL_21285_CONSOLE - fi -fi + +source drivers/serial/Config.in + bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 07ed55ac10ad..650d9b6eb6de 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -13,20 +13,18 @@ obj-y += mem.o tty_io.o n_tty.o tty_ioctl.o raw.o pty.o misc.o random.o # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. export-objs := busmouse.o console.o keyboard.o sysrq.o \ - misc.o pty.o random.o selection.o serial.o \ + misc.o pty.o random.o selection.o \ sonypi.o tty_io.o tty_ioctl.o generic_serial.o rtc.o \ ip2main.o KEYMAP =defkeymap.o KEYBD =pc_keyb.o CONSOLE =console.o -SERIAL =serial.o ifeq ($(ARCH),s390) KEYMAP = KEYBD = CONSOLE = - SERIAL = endif ifeq ($(ARCH),mips) @@ -39,7 +37,6 @@ ifeq ($(ARCH),s390x) KEYMAP = KEYBD = CONSOLE = - SERIAL = endif ifeq ($(ARCH),m68k) @@ -48,7 +45,6 @@ ifeq ($(ARCH),m68k) else KEYBD = endif - SERIAL = endif ifeq ($(ARCH),arm) @@ -96,15 +92,6 @@ endif ifeq ($(CONFIG_BAGET_MIPS),y) KEYBD = - SERIAL = -endif - -ifeq ($(CONFIG_NINO),y) - SERIAL = -endif - -ifneq ($(CONFIG_SUN_SERIAL),) - SERIAL = endif ifeq ($(CONFIG_QTRONIX_KEYBOARD),y) @@ -113,11 +100,7 @@ ifeq ($(CONFIG_QTRONIX_KEYBOARD),y) endif obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o -obj-$(CONFIG_SERIAL) += $(SERIAL) -obj-$(CONFIG_SERIAL_ACPI) += acpi_serial.o -obj-$(CONFIG_SERIAL_21285) += serial_21285.o -obj-$(CONFIG_SERIAL_SA1100) += serial_sa1100.o -obj-$(CONFIG_SERIAL_AMBA) += serial_amba.o +#obj-$(CONFIG_SERIAL) += $(SERIAL) # Fix for decserial.o ifndef CONFIG_SUN_KEYBOARD obj-$(CONFIG_VT) += keyboard.o $(KEYMAP) $(KEYBD) diff --git a/drivers/char/acpi_serial.c b/drivers/char/acpi_serial.c deleted file mode 100644 index f0c7f188299f..000000000000 --- a/drivers/char/acpi_serial.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * linux/drivers/char/acpi_serial.c - * - * Copyright (C) 2000 Hewlett-Packard Co. - * Copyright (C) 2000 Khalid Aziz <khalid_aziz@hp.com> - * - * Detect and initialize the headless console serial port defined in - * SPCR table and debug serial port defined in DBGP table - * - */ -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/pci.h> -#include <linux/pm.h> -#include <linux/acpi.h> -#include <linux/init.h> -#include <linux/serial.h> -#include <asm/serial.h> -#include <asm/io.h> -#include <linux/acpi_serial.h> -/*#include <asm/acpi-ext.h>*/ - -#undef SERIAL_DEBUG_ACPI - -/* - * Query ACPI tables for a debug and a headless console serial - * port. If found, add them to rs_table[]. A pointer to either SPCR - * or DBGP table is passed as parameter. This function should be called - * before serial_console_init() is called to make sure the SPCR serial - * console will be available for use. IA-64 kernel calls this function - * from within acpi.c when it encounters SPCR or DBGP tables as it parses - * the ACPI 2.0 tables during bootup. - * - */ -void __init setup_serial_acpi(void *tablep) -{ - acpi_ser_t *acpi_ser_p; - struct serial_struct serial_req; - unsigned long iobase; - int global_sys_irq; - -#ifdef SERIAL_DEBUG_ACPI - printk("Entering setup_serial_acpi()\n"); -#endif - - /* Now get the table */ - if (tablep == NULL) { - return; - } - - acpi_ser_p = (acpi_ser_t *)tablep; - - /* - * Perform a sanity check on the table. Table should have a - * signature of "SPCR" or "DBGP" and it should be atleast 52 bytes - * long. - */ - if ((strncmp(acpi_ser_p->signature, ACPI_SPCRT_SIGNATURE, - ACPI_SIG_LEN) != 0) && - (strncmp(acpi_ser_p->signature, ACPI_DBGPT_SIGNATURE, - ACPI_SIG_LEN) != 0)) { - return; - } - if (acpi_ser_p->length < 52) { - return; - } - - iobase = (((u64) acpi_ser_p->base_addr.addrh) << 32) | acpi_ser_p->base_addr.addrl; - global_sys_irq = (acpi_ser_p->global_int[3] << 24) | - (acpi_ser_p->global_int[2] << 16) | - (acpi_ser_p->global_int[1] << 8) | - acpi_ser_p->global_int[0]; - -#ifdef SERIAL_DEBUG_ACPI - printk("setup_serial_acpi(): table pointer = 0x%p\n", acpi_ser_p); - printk(" sig = '%c%c%c%c'\n", - acpi_ser_p->signature[0], - acpi_ser_p->signature[1], - acpi_ser_p->signature[2], - acpi_ser_p->signature[3]); - printk(" length = %d\n", acpi_ser_p->length); - printk(" Rev = %d\n", acpi_ser_p->rev); - printk(" Interface type = %d\n", acpi_ser_p->intfc_type); - printk(" Base address = 0x%lX\n", iobase); - printk(" IRQ = %d\n", acpi_ser_p->irq); - printk(" Global System Int = %d\n", global_sys_irq); - printk(" Baud rate = "); - switch (acpi_ser_p->baud) { - case ACPI_SERIAL_BAUD_9600: - printk("9600\n"); - break; - - case ACPI_SERIAL_BAUD_19200: - printk("19200\n"); - break; - - case ACPI_SERIAL_BAUD_57600: - printk("57600\n"); - break; - - case ACPI_SERIAL_BAUD_115200: - printk("115200\n"); - break; - - default: - printk("Huh (%d)\n", acpi_ser_p->baud); - break; - - } - if (acpi_ser_p->base_addr.space_id == ACPI_SERIAL_PCICONF_SPACE) { - printk(" PCI serial port:\n"); - printk(" Bus %d, Device %d, Vendor ID 0x%x, Dev ID 0x%x\n", - acpi_ser_p->pci_bus, acpi_ser_p->pci_dev, - acpi_ser_p->pci_vendor_id, acpi_ser_p->pci_dev_id); - } -#endif - - /* - * Now build a serial_req structure to update the entry in - * rs_table for the headless console port. - */ - switch (acpi_ser_p->intfc_type) { - case ACPI_SERIAL_INTFC_16550: - serial_req.type = PORT_16550; - serial_req.baud_base = BASE_BAUD; - break; - - case ACPI_SERIAL_INTFC_16450: - serial_req.type = PORT_16450; - serial_req.baud_base = BASE_BAUD; - break; - - default: - serial_req.type = PORT_UNKNOWN; - break; - } - if (strncmp(acpi_ser_p->signature, ACPI_SPCRT_SIGNATURE, - ACPI_SIG_LEN) == 0) { - serial_req.line = ACPI_SERIAL_CONSOLE_PORT; - } - else if (strncmp(acpi_ser_p->signature, ACPI_DBGPT_SIGNATURE, - ACPI_SIG_LEN) == 0) { - serial_req.line = ACPI_SERIAL_DEBUG_PORT; - } - /* - * Check if this is an I/O mapped address or a memory mapped address - */ - if (acpi_ser_p->base_addr.space_id == ACPI_SERIAL_MEM_SPACE) { - serial_req.port = 0; - serial_req.port_high = 0; - serial_req.iomem_base = (void *)ioremap(iobase, 64); - serial_req.io_type = SERIAL_IO_MEM; - } - else if (acpi_ser_p->base_addr.space_id == ACPI_SERIAL_IO_SPACE) { - serial_req.port = (unsigned long) iobase & 0xffffffff; - serial_req.port_high = (unsigned long)(((u64)iobase) >> 32); - serial_req.iomem_base = NULL; - serial_req.io_type = SERIAL_IO_PORT; - } - else if (acpi_ser_p->base_addr.space_id == ACPI_SERIAL_PCICONF_SPACE) { - printk("WARNING: No support for PCI serial console\n"); - return; - } - - /* - * If the table does not have IRQ information, use 0 for IRQ. - * This will force rs_init() to probe for IRQ. - */ - if (acpi_ser_p->length < 53) { - serial_req.irq = 0; - } - else { - serial_req.flags = ASYNC_SKIP_TEST | ASYNC_BOOT_AUTOCONF | - ASYNC_AUTO_IRQ; - if (acpi_ser_p->int_type & - (ACPI_SERIAL_INT_APIC | ACPI_SERIAL_INT_SAPIC)) { - serial_req.irq = global_sys_irq; - } - else if (acpi_ser_p->int_type & ACPI_SERIAL_INT_PCAT) { - serial_req.irq = acpi_ser_p->irq; - } - else { - /* - * IRQ type not being set would mean UART will - * run in polling mode. Do not probe for IRQ in - * that case. - */ - serial_req.flags = ASYNC_SKIP_TEST|ASYNC_BOOT_AUTOCONF; - } - } - - serial_req.xmit_fifo_size = serial_req.custom_divisor = 0; - serial_req.close_delay = serial_req.hub6 = serial_req.closing_wait = 0; - serial_req.iomem_reg_shift = 0; - if (early_serial_setup(&serial_req) < 0) { - printk("early_serial_setup() for ACPI serial console port failed\n"); - return; - } - -#ifdef SERIAL_DEBUG_ACPI - printk("Leaving setup_serial_acpi()\n"); -#endif -} diff --git a/drivers/char/agp/Config.help b/drivers/char/agp/Config.help new file mode 100644 index 000000000000..b6083743dad6 --- /dev/null +++ b/drivers/char/agp/Config.help @@ -0,0 +1,88 @@ +CONFIG_AGP + AGP (Accelerated Graphics Port) is a bus system mainly used to + connect graphics cards to the rest of the system. + + If you have an AGP system and you say Y here, it will be possible to + use the AGP features of your 3D rendering video card. This code acts + as a sort of "AGP driver" for the motherboard's chipset. + + If you need more texture memory than you can get with the AGP GART + (theoretically up to 256 MB, but in practice usually 64 or 128 MB + due to kernel allocation issues), you could use PCI accesses + and have up to a couple gigs of texture space. + + Note that this is the only means to have XFree4/GLX use + write-combining with MTRR support on the AGP bus. Without it, OpenGL + direct rendering will be a lot slower but still faster than PIO. + + You should say Y here if you use XFree86 3.3.6 or 4.x and want to + use GLX or DRI. If unsure, say N. + + This driver is available as a module. If you want to compile it as + a module, say M here and read <file:Documentation/modules.txt>. The + module will be called agpgart.o. + +CONFIG_AGP_INTEL + This option gives you AGP support for the GLX component of the + XFree86 4.x on Intel 440LX/BX/GX, 815, 820, 830, 840, 845, 850 and 860 chipsets. + + You should say Y here if you use XFree86 3.3.6 or 4.x and want to + use GLX or DRI. If unsure, say N. + +CONFIG_AGP_I810 + This option gives you AGP support for the Xserver on the Intel 810 + 815 and 830m chipset boards for their on-board integrated graphics. This + is required to do any useful video modes with these boards. + +CONFIG_AGP_I460 + This option gives you AGP GART support for the Intel 460GX chipset + for IA64 processors. + +CONFIG_AGP_VIA + This option gives you AGP support for the GLX component of the + XFree86 4.x on VIA MPV3/Apollo Pro chipsets. + + You should say Y here if you use XFree86 3.3.6 or 4.x and want to + use GLX or DRI. If unsure, say N. + +CONFIG_AGP_AMD + This option gives you AGP support for the GLX component of the + XFree86 4.x on AMD Irongate, 761, and 762 chipsets. + + You should say Y here if you use XFree86 3.3.6 or 4.x and want to + use GLX or DRI. If unsure, say N. + +CONFIG_AGP_SIS + This option gives you AGP support for the GLX component of the "soon + to be released" XFree86 4.x on Silicon Integrated Systems [SiS] + chipsets. + + Note that 5591/5592 AGP chipsets are NOT supported. + + You should say Y here if you use XFree86 3.3.6 or 4.x and want to + use GLX or DRI. If unsure, say N. + +CONFIG_AGP_SWORKS + Say Y here to support the Serverworks AGP card. See + <http://www.serverworks.com/> for product descriptions and images. + +CONFIG_AGP_ALI + This option gives you AGP support for the GLX component of the + XFree86 4.x on the following ALi chipsets. The supported chipsets + include M1541, M1621, M1631, M1632, M1641,M1647,and M1651. + For the ALi-chipset question, ALi suggests you refer to + <http://www.ali.com.tw/eng/support/index.shtml>. + + The M1541 chipset can do AGP 1x and 2x, but note that there is an + acknowledged incompatibility with Matrox G200 cards. Due to + timing issues, this chipset cannot do AGP 2x with the G200. + This is a hardware limitation. AGP 1x seems to be fine, though. + + You should say Y here if you use XFree86 3.3.6 or 4.x and want to + use GLX or DRI. If unsure, say N. + +CONFIG_AGP_HP_ZX1 + This option gives you AGP GART support for the HP ZX1 chipset + for IA64 processors. + + diff --git a/drivers/char/agp/Config.in b/drivers/char/agp/Config.in new file mode 100644 index 000000000000..91c2fbdf5e4a --- /dev/null +++ b/drivers/char/agp/Config.in @@ -0,0 +1,14 @@ +dep_tristate '/dev/agpgart (AGP Support)' CONFIG_AGP $CONFIG_DRM_AGP +if [ "$CONFIG_AGP" != "n" ]; then + bool ' Intel 440LX/BX/GX and I815/I820/I830M/I830MP/I840/I845/I850/I860 support' CONFIG_AGP_INTEL + bool ' Intel I810/I815/I830M (on-board) support' CONFIG_AGP_I810 + bool ' VIA chipset support' CONFIG_AGP_VIA + bool ' AMD Irongate, 761, and 762 support' CONFIG_AGP_AMD + bool ' Generic SiS support' CONFIG_AGP_SIS + bool ' ALI chipset support' CONFIG_AGP_ALI + bool ' Serverworks LE/HE support' CONFIG_AGP_SWORKS + if [ "$CONFIG_IA64" = "y" ]; then + bool ' Intel 460GX support' CONFIG_AGP_I460 + bool ' HP ZX1 AGP support' CONFIG_AGP_HP_ZX1 + fi +fi diff --git a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile index 8c78cba4f66e..940b293804a8 100644 --- a/drivers/char/agp/Makefile +++ b/drivers/char/agp/Makefile @@ -3,9 +3,20 @@ # space ioctl interface to use agp memory. It also adds a kernel interface # that other drivers could use to manipulate agp memory. -export-objs := agpgart_be.o +export-objs := agp.o -agpgart-objs := agpgart_fe.o agpgart_be.o +agpgart-y := agp.o frontend.o + +agpgart-$(CONFIG_AGP_INTEL) += i8x0-agp.o +agpgart-$(CONFIG_AGP_I810) += i810-agp.o +agpgart-$(CONFIG_AGP_VIA) += via-agp.o +agpgart-$(CONFIG_AGP_AMD) += amd-agp.o +agpgart-$(CONFIG_AGP_SIS) += sis-agp.o +agpgart-$(CONFIG_AGP_ALI) += ali-agp.o +agpgart-$(CONFIG_AGP_SWORKS) += sworks-agp.o +agpgart-$(CONFIG_AGP_I460) += i460-agp.o +agpgart-$(CONFIG_AGP_HP_ZX1) += hp-agp.o +agpgart-objs := $(agpgart-y) obj-$(CONFIG_AGP) += agpgart.o diff --git a/drivers/char/agp/agp.c b/drivers/char/agp/agp.c new file mode 100644 index 000000000000..ff0564a4faed --- /dev/null +++ b/drivers/char/agp/agp.c @@ -0,0 +1,1662 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * TODO: + * - Allocate more than order 0 pages to avoid too much linear map splitting. + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/pagemap.h> +#include <linux/miscdevice.h> +#include <linux/pm.h> +#include <linux/agp_backend.h> +#include "agp.h" + +MODULE_AUTHOR("Jeff Hartmann <jhartmann@precisioninsight.com>"); +MODULE_PARM(agp_try_unsupported, "1i"); +MODULE_LICENSE("GPL and additional rights"); +EXPORT_SYMBOL(agp_free_memory); +EXPORT_SYMBOL(agp_allocate_memory); +EXPORT_SYMBOL(agp_copy_info); +EXPORT_SYMBOL(agp_bind_memory); +EXPORT_SYMBOL(agp_unbind_memory); +EXPORT_SYMBOL(agp_enable); +EXPORT_SYMBOL(agp_backend_acquire); +EXPORT_SYMBOL(agp_backend_release); + +struct agp_bridge_data agp_bridge = { type: NOT_SUPPORTED }; +static int agp_try_unsupported __initdata = 0; + +int agp_backend_acquire(void) +{ + if (agp_bridge.type == NOT_SUPPORTED) + return -EINVAL; + + atomic_inc(&agp_bridge.agp_in_use); + + if (atomic_read(&agp_bridge.agp_in_use) != 1) { + atomic_dec(&agp_bridge.agp_in_use); + return -EBUSY; + } + MOD_INC_USE_COUNT; + return 0; +} + +void agp_backend_release(void) +{ + if (agp_bridge.type == NOT_SUPPORTED) + return; + + atomic_dec(&agp_bridge.agp_in_use); + MOD_DEC_USE_COUNT; +} + +/* + * Generic routines for handling agp_memory structures - + * They use the basic page allocation routines to do the + * brunt of the work. + */ + + +void agp_free_key(int key) +{ + + if (key < 0) + return; + + if (key < MAXKEY) + clear_bit(key, agp_bridge.key_list); +} + +static int agp_get_key(void) +{ + int bit; + + bit = find_first_zero_bit(agp_bridge.key_list, MAXKEY); + if (bit < MAXKEY) { + set_bit(bit, agp_bridge.key_list); + return bit; + } + return -1; +} + +agp_memory *agp_create_memory(int scratch_pages) +{ + agp_memory *new; + + new = kmalloc(sizeof(agp_memory), GFP_KERNEL); + + if (new == NULL) + return NULL; + + memset(new, 0, sizeof(agp_memory)); + new->key = agp_get_key(); + + if (new->key < 0) { + kfree(new); + return NULL; + } + new->memory = vmalloc(PAGE_SIZE * scratch_pages); + + if (new->memory == NULL) { + agp_free_key(new->key); + kfree(new); + return NULL; + } + new->num_scratch_pages = scratch_pages; + return new; +} + +void agp_free_memory(agp_memory * curr) +{ + int i; + + if ((agp_bridge.type == NOT_SUPPORTED) || (curr == NULL)) + return; + + if (curr->is_bound == TRUE) + agp_unbind_memory(curr); + + if (curr->type != 0) { + agp_bridge.free_by_type(curr); + return; + } + if (curr->page_count != 0) { + for (i = 0; i < curr->page_count; i++) { + curr->memory[i] &= ~(0x00000fff); + agp_bridge.agp_destroy_page(phys_to_virt(curr->memory[i])); + } + } + agp_free_key(curr->key); + vfree(curr->memory); + kfree(curr); + MOD_DEC_USE_COUNT; +} + +#define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(unsigned long)) + +agp_memory *agp_allocate_memory(size_t page_count, u32 type) +{ + int scratch_pages; + agp_memory *new; + int i; + + if (agp_bridge.type == NOT_SUPPORTED) + return NULL; + + if ((atomic_read(&agp_bridge.current_memory_agp) + page_count) > + agp_bridge.max_memory_agp) { + return NULL; + } + + if (type != 0) { + new = agp_bridge.alloc_by_type(page_count, type); + return new; + } + /* We always increase the module count, since free auto-decrements + * it + */ + + MOD_INC_USE_COUNT; + + scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; + + new = agp_create_memory(scratch_pages); + + if (new == NULL) { + MOD_DEC_USE_COUNT; + return NULL; + } + + for (i = 0; i < page_count; i++) { + void *addr = agp_bridge.agp_alloc_page(); + + if (addr == NULL) { + /* Free this structure */ + agp_free_memory(new); + return NULL; + } + new->memory[i] = agp_bridge.mask_memory(virt_to_phys(addr), type); + new->page_count++; + } + + flush_agp_mappings(); + + return new; +} + +/* End - Generic routines for handling agp_memory structures */ + +static int agp_return_size(void) +{ + int current_size; + void *temp; + + temp = agp_bridge.current_size; + + switch (agp_bridge.size_type) { + case U8_APER_SIZE: + current_size = A_SIZE_8(temp)->size; + break; + case U16_APER_SIZE: + current_size = A_SIZE_16(temp)->size; + break; + case U32_APER_SIZE: + current_size = A_SIZE_32(temp)->size; + break; + case LVL2_APER_SIZE: + current_size = A_SIZE_LVL2(temp)->size; + break; + case FIXED_APER_SIZE: + current_size = A_SIZE_FIX(temp)->size; + break; + default: + current_size = 0; + break; + } + + return current_size; +} + +/* Routine to copy over information structure */ + +void agp_copy_info(agp_kern_info * info) +{ + unsigned long page_mask = 0; + int i; + + memset(info, 0, sizeof(agp_kern_info)); + if (agp_bridge.type == NOT_SUPPORTED) { + info->chipset = agp_bridge.type; + return; + } + info->version.major = agp_bridge.version->major; + info->version.minor = agp_bridge.version->minor; + info->device = agp_bridge.dev; + info->chipset = agp_bridge.type; + info->mode = agp_bridge.mode; + info->aper_base = agp_bridge.gart_bus_addr; + info->aper_size = agp_return_size(); + info->max_memory = agp_bridge.max_memory_agp; + info->current_memory = atomic_read(&agp_bridge.current_memory_agp); + info->cant_use_aperture = agp_bridge.cant_use_aperture; + + for(i = 0; i < agp_bridge.num_of_masks; i++) + page_mask |= agp_bridge.mask_memory(page_mask, i); + + info->page_mask = ~page_mask; +} + +/* End - Routine to copy over information structure */ + +/* + * Routines for handling swapping of agp_memory into the GATT - + * These routines take agp_memory and insert them into the GATT. + * They call device specific routines to actually write to the GATT. + */ + +int agp_bind_memory(agp_memory * curr, off_t pg_start) +{ + int ret_val; + + if ((agp_bridge.type == NOT_SUPPORTED) || + (curr == NULL) || (curr->is_bound == TRUE)) { + return -EINVAL; + } + if (curr->is_flushed == FALSE) { + CACHE_FLUSH(); + curr->is_flushed = TRUE; + } + ret_val = agp_bridge.insert_memory(curr, pg_start, curr->type); + + if (ret_val != 0) + return ret_val; + + curr->is_bound = TRUE; + curr->pg_start = pg_start; + return 0; +} + +int agp_unbind_memory(agp_memory * curr) +{ + int ret_val; + + if ((agp_bridge.type == NOT_SUPPORTED) || (curr == NULL)) + return -EINVAL; + + if (curr->is_bound != TRUE) + return -EINVAL; + + ret_val = agp_bridge.remove_memory(curr, curr->pg_start, curr->type); + + if (ret_val != 0) + return ret_val; + + curr->is_bound = FALSE; + curr->pg_start = 0; + return 0; +} + +/* End - Routines for handling swapping of agp_memory into the GATT */ + +/* + * Driver routines - start + * Currently this module supports the following chipsets: + * i810, i815, 440lx, 440bx, 440gx, i830, i840, i845, i850, i860, via vp3, + * via mvp3, via kx133, via kt133, amd irongate, amd 761, amd 762, ALi M1541, + * and generic support for the SiS chipsets. + */ + +/* Generic Agp routines - Start */ + +void agp_generic_agp_enable(u32 mode) +{ + struct pci_dev *device = NULL; + u32 command, scratch; + u8 cap_ptr; + + pci_read_config_dword(agp_bridge.dev, agp_bridge.capndx + 4, &command); + + /* + * PASS1: go throu all devices that claim to be + * AGP devices and collect their data. + */ + + + pci_for_each_dev(device) { + cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); + if (cap_ptr != 0x00) { + /* + * Ok, here we have a AGP device. Disable impossible + * settings, and adjust the readqueue to the minimum. + */ + + pci_read_config_dword(device, cap_ptr + 4, &scratch); + + /* adjust RQ depth */ + command = ((command & ~0xff000000) | + min_t(u32, (mode & 0xff000000), + min_t(u32, (command & 0xff000000), + (scratch & 0xff000000)))); + + /* disable SBA if it's not supported */ + if (!((command & 0x00000200) && + (scratch & 0x00000200) && + (mode & 0x00000200))) + command &= ~0x00000200; + + /* disable FW if it's not supported */ + if (!((command & 0x00000010) && + (scratch & 0x00000010) && + (mode & 0x00000010))) + command &= ~0x00000010; + + if (!((command & 4) && + (scratch & 4) && + (mode & 4))) + command &= ~0x00000004; + + if (!((command & 2) && + (scratch & 2) && + (mode & 2))) + command &= ~0x00000002; + + if (!((command & 1) && + (scratch & 1) && + (mode & 1))) + command &= ~0x00000001; + } + } + /* + * PASS2: Figure out the 4X/2X/1X setting and enable the + * target (our motherboard chipset). + */ + + if (command & 4) + command &= ~3; /* 4X */ + + if (command & 2) + command &= ~5; /* 2X */ + + if (command & 1) + command &= ~6; /* 1X */ + + command |= 0x00000100; + + pci_write_config_dword(agp_bridge.dev, + agp_bridge.capndx + 8, + command); + + /* + * PASS3: Go throu all AGP devices and update the + * command registers. + */ + + pci_for_each_dev(device) { + cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); + if (cap_ptr != 0x00) + pci_write_config_dword(device, cap_ptr + 8, command); + } +} + +int agp_generic_create_gatt_table(void) +{ + char *table; + char *table_end; + int size; + int page_order; + int num_entries; + int i; + void *temp; + struct page *page; + + /* The generic routines can't handle 2 level gatt's */ + if (agp_bridge.size_type == LVL2_APER_SIZE) { + return -EINVAL; + } + + table = NULL; + i = agp_bridge.aperture_size_idx; + temp = agp_bridge.current_size; + size = page_order = num_entries = 0; + + if (agp_bridge.size_type != FIXED_APER_SIZE) { + do { + switch (agp_bridge.size_type) { + case U8_APER_SIZE: + size = A_SIZE_8(temp)->size; + page_order = + A_SIZE_8(temp)->page_order; + num_entries = + A_SIZE_8(temp)->num_entries; + break; + case U16_APER_SIZE: + size = A_SIZE_16(temp)->size; + page_order = A_SIZE_16(temp)->page_order; + num_entries = A_SIZE_16(temp)->num_entries; + break; + case U32_APER_SIZE: + size = A_SIZE_32(temp)->size; + page_order = A_SIZE_32(temp)->page_order; + num_entries = A_SIZE_32(temp)->num_entries; + break; + /* This case will never really happen. */ + case FIXED_APER_SIZE: + case LVL2_APER_SIZE: + default: + size = page_order = num_entries = 0; + break; + } + + table = (char *) __get_free_pages(GFP_KERNEL, + page_order); + + if (table == NULL) { + i++; + switch (agp_bridge.size_type) { + case U8_APER_SIZE: + agp_bridge.current_size = A_IDX8(); + break; + case U16_APER_SIZE: + agp_bridge.current_size = A_IDX16(); + break; + case U32_APER_SIZE: + agp_bridge.current_size = A_IDX32(); + break; + /* This case will never really + * happen. + */ + case FIXED_APER_SIZE: + case LVL2_APER_SIZE: + default: + agp_bridge.current_size = + agp_bridge.current_size; + break; + } + temp = agp_bridge.current_size; + } else { + agp_bridge.aperture_size_idx = i; + } + } while ((table == NULL) && + (i < agp_bridge.num_aperture_sizes)); + } else { + size = ((struct aper_size_info_fixed *) temp)->size; + page_order = ((struct aper_size_info_fixed *) temp)->page_order; + num_entries = ((struct aper_size_info_fixed *) temp)->num_entries; + table = (char *) __get_free_pages(GFP_KERNEL, page_order); + } + + if (table == NULL) + return -ENOMEM; + + table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); + + for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) + SetPageReserved(page); + + agp_bridge.gatt_table_real = (unsigned long *) table; + CACHE_FLUSH(); + agp_bridge.gatt_table = ioremap_nocache(virt_to_phys(table), + (PAGE_SIZE * (1 << page_order))); + CACHE_FLUSH(); + + if (agp_bridge.gatt_table == NULL) { + for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) + ClearPageReserved(page); + + free_pages((unsigned long) table, page_order); + + return -ENOMEM; + } + agp_bridge.gatt_bus_addr = virt_to_phys(agp_bridge.gatt_table_real); + + for (i = 0; i < num_entries; i++) + agp_bridge.gatt_table[i] = (unsigned long) agp_bridge.scratch_page; + + return 0; +} + +int agp_generic_suspend(void) +{ + return 0; +} + +void agp_generic_resume(void) +{ + return; +} + +int agp_generic_free_gatt_table(void) +{ + int page_order; + char *table, *table_end; + void *temp; + struct page *page; + + temp = agp_bridge.current_size; + + switch (agp_bridge.size_type) { + case U8_APER_SIZE: + page_order = A_SIZE_8(temp)->page_order; + break; + case U16_APER_SIZE: + page_order = A_SIZE_16(temp)->page_order; + break; + case U32_APER_SIZE: + page_order = A_SIZE_32(temp)->page_order; + break; + case FIXED_APER_SIZE: + page_order = A_SIZE_FIX(temp)->page_order; + break; + case LVL2_APER_SIZE: + /* The generic routines can't deal with 2 level gatt's */ + return -EINVAL; + break; + default: + page_order = 0; + break; + } + + /* Do not worry about freeing memory, because if this is + * called, then all agp memory is deallocated and removed + * from the table. + */ + + iounmap(agp_bridge.gatt_table); + table = (char *) agp_bridge.gatt_table_real; + table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); + + for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) + ClearPageReserved(page); + + free_pages((unsigned long) agp_bridge.gatt_table_real, page_order); + return 0; +} + +int agp_generic_insert_memory(agp_memory * mem, off_t pg_start, int type) +{ + int i, j, num_entries; + void *temp; + + temp = agp_bridge.current_size; + + switch (agp_bridge.size_type) { + case U8_APER_SIZE: + num_entries = A_SIZE_8(temp)->num_entries; + break; + case U16_APER_SIZE: + num_entries = A_SIZE_16(temp)->num_entries; + break; + case U32_APER_SIZE: + num_entries = A_SIZE_32(temp)->num_entries; + break; + case FIXED_APER_SIZE: + num_entries = A_SIZE_FIX(temp)->num_entries; + break; + case LVL2_APER_SIZE: + /* The generic routines can't deal with 2 level gatt's */ + return -EINVAL; + break; + default: + num_entries = 0; + break; + } + + if (type != 0 || mem->type != 0) { + /* The generic routines know nothing of memory types */ + return -EINVAL; + } + + if ((pg_start + mem->page_count) > num_entries) + return -EINVAL; + + j = pg_start; + + while (j < (pg_start + mem->page_count)) { + if (!PGE_EMPTY(agp_bridge.gatt_table[j])) { + return -EBUSY; + } + j++; + } + + if (mem->is_flushed == FALSE) { + CACHE_FLUSH(); + mem->is_flushed = TRUE; + } + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) + agp_bridge.gatt_table[j] = mem->memory[i]; + + agp_bridge.tlb_flush(mem); + return 0; +} + +int agp_generic_remove_memory(agp_memory * mem, off_t pg_start, int type) +{ + int i; + + if (type != 0 || mem->type != 0) { + /* The generic routines know nothing of memory types */ + return -EINVAL; + } + for (i = pg_start; i < (mem->page_count + pg_start); i++) { + agp_bridge.gatt_table[i] = + (unsigned long) agp_bridge.scratch_page; + } + + agp_bridge.tlb_flush(mem); + return 0; +} + +agp_memory *agp_generic_alloc_by_type(size_t page_count, int type) +{ + return NULL; +} + +void agp_generic_free_by_type(agp_memory * curr) +{ + if (curr->memory != NULL) + vfree(curr->memory); + + agp_free_key(curr->key); + kfree(curr); +} + +/* + * Basic Page Allocation Routines - + * These routines handle page allocation + * and by default they reserve the allocated + * memory. They also handle incrementing the + * current_memory_agp value, Which is checked + * against a maximum value. + */ + +void *agp_generic_alloc_page(void) +{ + struct page * page; + + page = alloc_page(GFP_KERNEL); + if (page == NULL) + return 0; + + map_page_into_agp(page); + + get_page(page); + SetPageLocked(page); + atomic_inc(&agp_bridge.current_memory_agp); + return page_address(page); +} + +void agp_generic_destroy_page(void *addr) +{ + struct page *page; + + if (addr == NULL) + return; + + page = virt_to_page(addr); + unmap_page_from_agp(page); + put_page(page); + unlock_page(page); + free_page((unsigned long)addr); + atomic_dec(&agp_bridge.current_memory_agp); +} + +/* End Basic Page Allocation Routines */ + +void agp_enable(u32 mode) +{ + if (agp_bridge.type == NOT_SUPPORTED) + return; + agp_bridge.agp_enable(mode); +} + +/* End - Generic Agp routines */ + + +/* per-chipset initialization data. + * note -- all chipsets for a single vendor MUST be grouped together + */ +static struct { + unsigned short device_id; /* first, to make table easier to read */ + unsigned short vendor_id; + enum chipset_type chipset; + const char *vendor_name; + const char *chipset_name; + int (*chipset_setup) (struct pci_dev *pdev); +} agp_bridge_info[] __initdata = { + +#ifdef CONFIG_AGP_ALI + { + device_id: PCI_DEVICE_ID_AL_M1541_0, + vendor_id: PCI_VENDOR_ID_AL, + chipset: ALI_M1541, + vendor_name: "Ali", + chipset_name: "M1541", + chipset_setup: ali_generic_setup, + }, + { + device_id: PCI_DEVICE_ID_AL_M1621_0, + vendor_id: PCI_VENDOR_ID_AL, + chipset: ALI_M1621, + vendor_name: "Ali", + chipset_name: "M1621", + chipset_setup: ali_generic_setup, + }, + { + device_id: PCI_DEVICE_ID_AL_M1631_0, + vendor_id: PCI_VENDOR_ID_AL, + chipset: ALI_M1631, + vendor_name: "Ali", + chipset_name: "M1631", + chipset_setup: ali_generic_setup, + }, + { + device_id: PCI_DEVICE_ID_AL_M1632_0, + vendor_id: PCI_VENDOR_ID_AL, + chipset: ALI_M1632, + vendor_name: "Ali", + chipset_name: "M1632", + chipset_setup: ali_generic_setup, + }, + { + device_id: PCI_DEVICE_ID_AL_M1641_0, + vendor_id: PCI_VENDOR_ID_AL, + chipset: ALI_M1641, + vendor_name: "Ali", + chipset_name: "M1641", + chipset_setup: ali_generic_setup, + }, + { + device_id: PCI_DEVICE_ID_AL_M1644_0, + vendor_id: PCI_VENDOR_ID_AL, + chipset: ALI_M1644, + vendor_name: "Ali", + chipset_name: "M1644", + chipset_setup: ali_generic_setup, + }, + { + device_id: PCI_DEVICE_ID_AL_M1647_0, + vendor_id: PCI_VENDOR_ID_AL, + chipset: ALI_M1647, + vendor_name: "Ali", + chipset_name: "M1647", + chipset_setup: ali_generic_setup, + }, + { + device_id: PCI_DEVICE_ID_AL_M1651_0, + vendor_id: PCI_VENDOR_ID_AL, + chipset: ALI_M1651, + vendor_name: "Ali", + chipset_name: "M1651", + chipset_setup: ali_generic_setup, + }, + { + device_id: 0, + vendor_id: PCI_VENDOR_ID_AL, + chipset: ALI_GENERIC, + vendor_name: "Ali", + chipset_name: "Generic", + chipset_setup: ali_generic_setup, + }, +#endif /* CONFIG_AGP_ALI */ + +#ifdef CONFIG_AGP_AMD + { + device_id: PCI_DEVICE_ID_AMD_IRONGATE_0, + vendor_id: PCI_VENDOR_ID_AMD, + chipset: AMD_IRONGATE, + vendor_name: "AMD", + chipset_name: "Irongate", + chipset_setup: amd_irongate_setup, + }, + { + device_id: PCI_DEVICE_ID_AMD_761_0, + vendor_id: PCI_VENDOR_ID_AMD, + chipset: AMD_761, + vendor_name: "AMD", + chipset_name: "761", + chipset_setup: amd_irongate_setup, + }, + { + device_id: PCI_DEVICE_ID_AMD_762_0, + vendor_id: PCI_VENDOR_ID_AMD, + chipset: AMD_762, + vendor_name: "AMD", + chipset_name: "760MP", + chipset_setup: amd_irongate_setup, + }, + { + device_id: 0, + vendor_id: PCI_VENDOR_ID_AMD, + chipset: AMD_GENERIC, + vendor_name: "AMD", + chipset_name: "Generic", + chipset_setup: amd_irongate_setup, + }, +#endif /* CONFIG_AGP_AMD */ + +#ifdef CONFIG_AGP_INTEL + { + device_id: PCI_DEVICE_ID_INTEL_82443LX_0, + vendor_id: PCI_VENDOR_ID_INTEL, + chipset: INTEL_LX, + vendor_name: "Intel", + chipset_name: "440LX", + chipset_setup: intel_generic_setup + }, + { + device_id: PCI_DEVICE_ID_INTEL_82443BX_0, + vendor_id: PCI_VENDOR_ID_INTEL, + chipset: INTEL_BX, + vendor_name: "Intel", + chipset_name: "440BX", + chipset_setup: intel_generic_setup + }, + { + device_id: PCI_DEVICE_ID_INTEL_82443GX_0, + vendor_id: PCI_VENDOR_ID_INTEL, + chipset: INTEL_GX, + vendor_name: "Intel", + chipset_name: "440GX", + chipset_setup: intel_generic_setup + }, + { + device_id: PCI_DEVICE_ID_INTEL_815_0, + vendor_id: PCI_VENDOR_ID_INTEL, + chipset: INTEL_I815, + vendor_name: "Intel", + chipset_name: "i815", + chipset_setup: intel_815_setup + }, + { + device_id: PCI_DEVICE_ID_INTEL_820_0, + vendor_id: PCI_VENDOR_ID_INTEL, + chipset: INTEL_I820, + vendor_name: "Intel", + chipset_name: "i820", + chipset_setup: intel_820_setup + }, + { + device_id: PCI_DEVICE_ID_INTEL_820_UP_0, + vendor_id: PCI_VENDOR_ID_INTEL, + chipset: INTEL_I820, + vendor_name: "Intel", + chipset_name: "i820", + chipset_setup: intel_820_setup + }, + { + device_id: PCI_DEVICE_ID_INTEL_830_M_0, + vendor_id: PCI_VENDOR_ID_INTEL, + chipset: INTEL_I830_M, + vendor_name: "Intel", + chipset_name: "i830M", + chipset_setup: intel_830mp_setup + }, + { + device_id: PCI_DEVICE_ID_INTEL_845_G_0, + vendor_id: PCI_VENDOR_ID_INTEL, + chipset: INTEL_I845_G, + vendor_name: "Intel", + chipset_name: "i845G", + chipset_setup: intel_830mp_setup + }, + { + device_id: PCI_DEVICE_ID_INTEL_840_0, + vendor_id: PCI_VENDOR_ID_INTEL, + chipset: INTEL_I840, + vendor_name: "Intel", + chipset_name: "i840", + chipset_setup: intel_840_setup + }, + { + device_id: PCI_DEVICE_ID_INTEL_845_0, + vendor_id: PCI_VENDOR_ID_INTEL, + chipset: INTEL_I845, + vendor_name: "Intel", + chipset_name: "i845", + chipset_setup: intel_845_setup + }, + { + device_id: PCI_DEVICE_ID_INTEL_850_0, + vendor_id: PCI_VENDOR_ID_INTEL, + chipset: INTEL_I850, + vendor_name: "Intel", + chipset_name: "i850", + chipset_setup: intel_850_setup + }, + { + device_id: PCI_DEVICE_ID_INTEL_860_0, + vendor_id: PCI_VENDOR_ID_INTEL, + chipset: INTEL_I860, + vendor_name: "Intel", + chipset_name: "i860", + chipset_setup: intel_860_setup + }, + { + device_id: 0, + vendor_id: PCI_VENDOR_ID_INTEL, + chipset: INTEL_GENERIC, + vendor_name: "Intel", + chipset_name: "Generic", + chipset_setup: intel_generic_setup + }, + +#endif /* CONFIG_AGP_INTEL */ + +#ifdef CONFIG_AGP_SIS + { + device_id: PCI_DEVICE_ID_SI_740, + vendor_id: PCI_VENDOR_ID_SI, + chipset: SIS_GENERIC, + vendor_name: "SiS", + chipset_name: "740", + chipset_setup: sis_generic_setup + }, + { + device_id: PCI_DEVICE_ID_SI_650, + vendor_id: PCI_VENDOR_ID_SI, + chipset: SIS_GENERIC, + vendor_name: "SiS", + chipset_name: "650", + chipset_setup: sis_generic_setup + }, + { + device_id: PCI_DEVICE_ID_SI_645, + vendor_id: PCI_VENDOR_ID_SI, + chipset: SIS_GENERIC, + vendor_name: "SiS", + chipset_name: "645", + chipset_setup: sis_generic_setup + }, + { + device_id: PCI_DEVICE_ID_SI_735, + vendor_id: PCI_VENDOR_ID_SI, + chipset: SIS_GENERIC, + vendor_name: "SiS", + chipset_name: "735", + chipset_setup: sis_generic_setup + }, + { + device_id: PCI_DEVICE_ID_SI_745, + vendor_id: PCI_VENDOR_ID_SI, + chipset: SIS_GENERIC, + vendor_name: "SiS", + chipset_name: "745", + chipset_setup: sis_generic_setup + }, + { + device_id: PCI_DEVICE_ID_SI_730, + vendor_id: PCI_VENDOR_ID_SI, + chipset: SIS_GENERIC, + vendor_name: "SiS", + chipset_name: "730", + chipset_setup: sis_generic_setup + }, + { + device_id: PCI_DEVICE_ID_SI_630, + vendor_id: PCI_VENDOR_ID_SI, + chipset: SIS_GENERIC, + vendor_name: "SiS", + chipset_name: "630", + chipset_setup: sis_generic_setup + }, + { + device_id: PCI_DEVICE_ID_SI_540, + vendor_id: PCI_VENDOR_ID_SI, + chipset: SIS_GENERIC, + vendor_name: "SiS", + chipset_name: "540", + chipset_setup: sis_generic_setup + }, + { + device_id: PCI_DEVICE_ID_SI_620, + vendor_id: PCI_VENDOR_ID_SI, + chipset: SIS_GENERIC, + vendor_name: "SiS", + chipset_name: "620", + chipset_setup: sis_generic_setup + }, + { + device_id: PCI_DEVICE_ID_SI_530, + vendor_id: PCI_VENDOR_ID_SI, + chipset: SIS_GENERIC, + vendor_name: "SiS", + chipset_name: "530", + chipset_setup: sis_generic_setup + }, + { + device_id: PCI_DEVICE_ID_SI_550, + vendor_id: PCI_VENDOR_ID_SI, + chipset: SIS_GENERIC, + vendor_name: "SiS", + chipset_name: "550", + chipset_setup: sis_generic_setup + }, + { + device_id: 0, + vendor_id: PCI_VENDOR_ID_SI, + chipset: SIS_GENERIC, + vendor_name: "SiS", + chipset_name: "Generic", + chipset_setup: sis_generic_setup + }, +#endif /* CONFIG_AGP_SIS */ + +#ifdef CONFIG_AGP_VIA + { + device_id: PCI_DEVICE_ID_VIA_8501_0, + vendor_id: PCI_VENDOR_ID_VIA, + chipset: VIA_MVP4, + vendor_name: "Via", + chipset_name: "MVP4", + chipset_setup: via_generic_setup + }, + { + device_id: PCI_DEVICE_ID_VIA_82C597_0, + vendor_id: PCI_VENDOR_ID_VIA, + chipset: VIA_VP3, + vendor_name: "Via", + chipset_name: "VP3", + chipset_setup: via_generic_setup + }, + { + device_id: PCI_DEVICE_ID_VIA_82C598_0, + vendor_id: PCI_VENDOR_ID_VIA, + chipset: VIA_MVP3, + vendor_name: "Via", + chipset_name: "MVP3", + chipset_setup: via_generic_setup + }, + { + device_id: PCI_DEVICE_ID_VIA_82C691_0, + vendor_id: PCI_VENDOR_ID_VIA, + chipset: VIA_APOLLO_PRO, + vendor_name: "Via", + chipset_name: "Apollo Pro", + chipset_setup: via_generic_setup + }, + { + device_id: PCI_DEVICE_ID_VIA_8371_0, + vendor_id: PCI_VENDOR_ID_VIA, + chipset: VIA_APOLLO_KX133, + vendor_name: "Via", + chipset_name: "Apollo Pro KX133", + chipset_setup: via_generic_setup + }, + { + device_id: PCI_DEVICE_ID_VIA_8363_0, + vendor_id: PCI_VENDOR_ID_VIA, + chipset: VIA_APOLLO_KT133, + vendor_name: "Via", + chipset_name: "Apollo Pro KT133", + chipset_setup: via_generic_setup + }, + { + device_id: PCI_DEVICE_ID_VIA_8367_0, + vendor_id: PCI_VENDOR_ID_VIA, + chipset: VIA_APOLLO_KT133, + vendor_name: "Via", + chipset_name: "Apollo Pro KT266", + chipset_setup: via_generic_setup + }, + { + device_id: 0, + vendor_id: PCI_VENDOR_ID_VIA, + chipset: VIA_GENERIC, + vendor_name: "Via", + chipset_name: "Generic", + chipset_setup: via_generic_setup + }, +#endif /* CONFIG_AGP_VIA */ + +#ifdef CONFIG_AGP_HP_ZX1 + { + device_id: PCI_DEVICE_ID_HP_ZX1_LBA, + vendor_id: PCI_VENDOR_ID_HP, + chipset: HP_ZX1, + vendor_name: "HP", + chipset_name: "ZX1", + chipset_setup: hp_zx1_setup + }, +#endif + + { }, /* dummy final entry, always present */ +}; + + +/* scan table above for supported devices */ +static int __init agp_lookup_host_bridge (struct pci_dev *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE (agp_bridge_info); i++) + if (pdev->vendor == agp_bridge_info[i].vendor_id) + break; + + if (i >= ARRAY_SIZE (agp_bridge_info)) { + printk (KERN_DEBUG PFX "unsupported bridge\n"); + return -ENODEV; + } + + while ((i < ARRAY_SIZE (agp_bridge_info)) && + (agp_bridge_info[i].vendor_id == pdev->vendor)) { + if (pdev->device == agp_bridge_info[i].device_id) { +#ifdef CONFIG_AGP_ALI + if (pdev->device == PCI_DEVICE_ID_AL_M1621_0) { + u8 hidden_1621_id; + + pci_read_config_byte(pdev, 0xFB, &hidden_1621_id); + switch (hidden_1621_id) { + case 0x31: + agp_bridge_info[i].chipset_name="M1631"; + break; + case 0x32: + agp_bridge_info[i].chipset_name="M1632"; + break; + case 0x41: + agp_bridge_info[i].chipset_name="M1641"; + break; + case 0x43: + break; + case 0x47: + agp_bridge_info[i].chipset_name="M1647"; + break; + case 0x51: + agp_bridge_info[i].chipset_name="M1651"; + break; + default: + break; + } + } +#endif + + printk (KERN_INFO PFX "Detected %s %s chipset\n", + agp_bridge_info[i].vendor_name, + agp_bridge_info[i].chipset_name); + agp_bridge.type = agp_bridge_info[i].chipset; + return agp_bridge_info[i].chipset_setup (pdev); + } + + i++; + } + + i--; /* point to vendor generic entry (device_id == 0) */ + + /* try init anyway, if user requests it AND + * there is a 'generic' bridge entry for this vendor */ + if (agp_try_unsupported && agp_bridge_info[i].device_id == 0) { + printk(KERN_WARNING PFX "Trying generic %s routines" + " for device id: %04x\n", + agp_bridge_info[i].vendor_name, pdev->device); + agp_bridge.type = agp_bridge_info[i].chipset; + return agp_bridge_info[i].chipset_setup (pdev); + } + + printk(KERN_ERR PFX "Unsupported %s chipset (device id: %04x)," + " you might want to try agp_try_unsupported=1.\n", + agp_bridge_info[i].vendor_name, pdev->device); + return -ENODEV; +} + + +/* Supported Device Scanning routine */ + +static int __init agp_find_supported_device(struct pci_dev *dev) +{ + u8 cap_ptr = 0x00; + + agp_bridge.dev = dev; + + /* Need to test for I810 here */ +#ifdef CONFIG_AGP_I810 + if (dev->vendor == PCI_VENDOR_ID_INTEL) { + struct pci_dev *i810_dev; + + switch (dev->device) { + case PCI_DEVICE_ID_INTEL_810_0: + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_810_1, + NULL); + if (i810_dev == NULL) { + printk(KERN_ERR PFX "Detected an Intel i810," + " but could not find the secondary" + " device.\n"); + return -ENODEV; + } + printk(KERN_INFO PFX "Detected an Intel " + "i810 Chipset.\n"); + agp_bridge.type = INTEL_I810; + return intel_i810_setup (i810_dev); + + case PCI_DEVICE_ID_INTEL_810_DC100_0: + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_810_DC100_1, + NULL); + if (i810_dev == NULL) { + printk(KERN_ERR PFX "Detected an Intel i810 " + "DC100, but could not find the " + "secondary device.\n"); + return -ENODEV; + } + printk(KERN_INFO PFX "Detected an Intel i810 " + "DC100 Chipset.\n"); + agp_bridge.type = INTEL_I810; + return intel_i810_setup(i810_dev); + + case PCI_DEVICE_ID_INTEL_810_E_0: + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_810_E_1, + NULL); + if (i810_dev == NULL) { + printk(KERN_ERR PFX "Detected an Intel i810 E" + ", but could not find the secondary " + "device.\n"); + return -ENODEV; + } + printk(KERN_INFO PFX "Detected an Intel i810 E " + "Chipset.\n"); + agp_bridge.type = INTEL_I810; + return intel_i810_setup(i810_dev); + + case PCI_DEVICE_ID_INTEL_815_0: + /* The i815 can operate either as an i810 style + * integrated device, or as an AGP4X motherboard. + * + * This only addresses the first mode: + */ + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_815_1, + NULL); + if (i810_dev == NULL) { + printk(KERN_ERR PFX "agpgart: Detected an " + "Intel i815, but could not find the" + " secondary device. Assuming a " + "non-integrated video card.\n"); + break; + } + printk(KERN_INFO PFX "agpgart: Detected an Intel i815 " + "Chipset.\n"); + agp_bridge.type = INTEL_I810; + return intel_i810_setup(i810_dev); + + case PCI_DEVICE_ID_INTEL_845_G_0: + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_845_G_1, NULL); + if(i810_dev && PCI_FUNC(i810_dev->devfn) != 0) { + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_845_G_1, i810_dev); + } + + if (i810_dev == NULL) { + /* + * We probably have a I845MP chipset + * with an external graphics + * card. It will be initialized later + */ + agp_bridge.type = INTEL_I845_G; + break; + } + printk(KERN_INFO PFX "Detected an Intel " + "845G Chipset.\n"); + agp_bridge.type = INTEL_I810; + return intel_i830_setup(i810_dev); + + case PCI_DEVICE_ID_INTEL_830_M_0: + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_830_M_1, + NULL); + if(i810_dev && PCI_FUNC(i810_dev->devfn) != 0) { + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_830_M_1, + i810_dev); + } + + if (i810_dev == NULL) { + /* Intel 830MP with external graphic card */ + /* It will be initialized later */ + agp_bridge.type = INTEL_I830_M; + break; + } + printk(KERN_INFO PFX "Detected an Intel " + "830M Chipset.\n"); + agp_bridge.type = INTEL_I810; + return intel_i830_setup(i810_dev); + default: + break; + } + } +#endif /* CONFIG_AGP_I810 */ + +#ifdef CONFIG_AGP_SWORKS + /* Everything is on func 1 here so we are hardcoding function one */ + if (dev->vendor == PCI_VENDOR_ID_SERVERWORKS) { + struct pci_dev *bridge_dev; + + bridge_dev = pci_find_slot ((unsigned int)dev->bus->number, + PCI_DEVFN(0, 1)); + if(bridge_dev == NULL) { + printk(KERN_INFO PFX "agpgart: Detected a Serverworks " + "Chipset, but could not find the secondary " + "device.\n"); + return -ENODEV; + } + + switch (dev->device) { + case PCI_DEVICE_ID_SERVERWORKS_HE: + agp_bridge.type = SVWRKS_HE; + return serverworks_setup(bridge_dev); + + case PCI_DEVICE_ID_SERVERWORKS_LE: + case 0x0007: + agp_bridge.type = SVWRKS_LE; + return serverworks_setup(bridge_dev); + + default: + if(agp_try_unsupported) { + agp_bridge.type = SVWRKS_GENERIC; + return serverworks_setup(bridge_dev); + } + break; + } + } + +#endif /* CONFIG_AGP_SWORKS */ + +#ifdef CONFIG_AGP_HP_ZX1 + if (dev->vendor == PCI_VENDOR_ID_HP) { + /* ZX1 LBAs can be either PCI or AGP bridges */ + if (pci_find_capability(dev, PCI_CAP_ID_AGP)) { + printk(KERN_INFO PFX "Detected HP ZX1 AGP " + "chipset at %s\n", dev->slot_name); + agp_bridge.type = HP_ZX1; + agp_bridge.dev = dev; + return hp_zx1_setup(dev); + } + return -ENODEV; + } +#endif /* CONFIG_AGP_HP_ZX1 */ + + /* find capndx */ + cap_ptr = pci_find_capability(dev, PCI_CAP_ID_AGP); + if (cap_ptr == 0x00) + return -ENODEV; + agp_bridge.capndx = cap_ptr; + + /* Fill in the mode register */ + pci_read_config_dword(agp_bridge.dev, + agp_bridge.capndx + 4, + &agp_bridge.mode); + + /* probe for known chipsets */ + return agp_lookup_host_bridge (dev); +} + +struct agp_max_table { + int mem; + int agp; +}; + +static struct agp_max_table maxes_table[9] __initdata = +{ + {0, 0}, + {32, 4}, + {64, 28}, + {128, 96}, + {256, 204}, + {512, 440}, + {1024, 942}, + {2048, 1920}, + {4096, 3932} +}; + +static int __init agp_find_max (void) +{ + long memory, index, result; + + memory = virt_to_phys(high_memory) >> 20; + index = 1; + + while ((memory > maxes_table[index].mem) && + (index < 8)) { + index++; + } + + result = maxes_table[index - 1].agp + + ( (memory - maxes_table[index - 1].mem) * + (maxes_table[index].agp - maxes_table[index - 1].agp)) / + (maxes_table[index].mem - maxes_table[index - 1].mem); + + printk(KERN_INFO PFX "Maximum main memory to use " + "for agp memory: %ldM\n", result); + result = result << (20 - PAGE_SHIFT); + return result; +} + +#define AGPGART_VERSION_MAJOR 0 +#define AGPGART_VERSION_MINOR 99 + +static struct agp_version agp_current_version = +{ + major: AGPGART_VERSION_MAJOR, + minor: AGPGART_VERSION_MINOR, +}; + +static int __init agp_backend_initialize(struct pci_dev *dev) +{ + int size_value, rc, got_gatt=0, got_keylist=0; + + memset(&agp_bridge, 0, sizeof(struct agp_bridge_data)); + agp_bridge.type = NOT_SUPPORTED; + agp_bridge.max_memory_agp = agp_find_max(); + agp_bridge.version = &agp_current_version; + + rc = agp_find_supported_device(dev); + if (rc) { + /* not KERN_ERR because error msg should have already printed */ + printk(KERN_DEBUG PFX "no supported devices found.\n"); + return rc; + } + + if (agp_bridge.needs_scratch_page == TRUE) { + void *addr; + addr = agp_bridge.agp_alloc_page(); + + if (addr == NULL) { + printk(KERN_ERR PFX "unable to get memory for " + "scratch page.\n"); + return -ENOMEM; + } + agp_bridge.scratch_page = virt_to_phys(addr); + agp_bridge.scratch_page = + agp_bridge.mask_memory(agp_bridge.scratch_page, 0); + } + + size_value = agp_bridge.fetch_size(); + + if (size_value == 0) { + printk(KERN_ERR PFX "unable to determine aperture size.\n"); + rc = -EINVAL; + goto err_out; + } + if (agp_bridge.create_gatt_table()) { + printk(KERN_ERR PFX "unable to get memory for graphics " + "translation table.\n"); + rc = -ENOMEM; + goto err_out; + } + got_gatt = 1; + + agp_bridge.key_list = vmalloc(PAGE_SIZE * 4); + if (agp_bridge.key_list == NULL) { + printk(KERN_ERR PFX "error allocating memory for key lists.\n"); + rc = -ENOMEM; + goto err_out; + } + got_keylist = 1; + + /* FIXME vmalloc'd memory not guaranteed contiguous */ + memset(agp_bridge.key_list, 0, PAGE_SIZE * 4); + + if (agp_bridge.configure()) { + printk(KERN_ERR PFX "error configuring host chipset.\n"); + rc = -EINVAL; + goto err_out; + } + + printk(KERN_INFO PFX "AGP aperture is %dM @ 0x%lx\n", + size_value, agp_bridge.gart_bus_addr); + + return 0; + +err_out: + if (agp_bridge.needs_scratch_page == TRUE) { + agp_bridge.scratch_page &= ~(0x00000fff); + agp_bridge.agp_destroy_page(phys_to_virt(agp_bridge.scratch_page)); + } + if (got_gatt) + agp_bridge.free_gatt_table(); + if (got_keylist) + vfree(agp_bridge.key_list); + return rc; +} + + +/* cannot be __exit b/c as it could be called from __init code */ +static void agp_backend_cleanup(void) +{ + agp_bridge.cleanup(); + agp_bridge.free_gatt_table(); + vfree(agp_bridge.key_list); + + if (agp_bridge.needs_scratch_page == TRUE) { + agp_bridge.scratch_page &= ~(0x00000fff); + agp_bridge.agp_destroy_page(phys_to_virt(agp_bridge.scratch_page)); + } +} + +static int agp_power(struct pm_dev *dev, pm_request_t rq, void *data) +{ + switch(rq) + { + case PM_SUSPEND: + return agp_bridge.suspend(); + case PM_RESUME: + agp_bridge.resume(); + return 0; + } + return 0; +} + +extern int agp_frontend_initialize(void); +extern void agp_frontend_cleanup(void); + +static const drm_agp_t drm_agp = { + &agp_free_memory, + &agp_allocate_memory, + &agp_bind_memory, + &agp_unbind_memory, + &agp_enable, + &agp_backend_acquire, + &agp_backend_release, + &agp_copy_info +}; + +static int agp_probe (struct pci_dev *dev, const struct pci_device_id *ent) +{ + int ret_val; + + if (agp_bridge.type != NOT_SUPPORTED) { + printk (KERN_DEBUG PFX "Oops, don't init more than one agpgart device.\n"); + return -ENODEV; + } + + ret_val = agp_backend_initialize(dev); + if (ret_val) { + agp_bridge.type = NOT_SUPPORTED; + return ret_val; + } + ret_val = agp_frontend_initialize(); + if (ret_val) { + agp_bridge.type = NOT_SUPPORTED; + agp_backend_cleanup(); + return ret_val; + } + + inter_module_register("drm_agp", THIS_MODULE, &drm_agp); + + pm_register(PM_PCI_DEV, PM_PCI_ID(agp_bridge.dev), agp_power); + return 0; +} + +static struct pci_device_id agp_pci_table[] __initdata = { + { + class: (PCI_CLASS_BRIDGE_HOST << 8), + class_mask: ~0, + vendor: PCI_ANY_ID, + device: PCI_ANY_ID, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + }, + { } +}; + +MODULE_DEVICE_TABLE(pci, agp_pci_table); + +static struct pci_driver agp_pci_driver = { + name: "agpgart", + id_table: agp_pci_table, + probe: agp_probe, +}; + +static int __init agp_init(void) +{ + int ret_val; + + printk(KERN_INFO "Linux agpgart interface v%d.%d (c) Jeff Hartmann\n", + AGPGART_VERSION_MAJOR, AGPGART_VERSION_MINOR); + + ret_val = pci_module_init(&agp_pci_driver); + if (ret_val) { + agp_bridge.type = NOT_SUPPORTED; + return ret_val; + } + return 0; +} + +static void __exit agp_cleanup(void) +{ + pci_unregister_driver(&agp_pci_driver); + if (agp_bridge.type != NOT_SUPPORTED) { + pm_unregister_all(agp_power); + agp_frontend_cleanup(); + agp_backend_cleanup(); + inter_module_unregister("drm_agp"); + } +} + +module_init(agp_init); +module_exit(agp_cleanup); diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 94e405104df4..4515f8ee5d2e 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -27,6 +27,67 @@ #ifndef _AGP_BACKEND_PRIV_H #define _AGP_BACKEND_PRIV_H 1 +#include <asm/agp.h> /* for flush_agp_cache() */ + +extern struct agp_bridge_data agp_bridge; + +/* Generic routines. */ +void agp_generic_agp_enable(u32 mode); +int agp_generic_create_gatt_table(void); +int agp_generic_free_gatt_table(void); +agp_memory *agp_create_memory(int scratch_pages); +int agp_generic_insert_memory(agp_memory * mem, off_t pg_start, int type); +int agp_generic_remove_memory(agp_memory * mem, off_t pg_start, int type); +agp_memory *agp_generic_alloc_by_type(size_t page_count, int type); +void agp_generic_free_by_type(agp_memory * curr); +void *agp_generic_alloc_page(void); +void agp_generic_destroy_page(void *addr); +int agp_generic_suspend(void); +void agp_generic_resume(void); +void agp_free_key(int key); + +/* chipset specific init routines. */ +int __init ali_generic_setup (struct pci_dev *pdev); +int __init amd_irongate_setup (struct pci_dev *pdev); +int __init hp_zx1_setup (struct pci_dev *pdev); +int __init intel_i460_setup (struct pci_dev *pdev); +int __init intel_generic_setup (struct pci_dev *pdev); +int __init intel_i810_setup(struct pci_dev *i810_dev); +int __init intel_815_setup(struct pci_dev *pdev); +int __init intel_i830_setup(struct pci_dev *i830_dev); +int __init intel_820_setup (struct pci_dev *pdev); +int __init intel_830mp_setup (struct pci_dev *pdev); +int __init intel_840_setup (struct pci_dev *pdev); +int __init intel_845_setup (struct pci_dev *pdev); +int __init intel_850_setup (struct pci_dev *pdev); +int __init intel_860_setup (struct pci_dev *pdev); +int __init serverworks_setup (struct pci_dev *pdev); +int __init sis_generic_setup (struct pci_dev *pdev); +int __init via_generic_setup (struct pci_dev *pdev); + +#define AGPGART_MODULE_NAME "agpgart" +#define PFX AGPGART_MODULE_NAME ": " + + +#ifdef CONFIG_SMP +static void ipi_handler(void *null) +{ + flush_agp_cache(); +} + +static void __attribute__((unused)) global_cache_flush(void) +{ + if (smp_call_function(ipi_handler, NULL, 1, 1) != 0) + panic(PFX "timed out waiting for the other CPUs!\n"); + flush_agp_cache(); +} +#else +static void global_cache_flush(void) +{ + flush_agp_cache(); +} +#endif /* !CONFIG_SMP */ + enum aper_size_type { U8_APER_SIZE, U16_APER_SIZE, @@ -35,55 +96,55 @@ enum aper_size_type { FIXED_APER_SIZE }; -typedef struct _gatt_mask { +struct gatt_mask { unsigned long mask; u32 type; /* totally device specific, for integrated chipsets that * might have different types of memory masks. For other * devices this will probably be ignored */ -} gatt_mask; +}; -typedef struct _aper_size_info_8 { +struct aper_size_info_8 { int size; int num_entries; int page_order; u8 size_value; -} aper_size_info_8; +}; -typedef struct _aper_size_info_16 { +struct aper_size_info_16 { int size; int num_entries; int page_order; u16 size_value; -} aper_size_info_16; +}; -typedef struct _aper_size_info_32 { +struct aper_size_info_32 { int size; int num_entries; int page_order; u32 size_value; -} aper_size_info_32; +}; -typedef struct _aper_size_info_lvl2 { +struct aper_size_info_lvl2 { int size; int num_entries; u32 size_value; -} aper_size_info_lvl2; +}; -typedef struct _aper_size_info_fixed { +struct aper_size_info_fixed { int size; int num_entries; int page_order; -} aper_size_info_fixed; +}; struct agp_bridge_data { - agp_version *version; + struct agp_version *version; void *aperture_sizes; void *previous_size; void *current_size; void *dev_private_data; struct pci_dev *dev; - gatt_mask *masks; + struct gatt_mask *masks; unsigned long *gatt_table; unsigned long *gatt_table_real; unsigned long scratch_page; @@ -125,26 +186,26 @@ struct agp_bridge_data { }; -#define OUTREG64(mmap, addr, val) __raw_writeq((val), (mmap)+(addr)) -#define OUTREG32(mmap, addr, val) __raw_writel((val), (mmap)+(addr)) -#define OUTREG16(mmap, addr, val) __raw_writew((val), (mmap)+(addr)) -#define OUTREG8(mmap, addr, val) __raw_writeb((val), (mmap)+(addr)) +#define OUTREG64(mmap, addr, val) __raw_writeq((val), (mmap)+(addr)) +#define OUTREG32(mmap, addr, val) __raw_writel((val), (mmap)+(addr)) +#define OUTREG16(mmap, addr, val) __raw_writew((val), (mmap)+(addr)) +#define OUTREG8(mmap, addr, val) __raw_writeb((val), (mmap)+(addr)) -#define INREG64(mmap, addr) __raw_readq((mmap)+(addr)) -#define INREG32(mmap, addr) __raw_readl((mmap)+(addr)) -#define INREG16(mmap, addr) __raw_readw((mmap)+(addr)) -#define INREG8(mmap, addr) __raw_readb((mmap)+(addr)) +#define INREG64(mmap, addr) __raw_readq((mmap)+(addr)) +#define INREG32(mmap, addr) __raw_readl((mmap)+(addr)) +#define INREG16(mmap, addr) __raw_readw((mmap)+(addr)) +#define INREG8(mmap, addr) __raw_readb((mmap)+(addr)) -#define KB(x) ((x) * 1024) -#define MB(x) (KB (KB (x))) -#define GB(x) (MB (KB (x))) +#define KB(x) ((x) * 1024) +#define MB(x) (KB (KB (x))) +#define GB(x) (MB (KB (x))) #define CACHE_FLUSH agp_bridge.cache_flush -#define A_SIZE_8(x) ((aper_size_info_8 *) x) -#define A_SIZE_16(x) ((aper_size_info_16 *) x) -#define A_SIZE_32(x) ((aper_size_info_32 *) x) -#define A_SIZE_LVL2(x) ((aper_size_info_lvl2 *) x) -#define A_SIZE_FIX(x) ((aper_size_info_fixed *) x) +#define A_SIZE_8(x) ((struct aper_size_info_8 *) x) +#define A_SIZE_16(x) ((struct aper_size_info_16 *) x) +#define A_SIZE_32(x) ((struct aper_size_info_32 *) x) +#define A_SIZE_LVL2(x) ((struct aper_size_info_lvl2 *) x) +#define A_SIZE_FIX(x) ((struct aper_size_info_fixed *) x) #define A_IDX8() (A_SIZE_8(agp_bridge.aperture_sizes) + i) #define A_IDX16() (A_SIZE_16(agp_bridge.aperture_sizes) + i) #define A_IDX32() (A_SIZE_32(agp_bridge.aperture_sizes) + i) @@ -152,91 +213,88 @@ struct agp_bridge_data { #define A_IDXFIX() (A_SIZE_FIX(agp_bridge.aperture_sizes) + i) #define MAXKEY (4096 * 32) -#define AGPGART_MODULE_NAME "agpgart" -#define PFX AGPGART_MODULE_NAME ": " - -#define PGE_EMPTY(p) (!(p) || (p) == (unsigned long) agp_bridge.scratch_page) +#define PGE_EMPTY(p) (!(p) || (p) == (unsigned long) agp_bridge.scratch_page) #ifndef PCI_DEVICE_ID_VIA_82C691_0 -#define PCI_DEVICE_ID_VIA_82C691_0 0x0691 +#define PCI_DEVICE_ID_VIA_82C691_0 0x0691 #endif #ifndef PCI_DEVICE_ID_VIA_8371_0 -#define PCI_DEVICE_ID_VIA_8371_0 0x0391 +#define PCI_DEVICE_ID_VIA_8371_0 0x0391 #endif #ifndef PCI_DEVICE_ID_VIA_8363_0 -#define PCI_DEVICE_ID_VIA_8363_0 0x0305 +#define PCI_DEVICE_ID_VIA_8363_0 0x0305 #endif #ifndef PCI_DEVICE_ID_VIA_82C694X_0 -#define PCI_DEVICE_ID_VIA_82C694X_0 0x0605 +#define PCI_DEVICE_ID_VIA_82C694X_0 0x0605 #endif #ifndef PCI_DEVICE_ID_INTEL_810_0 -#define PCI_DEVICE_ID_INTEL_810_0 0x7120 +#define PCI_DEVICE_ID_INTEL_810_0 0x7120 #endif #ifndef PCI_DEVICE_ID_INTEL_845_G_0 #define PCI_DEVICE_ID_INTEL_845_G_0 0x2560 #endif #ifndef PCI_DEVICE_ID_INTEL_845_G_1 -#define PCI_DEVICE_ID_INTEL_845_G_1 0x2562 +#define PCI_DEVICE_ID_INTEL_845_G_1 0x2562 #endif #ifndef PCI_DEVICE_ID_INTEL_830_M_0 #define PCI_DEVICE_ID_INTEL_830_M_0 0x3575 #endif #ifndef PCI_DEVICE_ID_INTEL_830_M_1 -#define PCI_DEVICE_ID_INTEL_830_M_1 0x3577 +#define PCI_DEVICE_ID_INTEL_830_M_1 0x3577 #endif #ifndef PCI_DEVICE_ID_INTEL_820_0 -#define PCI_DEVICE_ID_INTEL_820_0 0x2500 +#define PCI_DEVICE_ID_INTEL_820_0 0x2500 #endif #ifndef PCI_DEVICE_ID_INTEL_820_UP_0 -#define PCI_DEVICE_ID_INTEL_820_UP_0 0x2501 +#define PCI_DEVICE_ID_INTEL_820_UP_0 0x2501 #endif #ifndef PCI_DEVICE_ID_INTEL_840_0 -#define PCI_DEVICE_ID_INTEL_840_0 0x1a21 +#define PCI_DEVICE_ID_INTEL_840_0 0x1a21 #endif #ifndef PCI_DEVICE_ID_INTEL_845_0 -#define PCI_DEVICE_ID_INTEL_845_0 0x1a30 +#define PCI_DEVICE_ID_INTEL_845_0 0x1a30 #endif #ifndef PCI_DEVICE_ID_INTEL_850_0 -#define PCI_DEVICE_ID_INTEL_850_0 0x2530 +#define PCI_DEVICE_ID_INTEL_850_0 0x2530 #endif #ifndef PCI_DEVICE_ID_INTEL_860_0 -#define PCI_DEVICE_ID_INTEL_860_0 0x2531 +#define PCI_DEVICE_ID_INTEL_860_0 0x2531 #endif #ifndef PCI_DEVICE_ID_INTEL_810_DC100_0 -#define PCI_DEVICE_ID_INTEL_810_DC100_0 0x7122 +#define PCI_DEVICE_ID_INTEL_810_DC100_0 0x7122 #endif #ifndef PCI_DEVICE_ID_INTEL_810_E_0 -#define PCI_DEVICE_ID_INTEL_810_E_0 0x7124 +#define PCI_DEVICE_ID_INTEL_810_E_0 0x7124 #endif #ifndef PCI_DEVICE_ID_INTEL_82443GX_0 -#define PCI_DEVICE_ID_INTEL_82443GX_0 0x71a0 +#define PCI_DEVICE_ID_INTEL_82443GX_0 0x71a0 #endif #ifndef PCI_DEVICE_ID_INTEL_810_1 -#define PCI_DEVICE_ID_INTEL_810_1 0x7121 +#define PCI_DEVICE_ID_INTEL_810_1 0x7121 #endif #ifndef PCI_DEVICE_ID_INTEL_810_DC100_1 -#define PCI_DEVICE_ID_INTEL_810_DC100_1 0x7123 +#define PCI_DEVICE_ID_INTEL_810_DC100_1 0x7123 #endif #ifndef PCI_DEVICE_ID_INTEL_810_E_1 -#define PCI_DEVICE_ID_INTEL_810_E_1 0x7125 +#define PCI_DEVICE_ID_INTEL_810_E_1 0x7125 #endif #ifndef PCI_DEVICE_ID_INTEL_815_0 -#define PCI_DEVICE_ID_INTEL_815_0 0x1130 +#define PCI_DEVICE_ID_INTEL_815_0 0x1130 #endif #ifndef PCI_DEVICE_ID_INTEL_815_1 -#define PCI_DEVICE_ID_INTEL_815_1 0x1132 +#define PCI_DEVICE_ID_INTEL_815_1 0x1132 #endif #ifndef PCI_DEVICE_ID_INTEL_82443GX_1 -#define PCI_DEVICE_ID_INTEL_82443GX_1 0x71a1 +#define PCI_DEVICE_ID_INTEL_82443GX_1 0x71a1 #endif #ifndef PCI_DEVICE_ID_INTEL_460GX -#define PCI_DEVICE_ID_INTEL_460GX 0x84ea +#define PCI_DEVICE_ID_INTEL_460GX 0x84ea #endif #ifndef PCI_DEVICE_ID_AMD_IRONGATE_0 -#define PCI_DEVICE_ID_AMD_IRONGATE_0 0x7006 +#define PCI_DEVICE_ID_AMD_IRONGATE_0 0x7006 #endif #ifndef PCI_DEVICE_ID_AMD_761_0 -#define PCI_DEVICE_ID_AMD_761_0 0x700e +#define PCI_DEVICE_ID_AMD_761_0 0x700e #endif #ifndef PCI_DEVICE_ID_AMD_762_0 #define PCI_DEVICE_ID_AMD_762_0 0x700C @@ -270,12 +328,12 @@ struct agp_bridge_data { #endif /* intel register */ -#define INTEL_APBASE 0x10 -#define INTEL_APSIZE 0xb4 -#define INTEL_ATTBASE 0xb8 -#define INTEL_AGPCTRL 0xb0 -#define INTEL_NBXCFG 0x50 -#define INTEL_ERRSTS 0x91 +#define INTEL_APBASE 0x10 +#define INTEL_APSIZE 0xb4 +#define INTEL_ATTBASE 0xb8 +#define INTEL_AGPCTRL 0xb0 +#define INTEL_NBXCFG 0x50 +#define INTEL_ERRSTS 0x91 /* Intel 460GX Registers */ #define INTEL_I460_APBASE 0x10 @@ -287,116 +345,120 @@ struct agp_bridge_data { #define INTEL_I460_GATT_COHERENT (1UL << 25) /* intel i830 registers */ -#define I830_GMCH_CTRL 0x52 -#define I830_GMCH_ENABLED 0x4 -#define I830_GMCH_MEM_MASK 0x1 -#define I830_GMCH_MEM_64M 0x1 -#define I830_GMCH_MEM_128M 0 -#define I830_GMCH_GMS_MASK 0x70 -#define I830_GMCH_GMS_DISABLED 0x00 -#define I830_GMCH_GMS_LOCAL 0x10 -#define I830_GMCH_GMS_STOLEN_512 0x20 -#define I830_GMCH_GMS_STOLEN_1024 0x30 -#define I830_GMCH_GMS_STOLEN_8192 0x40 -#define I830_RDRAM_CHANNEL_TYPE 0x03010 -#define I830_RDRAM_ND(x) (((x) & 0x20) >> 5) -#define I830_RDRAM_DDT(x) (((x) & 0x18) >> 3) +#define I830_GMCH_CTRL 0x52 +#define I830_GMCH_ENABLED 0x4 +#define I830_GMCH_MEM_MASK 0x1 +#define I830_GMCH_MEM_64M 0x1 +#define I830_GMCH_MEM_128M 0 +#define I830_GMCH_GMS_MASK 0x70 +#define I830_GMCH_GMS_DISABLED 0x00 +#define I830_GMCH_GMS_LOCAL 0x10 +#define I830_GMCH_GMS_STOLEN_512 0x20 +#define I830_GMCH_GMS_STOLEN_1024 0x30 +#define I830_GMCH_GMS_STOLEN_8192 0x40 +#define I830_RDRAM_CHANNEL_TYPE 0x03010 +#define I830_RDRAM_ND(x) (((x) & 0x20) >> 5) +#define I830_RDRAM_DDT(x) (((x) & 0x18) >> 3) /* This one is for I830MP w. an external graphic card */ -#define INTEL_I830_ERRSTS 0x92 +#define INTEL_I830_ERRSTS 0x92 + +/* intel 815 register */ +#define INTEL_815_APCONT 0x51 +#define INTEL_815_ATTBASE_MASK ~0x1FFFFFFF /* intel i820 registers */ -#define INTEL_I820_RDCR 0x51 -#define INTEL_I820_ERRSTS 0xc8 +#define INTEL_I820_RDCR 0x51 +#define INTEL_I820_ERRSTS 0xc8 /* intel i840 registers */ -#define INTEL_I840_MCHCFG 0x50 -#define INTEL_I840_ERRSTS 0xc8 +#define INTEL_I840_MCHCFG 0x50 +#define INTEL_I840_ERRSTS 0xc8 /* intel i845 registers */ -#define INTEL_I845_AGPM 0x51 -#define INTEL_I845_ERRSTS 0xc8 +#define INTEL_I845_AGPM 0x51 +#define INTEL_I845_ERRSTS 0xc8 /* intel i850 registers */ -#define INTEL_I850_MCHCFG 0x50 -#define INTEL_I850_ERRSTS 0xc8 +#define INTEL_I850_MCHCFG 0x50 +#define INTEL_I850_ERRSTS 0xc8 /* intel i860 registers */ #define INTEL_I860_MCHCFG 0x50 #define INTEL_I860_ERRSTS 0xc8 /* intel i810 registers */ -#define I810_GMADDR 0x10 -#define I810_MMADDR 0x14 -#define I810_PTE_BASE 0x10000 -#define I810_PTE_MAIN_UNCACHED 0x00000000 -#define I810_PTE_LOCAL 0x00000002 -#define I810_PTE_VALID 0x00000001 -#define I810_SMRAM_MISCC 0x70 -#define I810_GFX_MEM_WIN_SIZE 0x00010000 -#define I810_GFX_MEM_WIN_32M 0x00010000 -#define I810_GMS 0x000000c0 -#define I810_GMS_DISABLE 0x00000000 -#define I810_PGETBL_CTL 0x2020 -#define I810_PGETBL_ENABLED 0x00000001 -#define I810_DRAM_CTL 0x3000 -#define I810_DRAM_ROW_0 0x00000001 -#define I810_DRAM_ROW_0_SDRAM 0x00000001 +#define I810_GMADDR 0x10 +#define I810_MMADDR 0x14 +#define I810_PTE_BASE 0x10000 +#define I810_PTE_MAIN_UNCACHED 0x00000000 +#define I810_PTE_LOCAL 0x00000002 +#define I810_PTE_VALID 0x00000001 +#define I810_SMRAM_MISCC 0x70 +#define I810_GFX_MEM_WIN_SIZE 0x00010000 +#define I810_GFX_MEM_WIN_32M 0x00010000 +#define I810_GMS 0x000000c0 +#define I810_GMS_DISABLE 0x00000000 +#define I810_PGETBL_CTL 0x2020 +#define I810_PGETBL_ENABLED 0x00000001 +#define I810_DRAM_CTL 0x3000 +#define I810_DRAM_ROW_0 0x00000001 +#define I810_DRAM_ROW_0_SDRAM 0x00000001 /* VIA register */ -#define VIA_APBASE 0x10 -#define VIA_GARTCTRL 0x80 -#define VIA_APSIZE 0x84 -#define VIA_ATTBASE 0x88 +#define VIA_APBASE 0x10 +#define VIA_GARTCTRL 0x80 +#define VIA_APSIZE 0x84 +#define VIA_ATTBASE 0x88 /* SiS registers */ -#define SIS_APBASE 0x10 -#define SIS_ATTBASE 0x90 -#define SIS_APSIZE 0x94 -#define SIS_TLBCNTRL 0x97 -#define SIS_TLBFLUSH 0x98 +#define SIS_APBASE 0x10 +#define SIS_ATTBASE 0x90 +#define SIS_APSIZE 0x94 +#define SIS_TLBCNTRL 0x97 +#define SIS_TLBFLUSH 0x98 /* AMD registers */ -#define AMD_APBASE 0x10 -#define AMD_MMBASE 0x14 -#define AMD_APSIZE 0xac -#define AMD_MODECNTL 0xb0 -#define AMD_MODECNTL2 0xb2 -#define AMD_GARTENABLE 0x02 /* In mmio region (16-bit register) */ -#define AMD_ATTBASE 0x04 /* In mmio region (32-bit register) */ -#define AMD_TLBFLUSH 0x0c /* In mmio region (32-bit register) */ -#define AMD_CACHEENTRY 0x10 /* In mmio region (32-bit register) */ +#define AMD_APBASE 0x10 +#define AMD_MMBASE 0x14 +#define AMD_APSIZE 0xac +#define AMD_MODECNTL 0xb0 +#define AMD_MODECNTL2 0xb2 +#define AMD_GARTENABLE 0x02 /* In mmio region (16-bit register) */ +#define AMD_ATTBASE 0x04 /* In mmio region (32-bit register) */ +#define AMD_TLBFLUSH 0x0c /* In mmio region (32-bit register) */ +#define AMD_CACHEENTRY 0x10 /* In mmio region (32-bit register) */ /* ALi registers */ -#define ALI_APBASE 0x10 -#define ALI_AGPCTRL 0xb8 -#define ALI_ATTBASE 0xbc -#define ALI_TLBCTRL 0xc0 -#define ALI_TAGCTRL 0xc4 -#define ALI_CACHE_FLUSH_CTRL 0xD0 +#define ALI_APBASE 0x10 +#define ALI_AGPCTRL 0xb8 +#define ALI_ATTBASE 0xbc +#define ALI_TLBCTRL 0xc0 +#define ALI_TAGCTRL 0xc4 +#define ALI_CACHE_FLUSH_CTRL 0xD0 #define ALI_CACHE_FLUSH_ADDR_MASK 0xFFFFF000 -#define ALI_CACHE_FLUSH_EN 0x100 +#define ALI_CACHE_FLUSH_EN 0x100 /* Serverworks Registers */ -#define SVWRKS_APSIZE 0x10 -#define SVWRKS_SIZE_MASK 0xfe000000 +#define SVWRKS_APSIZE 0x10 +#define SVWRKS_SIZE_MASK 0xfe000000 -#define SVWRKS_MMBASE 0x14 -#define SVWRKS_CACHING 0x4b -#define SVWRKS_FEATURE 0x68 +#define SVWRKS_MMBASE 0x14 +#define SVWRKS_CACHING 0x4b +#define SVWRKS_FEATURE 0x68 /* func 1 registers */ -#define SVWRKS_AGP_ENABLE 0x60 -#define SVWRKS_COMMAND 0x04 +#define SVWRKS_AGP_ENABLE 0x60 +#define SVWRKS_COMMAND 0x04 /* Memory mapped registers */ -#define SVWRKS_GART_CACHE 0x02 -#define SVWRKS_GATTBASE 0x04 -#define SVWRKS_TLBFLUSH 0x10 -#define SVWRKS_POSTFLUSH 0x14 -#define SVWRKS_DIRFLUSH 0x0c +#define SVWRKS_GART_CACHE 0x02 +#define SVWRKS_GATTBASE 0x04 +#define SVWRKS_TLBFLUSH 0x10 +#define SVWRKS_POSTFLUSH 0x14 +#define SVWRKS_DIRFLUSH 0x0c /* HP ZX1 SBA registers */ #define HP_ZX1_CTRL 0x200 diff --git a/drivers/char/agp/agpgart_be.c b/drivers/char/agp/agpgart_be.c deleted file mode 100644 index 8ba761695215..000000000000 --- a/drivers/char/agp/agpgart_be.c +++ /dev/null @@ -1,5114 +0,0 @@ -/* - * AGPGART module version 0.99 - * Copyright (C) 1999 Jeff Hartmann - * Copyright (C) 1999 Precision Insight, Inc. - * Copyright (C) 1999 Xi Graphics, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * TODO: - * - Allocate more than order 0 pages to avoid too much linear map splitting. - */ -#include <linux/config.h> -#include <linux/version.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/jiffies.h> -#include <linux/mm.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> -#include <linux/pci.h> -#include <linux/init.h> -#include <linux/pagemap.h> -#include <linux/miscdevice.h> -#include <linux/pm.h> -#include <asm/system.h> -#include <asm/uaccess.h> -#include <asm/io.h> -#include <asm/page.h> -#include <asm/agp.h> - -#include <linux/agp_backend.h> -#include "agp.h" - -MODULE_AUTHOR("Jeff Hartmann <jhartmann@precisioninsight.com>"); -MODULE_PARM(agp_try_unsupported, "1i"); -MODULE_LICENSE("GPL and additional rights"); -EXPORT_SYMBOL(agp_free_memory); -EXPORT_SYMBOL(agp_allocate_memory); -EXPORT_SYMBOL(agp_copy_info); -EXPORT_SYMBOL(agp_bind_memory); -EXPORT_SYMBOL(agp_unbind_memory); -EXPORT_SYMBOL(agp_enable); -EXPORT_SYMBOL(agp_backend_acquire); -EXPORT_SYMBOL(agp_backend_release); - -static struct agp_bridge_data agp_bridge; -static int agp_try_unsupported __initdata = 0; - -#ifdef CONFIG_SMP -static void ipi_handler(void *null) -{ - flush_agp_cache(); -} - -static void smp_flush_cache(void) -{ - if (smp_call_function(ipi_handler, NULL, 1, 1) != 0) - panic(PFX "timed out waiting for the other CPUs!\n"); - flush_agp_cache(); -} -#define global_cache_flush smp_flush_cache -#else /* CONFIG_SMP */ -static void global_cache_flush(void) -{ - flush_agp_cache(); -} -#endif /* !CONFIG_SMP */ - -int agp_backend_acquire(void) -{ - if (agp_bridge.type == NOT_SUPPORTED) { - return -EINVAL; - } - atomic_inc(&agp_bridge.agp_in_use); - - if (atomic_read(&agp_bridge.agp_in_use) != 1) { - atomic_dec(&agp_bridge.agp_in_use); - return -EBUSY; - } - MOD_INC_USE_COUNT; - return 0; -} - -void agp_backend_release(void) -{ - if (agp_bridge.type == NOT_SUPPORTED) { - return; - } - atomic_dec(&agp_bridge.agp_in_use); - MOD_DEC_USE_COUNT; -} - -/* - * Generic routines for handling agp_memory structures - - * They use the basic page allocation routines to do the - * brunt of the work. - */ - - -static void agp_free_key(int key) -{ - - if (key < 0) { - return; - } - if (key < MAXKEY) { - clear_bit(key, agp_bridge.key_list); - } -} - -static int agp_get_key(void) -{ - int bit; - - bit = find_first_zero_bit(agp_bridge.key_list, MAXKEY); - if (bit < MAXKEY) { - set_bit(bit, agp_bridge.key_list); - return bit; - } - return -1; -} - -static agp_memory *agp_create_memory(int scratch_pages) -{ - agp_memory *new; - - new = kmalloc(sizeof(agp_memory), GFP_KERNEL); - - if (new == NULL) { - return NULL; - } - memset(new, 0, sizeof(agp_memory)); - new->key = agp_get_key(); - - if (new->key < 0) { - kfree(new); - return NULL; - } - new->memory = vmalloc(PAGE_SIZE * scratch_pages); - - if (new->memory == NULL) { - agp_free_key(new->key); - kfree(new); - return NULL; - } - new->num_scratch_pages = scratch_pages; - return new; -} - -void agp_free_memory(agp_memory * curr) -{ - int i; - - if ((agp_bridge.type == NOT_SUPPORTED) || (curr == NULL)) { - return; - } - if (curr->is_bound == TRUE) { - agp_unbind_memory(curr); - } - if (curr->type != 0) { - agp_bridge.free_by_type(curr); - return; - } - if (curr->page_count != 0) { - for (i = 0; i < curr->page_count; i++) { - curr->memory[i] &= ~(0x00000fff); - agp_bridge.agp_destroy_page(phys_to_virt(curr->memory[i])); - } - } - agp_free_key(curr->key); - vfree(curr->memory); - kfree(curr); - MOD_DEC_USE_COUNT; -} - -#define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(unsigned long)) - -agp_memory *agp_allocate_memory(size_t page_count, u32 type) -{ - int scratch_pages; - agp_memory *new; - int i; - - if (agp_bridge.type == NOT_SUPPORTED) { - return NULL; - } - if ((atomic_read(&agp_bridge.current_memory_agp) + page_count) > - agp_bridge.max_memory_agp) { - return NULL; - } - - if (type != 0) { - new = agp_bridge.alloc_by_type(page_count, type); - return new; - } - /* We always increase the module count, since free auto-decrements - * it - */ - - MOD_INC_USE_COUNT; - - scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; - - new = agp_create_memory(scratch_pages); - - if (new == NULL) { - MOD_DEC_USE_COUNT; - return NULL; - } - - for (i = 0; i < page_count; i++) { - void *addr = agp_bridge.agp_alloc_page(); - - if (addr == NULL) { - /* Free this structure */ - agp_free_memory(new); - return NULL; - } - new->memory[i] = - agp_bridge.mask_memory(virt_to_phys(addr), type); - new->page_count++; - } - - flush_agp_mappings(); - - return new; -} - -/* End - Generic routines for handling agp_memory structures */ - -static int agp_return_size(void) -{ - int current_size; - void *temp; - - temp = agp_bridge.current_size; - - switch (agp_bridge.size_type) { - case U8_APER_SIZE: - current_size = A_SIZE_8(temp)->size; - break; - case U16_APER_SIZE: - current_size = A_SIZE_16(temp)->size; - break; - case U32_APER_SIZE: - current_size = A_SIZE_32(temp)->size; - break; - case LVL2_APER_SIZE: - current_size = A_SIZE_LVL2(temp)->size; - break; - case FIXED_APER_SIZE: - current_size = A_SIZE_FIX(temp)->size; - break; - default: - current_size = 0; - break; - } - - return current_size; -} - -/* Routine to copy over information structure */ - -void agp_copy_info(agp_kern_info * info) -{ - unsigned long page_mask = 0; - int i; - - memset(info, 0, sizeof(agp_kern_info)); - if (agp_bridge.type == NOT_SUPPORTED) { - info->chipset = agp_bridge.type; - return; - } - info->version.major = agp_bridge.version->major; - info->version.minor = agp_bridge.version->minor; - info->device = agp_bridge.dev; - info->chipset = agp_bridge.type; - info->mode = agp_bridge.mode; - info->aper_base = agp_bridge.gart_bus_addr; - info->aper_size = agp_return_size(); - info->max_memory = agp_bridge.max_memory_agp; - info->current_memory = atomic_read(&agp_bridge.current_memory_agp); - info->cant_use_aperture = agp_bridge.cant_use_aperture; - - for(i = 0; i < agp_bridge.num_of_masks; i++) - page_mask |= agp_bridge.mask_memory(page_mask, i); - - info->page_mask = ~page_mask; -} - -/* End - Routine to copy over information structure */ - -/* - * Routines for handling swapping of agp_memory into the GATT - - * These routines take agp_memory and insert them into the GATT. - * They call device specific routines to actually write to the GATT. - */ - -int agp_bind_memory(agp_memory * curr, off_t pg_start) -{ - int ret_val; - - if ((agp_bridge.type == NOT_SUPPORTED) || - (curr == NULL) || (curr->is_bound == TRUE)) { - return -EINVAL; - } - if (curr->is_flushed == FALSE) { - CACHE_FLUSH(); - curr->is_flushed = TRUE; - } - ret_val = agp_bridge.insert_memory(curr, pg_start, curr->type); - - if (ret_val != 0) { - return ret_val; - } - curr->is_bound = TRUE; - curr->pg_start = pg_start; - return 0; -} - -int agp_unbind_memory(agp_memory * curr) -{ - int ret_val; - - if ((agp_bridge.type == NOT_SUPPORTED) || (curr == NULL)) { - return -EINVAL; - } - if (curr->is_bound != TRUE) { - return -EINVAL; - } - ret_val = agp_bridge.remove_memory(curr, curr->pg_start, curr->type); - - if (ret_val != 0) { - return ret_val; - } - curr->is_bound = FALSE; - curr->pg_start = 0; - return 0; -} - -/* End - Routines for handling swapping of agp_memory into the GATT */ - -/* - * Driver routines - start - * Currently this module supports the following chipsets: - * i810, i815, 440lx, 440bx, 440gx, i830, i840, i845, i850, i860, via vp3, - * via mvp3, via kx133, via kt133, amd irongate, amd 761, amd 762, ALi M1541, - * and generic support for the SiS chipsets. - */ - -/* Generic Agp routines - Start */ - -static void agp_generic_agp_enable(u32 mode) -{ - struct pci_dev *device = NULL; - u32 command, scratch; - u8 cap_ptr; - - pci_read_config_dword(agp_bridge.dev, - agp_bridge.capndx + 4, - &command); - - /* - * PASS1: go throu all devices that claim to be - * AGP devices and collect their data. - */ - - - pci_for_each_dev(device) { - cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); - if (cap_ptr != 0x00) { - /* - * Ok, here we have a AGP device. Disable impossible - * settings, and adjust the readqueue to the minimum. - */ - - pci_read_config_dword(device, cap_ptr + 4, &scratch); - - /* adjust RQ depth */ - command = - ((command & ~0xff000000) | - min_t(u32, (mode & 0xff000000), - min_t(u32, (command & 0xff000000), - (scratch & 0xff000000)))); - - /* disable SBA if it's not supported */ - if (!((command & 0x00000200) && - (scratch & 0x00000200) && - (mode & 0x00000200))) - command &= ~0x00000200; - - /* disable FW if it's not supported */ - if (!((command & 0x00000010) && - (scratch & 0x00000010) && - (mode & 0x00000010))) - command &= ~0x00000010; - - if (!((command & 4) && - (scratch & 4) && - (mode & 4))) - command &= ~0x00000004; - - if (!((command & 2) && - (scratch & 2) && - (mode & 2))) - command &= ~0x00000002; - - if (!((command & 1) && - (scratch & 1) && - (mode & 1))) - command &= ~0x00000001; - } - } - /* - * PASS2: Figure out the 4X/2X/1X setting and enable the - * target (our motherboard chipset). - */ - - if (command & 4) { - command &= ~3; /* 4X */ - } - if (command & 2) { - command &= ~5; /* 2X */ - } - if (command & 1) { - command &= ~6; /* 1X */ - } - command |= 0x00000100; - - pci_write_config_dword(agp_bridge.dev, - agp_bridge.capndx + 8, - command); - - /* - * PASS3: Go throu all AGP devices and update the - * command registers. - */ - - pci_for_each_dev(device) { - cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); - if (cap_ptr != 0x00) - pci_write_config_dword(device, cap_ptr + 8, command); - } -} - -static int agp_generic_create_gatt_table(void) -{ - char *table; - char *table_end; - int size; - int page_order; - int num_entries; - int i; - void *temp; - struct page *page; - - /* The generic routines can't handle 2 level gatt's */ - if (agp_bridge.size_type == LVL2_APER_SIZE) { - return -EINVAL; - } - - table = NULL; - i = agp_bridge.aperture_size_idx; - temp = agp_bridge.current_size; - size = page_order = num_entries = 0; - - if (agp_bridge.size_type != FIXED_APER_SIZE) { - do { - switch (agp_bridge.size_type) { - case U8_APER_SIZE: - size = A_SIZE_8(temp)->size; - page_order = - A_SIZE_8(temp)->page_order; - num_entries = - A_SIZE_8(temp)->num_entries; - break; - case U16_APER_SIZE: - size = A_SIZE_16(temp)->size; - page_order = A_SIZE_16(temp)->page_order; - num_entries = A_SIZE_16(temp)->num_entries; - break; - case U32_APER_SIZE: - size = A_SIZE_32(temp)->size; - page_order = A_SIZE_32(temp)->page_order; - num_entries = A_SIZE_32(temp)->num_entries; - break; - /* This case will never really happen. */ - case FIXED_APER_SIZE: - case LVL2_APER_SIZE: - default: - size = page_order = num_entries = 0; - break; - } - - table = (char *) __get_free_pages(GFP_KERNEL, - page_order); - - if (table == NULL) { - i++; - switch (agp_bridge.size_type) { - case U8_APER_SIZE: - agp_bridge.current_size = A_IDX8(); - break; - case U16_APER_SIZE: - agp_bridge.current_size = A_IDX16(); - break; - case U32_APER_SIZE: - agp_bridge.current_size = A_IDX32(); - break; - /* This case will never really - * happen. - */ - case FIXED_APER_SIZE: - case LVL2_APER_SIZE: - default: - agp_bridge.current_size = - agp_bridge.current_size; - break; - } - temp = agp_bridge.current_size; - } else { - agp_bridge.aperture_size_idx = i; - } - } while ((table == NULL) && - (i < agp_bridge.num_aperture_sizes)); - } else { - size = ((aper_size_info_fixed *) temp)->size; - page_order = ((aper_size_info_fixed *) temp)->page_order; - num_entries = ((aper_size_info_fixed *) temp)->num_entries; - table = (char *) __get_free_pages(GFP_KERNEL, page_order); - } - - if (table == NULL) { - return -ENOMEM; - } - table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); - - for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) - SetPageReserved(page); - - agp_bridge.gatt_table_real = (unsigned long *) table; - CACHE_FLUSH(); - agp_bridge.gatt_table = ioremap_nocache(virt_to_phys(table), - (PAGE_SIZE * (1 << page_order))); - CACHE_FLUSH(); - - if (agp_bridge.gatt_table == NULL) { - for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) - ClearPageReserved(page); - - free_pages((unsigned long) table, page_order); - - return -ENOMEM; - } - agp_bridge.gatt_bus_addr = virt_to_phys(agp_bridge.gatt_table_real); - - for (i = 0; i < num_entries; i++) { - agp_bridge.gatt_table[i] = - (unsigned long) agp_bridge.scratch_page; - } - - return 0; -} - -static int agp_generic_suspend(void) -{ - return 0; -} - -static void agp_generic_resume(void) -{ - return; -} - -static int agp_generic_free_gatt_table(void) -{ - int page_order; - char *table, *table_end; - void *temp; - struct page *page; - - temp = agp_bridge.current_size; - - switch (agp_bridge.size_type) { - case U8_APER_SIZE: - page_order = A_SIZE_8(temp)->page_order; - break; - case U16_APER_SIZE: - page_order = A_SIZE_16(temp)->page_order; - break; - case U32_APER_SIZE: - page_order = A_SIZE_32(temp)->page_order; - break; - case FIXED_APER_SIZE: - page_order = A_SIZE_FIX(temp)->page_order; - break; - case LVL2_APER_SIZE: - /* The generic routines can't deal with 2 level gatt's */ - return -EINVAL; - break; - default: - page_order = 0; - break; - } - - /* Do not worry about freeing memory, because if this is - * called, then all agp memory is deallocated and removed - * from the table. - */ - - iounmap(agp_bridge.gatt_table); - table = (char *) agp_bridge.gatt_table_real; - table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); - - for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) - ClearPageReserved(page); - - free_pages((unsigned long) agp_bridge.gatt_table_real, page_order); - return 0; -} - -static int agp_generic_insert_memory(agp_memory * mem, - off_t pg_start, int type) -{ - int i, j, num_entries; - void *temp; - - temp = agp_bridge.current_size; - - switch (agp_bridge.size_type) { - case U8_APER_SIZE: - num_entries = A_SIZE_8(temp)->num_entries; - break; - case U16_APER_SIZE: - num_entries = A_SIZE_16(temp)->num_entries; - break; - case U32_APER_SIZE: - num_entries = A_SIZE_32(temp)->num_entries; - break; - case FIXED_APER_SIZE: - num_entries = A_SIZE_FIX(temp)->num_entries; - break; - case LVL2_APER_SIZE: - /* The generic routines can't deal with 2 level gatt's */ - return -EINVAL; - break; - default: - num_entries = 0; - break; - } - - if (type != 0 || mem->type != 0) { - /* The generic routines know nothing of memory types */ - return -EINVAL; - } - if ((pg_start + mem->page_count) > num_entries) { - return -EINVAL; - } - j = pg_start; - - while (j < (pg_start + mem->page_count)) { - if (!PGE_EMPTY(agp_bridge.gatt_table[j])) { - return -EBUSY; - } - j++; - } - - if (mem->is_flushed == FALSE) { - CACHE_FLUSH(); - mem->is_flushed = TRUE; - } - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - agp_bridge.gatt_table[j] = mem->memory[i]; - } - - agp_bridge.tlb_flush(mem); - return 0; -} - -static int agp_generic_remove_memory(agp_memory * mem, off_t pg_start, - int type) -{ - int i; - - if (type != 0 || mem->type != 0) { - /* The generic routines know nothing of memory types */ - return -EINVAL; - } - for (i = pg_start; i < (mem->page_count + pg_start); i++) { - agp_bridge.gatt_table[i] = - (unsigned long) agp_bridge.scratch_page; - } - - agp_bridge.tlb_flush(mem); - return 0; -} - -static agp_memory *agp_generic_alloc_by_type(size_t page_count, int type) -{ - return NULL; -} - -static void agp_generic_free_by_type(agp_memory * curr) -{ - if (curr->memory != NULL) { - vfree(curr->memory); - } - agp_free_key(curr->key); - kfree(curr); -} - -/* - * Basic Page Allocation Routines - - * These routines handle page allocation - * and by default they reserve the allocated - * memory. They also handle incrementing the - * current_memory_agp value, Which is checked - * against a maximum value. - */ - -static void *agp_generic_alloc_page(void) -{ - struct page * page; - - page = alloc_page(GFP_KERNEL); - if (page == NULL) - return 0; - - map_page_into_agp(page); - - get_page(page); - SetPageLocked(page); - atomic_inc(&agp_bridge.current_memory_agp); - return page_address(page); -} - -static void agp_generic_destroy_page(void *addr) -{ - struct page *page; - - if (addr == NULL) - return; - - page = virt_to_page(addr); - unmap_page_from_agp(page); - put_page(page); - unlock_page(page); - free_page((unsigned long)addr); - atomic_dec(&agp_bridge.current_memory_agp); -} - -/* End Basic Page Allocation Routines */ - -void agp_enable(u32 mode) -{ - if (agp_bridge.type == NOT_SUPPORTED) return; - agp_bridge.agp_enable(mode); -} - -/* End - Generic Agp routines */ - -#ifdef CONFIG_AGP_I810 -static aper_size_info_fixed intel_i810_sizes[] = -{ - {64, 16384, 4}, - /* The 32M mode still requires a 64k gatt */ - {32, 8192, 4} -}; - -#define AGP_DCACHE_MEMORY 1 -#define AGP_PHYS_MEMORY 2 - -static gatt_mask intel_i810_masks[] = -{ - {I810_PTE_VALID, 0}, - {(I810_PTE_VALID | I810_PTE_LOCAL), AGP_DCACHE_MEMORY}, - {I810_PTE_VALID, 0} -}; - -static struct _intel_i810_private { - struct pci_dev *i810_dev; /* device one */ - volatile u8 *registers; - int num_dcache_entries; -} intel_i810_private; - -static int intel_i810_fetch_size(void) -{ - u32 smram_miscc; - aper_size_info_fixed *values; - - pci_read_config_dword(agp_bridge.dev, I810_SMRAM_MISCC, &smram_miscc); - values = A_SIZE_FIX(agp_bridge.aperture_sizes); - - if ((smram_miscc & I810_GMS) == I810_GMS_DISABLE) { - printk(KERN_WARNING PFX "i810 is disabled\n"); - return 0; - } - if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) == I810_GFX_MEM_WIN_32M) { - agp_bridge.previous_size = - agp_bridge.current_size = (void *) (values + 1); - agp_bridge.aperture_size_idx = 1; - return values[1].size; - } else { - agp_bridge.previous_size = - agp_bridge.current_size = (void *) (values); - agp_bridge.aperture_size_idx = 0; - return values[0].size; - } - - return 0; -} - -static int intel_i810_configure(void) -{ - aper_size_info_fixed *current_size; - u32 temp; - int i; - - current_size = A_SIZE_FIX(agp_bridge.current_size); - - pci_read_config_dword(intel_i810_private.i810_dev, I810_MMADDR, &temp); - temp &= 0xfff80000; - - intel_i810_private.registers = - (volatile u8 *) ioremap(temp, 128 * 4096); - - if ((INREG32(intel_i810_private.registers, I810_DRAM_CTL) - & I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) { - /* This will need to be dynamically assigned */ - printk(KERN_INFO PFX "detected 4MB dedicated video ram.\n"); - intel_i810_private.num_dcache_entries = 1024; - } - pci_read_config_dword(intel_i810_private.i810_dev, I810_GMADDR, &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - OUTREG32(intel_i810_private.registers, I810_PGETBL_CTL, - agp_bridge.gatt_bus_addr | I810_PGETBL_ENABLED); - CACHE_FLUSH(); - - if (agp_bridge.needs_scratch_page == TRUE) { - for (i = 0; i < current_size->num_entries; i++) { - OUTREG32(intel_i810_private.registers, - I810_PTE_BASE + (i * 4), - agp_bridge.scratch_page); - } - } - return 0; -} - -static void intel_i810_cleanup(void) -{ - OUTREG32(intel_i810_private.registers, I810_PGETBL_CTL, 0); - iounmap((void *) intel_i810_private.registers); -} - -static void intel_i810_tlbflush(agp_memory * mem) -{ - return; -} - -static void intel_i810_agp_enable(u32 mode) -{ - return; -} - -static int intel_i810_insert_entries(agp_memory * mem, off_t pg_start, - int type) -{ - int i, j, num_entries; - void *temp; - - temp = agp_bridge.current_size; - num_entries = A_SIZE_FIX(temp)->num_entries; - - if ((pg_start + mem->page_count) > num_entries) { - return -EINVAL; - } - for (j = pg_start; j < (pg_start + mem->page_count); j++) { - if (!PGE_EMPTY(agp_bridge.gatt_table[j])) { - return -EBUSY; - } - } - - if (type != 0 || mem->type != 0) { - if ((type == AGP_DCACHE_MEMORY) && - (mem->type == AGP_DCACHE_MEMORY)) { - /* special insert */ - CACHE_FLUSH(); - for (i = pg_start; - i < (pg_start + mem->page_count); i++) { - OUTREG32(intel_i810_private.registers, - I810_PTE_BASE + (i * 4), - (i * 4096) | I810_PTE_LOCAL | - I810_PTE_VALID); - } - CACHE_FLUSH(); - agp_bridge.tlb_flush(mem); - return 0; - } - if((type == AGP_PHYS_MEMORY) && - (mem->type == AGP_PHYS_MEMORY)) { - goto insert; - } - return -EINVAL; - } - -insert: - CACHE_FLUSH(); - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - OUTREG32(intel_i810_private.registers, - I810_PTE_BASE + (j * 4), mem->memory[i]); - } - CACHE_FLUSH(); - - agp_bridge.tlb_flush(mem); - return 0; -} - -static int intel_i810_remove_entries(agp_memory * mem, off_t pg_start, - int type) -{ - int i; - - for (i = pg_start; i < (mem->page_count + pg_start); i++) { - OUTREG32(intel_i810_private.registers, - I810_PTE_BASE + (i * 4), - agp_bridge.scratch_page); - } - - CACHE_FLUSH(); - agp_bridge.tlb_flush(mem); - return 0; -} - -static agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type) -{ - agp_memory *new; - - if (type == AGP_DCACHE_MEMORY) { - if (pg_count != intel_i810_private.num_dcache_entries) { - return NULL; - } - new = agp_create_memory(1); - - if (new == NULL) { - return NULL; - } - new->type = AGP_DCACHE_MEMORY; - new->page_count = pg_count; - new->num_scratch_pages = 0; - vfree(new->memory); - MOD_INC_USE_COUNT; - return new; - } - if(type == AGP_PHYS_MEMORY) { - void *addr; - /* The I810 requires a physical address to program - * it's mouse pointer into hardware. However the - * Xserver still writes to it through the agp - * aperture - */ - if (pg_count != 1) { - return NULL; - } - new = agp_create_memory(1); - - if (new == NULL) { - return NULL; - } - MOD_INC_USE_COUNT; - addr = agp_bridge.agp_alloc_page(); - - if (addr == NULL) { - /* Free this structure */ - agp_free_memory(new); - return NULL; - } - new->memory[0] = agp_bridge.mask_memory(virt_to_phys(addr), type); - new->page_count = 1; - new->num_scratch_pages = 1; - new->type = AGP_PHYS_MEMORY; - new->physical = virt_to_phys((void *) new->memory[0]); - return new; - } - - return NULL; -} - -static void intel_i810_free_by_type(agp_memory * curr) -{ - agp_free_key(curr->key); - if(curr->type == AGP_PHYS_MEMORY) { - agp_bridge.agp_destroy_page( - phys_to_virt(curr->memory[0])); - vfree(curr->memory); - } - kfree(curr); - MOD_DEC_USE_COUNT; -} - -static unsigned long intel_i810_mask_memory(unsigned long addr, int type) -{ - /* Type checking must be done elsewhere */ - return addr | agp_bridge.masks[type].mask; -} - -static int __init intel_i810_setup(struct pci_dev *i810_dev) -{ - intel_i810_private.i810_dev = i810_dev; - - agp_bridge.masks = intel_i810_masks; - agp_bridge.num_of_masks = 2; - agp_bridge.aperture_sizes = (void *) intel_i810_sizes; - agp_bridge.size_type = FIXED_APER_SIZE; - agp_bridge.num_aperture_sizes = 2; - agp_bridge.dev_private_data = (void *) &intel_i810_private; - agp_bridge.needs_scratch_page = TRUE; - agp_bridge.configure = intel_i810_configure; - agp_bridge.fetch_size = intel_i810_fetch_size; - agp_bridge.cleanup = intel_i810_cleanup; - agp_bridge.tlb_flush = intel_i810_tlbflush; - agp_bridge.mask_memory = intel_i810_mask_memory; - agp_bridge.agp_enable = intel_i810_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = agp_generic_create_gatt_table; - agp_bridge.free_gatt_table = agp_generic_free_gatt_table; - agp_bridge.insert_memory = intel_i810_insert_entries; - agp_bridge.remove_memory = intel_i810_remove_entries; - agp_bridge.alloc_by_type = intel_i810_alloc_by_type; - agp_bridge.free_by_type = intel_i810_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 0; - - return 0; -} - -static aper_size_info_fixed intel_i830_sizes[] = -{ - {128, 32768, 5}, - /* The 64M mode still requires a 128k gatt */ - {64, 16384, 5} -}; - -static struct _intel_i830_private { - struct pci_dev *i830_dev; /* device one */ - volatile u8 *registers; - int gtt_entries; -} intel_i830_private; - -static void intel_i830_init_gtt_entries(void) { - u16 gmch_ctrl; - int gtt_entries; - u8 rdct; - static const int ddt[4] = { 0, 16, 32, 64 }; - - pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl); - - switch (gmch_ctrl & I830_GMCH_GMS_MASK) { - case I830_GMCH_GMS_STOLEN_512: - gtt_entries = KB(512); - printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1)); - break; - case I830_GMCH_GMS_STOLEN_1024: - gtt_entries = MB(1); - printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1)); - break; - case I830_GMCH_GMS_STOLEN_8192: - gtt_entries = MB(8); - printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1)); - break; - case I830_GMCH_GMS_LOCAL: - rdct = INREG8(intel_i830_private.registers,I830_RDRAM_CHANNEL_TYPE); - gtt_entries = (I830_RDRAM_ND(rdct) + 1) * MB(ddt[I830_RDRAM_DDT(rdct)]); - printk(KERN_INFO PFX "detected %dK local memory.\n",gtt_entries / KB(1)); - break; - default: - printk(KERN_INFO PFX "no video memory detected.\n"); - gtt_entries = 0; - break; - } - - gtt_entries /= KB(4); - - intel_i830_private.gtt_entries = gtt_entries; -} - -/* The intel i830 automatically initializes the agp aperture during POST. - * Use the memory already set aside for in the GTT. - */ -static int intel_i830_create_gatt_table(void) -{ - int page_order; - aper_size_info_fixed *size; - int num_entries; - u32 temp; - - size = agp_bridge.current_size; - page_order = size->page_order; - num_entries = size->num_entries; - agp_bridge.gatt_table_real = 0; - - pci_read_config_dword(intel_i830_private.i830_dev,I810_MMADDR,&temp); - temp &= 0xfff80000; - - intel_i830_private.registers = (volatile u8 *) ioremap(temp,128 * 4096); - if (!intel_i830_private.registers) return (-ENOMEM); - - temp = INREG32(intel_i830_private.registers,I810_PGETBL_CTL) & 0xfffff000; - CACHE_FLUSH(); - - /* we have to call this as early as possible after the MMIO base address is known */ - intel_i830_init_gtt_entries(); - - agp_bridge.gatt_table = NULL; - - agp_bridge.gatt_bus_addr = temp; - - return(0); -} - -/* Return the gatt table to a sane state. Use the top of stolen - * memory for the GTT. - */ -static int intel_i830_free_gatt_table(void) -{ - return(0); -} - -static int intel_i830_fetch_size(void) -{ - u16 gmch_ctrl; - aper_size_info_fixed *values; - - pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl); - values = A_SIZE_FIX(agp_bridge.aperture_sizes); - - if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) { - agp_bridge.previous_size = agp_bridge.current_size = (void *) values; - agp_bridge.aperture_size_idx = 0; - return(values[0].size); - } else { - agp_bridge.previous_size = agp_bridge.current_size = (void *) values; - agp_bridge.aperture_size_idx = 1; - return(values[1].size); - } - - return(0); -} - -static int intel_i830_configure(void) -{ - aper_size_info_fixed *current_size; - u32 temp; - u16 gmch_ctrl; - int i; - - current_size = A_SIZE_FIX(agp_bridge.current_size); - - pci_read_config_dword(intel_i830_private.i830_dev,I810_GMADDR,&temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl); - gmch_ctrl |= I830_GMCH_ENABLED; - pci_write_config_word(agp_bridge.dev,I830_GMCH_CTRL,gmch_ctrl); - - OUTREG32(intel_i830_private.registers,I810_PGETBL_CTL,agp_bridge.gatt_bus_addr | I810_PGETBL_ENABLED); - CACHE_FLUSH(); - - if (agp_bridge.needs_scratch_page == TRUE) - for (i = intel_i830_private.gtt_entries; i < current_size->num_entries; i++) - OUTREG32(intel_i830_private.registers,I810_PTE_BASE + (i * 4),agp_bridge.scratch_page); - - return (0); -} - -static void intel_i830_cleanup(void) -{ - iounmap((void *) intel_i830_private.registers); -} - -static int intel_i830_insert_entries(agp_memory *mem,off_t pg_start,int type) -{ - int i,j,num_entries; - void *temp; - - temp = agp_bridge.current_size; - num_entries = A_SIZE_FIX(temp)->num_entries; - - if (pg_start < intel_i830_private.gtt_entries) { - printk (KERN_DEBUG "pg_start == 0x%.8lx,intel_i830_private.gtt_entries == 0x%.8x\n", - pg_start,intel_i830_private.gtt_entries); - - printk ("Trying to insert into local/stolen memory\n"); - return (-EINVAL); - } - - if ((pg_start + mem->page_count) > num_entries) - return (-EINVAL); - - /* The i830 can't check the GTT for entries since its read only, - * depend on the caller to make the correct offset decisions. - */ - - if ((type != 0 && type != AGP_PHYS_MEMORY) || - (mem->type != 0 && mem->type != AGP_PHYS_MEMORY)) - return (-EINVAL); - - CACHE_FLUSH(); - - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) - OUTREG32(intel_i830_private.registers,I810_PTE_BASE + (j * 4),mem->memory[i]); - - CACHE_FLUSH(); - - agp_bridge.tlb_flush(mem); - - return(0); -} - -static int intel_i830_remove_entries(agp_memory *mem,off_t pg_start,int type) -{ - int i; - - CACHE_FLUSH (); - - if (pg_start < intel_i830_private.gtt_entries) { - printk ("Trying to disable local/stolen memory\n"); - return (-EINVAL); - } - - for (i = pg_start; i < (mem->page_count + pg_start); i++) - OUTREG32(intel_i830_private.registers,I810_PTE_BASE + (i * 4),agp_bridge.scratch_page); - - CACHE_FLUSH(); - - agp_bridge.tlb_flush(mem); - - return (0); -} - -static agp_memory *intel_i830_alloc_by_type(size_t pg_count,int type) -{ - agp_memory *nw; - - /* always return NULL for now */ - if (type == AGP_DCACHE_MEMORY) return(NULL); - - if (type == AGP_PHYS_MEMORY) { - void *addr; - - /* The i830 requires a physical address to program - * it's mouse pointer into hardware. However the - * Xserver still writes to it through the agp - * aperture - */ - - if (pg_count != 1) return(NULL); - - nw = agp_create_memory(1); - - if (nw == NULL) return(NULL); - - MOD_INC_USE_COUNT; - addr = agp_bridge.agp_alloc_page(); - if (addr == NULL) { - /* free this structure */ - agp_free_memory(nw); - return(NULL); - } - - nw->memory[0] = agp_bridge.mask_memory(virt_to_phys(addr),type); - nw->page_count = 1; - nw->num_scratch_pages = 1; - nw->type = AGP_PHYS_MEMORY; - nw->physical = virt_to_phys(addr); - return(nw); - } - - return(NULL); -} - -static int __init intel_i830_setup(struct pci_dev *i830_dev) -{ - intel_i830_private.i830_dev = i830_dev; - - agp_bridge.masks = intel_i810_masks; - agp_bridge.num_of_masks = 3; - agp_bridge.aperture_sizes = (void *) intel_i830_sizes; - agp_bridge.size_type = FIXED_APER_SIZE; - agp_bridge.num_aperture_sizes = 2; - - agp_bridge.dev_private_data = (void *) &intel_i830_private; - agp_bridge.needs_scratch_page = TRUE; - - agp_bridge.configure = intel_i830_configure; - agp_bridge.fetch_size = intel_i830_fetch_size; - agp_bridge.cleanup = intel_i830_cleanup; - agp_bridge.tlb_flush = intel_i810_tlbflush; - agp_bridge.mask_memory = intel_i810_mask_memory; - agp_bridge.agp_enable = intel_i810_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - - agp_bridge.create_gatt_table = intel_i830_create_gatt_table; - agp_bridge.free_gatt_table = intel_i830_free_gatt_table; - - agp_bridge.insert_memory = intel_i830_insert_entries; - agp_bridge.remove_memory = intel_i830_remove_entries; - agp_bridge.alloc_by_type = intel_i830_alloc_by_type; - agp_bridge.free_by_type = intel_i810_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 0; - - return(0); -} - -#endif /* CONFIG_AGP_I810 */ - -#ifdef CONFIG_AGP_I460 - -/* BIOS configures the chipset so that one of two apbase registers are used */ -static u8 intel_i460_dynamic_apbase = 0x10; - -/* 460 supports multiple GART page sizes, so GART pageshift is dynamic */ -static u8 intel_i460_pageshift = 12; -static u32 intel_i460_pagesize; - -/* Keep track of which is larger, chipset or kernel page size. */ -static u32 intel_i460_cpk = 1; - -/* Structure for tracking partial use of 4MB GART pages */ -static u32 **i460_pg_detail = NULL; -static u32 *i460_pg_count = NULL; - -#define I460_CPAGES_PER_KPAGE (PAGE_SIZE >> intel_i460_pageshift) -#define I460_KPAGES_PER_CPAGE ((1 << intel_i460_pageshift) >> PAGE_SHIFT) - -#define I460_SRAM_IO_DISABLE (1 << 4) -#define I460_BAPBASE_ENABLE (1 << 3) -#define I460_AGPSIZ_MASK 0x7 -#define I460_4M_PS (1 << 1) - -#define log2(x) ffz(~(x)) - -static inline void intel_i460_read_back (volatile u32 *entry) -{ - /* - * The 460 spec says we have to read the last location written to - * make sure that all writes have taken effect - */ - *entry; -} - -static int intel_i460_fetch_size(void) -{ - int i; - u8 temp; - aper_size_info_8 *values; - - /* Determine the GART page size */ - pci_read_config_byte(agp_bridge.dev, INTEL_I460_GXBCTL, &temp); - intel_i460_pageshift = (temp & I460_4M_PS) ? 22 : 12; - intel_i460_pagesize = 1UL << intel_i460_pageshift; - - values = A_SIZE_8(agp_bridge.aperture_sizes); - - pci_read_config_byte(agp_bridge.dev, INTEL_I460_AGPSIZ, &temp); - - /* Exit now if the IO drivers for the GART SRAMS are turned off */ - if (temp & I460_SRAM_IO_DISABLE) { - printk(KERN_ERR PFX "GART SRAMS disabled on 460GX chipset\n"); - printk(KERN_ERR PFX "AGPGART operation not possible\n"); - return 0; - } - - /* Make sure we don't try to create an 2 ^ 23 entry GATT */ - if ((intel_i460_pageshift == 0) && ((temp & I460_AGPSIZ_MASK) == 4)) { - printk(KERN_ERR PFX "We can't have a 32GB aperture with 4KB GART pages\n"); - return 0; - } - - /* Determine the proper APBASE register */ - if (temp & I460_BAPBASE_ENABLE) - intel_i460_dynamic_apbase = INTEL_I460_BAPBASE; - else - intel_i460_dynamic_apbase = INTEL_I460_APBASE; - - for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { - /* - * Dynamically calculate the proper num_entries and page_order values for - * the define aperture sizes. Take care not to shift off the end of - * values[i].size. - */ - values[i].num_entries = (values[i].size << 8) >> (intel_i460_pageshift - 12); - values[i].page_order = log2((sizeof(u32)*values[i].num_entries) >> PAGE_SHIFT); - } - - for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { - /* Neglect control bits when matching up size_value */ - if ((temp & I460_AGPSIZ_MASK) == values[i].size_value) { - agp_bridge.previous_size = agp_bridge.current_size = (void *) (values + i); - agp_bridge.aperture_size_idx = i; - return values[i].size; - } - } - - return 0; -} - -/* There isn't anything to do here since 460 has no GART TLB. */ -static void intel_i460_tlb_flush(agp_memory * mem) -{ - return; -} - -/* - * This utility function is needed to prevent corruption of the control bits - * which are stored along with the aperture size in 460's AGPSIZ register - */ -static void intel_i460_write_agpsiz(u8 size_value) -{ - u8 temp; - - pci_read_config_byte(agp_bridge.dev, INTEL_I460_AGPSIZ, &temp); - pci_write_config_byte(agp_bridge.dev, INTEL_I460_AGPSIZ, - ((temp & ~I460_AGPSIZ_MASK) | size_value)); -} - -static void intel_i460_cleanup(void) -{ - aper_size_info_8 *previous_size; - - previous_size = A_SIZE_8(agp_bridge.previous_size); - intel_i460_write_agpsiz(previous_size->size_value); - - if (intel_i460_cpk == 0) { - vfree(i460_pg_detail); - vfree(i460_pg_count); - } -} - - -/* Control bits for Out-Of-GART coherency and Burst Write Combining */ -#define I460_GXBCTL_OOG (1UL << 0) -#define I460_GXBCTL_BWC (1UL << 2) - -static int intel_i460_configure(void) -{ - union { - u32 small[2]; - u64 large; - } temp; - u8 scratch; - int i; - - aper_size_info_8 *current_size; - - temp.large = 0; - - current_size = A_SIZE_8(agp_bridge.current_size); - intel_i460_write_agpsiz(current_size->size_value); - - /* - * Do the necessary rigmarole to read all eight bytes of APBASE. - * This has to be done since the AGP aperture can be above 4GB on - * 460 based systems. - */ - pci_read_config_dword(agp_bridge.dev, intel_i460_dynamic_apbase, &(temp.small[0])); - pci_read_config_dword(agp_bridge.dev, intel_i460_dynamic_apbase + 4, &(temp.small[1])); - - /* Clear BAR control bits */ - agp_bridge.gart_bus_addr = temp.large & ~((1UL << 3) - 1); - - pci_read_config_byte(agp_bridge.dev, INTEL_I460_GXBCTL, &scratch); - pci_write_config_byte(agp_bridge.dev, INTEL_I460_GXBCTL, - (scratch & 0x02) | I460_GXBCTL_OOG | I460_GXBCTL_BWC); - - /* - * Initialize partial allocation trackers if a GART page is bigger than - * a kernel page. - */ - if (I460_CPAGES_PER_KPAGE >= 1) { - intel_i460_cpk = 1; - } else { - intel_i460_cpk = 0; - - i460_pg_detail = vmalloc(sizeof(*i460_pg_detail) * current_size->num_entries); - i460_pg_count = vmalloc(sizeof(*i460_pg_count) * current_size->num_entries); - - for (i = 0; i < current_size->num_entries; i++) { - i460_pg_count[i] = 0; - i460_pg_detail[i] = NULL; - } - } - return 0; -} - -static int intel_i460_create_gatt_table(void) -{ - char *table; - int i; - int page_order; - int num_entries; - void *temp; - - /* - * Load up the fixed address of the GART SRAMS which hold our - * GATT table. - */ - table = (char *) __va(INTEL_I460_ATTBASE); - - temp = agp_bridge.current_size; - page_order = A_SIZE_8(temp)->page_order; - num_entries = A_SIZE_8(temp)->num_entries; - - agp_bridge.gatt_table_real = (u32 *) table; - agp_bridge.gatt_table = ioremap_nocache(virt_to_phys(table), - (PAGE_SIZE * (1 << page_order))); - agp_bridge.gatt_bus_addr = virt_to_phys(agp_bridge.gatt_table_real); - - for (i = 0; i < num_entries; i++) { - agp_bridge.gatt_table[i] = 0; - } - - intel_i460_read_back(agp_bridge.gatt_table + i - 1); - return 0; -} - -static int intel_i460_free_gatt_table(void) -{ - int num_entries; - int i; - void *temp; - - temp = agp_bridge.current_size; - - num_entries = A_SIZE_8(temp)->num_entries; - - for (i = 0; i < num_entries; i++) { - agp_bridge.gatt_table[i] = 0; - } - - intel_i460_read_back(agp_bridge.gatt_table + i - 1); - - iounmap(agp_bridge.gatt_table); - return 0; -} - -/* These functions are called when PAGE_SIZE exceeds the GART page size */ - -static int intel_i460_insert_memory_cpk(agp_memory * mem, off_t pg_start, int type) -{ - int i, j, k, num_entries; - void *temp; - unsigned long paddr; - - /* - * The rest of the kernel will compute page offsets in terms of - * PAGE_SIZE. - */ - pg_start = I460_CPAGES_PER_KPAGE * pg_start; - - temp = agp_bridge.current_size; - num_entries = A_SIZE_8(temp)->num_entries; - - if ((pg_start + I460_CPAGES_PER_KPAGE * mem->page_count) > num_entries) { - printk(KERN_ERR PFX "Looks like we're out of AGP memory\n"); - return -EINVAL; - } - - j = pg_start; - while (j < (pg_start + I460_CPAGES_PER_KPAGE * mem->page_count)) { - if (!PGE_EMPTY(agp_bridge.gatt_table[j])) { - return -EBUSY; - } - j++; - } - -#if 0 - /* not necessary since 460 GART is operated in coherent mode... */ - if (mem->is_flushed == FALSE) { - CACHE_FLUSH(); - mem->is_flushed = TRUE; - } -#endif - - for (i = 0, j = pg_start; i < mem->page_count; i++) { - paddr = mem->memory[i]; - for (k = 0; k < I460_CPAGES_PER_KPAGE; k++, j++, paddr += intel_i460_pagesize) - agp_bridge.gatt_table[j] = (u32) agp_bridge.mask_memory(paddr, mem->type); - } - - intel_i460_read_back(agp_bridge.gatt_table + j - 1); - return 0; -} - -static int intel_i460_remove_memory_cpk(agp_memory * mem, off_t pg_start, int type) -{ - int i; - - pg_start = I460_CPAGES_PER_KPAGE * pg_start; - - for (i = pg_start; i < (pg_start + I460_CPAGES_PER_KPAGE * mem->page_count); i++) - agp_bridge.gatt_table[i] = 0; - - intel_i460_read_back(agp_bridge.gatt_table + i - 1); - return 0; -} - -/* - * These functions are called when the GART page size exceeds PAGE_SIZE. - * - * This situation is interesting since AGP memory allocations that are - * smaller than a single GART page are possible. The structures i460_pg_count - * and i460_pg_detail track partial allocation of the large GART pages to - * work around this issue. - * - * i460_pg_count[pg_num] tracks the number of kernel pages in use within - * GART page pg_num. i460_pg_detail[pg_num] is an array containing a - * psuedo-GART entry for each of the aforementioned kernel pages. The whole - * of i460_pg_detail is equivalent to a giant GATT with page size equal to - * that of the kernel. - */ - -static void *intel_i460_alloc_large_page(int pg_num) -{ - int i; - void *bp, *bp_end; - struct page *page; - - i460_pg_detail[pg_num] = (void *) vmalloc(sizeof(u32) * I460_KPAGES_PER_CPAGE); - if (i460_pg_detail[pg_num] == NULL) { - printk(KERN_ERR PFX "Out of memory, we're in trouble...\n"); - return NULL; - } - - for (i = 0; i < I460_KPAGES_PER_CPAGE; i++) - i460_pg_detail[pg_num][i] = 0; - - bp = (void *) __get_free_pages(GFP_KERNEL, intel_i460_pageshift - PAGE_SHIFT); - if (bp == NULL) { - printk(KERN_ERR PFX "Couldn't alloc 4M GART page...\n"); - return NULL; - } - - bp_end = bp + ((PAGE_SIZE * (1 << (intel_i460_pageshift - PAGE_SHIFT))) - 1); - - for (page = virt_to_page(bp); page <= virt_to_page(bp_end); page++) { - atomic_inc(&agp_bridge.current_memory_agp); - } - return bp; -} - -static void intel_i460_free_large_page(int pg_num, unsigned long addr) -{ - struct page *page; - void *bp, *bp_end; - - bp = (void *) __va(addr); - bp_end = bp + (PAGE_SIZE * (1 << (intel_i460_pageshift - PAGE_SHIFT))); - - vfree(i460_pg_detail[pg_num]); - i460_pg_detail[pg_num] = NULL; - - for (page = virt_to_page(bp); page < virt_to_page(bp_end); page++) { - atomic_dec(&agp_bridge.current_memory_agp); - } - - free_pages((unsigned long) bp, intel_i460_pageshift - PAGE_SHIFT); -} - -static int intel_i460_insert_memory_kpc(agp_memory * mem, off_t pg_start, int type) -{ - int i, pg, start_pg, end_pg, start_offset, end_offset, idx; - int num_entries; - void *temp; - unsigned long paddr; - - temp = agp_bridge.current_size; - num_entries = A_SIZE_8(temp)->num_entries; - - /* Figure out what pg_start means in terms of our large GART pages */ - start_pg = pg_start / I460_KPAGES_PER_CPAGE; - start_offset = pg_start % I460_KPAGES_PER_CPAGE; - end_pg = (pg_start + mem->page_count - 1) / I460_KPAGES_PER_CPAGE; - end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_CPAGE; - - if (end_pg > num_entries) { - printk(KERN_ERR PFX "Looks like we're out of AGP memory\n"); - return -EINVAL; - } - - /* Check if the requested region of the aperture is free */ - for (pg = start_pg; pg <= end_pg; pg++) { - /* Allocate new GART pages if necessary */ - if (i460_pg_detail[pg] == NULL) { - temp = intel_i460_alloc_large_page(pg); - if (temp == NULL) - return -ENOMEM; - agp_bridge.gatt_table[pg] = agp_bridge.mask_memory((unsigned long) temp, - 0); - intel_i460_read_back(agp_bridge.gatt_table + pg); - } - - for (idx = ((pg == start_pg) ? start_offset : 0); - idx < ((pg == end_pg) ? (end_offset + 1) : I460_KPAGES_PER_CPAGE); - idx++) - { - if (i460_pg_detail[pg][idx] != 0) - return -EBUSY; - } - } - -#if 0 - /* not necessary since 460 GART is operated in coherent mode... */ - if (mem->is_flushed == FALSE) { - CACHE_FLUSH(); - mem->is_flushed = TRUE; - } -#endif - - for (pg = start_pg, i = 0; pg <= end_pg; pg++) { - paddr = agp_bridge.unmask_memory(agp_bridge.gatt_table[pg]); - for (idx = ((pg == start_pg) ? start_offset : 0); - idx < ((pg == end_pg) ? (end_offset + 1) : I460_KPAGES_PER_CPAGE); - idx++, i++) - { - mem->memory[i] = paddr + (idx * PAGE_SIZE); - i460_pg_detail[pg][idx] = agp_bridge.mask_memory(mem->memory[i], - mem->type); - i460_pg_count[pg]++; - } - } - - return 0; -} - -static int intel_i460_remove_memory_kpc(agp_memory * mem, off_t pg_start, int type) -{ - int i, pg, start_pg, end_pg, start_offset, end_offset, idx; - int num_entries; - void *temp; - unsigned long paddr; - - temp = agp_bridge.current_size; - num_entries = A_SIZE_8(temp)->num_entries; - - /* Figure out what pg_start means in terms of our large GART pages */ - start_pg = pg_start / I460_KPAGES_PER_CPAGE; - start_offset = pg_start % I460_KPAGES_PER_CPAGE; - end_pg = (pg_start + mem->page_count - 1) / I460_KPAGES_PER_CPAGE; - end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_CPAGE; - - for (i = 0, pg = start_pg; pg <= end_pg; pg++) { - for (idx = ((pg == start_pg) ? start_offset : 0); - idx < ((pg == end_pg) ? (end_offset + 1) : I460_KPAGES_PER_CPAGE); - idx++, i++) - { - mem->memory[i] = 0; - i460_pg_detail[pg][idx] = 0; - i460_pg_count[pg]--; - } - - /* Free GART pages if they are unused */ - if (i460_pg_count[pg] == 0) { - paddr = agp_bridge.unmask_memory(agp_bridge.gatt_table[pg]); - agp_bridge.gatt_table[pg] = agp_bridge.scratch_page; - intel_i460_read_back(agp_bridge.gatt_table + pg); - intel_i460_free_large_page(pg, paddr); - } - } - return 0; -} - -/* Dummy routines to call the approriate {cpk,kpc} function */ - -static int intel_i460_insert_memory(agp_memory * mem, off_t pg_start, int type) -{ - if (intel_i460_cpk) - return intel_i460_insert_memory_cpk(mem, pg_start, type); - else - return intel_i460_insert_memory_kpc(mem, pg_start, type); -} - -static int intel_i460_remove_memory(agp_memory * mem, off_t pg_start, int type) -{ - if (intel_i460_cpk) - return intel_i460_remove_memory_cpk(mem, pg_start, type); - else - return intel_i460_remove_memory_kpc(mem, pg_start, type); -} - -/* - * If the kernel page size is smaller that the chipset page size, we don't - * want to allocate memory until we know where it is to be bound in the - * aperture (a multi-kernel-page alloc might fit inside of an already - * allocated GART page). Consequently, don't allocate or free anything - * if i460_cpk (meaning chipset pages per kernel page) isn't set. - * - * Let's just hope nobody counts on the allocated AGP memory being there - * before bind time (I don't think current drivers do)... - */ -static void * intel_i460_alloc_page(void) -{ - if (intel_i460_cpk) - return agp_generic_alloc_page(); - - /* Returning NULL would cause problems */ - /* AK: really dubious code. */ - return (void *)~0UL; -} - -static void intel_i460_destroy_page(void *page) -{ - if (intel_i460_cpk) - agp_generic_destroy_page(page); -} - -static gatt_mask intel_i460_masks[] = -{ - { - INTEL_I460_GATT_VALID | INTEL_I460_GATT_COHERENT, - 0 - } -}; - -static unsigned long intel_i460_mask_memory(unsigned long addr, int type) -{ - /* Make sure the returned address is a valid GATT entry */ - return (agp_bridge.masks[0].mask - | (((addr & ~((1 << intel_i460_pageshift) - 1)) & 0xffffff000) >> 12)); -} - -static unsigned long intel_i460_unmask_memory(unsigned long addr) -{ - /* Turn a GATT entry into a physical address */ - return ((addr & 0xffffff) << 12); -} - -static aper_size_info_8 intel_i460_sizes[3] = -{ - /* - * The 32GB aperture is only available with a 4M GART page size. - * Due to the dynamic GART page size, we can't figure out page_order - * or num_entries until runtime. - */ - {32768, 0, 0, 4}, - {1024, 0, 0, 2}, - {256, 0, 0, 1} -}; - -static int __init intel_i460_setup (struct pci_dev *pdev __attribute__((unused))) -{ - agp_bridge.masks = intel_i460_masks; - agp_bridge.aperture_sizes = (void *) intel_i460_sizes; - agp_bridge.size_type = U8_APER_SIZE; - agp_bridge.num_aperture_sizes = 3; - agp_bridge.dev_private_data = NULL; - agp_bridge.needs_scratch_page = FALSE; - agp_bridge.configure = intel_i460_configure; - agp_bridge.fetch_size = intel_i460_fetch_size; - agp_bridge.cleanup = intel_i460_cleanup; - agp_bridge.tlb_flush = intel_i460_tlb_flush; - agp_bridge.mask_memory = intel_i460_mask_memory; - agp_bridge.unmask_memory = intel_i460_unmask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = intel_i460_create_gatt_table; - agp_bridge.free_gatt_table = intel_i460_free_gatt_table; - agp_bridge.insert_memory = intel_i460_insert_memory; - agp_bridge.remove_memory = intel_i460_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = intel_i460_alloc_page; - agp_bridge.agp_destroy_page = intel_i460_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 1; - return 0; -} - -#endif /* CONFIG_AGP_I460 */ - -#ifdef CONFIG_AGP_INTEL - -static int intel_fetch_size(void) -{ - int i; - u16 temp; - aper_size_info_16 *values; - - pci_read_config_word(agp_bridge.dev, INTEL_APSIZE, &temp); - values = A_SIZE_16(agp_bridge.aperture_sizes); - - for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { - if (temp == values[i].size_value) { - agp_bridge.previous_size = - agp_bridge.current_size = (void *) (values + i); - agp_bridge.aperture_size_idx = i; - return values[i].size; - } - } - - return 0; -} - - -static int intel_8xx_fetch_size(void) -{ - int i; - u8 temp; - aper_size_info_8 *values; - - pci_read_config_byte(agp_bridge.dev, INTEL_APSIZE, &temp); - values = A_SIZE_8(agp_bridge.aperture_sizes); - - for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { - if (temp == values[i].size_value) { - agp_bridge.previous_size = - agp_bridge.current_size = (void *) (values + i); - agp_bridge.aperture_size_idx = i; - return values[i].size; - } - } - - return 0; -} - -static void intel_tlbflush(agp_memory * mem) -{ - pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x2200); - pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x2280); -} - - -static void intel_8xx_tlbflush(agp_memory * mem) -{ - u32 temp; - pci_read_config_dword(agp_bridge.dev, INTEL_AGPCTRL, &temp); - pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, temp & ~(1 << 7)); - pci_read_config_dword(agp_bridge.dev, INTEL_AGPCTRL, &temp); - pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, temp | (1 << 7)); -} - - -static void intel_cleanup(void) -{ - u16 temp; - aper_size_info_16 *previous_size; - - previous_size = A_SIZE_16(agp_bridge.previous_size); - pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp); - pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG, temp & ~(1 << 9)); - pci_write_config_word(agp_bridge.dev, INTEL_APSIZE, - previous_size->size_value); -} - - -static void intel_8xx_cleanup(void) -{ - u16 temp; - aper_size_info_8 *previous_size; - - previous_size = A_SIZE_8(agp_bridge.previous_size); - pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp); - pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG, temp & ~(1 << 9)); - pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, - previous_size->size_value); -} - - -static int intel_configure(void) -{ - u32 temp; - u16 temp2; - aper_size_info_16 *current_size; - - current_size = A_SIZE_16(agp_bridge.current_size); - - /* aperture size */ - pci_write_config_word(agp_bridge.dev, INTEL_APSIZE, - current_size->size_value); - - /* address to map to */ - pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - /* attbase - aperture base */ - pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, - agp_bridge.gatt_bus_addr); - - /* agpctrl */ - pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x2280); - - /* paccfg/nbxcfg */ - pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp2); - pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG, - (temp2 & ~(1 << 10)) | (1 << 9)); - /* clear any possible error conditions */ - pci_write_config_byte(agp_bridge.dev, INTEL_ERRSTS + 1, 7); - return 0; -} - -static void intel_820_tlbflush(agp_memory * mem) -{ - return; -} - -static void intel_820_cleanup(void) -{ - u8 temp; - aper_size_info_8 *previous_size; - - previous_size = A_SIZE_8(agp_bridge.previous_size); - pci_read_config_byte(agp_bridge.dev, INTEL_I820_RDCR, &temp); - pci_write_config_byte(agp_bridge.dev, INTEL_I820_RDCR, - temp & ~(1 << 1)); - pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, - previous_size->size_value); -} - - -static int intel_820_configure(void) -{ - u32 temp; - u8 temp2; - aper_size_info_8 *current_size; - - current_size = A_SIZE_8(agp_bridge.current_size); - - /* aperture size */ - pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, - current_size->size_value); - - /* address to map to */ - pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - /* attbase - aperture base */ - pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, - agp_bridge.gatt_bus_addr); - - /* agpctrl */ - pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); - - /* global enable aperture access */ - /* This flag is not accessed through MCHCFG register as in */ - /* i850 chipset. */ - pci_read_config_byte(agp_bridge.dev, INTEL_I820_RDCR, &temp2); - pci_write_config_byte(agp_bridge.dev, INTEL_I820_RDCR, - temp2 | (1 << 1)); - /* clear any possible AGP-related error conditions */ - pci_write_config_word(agp_bridge.dev, INTEL_I820_ERRSTS, 0x001c); - return 0; -} - -static int intel_840_configure(void) -{ - u32 temp; - u16 temp2; - aper_size_info_8 *current_size; - - current_size = A_SIZE_8(agp_bridge.current_size); - - /* aperture size */ - pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, - current_size->size_value); - - /* address to map to */ - pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - /* attbase - aperture base */ - pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, - agp_bridge.gatt_bus_addr); - - /* agpctrl */ - pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); - - /* mcgcfg */ - pci_read_config_word(agp_bridge.dev, INTEL_I840_MCHCFG, &temp2); - pci_write_config_word(agp_bridge.dev, INTEL_I840_MCHCFG, - temp2 | (1 << 9)); - /* clear any possible error conditions */ - pci_write_config_word(agp_bridge.dev, INTEL_I840_ERRSTS, 0xc000); - return 0; -} - -static int intel_845_configure(void) -{ - u32 temp; - u8 temp2; - aper_size_info_8 *current_size; - - current_size = A_SIZE_8(agp_bridge.current_size); - - /* aperture size */ - pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, - current_size->size_value); - - /* address to map to */ - pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - /* attbase - aperture base */ - pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, - agp_bridge.gatt_bus_addr); - - /* agpctrl */ - pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); - - /* agpm */ - pci_read_config_byte(agp_bridge.dev, INTEL_I845_AGPM, &temp2); - pci_write_config_byte(agp_bridge.dev, INTEL_I845_AGPM, - temp2 | (1 << 1)); - /* clear any possible error conditions */ - pci_write_config_word(agp_bridge.dev, INTEL_I845_ERRSTS, 0x001c); - return 0; -} - -static int intel_850_configure(void) -{ - u32 temp; - u16 temp2; - aper_size_info_8 *current_size; - - current_size = A_SIZE_8(agp_bridge.current_size); - - /* aperture size */ - pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, - current_size->size_value); - - /* address to map to */ - pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - /* attbase - aperture base */ - pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, - agp_bridge.gatt_bus_addr); - - /* agpctrl */ - pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); - - /* mcgcfg */ - pci_read_config_word(agp_bridge.dev, INTEL_I850_MCHCFG, &temp2); - pci_write_config_word(agp_bridge.dev, INTEL_I850_MCHCFG, - temp2 | (1 << 9)); - /* clear any possible AGP-related error conditions */ - pci_write_config_word(agp_bridge.dev, INTEL_I850_ERRSTS, 0x001c); - return 0; -} - -static int intel_860_configure(void) -{ - u32 temp; - u16 temp2; - aper_size_info_8 *current_size; - - current_size = A_SIZE_8(agp_bridge.current_size); - - /* aperture size */ - pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, - current_size->size_value); - - /* address to map to */ - pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - /* attbase - aperture base */ - pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, - agp_bridge.gatt_bus_addr); - - /* agpctrl */ - pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); - - /* mcgcfg */ - pci_read_config_word(agp_bridge.dev, INTEL_I860_MCHCFG, &temp2); - pci_write_config_word(agp_bridge.dev, INTEL_I860_MCHCFG, - temp2 | (1 << 9)); - /* clear any possible AGP-related error conditions */ - pci_write_config_word(agp_bridge.dev, INTEL_I860_ERRSTS, 0xf700); - return 0; -} - -static int intel_830mp_configure(void) -{ - u32 temp; - u16 temp2; - aper_size_info_8 *current_size; - - current_size = A_SIZE_8(agp_bridge.current_size); - - /* aperture size */ - pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, - current_size->size_value); - - /* address to map to */ - pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - /* attbase - aperture base */ - pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, - agp_bridge.gatt_bus_addr); - - /* agpctrl */ - pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); - - /* gmch */ - pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp2); - pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG, - temp2 | (1 << 9)); - /* clear any possible AGP-related error conditions */ - pci_write_config_word(agp_bridge.dev, INTEL_I830_ERRSTS, 0x1c); - return 0; -} - -static unsigned long intel_mask_memory(unsigned long addr, int type) -{ - /* Memory type is ignored */ - - return addr | agp_bridge.masks[0].mask; -} - -static void intel_resume(void) -{ - intel_configure(); -} - -/* Setup function */ -static gatt_mask intel_generic_masks[] = -{ - {0x00000017, 0} -}; - -static aper_size_info_8 intel_8xx_sizes[7] = -{ - {256, 65536, 6, 0}, - {128, 32768, 5, 32}, - {64, 16384, 4, 48}, - {32, 8192, 3, 56}, - {16, 4096, 2, 60}, - {8, 2048, 1, 62}, - {4, 1024, 0, 63} -}; - -static aper_size_info_16 intel_generic_sizes[7] = -{ - {256, 65536, 6, 0}, - {128, 32768, 5, 32}, - {64, 16384, 4, 48}, - {32, 8192, 3, 56}, - {16, 4096, 2, 60}, - {8, 2048, 1, 62}, - {4, 1024, 0, 63} -}; - -static aper_size_info_8 intel_830mp_sizes[4] = -{ - {256, 65536, 6, 0}, - {128, 32768, 5, 32}, - {64, 16384, 4, 48}, - {32, 8192, 3, 56} -}; - -static int __init intel_generic_setup (struct pci_dev *pdev) -{ - agp_bridge.masks = intel_generic_masks; - agp_bridge.num_of_masks = 1; - agp_bridge.aperture_sizes = (void *) intel_generic_sizes; - agp_bridge.size_type = U16_APER_SIZE; - agp_bridge.num_aperture_sizes = 7; - agp_bridge.dev_private_data = NULL; - agp_bridge.needs_scratch_page = FALSE; - agp_bridge.configure = intel_configure; - agp_bridge.fetch_size = intel_fetch_size; - agp_bridge.cleanup = intel_cleanup; - agp_bridge.tlb_flush = intel_tlbflush; - agp_bridge.mask_memory = intel_mask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = agp_generic_create_gatt_table; - agp_bridge.free_gatt_table = agp_generic_free_gatt_table; - agp_bridge.insert_memory = agp_generic_insert_memory; - agp_bridge.remove_memory = agp_generic_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = intel_resume; - agp_bridge.cant_use_aperture = 0; - - return 0; - - (void) pdev; /* unused */ -} - - -static int __init intel_820_setup (struct pci_dev *pdev) -{ - agp_bridge.masks = intel_generic_masks; - agp_bridge.num_of_masks = 1; - agp_bridge.aperture_sizes = (void *) intel_8xx_sizes; - agp_bridge.size_type = U8_APER_SIZE; - agp_bridge.num_aperture_sizes = 7; - agp_bridge.dev_private_data = NULL; - agp_bridge.needs_scratch_page = FALSE; - agp_bridge.configure = intel_820_configure; - agp_bridge.fetch_size = intel_8xx_fetch_size; - agp_bridge.cleanup = intel_820_cleanup; - agp_bridge.tlb_flush = intel_820_tlbflush; - agp_bridge.mask_memory = intel_mask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = agp_generic_create_gatt_table; - agp_bridge.free_gatt_table = agp_generic_free_gatt_table; - agp_bridge.insert_memory = agp_generic_insert_memory; - agp_bridge.remove_memory = agp_generic_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 0; - - return 0; - - (void) pdev; /* unused */ -} - -static int __init intel_830mp_setup (struct pci_dev *pdev) -{ - agp_bridge.masks = intel_generic_masks; - agp_bridge.num_of_masks = 1; - agp_bridge.aperture_sizes = (void *) intel_830mp_sizes; - agp_bridge.size_type = U8_APER_SIZE; - agp_bridge.num_aperture_sizes = 4; - agp_bridge.dev_private_data = NULL; - agp_bridge.needs_scratch_page = FALSE; - agp_bridge.configure = intel_830mp_configure; - agp_bridge.fetch_size = intel_8xx_fetch_size; - agp_bridge.cleanup = intel_8xx_cleanup; - agp_bridge.tlb_flush = intel_8xx_tlbflush; - agp_bridge.mask_memory = intel_mask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = agp_generic_create_gatt_table; - agp_bridge.free_gatt_table = agp_generic_free_gatt_table; - agp_bridge.insert_memory = agp_generic_insert_memory; - agp_bridge.remove_memory = agp_generic_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 0; - - return 0; - - (void) pdev; /* unused */ -} - -static int __init intel_840_setup (struct pci_dev *pdev) -{ - agp_bridge.masks = intel_generic_masks; - agp_bridge.num_of_masks = 1; - agp_bridge.aperture_sizes = (void *) intel_8xx_sizes; - agp_bridge.size_type = U8_APER_SIZE; - agp_bridge.num_aperture_sizes = 7; - agp_bridge.dev_private_data = NULL; - agp_bridge.needs_scratch_page = FALSE; - agp_bridge.configure = intel_840_configure; - agp_bridge.fetch_size = intel_8xx_fetch_size; - agp_bridge.cleanup = intel_8xx_cleanup; - agp_bridge.tlb_flush = intel_8xx_tlbflush; - agp_bridge.mask_memory = intel_mask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = agp_generic_create_gatt_table; - agp_bridge.free_gatt_table = agp_generic_free_gatt_table; - agp_bridge.insert_memory = agp_generic_insert_memory; - agp_bridge.remove_memory = agp_generic_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 0; - - return 0; - - (void) pdev; /* unused */ -} - -static int __init intel_845_setup (struct pci_dev *pdev) -{ - agp_bridge.masks = intel_generic_masks; - agp_bridge.num_of_masks = 1; - agp_bridge.aperture_sizes = (void *) intel_8xx_sizes; - agp_bridge.size_type = U8_APER_SIZE; - agp_bridge.num_aperture_sizes = 7; - agp_bridge.dev_private_data = NULL; - agp_bridge.needs_scratch_page = FALSE; - agp_bridge.configure = intel_845_configure; - agp_bridge.fetch_size = intel_8xx_fetch_size; - agp_bridge.cleanup = intel_8xx_cleanup; - agp_bridge.tlb_flush = intel_8xx_tlbflush; - agp_bridge.mask_memory = intel_mask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = agp_generic_create_gatt_table; - agp_bridge.free_gatt_table = agp_generic_free_gatt_table; - agp_bridge.insert_memory = agp_generic_insert_memory; - agp_bridge.remove_memory = agp_generic_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 0; - - return 0; - - (void) pdev; /* unused */ -} - -static int __init intel_850_setup (struct pci_dev *pdev) -{ - agp_bridge.masks = intel_generic_masks; - agp_bridge.num_of_masks = 1; - agp_bridge.aperture_sizes = (void *) intel_8xx_sizes; - agp_bridge.size_type = U8_APER_SIZE; - agp_bridge.num_aperture_sizes = 7; - agp_bridge.dev_private_data = NULL; - agp_bridge.needs_scratch_page = FALSE; - agp_bridge.configure = intel_850_configure; - agp_bridge.fetch_size = intel_8xx_fetch_size; - agp_bridge.cleanup = intel_8xx_cleanup; - agp_bridge.tlb_flush = intel_8xx_tlbflush; - agp_bridge.mask_memory = intel_mask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = agp_generic_create_gatt_table; - agp_bridge.free_gatt_table = agp_generic_free_gatt_table; - agp_bridge.insert_memory = agp_generic_insert_memory; - agp_bridge.remove_memory = agp_generic_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 0; - - return 0; - - (void) pdev; /* unused */ -} - -static int __init intel_860_setup (struct pci_dev *pdev) -{ - agp_bridge.masks = intel_generic_masks; - agp_bridge.num_of_masks = 1; - agp_bridge.aperture_sizes = (void *) intel_8xx_sizes; - agp_bridge.size_type = U8_APER_SIZE; - agp_bridge.num_aperture_sizes = 7; - agp_bridge.dev_private_data = NULL; - agp_bridge.needs_scratch_page = FALSE; - agp_bridge.configure = intel_860_configure; - agp_bridge.fetch_size = intel_8xx_fetch_size; - agp_bridge.cleanup = intel_8xx_cleanup; - agp_bridge.tlb_flush = intel_8xx_tlbflush; - agp_bridge.mask_memory = intel_mask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = agp_generic_create_gatt_table; - agp_bridge.free_gatt_table = agp_generic_free_gatt_table; - agp_bridge.insert_memory = agp_generic_insert_memory; - agp_bridge.remove_memory = agp_generic_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 0; - - return 0; - - (void) pdev; /* unused */ -} - -#endif /* CONFIG_AGP_INTEL */ - -#ifdef CONFIG_AGP_VIA - -static int via_fetch_size(void) -{ - int i; - u8 temp; - aper_size_info_8 *values; - - values = A_SIZE_8(agp_bridge.aperture_sizes); - pci_read_config_byte(agp_bridge.dev, VIA_APSIZE, &temp); - for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { - if (temp == values[i].size_value) { - agp_bridge.previous_size = - agp_bridge.current_size = (void *) (values + i); - agp_bridge.aperture_size_idx = i; - return values[i].size; - } - } - - return 0; -} - -static int via_configure(void) -{ - u32 temp; - aper_size_info_8 *current_size; - - current_size = A_SIZE_8(agp_bridge.current_size); - /* aperture size */ - pci_write_config_byte(agp_bridge.dev, VIA_APSIZE, - current_size->size_value); - /* address to map too */ - pci_read_config_dword(agp_bridge.dev, VIA_APBASE, &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - /* GART control register */ - pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000000f); - - /* attbase - aperture GATT base */ - pci_write_config_dword(agp_bridge.dev, VIA_ATTBASE, - (agp_bridge.gatt_bus_addr & 0xfffff000) | 3); - return 0; -} - -static void via_cleanup(void) -{ - aper_size_info_8 *previous_size; - - previous_size = A_SIZE_8(agp_bridge.previous_size); - pci_write_config_byte(agp_bridge.dev, VIA_APSIZE, - previous_size->size_value); - /* Do not disable by writing 0 to VIA_ATTBASE, it screws things up - * during reinitialization. - */ -} - -static void via_tlbflush(agp_memory * mem) -{ - pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000008f); - pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000000f); -} - -static unsigned long via_mask_memory(unsigned long addr, int type) -{ - /* Memory type is ignored */ - - return addr | agp_bridge.masks[0].mask; -} - -static aper_size_info_8 via_generic_sizes[7] = -{ - {256, 65536, 6, 0}, - {128, 32768, 5, 128}, - {64, 16384, 4, 192}, - {32, 8192, 3, 224}, - {16, 4096, 2, 240}, - {8, 2048, 1, 248}, - {4, 1024, 0, 252} -}; - -static gatt_mask via_generic_masks[] = -{ - {0x00000000, 0} -}; - -static int __init via_generic_setup (struct pci_dev *pdev) -{ - agp_bridge.masks = via_generic_masks; - agp_bridge.num_of_masks = 1; - agp_bridge.aperture_sizes = (void *) via_generic_sizes; - agp_bridge.size_type = U8_APER_SIZE; - agp_bridge.num_aperture_sizes = 7; - agp_bridge.dev_private_data = NULL; - agp_bridge.needs_scratch_page = FALSE; - agp_bridge.configure = via_configure; - agp_bridge.fetch_size = via_fetch_size; - agp_bridge.cleanup = via_cleanup; - agp_bridge.tlb_flush = via_tlbflush; - agp_bridge.mask_memory = via_mask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = agp_generic_create_gatt_table; - agp_bridge.free_gatt_table = agp_generic_free_gatt_table; - agp_bridge.insert_memory = agp_generic_insert_memory; - agp_bridge.remove_memory = agp_generic_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 0; - - return 0; - - (void) pdev; /* unused */ -} - -#endif /* CONFIG_AGP_VIA */ - -#ifdef CONFIG_AGP_SIS - -static int sis_fetch_size(void) -{ - u8 temp_size; - int i; - aper_size_info_8 *values; - - pci_read_config_byte(agp_bridge.dev, SIS_APSIZE, &temp_size); - values = A_SIZE_8(agp_bridge.aperture_sizes); - for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { - if ((temp_size == values[i].size_value) || - ((temp_size & ~(0x03)) == - (values[i].size_value & ~(0x03)))) { - agp_bridge.previous_size = - agp_bridge.current_size = (void *) (values + i); - - agp_bridge.aperture_size_idx = i; - return values[i].size; - } - } - - return 0; -} - - -static void sis_tlbflush(agp_memory * mem) -{ - pci_write_config_byte(agp_bridge.dev, SIS_TLBFLUSH, 0x02); -} - -static int sis_configure(void) -{ - u32 temp; - aper_size_info_8 *current_size; - - current_size = A_SIZE_8(agp_bridge.current_size); - pci_write_config_byte(agp_bridge.dev, SIS_TLBCNTRL, 0x05); - pci_read_config_dword(agp_bridge.dev, SIS_APBASE, &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - pci_write_config_dword(agp_bridge.dev, SIS_ATTBASE, - agp_bridge.gatt_bus_addr); - pci_write_config_byte(agp_bridge.dev, SIS_APSIZE, - current_size->size_value); - return 0; -} - -static void sis_cleanup(void) -{ - aper_size_info_8 *previous_size; - - previous_size = A_SIZE_8(agp_bridge.previous_size); - pci_write_config_byte(agp_bridge.dev, SIS_APSIZE, - (previous_size->size_value & ~(0x03))); -} - -static unsigned long sis_mask_memory(unsigned long addr, int type) -{ - /* Memory type is ignored */ - - return addr | agp_bridge.masks[0].mask; -} - -static aper_size_info_8 sis_generic_sizes[7] = -{ - {256, 65536, 6, 99}, - {128, 32768, 5, 83}, - {64, 16384, 4, 67}, - {32, 8192, 3, 51}, - {16, 4096, 2, 35}, - {8, 2048, 1, 19}, - {4, 1024, 0, 3} -}; - -static gatt_mask sis_generic_masks[] = -{ - {0x00000000, 0} -}; - -static int __init sis_generic_setup (struct pci_dev *pdev) -{ - agp_bridge.masks = sis_generic_masks; - agp_bridge.num_of_masks = 1; - agp_bridge.aperture_sizes = (void *) sis_generic_sizes; - agp_bridge.size_type = U8_APER_SIZE; - agp_bridge.num_aperture_sizes = 7; - agp_bridge.dev_private_data = NULL; - agp_bridge.needs_scratch_page = FALSE; - agp_bridge.configure = sis_configure; - agp_bridge.fetch_size = sis_fetch_size; - agp_bridge.cleanup = sis_cleanup; - agp_bridge.tlb_flush = sis_tlbflush; - agp_bridge.mask_memory = sis_mask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = agp_generic_create_gatt_table; - agp_bridge.free_gatt_table = agp_generic_free_gatt_table; - agp_bridge.insert_memory = agp_generic_insert_memory; - agp_bridge.remove_memory = agp_generic_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 0; - - return 0; -} - -#endif /* CONFIG_AGP_SIS */ - -#ifdef CONFIG_AGP_AMD - -typedef struct _amd_page_map { - unsigned long *real; - unsigned long *remapped; -} amd_page_map; - -static struct _amd_irongate_private { - volatile u8 *registers; - amd_page_map **gatt_pages; - int num_tables; -} amd_irongate_private; - -static int amd_create_page_map(amd_page_map *page_map) -{ - int i; - - page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL); - if (page_map->real == NULL) { - return -ENOMEM; - } - SetPageReserved(virt_to_page(page_map->real)); - CACHE_FLUSH(); - page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real), - PAGE_SIZE); - if (page_map->remapped == NULL) { - ClearPageReserved(virt_to_page(page_map->real)); - free_page((unsigned long) page_map->real); - page_map->real = NULL; - return -ENOMEM; - } - CACHE_FLUSH(); - - for(i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) { - page_map->remapped[i] = agp_bridge.scratch_page; - } - - return 0; -} - -static void amd_free_page_map(amd_page_map *page_map) -{ - iounmap(page_map->remapped); - ClearPageReserved(virt_to_page(page_map->real)); - free_page((unsigned long) page_map->real); -} - -static void amd_free_gatt_pages(void) -{ - int i; - amd_page_map **tables; - amd_page_map *entry; - - tables = amd_irongate_private.gatt_pages; - for(i = 0; i < amd_irongate_private.num_tables; i++) { - entry = tables[i]; - if (entry != NULL) { - if (entry->real != NULL) { - amd_free_page_map(entry); - } - kfree(entry); - } - } - kfree(tables); -} - -static int amd_create_gatt_pages(int nr_tables) -{ - amd_page_map **tables; - amd_page_map *entry; - int retval = 0; - int i; - - tables = kmalloc((nr_tables + 1) * sizeof(amd_page_map *), - GFP_KERNEL); - if (tables == NULL) { - return -ENOMEM; - } - memset(tables, 0, sizeof(amd_page_map *) * (nr_tables + 1)); - for (i = 0; i < nr_tables; i++) { - entry = kmalloc(sizeof(amd_page_map), GFP_KERNEL); - if (entry == NULL) { - retval = -ENOMEM; - break; - } - memset(entry, 0, sizeof(amd_page_map)); - tables[i] = entry; - retval = amd_create_page_map(entry); - if (retval != 0) break; - } - amd_irongate_private.num_tables = nr_tables; - amd_irongate_private.gatt_pages = tables; - - if (retval != 0) amd_free_gatt_pages(); - - return retval; -} - -/* Since we don't need contigious memory we just try - * to get the gatt table once - */ - -#define GET_PAGE_DIR_OFF(addr) (addr >> 22) -#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \ - GET_PAGE_DIR_OFF(agp_bridge.gart_bus_addr)) -#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) -#define GET_GATT(addr) (amd_irongate_private.gatt_pages[\ - GET_PAGE_DIR_IDX(addr)]->remapped) - -static int amd_create_gatt_table(void) -{ - aper_size_info_lvl2 *value; - amd_page_map page_dir; - unsigned long addr; - int retval; - u32 temp; - int i; - - value = A_SIZE_LVL2(agp_bridge.current_size); - retval = amd_create_page_map(&page_dir); - if (retval != 0) { - return retval; - } - - retval = amd_create_gatt_pages(value->num_entries / 1024); - if (retval != 0) { - amd_free_page_map(&page_dir); - return retval; - } - - agp_bridge.gatt_table_real = page_dir.real; - agp_bridge.gatt_table = page_dir.remapped; - agp_bridge.gatt_bus_addr = virt_to_phys(page_dir.real); - - /* Get the address for the gart region. - * This is a bus address even on the alpha, b/c its - * used to program the agp master not the cpu - */ - - pci_read_config_dword(agp_bridge.dev, AMD_APBASE, &temp); - addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - agp_bridge.gart_bus_addr = addr; - - /* Calculate the agp offset */ - for(i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) { - page_dir.remapped[GET_PAGE_DIR_OFF(addr)] = - virt_to_phys(amd_irongate_private.gatt_pages[i]->real); - page_dir.remapped[GET_PAGE_DIR_OFF(addr)] |= 0x00000001; - } - - return 0; -} - -static int amd_free_gatt_table(void) -{ - amd_page_map page_dir; - - page_dir.real = agp_bridge.gatt_table_real; - page_dir.remapped = agp_bridge.gatt_table; - - amd_free_gatt_pages(); - amd_free_page_map(&page_dir); - return 0; -} - -static int amd_irongate_fetch_size(void) -{ - int i; - u32 temp; - aper_size_info_lvl2 *values; - - pci_read_config_dword(agp_bridge.dev, AMD_APSIZE, &temp); - temp = (temp & 0x0000000e); - values = A_SIZE_LVL2(agp_bridge.aperture_sizes); - for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { - if (temp == values[i].size_value) { - agp_bridge.previous_size = - agp_bridge.current_size = (void *) (values + i); - - agp_bridge.aperture_size_idx = i; - return values[i].size; - } - } - - return 0; -} - -static int amd_irongate_configure(void) -{ - aper_size_info_lvl2 *current_size; - u32 temp; - u16 enable_reg; - - current_size = A_SIZE_LVL2(agp_bridge.current_size); - - /* Get the memory mapped registers */ - pci_read_config_dword(agp_bridge.dev, AMD_MMBASE, &temp); - temp = (temp & PCI_BASE_ADDRESS_MEM_MASK); - amd_irongate_private.registers = (volatile u8 *) ioremap(temp, 4096); - - /* Write out the address of the gatt table */ - OUTREG32(amd_irongate_private.registers, AMD_ATTBASE, - agp_bridge.gatt_bus_addr); - - /* Write the Sync register */ - pci_write_config_byte(agp_bridge.dev, AMD_MODECNTL, 0x80); - - /* Set indexing mode */ - pci_write_config_byte(agp_bridge.dev, AMD_MODECNTL2, 0x00); - - /* Write the enable register */ - enable_reg = INREG16(amd_irongate_private.registers, AMD_GARTENABLE); - enable_reg = (enable_reg | 0x0004); - OUTREG16(amd_irongate_private.registers, AMD_GARTENABLE, enable_reg); - - /* Write out the size register */ - pci_read_config_dword(agp_bridge.dev, AMD_APSIZE, &temp); - temp = (((temp & ~(0x0000000e)) | current_size->size_value) - | 0x00000001); - pci_write_config_dword(agp_bridge.dev, AMD_APSIZE, temp); - - /* Flush the tlb */ - OUTREG32(amd_irongate_private.registers, AMD_TLBFLUSH, 0x00000001); - - return 0; -} - -static void amd_irongate_cleanup(void) -{ - aper_size_info_lvl2 *previous_size; - u32 temp; - u16 enable_reg; - - previous_size = A_SIZE_LVL2(agp_bridge.previous_size); - - enable_reg = INREG16(amd_irongate_private.registers, AMD_GARTENABLE); - enable_reg = (enable_reg & ~(0x0004)); - OUTREG16(amd_irongate_private.registers, AMD_GARTENABLE, enable_reg); - - /* Write back the previous size and disable gart translation */ - pci_read_config_dword(agp_bridge.dev, AMD_APSIZE, &temp); - temp = ((temp & ~(0x0000000f)) | previous_size->size_value); - pci_write_config_dword(agp_bridge.dev, AMD_APSIZE, temp); - iounmap((void *) amd_irongate_private.registers); -} - -/* - * This routine could be implemented by taking the addresses - * written to the GATT, and flushing them individually. However - * currently it just flushes the whole table. Which is probably - * more efficent, since agp_memory blocks can be a large number of - * entries. - */ - -static void amd_irongate_tlbflush(agp_memory * temp) -{ - OUTREG32(amd_irongate_private.registers, AMD_TLBFLUSH, 0x00000001); -} - -static unsigned long amd_irongate_mask_memory(unsigned long addr, int type) -{ - /* Only type 0 is supported by the irongate */ - - return addr | agp_bridge.masks[0].mask; -} - -static int amd_insert_memory(agp_memory * mem, - off_t pg_start, int type) -{ - int i, j, num_entries; - unsigned long *cur_gatt; - unsigned long addr; - - num_entries = A_SIZE_LVL2(agp_bridge.current_size)->num_entries; - - if (type != 0 || mem->type != 0) { - return -EINVAL; - } - if ((pg_start + mem->page_count) > num_entries) { - return -EINVAL; - } - - j = pg_start; - while (j < (pg_start + mem->page_count)) { - addr = (j * PAGE_SIZE) + agp_bridge.gart_bus_addr; - cur_gatt = GET_GATT(addr); - if (!PGE_EMPTY(cur_gatt[GET_GATT_OFF(addr)])) { - return -EBUSY; - } - j++; - } - - if (mem->is_flushed == FALSE) { - CACHE_FLUSH(); - mem->is_flushed = TRUE; - } - - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - addr = (j * PAGE_SIZE) + agp_bridge.gart_bus_addr; - cur_gatt = GET_GATT(addr); - cur_gatt[GET_GATT_OFF(addr)] = mem->memory[i]; - } - agp_bridge.tlb_flush(mem); - return 0; -} - -static int amd_remove_memory(agp_memory * mem, off_t pg_start, - int type) -{ - int i; - unsigned long *cur_gatt; - unsigned long addr; - - if (type != 0 || mem->type != 0) { - return -EINVAL; - } - for (i = pg_start; i < (mem->page_count + pg_start); i++) { - addr = (i * PAGE_SIZE) + agp_bridge.gart_bus_addr; - cur_gatt = GET_GATT(addr); - cur_gatt[GET_GATT_OFF(addr)] = - (unsigned long) agp_bridge.scratch_page; - } - - agp_bridge.tlb_flush(mem); - return 0; -} - -static aper_size_info_lvl2 amd_irongate_sizes[7] = -{ - {2048, 524288, 0x0000000c}, - {1024, 262144, 0x0000000a}, - {512, 131072, 0x00000008}, - {256, 65536, 0x00000006}, - {128, 32768, 0x00000004}, - {64, 16384, 0x00000002}, - {32, 8192, 0x00000000} -}; - -static gatt_mask amd_irongate_masks[] = -{ - {0x00000001, 0} -}; - -static int __init amd_irongate_setup (struct pci_dev *pdev) -{ - agp_bridge.masks = amd_irongate_masks; - agp_bridge.num_of_masks = 1; - agp_bridge.aperture_sizes = (void *) amd_irongate_sizes; - agp_bridge.size_type = LVL2_APER_SIZE; - agp_bridge.num_aperture_sizes = 7; - agp_bridge.dev_private_data = (void *) &amd_irongate_private; - agp_bridge.needs_scratch_page = FALSE; - agp_bridge.configure = amd_irongate_configure; - agp_bridge.fetch_size = amd_irongate_fetch_size; - agp_bridge.cleanup = amd_irongate_cleanup; - agp_bridge.tlb_flush = amd_irongate_tlbflush; - agp_bridge.mask_memory = amd_irongate_mask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = amd_create_gatt_table; - agp_bridge.free_gatt_table = amd_free_gatt_table; - agp_bridge.insert_memory = amd_insert_memory; - agp_bridge.remove_memory = amd_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 0; - - return 0; - - (void) pdev; /* unused */ -} - -#endif /* CONFIG_AGP_AMD */ - -#ifdef CONFIG_AGP_ALI - -static int ali_fetch_size(void) -{ - int i; - u32 temp; - aper_size_info_32 *values; - - pci_read_config_dword(agp_bridge.dev, ALI_ATTBASE, &temp); - temp &= ~(0xfffffff0); - values = A_SIZE_32(agp_bridge.aperture_sizes); - - for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { - if (temp == values[i].size_value) { - agp_bridge.previous_size = - agp_bridge.current_size = (void *) (values + i); - agp_bridge.aperture_size_idx = i; - return values[i].size; - } - } - - return 0; -} - -static void ali_tlbflush(agp_memory * mem) -{ - u32 temp; - - pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp); -// clear tag - pci_write_config_dword(agp_bridge.dev, ALI_TAGCTRL, - ((temp & 0xfffffff0) | 0x00000001|0x00000002)); -} - -static void ali_cleanup(void) -{ - aper_size_info_32 *previous_size; - u32 temp; - - previous_size = A_SIZE_32(agp_bridge.previous_size); - - pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp); -// clear tag - pci_write_config_dword(agp_bridge.dev, ALI_TAGCTRL, - ((temp & 0xffffff00) | 0x00000001|0x00000002)); - - pci_read_config_dword(agp_bridge.dev, ALI_ATTBASE, &temp); - pci_write_config_dword(agp_bridge.dev, ALI_ATTBASE, - ((temp & 0x00000ff0) | previous_size->size_value)); -} - -static int ali_configure(void) -{ - u32 temp; - aper_size_info_32 *current_size; - - current_size = A_SIZE_32(agp_bridge.current_size); - - /* aperture size and gatt addr */ - pci_read_config_dword(agp_bridge.dev, ALI_ATTBASE, &temp); - temp = (((temp & 0x00000ff0) | (agp_bridge.gatt_bus_addr & 0xfffff000)) - | (current_size->size_value & 0xf)); - pci_write_config_dword(agp_bridge.dev, ALI_ATTBASE, temp); - - /* tlb control */ - - /* - * Question: Jeff, ALi's patch deletes this: - * - * pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp); - * pci_write_config_dword(agp_bridge.dev, ALI_TLBCTRL, - * ((temp & 0xffffff00) | 0x00000010)); - * - * and replaces it with the following, which seems to duplicate the - * next couple of lines below it. I suspect this was an oversight, - * but you might want to check up on this? - */ - - pci_read_config_dword(agp_bridge.dev, ALI_APBASE, &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - /* address to map to */ - pci_read_config_dword(agp_bridge.dev, ALI_APBASE, &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - -#if 0 - if (agp_bridge.type == ALI_M1541) { - u32 nlvm_addr = 0; - - switch (current_size->size_value) { - case 0: break; - case 1: nlvm_addr = 0x100000;break; - case 2: nlvm_addr = 0x200000;break; - case 3: nlvm_addr = 0x400000;break; - case 4: nlvm_addr = 0x800000;break; - case 6: nlvm_addr = 0x1000000;break; - case 7: nlvm_addr = 0x2000000;break; - case 8: nlvm_addr = 0x4000000;break; - case 9: nlvm_addr = 0x8000000;break; - case 10: nlvm_addr = 0x10000000;break; - default: break; - } - nlvm_addr--; - nlvm_addr&=0xfff00000; - - nlvm_addr+= agp_bridge.gart_bus_addr; - nlvm_addr|=(agp_bridge.gart_bus_addr>>12); - printk(KERN_INFO PFX "nlvm top &base = %8x\n",nlvm_addr); - } -#endif - - pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp); - temp &= 0xffffff7f; //enable TLB - pci_write_config_dword(agp_bridge.dev, ALI_TLBCTRL, temp); - - return 0; -} - -static unsigned long ali_mask_memory(unsigned long addr, int type) -{ - /* Memory type is ignored */ - - return addr | agp_bridge.masks[0].mask; -} - -static void ali_cache_flush(void) -{ - global_cache_flush(); - - if (agp_bridge.type == ALI_M1541) { - int i, page_count; - u32 temp; - - page_count = 1 << A_SIZE_32(agp_bridge.current_size)->page_order; - for (i = 0; i < PAGE_SIZE * page_count; i += PAGE_SIZE) { - pci_read_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, &temp); - pci_write_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, - (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | - (agp_bridge.gatt_bus_addr + i)) | - ALI_CACHE_FLUSH_EN)); - } - } -} - -static void *ali_alloc_page(void) -{ - void *adr = agp_generic_alloc_page(); - unsigned temp; - - if (adr == 0) - return 0; - - if (agp_bridge.type == ALI_M1541) { - pci_read_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, &temp); - pci_write_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, - (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | - virt_to_phys(adr)) | - ALI_CACHE_FLUSH_EN )); - } - return adr; -} - -static void ali_destroy_page(void * addr) -{ - u32 temp; - - if (addr == NULL) - return; - - global_cache_flush(); - - if (agp_bridge.type == ALI_M1541) { - pci_read_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, &temp); - pci_write_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, - (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | - virt_to_phys(addr)) | - ALI_CACHE_FLUSH_EN)); - } - - agp_generic_destroy_page(addr); -} - -/* Setup function */ -static gatt_mask ali_generic_masks[] = -{ - {0x00000000, 0} -}; - -static aper_size_info_32 ali_generic_sizes[7] = -{ - {256, 65536, 6, 10}, - {128, 32768, 5, 9}, - {64, 16384, 4, 8}, - {32, 8192, 3, 7}, - {16, 4096, 2, 6}, - {8, 2048, 1, 4}, - {4, 1024, 0, 3} -}; - -static int __init ali_generic_setup (struct pci_dev *pdev) -{ - agp_bridge.masks = ali_generic_masks; - agp_bridge.num_of_masks = 1; - agp_bridge.aperture_sizes = (void *) ali_generic_sizes; - agp_bridge.size_type = U32_APER_SIZE; - agp_bridge.num_aperture_sizes = 7; - agp_bridge.dev_private_data = NULL; - agp_bridge.needs_scratch_page = FALSE; - agp_bridge.configure = ali_configure; - agp_bridge.fetch_size = ali_fetch_size; - agp_bridge.cleanup = ali_cleanup; - agp_bridge.tlb_flush = ali_tlbflush; - agp_bridge.mask_memory = ali_mask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; - agp_bridge.cache_flush = ali_cache_flush; - agp_bridge.create_gatt_table = agp_generic_create_gatt_table; - agp_bridge.free_gatt_table = agp_generic_free_gatt_table; - agp_bridge.insert_memory = agp_generic_insert_memory; - agp_bridge.remove_memory = agp_generic_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = ali_alloc_page; - agp_bridge.agp_destroy_page = ali_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 0; - - return 0; - - (void) pdev; /* unused */ -} - -#endif /* CONFIG_AGP_ALI */ - -#ifdef CONFIG_AGP_SWORKS -typedef struct _serverworks_page_map { - unsigned long *real; - unsigned long *remapped; -} serverworks_page_map; - -static struct _serverworks_private { - struct pci_dev *svrwrks_dev; /* device one */ - volatile u8 *registers; - serverworks_page_map **gatt_pages; - int num_tables; - serverworks_page_map scratch_dir; - - int gart_addr_ofs; - int mm_addr_ofs; -} serverworks_private; - -static int serverworks_create_page_map(serverworks_page_map *page_map) -{ - int i; - - page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL); - if (page_map->real == NULL) { - return -ENOMEM; - } - SetPageReserved(virt_to_page(page_map->real)); - CACHE_FLUSH(); - page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real), - PAGE_SIZE); - if (page_map->remapped == NULL) { - ClearPageReserved(virt_to_page(page_map->real)); - free_page((unsigned long) page_map->real); - page_map->real = NULL; - return -ENOMEM; - } - CACHE_FLUSH(); - - for(i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) { - page_map->remapped[i] = agp_bridge.scratch_page; - } - - return 0; -} - -static void serverworks_free_page_map(serverworks_page_map *page_map) -{ - iounmap(page_map->remapped); - ClearPageReserved(virt_to_page(page_map->real)); - free_page((unsigned long) page_map->real); -} - -static void serverworks_free_gatt_pages(void) -{ - int i; - serverworks_page_map **tables; - serverworks_page_map *entry; - - tables = serverworks_private.gatt_pages; - for(i = 0; i < serverworks_private.num_tables; i++) { - entry = tables[i]; - if (entry != NULL) { - if (entry->real != NULL) { - serverworks_free_page_map(entry); - } - kfree(entry); - } - } - kfree(tables); -} - -static int serverworks_create_gatt_pages(int nr_tables) -{ - serverworks_page_map **tables; - serverworks_page_map *entry; - int retval = 0; - int i; - - tables = kmalloc((nr_tables + 1) * sizeof(serverworks_page_map *), - GFP_KERNEL); - if (tables == NULL) { - return -ENOMEM; - } - memset(tables, 0, sizeof(serverworks_page_map *) * (nr_tables + 1)); - for (i = 0; i < nr_tables; i++) { - entry = kmalloc(sizeof(serverworks_page_map), GFP_KERNEL); - if (entry == NULL) { - retval = -ENOMEM; - break; - } - memset(entry, 0, sizeof(serverworks_page_map)); - tables[i] = entry; - retval = serverworks_create_page_map(entry); - if (retval != 0) break; - } - serverworks_private.num_tables = nr_tables; - serverworks_private.gatt_pages = tables; - - if (retval != 0) serverworks_free_gatt_pages(); - - return retval; -} - -#define SVRWRKS_GET_GATT(addr) (serverworks_private.gatt_pages[\ - GET_PAGE_DIR_IDX(addr)]->remapped) - -#ifndef GET_PAGE_DIR_OFF -#define GET_PAGE_DIR_OFF(addr) (addr >> 22) -#endif - -#ifndef GET_PAGE_DIR_IDX -#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \ - GET_PAGE_DIR_OFF(agp_bridge.gart_bus_addr)) -#endif - -#ifndef GET_GATT_OFF -#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) -#endif - -static int serverworks_create_gatt_table(void) -{ - aper_size_info_lvl2 *value; - serverworks_page_map page_dir; - int retval; - u32 temp; - int i; - - value = A_SIZE_LVL2(agp_bridge.current_size); - retval = serverworks_create_page_map(&page_dir); - if (retval != 0) { - return retval; - } - retval = serverworks_create_page_map(&serverworks_private.scratch_dir); - if (retval != 0) { - serverworks_free_page_map(&page_dir); - return retval; - } - /* Create a fake scratch directory */ - for(i = 0; i < 1024; i++) { - serverworks_private.scratch_dir.remapped[i] = (unsigned long) agp_bridge.scratch_page; - page_dir.remapped[i] = - virt_to_phys(serverworks_private.scratch_dir.real); - page_dir.remapped[i] |= 0x00000001; - } - - retval = serverworks_create_gatt_pages(value->num_entries / 1024); - if (retval != 0) { - serverworks_free_page_map(&page_dir); - serverworks_free_page_map(&serverworks_private.scratch_dir); - return retval; - } - - agp_bridge.gatt_table_real = page_dir.real; - agp_bridge.gatt_table = page_dir.remapped; - agp_bridge.gatt_bus_addr = virt_to_phys(page_dir.real); - - /* Get the address for the gart region. - * This is a bus address even on the alpha, b/c its - * used to program the agp master not the cpu - */ - - pci_read_config_dword(agp_bridge.dev, - serverworks_private.gart_addr_ofs, - &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - /* Calculate the agp offset */ - - for(i = 0; i < value->num_entries / 1024; i++) { - page_dir.remapped[i] = - virt_to_phys(serverworks_private.gatt_pages[i]->real); - page_dir.remapped[i] |= 0x00000001; - } - - return 0; -} - -static int serverworks_free_gatt_table(void) -{ - serverworks_page_map page_dir; - - page_dir.real = agp_bridge.gatt_table_real; - page_dir.remapped = agp_bridge.gatt_table; - - serverworks_free_gatt_pages(); - serverworks_free_page_map(&page_dir); - serverworks_free_page_map(&serverworks_private.scratch_dir); - return 0; -} - -static int serverworks_fetch_size(void) -{ - int i; - u32 temp; - u32 temp2; - aper_size_info_lvl2 *values; - - values = A_SIZE_LVL2(agp_bridge.aperture_sizes); - pci_read_config_dword(agp_bridge.dev, - serverworks_private.gart_addr_ofs, - &temp); - pci_write_config_dword(agp_bridge.dev, - serverworks_private.gart_addr_ofs, - SVWRKS_SIZE_MASK); - pci_read_config_dword(agp_bridge.dev, - serverworks_private.gart_addr_ofs, - &temp2); - pci_write_config_dword(agp_bridge.dev, - serverworks_private.gart_addr_ofs, - temp); - temp2 &= SVWRKS_SIZE_MASK; - - for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { - if (temp2 == values[i].size_value) { - agp_bridge.previous_size = - agp_bridge.current_size = (void *) (values + i); - - agp_bridge.aperture_size_idx = i; - return values[i].size; - } - } - - return 0; -} - -static int serverworks_configure(void) -{ - aper_size_info_lvl2 *current_size; - u32 temp; - u8 enable_reg; - u8 cap_ptr; - u32 cap_id; - u16 cap_reg; - - current_size = A_SIZE_LVL2(agp_bridge.current_size); - - /* Get the memory mapped registers */ - pci_read_config_dword(agp_bridge.dev, - serverworks_private.mm_addr_ofs, - &temp); - temp = (temp & PCI_BASE_ADDRESS_MEM_MASK); - serverworks_private.registers = (volatile u8 *) ioremap(temp, 4096); - - OUTREG8(serverworks_private.registers, SVWRKS_GART_CACHE, 0x0a); - - OUTREG32(serverworks_private.registers, SVWRKS_GATTBASE, - agp_bridge.gatt_bus_addr); - - cap_reg = INREG16(serverworks_private.registers, SVWRKS_COMMAND); - cap_reg &= ~0x0007; - cap_reg |= 0x4; - OUTREG16(serverworks_private.registers, SVWRKS_COMMAND, cap_reg); - - pci_read_config_byte(serverworks_private.svrwrks_dev, - SVWRKS_AGP_ENABLE, &enable_reg); - enable_reg |= 0x1; /* Agp Enable bit */ - pci_write_config_byte(serverworks_private.svrwrks_dev, - SVWRKS_AGP_ENABLE, enable_reg); - agp_bridge.tlb_flush(NULL); - - pci_read_config_byte(serverworks_private.svrwrks_dev, 0x34, &cap_ptr); - if (cap_ptr != 0x00) { - do { - pci_read_config_dword(serverworks_private.svrwrks_dev, - cap_ptr, &cap_id); - - if ((cap_id & 0xff) != 0x02) - cap_ptr = (cap_id >> 8) & 0xff; - } - while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00)); - } - agp_bridge.capndx = cap_ptr; - - /* Fill in the mode register */ - pci_read_config_dword(serverworks_private.svrwrks_dev, - agp_bridge.capndx + 4, - &agp_bridge.mode); - - pci_read_config_byte(agp_bridge.dev, - SVWRKS_CACHING, - &enable_reg); - enable_reg &= ~0x3; - pci_write_config_byte(agp_bridge.dev, - SVWRKS_CACHING, - enable_reg); - - pci_read_config_byte(agp_bridge.dev, - SVWRKS_FEATURE, - &enable_reg); - enable_reg |= (1<<6); - pci_write_config_byte(agp_bridge.dev, - SVWRKS_FEATURE, - enable_reg); - - return 0; -} - -static void serverworks_cleanup(void) -{ - iounmap((void *) serverworks_private.registers); -} - -/* - * This routine could be implemented by taking the addresses - * written to the GATT, and flushing them individually. However - * currently it just flushes the whole table. Which is probably - * more efficent, since agp_memory blocks can be a large number of - * entries. - */ - -static void serverworks_tlbflush(agp_memory * temp) -{ - unsigned long end; - - OUTREG8(serverworks_private.registers, SVWRKS_POSTFLUSH, 0x01); - end = jiffies + 3*HZ; - while(INREG8(serverworks_private.registers, - SVWRKS_POSTFLUSH) == 0x01) { - if((signed)(end - jiffies) <= 0) { - printk(KERN_ERR "Posted write buffer flush took more" - "then 3 seconds\n"); - } - } - OUTREG32(serverworks_private.registers, SVWRKS_DIRFLUSH, 0x00000001); - end = jiffies + 3*HZ; - while(INREG32(serverworks_private.registers, - SVWRKS_DIRFLUSH) == 0x00000001) { - if((signed)(end - jiffies) <= 0) { - printk(KERN_ERR "TLB flush took more" - "then 3 seconds\n"); - } - } -} - -static unsigned long serverworks_mask_memory(unsigned long addr, int type) -{ - /* Only type 0 is supported by the serverworks chipsets */ - - return addr | agp_bridge.masks[0].mask; -} - -static int serverworks_insert_memory(agp_memory * mem, - off_t pg_start, int type) -{ - int i, j, num_entries; - unsigned long *cur_gatt; - unsigned long addr; - - num_entries = A_SIZE_LVL2(agp_bridge.current_size)->num_entries; - - if (type != 0 || mem->type != 0) { - return -EINVAL; - } - if ((pg_start + mem->page_count) > num_entries) { - return -EINVAL; - } - - j = pg_start; - while (j < (pg_start + mem->page_count)) { - addr = (j * PAGE_SIZE) + agp_bridge.gart_bus_addr; - cur_gatt = SVRWRKS_GET_GATT(addr); - if (!PGE_EMPTY(cur_gatt[GET_GATT_OFF(addr)])) { - return -EBUSY; - } - j++; - } - - if (mem->is_flushed == FALSE) { - CACHE_FLUSH(); - mem->is_flushed = TRUE; - } - - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - addr = (j * PAGE_SIZE) + agp_bridge.gart_bus_addr; - cur_gatt = SVRWRKS_GET_GATT(addr); - cur_gatt[GET_GATT_OFF(addr)] = mem->memory[i]; - } - agp_bridge.tlb_flush(mem); - return 0; -} - -static int serverworks_remove_memory(agp_memory * mem, off_t pg_start, - int type) -{ - int i; - unsigned long *cur_gatt; - unsigned long addr; - - if (type != 0 || mem->type != 0) { - return -EINVAL; - } - - CACHE_FLUSH(); - agp_bridge.tlb_flush(mem); - - for (i = pg_start; i < (mem->page_count + pg_start); i++) { - addr = (i * PAGE_SIZE) + agp_bridge.gart_bus_addr; - cur_gatt = SVRWRKS_GET_GATT(addr); - cur_gatt[GET_GATT_OFF(addr)] = - (unsigned long) agp_bridge.scratch_page; - } - - agp_bridge.tlb_flush(mem); - return 0; -} - -static gatt_mask serverworks_masks[] = -{ - {0x00000001, 0} -}; - -static aper_size_info_lvl2 serverworks_sizes[7] = -{ - {2048, 524288, 0x80000000}, - {1024, 262144, 0xc0000000}, - {512, 131072, 0xe0000000}, - {256, 65536, 0xf0000000}, - {128, 32768, 0xf8000000}, - {64, 16384, 0xfc000000}, - {32, 8192, 0xfe000000} -}; - -static void serverworks_agp_enable(u32 mode) -{ - struct pci_dev *device = NULL; - u32 command, scratch, cap_id; - u8 cap_ptr; - - pci_read_config_dword(serverworks_private.svrwrks_dev, - agp_bridge.capndx + 4, - &command); - - /* - * PASS1: go throu all devices that claim to be - * AGP devices and collect their data. - */ - - - pci_for_each_dev(device) { - cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); - if (cap_ptr != 0x00) { - do { - pci_read_config_dword(device, - cap_ptr, &cap_id); - - if ((cap_id & 0xff) != 0x02) - cap_ptr = (cap_id >> 8) & 0xff; - } - while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00)); - } - if (cap_ptr != 0x00) { - /* - * Ok, here we have a AGP device. Disable impossible - * settings, and adjust the readqueue to the minimum. - */ - - pci_read_config_dword(device, cap_ptr + 4, &scratch); - - /* adjust RQ depth */ - command = - ((command & ~0xff000000) | - min_t(u32, (mode & 0xff000000), - min_t(u32, (command & 0xff000000), - (scratch & 0xff000000)))); - - /* disable SBA if it's not supported */ - if (!((command & 0x00000200) && - (scratch & 0x00000200) && - (mode & 0x00000200))) - command &= ~0x00000200; - - /* disable FW */ - command &= ~0x00000010; - - command &= ~0x00000008; - - if (!((command & 4) && - (scratch & 4) && - (mode & 4))) - command &= ~0x00000004; - - if (!((command & 2) && - (scratch & 2) && - (mode & 2))) - command &= ~0x00000002; - - if (!((command & 1) && - (scratch & 1) && - (mode & 1))) - command &= ~0x00000001; - } - } - /* - * PASS2: Figure out the 4X/2X/1X setting and enable the - * target (our motherboard chipset). - */ - - if (command & 4) { - command &= ~3; /* 4X */ - } - if (command & 2) { - command &= ~5; /* 2X */ - } - if (command & 1) { - command &= ~6; /* 1X */ - } - command |= 0x00000100; - - pci_write_config_dword(serverworks_private.svrwrks_dev, - agp_bridge.capndx + 8, - command); - - /* - * PASS3: Go throu all AGP devices and update the - * command registers. - */ - - pci_for_each_dev(device) { - cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); - if (cap_ptr != 0x00) - pci_write_config_dword(device, cap_ptr + 8, command); - } -} - -static int __init serverworks_setup (struct pci_dev *pdev) -{ - u32 temp; - u32 temp2; - - serverworks_private.svrwrks_dev = pdev; - - agp_bridge.masks = serverworks_masks; - agp_bridge.num_of_masks = 1; - agp_bridge.aperture_sizes = (void *) serverworks_sizes; - agp_bridge.size_type = LVL2_APER_SIZE; - agp_bridge.num_aperture_sizes = 7; - agp_bridge.dev_private_data = (void *) &serverworks_private; - agp_bridge.needs_scratch_page = TRUE; - agp_bridge.configure = serverworks_configure; - agp_bridge.fetch_size = serverworks_fetch_size; - agp_bridge.cleanup = serverworks_cleanup; - agp_bridge.tlb_flush = serverworks_tlbflush; - agp_bridge.mask_memory = serverworks_mask_memory; - agp_bridge.agp_enable = serverworks_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = serverworks_create_gatt_table; - agp_bridge.free_gatt_table = serverworks_free_gatt_table; - agp_bridge.insert_memory = serverworks_insert_memory; - agp_bridge.remove_memory = serverworks_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - agp_bridge.suspend = agp_generic_suspend; - agp_bridge.resume = agp_generic_resume; - agp_bridge.cant_use_aperture = 0; - - pci_read_config_dword(agp_bridge.dev, - SVWRKS_APSIZE, - &temp); - - serverworks_private.gart_addr_ofs = 0x10; - - if(temp & PCI_BASE_ADDRESS_MEM_TYPE_64) { - pci_read_config_dword(agp_bridge.dev, - SVWRKS_APSIZE + 4, - &temp2); - if(temp2 != 0) { - printk("Detected 64 bit aperture address, but top " - "bits are not zero. Disabling agp\n"); - return -ENODEV; - } - serverworks_private.mm_addr_ofs = 0x18; - } else { - serverworks_private.mm_addr_ofs = 0x14; - } - - pci_read_config_dword(agp_bridge.dev, - serverworks_private.mm_addr_ofs, - &temp); - if(temp & PCI_BASE_ADDRESS_MEM_TYPE_64) { - pci_read_config_dword(agp_bridge.dev, - serverworks_private.mm_addr_ofs + 4, - &temp2); - if(temp2 != 0) { - printk("Detected 64 bit MMIO address, but top " - "bits are not zero. Disabling agp\n"); - return -ENODEV; - } - } - - return 0; -} - -#endif /* CONFIG_AGP_SWORKS */ - -#ifdef CONFIG_AGP_HP_ZX1 - -#ifndef log2 -#define log2(x) ffz(~(x)) -#endif - -#define HP_ZX1_IOVA_BASE GB(1UL) -#define HP_ZX1_IOVA_SIZE GB(1UL) -#define HP_ZX1_GART_SIZE (HP_ZX1_IOVA_SIZE / 2) -#define HP_ZX1_SBA_IOMMU_COOKIE 0x0000badbadc0ffeeUL - -#define HP_ZX1_PDIR_VALID_BIT 0x8000000000000000UL -#define HP_ZX1_IOVA_TO_PDIR(va) ((va - hp_private.iova_base) >> \ - hp_private.io_tlb_shift) - -static aper_size_info_fixed hp_zx1_sizes[] = -{ - {0, 0, 0}, /* filled in by hp_zx1_fetch_size() */ -}; - -static gatt_mask hp_zx1_masks[] = -{ - {HP_ZX1_PDIR_VALID_BIT, 0} -}; - -static struct _hp_private { - struct pci_dev *ioc; - volatile u8 *registers; - u64 *io_pdir; // PDIR for entire IOVA - u64 *gatt; // PDIR just for GART (subset of above) - u64 gatt_entries; - u64 iova_base; - u64 gart_base; - u64 gart_size; - u64 io_pdir_size; - int io_pdir_owner; // do we own it, or share it with sba_iommu? - int io_page_size; - int io_tlb_shift; - int io_tlb_ps; // IOC ps config - int io_pages_per_kpage; -} hp_private; - -static int __init hp_zx1_ioc_shared(void) -{ - struct _hp_private *hp = &hp_private; - - printk(KERN_INFO PFX "HP ZX1 IOC: IOPDIR shared with sba_iommu\n"); - - /* - * IOC already configured by sba_iommu module; just use - * its setup. We assume: - * - IOVA space is 1Gb in size - * - first 512Mb is IOMMU, second 512Mb is GART - */ - hp->io_tlb_ps = INREG64(hp->registers, HP_ZX1_TCNFG); - switch (hp->io_tlb_ps) { - case 0: hp->io_tlb_shift = 12; break; - case 1: hp->io_tlb_shift = 13; break; - case 2: hp->io_tlb_shift = 14; break; - case 3: hp->io_tlb_shift = 16; break; - default: - printk(KERN_ERR PFX "Invalid IOTLB page size " - "configuration 0x%x\n", hp->io_tlb_ps); - hp->gatt = 0; - hp->gatt_entries = 0; - return -ENODEV; - } - hp->io_page_size = 1 << hp->io_tlb_shift; - hp->io_pages_per_kpage = PAGE_SIZE / hp->io_page_size; - - hp->iova_base = INREG64(hp->registers, HP_ZX1_IBASE) & ~0x1; - hp->gart_base = hp->iova_base + HP_ZX1_IOVA_SIZE - HP_ZX1_GART_SIZE; - - hp->gart_size = HP_ZX1_GART_SIZE; - hp->gatt_entries = hp->gart_size / hp->io_page_size; - - hp->io_pdir = phys_to_virt(INREG64(hp->registers, HP_ZX1_PDIR_BASE)); - hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)]; - - if (hp->gatt[0] != HP_ZX1_SBA_IOMMU_COOKIE) { - hp->gatt = 0; - hp->gatt_entries = 0; - printk(KERN_ERR PFX "No reserved IO PDIR entry found; " - "GART disabled\n"); - return -ENODEV; - } - - return 0; -} - -static int __init hp_zx1_ioc_owner(u8 ioc_rev) -{ - struct _hp_private *hp = &hp_private; - - printk(KERN_INFO PFX "HP ZX1 IOC: IOPDIR dedicated to GART\n"); - - /* - * Select an IOV page size no larger than system page size. - */ - if (PAGE_SIZE >= KB(64)) { - hp->io_tlb_shift = 16; - hp->io_tlb_ps = 3; - } else if (PAGE_SIZE >= KB(16)) { - hp->io_tlb_shift = 14; - hp->io_tlb_ps = 2; - } else if (PAGE_SIZE >= KB(8)) { - hp->io_tlb_shift = 13; - hp->io_tlb_ps = 1; - } else { - hp->io_tlb_shift = 12; - hp->io_tlb_ps = 0; - } - hp->io_page_size = 1 << hp->io_tlb_shift; - hp->io_pages_per_kpage = PAGE_SIZE / hp->io_page_size; - - hp->iova_base = HP_ZX1_IOVA_BASE; - hp->gart_size = HP_ZX1_GART_SIZE; - hp->gart_base = hp->iova_base + HP_ZX1_IOVA_SIZE - hp->gart_size; - - hp->gatt_entries = hp->gart_size / hp->io_page_size; - hp->io_pdir_size = (HP_ZX1_IOVA_SIZE / hp->io_page_size) * sizeof(u64); - - return 0; -} - -static int __init hp_zx1_ioc_init(void) -{ - struct _hp_private *hp = &hp_private; - struct pci_dev *ioc; - int i; - u8 ioc_rev; - - ioc = pci_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_ZX1_IOC, NULL); - if (!ioc) { - printk(KERN_ERR PFX "Detected HP ZX1 AGP bridge but no IOC\n"); - return -ENODEV; - } - hp->ioc = ioc; - - pci_read_config_byte(ioc, PCI_REVISION_ID, &ioc_rev); - - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - if (pci_resource_flags(ioc, i) == IORESOURCE_MEM) { - hp->registers = (u8 *) ioremap(pci_resource_start(ioc, - i), - pci_resource_len(ioc, i)); - break; - } - } - if (!hp->registers) { - printk(KERN_ERR PFX "Detected HP ZX1 AGP bridge but no CSRs\n"); - - return -ENODEV; - } - - /* - * If the IOTLB is currently disabled, we can take it over. - * Otherwise, we have to share with sba_iommu. - */ - hp->io_pdir_owner = (INREG64(hp->registers, HP_ZX1_IBASE) & 0x1) == 0; - - if (hp->io_pdir_owner) - return hp_zx1_ioc_owner(ioc_rev); - - return hp_zx1_ioc_shared(); -} - -static int hp_zx1_fetch_size(void) -{ - int size; - - size = hp_private.gart_size / MB(1); - hp_zx1_sizes[0].size = size; - agp_bridge.current_size = (void *) &hp_zx1_sizes[0]; - return size; -} - -static int hp_zx1_configure(void) -{ - struct _hp_private *hp = &hp_private; - - agp_bridge.gart_bus_addr = hp->gart_base; - agp_bridge.capndx = pci_find_capability(agp_bridge.dev, PCI_CAP_ID_AGP); - pci_read_config_dword(agp_bridge.dev, - agp_bridge.capndx + PCI_AGP_STATUS, &agp_bridge.mode); - - if (hp->io_pdir_owner) { - OUTREG64(hp->registers, HP_ZX1_PDIR_BASE, - virt_to_phys(hp->io_pdir)); - OUTREG64(hp->registers, HP_ZX1_TCNFG, hp->io_tlb_ps); - OUTREG64(hp->registers, HP_ZX1_IMASK, ~(HP_ZX1_IOVA_SIZE - 1)); - OUTREG64(hp->registers, HP_ZX1_IBASE, hp->iova_base | 0x1); - OUTREG64(hp->registers, HP_ZX1_PCOM, - hp->iova_base | log2(HP_ZX1_IOVA_SIZE)); - INREG64(hp->registers, HP_ZX1_PCOM); - } - - return 0; -} - -static void hp_zx1_cleanup(void) -{ - struct _hp_private *hp = &hp_private; - - if (hp->io_pdir_owner) - OUTREG64(hp->registers, HP_ZX1_IBASE, 0); - iounmap((void *) hp->registers); -} - -static void hp_zx1_tlbflush(agp_memory * mem) -{ - struct _hp_private *hp = &hp_private; - - OUTREG64(hp->registers, HP_ZX1_PCOM, - hp->gart_base | log2(hp->gart_size)); - INREG64(hp->registers, HP_ZX1_PCOM); -} - -static int hp_zx1_create_gatt_table(void) -{ - struct _hp_private *hp = &hp_private; - int i; - - if (hp->io_pdir_owner) { - hp->io_pdir = (u64 *) __get_free_pages(GFP_KERNEL, - get_order(hp->io_pdir_size)); - if (!hp->io_pdir) { - printk(KERN_ERR PFX "Couldn't allocate contiguous " - "memory for I/O PDIR\n"); - hp->gatt = 0; - hp->gatt_entries = 0; - return -ENOMEM; - } - memset(hp->io_pdir, 0, hp->io_pdir_size); - - hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)]; - } - - for (i = 0; i < hp->gatt_entries; i++) { - hp->gatt[i] = (unsigned long) agp_bridge.scratch_page; - } - - return 0; -} - -static int hp_zx1_free_gatt_table(void) -{ - struct _hp_private *hp = &hp_private; - - if (hp->io_pdir_owner) - free_pages((unsigned long) hp->io_pdir, - get_order(hp->io_pdir_size)); - else - hp->gatt[0] = HP_ZX1_SBA_IOMMU_COOKIE; - return 0; -} - -static int hp_zx1_insert_memory(agp_memory * mem, off_t pg_start, int type) -{ - struct _hp_private *hp = &hp_private; - int i, k; - off_t j, io_pg_start; - int io_pg_count; - - if (type != 0 || mem->type != 0) { - return -EINVAL; - } - - io_pg_start = hp->io_pages_per_kpage * pg_start; - io_pg_count = hp->io_pages_per_kpage * mem->page_count; - if ((io_pg_start + io_pg_count) > hp->gatt_entries) { - return -EINVAL; - } - - j = io_pg_start; - while (j < (io_pg_start + io_pg_count)) { - if (hp->gatt[j]) { - return -EBUSY; - } - j++; - } - - if (mem->is_flushed == FALSE) { - CACHE_FLUSH(); - mem->is_flushed = TRUE; - } - - for (i = 0, j = io_pg_start; i < mem->page_count; i++) { - unsigned long paddr; - - paddr = mem->memory[i]; - for (k = 0; - k < hp->io_pages_per_kpage; - k++, j++, paddr += hp->io_page_size) { - hp->gatt[j] = agp_bridge.mask_memory(paddr, type); - } - } - - agp_bridge.tlb_flush(mem); - return 0; -} - -static int hp_zx1_remove_memory(agp_memory * mem, off_t pg_start, int type) -{ - struct _hp_private *hp = &hp_private; - int i, io_pg_start, io_pg_count; - - if (type != 0 || mem->type != 0) { - return -EINVAL; - } - - io_pg_start = hp->io_pages_per_kpage * pg_start; - io_pg_count = hp->io_pages_per_kpage * mem->page_count; - for (i = io_pg_start; i < io_pg_count + io_pg_start; i++) { - hp->gatt[i] = agp_bridge.scratch_page; - } - - agp_bridge.tlb_flush(mem); - return 0; -} - -static unsigned long hp_zx1_mask_memory(unsigned long addr, int type) -{ - return HP_ZX1_PDIR_VALID_BIT | addr; -} - -static unsigned long hp_zx1_unmask_memory(unsigned long addr) -{ - return addr & ~(HP_ZX1_PDIR_VALID_BIT); -} - -static int __init hp_zx1_setup (struct pci_dev *pdev) -{ - agp_bridge.masks = hp_zx1_masks; - agp_bridge.num_of_masks = 1; - agp_bridge.dev_private_data = NULL; - agp_bridge.size_type = FIXED_APER_SIZE; - agp_bridge.needs_scratch_page = FALSE; - agp_bridge.configure = hp_zx1_configure; - agp_bridge.fetch_size = hp_zx1_fetch_size; - agp_bridge.cleanup = hp_zx1_cleanup; - agp_bridge.tlb_flush = hp_zx1_tlbflush; - agp_bridge.mask_memory = hp_zx1_mask_memory; - agp_bridge.unmask_memory = hp_zx1_unmask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; - agp_bridge.cache_flush = global_cache_flush; - agp_bridge.create_gatt_table = hp_zx1_create_gatt_table; - agp_bridge.free_gatt_table = hp_zx1_free_gatt_table; - agp_bridge.insert_memory = hp_zx1_insert_memory; - agp_bridge.remove_memory = hp_zx1_remove_memory; - agp_bridge.alloc_by_type = agp_generic_alloc_by_type; - agp_bridge.free_by_type = agp_generic_free_by_type; - agp_bridge.agp_alloc_page = agp_generic_alloc_page; - agp_bridge.agp_destroy_page = agp_generic_destroy_page; - agp_bridge.cant_use_aperture = 1; - - return hp_zx1_ioc_init(); - - (void) pdev; /* unused */ -} - -#endif /* CONFIG_AGP_HP_ZX1 */ - -/* per-chipset initialization data. - * note -- all chipsets for a single vendor MUST be grouped together - */ -static struct { - unsigned short device_id; /* first, to make table easier to read */ - unsigned short vendor_id; - enum chipset_type chipset; - const char *vendor_name; - const char *chipset_name; - int (*chipset_setup) (struct pci_dev *pdev); -} agp_bridge_info[] __initdata = { - -#ifdef CONFIG_AGP_ALI - { PCI_DEVICE_ID_AL_M1541_0, - PCI_VENDOR_ID_AL, - ALI_M1541, - "Ali", - "M1541", - ali_generic_setup }, - { PCI_DEVICE_ID_AL_M1621_0, - PCI_VENDOR_ID_AL, - ALI_M1621, - "Ali", - "M1621", - ali_generic_setup }, - { PCI_DEVICE_ID_AL_M1631_0, - PCI_VENDOR_ID_AL, - ALI_M1631, - "Ali", - "M1631", - ali_generic_setup }, - { PCI_DEVICE_ID_AL_M1632_0, - PCI_VENDOR_ID_AL, - ALI_M1632, - "Ali", - "M1632", - ali_generic_setup }, - { PCI_DEVICE_ID_AL_M1641_0, - PCI_VENDOR_ID_AL, - ALI_M1641, - "Ali", - "M1641", - ali_generic_setup }, - { PCI_DEVICE_ID_AL_M1644_0, - PCI_VENDOR_ID_AL, - ALI_M1644, - "Ali", - "M1644", - ali_generic_setup }, - { PCI_DEVICE_ID_AL_M1647_0, - PCI_VENDOR_ID_AL, - ALI_M1647, - "Ali", - "M1647", - ali_generic_setup }, - { PCI_DEVICE_ID_AL_M1651_0, - PCI_VENDOR_ID_AL, - ALI_M1651, - "Ali", - "M1651", - ali_generic_setup }, - { 0, - PCI_VENDOR_ID_AL, - ALI_GENERIC, - "Ali", - "Generic", - ali_generic_setup }, -#endif /* CONFIG_AGP_ALI */ - -#ifdef CONFIG_AGP_AMD - { PCI_DEVICE_ID_AMD_IRONGATE_0, - PCI_VENDOR_ID_AMD, - AMD_IRONGATE, - "AMD", - "Irongate", - amd_irongate_setup }, - { PCI_DEVICE_ID_AMD_761_0, - PCI_VENDOR_ID_AMD, - AMD_761, - "AMD", - "761", - amd_irongate_setup }, - { PCI_DEVICE_ID_AMD_762_0, - PCI_VENDOR_ID_AMD, - AMD_762, - "AMD", - "760MP", - amd_irongate_setup }, - { 0, - PCI_VENDOR_ID_AMD, - AMD_GENERIC, - "AMD", - "Generic", - amd_irongate_setup }, -#endif /* CONFIG_AGP_AMD */ - -#ifdef CONFIG_AGP_INTEL - { PCI_DEVICE_ID_INTEL_82443LX_0, - PCI_VENDOR_ID_INTEL, - INTEL_LX, - "Intel", - "440LX", - intel_generic_setup }, - { PCI_DEVICE_ID_INTEL_82443BX_0, - PCI_VENDOR_ID_INTEL, - INTEL_BX, - "Intel", - "440BX", - intel_generic_setup }, - { PCI_DEVICE_ID_INTEL_82443GX_0, - PCI_VENDOR_ID_INTEL, - INTEL_GX, - "Intel", - "440GX", - intel_generic_setup }, - { PCI_DEVICE_ID_INTEL_815_0, - PCI_VENDOR_ID_INTEL, - INTEL_I815, - "Intel", - "i815", - intel_generic_setup }, - { PCI_DEVICE_ID_INTEL_820_0, - PCI_VENDOR_ID_INTEL, - INTEL_I820, - "Intel", - "i820", - intel_820_setup }, - { PCI_DEVICE_ID_INTEL_820_UP_0, - PCI_VENDOR_ID_INTEL, - INTEL_I820, - "Intel", - "i820", - intel_820_setup }, - { PCI_DEVICE_ID_INTEL_830_M_0, - PCI_VENDOR_ID_INTEL, - INTEL_I830_M, - "Intel", - "i830M", - intel_830mp_setup }, - { PCI_DEVICE_ID_INTEL_845_G_0, - PCI_VENDOR_ID_INTEL, - INTEL_I845_G, - "Intel", - "i845G", - intel_830mp_setup }, - { PCI_DEVICE_ID_INTEL_840_0, - PCI_VENDOR_ID_INTEL, - INTEL_I840, - "Intel", - "i840", - intel_840_setup }, - { PCI_DEVICE_ID_INTEL_845_0, - PCI_VENDOR_ID_INTEL, - INTEL_I845, - "Intel", - "i845", - intel_845_setup }, - { PCI_DEVICE_ID_INTEL_850_0, - PCI_VENDOR_ID_INTEL, - INTEL_I850, - "Intel", - "i850", -intel_850_setup }, - { PCI_DEVICE_ID_INTEL_860_0, - PCI_VENDOR_ID_INTEL, - INTEL_I860, - "Intel", - "i860", - intel_860_setup }, - { 0, - PCI_VENDOR_ID_INTEL, - INTEL_GENERIC, - "Intel", - "Generic", - intel_generic_setup }, - -#endif /* CONFIG_AGP_INTEL */ - -#ifdef CONFIG_AGP_SIS - { PCI_DEVICE_ID_SI_740, - PCI_VENDOR_ID_SI, - SIS_GENERIC, - "SiS", - "740", - sis_generic_setup }, - { PCI_DEVICE_ID_SI_650, - PCI_VENDOR_ID_SI, - SIS_GENERIC, - "SiS", - "650", - sis_generic_setup }, - { PCI_DEVICE_ID_SI_645, - PCI_VENDOR_ID_SI, - SIS_GENERIC, - "SiS", - "645", - sis_generic_setup }, - { PCI_DEVICE_ID_SI_735, - PCI_VENDOR_ID_SI, - SIS_GENERIC, - "SiS", - "735", - sis_generic_setup }, - { PCI_DEVICE_ID_SI_730, - PCI_VENDOR_ID_SI, - SIS_GENERIC, - "SiS", - "730", - sis_generic_setup }, - { PCI_DEVICE_ID_SI_630, - PCI_VENDOR_ID_SI, - SIS_GENERIC, - "SiS", - "630", - sis_generic_setup }, - { PCI_DEVICE_ID_SI_540, - PCI_VENDOR_ID_SI, - SIS_GENERIC, - "SiS", - "540", - sis_generic_setup }, - { PCI_DEVICE_ID_SI_620, - PCI_VENDOR_ID_SI, - SIS_GENERIC, - "SiS", - "620", - sis_generic_setup }, - { PCI_DEVICE_ID_SI_530, - PCI_VENDOR_ID_SI, - SIS_GENERIC, - "SiS", - "530", - sis_generic_setup }, - { PCI_DEVICE_ID_SI_550, - PCI_VENDOR_ID_SI, - SIS_GENERIC, - "SiS", - "550", - sis_generic_setup }, - { 0, - PCI_VENDOR_ID_SI, - SIS_GENERIC, - "SiS", - "Generic", - sis_generic_setup }, -#endif /* CONFIG_AGP_SIS */ - -#ifdef CONFIG_AGP_VIA - { PCI_DEVICE_ID_VIA_8501_0, - PCI_VENDOR_ID_VIA, - VIA_MVP4, - "Via", - "MVP4", - via_generic_setup }, - { PCI_DEVICE_ID_VIA_82C597_0, - PCI_VENDOR_ID_VIA, - VIA_VP3, - "Via", - "VP3", - via_generic_setup }, - { PCI_DEVICE_ID_VIA_82C598_0, - PCI_VENDOR_ID_VIA, - VIA_MVP3, - "Via", - "MVP3", - via_generic_setup }, - { PCI_DEVICE_ID_VIA_82C691_0, - PCI_VENDOR_ID_VIA, - VIA_APOLLO_PRO, - "Via", - "Apollo Pro", - via_generic_setup }, - { PCI_DEVICE_ID_VIA_8371_0, - PCI_VENDOR_ID_VIA, - VIA_APOLLO_KX133, - "Via", - "Apollo Pro KX133", - via_generic_setup }, - { PCI_DEVICE_ID_VIA_8363_0, - PCI_VENDOR_ID_VIA, - VIA_APOLLO_KT133, - "Via", - "Apollo Pro KT133", - via_generic_setup }, - { PCI_DEVICE_ID_VIA_8367_0, - PCI_VENDOR_ID_VIA, - VIA_APOLLO_KT133, - "Via", - "Apollo Pro KT266", - via_generic_setup }, - { 0, - PCI_VENDOR_ID_VIA, - VIA_GENERIC, - "Via", - "Generic", - via_generic_setup }, -#endif /* CONFIG_AGP_VIA */ - -#ifdef CONFIG_AGP_HP_ZX1 - { PCI_DEVICE_ID_HP_ZX1_LBA, - PCI_VENDOR_ID_HP, - HP_ZX1, - "HP", - "ZX1", - hp_zx1_setup }, -#endif - - { 0, }, /* dummy final entry, always present */ -}; - - -/* scan table above for supported devices */ -static int __init agp_lookup_host_bridge (struct pci_dev *pdev) -{ - int i; - - for (i = 0; i < ARRAY_SIZE (agp_bridge_info); i++) - if (pdev->vendor == agp_bridge_info[i].vendor_id) - break; - - if (i >= ARRAY_SIZE (agp_bridge_info)) { - printk (KERN_DEBUG PFX "unsupported bridge\n"); - return -ENODEV; - } - - while ((i < ARRAY_SIZE (agp_bridge_info)) && - (agp_bridge_info[i].vendor_id == pdev->vendor)) { - if (pdev->device == agp_bridge_info[i].device_id) { -#ifdef CONFIG_AGP_ALI - if (pdev->device == PCI_DEVICE_ID_AL_M1621_0) { - u8 hidden_1621_id; - - pci_read_config_byte(pdev, 0xFB, &hidden_1621_id); - switch (hidden_1621_id) { - case 0x31: - agp_bridge_info[i].chipset_name="M1631"; - break; - case 0x32: - agp_bridge_info[i].chipset_name="M1632"; - break; - case 0x41: - agp_bridge_info[i].chipset_name="M1641"; - break; - case 0x43: - break; - case 0x47: - agp_bridge_info[i].chipset_name="M1647"; - break; - case 0x51: - agp_bridge_info[i].chipset_name="M1651"; - break; - default: - break; - } - } -#endif - - printk (KERN_INFO PFX "Detected %s %s chipset\n", - agp_bridge_info[i].vendor_name, - agp_bridge_info[i].chipset_name); - agp_bridge.type = agp_bridge_info[i].chipset; - return agp_bridge_info[i].chipset_setup (pdev); - } - - i++; - } - - i--; /* point to vendor generic entry (device_id == 0) */ - - /* try init anyway, if user requests it AND - * there is a 'generic' bridge entry for this vendor */ - if (agp_try_unsupported && agp_bridge_info[i].device_id == 0) { - printk(KERN_WARNING PFX "Trying generic %s routines" - " for device id: %04x\n", - agp_bridge_info[i].vendor_name, pdev->device); - agp_bridge.type = agp_bridge_info[i].chipset; - return agp_bridge_info[i].chipset_setup (pdev); - } - - printk(KERN_ERR PFX "Unsupported %s chipset (device id: %04x)," - " you might want to try agp_try_unsupported=1.\n", - agp_bridge_info[i].vendor_name, pdev->device); - return -ENODEV; -} - - -/* Supported Device Scanning routine */ - -static int __init agp_find_supported_device(void) -{ - struct pci_dev *dev = NULL; - u8 cap_ptr = 0x00; - - if ((dev = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8, NULL)) == NULL) - return -ENODEV; - - agp_bridge.dev = dev; - - /* Need to test for I810 here */ -#ifdef CONFIG_AGP_I810 - if (dev->vendor == PCI_VENDOR_ID_INTEL) { - struct pci_dev *i810_dev; - - switch (dev->device) { - case PCI_DEVICE_ID_INTEL_810_0: - i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_810_1, - NULL); - if (i810_dev == NULL) { - printk(KERN_ERR PFX "Detected an Intel i810," - " but could not find the secondary" - " device.\n"); - return -ENODEV; - } - printk(KERN_INFO PFX "Detected an Intel " - "i810 Chipset.\n"); - agp_bridge.type = INTEL_I810; - return intel_i810_setup (i810_dev); - - case PCI_DEVICE_ID_INTEL_810_DC100_0: - i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_810_DC100_1, - NULL); - if (i810_dev == NULL) { - printk(KERN_ERR PFX "Detected an Intel i810 " - "DC100, but could not find the " - "secondary device.\n"); - return -ENODEV; - } - printk(KERN_INFO PFX "Detected an Intel i810 " - "DC100 Chipset.\n"); - agp_bridge.type = INTEL_I810; - return intel_i810_setup(i810_dev); - - case PCI_DEVICE_ID_INTEL_810_E_0: - i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_810_E_1, - NULL); - if (i810_dev == NULL) { - printk(KERN_ERR PFX "Detected an Intel i810 E" - ", but could not find the secondary " - "device.\n"); - return -ENODEV; - } - printk(KERN_INFO PFX "Detected an Intel i810 E " - "Chipset.\n"); - agp_bridge.type = INTEL_I810; - return intel_i810_setup(i810_dev); - - case PCI_DEVICE_ID_INTEL_815_0: - /* The i815 can operate either as an i810 style - * integrated device, or as an AGP4X motherboard. - * - * This only addresses the first mode: - */ - i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_815_1, - NULL); - if (i810_dev == NULL) { - printk(KERN_ERR PFX "agpgart: Detected an " - "Intel i815, but could not find the" - " secondary device. Assuming a " - "non-integrated video card.\n"); - break; - } - printk(KERN_INFO PFX "agpgart: Detected an Intel i815 " - "Chipset.\n"); - agp_bridge.type = INTEL_I810; - return intel_i810_setup(i810_dev); - - case PCI_DEVICE_ID_INTEL_845_G_0: - i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_845_G_1, NULL); - if(i810_dev && PCI_FUNC(i810_dev->devfn) != 0) { - i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_845_G_1, i810_dev); - } - - if (i810_dev == NULL) { - /* - * We probably have a I845MP chipset - * with an external graphics - * card. It will be initialized later - */ - agp_bridge.type = INTEL_I845_G; - break; - } - printk(KERN_INFO PFX "Detected an Intel " - "845G Chipset.\n"); - agp_bridge.type = INTEL_I810; - return intel_i830_setup(i810_dev); - - case PCI_DEVICE_ID_INTEL_830_M_0: - i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_830_M_1, - NULL); - if(i810_dev && PCI_FUNC(i810_dev->devfn) != 0) { - i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_830_M_1, - i810_dev); - } - - if (i810_dev == NULL) { - /* Intel 830MP with external graphic card */ - /* It will be initialized later */ - agp_bridge.type = INTEL_I830_M; - break; - } - printk(KERN_INFO PFX "Detected an Intel " - "830M Chipset.\n"); - agp_bridge.type = INTEL_I810; - return intel_i830_setup(i810_dev); - default: - break; - } - } -#endif /* CONFIG_AGP_I810 */ - -#ifdef CONFIG_AGP_SWORKS - /* Everything is on func 1 here so we are hardcoding function one */ - if (dev->vendor == PCI_VENDOR_ID_SERVERWORKS) { - struct pci_dev *bridge_dev; - - bridge_dev = pci_find_slot ((unsigned int)dev->bus->number, - PCI_DEVFN(0, 1)); - if(bridge_dev == NULL) { - printk(KERN_INFO PFX "agpgart: Detected a Serverworks " - "Chipset, but could not find the secondary " - "device.\n"); - return -ENODEV; - } - - switch (dev->device) { - case PCI_DEVICE_ID_SERVERWORKS_HE: - agp_bridge.type = SVWRKS_HE; - return serverworks_setup(bridge_dev); - - case PCI_DEVICE_ID_SERVERWORKS_LE: - case 0x0007: - agp_bridge.type = SVWRKS_LE; - return serverworks_setup(bridge_dev); - - default: - if(agp_try_unsupported) { - agp_bridge.type = SVWRKS_GENERIC; - return serverworks_setup(bridge_dev); - } - break; - } - } - -#endif /* CONFIG_AGP_SWORKS */ - -#ifdef CONFIG_AGP_HP_ZX1 - if (dev->vendor == PCI_VENDOR_ID_HP) { - do { - /* ZX1 LBAs can be either PCI or AGP bridges */ - if (pci_find_capability(dev, PCI_CAP_ID_AGP)) { - printk(KERN_INFO PFX "Detected HP ZX1 AGP " - "chipset at %s\n", dev->slot_name); - agp_bridge.type = HP_ZX1; - agp_bridge.dev = dev; - return hp_zx1_setup(dev); - } - dev = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8, dev); - } while (dev); - return -ENODEV; - } -#endif /* CONFIG_AGP_HP_ZX1 */ - - /* find capndx */ - cap_ptr = pci_find_capability(dev, PCI_CAP_ID_AGP); - if (cap_ptr == 0x00) - return -ENODEV; - agp_bridge.capndx = cap_ptr; - - /* Fill in the mode register */ - pci_read_config_dword(agp_bridge.dev, - agp_bridge.capndx + 4, - &agp_bridge.mode); - - /* probe for known chipsets */ - return agp_lookup_host_bridge (dev); -} - -struct agp_max_table { - int mem; - int agp; -}; - -static struct agp_max_table maxes_table[9] __initdata = -{ - {0, 0}, - {32, 4}, - {64, 28}, - {128, 96}, - {256, 204}, - {512, 440}, - {1024, 942}, - {2048, 1920}, - {4096, 3932} -}; - -static int __init agp_find_max (void) -{ - long memory, index, result; - - memory = virt_to_phys(high_memory) >> 20; - index = 1; - - while ((memory > maxes_table[index].mem) && - (index < 8)) { - index++; - } - - result = maxes_table[index - 1].agp + - ( (memory - maxes_table[index - 1].mem) * - (maxes_table[index].agp - maxes_table[index - 1].agp)) / - (maxes_table[index].mem - maxes_table[index - 1].mem); - - printk(KERN_INFO PFX "Maximum main memory to use " - "for agp memory: %ldM\n", result); - result = result << (20 - PAGE_SHIFT); - return result; -} - -#define AGPGART_VERSION_MAJOR 0 -#define AGPGART_VERSION_MINOR 99 - -static agp_version agp_current_version = -{ - AGPGART_VERSION_MAJOR, - AGPGART_VERSION_MINOR -}; - -static int __init agp_backend_initialize(void) -{ - int size_value, rc, got_gatt=0, got_keylist=0; - - memset(&agp_bridge, 0, sizeof(struct agp_bridge_data)); - agp_bridge.type = NOT_SUPPORTED; - agp_bridge.max_memory_agp = agp_find_max(); - agp_bridge.version = &agp_current_version; - - rc = agp_find_supported_device(); - if (rc) { - /* not KERN_ERR because error msg should have already printed */ - printk(KERN_DEBUG PFX "no supported devices found.\n"); - return rc; - } - - if (agp_bridge.needs_scratch_page == TRUE) { - void *addr; - addr = agp_bridge.agp_alloc_page(); - - if (addr == NULL) { - printk(KERN_ERR PFX "unable to get memory for " - "scratch page.\n"); - return -ENOMEM; - } - agp_bridge.scratch_page = virt_to_phys(addr); - agp_bridge.scratch_page = - agp_bridge.mask_memory(agp_bridge.scratch_page, 0); - } - - size_value = agp_bridge.fetch_size(); - - if (size_value == 0) { - printk(KERN_ERR PFX "unable to determine aperture size.\n"); - rc = -EINVAL; - goto err_out; - } - if (agp_bridge.create_gatt_table()) { - printk(KERN_ERR PFX "unable to get memory for graphics " - "translation table.\n"); - rc = -ENOMEM; - goto err_out; - } - got_gatt = 1; - - agp_bridge.key_list = vmalloc(PAGE_SIZE * 4); - if (agp_bridge.key_list == NULL) { - printk(KERN_ERR PFX "error allocating memory for key lists.\n"); - rc = -ENOMEM; - goto err_out; - } - got_keylist = 1; - - /* FIXME vmalloc'd memory not guaranteed contiguous */ - memset(agp_bridge.key_list, 0, PAGE_SIZE * 4); - - if (agp_bridge.configure()) { - printk(KERN_ERR PFX "error configuring host chipset.\n"); - rc = -EINVAL; - goto err_out; - } - - printk(KERN_INFO PFX "AGP aperture is %dM @ 0x%lx\n", - size_value, agp_bridge.gart_bus_addr); - - return 0; - -err_out: - if (agp_bridge.needs_scratch_page == TRUE) { - agp_bridge.scratch_page &= ~(0x00000fff); - agp_bridge.agp_destroy_page(phys_to_virt(agp_bridge.scratch_page)); - } - if (got_gatt) - agp_bridge.free_gatt_table(); - if (got_keylist) - vfree(agp_bridge.key_list); - return rc; -} - - -/* cannot be __exit b/c as it could be called from __init code */ -static void agp_backend_cleanup(void) -{ - agp_bridge.cleanup(); - agp_bridge.free_gatt_table(); - vfree(agp_bridge.key_list); - - if (agp_bridge.needs_scratch_page == TRUE) { - agp_bridge.scratch_page &= ~(0x00000fff); - agp_bridge.agp_destroy_page(phys_to_virt(agp_bridge.scratch_page)); - } -} - -static int agp_power(struct pm_dev *dev, pm_request_t rq, void *data) -{ - switch(rq) - { - case PM_SUSPEND: - return agp_bridge.suspend(); - case PM_RESUME: - agp_bridge.resume(); - return 0; - } - return 0; -} - -extern int agp_frontend_initialize(void); -extern void agp_frontend_cleanup(void); - -static const drm_agp_t drm_agp = { - &agp_free_memory, - &agp_allocate_memory, - &agp_bind_memory, - &agp_unbind_memory, - &agp_enable, - &agp_backend_acquire, - &agp_backend_release, - &agp_copy_info -}; - -static int __init agp_init(void) -{ - int ret_val; - - printk(KERN_INFO "Linux agpgart interface v%d.%d (c) Jeff Hartmann\n", - AGPGART_VERSION_MAJOR, AGPGART_VERSION_MINOR); - - ret_val = agp_backend_initialize(); - if (ret_val) { - agp_bridge.type = NOT_SUPPORTED; - return ret_val; - } - ret_val = agp_frontend_initialize(); - if (ret_val) { - agp_bridge.type = NOT_SUPPORTED; - agp_backend_cleanup(); - return ret_val; - } - - inter_module_register("drm_agp", THIS_MODULE, &drm_agp); - - pm_register(PM_PCI_DEV, PM_PCI_ID(agp_bridge.dev), agp_power); - return 0; -} - -static void __exit agp_cleanup(void) -{ - pm_unregister_all(agp_power); - agp_frontend_cleanup(); - agp_backend_cleanup(); - inter_module_unregister("drm_agp"); -} - -module_init(agp_init); -module_exit(agp_cleanup); diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c new file mode 100644 index 000000000000..e60ca2a30fd3 --- /dev/null +++ b/drivers/char/agp/ali-agp.c @@ -0,0 +1,265 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * TODO: + * - Allocate more than order 0 pages to avoid too much linear map splitting. + */ +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/agp_backend.h> +#include "agp.h" + +static int ali_fetch_size(void) +{ + int i; + u32 temp; + struct aper_size_info_32 *values; + + pci_read_config_dword(agp_bridge.dev, ALI_ATTBASE, &temp); + temp &= ~(0xfffffff0); + values = A_SIZE_32(agp_bridge.aperture_sizes); + + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if (temp == values[i].size_value) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + + return 0; +} + +static void ali_tlbflush(agp_memory * mem) +{ + u32 temp; + + pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp); +// clear tag + pci_write_config_dword(agp_bridge.dev, ALI_TAGCTRL, + ((temp & 0xfffffff0) | 0x00000001|0x00000002)); +} + +static void ali_cleanup(void) +{ + struct aper_size_info_32 *previous_size; + u32 temp; + + previous_size = A_SIZE_32(agp_bridge.previous_size); + + pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp); +// clear tag + pci_write_config_dword(agp_bridge.dev, ALI_TAGCTRL, + ((temp & 0xffffff00) | 0x00000001|0x00000002)); + + pci_read_config_dword(agp_bridge.dev, ALI_ATTBASE, &temp); + pci_write_config_dword(agp_bridge.dev, ALI_ATTBASE, + ((temp & 0x00000ff0) | previous_size->size_value)); +} + +static int ali_configure(void) +{ + u32 temp; + struct aper_size_info_32 *current_size; + + current_size = A_SIZE_32(agp_bridge.current_size); + + /* aperture size and gatt addr */ + pci_read_config_dword(agp_bridge.dev, ALI_ATTBASE, &temp); + temp = (((temp & 0x00000ff0) | (agp_bridge.gatt_bus_addr & 0xfffff000)) + | (current_size->size_value & 0xf)); + pci_write_config_dword(agp_bridge.dev, ALI_ATTBASE, temp); + + /* tlb control */ + + /* + * Question: Jeff, ALi's patch deletes this: + * + * pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp); + * pci_write_config_dword(agp_bridge.dev, ALI_TLBCTRL, + * ((temp & 0xffffff00) | 0x00000010)); + * + * and replaces it with the following, which seems to duplicate the + * next couple of lines below it. I suspect this was an oversight, + * but you might want to check up on this? + */ + + pci_read_config_dword(agp_bridge.dev, ALI_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* address to map to */ + pci_read_config_dword(agp_bridge.dev, ALI_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + +#if 0 + if (agp_bridge.type == ALI_M1541) { + u32 nlvm_addr = 0; + + switch (current_size->size_value) { + case 0: break; + case 1: nlvm_addr = 0x100000;break; + case 2: nlvm_addr = 0x200000;break; + case 3: nlvm_addr = 0x400000;break; + case 4: nlvm_addr = 0x800000;break; + case 6: nlvm_addr = 0x1000000;break; + case 7: nlvm_addr = 0x2000000;break; + case 8: nlvm_addr = 0x4000000;break; + case 9: nlvm_addr = 0x8000000;break; + case 10: nlvm_addr = 0x10000000;break; + default: break; + } + nlvm_addr--; + nlvm_addr&=0xfff00000; + + nlvm_addr+= agp_bridge.gart_bus_addr; + nlvm_addr|=(agp_bridge.gart_bus_addr>>12); + printk(KERN_INFO PFX "nlvm top &base = %8x\n",nlvm_addr); + } +#endif + + pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp); + temp &= 0xffffff7f; //enable TLB + pci_write_config_dword(agp_bridge.dev, ALI_TLBCTRL, temp); + + return 0; +} + +static unsigned long ali_mask_memory(unsigned long addr, int type) +{ + /* Memory type is ignored */ + + return addr | agp_bridge.masks[0].mask; +} + +static void ali_cache_flush(void) +{ + global_cache_flush(); + + if (agp_bridge.type == ALI_M1541) { + int i, page_count; + u32 temp; + + page_count = 1 << A_SIZE_32(agp_bridge.current_size)->page_order; + for (i = 0; i < PAGE_SIZE * page_count; i += PAGE_SIZE) { + pci_read_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, &temp); + pci_write_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, + (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | + (agp_bridge.gatt_bus_addr + i)) | + ALI_CACHE_FLUSH_EN)); + } + } +} + +static void *ali_alloc_page(void) +{ + void *adr = agp_generic_alloc_page(); + u32 temp; + + if (adr == 0) + return 0; + + if (agp_bridge.type == ALI_M1541) { + pci_read_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, &temp); + pci_write_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, + (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | + virt_to_phys(adr)) | + ALI_CACHE_FLUSH_EN )); + } + return adr; +} + +static void ali_destroy_page(void * addr) +{ + u32 temp; + + if (addr == NULL) + return; + + global_cache_flush(); + + if (agp_bridge.type == ALI_M1541) { + pci_read_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, &temp); + pci_write_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, + (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | + virt_to_phys(addr)) | + ALI_CACHE_FLUSH_EN)); + } + + agp_generic_destroy_page(addr); +} + +/* Setup function */ +static struct gatt_mask ali_generic_masks[] = +{ + {mask: 0x00000000, type: 0} +}; + +static struct aper_size_info_32 ali_generic_sizes[7] = +{ + {256, 65536, 6, 10}, + {128, 32768, 5, 9}, + {64, 16384, 4, 8}, + {32, 8192, 3, 7}, + {16, 4096, 2, 6}, + {8, 2048, 1, 4}, + {4, 1024, 0, 3} +}; + +int __init ali_generic_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = ali_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) ali_generic_sizes; + agp_bridge.size_type = U32_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = ali_configure; + agp_bridge.fetch_size = ali_fetch_size; + agp_bridge.cleanup = ali_cleanup; + agp_bridge.tlb_flush = ali_tlbflush; + agp_bridge.mask_memory = ali_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = ali_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = ali_alloc_page; + agp_bridge.agp_destroy_page = ali_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; + + (void) pdev; /* unused */ +} + diff --git a/drivers/char/agp/amd-agp.c b/drivers/char/agp/amd-agp.c new file mode 100644 index 000000000000..9fc81a0011e6 --- /dev/null +++ b/drivers/char/agp/amd-agp.c @@ -0,0 +1,408 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * TODO: + * - Allocate more than order 0 pages to avoid too much linear map splitting. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/agp_backend.h> +#include "agp.h" + +struct amd_page_map { + unsigned long *real; + unsigned long *remapped; +}; + +static struct _amd_irongate_private { + volatile u8 *registers; + struct amd_page_map **gatt_pages; + int num_tables; +} amd_irongate_private; + +static int amd_create_page_map(struct amd_page_map *page_map) +{ + int i; + + page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL); + if (page_map->real == NULL) { + return -ENOMEM; + } + SetPageReserved(virt_to_page(page_map->real)); + CACHE_FLUSH(); + page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real), + PAGE_SIZE); + if (page_map->remapped == NULL) { + ClearPageReserved(virt_to_page(page_map->real)); + free_page((unsigned long) page_map->real); + page_map->real = NULL; + return -ENOMEM; + } + CACHE_FLUSH(); + + for(i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) { + page_map->remapped[i] = agp_bridge.scratch_page; + } + + return 0; +} + +static void amd_free_page_map(struct amd_page_map *page_map) +{ + iounmap(page_map->remapped); + ClearPageReserved(virt_to_page(page_map->real)); + free_page((unsigned long) page_map->real); +} + +static void amd_free_gatt_pages(void) +{ + int i; + struct amd_page_map **tables; + struct amd_page_map *entry; + + tables = amd_irongate_private.gatt_pages; + for(i = 0; i < amd_irongate_private.num_tables; i++) { + entry = tables[i]; + if (entry != NULL) { + if (entry->real != NULL) { + amd_free_page_map(entry); + } + kfree(entry); + } + } + kfree(tables); +} + +static int amd_create_gatt_pages(int nr_tables) +{ + struct amd_page_map **tables; + struct amd_page_map *entry; + int retval = 0; + int i; + + tables = kmalloc((nr_tables + 1) * sizeof(struct amd_page_map *), + GFP_KERNEL); + if (tables == NULL) { + return -ENOMEM; + } + memset(tables, 0, sizeof(struct amd_page_map *) * (nr_tables + 1)); + for (i = 0; i < nr_tables; i++) { + entry = kmalloc(sizeof(struct amd_page_map), GFP_KERNEL); + if (entry == NULL) { + retval = -ENOMEM; + break; + } + memset(entry, 0, sizeof(struct amd_page_map)); + tables[i] = entry; + retval = amd_create_page_map(entry); + if (retval != 0) break; + } + amd_irongate_private.num_tables = nr_tables; + amd_irongate_private.gatt_pages = tables; + + if (retval != 0) amd_free_gatt_pages(); + + return retval; +} + +/* Since we don't need contigious memory we just try + * to get the gatt table once + */ + +#define GET_PAGE_DIR_OFF(addr) (addr >> 22) +#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \ + GET_PAGE_DIR_OFF(agp_bridge.gart_bus_addr)) +#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) +#define GET_GATT(addr) (amd_irongate_private.gatt_pages[\ + GET_PAGE_DIR_IDX(addr)]->remapped) + +static int amd_create_gatt_table(void) +{ + struct aper_size_info_lvl2 *value; + struct amd_page_map page_dir; + unsigned long addr; + int retval; + u32 temp; + int i; + + value = A_SIZE_LVL2(agp_bridge.current_size); + retval = amd_create_page_map(&page_dir); + if (retval != 0) { + return retval; + } + + retval = amd_create_gatt_pages(value->num_entries / 1024); + if (retval != 0) { + amd_free_page_map(&page_dir); + return retval; + } + + agp_bridge.gatt_table_real = page_dir.real; + agp_bridge.gatt_table = page_dir.remapped; + agp_bridge.gatt_bus_addr = virt_to_phys(page_dir.real); + + /* Get the address for the gart region. + * This is a bus address even on the alpha, b/c its + * used to program the agp master not the cpu + */ + + pci_read_config_dword(agp_bridge.dev, AMD_APBASE, &temp); + addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge.gart_bus_addr = addr; + + /* Calculate the agp offset */ + for(i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) { + page_dir.remapped[GET_PAGE_DIR_OFF(addr)] = + virt_to_phys(amd_irongate_private.gatt_pages[i]->real); + page_dir.remapped[GET_PAGE_DIR_OFF(addr)] |= 0x00000001; + } + + return 0; +} + +static int amd_free_gatt_table(void) +{ + struct amd_page_map page_dir; + + page_dir.real = agp_bridge.gatt_table_real; + page_dir.remapped = agp_bridge.gatt_table; + + amd_free_gatt_pages(); + amd_free_page_map(&page_dir); + return 0; +} + +static int amd_irongate_fetch_size(void) +{ + int i; + u32 temp; + struct aper_size_info_lvl2 *values; + + pci_read_config_dword(agp_bridge.dev, AMD_APSIZE, &temp); + temp = (temp & 0x0000000e); + values = A_SIZE_LVL2(agp_bridge.aperture_sizes); + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if (temp == values[i].size_value) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + + return 0; +} + +static int amd_irongate_configure(void) +{ + struct aper_size_info_lvl2 *current_size; + u32 temp; + u16 enable_reg; + + current_size = A_SIZE_LVL2(agp_bridge.current_size); + + /* Get the memory mapped registers */ + pci_read_config_dword(agp_bridge.dev, AMD_MMBASE, &temp); + temp = (temp & PCI_BASE_ADDRESS_MEM_MASK); + amd_irongate_private.registers = (volatile u8 *) ioremap(temp, 4096); + + /* Write out the address of the gatt table */ + OUTREG32(amd_irongate_private.registers, AMD_ATTBASE, + agp_bridge.gatt_bus_addr); + + /* Write the Sync register */ + pci_write_config_byte(agp_bridge.dev, AMD_MODECNTL, 0x80); + + /* Set indexing mode */ + pci_write_config_byte(agp_bridge.dev, AMD_MODECNTL2, 0x00); + + /* Write the enable register */ + enable_reg = INREG16(amd_irongate_private.registers, AMD_GARTENABLE); + enable_reg = (enable_reg | 0x0004); + OUTREG16(amd_irongate_private.registers, AMD_GARTENABLE, enable_reg); + + /* Write out the size register */ + pci_read_config_dword(agp_bridge.dev, AMD_APSIZE, &temp); + temp = (((temp & ~(0x0000000e)) | current_size->size_value) + | 0x00000001); + pci_write_config_dword(agp_bridge.dev, AMD_APSIZE, temp); + + /* Flush the tlb */ + OUTREG32(amd_irongate_private.registers, AMD_TLBFLUSH, 0x00000001); + + return 0; +} + +static void amd_irongate_cleanup(void) +{ + struct aper_size_info_lvl2 *previous_size; + u32 temp; + u16 enable_reg; + + previous_size = A_SIZE_LVL2(agp_bridge.previous_size); + + enable_reg = INREG16(amd_irongate_private.registers, AMD_GARTENABLE); + enable_reg = (enable_reg & ~(0x0004)); + OUTREG16(amd_irongate_private.registers, AMD_GARTENABLE, enable_reg); + + /* Write back the previous size and disable gart translation */ + pci_read_config_dword(agp_bridge.dev, AMD_APSIZE, &temp); + temp = ((temp & ~(0x0000000f)) | previous_size->size_value); + pci_write_config_dword(agp_bridge.dev, AMD_APSIZE, temp); + iounmap((void *) amd_irongate_private.registers); +} + +/* + * This routine could be implemented by taking the addresses + * written to the GATT, and flushing them individually. However + * currently it just flushes the whole table. Which is probably + * more efficent, since agp_memory blocks can be a large number of + * entries. + */ + +static void amd_irongate_tlbflush(agp_memory * temp) +{ + OUTREG32(amd_irongate_private.registers, AMD_TLBFLUSH, 0x00000001); +} + +static unsigned long amd_irongate_mask_memory(unsigned long addr, int type) +{ + /* Only type 0 is supported by the irongate */ + + return addr | agp_bridge.masks[0].mask; +} + +static int amd_insert_memory(agp_memory * mem, + off_t pg_start, int type) +{ + int i, j, num_entries; + unsigned long *cur_gatt; + unsigned long addr; + + num_entries = A_SIZE_LVL2(agp_bridge.current_size)->num_entries; + + if (type != 0 || mem->type != 0) { + return -EINVAL; + } + if ((pg_start + mem->page_count) > num_entries) { + return -EINVAL; + } + + j = pg_start; + while (j < (pg_start + mem->page_count)) { + addr = (j * PAGE_SIZE) + agp_bridge.gart_bus_addr; + cur_gatt = GET_GATT(addr); + if (!PGE_EMPTY(cur_gatt[GET_GATT_OFF(addr)])) { + return -EBUSY; + } + j++; + } + + if (mem->is_flushed == FALSE) { + CACHE_FLUSH(); + mem->is_flushed = TRUE; + } + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + addr = (j * PAGE_SIZE) + agp_bridge.gart_bus_addr; + cur_gatt = GET_GATT(addr); + cur_gatt[GET_GATT_OFF(addr)] = mem->memory[i]; + } + agp_bridge.tlb_flush(mem); + return 0; +} + +static int amd_remove_memory(agp_memory * mem, off_t pg_start, + int type) +{ + int i; + unsigned long *cur_gatt; + unsigned long addr; + + if (type != 0 || mem->type != 0) { + return -EINVAL; + } + for (i = pg_start; i < (mem->page_count + pg_start); i++) { + addr = (i * PAGE_SIZE) + agp_bridge.gart_bus_addr; + cur_gatt = GET_GATT(addr); + cur_gatt[GET_GATT_OFF(addr)] = + (unsigned long) agp_bridge.scratch_page; + } + + agp_bridge.tlb_flush(mem); + return 0; +} + +static struct aper_size_info_lvl2 amd_irongate_sizes[7] = +{ + {2048, 524288, 0x0000000c}, + {1024, 262144, 0x0000000a}, + {512, 131072, 0x00000008}, + {256, 65536, 0x00000006}, + {128, 32768, 0x00000004}, + {64, 16384, 0x00000002}, + {32, 8192, 0x00000000} +}; + +static struct gatt_mask amd_irongate_masks[] = +{ + {mask: 0x00000001, type: 0} +}; + +int __init amd_irongate_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = amd_irongate_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) amd_irongate_sizes; + agp_bridge.size_type = LVL2_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = (void *) &amd_irongate_private; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = amd_irongate_configure; + agp_bridge.fetch_size = amd_irongate_fetch_size; + agp_bridge.cleanup = amd_irongate_cleanup; + agp_bridge.tlb_flush = amd_irongate_tlbflush; + agp_bridge.mask_memory = amd_irongate_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = amd_create_gatt_table; + agp_bridge.free_gatt_table = amd_free_gatt_table; + agp_bridge.insert_memory = amd_insert_memory; + agp_bridge.remove_memory = amd_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; + + (void) pdev; /* unused */ +} + diff --git a/drivers/char/agp/agpgart_fe.c b/drivers/char/agp/frontend.c index e603a9e5f1e8..a2e0a5ee1b42 100644 --- a/drivers/char/agp/agpgart_fe.c +++ b/drivers/char/agp/frontend.c @@ -24,29 +24,16 @@ * */ -#define __NO_VERSION__ -#include <linux/version.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> +#include <linux/mman.h> #include <linux/pci.h> #include <linux/init.h> -#include <linux/pagemap.h> #include <linux/miscdevice.h> #include <linux/agp_backend.h> #include <linux/agpgart.h> -#include <linux/smp_lock.h> -#include <asm/system.h> #include <asm/uaccess.h> -#include <asm/io.h> -#include <asm/page.h> -#include <asm/mman.h> #include "agp.h" diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c new file mode 100644 index 000000000000..6798e967d386 --- /dev/null +++ b/drivers/char/agp/hp-agp.c @@ -0,0 +1,394 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * TODO: + * - Allocate more than order 0 pages to avoid too much linear map splitting. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/agp_backend.h> +#include "agp.h" + + +#ifndef log2 +#define log2(x) ffz(~(x)) +#endif + +#define HP_ZX1_IOVA_BASE GB(1UL) +#define HP_ZX1_IOVA_SIZE GB(1UL) +#define HP_ZX1_GART_SIZE (HP_ZX1_IOVA_SIZE / 2) +#define HP_ZX1_SBA_IOMMU_COOKIE 0x0000badbadc0ffeeUL + +#define HP_ZX1_PDIR_VALID_BIT 0x8000000000000000UL +#define HP_ZX1_IOVA_TO_PDIR(va) ((va - hp_private.iova_base) >> \ + hp_private.io_tlb_shift) + +static struct aper_size_info_fixed hp_zx1_sizes[] = +{ + {0, 0, 0}, /* filled in by hp_zx1_fetch_size() */ +}; + +static struct gatt_mask hp_zx1_masks[] = +{ + {mask: HP_ZX1_PDIR_VALID_BIT, type: 0} +}; + +static struct _hp_private { + struct pci_dev *ioc; + volatile u8 *registers; + u64 *io_pdir; // PDIR for entire IOVA + u64 *gatt; // PDIR just for GART (subset of above) + u64 gatt_entries; + u64 iova_base; + u64 gart_base; + u64 gart_size; + u64 io_pdir_size; + int io_pdir_owner; // do we own it, or share it with sba_iommu? + int io_page_size; + int io_tlb_shift; + int io_tlb_ps; // IOC ps config + int io_pages_per_kpage; +} hp_private; + +static int __init hp_zx1_ioc_shared(void) +{ + struct _hp_private *hp = &hp_private; + + printk(KERN_INFO PFX "HP ZX1 IOC: IOPDIR shared with sba_iommu\n"); + + /* + * IOC already configured by sba_iommu module; just use + * its setup. We assume: + * - IOVA space is 1Gb in size + * - first 512Mb is IOMMU, second 512Mb is GART + */ + hp->io_tlb_ps = INREG64(hp->registers, HP_ZX1_TCNFG); + switch (hp->io_tlb_ps) { + case 0: hp->io_tlb_shift = 12; break; + case 1: hp->io_tlb_shift = 13; break; + case 2: hp->io_tlb_shift = 14; break; + case 3: hp->io_tlb_shift = 16; break; + default: + printk(KERN_ERR PFX "Invalid IOTLB page size " + "configuration 0x%x\n", hp->io_tlb_ps); + hp->gatt = 0; + hp->gatt_entries = 0; + return -ENODEV; + } + hp->io_page_size = 1 << hp->io_tlb_shift; + hp->io_pages_per_kpage = PAGE_SIZE / hp->io_page_size; + + hp->iova_base = INREG64(hp->registers, HP_ZX1_IBASE) & ~0x1; + hp->gart_base = hp->iova_base + HP_ZX1_IOVA_SIZE - HP_ZX1_GART_SIZE; + + hp->gart_size = HP_ZX1_GART_SIZE; + hp->gatt_entries = hp->gart_size / hp->io_page_size; + + hp->io_pdir = phys_to_virt(INREG64(hp->registers, HP_ZX1_PDIR_BASE)); + hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)]; + + if (hp->gatt[0] != HP_ZX1_SBA_IOMMU_COOKIE) { + hp->gatt = 0; + hp->gatt_entries = 0; + printk(KERN_ERR PFX "No reserved IO PDIR entry found; " + "GART disabled\n"); + return -ENODEV; + } + + return 0; +} + +static int __init hp_zx1_ioc_owner(u8 ioc_rev) +{ + struct _hp_private *hp = &hp_private; + + printk(KERN_INFO PFX "HP ZX1 IOC: IOPDIR dedicated to GART\n"); + + /* + * Select an IOV page size no larger than system page size. + */ + if (PAGE_SIZE >= KB(64)) { + hp->io_tlb_shift = 16; + hp->io_tlb_ps = 3; + } else if (PAGE_SIZE >= KB(16)) { + hp->io_tlb_shift = 14; + hp->io_tlb_ps = 2; + } else if (PAGE_SIZE >= KB(8)) { + hp->io_tlb_shift = 13; + hp->io_tlb_ps = 1; + } else { + hp->io_tlb_shift = 12; + hp->io_tlb_ps = 0; + } + hp->io_page_size = 1 << hp->io_tlb_shift; + hp->io_pages_per_kpage = PAGE_SIZE / hp->io_page_size; + + hp->iova_base = HP_ZX1_IOVA_BASE; + hp->gart_size = HP_ZX1_GART_SIZE; + hp->gart_base = hp->iova_base + HP_ZX1_IOVA_SIZE - hp->gart_size; + + hp->gatt_entries = hp->gart_size / hp->io_page_size; + hp->io_pdir_size = (HP_ZX1_IOVA_SIZE / hp->io_page_size) * sizeof(u64); + + return 0; +} + +static int __init hp_zx1_ioc_init(void) +{ + struct _hp_private *hp = &hp_private; + struct pci_dev *ioc; + int i; + u8 ioc_rev; + + ioc = pci_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_ZX1_IOC, NULL); + if (!ioc) { + printk(KERN_ERR PFX "Detected HP ZX1 AGP bridge but no IOC\n"); + return -ENODEV; + } + hp->ioc = ioc; + + pci_read_config_byte(ioc, PCI_REVISION_ID, &ioc_rev); + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + if (pci_resource_flags(ioc, i) == IORESOURCE_MEM) { + hp->registers = (u8 *) ioremap(pci_resource_start(ioc, + i), + pci_resource_len(ioc, i)); + break; + } + } + if (!hp->registers) { + printk(KERN_ERR PFX "Detected HP ZX1 AGP bridge but no CSRs\n"); + + return -ENODEV; + } + + /* + * If the IOTLB is currently disabled, we can take it over. + * Otherwise, we have to share with sba_iommu. + */ + hp->io_pdir_owner = (INREG64(hp->registers, HP_ZX1_IBASE) & 0x1) == 0; + + if (hp->io_pdir_owner) + return hp_zx1_ioc_owner(ioc_rev); + + return hp_zx1_ioc_shared(); +} + +static int hp_zx1_fetch_size(void) +{ + int size; + + size = hp_private.gart_size / MB(1); + hp_zx1_sizes[0].size = size; + agp_bridge.current_size = (void *) &hp_zx1_sizes[0]; + return size; +} + +static int hp_zx1_configure(void) +{ + struct _hp_private *hp = &hp_private; + + agp_bridge.gart_bus_addr = hp->gart_base; + agp_bridge.capndx = pci_find_capability(agp_bridge.dev, PCI_CAP_ID_AGP); + pci_read_config_dword(agp_bridge.dev, + agp_bridge.capndx + PCI_AGP_STATUS, &agp_bridge.mode); + + if (hp->io_pdir_owner) { + OUTREG64(hp->registers, HP_ZX1_PDIR_BASE, + virt_to_phys(hp->io_pdir)); + OUTREG64(hp->registers, HP_ZX1_TCNFG, hp->io_tlb_ps); + OUTREG64(hp->registers, HP_ZX1_IMASK, ~(HP_ZX1_IOVA_SIZE - 1)); + OUTREG64(hp->registers, HP_ZX1_IBASE, hp->iova_base | 0x1); + OUTREG64(hp->registers, HP_ZX1_PCOM, + hp->iova_base | log2(HP_ZX1_IOVA_SIZE)); + INREG64(hp->registers, HP_ZX1_PCOM); + } + + return 0; +} + +static void hp_zx1_cleanup(void) +{ + struct _hp_private *hp = &hp_private; + + if (hp->io_pdir_owner) + OUTREG64(hp->registers, HP_ZX1_IBASE, 0); + iounmap((void *) hp->registers); +} + +static void hp_zx1_tlbflush(agp_memory * mem) +{ + struct _hp_private *hp = &hp_private; + + OUTREG64(hp->registers, HP_ZX1_PCOM, + hp->gart_base | log2(hp->gart_size)); + INREG64(hp->registers, HP_ZX1_PCOM); +} + +static int hp_zx1_create_gatt_table(void) +{ + struct _hp_private *hp = &hp_private; + int i; + + if (hp->io_pdir_owner) { + hp->io_pdir = (u64 *) __get_free_pages(GFP_KERNEL, + get_order(hp->io_pdir_size)); + if (!hp->io_pdir) { + printk(KERN_ERR PFX "Couldn't allocate contiguous " + "memory for I/O PDIR\n"); + hp->gatt = 0; + hp->gatt_entries = 0; + return -ENOMEM; + } + memset(hp->io_pdir, 0, hp->io_pdir_size); + + hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)]; + } + + for (i = 0; i < hp->gatt_entries; i++) { + hp->gatt[i] = (unsigned long) agp_bridge.scratch_page; + } + + return 0; +} + +static int hp_zx1_free_gatt_table(void) +{ + struct _hp_private *hp = &hp_private; + + if (hp->io_pdir_owner) + free_pages((unsigned long) hp->io_pdir, + get_order(hp->io_pdir_size)); + else + hp->gatt[0] = HP_ZX1_SBA_IOMMU_COOKIE; + return 0; +} + +static int hp_zx1_insert_memory(agp_memory * mem, off_t pg_start, int type) +{ + struct _hp_private *hp = &hp_private; + int i, k; + off_t j, io_pg_start; + int io_pg_count; + + if (type != 0 || mem->type != 0) { + return -EINVAL; + } + + io_pg_start = hp->io_pages_per_kpage * pg_start; + io_pg_count = hp->io_pages_per_kpage * mem->page_count; + if ((io_pg_start + io_pg_count) > hp->gatt_entries) { + return -EINVAL; + } + + j = io_pg_start; + while (j < (io_pg_start + io_pg_count)) { + if (hp->gatt[j]) { + return -EBUSY; + } + j++; + } + + if (mem->is_flushed == FALSE) { + CACHE_FLUSH(); + mem->is_flushed = TRUE; + } + + for (i = 0, j = io_pg_start; i < mem->page_count; i++) { + unsigned long paddr; + + paddr = mem->memory[i]; + for (k = 0; + k < hp->io_pages_per_kpage; + k++, j++, paddr += hp->io_page_size) { + hp->gatt[j] = agp_bridge.mask_memory(paddr, type); + } + } + + agp_bridge.tlb_flush(mem); + return 0; +} + +static int hp_zx1_remove_memory(agp_memory * mem, off_t pg_start, int type) +{ + struct _hp_private *hp = &hp_private; + int i, io_pg_start, io_pg_count; + + if (type != 0 || mem->type != 0) { + return -EINVAL; + } + + io_pg_start = hp->io_pages_per_kpage * pg_start; + io_pg_count = hp->io_pages_per_kpage * mem->page_count; + for (i = io_pg_start; i < io_pg_count + io_pg_start; i++) { + hp->gatt[i] = agp_bridge.scratch_page; + } + + agp_bridge.tlb_flush(mem); + return 0; +} + +static unsigned long hp_zx1_mask_memory(unsigned long addr, int type) +{ + return HP_ZX1_PDIR_VALID_BIT | addr; +} + +static unsigned long hp_zx1_unmask_memory(unsigned long addr) +{ + return addr & ~(HP_ZX1_PDIR_VALID_BIT); +} + +int __init hp_zx1_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = hp_zx1_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.dev_private_data = NULL; + agp_bridge.size_type = FIXED_APER_SIZE; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = hp_zx1_configure; + agp_bridge.fetch_size = hp_zx1_fetch_size; + agp_bridge.cleanup = hp_zx1_cleanup; + agp_bridge.tlb_flush = hp_zx1_tlbflush; + agp_bridge.mask_memory = hp_zx1_mask_memory; + agp_bridge.unmask_memory = hp_zx1_unmask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = hp_zx1_create_gatt_table; + agp_bridge.free_gatt_table = hp_zx1_free_gatt_table; + agp_bridge.insert_memory = hp_zx1_insert_memory; + agp_bridge.remove_memory = hp_zx1_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.cant_use_aperture = 1; + + return hp_zx1_ioc_init(); + + (void) pdev; /* unused */ +} + diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c new file mode 100644 index 000000000000..e09f3974ae40 --- /dev/null +++ b/drivers/char/agp/i460-agp.c @@ -0,0 +1,595 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * TODO: + * - Allocate more than order 0 pages to avoid too much linear map splitting. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/agp_backend.h> +#include "agp.h" + +/* BIOS configures the chipset so that one of two apbase registers are used */ +static u8 intel_i460_dynamic_apbase = 0x10; + +/* 460 supports multiple GART page sizes, so GART pageshift is dynamic */ +static u8 intel_i460_pageshift = 12; +static u32 intel_i460_pagesize; + +/* Keep track of which is larger, chipset or kernel page size. */ +static u32 intel_i460_cpk = 1; + +/* Structure for tracking partial use of 4MB GART pages */ +static u32 **i460_pg_detail = NULL; +static u32 *i460_pg_count = NULL; + +#define I460_CPAGES_PER_KPAGE (PAGE_SIZE >> intel_i460_pageshift) +#define I460_KPAGES_PER_CPAGE ((1 << intel_i460_pageshift) >> PAGE_SHIFT) + +#define I460_SRAM_IO_DISABLE (1 << 4) +#define I460_BAPBASE_ENABLE (1 << 3) +#define I460_AGPSIZ_MASK 0x7 +#define I460_4M_PS (1 << 1) + +#define log2(x) ffz(~(x)) + +static inline void intel_i460_read_back (volatile u32 *entry) +{ + /* + * The 460 spec says we have to read the last location written to + * make sure that all writes have taken effect + */ + *entry; +} + +static int intel_i460_fetch_size(void) +{ + int i; + u8 temp; + struct aper_size_info_8 *values; + + /* Determine the GART page size */ + pci_read_config_byte(agp_bridge.dev, INTEL_I460_GXBCTL, &temp); + intel_i460_pageshift = (temp & I460_4M_PS) ? 22 : 12; + intel_i460_pagesize = 1UL << intel_i460_pageshift; + + values = A_SIZE_8(agp_bridge.aperture_sizes); + + pci_read_config_byte(agp_bridge.dev, INTEL_I460_AGPSIZ, &temp); + + /* Exit now if the IO drivers for the GART SRAMS are turned off */ + if (temp & I460_SRAM_IO_DISABLE) { + printk(KERN_ERR PFX "GART SRAMS disabled on 460GX chipset\n"); + printk(KERN_ERR PFX "AGPGART operation not possible\n"); + return 0; + } + + /* Make sure we don't try to create an 2 ^ 23 entry GATT */ + if ((intel_i460_pageshift == 0) && ((temp & I460_AGPSIZ_MASK) == 4)) { + printk(KERN_ERR PFX "We can't have a 32GB aperture with 4KB GART pages\n"); + return 0; + } + + /* Determine the proper APBASE register */ + if (temp & I460_BAPBASE_ENABLE) + intel_i460_dynamic_apbase = INTEL_I460_BAPBASE; + else + intel_i460_dynamic_apbase = INTEL_I460_APBASE; + + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + /* + * Dynamically calculate the proper num_entries and page_order values for + * the define aperture sizes. Take care not to shift off the end of + * values[i].size. + */ + values[i].num_entries = (values[i].size << 8) >> (intel_i460_pageshift - 12); + values[i].page_order = log2((sizeof(u32)*values[i].num_entries) >> PAGE_SHIFT); + } + + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + /* Neglect control bits when matching up size_value */ + if ((temp & I460_AGPSIZ_MASK) == values[i].size_value) { + agp_bridge.previous_size = agp_bridge.current_size = (void *) (values + i); + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + + return 0; +} + +/* There isn't anything to do here since 460 has no GART TLB. */ +static void intel_i460_tlb_flush(agp_memory * mem) +{ + return; +} + +/* + * This utility function is needed to prevent corruption of the control bits + * which are stored along with the aperture size in 460's AGPSIZ register + */ +static void intel_i460_write_agpsiz(u8 size_value) +{ + u8 temp; + + pci_read_config_byte(agp_bridge.dev, INTEL_I460_AGPSIZ, &temp); + pci_write_config_byte(agp_bridge.dev, INTEL_I460_AGPSIZ, + ((temp & ~I460_AGPSIZ_MASK) | size_value)); +} + +static void intel_i460_cleanup(void) +{ + struct aper_size_info_8 *previous_size; + + previous_size = A_SIZE_8(agp_bridge.previous_size); + intel_i460_write_agpsiz(previous_size->size_value); + + if (intel_i460_cpk == 0) { + vfree(i460_pg_detail); + vfree(i460_pg_count); + } +} + + +/* Control bits for Out-Of-GART coherency and Burst Write Combining */ +#define I460_GXBCTL_OOG (1UL << 0) +#define I460_GXBCTL_BWC (1UL << 2) + +static int intel_i460_configure(void) +{ + union { + u32 small[2]; + u64 large; + } temp; + u8 scratch; + int i; + + struct aper_size_info_8 *current_size; + + temp.large = 0; + + current_size = A_SIZE_8(agp_bridge.current_size); + intel_i460_write_agpsiz(current_size->size_value); + + /* + * Do the necessary rigmarole to read all eight bytes of APBASE. + * This has to be done since the AGP aperture can be above 4GB on + * 460 based systems. + */ + pci_read_config_dword(agp_bridge.dev, intel_i460_dynamic_apbase, &(temp.small[0])); + pci_read_config_dword(agp_bridge.dev, intel_i460_dynamic_apbase + 4, &(temp.small[1])); + + /* Clear BAR control bits */ + agp_bridge.gart_bus_addr = temp.large & ~((1UL << 3) - 1); + + pci_read_config_byte(agp_bridge.dev, INTEL_I460_GXBCTL, &scratch); + pci_write_config_byte(agp_bridge.dev, INTEL_I460_GXBCTL, + (scratch & 0x02) | I460_GXBCTL_OOG | I460_GXBCTL_BWC); + + /* + * Initialize partial allocation trackers if a GART page is bigger than + * a kernel page. + */ + if (I460_CPAGES_PER_KPAGE >= 1) { + intel_i460_cpk = 1; + } else { + intel_i460_cpk = 0; + + i460_pg_detail = vmalloc(sizeof(*i460_pg_detail) * current_size->num_entries); + i460_pg_count = vmalloc(sizeof(*i460_pg_count) * current_size->num_entries); + + for (i = 0; i < current_size->num_entries; i++) { + i460_pg_count[i] = 0; + i460_pg_detail[i] = NULL; + } + } + return 0; +} + +static int intel_i460_create_gatt_table(void) +{ + char *table; + int i; + int page_order; + int num_entries; + void *temp; + + /* + * Load up the fixed address of the GART SRAMS which hold our + * GATT table. + */ + table = (char *) __va(INTEL_I460_ATTBASE); + + temp = agp_bridge.current_size; + page_order = A_SIZE_8(temp)->page_order; + num_entries = A_SIZE_8(temp)->num_entries; + + agp_bridge.gatt_table_real = (u32 *) table; + agp_bridge.gatt_table = ioremap_nocache(virt_to_phys(table), + (PAGE_SIZE * (1 << page_order))); + agp_bridge.gatt_bus_addr = virt_to_phys(agp_bridge.gatt_table_real); + + for (i = 0; i < num_entries; i++) { + agp_bridge.gatt_table[i] = 0; + } + + intel_i460_read_back(agp_bridge.gatt_table + i - 1); + return 0; +} + +static int intel_i460_free_gatt_table(void) +{ + int num_entries; + int i; + void *temp; + + temp = agp_bridge.current_size; + + num_entries = A_SIZE_8(temp)->num_entries; + + for (i = 0; i < num_entries; i++) { + agp_bridge.gatt_table[i] = 0; + } + + intel_i460_read_back(agp_bridge.gatt_table + i - 1); + + iounmap(agp_bridge.gatt_table); + return 0; +} + +/* These functions are called when PAGE_SIZE exceeds the GART page size */ + +static int intel_i460_insert_memory_cpk(agp_memory * mem, off_t pg_start, int type) +{ + int i, j, k, num_entries; + void *temp; + unsigned long paddr; + + /* + * The rest of the kernel will compute page offsets in terms of + * PAGE_SIZE. + */ + pg_start = I460_CPAGES_PER_KPAGE * pg_start; + + temp = agp_bridge.current_size; + num_entries = A_SIZE_8(temp)->num_entries; + + if ((pg_start + I460_CPAGES_PER_KPAGE * mem->page_count) > num_entries) { + printk(KERN_ERR PFX "Looks like we're out of AGP memory\n"); + return -EINVAL; + } + + j = pg_start; + while (j < (pg_start + I460_CPAGES_PER_KPAGE * mem->page_count)) { + if (!PGE_EMPTY(agp_bridge.gatt_table[j])) { + return -EBUSY; + } + j++; + } + +#if 0 + /* not necessary since 460 GART is operated in coherent mode... */ + if (mem->is_flushed == FALSE) { + CACHE_FLUSH(); + mem->is_flushed = TRUE; + } +#endif + + for (i = 0, j = pg_start; i < mem->page_count; i++) { + paddr = mem->memory[i]; + for (k = 0; k < I460_CPAGES_PER_KPAGE; k++, j++, paddr += intel_i460_pagesize) + agp_bridge.gatt_table[j] = (u32) agp_bridge.mask_memory(paddr, mem->type); + } + + intel_i460_read_back(agp_bridge.gatt_table + j - 1); + return 0; +} + +static int intel_i460_remove_memory_cpk(agp_memory * mem, off_t pg_start, int type) +{ + int i; + + pg_start = I460_CPAGES_PER_KPAGE * pg_start; + + for (i = pg_start; i < (pg_start + I460_CPAGES_PER_KPAGE * mem->page_count); i++) + agp_bridge.gatt_table[i] = 0; + + intel_i460_read_back(agp_bridge.gatt_table + i - 1); + return 0; +} + +/* + * These functions are called when the GART page size exceeds PAGE_SIZE. + * + * This situation is interesting since AGP memory allocations that are + * smaller than a single GART page are possible. The structures i460_pg_count + * and i460_pg_detail track partial allocation of the large GART pages to + * work around this issue. + * + * i460_pg_count[pg_num] tracks the number of kernel pages in use within + * GART page pg_num. i460_pg_detail[pg_num] is an array containing a + * psuedo-GART entry for each of the aforementioned kernel pages. The whole + * of i460_pg_detail is equivalent to a giant GATT with page size equal to + * that of the kernel. + */ + +static void *intel_i460_alloc_large_page(int pg_num) +{ + int i; + void *bp, *bp_end; + struct page *page; + + i460_pg_detail[pg_num] = (void *) vmalloc(sizeof(u32) * I460_KPAGES_PER_CPAGE); + if (i460_pg_detail[pg_num] == NULL) { + printk(KERN_ERR PFX "Out of memory, we're in trouble...\n"); + return NULL; + } + + for (i = 0; i < I460_KPAGES_PER_CPAGE; i++) + i460_pg_detail[pg_num][i] = 0; + + bp = (void *) __get_free_pages(GFP_KERNEL, intel_i460_pageshift - PAGE_SHIFT); + if (bp == NULL) { + printk(KERN_ERR PFX "Couldn't alloc 4M GART page...\n"); + return NULL; + } + + bp_end = bp + ((PAGE_SIZE * (1 << (intel_i460_pageshift - PAGE_SHIFT))) - 1); + + for (page = virt_to_page(bp); page <= virt_to_page(bp_end); page++) { + atomic_inc(&agp_bridge.current_memory_agp); + } + return bp; +} + +static void intel_i460_free_large_page(int pg_num, unsigned long addr) +{ + struct page *page; + void *bp, *bp_end; + + bp = (void *) __va(addr); + bp_end = bp + (PAGE_SIZE * (1 << (intel_i460_pageshift - PAGE_SHIFT))); + + vfree(i460_pg_detail[pg_num]); + i460_pg_detail[pg_num] = NULL; + + for (page = virt_to_page(bp); page < virt_to_page(bp_end); page++) { + atomic_dec(&agp_bridge.current_memory_agp); + } + + free_pages((unsigned long) bp, intel_i460_pageshift - PAGE_SHIFT); +} + +static int intel_i460_insert_memory_kpc(agp_memory * mem, off_t pg_start, int type) +{ + int i, pg, start_pg, end_pg, start_offset, end_offset, idx; + int num_entries; + void *temp; + unsigned long paddr; + + temp = agp_bridge.current_size; + num_entries = A_SIZE_8(temp)->num_entries; + + /* Figure out what pg_start means in terms of our large GART pages */ + start_pg = pg_start / I460_KPAGES_PER_CPAGE; + start_offset = pg_start % I460_KPAGES_PER_CPAGE; + end_pg = (pg_start + mem->page_count - 1) / I460_KPAGES_PER_CPAGE; + end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_CPAGE; + + if (end_pg > num_entries) { + printk(KERN_ERR PFX "Looks like we're out of AGP memory\n"); + return -EINVAL; + } + + /* Check if the requested region of the aperture is free */ + for (pg = start_pg; pg <= end_pg; pg++) { + /* Allocate new GART pages if necessary */ + if (i460_pg_detail[pg] == NULL) { + temp = intel_i460_alloc_large_page(pg); + if (temp == NULL) + return -ENOMEM; + agp_bridge.gatt_table[pg] = agp_bridge.mask_memory((unsigned long) temp, + 0); + intel_i460_read_back(agp_bridge.gatt_table + pg); + } + + for (idx = ((pg == start_pg) ? start_offset : 0); + idx < ((pg == end_pg) ? (end_offset + 1) : I460_KPAGES_PER_CPAGE); + idx++) + { + if (i460_pg_detail[pg][idx] != 0) + return -EBUSY; + } + } + +#if 0 + /* not necessary since 460 GART is operated in coherent mode... */ + if (mem->is_flushed == FALSE) { + CACHE_FLUSH(); + mem->is_flushed = TRUE; + } +#endif + + for (pg = start_pg, i = 0; pg <= end_pg; pg++) { + paddr = agp_bridge.unmask_memory(agp_bridge.gatt_table[pg]); + for (idx = ((pg == start_pg) ? start_offset : 0); + idx < ((pg == end_pg) ? (end_offset + 1) : I460_KPAGES_PER_CPAGE); + idx++, i++) + { + mem->memory[i] = paddr + (idx * PAGE_SIZE); + i460_pg_detail[pg][idx] = agp_bridge.mask_memory(mem->memory[i], + mem->type); + i460_pg_count[pg]++; + } + } + + return 0; +} + +static int intel_i460_remove_memory_kpc(agp_memory * mem, off_t pg_start, int type) +{ + int i, pg, start_pg, end_pg, start_offset, end_offset, idx; + int num_entries; + void *temp; + unsigned long paddr; + + temp = agp_bridge.current_size; + num_entries = A_SIZE_8(temp)->num_entries; + + /* Figure out what pg_start means in terms of our large GART pages */ + start_pg = pg_start / I460_KPAGES_PER_CPAGE; + start_offset = pg_start % I460_KPAGES_PER_CPAGE; + end_pg = (pg_start + mem->page_count - 1) / I460_KPAGES_PER_CPAGE; + end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_CPAGE; + + for (i = 0, pg = start_pg; pg <= end_pg; pg++) { + for (idx = ((pg == start_pg) ? start_offset : 0); + idx < ((pg == end_pg) ? (end_offset + 1) : I460_KPAGES_PER_CPAGE); + idx++, i++) + { + mem->memory[i] = 0; + i460_pg_detail[pg][idx] = 0; + i460_pg_count[pg]--; + } + + /* Free GART pages if they are unused */ + if (i460_pg_count[pg] == 0) { + paddr = agp_bridge.unmask_memory(agp_bridge.gatt_table[pg]); + agp_bridge.gatt_table[pg] = agp_bridge.scratch_page; + intel_i460_read_back(agp_bridge.gatt_table + pg); + intel_i460_free_large_page(pg, paddr); + } + } + return 0; +} + +/* Dummy routines to call the approriate {cpk,kpc} function */ + +static int intel_i460_insert_memory(agp_memory * mem, off_t pg_start, int type) +{ + if (intel_i460_cpk) + return intel_i460_insert_memory_cpk(mem, pg_start, type); + else + return intel_i460_insert_memory_kpc(mem, pg_start, type); +} + +static int intel_i460_remove_memory(agp_memory * mem, off_t pg_start, int type) +{ + if (intel_i460_cpk) + return intel_i460_remove_memory_cpk(mem, pg_start, type); + else + return intel_i460_remove_memory_kpc(mem, pg_start, type); +} + +/* + * If the kernel page size is smaller that the chipset page size, we don't + * want to allocate memory until we know where it is to be bound in the + * aperture (a multi-kernel-page alloc might fit inside of an already + * allocated GART page). Consequently, don't allocate or free anything + * if i460_cpk (meaning chipset pages per kernel page) isn't set. + * + * Let's just hope nobody counts on the allocated AGP memory being there + * before bind time (I don't think current drivers do)... + */ +static void * intel_i460_alloc_page(void) +{ + if (intel_i460_cpk) + return agp_generic_alloc_page(); + + /* Returning NULL would cause problems */ + /* AK: really dubious code. */ + return (void *)~0UL; +} + +static void intel_i460_destroy_page(void *page) +{ + if (intel_i460_cpk) + agp_generic_destroy_page(page); +} + +static struct gatt_mask intel_i460_masks[] = +{ + { + mask: INTEL_I460_GATT_VALID | INTEL_I460_GATT_COHERENT, + type: 0 + } +}; + +static unsigned long intel_i460_mask_memory(unsigned long addr, int type) +{ + /* Make sure the returned address is a valid GATT entry */ + return (agp_bridge.masks[0].mask + | (((addr & ~((1 << intel_i460_pageshift) - 1)) & 0xffffff000) >> 12)); +} + +static unsigned long intel_i460_unmask_memory(unsigned long addr) +{ + /* Turn a GATT entry into a physical address */ + return ((addr & 0xffffff) << 12); +} + +static struct aper_size_info_8 intel_i460_sizes[3] = +{ + /* + * The 32GB aperture is only available with a 4M GART page size. + * Due to the dynamic GART page size, we can't figure out page_order + * or num_entries until runtime. + */ + {32768, 0, 0, 4}, + {1024, 0, 0, 2}, + {256, 0, 0, 1} +}; + +int __init intel_i460_setup (struct pci_dev *pdev __attribute__((unused))) +{ + agp_bridge.masks = intel_i460_masks; + agp_bridge.aperture_sizes = (void *) intel_i460_sizes; + agp_bridge.size_type = U8_APER_SIZE; + agp_bridge.num_aperture_sizes = 3; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = intel_i460_configure; + agp_bridge.fetch_size = intel_i460_fetch_size; + agp_bridge.cleanup = intel_i460_cleanup; + agp_bridge.tlb_flush = intel_i460_tlb_flush; + agp_bridge.mask_memory = intel_i460_mask_memory; + agp_bridge.unmask_memory = intel_i460_unmask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = intel_i460_create_gatt_table; + agp_bridge.free_gatt_table = intel_i460_free_gatt_table; + agp_bridge.insert_memory = intel_i460_insert_memory; + agp_bridge.remove_memory = intel_i460_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = intel_i460_alloc_page; + agp_bridge.agp_destroy_page = intel_i460_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 1; + return 0; +} + diff --git a/drivers/char/agp/i810-agp.c b/drivers/char/agp/i810-agp.c new file mode 100644 index 000000000000..77d721dfad9c --- /dev/null +++ b/drivers/char/agp/i810-agp.c @@ -0,0 +1,594 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * TODO: + * - Allocate more than order 0 pages to avoid too much linear map splitting. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/agp_backend.h> +#include "agp.h" + +static struct aper_size_info_fixed intel_i810_sizes[] = +{ + {64, 16384, 4}, + /* The 32M mode still requires a 64k gatt */ + {32, 8192, 4} +}; + +#define AGP_DCACHE_MEMORY 1 +#define AGP_PHYS_MEMORY 2 + +static struct gatt_mask intel_i810_masks[] = +{ + {mask: I810_PTE_VALID, type: 0}, + {mask: (I810_PTE_VALID | I810_PTE_LOCAL), type: AGP_DCACHE_MEMORY}, + {mask: I810_PTE_VALID, type: 0} +}; + +static struct _intel_i810_private { + struct pci_dev *i810_dev; /* device one */ + volatile u8 *registers; + int num_dcache_entries; +} intel_i810_private; + +static int intel_i810_fetch_size(void) +{ + u32 smram_miscc; + struct aper_size_info_fixed *values; + + pci_read_config_dword(agp_bridge.dev, I810_SMRAM_MISCC, &smram_miscc); + values = A_SIZE_FIX(agp_bridge.aperture_sizes); + + if ((smram_miscc & I810_GMS) == I810_GMS_DISABLE) { + printk(KERN_WARNING PFX "i810 is disabled\n"); + return 0; + } + if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) == I810_GFX_MEM_WIN_32M) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + 1); + agp_bridge.aperture_size_idx = 1; + return values[1].size; + } else { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values); + agp_bridge.aperture_size_idx = 0; + return values[0].size; + } + + return 0; +} + +static int intel_i810_configure(void) +{ + struct aper_size_info_fixed *current_size; + u32 temp; + int i; + + current_size = A_SIZE_FIX(agp_bridge.current_size); + + pci_read_config_dword(intel_i810_private.i810_dev, I810_MMADDR, &temp); + temp &= 0xfff80000; + + intel_i810_private.registers = + (volatile u8 *) ioremap(temp, 128 * 4096); + + if ((INREG32(intel_i810_private.registers, I810_DRAM_CTL) + & I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) { + /* This will need to be dynamically assigned */ + printk(KERN_INFO PFX "detected 4MB dedicated video ram.\n"); + intel_i810_private.num_dcache_entries = 1024; + } + pci_read_config_dword(intel_i810_private.i810_dev, I810_GMADDR, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + OUTREG32(intel_i810_private.registers, I810_PGETBL_CTL, + agp_bridge.gatt_bus_addr | I810_PGETBL_ENABLED); + CACHE_FLUSH(); + + if (agp_bridge.needs_scratch_page == TRUE) { + for (i = 0; i < current_size->num_entries; i++) { + OUTREG32(intel_i810_private.registers, + I810_PTE_BASE + (i * 4), + agp_bridge.scratch_page); + } + } + return 0; +} + +static void intel_i810_cleanup(void) +{ + OUTREG32(intel_i810_private.registers, I810_PGETBL_CTL, 0); + iounmap((void *) intel_i810_private.registers); +} + +static void intel_i810_tlbflush(agp_memory * mem) +{ + return; +} + +static void intel_i810_agp_enable(u32 mode) +{ + return; +} + +static int intel_i810_insert_entries(agp_memory * mem, off_t pg_start, + int type) +{ + int i, j, num_entries; + void *temp; + + temp = agp_bridge.current_size; + num_entries = A_SIZE_FIX(temp)->num_entries; + + if ((pg_start + mem->page_count) > num_entries) { + return -EINVAL; + } + for (j = pg_start; j < (pg_start + mem->page_count); j++) { + if (!PGE_EMPTY(agp_bridge.gatt_table[j])) { + return -EBUSY; + } + } + + if (type != 0 || mem->type != 0) { + if ((type == AGP_DCACHE_MEMORY) && + (mem->type == AGP_DCACHE_MEMORY)) { + /* special insert */ + CACHE_FLUSH(); + for (i = pg_start; + i < (pg_start + mem->page_count); i++) { + OUTREG32(intel_i810_private.registers, + I810_PTE_BASE + (i * 4), + (i * 4096) | I810_PTE_LOCAL | + I810_PTE_VALID); + } + CACHE_FLUSH(); + agp_bridge.tlb_flush(mem); + return 0; + } + if((type == AGP_PHYS_MEMORY) && + (mem->type == AGP_PHYS_MEMORY)) { + goto insert; + } + return -EINVAL; + } + +insert: + CACHE_FLUSH(); + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + OUTREG32(intel_i810_private.registers, + I810_PTE_BASE + (j * 4), mem->memory[i]); + } + CACHE_FLUSH(); + + agp_bridge.tlb_flush(mem); + return 0; +} + +static int intel_i810_remove_entries(agp_memory * mem, off_t pg_start, + int type) +{ + int i; + + for (i = pg_start; i < (mem->page_count + pg_start); i++) { + OUTREG32(intel_i810_private.registers, + I810_PTE_BASE + (i * 4), + agp_bridge.scratch_page); + } + + CACHE_FLUSH(); + agp_bridge.tlb_flush(mem); + return 0; +} + +static agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type) +{ + agp_memory *new; + + if (type == AGP_DCACHE_MEMORY) { + if (pg_count != intel_i810_private.num_dcache_entries) { + return NULL; + } + new = agp_create_memory(1); + + if (new == NULL) { + return NULL; + } + new->type = AGP_DCACHE_MEMORY; + new->page_count = pg_count; + new->num_scratch_pages = 0; + vfree(new->memory); + MOD_INC_USE_COUNT; + return new; + } + if(type == AGP_PHYS_MEMORY) { + void *addr; + /* The I810 requires a physical address to program + * it's mouse pointer into hardware. However the + * Xserver still writes to it through the agp + * aperture + */ + if (pg_count != 1) { + return NULL; + } + new = agp_create_memory(1); + + if (new == NULL) { + return NULL; + } + MOD_INC_USE_COUNT; + addr = agp_bridge.agp_alloc_page(); + + if (addr == NULL) { + /* Free this structure */ + agp_free_memory(new); + return NULL; + } + new->memory[0] = agp_bridge.mask_memory(virt_to_phys(addr), type); + new->page_count = 1; + new->num_scratch_pages = 1; + new->type = AGP_PHYS_MEMORY; + new->physical = virt_to_phys((void *) new->memory[0]); + return new; + } + + return NULL; +} + +static void intel_i810_free_by_type(agp_memory * curr) +{ + agp_free_key(curr->key); + if(curr->type == AGP_PHYS_MEMORY) { + agp_bridge.agp_destroy_page( + phys_to_virt(curr->memory[0])); + vfree(curr->memory); + } + kfree(curr); + MOD_DEC_USE_COUNT; +} + +static unsigned long intel_i810_mask_memory(unsigned long addr, int type) +{ + /* Type checking must be done elsewhere */ + return addr | agp_bridge.masks[type].mask; +} + +int __init intel_i810_setup(struct pci_dev *i810_dev) +{ + intel_i810_private.i810_dev = i810_dev; + + agp_bridge.masks = intel_i810_masks; + agp_bridge.num_of_masks = 2; + agp_bridge.aperture_sizes = (void *) intel_i810_sizes; + agp_bridge.size_type = FIXED_APER_SIZE; + agp_bridge.num_aperture_sizes = 2; + agp_bridge.dev_private_data = (void *) &intel_i810_private; + agp_bridge.needs_scratch_page = TRUE; + agp_bridge.configure = intel_i810_configure; + agp_bridge.fetch_size = intel_i810_fetch_size; + agp_bridge.cleanup = intel_i810_cleanup; + agp_bridge.tlb_flush = intel_i810_tlbflush; + agp_bridge.mask_memory = intel_i810_mask_memory; + agp_bridge.agp_enable = intel_i810_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = intel_i810_insert_entries; + agp_bridge.remove_memory = intel_i810_remove_entries; + agp_bridge.alloc_by_type = intel_i810_alloc_by_type; + agp_bridge.free_by_type = intel_i810_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; +} + +static struct aper_size_info_fixed intel_i830_sizes[] = +{ + {128, 32768, 5}, + /* The 64M mode still requires a 128k gatt */ + {64, 16384, 5} +}; + +static struct _intel_i830_private { + struct pci_dev *i830_dev; /* device one */ + volatile u8 *registers; + int gtt_entries; +} intel_i830_private; + +static void intel_i830_init_gtt_entries(void) +{ + u16 gmch_ctrl; + int gtt_entries; + u8 rdct; + static const int ddt[4] = { 0, 16, 32, 64 }; + + pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl); + + switch (gmch_ctrl & I830_GMCH_GMS_MASK) { + case I830_GMCH_GMS_STOLEN_512: + gtt_entries = KB(512) - KB(132); + printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1)); + break; + case I830_GMCH_GMS_STOLEN_1024: + gtt_entries = MB(1) - KB(132); + printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1)); + break; + case I830_GMCH_GMS_STOLEN_8192: + gtt_entries = MB(8) - KB(132); + printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1)); + break; + case I830_GMCH_GMS_LOCAL: + rdct = INREG8(intel_i830_private.registers,I830_RDRAM_CHANNEL_TYPE); + gtt_entries = (I830_RDRAM_ND(rdct) + 1) * MB(ddt[I830_RDRAM_DDT(rdct)]); + printk(KERN_INFO PFX "detected %dK local memory.\n",gtt_entries / KB(1)); + break; + default: + printk(KERN_INFO PFX "no video memory detected.\n"); + gtt_entries = 0; + break; + } + + gtt_entries /= KB(4); + + intel_i830_private.gtt_entries = gtt_entries; +} + +/* The intel i830 automatically initializes the agp aperture during POST. + * Use the memory already set aside for in the GTT. + */ +static int intel_i830_create_gatt_table(void) +{ + int page_order; + struct aper_size_info_fixed *size; + int num_entries; + u32 temp; + + size = agp_bridge.current_size; + page_order = size->page_order; + num_entries = size->num_entries; + agp_bridge.gatt_table_real = 0; + + pci_read_config_dword(intel_i830_private.i830_dev,I810_MMADDR,&temp); + temp &= 0xfff80000; + + intel_i830_private.registers = (volatile u8 *) ioremap(temp,128 * 4096); + if (!intel_i830_private.registers) return (-ENOMEM); + + temp = INREG32(intel_i830_private.registers,I810_PGETBL_CTL) & 0xfffff000; + CACHE_FLUSH(); + + /* we have to call this as early as possible after the MMIO base address is known */ + intel_i830_init_gtt_entries(); + + agp_bridge.gatt_table = NULL; + + agp_bridge.gatt_bus_addr = temp; + + return(0); +} + +/* Return the gatt table to a sane state. Use the top of stolen + * memory for the GTT. + */ +static int intel_i830_free_gatt_table(void) +{ + return(0); +} + +static int intel_i830_fetch_size(void) +{ + u16 gmch_ctrl; + struct aper_size_info_fixed *values; + + pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl); + values = A_SIZE_FIX(agp_bridge.aperture_sizes); + + if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) { + agp_bridge.previous_size = agp_bridge.current_size = (void *) values; + agp_bridge.aperture_size_idx = 0; + return(values[0].size); + } else { + agp_bridge.previous_size = agp_bridge.current_size = (void *) values; + agp_bridge.aperture_size_idx = 1; + return(values[1].size); + } + + return(0); +} + +static int intel_i830_configure(void) +{ + struct aper_size_info_fixed *current_size; + u32 temp; + u16 gmch_ctrl; + int i; + + current_size = A_SIZE_FIX(agp_bridge.current_size); + + pci_read_config_dword(intel_i830_private.i830_dev,I810_GMADDR,&temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl); + gmch_ctrl |= I830_GMCH_ENABLED; + pci_write_config_word(agp_bridge.dev,I830_GMCH_CTRL,gmch_ctrl); + + OUTREG32(intel_i830_private.registers,I810_PGETBL_CTL,agp_bridge.gatt_bus_addr | I810_PGETBL_ENABLED); + CACHE_FLUSH(); + + if (agp_bridge.needs_scratch_page == TRUE) + for (i = intel_i830_private.gtt_entries; i < current_size->num_entries; i++) + OUTREG32(intel_i830_private.registers,I810_PTE_BASE + (i * 4),agp_bridge.scratch_page); + + return (0); +} + +static void intel_i830_cleanup(void) +{ + iounmap((void *) intel_i830_private.registers); +} + +static int intel_i830_insert_entries(agp_memory *mem,off_t pg_start,int type) +{ + int i,j,num_entries; + void *temp; + + temp = agp_bridge.current_size; + num_entries = A_SIZE_FIX(temp)->num_entries; + + if (pg_start < intel_i830_private.gtt_entries) { + printk (KERN_DEBUG "pg_start == 0x%.8lx,intel_i830_private.gtt_entries == 0x%.8x\n", + pg_start,intel_i830_private.gtt_entries); + + printk ("Trying to insert into local/stolen memory\n"); + return (-EINVAL); + } + + if ((pg_start + mem->page_count) > num_entries) + return (-EINVAL); + + /* The i830 can't check the GTT for entries since its read only, + * depend on the caller to make the correct offset decisions. + */ + + if ((type != 0 && type != AGP_PHYS_MEMORY) || + (mem->type != 0 && mem->type != AGP_PHYS_MEMORY)) + return (-EINVAL); + + CACHE_FLUSH(); + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) + OUTREG32(intel_i830_private.registers,I810_PTE_BASE + (j * 4),mem->memory[i]); + + CACHE_FLUSH(); + + agp_bridge.tlb_flush(mem); + + return(0); +} + +static int intel_i830_remove_entries(agp_memory *mem,off_t pg_start,int type) +{ + int i; + + CACHE_FLUSH (); + + if (pg_start < intel_i830_private.gtt_entries) { + printk ("Trying to disable local/stolen memory\n"); + return (-EINVAL); + } + + for (i = pg_start; i < (mem->page_count + pg_start); i++) + OUTREG32(intel_i830_private.registers,I810_PTE_BASE + (i * 4),agp_bridge.scratch_page); + + CACHE_FLUSH(); + + agp_bridge.tlb_flush(mem); + + return (0); +} + +static agp_memory *intel_i830_alloc_by_type(size_t pg_count,int type) +{ + agp_memory *nw; + + /* always return NULL for now */ + if (type == AGP_DCACHE_MEMORY) return(NULL); + + if (type == AGP_PHYS_MEMORY) { + void *addr; + + /* The i830 requires a physical address to program + * it's mouse pointer into hardware. However the + * Xserver still writes to it through the agp + * aperture + */ + + if (pg_count != 1) return(NULL); + + nw = agp_create_memory(1); + + if (nw == NULL) return(NULL); + + MOD_INC_USE_COUNT; + addr = agp_bridge.agp_alloc_page(); + if (addr == NULL) { + /* free this structure */ + agp_free_memory(nw); + return(NULL); + } + + nw->memory[0] = agp_bridge.mask_memory(virt_to_phys(addr),type); + nw->page_count = 1; + nw->num_scratch_pages = 1; + nw->type = AGP_PHYS_MEMORY; + nw->physical = virt_to_phys(addr); + return(nw); + } + + return(NULL); +} + +int __init intel_i830_setup(struct pci_dev *i830_dev) +{ + intel_i830_private.i830_dev = i830_dev; + + agp_bridge.masks = intel_i810_masks; + agp_bridge.num_of_masks = 3; + agp_bridge.aperture_sizes = (void *) intel_i830_sizes; + agp_bridge.size_type = FIXED_APER_SIZE; + agp_bridge.num_aperture_sizes = 2; + + agp_bridge.dev_private_data = (void *) &intel_i830_private; + agp_bridge.needs_scratch_page = TRUE; + + agp_bridge.configure = intel_i830_configure; + agp_bridge.fetch_size = intel_i830_fetch_size; + agp_bridge.cleanup = intel_i830_cleanup; + agp_bridge.tlb_flush = intel_i810_tlbflush; + agp_bridge.mask_memory = intel_i810_mask_memory; + agp_bridge.agp_enable = intel_i810_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + + agp_bridge.create_gatt_table = intel_i830_create_gatt_table; + agp_bridge.free_gatt_table = intel_i830_free_gatt_table; + + agp_bridge.insert_memory = intel_i830_insert_entries; + agp_bridge.remove_memory = intel_i830_remove_entries; + agp_bridge.alloc_by_type = intel_i830_alloc_by_type; + agp_bridge.free_by_type = intel_i810_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return(0); +} + diff --git a/drivers/char/agp/i8x0-agp.c b/drivers/char/agp/i8x0-agp.c new file mode 100644 index 000000000000..bf1daf0a69dd --- /dev/null +++ b/drivers/char/agp/i8x0-agp.c @@ -0,0 +1,726 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * TODO: + * - Allocate more than order 0 pages to avoid too much linear map splitting. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/agp_backend.h> +#include "agp.h" + + +static int intel_fetch_size(void) +{ + int i; + u16 temp; + struct aper_size_info_16 *values; + + pci_read_config_word(agp_bridge.dev, INTEL_APSIZE, &temp); + values = A_SIZE_16(agp_bridge.aperture_sizes); + + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if (temp == values[i].size_value) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + + return 0; +} + +static int intel_8xx_fetch_size(void) +{ + int i; + u8 temp; + struct aper_size_info_8 *values; + + pci_read_config_byte(agp_bridge.dev, INTEL_APSIZE, &temp); + + /* Intel 815 chipsets have a _weird_ APSIZE register with only + * one non-reserved bit, so mask the others out ... */ + if (agp_bridge.type == INTEL_I815) + temp &= (1 << 3); + + values = A_SIZE_8(agp_bridge.aperture_sizes); + + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if (temp == values[i].size_value) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + return 0; +} + + +static void intel_tlbflush(agp_memory * mem) +{ + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x2200); + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x2280); +} + + +static void intel_8xx_tlbflush(agp_memory * mem) +{ + u32 temp; + pci_read_config_dword(agp_bridge.dev, INTEL_AGPCTRL, &temp); + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, temp & ~(1 << 7)); + pci_read_config_dword(agp_bridge.dev, INTEL_AGPCTRL, &temp); + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, temp | (1 << 7)); +} + + +static void intel_cleanup(void) +{ + u16 temp; + struct aper_size_info_16 *previous_size; + + previous_size = A_SIZE_16(agp_bridge.previous_size); + pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp); + pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG, temp & ~(1 << 9)); + pci_write_config_word(agp_bridge.dev, INTEL_APSIZE, + previous_size->size_value); +} + + +static void intel_8xx_cleanup(void) +{ + u16 temp; + struct aper_size_info_8 *previous_size; + + previous_size = A_SIZE_8(agp_bridge.previous_size); + pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp); + pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG, temp & ~(1 << 9)); + pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, + previous_size->size_value); +} + + +static int intel_configure(void) +{ + u32 temp; + u16 temp2; + struct aper_size_info_16 *current_size; + + current_size = A_SIZE_16(agp_bridge.current_size); + + /* aperture size */ + pci_write_config_word(agp_bridge.dev, INTEL_APSIZE, + current_size->size_value); + + /* address to map to */ + pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* attbase - aperture base */ + pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, + agp_bridge.gatt_bus_addr); + + /* agpctrl */ + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x2280); + + /* paccfg/nbxcfg */ + pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp2); + pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG, + (temp2 & ~(1 << 10)) | (1 << 9)); + /* clear any possible error conditions */ + pci_write_config_byte(agp_bridge.dev, INTEL_ERRSTS + 1, 7); + return 0; +} + +static int intel_815_configure(void) +{ + u32 temp, addr; + u8 temp2; + struct aper_size_info_8 *current_size; + + current_size = A_SIZE_8(agp_bridge.current_size); + + /* aperture size */ + pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, + current_size->size_value); + + /* address to map to */ + pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* attbase - aperture base */ + /* the Intel 815 chipset spec. says that bits 29-31 in the + * ATTBASE register are reserved -> try not to write them */ + if (agp_bridge.gatt_bus_addr & INTEL_815_ATTBASE_MASK) + panic("gatt bus addr too high"); + pci_read_config_dword(agp_bridge.dev, INTEL_ATTBASE, &addr); + addr &= INTEL_815_ATTBASE_MASK; + addr |= agp_bridge.gatt_bus_addr; + pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, addr); + + /* agpctrl */ + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); + + /* apcont */ + pci_read_config_byte(agp_bridge.dev, INTEL_815_APCONT, &temp2); + pci_write_config_byte(agp_bridge.dev, INTEL_815_APCONT, temp2 | (1 << 1)); + + /* clear any possible error conditions */ + /* Oddness : this chipset seems to have no ERRSTS register ! */ + return 0; +} + +static void intel_820_tlbflush(agp_memory * mem) +{ + return; +} + +static void intel_820_cleanup(void) +{ + u8 temp; + struct aper_size_info_8 *previous_size; + + previous_size = A_SIZE_8(agp_bridge.previous_size); + pci_read_config_byte(agp_bridge.dev, INTEL_I820_RDCR, &temp); + pci_write_config_byte(agp_bridge.dev, INTEL_I820_RDCR, + temp & ~(1 << 1)); + pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, + previous_size->size_value); +} + + +static int intel_820_configure(void) +{ + u32 temp; + u8 temp2; + struct aper_size_info_8 *current_size; + + current_size = A_SIZE_8(agp_bridge.current_size); + + /* aperture size */ + pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, + current_size->size_value); + + /* address to map to */ + pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* attbase - aperture base */ + pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, + agp_bridge.gatt_bus_addr); + + /* agpctrl */ + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); + + /* global enable aperture access */ + /* This flag is not accessed through MCHCFG register as in */ + /* i850 chipset. */ + pci_read_config_byte(agp_bridge.dev, INTEL_I820_RDCR, &temp2); + pci_write_config_byte(agp_bridge.dev, INTEL_I820_RDCR, + temp2 | (1 << 1)); + /* clear any possible AGP-related error conditions */ + pci_write_config_word(agp_bridge.dev, INTEL_I820_ERRSTS, 0x001c); + return 0; +} + +static int intel_840_configure(void) +{ + u32 temp; + u16 temp2; + struct aper_size_info_8 *current_size; + + current_size = A_SIZE_8(agp_bridge.current_size); + + /* aperture size */ + pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, + current_size->size_value); + + /* address to map to */ + pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* attbase - aperture base */ + pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, + agp_bridge.gatt_bus_addr); + + /* agpctrl */ + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); + + /* mcgcfg */ + pci_read_config_word(agp_bridge.dev, INTEL_I840_MCHCFG, &temp2); + pci_write_config_word(agp_bridge.dev, INTEL_I840_MCHCFG, + temp2 | (1 << 9)); + /* clear any possible error conditions */ + pci_write_config_word(agp_bridge.dev, INTEL_I840_ERRSTS, 0xc000); + return 0; +} + +static int intel_845_configure(void) +{ + u32 temp; + u8 temp2; + struct aper_size_info_8 *current_size; + + current_size = A_SIZE_8(agp_bridge.current_size); + + /* aperture size */ + pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, + current_size->size_value); + + /* address to map to */ + pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* attbase - aperture base */ + pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, + agp_bridge.gatt_bus_addr); + + /* agpctrl */ + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); + + /* agpm */ + pci_read_config_byte(agp_bridge.dev, INTEL_I845_AGPM, &temp2); + pci_write_config_byte(agp_bridge.dev, INTEL_I845_AGPM, + temp2 | (1 << 1)); + /* clear any possible error conditions */ + pci_write_config_word(agp_bridge.dev, INTEL_I845_ERRSTS, 0x001c); + return 0; +} + +static int intel_850_configure(void) +{ + u32 temp; + u16 temp2; + struct aper_size_info_8 *current_size; + + current_size = A_SIZE_8(agp_bridge.current_size); + + /* aperture size */ + pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, + current_size->size_value); + + /* address to map to */ + pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* attbase - aperture base */ + pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, + agp_bridge.gatt_bus_addr); + + /* agpctrl */ + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); + + /* mcgcfg */ + pci_read_config_word(agp_bridge.dev, INTEL_I850_MCHCFG, &temp2); + pci_write_config_word(agp_bridge.dev, INTEL_I850_MCHCFG, + temp2 | (1 << 9)); + /* clear any possible AGP-related error conditions */ + pci_write_config_word(agp_bridge.dev, INTEL_I850_ERRSTS, 0x001c); + return 0; +} + +static int intel_860_configure(void) +{ + u32 temp; + u16 temp2; + struct aper_size_info_8 *current_size; + + current_size = A_SIZE_8(agp_bridge.current_size); + + /* aperture size */ + pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, + current_size->size_value); + + /* address to map to */ + pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* attbase - aperture base */ + pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, + agp_bridge.gatt_bus_addr); + + /* agpctrl */ + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); + + /* mcgcfg */ + pci_read_config_word(agp_bridge.dev, INTEL_I860_MCHCFG, &temp2); + pci_write_config_word(agp_bridge.dev, INTEL_I860_MCHCFG, + temp2 | (1 << 9)); + /* clear any possible AGP-related error conditions */ + pci_write_config_word(agp_bridge.dev, INTEL_I860_ERRSTS, 0xf700); + return 0; +} + +static int intel_830mp_configure(void) +{ + u32 temp; + u16 temp2; + struct aper_size_info_8 *current_size; + + current_size = A_SIZE_8(agp_bridge.current_size); + + /* aperture size */ + pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, + current_size->size_value); + + /* address to map to */ + pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* attbase - aperture base */ + pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, + agp_bridge.gatt_bus_addr); + + /* agpctrl */ + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); + + /* gmch */ + pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp2); + pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG, + temp2 | (1 << 9)); + /* clear any possible AGP-related error conditions */ + pci_write_config_word(agp_bridge.dev, INTEL_I830_ERRSTS, 0x1c); + return 0; +} + +static unsigned long intel_mask_memory(unsigned long addr, int type) +{ + /* Memory type is ignored */ + + return addr | agp_bridge.masks[0].mask; +} + +static void intel_resume(void) +{ + intel_configure(); +} + +/* Setup function */ +static struct gatt_mask intel_generic_masks[] = +{ + {mask: 0x00000017, type: 0} +}; + +static struct aper_size_info_8 intel_815_sizes[2] = +{ + {64, 16384, 4, 0}, + {32, 8192, 3, 8}, +}; + +static struct aper_size_info_8 intel_8xx_sizes[7] = +{ + {256, 65536, 6, 0}, + {128, 32768, 5, 32}, + {64, 16384, 4, 48}, + {32, 8192, 3, 56}, + {16, 4096, 2, 60}, + {8, 2048, 1, 62}, + {4, 1024, 0, 63} +}; + +static struct aper_size_info_16 intel_generic_sizes[7] = +{ + {256, 65536, 6, 0}, + {128, 32768, 5, 32}, + {64, 16384, 4, 48}, + {32, 8192, 3, 56}, + {16, 4096, 2, 60}, + {8, 2048, 1, 62}, + {4, 1024, 0, 63} +}; + +static struct aper_size_info_8 intel_830mp_sizes[4] = +{ + {256, 65536, 6, 0}, + {128, 32768, 5, 32}, + {64, 16384, 4, 48}, + {32, 8192, 3, 56} +}; + +int __init intel_generic_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = intel_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) intel_generic_sizes; + agp_bridge.size_type = U16_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = intel_configure; + agp_bridge.fetch_size = intel_fetch_size; + agp_bridge.cleanup = intel_cleanup; + agp_bridge.tlb_flush = intel_tlbflush; + agp_bridge.mask_memory = intel_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = intel_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; + + (void) pdev; /* unused */ +} + +int __init intel_815_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = intel_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) intel_815_sizes; + agp_bridge.size_type = U8_APER_SIZE; + agp_bridge.num_aperture_sizes = 2; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = intel_815_configure; + agp_bridge.fetch_size = intel_8xx_fetch_size; + agp_bridge.cleanup = intel_8xx_cleanup; + agp_bridge.tlb_flush = intel_8xx_tlbflush; + agp_bridge.mask_memory = intel_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; +} + + +int __init intel_820_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = intel_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) intel_8xx_sizes; + agp_bridge.size_type = U8_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = intel_820_configure; + agp_bridge.fetch_size = intel_8xx_fetch_size; + agp_bridge.cleanup = intel_820_cleanup; + agp_bridge.tlb_flush = intel_820_tlbflush; + agp_bridge.mask_memory = intel_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; + + (void) pdev; /* unused */ +} + +int __init intel_830mp_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = intel_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) intel_830mp_sizes; + agp_bridge.size_type = U8_APER_SIZE; + agp_bridge.num_aperture_sizes = 4; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = intel_830mp_configure; + agp_bridge.fetch_size = intel_8xx_fetch_size; + agp_bridge.cleanup = intel_8xx_cleanup; + agp_bridge.tlb_flush = intel_8xx_tlbflush; + agp_bridge.mask_memory = intel_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; + + (void) pdev; /* unused */ +} + +int __init intel_840_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = intel_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) intel_8xx_sizes; + agp_bridge.size_type = U8_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = intel_840_configure; + agp_bridge.fetch_size = intel_8xx_fetch_size; + agp_bridge.cleanup = intel_8xx_cleanup; + agp_bridge.tlb_flush = intel_8xx_tlbflush; + agp_bridge.mask_memory = intel_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; + + (void) pdev; /* unused */ +} + +int __init intel_845_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = intel_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) intel_8xx_sizes; + agp_bridge.size_type = U8_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = intel_845_configure; + agp_bridge.fetch_size = intel_8xx_fetch_size; + agp_bridge.cleanup = intel_8xx_cleanup; + agp_bridge.tlb_flush = intel_8xx_tlbflush; + agp_bridge.mask_memory = intel_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; + + (void) pdev; /* unused */ +} + +int __init intel_850_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = intel_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) intel_8xx_sizes; + agp_bridge.size_type = U8_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = intel_850_configure; + agp_bridge.fetch_size = intel_8xx_fetch_size; + agp_bridge.cleanup = intel_8xx_cleanup; + agp_bridge.tlb_flush = intel_8xx_tlbflush; + agp_bridge.mask_memory = intel_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; + + (void) pdev; /* unused */ +} + +int __init intel_860_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = intel_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) intel_8xx_sizes; + agp_bridge.size_type = U8_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = intel_860_configure; + agp_bridge.fetch_size = intel_8xx_fetch_size; + agp_bridge.cleanup = intel_8xx_cleanup; + agp_bridge.tlb_flush = intel_8xx_tlbflush; + agp_bridge.mask_memory = intel_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; + + (void) pdev; /* unused */ +} + diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c new file mode 100644 index 000000000000..841c32a40267 --- /dev/null +++ b/drivers/char/agp/sis-agp.c @@ -0,0 +1,142 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * TODO: + * - Allocate more than order 0 pages to avoid too much linear map splitting. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/agp_backend.h> +#include "agp.h" + +static int sis_fetch_size(void) +{ + u8 temp_size; + int i; + struct aper_size_info_8 *values; + + pci_read_config_byte(agp_bridge.dev, SIS_APSIZE, &temp_size); + values = A_SIZE_8(agp_bridge.aperture_sizes); + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if ((temp_size == values[i].size_value) || + ((temp_size & ~(0x03)) == + (values[i].size_value & ~(0x03)))) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + + return 0; +} + + +static void sis_tlbflush(agp_memory * mem) +{ + pci_write_config_byte(agp_bridge.dev, SIS_TLBFLUSH, 0x02); +} + +static int sis_configure(void) +{ + u32 temp; + struct aper_size_info_8 *current_size; + + current_size = A_SIZE_8(agp_bridge.current_size); + pci_write_config_byte(agp_bridge.dev, SIS_TLBCNTRL, 0x05); + pci_read_config_dword(agp_bridge.dev, SIS_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + pci_write_config_dword(agp_bridge.dev, SIS_ATTBASE, + agp_bridge.gatt_bus_addr); + pci_write_config_byte(agp_bridge.dev, SIS_APSIZE, + current_size->size_value); + return 0; +} + +static void sis_cleanup(void) +{ + struct aper_size_info_8 *previous_size; + + previous_size = A_SIZE_8(agp_bridge.previous_size); + pci_write_config_byte(agp_bridge.dev, SIS_APSIZE, + (previous_size->size_value & ~(0x03))); +} + +static unsigned long sis_mask_memory(unsigned long addr, int type) +{ + /* Memory type is ignored */ + + return addr | agp_bridge.masks[0].mask; +} + +static struct aper_size_info_8 sis_generic_sizes[7] = +{ + {256, 65536, 6, 99}, + {128, 32768, 5, 83}, + {64, 16384, 4, 67}, + {32, 8192, 3, 51}, + {16, 4096, 2, 35}, + {8, 2048, 1, 19}, + {4, 1024, 0, 3} +}; + +static struct gatt_mask sis_generic_masks[] = +{ + {mask: 0x00000000, type: 0} +}; + +int __init sis_generic_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = sis_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) sis_generic_sizes; + agp_bridge.size_type = U8_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = sis_configure; + agp_bridge.fetch_size = sis_fetch_size; + agp_bridge.cleanup = sis_cleanup; + agp_bridge.tlb_flush = sis_tlbflush; + agp_bridge.mask_memory = sis_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; +} + diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c new file mode 100644 index 000000000000..ad9e4c46cc52 --- /dev/null +++ b/drivers/char/agp/sworks-agp.c @@ -0,0 +1,626 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * TODO: + * - Allocate more than order 0 pages to avoid too much linear map splitting. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/agp_backend.h> +#include "agp.h" + +struct serverworks_page_map { + unsigned long *real; + unsigned long *remapped; +}; + +static struct _serverworks_private { + struct pci_dev *svrwrks_dev; /* device one */ + volatile u8 *registers; + struct serverworks_page_map **gatt_pages; + int num_tables; + struct serverworks_page_map scratch_dir; + + int gart_addr_ofs; + int mm_addr_ofs; +} serverworks_private; + +static int serverworks_create_page_map(struct serverworks_page_map *page_map) +{ + int i; + + page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL); + if (page_map->real == NULL) { + return -ENOMEM; + } + SetPageReserved(virt_to_page(page_map->real)); + CACHE_FLUSH(); + page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real), + PAGE_SIZE); + if (page_map->remapped == NULL) { + ClearPageReserved(virt_to_page(page_map->real)); + free_page((unsigned long) page_map->real); + page_map->real = NULL; + return -ENOMEM; + } + CACHE_FLUSH(); + + for(i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) { + page_map->remapped[i] = agp_bridge.scratch_page; + } + + return 0; +} + +static void serverworks_free_page_map(struct serverworks_page_map *page_map) +{ + iounmap(page_map->remapped); + ClearPageReserved(virt_to_page(page_map->real)); + free_page((unsigned long) page_map->real); +} + +static void serverworks_free_gatt_pages(void) +{ + int i; + struct serverworks_page_map **tables; + struct serverworks_page_map *entry; + + tables = serverworks_private.gatt_pages; + for(i = 0; i < serverworks_private.num_tables; i++) { + entry = tables[i]; + if (entry != NULL) { + if (entry->real != NULL) { + serverworks_free_page_map(entry); + } + kfree(entry); + } + } + kfree(tables); +} + +static int serverworks_create_gatt_pages(int nr_tables) +{ + struct serverworks_page_map **tables; + struct serverworks_page_map *entry; + int retval = 0; + int i; + + tables = kmalloc((nr_tables + 1) * sizeof(struct serverworks_page_map *), + GFP_KERNEL); + if (tables == NULL) { + return -ENOMEM; + } + memset(tables, 0, sizeof(struct serverworks_page_map *) * (nr_tables + 1)); + for (i = 0; i < nr_tables; i++) { + entry = kmalloc(sizeof(struct serverworks_page_map), GFP_KERNEL); + if (entry == NULL) { + retval = -ENOMEM; + break; + } + memset(entry, 0, sizeof(struct serverworks_page_map)); + tables[i] = entry; + retval = serverworks_create_page_map(entry); + if (retval != 0) break; + } + serverworks_private.num_tables = nr_tables; + serverworks_private.gatt_pages = tables; + + if (retval != 0) serverworks_free_gatt_pages(); + + return retval; +} + +#define SVRWRKS_GET_GATT(addr) (serverworks_private.gatt_pages[\ + GET_PAGE_DIR_IDX(addr)]->remapped) + +#ifndef GET_PAGE_DIR_OFF +#define GET_PAGE_DIR_OFF(addr) (addr >> 22) +#endif + +#ifndef GET_PAGE_DIR_IDX +#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \ + GET_PAGE_DIR_OFF(agp_bridge.gart_bus_addr)) +#endif + +#ifndef GET_GATT_OFF +#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) +#endif + +static int serverworks_create_gatt_table(void) +{ + struct aper_size_info_lvl2 *value; + struct serverworks_page_map page_dir; + int retval; + u32 temp; + int i; + + value = A_SIZE_LVL2(agp_bridge.current_size); + retval = serverworks_create_page_map(&page_dir); + if (retval != 0) { + return retval; + } + retval = serverworks_create_page_map(&serverworks_private.scratch_dir); + if (retval != 0) { + serverworks_free_page_map(&page_dir); + return retval; + } + /* Create a fake scratch directory */ + for(i = 0; i < 1024; i++) { + serverworks_private.scratch_dir.remapped[i] = (unsigned long) agp_bridge.scratch_page; + page_dir.remapped[i] = + virt_to_phys(serverworks_private.scratch_dir.real); + page_dir.remapped[i] |= 0x00000001; + } + + retval = serverworks_create_gatt_pages(value->num_entries / 1024); + if (retval != 0) { + serverworks_free_page_map(&page_dir); + serverworks_free_page_map(&serverworks_private.scratch_dir); + return retval; + } + + agp_bridge.gatt_table_real = page_dir.real; + agp_bridge.gatt_table = page_dir.remapped; + agp_bridge.gatt_bus_addr = virt_to_phys(page_dir.real); + + /* Get the address for the gart region. + * This is a bus address even on the alpha, b/c its + * used to program the agp master not the cpu + */ + + pci_read_config_dword(agp_bridge.dev, + serverworks_private.gart_addr_ofs, + &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* Calculate the agp offset */ + + for(i = 0; i < value->num_entries / 1024; i++) { + page_dir.remapped[i] = + virt_to_phys(serverworks_private.gatt_pages[i]->real); + page_dir.remapped[i] |= 0x00000001; + } + + return 0; +} + +static int serverworks_free_gatt_table(void) +{ + struct serverworks_page_map page_dir; + + page_dir.real = agp_bridge.gatt_table_real; + page_dir.remapped = agp_bridge.gatt_table; + + serverworks_free_gatt_pages(); + serverworks_free_page_map(&page_dir); + serverworks_free_page_map(&serverworks_private.scratch_dir); + return 0; +} + +static int serverworks_fetch_size(void) +{ + int i; + u32 temp; + u32 temp2; + struct aper_size_info_lvl2 *values; + + values = A_SIZE_LVL2(agp_bridge.aperture_sizes); + pci_read_config_dword(agp_bridge.dev, + serverworks_private.gart_addr_ofs, + &temp); + pci_write_config_dword(agp_bridge.dev, + serverworks_private.gart_addr_ofs, + SVWRKS_SIZE_MASK); + pci_read_config_dword(agp_bridge.dev, + serverworks_private.gart_addr_ofs, + &temp2); + pci_write_config_dword(agp_bridge.dev, + serverworks_private.gart_addr_ofs, + temp); + temp2 &= SVWRKS_SIZE_MASK; + + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if (temp2 == values[i].size_value) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + + return 0; +} + +static int serverworks_configure(void) +{ + struct aper_size_info_lvl2 *current_size; + u32 temp; + u8 enable_reg; + u8 cap_ptr; + u32 cap_id; + u16 cap_reg; + + current_size = A_SIZE_LVL2(agp_bridge.current_size); + + /* Get the memory mapped registers */ + pci_read_config_dword(agp_bridge.dev, + serverworks_private.mm_addr_ofs, + &temp); + temp = (temp & PCI_BASE_ADDRESS_MEM_MASK); + serverworks_private.registers = (volatile u8 *) ioremap(temp, 4096); + + OUTREG8(serverworks_private.registers, SVWRKS_GART_CACHE, 0x0a); + + OUTREG32(serverworks_private.registers, SVWRKS_GATTBASE, + agp_bridge.gatt_bus_addr); + + cap_reg = INREG16(serverworks_private.registers, SVWRKS_COMMAND); + cap_reg &= ~0x0007; + cap_reg |= 0x4; + OUTREG16(serverworks_private.registers, SVWRKS_COMMAND, cap_reg); + + pci_read_config_byte(serverworks_private.svrwrks_dev, + SVWRKS_AGP_ENABLE, &enable_reg); + enable_reg |= 0x1; /* Agp Enable bit */ + pci_write_config_byte(serverworks_private.svrwrks_dev, + SVWRKS_AGP_ENABLE, enable_reg); + agp_bridge.tlb_flush(NULL); + + pci_read_config_byte(serverworks_private.svrwrks_dev, 0x34, &cap_ptr); + if (cap_ptr != 0x00) { + do { + pci_read_config_dword(serverworks_private.svrwrks_dev, + cap_ptr, &cap_id); + + if ((cap_id & 0xff) != 0x02) + cap_ptr = (cap_id >> 8) & 0xff; + } + while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00)); + } + agp_bridge.capndx = cap_ptr; + + /* Fill in the mode register */ + pci_read_config_dword(serverworks_private.svrwrks_dev, + agp_bridge.capndx + 4, + &agp_bridge.mode); + + pci_read_config_byte(agp_bridge.dev, + SVWRKS_CACHING, + &enable_reg); + enable_reg &= ~0x3; + pci_write_config_byte(agp_bridge.dev, + SVWRKS_CACHING, + enable_reg); + + pci_read_config_byte(agp_bridge.dev, + SVWRKS_FEATURE, + &enable_reg); + enable_reg |= (1<<6); + pci_write_config_byte(agp_bridge.dev, + SVWRKS_FEATURE, + enable_reg); + + return 0; +} + +static void serverworks_cleanup(void) +{ + iounmap((void *) serverworks_private.registers); +} + +/* + * This routine could be implemented by taking the addresses + * written to the GATT, and flushing them individually. However + * currently it just flushes the whole table. Which is probably + * more efficent, since agp_memory blocks can be a large number of + * entries. + */ + +static void serverworks_tlbflush(agp_memory * temp) +{ + unsigned long end; + + OUTREG8(serverworks_private.registers, SVWRKS_POSTFLUSH, 0x01); + end = jiffies + 3*HZ; + while(INREG8(serverworks_private.registers, + SVWRKS_POSTFLUSH) == 0x01) { + if((signed)(end - jiffies) <= 0) { + printk(KERN_ERR "Posted write buffer flush took more" + "then 3 seconds\n"); + } + } + OUTREG32(serverworks_private.registers, SVWRKS_DIRFLUSH, 0x00000001); + end = jiffies + 3*HZ; + while(INREG32(serverworks_private.registers, + SVWRKS_DIRFLUSH) == 0x00000001) { + if((signed)(end - jiffies) <= 0) { + printk(KERN_ERR "TLB flush took more" + "then 3 seconds\n"); + } + } +} + +static unsigned long serverworks_mask_memory(unsigned long addr, int type) +{ + /* Only type 0 is supported by the serverworks chipsets */ + + return addr | agp_bridge.masks[0].mask; +} + +static int serverworks_insert_memory(agp_memory * mem, + off_t pg_start, int type) +{ + int i, j, num_entries; + unsigned long *cur_gatt; + unsigned long addr; + + num_entries = A_SIZE_LVL2(agp_bridge.current_size)->num_entries; + + if (type != 0 || mem->type != 0) { + return -EINVAL; + } + if ((pg_start + mem->page_count) > num_entries) { + return -EINVAL; + } + + j = pg_start; + while (j < (pg_start + mem->page_count)) { + addr = (j * PAGE_SIZE) + agp_bridge.gart_bus_addr; + cur_gatt = SVRWRKS_GET_GATT(addr); + if (!PGE_EMPTY(cur_gatt[GET_GATT_OFF(addr)])) { + return -EBUSY; + } + j++; + } + + if (mem->is_flushed == FALSE) { + CACHE_FLUSH(); + mem->is_flushed = TRUE; + } + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + addr = (j * PAGE_SIZE) + agp_bridge.gart_bus_addr; + cur_gatt = SVRWRKS_GET_GATT(addr); + cur_gatt[GET_GATT_OFF(addr)] = mem->memory[i]; + } + agp_bridge.tlb_flush(mem); + return 0; +} + +static int serverworks_remove_memory(agp_memory * mem, off_t pg_start, + int type) +{ + int i; + unsigned long *cur_gatt; + unsigned long addr; + + if (type != 0 || mem->type != 0) { + return -EINVAL; + } + + CACHE_FLUSH(); + agp_bridge.tlb_flush(mem); + + for (i = pg_start; i < (mem->page_count + pg_start); i++) { + addr = (i * PAGE_SIZE) + agp_bridge.gart_bus_addr; + cur_gatt = SVRWRKS_GET_GATT(addr); + cur_gatt[GET_GATT_OFF(addr)] = + (unsigned long) agp_bridge.scratch_page; + } + + agp_bridge.tlb_flush(mem); + return 0; +} + +static struct gatt_mask serverworks_masks[] = +{ + {mask: 0x00000001, type: 0} +}; + +static struct aper_size_info_lvl2 serverworks_sizes[7] = +{ + {2048, 524288, 0x80000000}, + {1024, 262144, 0xc0000000}, + {512, 131072, 0xe0000000}, + {256, 65536, 0xf0000000}, + {128, 32768, 0xf8000000}, + {64, 16384, 0xfc000000}, + {32, 8192, 0xfe000000} +}; + +static void serverworks_agp_enable(u32 mode) +{ + struct pci_dev *device = NULL; + u32 command, scratch, cap_id; + u8 cap_ptr; + + pci_read_config_dword(serverworks_private.svrwrks_dev, + agp_bridge.capndx + 4, + &command); + + /* + * PASS1: go throu all devices that claim to be + * AGP devices and collect their data. + */ + + + pci_for_each_dev(device) { + cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); + if (cap_ptr != 0x00) { + do { + pci_read_config_dword(device, + cap_ptr, &cap_id); + + if ((cap_id & 0xff) != 0x02) + cap_ptr = (cap_id >> 8) & 0xff; + } + while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00)); + } + if (cap_ptr != 0x00) { + /* + * Ok, here we have a AGP device. Disable impossible + * settings, and adjust the readqueue to the minimum. + */ + + pci_read_config_dword(device, cap_ptr + 4, &scratch); + + /* adjust RQ depth */ + command = + ((command & ~0xff000000) | + min_t(u32, (mode & 0xff000000), + min_t(u32, (command & 0xff000000), + (scratch & 0xff000000)))); + + /* disable SBA if it's not supported */ + if (!((command & 0x00000200) && + (scratch & 0x00000200) && + (mode & 0x00000200))) + command &= ~0x00000200; + + /* disable FW */ + command &= ~0x00000010; + + command &= ~0x00000008; + + if (!((command & 4) && + (scratch & 4) && + (mode & 4))) + command &= ~0x00000004; + + if (!((command & 2) && + (scratch & 2) && + (mode & 2))) + command &= ~0x00000002; + + if (!((command & 1) && + (scratch & 1) && + (mode & 1))) + command &= ~0x00000001; + } + } + /* + * PASS2: Figure out the 4X/2X/1X setting and enable the + * target (our motherboard chipset). + */ + + if (command & 4) { + command &= ~3; /* 4X */ + } + if (command & 2) { + command &= ~5; /* 2X */ + } + if (command & 1) { + command &= ~6; /* 1X */ + } + command |= 0x00000100; + + pci_write_config_dword(serverworks_private.svrwrks_dev, + agp_bridge.capndx + 8, + command); + + /* + * PASS3: Go throu all AGP devices and update the + * command registers. + */ + + pci_for_each_dev(device) { + cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); + if (cap_ptr != 0x00) + pci_write_config_dword(device, cap_ptr + 8, command); + } +} + +int __init serverworks_setup (struct pci_dev *pdev) +{ + u32 temp; + u32 temp2; + + serverworks_private.svrwrks_dev = pdev; + + agp_bridge.masks = serverworks_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) serverworks_sizes; + agp_bridge.size_type = LVL2_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = (void *) &serverworks_private; + agp_bridge.needs_scratch_page = TRUE; + agp_bridge.configure = serverworks_configure; + agp_bridge.fetch_size = serverworks_fetch_size; + agp_bridge.cleanup = serverworks_cleanup; + agp_bridge.tlb_flush = serverworks_tlbflush; + agp_bridge.mask_memory = serverworks_mask_memory; + agp_bridge.agp_enable = serverworks_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = serverworks_create_gatt_table; + agp_bridge.free_gatt_table = serverworks_free_gatt_table; + agp_bridge.insert_memory = serverworks_insert_memory; + agp_bridge.remove_memory = serverworks_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + pci_read_config_dword(agp_bridge.dev, + SVWRKS_APSIZE, + &temp); + + serverworks_private.gart_addr_ofs = 0x10; + + if(temp & PCI_BASE_ADDRESS_MEM_TYPE_64) { + pci_read_config_dword(agp_bridge.dev, + SVWRKS_APSIZE + 4, + &temp2); + if(temp2 != 0) { + printk("Detected 64 bit aperture address, but top " + "bits are not zero. Disabling agp\n"); + return -ENODEV; + } + serverworks_private.mm_addr_ofs = 0x18; + } else { + serverworks_private.mm_addr_ofs = 0x14; + } + + pci_read_config_dword(agp_bridge.dev, + serverworks_private.mm_addr_ofs, + &temp); + if(temp & PCI_BASE_ADDRESS_MEM_TYPE_64) { + pci_read_config_dword(agp_bridge.dev, + serverworks_private.mm_addr_ofs + 4, + &temp2); + if(temp2 != 0) { + printk("Detected 64 bit MMIO address, but top " + "bits are not zero. Disabling agp\n"); + return -ENODEV; + } + } + + return 0; +} + diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c new file mode 100644 index 000000000000..5facf9f64062 --- /dev/null +++ b/drivers/char/agp/via-agp.c @@ -0,0 +1,151 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * TODO: + * - Allocate more than order 0 pages to avoid too much linear map splitting. + */ +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/agp_backend.h> +#include "agp.h" + + +static int via_fetch_size(void) +{ + int i; + u8 temp; + struct aper_size_info_8 *values; + + values = A_SIZE_8(agp_bridge.aperture_sizes); + pci_read_config_byte(agp_bridge.dev, VIA_APSIZE, &temp); + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if (temp == values[i].size_value) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + + return 0; +} + +static int via_configure(void) +{ + u32 temp; + struct aper_size_info_8 *current_size; + + current_size = A_SIZE_8(agp_bridge.current_size); + /* aperture size */ + pci_write_config_byte(agp_bridge.dev, VIA_APSIZE, + current_size->size_value); + /* address to map too */ + pci_read_config_dword(agp_bridge.dev, VIA_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* GART control register */ + pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000000f); + + /* attbase - aperture GATT base */ + pci_write_config_dword(agp_bridge.dev, VIA_ATTBASE, + (agp_bridge.gatt_bus_addr & 0xfffff000) | 3); + return 0; +} + +static void via_cleanup(void) +{ + struct aper_size_info_8 *previous_size; + + previous_size = A_SIZE_8(agp_bridge.previous_size); + pci_write_config_byte(agp_bridge.dev, VIA_APSIZE, + previous_size->size_value); + /* Do not disable by writing 0 to VIA_ATTBASE, it screws things up + * during reinitialization. + */ +} + +static void via_tlbflush(agp_memory * mem) +{ + pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000008f); + pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000000f); +} + +static unsigned long via_mask_memory(unsigned long addr, int type) +{ + /* Memory type is ignored */ + + return addr | agp_bridge.masks[0].mask; +} + +static struct aper_size_info_8 via_generic_sizes[7] = +{ + {256, 65536, 6, 0}, + {128, 32768, 5, 128}, + {64, 16384, 4, 192}, + {32, 8192, 3, 224}, + {16, 4096, 2, 240}, + {8, 2048, 1, 248}, + {4, 1024, 0, 252} +}; + +static struct gatt_mask via_generic_masks[] = +{ + {mask: 0x00000000, type: 0} +}; + +int __init via_generic_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = via_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) via_generic_sizes; + agp_bridge.size_type = U8_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = via_configure; + agp_bridge.fetch_size = via_fetch_size; + agp_bridge.cleanup = via_cleanup; + agp_bridge.tlb_flush = via_tlbflush; + agp_bridge.mask_memory = via_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; + + (void) pdev; /* unused */ +} diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index d748d499da60..7cf8fb5cfcb5 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -807,7 +807,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old) I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || I_PARMRK(tty)) { - cli(); + __cli(); // FIXME: is this safe? memset(tty->process_char_map, 0, 256/8); if (I_IGNCR(tty) || I_ICRNL(tty)) @@ -843,7 +843,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old) set_bit(SUSP_CHAR(tty), tty->process_char_map); } clear_bit(__DISABLED_CHAR, tty->process_char_map); - sti(); + __sti(); // FIXME: is this safe? tty->raw = 0; tty->real_raw = 0; } else { diff --git a/drivers/char/pcmcia/Config.in b/drivers/char/pcmcia/Config.in index 5905bd307d97..e44fa2a25ec8 100644 --- a/drivers/char/pcmcia/Config.in +++ b/drivers/char/pcmcia/Config.in @@ -5,7 +5,6 @@ mainmenu_option next_comment comment 'PCMCIA character devices' -dep_tristate 'PCMCIA serial device support' CONFIG_PCMCIA_SERIAL_CS $CONFIG_PCMCIA $CONFIG_SERIAL dep_tristate 'SyncLink PC Card support' CONFIG_SYNCLINK_CS $CONFIG_PCMCIA endmenu diff --git a/drivers/char/pcmcia/Makefile b/drivers/char/pcmcia/Makefile index 0b499b55814b..a1db3f706121 100644 --- a/drivers/char/pcmcia/Makefile +++ b/drivers/char/pcmcia/Makefile @@ -4,7 +4,6 @@ # Makefile for the Linux PCMCIA char device drivers. # -obj-$(CONFIG_PCMCIA_SERIAL_CS) += serial_cs.o obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o include $(TOPDIR)/Rules.make diff --git a/drivers/char/pcmcia/serial_cs.c b/drivers/char/pcmcia/serial_cs.c deleted file mode 100644 index 892f2c832a9e..000000000000 --- a/drivers/char/pcmcia/serial_cs.c +++ /dev/null @@ -1,667 +0,0 @@ -/*====================================================================== - - A driver for PCMCIA serial devices - - serial_cs.c 1.123 2000/08/24 18:46:38 - - The contents of this file are subject to the Mozilla Public - License Version 1.1 (the "License"); you may not use this file - except in compliance with the License. You may obtain a copy of - the License at http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS - IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - implied. See the License for the specific language governing - rights and limitations under the License. - - The initial developer of the original code is David A. Hinds - <dahinds@users.sourceforge.net>. Portions created by David A. Hinds - are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - - Alternatively, the contents of this file may be used under the - terms of the GNU General Public License version 2 (the "GPL"), in which - case the provisions of the GPL are applicable instead of the - above. If you wish to allow the use of your version of this file - only under the terms of the GPL and not to allow others to use - your version of this file under the MPL, indicate your decision - by deleting the provisions above and replace them with the notice - and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this - file under either the MPL or the GPL. - -======================================================================*/ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/sched.h> -#include <linux/ptrace.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/tty.h> -#include <linux/serial.h> -#include <linux/major.h> -#include <asm/io.h> -#include <asm/system.h> - -#include <pcmcia/version.h> -#include <pcmcia/cs_types.h> -#include <pcmcia/cs.h> -#include <pcmcia/cistpl.h> -#include <pcmcia/ciscode.h> -#include <pcmcia/ds.h> -#include <pcmcia/cisreg.h> - -#ifdef PCMCIA_DEBUG -static int pc_debug = PCMCIA_DEBUG; -MODULE_PARM(pc_debug, "i"); -#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) -static char *version = -"serial_cs.c 1.123 2000/08/24 18:46:38 (David Hinds)"; -#else -#define DEBUG(n, args...) -#endif - -/*====================================================================*/ - -/* Parameters that can be set with 'insmod' */ - -/* Bit map of interrupts to choose from */ -static u_int irq_mask = 0xdeb8; -static int irq_list[4] = { -1 }; - -/* Enable the speaker? */ -static int do_sound = 1; - -MODULE_PARM(irq_mask, "i"); -MODULE_PARM(irq_list, "1-4i"); -MODULE_PARM(do_sound, "i"); - -/*====================================================================*/ - -/* Table of multi-port card ID's */ - -typedef struct { - u_short manfid; - u_short prodid; - int multi; /* 1 = multifunction, > 1 = # ports */ -} multi_id_t; - -static multi_id_t multi_id[] = { - { MANFID_OMEGA, PRODID_OMEGA_QSP_100, 4 }, - { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232, 2 }, - { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 }, - { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232, 4 }, - { MANFID_SOCKET, PRODID_SOCKET_DUAL_RS232, 2 }, - { MANFID_INTEL, PRODID_INTEL_DUAL_RS232, 2 }, - { MANFID_NATINST, PRODID_NATINST_QUAD_RS232, 4 } -}; -#define MULTI_COUNT (sizeof(multi_id)/sizeof(multi_id_t)) - -typedef struct serial_info_t { - dev_link_t link; - int ndev; - int multi; - int slave; - int manfid; - dev_node_t node[4]; - int line[4]; -} serial_info_t; - -static void serial_config(dev_link_t *link); -static void serial_release(u_long arg); -static int serial_event(event_t event, int priority, - event_callback_args_t *args); - -static dev_info_t dev_info = "serial_cs"; - -static dev_link_t *serial_attach(void); -static void serial_detach(dev_link_t *); - -static dev_link_t *dev_list = NULL; - -/*====================================================================*/ - -static void cs_error(client_handle_t handle, int func, int ret) -{ - error_info_t err = { func, ret }; - CardServices(ReportError, handle, &err); -} - -/*====================================================================== - - serial_attach() creates an "instance" of the driver, allocating - local data structures for one device. The device is registered - with Card Services. - -======================================================================*/ - -static dev_link_t *serial_attach(void) -{ - serial_info_t *info; - client_reg_t client_reg; - dev_link_t *link; - int i, ret; - - DEBUG(0, "serial_attach()\n"); - - /* Create new serial device */ - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) return NULL; - memset(info, 0, sizeof(*info)); - link = &info->link; link->priv = info; - - link->release.function = &serial_release; - link->release.data = (u_long)link; - link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; - link->io.NumPorts1 = 8; - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; - if (irq_list[0] == -1) - link->irq.IRQInfo2 = irq_mask; - else - for (i = 0; i < 4; i++) - link->irq.IRQInfo2 |= 1 << irq_list[i]; - link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; - if (do_sound) { - link->conf.Attributes |= CONF_ENABLE_SPKR; - link->conf.Status = CCSR_AUDIO_ENA; - } - link->conf.IntType = INT_MEMORY_AND_IO; - - /* Register with Card Services */ - link->next = dev_list; - dev_list = link; - client_reg.dev_info = &dev_info; - client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; - client_reg.EventMask = - CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | - CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | - CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; - client_reg.event_handler = &serial_event; - client_reg.Version = 0x0210; - client_reg.event_callback_args.client_data = link; - ret = CardServices(RegisterClient, &link->handle, &client_reg); - if (ret != CS_SUCCESS) { - cs_error(link->handle, RegisterClient, ret); - serial_detach(link); - return NULL; - } - - return link; -} /* serial_attach */ - -/*====================================================================== - - This deletes a driver "instance". The device is de-registered - with Card Services. If it has been released, all local data - structures are freed. Otherwise, the structures will be freed - when the device is released. - -======================================================================*/ - -static void serial_detach(dev_link_t *link) -{ - serial_info_t *info = link->priv; - dev_link_t **linkp; - int ret; - - DEBUG(0, "serial_detach(0x%p)\n", link); - - /* Locate device structure */ - for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) - if (*linkp == link) break; - if (*linkp == NULL) - return; - - del_timer(&link->release); - if (link->state & DEV_CONFIG) - serial_release((u_long)link); - - if (link->handle) { - ret = CardServices(DeregisterClient, link->handle); - if (ret != CS_SUCCESS) - cs_error(link->handle, DeregisterClient, ret); - } - - /* Unlink device structure, free bits */ - *linkp = link->next; - kfree(info); - -} /* serial_detach */ - -/*====================================================================*/ - -static int setup_serial(serial_info_t *info, ioaddr_t port, int irq) -{ - struct serial_struct serial; - int line; - - memset(&serial, 0, sizeof(serial)); - serial.port = port; - serial.irq = irq; - serial.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ; - line = register_serial(&serial); - if (line < 0) { - printk(KERN_NOTICE "serial_cs: register_serial() at 0x%04lx," - " irq %d failed\n", (u_long)serial.port, serial.irq); - return -1; - } - - info->line[info->ndev] = line; - sprintf(info->node[info->ndev].dev_name, "ttyS%d", line); - info->node[info->ndev].major = TTY_MAJOR; - info->node[info->ndev].minor = 0x40+line; - if (info->ndev > 0) - info->node[info->ndev-1].next = &info->node[info->ndev]; - info->ndev++; - - return 0; -} - -/*====================================================================*/ - -static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, - cisparse_t *parse) -{ - int i; - i = CardServices(fn, handle, tuple); - if (i != CS_SUCCESS) return CS_NO_MORE_ITEMS; - i = CardServices(GetTupleData, handle, tuple); - if (i != CS_SUCCESS) return i; - return CardServices(ParseTuple, handle, tuple, parse); -} - -#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) -#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c) - -/*====================================================================*/ - -static int simple_config(dev_link_t *link) -{ - static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; - client_handle_t handle = link->handle; - serial_info_t *info = link->priv; - tuple_t tuple; - u_char buf[256]; - cisparse_t parse; - cistpl_cftable_entry_t *cf = &parse.cftable_entry; - config_info_t config; - int i, j, try; - - /* If the card is already configured, look up the port and irq */ - i = CardServices(GetConfigurationInfo, handle, &config); - if ((i == CS_SUCCESS) && - (config.Attributes & CONF_VALID_CLIENT)) { - ioaddr_t port = 0; - if ((config.BasePort2 != 0) && (config.NumPorts2 == 8)) { - port = config.BasePort2; - info->slave = 1; - } else if ((info->manfid == MANFID_OSITECH) && - (config.NumPorts1 == 0x40)) { - port = config.BasePort1 + 0x28; - info->slave = 1; - } - if (info->slave) - return setup_serial(info, port, config.AssignedIRQ); - } - link->conf.Vcc = config.Vcc; - - /* First pass: look for a config entry that looks normal. */ - tuple.TupleData = (cisdata_t *)buf; - tuple.TupleOffset = 0; tuple.TupleDataMax = 255; - tuple.Attributes = 0; - tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - /* Two tries: without IO aliases, then with aliases */ - for (try = 0; try < 2; try++) { - i = first_tuple(handle, &tuple, &parse); - while (i != CS_NO_MORE_ITEMS) { - if (i != CS_SUCCESS) goto next_entry; - if (cf->vpp1.present & (1<<CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = - cf->vpp1.param[CISTPL_POWER_VNOM]/10000; - if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && - (cf->io.win[0].base != 0)) { - link->conf.ConfigIndex = cf->index; - link->io.BasePort1 = cf->io.win[0].base; - link->io.IOAddrLines = (try == 0) ? - 16 : cf->io.flags & CISTPL_IO_LINES_MASK; - i = CardServices(RequestIO, link->handle, &link->io); - if (i == CS_SUCCESS) goto found_port; - } - next_entry: - i = next_tuple(handle, &tuple, &parse); - } - } - - /* Second pass: try to find an entry that isn't picky about - its base address, then try to grab any standard serial port - address, and finally try to get any free port. */ - i = first_tuple(handle, &tuple, &parse); - while (i != CS_NO_MORE_ITEMS) { - if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && - ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { - link->conf.ConfigIndex = cf->index; - for (j = 0; j < 5; j++) { - link->io.BasePort1 = base[j]; - link->io.IOAddrLines = base[j] ? 16 : 3; - i = CardServices(RequestIO, link->handle, - &link->io); - if (i == CS_SUCCESS) goto found_port; - } - } - i = next_tuple(handle, &tuple, &parse); - } - -found_port: - if (i != CS_SUCCESS) { - printk(KERN_NOTICE "serial_cs: no usable port range found, giving up\n"); - cs_error(link->handle, RequestIO, i); - return -1; - } - - i = CardServices(RequestIRQ, link->handle, &link->irq); - if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIRQ, i); - link->irq.AssignedIRQ = 0; - } - if (info->multi && (info->manfid == MANFID_3COM)) - link->conf.ConfigIndex &= ~(0x08); - i = CardServices(RequestConfiguration, link->handle, &link->conf); - if (i != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, i); - return -1; - } - - return setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); -} - -static int multi_config(dev_link_t *link) -{ - client_handle_t handle = link->handle; - serial_info_t *info = link->priv; - tuple_t tuple; - u_char buf[256]; - cisparse_t parse; - cistpl_cftable_entry_t *cf = &parse.cftable_entry; - int i, base2 = 0; - - tuple.TupleData = (cisdata_t *)buf; - tuple.TupleOffset = 0; tuple.TupleDataMax = 255; - tuple.Attributes = 0; - tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - - /* First, look for a generic full-sized window */ - link->io.NumPorts1 = info->multi * 8; - i = first_tuple(handle, &tuple, &parse); - while (i != CS_NO_MORE_ITEMS) { - /* The quad port cards have bad CIS's, so just look for a - window larger than 8 ports and assume it will be right */ - if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && - (cf->io.win[0].len > 8)) { - link->conf.ConfigIndex = cf->index; - link->io.BasePort1 = cf->io.win[0].base; - link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; - i = CardServices(RequestIO, link->handle, &link->io); - base2 = link->io.BasePort1 + 8; - if (i == CS_SUCCESS) break; - } - i = next_tuple(handle, &tuple, &parse); - } - - /* If that didn't work, look for two windows */ - if (i != CS_SUCCESS) { - link->io.NumPorts1 = link->io.NumPorts2 = 8; - info->multi = 2; - i = first_tuple(handle, &tuple, &parse); - while (i != CS_NO_MORE_ITEMS) { - if ((i == CS_SUCCESS) && (cf->io.nwin == 2)) { - link->conf.ConfigIndex = cf->index; - link->io.BasePort1 = cf->io.win[0].base; - link->io.BasePort2 = cf->io.win[1].base; - link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; - i = CardServices(RequestIO, link->handle, &link->io); - base2 = link->io.BasePort2; - if (i == CS_SUCCESS) break; - } - i = next_tuple(handle, &tuple, &parse); - } - } - - if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIO, i); - return -1; - } - - i = CardServices(RequestIRQ, link->handle, &link->irq); - if (i != CS_SUCCESS) { - printk(KERN_NOTICE "serial_cs: no usable port range found, giving up\n"); - cs_error(link->handle, RequestIRQ, i); - link->irq.AssignedIRQ = 0; - } - /* Socket Dual IO: this enables irq's for second port */ - if (info->multi && (info->manfid == MANFID_SOCKET)) { - link->conf.Present |= PRESENT_EXT_STATUS; - link->conf.ExtStatus = ESR_REQ_ATTN_ENA; - } - i = CardServices(RequestConfiguration, link->handle, &link->conf); - if (i != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, i); - return -1; - } - - setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); - /* The Nokia cards are not really multiport cards */ - if (info->manfid == MANFID_NOKIA) - return 0; - for (i = 0; i < info->multi-1; i++) - setup_serial(info, base2+(8*i), link->irq.AssignedIRQ); - - return 0; -} - -/*====================================================================== - - serial_config() is scheduled to run after a CARD_INSERTION event - is received, to configure the PCMCIA socket, and to make the - serial device available to the system. - -======================================================================*/ - -#define CS_CHECK(fn, args...) \ -while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed - -void serial_config(dev_link_t *link) -{ - client_handle_t handle = link->handle; - serial_info_t *info = link->priv; - tuple_t tuple; - u_short buf[128]; - cisparse_t parse; - cistpl_cftable_entry_t *cf = &parse.cftable_entry; - int i, last_ret, last_fn; - - DEBUG(0, "serial_config(0x%p)\n", link); - - tuple.TupleData = (cisdata_t *)buf; - tuple.TupleOffset = 0; tuple.TupleDataMax = 255; - tuple.Attributes = 0; - /* Get configuration register information */ - tuple.DesiredTuple = CISTPL_CONFIG; - last_ret = first_tuple(handle, &tuple, &parse); - if (last_ret != CS_SUCCESS) { - last_fn = ParseTuple; - goto cs_failed; - } - link->conf.ConfigBase = parse.config.base; - link->conf.Present = parse.config.rmask[0]; - - /* Configure card */ - link->state |= DEV_CONFIG; - - /* Is this a compliant multifunction card? */ - tuple.DesiredTuple = CISTPL_LONGLINK_MFC; - tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK; - info->multi = (first_tuple(handle, &tuple, &parse) == CS_SUCCESS); - - /* Is this a multiport card? */ - tuple.DesiredTuple = CISTPL_MANFID; - if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) { - info->manfid = le16_to_cpu(buf[0]); - for (i = 0; i < MULTI_COUNT; i++) - if ((info->manfid == multi_id[i].manfid) && - (le16_to_cpu(buf[1]) == multi_id[i].prodid)) - break; - if (i < MULTI_COUNT) - info->multi = multi_id[i].multi; - } - - /* Another check for dual-serial cards: look for either serial or - multifunction cards that ask for appropriate IO port ranges */ - tuple.DesiredTuple = CISTPL_FUNCID; - if ((info->multi == 0) && - ((first_tuple(handle, &tuple, &parse) != CS_SUCCESS) || - (parse.funcid.func == CISTPL_FUNCID_MULTI) || - (parse.funcid.func == CISTPL_FUNCID_SERIAL))) { - tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) { - if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0)) - info->multi = cf->io.win[0].len >> 3; - if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) && - (cf->io.win[1].len == 8)) - info->multi = 2; - } - } - - if (info->multi > 1) - multi_config(link); - else - simple_config(link); - - if (info->ndev == 0) - goto failed; - - if (info->manfid == MANFID_IBM) { - conf_reg_t reg = { 0, CS_READ, 0x800, 0 }; - CS_CHECK(AccessConfigurationRegister, link->handle, ®); - reg.Action = CS_WRITE; - reg.Value = reg.Value | 1; - CS_CHECK(AccessConfigurationRegister, link->handle, ®); - } - - link->dev = &info->node[0]; - link->state &= ~DEV_CONFIG_PENDING; - return; - -cs_failed: - cs_error(link->handle, last_fn, last_ret); -failed: - serial_release((u_long)link); - -} /* serial_config */ - -/*====================================================================== - - After a card is removed, serial_release() will unregister the net - device, and release the PCMCIA configuration. - -======================================================================*/ - -void serial_release(u_long arg) -{ - dev_link_t *link = (dev_link_t *)arg; - serial_info_t *info = link->priv; - int i; - - DEBUG(0, "serial_release(0x%p)\n", link); - - for (i = 0; i < info->ndev; i++) { - unregister_serial(info->line[i]); - } - link->dev = NULL; - - if (!info->slave) { - CardServices(ReleaseConfiguration, link->handle); - CardServices(ReleaseIO, link->handle, &link->io); - CardServices(ReleaseIRQ, link->handle, &link->irq); - } - - link->state &= ~DEV_CONFIG; - -} /* serial_release */ - -/*====================================================================== - - The card status event handler. Mostly, this schedules other - stuff to run after an event is received. A CARD_REMOVAL event - also sets some flags to discourage the serial drivers from - talking to the ports. - -======================================================================*/ - -static int serial_event(event_t event, int priority, - event_callback_args_t *args) -{ - dev_link_t *link = args->client_data; - serial_info_t *info = link->priv; - - DEBUG(1, "serial_event(0x%06x)\n", event); - - switch (event) { - case CS_EVENT_CARD_REMOVAL: - link->state &= ~DEV_PRESENT; - if (link->state & DEV_CONFIG) - mod_timer(&link->release, jiffies + HZ/20); - break; - case CS_EVENT_CARD_INSERTION: - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - serial_config(link); - break; - case CS_EVENT_PM_SUSPEND: - link->state |= DEV_SUSPEND; - /* Fall through... */ - case CS_EVENT_RESET_PHYSICAL: - if ((link->state & DEV_CONFIG) && !info->slave) - CardServices(ReleaseConfiguration, link->handle); - break; - case CS_EVENT_PM_RESUME: - link->state &= ~DEV_SUSPEND; - /* Fall through... */ - case CS_EVENT_CARD_RESET: - if (DEV_OK(link) && !info->slave) - CardServices(RequestConfiguration, link->handle, &link->conf); - break; - } - return 0; -} /* serial_event */ - -/*====================================================================*/ - -static int __init init_serial_cs(void) -{ - servinfo_t serv; - DEBUG(0, "%s\n", version); - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "serial_cs: Card Services release " - "does not match!\n"); - return -1; - } - register_pccard_driver(&dev_info, &serial_attach, &serial_detach); - return 0; -} - -static void __exit exit_serial_cs(void) -{ - DEBUG(0, "serial_cs: unloading\n"); - unregister_pccard_driver(&dev_info); - while (dev_list != NULL) - serial_detach(dev_list); -} - -module_init(init_serial_cs); -module_exit(exit_serial_cs); - -MODULE_LICENSE("GPL"); diff --git a/drivers/char/serial.c b/drivers/char/serial.c deleted file mode 100644 index c3cd00f3caea..000000000000 --- a/drivers/char/serial.c +++ /dev/null @@ -1,6003 +0,0 @@ -/* - * linux/drivers/char/serial.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, - * 1998, 1999 Theodore Ts'o - * - * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now - * much more extensible to support other serial cards based on the - * 16450/16550A UART's. Added support for the AST FourPort and the - * Accent Async board. - * - * set_serial_info fixed to set the flags, custom divisor, and uart - * type fields. Fix suggested by Michael K. Johnson 12/12/92. - * - * 11/95: TIOCMIWAIT, TIOCGICOUNT by Angelo Haritsis <ah@doc.ic.ac.uk> - * - * 03/96: Modularised by Angelo Haritsis <ah@doc.ic.ac.uk> - * - * rs_set_termios fixed to look also for changes of the input - * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK. - * Bernd Anhäupl 05/17/96. - * - * 1/97: Extended dumb serial ports are a config option now. - * Saves 4k. Michael A. Griffith <grif@acm.org> - * - * 8/97: Fix bug in rs_set_termios with RTS - * Stanislav V. Voronyi <stas@uanet.kharkov.ua> - * - * 3/98: Change the IRQ detection, use of probe_irq_o*(), - * suppress TIOCSERGWILD and TIOCSERSWILD - * Etienne Lorrain <etienne.lorrain@ibm.net> - * - * 4/98: Added changes to support the ARM architecture proposed by - * Russell King - * - * 5/99: Updated to include support for the XR16C850 and ST16C654 - * uarts. Stuart MacDonald <stuartm@connecttech.com> - * - * 8/99: Generalized PCI support added. Theodore Ts'o - * - * 3/00: Rid circular buffer of redundant xmit_cnt. Fix a - * few races on freeing buffers too. - * Alan Modra <alan@linuxcare.com> - * - * 5/00: Support for the RSA-DV II/S card added. - * Kiyokazu SUTO <suto@ks-and-ks.ne.jp> - * - * 6/00: Remove old-style timer, use timer_list - * Andrew Morton <andrewm@uow.edu.au> - * - * 7/00: Support Timedia/Sunix/Exsys PCI cards - * - * 7/00: fix some returns on failure not using MOD_DEC_USE_COUNT. - * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - * - * 10/00: add in optional software flow control for serial console. - * Kanoj Sarcar <kanoj@sgi.com> (Modified by Theodore Ts'o) - * - */ - -static char *serial_version = "5.05c"; -static char *serial_revdate = "2001-07-08"; - -/* - * Serial driver configuration section. Here are the various options: - * - * CONFIG_HUB6 - * Enables support for the venerable Bell Technologies - * HUB6 card. - * - * CONFIG_SERIAL_MANY_PORTS - * Enables support for ports beyond the standard, stupid - * COM 1/2/3/4. - * - * CONFIG_SERIAL_MULTIPORT - * Enables support for special multiport board support. - * - * CONFIG_SERIAL_SHARE_IRQ - * Enables support for multiple serial ports on one IRQ - * - * CONFIG_SERIAL_DETECT_IRQ - * Enable the autodetection of IRQ on standart ports - * - * SERIAL_PARANOIA_CHECK - * Check the magic number for the async_structure where - * ever possible. - * - * CONFIG_SERIAL_ACPI - * Enable support for serial console port and serial - * debug port as defined by the SPCR and DBGP tables in - * ACPI 2.0. - */ - -#include <linux/config.h> -#include <linux/version.h> - -#undef SERIAL_PARANOIA_CHECK -#define CONFIG_SERIAL_NOPAUSE_IO -#define SERIAL_DO_RESTART - -#if 0 -/* These defines are normally controlled by the autoconf.h */ -#define CONFIG_SERIAL_MANY_PORTS -#define CONFIG_SERIAL_SHARE_IRQ -#define CONFIG_SERIAL_DETECT_IRQ -#define CONFIG_SERIAL_MULTIPORT -#define CONFIG_HUB6 -#endif - -#ifdef CONFIG_PCI -#define ENABLE_SERIAL_PCI -#ifndef CONFIG_SERIAL_SHARE_IRQ -#define CONFIG_SERIAL_SHARE_IRQ -#endif -#ifndef CONFIG_SERIAL_MANY_PORTS -#define CONFIG_SERIAL_MANY_PORTS -#endif -#endif - -#ifdef CONFIG_SERIAL_ACPI -#define ENABLE_SERIAL_ACPI -#endif - - -/* Set of debugging defines */ - -#undef SERIAL_DEBUG_INTR -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_FLOW -#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT -#undef SERIAL_DEBUG_PCI -#undef SERIAL_DEBUG_AUTOCONF - -/* Sanity checks */ - -#ifdef CONFIG_SERIAL_MULTIPORT -#ifndef CONFIG_SERIAL_SHARE_IRQ -#define CONFIG_SERIAL_SHARE_IRQ -#endif -#endif - -#ifdef CONFIG_HUB6 -#ifndef CONFIG_SERIAL_MANY_PORTS -#define CONFIG_SERIAL_MANY_PORTS -#endif -#ifndef CONFIG_SERIAL_SHARE_IRQ -#define CONFIG_SERIAL_SHARE_IRQ -#endif -#endif - -#ifdef MODULE -#undef CONFIG_SERIAL_CONSOLE -#endif - -#define CONFIG_SERIAL_RSA - -#define RS_STROBE_TIME (10*HZ) -#define RS_ISR_PASS_LIMIT 256 - -#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) -#define SERIAL_INLINE -#endif - -/* - * End of serial driver configuration section. - */ - -#include <linux/module.h> - -#include <linux/types.h> -#ifdef LOCAL_HEADERS -#include "serial_local.h" -#else -#include <linux/serial.h> -#include <linux/serialP.h> -#include <linux/serial_reg.h> -#include <asm/serial.h> -#define LOCAL_VERSTRING "" -#endif - -#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/ptrace.h> -#include <linux/ioport.h> -#include <linux/mm.h> -#include <linux/slab.h> -#if (LINUX_VERSION_CODE >= 131343) -#include <linux/init.h> -#endif -#if (LINUX_VERSION_CODE >= 131336) -#include <asm/uaccess.h> -#endif -#include <linux/delay.h> -#ifdef CONFIG_SERIAL_CONSOLE -#include <linux/console.h> -#endif -#ifdef ENABLE_SERIAL_PCI -#include <linux/pci.h> -#endif - -#include <linux/isapnp.h> -#ifdef __ISAPNP__ -#ifndef ENABLE_SERIAL_PNP -#define ENABLE_SERIAL_PNP -#endif -#endif - -#ifdef CONFIG_MAGIC_SYSRQ -#include <linux/sysrq.h> -#endif - -/* - * All of the compatibilty code so we can compile serial.c against - * older kernels is hidden in serial_compat.h - */ -#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */ -#include "serial_compat.h" -#endif - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/bitops.h> - -#ifdef CONFIG_MAC_SERIAL -#define SERIAL_DEV_OFFSET 2 -#else -#define SERIAL_DEV_OFFSET 0 -#endif - -#ifdef SERIAL_INLINE -#define _INLINE_ inline -#else -#define _INLINE_ -#endif - -static char *serial_name = "Serial driver"; - -static DECLARE_TASK_QUEUE(tq_serial); - -static struct tty_driver serial_driver, callout_driver; -static int serial_refcount; - -static struct timer_list serial_timer; - -/* serial subtype definitions */ -#ifndef SERIAL_TYPE_NORMAL -#define SERIAL_TYPE_NORMAL 1 -#define SERIAL_TYPE_CALLOUT 2 -#endif - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - -/* - * IRQ_timeout - How long the timeout should be for each IRQ - * should be after the IRQ has been active. - */ - -static struct async_struct *IRQ_ports[NR_IRQS]; -#ifdef CONFIG_SERIAL_MULTIPORT -static struct rs_multiport_struct rs_multiport[NR_IRQS]; -#endif -static int IRQ_timeout[NR_IRQS]; -#ifdef CONFIG_SERIAL_CONSOLE -static struct console sercons; -static int lsr_break_flag; -#endif -#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -static unsigned long break_pressed; /* break, really ... */ -#endif - -static unsigned detect_uart_irq (struct serial_state * state); -static void autoconfig(struct serial_state * state); -static void change_speed(struct async_struct *info, struct termios *old); -static void rs_wait_until_sent(struct tty_struct *tty, int timeout); - -/* - * 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 }, /* usurped by cyclades.c */ - { "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}, - { "Startech", 1, 0}, /* usurped by cyclades.c */ - { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO}, - { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | - UART_STARTECH }, - { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | - UART_STARTECH }, - { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, - { 0, 0} -}; - -#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) - -#define PORT_RSA_MAX 4 -static int probe_rsa[PORT_RSA_MAX]; -static int force_rsa[PORT_RSA_MAX]; - -MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); -MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); -MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); -MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA"); -#endif /* CONFIG_SERIAL_RSA */ - -static struct serial_state rs_table[RS_TABLE_SIZE] = { - SERIAL_PORT_DFNS /* Defined in serial.h */ -}; - -#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) - -#if (defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)) -#define NR_PCI_BOARDS 8 - -static struct pci_board_inst serial_pci_board[NR_PCI_BOARDS]; - -#ifndef IS_PCI_REGION_IOPORT -#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ - IORESOURCE_IO) -#endif -#ifndef IS_PCI_REGION_IOMEM -#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ - IORESOURCE_MEM) -#endif -#ifndef PCI_IRQ_RESOURCE -#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) -#endif -#ifndef pci_get_subvendor -#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) -#define pci_get_subdevice(dev) ((dev)->subsystem_device) -#endif -#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */ - -#ifndef PREPARE_FUNC -#define PREPARE_FUNC(dev) (dev->prepare) -#define ACTIVATE_FUNC(dev) (dev->activate) -#define DEACTIVATE_FUNC(dev) (dev->deactivate) -#endif - -#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) - -static struct tty_struct *serial_table[NR_PORTS]; -static struct termios *serial_termios[NR_PORTS]; -static struct termios *serial_termios_locked[NR_PORTS]; - - -#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) -#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ - kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s) -#else -#define DBG_CNT(s) -#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; -#ifdef DECLARE_MUTEX -static DECLARE_MUTEX(tmp_buf_sem); -#else -static struct semaphore tmp_buf_sem = MUTEX; -#endif - - -static inline int serial_paranoia_check(struct async_struct *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 async_struct 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; -} - -static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset) -{ - switch (info->io_type) { -#ifdef CONFIG_HUB6 - case SERIAL_IO_HUB6: - outb(info->hub6 - 1 + offset, info->port); - return inb(info->port+1); -#endif - case SERIAL_IO_MEM: - return readb((unsigned long) info->iomem_base + - (offset<<info->iomem_reg_shift)); -#ifdef CONFIG_SERIAL_GSC - case SERIAL_IO_GSC: - return gsc_readb(info->iomem_base + offset); -#endif - default: - return inb(info->port + offset); - } -} - -static _INLINE_ void serial_out(struct async_struct *info, int offset, - int value) -{ - switch (info->io_type) { -#ifdef CONFIG_HUB6 - case SERIAL_IO_HUB6: - outb(info->hub6 - 1 + offset, info->port); - outb(value, info->port+1); - break; -#endif - case SERIAL_IO_MEM: - writeb(value, (unsigned long) info->iomem_base + - (offset<<info->iomem_reg_shift)); - break; -#ifdef CONFIG_SERIAL_GSC - case SERIAL_IO_GSC: - gsc_writeb(value, info->iomem_base + offset); - break; -#endif - default: - outb(value, info->port+offset); - } -} - -/* - * We used to support using pause I/O for certain machines. We - * haven't supported this for a while, but just in case it's badly - * needed for certain old 386 machines, I've left these #define's - * in.... - */ -#define serial_inp(info, offset) serial_in(info, offset) -#define serial_outp(info, offset, value) serial_out(info, offset, value) - - -/* - * For the 16C950 - */ -void serial_icr_write(struct async_struct *info, int offset, int value) -{ - serial_out(info, UART_SCR, offset); - serial_out(info, UART_ICR, value); -} - -unsigned int serial_icr_read(struct async_struct *info, int offset) -{ - int value; - - serial_icr_write(info, UART_ACR, info->ACR | UART_ACR_ICRRD); - serial_out(info, UART_SCR, offset); - value = serial_in(info, UART_ICR); - serial_icr_write(info, UART_ACR, info->ACR); - return value; -} - -/* - * ------------------------------------------------------------ - * 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) -{ - struct async_struct *info = (struct async_struct *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->device, "rs_stop")) - return; - - save_flags(flags); cli(); - if (info->IER & UART_IER_THRI) { - info->IER &= ~UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - } - if (info->state->type == PORT_16C950) { - info->ACR |= UART_ACR_TXDIS; - serial_icr_write(info, UART_ACR, info->ACR); - } - restore_flags(flags); -} - -static void rs_start(struct tty_struct *tty) -{ - struct async_struct *info = (struct async_struct *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->device, "rs_start")) - return; - - save_flags(flags); cli(); - if (info->xmit.head != info->xmit.tail - && info->xmit.buf - && !(info->IER & UART_IER_THRI)) { - info->IER |= UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - } - if (info->state->type == PORT_16C950) { - info->ACR &= ~UART_ACR_TXDIS; - serial_icr_write(info, UART_ACR, info->ACR); - } - restore_flags(flags); -} - -/* - * ---------------------------------------------------------------------- - * - * 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 async_struct *info, - int event) -{ - info->event |= 1 << event; - queue_task(&info->tqueue, &tq_serial); - mark_bh(SERIAL_BH); -} - -static _INLINE_ void receive_chars(struct async_struct *info, - int *status, struct pt_regs * regs) -{ - struct tty_struct *tty = info->tty; - unsigned char ch; - struct async_icount *icount; - int max_count = 256; - - icount = &info->state->icount; - do { - if (tty->flip.count >= TTY_FLIPBUF_SIZE) { - tty->flip.tqueue.routine((void *) tty); - if (tty->flip.count >= TTY_FLIPBUF_SIZE) - return; // if TTY_DONT_FLIP is set - } - ch = serial_inp(info, UART_RX); - *tty->flip.char_buf_ptr = ch; - icount->rx++; - -#ifdef SERIAL_DEBUG_INTR - printk("DR%02x:%02x...", ch, *status); -#endif - *tty->flip.flag_buf_ptr = 0; - if (*status & (UART_LSR_BI | UART_LSR_PE | - UART_LSR_FE | UART_LSR_OE)) { - /* - * For statistics only - */ - if (*status & UART_LSR_BI) { - *status &= ~(UART_LSR_FE | UART_LSR_PE); - icount->brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ -#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) - if (info->line == sercons.index) { - if (!break_pressed) { - break_pressed = jiffies; - goto ignore_char; - } - break_pressed = 0; - } -#endif - if (info->flags & ASYNC_SAK) - do_SAK(tty); - } else if (*status & UART_LSR_PE) - icount->parity++; - else if (*status & UART_LSR_FE) - icount->frame++; - if (*status & UART_LSR_OE) - icount->overrun++; - - /* - * Mask off conditions which should be ignored. - */ - *status &= info->read_status_mask; - -#ifdef CONFIG_SERIAL_CONSOLE - if (info->line == sercons.index) { - /* Recover the break flag from console xmit */ - *status |= lsr_break_flag; - lsr_break_flag = 0; - } -#endif - if (*status & (UART_LSR_BI)) { -#ifdef SERIAL_DEBUG_INTR - printk("handling break...."); -#endif - *tty->flip.flag_buf_ptr = TTY_BREAK; - } else if (*status & UART_LSR_PE) - *tty->flip.flag_buf_ptr = TTY_PARITY; - else if (*status & UART_LSR_FE) - *tty->flip.flag_buf_ptr = TTY_FRAME; - } -#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) - if (break_pressed && info->line == sercons.index) { - if (ch != 0 && - time_before(jiffies, break_pressed + HZ*5)) { - handle_sysrq(ch, regs, NULL); - break_pressed = 0; - goto ignore_char; - } - break_pressed = 0; - } -#endif - if ((*status & info->ignore_status_mask) == 0) { - tty->flip.flag_buf_ptr++; - tty->flip.char_buf_ptr++; - tty->flip.count++; - } - if ((*status & UART_LSR_OE) && - (tty->flip.count < TTY_FLIPBUF_SIZE)) { - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character - */ - *tty->flip.flag_buf_ptr = TTY_OVERRUN; - tty->flip.count++; - tty->flip.flag_buf_ptr++; - tty->flip.char_buf_ptr++; - } -#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) - ignore_char: -#endif - *status = serial_inp(info, UART_LSR); - } while ((*status & UART_LSR_DR) && (max_count-- > 0)); -#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */ - tty_flip_buffer_push(tty); -#else - queue_task_irq_off(&tty->flip.tqueue, &tq_timer); -#endif -} - -static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) -{ - int count; - - if (info->x_char) { - serial_outp(info, UART_TX, info->x_char); - info->state->icount.tx++; - info->x_char = 0; - if (intr_done) - *intr_done = 0; - return; - } - if (info->xmit.head == info->xmit.tail - || info->tty->stopped - || info->tty->hw_stopped) { - info->IER &= ~UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - return; - } - - count = info->xmit_fifo_size; - do { - serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]); - info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); - info->state->icount.tx++; - if (info->xmit.head == info->xmit.tail) - break; - } while (--count > 0); - - if (CIRC_CNT(info->xmit.head, - info->xmit.tail, - SERIAL_XMIT_SIZE) < WAKEUP_CHARS) - rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); - -#ifdef SERIAL_DEBUG_INTR - printk("THRE..."); -#endif - if (intr_done) - *intr_done = 0; - - if (info->xmit.head == info->xmit.tail) { - info->IER &= ~UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - } -} - -static _INLINE_ void check_modem_status(struct async_struct *info) -{ - int status; - struct async_icount *icount; - - status = serial_in(info, UART_MSR); - - if (status & UART_MSR_ANY_DELTA) { - icount = &info->state->icount; - /* update input line counters */ - if (status & UART_MSR_TERI) - icount->rng++; - if (status & UART_MSR_DDSR) - icount->dsr++; - if (status & UART_MSR_DDCD) { - icount->dcd++; -#ifdef CONFIG_HARD_PPS - if ((info->flags & ASYNC_HARDPPS_CD) && - (status & UART_MSR_DCD)) - hardpps(); -#endif - } - if (status & UART_MSR_DCTS) - icount->cts++; - wake_up_interruptible(&info->delta_msr_wait); - } - - if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { -#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) - printk("ttys%d CD now %s...", info->line, - (status & UART_MSR_DCD) ? "on" : "off"); -#endif - if (status & UART_MSR_DCD) - wake_up_interruptible(&info->open_wait); - else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && - (info->flags & ASYNC_CALLOUT_NOHUP))) { -#ifdef SERIAL_DEBUG_OPEN - printk("doing serial hangup..."); -#endif - if (info->tty) - tty_hangup(info->tty); - } - } - if (info->flags & ASYNC_CTS_FLOW) { - if (info->tty->hw_stopped) { - if (status & UART_MSR_CTS) { -#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) - printk("CTS tx start..."); -#endif - info->tty->hw_stopped = 0; - info->IER |= UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); - return; - } - } else { - if (!(status & UART_MSR_CTS)) { -#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) - printk("CTS tx stop..."); -#endif - info->tty->hw_stopped = 1; - info->IER &= ~UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - } - } - } -} - -#ifdef CONFIG_SERIAL_SHARE_IRQ -/* - * This is the serial driver's generic interrupt routine - */ -static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - int status; - struct async_struct * info; - int pass_counter = 0; - struct async_struct *end_mark = 0; -#ifdef CONFIG_SERIAL_MULTIPORT - int first_multi = 0; - struct rs_multiport_struct *multi; -#endif - -#ifdef SERIAL_DEBUG_INTR - printk("rs_interrupt(%d)...", irq); -#endif - - info = IRQ_ports[irq]; - if (!info) - return; - -#ifdef CONFIG_SERIAL_MULTIPORT - multi = &rs_multiport[irq]; - if (multi->port_monitor) - first_multi = inb(multi->port_monitor); -#endif - - do { - if (!info->tty || - (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) { - if (!end_mark) - end_mark = info; - goto next; - } -#ifdef SERIAL_DEBUG_INTR - printk("IIR = %x...", serial_in(info, UART_IIR)); -#endif - end_mark = 0; - - info->last_active = jiffies; - - status = serial_inp(info, UART_LSR); -#ifdef SERIAL_DEBUG_INTR - printk("status = %x...", status); -#endif - if (status & UART_LSR_DR) - receive_chars(info, &status, regs); - check_modem_status(info); - if (status & UART_LSR_THRE) - transmit_chars(info, 0); - - next: - info = info->next_port; - if (!info) { - info = IRQ_ports[irq]; - if (pass_counter++ > RS_ISR_PASS_LIMIT) { -#if 0 - printk("rs loop break\n"); -#endif - break; /* Prevent infinite loops */ - } - continue; - } - } while (end_mark != info); -#ifdef CONFIG_SERIAL_MULTIPORT - if (multi->port_monitor) - printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n", - info->state->irq, first_multi, - inb(multi->port_monitor)); -#endif -#ifdef SERIAL_DEBUG_INTR - printk("end.\n"); -#endif -} -#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */ - - -/* - * This is the serial driver's interrupt routine for a single port - */ -static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) -{ - int status; - int pass_counter = 0; - struct async_struct * info; -#ifdef CONFIG_SERIAL_MULTIPORT - int first_multi = 0; - struct rs_multiport_struct *multi; -#endif - -#ifdef SERIAL_DEBUG_INTR - printk("rs_interrupt_single(%d)...", irq); -#endif - - info = IRQ_ports[irq]; - if (!info || !info->tty) - return; - -#ifdef CONFIG_SERIAL_MULTIPORT - multi = &rs_multiport[irq]; - if (multi->port_monitor) - first_multi = inb(multi->port_monitor); -#endif - - do { - status = serial_inp(info, UART_LSR); -#ifdef SERIAL_DEBUG_INTR - printk("status = %x...", status); -#endif - if (status & UART_LSR_DR) - receive_chars(info, &status, regs); - check_modem_status(info); - if (status & UART_LSR_THRE) - transmit_chars(info, 0); - if (pass_counter++ > RS_ISR_PASS_LIMIT) { -#if 0 - printk("rs_single loop break.\n"); -#endif - break; - } -#ifdef SERIAL_DEBUG_INTR - printk("IIR = %x...", serial_in(info, UART_IIR)); -#endif - } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); - info->last_active = jiffies; -#ifdef CONFIG_SERIAL_MULTIPORT - if (multi->port_monitor) - printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", - info->state->irq, first_multi, - inb(multi->port_monitor)); -#endif -#ifdef SERIAL_DEBUG_INTR - printk("end.\n"); -#endif -} - -#ifdef CONFIG_SERIAL_MULTIPORT -/* - * This is the serial driver's for multiport boards - */ -static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs * regs) -{ - int status; - struct async_struct * info; - int pass_counter = 0; - int first_multi= 0; - struct rs_multiport_struct *multi; - -#ifdef SERIAL_DEBUG_INTR - printk("rs_interrupt_multi(%d)...", irq); -#endif - - info = IRQ_ports[irq]; - if (!info) - return; - multi = &rs_multiport[irq]; - if (!multi->port1) { - /* Should never happen */ - printk("rs_interrupt_multi: NULL port1!\n"); - return; - } - if (multi->port_monitor) - first_multi = inb(multi->port_monitor); - - while (1) { - if (!info->tty || - (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) - goto next; - - info->last_active = jiffies; - - status = serial_inp(info, UART_LSR); -#ifdef SERIAL_DEBUG_INTR - printk("status = %x...", status); -#endif - if (status & UART_LSR_DR) - receive_chars(info, &status, regs); - check_modem_status(info); - if (status & UART_LSR_THRE) - transmit_chars(info, 0); - - next: - info = info->next_port; - if (info) - continue; - - info = IRQ_ports[irq]; - /* - * The user was a bonehead, and misconfigured their - * multiport info. Rather than lock up the kernel - * in an infinite loop, if we loop too many times, - * print a message and break out of the loop. - */ - if (pass_counter++ > RS_ISR_PASS_LIMIT) { - printk("Misconfigured multiport serial info " - "for irq %d. Breaking out irq loop\n", irq); - break; - } - if (multi->port_monitor) - printk("rs port monitor irq %d: 0x%x, 0x%x\n", - info->state->irq, first_multi, - inb(multi->port_monitor)); - if ((inb(multi->port1) & multi->mask1) != multi->match1) - continue; - if (!multi->port2) - break; - if ((inb(multi->port2) & multi->mask2) != multi->match2) - continue; - if (!multi->port3) - break; - if ((inb(multi->port3) & multi->mask3) != multi->match3) - continue; - if (!multi->port4) - break; - if ((inb(multi->port4) & multi->mask4) != multi->match4) - continue; - break; - } -#ifdef SERIAL_DEBUG_INTR - printk("end.\n"); -#endif -} -#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 - * 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 async_struct *info = (struct async_struct *) 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); -#ifdef SERIAL_HAVE_POLL_WAIT - wake_up_interruptible(&tty->poll_wait); -#endif - } -} - -/* - * 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 very well for 16450's, but gives barely - * passable results for a 16550A. (Although at the expense of much - * CPU overhead). - */ -static void rs_timer(unsigned long dummy) -{ - static unsigned long last_strobe; - struct async_struct *info; - unsigned int i; - unsigned long flags; - - if ((jiffies - last_strobe) >= RS_STROBE_TIME) { - for (i=0; i < NR_IRQS; i++) { - info = IRQ_ports[i]; - if (!info) - continue; - save_flags(flags); cli(); -#ifdef CONFIG_SERIAL_SHARE_IRQ - if (info->next_port) { - do { - serial_out(info, UART_IER, 0); - info->IER |= UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - info = info->next_port; - } while (info); -#ifdef CONFIG_SERIAL_MULTIPORT - if (rs_multiport[i].port1) - rs_interrupt_multi(i, NULL, NULL); - else -#endif - rs_interrupt(i, NULL, NULL); - } else -#endif /* CONFIG_SERIAL_SHARE_IRQ */ - rs_interrupt_single(i, NULL, NULL); - restore_flags(flags); - } - } - last_strobe = jiffies; - mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); - - if (IRQ_ports[0]) { - save_flags(flags); cli(); -#ifdef CONFIG_SERIAL_SHARE_IRQ - rs_interrupt(0, NULL, NULL); -#else - rs_interrupt_single(0, NULL, NULL); -#endif - restore_flags(flags); - - mod_timer(&serial_timer, jiffies + IRQ_timeout[0]); - } -} - -/* - * --------------------------------------------------------------- - * Low level utility subroutines for the serial driver: routines to - * figure out the appropriate timeout for an interrupt chain, routines - * to initialize and startup a serial port, and routines to shutdown a - * serial port. Useful stuff like that. - * --------------------------------------------------------------- - */ - -/* - * This routine figures out the correct timeout for a particular IRQ. - * It uses the smallest timeout of all of the serial ports in a - * particular interrupt chain. Now only used for IRQ 0.... - */ -static void figure_IRQ_timeout(int irq) -{ - struct async_struct *info; - int timeout = 60*HZ; /* 60 seconds === a long time :-) */ - - info = IRQ_ports[irq]; - if (!info) { - IRQ_timeout[irq] = 60*HZ; - return; - } - while (info) { - if (info->timeout < timeout) - timeout = info->timeout; - info = info->next_port; - } - if (!irq) - timeout = timeout / 2; - IRQ_timeout[irq] = (timeout > 3) ? timeout-2 : 1; -} - -#ifdef CONFIG_SERIAL_RSA -/* Attempts to turn on the RSA FIFO. Returns zero on failure */ -static int enable_rsa(struct async_struct *info) -{ - unsigned char mode; - int result; - unsigned long flags; - - save_flags(flags); cli(); - mode = serial_inp(info, UART_RSA_MSR); - result = mode & UART_RSA_MSR_FIFO; - - if (!result) { - serial_outp(info, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); - mode = serial_inp(info, UART_RSA_MSR); - result = mode & UART_RSA_MSR_FIFO; - } - - restore_flags(flags); - return result; -} - -/* Attempts to turn off the RSA FIFO. Returns zero on failure */ -static int disable_rsa(struct async_struct *info) -{ - unsigned char mode; - int result; - unsigned long flags; - - save_flags(flags); cli(); - mode = serial_inp(info, UART_RSA_MSR); - result = !(mode & UART_RSA_MSR_FIFO); - - if (!result) { - serial_outp(info, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); - mode = serial_inp(info, UART_RSA_MSR); - result = !(mode & UART_RSA_MSR_FIFO); - } - - restore_flags(flags); - return result; -} -#endif /* CONFIG_SERIAL_RSA */ - -static int startup(struct async_struct * info) -{ - unsigned long flags; - int retval=0; - void (*handler)(int, void *, struct pt_regs *); - struct serial_state *state= info->state; - unsigned long page; -#ifdef CONFIG_SERIAL_MANY_PORTS - unsigned short ICP; -#endif - - page = get_zeroed_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - save_flags(flags); cli(); - - if (info->flags & ASYNC_INITIALIZED) { - free_page(page); - goto errout; - } - - if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { - 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 ttys%d (irq %d)...", info->line, state->irq); -#endif - - if (uart_config[state->type].flags & UART_STARTECH) { - /* Wake up UART */ - serial_outp(info, UART_LCR, 0xBF); - serial_outp(info, UART_EFR, UART_EFR_ECB); - /* - * Turn off LCR == 0xBF so we actually set the IER - * register on the XR16C850 - */ - serial_outp(info, UART_LCR, 0); - serial_outp(info, UART_IER, 0); - /* - * Now reset LCR so we can turn off the ECB bit - */ - serial_outp(info, UART_LCR, 0xBF); - serial_outp(info, UART_EFR, 0); - /* - * For a XR16C850, we need to set the trigger levels - */ - if (state->type == PORT_16850) { - serial_outp(info, UART_FCTR, UART_FCTR_TRGD | - UART_FCTR_RX); - serial_outp(info, UART_TRG, UART_TRG_96); - serial_outp(info, UART_FCTR, UART_FCTR_TRGD | - UART_FCTR_TX); - serial_outp(info, UART_TRG, UART_TRG_96); - } - serial_outp(info, UART_LCR, 0); - } - - if (state->type == PORT_16750) { - /* Wake up UART */ - serial_outp(info, UART_IER, 0); - } - - if (state->type == PORT_16C950) { - /* Wake up and initialize UART */ - info->ACR = 0; - serial_outp(info, UART_LCR, 0xBF); - serial_outp(info, UART_EFR, UART_EFR_ECB); - serial_outp(info, UART_IER, 0); - serial_outp(info, UART_LCR, 0); - serial_icr_write(info, UART_CSR, 0); /* Reset the UART */ - serial_outp(info, UART_LCR, 0xBF); - serial_outp(info, UART_EFR, UART_EFR_ECB); - serial_outp(info, UART_LCR, 0); - } - -#ifdef CONFIG_SERIAL_RSA - /* - * If this is an RSA port, see if we can kick it up to the - * higher speed clock. - */ - if (state->type == PORT_RSA) { - if (state->baud_base != SERIAL_RSA_BAUD_BASE && - enable_rsa(info)) - state->baud_base = SERIAL_RSA_BAUD_BASE; - if (state->baud_base == SERIAL_RSA_BAUD_BASE) - serial_outp(info, UART_RSA_FRR, 0); - } -#endif - - /* - * Clear the FIFO buffers and disable them - * (they will be reenabled in change_speed()) - */ - if (uart_config[state->type].flags & UART_CLEAR_FIFO) { - serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT)); - serial_outp(info, UART_FCR, 0); - } - - /* - * Clear the interrupt registers. - */ - (void) serial_inp(info, UART_LSR); - (void) serial_inp(info, UART_RX); - (void) serial_inp(info, UART_IIR); - (void) serial_inp(info, UART_MSR); - - /* - * 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 (!(info->flags & ASYNC_BUGGY_UART) && - (serial_inp(info, UART_LSR) == 0xff)) { - printk("ttyS%d: LSR safety check engaged!\n", state->line); - if (capable(CAP_SYS_ADMIN)) { - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - } else - retval = -ENODEV; - goto errout; - } - - /* - * Allocate the IRQ if necessary - */ - if (state->irq && (!IRQ_ports[state->irq] || - !IRQ_ports[state->irq]->next_port)) { - if (IRQ_ports[state->irq]) { -#ifdef CONFIG_SERIAL_SHARE_IRQ - free_irq(state->irq, &IRQ_ports[state->irq]); -#ifdef CONFIG_SERIAL_MULTIPORT - if (rs_multiport[state->irq].port1) - handler = rs_interrupt_multi; - else -#endif - handler = rs_interrupt; -#else - retval = -EBUSY; - goto errout; -#endif /* CONFIG_SERIAL_SHARE_IRQ */ - } else - handler = rs_interrupt_single; - - retval = request_irq(state->irq, handler, SA_SHIRQ, - "serial", &IRQ_ports[state->irq]); - if (retval) { - if (capable(CAP_SYS_ADMIN)) { - if (info->tty) - set_bit(TTY_IO_ERROR, - &info->tty->flags); - retval = 0; - } - goto errout; - } - } - - /* - * Insert serial port into IRQ chain. - */ - info->prev_port = 0; - info->next_port = IRQ_ports[state->irq]; - if (info->next_port) - info->next_port->prev_port = info; - IRQ_ports[state->irq] = info; - figure_IRQ_timeout(state->irq); - - /* - * Now, initialize the UART - */ - serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ - - info->MCR = 0; - if (info->tty->termios->c_cflag & CBAUD) - info->MCR = UART_MCR_DTR | UART_MCR_RTS; -#ifdef CONFIG_SERIAL_MANY_PORTS - if (info->flags & ASYNC_FOURPORT) { - if (state->irq == 0) - info->MCR |= UART_MCR_OUT1; - } else -#endif - { - if (state->irq != 0) - info->MCR |= UART_MCR_OUT2; - } - info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ - serial_outp(info, UART_MCR, info->MCR); - - /* - * Finally, enable interrupts - */ - info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; - serial_outp(info, UART_IER, info->IER); /* enable interrupts */ - -#ifdef CONFIG_SERIAL_MANY_PORTS - if (info->flags & ASYNC_FOURPORT) { - /* Enable interrupts on the AST Fourport board */ - ICP = (info->port & 0xFE0) | 0x01F; - outb_p(0x80, ICP); - (void) inb_p(ICP); - } -#endif - - /* - * And clear the interrupt registers again for luck. - */ - (void)serial_inp(info, UART_LSR); - (void)serial_inp(info, UART_RX); - (void)serial_inp(info, UART_IIR); - (void)serial_inp(info, UART_MSR); - - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); - info->xmit.head = info->xmit.tail = 0; - - /* - * Set up serial timers... - */ - mod_timer(&serial_timer, jiffies + 2*HZ/100); - - /* - * Set up the tty->alt_speed kludge - */ -#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ - if (info->tty) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; - } -#endif - - /* - * and set the speed of the serial port - */ - change_speed(info, 0); - - info->flags |= ASYNC_INITIALIZED; - restore_flags(flags); - return 0; - -errout: - restore_flags(flags); - return retval; -} - -/* - * 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 async_struct * info) -{ - unsigned long flags; - struct serial_state *state; - int retval; - - if (!(info->flags & ASYNC_INITIALIZED)) - return; - - state = info->state; - -#ifdef SERIAL_DEBUG_OPEN - printk("Shutting down serial port %d (irq %d)....", info->line, - state->irq); -#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); - - /* - * First unlink the serial port from the IRQ chain... - */ - if (info->next_port) - info->next_port->prev_port = info->prev_port; - if (info->prev_port) - info->prev_port->next_port = info->next_port; - else - IRQ_ports[state->irq] = info->next_port; - figure_IRQ_timeout(state->irq); - - /* - * Free the IRQ, if necessary - */ - if (state->irq && (!IRQ_ports[state->irq] || - !IRQ_ports[state->irq]->next_port)) { - if (IRQ_ports[state->irq]) { - free_irq(state->irq, &IRQ_ports[state->irq]); - retval = request_irq(state->irq, rs_interrupt_single, - SA_SHIRQ, "serial", - &IRQ_ports[state->irq]); - - if (retval) - printk("serial shutdown: request_irq: error %d" - " Couldn't reacquire IRQ.\n", retval); - } else - free_irq(state->irq, &IRQ_ports[state->irq]); - } - - if (info->xmit.buf) { - unsigned long pg = (unsigned long) info->xmit.buf; - info->xmit.buf = 0; - free_page(pg); - } - - info->IER = 0; - serial_outp(info, UART_IER, 0x00); /* disable all intrs */ -#ifdef CONFIG_SERIAL_MANY_PORTS - if (info->flags & ASYNC_FOURPORT) { - /* reset interrupts on the AST Fourport board */ - (void) inb((info->port & 0xFE0) | 0x01F); - info->MCR |= UART_MCR_OUT1; - } else -#endif - info->MCR &= ~UART_MCR_OUT2; - info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ - - /* disable break condition */ - serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); - - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) - info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); - serial_outp(info, UART_MCR, info->MCR); - - /* disable FIFO's */ - serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT)); - serial_outp(info, UART_FCR, 0); - -#ifdef CONFIG_SERIAL_RSA - /* - * Reset the RSA board back to 115kbps compat mode. - */ - if ((state->type == PORT_RSA) && - (state->baud_base == SERIAL_RSA_BAUD_BASE && - disable_rsa(info))) - state->baud_base = SERIAL_RSA_BAUD_BASE_LO; -#endif - - - (void)serial_in(info, UART_RX); /* read data port to reset things */ - - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - - if (uart_config[info->state->type].flags & UART_STARTECH) { - /* Arrange to enter sleep mode */ - serial_outp(info, UART_LCR, 0xBF); - serial_outp(info, UART_EFR, UART_EFR_ECB); - serial_outp(info, UART_LCR, 0); - serial_outp(info, UART_IER, UART_IERX_SLEEP); - serial_outp(info, UART_LCR, 0xBF); - serial_outp(info, UART_EFR, 0); - serial_outp(info, UART_LCR, 0); - } - if (info->state->type == PORT_16750) { - /* Arrange to enter sleep mode */ - serial_outp(info, UART_IER, UART_IERX_SLEEP); - } - info->flags &= ~ASYNC_INITIALIZED; - restore_flags(flags); -} - -#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ -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 int tty_get_baud_rate(struct tty_struct *tty) -{ - struct async_struct * info = (struct async_struct *)tty->driver_data; - unsigned int cflag, i; - - cflag = tty->termios->c_cflag; - - i = cflag & CBAUD; - if (i & CBAUDEX) { - i &= ~CBAUDEX; - if (i < 1 || i > 2) - tty->termios->c_cflag &= ~CBAUDEX; - else - i += 15; - } - if (i == 15) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - i += 1; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - i += 2; - } - return baud_table[i]; -} -#endif - -/* - * 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 async_struct *info, - struct termios *old_termios) -{ - int quot = 0, baud_base, baud; - unsigned cflag, cval, fcr = 0; - int bits; - unsigned long flags; - - if (!info->tty || !info->tty->termios) - return; - cflag = info->tty->termios->c_cflag; - if (!CONFIGURED_SERIAL_PORT(info)) - return; - - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: cval = 0x00; bits = 7; break; - case CS6: cval = 0x01; bits = 8; break; - case CS7: cval = 0x02; bits = 9; break; - case CS8: cval = 0x03; bits = 10; break; - /* Never happens, but GCC is too dumb to figure it out */ - default: cval = 0x00; bits = 7; break; - } - if (cflag & CSTOPB) { - cval |= 0x04; - bits++; - } - if (cflag & PARENB) { - cval |= UART_LCR_PARITY; - bits++; - } - if (!(cflag & PARODD)) - cval |= UART_LCR_EPAR; -#ifdef CMSPAR - if (cflag & CMSPAR) - cval |= UART_LCR_SPAR; -#endif - - /* Determine divisor based on baud rate */ - baud = tty_get_baud_rate(info->tty); - if (!baud) - baud = 9600; /* B0 transition handled in rs_set_termios */ -#ifdef CONFIG_SERIAL_RSA - if ((info->state->type == PORT_RSA) && - (info->state->baud_base != SERIAL_RSA_BAUD_BASE) && - enable_rsa(info)) - info->state->baud_base = SERIAL_RSA_BAUD_BASE; -#endif - baud_base = info->state->baud_base; - if (info->state->type == PORT_16C950) { - if (baud <= baud_base) - serial_icr_write(info, UART_TCR, 0); - else if (baud <= 2*baud_base) { - serial_icr_write(info, UART_TCR, 0x8); - baud_base = baud_base * 2; - } else if (baud <= 4*baud_base) { - serial_icr_write(info, UART_TCR, 0x4); - baud_base = baud_base * 4; - } else - serial_icr_write(info, UART_TCR, 0); - } - if (baud == 38400 && - ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) - quot = info->state->custom_divisor; - else { - if (baud == 134) - /* Special case since 134 is really 134.5 */ - quot = (2*baud_base / 269); - else if (baud) - quot = baud_base / baud; - } - /* If the quotient is zero refuse the change */ - if (!quot && old_termios) { - info->tty->termios->c_cflag &= ~CBAUD; - info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); - baud = tty_get_baud_rate(info->tty); - if (!baud) - baud = 9600; - if (baud == 38400 && - ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) - quot = info->state->custom_divisor; - else { - if (baud == 134) - /* Special case since 134 is really 134.5 */ - quot = (2*baud_base / 269); - else if (baud) - quot = baud_base / baud; - } - } - /* As a last resort, if the quotient is zero, default to 9600 bps */ - if (!quot) - quot = baud_base / 9600; - /* - * Work around a bug in the Oxford Semiconductor 952 rev B - * chip which causes it to seriously miscalculate baud rates - * when DLL is 0. - */ - if (((quot & 0xFF) == 0) && (info->state->type == PORT_16C950) && - (info->state->revision == 0x5201)) - quot++; - - info->quot = quot; - info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); - info->timeout += HZ/50; /* Add .02 seconds of slop */ - - /* Set up FIFO's */ - if (uart_config[info->state->type].flags & UART_USE_FIFO) { - if ((info->state->baud_base / quot) < 2400) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; -#ifdef CONFIG_SERIAL_RSA - else if (info->state->type == PORT_RSA) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; -#endif - else - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; - } - if (info->state->type == PORT_16750) - fcr |= UART_FCR7_64BYTE; - - /* CTS flow control flag and modem status interrupts */ - info->IER &= ~UART_IER_MSI; - if (info->flags & ASYNC_HARDPPS_CD) - info->IER |= UART_IER_MSI; - if (cflag & CRTSCTS) { - info->flags |= ASYNC_CTS_FLOW; - info->IER |= UART_IER_MSI; - } else - info->flags &= ~ASYNC_CTS_FLOW; - if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; - else { - info->flags |= ASYNC_CHECK_CD; - info->IER |= UART_IER_MSI; - } - serial_out(info, UART_IER, info->IER); - - /* - * Set up parity check flag - */ -#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - - info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (I_INPCK(info->tty)) - info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) - info->read_status_mask |= UART_LSR_BI; - - /* - * Characters to ignore - */ - info->ignore_status_mask = 0; - if (I_IGNPAR(info->tty)) - info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; - if (I_IGNBRK(info->tty)) { - info->ignore_status_mask |= UART_LSR_BI; - /* - * If we're ignore parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (I_IGNPAR(info->tty)) - info->ignore_status_mask |= UART_LSR_OE; - } - /* - * !!! ignore all characters if CREAD is not set - */ - if ((cflag & CREAD) == 0) - info->ignore_status_mask |= UART_LSR_DR; - save_flags(flags); cli(); - if (uart_config[info->state->type].flags & UART_STARTECH) { - serial_outp(info, UART_LCR, 0xBF); - serial_outp(info, UART_EFR, - (cflag & CRTSCTS) ? UART_EFR_CTS : 0); - } - serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ - serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */ - serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */ - if (info->state->type == PORT_16750) - serial_outp(info, UART_FCR, fcr); /* set fcr */ - serial_outp(info, UART_LCR, cval); /* reset DLAB */ - info->LCR = cval; /* Save LCR */ - if (info->state->type != PORT_16750) { - if (fcr & UART_FCR_ENABLE_FIFO) { - /* emulated UARTs (Lucent Venus 167x) need two steps */ - serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); - } - serial_outp(info, UART_FCR, fcr); /* set fcr */ - } - restore_flags(flags); -} - -static void rs_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct async_struct *info = (struct async_struct *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->device, "rs_put_char")) - return; - - if (!tty || !info->xmit.buf) - return; - - save_flags(flags); cli(); - if (CIRC_SPACE(info->xmit.head, - info->xmit.tail, - SERIAL_XMIT_SIZE) == 0) { - restore_flags(flags); - return; - } - - info->xmit.buf[info->xmit.head] = ch; - info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); - restore_flags(flags); -} - -static void rs_flush_chars(struct tty_struct *tty) -{ - struct async_struct *info = (struct async_struct *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) - return; - - if (info->xmit.head == info->xmit.tail - || tty->stopped - || tty->hw_stopped - || !info->xmit.buf) - return; - - save_flags(flags); cli(); - info->IER |= UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - restore_flags(flags); -} - -static int rs_write(struct tty_struct * tty, int from_user, - const unsigned char *buf, int count) -{ - int c, ret = 0; - struct async_struct *info = (struct async_struct *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->device, "rs_write")) - return 0; - - if (!tty || !info->xmit.buf || !tmp_buf) - return 0; - - save_flags(flags); - if (from_user) { - down(&tmp_buf_sem); - while (1) { - int c1; - c = CIRC_SPACE_TO_END(info->xmit.head, - info->xmit.tail, - SERIAL_XMIT_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - - c -= copy_from_user(tmp_buf, buf, c); - if (!c) { - if (!ret) - ret = -EFAULT; - break; - } - cli(); - c1 = CIRC_SPACE_TO_END(info->xmit.head, - info->xmit.tail, - SERIAL_XMIT_SIZE); - if (c1 < c) - c = c1; - memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); - info->xmit.head = ((info->xmit.head + c) & - (SERIAL_XMIT_SIZE-1)); - restore_flags(flags); - buf += c; - count -= c; - ret += c; - } - up(&tmp_buf_sem); - } else { - cli(); - while (1) { - c = CIRC_SPACE_TO_END(info->xmit.head, - info->xmit.tail, - SERIAL_XMIT_SIZE); - if (count < c) - c = count; - if (c <= 0) { - break; - } - memcpy(info->xmit.buf + info->xmit.head, buf, c); - info->xmit.head = ((info->xmit.head + c) & - (SERIAL_XMIT_SIZE-1)); - buf += c; - count -= c; - ret += c; - } - restore_flags(flags); - } - if (info->xmit.head != info->xmit.tail - && !tty->stopped - && !tty->hw_stopped - && !(info->IER & UART_IER_THRI)) { - info->IER |= UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - } - return ret; -} - -static int rs_write_room(struct tty_struct *tty) -{ - struct async_struct *info = (struct async_struct *)tty->driver_data; - - if (serial_paranoia_check(info, tty->device, "rs_write_room")) - return 0; - return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); -} - -static int rs_chars_in_buffer(struct tty_struct *tty) -{ - struct async_struct *info = (struct async_struct *)tty->driver_data; - - if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) - return 0; - return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); -} - -static void rs_flush_buffer(struct tty_struct *tty) -{ - struct async_struct *info = (struct async_struct *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) - return; - save_flags(flags); cli(); - info->xmit.head = info->xmit.tail = 0; - restore_flags(flags); - wake_up_interruptible(&tty->write_wait); -#ifdef SERIAL_HAVE_POLL_WAIT - wake_up_interruptible(&tty->poll_wait); -#endif - 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 rs_send_xchar(struct tty_struct *tty, char ch) -{ - struct async_struct *info = (struct async_struct *)tty->driver_data; - - if (serial_paranoia_check(info, tty->device, "rs_send_char")) - return; - - info->x_char = ch; - if (ch) { - /* Make sure transmit interrupts are on */ - info->IER |= UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - } -} - -/* - * ------------------------------------------------------------ - * 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 async_struct *info = (struct async_struct *)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)) - rs_send_xchar(tty, STOP_CHAR(tty)); - - if (tty->termios->c_cflag & CRTSCTS) - info->MCR &= ~UART_MCR_RTS; - - save_flags(flags); cli(); - serial_out(info, UART_MCR, info->MCR); - restore_flags(flags); -} - -static void rs_unthrottle(struct tty_struct * tty) -{ - struct async_struct *info = (struct async_struct *)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)) { - if (info->x_char) - info->x_char = 0; - else - rs_send_xchar(tty, START_CHAR(tty)); - } - if (tty->termios->c_cflag & CRTSCTS) - info->MCR |= UART_MCR_RTS; - save_flags(flags); cli(); - serial_out(info, UART_MCR, info->MCR); - restore_flags(flags); -} - -/* - * ------------------------------------------------------------ - * rs_ioctl() and friends - * ------------------------------------------------------------ - */ - -static int get_serial_info(struct async_struct * info, - struct serial_struct * retinfo) -{ - struct serial_struct tmp; - struct serial_state *state = info->state; - - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof(tmp)); - tmp.type = state->type; - tmp.line = state->line; - tmp.port = state->port; - if (HIGH_BITS_OFFSET) - tmp.port_high = state->port >> HIGH_BITS_OFFSET; - else - tmp.port_high = 0; - tmp.irq = state->irq; - tmp.flags = state->flags; - tmp.xmit_fifo_size = state->xmit_fifo_size; - tmp.baud_base = state->baud_base; - tmp.close_delay = state->close_delay; - tmp.closing_wait = state->closing_wait; - tmp.custom_divisor = state->custom_divisor; - tmp.hub6 = state->hub6; - tmp.io_type = state->io_type; - if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) - return -EFAULT; - return 0; -} - -static int set_serial_info(struct async_struct * info, - struct serial_struct * new_info) -{ - struct serial_struct new_serial; - struct serial_state old_state, *state; - unsigned int i,change_irq,change_port; - int retval = 0; - unsigned long new_port; - - if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) - return -EFAULT; - state = info->state; - old_state = *state; - - new_port = new_serial.port; - if (HIGH_BITS_OFFSET) - new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; - - change_irq = new_serial.irq != state->irq; - change_port = (new_port != ((int) state->port)) || - (new_serial.hub6 != state->hub6); - - if (!capable(CAP_SYS_ADMIN)) { - if (change_irq || change_port || - (new_serial.baud_base != state->baud_base) || - (new_serial.type != state->type) || - (new_serial.close_delay != state->close_delay) || - (new_serial.xmit_fifo_size != state->xmit_fifo_size) || - ((new_serial.flags & ~ASYNC_USR_MASK) != - (state->flags & ~ASYNC_USR_MASK))) - return -EPERM; - state->flags = ((state->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - info->flags = ((info->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - state->custom_divisor = new_serial.custom_divisor; - goto check_and_exit; - } - - new_serial.irq = irq_cannonicalize(new_serial.irq); - - if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || - (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) || - (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) || - (new_serial.type == PORT_STARTECH)) { - return -EINVAL; - } - - if ((new_serial.type != state->type) || - (new_serial.xmit_fifo_size <= 0)) - new_serial.xmit_fifo_size = - uart_config[new_serial.type].dfl_xmit_fifo_size; - - /* Make sure address is not already in use */ - if (new_serial.type) { - for (i = 0 ; i < NR_PORTS; i++) - if ((state != &rs_table[i]) && - (rs_table[i].port == new_port) && - rs_table[i].type) - return -EADDRINUSE; - } - - if ((change_port || change_irq) && (state->count > 1)) - return -EBUSY; - - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - - state->baud_base = new_serial.baud_base; - state->flags = ((state->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); - info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | - (info->flags & ASYNC_INTERNAL_FLAGS)); - state->custom_divisor = new_serial.custom_divisor; - state->close_delay = new_serial.close_delay * HZ/100; - state->closing_wait = new_serial.closing_wait * HZ/100; -#if (LINUX_VERSION_CODE > 0x20100) - info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; -#endif - info->xmit_fifo_size = state->xmit_fifo_size = - new_serial.xmit_fifo_size; - - if ((state->type != PORT_UNKNOWN) && state->port) { -#ifdef CONFIG_SERIAL_RSA - if (old_state.type == PORT_RSA) - release_region(state->port + UART_RSA_BASE, 16); - else -#endif - release_region(state->port,8); - } - state->type = new_serial.type; - if (change_port || change_irq) { - /* - * We need to shutdown the serial port at the old - * port/irq combination. - */ - shutdown(info); - state->irq = new_serial.irq; - info->port = state->port = new_port; - info->hub6 = state->hub6 = new_serial.hub6; - if (info->hub6) - info->io_type = state->io_type = SERIAL_IO_HUB6; - else if (info->io_type == SERIAL_IO_HUB6) - info->io_type = state->io_type = SERIAL_IO_PORT; - } - if ((state->type != PORT_UNKNOWN) && state->port) { -#ifdef CONFIG_SERIAL_RSA - if (state->type == PORT_RSA) - request_region(state->port + UART_RSA_BASE, - 16, "serial_rsa(set)"); - else -#endif - request_region(state->port,8,"serial(set)"); - } - - -check_and_exit: - if (!state->port || !state->type) - return 0; - if (info->flags & ASYNC_INITIALIZED) { - if (((old_state.flags & ASYNC_SPD_MASK) != - (state->flags & ASYNC_SPD_MASK)) || - (old_state.custom_divisor != state->custom_divisor)) { -#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; -#endif - change_speed(info, 0); - } - } else - 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 async_struct * info, unsigned int *value) -{ - unsigned char status; - unsigned int result; - unsigned long flags; - - save_flags(flags); cli(); - status = serial_in(info, UART_LSR); - restore_flags(flags); - result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); - - /* - * If we're about to load something into the transmit - * register, we'll pretend the transmitter isn't empty to - * avoid a race condition (depending on when the transmit - * interrupt happens). - */ - if (info->x_char || - ((CIRC_CNT(info->xmit.head, info->xmit.tail, - SERIAL_XMIT_SIZE) > 0) && - !info->tty->stopped && !info->tty->hw_stopped)) - result &= ~TIOCSER_TEMT; - - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - return 0; -} - - -static int get_modem_info(struct async_struct * info, unsigned int *value) -{ - unsigned char control, status; - unsigned int result; - unsigned long flags; - - control = info->MCR; - save_flags(flags); cli(); - status = serial_in(info, UART_MSR); - restore_flags(flags); - result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) - | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) -#ifdef TIOCM_OUT1 - | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) - | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) -#endif - | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) - | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) - | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) - | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); - - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - return 0; -} - -static int set_modem_info(struct async_struct * info, unsigned int cmd, - unsigned int *value) -{ - unsigned int arg; - unsigned long flags; - - if (copy_from_user(&arg, value, sizeof(int))) - return -EFAULT; - - switch (cmd) { - case TIOCMBIS: - if (arg & TIOCM_RTS) - info->MCR |= UART_MCR_RTS; - if (arg & TIOCM_DTR) - info->MCR |= UART_MCR_DTR; -#ifdef TIOCM_OUT1 - if (arg & TIOCM_OUT1) - info->MCR |= UART_MCR_OUT1; - if (arg & TIOCM_OUT2) - info->MCR |= UART_MCR_OUT2; -#endif - if (arg & TIOCM_LOOP) - info->MCR |= UART_MCR_LOOP; - break; - case TIOCMBIC: - if (arg & TIOCM_RTS) - info->MCR &= ~UART_MCR_RTS; - if (arg & TIOCM_DTR) - info->MCR &= ~UART_MCR_DTR; -#ifdef TIOCM_OUT1 - if (arg & TIOCM_OUT1) - info->MCR &= ~UART_MCR_OUT1; - if (arg & TIOCM_OUT2) - info->MCR &= ~UART_MCR_OUT2; -#endif - if (arg & TIOCM_LOOP) - info->MCR &= ~UART_MCR_LOOP; - break; - case TIOCMSET: - info->MCR = ((info->MCR & ~(UART_MCR_RTS | -#ifdef TIOCM_OUT1 - UART_MCR_OUT1 | - UART_MCR_OUT2 | -#endif - UART_MCR_LOOP | - UART_MCR_DTR)) - | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) -#ifdef TIOCM_OUT1 - | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0) - | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0) -#endif - | ((arg & TIOCM_LOOP) ? UART_MCR_LOOP : 0) - | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); - break; - default: - return -EINVAL; - } - save_flags(flags); cli(); - info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ - serial_out(info, UART_MCR, info->MCR); - restore_flags(flags); - return 0; -} - -static int do_autoconfig(struct async_struct * info) -{ - int irq, retval; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (info->state->count > 1) - return -EBUSY; - - shutdown(info); - - autoconfig(info->state); - if ((info->state->flags & ASYNC_AUTO_IRQ) && - (info->state->port != 0 || info->state->iomem_base != 0) && - (info->state->type != PORT_UNKNOWN)) { - irq = detect_uart_irq(info->state); - if (irq > 0) - info->state->irq = irq; - } - - retval = startup(info); - if (retval) - return retval; - return 0; -} - -/* - * rs_break() --- routine which turns the break handling on or off - */ -#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ -static void send_break( struct async_struct * info, int duration) -{ - if (!CONFIGURED_SERIAL_PORT(info)) - return; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + duration; - cli(); - info->LCR |= UART_LCR_SBC; - serial_out(info, UART_LCR, info->LCR); - schedule(); - info->LCR &= ~UART_LCR_SBC; - serial_out(info, UART_LCR, info->LCR); - sti(); -} -#else -static void rs_break(struct tty_struct *tty, int break_state) -{ - struct async_struct * info = (struct async_struct *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->device, "rs_break")) - return; - - if (!CONFIGURED_SERIAL_PORT(info)) - return; - save_flags(flags); cli(); - if (break_state == -1) - info->LCR |= UART_LCR_SBC; - else - info->LCR &= ~UART_LCR_SBC; - serial_out(info, UART_LCR, info->LCR); - restore_flags(flags); -} -#endif - -#ifdef CONFIG_SERIAL_MULTIPORT -static int get_multiport_struct(struct async_struct * info, - struct serial_multiport_struct *retinfo) -{ - struct serial_multiport_struct ret; - struct rs_multiport_struct *multi; - - multi = &rs_multiport[info->state->irq]; - - ret.port_monitor = multi->port_monitor; - - ret.port1 = multi->port1; - ret.mask1 = multi->mask1; - ret.match1 = multi->match1; - - ret.port2 = multi->port2; - ret.mask2 = multi->mask2; - ret.match2 = multi->match2; - - ret.port3 = multi->port3; - ret.mask3 = multi->mask3; - ret.match3 = multi->match3; - - ret.port4 = multi->port4; - ret.mask4 = multi->mask4; - ret.match4 = multi->match4; - - ret.irq = info->state->irq; - - if (copy_to_user(retinfo,&ret,sizeof(*retinfo))) - return -EFAULT; - return 0; -} - -static int set_multiport_struct(struct async_struct * info, - struct serial_multiport_struct *in_multi) -{ - struct serial_multiport_struct new_multi; - struct rs_multiport_struct *multi; - struct serial_state *state; - int was_multi, now_multi; - int retval; - void (*handler)(int, void *, struct pt_regs *); - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - state = info->state; - - if (copy_from_user(&new_multi, in_multi, - sizeof(struct serial_multiport_struct))) - return -EFAULT; - - if (new_multi.irq != state->irq || state->irq == 0 || - !IRQ_ports[state->irq]) - return -EINVAL; - - multi = &rs_multiport[state->irq]; - was_multi = (multi->port1 != 0); - - multi->port_monitor = new_multi.port_monitor; - - if (multi->port1) - release_region(multi->port1,1); - multi->port1 = new_multi.port1; - multi->mask1 = new_multi.mask1; - multi->match1 = new_multi.match1; - if (multi->port1) - request_region(multi->port1,1,"serial(multiport1)"); - - if (multi->port2) - release_region(multi->port2,1); - multi->port2 = new_multi.port2; - multi->mask2 = new_multi.mask2; - multi->match2 = new_multi.match2; - if (multi->port2) - request_region(multi->port2,1,"serial(multiport2)"); - - if (multi->port3) - release_region(multi->port3,1); - multi->port3 = new_multi.port3; - multi->mask3 = new_multi.mask3; - multi->match3 = new_multi.match3; - if (multi->port3) - request_region(multi->port3,1,"serial(multiport3)"); - - if (multi->port4) - release_region(multi->port4,1); - multi->port4 = new_multi.port4; - multi->mask4 = new_multi.mask4; - multi->match4 = new_multi.match4; - if (multi->port4) - request_region(multi->port4,1,"serial(multiport4)"); - - now_multi = (multi->port1 != 0); - - if (IRQ_ports[state->irq]->next_port && - (was_multi != now_multi)) { - free_irq(state->irq, &IRQ_ports[state->irq]); - if (now_multi) - handler = rs_interrupt_multi; - else - handler = rs_interrupt; - - retval = request_irq(state->irq, handler, SA_SHIRQ, - "serial", &IRQ_ports[state->irq]); - if (retval) { - printk("Couldn't reallocate serial interrupt " - "driver!!\n"); - } - } - return 0; -} -#endif - -static int rs_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) -{ - struct async_struct * info = (struct async_struct *)tty->driver_data; - struct async_icount cprev, cnow; /* kernel counter temps */ - struct serial_icounter_struct icount; - unsigned long flags; -#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ - int retval, tmp; -#endif - - if (serial_paranoia_check(info, tty->device, "rs_ioctl")) - return -ENODEV; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && - (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { -#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ - 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 (signal_pending(current)) - return -EINTR; - if (!arg) { - send_break(info, HZ/4); /* 1/4 second */ - if (signal_pending(current)) - 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 (signal_pending(current)) - return -EINTR; - send_break(info, arg ? arg*(HZ/10) : HZ/4); - if (signal_pending(current)) - return -EINTR; - return 0; - case TIOCGSOFTCAR: - tmp = C_CLOCAL(tty) ? 1 : 0; - if (copy_to_user((void *)arg, &tmp, sizeof(int))) - return -EFAULT; - return 0; - case TIOCSSOFTCAR: - if (copy_from_user(&tmp, (void *)arg, sizeof(int))) - return -EFAULT; - - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (tmp ? CLOCAL : 0)); - return 0; -#endif - 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 TIOCSERCONFIG: - return do_autoconfig(info); - - case TIOCSERGETLSR: /* Get line status register */ - return get_lsr_info(info, (unsigned int *) arg); - - case TIOCSERGSTRUCT: - if (copy_to_user((struct async_struct *) arg, - info, sizeof(struct async_struct))) - return -EFAULT; - return 0; - -#ifdef CONFIG_SERIAL_MULTIPORT - case TIOCSERGETMULTI: - return get_multiport_struct(info, - (struct serial_multiport_struct *) arg); - case TIOCSERSETMULTI: - return set_multiport_struct(info, - (struct serial_multiport_struct *) arg); -#endif - - /* - * 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: - save_flags(flags); cli(); - /* note the counters on entry */ - cprev = info->state->icount; - restore_flags(flags); - /* Force modem status interrupts on */ - info->IER |= UART_IER_MSI; - serial_out(info, UART_IER, info->IER); - while (1) { - interruptible_sleep_on(&info->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - save_flags(flags); cli(); - cnow = info->state->icount; /* atomic copy */ - restore_flags(flags); - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { - return 0; - } - cprev = cnow; - } - /* 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: - save_flags(flags); cli(); - cnow = info->state->icount; - restore_flags(flags); - icount.cts = cnow.cts; - icount.dsr = cnow.dsr; - icount.rng = cnow.rng; - icount.dcd = cnow.dcd; - icount.rx = cnow.rx; - icount.tx = cnow.tx; - icount.frame = cnow.frame; - icount.overrun = cnow.overrun; - icount.parity = cnow.parity; - icount.brk = cnow.brk; - icount.buf_overrun = cnow.buf_overrun; - - if (copy_to_user((void *)arg, &icount, sizeof(icount))) - return -EFAULT; - return 0; - case TIOCSERGWILD: - case TIOCSERSWILD: - /* "setserial -W" is called in Debian boot */ - printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); - return 0; - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) -{ - struct async_struct *info = (struct async_struct *)tty->driver_data; - unsigned long flags; - unsigned int cflag = tty->termios->c_cflag; - - if ( (cflag == old_termios->c_cflag) - && ( RELEVANT_IFLAG(tty->termios->c_iflag) - == RELEVANT_IFLAG(old_termios->c_iflag))) - return; - - change_speed(info, old_termios); - - /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && - !(cflag & CBAUD)) { - info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); - save_flags(flags); cli(); - serial_out(info, UART_MCR, info->MCR); - restore_flags(flags); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && - (cflag & CBAUD)) { - info->MCR |= UART_MCR_DTR; - if (!(tty->termios->c_cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) { - info->MCR |= UART_MCR_RTS; - } - save_flags(flags); cli(); - serial_out(info, UART_MCR, info->MCR); - restore_flags(flags); - } - - /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - rs_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 -} - -/* - * ------------------------------------------------------------ - * rs_close() - * - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we unlink its - * async structure from the interrupt chain if necessary, and we free - * that IRQ if nothing is left in the chain. - * ------------------------------------------------------------ - */ -static void rs_close(struct tty_struct *tty, struct file * filp) -{ - struct async_struct * info = (struct async_struct *)tty->driver_data; - struct serial_state *state; - unsigned long flags; - - if (!info || serial_paranoia_check(info, tty->device, "rs_close")) - return; - - state = info->state; - - save_flags(flags); cli(); - - if (tty_hung_up_p(filp)) { - DBG_CNT("before DEC-hung"); - MOD_DEC_USE_COUNT; - restore_flags(flags); - return; - } - -#ifdef SERIAL_DEBUG_OPEN - printk("rs_close ttys%d, count = %d\n", info->line, state->count); -#endif - if ((tty->count == 1) && (state->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. state->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, " - "state->count is %d\n", state->count); - state->count = 1; - } - if (--state->count < 0) { - printk("rs_close: bad serial port count for ttys%d: %d\n", - info->line, state->count); - state->count = 0; - } - if (state->count) { - DBG_CNT("before DEC-2"); - MOD_DEC_USE_COUNT; - restore_flags(flags); - return; - } - info->flags |= ASYNC_CLOSING; - restore_flags(flags); - /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - if (info->flags & ASYNC_NORMAL_ACTIVE) - info->state->normal_termios = *tty->termios; - if (info->flags & ASYNC_CALLOUT_ACTIVE) - info->state->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 tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - info->IER &= ~UART_IER_RLSI; - info->read_status_mask &= ~UART_LSR_DR; - if (info->flags & ASYNC_INITIALIZED) { - serial_out(info, UART_IER, info->IER); - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - rs_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) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(info->close_delay); - } - 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; -} - -/* - * rs_wait_until_sent() --- wait until the transmitter is empty - */ -static void rs_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct async_struct * info = (struct async_struct *)tty->driver_data; - unsigned long orig_jiffies, char_time; - int lsr; - - if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) - return; - - if (info->state->type == PORT_UNKNOWN) - return; - - if (info->xmit_fifo_size == 0) - return; /* Just in case.... */ - - 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 && timeout < char_time) - char_time = timeout; - /* - * If the transmitter hasn't cleared in twice the approximate - * amount of time to send the entire FIFO, it probably won't - * ever clear. This assumes the UART isn't doing flow - * control, which is currently the case. Hence, if it ever - * takes longer than info->timeout, this is probably due to a - * UART bug of some kind. So, we clamp the timeout parameter at - * 2*info->timeout. - */ - if (!timeout || timeout > 2*info->timeout) - timeout = 2*info->timeout; -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); - printk("jiff=%lu...", jiffies); -#endif - while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT)) { -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...", lsr, jiffies); -#endif - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(char_time); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); -#endif -} - -/* - * rs_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -static void rs_hangup(struct tty_struct *tty) -{ - struct async_struct * info = (struct async_struct *)tty->driver_data; - struct serial_state *state = info->state; - - if (serial_paranoia_check(info, tty->device, "rs_hangup")) - return; - - state = info->state; - - rs_flush_buffer(tty); - if (info->flags & ASYNC_CLOSING) - return; - shutdown(info); - info->event = 0; - state->count = 0; - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_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 async_struct *info) -{ - DECLARE_WAITQUEUE(wait, current); - struct serial_state *state = info->state; - int retval; - int do_clocal = 0, extra_count = 0; - unsigned long flags; - - /* - * 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 - return ((info->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -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 (state->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, state->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", - state->line, state->count); -#endif - save_flags(flags); cli(); - if (!tty_hung_up_p(filp)) { - extra_count = 1; - state->count--; - } - restore_flags(flags); - info->blocked_open++; - while (1) { - save_flags(flags); cli(); - if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && - (tty->termios->c_cflag & CBAUD)) - serial_out(info, UART_MCR, - serial_inp(info, UART_MCR) | - (UART_MCR_DTR | UART_MCR_RTS)); - restore_flags(flags); - set_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 || (serial_in(info, UART_MSR) & - UART_MSR_DCD))) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready blocking: ttys%d, count = %d\n", - info->line, state->count); -#endif - schedule(); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); - if (extra_count) - state->count++; - info->blocked_open--; -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: ttys%d, count = %d\n", - info->line, state->count); -#endif - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} - -static int get_async_struct(int line, struct async_struct **ret_info) -{ - struct async_struct *info; - struct serial_state *sstate; - - sstate = rs_table + line; - sstate->count++; - info = sstate->info; - - /* - * If the async_struct is already allocated, do the fastpath. - */ - if (info) - goto out; - - info = kmalloc(sizeof(struct async_struct), GFP_KERNEL); - if (!info) { - sstate->count--; - return -ENOMEM; - } - - memset(info, 0, sizeof(struct async_struct)); - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - init_waitqueue_head(&info->delta_msr_wait); - info->magic = SERIAL_MAGIC; - info->port = sstate->port; - info->hub6 = sstate->hub6; - info->flags = sstate->flags; - info->xmit_fifo_size = sstate->xmit_fifo_size; - info->state = sstate; - info->line = line; - info->iomem_base = sstate->iomem_base; - info->iomem_reg_shift = sstate->iomem_reg_shift; - info->io_type = sstate->io_type; - info->tqueue.routine = do_softint; - info->tqueue.data = info; - - if (sstate->info) { - kfree(info); - info = sstate->info; - } else { - sstate->info = info; - } - -out: - /* - * If this is the first open, copy over some timeouts. - */ - if (sstate->count == 1) { - info->closing_wait = sstate->closing_wait; - } - *ret_info = info; - 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 rs_open(struct tty_struct *tty, struct file * filp) -{ - struct async_struct *info; - int retval, line; - unsigned long page; - - MOD_INC_USE_COUNT; - line = minor(tty->device) - tty->driver.minor_start; - if ((line < 0) || (line >= NR_PORTS)) { - MOD_DEC_USE_COUNT; - return -ENODEV; - } - retval = get_async_struct(line, &info); - if (retval) { - MOD_DEC_USE_COUNT; - return retval; - } - tty->driver_data = info; - info->tty = tty; - if (serial_paranoia_check(info, tty->device, "rs_open")) { - MOD_DEC_USE_COUNT; - return -ENODEV; - } - -#ifdef SERIAL_DEBUG_OPEN - printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, - info->state->count); -#endif -#if (LINUX_VERSION_CODE > 0x20100) - info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; -#endif - - /* - * This relies on lock_kernel() stuff so wants tidying for 2.5 - */ - if (!tmp_buf) { - page = get_zeroed_page(GFP_KERNEL); - if (!page) { - MOD_DEC_USE_COUNT; - return -ENOMEM; - } - if (tmp_buf) - free_page(page); - else - tmp_buf = (unsigned char *) page; - } - - /* - * If the port is the middle of closing, bail out now - */ - if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); - MOD_DEC_USE_COUNT; -#ifdef SERIAL_DO_RESTART - return ((info->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); -#else - return -EAGAIN; -#endif - } - - /* - * Start up serial port - */ - retval = startup(info); - if (retval) { - MOD_DEC_USE_COUNT; - 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 - MOD_DEC_USE_COUNT; - return retval; - } - - if ((info->state->count == 1) && - (info->flags & ASYNC_SPLIT_TERMIOS)) { - if (tty->driver.subtype == SERIAL_TYPE_NORMAL) - *tty->termios = info->state->normal_termios; - else - *tty->termios = info->state->callout_termios; - change_speed(info, 0); - } -#ifdef CONFIG_SERIAL_CONSOLE - if (sercons.cflag && sercons.index == line) { - tty->termios->c_cflag = sercons.cflag; - sercons.cflag = 0; - change_speed(info, 0); - } -#endif - info->session = current->session; - info->pgrp = current->pgrp; - -#ifdef SERIAL_DEBUG_OPEN - printk("rs_open ttys%d successful...", info->line); -#endif - return 0; -} - -/* - * /proc fs routines.... - */ - -static inline int line_info(char *buf, struct serial_state *state) -{ - struct async_struct *info = state->info, scr_info; - char stat_buf[30], control, status; - int ret; - unsigned long flags; - - ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d", - state->line, uart_config[state->type].name, - state->port, state->irq); - - if (!state->port || (state->type == PORT_UNKNOWN)) { - ret += sprintf(buf+ret, "\n"); - return ret; - } - - /* - * Figure out the current RS-232 lines - */ - if (!info) { - info = &scr_info; /* This is just for serial_{in,out} */ - - info->magic = SERIAL_MAGIC; - info->port = state->port; - info->flags = state->flags; - info->hub6 = state->hub6; - info->io_type = state->io_type; - info->iomem_base = state->iomem_base; - info->iomem_reg_shift = state->iomem_reg_shift; - info->quot = 0; - info->tty = 0; - } - save_flags(flags); cli(); - status = serial_in(info, UART_MSR); - control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR); - restore_flags(flags); - - stat_buf[0] = 0; - stat_buf[1] = 0; - if (control & UART_MCR_RTS) - strcat(stat_buf, "|RTS"); - if (status & UART_MSR_CTS) - strcat(stat_buf, "|CTS"); - if (control & UART_MCR_DTR) - strcat(stat_buf, "|DTR"); - if (status & UART_MSR_DSR) - strcat(stat_buf, "|DSR"); - if (status & UART_MSR_DCD) - strcat(stat_buf, "|CD"); - if (status & UART_MSR_RI) - strcat(stat_buf, "|RI"); - - if (info->quot) { - ret += sprintf(buf+ret, " baud:%d", - state->baud_base / info->quot); - } - - ret += sprintf(buf+ret, " tx:%d rx:%d", - state->icount.tx, state->icount.rx); - - if (state->icount.frame) - ret += sprintf(buf+ret, " fe:%d", state->icount.frame); - - if (state->icount.parity) - ret += sprintf(buf+ret, " pe:%d", state->icount.parity); - - if (state->icount.brk) - ret += sprintf(buf+ret, " brk:%d", state->icount.brk); - - if (state->icount.overrun) - ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); - - /* - * Last thing is the RS-232 status lines - */ - ret += sprintf(buf+ret, " %s\n", stat_buf+1); - return ret; -} - -int rs_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - int i, len = 0, l; - off_t begin = 0; - - len += sprintf(page, "serinfo:1.0 driver:%s%s revision:%s\n", - serial_version, LOCAL_VERSTRING, serial_revdate); - for (i = 0; i < NR_PORTS && len < 4000; i++) { - l = line_info(page + len, &rs_table[i]); - len += l; - 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 + (off-begin); - return ((count < begin+len-off) ? count : begin+len-off); -} - -/* - * --------------------------------------------------------------------- - * rs_init() and friends - * - * rs_init() is called at boot-time to initialize the serial driver. - * --------------------------------------------------------------------- - */ - -/* - * This routine prints out the appropriate serial driver version - * number, and identifies which options were configured into this - * driver. - */ -static char serial_options[] __initdata = -#ifdef CONFIG_HUB6 - " HUB-6" -#define SERIAL_OPT -#endif -#ifdef CONFIG_SERIAL_MANY_PORTS - " MANY_PORTS" -#define SERIAL_OPT -#endif -#ifdef CONFIG_SERIAL_MULTIPORT - " MULTIPORT" -#define SERIAL_OPT -#endif -#ifdef CONFIG_SERIAL_SHARE_IRQ - " SHARE_IRQ" -#define SERIAL_OPT -#endif -#ifdef CONFIG_SERIAL_DETECT_IRQ - " DETECT_IRQ" -#define SERIAL_OPT -#endif -#ifdef ENABLE_SERIAL_PCI - " SERIAL_PCI" -#define SERIAL_OPT -#endif -#ifdef ENABLE_SERIAL_PNP - " ISAPNP" -#define SERIAL_OPT -#endif -#ifdef ENABLE_SERIAL_ACPI - " SERIAL_ACPI" -#define SERIAL_OPT -#endif -#ifdef SERIAL_OPT - " enabled\n"; -#else - " no serial options enabled\n"; -#endif -#undef SERIAL_OPT - -static _INLINE_ void show_serial_version(void) -{ - printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name, - serial_version, LOCAL_VERSTRING, serial_revdate, - serial_options); -} - -/* - * This routine detect the IRQ of a serial port by clearing OUT2 when - * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at - * each time, as long as no other device permanently request the IRQ. - * If no IRQ is detected, or multiple IRQ appear, this function returns 0. - * The variable "state" and the field "state->port" should not be null. - */ -static unsigned detect_uart_irq (struct serial_state * state) -{ - int irq; - unsigned long irqs; - unsigned char save_mcr, save_ier; - struct async_struct scr_info; /* serial_{in,out} because HUB6 */ - -#ifdef CONFIG_SERIAL_MANY_PORTS - unsigned char save_ICP=0; /* no warning */ - unsigned short ICP=0; - - if (state->flags & ASYNC_FOURPORT) { - ICP = (state->port & 0xFE0) | 0x01F; - save_ICP = inb_p(ICP); - outb_p(0x80, ICP); - (void) inb_p(ICP); - } -#endif - scr_info.magic = SERIAL_MAGIC; - scr_info.state = state; - scr_info.port = state->port; - scr_info.flags = state->flags; -#ifdef CONFIG_HUB6 - scr_info.hub6 = state->hub6; -#endif - scr_info.io_type = state->io_type; - scr_info.iomem_base = state->iomem_base; - scr_info.iomem_reg_shift = state->iomem_reg_shift; - - /* forget possible initially masked and pending IRQ */ - probe_irq_off(probe_irq_on()); - save_mcr = serial_inp(&scr_info, UART_MCR); - save_ier = serial_inp(&scr_info, UART_IER); - serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); - - irqs = probe_irq_on(); - serial_outp(&scr_info, UART_MCR, 0); - udelay (10); - if (state->flags & ASYNC_FOURPORT) { - serial_outp(&scr_info, UART_MCR, - UART_MCR_DTR | UART_MCR_RTS); - } else { - serial_outp(&scr_info, UART_MCR, - UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); - } - serial_outp(&scr_info, UART_IER, 0x0f); /* enable all intrs */ - (void)serial_inp(&scr_info, UART_LSR); - (void)serial_inp(&scr_info, UART_RX); - (void)serial_inp(&scr_info, UART_IIR); - (void)serial_inp(&scr_info, UART_MSR); - serial_outp(&scr_info, UART_TX, 0xFF); - udelay (20); - irq = probe_irq_off(irqs); - - serial_outp(&scr_info, UART_MCR, save_mcr); - serial_outp(&scr_info, UART_IER, save_ier); -#ifdef CONFIG_SERIAL_MANY_PORTS - if (state->flags & ASYNC_FOURPORT) - outb_p(save_ICP, ICP); -#endif - return (irq > 0)? irq : 0; -} - -/* - * This is a quickie test to see how big the FIFO is. - * It doesn't work at all the time, more's the pity. - */ -static int size_fifo(struct async_struct *info) -{ - unsigned char old_fcr, old_mcr, old_dll, old_dlm; - int count; - - old_fcr = serial_inp(info, UART_FCR); - old_mcr = serial_inp(info, UART_MCR); - serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_outp(info, UART_MCR, UART_MCR_LOOP); - serial_outp(info, UART_LCR, UART_LCR_DLAB); - old_dll = serial_inp(info, UART_DLL); - old_dlm = serial_inp(info, UART_DLM); - serial_outp(info, UART_DLL, 0x01); - serial_outp(info, UART_DLM, 0x00); - serial_outp(info, UART_LCR, 0x03); - for (count = 0; count < 256; count++) - serial_outp(info, UART_TX, count); - mdelay(20); - for (count = 0; (serial_inp(info, UART_LSR) & UART_LSR_DR) && - (count < 256); count++) - serial_inp(info, UART_RX); - serial_outp(info, UART_FCR, old_fcr); - serial_outp(info, UART_MCR, old_mcr); - serial_outp(info, UART_LCR, UART_LCR_DLAB); - serial_outp(info, UART_DLL, old_dll); - serial_outp(info, UART_DLM, old_dlm); - - return count; -} - -/* - * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. - * When this function is called we know it is at least a StarTech - * 16650 V2, but it might be one of several StarTech UARTs, or one of - * its clones. (We treat the broken original StarTech 16650 V1 as a - * 16550, and why not? Startech doesn't seem to even acknowledge its - * existence.) - * - * What evil have men's minds wrought... - */ -static void autoconfig_startech_uarts(struct async_struct *info, - struct serial_state *state, - unsigned long flags) -{ - unsigned char scratch, scratch2, scratch3, scratch4; - - /* - * First we check to see if it's an Oxford Semiconductor UART. - * - * If we have to do this here because some non-National - * Semiconductor clone chips lock up if you try writing to the - * LSR register (which serial_icr_read does) - */ - if (state->type == PORT_16550A) { - /* - * EFR [4] must be set else this test fails - * - * This shouldn't be necessary, but Mike Hudson - * (Exoray@isys.ca) claims that it's needed for 952 - * dual UART's (which are not recommended for new designs). - */ - info->ACR = 0; - serial_out(info, UART_LCR, 0xBF); - serial_out(info, UART_EFR, 0x10); - serial_out(info, UART_LCR, 0x00); - /* Check for Oxford Semiconductor 16C950 */ - scratch = serial_icr_read(info, UART_ID1); - scratch2 = serial_icr_read(info, UART_ID2); - scratch3 = serial_icr_read(info, UART_ID3); - - if (scratch == 0x16 && scratch2 == 0xC9 && - (scratch3 == 0x50 || scratch3 == 0x52 || - scratch3 == 0x54)) { - state->type = PORT_16C950; - state->revision = serial_icr_read(info, UART_REV) | - (scratch3 << 8); - return; - } - } - - /* - * We check for a XR16C850 by setting DLL and DLM to 0, and - * then reading back DLL and DLM. If DLM reads back 0x10, - * then the UART is a XR16C850 and the DLL contains the chip - * revision. If DLM reads back 0x14, then the UART is a - * XR16C854. - * - */ - - /* Save the DLL and DLM */ - - serial_outp(info, UART_LCR, UART_LCR_DLAB); - scratch3 = serial_inp(info, UART_DLL); - scratch4 = serial_inp(info, UART_DLM); - - serial_outp(info, UART_DLL, 0); - serial_outp(info, UART_DLM, 0); - scratch2 = serial_inp(info, UART_DLL); - scratch = serial_inp(info, UART_DLM); - serial_outp(info, UART_LCR, 0); - - if (scratch == 0x10 || scratch == 0x14) { - if (scratch == 0x10) - state->revision = scratch2; - state->type = PORT_16850; - return; - } - - /* Restore the DLL and DLM */ - - serial_outp(info, UART_LCR, UART_LCR_DLAB); - serial_outp(info, UART_DLL, scratch3); - serial_outp(info, UART_DLM, scratch4); - serial_outp(info, UART_LCR, 0); - /* - * We distinguish between the '654 and the '650 by counting - * how many bytes are in the FIFO. I'm using this for now, - * since that's the technique that was sent to me in the - * serial driver update, but I'm not convinced this works. - * I've had problems doing this in the past. -TYT - */ - if (size_fifo(info) == 64) - state->type = PORT_16654; - else - state->type = PORT_16650V2; -} - -/* - * This routine is called by rs_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 serial_state * state) -{ - unsigned char status1, status2, scratch, scratch2, scratch3; - unsigned char save_lcr, save_mcr; - struct async_struct *info, scr_info; - unsigned long flags; - - state->type = PORT_UNKNOWN; - -#ifdef SERIAL_DEBUG_AUTOCONF - printk("Testing ttyS%d (0x%04lx, 0x%04x)...\n", state->line, - state->port, (unsigned) state->iomem_base); -#endif - - if (!CONFIGURED_SERIAL_PORT(state)) - return; - - info = &scr_info; /* This is just for serial_{in,out} */ - - info->magic = SERIAL_MAGIC; - info->state = state; - info->port = state->port; - info->flags = state->flags; -#ifdef CONFIG_HUB6 - info->hub6 = state->hub6; -#endif - info->io_type = state->io_type; - info->iomem_base = state->iomem_base; - info->iomem_reg_shift = state->iomem_reg_shift; - - save_flags(flags); cli(); - - if (!(state->flags & ASYNC_BUGGY_UART) && - !state->iomem_base) { - /* - * 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 = serial_inp(info, UART_IER); - serial_outp(info, UART_IER, 0); -#ifdef __i386__ - outb(0xff, 0x080); -#endif - scratch2 = serial_inp(info, UART_IER); - serial_outp(info, UART_IER, 0x0F); -#ifdef __i386__ - outb(0, 0x080); -#endif - scratch3 = serial_inp(info, UART_IER); - serial_outp(info, UART_IER, scratch); - if (scratch2 || scratch3 != 0x0F) { -#ifdef SERIAL_DEBUG_AUTOCONF - printk("serial: ttyS%d: simple autoconfig failed " - "(%02x, %02x)\n", state->line, - scratch2, scratch3); -#endif - restore_flags(flags); - return; /* We failed; there's nothing here */ - } - } - - save_mcr = serial_in(info, UART_MCR); - save_lcr = serial_in(info, UART_LCR); - - /* - * Check to see if a UART is really there. Certain broken - * internal modems based on the Rockwell chipset fail this - * test, because they apparently don't implement the loopback - * test mode. So this test is skipped on the COM 1 through - * COM 4 ports. This *should* be safe, since no board - * manufacturer would be stupid enough to design a board - * that conflicts with COM 1-4 --- we hope! - */ - if (!(state->flags & ASYNC_SKIP_TEST)) { - serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A); - status1 = serial_inp(info, UART_MSR) & 0xF0; - serial_outp(info, UART_MCR, save_mcr); - if (status1 != 0x90) { -#ifdef SERIAL_DEBUG_AUTOCONF - printk("serial: ttyS%d: no UART loopback failed\n", - state->line); -#endif - restore_flags(flags); - return; - } - } - serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */ - serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */ - serial_outp(info, UART_LCR, 0); - serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); - scratch = serial_in(info, UART_IIR) >> 6; - switch (scratch) { - case 0: - state->type = PORT_16450; - break; - case 1: - state->type = PORT_UNKNOWN; - break; - case 2: - state->type = PORT_16550; - break; - case 3: - state->type = PORT_16550A; - break; - } - if (state->type == PORT_16550A) { - /* Check for Startech UART's */ - serial_outp(info, UART_LCR, UART_LCR_DLAB); - if (serial_in(info, UART_EFR) == 0) { - state->type = PORT_16650; - } else { - serial_outp(info, UART_LCR, 0xBF); - if (serial_in(info, UART_EFR) == 0) - autoconfig_startech_uarts(info, state, flags); - } - } - if (state->type == PORT_16550A) { - /* Check for TI 16750 */ - serial_outp(info, UART_LCR, save_lcr | UART_LCR_DLAB); - serial_outp(info, UART_FCR, - UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - scratch = serial_in(info, UART_IIR) >> 5; - if (scratch == 7) { - /* - * If this is a 16750, and not a cheap UART - * clone, then it should only go into 64 byte - * mode if the UART_FCR7_64BYTE bit was set - * while UART_LCR_DLAB was latched. - */ - serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(info, UART_LCR, 0); - serial_outp(info, UART_FCR, - UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - scratch = serial_in(info, UART_IIR) >> 5; - if (scratch == 6) - state->type = PORT_16750; - } - serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); - } -#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) - if (state->type == PORT_16550A) { - int i; - - for (i = 0 ; i < PORT_RSA_MAX ; ++i) { - if (!probe_rsa[i] && !force_rsa[i]) - break; - if (((probe_rsa[i] != state->port) || - check_region(state->port + UART_RSA_BASE, 16)) && - (force_rsa[i] != state->port)) - continue; - if (!enable_rsa(info)) - continue; - state->type = PORT_RSA; - state->baud_base = SERIAL_RSA_BAUD_BASE; - break; - } - } -#endif - serial_outp(info, UART_LCR, save_lcr); - if (state->type == PORT_16450) { - scratch = serial_in(info, UART_SCR); - serial_outp(info, UART_SCR, 0xa5); - status1 = serial_in(info, UART_SCR); - serial_outp(info, UART_SCR, 0x5a); - status2 = serial_in(info, UART_SCR); - serial_outp(info, UART_SCR, scratch); - - if ((status1 != 0xa5) || (status2 != 0x5a)) - state->type = PORT_8250; - } - state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size; - - if (state->type == PORT_UNKNOWN) { - restore_flags(flags); - return; - } - - if (info->port) { -#ifdef CONFIG_SERIAL_RSA - if (state->type == PORT_RSA) - request_region(info->port + UART_RSA_BASE, 16, - "serial_rsa(auto)"); - else -#endif - request_region(info->port,8,"serial(auto)"); - } - - /* - * Reset the UART. - */ -#ifdef CONFIG_SERIAL_RSA - if (state->type == PORT_RSA) - serial_outp(info, UART_RSA_FRR, 0); -#endif - serial_outp(info, UART_MCR, save_mcr); - serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT)); - serial_outp(info, UART_FCR, 0); - (void)serial_in(info, UART_RX); - serial_outp(info, UART_IER, 0); - - restore_flags(flags); -} - -int register_serial(struct serial_struct *req); -void unregister_serial(int line); - -#if (LINUX_VERSION_CODE > 0x20100) -EXPORT_SYMBOL(register_serial); -EXPORT_SYMBOL(unregister_serial); -#else -static struct symbol_table serial_syms = { -#include <linux/symtab_begin.h> - X(register_serial), - X(unregister_serial), -#include <linux/symtab_end.h> -}; -#endif - - -#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) - -static void __devinit printk_pnp_dev_id(unsigned short vendor, - unsigned short device) -{ - printk("%c%c%c%x%x%x%x", - 'A' + ((vendor >> 2) & 0x3f) - 1, - 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, - 'A' + ((vendor >> 8) & 0x1f) - 1, - (device >> 4) & 0x0f, - device & 0x0f, - (device >> 12) & 0x0f, - (device >> 8) & 0x0f); -} - -static _INLINE_ int get_pci_port(struct pci_dev *dev, - struct pci_board *board, - struct serial_struct *req, - int idx) -{ - unsigned long port; - int base_idx; - int max_port; - int offset; - - base_idx = SPCI_FL_GET_BASE(board->flags); - if (board->flags & SPCI_FL_BASE_TABLE) - base_idx += idx; - - if (board->flags & SPCI_FL_REGION_SZ_CAP) { - max_port = pci_resource_len(dev, base_idx) / 8; - if (idx >= max_port) - return 1; - } - - offset = board->first_uart_offset; - - /* Timedia/SUNIX uses a mixture of BARs and offsets */ - /* Ugh, this is ugly as all hell --- TYT */ - if(dev->vendor == PCI_VENDOR_ID_TIMEDIA ) /* 0x1409 */ - switch(idx) { - case 0: base_idx=0; - break; - case 1: base_idx=0; offset=8; - break; - case 2: base_idx=1; - break; - case 3: base_idx=1; offset=8; - break; - case 4: /* BAR 2*/ - case 5: /* BAR 3 */ - case 6: /* BAR 4*/ - case 7: base_idx=idx-2; /* BAR 5*/ - } - - /* Some Titan cards are also a little weird */ - if (dev->vendor == PCI_VENDOR_ID_TITAN && - (dev->device == PCI_DEVICE_ID_TITAN_400L || - dev->device == PCI_DEVICE_ID_TITAN_800L)) { - switch (idx) { - case 0: base_idx = 1; - break; - case 1: base_idx = 2; - break; - default: - base_idx = 4; - offset = 8 * (idx - 2); - } - - } - - port = pci_resource_start(dev, base_idx) + offset; - - if ((board->flags & SPCI_FL_BASE_TABLE) == 0) - port += idx * (board->uart_offset ? board->uart_offset : 8); - - if (IS_PCI_REGION_IOPORT(dev, base_idx)) { - req->port = port; - if (HIGH_BITS_OFFSET) - req->port_high = port >> HIGH_BITS_OFFSET; - else - req->port_high = 0; - return 0; - } - req->io_type = SERIAL_IO_MEM; - req->iomem_base = ioremap(port, board->uart_offset); - req->iomem_reg_shift = board->reg_shift; - req->port = 0; - return 0; -} - -static _INLINE_ int get_pci_irq(struct pci_dev *dev, - struct pci_board *board, - int idx) -{ - int base_idx; - - if ((board->flags & SPCI_FL_IRQRESOURCE) == 0) - return dev->irq; - - base_idx = SPCI_FL_GET_IRQBASE(board->flags); - if (board->flags & SPCI_FL_IRQ_TABLE) - base_idx += idx; - - return PCI_IRQ_RESOURCE(dev, base_idx); -} - -/* - * Common enabler code shared by both PCI and ISAPNP probes - */ -static void __devinit start_pci_pnp_board(struct pci_dev *dev, - struct pci_board *board) -{ - int k, line; - struct serial_struct serial_req; - int base_baud; - - if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) { - printk("serial: PNP device '"); - printk_pnp_dev_id(dev->vendor, dev->device); - printk("' prepare failed\n"); - return; - } - - if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) { - printk("serial: PNP device '"); - printk_pnp_dev_id(dev->vendor, dev->device); - printk("' activate failed\n"); - return; - } - - /* - * Run the initialization function, if any - */ - if (board->init_fn && ((board->init_fn)(dev, board, 1) != 0)) - return; - - /* - * Register the serial board in the array if we need to - * shutdown the board on a module unload or card removal - */ - if (DEACTIVATE_FUNC(dev) || board->init_fn) { - for (k=0; k < NR_PCI_BOARDS; k++) - if (serial_pci_board[k].dev == 0) - break; - if (k >= NR_PCI_BOARDS) - return; - serial_pci_board[k].board = *board; - serial_pci_board[k].dev = dev; - } - - base_baud = board->base_baud; - if (!base_baud) - base_baud = BASE_BAUD; - memset(&serial_req, 0, sizeof(serial_req)); - - for (k=0; k < board->num_ports; k++) { - serial_req.irq = get_pci_irq(dev, board, k); - if (get_pci_port(dev, board, &serial_req, k)) - break; - serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; -#ifdef SERIAL_DEBUG_PCI - printk("Setup PCI/PNP port: port %x, irq %d, type %d\n", - serial_req.port, serial_req.irq, serial_req.io_type); -#endif - line = register_serial(&serial_req); - if (line < 0) - break; - rs_table[line].baud_base = base_baud; - rs_table[line].dev = dev; - } -} -#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */ - -#ifdef ENABLE_SERIAL_PCI -/* - * Some PCI serial cards using the PLX 9050 PCI interface chip require - * that the card interrupt be explicitly enabled or disabled. This - * seems to be mainly needed on card using the PLX which also use I/O - * mapped memory. - */ -static int __devinit -pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable) -{ - u8 data, *p, irq_config; - int pci_config; - - irq_config = 0x41; - pci_config = PCI_COMMAND_MEMORY; - if (dev->vendor == PCI_VENDOR_ID_PANACOM) - irq_config = 0x43; - if ((dev->vendor == PCI_VENDOR_ID_PLX) && - (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { - /* - * As the megawolf cards have the int pins active - * high, and have 2 UART chips, both ints must be - * enabled on the 9050. Also, the UARTS are set in - * 16450 mode by default, so we have to enable the - * 16C950 'enhanced' mode so that we can use the deep - * FIFOs - */ - irq_config = 0x5b; - pci_config = PCI_COMMAND_MEMORY | PCI_COMMAND_IO; - } - - pci_read_config_byte(dev, PCI_COMMAND, &data); - - if (enable) - pci_write_config_byte(dev, PCI_COMMAND, - data | pci_config); - - /* enable/disable interrupts */ - p = ioremap(pci_resource_start(dev, 0), 0x80); - writel(enable ? irq_config : 0x00, (unsigned long)p + 0x4c); - iounmap(p); - - if (!enable) - pci_write_config_byte(dev, PCI_COMMAND, - data & ~pci_config); - return 0; -} - - -/* - * SIIG serial cards have an PCI interface chip which also controls - * the UART clocking frequency. Each UART can be clocked independently - * (except cards equiped with 4 UARTs) and initial clocking settings - * are stored in the EEPROM chip. It can cause problems because this - * version of serial driver doesn't support differently clocked UART's - * on single PCI card. To prevent this, initialization functions set - * high frequency clocking for all UART's on given card. It is safe (I - * hope) because it doesn't touch EEPROM settings to prevent conflicts - * with other OSes (like M$ DOS). - * - * SIIG support added by Andrey Panin <pazke@mail.tp.ru>, 10/1999 - * - * There is two family of SIIG serial cards with different PCI - * interface chip and different configuration methods: - * - 10x cards have control registers in IO and/or memory space; - * - 20x cards have control registers in standard PCI configuration space. - */ - -#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) -#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) - -static int __devinit -pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable) -{ - u16 data, *p; - - if (!enable) return 0; - - p = ioremap(pci_resource_start(dev, 0), 0x80); - - switch (dev->device & 0xfff8) { - case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ - data = 0xffdf; - break; - case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ - data = 0xf7ff; - break; - default: /* 1S1P, 4S */ - data = 0xfffb; - break; - } - - writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28); - iounmap(p); - return 0; -} - -#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) -#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) - -static int __devinit -pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable) -{ - u8 data; - - if (!enable) return 0; - - /* Change clock frequency for the first UART. */ - pci_read_config_byte(dev, 0x6f, &data); - pci_write_config_byte(dev, 0x6f, data & 0xef); - - /* If this card has 2 UART, we have to do the same with second UART. */ - if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) || - ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) { - pci_read_config_byte(dev, 0x73, &data); - pci_write_config_byte(dev, 0x73, data & 0xef); - } - return 0; -} - -/* Added for EKF Intel i960 serial boards */ -static int __devinit -pci_inteli960ni_fn(struct pci_dev *dev, - struct pci_board *board, - int enable) -{ - unsigned long oldval; - - if (!(pci_get_subdevice(dev) & 0x1000)) - return(-1); - - if (!enable) /* is there something to deinit? */ - return(0); - -#ifdef SERIAL_DEBUG_PCI - printk(KERN_DEBUG " Subsystem ID %lx (intel 960)\n", - (unsigned long) board->subdevice); -#endif - /* is firmware started? */ - pci_read_config_dword(dev, 0x44, (void*) &oldval); - if (oldval == 0x00001000L) { /* RESET value */ - printk(KERN_DEBUG "Local i960 firmware missing"); - return(-1); - } - return(0); -} - -/* - * Timedia has an explosion of boards, and to avoid the PCI table from - * growing *huge*, we use this function to collapse some 70 entries - * in the PCI table into one, for sanity's and compactness's sake. - */ -static unsigned short timedia_single_port[] = { - 0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0 }; -static unsigned short timedia_dual_port[] = { - 0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085, - 0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, - 0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, - 0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079, - 0xD079, 0 }; -static unsigned short timedia_quad_port[] = { - 0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, - 0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, - 0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056, - 0xB157, 0 }; -static unsigned short timedia_eight_port[] = { - 0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, - 0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0 }; -static struct timedia_struct { - int num; - unsigned short *ids; -} timedia_data[] = { - { 1, timedia_single_port }, - { 2, timedia_dual_port }, - { 4, timedia_quad_port }, - { 8, timedia_eight_port }, - { 0, 0 } -}; - -static int __devinit -pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable) -{ - int i, j; - unsigned short *ids; - - if (!enable) - return 0; - - for (i=0; timedia_data[i].num; i++) { - ids = timedia_data[i].ids; - for (j=0; ids[j]; j++) { - if (pci_get_subdevice(dev) == ids[j]) { - board->num_ports = timedia_data[i].num; - return 0; - } - } - } - return 0; -} - -static int __devinit -pci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable) -{ - __set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/10); - return 0; -} - -/* - * This is the configuration table for all of the PCI serial boards - * which we support. It is directly indexed by the pci_board_num_t enum - * value, which is encoded in the pci_device_id PCI probe table's - * driver_data member. - */ -enum pci_board_num_t { - pbn_b0_1_115200, - pbn_default = 0, - - pbn_b0_2_115200, - pbn_b0_4_115200, - - pbn_b0_1_921600, - pbn_b0_2_921600, - pbn_b0_4_921600, - - pbn_b0_bt_1_115200, - pbn_b0_bt_2_115200, - pbn_b0_bt_1_460800, - pbn_b0_bt_2_460800, - - pbn_b1_1_115200, - pbn_b1_2_115200, - pbn_b1_4_115200, - pbn_b1_8_115200, - - pbn_b1_2_921600, - pbn_b1_4_921600, - pbn_b1_8_921600, - - pbn_b1_2_1382400, - pbn_b1_4_1382400, - pbn_b1_8_1382400, - - pbn_b2_8_115200, - pbn_b2_4_460800, - pbn_b2_8_460800, - pbn_b2_16_460800, - pbn_b2_4_921600, - pbn_b2_8_921600, - - pbn_b2_bt_1_115200, - pbn_b2_bt_2_115200, - pbn_b2_bt_4_115200, - pbn_b2_bt_2_921600, - - pbn_panacom, - pbn_panacom2, - pbn_panacom4, - pbn_plx_romulus, - pbn_oxsemi, - pbn_timedia, - pbn_intel_i960, - pbn_sgi_ioc3, -#ifdef CONFIG_DDB5074 - pbn_nec_nile4, -#endif -#if 0 - pbn_dci_pccom8, -#endif - pbn_xircom_combo, - - pbn_siig10x_0, - pbn_siig10x_1, - pbn_siig10x_2, - pbn_siig10x_4, - pbn_siig20x_0, - pbn_siig20x_2, - pbn_siig20x_4, - - pbn_computone_4, - pbn_computone_6, - pbn_computone_8, -}; - -static struct pci_board pci_boards[] __devinitdata = { - /* - * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, - * Offset to get to next UART's registers, - * Register shift to use for memory-mapped I/O, - * Initialization function, first UART offset - */ - - /* Generic serial board, pbn_b0_1_115200, pbn_default */ - { SPCI_FL_BASE0, 1, 115200 }, /* pbn_b0_1_115200, - pbn_default */ - - { SPCI_FL_BASE0, 2, 115200 }, /* pbn_b0_2_115200 */ - { SPCI_FL_BASE0, 4, 115200 }, /* pbn_b0_4_115200 */ - - { SPCI_FL_BASE0, 1, 921600 }, /* pbn_b0_1_921600 */ - { SPCI_FL_BASE0, 2, 921600 }, /* pbn_b0_2_921600 */ - { SPCI_FL_BASE0, 4, 921600 }, /* pbn_b0_4_921600 */ - - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ - - { SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */ - { SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */ - { SPCI_FL_BASE1, 4, 115200 }, /* pbn_b1_4_115200 */ - { SPCI_FL_BASE1, 8, 115200 }, /* pbn_b1_8_115200 */ - - { SPCI_FL_BASE1, 2, 921600 }, /* pbn_b1_2_921600 */ - { SPCI_FL_BASE1, 4, 921600 }, /* pbn_b1_4_921600 */ - { SPCI_FL_BASE1, 8, 921600 }, /* pbn_b1_8_921600 */ - - { SPCI_FL_BASE1, 2, 1382400 }, /* pbn_b1_2_1382400 */ - { SPCI_FL_BASE1, 4, 1382400 }, /* pbn_b1_4_1382400 */ - { SPCI_FL_BASE1, 8, 1382400 }, /* pbn_b1_8_1382400 */ - - { SPCI_FL_BASE2, 8, 115200 }, /* pbn_b2_8_115200 */ - { SPCI_FL_BASE2, 4, 460800 }, /* pbn_b2_4_460800 */ - { SPCI_FL_BASE2, 8, 460800 }, /* pbn_b2_8_460800 */ - { SPCI_FL_BASE2, 16, 460800 }, /* pbn_b2_16_460800 */ - { SPCI_FL_BASE2, 4, 921600 }, /* pbn_b2_4_921600 */ - { SPCI_FL_BASE2, 8, 921600 }, /* pbn_b2_8_921600 */ - - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */ - - { SPCI_FL_BASE2, 2, 921600, /* IOMEM */ /* pbn_panacom */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_panacom2 */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_panacom4 */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */ - 0x20, 2, pci_plx9050_fn, 0x03 }, - /* This board uses the size of PCI Base region 0 to - * signal now many ports are available */ - { SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */ - { SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */ - 0, 0, pci_timedia_fn }, - /* EKF addition for i960 Boards form EKF with serial port */ - { SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ /* pbn_intel_i960 */ - 8<<2, 2, pci_inteli960ni_fn, 0x10000}, - { SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE, /* pbn_sgi_ioc3 */ - 1, 458333, 0, 0, 0, 0x20178 }, -#ifdef CONFIG_DDB5074 - /* - * NEC Vrc-5074 (Nile 4) builtin UART. - * Conditionally compiled in since this is a motherboard device. - */ - { SPCI_FL_BASE0, 1, 520833, /* pbn_nec_nile4 */ - 64, 3, NULL, 0x300 }, -#endif -#if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ /* pbn_dci_pccom8 */ - { SPCI_FL_BASE3, 8, 115200, 8 }, -#endif - { SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */ - 0, 0, pci_xircom_fn }, - - { SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siig10x_2 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siig10x_4 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE0, 1, 921600, /* pbn_siix20x_0 */ - 0, 0, pci_siig20x_fn }, - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siix20x_2 */ - 0, 0, pci_siig20x_fn }, - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */ - 0, 0, pci_siig20x_fn }, - - { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */ - 0x40, 2, NULL, 0x200 }, - { SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */ - 0x40, 2, NULL, 0x200 }, - { SPCI_FL_BASE0, 8, 921600, /* IOMEM */ /* pbn_computone_8 */ - 0x40, 2, NULL, 0x200 }, -}; - -/* - * Given a complete unknown PCI device, try to use some heuristics to - * guess what the configuration might be, based on the pitiful PCI - * serial specs. Returns 0 on success, 1 on failure. - */ -static int __devinit serial_pci_guess_board(struct pci_dev *dev, - struct pci_board *board) -{ - int num_iomem = 0, num_port = 0, first_port = -1; - int i; - - /* - * If it is not a communications device or the programming - * interface is greater than 6, give up. - * - * (Should we try to make guesses for multiport serial devices - * later?) - */ - if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && - ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || - (dev->class & 0xff) > 6) - return 1; - - for (i=0; i < 6; i++) { - if (IS_PCI_REGION_IOPORT(dev, i)) { - num_port++; - if (first_port == -1) - first_port = i; - } - if (IS_PCI_REGION_IOMEM(dev, i)) - num_iomem++; - } - - /* - * If there is 1 or 0 iomem regions, and exactly one port, use - * it. - */ - if (num_iomem <= 1 && num_port == 1) { - board->flags = first_port; - return 0; - } - return 1; -} - -static int __devinit serial_init_one(struct pci_dev *dev, - const struct pci_device_id *ent) -{ - struct pci_board *board, tmp; - int rc; - - board = &pci_boards[ent->driver_data]; - - rc = pci_enable_device(dev); - if (rc) return rc; - - if (ent->driver_data == pbn_default && - serial_pci_guess_board(dev, board)) - return -ENODEV; - else if (serial_pci_guess_board(dev, &tmp) == 0) { - printk(KERN_INFO "Redundant entry in serial pci_table. " - "Please send the output of\n" - "lspci -vv, this message (%04x,%04x,%04x,%04x)\n" - "and the manufacturer and name of " - "serial board or modem board\n" - "to serial-pci-info@lists.sourceforge.net.\n", - dev->vendor, dev->device, - pci_get_subvendor(dev), pci_get_subdevice(dev)); - } - - start_pci_pnp_board(dev, board); - - return 0; -} - -static void __devexit serial_remove_one(struct pci_dev *dev) -{ - int i; - - /* - * Iterate through all of the ports finding those that belong - * to this PCI device. - */ - for(i = 0; i < NR_PORTS; i++) { - if (rs_table[i].dev != dev) - continue; - unregister_serial(i); - rs_table[i].dev = 0; - } - /* - * Now execute any board-specific shutdown procedure - */ - for (i=0; i < NR_PCI_BOARDS; i++) { - struct pci_board_inst *brd = &serial_pci_board[i]; - - if (serial_pci_board[i].dev != dev) - continue; - if (brd->board.init_fn) - (brd->board.init_fn)(brd->dev, &brd->board, 0); - if (DEACTIVATE_FUNC(brd->dev)) - (DEACTIVATE_FUNC(brd->dev))(brd->dev); - serial_pci_board[i].dev = 0; - } -} - - -static struct pci_device_id serial_pci_tbl[] __devinitdata = { - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, - pbn_b1_8_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, - pbn_b1_4_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, - pbn_b1_2_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, - pbn_b1_8_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, - pbn_b1_4_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, - pbn_b1_2_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, - pbn_b1_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, - pbn_b1_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, - pbn_b1_2_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, - pbn_b1_4_921600 }, - - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_1_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_115200 }, - - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - /* VScom SPCOM800, from sl@s.pl */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_921600 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_4_921600 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_KEYSPAN, - PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, - pbn_panacom }, - { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_panacom4 }, - { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_panacom2 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, - pbn_b2_4_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, - pbn_b2_8_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, - pbn_b2_16_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, - pbn_b2_16_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIRAS, - PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, - pbn_b2_4_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIRAS, - PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, - pbn_b2_8_460800 }, - /* Megawolf Romulus PCI Serial Card, from Mike Hudson */ - /* (Exoray@isys.ca) */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, - 0x10b5, 0x106a, 0, 0, - pbn_plx_romulus }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_4_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_2_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_115200 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_2_115200 }, - - /* Digitan DS560-558, from jimd@esoft.com */ - { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_115200 }, - - /* 3Com US Robotics 56k Voice Internal PCI model 5610 */ - { PCI_VENDOR_ID_USR, 0x1008, - PCI_ANY_ID, PCI_ANY_ID, }, - - /* Titan Electronic cards */ - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_2_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1, 1, 921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, - /* The 400L and 800L have a custom hack in get_pci_port */ - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE_TABLE, 4, 921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE_TABLE, 8, 921600 }, - - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_1 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_1 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_1 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, - - /* Computone devices submitted by Doug McNash dmcnash@computone.com */ - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, - 0, 0, pbn_computone_4 }, - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, - 0, 0, pbn_computone_8 }, - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, - 0, 0, pbn_computone_6 }, - - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi }, - { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, - PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia }, - - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_1_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_1_460800 }, - - /* RAStel 2 port modem, gerg@moreton.com.au */ - { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - - /* EKF addition for i960 Boards form EKF with serial port */ - { PCI_VENDOR_ID_INTEL, 0x1960, - 0xE4BF, PCI_ANY_ID, 0, 0, - pbn_intel_i960 }, - - /* Xircom Cardbus/Ethernet combos */ - { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_xircom_combo }, - - /* - * Untested PCI modems, sent in from various folks... - */ - - /* Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de> */ - { PCI_VENDOR_ID_ROCKWELL, 0x1004, - 0x1048, 0x1500, 0, 0, - pbn_b1_1_115200 }, - - { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, - 0xFF00, 0, 0, 0, - pbn_sgi_ioc3 }, - -#ifdef CONFIG_DDB5074 - /* - * NEC Vrc-5074 (Nile 4) builtin UART. - * Conditionally compiled in since this is a motherboard device. - */ - { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NILE4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_nec_nile4 }, -#endif - -#if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ - { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_dci_pccom8 }, -#endif - - { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00, }, - { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_MODEM << 8, 0xffff00, }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, serial_pci_tbl); - -static struct pci_driver serial_pci_driver = { - name: "serial", - probe: serial_init_one, - remove: __devexit_p(serial_remove_one), - id_table: serial_pci_tbl, -}; - - -/* - * Query PCI space for known serial boards - * If found, add them to the PCI device space in rs_table[] - * - * Accept a maximum of eight boards - * - */ -static void __devinit probe_serial_pci(void) -{ -#ifdef SERIAL_DEBUG_PCI - printk(KERN_DEBUG "Entered probe_serial_pci()\n"); -#endif - - /* Register call PCI serial devices. Null out - * the driver name upon failure, as a signal - * not to attempt to unregister the driver later - */ - if (pci_module_init (&serial_pci_driver) != 0) - serial_pci_driver.name = ""; - -#ifdef SERIAL_DEBUG_PCI - printk(KERN_DEBUG "Leaving probe_serial_pci() (probe finished)\n"); -#endif - return; -} - -#endif /* ENABLE_SERIAL_PCI */ - -#ifdef ENABLE_SERIAL_PNP - -struct pnp_board { - unsigned short vendor; - unsigned short device; -}; - -static struct pnp_board pnp_devices[] __devinitdata = { - /* Archtek America Corp. */ - /* Archtek SmartLink Modem 3334BT Plug & Play */ - { ISAPNP_VENDOR('A', 'A', 'C'), ISAPNP_DEVICE(0x000F) }, - /* Anchor Datacomm BV */ - /* SXPro 144 External Data Fax Modem Plug & Play */ - { ISAPNP_VENDOR('A', 'D', 'C'), ISAPNP_DEVICE(0x0001) }, - /* SXPro 288 External Data Fax Modem Plug & Play */ - { ISAPNP_VENDOR('A', 'D', 'C'), ISAPNP_DEVICE(0x0002) }, - /* Rockwell 56K ACF II Fax+Data+Voice Modem */ - { ISAPNP_VENDOR('A', 'K', 'Y'), ISAPNP_DEVICE(0x1021) }, - /* AZT3005 PnP SOUND DEVICE */ - { ISAPNP_VENDOR('A', 'Z', 'T'), ISAPNP_DEVICE(0x4001) }, - /* Best Data Products Inc. Smart One 336F PnP Modem */ - { ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336) }, - /* Boca Research */ - /* Boca Complete Ofc Communicator 14.4 Data-FAX */ - { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x0A49) }, - /* Boca Research 33,600 ACF Modem */ - { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x1400) }, - /* Boca 33.6 Kbps Internal FD34FSVD */ - { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x3400) }, - /* Boca 33.6 Kbps Internal FD34FSVD */ - { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x0A49) }, - /* Best Data Products Inc. Smart One 336F PnP Modem */ - { ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336) }, - /* Computer Peripherals Inc */ - /* EuroViVa CommCenter-33.6 SP PnP */ - { ISAPNP_VENDOR('C', 'P', 'I'), ISAPNP_DEVICE(0x4050) }, - /* Creative Labs */ - /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ - { ISAPNP_VENDOR('C', 'T', 'L'), ISAPNP_DEVICE(0x3001) }, - /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ - { ISAPNP_VENDOR('C', 'T', 'L'), ISAPNP_DEVICE(0x3011) }, - /* Creative */ - /* Creative Modem Blaster Flash56 DI5601-1 */ - { ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x1032) }, - /* Creative Modem Blaster V.90 DI5660 */ - { ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x2001) }, - /* FUJITSU */ - /* Fujitsu 33600 PnP-I2 R Plug & Play */ - { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0202) }, - /* Fujitsu FMV-FX431 Plug & Play */ - { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0205) }, - /* Fujitsu 33600 PnP-I4 R Plug & Play */ - { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0206) }, - /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ - { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0209) }, - /* Archtek America Corp. */ - /* Archtek SmartLink Modem 3334BT Plug & Play */ - { ISAPNP_VENDOR('G', 'V', 'C'), ISAPNP_DEVICE(0x000F) }, - /* Hayes */ - /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x0001) }, - /* Hayes Optima 336 V.34 + FAX + Voice PnP */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x000C) }, - /* Hayes Optima 336B V.34 + FAX + Voice PnP */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x000D) }, - /* Hayes Accura 56K Ext Fax Modem PnP */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5670) }, - /* Hayes Accura 56K Ext Fax Modem PnP */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5674) }, - /* Hayes Accura 56K Fax Modem PnP */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5675) }, - /* Hayes 288, V.34 + FAX */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0xF000) }, - /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0xF001) }, - /* IBM */ - /* IBM Thinkpad 701 Internal Modem Voice */ - { ISAPNP_VENDOR('I', 'B', 'M'), ISAPNP_DEVICE(0x0033) }, - /* Intertex */ - /* Intertex 28k8 33k6 Voice EXT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xC801) }, - /* Intertex 33k6 56k Voice EXT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xC901) }, - /* Intertex 28k8 33k6 Voice SP EXT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xD801) }, - /* Intertex 33k6 56k Voice SP EXT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xD901) }, - /* Intertex 28k8 33k6 Voice SP INT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF401) }, - /* Intertex 28k8 33k6 Voice SP EXT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF801) }, - /* Intertex 33k6 56k Voice SP EXT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF901) }, - /* Kortex International */ - /* KORTEX 28800 Externe PnP */ - { ISAPNP_VENDOR('K', 'O', 'R'), ISAPNP_DEVICE(0x4522) }, - /* KXPro 33.6 Vocal ASVD PnP */ - { ISAPNP_VENDOR('K', 'O', 'R'), ISAPNP_DEVICE(0xF661) }, - /* Lasat */ - /* LASAT Internet 33600 PnP */ - { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x4040) }, - /* Lasat Safire 560 PnP */ - { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x4540) }, - /* Lasat Safire 336 PnP */ - { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x5440) }, - /* Microcom, Inc. */ - /* Microcom TravelPorte FAST V.34 Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x281) }, - /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0336) }, - /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0339) }, - /* Microcom DeskPorte 28.8P Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0342) }, - /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0500) }, - /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0501) }, - /* Microcom DeskPorte 28.8S Internal Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0502) }, - /* Motorola */ - /* Motorola BitSURFR Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1105) }, - /* Motorola TA210 Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1111) }, - /* Motorola HMTA 200 (ISDN) Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1114) }, - /* Motorola BitSURFR Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1115) }, - /* Motorola Lifestyle 28.8 Internal */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1190) }, - /* Motorola V.3400 Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1501) }, - /* Motorola Lifestyle 28.8 V.34 Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1502) }, - /* Motorola Power 28.8 V.34 Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1505) }, - /* Motorola ModemSURFR External 28.8 Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1509) }, - /* Motorola Premier 33.6 Desktop Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x150A) }, - /* Motorola VoiceSURFR 56K External PnP */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x150F) }, - /* Motorola ModemSURFR 56K External PnP */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1510) }, - /* Motorola ModemSURFR 56K Internal PnP */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1550) }, - /* Motorola ModemSURFR Internal 28.8 Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1560) }, - /* Motorola Premier 33.6 Internal Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1580) }, - /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15B0) }, - /* Motorola VoiceSURFR 56K Internal PnP */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15F0) }, - /* Com 1 */ - /* Deskline K56 Phone System PnP */ - { ISAPNP_VENDOR('M', 'V', 'X'), ISAPNP_DEVICE(0x00A1) }, - /* PC Rider K56 Phone System PnP */ - { ISAPNP_VENDOR('M', 'V', 'X'), ISAPNP_DEVICE(0x00F2) }, - /* Pace 56 Voice Internal Plug & Play Modem */ - { ISAPNP_VENDOR('P', 'M', 'C'), ISAPNP_DEVICE(0x2430) }, - /* Generic */ - /* Generic standard PC COM port */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0500) }, - /* Generic 16550A-compatible COM port */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0501) }, - /* Compaq 14400 Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC000) }, - /* Compaq 2400/9600 Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC001) }, - /* Dial-Up Networking Serial Cable between 2 PCs */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC031) }, - /* Dial-Up Networking Parallel Cable between 2 PCs */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC032) }, - /* Standard 9600 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC100) }, - /* Standard 14400 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC101) }, - /* Standard 28800 bps Modem*/ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC102) }, - /* Standard Modem*/ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC103) }, - /* Standard 9600 bps Modem*/ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC104) }, - /* Standard 14400 bps Modem*/ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC105) }, - /* Standard 28800 bps Modem*/ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC106) }, - /* Standard Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC107) }, - /* Standard 9600 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC108) }, - /* Standard 14400 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC109) }, - /* Standard 28800 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10A) }, - /* Standard Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10B) }, - /* Standard 9600 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10C) }, - /* Standard 14400 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10D) }, - /* Standard 28800 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10E) }, - /* Standard Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10F) }, - /* Standard PCMCIA Card Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x2000) }, - /* Rockwell */ - /* Modular Technology */ - /* Rockwell 33.6 DPF Internal PnP */ - /* Modular Technology 33.6 Internal PnP */ - { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x0030) }, - /* Kortex International */ - /* KORTEX 14400 Externe PnP */ - { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x0100) }, - /* Viking Components, Inc */ - /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ - { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x4920) }, - /* Rockwell */ - /* British Telecom */ - /* Modular Technology */ - /* Rockwell 33.6 DPF External PnP */ - /* BT Prologue 33.6 External PnP */ - /* Modular Technology 33.6 External PnP */ - { ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x00A0) }, - /* Viking 56K FAX INT */ - { ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x0262) }, - /* SupraExpress 28.8 Data/Fax PnP modem */ - { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1310) }, - /* SupraExpress 33.6 Data/Fax PnP modem */ - { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1421) }, - /* SupraExpress 33.6 Data/Fax PnP modem */ - { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1590) }, - /* SupraExpress 33.6 Data/Fax PnP modem */ - { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1760) }, - /* Phoebe Micro */ - /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ - { ISAPNP_VENDOR('T', 'E', 'X'), ISAPNP_DEVICE(0x0011) }, - /* Archtek America Corp. */ - /* Archtek SmartLink Modem 3334BT Plug & Play */ - { ISAPNP_VENDOR('U', 'A', 'C'), ISAPNP_DEVICE(0x000F) }, - /* 3Com Corp. */ - /* Gateway Telepath IIvi 33.6 */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0000) }, - /* Sportster Vi 14.4 PnP FAX Voicemail */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0004) }, - /* U.S. Robotics 33.6K Voice INT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0006) }, - /* U.S. Robotics 33.6K Voice EXT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0007) }, - /* U.S. Robotics 33.6K Voice INT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2002) }, - /* U.S. Robotics 56K Voice INT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2070) }, - /* U.S. Robotics 56K Voice EXT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2080) }, - /* U.S. Robotics 56K FAX INT */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3031) }, - /* U.S. Robotics 56K Voice INT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3070) }, - /* U.S. Robotics 56K Voice EXT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3080) }, - /* U.S. Robotics 56K Voice INT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3090) }, - /* U.S. Robotics 56K Message */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9100) }, - /* U.S. Robotics 56K FAX EXT PnP*/ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9160) }, - /* U.S. Robotics 56K FAX INT PnP*/ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9170) }, - /* U.S. Robotics 56K Voice EXT PnP*/ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9180) }, - /* U.S. Robotics 56K Voice INT PnP*/ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9190) }, - { 0, } -}; - -static void inline avoid_irq_share(struct pci_dev *dev) -{ - int i; - unsigned long map = 0x1FF8; - struct serial_state *state = rs_table; - struct isapnp_irq *irq; - struct isapnp_resources *res = dev->sysdata; - - for (i = 0; i < NR_PORTS; i++) { - if (state->type != PORT_UNKNOWN) - clear_bit(state->irq, &map); - state++; - } - - for ( ; res; res = res->alt) - for(irq = res->irq; irq; irq = irq->next) - irq->map = map; -} - -static char *modem_names[] __devinitdata = { - "MODEM", "Modem", "modem", "FAX", "Fax", "fax", - "56K", "56k", "K56", "33.6", "28.8", "14.4", - "33,600", "28,800", "14,400", "33.600", "28.800", "14.400", - "33600", "28800", "14400", "V.90", "V.34", "V.32", 0 -}; - -static int __devinit check_name(char *name) -{ - char **tmp = modem_names; - - while (*tmp) { - if (strstr(name, *tmp)) - return 1; - tmp++; - } - return 0; -} - -static int inline check_compatible_id(struct pci_dev *dev) -{ - int i; - for (i = 0; i < DEVICE_COUNT_COMPATIBLE; i++) - if ((dev->vendor_compatible[i] == - ISAPNP_VENDOR('P', 'N', 'P')) && - (swab16(dev->device_compatible[i]) >= 0xc000) && - (swab16(dev->device_compatible[i]) <= 0xdfff)) - return 0; - return 1; -} - -/* - * Given a complete unknown ISA PnP device, try to use some heuristics to - * detect modems. Currently use such heuristic set: - * - dev->name or dev->bus->name must contain "modem" substring; - * - device must have only one IO region (8 byte long) with base adress - * 0x2e8, 0x3e8, 0x2f8 or 0x3f8. - * - * Such detection looks very ugly, but can detect at least some of numerous - * ISA PnP modems, alternatively we must hardcode all modems in pnp_devices[] - * table. - */ -static int _INLINE_ serial_pnp_guess_board(struct pci_dev *dev, - struct pci_board *board) -{ - struct isapnp_resources *res = (struct isapnp_resources *)dev->sysdata; - struct isapnp_resources *resa; - - if (!(check_name(dev->name) || check_name(dev->bus->name)) && - !(check_compatible_id(dev))) - return 1; - - if (!res || res->next) - return 1; - - for (resa = res->alt; resa; resa = resa->alt) { - struct isapnp_port *port; - for (port = res->port; port; port = port->next) - if ((port->size == 8) && - ((port->min == 0x2f8) || - (port->min == 0x3f8) || - (port->min == 0x2e8) || - (port->min == 0x3e8))) - return 0; - } - - return 1; -} - -static void __devinit probe_serial_pnp(void) -{ - struct pci_dev *dev = NULL; - struct pnp_board *pnp_board; - struct pci_board board; - -#ifdef SERIAL_DEBUG_PNP - printk("Entered probe_serial_pnp()\n"); -#endif - if (!isapnp_present()) { -#ifdef SERIAL_DEBUG_PNP - printk("Leaving probe_serial_pnp() (no isapnp)\n"); -#endif - return; - } - - isapnp_for_each_dev(dev) { - if (dev->active) - continue; - - memset(&board, 0, sizeof(board)); - board.flags = SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT; - board.num_ports = 1; - board.base_baud = 115200; - - for (pnp_board = pnp_devices; pnp_board->vendor; pnp_board++) - if ((dev->vendor == pnp_board->vendor) && - (dev->device == pnp_board->device)) - break; - - if (pnp_board->vendor) { - /* Special case that's more efficient to hardcode */ - if ((pnp_board->vendor == ISAPNP_VENDOR('A', 'K', 'Y') && - pnp_board->device == ISAPNP_DEVICE(0x1021))) - board.flags |= SPCI_FL_NO_SHIRQ; - } else { - if (serial_pnp_guess_board(dev, &board)) - continue; - } - - if (board.flags & SPCI_FL_NO_SHIRQ) - avoid_irq_share(dev); - start_pci_pnp_board(dev, &board); - } - -#ifdef SERIAL_DEBUG_PNP - printk("Leaving probe_serial_pnp() (probe finished)\n"); -#endif - return; -} - -#endif /* ENABLE_SERIAL_PNP */ - -/* - * The serial driver boot-time initialization code! - */ -static int __init rs_init(void) -{ - int i; - struct serial_state * state; - - init_bh(SERIAL_BH, do_serial_bh); - init_timer(&serial_timer); - serial_timer.function = rs_timer; - mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); - - for (i = 0; i < NR_IRQS; i++) { - IRQ_ports[i] = 0; - IRQ_timeout[i] = 0; -#ifdef CONFIG_SERIAL_MULTIPORT - memset(&rs_multiport[i], 0, - sizeof(struct rs_multiport_struct)); -#endif - } -#ifdef CONFIG_SERIAL_CONSOLE - /* - * The interrupt of the serial console port - * can't be shared. - */ - if (sercons.flags & CON_CONSDEV) { - for(i = 0; i < NR_PORTS; i++) - if (i != sercons.index && - rs_table[i].irq == rs_table[sercons.index].irq) - rs_table[i].irq = 0; - } -#endif - show_serial_version(); - - /* Initialize the tty_driver structure */ - - memset(&serial_driver, 0, sizeof(struct tty_driver)); - serial_driver.magic = TTY_DRIVER_MAGIC; -#if (LINUX_VERSION_CODE > 0x20100) - serial_driver.driver_name = "serial"; -#endif -#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) - serial_driver.name = "tts/%d"; -#else - serial_driver.name = "ttyS"; -#endif - serial_driver.major = TTY_MAJOR; - serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET; - 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 | TTY_DRIVER_NO_DEVFS; - 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.put_char = rs_put_char; - 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; -#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ - serial_driver.break_ctl = rs_break; -#endif -#if (LINUX_VERSION_CODE >= 131343) - serial_driver.send_xchar = rs_send_xchar; - serial_driver.wait_until_sent = rs_wait_until_sent; - serial_driver.read_proc = rs_read_proc; -#endif - - /* - * The callout device is just like normal device except for - * major number and the subtype code. - */ - callout_driver = serial_driver; -#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) - callout_driver.name = "cua/%d"; -#else - callout_driver.name = "cua"; -#endif - callout_driver.major = TTYAUX_MAJOR; - callout_driver.subtype = SERIAL_TYPE_CALLOUT; -#if (LINUX_VERSION_CODE >= 131343) - callout_driver.read_proc = 0; - callout_driver.proc_entry = 0; -#endif - - 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 (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { - state->magic = SSTATE_MAGIC; - state->line = i; - state->type = PORT_UNKNOWN; - state->custom_divisor = 0; - state->close_delay = 5*HZ/10; - state->closing_wait = 30*HZ; - state->callout_termios = callout_driver.init_termios; - state->normal_termios = serial_driver.init_termios; - state->icount.cts = state->icount.dsr = - state->icount.rng = state->icount.dcd = 0; - state->icount.rx = state->icount.tx = 0; - state->icount.frame = state->icount.parity = 0; - state->icount.overrun = state->icount.brk = 0; - state->irq = irq_cannonicalize(state->irq); - if (state->hub6) - state->io_type = SERIAL_IO_HUB6; - if (state->port && check_region(state->port,8)) - continue; -#ifdef CONFIG_MCA - if ((state->flags & ASYNC_BOOT_ONLYMCA) && !MCA_bus) - continue; -#endif - if (state->flags & ASYNC_BOOT_AUTOCONF) - autoconfig(state); - } - for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { - if (state->type == PORT_UNKNOWN) - continue; - if ( (state->flags & ASYNC_BOOT_AUTOCONF) - && (state->flags & ASYNC_AUTO_IRQ) - && (state->port != 0 || state->iomem_base != 0)) - state->irq = detect_uart_irq(state); - if (state->io_type == SERIAL_IO_MEM) { - printk(KERN_INFO"ttyS%02d%s at 0x%px (irq = %d) is a %s\n", - state->line + SERIAL_DEV_OFFSET, - (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", - state->iomem_base, state->irq, - uart_config[state->type].name); - } - else { - printk(KERN_INFO "ttyS%02d%s at 0x%04lx (irq = %d) is a %s\n", - state->line + SERIAL_DEV_OFFSET, - (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", - state->port, state->irq, - uart_config[state->type].name); - } - tty_register_devfs(&serial_driver, 0, - serial_driver.minor_start + state->line); - tty_register_devfs(&callout_driver, 0, - callout_driver.minor_start + state->line); - } -#ifdef ENABLE_SERIAL_PCI - probe_serial_pci(); -#endif -#ifdef ENABLE_SERIAL_PNP - probe_serial_pnp(); -#endif - return 0; -} - -/* - * This is for use by architectures that know their serial console - * attributes only at run time. Not to be invoked after rs_init(). - */ -int __init early_serial_setup(struct serial_struct *req) -{ - int i = req->line; - - if (i >= NR_IRQS) - return(-ENOENT); - rs_table[i].magic = 0; - rs_table[i].baud_base = req->baud_base; - rs_table[i].port = req->port; - if (HIGH_BITS_OFFSET) - rs_table[i].port += (unsigned long) req->port_high << - HIGH_BITS_OFFSET; - rs_table[i].irq = req->irq; - rs_table[i].flags = req->flags; - rs_table[i].close_delay = req->close_delay; - rs_table[i].io_type = req->io_type; - rs_table[i].hub6 = req->hub6; - rs_table[i].iomem_base = req->iomem_base; - rs_table[i].iomem_reg_shift = req->iomem_reg_shift; - rs_table[i].type = req->type; - rs_table[i].xmit_fifo_size = req->xmit_fifo_size; - rs_table[i].custom_divisor = req->custom_divisor; - rs_table[i].closing_wait = req->closing_wait; - return(0); -} - -/* - * register_serial and unregister_serial allows for 16x50 serial ports to be - * configured at run-time, to support PCMCIA modems. - */ - -/** - * register_serial - configure a 16x50 serial port at runtime - * @req: request structure - * - * Configure the serial port specified by the request. If the - * port exists and is in use an error is returned. If the port - * is not currently in the table it is added. - * - * The port is then probed and if neccessary the IRQ is autodetected - * If this fails an error is returned. - * - * On success the port is ready to use and the line number is returned. - */ - -int register_serial(struct serial_struct *req) -{ - int i; - unsigned long flags; - struct serial_state *state; - struct async_struct *info; - unsigned long port; - - port = req->port; - if (HIGH_BITS_OFFSET) - port += (unsigned long) req->port_high << HIGH_BITS_OFFSET; - - save_flags(flags); cli(); - for (i = 0; i < NR_PORTS; i++) { - if ((rs_table[i].port == port) && - (rs_table[i].iomem_base == req->iomem_base)) - break; - } -#ifdef __i386__ - if (i == NR_PORTS) { - for (i = 4; i < NR_PORTS; i++) - if ((rs_table[i].type == PORT_UNKNOWN) && - (rs_table[i].count == 0)) - break; - } -#endif - if (i == NR_PORTS) { - for (i = 0; i < NR_PORTS; i++) - if ((rs_table[i].type == PORT_UNKNOWN) && - (rs_table[i].count == 0)) - break; - } - if (i == NR_PORTS) { - restore_flags(flags); - return -1; - } - state = &rs_table[i]; - if (rs_table[i].count) { - restore_flags(flags); - printk("Couldn't configure serial #%d (port=%ld,irq=%d): " - "device already open\n", i, port, req->irq); - return -1; - } - state->irq = req->irq; - state->port = port; - state->flags = req->flags; - state->io_type = req->io_type; - state->iomem_base = req->iomem_base; - state->iomem_reg_shift = req->iomem_reg_shift; - if (req->baud_base) - state->baud_base = req->baud_base; - if ((info = state->info) != NULL) { - info->port = port; - info->flags = req->flags; - info->io_type = req->io_type; - info->iomem_base = req->iomem_base; - info->iomem_reg_shift = req->iomem_reg_shift; - } - autoconfig(state); - if (state->type == PORT_UNKNOWN) { - restore_flags(flags); - printk("register_serial(): autoconfig failed\n"); - return -1; - } - restore_flags(flags); - - if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state)) - state->irq = detect_uart_irq(state); - - printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n", - state->line + SERIAL_DEV_OFFSET, - state->iomem_base ? "iomem" : "port", - state->iomem_base ? (unsigned long)state->iomem_base : - state->port, state->irq, uart_config[state->type].name); - tty_register_devfs(&serial_driver, 0, - serial_driver.minor_start + state->line); - tty_register_devfs(&callout_driver, 0, - callout_driver.minor_start + state->line); - return state->line + SERIAL_DEV_OFFSET; -} - -/** - * unregister_serial - deconfigure a 16x50 serial port - * @line: line to deconfigure - * - * The port specified is deconfigured and its resources are freed. Any - * user of the port is disconnected as if carrier was dropped. Line is - * the port number returned by register_serial(). - */ - -void unregister_serial(int line) -{ - unsigned long flags; - struct serial_state *state = &rs_table[line]; - - save_flags(flags); cli(); - if (state->info && state->info->tty) - tty_hangup(state->info->tty); - state->type = PORT_UNKNOWN; - printk(KERN_INFO "ttyS%02d unloaded\n", state->line); - /* These will be hidden, because they are devices that will no longer - * be available to the system. (ie, PCMCIA modems, once ejected) - */ - tty_unregister_devfs(&serial_driver, - serial_driver.minor_start + state->line); - tty_unregister_devfs(&callout_driver, - callout_driver.minor_start + state->line); - restore_flags(flags); -} - -static void __exit rs_fini(void) -{ - unsigned long flags; - int e1, e2; - int i; - struct async_struct *info; - - /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ - del_timer_sync(&serial_timer); - save_flags(flags); cli(); - 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 ((info = rs_table[i].info)) { - rs_table[i].info = NULL; - kfree(info); - } - if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) { -#ifdef CONFIG_SERIAL_RSA - if (rs_table[i].type == PORT_RSA) - release_region(rs_table[i].port + - UART_RSA_BASE, 16); - else -#endif - release_region(rs_table[i].port, 8); - } -#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) - if (rs_table[i].iomem_base) - iounmap(rs_table[i].iomem_base); -#endif - } -#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) - for (i=0; i < NR_PCI_BOARDS; i++) { - struct pci_board_inst *brd = &serial_pci_board[i]; - - if (serial_pci_board[i].dev == 0) - continue; - if (brd->board.init_fn) - (brd->board.init_fn)(brd->dev, &brd->board, 0); - if (DEACTIVATE_FUNC(brd->dev)) - (DEACTIVATE_FUNC(brd->dev))(brd->dev); - } -#endif - if (tmp_buf) { - unsigned long pg = (unsigned long) tmp_buf; - tmp_buf = NULL; - free_page(pg); - } - -#ifdef ENABLE_SERIAL_PCI - if (serial_pci_driver.name[0]) - pci_unregister_driver (&serial_pci_driver); -#endif -} - -module_init(rs_init); -module_exit(rs_fini); -MODULE_DESCRIPTION("Standard/generic (dumb) serial driver"); -MODULE_AUTHOR("Theodore Ts'o <tytso@mit.edu>"); -MODULE_LICENSE("GPL"); - - -/* - * ------------------------------------------------------------ - * Serial console driver - * ------------------------------------------------------------ - */ -#ifdef CONFIG_SERIAL_CONSOLE - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -static struct async_struct async_sercons; - -/* - * Wait for transmitter & holding register to empty - */ -static inline void wait_for_xmitr(struct async_struct *info) -{ - unsigned int status, tmout = 1000000; - - do { - status = serial_in(info, UART_LSR); - - if (status & UART_LSR_BI) - lsr_break_flag = UART_LSR_BI; - - if (--tmout == 0) - break; - } while((status & BOTH_EMPTY) != BOTH_EMPTY); - - /* Wait for flow control if necessary */ - if (info->flags & ASYNC_CONS_FLOW) { - tmout = 1000000; - while (--tmout && - ((serial_in(info, UART_MSR) & UART_MSR_CTS) == 0)); - } -} - - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * The console must be locked when we get here. - */ -static void serial_console_write(struct console *co, const char *s, - unsigned count) -{ - static struct async_struct *info = &async_sercons; - int ier; - unsigned i; - - /* - * First save the IER then disable the interrupts - */ - ier = serial_in(info, UART_IER); - serial_out(info, UART_IER, 0x00); - - /* - * Now, do each character - */ - for (i = 0; i < count; i++, s++) { - wait_for_xmitr(info); - - /* - * Send the character out. - * If a LF, also do CR... - */ - serial_out(info, UART_TX, *s); - if (*s == 10) { - wait_for_xmitr(info); - serial_out(info, UART_TX, 13); - } - } - - /* - * Finally, Wait for transmitter & holding register to empty - * and restore the IER - */ - wait_for_xmitr(info); - serial_out(info, UART_IER, ier); -} - -static kdev_t serial_console_device(struct console *c) -{ - return mk_kdev(TTY_MAJOR, 64 + c->index); -} - -/* - * Setup initial baud/bits/parity/flow control. We do two things here: - * - construct a cflag setting for the first rs_open() - * - initialize the serial port - * Return non-zero if we didn't find a serial port. - */ -static int __init serial_console_setup(struct console *co, char *options) -{ - static struct async_struct *info; - struct serial_state *state; - unsigned cval; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int doflow = 0; - int cflag = CREAD | HUPCL | CLOCAL; - int quot = 0; - char *s; - - if (options) { - baud = simple_strtoul(options, NULL, 10); - s = options; - while(*s >= '0' && *s <= '9') - s++; - if (*s) parity = *s++; - if (*s) bits = *s++ - '0'; - if (*s) doflow = (*s++ == 'r'); - } - - /* - * Now construct a cflag setting. - */ - switch(baud) { - case 1200: - cflag |= B1200; - break; - case 2400: - cflag |= B2400; - break; - case 4800: - cflag |= B4800; - break; - case 19200: - cflag |= B19200; - break; - case 38400: - cflag |= B38400; - break; - case 57600: - cflag |= B57600; - break; - case 115200: - cflag |= B115200; - break; - case 9600: - default: - cflag |= B9600; - /* - * Set this to a sane value to prevent a divide error - */ - baud = 9600; - break; - } - switch(bits) { - case 7: - cflag |= CS7; - break; - default: - case 8: - cflag |= CS8; - break; - } - switch(parity) { - case 'o': case 'O': - cflag |= PARODD; - break; - case 'e': case 'E': - cflag |= PARENB; - break; - } - co->cflag = cflag; - - /* - * Divisor, bytesize and parity - */ - state = rs_table + co->index; - if (doflow) - state->flags |= ASYNC_CONS_FLOW; - info = &async_sercons; - info->magic = SERIAL_MAGIC; - info->state = state; - info->port = state->port; - info->flags = state->flags; -#ifdef CONFIG_HUB6 - info->hub6 = state->hub6; -#endif - info->io_type = state->io_type; - info->iomem_base = state->iomem_base; - info->iomem_reg_shift = state->iomem_reg_shift; - quot = state->baud_base / baud; - cval = cflag & (CSIZE | CSTOPB); -#if defined(__powerpc__) || defined(__alpha__) - cval >>= 8; -#else /* !__powerpc__ && !__alpha__ */ - cval >>= 4; -#endif /* !__powerpc__ && !__alpha__ */ - if (cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(cflag & PARODD)) - cval |= UART_LCR_EPAR; - - /* - * Disable UART interrupts, set DTR and RTS high - * and set speed. - */ - serial_out(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ - serial_out(info, UART_DLL, quot & 0xff); /* LS of divisor */ - serial_out(info, UART_DLM, quot >> 8); /* MS of divisor */ - serial_out(info, UART_LCR, cval); /* reset DLAB */ - serial_out(info, UART_IER, 0); - serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); - - /* - * If we read 0xff from the LSR, there is no UART here. - */ - if (serial_in(info, UART_LSR) == 0xff) - return -1; - - return 0; -} - -static struct console sercons = { - name: "ttyS", - write: serial_console_write, - device: serial_console_device, - setup: serial_console_setup, - flags: CON_PRINTBUFFER, - index: -1, -}; - -/* - * Register console. - */ -void __init serial_console_init(void) -{ - register_console(&sercons); -} -#endif - -/* - Local variables: - compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" - End: -*/ diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 6789e5ecf6b5..ce8b07ccc5f4 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -150,8 +150,7 @@ extern void con3215_init(void); extern void tty3215_init(void); extern void tub3270_con_init(void); extern void tub3270_init(void); -extern void rs285_console_init(void); -extern void sa1100_rs_console_init(void); +extern void uart_console_init(void); extern void sgi_serial_console_init(void); extern void sci_console_init(void); extern void tx3912_console_init(void); @@ -456,11 +455,12 @@ void do_tty_hangup(void *data) } file_list_unlock(); - /* FIXME! What are the locking issues here? This may me overdoing things.. */ + /* FIXME! What are the locking issues here? This may me overdoing things.. + * this question is especially important now that we've removed the irqlock. */ { unsigned long flags; - save_flags(flags); cli(); + __save_flags(flags); __cli(); // FIXME: is this safe? if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); if (tty->driver.flush_buffer) @@ -468,7 +468,7 @@ void do_tty_hangup(void *data) if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); - restore_flags(flags); + __restore_flags(flags); // FIXME: is this safe? } wake_up_interruptible(&tty->write_wait); @@ -1900,7 +1900,7 @@ static void flush_to_ldisc(void *private_) fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE; tty->flip.buf_num = 0; - save_flags(flags); cli(); + __save_flags(flags); __cli(); // FIXME: is this safe? tty->flip.char_buf_ptr = tty->flip.char_buf; tty->flip.flag_buf_ptr = tty->flip.flag_buf; } else { @@ -1908,13 +1908,13 @@ static void flush_to_ldisc(void *private_) fp = tty->flip.flag_buf; tty->flip.buf_num = 1; - save_flags(flags); cli(); + __save_flags(flags); __cli(); // FIXME: is this safe? tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE; tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE; } count = tty->flip.count; tty->flip.count = 0; - restore_flags(flags); + __restore_flags(flags); // FIXME: is this safe? tty->ldisc.receive_buf(tty, cp, fp, count); } @@ -2220,18 +2220,12 @@ void __init console_init(void) #ifdef CONFIG_STDIO_CONSOLE stdio_console_init(); #endif -#ifdef CONFIG_SERIAL_21285_CONSOLE - rs285_console_init(); -#endif -#ifdef CONFIG_SERIAL_SA1100_CONSOLE - sa1100_rs_console_init(); +#ifdef CONFIG_SERIAL_CORE_CONSOLE + uart_console_init(); #endif #ifdef CONFIG_ARC_CONSOLE arc_console_init(); #endif -#ifdef CONFIG_SERIAL_AMBA_CONSOLE - ambauart_console_init(); -#endif #ifdef CONFIG_SERIAL_TX3912_CONSOLE tx3912_console_init(); #endif diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 6fe1ef20e409..68662c975de4 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -97,7 +97,7 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios int canon_change; struct termios old_termios = *tty->termios; - cli(); + __cli(); // FIXME: is this safe? *tty->termios = *new_termios; unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON; @@ -107,7 +107,7 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios tty->canon_data = 0; tty->erasing = 0; } - sti(); + __sti(); // FIXME: is this safe? if (canon_change && !L_ICANON(tty) && tty->read_cnt) /* Get characters left over from canonical mode. */ wake_up_interruptible(&tty->read_wait); diff --git a/drivers/char/vt.c b/drivers/char/vt.c index e59c0cef09e7..87a0b5904263 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -113,8 +113,8 @@ _kd_mksound(unsigned int hz, unsigned int ticks) if (hz > 20 && hz < 32767) count = 1193180 / hz; - save_flags(flags); - cli(); + __save_flags(flags); // FIXME: is this safe? + __cli(); del_timer(&sound_timer); if (count) { /* enable counter 2 */ @@ -131,7 +131,7 @@ _kd_mksound(unsigned int hz, unsigned int ticks) } } else kd_nosound(0); - restore_flags(flags); + __restore_flags(flags); return; } diff --git a/drivers/ide/hd.c b/drivers/ide/hd.c index a34945a2382f..f5fb26632eb2 100644 --- a/drivers/ide/hd.c +++ b/drivers/ide/hd.c @@ -83,9 +83,6 @@ static void bad_rw_intr(void); static char recalibrate[MAX_HD]; static char special_op[MAX_HD]; -static int access_count[MAX_HD]; -static char busy[MAX_HD]; -static DECLARE_WAIT_QUEUE_HEAD(busy_wait); static int reset; static int hd_error; @@ -644,7 +641,7 @@ static int hd_ioctl(struct inode * inode, struct file * file, g.heads = hd_info[dev].head; g.sectors = hd_info[dev].sect; g.cylinders = hd_info[dev].cyl; - g.start = get_start_sect(inode->i_rdev); + g.start = get_start_sect(inode->i_bdev); return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0; } @@ -653,14 +650,6 @@ static int hd_ioctl(struct inode * inode, struct file * file, return -EACCES; return revalidate_hddisk(inode->i_rdev, 1); - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKROSET: - case BLKROGET: - case BLKFLSBUF: - case BLKPG: - return blk_ioctl(inode->i_bdev, cmd, arg); - default: return -EINVAL; } @@ -668,14 +657,9 @@ static int hd_ioctl(struct inode * inode, struct file * file, static int hd_open(struct inode * inode, struct file * filp) { - int target; - target = DEVICE_NR(inode->i_rdev); - + int target = DEVICE_NR(inode->i_rdev); if (target >= NR_HD) return -ENODEV; - while (busy[target]) - sleep_on(&busy_wait); - access_count[target]++; return 0; } @@ -683,12 +667,6 @@ static int hd_open(struct inode * inode, struct file * filp) * Releasing a block device means we sync() it, so that it can safely * be forgotten about... */ -static int hd_release(struct inode * inode, struct file * file) -{ - int target = DEVICE_NR(inode->i_rdev); - access_count[target]--; - return 0; -} extern struct block_device_operations hd_fops; @@ -715,7 +693,6 @@ static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs) static struct block_device_operations hd_fops = { .open = hd_open, - .release = hd_release, .ioctl = hd_ioctl, }; @@ -854,13 +831,9 @@ int __init hd_init(void) return 0; } -#define DEVICE_BUSY busy[target] -#define USAGE access_count[target] #define CAPACITY (hd_info[target].head*hd_info[target].sect*hd_info[target].cyl) /* We assume that the BIOS parameters do not change, so the disk capacity will not change */ -#undef MAYBE_REINIT -#define GENDISK_STRUCT hd_gendisk /* * This routine is called to flush all partitions and partition tables @@ -872,36 +845,15 @@ int __init hd_init(void) */ static int revalidate_hddisk(kdev_t dev, int maxusage) { - int target; - struct gendisk * gdev; - int res; - long flags; - - target = DEVICE_NR(dev); - gdev = &GENDISK_STRUCT; - - save_flags(flags); - cli(); - if (DEVICE_BUSY || USAGE > maxusage) { - restore_flags(flags); - return -EBUSY; - } - DEVICE_BUSY = 1; - restore_flags(flags); - - res = wipe_partitions(dev); - if (res) - goto leave; - -#ifdef MAYBE_REINIT - MAYBE_REINIT; -#endif - - grok_partitions(dev, CAPACITY); - -leave: - DEVICE_BUSY = 0; - wake_up(&busy_wait); + int target = DEVICE_NR(dev); + kdev_t device = mk_kdev(MAJOR_NR, target << 6); + int res = dev_lock_part(device); + if (res < 0) + return res; + res = wipe_partitions(device); + if (!res) + grok_partitions(device, CAPACITY); + dev_unlock_part(device); return res; } diff --git a/drivers/ide/hptraid.c b/drivers/ide/hptraid.c index 9e8e07b35a2f..43b45c6e06eb 100644 --- a/drivers/ide/hptraid.c +++ b/drivers/ide/hptraid.c @@ -122,11 +122,6 @@ static int hptraid_ioctl(struct inode *inode, struct file *file, return 0; } - case BLKROSET: - case BLKROGET: - case BLKSSZGET: - return blk_ioctl(inode->i_bdev, cmd, arg); - default: return -EINVAL; }; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index a83f8e7ee6d6..ce566e1b0645 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -1105,9 +1105,6 @@ static int ide_open(struct inode * inode, struct file * filp) if (drive->driver == NULL) ide_driver_module(); - while (drive->busy) - sleep_on(&drive->wqueue); - ++drive->usage; if (ata_ops(drive) && ata_ops(drive)->open) return ata_ops(drive)->open(inode, filp, drive); diff --git a/drivers/ide/ioctl.c b/drivers/ide/ioctl.c index 1c86cbc9c177..003b743b4772 100644 --- a/drivers/ide/ioctl.c +++ b/drivers/ide/ioctl.c @@ -343,19 +343,6 @@ int ata_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned case BLKRRPART: /* Re-read partition tables */ return ata_revalidate(inode->i_rdev); - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKROSET: - case BLKROGET: - case BLKFLSBUF: - case BLKSSZGET: - case BLKPG: - case BLKELVGET: - case BLKELVSET: - case BLKBSZGET: - case BLKBSZSET: - return blk_ioctl(inode->i_bdev, cmd, arg); - /* Now check whatever this particular ioctl has a device type * specific implementation. */ diff --git a/drivers/ide/main.c b/drivers/ide/main.c index 24cc8bc9a75e..6c09337f334f 100644 --- a/drivers/ide/main.c +++ b/drivers/ide/main.c @@ -249,28 +249,22 @@ struct ata_device *get_info_ptr(kdev_t i_rdev) */ int ata_revalidate(kdev_t i_rdev) { + kdev_t device = mk_kdev(major(i_rdev), minor(i_rdev) & ~PARTN_MASK); struct ata_device *drive; - unsigned long flags; int res; - if ((drive = get_info_ptr(i_rdev)) == NULL) + if ((drive = get_info_ptr(device)) == NULL) return -ENODEV; - /* FIXME: The locking here doesn't make the slightest sense! */ - spin_lock_irqsave(&ide_lock, flags); - - if (drive->busy || (drive->usage > 1)) { - spin_unlock_irqrestore(&ide_lock, flags); - - return -EBUSY; - } - - drive->busy = 1; MOD_INC_USE_COUNT; - spin_unlock_irqrestore(&ide_lock, flags); + res = dev_lock_part(device); + if (res < 0) { + MOD_DEC_USE_COUNT; + return res; + } - res = wipe_partitions(i_rdev); + res = wipe_partitions(device); if (!res) { if (ata_ops(drive) && ata_ops(drive)->revalidate) { ata_get(ata_ops(drive)); @@ -281,14 +275,11 @@ int ata_revalidate(kdev_t i_rdev) ata_ops(drive)->revalidate(drive); ata_put(ata_ops(drive)); } else - grok_partitions(i_rdev, ata_capacity(drive)); + grok_partitions(device, ata_capacity(drive)); } - drive->busy = 0; - wake_up(&drive->wqueue); - + dev_unlock_part(device); MOD_DEC_USE_COUNT; - return res; } @@ -1091,18 +1082,18 @@ int ide_unregister_subdriver(struct ata_device *drive) { unsigned long flags; - save_flags(flags); /* all CPUs */ - cli(); /* all CPUs */ + __save_flags(flags); // FIXME: is this safe? + __cli(); #if 0 if (__MOD_IN_USE(ata_ops(drive)->owner)) { - restore_flags(flags); + __restore_flags(flags); // FIXME: is this safe? return 1; } #endif if (drive->usage || drive->busy || !ata_ops(drive)) { - restore_flags(flags); /* all CPUs */ + __restore_flags(flags); // FIXME: is this safe? return 1; } @@ -1111,7 +1102,7 @@ int ide_unregister_subdriver(struct ata_device *drive) #endif drive->driver = NULL; - restore_flags(flags); /* all CPUs */ + __restore_flags(flags); // FIXME: is this safe? return 0; } diff --git a/drivers/ide/pdcraid.c b/drivers/ide/pdcraid.c index 4d6f81507582..d1bd67ba7f45 100644 --- a/drivers/ide/pdcraid.c +++ b/drivers/ide/pdcraid.c @@ -152,11 +152,6 @@ static int pdcraid_ioctl(struct inode *inode, struct file *file, return 0; } - case BLKROSET: - case BLKROGET: - case BLKSSZGET: - return blk_ioctl(inode->i_bdev, cmd, arg); - default: printk("Invalid ioctl \n"); return -EINVAL; diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index 3338ed620e69..3a0244704002 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -3097,7 +3097,7 @@ static int sbp2scsi_reset (Scsi_Cmnd *SCpnt) /* * Called by scsi stack to get bios parameters (used by fdisk, and at boot). */ -static int sbp2scsi_biosparam (Scsi_Disk *disk, kdev_t dev, int geom[]) +static int sbp2scsi_biosparam (Scsi_Disk *disk, struct block_device *dev, int geom[]) { int heads, sectors, cylinders; diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h index 7d6da4a61dd5..12e6dde4944b 100644 --- a/drivers/ieee1394/sbp2.h +++ b/drivers/ieee1394/sbp2.h @@ -542,7 +542,7 @@ static int sbp2_max_speed_and_size(struct sbp2scsi_host_info *hi, struct scsi_id static int sbp2scsi_detect (Scsi_Host_Template *tpnt); static const char *sbp2scsi_info (struct Scsi_Host *host); void sbp2scsi_setup(char *str, int *ints); -static int sbp2scsi_biosparam (Scsi_Disk *disk, kdev_t dev, int geom[]); +static int sbp2scsi_biosparam (Scsi_Disk *disk, struct block_device *dev, int geom[]); static int sbp2scsi_abort (Scsi_Cmnd *SCpnt); static int sbp2scsi_reset (Scsi_Cmnd *SCpnt); static int sbp2scsi_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); diff --git a/drivers/md/lvm.c b/drivers/md/lvm.c index c44a1b8a74b2..ab05b01ee4ce 100644 --- a/drivers/md/lvm.c +++ b/drivers/md/lvm.c @@ -872,17 +872,6 @@ static int lvm_blk_ioctl(struct inode *inode, struct file *file, return -EFAULT; break; - - case BLKFLSBUF: - /* flush buffer cache */ - if (!capable(CAP_SYS_ADMIN)) return -EACCES; - - P_IOCTL("BLKFLSBUF\n"); - - fsync_bdev(inode->i_bdev); - invalidate_buffers(inode->i_rdev); - break; - case HDIO_GETGEO: /* get disk geometry */ P_IOCTL("%s -- lvm_blk_ioctl -- HDIO_GETGEO\n", lvm_name); diff --git a/drivers/md/md.c b/drivers/md/md.c index 549063948426..d08613aea3bb 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -2241,28 +2241,6 @@ static int md_ioctl(struct inode *inode, struct file *file, autostart_arrays(); goto done; #endif - - case BLKGETSIZE: /* Return device size */ - if (!arg) { - err = -EINVAL; - MD_BUG(); - goto abort; - } - err = put_user(md_hd_struct[minor].nr_sects, - (unsigned long *) arg); - goto done; - - case BLKGETSIZE64: /* Return device size */ - err = put_user((u64)md_hd_struct[minor].nr_sects << 9, - (u64 *) arg); - goto done; - - case BLKFLSBUF: - case BLKBSZGET: - case BLKBSZSET: - err = blk_ioctl(inode->i_bdev, cmd, arg); - goto abort; - default:; } @@ -2386,7 +2364,7 @@ static int md_ioctl(struct inode *inode, struct file *file, (short *) &loc->cylinders); if (err) goto abort_unlock; - err = put_user (get_start_sect(dev), + err = put_user (get_start_sect(inode->i_bdev), (long *) &loc->start); goto done_unlock; } diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c index 8d03a096d507..5c049fb8dfdd 100644 --- a/drivers/message/fusion/mptscsih.c +++ b/drivers/message/fusion/mptscsih.c @@ -3576,7 +3576,7 @@ mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *m */ int -mptscsih_bios_param(Disk * disk, kdev_t dev, int *ip) +mptscsih_bios_param(Disk * disk, struct block_device *dev, int *ip) { int size; diff --git a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h index c26ec0fe1845..b0c55517d1e6 100644 --- a/drivers/message/fusion/mptscsih.h +++ b/drivers/message/fusion/mptscsih.h @@ -201,7 +201,7 @@ extern int x_scsi_host_reset(Scsi_Cmnd *); extern int x_scsi_old_abort(Scsi_Cmnd *); extern int x_scsi_old_reset(Scsi_Cmnd *, unsigned int); #endif -extern int x_scsi_bios_param(Disk *, kdev_t, int *); +extern int x_scsi_bios_param(Disk *, struct block_device *, int *); extern void x_scsi_select_queue_depths(struct Scsi_Host *, Scsi_Device *); extern void x_scsi_taskmgmt_bh(void *); diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c index bf11c559d43f..b82610655b49 100644 --- a/drivers/message/i2o/i2o_block.c +++ b/drivers/message/i2o/i2o_block.c @@ -1050,13 +1050,7 @@ static int do_i2ob_revalidate(kdev_t dev, int maxu) return -EBUSY; } - for( i = 15; i>=0 ; i--) - { - int m = minor+i; - invalidate_device(mk_kdev(MAJOR_NR, m), 1); - i2ob_gendisk.part[m].start_sect = 0; - i2ob_gendisk.part[m].nr_sects = 0; - } + wipe_partitions(mk_kdev(MAJOR_NR, minor), 1); /* * Do a physical check and then reconfigure @@ -1089,7 +1083,7 @@ static int i2ob_ioctl(struct inode *inode, struct file *file, int u = minor(inode->i_rdev) & 0xF0; i2o_block_biosparam(i2ob_sizes[u]<<1, &g.cylinders, &g.heads, &g.sectors); - g.start = get_start_sect(inode->i_rdev); + g.start = get_start_sect(inode->i_bdev); return copy_to_user((void *)arg, &g, sizeof(g)) ? -EFAULT : 0; } @@ -1099,14 +1093,6 @@ static int i2ob_ioctl(struct inode *inode, struct file *file, return -EACCES; return do_i2ob_revalidate(inode->i_rdev,1); - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKFLSBUF: - case BLKROSET: - case BLKROGET: - case BLKPG: - return blk_ioctl(inode->i_bdev, cmd, arg); - default: return -EINVAL; } @@ -1815,7 +1801,6 @@ int i2o_block_init(void) * Now fill in the boiler plate */ - blk_size[MAJOR_NR] = i2ob_sizes; blk_dev[MAJOR_NR].queue = i2ob_get_queue; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), i2ob_request); diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c index a25603a79897..1c119e8a44ce 100644 --- a/drivers/message/i2o/i2o_scsi.c +++ b/drivers/message/i2o/i2o_scsi.c @@ -902,7 +902,7 @@ int i2o_scsi_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags) * This is anyones guess quite frankly. */ -int i2o_scsi_bios_param(Disk * disk, kdev_t dev, int *ip) +int i2o_scsi_bios_param(Disk * disk, struct block_device *dev, int *ip) { int size; diff --git a/drivers/message/i2o/i2o_scsi.h b/drivers/message/i2o/i2o_scsi.h index c62d2849b118..1979284f10c6 100644 --- a/drivers/message/i2o/i2o_scsi.h +++ b/drivers/message/i2o/i2o_scsi.h @@ -20,7 +20,7 @@ extern int i2o_scsi_command(Scsi_Cmnd *); extern int i2o_scsi_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); extern int i2o_scsi_abort(Scsi_Cmnd *); extern int i2o_scsi_reset(Scsi_Cmnd *, unsigned int); -extern int i2o_scsi_bios_param(Disk *, kdev_t, int *); +extern int i2o_scsi_bios_param(Disk *, struct block_device *, int *); extern void i2o_scsi_setup(char *str, int *ints); extern int i2o_scsi_release(struct Scsi_Host *host); diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index de201fdea95e..c68d10d829f9 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -880,9 +880,6 @@ static release_t ftl_close(struct inode *inode, struct file *file) DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor); - /* Flush all writes */ - invalidate_device(inode->i_rdev, 1); - /* Wait for any pending erase operations to complete */ if (part->mtd->sync) part->mtd->sync(part->mtd); @@ -1126,22 +1123,11 @@ static int ftl_ioctl(struct inode *inode, struct file *file, put_user(1, (char *)&geo->heads); put_user(8, (char *)&geo->sectors); put_user((sect>>3), (short *)&geo->cylinders); - put_user(get_start_sect(inode->i_rdev), (u_long *)&geo->start); - break; - case BLKGETSIZE: - ret = put_user(ftl_hd[minor].nr_sects, (unsigned long *)arg); - break; - case BLKGETSIZE64: - ret = put_user((u64)ftl_hd[minor].nr_sects << 9, (u64 *)arg); + put_user(get_start_sect(inode->i_bdev), (u_long *)&geo->start); break; case BLKRRPART: ret = ftl_reread_partitions(inode->i_rdev); break; - case BLKROSET: - case BLKROGET: - case BLKFLSBUF: - ret = blk_ioctl(inode->i_bdev, cmd, arg); - break; default: ret = -EINVAL; } @@ -1157,25 +1143,20 @@ static int ftl_ioctl(struct inode *inode, struct file *file, static int ftl_reread_partitions(kdev_t dev) { - int minor = minor(dev); - partition_t *part = myparts[minor >> 4]; - int res; - - DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor); - if ((atomic_read(&part->open) > 1)) { - return -EBUSY; - } - - res = wipe_partitions(dev); - if (res) - goto leave; - - scan_header(part); - - register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART, - &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE); - - return res; + int minor = minor(dev); + partition_t *part = myparts[minor >> 4]; + kdev_t device = mk_kdev(MAJOR_NR, minor & ~15); + int res = dev_lock_part(device); + if (rec < 0) + return res; + res = wipe_partitions(device); + if (!res) { + scan_header(part); + grok_partitions(device, + le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE); + } + dev_unlock_part(device); + return res; } /*====================================================================== @@ -1285,13 +1266,13 @@ static void ftl_notify_add(struct mtd_info *mtd) partition->mtd = mtd; - if ((scan_header(partition) == 0) && - (build_maps(partition) == 0)) { - + if ((scan_header(partition) == 0) && (build_maps(partition) == 0)) { partition->state = FTL_FORMATTED; atomic_set(&partition->open, 0); myparts[device] = partition; - ftl_reread_partitions(device << 4); + register_disk(&ftl_gendisk, mk_kdev(MAJOR_NR, device << 4), + MAX_PART, &ftl_blk_fops, + le32_to_cpu(partition->header.FormattedSize)/SECTOR_SIZE); #ifdef PCMCIA_DEBUG printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n", le32_to_cpu(partition->header.FormattedSize) >> 10); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 56535e9a41e9..928ebe405251 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -354,8 +354,6 @@ static release_t mtdblock_release(struct inode *inode, struct file *file) if (inode == NULL) release_return(-ENODEV); - invalidate_device(inode->i_rdev, 1); - dev = minor(inode->i_rdev); mtdblk = mtdblks[dev]; diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index 4939a5cb8145..72482560b696 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -76,8 +76,6 @@ static release_t mtdblock_release(struct inode *inode, struct file *file) if (inode == NULL) release_return(-ENODEV); - invalidate_device(inode->i_rdev, 1); - dev = minor(inode->i_rdev); mtd = __get_mtd_device(NULL, dev); diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index bbe20a1721b3..f54e17f14540 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -115,7 +115,6 @@ static void NFTL_setup(struct mtd_info *mtd) #endif /* linux stuff */ - nftl->usecount = 0; nftl->cylinders = 1024; nftl->heads = 16; @@ -153,8 +152,9 @@ static void NFTL_setup(struct mtd_info *mtd) #if LINUX_VERSION_CODE < 0x20328 resetup_one_dev(&nftl_gendisk, firstfree); #else - grok_partitions(mk_kdev(MAJOR_NR,firstfree<<NFTL_PARTN_BITS), - nftl->nr_sects); + register_disk(&nftl_gendisk, + mk_kdev(MAJOR_NR,firstfree<<NFTL_PARTN_BITS), + 1<<NFTL_PARTN_BITS, &nftl_fops, nftl->nr_sects); #endif } @@ -787,7 +787,7 @@ static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd g.heads = nftl->heads; g.sectors = nftl->sectors; g.cylinders = nftl->cylinders; - g.start = get_start_sect(inode->i_rdev); + g.start = get_start_sect(inode->i_bdev); return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0; } case BLKFLSBUF: @@ -800,29 +800,18 @@ static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd case BLKRRPART: if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (nftl->usecount > 1) return -EBUSY; - /* - * We have to flush all buffers and invalidate caches, - * or we won't be able to re-use the partitions, - * if there was a change and we don't want to reboot - */ - res = wipe_partitions(inode->i_rdev); + { + kdev_t device = mk_kdev(MAJOR_NR, + minor(inode->i_rdev) & -(1<<NFTL_PARTN_BITS)); + res = dev_lock_part(device); + if (res < 0) + return res; + res = wipe_partitions(device); if (!res) - grok_partitions(inode->i_rdev, nftl->nr_sects); - + grok_partitions(device, nftl->nr_sects); + dev_unlock_part(device); + } return res; - -#if (LINUX_VERSION_CODE < 0x20303) - RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */ -#else - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKROSET: - case BLKROGET: - case BLKSSZGET: - return blk_ioctl(inode->i_bdev, cmd, arg); -#endif - default: return -EINVAL; } @@ -955,7 +944,6 @@ static int nftl_open(struct inode *ip, struct file *fp) return -EROFS; #endif /* !CONFIG_NFTL_RW */ - thisNFTL->usecount++; if (!get_mtd_device(thisNFTL->mtd, -1)) return /* -E'SBUGGEREDOFF */ -ENXIO; @@ -970,11 +958,8 @@ static int nftl_release(struct inode *inode, struct file *fp) DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n"); - invalidate_device(inode->i_rdev, 1); - if (thisNFTL->mtd->sync) thisNFTL->mtd->sync(thisNFTL->mtd); - thisNFTL->usecount--; put_mtd_device(thisNFTL->mtd); diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index cd6524d58341..485a0a319a98 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -1354,7 +1354,7 @@ static void speedo_tx_timeout(struct net_device *dev) udelay(10); /* Disable interrupts. */ outw(SCBMaskAll, ioaddr + SCBCmd); - synchronize_irq(); + synchronize_irq(dev->irq); speedo_tx_buffer_gc(dev); /* Free as much as possible. It helps to recover from a hang because of out-of-memory. diff --git a/drivers/net/fc/iph5526.c b/drivers/net/fc/iph5526.c index 0819ccc93818..771762a06bed 100644 --- a/drivers/net/fc/iph5526.c +++ b/drivers/net/fc/iph5526.c @@ -3891,7 +3891,7 @@ struct pci_dev *pdev = NULL; } -int iph5526_biosparam(Disk * disk, kdev_t n, int ip[]) +int iph5526_biosparam(Disk * disk, struct block_device *n, int ip[]) { int size = disk->capacity; ip[0] = 64; diff --git a/drivers/net/fc/iph5526_scsi.h b/drivers/net/fc/iph5526_scsi.h index 18221157ba29..a0224c6a0925 100644 --- a/drivers/net/fc/iph5526_scsi.h +++ b/drivers/net/fc/iph5526_scsi.h @@ -25,7 +25,7 @@ int iph5526_queuecommand(Scsi_Cmnd *Cmnd, void (*done) (Scsi_Cmnd *)); int iph5526_release(struct Scsi_Host *host); int iph5526_abort(Scsi_Cmnd *Cmnd); const char *iph5526_info(struct Scsi_Host *host); -int iph5526_biosparam(Disk * disk, kdev_t n, int ip[]); +int iph5526_biosparam(Disk * disk, struct block_device *n, int ip[]); #endif diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index ca62073e1009..beea10ab977d 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -447,11 +447,6 @@ static int dasd_ioctl_set_ro(void *inp, int no, long args) return 0; } -static int dasd_ioctl_blkioctl(void *inp, int no, long args) -{ - return blk_ioctl(((struct inode *) inp)->i_bdev, no, args); -} - /* * Return device size in number of sectors. */ @@ -517,12 +512,12 @@ static int dasd_ioctl_rr_partition(void *inp, int no, long args) static int dasd_ioctl_getgeo(void *inp, int no, long args) { struct hd_geometry geo = { 0, }; + struct inode *inode = inp; dasd_devmap_t *devmap; dasd_device_t *device; - kdev_t kdev; + kdev_t kdev = inode->i_rdev; int rc; - kdev = ((struct inode *) inp)->i_rdev; devmap = dasd_devmap_from_kdev(kdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); @@ -532,7 +527,7 @@ static int dasd_ioctl_getgeo(void *inp, int no, long args) if (device != NULL && device->discipline != NULL && device->discipline->fill_geometry != NULL) { device->discipline->fill_geometry(device, &geo); - geo.start = get_start_sect(kdev); + geo.start = get_start_sect(inode->i_bdev); if (copy_to_user((struct hd_geometry *) args, &geo, sizeof (struct hd_geometry))) rc = -EFAULT; @@ -554,16 +549,10 @@ static struct { int no; dasd_ioctl_fn_t fn; } dasd_ioctls[] = { BIODASDINFO2, dasd_ioctl_information }, { BIODASDPRRD, dasd_ioctl_read_profile }, { BIODASDPRRST, dasd_ioctl_reset_profile }, - { BLKELVGET, dasd_ioctl_blkioctl }, - { BLKELVSET, dasd_ioctl_blkioctl }, - { BLKFLSBUF, dasd_ioctl_blkioctl }, { BLKGETSIZE, dasd_ioctl_blkgetsize }, { BLKGETSIZE64, dasd_ioctl_blkgetsize64 }, - { BLKPG, dasd_ioctl_blkioctl }, - { BLKROGET, dasd_ioctl_blkioctl }, { BLKROSET, dasd_ioctl_set_ro }, { BLKRRPART, dasd_ioctl_rr_partition }, - { BLKSSZGET, dasd_ioctl_blkioctl }, { DASDAPIVER, dasd_ioctl_api_version }, { HDIO_GETGEO, dasd_ioctl_getgeo }, { -1, NULL } diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index be899c77943a..3985f4274eac 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c @@ -342,14 +342,6 @@ static int xpram_ioctl (struct inode *inode, struct file *filp, if (idx >= xpram_devs) return -ENODEV; switch (cmd) { - case BLKGETSIZE: - /* Return the device size, expressed in sectors */ - return put_user(xpram_sizes[idx] << 1, (unsigned long *) arg); - case BLKGETSIZE64: - /* Return the device size, expressed in bytes */ - return put_user((u64) xpram_sizes[idx] << 10, (u64 *) arg); - case BLKFLSBUF: - return blk_ioctl(((struct inode *) inode)->i_bdev, cmd, arg); case BLKRRPART: /* re-read partition table: can't do it */ return -EINVAL; diff --git a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c index a2c58e179445..c7d5306043c6 100644 --- a/drivers/sbus/char/jsflash.c +++ b/drivers/sbus/char/jsflash.c @@ -464,14 +464,6 @@ static int jsfd_ioctl(struct inode *inode, struct file *file, case BLKGETSIZE64: return put_user(jsfd_bytesizes[dev], (u64 *) arg); -#if 0 - case BLKROSET: - case BLKROGET: - case BLKSSZGET: - return blk_ioctl(inode->i_bdev, cmd, arg); -#endif - - /* case BLKFLSBUF: */ /* Program, then read, what happens? Stale? */ default: ; } return -ENOTTY; diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index 87b883e6107f..3e71b678fb64 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/drivers/scsi/3w-xxxx.c @@ -2084,7 +2084,7 @@ int tw_reset_sequence(TW_Device_Extension *tw_dev) } /* End tw_reset_sequence() */ /* This funciton returns unit geometry in cylinders/heads/sectors */ -int tw_scsi_biosparam(Disk *disk, kdev_t dev, int geom[]) +int tw_scsi_biosparam(Disk *disk, struct block_device *dev, int geom[]) { int heads, sectors, cylinders; TW_Device_Extension *tw_dev; diff --git a/drivers/scsi/3w-xxxx.h b/drivers/scsi/3w-xxxx.h index 4dce8ea588e0..3f96e3753da3 100644 --- a/drivers/scsi/3w-xxxx.h +++ b/drivers/scsi/3w-xxxx.h @@ -56,7 +56,6 @@ #include <linux/version.h> #include <linux/types.h> -#include <linux/kdev_t.h> /* AEN strings */ static char *tw_aen_string[] = { @@ -445,7 +444,7 @@ int tw_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds); int tw_post_command_packet(TW_Device_Extension *tw_dev, int request_id); int tw_reset_device_extension(TW_Device_Extension *tw_dev); int tw_reset_sequence(TW_Device_Extension *tw_dev); -int tw_scsi_biosparam(Disk *disk, kdev_t dev, int geom[]); +int tw_scsi_biosparam(Disk *disk, struct block_device *dev, int geom[]); int tw_scsi_detect(Scsi_Host_Template *tw_host); int tw_scsi_eh_abort(Scsi_Cmnd *SCpnt); int tw_scsi_eh_reset(Scsi_Cmnd *SCpnt); diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c index e7e9dcab1884..08ba8fba4876 100644 --- a/drivers/scsi/BusLogic.c +++ b/drivers/scsi/BusLogic.c @@ -4110,7 +4110,7 @@ int BusLogic_ResetCommand(SCSI_Command_T *Command, unsigned int ResetFlags) the BIOS, and a warning may be displayed. */ -int BusLogic_BIOSDiskParameters(SCSI_Disk_T *Disk, KernelDevice_T Device, +int BusLogic_BIOSDiskParameters(SCSI_Disk_T *Disk, struct block_device *bdev, int *Parameters) { BusLogic_HostAdapter_T *HostAdapter = @@ -4138,7 +4138,7 @@ int BusLogic_BIOSDiskParameters(SCSI_Disk_T *Disk, KernelDevice_T Device, } DiskParameters->Cylinders = Disk->capacity / (DiskParameters->Heads * DiskParameters->Sectors); - buf = scsi_bios_ptable(Device); + buf = scsi_bios_ptable(bdev); if (buf == NULL) return 0; /* If the boot sector partition table flag is valid, search for a partition diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h index e415cbc9a67d..44822851a138 100644 --- a/drivers/scsi/BusLogic.h +++ b/drivers/scsi/BusLogic.h @@ -34,7 +34,6 @@ of the Linux Kernel and SCSI Subsystem. */ -typedef kdev_t KernelDevice_T; typedef unsigned long ProcessorFlags_T; typedef struct pt_regs Registers_T; typedef struct partition PartitionTable_T; @@ -58,7 +57,7 @@ extern int BusLogic_QueueCommand(SCSI_Command_T *, void (*CompletionRoutine)(SCSI_Command_T *)); extern int BusLogic_AbortCommand(SCSI_Command_T *); extern int BusLogic_ResetCommand(SCSI_Command_T *, unsigned int); -extern int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, KernelDevice_T, int *); +extern int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, struct block_device *, int *); extern int BusLogic_ProcDirectoryInfo(char *, char **, off_t, int, int, int); diff --git a/drivers/scsi/NCR53c406a.c b/drivers/scsi/NCR53c406a.c index bffa89fb0d43..119869ca0e8e 100644 --- a/drivers/scsi/NCR53c406a.c +++ b/drivers/scsi/NCR53c406a.c @@ -760,7 +760,7 @@ NCR53c406a_reset(Scsi_Cmnd *SCpnt, unsigned int ignored){ } int -NCR53c406a_biosparm(Scsi_Disk *disk, kdev_t dev, int* info_array){ +NCR53c406a_biosparm(Scsi_Disk *disk, struct block_device *dev, int* info_array){ int size; DEB(printk("NCR53c406a_biosparm called\n")); diff --git a/drivers/scsi/NCR53c406a.h b/drivers/scsi/NCR53c406a.h index 0ef6c67ba6a7..b056a9367f8b 100644 --- a/drivers/scsi/NCR53c406a.h +++ b/drivers/scsi/NCR53c406a.h @@ -50,7 +50,7 @@ int NCR53c406a_command(Scsi_Cmnd *); int NCR53c406a_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int NCR53c406a_abort(Scsi_Cmnd *); int NCR53c406a_reset(Scsi_Cmnd *, unsigned int); -int NCR53c406a_biosparm(Disk *, kdev_t, int []); +int NCR53c406a_biosparm(Disk *, struct block_device *, int []); #endif /* _NCR53C406A_H */ diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index 107fae08cb44..ace0da2c55c0 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -6114,7 +6114,7 @@ advansys_reset(Scsi_Cmnd *scp) * ip[2]: cylinders */ int -advansys_biosparam(Disk *dp, kdev_t dep, int ip[]) +advansys_biosparam(Disk *dp, struct block_device *dep, int ip[]) { asc_board_t *boardp; diff --git a/drivers/scsi/advansys.h b/drivers/scsi/advansys.h index 22cdadd55f2f..756e5c64cc78 100644 --- a/drivers/scsi/advansys.h +++ b/drivers/scsi/advansys.h @@ -52,7 +52,7 @@ int advansys_release(struct Scsi_Host *); const char *advansys_info(struct Scsi_Host *); int advansys_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); int advansys_reset(Scsi_Cmnd *); -int advansys_biosparam(Disk *, kdev_t, int[]); +int advansys_biosparam(Disk *, struct block_device *, int[]); #ifdef CONFIG_PROC_FS #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,3,28) extern struct proc_dir_entry proc_scsi_advansys; diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c index 4a9a9cfb93f5..7f353ce582d9 100644 --- a/drivers/scsi/aha152x.c +++ b/drivers/scsi/aha152x.c @@ -1830,7 +1830,7 @@ int aha152x_host_reset(Scsi_Cmnd * SCpnt) * Return the "logical geometry" * */ -int aha152x_biosparam(Scsi_Disk * disk, kdev_t dev, int *info_array) +int aha152x_biosparam(Scsi_Disk * disk, struct block_device *bdev, int *info_array) { struct Scsi_Host *shpnt = disk->device->host; @@ -1844,7 +1844,7 @@ int aha152x_biosparam(Scsi_Disk * disk, kdev_t dev, int *info_array) int info[3]; /* try to figure out the geometry from the partition table */ - if (scsicam_bios_param(disk, dev, info) < 0 || + if (scsicam_bios_param(disk, bdev, info) < 0 || !((info[0] == 64 && info[1] == 32) || (info[0] == 255 && info[1] == 63))) { if (EXT_TRANS) { printk(KERN_NOTICE diff --git a/drivers/scsi/aha152x.h b/drivers/scsi/aha152x.h index d0a7126ccf7d..0bd6c02ff796 100644 --- a/drivers/scsi/aha152x.h +++ b/drivers/scsi/aha152x.h @@ -20,7 +20,7 @@ int aha152x_release(struct Scsi_Host *shpnt); int aha152x_device_reset(Scsi_Cmnd *); int aha152x_bus_reset(Scsi_Cmnd *); int aha152x_host_reset(Scsi_Cmnd *); -int aha152x_biosparam(Disk *, kdev_t, int*); +int aha152x_biosparam(Disk *, struct block_device *, int*); int aha152x_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout); /* number of queueable commands diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c index ad1af6379ffb..96712452a8fa 100644 --- a/drivers/scsi/aha1542.c +++ b/drivers/scsi/aha1542.c @@ -1783,7 +1783,7 @@ fail: #include "sd.h" -static int aha1542_biosparam(Scsi_Disk * disk, kdev_t dev, int *ip) +static int aha1542_biosparam(Scsi_Disk * disk, struct block_device *dev, int *ip) { int translation_algorithm; int size = disk->capacity; diff --git a/drivers/scsi/aha1542.h b/drivers/scsi/aha1542.h index a15fff3b2fd2..4808317841f6 100644 --- a/drivers/scsi/aha1542.h +++ b/drivers/scsi/aha1542.h @@ -32,7 +32,6 @@ */ #include <linux/types.h> -#include <linux/kdev_t.h> /* I/O Port interface 4.2 */ /* READ */ @@ -141,7 +140,7 @@ static int aha1542_host_reset(Scsi_Cmnd * SCpnt); static int aha1542_old_abort(Scsi_Cmnd * SCpnt); static int aha1542_old_reset(Scsi_Cmnd *, unsigned int); #endif -static int aha1542_biosparam(Disk *, kdev_t, int*); +static int aha1542_biosparam(Disk *, struct block_device *, int*); #define AHA1542_MAILBOXES 8 #define AHA1542_SCATTER 16 diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c index ad043e6020c8..de1e23c9fbb7 100644 --- a/drivers/scsi/aha1740.c +++ b/drivers/scsi/aha1740.c @@ -590,7 +590,7 @@ int aha1740_reset(Scsi_Cmnd * SCpnt, unsigned int ignored) return SCSI_RESET_PUNT; } -int aha1740_biosparam(Disk * disk, kdev_t dev, int* ip) +int aha1740_biosparam(Disk * disk, struct block_device *dev, int* ip) { int size = disk->capacity; int extended = HOSTDATA(disk->device->host)->translation; diff --git a/drivers/scsi/aha1740.h b/drivers/scsi/aha1740.h index 036effaa4e17..9a4410976961 100644 --- a/drivers/scsi/aha1740.h +++ b/drivers/scsi/aha1740.h @@ -11,7 +11,6 @@ */ #include <linux/types.h> -#include <linux/kdev_t.h> /* Eisa Enhanced mode operation - slot locating and addressing */ #define MINEISA 1 /* I don't have an EISA Spec to know these ranges, so I */ @@ -158,7 +157,7 @@ int aha1740_command(Scsi_Cmnd *); int aha1740_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int aha1740_abort(Scsi_Cmnd *); int aha1740_reset(Scsi_Cmnd *, unsigned int); -int aha1740_biosparam(Disk *, kdev_t, int*); +int aha1740_biosparam(Disk *, struct block_device *, int*); int aha1740_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout); diff --git a/drivers/scsi/aic7xxx/aic7xxx_linux.c b/drivers/scsi/aic7xxx/aic7xxx_linux.c index a5ed9dac061a..ffcbb4aa09e0 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_linux.c +++ b/drivers/scsi/aic7xxx/aic7xxx_linux.c @@ -2722,7 +2722,7 @@ ahc_linux_bus_reset(Scsi_Cmnd *cmd) * Return the disk geometry for the given SCSI device. */ int -ahc_linux_biosparam(Disk *disk, kdev_t dev, int geom[]) +ahc_linux_biosparam(Disk *disk, struct block_device *bdev, int geom[]) { int heads; int sectors; @@ -2733,7 +2733,7 @@ ahc_linux_biosparam(Disk *disk, kdev_t dev, int geom[]) unsigned char *buf; ahc = *((struct ahc_softc **)disk->device->host->hostdata); - buf = scsi_bios_ptable(dev); + buf = scsi_bios_ptable(bdev); if (buf) { ret = scsi_partsize(buf, disk->capacity, diff --git a/drivers/scsi/aic7xxx/aic7xxx_linux_host.h b/drivers/scsi/aic7xxx/aic7xxx_linux_host.h index 491b0eb49872..4c3735f5a392 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_linux_host.h +++ b/drivers/scsi/aic7xxx/aic7xxx_linux_host.h @@ -47,7 +47,7 @@ int ahc_linux_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *)); int ahc_linux_detect(Scsi_Host_Template *); int ahc_linux_release(struct Scsi_Host *); const char *ahc_linux_info(struct Scsi_Host *); -int ahc_linux_biosparam(Disk *, kdev_t, int[]); +int ahc_linux_biosparam(Disk *, struct block_device *, int[]); int ahc_linux_bus_reset(Scsi_Cmnd *); int ahc_linux_dev_reset(Scsi_Cmnd *); int ahc_linux_abort(Scsi_Cmnd *); diff --git a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c index ae927a405e08..acf88a76fca0 100644 --- a/drivers/scsi/aic7xxx_old.c +++ b/drivers/scsi/aic7xxx_old.c @@ -11719,14 +11719,14 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags) * Return the disk geometry for the given SCSI device. *-F*************************************************************************/ int -aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[]) +aic7xxx_biosparam(Disk *disk, struct block_device *bdev, int geom[]) { int heads, sectors, cylinders, ret; struct aic7xxx_host *p; unsigned char *buf; p = (struct aic7xxx_host *) disk->device->host->hostdata; - buf = scsi_bios_ptable(dev); + buf = scsi_bios_ptable(bdev); if ( buf ) { diff --git a/drivers/scsi/aic7xxx_old/aic7xxx.h b/drivers/scsi/aic7xxx_old/aic7xxx.h index ce397e38d5e8..ac1d78b8602f 100644 --- a/drivers/scsi/aic7xxx_old/aic7xxx.h +++ b/drivers/scsi/aic7xxx_old/aic7xxx.h @@ -58,7 +58,7 @@ } extern int aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *)); -extern int aic7xxx_biosparam(Disk *, kdev_t, int[]); +extern int aic7xxx_biosparam(Disk *, struct block_device *, int[]); extern int aic7xxx_detect(Scsi_Host_Template *); extern int aic7xxx_command(Scsi_Cmnd *); extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int); diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c index 5ba17a3bc2ff..6f64a4466d71 100644 --- a/drivers/scsi/atp870u.c +++ b/drivers/scsi/atp870u.c @@ -2835,7 +2835,7 @@ stop_output: #include "sd.h" -int atp870u_biosparam(Scsi_Disk * disk, kdev_t dev, int *ip) +int atp870u_biosparam(Scsi_Disk * disk, struct block_device *dev, int *ip) { int heads, sectors, cylinders; diff --git a/drivers/scsi/atp870u.h b/drivers/scsi/atp870u.h index cda1a387ca67..ab61bf99891e 100644 --- a/drivers/scsi/atp870u.h +++ b/drivers/scsi/atp870u.h @@ -11,7 +11,6 @@ */ #include <linux/types.h> -#include <linux/kdev_t.h> /* I/O Port */ @@ -23,7 +22,7 @@ int atp870u_command(Scsi_Cmnd *); int atp870u_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); int atp870u_abort(Scsi_Cmnd *); int atp870u_reset(Scsi_Cmnd *, unsigned int); -int atp870u_biosparam(Disk *, kdev_t, int *); +int atp870u_biosparam(Disk *, struct block_device *, int *); int atp870u_release(struct Scsi_Host *); void send_s870(unsigned char); diff --git a/drivers/scsi/cpqfcTS.h b/drivers/scsi/cpqfcTS.h index 529171e82b9a..a91b3ca51a99 100644 --- a/drivers/scsi/cpqfcTS.h +++ b/drivers/scsi/cpqfcTS.h @@ -12,7 +12,7 @@ extern int cpqfcTS_abort(Scsi_Cmnd *); extern int cpqfcTS_reset(Scsi_Cmnd *, unsigned int); extern int cpqfcTS_eh_abort(Scsi_Cmnd *Cmnd); extern int cpqfcTS_eh_device_reset(Scsi_Cmnd *); -extern int cpqfcTS_biosparam(Disk *, kdev_t, int[]); +extern int cpqfcTS_biosparam(Disk *, struct block_device *, int[]); extern int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg); // note: since Tachyon TS supports an extended scatter/gather diff --git a/drivers/scsi/cpqfcTSinit.c b/drivers/scsi/cpqfcTSinit.c index f38e377207c7..dbac6cde86d9 100644 --- a/drivers/scsi/cpqfcTSinit.c +++ b/drivers/scsi/cpqfcTSinit.c @@ -1625,7 +1625,7 @@ int cpqfcTS_reset(Scsi_Cmnd *Cmnd, unsigned int reset_flags) (from hosts.h) */ -int cpqfcTS_biosparam(Disk *disk, kdev_t n, int ip[]) +int cpqfcTS_biosparam(Disk *disk, struct block_device *n, int ip[]) { int size = disk->capacity; diff --git a/drivers/scsi/dc390.h b/drivers/scsi/dc390.h index 02fe82eaf1db..be436bf43d83 100644 --- a/drivers/scsi/dc390.h +++ b/drivers/scsi/dc390.h @@ -39,7 +39,7 @@ extern int DC390_detect(Scsi_Host_Template *psht); extern int DC390_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)); extern int DC390_abort(Scsi_Cmnd *cmd); extern int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags); -extern int DC390_bios_param(Disk *disk, kdev_t devno, int geom[]); +extern int DC390_bios_param(Disk *disk, struct block_device *dev, int geom[]); #ifdef MODULE static int DC390_release(struct Scsi_Host *); diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index e20cfea59b60..f063b5dc1738 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -453,7 +453,7 @@ static int adpt_queue(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) return adpt_scsi_to_i2o(pHba, cmd, pDev); } -static int adpt_bios_param(Disk* disk, kdev_t dev, int geom[]) +static int adpt_bios_param(Disk* disk, struct block_device *dev, int geom[]) { int heads=-1; int sectors=-1; diff --git a/drivers/scsi/dpti.h b/drivers/scsi/dpti.h index 670afcc2c070..dfff119adc2b 100644 --- a/drivers/scsi/dpti.h +++ b/drivers/scsi/dpti.h @@ -45,7 +45,7 @@ static int adpt_reset(Scsi_Cmnd* cmd); static int adpt_release(struct Scsi_Host *host); static const char *adpt_info(struct Scsi_Host *pSHost); -static int adpt_bios_param(Disk * disk, kdev_t dev, int geom[]); +static int adpt_bios_param(Disk * disk, struct block_device *dev, int geom[]); static int adpt_bus_reset(Scsi_Cmnd* cmd); static int adpt_device_reset(Scsi_Cmnd* cmd); diff --git a/drivers/scsi/dtc.c b/drivers/scsi/dtc.c index 910effea59b4..2fa2cd414363 100644 --- a/drivers/scsi/dtc.c +++ b/drivers/scsi/dtc.c @@ -295,7 +295,7 @@ int __init dtc_detect(Scsi_Host_Template * tpnt){ } /* - * Function : int dtc_biosparam(Disk * disk, kdev_t dev, int *ip) + * Function : int dtc_biosparam(Disk * disk, struct block_device *dev, int *ip) * * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for * the specified device / size. @@ -314,7 +314,7 @@ int __init dtc_detect(Scsi_Host_Template * tpnt){ * and matching the H_C_S coordinates to what DOS uses. */ -int dtc_biosparam(Disk * disk, kdev_t dev, int * ip) +int dtc_biosparam(Disk * disk, struct block_device *dev, int * ip) { int size = disk->capacity; diff --git a/drivers/scsi/dtc.h b/drivers/scsi/dtc.h index 73616ed085c0..f5ce0c95df3e 100644 --- a/drivers/scsi/dtc.h +++ b/drivers/scsi/dtc.h @@ -30,7 +30,7 @@ #ifndef ASM int dtc_abort(Scsi_Cmnd *); -int dtc_biosparam(Disk *, kdev_t, int*); +int dtc_biosparam(Disk *, struct block_device *, int*); int dtc_detect(Scsi_Host_Template *); int dtc_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int dtc_reset(Scsi_Cmnd *, unsigned int reset_flags); diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c index cc73354accc6..9690e38bed42 100644 --- a/drivers/scsi/eata.c +++ b/drivers/scsi/eata.c @@ -1803,10 +1803,10 @@ int eata2x_reset(Scsi_Cmnd *SCarg) { return do_reset(SCarg); } -int eata2x_biosparam(Disk *disk, kdev_t dev, int *dkinfo) { +int eata2x_biosparam(Disk *disk, struct block_device *bdev, int *dkinfo) { int size = disk->capacity; - if (ext_tran || (scsicam_bios_param(disk, dev, dkinfo) < 0)) { + if (ext_tran || (scsicam_bios_param(disk, bdev, dkinfo) < 0)) { dkinfo[0] = 255; dkinfo[1] = 63; dkinfo[2] = size / (dkinfo[0] * dkinfo[1]); diff --git a/drivers/scsi/eata.h b/drivers/scsi/eata.h index f1c5e5b84b91..56f00bf01c7b 100644 --- a/drivers/scsi/eata.h +++ b/drivers/scsi/eata.h @@ -11,7 +11,7 @@ int eata2x_release(struct Scsi_Host *); int eata2x_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int eata2x_abort(Scsi_Cmnd *); int eata2x_reset(Scsi_Cmnd *); -int eata2x_biosparam(Disk *, kdev_t, int *); +int eata2x_biosparam(Disk *, struct block_device *, int *); #define EATA_VERSION "7.22.00" diff --git a/drivers/scsi/fd_mcs.c b/drivers/scsi/fd_mcs.c index c41d92c3f08e..271ce78aa14f 100644 --- a/drivers/scsi/fd_mcs.c +++ b/drivers/scsi/fd_mcs.c @@ -1383,9 +1383,8 @@ int fd_mcs_reset( Scsi_Cmnd *SCpnt, unsigned int reset_flags ) #include "sd.h" #include <scsi/scsi_ioctl.h> -int fd_mcs_biosparam( Scsi_Disk *disk, kdev_t dev, int *info_array ) +int fd_mcs_biosparam( Scsi_Disk *disk, struct block_device *bdev, int *info_array ) { - int drive; unsigned char buf[512 + sizeof( int ) * 2]; int size = disk->capacity; int *sizes = (int *)buf; @@ -1394,8 +1393,6 @@ int fd_mcs_biosparam( Scsi_Disk *disk, kdev_t dev, int *info_array ) int retcode; /* BIOS >= 3.4 for MCA cards */ - drive = MINOR(dev) / 16; - /* This algorithm was provided by Future Domain (much thanks!). */ sizes[0] = 0; /* zero bytes out */ diff --git a/drivers/scsi/fd_mcs.h b/drivers/scsi/fd_mcs.h index 1841d5c28661..fb490c906afc 100644 --- a/drivers/scsi/fd_mcs.h +++ b/drivers/scsi/fd_mcs.h @@ -28,7 +28,7 @@ extern int fd_mcs_command( Scsi_Cmnd * ); extern int fd_mcs_abort( Scsi_Cmnd * ); extern int fd_mcs_reset( Scsi_Cmnd *, unsigned int ); extern int fd_mcs_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) ); -extern int fd_mcs_biosparam( Disk *, kdev_t, int * ); +extern int fd_mcs_biosparam( Disk *, struct block_device *, int * ); extern int fd_mcs_proc_info( char *, char **, off_t, int, int, int ); extern const char *fd_mcs_info(struct Scsi_Host *); diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c index c853eabf386f..6fdd13a4c1a4 100644 --- a/drivers/scsi/fdomain.c +++ b/drivers/scsi/fdomain.c @@ -1874,7 +1874,7 @@ int fdomain_16x0_reset( Scsi_Cmnd *SCpnt, unsigned int ignored ) #include "sd.h" #include <scsi/scsi_ioctl.h> -int fdomain_16x0_biosparam( Scsi_Disk *disk, kdev_t dev, int *info_array ) +int fdomain_16x0_biosparam( Scsi_Disk *disk, struct block_device *bdev, int *info_array ) { int drive; unsigned char buf[512 + sizeof (Scsi_Ioctl_Command)]; @@ -1933,11 +1933,11 @@ int fdomain_16x0_biosparam( Scsi_Disk *disk, kdev_t dev, int *info_array ) 0x0a bytes long. Heads are one less than we need to report. */ - if (major(dev) != SCSI_DISK0_MAJOR) { + if (MAJOR(bdev->bd_dev) != SCSI_DISK0_MAJOR) { printk("scsi: <fdomain> fdomain_16x0_biosparam: too many disks"); return 0; } - drive = minor(dev) >> 4; + drive = MINOR(bdev->bd_dev) >> 4; if (bios_major == 2) { switch (Quantum) { diff --git a/drivers/scsi/fdomain.h b/drivers/scsi/fdomain.h index a85539af125b..cc1f8babadaa 100644 --- a/drivers/scsi/fdomain.h +++ b/drivers/scsi/fdomain.h @@ -31,7 +31,7 @@ int fdomain_16x0_abort( Scsi_Cmnd * ); const char *fdomain_16x0_info( struct Scsi_Host * ); int fdomain_16x0_reset( Scsi_Cmnd *, unsigned int ); int fdomain_16x0_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) ); -int fdomain_16x0_biosparam( Disk *, kdev_t, int * ); +int fdomain_16x0_biosparam( Disk *, struct block_device *, int * ); int fdomain_16x0_proc_info( char *buffer, char **start, off_t offset, int length, int hostno, int inout ); int fdomain_16x0_release( struct Scsi_Host *shpnt ); diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c index ae4136b29491..8789580496b6 100644 --- a/drivers/scsi/g_NCR5380.c +++ b/drivers/scsi/g_NCR5380.c @@ -512,7 +512,7 @@ int generic_NCR5380_release_resources(struct Scsi_Host *instance) * Locks: none */ -int generic_NCR5380_biosparam(Disk * disk, kdev_t dev, int *ip) +int generic_NCR5380_biosparam(Disk * disk, struct block_device *dev, int *ip) { int size = disk->capacity; ip[0] = 64; diff --git a/drivers/scsi/g_NCR5380.h b/drivers/scsi/g_NCR5380.h index 3ab55b6f7bcd..0d07adaa8e9f 100644 --- a/drivers/scsi/g_NCR5380.h +++ b/drivers/scsi/g_NCR5380.h @@ -53,7 +53,7 @@ int notyet_generic_proc_info (char *buffer ,char **start, off_t offset, int length, int hostno, int inout); const char* generic_NCR5380_info(struct Scsi_Host *); #ifdef BIOSPARAM -int generic_NCR5380_biosparam(Disk *, kdev_t, int *); +int generic_NCR5380_biosparam(Disk *, struct block_device *, int *); #endif int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int length, int hostno, int inout); diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 8508b693f316..2cb9f5d75851 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -4512,11 +4512,7 @@ int gdth_eh_host_reset(Scsi_Cmnd *scp) } #endif -#if LINUX_VERSION_CODE >= 0x010300 -int gdth_bios_param(Disk *disk,kdev_t dev,int *ip) -#else -int gdth_bios_param(Disk *disk,int dev,int *ip) -#endif +int gdth_bios_param(Disk *disk,struct block_device *bdev,int *ip) { unchar b, t; int hanum; diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h index 1b34a881014a..67355b74b832 100644 --- a/drivers/scsi/gdth.h +++ b/drivers/scsi/gdth.h @@ -1031,8 +1031,7 @@ int gdth_reset(Scsi_Cmnd *); #endif const char *gdth_info(struct Scsi_Host *); -#if LINUX_VERSION_CODE >= 0x020322 -int gdth_bios_param(Disk *,kdev_t,int *); +int gdth_bios_param(Disk *,struct block_device *,int *); int gdth_proc_info(char *,char **,off_t,int,int,int); int gdth_eh_abort(Scsi_Cmnd *scp); int gdth_eh_device_reset(Scsi_Cmnd *scp); @@ -1061,83 +1060,5 @@ int gdth_eh_host_reset(Scsi_Cmnd *scp); unchecked_isa_dma: 1, \ use_clustering: ENABLE_CLUSTERING } -#elif LINUX_VERSION_CODE >= 0x02015F -int gdth_bios_param(Disk *,kdev_t,int *); -extern struct proc_dir_entry proc_scsi_gdth; -int gdth_proc_info(char *,char **,off_t,int,int,int); -int gdth_eh_abort(Scsi_Cmnd *scp); -int gdth_eh_device_reset(Scsi_Cmnd *scp); -int gdth_eh_bus_reset(Scsi_Cmnd *scp); -int gdth_eh_host_reset(Scsi_Cmnd *scp); -#define GDTH { proc_dir: &proc_scsi_gdth, \ - proc_info: gdth_proc_info, \ - name: "GDT SCSI Disk Array Controller",\ - detect: gdth_detect, \ - release: gdth_release, \ - info: gdth_info, \ - command: NULL, \ - queuecommand: gdth_queuecommand, \ - eh_abort_handler: gdth_eh_abort, \ - eh_device_reset_handler: gdth_eh_device_reset, \ - eh_bus_reset_handler: gdth_eh_bus_reset, \ - eh_host_reset_handler: gdth_eh_host_reset, \ - abort: gdth_abort, \ - reset: gdth_reset, \ - bios_param: gdth_bios_param, \ - can_queue: GDTH_MAXCMDS, \ - this_id: -1, \ - sg_tablesize: GDTH_MAXSG, \ - cmd_per_lun: GDTH_MAXC_P_L, \ - present: 0, \ - unchecked_isa_dma: 1, \ - use_clustering: ENABLE_CLUSTERING } - -#elif LINUX_VERSION_CODE >= 0x010300 -int gdth_bios_param(Disk *,kdev_t,int *); -extern struct proc_dir_entry proc_scsi_gdth; -int gdth_proc_info(char *,char **,off_t,int,int,int); -#define GDTH { NULL, NULL, \ - &proc_scsi_gdth, \ - gdth_proc_info, \ - "GDT SCSI Disk Array Controller", \ - gdth_detect, \ - gdth_release, \ - gdth_info, \ - NULL, \ - gdth_queuecommand, \ - gdth_abort, \ - gdth_reset, \ - NULL, \ - gdth_bios_param, \ - GDTH_MAXCMDS, \ - -1, \ - GDTH_MAXSG, \ - GDTH_MAXC_P_L, \ - 0, \ - 1, \ - ENABLE_CLUSTERING} - -#else -int gdth_bios_param(Disk *,int,int *); -#define GDTH { NULL, NULL, \ - "GDT SCSI Disk Array Controller", \ - gdth_detect, \ - gdth_release, \ - gdth_info, \ - NULL, \ - gdth_queuecommand, \ - gdth_abort, \ - gdth_reset, \ - NULL, \ - gdth_bios_param, \ - GDTH_MAXCMDS, \ - -1, \ - GDTH_MAXSG, \ - GDTH_MAXC_P_L, \ - 0, \ - 1, \ - ENABLE_CLUSTERING} -#endif - #endif diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h index f338366ff443..4d7f25dda311 100644 --- a/drivers/scsi/hosts.h +++ b/drivers/scsi/hosts.h @@ -210,9 +210,9 @@ typedef struct SHT * This function determines the bios parameters for a given * harddisk. These tend to be numbers that are made up by * the host adapter. Parameters: - * size, device number, list (heads, sectors, cylinders) + * size, device, list (heads, sectors, cylinders) */ - int (* bios_param)(Disk *, kdev_t, int []); + int (* bios_param)(Disk *, struct block_device *, int []); /* diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c index 1b034e4e4e7f..09d809bae4c8 100644 --- a/drivers/scsi/ibmmca.c +++ b/drivers/scsi/ibmmca.c @@ -2382,7 +2382,7 @@ int ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags) return SCSI_RESET_SUCCESS; } -int ibmmca_biosparam (Disk * disk, kdev_t dev, int *info) +int ibmmca_biosparam (Disk * disk, struct block_device *dev, int *info) { info[0] = 64; info[1] = 32; diff --git a/drivers/scsi/ibmmca.h b/drivers/scsi/ibmmca.h index fa3ec5458576..d8df93cd0ec3 100644 --- a/drivers/scsi/ibmmca.h +++ b/drivers/scsi/ibmmca.h @@ -17,7 +17,7 @@ extern int ibmmca_command (Scsi_Cmnd *); extern int ibmmca_queuecommand (Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); extern int ibmmca_abort (Scsi_Cmnd *); extern int ibmmca_reset (Scsi_Cmnd *, unsigned int); -extern int ibmmca_biosparam (Disk *, kdev_t, int *); +extern int ibmmca_biosparam (Disk *, struct block_device *, int *); /*structure for /proc filesystem */ extern struct proc_dir_entry proc_scsi_ibmmca; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 6cb967d14650..f05c5f37b1ed 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -711,7 +711,7 @@ static int idescsi_device_reset(Scsi_Cmnd *cmd) return SUCCESS; } -static int idescsi_bios(Disk *disk, kdev_t dev, int *parm) +static int idescsi_bios(Disk *disk, struct block_device *dev, int *parm) { idescsi_scsi_t *scsi = idescsi_private(disk->device->host); struct ata_device *drive = scsi->drive; diff --git a/drivers/scsi/imm.c b/drivers/scsi/imm.c index 161cf9799a40..a23e04a157f1 100644 --- a/drivers/scsi/imm.c +++ b/drivers/scsi/imm.c @@ -1117,7 +1117,7 @@ int imm_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) * be done in sd.c. Even if it gets fixed there, this will still * work. */ -int imm_biosparam(Disk * disk, kdev_t dev, int ip[]) +int imm_biosparam(Disk * disk, struct block_device *dev, int ip[]) { ip[0] = 0x40; ip[1] = 0x20; diff --git a/drivers/scsi/imm.h b/drivers/scsi/imm.h index ab3c8a71c369..ac28d5063c4d 100644 --- a/drivers/scsi/imm.h +++ b/drivers/scsi/imm.h @@ -162,7 +162,7 @@ int imm_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); int imm_abort(Scsi_Cmnd *); int imm_reset(Scsi_Cmnd *); int imm_proc_info(char *, char **, off_t, int, int, int); -int imm_biosparam(Disk *, kdev_t, int *); +int imm_biosparam(Disk *, struct block_device *, int *); #define IMM { proc_name: "imm", \ proc_info: imm_proc_info, \ diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c index 5659858c18d9..83fe01110ce0 100644 --- a/drivers/scsi/in2000.c +++ b/drivers/scsi/in2000.c @@ -2158,7 +2158,7 @@ char buf[32]; * supposed to do... */ -int in2000_biosparam(Disk *disk, kdev_t dev, int *iinfo) +int in2000_biosparam(Disk *disk, struct block_device *dev, int *iinfo) { int size; diff --git a/drivers/scsi/in2000.h b/drivers/scsi/in2000.h index 4f12b8fd5e94..f29f4de0bd94 100644 --- a/drivers/scsi/in2000.h +++ b/drivers/scsi/in2000.h @@ -402,7 +402,7 @@ int in2000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int in2000_abort(Scsi_Cmnd *); void in2000_setup(char *, int *) in2000__INIT; int in2000_proc_info(char *, char **, off_t, int, int, int); -int in2000_biosparam(struct scsi_disk *, kdev_t, int *); +int in2000_biosparam(struct scsi_disk *, struct block_device *, int *); int in2000_reset(Scsi_Cmnd *, unsigned int); diff --git a/drivers/scsi/ini9100u.c b/drivers/scsi/ini9100u.c index c6c53caf11d3..dd252714d421 100644 --- a/drivers/scsi/ini9100u.c +++ b/drivers/scsi/ini9100u.c @@ -583,7 +583,7 @@ int i91u_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags) /* * Return the "logical geometry" */ -int i91u_biosparam(Scsi_Disk * disk, kdev_t dev, int *info_array) +int i91u_biosparam(Scsi_Disk * disk, struct block_device *dev, int *info_array) { HCS *pHcb; /* Point to Host adapter control block */ TCS *pTcb; diff --git a/drivers/scsi/ini9100u.h b/drivers/scsi/ini9100u.h index 557518771e40..a8e88f538f97 100644 --- a/drivers/scsi/ini9100u.h +++ b/drivers/scsi/ini9100u.h @@ -84,7 +84,7 @@ extern int i91u_command(Scsi_Cmnd *); extern int i91u_queue(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); extern int i91u_abort(Scsi_Cmnd *); extern int i91u_reset(Scsi_Cmnd *, unsigned int); -extern int i91u_biosparam(Scsi_Disk *, kdev_t, int *); /*for linux v2.0 */ +extern int i91u_biosparam(Scsi_Disk *, struct block_device *, int *); #define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.03g" diff --git a/drivers/scsi/inia100.c b/drivers/scsi/inia100.c index f4685153398f..e5dc661cc491 100644 --- a/drivers/scsi/inia100.c +++ b/drivers/scsi/inia100.c @@ -653,7 +653,7 @@ void inia100SCBPost(BYTE * pHcb, BYTE * pScb) Output : None. Return : pSRB - Pointer to SCSI request block. *****************************************************************************/ -int inia100_biosparam(Scsi_Disk * disk, kdev_t dev, int *info_array) +int inia100_biosparam(Scsi_Disk * disk, struct block_device *dev, int *info_array) { ORC_HCS *pHcb; /* Point to Host adapter control block */ ORC_TCS *pTcb; diff --git a/drivers/scsi/inia100.h b/drivers/scsi/inia100.h index 786b28329aa9..431c8adfff09 100644 --- a/drivers/scsi/inia100.h +++ b/drivers/scsi/inia100.h @@ -83,7 +83,7 @@ extern int inia100_queue(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); extern int inia100_abort(Scsi_Cmnd *); extern int inia100_reset(Scsi_Cmnd *, unsigned int); -extern int inia100_biosparam(Scsi_Disk *, kdev_t, int *); /*for linux v2.0 */ +extern int inia100_biosparam(Scsi_Disk *, struct block_device *, int *); #define inia100_REVID "Initio INI-A100U2W SCSI device driver; Revision: 1.02c" diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index be8f9cee7e88..3d22bcaf7072 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -437,7 +437,7 @@ int ips_release(struct Scsi_Host *); int ips_eh_abort(Scsi_Cmnd *); int ips_eh_reset(Scsi_Cmnd *); int ips_queue(Scsi_Cmnd *, void (*) (Scsi_Cmnd *)); -int ips_biosparam(Disk *, kdev_t, int *); +int ips_biosparam(Disk *, struct block_device *, int *); const char * ips_info(struct Scsi_Host *); void do_ipsintr(int, void *, struct pt_regs *); static int ips_hainit(ips_ha_t *); @@ -1853,7 +1853,7 @@ ips_queue(Scsi_Cmnd *SC, void (*done) (Scsi_Cmnd *)) { /* */ /****************************************************************************/ int -ips_biosparam(Disk *disk, kdev_t dev, int geom[]) { +ips_biosparam(Disk *disk, struct block_device *dev, int geom[]) { ips_ha_t *ha; int heads; int sectors; diff --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h index febbd953eb00..9cc112d362e6 100644 --- a/drivers/scsi/ips.h +++ b/drivers/scsi/ips.h @@ -56,7 +56,7 @@ extern int ips_eh_abort(Scsi_Cmnd *); extern int ips_eh_reset(Scsi_Cmnd *); extern int ips_queue(Scsi_Cmnd *, void (*) (Scsi_Cmnd *)); - extern int ips_biosparam(Disk *, kdev_t, int *); + extern int ips_biosparam(Disk *, struct block_device *, int *); extern const char * ips_info(struct Scsi_Host *); extern void do_ips(int, void *, struct pt_regs *); diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index ea23cc09fba7..7d2bf583126f 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -4218,13 +4218,13 @@ static void mega_create_proc_entry (int index, struct proc_dir_entry *parent) * Return the disk geometry for a particular disk * Input: * Disk *disk - Disk geometry - * kdev_t dev - Device node + * struct block_device *dev - Device node * int *geom - Returns geometry fields * geom[0] = heads * geom[1] = sectors * geom[2] = cylinders *-------------------------------------------------------------*/ -int megaraid_biosparam (Disk * disk, kdev_t dev, int *geom) +int megaraid_biosparam (Disk * disk, struct block_device *bdev, int *geom) { int heads, sectors, cylinders; mega_host_config *megaCfg; @@ -4251,7 +4251,7 @@ int megaraid_biosparam (Disk * disk, kdev_t dev, int *geom) geom[2] = cylinders; } else { - if( mega_partsize(disk, dev, geom) == 0 ) return 0; + if( mega_partsize(disk, bdev, geom) == 0 ) return 0; printk(KERN_WARNING "megaraid: invalid partition on this disk on channel %d\n", @@ -4279,7 +4279,7 @@ int megaraid_biosparam (Disk * disk, kdev_t dev, int *geom) } /* - * Function : static int mega_partsize(Disk * disk, kdev_t dev, int *geom) + * Function : static int mega_partsize(Disk * disk, struct block_device *bdev, int *geom) * * Purpose : to determine the BIOS mapping used to create the partition * table, storing the results (cyls, hds, and secs) in geom @@ -4289,7 +4289,7 @@ int megaraid_biosparam (Disk * disk, kdev_t dev, int *geom) * Returns : -1 on failure, 0 on success. */ static int -mega_partsize(Disk * disk, kdev_t dev, int *geom) +mega_partsize(Disk * disk, struct block_device *bdev, int *geom) { struct partition *p, *largest = NULL; int i, largest_cyl; @@ -4297,7 +4297,7 @@ mega_partsize(Disk * disk, kdev_t dev, int *geom) int capacity = disk->capacity; unsigned char *buf; - if (!(buf = scsi_bios_ptable(dev))) + if (!(buf = scsi_bios_ptable(bdev))) return -1; if( *(unsigned short *)(buf + 64) == 0xAA55 ) { @@ -4534,7 +4534,6 @@ static int megadev_ioctl (struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg) { int adapno; - kdev_t dev; u32 inlen; struct uioctl_t ioc; char *kvaddr = NULL; @@ -4561,8 +4560,6 @@ static int megadev_ioctl (struct inode *inode, struct file *filep, if (!inode) return -EINVAL; - dev = inode->i_rdev; - if (_IOC_TYPE (cmd) != MEGAIOC_MAGIC) return (-EINVAL); diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h index c7f011222769..746252eb44c9 100644 --- a/drivers/scsi/megaraid.h +++ b/drivers/scsi/megaraid.h @@ -950,7 +950,7 @@ int megaraid_command (Scsi_Cmnd *); int megaraid_abort (Scsi_Cmnd *); int megaraid_reset (Scsi_Cmnd *, unsigned int); int megaraid_queue (Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); -int megaraid_biosparam (Disk *, kdev_t, int *); +int megaraid_biosparam (Disk *, struct block_device *, int *); int megaraid_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout); @@ -998,7 +998,7 @@ static mega_passthru* mega_prepare_passthru(mega_host_config *, mega_scb *, static mega_ext_passthru* mega_prepare_extpassthru(mega_host_config *, mega_scb *, Scsi_Cmnd *); static void mega_enum_raid_scsi(mega_host_config *); -static int mega_partsize(Disk *, kdev_t, int *); +static int mega_partsize(Disk *, struct block_device *, int *); static void mega_get_boot_ldrv(mega_host_config *); static int mega_get_lun(mega_host_config *, Scsi_Cmnd *); static int mega_support_random_del(mega_host_config *); diff --git a/drivers/scsi/pas16.c b/drivers/scsi/pas16.c index 339287d6238e..5c347dc8edea 100644 --- a/drivers/scsi/pas16.c +++ b/drivers/scsi/pas16.c @@ -486,7 +486,7 @@ int __init pas16_detect(Scsi_Host_Template * tpnt) } /* - * Function : int pas16_biosparam(Disk *disk, kdev_t dev, int *ip) + * Function : int pas16_biosparam(Disk *disk, struct block_device *dev, int *ip) * * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for * the specified device / size. @@ -505,7 +505,7 @@ int __init pas16_detect(Scsi_Host_Template * tpnt) * and matching the H_C_S coordinates to what DOS uses. */ -int pas16_biosparam(Disk * disk, kdev_t dev, int * ip) +int pas16_biosparam(Disk * disk, struct block_device *dev, int * ip) { int size = disk->capacity; ip[0] = 64; diff --git a/drivers/scsi/pas16.h b/drivers/scsi/pas16.h index 543a4cfc502f..e16e94bb312a 100644 --- a/drivers/scsi/pas16.h +++ b/drivers/scsi/pas16.h @@ -115,7 +115,7 @@ #ifndef ASM int pas16_abort(Scsi_Cmnd *); -int pas16_biosparam(Disk *, kdev_t, int*); +int pas16_biosparam(Disk *, struct block_device *, int*); int pas16_detect(Scsi_Host_Template *); int pas16_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int pas16_reset(Scsi_Cmnd *, unsigned int); diff --git a/drivers/scsi/pci2000.c b/drivers/scsi/pci2000.c index 0e86e7362f90..1b840371057b 100644 --- a/drivers/scsi/pci2000.c +++ b/drivers/scsi/pci2000.c @@ -830,7 +830,7 @@ int Pci2000_Release (struct Scsi_Host *pshost) * Returns: zero. * ****************************************************************/ -int Pci2000_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[]) +int Pci2000_BiosParam (Scsi_Disk *disk, struct block_device *dev, int geom[]) { PADAPTER2000 padapter; diff --git a/drivers/scsi/pci2000.h b/drivers/scsi/pci2000.h index fe990d4e93d0..dd5882c7091a 100644 --- a/drivers/scsi/pci2000.h +++ b/drivers/scsi/pci2000.h @@ -22,7 +22,6 @@ #define _PCI2000_H #include <linux/types.h> -#include <linux/kdev_t.h> #ifndef PSI_EIDE_SCSIOP #define PSI_EIDE_SCSIOP 1 @@ -194,7 +193,7 @@ int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); int Pci2000_Abort (Scsi_Cmnd *SCpnt); int Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int flags); int Pci2000_Release (struct Scsi_Host *pshost); -int Pci2000_BiosParam (Disk *disk, kdev_t dev, int geom[]); +int Pci2000_BiosParam (Disk *disk, struct block_device *dev, int geom[]); #ifndef NULL #define NULL 0 diff --git a/drivers/scsi/pci2220i.c b/drivers/scsi/pci2220i.c index 77f007e9d85a..4c0cb5f6df45 100644 --- a/drivers/scsi/pci2220i.c +++ b/drivers/scsi/pci2220i.c @@ -49,7 +49,6 @@ #include <linux/sched.h> #include <linux/proc_fs.h> #include <linux/stat.h> -#include <linux/kdev_t.h> #include <linux/blk.h> #include <linux/timer.h> #include <linux/spinlock.h> @@ -2903,7 +2902,7 @@ int Pci2220i_Release (struct Scsi_Host *pshost) * Returns: zero. * ****************************************************************/ -int Pci2220i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[]) +int Pci2220i_BiosParam (Scsi_Disk *disk, struct block_device *dev, int geom[]) { POUR_DEVICE pdev; diff --git a/drivers/scsi/pci2220i.h b/drivers/scsi/pci2220i.h index e7aad4d958e6..d34b3a0acea3 100644 --- a/drivers/scsi/pci2220i.h +++ b/drivers/scsi/pci2220i.h @@ -33,7 +33,7 @@ int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); int Pci2220i_Abort (Scsi_Cmnd *SCpnt); int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int flags); int Pci2220i_Release (struct Scsi_Host *pshost); -int Pci2220i_BiosParam (Disk *disk, kdev_t dev, int geom[]); +int Pci2220i_BiosParam (Disk *disk, struct block_device *dev, int geom[]); #ifndef NULL #define NULL 0 diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c index f0618cd93f6b..dd4d0544e18f 100644 --- a/drivers/scsi/ppa.c +++ b/drivers/scsi/ppa.c @@ -998,7 +998,7 @@ int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) * be done in sd.c. Even if it gets fixed there, this will still * work. */ -int ppa_biosparam(Disk * disk, kdev_t dev, int ip[]) +int ppa_biosparam(Disk * disk, struct block_device *dev, int ip[]) { ip[0] = 0x40; ip[1] = 0x20; diff --git a/drivers/scsi/ppa.h b/drivers/scsi/ppa.h index 7479092b906e..b789f94734b6 100644 --- a/drivers/scsi/ppa.h +++ b/drivers/scsi/ppa.h @@ -170,7 +170,7 @@ int ppa_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); int ppa_abort(Scsi_Cmnd *); int ppa_reset(Scsi_Cmnd *); int ppa_proc_info(char *, char **, off_t, int, int, int); -int ppa_biosparam(Disk *, kdev_t, int *); +int ppa_biosparam(Disk *, struct block_device *, int *); #define PPA { proc_name: "ppa", \ proc_info: ppa_proc_info, \ diff --git a/drivers/scsi/psi240i.c b/drivers/scsi/psi240i.c index 8ea40aab26cc..cfb7f3f7ae5e 100644 --- a/drivers/scsi/psi240i.c +++ b/drivers/scsi/psi240i.c @@ -703,7 +703,7 @@ int Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) * Returns: zero. * ****************************************************************/ -int Psi240i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[]) +int Psi240i_BiosParam (Scsi_Disk *disk, struct block_device *dev, int geom[]) { POUR_DEVICE pdev; diff --git a/drivers/scsi/psi240i.h b/drivers/scsi/psi240i.h index d41a5298a805..904c3870b3b9 100644 --- a/drivers/scsi/psi240i.h +++ b/drivers/scsi/psi240i.h @@ -28,7 +28,6 @@ #define _PSI240I_H #include <linux/types.h> -#include <linux/kdev_t.h> #ifndef PSI_EIDE_SCSIOP #define PSI_EIDE_SCSIOP 1 @@ -315,7 +314,7 @@ int Psi240i_Command (Scsi_Cmnd *SCpnt); int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); int Psi240i_Abort (Scsi_Cmnd *SCpnt); int Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int flags); -int Psi240i_BiosParam (Disk *disk, kdev_t dev, int geom[]); +int Psi240i_BiosParam (Disk *disk, struct block_device * dev, int geom[]); #ifndef NULL #define NULL 0 diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c index 9debb242fe73..7930540f1bb6 100644 --- a/drivers/scsi/qla1280.c +++ b/drivers/scsi/qla1280.c @@ -1699,7 +1699,7 @@ qla1280_reset(Scsi_Cmnd * cmd, unsigned int flags) * Return the disk geometry for the given SCSI device. **************************************************************************/ int -qla1280_biosparam(Disk * disk, kdev_t dev, int geom[]) +qla1280_biosparam(Disk * disk, struct block_device *dev, int geom[]) { int heads, sectors, cylinders; diff --git a/drivers/scsi/qla1280.h b/drivers/scsi/qla1280.h index 76b60c6e3d73..b8b90c170ee2 100644 --- a/drivers/scsi/qla1280.h +++ b/drivers/scsi/qla1280.h @@ -1313,7 +1313,7 @@ int qla1280_release(struct Scsi_Host *); int qla1280_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); int qla1280_abort(Scsi_Cmnd *); int qla1280_reset(Scsi_Cmnd *, unsigned int); -int qla1280_biosparam(Disk *, kdev_t, int[]); +int qla1280_biosparam(Disk *, struct block_device *, int[]); void qla1280_intr_handler(int, void *, struct pt_regs *); void qla1280_setup(char *s, int *dummy); diff --git a/drivers/scsi/qlogicfas.c b/drivers/scsi/qlogicfas.c index 6fdb00edcfac..dc589a0a5f32 100644 --- a/drivers/scsi/qlogicfas.c +++ b/drivers/scsi/qlogicfas.c @@ -651,7 +651,7 @@ host->proc_name = "qlogicfas"; /*----------------------------------------------------------------*/ /* return bios parameters */ -int qlogicfas_biosparam(Disk * disk, kdev_t dev, int ip[]) +int qlogicfas_biosparam(Disk * disk, struct block_device *dev, int ip[]) { /* This should mimic the DOS Qlogic driver's behavior exactly */ ip[0] = 0x40; diff --git a/drivers/scsi/qlogicfas.h b/drivers/scsi/qlogicfas.h index 78129c4d0769..cca31ef3df58 100644 --- a/drivers/scsi/qlogicfas.h +++ b/drivers/scsi/qlogicfas.h @@ -7,7 +7,7 @@ int qlogicfas_command(Scsi_Cmnd *); int qlogicfas_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); int qlogicfas_abort(Scsi_Cmnd *); int qlogicfas_reset(Scsi_Cmnd *, unsigned int); -int qlogicfas_biosparam(Disk *, kdev_t, int[]); +int qlogicfas_biosparam(Disk *, struct block_device *, int[]); #ifndef NULL #define NULL (0) diff --git a/drivers/scsi/qlogicfc.c b/drivers/scsi/qlogicfc.c index 32627b9e3c05..6ae15e45a619 100644 --- a/drivers/scsi/qlogicfc.c +++ b/drivers/scsi/qlogicfc.c @@ -1800,7 +1800,7 @@ int isp2x00_reset(Scsi_Cmnd * Cmnd, unsigned int reset_flags) } -int isp2x00_biosparam(Disk * disk, kdev_t n, int ip[]) +int isp2x00_biosparam(Disk * disk, struct block_device *n, int ip[]) { int size = disk->capacity; diff --git a/drivers/scsi/qlogicfc.h b/drivers/scsi/qlogicfc.h index d8831ccb2328..e67e9f5af274 100644 --- a/drivers/scsi/qlogicfc.h +++ b/drivers/scsi/qlogicfc.h @@ -75,7 +75,7 @@ const char * isp2x00_info(struct Scsi_Host *); int isp2x00_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); int isp2x00_abort(Scsi_Cmnd *); int isp2x00_reset(Scsi_Cmnd *, unsigned int); -int isp2x00_biosparam(Disk *, kdev_t, int[]); +int isp2x00_biosparam(Disk *, struct block_device *, int[]); #ifndef NULL #define NULL (0) diff --git a/drivers/scsi/qlogicisp.c b/drivers/scsi/qlogicisp.c index edfa513d9614..4d233ac5d9b0 100644 --- a/drivers/scsi/qlogicisp.c +++ b/drivers/scsi/qlogicisp.c @@ -1240,7 +1240,7 @@ int isp1020_reset(Scsi_Cmnd *Cmnd, unsigned int reset_flags) } -int isp1020_biosparam(Disk *disk, kdev_t n, int ip[]) +int isp1020_biosparam(Disk *disk, struct block_device *n, int ip[]) { int size = disk->capacity; diff --git a/drivers/scsi/qlogicisp.h b/drivers/scsi/qlogicisp.h index 85940ed2f6ae..724d807dfd72 100644 --- a/drivers/scsi/qlogicisp.h +++ b/drivers/scsi/qlogicisp.h @@ -64,7 +64,7 @@ const char * isp1020_info(struct Scsi_Host *); int isp1020_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); int isp1020_abort(Scsi_Cmnd *); int isp1020_reset(Scsi_Cmnd *, unsigned int); -int isp1020_biosparam(Disk *, kdev_t, int[]); +int isp1020_biosparam(Disk *, struct block_device *, int[]); #ifndef NULL #define NULL (0) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 058688349f5d..4f7ae5c4fc34 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -1008,7 +1008,7 @@ static int scsi_debug_abort(Scsi_Cmnd * SCpnt) #endif } -static int scsi_debug_biosparam(Disk * disk, kdev_t dev, int *info) +static int scsi_debug_biosparam(Disk * disk, struct block_device *dev, int *info) { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: biosparam\n"); diff --git a/drivers/scsi/scsi_debug.h b/drivers/scsi/scsi_debug.h index d1eb11c93fd6..92290b6158d5 100644 --- a/drivers/scsi/scsi_debug.h +++ b/drivers/scsi/scsi_debug.h @@ -1,13 +1,12 @@ #ifndef _SCSI_DEBUG_H #include <linux/types.h> -#include <linux/kdev_t.h> static int scsi_debug_detect(Scsi_Host_Template *); /* static int scsi_debug_command(Scsi_Cmnd *); */ static int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); static int scsi_debug_abort(Scsi_Cmnd *); -static int scsi_debug_biosparam(Disk *, kdev_t, int[]); +static int scsi_debug_biosparam(Disk *, struct block_device *, int[]); static int scsi_debug_bus_reset(Scsi_Cmnd *); static int scsi_debug_device_reset(Scsi_Cmnd *); static int scsi_debug_host_reset(Scsi_Cmnd *); diff --git a/drivers/scsi/scsi_mid_low_api.txt b/drivers/scsi/scsi_mid_low_api.txt index 8af86db6d3f2..fbde06519c97 100644 --- a/drivers/scsi/scsi_mid_low_api.txt +++ b/drivers/scsi/scsi_mid_low_api.txt @@ -85,7 +85,7 @@ The interface functions are listed below in alphabetical order. * pre-initialized with made up values just in case this function * doesn't output anything. **/ - int bios_param(Scsi_Disk * sdkp, kdev_t dev, int params[3]); + int bios_param(Scsi_Disk * sdkp, struct block_device *bdev, int params[3]); /** diff --git a/drivers/scsi/scsicam.c b/drivers/scsi/scsicam.c index 7559b235eba3..db6ca281c59e 100644 --- a/drivers/scsi/scsicam.c +++ b/drivers/scsi/scsicam.c @@ -26,39 +26,25 @@ static int setsize(unsigned long capacity, unsigned int *cyls, unsigned int *hds, unsigned int *secs); -unsigned char *scsi_bios_ptable(kdev_t dev) +unsigned char *scsi_bios_ptable(struct block_device *dev) { - struct block_device *bdev; unsigned char *res = kmalloc(66, GFP_KERNEL); - kdev_t rdev = mk_kdev(major(dev), minor(dev) & ~0x0f); - if (res) { - struct buffer_head *bh; - int err; - - bdev = bdget(kdev_t_to_nr(rdev)); - if (!bdev) - goto fail; - err = blkdev_get(bdev, FMODE_READ, 0, BDEV_FILE); - if (err) - goto fail; - bh = __bread(bdev, 0, block_size(bdev)); - if (!bh) - goto fail2; - memcpy(res, bh->b_data + 0x1be, 66); - brelse(bh); - blkdev_put(bdev, BDEV_FILE); + struct block_device *bdev = dev->bd_contains; + struct buffer_head *bh = __bread(bdev, 0, block_size(bdev)); + if (bh) { + memcpy(res, bh->b_data + 0x1be, 66); + brelse(bh); + } else { + kfree(res); + res = NULL; + } } return res; -fail2: - blkdev_put(bdev, BDEV_FILE); -fail: - kfree(res); - return NULL; } /* - * Function : int scsicam_bios_param (Disk *disk, int dev, int *ip) + * Function : int scsicam_bios_param (Disk *disk, struct block_device *bdev, int *ip) * * Purpose : to determine the BIOS mapping used for a drive in a * SCSI-CAM system, storing the results in ip as required @@ -69,13 +55,13 @@ fail: */ int scsicam_bios_param(Disk * disk, /* SCSI disk */ - kdev_t dev, /* Device major, minor */ + struct block_device *bdev, int *ip /* Heads, sectors, cylinders in that order */ ) { int ret_code; int size = disk->capacity; unsigned long temp_cyl; - unsigned char *p = scsi_bios_ptable(dev); + unsigned char *p = scsi_bios_ptable(bdev); if (!p) return -1; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index fd92ed1924f1..f6dd5107974d 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -233,33 +233,20 @@ static int sd_ioctl(struct inode * inode, struct file * filp, or driver values */ if(host->hostt->bios_param != NULL) - host->hostt->bios_param(sdkp, dev, + host->hostt->bios_param(sdkp, inode->i_bdev, &diskinfo[0]); else - scsicam_bios_param(sdkp, dev, &diskinfo[0]); + scsicam_bios_param(sdkp, inode->i_bdev, &diskinfo[0]); if (put_user(diskinfo[0], &loc->heads) || put_user(diskinfo[1], &loc->sectors) || put_user(diskinfo[2], &loc->cylinders) || put_user((unsigned) - get_start_sect(inode->i_rdev), + get_start_sect(inode->i_bdev), (unsigned long *) &loc->start)) return -EFAULT; return 0; } - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKROSET: - case BLKROGET: - case BLKFLSBUF: - case BLKSSZGET: - case BLKPG: - case BLKELVGET: - case BLKELVSET: - case BLKBSZGET: - case BLKBSZSET: - return blk_ioctl(inode->i_bdev, cmd, arg); - case BLKRRPART: /* Re-read partition tables */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -507,16 +494,6 @@ static int sd_open(struct inode *inode, struct file *filp) if (!scsi_block_when_processing_errors(sdp)) return -ENXIO; /* - * Make sure that only one process can do a check_change_disk at - * one time. This is also used to lock out further access when - * the partition table is being re-read. - */ - - while (sdp->busy) { - barrier(); - cpu_relax(); - } - /* * The following code can sleep. * Module unloading must be prevented */ @@ -527,9 +504,7 @@ static int sd_open(struct inode *inode, struct file *filp) sdp->access_count++; if (sdp->removable) { - sdp->allow_revalidate = 1; check_disk_change(inode->i_rdev); - sdp->allow_revalidate = 0; /* * If the drive is empty, just let the open fail. @@ -1464,9 +1439,10 @@ static int sd_attach(Scsi_Device * sdp) int revalidate_scsidisk(kdev_t dev, int maxusage) { int dsk_nr = DEVICE_NR(dev); - int res; Scsi_Disk * sdkp; Scsi_Device * sdp; + kdev_t device = mk_kdev(major(dev), minor(dev) & ~15); + int res; SCSI_LOG_HLQUEUE(3, printk("revalidate_scsidisk: dsk_nr=%d\n", DEVICE_NR(dev))); @@ -1474,24 +1450,20 @@ int revalidate_scsidisk(kdev_t dev, int maxusage) if ((NULL == sdkp) || (NULL == (sdp = sdkp->device))) return -ENODEV; - if (sdp->busy || ((sdp->allow_revalidate == 0) && - (sdp->access_count > maxusage))) { - printk(KERN_WARNING "Device busy for revalidation " - "(access_count=%d)\n", sdp->access_count); - return -EBUSY; - } - sdp->busy = 1; + res = dev_lock_part(device); + if (res < 0) + return res; - res = wipe_partitions(dev); + res = wipe_partitions(device); if (res) goto leave; sd_init_onedisk(sdkp, dsk_nr); - grok_partitions(dev, sdkp->capacity); + grok_partitions(device, sdkp->capacity); leave: - sdp->busy = 0; + dev_unlock_part(device); return res; } diff --git a/drivers/scsi/sim710.h b/drivers/scsi/sim710.h index 2fd837fea8a2..7cd2ff5558d7 100644 --- a/drivers/scsi/sim710.h +++ b/drivers/scsi/sim710.h @@ -14,7 +14,7 @@ int sim710_abort(Scsi_Cmnd * SCpnt); int sim710_bus_reset(Scsi_Cmnd * SCpnt); int sim710_dev_reset(Scsi_Cmnd * SCpnt); int sim710_host_reset(Scsi_Cmnd * SCpnt); -int sim710_biosparam(Disk *, kdev_t, int*); +int sim710_biosparam(Disk *, struct block_device *, int*); #ifdef MODULE int sim710_release(struct Scsi_Host *); #else diff --git a/drivers/scsi/sym53c416.c b/drivers/scsi/sym53c416.c index 2ad532933c49..e32c05b5ddc4 100644 --- a/drivers/scsi/sym53c416.c +++ b/drivers/scsi/sym53c416.c @@ -828,7 +828,7 @@ static int sym53c416_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) return SCSI_RESET_PENDING; } -static int sym53c416_bios_param(Disk *disk, kdev_t dev, int *ip) +static int sym53c416_bios_param(Disk *disk, struct block_device *dev, int *ip) { int size; diff --git a/drivers/scsi/sym53c416.h b/drivers/scsi/sym53c416.h index 8124b1c78d28..0ecf3917626a 100644 --- a/drivers/scsi/sym53c416.h +++ b/drivers/scsi/sym53c416.h @@ -27,7 +27,6 @@ #endif #include <linux/types.h> -#include <linux/kdev_t.h> #define SYM53C416_SCSI_ID 7 @@ -37,7 +36,7 @@ static int sym53c416_command(Scsi_Cmnd *); static int sym53c416_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); static int sym53c416_abort(Scsi_Cmnd *); static int sym53c416_reset(Scsi_Cmnd *, unsigned int); -static int sym53c416_bios_param(Disk *, kdev_t, int *); +static int sym53c416_bios_param(Disk *, struct block_device *, int *); static void sym53c416_setup(char *str, int *ints); #define SYM53C416 { \ diff --git a/drivers/scsi/t128.c b/drivers/scsi/t128.c index a803b1ab9474..4bd494a01a1c 100644 --- a/drivers/scsi/t128.c +++ b/drivers/scsi/t128.c @@ -279,7 +279,7 @@ int __init t128_detect(Scsi_Host_Template * tpnt){ } /* - * Function : int t128_biosparam(Disk * disk, kdev_t dev, int *ip) + * Function : int t128_biosparam(Disk * disk, struct block_device *dev, int *ip) * * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for * the specified device / size. @@ -298,7 +298,7 @@ int __init t128_detect(Scsi_Host_Template * tpnt){ * and matching the H_C_S coordinates to what DOS uses. */ -int t128_biosparam(Disk * disk, kdev_t dev, int * ip) +int t128_biosparam(Disk * disk, struct block_device *dev, int * ip) { int size = disk->capacity; ip[0] = 64; diff --git a/drivers/scsi/t128.h b/drivers/scsi/t128.h index f315160773ef..e07449e524ab 100644 --- a/drivers/scsi/t128.h +++ b/drivers/scsi/t128.h @@ -92,7 +92,7 @@ #ifndef ASM int t128_abort(Scsi_Cmnd *); -int t128_biosparam(Disk *, kdev_t, int*); +int t128_biosparam(Disk *, struct block_device *, int*); int t128_detect(Scsi_Host_Template *); int t128_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int t128_reset(Scsi_Cmnd *, unsigned int reset_flags); diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c index dac2c059d02b..e9bd6a72ffbc 100644 --- a/drivers/scsi/tmscsim.c +++ b/drivers/scsi/tmscsim.c @@ -1439,7 +1439,7 @@ static int partsize(unsigned char *buf, unsigned long capacity, * Note: * In contrary to other externally callable funcs (DC390_), we don't lock ***********************************************************************/ -int DC390_bios_param (Disk *disk, kdev_t devno, int geom[]) +int DC390_bios_param (Disk *disk, struct block_device *bdev, int geom[]) { int heads, sectors, cylinders; PACB pACB = (PACB) disk->device->host->hostdata; @@ -1447,7 +1447,7 @@ int DC390_bios_param (Disk *disk, kdev_t devno, int geom[]) int size = disk->capacity; unsigned char *buf; - if ((buf = scsi_bios_ptable(devno))) + if ((buf = scsi_bios_ptable(bdev))) { /* try to infer mapping from partition table */ ret_code = partsize (buf, (unsigned long) size, (unsigned int *) geom + 2, @@ -1475,9 +1475,9 @@ int DC390_bios_param (Disk *disk, kdev_t devno, int geom[]) return (0); } #else -int DC390_bios_param (Disk *disk, kdev_t devno, int geom[]) +int DC390_bios_param (Disk *disk, struct block_device *bdev, int geom[]) { - return scsicam_bios_param (disk, devno, geom); + return scsicam_bios_param (disk, bdev, geom); }; #endif @@ -2525,8 +2525,8 @@ else if (!p1) goto ok2 if (dc390_search (&buffer, &pos, &p0, &var, txt, max, scale, "")) goto einv2; \ else if (!p1) goto ok2 -#define SEARCH3(buffer, pos, &p0, var, txt, max, scale, ign) \ -if (dc390_search (&buffer, &pos, p0, &var, txt, max, scale, ign)) goto einv2; \ +#define SEARCH3(buffer, pos, p0, var, txt, max, scale, ign) \ +if (dc390_search (&buffer, &pos, &p0, &var, txt, max, scale, ign)) goto einv2; \ else if (!p1) goto ok2 diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c index 6de097b8368b..993109521385 100644 --- a/drivers/scsi/u14-34f.c +++ b/drivers/scsi/u14-34f.c @@ -1452,7 +1452,7 @@ int u14_34f_reset(Scsi_Cmnd *SCarg) { return do_reset(SCarg); } -int u14_34f_biosparam(Disk *disk, kdev_t dev, int *dkinfo) { +int u14_34f_biosparam(Disk *disk, struct block_device *bdev, int *dkinfo) { unsigned int j = 0; int size = disk->capacity; @@ -1460,7 +1460,7 @@ int u14_34f_biosparam(Disk *disk, kdev_t dev, int *dkinfo) { dkinfo[1] = HD(j)->sectors; dkinfo[2] = size / (HD(j)->heads * HD(j)->sectors); - if (ext_tran && (scsicam_bios_param(disk, dev, dkinfo) < 0)) { + if (ext_tran && (scsicam_bios_param(disk, bdev, dkinfo) < 0)) { dkinfo[0] = 255; dkinfo[1] = 63; dkinfo[2] = size / (dkinfo[0] * dkinfo[1]); diff --git a/drivers/scsi/u14-34f.h b/drivers/scsi/u14-34f.h index c5dfe2d80f55..fe8fe7830a1a 100644 --- a/drivers/scsi/u14-34f.h +++ b/drivers/scsi/u14-34f.h @@ -11,7 +11,7 @@ int u14_34f_release(struct Scsi_Host *); int u14_34f_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int u14_34f_abort(Scsi_Cmnd *); int u14_34f_reset(Scsi_Cmnd *); -int u14_34f_biosparam(Disk *, kdev_t, int *); +int u14_34f_biosparam(Disk *, struct block_device *, int *); #define U14_34F_VERSION "7.22.00" diff --git a/drivers/scsi/ultrastor.c b/drivers/scsi/ultrastor.c index 1a042201b07a..1876d52a86ab 100644 --- a/drivers/scsi/ultrastor.c +++ b/drivers/scsi/ultrastor.c @@ -1020,7 +1020,7 @@ int ultrastor_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags) } -int ultrastor_biosparam(Disk * disk, kdev_t dev, int * dkinfo) +int ultrastor_biosparam(Disk * disk, struct block_device *dev, int * dkinfo) { int size = disk->capacity; unsigned int s = config.heads * config.sectors; diff --git a/drivers/scsi/ultrastor.h b/drivers/scsi/ultrastor.h index ffe57ec18b80..4dcb9def40fd 100644 --- a/drivers/scsi/ultrastor.h +++ b/drivers/scsi/ultrastor.h @@ -12,14 +12,13 @@ #ifndef _ULTRASTOR_H #define _ULTRASTOR_H -#include <linux/kdev_t.h> int ultrastor_detect(Scsi_Host_Template *); const char *ultrastor_info(struct Scsi_Host * shpnt); int ultrastor_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int ultrastor_abort(Scsi_Cmnd *); int ultrastor_reset(Scsi_Cmnd *, unsigned int); -int ultrastor_biosparam(Disk *, kdev_t, int *); +int ultrastor_biosparam(Disk *, struct block_device *, int *); #define ULTRASTOR_14F_MAX_SG 16 diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c index 3e1bafa68cb1..97c891fb9f03 100644 --- a/drivers/scsi/wd7000.c +++ b/drivers/scsi/wd7000.c @@ -1706,9 +1706,9 @@ int wd7000_reset (Scsi_Cmnd *SCpnt, unsigned int unused) /* * This was borrowed directly from aha1542.c. (Zaga) */ -int wd7000_biosparam (Disk *disk, kdev_t dev, int *ip) +int wd7000_biosparam (Disk *disk, struct block_device *bdev, int *ip) { - dprintk("wd7000_biosparam: dev=%s, size=%d, ", kdevname(dev), + dprintk("wd7000_biosparam: dev=%s, size=%d, ", bdevname(bdev), disk->capacity); /* @@ -1727,7 +1727,7 @@ int wd7000_biosparam (Disk *disk, kdev_t dev, int *ip) /* * try to figure out the geometry from the partition table */ - if ((scsicam_bios_param (disk, dev, info) < 0) || + if ((scsicam_bios_param (disk, bdev, info) < 0) || !(((info[0] == 64) && (info[1] == 32)) || ((info[0] == 255) && (info[1] == 63)))) { printk ("wd7000_biosparam: unable to verify geometry for disk with >1GB.\n" diff --git a/drivers/scsi/wd7000.h b/drivers/scsi/wd7000.h index c90e07f787f0..a9570edcffce 100644 --- a/drivers/scsi/wd7000.h +++ b/drivers/scsi/wd7000.h @@ -12,7 +12,6 @@ */ #include <linux/types.h> -#include <linux/kdev_t.h> int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host); int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout); @@ -21,7 +20,7 @@ int wd7000_command (Scsi_Cmnd *); int wd7000_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int wd7000_abort (Scsi_Cmnd *); int wd7000_reset (Scsi_Cmnd *, unsigned int); -int wd7000_biosparam (Disk *, kdev_t, int *); +int wd7000_biosparam (Disk *, struct block_device *, int *); #ifndef NULL #define NULL 0L diff --git a/drivers/serial/Config.help b/drivers/serial/Config.help new file mode 100644 index 000000000000..c9f938fbb513 --- /dev/null +++ b/drivers/serial/Config.help @@ -0,0 +1,225 @@ +# $Id: Config.help,v 1.5 2002/07/06 17:16:24 rmk Exp $ + +CONFIG_SERIAL_8250 + This selects whether you want to include the driver for the standard + serial ports. The standard answer is Y. People who might say N + here are those that are setting up dedicated Ethernet WWW/FTP + servers, or users that have one of the various bus mice instead of a + serial mouse and don't intend to use their machine's standard serial + port for anything. (Note that the Cyclades and Stallion multi + serial port drivers do not need this driver built in for them to + work.) + + If you want to compile this driver as a module, say M here and read + <file:Documentation/modules.txt>. The module will be called + serial.o. + [WARNING: Do not compile this driver as a module if you are using + non-standard serial ports, since the configuration information will + be lost when the driver is unloaded. This limitation may be lifted + in the future.] + + BTW1: If you have a mouseman serial mouse which is not recognized by + the X window system, try running gpm first. + + BTW2: If you intend to use a software modem (also called Winmodem) + under Linux, forget it. These modems are crippled and require + proprietary drivers which are only available under Windows. + + Most people will say Y or M here, so that they can use serial mice, + modems and similar devices connecting to the standard serial ports. + +CONFIG_SERIAL_8250_CONSOLE + If you say Y here, it will be possible to use a serial port as the + system console (the system console is the device which receives all + kernel messages and warnings and which allows logins in single user + mode). This could be useful if some terminal or printer is connected + to that serial port. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyS1". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + + If you don't have a VGA card installed and you say Y here, the + kernel will automatically use the first serial line, /dev/ttyS0, as + system console. + + If unsure, say N. + +CONFIG_SERIAL_8250_CS + Say Y here to enable support for 16-bit PCMCIA serial devices, + including serial port cards, modems, and the modem functions of + multi-function Ethernet/modem cards. (PCMCIA- or PC-cards are + credit-card size devices often used with laptops.) + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called serial_cs.o. If you want to compile it as + a module, say M here and read <file:Documentation/modules.txt>. + If unsure, say N. + +CONFIG_SERIAL_8250_EXTENDED + If you wish to use any non-standard features of the standard "dumb" + driver, say Y here. This includes HUB6 support, shared serial + interrupts, special multiport support, support for more than the + four COM 1/2/3/4 boards, etc. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about serial driver options. If unsure, say N. + +CONFIG_SERIAL_8250_MANY_PORTS + Say Y here if you have dumb serial boards other than the four + standard COM 1/2/3/4 ports. This may happen if you have an AST + FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available + from <http://www.linuxdoc.org/docs.html#howto>), or other custom + serial port hardware which acts similar to standard serial port + hardware. If you only use the standard COM 1/2/3/4 ports, you can + say N here to save some memory. You can also say Y if you have an + "intelligent" multiport card such as Cyclades, Digiboards, etc. + +CONFIG_SERIAL_8250_SHARE_IRQ + Some serial boards have hardware support which allows multiple dumb + serial ports on the same board to share a single IRQ. To enable + support for this in the serial driver, say Y here. + +CONFIG_SERIAL_8250_DETECT_IRQ + Say Y here if you want the kernel to try to guess which IRQ + to use for your serial port. + + This is considered unsafe; it is far better to configure the IRQ in + a boot script using the setserial command. + + If unsure, say N. + +CONFIG_SERIAL_8250_MULTIPORT + Some multiport serial ports have special ports which are used to + signal when there are any serial ports on the board which need + servicing. Say Y here to enable the serial driver to take advantage + of those special I/O ports. + +CONFIG_SERIAL_8250_RSA + ::: To be written ::: + +CONFIG_ATOMWIDE_SERIAL + If you have an Atomwide Serial card for an Acorn system, say Y to + this option. The driver can handle 1, 2, or 3 port cards. + If unsure, say N. + +CONFIG_DUALSP_SERIAL + If you have the Serial Port's dual serial card for an Acorn system, + say Y to this option. If unsure, say N. + +CONFIG_SERIAL_ANAKIN + ::: To be written ::: +CONFIG_SERIAL_ANAKIN_CONSOLE + ::: To be written ::: + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyAN0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +CONFIG_ANAKIN_DEFAULT_BAUDRATE + ::: To be written ::: + +CONFIG_SERIAL_AMBA + This selects the ARM(R) AMBA(R) PrimeCell UART. If you have an + Integrator platform, say Y or M here. + + If unsure, say N. + +CONFIG_SERIAL_AMBA_CONSOLE + Say Y here if you wish to use an AMBA PrimeCell UART as the system + console (the system console is the device which receives all kernel + messages and warnings and which allows logins in single user mode). + + Even if you say Y here, the currently visible framebuffer console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyAM0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +CONFIG_SERIAL_CLPS711X + ::: To be written ::: + +CONFIG_SERIAL_CLPS711X_CONSOLE + ::: To be written ::: + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyCL1". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +CONFIG_SERIAL_CLPS711X_OLD_NAME + ::: To be written ::: + +CONFIG_SERIAL_21285 + If you have a machine based on a 21285 (Footbridge) StrongARM(R)/ + PCI bridge you can enable its onboard serial port by enabling this + option. + +CONFIG_SERIAL_21285_OLD + Use the old /dev/ttyS name, major 4 minor 64. This is obsolete + and will be removed during later 2.5 development. + +CONFIG_SERIAL_21285_CONSOLE + If you have enabled the serial port on the 21285 footbridge you can + make it the console by answering Y to this option. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyFB". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +CONFIG_SERIAL_UART00 + Say Y here if you want to use the hard logic uart on Excalibur. This + driver also supports soft logic implentations of this uart core. + +CONFIG_SERIAL_UART00_CONSOLE + Say Y here if you want to support a serial console on an Excalibur + hard logic uart or uart00 IP core. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyS1". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +CONFIG_SERIAL_SA1100 + If you have a machine based on a SA1100/SA1110 StrongARM(R) CPU you + can enable its onboard serial port by enabling this option. + Please read <file:Documentation/arm/SA1100/serial_UART> for further + info. + +CONFIG_SERIAL_SA1100_CONSOLE + If you have enabled the serial port on the SA1100/SA1110 StrongARM + CPU you can make it the console by answering Y to this option. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttySA0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +#CONFIG_SERIAL_L7200 +# If you have a LinkUp Systems L7200 board you can enable its two +# onboard serial ports by enabling this option. The device numbers +# are major ID 4 with minor 64 and 65 respectively. +# +#CONFIG_SERIAL_L7200_CONSOLE +# If you have enabled the serial ports on the L7200 development board +# you can make the first serial port the console by answering Y to +# this option. + diff --git a/drivers/serial/Config.in b/drivers/serial/Config.in new file mode 100644 index 000000000000..7783775e9420 --- /dev/null +++ b/drivers/serial/Config.in @@ -0,0 +1,77 @@ +# +# Serial device configuration +# +# $Id: Config.in,v 1.15 2002/07/06 17:16:24 rmk Exp $ +# +mainmenu_option next_comment +comment 'Serial drivers' + +# +# The new 8250/16550 serial drivers +dep_tristate '8250/16550 and compatible serial support (EXPERIMENTAL)' CONFIG_SERIAL_8250 $CONFIG_EXPERIMENTAL +dep_bool ' Console on 8250/16550 and compatible serial port (EXPERIMENTAL)' CONFIG_SERIAL_8250_CONSOLE $CONFIG_SERIAL_8250 $CONFIG_EXPERIMENTAL +dep_tristate ' 8250/16550 PCMCIA device support' CONFIG_SERIAL_8250_CS $CONFIG_PCMCIA $CONFIG_SERIAL_8250 + +dep_mbool 'Extended 8250/16550 serial driver options' CONFIG_SERIAL_8250_EXTENDED $CONFIG_SERIAL_8250 +dep_bool ' Support more than 4 serial ports' CONFIG_SERIAL_8250_MANY_PORTS $CONFIG_SERIAL_8250_EXTENDED +dep_bool ' Support for sharing serial interrupts' CONFIG_SERIAL_8250_SHARE_IRQ $CONFIG_SERIAL_8250_EXTENDED +dep_bool ' Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_8250_DETECT_IRQ $CONFIG_SERIAL_8250_EXTENDED +dep_bool ' Support special multiport boards' CONFIG_SERIAL_8250_MULTIPORT $CONFIG_SERIAL_8250_EXTENDED +dep_bool ' Support RSA serial ports' CONFIG_SERIAL_8250_RSA $CONFIG_SERIAL_8250_EXTENDED + +comment 'Non-8250 serial port support' + +if [ "$CONFIG_ARM" = "y" ]; then + dep_tristate 'Acorn Atomwide 16550 serial port support' CONFIG_ATOMWIDE_SERIAL $CONFIG_ARCH_ACORN $CONFIG_SERIAL_8250 + dep_tristate 'Acorn Dual 16550 serial port support' CONFIG_DUALSP_SERIAL $CONFIG_ARCH_ACORN $CONFIG_SERIAL_8250 + dep_bool 'Anakin serial port support' CONFIG_SERIAL_ANAKIN $CONFIG_ARCH_ANAKIN + dep_bool ' Console on Anakin serial port' CONFIG_SERIAL_ANAKIN_CONSOLE $CONFIG_SERIAL_ANAKIN + if [ "$CONFIG_SERIAL_ANAKIN" = "y" ]; then + int ' Default Anakin serial baudrate' CONFIG_ANAKIN_DEFAULT_BAUDRATE 9600 + fi + + dep_tristate 'ARM AMBA serial port support' CONFIG_SERIAL_AMBA $CONFIG_ARCH_INTEGRATOR + dep_bool ' Support for console on AMBA serial port' CONFIG_SERIAL_AMBA_CONSOLE $CONFIG_SERIAL_AMBA + if [ "$CONFIG_SERIAL_AMBA" = "y" ]; then + define_bool CONFIG_SERIAL_INTEGRATOR y + fi + + dep_tristate 'CLPS711X serial port support' CONFIG_SERIAL_CLPS711X $CONFIG_ARCH_CLPS711X + dep_bool ' Support for console on CLPS711X serial port' CONFIG_SERIAL_CLPS711X_CONSOLE $CONFIG_SERIAL_CLPS711X + dep_bool ' Use the old 2.4 names for CLPS711X serial port' CONFIG_SERIAL_CLPS711X_OLD_NAME $CONFIG_SERIAL_CLPS711X + + dep_tristate 'DC21285 serial port support' CONFIG_SERIAL_21285 $CONFIG_FOOTBRIDGE + dep_bool ' Use /dev/ttyS0 device (OBSOLETE)' CONFIG_SERIAL_21285_OLD $CONFIG_SERIAL_21285 $CONFIG_OBSOLETE + dep_bool ' Console on DC21285 serial port' CONFIG_SERIAL_21285_CONSOLE $CONFIG_SERIAL_21285 + + dep_bool 'Excalibur serial port (uart00) support' CONFIG_SERIAL_UART00 $CONFIG_ARCH_CAMELOT + dep_bool ' Support for console on Excalibur serial port' CONFIG_SERIAL_UART00_CONSOLE $CONFIG_SERIAL_UART00 + + dep_bool 'SA1100 serial port support' CONFIG_SERIAL_SA1100 $CONFIG_ARCH_SA1100 + dep_bool ' Console on SA1100 serial port' CONFIG_SERIAL_SA1100_CONSOLE $CONFIG_SERIAL_SA1100 +fi + +if [ "$CONFIG_SERIAL_AMBA" = "y" -o "$CONFIG_SERIAL_CLPS711X" = "y" -o \ + "$CONFIG_SERIAL_21285" = "y" -o "$CONFIG_SERIAL_SA1100" = "y" -o \ + "$CONFIG_SERIAL_ANAKIN" = "y" -o "$CONFIG_SERIAL_UART00" = "y" -o \ + "$CONFIG_SERIAL_8250" = "y" -o "$CONFIG_SERIAL_ROCKETPORT" = "y" ]; then + define_bool CONFIG_SERIAL_CORE y +else + if [ "$CONFIG_SERIAL_AMBA" = "m" -o "$CONFIG_SERIAL_CLPS711X" = "m" -o \ + "$CONFIG_SERIAL_21285" = "m" -o "$CONFIG_SERIAL_SA1100" = "m" -o \ + "$CONFIG_SERIAL_ANAKIN" = "m" -o "$CONFIG_SERIAL_UART00" = "m" -o \ + "$CONFIG_SERIAL_8250" = "m" -o "$CONFIG_SERIAL_ROCKETPORT" = "m" ]; then + define_bool CONFIG_SERIAL_CORE m + fi +fi +if [ "$CONFIG_SERIAL_AMBA_CONSOLE" = "y" -o \ + "$CONFIG_SERIAL_CLPS711X_CONSOLE" = "y" -o \ + "$CONFIG_SERIAL_21285_CONSOLE" = "y" -o \ + "$CONFIG_SERIAL_SA1100_CONSOLE" = "y" -o \ + "$CONFIG_SERIAL_ANAKIN_CONSOLE" = "y" -o \ + "$CONFIG_SERIAL_UART00_CONSOLE" = "y" -o \ + "$CONFIG_SERIAL_8250_CONSOLE" = "y" ]; then + define_bool CONFIG_SERIAL_CORE_CONSOLE y +fi + +endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile new file mode 100644 index 000000000000..613e9d9b7cef --- /dev/null +++ b/drivers/serial/Makefile @@ -0,0 +1,22 @@ +# +# Makefile for the kernel serial device drivers. +# +# $Id: Makefile,v 1.7 2002/07/06 17:16:24 rmk Exp $ +# + +export-objs := serial_core.o serial_8250.o + +serial-8250-y := +serial-8250-$(CONFIG_PCI) += serial_8250_pci.o +serial-8250-$(CONFIG_ISAPNP) += serial_8250_pnp.o +obj-$(CONFIG_SERIAL_CORE) += serial_core.o +obj-$(CONFIG_SERIAL_21285) += serial_21285.o +obj-$(CONFIG_SERIAL_8250) += serial_8250.o $(serial-8250-y) +obj-$(CONFIG_SERIAL_8250_CS) += serial_8250_cs.o +obj-$(CONFIG_SERIAL_ANAKIN) += serial_anakin.o +obj-$(CONFIG_SERIAL_AMBA) += serial_amba.o +obj-$(CONFIG_SERIAL_CLPS711X) += serial_clps711x.o +obj-$(CONFIG_SERIAL_SA1100) += serial_sa1100.o +obj-$(CONFIG_SERIAL_UART00) += serial_uart00.o + +include $(TOPDIR)/Rules.make diff --git a/drivers/serial/serial_21285.c b/drivers/serial/serial_21285.c new file mode 100644 index 000000000000..0b63b3a7deb6 --- /dev/null +++ b/drivers/serial/serial_21285.c @@ -0,0 +1,553 @@ +/* + * linux/drivers/char/serial_21285.c + * + * Driver for the serial port on the 21285 StrongArm-110 core logic chip. + * + * Based on drivers/char/serial.c + * + * $Id: serial_21285.c,v 1.32 2002/07/21 08:57:55 rmk Exp $ + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/major.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/serial_core.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/hardware/dec21285.h> +#include <asm/hardware.h> + +#define BAUD_BASE (mem_fclk_21285/64) + +#define SERIAL_21285_NAME "ttyFB" +#define SERIAL_21285_MAJOR 204 +#define SERIAL_21285_MINOR 4 + +#define RXSTAT_DUMMY_READ 0x80000000 +#define RXSTAT_FRAME (1 << 0) +#define RXSTAT_PARITY (1 << 1) +#define RXSTAT_OVERRUN (1 << 2) +#define RXSTAT_ANYERR (RXSTAT_FRAME|RXSTAT_PARITY|RXSTAT_OVERRUN) + +#define H_UBRLCR_BREAK (1 << 0) +#define H_UBRLCR_PARENB (1 << 1) +#define H_UBRLCR_PAREVN (1 << 2) +#define H_UBRLCR_STOPB (1 << 3) +#define H_UBRLCR_FIFO (1 << 4) + +static const char serial21285_name[] = "Footbridge UART"; + +#define tx_enabled(port) ((port)->unused[0]) +#define rx_enabled(port) ((port)->unused[1]) + +/* + * The documented expression for selecting the divisor is: + * BAUD_BASE / baud - 1 + * However, typically BAUD_BASE is not divisible by baud, so + * we want to select the divisor that gives us the minimum + * error. Therefore, we want: + * int(BAUD_BASE / baud - 0.5) -> + * int(BAUD_BASE / baud - (baud >> 1) / baud) -> + * int((BAUD_BASE - (baud >> 1)) / baud) + */ + +static void __serial21285_stop_tx(struct uart_port *port) +{ + if (tx_enabled(port)) { + disable_irq(IRQ_CONTX); + tx_enabled(port) = 0; + } +} + +static void +serial21285_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + __serial21285_stop_tx(port); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void +serial21285_start_tx(struct uart_port *port, unsigned int tty_start) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (!tx_enabled(port)) { + enable_irq(IRQ_CONTX); + tx_enabled(port) = 1; + } + spin_unlock_irqrestore(&port->lock, flags); +} + +static void serial21285_stop_rx(struct uart_port *port) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (rx_enabled(port)) { + disable_irq(IRQ_CONRX); + rx_enabled(port) = 0; + } + spin_unlock_irqrestore(&port->lock, flags); +} + +static void serial21285_enable_ms(struct uart_port *port) +{ +} + +static void serial21285_rx_chars(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_port *port = dev_id; + struct tty_struct *tty = port->info->tty; + unsigned int status, ch, rxs, max_count = 256; + + status = *CSR_UARTFLG; + while (!(status & 0x10) && max_count--) { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty->flip.tqueue.routine((void *)tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + printk(KERN_WARNING "TTY_DONT_FLIP set\n"); + return; + } + } + + ch = *CSR_UARTDR; + + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = TTY_NORMAL; + port->icount.rx++; + + rxs = *CSR_RXSTAT | RXSTAT_DUMMY_READ; + if (rxs & RXSTAT_ANYERR) { + if (rxs & RXSTAT_PARITY) + port->icount.parity++; + else if (rxs & RXSTAT_FRAME) + port->icount.frame++; + if (rxs & RXSTAT_OVERRUN) + port->icount.overrun++; + + rxs &= port->read_status_mask; + + if (rxs & RXSTAT_PARITY) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (rxs & RXSTAT_FRAME) + *tty->flip.flag_buf_ptr = TTY_FRAME; + } + + if ((rxs & port->ignore_status_mask) == 0) { + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + if ((rxs & RXSTAT_OVERRUN) && + tty->flip.count < TTY_FLIPBUF_SIZE) { + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character. + */ + *tty->flip.char_buf_ptr++ = 0; + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + tty->flip.count++; + } + status = *CSR_UARTFLG; + } + tty_flip_buffer_push(tty); +} + +static void serial21285_tx_chars(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_port *port = dev_id; + struct circ_buf *xmit = &port->info->xmit; + int count = 256; + + if (port->x_char) { + *CSR_UARTDR = port->x_char; + port->icount.tx++; + port->x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + __serial21285_stop_tx(port); + return; + } + + do { + *CSR_UARTDR = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0 && !(*CSR_UARTFLG & 0x20)); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_event(port, EVT_WRITE_WAKEUP); + + if (uart_circ_empty(xmit)) + __serial21285_stop_tx(port); +} + +static unsigned int serial21285_tx_empty(struct uart_port *port) +{ + return (*CSR_UARTFLG & 8) ? 0 : TIOCSER_TEMT; +} + +/* no modem control lines */ +static unsigned int serial21285_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +static void serial21285_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static void serial21285_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned int h_lcr; + + spin_lock_irqsave(&port->lock, flags); + h_lcr = *CSR_H_UBRLCR; + if (break_state) + h_lcr |= H_UBRLCR_BREAK; + else + h_lcr &= ~H_UBRLCR_BREAK; + *CSR_H_UBRLCR = h_lcr; + spin_unlock_irqrestore(&port->lock, flags); +} + +static int serial21285_startup(struct uart_port *port) +{ + int ret; + + tx_enabled(port) = 1; + rx_enabled(port) = 1; + + ret = request_irq(IRQ_CONRX, serial21285_rx_chars, 0, + serial21285_name, port); + if (ret == 0) { + ret = request_irq(IRQ_CONTX, serial21285_tx_chars, 0, + serial21285_name, port); + if (ret) + free_irq(IRQ_CONRX, port); + } + + return ret; +} + +static void serial21285_shutdown(struct uart_port *port) +{ + free_irq(IRQ_CONTX, port); + free_irq(IRQ_CONRX, port); +} + +static void +serial21285_change_speed(struct uart_port *port, unsigned int cflag, + unsigned int iflag, unsigned int quot) +{ + unsigned int h_lcr; + + switch (cflag & CSIZE) { + case CS5: + h_lcr = 0x00; + break; + case CS6: + h_lcr = 0x20; + break; + case CS7: + h_lcr = 0x40; + break; + default: /* CS8 */ + h_lcr = 0x60; + break; + } + + if (cflag & CSTOPB) + h_lcr |= H_UBRLCR_STOPB; + if (cflag & PARENB) { + h_lcr |= H_UBRLCR_PARENB; + if (!(cflag & PARODD)) + h_lcr |= H_UBRLCR_PAREVN; + } + + if (port->fifosize) + h_lcr |= H_UBRLCR_FIFO; + + port->read_status_mask = RXSTAT_OVERRUN; + if (iflag & INPCK) + port->read_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (iflag & IGNPAR) + port->ignore_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY; + if (iflag & IGNBRK && iflag & IGNPAR) + port->ignore_status_mask |= RXSTAT_OVERRUN; + + /* + * Ignore all characters if CREAD is not set. + */ + if ((cflag & CREAD) == 0) + port->ignore_status_mask |= RXSTAT_DUMMY_READ; + + quot -= 1; + + *CSR_UARTCON = 0; + *CSR_L_UBRLCR = quot & 0xff; + *CSR_M_UBRLCR = (quot >> 8) & 0x0f; + *CSR_H_UBRLCR = h_lcr; + *CSR_UARTCON = 1; +} + +static const char *serial21285_type(struct uart_port *port) +{ + return port->type == PORT_21285 ? "DC21285" : NULL; +} + +static void serial21285_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, 32); +} + +static int serial21285_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, 32, serial21285_name) + != NULL ? 0 : -EBUSY; +} + +static void serial21285_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE && serial21285_request_port(port) == 0) + port->type = PORT_21285; +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int serial21285_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_21285) + ret = -EINVAL; + if (ser->irq != NO_IRQ) + ret = -EINVAL; + if (ser->baud_base != port->uartclk / 16) + ret = -EINVAL; + return ret; +} + +static struct uart_ops serial21285_ops = { + tx_empty: serial21285_tx_empty, + get_mctrl: serial21285_get_mctrl, + set_mctrl: serial21285_set_mctrl, + stop_tx: serial21285_stop_tx, + start_tx: serial21285_start_tx, + stop_rx: serial21285_stop_rx, + enable_ms: serial21285_enable_ms, + break_ctl: serial21285_break_ctl, + startup: serial21285_startup, + shutdown: serial21285_shutdown, + change_speed: serial21285_change_speed, + type: serial21285_type, + release_port: serial21285_release_port, + request_port: serial21285_request_port, + config_port: serial21285_config_port, + verify_port: serial21285_verify_port, +}; + +static struct uart_port serial21285_port = { + membase: 0, + mapbase: 0x42000160, + iotype: SERIAL_IO_MEM, + irq: NO_IRQ, + uartclk: 0, + fifosize: 16, + ops: &serial21285_ops, + flags: ASYNC_BOOT_AUTOCONF, +}; + +static void serial21285_setup_ports(void) +{ + serial21285_port.uartclk = mem_fclk_21285 / 4; +} + +#ifdef CONFIG_SERIAL_21285_CONSOLE + +static void +serial21285_console_write(struct console *co, const char *s, + unsigned int count) +{ + int i; + + for (i = 0; i < count; i++) { + while (*CSR_UARTFLG & 0x20) + barrier(); + *CSR_UARTDR = s[i]; + if (s[i] == '\n') { + while (*CSR_UARTFLG & 0x20) + barrier(); + *CSR_UARTDR = '\r'; + } + } +} + +static kdev_t serial21285_console_device(struct console *c) +{ + return mk_kdev(SERIAL_21285_MAJOR, SERIAL_21285_MINOR); +} + +static void __init +serial21285_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + if (*CSR_UARTCON == 1) { + unsigned int tmp; + + tmp = *CSR_H_UBRLCR; + switch (tmp & 0x60) { + case 0x00: + *bits = 5; + break; + case 0x20: + *bits = 6; + break; + case 0x40: + *bits = 7; + break; + default: + case 0x60: + *bits = 8; + break; + } + + if (tmp & H_UBRLCR_PARENB) { + *parity = 'o'; + if (tmp & H_UBRLCR_PAREVN) + *parity = 'e'; + } + + tmp = *CSR_L_UBRLCR | (*CSR_M_UBRLCR << 8); + + *baud = port->uartclk / (16 * (tmp + 1)); + } +} + +static int __init serial21285_console_setup(struct console *co, char *options) +{ + struct uart_port *port = &serial21285_port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (machine_is_personal_server()) + baud = 57600; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + serial21285_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +#ifdef CONFIG_SERIAL_21285_OLD +static struct console serial21285_old_cons = +{ + name: SERIAL_21285_OLD_NAME, + write: serial21285_console_write, + device: serial21285_console_device, + setup: serial21285_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; +#endif + +static struct console serial21285_console = +{ + name: SERIAL_21285_NAME, + write: serial21285_console_write, + device: serial21285_console_device, + setup: serial21285_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +void __init rs285_console_init(void) +{ + serial21285_setup_ports(); + register_console(&serial21285_console); +} + +#define SERIAL_21285_CONSOLE &serial21285_console +#else +#define SERIAL_21285_CONSOLE NULL +#endif + +static struct uart_driver serial21285_reg = { + owner: THIS_MODULE, + driver_name: "ttyFB", +#ifdef CONFIG_DEVFS_FS + dev_name: "ttyFB%d", +#else + dev_name: "ttyFB", +#endif + major: SERIAL_21285_MAJOR, + minor: SERIAL_21285_MINOR, + nr: 1, + cons: SERIAL_21285_CONSOLE, +}; + +static int __init serial21285_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: 21285 driver $Revision: 1.32 $\n"); + + serial21285_setup_ports(); + + ret = uart_register_driver(&serial21285_reg); + if (ret == 0) + uart_add_one_port(&serial21285_reg, &serial21285_port); + + return ret; +} + +static void __exit serial21285_exit(void) +{ + uart_remove_one_port(&serial21285_reg, &serial21285_port); + uart_unregister_driver(&serial21285_reg); +} + +module_init(serial21285_init); +module_exit(serial21285_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver $Revision: 1.32 $"); diff --git a/drivers/serial/serial_8250.c b/drivers/serial/serial_8250.c new file mode 100644 index 000000000000..b763e3faa3de --- /dev/null +++ b/drivers/serial/serial_8250.c @@ -0,0 +1,2015 @@ +/* + * linux/drivers/char/serial_8250.c + * + * Driver for 8250/16550-type serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King. + * + * 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. + * + * $Id: serial_8250.c,v 1.80 2002/07/21 08:57:55 rmk Exp $ + * + * A note about mapbase / membase + * + * mapbase is the physical address of the IO port. Currently, we don't + * support this very well, and it may well be dropped from this driver + * in future. As such, mapbase should be NULL. + * + * membase is an 'ioremapped' cookie. This is compatible with the old + * serial.c driver, and is currently the preferred form. + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/compiler.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/major.h> +#include <linux/slab.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/serial_reg.h> +#include <linux/serialP.h> +#include <linux/delay.h> +#include <linux/kmod.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/bitops.h> + +#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/serial_core.h> +#include "serial_8250.h" + +/* + * Configuration: + * share_irqs - whether we pass SA_SHIRQ to request_irq(). This option + * is unsafe when used on edge-triggered interrupts. + */ +unsigned int share_irqs = SERIAL8250_SHARE_IRQS; + +/* + * Debugging. + */ +#if 0 +#define DEBUG_AUTOCONF(fmt...) printk(fmt) +#else +#define DEBUG_AUTOCONF(fmt...) do { } while (0) +#endif + +#if 0 +#define DEBUG_INTR(fmt...) printk(fmt) +#else +#define DEBUG_INTR(fmt...) do { } while (0) +#endif + +#define PASS_LIMIT 256 + +/* + * We default to IRQ0 for the "no irq" hack. Some + * machine types want others as well - they're free + * to redefine this in their header file. + */ +#define is_real_interrupt(irq) ((irq) != 0) + +/* + * This converts from our new CONFIG_ symbols to the symbols + * that asm/serial.h expects. You _NEED_ to comment out the + * linux/config.h include contained inside asm/serial.h for + * this to work. + */ +#undef CONFIG_SERIAL_MANY_PORTS +#undef CONFIG_SERIAL_DETECT_IRQ +#undef CONFIG_SERIAL_MULTIPORT +#undef CONFIG_HUB6 + +#ifdef CONFIG_SERIAL_8250_DETECT_IRQ +#define CONFIG_SERIAL_DETECT_IRQ 1 +#endif +#ifdef CONFIG_SERIAL_8250_MULTIPORT +#define CONFIG_SERIAL_MULTIPORT 1 +#endif + +/* + * HUB6 is always on. This will be removed once the header + * files have been cleaned. + */ +#define CONFIG_HUB6 1 +#define CONFIG_SERIAL_MANY_PORTS 1 + +#include <asm/serial.h> + +static struct old_serial_port old_serial_port[] = { + SERIAL_PORT_DFNS /* defined in asm/serial.h */ +}; + +#define UART_NR ARRAY_SIZE(old_serial_port) + +#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE) + +#define PORT_RSA_MAX 4 +static int probe_rsa[PORT_RSA_MAX]; +static int force_rsa[PORT_RSA_MAX]; +#endif /* CONFIG_SERIAL_8250_RSA */ + +struct uart_8250_port { + struct uart_port port; + struct timer_list timer; /* "no irq" timer */ + struct list_head list; /* ports on this IRQ */ + unsigned char acr; + unsigned char ier; + unsigned char rev; + unsigned char lcr; + unsigned int lsr_break_flag; + + /* + * We provide a per-port pm hook. + */ + void (*pm)(struct uart_port *port, + unsigned int state, unsigned int old); +}; + +struct irq_info { + spinlock_t lock; + struct list_head *head; +}; + +static struct irq_info irq_lists[NR_IRQS]; + +/* + * Here we define the default xmit fifo size used for each type of UART. + */ +static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = { + { "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 }, + { "Startech", 1, 0 }, + { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, + { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, + { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO } +}; + +static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset) +{ + offset <<= up->port.regshift; + + switch (up->port.iotype) { + case SERIAL_IO_HUB6: + outb(up->port.hub6 - 1 + offset, up->port.iobase); + return inb(up->port.iobase + 1); + + case SERIAL_IO_MEM: + return readb(up->port.membase + offset); + + default: + return inb(up->port.iobase + offset); + } +} + +static _INLINE_ void +serial_out(struct uart_8250_port *up, int offset, int value) +{ + offset <<= up->port.regshift; + + switch (up->port.iotype) { + case SERIAL_IO_HUB6: + outb(up->port.hub6 - 1 + offset, up->port.iobase); + outb(value, up->port.iobase + 1); + break; + + case SERIAL_IO_MEM: + writeb(value, up->port.membase + offset); + break; + + default: + outb(value, up->port.iobase + offset); + } +} + +/* + * We used to support using pause I/O for certain machines. We + * haven't supported this for a while, but just in case it's badly + * needed for certain old 386 machines, I've left these #define's + * in.... + */ +#define serial_inp(up, offset) serial_in(up, offset) +#define serial_outp(up, offset, value) serial_out(up, offset, value) + + +/* + * For the 16C950 + */ +static void serial_icr_write(struct uart_8250_port *up, int offset, int value) +{ + serial_out(up, UART_SCR, offset); + serial_out(up, UART_ICR, value); +} + +static unsigned int serial_icr_read(struct uart_8250_port *up, int offset) +{ + unsigned int value; + + serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD); + serial_out(up, UART_SCR, offset); + value = serial_in(up, UART_ICR); + serial_icr_write(up, UART_ACR, up->acr); + + return value; +} + +#ifdef CONFIG_SERIAL_8250_RSA +/* + * Attempts to turn on the RSA FIFO. Returns zero on failure. + * We set the port uart clock rate if we succeed. + */ +static int __enable_rsa(struct uart_8250_port *up) +{ + unsigned char mode; + int result; + + mode = serial_inp(up, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_inp(up, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16; + + return result; +} + +static void enable_rsa(struct uart_8250_port *up) +{ + if (up->port.type == PORT_RSA) { + if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { + spin_lock_irq(&up->port.lock); + __enable_rsa(up); + spin_unlock_irq(&up->port.lock); + } + if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) + serial_outp(up, UART_RSA_FRR, 0); + } +} + +/* + * Attempts to turn off the RSA FIFO. Returns zero on failure. + * It is unknown why interrupts were disabled in here. However, + * the caller is expected to preserve this behaviour by grabbing + * the spinlock before calling this function. + */ +static void disable_rsa(struct uart_8250_port *up) +{ + unsigned char mode; + int result; + + if (up->port.type == PORT_RSA && + up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { + spin_lock_irq(&up->port.lock); + + mode = serial_inp(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_inp(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; + spin_unlock_irq(&up->port.lock); + } +} +#endif /* CONFIG_SERIAL_8250_RSA */ + +/* + * This is a quickie test to see how big the FIFO is. + * It doesn't work at all the time, more's the pity. + */ +static int size_fifo(struct uart_8250_port *up) +{ + unsigned char old_fcr, old_mcr, old_dll, old_dlm; + int count; + + old_fcr = serial_inp(up, UART_FCR); + old_mcr = serial_inp(up, UART_MCR); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(up, UART_MCR, UART_MCR_LOOP); + serial_outp(up, UART_LCR, UART_LCR_DLAB); + old_dll = serial_inp(up, UART_DLL); + old_dlm = serial_inp(up, UART_DLM); + serial_outp(up, UART_DLL, 0x01); + serial_outp(up, UART_DLM, 0x00); + serial_outp(up, UART_LCR, 0x03); + for (count = 0; count < 256; count++) + serial_outp(up, UART_TX, count); + mdelay(20);/* FIXME - schedule_timeout */ + for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_inp(up, UART_RX); + serial_outp(up, UART_FCR, old_fcr); + serial_outp(up, UART_MCR, old_mcr); + serial_outp(up, UART_LCR, UART_LCR_DLAB); + serial_outp(up, UART_DLL, old_dll); + serial_outp(up, UART_DLM, old_dlm); + + return count; +} + +/* + * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. + * When this function is called we know it is at least a StarTech + * 16650 V2, but it might be one of several StarTech UARTs, or one of + * its clones. (We treat the broken original StarTech 16650 V1 as a + * 16550, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +static void +autoconfig_startech_uarts(struct uart_8250_port *up) +{ + unsigned char scratch, scratch2, scratch3, scratch4; + + /* + * First we check to see if it's an Oxford Semiconductor UART. + * + * If we have to do this here because some non-National + * Semiconductor clone chips lock up if you try writing to the + * LSR register (which serial_icr_read does) + */ + if (up->port.type == PORT_16550A) { + /* + * EFR [4] must be set else this test fails + * + * This shouldn't be necessary, but Mike Hudson + * (Exoray@isys.ca) claims that it's needed for 952 + * dual UART's (which are not recommended for new designs). + */ + up->acr = 0; + serial_out(up, UART_LCR, 0xBF); + serial_out(up, UART_EFR, 0x10); + serial_out(up, UART_LCR, 0x00); + /* Check for Oxford Semiconductor 16C950 */ + scratch = serial_icr_read(up, UART_ID1); + scratch2 = serial_icr_read(up, UART_ID2); + scratch3 = serial_icr_read(up, UART_ID3); + + if (scratch == 0x16 && scratch2 == 0xC9 && + (scratch3 == 0x50 || scratch3 == 0x52 || + scratch3 == 0x54)) { + up->port.type = PORT_16C950; + up->rev = serial_icr_read(up, UART_REV) | + (scratch3 << 8); + return; + } + } + + /* + * We check for a XR16C850 by setting DLL and DLM to 0, and then + * reading back DLL and DLM. The chip type depends on the DLM + * value read back: + * 0x10 - XR16C850 and the DLL contains the chip revision. + * 0x12 - XR16C2850. + * 0x14 - XR16C854. + */ + + /* Save the DLL and DLM */ + + serial_outp(up, UART_LCR, UART_LCR_DLAB); + scratch3 = serial_inp(up, UART_DLL); + scratch4 = serial_inp(up, UART_DLM); + + serial_outp(up, UART_DLL, 0); + serial_outp(up, UART_DLM, 0); + scratch2 = serial_inp(up, UART_DLL); + scratch = serial_inp(up, UART_DLM); + serial_outp(up, UART_LCR, 0); + + if (scratch == 0x10 || scratch == 0x12 || scratch == 0x14) { + if (scratch == 0x10) + up->rev = scratch2; + up->port.type = PORT_16850; + return; + } + + /* Restore the DLL and DLM */ + + serial_outp(up, UART_LCR, UART_LCR_DLAB); + serial_outp(up, UART_DLL, scratch3); + serial_outp(up, UART_DLM, scratch4); + serial_outp(up, UART_LCR, 0); + + /* + * We distinguish between the '654 and the '650 by counting + * how many bytes are in the FIFO. I'm using this for now, + * since that's the technique that was sent to me in the + * serial driver update, but I'm not convinced this works. + * I've had problems doing this in the past. -TYT + */ + if (size_fifo(up) == 64) + up->port.type = PORT_16654; + else + up->port.type = PORT_16650V2; +} + +/* + * This routine is called by rs_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 uart_8250_port *up, unsigned int probeflags) +{ + unsigned char status1, status2, scratch, scratch2, scratch3; + unsigned char save_lcr, save_mcr; + unsigned long flags; + + DEBUG_AUTOCONF("Testing ttyS%d (0x%04x, 0x%08lx)...\n", + up->port.line, up->port.iobase, up->port.membase); + + if (!up->port.iobase && !up->port.membase) + return; + + /* + * We really do need global IRQs disabled here - we're going to + * be frobbing the chips IRQ enable register to see if it exists. + */ + spin_lock_irqsave(&up->port.lock, flags); +// save_flags(flags); cli(); + + if (!(up->port.flags & ASYNC_BUGGY_UART)) { + /* + * 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 = serial_inp(up, UART_IER); + serial_outp(up, UART_IER, 0); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = serial_inp(up, UART_IER); + serial_outp(up, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(up, UART_IER); + serial_outp(up, UART_IER, scratch); + if (scratch2 != 0 || scratch3 != 0x0F) { + /* + * We failed; there's nothing here + */ + DEBUG_AUTOCONF("serial: ttyS%d: simple autoconfig " + "failed (%02x, %02x)\n", + up->port.line, scratch2, scratch3); + goto out; + } + } + + save_mcr = serial_in(up, UART_MCR); + save_lcr = serial_in(up, UART_LCR); + + /* + * Check to see if a UART is really there. Certain broken + * internal modems based on the Rockwell chipset fail this + * test, because they apparently don't implement the loopback + * test mode. So this test is skipped on the COM 1 through + * COM 4 ports. This *should* be safe, since no board + * manufacturer would be stupid enough to design a board + * that conflicts with COM 1-4 --- we hope! + */ + if (!(up->port.flags & ASYNC_SKIP_TEST)) { + serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(up, UART_MSR) & 0xF0; + serial_outp(up, UART_MCR, save_mcr); + if (status1 != 0x90) { + DEBUG_AUTOCONF("serial: ttyS%d: no UART loopback " + "failed\n", up->port.line); + goto out; + } + } + serial_outp(up, UART_LCR, 0xBF); /* set up for StarTech test */ + serial_outp(up, UART_EFR, 0); /* EFR is the same as FCR */ + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(up, UART_IIR) >> 6; + switch (scratch) { + case 0: + up->port.type = PORT_16450; + break; + case 1: + up->port.type = PORT_UNKNOWN; + break; + case 2: + up->port.type = PORT_16550; + break; + case 3: + up->port.type = PORT_16550A; + break; + } + if (up->port.type == PORT_16550A) { + /* Check for Startech UART's */ + serial_outp(up, UART_LCR, UART_LCR_DLAB); + if (serial_in(up, UART_EFR) == 0) { + up->port.type = PORT_16650; + } else { + serial_outp(up, UART_LCR, 0xBF); + if (serial_in(up, UART_EFR) == 0) + autoconfig_startech_uarts(up); + } + } + if (up->port.type == PORT_16550A) { + /* Check for TI 16750 */ + serial_outp(up, UART_LCR, save_lcr | UART_LCR_DLAB); + serial_outp(up, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(up, UART_IIR) >> 5; + if (scratch == 7) { + /* + * If this is a 16750, and not a cheap UART + * clone, then it should only go into 64 byte + * mode if the UART_FCR7_64BYTE bit was set + * while UART_LCR_DLAB was latched. + */ + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(up, UART_IIR) >> 5; + if (scratch == 6) + up->port.type = PORT_16750; + } + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + } +#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE) + /* + * Only probe for RSA ports if we got the region. + */ + if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) { + int i; + + for (i = 0 ; i < PORT_RSA_MAX ; ++i) { + if (!probe_rsa[i] && !force_rsa[i]) + break; + if (((probe_rsa[i] != up->port.iobase) || + check_region(up->port.iobase + UART_RSA_BASE, 16)) && + (force_rsa[i] != up->port.iobase)) + continue; + if (__enable_rsa(up)) { + up->port.type = PORT_RSA; + break; + } + } + } +#endif + serial_outp(up, UART_LCR, save_lcr); + if (up->port.type == PORT_16450) { + scratch = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, 0xa5); + status1 = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, 0x5a); + status2 = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, scratch); + + if ((status1 != 0xa5) || (status2 != 0x5a)) + up->port.type = PORT_8250; + } + + up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; + + if (up->port.type == PORT_UNKNOWN) + goto out; + + /* + * Reset the UART. + */ +#ifdef CONFIG_SERIAL_8250_RSA + if (up->port.type == PORT_RSA) + serial_outp(up, UART_RSA_FRR, 0); +#endif + serial_outp(up, UART_MCR, save_mcr); + serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(up, UART_FCR, 0); + (void)serial_in(up, UART_RX); + serial_outp(up, UART_IER, 0); + + out: + spin_unlock_irqrestore(&up->port.lock, flags); +// restore_flags(flags); +#ifdef CONFIG_SERIAL_8250_RSA + if (up->port.iobase && up->port.type == PORT_RSA) { + release_region(up->port.iobase, 8); + request_region(up->port.iobase + UART_RSA_BASE, 16, + "serial_rsa"); + } +#endif +} + +static void autoconfig_irq(struct uart_8250_port *up) +{ + unsigned char save_mcr, save_ier; + unsigned char save_ICP = 0; + unsigned int ICP = 0; + unsigned long irqs; + int irq; + + if (up->port.flags & ASYNC_FOURPORT) { + ICP = (up->port.iobase & 0xfe0) | 0x1f; + save_ICP = inb_p(ICP); + outb_p(0x80, ICP); + (void) inb_p(ICP); + } + + /* forget possible initially masked and pending IRQ */ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(up, UART_MCR); + save_ier = serial_inp(up, UART_IER); + serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(up, UART_MCR, 0); + udelay (10); + if (up->port.flags & ASYNC_FOURPORT) { + serial_outp(up, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS); + } else { + serial_outp(up, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + serial_outp(up, UART_IER, 0x0f); /* enable all intrs */ + (void)serial_inp(up, UART_LSR); + (void)serial_inp(up, UART_RX); + (void)serial_inp(up, UART_IIR); + (void)serial_inp(up, UART_MSR); + serial_outp(up, UART_TX, 0xFF); + udelay (20); + irq = probe_irq_off(irqs); + + serial_outp(up, UART_MCR, save_mcr); + serial_outp(up, UART_IER, save_ier); + + if (up->port.flags & ASYNC_FOURPORT) + outb_p(save_ICP, ICP); + + up->port.irq = (irq > 0) ? irq : 0; +} + +static void __serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + + if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } + if (up->port.type == PORT_16C950 && tty_stop) { + up->acr |= UART_ACR_TXDIS; + serial_icr_write(up, UART_ACR, up->acr); + } +} + +static void serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + __serial8250_stop_tx(port, tty_stop); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void serial8250_start_tx(struct uart_port *port, unsigned int tty_start) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } + /* + * We only do this from uart_start + */ + if (tty_start && up->port.type == PORT_16C950) { + up->acr &= ~UART_ACR_TXDIS; + serial_icr_write(up, UART_ACR, up->acr); + } +} + +static void serial8250_stop_rx(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void serial8250_enable_ms(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static _INLINE_ void +receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs) +{ + struct tty_struct *tty = up->port.info->tty; + unsigned char ch; + int max_count = 256; + + do { + if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) { + tty->flip.tqueue.routine((void *)tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + return; // if TTY_DONT_FLIP is set + } + ch = serial_inp(up, UART_RX); + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE))) { + /* + * For statistics only + */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (*status & UART_LSR_PE) + up->port.icount.parity++; + else if (*status & UART_LSR_FE) + up->port.icount.frame++; + if (*status & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ingored. + */ + *status &= up->port.read_status_mask; + +#ifdef CONFIG_SERIAL_8250_CONSOLE + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + *status |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } +#endif + if (*status & UART_LSR_BI) { + DEBUG_INTR("handling break...."); + *tty->flip.flag_buf_ptr = TTY_BREAK; + } else if (*status & UART_LSR_PE) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (*status & UART_LSR_FE) + *tty->flip.flag_buf_ptr = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch, regs)) + goto ignore_char; + if ((*status & up->port.ignore_status_mask) == 0) { + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + if ((*status & UART_LSR_OE) && + tty->flip.count < TTY_FLIPBUF_SIZE) { + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character. + */ + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + ignore_char: + *status = serial_inp(up, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + tty_flip_buffer_push(tty); +} + +static _INLINE_ void transmit_chars(struct uart_8250_port *up) +{ + struct circ_buf *xmit = &up->port.info->xmit; + int count; + + if (up->port.x_char) { + serial_outp(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + __serial8250_stop_tx(&up->port, 0); + return; + } + + count = up->port.fifosize; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_event(&up->port, EVT_WRITE_WAKEUP); + + DEBUG_INTR("THRE..."); + + if (uart_circ_empty(xmit)) + __serial8250_stop_tx(&up->port, 0); +} + +static _INLINE_ void check_modem_status(struct uart_8250_port *up) +{ + int status; + + status = serial_in(up, UART_MSR); + + if ((status & UART_MSR_ANY_DELTA) == 0) + return; + + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change(&up->port, status & UART_MSR_CTS); + + wake_up_interruptible(&up->port.info->delta_msr_wait); +} + +/* + * This handles the interrupt from one port. + */ +static inline void +serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs) +{ + unsigned int status = serial_inp(up, UART_LSR); + + DEBUG_INTR("status = %x...", status); + + if (status & UART_LSR_DR) + receive_chars(up, &status, regs); + check_modem_status(up); + if (status & UART_LSR_THRE) + transmit_chars(up); +} + +/* + * This is the serial driver's interrupt routine. + * + * Arjan thinks the old way was overly complex, so it got simplified. + * Alan disagrees, saying that need the complexity to handle the weird + * nature of ISA shared interrupts. (This is a special exception.) + * + * In order to handle ISA shared interrupts properly, we need to check + * that all ports have been serviced, and therefore the ISA interrupt + * line has been de-asserted. + * + * This means we need to loop through all ports. checking that they + * don't have an interrupt pending. + */ +static void serial8250_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct irq_info *i = dev_id; + struct list_head *l, *end = NULL; + int pass_counter = 0; + + DEBUG_INTR("serial8250_interrupt(%d)...", irq); + + spin_lock(&i->lock); + + l = i->head; + do { + struct uart_8250_port *up; + unsigned int iir; + + up = list_entry(l, struct uart_8250_port, list); + + iir = serial_in(up, UART_IIR); + if (!(iir & UART_IIR_NO_INT)) { + spin_lock(&up->port.lock); + serial8250_handle_port(up, regs); + spin_unlock(&up->port.lock); + + end = NULL; + } else if (end == NULL) + end = l; + + l = l->next; + + if (l == i->head && pass_counter++ > PASS_LIMIT) { + /* If we hit this, we're dead. */ + printk(KERN_ERR "serial8250: too much work for " + "irq%d\n", irq); + break; + } + } while (l != end); + + spin_unlock(&i->lock); + + DEBUG_INTR("end.\n"); +} + +/* + * To support ISA shared interrupts, we need to have one interrupt + * handler that ensures that the IRQ line has been deasserted + * before returning. Failing to do this will result in the IRQ + * line being stuck active, and, since ISA irqs are edge triggered, + * no more IRQs will be seen. + */ +static int serial_link_irq_chain(struct uart_8250_port *up) +{ + struct irq_info *i = irq_lists + up->port.irq; + int ret, irq_flags = share_irqs ? SA_SHIRQ : 0; + + spin_lock_irq(&i->lock); + + if (i->head) { + list_add(&up->list, i->head); + spin_unlock_irq(&i->lock); + + ret = 0; + } else { + INIT_LIST_HEAD(&up->list); + i->head = &up->list; + spin_unlock_irq(&i->lock); + + ret = request_irq(up->port.irq, serial8250_interrupt, + irq_flags, "serial", i); + } + + return ret; +} + +static void serial_unlink_irq_chain(struct uart_8250_port *up) +{ + struct irq_info *i = irq_lists + up->port.irq; + + BUG_ON(i->head == NULL); + + if (list_empty(i->head)) + free_irq(up->port.irq, i); + + spin_lock_irq(&i->lock); + + if (!list_empty(i->head)) { + if (i->head == &up->list) + i->head = i->head->next; + list_del(&up->list); + } else { + BUG_ON(i->head != &up->list); + i->head = NULL; + } + + spin_unlock_irq(&i->lock); +} + +/* + * This function is used to handle ports that do not have an + * interrupt. This doesn't work very well for 16450's, but gives + * barely passable results for a 16550A. (Although at the expense + * of much CPU overhead). + */ +static void serial8250_timeout(unsigned long data) +{ + struct uart_8250_port *up = (struct uart_8250_port *)data; + unsigned int timeout; + unsigned int iir; + + iir = serial_in(up, UART_IIR); + if (!(iir & UART_IIR_NO_INT)) { + spin_lock(&up->port.lock); + serial8250_handle_port(up, NULL); + spin_unlock(&up->port.lock); + } + + timeout = up->port.timeout; + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + mod_timer(&up->timer, jiffies + timeout); +} + +static unsigned int serial8250_tx_empty(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int serial8250_get_mctrl(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long flags; + unsigned char status; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + status = serial_in(up, UART_MSR); + spin_unlock_irqrestore(&up->port.lock, flags); + + ret = 0; + if (status & UART_MSR_DCD) + ret |= TIOCM_CAR; + if (status & UART_MSR_RI) + ret |= TIOCM_RNG; + if (status & UART_MSR_DSR) + ret |= TIOCM_DSR; + if (status & UART_MSR_CTS) + ret |= TIOCM_CTS; + return ret; +} + +static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned char mcr = ALPHA_KLUDGE_MCR; + + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + serial_out(up, UART_MCR, mcr); +} + +static void serial8250_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static int serial8250_startup(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long flags; + int retval; + + if (up->port.type == PORT_16C950) { + /* Wake up and initialize UART */ + up->acr = 0; + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_IER, 0); + serial_outp(up, UART_LCR, 0); + serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_LCR, 0); + } + +#ifdef CONFIG_SERIAL_8250_RSA + /* + * If this is an RSA port, see if we can kick it up to the + * higher speed clock. + */ + enable_rsa(up); +#endif + + /* + * Clear the FIFO buffers and disable them. + * (they will be reeanbled in change_speed()) + */ + if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) { + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(up, UART_FCR, 0); + } + + /* + * Clear the interrupt registers. + */ + (void) serial_inp(up, UART_LSR); + (void) serial_inp(up, UART_RX); + (void) serial_inp(up, UART_IIR); + (void) serial_inp(up, UART_MSR); + + /* + * 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 (!(up->port.flags & ASYNC_BUGGY_UART) && + (serial_inp(up, UART_LSR) == 0xff)) { + printk("ttyS%d: LSR safety check engaged!\n", up->port.line); + return -ENODEV; + } + + /* + * If the "interrupt" for this port doesn't correspond with any + * hardware interrupt, we use a timer-based system. The original + * driver used to do this with IRQ0. + */ + if (!is_real_interrupt(up->port.irq)) { + unsigned int timeout = up->port.timeout; + + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + timeout); + } else { + retval = serial_link_irq_chain(up); + if (retval) + return retval; + } + + /* + * Now, initialize the UART + */ + serial_outp(up, UART_LCR, UART_LCR_WLEN8); + + spin_lock_irqsave(&up->port.lock, flags); + if (up->port.flags & ASYNC_FOURPORT) { + if (!is_real_interrupt(up->port.irq)) + up->port.mctrl |= TIOCM_OUT1; + } else + /* + * Most PC uarts need OUT2 raised to enable interrupts. + */ + if (is_real_interrupt(up->port.irq)) + up->port.mctrl |= TIOCM_OUT2; + + serial8250_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via change_speed(), which will be occuring imminently + * anyway, so we don't enable them here. + */ + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_outp(up, UART_IER, up->ier); + + if (up->port.flags & ASYNC_FOURPORT) { + unsigned int icp; + /* + * Enable interrupts on the AST Fourport board + */ + icp = (up->port.iobase & 0xfe0) | 0x01f; + outb_p(0x80, icp); + (void) inb_p(icp); + } + + /* + * And clear the interrupt registers again for luck. + */ + (void) serial_inp(up, UART_LSR); + (void) serial_inp(up, UART_RX); + (void) serial_inp(up, UART_IIR); + (void) serial_inp(up, UART_MSR); + + return 0; +} + +static void serial8250_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long flags; + + /* + * Disable interrupts from this port + */ + up->ier = 0; + serial_outp(up, UART_IER, 0); + + spin_lock_irqsave(&up->port.lock, flags); + if (up->port.flags & ASYNC_FOURPORT) { + /* reset interrupts on the AST Fourport board */ + inb((up->port.iobase & 0xfe0) | 0x1f); + up->port.mctrl |= TIOCM_OUT1; + } else + up->port.mctrl &= ~TIOCM_OUT2; + + serial8250_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition and FIFOs + */ + serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_outp(up, UART_FCR, 0); + +#ifdef CONFIG_SERIAL_8250_RSA + /* + * Reset the RSA board back to 115kbps compat mode. + */ + disable_rsa(up); +#endif + + /* + * Read data port to reset things, and then unlink from + * the IRQ chain. + */ + (void) serial_in(up, UART_RX); + + if (!is_real_interrupt(up->port.irq)) + del_timer_sync(&up->timer); + else + serial_unlink_irq_chain(up); +} + +static void +serial8250_change_speed(struct uart_port *port, unsigned int cflag, + unsigned int iflag, unsigned int quot) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned char cval, fcr = 0; + unsigned long flags; + + printk("+++ change_speed port %p cflag %08x quot %d\n", port, cflag, quot); + + switch (cflag & CSIZE) { + case CS5: + cval = 0x00; + break; + case CS6: + cval = 0x01; + break; + case CS7: + cval = 0x02; + break; + default: + case CS8: + cval = 0x03; + break; + } + + if (cflag & CSTOPB) + cval |= 0x04; + if (cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif + + /* + * Work around a bug in the Oxford Semiconductor 952 rev B + * chip which causes it to seriously miscalculate baud rates + * when DLL is 0. + */ + if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 && + up->rev == 0x5201) + quot ++; + + if (uart_config[up->port.type].flags & UART_USE_FIFO) { + if ((up->port.uartclk / quot) < (2400 * 16)) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; +#ifdef CONFIG_SERIAL_8250_RSA + else if (up->port.type == PORT_RSA) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; +#endif + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + } + if (up->port.type == PORT_16750) + fcr |= UART_FCR7_64BYTE; + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (iflag & IGNPAR) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characteres to ignore + */ + up->port.ignore_status_mask = 0; + if (iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, cflag)) + up->ier |= UART_IER_MSI; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + serial_out(up, UART_IER, up->ier); + + if (uart_config[up->port.type].flags & UART_STARTECH) { + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, cflag & CRTSCTS ? UART_EFR_CTS :0); + } + serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + serial_outp(up, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_outp(up, UART_DLM, quot >> 8); /* MS of divisor */ + if (up->port.type == PORT_16750) + serial_outp(up, UART_FCR, fcr); /* set fcr */ + serial_outp(up, UART_LCR, cval); /* reset DLAB */ + up->lcr = cval; /* Save LCR */ + if (up->port.type != PORT_16750) { + if (fcr & UART_FCR_ENABLE_FIFO) { + /* emulated UARTs (Lucent Venus 167x) need two steps */ + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + } + serial_outp(up, UART_FCR, fcr); /* set fcr */ + } + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void +serial8250_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + if (state) { + /* sleep */ + if (uart_config[up->port.type].flags & UART_STARTECH) { + /* Arrange to enter sleep mode */ + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_IER, UART_IERX_SLEEP); + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, 0); + serial_outp(up, UART_LCR, 0); + } + if (up->port.type == PORT_16750) { + /* Arrange to enter sleep mode */ + serial_outp(up, UART_IER, UART_IERX_SLEEP); + } + + if (up->pm) + up->pm(port, state, oldstate); + } else { + /* wake */ + if (uart_config[up->port.type].flags & UART_STARTECH) { + /* Wake up UART */ + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, UART_EFR_ECB); + /* + * Turn off LCR == 0xBF so we actually set the IER + * register on the XR16C850 + */ + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_IER, 0); + /* + * Now reset LCR so we can turn off the ECB bit + */ + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, 0); + /* + * For a XR16C850, we need to set the trigger levels + */ + if (up->port.type == PORT_16850) { + unsigned char fctr; + + fctr = serial_inp(up, UART_FCTR) & + ~(UART_FCTR_RX | UART_FCTR_TX); + serial_outp(up, UART_FCTR, fctr | + UART_FCTR_TRGD | + UART_FCTR_RX); + serial_outp(up, UART_TRG, UART_TRG_96); + serial_outp(up, UART_FCTR, fctr | + UART_FCTR_TRGD | + UART_FCTR_TX); + serial_outp(up, UART_TRG, UART_TRG_96); + } + serial_outp(up, UART_LCR, 0); + } + + if (up->port.type == PORT_16750) { + /* Wake up UART */ + serial_outp(up, UART_IER, 0); + } + + if (up->pm) + up->pm(port, state, oldstate); + } +} + +/* + * Resource handling. This is complicated by the fact that resources + * depend on the port type. Maybe we should be claiming the standard + * 8250 ports, and then trying to get other resources as necessary? + */ +static int +serial8250_request_std_resource(struct uart_8250_port *up, struct resource **res) +{ + unsigned int size = 8 << up->port.regshift; + int ret = 0; + + switch (up->port.iotype) { + case SERIAL_IO_MEM: + if (up->port.mapbase) { + *res = request_mem_region(up->port.mapbase, size, "serial"); + if (!*res) + ret = -EBUSY; + } + break; + + case SERIAL_IO_HUB6: + case SERIAL_IO_PORT: + *res = request_region(up->port.iobase, size, "serial"); + if (!*res) + ret = -EBUSY; + break; + } + return ret; +} + +static int +serial8250_request_rsa_resource(struct uart_8250_port *up, struct resource **res) +{ + unsigned int size = 8 << up->port.regshift; + unsigned long start; + int ret = 0; + + switch (up->port.iotype) { + case SERIAL_IO_MEM: + if (up->port.mapbase) { + start = up->port.mapbase; + start += UART_RSA_BASE << up->port.regshift; + *res = request_mem_region(start, size, "serial-rsa"); + if (!*res) + ret = -EBUSY; + } + break; + + case SERIAL_IO_HUB6: + case SERIAL_IO_PORT: + start = up->port.iobase; + start += UART_RSA_BASE << up->port.regshift; + *res = request_region(start, size, "serial-rsa"); + if (!*res) + ret = -EBUSY; + break; + } + + return ret; +} + +static void serial8250_release_port(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long start, offset = 0, size = 0; + + if (up->port.type == PORT_RSA) { + offset = UART_RSA_BASE << up->port.regshift; + size = 8; + } + + size <<= up->port.regshift; + + switch (up->port.iotype) { + case SERIAL_IO_MEM: + if (up->port.mapbase) { + /* + * Unmap the area. + */ + iounmap(up->port.membase); + up->port.membase = NULL; + + start = up->port.mapbase; + + if (size) + release_mem_region(start + offset, size); + release_mem_region(start, 8 << up->port.regshift); + } + break; + + case SERIAL_IO_HUB6: + case SERIAL_IO_PORT: + start = up->port.iobase; + + if (size) + release_region(start + offset, size); + release_region(start + offset, 8 << up->port.regshift); + break; + + default: + break; + } +} + +static int serial8250_request_port(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + struct resource *res = NULL, *res_rsa = NULL; + int ret = -EBUSY; + + if (up->port.type == PORT_RSA) { + ret = serial8250_request_rsa_resource(up, &res_rsa); + if (ret) + return ret; + } + + ret = serial8250_request_std_resource(up, &res); + + /* + * If we have a mapbase, then request that as well. + */ + if (res != NULL && up->port.iotype == SERIAL_IO_MEM && + up->port.mapbase) { + int size = res->end - res->start + 1; + + up->port.membase = ioremap(up->port.mapbase, size); + if (!up->port.membase) + ret = -ENOMEM; + } + + if (ret) { + if (res_rsa) + release_resource(res_rsa); + if (res) + release_resource(res); + } + return ret; +} + +static void serial8250_config_port(struct uart_port *port, int flags) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + struct resource *res_std = NULL, *res_rsa = NULL; + int probeflags = PROBE_ANY; + int ret; + +#ifdef CONFIG_MCA + /* + * Don't probe for MCA ports on non-MCA machines. + */ + if (up->port.flags & ASYNC_BOOT_ONLYMCA && !MCA_bus) + return; +#endif + + /* + * Find the region that we can probe for. This in turn + * tells us whether we can probe for the type of port. + */ + ret = serial8250_request_std_resource(up, &res_std); + if (ret) + return; + + ret = serial8250_request_rsa_resource(up, &res_rsa); + if (ret) + probeflags &= ~PROBE_RSA; + + if (flags & UART_CONFIG_TYPE) + autoconfig(up, probeflags); + if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) + autoconfig_irq(up); + + /* + * If the port wasn't an RSA port, release the resource. + */ + if (up->port.type != PORT_RSA && res_rsa) + release_resource(res_rsa); + + if (up->port.type == PORT_UNKNOWN) + release_resource(res_std); +} + +static int +serial8250_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (ser->irq >= NR_IRQS || ser->irq < 0 || + ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || + ser->type > PORT_MAX_8250 || ser->type == PORT_CIRRUS || + ser->type == PORT_STARTECH) + return -EINVAL; + return 0; +} + +static const char * +serial8250_type(struct uart_port *port) +{ + int type = port->type; + + if (type >= ARRAY_SIZE(uart_config)) + type = 0; + return uart_config[type].name; +} + +static struct uart_ops serial8250_pops = { + tx_empty: serial8250_tx_empty, + set_mctrl: serial8250_set_mctrl, + get_mctrl: serial8250_get_mctrl, + stop_tx: serial8250_stop_tx, + start_tx: serial8250_start_tx, + stop_rx: serial8250_stop_rx, + enable_ms: serial8250_enable_ms, + break_ctl: serial8250_break_ctl, + startup: serial8250_startup, + shutdown: serial8250_shutdown, + change_speed: serial8250_change_speed, + pm: serial8250_pm, + type: serial8250_type, + release_port: serial8250_release_port, + request_port: serial8250_request_port, + config_port: serial8250_config_port, + verify_port: serial8250_verify_port, +}; + +static struct uart_8250_port serial8250_ports[UART_NR]; + +static void __init serial8250_isa_init_ports(void) +{ + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0; i < ARRAY_SIZE(old_serial_port); i++) { + serial8250_ports[i].port.iobase = old_serial_port[i].port; + serial8250_ports[i].port.irq = irq_cannonicalize(old_serial_port[i].irq); + serial8250_ports[i].port.uartclk = old_serial_port[i].base_baud * 16; + serial8250_ports[i].port.flags = old_serial_port[i].flags; + serial8250_ports[i].port.ops = &serial8250_pops; + } +} + +static void __init serial8250_register_ports(struct uart_driver *drv) +{ + int i; + + serial8250_isa_init_ports(); + + for (i = 0; i < UART_NR; i++) { + serial8250_ports[i].port.line = i; + serial8250_ports[i].port.ops = &serial8250_pops; + init_timer(&serial8250_ports[i].timer); + serial8250_ports[i].timer.function = serial8250_timeout; + uart_add_one_port(drv, &serial8250_ports[i].port); + } +} + +#ifdef CONFIG_SERIAL_8250_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct uart_8250_port *up) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = serial_in(up, UART_LSR); + + if (status & UART_LSR_BI) + up->lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + udelay(1); + } while ((status & BOTH_EMPTY) != BOTH_EMPTY); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & ASYNC_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) + udelay(1); + } +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void +serial8250_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_8250_port *up = &serial8250_ports[co->index]; + unsigned int ier; + int i; + + /* + * First save the UER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(up); + + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(up, UART_TX, *s); + if (*s == 10) { + wait_for_xmitr(up); + serial_out(up, UART_TX, 13); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + serial_out(up, UART_IER, ier); +} + +static kdev_t serial8250_console_device(struct console *co) +{ + return mk_kdev(TTY_MAJOR, 64 + co->index); +} + +static int __init serial8250_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= UART_NR) + co->index = 0; + port = &serial8250_ports[co->index].port; + printk("+++ index %d port %p iobase %x\n", co->index, port, port->iobase); + + /* + * Temporary fix. + */ + spin_lock_init(&port->lock); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + printk("+++ baud %d bits %d parity %c flow %c\n", baud, parity, bits, flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console serial8250_console = { + name: "ttyS", + write: serial8250_console_write, + device: serial8250_console_device, + setup: serial8250_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +void __init serial8250_console_init(void) +{ + serial8250_isa_init_ports(); + register_console(&serial8250_console); +} + +#define SERIAL8250_CONSOLE &serial8250_console +#else +#define SERIAL8250_CONSOLE NULL +#endif + +static struct uart_driver serial8250_reg = { + owner: THIS_MODULE, + driver_name: "serial", +#ifdef CONFIG_DEVFS_FS + dev_name: "tts/%d", +#else + dev_name: "ttyS", +#endif + major: TTY_MAJOR, + minor: 64, + nr: UART_NR, + cons: SERIAL8250_CONSOLE, +}; + +/* + * register_serial and unregister_serial allows for 16x50 serial ports to be + * configured at run-time, to support PCMCIA modems. + */ + +static int __register_serial(struct serial_struct *req, int line) +{ + struct uart_port port; + + port.iobase = req->port; + port.membase = req->iomem_base; + port.irq = req->irq; + port.uartclk = req->baud_base * 16; + port.fifosize = req->xmit_fifo_size; + port.regshift = req->iomem_reg_shift; + port.iotype = req->io_type; + port.flags = req->flags | ASYNC_BOOT_AUTOCONF; + port.line = line; + + if (HIGH_BITS_OFFSET) + port.iobase |= req->port_high << HIGH_BITS_OFFSET; + + /* + * If a clock rate wasn't specified by the low level + * driver, then default to the standard clock rate. + */ + if (port.uartclk == 0) + port.uartclk = BASE_BAUD * 16; + + return uart_register_port(&serial8250_reg, &port); +} + +/** + * register_serial - configure a 16x50 serial port at runtime + * @req: request structure + * + * Configure the serial port specified by the request. If the + * port exists and is in use an error is returned. If the port + * is not currently in the table it is added. + * + * The port is then probed and if neccessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ +int register_serial(struct serial_struct *req) +{ + return __register_serial(req, -1); +} + +int __init early_serial_setup(struct serial_struct *req) +{ + __register_serial(req, req->line); + return 0; +} + +/** + * unregister_serial - remove a 16x50 serial port at runtime + * @line: serial line number + * + * Remove one serial port. This may be called from interrupt + * context. + */ +void unregister_serial(int line) +{ + uart_unregister_port(&serial8250_reg, line); +} + +/* + * This is for ISAPNP only. + */ +void serial8250_get_irq_map(unsigned int *map) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + if (serial8250_ports[i].port.type != PORT_UNKNOWN && + serial8250_ports[i].port.irq < 16) + *map |= 1 << serial8250_ports[i].port.irq; + } +} + +static int __init serial8250_init(void) +{ + int ret, i; + + printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.80 $ " + "IRQ sharing %sabled\n", share_irqs ? "en" : "dis"); + + for (i = 0; i < NR_IRQS; i++) + spin_lock_init(&irq_lists[i].lock); + + ret = uart_register_driver(&serial8250_reg); + if (ret) + return ret; + + serial8250_register_ports(&serial8250_reg); + return 0; +} + +static void __exit serial8250_exit(void) +{ + int i; + + for (i = 0; i < UART_NR; i++) + uart_remove_one_port(&serial8250_reg, &serial8250_ports[i].port); + + uart_unregister_driver(&serial8250_reg); +} + +module_init(serial8250_init); +module_exit(serial8250_exit); + +EXPORT_SYMBOL(register_serial); +EXPORT_SYMBOL(unregister_serial); +EXPORT_SYMBOL(serial8250_get_irq_map); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.80 $"); + +MODULE_PARM(share_irqs, "i"); +MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices" + " (unsafe)"); + +#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE) +MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); +MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA"); +#endif diff --git a/drivers/serial/serial_8250.h b/drivers/serial/serial_8250.h new file mode 100644 index 000000000000..1002d1d3d693 --- /dev/null +++ b/drivers/serial/serial_8250.h @@ -0,0 +1,60 @@ +/* + * linux/drivers/char/serial_8250.h + * + * Driver for 8250/16550-type serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King. + * + * 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. + * + * $Id: serial_8250.h,v 1.7 2002/07/06 16:24:46 rmk Exp $ + */ + +#include <linux/config.h> + +struct serial8250_probe { + struct module *owner; + int (*pci_init_one)(struct pci_dev *dev); + void (*pci_remove_one)(struct pci_dev *dev); + void (*pnp_init)(void); +}; + +int serial8250_register_probe(struct serial8250_probe *probe); +void serial8250_unregister_probe(struct serial8250_probe *probe); +void serial8250_get_irq_map(unsigned int *map); + +struct old_serial_port { + unsigned int uart; + unsigned int base_baud; + unsigned int port; + unsigned int irq; + unsigned int flags; +}; + +#undef SERIAL_DEBUG_PCI + +#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) +#define SERIAL_INLINE +#endif + +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#else +#define _INLINE_ +#endif + +#define PROBE_RSA (1 << 0) +#define PROBE_ANY (~0) + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +#ifdef CONFIG_SERIAL_8250_SHARE_IRQ +#define SERIAL8250_SHARE_IRQS 1 +#else +#define SERIAL8250_SHARE_IRQS 0 +#endif diff --git a/drivers/serial/serial_8250_cs.c b/drivers/serial/serial_8250_cs.c new file mode 100644 index 000000000000..216612335008 --- /dev/null +++ b/drivers/serial/serial_8250_cs.c @@ -0,0 +1,719 @@ +/*====================================================================== + + A driver for PCMCIA serial devices + + serial_cs.c 1.123 2000/08/24 18:46:38 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU General Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/tty.h> +#include <linux/serial.h> +#include <linux/major.h> +#include <linux/tqueue.h> +#include <asm/io.h> +#include <asm/system.h> + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ciscode.h> +#include <pcmcia/ds.h> +#include <pcmcia/cisreg.h> + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = "serial_cs.c 1.123 2000/08/24 18:46:38 (David Hinds)"; +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Bit map of interrupts to choose from */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +/* Enable the speaker? */ +static int do_sound = 1; + +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); +MODULE_PARM(do_sound, "i"); + +/*====================================================================*/ + +/* Table of multi-port card ID's */ + +struct multi_id { + u_short manfid; + u_short prodid; + int multi; /* 1 = multifunction, > 1 = # ports */ +}; + +static struct multi_id multi_id[] = { + { MANFID_OMEGA, PRODID_OMEGA_QSP_100, 4 }, + { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232, 2 }, + { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 }, + { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232, 4 }, + { MANFID_SOCKET, PRODID_SOCKET_DUAL_RS232, 2 }, + { MANFID_INTEL, PRODID_INTEL_DUAL_RS232, 2 }, + { MANFID_NATINST, PRODID_NATINST_QUAD_RS232, 4 } +}; +#define MULTI_COUNT (sizeof(multi_id)/sizeof(struct multi_id)) + +typedef struct serial_info { + dev_link_t link; + int ndev; + int multi; + int slave; + int manfid; + dev_node_t node[4]; + int line[4]; + struct tq_struct remove; +} serial_info_t; + +static void serial_config(dev_link_t * link); +static int serial_event(event_t event, int priority, + event_callback_args_t * args); + +static dev_info_t dev_info = "serial_cs"; + +static dev_link_t *serial_attach(void); +static void serial_detach(dev_link_t *); + +static dev_link_t *dev_list = NULL; + +/*====================================================================*/ + +static void +cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================== + + After a card is removed, do_serial_release() will unregister + the serial device(s), and release the PCMCIA configuration. + +======================================================================*/ + +/* + * This always runs in process context. + */ +static void do_serial_release(void *arg) +{ + struct serial_info *info = arg; + int i; + + DEBUG(0, "serial_release(0x%p)\n", &info->link); + + /* + * Recheck to see if the device is still configured. + */ + if (info->link.state & DEV_CONFIG) { + for (i = 0; i < info->ndev; i++) + unregister_serial(info->line[i]); + + info->link.dev = NULL; + + if (!info->slave) { + CardServices(ReleaseConfiguration, info->link.handle); + CardServices(ReleaseIO, info->link.handle, &info->link.io); + CardServices(ReleaseIRQ, info->link.handle, &info->link.irq); + } + + info->link.state &= ~DEV_CONFIG; + } +} + +/* + * This may be called from IRQ context. + */ +static void serial_remove(dev_link_t *link) +{ + struct serial_info *info = link->priv; + + link->state &= ~DEV_PRESENT; + + /* + * FIXME: Since the card has probably been removed, + * we should call into the serial layer and hang up + * the ports on the card immediately. + */ + + if (link->state & DEV_CONFIG) + schedule_task(&info->remove); +} + +/*====================================================================== + + serial_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. + +======================================================================*/ + +static dev_link_t *serial_attach(void) +{ + serial_info_t *info; + client_reg_t client_reg; + dev_link_t *link; + int i, ret; + + DEBUG(0, "serial_attach()\n"); + + /* Create new serial device */ + info = kmalloc(sizeof (*info), GFP_KERNEL); + if (!info) + return NULL; + memset(info, 0, sizeof (*info)); + link = &info->link; + link->priv = info; + + INIT_TQUEUE(&info->remove, do_serial_release, info); + + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.NumPorts1 = 8; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + if (do_sound) { + link->conf.Attributes |= CONF_ENABLE_SPKR; + link->conf.Status = CCSR_AUDIO_ENA; + } + link->conf.IntType = INT_MEMORY_AND_IO; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &serial_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + serial_detach(link); + return NULL; + } + + return link; +} + +/*====================================================================== + + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. + +======================================================================*/ + +static void serial_detach(dev_link_t * link) +{ + serial_info_t *info = link->priv; + dev_link_t **linkp; + int ret; + + DEBUG(0, "serial_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) + break; + if (*linkp == NULL) + return; + + /* + * Ensure any outstanding scheduled tasks are completed. + */ + flush_scheduled_tasks(); + + /* + * Ensure that the ports have been released. + */ + do_serial_release(info); + + if (link->handle) { + ret = CardServices(DeregisterClient, link->handle); + if (ret != CS_SUCCESS) + cs_error(link->handle, DeregisterClient, ret); + } + + /* Unlink device structure, free bits */ + *linkp = link->next; + kfree(info); +} + +/*====================================================================*/ + +static int setup_serial(serial_info_t * info, ioaddr_t port, int irq) +{ + struct serial_struct serial; + int line; + + memset(&serial, 0, sizeof (serial)); + serial.port = port; + serial.irq = irq; + serial.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ; + line = register_serial(&serial); + if (line < 0) { + printk(KERN_NOTICE "serial_cs: register_serial() at 0x%04lx," + " irq %d failed\n", (u_long) serial.port, serial.irq); + return -1; + } + + info->line[info->ndev] = line; + sprintf(info->node[info->ndev].dev_name, "ttyS%d", line); + info->node[info->ndev].major = TTY_MAJOR; + info->node[info->ndev].minor = 0x40 + line; + if (info->ndev > 0) + info->node[info->ndev - 1].next = &info->node[info->ndev]; + info->ndev++; + + return 0; +} + +/*====================================================================*/ + +static int +get_tuple(int fn, client_handle_t handle, tuple_t * tuple, cisparse_t * parse) +{ + int i; + i = CardServices(fn, handle, tuple); + if (i != CS_SUCCESS) + return CS_NO_MORE_ITEMS; + i = CardServices(GetTupleData, handle, tuple); + if (i != CS_SUCCESS) + return i; + return CardServices(ParseTuple, handle, tuple, parse); +} + +#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) +#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c) + +/*====================================================================*/ + +static int simple_config(dev_link_t * link) +{ + static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; + client_handle_t handle = link->handle; + serial_info_t *info = link->priv; + tuple_t tuple; + u_char buf[256]; + cisparse_t parse; + cistpl_cftable_entry_t *cf = &parse.cftable_entry; + config_info_t config; + int i, j, try; + + /* If the card is already configured, look up the port and irq */ + i = CardServices(GetConfigurationInfo, handle, &config); + if ((i == CS_SUCCESS) && (config.Attributes & CONF_VALID_CLIENT)) { + ioaddr_t port = 0; + if ((config.BasePort2 != 0) && (config.NumPorts2 == 8)) { + port = config.BasePort2; + info->slave = 1; + } else if ((info->manfid == MANFID_OSITECH) && + (config.NumPorts1 == 0x40)) { + port = config.BasePort1 + 0x28; + info->slave = 1; + } + if (info->slave) + return setup_serial(info, port, config.AssignedIRQ); + } + link->conf.Vcc = config.Vcc; + + /* First pass: look for a config entry that looks normal. */ + tuple.TupleData = (cisdata_t *) buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + /* Two tries: without IO aliases, then with aliases */ + for (try = 0; try < 2; try++) { + i = first_tuple(handle, &tuple, &parse); + while (i != CS_NO_MORE_ITEMS) { + if (i != CS_SUCCESS) + goto next_entry; + if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; + if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && + (cf->io.win[0].base != 0)) { + link->conf.ConfigIndex = cf->index; + link->io.BasePort1 = cf->io.win[0].base; + link->io.IOAddrLines = (try == 0) ? + 16 : cf->io.flags & CISTPL_IO_LINES_MASK; + i = + CardServices(RequestIO, link->handle, + &link->io); + if (i == CS_SUCCESS) + goto found_port; + } + next_entry: + i = next_tuple(handle, &tuple, &parse); + } + } + + /* Second pass: try to find an entry that isn't picky about + its base address, then try to grab any standard serial port + address, and finally try to get any free port. */ + i = first_tuple(handle, &tuple, &parse); + while (i != CS_NO_MORE_ITEMS) { + if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && + ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { + link->conf.ConfigIndex = cf->index; + for (j = 0; j < 5; j++) { + link->io.BasePort1 = base[j]; + link->io.IOAddrLines = base[j] ? 16 : 3; + i = CardServices(RequestIO, link->handle, + &link->io); + if (i == CS_SUCCESS) + goto found_port; + } + } + i = next_tuple(handle, &tuple, &parse); + } + + found_port: + if (i != CS_SUCCESS) { + printk(KERN_NOTICE + "serial_cs: no usable port range found, giving up\n"); + cs_error(link->handle, RequestIO, i); + return -1; + } + + i = CardServices(RequestIRQ, link->handle, &link->irq); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIRQ, i); + link->irq.AssignedIRQ = 0; + } + if (info->multi && (info->manfid == MANFID_3COM)) + link->conf.ConfigIndex &= ~(0x08); + i = CardServices(RequestConfiguration, link->handle, &link->conf); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestConfiguration, i); + return -1; + } + + return setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); +} + +static int multi_config(dev_link_t * link) +{ + client_handle_t handle = link->handle; + serial_info_t *info = link->priv; + tuple_t tuple; + u_char buf[256]; + cisparse_t parse; + cistpl_cftable_entry_t *cf = &parse.cftable_entry; + int i, base2 = 0; + + tuple.TupleData = (cisdata_t *) buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + + /* First, look for a generic full-sized window */ + link->io.NumPorts1 = info->multi * 8; + i = first_tuple(handle, &tuple, &parse); + while (i != CS_NO_MORE_ITEMS) { + /* The quad port cards have bad CIS's, so just look for a + window larger than 8 ports and assume it will be right */ + if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && + (cf->io.win[0].len > 8)) { + link->conf.ConfigIndex = cf->index; + link->io.BasePort1 = cf->io.win[0].base; + link->io.IOAddrLines = + cf->io.flags & CISTPL_IO_LINES_MASK; + i = CardServices(RequestIO, link->handle, &link->io); + base2 = link->io.BasePort1 + 8; + if (i == CS_SUCCESS) + break; + } + i = next_tuple(handle, &tuple, &parse); + } + + /* If that didn't work, look for two windows */ + if (i != CS_SUCCESS) { + link->io.NumPorts1 = link->io.NumPorts2 = 8; + info->multi = 2; + i = first_tuple(handle, &tuple, &parse); + while (i != CS_NO_MORE_ITEMS) { + if ((i == CS_SUCCESS) && (cf->io.nwin == 2)) { + link->conf.ConfigIndex = cf->index; + link->io.BasePort1 = cf->io.win[0].base; + link->io.BasePort2 = cf->io.win[1].base; + link->io.IOAddrLines = + cf->io.flags & CISTPL_IO_LINES_MASK; + i = + CardServices(RequestIO, link->handle, + &link->io); + base2 = link->io.BasePort2; + if (i == CS_SUCCESS) + break; + } + i = next_tuple(handle, &tuple, &parse); + } + } + + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIO, i); + return -1; + } + + i = CardServices(RequestIRQ, link->handle, &link->irq); + if (i != CS_SUCCESS) { + printk(KERN_NOTICE + "serial_cs: no usable port range found, giving up\n"); + cs_error(link->handle, RequestIRQ, i); + link->irq.AssignedIRQ = 0; + } + /* Socket Dual IO: this enables irq's for second port */ + if (info->multi && (info->manfid == MANFID_SOCKET)) { + link->conf.Present |= PRESENT_EXT_STATUS; + link->conf.ExtStatus = ESR_REQ_ATTN_ENA; + } + i = CardServices(RequestConfiguration, link->handle, &link->conf); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestConfiguration, i); + return -1; + } + + setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); + /* The Nokia cards are not really multiport cards */ + if (info->manfid == MANFID_NOKIA) + return 0; + for (i = 0; i < info->multi - 1; i++) + setup_serial(info, base2 + (8 * i), link->irq.AssignedIRQ); + + return 0; +} + +/*====================================================================== + + serial_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + serial device available to the system. + +======================================================================*/ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +void serial_config(dev_link_t * link) +{ + client_handle_t handle = link->handle; + serial_info_t *info = link->priv; + tuple_t tuple; + u_short buf[128]; + cisparse_t parse; + cistpl_cftable_entry_t *cf = &parse.cftable_entry; + int i, last_ret, last_fn; + + DEBUG(0, "serial_config(0x%p)\n", link); + + tuple.TupleData = (cisdata_t *) buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + tuple.Attributes = 0; + /* Get configuration register information */ + tuple.DesiredTuple = CISTPL_CONFIG; + last_ret = first_tuple(handle, &tuple, &parse); + if (last_ret != CS_SUCCESS) { + last_fn = ParseTuple; + goto cs_failed; + } + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Configure card */ + link->state |= DEV_CONFIG; + + /* Is this a compliant multifunction card? */ + tuple.DesiredTuple = CISTPL_LONGLINK_MFC; + tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK; + info->multi = (first_tuple(handle, &tuple, &parse) == CS_SUCCESS); + + /* Is this a multiport card? */ + tuple.DesiredTuple = CISTPL_MANFID; + if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) { + info->manfid = le16_to_cpu(buf[0]); + for (i = 0; i < MULTI_COUNT; i++) + if ((info->manfid == multi_id[i].manfid) && + (le16_to_cpu(buf[1]) == multi_id[i].prodid)) + break; + if (i < MULTI_COUNT) + info->multi = multi_id[i].multi; + } + + /* Another check for dual-serial cards: look for either serial or + multifunction cards that ask for appropriate IO port ranges */ + tuple.DesiredTuple = CISTPL_FUNCID; + if ((info->multi == 0) && + ((first_tuple(handle, &tuple, &parse) != CS_SUCCESS) || + (parse.funcid.func == CISTPL_FUNCID_MULTI) || + (parse.funcid.func == CISTPL_FUNCID_SERIAL))) { + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) { + if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0)) + info->multi = cf->io.win[0].len >> 3; + if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) && + (cf->io.win[1].len == 8)) + info->multi = 2; + } + } + + if (info->multi > 1) + multi_config(link); + else + simple_config(link); + + if (info->ndev == 0) + goto failed; + + if (info->manfid == MANFID_IBM) { + conf_reg_t reg = { 0, CS_READ, 0x800, 0 }; + CS_CHECK(AccessConfigurationRegister, link->handle, ®); + reg.Action = CS_WRITE; + reg.Value = reg.Value | 1; + CS_CHECK(AccessConfigurationRegister, link->handle, ®); + } + + link->dev = &info->node[0]; + link->state &= ~DEV_CONFIG_PENDING; + return; + + cs_failed: + cs_error(link->handle, last_fn, last_ret); + failed: + do_serial_release(info); +} + +/*====================================================================== + + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the serial drivers from + talking to the ports. + +======================================================================*/ + +static int +serial_event(event_t event, int priority, event_callback_args_t * args) +{ + dev_link_t *link = args->client_data; + serial_info_t *info = link->priv; + + DEBUG(1, "serial_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + serial_remove(link); + break; + + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + serial_config(link); + break; + + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if ((link->state & DEV_CONFIG) && !info->slave) + CardServices(ReleaseConfiguration, link->handle); + break; + + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (DEV_OK(link) && !info->slave) + CardServices(RequestConfiguration, link->handle, + &link->conf); + break; + } + return 0; +} + +/*====================================================================*/ + +static int __init init_serial_cs(void) +{ + servinfo_t serv; + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "serial_cs: Card Services release " + "does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &serial_attach, &serial_detach); + return 0; +} + +static void __exit exit_serial_cs(void) +{ + DEBUG(0, "serial_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + serial_detach(dev_list); +} + +module_init(init_serial_cs); +module_exit(exit_serial_cs); + +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/serial_8250_pci.c b/drivers/serial/serial_8250_pci.c new file mode 100644 index 000000000000..5b20507bbbc8 --- /dev/null +++ b/drivers/serial/serial_8250_pci.c @@ -0,0 +1,1138 @@ +/* + * linux/drivers/char/serial_8250_pci.c + * + * Probe module for 8250/16550-type PCI serial ports. + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * 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. + * + * $Id: serial_8250_pci.c,v 1.18 2002/03/10 22:32:08 rmk Exp $ + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/serial.h> + +/* 2.4.6 compatibility cruft - to be removed with the old serial.c code */ +#define pci_board __pci_board +#include <linux/serialP.h> +#undef pci_board + +#include <asm/bitops.h> +#include <asm/byteorder.h> +#include <asm/serial.h> + +#include "serial_8250.h" + + +#ifndef IS_PCI_REGION_IOPORT +#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_IO) +#endif +#ifndef IS_PCI_REGION_IOMEM +#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_MEM) +#endif +#ifndef PCI_IRQ_RESOURCE +#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) +#endif + +#ifndef pci_get_subvendor +#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) +#define pci_get_subdevice(dev) ((dev)->subsystem_device) +#endif + +struct serial_private { + unsigned int nr; + struct pci_board *board; + int line[0]; +}; + +struct pci_board { + int flags; + int num_ports; + int base_baud; + int uart_offset; + int reg_shift; + int (*init_fn)(struct pci_dev *dev, struct pci_board *board, + int enable); + int first_uart_offset; +}; + +static int +get_pci_port(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned long port; + int base_idx; + int max_port; + int offset; + + base_idx = SPCI_FL_GET_BASE(board->flags); + if (board->flags & SPCI_FL_BASE_TABLE) + base_idx += idx; + + if (board->flags & SPCI_FL_REGION_SZ_CAP) { + max_port = pci_resource_len(dev, base_idx) / 8; + if (idx >= max_port) + return 1; + } + + offset = board->first_uart_offset; + + /* + * Timedia/SUNIX uses a mixture of BARs and offsets + * Ugh, this is ugly as all hell --- TYT + */ + if (dev->vendor == PCI_VENDOR_ID_TIMEDIA) + switch(idx) { + case 0: + base_idx = 0; + break; + case 1: + base_idx = 0; + offset = 8; + break; + case 2: + base_idx = 1; + break; + case 3: + base_idx = 1; + offset = 8; + break; + case 4: /* BAR 2 */ + case 5: /* BAR 3 */ + case 6: /* BAR 4 */ + case 7: /* BAR 5 */ + base_idx = idx - 2; + } + + /* AFAVLAB uses a different mixture of BARs and offsets */ + /* Not that ugly ;) -- HW */ + if (dev->vendor == PCI_VENDOR_ID_AFAVLAB && idx >= 4) { + base_idx = 4; + offset = (idx - 4) * 8; + } + + /* Some Titan cards are also a little weird */ + if (dev->vendor == PCI_VENDOR_ID_TITAN && + (dev->device == PCI_DEVICE_ID_TITAN_400L || + dev->device == PCI_DEVICE_ID_TITAN_800L)) { + switch (idx) { + case 0: base_idx = 1; + break; + case 1: base_idx = 2; + break; + default: + base_idx = 4; + offset = 8 * (idx - 2); + } + } + + port = pci_resource_start(dev, base_idx) + offset; + + if ((board->flags & SPCI_FL_BASE_TABLE) == 0) + port += idx * (board->uart_offset ? board->uart_offset : 8); + + if (IS_PCI_REGION_IOPORT(dev, base_idx)) { + req->port = port; + if (HIGH_BITS_OFFSET) + req->port_high = port >> HIGH_BITS_OFFSET; + else + req->port_high = 0; + return 0; + } + req->io_type = SERIAL_IO_MEM; + req->iomem_base = ioremap(port, board->uart_offset); + if (req->iomem_base == NULL) + return -ENOMEM; + req->iomem_reg_shift = board->reg_shift; + req->port = 0; + return 0; +} + +static _INLINE_ int +get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) +{ + int base_idx; + + if ((board->flags & SPCI_FL_IRQRESOURCE) == 0) + return dev->irq; + + base_idx = SPCI_FL_GET_IRQBASE(board->flags); + if (board->flags & SPCI_FL_IRQ_TABLE) + base_idx += idx; + + return PCI_IRQ_RESOURCE(dev, base_idx); +} + +/* + * Some PCI serial cards using the PLX 9050 PCI interface chip require + * that the card interrupt be explicitly enabled or disabled. This + * seems to be mainly needed on card using the PLX which also use I/O + * mapped memory. + */ +static int __devinit +pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + u8 *p, irq_config = 0; + + if (enable) { + irq_config = 0x41; + if (dev->vendor == PCI_VENDOR_ID_PANACOM) + irq_config = 0x43; + if ((dev->vendor == PCI_VENDOR_ID_PLX) && + (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { + /* + * As the megawolf cards have the int pins active + * high, and have 2 UART chips, both ints must be + * enabled on the 9050. Also, the UARTS are set in + * 16450 mode by default, so we have to enable the + * 16C950 'enhanced' mode so that we can use the + * deep FIFOs + */ + irq_config = 0x5b; + } + } + + /* + * enable/disable interrupts + */ + p = ioremap(pci_resource_start(dev, 0), 0x80); + if (p == NULL) + return -ENOMEM; + writel(irq_config, (unsigned long)p + 0x4c); + + /* + * Read the register back to ensure that it took effect. + */ + readl((unsigned long)p + 0x4c); + iounmap(p); + + return 0; +} + + +/* + * SIIG serial cards have an PCI interface chip which also controls + * the UART clocking frequency. Each UART can be clocked independently + * (except cards equiped with 4 UARTs) and initial clocking settings + * are stored in the EEPROM chip. It can cause problems because this + * version of serial driver doesn't support differently clocked UART's + * on single PCI card. To prevent this, initialization functions set + * high frequency clocking for all UART's on given card. It is safe (I + * hope) because it doesn't touch EEPROM settings to prevent conflicts + * with other OSes (like M$ DOS). + * + * SIIG support added by Andrey Panin <pazke@mail.tp.ru>, 10/1999 + * + * There is two family of SIIG serial cards with different PCI + * interface chip and different configuration methods: + * - 10x cards have control registers in IO and/or memory space; + * - 20x cards have control registers in standard PCI configuration space. + */ + +#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) +#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) + +static int __devinit +pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + u16 data, *p; + + if (!enable) + return 0; + + switch (dev->device & 0xfff8) { + case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ + data = 0xffdf; + break; + case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ + data = 0xf7ff; + break; + default: /* 1S1P, 4S */ + data = 0xfffb; + break; + } + + p = ioremap(pci_resource_start(dev, 0), 0x80); + if (p == NULL) + return -ENOMEM; + + writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28); + iounmap(p); + return 0; +} + +#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) +#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) + +static int __devinit +pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + u8 data; + + if (!enable) + return 0; + + /* Change clock frequency for the first UART. */ + pci_read_config_byte(dev, 0x6f, &data); + pci_write_config_byte(dev, 0x6f, data & 0xef); + + /* If this card has 2 UART, we have to do the same with second UART. */ + if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) || + ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) { + pci_read_config_byte(dev, 0x73, &data); + pci_write_config_byte(dev, 0x73, data & 0xef); + } + return 0; +} + +/* Added for EKF Intel i960 serial boards */ +static int __devinit +pci_inteli960ni_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + unsigned long oldval; + + if (!(pci_get_subdevice(dev) & 0x1000)) + return -ENODEV; + + if (!enable) /* is there something to deinit? */ + return 0; + + /* is firmware started? */ + pci_read_config_dword(dev, 0x44, (void*) &oldval); + if (oldval == 0x00001000L) { /* RESET value */ + printk(KERN_DEBUG "Local i960 firmware missing"); + return -ENODEV; + } + return 0; +} + +/* + * Timedia has an explosion of boards, and to avoid the PCI table from + * growing *huge*, we use this function to collapse some 70 entries + * in the PCI table into one, for sanity's and compactness's sake. + */ +static unsigned short timedia_single_port[] = { + 0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0 +}; + +static unsigned short timedia_dual_port[] = { + 0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085, + 0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, + 0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, + 0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079, + 0xD079, 0 +}; + +static unsigned short timedia_quad_port[] = { + 0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, + 0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, + 0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056, + 0xB157, 0 +}; + +static unsigned short timedia_eight_port[] = { + 0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, + 0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0 +}; + +static struct timedia_struct { + int num; + unsigned short *ids; +} timedia_data[] = { + { 1, timedia_single_port }, + { 2, timedia_dual_port }, + { 4, timedia_quad_port }, + { 8, timedia_eight_port }, + { 0, 0 } +}; + +static int __devinit +pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + int i, j; + unsigned short *ids; + + if (!enable) + return 0; + + for (i = 0; timedia_data[i].num; i++) { + ids = timedia_data[i].ids; + for (j = 0; ids[j]; j++) { + if (pci_get_subdevice(dev) == ids[j]) { + board->num_ports = timedia_data[i].num; + return 0; + } + } + } + return 0; +} + +static int __devinit +pci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/10); + return 0; +} + +/* + * This is the configuration table for all of the PCI serial boards + * which we support. It is directly indexed by the pci_board_num_t enum + * value, which is encoded in the pci_device_id PCI probe table's + * driver_data member. + */ +enum pci_board_num_t { + pbn_b0_1_115200, + pbn_default = 0, + + pbn_b0_2_115200, + pbn_b0_4_115200, + + pbn_b0_1_921600, + pbn_b0_2_921600, + pbn_b0_4_921600, + + pbn_b0_bt_1_115200, + pbn_b0_bt_2_115200, + pbn_b0_bt_8_115200, + pbn_b0_bt_1_460800, + pbn_b0_bt_2_460800, + + pbn_b1_1_115200, + pbn_b1_2_115200, + pbn_b1_4_115200, + pbn_b1_8_115200, + + pbn_b1_2_921600, + pbn_b1_4_921600, + pbn_b1_8_921600, + + pbn_b1_2_1382400, + pbn_b1_4_1382400, + pbn_b1_8_1382400, + + pbn_b2_8_115200, + pbn_b2_4_460800, + pbn_b2_8_460800, + pbn_b2_16_460800, + pbn_b2_4_921600, + pbn_b2_8_921600, + + pbn_b2_bt_1_115200, + pbn_b2_bt_2_115200, + pbn_b2_bt_4_115200, + pbn_b2_bt_2_921600, + + pbn_panacom, + pbn_panacom2, + pbn_panacom4, + pbn_plx_romulus, + pbn_oxsemi, + pbn_timedia, + pbn_intel_i960, + pbn_sgi_ioc3, + pbn_nec_nile4, + + pbn_dci_pccom4, + pbn_dci_pccom8, + + pbn_xircom_combo, + + pbn_siig10x_0, + pbn_siig10x_1, + pbn_siig10x_2, + pbn_siig10x_4, + pbn_siig20x_0, + pbn_siig20x_2, + pbn_siig20x_4, + + pbn_computone_4, + pbn_computone_6, + pbn_computone_8, +}; + +static struct pci_board pci_boards[] __devinitdata = { + /* + * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, + * Offset to get to next UART's registers, + * Register shift to use for memory-mapped I/O, + * Initialization function, first UART offset + */ + + /* Generic serial board, pbn_b0_1_115200, pbn_default */ + { SPCI_FL_BASE0, 1, 115200 }, /* pbn_b0_1_115200, + pbn_default */ + + { SPCI_FL_BASE0, 2, 115200 }, /* pbn_b0_2_115200 */ + { SPCI_FL_BASE0, 4, 115200 }, /* pbn_b0_4_115200 */ + + { SPCI_FL_BASE0, 1, 921600 }, /* pbn_b0_1_921600 */ + { SPCI_FL_BASE0, 2, 921600 }, /* pbn_b0_2_921600 */ + { SPCI_FL_BASE0, 4, 921600 }, /* pbn_b0_4_921600 */ + + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 8, 115200 }, /* pbn_b0_bt_8_115200 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ + + { SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */ + { SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */ + { SPCI_FL_BASE1, 4, 115200 }, /* pbn_b1_4_115200 */ + { SPCI_FL_BASE1, 8, 115200 }, /* pbn_b1_8_115200 */ + + { SPCI_FL_BASE1, 2, 921600 }, /* pbn_b1_2_921600 */ + { SPCI_FL_BASE1, 4, 921600 }, /* pbn_b1_4_921600 */ + { SPCI_FL_BASE1, 8, 921600 }, /* pbn_b1_8_921600 */ + + { SPCI_FL_BASE1, 2, 1382400 }, /* pbn_b1_2_1382400 */ + { SPCI_FL_BASE1, 4, 1382400 }, /* pbn_b1_4_1382400 */ + { SPCI_FL_BASE1, 8, 1382400 }, /* pbn_b1_8_1382400 */ + + { SPCI_FL_BASE2, 8, 115200 }, /* pbn_b2_8_115200 */ + { SPCI_FL_BASE2, 4, 460800 }, /* pbn_b2_4_460800 */ + { SPCI_FL_BASE2, 8, 460800 }, /* pbn_b2_8_460800 */ + { SPCI_FL_BASE2, 16, 460800 }, /* pbn_b2_16_460800 */ + { SPCI_FL_BASE2, 4, 921600 }, /* pbn_b2_4_921600 */ + { SPCI_FL_BASE2, 8, 921600 }, /* pbn_b2_8_921600 */ + + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */ + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */ + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */ + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */ + + { SPCI_FL_BASE2, 2, 921600, /* IOMEM */ /* pbn_panacom */ + 0x400, 7, pci_plx9050_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_panacom2 */ + 0x400, 7, pci_plx9050_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_panacom4 */ + 0x400, 7, pci_plx9050_fn }, + { SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */ + 0x20, 2, pci_plx9050_fn, 0x03 }, + /* This board uses the size of PCI Base region 0 to + * signal now many ports are available */ + { SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */ + { SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */ + 0, 0, pci_timedia_fn }, + /* EKF addition for i960 Boards form EKF with serial port */ + { SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ /* pbn_intel_i960 */ + 8<<2, 2, pci_inteli960ni_fn, 0x10000}, + { SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE, /* pbn_sgi_ioc3 */ + 1, 458333, 0, 0, 0, 0x20178 }, + + /* + * NEC Vrc-5074 (Nile 4) builtin UART. + */ + { SPCI_FL_BASE0, 1, 520833, /* pbn_nec_nile4 */ + 64, 3, NULL, 0x300 }, + + { SPCI_FL_BASE3, 4, 115200, 8 }, /* pbn_dci_pccom4 */ + { SPCI_FL_BASE3, 8, 115200, 8 }, /* pbn_dci_pccom8 */ + + { SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */ + 0, 0, pci_xircom_fn }, + + { SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siig10x_2 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siig10x_4 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE0, 1, 921600, /* pbn_siix20x_0 */ + 0, 0, pci_siig20x_fn }, + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siix20x_2 */ + 0, 0, pci_siig20x_fn }, + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */ + 0, 0, pci_siig20x_fn }, + + { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */ + 0x40, 2, NULL, 0x200 }, + { SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */ + 0x40, 2, NULL, 0x200 }, + { SPCI_FL_BASE0, 8, 921600, /* IOMEM */ /* pbn_computone_8 */ + 0x40, 2, NULL, 0x200 }, +}; + +/* + * Given a complete unknown PCI device, try to use some heuristics to + * guess what the configuration might be, based on the pitiful PCI + * serial specs. Returns 0 on success, 1 on failure. + */ +static int __devinit +serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board) +{ + int num_iomem = 0, num_port = 0, first_port = -1; + int i; + + /* + * If it is not a communications device or the programming + * interface is greater than 6, give up. + * + * (Should we try to make guesses for multiport serial devices + * later?) + */ + if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && + ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || + (dev->class & 0xff) > 6) + return 1; + + for (i = 0; i < 6; i++) { + if (IS_PCI_REGION_IOPORT(dev, i)) { + num_port++; + if (first_port == -1) + first_port = i; + } + if (IS_PCI_REGION_IOMEM(dev, i)) + num_iomem++; + } + + /* + * If there is 1 or 0 iomem regions, and exactly one port, use + * it. + */ + if (num_iomem <= 1 && num_port == 1) { + board->flags = first_port; + return 0; + } + return 1; +} + +/* + * return an error code to refuse. + * + * serial_struct is 60 bytes. + */ +static int __devinit pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) +{ + struct serial_private *priv; + struct pci_board *board, tmp; + struct serial_struct serial_req; + int base_baud, rc, k; + + if (ent->driver_data >= ARRAY_SIZE(pci_boards)) { + printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n", + ent->driver_data); + return -EINVAL; + } + + board = &pci_boards[ent->driver_data]; + + rc = pci_enable_device(dev); + if (rc) + return rc; + + if (ent->driver_data == pbn_default && + serial_pci_guess_board(dev, board)) + return -ENODEV; + else if (serial_pci_guess_board(dev, &tmp) == 0) { + printk(KERN_INFO "Redundant entry in serial pci_table. " + "Please send the output of\n" + "lspci -vv, this message (%d,%d,%d,%d)\n" + "and the manufacturer and name of " + "serial board or modem board\n" + "to serial-pci-info@lists.sourceforge.net.\n", + dev->vendor, dev->device, + pci_get_subvendor(dev), pci_get_subdevice(dev)); + } + + priv = kmalloc(sizeof(struct serial_private) + + sizeof(unsigned int) * board->num_ports, + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* + * Run the initialization function, if any + */ + if (board->init_fn) { + rc = board->init_fn(dev, board, 1); + if (rc != 0) { + kfree(priv); + return rc; + } + } + + base_baud = board->base_baud; + if (!base_baud) + base_baud = BASE_BAUD; + memset(&serial_req, 0, sizeof(serial_req)); + for (k = 0; k < board->num_ports; k++) { + serial_req.irq = get_pci_irq(dev, board, k); + if (get_pci_port(dev, board, &serial_req, k)) + break; +#ifdef SERIAL_DEBUG_PCI + printk("Setup PCI/PNP port: port %x, irq %d, type %d\n", + serial_req.port, serial_req.irq, serial_req.io_type); +#endif + serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; + serial_req.baud_base = base_baud; + + priv->line[k] = register_serial(&serial_req); + if (priv->line[k] < 0) + break; + } + + priv->board = board; + priv->nr = k; + + pci_set_drvdata(dev, priv); + + return 0; +} + +static void __devexit pci_remove_one(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + int i; + + pci_set_drvdata(dev, NULL); + + if (priv) { + for (i = 0; i < priv->nr; i++) + unregister_serial(priv->line[i]); + + priv->board->init_fn(dev, priv->board, 0); + + pci_disable_device(dev); + + kfree(priv); + } +} + +static struct pci_device_id serial_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, + pbn_b1_2_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, + pbn_b1_4_921600 }, + + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_1_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_115200 }, + + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + /* VScom SPCOM800, from sl@s.pl */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_921600 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_4_921600 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_KEYSPAN, + PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, + pbn_panacom }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom4 }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom2 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, + pbn_b2_4_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, + pbn_b2_8_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, + pbn_b2_16_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, + pbn_b2_16_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, + PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, + pbn_b2_4_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, + PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, + pbn_b2_8_460800 }, + /* Megawolf Romulus PCI Serial Card, from Mike Hudson */ + /* (Exoray@isys.ca) */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, + 0x10b5, 0x106a, 0, 0, + pbn_plx_romulus }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_115200 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_115200 }, + + /* Digitan DS560-558, from jimd@esoft.com */ + { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_115200 }, + + /* 3Com US Robotics 56k Voice Internal PCI model 5610 */ + { PCI_VENDOR_ID_USR, 0x1008, + PCI_ANY_ID, PCI_ANY_ID, }, + + /* Titan Electronic cards */ + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE1, 1, 921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, + /* The 400L and 800L have a custom hack in get_pci_port */ + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE_TABLE, 4, 921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE_TABLE, 8, 921600 }, + + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_1 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_1 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_1 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_4 }, + + /* Computone devices submitted by Doug McNash dmcnash@computone.com */ + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, + 0, 0, pbn_computone_4 }, + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, + 0, 0, pbn_computone_8 }, + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, + 0, 0, pbn_computone_6 }, + + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi }, + { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, + PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia }, + + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + /* AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org> */ + { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_115200 }, + + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_460800 }, + + /* RAStel 2 port modem, gerg@moreton.com.au */ + { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + + /* EKF addition for i960 Boards form EKF with serial port */ + { PCI_VENDOR_ID_INTEL, 0x1960, + 0xE4BF, PCI_ANY_ID, 0, 0, + pbn_intel_i960 }, + + /* Xircom Cardbus/Ethernet combos */ + { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_xircom_combo }, + + /* + * Untested PCI modems, sent in from various folks... + */ + + /* Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de> */ + { PCI_VENDOR_ID_ROCKWELL, 0x1004, + 0x1048, 0x1500, 0, 0, + pbn_b1_1_115200 }, + + { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, + 0xFF00, 0, 0, 0, + pbn_sgi_ioc3 }, + + /* + * NEC Vrc-5074 (Nile 4) builtin UART. + */ + { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NILE4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_nec_nile4 }, + + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_dci_pccom4 }, + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_dci_pccom8 }, + + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL << 8, + 0xffff00, }, + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MODEM << 8, + 0xffff00, }, + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, + 0xffff00, }, + { 0, } +}; + +#ifndef __devexit_p +#if defined(MODULE) || defined(CONFIG_HOTPLUG) +#define __devexit_p(x) x +#else +#define __devexit_p(x) NULL +#endif +#endif + +static struct pci_driver serial_pci_driver = { + name: "serial", + probe: pci_init_one, + remove: __devexit_p(pci_remove_one), + id_table: serial_pci_tbl, +}; + +static int __init serial8250_pci_init(void) +{ + return pci_module_init(&serial_pci_driver); +} + +static void __exit serial8250_pci_exit(void) +{ + pci_unregister_driver(&serial_pci_driver); +} + +module_init(serial8250_pci_init); +module_exit(serial8250_pci_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); +MODULE_DEVICE_TABLE(pci, serial_pci_tbl); diff --git a/drivers/serial/serial_8250_pnp.c b/drivers/serial/serial_8250_pnp.c new file mode 100644 index 000000000000..37159599913f --- /dev/null +++ b/drivers/serial/serial_8250_pnp.c @@ -0,0 +1,548 @@ +/* + * linux/drivers/char/serial_8250_pnp.c + * + * Probe module for 8250/16550-type ISAPNP serial ports. + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * 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. + * + * $Id: serial_8250_pnp.c,v 1.9 2002/02/18 19:20:29 rmk Exp $ + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/isapnp.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/serial.h> +#include <linux/serialP.h> + +#include <asm/bitops.h> +#include <asm/byteorder.h> +#include <asm/serial.h> + +#include "serial_8250.h" + +struct pnpbios_device_id +{ + char id[8]; + unsigned long driver_data; +}; + +static const struct pnpbios_device_id pnp_dev_table[] = { + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "AAC000F", 0 }, + /* Anchor Datacomm BV */ + /* SXPro 144 External Data Fax Modem Plug & Play */ + { "ADC0001", 0 }, + /* SXPro 288 External Data Fax Modem Plug & Play */ + { "ADC0002", 0 }, + /* Rockwell 56K ACF II Fax+Data+Voice Modem */ + { "AKY1021", SPCI_FL_NO_SHIRQ }, + /* AZT3005 PnP SOUND DEVICE */ + { "AZT4001", 0 }, + /* Best Data Products Inc. Smart One 336F PnP Modem */ + { "BDP3336", 0 }, + /* Boca Research */ + /* Boca Complete Ofc Communicator 14.4 Data-FAX */ + { "BRI0A49", 0 }, + /* Boca Research 33,600 ACF Modem */ + { "BRI1400", 0 }, + /* Boca 33.6 Kbps Internal FD34FSVD */ + { "BRI3400", 0 }, + /* Boca 33.6 Kbps Internal FD34FSVD */ + { "BRI0A49", 0 }, + /* Best Data Products Inc. Smart One 336F PnP Modem */ + { "BDP3336", 0 }, + /* Computer Peripherals Inc */ + /* EuroViVa CommCenter-33.6 SP PnP */ + { "CPI4050", 0 }, + /* Creative Labs */ + /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ + { "CTL3001", 0 }, + /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ + { "CTL3011", 0 }, + /* Creative */ + /* Creative Modem Blaster Flash56 DI5601-1 */ + { "DMB1032", 0 }, + /* Creative Modem Blaster V.90 DI5660 */ + { "DMB2001", 0 }, + /* FUJITSU */ + /* Fujitsu 33600 PnP-I2 R Plug & Play */ + { "FUJ0202", 0 }, + /* Fujitsu FMV-FX431 Plug & Play */ + { "FUJ0205", 0 }, + /* Fujitsu 33600 PnP-I4 R Plug & Play */ + { "FUJ0206", 0 }, + /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ + { "FUJ0209", 0 }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "GVC000F", 0 }, + /* Hayes */ + /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ + { "HAY0001", 0 }, + /* Hayes Optima 336 V.34 + FAX + Voice PnP */ + { "HAY000C", 0 }, + /* Hayes Optima 336B V.34 + FAX + Voice PnP */ + { "HAY000D", 0 }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { "HAY5670", 0 }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { "HAY5674", 0 }, + /* Hayes Accura 56K Fax Modem PnP */ + { "HAY5675", 0 }, + /* Hayes 288, V.34 + FAX */ + { "HAYF000", 0 }, + /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ + { "HAYF001", 0 }, + /* IBM */ + /* IBM Thinkpad 701 Internal Modem Voice */ + { "IBM0033", 0 }, + /* Intertex */ + /* Intertex 28k8 33k6 Voice EXT PnP */ + { "IXDC801", 0 }, + /* Intertex 33k6 56k Voice EXT PnP */ + { "IXDC901", 0 }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { "IXDD801", 0 }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { "IXDD901", 0 }, + /* Intertex 28k8 33k6 Voice SP INT PnP */ + { "IXDF401", 0 }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { "IXDF801", 0 }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { "IXDF901", 0 }, + /* Kortex International */ + /* KORTEX 28800 Externe PnP */ + { "KOR4522", 0 }, + /* KXPro 33.6 Vocal ASVD PnP */ + { "KORF661", 0 }, + /* Lasat */ + /* LASAT Internet 33600 PnP */ + { "LAS4040", 0 }, + /* Lasat Safire 560 PnP */ + { "LAS4540", 0 }, + /* Lasat Safire 336 PnP */ + { "LAS5440", 0 }, + /* Microcom, Inc. */ + /* Microcom TravelPorte FAST V.34 Plug & Play */ + { "MNP0281", 0 }, + /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ + { "MNP0336", 0 }, + /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ + { "MNP0339", 0 }, + /* Microcom DeskPorte 28.8P Plug & Play */ + { "MNP0342", 0 }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { "MNP0500", 0 }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { "MNP0501", 0 }, + /* Microcom DeskPorte 28.8S Internal Plug & Play */ + { "MNP0502", 0 }, + /* Motorola */ + /* Motorola BitSURFR Plug & Play */ + { "MOT1105", 0 }, + /* Motorola TA210 Plug & Play */ + { "MOT1111", 0 }, + /* Motorola HMTA 200 (ISDN) Plug & Play */ + { "MOT1114", 0 }, + /* Motorola BitSURFR Plug & Play */ + { "MOT1115", 0 }, + /* Motorola Lifestyle 28.8 Internal */ + { "MOT1190", 0 }, + /* Motorola V.3400 Plug & Play */ + { "MOT1501", 0 }, + /* Motorola Lifestyle 28.8 V.34 Plug & Play */ + { "MOT1502", 0 }, + /* Motorola Power 28.8 V.34 Plug & Play */ + { "MOT1505", 0 }, + /* Motorola ModemSURFR External 28.8 Plug & Play */ + { "MOT1509", 0 }, + /* Motorola Premier 33.6 Desktop Plug & Play */ + { "MOT150A", 0 }, + /* Motorola VoiceSURFR 56K External PnP */ + { "MOT150F", 0 }, + /* Motorola ModemSURFR 56K External PnP */ + { "MOT1510", 0 }, + /* Motorola ModemSURFR 56K Internal PnP */ + { "MOT1550", 0 }, + /* Motorola ModemSURFR Internal 28.8 Plug & Play */ + { "MOT1560", 0 }, + /* Motorola Premier 33.6 Internal Plug & Play */ + { "MOT1580", 0 }, + /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ + { "MOT15B0", 0 }, + /* Motorola VoiceSURFR 56K Internal PnP */ + { "MOT15F0", 0 }, + /* Com 1 */ + /* Deskline K56 Phone System PnP */ + { "MVX00A1", 0 }, + /* PC Rider K56 Phone System PnP */ + { "MVX00F2", 0 }, + /* Pace 56 Voice Internal Plug & Play Modem */ + { "PMC2430", 0 }, + /* Generic */ + /* Generic standard PC COM port */ + { "PNP0500", 0 }, + /* Generic 16550A-compatible COM port */ + { "PNP0501", 0 }, + /* Compaq 14400 Modem */ + { "PNPC000", 0 }, + /* Compaq 2400/9600 Modem */ + { "PNPC001", 0 }, + /* Dial-Up Networking Serial Cable between 2 PCs */ + { "PNPC031", 0 }, + /* Dial-Up Networking Parallel Cable between 2 PCs */ + { "PNPC032", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC100", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC101", 0 }, + /* Standard 28800 bps Modem*/ + { "PNPC102", 0 }, + /* Standard Modem*/ + { "PNPC103", 0 }, + /* Standard 9600 bps Modem*/ + { "PNPC104", 0 }, + /* Standard 14400 bps Modem*/ + { "PNPC105", 0 }, + /* Standard 28800 bps Modem*/ + { "PNPC106", 0 }, + /* Standard Modem */ + { "PNPC107", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC108", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC109", 0 }, + /* Standard 28800 bps Modem */ + { "PNPC10A", 0 }, + /* Standard Modem */ + { "PNPC10B", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC10C", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC10D", 0 }, + /* Standard 28800 bps Modem */ + { "PNPC10E", 0 }, + /* Standard Modem */ + { "PNPC10F", 0 }, + /* Standard PCMCIA Card Modem */ + { "PNP2000", 0 }, + /* Rockwell */ + /* Modular Technology */ + /* Rockwell 33.6 DPF Internal PnP */ + /* Modular Technology 33.6 Internal PnP */ + { "ROK0030", 0 }, + /* Kortex International */ + /* KORTEX 14400 Externe PnP */ + { "ROK0100", 0 }, + /* Viking Components, Inc */ + /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ + { "ROK4920", 0 }, + /* Rockwell */ + /* British Telecom */ + /* Modular Technology */ + /* Rockwell 33.6 DPF External PnP */ + /* BT Prologue 33.6 External PnP */ + /* Modular Technology 33.6 External PnP */ + { "RSS00A0", 0 }, + /* Viking 56K FAX INT */ + { "RSS0262", 0 }, + /* SupraExpress 28.8 Data/Fax PnP modem */ + { "SUP1310", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1421", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1590", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1760", 0 }, + /* Phoebe Micro */ + /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ + { "TEX0011", 0 }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "UAC000F", 0 }, + /* 3Com Corp. */ + /* Gateway Telepath IIvi 33.6 */ + { "USR0000", 0 }, + /* Sportster Vi 14.4 PnP FAX Voicemail */ + { "USR0004", 0 }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { "USR0006", 0 }, + /* U.S. Robotics 33.6K Voice EXT PnP */ + { "USR0007", 0 }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { "USR2002", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR2070", 0 }, + /* U.S. Robotics 56K Voice EXT PnP */ + { "USR2080", 0 }, + /* U.S. Robotics 56K FAX INT */ + { "USR3031", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR3070", 0 }, + /* U.S. Robotics 56K Voice EXT PnP */ + { "USR3080", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR3090", 0 }, + /* U.S. Robotics 56K Message */ + { "USR9100", 0 }, + /* U.S. Robotics 56K FAX EXT PnP*/ + { "USR9160", 0 }, + /* U.S. Robotics 56K FAX INT PnP*/ + { "USR9170", 0 }, + /* U.S. Robotics 56K Voice EXT PnP*/ + { "USR9180", 0 }, + /* U.S. Robotics 56K Voice INT PnP*/ + { "USR9190", 0 }, + { "", 0 } +}; + +static void inline avoid_irq_share(struct pci_dev *dev) +{ + unsigned int map = 0x1FF8; + struct isapnp_irq *irq; + struct isapnp_resources *res = dev->sysdata; + + serial8250_get_irq_map(&map); + + for ( ; res; res = res->alt) + for (irq = res->irq; irq; irq = irq->next) + irq->map = map; +} + +static char *modem_names[] __devinitdata = { + "MODEM", "Modem", "modem", "FAX", "Fax", "fax", + "56K", "56k", "K56", "33.6", "28.8", "14.4", + "33,600", "28,800", "14,400", "33.600", "28.800", "14.400", + "33600", "28800", "14400", "V.90", "V.34", "V.32", 0 +}; + +static int __devinit check_name(char *name) +{ + char **tmp; + + for (tmp = modem_names; *tmp; tmp++) + if (strstr(name, *tmp)) + return 1; + + return 0; +} + +static int inline check_compatible_id(struct pci_dev *dev) +{ + int i; + for (i = 0; i < DEVICE_COUNT_COMPATIBLE; i++) + if ((dev->vendor_compatible[i] == + ISAPNP_VENDOR('P', 'N', 'P')) && + (swab16(dev->device_compatible[i]) >= 0xc000) && + (swab16(dev->device_compatible[i]) <= 0xdfff)) + return 0; + return 1; +} + +/* + * Given a complete unknown ISA PnP device, try to use some heuristics to + * detect modems. Currently use such heuristic set: + * - dev->name or dev->bus->name must contain "modem" substring; + * - device must have only one IO region (8 byte long) with base adress + * 0x2e8, 0x3e8, 0x2f8 or 0x3f8. + * + * Such detection looks very ugly, but can detect at least some of numerous + * ISA PnP modems, alternatively we must hardcode all modems in pnp_devices[] + * table. + */ +static int serial_pnp_guess_board(struct pci_dev *dev, int *flags) +{ + struct isapnp_resources *res = (struct isapnp_resources *)dev->sysdata; + struct isapnp_resources *resa; + + if (!(check_name(dev->name) || check_name(dev->bus->name)) && + !(check_compatible_id(dev))) + return -ENODEV; + + if (!res || res->next) + return -ENODEV; + + for (resa = res->alt; resa; resa = resa->alt) { + struct isapnp_port *port; + for (port = res->port; port; port = port->next) + if ((port->size == 8) && + ((port->min == 0x2f8) || + (port->min == 0x3f8) || + (port->min == 0x2e8) || + (port->min == 0x3e8))) + return 0; + } + + return -ENODEV; +} + +static int +pnp_init_one(struct pci_dev *dev, const struct pnpbios_device_id *ent, + char *slot_name) +{ + struct serial_struct serial_req; + int ret, line, flags = ent ? ent->driver_data : 0; + + if (!ent) { + ret = serial_pnp_guess_board(dev, &flags); + if (ret) + return ret; + } + + if (dev->prepare(dev) < 0) { + printk("serial: PNP device '%s' prepare failed\n", + slot_name); + return -ENODEV; + } + + if (dev->active) + return -ENODEV; + + if (flags & SPCI_FL_NO_SHIRQ) + avoid_irq_share(dev); + + if (dev->activate(dev) < 0) { + printk("serial: PNP device '%s' activate failed\n", + slot_name); + return -ENODEV; + } + + memset(&serial_req, 0, sizeof(serial_req)); + serial_req.irq = dev->irq_resource[0].start; + serial_req.port = pci_resource_start(dev, 0); + if (HIGH_BITS_OFFSET) + serial_req.port = pci_resource_start(dev, 0) >> HIGH_BITS_OFFSET; + +#ifdef SERIAL_DEBUG_PNP + printk("Setup PNP port: port %x, irq %d, type %d\n", + serial_req.port, serial_req.irq, serial_req.io_type); +#endif + + serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; + serial_req.baud_base = 115200; + line = register_serial(&serial_req); + + if (line >= 0) { + pci_set_drvdata(dev, (void *)(line + 1)); + + /* + * Public health warning: remove this once the 2.5 + * pnpbios_module_init() stuff is incorporated. + */ + dev->driver = (void *)pnp_dev_table; + } else + dev->deactivate(dev); + + return line >= 0 ? 0 : -ENODEV; +} + +static void pnp_remove_one(struct pci_dev *dev) +{ + int line = (int)pci_get_drvdata(dev); + + if (line) { + pci_set_drvdata(dev, NULL); + + unregister_serial(line - 1); + + dev->deactivate(dev); + } +} + +static char hex[] = "0123456789ABCDEF"; + +/* + * This function should vanish when 2.5 comes around and + * we have pnpbios_module_init() + */ +static int pnp_init(void) +{ + const struct pnpbios_device_id *id; + struct pci_dev *dev = NULL; + int nr = 0, rc = -ENODEV; + +#ifdef SERIAL_DEBUG_PNP + printk("Entered probe_serial_pnp()\n"); +#endif + + isapnp_for_each_dev(dev) { + char slot_name[8]; + u32 pnpid; + + if (dev->active) + continue; + + pnpid = dev->vendor << 16 | dev->device; + pnpid = cpu_to_le32(pnpid); + +#define HEX(id,a) hex[((id)>>a) & 15] +#define CHAR(id,a) (0x40 + (((id)>>a) & 31)) + slot_name[0] = CHAR(pnpid, 26); + slot_name[1] = CHAR(pnpid, 21); + slot_name[2] = CHAR(pnpid, 16); + slot_name[3] = HEX(pnpid, 12); + slot_name[4] = HEX(pnpid, 8); + slot_name[5] = HEX(pnpid, 4); + slot_name[6] = HEX(pnpid, 0); + slot_name[7] = '\0'; + + for (id = pnp_dev_table; id->id[0]; id++) + if (memcmp(id->id, slot_name, 7) == 0) + break; + + if (id->id[0]) + rc = pnp_init_one(dev, id, slot_name); + else + rc = pnp_init_one(dev, NULL, slot_name); + + if (rc == 0) + nr++; + } + +#ifdef SERIAL_DEBUG_PNP + printk("Leaving probe_serial_pnp() (probe finished)\n"); +#endif + + return nr == 0 ? rc : 0; +} + +static int __init serial8250_pnp_init(void) +{ + if (!isapnp_present()) { +#ifdef SERIAL_DEBUG_PNP + printk("Leaving probe_serial_pnp() (no isapnp)\n"); +#endif + return -ENODEV; + } + return pnp_init(); +} + +static void __exit serial8250_pnp_exit(void) +{ + struct pci_dev *dev = NULL; + + isapnp_for_each_dev(dev) { + if (dev->driver != (void *)pnp_dev_table) + continue; + pnp_remove_one(dev); + } +} + +module_init(serial8250_pnp_init); +module_exit(serial8250_pnp_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 PNPBIOS serial probe module"); +MODULE_DEVICE_TABLE(pnpbios, pnp_dev_table); diff --git a/drivers/serial/serial_amba.c b/drivers/serial/serial_amba.c new file mode 100644 index 000000000000..016f717c7242 --- /dev/null +++ b/drivers/serial/serial_amba.c @@ -0,0 +1,783 @@ +/* + * linux/drivers/char/serial_amba.c + * + * Driver for AMBA serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright 1999 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: serial_amba.c,v 1.35 2002/07/21 08:57:55 rmk Exp $ + * + * This is a generic driver for ARM AMBA-type serial ports. They + * have a lot of 16550-like features, but are not register compatable. + * Note that although they do have CTS, DCD and DSR inputs, they do + * not have an RI input, nor do they have DTR or RTS outputs. If + * required, these have to be supplied via some other means (eg, GPIO) + * and hooked into this driver. + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.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/ptrace.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/circ_buf.h> +#include <linux/serial.h> +#include <linux/console.h> +#include <linux/sysrq.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/bitops.h> + +#if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/serial_core.h> + +#include <asm/hardware/serial_amba.h> + +#define UART_NR 2 + +#define SERIAL_AMBA_MAJOR 204 +#define SERIAL_AMBA_MINOR 16 +#define SERIAL_AMBA_NR UART_NR + +#define AMBA_ISR_PASS_LIMIT 256 + +/* + * Access macros for the AMBA UARTs + */ +#define UART_GET_INT_STATUS(p) readb((p)->membase + AMBA_UARTIIR) +#define UART_PUT_ICR(p, c) writel((c), (p)->membase + AMBA_UARTICR) +#define UART_GET_FR(p) readb((p)->membase + AMBA_UARTFR) +#define UART_GET_CHAR(p) readb((p)->membase + AMBA_UARTDR) +#define UART_PUT_CHAR(p, c) writel((c), (p)->membase + AMBA_UARTDR) +#define UART_GET_RSR(p) readb((p)->membase + AMBA_UARTRSR) +#define UART_GET_CR(p) readb((p)->membase + AMBA_UARTCR) +#define UART_PUT_CR(p,c) writel((c), (p)->membase + AMBA_UARTCR) +#define UART_GET_LCRL(p) readb((p)->membase + AMBA_UARTLCR_L) +#define UART_PUT_LCRL(p,c) writel((c), (p)->membase + AMBA_UARTLCR_L) +#define UART_GET_LCRM(p) readb((p)->membase + AMBA_UARTLCR_M) +#define UART_PUT_LCRM(p,c) writel((c), (p)->membase + AMBA_UARTLCR_M) +#define UART_GET_LCRH(p) readb((p)->membase + AMBA_UARTLCR_H) +#define UART_PUT_LCRH(p,c) writel((c), (p)->membase + AMBA_UARTLCR_H) +#define UART_RX_DATA(s) (((s) & AMBA_UARTFR_RXFE) == 0) +#define UART_TX_READY(s) (((s) & AMBA_UARTFR_TXFF) == 0) +#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & AMBA_UARTFR_TMSK) == 0) + +#define UART_DUMMY_RSR_RX 256 +#define UART_PORT_SIZE 64 + +/* + * On the Integrator platform, the port RTS and DTR are provided by + * bits in the following SC_CTRLS register bits: + * RTS DTR + * UART0 7 6 + * UART1 5 4 + */ +#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) +#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) + +/* + * We wrap our port structure around the generic uart_port. + */ +struct uart_amba_port { + struct uart_port port; + unsigned int dtr_mask; + unsigned int rts_mask; + unsigned int old_status; +}; + +static void __ambauart_stop_tx(struct uart_port *port) +{ + unsigned int cr; + + cr = UART_GET_CR(port); + cr &= ~AMBA_UARTCR_TIE; + UART_PUT_CR(port, cr); +} + +static void ambauart_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + __ambauart_stop_tx(port); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void ambauart_start_tx(struct uart_port *port, unsigned int tty_start) +{ + unsigned int cr; + + cr = UART_GET_CR(port); + cr |= AMBA_UARTCR_TIE; + UART_PUT_CR(port, cr); +} + +static void ambauart_stop_rx(struct uart_port *port) +{ + unsigned long flags; + unsigned int cr; + + spin_lock_irqsave(&port->lock, flags); + cr = UART_GET_CR(port); + cr &= ~(AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE); + UART_PUT_CR(port, cr); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void ambauart_enable_ms(struct uart_port *port) +{ + unsigned long flags; + unsigned int cr; + + spin_lock_irqsave(&port->lock, flags); + cr = UART_GET_CR(port); + cr |= AMBA_UARTCR_MSIE; + UART_PUT_CR(port, cr); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void +#ifdef SUPPORT_SYSRQ +ambauart_rx_chars(struct uart_port *port, struct pt_regs *regs) +#else +ambauart_rx_chars(struct uart_port *port) +#endif +{ + struct tty_struct *tty = port->info->tty; + unsigned int status, ch, rsr, max_count = 256; + + status = UART_GET_FR(port); + while (UART_RX_DATA(status) && max_count--) { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty->flip.tqueue.routine((void *)tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + printk(KERN_WARNING "TTY_DONT_FLIP set\n"); + return; + } + } + + ch = UART_GET_CHAR(port); + + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = TTY_NORMAL; + port->icount.rx++; + + /* + * Note that the error handling code is + * out of the main execution path + */ + rsr = UART_GET_RSR(port) | UART_DUMMY_RSR_RX; + if (rsr & AMBA_UARTRSR_ANY) { + if (rsr & AMBA_UARTRSR_BE) { + rsr &= ~(AMBA_UARTRSR_FE | AMBA_UARTRSR_PE); + port->icount.brk++; + if (uart_handle_break(port)) + goto ignore_char; + } else if (rsr & AMBA_UARTRSR_PE) + port->icount.parity++; + else if (rsr & AMBA_UARTRSR_FE) + port->icount.frame++; + if (rsr & AMBA_UARTRSR_OE) + port->icount.overrun++; + + rsr &= port->read_status_mask; + + if (rsr & AMBA_UARTRSR_BE) + *tty->flip.flag_buf_ptr = TTY_BREAK; + else if (rsr & AMBA_UARTRSR_PE) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (rsr & AMBA_UARTRSR_FE) + *tty->flip.flag_buf_ptr = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch, regs)) + goto ignore_char; + + if ((rsr & port->ignore_status_mask) == 0) { + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + if ((rsr & AMBA_UARTRSR_OE) && + tty->flip.count < TTY_FLIPBUF_SIZE) { + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character + */ + *tty->flip.char_buf_ptr++ = 0; + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + tty->flip.count++; + } + ignore_char: + status = UART_GET_FR(port); + } + tty_flip_buffer_push(tty); + return; +} + +static void ambauart_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + int count; + + if (port->x_char) { + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + __ambauart_stop_tx(port); + return; + } + + count = port->fifosize >> 1; + do { + UART_PUT_CHAR(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_event(port, EVT_WRITE_WAKEUP); + + if (uart_circ_empty(xmit)) + __ambauart_stop_tx(port); +} + +static void ambauart_modem_status(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int status, delta; + + UART_PUT_ICR(&uap->port, 0); + + status = UART_GET_FR(&uap->port) & AMBA_UARTFR_MODEM_ANY; + + delta = status ^ uap->old_status; + uap->old_status = status; + + if (!delta) + return; + + if (delta & AMBA_UARTFR_DCD) + uart_handle_dcd_change(&uap->port, status & AMBA_UARTFR_DCD); + + if (delta & AMBA_UARTFR_DSR) + uap->port.icount.dsr++; + + if (delta & AMBA_UARTFR_CTS) + uart_handle_cts_change(&uap->port, status & AMBA_UARTFR_CTS); + + wake_up_interruptible(&uap->port.info->delta_msr_wait); +} + +static void ambauart_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_port *port = dev_id; + unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; + + status = UART_GET_INT_STATUS(port); + do { + if (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS)) +#ifdef SUPPORT_SYSRQ + ambauart_rx_chars(port, regs); +#else + ambauart_rx_chars(port); +#endif + if (status & AMBA_UARTIIR_MIS) + ambauart_modem_status(port); + if (status & AMBA_UARTIIR_TIS) + ambauart_tx_chars(port); + + if (pass_counter-- == 0) + break; + + status = UART_GET_INT_STATUS(port); + } while (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS | + AMBA_UARTIIR_TIS)); +} + +static unsigned int ambauart_tx_empty(struct uart_port *port) +{ + return UART_GET_FR(port) & AMBA_UARTFR_BUSY ? 0 : TIOCSER_TEMT; +} + +static unsigned int ambauart_get_mctrl(struct uart_port *port) +{ + unsigned int result = 0; + unsigned int status; + + status = UART_GET_FR(port); + if (status & AMBA_UARTFR_DCD) + result |= TIOCM_CAR; + if (status & AMBA_UARTFR_DSR) + result |= TIOCM_DSR; + if (status & AMBA_UARTFR_CTS) + result |= TIOCM_CTS; + + return result; +} + +static void ambauart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int ctrls = 0, ctrlc = 0; + + if (mctrl & TIOCM_RTS) + ctrlc |= uap->rts_mask; + else + ctrls |= uap->rts_mask; + + if (mctrl & TIOCM_DTR) + ctrlc |= uap->dtr_mask; + else + ctrls |= uap->dtr_mask; + + __raw_writel(ctrls, SC_CTRLS); + __raw_writel(ctrlc, SC_CTRLC); +} + +static void ambauart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned int lcr_h; + + spin_lock_irqsave(&port->lock, flags); + lcr_h = UART_GET_LCRH(port); + if (break_state == -1) + lcr_h |= AMBA_UARTLCR_H_BRK; + else + lcr_h &= ~AMBA_UARTLCR_H_BRK; + UART_PUT_LCRH(port, lcr_h); + spin_unlock_irqrestore(&port->lock, flags); +} + +static int ambauart_startup(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(port->irq, ambauart_int, 0, "amba", port); + if (retval) + return retval; + + /* + * initialise the old status of the modem signals + */ + uap->old_status = UART_GET_FR(port) & AMBA_UARTFR_MODEM_ANY; + + /* + * Finally, enable interrupts + */ + UART_PUT_CR(port, AMBA_UARTCR_UARTEN | AMBA_UARTCR_RIE | + AMBA_UARTCR_RTIE); + + return 0; +} + +static void ambauart_shutdown(struct uart_port *port) +{ + /* + * Free the interrupt + */ + free_irq(port->irq, port); + + /* + * disable all interrupts, disable the port + */ + UART_PUT_CR(port, 0); + + /* disable break condition and fifos */ + UART_PUT_LCRH(port, UART_GET_LCRH(port) & + ~(AMBA_UARTLCR_H_BRK | AMBA_UARTLCR_H_FEN)); +} + +static void +ambauart_change_speed(struct uart_port *port, unsigned int cflag, + unsigned int iflag, unsigned int quot) +{ + unsigned int lcr_h, old_cr; + unsigned long flags; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + lcr_h = AMBA_UARTLCR_H_WLEN_5; + break; + case CS6: + lcr_h = AMBA_UARTLCR_H_WLEN_6; + break; + case CS7: + lcr_h = AMBA_UARTLCR_H_WLEN_7; + break; + default: // CS8 + lcr_h = AMBA_UARTLCR_H_WLEN_8; + break; + } + if (cflag & CSTOPB) + lcr_h |= AMBA_UARTLCR_H_STP2; + if (cflag & PARENB) { + lcr_h |= AMBA_UARTLCR_H_PEN; + if (!(cflag & PARODD)) + lcr_h |= AMBA_UARTLCR_H_EPS; + } + if (port->fifosize > 1) + lcr_h |= AMBA_UARTLCR_H_FEN; + + port->read_status_mask = AMBA_UARTRSR_OE; + if (iflag & INPCK) + port->read_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; + if (iflag & (BRKINT | PARMRK)) + port->read_status_mask |= AMBA_UARTRSR_BE; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (iflag & IGNPAR) + port->ignore_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; + if (iflag & IGNBRK) { + port->ignore_status_mask |= AMBA_UARTRSR_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (iflag & IGNPAR) + port->ignore_status_mask |= AMBA_UARTRSR_OE; + } + + /* + * Ignore all characters if CREAD is not set. + */ + if ((cflag & CREAD) == 0) + port->ignore_status_mask |= UART_DUMMY_RSR_RX; + + /* first, disable everything */ + spin_lock_irqsave(&port->lock, flags); + old_cr = UART_GET_CR(port) & ~AMBA_UARTCR_MSIE; + + if (UART_ENABLE_MS(port, cflag)) + old_cr |= AMBA_UARTCR_MSIE; + + UART_PUT_CR(port, 0); + + /* Set baud rate */ + quot -= 1; + UART_PUT_LCRM(port, ((quot & 0xf00) >> 8)); + UART_PUT_LCRL(port, (quot & 0xff)); + + /* + * ----------v----------v----------v----------v----- + * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L + * ----------^----------^----------^----------^----- + */ + UART_PUT_LCRH(port, lcr_h); + UART_PUT_CR(port, old_cr); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *ambauart_type(struct uart_port *port) +{ + return port->type == PORT_AMBA ? "AMBA" : NULL; +} + +/* + * Release the memory region(s) being used by 'port' + */ +static void ambauart_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port' + */ +static int ambauart_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, UART_PORT_SIZE, "serial_amba") + != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void ambauart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_AMBA; + ambauart_request_port(port); + } +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int ambauart_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= NR_IRQS) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops amba_pops = { + tx_empty: ambauart_tx_empty, + set_mctrl: ambauart_set_mctrl, + get_mctrl: ambauart_get_mctrl, + stop_tx: ambauart_stop_tx, + start_tx: ambauart_start_tx, + stop_rx: ambauart_stop_rx, + enable_ms: ambauart_enable_ms, + break_ctl: ambauart_break_ctl, + startup: ambauart_startup, + shutdown: ambauart_shutdown, + change_speed: ambauart_change_speed, + type: ambauart_type, + release_port: ambauart_release_port, + request_port: ambauart_request_port, + config_port: ambauart_config_port, + verify_port: ambauart_verify_port, +}; + +static struct uart_amba_port amba_ports[UART_NR] = { + { + port: { + membase: (void *)IO_ADDRESS(INTEGRATOR_UART0_BASE), + mapbase: INTEGRATOR_UART0_BASE, + iotype: SERIAL_IO_MEM, + irq: IRQ_UARTINT0, + uartclk: 14745600, + fifosize: 16, + ops: &amba_pops, + flags: ASYNC_BOOT_AUTOCONF, + line: 0, + }, + dtr_mask: 1 << 5, + rts_mask: 1 << 4, + }, + { + port: { + membase: (void *)IO_ADDRESS(INTEGRATOR_UART1_BASE), + mapbase: INTEGRATOR_UART1_BASE, + iotype: SERIAL_IO_MEM, + irq: IRQ_UARTINT1, + uartclk: 14745600, + fifosize: 16, + ops: &amba_pops, + flags: ASYNC_BOOT_AUTOCONF, + line: 1, + }, + dtr_mask: 1 << 7, + rts_mask: 1 << 6, + } +}; + +#ifdef CONFIG_SERIAL_AMBA_CONSOLE + +static void +ambauart_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_port *port = &amba_ports[co->index].port; + unsigned int status, old_cr; + int i; + + /* + * First save the CR then disable the interrupts + */ + old_cr = UART_GET_CR(port); + UART_PUT_CR(port, AMBA_UARTCR_UARTEN); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = UART_GET_FR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, s[i]); + if (s[i] == '\n') { + do { + status = UART_GET_FR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, '\r'); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + do { + status = UART_GET_FR(port); + } while (status & AMBA_UARTFR_BUSY); + UART_PUT_CR(port, old_cr); +} + +static kdev_t ambauart_console_device(struct console *co) +{ + return mk_kdev(SERIAL_AMBA_MAJOR, SERIAL_AMBA_MINOR + co->index); +} + +static void __init +ambauart_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + if (UART_GET_CR(port) & AMBA_UARTCR_UARTEN) { + unsigned int lcr_h, quot; + lcr_h = UART_GET_LCRH(port); + + *parity = 'n'; + if (lcr_h & AMBA_UARTLCR_H_PEN) { + if (lcr_h & AMBA_UARTLCR_H_EPS) + *parity = 'e'; + else + *parity = 'o'; + } + + if ((lcr_h & 0x60) == AMBA_UARTLCR_H_WLEN_7) + *bits = 7; + else + *bits = 8; + + quot = UART_GET_LCRL(port) | UART_GET_LCRM(port) << 8; + *baud = port->uartclk / (16 * (quot + 1)); + } +} + +static int __init ambauart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= UART_NR) + co->index = 0; + port = &amba_ports[co->index].port; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + ambauart_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console amba_console = { + name: "ttyAM", + write: ambauart_console_write, + device: ambauart_console_device, + setup: ambauart_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +void __init ambauart_console_init(void) +{ + register_console(&amba_console); +} + +#define AMBA_CONSOLE &amba_console +#else +#define AMBA_CONSOLE NULL +#endif + +static struct uart_driver amba_reg = { + owner: THIS_MODULE, + driver_name: "ttyAM", +#ifdef CONFIG_DEVFS_FS + dev_name: "ttyAM%d", +#else + dev_name: "ttyAM", +#endif + major: SERIAL_AMBA_MAJOR, + minor: SERIAL_AMBA_MINOR, + nr: UART_NR, + cons: AMBA_CONSOLE, +}; + +static int __init ambauart_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: AMBA driver $Revision: 1.35 $\n"); + + ret = uart_register_driver(&amba_reg); + if (ret == 0) { + int i; + + for (i = 0; i < UART_NR; i++) + uart_add_one_port(&amba_reg, &amba_ports[i].port); + } + return ret; +} + +static void __exit ambauart_exit(void) +{ + int i; + + for (i = 0; i < UART_NR; i++) + uart_remove_one_port(&amba_reg, &amba_ports[i].port); + + uart_unregister_driver(&amba_reg); +} + +module_init(ambauart_init); +module_exit(ambauart_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd"); +MODULE_DESCRIPTION("ARM AMBA serial port driver $Revision: 1.35 $"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/serial_anakin.c b/drivers/serial/serial_anakin.c new file mode 100644 index 000000000000..f9ee4c2bb2a4 --- /dev/null +++ b/drivers/serial/serial_anakin.c @@ -0,0 +1,546 @@ +/* + * linux/drivers/char/serial_anakin.c + * + * Based on driver for AMBA serial ports, by ARM Limited, + * Deep Blue Solutions Ltd., Linus Torvalds and Theodore Ts'o. + * + * Copyright (C) 2001 Aleph One Ltd. for Acunia N.V. + * + * Copyright (C) 2001 Blue Mug, Inc. for Acunia N.V. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Changelog: + * 20-Apr-2001 TTC Created + * 05-May-2001 W/TTC Updated for serial_core.c + * 27-Jun-2001 jonm Minor changes; add mctrl support, switch to + * SA_INTERRUPT. Works reliably now. No longer requires + * changes to the serial_core API. + * + * $Id: serial_anakin.c,v 1.27 2002/07/20 17:10:03 rmk Exp $ + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.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/ptrace.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/circ_buf.h> +#include <linux/serial.h> +#include <linux/console.h> +#include <linux/sysrq.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/bitops.h> + +#include <linux/serial_core.h> + +#include <asm/arch/serial_reg.h> + +#define UART_NR 5 + +#define SERIAL_ANAKIN_NAME "ttyAN" +#define SERIAL_ANAKIN_MAJOR 204 +#define SERIAL_ANAKIN_MINOR 32 + +static unsigned int txenable[NR_IRQS]; /* Software interrupt register */ + +static inline unsigned int +anakin_in(struct uart_port *port, unsigned int offset) +{ + return __raw_readl(port->base + offset); +} + +static inline void +anakin_out(struct uart_port *port, unsigned int offset, unsigned int value) +{ + __raw_writel(value, port->base + offset); +} + +static void +anakin_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + txenable[port->irq] = 0; +} + +static inline void +anakin_transmit_buffer(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + + while (!(anakin_in(port, 0x10) & TXEMPTY)); + anakin_out(port, 0x14, xmit->buf[xmit->tail]); + anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); + port->icount.tx++; + + if (uart_circ_empty(xmit)) + anakin_stop_tx(port, 0); +} + +static inline void +anakin_transmit_x_char(struct uart_port *port) +{ + anakin_out(port, 0x14, port->x_char); + anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST); + port->icount.tx++; + port->x_char = 0; +} + +static void +anakin_start_tx(struct uart_port *port, unsigned int tty_start) +{ + unsigned int flags; + + spin_lock_irqsave(&port->lock, flags); + + // is it this... or below + if (!txenable[port->irq]) { + txenable[port->irq] = TXENABLE; + + if ((anakin_in(port, 0x10) & TXEMPTY)) { + anakin_transmit_buffer(port); + } + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void +anakin_stop_rx(struct uart_port *port) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + while (anakin_in(port, 0x10) & RXRELEASE) + anakin_in(port, 0x14); + anakin_out(port, 0x18, anakin_in(port, 0x18) | BLOCKRX); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void +anakin_enable_ms(struct uart_port *port) +{ +} + +static inline void +anakin_rx_chars(struct uart_port *port) +{ + unsigned int ch; + struct tty_struct *tty = port->info->tty; + + if (!(anakin_in(port, 0x10) & RXRELEASE)) + return; + + ch = anakin_in(port, 0x14) & 0xff; + + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + *tty->flip.char_buf_ptr++ = ch; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + port->icount.rx++; + tty->flip.count++; + } + tty_flip_buffer_push(tty); +} + +static inline void +anakin_overrun_chars(struct uart_port *port) +{ + unsigned int ch; + + ch = anakin_in(port, 0x14); + port->icount.overrun++; +} + +static inline void +anakin_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + + if (port->x_char) { + anakin_transmit_x_char(port); + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + anakin_stop_tx(port, 0); + return; + } + + anakin_transmit_buffer(port); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_event(port, EVT_WRITE_WAKEUP); +} + +static void +anakin_int(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int status; + struct uart_port *port = dev_id; + + status = anakin_in(port, 0x1c); + + if (status & RX) + anakin_rx_chars(port); + + if (status & OVERRUN) + anakin_overrun_chars(port); + + if (txenable[port->irq] && (status & TX)) + anakin_tx_chars(port); +} + +static unsigned int +anakin_tx_empty(struct uart_port *port) +{ + return anakin_in(port, 0x10) & TXEMPTY ? TIOCSER_TEMT : 0; +} + +static unsigned int +anakin_get_mctrl(struct uart_port *port) +{ + unsigned int status = 0; + + status |= (anakin_in(port, 0x10) & CTS ? TIOCM_CTS : 0); + status |= (anakin_in(port, 0x18) & DCD ? TIOCM_CAR : 0); + status |= (anakin_in(port, 0x18) & DTR ? TIOCM_DTR : 0); + status |= (anakin_in(port, 0x18) & RTS ? TIOCM_RTS : 0); + + return status; +} + +static void +anakin_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned int status; + + status = anakin_in(port, 0x18); + + if (mctrl & TIOCM_RTS) + status |= RTS; + else + status &= ~RTS; + + if (mctrl & TIOCM_CAR) + status |= DCD; + else + status &= ~DCD; + + anakin_out(port, 0x18, status); +} + +static void +anakin_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned int status; + + spin_lock_irqsave(&port->lock, flags); + status = anakin_in(port, 0x20); + + if (break_state == -1) + status |= SETBREAK; + else + status &= ~SETBREAK; + + anakin_out(port, 0x20, status); + spin_unlock_irqrestore(&port->lock, flags); +} + +static int anakin_startup(struct uart_port *port) +{ + int retval; + unsigned int read,write; + + /* + * Allocate the IRQ + */ + retval = request_irq(port->irq, anakin_int, SA_INTERRUPT, + "serial_anakin", port); + if (retval) + return retval; + + /* + * initialise the old status of the modem signals + */ + port->old_status = 0; + + /* + * Finally, disable IRQ and softIRQs for first byte) + */ + txenable[port->irq] = 0; + read = anakin_in(port, 0x18); + write = (read & ~(RTS | DTR | BLOCKRX)) | IRQENABLE; + anakin_out(port, 0x18, write); + + return 0; +} + +static void anakin_shutdown(struct uart_port *port) +{ + /* + * Free the interrupt + */ + free_irq(port->irq, port); + + /* + * disable all interrupts, disable the port + */ + anakin_out(port, 0x18, anakin_in(port, 0x18) & ~IRQENABLE); +} + +static void +anakin_change_speed(struct uart_port *port, unsigned int cflag, + unsigned int iflag, unsigned int quot) +{ + unsigned int flags; + + spin_lock_irqsave(&port->lock, flags); + while (!(anakin_in(port, 0x10) & TXEMPTY)); + anakin_out(port, 0x10, (anakin_in(port, 0x10) & ~PRESCALER) + | (quot << 3)); + + //parity always set to none + anakin_out(port, 0x18, anakin_in(port, 0x18) & ~PARITY); + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *anakin_type(struct port *port) +{ + return port->type == PORT_ANAKIN ? "ANAKIN" : NULL; +} + +static struct uart_ops anakin_pops = { + tx_empty: anakin_tx_empty, + set_mctrl: anakin_set_mctrl, + get_mctrl: anakin_get_mctrl, + stop_tx: anakin_stop_tx, + start_tx: anakin_start_tx, + stop_rx: anakin_stop_rx, + enable_ms: anakin_enable_ms, + break_ctl: anakin_break_ctl, + startup: anakin_startup, + shutdown: anakin_shutdown, + change_speed: anakin_change_speed, + type: anakin_type, +}; + +static struct uart_port anakin_ports[UART_NR] = { + { + base: IO_BASE + UART0, + irq: IRQ_UART0, + uartclk: 3686400, + fifosize: 0, + ops: &anakin_pops, + flags: ASYNC_BOOT_AUTOCONF, + line: 0, + }, + { + base: IO_BASE + UART1, + irq: IRQ_UART1, + uartclk: 3686400, + fifosize: 0, + ops: &anakin_pops, + flags: ASYNC_BOOT_AUTOCONF, + line: 1, + }, + { + base: IO_BASE + UART2, + irq: IRQ_UART2, + uartclk: 3686400, + fifosize: 0, + ops: &anakin_pops, + flags: ASYNC_BOOT_AUTOCONF, + line: 2, + }, + { + base: IO_BASE + UART3, + irq: IRQ_UART3, + uartclk: 3686400, + fifosize: 0, + ops: &anakin_pops, + flags: ASYNC_BOOT_AUTOCONF, + line: 3, + }, + { + base: IO_BASE + UART4, + irq: IRQ_UART4, + uartclk: 3686400, + fifosize: 0, + ops: &anakin_pops, + flags: ASYNC_BOOT_AUTOCONF, + line: 4, + }, +}; + + +#ifdef CONFIG_SERIAL_ANAKIN_CONSOLE + +static void +anakin_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_port *port = &anakin_ports[co->index]; + unsigned int flags, status, i; + + /* + * First save the status then disable the interrupts + */ + local_irq_save(flags); + status = anakin_in(port, 0x18); + anakin_out(port, 0x18, status & ~IRQENABLE); + local_irq_restore(flags); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + while (!(anakin_in(port, 0x10) & TXEMPTY)); + + /* + * Send the character out. + * If a LF, also do CR... + */ + anakin_out(port, 0x14, *s); + anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST); + + if (*s == 10) { + while (!(anakin_in(port, 0x10) & TXEMPTY)); + anakin_out(port, 0x14, 13); + anakin_out(port, 0x18, anakin_in(port, 0x18) + | SENDREQUEST); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the interrupts + */ + while (!(anakin_in(port, 0x10) & TXEMPTY)); + + if (status & IRQENABLE) { + local_irq_save(flags); + anakin_out(port, 0x18, anakin_in(port, 0x18) | IRQENABLE); + local_irq_restore(flags); + } +} + +static kdev_t +anakin_console_device(struct console *co) +{ + return mk_kdev(SERIAL_ANAKIN_MAJOR, SERIAL_ANAKIN_MINOR + co->index); +} + +/* + * Read the current UART setup. + */ +static void __init +anakin_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) +{ + int paritycode; + + *baud = GETBAUD (anakin_in(port, 0x10) & PRESCALER); + paritycode = GETPARITY(anakin_in(port, 0x18) & PARITY); + switch (paritycode) { + case NONEPARITY: *parity = 'n'; break; + case ODDPARITY: *parity = 'o'; break; + case EVENPARITY: *parity = 'e'; break; + } + *bits = 8; +} + +static int __init +anakin_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = CONFIG_ANAKIN_DEFAULT_BAUDRATE; + int bits = 8; + int parity = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= UART_NR) + co->index = 0; + port = &anakin_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits); + else + anakin_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits); +} + +static struct console anakin_console = { + name: SERIAL_ANAKIN_NAME, + write: anakin_console_write, + device: anakin_console_device, + setup: anakin_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +void __init +anakin_console_init(void) +{ + register_console(&anakin_console); +} + +#define ANAKIN_CONSOLE &anakin_console +#else +#define ANAKIN_CONSOLE NULL +#endif + +static struct uart_register anakin_reg = { + driver_name: SERIAL_ANAKIN_NAME, + dev_name: SERIAL_ANAKIN_NAME, + major: SERIAL_ANAKIN_MAJOR, + minor: SERIAL_ANAKIN_MINOR, + nr: UART_NR, + cons: ANAKIN_CONSOLE, +}; + +static int __init +anakin_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: Anakin driver $Revision: 1.27 $\n"); + + ret = uart_register_driver(&anakin_reg); + if (ret == 0) { + int i; + + for (i = 0; i < UART_NR; i++) + uart_add_one_port(&anakin_reg, &anakin_ports[i]); + } + return ret; +} + +__initcall(anakin_init); + +MODULE_DESCRIPTION("Anakin serial driver"); +MODULE_AUTHOR("Tak-Shing Chan <chan@aleph1.co.uk>"); +MODULE_SUPPORTED_DEVICE("ttyAN"); +MODULE_LICENSE("GPL"); + +EXPORT_NO_SYMBOLS; diff --git a/drivers/serial/serial_clps711x.c b/drivers/serial/serial_clps711x.c new file mode 100644 index 000000000000..500b9a2b9f51 --- /dev/null +++ b/drivers/serial/serial_clps711x.c @@ -0,0 +1,643 @@ +/* + * linux/drivers/char/serial_clps711x.c + * + * Driver for CLPS711x serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright 1999 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: serial_clps711x.c,v 1.38 2002/07/21 08:57:55 rmk Exp $ + * + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.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/ptrace.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/circ_buf.h> +#include <linux/serial.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/spinlock.h> + +#include <asm/bitops.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/uaccess.h> + +#if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/serial_core.h> + +#include <asm/hardware/clps7111.h> + +#define UART_NR 2 + +#ifndef CONFIG_SERIAL_CLPS711X_OLD_NAME +#define SERIAL_CLPS711X_NAME "ttyCL" +#define SERIAL_CLPS711X_MAJOR 204 +#define SERIAL_CLPS711X_MINOR 40 +#define SERIAL_CLPS711X_NR UART_NR + +#else +#warning The old names/device number for this driver if compatabity is needed +#define SERIAL_CLPS711X_NAME "ttyAM" +#define SERIAL_CLPS711X_MAJOR 204 +#define SERIAL_CLPS711X_MINOR 16 +#define SERIAL_CLPS711X_NR UART_NR + +#endif + +/* + * We use the relevant SYSCON register as a base address for these ports. + */ +#define UBRLCR(port) ((port)->iobase + UBRLCR1 - SYSCON1) +#define UARTDR(port) ((port)->iobase + UARTDR1 - SYSCON1) +#define SYSFLG(port) ((port)->iobase + SYSFLG1 - SYSCON1) +#define SYSCON(port) ((port)->iobase + SYSCON1 - SYSCON1) + +#define TX_IRQ(port) ((port)->irq) +#define RX_IRQ(port) ((port)->irq + 1) + +#define UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR) + +#define tx_enabled(port) ((port)->unused[0]) + +static void +__clps711xuart_stop_tx(struct uart_port *port) +{ + if (tx_enabled(port)) { + disable_irq(TX_IRQ(port)); + tx_enabled(port) = 0; + } +} + +static void +clps711xuart_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + __clps711xuart_stop_tx(port); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void +clps711xuart_start_tx(struct uart_port *port, unsigned int tty_start) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (!tx_enabled(port)) { + enable_irq(TX_IRQ(port)); + tx_enabled(port) = 1; + } + spin_unlock_irqrestore(&port->lock, flags); +} + +static void clps711xuart_stop_rx(struct uart_port *port) +{ + disable_irq(RX_IRQ(port)); +} + +static void clps711xuart_enable_ms(struct uart_port *port) +{ +} + +static void clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_port *port = dev_id; + struct tty_struct *tty = port->info->tty; + unsigned int status, ch, flg, ignored = 0; + + status = clps_readl(SYSFLG(port)); + while (!(status & SYSFLG_URXFE)) { + ch = clps_readl(UARTDR(port)); + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + port->icount.rx++; + + flg = TTY_NORMAL; + + /* + * Note that the error handling code is + * out of the main execution path + */ + if (ch & UART_ANY_ERR) + goto handle_error; + + if (uart_handle_sysrq_char(port, ch, regs)) + goto ignore_char; + + error_return: + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + ignore_char: + status = clps_readl(SYSFLG(port)); + } + out: + tty_flip_buffer_push(tty); + return; + + handle_error: + if (ch & UARTDR_PARERR) + port->icount.parity++; + else if (ch & UARTDR_FRMERR) + port->icount.frame++; + if (ch & UARTDR_OVERR) + port->icount.overrun++; + + if (ch & port->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + ch &= port->read_status_mask; + + if (ch & UARTDR_PARERR) + flg = TTY_PARITY; + else if (ch & UARTDR_FRMERR) + flg = TTY_FRAME; + + if (ch & UARTDR_OVERR) { + /* + * CHECK: does overrun affect the current character? + * ASSUMPTION: it does not. + */ + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef SUPPORT_SYSRQ + port->sysrq = 0; +#endif + goto error_return; +} + +static void clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_port *port = dev_id; + struct circ_buf *xmit = &port->info->xmit; + int count; + + if (port->x_char) { + clps_writel(port->x_char, UARTDR(port)); + port->icount.tx++; + port->x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + __clps711xuart_stop_tx(port); + return; + } + + count = port->fifosize >> 1; + do { + clps_writel(xmit->buf[xmit->tail], UARTDR(port)); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_event(port, EVT_WRITE_WAKEUP); + + if (uart_circ_empty(xmit)) + __clps711xuart_stop_tx(port); +} + +static unsigned int clps711xuart_tx_empty(struct uart_port *port) +{ + unsigned int status = clps_readl(SYSFLG(port)); + return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT; +} + +static unsigned int clps711xuart_get_mctrl(struct uart_port *port) +{ + unsigned int port_addr; + unsigned int result = 0; + unsigned int status; + + port_addr = SYSFLG(port); + if (port_addr == SYSFLG1) { + status = clps_readl(SYSFLG1); + if (status & SYSFLG1_DCD) + result |= TIOCM_CAR; + if (status & SYSFLG1_DSR) + result |= TIOCM_DSR; + if (status & SYSFLG1_CTS) + result |= TIOCM_CTS; + } + + return result; +} + +static void +clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl) +{ +} + +static void clps711xuart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned int ubrlcr; + + spin_lock_irqsave(&port->lock, flags); + ubrlcr = clps_readl(UBRLCR(port)); + if (break_state == -1) + ubrlcr |= UBRLCR_BREAK; + else + ubrlcr &= ~UBRLCR_BREAK; + clps_writel(ubrlcr, UBRLCR(port)); + spin_unlock_irqrestore(&port->lock, flags); +} + +static int clps711xuart_startup(struct uart_port *port) +{ + unsigned int syscon; + int retval; + + tx_enabled(port) = 1; + + /* + * Allocate the IRQs + */ + retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0, + "clps711xuart_tx", port); + if (retval) + return retval; + + retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0, + "clps711xuart_rx", port); + if (retval) { + free_irq(TX_IRQ(port), port); + return retval; + } + + /* + * enable the port + */ + syscon = clps_readl(SYSCON(port)); + syscon |= SYSCON_UARTEN; + clps_writel(syscon, SYSCON(port)); + + return 0; +} + +static void clps711xuart_shutdown(struct uart_port *port) +{ + unsigned int ubrlcr, syscon; + + /* + * Free the interrupt + */ + free_irq(TX_IRQ(port), port); /* TX interrupt */ + free_irq(RX_IRQ(port), port); /* RX interrupt */ + + /* + * disable the port + */ + syscon = clps_readl(SYSCON(port)); + syscon &= ~SYSCON_UARTEN; + clps_writel(syscon, SYSCON(port)); + + /* + * disable break condition and fifos + */ + ubrlcr = clps_readl(UBRLCR(port)); + ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK); + clps_writel(ubrlcr, UBRLCR(port)); +} + +static void +clps711xuart_change_speed(struct uart_port *port, unsigned int cflag, + unsigned int iflag, unsigned int quot) +{ + unsigned int ubrlcr; + unsigned long flags; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + ubrlcr = UBRLCR_WRDLEN5; + break; + case CS6: + ubrlcr = UBRLCR_WRDLEN6; + break; + case CS7: + ubrlcr = UBRLCR_WRDLEN7; + break; + default: // CS8 + ubrlcr = UBRLCR_WRDLEN8; + break; + } + if (cflag & CSTOPB) + ubrlcr |= UBRLCR_XSTOP; + if (cflag & PARENB) { + ubrlcr |= UBRLCR_PRTEN; + if (!(cflag & PARODD)) + ubrlcr |= UBRLCR_EVENPRT; + } + if (port->fifosize > 1) + ubrlcr |= UBRLCR_FIFOEN; + + port->read_status_mask = UARTDR_OVERR; + if (iflag & INPCK) + port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (iflag & IGNPAR) + port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR; + if (iflag & IGNBRK) { + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (iflag & IGNPAR) + port->ignore_status_mask |= UARTDR_OVERR; + } + + quot -= 1; + + /* first, disable everything */ + spin_lock_irqsave(&port->lock, flags); + + clps_writel(ubrlcr | quot, UBRLCR(port)); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *clps711xuart_type(struct uart_port *port) +{ + return port->type == PORT_CLPS711X ? "CLPS711x" : NULL; +} + +/* + * Configure/autoconfigure the port. + */ +static void clps711xuart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_CLPS711X; +} + +static void clps711xuart_release_port(struct uart_port *port) +{ +} + +static int clps711xuart_request_port(struct uart_port *port) +{ + return 0; +} + +static struct uart_ops clps711x_pops = { + tx_empty: clps711xuart_tx_empty, + set_mctrl: clps711xuart_set_mctrl_null, + get_mctrl: clps711xuart_get_mctrl, + stop_tx: clps711xuart_stop_tx, + start_tx: clps711xuart_start_tx, + stop_rx: clps711xuart_stop_rx, + enable_ms: clps711xuart_enable_ms, + break_ctl: clps711xuart_break_ctl, + startup: clps711xuart_startup, + shutdown: clps711xuart_shutdown, + change_speed: clps711xuart_change_speed, + type: clps711xuart_type, + config_port: clps711xuart_config_port, + release_port: clps711xuart_release_port, + request_port: clps711xuart_request_port, +}; + +static struct uart_port clps711x_ports[UART_NR] = { + { + iobase: SYSCON1, + irq: IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */ + uartclk: 3686400, + fifosize: 16, + ops: &clps711x_pops, + flags: ASYNC_BOOT_AUTOCONF, + }, + { + iobase: SYSCON2, + irq: IRQ_UTXINT2, /* IRQ_URXINT2 */ + uartclk: 3686400, + fifosize: 16, + ops: &clps711x_pops, + flags: ASYNC_BOOT_AUTOCONF, + } +}; + +#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + * + * Note that this is called with interrupts already disabled + */ +static void +clps711xuart_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = clps711x_ports + co->index; + unsigned int status, syscon; + int i; + + /* + * Ensure that the port is enabled. + */ + syscon = clps_readl(SYSCON(port)); + clps_writel(syscon | SYSCON_UARTEN, SYSCON(port)); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = clps_readl(SYSFLG(port)); + } while (status & SYSFLG_UTXFF); + clps_writel(s[i], UARTDR(port)); + if (s[i] == '\n') { + do { + status = clps_readl(SYSFLG(port)); + } while (status & SYSFLG_UTXFF); + clps_writel('\r', UARTDR(port)); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the uart state. + */ + do { + status = clps_readl(SYSFLG(port)); + } while (status & SYSFLG_UBUSY); + + clps_writel(syscon, SYSCON(port)); +} + +static kdev_t clps711xuart_console_device(struct console *co) +{ + return mk_kdev(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR + co->index); +} + +static void __init +clps711xuart_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) { + unsigned int ubrlcr, quot; + + ubrlcr = clps_readl(UBRLCR(port)); + + *parity = 'n'; + if (ubrlcr & UBRLCR_PRTEN) { + if (ubrlcr & UBRLCR_EVENPRT) + *parity = 'e'; + else + *parity = 'o'; + } + + if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7) + *bits = 7; + else + *bits = 8; + + quot = ubrlcr & UBRLCR_BAUD_MASK; + *baud = port->uartclk / (16 * (quot + 1)); + } +} + +static int __init clps711xuart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + port = uart_get_console(clps711x_ports, UART_NR, co); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + clps711xuart_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console clps711x_console = { + name: SERIAL_CLPS711X_NAME, + write: clps711xuart_console_write, + device: clps711xuart_console_device, + setup: clps711xuart_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +void __init clps711xuart_console_init(void) +{ + register_console(&clps711x_console); +} + +#define CLPS711X_CONSOLE &clps711x_console +#else +#define CLPS711X_CONSOLE NULL +#endif + +static struct uart_driver clps711x_reg = { + driver_name: "ttyCL", +#ifdef CONFIG_DEVFS_FS + dev_name: SERIAL_CLPS711X_NAME, +#else + dev_name: SERIAL_CLPS711X_NAME, +#endif + + major: SERIAL_CLPS711X_MAJOR, + minor: SERIAL_CLPS711X_MINOR, + nr: UART_NR, + + cons: CLPS711X_CONSOLE, +}; + +static int __init clps711xuart_init(void) +{ + int ret, i; + + printk(KERN_INFO "Serial: CLPS711x driver $Revision: 1.38 $\n"); + + ret = uart_register_driver(&clps711x_reg); + if (ret) + return ret; + + for (i = 0; i < UART_NR; i++) + uart_add_one_port(&clps711x_reg, &clps711x_ports[i]); + + return 0; +} + +static void __exit clps711xuart_exit(void) +{ + int i; + + for (i = 0; i < UART_NR; i++) + uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]); + + uart_unregister_driver(&clps711x_reg); +} + +module_init(clps711xuart_init); +module_exit(clps711xuart_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Deep Blue Solutions Ltd"); +MODULE_DESCRIPTION("CLPS-711x generic serial driver $Revision: 1.38 $"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c new file mode 100644 index 000000000000..c6d354329e02 --- /dev/null +++ b/drivers/serial/serial_core.c @@ -0,0 +1,2467 @@ +/* + * linux/drivers/char/serial_core.c + * + * Driver core for serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright 1999 ARM Limited + * Copyright (C) 2000-2001 Deep Blue Solutions Ltd. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: serial_core.c,v 1.89 2002/07/20 18:07:32 rmk Exp $ + * + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.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/ptrace.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/circ_buf.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/pm.h> +#include <linux/serial_core.h> +#include <linux/smp_lock.h> +#include <linux/serial.h> /* for serial_state and serial_icounter_struct */ + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/bitops.h> + +#undef DEBUG +#ifdef DEBUG +#define DPRINTK(x...) printk(x) +#else +#define DPRINTK(x...) do { } while (0) +#endif + +#ifndef CONFIG_PM +#define pm_access(pm) do { } while (0) +#define pm_unregister(pm) do { } while (0) +#endif + +/* + * This is used to lock changes in serial line configuration. + */ +static DECLARE_MUTEX(port_sem); + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +static void uart_change_speed(struct uart_info *info, struct termios *old_termios); +static void uart_wait_until_sent(struct tty_struct *tty, int timeout); + +/* + * This routine is used by the interrupt handler to schedule processing in + * the software interrupt portion of the driver. + */ +void uart_event(struct uart_port *port, int event) +{ + struct uart_info *info = port->info; + + set_bit(0, &info->event); + tasklet_schedule(&info->tlet); +} + +static void uart_stop(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + struct uart_port *port = info->port; + + port->ops->stop_tx(port, 1); +} + +static void __uart_start(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + struct uart_port *port = info->port; + + if (!uart_circ_empty(&info->xmit) && info->xmit.buf && + !tty->stopped && !tty->hw_stopped) + port->ops->start_tx(port, 1); +} + +static void uart_start(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + unsigned long flags; + + pm_access(info->state->pm); + + spin_lock_irqsave(&info->port->lock, flags); + __uart_start(tty); + spin_unlock_irqrestore(&info->port->lock, flags); +} + +static void uart_tasklet_action(unsigned long data) +{ + struct uart_info *info = (struct uart_info *)data; + struct tty_struct *tty; + + tty = info->tty; + if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event)) + return; + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); +} + +static inline void +uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) +{ + unsigned long flags; + unsigned int old; + + spin_lock_irqsave(&port->lock, flags); + old = port->mctrl; + port->mctrl = (old & ~clear) | set; + if (old != port->mctrl) + port->ops->set_mctrl(port, port->mctrl); + spin_unlock_irqrestore(&port->lock, flags); +} + +#define uart_set_mctrl(port,set) uart_update_mctrl(port,set,0) +#define uart_clear_mctrl(port,clear) uart_update_mctrl(port,0,clear) + +static inline void uart_update_altspeed(struct uart_info *info) +{ + unsigned int flags = info->port->flags & UPF_SPD_MASK; + + if (flags == UPF_SPD_HI) + info->tty->alt_speed = 57600; + if (flags == UPF_SPD_VHI) + info->tty->alt_speed = 115200; + if (flags == UPF_SPD_SHI) + info->tty->alt_speed = 230400; + if (flags == UPF_SPD_WARP) + info->tty->alt_speed = 460800; +} + +/* + * Startup the port. This will be called once per open. All calls + * will be serialised by the global port semaphore. + */ +static int uart_startup(struct uart_info *info, int init_hw) +{ + struct uart_port *port = info->port; + unsigned long page; + int retval = 0; + + if (info->flags & UIF_INITIALIZED) + return 0; + + /* + * Set the TTY IO error marker - we will only clear this + * once we have successfully opened the port. Also set + * up the tty->alt_speed kludge + */ + if (info->tty) { + set_bit(TTY_IO_ERROR, &info->tty->flags); + uart_update_altspeed(info); + } + + if (port->type == PORT_UNKNOWN) + return 0; + + /* + * Initialise and allocate the transmit and temporary + * buffer. + */ + if (!info->xmit.buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + info->xmit.buf = (unsigned char *) page; + info->tmpbuf = info->xmit.buf + UART_XMIT_SIZE; + init_MUTEX(&info->tmpbuf_sem); + uart_circ_clear(&info->xmit); + } + + port->mctrl = 0; + + retval = port->ops->startup(port); + if (retval == 0) { + if (init_hw) { + /* + * Initialise the hardware port settings. + */ + uart_change_speed(info, NULL); + + /* + * Setup the RTS and DTR signals once the + * port is open and ready to respond. + */ + if (info->tty->termios->c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); + } + + info->flags |= UIF_INITIALIZED; + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + + if (retval && capable(CAP_SYS_ADMIN)) + retval = 0; + + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. Calls to + * uart_shutdown are serialised by port_sem. + */ +static void uart_shutdown(struct uart_info *info) +{ + struct uart_port *port = info->port; + + if (!(info->flags & UIF_INITIALIZED)) + return; + + /* + * Turn off DTR and RTS early. + */ + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + uart_clear_mctrl(info->port, TIOCM_DTR | TIOCM_RTS); + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free + * the irq here so the queue might never be woken up. Note + * that we won't end up waiting on delta_msr_wait again since + * any outstanding file descriptors should be pointing at + * hung_up_tty_fops now. + */ + wake_up_interruptible(&info->delta_msr_wait); + + /* + * Free the IRQ and disable the port. + */ + port->ops->shutdown(port); + + /* + * Ensure that the IRQ handler isn't running on another CPU. + */ + synchronize_irq(port->irq); + + /* + * Free the transmit buffer page. + */ + if (info->xmit.buf) { + free_page((unsigned long)info->xmit.buf); + info->xmit.buf = NULL; + info->tmpbuf = NULL; + } + + /* + * kill off our tasklet + */ + tasklet_kill(&info->tlet); + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~UIF_INITIALIZED; +} + +static inline +unsigned int uart_calculate_quot(struct uart_info *info, unsigned int baud) +{ + struct uart_port *port = info->port; + unsigned int quot; + + /* Special case: B0 rate */ + if (baud == 0) + baud = 9600; + + /* Old HI/VHI/custom speed handling */ + if (baud == 38400 && + ((port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)) + quot = info->state->custom_divisor; + else + quot = port->uartclk / (16 * baud); + + return quot; +} + +static void +uart_change_speed(struct uart_info *info, struct termios *old_termios) +{ + struct uart_port *port = info->port; + unsigned int quot, cflag, bits, try; + + /* + * If we have no tty, termios, or the port does not exist, + * then we can't set the parameters for this port. + */ + if (!info->tty || !info->tty->termios || port->type == PORT_UNKNOWN) + return; + + /* + * Set flags based on termios cflag + */ + cflag = info->tty->termios->c_cflag; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + bits = 7; + break; + case CS6: + bits = 8; + break; + case CS7: + bits = 9; + break; + default: + bits = 10; + break; // CS8 + } + + if (cflag & CSTOPB) + bits++; + if (cflag & PARENB) + bits++; + + for (try = 0; try < 3; try ++) { + unsigned int baud; + + /* Determine divisor based on baud rate */ + baud = tty_get_baud_rate(info->tty); + quot = uart_calculate_quot(info, baud); + if (quot) + break; + + /* + * Oops, the quotient was zero. Try again with + * the old baud rate if possible. + */ + info->tty->termios->c_cflag &= ~CBAUD; + if (old_termios) { + info->tty->termios->c_cflag |= + (old_termios->c_cflag & CBAUD); + old_termios = NULL; + continue; + } + + /* + * As a last resort, if the quotient is zero, + * default to 9600 bps + */ + info->tty->termios->c_cflag |= B9600; + } + + /* + * The total number of bits to be transmitted in the fifo. + */ + bits = bits * port->fifosize; + + /* + * Figure the timeout to send the above number of bits. + * Add .02 seconds of slop + */ + port->timeout = (HZ * bits) / (port->uartclk / (16 * quot)) + HZ/50; + + if (cflag & CRTSCTS) + info->flags |= UIF_CTS_FLOW; + else + info->flags &= ~UIF_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~UIF_CHECK_CD; + else + info->flags |= UIF_CHECK_CD; + + port->ops->change_speed(port, cflag, info->tty->termios->c_iflag, quot); +} + +static inline void +__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c) +{ + unsigned long flags; + + if (!circ->buf) + return; + + spin_lock_irqsave(&port->lock, flags); + if (uart_circ_chars_free(circ) != 0) { + circ->buf[circ->head] = c; + circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); + } + spin_unlock_irqrestore(&port->lock, flags); +} + +static inline int +__uart_user_write(struct uart_port *port, struct circ_buf *circ, + const unsigned char *buf, int count) +{ + unsigned long flags; + int c, ret = 0; + + if (down_interruptible(&port->info->tmpbuf_sem)) + return -EINTR; + + while (1) { + int c1; + c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + c -= copy_from_user(port->info->tmpbuf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + spin_lock_irqsave(&port->lock, flags); + c1 = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(circ->buf + circ->head, port->info->tmpbuf, c); + circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); + spin_unlock_irqrestore(&port->lock, flags); + buf += c; + count -= c; + ret += c; + } + up(&port->info->tmpbuf_sem); + + return ret; +} + +static inline int +__uart_kern_write(struct uart_port *port, struct circ_buf *circ, + const unsigned char *buf, int count) +{ + unsigned long flags; + int c, ret = 0; + + spin_lock_irqsave(&port->lock, flags); + while (1) { + c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + memcpy(circ->buf + circ->head, buf, c); + circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); + buf += c; + count -= c; + ret += c; + } + spin_unlock_irqrestore(&port->lock, flags); + + return ret; +} + +static void uart_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct uart_info *info = tty->driver_data; + + if (tty) + __uart_put_char(info->port, &info->xmit, ch); +} + +static void uart_flush_chars(struct tty_struct *tty) +{ + uart_start(tty); +} + +static int +uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf, + int count) +{ + struct uart_info *info = tty->driver_data; + int ret; + + if (!tty || !info->xmit.buf) + return 0; + + if (from_user) + ret = __uart_user_write(info->port, &info->xmit, buf, count); + else + ret = __uart_kern_write(info->port, &info->xmit, buf, count); + + uart_start(tty); + return ret; +} + +static int uart_write_room(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + + return uart_circ_chars_free(&info->xmit); +} + +static int uart_chars_in_buffer(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + + return uart_circ_chars_pending(&info->xmit); +} + +static void uart_flush_buffer(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + unsigned long flags; + + DPRINTK("uart_flush_buffer(%d) called\n", + MINOR(tty->device) - tty->driver.minor_start); + + spin_lock_irqsave(&info->port->lock, flags); + uart_circ_clear(&info->xmit); + spin_unlock_irqrestore(&info->port->lock, flags); + 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 uart_send_xchar(struct tty_struct *tty, char ch) +{ + struct uart_info *info = tty->driver_data; + struct uart_port *port = info->port; + + if (port->ops->send_xchar) + port->ops->send_xchar(port, ch); + else { + port->x_char = ch; + if (ch) + port->ops->start_tx(port, 0); + } +} + +static void uart_throttle(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + + if (I_IXOFF(tty)) + uart_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) + uart_clear_mctrl(info->port, TIOCM_RTS); +} + +static void uart_unthrottle(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + struct uart_port *port = info->port; + + if (I_IXOFF(tty)) { + if (port->x_char) + port->x_char = 0; + else + uart_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios->c_cflag & CRTSCTS) + uart_set_mctrl(port, TIOCM_RTS); +} + +static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo) +{ + struct uart_state *state = info->state; + struct uart_port *port = info->port; + struct serial_struct tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = port->type; + tmp.line = port->line; + tmp.port = port->iobase; + if (HIGH_BITS_OFFSET) + tmp.port_high = port->iobase >> HIGH_BITS_OFFSET; + tmp.irq = port->irq; + tmp.flags = port->flags | info->flags; + tmp.xmit_fifo_size = port->fifosize; + tmp.baud_base = port->uartclk / 16; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = state->custom_divisor; + tmp.hub6 = port->hub6; + tmp.io_type = port->iotype; + tmp.iomem_reg_shift = port->regshift; + tmp.iomem_base = (void *)port->mapbase; + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int +uart_set_info(struct uart_info *info, struct serial_struct *newinfo) +{ + struct serial_struct new_serial; + struct uart_state *state = info->state; + struct uart_port *port = info->port; + unsigned long new_port; + unsigned int change_irq, change_port, old_flags; + unsigned int old_custom_divisor; + int retval = 0; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + new_serial.irq = irq_cannonicalize(new_serial.irq); + + /* + * This semaphore protects state->count. It is also + * very useful to prevent opens. Also, take the + * port configuration semaphore to make sure that a + * module insertion/removal doesn't change anything + * under us. + */ + down(&port_sem); + + change_irq = new_serial.irq != port->irq; + + /* + * Since changing the 'type' of the port changes its resource + * allocations, we should treat type changes the same as + * IO port changes. + */ + change_port = new_port != port->iobase || + (unsigned long)new_serial.iomem_base != port->mapbase || + new_serial.hub6 != port->hub6 || + new_serial.io_type != port->iotype || + new_serial.iomem_reg_shift != port->regshift || + new_serial.type != port->type; + + old_flags = port->flags; + old_custom_divisor = state->custom_divisor; + + if (!capable(CAP_SYS_ADMIN)) { + retval = -EPERM; + if (change_irq || change_port || + (new_serial.baud_base != port->uartclk / 16) || + (new_serial.close_delay != state->close_delay) || + (new_serial.closing_wait != state->closing_wait) || + (new_serial.xmit_fifo_size != port->fifosize) || + (((new_serial.flags ^ old_flags) & ~UPF_USR_MASK) != 0)) + goto exit; + port->flags = ((port->flags & ~UPF_USR_MASK) | + (new_serial.flags & UPF_USR_MASK)); + state->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + /* + * Ask the low level driver to verify the settings. + */ + if (port->ops->verify_port) + retval = port->ops->verify_port(port, &new_serial); + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)) + retval = -EINVAL; + + if (retval) + goto exit; + + if (change_port || change_irq) { + retval = -EBUSY; + + /* + * Make sure that we are the sole user of this port. + */ + if (state->count > 1 || info->blocked_open != 0) + goto exit; + + /* + * We need to shutdown the serial port at the old + * port/type/irq combination. + */ + uart_shutdown(info); + } + + if (change_port) { + unsigned long old_iobase, old_mapbase; + unsigned int old_type, old_iotype, old_hub6, old_shift; + + old_iobase = port->iobase; + old_mapbase = port->mapbase; + old_type = port->type; + old_hub6 = port->hub6; + old_iotype = port->iotype; + old_shift = port->regshift; + + /* + * Free and release old regions + */ + if (old_type != PORT_UNKNOWN) + port->ops->release_port(port); + + port->iobase = new_port; + port->type = new_serial.type; + port->hub6 = new_serial.hub6; + port->iotype = new_serial.io_type; + port->regshift = new_serial.iomem_reg_shift; + port->mapbase = (unsigned long)new_serial.iomem_base; + + /* + * Claim and map the new regions + */ + if (port->type != PORT_UNKNOWN) + retval = port->ops->request_port(port); + + /* + * If we fail to request resources for the + * new port, try to restore the old settings. + */ + if (retval && old_type != PORT_UNKNOWN) { + port->iobase = old_iobase; + port->type = old_type; + port->hub6 = old_hub6; + port->iotype = old_iotype; + port->regshift = old_shift; + port->mapbase = old_mapbase; + retval = port->ops->request_port(port); + /* + * If we failed to restore the old settings, + * we fail like this. + */ + if (retval) + port->type = PORT_UNKNOWN; + + /* + * We failed anyway. + */ + retval = -EBUSY; + } + } + + port->irq = new_serial.irq; + port->uartclk = new_serial.baud_base * 16; + port->flags = new_serial.flags & UPF_FLAGS; + state->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ / 100; + state->closing_wait = new_serial.closing_wait * HZ / 100; + port->fifosize = new_serial.xmit_fifo_size; + info->tty->low_latency = (port->flags & UPF_LOW_LATENCY) ? 1 : 0; + + check_and_exit: + retval = 0; + if (port->type == PORT_UNKNOWN) + goto exit; + if (info->flags & UIF_INITIALIZED) { + if (((old_flags ^ port->flags) & UPF_SPD_MASK) || + old_custom_divisor != state->custom_divisor) { + uart_update_altspeed(info); + uart_change_speed(info, NULL); + } + } else + retval = uart_startup(info, 1); + exit: + up(&port_sem); + return retval; +} + + +/* + * uart_get_lsr_info - get line status register info + */ +static int uart_get_lsr_info(struct uart_info *info, unsigned int *value) +{ + struct uart_port *port = info->port; + unsigned int result; + + result = port->ops->tx_empty(port); + + /* + * If we're about to load something into the transmit + * register, we'll pretend the transmitter isn't empty to + * avoid a race condition (depending on when the transmit + * interrupt happens). + */ + if (info->port->x_char || + ((uart_circ_chars_pending(&info->xmit) > 0) && + !info->tty->stopped && !info->tty->hw_stopped)) + result &= ~TIOCSER_TEMT; + + return put_user(result, value); +} + +static int uart_get_modem_info(struct uart_port *port, unsigned int *value) +{ + unsigned int result = port->mctrl; + + result |= port->ops->get_mctrl(port); + + return put_user(result, value); +} + +static int +uart_set_modem_info(struct uart_port *port, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg, set, clear; + int ret = 0; + + if (get_user(arg, value)) + return -EFAULT; + + set = clear = 0; + switch (cmd) { + case TIOCMBIS: + set = arg; + break; + case TIOCMBIC: + clear = arg; + break; + case TIOCMSET: + set = arg; + clear = ~arg; + break; + default: + ret = -EINVAL; + break; + } + if (ret == 0) + uart_update_mctrl(port, set, clear); + return ret; +} + +static void uart_break_ctl(struct tty_struct *tty, int break_state) +{ + struct uart_info *info = tty->driver_data; + struct uart_port *port = info->port; + + BUG_ON(!kernel_locked()); + + if (port->type != PORT_UNKNOWN) + port->ops->break_ctl(port, break_state); +} + +static int uart_do_autoconfig(struct uart_info *info) +{ + struct uart_port *port = info->port; + int flags, ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + /* + * Take the 'count' lock. This prevents count + * from incrementing, and hence any extra opens + * of the port while we're auto-configging. + */ + if (down_interruptible(&port_sem)) + return -ERESTARTSYS; + + ret = -EBUSY; + if (info->state->count == 1 && info->blocked_open == 0) { + uart_shutdown(info); + + /* + * If we already have a port type configured, + * we must release its resources. + */ + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + flags = UART_CONFIG_TYPE; + if (port->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + + /* + * This will claim the ports resources if + * a port is found. + */ + port->ops->config_port(port, flags); + + ret = uart_startup(info, 1); + } + up(&port_sem); + return ret; +} + +static int +uart_wait_modem_status(struct uart_info *info, unsigned long arg) +{ + struct uart_port *port = info->port; + DECLARE_WAITQUEUE(wait, current); + struct uart_icount cprev, cnow; + int ret; + + /* + * note the counters on entry + */ + spin_lock_irq(&port->lock); + memcpy(&cprev, &port->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&port->lock); + + /* + * Force modem status interrupts on + */ + port->ops->enable_ms(port); + + add_wait_queue(&info->delta_msr_wait, &wait); + for (;;) { + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&port->lock); + + set_current_state(TASK_INTERRUPTIBLE); + + 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))) { + ret = 0; + break; + } + + schedule(); + + /* see if a signal did it */ + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + cprev = cnow; + } + + current->state = TASK_RUNNING; + remove_wait_queue(&info->delta_msr_wait, &wait); + + return ret; +} + +/* + * Called via sys_ioctl under the BKL. We can use spin_lock_irq() here. + */ +static int +uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct uart_info *info = tty->driver_data; + struct serial_icounter_struct icount; + struct uart_icount cnow; + int ret = -ENOIOCTLCMD; + + BUG_ON(!kernel_locked()); + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TIOCMGET: + ret = uart_get_modem_info(info->port, + (unsigned int *)arg); + break; + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + ret = uart_set_modem_info(info->port, cmd, + (unsigned int *)arg); + break; + + case TIOCGSERIAL: + ret = uart_get_info(info, (struct serial_struct *)arg); + break; + + case TIOCSSERIAL: + ret = uart_set_info(info, (struct serial_struct *)arg); + break; + + case TIOCSERCONFIG: + ret = uart_do_autoconfig(info); + break; + + case TIOCSERGETLSR: /* Get line status register */ + ret = uart_get_lsr_info(info, (unsigned int *)arg); + break; + + /* + * 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: + ret = uart_wait_modem_status(info, arg); + break; + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + spin_lock_irq(&info->port->lock); + memcpy(&cnow, &info->port->icount, + sizeof(struct uart_icount)); + spin_unlock_irq(&info->port->lock); + + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + ret = copy_to_user((void *)arg, &icount, sizeof(icount)) + ? -EFAULT : 0; + break; + + case TIOCSERGWILD: /* obsolete */ + case TIOCSERSWILD: /* obsolete */ + ret = 0; + break; + + default: { + struct uart_port *port = info->port; + if (port->ops->ioctl) + ret = port->ops->ioctl(port, cmd, arg); + break; + } + } + return ret; +} + +static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct uart_info *info = tty->driver_data; + unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + BUG_ON(!kernel_locked()); + + /* + * These are the bits that are used to setup various + * flags in the low level driver. + */ +#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + if ((cflag ^ old_termios->c_cflag) == 0 && + RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) + return; + + uart_change_speed(info, old_termios); + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) + uart_clear_mctrl(info->port, TIOCM_RTS | TIOCM_DTR); + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { + unsigned int mask = TIOCM_DTR; + if (!(cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) + mask |= TIOCM_RTS; + uart_set_mctrl(info->port, mask); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { + spin_lock_irqsave(&info->port->lock, flags); + tty->hw_stopped = 0; + __uart_start(tty); + spin_unlock_irqrestore(&info->port->lock, flags); + } + +#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 +} + +/* + * In 2.4.5, calls to this will be serialized via the BKL in + * linux/drivers/char/tty_io.c:tty_release() + * linux/drivers/char/tty_io.c:do_tty_handup() + */ +static void uart_close(struct tty_struct *tty, struct file *filp) +{ + struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; + struct uart_info *info = tty->driver_data; + struct uart_port *port = info->port; + struct uart_state *state; + unsigned long flags; + + BUG_ON(!kernel_locked()); + + if (!info) + return; + + state = info->state; + + DPRINTK("uart_close() called\n"); + + /* + * This is safe, as long as the BKL exists in + * do_tty_hangup(), and we're protected by the BKL. + */ + if (tty_hung_up_p(filp)) + goto done; + + spin_lock_irqsave(&info->port->lock, flags); + if ((tty->count == 1) && (state->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. state->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("uart_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for %s%d: %d\n", + tty->driver.name, info->port->line, state->count); + state->count = 0; + } + if (state->count) { + spin_unlock_irqrestore(&info->port->lock, flags); + goto done; + } + + /* + * The UIF_CLOSING flag protects us against further opens + * of this port. + */ + info->flags |= UIF_CLOSING; + spin_unlock_irqrestore(&info->port->lock, flags); + + /* + * 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->state->closing_wait != USF_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->state->closing_wait); + + /* + * At this point, we stop accepting input. To do this, we + * disable the receive line status interrupts. + */ + if (info->flags & UIF_INITIALIZED) { + port->ops->stop_rx(port); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + uart_wait_until_sent(tty, port->timeout); + } + down(&port_sem); + uart_shutdown(info); + up(&port_sem); + uart_flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = NULL; + if (info->blocked_open) { + if (info->state->close_delay) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(info->state->close_delay); + set_current_state(TASK_RUNNING); + } + } else { +#ifdef CONFIG_PM + /* + * Put device into D3 state. + */ + pm_send(info->state->pm, PM_SUSPEND, (void *)3); +#else + if (port->ops->pm) + port->ops->pm(port, 3, 0); +#endif + } + + /* + * Wake up anyone trying to open this port. + */ + info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CLOSING); + wake_up_interruptible(&info->open_wait); + + done: + if (drv->owner) + __MOD_DEC_USE_COUNT(drv->owner); +} + +static void uart_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct uart_info *info = tty->driver_data; + struct uart_port *port = info->port; + unsigned long char_time, expire; + + BUG_ON(!kernel_locked()); + + if (port->type == PORT_UNKNOWN || port->fifosize == 0) + return; + + /* + * 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 = (port->timeout - HZ/50) / port->fifosize; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + + /* + * If the transmitter hasn't cleared in twice the approximate + * amount of time to send the entire FIFO, it probably won't + * ever clear. This assumes the UART isn't doing flow + * control, which is currently the case. Hence, if it ever + * takes longer than port->timeout, this is probably due to a + * UART bug of some kind. So, we clamp the timeout parameter at + * 2*port->timeout. + */ + if (timeout == 0 || timeout > 2 * port->timeout) + timeout = 2 * port->timeout; + + expire = jiffies + timeout; + + DPRINTK("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n", + port->line, jiffies, expire); + + /* + * Check whether the transmitter is empty every 'char_time'. + * 'timeout' / 'expire' give us the maximum amount of time + * we wait. + */ + while (!port->ops->tx_empty(port)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (time_after(jiffies, expire)) + break; + } + set_current_state(TASK_RUNNING); /* might not be needed */ +} + +/* + * This is called with the BKL held in + * linux/drivers/char/tty_io.c:do_tty_hangup() + * We're called from the eventd thread, so we can sleep for + * a _short_ time only. + */ +static void uart_hangup(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + struct uart_state *state = info->state; + + BUG_ON(!kernel_locked()); + + uart_flush_buffer(tty); + down(&port_sem); + if (info->flags & UIF_CLOSING) { + up(&port_sem); + return; + } + uart_shutdown(info); + info->event = 0; + state->count = 0; + info->flags &= ~UIF_NORMAL_ACTIVE; + info->tty = NULL; + up(&port_sem); + wake_up_interruptible(&info->open_wait); +} + +/* + * Copy across the serial console cflag setting into the termios settings + * for the initial open of the port. This allows continuity between the + * kernel settings, and the settings init adopts when it opens the port + * for the first time. + */ +static void uart_update_termios(struct uart_info *info) +{ + struct tty_struct *tty = info->tty; + +#ifdef CONFIG_SERIAL_CORE_CONSOLE + struct console *c = info->port->cons; + + if (c && c->cflag && c->index == info->port->line) { + tty->termios->c_cflag = c->cflag; + c->cflag = 0; + } +#endif + + /* + * If the device failed to grab its irq resources, + * or some other error occurred, don't try to talk + * to the port hardware. + */ + if (!(tty->flags & (1 << TTY_IO_ERROR))) { + /* + * Make termios settings take effect. + */ + uart_change_speed(info, NULL); + + /* + * And finally enable the RTS and DTR signals. + */ + if (tty->termios->c_cflag & CBAUD) + uart_set_mctrl(info->port, TIOCM_DTR | TIOCM_RTS); + } +} + +static int +uart_block_til_ready(struct file *filp, struct uart_info *info) +{ + DECLARE_WAITQUEUE(wait, current); + struct uart_state *state = info->state; + struct uart_port *port = info->port; + + info->blocked_open++; + state->count--; + + add_wait_queue(&info->open_wait, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + + /* + * If we have been hung up, tell userspace/restart open. + */ + if (tty_hung_up_p(filp)) + break; + + /* + * If the device is in the middle of being closed, block + * until it's done. We will need to re-initialise the + * port. Hmm, is it legal to block a non-blocking open? + */ + if (info->flags & UIF_CLOSING) + goto wait; + + /* + * If the port has been closed, tell userspace/restart open. + */ + if (!(info->flags & UIF_INITIALIZED)) + break; + + /* + * If non-blocking mode is set, or CLOCAL mode is set, + * we don't want to wait for the modem status lines to + * indicate that the port is ready. + * + * Also, if the port is not enabled/configured, we want + * to allow the open to succeed here. Note that we will + * have set TTY_IO_ERROR for a non-existant port. + */ + if ((filp->f_flags & O_NONBLOCK) || + (info->tty->termios->c_cflag & CLOCAL) || + (info->tty->flags & (1 << TTY_IO_ERROR))) { + break; + } + + /* + * Set DTR to allow modem to know we're waiting. Do + * not set RTS here - we want to make sure we catch + * the data from the modem. + */ + if (info->tty->termios->c_cflag & CBAUD) + uart_set_mctrl(info->port, TIOCM_DTR); + + /* + * and wait for the carrier to indicate that the + * modem is ready for us. + */ + if (port->ops->get_mctrl(port) & TIOCM_CAR) + break; + + wait: + schedule(); + + if (signal_pending(current)) + break; + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + + state->count++; + info->blocked_open--; + + if (signal_pending(current)) + return -ERESTARTSYS; + + if (tty_hung_up_p(filp) || !(info->flags & UIF_INITIALIZED)) + return (port->flags & UPF_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS; + + return 0; +} + +static struct uart_info *uart_get(struct uart_driver *drv, int line) +{ + struct uart_state *state = drv->state + line; + struct uart_info *info = NULL; + + down(&port_sem); + if (!state->port) + goto out; + + state->count++; + info = state->info; + + if (!info) { + info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); + if (info) { + memset(info, 0, sizeof(struct uart_info)); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->delta_msr_wait); + + /* + * Link the info into the other structures. + */ + info->port = state->port; + info->state = state; + state->port->info = info; + + tasklet_init(&info->tlet, uart_tasklet_action, + (unsigned long)info); + state->info = info; + } else + state->count--; + } + + out: + up(&port_sem); + return info; +} + +/* + * In 2.4.5, calls to uart_open are serialised by the BKL in + * linux/fs/devices.c:chrdev_open() + * Note that if this fails, then uart_close() _will_ be called. + * + * In time, we want to scrap the "opening nonpresent ports" + * behaviour and implement an alternative way for setserial + * to set base addresses/ports/types. This will allow us to + * get rid of a certain amount of extra tests. + */ +static int uart_open(struct tty_struct *tty, struct file *filp) +{ + struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; + struct uart_info *info; + int retval, line = minor(tty->device) - tty->driver.minor_start; + + BUG_ON(!kernel_locked()); + + DPRINTK("uart_open(%d) called\n", line); + + /* + * tty->driver.num won't change, so we won't fail here with + * tty->driver_data set to something non-NULL (and therefore + * we won't get caught by uart_close()). + */ + retval = -ENODEV; + if (line >= tty->driver.num) + goto fail; + + /* + * If we fail to increment the module use count, we can't have + * any other users of this tty (since this implies that the module + * is about to be unloaded). Therefore, it is safe to set + * tty->driver_data to be NULL, so uart_close() doesn't bite us. + */ + if (!try_inc_mod_count(drv->owner)) { + tty->driver_data = NULL; + goto fail; + } + + /* + * FIXME: This one isn't fun. We can't guarantee that the tty isn't + * already in open, nor can we guarantee the state of tty->driver_data + */ + info = uart_get(drv, line); + retval = -ENOMEM; + if (!info) { + if (tty->driver_data) + goto fail; + else + goto out; + } + + /* + * Once we set tty->driver_data here, we are guaranteed that + * uart_close() will decrement the driver module use count. + * Any failures from here onwards should not touch the count. + */ + tty->driver_data = info; + info->tty = tty; + info->tty->low_latency = (info->port->flags & UPF_LOW_LATENCY) ? 1 : 0; + + /* + * If the port is in the middle of closing, bail out now. + */ + if (tty_hung_up_p(filp) || (info->flags & UIF_CLOSING)) { + wait_event_interruptible(info->open_wait, + !(info->flags & UIF_CLOSING)); + retval = (info->port->flags & UPF_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS; + goto fail; + } + + /* + * Make sure the device is in D0 state. + */ + if (info->state->count == 1) { +#ifdef CONFIG_PM + pm_send(info->state->pm, PM_RESUME, (void *)0); +#else + struct uart_port *port = info->port; + if (port->ops->pm) + port->ops->pm(port, 0, 3); +#endif + } + + /* + * Start up the serial port. We have this semaphore here to + * prevent uart_startup or uart_shutdown being re-entered if + * we sleep while requesting an IRQ. + */ + down(&port_sem); + retval = uart_startup(info, 0); + up(&port_sem); + if (retval) + goto fail; + + /* + * Wait until the port is ready. + */ + retval = uart_block_til_ready(filp, info); + + /* + * If this is the first open to succeed, adjust things to suit. + */ + if (retval == 0 && !(info->flags & UIF_NORMAL_ACTIVE)) { + info->flags |= UIF_NORMAL_ACTIVE; + + uart_update_termios(info); + } + + return retval; + + out: + if (drv->owner) + __MOD_DEC_USE_COUNT(drv->owner); + fail: + return retval; +} + +#ifdef CONFIG_PROC_FS + +static const char *uart_type(struct uart_port *port) +{ + const char *str = NULL; + + if (port->ops->type) + str = port->ops->type(port); + + if (!str) + str = "unknown"; + + return str; +} + +static int uart_line_info(char *buf, struct uart_driver *drv, int i) +{ + struct uart_state *state = drv->state + i; + struct uart_port *port = state->port; + char stat_buf[32]; + unsigned int status; + int ret; + + if (!port) + return 0; + + ret = sprintf(buf, "%d: uart:%s port:%08X irq:%d", + port->line, uart_type(port), + port->iobase, port->irq); + + if (port->type == PORT_UNKNOWN) { + strcat(buf, "\n"); + return ret + 1; + } + + status = port->ops->get_mctrl(port); + + ret += sprintf(buf + ret, " tx:%d rx:%d", + port->icount.tx, port->icount.rx); + if (port->icount.frame) + ret += sprintf(buf + ret, " fe:%d", + port->icount.frame); + if (port->icount.parity) + ret += sprintf(buf + ret, " pe:%d", + port->icount.parity); + if (port->icount.brk) + ret += sprintf(buf + ret, " brk:%d", + port->icount.brk); + if (port->icount.overrun) + ret += sprintf(buf + ret, " oe:%d", + port->icount.overrun); + +#define INFOBIT(bit,str) \ + if (port->mctrl & (bit)) \ + strncat(stat_buf, (str), sizeof(stat_buf) - \ + strlen(stat_buf) - 2) +#define STATBIT(bit,str) \ + if (status & (bit)) \ + strncat(stat_buf, (str), sizeof(stat_buf) - \ + strlen(stat_buf) - 2) + + stat_buf[0] = '\0'; + stat_buf[1] = '\0'; + INFOBIT(TIOCM_RTS, "|RTS"); + STATBIT(TIOCM_CTS, "|CTS"); + INFOBIT(TIOCM_DTR, "|DTR"); + STATBIT(TIOCM_DSR, "|DSR"); + STATBIT(TIOCM_CAR, "|CD"); + STATBIT(TIOCM_RNG, "|RI"); + if (stat_buf[0]) + stat_buf[0] = ' '; + strcat(stat_buf, "\n"); + + ret += sprintf(buf + ret, stat_buf); + return ret; +} + +static int uart_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct tty_driver *ttydrv = data; + struct uart_driver *drv = ttydrv->driver_state; + int i, len = 0, l; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n", + "", "", ""); + for (i = 0; i < drv->nr && len < PAGE_SIZE - 96; i++) { + l = uart_line_info(page + len, drv, i); + len += l; + 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 + (off - begin); + return (count < begin + len - off) ? count : (begin + len - off); +} +#endif + +#ifdef CONFIG_SERIAL_CORE_CONSOLE +/* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ +struct uart_port * __init +uart_get_console(struct uart_port *ports, int nr, struct console *co) +{ + int idx = co->index; + + if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 && + ports[idx].membase == NULL)) + for (idx = 0; idx < nr; idx++) + if (ports[idx].iobase != 0 || + ports[idx].membase != NULL) + break; + + co->index = idx; + + return ports + idx; +} + +/** + * uart_parse_options - Parse serial port baud/parity/bits/flow contro. + * @options: pointer to option string + * @baud: pointer to an 'int' variable for the baud rate. + * @parity: pointer to an 'int' variable for the parity. + * @bits: pointer to an 'int' variable for the number of data bits. + * @flow: pointer to an 'int' variable for the flow control character. + * + * uart_parse_options decodes a string containing the serial console + * options. The format of the string is <baud><parity><bits><flow>, + * eg: 115200n8r + */ +void __init +uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow) +{ + char *s = options; + + *baud = simple_strtoul(s, NULL, 10); + while (*s >= '0' && *s <= '9') + s++; + if (*s) + *parity = *s++; + if (*s) + *bits = *s++ - '0'; + if (*s) + *flow = *s; +} + +struct baud_rates { + unsigned int rate; + unsigned int cflag; +}; + +static struct baud_rates baud_rates[] = { + { 921600, B921600 }, + { 460800, B460800 }, + { 230400, B230400 }, + { 115200, B115200 }, + { 57600, B57600 }, + { 38400, B38400 }, + { 19200, B19200 }, + { 9600, B9600 }, + { 4800, B4800 }, + { 2400, B2400 }, + { 1200, B1200 }, + { 0, B38400 } +}; + +/** + * uart_set_options - setup the serial console parameters + * @port: pointer to the serial ports uart_port structure + * @co: console pointer + * @baud: baud rate + * @parity: parity character - 'n' (none), 'o' (odd), 'e' (even) + * @bits: number of data bits + * @flow: flow control character - 'r' (rts) + */ +int __init +uart_set_options(struct uart_port *port, struct console *co, + int baud, int parity, int bits, int flow) +{ + unsigned int cflag = CREAD | HUPCL | CLOCAL; + unsigned int quot; + int i; + + /* + * Construct a cflag setting. + */ + for (i = 0; baud_rates[i].rate; i++) + if (baud_rates[i].rate <= baud) + break; + + cflag |= baud_rates[i].cflag; + + if (bits == 7) + cflag |= CS7; + else + cflag |= CS8; + + switch (parity) { + case 'o': case 'O': + cflag |= PARODD; + /*fall through*/ + case 'e': case 'E': + cflag |= PARENB; + break; + } + + if (flow == 'r') + cflag |= CRTSCTS; + + co->cflag = cflag; + quot = (port->uartclk / (16 * baud)); + port->ops->change_speed(port, cflag, 0, quot); + + return 0; +} + +extern void ambauart_console_init(void); +extern void anakin_console_init(void); +extern void clps711xuart_console_init(void); +extern void rs285_console_init(void); +extern void sa1100_rs_console_init(void); +extern void serial8250_console_init(void); +extern void uart00_console_init(void); + +/* + * Central "initialise all serial consoles" container. Needs to be killed. + */ +void __init uart_console_init(void) +{ +#ifdef CONFIG_SERIAL_AMBA_CONSOLE + ambauart_console_init(); +#endif +#ifdef CONFIG_SERIAL_ANAKIN_CONSOLE + anakin_console_init(); +#endif +#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE + clps711xuart_console_init(); +#endif +#ifdef CONFIG_SERIAL_21285_CONSOLE + rs285_console_init(); +#endif +#ifdef CONFIG_SERIAL_SA1100_CONSOLE + sa1100_rs_console_init(); +#endif +#ifdef CONFIG_SERIAL_8250_CONSOLE + serial8250_console_init(); +#endif +#ifdef CONFIG_SERIAL_UART00_CONSOLE + uart00_console_init(); +#endif +} +#endif /* CONFIG_SERIAL_CORE_CONSOLE */ + +#ifdef CONFIG_PM +/* + * Serial port power management. + * + * This is pretty coarse at the moment - either all on or all off. We + * should probably some day do finer power management here some day. + * + * We don't actually save any state; the serial driver already has the + * state held internally to re-setup the port when we come out of D3. + */ +static int uart_pm_set_state(struct uart_state *state, int pm_state, int oldstate) +{ + struct uart_port *port; + struct uart_ops *ops; + int running = state->info && + state->info->flags & UIF_INITIALIZED; + + down(&port_sem); + + if (!state->port || state->port->type == PORT_UNKNOWN) { + up(&port_sem); + return 0; + } + + port = state->port; + ops = port->ops; + + DPRINTK("pm: %08x: %d -> %d, %srunning\n", + port->iobase, dev->state, pm_state, running ? "" : "not "); + + if (pm_state == 0) { + if (ops->pm) + ops->pm(port, pm_state, oldstate); + if (running) { + /* + * The port lock isn't taken here - + * the port isn't initialised. + */ + ops->set_mctrl(port, 0); + ops->startup(port); + uart_change_speed(state->info, NULL); + spin_lock_irq(&port->lock); + ops->set_mctrl(port, port->mctrl); + ops->start_tx(port, 0); + spin_unlock_irq(&port->lock); + } + + /* + * Re-enable the console device after suspending. + */ + if (port->cons && port->cons->index == port->line) + port->cons->flags |= CON_ENABLED; + } else if (pm_state == 1) { + if (ops->pm) + ops->pm(port, pm_state, oldstate); + } else { + /* + * Disable the console device before suspending. + */ + if (port->cons && port->cons->index == port->line) + port->cons->flags &= ~CON_ENABLED; + + if (running) { + ops->stop_tx(port, 0); + spin_lock_irq(&port->lock); + ops->set_mctrl(port, 0); + spin_unlock_irq(&port->lock); + ops->stop_rx(port); + ops->shutdown(port); + } + if (ops->pm) + ops->pm(port, pm_state, oldstate); + } + up(&port_sem); + + return 0; +} + +/* + * Wakeup support. + */ +static int uart_pm_set_wakeup(struct uart_state *state, int data) +{ + int err = 0; + + if (state->port->ops->set_wake) + err = state->port->ops->set_wake(state->port, data); + + return err; +} + +static int uart_pm(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct uart_state *state = dev->data; + int err = 0; + + switch (rqst) { + case PM_SUSPEND: + case PM_RESUME: + err = uart_pm_set_state(state, (int)data, dev->state); + break; + + case PM_SET_WAKEUP: + err = uart_pm_set_wakeup(state, (int)data); + break; + } + return err; +} +#endif + +static inline void +uart_report_port(struct uart_driver *drv, struct uart_port *port) +{ + printk("%s%d at ", drv->dev_name, port->line); + switch (port->iotype) { + case UPIO_PORT: + printk("I/O 0x%x", port->iobase); + break; + case UPIO_HUB6: + printk("I/O 0x%x offset 0x%x", port->iobase, port->hub6); + break; + case UPIO_MEM: + printk("MMIO 0x%lx", port->mapbase); + break; + } + printk(" (irq = %d) is a %s\n", port->irq, uart_type(port)); +} + +static void +__uart_register_port(struct uart_driver *drv, struct uart_state *state, + struct uart_port *port) +{ + unsigned int flags; + + state->port = port; + + spin_lock_init(&port->lock); + port->type = PORT_UNKNOWN; + port->cons = drv->cons; + port->info = state->info; + + /* + * If there isn't a port here, don't do anything further. + */ + if (!port->iobase && !port->mapbase) + return; + + /* + * Now do the auto configuration stuff. Note that config_port + * is expected to claim the resources and map the port for us. + */ + flags = UART_CONFIG_TYPE; + if (port->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + if (port->flags & UPF_BOOT_AUTOCONF) + port->ops->config_port(port, flags); + + /* + * Register the port whether it's detected or not. This allows + * setserial to be used to alter this ports parameters. + */ + tty_register_devfs(drv->tty_driver, 0, drv->minor + port->line); + + if (port->type != PORT_UNKNOWN) { + unsigned long flags; + + uart_report_port(drv, port); + + /* + * Ensure that the modem control lines are de-activated. + * We probably don't need a spinlock around this, but + */ + spin_lock_irqsave(&port->lock, flags); + port->ops->set_mctrl(port, 0); + spin_unlock_irqrestore(&port->lock, flags); + +#ifdef CONFIG_PM + /* + * Power down all ports by default, except the + * console if we have one. We need to drop the + * port semaphore here. + */ + if (state->pm && (!drv->cons || port->line != drv->cons->index)) { + up(&port_sem); + pm_send(state->pm, PM_SUSPEND, (void *)3); + down(&port_sem); + } +#endif + } +} + +/* + * Hangup the port. This must be done outside the port_sem + * since uart_hangup() grabs this same semaphore. Grr. + */ +static void +__uart_hangup_port(struct uart_driver *drv, struct uart_state *state) +{ + struct uart_info *info = state->info; + + if (info && info->tty) + tty_vhangup(info->tty); +} + +/* + * This reverses the affects of __uart_register_port. + */ +static void +__uart_unregister_port(struct uart_driver *drv, struct uart_state *state) +{ + struct uart_port *port = state->port; + struct uart_info *info = state->info; + + state->info = NULL; + + /* + * Remove the devices from devfs + */ + tty_unregister_devfs(drv->tty_driver, drv->minor + port->line); + + /* + * Free the port IO and memory resources, if any. + */ + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + /* + * Indicate that there isn't a port here anymore. + */ + port->type = PORT_UNKNOWN; + + /* + * Kill the tasklet, and free resources. + */ + if (info) { + tasklet_kill(&info->tlet); + kfree(info); + } +} + +/** + * uart_register_driver - register a driver with the uart core layer + * @drv: low level driver structure + * + * Register a uart driver with the core driver. We in turn register + * with the tty layer, and initialise the core driver per-port state. + * + * We have a proc file in /proc/tty/driver which is named after the + * normal driver. + * + * drv->port should be NULL, and the per-port structures should be + * registered using uart_add_one_port after this call has succeeded. + */ +int uart_register_driver(struct uart_driver *drv) +{ + struct tty_driver *normal = NULL; + struct termios **termios = NULL; + int i, retval; + + BUG_ON(drv->state); + + /* + * Maybe we should be using a slab cache for this, especially if + * we have a large number of ports to handle. Note that we also + * allocate space for an integer for reference counting. + */ + drv->state = kmalloc(sizeof(struct uart_state) * drv->nr + + sizeof(int), GFP_KERNEL); + retval = -ENOMEM; + if (!drv->state) + goto out; + + memset(drv->state, 0, sizeof(struct uart_state) * drv->nr + + sizeof(int)); + + termios = kmalloc(sizeof(struct termios *) * drv->nr * 2 + + sizeof(struct tty_struct *) * drv->nr, GFP_KERNEL); + if (!termios) + goto out; + + memset(termios, 0, sizeof(struct termios *) * drv->nr * 2 + + sizeof(struct tty_struct *) * drv->nr); + + normal = kmalloc(sizeof(struct tty_driver), GFP_KERNEL); + if (!normal) + goto out; + + memset(normal, 0, sizeof(struct tty_driver)); + + drv->tty_driver = normal; + + normal->magic = TTY_DRIVER_MAGIC; + normal->driver_name = drv->driver_name; + normal->name = drv->dev_name; + normal->major = drv->major; + normal->minor_start = drv->minor; + normal->num = drv->nr; + normal->type = TTY_DRIVER_TYPE_SERIAL; + normal->subtype = SERIAL_TYPE_NORMAL; + normal->init_termios = tty_std_termios; + normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + normal->refcount = (int *)(drv->state + drv->nr); + normal->termios = termios; + normal->termios_locked = termios + drv->nr; + normal->table = (struct tty_struct **)(termios + drv->nr * 2); + normal->driver_state = drv; + + normal->open = uart_open; + normal->close = uart_close; + normal->write = uart_write; + normal->put_char = uart_put_char; + normal->flush_chars = uart_flush_chars; + normal->write_room = uart_write_room; + normal->chars_in_buffer = uart_chars_in_buffer; + normal->flush_buffer = uart_flush_buffer; + normal->ioctl = uart_ioctl; + normal->throttle = uart_throttle; + normal->unthrottle = uart_unthrottle; + normal->send_xchar = uart_send_xchar; + normal->set_termios = uart_set_termios; + normal->stop = uart_stop; + normal->start = uart_start; + normal->hangup = uart_hangup; + normal->break_ctl = uart_break_ctl; + normal->wait_until_sent = uart_wait_until_sent; +#ifdef CONFIG_PROC_FS + normal->read_proc = uart_read_proc; +#endif + + /* + * Initialise the UART state(s). + */ + for (i = 0; i < drv->nr; i++) { + struct uart_state *state = drv->state + i; + + state->close_delay = 5 * HZ / 10; + state->closing_wait = 30 * HZ; +#ifdef CONFIG_PM + state->pm = pm_register(PM_SYS_DEV, PM_SYS_COM, uart_pm); + if (state->pm) + state->pm->data = state; +#endif + } + + retval = tty_register_driver(normal); + out: + if (retval < 0) { +#ifdef CONFIG_PM + for (i = 0; i < drv->nr; i++) + pm_unregister(drv->state[i].pm); +#endif + kfree(normal); + kfree(drv->state); + kfree(termios); + } + return retval; +} + +/** + * uart_unregister_driver - remove a driver from the uart core layer + * @drv: low level driver structure + * + * Remove all references to a driver from the core driver. The low + * level driver must have removed all its ports via the + * uart_remove_one_port() if it registered them with uart_add_one_port(). + * (ie, drv->port == NULL) + */ +void uart_unregister_driver(struct uart_driver *drv) +{ + int i; + + for (i = 0; i < drv->nr; i++) + pm_unregister(drv->state[i].pm); + + tty_unregister_driver(drv->tty_driver); + + kfree(drv->state); + kfree(drv->tty_driver->termios); + kfree(drv->tty_driver); +} + +/** + * uart_add_one_port - attach a driver-defined port structure + * @drv: pointer to the uart low level driver structure for this port + * @port: uart port structure to use for this port. + * + * This allows the driver to register its own uart_port structure + * with the core driver. The main purpose is to allow the low + * level uart drivers to expand uart_port, rather than having yet + * more levels of structures. + */ +int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) +{ + struct uart_state *state; + + BUG_ON(in_interrupt()); + + if (port->line >= drv->nr) + return -EINVAL; + + state = drv->state + port->line; + + down(&port_sem); + __uart_register_port(drv, state, port); + up(&port_sem); + + return 0; +} + +/** + * uart_remove_one_port - detach a driver defined port structure + * @drv: pointer to the uart low level driver structure for this port + * @port: uart port structure for this port + * + * This unhooks (and hangs up) the specified port structure from the + * core driver. No further calls will be made to the low-level code + * for this port. + */ +int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) +{ + struct uart_state *state = drv->state + port->line; + + BUG_ON(in_interrupt()); + + if (state->port != port) + printk(KERN_ALERT "Removing wrong port: %p != %p\n", + state->port, port); + + __uart_hangup_port(drv, state); + + down(&port_sem); + __uart_unregister_port(drv, state); + state->port = NULL; + up(&port_sem); + + return 0; +} + +/* + * Are the two ports equivalent? + */ +static int uart_match_port(struct uart_port *port1, struct uart_port *port2) +{ + if (port1->iotype != port2->iotype) + return 0; + + switch (port1->iotype) { + case UPIO_PORT: + return (port1->iobase == port2->iobase); + case UPIO_HUB6: + return (port1->iobase == port2->iobase) && + (port1->hub6 == port2->hub6); + case UPIO_MEM: + return (port1->membase == port2->membase); + } + return 0; +} + +/* + * Try to find an unused uart_state slot for a port. + */ +static struct uart_state * +uart_find_match_or_unused(struct uart_driver *drv, struct uart_port *port) +{ + int i; + + /* + * First, find a port entry which matches. Note: if we do + * find a matching entry, and it has a non-zero use count, + * then we can't register the port. + */ + for (i = 0; i < drv->nr; i++) + if (uart_match_port(drv->state[i].port, port)) + return &drv->state[i]; + + /* + * We didn't find a matching entry, so look for the first + * free entry. We look for one which hasn't been previously + * used (indicated by zero iobase). + */ + for (i = 0; i < drv->nr; i++) + if (drv->state[i].port->type == PORT_UNKNOWN && + drv->state[i].port->iobase == 0 && + drv->state[i].count == 0) + return &drv->state[i]; + + /* + * That also failed. Last resort is to find any currently + * entry which doesn't have a real port associated with it. + */ + for (i = 0; i < drv->nr; i++) + if (drv->state[i].port->type == PORT_UNKNOWN && + drv->state[i].count == 0) + return &drv->state[i]; + + return NULL; +} + +/** + * uart_register_port: register uart settings with a port + * @drv: pointer to the uart low level driver structure for this port + * @port: uart port structure describing the port + * + * Register UART settings with the specified low level driver. Detect + * the type of the port if UPF_BOOT_AUTOCONF is set, and detect the + * IRQ if UPF_AUTO_IRQ is set. + * + * We try to pick the same port for the same IO base address, so that + * when a modem is plugged in, unplugged and plugged back in, it gets + * allocated the same port. + * + * Returns negative error, or positive line number. + */ +int uart_register_port(struct uart_driver *drv, struct uart_port *port) +{ + struct uart_state *state; + int ret; + + down(&port_sem); + + state = uart_find_match_or_unused(drv, port); + + if (state) { + /* + * Ok, we've found a line that we can use. + * + * If we find a port that matches this one, and it appears + * to be in-use (even if it doesn't have a type) we shouldn't + * alter it underneath itself - the port may be open and + * trying to do useful work. + */ + if (state->count != 0 || + (state->info && state->info->blocked_open != 0)) { + ret = -EBUSY; + goto out; + } + + state->port->iobase = port->iobase; + state->port->membase = port->membase; + state->port->irq = port->irq; + state->port->uartclk = port->uartclk; + state->port->fifosize = port->fifosize; + state->port->regshift = port->regshift; + state->port->iotype = port->iotype; + state->port->flags = port->flags; + state->port->line = drv->state - state; + + __uart_register_port(drv, state, state->port); + + ret = state->port->line; + } else + ret = -ENOSPC; + out: + up(&port_sem); + return ret; +} + +/** + * uart_unregister_port - de-allocate a port + * @drv: pointer to the uart low level driver structure for this port + * @line: line index previously returned from uart_register_port() + * + * Hang up the specified line associated with the low level driver, + * and mark the port as unused. + */ +void uart_unregister_port(struct uart_driver *drv, int line) +{ + struct uart_state *state; + + if (line < 0 || line >= drv->nr) { + printk(KERN_ERR "Attempt to unregister %s%d\n", + drv->dev_name, line); + return; + } + + state = drv->state + line; + + __uart_hangup_port(drv, state); + + down(&port_sem); + __uart_unregister_port(drv, state); + up(&port_sem); +} + +EXPORT_SYMBOL(uart_event); +EXPORT_SYMBOL(uart_register_driver); +EXPORT_SYMBOL(uart_unregister_driver); +EXPORT_SYMBOL(uart_register_port); +EXPORT_SYMBOL(uart_unregister_port); + +MODULE_DESCRIPTION("Serial driver core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/serial_sa1100.c b/drivers/serial/serial_sa1100.c new file mode 100644 index 000000000000..0f578fcdd757 --- /dev/null +++ b/drivers/serial/serial_sa1100.c @@ -0,0 +1,899 @@ +/* + * linux/drivers/char/serial_sa1100.c + * + * Driver for SA11x0 serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: serial_sa1100.c,v 1.41 2002/07/21 08:57:55 rmk Exp $ + * + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.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/ptrace.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/circ_buf.h> +#include <linux/serial.h> +#include <linux/console.h> +#include <linux/sysrq.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/bitops.h> +#include <asm/hardware.h> +#include <asm/mach/serial_sa1100.h> + +#if defined(CONFIG_SERIAL_SA1100_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/serial_core.h> + +/* We've been assigned a range on the "Low-density serial ports" major */ +#define SERIAL_SA1100_MAJOR 204 +#define MINOR_START 5 + +#define NR_PORTS 3 + +#define SA1100_ISR_PASS_LIMIT 256 + +/* + * Convert from ignore_status_mask or read_status_mask to UTSR[01] + */ +#define SM_TO_UTSR0(x) ((x) & 0xff) +#define SM_TO_UTSR1(x) ((x) >> 8) +#define UTSR0_TO_SM(x) ((x)) +#define UTSR1_TO_SM(x) ((x) << 8) + +#define UART_GET_UTCR0(sport) __raw_readl((sport)->port.membase + UTCR0) +#define UART_GET_UTCR1(sport) __raw_readl((sport)->port.membase + UTCR1) +#define UART_GET_UTCR2(sport) __raw_readl((sport)->port.membase + UTCR2) +#define UART_GET_UTCR3(sport) __raw_readl((sport)->port.membase + UTCR3) +#define UART_GET_UTSR0(sport) __raw_readl((sport)->port.membase + UTSR0) +#define UART_GET_UTSR1(sport) __raw_readl((sport)->port.membase + UTSR1) +#define UART_GET_CHAR(sport) __raw_readl((sport)->port.membase + UTDR) + +#define UART_PUT_UTCR0(sport,v) __raw_writel((v),(sport)->port.membase + UTCR0) +#define UART_PUT_UTCR1(sport,v) __raw_writel((v),(sport)->port.membase + UTCR1) +#define UART_PUT_UTCR2(sport,v) __raw_writel((v),(sport)->port.membase + UTCR2) +#define UART_PUT_UTCR3(sport,v) __raw_writel((v),(sport)->port.membase + UTCR3) +#define UART_PUT_UTSR0(sport,v) __raw_writel((v),(sport)->port.membase + UTSR0) +#define UART_PUT_UTSR1(sport,v) __raw_writel((v),(sport)->port.membase + UTSR1) +#define UART_PUT_CHAR(sport,v) __raw_writel((v),(sport)->port.membase + UTDR) + +/* + * This is the size of our serial port register set. + */ +#define UART_PORT_SIZE 0x24 + +/* + * This determines how often we check the modem status signals + * for any change. They generally aren't connected to an IRQ + * so we have to poll them. We also check immediately before + * filling the TX fifo incase CTS has been dropped. + */ +#define MCTRL_TIMEOUT (250*HZ/1000) + +struct sa1100_port { + struct uart_port port; + struct timer_list timer; + unsigned int old_status; +}; + +/* + * Handle any change of modem status signal since we were last called. + */ +static void sa1100_mctrl_check(struct sa1100_port *sport) +{ + unsigned int status, changed; + + status = sport->port.ops->get_mctrl(&sport->port); + changed = status ^ sport->old_status; + + if (changed == 0) + return; + + sport->old_status = status; + + if (changed & TIOCM_RI) + sport->port.icount.rng++; + if (changed & TIOCM_DSR) + sport->port.icount.dsr++; + if (changed & TIOCM_CAR) + uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); + if (changed & TIOCM_CTS) + uart_handle_cts_change(&sport->port, status & TIOCM_CTS); + + wake_up_interruptible(&sport->port.info->delta_msr_wait); +} + +/* + * This is our per-port timeout handler, for checking the + * modem status signals. + */ +static void sa1100_timeout(unsigned long data) +{ + struct sa1100_port *sport = (struct sa1100_port *)data; + unsigned long flags; + + if (sport->port.info) { + spin_lock_irqsave(&sport->port.lock, flags); + sa1100_mctrl_check(sport); + spin_unlock_irqrestore(&sport->port.lock, flags); + + mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); + } +} + +static void __sa1100_stop_tx(struct sa1100_port *sport) +{ + u32 utcr3; + + utcr3 = UART_GET_UTCR3(sport); + UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_TIE); + sport->port.read_status_mask &= ~UTSR0_TO_SM(UTSR0_TFS); +} + +/* + * interrupts disabled on entry + */ +static void sa1100_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + __sa1100_stop_tx(sport); + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +/* + * interrupts may not be disabled on entry + */ +static void sa1100_start_tx(struct uart_port *port, unsigned int tty_start) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + unsigned long flags; + u32 utcr3; + + spin_lock_irqsave(&sport->port.lock, flags); + utcr3 = UART_GET_UTCR3(sport); + sport->port.read_status_mask |= UTSR0_TO_SM(UTSR0_TFS); + UART_PUT_UTCR3(sport, utcr3 | UTCR3_TIE); + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +/* + * Interrupts enabled + */ +static void sa1100_stop_rx(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + unsigned long flags; + u32 utcr3; + + spin_lock_irqsave(&sport->port.lock, flags); + utcr3 = UART_GET_UTCR3(sport); + UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_RIE); + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +/* + * Set the modem control timer to fire immediately. + */ +static void sa1100_enable_ms(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + mod_timer(&sport->timer, jiffies); +} + +static void +sa1100_rx_chars(struct sa1100_port *sport, struct pt_regs *regs) +{ + struct tty_struct *tty = sport->port.info->tty; + unsigned int status, ch, flg, ignored = 0; + + status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | + UTSR0_TO_SM(UART_GET_UTSR0(sport)); + while (status & UTSR1_TO_SM(UTSR1_RNE)) { + ch = UART_GET_CHAR(sport); + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + sport->port.icount.rx++; + + flg = TTY_NORMAL; + + /* + * note that the error handling code is + * out of the main execution path + */ + if (status & UTSR1_TO_SM(UTSR1_PRE | UTSR1_FRE | UTSR1_ROR)) + goto handle_error; + + if (uart_handle_sysrq_char(&sport->port, ch, regs)) + goto ignore_char; + + error_return: + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + ignore_char: + status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | + UTSR0_TO_SM(UART_GET_UTSR0(sport)); + } + out: + tty_flip_buffer_push(tty); + return; + + handle_error: + if (status & UTSR1_TO_SM(UTSR1_PRE)) + sport->port.icount.parity++; + else if (status & UTSR1_TO_SM(UTSR1_FRE)) + sport->port.icount.frame++; + if (status & UTSR1_TO_SM(UTSR1_ROR)) + sport->port.icount.overrun++; + + if (status & sport->port.ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + + status &= sport->port.read_status_mask; + + if (status & UTSR1_TO_SM(UTSR1_PRE)) + flg = TTY_PARITY; + else if (status & UTSR1_TO_SM(UTSR1_FRE)) + flg = TTY_FRAME; + + if (status & UTSR1_TO_SM(UTSR1_ROR)) { + /* + * overrun does *not* affect the character + * we read from the FIFO + */ + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; +#endif + goto error_return; +} + +static void sa1100_tx_chars(struct sa1100_port *sport) +{ + struct circ_buf *xmit = &sport->port.info->xmit; + + if (sport->port.x_char) { + UART_PUT_CHAR(sport, sport->port.x_char); + sport->port.icount.tx++; + sport->port.x_char = 0; + return; + } + + /* + * Check the modem control lines before + * transmitting anything. + */ + sa1100_mctrl_check(sport); + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + __sa1100_stop_tx(sport); + return; + } + + /* + * Tried using FIFO (not checking TNF) for fifo fill: + * still had the '4 bytes repeated' problem. + */ + while (UART_GET_UTSR1(sport) & UTSR1_TNF) { + UART_PUT_CHAR(sport, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_event(&sport->port, EVT_WRITE_WAKEUP); + + if (uart_circ_empty(xmit)) + __sa1100_stop_tx(sport); +} + +static void sa1100_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sa1100_port *sport = dev_id; + unsigned int status, pass_counter = 0; + + spin_lock(&sport->port.lock); + status = UART_GET_UTSR0(sport); + status &= SM_TO_UTSR0(sport->port.read_status_mask) | ~UTSR0_TFS; + do { + if (status & (UTSR0_RFS | UTSR0_RID)) { + /* Clear the receiver idle bit, if set */ + if (status & UTSR0_RID) + UART_PUT_UTSR0(sport, UTSR0_RID); + sa1100_rx_chars(sport, regs); + } + + /* Clear the relevent break bits */ + if (status & (UTSR0_RBB | UTSR0_REB)) + UART_PUT_UTSR0(sport, status & (UTSR0_RBB | UTSR0_REB)); + + if (status & UTSR0_RBB) + sport->port.icount.brk++; + + if (status & UTSR0_REB) + uart_handle_break(&sport->port); + + if (status & UTSR0_TFS) + sa1100_tx_chars(sport); + if (pass_counter++ > SA1100_ISR_PASS_LIMIT) + break; + status = UART_GET_UTSR0(sport); + status &= SM_TO_UTSR0(sport->port.read_status_mask) | + ~UTSR0_TFS; + } while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID)); + spin_unlock(&sport->port.lock); +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int sa1100_tx_empty(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + return UART_GET_UTSR1(sport) & UTSR1_TBY ? 0 : TIOCSER_TEMT; +} + +static unsigned int sa1100_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void sa1100_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +/* + * Interrupts always disabled. + */ +static void sa1100_break_ctl(struct uart_port *port, int break_state) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + unsigned long flags; + unsigned int utcr3; + + spin_lock_irqsave(&sport->port.lock, flags); + utcr3 = UART_GET_UTCR3(sport); + if (break_state == -1) + utcr3 |= UTCR3_BRK; + else + utcr3 &= ~UTCR3_BRK; + UART_PUT_UTCR3(sport, utcr3); + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static int sa1100_startup(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(sport->port.irq, sa1100_int, 0, + "serial_sa1100", sport); + if (retval) + return retval; + + /* + * Finally, clear and enable interrupts + */ + UART_PUT_UTSR0(sport, -1); + UART_PUT_UTCR3(sport, UTCR3_RXE | UTCR3_TXE | UTCR3_RIE); + + /* + * Enable modem status interrupts + */ + sa1100_enable_ms(&sport->port); + + return 0; +} + +static void sa1100_shutdown(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + /* + * Stop our timer. + */ + del_timer_sync(&sport->timer); + + /* + * Free the interrupt + */ + free_irq(sport->port.irq, sport); + + /* + * Disable all interrupts, port and break condition. + */ + UART_PUT_UTCR3(sport, 0); +} + +static void +sa1100_change_speed(struct uart_port *port, unsigned int cflag, + unsigned int iflag, unsigned int quot) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + unsigned long flags; + unsigned int utcr0, old_utcr3; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS7: + utcr0 = 0; + break; + default: + utcr0 = UTCR0_DSS; + break; + } + if (cflag & CSTOPB) + utcr0 |= UTCR0_SBS; + if (cflag & PARENB) { + utcr0 |= UTCR0_PE; + if (!(cflag & PARODD)) + utcr0 |= UTCR0_OES; + } + + sport->port.read_status_mask &= UTSR0_TO_SM(UTSR0_TFS); + sport->port.read_status_mask |= UTSR1_TO_SM(UTSR1_ROR); + if (iflag & INPCK) + sport->port.read_status_mask |= + UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE); + if (iflag & (BRKINT | PARMRK)) + sport->port.read_status_mask |= + UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB); + + /* + * Characters to ignore + */ + sport->port.ignore_status_mask = 0; + if (iflag & IGNPAR) + sport->port.ignore_status_mask |= + UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE); + if (iflag & IGNBRK) { + sport->port.ignore_status_mask |= + UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB); + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (iflag & IGNPAR) + sport->port.ignore_status_mask |= + UTSR1_TO_SM(UTSR1_ROR); + } + + del_timer_sync(&sport->timer); + + /* first, disable interrupts and drain transmitter */ + spin_lock_irqsave(&sport->port.lock, flags); + old_utcr3 = UART_GET_UTCR3(sport); + UART_PUT_UTCR3(sport, old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)); + + while (UART_GET_UTSR1(sport) & UTSR1_TBY); + + /* then, disable everything */ + UART_PUT_UTCR3(sport, 0); + + /* set the parity, stop bits and data size */ + UART_PUT_UTCR0(sport, utcr0); + + /* set the baud rate */ + quot -= 1; + UART_PUT_UTCR1(sport, ((quot & 0xf00) >> 8)); + UART_PUT_UTCR2(sport, (quot & 0xff)); + + UART_PUT_UTSR0(sport, -1); + + UART_PUT_UTCR3(sport, old_utcr3); + spin_unlock_irqrestore(&sport->port.lock, flags); + + if (UART_ENABLE_MS(&sport->port, cflag)) + sa1100_enable_ms(&sport->port); +} + +static const char *sa1100_type(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + return sport->port.type == PORT_SA1100 ? "SA1100" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void sa1100_release_port(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + release_mem_region(sport->port.mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int sa1100_request_port(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + return request_mem_region(sport->port.mapbase, UART_PORT_SIZE, + "serial_sa1100") != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void sa1100_config_port(struct uart_port *port, int flags) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + if (flags & UART_CONFIG_TYPE && + sa1100_request_port(&sport->port) == 0) + sport->port.type = PORT_SA1100; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + * The only change we allow are to the flags and type, and + * even then only between PORT_SA1100 and PORT_UNKNOWN + */ +static int +sa1100_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_SA1100) + ret = -EINVAL; + if (sport->port.irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != SERIAL_IO_MEM) + ret = -EINVAL; + if (sport->port.uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)sport->port.mapbase != ser->iomem_base) + ret = -EINVAL; + if (sport->port.iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; +} + +static struct uart_ops sa1100_pops = { + tx_empty: sa1100_tx_empty, + set_mctrl: sa1100_set_mctrl, + get_mctrl: sa1100_get_mctrl, + stop_tx: sa1100_stop_tx, + start_tx: sa1100_start_tx, + stop_rx: sa1100_stop_rx, + enable_ms: sa1100_enable_ms, + break_ctl: sa1100_break_ctl, + startup: sa1100_startup, + shutdown: sa1100_shutdown, + change_speed: sa1100_change_speed, + type: sa1100_type, + release_port: sa1100_release_port, + request_port: sa1100_request_port, + config_port: sa1100_config_port, + verify_port: sa1100_verify_port, +}; + +static struct sa1100_port sa1100_ports[NR_PORTS]; + +/* + * Setup the SA1100 serial ports. Note that we don't include the IrDA + * port here since we have our own SIR/FIR driver (see drivers/net/irda) + * + * Note also that we support "console=ttySAx" where "x" is either 0 or 1. + * Which serial port this ends up being depends on the machine you're + * running this kernel on. I'm not convinced that this is a good idea, + * but that's the way it traditionally works. + * + * Note that NanoEngine UART3 becomes UART2, and UART2 is no longer + * used here. + */ +static void __init sa1100_init_ports(void) +{ + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0; i < NR_PORTS; i++) { + sa1100_ports[i].port.uartclk = 3686400; + sa1100_ports[i].port.ops = &sa1100_pops; + sa1100_ports[i].port.fifosize = 8; + sa1100_ports[i].port.line = i; + sa1100_ports[i].port.iotype = SERIAL_IO_MEM; + init_timer(&sa1100_ports[i].timer); + sa1100_ports[i].timer.function = sa1100_timeout; + sa1100_ports[i].timer.data = (unsigned long)&sa1100_ports[i]; + } + + /* + * make transmit lines outputs, so that when the port + * is closed, the output is in the MARK state. + */ + PPDR |= PPC_TXD1 | PPC_TXD3; + PPSR |= PPC_TXD1 | PPC_TXD3; +} + +void __init sa1100_register_uart_fns(struct sa1100_port_fns *fns) +{ + if (fns->get_mctrl) + sa1100_pops.get_mctrl = fns->get_mctrl; + if (fns->set_mctrl) + sa1100_pops.set_mctrl = fns->set_mctrl; + + sa1100_pops.pm = fns->pm; + sa1100_pops.set_wake = fns->set_wake; +} + +void __init sa1100_register_uart(int idx, int port) +{ + if (idx >= NR_PORTS) { + printk(KERN_ERR __FUNCTION__ ": bad index number %d\n", idx); + return; + } + + switch (port) { + case 1: + sa1100_ports[idx].port.membase = (void *)&Ser1UTCR0; + sa1100_ports[idx].port.mapbase = _Ser1UTCR0; + sa1100_ports[idx].port.irq = IRQ_Ser1UART; + sa1100_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF; + break; + + case 2: + sa1100_ports[idx].port.membase = (void *)&Ser2UTCR0; + sa1100_ports[idx].port.mapbase = _Ser2UTCR0; + sa1100_ports[idx].port.irq = IRQ_Ser2ICP; + sa1100_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF; + break; + + case 3: + sa1100_ports[idx].port.membase = (void *)&Ser3UTCR0; + sa1100_ports[idx].port.mapbase = _Ser3UTCR0; + sa1100_ports[idx].port.irq = IRQ_Ser3UART; + sa1100_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF; + break; + + default: + printk(KERN_ERR __FUNCTION__ ": bad port number %d\n", port); + } +} + + +#ifdef CONFIG_SERIAL_SA1100_CONSOLE + +/* + * Interrupts are disabled on entering + */ +static void +sa1100_console_write(struct console *co, const char *s, unsigned int count) +{ + struct sa1100_port *sport = &sa1100_ports[co->index]; + unsigned int old_utcr3, status, i; + + /* + * First, save UTCR3 and then disable interrupts + */ + old_utcr3 = UART_GET_UTCR3(sport); + UART_PUT_UTCR3(sport, (old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)) | + UTCR3_TXE); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = UART_GET_UTSR1(sport); + } while (!(status & UTSR1_TNF)); + UART_PUT_CHAR(sport, s[i]); + if (s[i] == '\n') { + do { + status = UART_GET_UTSR1(sport); + } while (!(status & UTSR1_TNF)); + UART_PUT_CHAR(sport, '\r'); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore UTCR3 + */ + do { + status = UART_GET_UTSR1(sport); + } while (status & UTSR1_TBY); + UART_PUT_UTCR3(sport, old_utcr3); +} + +static kdev_t sa1100_console_device(struct console *co) +{ + return mk_kdev(SERIAL_SA1100_MAJOR, MINOR_START + co->index); +} + +/* + * If the port was already initialised (eg, by a boot loader), + * try to determine the current setup. + */ +static void __init +sa1100_console_get_options(struct sa1100_port *sport, int *baud, + int *parity, int *bits) +{ + unsigned int utcr3; + + utcr3 = UART_GET_UTCR3(sport) & (UTCR3_RXE | UTCR3_TXE); + if (utcr3 == (UTCR3_RXE | UTCR3_TXE)) { + /* ok, the port was enabled */ + unsigned int utcr0, quot; + + utcr0 = UART_GET_UTCR0(sport); + + *parity = 'n'; + if (utcr0 & UTCR0_PE) { + if (utcr0 & UTCR0_OES) + *parity = 'e'; + else + *parity = 'o'; + } + + if (utcr0 & UTCR0_DSS) + *bits = 8; + else + *bits = 7; + + quot = UART_GET_UTCR2(sport) | UART_GET_UTCR1(sport) << 8; + quot &= 0xfff; + *baud = sport->port.uartclk / (16 * (quot + 1)); + } +} + +static int __init +sa1100_console_setup(struct console *co, char *options) +{ + struct sa1100_port *sport; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index == -1 || co->index >= NR_PORTS) + co->index = 0; + sport = &sa1100_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + sa1100_console_get_options(sport, &baud, &parity, &bits); + + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + +static struct console sa1100_console = { + name: "ttySA", + write: sa1100_console_write, + device: sa1100_console_device, + setup: sa1100_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +void __init sa1100_rs_console_init(void) +{ + sa1100_init_ports(); + register_console(&sa1100_console); +} + +#define SA1100_CONSOLE &sa1100_console +#else +#define SA1100_CONSOLE NULL +#endif + +static struct uart_driver sa1100_reg = { + owner: THIS_MODULE, + driver_name: "ttySA", +#ifdef CONFIG_DEVFS_FS + dev_name: "ttySA%d", +#else + dev_name: "ttySA", +#endif + major: SERIAL_SA1100_MAJOR, + minor: MINOR_START, + nr: NR_PORTS, + cons: SA1100_CONSOLE, +}; + +static int __init sa1100_serial_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: SA11x0 driver $Revision: 1.41 $\n"); + + sa1100_init_ports(); + ret = uart_register_driver(&sa1100_reg); + if (ret == 0) { + int i; + + for (i = 0; i < NR_PORTS; i++) + uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port); + } + return ret; +} + +static void __exit sa1100_serial_exit(void) +{ + int i; + + for (i = 0; i < NR_PORTS; i++) + uart_remove_one_port(&sa1100_reg, &sa1100_ports[i].port); + + uart_unregister_driver(&sa1100_reg); +} + +module_init(sa1100_serial_init); +module_exit(sa1100_serial_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Deep Blue Solutions Ltd"); +MODULE_DESCRIPTION("SA1100 generic serial port driver $Revision: 1.41 $"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/serial_uart00.c b/drivers/serial/serial_uart00.c new file mode 100644 index 000000000000..e90c36f6ac97 --- /dev/null +++ b/drivers/serial/serial_uart00.c @@ -0,0 +1,778 @@ +/* + * linux/drivers/char/serial_uart00.c + * + * Driver for UART00 serial ports + * + * Based on drivers/char/serial_amba.c, by ARM Limited & + * Deep Blue Solutions Ltd. + * Copyright 2001 Altera Corporation + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: serial_uart00.c,v 1.32 2002/07/20 17:10:04 rmk Exp $ + * + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.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/ptrace.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/circ_buf.h> +#include <linux/serial.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/pld/pld_hotswap.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/bitops.h> +#include <asm/sizes.h> + +#if defined(CONFIG_SERIAL_UART00_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/serial_core.h> +#include <asm/arch/excalibur.h> +#define UART00_TYPE (volatile unsigned int*) +#include <asm/arch/uart00.h> +#include <asm/arch/int_ctrl00.h> + +#define UART_NR 2 + +#define SERIAL_UART00_NAME "ttyUA" +#define SERIAL_UART00_MAJOR 204 +#define SERIAL_UART00_MINOR 16 /* Temporary - will change in future */ +#define SERIAL_UART00_NR UART_NR +#define UART_PORT_SIZE 0x50 + +#define UART00_ISR_PASS_LIMIT 256 + +/* + * Access macros for the UART00 UARTs + */ +#define UART_GET_INT_STATUS(p) inl(UART_ISR((p)->membase)) +#define UART_PUT_IES(p, c) outl(c,UART_IES((p)->membase)) +#define UART_GET_IES(p) inl(UART_IES((p)->membase)) +#define UART_PUT_IEC(p, c) outl(c,UART_IEC((p)->membase)) +#define UART_GET_IEC(p) inl(UART_IEC((p)->membase)) +#define UART_PUT_CHAR(p, c) outl(c,UART_TD((p)->membase)) +#define UART_GET_CHAR(p) inl(UART_RD((p)->membase)) +#define UART_GET_RSR(p) inl(UART_RSR((p)->membase)) +#define UART_GET_RDS(p) inl(UART_RDS((p)->membase)) +#define UART_GET_MSR(p) inl(UART_MSR((p)->membase)) +#define UART_GET_MCR(p) inl(UART_MCR((p)->membase)) +#define UART_PUT_MCR(p, c) outl(c,UART_MCR((p)->membase)) +#define UART_GET_MC(p) inl(UART_MC((p)->membase)) +#define UART_PUT_MC(p, c) outl(c,UART_MC((p)->membase)) +#define UART_GET_TSR(p) inl(UART_TSR((p)->membase)) +#define UART_GET_DIV_HI(p) inl(UART_DIV_HI((p)->membase)) +#define UART_PUT_DIV_HI(p,c) outl(c,UART_DIV_HI((p)->membase)) +#define UART_GET_DIV_LO(p) inl(UART_DIV_LO((p)->membase)) +#define UART_PUT_DIV_LO(p,c) outl(c,UART_DIV_LO((p)->membase)) +#define UART_RX_DATA(s) ((s) & UART_RSR_RX_LEVEL_MSK) +#define UART_TX_READY(s) (((s) & UART_TSR_TX_LEVEL_MSK) < 15) +//#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & UART00_UARTFR_TMSK) == 0) + +static void uart00_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + UART_PUT_IEC(port, UART_IEC_TIE_MSK); +} + +static void uart00_stop_rx(struct uart_port *port) +{ + UART_PUT_IEC(port, UART_IEC_RE_MSK); +} + +static void uart00_enable_ms(struct uart_port *port) +{ + UART_PUT_IES(port, UART_IES_ME_MSK); +} + +static void +uart00_rx_chars(struct uart_port *port, struct pt_regs *regs) +{ + struct tty_struct *tty = port->info->tty; + unsigned int status, ch, rds, flg, ignored = 0; + + status = UART_GET_RSR(port); + while (UART_RX_DATA(status)) { + /* + * We need to read rds before reading the + * character from the fifo + */ + rds = UART_GET_RDS(port); + ch = UART_GET_CHAR(port); + port->icount.rx++; + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + + flg = TTY_NORMAL; + + /* + * Note that the error handling code is + * out of the main execution path + */ + if (rds & (UART_RDS_BI_MSK |UART_RDS_FE_MSK| + UART_RDS_PE_MSK |UART_RDS_PE_MSK)) + goto handle_error; + if (uart_handle_sysrq_char(port, ch, regs)) + goto ignore_char; + + error_return: + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + ignore_char: + status = UART_GET_RSR(port); + } + out: + tty_flip_buffer_push(tty); + return; + + handle_error: + if (rds & UART_RDS_BI_MSK) { + status &= ~(UART_RDS_FE_MSK | UART_RDS_PE_MSK); + port->icount.brk++; + if (uart_handle_break(port)) + goto ignore_char; + } else if (rds & UART_RDS_PE_MSK) + port->icount.parity++; + else if (rds & UART_RDS_PE_MSK) + port->icount.frame++; + if (rds & UART_RDS_OE_MSK) + port->icount.overrun++; + + if (rds & port->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + rds &= port->read_status_mask; + + if (rds & UART_RDS_BI_MSK) + flg = TTY_BREAK; + else if (rds & UART_RDS_PE_MSK) + flg = TTY_PARITY; + else if (rds & UART_RDS_FE_MSK) + flg = TTY_FRAME; + + if (status & UART_RDS_OE_MSK) { + /* + * CHECK: does overrun affect the current character? + * ASSUMPTION: it does not. + */ + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef SUPPORT_SYSRQ + port->sysrq = 0; +#endif + goto error_return; +} + +static void uart00_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + int count; + + if (port->x_char) { + while ((UART_GET_TSR(port) & UART_TSR_TX_LEVEL_MSK) == 15); + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + uart00_stop_tx(port, 0); + return; + } + + count = port->fifosize >> 1; + do { + while ((UART_GET_TSR(port) & UART_TSR_TX_LEVEL_MSK) == 15); + UART_PUT_CHAR(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_event(port, EVT_WRITE_WAKEUP); + + if (uart_circ_empty(xmit)) + uart00_stop_tx(port, 0); +} + +static void uart00_start_tx(struct uart_port *port, unsigned int tty_start) +{ + UART_PUT_IES(port, UART_IES_TIE_MSK); + uart00_tx_chars(port); +} + +static void uart00_modem_status(struct uart_port *port) +{ + unsigned int status; + + status = UART_GET_MSR(port); + + if (!status & (UART_MSR_DCTS_MSK | UART_MSR_DDSR_MSK | + UART_MSR_TERI_MSK | UART_MSR_DDCD_MSK)) + return; + + if (status & UART_MSR_DDCD_MSK) + uart_handle_dcd_change(port, status & UART_MSR_DCD_MSK); + + if (status & UART_MSR_DDSR_MSK) + port->icount.dsr++; + + if (status & UART_MSR_DCTS_MSK) + uart_handle_cts_change(port, status & UART_MSR_CTS_MSK); + + wake_up_interruptible(&port->info->delta_msr_wait); +} + +static void uart00_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_port *port = dev_id; + unsigned int status, pass_counter = 0; + + status = UART_GET_INT_STATUS(port); + do { + if (status & UART_ISR_RI_MSK) + uart00_rx_chars(port, regs); + if (status & UART_ISR_MI_MSK) + uart00_modem_status(port); + if (status & (UART_ISR_TI_MSK | UART_ISR_TII_MSK)) + uart00_tx_chars(port); + if (pass_counter++ > UART00_ISR_PASS_LIMIT) + break; + + status = UART_GET_INT_STATUS(port); + } while (status); +} + +static unsigned int uart00_tx_empty(struct uart_port *port) +{ + return UART_GET_TSR(port) & UART_TSR_TX_LEVEL_MSK? 0 : TIOCSER_TEMT; +} + +static unsigned int uart00_get_mctrl(struct uart_port *port) +{ + unsigned int result = 0; + unsigned int status; + + status = UART_GET_MSR(port); + if (status & UART_MSR_DCD_MSK) + result |= TIOCM_CAR; + if (status & UART_MSR_DSR_MSK) + result |= TIOCM_DSR; + if (status & UART_MSR_CTS_MSK) + result |= TIOCM_CTS; + if (status & UART_MSR_RI_MSK) + result |= TIOCM_RI; + + return result; +} + +static void uart00_set_mctrl_null(struct uart_port *port, unsigned int mctrl) +{ +} + +static void uart00_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned int mcr; + + spin_lock_irqsave(&port->lock, flags); + mcr = UART_GET_MCR(port); + if (break_state == -1) + mcr |= UART_MCR_BR_MSK; + else + mcr &= ~UART_MCR_BR_MSK; + UART_PUT_MCR(port, mcr); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void +uart00_change_speed(struct uart_port *port, unsigned int cflag, + unsigned int iflag, unsigned int quot) +{ + unsigned int uart_mc, old_ies; + unsigned long flags; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + uart_mc = UART_MC_CLS_CHARLEN_5; + break; + case CS6: + uart_mc = UART_MC_CLS_CHARLEN_6; + break; + case CS7: + uart_mc = UART_MC_CLS_CHARLEN_7; + break; + default: // CS8 + uart_mc = UART_MC_CLS_CHARLEN_8; + break; + } + if (cflag & CSTOPB) + uart_mc|= UART_MC_ST_TWO; + if (cflag & PARENB) { + uart_mc |= UART_MC_PE_MSK; + if (!(cflag & PARODD)) + uart_mc |= UART_MC_EP_MSK; + } + + port->read_status_mask = UART_RDS_OE_MSK; + if (iflag & INPCK) + port->read_status_mask |= UART_RDS_FE_MSK | UART_RDS_PE_MSK; + if (iflag & (BRKINT | PARMRK)) + port->read_status_mask |= UART_RDS_BI_MSK; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (iflag & IGNPAR) + port->ignore_status_mask |= UART_RDS_FE_MSK | UART_RDS_PE_MSK; + if (iflag & IGNBRK) { + port->ignore_status_mask |= UART_RDS_BI_MSK; + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (iflag & IGNPAR) + port->ignore_status_mask |= UART_RDS_OE_MSK; + } + + /* first, disable everything */ + spin_lock_irqsave(&port->lock, flags); + old_ies = UART_GET_IES(port); + + if (UART_ENABLE_MS(port, cflag)) + old_ies |= UART_IES_ME_MSK; + + /* Set baud rate */ + UART_PUT_DIV_LO(port, (quot & 0xff)); + UART_PUT_DIV_HI(port, ((quot & 0xf00) >> 8)); + + UART_PUT_MC(port, uart_mc); + UART_PUT_IES(port, old_ies); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static int uart00_startup(struct uart_port *port) +{ + int result; + + /* + * Allocate the IRQ + */ + result = request_irq(port->irq, uart00_int, 0, "uart00", port); + if (result) { + printk(KERN_ERR "Request of irq %d failed\n", port->irq); + return result; + } + + /* + * Finally, enable interrupts. Use the TII interrupt to minimise + * the number of interrupts generated. If higher performance is + * needed, consider using the TI interrupt with a suitable FIFO + * threshold + */ + UART_PUT_IES(port, UART_IES_RE_MSK | UART_IES_TIE_MSK); + + return 0; +} + +static void uart00_shutdown(struct uart_port *port) +{ + /* + * disable all interrupts, disable the port + */ + UART_PUT_IEC(port, 0xff); + + /* disable break condition and fifos */ + UART_PUT_MCR(port, UART_GET_MCR(port) &~UART_MCR_BR_MSK); + + /* + * Free the interrupt + */ + free_irq(port->irq, port); +} + +static const char *uart00_type(struct uart_port *port) +{ + return port->type == PORT_UART00 ? "Altera UART00" : NULL; +} + +/* + * Release the memory region(s) being used by 'port' + */ +static void uart00_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, UART_PORT_SIZE); + +#ifdef CONFIG_ARCH_CAMELOT + if (port->membase != (void*)IO_ADDRESS(EXC_UART00_BASE)) { + iounmap(port->membase); + } +#endif +} + +/* + * Request the memory region(s) being used by 'port' + */ +static int uart00_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, UART_PORT_SIZE, "serial_uart00") + != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void uart00_config_port(struct uart_port *port, int flags) +{ + + /* + * Map the io memory if this is a soft uart + */ + if (!port->membase) + port->membase = ioremap_nocache(port->mapbase,SZ_4K); + + if (!port->membase) + printk(KERN_ERR "serial00: cannot map io memory\n"); + else + port->type = PORT_UART00; + +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int uart00_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_UART00) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= NR_IRQS) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops uart00_pops = { + tx_empty: uart00_tx_empty, + set_mctrl: uart00_set_mctrl_null, + get_mctrl: uart00_get_mctrl, + stop_tx: uart00_stop_tx, + start_tx: uart00_start_tx, + stop_rx: uart00_stop_rx, + enable_ms: uart00_enable_ms, + break_ctl: uart00_break_ctl, + startup: uart00_startup, + shutdown: uart00_shutdown, + change_speed: uart00_change_speed, + type: uart00_type, + release_port: uart00_release_port, + request_port: uart00_request_port, + config_port: uart00_config_port, + verify_port: uart00_verify_port, +}; + + +#ifdef CONFIG_ARCH_CAMELOT +static struct uart_port epxa10db_port = { + membase: (void*)IO_ADDRESS(EXC_UART00_BASE), + mapbase: EXC_UART00_BASE, + iotype: SERIAL_IO_MEM, + irq: IRQ_UART, + uartclk: EXC_AHB2_CLK_FREQUENCY, + fifosize: 16, + ops: &uart00_pops, + flags: ASYNC_BOOT_AUTOCONF, +}; +#endif + + +#ifdef CONFIG_SERIAL_UART00_CONSOLE +static void uart00_console_write(struct console *co, const char *s, unsigned count) +{ +#ifdef CONFIG_ARCH_CAMELOT + struct uart_port *port = &epxa10db_port; + unsigned int status, old_ies; + int i; + + /* + * First save the CR then disable the interrupts + */ + old_ies = UART_GET_IES(port); + UART_PUT_IEC(port,0xff); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = UART_GET_TSR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, s[i]); + if (s[i] == '\n') { + do { + status = UART_GET_TSR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, '\r'); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IES + */ + do { + status = UART_GET_TSR(port); + } while (status & UART_TSR_TX_LEVEL_MSK); + UART_PUT_IES(port, old_ies); +#endif +} + +static kdev_t uart00_console_device(struct console *co) +{ + return mk_kdev(SERIAL_UART00_MAJOR, SERIAL_UART00_MINOR + co->index); +} + +static void __init +uart00_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + unsigned int uart_mc, quot; + + uart_mc = UART_GET_MC(port); + + *parity = 'n'; + if (uart_mc & UART_MC_PE_MSK) { + if (uart_mc & UART_MC_EP_MSK) + *parity = 'e'; + else + *parity = 'o'; + } + + switch (uart_mc & UART_MC_CLS_MSK) { + case UART_MC_CLS_CHARLEN_5: + *bits = 5; + break; + case UART_MC_CLS_CHARLEN_6: + *bits = 6; + break; + case UART_MC_CLS_CHARLEN_7: + *bits = 7; + break; + case UART_MC_CLS_CHARLEN_8: + *bits = 8; + break; + } + quot = UART_GET_DIV_LO(port) | (UART_GET_DIV_HI(port) << 8); + *baud = port->uartclk / (16 *quot ); +} + +static int __init uart00_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + +#ifdef CONFIG_ARCH_CAMELOT + port = &epxa10db_port; ; +#else + return -ENODEV; +#endif + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + uart00_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console uart00_console = { + name: SERIAL_UART00_NAME, + write: uart00_console_write, + device: uart00_console_device, + setup: uart00_console_setup, + flags: CON_PRINTBUFFER, + index: 0, +}; + +void __init uart00_console_init(void) +{ + register_console(&uart00_console); +} + +#define UART00_CONSOLE &uart00_console +#else +#define UART00_CONSOLE NULL +#endif + +static struct uart_driver uart00_reg = { + owner: NULL, + driver_name: SERIAL_UART00_NAME, + dev_name: SERIAL_UART00_NAME, + major: SERIAL_UART00_MAJOR, + minor: SERIAL_UART00_MINOR, + nr: UART_NR, + cons: UART00_CONSOLE, +}; + +struct dev_port_entry{ + unsigned int base_addr; + struct uart_port *port; +}; + +static struct dev_port_entry dev_port_map[UART_NR]; + +#ifdef CONFIG_PLD_HOTSWAP +/* + * Keep a mapping of dev_info addresses -> port lines to use when + * removing ports dev==NULL indicates unused entry + */ + +struct uart00_ps_data{ + unsigned int clk; + unsigned int fifosize; +}; + +int uart00_add_device(struct pldhs_dev_info* dev_info, void* dev_ps_data) +{ + struct uart00_ps_data* dev_ps=dev_ps_data; + struct uart_port * port; + int i,result; + + i=0; + while(dev_port_map[i].port) + i++; + + if(i==UART_NR){ + printk(KERN_WARNING "uart00: Maximum number of ports reached\n"); + return 0; + } + + port=kmalloc(sizeof(struct uart_port),GFP_KERNEL); + if(!port) + return -ENOMEM; + + printk("clk=%d fifo=%d\n",dev_ps->clk,dev_ps->fifosize); + port->membase=0; + port->mapbase=dev_info->base_addr; + port->iotype=SERIAL_IO_MEM; + port->irq=dev_info->irq; + port->uartclk=dev_ps->clk; + port->fifosize=dev_ps->fifosize; + port->ops=&uart00_pops; + port->line=i; + port->flags=ASYNC_BOOT_AUTOCONF; + + result=uart_add_one_port(&uart00_reg, port); + if(result){ + printk("uart_add_one_port returned %d\n",result); + return result; + } + dev_port_map[i].base_addr=dev_info->base_addr; + dev_port_map[i].port=port; + printk("uart00: added device at %x as ttyUA%d\n",dev_port_map[i].base_addr,i); + return 0; + +} + +int uart00_remove_devices(void) +{ + int i,result; + + + result=0; + for(i=1;i<UART_NR;i++){ + if(dev_port_map[i].base_addr){ + result=uart_remove_one_port(&uart00_reg, dev_port_map[i].port); + if(result) + return result; + + /* port removed sucessfully, so now tidy up */ + kfree(dev_port_map[i].port); + dev_port_map[i].base_addr=0; + dev_port_map[i].port=NULL; + } + } + return 0; + +} + +struct pld_hotswap_ops uart00_pldhs_ops={ + name: "uart00", + add_device: uart00_add_device, + remove_devices:uart00_remove_devices, +}; + +#endif + +static int __init uart00_init(void) +{ + int result; + + printk(KERN_INFO "Serial: UART00 driver $Revision: 1.32 $\n"); + + printk(KERN_WARNING "serial_uart00:Using temporary major/minor pairs" + " - these WILL change in the future\n"); + + result = uart_register_driver(&uart00_reg); + if (result) + return result; +#ifdef CONFIG_ARCH_CAMELOT + result = uart_add_one_port(&uart00_reg,&epxa10db_port); +#endif + if (result) + uart_unregister_driver(&uart00_reg); + +#ifdef CONFIG_PLD_HOTSWAP + pldhs_register_driver(&uart00_pldhs_ops); +#endif + return result; +} + +__initcall(uart00_init); diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c index 65e4999ba5bf..07c5ea5b7e81 100644 --- a/drivers/usb/class/audio.c +++ b/drivers/usb/class/audio.c @@ -2091,11 +2091,11 @@ static int usb_audio_ioctl_mixdev(struct inode *inode, struct file *file, unsign } static /*const*/ struct file_operations usb_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: usb_audio_ioctl_mixdev, - open: usb_audio_open_mixdev, - release: usb_audio_release_mixdev, + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = usb_audio_ioctl_mixdev, + .open = usb_audio_open_mixdev, + .release = usb_audio_release_mixdev, }; /* --------------------------------------------------------------------- */ @@ -2727,15 +2727,15 @@ static int usb_audio_release(struct inode *inode, struct file *file) } static /*const*/ struct file_operations usb_audio_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: usb_audio_read, - write: usb_audio_write, - poll: usb_audio_poll, - ioctl: usb_audio_ioctl, - mmap: usb_audio_mmap, - open: usb_audio_open, - release: usb_audio_release, + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = usb_audio_read, + .write = usb_audio_write, + .poll = usb_audio_poll, + .ioctl = usb_audio_ioctl, + .mmap = usb_audio_mmap, + .open = usb_audio_open, + .release = usb_audio_release, }; /* --------------------------------------------------------------------- */ @@ -2753,11 +2753,11 @@ static struct usb_device_id usb_audio_ids [] = { MODULE_DEVICE_TABLE (usb, usb_audio_ids); static struct usb_driver usb_audio_driver = { - name: "audio", - probe: usb_audio_probe, - disconnect: usb_audio_disconnect, - driver_list: LIST_HEAD_INIT(usb_audio_driver.driver_list), - id_table: usb_audio_ids, + .name = "audio", + .probe = usb_audio_probe, + .disconnect = usb_audio_disconnect, + .driver_list = LIST_HEAD_INIT(usb_audio_driver.driver_list), + .id_table = usb_audio_ids, }; static void *find_descriptor(void *descstart, unsigned int desclen, void *after, diff --git a/drivers/usb/class/bluetty.c b/drivers/usb/class/bluetty.c index a2ad9ef139c7..8fae68841bbf 100644 --- a/drivers/usb/class/bluetty.c +++ b/drivers/usb/class/bluetty.c @@ -234,10 +234,10 @@ static struct usb_device_id usb_bluetooth_ids [] = { MODULE_DEVICE_TABLE (usb, usb_bluetooth_ids); static struct usb_driver usb_bluetooth_driver = { - name: "bluetty", - probe: usb_bluetooth_probe, - disconnect: usb_bluetooth_disconnect, - id_table: usb_bluetooth_ids, + .name = "bluetty", + .probe = usb_bluetooth_probe, + .disconnect = usb_bluetooth_disconnect, + .id_table = usb_bluetooth_ids, }; static int bluetooth_refcount; @@ -1284,30 +1284,30 @@ static void usb_bluetooth_disconnect(struct usb_device *dev, void *ptr) static struct tty_driver bluetooth_tty_driver = { - magic: TTY_DRIVER_MAGIC, - driver_name: "usb-bluetooth", - name: "usb/ttub/%d", - major: BLUETOOTH_TTY_MAJOR, - minor_start: 0, - num: BLUETOOTH_TTY_MINORS, - type: TTY_DRIVER_TYPE_SERIAL, - subtype: SERIAL_TYPE_NORMAL, - flags: TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, - - refcount: &bluetooth_refcount, - table: bluetooth_tty, - termios: bluetooth_termios, - termios_locked: bluetooth_termios_locked, - - open: bluetooth_open, - close: bluetooth_close, - write: bluetooth_write, - write_room: bluetooth_write_room, - ioctl: bluetooth_ioctl, - set_termios: bluetooth_set_termios, - throttle: bluetooth_throttle, - unthrottle: bluetooth_unthrottle, - chars_in_buffer: bluetooth_chars_in_buffer, + .magic = TTY_DRIVER_MAGIC, + .driver_name = "usb-bluetooth", + .name = "usb/ttub/%d", + .major = BLUETOOTH_TTY_MAJOR, + .minor_start = 0, + .num = BLUETOOTH_TTY_MINORS, + .type = TTY_DRIVER_TYPE_SERIAL, + .subtype = SERIAL_TYPE_NORMAL, + .flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, + + .refcount = &bluetooth_refcount, + .table = bluetooth_tty, + .termios = bluetooth_termios, + .termios_locked = bluetooth_termios_locked, + + .open = bluetooth_open, + .close = bluetooth_close, + .write = bluetooth_write, + .write_room = bluetooth_write_room, + .ioctl = bluetooth_ioctl, + .set_termios = bluetooth_set_termios, + .throttle = bluetooth_throttle, + .unthrottle = bluetooth_unthrottle, + .chars_in_buffer = bluetooth_chars_in_buffer, }; diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 0e1d275b53b1..8fc07f6fb5d7 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -683,10 +683,10 @@ static struct usb_device_id acm_ids[] = { MODULE_DEVICE_TABLE (usb, acm_ids); static struct usb_driver acm_driver = { - name: "acm", - probe: acm_probe, - disconnect: acm_disconnect, - id_table: acm_ids, + .name = "acm", + .probe = acm_probe, + .disconnect = acm_disconnect, + .id_table = acm_ids, }; /* @@ -700,32 +700,32 @@ static struct termios *acm_tty_termios[ACM_TTY_MINORS]; static struct termios *acm_tty_termios_locked[ACM_TTY_MINORS]; static struct tty_driver acm_tty_driver = { - magic: TTY_DRIVER_MAGIC, - driver_name: "acm", - name: "usb/acm/%d", - major: ACM_TTY_MAJOR, - minor_start: 0, - num: ACM_TTY_MINORS, - type: TTY_DRIVER_TYPE_SERIAL, - subtype: SERIAL_TYPE_NORMAL, - flags: TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, - - refcount: &acm_tty_refcount, - - table: acm_tty_table, - termios: acm_tty_termios, - termios_locked: acm_tty_termios_locked, - - open: acm_tty_open, - close: acm_tty_close, - write: acm_tty_write, - write_room: acm_tty_write_room, - ioctl: acm_tty_ioctl, - throttle: acm_tty_throttle, - unthrottle: acm_tty_unthrottle, - chars_in_buffer: acm_tty_chars_in_buffer, - break_ctl: acm_tty_break_ctl, - set_termios: acm_tty_set_termios + .magic = TTY_DRIVER_MAGIC, + .driver_name = "acm", + .name = "usb/acm/%d", + .major = ACM_TTY_MAJOR, + .minor_start = 0, + .num = ACM_TTY_MINORS, + .type = TTY_DRIVER_TYPE_SERIAL, + .subtype = SERIAL_TYPE_NORMAL, + .flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, + + .refcount = &acm_tty_refcount, + + .table = acm_tty_table, + .termios = acm_tty_termios, + .termios_locked = acm_tty_termios_locked, + + .open = acm_tty_open, + .close = acm_tty_close, + .write = acm_tty_write, + .write_room = acm_tty_write_room, + .ioctl = acm_tty_ioctl, + .throttle = acm_tty_throttle, + .unthrottle = acm_tty_unthrottle, + .chars_in_buffer = acm_tty_chars_in_buffer, + .break_ctl = acm_tty_break_ctl, + .set_termios = acm_tty_set_termios }; /* diff --git a/drivers/usb/class/printer.c b/drivers/usb/class/printer.c index cd9b52896fa5..b08c164dfd75 100644 --- a/drivers/usb/class/printer.c +++ b/drivers/usb/class/printer.c @@ -655,7 +655,10 @@ static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, (count - writecount) : USBLP_BUF_SIZE; if (copy_from_user(usblp->writeurb->transfer_buffer, buffer + writecount, - usblp->writeurb->transfer_buffer_length)) return -EFAULT; + usblp->writeurb->transfer_buffer_length)) { + up(&usblp->sem); + return writecount ? writecount : -EFAULT; + } usblp->writeurb->dev = usblp->dev; usblp->wcomplete = 0; @@ -783,13 +786,13 @@ static unsigned int usblp_quirks (__u16 vendor, __u16 product) } static struct file_operations usblp_fops = { - owner: THIS_MODULE, - read: usblp_read, - write: usblp_write, - poll: usblp_poll, - ioctl: usblp_ioctl, - open: usblp_open, - release: usblp_release, + .owner = THIS_MODULE, + .read = usblp_read, + .write = usblp_write, + .poll = usblp_poll, + .ioctl = usblp_ioctl, + .open = usblp_open, + .release = usblp_release, }; static void *usblp_probe(struct usb_device *dev, unsigned int ifnum, @@ -1097,11 +1100,11 @@ static struct usb_device_id usblp_ids [] = { MODULE_DEVICE_TABLE (usb, usblp_ids); static struct usb_driver usblp_driver = { - owner: THIS_MODULE, - name: "usblp", - probe: usblp_probe, - disconnect: usblp_disconnect, - id_table: usblp_ids, + .owner = THIS_MODULE, + .name = "usblp", + .probe = usblp_probe, + .disconnect = usblp_disconnect, + .id_table = usblp_ids, }; static int __init usblp_init(void) diff --git a/drivers/usb/class/usb-midi.c b/drivers/usb/class/usb-midi.c index 8aae77591839..cd35694b7d35 100644 --- a/drivers/usb/class/usb-midi.c +++ b/drivers/usb/class/usb-midi.c @@ -988,12 +988,12 @@ static int usb_midi_release(struct inode *inode, struct file *file) } static struct file_operations usb_midi_fops = { - llseek: usb_midi_llseek, - read: usb_midi_read, - write: usb_midi_write, - poll: usb_midi_poll, - open: usb_midi_open, - release: usb_midi_release, + .llseek = usb_midi_llseek, + .read = usb_midi_read, + .write = usb_midi_write, + .poll = usb_midi_poll, + .open = usb_midi_open, + .release = usb_midi_release, }; /* ------------------------------------------------------------------------- */ @@ -2095,11 +2095,11 @@ static void usb_midi_disconnect(struct usb_device *dev, void *ptr) static struct usb_driver usb_midi_driver = { - name: "midi", - probe: usb_midi_probe, - disconnect: usb_midi_disconnect, - id_table: NULL, /* check all devices */ - driver_list: LIST_HEAD_INIT(usb_midi_driver.driver_list) + .name = "midi", + .probe = usb_midi_probe, + .disconnect = usb_midi_disconnect, + .id_table = NULL, /* check all devices */ + .driver_list = LIST_HEAD_INIT(usb_midi_driver.driver_list) }; /* ------------------------------------------------------------------------- */ @@ -2168,15 +2168,15 @@ static void snd_usb_midi_output_trigger(snd_rawmidi_substream_t * substream, static snd_rawmidi_ops_t snd_usbmidi_output = { - open: snd_usbmidi_output_open, - close: snd_usbmidi_output_close, - trigger: snd_usbmidi_output_trigger, + .open = snd_usbmidi_output_open, + .close = snd_usbmidi_output_close, + .trigger = snd_usbmidi_output_trigger, }; static snd_rawmidi_ops_t snd_usbmidi_input = { - open: snd_usbmidi_input_open, - close: snd_usbmidi_input_close, - trigger: snd_usbmidi_input_trigger, + .open = snd_usbmidi_input_open, + .close = snd_usbmidi_input_close, + .trigger = snd_usbmidi_input_trigger, }; int snd_usbmidi_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rrawmidi) @@ -2211,7 +2211,7 @@ int snd_usbmidi_create( snd_card_t * card, int err, idx; snd_region_t *region; static snd_device_opt_t ops = { - dev_free: snd_usbmidi_dev_free, + .dev_free = snd_usbmidi_dev_free, }; *rchip = NULL; diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index c15f5f4e60b2..e701a4975071 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -152,8 +152,8 @@ static const struct class_info clas_info[] = void usbdevfs_conn_disc_event(void) { - wake_up(&deviceconndiscwq); conndiscevcnt++; + wake_up(&deviceconndiscwq); } static const char *class_decode(const int class) @@ -239,6 +239,7 @@ static char *usb_dump_interface_descriptor(char *start, char *end, const struct if (start > end) return start; + lock_kernel(); /* driver might be unloaded */ start += sprintf(start, format_iface, desc->bInterfaceNumber, desc->bAlternateSetting, @@ -248,6 +249,7 @@ static char *usb_dump_interface_descriptor(char *start, char *end, const struct desc->bInterfaceSubClass, desc->bInterfaceProtocol, iface->driver ? iface->driver->name : "(none)"); + unlock_kernel(); return start; } @@ -597,6 +599,13 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct unlock_kernel(); return POLLIN; } + + /* we may have dropped BKL - need to check for having lost the race */ + if (file->private_data) { + kfree(st); + goto lost_race; + } + /* * need to prevent the module from being unloaded, since * proc_unregister does not call the release method and @@ -606,6 +615,7 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct file->private_data = st; mask = POLLIN; } +lost_race: if (file->f_mode & FMODE_READ) poll_wait(file, &deviceconndiscwq, wait); if (st->lastev != conndiscevcnt) @@ -656,9 +666,9 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig) } struct file_operations usbdevfs_devices_fops = { - llseek: usb_device_lseek, - read: usb_device_read, - poll: usb_device_poll, - open: usb_device_open, - release: usb_device_release, + .llseek = usb_device_lseek, + .read = usb_device_read, + .poll = usb_device_poll, + .open = usb_device_open, + .release = usb_device_release, }; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index b7c95b292a48..e80a3ed170f8 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -324,9 +324,9 @@ static void driver_disconnect(struct usb_device *dev, void *context) } struct usb_driver usbdevfs_driver = { - name: "usbfs", - probe: driver_probe, - disconnect: driver_disconnect, + .name = "usbfs", + .probe = driver_probe, + .disconnect = driver_disconnect, }; static int claimintf(struct dev_state *ps, unsigned int intf) @@ -361,14 +361,14 @@ static int releaseintf(struct dev_state *ps, unsigned int intf) if (intf >= 8*sizeof(ps->ifclaimed)) return -EINVAL; err = -EINVAL; - lock_kernel(); dev = ps->dev; + down(&dev->serialize); if (dev && test_and_clear_bit(intf, &ps->ifclaimed)) { iface = &dev->actconfig->interface[intf]; usb_driver_release_interface(&usbdevfs_driver, iface); err = 0; } - unlock_kernel(); + up(&dev->serialize); return err; } @@ -722,14 +722,11 @@ static int proc_resetdevice(struct dev_state *ps) if (test_bit(i, &ps->ifclaimed)) continue; - if (intf->driver) { - const struct usb_device_id *id; - down(&intf->driver->serialize); - intf->driver->disconnect(ps->dev, intf->private_data); - id = usb_match_id(ps->dev,intf,intf->driver->id_table); - intf->driver->probe(ps->dev, i, id); - up(&intf->driver->serialize); + lock_kernel(); + if (intf->driver && ps->dev) { + usb_bind_driver(intf->driver,ps->dev, i); } + unlock_kernel(); } return 0; @@ -1092,16 +1089,17 @@ static int proc_ioctl (struct dev_state *ps, void *arg) /* disconnect kernel driver from interface, leaving it unbound. */ case USBDEVFS_DISCONNECT: + /* this function is voodoo. without locking it is a maybe thing */ + lock_kernel(); driver = ifp->driver; if (driver) { - down (&driver->serialize); dbg ("disconnect '%s' from dev %d interface %d", driver->name, ps->dev->devnum, ctrl.ifno); - driver->disconnect (ps->dev, ifp->private_data); + usb_unbind_driver(ps->dev, ifp); usb_driver_release_interface (driver, ifp); - up (&driver->serialize); } else retval = -EINVAL; + unlock_kernel(); break; /* let kernel drivers try to (re)bind to the interface */ @@ -1111,18 +1109,28 @@ static int proc_ioctl (struct dev_state *ps, void *arg) /* talk directly to the interface's driver */ default: + lock_kernel(); /* against module unload */ driver = ifp->driver; - if (driver == 0 || driver->ioctl == 0) - retval = -ENOSYS; - else { - if (ifp->driver->owner) + if (driver == 0 || driver->ioctl == 0) { + unlock_kernel(); + retval = -ENOSYS; + } else { + if (ifp->driver->owner) { __MOD_INC_USE_COUNT(ifp->driver->owner); + unlock_kernel(); + } /* ifno might usefully be passed ... */ retval = driver->ioctl (ps->dev, ctrl.ioctl_code, buf); /* size = min_t(int, size, retval)? */ - if (ifp->driver->owner) + if (ifp->driver->owner) { __MOD_DEC_USE_COUNT(ifp->driver->owner); + } else { + unlock_kernel(); + } } + + if (retval == -ENOIOCTLCMD) + retval = -ENOTTY; } /* cleanup and return */ @@ -1139,7 +1147,7 @@ static int proc_ioctl (struct dev_state *ps, void *arg) static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct dev_state *ps = (struct dev_state *)file->private_data; - int ret = -ENOIOCTLCMD; + int ret = -ENOTTY; if (!(file->f_mode & FMODE_WRITE)) return -EPERM; @@ -1248,10 +1256,10 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai } struct file_operations usbdevfs_device_file_operations = { - llseek: usbdev_lseek, - read: usbdev_read, - poll: usbdev_poll, - ioctl: usbdev_ioctl, - open: usbdev_open, - release: usbdev_release, + .llseek = usbdev_lseek, + .read = usbdev_read, + .poll = usbdev_poll, + .ioctl = usbdev_ioctl, + .open = usbdev_open, + .release = usbdev_release, }; diff --git a/drivers/usb/core/drivers.c b/drivers/usb/core/drivers.c index 3419792c7fb6..22dc21354868 100644 --- a/drivers/usb/core/drivers.c +++ b/drivers/usb/core/drivers.c @@ -66,6 +66,7 @@ static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff start = page; end = page + (PAGE_SIZE - 100); pos = *ppos; + lock_kernel(); /* else drivers might be unloaded */ for (; tmp != &usb_driver_list; tmp = tmp->next) { struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list); int minor = driver->fops ? driver->minor : -1; @@ -80,6 +81,7 @@ static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff break; } } + unlock_kernel(); if (start == page) start += sprintf(start, "(none)\n"); len = start - page; @@ -120,6 +122,6 @@ static loff_t usb_driver_lseek(struct file * file, loff_t offset, int orig) } struct file_operations usbdevfs_drivers_fops = { - llseek: usb_driver_lseek, - read: usb_driver_read, + .llseek = usb_driver_lseek, + .read = usb_driver_read, }; diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index eba43f1d84e5..803fb19e5ad2 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -44,10 +44,13 @@ static int usb_open(struct inode * inode, struct file * file) spin_lock (&minor_lock); c = usb_minors[minor]; - spin_unlock (&minor_lock); - if (!c || !(new_fops = fops_get(c))) + if (!c || !(new_fops = fops_get(c))) { + spin_unlock(&minor_lock); return err; + } + spin_unlock(&minor_lock); + old_fops = file->f_op; file->f_op = new_fops; /* Curiouser and curiouser... NULL ->open() as "no device" ? */ @@ -62,8 +65,8 @@ static int usb_open(struct inode * inode, struct file * file) } static struct file_operations usb_fops = { - owner: THIS_MODULE, - open: usb_open, + .owner = THIS_MODULE, + .open = usb_open, }; int usb_major_init(void) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 15a2c346b4af..05bb930ea5fd 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1240,11 +1240,11 @@ static int hcd_free_dev (struct usb_device *udev) * bus glue for non-PCI system busses will need to use this. */ struct usb_operations usb_hcd_operations = { - allocate: hcd_alloc_dev, - get_frame_number: hcd_get_frame_number, - submit_urb: hcd_submit_urb, - unlink_urb: hcd_unlink_urb, - deallocate: hcd_free_dev, + .allocate = hcd_alloc_dev, + .get_frame_number = hcd_get_frame_number, + .submit_urb = hcd_submit_urb, + .unlink_urb = hcd_unlink_urb, + .deallocate = hcd_free_dev, }; EXPORT_SYMBOL (usb_hcd_operations); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index ad1422fabdfa..f80a98621d26 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1046,8 +1046,6 @@ static void usb_hub_events(void) static int usb_hub_thread(void *__hub) { - lock_kernel(); - /* * This thread doesn't need any user-level access, * so get rid of all our resources @@ -1067,8 +1065,6 @@ static int usb_hub_thread(void *__hub) } while (!signal_pending(current)); dbg("usb_hub_thread exiting"); - - unlock_kernel(); complete_and_exit(&khubd_exited, 0); } @@ -1083,11 +1079,11 @@ static struct usb_device_id hub_id_table [] = { MODULE_DEVICE_TABLE (usb, hub_id_table); static struct usb_driver hub_driver = { - name: "hub", - probe: hub_probe, - ioctl: hub_ioctl, - disconnect: hub_disconnect, - id_table: hub_id_table, + .name = "hub", + .probe = hub_probe, + .ioctl = hub_ioctl, + .disconnect = hub_disconnect, + .id_table = hub_id_table, }; /* diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 08d08e3516e8..ffb7e6e13284 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -284,23 +284,23 @@ static int default_open (struct inode *inode, struct file *filp) } static struct file_operations default_file_operations = { - read: default_read_file, - write: default_write_file, - open: default_open, - llseek: default_file_lseek, + .read = default_read_file, + .write = default_write_file, + .open = default_open, + .llseek = default_file_lseek, }; static struct inode_operations usbfs_dir_inode_operations = { - create: usbfs_create, - lookup: simple_lookup, - unlink: usbfs_unlink, - mkdir: usbfs_mkdir, - rmdir: usbfs_rmdir, + .create = usbfs_create, + .lookup = simple_lookup, + .unlink = usbfs_unlink, + .mkdir = usbfs_mkdir, + .rmdir = usbfs_rmdir, }; static struct super_operations usbfs_ops = { - statfs: simple_statfs, - drop_inode: generic_delete_inode, + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, }; static int usbfs_fill_super(struct super_block *sb, void *data, int silent) @@ -468,17 +468,17 @@ static struct super_block *usb_get_sb(struct file_system_type *fs_type, } static struct file_system_type usbdevice_fs_type = { - owner: THIS_MODULE, - name: "usbdevfs", - get_sb: usb_get_sb, - kill_sb: kill_anon_super, + .owner = THIS_MODULE, + .name = "usbdevfs", + .get_sb = usb_get_sb, + .kill_sb = kill_anon_super, }; static struct file_system_type usb_fs_type = { - owner: THIS_MODULE, - name: "usbfs", - get_sb: usb_get_sb, - kill_sb: kill_anon_super, + .owner = THIS_MODULE, + .name = "usbfs", + .get_sb = usb_get_sb, + .kill_sb = kill_anon_super, }; /* --------------------------------------------------------------------- */ diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 55c9e8955d24..ed2e03089e57 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -32,6 +32,7 @@ #include <linux/init.h> #include <linux/spinlock.h> #include <linux/errno.h> +#include <linux/smp_lock.h> #ifdef CONFIG_USB_DEBUG #define DEBUG @@ -117,6 +118,108 @@ void usb_scan_devices(void) up (&usb_bus_list_lock); } +/** + * usb_unbind_driver - disconnects a driver from a device + * @device: usb device to be disconnected + * @intf: interface of the device to be disconnected + * Context: BKL held + * + * Handles module usage count correctly + */ + +void usb_unbind_driver(struct usb_device *device, struct usb_interface *intf) +{ + struct usb_driver *driver; + void *priv; + int m; + + + driver = intf->driver; + priv = intf->private_data; + + if (!driver) + return; + + /* as soon as we increase the module use count we drop the BKL + before that we must not sleep */ + if (driver->owner) { + m = try_inc_mod_count(driver->owner); + if (m == 0) { + err("Dieing driver still bound to device.\n"); + return; + } + unlock_kernel(); + } + down(&driver->serialize); /* if we sleep here on an umanaged driver + the holder of the lock guards against + module unload */ + + driver->disconnect(device, priv); + + up(&driver->serialize); + if (driver->owner) { + lock_kernel(); + __MOD_DEC_USE_COUNT(driver->owner); + } +} + +/** + * usb_bind_driver - connect a driver to a device's interface + * @driver: device driver to be bound to a devices interface + * @dev: device to be bound + * @ifnum: index number of the interface to be used + * + * Does a save binding of a driver to a device's interface + * Returns a pointer to the drivers private description of the binding + */ + +void *usb_bind_driver(struct usb_driver *driver, struct usb_device *dev, unsigned int ifnum) +{ + int i,m; + void *private = NULL; + const struct usb_device_id *id; + struct usb_interface *interface; + + if (driver->owner) { + m = try_inc_mod_count(driver->owner); + if (m == 0) + return NULL; /* this horse is dead - don't ride*/ + unlock_kernel(); + } + + interface = &dev->actconfig->interface[ifnum]; + + id = driver->id_table; + /* new style driver? */ + if (id) { + for (i = 0; i < interface->num_altsetting; i++) { + interface->act_altsetting = i; + id = usb_match_id(dev, interface, id); + if (id) { + down(&driver->serialize); + private = driver->probe(dev,ifnum,id); + up(&driver->serialize); + if (private != NULL) + break; + } + } + + /* if driver not bound, leave defaults unchanged */ + if (private == NULL) + interface->act_altsetting = 0; + } else { /* "old style" driver */ + down(&driver->serialize); + private = driver->probe(dev, ifnum, NULL); + up(&driver->serialize); + } + if (driver->owner) { + lock_kernel(); + __MOD_DEC_USE_COUNT(driver->owner); + } + + return private; +} + /* * This function is part of a depth-first search down the device tree, * removing any instances of a device driver. @@ -136,18 +239,12 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev) if (!dev->actconfig) return; - + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { struct usb_interface *interface = &dev->actconfig->interface[i]; - + if (interface->driver == driver) { - if (driver->owner) - __MOD_INC_USE_COUNT(driver->owner); - down(&driver->serialize); - driver->disconnect(dev, interface->private_data); - up(&driver->serialize); - if (driver->owner) - __MOD_DEC_USE_COUNT(driver->owner); + usb_unbind_driver(dev, interface); /* if driver->disconnect didn't release the interface */ if (interface->driver) usb_driver_release_interface(driver, interface); @@ -163,7 +260,7 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev) /** * usb_deregister - unregister a USB driver * @driver: USB operations of the driver to unregister - * Context: !in_interrupt () + * Context: !in_interrupt (), must be called with BKL held * * Unlinks the specified driver from the internal USB driver list. * @@ -528,9 +625,7 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) struct list_head *tmp; struct usb_interface *interface; void *private; - const struct usb_device_id *id; struct usb_driver *driver; - int i; if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) { err("bad find_interface_driver params"); @@ -545,37 +640,12 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) goto out_err; private = NULL; + lock_kernel(); for (tmp = usb_driver_list.next; tmp != &usb_driver_list;) { driver = list_entry(tmp, struct usb_driver, driver_list); tmp = tmp->next; - if (driver->owner) - __MOD_INC_USE_COUNT(driver->owner); - id = driver->id_table; - /* new style driver? */ - if (id) { - for (i = 0; i < interface->num_altsetting; i++) { - interface->act_altsetting = i; - id = usb_match_id(dev, interface, id); - if (id) { - down(&driver->serialize); - private = driver->probe(dev,ifnum,id); - up(&driver->serialize); - if (private != NULL) - break; - } - } - - /* if driver not bound, leave defaults unchanged */ - if (private == NULL) - interface->act_altsetting = 0; - } else { /* "old style" driver */ - down(&driver->serialize); - private = driver->probe(dev, ifnum, NULL); - up(&driver->serialize); - } - if (driver->owner) - __MOD_DEC_USE_COUNT(driver->owner); + private = usb_bind_driver(driver, dev, ifnum); /* probe() may have changed the config on us */ interface = dev->actconfig->interface + ifnum; @@ -583,9 +653,11 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) if (private) { usb_driver_claim_interface(driver, interface, private); up(&dev->serialize); + unlock_kernel(); return 0; } } + unlock_kernel(); out_err: up(&dev->serialize); @@ -764,9 +836,9 @@ show_config (struct device *dev, char *buf, size_t count, loff_t off) return sprintf (buf, "%u\n", udev->actconfig->bConfigurationValue); } static struct driver_file_entry usb_config_entry = { - name: "configuration", - mode: S_IRUGO, - show: show_config, + .name = "configuration", + .mode = S_IRUGO, + .show = show_config, }; /* interfaces have one current setting; alternates @@ -783,9 +855,9 @@ show_altsetting (struct device *dev, char *buf, size_t count, loff_t off) return sprintf (buf, "%u\n", interface->altsetting->bAlternateSetting); } static struct driver_file_entry usb_altsetting_entry = { - name: "altsetting", - mode: S_IRUGO, - show: show_altsetting, + .name = "altsetting", + .mode = S_IRUGO, + .show = show_altsetting, }; /* product driverfs file */ @@ -804,9 +876,9 @@ static ssize_t show_product (struct device *dev, char *buf, size_t count, loff_t return len+1; } static struct driver_file_entry usb_product_entry = { - name: "product", - mode: S_IRUGO, - show: show_product, + .name = "product", + .mode = S_IRUGO, + .show = show_product, }; /* manufacturer driverfs file */ @@ -826,9 +898,9 @@ show_manufacturer (struct device *dev, char *buf, size_t count, loff_t off) return len+1; } static struct driver_file_entry usb_manufacturer_entry = { - name: "manufacturer", - mode: S_IRUGO, - show: show_manufacturer, + .name = "manufacturer", + .mode = S_IRUGO, + .show = show_manufacturer, }; /* serial number driverfs file */ @@ -848,9 +920,9 @@ show_serial (struct device *dev, char *buf, size_t count, loff_t off) return len+1; } static struct driver_file_entry usb_serial_entry = { - name: "serial", - mode: S_IRUGO, - show: show_serial, + .name = "serial", + .mode = S_IRUGO, + .show = show_serial, }; /* @@ -1121,27 +1193,22 @@ void usb_disconnect(struct usb_device **pdev) info("USB disconnect on device %d", dev->devnum); + lock_kernel(); if (dev->actconfig) { for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { struct usb_interface *interface = &dev->actconfig->interface[i]; struct usb_driver *driver = interface->driver; if (driver) { - if (driver->owner) - __MOD_INC_USE_COUNT(driver->owner); - down(&driver->serialize); - driver->disconnect(dev, interface->private_data); - up(&driver->serialize); + usb_unbind_driver(dev, interface); /* if driver->disconnect didn't release the interface */ if (interface->driver) usb_driver_release_interface(driver, interface); - /* we don't need the driver any longer */ - if (driver->owner) - __MOD_DEC_USE_COUNT(driver->owner); } /* remove our device node for this interface */ put_device(&interface->dev); } } + unlock_kernel(); /* Free up all the children.. */ for (i = 0; i < USB_MAXCHILDREN; i++) { @@ -1416,7 +1483,7 @@ struct list_head *usb_bus_get_list(void) #endif struct bus_type usb_bus_type = { - name: "usb", + .name = "usb", }; /* @@ -1475,6 +1542,8 @@ EXPORT_SYMBOL(usb_new_device); EXPORT_SYMBOL(usb_reset_device); EXPORT_SYMBOL(usb_connect); EXPORT_SYMBOL(usb_disconnect); +EXPORT_SYMBOL(usb_bind_driver); +EXPORT_SYMBOL(usb_unbind_driver); EXPORT_SYMBOL(__usb_get_extra_descriptor); diff --git a/drivers/usb/host/Config.in b/drivers/usb/host/Config.in index dbfb819f8d33..0a6f97342a77 100644 --- a/drivers/usb/host/Config.in +++ b/drivers/usb/host/Config.in @@ -6,6 +6,5 @@ dep_tristate ' EHCI HCD (USB 2.0) support' CONFIG_USB_EHCI_HCD $CONFIG_USB dep_tristate ' OHCI HCD support' CONFIG_USB_OHCI_HCD $CONFIG_USB dep_tristate ' UHCI HCD (most Intel and VIA) support' CONFIG_USB_UHCI_HCD_ALT $CONFIG_USB if [ "$CONFIG_ARM" = "y" ]; then - dep_tristate ' SA1111 OHCI-compatible host interface support' CONFIG_USB_OHCI_SA1111 $CONFIG_USB dep_tristate ' SL811HS support' CONFIG_USB_SL811HS $CONFIG_USB fi diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 85b355751fa2..32d1f6086dc1 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -9,8 +9,6 @@ obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD_ALT) += uhci-hcd.o -obj-$(CONFIG_USB_OHCI) += usb-ohci.o usb-ohci-pci.o -obj-$(CONFIG_USB_OHCI_SA1111) += usb-ohci.o usb-ohci-sa1111.o obj-$(CONFIG_USB_SL811HS) += hc_sl811.o include $(TOPDIR)/Rules.make diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index fd9bbb044010..74496b06a130 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -702,47 +702,47 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) static const char hcd_name [] = "ehci-hcd"; static const struct hc_driver ehci_driver = { - description: hcd_name, + .description = hcd_name, /* * generic hardware linkage */ - irq: ehci_irq, - flags: HCD_MEMORY | HCD_USB2, + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, /* * basic lifecycle operations */ - start: ehci_start, + .start = ehci_start, #ifdef CONFIG_PM - suspend: ehci_suspend, - resume: ehci_resume, + .suspend = ehci_suspend, + .resume = ehci_resume, #endif - stop: ehci_stop, + .stop = ehci_stop, /* * memory lifecycle (except per-request) */ - hcd_alloc: ehci_hcd_alloc, - hcd_free: ehci_hcd_free, + .hcd_alloc = ehci_hcd_alloc, + .hcd_free = ehci_hcd_free, /* * managing i/o requests and associated device resources */ - urb_enqueue: ehci_urb_enqueue, - urb_dequeue: ehci_urb_dequeue, - free_config: ehci_free_config, + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .free_config = ehci_free_config, /* * scheduling support */ - get_frame_number: ehci_get_frame, + .get_frame_number = ehci_get_frame, /* * root hub support */ - hub_status_data: ehci_hub_status_data, - hub_control: ehci_hub_control, + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, }; /*-------------------------------------------------------------------------*/ @@ -754,15 +754,15 @@ static const struct pci_device_id __devinitdata pci_ids [] = { { /* handle any USB 2.0 EHCI controller */ - class: ((PCI_CLASS_SERIAL_USB << 8) | 0x20), - class_mask: ~0, - driver_data: (unsigned long) &ehci_driver, + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0x20), + .class_mask = ~0, + .driver_data = (unsigned long) &ehci_driver, /* no matter who makes it */ - vendor: PCI_ANY_ID, - device: PCI_ANY_ID, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, }, { /* end: all zeroes */ } }; @@ -770,15 +770,15 @@ MODULE_DEVICE_TABLE (pci, pci_ids); /* pci driver glue; this is a "new style" PCI driver module */ static struct pci_driver ehci_pci_driver = { - name: (char *) hcd_name, - id_table: pci_ids, + .name = (char *) hcd_name, + .id_table = pci_ids, - probe: usb_hcd_pci_probe, - remove: usb_hcd_pci_remove, + .probe = usb_hcd_pci_probe, + .remove = usb_hcd_pci_remove, #ifdef CONFIG_PM - suspend: usb_hcd_pci_suspend, - resume: usb_hcd_pci_resume, + .suspend = usb_hcd_pci_suspend, + .resume = usb_hcd_pci_resume, #endif }; diff --git a/drivers/usb/host/hc_simple.c b/drivers/usb/host/hc_simple.c index 3a7218442adb..570267835101 100644 --- a/drivers/usb/host/hc_simple.c +++ b/drivers/usb/host/hc_simple.c @@ -343,11 +343,11 @@ static int hci_get_current_frame_number (struct usb_device *usb_dev) **************************************************************************/ static struct usb_operations hci_device_operations = { - allocate: hci_alloc_dev, - deallocate: hci_free_dev, - get_frame_number: hci_get_current_frame_number, - submit_urb: hci_submit_urb, - unlink_urb: hci_unlink_urb, + .allocate = hci_alloc_dev, + .deallocate = hci_free_dev, + .get_frame_number = hci_get_current_frame_number, + .submit_urb = hci_submit_urb, + .unlink_urb = hci_unlink_urb, }; /*************************************************************************** diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index a56ae64e60cc..2ee09b0a3bcd 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -167,7 +167,7 @@ static int ohci_urb_enqueue ( else if ((urb->transfer_flags & USB_ZERO_PACKET) != 0 && (urb->transfer_buffer_length % usb_maxpacket (urb->dev, pipe, - usb_pipeout (pipe))) != 0) + usb_pipeout (pipe))) == 0) size++; break; case PIPE_ISOCHRONOUS: /* number of packets from URB */ diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 347dc5d34b0c..a1979e532787 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -284,47 +284,47 @@ dbg ("sleeping = %d, disabled = %d", ohci->sleeping, ohci->disabled); /*-------------------------------------------------------------------------*/ static const struct hc_driver ohci_pci_hc_driver = { - description: hcd_name, + .description = hcd_name, /* * generic hardware linkage */ - irq: ohci_irq, - flags: HCD_MEMORY | HCD_USB11, + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_USB11, /* * basic lifecycle operations */ - start: ohci_pci_start, + .start = ohci_pci_start, #ifdef CONFIG_PM - suspend: ohci_pci_suspend, - resume: ohci_pci_resume, + .suspend = ohci_pci_suspend, + .resume = ohci_pci_resume, #endif - stop: ohci_stop, + .stop = ohci_stop, /* * memory lifecycle (except per-request) */ - hcd_alloc: ohci_hcd_alloc, - hcd_free: ohci_hcd_free, + .hcd_alloc = ohci_hcd_alloc, + .hcd_free = ohci_hcd_free, /* * managing i/o requests and associated device resources */ - urb_enqueue: ohci_urb_enqueue, - urb_dequeue: ohci_urb_dequeue, - free_config: ohci_free_config, + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .free_config = ohci_free_config, /* * scheduling support */ - get_frame_number: ohci_get_frame, + .get_frame_number = ohci_get_frame, /* * root hub support */ - hub_status_data: ohci_hub_status_data, - hub_control: ohci_hub_control, + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, }; /*-------------------------------------------------------------------------*/ @@ -333,15 +333,15 @@ static const struct hc_driver ohci_pci_hc_driver = { static const struct pci_device_id __devinitdata pci_ids [] = { { /* handle any USB OHCI controller */ - class: (PCI_CLASS_SERIAL_USB << 8) | 0x10, - class_mask: ~0, - driver_data: (unsigned long) &ohci_pci_hc_driver, + .class = (PCI_CLASS_SERIAL_USB << 8) | 0x10, + .class_mask = ~0, + .driver_data = (unsigned long) &ohci_pci_hc_driver, /* no matter who makes it */ - vendor: PCI_ANY_ID, - device: PCI_ANY_ID, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, }, { /* end: all zeroes */ } }; @@ -349,15 +349,15 @@ MODULE_DEVICE_TABLE (pci, pci_ids); /* pci driver glue; this is a "new style" PCI driver module */ static struct pci_driver ohci_pci_driver = { - name: (char *) hcd_name, - id_table: pci_ids, + .name = (char *) hcd_name, + .id_table = pci_ids, - probe: usb_hcd_pci_probe, - remove: usb_hcd_pci_remove, + .probe = usb_hcd_pci_probe, + .remove = usb_hcd_pci_remove, #ifdef CONFIG_PM - suspend: usb_hcd_pci_suspend, - resume: usb_hcd_pci_resume, + .suspend = usb_hcd_pci_suspend, + .resume = usb_hcd_pci_resume, #endif }; diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index 95fc8b82af57..05c2127aa57f 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -288,47 +288,47 @@ ohci_sa1111_start (struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ static const struct hc_driver ohci_sa1111_hc_driver = { - description: hcd_name, + .description = hcd_name, /* * generic hardware linkage */ - irq: ohci_irq, - flags: HCD_USB11, + .irq = ohci_irq, + .flags = HCD_USB11, /* * basic lifecycle operations */ - start: ohci_sa1111_start, + .start = ohci_sa1111_start, #ifdef CONFIG_PM /* suspend: ohci_sa1111_suspend, -- tbd */ /* resume: ohci_sa1111_resume, -- tbd */ #endif - stop: ohci_stop, + .stop = ohci_stop, /* * memory lifecycle (except per-request) */ - hcd_alloc: ohci_hcd_alloc, - hcd_free: ohci_hcd_free, + .hcd_alloc = ohci_hcd_alloc, + .hcd_free = ohci_hcd_free, /* * managing i/o requests and associated device resources */ - urb_enqueue: ohci_urb_enqueue, - urb_dequeue: ohci_urb_dequeue, - free_config: ohci_free_config, + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .free_config = ohci_free_config, /* * scheduling support */ - get_frame_number: ohci_get_frame, + .get_frame_number = ohci_get_frame, /* * root hub support */ - hub_status_data: ohci_hub_status_data, - hub_control: ohci_hub_control, + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 2c27eae0687b..b7817920c542 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -571,11 +571,11 @@ static int uhci_proc_release(struct inode *inode, struct file *file) } static struct file_operations uhci_proc_operations = { - open: uhci_proc_open, - llseek: uhci_proc_lseek, - read: uhci_proc_read, + .open = uhci_proc_open, + .llseek = uhci_proc_lseek, + .read = uhci_proc_read, // write: uhci_proc_write, - release: uhci_proc_release, + .release = uhci_proc_release, }; #endif diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index b077975c6e22..69cf61862f85 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -2476,45 +2476,45 @@ static int uhci_hcd_get_frame_number(struct usb_hcd *hcd) static const char hcd_name[] = "uhci-hcd"; static const struct hc_driver uhci_driver = { - description: hcd_name, + .description = hcd_name, /* Generic hardware linkage */ - irq: uhci_irq, - flags: HCD_USB11, + .irq = uhci_irq, + .flags = HCD_USB11, /* Basic lifecycle operations */ - start: uhci_start, + .start = uhci_start, #ifdef CONFIG_PM - suspend: uhci_suspend, - resume: uhci_resume, + .suspend = uhci_suspend, + .resume = uhci_resume, #endif - stop: uhci_stop, + .stop = uhci_stop, - hcd_alloc: uhci_hcd_alloc, - hcd_free: uhci_hcd_free, + .hcd_alloc = uhci_hcd_alloc, + .hcd_free = uhci_hcd_free, - urb_enqueue: uhci_urb_enqueue, - urb_dequeue: uhci_urb_dequeue, - free_config: NULL, + .urb_enqueue = uhci_urb_enqueue, + .urb_dequeue = uhci_urb_dequeue, + .free_config = NULL, - get_frame_number: uhci_hcd_get_frame_number, + .get_frame_number = uhci_hcd_get_frame_number, - hub_status_data: uhci_hub_status_data, - hub_control: uhci_hub_control, + .hub_status_data = uhci_hub_status_data, + .hub_control = uhci_hub_control, }; static const struct pci_device_id __devinitdata uhci_pci_ids[] = { { /* handle any USB UHCI controller */ - class: ((PCI_CLASS_SERIAL_USB << 8) | 0x00), - class_mask: ~0, - driver_data: (unsigned long) &uhci_driver, + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0x00), + .class_mask = ~0, + .driver_data = (unsigned long) &uhci_driver, /* no matter who makes it */ - vendor: PCI_ANY_ID, - device: PCI_ANY_ID, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, }, { /* end: all zeroes */ } }; @@ -2522,15 +2522,15 @@ static const struct pci_device_id __devinitdata uhci_pci_ids[] = { { MODULE_DEVICE_TABLE(pci, uhci_pci_ids); static struct pci_driver uhci_pci_driver = { - name: (char *)hcd_name, - id_table: uhci_pci_ids, + .name = (char *)hcd_name, + .id_table = uhci_pci_ids, - probe: usb_hcd_pci_probe, - remove: usb_hcd_pci_remove, + .probe = usb_hcd_pci_probe, + .remove = usb_hcd_pci_remove, #ifdef CONFIG_PM - suspend: usb_hcd_pci_suspend, - resume: usb_hcd_pci_resume, + .suspend = usb_hcd_pci_suspend, + .resume = usb_hcd_pci_resume, #endif /* PM */ }; diff --git a/drivers/usb/host/usb-ohci-pci.c b/drivers/usb/host/usb-ohci-pci.c deleted file mode 100644 index ad3958c025d9..000000000000 --- a/drivers/usb/host/usb-ohci-pci.c +++ /dev/null @@ -1,456 +0,0 @@ -#include <linux/config.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/interrupt.h> /* for in_interrupt() */ -#undef DEBUG -#include <linux/usb.h> - -#include "../core/hcd.h" -#include "usb-ohci.h" - -#ifdef CONFIG_PMAC_PBOOK -#include <asm/machdep.h> -#include <asm/pmac_feature.h> -#include <asm/pci-bridge.h> -#ifndef CONFIG_PM -#define CONFIG_PM -#endif -#endif - -int __devinit -hc_add_ohci(struct pci_dev *dev, int irq, void *membase, unsigned long flags, - ohci_t **ohci, const char *name, const char *slot_name); -extern void hc_remove_ohci(ohci_t *ohci); -extern int hc_start (ohci_t * ohci, struct device *parent_dev); -extern int hc_reset (ohci_t * ohci); - -/*-------------------------------------------------------------------------*/ - -/* Increment the module usage count, start the control thread and - * return success. */ - -static struct pci_driver ohci_pci_driver; - -static int __devinit -hc_found_ohci (struct pci_dev *dev, int irq, - void *mem_base, const struct pci_device_id *id) -{ - u8 latency, limit; - ohci_t * ohci; - int ret; - - printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name); - - /* bad pci latencies can contribute to overruns */ - pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency); - if (latency) { - pci_read_config_byte (dev, PCI_MAX_LAT, &limit); - if (limit && limit < latency) { - dbg ("PCI latency reduced to max %d", limit); - pci_write_config_byte (dev, PCI_LATENCY_TIMER, limit); - latency = limit; - } - } - - ret = hc_add_ohci(dev, irq, mem_base, id->driver_data, - &ohci, ohci_pci_driver.name, dev->slot_name); - - if (ret == 0) { - ohci->pci_latency = latency; - - if (hc_start (ohci, &ohci->ohci_dev->dev) < 0) { - err ("can't start usb-%s", ohci->slot_name); - hc_remove_ohci(ohci); - return -EBUSY; - } - -#ifdef DEBUG - ohci_dump (ohci, 1); -#endif - } - - return ret; -} - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_PM - -/* controller died; cleanup debris, then restart */ -/* must not be called from interrupt context */ - -static void hc_restart (ohci_t *ohci) -{ - int temp; - int i; - - if (ohci->pci_latency) - pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency); - - ohci->disabled = 1; - ohci->sleeping = 0; - if (ohci->bus->root_hub) - usb_disconnect (&ohci->bus->root_hub); - - /* empty the interrupt branches */ - for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0; - for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0; - - /* no EDs to remove */ - ohci->ed_rm_list [0] = NULL; - ohci->ed_rm_list [1] = NULL; - - /* empty control and bulk lists */ - ohci->ed_isotail = NULL; - ohci->ed_controltail = NULL; - ohci->ed_bulktail = NULL; - - if ((temp = hc_reset (ohci)) < 0 || - (temp = hc_start (ohci, &ohci->ohci_dev->dev)) < 0) { - err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp); - } else - dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name); -} - -#endif /* CONFIG_PM */ - -/*-------------------------------------------------------------------------*/ - -/* configured so that an OHCI device is always provided */ -/* always called with process context; sleeping is OK */ - -static int __devinit -ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) -{ - unsigned long mem_resource, mem_len; - void *mem_base; - int status; - - if (pci_enable_device(dev) < 0) - return -ENODEV; - - if (!dev->irq) { - err("found OHCI device with no IRQ assigned. check BIOS settings!"); - pci_disable_device (dev); - return -ENODEV; - } - - /* we read its hardware registers as memory */ - mem_resource = pci_resource_start(dev, 0); - mem_len = pci_resource_len(dev, 0); - if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) { - dbg ("controller already in use"); - pci_disable_device (dev); - return -EBUSY; - } - - mem_base = ioremap_nocache (mem_resource, mem_len); - if (!mem_base) { - err("Error mapping OHCI memory"); - release_mem_region(mem_resource, mem_len); - pci_disable_device (dev); - return -EFAULT; - } - - /* controller writes into our memory */ - pci_set_master (dev); - - status = hc_found_ohci (dev, dev->irq, mem_base, id); - if (status < 0) { - iounmap (mem_base); - release_mem_region(mem_resource, mem_len); - pci_disable_device (dev); - } - return status; -} - -/*-------------------------------------------------------------------------*/ - -/* may be called from interrupt context [interface spec] */ -/* may be called without controller present */ -/* may be called with controller, bus, and devices active */ - -static void __devexit -ohci_pci_remove (struct pci_dev *dev) -{ - ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev); - void *membase = ohci->regs; - - dbg ("remove %s controller usb-%s%s%s", - hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), - dev->slot_name, - ohci->disabled ? " (disabled)" : "", - in_interrupt () ? " in interrupt" : "" - ); - - hc_remove_ohci(ohci); - - /* unmap the IO address space */ - iounmap (membase); - - release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0)); -} - - -#ifdef CONFIG_PM - -/*-------------------------------------------------------------------------*/ - -static int -ohci_pci_suspend (struct pci_dev *dev, u32 state) -{ - ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev); - unsigned long flags; - u16 cmd; - - if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) { - dbg ("can't suspend usb-%s (state is %s)", dev->slot_name, - hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS)); - return -EIO; - } - - /* act as if usb suspend can always be used */ - info ("USB suspend: usb-%s", dev->slot_name); - ohci->sleeping = 1; - - /* First stop processing */ - spin_lock_irqsave (&usb_ed_lock, flags); - ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); - writel (ohci->hc_control, &ohci->regs->control); - writel (OHCI_INTR_SF, &ohci->regs->intrstatus); - (void) readl (&ohci->regs->intrstatus); - spin_unlock_irqrestore (&usb_ed_lock, flags); - - /* Wait a frame or two */ - mdelay(1); - if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF) - mdelay (1); - -#ifdef CONFIG_PMAC_PBOOK - if (_machine == _MACH_Pmac) - disable_irq (ohci->irq); - /* else, 2.4 assumes shared irqs -- don't disable */ -#endif - /* Enable remote wakeup */ - writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable); - - /* Suspend chip and let things settle down a bit */ - ohci->hc_control = OHCI_USB_SUSPEND; - writel (ohci->hc_control, &ohci->regs->control); - (void) readl (&ohci->regs->control); - mdelay (500); /* No schedule here ! */ - switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) { - case OHCI_USB_RESET: - dbg("Bus in reset phase ???"); - break; - case OHCI_USB_RESUME: - dbg("Bus in resume phase ???"); - break; - case OHCI_USB_OPER: - dbg("Bus in operational phase ???"); - break; - case OHCI_USB_SUSPEND: - dbg("Bus suspended"); - break; - } - /* In some rare situations, Apple's OHCI have happily trashed - * memory during sleep. We disable it's bus master bit during - * suspend - */ - pci_read_config_word (dev, PCI_COMMAND, &cmd); - cmd &= ~PCI_COMMAND_MASTER; - pci_write_config_word (dev, PCI_COMMAND, cmd); -#ifdef CONFIG_PMAC_PBOOK - { - struct device_node *of_node; - - /* Disable USB PAD & cell clock */ - of_node = pci_device_to_OF_node (ohci->ohci_dev); - if (of_node && _machine == _MACH_Pmac) - pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); - } -#endif - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static int -ohci_pci_resume (struct pci_dev *dev) -{ - ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev); - int temp; - unsigned long flags; - - /* guard against multiple resumes */ - atomic_inc (&ohci->resume_count); - if (atomic_read (&ohci->resume_count) != 1) { - err ("concurrent PCI resumes for usb-%s", dev->slot_name); - atomic_dec (&ohci->resume_count); - return 0; - } - -#ifdef CONFIG_PMAC_PBOOK - { - struct device_node *of_node; - - /* Re-enable USB PAD & cell clock */ - of_node = pci_device_to_OF_node (ohci->ohci_dev); - if (of_node && _machine == _MACH_Pmac) - pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1); - } -#endif - - /* did we suspend, or were we powered off? */ - ohci->hc_control = readl (&ohci->regs->control); - temp = ohci->hc_control & OHCI_CTRL_HCFS; - -#ifdef DEBUG - /* the registers may look crazy here */ - ohci_dump_status (ohci); -#endif - - /* Re-enable bus mastering */ - pci_set_master(ohci->ohci_dev); - - switch (temp) { - - case OHCI_USB_RESET: // lost power - info ("USB restart: usb-%s", dev->slot_name); - hc_restart (ohci); - break; - - case OHCI_USB_SUSPEND: // host wakeup - case OHCI_USB_RESUME: // remote wakeup - info ("USB continue: usb-%s from %s wakeup", dev->slot_name, - (temp == OHCI_USB_SUSPEND) - ? "host" : "remote"); - ohci->hc_control = OHCI_USB_RESUME; - writel (ohci->hc_control, &ohci->regs->control); - (void) readl (&ohci->regs->control); - mdelay (20); /* no schedule here ! */ - /* Some controllers (lucent) need a longer delay here */ - mdelay (15); - temp = readl (&ohci->regs->control); - temp = ohci->hc_control & OHCI_CTRL_HCFS; - if (temp != OHCI_USB_RESUME) { - err ("controller usb-%s won't resume", dev->slot_name); - ohci->disabled = 1; - return -EIO; - } - - /* Some chips likes being resumed first */ - writel (OHCI_USB_OPER, &ohci->regs->control); - (void) readl (&ohci->regs->control); - mdelay (3); - - /* Then re-enable operations */ - spin_lock_irqsave (&usb_ed_lock, flags); - ohci->disabled = 0; - ohci->sleeping = 0; - ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; - if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) { - if (ohci->ed_controltail) - ohci->hc_control |= OHCI_CTRL_CLE; - if (ohci->ed_bulktail) - ohci->hc_control |= OHCI_CTRL_BLE; - } - writel (ohci->hc_control, &ohci->regs->control); - writel (OHCI_INTR_SF, &ohci->regs->intrstatus); - writel (OHCI_INTR_SF, &ohci->regs->intrenable); - /* Check for a pending done list */ - writel (OHCI_INTR_WDH, &ohci->regs->intrdisable); - (void) readl (&ohci->regs->intrdisable); - spin_unlock_irqrestore (&usb_ed_lock, flags); -#ifdef CONFIG_PMAC_PBOOK - if (_machine == _MACH_Pmac) - enable_irq (ohci->irq); -#endif - if (ohci->hcca->done_head) - dl_done_list (ohci, dl_reverse_done_list (ohci)); - writel (OHCI_INTR_WDH, &ohci->regs->intrenable); - writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ - writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ - break; - - default: - warn ("odd PCI resume for usb-%s", dev->slot_name); - } - - /* controller is operational, extra resumes are harmless */ - atomic_dec (&ohci->resume_count); - - return 0; -} - -#endif /* CONFIG_PM */ - - -/*-------------------------------------------------------------------------*/ - -static const struct pci_device_id __devinitdata ohci_pci_ids [] = { { - - /* - * AMD-756 [Viper] USB has a serious erratum when used with - * lowspeed devices like mice. - */ - vendor: 0x1022, - device: 0x740c, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, - - driver_data: OHCI_QUIRK_AMD756, - -} , { - - /* handle any USB OHCI controller */ - class: ((PCI_CLASS_SERIAL_USB << 8) | 0x10), - class_mask: ~0, - - /* no matter who makes it */ - vendor: PCI_ANY_ID, - device: PCI_ANY_ID, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, - - }, { /* end: all zeroes */ } -}; - -MODULE_DEVICE_TABLE (pci, ohci_pci_ids); - -static struct pci_driver ohci_pci_driver = { - name: "usb-ohci", - id_table: &ohci_pci_ids [0], - - probe: ohci_pci_probe, - remove: __devexit_p(ohci_pci_remove), - -#ifdef CONFIG_PM - suspend: ohci_pci_suspend, - resume: ohci_pci_resume, -#endif /* PM */ -}; - - -/*-------------------------------------------------------------------------*/ - -static int __init ohci_hcd_init (void) -{ - return pci_module_init (&ohci_pci_driver); -} - -/*-------------------------------------------------------------------------*/ - -static void __exit ohci_hcd_cleanup (void) -{ - pci_unregister_driver (&ohci_pci_driver); -} - -module_init (ohci_hcd_init); -module_exit (ohci_hcd_cleanup); - -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/usb-ohci-sa1111.c b/drivers/usb/host/usb-ohci-sa1111.c deleted file mode 100644 index c6bbd515bc91..000000000000 --- a/drivers/usb/host/usb-ohci-sa1111.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * linux/drivers/usb/usb-ohci-sa1111.c - * - * The outline of this code was taken from Brad Parkers <brad@heeltoe.com> - * original OHCI driver modifications, and reworked into a cleaner form - * by Russell King <rmk@arm.linux.org.uk>. - */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/sched.h> -#include <linux/ioport.h> -#include <linux/interrupt.h> -#include <linux/slab.h> -#include <linux/usb.h> -#include <linux/pci.h> -#include <linux/errno.h> - -#include <asm/hardware.h> -#include <asm/irq.h> -#include <asm/io.h> -#include <asm/arch/assabet.h> -#include <asm/arch/badge4.h> -#include <asm/hardware/sa1111.h> - -#include "usb-ohci.h" - -int __devinit -hc_add_ohci(struct pci_dev *dev, int irq, void *membase, unsigned long flags, - ohci_t **ohci, const char *name, const char *slot_name); -extern void hc_remove_ohci(ohci_t *ohci); -extern int hc_start (ohci_t * ohci, struct device *parent_dev); -extern int hc_reset (ohci_t * ohci); - - -static ohci_t *sa1111_ohci; - -static void __init sa1111_ohci_configure(void) -{ - unsigned int usb_rst = 0; - - printk(KERN_DEBUG __FILE__ - ": starting SA-1111 OHCI USB Controller\n"); - -#ifdef CONFIG_SA1100_BADGE4 - if (machine_is_badge4()) - /* power the bus */ - badge4_set_5V(BADGE4_5V_USB, 1); -#endif - - if (machine_is_xp860() || - machine_has_neponset() || - machine_is_pfs168() || - machine_is_badge4()) - usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW; - - /* - * Configure the power sense and control lines. Place the USB - * host controller in reset. - */ - USB_RESET = usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET; - - /* - * Now, carefully enable the USB clock, and take - * the USB host controller out of reset. - */ - SKPCR |= SKPCR_UCLKEN; - udelay(11); - USB_RESET = usb_rst; -} - -static void __exit sa1111_ohci_unconfigure(void) -{ - printk(KERN_DEBUG __FILE__ - ": stopping SA-1111 OHCI USB Controller\n"); - - /* - * Put the USB host controller into reset. - */ - USB_RESET |= USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET; - - /* - * Stop the USB clock. - */ - SKPCR &= ~SKPCR_UCLKEN; - -#ifdef CONFIG_SA1100_BADGE4 - if (machine_is_badge4()) - badge4_set_5V(BADGE4_5V_USB, 0); -#endif -} - - -static int __init sa1111_ohci_init(void) -{ - int ret; - - if (!sa1111) - return -ENODEV; - - /* - * Request memory resources. - */ - if (!request_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT, "usb-ohci")) - return -EBUSY; - - sa1111_ohci_configure(); - - /* - * Initialise the generic OHCI driver. - */ - sa1111_ohci = 0; - ret = hc_add_ohci(SA1111_FAKE_PCIDEV, NIRQHCIM, - (void *)&USB_OHCI_OP_BASE, 0, &sa1111_ohci, - "usb-ohci", "sa1111"); - - if (ret || !sa1111_ohci) { - sa1111_ohci = 0; - sa1111_ohci_unconfigure(); - release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT); - return -EBUSY; - } - - if (hc_start (sa1111_ohci, &sa1111->dev) < 0) { - err ("can't start usb-%s", sa1111_ohci->slot_name); - hc_remove_ohci (sa1111_ohci); - sa1111_ohci = 0; - sa1111_ohci_unconfigure(); - release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT); - return -EBUSY; - } - - return 0; -} - -static void __exit sa1111_ohci_exit(void) -{ - printk(KERN_DEBUG __FUNCTION__ ": cleaning up\n"); - - if (sa1111_ohci) { - hc_remove_ohci(sa1111_ohci); - sa1111_ohci = 0; - } - - sa1111_ohci_unconfigure(); - release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT); - - printk(KERN_DEBUG __FUNCTION__ ": exiting\n"); -} - -module_init(sa1111_ohci_init); -module_exit(sa1111_ohci_exit); - -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/usb-ohci.c b/drivers/usb/host/usb-ohci.c deleted file mode 100644 index cf64e80bf4a6..000000000000 --- a/drivers/usb/host/usb-ohci.c +++ /dev/null @@ -1,2537 +0,0 @@ -/* - * URB OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> - * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> - * - * [ Initialisation is based on Linus' ] - * [ uhci code and gregs ohci fragments ] - * [ (C) Copyright 1999 Linus Torvalds ] - * [ (C) Copyright 1999 Gregory P. Smith] - * - * - * History: - * - * 2002/03/08 interrupt unlink fix (Matt Hughes), better cleanup on - * load failure (Matthew Frederickson) - * 2002/01/20 async unlink fixes: return -EINPROGRESS (per spec) and - * make interrupt unlink-in-completion work (db) - * - * 2001/09/19 USB_ZERO_PACKET support (Jean Tourrilhes) - * 2001/07/17 power management and pmac cleanup (Benjamin Herrenschmidt) - * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam); - pci_map_single (db) - * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db) - * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam) - * - * 2000/09/26 fixed races in removing the private portion of the urb - * 2000/09/07 disable bulk and control lists when unlinking the last - * endpoint descriptor in order to avoid unrecoverable errors on - * the Lucent chips. (rwc@sgi) - * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some - * urb unlink probs, indentation fixes - * 2000/08/11 various oops fixes mostly affecting iso and cleanup from - * device unplugs. - * 2000/06/28 use PCI hotplug framework, for better power management - * and for Cardbus support (David Brownell) - * 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling - * when the controller loses power; handle UE; cleanup; ... - * - * v5.2 1999/12/07 URB 3rd preview, - * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi) - * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume - * i386: HUB, Keyboard, Mouse, Printer - * - * v4.3 1999/10/27 multiple HCs, bulk_request - * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes - * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl. - * v4.0 1999/08/18 - * v3.0 1999/06/25 - * v2.1 1999/05/09 code clean up - * v2.0 1999/05/04 - * v1.0 1999/04/27 initial release - */ - -#include <linux/config.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/smp_lock.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/timer.h> -#include <linux/list.h> -#include <linux/interrupt.h> /* for in_interrupt() */ - -#ifdef CONFIG_USB_DEBUG -# define DEBUG -#else -# undef DEBUG -#endif -#include <linux/usb.h> - -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/system.h> -#include <asm/unaligned.h> -#include <asm/byteorder.h> - -#define OHCI_USE_NPS // force NoPowerSwitching mode -// #define OHCI_VERBOSE_DEBUG /* not always helpful */ - -#include "../core/hcd.h" -#include "usb-ohci.h" - - -/* - * Version Information - */ -#define DRIVER_VERSION "v5.3" -#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell" -#define DRIVER_DESC "USB OHCI Host Controller Driver" - -#define OHCI_UNLINK_TIMEOUT (HZ / 10) - -static LIST_HEAD (ohci_hcd_list); -spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED; - - -/*-------------------------------------------------------------------------*/ - -/* AMD-756 (D2 rev) reports corrupt register contents in some cases. - * The erratum (#4) description is incorrect. AMD's workaround waits - * till some bits (mostly reserved) are clear; ok for all revs. - */ -#define read_roothub(hc, register, mask) ({ \ - u32 temp = readl (&hc->regs->roothub.register); \ - if (hc->flags & OHCI_QUIRK_AMD756) \ - while (temp & mask) \ - temp = readl (&hc->regs->roothub.register); \ - temp; }) - -static u32 roothub_a (struct ohci *hc) - { return read_roothub (hc, a, 0xfc0fe000); } -static inline u32 roothub_b (struct ohci *hc) - { return readl (&hc->regs->roothub.b); } -static inline u32 roothub_status (struct ohci *hc) - { return readl (&hc->regs->roothub.status); } -static u32 roothub_portstatus (struct ohci *hc, int i) - { return read_roothub (hc, portstatus [i], 0xffe0fce0); } - - -/*-------------------------------------------------------------------------* - * URB support functions - *-------------------------------------------------------------------------*/ - -/* free HCD-private data associated with this URB */ - -static void urb_free_priv (struct ohci *hc, urb_priv_t * urb_priv) -{ - int i; - int last = urb_priv->length - 1; - int len; - int dir; - struct td *td; - - if (last >= 0) { - - /* ISOC, BULK, INTR data buffer starts at td 0 - * CTRL setup starts at td 0 */ - td = urb_priv->td [0]; - - len = td->urb->transfer_buffer_length, - dir = usb_pipeout (td->urb->pipe) - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE; - - /* unmap CTRL URB setup */ - if (usb_pipecontrol (td->urb->pipe)) { - pci_unmap_single (hc->ohci_dev, - td->data_dma, 8, PCI_DMA_TODEVICE); - - /* CTRL data buffer starts at td 1 if len > 0 */ - if (len && last > 0) - td = urb_priv->td [1]; - } - - /* unmap data buffer */ - if (len && td->data_dma) - pci_unmap_single (hc->ohci_dev, td->data_dma, len, dir); - - for (i = 0; i <= last; i++) { - td = urb_priv->td [i]; - if (td) - td_free (hc, td); - } - } - - kfree (urb_priv); -} - -static void urb_rm_priv_locked (struct urb * urb) -{ - urb_priv_t * urb_priv = urb->hcpriv; - - if (urb_priv) { - urb->hcpriv = NULL; - -#ifdef DO_TIMEOUTS - if (urb->timeout) { - list_del (&urb->urb_list); - urb->timeout -= jiffies; - } -#endif - - /* Release int/iso bandwidth */ - if (urb->bandwidth) { - switch (usb_pipetype(urb->pipe)) { - case PIPE_INTERRUPT: - usb_release_bandwidth (urb->dev, urb, 0); - break; - case PIPE_ISOCHRONOUS: - usb_release_bandwidth (urb->dev, urb, 1); - break; - default: - break; - } - } - - urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv); - usb_put_dev (urb->dev); - urb->dev = NULL; - usb_put_urb (urb); - } -} - -static void urb_rm_priv (struct urb * urb) -{ - unsigned long flags; - - spin_lock_irqsave (&usb_ed_lock, flags); - urb_rm_priv_locked (urb); - spin_unlock_irqrestore (&usb_ed_lock, flags); -} - -/*-------------------------------------------------------------------------*/ - -#ifdef DEBUG -static int sohci_get_current_frame_number (struct usb_device * dev); - -/* debug| print the main components of an URB - * small: 0) header + data packets 1) just header */ - -static void urb_print (struct urb * urb, char * str, int small) -{ - unsigned int pipe= urb->pipe; - - if (!urb->dev || !urb->dev->bus) { - dbg("%s URB: no dev", str); - return; - } - -#ifndef OHCI_VERBOSE_DEBUG - if (urb->status != 0) -#endif - dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)", - str, - sohci_get_current_frame_number (urb->dev), - usb_pipedevice (pipe), - usb_pipeendpoint (pipe), - usb_pipeout (pipe)? 'O': 'I', - usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): - (usb_pipecontrol (pipe)? "CTRL": "BULK"), - urb->transfer_flags, - urb->actual_length, - urb->transfer_buffer_length, - urb->status, urb->status); -#ifdef OHCI_VERBOSE_DEBUG - if (!small) { - int i, len; - - if (usb_pipecontrol (pipe)) { - printk (KERN_DEBUG __FILE__ ": cmd(8):"); - for (i = 0; i < 8 ; i++) - printk (" %02x", ((__u8 *) urb->setup_packet) [i]); - printk ("\n"); - } - if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { - printk (KERN_DEBUG __FILE__ ": data(%d/%d):", - urb->actual_length, - urb->transfer_buffer_length); - len = usb_pipeout (pipe)? - urb->transfer_buffer_length: urb->actual_length; - for (i = 0; i < 16 && i < len; i++) - printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]); - printk ("%s stat:%d\n", i < len? "...": "", urb->status); - } - } -#endif -} - -/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ -void ep_print_int_eds (ohci_t * ohci, char * str) { - int i, j; - __u32 * ed_p; - for (i= 0; i < 32; i++) { - j = 5; - ed_p = &(ohci->hcca->int_table [i]); - if (*ed_p == 0) - continue; - printk (KERN_DEBUG __FILE__ ": %s branch int %2d(%2x):", str, i, i); - while (*ed_p != 0 && j--) { - ed_t *ed = dma_to_ed (ohci, le32_to_cpup(ed_p)); - printk (" ed: %4x;", ed->hwINFO); - ed_p = &ed->hwNextED; - } - printk ("\n"); - } -} - - -static void ohci_dump_intr_mask (char *label, __u32 mask) -{ - dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", - label, - mask, - (mask & OHCI_INTR_MIE) ? " MIE" : "", - (mask & OHCI_INTR_OC) ? " OC" : "", - (mask & OHCI_INTR_RHSC) ? " RHSC" : "", - (mask & OHCI_INTR_FNO) ? " FNO" : "", - (mask & OHCI_INTR_UE) ? " UE" : "", - (mask & OHCI_INTR_RD) ? " RD" : "", - (mask & OHCI_INTR_SF) ? " SF" : "", - (mask & OHCI_INTR_WDH) ? " WDH" : "", - (mask & OHCI_INTR_SO) ? " SO" : "" - ); -} - -static void maybe_print_eds (char *label, __u32 value) -{ - if (value) - dbg ("%s %08x", label, value); -} - -static char *hcfs2string (int state) -{ - switch (state) { - case OHCI_USB_RESET: return "reset"; - case OHCI_USB_RESUME: return "resume"; - case OHCI_USB_OPER: return "operational"; - case OHCI_USB_SUSPEND: return "suspend"; - } - return "?"; -} - -// dump control and status registers -static void ohci_dump_status (ohci_t *controller) -{ - struct ohci_regs *regs = controller->regs; - __u32 temp; - - temp = readl (®s->revision) & 0xff; - if (temp != 0x10) - dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); - - temp = readl (®s->control); - dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, - (temp & OHCI_CTRL_RWE) ? " RWE" : "", - (temp & OHCI_CTRL_RWC) ? " RWC" : "", - (temp & OHCI_CTRL_IR) ? " IR" : "", - hcfs2string (temp & OHCI_CTRL_HCFS), - (temp & OHCI_CTRL_BLE) ? " BLE" : "", - (temp & OHCI_CTRL_CLE) ? " CLE" : "", - (temp & OHCI_CTRL_IE) ? " IE" : "", - (temp & OHCI_CTRL_PLE) ? " PLE" : "", - temp & OHCI_CTRL_CBSR - ); - - temp = readl (®s->cmdstatus); - dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, - (temp & OHCI_SOC) >> 16, - (temp & OHCI_OCR) ? " OCR" : "", - (temp & OHCI_BLF) ? " BLF" : "", - (temp & OHCI_CLF) ? " CLF" : "", - (temp & OHCI_HCR) ? " HCR" : "" - ); - - ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); - ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); - // intrdisable always same as intrenable - // ohci_dump_intr_mask ("intrdisable", readl (®s->intrdisable)); - - maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); - - maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); - maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); - - maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); - maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); - - maybe_print_eds ("donehead", readl (®s->donehead)); -} - -static void ohci_dump_roothub (ohci_t *controller, int verbose) -{ - __u32 temp, ndp, i; - - temp = roothub_a (controller); - ndp = (temp & RH_A_NDP); - - if (verbose) { - dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, - ((temp & RH_A_POTPGT) >> 24) & 0xff, - (temp & RH_A_NOCP) ? " NOCP" : "", - (temp & RH_A_OCPM) ? " OCPM" : "", - (temp & RH_A_DT) ? " DT" : "", - (temp & RH_A_NPS) ? " NPS" : "", - (temp & RH_A_PSM) ? " PSM" : "", - ndp - ); - temp = roothub_b (controller); - dbg ("roothub.b: %08x PPCM=%04x DR=%04x", - temp, - (temp & RH_B_PPCM) >> 16, - (temp & RH_B_DR) - ); - temp = roothub_status (controller); - dbg ("roothub.status: %08x%s%s%s%s%s%s", - temp, - (temp & RH_HS_CRWE) ? " CRWE" : "", - (temp & RH_HS_OCIC) ? " OCIC" : "", - (temp & RH_HS_LPSC) ? " LPSC" : "", - (temp & RH_HS_DRWE) ? " DRWE" : "", - (temp & RH_HS_OCI) ? " OCI" : "", - (temp & RH_HS_LPS) ? " LPS" : "" - ); - } - - for (i = 0; i < ndp; i++) { - temp = roothub_portstatus (controller, i); - dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", - i, - temp, - (temp & RH_PS_PRSC) ? " PRSC" : "", - (temp & RH_PS_OCIC) ? " OCIC" : "", - (temp & RH_PS_PSSC) ? " PSSC" : "", - (temp & RH_PS_PESC) ? " PESC" : "", - (temp & RH_PS_CSC) ? " CSC" : "", - - (temp & RH_PS_LSDA) ? " LSDA" : "", - (temp & RH_PS_PPS) ? " PPS" : "", - (temp & RH_PS_PRS) ? " PRS" : "", - (temp & RH_PS_POCI) ? " POCI" : "", - (temp & RH_PS_PSS) ? " PSS" : "", - - (temp & RH_PS_PES) ? " PES" : "", - (temp & RH_PS_CCS) ? " CCS" : "" - ); - } -} - -static void ohci_dump (ohci_t *controller, int verbose) -{ - dbg ("OHCI controller usb-%s state", controller->slot_name); - - // dumps some of the state we know about - ohci_dump_status (controller); - if (verbose) - ep_print_int_eds (controller, "hcca"); - dbg ("hcca frame #%04x", controller->hcca->frame_no); - ohci_dump_roothub (controller, 1); -} - - -#endif - -/*-------------------------------------------------------------------------* - * Interface functions (URB) - *-------------------------------------------------------------------------*/ - -/* return a request to the completion handler */ - -static int sohci_return_urb (struct ohci *hc, struct urb * urb) -{ - urb_priv_t * urb_priv = urb->hcpriv; - struct urb * urbt = NULL; - unsigned long flags; - int i; - - if (!urb_priv) - return -1; /* urb already unlinked */ - - /* just to be sure */ - if (!urb->complete) { - urb_rm_priv (urb); - return -1; - } - -#ifdef DEBUG - urb_print (urb, "RET", usb_pipeout (urb->pipe)); -#endif - - switch (usb_pipetype (urb->pipe)) { - case PIPE_INTERRUPT: - pci_unmap_single (hc->ohci_dev, - urb_priv->td [0]->data_dma, - urb->transfer_buffer_length, - usb_pipeout (urb->pipe) - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE); - urb->complete (urb); - - /* implicitly requeued */ - urb->actual_length = 0; - urb->status = -EINPROGRESS; - td_submit_urb (urb); - break; - - case PIPE_ISOCHRONOUS: - // for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next); - if (urbt) { /* send the reply and requeue URB */ - pci_unmap_single (hc->ohci_dev, - urb_priv->td [0]->data_dma, - urb->transfer_buffer_length, - usb_pipeout (urb->pipe) - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE); - urb->complete (urb); - spin_lock_irqsave (&usb_ed_lock, flags); - urb->actual_length = 0; - urb->status = -EINPROGRESS; - urb->start_frame = urb_priv->ed->last_iso + 1; - if (urb_priv->state != URB_DEL) { - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].actual_length = 0; - urb->iso_frame_desc[i].status = -EXDEV; - } - td_submit_urb (urb); - } - spin_unlock_irqrestore (&usb_ed_lock, flags); - - } else { /* unlink URB, call complete */ - urb_rm_priv (urb); - urb->complete (urb); - } - break; - - case PIPE_BULK: - case PIPE_CONTROL: /* unlink URB, call complete */ - urb_rm_priv (urb); - urb->complete (urb); - break; - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* get a transfer request */ - -static int sohci_submit_urb (struct urb * urb, int mem_flags) -{ - ohci_t * ohci; - ed_t * ed; - urb_priv_t * urb_priv; - unsigned int pipe = urb->pipe; - int maxps = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); - int i, size = 0; - unsigned long flags; - int bustime = 0; - - if (!urb->dev || !urb->dev->bus) - return -ENODEV; - - if (urb->hcpriv) /* urb already in use */ - return -EINVAL; - -// if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) -// return -EPIPE; - - /* increment the reference count of the urb, as we now also control it */ - urb = usb_get_urb (urb); - - usb_get_dev (urb->dev); - ohci = (ohci_t *) urb->dev->bus->hcpriv; - -#ifdef DEBUG - urb_print (urb, "SUB", usb_pipein (pipe)); -#endif - - /* handle a request to the virtual root hub */ - if (usb_pipedevice (pipe) == ohci->rh.devnum) - return rh_submit_urb (urb); - - /* when controller's hung, permit only roothub cleanup attempts - * such as powering down ports */ - if (ohci->disabled) { - usb_put_dev (urb->dev); - usb_put_urb (urb); - return -ESHUTDOWN; - } - - /* every endpoint has a ed, locate and fill it */ - if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { - usb_put_dev (urb->dev); - usb_put_urb (urb); - return -ENOMEM; - } - - /* for the private part of the URB we need the number of TDs (size) */ - switch (usb_pipetype (pipe)) { - case PIPE_BULK: /* one TD for every 4096 Byte */ - size = (urb->transfer_buffer_length - 1) / 4096 + 1; - - /* If the transfer size is multiple of the pipe mtu, - * we may need an extra TD to create a empty frame - * Jean II */ - if ((urb->transfer_flags & USB_ZERO_PACKET) && - usb_pipeout (pipe) && - (urb->transfer_buffer_length != 0) && - ((urb->transfer_buffer_length % maxps) == 0)) - size++; - break; - case PIPE_ISOCHRONOUS: /* number of packets from URB */ - size = urb->number_of_packets; - if (size <= 0) { - usb_put_dev (urb->dev); - usb_put_urb (urb); - return -EINVAL; - } - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].actual_length = 0; - urb->iso_frame_desc[i].status = -EXDEV; - } - break; - case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ - size = (urb->transfer_buffer_length == 0)? 2: - (urb->transfer_buffer_length - 1) / 4096 + 3; - break; - case PIPE_INTERRUPT: /* one TD */ - size = 1; - break; - } - - /* allocate the private part of the URB */ - urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), mem_flags); - if (!urb_priv) { - usb_put_dev (urb->dev); - usb_put_urb (urb); - return -ENOMEM; - } - memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *)); - - /* fill the private part of the URB */ - urb_priv->length = size; - urb_priv->ed = ed; - - /* allocate the TDs (updating hash chains) */ - spin_lock_irqsave (&usb_ed_lock, flags); - for (i = 0; i < size; i++) { - urb_priv->td[i] = td_alloc (ohci, SLAB_ATOMIC); - if (!urb_priv->td[i]) { - urb_priv->length = i; - urb_free_priv (ohci, urb_priv); - spin_unlock_irqrestore (&usb_ed_lock, flags); - usb_put_dev (urb->dev); - usb_put_urb (urb); - return -ENOMEM; - } - } - - if (ed->state == ED_NEW || (ed->state & ED_DEL)) { - urb_free_priv (ohci, urb_priv); - spin_unlock_irqrestore (&usb_ed_lock, flags); - usb_put_dev (urb->dev); - usb_put_urb (urb); - return -EINVAL; - } - - /* allocate and claim bandwidth if needed; ISO - * needs start frame index if it was't provided. - */ - switch (usb_pipetype (pipe)) { - case PIPE_ISOCHRONOUS: - if (urb->transfer_flags & USB_ISO_ASAP) { - urb->start_frame = ((ed->state == ED_OPER) - ? (ed->last_iso + 1) - : (le16_to_cpu (ohci->hcca->frame_no) + 10)) & 0xffff; - } - /* FALLTHROUGH */ - case PIPE_INTERRUPT: - if (urb->bandwidth == 0) { - bustime = usb_check_bandwidth (urb->dev, urb); - } - if (bustime < 0) { - urb_free_priv (ohci, urb_priv); - spin_unlock_irqrestore (&usb_ed_lock, flags); - usb_put_dev (urb->dev); - usb_put_urb (urb); - return bustime; - } - usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe)); -#ifdef DO_TIMEOUTS - urb->timeout = 0; -#endif - } - - urb->actual_length = 0; - urb->hcpriv = urb_priv; - urb->status = -EINPROGRESS; - - /* link the ed into a chain if is not already */ - if (ed->state != ED_OPER) - ep_link (ohci, ed); - - /* fill the TDs and link it to the ed */ - td_submit_urb (urb); - -#ifdef DO_TIMEOUTS - /* maybe add to ordered list of timeouts */ - if (urb->timeout) { - struct list_head *entry; - - // FIXME: usb-uhci uses relative timeouts (like this), - // while uhci uses absolute ones (probably better). - // Pick one solution and change the affected drivers. - urb->timeout += jiffies; - - list_for_each (entry, &ohci->timeout_list) { - struct urb *next_urb; - - next_urb = list_entry (entry, struct urb, urb_list); - if (time_after_eq (urb->timeout, next_urb->timeout)) - break; - } - list_add (&urb->urb_list, entry); - - /* drive timeouts by SF (messy, but works) */ - writel (OHCI_INTR_SF, &ohci->regs->intrenable); - } -#endif - - spin_unlock_irqrestore (&usb_ed_lock, flags); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* deactivate all TDs and remove the private part of the URB */ -/* interrupt callers must use async unlink mode */ - -static int sohci_unlink_urb (struct urb * urb) -{ - unsigned long flags; - ohci_t * ohci; - - if (!urb) /* just to be sure */ - return -EINVAL; - - if (!urb->dev || !urb->dev->bus) - return -ENODEV; - - ohci = (ohci_t *) urb->dev->bus->hcpriv; - -#ifdef DEBUG - urb_print (urb, "UNLINK", 1); -#endif - - /* handle a request to the virtual root hub */ - if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) - return rh_unlink_urb (urb); - - if (urb->hcpriv && (urb->status == -EINPROGRESS)) { - if (!ohci->disabled) { - urb_priv_t * urb_priv; - - /* interrupt code may not sleep; it must use - * async status return to unlink pending urbs. - */ - if (!(urb->transfer_flags & USB_ASYNC_UNLINK) - && in_interrupt ()) { - err ("bug in call from %p; use async!", - __builtin_return_address(0)); - return -EWOULDBLOCK; - } - - /* flag the urb and its TDs for deletion in some - * upcoming SF interrupt delete list processing - */ - spin_lock_irqsave (&usb_ed_lock, flags); - urb_priv = urb->hcpriv; - - if (!urb_priv || (urb_priv->state == URB_DEL)) { - spin_unlock_irqrestore (&usb_ed_lock, flags); - return 0; - } - - urb_priv->state = URB_DEL; - ep_rm_ed (urb->dev, urb_priv->ed); - urb_priv->ed->state |= ED_URB_DEL; - - if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) { - DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup); - DECLARE_WAITQUEUE (wait, current); - int timeout = OHCI_UNLINK_TIMEOUT; - - add_wait_queue (&unlink_wakeup, &wait); - urb_priv->wait = &unlink_wakeup; - spin_unlock_irqrestore (&usb_ed_lock, flags); - - /* wait until all TDs are deleted */ - set_current_state(TASK_UNINTERRUPTIBLE); - while (timeout && (urb->status == -EINPROGRESS)) - timeout = schedule_timeout (timeout); - set_current_state(TASK_RUNNING); - remove_wait_queue (&unlink_wakeup, &wait); - if (urb->status == -EINPROGRESS) { - err ("unlink URB timeout"); - return -ETIMEDOUT; - } - } else { - /* usb_put_dev done in dl_del_list() */ - urb->status = -EINPROGRESS; - spin_unlock_irqrestore (&usb_ed_lock, flags); - return -EINPROGRESS; - } - } else { - urb_rm_priv (urb); - if (urb->transfer_flags & USB_ASYNC_UNLINK) { - urb->status = -ECONNRESET; - if (urb->complete) - urb->complete (urb); - } else - urb->status = -ENOENT; - } - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* allocate private data space for a usb device */ - -static int sohci_alloc_dev (struct usb_device *usb_dev) -{ - struct ohci_device * dev; - - dev = dev_alloc ((struct ohci *) usb_dev->bus->hcpriv, ALLOC_FLAGS); - if (!dev) - return -ENOMEM; - - usb_dev->hcpriv = dev; - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* may be called from interrupt context */ -/* frees private data space of usb device */ - -static int sohci_free_dev (struct usb_device * usb_dev) -{ - unsigned long flags; - int i, cnt = 0; - ed_t * ed; - struct ohci_device * dev = usb_to_ohci (usb_dev); - ohci_t * ohci = usb_dev->bus->hcpriv; - - if (!dev) - return 0; - - if (usb_dev->devnum >= 0) { - - /* driver disconnects should have unlinked all urbs - * (freeing all the TDs, unlinking EDs) but we need - * to defend against bugs that prevent that. - */ - spin_lock_irqsave (&usb_ed_lock, flags); - for(i = 0; i < NUM_EDS; i++) { - ed = &(dev->ed[i]); - if (ed->state != ED_NEW) { - if (ed->state == ED_OPER) { - /* driver on that interface didn't unlink an urb */ - dbg ("driver usb-%s dev %d ed 0x%x unfreed URB", - ohci->slot_name, usb_dev->devnum, i); - ep_unlink (ohci, ed); - } - ep_rm_ed (usb_dev, ed); - ed->state = ED_DEL; - cnt++; - } - } - spin_unlock_irqrestore (&usb_ed_lock, flags); - - /* if the controller is running, tds for those unlinked - * urbs get freed by dl_del_list at the next SF interrupt - */ - if (cnt > 0) { - - if (ohci->disabled) { - /* FIXME: Something like this should kick in, - * though it's currently an exotic case ... - * the controller won't ever be touching - * these lists again!! - dl_del_list (ohci, - le16_to_cpu (ohci->hcca->frame_no) & 1); - */ - warn ("TD leak, %d", cnt); - - } else if (!in_interrupt ()) { - DECLARE_WAIT_QUEUE_HEAD (freedev_wakeup); - DECLARE_WAITQUEUE (wait, current); - int timeout = OHCI_UNLINK_TIMEOUT; - - /* SF interrupt handler calls dl_del_list */ - add_wait_queue (&freedev_wakeup, &wait); - dev->wait = &freedev_wakeup; - set_current_state(TASK_UNINTERRUPTIBLE); - while (timeout && dev->ed_cnt) - timeout = schedule_timeout (timeout); - set_current_state(TASK_RUNNING); - remove_wait_queue (&freedev_wakeup, &wait); - if (dev->ed_cnt) { - err ("free device %d timeout", usb_dev->devnum); - return -ETIMEDOUT; - } - } else { - /* likely some interface's driver has a refcount bug */ - err ("bus %s devnum %d deletion in interrupt", - ohci->slot_name, usb_dev->devnum); - BUG (); - } - } - } - - /* free device, and associated EDs */ - dev_free (ohci, dev); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* tell us the current USB frame number */ - -static int sohci_get_current_frame_number (struct usb_device *usb_dev) -{ - ohci_t * ohci = usb_dev->bus->hcpriv; - - return le16_to_cpu (ohci->hcca->frame_no); -} - -/*-------------------------------------------------------------------------*/ - -struct usb_operations sohci_device_operations = { - allocate: sohci_alloc_dev, - deallocate: sohci_free_dev, - get_frame_number: sohci_get_current_frame_number, - submit_urb: sohci_submit_urb, - unlink_urb: sohci_unlink_urb, -}; - -/*-------------------------------------------------------------------------* - * ED handling functions - *-------------------------------------------------------------------------*/ - -/* search for the right branch to insert an interrupt ed into the int tree - * do some load ballancing; - * returns the branch and - * sets the interval to interval = 2^integer (ld (interval)) */ - -static int ep_int_ballance (ohci_t * ohci, int interval, int load) -{ - int i, branch = 0; - - /* search for the least loaded interrupt endpoint branch of all 32 branches */ - for (i = 0; i < 32; i++) - if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i; - - branch = branch % interval; - for (i = branch; i < 32; i += interval) ohci->ohci_int_load [i] += load; - - return branch; -} - -/*-------------------------------------------------------------------------*/ - -/* 2^int( ld (inter)) */ - -static int ep_2_n_interval (int inter) -{ - int i; - for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++); - return 1 << i; -} - -/*-------------------------------------------------------------------------*/ - -/* the int tree is a binary tree - * in order to process it sequentially the indexes of the branches have to be mapped - * the mapping reverses the bits of a word of num_bits length */ - -static int ep_rev (int num_bits, int word) -{ - int i, wout = 0; - - for (i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1)); - return wout; -} - -/*-------------------------------------------------------------------------*/ - -/* link an ed into one of the HC chains */ - -static int ep_link (ohci_t * ohci, ed_t * edi) -{ - int int_branch; - int i; - int inter; - int interval; - int load; - __u32 * ed_p; - volatile ed_t * ed = edi; - - ed->state = ED_OPER; - - switch (ed->type) { - case PIPE_CONTROL: - ed->hwNextED = 0; - if (ohci->ed_controltail == NULL) { - writel (ed->dma, &ohci->regs->ed_controlhead); - } else { - ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma); - } - ed->ed_prev = ohci->ed_controltail; - if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && - !ohci->ed_rm_list[1] && !ohci->sleeping) { - ohci->hc_control |= OHCI_CTRL_CLE; - writel (ohci->hc_control, &ohci->regs->control); - } - ohci->ed_controltail = edi; - break; - - case PIPE_BULK: - ed->hwNextED = 0; - if (ohci->ed_bulktail == NULL) { - writel (ed->dma, &ohci->regs->ed_bulkhead); - } else { - ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma); - } - ed->ed_prev = ohci->ed_bulktail; - if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && - !ohci->ed_rm_list[1] && !ohci->sleeping) { - ohci->hc_control |= OHCI_CTRL_BLE; - writel (ohci->hc_control, &ohci->regs->control); - } - ohci->ed_bulktail = edi; - break; - - case PIPE_INTERRUPT: - load = ed->int_load; - interval = ep_2_n_interval (ed->int_period); - ed->int_interval = interval; - int_branch = ep_int_ballance (ohci, interval, load); - ed->int_branch = int_branch; - - for (i = 0; i < ep_rev (6, interval); i += inter) { - inter = 1; - for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]); - (*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval); - ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) - inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval); - ed->hwNextED = *ed_p; - *ed_p = cpu_to_le32 (ed->dma); - } -#ifdef DEBUG - ep_print_int_eds (ohci, "LINK_INT"); -#endif - break; - - case PIPE_ISOCHRONOUS: - ed->hwNextED = 0; - ed->int_interval = 1; - if (ohci->ed_isotail != NULL) { - ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma); - ed->ed_prev = ohci->ed_isotail; - } else { - for ( i = 0; i < 32; i += inter) { - inter = 1; - for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]); - *ed_p != 0; - ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) - inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval); - *ed_p = cpu_to_le32 (ed->dma); - } - ed->ed_prev = NULL; - } - ohci->ed_isotail = edi; -#ifdef DEBUG - ep_print_int_eds (ohci, "LINK_ISO"); -#endif - break; - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* scan the periodic table to find and unlink this ED */ -static void periodic_unlink ( - struct ohci *ohci, - struct ed *ed, - unsigned index, - unsigned period -) { - for (; index < NUM_INTS; index += period) { - __u32 *ed_p = &ohci->hcca->int_table [index]; - - /* ED might have been unlinked through another path */ - while (*ed_p != 0) { - if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) { - *ed_p = ed->hwNextED; - break; - } - ed_p = & ((dma_to_ed (ohci, - le32_to_cpup (ed_p)))->hwNextED); - } - } -} - -/* unlink an ed from one of the HC chains. - * just the link to the ed is unlinked. - * the link from the ed still points to another operational ed or 0 - * so the HC can eventually finish the processing of the unlinked ed */ - -static int ep_unlink (ohci_t * ohci, ed_t * ed) -{ - int i; - - ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP); - - switch (ed->type) { - case PIPE_CONTROL: - if (ed->ed_prev == NULL) { - if (!ed->hwNextED) { - ohci->hc_control &= ~OHCI_CTRL_CLE; - writel (ohci->hc_control, &ohci->regs->control); - } - writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead); - } else { - ed->ed_prev->hwNextED = ed->hwNextED; - } - if (ohci->ed_controltail == ed) { - ohci->ed_controltail = ed->ed_prev; - } else { - (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev; - } - break; - - case PIPE_BULK: - if (ed->ed_prev == NULL) { - if (!ed->hwNextED) { - ohci->hc_control &= ~OHCI_CTRL_BLE; - writel (ohci->hc_control, &ohci->regs->control); - } - writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead); - } else { - ed->ed_prev->hwNextED = ed->hwNextED; - } - if (ohci->ed_bulktail == ed) { - ohci->ed_bulktail = ed->ed_prev; - } else { - (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev; - } - break; - - case PIPE_INTERRUPT: - periodic_unlink (ohci, ed, 0, 1); - for (i = ed->int_branch; i < 32; i += ed->int_interval) - ohci->ohci_int_load[i] -= ed->int_load; -#ifdef DEBUG - ep_print_int_eds (ohci, "UNLINK_INT"); -#endif - break; - - case PIPE_ISOCHRONOUS: - if (ohci->ed_isotail == ed) - ohci->ed_isotail = ed->ed_prev; - if (ed->hwNextED != 0) - (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED))) - ->ed_prev = ed->ed_prev; - - if (ed->ed_prev != NULL) - ed->ed_prev->hwNextED = ed->hwNextED; - else - periodic_unlink (ohci, ed, 0, 1); -#ifdef DEBUG - ep_print_int_eds (ohci, "UNLINK_ISO"); -#endif - break; - } - ed->state = ED_UNLINK; - return 0; -} - - -/*-------------------------------------------------------------------------*/ - -/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, - * but the USB stack is a little bit stateless so we do it at every transaction - * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK - * in all other cases the state is left unchanged - * the ed info fields are setted anyway even though most of them should not change */ - -static ed_t * ep_add_ed ( - struct usb_device * usb_dev, - unsigned int pipe, - int interval, - int load, - int mem_flags -) -{ - ohci_t * ohci = usb_dev->bus->hcpriv; - td_t * td; - ed_t * ed_ret; - volatile ed_t * ed; - unsigned long flags; - - - spin_lock_irqsave (&usb_ed_lock, flags); - - ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | - (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]); - - if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { - /* pending delete request */ - spin_unlock_irqrestore (&usb_ed_lock, flags); - return NULL; - } - - if (ed->state == ED_NEW) { - ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ - /* dummy td; end of td list for ed */ - td = td_alloc (ohci, SLAB_ATOMIC); - /* hash the ed for later reverse mapping */ - if (!td || !hash_add_ed (ohci, (ed_t *)ed)) { - /* out of memory */ - if (td) - td_free(ohci, td); - spin_unlock_irqrestore (&usb_ed_lock, flags); - return NULL; - } - ed->hwTailP = cpu_to_le32 (td->td_dma); - ed->hwHeadP = ed->hwTailP; - ed->state = ED_UNLINK; - ed->type = usb_pipetype (pipe); - usb_to_ohci (usb_dev)->ed_cnt++; - } - - ohci->dev[usb_pipedevice (pipe)] = usb_dev; - - ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe) - | usb_pipeendpoint (pipe) << 7 - | (usb_pipeisoc (pipe)? 0x8000: 0) - | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) - | (usb_dev->speed == USB_SPEED_LOW) << 13 - | usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16); - - if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) { - ed->int_period = interval; - ed->int_load = load; - } - - spin_unlock_irqrestore (&usb_ed_lock, flags); - return ed_ret; -} - -/*-------------------------------------------------------------------------*/ - -/* request the removal of an endpoint - * put the ep on the rm_list and request a stop of the bulk or ctrl list - * real removal is done at the next start frame (SF) hardware interrupt */ - -static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed) -{ - unsigned int frame; - ohci_t * ohci = usb_dev->bus->hcpriv; - - if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) - return; - - ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP); - - if (!ohci->disabled) { - switch (ed->type) { - case PIPE_CONTROL: /* stop control list */ - ohci->hc_control &= ~OHCI_CTRL_CLE; - writel (ohci->hc_control, &ohci->regs->control); - break; - case PIPE_BULK: /* stop bulk list */ - ohci->hc_control &= ~OHCI_CTRL_BLE; - writel (ohci->hc_control, &ohci->regs->control); - break; - } - } - - frame = le16_to_cpu (ohci->hcca->frame_no) & 0x1; - ed->ed_rm_list = ohci->ed_rm_list[frame]; - ohci->ed_rm_list[frame] = ed; - - if (!ohci->disabled && !ohci->sleeping) { - /* enable SOF interrupt */ - writel (OHCI_INTR_SF, &ohci->regs->intrstatus); - writel (OHCI_INTR_SF, &ohci->regs->intrenable); - } -} - -/*-------------------------------------------------------------------------* - * TD handling functions - *-------------------------------------------------------------------------*/ - -/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ - -static void -td_fill (ohci_t * ohci, unsigned int info, - dma_addr_t data, int len, - struct urb * urb, int index) -{ - volatile td_t * td, * td_pt; - urb_priv_t * urb_priv = urb->hcpriv; - - if (index >= urb_priv->length) { - err("internal OHCI error: TD index > length"); - return; - } - - /* use this td as the next dummy */ - td_pt = urb_priv->td [index]; - td_pt->hwNextTD = 0; - - /* fill the old dummy TD */ - td = urb_priv->td [index] = dma_to_td (ohci, - le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf); - - td->ed = urb_priv->ed; - td->next_dl_td = NULL; - td->index = index; - td->urb = urb; - td->data_dma = data; - if (!len) - data = 0; - - td->hwINFO = cpu_to_le32 (info); - if ((td->ed->type) == PIPE_ISOCHRONOUS) { - td->hwCBP = cpu_to_le32 (data & 0xFFFFF000); - td->ed->last_iso = info & 0xffff; - } else { - td->hwCBP = cpu_to_le32 (data); - } - if (data) - td->hwBE = cpu_to_le32 (data + len - 1); - else - td->hwBE = 0; - td->hwNextTD = cpu_to_le32 (td_pt->td_dma); - td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000); - - /* append to queue */ - td->ed->hwTailP = td->hwNextTD; -} - -/*-------------------------------------------------------------------------*/ - -/* prepare all TDs of a transfer */ - -static void td_submit_urb (struct urb * urb) -{ - urb_priv_t * urb_priv = urb->hcpriv; - ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv; - dma_addr_t data; - int data_len = urb->transfer_buffer_length; - int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); - int cnt = 0; - __u32 info = 0; - unsigned int toggle = 0; - - /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ - if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) { - toggle = TD_T_TOGGLE; - } else { - toggle = TD_T_DATA0; - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1); - } - - urb_priv->td_cnt = 0; - - if (data_len) { - data = pci_map_single (ohci->ohci_dev, - urb->transfer_buffer, data_len, - usb_pipeout (urb->pipe) - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE - ); - } else - data = 0; - - switch (usb_pipetype (urb->pipe)) { - case PIPE_BULK: - info = usb_pipeout (urb->pipe)? - TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; - while(data_len > 4096) { - td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt); - data += 4096; data_len -= 4096; cnt++; - } - info = usb_pipeout (urb->pipe)? - TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; - td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt); - cnt++; - - /* If the transfer size is multiple of the pipe mtu, - * we may need an extra TD to create a empty frame - * Note : another way to check this condition is - * to test if(urb_priv->length > cnt) - Jean II */ - if ((urb->transfer_flags & USB_ZERO_PACKET) && - usb_pipeout (urb->pipe) && - (urb->transfer_buffer_length != 0) && - ((urb->transfer_buffer_length % maxps) == 0)) { - td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), 0, 0, urb, cnt); - cnt++; - } - - if (!ohci->sleeping) - writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ - break; - - case PIPE_INTERRUPT: - info = usb_pipeout (urb->pipe)? - TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle; - td_fill (ohci, info, data, data_len, urb, cnt++); - break; - - case PIPE_CONTROL: - info = TD_CC | TD_DP_SETUP | TD_T_DATA0; - td_fill (ohci, info, - pci_map_single (ohci->ohci_dev, - urb->setup_packet, 8, - PCI_DMA_TODEVICE), - 8, urb, cnt++); - if (data_len > 0) { - info = usb_pipeout (urb->pipe)? - TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; - /* NOTE: mishandles transfers >8K, some >4K */ - td_fill (ohci, info, data, data_len, urb, cnt++); - } - info = usb_pipeout (urb->pipe)? - TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; - td_fill (ohci, info, data, 0, urb, cnt++); - if (!ohci->sleeping) - writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ - break; - - case PIPE_ISOCHRONOUS: - for (cnt = 0; cnt < urb->number_of_packets; cnt++) { - td_fill (ohci, TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff), - data + urb->iso_frame_desc[cnt].offset, - urb->iso_frame_desc[cnt].length, urb, cnt); - } - break; - } - if (urb_priv->length != cnt) - dbg("TD LENGTH %d != CNT %d", urb_priv->length, cnt); -} - -/*-------------------------------------------------------------------------* - * Done List handling functions - *-------------------------------------------------------------------------*/ - - -/* calculate the transfer length and update the urb */ - -static void dl_transfer_length(td_t * td) -{ - __u32 tdINFO, tdBE, tdCBP; - __u16 tdPSW; - struct urb * urb = td->urb; - urb_priv_t * urb_priv = urb->hcpriv; - int dlen = 0; - int cc = 0; - - tdINFO = le32_to_cpup (&td->hwINFO); - tdBE = le32_to_cpup (&td->hwBE); - tdCBP = le32_to_cpup (&td->hwCBP); - - - if (tdINFO & TD_ISO) { - tdPSW = le16_to_cpu (td->hwPSW[0]); - cc = (tdPSW >> 12) & 0xF; - if (cc < 0xE) { - if (usb_pipeout(urb->pipe)) { - dlen = urb->iso_frame_desc[td->index].length; - } else { - dlen = tdPSW & 0x3ff; - } - urb->actual_length += dlen; - urb->iso_frame_desc[td->index].actual_length = dlen; - if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN)) - cc = TD_CC_NOERROR; - - urb->iso_frame_desc[td->index].status = cc_to_error[cc]; - } - } else { /* BULK, INT, CONTROL DATA */ - if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && - ((td->index == 0) || (td->index == urb_priv->length - 1)))) { - if (tdBE != 0) { - if (td->hwCBP == 0) - urb->actual_length += tdBE - td->data_dma + 1; - else - urb->actual_length += tdCBP - td->data_dma; - } - } - } -} - -/* handle an urb that is being unlinked */ - -static void dl_del_urb (struct urb * urb) -{ - wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait; - - urb_rm_priv_locked (urb); - - if (urb->transfer_flags & USB_ASYNC_UNLINK) { - urb->status = -ECONNRESET; - if (urb->complete) - urb->complete (urb); - } else { - urb->status = -ENOENT; - - /* unblock sohci_unlink_urb */ - if (wait_head) - wake_up (wait_head); - } -} - -/*-------------------------------------------------------------------------*/ - -/* replies to the request have to be on a FIFO basis so - * we reverse the reversed done-list */ - -td_t * dl_reverse_done_list (ohci_t * ohci) -{ - __u32 td_list_hc; - td_t * td_rev = NULL; - td_t * td_list = NULL; - urb_priv_t * urb_priv = NULL; - unsigned long flags; - - spin_lock_irqsave (&usb_ed_lock, flags); - - td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; - ohci->hcca->done_head = 0; - - while (td_list_hc) { - td_list = dma_to_td (ohci, td_list_hc); - - if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) { - urb_priv = (urb_priv_t *) td_list->urb->hcpriv; - dbg(" USB-error/status: %x : %p", - TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list); - if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) { - if (urb_priv && ((td_list->index + 1) < urb_priv->length)) { - td_list->ed->hwHeadP = - (urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) | - (td_list->ed->hwHeadP & cpu_to_le32 (0x2)); - urb_priv->td_cnt += urb_priv->length - td_list->index - 1; - } else - td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2); - } - } - - td_list->next_dl_td = td_rev; - td_rev = td_list; - td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; - } - spin_unlock_irqrestore (&usb_ed_lock, flags); - return td_list; -} - -/*-------------------------------------------------------------------------*/ - -/* there are some pending requests to remove - * - some of the eds (if ed->state & ED_DEL (set by sohci_free_dev) - * - some URBs/TDs if urb_priv->state == URB_DEL */ - -static void dl_del_list (ohci_t * ohci, unsigned int frame) -{ - unsigned long flags; - ed_t * ed; - __u32 edINFO; - __u32 tdINFO; - td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP; - __u32 * td_p; - int ctrl = 0, bulk = 0; - - spin_lock_irqsave (&usb_ed_lock, flags); - - for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) { - - tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP) & 0xfffffff0); - tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0); - edINFO = le32_to_cpup (&ed->hwINFO); - td_p = &ed->hwHeadP; - - for (td = tdHeadP; td != tdTailP; td = td_next) { - struct urb * urb = td->urb; - urb_priv_t * urb_priv = td->urb->hcpriv; - - td_next = dma_to_td (ohci, le32_to_cpup (&td->hwNextTD) & 0xfffffff0); - if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) { - tdINFO = le32_to_cpup (&td->hwINFO); - if (TD_CC_GET (tdINFO) < 0xE) - dl_transfer_length (td); - *td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3)); - - /* URB is done; clean up */ - if (++(urb_priv->td_cnt) == urb_priv->length) - dl_del_urb (urb); - } else { - td_p = &td->hwNextTD; - } - } - - if (ed->state & ED_DEL) { /* set by sohci_free_dev */ - struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]); - td_free (ohci, tdTailP); /* free dummy td */ - ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); - ed->state = ED_NEW; - hash_free_ed(ohci, ed); - /* if all eds are removed wake up sohci_free_dev */ - if (!--dev->ed_cnt) { - wait_queue_head_t *wait_head = dev->wait; - - dev->wait = 0; - if (wait_head) - wake_up (wait_head); - } - } else { - ed->state &= ~ED_URB_DEL; - tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0); - - if (tdHeadP == tdTailP) { - if (ed->state == ED_OPER) - ep_unlink(ohci, ed); - td_free (ohci, tdTailP); - ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); - ed->state = ED_NEW; - hash_free_ed(ohci, ed); - --(usb_to_ohci (ohci->dev[edINFO & 0x7F]))->ed_cnt; - } else - ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP); - } - - switch (ed->type) { - case PIPE_CONTROL: - ctrl = 1; - break; - case PIPE_BULK: - bulk = 1; - break; - } - } - - /* maybe reenable control and bulk lists */ - if (!ohci->disabled) { - if (ctrl) /* reset control list */ - writel (0, &ohci->regs->ed_controlcurrent); - if (bulk) /* reset bulk list */ - writel (0, &ohci->regs->ed_bulkcurrent); - if (!ohci->ed_rm_list[!frame] && !ohci->sleeping) { - if (ohci->ed_controltail) - ohci->hc_control |= OHCI_CTRL_CLE; - if (ohci->ed_bulktail) - ohci->hc_control |= OHCI_CTRL_BLE; - writel (ohci->hc_control, &ohci->regs->control); - } - } - - ohci->ed_rm_list[frame] = NULL; - spin_unlock_irqrestore (&usb_ed_lock, flags); -} - - - -/*-------------------------------------------------------------------------*/ - -/* td done list */ - -void dl_done_list (ohci_t * ohci, td_t * td_list) -{ - td_t * td_list_next = NULL; - ed_t * ed; - int cc = 0; - struct urb * urb; - urb_priv_t * urb_priv; - __u32 tdINFO, edHeadP, edTailP; - - unsigned long flags; - - while (td_list) { - td_list_next = td_list->next_dl_td; - - urb = td_list->urb; - urb_priv = urb->hcpriv; - tdINFO = le32_to_cpup (&td_list->hwINFO); - - ed = td_list->ed; - - dl_transfer_length(td_list); - - /* error code of transfer */ - cc = TD_CC_GET (tdINFO); - if (cc == TD_CC_STALL) - usb_endpoint_halt(urb->dev, - usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)); - - if (!(urb->transfer_flags & USB_DISABLE_SPD) - && (cc == TD_DATAUNDERRUN)) - cc = TD_CC_NOERROR; - - if (++(urb_priv->td_cnt) == urb_priv->length) { - if ((ed->state & (ED_OPER | ED_UNLINK)) - && (urb_priv->state != URB_DEL)) { - urb->status = cc_to_error[cc]; - sohci_return_urb (ohci, urb); - } else { - spin_lock_irqsave (&usb_ed_lock, flags); - dl_del_urb (urb); - spin_unlock_irqrestore (&usb_ed_lock, flags); - } - } - - spin_lock_irqsave (&usb_ed_lock, flags); - if (ed->state != ED_NEW) { - edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0; - edTailP = le32_to_cpup (&ed->hwTailP); - - /* unlink eds if they are not busy */ - if ((edHeadP == edTailP) && (ed->state == ED_OPER)) - ep_unlink (ohci, ed); - } - spin_unlock_irqrestore (&usb_ed_lock, flags); - - td_list = td_list_next; - } -} - - - - -/*-------------------------------------------------------------------------* - * Virtual Root Hub - *-------------------------------------------------------------------------*/ - -/* Device descriptor */ -static __u8 root_hub_dev_des[] = -{ - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x10, /* __u16 bcdUSB; v1.1 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x02, /* __u8 iProduct; */ - 0x01, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -/* Hub class-specific descriptor is constructed dynamically */ - - -/*-------------------------------------------------------------------------*/ - -/* prepare Interrupt pipe data; HUB INTERRUPT ENDPOINT */ - -static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len) -{ - int num_ports; - int i; - int ret; - int len; - - __u8 data[8]; - - num_ports = roothub_a (ohci) & RH_A_NDP; - if (num_ports > MAX_ROOT_PORTS) { - err ("bogus NDP=%d for OHCI usb-%s", num_ports, - ohci->slot_name); - err ("rereads as NDP=%d", - readl (&ohci->regs->roothub.a) & RH_A_NDP); - /* retry later; "should not happen" */ - return 0; - } - *(__u8 *) data = (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC)) - ? 1: 0; - ret = *(__u8 *) data; - - for ( i = 0; i < num_ports; i++) { - *(__u8 *) (data + (i + 1) / 8) |= - ((roothub_portstatus (ohci, i) & - (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC | RH_PS_PRSC)) - ? 1: 0) << ((i + 1) % 8); - ret += *(__u8 *) (data + (i + 1) / 8); - } - len = i/8 + 1; - - if (ret > 0) { - memcpy(rh_data, data, - min_t(unsigned int, len, - min_t(unsigned int, rh_len, sizeof(data)))); - return len; - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* Virtual Root Hub INTs are polled by this timer every "interval" ms */ - -static void rh_int_timer_do (unsigned long ptr) -{ - int len; - - struct urb * urb = (struct urb *) ptr; - ohci_t * ohci = urb->dev->bus->hcpriv; - - if (ohci->disabled) - return; - - /* ignore timers firing during PM suspend, etc */ - if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) - goto out; - - if(ohci->rh.send) { - len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length); - if (len > 0) { - urb->actual_length = len; -#ifdef DEBUG - urb_print (urb, "RET-t(rh)", usb_pipeout (urb->pipe)); -#endif - if (urb->complete) - urb->complete (urb); - } - } - out: - rh_init_int_timer (urb); -} - -/*-------------------------------------------------------------------------*/ - -/* Root Hub INTs are polled by this timer */ - -static int rh_init_int_timer (struct urb * urb) -{ - ohci_t * ohci = urb->dev->bus->hcpriv; - - ohci->rh.interval = urb->interval; - init_timer (&ohci->rh.rh_int_timer); - ohci->rh.rh_int_timer.function = rh_int_timer_do; - ohci->rh.rh_int_timer.data = (unsigned long) urb; - ohci->rh.rh_int_timer.expires = - jiffies + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000; - add_timer (&ohci->rh.rh_int_timer); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -#define OK(x) len = (x); break -#define WR_RH_STAT(x) writel((x), &ohci->regs->roothub.status) -#define WR_RH_PORTSTAT(x) writel((x), &ohci->regs->roothub.portstatus[wIndex-1]) -#define RD_RH_STAT roothub_status(ohci) -#define RD_RH_PORTSTAT roothub_portstatus(ohci,wIndex-1) - -/* request to virtual root hub */ - -static int rh_submit_urb (struct urb * urb) -{ - struct usb_device * usb_dev = urb->dev; - ohci_t * ohci = usb_dev->bus->hcpriv; - unsigned int pipe = urb->pipe; - struct usb_ctrlrequest * cmd = (struct usb_ctrlrequest *) urb->setup_packet; - void * data = urb->transfer_buffer; - int leni = urb->transfer_buffer_length; - int len = 0; - int status = TD_CC_NOERROR; - - __u32 datab[4]; - __u8 * data_buf = (__u8 *) datab; - - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - - if (usb_pipeint(pipe)) { - ohci->rh.urb = urb; - ohci->rh.send = 1; - ohci->rh.interval = urb->interval; - rh_init_int_timer(urb); - urb->status = cc_to_error [TD_CC_NOERROR]; - - return 0; - } - - bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8); - wValue = le16_to_cpu (cmd->wValue); - wIndex = le16_to_cpu (cmd->wIndex); - wLength = le16_to_cpu (cmd->wLength); - - switch (bmRType_bReq) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - *(__u16 *) data_buf = cpu_to_le16 (1); OK (2); - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = cpu_to_le16 (0); OK (2); - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = cpu_to_le16 (0); OK (2); - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = cpu_to_le32 ( - RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); - OK (4); - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4); - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case RH_C_HUB_LOCAL_POWER: - OK(0); - case (RH_C_HUB_OVER_CURRENT): - WR_RH_STAT(RH_HS_OCIC); OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): - WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); - case (RH_PORT_SUSPEND): - WR_RH_PORTSTAT (RH_PS_POCI); OK (0); - case (RH_PORT_POWER): - WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); - case (RH_C_PORT_CONNECTION): - WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); - case (RH_C_PORT_ENABLE): - WR_RH_PORTSTAT (RH_PS_PESC); OK (0); - case (RH_C_PORT_SUSPEND): - WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); - case (RH_C_PORT_OVER_CURRENT): - WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); - case (RH_C_PORT_RESET): - WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); - case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ - if (RD_RH_PORTSTAT & RH_PS_CCS) - WR_RH_PORTSTAT (RH_PS_PRS); - OK (0); - case (RH_PORT_POWER): - WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); - case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ - if (RD_RH_PORTSTAT & RH_PS_CCS) - WR_RH_PORTSTAT (RH_PS_PES ); - OK (0); - } - break; - - case RH_SET_ADDRESS: ohci->rh.devnum = wValue; OK(0); - - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_dev_des), - wLength)); - data_buf = root_hub_dev_des; OK(len); - case (0x02): /* configuration descriptor */ - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_config_des), - wLength)); - data_buf = root_hub_config_des; OK(len); - case (0x03): /* string descriptors */ - len = usb_root_hub_string (wValue & 0xff, - (int)(long) ohci->regs, "OHCI", - data, wLength); - if (len > 0) { - data_buf = data; - OK(min_t(int, leni, len)); - } - // else fallthrough - default: - status = TD_CC_STALL; - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - { - __u32 temp = roothub_a (ohci); - - data_buf [0] = 9; // min length; - data_buf [1] = 0x29; - data_buf [2] = temp & RH_A_NDP; - data_buf [3] = 0; - if (temp & RH_A_PSM) /* per-port power switching? */ - data_buf [3] |= 0x1; - if (temp & RH_A_NOCP) /* no overcurrent reporting? */ - data_buf [3] |= 0x10; - else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */ - data_buf [3] |= 0x8; - - datab [1] = 0; - data_buf [5] = (temp & RH_A_POTPGT) >> 24; - temp = roothub_b (ohci); - data_buf [7] = temp & RH_B_DR; - if (data_buf [2] < 7) { - data_buf [8] = 0xff; - } else { - data_buf [0] += 2; - data_buf [8] = (temp & RH_B_DR) >> 8; - data_buf [10] = data_buf [9] = 0xff; - } - - len = min_t(unsigned int, leni, - min_t(unsigned int, data_buf [0], wLength)); - OK (len); - } - - case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); - - case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); - - default: - dbg ("unsupported root hub command"); - status = TD_CC_STALL; - } - -#ifdef DEBUG - // ohci_dump_roothub (ohci, 0); -#endif - - len = min_t(int, len, leni); - if (data != data_buf) - memcpy (data, data_buf, len); - urb->actual_length = len; - urb->status = cc_to_error [status]; - -#ifdef DEBUG - urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe)); -#endif - - urb->hcpriv = NULL; - usb_put_dev (usb_dev); - urb->dev = NULL; - if (urb->complete) - urb->complete (urb); - usb_put_urb (urb); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static int rh_unlink_urb (struct urb * urb) -{ - ohci_t * ohci = urb->dev->bus->hcpriv; - - if (ohci->rh.urb == urb) { - ohci->rh.send = 0; - del_timer (&ohci->rh.rh_int_timer); - ohci->rh.urb = NULL; - - urb->hcpriv = NULL; - usb_put_dev (urb->dev); - urb->dev = NULL; - if (urb->transfer_flags & USB_ASYNC_UNLINK) { - urb->status = -ECONNRESET; - if (urb->complete) - urb->complete (urb); - } else - urb->status = -ENOENT; - usb_put_urb (urb); - } - return 0; -} - -/*-------------------------------------------------------------------------* - * HC functions - *-------------------------------------------------------------------------*/ - -/* reset the HC and BUS */ - -int hc_reset (ohci_t * ohci) -{ - int timeout = 30; - int smm_timeout = 50; /* 0,5 sec */ - - if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ - writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */ - dbg("USB HC TakeOver from SMM"); - while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { - wait_ms (10); - if (--smm_timeout == 0) { - err("USB HC TakeOver failed!"); - return -1; - } - } - } - - /* Disable HC interrupts */ - writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); - - dbg("USB HC reset_hc usb-%s: ctrl = 0x%x ;", - ohci->slot_name, - readl (&ohci->regs->control)); - - /* Reset USB (needed by some controllers) */ - writel (0, &ohci->regs->control); - - /* HC Reset requires max 10 ms delay */ - writel (OHCI_HCR, &ohci->regs->cmdstatus); - while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { - if (--timeout == 0) { - err("USB HC reset timed out!"); - return -1; - } - udelay (1); - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* Start an OHCI controller, set the BUS operational - * enable interrupts - * connect the virtual root hub */ - -int hc_start (ohci_t * ohci, struct device *parent_dev) -{ - __u32 mask; - unsigned int fminterval; - struct usb_device * usb_dev; - struct ohci_device * dev; - - ohci->disabled = 1; - - /* Tell the controller where the control and bulk lists are - * The lists are empty now. */ - - writel (0, &ohci->regs->ed_controlhead); - writel (0, &ohci->regs->ed_bulkhead); - - writel (ohci->hcca_dma, &ohci->regs->hcca); /* a reset clears this */ - - fminterval = 0x2edf; - writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); - fminterval |= ((((fminterval - 210) * 6) / 7) << 16); - writel (fminterval, &ohci->regs->fminterval); - writel (0x628, &ohci->regs->lsthresh); - - /* start controller operations */ - ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; - ohci->disabled = 0; - writel (ohci->hc_control, &ohci->regs->control); - - /* Choose the interrupts we care about now, others later on demand */ - mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; - writel (mask, &ohci->regs->intrenable); - writel (mask, &ohci->regs->intrstatus); - -#ifdef OHCI_USE_NPS - /* required for AMD-756 and some Mac platforms */ - writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, - &ohci->regs->roothub.a); - writel (RH_HS_LPSC, &ohci->regs->roothub.status); -#endif /* OHCI_USE_NPS */ - - // POTPGT delay is bits 24-31, in 2 ms units. - mdelay ((roothub_a (ohci) >> 23) & 0x1fe); - - /* connect the virtual root hub */ - ohci->rh.devnum = 0; - usb_dev = usb_alloc_dev (NULL, ohci->bus); - if (!usb_dev) { - ohci->disabled = 1; - return -ENOMEM; - } - - dev = usb_to_ohci (usb_dev); - ohci->bus->root_hub = usb_dev; - usb_connect (usb_dev); - if (usb_register_root_hub (usb_dev, parent_dev) != 0) { - usb_free_dev (usb_dev); - ohci->disabled = 1; - return -ENODEV; - } - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* called only from interrupt handler */ - -static void check_timeouts (struct ohci *ohci) -{ - spin_lock (&usb_ed_lock); - while (!list_empty (&ohci->timeout_list)) { - struct urb *urb; - - urb = list_entry (ohci->timeout_list.next, struct urb, urb_list); - if (time_after (jiffies, urb->timeout)) - break; - - list_del_init (&urb->urb_list); - if (urb->status != -EINPROGRESS) - continue; - - urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK; - spin_unlock (&usb_ed_lock); - - // outside the interrupt handler (in a timer...) - // this reference would race interrupts - sohci_unlink_urb (urb); - - spin_lock (&usb_ed_lock); - } - spin_unlock (&usb_ed_lock); -} - - -/*-------------------------------------------------------------------------*/ - -/* an interrupt happens */ - -static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r) -{ - ohci_t * ohci = __ohci; - struct ohci_regs * regs = ohci->regs; - int ints; - - if ((ohci->hcca->done_head != 0) && !(le32_to_cpup (&ohci->hcca->done_head) & 0x01)) { - ints = OHCI_INTR_WDH; - } else if ((ints = (readl (®s->intrstatus) & readl (®s->intrenable))) == 0) { - return; - } - - // dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); - - if (ints & OHCI_INTR_UE) { - ohci->disabled++; - err ("OHCI Unrecoverable Error, controller usb-%s disabled", - ohci->slot_name); - // e.g. due to PCI Master/Target Abort - -#ifdef DEBUG - ohci_dump (ohci, 1); -#else - // FIXME: be optimistic, hope that bug won't repeat often. - // Make some non-interrupt context restart the controller. - // Count and limit the retries though; either hardware or - // software errors can go forever... -#endif - hc_reset (ohci); - } - - if (ints & OHCI_INTR_WDH) { - writel (OHCI_INTR_WDH, ®s->intrdisable); - dl_done_list (ohci, dl_reverse_done_list (ohci)); - writel (OHCI_INTR_WDH, ®s->intrenable); - } - - if (ints & OHCI_INTR_SO) { - dbg("USB Schedule overrun"); - writel (OHCI_INTR_SO, ®s->intrenable); - } - - // FIXME: this assumes SOF (1/ms) interrupts don't get lost... - if (ints & OHCI_INTR_SF) { - unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1; - writel (OHCI_INTR_SF, ®s->intrdisable); - if (ohci->ed_rm_list[!frame] != NULL) { - dl_del_list (ohci, !frame); - } - if (ohci->ed_rm_list[frame] != NULL) - writel (OHCI_INTR_SF, ®s->intrenable); - } - - if (!list_empty (&ohci->timeout_list)) { - check_timeouts (ohci); -// FIXME: enable SF as needed in a timer; -// don't make lots of 1ms interrupts -// On unloaded USB, think 4k ~= 4-5msec - if (!list_empty (&ohci->timeout_list)) - writel (OHCI_INTR_SF, ®s->intrenable); - } - - writel (ints, ®s->intrstatus); - writel (OHCI_INTR_MIE, ®s->intrenable); -} - -/*-------------------------------------------------------------------------*/ - -/* allocate OHCI */ - -static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base) -{ - ohci_t * ohci; - - ohci = (ohci_t *) kmalloc (sizeof *ohci, GFP_KERNEL); - if (!ohci) - return NULL; - - memset (ohci, 0, sizeof (ohci_t)); - - ohci->hcca = pci_alloc_consistent (dev, sizeof *ohci->hcca, - &ohci->hcca_dma); - if (!ohci->hcca) { - kfree (ohci); - return NULL; - } - memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); - - ohci->disabled = 1; - ohci->sleeping = 0; - ohci->irq = -1; - ohci->regs = mem_base; - - ohci->ohci_dev = dev; -#ifdef CONFIG_PCI - pci_set_drvdata(dev, ohci); -#endif - - INIT_LIST_HEAD (&ohci->ohci_hcd_list); - list_add (&ohci->ohci_hcd_list, &ohci_hcd_list); - - INIT_LIST_HEAD (&ohci->timeout_list); - - ohci->bus = usb_alloc_bus (&sohci_device_operations); - if (!ohci->bus) { -#ifdef CONFIG_PCI - pci_set_drvdata (dev, NULL); -#endif - pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca, - ohci->hcca, ohci->hcca_dma); - kfree (ohci); - return NULL; - } - ohci->bus->hcpriv = (void *) ohci; -#ifdef CONFIG_PCI - ohci->bus->bus_name = dev->slot_name; -#else - ohci->bus->bus_name = "ohci-hc"; -#endif - - return ohci; -} - - -/*-------------------------------------------------------------------------*/ - -/* De-allocate all resources.. */ - -static void hc_release_ohci (ohci_t * ohci) -{ - dbg ("USB HC release ohci usb-%s", ohci->slot_name); - - /* disconnect all devices */ - if (ohci->bus->root_hub) - usb_disconnect (&ohci->bus->root_hub); - - if (!ohci->disabled) - hc_reset (ohci); - - if (ohci->irq >= 0) { - free_irq (ohci->irq, ohci); - ohci->irq = -1; - } -#ifdef CONFIG_PCI - pci_set_drvdata(ohci->ohci_dev, NULL); -#endif - if (ohci->bus) { - if (ohci->bus->busnum) - usb_deregister_bus (ohci->bus); - usb_free_bus (ohci->bus); - } - - list_del (&ohci->ohci_hcd_list); - INIT_LIST_HEAD (&ohci->ohci_hcd_list); - - ohci_mem_cleanup (ohci); - - pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca, - ohci->hcca, ohci->hcca_dma); - kfree (ohci); -} - -/*-------------------------------------------------------------------------*/ - -/* - * Host bus independent add one OHCI host controller. - */ -int -hc_add_ohci(struct pci_dev *dev, int irq, void *mem_base, unsigned long flags, - ohci_t **ohcip, const char *name, const char *slot_name) -{ - char buf[8], *bufp = buf; - ohci_t * ohci; - int ret; - -#ifndef __sparc__ - sprintf(buf, "%d", irq); -#else - bufp = __irq_itoa(irq); -#endif - printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n", - (unsigned long) mem_base, bufp); - - ohci = hc_alloc_ohci (dev, mem_base); - if (!ohci) { - return -ENOMEM; - } - ohci->slot_name = slot_name; - if ((ret = ohci_mem_init (ohci)) < 0) { - hc_release_ohci (ohci); - return ret; - } - ohci->flags = flags; - if (ohci->flags & OHCI_QUIRK_AMD756) - printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n"); - - if (hc_reset (ohci) < 0) { - hc_release_ohci (ohci); - return -ENODEV; - } - - /* FIXME this is a second HC reset; why?? */ - writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control); - wait_ms (10); - - usb_register_bus (ohci->bus); - - if (request_irq (irq, hc_interrupt, SA_SHIRQ, name, ohci) != 0) { - err ("request interrupt %s failed", bufp); - hc_release_ohci (ohci); - return -EBUSY; - } - ohci->irq = irq; - - *ohcip = ohci; - - return 0; -} - -/* - * Host bus independent remove one OHCI host controller. - */ -void hc_remove_ohci(ohci_t *ohci) -{ -#ifdef DEBUG - ohci_dump (ohci, 1); -#endif - - /* don't wake up sleeping controllers, or block in interrupt context */ - if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER || in_interrupt ()) { - dbg ("controller being disabled"); - ohci->disabled = 1; - } - - /* on return, USB will always be reset (if present) */ - if (ohci->disabled) - writel (ohci->hc_control = OHCI_USB_RESET, - &ohci->regs->control); - - hc_release_ohci (ohci); -} - -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); - -EXPORT_SYMBOL(hc_add_ohci); -EXPORT_SYMBOL(hc_remove_ohci); -EXPORT_SYMBOL(hc_start); -EXPORT_SYMBOL(hc_reset); -EXPORT_SYMBOL(dl_done_list); -EXPORT_SYMBOL(dl_reverse_done_list); -EXPORT_SYMBOL(usb_ed_lock); diff --git a/drivers/usb/host/usb-ohci.h b/drivers/usb/host/usb-ohci.h deleted file mode 100644 index c04bb49a003f..000000000000 --- a/drivers/usb/host/usb-ohci.h +++ /dev/null @@ -1,648 +0,0 @@ -/* - * URB OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> - * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net> - * - * usb-ohci.h - */ - - -static int cc_to_error[16] = { - -/* mapping of the OHCI CC status to error codes */ - /* No Error */ 0, - /* CRC Error */ -EILSEQ, - /* Bit Stuff */ -EPROTO, - /* Data Togg */ -EILSEQ, - /* Stall */ -EPIPE, - /* DevNotResp */ -ETIMEDOUT, - /* PIDCheck */ -EPROTO, - /* UnExpPID */ -EPROTO, - /* DataOver */ -EOVERFLOW, - /* DataUnder */ -EREMOTEIO, - /* reservd */ -ETIMEDOUT, - /* reservd */ -ETIMEDOUT, - /* BufferOver */ -ECOMM, - /* BuffUnder */ -ENOSR, - /* Not Access */ -ETIMEDOUT, - /* Not Access */ -ETIMEDOUT -}; - -#include <linux/config.h> - -/* ED States */ - -#define ED_NEW 0x00 -#define ED_UNLINK 0x01 -#define ED_OPER 0x02 -#define ED_DEL 0x04 -#define ED_URB_DEL 0x08 - -/* usb_ohci_ed */ -struct ed { - __u32 hwINFO; - __u32 hwTailP; - __u32 hwHeadP; - __u32 hwNextED; - - struct ed * ed_prev; - __u8 int_period; - __u8 int_branch; - __u8 int_load; - __u8 int_interval; - __u8 state; - __u8 type; - __u16 last_iso; - struct ed * ed_rm_list; - - dma_addr_t dma; - __u32 unused[3]; -} __attribute((aligned(16))); -typedef struct ed ed_t; - - -/* TD info field */ -#define TD_CC 0xf0000000 -#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) -#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) -#define TD_EC 0x0C000000 -#define TD_T 0x03000000 -#define TD_T_DATA0 0x02000000 -#define TD_T_DATA1 0x03000000 -#define TD_T_TOGGLE 0x00000000 -#define TD_R 0x00040000 -#define TD_DI 0x00E00000 -#define TD_DI_SET(X) (((X) & 0x07)<< 21) -#define TD_DP 0x00180000 -#define TD_DP_SETUP 0x00000000 -#define TD_DP_IN 0x00100000 -#define TD_DP_OUT 0x00080000 - -#define TD_ISO 0x00010000 -#define TD_DEL 0x00020000 - -/* CC Codes */ -#define TD_CC_NOERROR 0x00 -#define TD_CC_CRC 0x01 -#define TD_CC_BITSTUFFING 0x02 -#define TD_CC_DATATOGGLEM 0x03 -#define TD_CC_STALL 0x04 -#define TD_DEVNOTRESP 0x05 -#define TD_PIDCHECKFAIL 0x06 -#define TD_UNEXPECTEDPID 0x07 -#define TD_DATAOVERRUN 0x08 -#define TD_DATAUNDERRUN 0x09 -#define TD_BUFFEROVERRUN 0x0C -#define TD_BUFFERUNDERRUN 0x0D -#define TD_NOTACCESSED 0x0F - - -#define MAXPSW 1 - -struct td { - __u32 hwINFO; - __u32 hwCBP; /* Current Buffer Pointer */ - __u32 hwNextTD; /* Next TD Pointer */ - __u32 hwBE; /* Memory Buffer End Pointer */ - - __u16 hwPSW[MAXPSW]; - __u8 unused; - __u8 index; - struct ed * ed; - struct td * next_dl_td; - struct urb * urb; - - dma_addr_t td_dma; - dma_addr_t data_dma; - __u32 unused2[2]; -} __attribute((aligned(32))); /* normally 16, iso needs 32 */ -typedef struct td td_t; - -#define OHCI_ED_SKIP (1 << 14) - -/* - * The HCCA (Host Controller Communications Area) is a 256 byte - * structure defined in the OHCI spec. that the host controller is - * told the base address of. It must be 256-byte aligned. - */ - -#define NUM_INTS 32 /* part of the OHCI standard */ -struct ohci_hcca { - __u32 int_table[NUM_INTS]; /* Interrupt ED table */ - __u16 frame_no; /* current frame number */ - __u16 pad1; /* set to 0 on each frame_no change */ - __u32 done_head; /* info returned for an interrupt */ - u8 reserved_for_hc[116]; -} __attribute((aligned(256))); - - -/* - * Maximum number of root hub ports. - */ -#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ - -/* - * This is the structure of the OHCI controller's memory mapped I/O - * region. This is Memory Mapped I/O. You must use the readl() and - * writel() macros defined in asm/io.h to access these!! - */ -struct ohci_regs { - /* control and status registers */ - __u32 revision; - __u32 control; - __u32 cmdstatus; - __u32 intrstatus; - __u32 intrenable; - __u32 intrdisable; - /* memory pointers */ - __u32 hcca; - __u32 ed_periodcurrent; - __u32 ed_controlhead; - __u32 ed_controlcurrent; - __u32 ed_bulkhead; - __u32 ed_bulkcurrent; - __u32 donehead; - /* frame counters */ - __u32 fminterval; - __u32 fmremaining; - __u32 fmnumber; - __u32 periodicstart; - __u32 lsthresh; - /* Root hub ports */ - struct ohci_roothub_regs { - __u32 a; - __u32 b; - __u32 status; - __u32 portstatus[MAX_ROOT_PORTS]; - } roothub; -} __attribute((aligned(32))); - - -/* OHCI CONTROL AND STATUS REGISTER MASKS */ - -/* - * HcControl (control) register masks - */ -#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ -#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ -#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ -#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ -#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ -#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ -#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ -#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ -#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ - -/* pre-shifted values for HCFS */ -# define OHCI_USB_RESET (0 << 6) -# define OHCI_USB_RESUME (1 << 6) -# define OHCI_USB_OPER (2 << 6) -# define OHCI_USB_SUSPEND (3 << 6) - -/* - * HcCommandStatus (cmdstatus) register masks - */ -#define OHCI_HCR (1 << 0) /* host controller reset */ -#define OHCI_CLF (1 << 1) /* control list filled */ -#define OHCI_BLF (1 << 2) /* bulk list filled */ -#define OHCI_OCR (1 << 3) /* ownership change request */ -#define OHCI_SOC (3 << 16) /* scheduling overrun count */ - -/* - * masks used with interrupt registers: - * HcInterruptStatus (intrstatus) - * HcInterruptEnable (intrenable) - * HcInterruptDisable (intrdisable) - */ -#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ -#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ -#define OHCI_INTR_SF (1 << 2) /* start frame */ -#define OHCI_INTR_RD (1 << 3) /* resume detect */ -#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ -#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ -#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ -#define OHCI_INTR_OC (1 << 30) /* ownership change */ -#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ - - - -/* Virtual Root HUB */ -struct virt_root_hub { - int devnum; /* Address of Root Hub endpoint */ - void * urb; - void * int_addr; - int send; - int interval; - struct timer_list rh_int_timer; -}; - - -/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ - -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 - -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -#define RH_ACK 0x01 -#define RH_REQ_ERR -1 -#define RH_NACK 0x00 - - -/* OHCI ROOT HUB REGISTER MASKS */ - -/* roothub.portstatus [i] bits */ -#define RH_PS_CCS 0x00000001 /* current connect status */ -#define RH_PS_PES 0x00000002 /* port enable status*/ -#define RH_PS_PSS 0x00000004 /* port suspend status */ -#define RH_PS_POCI 0x00000008 /* port over current indicator */ -#define RH_PS_PRS 0x00000010 /* port reset status */ -#define RH_PS_PPS 0x00000100 /* port power status */ -#define RH_PS_LSDA 0x00000200 /* low speed device attached */ -#define RH_PS_CSC 0x00010000 /* connect status change */ -#define RH_PS_PESC 0x00020000 /* port enable status change */ -#define RH_PS_PSSC 0x00040000 /* port suspend status change */ -#define RH_PS_OCIC 0x00080000 /* over current indicator change */ -#define RH_PS_PRSC 0x00100000 /* port reset status change */ - -/* roothub.status bits */ -#define RH_HS_LPS 0x00000001 /* local power status */ -#define RH_HS_OCI 0x00000002 /* over current indicator */ -#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ -#define RH_HS_LPSC 0x00010000 /* local power status change */ -#define RH_HS_OCIC 0x00020000 /* over current indicator change */ -#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ - -/* roothub.b masks */ -#define RH_B_DR 0x0000ffff /* device removable flags */ -#define RH_B_PPCM 0xffff0000 /* port power control mask */ - -/* roothub.a masks */ -#define RH_A_NDP (0xff << 0) /* number of downstream ports */ -#define RH_A_PSM (1 << 8) /* power switching mode */ -#define RH_A_NPS (1 << 9) /* no power switching */ -#define RH_A_DT (1 << 10) /* device type (mbz) */ -#define RH_A_OCPM (1 << 11) /* over current protection mode */ -#define RH_A_NOCP (1 << 12) /* no over current protection */ -#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ - -/* urb */ -typedef struct -{ - ed_t * ed; - __u16 length; // number of tds associated with this request - __u16 td_cnt; // number of tds already serviced - int state; - wait_queue_head_t * wait; - td_t * td[0]; // list pointer to all corresponding TDs associated with this request - -} urb_priv_t; -#define URB_DEL 1 - - -/* Hash struct used for TD/ED hashing */ -struct hash_t { - void *virt; - dma_addr_t dma; - struct hash_t *next; // chaining for collision cases -}; - -/* List of TD/ED hash entries */ -struct hash_list_t { - struct hash_t *head; - struct hash_t *tail; -}; - -#define TD_HASH_SIZE 64 /* power'o'two */ -#define ED_HASH_SIZE 64 /* power'o'two */ - -#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 5)) % TD_HASH_SIZE) -#define ED_HASH_FUNC(ed_dma) ((ed_dma ^ (ed_dma >> 5)) % ED_HASH_SIZE) - - -/* - * This is the full ohci controller description - * - * Note how the "proper" USB information is just - * a subset of what the full implementation needs. (Linus) - */ - - -typedef struct ohci { - struct ohci_hcca *hcca; /* hcca */ - dma_addr_t hcca_dma; - - int irq; - int disabled; /* e.g. got a UE, we're hung */ - int sleeping; - atomic_t resume_count; /* defending against multiple resumes */ - unsigned long flags; /* for HC bugs */ -#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ - - struct ohci_regs * regs; /* OHCI controller's memory */ - struct list_head ohci_hcd_list; /* list of all ohci_hcd */ - - struct ohci * next; // chain of ohci device contexts - struct list_head timeout_list; - // struct list_head urb_list; // list of all pending urbs - // spinlock_t urb_list_lock; // lock to keep consistency - - int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/ - ed_t * ed_rm_list[2]; /* lists of all endpoints to be removed */ - ed_t * ed_bulktail; /* last endpoint of bulk list */ - ed_t * ed_controltail; /* last endpoint of control list */ - ed_t * ed_isotail; /* last endpoint of iso list */ - int intrstatus; - __u32 hc_control; /* copy of the hc control reg */ - struct usb_bus * bus; - struct usb_device * dev[128]; - struct virt_root_hub rh; - - /* PCI device handle, settings, ... */ - struct pci_dev *ohci_dev; - const char *slot_name; - u8 pci_latency; - struct pci_pool *td_cache; - struct pci_pool *dev_cache; - struct hash_list_t td_hash[TD_HASH_SIZE]; - struct hash_list_t ed_hash[ED_HASH_SIZE]; - -} ohci_t; - -#define NUM_EDS 32 /* num of preallocated endpoint descriptors */ - -struct ohci_device { - ed_t ed[NUM_EDS]; - dma_addr_t dma; - int ed_cnt; - wait_queue_head_t * wait; -}; - -// #define ohci_to_usb(ohci) ((ohci)->usb) -#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv) - -/* For initializing controller (mask in an HCFS mode too) */ -#define OHCI_CONTROL_INIT \ - (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE - -/* hcd */ -/* endpoint */ -static int ep_link(ohci_t * ohci, ed_t * ed); -static int ep_unlink(ohci_t * ohci, ed_t * ed); -static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load, int mem_flags); -static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed); -/* td */ -static void td_fill(ohci_t * ohci, unsigned int info, dma_addr_t data, int len, struct urb * urb, int index); -static void td_submit_urb(struct urb * urb); -/* root hub */ -static int rh_submit_urb(struct urb * urb); -static int rh_unlink_urb(struct urb * urb); -static int rh_init_int_timer(struct urb * urb); - -/*-------------------------------------------------------------------------*/ - -#define ALLOC_FLAGS (in_interrupt () ? GFP_ATOMIC : GFP_KERNEL) - -#ifdef DEBUG -# define OHCI_MEM_FLAGS SLAB_POISON -#else -# define OHCI_MEM_FLAGS 0 -#endif - - -/* Recover a TD/ED using its collision chain */ -static void * -dma_to_ed_td (struct hash_list_t * entry, dma_addr_t dma) -{ - struct hash_t * scan = entry->head; - while (scan && scan->dma != dma) - scan = scan->next; - if (!scan) - BUG(); - return scan->virt; -} - -static struct ed * -dma_to_ed (struct ohci * hc, dma_addr_t ed_dma) -{ - return (struct ed *) dma_to_ed_td(&(hc->ed_hash[ED_HASH_FUNC(ed_dma)]), - ed_dma); -} - -static struct td * -dma_to_td (struct ohci * hc, dma_addr_t td_dma) -{ - return (struct td *) dma_to_ed_td(&(hc->td_hash[TD_HASH_FUNC(td_dma)]), - td_dma); -} - -/* Add a hash entry for a TD/ED; return true on success */ -static int -hash_add_ed_td(struct hash_list_t * entry, void * virt, dma_addr_t dma) -{ - struct hash_t * scan; - - scan = (struct hash_t *)kmalloc(sizeof(struct hash_t), ALLOC_FLAGS); - if (!scan) - return 0; - - if (!entry->tail) { - entry->head = entry->tail = scan; - } else { - entry->tail->next = scan; - entry->tail = scan; - } - - scan->virt = virt; - scan->dma = dma; - scan->next = NULL; - return 1; -} - -static int -hash_add_ed (struct ohci * hc, struct ed * ed) -{ - return hash_add_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]), - ed, ed->dma); -} - -static int -hash_add_td (struct ohci * hc, struct td * td) -{ - return hash_add_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]), - td, td->td_dma); -} - - -static void -hash_free_ed_td (struct hash_list_t * entry, void * virt) -{ - struct hash_t *scan, *prev; - scan = prev = entry->head; - - // Find and unlink hash entry - while (scan && scan->virt != virt) { - prev = scan; - scan = scan->next; - } - if (scan) { - if (scan == entry->head) { - if (entry->head == entry->tail) - entry->head = entry->tail = NULL; - else - entry->head = scan->next; - } else if (scan == entry->tail) { - entry->tail = prev; - prev->next = NULL; - } else - prev->next = scan->next; - kfree(scan); - } -} - -static void -hash_free_ed (struct ohci * hc, struct ed * ed) -{ - hash_free_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]), ed); -} - -static void -hash_free_td (struct ohci * hc, struct td * td) -{ - hash_free_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]), td); -} - - -static int ohci_mem_init (struct ohci *ohci) -{ - ohci->td_cache = pci_pool_create ("ohci_td", ohci->ohci_dev, - sizeof (struct td), - 32 /* byte alignment */, - 0 /* no page-crossing issues */, - GFP_KERNEL | OHCI_MEM_FLAGS); - if (!ohci->td_cache) - return -ENOMEM; - ohci->dev_cache = pci_pool_create ("ohci_dev", ohci->ohci_dev, - sizeof (struct ohci_device), - 16 /* byte alignment */, - 0 /* no page-crossing issues */, - GFP_KERNEL | OHCI_MEM_FLAGS); - if (!ohci->dev_cache) - return -ENOMEM; - return 0; -} - -static void ohci_mem_cleanup (struct ohci *ohci) -{ - if (ohci->td_cache) { - pci_pool_destroy (ohci->td_cache); - ohci->td_cache = 0; - } - if (ohci->dev_cache) { - pci_pool_destroy (ohci->dev_cache); - ohci->dev_cache = 0; - } -} - -/* TDs ... */ -static struct td * -td_alloc (struct ohci *hc, int mem_flags) -{ - dma_addr_t dma; - struct td *td; - - td = pci_pool_alloc (hc->td_cache, mem_flags, &dma); - if (td) { - td->td_dma = dma; - - /* hash it for later reverse mapping */ - if (!hash_add_td (hc, td)) { - pci_pool_free (hc->td_cache, td, dma); - return NULL; - } - } - return td; -} - -static inline void -td_free (struct ohci *hc, struct td *td) -{ - hash_free_td (hc, td); - pci_pool_free (hc->td_cache, td, td->td_dma); -} - - -/* DEV + EDs ... only the EDs need to be consistent */ -static struct ohci_device * -dev_alloc (struct ohci *hc, int mem_flags) -{ - dma_addr_t dma; - struct ohci_device *dev; - int i, offset; - - dev = pci_pool_alloc (hc->dev_cache, mem_flags, &dma); - if (dev) { - memset (dev, 0, sizeof (*dev)); - dev->dma = dma; - offset = ((char *)&dev->ed) - ((char *)dev); - for (i = 0; i < NUM_EDS; i++, offset += sizeof dev->ed [0]) - dev->ed [i].dma = dma + offset; - /* add to hashtable if used */ - } - return dev; -} - -static inline void -dev_free (struct ohci *hc, struct ohci_device *dev) -{ - pci_pool_free (hc->dev_cache, dev, dev->dma); -} - -extern spinlock_t usb_ed_lock; -extern void dl_done_list (ohci_t * ohci, td_t * td_list); -extern td_t * dl_reverse_done_list (ohci_t * ohci); - - diff --git a/drivers/usb/image/hpusbscsi.c b/drivers/usb/image/hpusbscsi.c index 58c311db3c4e..5d02c88fcf1b 100644 --- a/drivers/usb/image/hpusbscsi.c +++ b/drivers/usb/image/hpusbscsi.c @@ -164,10 +164,10 @@ MODULE_LICENSE("GPL"); static struct usb_driver hpusbscsi_usb_driver = { - name:"hpusbscsi", - probe:hpusbscsi_usb_probe, - disconnect:hpusbscsi_usb_disconnect, - id_table:hpusbscsi_usb_ids, + .name ="hpusbscsi", + .probe =hpusbscsi_usb_probe, + .disconnect =hpusbscsi_usb_disconnect, + .id_table =hpusbscsi_usb_ids, }; /* module initialisation */ diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index 6514054a8813..662dba287d3d 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -923,11 +923,11 @@ static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t l /* File Operations of this drivers */ static struct file_operations mdc800_device_ops = { - owner: THIS_MODULE, - read: mdc800_device_read, - write: mdc800_device_write, - open: mdc800_device_open, - release: mdc800_device_release, + .owner = THIS_MODULE, + .read = mdc800_device_read, + .write = mdc800_device_write, + .open = mdc800_device_open, + .release = mdc800_device_release, }; @@ -943,11 +943,11 @@ MODULE_DEVICE_TABLE (usb, mdc800_table); */ static struct usb_driver mdc800_usb_driver = { - owner: THIS_MODULE, - name: "mdc800", - probe: mdc800_usb_probe, - disconnect: mdc800_usb_disconnect, - id_table: mdc800_table + .owner = THIS_MODULE, + .name = "mdc800", + .probe = mdc800_usb_probe, + .disconnect = mdc800_usb_disconnect, + .id_table = mdc800_table }; diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 6b2591e7e071..dd454fd08fab 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -161,10 +161,10 @@ static void mts_usb_disconnect(struct usb_device *dev, void *ptr); static struct usb_device_id mts_usb_ids []; static struct usb_driver mts_usb_driver = { - name: "microtekX6", - probe: mts_usb_probe, - disconnect: mts_usb_disconnect, - id_table: mts_usb_ids, + .name = "microtekX6", + .probe = mts_usb_probe, + .disconnect = mts_usb_disconnect, + .id_table = mts_usb_ids, }; @@ -743,22 +743,22 @@ out: static Scsi_Host_Template mts_scsi_host_template = { - name: "microtekX6", - detect: mts_scsi_detect, - release: mts_scsi_release, - queuecommand: mts_scsi_queuecommand, - - eh_abort_handler: mts_scsi_abort, - eh_host_reset_handler: mts_scsi_host_reset, - - sg_tablesize: SG_ALL, - can_queue: 1, - this_id: -1, - cmd_per_lun: 1, - present: 0, - unchecked_isa_dma: FALSE, - use_clustering: TRUE, - emulated: TRUE + .name = "microtekX6", + .detect = mts_scsi_detect, + .release = mts_scsi_release, + .queuecommand = mts_scsi_queuecommand, + + .eh_abort_handler = mts_scsi_abort, + .eh_host_reset_handler =mts_scsi_host_reset, + + .sg_tablesize = SG_ALL, + .can_queue = 1, + .this_id = -1, + .cmd_per_lun = 1, + .present = 0, + .unchecked_isa_dma = FALSE, + .use_clustering = TRUE, + .emulated = TRUE }; diff --git a/drivers/usb/image/scanner.c b/drivers/usb/image/scanner.c index 2bb1de40e3c0..9c9550e5cce7 100644 --- a/drivers/usb/image/scanner.c +++ b/drivers/usb/image/scanner.c @@ -811,11 +811,11 @@ ioctl_scanner(struct inode *inode, struct file *file, static struct file_operations usb_scanner_fops = { - read: read_scanner, - write: write_scanner, - ioctl: ioctl_scanner, - open: open_scanner, - release: close_scanner, + .read = read_scanner, + .write = write_scanner, + .ioctl = ioctl_scanner, + .open = open_scanner, + .release = close_scanner, }; static void * @@ -1116,10 +1116,10 @@ disconnect_scanner(struct usb_device *dev, void *ptr) static struct usb_driver scanner_driver = { - name: "usbscanner", - probe: probe_scanner, - disconnect: disconnect_scanner, - id_table: NULL, /* This would be scanner_device_ids, but we + .name = "usbscanner", + .probe = probe_scanner, + .disconnect = disconnect_scanner, + .id_table = NULL, /* This would be scanner_device_ids, but we need to check every USB device, in case we match a user defined vendor/product ID. */ }; diff --git a/drivers/usb/input/aiptek.c b/drivers/usb/input/aiptek.c index cbed3efbb7c4..764f52074649 100644 --- a/drivers/usb/input/aiptek.c +++ b/drivers/usb/input/aiptek.c @@ -313,10 +313,10 @@ aiptek_disconnect(struct usb_device *dev, void *ptr) } static struct usb_driver aiptek_driver = { - name:"aiptek", - probe:aiptek_probe, - disconnect:aiptek_disconnect, - id_table:aiptek_ids, + .name ="aiptek", + .probe =aiptek_probe, + .disconnect =aiptek_disconnect, + .id_table =aiptek_ids, }; static int __init diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 068417749db6..19b6ff130baf 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -1556,10 +1556,10 @@ static struct usb_device_id hid_usb_ids [] = { MODULE_DEVICE_TABLE (usb, hid_usb_ids); static struct usb_driver hid_driver = { - name: "hid", - probe: hid_probe, - disconnect: hid_disconnect, - id_table: hid_usb_ids, + .name = "hid", + .probe = hid_probe, + .disconnect = hid_disconnect, + .id_table = hid_usb_ids, }; static int __init hid_init(void) diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c index 563285d58541..93dcc5184758 100644 --- a/drivers/usb/input/hiddev.c +++ b/drivers/usb/input/hiddev.c @@ -657,14 +657,14 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd } static struct file_operations hiddev_fops = { - owner: THIS_MODULE, - read: hiddev_read, - write: hiddev_write, - poll: hiddev_poll, - open: hiddev_open, - release: hiddev_release, - ioctl: hiddev_ioctl, - fasync: hiddev_fasync, + .owner = THIS_MODULE, + .read = hiddev_read, + .write = hiddev_write, + .poll = hiddev_poll, + .open = hiddev_open, + .release = hiddev_release, + .ioctl = hiddev_ioctl, + .fasync = hiddev_fasync, }; /* @@ -759,8 +759,8 @@ static void *hiddev_usbd_probe(struct usb_device *dev, unsigned int ifnum, static /* const */ struct usb_driver hiddev_driver = { - name: "hiddev", - probe: hiddev_usbd_probe, + .name = "hiddev", + .probe = hiddev_usbd_probe, }; int __init hiddev_init(void) diff --git a/drivers/usb/input/powermate.c b/drivers/usb/input/powermate.c index 378cfa3c0717..9dd719300002 100644 --- a/drivers/usb/input/powermate.c +++ b/drivers/usb/input/powermate.c @@ -334,10 +334,10 @@ static struct usb_device_id powermate_devices [] = { MODULE_DEVICE_TABLE (usb, powermate_devices); static struct usb_driver powermate_driver = { - name: "powermate", - probe: powermate_probe, - disconnect: powermate_disconnect, - id_table: powermate_devices, + .name = "powermate", + .probe = powermate_probe, + .disconnect = powermate_disconnect, + .id_table = powermate_devices, }; int powermate_init(void) diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c index 31edd4e7b52b..b9b3e0923cb5 100644 --- a/drivers/usb/input/usbkbd.c +++ b/drivers/usb/input/usbkbd.c @@ -287,10 +287,10 @@ static struct usb_device_id usb_kbd_id_table [] = { MODULE_DEVICE_TABLE (usb, usb_kbd_id_table); static struct usb_driver usb_kbd_driver = { - name: "keyboard", - probe: usb_kbd_probe, - disconnect: usb_kbd_disconnect, - id_table: usb_kbd_id_table, + .name = "keyboard", + .probe = usb_kbd_probe, + .disconnect = usb_kbd_disconnect, + .id_table = usb_kbd_id_table, }; static int __init usb_kbd_init(void) diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c index 22673b42d60a..cfec38752242 100644 --- a/drivers/usb/input/usbmouse.c +++ b/drivers/usb/input/usbmouse.c @@ -195,10 +195,10 @@ static struct usb_device_id usb_mouse_id_table [] = { MODULE_DEVICE_TABLE (usb, usb_mouse_id_table); static struct usb_driver usb_mouse_driver = { - name: "usb_mouse", - probe: usb_mouse_probe, - disconnect: usb_mouse_disconnect, - id_table: usb_mouse_id_table, + .name = "usb_mouse", + .probe = usb_mouse_probe, + .disconnect = usb_mouse_disconnect, + .id_table = usb_mouse_id_table, }; static int __init usb_mouse_init(void) diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c index 74bfb073e78d..2d1c8f4ec2d7 100644 --- a/drivers/usb/input/wacom.c +++ b/drivers/usb/input/wacom.c @@ -428,10 +428,10 @@ static void wacom_disconnect(struct usb_device *dev, void *ptr) } static struct usb_driver wacom_driver = { - name: "wacom", - probe: wacom_probe, - disconnect: wacom_disconnect, - id_table: wacom_ids, + .name = "wacom", + .probe = wacom_probe, + .disconnect = wacom_disconnect, + .id_table = wacom_ids, }; static int __init wacom_init(void) diff --git a/drivers/usb/input/xpad.c b/drivers/usb/input/xpad.c index ee3ae7fbd648..ace599597455 100644 --- a/drivers/usb/input/xpad.c +++ b/drivers/usb/input/xpad.c @@ -304,10 +304,10 @@ static void xpad_disconnect(struct usb_device *udev, void *ptr) } static struct usb_driver xpad_driver = { - name: "xpad", - probe: xpad_probe, - disconnect: xpad_disconnect, - id_table: xpad_table, + .name = "xpad", + .probe = xpad_probe, + .disconnect = xpad_disconnect, + .id_table = xpad_table, }; static int __init usb_xpad_init(void) diff --git a/drivers/usb/media/dabusb.c b/drivers/usb/media/dabusb.c index 3658e693484f..50d16e4fd6c7 100644 --- a/drivers/usb/media/dabusb.c +++ b/drivers/usb/media/dabusb.c @@ -704,12 +704,12 @@ static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cm static struct file_operations dabusb_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: dabusb_read, - ioctl: dabusb_ioctl, - open: dabusb_open, - release: dabusb_release, + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = dabusb_read, + .ioctl = dabusb_ioctl, + .open = dabusb_open, + .release = dabusb_release, }; static int dabusb_find_struct (void) @@ -806,10 +806,10 @@ MODULE_DEVICE_TABLE (usb, dabusb_ids); static struct usb_driver dabusb_driver = { - name: "dabusb", - probe: dabusb_probe, - disconnect: dabusb_disconnect, - id_table: dabusb_ids, + .name = "dabusb", + .probe = dabusb_probe, + .disconnect = dabusb_disconnect, + .id_table = dabusb_ids, }; /* --------------------------------------------------------------------- */ diff --git a/drivers/usb/media/dsbr100.c b/drivers/usb/media/dsbr100.c index f4bdcaaa1fd3..ddaef26aa8eb 100644 --- a/drivers/usb/media/dsbr100.c +++ b/drivers/usb/media/dsbr100.c @@ -100,19 +100,19 @@ typedef struct static struct file_operations usb_dsbr100_fops = { - owner: THIS_MODULE, - open: usb_dsbr100_open, - release: usb_dsbr100_close, - ioctl: usb_dsbr100_ioctl, - llseek: no_llseek, + .owner = THIS_MODULE, + .open = usb_dsbr100_open, + .release = usb_dsbr100_close, + .ioctl = usb_dsbr100_ioctl, + .llseek = no_llseek, }; static struct video_device usb_dsbr100_radio= { - owner: THIS_MODULE, - name: "D-Link DSB R-100 USB radio", - type: VID_TYPE_TUNER, - hardware: VID_HARDWARE_AZTECH, - fops: &usb_dsbr100_fops, + .owner = THIS_MODULE, + .name = "D-Link DSB R-100 USB radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_AZTECH, + .fops = &usb_dsbr100_fops, }; static int users = 0; @@ -125,10 +125,10 @@ static struct usb_device_id usb_dsbr100_table [] = { MODULE_DEVICE_TABLE (usb, usb_dsbr100_table); static struct usb_driver usb_dsbr100_driver = { - name: "dsbr100", - probe: usb_dsbr100_probe, - disconnect: usb_dsbr100_disconnect, - id_table: usb_dsbr100_table, + .name = "dsbr100", + .probe = usb_dsbr100_probe, + .disconnect = usb_dsbr100_disconnect, + .id_table = usb_dsbr100_table, }; diff --git a/drivers/usb/media/ov511.c b/drivers/usb/media/ov511.c index e5b3f9144fb4..9dff57ad4f38 100644 --- a/drivers/usb/media/ov511.c +++ b/drivers/usb/media/ov511.c @@ -9,7 +9,6 @@ * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org> * Changes by Claudio Matsuoka <claudio@conectiva.com> * Original SAA7111A code by Dave Perks <dperks@ibm.net> - * Kernel I2C interface adapted from nt1003 driver * URB error messages from pwc driver by Nemosoft * generic_ioctl() code from videodev.c by Gerd Knorr and Alan Cox * Memory management (rvmalloc) code from bttv driver, by Gerd Knorr and others @@ -61,7 +60,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.60a for Linux 2.5" +#define DRIVER_VERSION "v1.61 for Linux 2.5" #define EMAIL "mmcclell@bigfoot.com" #define DRIVER_AUTHOR "Mark McClelland <mmcclell@bigfoot.com> & Bret Wallach \ & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \ @@ -72,7 +71,6 @@ #define ENABLE_Y_QUANTABLE 1 #define ENABLE_UV_QUANTABLE 1 -/* If you change this, you must also change the MODULE_PARM definition */ #define OV511_MAX_UNIT_VIDEO 16 /* Pixel count * bytes per YUV420 pixel (1.5) */ @@ -127,7 +125,6 @@ static int framedrop = -1; static int fastset; static int force_palette; -static int tuner = -1; static int backlight; static int unit_video[OV511_MAX_UNIT_VIDEO]; static int remove_zeros; @@ -194,11 +191,9 @@ MODULE_PARM(fastset, "i"); MODULE_PARM_DESC(fastset, "Allows picture settings to take effect immediately"); MODULE_PARM(force_palette, "i"); MODULE_PARM_DESC(force_palette, "Force the palette to a specific value"); -MODULE_PARM(tuner, "i"); -MODULE_PARM_DESC(tuner, "Set tuner type, if not autodetected"); MODULE_PARM(backlight, "i"); MODULE_PARM_DESC(backlight, "For objects that are lit from behind"); -MODULE_PARM(unit_video, "0-16i"); +MODULE_PARM(unit_video, "1-" __MODULE_STRING(OV511_MAX_UNIT_VIDEO) "i"); MODULE_PARM_DESC(unit_video, "Force use of specific minor number(s). 0 is not allowed."); MODULE_PARM(remove_zeros, "i"); @@ -337,12 +332,14 @@ static struct symbolic_list urb_errlist[] = { **********************************************************************/ static void ov51x_clear_snapshot(struct usb_ov511 *); -static int ov51x_check_snapshot(struct usb_ov511 *); -static inline int sensor_get_picture(struct usb_ov511 *, +static inline int sensor_get_picture(struct usb_ov511 *, struct video_picture *); +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) static int sensor_get_exposure(struct usb_ov511 *, unsigned char *); static int ov51x_control_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +static int ov51x_check_snapshot(struct usb_ov511 *); +#endif /********************************************************************** * Memory management @@ -351,7 +348,7 @@ static int ov51x_control_ioctl(struct inode *, struct file *, unsigned int, /* Here we want the physical address of the memory. * This is used when initializing the contents of the area. */ -static inline unsigned long +static inline unsigned long kvirt_to_pa(unsigned long adr) { unsigned long kva, ret; @@ -384,7 +381,7 @@ rvmalloc(unsigned long size) return mem; } -static void +static void rvfree(void *mem, unsigned long size) { unsigned long adr; @@ -412,13 +409,13 @@ static struct proc_dir_entry *ov511_proc_entry = NULL; extern struct proc_dir_entry *video_proc_entry; static struct file_operations ov511_control_fops = { - ioctl: ov51x_control_ioctl, + .ioctl = ov51x_control_ioctl, }; #define YES_NO(x) ((x) ? "yes" : "no") /* /proc/video/ov511/<minor#>/info */ -static int +static int ov511_read_proc_info(char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -490,13 +487,13 @@ ov511_read_proc_info(char *page, char **start, off_t off, int count, int *eof, * When the camera's button is pressed, the output of this will change from a * 0 to a 1 (ASCII). It will retain this value until it is read, after which * it will reset to zero. - * + * * SECURITY NOTE: Since reading this file can change the state of the snapshot * status, it is important for applications that open it to keep it locked * against access by other processes, using flock() or a similar mechanism. No * locking is provided by this driver. */ -static int +static int ov511_read_proc_button(char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -528,7 +525,7 @@ ov511_read_proc_button(char *page, char **start, off_t off, int count, int *eof, return len; } -static void +static void create_proc_ov511_cam(struct usb_ov511 *ov) { char dirname[10]; @@ -579,11 +576,11 @@ create_proc_ov511_cam(struct usb_ov511 *ov) unlock_kernel(); } -static void +static void destroy_proc_ov511_cam(struct usb_ov511 *ov) { char dirname[10]; - + if (!ov || !ov->proc_devdir) return; @@ -616,7 +613,7 @@ destroy_proc_ov511_cam(struct usb_ov511 *ov) ov->proc_devdir = NULL; } -static void +static void proc_ov511_create(void) { /* No current standard here. Alan prefers /proc/video/ as it keeps @@ -637,7 +634,7 @@ proc_ov511_create(void) err("Unable to create /proc/video/ov511"); } -static void +static void proc_ov511_destroy(void) { PDEBUG(3, "removing /proc/video/ov511"); @@ -656,7 +653,7 @@ proc_ov511_destroy(void) **********************************************************************/ /* Write an OV51x register */ -static int +static int reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) { int rc; @@ -669,7 +666,7 @@ reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) usb_sndctrlpipe(ov->dev, 0), (ov->bclass == BCL_OV518)?1:2 /* REG_IO */, USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, (__u16)reg, &ov->cbuf[0], 1, HZ); + 0, (__u16)reg, &ov->cbuf[0], 1, HZ); up(&ov->cbuf_lock); if (rc < 0) @@ -680,7 +677,7 @@ reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) /* Read from an OV51x register */ /* returns: negative is error, pos or zero is data */ -static int +static int reg_r(struct usb_ov511 *ov, unsigned char reg) { int rc; @@ -691,13 +688,13 @@ reg_r(struct usb_ov511 *ov, unsigned char reg) (ov->bclass == BCL_OV518)?1:3 /* REG_IO */, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, (__u16)reg, &ov->cbuf[0], 1, HZ); - - PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]); - - if (rc < 0) + + if (rc < 0) { err("reg read: error %d: %s", rc, symbolic(urb_errlist, rc)); - else - rc = ov->cbuf[0]; + } else { + rc = ov->cbuf[0]; + PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]); + } up(&ov->cbuf_lock); @@ -707,10 +704,10 @@ reg_r(struct usb_ov511 *ov, unsigned char reg) /* * Writes bits at positions specified by mask to an OV51x reg. Bits that are in * the same position as 1's in "mask" are cleared and set to "value". Bits - * that are in the same position as 0's in "mask" are preserved, regardless + * that are in the same position as 0's in "mask" are preserved, regardless * of their respective state in "value". */ -static int +static int reg_w_mask(struct usb_ov511 *ov, unsigned char reg, unsigned char value, @@ -735,7 +732,7 @@ reg_w_mask(struct usb_ov511 *ov, * Writes multiple (n) byte value to a single register. Only valid with certain * registers (0x30 and 0xc4 - 0xce). */ -static int +static int ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n) { int rc; @@ -760,7 +757,7 @@ ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n) return rc; } -static int +static int ov511_upload_quan_tables(struct usb_ov511 *ov) { unsigned char *pYTable = yQuanTable511; @@ -770,10 +767,8 @@ ov511_upload_quan_tables(struct usb_ov511 *ov) PDEBUG(4, "Uploading quantization tables"); - for (i = 0; i < OV511_QUANTABLESIZE / 2; i++) - { - if (ENABLE_Y_QUANTABLE) - { + for (i = 0; i < OV511_QUANTABLESIZE / 2; i++) { + if (ENABLE_Y_QUANTABLE) { val0 = *pYTable++; val1 = *pYTable++; val0 &= 0x0f; @@ -784,8 +779,7 @@ ov511_upload_quan_tables(struct usb_ov511 *ov) return rc; } - if (ENABLE_UV_QUANTABLE) - { + if (ENABLE_UV_QUANTABLE) { val0 = *pUVTable++; val1 = *pUVTable++; val0 &= 0x0f; @@ -803,7 +797,7 @@ ov511_upload_quan_tables(struct usb_ov511 *ov) } /* OV518 quantization tables are 8x4 (instead of 8x8) */ -static int +static int ov518_upload_quan_tables(struct usb_ov511 *ov) { unsigned char *pYTable = yQuanTable518; @@ -813,10 +807,8 @@ ov518_upload_quan_tables(struct usb_ov511 *ov) PDEBUG(4, "Uploading quantization tables"); - for (i = 0; i < OV518_QUANTABLESIZE / 2; i++) - { - if (ENABLE_Y_QUANTABLE) - { + for (i = 0; i < OV518_QUANTABLESIZE / 2; i++) { + if (ENABLE_Y_QUANTABLE) { val0 = *pYTable++; val1 = *pYTable++; val0 &= 0x0f; @@ -827,8 +819,7 @@ ov518_upload_quan_tables(struct usb_ov511 *ov) return rc; } - if (ENABLE_UV_QUANTABLE) - { + if (ENABLE_UV_QUANTABLE) { val0 = *pUVTable++; val1 = *pUVTable++; val0 &= 0x0f; @@ -845,16 +836,16 @@ ov518_upload_quan_tables(struct usb_ov511 *ov) return 0; } -static int +static int ov51x_reset(struct usb_ov511 *ov, unsigned char reset_type) { int rc; - + /* Setting bit 0 not allowed on 518/518Plus */ if (ov->bclass == BCL_OV518) reset_type &= 0xfe; - PDEBUG(4, "Reset: type=0x%X", reset_type); + PDEBUG(4, "Reset: type=0x%02X", reset_type); rc = reg_w(ov, R51x_SYS_RESET, reset_type); rc = reg_w(ov, R51x_SYS_RESET, 0); @@ -876,7 +867,7 @@ ov51x_reset(struct usb_ov511 *ov, unsigned char reset_type) * This is normally only called from i2c_w(). Note that this function * always succeeds regardless of whether the sensor is present and working. */ -static int +static int ov518_i2c_write_internal(struct usb_ov511 *ov, unsigned char reg, unsigned char value) @@ -901,7 +892,7 @@ ov518_i2c_write_internal(struct usb_ov511 *ov, } /* NOTE: Do not call this function directly! */ -static int +static int ov511_i2c_write_internal(struct usb_ov511 *ov, unsigned char reg, unsigned char value) @@ -917,7 +908,7 @@ ov511_i2c_write_internal(struct usb_ov511 *ov, if (rc < 0) return rc; /* Write "value" to I2C data port of OV511 */ - rc = reg_w(ov, R51x_I2C_DATA, value); + rc = reg_w(ov, R51x_I2C_DATA, value); if (rc < 0) return rc; /* Initiate 3-byte write cycle */ @@ -931,7 +922,7 @@ ov511_i2c_write_internal(struct usb_ov511 *ov, if ((rc&2) == 0) /* Ack? */ break; #if 0 - /* I2C abort */ + /* I2C abort */ reg_w(ov, R511_I2C_CTL, 0x10); #endif if (--retries < 0) { @@ -948,7 +939,7 @@ ov511_i2c_write_internal(struct usb_ov511 *ov, * This is normally only called from i2c_r(). Note that this function * always succeeds regardless of whether the sensor is present and working. */ -static int +static int ov518_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) { int rc, value; @@ -974,7 +965,7 @@ ov518_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) /* NOTE: Do not call this function directly! * returns: negative is error, pos or zero is data */ -static int +static int ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) { int rc, value, retries; @@ -996,7 +987,7 @@ ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) if ((rc&2) == 0) /* Ack? */ break; - /* I2C abort */ + /* I2C abort */ reg_w(ov, R511_I2C_CTL, 0x10); if (--retries < 0) { @@ -1018,7 +1009,7 @@ ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) if ((rc&2) == 0) /* Ack? */ break; - /* I2C abort */ + /* I2C abort */ rc = reg_w(ov, R511_I2C_CTL, 0x10); if (rc < 0) return rc; @@ -1031,17 +1022,17 @@ ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) value = reg_r(ov, R51x_I2C_DATA); PDEBUG(5, "0x%02X:0x%02X", reg, value); - + /* This is needed to make i2c_w() work */ rc = reg_w(ov, R511_I2C_CTL, 0x05); if (rc < 0) return rc; - + return value; } /* returns: negative is error, pos or zero is data */ -static int +static int i2c_r(struct usb_ov511 *ov, unsigned char reg) { int rc; @@ -1058,7 +1049,7 @@ i2c_r(struct usb_ov511 *ov, unsigned char reg) return rc; } -static int +static int i2c_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) { int rc; @@ -1076,7 +1067,7 @@ i2c_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) } /* Do not call this function directly! */ -static int +static int ov51x_i2c_write_mask_internal(struct usb_ov511 *ov, unsigned char reg, unsigned char value, @@ -1109,10 +1100,10 @@ ov51x_i2c_write_mask_internal(struct usb_ov511 *ov, /* Writes bits at positions specified by mask to an I2C reg. Bits that are in * the same position as 1's in "mask" are cleared and set to "value". Bits - * that are in the same position as 0's in "mask" are preserved, regardless + * that are in the same position as 0's in "mask" are preserved, regardless * of their respective state in "value". */ -static int +static int i2c_w_mask(struct usb_ov511 *ov, unsigned char reg, unsigned char value, @@ -1132,7 +1123,7 @@ i2c_w_mask(struct usb_ov511 *ov, * when calling this. This should not be called from outside the i2c I/O * functions. */ -static inline int +static inline int i2c_set_slave_internal(struct usb_ov511 *ov, unsigned char slave) { int rc; @@ -1146,8 +1137,10 @@ i2c_set_slave_internal(struct usb_ov511 *ov, unsigned char slave) return 0; } +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + /* Write to a specific I2C slave ID and register, using the specified mask */ -static int +static int i2c_w_slave(struct usb_ov511 *ov, unsigned char slave, unsigned char reg, @@ -1166,7 +1159,7 @@ i2c_w_slave(struct usb_ov511 *ov, out: /* Restore primary IDs */ - if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0) + if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0) err("Couldn't restore primary I2C slave"); up(&ov->i2c_lock); @@ -1174,7 +1167,7 @@ out: } /* Read from a specific I2C slave ID and register */ -static int +static int i2c_r_slave(struct usb_ov511 *ov, unsigned char slave, unsigned char reg) @@ -1194,15 +1187,17 @@ i2c_r_slave(struct usb_ov511 *ov, out: /* Restore primary IDs */ - if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0) + if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0) err("Couldn't restore primary I2C slave"); up(&ov->i2c_lock); return rc; } +#endif /* defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) */ + /* Sets I2C read and write slave IDs. Returns <0 for error */ -static int +static int ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid) { int rc; @@ -1221,7 +1216,7 @@ out: return rc; } -static int +static int write_regvals(struct usb_ov511 *ov, struct ov511_regvals * pRegvals) { int rc; @@ -1242,8 +1237,8 @@ write_regvals(struct usb_ov511 *ov, struct ov511_regvals * pRegvals) return 0; } -#ifdef OV511_DEBUG -static void +#ifdef OV511_DEBUG +static void dump_i2c_range(struct usb_ov511 *ov, int reg1, int regn) { int i; @@ -1251,18 +1246,18 @@ dump_i2c_range(struct usb_ov511 *ov, int reg1, int regn) for (i = reg1; i <= regn; i++) { rc = i2c_r(ov, i); - info("Sensor[0x%X] = 0x%X", i, rc); + info("Sensor[0x%02X] = 0x%02X", i, rc); } } -static void +static void dump_i2c_regs(struct usb_ov511 *ov) { info("I2C REGS"); dump_i2c_range(ov, 0x00, 0x7C); } -static void +static void dump_reg_range(struct usb_ov511 *ov, int reg1, int regn) { int i; @@ -1270,12 +1265,12 @@ dump_reg_range(struct usb_ov511 *ov, int reg1, int regn) for (i = reg1; i <= regn; i++) { rc = reg_r(ov, i); - info("OV511[0x%X] = 0x%X", i, rc); + info("OV511[0x%02X] = 0x%02X", i, rc); } } /* FIXME: Should there be an OV518 version of this? */ -static void +static void ov511_dump_regs(struct usb_ov511 *ov) { info("CAMERA INTERFACE REGS"); @@ -1302,29 +1297,15 @@ ov511_dump_regs(struct usb_ov511 *ov) } #endif -/********************************************************************** - * - * Kernel I2C Interface (not supported with OV518/OV518+) - * - **********************************************************************/ - -/* For as-yet unimplemented I2C interface */ -static void -call_i2c_clients(struct usb_ov511 *ov, unsigned int cmd, - void *arg) -{ - /* Do nothing */ -} - /*****************************************************************************/ /* Temporarily stops OV511 from functioning. Must do this before changing * registers while the camera is streaming */ -static inline int +static inline int ov51x_stop(struct usb_ov511 *ov) { PDEBUG(4, "stopping"); - ov->stopped = 1; + ov->stopped = 1; if (ov->bclass == BCL_OV518) return (reg_w_mask(ov, R51x_SYS_RESET, 0x3a, 0x3a)); else @@ -1333,12 +1314,12 @@ ov51x_stop(struct usb_ov511 *ov) /* Restarts OV511 after ov511_stop() is called. Has no effect if it is not * actually stopped (for performance). */ -static inline int +static inline int ov51x_restart(struct usb_ov511 *ov) { if (ov->stopped) { PDEBUG(4, "restarting"); - ov->stopped = 0; + ov->stopped = 0; /* Reinitialize the stream */ if (ov->bclass == BCL_OV518) @@ -1351,7 +1332,7 @@ ov51x_restart(struct usb_ov511 *ov) } /* Resets the hardware snapshot button */ -static void +static void ov51x_clear_snapshot(struct usb_ov511 *ov) { if (ov->bclass == BCL_OV511) { @@ -1363,12 +1344,12 @@ ov51x_clear_snapshot(struct usb_ov511 *ov) } else { err("clear snap: invalid bridge type"); } - } +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) /* Checks the status of the snapshot button. Returns 1 if it was pressed since * it was last cleared, and zero in all other cases (including errors) */ -static int +static int ov51x_check_snapshot(struct usb_ov511 *ov) { int ret, status = 0; @@ -1388,19 +1369,20 @@ ov51x_check_snapshot(struct usb_ov511 *ov) return status; } +#endif /* This does an initial reset of an OmniVision sensor and ensures that I2C * is synchronized. Returns <0 for failure. */ -static int +static int init_ov_sensor(struct usb_ov511 *ov) { int i, success; - /* Reset the sensor */ + /* Reset the sensor */ if (i2c_w(ov, 0x12, 0x80) < 0) return -EIO; - /* Wait for it to initialize */ + /* Wait for it to initialize */ schedule_timeout (1 + 150 * HZ / 1000); for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { @@ -1410,9 +1392,9 @@ init_ov_sensor(struct usb_ov511 *ov) continue; } - /* Reset the sensor */ + /* Reset the sensor */ if (i2c_w(ov, 0x12, 0x80) < 0) return -EIO; - /* Wait for it to initialize */ + /* Wait for it to initialize */ schedule_timeout(1 + 150 * HZ / 1000); /* Dummy read to sync I2C */ if (i2c_r(ov, 0x00) < 0) return -EIO; @@ -1420,13 +1402,13 @@ init_ov_sensor(struct usb_ov511 *ov) if (!success) return -EIO; - + PDEBUG(1, "I2C synced in %d attempt(s)", i); return 0; } -static int +static int ov511_set_packet_size(struct usb_ov511 *ov, int size) { int alt, mult; @@ -1468,7 +1450,7 @@ ov511_set_packet_size(struct usb_ov511 *ov, int size) if (reg_w(ov, R51x_FIFO_PSIZE, mult) < 0) return -EIO; - + if (usb_set_interface(ov->dev, ov->iface, alt) < 0) { err("Set packet size: set interface error"); return -EBUSY; @@ -1488,7 +1470,7 @@ ov511_set_packet_size(struct usb_ov511 *ov, int size) /* Note: Unlike the OV511/OV511+, the size argument does NOT include the * optional packet number byte. The actual size *is* stored in ov->packet_size, * though. */ -static int +static int ov518_set_packet_size(struct usb_ov511 *ov, int size) { int alt; @@ -1550,7 +1532,6 @@ ov511_init_compression(struct usb_ov511 *ov) int rc = 0; if (!ov->compress_inited) { - reg_w(ov, 0x70, phy); reg_w(ov, 0x71, phuv); reg_w(ov, 0x72, pvy); @@ -1568,7 +1549,7 @@ ov511_init_compression(struct usb_ov511 *ov) } ov->compress_inited = 1; -out: +out: return rc; } @@ -1579,7 +1560,6 @@ ov518_init_compression(struct usb_ov511 *ov) int rc = 0; if (!ov->compress_inited) { - if (ov518_upload_quan_tables(ov) < 0) { err("Error uploading quantization tables"); rc = -EIO; @@ -1588,7 +1568,7 @@ ov518_init_compression(struct usb_ov511 *ov) } ov->compress_inited = 1; -out: +out: return rc; } @@ -2130,7 +2110,7 @@ sensor_get_exposure(struct usb_ov511 *ov, unsigned char *val) #endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */ /* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */ -static inline void +static inline void ov51x_led_control(struct usb_ov511 *ov, int enable) { PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); @@ -2181,7 +2161,7 @@ sensor_set_light_freq(struct usb_ov511 *ov, int freq) i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); i2c_w(ov, 0x2b, sixty?0x00:0xac); i2c_w_mask(ov, 0x76, 0x01, 0x01); - break; + break; case SEN_OV6620: case SEN_OV6630: i2c_w(ov, 0x2b, sixty?0xa8:0x28); @@ -2269,7 +2249,7 @@ sensor_set_auto_brightness(struct usb_ov511 *ov, int enable) */ static inline int sensor_set_auto_exposure(struct usb_ov511 *ov, int enable) -{ +{ PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); switch (ov->sensor) { @@ -2281,7 +2261,7 @@ sensor_set_auto_exposure(struct usb_ov511 *ov, int enable) case SEN_OV76BE: case SEN_OV8600: i2c_w_mask(ov, 0x13, enable?0x01:0x00, 0x01); - break; + break; case SEN_OV6630: i2c_w_mask(ov, 0x28, enable?0x00:0x10, 0x10); break; @@ -2318,7 +2298,7 @@ sensor_set_backlight(struct usb_ov511 *ov, int enable) i2c_w_mask(ov, 0x68, enable?0xe0:0xc0, 0xe0); i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02); - break; + break; case SEN_OV6620: i2c_w_mask(ov, 0x4e, enable?0xe0:0xc0, 0xe0); i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); @@ -2378,7 +2358,7 @@ sensor_set_mirror(struct usb_ov511 *ov, int enable) /* Returns number of bits per pixel (regardless of where they are located; * planar or not), or zero for unsupported format. */ -static inline int +static inline int get_depth(int palette) { switch (palette) { @@ -2390,7 +2370,7 @@ get_depth(int palette) } /* Bytes per frame. Used by read(). Return of 0 indicates error */ -static inline long int +static inline long int get_frame_length(struct ov511_frame *frame) { if (!frame) @@ -2785,12 +2765,12 @@ ov518_mode_init_regs(struct usb_ov511 *ov, return -EINVAL; } else { hi_res = 0; - } + } if (ov51x_stop(ov) < 0) return -EIO; - /******** Set the mode ********/ + /******** Set the mode ********/ reg_w(ov, 0x2b, 0); reg_w(ov, 0x2c, 0); @@ -2906,10 +2886,10 @@ mode_init_regs(struct usb_ov511 *ov, rc = -EINVAL; break; case SEN_SAA7111A: -// rc = mode_init_saa_sensor_regs(ov, width, height, mode, +// rc = mode_init_saa_sensor_regs(ov, width, height, mode, // sub_flag); - PDEBUG(1, "SAA status = 0X%x", i2c_r(ov, 0x1f)); + PDEBUG(1, "SAA status = 0x%02X", i2c_r(ov, 0x1f)); break; default: err("Unknown sensor"); @@ -2952,7 +2932,7 @@ mode_init_regs(struct usb_ov511 *ov, /* This sets the default image parameters. This is useful for apps that use * read() and do not set these. */ -static int +static int ov51x_set_default_params(struct usb_ov511 *ov) { int i; @@ -2988,7 +2968,7 @@ ov51x_set_default_params(struct usb_ov511 *ov) **********************************************************************/ /* Set analog input port of decoder */ -static int +static int decoder_set_input(struct usb_ov511 *ov, int input) { PDEBUG(4, "port %d", input); @@ -3010,7 +2990,7 @@ decoder_set_input(struct usb_ov511 *ov, int input) } /* Get ASCII name of video input */ -static int +static int decoder_get_input_name(struct usb_ov511 *ov, int input, char *name) { switch (ov->sensor) { @@ -3022,7 +3002,6 @@ decoder_get_input_name(struct usb_ov511 *ov, int input, char *name) sprintf(name, "CVBS-%d", input); else // if (input < 8) sprintf(name, "S-Video-%d", input - 4); - break; } default: @@ -3033,7 +3012,7 @@ decoder_get_input_name(struct usb_ov511 *ov, int input, char *name) } /* Set norm (NTSC, PAL, SECAM, AUTO) */ -static int +static int decoder_set_norm(struct usb_ov511 *ov, int norm) { PDEBUG(4, "%d", norm); @@ -3048,7 +3027,7 @@ decoder_set_norm(struct usb_ov511 *ov, int norm) reg_e = 0x00; /* NTSC M / PAL BGHI */ } else if (norm == VIDEO_MODE_PAL) { reg_8 = 0x00; /* 50 Hz */ - reg_e = 0x00; /* NTSC M / PAL BGHI */ + reg_e = 0x00; /* NTSC M / PAL BGHI */ } else if (norm == VIDEO_MODE_AUTO) { reg_8 = 0x80; /* Auto field detect */ reg_e = 0x00; /* NTSC M / PAL BGHI */ @@ -3079,7 +3058,7 @@ decoder_set_norm(struct usb_ov511 *ov, int norm) /* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the * image at pOut is specified by w. */ -static inline void +static inline void make_8x8(unsigned char *pIn, unsigned char *pOut, int w) { unsigned char *pOut1 = pOut; @@ -3092,7 +3071,6 @@ make_8x8(unsigned char *pIn, unsigned char *pOut, int w) } pOut += w; } - } /* @@ -3214,7 +3192,7 @@ yuv420raw_to_yuv420p(struct ov511_frame *frame, * accordingly. Returns -ENXIO if decompressor is not available, otherwise * returns 0 if no other error. */ -static int +static int request_decompressor(struct usb_ov511 *ov) { if (!ov) @@ -3270,7 +3248,7 @@ request_decompressor(struct usb_ov511 *ov) /* Unlocks decompression module and nulls ov->decomp_ops. Safe to call even * if ov->decomp_ops is NULL. */ -static void +static void release_decompressor(struct usb_ov511 *ov) { int released = 0; /* Did we actually do anything? */ @@ -3286,14 +3264,14 @@ release_decompressor(struct usb_ov511 *ov) } ov->decomp_ops = NULL; - + unlock_kernel(); if (released) PDEBUG(3, "Decompressor released"); } -static void +static void decompress(struct usb_ov511 *ov, struct ov511_frame *frame, unsigned char *pIn0, unsigned char *pOut0) { @@ -3303,7 +3281,7 @@ decompress(struct usb_ov511 *ov, struct ov511_frame *frame, PDEBUG(4, "Decompressing %d bytes", frame->bytes_recvd); - if (frame->format == VIDEO_PALETTE_GREY + if (frame->format == VIDEO_PALETTE_GREY && ov->decomp_ops->decomp_400) { int ret = ov->decomp_ops->decomp_400( pIn0, @@ -3313,7 +3291,7 @@ decompress(struct usb_ov511 *ov, struct ov511_frame *frame, frame->rawheight, frame->bytes_recvd); PDEBUG(4, "DEBUG: decomp_400 returned %d", ret); - } else if (frame->format != VIDEO_PALETTE_GREY + } else if (frame->format != VIDEO_PALETTE_GREY && ov->decomp_ops->decomp_420) { int ret = ov->decomp_ops->decomp_420( pIn0, @@ -3328,6 +3306,12 @@ decompress(struct usb_ov511 *ov, struct ov511_frame *frame, } } +/********************************************************************** + * + * Format conversion + * + **********************************************************************/ + /* Fuses even and odd fields together, and doubles width. * INPUT: an odd field followed by an even field at pIn0, in YUV planar format * OUTPUT: a normal YUV planar image, with correct aspect ratio @@ -3432,7 +3416,7 @@ ov51x_postprocess_yuv420(struct usb_ov511 *ov, struct ov511_frame *frame) if (frame->compressed) decompress(ov, frame, frame->rawdata, frame->tempdata); else - yuv420raw_to_yuv420p(frame, frame->rawdata, + yuv420raw_to_yuv420p(frame, frame->rawdata, frame->tempdata); deinterlace(frame, RAWFMT_YUV420, frame->tempdata, @@ -3452,11 +3436,11 @@ ov51x_postprocess_yuv420(struct usb_ov511 *ov, struct ov511_frame *frame) * 3. Convert from YUV planar to destination format, if necessary * 4. Fix the RGB offset, if necessary */ -static void +static void ov51x_postprocess(struct usb_ov511 *ov, struct ov511_frame *frame) { if (dumppix) { - memset(frame->data, 0, + memset(frame->data, 0, MAX_DATA_SIZE(ov->maxwidth, ov->maxheight)); PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd); memcpy(frame->data, frame->rawdata, frame->bytes_recvd); @@ -3482,7 +3466,7 @@ ov51x_postprocess(struct usb_ov511 *ov, struct ov511_frame *frame) * **********************************************************************/ -static inline void +static inline void ov511_move_data(struct usb_ov511 *ov, unsigned char *in, int n) { int num, offset; @@ -3518,7 +3502,7 @@ ov511_move_data(struct usb_ov511 *ov, unsigned char *in, int n) /* Frame end */ if (in[8] & 0x80) { - ts = (struct timeval *)(frame->data + ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight)); do_gettimeofday(ts); @@ -3656,7 +3640,7 @@ check_middle: } } -static inline void +static inline void ov518_move_data(struct usb_ov511 *ov, unsigned char *in, int n) { int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight); @@ -3796,7 +3780,7 @@ check_middle: } else { if (frame->bytes_recvd + copied + 8 <= max_raw) { - memcpy(frame->rawdata + memcpy(frame->rawdata + frame->bytes_recvd + copied, in + read, 8); copied += 8; @@ -3810,7 +3794,7 @@ check_middle: } } -static void +static void ov51x_isoc_irq(struct urb *urb) { int i; @@ -3896,7 +3880,7 @@ ov51x_isoc_irq(struct urb *urb) * ***************************************************************************/ -static int +static int ov51x_init_isoc(struct usb_ov511 *ov) { struct urb *urb; @@ -3991,7 +3975,7 @@ ov51x_init_isoc(struct usb_ov511 *ov) return 0; } -static void +static void ov51x_unlink_isoc(struct usb_ov511 *ov) { int n; @@ -4006,7 +3990,7 @@ ov51x_unlink_isoc(struct usb_ov511 *ov) } } -static void +static void ov51x_stop_isoc(struct usb_ov511 *ov) { if (!ov->streaming || !ov->dev) @@ -4024,7 +4008,7 @@ ov51x_stop_isoc(struct usb_ov511 *ov) ov51x_unlink_isoc(ov); } -static int +static int ov51x_new_frame(struct usb_ov511 *ov, int framenum) { struct ov511_frame *frame; @@ -4046,7 +4030,7 @@ ov51x_new_frame(struct usb_ov511 *ov, int framenum) frame = &ov->frame[framenum]; - PDEBUG(4, "framenum = %d, width = %d, height = %d", framenum, + PDEBUG(4, "framenum = %d, width = %d, height = %d", framenum, frame->width, frame->height); frame->grabstate = FRAME_GRABBING; @@ -4075,12 +4059,12 @@ ov51x_new_frame(struct usb_ov511 *ov, int framenum) * ***************************************************************************/ -/* +/* * - You must acquire buf_lock before entering this function. * - Because this code will free any non-null pointer, you must be sure to null * them if you explicitly free them somewhere else! */ -static void +static void ov51x_do_dealloc(struct usb_ov511 *ov) { int i; @@ -4124,7 +4108,7 @@ ov51x_do_dealloc(struct usb_ov511 *ov) PDEBUG(4, "leaving"); } -static int +static int ov51x_alloc(struct usb_ov511 *ov) { int i; @@ -4171,12 +4155,12 @@ ov51x_alloc(struct usb_ov511 *ov) for (i = 0; i < OV511_NUMFRAMES; i++) { ov->frame[i].data = ov->fbuf + i * MAX_DATA_SIZE(w, h); - ov->frame[i].rawdata = ov->rawfbuf + ov->frame[i].rawdata = ov->rawfbuf + i * MAX_RAW_DATA_SIZE(w, h); - ov->frame[i].tempdata = ov->tempfbuf + ov->frame[i].tempdata = ov->tempfbuf + i * MAX_RAW_DATA_SIZE(w, h); - ov->frame[i].compbuf = + ov->frame[i].compbuf = (unsigned char *) __get_free_page(GFP_KERNEL); if (!ov->frame[i].compbuf) goto error; @@ -4196,7 +4180,7 @@ error: return -ENOMEM; } -static void +static void ov51x_dealloc(struct usb_ov511 *ov, int now) { PDEBUG(4, "entered"); @@ -4212,7 +4196,7 @@ ov51x_dealloc(struct usb_ov511 *ov, int now) * ***************************************************************************/ -static int +static int ov51x_v4l1_open(struct inode *inode, struct file *file) { struct video_device *vdev = video_devdata(file); @@ -4224,17 +4208,18 @@ ov51x_v4l1_open(struct inode *inode, struct file *file) down(&ov->lock); err = -EBUSY; - if (ov->user) + if (ov->user) goto out; - err = -ENOMEM; - if (ov51x_alloc(ov)) + err = ov51x_alloc(ov); + if (err < 0) goto out; ov->sub_flag = 0; /* In case app doesn't set them... */ - if (ov51x_set_default_params(ov) < 0) + err = ov51x_set_default_params(ov); + if (err < 0) goto out; /* Make sure frames are reset */ @@ -4243,7 +4228,7 @@ ov51x_v4l1_open(struct inode *inode, struct file *file) ov->frame[i].bytes_read = 0; } - /* If compression is on, make sure now that a + /* If compression is on, make sure now that a * decompressor can be loaded */ if (ov->compress && !ov->decomp_ops) { err = request_decompressor(ov); @@ -4268,14 +4253,14 @@ out: return err; } -static int +static int ov51x_v4l1_close(struct inode *inode, struct file *file) { struct video_device *vdev = file->private_data; struct usb_ov511 *ov = vdev->priv; PDEBUG(4, "ov511_close"); - + down(&ov->lock); ov->user--; @@ -4303,6 +4288,7 @@ ov51x_v4l1_close(struct inode *inode, struct file *file) kfree(ov); ov = NULL; } + file->private_data = NULL; return 0; @@ -4318,7 +4304,7 @@ ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, PDEBUG(5, "IOCtl: 0x%X", cmd); if (!ov->dev) - return -EIO; + return -EIO; switch (cmd) { case VIDIOCGCAP: @@ -4331,10 +4317,8 @@ ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, sprintf(b->name, "%s USB Camera", symbolic(brglist, ov->bridge)); b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; - if (ov->has_tuner) - b->type |= VID_TYPE_TUNER; b->channels = ov->num_inputs; - b->audios = ov->has_audio_proc ? 1:0; + b->audios = 0; b->maxwidth = ov->maxwidth; b->maxheight = ov->maxheight; b->minwidth = ov->minwidth; @@ -4354,11 +4338,10 @@ ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, } v->norm = ov->norm; - v->type = (ov->has_tuner) ? VIDEO_TYPE_TV : VIDEO_TYPE_CAMERA; - v->flags = (ov->has_tuner) ? VIDEO_VC_TUNER : 0; - v->flags |= (ov->has_audio_proc) ? VIDEO_VC_AUDIO : 0; + v->type = VIDEO_TYPE_CAMERA; + v->flags = 0; // v->flags |= (ov->has_decoder) ? VIDEO_VC_NORM : 0; - v->tuners = (ov->has_tuner) ? 1:0; + v->tuners = 0; decoder_get_input_name(ov, v->channel, v->name); return 0; @@ -4585,7 +4568,7 @@ ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, return -EINVAL; } - if (vm->width > ov->maxwidth + if (vm->width > ov->maxwidth || vm->height > ov->maxheight) { err("VIDIOCMCAPTURE: requested dimensions too big"); return -EINVAL; @@ -4641,7 +4624,6 @@ ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, struct ov511_frame *frame; int rc; - if (fnum >= OV511_NUMFRAMES) { err("VIDIOCSYNC: invalid frame (%d)", fnum); return -EINVAL; @@ -4676,7 +4658,7 @@ redo: return ret; goto redo; } - /* Fall through */ + /* Fall through */ case FRAME_DONE: if (ov->snap_enabled && !frame->snapshot) { int ret; @@ -4728,92 +4710,6 @@ redo: return 0; } - case VIDIOCGTUNER: - { - struct video_tuner *v = arg; - - PDEBUG(4, "VIDIOCGTUNER"); - - if (!ov->has_tuner || v->tuner) // Only tuner 0 - return -EINVAL; - - strcpy(v->name, "Television"); - - // FIXME: Need a way to get the real values - v->rangelow = 0; - v->rangehigh = ~0; - - v->flags = VIDEO_TUNER_PAL | VIDEO_TUNER_NTSC - | VIDEO_TUNER_SECAM; - v->mode = 0; /* FIXME: Not sure what this is yet */ - v->signal = 0xFFFF; /* unknown */ - - call_i2c_clients(ov, cmd, v); - - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner *v = arg; - int err; - - PDEBUG(4, "VIDIOCSTUNER"); - - /* Only no or one tuner for now */ - if (!ov->has_tuner || v->tuner) - return -EINVAL; - - /* and it only has certain valid modes */ - if (v->mode != VIDEO_MODE_PAL && - v->mode != VIDEO_MODE_NTSC && - v->mode != VIDEO_MODE_SECAM) - return -EOPNOTSUPP; - - /* Is this right/necessary? */ - err = decoder_set_norm(ov, v->mode); - if (err) - return err; - - call_i2c_clients(ov, cmd, v); - - return 0; - } - case VIDIOCGFREQ: - { - unsigned long v = *((unsigned long *) arg); - - PDEBUG(4, "VIDIOCGFREQ"); - - if (!ov->has_tuner) - return -EINVAL; - - v = ov->freq; -#if 0 - /* FIXME: this is necessary for testing */ - v = 46*16; -#endif - return 0; - } - case VIDIOCSFREQ: - { - unsigned long v = *((unsigned long *) arg); - - PDEBUG(4, "VIDIOCSFREQ: %lx", v); - - if (!ov->has_tuner) - return -EINVAL; - - ov->freq = v; - call_i2c_clients(ov, cmd, &v); - - return 0; - } - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - { - /* FIXME: Implement this... */ - return 0; - } default: PDEBUG(3, "Unsupported IOCtl: 0x%X", cmd); return -ENOIOCTLCMD; @@ -4822,7 +4718,7 @@ redo: return 0; } -static int +static int ov51x_v4l1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { @@ -4839,7 +4735,7 @@ ov51x_v4l1_ioctl(struct inode *inode, struct file *file, return rc; } -static inline int +static inline int ov51x_v4l1_read(struct file *file, char *buf, size_t cnt, loff_t *ppos) { struct video_device *vdev = file->private_data; @@ -4904,7 +4800,7 @@ restart: /* Wait while we're grabbing the image */ PDEBUG(4, "Waiting image grabbing"); - rc = wait_event_interruptible(frame->wq, + rc = wait_event_interruptible(frame->wq, (frame->grabstate == FRAME_DONE) || (frame->grabstate == FRAME_ERROR)); @@ -4951,7 +4847,7 @@ restart: get_frame_length(frame)); /* copy bytes to user space; we allow for partials reads */ -// if ((count + frame->bytes_read) +// if ((count + frame->bytes_read) // > get_frame_length((struct ov511_frame *)frame)) // count = frame->scanlength - frame->bytes_read; @@ -5036,25 +4932,25 @@ ov51x_v4l1_mmap(struct file *file, struct vm_area_struct *vma) } static struct file_operations ov511_fops = { - owner: THIS_MODULE, - open: ov51x_v4l1_open, - release: ov51x_v4l1_close, - read: ov51x_v4l1_read, - mmap: ov51x_v4l1_mmap, - ioctl: ov51x_v4l1_ioctl, - llseek: no_llseek, + .owner = THIS_MODULE, + .open = ov51x_v4l1_open, + .release = ov51x_v4l1_close, + .read = ov51x_v4l1_read, + .mmap = ov51x_v4l1_mmap, + .ioctl = ov51x_v4l1_ioctl, + .llseek = no_llseek, }; static struct video_device vdev_template = { - owner: THIS_MODULE, - name: "OV511 USB Camera", - type: VID_TYPE_CAPTURE, - hardware: VID_HARDWARE_OV511, - fops: &ov511_fops, + .owner = THIS_MODULE, + .name = "OV511 USB Camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_OV511, + .fops = &ov511_fops, }; #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) -static int +static int ov51x_control_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long ularg) { @@ -5278,7 +5174,7 @@ ov51x_control_ioctl(struct inode *inode, struct file *file, unsigned int cmd, /* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses * the same register settings as the OV7610, since they are very similar. */ -static int +static int ov7xx0_configure(struct usb_ov511 *ov) { int i, success; @@ -5429,7 +5325,7 @@ ov7xx0_configure(struct usb_ov511 *ov) err("this to " EMAIL); err("This is only a warning. You can attempt to use"); err("your camera anyway"); -// Only issue a warning for now +// Only issue a warning for now // return -1; } else { PDEBUG(1, "OV7xx0 initialized (method 2, %dx)", i+1); @@ -5449,8 +5345,6 @@ ov7xx0_configure(struct usb_ov511 *ov) /* I don't know what's different about the 76BE yet. */ if (i2c_r(ov, 0x15) & 1) { info("Sensor is an OV7620AE"); - info("PLEASE REPORT THE EXISTENCE OF THIS SENSOR TO"); - info("THE DRIVER AUTHOR"); } else { info("Sensor is an OV76BE"); } @@ -5498,7 +5392,7 @@ ov7xx0_configure(struct usb_ov511 *ov) } /* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */ -static int +static int ov6xx0_configure(struct usb_ov511 *ov) { int rc; @@ -5513,7 +5407,7 @@ ov6xx0_configure(struct usb_ov511 *ov) { OV511_I2C_BUS, 0x0c, 0x24 }, { OV511_I2C_BUS, 0x0d, 0x24 }, { OV511_I2C_BUS, 0x0f, 0x15 }, /* COMS */ - { OV511_I2C_BUS, 0x10, 0x75 }, /* AEC Exposure time */ + { OV511_I2C_BUS, 0x10, 0x75 }, /* AEC Exposure time */ { OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */ { OV511_I2C_BUS, 0x14, 0x04 }, /* 0x16: 0x06 helps frame stability with moving objects */ @@ -5525,11 +5419,11 @@ ov6xx0_configure(struct usb_ov511 *ov) { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */ // { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */ { OV511_I2C_BUS, 0x2d, 0x99 }, - { OV511_I2C_BUS, 0x33, 0xa0 }, /* Color Procesing Parameter */ + { OV511_I2C_BUS, 0x33, 0xa0 }, /* Color Procesing Parameter */ { OV511_I2C_BUS, 0x34, 0xd2 }, /* Max A/D range */ { OV511_I2C_BUS, 0x38, 0x8b }, { OV511_I2C_BUS, 0x39, 0x40 }, - + { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */ { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */ { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */ @@ -5609,7 +5503,7 @@ ov6xx0_configure(struct usb_ov511 *ov) * control the color balance */ // /*OK?*/ { OV511_I2C_BUS, 0x4a, 0x80 }, // Check these // /*OK?*/ { OV511_I2C_BUS, 0x4b, 0x80 }, -// /*U*/ { OV511_I2C_BUS, 0x4c, 0xd0 }, +// /*U*/ { OV511_I2C_BUS, 0x4c, 0xd0 }, /*d2?*/ { OV511_I2C_BUS, 0x4d, 0x10 }, /* This reduces noise a bit */ /*c1?*/ { OV511_I2C_BUS, 0x4e, 0x40 }, /*04?*/ { OV511_I2C_BUS, 0x4f, 0x07 }, @@ -5627,7 +5521,7 @@ ov6xx0_configure(struct usb_ov511 *ov) }; PDEBUG(4, "starting sensor configuration"); - + if (init_ov_sensor(ov) < 0) { err("Failed to read sensor ID. You might not have an OV6xx0,"); err("or it may be not responding. Report this to " EMAIL); @@ -5642,7 +5536,7 @@ ov6xx0_configure(struct usb_ov511 *ov) if (rc < 0) { err("Error detecting sensor type"); return -1; - } + } if ((rc & 3) == 0) ov->sensor = SEN_OV6630; @@ -5676,7 +5570,7 @@ ov6xx0_configure(struct usb_ov511 *ov) if (write_regvals(ov, aRegvalsNorm6x30)) return -1; } - + return 0; } @@ -5738,7 +5632,7 @@ ks0127_configure(struct usb_ov511 *ov) } /* This initializes the SAA7111A video decoder. */ -static int +static int saa7111a_configure(struct usb_ov511 *ov) { int rc; @@ -5890,12 +5784,8 @@ ov511_configure(struct usb_ov511 *ov) err("Also include the output of the detection process."); } - if (ov->customid == 6) { /* USB Life TV (NTSC) */ - ov->tuner_type = 8; /* Temic 4036FY5 3X 1981 */ - } else if (ov->customid == 70) { /* USB Life TV (PAL/SECAM) */ - ov->tuner_type = 3; /* Philips FI1216MF */ + if (ov->customid == 70) /* USB Life TV (PAL/SECAM) */ ov->pal = 1; - } if (write_regvals(ov, aRegvalsInit511)) goto error; @@ -5917,7 +5807,7 @@ ov511_configure(struct usb_ov511 *ov) ov->packet_numbering = 1; ov511_set_packet_size(ov, 0); - ov->snap_enabled = snapshot; + ov->snap_enabled = snapshot; /* Test for 7xx0 */ PDEBUG(3, "Testing for 0V7xx0"); @@ -5994,7 +5884,7 @@ error: } /* This initializes the OV518/OV518+ and the sensor */ -static int +static int ov518_configure(struct usb_ov511 *ov) { /* For 518 and 518+ */ @@ -6190,7 +6080,6 @@ ov51x_probe(struct usb_device *dev, unsigned int ifnum, ov->lightfreq = lightfreq; ov->num_inputs = 1; /* Video decoder init functs. change this */ ov->stop_during_set = !fastset; - ov->tuner_type = tuner; ov->backlight = backlight; ov->mirror = mirror; ov->auto_brt = autobright; @@ -6221,7 +6110,7 @@ ov51x_probe(struct usb_device *dev, unsigned int ifnum, ov->bclass = BCL_OV511; break; default: - err("Unknown product ID 0x%x", dev->descriptor.idProduct); + err("Unknown product ID 0x%04x", dev->descriptor.idProduct); goto error_dealloc; } @@ -6373,11 +6262,11 @@ ov51x_disconnect(struct usb_device *dev, void *ptr) } static struct usb_driver ov511_driver = { - owner: THIS_MODULE, - name: "ov511", - id_table: device_table, - probe: ov51x_probe, - disconnect: ov51x_disconnect + .owner = THIS_MODULE, + .name = "ov511", + .id_table = device_table, + .probe = ov51x_probe, + .disconnect = ov51x_disconnect }; @@ -6388,7 +6277,7 @@ static struct usb_driver ov511_driver = { ***************************************************************************/ /* Returns 0 for success */ -int +int ov511_register_decomp_module(int ver, struct ov51x_decomp_ops *ops, int ov518, int mmx) { @@ -6445,7 +6334,7 @@ err_in_use: return -EBUSY; } -void +void ov511_deregister_decomp_module(int ov518, int mmx) { lock_kernel(); @@ -6461,13 +6350,13 @@ ov511_deregister_decomp_module(int ov518, int mmx) else ov511_decomp_ops = NULL; } - + MOD_DEC_USE_COUNT; unlock_kernel(); } -static int __init +static int __init usb_ov511_init(void) { #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) @@ -6482,7 +6371,7 @@ usb_ov511_init(void) return 0; } -static void __exit +static void __exit usb_ov511_exit(void) { usb_deregister(&ov511_driver); diff --git a/drivers/usb/media/ov511.h b/drivers/usb/media/ov511.h index 02a8c2c4139f..20e4011b1794 100644 --- a/drivers/usb/media/ov511.h +++ b/drivers/usb/media/ov511.h @@ -551,17 +551,11 @@ struct usb_ov511 { int num_inputs; /* Number of inputs */ int norm; /* NTSC / PAL / SECAM */ int has_decoder; /* Device has a video decoder */ - int has_tuner; /* Device has a TV tuner */ - int has_audio_proc; /* Device has an audio processor */ - int freq; /* Current tuner frequency */ - int tuner_type; /* Specific tuner model */ int pal; /* Device is designed for PAL resolution */ - /* I2C interface to kernel */ + /* I2C interface */ struct semaphore i2c_lock; /* Protect I2C controller regs */ unsigned char primary_i2c_slave; /* I2C write id of sensor */ - unsigned char tuner_i2c_slave; /* I2C write id of tuner */ - unsigned char audio_i2c_slave; /* I2C write id of audio processor */ /* Control transaction stuff */ unsigned char *cbuf; /* Buffer for payload */ diff --git a/drivers/usb/media/pwc-if.c b/drivers/usb/media/pwc-if.c index 7f49d02d7316..72d110a78f05 100644 --- a/drivers/usb/media/pwc-if.c +++ b/drivers/usb/media/pwc-if.c @@ -91,10 +91,10 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr); static struct usb_driver pwc_driver = { - name: "Philips webcam", /* name */ - id_table: pwc_device_table, - probe: usb_pwc_probe, /* probe() */ - disconnect: usb_pwc_disconnect, /* disconnect() */ + .name = "Philips webcam", /* name */ + .id_table = pwc_device_table, + .probe = usb_pwc_probe, /* probe() */ + .disconnect = usb_pwc_disconnect, /* disconnect() */ }; #define MAX_DEV_HINTS 10 @@ -130,21 +130,21 @@ static int pwc_video_ioctl(struct inode *inode, struct file *file, static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma); static struct file_operations pwc_fops = { - owner: THIS_MODULE, - open: pwc_video_open, - release: pwc_video_close, - read: pwc_video_read, - poll: pwc_video_poll, - mmap: pwc_video_mmap, - ioctl: pwc_video_ioctl, - llseek: no_llseek, + .owner = THIS_MODULE, + .open = pwc_video_open, + .release = pwc_video_close, + .read = pwc_video_read, + .poll = pwc_video_poll, + .mmap = pwc_video_mmap, + .ioctl = pwc_video_ioctl, + .llseek = no_llseek, }; static struct video_device pwc_template = { - owner: THIS_MODULE, - name: "Philips Webcam", /* Filled in later */ - type: VID_TYPE_CAPTURE, - hardware: VID_HARDWARE_PWC, - fops: &pwc_fops, + .owner = THIS_MODULE, + .name = "Philips Webcam", /* Filled in later */ + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_PWC, + .fops = &pwc_fops, }; /***************************************************************************/ @@ -1756,40 +1756,40 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr) pdev = (struct pwc_device *)ptr; if (pdev == NULL) { Err("pwc_disconnect() Called without private pointer.\n"); - return; + goto out_err; } if (pdev->udev == NULL) { Err("pwc_disconnect() already called for %p\n", pdev); - return; + goto out_err; } if (pdev->udev != udev) { Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n"); - return; + goto out_err; } #ifdef PWC_MAGIC if (pdev->magic != PWC_MAGIC) { Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n"); - return; + goto out_err; } -#endif - +#endif + pdev->unplugged = 1; if (pdev->vdev != NULL) { - video_unregister_device(pdev->vdev); + video_unregister_device(pdev->vdev); if (pdev->vopen) { Info("Disconnected while device/video is open!\n"); - + /* Wake up any processes that might be waiting for a frame, let them return an error condition */ wake_up(&pdev->frameq); - + /* Wait until we get a 'go' from _close(). This used to have a gigantic race condition, since we kfree() - stuff here, but we have to wait until close() - is finished. + stuff here, but we have to wait until close() + is finished. */ - + Trace(TRACE_PROBE, "Sleeping on remove_ok.\n"); add_wait_queue(&pdev->remove_ok, &wait); set_current_state(TASK_UNINTERRUPTIBLE); @@ -1815,6 +1815,7 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr) device_hint[hint].pdev = NULL; pdev->udev = NULL; +out_err: unlock_kernel(); kfree(pdev); } diff --git a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c index 9c303e3d3a64..36dd251148bd 100644 --- a/drivers/usb/media/se401.c +++ b/drivers/usb/media/se401.c @@ -1283,20 +1283,20 @@ static int se401_mmap(struct file *file, struct vm_area_struct *vma) } static struct file_operations se401_fops = { - owner: THIS_MODULE, - open: se401_open, - release: se401_close, - read: se401_read, - mmap: se401_mmap, - ioctl: se401_ioctl, - llseek: no_llseek, + .owner = THIS_MODULE, + .open = se401_open, + .release = se401_close, + .read = se401_read, + .mmap = se401_mmap, + .ioctl = se401_ioctl, + .llseek = no_llseek, }; static struct video_device se401_template = { - owner: THIS_MODULE, - name: "se401 USB camera", - type: VID_TYPE_CAPTURE, - hardware: VID_HARDWARE_SE401, - fops: &se401_fops, + .owner = THIS_MODULE, + .name = "se401 USB camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_SE401, + .fops = &se401_fops, }; @@ -1523,7 +1523,7 @@ static inline void usb_se401_remove_disconnected (struct usb_se401 *se401) static struct usb_driver se401_driver = { name: "se401", id_table: device_table, - probe: se401_probe, + .probe = se401_probe, disconnect: se401_disconnect }; diff --git a/drivers/usb/media/stv680.c b/drivers/usb/media/stv680.c index 65ad3a3514e0..ce5134c3dbde 100644 --- a/drivers/usb/media/stv680.c +++ b/drivers/usb/media/stv680.c @@ -1432,20 +1432,20 @@ static int stv680_read (struct file *file, char *buf, } /* stv680_read */ static struct file_operations stv680_fops = { - owner: THIS_MODULE, - open: stv_open, - release: stv_close, - read: stv680_read, - mmap: stv680_mmap, - ioctl: stv680_ioctl, - llseek: no_llseek, + .owner = THIS_MODULE, + .open = stv_open, + .release = stv_close, + .read = stv680_read, + .mmap = stv680_mmap, + .ioctl = stv680_ioctl, + .llseek = no_llseek, }; static struct video_device stv680_template = { - owner: THIS_MODULE, - name: "STV0680 USB camera", - type: VID_TYPE_CAPTURE, - hardware: VID_HARDWARE_SE401, - fops: &stv680_fops, + .owner = THIS_MODULE, + .name = "STV0680 USB camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_SE401, + .fops = &stv680_fops, }; static void *stv680_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id) @@ -1545,10 +1545,10 @@ static void stv680_disconnect (struct usb_device *dev, void *ptr) } static struct usb_driver stv680_driver = { - name: "stv680", - probe: stv680_probe, - disconnect: stv680_disconnect, - id_table: device_table + .name = "stv680", + .probe = stv680_probe, + .disconnect = stv680_disconnect, + .id_table = device_table }; /******************************************************************** diff --git a/drivers/usb/media/usbvideo.c b/drivers/usb/media/usbvideo.c index f2ad752d6090..17103d7a69a1 100644 --- a/drivers/usb/media/usbvideo.c +++ b/drivers/usb/media/usbvideo.c @@ -1054,19 +1054,19 @@ static int usbvideo_find_struct(usbvideo_t *cams) } static struct file_operations usbvideo_fops = { - owner: THIS_MODULE, - open: usbvideo_v4l_open, - release: usbvideo_v4l_close, - read: usbvideo_v4l_read, - mmap: usbvideo_v4l_mmap, - ioctl: usbvideo_v4l_ioctl, - llseek: no_llseek, + .owner = THIS_MODULE, + .open = usbvideo_v4l_open, + .release =usbvideo_v4l_close, + .read = usbvideo_v4l_read, + .mmap = usbvideo_v4l_mmap, + .ioctl = usbvideo_v4l_ioctl, + .llseek = no_llseek, }; static struct video_device usbvideo_template = { - owner: THIS_MODULE, - type: VID_TYPE_CAPTURE, - hardware: VID_HARDWARE_CPIA, - fops: &usbvideo_fops, + .owner = THIS_MODULE, + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_CPIA, + .fops = &usbvideo_fops, }; uvd_t *usbvideo_AllocateDevice(usbvideo_t *cams) diff --git a/drivers/usb/media/vicam.c b/drivers/usb/media/vicam.c index 51ed32717520..4426717cb016 100644 --- a/drivers/usb/media/vicam.c +++ b/drivers/usb/media/vicam.c @@ -640,20 +640,20 @@ static int vicam_v4l_mmap(struct file *file, struct vm_area_struct *vma) /* FIXME - vicam_template - important */ static struct file_operations vicam_fops = { - owner: THIS_MODULE, - open: vicam_v4l_open, - release: vicam_v4l_close, - read: vicam_v4l_read, - mmap: vicam_v4l_mmap, - ioctl: vicam_v4l_ioctl, - llseek: no_llseek, + .owner = THIS_MODULE, + .open = vicam_v4l_open, + .release = vicam_v4l_close, + .read = vicam_v4l_read, + .mmap = vicam_v4l_mmap, + .ioctl = vicam_v4l_ioctl, + .llseek = no_llseek, }; static struct video_device vicam_template = { - owner: THIS_MODULE, - name: "vicam USB camera", - type: VID_TYPE_CAPTURE, - hardware: VID_HARDWARE_SE401, /* need to ask for own id */ - fops: &vicam_fops, + .owner = THIS_MODULE, + .name = "vicam USB camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_SE401, /* need to ask for own id */ + .fops = &vicam_fops, }; /****************************************************************************** @@ -876,11 +876,11 @@ static void vicam_disconnect(struct usb_device *udev, void *ptr) /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver vicam_driver = { - owner: THIS_MODULE, - name: "vicam", - probe: vicam_probe, - disconnect: vicam_disconnect, - id_table: vicam_table, + .owner = THIS_MODULE, + .name = "vicam", + .probe = vicam_probe, + .disconnect = vicam_disconnect, + .id_table = vicam_table, }; /****************************************************************************** diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index 0d55087b038b..aeb5ca4f3086 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -1879,13 +1879,13 @@ static int auerchar_release (struct inode *inode, struct file *file) /* File operation structure */ static struct file_operations auerswald_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: auerchar_read, - write: auerchar_write, - ioctl: auerchar_ioctl, - open: auerchar_open, - release: auerchar_release, + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = auerchar_read, + .write = auerchar_write, + .ioctl = auerchar_ioctl, + .open = auerchar_open, + .release = auerchar_release, }; @@ -2138,10 +2138,10 @@ MODULE_DEVICE_TABLE (usb, auerswald_ids); /* Standard usb driver struct */ static struct usb_driver auerswald_driver = { - name: "auerswald", - probe: auerswald_probe, - disconnect: auerswald_disconnect, - id_table: auerswald_ids, + .name = "auerswald", + .probe = auerswald_probe, + .disconnect = auerswald_disconnect, + .id_table = auerswald_ids, }; diff --git a/drivers/usb/misc/brlvger.c b/drivers/usb/misc/brlvger.c index 52b815eb57a3..47195779469f 100644 --- a/drivers/usb/misc/brlvger.c +++ b/drivers/usb/misc/brlvger.c @@ -230,23 +230,23 @@ MODULE_DEVICE_TABLE (usb, brlvger_ids); static struct file_operations brlvger_fops = { - owner: THIS_MODULE, - llseek: brlvger_llseek, - read: brlvger_read, - write: brlvger_write, - ioctl: brlvger_ioctl, - open: brlvger_open, - release: brlvger_release, - poll: brlvger_poll, + .owner = THIS_MODULE, + .llseek = brlvger_llseek, + .read = brlvger_read, + .write = brlvger_write, + .ioctl = brlvger_ioctl, + .open = brlvger_open, + .release = brlvger_release, + .poll = brlvger_poll, }; static struct usb_driver brlvger_driver = { - owner: THIS_MODULE, - name: "brlvger", - probe: brlvger_probe, - disconnect: brlvger_disconnect, - id_table: brlvger_ids, + .owner = THIS_MODULE, + .name = "brlvger", + .probe = brlvger_probe, + .disconnect = brlvger_disconnect, + .id_table = brlvger_ids, }; static int diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index 705f753b8ed2..70388454d8f5 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -443,11 +443,11 @@ read_rio(struct file *file, char *buffer, size_t count, loff_t * ppos) static struct file_operations usb_rio_fops = { - read: read_rio, - write: write_rio, - ioctl: ioctl_rio, - open: open_rio, - release: close_rio, + .read = read_rio, + .write = write_rio, + .ioctl = ioctl_rio, + .open = open_rio, + .release = close_rio, }; static void *probe_rio(struct usb_device *dev, unsigned int ifnum, @@ -525,10 +525,10 @@ static struct usb_device_id rio_table [] = { MODULE_DEVICE_TABLE (usb, rio_table); static struct usb_driver rio_driver = { - name: "rio500", - probe: probe_rio, - disconnect: disconnect_rio, - id_table: rio_table, + .name = "rio500", + .probe = probe_rio, + .disconnect = disconnect_rio, + .id_table = rio_table, }; int usb_rio_init(void) diff --git a/drivers/usb/misc/tiglusb.c b/drivers/usb/misc/tiglusb.c index 00fd9f31353f..e6cb63af2341 100644 --- a/drivers/usb/misc/tiglusb.c +++ b/drivers/usb/misc/tiglusb.c @@ -295,13 +295,13 @@ static int tiglusb_ioctl (struct inode *inode, struct file *file, /* ----- kernel module registering ------------------------------------ */ static struct file_operations tiglusb_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: tiglusb_read, - write: tiglusb_write, - ioctl: tiglusb_ioctl, - open: tiglusb_open, - release: tiglusb_release, + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = tiglusb_read, + .write = tiglusb_write, + .ioctl = tiglusb_ioctl, + .open = tiglusb_open, + .release = tiglusb_release, }; static int tiglusb_find_struct (void) @@ -407,11 +407,11 @@ static struct usb_device_id tiglusb_ids[] = { MODULE_DEVICE_TABLE (usb, tiglusb_ids); static struct usb_driver tiglusb_driver = { - owner: THIS_MODULE, - name: "tiglusb", - probe: tiglusb_probe, - disconnect: tiglusb_disconnect, - id_table: tiglusb_ids, + .owner = THIS_MODULE, + .name = "tiglusb", + .probe = tiglusb_probe, + .disconnect = tiglusb_disconnect, + .id_table = tiglusb_ids, }; /* --- initialisation code ------------------------------------- */ diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index ebf8e985bcec..3940bb1fbe5c 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -646,10 +646,10 @@ MODULE_DEVICE_TABLE (usb, uss720_table); static struct usb_driver uss720_driver = { - name: "uss720", - probe: uss720_probe, - disconnect: uss720_disconnect, - id_table: uss720_table, + .name = "uss720", + .probe = uss720_probe, + .disconnect = uss720_disconnect, + .id_table = uss720_table, }; /* --------------------------------------------------------------------- */ diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c index 0b2336935381..b270dc757afb 100644 --- a/drivers/usb/net/catc.c +++ b/drivers/usb/net/catc.c @@ -941,10 +941,10 @@ static struct usb_device_id catc_id_table [] = { MODULE_DEVICE_TABLE(usb, catc_id_table); static struct usb_driver catc_driver = { - name: "catc", - probe: catc_probe, - disconnect: catc_disconnect, - id_table: catc_id_table, + .name = "catc", + .probe = catc_probe, + .disconnect = catc_disconnect, + .id_table = catc_id_table, }; static int __init catc_init(void) diff --git a/drivers/usb/net/cdc-ether.c b/drivers/usb/net/cdc-ether.c index 1d3ac4508363..236a0d80cf22 100644 --- a/drivers/usb/net/cdc-ether.c +++ b/drivers/usb/net/cdc-ether.c @@ -1325,10 +1325,10 @@ static void CDCEther_disconnect( struct usb_device *usb, void *ptr ) ////////////////////////////////////////////////////////////////////////////// static struct usb_driver CDCEther_driver = { - name: "CDCEther", - probe: CDCEther_probe, - disconnect: CDCEther_disconnect, - id_table: CDCEther_ids, + .name = "CDCEther", + .probe = CDCEther_probe, + .disconnect = CDCEther_disconnect, + .id_table = CDCEther_ids, }; ////////////////////////////////////////////////////////////////////////////// diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c index 096a8b1aded2..738baabda267 100644 --- a/drivers/usb/net/kaweth.c +++ b/drivers/usb/net/kaweth.c @@ -165,11 +165,11 @@ MODULE_DEVICE_TABLE (usb, usb_klsi_table); * kaweth_driver ****************************************************************/ static struct usb_driver kaweth_driver = { - owner: THIS_MODULE, - name: "kaweth", - probe: kaweth_probe, - disconnect: kaweth_disconnect, - id_table: usb_klsi_table, + .owner = THIS_MODULE, + .name = "kaweth", + .probe = kaweth_probe, + .disconnect = kaweth_disconnect, + .id_table = usb_klsi_table, }; typedef __u8 eth_addr_t[6]; diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c index a7d9c72cdbcc..6728f5cfd6ce 100644 --- a/drivers/usb/net/pegasus.c +++ b/drivers/usb/net/pegasus.c @@ -1148,10 +1148,10 @@ static void pegasus_disconnect(struct usb_device *dev, void *ptr) } static struct usb_driver pegasus_driver = { - name: driver_name, - probe: pegasus_probe, - disconnect: pegasus_disconnect, - id_table: pegasus_ids, + .name = driver_name, + .probe = pegasus_probe, + .disconnect = pegasus_disconnect, + .id_table = pegasus_ids, }; int __init pegasus_init(void) diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c index ffd2737fee5b..89645c118480 100644 --- a/drivers/usb/net/rtl8150.c +++ b/drivers/usb/net/rtl8150.c @@ -110,10 +110,10 @@ static void *rtl8150_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id); static struct usb_driver rtl8150_driver = { - name: "rtl8150", - probe: rtl8150_probe, - disconnect: rtl8150_disconnect, - id_table: rtl8150_table, + .name = "rtl8150", + .probe = rtl8150_probe, + .disconnect = rtl8150_disconnect, + .id_table = rtl8150_table, }; /* diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c index b6b9fc8b51e9..c6ac7dd2821e 100644 --- a/drivers/usb/net/usbnet.c +++ b/drivers/usb/net/usbnet.c @@ -26,12 +26,7 @@ * See the LINUXDEV support. * * - * TODO: - * - * This needs to be retested for bulk queuing problems ... earlier versions - * seemed to find different types of problems in each HCD. Once they're fixed, - * re-enable queues to get higher bandwidth utilization (without needing - * to tweak MTU for larger packets). + * Status: * * - AN2720 ... not widely available, but reportedly works well * @@ -45,8 +40,8 @@ * but the Sharp Zaurus uses an incompatible protocol (extra checksums). * No reason not to merge the Zaurus protocol here too (got patch? :) * - * - For Netchip, use keventd to poll via control requests to detect hardware - * level "carrier detect". + * - For Netchip, should use keventd to poll via control requests to detect + * hardware level "carrier detect". * * - PL-230x ... the initialization protocol doesn't seem to match chip data * sheets, sometimes it's not needed and sometimes it hangs. Prolific has @@ -60,9 +55,9 @@ * * There are reports that bridging gives lower-than-usual throughput. * - * Craft smarter hotplug policy scripts ... ones that know how to arrange + * Need smarter hotplug policy scripts ... ones that know how to arrange * bridging with "brctl", and can handle static and dynamic ("pump") setups. - * Use those "peer connected" events. + * Use those eventual "peer connected" events. * * * CHANGELOG: @@ -122,7 +117,7 @@ // #define DEBUG // error path messages, extra info // #define VERBOSE // more; success messages -// #define REALLY_QUEUE +#define REALLY_QUEUE #if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) # define DEBUG @@ -139,7 +134,7 @@ #define CONFIG_USB_PL2301 -#define DRIVER_VERSION "07-May-2002" +#define DRIVER_VERSION "17-Jul-2002" /*-------------------------------------------------------------------------*/ @@ -301,12 +296,12 @@ MODULE_PARM_DESC (msg_level, "Initial message level (default = 1)"); *-------------------------------------------------------------------------*/ static const struct driver_info an2720_info = { - description: "AnchorChips/Cypress 2720", + .description = "AnchorChips/Cypress 2720", // no reset available! // no check_connect available! - in: 2, out: 2, // direction distinguishes these - epsize: 64, + .in = 2, out: 2, // direction distinguishes these + .epsize =64, }; #endif /* CONFIG_USB_AN2720 */ @@ -324,10 +319,10 @@ static const struct driver_info an2720_info = { *-------------------------------------------------------------------------*/ static const struct driver_info belkin_info = { - description: "Belkin, eTEK, or compatible", + .description = "Belkin, eTEK, or compatible", - in: 1, out: 1, // direction distinguishes these - epsize: 64, + .in = 1, out: 1, // direction distinguishes these + .epsize =64, }; #endif /* CONFIG_USB_BELKIN */ @@ -635,17 +630,17 @@ genelink_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags) } static const struct driver_info genelink_info = { - description: "Genesys GeneLink", - flags: FLAG_FRAMING_GL | FLAG_NO_SETINT, - reset: genelink_reset, - rx_fixup: genelink_rx_fixup, - tx_fixup: genelink_tx_fixup, + .description = "Genesys GeneLink", + .flags = FLAG_FRAMING_GL | FLAG_NO_SETINT, + .reset = genelink_reset, + .rx_fixup = genelink_rx_fixup, + .tx_fixup = genelink_tx_fixup, - in: 1, out: 2, - epsize: 64, + .in = 1, out: 2, + .epsize =64, #ifdef GENELINK_ACK - check_connect: genelink_check_connect, + .check_connect =genelink_check_connect, #endif }; @@ -676,11 +671,11 @@ static int linuxdev_check_connect (struct usbnet *dev) } static const struct driver_info linuxdev_info = { - description: "Linux Device", + .description = "Linux Device", // no reset defined (yet?) - check_connect: linuxdev_check_connect, - in: 2, out: 1, - epsize: 64, + .check_connect =linuxdev_check_connect, + .in = 2, out: 1, + .epsize =64, }; #endif /* CONFIG_USB_LINUXDEV */ @@ -1123,15 +1118,15 @@ net1080_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags) } static const struct driver_info net1080_info = { - description: "NetChip TurboCONNECT", - flags: FLAG_FRAMING_NC, - reset: net1080_reset, - check_connect: net1080_check_connect, - rx_fixup: net1080_rx_fixup, - tx_fixup: net1080_tx_fixup, - - in: 1, out: 1, // direction distinguishes these - epsize: 64, + .description = "NetChip TurboCONNECT", + .flags = FLAG_FRAMING_NC, + .reset = net1080_reset, + .check_connect =net1080_check_connect, + .rx_fixup = net1080_rx_fixup, + .tx_fixup = net1080_tx_fixup, + + .in = 1, out: 1, // direction distinguishes these + .epsize =64, }; #endif /* CONFIG_USB_NET1080 */ @@ -1192,13 +1187,13 @@ static int pl_reset (struct usbnet *dev) } static const struct driver_info prolific_info = { - description: "Prolific PL-2301/PL-2302", - flags: FLAG_NO_SETINT, + .description = "Prolific PL-2301/PL-2302", + .flags = FLAG_NO_SETINT, /* some PL-2302 versions seem to fail usb_set_interface() */ - reset: pl_reset, + .reset = pl_reset, - in: 3, out: 2, - epsize: 64, + .in = 3, out: 2, + .epsize =64, }; #endif /* CONFIG_USB_PL2301 */ @@ -1815,20 +1810,19 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) } #endif /* CONFIG_USB_NET1080 */ - netif_stop_queue (net); switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) { case -EPIPE: + netif_stop_queue (net); defer_kevent (dev, EVENT_TX_HALT); break; default: - netif_start_queue (net); dbg ("%s tx: submit urb err %d", net->name, retval); break; case 0: net->trans_start = jiffies; __skb_queue_tail (&dev->txq, skb); - if (dev->txq.qlen < TX_QLEN) - netif_start_queue (net); + if (dev->txq.qlen >= TX_QLEN) + netif_stop_queue (net); } spin_unlock_irqrestore (&dev->txq.lock, flags); @@ -2054,32 +2048,32 @@ static const struct usb_device_id products [] = { #ifdef CONFIG_USB_AN2720 { USB_DEVICE (0x0547, 0x2720), // AnchorChips defaults - driver_info: (unsigned long) &an2720_info, + .driver_info = (unsigned long) &an2720_info, }, { USB_DEVICE (0x0547, 0x2727), // Xircom PGUNET - driver_info: (unsigned long) &an2720_info, + .driver_info = (unsigned long) &an2720_info, }, #endif #ifdef CONFIG_USB_BELKIN { USB_DEVICE (0x050d, 0x0004), // Belkin - driver_info: (unsigned long) &belkin_info, + .driver_info = (unsigned long) &belkin_info, }, { USB_DEVICE (0x056c, 0x8100), // eTEK - driver_info: (unsigned long) &belkin_info, + .driver_info = (unsigned long) &belkin_info, }, { USB_DEVICE (0x0525, 0x9901), // Advance USBNET (eTEK) - driver_info: (unsigned long) &belkin_info, + .driver_info = (unsigned long) &belkin_info, }, #endif #ifdef CONFIG_USB_GENESYS { USB_DEVICE (0x05e3, 0x0502), // GL620USB-A - driver_info: (unsigned long) &genelink_info, + .driver_info = (unsigned long) &genelink_info, }, #endif @@ -2091,28 +2085,28 @@ static const struct usb_device_id products [] = { { // 1183 = 0x049F, both used as hex values? USB_DEVICE (0x049F, 0x505A), // Compaq "Itsy" - driver_info: (unsigned long) &linuxdev_info, + .driver_info = (unsigned long) &linuxdev_info, }, #endif #ifdef CONFIG_USB_NET1080 { USB_DEVICE (0x0525, 0x1080), // NetChip ref design - driver_info: (unsigned long) &net1080_info, + .driver_info = (unsigned long) &net1080_info, }, { USB_DEVICE (0x06D0, 0x0622), // Laplink Gold - driver_info: (unsigned long) &net1080_info, + .driver_info = (unsigned long) &net1080_info, }, #endif #ifdef CONFIG_USB_PL2301 { USB_DEVICE (0x067b, 0x0000), // PL-2301 - driver_info: (unsigned long) &prolific_info, + .driver_info = (unsigned long) &prolific_info, }, { USB_DEVICE (0x067b, 0x0001), // PL-2302 - driver_info: (unsigned long) &prolific_info, + .driver_info = (unsigned long) &prolific_info, }, #endif @@ -2123,10 +2117,10 @@ static const struct usb_device_id products [] = { MODULE_DEVICE_TABLE (usb, products); static struct usb_driver usbnet_driver = { - name: driver_name, - id_table: products, - probe: usbnet_probe, - disconnect: usbnet_disconnect, + .name = driver_name, + .id_table = products, + .probe = usbnet_probe, + .disconnect = usbnet_disconnect, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 1b95a25f286b..fa8c7f874dd6 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -116,21 +116,21 @@ MODULE_DEVICE_TABLE (usb, id_table_combined); /* All of the device info needed for the serial converters */ static struct usb_serial_device_type belkin_device = { - owner: THIS_MODULE, - name: "Belkin / Peracom / GoHubs USB Serial Adapter", - id_table: id_table_combined, - num_interrupt_in: 1, - num_bulk_in: 1, - num_bulk_out: 1, - num_ports: 1, - open: belkin_sa_open, - close: belkin_sa_close, - read_int_callback: belkin_sa_read_int_callback, /* How we get the status info */ - ioctl: belkin_sa_ioctl, - set_termios: belkin_sa_set_termios, - break_ctl: belkin_sa_break_ctl, - attach: belkin_sa_startup, - shutdown: belkin_sa_shutdown, + .owner = THIS_MODULE, + .name = "Belkin / Peracom / GoHubs USB Serial Adapter", + .id_table = id_table_combined, + .num_interrupt_in = 1, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = belkin_sa_open, + .close = belkin_sa_close, + .read_int_callback = belkin_sa_read_int_callback, /* How we get the status info */ + .ioctl = belkin_sa_ioctl, + .set_termios = belkin_sa_set_termios, + .break_ctl = belkin_sa_break_ctl, + .attach = belkin_sa_startup, + .shutdown = belkin_sa_shutdown, }; diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 9ca8b075f007..7a15cc3564a4 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -74,21 +74,21 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE (usb, id_table); static struct usb_serial_device_type cyberjack_device = { - owner: THIS_MODULE, - name: "Reiner SCT Cyberjack USB card reader", - id_table: id_table, - num_interrupt_in: 1, - num_bulk_in: 1, - num_bulk_out: 1, - num_ports: 1, - attach: cyberjack_startup, - shutdown: cyberjack_shutdown, - open: cyberjack_open, - close: cyberjack_close, - write: cyberjack_write, - read_int_callback: cyberjack_read_int_callback, - read_bulk_callback: cyberjack_read_bulk_callback, - write_bulk_callback: cyberjack_write_bulk_callback, + .owner = THIS_MODULE, + .name = "Reiner SCT Cyberjack USB card reader", + .id_table = id_table, + .num_interrupt_in = 1, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .attach = cyberjack_startup, + .shutdown = cyberjack_shutdown, + .open = cyberjack_open, + .close = cyberjack_close, + .write = cyberjack_write, + .read_int_callback = cyberjack_read_int_callback, + .read_bulk_callback = cyberjack_read_bulk_callback, + .write_bulk_callback = cyberjack_write_bulk_callback, }; struct cyberjack_private { diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 4dd615fda3f7..eee56288d2ea 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -498,51 +498,51 @@ MODULE_DEVICE_TABLE (usb, id_table_combined); /* device info needed for the Digi serial converter */ static struct usb_serial_device_type digi_acceleport_2_device = { - owner: THIS_MODULE, - name: "Digi USB", - id_table: id_table_2, - num_interrupt_in: 0, - num_bulk_in: 4, - num_bulk_out: 4, - num_ports: 3, - open: digi_open, - close: digi_close, - write: digi_write, - write_room: digi_write_room, - write_bulk_callback: digi_write_bulk_callback, - read_bulk_callback: digi_read_bulk_callback, - chars_in_buffer: digi_chars_in_buffer, - throttle: digi_rx_throttle, - unthrottle: digi_rx_unthrottle, - ioctl: digi_ioctl, - set_termios: digi_set_termios, - break_ctl: digi_break_ctl, - attach: digi_startup, - shutdown: digi_shutdown, + .owner = THIS_MODULE, + .name = "Digi USB", + .id_table = id_table_2, + .num_interrupt_in = 0, + .num_bulk_in = 4, + .num_bulk_out = 4, + .num_ports = 3, + .open = digi_open, + .close = digi_close, + .write = digi_write, + .write_room = digi_write_room, + .write_bulk_callback = digi_write_bulk_callback, + .read_bulk_callback = digi_read_bulk_callback, + .chars_in_buffer = digi_chars_in_buffer, + .throttle = digi_rx_throttle, + .unthrottle = digi_rx_unthrottle, + .ioctl = digi_ioctl, + .set_termios = digi_set_termios, + .break_ctl = digi_break_ctl, + .attach = digi_startup, + .shutdown = digi_shutdown, }; static struct usb_serial_device_type digi_acceleport_4_device = { - owner: THIS_MODULE, - name: "Digi USB", - id_table: id_table_4, - num_interrupt_in: 0, - num_bulk_in: 5, - num_bulk_out: 5, - num_ports: 4, - open: digi_open, - close: digi_close, - write: digi_write, - write_room: digi_write_room, - write_bulk_callback: digi_write_bulk_callback, - read_bulk_callback: digi_read_bulk_callback, - chars_in_buffer: digi_chars_in_buffer, - throttle: digi_rx_throttle, - unthrottle: digi_rx_unthrottle, - ioctl: digi_ioctl, - set_termios: digi_set_termios, - break_ctl: digi_break_ctl, - attach: digi_startup, - shutdown: digi_shutdown, + .owner = THIS_MODULE, + .name = "Digi USB", + .id_table = id_table_4, + .num_interrupt_in = 0, + .num_bulk_in = 5, + .num_bulk_out = 5, + .num_ports = 4, + .open = digi_open, + .close = digi_close, + .write = digi_write, + .write_room = digi_write_room, + .write_bulk_callback = digi_write_bulk_callback, + .read_bulk_callback = digi_read_bulk_callback, + .chars_in_buffer = digi_chars_in_buffer, + .throttle = digi_rx_throttle, + .unthrottle = digi_rx_unthrottle, + .ioctl = digi_ioctl, + .set_termios = digi_set_termios, + .break_ctl = digi_break_ctl, + .attach = digi_startup, + .shutdown = digi_shutdown, }; diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index c6b255a36326..4f66d87f9a10 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -111,26 +111,26 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE (usb, id_table); static struct usb_serial_device_type empeg_device = { - owner: THIS_MODULE, - name: "Empeg", - id_table: id_table, - num_interrupt_in: 0, - num_bulk_in: 1, - num_bulk_out: 1, - num_ports: 1, - open: empeg_open, - close: empeg_close, - throttle: empeg_throttle, - unthrottle: empeg_unthrottle, - attach: empeg_startup, - shutdown: empeg_shutdown, - ioctl: empeg_ioctl, - set_termios: empeg_set_termios, - write: empeg_write, - write_room: empeg_write_room, - chars_in_buffer: empeg_chars_in_buffer, - write_bulk_callback: empeg_write_bulk_callback, - read_bulk_callback: empeg_read_bulk_callback, + .owner = THIS_MODULE, + .name = "Empeg", + .id_table = id_table, + .num_interrupt_in = 0, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = empeg_open, + .close = empeg_close, + .throttle = empeg_throttle, + .unthrottle = empeg_unthrottle, + .attach = empeg_startup, + .shutdown = empeg_shutdown, + .ioctl = empeg_ioctl, + .set_termios = empeg_set_termios, + .write = empeg_write, + .write_room = empeg_write_room, + .chars_in_buffer = empeg_chars_in_buffer, + .write_bulk_callback = empeg_write_bulk_callback, + .read_bulk_callback = empeg_read_bulk_callback, }; #define NUM_URBS 16 diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index b833b4577406..fb20bd131723 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -173,45 +173,45 @@ static void ftdi_sio_break_ctl (struct usb_serial_port *port, int break_state ) which share common code */ static struct usb_serial_device_type ftdi_sio_device = { - owner: THIS_MODULE, - name: "FTDI SIO", - id_table: id_table_sio, - num_interrupt_in: 0, - num_bulk_in: 1, - num_bulk_out: 1, - num_ports: 1, - open: ftdi_sio_open, - close: ftdi_sio_close, - write: ftdi_sio_write, - write_room: ftdi_sio_write_room, - read_bulk_callback: ftdi_sio_read_bulk_callback, - write_bulk_callback: ftdi_sio_write_bulk_callback, - ioctl: ftdi_sio_ioctl, - set_termios: ftdi_sio_set_termios, - break_ctl: ftdi_sio_break_ctl, - attach: ftdi_sio_startup, - shutdown: ftdi_sio_shutdown, + .owner = THIS_MODULE, + .name = "FTDI SIO", + .id_table = id_table_sio, + .num_interrupt_in = 0, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = ftdi_sio_open, + .close = ftdi_sio_close, + .write = ftdi_sio_write, + .write_room = ftdi_sio_write_room, + .read_bulk_callback = ftdi_sio_read_bulk_callback, + .write_bulk_callback = ftdi_sio_write_bulk_callback, + .ioctl = ftdi_sio_ioctl, + .set_termios = ftdi_sio_set_termios, + .break_ctl = ftdi_sio_break_ctl, + .attach = ftdi_sio_startup, + .shutdown = ftdi_sio_shutdown, }; static struct usb_serial_device_type ftdi_8U232AM_device = { - owner: THIS_MODULE, - name: "FTDI 8U232AM", - id_table: id_table_8U232AM, - num_interrupt_in: 0, - num_bulk_in: 1, - num_bulk_out: 1, - num_ports: 1, - open: ftdi_sio_open, - close: ftdi_sio_close, - write: ftdi_sio_write, - write_room: ftdi_sio_write_room, - read_bulk_callback: ftdi_sio_read_bulk_callback, - write_bulk_callback: ftdi_sio_write_bulk_callback, - ioctl: ftdi_sio_ioctl, - set_termios: ftdi_sio_set_termios, - break_ctl: ftdi_sio_break_ctl, - attach: ftdi_8U232AM_startup, - shutdown: ftdi_sio_shutdown, + .owner = THIS_MODULE, + .name = "FTDI 8U232AM", + .id_table = id_table_8U232AM, + .num_interrupt_in = 0, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = ftdi_sio_open, + .close = ftdi_sio_close, + .write = ftdi_sio_write, + .write_room = ftdi_sio_write_room, + .read_bulk_callback = ftdi_sio_read_bulk_callback, + .write_bulk_callback = ftdi_sio_write_bulk_callback, + .ioctl = ftdi_sio_ioctl, + .set_termios = ftdi_sio_set_termios, + .break_ctl = ftdi_sio_break_ctl, + .attach = ftdi_8U232AM_startup, + .shutdown = ftdi_sio_shutdown, }; diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 9323ffefb9d2..aae807c97f65 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -90,22 +90,22 @@ MODULE_DEVICE_TABLE (usb, ipaq_id_table); /* All of the device info needed for the Compaq iPAQ */ struct usb_serial_device_type ipaq_device = { - owner: THIS_MODULE, - name: "Compaq iPAQ", - id_table: ipaq_id_table, - num_interrupt_in: NUM_DONT_CARE, - num_bulk_in: 1, - num_bulk_out: 1, - num_ports: 1, - open: ipaq_open, - close: ipaq_close, - attach: ipaq_startup, - shutdown: ipaq_shutdown, - write: ipaq_write, - write_room: ipaq_write_room, - chars_in_buffer: ipaq_chars_in_buffer, - read_bulk_callback: ipaq_read_bulk_callback, - write_bulk_callback: ipaq_write_bulk_callback, + .owner = THIS_MODULE, + .name = "Compaq iPAQ", + .id_table = ipaq_id_table, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = ipaq_open, + .close = ipaq_close, + .attach = ipaq_startup, + .shutdown = ipaq_shutdown, + .write = ipaq_write, + .write_room = ipaq_write_room, + .chars_in_buffer = ipaq_chars_in_buffer, + .read_bulk_callback = ipaq_read_bulk_callback, + .write_bulk_callback = ipaq_write_bulk_callback, }; static spinlock_t write_list_lock; diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 5fbd5ea1bfa7..f89ebb682280 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -131,20 +131,20 @@ MODULE_DEVICE_TABLE (usb, id_table); struct usb_serial_device_type ir_device = { - owner: THIS_MODULE, - name: "IR Dongle", - id_table: id_table, - num_interrupt_in: 1, - num_bulk_in: 1, - num_bulk_out: 1, - num_ports: 1, - set_termios: ir_set_termios, - attach: ir_startup, - open: ir_open, - close: ir_close, - write: ir_write, - write_bulk_callback: ir_write_bulk_callback, - read_bulk_callback: ir_read_bulk_callback, + .owner = THIS_MODULE, + .name = "IR Dongle", + .id_table = id_table, + .num_interrupt_in = 1, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .set_termios = ir_set_termios, + .attach = ir_startup, + .open = ir_open, + .close = ir_close, + .write = ir_write, + .write_bulk_callback = ir_write_bulk_callback, + .read_bulk_callback = ir_read_bulk_callback, }; static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 4804b7d8f0ce..adb378a75ca3 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1103,28 +1103,28 @@ static struct callbacks { } keyspan_callbacks[] = { { /* msg_usa26 callbacks */ - instat_callback: usa26_instat_callback, - glocont_callback: usa26_glocont_callback, - indat_callback: usa26_indat_callback, - outdat_callback: usa2x_outdat_callback, - inack_callback: usa26_inack_callback, - outcont_callback: usa26_outcont_callback, + .instat_callback = usa26_instat_callback, + .glocont_callback = usa26_glocont_callback, + .indat_callback = usa26_indat_callback, + .outdat_callback = usa2x_outdat_callback, + .inack_callback = usa26_inack_callback, + .outcont_callback = usa26_outcont_callback, }, { /* msg_usa28 callbacks */ - instat_callback: usa28_instat_callback, - glocont_callback: usa28_glocont_callback, - indat_callback: usa28_indat_callback, - outdat_callback: usa2x_outdat_callback, - inack_callback: usa28_inack_callback, - outcont_callback: usa28_outcont_callback, + .instat_callback = usa28_instat_callback, + .glocont_callback = usa28_glocont_callback, + .indat_callback = usa28_indat_callback, + .outdat_callback = usa2x_outdat_callback, + .inack_callback = usa28_inack_callback, + .outcont_callback = usa28_outcont_callback, }, { /* msg_usa49 callbacks */ - instat_callback: usa49_instat_callback, - glocont_callback: usa49_glocont_callback, - indat_callback: usa49_indat_callback, - outdat_callback: usa2x_outdat_callback, - inack_callback: usa49_inack_callback, - outcont_callback: usa49_outcont_callback, + .instat_callback = usa49_instat_callback, + .glocont_callback = usa49_glocont_callback, + .indat_callback = usa49_indat_callback, + .outdat_callback = usa2x_outdat_callback, + .inack_callback = usa49_inack_callback, + .outcont_callback = usa49_outcont_callback, } }; diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index b0dac088b979..2ee0a07f28fb 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -804,52 +804,52 @@ static void keyspan_pda_shutdown (struct usb_serial *serial) #ifdef KEYSPAN static struct usb_serial_device_type keyspan_pda_fake_device = { - owner: THIS_MODULE, - name: "Keyspan PDA - (prerenumeration)", - id_table: id_table_fake, - num_interrupt_in: NUM_DONT_CARE, - num_bulk_in: NUM_DONT_CARE, - num_bulk_out: NUM_DONT_CARE, - num_ports: 1, - attach: keyspan_pda_fake_startup, + .owner = THIS_MODULE, + .name = "Keyspan PDA - (prerenumeration)", + .id_table = id_table_fake, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = NUM_DONT_CARE, + .num_bulk_out = NUM_DONT_CARE, + .num_ports = 1, + .attach = keyspan_pda_fake_startup, }; #endif #ifdef XIRCOM static struct usb_serial_device_type xircom_pgs_fake_device = { - owner: THIS_MODULE, - name: "Xircom / Entregra PGS - (prerenumeration)", - id_table: id_table_fake_xircom, - num_interrupt_in: NUM_DONT_CARE, - num_bulk_in: NUM_DONT_CARE, - num_bulk_out: NUM_DONT_CARE, - num_ports: 1, - attach: keyspan_pda_fake_startup, + .owner = THIS_MODULE, + .name = "Xircom / Entregra PGS - (prerenumeration)", + .id_table = id_table_fake_xircom, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = NUM_DONT_CARE, + .num_bulk_out = NUM_DONT_CARE, + .num_ports = 1, + .attach = keyspan_pda_fake_startup, }; #endif static struct usb_serial_device_type keyspan_pda_device = { - owner: THIS_MODULE, - name: "Keyspan PDA", - id_table: id_table_std, - num_interrupt_in: 1, - num_bulk_in: 0, - num_bulk_out: 1, - num_ports: 1, - open: keyspan_pda_open, - close: keyspan_pda_close, - write: keyspan_pda_write, - write_room: keyspan_pda_write_room, - write_bulk_callback: keyspan_pda_write_bulk_callback, - read_int_callback: keyspan_pda_rx_interrupt, - chars_in_buffer: keyspan_pda_chars_in_buffer, - throttle: keyspan_pda_rx_throttle, - unthrottle: keyspan_pda_rx_unthrottle, - ioctl: keyspan_pda_ioctl, - set_termios: keyspan_pda_set_termios, - break_ctl: keyspan_pda_break_ctl, - attach: keyspan_pda_startup, - shutdown: keyspan_pda_shutdown, + .owner = THIS_MODULE, + .name = "Keyspan PDA", + .id_table = id_table_std, + .num_interrupt_in = 1, + .num_bulk_in = 0, + .num_bulk_out = 1, + .num_ports = 1, + .open = keyspan_pda_open, + .close = keyspan_pda_close, + .write = keyspan_pda_write, + .write_room = keyspan_pda_write_room, + .write_bulk_callback = keyspan_pda_write_bulk_callback, + .read_int_callback = keyspan_pda_rx_interrupt, + .chars_in_buffer = keyspan_pda_chars_in_buffer, + .throttle = keyspan_pda_rx_throttle, + .unthrottle = keyspan_pda_rx_unthrottle, + .ioctl = keyspan_pda_ioctl, + .set_termios = keyspan_pda_set_termios, + .break_ctl = keyspan_pda_break_ctl, + .attach = keyspan_pda_startup, + .shutdown = keyspan_pda_shutdown, }; diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 22a965066c3d..58e83e62c6de 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -119,27 +119,27 @@ MODULE_DEVICE_TABLE (usb, id_table); static struct usb_serial_device_type kl5kusb105d_device = { - owner: THIS_MODULE, - name: "KL5KUSB105D / PalmConnect", - id_table: id_table, - num_interrupt_in: 1, - num_bulk_in: 1, - num_bulk_out: 1, - num_ports: 1, - open: klsi_105_open, - close: klsi_105_close, - write: klsi_105_write, - write_bulk_callback: klsi_105_write_bulk_callback, - chars_in_buffer: klsi_105_chars_in_buffer, - write_room: klsi_105_write_room, - read_bulk_callback: klsi_105_read_bulk_callback, - ioctl: klsi_105_ioctl, - set_termios: klsi_105_set_termios, + .owner = THIS_MODULE, + .name = "KL5KUSB105D / PalmConnect", + .id_table = id_table, + .num_interrupt_in = 1, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = klsi_105_open, + .close = klsi_105_close, + .write = klsi_105_write, + .write_bulk_callback = klsi_105_write_bulk_callback, + .chars_in_buffer = klsi_105_chars_in_buffer, + .write_room = klsi_105_write_room, + .read_bulk_callback =klsi_105_read_bulk_callback, + .ioctl = klsi_105_ioctl, + .set_termios = klsi_105_set_termios, /*break_ctl: klsi_105_break_ctl,*/ - attach: klsi_105_startup, - shutdown: klsi_105_shutdown, - throttle: klsi_105_throttle, - unthrottle: klsi_105_unthrottle, + .attach = klsi_105_startup, + .shutdown = klsi_105_shutdown, + .throttle = klsi_105_throttle, + .unthrottle = klsi_105_unthrottle, }; struct klsi_105_port_settings { diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 1cfae6462896..9225f9396f42 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -141,25 +141,25 @@ MODULE_DEVICE_TABLE (usb, id_table_combined); static struct usb_serial_device_type mct_u232_device = { - owner: THIS_MODULE, - name: "Magic Control Technology USB-RS232", - id_table: id_table_combined, - num_interrupt_in: 2, - num_bulk_in: 0, - num_bulk_out: 1, - num_ports: 1, - open: mct_u232_open, - close: mct_u232_close, + .owner = THIS_MODULE, + .name = "Magic Control Technology USB-RS232", + .id_table = id_table_combined, + .num_interrupt_in = 2, + .num_bulk_in = 0, + .num_bulk_out = 1, + .num_ports = 1, + .open = mct_u232_open, + .close = mct_u232_close, #ifdef FIX_WRITE_RETURN_CODE_PROBLEM - write: mct_u232_write, - write_bulk_callback: mct_u232_write_bulk_callback, + .write = mct_u232_write, + .write_bulk_callback = mct_u232_write_bulk_callback, #endif - read_int_callback: mct_u232_read_int_callback, - ioctl: mct_u232_ioctl, - set_termios: mct_u232_set_termios, - break_ctl: mct_u232_break_ctl, - attach: mct_u232_startup, - shutdown: mct_u232_shutdown, + .read_int_callback = mct_u232_read_int_callback, + .ioctl = mct_u232_ioctl, + .set_termios = mct_u232_set_termios, + .break_ctl = mct_u232_break_ctl, + .attach = mct_u232_startup, + .shutdown = mct_u232_shutdown, }; diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index fbce2098e4fd..2cea1f883c9f 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -85,20 +85,20 @@ MODULE_DEVICE_TABLE (usb, id_table); static struct usb_serial_device_type zyxel_omninet_device = { - owner: THIS_MODULE, - name: "ZyXEL - omni.net lcd plus usb", - id_table: id_table, - num_interrupt_in: 1, - num_bulk_in: 1, - num_bulk_out: 2, - num_ports: 1, - open: omninet_open, - close: omninet_close, - write: omninet_write, - write_room: omninet_write_room, - read_bulk_callback: omninet_read_bulk_callback, - write_bulk_callback: omninet_write_bulk_callback, - shutdown: omninet_shutdown, + .owner = THIS_MODULE, + .name = "ZyXEL - omni.net lcd plus usb", + .id_table = id_table, + .num_interrupt_in = 1, + .num_bulk_in = 1, + .num_bulk_out = 2, + .num_ports = 1, + .open = omninet_open, + .close = omninet_close, + .write = omninet_write, + .write_room = omninet_write_room, + .read_bulk_callback = omninet_read_bulk_callback, + .write_bulk_callback = omninet_write_bulk_callback, + .shutdown = omninet_shutdown, }; diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index fb3ffd06ad2d..b4882dba51ec 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -118,24 +118,24 @@ static void pl2303_shutdown (struct usb_serial *serial); /* All of the device info needed for the PL2303 SIO serial converter */ static struct usb_serial_device_type pl2303_device = { - owner: THIS_MODULE, - name: "PL-2303", - id_table: id_table, - num_interrupt_in: NUM_DONT_CARE, - num_bulk_in: 1, - num_bulk_out: 1, - num_ports: 1, - open: pl2303_open, - close: pl2303_close, - write: pl2303_write, - ioctl: pl2303_ioctl, - break_ctl: pl2303_break_ctl, - set_termios: pl2303_set_termios, - read_bulk_callback: pl2303_read_bulk_callback, - read_int_callback: pl2303_read_int_callback, - write_bulk_callback: pl2303_write_bulk_callback, - attach: pl2303_startup, - shutdown: pl2303_shutdown, + .owner = THIS_MODULE, + .name = "PL-2303", + .id_table = id_table, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = pl2303_open, + .close = pl2303_close, + .write = pl2303_write, + .ioctl = pl2303_ioctl, + .break_ctl = pl2303_break_ctl, + .set_termios = pl2303_set_termios, + .read_bulk_callback = pl2303_read_bulk_callback, + .read_int_callback = pl2303_read_int_callback, + .write_bulk_callback = pl2303_write_bulk_callback, + .attach = pl2303_startup, + .shutdown = pl2303_shutdown, }; struct pl2303_private { diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index bc1fe0713ab9..54da202b1b95 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -399,17 +399,17 @@ static int safe_startup (struct usb_serial *serial) } static struct usb_serial_device_type safe_device = { - owner: THIS_MODULE, - name: "Safe", - id_table: id_table, - num_interrupt_in: NUM_DONT_CARE, - num_bulk_in: NUM_DONT_CARE, - num_bulk_out: NUM_DONT_CARE, - num_ports: 1, - write: safe_write, - write_room: safe_write_room, - read_bulk_callback: safe_read_bulk_callback, - attach: safe_startup, + .owner = THIS_MODULE, + .name = "Safe", + .id_table = id_table, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = NUM_DONT_CARE, + .num_bulk_out = NUM_DONT_CARE, + .num_ports = 1, + .write = safe_write, + .write_room = safe_write_room, + .read_bulk_callback = safe_read_bulk_callback, + .attach = safe_startup, }; static int __init safe_init (void) diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index 2e556292db6a..ae7f1f3da7a5 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -366,14 +366,14 @@ static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */ /* All of the device info needed for the Generic Serial Converter */ static struct usb_serial_device_type generic_device = { - owner: THIS_MODULE, - name: "Generic", - id_table: generic_device_ids, - num_interrupt_in: NUM_DONT_CARE, - num_bulk_in: NUM_DONT_CARE, - num_bulk_out: NUM_DONT_CARE, - num_ports: 1, - shutdown: generic_shutdown, + .owner = THIS_MODULE, + .name = "Generic", + .id_table = generic_device_ids, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = NUM_DONT_CARE, + .num_bulk_out = NUM_DONT_CARE, + .num_ports = 1, + .shutdown = generic_shutdown, }; #endif @@ -395,10 +395,10 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum, static void usb_serial_disconnect(struct usb_device *dev, void *ptr); static struct usb_driver usb_serial_driver = { - name: "serial", - probe: usb_serial_probe, - disconnect: usb_serial_disconnect, - id_table: NULL, /* check all devices */ + .name = "serial", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = NULL, /* check all devices */ }; /* There is no MODULE_DEVICE_TABLE for usbserial.c. Instead @@ -447,24 +447,18 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po good_spot = 1; for (j = 1; j <= num_ports-1; ++j) - if (serial_table[i+j]) + if ((serial_table[i+j]) || (i+j >= SERIAL_TTY_MINORS)) { good_spot = 0; + i += j; + break; + } if (good_spot == 0) continue; - if (!serial) { - serial = kmalloc(sizeof(*serial), GFP_KERNEL); - if (!serial) { - err(__FUNCTION__ " - Out of memory"); - return NULL; - } - memset(serial, 0, sizeof(*serial)); - } serial->magic = USB_SERIAL_MAGIC; - serial_table[i] = serial; *minor = i; dbg(__FUNCTION__ " - minor base = %d", *minor); - for (i = *minor+1; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) + for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) serial_table[i] = serial; return serial; } @@ -1207,14 +1201,14 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum, return(NULL); } + serial = create_serial (dev, interface, type); + if (!serial) { + err ("%s - out of memory", __FUNCTION__); + return NULL; + } + /* if this device type has a probe function, call it */ if (type->probe) { - serial = create_serial (dev, interface, type); - if (!serial) { - err ("%s - out of memory", __FUNCTION__); - return NULL; - } - if (type->owner) __MOD_INC_USE_COUNT(type->owner); retval = type->probe (serial); @@ -1293,6 +1287,7 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum, num_ports = num_bulk_out; if (num_ports == 0) { err("Generic device with no bulk out, not allowed."); + kfree (serial); return NULL; } } @@ -1300,14 +1295,6 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum, if (!num_ports) { /* if this device type has a calc_num_ports function, call it */ if (type->calc_num_ports) { - if (!serial) { - serial = create_serial (dev, interface, type); - if (!serial) { - err ("%s - out of memory", __FUNCTION__); - return NULL; - } - } - if (type->owner) __MOD_INC_USE_COUNT(type->owner); num_ports = type->calc_num_ports (serial); @@ -1318,22 +1305,17 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum, num_ports = type->num_ports; } - serial = get_free_serial (serial, num_ports, &minor); - if (serial == NULL) { + if (get_free_serial (serial, num_ports, &minor) == NULL) { err("No more free serial devices"); + kfree (serial); return NULL; } - serial->dev = dev; - serial->type = type; - serial->interface = interface; serial->minor = minor; serial->num_ports = num_ports; serial->num_bulk_in = num_bulk_in; serial->num_bulk_out = num_bulk_out; serial->num_interrupt_in = num_interrupt_in; - serial->vendor = dev->descriptor.idVendor; - serial->product = dev->descriptor.idProduct; /* set up the endpoint information */ for (i = 0; i < num_bulk_in; ++i) { @@ -1577,32 +1559,32 @@ static void usb_serial_disconnect(struct usb_device *dev, void *ptr) static struct tty_driver serial_tty_driver = { - magic: TTY_DRIVER_MAGIC, - driver_name: "usb-serial", - name: "usb/tts/%d", - major: SERIAL_TTY_MAJOR, - minor_start: 0, - num: SERIAL_TTY_MINORS, - type: TTY_DRIVER_TYPE_SERIAL, - subtype: SERIAL_TYPE_NORMAL, - flags: TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, - - refcount: &serial_refcount, - table: serial_tty, - termios: serial_termios, - termios_locked: serial_termios_locked, - - open: serial_open, - close: serial_close, - write: serial_write, - write_room: serial_write_room, - ioctl: serial_ioctl, - set_termios: serial_set_termios, - throttle: serial_throttle, - unthrottle: serial_unthrottle, - break_ctl: serial_break, - chars_in_buffer: serial_chars_in_buffer, - read_proc: serial_read_proc, + .magic = TTY_DRIVER_MAGIC, + .driver_name = "usb-serial", + .name = "usb/tts/%d", + .major = SERIAL_TTY_MAJOR, + .minor_start = 0, + .num = SERIAL_TTY_MINORS, + .type = TTY_DRIVER_TYPE_SERIAL, + .subtype = SERIAL_TYPE_NORMAL, + .flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, + + .refcount = &serial_refcount, + .table = serial_tty, + .termios = serial_termios, + .termios_locked = serial_termios_locked, + + .open = serial_open, + .close = serial_close, + .write = serial_write, + .write_room = serial_write_room, + .ioctl = serial_ioctl, + .set_termios = serial_set_termios, + .throttle = serial_throttle, + .unthrottle = serial_unthrottle, + .break_ctl = serial_break, + .chars_in_buffer = serial_chars_in_buffer, + .read_proc = serial_read_proc, }; @@ -1931,15 +1913,15 @@ static int usb_console_wait_key(struct console *co) #endif static struct console usbcons = { - name: "ttyUSB", /* only [8] */ - write: usb_console_write, + .name = "ttyUSB", /* only [8] */ + .write = usb_console_write, #if 0 - device: usb_console_device, /* TBD */ - wait_key: usb_console_wait_key, /* TBD */ + .device = usb_console_device, /* TBD */ + .wait_key = usb_console_wait_key, /* TBD */ #endif - setup: usb_console_setup, - flags: CON_PRINTBUFFER, - index: -1, + .setup = usb_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, }; #endif /* CONFIG_USB_SERIAL_CONSOLE */ diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 3eaefb051c5c..146394d96d91 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -218,50 +218,50 @@ MODULE_DEVICE_TABLE (usb, id_table_combined); /* All of the device info needed for the Handspring Visor, and Palm 4.0 devices */ static struct usb_serial_device_type handspring_device = { - owner: THIS_MODULE, - name: "Handspring Visor / Palm 4.0 / Clié 4.x", - id_table: id_table, - num_interrupt_in: 0, - num_bulk_in: 2, - num_bulk_out: 2, - num_ports: 2, - open: visor_open, - close: visor_close, - throttle: visor_throttle, - unthrottle: visor_unthrottle, - probe: visor_probe, - calc_num_ports: visor_calc_num_ports, - shutdown: visor_shutdown, - ioctl: visor_ioctl, - set_termios: visor_set_termios, - write: visor_write, - write_room: visor_write_room, - chars_in_buffer: visor_chars_in_buffer, - write_bulk_callback: visor_write_bulk_callback, - read_bulk_callback: visor_read_bulk_callback, + .owner = THIS_MODULE, + .name = "Handspring Visor / Palm 4.0 / Clié 4.x", + .id_table = id_table, + .num_interrupt_in = 0, + .num_bulk_in = 2, + .num_bulk_out = 2, + .num_ports = 2, + .open = visor_open, + .close = visor_close, + .throttle = visor_throttle, + .unthrottle = visor_unthrottle, + .probe = visor_probe, + .calc_num_ports = visor_calc_num_ports, + .shutdown = visor_shutdown, + .ioctl = visor_ioctl, + .set_termios = visor_set_termios, + .write = visor_write, + .write_room = visor_write_room, + .chars_in_buffer = visor_chars_in_buffer, + .write_bulk_callback = visor_write_bulk_callback, + .read_bulk_callback = visor_read_bulk_callback, }; /* device info for the Sony Clie OS version 3.5 */ static struct usb_serial_device_type clie_3_5_device = { - owner: THIS_MODULE, - name: "Sony Clié 3.5", - id_table: clie_id_3_5_table, - num_interrupt_in: 0, - num_bulk_in: 1, - num_bulk_out: 1, - num_ports: 1, - open: visor_open, - close: visor_close, - throttle: visor_throttle, - unthrottle: visor_unthrottle, - attach: clie_3_5_startup, - ioctl: visor_ioctl, - set_termios: visor_set_termios, - write: visor_write, - write_room: visor_write_room, - chars_in_buffer: visor_chars_in_buffer, - write_bulk_callback: visor_write_bulk_callback, - read_bulk_callback: visor_read_bulk_callback, + .owner = THIS_MODULE, + .name = "Sony Clié 3.5", + .id_table = clie_id_3_5_table, + .num_interrupt_in = 0, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = visor_open, + .close = visor_close, + .throttle = visor_throttle, + .unthrottle = visor_unthrottle, + .attach = clie_3_5_startup, + .ioctl = visor_ioctl, + .set_termios = visor_set_termios, + .write = visor_write, + .write_room = visor_write_room, + .chars_in_buffer = visor_chars_in_buffer, + .write_bulk_callback = visor_write_bulk_callback, + .read_bulk_callback = visor_read_bulk_callback, }; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 483dfa361700..1e41ef169a87 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -130,32 +130,32 @@ static int whiteheat_attach (struct usb_serial *serial); static void whiteheat_shutdown (struct usb_serial *serial); static struct usb_serial_device_type whiteheat_fake_device = { - owner: THIS_MODULE, - name: "Connect Tech - WhiteHEAT - (prerenumeration)", - id_table: id_table_prerenumeration, - num_interrupt_in: NUM_DONT_CARE, - num_bulk_in: NUM_DONT_CARE, - num_bulk_out: NUM_DONT_CARE, - num_ports: 1, - probe: whiteheat_firmware_download, + .owner = THIS_MODULE, + .name = "Connect Tech - WhiteHEAT - (prerenumeration)", + .id_table = id_table_prerenumeration, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = NUM_DONT_CARE, + .num_bulk_out = NUM_DONT_CARE, + .num_ports = 1, + .probe = whiteheat_firmware_download, }; static struct usb_serial_device_type whiteheat_device = { - owner: THIS_MODULE, - name: "Connect Tech - WhiteHEAT", - id_table: id_table_std, - num_interrupt_in: NUM_DONT_CARE, - num_bulk_in: NUM_DONT_CARE, - num_bulk_out: NUM_DONT_CARE, - num_ports: 4, - open: whiteheat_open, - close: whiteheat_close, - throttle: whiteheat_throttle, - unthrottle: whiteheat_unthrottle, - ioctl: whiteheat_ioctl, - set_termios: whiteheat_set_termios, - attach: whiteheat_attach, - shutdown: whiteheat_shutdown, + .owner = THIS_MODULE, + .name = "Connect Tech - WhiteHEAT", + .id_table = id_table_std, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = NUM_DONT_CARE, + .num_bulk_out = NUM_DONT_CARE, + .num_ports = 4, + .open = whiteheat_open, + .close = whiteheat_close, + .throttle = whiteheat_throttle, + .unthrottle = whiteheat_unthrottle, + .ioctl = whiteheat_ioctl, + .set_termios = whiteheat_set_termios, + .attach = whiteheat_attach, + .shutdown = whiteheat_shutdown, }; struct whiteheat_private { diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index e1fc6507734a..21fb4e6c1835 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -354,29 +354,29 @@ static int proc_info (char *buffer, char **start, off_t offset, int length, */ Scsi_Host_Template usb_stor_host_template = { - name: "usb-storage", - proc_info: proc_info, - info: host_info, - - detect: detect, - release: release, - command: command, - queuecommand: queuecommand, - - eh_abort_handler: command_abort, - eh_device_reset_handler:device_reset, - eh_bus_reset_handler: bus_reset, - eh_host_reset_handler: host_reset, - - can_queue: 1, - this_id: -1, - - sg_tablesize: SG_ALL, - cmd_per_lun: 1, - present: 0, - unchecked_isa_dma: FALSE, - use_clustering: TRUE, - emulated: TRUE + .name = "usb-storage", + .proc_info = proc_info, + .info = host_info, + + .detect = detect, + .release = release, + .command = command, + .queuecommand = queuecommand, + + .eh_abort_handler = command_abort, + .eh_device_reset_handler =device_reset, + .eh_bus_reset_handler = bus_reset, + .eh_host_reset_handler =host_reset, + + .can_queue = 1, + .this_id = -1, + + .sg_tablesize = SG_ALL, + .cmd_per_lun = 1, + .present = 0, + .unchecked_isa_dma = FALSE, + .use_clustering = TRUE, + .emulated = TRUE }; unsigned char usb_stor_sense_notready[18] = { diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 2e8b68f47517..c0bdea0c4e6e 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -170,12 +170,12 @@ MODULE_DEVICE_TABLE (usb, storage_usb_ids); vendor_name, product_name, use_protocol, use_transport, \ init_function, Flags) \ { \ - vendorName: vendor_name, \ - productName: product_name, \ - useProtocol: use_protocol, \ - useTransport: use_transport, \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ initFunction : init_function, \ - flags: Flags, \ + .flags = Flags, \ } static struct us_unusual_dev us_unusual_dev_list[] = { @@ -228,10 +228,10 @@ static struct us_unusual_dev us_unusual_dev_list[] = { }; struct usb_driver usb_storage_driver = { - name: "usb-storage", - probe: storage_probe, - disconnect: storage_disconnect, - id_table: storage_usb_ids, + .name = "usb-storage", + .probe = storage_probe, + .disconnect = storage_disconnect, + .id_table = storage_usb_ids, }; /* diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 64fb0078a6cc..f73d6ba9d43b 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -174,22 +174,22 @@ static struct file_operations skel_fops = { * and decrement it again in the release() function * yourself. */ - owner: THIS_MODULE, + .owner = THIS_MODULE, - read: skel_read, - write: skel_write, - ioctl: skel_ioctl, - open: skel_open, - release: skel_release, + .read = skel_read, + .write = skel_write, + .ioctl = skel_ioctl, + .open = skel_open, + .release = skel_release, }; /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver skel_driver = { - name: "skeleton", - probe: skel_probe, - disconnect: skel_disconnect, - id_table: skel_table, + .name = "skeleton", + .probe = skel_probe, + .disconnect = skel_disconnect, + .id_table = skel_table, }; |
