diff options
Diffstat (limited to 'drivers')
143 files changed, 11480 insertions, 1500 deletions
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index 373e7b728fa..6b2eb6f39b4 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -152,12 +152,13 @@ attribute_container_add_device(struct device *dev, if (!cont->match(cont, dev)) continue; - ic = kmalloc(sizeof(struct internal_container), GFP_KERNEL); + + ic = kzalloc(sizeof(*ic), GFP_KERNEL); if (!ic) { dev_printk(KERN_ERR, dev, "failed to allocate class container\n"); continue; } - memset(ic, 0, sizeof(struct internal_container)); + ic->cont = cont; class_device_initialize(&ic->classdev); ic->classdev.dev = get_device(dev); diff --git a/drivers/base/class.c b/drivers/base/class.c index d164c32a97a..3b112e3542f 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -189,12 +189,11 @@ struct class *class_create(struct module *owner, char *name) struct class *cls; int retval; - cls = kmalloc(sizeof(struct class), GFP_KERNEL); + cls = kzalloc(sizeof(*cls), GFP_KERNEL); if (!cls) { retval = -ENOMEM; goto error; } - memset(cls, 0x00, sizeof(struct class)); cls->name = name; cls->owner = owner; @@ -500,13 +499,13 @@ int class_device_add(struct class_device *class_dev) /* add the needed attributes to this device */ if (MAJOR(class_dev->devt)) { struct class_device_attribute *attr; - attr = kmalloc(sizeof(*attr), GFP_KERNEL); + attr = kzalloc(sizeof(*attr), GFP_KERNEL); if (!attr) { error = -ENOMEM; kobject_del(&class_dev->kobj); goto register_done; } - memset(attr, sizeof(*attr), 0x00); + attr->attr.name = "dev"; attr->attr.mode = S_IRUGO; attr->attr.owner = parent->owner; @@ -577,12 +576,11 @@ struct class_device *class_device_create(struct class *cls, dev_t devt, if (cls == NULL || IS_ERR(cls)) goto error; - class_dev = kmalloc(sizeof(struct class_device), GFP_KERNEL); + class_dev = kzalloc(sizeof(*class_dev), GFP_KERNEL); if (!class_dev) { retval = -ENOMEM; goto error; } - memset(class_dev, 0x00, sizeof(struct class_device)); class_dev->devt = devt; class_dev->dev = device; diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 5bfa2e9a7c2..4acb2c5733c 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -301,9 +301,9 @@ fw_register_class_device(struct class_device **class_dev_p, const char *fw_name, struct device *device) { int retval; - struct firmware_priv *fw_priv = kmalloc(sizeof (struct firmware_priv), + struct firmware_priv *fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL); - struct class_device *class_dev = kmalloc(sizeof (struct class_device), + struct class_device *class_dev = kzalloc(sizeof(*class_dev), GFP_KERNEL); *class_dev_p = NULL; @@ -313,8 +313,6 @@ fw_register_class_device(struct class_device **class_dev_p, retval = -ENOMEM; goto error_kfree; } - memset(fw_priv, 0, sizeof (*fw_priv)); - memset(class_dev, 0, sizeof (*class_dev)); init_completion(&fw_priv->completion); fw_priv->attr_data = firmware_attr_data_tmpl; @@ -402,14 +400,13 @@ _request_firmware(const struct firmware **firmware_p, const char *name, if (!firmware_p) return -EINVAL; - *firmware_p = firmware = kmalloc(sizeof (struct firmware), GFP_KERNEL); + *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); if (!firmware) { printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n", __FUNCTION__); retval = -ENOMEM; goto out; } - memset(firmware, 0, sizeof (*firmware)); retval = fw_setup_class_device(firmware, &class_dev, name, device, hotplug); diff --git a/drivers/base/map.c b/drivers/base/map.c index 2f455d86793..b449dae6f0d 100644 --- a/drivers/base/map.c +++ b/drivers/base/map.c @@ -135,7 +135,7 @@ retry: struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct semaphore *sem) { struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL); - struct probe *base = kmalloc(sizeof(struct probe), GFP_KERNEL); + struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL); int i; if ((p == NULL) || (base == NULL)) { @@ -144,7 +144,6 @@ struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct semaphore *sem) return NULL; } - memset(base, 0, sizeof(struct probe)); base->dev = 1; base->range = ~0; base->get = base_probe; diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 3a5f4c99179..361e204209e 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -225,13 +225,12 @@ struct platform_device *platform_device_register_simple(char *name, unsigned int struct platform_object *pobj; int retval; - pobj = kmalloc(sizeof(struct platform_object) + sizeof(struct resource) * num, GFP_KERNEL); + pobj = kzalloc(sizeof(*pobj) + sizeof(struct resource) * num, GFP_KERNEL); if (!pobj) { retval = -ENOMEM; goto error; } - memset(pobj, 0, sizeof(*pobj)); pobj->pdev.name = name; pobj->pdev.id = id; pobj->pdev.dev.release = platform_device_release_simple; diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h index 721ba808604..0e9e586e9ba 100644 --- a/drivers/block/aoe/aoe.h +++ b/drivers/block/aoe/aoe.h @@ -1,5 +1,5 @@ /* Copyright (c) 2004 Coraid, Inc. See COPYING for GPL terms. */ -#define VERSION "10" +#define VERSION "12" #define AOE_MAJOR 152 #define DEVICE_NAME "aoe" @@ -7,12 +7,12 @@ * default is 16, which is 15 partitions plus the whole disk */ #ifndef AOE_PARTITIONS -#define AOE_PARTITIONS 16 +#define AOE_PARTITIONS (16) #endif -#define SYSMINOR(aoemajor, aoeminor) ((aoemajor) * 10 + (aoeminor)) -#define AOEMAJOR(sysminor) ((sysminor) / 10) -#define AOEMINOR(sysminor) ((sysminor) % 10) +#define SYSMINOR(aoemajor, aoeminor) ((aoemajor) * NPERSHELF + (aoeminor)) +#define AOEMAJOR(sysminor) ((sysminor) / NPERSHELF) +#define AOEMINOR(sysminor) ((sysminor) % NPERSHELF) #define WHITESPACE " \t\v\f\n" enum { @@ -83,7 +83,7 @@ enum { enum { MAXATADATA = 1024, - NPERSHELF = 10, + NPERSHELF = 16, /* number of slots per shelf address */ FREETAG = -1, MIN_BUFS = 8, }; diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 28f2c177a54..c56f995aada 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -47,14 +47,14 @@ #include <linux/completion.h> #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) -#define DRIVER_NAME "HP CISS Driver (v 2.6.6)" -#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,6) +#define DRIVER_NAME "HP CISS Driver (v 2.6.8)" +#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,8) /* Embedded module documentation macros - see modules.h */ MODULE_AUTHOR("Hewlett-Packard Company"); -MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.6"); +MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.8"); MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400" - " SA6i P600 P800 E400 E300"); + " SA6i P600 P800 P400 P400i E200 E200i"); MODULE_LICENSE("GPL"); #include "cciss_cmd.h" @@ -83,12 +83,22 @@ static const struct pci_device_id cciss_pci_device_id[] = { 0x0E11, 0x4091, 0, 0, 0}, { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSA, 0x103C, 0x3225, 0, 0, 0}, - { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSB, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, 0x103c, 0x3223, 0, 0, 0}, { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, - 0x103c, 0x3231, 0, 0, 0}, + 0x103c, 0x3234, 0, 0, 0}, { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, - 0x103c, 0x3233, 0, 0, 0}, + 0x103c, 0x3235, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, + 0x103c, 0x3211, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, + 0x103c, 0x3212, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, + 0x103c, 0x3213, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, + 0x103c, 0x3214, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, + 0x103c, 0x3215, 0, 0, 0}, {0,} }; MODULE_DEVICE_TABLE(pci, cciss_pci_device_id); @@ -111,8 +121,13 @@ static struct board_type products[] = { { 0x40910E11, "Smart Array 6i", &SA5_access}, { 0x3225103C, "Smart Array P600", &SA5_access}, { 0x3223103C, "Smart Array P800", &SA5_access}, - { 0x3231103C, "Smart Array E400", &SA5_access}, - { 0x3233103C, "Smart Array E300", &SA5_access}, + { 0x3234103C, "Smart Array P400", &SA5_access}, + { 0x3235103C, "Smart Array P400i", &SA5_access}, + { 0x3211103C, "Smart Array E200i", &SA5_access}, + { 0x3212103C, "Smart Array E200", &SA5_access}, + { 0x3213103C, "Smart Array E200i", &SA5_access}, + { 0x3214103C, "Smart Array E200i", &SA5_access}, + { 0x3215103C, "Smart Array E200i", &SA5_access}, }; /* How long to wait (in millesconds) for board to go into simple mode */ @@ -140,15 +155,26 @@ static int cciss_ioctl(struct inode *inode, struct file *filep, static int revalidate_allvol(ctlr_info_t *host); static int cciss_revalidate(struct gendisk *disk); -static int deregister_disk(struct gendisk *disk); -static int register_new_disk(ctlr_info_t *h); +static int rebuild_lun_table(ctlr_info_t *h, struct gendisk *del_disk); +static int deregister_disk(struct gendisk *disk, drive_info_struct *drv, int clear_all); +static void cciss_read_capacity(int ctlr, int logvol, ReadCapdata_struct *buf, + int withirq, unsigned int *total_size, unsigned int *block_size); +static void cciss_geometry_inquiry(int ctlr, int logvol, + int withirq, unsigned int total_size, + unsigned int block_size, InquiryData_struct *inq_buff, + drive_info_struct *drv); static void cciss_getgeometry(int cntl_num); static void start_io( ctlr_info_t *h); static int sendcmd( __u8 cmd, int ctlr, void *buff, size_t size, unsigned int use_unit_num, unsigned int log_unit, __u8 page_code, unsigned char *scsi3addr, int cmd_type); +static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size, + unsigned int use_unit_num, unsigned int log_unit, __u8 page_code, + int cmd_type); + +static void fail_all_cmds(unsigned long ctlr); #ifdef CONFIG_PROC_FS static int cciss_proc_get_info(char *buffer, char **start, off_t offset, @@ -265,7 +291,7 @@ static int cciss_proc_get_info(char *buffer, char **start, off_t offset, for(i=0; i<=h->highest_lun; i++) { drv = &h->drv[i]; - if (drv->block_size == 0) + if (drv->heads == 0) continue; vol_sz = drv->nr_blocks; @@ -363,6 +389,8 @@ static CommandList_struct * cmd_alloc(ctlr_info_t *h, int get_from_pool) return NULL; memset(c, 0, sizeof(CommandList_struct)); + c->cmdindex = -1; + c->err_info = (ErrorInfo_struct *)pci_alloc_consistent( h->pdev, sizeof(ErrorInfo_struct), &err_dma_handle); @@ -393,6 +421,8 @@ static CommandList_struct * cmd_alloc(ctlr_info_t *h, int get_from_pool) err_dma_handle = h->errinfo_pool_dhandle + i*sizeof(ErrorInfo_struct); h->nr_allocs++; + + c->cmdindex = i; } c->busaddr = (__u32) cmd_dma_handle; @@ -453,6 +483,11 @@ static int cciss_open(struct inode *inode, struct file *filep) printk(KERN_DEBUG "cciss_open %s\n", inode->i_bdev->bd_disk->disk_name); #endif /* CCISS_DEBUG */ + if (host->busy_initializing) + return -EBUSY; + + if (host->busy_initializing || drv->busy_configuring) + return -EBUSY; /* * Root is allowed to open raw volume zero even if it's not configured * so array config can still work. Root is also allowed to open any @@ -796,10 +831,10 @@ static int cciss_ioctl(struct inode *inode, struct file *filep, return(0); } case CCISS_DEREGDISK: - return deregister_disk(disk); + return rebuild_lun_table(host, disk); case CCISS_REGNEWD: - return register_new_disk(host); + return rebuild_lun_table(host, NULL); case CCISS_PASSTHRU: { @@ -1143,48 +1178,323 @@ static int revalidate_allvol(ctlr_info_t *host) return 0; } -static int deregister_disk(struct gendisk *disk) +/* This function will check the usage_count of the drive to be updated/added. + * If the usage_count is zero then the drive information will be updated and + * the disk will be re-registered with the kernel. If not then it will be + * left alone for the next reboot. The exception to this is disk 0 which + * will always be left registered with the kernel since it is also the + * controller node. Any changes to disk 0 will show up on the next + * reboot. +*/ +static void cciss_update_drive_info(int ctlr, int drv_index) + { + ctlr_info_t *h = hba[ctlr]; + struct gendisk *disk; + ReadCapdata_struct *size_buff = NULL; + InquiryData_struct *inq_buff = NULL; + unsigned int block_size; + unsigned int total_size; + unsigned long flags = 0; + int ret = 0; + + /* if the disk already exists then deregister it before proceeding*/ + if (h->drv[drv_index].raid_level != -1){ + spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); + h->drv[drv_index].busy_configuring = 1; + spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); + ret = deregister_disk(h->gendisk[drv_index], + &h->drv[drv_index], 0); + h->drv[drv_index].busy_configuring = 0; + } + + /* If the disk is in use return */ + if (ret) + return; + + + /* Get information about the disk and modify the driver sturcture */ + size_buff = kmalloc(sizeof( ReadCapdata_struct), GFP_KERNEL); + if (size_buff == NULL) + goto mem_msg; + inq_buff = kmalloc(sizeof( InquiryData_struct), GFP_KERNEL); + if (inq_buff == NULL) + goto mem_msg; + + cciss_read_capacity(ctlr, drv_index, size_buff, 1, + &total_size, &block_size); + cciss_geometry_inquiry(ctlr, drv_index, 1, total_size, block_size, + inq_buff, &h->drv[drv_index]); + + ++h->num_luns; + disk = h->gendisk[drv_index]; + set_capacity(disk, h->drv[drv_index].nr_blocks); + + + /* if it's the controller it's already added */ + if (drv_index){ + disk->queue = blk_init_queue(do_cciss_request, &h->lock); + + /* Set up queue information */ + disk->queue->backing_dev_info.ra_pages = READ_AHEAD; + blk_queue_bounce_limit(disk->queue, hba[ctlr]->pdev->dma_mask); + + /* This is a hardware imposed limit. */ + blk_queue_max_hw_segments(disk->queue, MAXSGENTRIES); + + /* This is a limit in the driver and could be eliminated. */ + blk_queue_max_phys_segments(disk->queue, MAXSGENTRIES); + + blk_queue_max_sectors(disk->queue, 512); + + disk->queue->queuedata = hba[ctlr]; + + blk_queue_hardsect_size(disk->queue, + hba[ctlr]->drv[drv_index].block_size); + + h->drv[drv_index].queue = disk->queue; + add_disk(disk); + } + +freeret: + kfree(size_buff); + kfree(inq_buff); + return; +mem_msg: + printk(KERN_ERR "cciss: out of memory\n"); + goto freeret; +} + +/* This function will find the first index of the controllers drive array + * that has a -1 for the raid_level and will return that index. This is + * where new drives will be added. If the index to be returned is greater + * than the highest_lun index for the controller then highest_lun is set + * to this new index. If there are no available indexes then -1 is returned. +*/ +static int cciss_find_free_drive_index(int ctlr) { + int i; + + for (i=0; i < CISS_MAX_LUN; i++){ + if (hba[ctlr]->drv[i].raid_level == -1){ + if (i > hba[ctlr]->highest_lun) + hba[ctlr]->highest_lun = i; + return i; + } + } + return -1; +} + +/* This function will add and remove logical drives from the Logical + * drive array of the controller and maintain persistancy of ordering + * so that mount points are preserved until the next reboot. This allows + * for the removal of logical drives in the middle of the drive array + * without a re-ordering of those drives. + * INPUT + * h = The controller to perform the operations on + * del_disk = The disk to remove if specified. If the value given + * is NULL then no disk is removed. +*/ +static int rebuild_lun_table(ctlr_info_t *h, struct gendisk *del_disk) +{ + int ctlr = h->ctlr; + int num_luns; + ReportLunData_struct *ld_buff = NULL; + drive_info_struct *drv = NULL; + int return_code; + int listlength = 0; + int i; + int drv_found; + int drv_index = 0; + __u32 lunid = 0; unsigned long flags; + + /* Set busy_configuring flag for this operation */ + spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); + if (h->num_luns >= CISS_MAX_LUN){ + spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); + return -EINVAL; + } + + if (h->busy_configuring){ + spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); + return -EBUSY; + } + h->busy_configuring = 1; + + /* if del_disk is NULL then we are being called to add a new disk + * and update the logical drive table. If it is not NULL then + * we will check if the disk is in use or not. + */ + if (del_disk != NULL){ + drv = get_drv(del_disk); + drv->busy_configuring = 1; + spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); + return_code = deregister_disk(del_disk, drv, 1); + drv->busy_configuring = 0; + h->busy_configuring = 0; + return return_code; + } else { + spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + ld_buff = kzalloc(sizeof(ReportLunData_struct), GFP_KERNEL); + if (ld_buff == NULL) + goto mem_msg; + + return_code = sendcmd_withirq(CISS_REPORT_LOG, ctlr, ld_buff, + sizeof(ReportLunData_struct), 0, 0, 0, + TYPE_CMD); + + if (return_code == IO_OK){ + listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[0])) << 24; + listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[1])) << 16; + listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[2])) << 8; + listlength |= 0xff & (unsigned int)(ld_buff->LUNListLength[3]); + } else{ /* reading number of logical volumes failed */ + printk(KERN_WARNING "cciss: report logical volume" + " command failed\n"); + listlength = 0; + goto freeret; + } + + num_luns = listlength / 8; /* 8 bytes per entry */ + if (num_luns > CISS_MAX_LUN){ + num_luns = CISS_MAX_LUN; + printk(KERN_WARNING "cciss: more luns configured" + " on controller than can be handled by" + " this driver.\n"); + } + + /* Compare controller drive array to drivers drive array. + * Check for updates in the drive information and any new drives + * on the controller. + */ + for (i=0; i < num_luns; i++){ + int j; + + drv_found = 0; + + lunid = (0xff & + (unsigned int)(ld_buff->LUN[i][3])) << 24; + lunid |= (0xff & + (unsigned int)(ld_buff->LUN[i][2])) << 16; + lunid |= (0xff & + (unsigned int)(ld_buff->LUN[i][1])) << 8; + lunid |= 0xff & + (unsigned int)(ld_buff->LUN[i][0]); + + /* Find if the LUN is already in the drive array + * of the controller. If so then update its info + * if not is use. If it does not exist then find + * the first free index and add it. + */ + for (j=0; j <= h->highest_lun; j++){ + if (h->drv[j].LunID == lunid){ + drv_index = j; + drv_found = 1; + } + } + + /* check if the drive was found already in the array */ + if (!drv_found){ + drv_index = cciss_find_free_drive_index(ctlr); + if (drv_index == -1) + goto freeret; + + } + h->drv[drv_index].LunID = lunid; + cciss_update_drive_info(ctlr, drv_index); + } /* end for */ + } /* end else */ + +freeret: + kfree(ld_buff); + h->busy_configuring = 0; + /* We return -1 here to tell the ACU that we have registered/updated + * all of the drives that we can and to keep it from calling us + * additional times. + */ + return -1; +mem_msg: + printk(KERN_ERR "cciss: out of memory\n"); + goto freeret; +} + +/* This function will deregister the disk and it's queue from the + * kernel. It must be called with the controller lock held and the + * drv structures busy_configuring flag set. It's parameters are: + * + * disk = This is the disk to be deregistered + * drv = This is the drive_info_struct associated with the disk to be + * deregistered. It contains information about the disk used + * by the driver. + * clear_all = This flag determines whether or not the disk information + * is going to be completely cleared out and the highest_lun + * reset. Sometimes we want to clear out information about + * the disk in preperation for re-adding it. In this case + * the highest_lun should be left unchanged and the LunID + * should not be cleared. +*/ +static int deregister_disk(struct gendisk *disk, drive_info_struct *drv, + int clear_all) +{ ctlr_info_t *h = get_host(disk); - drive_info_struct *drv = get_drv(disk); - int ctlr = h->ctlr; if (!capable(CAP_SYS_RAWIO)) return -EPERM; - spin_lock_irqsave(CCISS_LOCK(ctlr), flags); /* make sure logical volume is NOT is use */ - if( drv->usage_count > 1) { - spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); + if(clear_all || (h->gendisk[0] == disk)) { + if (drv->usage_count > 1) return -EBUSY; } - drv->usage_count++; - spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); + else + if( drv->usage_count > 0 ) + return -EBUSY; - /* invalidate the devices and deregister the disk */ - if (disk->flags & GENHD_FL_UP) + /* invalidate the devices and deregister the disk. If it is disk + * zero do not deregister it but just zero out it's values. This + * allows us to delete disk zero but keep the controller registered. + */ + if (h->gendisk[0] != disk){ + if (disk->flags & GENHD_FL_UP){ + blk_cleanup_queue(disk->queue); del_gendisk(disk); + drv->queue = NULL; + } + } + + --h->num_luns; + /* zero out the disk size info */ + drv->nr_blocks = 0; + drv->block_size = 0; + drv->heads = 0; + drv->sectors = 0; + drv->cylinders = 0; + drv->raid_level = -1; /* This can be used as a flag variable to + * indicate that this element of the drive + * array is free. + */ + + if (clear_all){ /* check to see if it was the last disk */ if (drv == h->drv + h->highest_lun) { /* if so, find the new hightest lun */ int i, newhighest =-1; for(i=0; i<h->highest_lun; i++) { /* if the disk has size > 0, it is available */ - if (h->drv[i].nr_blocks) + if (h->drv[i].heads) newhighest = i; } h->highest_lun = newhighest; - } - --h->num_luns; - /* zero out the disk size info */ - drv->nr_blocks = 0; - drv->block_size = 0; - drv->cylinders = 0; + drv->LunID = 0; + } return(0); } + static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_t size, unsigned int use_unit_num, /* 0: address the controller, @@ -1420,8 +1730,10 @@ case CMD_HARDWARE_ERR: } } /* unlock the buffers from DMA */ + buff_dma_handle.val32.lower = c->SG[0].Addr.lower; + buff_dma_handle.val32.upper = c->SG[0].Addr.upper; pci_unmap_single( h->pdev, (dma_addr_t) buff_dma_handle.val, - size, PCI_DMA_BIDIRECTIONAL); + c->SG[0].Len, PCI_DMA_BIDIRECTIONAL); cmd_free(h, c, 0); return(return_status); @@ -1495,164 +1807,6 @@ cciss_read_capacity(int ctlr, int logvol, ReadCapdata_struct *buf, return; } -static int register_new_disk(ctlr_info_t *h) -{ - struct gendisk *disk; - int ctlr = h->ctlr; - int i; - int num_luns; - int logvol; - int new_lun_found = 0; - int new_lun_index = 0; - int free_index_found = 0; - int free_index = 0; - ReportLunData_struct *ld_buff = NULL; - ReadCapdata_struct *size_buff = NULL; - InquiryData_struct *inq_buff = NULL; - int return_code; - int listlength = 0; - __u32 lunid = 0; - unsigned int block_size; - unsigned int total_size; - - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - /* if we have no space in our disk array left to add anything */ - if( h->num_luns >= CISS_MAX_LUN) - return -EINVAL; - - ld_buff = kmalloc(sizeof(ReportLunData_struct), GFP_KERNEL); - if (ld_buff == NULL) - goto mem_msg; - memset(ld_buff, 0, sizeof(ReportLunData_struct)); - size_buff = kmalloc(sizeof( ReadCapdata_struct), GFP_KERNEL); - if (size_buff == NULL) - goto mem_msg; - inq_buff = kmalloc(sizeof( InquiryData_struct), GFP_KERNEL); - if (inq_buff == NULL) - goto mem_msg; - - return_code = sendcmd_withirq(CISS_REPORT_LOG, ctlr, ld_buff, - sizeof(ReportLunData_struct), 0, 0, 0, TYPE_CMD); - - if( return_code == IO_OK) - { - - // printk("LUN Data\n--------------------------\n"); - - listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[0])) << 24; - listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[1])) << 16; - listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[2])) << 8; - listlength |= 0xff & (unsigned int)(ld_buff->LUNListLength[3]); - } else /* reading number of logical volumes failed */ - { - printk(KERN_WARNING "cciss: report logical volume" - " command failed\n"); - listlength = 0; - goto free_err; - } - num_luns = listlength / 8; // 8 bytes pre entry - if (num_luns > CISS_MAX_LUN) - { - num_luns = CISS_MAX_LUN; - } -#ifdef CCISS_DEBUG - printk(KERN_DEBUG "Length = %x %x %x %x = %d\n", ld_buff->LUNListLength[0], - ld_buff->LUNListLength[1], ld_buff->LUNListLength[2], - ld_buff->LUNListLength[3], num_luns); -#endif - for(i=0; i< num_luns; i++) - { - int j; - int lunID_found = 0; - - lunid = (0xff & (unsigned int)(ld_buff->LUN[i][3])) << 24; - lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][2])) << 16; - lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][1])) << 8; - lunid |= 0xff & (unsigned int)(ld_buff->LUN[i][0]); - - /* check to see if this is a new lun */ - for(j=0; j <= h->highest_lun; j++) - { -#ifdef CCISS_DEBUG - printk("Checking %d %x against %x\n", j,h->drv[j].LunID, - lunid); -#endif /* CCISS_DEBUG */ - if (h->drv[j].LunID == lunid) - { - lunID_found = 1; - break; - } - - } - if( lunID_found == 1) - continue; - else - { /* It is the new lun we have been looking for */ -#ifdef CCISS_DEBUG - printk("new lun found at %d\n", i); -#endif /* CCISS_DEBUG */ - new_lun_index = i; - new_lun_found = 1; - break; - } - } - if (!new_lun_found) - { - printk(KERN_WARNING "cciss: New Logical Volume not found\n"); - goto free_err; - } - /* Now find the free index */ - for(i=0; i <CISS_MAX_LUN; i++) - { -#ifdef CCISS_DEBUG - printk("Checking Index %d\n", i); -#endif /* CCISS_DEBUG */ - if(h->drv[i].LunID == 0) - { -#ifdef CCISS_DEBUG - printk("free index found at %d\n", i); -#endif /* CCISS_DEBUG */ - free_index_found = 1; - free_index = i; - break; - } - } - if (!free_index_found) - { - printk(KERN_WARNING "cciss: unable to find free slot for disk\n"); - goto free_err; - } - - logvol = free_index; - h->drv[logvol].LunID = lunid; - /* there could be gaps in lun numbers, track hightest */ - if(h->highest_lun < lunid) - h->highest_lun = logvol; - cciss_read_capacity(ctlr, logvol, size_buff, 1, - &total_size, &block_size); - cciss_geometry_inquiry(ctlr, logvol, 1, total_size, block_size, - inq_buff, &h->drv[logvol]); - h->drv[logvol].usage_count = 0; - ++h->num_luns; - /* setup partitions per disk */ - disk = h->gendisk[logvol]; - set_capacity(disk, h->drv[logvol].nr_blocks); - /* if it's the controller it's already added */ - if(logvol) - add_disk(disk); -freeret: - kfree(ld_buff); - kfree(size_buff); - kfree(inq_buff); - return (logvol); -mem_msg: - printk(KERN_ERR "cciss: out of memory\n"); -free_err: - logvol = -1; - goto freeret; -} - static int cciss_revalidate(struct gendisk *disk) { ctlr_info_t *h = get_host(disk); @@ -1859,8 +2013,10 @@ resend_cmd1: cleanup1: /* unlock the data buffer from DMA */ + buff_dma_handle.val32.lower = c->SG[0].Addr.lower; + buff_dma_handle.val32.upper = c->SG[0].Addr.upper; pci_unmap_single(info_p->pdev, (dma_addr_t) buff_dma_handle.val, - size, PCI_DMA_BIDIRECTIONAL); + c->SG[0].Len, PCI_DMA_BIDIRECTIONAL); cmd_free(info_p, c, 1); return (status); } @@ -2111,7 +2267,11 @@ queue: /* fill in the request */ drv = creq->rq_disk->private_data; c->Header.ReplyQueue = 0; // unused in simple mode - c->Header.Tag.lower = c->busaddr; // use the physical address the cmd block for tag + /* got command from pool, so use the command block index instead */ + /* for direct lookups. */ + /* The first 2 bits are reserved for controller error reporting. */ + c->Header.Tag.lower = (c->cmdindex << 3); + c->Header.Tag.lower |= 0x04; /* flag for direct lookup. */ c->Header.LUN.LogDev.VolId= drv->LunID; c->Header.LUN.LogDev.Mode = 1; c->Request.CDBLen = 10; // 12 byte commands not in FW yet; @@ -2186,7 +2346,7 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id, struct pt_regs *regs) ctlr_info_t *h = dev_id; CommandList_struct *c; unsigned long flags; - __u32 a, a1; + __u32 a, a1, a2; int j; int start_queue = h->next_to_run; @@ -2204,10 +2364,21 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id, struct pt_regs *regs) while((a = h->access.command_completed(h)) != FIFO_EMPTY) { a1 = a; + if ((a & 0x04)) { + a2 = (a >> 3); + if (a2 >= NR_CMDS) { + printk(KERN_WARNING "cciss: controller cciss%d failed, stopping.\n", h->ctlr); + fail_all_cmds(h->ctlr); + return IRQ_HANDLED; + } + + c = h->cmd_pool + a2; + a = c->busaddr; + + } else { a &= ~3; - if ((c = h->cmpQ) == NULL) - { - printk(KERN_WARNING "cciss: Completion of %08lx ignored\n", (unsigned long)a1); + if ((c = h->cmpQ) == NULL) { + printk(KERN_WARNING "cciss: Completion of %08x ignored\n", a1); continue; } while(c->busaddr != a) { @@ -2215,6 +2386,7 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id, struct pt_regs *regs) if (c == h->cmpQ) break; } + } /* * If we've found the command, take it off the * completion Q and free it @@ -2634,12 +2806,16 @@ static void cciss_getgeometry(int cntl_num) #endif /* CCISS_DEBUG */ hba[cntl_num]->highest_lun = hba[cntl_num]->num_luns-1; - for(i=0; i< hba[cntl_num]->num_luns; i++) +// for(i=0; i< hba[cntl_num]->num_luns; i++) + for(i=0; i < CISS_MAX_LUN; i++) { - - lunid = (0xff & (unsigned int)(ld_buff->LUN[i][3])) << 24; - lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][2])) << 16; - lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][1])) << 8; + if (i < hba[cntl_num]->num_luns){ + lunid = (0xff & (unsigned int)(ld_buff->LUN[i][3])) + << 24; + lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][2])) + << 16; + lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][1])) + << 8; lunid |= 0xff & (unsigned int)(ld_buff->LUN[i][0]); hba[cntl_num]->drv[i].LunID = lunid; @@ -2647,13 +2823,18 @@ static void cciss_getgeometry(int cntl_num) #ifdef CCISS_DEBUG printk(KERN_DEBUG "LUN[%d]: %x %x %x %x = %x\n", i, - ld_buff->LUN[i][0], ld_buff->LUN[i][1],ld_buff->LUN[i][2], - ld_buff->LUN[i][3], hba[cntl_num]->drv[i].LunID); + ld_buff->LUN[i][0], ld_buff->LUN[i][1], + ld_buff->LUN[i][2], ld_buff->LUN[i][3], + hba[cntl_num]->drv[i].LunID); #endif /* CCISS_DEBUG */ cciss_read_capacity(cntl_num, i, size_buff, 0, &total_size, &block_size); - cciss_geometry_inquiry(cntl_num, i, 0, total_size, block_size, - inq_buff, &hba[cntl_num]->drv[i]); + cciss_geometry_inquiry(cntl_num, i, 0, total_size, + block_size, inq_buff, &hba[cntl_num]->drv[i]); + } else { + /* initialize raid_level to indicate a free space */ + hba[cntl_num]->drv[i].raid_level = -1; + } } kfree(ld_buff); kfree(size_buff); @@ -2727,6 +2908,9 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, i = alloc_cciss_hba(); if(i < 0) return (-1); + + hba[i]->busy_initializing = 1; + if (cciss_pci_init(hba[i], pdev) != 0) goto clean1; @@ -2849,6 +3033,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, add_disk(disk); } + hba[i]->busy_initializing = 0; return(1); clean4: @@ -2869,6 +3054,7 @@ clean2: clean1: release_io_mem(hba[i]); free_hba(i); + hba[i]->busy_initializing = 0; return(-1); } @@ -2913,9 +3099,10 @@ static void __devexit cciss_remove_one (struct pci_dev *pdev) /* remove it from the disk list */ for (j = 0; j < NWD; j++) { struct gendisk *disk = hba[i]->gendisk[j]; - if (disk->flags & GENHD_FL_UP) - blk_cleanup_queue(disk->queue); + if (disk->flags & GENHD_FL_UP) { del_gendisk(disk); + blk_cleanup_queue(disk->queue); + } } pci_free_consistent(hba[i]->pdev, NR_CMDS * sizeof(CommandList_struct), @@ -2964,5 +3151,43 @@ static void __exit cciss_cleanup(void) remove_proc_entry("cciss", proc_root_driver); } +static void fail_all_cmds(unsigned long ctlr) +{ + /* If we get here, the board is apparently dead. */ + ctlr_info_t *h = hba[ctlr]; + CommandList_struct *c; + unsigned long flags; + + printk(KERN_WARNING "cciss%d: controller not responding.\n", h->ctlr); + h->alive = 0; /* the controller apparently died... */ + + spin_lock_irqsave(CCISS_LOCK(ctlr), flags); + + pci_disable_device(h->pdev); /* Make sure it is really dead. */ + + /* move everything off the request queue onto the completed queue */ + while( (c = h->reqQ) != NULL ) { + removeQ(&(h->reqQ), c); + h->Qdepth--; + addQ (&(h->cmpQ), c); + } + + /* Now, fail everything on the completed queue with a HW error */ + while( (c = h->cmpQ) != NULL ) { + removeQ(&h->cmpQ, c); + c->err_info->CommandStatus = CMD_HARDWARE_ERR; + if (c->cmd_type == CMD_RWREQ) { + complete_command(h, c, 0); + } else if (c->cmd_type == CMD_IOCTL_PEND) + complete(c->waiting); +#ifdef CONFIG_CISS_SCSI_TAPE + else if (c->cmd_type == CMD_SCSI) + complete_scsi_command(c, 0, 0); +#endif + } + spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); + return; +} + module_init(cciss_init); module_exit(cciss_cleanup); diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 566587d0a50..ef277baee9f 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -35,7 +35,13 @@ typedef struct _drive_info_struct int heads; int sectors; int cylinders; - int raid_level; + int raid_level; /* set to -1 to indicate that + * the drive is not in use/configured + */ + int busy_configuring; /*This is set when the drive is being removed + *to prevent it from being opened or it's queue + *from being started. + */ } drive_info_struct; struct ctlr_info @@ -83,6 +89,7 @@ struct ctlr_info int nr_allocs; int nr_frees; int busy_configuring; + int busy_initializing; /* This element holds the zero based queue number of the last * queue to be started. It is used for fairness. @@ -94,6 +101,7 @@ struct ctlr_info #ifdef CONFIG_CISS_SCSI_TAPE void *scsi_ctlr; /* ptr to structure containing scsi related stuff */ #endif + unsigned char alive; }; /* Defining the diffent access_menthods */ diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h index a88a8881762..53fea549ba8 100644 --- a/drivers/block/cciss_cmd.h +++ b/drivers/block/cciss_cmd.h @@ -226,6 +226,10 @@ typedef struct _ErrorInfo_struct { #define CMD_MSG_DONE 0x04 #define CMD_MSG_TIMEOUT 0x05 +/* This structure needs to be divisible by 8 for new + * indexing method. + */ +#define PADSIZE (sizeof(long) - 4) typedef struct _CommandList_struct { CommandListHeader_struct Header; RequestBlock_struct Request; @@ -236,14 +240,14 @@ typedef struct _CommandList_struct { ErrorInfo_struct * err_info; /* pointer to the allocated mem */ int ctlr; int cmd_type; + long cmdindex; struct _CommandList_struct *prev; struct _CommandList_struct *next; struct request * rq; struct completion *waiting; int retry_count; -#ifdef CONFIG_CISS_SCSI_TAPE void * scsi_cmd; -#endif + char pad[PADSIZE]; } CommandList_struct; //Configuration Table Structure diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c index f16e3caed58..e183a3ef783 100644 --- a/drivers/block/cciss_scsi.c +++ b/drivers/block/cciss_scsi.c @@ -93,6 +93,7 @@ struct cciss_scsi_cmd_stack_elem_t { CommandList_struct cmd; ErrorInfo_struct Err; __u32 busaddr; + __u32 pad; }; #pragma pack() @@ -877,7 +878,7 @@ cciss_scsi_interpret_error(CommandList_struct *cp) static int cciss_scsi_do_inquiry(ctlr_info_t *c, unsigned char *scsi3addr, - InquiryData_struct *buf) + unsigned char *buf, unsigned char bufsize) { int rc; CommandList_struct *cp; @@ -900,11 +901,10 @@ cciss_scsi_do_inquiry(ctlr_info_t *c, unsigned char *scsi3addr, cdb[1] = 0; cdb[2] = 0; cdb[3] = 0; - cdb[4] = sizeof(*buf) & 0xff; + cdb[4] = bufsize; cdb[5] = 0; rc = cciss_scsi_do_simple_cmd(c, cp, scsi3addr, cdb, - 6, (unsigned char *) buf, - sizeof(*buf), XFER_READ); + 6, buf, bufsize, XFER_READ); if (rc != 0) return rc; /* something went wrong */ @@ -1000,9 +1000,10 @@ cciss_update_non_disk_devices(int cntl_num, int hostno) that though. */ - +#define OBDR_TAPE_INQ_SIZE 49 +#define OBDR_TAPE_SIG "$DR-10" ReportLunData_struct *ld_buff; - InquiryData_struct *inq_buff; + unsigned char *inq_buff; unsigned char scsi3addr[8]; ctlr_info_t *c; __u32 num_luns=0; @@ -1020,7 +1021,7 @@ cciss_update_non_disk_devices(int cntl_num, int hostno) return; } memset(ld_buff, 0, reportlunsize); - inq_buff = kmalloc(sizeof( InquiryData_struct), GFP_KERNEL); + inq_buff = kmalloc(OBDR_TAPE_INQ_SIZE, GFP_KERNEL); if (inq_buff == NULL) { printk(KERN_ERR "cciss: out of memory\n"); kfree(ld_buff); @@ -1051,19 +1052,36 @@ cciss_update_non_disk_devices(int cntl_num, int hostno) /* for each physical lun, do an inquiry */ if (ld_buff->LUN[i][3] & 0xC0) continue; - memset(inq_buff, 0, sizeof(InquiryData_struct)); + memset(inq_buff, 0, OBDR_TAPE_INQ_SIZE); memcpy(&scsi3addr[0], &ld_buff->LUN[i][0], 8); - if (cciss_scsi_do_inquiry(hba[cntl_num], - scsi3addr, inq_buff) != 0) - { + if (cciss_scsi_do_inquiry(hba[cntl_num], scsi3addr, inq_buff, + (unsigned char) OBDR_TAPE_INQ_SIZE) != 0) { /* Inquiry failed (msg printed already) */ devtype = 0; /* so we will skip this device. */ } else /* what kind of device is this? */ - devtype = (inq_buff->data_byte[0] & 0x1f); + devtype = (inq_buff[0] & 0x1f); switch (devtype) { + case 0x05: /* CD-ROM */ { + + /* We don't *really* support actual CD-ROM devices, + * just this "One Button Disaster Recovery" tape drive + * which temporarily pretends to be a CD-ROM drive. + * So we check that the device is really an OBDR tape + * device by checking for "$DR-10" in bytes 43-48 of + * the inquiry data. + */ + char obdr_sig[7]; + + strncpy(obdr_sig, &inq_buff[43], 6); + obdr_sig[6] = '\0'; + if (strncmp(obdr_sig, OBDR_TAPE_SIG, 6) != 0) + /* Not OBDR device, ignore it. */ + break; + } + /* fall through . . . */ case 0x01: /* sequential access, (tape) */ case 0x08: /* medium changer */ if (ncurrent >= CCISS_MAX_SCSI_DEVS_PER_HBA) { @@ -1126,6 +1144,7 @@ cciss_scsi_proc_info(struct Scsi_Host *sh, int buflen, datalen; ctlr_info_t *ci; + int i; int cntl_num; @@ -1136,8 +1155,28 @@ cciss_scsi_proc_info(struct Scsi_Host *sh, cntl_num = ci->ctlr; /* Get our index into the hba[] array */ if (func == 0) { /* User is reading from /proc/scsi/ciss*?/?* */ - buflen = sprintf(buffer, "hostnum=%d\n", sh->host_no); - + buflen = sprintf(buffer, "cciss%d: SCSI host: %d\n", + cntl_num, sh->host_no); + + /* this information is needed by apps to know which cciss + device corresponds to which scsi host number without + having to open a scsi target device node. The device + information is not a duplicate of /proc/scsi/scsi because + the two may be out of sync due to scsi hotplug, rather + this info is for an app to be able to use to know how to + get them back in sync. */ + + for (i=0;i<ccissscsi[cntl_num].ndevices;i++) { + struct cciss_scsi_dev_t *sd = &ccissscsi[cntl_num].dev[i]; + buflen += sprintf(&buffer[buflen], "c%db%dt%dl%d %02d " + "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", + sh->host_no, sd->bus, sd->target, sd->lun, + sd->devtype, + sd->scsi3addr[0], sd->scsi3addr[1], + sd->scsi3addr[2], sd->scsi3addr[3], + sd->scsi3addr[4], sd->scsi3addr[5], + sd->scsi3addr[6], sd->scsi3addr[7]); + } datalen = buflen - offset; if (datalen < 0) { /* they're reading past EOF. */ datalen = 0; @@ -1399,7 +1438,7 @@ cciss_proc_tape_report(int ctlr, unsigned char *buffer, off_t *pos, off_t *len) CPQ_TAPE_LOCK(ctlr, flags); size = sprintf(buffer + *len, - " Sequential access devices: %d\n\n", + "Sequential access devices: %d\n\n", ccissscsi[ctlr].ndevices); CPQ_TAPE_UNLOCK(ctlr, flags); *pos += size; *len += size; diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 7b838342f0a..7e22a58926b 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -5,29 +5,41 @@ * May be copied or modified under the terms of the GNU General Public * License. See linux/COPYING for more information. * - * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and - * DVD-RW devices (aka an exercise in block layer masturbation) + * Packet writing layer for ATAPI and SCSI CD-RW, DVD+RW, DVD-RW and + * DVD-RAM devices. * + * Theory of operation: * - * TODO: (circa order of when I will fix it) - * - Only able to write on CD-RW media right now. - * - check host application code on media and set it in write page - * - interface for UDF <-> packet to negotiate a new location when a write - * fails. - * - handle OPC, especially for -RW media + * At the lowest level, there is the standard driver for the CD/DVD device, + * typically ide-cd.c or sr.c. This driver can handle read and write requests, + * but it doesn't know anything about the special restrictions that apply to + * packet writing. One restriction is that write requests must be aligned to + * packet boundaries on the physical media, and the size of a write request + * must be equal to the packet size. Another restriction is that a + * GPCMD_FLUSH_CACHE command has to be issued to the drive before a read + * command, if the previous command was a write. * - * Theory of operation: + * The purpose of the packet writing driver is to hide these restrictions from + * higher layers, such as file systems, and present a block device that can be + * randomly read and written using 2kB-sized blocks. + * + * The lowest layer in the packet writing driver is the packet I/O scheduler. + * Its data is defined by the struct packet_iosched and includes two bio + * queues with pending read and write requests. These queues are processed + * by the pkt_iosched_process_queue() function. The write requests in this + * queue are already properly aligned and sized. This layer is responsible for + * issuing the flush cache commands and scheduling the I/O in a good order. * - * We use a custom make_request_fn function that forwards reads directly to - * the underlying CD device. Write requests are either attached directly to - * a live packet_data object, or simply stored sequentially in a list for - * later processing by the kcdrwd kernel thread. This driver doesn't use - * any elevator functionally as defined by the elevator_s struct, but the - * underlying CD device uses a standard elevator. + * The next layer transforms unaligned write requests to aligned writes. This + * transformation requires reading missing pieces of data from the underlying + * block device, assembling the pieces to full packets and queuing them to the + * packet I/O scheduler. * - * This strategy makes it possible to do very late merging of IO requests. - * A new bio sent to pkt_make_request can be merged with a live packet_data - * object even if the object is in the data gathering state. + * At the top layer there is a custom make_request_fn function that forwards + * read requests directly to the iosched queue and puts write requests in the + * unaligned write queue. A kernel thread performs the necessary read + * gathering to convert the unaligned writes to aligned writes and then feeds + * them to the packet I/O scheduler. * *************************************************************************/ @@ -100,10 +112,9 @@ static struct bio *pkt_bio_alloc(int nr_iovecs) goto no_bio; bio_init(bio); - bvl = kmalloc(nr_iovecs * sizeof(struct bio_vec), GFP_KERNEL); + bvl = kcalloc(nr_iovecs, sizeof(struct bio_vec), GFP_KERNEL); if (!bvl) goto no_bvl; - memset(bvl, 0, nr_iovecs * sizeof(struct bio_vec)); bio->bi_max_vecs = nr_iovecs; bio->bi_io_vec = bvl; @@ -125,10 +136,9 @@ static struct packet_data *pkt_alloc_packet_data(void) int i; struct packet_data *pkt; - pkt = kmalloc(sizeof(struct packet_data), GFP_KERNEL); + pkt = kzalloc(sizeof(struct packet_data), GFP_KERNEL); if (!pkt) goto no_pkt; - memset(pkt, 0, sizeof(struct packet_data)); pkt->w_bio = pkt_bio_alloc(PACKET_MAX_SIZE); if (!pkt->w_bio) @@ -659,7 +669,6 @@ static void pkt_make_local_copy(struct packet_data *pkt, struct page **pages, in } offs += CD_FRAMESIZE; if (offs >= PAGE_SIZE) { - BUG_ON(offs > PAGE_SIZE); offs = 0; p++; } @@ -724,12 +733,6 @@ static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt) atomic_set(&pkt->io_wait, 0); atomic_set(&pkt->io_errors, 0); - if (pkt->cache_valid) { - VPRINTK("pkt_gather_data: zone %llx cached\n", - (unsigned long long)pkt->sector); - goto out_account; - } - /* * Figure out which frames we need to read before we can write. */ @@ -738,6 +741,7 @@ static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt) for (bio = pkt->orig_bios; bio; bio = bio->bi_next) { int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9); int num_frames = bio->bi_size / CD_FRAMESIZE; + pd->stats.secs_w += num_frames * (CD_FRAMESIZE >> 9); BUG_ON(first_frame < 0); BUG_ON(first_frame + num_frames > pkt->frames); for (f = first_frame; f < first_frame + num_frames; f++) @@ -745,6 +749,12 @@ static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt) } spin_unlock(&pkt->lock); + if (pkt->cache_valid) { + VPRINTK("pkt_gather_data: zone %llx cached\n", + (unsigned long long)pkt->sector); + goto out_account; + } + /* * Schedule reads for missing parts of the packet. */ @@ -778,7 +788,6 @@ out_account: frames_read, (unsigned long long)pkt->sector); pd->stats.pkt_started++; pd->stats.secs_rg += frames_read * (CD_FRAMESIZE >> 9); - pd->stats.secs_w += pd->settings.size; } /* @@ -794,10 +803,11 @@ static struct packet_data *pkt_get_packet_data(struct pktcdvd_device *pd, int zo list_del_init(&pkt->list); if (pkt->sector != zone) pkt->cache_valid = 0; - break; + return pkt; } } - return pkt; + BUG(); + return NULL; } static void pkt_put_packet_data(struct pktcdvd_device *pd, struct packet_data *pkt) @@ -941,12 +951,10 @@ try_next_bio: } pkt = pkt_get_packet_data(pd, zone); - BUG_ON(!pkt); pd->current_sector = zone + pd->settings.size; pkt->sector = zone; pkt->frames = pd->settings.size >> 2; - BUG_ON(pkt->frames > PACKET_MAX_SIZE); pkt->write_size = 0; /* @@ -1636,6 +1644,10 @@ static int pkt_probe_settings(struct pktcdvd_device *pd) printk("pktcdvd: detected zero packet size!\n"); pd->settings.size = 128; } + if (pd->settings.size > PACKET_MAX_SECTORS) { + printk("pktcdvd: packet size is too big\n"); + return -ENXIO; + } pd->settings.fp = ti.fp; pd->offset = (be32_to_cpu(ti.track_start) << 2) & (pd->settings.size - 1); @@ -2198,7 +2210,6 @@ static int pkt_make_request(request_queue_t *q, struct bio *bio) * No matching packet found. Store the bio in the work queue. */ node = mempool_alloc(pd->rb_pool, GFP_NOIO); - BUG_ON(!node); node->bio = bio; spin_lock(&pd->lock); BUG_ON(pd->bio_queue_size < 0); @@ -2406,7 +2417,6 @@ static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data; VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode)); - BUG_ON(!pd); switch (cmd) { /* @@ -2477,10 +2487,9 @@ static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd) return -EBUSY; } - pd = kmalloc(sizeof(struct pktcdvd_device), GFP_KERNEL); + pd = kzalloc(sizeof(struct pktcdvd_device), GFP_KERNEL); if (!pd) return ret; - memset(pd, 0, sizeof(struct pktcdvd_device)); pd->rb_pool = mempool_create(PKT_RB_POOL_SIZE, pkt_rb_alloc, pkt_rb_free, NULL); if (!pd->rb_pool) diff --git a/drivers/block/scsi_ioctl.c b/drivers/block/scsi_ioctl.c index abb2df249fd..856c2278e9d 100644 --- a/drivers/block/scsi_ioctl.c +++ b/drivers/block/scsi_ioctl.c @@ -123,6 +123,7 @@ static int verify_command(struct file *file, unsigned char *cmd) safe_for_read(READ_12), safe_for_read(READ_16), safe_for_read(READ_BUFFER), + safe_for_read(READ_DEFECT_DATA), safe_for_read(READ_LONG), safe_for_read(INQUIRY), safe_for_read(MODE_SENSE), diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c index 67d96b5cbb9..57c48bbf6fe 100644 --- a/drivers/bluetooth/hci_usb.c +++ b/drivers/bluetooth/hci_usb.c @@ -65,13 +65,15 @@ #endif static int ignore = 0; +static int ignore_csr = 0; +static int ignore_sniffer = 0; static int reset = 0; #ifdef CONFIG_BT_HCIUSB_SCO static int isoc = 2; #endif -#define VERSION "2.8" +#define VERSION "2.9" static struct usb_driver hci_usb_driver; @@ -98,6 +100,9 @@ static struct usb_device_id bluetooth_ids[] = { MODULE_DEVICE_TABLE (usb, bluetooth_ids); static struct usb_device_id blacklist_ids[] = { + /* CSR BlueCore devices */ + { USB_DEVICE(0x0a12, 0x0001), .driver_info = HCI_CSR }, + /* Broadcom BCM2033 without firmware */ { USB_DEVICE(0x0a5c, 0x2033), .driver_info = HCI_IGNORE }, @@ -836,6 +841,12 @@ static int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id if (ignore || id->driver_info & HCI_IGNORE) return -ENODEV; + if (ignore_csr && id->driver_info & HCI_CSR) + return -ENODEV; + + if (ignore_sniffer && id->driver_info & HCI_SNIFFER) + return -ENODEV; + if (intf->cur_altsetting->desc.bInterfaceNumber > 0) return -ENODEV; @@ -1061,6 +1072,12 @@ module_exit(hci_usb_exit); module_param(ignore, bool, 0644); MODULE_PARM_DESC(ignore, "Ignore devices from the matching table"); +module_param(ignore_csr, bool, 0644); +MODULE_PARM_DESC(ignore_csr, "Ignore devices with id 0a12:0001"); + +module_param(ignore_sniffer, bool, 0644); +MODULE_PARM_DESC(ignore_sniffer, "Ignore devices with id 0a12:0002"); + module_param(reset, bool, 0644); MODULE_PARM_DESC(reset, "Send HCI reset command on initialization"); diff --git a/drivers/bluetooth/hci_usb.h b/drivers/bluetooth/hci_usb.h index 29936b43d4f..37100a6ea1a 100644 --- a/drivers/bluetooth/hci_usb.h +++ b/drivers/bluetooth/hci_usb.h @@ -31,9 +31,10 @@ #define HCI_IGNORE 0x01 #define HCI_RESET 0x02 #define HCI_DIGIANSWER 0x04 -#define HCI_SNIFFER 0x08 -#define HCI_BROKEN_ISOC 0x10 +#define HCI_CSR 0x08 +#define HCI_SNIFFER 0x10 #define HCI_BCM92035 0x20 +#define HCI_BROKEN_ISOC 0x40 #define HCI_MAX_IFACE_NUM 3 diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index 2a36561eec6..a124f8c5d06 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -2053,10 +2053,6 @@ static int __init rs_init(void) state->icount.rx = state->icount.tx = 0; state->icount.frame = state->icount.parity = 0; state->icount.overrun = state->icount.brk = 0; - /* - if(state->port && check_region(state->port,REGION_LENGTH(state))) - continue; - */ printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n", state->line); diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index c3898afce3a..344001b45af 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -84,6 +84,17 @@ config 977_WATCHDOG Not sure? It's safe to say N. +config IXP2000_WATCHDOG + tristate "IXP2000 Watchdog" + depends on WATCHDOG && ARCH_IXP2000 + help + Say Y here if to include support for the watchdog timer + in the Intel IXP2000(2400, 2800, 2850) network processors. + This driver can be built as a module by choosing M. The module + will be called ixp2000_wdt. + + Say N if you are unsure. + config IXP4XX_WATCHDOG tristate "IXP4xx Watchdog" depends on WATCHDOG && ARCH_IXP4XX @@ -100,17 +111,6 @@ config IXP4XX_WATCHDOG Say N if you are unsure. -config IXP2000_WATCHDOG - tristate "IXP2000 Watchdog" - depends on WATCHDOG && ARCH_IXP2000 - help - Say Y here if to include support for the watchdog timer - in the Intel IXP2000(2400, 2800, 2850) network processors. - This driver can be built as a module by choosing M. The module - will be called ixp2000_wdt. - - Say N if you are unsure. - config S3C2410_WATCHDOG tristate "S3C2410 Watchdog" depends on WATCHDOG && ARCH_S3C2410 @@ -139,6 +139,15 @@ config SA1100_WATCHDOG To compile this driver as a module, choose M here: the module will be called sa1100_wdt. +config MPCORE_WATCHDOG + tristate "MPcore watchdog" + depends on WATCHDOG && ARM_MPCORE_PLATFORM && LOCAL_TIMERS + help + Watchdog timer embedded into the MPcore system. + + To compile this driver as a module, choose M here: the + module will be called mpcore_wdt. + # X86 (i386 + ia64 + x86_64) Architecture config ACQUIRE_WDT @@ -224,6 +233,16 @@ config IB700_WDT Most people will say N. +config IBMASR + tristate "IBM Automatic Server Restart" + depends on WATCHDOG && X86 + help + This is the driver for the IBM Automatic Server Restart watchdog + timer builtin into some eServer xSeries machines. + + To compile this driver as a module, choose M here: the + module will be called ibmasr. + config WAFER_WDT tristate "ICP Wafer 5823 Single Board Computer Watchdog" depends on WATCHDOG && X86 @@ -234,6 +253,16 @@ config WAFER_WDT To compile this driver as a module, choose M here: the module will be called wafer5823wdt. +config I6300ESB_WDT + tristate "Intel 6300ESB Timer/Watchdog" + depends on WATCHDOG && X86 && PCI + ---help--- + Hardware driver for the watchdog timer built into the Intel + 6300ESB controller hub. + + To compile this driver as a module, choose M here: the + module will be called i6300esb. + config I8XX_TCO tristate "Intel i8xx TCO Timer/Watchdog" depends on WATCHDOG && (X86 || IA64) && PCI @@ -289,6 +318,19 @@ config 60XX_WDT You can compile this driver directly into the kernel, or use it as a module. The module will be called sbc60xxwdt. +config SBC8360_WDT + tristate "SBC8360 Watchdog Timer" + depends on WATCHDOG && X86 + ---help--- + + This is the driver for the hardware watchdog on the SBC8360 Single + Board Computer produced by Axiomtek Co., Ltd. (www.axiomtek.com). + + To compile this driver as a module, choose M here: the + module will be called sbc8360.ko. + + Most people will say N. + config CPU5_WDT tristate "SMA CPU5 Watchdog" depends on WATCHDOG && X86 @@ -327,6 +369,19 @@ config W83877F_WDT Most people will say N. +config W83977F_WDT + tristate "W83977F (PCM-5335) Watchdog Timer" + depends on WATCHDOG && X86 + ---help--- + This is the driver for the hardware watchdog on the W83977F I/O chip + as used in AAEON's PCM-5335 SBC (and likely others). This + watchdog simply watches your kernel to make sure it doesn't freeze, + and if it does, it reboots your computer after a certain amount of + time. + + To compile this driver as a module, choose M here: the + module will be called w83977f_wdt. + config MACHZ_WDT tristate "ZF MachZ Watchdog" depends on WATCHDOG && X86 @@ -346,6 +401,10 @@ config 8xx_WDT tristate "MPC8xx Watchdog Timer" depends on WATCHDOG && 8xx +config MV64X60_WDT + tristate "MV64X60 (Marvell Discovery) Watchdog Timer" + depends on WATCHDOG && MV64X60 + config BOOKE_WDT tristate "PowerPC Book-E Watchdog Timer" depends on WATCHDOG && (BOOKE || 4xx) @@ -353,6 +412,17 @@ config BOOKE_WDT Please see Documentation/watchdog/watchdog-api.txt for more information. +# PPC64 Architecture + +config WATCHDOG_RTAS + tristate "RTAS watchdog" + depends on WATCHDOG && PPC_RTAS + help + This driver adds watchdog support for the RTAS watchdog. + + To compile this driver as a module, choose M here. The module + will be called wdrtas. + # MIPS Architecture config INDYDOG @@ -421,16 +491,6 @@ config WATCHDOG_RIO machines. The watchdog timeout period is normally one minute but can be changed with a boot-time parameter. -# ppc64 RTAS watchdog -config WATCHDOG_RTAS - tristate "RTAS watchdog" - depends on WATCHDOG && PPC_RTAS - help - This driver adds watchdog support for the RTAS watchdog. - - To compile this driver as a module, choose M here. The module - will be called wdrtas. - # # ISA-based Watchdog Cards # diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index cfeac6f1013..cfd0a398771 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o +obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o # X86 (i386 + ia64 + x86_64) Architecture obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o @@ -38,22 +39,27 @@ obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o obj-$(CONFIG_SC520_WDT) += sc520_wdt.o obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o obj-$(CONFIG_IB700_WDT) += ib700wdt.o +obj-$(CONFIG_IBMASR) += ibmasr.o obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o +obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o obj-$(CONFIG_I8XX_TCO) += i8xx_tco.o obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o +obj-$(CONFIG_SBC8360_WDT) += sbc8360.o obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o +obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o obj-$(CONFIG_MACHZ_WDT) += machzwd.o # PowerPC Architecture obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o +obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o +obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o # PPC64 Architecture obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o -obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o # MIPS Architecture obj-$(CONFIG_INDYDOG) += indydog.o diff --git a/drivers/char/watchdog/i6300esb.c b/drivers/char/watchdog/i6300esb.c new file mode 100644 index 00000000000..93785f13242 --- /dev/null +++ b/drivers/char/watchdog/i6300esb.c @@ -0,0 +1,527 @@ +/* + * i6300esb: Watchdog timer driver for Intel 6300ESB chipset + * + * (c) Copyright 2004 Google Inc. + * (c) Copyright 2005 David Härdeman <david@2gen.com> + * + * 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. + * + * based on i810-tco.c which is in turn based on softdog.c + * + * The timer is implemented in the following I/O controller hubs: + * (See the intel documentation on http://developer.intel.com.) + * 6300ESB chip : document number 300641-003 + * + * 2004YYZZ Ross Biro + * Initial version 0.01 + * 2004YYZZ Ross Biro + * Version 0.02 + * 20050210 David Härdeman <david@2gen.com> + * Ported driver to kernel 2.6 + */ + +/* + * Includes, defines, variables, module parameters, ... + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/ioport.h> + +#include <asm/uaccess.h> +#include <asm/io.h> + +/* Module and version information */ +#define ESB_VERSION "0.03" +#define ESB_MODULE_NAME "i6300ESB timer" +#define ESB_DRIVER_NAME ESB_MODULE_NAME ", v" ESB_VERSION +#define PFX ESB_MODULE_NAME ": " + +/* PCI configuration registers */ +#define ESB_CONFIG_REG 0x60 /* Config register */ +#define ESB_LOCK_REG 0x68 /* WDT lock register */ + +/* Memory mapped registers */ +#define ESB_TIMER1_REG BASEADDR + 0x00 /* Timer1 value after each reset */ +#define ESB_TIMER2_REG BASEADDR + 0x04 /* Timer2 value after each reset */ +#define ESB_GINTSR_REG BASEADDR + 0x08 /* General Interrupt Status Register */ +#define ESB_RELOAD_REG BASEADDR + 0x0c /* Reload register */ + +/* Lock register bits */ +#define ESB_WDT_FUNC ( 0x01 << 2 ) /* Watchdog functionality */ +#define ESB_WDT_ENABLE ( 0x01 << 1 ) /* Enable WDT */ +#define ESB_WDT_LOCK ( 0x01 << 0 ) /* Lock (nowayout) */ + +/* Config register bits */ +#define ESB_WDT_REBOOT ( 0x01 << 5 ) /* Enable reboot on timeout */ +#define ESB_WDT_FREQ ( 0x01 << 2 ) /* Decrement frequency */ +#define ESB_WDT_INTTYPE ( 0x11 << 0 ) /* Interrupt type on timer1 timeout */ + +/* Reload register bits */ +#define ESB_WDT_RELOAD ( 0x01 << 8 ) /* prevent timeout */ + +/* Magic constants */ +#define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */ +#define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */ + +/* internal variables */ +static void __iomem *BASEADDR; +static spinlock_t esb_lock; /* Guards the hardware */ +static unsigned long timer_alive; +static struct pci_dev *esb_pci; +static unsigned short triggered; /* The status of the watchdog upon boot */ +static char esb_expect_close; + +/* module parameters */ +#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat (1<heartbeat<2*1023) */ +static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<heartbeat<2046, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); + +/* + * Some i6300ESB specific functions + */ + +/* + * Prepare for reloading the timer by unlocking the proper registers. + * This is performed by first writing 0x80 followed by 0x86 to the + * reload register. After this the appropriate registers can be written + * to once before they need to be unlocked again. + */ +static inline void esb_unlock_registers(void) { + writeb(ESB_UNLOCK1, ESB_RELOAD_REG); + writeb(ESB_UNLOCK2, ESB_RELOAD_REG); +} + +static void esb_timer_start(void) +{ + u8 val; + + /* Enable or Enable + Lock? */ + val = 0x02 | (nowayout ? 0x01 : 0x00); + + pci_write_config_byte(esb_pci, ESB_LOCK_REG, val); +} + +static int esb_timer_stop(void) +{ + u8 val; + + spin_lock(&esb_lock); + /* First, reset timers as suggested by the docs */ + esb_unlock_registers(); + writew(ESB_WDT_RELOAD, ESB_RELOAD_REG); + /* Then disable the WDT */ + pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x0); + pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val); + spin_unlock(&esb_lock); + + /* Returns 0 if the timer was disabled, non-zero otherwise */ + return (val & 0x01); +} + +static void esb_timer_keepalive(void) +{ + spin_lock(&esb_lock); + esb_unlock_registers(); + writew(ESB_WDT_RELOAD, ESB_RELOAD_REG); + /* FIXME: Do we need to flush anything here? */ + spin_unlock(&esb_lock); +} + +static int esb_timer_set_heartbeat(int time) +{ + u32 val; + + if (time < 0x1 || time > (2 * 0x03ff)) + return -EINVAL; + + spin_lock(&esb_lock); + + /* We shift by 9, so if we are passed a value of 1 sec, + * val will be 1 << 9 = 512, then write that to two + * timers => 2 * 512 = 1024 (which is decremented at 1KHz) + */ + val = time << 9; + + /* Write timer 1 */ + esb_unlock_registers(); + writel(val, ESB_TIMER1_REG); + + /* Write timer 2 */ + esb_unlock_registers(); + writel(val, ESB_TIMER2_REG); + + /* Reload */ + esb_unlock_registers(); + writew(ESB_WDT_RELOAD, ESB_RELOAD_REG); + + /* FIXME: Do we need to flush everything out? */ + + /* Done */ + heartbeat = time; + spin_unlock(&esb_lock); + return 0; +} + +static int esb_timer_read (void) +{ + u32 count; + + /* This isn't documented, and doesn't take into + * acount which stage is running, but it looks + * like a 20 bit count down, so we might as well report it. + */ + pci_read_config_dword(esb_pci, 0x64, &count); + return (int)count; +} + +/* + * /dev/watchdog handling + */ + +static int esb_open (struct inode *inode, struct file *file) +{ + /* /dev/watchdog can only be opened once */ + if (test_and_set_bit(0, &timer_alive)) + return -EBUSY; + + /* Reload and activate timer */ + esb_timer_keepalive (); + esb_timer_start (); + + return nonseekable_open(inode, file); +} + +static int esb_release (struct inode *inode, struct file *file) +{ + /* Shut off the timer. */ + if (esb_expect_close == 42) { + esb_timer_stop (); + } else { + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + esb_timer_keepalive (); + } + clear_bit(0, &timer_alive); + esb_expect_close = 0; + return 0; +} + +static ssize_t esb_write (struct file *file, const char __user *data, + size_t len, loff_t * ppos) +{ + /* See if we got the magic character 'V' and reload the timer */ + if (len) { + if (!nowayout) { + size_t i; + + /* note: just in case someone wrote the magic character + * five months ago... */ + esb_expect_close = 0; + + /* scan to see whether or not we got the magic character */ + for (i = 0; i != len; i++) { + char c; + if(get_user(c, data+i)) + return -EFAULT; + if (c == 'V') + esb_expect_close = 42; + } + } + + /* someone wrote to us, we should reload the timer */ + esb_timer_keepalive (); + } + return len; +} + +static int esb_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int new_options, retval = -EINVAL; + int new_heartbeat; + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = ESB_MODULE_NAME, + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &ident, + sizeof (ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + return put_user (esb_timer_read(), p); + + case WDIOC_GETBOOTSTATUS: + return put_user (triggered, p); + + case WDIOC_KEEPALIVE: + esb_timer_keepalive (); + return 0; + + case WDIOC_SETOPTIONS: + { + if (get_user (new_options, p)) + return -EFAULT; + + if (new_options & WDIOS_DISABLECARD) { + esb_timer_stop (); + retval = 0; + } + + if (new_options & WDIOS_ENABLECARD) { + esb_timer_keepalive (); + esb_timer_start (); + retval = 0; + } + + return retval; + } + + case WDIOC_SETTIMEOUT: + { + if (get_user(new_heartbeat, p)) + return -EFAULT; + + if (esb_timer_set_heartbeat(new_heartbeat)) + return -EINVAL; + + esb_timer_keepalive (); + /* Fall */ + } + + case WDIOC_GETTIMEOUT: + return put_user(heartbeat, p); + + default: + return -ENOIOCTLCMD; + } +} + +/* + * Notify system + */ + +static int esb_notify_sys (struct notifier_block *this, unsigned long code, void *unused) +{ + if (code==SYS_DOWN || code==SYS_HALT) { + /* Turn the WDT off */ + esb_timer_stop (); + } + + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + +static struct file_operations esb_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = esb_write, + .ioctl = esb_ioctl, + .open = esb_open, + .release = esb_release, +}; + +static struct miscdevice esb_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &esb_fops, +}; + +static struct notifier_block esb_notifier = { + .notifier_call = esb_notify_sys, +}; + +/* + * Data for PCI driver interface + * + * This data only exists for exporting the supported + * PCI ids via MODULE_DEVICE_TABLE. We do not actually + * register a pci_driver, because someone else might one day + * want to register another driver on the same PCI id. + */ +static struct pci_device_id esb_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_9), }, + { 0, }, /* End of list */ +}; +MODULE_DEVICE_TABLE (pci, esb_pci_tbl); + +/* + * Init & exit routines + */ + +static unsigned char __init esb_getdevice (void) +{ + u8 val1; + unsigned short val2; + + struct pci_dev *dev = NULL; + /* + * Find the PCI device + */ + + for_each_pci_dev(dev) { + if (pci_match_id(esb_pci_tbl, dev)) { + esb_pci = dev; + break; + } + } + + if (esb_pci) { + if (pci_enable_device(esb_pci)) { + printk (KERN_ERR PFX "failed to enable device\n"); + goto err_devput; + } + + if (pci_request_region(esb_pci, 0, ESB_MODULE_NAME)) { + printk (KERN_ERR PFX "failed to request region\n"); + goto err_disable; + } + + BASEADDR = ioremap(pci_resource_start(esb_pci, 0), + pci_resource_len(esb_pci, 0)); + if (BASEADDR == NULL) { + /* Something's wrong here, BASEADDR has to be set */ + printk (KERN_ERR PFX "failed to get BASEADDR\n"); + goto err_release; + } + + /* + * The watchdog has two timers, it can be setup so that the + * expiry of timer1 results in an interrupt and the expiry of + * timer2 results in a reboot. We set it to not generate + * any interrupts as there is not much we can do with it + * right now. + * + * We also enable reboots and set the timer frequency to + * the PCI clock divided by 2^15 (approx 1KHz). + */ + pci_write_config_word(esb_pci, ESB_CONFIG_REG, 0x0003); + + /* Check that the WDT isn't already locked */ + pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1); + if (val1 & ESB_WDT_LOCK) + printk (KERN_WARNING PFX "nowayout already set\n"); + + /* Set the timer to watchdog mode and disable it for now */ + pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00); + + /* Check if the watchdog was previously triggered */ + esb_unlock_registers(); + val2 = readw(ESB_RELOAD_REG); + triggered = (val2 & (0x01 << 9) >> 9); + + /* Reset trigger flag and timers */ + esb_unlock_registers(); + writew((0x11 << 8), ESB_RELOAD_REG); + + /* Done */ + return 1; + +err_release: + pci_release_region(esb_pci, 0); +err_disable: + pci_disable_device(esb_pci); +err_devput: + pci_dev_put(esb_pci); + } + return 0; +} + +static int __init watchdog_init (void) +{ + int ret; + + spin_lock_init(&esb_lock); + + /* Check whether or not the hardware watchdog is there */ + if (!esb_getdevice () || esb_pci == NULL) + return -ENODEV; + + /* Check that the heartbeat value is within it's range ; if not reset to the default */ + if (esb_timer_set_heartbeat (heartbeat)) { + esb_timer_set_heartbeat (WATCHDOG_HEARTBEAT); + printk(KERN_INFO PFX "heartbeat value must be 1<heartbeat<2046, using %d\n", + heartbeat); + } + + ret = register_reboot_notifier(&esb_notifier); + if (ret != 0) { + printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", + ret); + goto err_unmap; + } + + ret = misc_register(&esb_miscdev); + if (ret != 0) { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + goto err_notifier; + } + + esb_timer_stop (); + + printk (KERN_INFO PFX "initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", + BASEADDR, heartbeat, nowayout); + + return 0; + +err_notifier: + unregister_reboot_notifier(&esb_notifier); +err_unmap: + iounmap(BASEADDR); +/* err_release: */ + pci_release_region(esb_pci, 0); +/* err_disable: */ + pci_disable_device(esb_pci); +/* err_devput: */ + pci_dev_put(esb_pci); + return ret; +} + +static void __exit watchdog_cleanup (void) +{ + /* Stop the timer before we leave */ + if (!nowayout) + esb_timer_stop (); + + /* Deregister */ + misc_deregister(&esb_miscdev); + unregister_reboot_notifier(&esb_notifier); + iounmap(BASEADDR); + pci_release_region(esb_pci, 0); + pci_disable_device(esb_pci); + pci_dev_put(esb_pci); +} + +module_init(watchdog_init); +module_exit(watchdog_cleanup); + +MODULE_AUTHOR("Ross Biro and David Härdeman"); +MODULE_DESCRIPTION("Watchdog driver for Intel 6300ESB chipsets"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/ibmasr.c b/drivers/char/watchdog/ibmasr.c new file mode 100644 index 00000000000..294c474ae48 --- /dev/null +++ b/drivers/char/watchdog/ibmasr.c @@ -0,0 +1,405 @@ +/* + * IBM Automatic Server Restart driver. + * + * Copyright (c) 2005 Andrey Panin <pazke@donpac.ru> + * + * Based on driver written by Pete Reynolds. + * Copyright (c) IBM Corporation, 1998-2004. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + */ + +#include <linux/config.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/timer.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/dmi.h> + +#include <asm/io.h> +#include <asm/uaccess.h> + + +enum { + ASMTYPE_UNKNOWN, + ASMTYPE_TOPAZ, + ASMTYPE_JASPER, + ASMTYPE_PEARL, + ASMTYPE_JUNIPER, + ASMTYPE_SPRUCE, +}; + +#define PFX "ibmasr: " + +#define TOPAZ_ASR_REG_OFFSET 4 +#define TOPAZ_ASR_TOGGLE 0x40 +#define TOPAZ_ASR_DISABLE 0x80 + +/* PEARL ASR S/W REGISTER SUPERIO PORT ADDRESSES */ +#define PEARL_BASE 0xe04 +#define PEARL_WRITE 0xe06 +#define PEARL_READ 0xe07 + +#define PEARL_ASR_DISABLE_MASK 0x80 /* bit 7: disable = 1, enable = 0 */ +#define PEARL_ASR_TOGGLE_MASK 0x40 /* bit 6: 0, then 1, then 0 */ + +/* JASPER OFFSET FROM SIO BASE ADDR TO ASR S/W REGISTERS. */ +#define JASPER_ASR_REG_OFFSET 0x38 + +#define JASPER_ASR_DISABLE_MASK 0x01 /* bit 0: disable = 1, enable = 0 */ +#define JASPER_ASR_TOGGLE_MASK 0x02 /* bit 1: 0, then 1, then 0 */ + +#define JUNIPER_BASE_ADDRESS 0x54b /* Base address of Juniper ASR */ +#define JUNIPER_ASR_DISABLE_MASK 0x01 /* bit 0: disable = 1 enable = 0 */ +#define JUNIPER_ASR_TOGGLE_MASK 0x02 /* bit 1: 0, then 1, then 0 */ + +#define SPRUCE_BASE_ADDRESS 0x118e /* Base address of Spruce ASR */ +#define SPRUCE_ASR_DISABLE_MASK 0x01 /* bit 1: disable = 1 enable = 0 */ +#define SPRUCE_ASR_TOGGLE_MASK 0x02 /* bit 0: 0, then 1, then 0 */ + + +static int nowayout = WATCHDOG_NOWAYOUT; + +static unsigned long asr_is_open; +static char asr_expect_close; + +static unsigned int asr_type, asr_base, asr_length; +static unsigned int asr_read_addr, asr_write_addr; +static unsigned char asr_toggle_mask, asr_disable_mask; + +static void asr_toggle(void) +{ + unsigned char reg = inb(asr_read_addr); + + outb(reg & ~asr_toggle_mask, asr_write_addr); + reg = inb(asr_read_addr); + + outb(reg | asr_toggle_mask, asr_write_addr); + reg = inb(asr_read_addr); + + outb(reg & ~asr_toggle_mask, asr_write_addr); + reg = inb(asr_read_addr); +} + +static void asr_enable(void) +{ + unsigned char reg; + + if (asr_type == ASMTYPE_TOPAZ) { + /* asr_write_addr == asr_read_addr */ + reg = inb(asr_read_addr); + outb(reg & ~(TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE), + asr_read_addr); + } else { + /* + * First make sure the hardware timer is reset by toggling + * ASR hardware timer line. + */ + asr_toggle(); + + reg = inb(asr_read_addr); + outb(reg & ~asr_disable_mask, asr_write_addr); + } + reg = inb(asr_read_addr); +} + +static void asr_disable(void) +{ + unsigned char reg = inb(asr_read_addr); + + if (asr_type == ASMTYPE_TOPAZ) + /* asr_write_addr == asr_read_addr */ + outb(reg | TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE, + asr_read_addr); + else { + outb(reg | asr_toggle_mask, asr_write_addr); + reg = inb(asr_read_addr); + + outb(reg | asr_disable_mask, asr_write_addr); + } + reg = inb(asr_read_addr); +} + +static int __init asr_get_base_address(void) +{ + unsigned char low, high; + const char *type = ""; + + asr_length = 1; + + switch (asr_type) { + case ASMTYPE_TOPAZ: + /* SELECT SuperIO CHIP FOR QUERYING (WRITE 0x07 TO BOTH 0x2E and 0x2F) */ + outb(0x07, 0x2e); + outb(0x07, 0x2f); + + /* SELECT AND READ THE HIGH-NIBBLE OF THE GPIO BASE ADDRESS */ + outb(0x60, 0x2e); + high = inb(0x2f); + + /* SELECT AND READ THE LOW-NIBBLE OF THE GPIO BASE ADDRESS */ + outb(0x61, 0x2e); + low = inb(0x2f); + + asr_base = (high << 16) | low; + asr_read_addr = asr_write_addr = + asr_base + TOPAZ_ASR_REG_OFFSET; + asr_length = 5; + + break; + + case ASMTYPE_JASPER: + type = "Jaspers "; + + /* FIXME: need to use pci_config_lock here, but it's not exported */ + +/* spin_lock_irqsave(&pci_config_lock, flags);*/ + + /* Select the SuperIO chip in the PCI I/O port register */ + outl(0x8000f858, 0xcf8); + + /* + * Read the base address for the SuperIO chip. + * Only the lower 16 bits are valid, but the address is word + * aligned so the last bit must be masked off. + */ + asr_base = inl(0xcfc) & 0xfffe; + +/* spin_unlock_irqrestore(&pci_config_lock, flags);*/ + + asr_read_addr = asr_write_addr = + asr_base + JASPER_ASR_REG_OFFSET; + asr_toggle_mask = JASPER_ASR_TOGGLE_MASK; + asr_disable_mask = JASPER_ASR_DISABLE_MASK; + asr_length = JASPER_ASR_REG_OFFSET + 1; + + break; + + case ASMTYPE_PEARL: + type = "Pearls "; + asr_base = PEARL_BASE; + asr_read_addr = PEARL_READ; + asr_write_addr = PEARL_WRITE; + asr_toggle_mask = PEARL_ASR_TOGGLE_MASK; + asr_disable_mask = PEARL_ASR_DISABLE_MASK; + asr_length = 4; + break; + + case ASMTYPE_JUNIPER: + type = "Junipers "; + asr_base = JUNIPER_BASE_ADDRESS; + asr_read_addr = asr_write_addr = asr_base; + asr_toggle_mask = JUNIPER_ASR_TOGGLE_MASK; + asr_disable_mask = JUNIPER_ASR_DISABLE_MASK; + break; + + case ASMTYPE_SPRUCE: + type = "Spruce's "; + asr_base = SPRUCE_BASE_ADDRESS; + asr_read_addr = asr_write_addr = asr_base; + asr_toggle_mask = SPRUCE_ASR_TOGGLE_MASK; + asr_disable_mask = SPRUCE_ASR_DISABLE_MASK; + break; + } + + if (!request_region(asr_base, asr_length, "ibmasr")) { + printk(KERN_ERR PFX "address %#x already in use\n", + asr_base); + return -EBUSY; + } + + printk(KERN_INFO PFX "found %sASR @ addr %#x\n", type, asr_base); + + return 0; +} + + +static ssize_t asr_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + if (count) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + asr_expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + asr_expect_close = 42; + } + } + asr_toggle(); + } + return count; +} + +static int asr_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + static const struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .identity = "IBM ASR" + }; + void __user *argp = (void __user *)arg; + int __user *p = argp; + int heartbeat; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &ident, sizeof(ident)) ? + -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + asr_toggle(); + return 0; + + /* + * The hardware has a fixed timeout value, so no WDIOC_SETTIMEOUT + * and WDIOC_GETTIMEOUT always returns 256. + */ + case WDIOC_GETTIMEOUT: + heartbeat = 256; + return put_user(heartbeat, p); + + case WDIOC_SETOPTIONS: { + int new_options, retval = -EINVAL; + + if (get_user(new_options, p)) + return -EFAULT; + + if (new_options & WDIOS_DISABLECARD) { + asr_disable(); + retval = 0; + } + + if (new_options & WDIOS_ENABLECARD) { + asr_enable(); + asr_toggle(); + retval = 0; + } + + return retval; + } + } + + return -ENOIOCTLCMD; +} + +static int asr_open(struct inode *inode, struct file *file) +{ + if(test_and_set_bit(0, &asr_is_open)) + return -EBUSY; + + asr_toggle(); + asr_enable(); + + return nonseekable_open(inode, file); +} + +static int asr_release(struct inode *inode, struct file *file) +{ + if (asr_expect_close == 42) + asr_disable(); + else { + printk(KERN_CRIT PFX "unexpected close, not stopping watchdog!\n"); + asr_toggle(); + } + clear_bit(0, &asr_is_open); + asr_expect_close = 0; + return 0; +} + +static struct file_operations asr_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = asr_write, + .ioctl = asr_ioctl, + .open = asr_open, + .release = asr_release, +}; + +static struct miscdevice asr_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &asr_fops, +}; + + +struct ibmasr_id { + const char *desc; + int type; +}; + +static struct ibmasr_id __initdata ibmasr_id_table[] = { + { "IBM Automatic Server Restart - eserver xSeries 220", ASMTYPE_TOPAZ }, + { "IBM Automatic Server Restart - Machine Type 8673", ASMTYPE_PEARL }, + { "IBM Automatic Server Restart - Machine Type 8480", ASMTYPE_JASPER }, + { "IBM Automatic Server Restart - Machine Type 8482", ASMTYPE_JUNIPER }, + { "IBM Automatic Server Restart - Machine Type 8648", ASMTYPE_SPRUCE }, + { NULL } +}; + +static int __init ibmasr_init(void) +{ + struct ibmasr_id *id; + int rc; + + for (id = ibmasr_id_table; id->desc; id++) { + if (dmi_find_device(DMI_DEV_TYPE_OTHER, id->desc, NULL)) { + asr_type = id->type; + break; + } + } + + if (!asr_type) + return -ENODEV; + + rc = misc_register(&asr_miscdev); + if (rc < 0) { + printk(KERN_ERR PFX "failed to register misc device\n"); + return rc; + } + + rc = asr_get_base_address(); + if (rc) { + misc_deregister(&asr_miscdev); + return rc; + } + + return 0; +} + +static void __exit ibmasr_exit(void) +{ + if (!nowayout) + asr_disable(); + + misc_deregister(&asr_miscdev); + + release_region(asr_base, asr_length); +} + +module_init(ibmasr_init); +module_exit(ibmasr_exit); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); + +MODULE_DESCRIPTION("IBM Automatic Server Restart driver"); +MODULE_AUTHOR("Andrey Panin"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/mpcore_wdt.c b/drivers/char/watchdog/mpcore_wdt.c new file mode 100644 index 00000000000..c694eee1fb2 --- /dev/null +++ b/drivers/char/watchdog/mpcore_wdt.c @@ -0,0 +1,434 @@ +/* + * Watchdog driver for the mpcore watchdog timer + * + * (c) Copyright 2004 ARM Limited + * + * Based on the SoftDog driver: + * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> + * + */ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/fs.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <asm/uaccess.h> + +struct mpcore_wdt { + unsigned long timer_alive; + struct device *dev; + void __iomem *base; + int irq; + unsigned int perturb; + char expect_close; +}; + +static struct platform_device *mpcore_wdt_dev; + +extern unsigned int mpcore_timer_rate; + +#define TIMER_MARGIN 60 +static int mpcore_margin = TIMER_MARGIN; +module_param(mpcore_margin, int, 0); +MODULE_PARM_DESC(mpcore_margin, "MPcore timer margin in seconds. (0<mpcore_margin<65536, default=" __MODULE_STRING(TIMER_MARGIN) ")"); + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +#define ONLY_TESTING 0 +static int mpcore_noboot = ONLY_TESTING; +module_param(mpcore_noboot, int, 0); +MODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, set to 1 to ignore reboots, 0 to reboot (default=" __MODULE_STRING(ONLY_TESTING) ")"); + +/* + * This is the interrupt handler. Note that we only use this + * in testing mode, so don't actually do a reboot here. + */ +static irqreturn_t mpcore_wdt_fire(int irq, void *arg, struct pt_regs *regs) +{ + struct mpcore_wdt *wdt = arg; + + /* Check it really was our interrupt */ + if (readl(wdt->base + TWD_WDOG_INTSTAT)) { + dev_printk(KERN_CRIT, wdt->dev, "Triggered - Reboot ignored.\n"); + + /* Clear the interrupt on the watchdog */ + writel(1, wdt->base + TWD_WDOG_INTSTAT); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/* + * mpcore_wdt_keepalive - reload the timer + * + * Note that the spec says a DIFFERENT value must be written to the reload + * register each time. The "perturb" variable deals with this by adding 1 + * to the count every other time the function is called. + */ +static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt) +{ + unsigned int count; + + /* Assume prescale is set to 256 */ + count = (mpcore_timer_rate / 256) * mpcore_margin; + + /* Reload the counter */ + writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD); + + wdt->perturb = wdt->perturb ? 0 : 1; +} + +static void mpcore_wdt_stop(struct mpcore_wdt *wdt) +{ + writel(0x12345678, wdt->base + TWD_WDOG_DISABLE); + writel(0x87654321, wdt->base + TWD_WDOG_DISABLE); + writel(0x0, wdt->base + TWD_WDOG_CONTROL); +} + +static void mpcore_wdt_start(struct mpcore_wdt *wdt) +{ + dev_printk(KERN_INFO, wdt->dev, "enabling watchdog.\n"); + + /* This loads the count register but does NOT start the count yet */ + mpcore_wdt_keepalive(wdt); + + if (mpcore_noboot) { + /* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */ + writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL); + } else { + /* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */ + writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL); + } +} + +static int mpcore_wdt_set_heartbeat(int t) +{ + if (t < 0x0001 || t > 0xFFFF) + return -EINVAL; + + mpcore_margin = t; + return 0; +} + +/* + * /dev/watchdog handling + */ +static int mpcore_wdt_open(struct inode *inode, struct file *file) +{ + struct mpcore_wdt *wdt = dev_get_drvdata(&mpcore_wdt_dev->dev); + + if (test_and_set_bit(0, &wdt->timer_alive)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + file->private_data = wdt; + + /* + * Activate timer + */ + mpcore_wdt_start(wdt); + + return nonseekable_open(inode, file); +} + +static int mpcore_wdt_release(struct inode *inode, struct file *file) +{ + struct mpcore_wdt *wdt = file->private_data; + + /* + * Shut off the timer. + * Lock it in if it's a module and we set nowayout + */ + if (wdt->expect_close == 42) { + mpcore_wdt_stop(wdt); + } else { + dev_printk(KERN_CRIT, wdt->dev, "unexpected close, not stopping watchdog!\n"); + mpcore_wdt_keepalive(wdt); + } + clear_bit(0, &wdt->timer_alive); + wdt->expect_close = 0; + return 0; +} + +static ssize_t mpcore_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + struct mpcore_wdt *wdt = file->private_data; + + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + /* + * Refresh the timer. + */ + if (len) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + wdt->expect_close = 0; + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + wdt->expect_close = 42; + } + } + mpcore_wdt_keepalive(wdt); + } + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .identity = "MPcore Watchdog", +}; + +static int mpcore_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct mpcore_wdt *wdt = file->private_data; + int ret; + union { + struct watchdog_info ident; + int i; + } uarg; + + if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg)) + return -ENOIOCTLCMD; + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd)); + if (ret) + return -EFAULT; + } + + switch (cmd) { + case WDIOC_GETSUPPORT: + uarg.ident = ident; + ret = 0; + break; + + case WDIOC_SETOPTIONS: + ret = -EINVAL; + if (uarg.i & WDIOS_DISABLECARD) { + mpcore_wdt_stop(wdt); + ret = 0; + } + if (uarg.i & WDIOS_ENABLECARD) { + mpcore_wdt_start(wdt); + ret = 0; + } + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + uarg.i = 0; + ret = 0; + break; + + case WDIOC_KEEPALIVE: + mpcore_wdt_keepalive(wdt); + ret = 0; + break; + + case WDIOC_SETTIMEOUT: + ret = mpcore_wdt_set_heartbeat(uarg.i); + if (ret) + break; + + mpcore_wdt_keepalive(wdt); + /* Fall */ + case WDIOC_GETTIMEOUT: + uarg.i = mpcore_margin; + ret = 0; + break; + + default: + return -ENOIOCTLCMD; + } + + if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { + ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd)); + if (ret) + ret = -EFAULT; + } + return ret; +} + +/* + * System shutdown handler. Turn off the watchdog if we're + * restarting or halting the system. + */ +static void mpcore_wdt_shutdown(struct device *_dev) +{ + struct mpcore_wdt *wdt = dev_get_drvdata(_dev); + + if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT) + mpcore_wdt_stop(wdt); +} + +/* + * Kernel Interfaces + */ +static struct file_operations mpcore_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = mpcore_wdt_write, + .ioctl = mpcore_wdt_ioctl, + .open = mpcore_wdt_open, + .release = mpcore_wdt_release, +}; + +static struct miscdevice mpcore_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &mpcore_wdt_fops, +}; + +static int __devinit mpcore_wdt_probe(struct device *_dev) +{ + struct platform_device *dev = to_platform_device(_dev); + struct mpcore_wdt *wdt; + struct resource *res; + int ret; + + /* We only accept one device, and it must have an id of -1 */ + if (dev->id != -1) + return -ENODEV; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto err_out; + } + + wdt = kmalloc(sizeof(struct mpcore_wdt), GFP_KERNEL); + if (!wdt) { + ret = -ENOMEM; + goto err_out; + } + memset(wdt, 0, sizeof(struct mpcore_wdt)); + + wdt->dev = &dev->dev; + wdt->irq = platform_get_irq(dev, 0); + wdt->base = ioremap(res->start, res->end - res->start + 1); + if (!wdt->base) { + ret = -ENOMEM; + goto err_free; + } + + mpcore_wdt_miscdev.dev = &dev->dev; + ret = misc_register(&mpcore_wdt_miscdev); + if (ret) { + dev_printk(KERN_ERR, _dev, "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + goto err_misc; + } + + ret = request_irq(wdt->irq, mpcore_wdt_fire, SA_INTERRUPT, "mpcore_wdt", wdt); + if (ret) { + dev_printk(KERN_ERR, _dev, "cannot register IRQ%d for watchdog\n", wdt->irq); + goto err_irq; + } + + mpcore_wdt_stop(wdt); + dev_set_drvdata(&dev->dev, wdt); + mpcore_wdt_dev = dev; + + return 0; + + err_irq: + misc_deregister(&mpcore_wdt_miscdev); + err_misc: + iounmap(wdt->base); + err_free: + kfree(wdt); + err_out: + return ret; +} + +static int __devexit mpcore_wdt_remove(struct device *dev) +{ + struct mpcore_wdt *wdt = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + + misc_deregister(&mpcore_wdt_miscdev); + + mpcore_wdt_dev = NULL; + + free_irq(wdt->irq, wdt); + iounmap(wdt->base); + kfree(wdt); + return 0; +} + +static struct device_driver mpcore_wdt_driver = { + .name = "mpcore_wdt", + .bus = &platform_bus_type, + .probe = mpcore_wdt_probe, + .remove = __devexit_p(mpcore_wdt_remove), + .shutdown = mpcore_wdt_shutdown, +}; + +static char banner[] __initdata = KERN_INFO "MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n"; + +static int __init mpcore_wdt_init(void) +{ + /* + * Check that the margin value is within it's range; + * if not reset to the default + */ + if (mpcore_wdt_set_heartbeat(mpcore_margin)) { + mpcore_wdt_set_heartbeat(TIMER_MARGIN); + printk(KERN_INFO "mpcore_margin value must be 0<mpcore_margin<65536, using %d\n", + TIMER_MARGIN); + } + + printk(banner, mpcore_noboot, mpcore_margin, nowayout); + + return driver_register(&mpcore_wdt_driver); +} + +static void __exit mpcore_wdt_exit(void) +{ + driver_unregister(&mpcore_wdt_driver); +} + +module_init(mpcore_wdt_init); +module_exit(mpcore_wdt_exit); + +MODULE_AUTHOR("ARM Limited"); +MODULE_DESCRIPTION("MPcore Watchdog Device Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/mv64x60_wdt.c b/drivers/char/watchdog/mv64x60_wdt.c new file mode 100644 index 00000000000..1436aea3b28 --- /dev/null +++ b/drivers/char/watchdog/mv64x60_wdt.c @@ -0,0 +1,252 @@ +/* + * mv64x60_wdt.c - MV64X60 (Marvell Discovery) watchdog userspace interface + * + * Author: James Chapman <jchapman@katalix.com> + * + * Platform-specific setup code should configure the dog to generate + * interrupt or reset as required. This code only enables/disables + * and services the watchdog. + * + * Derived from mpc8xx_wdt.c, with the following copyright. + * + * 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include <linux/config.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/watchdog.h> +#include <asm/mv64x60.h> +#include <asm/uaccess.h> +#include <asm/io.h> + +/* MV64x60 WDC (config) register access definitions */ +#define MV64x60_WDC_CTL1_MASK (3 << 24) +#define MV64x60_WDC_CTL1(val) ((val & 3) << 24) +#define MV64x60_WDC_CTL2_MASK (3 << 26) +#define MV64x60_WDC_CTL2(val) ((val & 3) << 26) + +/* Flags bits */ +#define MV64x60_WDOG_FLAG_OPENED 0 +#define MV64x60_WDOG_FLAG_ENABLED 1 + +static unsigned long wdt_flags; +static int wdt_status; +static void __iomem *mv64x60_regs; +static int mv64x60_wdt_timeout; + +static void mv64x60_wdt_reg_write(u32 val) +{ + /* Allow write only to CTL1 / CTL2 fields, retaining values in + * other fields. + */ + u32 data = readl(mv64x60_regs + MV64x60_WDT_WDC); + data &= ~(MV64x60_WDC_CTL1_MASK | MV64x60_WDC_CTL2_MASK); + data |= val; + writel(data, mv64x60_regs + MV64x60_WDT_WDC); +} + +static void mv64x60_wdt_service(void) +{ + /* Write 01 followed by 10 to CTL2 */ + mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x01)); + mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x02)); +} + +static void mv64x60_wdt_handler_disable(void) +{ + if (test_and_clear_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) { + /* Write 01 followed by 10 to CTL1 */ + mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01)); + mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02)); + printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n"); + } +} + +static void mv64x60_wdt_handler_enable(void) +{ + if (!test_and_set_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) { + /* Write 01 followed by 10 to CTL1 */ + mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01)); + mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02)); + printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n"); + } +} + +static int mv64x60_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags)) + return -EBUSY; + + mv64x60_wdt_service(); + mv64x60_wdt_handler_enable(); + + return 0; +} + +static int mv64x60_wdt_release(struct inode *inode, struct file *file) +{ + mv64x60_wdt_service(); + +#if !defined(CONFIG_WATCHDOG_NOWAYOUT) + mv64x60_wdt_handler_disable(); +#endif + + clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags); + + return 0; +} + +static ssize_t mv64x60_wdt_write(struct file *file, const char *data, + size_t len, loff_t * ppos) +{ + if (*ppos != file->f_pos) + return -ESPIPE; + + if (len) + mv64x60_wdt_service(); + + return len; +} + +static int mv64x60_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int timeout; + static struct watchdog_info info = { + .options = WDIOF_KEEPALIVEPING, + .firmware_version = 0, + .identity = "MV64x60 watchdog", + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + if (put_user(wdt_status, (int *)arg)) + return -EFAULT; + wdt_status &= ~WDIOF_KEEPALIVEPING; + break; + + case WDIOC_GETTEMP: + return -EOPNOTSUPP; + + case WDIOC_SETOPTIONS: + return -EOPNOTSUPP; + + case WDIOC_KEEPALIVE: + mv64x60_wdt_service(); + wdt_status |= WDIOF_KEEPALIVEPING; + break; + + case WDIOC_SETTIMEOUT: + return -EOPNOTSUPP; + + case WDIOC_GETTIMEOUT: + timeout = mv64x60_wdt_timeout * HZ; + if (put_user(timeout, (int *)arg)) + return -EFAULT; + break; + + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static struct file_operations mv64x60_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = mv64x60_wdt_write, + .ioctl = mv64x60_wdt_ioctl, + .open = mv64x60_wdt_open, + .release = mv64x60_wdt_release, +}; + +static struct miscdevice mv64x60_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &mv64x60_wdt_fops, +}; + +static int __devinit mv64x60_wdt_probe(struct device *dev) +{ + struct platform_device *pd = to_platform_device(dev); + struct mv64x60_wdt_pdata *pdata = pd->dev.platform_data; + int bus_clk = 133; + + mv64x60_wdt_timeout = 10; + if (pdata) { + mv64x60_wdt_timeout = pdata->timeout; + bus_clk = pdata->bus_clk; + } + + mv64x60_regs = mv64x60_get_bridge_vbase(); + + writel((mv64x60_wdt_timeout * (bus_clk * 1000000)) >> 8, + mv64x60_regs + MV64x60_WDT_WDC); + + return misc_register(&mv64x60_wdt_miscdev); +} + +static int __devexit mv64x60_wdt_remove(struct device *dev) +{ + misc_deregister(&mv64x60_wdt_miscdev); + + mv64x60_wdt_service(); + mv64x60_wdt_handler_disable(); + + return 0; +} + +static struct device_driver mv64x60_wdt_driver = { + .name = MV64x60_WDT_NAME, + .bus = &platform_bus_type, + .probe = mv64x60_wdt_probe, + .remove = __devexit_p(mv64x60_wdt_remove), +}; + +static struct platform_device *mv64x60_wdt_dev; + +static int __init mv64x60_wdt_init(void) +{ + int ret; + + printk(KERN_INFO "MV64x60 watchdog driver\n"); + + mv64x60_wdt_dev = platform_device_register_simple(MV64x60_WDT_NAME, + -1, NULL, 0); + if (IS_ERR(mv64x60_wdt_dev)) { + ret = PTR_ERR(mv64x60_wdt_dev); + goto out; + } + + ret = driver_register(&mv64x60_wdt_driver); + out: + return ret; +} + +static void __exit mv64x60_wdt_exit(void) +{ + driver_unregister(&mv64x60_wdt_driver); + platform_device_unregister(mv64x60_wdt_dev); +} + +module_init(mv64x60_wdt_init); +module_exit(mv64x60_wdt_exit); + +MODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); +MODULE_DESCRIPTION("MV64x60 watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/pcwd_pci.c b/drivers/char/watchdog/pcwd_pci.c index 2b13afb09c5..5a80adbf803 100644 --- a/drivers/char/watchdog/pcwd_pci.c +++ b/drivers/char/watchdog/pcwd_pci.c @@ -29,27 +29,29 @@ * Includes, defines, variables, module parameters, ... */ -#include <linux/config.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/types.h> -#include <linux/delay.h> -#include <linux/miscdevice.h> -#include <linux/watchdog.h> -#include <linux/notifier.h> -#include <linux/reboot.h> -#include <linux/init.h> -#include <linux/fs.h> -#include <linux/pci.h> -#include <linux/ioport.h> -#include <linux/spinlock.h> - -#include <asm/uaccess.h> -#include <asm/io.h> +#include <linux/config.h> /* For CONFIG_WATCHDOG_NOWAYOUT/... */ +#include <linux/module.h> /* For module specific items */ +#include <linux/moduleparam.h> /* For new moduleparam's */ +#include <linux/types.h> /* For standard types (like size_t) */ +#include <linux/errno.h> /* For the -ENODEV/... values */ +#include <linux/kernel.h> /* For printk/panic/... */ +#include <linux/delay.h> /* For mdelay function */ +#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ +#include <linux/watchdog.h> /* For the watchdog specific items */ +#include <linux/notifier.h> /* For notifier support */ +#include <linux/reboot.h> /* For reboot_notifier stuff */ +#include <linux/init.h> /* For __init/__exit/... */ +#include <linux/fs.h> /* For file operations */ +#include <linux/pci.h> /* For pci functions */ +#include <linux/ioport.h> /* For io-port access */ +#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ + +#include <asm/uaccess.h> /* For copy_to_user/put_user/... */ +#include <asm/io.h> /* For inb/outb/... */ /* Module and version information */ #define WATCHDOG_VERSION "1.01" -#define WATCHDOG_DATE "15 Mar 2005" +#define WATCHDOG_DATE "02 Sep 2005" #define WATCHDOG_DRIVER_NAME "PCI-PC Watchdog" #define WATCHDOG_NAME "pcwd_pci" #define PFX WATCHDOG_NAME ": " @@ -335,12 +337,14 @@ static int pcipcwd_ioctl(struct inode *inode, struct file *file, return -EFAULT; if (new_options & WDIOS_DISABLECARD) { - pcipcwd_stop(); + if (pcipcwd_stop()) + return -EIO; retval = 0; } if (new_options & WDIOS_ENABLECARD) { - pcipcwd_start(); + if (pcipcwd_start()) + return -EIO; retval = 0; } diff --git a/drivers/char/watchdog/s3c2410_wdt.c b/drivers/char/watchdog/s3c2410_wdt.c index 8b292bf343c..3625b2601b4 100644 --- a/drivers/char/watchdog/s3c2410_wdt.c +++ b/drivers/char/watchdog/s3c2410_wdt.c @@ -464,7 +464,7 @@ static void s3c2410wdt_shutdown(struct device *dev) static unsigned long wtcon_save; static unsigned long wtdat_save; -static int s3c2410wdt_suspend(struct device *dev, u32 state, u32 level) +static int s3c2410wdt_suspend(struct device *dev, pm_message_t state, u32 level) { if (level == SUSPEND_POWER_DOWN) { /* Save watchdog state, and turn it off. */ diff --git a/drivers/char/watchdog/sbc8360.c b/drivers/char/watchdog/sbc8360.c new file mode 100644 index 00000000000..c6cbf808d8c --- /dev/null +++ b/drivers/char/watchdog/sbc8360.c @@ -0,0 +1,414 @@ +/* + * SBC8360 Watchdog driver + * + * (c) Copyright 2005 Webcon, Inc. + * + * Based on ib700wdt.c, which is based on advantechwdt.c which is based + * on acquirewdt.c which is based on wdt.c. + * + * (c) Copyright 2001 Charles Howes <chowes@vsol.net> + * + * Based on advantechwdt.c which is based on acquirewdt.c which + * is based on wdt.c. + * + * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl> + * + * Based on acquirewdt.c which is based on wdt.c. + * Original copyright messages: + * + * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox <alan@redhat.com> + * + * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com> + * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT + * Added timeout module option to override default + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/notifier.h> +#include <linux/fs.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/moduleparam.h> + +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/system.h> + +static unsigned long sbc8360_is_open; +static spinlock_t sbc8360_lock; +static char expect_close; + +#define PFX "sbc8360: " + +/* + * + * Watchdog Timer Configuration + * + * The function of the watchdog timer is to reset the system automatically + * and is defined at I/O port 0120H and 0121H. To enable the watchdog timer + * and allow the system to reset, write appropriate values from the table + * below to I/O port 0120H and 0121H. To disable the timer, write a zero + * value to I/O port 0121H for the system to stop the watchdog function. + * + * The following describes how the timer should be programmed (according to + * the vendor documentation) + * + * Enabling Watchdog: + * MOV AX,000AH (enable, phase I) + * MOV DX,0120H + * OUT DX,AX + * MOV AX,000BH (enable, phase II) + * MOV DX,0120H + * OUT DX,AX + * MOV AX,000nH (set multiplier n, from 1-4) + * MOV DX,0120H + * OUT DX,AX + * MOV AX,000mH (set base timer m, from 0-F) + * MOV DX,0121H + * OUT DX,AX + * + * Reset timer: + * MOV AX,000mH (same as set base timer, above) + * MOV DX,0121H + * OUT DX,AX + * + * Disabling Watchdog: + * MOV AX,0000H (a zero value) + * MOV DX,0120H + * OUT DX,AX + * + * Watchdog timeout configuration values: + * N + * M | 1 2 3 4 + * --|---------------------------------- + * 0 | 0.5s 5s 50s 100s + * 1 | 1s 10s 100s 200s + * 2 | 1.5s 15s 150s 300s + * 3 | 2s 20s 200s 400s + * 4 | 2.5s 25s 250s 500s + * 5 | 3s 30s 300s 600s + * 6 | 3.5s 35s 350s 700s + * 7 | 4s 40s 400s 800s + * 8 | 4.5s 45s 450s 900s + * 9 | 5s 50s 500s 1000s + * A | 5.5s 55s 550s 1100s + * B | 6s 60s 600s 1200s + * C | 6.5s 65s 650s 1300s + * D | 7s 70s 700s 1400s + * E | 7.5s 75s 750s 1500s + * F | 8s 80s 800s 1600s + * + * Another way to say the same things is: + * For N=1, Timeout = (M+1) * 0.5s + * For N=2, Timeout = (M+1) * 5s + * For N=3, Timeout = (M+1) * 50s + * For N=4, Timeout = (M+1) * 100s + * + */ + +static int wd_times[64][2] = { + {0, 1}, /* 0 = 0.5s */ + {1, 1}, /* 1 = 1s */ + {2, 1}, /* 2 = 1.5s */ + {3, 1}, /* 3 = 2s */ + {4, 1}, /* 4 = 2.5s */ + {5, 1}, /* 5 = 3s */ + {6, 1}, /* 6 = 3.5s */ + {7, 1}, /* 7 = 4s */ + {8, 1}, /* 8 = 4.5s */ + {9, 1}, /* 9 = 5s */ + {0xA, 1}, /* 10 = 5.5s */ + {0xB, 1}, /* 11 = 6s */ + {0xC, 1}, /* 12 = 6.5s */ + {0xD, 1}, /* 13 = 7s */ + {0xE, 1}, /* 14 = 7.5s */ + {0xF, 1}, /* 15 = 8s */ + {0, 2}, /* 16 = 5s */ + {1, 2}, /* 17 = 10s */ + {2, 2}, /* 18 = 15s */ + {3, 2}, /* 19 = 20s */ + {4, 2}, /* 20 = 25s */ + {5, 2}, /* 21 = 30s */ + {6, 2}, /* 22 = 35s */ + {7, 2}, /* 23 = 40s */ + {8, 2}, /* 24 = 45s */ + {9, 2}, /* 25 = 50s */ + {0xA, 2}, /* 26 = 55s */ + {0xB, 2}, /* 27 = 60s */ + {0xC, 2}, /* 28 = 65s */ + {0xD, 2}, /* 29 = 70s */ + {0xE, 2}, /* 30 = 75s */ + {0xF, 2}, /* 31 = 80s */ + {0, 3}, /* 32 = 50s */ + {1, 3}, /* 33 = 100s */ + {2, 3}, /* 34 = 150s */ + {3, 3}, /* 35 = 200s */ + {4, 3}, /* 36 = 250s */ + {5, 3}, /* 37 = 300s */ + {6, 3}, /* 38 = 350s */ + {7, 3}, /* 39 = 400s */ + {8, 3}, /* 40 = 450s */ + {9, 3}, /* 41 = 500s */ + {0xA, 3}, /* 42 = 550s */ + {0xB, 3}, /* 43 = 600s */ + {0xC, 3}, /* 44 = 650s */ + {0xD, 3}, /* 45 = 700s */ + {0xE, 3}, /* 46 = 750s */ + {0xF, 3}, /* 47 = 800s */ + {0, 4}, /* 48 = 100s */ + {1, 4}, /* 49 = 200s */ + {2, 4}, /* 50 = 300s */ + {3, 4}, /* 51 = 400s */ + {4, 4}, /* 52 = 500s */ + {5, 4}, /* 53 = 600s */ + {6, 4}, /* 54 = 700s */ + {7, 4}, /* 55 = 800s */ + {8, 4}, /* 56 = 900s */ + {9, 4}, /* 57 = 1000s */ + {0xA, 4}, /* 58 = 1100s */ + {0xB, 4}, /* 59 = 1200s */ + {0xC, 4}, /* 60 = 1300s */ + {0xD, 4}, /* 61 = 1400s */ + {0xE, 4}, /* 62 = 1500s */ + {0xF, 4} /* 63 = 1600s */ +}; + +#define SBC8360_ENABLE 0x120 +#define SBC8360_BASETIME 0x121 + +static int timeout = 27; +static int wd_margin = 0xB; +static int wd_multiplier = 2; +static int nowayout = WATCHDOG_NOWAYOUT; + +module_param(timeout, int, 27); +MODULE_PARM_DESC(timeout, "Index into timeout table (0-63) (default=27 (60s))"); +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); + +/* + * Kernel methods. + */ + +/* Activate and pre-configure watchdog */ +static void sbc8360_activate(void) +{ + /* Enable the watchdog */ + outb(0x0A, SBC8360_ENABLE); + msleep_interruptible(100); + outb(0x0B, SBC8360_ENABLE); + msleep_interruptible(100); + /* Set timeout multiplier */ + outb(wd_multiplier, SBC8360_ENABLE); + msleep_interruptible(100); + /* Nothing happens until first sbc8360_ping() */ +} + +/* Kernel pings watchdog */ +static void sbc8360_ping(void) +{ + /* Write the base timer register */ + outb(wd_margin, SBC8360_BASETIME); +} + +/* Userspace pings kernel driver, or requests clean close */ +static ssize_t sbc8360_write(struct file *file, const char __user * buf, + size_t count, loff_t * ppos) +{ + if (count) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + sbc8360_ping(); + } + return count; +} + +static int sbc8360_open(struct inode *inode, struct file *file) +{ + spin_lock(&sbc8360_lock); + if (test_and_set_bit(0, &sbc8360_is_open)) { + spin_unlock(&sbc8360_lock); + return -EBUSY; + } + if (nowayout) + __module_get(THIS_MODULE); + + /* Activate and ping once to start the countdown */ + spin_unlock(&sbc8360_lock); + sbc8360_activate(); + sbc8360_ping(); + return nonseekable_open(inode, file); +} + +static int sbc8360_close(struct inode *inode, struct file *file) +{ + spin_lock(&sbc8360_lock); + if (expect_close == 42) + outb(0, SBC8360_ENABLE); + else + printk(KERN_CRIT PFX + "SBC8360 device closed unexpectedly. SBC8360 will not stop!\n"); + + clear_bit(0, &sbc8360_is_open); + expect_close = 0; + spin_unlock(&sbc8360_lock); + return 0; +} + +/* + * Notifier for system down + */ + +static int sbc8360_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) { + /* Disable the SBC8360 Watchdog */ + outb(0, SBC8360_ENABLE); + } + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + +static struct file_operations sbc8360_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = sbc8360_write, + .open = sbc8360_open, + .release = sbc8360_close, +}; + +static struct miscdevice sbc8360_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &sbc8360_fops, +}; + +/* + * The SBC8360 needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block sbc8360_notifier = { + .notifier_call = sbc8360_notify_sys, +}; + +static int __init sbc8360_init(void) +{ + int res; + unsigned long int mseconds = 60000; + + spin_lock_init(&sbc8360_lock); + res = misc_register(&sbc8360_miscdev); + if (res) { + printk(KERN_ERR PFX "failed to register misc device\n"); + goto out_nomisc; + } + + if (!request_region(SBC8360_ENABLE, 1, "SBC8360")) { + printk(KERN_ERR PFX "ENABLE method I/O %X is not available.\n", + SBC8360_ENABLE); + res = -EIO; + goto out_noenablereg; + } + if (!request_region(SBC8360_BASETIME, 1, "SBC8360")) { + printk(KERN_ERR PFX + "BASETIME method I/O %X is not available.\n", + SBC8360_BASETIME); + res = -EIO; + goto out_nobasetimereg; + } + + res = register_reboot_notifier(&sbc8360_notifier); + if (res) { + printk(KERN_ERR PFX "Failed to register reboot notifier.\n"); + goto out_noreboot; + } + + if (timeout < 0 || timeout > 63) { + printk(KERN_ERR PFX "Invalid timeout index (must be 0-63).\n"); + res = -EINVAL; + goto out_noreboot; + } + + wd_margin = wd_times[timeout][0]; + wd_multiplier = wd_times[timeout][1]; + + if (wd_multiplier == 1) + mseconds = (wd_margin + 1) * 500; + else if (wd_multiplier == 2) + mseconds = (wd_margin + 1) * 5000; + else if (wd_multiplier == 3) + mseconds = (wd_margin + 1) * 50000; + else if (wd_multiplier == 4) + mseconds = (wd_margin + 1) * 100000; + + /* My kingdom for the ability to print "0.5 seconds" in the kernel! */ + printk(KERN_INFO PFX "Timeout set at %ld ms.\n", mseconds); + + return 0; + + out_noreboot: + release_region(SBC8360_ENABLE, 1); + release_region(SBC8360_BASETIME, 1); + out_noenablereg: + out_nobasetimereg: + misc_deregister(&sbc8360_miscdev); + out_nomisc: + return res; +} + +static void __exit sbc8360_exit(void) +{ + misc_deregister(&sbc8360_miscdev); + unregister_reboot_notifier(&sbc8360_notifier); + release_region(SBC8360_ENABLE, 1); + release_region(SBC8360_BASETIME, 1); +} + +module_init(sbc8360_init); +module_exit(sbc8360_exit); + +MODULE_AUTHOR("Ian E. Morgan <imorgan@webcon.ca>"); +MODULE_DESCRIPTION("SBC8360 watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + +/* end of sbc8360.c */ diff --git a/drivers/char/watchdog/w83977f_wdt.c b/drivers/char/watchdog/w83977f_wdt.c new file mode 100644 index 00000000000..a7ff64c8921 --- /dev/null +++ b/drivers/char/watchdog/w83977f_wdt.c @@ -0,0 +1,543 @@ +/* + * W83977F Watchdog Timer Driver for Winbond W83977F I/O Chip + * + * (c) Copyright 2005 Jose Goncalves <jose.goncalves@inov.pt> + * + * Based on w83877f_wdt.c by Scott Jennings, + * and wdt977.c by Woody Suwalski + * + * ----------------------- + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/watchdog.h> +#include <linux/notifier.h> +#include <linux/reboot.h> + +#include <asm/io.h> +#include <asm/system.h> +#include <asm/uaccess.h> + +#define WATCHDOG_VERSION "1.00" +#define WATCHDOG_NAME "W83977F WDT" +#define PFX WATCHDOG_NAME ": " +#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n" + +#define IO_INDEX_PORT 0x3F0 +#define IO_DATA_PORT (IO_INDEX_PORT+1) + +#define UNLOCK_DATA 0x87 +#define LOCK_DATA 0xAA +#define DEVICE_REGISTER 0x07 + +#define DEFAULT_TIMEOUT 45 /* default timeout in seconds */ + +static int timeout = DEFAULT_TIMEOUT; +static int timeoutW; /* timeout in watchdog counter units */ +static unsigned long timer_alive; +static int testmode; +static char expect_close; +static spinlock_t spinlock; + +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (15..7635), default=" __MODULE_STRING(DEFAULT_TIMEOUT) ")"); +module_param(testmode, int, 0); +MODULE_PARM_DESC(testmode,"Watchdog testmode (1 = no reboot), default=0"); + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); + +/* + * Start the watchdog + */ + +static int wdt_start(void) +{ + unsigned long flags; + + spin_lock_irqsave(&spinlock, flags); + + /* Unlock the SuperIO chip */ + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + + /* + * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4. + * F2 has the timeout in watchdog counter units. + * F3 is set to enable watchdog LED blink at timeout. + * F4 is used to just clear the TIMEOUT'ed state (bit 0). + */ + outb_p(DEVICE_REGISTER,IO_INDEX_PORT); + outb_p(0x08,IO_DATA_PORT); + outb_p(0xF2,IO_INDEX_PORT); + outb_p(timeoutW,IO_DATA_PORT); + outb_p(0xF3,IO_INDEX_PORT); + outb_p(0x08,IO_DATA_PORT); + outb_p(0xF4,IO_INDEX_PORT); + outb_p(0x00,IO_DATA_PORT); + + /* Set device Aux2 active */ + outb_p(0x30,IO_INDEX_PORT); + outb_p(0x01,IO_DATA_PORT); + + /* + * Select device Aux1 (dev=7) to set GP16 as the watchdog output + * (in reg E6) and GP13 as the watchdog LED output (in reg E3). + * Map GP16 at pin 119. + * In test mode watch the bit 0 on F4 to indicate "triggered" or + * check watchdog LED on SBC. + */ + outb_p(DEVICE_REGISTER,IO_INDEX_PORT); + outb_p(0x07,IO_DATA_PORT); + if (!testmode) + { + unsigned pin_map; + + outb_p(0xE6,IO_INDEX_PORT); + outb_p(0x0A,IO_DATA_PORT); + outb_p(0x2C,IO_INDEX_PORT); + pin_map = inb_p(IO_DATA_PORT); + pin_map |= 0x10; + pin_map &= ~(0x20); + outb_p(0x2C,IO_INDEX_PORT); + outb_p(pin_map,IO_DATA_PORT); + } + outb_p(0xE3,IO_INDEX_PORT); + outb_p(0x08,IO_DATA_PORT); + + /* Set device Aux1 active */ + outb_p(0x30,IO_INDEX_PORT); + outb_p(0x01,IO_DATA_PORT); + + /* Lock the SuperIO chip */ + outb_p(LOCK_DATA,IO_INDEX_PORT); + + spin_unlock_irqrestore(&spinlock, flags); + + printk(KERN_INFO PFX "activated.\n"); + + return 0; +} + +/* + * Stop the watchdog + */ + +static int wdt_stop(void) +{ + unsigned long flags; + + spin_lock_irqsave(&spinlock, flags); + + /* Unlock the SuperIO chip */ + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + + /* + * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4. + * F2 is reset to its default value (watchdog timer disabled). + * F3 is reset to its default state. + * F4 clears the TIMEOUT'ed state (bit 0) - back to default. + */ + outb_p(DEVICE_REGISTER,IO_INDEX_PORT); + outb_p(0x08,IO_DATA_PORT); + outb_p(0xF2,IO_INDEX_PORT); + outb_p(0xFF,IO_DATA_PORT); + outb_p(0xF3,IO_INDEX_PORT); + outb_p(0x00,IO_DATA_PORT); + outb_p(0xF4,IO_INDEX_PORT); + outb_p(0x00,IO_DATA_PORT); + outb_p(0xF2,IO_INDEX_PORT); + outb_p(0x00,IO_DATA_PORT); + + /* + * Select device Aux1 (dev=7) to set GP16 (in reg E6) and + * Gp13 (in reg E3) as inputs. + */ + outb_p(DEVICE_REGISTER,IO_INDEX_PORT); + outb_p(0x07,IO_DATA_PORT); + if (!testmode) + { + outb_p(0xE6,IO_INDEX_PORT); + outb_p(0x01,IO_DATA_PORT); + } + outb_p(0xE3,IO_INDEX_PORT); + outb_p(0x01,IO_DATA_PORT); + + /* Lock the SuperIO chip */ + outb_p(LOCK_DATA,IO_INDEX_PORT); + + spin_unlock_irqrestore(&spinlock, flags); + + printk(KERN_INFO PFX "shutdown.\n"); + + return 0; +} + +/* + * Send a keepalive ping to the watchdog + * This is done by simply re-writing the timeout to reg. 0xF2 + */ + +static int wdt_keepalive(void) +{ + unsigned long flags; + + spin_lock_irqsave(&spinlock, flags); + + /* Unlock the SuperIO chip */ + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + + /* Select device Aux2 (device=8) to kick watchdog reg F2 */ + outb_p(DEVICE_REGISTER,IO_INDEX_PORT); + outb_p(0x08,IO_DATA_PORT); + outb_p(0xF2,IO_INDEX_PORT); + outb_p(timeoutW,IO_DATA_PORT); + + /* Lock the SuperIO chip */ + outb_p(LOCK_DATA,IO_INDEX_PORT); + + spin_unlock_irqrestore(&spinlock, flags); + + return 0; +} + +/* + * Set the watchdog timeout value + */ + +static int wdt_set_timeout(int t) +{ + int tmrval; + + /* + * Convert seconds to watchdog counter time units, rounding up. + * On PCM-5335 watchdog units are 30 seconds/step with 15 sec startup + * value. This information is supplied in the PCM-5335 manual and was + * checked by me on a real board. This is a bit strange because W83977f + * datasheet says counter unit is in minutes! + */ + if (t < 15) + return -EINVAL; + + tmrval = ((t + 15) + 29) / 30; + + if (tmrval > 255) + return -EINVAL; + + /* + * timeout is the timeout in seconds, + * timeoutW is the timeout in watchdog counter units. + */ + timeoutW = tmrval; + timeout = (timeoutW * 30) - 15; + return 0; +} + +/* + * Get the watchdog status + */ + +static int wdt_get_status(int *status) +{ + int new_status; + unsigned long flags; + + spin_lock_irqsave(&spinlock, flags); + + /* Unlock the SuperIO chip */ + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + + /* Select device Aux2 (device=8) to read watchdog reg F4 */ + outb_p(DEVICE_REGISTER,IO_INDEX_PORT); + outb_p(0x08,IO_DATA_PORT); + outb_p(0xF4,IO_INDEX_PORT); + new_status = inb_p(IO_DATA_PORT); + + /* Lock the SuperIO chip */ + outb_p(LOCK_DATA,IO_INDEX_PORT); + + spin_unlock_irqrestore(&spinlock, flags); + + *status = 0; + if (new_status & 1) + *status |= WDIOF_CARDRESET; + + return 0; +} + + +/* + * /dev/watchdog handling + */ + +static int wdt_open(struct inode *inode, struct file *file) +{ + /* If the watchdog is alive we don't need to start it again */ + if( test_and_set_bit(0, &timer_alive) ) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + wdt_start(); + return nonseekable_open(inode, file); +} + +static int wdt_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + * Lock it in if it's a module and we set nowayout + */ + if (expect_close == 42) + { + wdt_stop(); + clear_bit(0, &timer_alive); + } else { + wdt_keepalive(); + printk(KERN_CRIT PFX "unexpected close, not stopping watchdog!\n"); + } + expect_close = 0; + return 0; +} + +/* + * wdt_write: + * @file: file handle to the watchdog + * @buf: buffer to write (unused as data does not matter here + * @count: count of bytes + * @ppos: pointer to the position to write. No seeks allowed + * + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we we don't define content meaning. + */ + +static ssize_t wdt_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + /* See if we got the magic character 'V' and reload the timer */ + if(count) + { + if (!nowayout) + { + size_t ofs; + + /* note: just in case someone wrote the magic character long ago */ + expect_close = 0; + + /* scan to see whether or not we got the magic character */ + for(ofs = 0; ofs != count; ofs++) + { + char c; + if (get_user(c, buf + ofs)) + return -EFAULT; + if (c == 'V') { + expect_close = 42; + } + } + } + + /* someone wrote to us, we should restart timer */ + wdt_keepalive(); + } + return count; +} + +/* + * wdt_ioctl: + * @inode: inode of the device + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. + */ + +static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, + .firmware_version = 1, + .identity = WATCHDOG_NAME, +}; + +static int wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int status; + int new_options, retval = -EINVAL; + int new_timeout; + union { + struct watchdog_info __user *ident; + int __user *i; + } uarg; + + uarg.i = (int __user *)arg; + + switch(cmd) + { + default: + return -ENOIOCTLCMD; + + case WDIOC_GETSUPPORT: + return copy_to_user(uarg.ident, &ident, sizeof(ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + wdt_get_status(&status); + return put_user(status, uarg.i); + + case WDIOC_GETBOOTSTATUS: + return put_user(0, uarg.i); + + case WDIOC_KEEPALIVE: + wdt_keepalive(); + return 0; + + case WDIOC_SETOPTIONS: + if (get_user (new_options, uarg.i)) + return -EFAULT; + + if (new_options & WDIOS_DISABLECARD) { + wdt_stop(); + retval = 0; + } + + if (new_options & WDIOS_ENABLECARD) { + wdt_start(); + retval = 0; + } + + return retval; + + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, uarg.i)) + return -EFAULT; + + if (wdt_set_timeout(new_timeout)) + return -EINVAL; + + wdt_keepalive(); + /* Fall */ + + case WDIOC_GETTIMEOUT: + return put_user(timeout, uarg.i); + + } +} + +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code==SYS_DOWN || code==SYS_HALT) + wdt_stop(); + return NOTIFY_DONE; +} + +static struct file_operations wdt_fops= +{ + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wdt_write, + .ioctl = wdt_ioctl, + .open = wdt_open, + .release = wdt_release, +}; + +static struct miscdevice wdt_miscdev= +{ + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdt_fops, +}; + +static struct notifier_block wdt_notifier = { + .notifier_call = wdt_notify_sys, +}; + +static int __init w83977f_wdt_init(void) +{ + int rc; + + printk(KERN_INFO PFX DRIVER_VERSION); + + spin_lock_init(&spinlock); + + /* + * Check that the timeout value is within it's range ; + * if not reset to the default + */ + if (wdt_set_timeout(timeout)) { + wdt_set_timeout(DEFAULT_TIMEOUT); + printk(KERN_INFO PFX "timeout value must be 15<=timeout<=7635, using %d\n", + DEFAULT_TIMEOUT); + } + + if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) + { + printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", + IO_INDEX_PORT); + rc = -EIO; + goto err_out; + } + + rc = misc_register(&wdt_miscdev); + if (rc) + { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + wdt_miscdev.minor, rc); + goto err_out_region; + } + + rc = register_reboot_notifier(&wdt_notifier); + if (rc) + { + printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", + rc); + goto err_out_miscdev; + } + + printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d testmode=%d)\n", + timeout, nowayout, testmode); + + return 0; + +err_out_miscdev: + misc_deregister(&wdt_miscdev); +err_out_region: + release_region(IO_INDEX_PORT,2); +err_out: + return rc; +} + +static void __exit w83977f_wdt_exit(void) +{ + wdt_stop(); + misc_deregister(&wdt_miscdev); + unregister_reboot_notifier(&wdt_notifier); + release_region(IO_INDEX_PORT,2); +} + +module_init(w83977f_wdt_init); +module_exit(w83977f_wdt_exit); + +MODULE_AUTHOR("Jose Goncalves <jose.goncalves@inov.pt>"); +MODULE_DESCRIPTION("Driver for watchdog timer in W83977F I/O chip"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 138dc50270e..7e72e922b41 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -411,6 +411,23 @@ config SENSORS_W83627EHF This driver can also be built as a module. If so, the module will be called w83627ehf. +config SENSORS_HDAPS + tristate "IBM Hard Drive Active Protection System (hdaps)" + depends on HWMON && INPUT && X86 + default n + help + This driver provides support for the IBM Hard Drive Active Protection + System (hdaps), which provides an accelerometer and other misc. data. + Supported laptops include the IBM ThinkPad T41, T42, T43, and R51. + The accelerometer data is readable via sysfs. + + This driver also provides an input class device, allowing the + laptop to act as a pinball machine-esque mouse. This is off by + default but enabled via sysfs or the module parameter "mousedev". + + Say Y here if you have an applicable laptop and want to experience + the awesome power of hdaps. + config HWMON_DEBUG_CHIP bool "Hardware Monitoring Chip debugging messages" depends on HWMON diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 381f1bf04cc..f7d6a2f61ee 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_SENSORS_FSCHER) += fscher.o obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o +obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_LM63) += lm63.o obj-$(CONFIG_SENSORS_LM75) += lm75.o diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c new file mode 100644 index 00000000000..eaebfc14c93 --- /dev/null +++ b/drivers/hwmon/hdaps.c @@ -0,0 +1,739 @@ +/* + * drivers/hwmon/hdaps.c - driver for IBM's Hard Drive Active Protection System + * + * Copyright (C) 2005 Robert Love <rml@novell.com> + * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> + * + * The HardDisk Active Protection System (hdaps) is present in the IBM ThinkPad + * T41, T42, T43, R51, and X40, at least. It provides a basic two-axis + * accelerometer and other data, such as the device's temperature. + * + * Based on the document by Mark A. Smith available at + * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html and a lot of trial + * and error. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License v2 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/timer.h> +#include <linux/dmi.h> +#include <asm/io.h> + +#define HDAPS_LOW_PORT 0x1600 /* first port used by hdaps */ +#define HDAPS_NR_PORTS 0x30 /* 0x1600 - 0x162f */ + +#define STATE_FRESH 0x50 /* accelerometer data is fresh */ + +#define REFRESH_ASYNC 0x00 /* do asynchronous refresh */ +#define REFRESH_SYNC 0x01 /* do synchronous refresh */ + +#define HDAPS_PORT_STATE 0x1611 /* device state */ +#define HDAPS_PORT_YPOS 0x1612 /* y-axis position */ +#define HDAPS_PORT_XPOS 0x1614 /* x-axis position */ +#define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in celcius */ +#define HDAPS_PORT_YVAR 0x1617 /* y-axis variance (what is this?) */ +#define HDAPS_PORT_XVAR 0x1619 /* x-axis variance (what is this?) */ +#define HDAPS_PORT_TEMP2 0x161b /* device temperature (again?) */ +#define HDAPS_PORT_UNKNOWN 0x161c /* what is this? */ +#define HDAPS_PORT_KMACT 0x161d /* keyboard or mouse activity */ + +#define HDAPS_READ_MASK 0xff /* some reads have the low 8 bits set */ + +#define KEYBD_MASK 0x20 /* set if keyboard activity */ +#define MOUSE_MASK 0x40 /* set if mouse activity */ +#define KEYBD_ISSET(n) (!! (n & KEYBD_MASK)) /* keyboard used? */ +#define MOUSE_ISSET(n) (!! (n & MOUSE_MASK)) /* mouse used? */ + +#define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */ +#define INIT_WAIT_MSECS 200 /* ... in 200ms increments */ + +static struct platform_device *pdev; +static struct input_dev hdaps_idev; +static struct timer_list hdaps_timer; +static unsigned int hdaps_mousedev_threshold = 4; +static unsigned long hdaps_poll_ms = 50; +static unsigned int hdaps_mousedev; +static unsigned int hdaps_invert; +static u8 km_activity; +static int rest_x; +static int rest_y; + +static DECLARE_MUTEX(hdaps_sem); + +/* + * __get_latch - Get the value from a given port. Callers must hold hdaps_sem. + */ +static inline u8 __get_latch(u16 port) +{ + return inb(port) & HDAPS_READ_MASK; +} + +/* + * __check_latch - Check a port latch for a given value. Callers must hold + * hdaps_sem. Returns zero if the port contains the given value. + */ +static inline unsigned int __check_latch(u16 port, u8 val) +{ + if (__get_latch(port) == val) + return 0; + return -EINVAL; +} + +/* + * __wait_latch - Wait up to 100us for a port latch to get a certain value, + * returning zero if the value is obtained. Callers must hold hdaps_sem. + */ +static unsigned int __wait_latch(u16 port, u8 val) +{ + unsigned int i; + + for (i = 0; i < 20; i++) { + if (!__check_latch(port, val)) + return 0; + udelay(5); + } + + return -EINVAL; +} + +/* + * __device_refresh - Request a refresh from the accelerometer. + * + * If sync is REFRESH_SYNC, we perform a synchronous refresh and will wait. + * Returns zero if successful and nonzero on error. + * + * If sync is REFRESH_ASYNC, we merely kick off a new refresh if the device is + * not up-to-date. Always returns zero. + * + * Callers must hold hdaps_sem. + */ +static int __device_refresh(unsigned int sync) +{ + u8 state; + + udelay(100); + + state = inb(0x1604); + if (state == STATE_FRESH) + return 0; + + outb(0x11, 0x1610); + outb(0x01, 0x161f); + if (sync == REFRESH_ASYNC) + return 0; + + return __wait_latch(0x1604, STATE_FRESH); +} + +/* + * __device_complete - Indicate to the accelerometer that we are done reading + * data, and then initiate an async refresh. Callers must hold hdaps_sem. + */ +static inline void __device_complete(void) +{ + inb(0x161f); + inb(0x1604); + __device_refresh(REFRESH_ASYNC); +} + +static int __hdaps_readb_one(unsigned int port, u8 *val) +{ + /* do a sync refresh -- we need to be sure that we read fresh data */ + if (__device_refresh(REFRESH_SYNC)) + return -EIO; + + *val = inb(port); + __device_complete(); + + return 0; +} + +/* + * hdaps_readb_one - reads a byte from a single I/O port, placing the value in + * the given pointer. Returns zero on success or a negative error on failure. + * Can sleep. + */ +static int hdaps_readb_one(unsigned int port, u8 *val) +{ + int ret; + + down(&hdaps_sem); + ret = __hdaps_readb_one(port, val); + up(&hdaps_sem); + + return ret; +} + +static int __hdaps_read_pair(unsigned int port1, unsigned int port2, + int *x, int *y) +{ + /* do a sync refresh -- we need to be sure that we read fresh data */ + if (__device_refresh(REFRESH_SYNC)) + return -EIO; + + *y = inw(port2); + *x = inw(port1); + km_activity = inb(HDAPS_PORT_KMACT); + __device_complete(); + + /* if hdaps_invert is set, negate the two values */ + if (hdaps_invert) { + *x = -*x; + *y = -*y; + } + + return 0; +} + +/* + * hdaps_read_pair - reads the values from a pair of ports, placing the values + * in the given pointers. Returns zero on success. Can sleep. + */ +static int hdaps_read_pair(unsigned int port1, unsigned int port2, + int *val1, int *val2) +{ + int ret; + + down(&hdaps_sem); + ret = __hdaps_read_pair(port1, port2, val1, val2); + up(&hdaps_sem); + + return ret; +} + +/* initialize the accelerometer */ +static int hdaps_device_init(void) +{ + unsigned int total_msecs = INIT_TIMEOUT_MSECS; + int ret = -ENXIO; + + down(&hdaps_sem); + + outb(0x13, 0x1610); + outb(0x01, 0x161f); + if (__wait_latch(0x161f, 0x00)) + goto out; + + /* + * The 0x03 value appears to only work on some thinkpads, such as the + * T42p. Others return 0x01. + * + * The 0x02 value occurs when the chip has been previously initialized. + */ + if (__check_latch(0x1611, 0x03) && + __check_latch(0x1611, 0x02) && + __check_latch(0x1611, 0x01)) + goto out; + + printk(KERN_DEBUG "hdaps: initial latch check good (0x%02x).\n", + __get_latch(0x1611)); + + outb(0x17, 0x1610); + outb(0x81, 0x1611); + outb(0x01, 0x161f); + if (__wait_latch(0x161f, 0x00)) + goto out; + if (__wait_latch(0x1611, 0x00)) + goto out; + if (__wait_latch(0x1612, 0x60)) + goto out; + if (__wait_latch(0x1613, 0x00)) + goto out; + outb(0x14, 0x1610); + outb(0x01, 0x1611); + outb(0x01, 0x161f); + if (__wait_latch(0x161f, 0x00)) + goto out; + outb(0x10, 0x1610); + outb(0xc8, 0x1611); + outb(0x00, 0x1612); + outb(0x02, 0x1613); + outb(0x01, 0x161f); + if (__wait_latch(0x161f, 0x00)) + goto out; + if (__device_refresh(REFRESH_SYNC)) + goto out; + if (__wait_latch(0x1611, 0x00)) + goto out; + + /* we have done our dance, now let's wait for the applause */ + while (total_msecs > 0) { + u8 ignored; + + /* a read of the device helps push it into action */ + __hdaps_readb_one(HDAPS_PORT_UNKNOWN, &ignored); + if (!__wait_latch(0x1611, 0x02)) { + ret = 0; + break; + } + + msleep(INIT_WAIT_MSECS); + total_msecs -= INIT_WAIT_MSECS; + } + +out: + up(&hdaps_sem); + return ret; +} + + +/* Input class stuff */ + +/* + * hdaps_calibrate - Zero out our "resting" values. Callers must hold hdaps_sem. + */ +static void hdaps_calibrate(void) +{ + int x, y; + + if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y)) + return; + + rest_x = x; + rest_y = y; +} + +static void hdaps_mousedev_poll(unsigned long unused) +{ + int x, y; + + /* Cannot sleep. Try nonblockingly. If we fail, try again later. */ + if (down_trylock(&hdaps_sem)) { + mod_timer(&hdaps_timer,jiffies+msecs_to_jiffies(hdaps_poll_ms)); + return; + } + + if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y)) + goto out; + + x -= rest_x; + y -= rest_y; + if (abs(x) > hdaps_mousedev_threshold) + input_report_rel(&hdaps_idev, REL_X, x); + if (abs(y) > hdaps_mousedev_threshold) + input_report_rel(&hdaps_idev, REL_Y, y); + input_sync(&hdaps_idev); + + mod_timer(&hdaps_timer, jiffies + msecs_to_jiffies(hdaps_poll_ms)); + +out: + up(&hdaps_sem); +} + +/* + * hdaps_mousedev_enable - enable the input class device. Can sleep. + */ +static void hdaps_mousedev_enable(void) +{ + down(&hdaps_sem); + + /* calibrate the device before enabling */ + hdaps_calibrate(); + + /* initialize the input class */ + init_input_dev(&hdaps_idev); + hdaps_idev.dev = &pdev->dev; + hdaps_idev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + hdaps_idev.relbit[0] = BIT(REL_X) | BIT(REL_Y); + hdaps_idev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT); + input_register_device(&hdaps_idev); + + /* start up our timer */ + init_timer(&hdaps_timer); + hdaps_timer.function = hdaps_mousedev_poll; + hdaps_timer.expires = jiffies + msecs_to_jiffies(hdaps_poll_ms); + add_timer(&hdaps_timer); + + hdaps_mousedev = 1; + + up(&hdaps_sem); + + printk(KERN_INFO "hdaps: input device enabled.\n"); +} + +/* + * hdaps_mousedev_disable - disable the input class device. Caller must hold + * hdaps_sem. + */ +static void hdaps_mousedev_disable(void) +{ + down(&hdaps_sem); + if (hdaps_mousedev) { + hdaps_mousedev = 0; + del_timer_sync(&hdaps_timer); + input_unregister_device(&hdaps_idev); + } + up(&hdaps_sem); +} + + +/* Device model stuff */ + +static int hdaps_probe(struct device *dev) +{ + int ret; + + ret = hdaps_device_init(); + if (ret) + return ret; + + printk(KERN_INFO "hdaps: device successfully initialized.\n"); + return 0; +} + +static int hdaps_resume(struct device *dev, u32 level) +{ + if (level == RESUME_ENABLE) + return hdaps_device_init(); + return 0; +} + +static struct device_driver hdaps_driver = { + .name = "hdaps", + .bus = &platform_bus_type, + .owner = THIS_MODULE, + .probe = hdaps_probe, + .resume = hdaps_resume +}; + + +/* Sysfs Files */ + +static ssize_t hdaps_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, x, y; + + ret = hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); + if (ret) + return ret; + + return sprintf(buf, "(%d,%d)\n", x, y); +} + +static ssize_t hdaps_variance_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, x, y; + + ret = hdaps_read_pair(HDAPS_PORT_XVAR, HDAPS_PORT_YVAR, &x, &y); + if (ret) + return ret; + + return sprintf(buf, "(%d,%d)\n", x, y); +} + +static ssize_t hdaps_temp1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 temp; + int ret; + + ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", temp); +} + +static ssize_t hdaps_temp2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 temp; + int ret; + + ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", temp); +} + +static ssize_t hdaps_keyboard_activity_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", KEYBD_ISSET(km_activity)); +} + +static ssize_t hdaps_mouse_activity_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", MOUSE_ISSET(km_activity)); +} + +static ssize_t hdaps_calibrate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "(%d,%d)\n", rest_x, rest_y); +} + +static ssize_t hdaps_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + down(&hdaps_sem); + hdaps_calibrate(); + up(&hdaps_sem); + + return count; +} + +static ssize_t hdaps_invert_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", hdaps_invert); +} + +static ssize_t hdaps_invert_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int invert; + + if (sscanf(buf, "%d", &invert) != 1 || (invert != 1 && invert != 0)) + return -EINVAL; + + hdaps_invert = invert; + hdaps_calibrate(); + + return count; +} + +static ssize_t hdaps_mousedev_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", hdaps_mousedev); +} + +static ssize_t hdaps_mousedev_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int enable; + + if (sscanf(buf, "%d", &enable) != 1) + return -EINVAL; + + if (enable == 1) + hdaps_mousedev_enable(); + else if (enable == 0) + hdaps_mousedev_disable(); + else + return -EINVAL; + + return count; +} + +static ssize_t hdaps_poll_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", hdaps_poll_ms); +} + +static ssize_t hdaps_poll_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int poll; + + if (sscanf(buf, "%u", &poll) != 1 || poll == 0) + return -EINVAL; + hdaps_poll_ms = poll; + + return count; +} + +static ssize_t hdaps_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", hdaps_mousedev_threshold); +} + +static ssize_t hdaps_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int threshold; + + if (sscanf(buf, "%u", &threshold) != 1 || threshold == 0) + return -EINVAL; + hdaps_mousedev_threshold = threshold; + + return count; +} + +static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL); +static DEVICE_ATTR(variance, 0444, hdaps_variance_show, NULL); +static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL); +static DEVICE_ATTR(temp2, 0444, hdaps_temp2_show, NULL); +static DEVICE_ATTR(keyboard_activity, 0444, hdaps_keyboard_activity_show, NULL); +static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL); +static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store); +static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store); +static DEVICE_ATTR(mousedev, 0644, hdaps_mousedev_show, hdaps_mousedev_store); +static DEVICE_ATTR(mousedev_poll_ms, 0644, hdaps_poll_show, hdaps_poll_store); +static DEVICE_ATTR(mousedev_threshold, 0644, hdaps_threshold_show, + hdaps_threshold_store); + +static struct attribute *hdaps_attributes[] = { + &dev_attr_position.attr, + &dev_attr_variance.attr, + &dev_attr_temp1.attr, + &dev_attr_temp2.attr, + &dev_attr_keyboard_activity.attr, + &dev_attr_mouse_activity.attr, + &dev_attr_calibrate.attr, + &dev_attr_mousedev.attr, + &dev_attr_mousedev_threshold.attr, + &dev_attr_mousedev_poll_ms.attr, + &dev_attr_invert.attr, + NULL, +}; + +static struct attribute_group hdaps_attribute_group = { + .attrs = hdaps_attributes, +}; + + +/* Module stuff */ + +/* + * XXX: We should be able to return nonzero and halt the detection process. + * But there is a bug in dmi_check_system() where a nonzero return from the + * first match will result in a return of failure from dmi_check_system(). + * I fixed this; the patch is in 2.6-mm. Once in Linus's tree we can make + * hdaps_dmi_match_invert() return hdaps_dmi_match(), which in turn returns 1. + */ +static int hdaps_dmi_match(struct dmi_system_id *id) +{ + printk(KERN_INFO "hdaps: %s detected.\n", id->ident); + return 0; +} + +static int hdaps_dmi_match_invert(struct dmi_system_id *id) +{ + hdaps_invert = 1; + printk(KERN_INFO "hdaps: inverting axis readings.\n"); + return 0; +} + +#define HDAPS_DMI_MATCH_NORMAL(model) { \ + .ident = "IBM " model, \ + .callback = hdaps_dmi_match, \ + .matches = { \ + DMI_MATCH(DMI_BOARD_VENDOR, "IBM"), \ + DMI_MATCH(DMI_PRODUCT_VERSION, model) \ + } \ +} + +#define HDAPS_DMI_MATCH_INVERT(model) { \ + .ident = "IBM " model, \ + .callback = hdaps_dmi_match_invert, \ + .matches = { \ + DMI_MATCH(DMI_BOARD_VENDOR, "IBM"), \ + DMI_MATCH(DMI_PRODUCT_VERSION, model) \ + } \ +} + +static int __init hdaps_init(void) +{ + int ret; + + /* Note that DMI_MATCH(...,"ThinkPad T42") will match "ThinkPad T42p" */ + struct dmi_system_id hdaps_whitelist[] = { + HDAPS_DMI_MATCH_INVERT("ThinkPad R50p"), + HDAPS_DMI_MATCH_NORMAL("ThinkPad R50"), + HDAPS_DMI_MATCH_NORMAL("ThinkPad R51"), + HDAPS_DMI_MATCH_INVERT("ThinkPad T41p"), + HDAPS_DMI_MATCH_NORMAL("ThinkPad T41"), + HDAPS_DMI_MATCH_INVERT("ThinkPad T42p"), + HDAPS_DMI_MATCH_NORMAL("ThinkPad T42"), + HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"), + HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"), + { .ident = NULL } + }; + + if (!dmi_check_system(hdaps_whitelist)) { + printk(KERN_WARNING "hdaps: supported laptop not found!\n"); + ret = -ENXIO; + goto out; + } + + if (!request_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS, "hdaps")) { + ret = -ENXIO; + goto out; + } + + ret = driver_register(&hdaps_driver); + if (ret) + goto out_region; + + pdev = platform_device_register_simple("hdaps", -1, NULL, 0); + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); + goto out_driver; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group); + if (ret) + goto out_device; + + if (hdaps_mousedev) + hdaps_mousedev_enable(); + + printk(KERN_INFO "hdaps: driver successfully loaded.\n"); + return 0; + +out_device: + platform_device_unregister(pdev); +out_driver: + driver_unregister(&hdaps_driver); +out_region: + release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS); +out: + printk(KERN_WARNING "hdaps: driver init failed (ret=%d)!\n", ret); + return ret; +} + +static void __exit hdaps_exit(void) +{ + hdaps_mousedev_disable(); + + sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); + platform_device_unregister(pdev); + driver_unregister(&hdaps_driver); + release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS); + + printk(KERN_INFO "hdaps: driver unloaded.\n"); +} + +module_init(hdaps_init); +module_exit(hdaps_exit); + +module_param_named(mousedev, hdaps_mousedev, bool, 0); +MODULE_PARM_DESC(mousedev, "enable the input class device"); + +module_param_named(invert, hdaps_invert, bool, 0); +MODULE_PARM_DESC(invert, "invert data along each axis"); + +MODULE_AUTHOR("Robert Love"); +MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c index 8610bce0824..21aa9a41f62 100644 --- a/drivers/hwmon/sis5595.c +++ b/drivers/hwmon/sis5595.c @@ -758,11 +758,6 @@ static int __devinit sis5595_pci_probe(struct pci_dev *dev, return -ENODEV; } - if (!address) { - dev_err(&dev->dev,"No SiS 5595 sensors found.\n"); - return -ENODEV; - } - s_bridge = pci_dev_get(dev); if (i2c_isa_add_driver(&sis5595_driver)) { pci_dev_put(s_bridge); diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index 7e699a8ede2..c9cc683eba4 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -2,8 +2,8 @@ smsc47m1.c - Part of lm_sensors, Linux kernel modules for hardware monitoring - Supports the SMSC LPC47B27x, LPC47M10x, LPC47M13x and LPC47M14x - Super-I/O chips. + Supports the SMSC LPC47B27x, LPC47M10x, LPC47M13x, LPC47M14x, + LPC47M15x and LPC47M192 Super-I/O chips. Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> Copyright (C) 2004 Jean Delvare <khali@linux-fr.org> diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c index eb84997627c..05ddc88e7dd 100644 --- a/drivers/hwmon/via686a.c +++ b/drivers/hwmon/via686a.c @@ -812,11 +812,6 @@ static int __devinit via686a_pci_probe(struct pci_dev *dev, return -ENODEV; } - if (!address) { - dev_err(&dev->dev, "No Via 686A sensors found.\n"); - return -ENODEV; - } - s_bridge = pci_dev_get(dev); if (i2c_isa_add_driver(&via686a_driver)) { pci_dev_put(s_bridge); diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 02bd5c0239a..3479dc5208e 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -64,6 +64,10 @@ static unsigned short address; /* Insmod parameters */ enum chips { any_chip, w83627hf, w83627thf, w83697hf, w83637hf }; +static int reset; +module_param(reset, bool, 0); +MODULE_PARM_DESC(reset, "Set to one to reset chip on load"); + static int init = 1; module_param(init, bool, 0); MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); @@ -1279,7 +1283,15 @@ static void w83627hf_init_client(struct i2c_client *client) int type = data->type; u8 tmp; - if(init) { + if (reset) { + /* Resetting the chip has been the default for a long time, + but repeatedly caused problems (fans going to full + speed...) so it is now optional. It might even go away if + nobody reports it as being useful, as I see very little + reason why this would be needed at all. */ + dev_info(&client->dev, "If reset=1 solved a problem you were " + "having, please report!\n"); + /* save this register */ i = w83627hf_read_value(client, W83781D_REG_BEEP_CONFIG); /* Reset all except Watchdog values and last conversion values diff --git a/drivers/i2c/busses/i2c-keywest.c b/drivers/i2c/busses/i2c-keywest.c index 37b49c2daf5..eff5896ce86 100644 --- a/drivers/i2c/busses/i2c-keywest.c +++ b/drivers/i2c/busses/i2c-keywest.c @@ -611,7 +611,6 @@ create_iface(struct device_node *np, struct device *dev) for (i=0; i<nchan; i++) { struct keywest_chan* chan = &iface->channels[i]; - u8 addr; sprintf(chan->adapter.name, "%s %d", np->parent->name, i); chan->iface = iface; diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index e0b7a913431..fe9c0f42a2b 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -98,11 +98,6 @@ struct nforce2_smbus { #define NVIDIA_SMB_PRTCL_PEC 0x80 -/* Other settings */ -#define MAX_TIMEOUT 256 - - - static s32 nforce2_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index b443b04a4c5..0b0aa4f5162 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -601,44 +601,15 @@ EXPORT_SYMBOL(ide_wait_stat); */ u8 eighty_ninty_three (ide_drive_t *drive) { -#if 0 - if (!HWIF(drive)->udma_four) + if(HWIF(drive)->udma_four == 0) + return 0; + if (!(drive->id->hw_config & 0x6000)) return 0; - - if (drive->id->major_rev_num) { - int hssbd = 0; - int i; - /* - * Determine highest Supported SPEC - */ - for (i=1; i<=15; i++) - if (drive->id->major_rev_num & (1<<i)) - hssbd++; - - switch (hssbd) { - case 7: - case 6: - case 5: - /* ATA-4 and older do not support above Ultra 33 */ - default: - return 0; - } - } - - return ((u8) ( -#ifndef CONFIG_IDEDMA_IVB - (drive->id->hw_config & 0x4000) && -#endif /* CONFIG_IDEDMA_IVB */ - (drive->id->hw_config & 0x6000)) ? 1 : 0); - -#else - - return ((u8) ((HWIF(drive)->udma_four) && #ifndef CONFIG_IDEDMA_IVB - (drive->id->hw_config & 0x4000) && + if(!(drive->id->hw_config & 0x4000)) + return 0; #endif /* CONFIG_IDEDMA_IVB */ - (drive->id->hw_config & 0x6000)) ? 1 : 0); -#endif + return 1; } EXPORT_SYMBOL(eighty_ninty_three); diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index 3de9ab897e4..3d9c7afc869 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -608,7 +608,7 @@ static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev, const cha #ifdef __i386__ if (dev->resource[PCI_ROM_RESOURCE].start) { - pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); + pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); printk(KERN_INFO "%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start); } #endif diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c index bbde4627998..be334da7a75 100644 --- a/drivers/ide/pci/hpt34x.c +++ b/drivers/ide/pci/hpt34x.c @@ -173,7 +173,7 @@ static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev, const cha if (cmd & PCI_COMMAND_MEMORY) { if (pci_resource_start(dev, PCI_ROM_RESOURCE)) { - pci_write_config_byte(dev, PCI_ROM_ADDRESS, + pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); printk(KERN_INFO "HPT345: ROM enabled at 0x%08lx\n", dev->resource[PCI_ROM_RESOURCE].start); diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index 627af507643..de88218ef7c 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -790,7 +790,7 @@ static void sbp2_host_reset(struct hpsb_host *host) static int sbp2_start_device(struct scsi_id_instance_data *scsi_id) { struct sbp2scsi_host_info *hi = scsi_id->hi; - struct scsi_device *sdev; + int error; SBP2_DEBUG("sbp2_start_device"); @@ -939,10 +939,10 @@ alloc_fail: sbp2_max_speed_and_size(scsi_id); /* Add this device to the scsi layer now */ - sdev = scsi_add_device(scsi_id->scsi_host, 0, scsi_id->ud->id, 0); - if (IS_ERR(sdev)) { + error = scsi_add_device(scsi_id->scsi_host, 0, scsi_id->ud->id, 0); + if (error) { SBP2_ERR("scsi_add_device failed"); - return PTR_ERR(sdev); + return error; } return 0; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index e55dee39077..444f7756fee 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -132,6 +132,17 @@ config KEYBOARD_CORGI To compile this driver as a module, choose M here: the module will be called corgikbd. +config KEYBOARD_SPITZ + tristate "Spitz keyboard" + depends on PXA_SHARPSL + default y + help + Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000, + SL-C3000 and Sl-C3100 series of PDAs. + + To compile this driver as a module, choose M here: the + module will be called spitzkbd. + config KEYBOARD_MAPLE tristate "Maple bus keyboard" depends on SH_DREAMCAST && MAPLE diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index b02eeceea3c..9ce0b87f2fa 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_98KBD) += 98kbd.o obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o +obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c new file mode 100644 index 00000000000..1714045a182 --- /dev/null +++ b/drivers/input/keyboard/spitzkbd.c @@ -0,0 +1,478 @@ +/* + * Keyboard driver for Sharp Spitz, Borzoi and Akita (SL-Cxx00 series) + * + * Copyright (c) 2005 Richard Purdie + * + * Based on corgikbd.c + * + * 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. + * + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <asm/irq.h> + +#include <asm/arch/spitz.h> +#include <asm/arch/hardware.h> +#include <asm/arch/pxa-regs.h> + +#define KB_ROWS 7 +#define KB_COLS 11 +#define KB_ROWMASK(r) (1 << (r)) +#define SCANCODE(r,c) (((r)<<4) + (c) + 1) +#define NR_SCANCODES ((KB_ROWS<<4) + 1) + +#define HINGE_SCAN_INTERVAL (150) /* ms */ + +#define SPITZ_KEY_CALENDER KEY_F1 +#define SPITZ_KEY_ADDRESS KEY_F2 +#define SPITZ_KEY_FN KEY_F3 +#define SPITZ_KEY_CANCEL KEY_F4 +#define SPITZ_KEY_EXOK KEY_F5 +#define SPITZ_KEY_EXCANCEL KEY_F6 +#define SPITZ_KEY_EXJOGDOWN KEY_F7 +#define SPITZ_KEY_EXJOGUP KEY_F8 +#define SPITZ_KEY_JAP1 KEY_LEFTALT +#define SPITZ_KEY_JAP2 KEY_RIGHTCTRL +#define SPITZ_KEY_SYNC KEY_F9 +#define SPITZ_KEY_MAIL KEY_F10 +#define SPITZ_KEY_OK KEY_F11 +#define SPITZ_KEY_MENU KEY_F12 + +static unsigned char spitzkbd_keycode[NR_SCANCODES] = { + 0, /* 0 */ + KEY_LEFTCTRL, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, SPITZ_KEY_EXOK, SPITZ_KEY_EXCANCEL, 0, 0, 0, 0, 0, /* 1-16 */ + 0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, SPITZ_KEY_EXJOGDOWN, SPITZ_KEY_EXJOGUP, 0, 0, 0, 0, 0, /* 17-32 */ + KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0, /* 33-48 */ + SPITZ_KEY_CALENDER, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, /* 49-64 */ + SPITZ_KEY_ADDRESS, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, /* 65-80 */ + SPITZ_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, SPITZ_KEY_FN, 0, 0, 0, 0, 0, /* 81-96 */ + KEY_SYSRQ, SPITZ_KEY_JAP1, SPITZ_KEY_JAP2, SPITZ_KEY_CANCEL, SPITZ_KEY_OK, SPITZ_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0 /* 97-112 */ +}; + +static int spitz_strobes[] = { + SPITZ_GPIO_KEY_STROBE0, + SPITZ_GPIO_KEY_STROBE1, + SPITZ_GPIO_KEY_STROBE2, + SPITZ_GPIO_KEY_STROBE3, + SPITZ_GPIO_KEY_STROBE4, + SPITZ_GPIO_KEY_STROBE5, + SPITZ_GPIO_KEY_STROBE6, + SPITZ_GPIO_KEY_STROBE7, + SPITZ_GPIO_KEY_STROBE8, + SPITZ_GPIO_KEY_STROBE9, + SPITZ_GPIO_KEY_STROBE10, +}; + +static int spitz_senses[] = { + SPITZ_GPIO_KEY_SENSE0, + SPITZ_GPIO_KEY_SENSE1, + SPITZ_GPIO_KEY_SENSE2, + SPITZ_GPIO_KEY_SENSE3, + SPITZ_GPIO_KEY_SENSE4, + SPITZ_GPIO_KEY_SENSE5, + SPITZ_GPIO_KEY_SENSE6, +}; + +struct spitzkbd { + unsigned char keycode[ARRAY_SIZE(spitzkbd_keycode)]; + struct input_dev input; + char phys[32]; + + spinlock_t lock; + struct timer_list timer; + struct timer_list htimer; + + unsigned int suspended; + unsigned long suspend_jiffies; +}; + +#define KB_DISCHARGE_DELAY 10 +#define KB_ACTIVATE_DELAY 10 + +/* Helper functions for reading the keyboard matrix + * Note: We should really be using pxa_gpio_mode to alter GPDR but it + * requires a function call per GPIO bit which is excessive + * when we need to access 11 bits at once, multiple times. + * These functions must be called within local_irq_save()/local_irq_restore() + * or similar. + */ +static inline void spitzkbd_discharge_all(void) +{ + /* STROBE All HiZ */ + GPCR0 = SPITZ_GPIO_G0_STROBE_BIT; + GPDR0 &= ~SPITZ_GPIO_G0_STROBE_BIT; + GPCR1 = SPITZ_GPIO_G1_STROBE_BIT; + GPDR1 &= ~SPITZ_GPIO_G1_STROBE_BIT; + GPCR2 = SPITZ_GPIO_G2_STROBE_BIT; + GPDR2 &= ~SPITZ_GPIO_G2_STROBE_BIT; + GPCR3 = SPITZ_GPIO_G3_STROBE_BIT; + GPDR3 &= ~SPITZ_GPIO_G3_STROBE_BIT; +} + +static inline void spitzkbd_activate_all(void) +{ + /* STROBE ALL -> High */ + GPSR0 = SPITZ_GPIO_G0_STROBE_BIT; + GPDR0 |= SPITZ_GPIO_G0_STROBE_BIT; + GPSR1 = SPITZ_GPIO_G1_STROBE_BIT; + GPDR1 |= SPITZ_GPIO_G1_STROBE_BIT; + GPSR2 = SPITZ_GPIO_G2_STROBE_BIT; + GPDR2 |= SPITZ_GPIO_G2_STROBE_BIT; + GPSR3 = SPITZ_GPIO_G3_STROBE_BIT; + GPDR3 |= SPITZ_GPIO_G3_STROBE_BIT; + + udelay(KB_DISCHARGE_DELAY); + + /* Clear any interrupts we may have triggered when altering the GPIO lines */ + GEDR0 = SPITZ_GPIO_G0_SENSE_BIT; + GEDR1 = SPITZ_GPIO_G1_SENSE_BIT; + GEDR2 = SPITZ_GPIO_G2_SENSE_BIT; + GEDR3 = SPITZ_GPIO_G3_SENSE_BIT; +} + +static inline void spitzkbd_activate_col(int col) +{ + int gpio = spitz_strobes[col]; + GPDR0 &= ~SPITZ_GPIO_G0_STROBE_BIT; + GPDR1 &= ~SPITZ_GPIO_G1_STROBE_BIT; + GPDR2 &= ~SPITZ_GPIO_G2_STROBE_BIT; + GPDR3 &= ~SPITZ_GPIO_G3_STROBE_BIT; + GPSR(gpio) = GPIO_bit(gpio); + GPDR(gpio) |= GPIO_bit(gpio); +} + +static inline void spitzkbd_reset_col(int col) +{ + int gpio = spitz_strobes[col]; + GPDR0 &= ~SPITZ_GPIO_G0_STROBE_BIT; + GPDR1 &= ~SPITZ_GPIO_G1_STROBE_BIT; + GPDR2 &= ~SPITZ_GPIO_G2_STROBE_BIT; + GPDR3 &= ~SPITZ_GPIO_G3_STROBE_BIT; + GPCR(gpio) = GPIO_bit(gpio); + GPDR(gpio) |= GPIO_bit(gpio); +} + +static inline int spitzkbd_get_row_status(int col) +{ + return ((GPLR0 >> 12) & 0x01) | ((GPLR0 >> 16) & 0x02) + | ((GPLR2 >> 25) & 0x04) | ((GPLR1 << 1) & 0x08) + | ((GPLR1 >> 0) & 0x10) | ((GPLR1 >> 1) & 0x60); +} + +/* + * The spitz keyboard only generates interrupts when a key is pressed. + * When a key is pressed, we enable a timer which then scans the + * keyboard to detect when the key is released. + */ + +/* Scan the hardware keyboard and push any changes up through the input layer */ +static void spitzkbd_scankeyboard(struct spitzkbd *spitzkbd_data, struct pt_regs *regs) +{ + unsigned int row, col, rowd; + unsigned long flags; + unsigned int num_pressed, pwrkey = ((GPLR(SPITZ_GPIO_ON_KEY) & GPIO_bit(SPITZ_GPIO_ON_KEY)) != 0); + + if (spitzkbd_data->suspended) + return; + + spin_lock_irqsave(&spitzkbd_data->lock, flags); + + if (regs) + input_regs(&spitzkbd_data->input, regs); + + num_pressed = 0; + for (col = 0; col < KB_COLS; col++) { + /* + * Discharge the output driver capacitatance + * in the keyboard matrix. (Yes it is significant..) + */ + + spitzkbd_discharge_all(); + udelay(KB_DISCHARGE_DELAY); + + spitzkbd_activate_col(col); + udelay(KB_ACTIVATE_DELAY); + + rowd = spitzkbd_get_row_status(col); + for (row = 0; row < KB_ROWS; row++) { + unsigned int scancode, pressed; + + scancode = SCANCODE(row, col); + pressed = rowd & KB_ROWMASK(row); + + input_report_key(&spitzkbd_data->input, spitzkbd_data->keycode[scancode], pressed); + + if (pressed) + num_pressed++; + } + spitzkbd_reset_col(col); + } + + spitzkbd_activate_all(); + + input_report_key(&spitzkbd_data->input, SPITZ_KEY_SYNC, (GPLR(SPITZ_GPIO_SYNC) & GPIO_bit(SPITZ_GPIO_SYNC)) != 0 ); + input_report_key(&spitzkbd_data->input, KEY_SUSPEND, pwrkey); + + if (pwrkey && time_after(jiffies, spitzkbd_data->suspend_jiffies + msecs_to_jiffies(1000))) { + input_event(&spitzkbd_data->input, EV_PWR, KEY_SUSPEND, 1); + spitzkbd_data->suspend_jiffies = jiffies; + } + + input_sync(&spitzkbd_data->input); + + /* if any keys are pressed, enable the timer */ + if (num_pressed) + mod_timer(&spitzkbd_data->timer, jiffies + msecs_to_jiffies(100)); + + spin_unlock_irqrestore(&spitzkbd_data->lock, flags); +} + +/* + * spitz keyboard interrupt handler. + */ +static irqreturn_t spitzkbd_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct spitzkbd *spitzkbd_data = dev_id; + + if (!timer_pending(&spitzkbd_data->timer)) { + /** wait chattering delay **/ + udelay(20); + spitzkbd_scankeyboard(spitzkbd_data, regs); + } + + return IRQ_HANDLED; +} + +/* + * spitz timer checking for released keys + */ +static void spitzkbd_timer_callback(unsigned long data) +{ + struct spitzkbd *spitzkbd_data = (struct spitzkbd *) data; + spitzkbd_scankeyboard(spitzkbd_data, NULL); +} + +/* + * The hinge switches generate an interrupt. + * We debounce the switches and pass them to the input system. + */ + +static irqreturn_t spitzkbd_hinge_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct spitzkbd *spitzkbd_data = dev_id; + + if (!timer_pending(&spitzkbd_data->htimer)) + mod_timer(&spitzkbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL)); + + return IRQ_HANDLED; +} + +#define HINGE_STABLE_COUNT 2 +static int sharpsl_hinge_state; +static int hinge_count; + +static void spitzkbd_hinge_timer(unsigned long data) +{ + struct spitzkbd *spitzkbd_data = (struct spitzkbd *) data; + unsigned long state; + unsigned long flags; + + state = GPLR(SPITZ_GPIO_SWA) & (GPIO_bit(SPITZ_GPIO_SWA)|GPIO_bit(SPITZ_GPIO_SWB)); + if (state != sharpsl_hinge_state) { + hinge_count = 0; + sharpsl_hinge_state = state; + } else if (hinge_count < HINGE_STABLE_COUNT) { + hinge_count++; + } + + if (hinge_count >= HINGE_STABLE_COUNT) { + spin_lock_irqsave(&spitzkbd_data->lock, flags); + + input_report_switch(&spitzkbd_data->input, SW_0, ((GPLR(SPITZ_GPIO_SWA) & GPIO_bit(SPITZ_GPIO_SWA)) != 0)); + input_report_switch(&spitzkbd_data->input, SW_1, ((GPLR(SPITZ_GPIO_SWB) & GPIO_bit(SPITZ_GPIO_SWB)) != 0)); + input_sync(&spitzkbd_data->input); + + spin_unlock_irqrestore(&spitzkbd_data->lock, flags); + } else { + mod_timer(&spitzkbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL)); + } +} + +#ifdef CONFIG_PM +static int spitzkbd_suspend(struct device *dev, pm_message_t state, uint32_t level) +{ + if (level == SUSPEND_POWER_DOWN) { + int i; + struct spitzkbd *spitzkbd = dev_get_drvdata(dev); + spitzkbd->suspended = 1; + + /* Set Strobe lines as inputs - *except* strobe line 0 leave this + enabled so we can detect a power button press for resume */ + for (i = 1; i < SPITZ_KEY_STROBE_NUM; i++) + pxa_gpio_mode(spitz_strobes[i] | GPIO_IN); + } + return 0; +} + +static int spitzkbd_resume(struct device *dev, uint32_t level) +{ + if (level == RESUME_POWER_ON) { + int i; + struct spitzkbd *spitzkbd = dev_get_drvdata(dev); + + for (i = 0; i < SPITZ_KEY_STROBE_NUM; i++) + pxa_gpio_mode(spitz_strobes[i] | GPIO_OUT | GPIO_DFLT_HIGH); + + /* Upon resume, ignore the suspend key for a short while */ + spitzkbd->suspend_jiffies = jiffies; + spitzkbd->suspended = 0; + } + return 0; +} +#else +#define spitzkbd_suspend NULL +#define spitzkbd_resume NULL +#endif + +static int __init spitzkbd_probe(struct device *dev) +{ + int i; + struct spitzkbd *spitzkbd; + + spitzkbd = kzalloc(sizeof(struct spitzkbd), GFP_KERNEL); + if (!spitzkbd) + return -ENOMEM; + + dev_set_drvdata(dev,spitzkbd); + strcpy(spitzkbd->phys, "spitzkbd/input0"); + + spin_lock_init(&spitzkbd->lock); + + /* Init Keyboard rescan timer */ + init_timer(&spitzkbd->timer); + spitzkbd->timer.function = spitzkbd_timer_callback; + spitzkbd->timer.data = (unsigned long) spitzkbd; + + /* Init Hinge Timer */ + init_timer(&spitzkbd->htimer); + spitzkbd->htimer.function = spitzkbd_hinge_timer; + spitzkbd->htimer.data = (unsigned long) spitzkbd; + + spitzkbd->suspend_jiffies=jiffies; + + init_input_dev(&spitzkbd->input); + spitzkbd->input.private = spitzkbd; + spitzkbd->input.name = "Spitz Keyboard"; + spitzkbd->input.dev = dev; + spitzkbd->input.phys = spitzkbd->phys; + spitzkbd->input.id.bustype = BUS_HOST; + spitzkbd->input.id.vendor = 0x0001; + spitzkbd->input.id.product = 0x0001; + spitzkbd->input.id.version = 0x0100; + spitzkbd->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR) | BIT(EV_SW); + spitzkbd->input.keycode = spitzkbd->keycode; + spitzkbd->input.keycodesize = sizeof(unsigned char); + spitzkbd->input.keycodemax = ARRAY_SIZE(spitzkbd_keycode); + + memcpy(spitzkbd->keycode, spitzkbd_keycode, sizeof(spitzkbd->keycode)); + for (i = 0; i < ARRAY_SIZE(spitzkbd_keycode); i++) + set_bit(spitzkbd->keycode[i], spitzkbd->input.keybit); + clear_bit(0, spitzkbd->input.keybit); + set_bit(SW_0, spitzkbd->input.swbit); + set_bit(SW_1, spitzkbd->input.swbit); + + input_register_device(&spitzkbd->input); + mod_timer(&spitzkbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL)); + + /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */ + for (i = 0; i < SPITZ_KEY_SENSE_NUM; i++) { + pxa_gpio_mode(spitz_senses[i] | GPIO_IN); + if (request_irq(IRQ_GPIO(spitz_senses[i]), spitzkbd_interrupt, + SA_INTERRUPT, "Spitzkbd Sense", spitzkbd)) + printk(KERN_WARNING "spitzkbd: Can't get Sense IRQ: %d!\n", i); + else + set_irq_type(IRQ_GPIO(spitz_senses[i]),IRQT_RISING); + } + + /* Set Strobe lines as outputs - set high */ + for (i = 0; i < SPITZ_KEY_STROBE_NUM; i++) + pxa_gpio_mode(spitz_strobes[i] | GPIO_OUT | GPIO_DFLT_HIGH); + + pxa_gpio_mode(SPITZ_GPIO_SYNC | GPIO_IN); + pxa_gpio_mode(SPITZ_GPIO_ON_KEY | GPIO_IN); + pxa_gpio_mode(SPITZ_GPIO_SWA | GPIO_IN); + pxa_gpio_mode(SPITZ_GPIO_SWB | GPIO_IN); + + request_irq(SPITZ_IRQ_GPIO_SYNC, spitzkbd_interrupt, SA_INTERRUPT, "Spitzkbd Sync", spitzkbd); + request_irq(SPITZ_IRQ_GPIO_ON_KEY, spitzkbd_interrupt, SA_INTERRUPT, "Spitzkbd PwrOn", spitzkbd); + request_irq(SPITZ_IRQ_GPIO_SWA, spitzkbd_hinge_isr, SA_INTERRUPT, "Spitzkbd SWA", spitzkbd); + request_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd_hinge_isr, SA_INTERRUPT, "Spitzkbd SWB", spitzkbd); + + set_irq_type(SPITZ_IRQ_GPIO_SYNC, IRQT_BOTHEDGE); + set_irq_type(SPITZ_IRQ_GPIO_ON_KEY, IRQT_BOTHEDGE); + set_irq_type(SPITZ_IRQ_GPIO_SWA, IRQT_BOTHEDGE); + set_irq_type(SPITZ_IRQ_GPIO_SWB, IRQT_BOTHEDGE); + + printk(KERN_INFO "input: Spitz Keyboard Registered\n"); + + return 0; +} + +static int spitzkbd_remove(struct device *dev) +{ + int i; + struct spitzkbd *spitzkbd = dev_get_drvdata(dev); + + for (i = 0; i < SPITZ_KEY_SENSE_NUM; i++) + free_irq(IRQ_GPIO(spitz_senses[i]), spitzkbd); + + free_irq(SPITZ_IRQ_GPIO_SYNC, spitzkbd); + free_irq(SPITZ_IRQ_GPIO_ON_KEY, spitzkbd); + free_irq(SPITZ_IRQ_GPIO_SWA, spitzkbd); + free_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd); + + del_timer_sync(&spitzkbd->htimer); + del_timer_sync(&spitzkbd->timer); + + input_unregister_device(&spitzkbd->input); + + kfree(spitzkbd); + + return 0; +} + +static struct device_driver spitzkbd_driver = { + .name = "spitz-keyboard", + .bus = &platform_bus_type, + .probe = spitzkbd_probe, + .remove = spitzkbd_remove, + .suspend = spitzkbd_suspend, + .resume = spitzkbd_resume, +}; + +static int __devinit spitzkbd_init(void) +{ + return driver_register(&spitzkbd_driver); +} + +static void __exit spitzkbd_exit(void) +{ + driver_unregister(&spitzkbd_driver); +} + +module_init(spitzkbd_init); +module_exit(spitzkbd_exit); + +MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); +MODULE_DESCRIPTION("Spitz Keyboard Driver"); +MODULE_LICENSE("GPLv2"); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 0489af5a80c..21d55ed4b88 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -24,17 +24,17 @@ config TOUCHSCREEN_BITSY module will be called h3600_ts_input. config TOUCHSCREEN_CORGI - tristate "Corgi touchscreen (for Sharp SL-C7xx)" + tristate "SharpSL (Corgi and Spitz series) touchscreen driver" depends on PXA_SHARPSL default y help Say Y here to enable the driver for the touchscreen on the - Sharp SL-C7xx series of PDAs. + Sharp SL-C7xx and SL-Cxx00 series of PDAs. If unsure, say N. To compile this driver as a module, choose M here: the - module will be called ads7846_ts. + module will be called corgi_ts. config TOUCHSCREEN_GUNZE tristate "Gunze AHL-51S touchscreen" diff --git a/drivers/input/touchscreen/corgi_ts.c b/drivers/input/touchscreen/corgi_ts.c index 5d19261b884..4c7fbe55036 100644 --- a/drivers/input/touchscreen/corgi_ts.c +++ b/drivers/input/touchscreen/corgi_ts.c @@ -1,5 +1,5 @@ /* - * Touchscreen driver for Sharp Corgi models (SL-C7xx) + * Touchscreen driver for Sharp SL-C7xx and SL-Cxx00 models * * Copyright (c) 2004-2005 Richard Purdie * @@ -19,7 +19,7 @@ #include <linux/slab.h> #include <asm/irq.h> -#include <asm/arch/corgi.h> +#include <asm/arch/sharpsl.h> #include <asm/arch/hardware.h> #include <asm/arch/pxa-regs.h> @@ -47,15 +47,20 @@ struct corgi_ts { struct ts_event tc; int pendown; int power_mode; + int irq_gpio; + struct corgits_machinfo *machinfo; }; -#define STATUS_HSYNC (GPLR(CORGI_GPIO_HSYNC) & GPIO_bit(CORGI_GPIO_HSYNC)) - -#define SyncHS() while((STATUS_HSYNC) == 0); while((STATUS_HSYNC) != 0); +#ifdef CONFIG_PXA25x #define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C0, 0" : "=r"(a)) #define PMNC_GET(x) asm volatile ("mrc p14, 0, %0, C0, C0, 0" : "=r"(x)) #define PMNC_SET(x) asm volatile ("mcr p14, 0, %0, C0, C0, 0" : : "r"(x)) - +#endif +#ifdef CONFIG_PXA27x +#define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C1, 0" : "=r"(a)) +#define PMNC_GET(x) asm volatile ("mrc p14, 0, %0, C0, C1, 0" : "=r"(x)) +#define PMNC_SET(x) asm volatile ("mcr p14, 0, %0, C0, C1, 0" : : "r"(x)) +#endif /* ADS7846 Touch Screen Controller bit definitions */ #define ADSCTRL_PD0 (1u << 0) /* PD0 */ @@ -66,12 +71,11 @@ struct corgi_ts { #define ADSCTRL_STS (1u << 7) /* Start Bit */ /* External Functions */ -extern unsigned long w100fb_get_hsynclen(struct device *dev); extern unsigned int get_clk_frequency_khz(int info); -static unsigned long calc_waittime(void) +static unsigned long calc_waittime(struct corgi_ts *corgi_ts) { - unsigned long hsync_len = w100fb_get_hsynclen(&corgifb_device.dev); + unsigned long hsync_len = corgi_ts->machinfo->get_hsync_len(); if (hsync_len) return get_clk_frequency_khz(0)*1000/hsync_len; @@ -79,7 +83,8 @@ static unsigned long calc_waittime(void) return 0; } -static int sync_receive_data_send_cmd(int doRecive, int doSend, unsigned int address, unsigned long wait_time) +static int sync_receive_data_send_cmd(struct corgi_ts *corgi_ts, int doRecive, int doSend, + unsigned int address, unsigned long wait_time) { unsigned long timer1 = 0, timer2, pmnc = 0; int pos = 0; @@ -90,7 +95,7 @@ static int sync_receive_data_send_cmd(int doRecive, int doSend, unsigned int add PMNC_SET(0x01); /* polling HSync */ - SyncHS(); + corgi_ts->machinfo->wait_hsync(); /* get CCNT */ CCNT(timer1); } @@ -109,7 +114,7 @@ static int sync_receive_data_send_cmd(int doRecive, int doSend, unsigned int add CCNT(timer2); if (timer2-timer1 > wait_time) { /* too slow - timeout, try again */ - SyncHS(); + corgi_ts->machinfo->wait_hsync(); /* get OSCR */ CCNT(timer1); /* Wait after HSync */ @@ -133,23 +138,23 @@ static int read_xydata(struct corgi_ts *corgi_ts) /* critical section */ local_irq_save(flags); corgi_ssp_ads7846_lock(); - wait_time=calc_waittime(); + wait_time = calc_waittime(corgi_ts); /* Y-axis */ - sync_receive_data_send_cmd(0, 1, 1u, wait_time); + sync_receive_data_send_cmd(corgi_ts, 0, 1, 1u, wait_time); /* Y-axis */ - sync_receive_data_send_cmd(1, 1, 1u, wait_time); + sync_receive_data_send_cmd(corgi_ts, 1, 1, 1u, wait_time); /* X-axis */ - y = sync_receive_data_send_cmd(1, 1, 5u, wait_time); + y = sync_receive_data_send_cmd(corgi_ts, 1, 1, 5u, wait_time); /* Z1 */ - x = sync_receive_data_send_cmd(1, 1, 3u, wait_time); + x = sync_receive_data_send_cmd(corgi_ts, 1, 1, 3u, wait_time); /* Z2 */ - z1 = sync_receive_data_send_cmd(1, 1, 4u, wait_time); - z2 = sync_receive_data_send_cmd(1, 0, 4u, wait_time); + z1 = sync_receive_data_send_cmd(corgi_ts, 1, 1, 4u, wait_time); + z2 = sync_receive_data_send_cmd(corgi_ts, 1, 0, 4u, wait_time); /* Power-Down Enable */ corgi_ssp_ads7846_put((1u << ADSCTRL_ADR_SH) | ADSCTRL_STS); @@ -189,9 +194,9 @@ static void new_data(struct corgi_ts *corgi_ts, struct pt_regs *regs) static void ts_interrupt_main(struct corgi_ts *corgi_ts, int isTimer, struct pt_regs *regs) { - if ((GPLR(CORGI_GPIO_TP_INT) & GPIO_bit(CORGI_GPIO_TP_INT)) == 0) { + if ((GPLR(IRQ_TO_GPIO(corgi_ts->irq_gpio)) & GPIO_bit(IRQ_TO_GPIO(corgi_ts->irq_gpio))) == 0) { /* Disable Interrupt */ - set_irq_type(CORGI_IRQ_GPIO_TP_INT, IRQT_NOEDGE); + set_irq_type(corgi_ts->irq_gpio, IRQT_NOEDGE); if (read_xydata(corgi_ts)) { corgi_ts->pendown = 1; new_data(corgi_ts, regs); @@ -210,7 +215,7 @@ static void ts_interrupt_main(struct corgi_ts *corgi_ts, int isTimer, struct pt_ } /* Enable Falling Edge */ - set_irq_type(CORGI_IRQ_GPIO_TP_INT, IRQT_FALLING); + set_irq_type(corgi_ts->irq_gpio, IRQT_FALLING); corgi_ts->pendown = 0; } } @@ -254,7 +259,7 @@ static int corgits_resume(struct device *dev, uint32_t level) corgi_ssp_ads7846_putget((4u << ADSCTRL_ADR_SH) | ADSCTRL_STS); /* Enable Falling Edge */ - set_irq_type(CORGI_IRQ_GPIO_TP_INT, IRQT_FALLING); + set_irq_type(corgi_ts->irq_gpio, IRQT_FALLING); corgi_ts->power_mode = PWR_MODE_ACTIVE; } return 0; @@ -267,6 +272,7 @@ static int corgits_resume(struct device *dev, uint32_t level) static int __init corgits_probe(struct device *dev) { struct corgi_ts *corgi_ts; + struct platform_device *pdev = to_platform_device(dev); if (!(corgi_ts = kmalloc(sizeof(struct corgi_ts), GFP_KERNEL))) return -ENOMEM; @@ -275,6 +281,14 @@ static int __init corgits_probe(struct device *dev) memset(corgi_ts, 0, sizeof(struct corgi_ts)); + corgi_ts->machinfo = dev->platform_data; + corgi_ts->irq_gpio = platform_get_irq(pdev, 0); + + if (corgi_ts->irq_gpio < 0) { + kfree(corgi_ts); + return -ENODEV; + } + init_input_dev(&corgi_ts->input); corgi_ts->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); corgi_ts->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); @@ -293,8 +307,7 @@ static int __init corgits_probe(struct device *dev) corgi_ts->input.id.product = 0x0002; corgi_ts->input.id.version = 0x0100; - pxa_gpio_mode(CORGI_GPIO_TP_INT | GPIO_IN); - pxa_gpio_mode(CORGI_GPIO_HSYNC | GPIO_IN); + pxa_gpio_mode(IRQ_TO_GPIO(corgi_ts->irq_gpio) | GPIO_IN); /* Initiaize ADS7846 Difference Reference mode */ corgi_ssp_ads7846_putget((1u << ADSCTRL_ADR_SH) | ADSCTRL_STS); @@ -313,14 +326,14 @@ static int __init corgits_probe(struct device *dev) input_register_device(&corgi_ts->input); corgi_ts->power_mode = PWR_MODE_ACTIVE; - if (request_irq(CORGI_IRQ_GPIO_TP_INT, ts_interrupt, SA_INTERRUPT, "ts", corgi_ts)) { + if (request_irq(corgi_ts->irq_gpio, ts_interrupt, SA_INTERRUPT, "ts", corgi_ts)) { input_unregister_device(&corgi_ts->input); kfree(corgi_ts); return -EBUSY; } /* Enable Falling Edge */ - set_irq_type(CORGI_IRQ_GPIO_TP_INT, IRQT_FALLING); + set_irq_type(corgi_ts->irq_gpio, IRQT_FALLING); printk(KERN_INFO "input: Corgi Touchscreen Registered\n"); @@ -331,8 +344,9 @@ static int corgits_remove(struct device *dev) { struct corgi_ts *corgi_ts = dev_get_drvdata(dev); - free_irq(CORGI_IRQ_GPIO_TP_INT, NULL); + free_irq(corgi_ts->irq_gpio, NULL); del_timer_sync(&corgi_ts->timer); + corgi_ts->machinfo->put_hsync(); input_unregister_device(&corgi_ts->input); kfree(corgi_ts); return 0; diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c index 40b0df04ed9..1ebed041672 100644 --- a/drivers/isdn/sc/init.c +++ b/drivers/isdn/sc/init.c @@ -87,7 +87,7 @@ static int __init sc_init(void) */ for (i = 0 ; i < MAX_IO_REGS - 1 ; i++) { if(!request_region(io[b] + i * 0x400, 1, "sc test")) { - pr_debug("check_region for 0x%x failed\n", io[b] + i * 0x400); + pr_debug("request_region for 0x%x failed\n", io[b] + i * 0x400); io[b] = 0; break; } else @@ -181,7 +181,7 @@ static int __init sc_init(void) for (i = SRAM_MIN ; i < SRAM_MAX ; i += SRAM_PAGESIZE) { pr_debug("Checking RAM address 0x%x...\n", i); if(request_region(i, SRAM_PAGESIZE, "sc test")) { - pr_debug(" check_region succeeded\n"); + pr_debug(" request_region succeeded\n"); model = identify_board(i, io[b]); release_region(i, SRAM_PAGESIZE); if (model >= 0) { diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c index 8b4ad70dd1b..877c770558e 100644 --- a/drivers/media/radio/radio-aimslab.c +++ b/drivers/media/radio/radio-aimslab.c @@ -29,7 +29,7 @@ #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ -#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/ioport.h> /* request_region */ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c index 013c835ed91..5319a9c9a97 100644 --- a/drivers/media/radio/radio-aztech.c +++ b/drivers/media/radio/radio-aztech.c @@ -26,7 +26,7 @@ #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ -#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/ioport.h> /* request_region */ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c index 53d399b6652..022913da8c5 100644 --- a/drivers/media/radio/radio-cadet.c +++ b/drivers/media/radio/radio-cadet.c @@ -29,7 +29,7 @@ #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ -#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/ioport.h> /* request_region */ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 202bfe6819b..6418f03b9ce 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -17,7 +17,7 @@ #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ -#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/ioport.h> /* request_region */ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c index c00245d4d24..b2256d675b4 100644 --- a/drivers/media/radio/radio-rtrack2.c +++ b/drivers/media/radio/radio-rtrack2.c @@ -10,7 +10,7 @@ #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ -#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/ioport.h> /* request_region */ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index 3a464a09221..6f03ce4dd7b 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -18,7 +18,7 @@ #include <linux/kernel.h> /* __setup */ #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ -#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/ioport.h> /* request_region */ #include <linux/delay.h> /* udelay */ #include <linux/videodev.h> /* kernel radio structs */ #include <linux/isapnp.h> diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index 0732efda6a9..71971e9bb34 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -14,7 +14,7 @@ #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ -#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/ioport.h> /* request_region */ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c index 248d67fde03..b03573c6840 100644 --- a/drivers/media/radio/radio-terratec.c +++ b/drivers/media/radio/radio-terratec.c @@ -25,7 +25,7 @@ #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ -#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/ioport.h> /* request_region */ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c index d7da901ebe9..f304f3c1476 100644 --- a/drivers/media/radio/radio-typhoon.c +++ b/drivers/media/radio/radio-typhoon.c @@ -31,7 +31,7 @@ #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ -#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/ioport.h> /* request_region */ #include <linux/proc_fs.h> /* radio card status report */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c index 342f92df4ab..4c6d6fb4903 100644 --- a/drivers/media/radio/radio-zoltrix.c +++ b/drivers/media/radio/radio-zoltrix.c @@ -28,7 +28,7 @@ #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ -#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/ioport.h> /* request_region */ #include <linux/delay.h> /* udelay, msleep */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index c9106b1d79d..4334744652d 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -221,9 +221,7 @@ static int lgdt330x_pll_set(struct dvb_frontend* fe, int err; /* Put the analog decoder in standby to keep it quiet */ - if (core->tda9887_conf) { - cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); - } + cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); dvb_pll_configure(core->pll_desc, buf, params->frequency, 0); dprintk(1, "%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n", @@ -402,6 +400,9 @@ static int dvb_register(struct cx8802_dev *dev) dev->dvb.frontend->ops->info.frequency_max = dev->core->pll_desc->max; } + /* Put the analog decoder in standby to keep it quiet */ + cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); + /* register everything */ return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev); } diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index e11e55dc892..3cbca7cbea8 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -93,7 +93,7 @@ static int dma = 2; static inline void wbsd_unlock_config(struct wbsd_host* host) { BUG_ON(host->config == 0); - + outb(host->unlock_code, host->config); outb(host->unlock_code, host->config); } @@ -101,14 +101,14 @@ static inline void wbsd_unlock_config(struct wbsd_host* host) static inline void wbsd_lock_config(struct wbsd_host* host) { BUG_ON(host->config == 0); - + outb(LOCK_CODE, host->config); } static inline void wbsd_write_config(struct wbsd_host* host, u8 reg, u8 value) { BUG_ON(host->config == 0); - + outb(reg, host->config); outb(value, host->config + 1); } @@ -116,7 +116,7 @@ static inline void wbsd_write_config(struct wbsd_host* host, u8 reg, u8 value) static inline u8 wbsd_read_config(struct wbsd_host* host, u8 reg) { BUG_ON(host->config == 0); - + outb(reg, host->config); return inb(host->config + 1); } @@ -140,21 +140,21 @@ static inline u8 wbsd_read_index(struct wbsd_host* host, u8 index) static void wbsd_init_device(struct wbsd_host* host) { u8 setup, ier; - + /* * Reset chip (SD/MMC part) and fifo. */ setup = wbsd_read_index(host, WBSD_IDX_SETUP); setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET; wbsd_write_index(host, WBSD_IDX_SETUP, setup); - + /* * Set DAT3 to input */ setup &= ~WBSD_DAT3_H; wbsd_write_index(host, WBSD_IDX_SETUP, setup); host->flags &= ~WBSD_FIGNORE_DETECT; - + /* * Read back default clock. */ @@ -164,12 +164,12 @@ static void wbsd_init_device(struct wbsd_host* host) * Power down port. */ outb(WBSD_POWER_N, host->base + WBSD_CSR); - + /* * Set maximum timeout. */ wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F); - + /* * Test for card presence */ @@ -177,7 +177,7 @@ static void wbsd_init_device(struct wbsd_host* host) host->flags |= WBSD_FCARD_PRESENT; else host->flags &= ~WBSD_FCARD_PRESENT; - + /* * Enable interesting interrupts. */ @@ -200,9 +200,9 @@ static void wbsd_init_device(struct wbsd_host* host) static void wbsd_reset(struct wbsd_host* host) { u8 setup; - + printk(KERN_ERR DRIVER_NAME ": Resetting chip\n"); - + /* * Soft reset of chip (SD/MMC part). */ @@ -214,9 +214,9 @@ static void wbsd_reset(struct wbsd_host* host) static void wbsd_request_end(struct wbsd_host* host, struct mmc_request* mrq) { unsigned long dmaflags; - + DBGF("Ending request, cmd (%x)\n", mrq->cmd->opcode); - + if (host->dma >= 0) { /* @@ -232,7 +232,7 @@ static void wbsd_request_end(struct wbsd_host* host, struct mmc_request* mrq) */ wbsd_write_index(host, WBSD_IDX_DMA, 0); } - + host->mrq = NULL; /* @@ -275,7 +275,7 @@ static inline int wbsd_next_sg(struct wbsd_host* host) host->offset = 0; host->remain = host->cur_sg->length; } - + return host->num_sg; } @@ -297,12 +297,12 @@ static inline void wbsd_sg_to_dma(struct wbsd_host* host, struct mmc_data* data) struct scatterlist* sg; char* dmabuf = host->dma_buffer; char* sgbuf; - + size = host->size; - + sg = data->sg; len = data->sg_len; - + /* * Just loop through all entries. Size might not * be the entire list though so make sure that @@ -317,23 +317,23 @@ static inline void wbsd_sg_to_dma(struct wbsd_host* host, struct mmc_data* data) memcpy(dmabuf, sgbuf, sg[i].length); kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ); dmabuf += sg[i].length; - + if (size < sg[i].length) size = 0; else size -= sg[i].length; - + if (size == 0) break; } - + /* * Check that we didn't get a request to transfer * more data than can fit into the SG list. */ - + BUG_ON(size != 0); - + host->size -= size; } @@ -343,12 +343,12 @@ static inline void wbsd_dma_to_sg(struct wbsd_host* host, struct mmc_data* data) struct scatterlist* sg; char* dmabuf = host->dma_buffer; char* sgbuf; - + size = host->size; - + sg = data->sg; len = data->sg_len; - + /* * Just loop through all entries. Size might not * be the entire list though so make sure that @@ -363,30 +363,30 @@ static inline void wbsd_dma_to_sg(struct wbsd_host* host, struct mmc_data* data) memcpy(sgbuf, dmabuf, sg[i].length); kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ); dmabuf += sg[i].length; - + if (size < sg[i].length) size = 0; else size -= sg[i].length; - + if (size == 0) break; } - + /* * Check that we didn't get a request to transfer * more data than can fit into the SG list. */ - + BUG_ON(size != 0); - + host->size -= size; } /* * Command handling */ - + static inline void wbsd_get_short_reply(struct wbsd_host* host, struct mmc_command* cmd) { @@ -398,7 +398,7 @@ static inline void wbsd_get_short_reply(struct wbsd_host* host, cmd->error = MMC_ERR_INVALID; return; } - + cmd->resp[0] = wbsd_read_index(host, WBSD_IDX_RESP12) << 24; cmd->resp[0] |= @@ -415,7 +415,7 @@ static inline void wbsd_get_long_reply(struct wbsd_host* host, struct mmc_command* cmd) { int i; - + /* * Correct response type? */ @@ -424,7 +424,7 @@ static inline void wbsd_get_long_reply(struct wbsd_host* host, cmd->error = MMC_ERR_INVALID; return; } - + for (i = 0;i < 4;i++) { cmd->resp[i] = @@ -442,7 +442,7 @@ static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd) { int i; u8 status, isr; - + DBGF("Sending cmd (%x)\n", cmd->opcode); /* @@ -451,16 +451,16 @@ static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd) * transfer. */ host->isr = 0; - + /* * Send the command (CRC calculated by host). */ outb(cmd->opcode, host->base + WBSD_CMDR); for (i = 3;i >= 0;i--) outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR); - + cmd->error = MMC_ERR_NONE; - + /* * Wait for the request to complete. */ @@ -477,7 +477,7 @@ static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd) * Read back status. */ isr = host->isr; - + /* Card removed? */ if (isr & WBSD_INT_CARD) cmd->error = MMC_ERR_TIMEOUT; @@ -509,13 +509,13 @@ static void wbsd_empty_fifo(struct wbsd_host* host) struct mmc_data* data = host->mrq->cmd->data; char* buffer; int i, fsr, fifo; - + /* * Handle excessive data. */ if (data->bytes_xfered == host->size) return; - + buffer = wbsd_kmap_sg(host) + host->offset; /* @@ -527,14 +527,14 @@ static void wbsd_empty_fifo(struct wbsd_host* host) /* * The size field in the FSR is broken so we have to * do some guessing. - */ + */ if (fsr & WBSD_FIFO_FULL) fifo = 16; else if (fsr & WBSD_FIFO_FUTHRE) fifo = 8; else fifo = 1; - + for (i = 0;i < fifo;i++) { *buffer = inb(host->base + WBSD_DFR); @@ -543,23 +543,23 @@ static void wbsd_empty_fifo(struct wbsd_host* host) host->remain--; data->bytes_xfered++; - + /* * Transfer done? */ if (data->bytes_xfered == host->size) { - wbsd_kunmap_sg(host); + wbsd_kunmap_sg(host); return; } - + /* * End of scatter list entry? */ if (host->remain == 0) { wbsd_kunmap_sg(host); - + /* * Get next entry. Check if last. */ @@ -572,17 +572,17 @@ static void wbsd_empty_fifo(struct wbsd_host* host) * into the scatter list. */ BUG_ON(1); - + host->size = data->bytes_xfered; - + return; } - + buffer = wbsd_kmap_sg(host); } } } - + wbsd_kunmap_sg(host); /* @@ -599,7 +599,7 @@ static void wbsd_fill_fifo(struct wbsd_host* host) struct mmc_data* data = host->mrq->cmd->data; char* buffer; int i, fsr, fifo; - + /* * Check that we aren't being called after the * entire buffer has been transfered. @@ -618,7 +618,7 @@ static void wbsd_fill_fifo(struct wbsd_host* host) /* * The size field in the FSR is broken so we have to * do some guessing. - */ + */ if (fsr & WBSD_FIFO_EMPTY) fifo = 0; else if (fsr & WBSD_FIFO_EMTHRE) @@ -632,9 +632,9 @@ static void wbsd_fill_fifo(struct wbsd_host* host) buffer++; host->offset++; host->remain--; - + data->bytes_xfered++; - + /* * Transfer done? */ @@ -650,7 +650,7 @@ static void wbsd_fill_fifo(struct wbsd_host* host) if (host->remain == 0) { wbsd_kunmap_sg(host); - + /* * Get next entry. Check if last. */ @@ -663,19 +663,19 @@ static void wbsd_fill_fifo(struct wbsd_host* host) * into the scatter list. */ BUG_ON(1); - + host->size = data->bytes_xfered; - + return; } - + buffer = wbsd_kmap_sg(host); } } } - + wbsd_kunmap_sg(host); - + /* * The controller stops sending interrupts for * 'FIFO empty' under certain conditions. So we @@ -694,7 +694,7 @@ static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data) 1 << data->blksz_bits, data->blocks, data->flags); DBGF("tsac %d ms nsac %d clk\n", data->timeout_ns / 1000000, data->timeout_clks); - + /* * Calculate size. */ @@ -708,12 +708,12 @@ static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data) wbsd_write_index(host, WBSD_IDX_TAAC, 127); else wbsd_write_index(host, WBSD_IDX_TAAC, data->timeout_ns/1000000); - + if (data->timeout_clks > 255) wbsd_write_index(host, WBSD_IDX_NSAC, 255); else wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks); - + /* * Inform the chip of how large blocks will be * sent. It needs this to determine when to @@ -732,7 +732,7 @@ static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data) else if (host->bus_width == MMC_BUS_WIDTH_4) { blksize = (1 << data->blksz_bits) + 2 * 4; - + wbsd_write_index(host, WBSD_IDX_PBSMSB, ((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH); wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); @@ -751,12 +751,12 @@ static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data) setup = wbsd_read_index(host, WBSD_IDX_SETUP); setup |= WBSD_FIFO_RESET; wbsd_write_index(host, WBSD_IDX_SETUP, setup); - + /* * DMA transfer? */ if (host->dma >= 0) - { + { /* * The buffer for DMA is only 64 kB. */ @@ -766,17 +766,17 @@ static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data) data->error = MMC_ERR_INVALID; return; } - + /* * Transfer data from the SG list to * the DMA buffer. */ if (data->flags & MMC_DATA_WRITE) wbsd_sg_to_dma(host, data); - + /* * Initialise the ISA DMA controller. - */ + */ dmaflags = claim_dma_lock(); disable_dma(host->dma); clear_dma_ff(host->dma); @@ -802,17 +802,17 @@ static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data) * output to a minimum. */ host->firsterr = 1; - + /* * Initialise the SG list. */ wbsd_init_sg(host, data); - + /* * Turn off DMA. */ wbsd_write_index(host, WBSD_IDX_DMA, 0); - + /* * Set up FIFO threshold levels (and fill * buffer if doing a write). @@ -828,8 +828,8 @@ static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data) WBSD_FIFOEN_EMPTY | 8); wbsd_fill_fifo(host); } - } - + } + data->error = MMC_ERR_NONE; } @@ -838,7 +838,7 @@ static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data) unsigned long dmaflags; int count; u8 status; - + WARN_ON(host->mrq == NULL); /* @@ -855,7 +855,7 @@ static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data) { status = wbsd_read_index(host, WBSD_IDX_STATUS); } while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE)); - + /* * DMA transfer? */ @@ -865,7 +865,7 @@ static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data) * Disable DMA on the host. */ wbsd_write_index(host, WBSD_IDX_DMA, 0); - + /* * Turn of ISA DMA controller. */ @@ -874,7 +874,7 @@ static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data) clear_dma_ff(host->dma); count = get_dma_residue(host->dma); release_dma_lock(dmaflags); - + /* * Any leftover data? */ @@ -882,7 +882,7 @@ static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data) { printk(KERN_ERR DRIVER_NAME ": Incomplete DMA " "transfer. %d bytes left.\n", count); - + data->error = MMC_ERR_FAILED; } else @@ -893,13 +893,13 @@ static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data) */ if (data->flags & MMC_DATA_READ) wbsd_dma_to_sg(host, data); - + data->bytes_xfered = host->size; } } - + DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered); - + wbsd_request_end(host, host->mrq); } @@ -924,7 +924,7 @@ static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq) cmd = mrq->cmd; host->mrq = mrq; - + /* * If there is no card in the slot then * timeout immediatly. @@ -941,18 +941,18 @@ static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq) if (cmd->data) { wbsd_prepare_data(host, cmd->data); - + if (cmd->data->error != MMC_ERR_NONE) goto done; } - + wbsd_send_command(host, cmd); /* * If this is a data transfer the request * will be finished after the data has * transfered. - */ + */ if (cmd->data && (cmd->error == MMC_ERR_NONE)) { /* @@ -965,7 +965,7 @@ static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq) return; } - + done: wbsd_request_end(host, mrq); @@ -976,7 +976,7 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) { struct wbsd_host* host = mmc_priv(mmc); u8 clk, setup, pwr; - + DBGF("clock %uHz busmode %u powermode %u cs %u Vdd %u width %u\n", ios->clock, ios->bus_mode, ios->power_mode, ios->chip_select, ios->vdd, ios->bus_width); @@ -989,7 +989,7 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) */ if (ios->power_mode == MMC_POWER_OFF) wbsd_init_device(host); - + if (ios->clock >= 24000000) clk = WBSD_CLK_24M; else if (ios->clock >= 16000000) @@ -1042,7 +1042,7 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) mod_timer(&host->ignore_timer, jiffies + HZ/100); } wbsd_write_index(host, WBSD_IDX_SETUP, setup); - + /* * Store bus width for later. Will be used when * setting up the data transfer. @@ -1128,7 +1128,7 @@ static inline struct mmc_data* wbsd_get_data(struct wbsd_host* host) WARN_ON(!host->mrq->cmd->data); if (!host->mrq->cmd->data) return NULL; - + return host->mrq->cmd->data; } @@ -1136,72 +1136,67 @@ static void wbsd_tasklet_card(unsigned long param) { struct wbsd_host* host = (struct wbsd_host*)param; u8 csr; - + int delay = -1; + spin_lock(&host->lock); - + if (host->flags & WBSD_FIGNORE_DETECT) { spin_unlock(&host->lock); return; } - + csr = inb(host->base + WBSD_CSR); WARN_ON(csr == 0xff); - + if (csr & WBSD_CARDPRESENT) { if (!(host->flags & WBSD_FCARD_PRESENT)) { DBG("Card inserted\n"); host->flags |= WBSD_FCARD_PRESENT; - - spin_unlock(&host->lock); - /* - * Delay card detection to allow electrical connections - * to stabilise. - */ - mmc_detect_change(host->mmc, msecs_to_jiffies(500)); + delay = 500; } - else - spin_unlock(&host->lock); } else if (host->flags & WBSD_FCARD_PRESENT) { DBG("Card removed\n"); host->flags &= ~WBSD_FCARD_PRESENT; - + if (host->mrq) { printk(KERN_ERR DRIVER_NAME ": Card removed during transfer!\n"); wbsd_reset(host); - + host->mrq->cmd->error = MMC_ERR_FAILED; tasklet_schedule(&host->finish_tasklet); } - - /* - * Unlock first since we might get a call back. - */ - spin_unlock(&host->lock); - mmc_detect_change(host->mmc, 0); + delay = 0; } - else - spin_unlock(&host->lock); + + /* + * Unlock first since we might get a call back. + */ + + spin_unlock(&host->lock); + + if (delay != -1) + mmc_detect_change(host->mmc, msecs_to_jiffies(delay)); } static void wbsd_tasklet_fifo(unsigned long param) { struct wbsd_host* host = (struct wbsd_host*)param; struct mmc_data* data; - + spin_lock(&host->lock); - + if (!host->mrq) goto end; - + data = wbsd_get_data(host); if (!data) goto end; @@ -1220,7 +1215,7 @@ static void wbsd_tasklet_fifo(unsigned long param) tasklet_schedule(&host->finish_tasklet); } -end: +end: spin_unlock(&host->lock); } @@ -1228,23 +1223,23 @@ static void wbsd_tasklet_crc(unsigned long param) { struct wbsd_host* host = (struct wbsd_host*)param; struct mmc_data* data; - + spin_lock(&host->lock); - + if (!host->mrq) goto end; - + data = wbsd_get_data(host); if (!data) goto end; - + DBGF("CRC error\n"); data->error = MMC_ERR_BADCRC; - + tasklet_schedule(&host->finish_tasklet); -end: +end: spin_unlock(&host->lock); } @@ -1252,23 +1247,23 @@ static void wbsd_tasklet_timeout(unsigned long param) { struct wbsd_host* host = (struct wbsd_host*)param; struct mmc_data* data; - + spin_lock(&host->lock); - + if (!host->mrq) goto end; - + data = wbsd_get_data(host); if (!data) goto end; - + DBGF("Timeout\n"); data->error = MMC_ERR_TIMEOUT; - + tasklet_schedule(&host->finish_tasklet); -end: +end: spin_unlock(&host->lock); } @@ -1276,20 +1271,20 @@ static void wbsd_tasklet_finish(unsigned long param) { struct wbsd_host* host = (struct wbsd_host*)param; struct mmc_data* data; - + spin_lock(&host->lock); - + WARN_ON(!host->mrq); if (!host->mrq) goto end; - + data = wbsd_get_data(host); if (!data) goto end; wbsd_finish_data(host, data); - -end: + +end: spin_unlock(&host->lock); } @@ -1297,7 +1292,7 @@ static void wbsd_tasklet_block(unsigned long param) { struct wbsd_host* host = (struct wbsd_host*)param; struct mmc_data* data; - + spin_lock(&host->lock); if ((wbsd_read_index(host, WBSD_IDX_CRCSTATUS) & WBSD_CRC_MASK) != @@ -1306,15 +1301,15 @@ static void wbsd_tasklet_block(unsigned long param) data = wbsd_get_data(host); if (!data) goto end; - + DBGF("CRC error\n"); data->error = MMC_ERR_BADCRC; - + tasklet_schedule(&host->finish_tasklet); } -end: +end: spin_unlock(&host->lock); } @@ -1326,7 +1321,7 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs) { struct wbsd_host* host = dev_id; int isr; - + isr = inb(host->base + WBSD_ISR); /* @@ -1334,7 +1329,7 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs) */ if (isr == 0xff || isr == 0x00) return IRQ_NONE; - + host->isr |= isr; /* @@ -1352,7 +1347,7 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs) tasklet_hi_schedule(&host->block_tasklet); if (isr & WBSD_INT_TC) tasklet_schedule(&host->finish_tasklet); - + return IRQ_HANDLED; } @@ -1370,14 +1365,14 @@ static int __devinit wbsd_alloc_mmc(struct device* dev) { struct mmc_host* mmc; struct wbsd_host* host; - + /* * Allocate MMC structure. */ mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev); if (!mmc) return -ENOMEM; - + host = mmc_priv(mmc); host->mmc = mmc; @@ -1391,37 +1386,37 @@ static int __devinit wbsd_alloc_mmc(struct device* dev) mmc->f_max = 24000000; mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; mmc->caps = MMC_CAP_4_BIT_DATA; - + spin_lock_init(&host->lock); - + /* * Set up timers */ init_timer(&host->ignore_timer); host->ignore_timer.data = (unsigned long)host; host->ignore_timer.function = wbsd_reset_ignore; - + /* * Maximum number of segments. Worst case is one sector per segment * so this will be 64kB/512. */ mmc->max_hw_segs = 128; mmc->max_phys_segs = 128; - + /* * Maximum number of sectors in one transfer. Also limited by 64kB * buffer. */ mmc->max_sectors = 128; - + /* * Maximum segment size. Could be one segment with the maximum number * of segments. */ mmc->max_seg_size = mmc->max_sectors * 512; - + dev_set_drvdata(dev, mmc); - + return 0; } @@ -1429,18 +1424,18 @@ static void __devexit wbsd_free_mmc(struct device* dev) { struct mmc_host* mmc; struct wbsd_host* host; - + mmc = dev_get_drvdata(dev); if (!mmc) return; - + host = mmc_priv(mmc); BUG_ON(host == NULL); - + del_timer_sync(&host->ignore_timer); - + mmc_free_host(mmc); - + dev_set_drvdata(dev, NULL); } @@ -1452,7 +1447,7 @@ static int __devinit wbsd_scan(struct wbsd_host* host) { int i, j, k; int id; - + /* * Iterate through all ports, all codes to * find hardware that is in our known list. @@ -1461,32 +1456,32 @@ static int __devinit wbsd_scan(struct wbsd_host* host) { if (!request_region(config_ports[i], 2, DRIVER_NAME)) continue; - + for (j = 0;j < sizeof(unlock_codes)/sizeof(int);j++) { id = 0xFFFF; - + outb(unlock_codes[j], config_ports[i]); outb(unlock_codes[j], config_ports[i]); - + outb(WBSD_CONF_ID_HI, config_ports[i]); id = inb(config_ports[i] + 1) << 8; outb(WBSD_CONF_ID_LO, config_ports[i]); id |= inb(config_ports[i] + 1); - + for (k = 0;k < sizeof(valid_ids)/sizeof(int);k++) { if (id == valid_ids[k]) - { + { host->chip_id = id; host->config = config_ports[i]; host->unlock_code = unlock_codes[i]; - + return 0; } } - + if (id != 0xFFFF) { DBG("Unknown hardware (id %x) found at %x\n", @@ -1495,10 +1490,10 @@ static int __devinit wbsd_scan(struct wbsd_host* host) outb(LOCK_CODE, config_ports[i]); } - + release_region(config_ports[i], 2); } - + return -ENODEV; } @@ -1510,12 +1505,12 @@ static int __devinit wbsd_request_region(struct wbsd_host* host, int base) { if (io & 0x7) return -EINVAL; - + if (!request_region(base, 8, DRIVER_NAME)) return -EIO; - + host->base = io; - + return 0; } @@ -1523,12 +1518,12 @@ static void __devexit wbsd_release_regions(struct wbsd_host* host) { if (host->base) release_region(host->base, 8); - + host->base = 0; if (host->config) release_region(host->config, 2); - + host->config = 0; } @@ -1540,10 +1535,10 @@ static void __devinit wbsd_request_dma(struct wbsd_host* host, int dma) { if (dma < 0) return; - + if (request_dma(dma, DRIVER_NAME)) goto err; - + /* * We need to allocate a special buffer in * order for ISA to be able to DMA to it. @@ -1558,7 +1553,7 @@ static void __devinit wbsd_request_dma(struct wbsd_host* host, int dma) */ host->dma_addr = dma_map_single(host->mmc->dev, host->dma_buffer, WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); - + /* * ISA DMA must be aligned on a 64k basis. */ @@ -1571,19 +1566,19 @@ static void __devinit wbsd_request_dma(struct wbsd_host* host, int dma) goto kfree; host->dma = dma; - + return; - + kfree: /* * If we've gotten here then there is some kind of alignment bug */ BUG_ON(1); - + dma_unmap_single(host->mmc->dev, host->dma_addr, WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); host->dma_addr = (dma_addr_t)NULL; - + kfree(host->dma_buffer); host->dma_buffer = NULL; @@ -1604,7 +1599,7 @@ static void __devexit wbsd_release_dma(struct wbsd_host* host) kfree(host->dma_buffer); if (host->dma >= 0) free_dma(host->dma); - + host->dma = -1; host->dma_buffer = NULL; host->dma_addr = (dma_addr_t)NULL; @@ -1617,7 +1612,7 @@ static void __devexit wbsd_release_dma(struct wbsd_host* host) static int __devinit wbsd_request_irq(struct wbsd_host* host, int irq) { int ret; - + /* * Allocate interrupt. */ @@ -1625,7 +1620,7 @@ static int __devinit wbsd_request_irq(struct wbsd_host* host, int irq) ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host); if (ret) return ret; - + host->irq = irq; /* @@ -1637,7 +1632,7 @@ static int __devinit wbsd_request_irq(struct wbsd_host* host, int irq) tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout, (unsigned long)host); tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, (unsigned long)host); tasklet_init(&host->block_tasklet, wbsd_tasklet_block, (unsigned long)host); - + return 0; } @@ -1647,9 +1642,9 @@ static void __devexit wbsd_release_irq(struct wbsd_host* host) return; free_irq(host->irq, host); - + host->irq = 0; - + tasklet_kill(&host->card_tasklet); tasklet_kill(&host->fifo_tasklet); tasklet_kill(&host->crc_tasklet); @@ -1666,7 +1661,7 @@ static int __devinit wbsd_request_resources(struct wbsd_host* host, int base, int irq, int dma) { int ret; - + /* * Allocate I/O ports. */ @@ -1685,7 +1680,7 @@ static int __devinit wbsd_request_resources(struct wbsd_host* host, * Allocate DMA. */ wbsd_request_dma(host, dma); - + return 0; } @@ -1708,7 +1703,7 @@ static void __devinit wbsd_chip_config(struct wbsd_host* host) { /* * Reset the chip. - */ + */ wbsd_write_config(host, WBSD_CONF_SWRST, 1); wbsd_write_config(host, WBSD_CONF_SWRST, 0); @@ -1716,23 +1711,23 @@ static void __devinit wbsd_chip_config(struct wbsd_host* host) * Select SD/MMC function. */ wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); - + /* * Set up card detection. */ wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11); - + /* * Configure chip */ wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8); wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff); - + wbsd_write_config(host, WBSD_CONF_IRQ, host->irq); - + if (host->dma >= 0) wbsd_write_config(host, WBSD_CONF_DRQ, host->dma); - + /* * Enable and power up chip. */ @@ -1743,26 +1738,26 @@ static void __devinit wbsd_chip_config(struct wbsd_host* host) /* * Check that configured resources are correct. */ - + static int __devinit wbsd_chip_validate(struct wbsd_host* host) { int base, irq, dma; - + /* * Select SD/MMC function. */ wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); - + /* * Read configuration. */ base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8; base |= wbsd_read_config(host, WBSD_CONF_PORT_LO); - + irq = wbsd_read_config(host, WBSD_CONF_IRQ); - + dma = wbsd_read_config(host, WBSD_CONF_DRQ); - + /* * Validate against given configuration. */ @@ -1772,7 +1767,7 @@ static int __devinit wbsd_chip_validate(struct wbsd_host* host) return 0; if ((dma != host->dma) && (host->dma != -1)) return 0; - + return 1; } @@ -1788,14 +1783,14 @@ static int __devinit wbsd_init(struct device* dev, int base, int irq, int dma, struct wbsd_host* host = NULL; struct mmc_host* mmc = NULL; int ret; - + ret = wbsd_alloc_mmc(dev); if (ret) return ret; - + mmc = dev_get_drvdata(dev); host = mmc_priv(mmc); - + /* * Scan for hardware. */ @@ -1814,7 +1809,7 @@ static int __devinit wbsd_init(struct device* dev, int base, int irq, int dma, return ret; } } - + /* * Request resources. */ @@ -1825,7 +1820,7 @@ static int __devinit wbsd_init(struct device* dev, int base, int irq, int dma, wbsd_free_mmc(dev); return ret; } - + /* * See if chip needs to be configured. */ @@ -1842,7 +1837,7 @@ static int __devinit wbsd_init(struct device* dev, int base, int irq, int dma, } else wbsd_chip_config(host); - + /* * Power Management stuff. No idea how this works. * Not tested. @@ -1860,7 +1855,7 @@ static int __devinit wbsd_init(struct device* dev, int base, int irq, int dma, * Reset the chip into a known state. */ wbsd_init_device(host); - + mmc_add_host(mmc); printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc)); @@ -1882,12 +1877,12 @@ static void __devexit wbsd_shutdown(struct device* dev, int pnp) { struct mmc_host* mmc = dev_get_drvdata(dev); struct wbsd_host* host; - + if (!mmc) return; host = mmc_priv(mmc); - + mmc_remove_host(mmc); if (!pnp) @@ -1900,9 +1895,9 @@ static void __devexit wbsd_shutdown(struct device* dev, int pnp) wbsd_write_config(host, WBSD_CONF_ENABLE, 0); wbsd_lock_config(host); } - + wbsd_release_resources(host); - + wbsd_free_mmc(dev); } @@ -1932,7 +1927,7 @@ static int __devinit wbsd_pnp_probe(struct pnp_dev * pnpdev, const struct pnp_device_id *dev_id) { int io, irq, dma; - + /* * Get resources from PnP layer. */ @@ -1942,9 +1937,9 @@ wbsd_pnp_probe(struct pnp_dev * pnpdev, const struct pnp_device_id *dev_id) dma = pnp_dma(pnpdev, 0); else dma = -1; - + DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma); - + return wbsd_init(&pnpdev->dev, io, irq, dma, 1); } @@ -1985,7 +1980,7 @@ static struct device_driver wbsd_driver = { .bus = &platform_bus_type, .probe = wbsd_probe, .remove = wbsd_remove, - + .suspend = wbsd_suspend, .resume = wbsd_resume, }; @@ -2008,7 +2003,7 @@ static struct pnp_driver wbsd_pnp_driver = { static int __init wbsd_drv_init(void) { int result; - + printk(KERN_INFO DRIVER_NAME ": Winbond W83L51xD SD/MMC card interface driver, " DRIVER_VERSION "\n"); @@ -2023,8 +2018,8 @@ static int __init wbsd_drv_init(void) return result; } -#endif /* CONFIG_PNP */ - +#endif /* CONFIG_PNP */ + if (nopnp) { result = driver_register(&wbsd_driver); @@ -2046,13 +2041,13 @@ static void __exit wbsd_drv_exit(void) if (!nopnp) pnp_unregister_driver(&wbsd_pnp_driver); - -#endif /* CONFIG_PNP */ + +#endif /* CONFIG_PNP */ if (nopnp) { platform_device_unregister(wbsd_device); - + driver_unregister(&wbsd_driver); } diff --git a/drivers/mmc/wbsd.h b/drivers/mmc/wbsd.h index 9005b5241b3..249baa701cb 100644 --- a/drivers/mmc/wbsd.h +++ b/drivers/mmc/wbsd.h @@ -139,51 +139,50 @@ struct wbsd_host { struct mmc_host* mmc; /* MMC structure */ - + spinlock_t lock; /* Mutex */ int flags; /* Driver states */ #define WBSD_FCARD_PRESENT (1<<0) /* Card is present */ #define WBSD_FIGNORE_DETECT (1<<1) /* Ignore card detection */ - + struct mmc_request* mrq; /* Current request */ - + u8 isr; /* Accumulated ISR */ - + struct scatterlist* cur_sg; /* Current SG entry */ unsigned int num_sg; /* Number of entries left */ void* mapped_sg; /* vaddr of mapped sg */ - + unsigned int offset; /* Offset into current entry */ unsigned int remain; /* Data left in curren entry */ int size; /* Total size of transfer */ - + char* dma_buffer; /* ISA DMA buffer */ dma_addr_t dma_addr; /* Physical address for same */ int firsterr; /* See fifo functions */ - + u8 clk; /* Current clock speed */ unsigned char bus_width; /* Current bus width */ - + int config; /* Config port */ u8 unlock_code; /* Code to unlock config */ int chip_id; /* ID of controller */ - + int base; /* I/O port base */ int irq; /* Interrupt */ int dma; /* DMA channel */ - + struct tasklet_struct card_tasklet; /* Tasklet structures */ struct tasklet_struct fifo_tasklet; struct tasklet_struct crc_tasklet; struct tasklet_struct timeout_tasklet; struct tasklet_struct finish_tasklet; struct tasklet_struct block_tasklet; - - struct timer_list detect_timer; /* Card detection timer */ + struct timer_list ignore_timer; /* Ignore detection timer */ }; diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 811d92e5f5b..cc372136e85 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -25,9 +25,6 @@ /****************************************************************************/ - -/****************************************************************************/ - struct map_info uclinux_ram_map = { .name = "RAM", }; @@ -60,14 +57,15 @@ int __init uclinux_mtd_init(void) struct mtd_info *mtd; struct map_info *mapp; extern char _ebss; + unsigned long addr = (unsigned long) &_ebss; mapp = &uclinux_ram_map; - mapp->phys = (unsigned long) &_ebss; - mapp->size = PAGE_ALIGN(*((unsigned long *)((&_ebss) + 8))); + mapp->phys = addr; + mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(addr + 8)))); mapp->bankwidth = 4; printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n", - (int) mapp->map_priv_2, (int) mapp->size); + (int) mapp->phys, (int) mapp->size); mapp->virt = ioremap_nocache(mapp->phys, mapp->size); @@ -95,7 +93,6 @@ int __init uclinux_mtd_init(void) printk("uclinux[mtd]: set %s to be root filesystem\n", uclinux_romfs[0].name); ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 0); - put_mtd_device(mtd); return(0); } @@ -109,7 +106,7 @@ void __exit uclinux_mtd_cleanup(void) map_destroy(uclinux_ram_mtdinfo); uclinux_ram_mtdinfo = NULL; } - if (uclinux_ram_map.map_priv_1) { + if (uclinux_ram_map.virt) { iounmap((void *) uclinux_ram_map.virt); uclinux_ram_map.virt = 0; } diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 6bb9232514b..54fff9c2e80 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1738,11 +1738,18 @@ config 68360_ENET the Motorola 68360 processor. config FEC - bool "FEC ethernet controller (of ColdFire 5272)" - depends on M5272 || M5282 + bool "FEC ethernet controller (of ColdFire CPUs)" + depends on M523x || M527x || M5272 || M528x help Say Y here if you want to use the built-in 10/100 Fast ethernet - controller on the Motorola ColdFire 5272 processor. + controller on some Motorola ColdFire processors. + +config FEC2 + bool "Second FEC ethernet controller (on some ColdFire CPUs)" + depends on FEC + help + Say Y here if you want to use the second built-in 10/100 Fast + ethernet controller on some Motorola ColdFire processors. config NE_H8300 tristate "NE2000 compatible support for H8/300" diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c index 52c77cbe8c6..1f030273541 100644 --- a/drivers/net/arcnet/com90io.c +++ b/drivers/net/arcnet/com90io.c @@ -160,7 +160,7 @@ static int __init com90io_probe(struct net_device *dev) return -ENODEV; } if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com90io probe")) { - BUGMSG(D_INIT_REASONS, "IO check_region %x-%x failed.\n", + BUGMSG(D_INIT_REASONS, "IO request_region %x-%x failed.\n", ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1); return -ENXIO; } @@ -242,7 +242,7 @@ static int __init com90io_found(struct net_device *dev) BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq); return -ENODEV; } - /* Reserve the I/O region - guaranteed to work by check_region */ + /* Reserve the I/O region */ if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE, "arcnet (COM90xx-IO)")) { free_irq(dev->irq, dev); return -EBUSY; diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 2c700849137..85504fb900d 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -8,7 +8,7 @@ * describes connections using the internal parallel port I/O, which * is basically all of Port D. * - * Right now, I am very watseful with the buffers. I allocate memory + * Right now, I am very wasteful with the buffers. I allocate memory * pages and then divide them into 2K frame buffers. This way I know I * have buffers large enough to hold one frame within one buffer descriptor. * Once I get this working, I will use 64 or 128 byte CPM buffers, which @@ -19,7 +19,10 @@ * Copyright (c) 2000 Ericsson Radio Systems AB. * * Support for FEC controller of ColdFire/5270/5271/5272/5274/5275/5280/5282. - * Copyrught (c) 2001-2004 Greg Ungerer (gerg@snapgear.com) + * Copyright (c) 2001-2004 Greg Ungerer (gerg@snapgear.com) + * + * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be) + * Copyright (c) 2004-2005 Macq Electronique SA. */ #include <linux/config.h> @@ -46,7 +49,8 @@ #include <asm/io.h> #include <asm/pgtable.h> -#if defined(CONFIG_M527x) || defined(CONFIG_M5272) || defined(CONFIG_M528x) +#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || \ + defined(CONFIG_M5272) || defined(CONFIG_M528x) #include <asm/coldfire.h> #include <asm/mcfsim.h> #include "fec.h" @@ -71,7 +75,7 @@ static unsigned int fec_hw[] = { #elif defined(CONFIG_M527x) (MCF_MBAR + 0x1000), (MCF_MBAR + 0x1800), -#elif defined(CONFIG_M528x) +#elif defined(CONFIG_M523x) || defined(CONFIG_M528x) (MCF_MBAR + 0x1000), #else &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec), @@ -94,12 +98,14 @@ static unsigned char fec_mac_default[] = { #define FEC_FLASHMAC 0xffe04000 #elif defined(CONFIG_CANCam) #define FEC_FLASHMAC 0xf0020000 +#elif defined (CONFIG_M5272C3) +#define FEC_FLASHMAC (0xffe04000 + 4) +#elif defined(CONFIG_MOD5272) +#define FEC_FLASHMAC 0xffc0406b #else #define FEC_FLASHMAC 0 #endif -unsigned char *fec_flashmac = (unsigned char *) FEC_FLASHMAC; - /* Forward declarations of some structures to support different PHYs */ @@ -158,7 +164,7 @@ typedef struct { * size bits. Other FEC hardware does not, so we need to take that into * account when setting it. */ -#if defined(CONFIG_M527x) || defined(CONFIG_M528x) +#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) #define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) #else #define OPT_FRAME_SIZE 0 @@ -196,7 +202,7 @@ struct fec_enet_private { uint phy_id_done; uint phy_status; uint phy_speed; - phy_info_t *phy; + phy_info_t const *phy; struct work_struct phy_task; uint sequence_done; @@ -209,7 +215,6 @@ struct fec_enet_private { int link; int old_link; int full_duplex; - unsigned char mac_addr[ETH_ALEN]; }; static int fec_enet_open(struct net_device *dev); @@ -237,10 +242,10 @@ typedef struct mii_list { } mii_list_t; #define NMII 20 -mii_list_t mii_cmds[NMII]; -mii_list_t *mii_free; -mii_list_t *mii_head; -mii_list_t *mii_tail; +static mii_list_t mii_cmds[NMII]; +static mii_list_t *mii_free; +static mii_list_t *mii_head; +static mii_list_t *mii_tail; static int mii_queue(struct net_device *dev, int request, void (*func)(uint, struct net_device *)); @@ -425,7 +430,7 @@ fec_timeout(struct net_device *dev) } } #endif - fec_restart(dev, 0); + fec_restart(dev, fep->full_duplex); netif_wake_queue(dev); } @@ -757,45 +762,52 @@ static void mii_parse_sr(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); volatile uint *s = &(fep->phy_status); + uint status; - *s &= ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC); + status = *s & ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC); if (mii_reg & 0x0004) - *s |= PHY_STAT_LINK; + status |= PHY_STAT_LINK; if (mii_reg & 0x0010) - *s |= PHY_STAT_FAULT; + status |= PHY_STAT_FAULT; if (mii_reg & 0x0020) - *s |= PHY_STAT_ANC; + status |= PHY_STAT_ANC; + + *s = status; } static void mii_parse_cr(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); volatile uint *s = &(fep->phy_status); + uint status; - *s &= ~(PHY_CONF_ANE | PHY_CONF_LOOP); + status = *s & ~(PHY_CONF_ANE | PHY_CONF_LOOP); if (mii_reg & 0x1000) - *s |= PHY_CONF_ANE; + status |= PHY_CONF_ANE; if (mii_reg & 0x4000) - *s |= PHY_CONF_LOOP; + status |= PHY_CONF_LOOP; + *s = status; } static void mii_parse_anar(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); volatile uint *s = &(fep->phy_status); + uint status; - *s &= ~(PHY_CONF_SPMASK); + status = *s & ~(PHY_CONF_SPMASK); if (mii_reg & 0x0020) - *s |= PHY_CONF_10HDX; + status |= PHY_CONF_10HDX; if (mii_reg & 0x0040) - *s |= PHY_CONF_10FDX; + status |= PHY_CONF_10FDX; if (mii_reg & 0x0080) - *s |= PHY_CONF_100HDX; + status |= PHY_CONF_100HDX; if (mii_reg & 0x00100) - *s |= PHY_CONF_100FDX; + status |= PHY_CONF_100FDX; + *s = status; } /* ------------------------------------------------------------------------- */ @@ -811,37 +823,34 @@ static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); volatile uint *s = &(fep->phy_status); + uint status; - *s &= ~(PHY_STAT_SPMASK); - + status = *s & ~(PHY_STAT_SPMASK); if (mii_reg & 0x0800) { if (mii_reg & 0x1000) - *s |= PHY_STAT_100FDX; + status |= PHY_STAT_100FDX; else - *s |= PHY_STAT_100HDX; + status |= PHY_STAT_100HDX; } else { if (mii_reg & 0x1000) - *s |= PHY_STAT_10FDX; + status |= PHY_STAT_10FDX; else - *s |= PHY_STAT_10HDX; + status |= PHY_STAT_10HDX; } + *s = status; } -static phy_info_t phy_info_lxt970 = { - 0x07810000, - "LXT970", - - (const phy_cmd_t []) { /* config */ +static phy_cmd_t const phy_cmd_lxt970_config[] = { { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, { mk_mii_end, } - }, - (const phy_cmd_t []) { /* startup - enable interrupts */ + }; +static phy_cmd_t const phy_cmd_lxt970_startup[] = { /* enable interrupts */ { mk_mii_write(MII_LXT970_IER, 0x0002), NULL }, { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ { mk_mii_end, } - }, - (const phy_cmd_t []) { /* ack_int */ + }; +static phy_cmd_t const phy_cmd_lxt970_ack_int[] = { /* read SR and ISR to acknowledge */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_read(MII_LXT970_ISR), NULL }, @@ -849,11 +858,18 @@ static phy_info_t phy_info_lxt970 = { /* find out the current status */ { mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr }, { mk_mii_end, } - }, - (const phy_cmd_t []) { /* shutdown - disable interrupts */ + }; +static phy_cmd_t const phy_cmd_lxt970_shutdown[] = { /* disable interrupts */ { mk_mii_write(MII_LXT970_IER, 0x0000), NULL }, { mk_mii_end, } - }, + }; +static phy_info_t const phy_info_lxt970 = { + .id = 0x07810000, + .name = "LXT970", + .config = phy_cmd_lxt970_config, + .startup = phy_cmd_lxt970_startup, + .ack_int = phy_cmd_lxt970_ack_int, + .shutdown = phy_cmd_lxt970_shutdown }; /* ------------------------------------------------------------------------- */ @@ -878,45 +894,44 @@ static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); volatile uint *s = &(fep->phy_status); + uint status; - *s &= ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC); + status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC); if (mii_reg & 0x0400) { fep->link = 1; - *s |= PHY_STAT_LINK; + status |= PHY_STAT_LINK; } else { fep->link = 0; } if (mii_reg & 0x0080) - *s |= PHY_STAT_ANC; + status |= PHY_STAT_ANC; if (mii_reg & 0x4000) { if (mii_reg & 0x0200) - *s |= PHY_STAT_100FDX; + status |= PHY_STAT_100FDX; else - *s |= PHY_STAT_100HDX; + status |= PHY_STAT_100HDX; } else { if (mii_reg & 0x0200) - *s |= PHY_STAT_10FDX; + status |= PHY_STAT_10FDX; else - *s |= PHY_STAT_10HDX; + status |= PHY_STAT_10HDX; } if (mii_reg & 0x0008) - *s |= PHY_STAT_FAULT; -} + status |= PHY_STAT_FAULT; -static phy_info_t phy_info_lxt971 = { - 0x0001378e, - "LXT971", + *s = status; +} - (const phy_cmd_t []) { /* config */ - /* limit to 10MBit because my protorype board +static phy_cmd_t const phy_cmd_lxt971_config[] = { + /* limit to 10MBit because my prototype board * doesn't work with 100. */ { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 }, { mk_mii_end, } - }, - (const phy_cmd_t []) { /* startup - enable interrupts */ + }; +static phy_cmd_t const phy_cmd_lxt971_startup[] = { /* enable interrupts */ { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL }, { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ { mk_mii_write(MII_LXT971_LCR, 0xd422), NULL }, /* LED config */ @@ -925,19 +940,26 @@ static phy_info_t phy_info_lxt971 = { * read here to get a valid value in ack_int */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_end, } - }, - (const phy_cmd_t []) { /* ack_int */ + }; +static phy_cmd_t const phy_cmd_lxt971_ack_int[] = { + /* acknowledge the int before reading status ! */ + { mk_mii_read(MII_LXT971_ISR), NULL }, /* find out the current status */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 }, - /* we only need to read ISR to acknowledge */ - { mk_mii_read(MII_LXT971_ISR), NULL }, { mk_mii_end, } - }, - (const phy_cmd_t []) { /* shutdown - disable interrupts */ + }; +static phy_cmd_t const phy_cmd_lxt971_shutdown[] = { /* disable interrupts */ { mk_mii_write(MII_LXT971_IER, 0x0000), NULL }, { mk_mii_end, } - }, + }; +static phy_info_t const phy_info_lxt971 = { + .id = 0x0001378e, + .name = "LXT971", + .config = phy_cmd_lxt971_config, + .startup = phy_cmd_lxt971_startup, + .ack_int = phy_cmd_lxt971_ack_int, + .shutdown = phy_cmd_lxt971_shutdown }; /* ------------------------------------------------------------------------- */ @@ -956,22 +978,21 @@ static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); volatile uint *s = &(fep->phy_status); + uint status; - *s &= ~(PHY_STAT_SPMASK); + status = *s & ~(PHY_STAT_SPMASK); switch((mii_reg >> 2) & 7) { - case 1: *s |= PHY_STAT_10HDX; break; - case 2: *s |= PHY_STAT_100HDX; break; - case 5: *s |= PHY_STAT_10FDX; break; - case 6: *s |= PHY_STAT_100FDX; break; - } + case 1: status |= PHY_STAT_10HDX; break; + case 2: status |= PHY_STAT_100HDX; break; + case 5: status |= PHY_STAT_10FDX; break; + case 6: status |= PHY_STAT_100FDX; break; } -static phy_info_t phy_info_qs6612 = { - 0x00181440, - "QS6612", - - (const phy_cmd_t []) { /* config */ + *s = status; +} + +static phy_cmd_t const phy_cmd_qs6612_config[] = { /* The PHY powers up isolated on the RPX, * so send a command to allow operation. */ @@ -981,13 +1002,13 @@ static phy_info_t phy_info_qs6612 = { { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, { mk_mii_end, } - }, - (const phy_cmd_t []) { /* startup - enable interrupts */ + }; +static phy_cmd_t const phy_cmd_qs6612_startup[] = { /* enable interrupts */ { mk_mii_write(MII_QS6612_IMR, 0x003a), NULL }, { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ { mk_mii_end, } - }, - (const phy_cmd_t []) { /* ack_int */ + }; +static phy_cmd_t const phy_cmd_qs6612_ack_int[] = { /* we need to read ISR, SR and ANER to acknowledge */ { mk_mii_read(MII_QS6612_ISR), NULL }, { mk_mii_read(MII_REG_SR), mii_parse_sr }, @@ -996,11 +1017,18 @@ static phy_info_t phy_info_qs6612 = { /* read pcr to get info */ { mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr }, { mk_mii_end, } - }, - (const phy_cmd_t []) { /* shutdown - disable interrupts */ + }; +static phy_cmd_t const phy_cmd_qs6612_shutdown[] = { /* disable interrupts */ { mk_mii_write(MII_QS6612_IMR, 0x0000), NULL }, { mk_mii_end, } - }, + }; +static phy_info_t const phy_info_qs6612 = { + .id = 0x00181440, + .name = "QS6612", + .config = phy_cmd_qs6612_config, + .startup = phy_cmd_qs6612_startup, + .ack_int = phy_cmd_qs6612_ack_int, + .shutdown = phy_cmd_qs6612_shutdown }; /* ------------------------------------------------------------------------- */ @@ -1020,49 +1048,54 @@ static void mii_parse_am79c874_dr(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); volatile uint *s = &(fep->phy_status); + uint status; - *s &= ~(PHY_STAT_SPMASK | PHY_STAT_ANC); + status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_ANC); if (mii_reg & 0x0080) - *s |= PHY_STAT_ANC; + status |= PHY_STAT_ANC; if (mii_reg & 0x0400) - *s |= ((mii_reg & 0x0800) ? PHY_STAT_100FDX : PHY_STAT_100HDX); + status |= ((mii_reg & 0x0800) ? PHY_STAT_100FDX : PHY_STAT_100HDX); else - *s |= ((mii_reg & 0x0800) ? PHY_STAT_10FDX : PHY_STAT_10HDX); + status |= ((mii_reg & 0x0800) ? PHY_STAT_10FDX : PHY_STAT_10HDX); + + *s = status; } -static phy_info_t phy_info_am79c874 = { - 0x00022561, - "AM79C874", - - (const phy_cmd_t []) { /* config */ - /* limit to 10MBit because my protorype board - * doesn't work with 100. */ +static phy_cmd_t const phy_cmd_am79c874_config[] = { { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, { mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr }, { mk_mii_end, } - }, - (const phy_cmd_t []) { /* startup - enable interrupts */ + }; +static phy_cmd_t const phy_cmd_am79c874_startup[] = { /* enable interrupts */ { mk_mii_write(MII_AM79C874_ICSR, 0xff00), NULL }, { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_end, } - }, - (const phy_cmd_t []) { /* ack_int */ + }; +static phy_cmd_t const phy_cmd_am79c874_ack_int[] = { /* find out the current status */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr }, /* we only need to read ISR to acknowledge */ { mk_mii_read(MII_AM79C874_ICSR), NULL }, { mk_mii_end, } - }, - (const phy_cmd_t []) { /* shutdown - disable interrupts */ + }; +static phy_cmd_t const phy_cmd_am79c874_shutdown[] = { /* disable interrupts */ { mk_mii_write(MII_AM79C874_ICSR, 0x0000), NULL }, { mk_mii_end, } - }, + }; +static phy_info_t const phy_info_am79c874 = { + .id = 0x00022561, + .name = "AM79C874", + .config = phy_cmd_am79c874_config, + .startup = phy_cmd_am79c874_startup, + .ack_int = phy_cmd_am79c874_ack_int, + .shutdown = phy_cmd_am79c874_shutdown }; + /* ------------------------------------------------------------------------- */ /* Kendin KS8721BL phy */ @@ -1072,37 +1105,40 @@ static phy_info_t phy_info_am79c874 = { #define MII_KS8721BL_ICSR 22 #define MII_KS8721BL_PHYCR 31 -static phy_info_t phy_info_ks8721bl = { - 0x00022161, - "KS8721BL", - - (const phy_cmd_t []) { /* config */ +static phy_cmd_t const phy_cmd_ks8721bl_config[] = { { mk_mii_read(MII_REG_CR), mii_parse_cr }, { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, { mk_mii_end, } - }, - (const phy_cmd_t []) { /* startup */ + }; +static phy_cmd_t const phy_cmd_ks8721bl_startup[] = { /* enable interrupts */ { mk_mii_write(MII_KS8721BL_ICSR, 0xff00), NULL }, { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, { mk_mii_end, } - }, - (const phy_cmd_t []) { /* ack_int */ + }; +static phy_cmd_t const phy_cmd_ks8721bl_ack_int[] = { /* find out the current status */ { mk_mii_read(MII_REG_SR), mii_parse_sr }, /* we only need to read ISR to acknowledge */ { mk_mii_read(MII_KS8721BL_ICSR), NULL }, { mk_mii_end, } - }, - (const phy_cmd_t []) { /* shutdown */ + }; +static phy_cmd_t const phy_cmd_ks8721bl_shutdown[] = { /* disable interrupts */ { mk_mii_write(MII_KS8721BL_ICSR, 0x0000), NULL }, { mk_mii_end, } - }, + }; +static phy_info_t const phy_info_ks8721bl = { + .id = 0x00022161, + .name = "KS8721BL", + .config = phy_cmd_ks8721bl_config, + .startup = phy_cmd_ks8721bl_startup, + .ack_int = phy_cmd_ks8721bl_ack_int, + .shutdown = phy_cmd_ks8721bl_shutdown }; /* ------------------------------------------------------------------------- */ -static phy_info_t *phy_info[] = { +static phy_info_t const * const phy_info[] = { &phy_info_lxt970, &phy_info_lxt971, &phy_info_qs6612, @@ -1129,16 +1165,23 @@ mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs); static void __inline__ fec_request_intrs(struct net_device *dev) { volatile unsigned long *icrp; + static const struct idesc { + char *name; + unsigned short irq; + irqreturn_t (*handler)(int, void *, struct pt_regs *); + } *idp, id[] = { + { "fec(RX)", 86, fec_enet_interrupt }, + { "fec(TX)", 87, fec_enet_interrupt }, + { "fec(OTHER)", 88, fec_enet_interrupt }, + { "fec(MII)", 66, mii_link_interrupt }, + { NULL }, + }; /* Setup interrupt handlers. */ - if (request_irq(86, fec_enet_interrupt, 0, "fec(RX)", dev) != 0) - printk("FEC: Could not allocate FEC(RC) IRQ(86)!\n"); - if (request_irq(87, fec_enet_interrupt, 0, "fec(TX)", dev) != 0) - printk("FEC: Could not allocate FEC(RC) IRQ(87)!\n"); - if (request_irq(88, fec_enet_interrupt, 0, "fec(OTHER)", dev) != 0) - printk("FEC: Could not allocate FEC(OTHER) IRQ(88)!\n"); - if (request_irq(66, mii_link_interrupt, 0, "fec(MII)", dev) != 0) - printk("FEC: Could not allocate MII IRQ(66)!\n"); + for (idp = id; idp->name; idp++) { + if (request_irq(idp->irq, idp->handler, 0, idp->name, dev) != 0) + printk("FEC: Could not allocate %s IRQ(%d)!\n", idp->name, idp->irq); + } /* Unmask interrupt at ColdFire 5272 SIM */ icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR3); @@ -1169,17 +1212,16 @@ static void __inline__ fec_get_mac(struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); volatile fec_t *fecp; - unsigned char *iap, tmpaddr[6]; - int i; + unsigned char *iap, tmpaddr[ETH_ALEN]; fecp = fep->hwp; - if (fec_flashmac) { + if (FEC_FLASHMAC) { /* * Get MAC address from FLASH. * If it is all 1's or 0's, use the default. */ - iap = fec_flashmac; + iap = (unsigned char *)FEC_FLASHMAC; if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) && (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0)) iap = fec_mac_default; @@ -1192,14 +1234,11 @@ static void __inline__ fec_get_mac(struct net_device *dev) iap = &tmpaddr[0]; } - for (i=0; i<ETH_ALEN; i++) - dev->dev_addr[i] = fep->mac_addr[i] = *iap++; + memcpy(dev->dev_addr, iap, ETH_ALEN); /* Adjust MAC if using default MAC address */ - if (iap == fec_mac_default) { - dev->dev_addr[ETH_ALEN-1] = fep->mac_addr[ETH_ALEN-1] = - iap[ETH_ALEN-1] + fep->index; - } + if (iap == fec_mac_default) + dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index; } static void __inline__ fec_enable_phy_intr(void) @@ -1234,48 +1273,44 @@ static void __inline__ fec_uncache(unsigned long addr) /* ------------------------------------------------------------------------- */ -#elif defined(CONFIG_M527x) || defined(CONFIG_M528x) +#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) /* - * Code specific to Coldfire 5270/5271/5274/5275 and 5280/5282 setups. + * Code specific to Coldfire 5230/5231/5232/5234/5235, + * the 5270/5271/5274/5275 and 5280/5282 setups. */ static void __inline__ fec_request_intrs(struct net_device *dev) { struct fec_enet_private *fep; int b; + static const struct idesc { + char *name; + unsigned short irq; + } *idp, id[] = { + { "fec(TXF)", 23 }, + { "fec(TXB)", 24 }, + { "fec(TXFIFO)", 25 }, + { "fec(TXCR)", 26 }, + { "fec(RXF)", 27 }, + { "fec(RXB)", 28 }, + { "fec(MII)", 29 }, + { "fec(LC)", 30 }, + { "fec(HBERR)", 31 }, + { "fec(GRA)", 32 }, + { "fec(EBERR)", 33 }, + { "fec(BABT)", 34 }, + { "fec(BABR)", 35 }, + { NULL }, + }; fep = netdev_priv(dev); b = (fep->index) ? 128 : 64; /* Setup interrupt handlers. */ - if (request_irq(b+23, fec_enet_interrupt, 0, "fec(TXF)", dev) != 0) - printk("FEC: Could not allocate FEC(TXF) IRQ(%d+23)!\n", b); - if (request_irq(b+24, fec_enet_interrupt, 0, "fec(TXB)", dev) != 0) - printk("FEC: Could not allocate FEC(TXB) IRQ(%d+24)!\n", b); - if (request_irq(b+25, fec_enet_interrupt, 0, "fec(TXFIFO)", dev) != 0) - printk("FEC: Could not allocate FEC(TXFIFO) IRQ(%d+25)!\n", b); - if (request_irq(b+26, fec_enet_interrupt, 0, "fec(TXCR)", dev) != 0) - printk("FEC: Could not allocate FEC(TXCR) IRQ(%d+26)!\n", b); - - if (request_irq(b+27, fec_enet_interrupt, 0, "fec(RXF)", dev) != 0) - printk("FEC: Could not allocate FEC(RXF) IRQ(%d+27)!\n", b); - if (request_irq(b+28, fec_enet_interrupt, 0, "fec(RXB)", dev) != 0) - printk("FEC: Could not allocate FEC(RXB) IRQ(%d+28)!\n", b); - - if (request_irq(b+29, fec_enet_interrupt, 0, "fec(MII)", dev) != 0) - printk("FEC: Could not allocate FEC(MII) IRQ(%d+29)!\n", b); - if (request_irq(b+30, fec_enet_interrupt, 0, "fec(LC)", dev) != 0) - printk("FEC: Could not allocate FEC(LC) IRQ(%d+30)!\n", b); - if (request_irq(b+31, fec_enet_interrupt, 0, "fec(HBERR)", dev) != 0) - printk("FEC: Could not allocate FEC(HBERR) IRQ(%d+31)!\n", b); - if (request_irq(b+32, fec_enet_interrupt, 0, "fec(GRA)", dev) != 0) - printk("FEC: Could not allocate FEC(GRA) IRQ(%d+32)!\n", b); - if (request_irq(b+33, fec_enet_interrupt, 0, "fec(EBERR)", dev) != 0) - printk("FEC: Could not allocate FEC(EBERR) IRQ(%d+33)!\n", b); - if (request_irq(b+34, fec_enet_interrupt, 0, "fec(BABT)", dev) != 0) - printk("FEC: Could not allocate FEC(BABT) IRQ(%d+34)!\n", b); - if (request_irq(b+35, fec_enet_interrupt, 0, "fec(BABR)", dev) != 0) - printk("FEC: Could not allocate FEC(BABR) IRQ(%d+35)!\n", b); + for (idp = id; idp->name; idp++) { + if (request_irq(b+idp->irq, fec_enet_interrupt, 0, idp->name, dev) != 0) + printk("FEC: Could not allocate %s IRQ(%d)!\n", idp->name, b+idp->irq); + } /* Unmask interrupts at ColdFire 5280/5282 interrupt controller */ { @@ -1300,11 +1335,13 @@ static void __inline__ fec_request_intrs(struct net_device *dev) #if defined(CONFIG_M528x) /* Set up gpio outputs for MII lines */ { - volatile unsigned short *gpio_paspar; + volatile u16 *gpio_paspar; + volatile u8 *gpio_pehlpar; - gpio_paspar = (volatile unsigned short *) (MCF_IPSBAR + - 0x100056); - *gpio_paspar = 0x0f00; + gpio_paspar = (volatile u16 *) (MCF_IPSBAR + 0x100056); + gpio_pehlpar = (volatile u16 *) (MCF_IPSBAR + 0x100058); + *gpio_paspar |= 0x0f00; + *gpio_pehlpar = 0xc0; } #endif } @@ -1331,17 +1368,16 @@ static void __inline__ fec_get_mac(struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); volatile fec_t *fecp; - unsigned char *iap, tmpaddr[6]; - int i; + unsigned char *iap, tmpaddr[ETH_ALEN]; fecp = fep->hwp; - if (fec_flashmac) { + if (FEC_FLASHMAC) { /* * Get MAC address from FLASH. * If it is all 1's or 0's, use the default. */ - iap = fec_flashmac; + iap = FEC_FLASHMAC; if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) && (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0)) iap = fec_mac_default; @@ -1354,14 +1390,11 @@ static void __inline__ fec_get_mac(struct net_device *dev) iap = &tmpaddr[0]; } - for (i=0; i<ETH_ALEN; i++) - dev->dev_addr[i] = fep->mac_addr[i] = *iap++; + memcpy(dev->dev_addr, iap, ETH_ALEN); /* Adjust MAC if using default MAC address */ - if (iap == fec_mac_default) { - dev->dev_addr[ETH_ALEN-1] = fep->mac_addr[ETH_ALEN-1] = - iap[ETH_ALEN-1] + fep->index; - } + if (iap == fec_mac_default) + dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index; } static void __inline__ fec_enable_phy_intr(void) @@ -1392,7 +1425,7 @@ static void __inline__ fec_uncache(unsigned long addr) #else /* - * Code sepcific to the MPC860T setup. + * Code specific to the MPC860T setup. */ static void __inline__ fec_request_intrs(struct net_device *dev) { @@ -1424,13 +1457,10 @@ static void __inline__ fec_request_intrs(struct net_device *dev) static void __inline__ fec_get_mac(struct net_device *dev) { - struct fec_enet_private *fep = netdev_priv(dev); - unsigned char *iap, tmpaddr[6]; bd_t *bd; - int i; - iap = bd->bi_enetaddr; bd = (bd_t *)__res; + memcpy(dev->dev_addr, bd->bi_enetaddr, ETH_ALEN); #ifdef CONFIG_RPXCLASSIC /* The Embedded Planet boards have only one MAC address in @@ -1439,14 +1469,8 @@ static void __inline__ fec_get_mac(struct net_device *dev) * the address bits above something that would have (up to * now) been allocated. */ - for (i=0; i<6; i++) - tmpaddr[i] = *iap++; - tmpaddr[3] |= 0x80; - iap = tmpaddr; + dev->dev_adrd[3] |= 0x80; #endif - - for (i=0; i<6; i++) - dev->dev_addr[i] = fep->mac_addr[i] = *iap++; } static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep) @@ -1556,7 +1580,7 @@ static void mii_display_status(struct net_device *dev) static void mii_display_config(struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - volatile uint *s = &(fep->phy_status); + uint status = fep->phy_status; /* ** When we get here, phy_task is already removed from @@ -1565,23 +1589,23 @@ static void mii_display_config(struct net_device *dev) fep->mii_phy_task_queued = 0; printk("%s: config: auto-negotiation ", dev->name); - if (*s & PHY_CONF_ANE) + if (status & PHY_CONF_ANE) printk("on"); else printk("off"); - if (*s & PHY_CONF_100FDX) + if (status & PHY_CONF_100FDX) printk(", 100FDX"); - if (*s & PHY_CONF_100HDX) + if (status & PHY_CONF_100HDX) printk(", 100HDX"); - if (*s & PHY_CONF_10FDX) + if (status & PHY_CONF_10FDX) printk(", 10FDX"); - if (*s & PHY_CONF_10HDX) + if (status & PHY_CONF_10HDX) printk(", 10HDX"); - if (!(*s & PHY_CONF_SPMASK)) + if (!(status & PHY_CONF_SPMASK)) printk(", No speed/duplex selected?"); - if (*s & PHY_CONF_LOOP) + if (status & PHY_CONF_LOOP) printk(", loopback enabled"); printk(".\n"); @@ -1639,7 +1663,7 @@ static void mii_queue_relink(uint mii_reg, struct net_device *dev) schedule_work(&fep->phy_task); } -/* mii_queue_config is called in user context from fec_enet_open */ +/* mii_queue_config is called in interrupt context from fec_enet_mii */ static void mii_queue_config(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); @@ -1652,14 +1676,14 @@ static void mii_queue_config(uint mii_reg, struct net_device *dev) schedule_work(&fep->phy_task); } - - -phy_cmd_t phy_cmd_relink[] = { { mk_mii_read(MII_REG_CR), mii_queue_relink }, - { mk_mii_end, } }; -phy_cmd_t phy_cmd_config[] = { { mk_mii_read(MII_REG_CR), mii_queue_config }, - { mk_mii_end, } }; - - +phy_cmd_t const phy_cmd_relink[] = { + { mk_mii_read(MII_REG_CR), mii_queue_relink }, + { mk_mii_end, } + }; +phy_cmd_t const phy_cmd_config[] = { + { mk_mii_read(MII_REG_CR), mii_queue_config }, + { mk_mii_end, } + }; /* Read remainder of PHY ID. */ @@ -1897,17 +1921,15 @@ static void set_multicast_list(struct net_device *dev) static void fec_set_mac_address(struct net_device *dev) { - struct fec_enet_private *fep; volatile fec_t *fecp; - fep = netdev_priv(dev); - fecp = fep->hwp; + fecp = ((struct fec_enet_private *)netdev_priv(dev))->hwp; /* Set station address. */ - fecp->fec_addr_low = fep->mac_addr[3] | (fep->mac_addr[2] << 8) | - (fep->mac_addr[1] << 16) | (fep->mac_addr[0] << 24); - fecp->fec_addr_high = (fep->mac_addr[5] << 16) | - (fep->mac_addr[4] << 24); + fecp->fec_addr_low = dev->dev_addr[3] | (dev->dev_addr[2] << 8) | + (dev->dev_addr[1] << 16) | (dev->dev_addr[0] << 24); + fecp->fec_addr_high = (dev->dev_addr[5] << 16) | + (dev->dev_addr[4] << 24); } @@ -1943,7 +1965,7 @@ int __init fec_enet_init(struct net_device *dev) udelay(10); /* Clear and enable interrupts */ - fecp->fec_ievent = 0xffc0; + fecp->fec_ievent = 0xffc00000; fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB | FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII); fecp->fec_hash_table_high = 0; @@ -2063,11 +2085,6 @@ int __init fec_enet_init(struct net_device *dev) /* setup MII interface */ fec_set_mii(dev, fep); - printk("%s: FEC ENET Version 0.2, ", dev->name); - for (i=0; i<5; i++) - printk("%02x:", dev->dev_addr[i]); - printk("%02x\n", dev->dev_addr[5]); - /* Queue up command to detect the PHY and initialize the * remainder of the interface. */ @@ -2106,18 +2123,12 @@ fec_restart(struct net_device *dev, int duplex) /* Clear any outstanding interrupt. */ - fecp->fec_ievent = 0xffc0; + fecp->fec_ievent = 0xffc00000; fec_enable_phy_intr(); /* Set station address. */ - fecp->fec_addr_low = fep->mac_addr[3] | (fep->mac_addr[2] << 8) | - (fep->mac_addr[1] << 16) | (fep->mac_addr[0] << 24); - fecp->fec_addr_high = (fep->mac_addr[5] << 16) | - (fep->mac_addr[4] << 24); - - for (i=0; i<ETH_ALEN; i++) - dev->dev_addr[i] = fep->mac_addr[i]; + fec_set_mac_address(dev); /* Reset all multicast. */ @@ -2215,7 +2226,7 @@ fec_stop(struct net_device *dev) fecp->fec_x_cntrl = 0x01; /* Graceful transmit stop */ - while(!(fecp->fec_ievent & 0x10000000)); + while(!(fecp->fec_ievent & FEC_ENET_GRA)); /* Whack a reset. We should wait for this. */ @@ -2234,7 +2245,9 @@ fec_stop(struct net_device *dev) static int __init fec_enet_module_init(void) { struct net_device *dev; - int i, err; + int i, j, err; + + printk("FEC ENET Version 0.2\n"); for (i = 0; (i < FEC_MAX_PORTS); i++) { dev = alloc_etherdev(sizeof(struct fec_enet_private)); @@ -2250,6 +2263,11 @@ static int __init fec_enet_module_init(void) free_netdev(dev); return -EIO; } + + printk("%s: ethernet ", dev->name); + for (j = 0; (j < 5); j++) + printk("%02x:", dev->dev_addr[j]); + printk("%02x\n", dev->dev_addr[5]); } return 0; } diff --git a/drivers/net/fec.h b/drivers/net/fec.h index c6e4f979ff5..045761b8a60 100644 --- a/drivers/net/fec.h +++ b/drivers/net/fec.h @@ -1,8 +1,9 @@ /****************************************************************************/ /* - * fec.h -- Fast Ethernet Controller for Motorola ColdFire 5270, - 5271, 5272, 5274, 5275, 5280 and 5282. + * fec.h -- Fast Ethernet Controller for Motorola ColdFire 5230, + * 5231, 5232, 5234, 5235, 5270, 5271, 5272, 5274, 5275, + * 5280 and 5282. * * (C) Copyright 2000-2003, Greg Ungerer (gerg@snapgear.com) * (C) Copyright 2000-2001, Lineo (www.lineo.com) @@ -13,7 +14,7 @@ #define FEC_H /****************************************************************************/ -#if defined(CONFIG_M527x) || defined(CONFIG_M528x) +#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) /* * Just figures, Motorola would have to change the offsets for * registers in the same peripheral device on different models diff --git a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c index d1fb1bab8aa..bedd7f9f23e 100644 --- a/drivers/net/wireless/orinoco_cs.c +++ b/drivers/net/wireless/orinoco_cs.c @@ -629,6 +629,7 @@ static struct pcmcia_device_id orinoco_cs_ids[] = { PCMCIA_DEVICE_PROD_ID12("Cabletron", "RoamAbout 802.11 DS", 0x32d445f5, 0xedeffd90), PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCC-11", 0x5261440f, 0xa6405584), PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCCA-11", 0x5261440f, 0xdf6115f9), + PCMCIA_DEVICE_PROD_ID12("corega_K.K.", "Wireless_LAN_PCCB-11", 0x29e33311, 0xee7a27ae), PCMCIA_DEVICE_PROD_ID12("D", "Link DRC-650 11Mbps WLAN Card", 0x71b18589, 0xf144e3ac), PCMCIA_DEVICE_PROD_ID12("D", "Link DWL-650 11Mbps WLAN Card", 0x71b18589, 0xb6f1b0ab), PCMCIA_DEVICE_PROD_ID12("ELSA", "AirLancer MC-11", 0x4507a33a, 0xef54f0e3), diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c index 39ba6406fd5..80969f7e7a0 100644 --- a/drivers/pcmcia/pcmcia_ioctl.c +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -376,6 +376,7 @@ static int ds_open(struct inode *inode, struct file *file) socket_t i = iminor(inode); struct pcmcia_socket *s; user_info_t *user; + static int warning_printed = 0; ds_dbg(0, "ds_open(socket %d)\n", i); @@ -407,6 +408,17 @@ static int ds_open(struct inode *inode, struct file *file) s->user = user; file->private_data = user; + if (!warning_printed) { + printk(KERN_INFO "pcmcia: Detected deprecated PCMCIA ioctl " + "usage.\n"); + printk(KERN_INFO "pcmcia: This interface will soon be removed from " + "the kernel; please expect breakage unless you upgrade " + "to new tools.\n"); + printk(KERN_INFO "pcmcia: see http://www.kernel.org/pub/linux/" + "utils/kernel/pcmcia/pcmcia.html for details.\n"); + warning_printed = 1; + } + if (s->pcmcia_state.present) queue_event(user, CS_EVENT_CARD_INSERTION); return 0; diff --git a/drivers/sbus/char/bpp.c b/drivers/sbus/char/bpp.c index 87302fb1488..ccb20a6f5f3 100644 --- a/drivers/sbus/char/bpp.c +++ b/drivers/sbus/char/bpp.c @@ -295,8 +295,7 @@ static unsigned short get_pins(unsigned minor) static void snooze(unsigned long snooze_time, unsigned minor) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(snooze_time + 1); + schedule_timeout_uninterruptible(snooze_time + 1); } static int wait_for(unsigned short set, unsigned short clr, diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c index dbad7f35eb0..24ed5893b4f 100644 --- a/drivers/sbus/char/display7seg.c +++ b/drivers/sbus/char/display7seg.c @@ -14,7 +14,7 @@ #include <linux/major.h> #include <linux/init.h> #include <linux/miscdevice.h> -#include <linux/ioport.h> /* request_region, check_region */ +#include <linux/ioport.h> /* request_region */ #include <asm/atomic.h> #include <asm/ebus.h> /* EBus device */ #include <asm/oplib.h> /* OpenProm Library */ diff --git a/drivers/sbus/char/vfc_i2c.c b/drivers/sbus/char/vfc_i2c.c index 739cad9b19a..ceec30648f4 100644 --- a/drivers/sbus/char/vfc_i2c.c +++ b/drivers/sbus/char/vfc_i2c.c @@ -81,8 +81,7 @@ int vfc_pcf8584_init(struct vfc_dev *dev) void vfc_i2c_delay_no_busy(struct vfc_dev *dev, unsigned long usecs) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(usecs_to_jiffies(usecs)); + schedule_timeout_uninterruptible(usecs_to_jiffies(usecs)); } void inline vfc_i2c_delay(struct vfc_dev *dev) diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index bc6e4627c7a..a6ac61611f3 100644 --- a/drivers/scsi/3w-9xxx.c +++ b/drivers/scsi/3w-9xxx.c @@ -59,6 +59,7 @@ Fix 'handled=1' ISR usage, remove bogus IRQ check. Remove un-needed eh_abort handler. Add support for embedded firmware error strings. + 2.26.02.003 - Correctly handle single sgl's with use_sg=1. */ #include <linux/module.h> @@ -81,7 +82,7 @@ #include "3w-9xxx.h" /* Globals */ -#define TW_DRIVER_VERSION "2.26.02.002" +#define TW_DRIVER_VERSION "2.26.02.003" static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT]; static unsigned int twa_device_extension_count; static int twa_major = -1; @@ -1805,6 +1806,8 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, if (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH) { command_packet->sg_list[0].address = tw_dev->generic_buffer_phys[request_id]; command_packet->sg_list[0].length = TW_MIN_SGL_LENGTH; + if (tw_dev->srb[request_id]->sc_data_direction == DMA_TO_DEVICE || tw_dev->srb[request_id]->sc_data_direction == DMA_BIDIRECTIONAL) + memcpy(tw_dev->generic_buffer_virt[request_id], tw_dev->srb[request_id]->request_buffer, tw_dev->srb[request_id]->request_bufflen); } else { buffaddr = twa_map_scsi_single_data(tw_dev, request_id); if (buffaddr == 0) @@ -1823,6 +1826,12 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, if (tw_dev->srb[request_id]->use_sg > 0) { if ((tw_dev->srb[request_id]->use_sg == 1) && (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH)) { + if (tw_dev->srb[request_id]->sc_data_direction == DMA_TO_DEVICE || tw_dev->srb[request_id]->sc_data_direction == DMA_BIDIRECTIONAL) { + struct scatterlist *sg = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; + char *buf = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; + memcpy(tw_dev->generic_buffer_virt[request_id], buf, sg->length); + kunmap_atomic(buf - sg->offset, KM_IRQ0); + } command_packet->sg_list[0].address = tw_dev->generic_buffer_phys[request_id]; command_packet->sg_list[0].length = TW_MIN_SGL_LENGTH; } else { @@ -1888,11 +1897,20 @@ out: /* This function completes an execute scsi operation */ static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id) { - /* Copy the response if too small */ - if ((tw_dev->srb[request_id]->request_buffer) && (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH)) { - memcpy(tw_dev->srb[request_id]->request_buffer, - tw_dev->generic_buffer_virt[request_id], - tw_dev->srb[request_id]->request_bufflen); + if (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH && + (tw_dev->srb[request_id]->sc_data_direction == DMA_FROM_DEVICE || + tw_dev->srb[request_id]->sc_data_direction == DMA_BIDIRECTIONAL)) { + if (tw_dev->srb[request_id]->use_sg == 0) { + memcpy(tw_dev->srb[request_id]->request_buffer, + tw_dev->generic_buffer_virt[request_id], + tw_dev->srb[request_id]->request_bufflen); + } + if (tw_dev->srb[request_id]->use_sg == 1) { + struct scatterlist *sg = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; + char *buf = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; + memcpy(buf, tw_dev->generic_buffer_virt[request_id], sg->length); + kunmap_atomic(buf - sg->offset, KM_IRQ0); + } } } /* End twa_scsiop_execute_scsi_complete() */ diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 2d21265e650..20019b82b4a 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -235,6 +235,13 @@ config SCSI_ISCSI_ATTRS each attached iSCSI device to sysfs, say Y. Otherwise, say N. +config SCSI_SAS_ATTRS + tristate "SAS Transport Attributes" + depends on SCSI + help + If you wish to export transport-specific information about + each attached SAS device to sysfs, say Y. + endmenu menu "SCSI low-level drivers" diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 4b4fd94c267..1e4edbdf273 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_RAID_ATTRS) += raid_class.o obj-$(CONFIG_SCSI_SPI_ATTRS) += scsi_transport_spi.o obj-$(CONFIG_SCSI_FC_ATTRS) += scsi_transport_fc.o obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o +obj-$(CONFIG_SCSI_SAS_ATTRS) += scsi_transport_sas.o obj-$(CONFIG_SCSI_AMIGA7XX) += amiga7xx.o 53c7xx.o obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 0e089a42c03..86eaf6d408d 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -966,21 +966,21 @@ static void lpfc_get_host_fabric_name (struct Scsi_Host *shost) { struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0]; - u64 nodename; + u64 node_name; spin_lock_irq(shost->host_lock); if ((phba->fc_flag & FC_FABRIC) || ((phba->fc_topology == TOPOLOGY_LOOP) && (phba->fc_flag & FC_PUBLIC_LOOP))) - memcpy(&nodename, &phba->fc_fabparam.nodeName, sizeof(u64)); + node_name = wwn_to_u64(phba->fc_fabparam.nodeName.wwn); else /* fabric is local port if there is no F/FL_Port */ - memcpy(&nodename, &phba->fc_nodename, sizeof(u64)); + node_name = wwn_to_u64(phba->fc_nodename.wwn); spin_unlock_irq(shost->host_lock); - fc_host_fabric_name(shost) = be64_to_cpu(nodename); + fc_host_fabric_name(shost) = node_name; } @@ -1103,21 +1103,20 @@ lpfc_get_starget_node_name(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0]; - uint64_t node_name = 0; + u64 node_name = 0; struct lpfc_nodelist *ndlp = NULL; spin_lock_irq(shost->host_lock); /* Search the mapped list for this target ID */ list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) { if (starget->id == ndlp->nlp_sid) { - memcpy(&node_name, &ndlp->nlp_nodename, - sizeof(struct lpfc_name)); + node_name = wwn_to_u64(ndlp->nlp_nodename.wwn); break; } } spin_unlock_irq(shost->host_lock); - fc_starget_node_name(starget) = be64_to_cpu(node_name); + fc_starget_node_name(starget) = node_name; } static void @@ -1125,21 +1124,20 @@ lpfc_get_starget_port_name(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0]; - uint64_t port_name = 0; + u64 port_name = 0; struct lpfc_nodelist *ndlp = NULL; spin_lock_irq(shost->host_lock); /* Search the mapped list for this target ID */ list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) { if (starget->id == ndlp->nlp_sid) { - memcpy(&port_name, &ndlp->nlp_portname, - sizeof(struct lpfc_name)); + port_name = wwn_to_u64(ndlp->nlp_portname.wwn); break; } } spin_unlock_irq(shost->host_lock); - fc_starget_port_name(starget) = be64_to_cpu(port_name); + fc_starget_port_name(starget) = port_name; } static void diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 0a8269d6b13..4fb8eb0c84c 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1017,13 +1017,10 @@ lpfc_register_remote_port(struct lpfc_hba * phba, struct fc_rport *rport; struct lpfc_rport_data *rdata; struct fc_rport_identifiers rport_ids; - uint64_t wwn; /* Remote port has reappeared. Re-register w/ FC transport */ - memcpy(&wwn, &ndlp->nlp_nodename, sizeof(uint64_t)); - rport_ids.node_name = be64_to_cpu(wwn); - memcpy(&wwn, &ndlp->nlp_portname, sizeof(uint64_t)); - rport_ids.port_name = be64_to_cpu(wwn); + rport_ids.node_name = wwn_to_u64(ndlp->nlp_nodename.wwn); + rport_ids.port_name = wwn_to_u64(ndlp->nlp_portname.wwn); rport_ids.port_id = ndlp->nlp_DID; rport_ids.roles = FC_RPORT_ROLE_UNKNOWN; if (ndlp->nlp_type & NLP_FCP_TARGET) diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 21591cb9f55..047a87c26cc 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -262,12 +262,14 @@ struct lpfc_sli_ct_request { #define FF_FRAME_SIZE 2048 struct lpfc_name { + union { + struct { #ifdef __BIG_ENDIAN_BITFIELD - uint8_t nameType:4; /* FC Word 0, bit 28:31 */ - uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ + uint8_t nameType:4; /* FC Word 0, bit 28:31 */ + uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ #else /* __LITTLE_ENDIAN_BITFIELD */ - uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ - uint8_t nameType:4; /* FC Word 0, bit 28:31 */ + uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ + uint8_t nameType:4; /* FC Word 0, bit 28:31 */ #endif #define NAME_IEEE 0x1 /* IEEE name - nameType */ @@ -276,8 +278,11 @@ struct lpfc_name { #define NAME_IP_TYPE 0x4 /* IP address */ #define NAME_CCITT_TYPE 0xC #define NAME_CCITT_GR_TYPE 0xE - uint8_t IEEEextLsb; /* FC Word 0, bit 16:23, IEEE extended Lsb */ - uint8_t IEEE[6]; /* FC IEEE address */ + uint8_t IEEEextLsb; /* FC Word 0, bit 16:23, IEEE extended Lsb */ + uint8_t IEEE[6]; /* FC IEEE address */ + }; + uint8_t wwn[8]; + }; }; struct csp { diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 6f3cb59bf9e..454058f655d 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1333,7 +1333,6 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) unsigned long bar0map_len, bar2map_len; int error = -ENODEV, retval; int i; - u64 wwname; if (pci_enable_device(pdev)) goto out; @@ -1524,10 +1523,8 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) * Must done after lpfc_sli_hba_setup() */ - memcpy(&wwname, &phba->fc_nodename, sizeof(u64)); - fc_host_node_name(host) = be64_to_cpu(wwname); - memcpy(&wwname, &phba->fc_portname, sizeof(u64)); - fc_host_port_name(host) = be64_to_cpu(wwname); + fc_host_node_name(host) = wwn_to_u64(phba->fc_nodename.wwn); + fc_host_port_name(host) = wwn_to_u64(phba->fc_portname.wwn); fc_host_supported_classes(host) = FC_COS_CLASS3; memset(fc_host_supported_fc4s(host), 0, diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index fe0fce71adc..fc25cd83466 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -360,16 +360,16 @@ qla2x00_get_starget_node_name(struct scsi_target *starget) struct Scsi_Host *host = dev_to_shost(starget->dev.parent); scsi_qla_host_t *ha = to_qla_host(host); fc_port_t *fcport; - uint64_t node_name = 0; + u64 node_name = 0; list_for_each_entry(fcport, &ha->fcports, list) { if (starget->id == fcport->os_target_id) { - node_name = *(uint64_t *)fcport->node_name; + node_name = wwn_to_u64(fcport->node_name); break; } } - fc_starget_node_name(starget) = be64_to_cpu(node_name); + fc_starget_node_name(starget) = node_name; } static void @@ -378,16 +378,16 @@ qla2x00_get_starget_port_name(struct scsi_target *starget) struct Scsi_Host *host = dev_to_shost(starget->dev.parent); scsi_qla_host_t *ha = to_qla_host(host); fc_port_t *fcport; - uint64_t port_name = 0; + u64 port_name = 0; list_for_each_entry(fcport, &ha->fcports, list) { if (starget->id == fcport->os_target_id) { - port_name = *(uint64_t *)fcport->port_name; + port_name = wwn_to_u64(fcport->port_name); break; } } - fc_starget_port_name(starget) = be64_to_cpu(port_name); + fc_starget_port_name(starget) = port_name; } static void @@ -460,9 +460,7 @@ struct fc_function_template qla2xxx_transport_functions = { void qla2x00_init_host_attr(scsi_qla_host_t *ha) { - fc_host_node_name(ha->host) = - be64_to_cpu(*(uint64_t *)ha->init_cb->node_name); - fc_host_port_name(ha->host) = - be64_to_cpu(*(uint64_t *)ha->init_cb->port_name); + fc_host_node_name(ha->host) = wwn_to_u64(ha->init_cb->node_name); + fc_host_port_name(ha->host) = wwn_to_u64(ha->init_cb->port_name); fc_host_supported_classes(ha->host) = FC_COS_CLASS3; } diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index c619583e646..3e9b6413787 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -2066,8 +2066,8 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport) return; } - rport_ids.node_name = be64_to_cpu(*(uint64_t *)fcport->node_name); - rport_ids.port_name = be64_to_cpu(*(uint64_t *)fcport->port_name); + rport_ids.node_name = wwn_to_u64(fcport->node_name); + rport_ids.port_name = wwn_to_u64(fcport->port_name); rport_ids.port_id = fcport->d_id.b.domain << 16 | fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa; rport_ids.roles = FC_RPORT_ROLE_UNKNOWN; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 77f2d444f7e..863bb6495da 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -97,6 +97,30 @@ int scsi_insert_special_req(struct scsi_request *sreq, int at_head) } static void scsi_run_queue(struct request_queue *q); +static void scsi_release_buffers(struct scsi_cmnd *cmd); + +/* + * Function: scsi_unprep_request() + * + * Purpose: Remove all preparation done for a request, including its + * associated scsi_cmnd, so that it can be requeued. + * + * Arguments: req - request to unprepare + * + * Lock status: Assumed that no locks are held upon entry. + * + * Returns: Nothing. + */ +static void scsi_unprep_request(struct request *req) +{ + struct scsi_cmnd *cmd = req->special; + + req->flags &= ~REQ_DONTPREP; + req->special = (req->flags & REQ_SPECIAL) ? cmd->sc_request : NULL; + + scsi_release_buffers(cmd); + scsi_put_command(cmd); +} /* * Function: scsi_queue_insert() @@ -116,12 +140,14 @@ static void scsi_run_queue(struct request_queue *q); * commands. * Notes: This could be called either from an interrupt context or a * normal process context. + * Notes: Upon return, cmd is a stale pointer. */ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) { struct Scsi_Host *host = cmd->device->host; struct scsi_device *device = cmd->device; struct request_queue *q = device->request_queue; + struct request *req = cmd->request; unsigned long flags; SCSI_LOG_MLQUEUE(1, @@ -162,8 +188,9 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) * function. The SCSI request function detects the blocked condition * and plugs the queue appropriately. */ + scsi_unprep_request(req); spin_lock_irqsave(q->queue_lock, flags); - blk_requeue_request(q, cmd->request); + blk_requeue_request(q, req); spin_unlock_irqrestore(q->queue_lock, flags); scsi_run_queue(q); @@ -339,7 +366,7 @@ int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd, int result; if (sshdr) { - sense = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL); + sense = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO); if (!sense) return DRIVER_ERROR << 24; memset(sense, 0, SCSI_SENSE_BUFFERSIZE); @@ -552,15 +579,16 @@ static void scsi_run_queue(struct request_queue *q) * I/O errors in the middle of the request, in which case * we need to request the blocks that come after the bad * sector. + * Notes: Upon return, cmd is a stale pointer. */ static void scsi_requeue_command(struct request_queue *q, struct scsi_cmnd *cmd) { + struct request *req = cmd->request; unsigned long flags; - cmd->request->flags &= ~REQ_DONTPREP; - + scsi_unprep_request(req); spin_lock_irqsave(q->queue_lock, flags); - blk_requeue_request(q, cmd->request); + blk_requeue_request(q, req); spin_unlock_irqrestore(q->queue_lock, flags); scsi_run_queue(q); @@ -595,13 +623,14 @@ void scsi_run_host_queues(struct Scsi_Host *shost) * * Lock status: Assumed that lock is not held upon entry. * - * Returns: cmd if requeue done or required, NULL otherwise + * Returns: cmd if requeue required, NULL otherwise. * * Notes: This is called for block device requests in order to * mark some number of sectors as complete. * * We are guaranteeing that the request queue will be goosed * at some point during this call. + * Notes: If cmd was requeued, upon return it will be a stale pointer. */ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int uptodate, int bytes, int requeue) @@ -624,14 +653,15 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int uptodate, if (!uptodate && blk_noretry_request(req)) end_that_request_chunk(req, 0, leftover); else { - if (requeue) + if (requeue) { /* * Bleah. Leftovers again. Stick the * leftovers in the front of the * queue, and goose the queue again. */ scsi_requeue_command(q, cmd); - + cmd = NULL; + } return cmd; } } @@ -857,15 +887,13 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, * requeueing right here - we will requeue down below * when we handle the bad sectors. */ - cmd = scsi_end_request(cmd, 1, good_bytes, result == 0); /* - * If the command completed without error, then either finish off the - * rest of the command, or start a new one. + * If the command completed without error, then either + * finish off the rest of the command, or start a new one. */ - if (result == 0 || cmd == NULL ) { + if (scsi_end_request(cmd, 1, good_bytes, result == 0) == NULL) return; - } } /* * Now, if we were good little boys and girls, Santa left us a request @@ -880,7 +908,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, * and quietly refuse further access. */ cmd->device->changed = 1; - cmd = scsi_end_request(cmd, 0, + scsi_end_request(cmd, 0, this_count, 1); return; } else { @@ -914,7 +942,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, scsi_requeue_command(q, cmd); result = 0; } else { - cmd = scsi_end_request(cmd, 0, this_count, 1); + scsi_end_request(cmd, 0, this_count, 1); return; } break; @@ -931,7 +959,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, dev_printk(KERN_INFO, &cmd->device->sdev_gendev, "Device not ready.\n"); - cmd = scsi_end_request(cmd, 0, this_count, 1); + scsi_end_request(cmd, 0, this_count, 1); return; case VOLUME_OVERFLOW: if (!(req->flags & REQ_QUIET)) { @@ -941,7 +969,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, __scsi_print_command(cmd->data_cmnd); scsi_print_sense("", cmd); } - cmd = scsi_end_request(cmd, 0, block_bytes, 1); + scsi_end_request(cmd, 0, block_bytes, 1); return; default: break; @@ -972,7 +1000,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, block_bytes = req->hard_cur_sectors << 9; if (!block_bytes) block_bytes = req->data_len; - cmd = scsi_end_request(cmd, 0, block_bytes, 1); + scsi_end_request(cmd, 0, block_bytes, 1); } } EXPORT_SYMBOL(scsi_io_completion); @@ -1118,7 +1146,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) if (unlikely(!scsi_device_online(sdev))) { printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to offline device\n", sdev->host->host_no, sdev->id, sdev->lun); - return BLKPREP_KILL; + goto kill; } if (unlikely(sdev->sdev_state != SDEV_RUNNING)) { /* OK, we're not in a running state don't prep @@ -1128,7 +1156,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) * at all allowed down */ printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to dead device\n", sdev->host->host_no, sdev->id, sdev->lun); - return BLKPREP_KILL; + goto kill; } /* OK, we only allow special commands (i.e. not * user initiated ones */ @@ -1160,11 +1188,11 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) if(unlikely(specials_only) && !(req->flags & REQ_SPECIAL)) { if(specials_only == SDEV_QUIESCE || specials_only == SDEV_BLOCK) - return BLKPREP_DEFER; + goto defer; printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to device being removed\n", sdev->host->host_no, sdev->id, sdev->lun); - return BLKPREP_KILL; + goto kill; } @@ -1182,7 +1210,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) cmd->tag = req->tag; } else { blk_dump_rq_flags(req, "SCSI bad req"); - return BLKPREP_KILL; + goto kill; } /* note the overloading of req->special. When the tag @@ -1220,8 +1248,13 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) * required). */ ret = scsi_init_io(cmd); - if (ret) /* BLKPREP_KILL return also releases the command */ - return ret; + switch(ret) { + case BLKPREP_KILL: + /* BLKPREP_KILL return also releases the command */ + goto kill; + case BLKPREP_DEFER: + goto defer; + } /* * Initialize the actual SCSI command for this request. @@ -1231,7 +1264,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) if (unlikely(!drv->init_command(cmd))) { scsi_release_buffers(cmd); scsi_put_command(cmd); - return BLKPREP_KILL; + goto kill; } } else { memcpy(cmd->cmnd, req->cmd, sizeof(cmd->cmnd)); @@ -1262,6 +1295,9 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) if (sdev->device_busy == 0) blk_plug_device(q); return BLKPREP_DEFER; + kill: + req->errors = DID_NO_CONNECT << 16; + return BLKPREP_KILL; } /* @@ -1336,19 +1372,24 @@ static inline int scsi_host_queue_ready(struct request_queue *q, } /* - * Kill requests for a dead device + * Kill a request for a dead device */ -static void scsi_kill_requests(request_queue_t *q) +static void scsi_kill_request(struct request *req, request_queue_t *q) { - struct request *req; + struct scsi_cmnd *cmd = req->special; + + blkdev_dequeue_request(req); - while ((req = elv_next_request(q)) != NULL) { - blkdev_dequeue_request(req); - req->flags |= REQ_QUIET; - while (end_that_request_first(req, 0, req->nr_sectors)) - ; - end_that_request_last(req); + if (unlikely(cmd == NULL)) { + printk(KERN_CRIT "impossible request in %s.\n", + __FUNCTION__); + BUG(); } + + scsi_init_cmd_errh(cmd); + cmd->result = DID_NO_CONNECT << 16; + atomic_inc(&cmd->device->iorequest_cnt); + __scsi_done(cmd); } /* @@ -1371,7 +1412,8 @@ static void scsi_request_fn(struct request_queue *q) if (!sdev) { printk("scsi: killing requests for dead queue\n"); - scsi_kill_requests(q); + while ((req = elv_next_request(q)) != NULL) + scsi_kill_request(req, q); return; } @@ -1398,11 +1440,7 @@ static void scsi_request_fn(struct request_queue *q) if (unlikely(!scsi_device_online(sdev))) { printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to offline device\n", sdev->host->host_no, sdev->id, sdev->lun); - blkdev_dequeue_request(req); - req->flags |= REQ_QUIET; - while (end_that_request_first(req, 0, req->nr_sectors)) - ; - end_that_request_last(req); + scsi_kill_request(req, q); continue; } @@ -1415,6 +1453,14 @@ static void scsi_request_fn(struct request_queue *q) sdev->device_busy++; spin_unlock(q->queue_lock); + cmd = req->special; + if (unlikely(cmd == NULL)) { + printk(KERN_CRIT "impossible request in %s.\n" + "please mail a stack trace to " + "linux-scsi@vger.kernel.org", + __FUNCTION__); + BUG(); + } spin_lock(shost->host_lock); if (!scsi_host_queue_ready(q, shost, sdev)) @@ -1433,15 +1479,6 @@ static void scsi_request_fn(struct request_queue *q) */ spin_unlock_irq(shost->host_lock); - cmd = req->special; - if (unlikely(cmd == NULL)) { - printk(KERN_CRIT "impossible request in %s.\n" - "please mail a stack trace to " - "linux-scsi@vger.kernel.org", - __FUNCTION__); - BUG(); - } - /* * Finally, initialize any error handling parameters, and set up * the timers for timeouts. @@ -1477,6 +1514,7 @@ static void scsi_request_fn(struct request_queue *q) * cases (host limits or settings) should run the queue at some * later time. */ + scsi_unprep_request(req); spin_lock_irq(q->queue_lock); blk_requeue_request(q, req); sdev->device_busy--; diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index ee6de1768e5..d05f778d31a 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -124,6 +124,7 @@ extern void scsi_sysfs_unregister(void); extern void scsi_sysfs_device_initialize(struct scsi_device *); extern int scsi_sysfs_target_initialize(struct scsi_device *); extern struct scsi_transport_template blank_transport_template; +extern void __scsi_remove_device(struct scsi_device *); extern struct bus_type scsi_bus_type; diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 19c9a232a75..b86f170fa8e 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -870,8 +870,12 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, out_free_sdev: if (res == SCSI_SCAN_LUN_PRESENT) { if (sdevp) { - scsi_device_get(sdev); - *sdevp = sdev; + if (scsi_device_get(sdev) == 0) { + *sdevp = sdev; + } else { + __scsi_remove_device(sdev); + res = SCSI_SCAN_NO_RESPONSE; + } } } else { if (sdev->host->hostt->slave_destroy) @@ -1260,6 +1264,19 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, } EXPORT_SYMBOL(__scsi_add_device); +int scsi_add_device(struct Scsi_Host *host, uint channel, + uint target, uint lun) +{ + struct scsi_device *sdev = + __scsi_add_device(host, channel, target, lun, NULL); + if (IS_ERR(sdev)) + return PTR_ERR(sdev); + + scsi_device_put(sdev); + return 0; +} +EXPORT_SYMBOL(scsi_add_device); + void scsi_rescan_device(struct device *dev) { struct scsi_driver *drv; @@ -1276,27 +1293,8 @@ void scsi_rescan_device(struct device *dev) } EXPORT_SYMBOL(scsi_rescan_device); -/** - * scsi_scan_target - scan a target id, possibly including all LUNs on the - * target. - * @sdevsca: Scsi_Device handle for scanning - * @shost: host to scan - * @channel: channel to scan - * @id: target id to scan - * - * Description: - * Scan the target id on @shost, @channel, and @id. Scan at least LUN - * 0, and possibly all LUNs on the target id. - * - * Use the pre-allocated @sdevscan as a handle for the scanning. This - * function sets sdevscan->host, sdevscan->id and sdevscan->lun; the - * scanning functions modify sdevscan->lun. - * - * First try a REPORT LUN scan, if that does not scan the target, do a - * sequential scan of LUNs on the target id. - **/ -void scsi_scan_target(struct device *parent, unsigned int channel, - unsigned int id, unsigned int lun, int rescan) +static void __scsi_scan_target(struct device *parent, unsigned int channel, + unsigned int id, unsigned int lun, int rescan) { struct Scsi_Host *shost = dev_to_shost(parent); int bflags = 0; @@ -1310,9 +1308,7 @@ void scsi_scan_target(struct device *parent, unsigned int channel, */ return; - starget = scsi_alloc_target(parent, channel, id); - if (!starget) return; @@ -1358,6 +1354,33 @@ void scsi_scan_target(struct device *parent, unsigned int channel, put_device(&starget->dev); } + +/** + * scsi_scan_target - scan a target id, possibly including all LUNs on the + * target. + * @parent: host to scan + * @channel: channel to scan + * @id: target id to scan + * @lun: Specific LUN to scan or SCAN_WILD_CARD + * @rescan: passed to LUN scanning routines + * + * Description: + * Scan the target id on @parent, @channel, and @id. Scan at least LUN 0, + * and possibly all LUNs on the target id. + * + * First try a REPORT LUN scan, if that does not scan the target, do a + * sequential scan of LUNs on the target id. + **/ +void scsi_scan_target(struct device *parent, unsigned int channel, + unsigned int id, unsigned int lun, int rescan) +{ + struct Scsi_Host *shost = dev_to_shost(parent); + + down(&shost->scan_mutex); + if (scsi_host_scan_allowed(shost)) + __scsi_scan_target(parent, channel, id, lun, rescan); + up(&shost->scan_mutex); +} EXPORT_SYMBOL(scsi_scan_target); static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel, @@ -1383,10 +1406,12 @@ static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel, order_id = shost->max_id - id - 1; else order_id = id; - scsi_scan_target(&shost->shost_gendev, channel, order_id, lun, rescan); + __scsi_scan_target(&shost->shost_gendev, channel, + order_id, lun, rescan); } else - scsi_scan_target(&shost->shost_gendev, channel, id, lun, rescan); + __scsi_scan_target(&shost->shost_gendev, channel, + id, lun, rescan); } int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, @@ -1484,12 +1509,15 @@ void scsi_forget_host(struct Scsi_Host *shost) */ struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost) { - struct scsi_device *sdev; + struct scsi_device *sdev = NULL; struct scsi_target *starget; + down(&shost->scan_mutex); + if (!scsi_host_scan_allowed(shost)) + goto out; starget = scsi_alloc_target(&shost->shost_gendev, 0, shost->this_id); if (!starget) - return NULL; + goto out; sdev = scsi_alloc_sdev(starget, 0, NULL); if (sdev) { @@ -1497,6 +1525,8 @@ struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost) sdev->borken = 0; } put_device(&starget->dev); + out: + up(&shost->scan_mutex); return sdev; } EXPORT_SYMBOL(scsi_get_host_dev); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index dae59d1da07..b8052d5206c 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -653,7 +653,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) error = attr_add(&sdev->sdev_gendev, sdev->host->hostt->sdev_attrs[i]); if (error) { - scsi_remove_device(sdev); + __scsi_remove_device(sdev); goto out; } } @@ -667,7 +667,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) scsi_sysfs_sdev_attrs[i]); error = device_create_file(&sdev->sdev_gendev, attr); if (error) { - scsi_remove_device(sdev); + __scsi_remove_device(sdev); goto out; } } @@ -687,17 +687,10 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) return error; } -/** - * scsi_remove_device - unregister a device from the scsi bus - * @sdev: scsi_device to unregister - **/ -void scsi_remove_device(struct scsi_device *sdev) +void __scsi_remove_device(struct scsi_device *sdev) { - struct Scsi_Host *shost = sdev->host; - - down(&shost->scan_mutex); if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0) - goto out; + return; class_device_unregister(&sdev->sdev_classdev); device_del(&sdev->sdev_gendev); @@ -706,8 +699,17 @@ void scsi_remove_device(struct scsi_device *sdev) sdev->host->hostt->slave_destroy(sdev); transport_unregister_device(&sdev->sdev_gendev); put_device(&sdev->sdev_gendev); -out: - up(&shost->scan_mutex); +} + +/** + * scsi_remove_device - unregister a device from the scsi bus + * @sdev: scsi_device to unregister + **/ +void scsi_remove_device(struct scsi_device *sdev) +{ + down(&sdev->host->scan_mutex); + __scsi_remove_device(sdev); + up(&sdev->host->scan_mutex); } EXPORT_SYMBOL(scsi_remove_device); diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c new file mode 100644 index 00000000000..ff724bbe661 --- /dev/null +++ b/drivers/scsi/scsi_transport_sas.c @@ -0,0 +1,820 @@ +/* + * Copyright (C) 2005 Dell Inc. + * Released under GPL v2. + * + * Serial Attached SCSI (SAS) transport class. + * + * The SAS transport class contains common code to deal with SAS HBAs, + * an aproximated representation of SAS topologies in the driver model, + * and various sysfs attributes to expose these topologies and managment + * interfaces to userspace. + * + * In addition to the basic SCSI core objects this transport class + * introduces two additional intermediate objects: The SAS PHY + * as represented by struct sas_phy defines an "outgoing" PHY on + * a SAS HBA or Expander, and the SAS remote PHY represented by + * struct sas_rphy defines an "incoming" PHY on a SAS Expander or + * end device. Note that this is purely a software concept, the + * underlying hardware for a PHY and a remote PHY is the exactly + * the same. + * + * There is no concept of a SAS port in this code, users can see + * what PHYs form a wide port based on the port_identifier attribute, + * which is the same for all PHYs in a port. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/err.h> + +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_transport.h> +#include <scsi/scsi_transport_sas.h> + + +#define SAS_HOST_ATTRS 0 +#define SAS_PORT_ATTRS 11 +#define SAS_RPORT_ATTRS 5 + +struct sas_internal { + struct scsi_transport_template t; + struct sas_function_template *f; + + struct class_device_attribute private_host_attrs[SAS_HOST_ATTRS]; + struct class_device_attribute private_phy_attrs[SAS_PORT_ATTRS]; + struct class_device_attribute private_rphy_attrs[SAS_RPORT_ATTRS]; + + struct transport_container phy_attr_cont; + struct transport_container rphy_attr_cont; + + /* + * The array of null terminated pointers to attributes + * needed by scsi_sysfs.c + */ + struct class_device_attribute *host_attrs[SAS_HOST_ATTRS + 1]; + struct class_device_attribute *phy_attrs[SAS_PORT_ATTRS + 1]; + struct class_device_attribute *rphy_attrs[SAS_RPORT_ATTRS + 1]; +}; +#define to_sas_internal(tmpl) container_of(tmpl, struct sas_internal, t) + +struct sas_host_attrs { + struct list_head rphy_list; + spinlock_t lock; + u32 next_target_id; +}; +#define to_sas_host_attrs(host) ((struct sas_host_attrs *)(host)->shost_data) + + +/* + * Hack to allow attributes of the same name in different objects. + */ +#define SAS_CLASS_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \ + struct class_device_attribute class_device_attr_##_prefix##_##_name = \ + __ATTR(_name,_mode,_show,_store) + + +/* + * Pretty printing helpers + */ + +#define sas_bitfield_name_match(title, table) \ +static ssize_t \ +get_sas_##title##_names(u32 table_key, char *buf) \ +{ \ + char *prefix = ""; \ + ssize_t len = 0; \ + int i; \ + \ + for (i = 0; i < sizeof(table)/sizeof(table[0]); i++) { \ + if (table[i].value & table_key) { \ + len += sprintf(buf + len, "%s%s", \ + prefix, table[i].name); \ + prefix = ", "; \ + } \ + } \ + len += sprintf(buf + len, "\n"); \ + return len; \ +} + +#define sas_bitfield_name_search(title, table) \ +static ssize_t \ +get_sas_##title##_names(u32 table_key, char *buf) \ +{ \ + ssize_t len = 0; \ + int i; \ + \ + for (i = 0; i < sizeof(table)/sizeof(table[0]); i++) { \ + if (table[i].value == table_key) { \ + len += sprintf(buf + len, "%s", \ + table[i].name); \ + break; \ + } \ + } \ + len += sprintf(buf + len, "\n"); \ + return len; \ +} + +static struct { + u32 value; + char *name; +} sas_device_type_names[] = { + { SAS_PHY_UNUSED, "unused" }, + { SAS_END_DEVICE, "end device" }, + { SAS_EDGE_EXPANDER_DEVICE, "edge expander" }, + { SAS_FANOUT_EXPANDER_DEVICE, "fanout expander" }, +}; +sas_bitfield_name_search(device_type, sas_device_type_names) + + +static struct { + u32 value; + char *name; +} sas_protocol_names[] = { + { SAS_PROTOCOL_SATA, "sata" }, + { SAS_PROTOCOL_SMP, "smp" }, + { SAS_PROTOCOL_STP, "stp" }, + { SAS_PROTOCOL_SSP, "ssp" }, +}; +sas_bitfield_name_match(protocol, sas_protocol_names) + +static struct { + u32 value; + char *name; +} sas_linkspeed_names[] = { + { SAS_LINK_RATE_UNKNOWN, "Unknown" }, + { SAS_PHY_DISABLED, "Phy disabled" }, + { SAS_LINK_RATE_FAILED, "Link Rate failed" }, + { SAS_SATA_SPINUP_HOLD, "Spin-up hold" }, + { SAS_LINK_RATE_1_5_GBPS, "1.5 Gbit" }, + { SAS_LINK_RATE_3_0_GBPS, "3.0 Gbit" }, +}; +sas_bitfield_name_search(linkspeed, sas_linkspeed_names) + + +/* + * SAS host attributes + */ + +static int sas_host_setup(struct transport_container *tc, struct device *dev, + struct class_device *cdev) +{ + struct Scsi_Host *shost = dev_to_shost(dev); + struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); + + INIT_LIST_HEAD(&sas_host->rphy_list); + spin_lock_init(&sas_host->lock); + sas_host->next_target_id = 0; + return 0; +} + +static DECLARE_TRANSPORT_CLASS(sas_host_class, + "sas_host", sas_host_setup, NULL, NULL); + +static int sas_host_match(struct attribute_container *cont, + struct device *dev) +{ + struct Scsi_Host *shost; + struct sas_internal *i; + + if (!scsi_is_host_device(dev)) + return 0; + shost = dev_to_shost(dev); + + if (!shost->transportt) + return 0; + if (shost->transportt->host_attrs.ac.class != + &sas_host_class.class) + return 0; + + i = to_sas_internal(shost->transportt); + return &i->t.host_attrs.ac == cont; +} + +static int do_sas_phy_delete(struct device *dev, void *data) +{ + if (scsi_is_sas_phy(dev)) + sas_phy_delete(dev_to_phy(dev)); + return 0; +} + +/** + * sas_remove_host -- tear down a Scsi_Host's SAS data structures + * @shost: Scsi Host that is torn down + * + * Removes all SAS PHYs and remote PHYs for a given Scsi_Host. + * Must be called just before scsi_remove_host for SAS HBAs. + */ +void sas_remove_host(struct Scsi_Host *shost) +{ + device_for_each_child(&shost->shost_gendev, NULL, do_sas_phy_delete); +} +EXPORT_SYMBOL(sas_remove_host); + + +/* + * SAS Port attributes + */ + +#define sas_phy_show_simple(field, name, format_string, cast) \ +static ssize_t \ +show_sas_phy_##name(struct class_device *cdev, char *buf) \ +{ \ + struct sas_phy *phy = transport_class_to_phy(cdev); \ + \ + return snprintf(buf, 20, format_string, cast phy->field); \ +} + +#define sas_phy_simple_attr(field, name, format_string, type) \ + sas_phy_show_simple(field, name, format_string, (type)) \ +static CLASS_DEVICE_ATTR(name, S_IRUGO, show_sas_phy_##name, NULL) + +#define sas_phy_show_protocol(field, name) \ +static ssize_t \ +show_sas_phy_##name(struct class_device *cdev, char *buf) \ +{ \ + struct sas_phy *phy = transport_class_to_phy(cdev); \ + \ + if (!phy->field) \ + return snprintf(buf, 20, "none\n"); \ + return get_sas_protocol_names(phy->field, buf); \ +} + +#define sas_phy_protocol_attr(field, name) \ + sas_phy_show_protocol(field, name) \ +static CLASS_DEVICE_ATTR(name, S_IRUGO, show_sas_phy_##name, NULL) + +#define sas_phy_show_linkspeed(field) \ +static ssize_t \ +show_sas_phy_##field(struct class_device *cdev, char *buf) \ +{ \ + struct sas_phy *phy = transport_class_to_phy(cdev); \ + \ + return get_sas_linkspeed_names(phy->field, buf); \ +} + +#define sas_phy_linkspeed_attr(field) \ + sas_phy_show_linkspeed(field) \ +static CLASS_DEVICE_ATTR(field, S_IRUGO, show_sas_phy_##field, NULL) + +static ssize_t +show_sas_device_type(struct class_device *cdev, char *buf) +{ + struct sas_phy *phy = transport_class_to_phy(cdev); + + if (!phy->identify.device_type) + return snprintf(buf, 20, "none\n"); + return get_sas_device_type_names(phy->identify.device_type, buf); +} + +static CLASS_DEVICE_ATTR(device_type, S_IRUGO, show_sas_device_type, NULL); + +sas_phy_protocol_attr(identify.initiator_port_protocols, + initiator_port_protocols); +sas_phy_protocol_attr(identify.target_port_protocols, + target_port_protocols); +sas_phy_simple_attr(identify.sas_address, sas_address, "0x%016llx\n", + unsigned long long); +sas_phy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8); +sas_phy_simple_attr(port_identifier, port_identifier, "%d\n", u8); +sas_phy_linkspeed_attr(negotiated_linkrate); +sas_phy_linkspeed_attr(minimum_linkrate_hw); +sas_phy_linkspeed_attr(minimum_linkrate); +sas_phy_linkspeed_attr(maximum_linkrate_hw); +sas_phy_linkspeed_attr(maximum_linkrate); + + +static DECLARE_TRANSPORT_CLASS(sas_phy_class, + "sas_phy", NULL, NULL, NULL); + +static int sas_phy_match(struct attribute_container *cont, struct device *dev) +{ + struct Scsi_Host *shost; + struct sas_internal *i; + + if (!scsi_is_sas_phy(dev)) + return 0; + shost = dev_to_shost(dev->parent); + + if (!shost->transportt) + return 0; + if (shost->transportt->host_attrs.ac.class != + &sas_host_class.class) + return 0; + + i = to_sas_internal(shost->transportt); + return &i->phy_attr_cont.ac == cont; +} + +static void sas_phy_release(struct device *dev) +{ + struct sas_phy *phy = dev_to_phy(dev); + + put_device(dev->parent); + kfree(phy); +} + +/** + * sas_phy_alloc -- allocates and initialize a SAS PHY structure + * @parent: Parent device + * @number: Port number + * + * Allocates an SAS PHY structure. It will be added in the device tree + * below the device specified by @parent, which has to be either a Scsi_Host + * or sas_rphy. + * + * Returns: + * SAS PHY allocated or %NULL if the allocation failed. + */ +struct sas_phy *sas_phy_alloc(struct device *parent, int number) +{ + struct Scsi_Host *shost = dev_to_shost(parent); + struct sas_phy *phy; + + phy = kmalloc(sizeof(*phy), GFP_KERNEL); + if (!phy) + return NULL; + memset(phy, 0, sizeof(*phy)); + + get_device(parent); + + phy->number = number; + + device_initialize(&phy->dev); + phy->dev.parent = get_device(parent); + phy->dev.release = sas_phy_release; + sprintf(phy->dev.bus_id, "phy-%d:%d", shost->host_no, number); + + transport_setup_device(&phy->dev); + + return phy; +} +EXPORT_SYMBOL(sas_phy_alloc); + +/** + * sas_phy_add -- add a SAS PHY to the device hierachy + * @phy: The PHY to be added + * + * Publishes a SAS PHY to the rest of the system. + */ +int sas_phy_add(struct sas_phy *phy) +{ + int error; + + error = device_add(&phy->dev); + if (!error) { + transport_add_device(&phy->dev); + transport_configure_device(&phy->dev); + } + + return error; +} +EXPORT_SYMBOL(sas_phy_add); + +/** + * sas_phy_free -- free a SAS PHY + * @phy: SAS PHY to free + * + * Frees the specified SAS PHY. + * + * Note: + * This function must only be called on a PHY that has not + * sucessfully been added using sas_phy_add(). + */ +void sas_phy_free(struct sas_phy *phy) +{ + transport_destroy_device(&phy->dev); + put_device(phy->dev.parent); + put_device(phy->dev.parent); + put_device(phy->dev.parent); + kfree(phy); +} +EXPORT_SYMBOL(sas_phy_free); + +/** + * sas_phy_delete -- remove SAS PHY + * @phy: SAS PHY to remove + * + * Removes the specified SAS PHY. If the SAS PHY has an + * associated remote PHY it is removed before. + */ +void +sas_phy_delete(struct sas_phy *phy) +{ + struct device *dev = &phy->dev; + + if (phy->rphy) + sas_rphy_delete(phy->rphy); + + transport_remove_device(dev); + device_del(dev); + transport_destroy_device(dev); + put_device(dev->parent); +} +EXPORT_SYMBOL(sas_phy_delete); + +/** + * scsi_is_sas_phy -- check if a struct device represents a SAS PHY + * @dev: device to check + * + * Returns: + * %1 if the device represents a SAS PHY, %0 else + */ +int scsi_is_sas_phy(const struct device *dev) +{ + return dev->release == sas_phy_release; +} +EXPORT_SYMBOL(scsi_is_sas_phy); + +/* + * SAS remote PHY attributes. + */ + +#define sas_rphy_show_simple(field, name, format_string, cast) \ +static ssize_t \ +show_sas_rphy_##name(struct class_device *cdev, char *buf) \ +{ \ + struct sas_rphy *rphy = transport_class_to_rphy(cdev); \ + \ + return snprintf(buf, 20, format_string, cast rphy->field); \ +} + +#define sas_rphy_simple_attr(field, name, format_string, type) \ + sas_rphy_show_simple(field, name, format_string, (type)) \ +static SAS_CLASS_DEVICE_ATTR(rphy, name, S_IRUGO, \ + show_sas_rphy_##name, NULL) + +#define sas_rphy_show_protocol(field, name) \ +static ssize_t \ +show_sas_rphy_##name(struct class_device *cdev, char *buf) \ +{ \ + struct sas_rphy *rphy = transport_class_to_rphy(cdev); \ + \ + if (!rphy->field) \ + return snprintf(buf, 20, "none\n"); \ + return get_sas_protocol_names(rphy->field, buf); \ +} + +#define sas_rphy_protocol_attr(field, name) \ + sas_rphy_show_protocol(field, name) \ +static SAS_CLASS_DEVICE_ATTR(rphy, name, S_IRUGO, \ + show_sas_rphy_##name, NULL) + +static ssize_t +show_sas_rphy_device_type(struct class_device *cdev, char *buf) +{ + struct sas_rphy *rphy = transport_class_to_rphy(cdev); + + if (!rphy->identify.device_type) + return snprintf(buf, 20, "none\n"); + return get_sas_device_type_names( + rphy->identify.device_type, buf); +} + +static SAS_CLASS_DEVICE_ATTR(rphy, device_type, S_IRUGO, + show_sas_rphy_device_type, NULL); + +sas_rphy_protocol_attr(identify.initiator_port_protocols, + initiator_port_protocols); +sas_rphy_protocol_attr(identify.target_port_protocols, target_port_protocols); +sas_rphy_simple_attr(identify.sas_address, sas_address, "0x%016llx\n", + unsigned long long); +sas_rphy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8); + +static DECLARE_TRANSPORT_CLASS(sas_rphy_class, + "sas_rphy", NULL, NULL, NULL); + +static int sas_rphy_match(struct attribute_container *cont, struct device *dev) +{ + struct Scsi_Host *shost; + struct sas_internal *i; + + if (!scsi_is_sas_rphy(dev)) + return 0; + shost = dev_to_shost(dev->parent->parent); + + if (!shost->transportt) + return 0; + if (shost->transportt->host_attrs.ac.class != + &sas_host_class.class) + return 0; + + i = to_sas_internal(shost->transportt); + return &i->rphy_attr_cont.ac == cont; +} + +static void sas_rphy_release(struct device *dev) +{ + struct sas_rphy *rphy = dev_to_rphy(dev); + + put_device(dev->parent); + kfree(rphy); +} + +/** + * sas_rphy_alloc -- allocates and initialize a SAS remote PHY structure + * @parent: SAS PHY this remote PHY is conneted to + * + * Allocates an SAS remote PHY structure, connected to @parent. + * + * Returns: + * SAS PHY allocated or %NULL if the allocation failed. + */ +struct sas_rphy *sas_rphy_alloc(struct sas_phy *parent) +{ + struct Scsi_Host *shost = dev_to_shost(&parent->dev); + struct sas_rphy *rphy; + + rphy = kmalloc(sizeof(*rphy), GFP_KERNEL); + if (!rphy) { + put_device(&parent->dev); + return NULL; + } + memset(rphy, 0, sizeof(*rphy)); + + device_initialize(&rphy->dev); + rphy->dev.parent = get_device(&parent->dev); + rphy->dev.release = sas_rphy_release; + sprintf(rphy->dev.bus_id, "rphy-%d:%d", + shost->host_no, parent->number); + transport_setup_device(&rphy->dev); + + return rphy; +} +EXPORT_SYMBOL(sas_rphy_alloc); + +/** + * sas_rphy_add -- add a SAS remote PHY to the device hierachy + * @rphy: The remote PHY to be added + * + * Publishes a SAS remote PHY to the rest of the system. + */ +int sas_rphy_add(struct sas_rphy *rphy) +{ + struct sas_phy *parent = dev_to_phy(rphy->dev.parent); + struct Scsi_Host *shost = dev_to_shost(parent->dev.parent); + struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); + struct sas_identify *identify = &rphy->identify; + int error; + + if (parent->rphy) + return -ENXIO; + parent->rphy = rphy; + + error = device_add(&rphy->dev); + if (error) + return error; + transport_add_device(&rphy->dev); + transport_configure_device(&rphy->dev); + + spin_lock(&sas_host->lock); + list_add_tail(&rphy->list, &sas_host->rphy_list); + if (identify->device_type == SAS_END_DEVICE && + (identify->target_port_protocols & + (SAS_PROTOCOL_SSP|SAS_PROTOCOL_STP|SAS_PROTOCOL_SATA))) + rphy->scsi_target_id = sas_host->next_target_id++; + else + rphy->scsi_target_id = -1; + spin_unlock(&sas_host->lock); + + if (rphy->scsi_target_id != -1) { + scsi_scan_target(&rphy->dev, parent->number, + rphy->scsi_target_id, ~0, 0); + } + + return 0; +} +EXPORT_SYMBOL(sas_rphy_add); + +/** + * sas_rphy_free -- free a SAS remote PHY + * @rphy SAS remote PHY to free + * + * Frees the specified SAS remote PHY. + * + * Note: + * This function must only be called on a remote + * PHY that has not sucessfully been added using + * sas_rphy_add(). + */ +void sas_rphy_free(struct sas_rphy *rphy) +{ + struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent); + struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); + + spin_lock(&sas_host->lock); + list_del(&rphy->list); + spin_unlock(&sas_host->lock); + + transport_destroy_device(&rphy->dev); + put_device(rphy->dev.parent); + put_device(rphy->dev.parent); + put_device(rphy->dev.parent); + kfree(rphy); +} +EXPORT_SYMBOL(sas_rphy_free); + +/** + * sas_rphy_delete -- remove SAS remote PHY + * @rphy: SAS remote PHY to remove + * + * Removes the specified SAS remote PHY. + */ +void +sas_rphy_delete(struct sas_rphy *rphy) +{ + struct device *dev = &rphy->dev; + struct sas_phy *parent = dev_to_phy(dev->parent); + struct Scsi_Host *shost = dev_to_shost(parent->dev.parent); + struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); + + transport_destroy_device(&rphy->dev); + + scsi_remove_target(&rphy->dev); + + spin_lock(&sas_host->lock); + list_del(&rphy->list); + spin_unlock(&sas_host->lock); + + transport_remove_device(dev); + device_del(dev); + transport_destroy_device(dev); + put_device(&parent->dev); +} +EXPORT_SYMBOL(sas_rphy_delete); + +/** + * scsi_is_sas_rphy -- check if a struct device represents a SAS remote PHY + * @dev: device to check + * + * Returns: + * %1 if the device represents a SAS remote PHY, %0 else + */ +int scsi_is_sas_rphy(const struct device *dev) +{ + return dev->release == sas_rphy_release; +} +EXPORT_SYMBOL(scsi_is_sas_rphy); + + +/* + * SCSI scan helper + */ + +static struct device *sas_target_parent(struct Scsi_Host *shost, + int channel, uint id) +{ + struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); + struct sas_rphy *rphy; + struct device *dev = NULL; + + spin_lock(&sas_host->lock); + list_for_each_entry(rphy, &sas_host->rphy_list, list) { + struct sas_phy *parent = dev_to_phy(rphy->dev.parent); + if (parent->number == channel && + rphy->scsi_target_id == id) + dev = &rphy->dev; + } + spin_unlock(&sas_host->lock); + + return dev; +} + + +/* + * Setup / Teardown code + */ + +#define SETUP_RPORT_ATTRIBUTE(field) \ + i->private_rphy_attrs[count] = class_device_attr_##field; \ + i->private_rphy_attrs[count].attr.mode = S_IRUGO; \ + i->private_rphy_attrs[count].store = NULL; \ + i->rphy_attrs[count] = &i->private_rphy_attrs[count]; \ + count++ + +#define SETUP_PORT_ATTRIBUTE(field) \ + i->private_phy_attrs[count] = class_device_attr_##field; \ + i->private_phy_attrs[count].attr.mode = S_IRUGO; \ + i->private_phy_attrs[count].store = NULL; \ + i->phy_attrs[count] = &i->private_phy_attrs[count]; \ + count++ + + +/** + * sas_attach_transport -- instantiate SAS transport template + * @ft: SAS transport class function template + */ +struct scsi_transport_template * +sas_attach_transport(struct sas_function_template *ft) +{ + struct sas_internal *i; + int count; + + i = kmalloc(sizeof(struct sas_internal), GFP_KERNEL); + if (!i) + return NULL; + memset(i, 0, sizeof(struct sas_internal)); + + i->t.target_parent = sas_target_parent; + + i->t.host_attrs.ac.attrs = &i->host_attrs[0]; + i->t.host_attrs.ac.class = &sas_host_class.class; + i->t.host_attrs.ac.match = sas_host_match; + transport_container_register(&i->t.host_attrs); + i->t.host_size = sizeof(struct sas_host_attrs); + + i->phy_attr_cont.ac.class = &sas_phy_class.class; + i->phy_attr_cont.ac.attrs = &i->phy_attrs[0]; + i->phy_attr_cont.ac.match = sas_phy_match; + transport_container_register(&i->phy_attr_cont); + + i->rphy_attr_cont.ac.class = &sas_rphy_class.class; + i->rphy_attr_cont.ac.attrs = &i->rphy_attrs[0]; + i->rphy_attr_cont.ac.match = sas_rphy_match; + transport_container_register(&i->rphy_attr_cont); + + i->f = ft; + + count = 0; + i->host_attrs[count] = NULL; + + count = 0; + SETUP_PORT_ATTRIBUTE(initiator_port_protocols); + SETUP_PORT_ATTRIBUTE(target_port_protocols); + SETUP_PORT_ATTRIBUTE(device_type); + SETUP_PORT_ATTRIBUTE(sas_address); + SETUP_PORT_ATTRIBUTE(phy_identifier); + SETUP_PORT_ATTRIBUTE(port_identifier); + SETUP_PORT_ATTRIBUTE(negotiated_linkrate); + SETUP_PORT_ATTRIBUTE(minimum_linkrate_hw); + SETUP_PORT_ATTRIBUTE(minimum_linkrate); + SETUP_PORT_ATTRIBUTE(maximum_linkrate_hw); + SETUP_PORT_ATTRIBUTE(maximum_linkrate); + i->phy_attrs[count] = NULL; + + count = 0; + SETUP_RPORT_ATTRIBUTE(rphy_initiator_port_protocols); + SETUP_RPORT_ATTRIBUTE(rphy_target_port_protocols); + SETUP_RPORT_ATTRIBUTE(rphy_device_type); + SETUP_RPORT_ATTRIBUTE(rphy_sas_address); + SETUP_RPORT_ATTRIBUTE(rphy_phy_identifier); + i->rphy_attrs[count] = NULL; + + return &i->t; +} +EXPORT_SYMBOL(sas_attach_transport); + +/** + * sas_release_transport -- release SAS transport template instance + * @t: transport template instance + */ +void sas_release_transport(struct scsi_transport_template *t) +{ + struct sas_internal *i = to_sas_internal(t); + + transport_container_unregister(&i->t.host_attrs); + transport_container_unregister(&i->phy_attr_cont); + transport_container_unregister(&i->rphy_attr_cont); + + kfree(i); +} +EXPORT_SYMBOL(sas_release_transport); + +static __init int sas_transport_init(void) +{ + int error; + + error = transport_class_register(&sas_host_class); + if (error) + goto out; + error = transport_class_register(&sas_phy_class); + if (error) + goto out_unregister_transport; + error = transport_class_register(&sas_rphy_class); + if (error) + goto out_unregister_phy; + + return 0; + + out_unregister_phy: + transport_class_unregister(&sas_phy_class); + out_unregister_transport: + transport_class_unregister(&sas_host_class); + out: + return error; + +} + +static void __exit sas_transport_exit(void) +{ + transport_class_unregister(&sas_host_class); + transport_class_unregister(&sas_phy_class); + transport_class_unregister(&sas_rphy_class); +} + +MODULE_AUTHOR("Christoph Hellwig"); +MODULE_DESCRIPTION("SAS Transphy Attributes"); +MODULE_LICENSE("GPL"); + +module_init(sas_transport_init); +module_exit(sas_transport_exit); diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index b1b69d738d0..9ea4765d1d1 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -61,7 +61,7 @@ static int sg_version_num = 30533; /* 2 digits for each component */ #ifdef CONFIG_SCSI_PROC_FS #include <linux/proc_fs.h> -static char *sg_version_date = "20050901"; +static char *sg_version_date = "20050908"; static int sg_proc_init(void); static void sg_proc_cleanup(void); @@ -1299,7 +1299,7 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) sg_rb_correct4mmap(rsv_schp, 1); /* do only once per fd lifetime */ sfp->mmap_called = 1; } - vma->vm_flags |= (VM_RESERVED | VM_IO); + vma->vm_flags |= VM_RESERVED; vma->vm_private_data = sfp; vma->vm_ops = &sg_mmap_vm_ops; return 0; diff --git a/drivers/serial/mcfserial.c b/drivers/serial/mcfserial.c index 43b03c55f45..e2ebdcad553 100644 --- a/drivers/serial/mcfserial.c +++ b/drivers/serial/mcfserial.c @@ -63,8 +63,13 @@ struct timer_list mcfrs_timer_struct; #endif #if defined(CONFIG_HW_FEITH) - #define CONSOLE_BAUD_RATE 38400 - #define DEFAULT_CBAUD B38400 +#define CONSOLE_BAUD_RATE 38400 +#define DEFAULT_CBAUD B38400 +#endif + +#if defined(CONFIG_MOD5272) +#define CONSOLE_BAUD_RATE 115200 +#define DEFAULT_CBAUD B115200 #endif #ifndef CONSOLE_BAUD_RATE @@ -90,7 +95,7 @@ static struct tty_driver *mcfrs_serial_driver; #undef SERIAL_DEBUG_OPEN #undef SERIAL_DEBUG_FLOW -#if defined(CONFIG_M527x) || defined(CONFIG_M528x) +#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) #define IRQBASE (MCFINT_VECBASE+MCFINT_UART0) #else #define IRQBASE 73 @@ -1510,7 +1515,7 @@ static void mcfrs_irqinit(struct mcf_serial *info) *portp = (*portp & ~0x000000ff) | 0x00000055; portp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_PDCNT); *portp = (*portp & ~0x000003fc) | 0x000002a8; -#elif defined(CONFIG_M527x) || defined(CONFIG_M528x) +#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) volatile unsigned char *icrp, *uartp; volatile unsigned long *imrp; diff --git a/drivers/tc/zs.c b/drivers/tc/zs.c index 4382ee60b6a..6bed8713897 100644 --- a/drivers/tc/zs.c +++ b/drivers/tc/zs.c @@ -1683,7 +1683,7 @@ static void __init probe_sccs(void) #ifndef CONFIG_SERIAL_DEC_CONSOLE /* * We're called early and memory managment isn't up, yet. - * Thus check_region would fail. + * Thus request_region would fail. */ if (!request_region((unsigned long) zs_channels[n_channels].control, diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c index f8f21567cc2..50858273f8d 100644 --- a/drivers/usb/class/audio.c +++ b/drivers/usb/class/audio.c @@ -631,8 +631,10 @@ static void usbin_stop(struct usb_audiodev *as) i = u->flags; spin_unlock_irqrestore(&as->lock, flags); while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) { - set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); - schedule_timeout(1); + if (notkilled) + schedule_timeout_interruptible(1); + else + schedule_timeout_uninterruptible(1); spin_lock_irqsave(&as->lock, flags); i = u->flags; spin_unlock_irqrestore(&as->lock, flags); @@ -1102,8 +1104,10 @@ static void usbout_stop(struct usb_audiodev *as) i = u->flags; spin_unlock_irqrestore(&as->lock, flags); while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) { - set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); - schedule_timeout(1); + if (notkilled) + schedule_timeout_interruptible(1); + else + schedule_timeout_uninterruptible(1); spin_lock_irqsave(&as->lock, flags); i = u->flags; spin_unlock_irqrestore(&as->lock, flags); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 12ecdb03ee5..1017a97a418 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1606,7 +1606,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r) return IRQ_NONE; hcd->saw_irq = 1; - if (hcd->state != start && hcd->state == HC_STATE_HALT) + if (hcd->state == HC_STATE_HALT) usb_hc_died (hcd); return IRQ_HANDLED; } @@ -1630,7 +1630,6 @@ void usb_hc_died (struct usb_hcd *hcd) spin_lock_irqsave (&hcd_root_hub_lock, flags); if (hcd->rh_registered) { hcd->poll_rh = 0; - del_timer(&hcd->rh_timer); /* make khubd clean up old urbs and devices */ usb_set_device_state (hcd->self.root_hub, diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 758c7f0ed15..a12cab5314e 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -435,6 +435,7 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) static void hub_power_on(struct usb_hub *hub) { int port1; + unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2; /* if hub supports power switching, enable power on each port */ if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) < 2) { @@ -444,8 +445,8 @@ static void hub_power_on(struct usb_hub *hub) USB_PORT_FEAT_POWER); } - /* Wait for power to be enabled */ - msleep(hub->descriptor->bPwrOn2PwrGood * 2); + /* Wait at least 100 msec for power to become stable */ + msleep(max(pgood_delay, (unsigned) 100)); } static void hub_quiesce(struct usb_hub *hub) @@ -1460,7 +1461,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1, port1, status); else { status = hub_port_wait_reset(hub, port1, udev, delay); - if (status) + if (status && status != -ENOTCONN) dev_dbg(hub->intfdev, "port_wait_reset: err = %d\n", status); @@ -1469,8 +1470,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1, /* return on disconnect or reset */ switch (status) { case 0: - /* TRSTRCY = 10 ms */ - msleep(10); + /* TRSTRCY = 10 ms; plus some extra */ + msleep(10 + 40); /* FALL THROUGH */ case -ENOTCONN: case -ENODEV: diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 020815397a4..5c40980a5bd 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -483,6 +483,7 @@ ep_release (struct inode *inode, struct file *fd) data->state = STATE_EP_DISABLED; data->desc.bDescriptorType = 0; data->hs_desc.bDescriptorType = 0; + usb_ep_disable(data->ep); } put_ep (data); return 0; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2507e898af0..b948ffd94f4 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -400,6 +400,23 @@ static int ehci_hc_reset (struct usb_hcd *hcd) return -EIO; } break; + case PCI_VENDOR_ID_NVIDIA: + /* NVidia reports that certain chips don't handle + * QH, ITD, or SITD addresses above 2GB. (But TD, + * data buffer, and periodic schedule are normal.) + */ + switch (pdev->device) { + case 0x003c: /* MCP04 */ + case 0x005b: /* CK804 */ + case 0x00d8: /* CK8 */ + case 0x00e8: /* CK8S */ + if (pci_set_consistent_dma_mask(pdev, + DMA_31BIT_MASK) < 0) + ehci_warn (ehci, "can't enable NVidia " + "workaround for >2GB RAM\n"); + break; + } + break; } /* optional debug port, normally in the first BAR */ @@ -759,12 +776,16 @@ static int ehci_resume (struct usb_hcd *hcd) if (time_before (jiffies, ehci->next_statechange)) msleep (100); - /* If any port is suspended, we know we can/must resume the HC. */ + /* If any port is suspended (or owned by the companion), + * we know we can/must resume the HC (and mustn't reset it). + */ for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) { u32 status; port--; status = readl (&ehci->regs->port_status [port]); - if (status & PORT_SUSPEND) { + if (!(status & PORT_POWER)) + continue; + if (status & (PORT_SUSPEND | PORT_OWNER)) { down (&hcd->self.root_hub->serialize); retval = ehci_hub_resume (hcd); up (&hcd->self.root_hub->serialize); @@ -1126,8 +1147,7 @@ rescan: case QH_STATE_UNLINK: /* wait for hw to finish? */ idle_timeout: spin_unlock_irqrestore (&ehci->lock, flags); - set_current_state (TASK_UNINTERRUPTIBLE); - schedule_timeout (1); + schedule_timeout_uninterruptible(1); goto rescan; case QH_STATE_IDLE: /* fully unlinked */ if (list_empty (&qh->qtd_list)) { diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 36cc1f2218d..18d3f227031 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -54,7 +54,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd) /* suspend any active/unsuspended ports, maybe allow wakeup */ while (port--) { u32 __iomem *reg = &ehci->regs->port_status [port]; - u32 t1 = readl (reg); + u32 t1 = readl (reg) & ~PORT_RWC_BITS; u32 t2 = t1; if ((t1 & PORT_PE) && !(t1 & PORT_OWNER)) @@ -115,7 +115,8 @@ static int ehci_hub_resume (struct usb_hcd *hcd) i = HCS_N_PORTS (ehci->hcs_params); while (i--) { temp = readl (&ehci->regs->port_status [i]); - temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); + temp &= ~(PORT_RWC_BITS + | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); if (temp & PORT_SUSPEND) { ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); temp |= PORT_RESUME; @@ -128,7 +129,7 @@ static int ehci_hub_resume (struct usb_hcd *hcd) temp = readl (&ehci->regs->port_status [i]); if ((temp & PORT_SUSPEND) == 0) continue; - temp &= ~PORT_RESUME; + temp &= ~(PORT_RWC_BITS | PORT_RESUME); writel (temp, &ehci->regs->port_status [i]); ehci_vdbg (ehci, "resumed port %d\n", i + 1); } @@ -191,6 +192,7 @@ static int check_reset_complete ( // what happens if HCS_N_CC(params) == 0 ? port_status |= PORT_OWNER; + port_status &= ~PORT_RWC_BITS; writel (port_status, &ehci->regs->port_status [index]); } else @@ -233,7 +235,8 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) if (temp & PORT_OWNER) { /* don't report this in GetPortStatus */ if (temp & PORT_CSC) { - temp &= ~PORT_CSC; + temp &= ~PORT_RWC_BITS; + temp |= PORT_CSC; writel (temp, &ehci->regs->port_status [i]); } continue; @@ -343,7 +346,7 @@ static int ehci_hub_control ( &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_ENABLE: - writel (temp | PORT_PEC, + writel((temp & ~PORT_RWC_BITS) | PORT_PEC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_SUSPEND: @@ -353,7 +356,8 @@ static int ehci_hub_control ( if ((temp & PORT_PE) == 0) goto error; /* resume signaling for 20 msec */ - writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME, + temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); + writel (temp | PORT_RESUME, &ehci->regs->port_status [wIndex]); ehci->reset_done [wIndex] = jiffies + msecs_to_jiffies (20); @@ -364,15 +368,15 @@ static int ehci_hub_control ( break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) - writel (temp & ~PORT_POWER, + writel (temp & ~(PORT_RWC_BITS | PORT_POWER), &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_CONNECTION: - writel (temp | PORT_CSC, + writel((temp & ~PORT_RWC_BITS) | PORT_CSC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_OVER_CURRENT: - writel (temp | PORT_OCC, + writel((temp & ~PORT_RWC_BITS) | PORT_OCC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_RESET: @@ -416,7 +420,7 @@ static int ehci_hub_control ( /* stop resume signaling */ temp = readl (&ehci->regs->port_status [wIndex]); - writel (temp & ~PORT_RESUME, + writel (temp & ~(PORT_RWC_BITS | PORT_RESUME), &ehci->regs->port_status [wIndex]); retval = handshake ( &ehci->regs->port_status [wIndex], @@ -437,7 +441,7 @@ static int ehci_hub_control ( ehci->reset_done [wIndex] = 0; /* force reset to complete */ - writel (temp & ~PORT_RESET, + writel (temp & ~(PORT_RWC_BITS | PORT_RESET), &ehci->regs->port_status [wIndex]); /* REVISIT: some hardware needs 550+ usec to clear * this bit; seems too long to spin routinely... @@ -500,6 +504,7 @@ static int ehci_hub_control ( if (temp & PORT_OWNER) break; + temp &= ~PORT_RWC_BITS; switch (wValue) { case USB_PORT_FEAT_SUSPEND: if ((temp & PORT_PE) == 0 diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 20c9b550097..f34a0516d35 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -263,6 +263,7 @@ struct ehci_regs { #define PORT_PE (1<<2) /* port enable */ #define PORT_CSC (1<<1) /* connect status change */ #define PORT_CONNECT (1<<0) /* device connected */ +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) } __attribute__ ((packed)); /* Appendix C, Debug port ... intended for use with special "debug devices" diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 447f488f5d9..7924c74f958 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -228,23 +228,22 @@ ohci_dump_roothub ( char **next, unsigned *size) { - u32 temp, ndp, i; + u32 temp, i; temp = roothub_a (controller); if (temp == ~(u32)0) return; - ndp = (temp & RH_A_NDP); if (verbose) { ohci_dbg_sw (controller, next, size, - "roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d\n", temp, + "roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d(%d)\n", 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 & RH_A_NDP), controller->num_ports ); temp = roothub_b (controller); ohci_dbg_sw (controller, next, size, @@ -266,7 +265,7 @@ ohci_dump_roothub ( ); } - for (i = 0; i < ndp; i++) { + for (i = 0; i < controller->num_ports; i++) { temp = roothub_portstatus (controller, i); dbg_port_sw (controller, i, temp, next, size); } diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 56b43f2a0e5..67c1aa5eb1c 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -382,8 +382,7 @@ sanitize: goto sanitize; } spin_unlock_irqrestore (&ohci->lock, flags); - set_current_state (TASK_UNINTERRUPTIBLE); - schedule_timeout (1); + schedule_timeout_uninterruptible(1); goto rescan; case ED_IDLE: /* fully unlinked */ if (list_empty (&ed->td_list)) { @@ -485,6 +484,10 @@ static int ohci_init (struct ohci_hcd *ohci) // flush the writes (void) ohci_readl (ohci, &ohci->regs->control); + /* Read the number of ports unless overridden */ + if (ohci->num_ports == 0) + ohci->num_ports = roothub_a(ohci) & RH_A_NDP; + if (ohci->hcca) return 0; @@ -561,10 +564,8 @@ static int ohci_run (struct ohci_hcd *ohci) msleep(temp); temp = roothub_a (ohci); if (!(temp & RH_A_NPS)) { - unsigned ports = temp & RH_A_NDP; - /* power down each port */ - for (temp = 0; temp < ports; temp++) + for (temp = 0; temp < ohci->num_ports; temp++) ohci_writel (ohci, RH_PS_LSDA, &ohci->regs->roothub.portstatus [temp]); } @@ -720,6 +721,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) if (ints & OHCI_INTR_RD) { ohci_vdbg (ohci, "resume detect\n"); + ohci_writel (ohci, OHCI_INTR_RD, ®s->intrstatus); if (hcd->state != HC_STATE_QUIESCING) schedule_work(&ohci->rh_resume); } @@ -861,7 +863,7 @@ static int ohci_restart (struct ohci_hcd *ohci) * and that if we try to turn them back on the root hub * will respond to CSC processing. */ - i = roothub_a (ohci) & RH_A_NDP; + i = ohci->num_ports; while (i--) ohci_writel (ohci, RH_PS_PSS, &ohci->regs->roothub.portstatus [temp]); diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 83ca4549a50..ce7b28da7a1 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -184,7 +184,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd) if (status != -EINPROGRESS) return status; - temp = roothub_a (ohci) & RH_A_NDP; + temp = ohci->num_ports; enables = 0; while (temp--) { u32 stat = ohci_readl (ohci, @@ -304,7 +304,7 @@ static int ohci_hub_status_data (struct usb_hcd *hcd, char *buf) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int ports, i, changed = 0, length = 1; + int i, changed = 0, length = 1; int can_suspend = hcd->can_wakeup; unsigned long flags; @@ -319,9 +319,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) goto done; } - ports = roothub_a (ohci) & RH_A_NDP; - if (ports > MAX_ROOT_PORTS) { - ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n", ports, + /* undocumented erratum seen on at least rev D */ + if ((ohci->flags & OHCI_QUIRK_AMD756) + && (roothub_a (ohci) & RH_A_NDP) > MAX_ROOT_PORTS) { + ohci_warn (ohci, "bogus NDP, rereads as NDP=%d\n", ohci_readl (ohci, &ohci->regs->roothub.a) & RH_A_NDP); /* retry later; "should not happen" */ goto done; @@ -332,13 +333,13 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) buf [0] = changed = 1; else buf [0] = 0; - if (ports > 7) { + if (ohci->num_ports > 7) { buf [1] = 0; length++; } /* look at each port */ - for (i = 0; i < ports; i++) { + for (i = 0; i < ohci->num_ports; i++) { u32 status = roothub_portstatus (ohci, i); if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC @@ -395,15 +396,14 @@ ohci_hub_descriptor ( struct usb_hub_descriptor *desc ) { u32 rh = roothub_a (ohci); - int ports = rh & RH_A_NDP; u16 temp; desc->bDescriptorType = 0x29; desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24; desc->bHubContrCurrent = 0; - desc->bNbrPorts = ports; - temp = 1 + (ports / 8); + desc->bNbrPorts = ohci->num_ports; + temp = 1 + (ohci->num_ports / 8); desc->bDescLength = 7 + 2 * temp; temp = 0; @@ -421,7 +421,7 @@ ohci_hub_descriptor ( rh = roothub_b (ohci); memset(desc->bitmap, 0xff, sizeof(desc->bitmap)); desc->bitmap [0] = rh & RH_B_DR; - if (ports > 7) { + if (ohci->num_ports > 7) { desc->bitmap [1] = (rh & RH_B_DR) >> 8; desc->bitmap [2] = 0xff; } else diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index e5bc1789d18..2fdb262d472 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -75,33 +75,6 @@ static int pxa27x_ohci_select_pmm( int mode ) return 0; } -/* - If you select PMM_PERPORT_MODE, you should set the port power - */ -static int pxa27x_ohci_set_port_power( int port ) -{ - if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE) - && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) { - UHCRHPS(port) |= 0x100; - return 0; - } - return -1; -} - -/* - If you select PMM_PERPORT_MODE, you should set the port power - */ -static int pxa27x_ohci_clear_port_power( int port ) -{ - if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE) - && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) { - UHCRHPS(port) |= 0x200; - return 0; - } - - return -1; -} - extern int usb_disabled(void); /*-------------------------------------------------------------------------*/ @@ -130,11 +103,17 @@ static void pxa27x_start_hc(struct platform_device *dev) Polarity Low to active low. Supply power to USB ports. */ UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) & ~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE); + + pxa27x_ohci_pmm_state = PMM_PERPORT_MODE; } UHCHR &= ~UHCHR_SSE; UHCHIE = (UHCHIE_UPRIE | UHCHIE_RWIE); + + /* Clear any OTG Pin Hold */ + if (PSSR & PSSR_OTGPH) + PSSR |= PSSR_OTGPH; } static void pxa27x_stop_hc(struct platform_device *dev) @@ -198,17 +177,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, pxa27x_start_hc(dev); /* Select Power Management Mode */ - pxa27x_ohci_select_pmm( PMM_PERPORT_MODE ); - - /* If choosing PMM_PERPORT_MODE, we should set the port power before we use it. */ - if (pxa27x_ohci_set_port_power(1) < 0) - printk(KERN_ERR "Setting port 1 power failed.\n"); - - if (pxa27x_ohci_clear_port_power(2) < 0) - printk(KERN_ERR "Setting port 2 power failed.\n"); - - if (pxa27x_ohci_clear_port_power(3) < 0) - printk(KERN_ERR "Setting port 3 power failed.\n"); + pxa27x_ohci_select_pmm(pxa27x_ohci_pmm_state); ohci_hcd_init(hcd_to_ohci(hcd)); @@ -258,6 +227,9 @@ ohci_pxa27x_start (struct usb_hcd *hcd) ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci); + /* The value of NDP in roothub_a is incorrect on this hardware */ + ohci->num_ports = 3; + if ((ret = ohci_init(ohci)) < 0) return ret; diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 71cdd226286..8a9b9d9209e 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -383,6 +383,7 @@ struct ohci_hcd { /* * driver state */ + int num_ports; int load [NUM_INTS]; u32 hc_control; /* copy of hc control reg */ unsigned long next_statechange; /* suspend/resume */ diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 0d5d2545bf0..0c024898cbe 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -97,14 +97,9 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci); /* to make sure it doesn't hog all of the bandwidth */ #define DEPTH_INTERVAL 5 -static inline void restart_timer(struct uhci_hcd *uhci) -{ - mod_timer(&uhci->stall_timer, jiffies + msecs_to_jiffies(100)); -} - -#include "uhci-hub.c" #include "uhci-debug.c" #include "uhci-q.c" +#include "uhci-hub.c" /* * Make sure the controller is completely inactive, unable to @@ -160,7 +155,6 @@ static void hc_died(struct uhci_hcd *uhci) { reset_hc(uhci); uhci->hc_inaccessible = 1; - del_timer(&uhci->stall_timer); } /* @@ -287,8 +281,11 @@ __acquires(uhci->lock) /* Enable resume-detect interrupts if they work. * Then enter Global Suspend mode, still configured. */ - int_enable = (resume_detect_interrupts_are_broken(uhci) ? - 0 : USBINTR_RESUME); + uhci->working_RD = 1; + int_enable = USBINTR_RESUME; + if (resume_detect_interrupts_are_broken(uhci)) { + uhci->working_RD = int_enable = 0; + } outw(int_enable, uhci->io_addr + USBINTR); outw(USBCMD_EGSM | USBCMD_CF, uhci->io_addr + USBCMD); mb(); @@ -315,7 +312,6 @@ __acquires(uhci->lock) uhci->rh_state = new_state; uhci->is_stopped = UHCI_IS_STOPPED; - del_timer(&uhci->stall_timer); uhci_to_hcd(uhci)->poll_rh = !int_enable; uhci_scan_schedule(uhci, NULL); @@ -335,7 +331,6 @@ static void start_rh(struct uhci_hcd *uhci) mb(); uhci->rh_state = UHCI_RH_RUNNING; uhci_to_hcd(uhci)->poll_rh = 1; - restart_timer(uhci); } static void wakeup_rh(struct uhci_hcd *uhci) @@ -374,20 +369,6 @@ __acquires(uhci->lock) mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies); } -static void stall_callback(unsigned long _uhci) -{ - struct uhci_hcd *uhci = (struct uhci_hcd *) _uhci; - unsigned long flags; - - spin_lock_irqsave(&uhci->lock, flags); - uhci_scan_schedule(uhci, NULL); - check_fsbr(uhci); - - if (!uhci->is_stopped) - restart_timer(uhci); - spin_unlock_irqrestore(&uhci->lock, flags); -} - static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); @@ -418,8 +399,10 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) "host controller halted, " "very bad!\n"); hc_died(uhci); - spin_unlock_irqrestore(&uhci->lock, flags); - return IRQ_HANDLED; + + /* Force a callback in case there are + * pending unlinks */ + mod_timer(&hcd->rh_timer, jiffies); } spin_unlock_irqrestore(&uhci->lock, flags); } @@ -427,10 +410,11 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) if (status & USBSTS_RD) usb_hcd_poll_rh_status(hcd); - - spin_lock_irqsave(&uhci->lock, flags); - uhci_scan_schedule(uhci, regs); - spin_unlock_irqrestore(&uhci->lock, flags); + else { + spin_lock_irqsave(&uhci->lock, flags); + uhci_scan_schedule(uhci, regs); + spin_unlock_irqrestore(&uhci->lock, flags); + } return IRQ_HANDLED; } @@ -595,10 +579,6 @@ static int uhci_start(struct usb_hcd *hcd) init_waitqueue_head(&uhci->waitqh); - init_timer(&uhci->stall_timer); - uhci->stall_timer.function = stall_callback; - uhci->stall_timer.data = (unsigned long) uhci; - uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl), &dma_handle, 0); if (!uhci->fl) { @@ -745,11 +725,11 @@ static void uhci_stop(struct usb_hcd *hcd) struct uhci_hcd *uhci = hcd_to_uhci(hcd); spin_lock_irq(&uhci->lock); - reset_hc(uhci); + if (!uhci->hc_inaccessible) + reset_hc(uhci); uhci_scan_schedule(uhci, NULL); spin_unlock_irq(&uhci->lock); - del_timer_sync(&uhci->stall_timer); release_uhci(uhci); } @@ -811,13 +791,12 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message) */ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0); uhci->hc_inaccessible = 1; + hcd->poll_rh = 0; /* FIXME: Enable non-PME# remote wakeup? */ done: spin_unlock_irq(&uhci->lock); - if (rc == 0) - del_timer_sync(&hcd->rh_timer); return rc; } @@ -850,8 +829,11 @@ static int uhci_resume(struct usb_hcd *hcd) spin_unlock_irq(&uhci->lock); - if (hcd->poll_rh) + if (!uhci->working_RD) { + /* Suspended root hub needs to be polled */ + hcd->poll_rh = 1; usb_hcd_poll_rh_status(hcd); + } return 0; } #endif diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index bf9c5f9b508..282f40b7588 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -345,9 +345,6 @@ enum uhci_rh_state { /* * This describes the full uhci information. - * - * Note how the "proper" USB information is just - * a subset of what the full implementation needs. */ struct uhci_hcd { @@ -360,8 +357,6 @@ struct uhci_hcd { struct dma_pool *qh_pool; struct dma_pool *td_pool; - struct usb_bus *bus; - struct uhci_td *term_td; /* Terminating TD, see UHCI bug */ struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */ @@ -380,6 +375,8 @@ struct uhci_hcd { unsigned int scan_in_progress:1; /* Schedule scan is running */ unsigned int need_rescan:1; /* Redo the schedule scan */ unsigned int hc_inaccessible:1; /* HC is suspended or dead */ + unsigned int working_RD:1; /* Suspended root hub doesn't + need to be polled */ /* Support for port suspend/resume/reset */ unsigned long port_c_suspend; /* Bit-arrays of ports */ @@ -405,9 +402,7 @@ struct uhci_hcd { /* List of URB's awaiting completion callback */ struct list_head complete_list; /* P: uhci->lock */ - int rh_numports; - - struct timer_list stall_timer; + int rh_numports; /* Number of root-hub ports */ wait_queue_head_t waitqh; /* endpoint_disable waiters */ }; diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 4eace2b19dd..a71e48a6680 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -145,15 +145,16 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); unsigned long flags; - int status; + int status = 0; spin_lock_irqsave(&uhci->lock, flags); - if (uhci->hc_inaccessible) { - status = 0; - goto done; - } + uhci_scan_schedule(uhci, NULL); + if (uhci->hc_inaccessible) + goto done; + check_fsbr(uhci); uhci_check_ports(uhci); + status = get_hub_status_data(uhci, buf); switch (uhci->rh_state) { diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index bbb36cd6ed6..ea0d168a8c6 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -33,7 +33,7 @@ static void uhci_free_pending_tds(struct uhci_hcd *uhci); static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci) { if (uhci->is_stopped) - mod_timer(&uhci->stall_timer, jiffies); + mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies); uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); } diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig index 482c4be521f..1e53934907c 100644 --- a/drivers/usb/input/Kconfig +++ b/drivers/usb/input/Kconfig @@ -286,3 +286,23 @@ config USB_KEYSPAN_REMOTE To compile this driver as a module, choose M here: the module will be called keyspan_remote. + +config USB_APPLETOUCH + tristate "Apple USB Touchpad support" + depends on USB && INPUT + ---help--- + Say Y here if you want to use an Apple USB Touchpad. + + These are the touchpads that can be found on post-February 2005 + Apple Powerbooks (prior models have a Synaptics touchpad connected + to the ADB bus). + + This driver provides a basic mouse driver but can be interfaced + with the synaptics X11 driver to provide acceleration and + scrolling in X11. + + For further information, see + <file:Documentation/input/appletouch.txt>. + + To compile this driver as a module, choose M here: the + module will be called appletouch. diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile index 43b2f999edf..5e03b93f29f 100644 --- a/drivers/usb/input/Makefile +++ b/drivers/usb/input/Makefile @@ -41,3 +41,4 @@ obj-$(CONFIG_USB_WACOM) += wacom.o obj-$(CONFIG_USB_ACECAD) += acecad.o obj-$(CONFIG_USB_YEALINK) += yealink.o obj-$(CONFIG_USB_XPAD) += xpad.o +obj-$(CONFIG_USB_APPLETOUCH) += appletouch.o diff --git a/drivers/usb/input/appletouch.c b/drivers/usb/input/appletouch.c new file mode 100644 index 00000000000..e03c1c567a1 --- /dev/null +++ b/drivers/usb/input/appletouch.c @@ -0,0 +1,469 @@ +/* + * Apple USB Touchpad (for post-February 2005 PowerBooks) driver + * + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net) + * Copyright (C) 2005 Stelian Pop (stelian@popies.net) + * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de) + * Copyright (C) 2005 Peter Osterlund (petero2@telia.com) + * + * Thanks to Alex Harper <basilisk@foobox.net> for his inputs. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/input.h> +#include <linux/usb_input.h> + +/* Apple has powerbooks which have the keyboard with different Product IDs */ +#define APPLE_VENDOR_ID 0x05AC + +#define ATP_DEVICE(prod) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS | \ + USB_DEVICE_ID_MATCH_INT_PROTOCOL, \ + .idVendor = APPLE_VENDOR_ID, \ + .idProduct = (prod), \ + .bInterfaceClass = 0x03, \ + .bInterfaceProtocol = 0x02 + +/* table of devices that work with this driver */ +static struct usb_device_id atp_table [] = { + { ATP_DEVICE(0x020E) }, + { ATP_DEVICE(0x020F) }, + { ATP_DEVICE(0x030A) }, + { ATP_DEVICE(0x030B) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, atp_table); + +/* size of a USB urb transfer */ +#define ATP_DATASIZE 81 + +/* + * number of sensors. Note that only 16 instead of 26 X (horizontal) + * sensors exist on 12" and 15" PowerBooks. All models have 16 Y + * (vertical) sensors. + */ +#define ATP_XSENSORS 26 +#define ATP_YSENSORS 16 + +/* amount of fuzz this touchpad generates */ +#define ATP_FUZZ 16 + +/* maximum pressure this driver will report */ +#define ATP_PRESSURE 300 +/* + * multiplication factor for the X and Y coordinates. + * We try to keep the touchpad aspect ratio while still doing only simple + * arithmetics. + * The factors below give coordinates like: + * 0 <= x < 960 on 12" and 15" Powerbooks + * 0 <= x < 1600 on 17" Powerbooks + * 0 <= y < 646 + */ +#define ATP_XFACT 64 +#define ATP_YFACT 43 + +/* + * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is + * ignored. + */ +#define ATP_THRESHOLD 5 + +/* Structure to hold all of our device specific stuff */ +struct atp { + struct usb_device * udev; /* usb device */ + struct urb * urb; /* usb request block */ + signed char * data; /* transferred data */ + int open; /* non-zero if opened */ + struct input_dev input; /* input dev */ + int valid; /* are the sensors valid ? */ + int x_old; /* last reported x/y, */ + int y_old; /* used for smoothing */ + /* current value of the sensors */ + signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS]; + /* last value of the sensors */ + signed char xy_old[ATP_XSENSORS + ATP_YSENSORS]; + /* accumulated sensors */ + int xy_acc[ATP_XSENSORS + ATP_YSENSORS]; +}; + +#define dbg_dump(msg, tab) \ + if (debug > 1) { \ + int i; \ + printk("appletouch: %s %lld", msg, (long long)jiffies); \ + for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) \ + printk(" %02x", tab[i]); \ + printk("\n"); \ + } + +#define dprintk(format, a...) \ + do { \ + if (debug) printk(format, ##a); \ + } while (0) + +MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold"); +MODULE_DESCRIPTION("Apple PowerBooks USB touchpad driver"); +MODULE_LICENSE("GPL"); + +static int debug = 1; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activate debugging output"); + +static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact, + int *z, int *fingers) +{ + int i; + /* values to calculate mean */ + int pcum = 0, psum = 0; + + *fingers = 0; + + for (i = 0; i < nb_sensors; i++) { + if (xy_sensors[i] < ATP_THRESHOLD) + continue; + if ((i - 1 < 0) || (xy_sensors[i - 1] < ATP_THRESHOLD)) + (*fingers)++; + pcum += xy_sensors[i] * i; + psum += xy_sensors[i]; + } + + if (psum > 0) { + *z = psum; + return pcum * fact / psum; + } + + return 0; +} + +static inline void atp_report_fingers(struct input_dev *input, int fingers) +{ + input_report_key(input, BTN_TOOL_FINGER, fingers == 1); + input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2); + input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2); +} + +static void atp_complete(struct urb* urb, struct pt_regs* regs) +{ + int x, y, x_z, y_z, x_f, y_f; + int retval, i; + struct atp *dev = urb->context; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* This urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + + /* drop incomplete datasets */ + if (dev->urb->actual_length != ATP_DATASIZE) { + dprintk("appletouch: incomplete data package.\n"); + goto exit; + } + + /* reorder the sensors values */ + for (i = 0; i < 8; i++) { + /* X values */ + dev->xy_cur[i ] = dev->data[5 * i + 2]; + dev->xy_cur[i + 8] = dev->data[5 * i + 4]; + dev->xy_cur[i + 16] = dev->data[5 * i + 42]; + if (i < 2) + dev->xy_cur[i + 24] = dev->data[5 * i + 44]; + + /* Y values */ + dev->xy_cur[i + 26] = dev->data[5 * i + 1]; + dev->xy_cur[i + 34] = dev->data[5 * i + 3]; + } + + dbg_dump("sample", dev->xy_cur); + + if (!dev->valid) { + /* first sample */ + dev->valid = 1; + dev->x_old = dev->y_old = -1; + memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old)); + + /* 17" Powerbooks have 10 extra X sensors */ + for (i = 16; i < ATP_XSENSORS; i++) + if (dev->xy_cur[i]) { + printk("appletouch: 17\" model detected.\n"); + input_set_abs_params(&dev->input, ABS_X, 0, + (ATP_XSENSORS - 1) * + ATP_XFACT - 1, + ATP_FUZZ, 0); + break; + } + + goto exit; + } + + for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) { + /* accumulate the change */ + signed char change = dev->xy_old[i] - dev->xy_cur[i]; + dev->xy_acc[i] -= change; + + /* prevent down drifting */ + if (dev->xy_acc[i] < 0) + dev->xy_acc[i] = 0; + } + + memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old)); + + dbg_dump("accumulator", dev->xy_acc); + + x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS, + ATP_XFACT, &x_z, &x_f); + y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, + ATP_YFACT, &y_z, &y_f); + + if (x && y) { + if (dev->x_old != -1) { + x = (dev->x_old * 3 + x) >> 2; + y = (dev->y_old * 3 + y) >> 2; + dev->x_old = x; + dev->y_old = y; + + if (debug > 1) + printk("appletouch: X: %3d Y: %3d " + "Xz: %3d Yz: %3d\n", + x, y, x_z, y_z); + + input_report_key(&dev->input, BTN_TOUCH, 1); + input_report_abs(&dev->input, ABS_X, x); + input_report_abs(&dev->input, ABS_Y, y); + input_report_abs(&dev->input, ABS_PRESSURE, + min(ATP_PRESSURE, x_z + y_z)); + atp_report_fingers(&dev->input, max(x_f, y_f)); + } + dev->x_old = x; + dev->y_old = y; + } + else if (!x && !y) { + + dev->x_old = dev->y_old = -1; + input_report_key(&dev->input, BTN_TOUCH, 0); + input_report_abs(&dev->input, ABS_PRESSURE, 0); + atp_report_fingers(&dev->input, 0); + + /* reset the accumulator on release */ + memset(dev->xy_acc, 0, sizeof(dev->xy_acc)); + } + + input_report_key(&dev->input, BTN_LEFT, !!dev->data[80]); + + input_sync(&dev->input); + +exit: + retval = usb_submit_urb(dev->urb, GFP_ATOMIC); + if (retval) { + err("%s - usb_submit_urb failed with result %d", + __FUNCTION__, retval); + } +} + +static int atp_open(struct input_dev *input) +{ + struct atp *dev = input->private; + + if (usb_submit_urb(dev->urb, GFP_ATOMIC)) + return -EIO; + + dev->open = 1; + return 0; +} + +static void atp_close(struct input_dev *input) +{ + struct atp *dev = input->private; + + usb_kill_urb(dev->urb); + dev->open = 0; +} + +static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id) +{ + struct atp *dev = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int int_in_endpointAddr = 0; + int i, retval = -ENOMEM; + + /* allocate memory for our device state and initialize it */ + dev = kmalloc(sizeof(struct atp), GFP_KERNEL); + if (dev == NULL) { + err("Out of memory"); + goto err_kmalloc; + } + memset(dev, 0, sizeof(struct atp)); + + dev->udev = interface_to_usbdev(iface); + + /* set up the endpoint information */ + /* use only the first interrupt-in endpoint */ + iface_desc = iface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { + endpoint = &iface_desc->endpoint[i].desc; + if (!int_in_endpointAddr && + (endpoint->bEndpointAddress & USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_INT)) { + /* we found an interrupt in endpoint */ + int_in_endpointAddr = endpoint->bEndpointAddress; + break; + } + } + if (!int_in_endpointAddr) { + retval = -EIO; + err("Could not find int-in endpoint"); + goto err_endpoint; + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(iface, dev); + + dev->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urb) { + retval = -ENOMEM; + goto err_usballoc; + } + dev->data = usb_buffer_alloc(dev->udev, ATP_DATASIZE, GFP_KERNEL, + &dev->urb->transfer_dma); + if (!dev->data) { + retval = -ENOMEM; + goto err_usbbufalloc; + } + usb_fill_int_urb(dev->urb, dev->udev, + usb_rcvintpipe(dev->udev, int_in_endpointAddr), + dev->data, ATP_DATASIZE, atp_complete, dev, 1); + + init_input_dev(&dev->input); + dev->input.name = "appletouch"; + dev->input.dev = &iface->dev; + dev->input.private = dev; + dev->input.open = atp_open; + dev->input.close = atp_close; + + usb_to_input_id(dev->udev, &dev->input.id); + + set_bit(EV_ABS, dev->input.evbit); + + /* + * 12" and 15" Powerbooks only have 16 x sensors, + * 17" models are detected later. + */ + input_set_abs_params(&dev->input, ABS_X, 0, + (16 - 1) * ATP_XFACT - 1, ATP_FUZZ, 0); + input_set_abs_params(&dev->input, ABS_Y, 0, + (ATP_YSENSORS - 1) * ATP_YFACT - 1, ATP_FUZZ, 0); + input_set_abs_params(&dev->input, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0); + + set_bit(EV_KEY, dev->input.evbit); + set_bit(BTN_TOUCH, dev->input.keybit); + set_bit(BTN_TOOL_FINGER, dev->input.keybit); + set_bit(BTN_TOOL_DOUBLETAP, dev->input.keybit); + set_bit(BTN_TOOL_TRIPLETAP, dev->input.keybit); + set_bit(BTN_LEFT, dev->input.keybit); + + input_register_device(&dev->input); + + printk(KERN_INFO "input: appletouch connected\n"); + + return 0; + +err_usbbufalloc: + usb_free_urb(dev->urb); +err_usballoc: + usb_set_intfdata(iface, NULL); +err_endpoint: + kfree(dev); +err_kmalloc: + return retval; +} + +static void atp_disconnect(struct usb_interface *iface) +{ + struct atp *dev = usb_get_intfdata(iface); + + usb_set_intfdata(iface, NULL); + if (dev) { + usb_kill_urb(dev->urb); + input_unregister_device(&dev->input); + usb_free_urb(dev->urb); + usb_buffer_free(dev->udev, ATP_DATASIZE, + dev->data, dev->urb->transfer_dma); + kfree(dev); + } + printk(KERN_INFO "input: appletouch disconnected\n"); +} + +static int atp_suspend(struct usb_interface *iface, pm_message_t message) +{ + struct atp *dev = usb_get_intfdata(iface); + usb_kill_urb(dev->urb); + dev->valid = 0; + return 0; +} + +static int atp_resume(struct usb_interface *iface) +{ + struct atp *dev = usb_get_intfdata(iface); + if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC)) + return -EIO; + + return 0; +} + +static struct usb_driver atp_driver = { + .owner = THIS_MODULE, + .name = "appletouch", + .probe = atp_probe, + .disconnect = atp_disconnect, + .suspend = atp_suspend, + .resume = atp_resume, + .id_table = atp_table, +}; + +static int __init atp_init(void) +{ + return usb_register(&atp_driver); +} + +static void __exit atp_exit(void) +{ + usb_deregister(&atp_driver); +} + +module_init(atp_init); +module_exit(atp_exit); diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index e108e0a36b7..a99865c689c 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -1446,7 +1446,6 @@ void hid_init_reports(struct hid_device *hid) #define USB_VENDOR_ID_APPLE 0x05ac #define USB_DEVICE_ID_APPLE_POWERMOUSE 0x0304 -#define USB_DEVICE_ID_APPLE_BLUETOOTH 0x1000 /* * Alphabetically sorted blacklist by quirk type. @@ -1465,7 +1464,6 @@ static struct hid_blacklist { { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_BLUETOOTH, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE }, diff --git a/drivers/usb/misc/sisusbvga/Kconfig b/drivers/usb/misc/sisusbvga/Kconfig index 3957e144caf..7603cbe0865 100644 --- a/drivers/usb/misc/sisusbvga/Kconfig +++ b/drivers/usb/misc/sisusbvga/Kconfig @@ -4,11 +4,43 @@ config USB_SISUSBVGA depends on USB && USB_EHCI_HCD ---help--- Say Y here if you intend to attach a USB2VGA dongle based on a - Net2280 and a SiS315 chip. - - Note that this device requires a USB 2.0 host controller. It will not + Net2280 and a SiS315 chip. + + Note that this device requires a USB 2.0 host controller. It will not work with USB 1.x controllers. - To compile this driver as a module, choose M here: the module will be - called sisusb. If unsure, say N. + To compile this driver as a module, choose M here; the module will be + called sisusbvga. If unsure, say N. + +config USB_SISUSBVGA_CON + bool "Text console and mode switching support" if USB_SISUSBVGA + depends on VT + select FONT_8x16 + ---help--- + Say Y here if you want a VGA text console via the USB dongle or + want to support userland applications that utilize the driver's + display mode switching capabilities. + + Note that this console supports VGA/EGA text mode only. + + By default, the console part of the driver will not kick in when + the driver is initialized. If you want the driver to take over + one or more of the consoles, you need to specify the number of + the first and last consoles (starting at 1) as driver parameters. + + For example, if the driver is compiled as a module: + + modprobe sisusbvga first=1 last=5 + + If you use hotplug, add this to your modutils config files with + the "options" keyword, such as eg. + + options sisusbvga first=1 last=5 + + If the driver is compiled into the kernel image, the parameters + must be given in the kernel command like, such as + + sisusbvga.first=1 sisusbvga.last=5 + + diff --git a/drivers/usb/misc/sisusbvga/Makefile b/drivers/usb/misc/sisusbvga/Makefile index 76f1643ceaf..7f934cfc906 100644 --- a/drivers/usb/misc/sisusbvga/Makefile +++ b/drivers/usb/misc/sisusbvga/Makefile @@ -2,5 +2,7 @@ # Makefile for the sisusb driver (if driver is inside kernel tree). # -obj-$(CONFIG_USB_SISUSBVGA) += sisusb.o +obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga.o + +sisusbvga-objs := sisusb.o sisusb_init.o sisusb_con.o diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index d63ce6c030f..39db3155723 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -1,6 +1,8 @@ /* * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles * + * Main part + * * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria * * If distributed as part of the Linux kernel, this code is licensed under the @@ -48,16 +50,60 @@ #include <linux/kref.h> #include <linux/usb.h> #include <linux/smp_lock.h> +#include <linux/vmalloc.h> #include "sisusb.h" +#ifdef INCL_SISUSB_CON +#include <linux/font.h> +#endif + #define SISUSB_DONTSYNC /* Forward declarations / clean-up routines */ +#ifdef INCL_SISUSB_CON +int sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data); +int sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data); +int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data); +int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data); +int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand, u8 myor); +int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor); +int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand); + +int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data); +int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data); +int sisusb_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data); +int sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *data); +int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src, + u32 dest, int length, size_t *bytes_written); + +int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init); + +extern int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo); +extern int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo); + +extern void sisusb_init_concode(void); +extern int sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last); +extern void sisusb_console_exit(struct sisusb_usb_data *sisusb); + +extern void sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location); + +extern int sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot, + u8 *arg, int cmapsz, int ch512, int dorecalc, + struct vc_data *c, int fh, int uplock); + +static int sisusb_first_vc = 0; +static int sisusb_last_vc = 0; +module_param_named(first, sisusb_first_vc, int, 0); +module_param_named(last, sisusb_last_vc, int, 0); +MODULE_PARM_DESC(first, "Number of first console to take over (1 - MAX_NR_CONSOLES)"); +MODULE_PARM_DESC(last, "Number of last console to take over (1 - MAX_NR_CONSOLES)"); +#endif + static struct usb_driver sisusb_driver; -static DECLARE_MUTEX(disconnect_sem); +DECLARE_MUTEX(disconnect_sem); static void sisusb_free_buffers(struct sisusb_usb_data *sisusb) @@ -639,7 +685,10 @@ static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len, /* The following routines assume being used to transfer byte, word, * long etc. - * This means that they assume "data" in machine endianness format. + * This means that + * - the write routines expect "data" in machine endianness format. + * The data will be converted to leXX in sisusb_xxx_packet. + * - the read routines can expect read data in machine-endianess. */ static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type, @@ -839,7 +888,7 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, if (get_user(swap16, (u16 __user *)userbuffer)) return -EFAULT; } else - swap16 = (kernbuffer[0] << 8) | kernbuffer[1]; + swap16 = *((u16 *)kernbuffer); ret = sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, @@ -855,14 +904,25 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, if (userbuffer) { if (copy_from_user(&buf, userbuffer, 3)) return -EFAULT; - +#ifdef __BIG_ENDIAN swap32 = (buf[0] << 16) | (buf[1] << 8) | buf[2]; +#else + swap32 = (buf[2] << 16) | + (buf[1] << 8) | + buf[0]; +#endif } else +#ifdef __BIG_ENDIAN swap32 = (kernbuffer[0] << 16) | (kernbuffer[1] << 8) | kernbuffer[2]; +#else + swap32 = (kernbuffer[2] << 16) | + (kernbuffer[1] << 8) | + kernbuffer[0]; +#endif ret = sisusb_write_memio_24bit(sisusb, SISUSB_TYPE_MEM, @@ -879,10 +939,7 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, if (get_user(swap32, (u32 __user *)userbuffer)) return -EFAULT; } else - swap32 = (kernbuffer[0] << 24) | - (kernbuffer[1] << 16) | - (kernbuffer[2] << 8) | - kernbuffer[3]; + swap32 = *((u32 *)kernbuffer); ret = sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, @@ -1005,6 +1062,10 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, return ret ? -EIO : 0; } +/* Remember: Read data in packet is in machine-endianess! So for + * byte, word, 24bit, long no endian correction is necessary. + */ + static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type, u32 addr, u8 *data) { @@ -1191,8 +1252,7 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, (u16 __user *)userbuffer)) return -EFAULT; } else { - kernbuffer[0] = swap16 >> 8; - kernbuffer[1] = swap16 & 0xff; + *((u16 *)kernbuffer) = swap16; } } return ret; @@ -1202,9 +1262,15 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, addr, &swap32); if (!ret) { (*bytes_read) += 3; +#ifdef __BIG_ENDIAN buf[0] = (swap32 >> 16) & 0xff; buf[1] = (swap32 >> 8) & 0xff; buf[2] = swap32 & 0xff; +#else + buf[2] = (swap32 >> 16) & 0xff; + buf[1] = (swap32 >> 8) & 0xff; + buf[0] = swap32 & 0xff; +#endif if (userbuffer) { if (copy_to_user(userbuffer, &buf[0], 3)) return -EFAULT; @@ -1228,10 +1294,7 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, userbuffer += 4; } else { - kernbuffer[0] = (swap32 >> 24) & 0xff; - kernbuffer[1] = (swap32 >> 16) & 0xff; - kernbuffer[2] = (swap32 >> 8) & 0xff; - kernbuffer[3] = swap32 & 0xff; + *((u32 *)kernbuffer) = swap32; kernbuffer += 4; } addr += 4; @@ -1289,7 +1352,24 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, /* High level: Gfx (indexed) register access */ -static int +#ifdef INCL_SISUSB_CON +int +sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data) +{ + return sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, data); +} + +int +sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data) +{ + return sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port, data); +} +#endif + +#ifndef INCL_SISUSB_CON +static +#endif +int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data) { int ret; @@ -1298,7 +1378,10 @@ sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data) return ret; } -static int +#ifndef INCL_SISUSB_CON +static +#endif +int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data) { int ret; @@ -1307,7 +1390,10 @@ sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data) return ret; } -static int +#ifndef INCL_SISUSB_CON +static +#endif +int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand, u8 myor) { @@ -1336,18 +1422,89 @@ sisusb_setidxregmask(struct sisusb_usb_data *sisusb, int port, u8 idx, return ret; } -static int +#ifndef INCL_SISUSB_CON +static +#endif +int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor) { return(sisusb_setidxregandor(sisusb, port, index, 0xff, myor)); } -static int +#ifndef INCL_SISUSB_CON +static +#endif +int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand) { return(sisusb_setidxregandor(sisusb, port, idx, myand, 0x00)); } +/* Write/read video ram */ + +#ifdef INCL_SISUSB_CON +int +sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data) +{ + return(sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data)); +} + +int +sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data) +{ + return(sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data)); +} + +int +sisusb_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data) +{ + return(sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, adr, data)); +} + +int +sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *data) +{ + return(sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM, adr, data)); +} + +int +sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src, + u32 dest, int length, size_t *bytes_written) +{ + return(sisusb_write_mem_bulk(sisusb, dest, src, length, NULL, 0, bytes_written)); +} + +#ifdef SISUSBENDIANTEST +int +sisusb_read_memory(struct sisusb_usb_data *sisusb, char *dest, + u32 src, int length, size_t *bytes_written) +{ + return(sisusb_read_mem_bulk(sisusb, src, dest, length, NULL, bytes_written)); +} +#endif +#endif + +#ifdef SISUSBENDIANTEST +static void +sisusb_testreadwrite(struct sisusb_usb_data *sisusb) +{ + static char srcbuffer[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; + char destbuffer[10]; + size_t dummy; + int i,j; + + sisusb_copy_memory(sisusb, srcbuffer, sisusb->vrambase, 7, &dummy); + + for(i = 1; i <= 7; i++) { + printk(KERN_DEBUG "sisusb: rwtest %d bytes\n", i); + sisusb_read_memory(sisusb, destbuffer, sisusb->vrambase, i, &dummy); + for(j = 0; j < i; j++) { + printk(KERN_DEBUG "sisusb: rwtest read[%d] = %x\n", j, destbuffer[j]); + } + } +} +#endif + /* access pci config registers (reg numbers 0, 4, 8, etc) */ static int @@ -2270,6 +2427,129 @@ sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen) return ret; } + +#ifdef INCL_SISUSB_CON + +/* Set up default text mode: + - Set text mode (0x03) + - Upload default font + - Upload user font (if available) +*/ + +int +sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) +{ + int ret = 0, slot = sisusb->font_slot, i; + struct font_desc *myfont; + u8 *tempbuf; + u16 *tempbufb; + size_t written; + static char bootstring[] = "SiSUSB VGA text console, (C) 2005 Thomas Winischhofer."; + static char bootlogo[] = "(o_ //\\ V_/_"; + + /* sisusb->lock is down */ + + if (!sisusb->SiS_Pr) + return 1; + + sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30; + sisusb->SiS_Pr->sisusb = (void *)sisusb; + + /* Set mode 0x03 */ + SiSUSBSetMode(sisusb->SiS_Pr, 0x03); + + if (!(myfont = find_font("VGA8x16"))) + return 1; + + if (!(tempbuf = vmalloc(8192))) + return 1; + + for (i = 0; i < 256; i++) + memcpy(tempbuf + (i * 32), myfont->data + (i * 16), 16); + + /* Upload default font */ + ret = sisusbcon_do_font_op(sisusb, 1, 0, tempbuf, 8192, 0, 1, NULL, 16, 0); + + vfree(tempbuf); + + /* Upload user font (and reset current slot) */ + if (sisusb->font_backup) { + ret |= sisusbcon_do_font_op(sisusb, 1, 2, sisusb->font_backup, + 8192, sisusb->font_backup_512, 1, NULL, + sisusb->font_backup_height, 0); + if (slot != 2) + sisusbcon_do_font_op(sisusb, 1, 0, NULL, 0, 0, 1, + NULL, 16, 0); + } + + if (init && !sisusb->scrbuf) { + + if ((tempbuf = vmalloc(8192))) { + + i = 4096; + tempbufb = (u16 *)tempbuf; + while (i--) + *(tempbufb++) = 0x0720; + + i = 0; + tempbufb = (u16 *)tempbuf; + while (bootlogo[i]) { + *(tempbufb++) = 0x0700 | bootlogo[i++]; + if (!(i % 4)) + tempbufb += 76; + } + + i = 0; + tempbufb = (u16 *)tempbuf + 6; + while (bootstring[i]) + *(tempbufb++) = 0x0700 | bootstring[i++]; + + ret |= sisusb_copy_memory(sisusb, tempbuf, + sisusb->vrambase, 8192, &written); + + vfree(tempbuf); + + } + + } else if (sisusb->scrbuf) { + + ret |= sisusb_copy_memory(sisusb, (char *)sisusb->scrbuf, + sisusb->vrambase, sisusb->scrbuf_size, &written); + + } + + if (sisusb->sisusb_cursor_size_from >= 0 && + sisusb->sisusb_cursor_size_to >= 0) { + sisusb_setidxreg(sisusb, SISCR, 0x0a, + sisusb->sisusb_cursor_size_from); + sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0, + sisusb->sisusb_cursor_size_to); + } else { + sisusb_setidxreg(sisusb, SISCR, 0x0a, 0x2d); + sisusb_setidxreg(sisusb, SISCR, 0x0b, 0x0e); + sisusb->sisusb_cursor_size_to = -1; + } + + slot = sisusb->sisusb_cursor_loc; + if(slot < 0) slot = 0; + + sisusb->sisusb_cursor_loc = -1; + sisusb->bad_cursor_pos = 1; + + sisusb_set_cursor(sisusb, slot); + + sisusb_setidxreg(sisusb, SISCR, 0x0c, (sisusb->cur_start_addr >> 8)); + sisusb_setidxreg(sisusb, SISCR, 0x0d, (sisusb->cur_start_addr & 0xff)); + + sisusb->textmodedestroyed = 0; + + /* sisusb->lock is down */ + + return ret; +} + +#endif + /* fops */ static int @@ -2329,7 +2609,7 @@ sisusb_open(struct inode *inode, struct file *file) } } - /* increment usage count for the device */ + /* Increment usage count for our sisusb */ kref_get(&sisusb->kref); sisusb->isopen = 1; @@ -2340,12 +2620,10 @@ sisusb_open(struct inode *inode, struct file *file) up(&disconnect_sem); - printk(KERN_DEBUG "sisusbvga[%d]: opened", sisusb->minor); - return 0; } -static void +void sisusb_delete(struct kref *kref) { struct sisusb_usb_data *sisusb = to_sisusb_dev(kref); @@ -2359,6 +2637,9 @@ sisusb_delete(struct kref *kref) sisusb->sisusb_dev = NULL; sisusb_free_buffers(sisusb); sisusb_free_urbs(sisusb); +#ifdef INCL_SISUSB_CON + kfree(sisusb->SiS_Pr); +#endif kfree(sisusb); } @@ -2395,8 +2676,6 @@ sisusb_release(struct inode *inode, struct file *file) up(&disconnect_sem); - printk(KERN_DEBUG "sisusbvga[%d]: released", myminor); - return 0; } @@ -2733,6 +3012,12 @@ sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y, int retval, port, length; u32 address; + /* All our commands require the device + * to be initialized. + */ + if (!sisusb->devinit) + return -ENODEV; + port = y->data3 - SISUSB_PCI_PSEUDO_IOPORTBASE + SISUSB_PCI_IOPORTBASE; @@ -2774,6 +3059,10 @@ sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y, break; case SUCMD_CLRSCR: + /* Gfx core must be initialized */ + if (!sisusb->gfxinit) + return -ENODEV; + length = (y->data0 << 16) | (y->data1 << 8) | y->data2; address = y->data3 - SISUSB_PCI_PSEUDO_MEMBASE + @@ -2781,11 +3070,61 @@ sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y, retval = sisusb_clear_vram(sisusb, address, length); break; + case SUCMD_HANDLETEXTMODE: + retval = 0; +#ifdef INCL_SISUSB_CON + /* Gfx core must be initialized, SiS_Pr must exist */ + if (!sisusb->gfxinit || !sisusb->SiS_Pr) + return -ENODEV; + + switch (y->data0) { + case 0: + retval = sisusb_reset_text_mode(sisusb, 0); + break; + case 1: + sisusb->textmodedestroyed = 1; + break; + } +#endif + break; + +#ifdef INCL_SISUSB_CON + case SUCMD_SETMODE: + /* Gfx core must be initialized, SiS_Pr must exist */ + if (!sisusb->gfxinit || !sisusb->SiS_Pr) + return -ENODEV; + + retval = 0; + + sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30; + sisusb->SiS_Pr->sisusb = (void *)sisusb; + + if (SiSUSBSetMode(sisusb->SiS_Pr, y->data3)) + retval = -EINVAL; + + break; + + case SUCMD_SETVESAMODE: + /* Gfx core must be initialized, SiS_Pr must exist */ + if (!sisusb->gfxinit || !sisusb->SiS_Pr) + return -ENODEV; + + retval = 0; + + sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30; + sisusb->SiS_Pr->sisusb = (void *)sisusb; + + if (SiSUSBSetVESAMode(sisusb->SiS_Pr, y->data3)) + retval = -EINVAL; + + break; +#endif + default: retval = -EINVAL; } - if(retval > 0) + if (retval > 0) retval = -EIO; return retval; @@ -2835,6 +3174,11 @@ sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, x.sisusb_vramsize = sisusb->vramsize; x.sisusb_minor = sisusb->minor; x.sisusb_fbdevactive= 0; +#ifdef INCL_SISUSB_CON + x.sisusb_conactive = sisusb->haveconsole ? 1 : 0; +#else + x.sisusb_conactive = 0; +#endif if (copy_to_user((void __user *)arg, &x, sizeof(x))) retval = -EFAULT; @@ -2895,9 +3239,13 @@ static struct file_operations usb_sisusb_fops = { }; static struct usb_class_driver usb_sisusb_class = { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13) .name = "usb/sisusbvga%d", - .fops = &usb_sisusb_fops, .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, +#else + .name = "sisusbvga%d", +#endif + .fops = &usb_sisusb_fops, .minor_base = SISUSB_MINOR }; @@ -2994,12 +3342,25 @@ static int sisusb_probe(struct usb_interface *intf, printk(KERN_INFO "sisusbvga[%d]: Allocated %d output buffers\n", sisusb->minor, sisusb->numobufs); +#ifdef INCL_SISUSB_CON + /* Allocate our SiS_Pr */ + if (!(sisusb->SiS_Pr = kmalloc(sizeof(struct SiS_Private), GFP_KERNEL))) { + printk(KERN_ERR + "sisusbvga[%d]: Failed to allocate SiS_Pr\n", + sisusb->minor); + } +#endif + /* Do remaining init stuff */ init_waitqueue_head(&sisusb->wait_q); usb_set_intfdata(intf, sisusb); + usb_get_dev(sisusb->sisusb_dev); + + sisusb->present = 1; + #ifdef SISUSB_OLD_CONFIG_COMPAT { int ret; @@ -3014,14 +3375,19 @@ static int sisusb_probe(struct usb_interface *intf, sisusb->minor); else sisusb->ioctl32registered = 1; - } #endif - sisusb->present = 1; - if (dev->speed == USB_SPEED_HIGH) { - if (sisusb_init_gfxdevice(sisusb, 1)) + int initscreen = 1; +#ifdef INCL_SISUSB_CON + if (sisusb_first_vc > 0 && + sisusb_last_vc > 0 && + sisusb_first_vc <= sisusb_last_vc && + sisusb_last_vc <= MAX_NR_CONSOLES) + initscreen = 0; +#endif + if (sisusb_init_gfxdevice(sisusb, initscreen)) printk(KERN_ERR "sisusbvga[%d]: Failed to early " "initialize device\n", @@ -3035,6 +3401,16 @@ static int sisusb_probe(struct usb_interface *intf, sisusb->ready = 1; +#ifdef SISUSBENDIANTEST + printk(KERN_DEBUG "sisusb: *** RWTEST ***\n"); + sisusb_testreadwrite(sisusb); + printk(KERN_DEBUG "sisusb: *** RWTEST END ***\n"); +#endif + +#ifdef INCL_SISUSB_CON + sisusb_console_init(sisusb, sisusb_first_vc, sisusb_last_vc); +#endif + return 0; error_4: @@ -3053,13 +3429,20 @@ static void sisusb_disconnect(struct usb_interface *intf) struct sisusb_usb_data *sisusb; int minor; - down(&disconnect_sem); - /* This should *not* happen */ - if (!(sisusb = usb_get_intfdata(intf))) { - up(&disconnect_sem); + if (!(sisusb = usb_get_intfdata(intf))) return; - } + +#ifdef INCL_SISUSB_CON + sisusb_console_exit(sisusb); +#endif + + /* The above code doesn't need the disconnect + * semaphore to be down; its meaning is to + * protect all other routines from the disconnect + * case, not the other way round. + */ + down(&disconnect_sem); down(&sisusb->lock); @@ -3123,11 +3506,17 @@ static int __init usb_sisusb_init(void) { int retval; +#ifdef INCL_SISUSB_CON + sisusb_init_concode(); +#endif + if (!(retval = usb_register(&sisusb_driver))) { + printk(KERN_INFO "sisusb: Driver version %d.%d.%d\n", SISUSB_VERSION, SISUSB_REVISION, SISUSB_PATCHLEVEL); printk(KERN_INFO "sisusb: Copyright (C) 2005 Thomas Winischhofer\n"); + } return retval; @@ -3142,6 +3531,6 @@ module_init(usb_sisusb_init); module_exit(usb_sisusb_exit); MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>"); -MODULE_DESCRIPTION("sisusb - Driver for Net2280/SiS315-based USB2VGA dongles"); +MODULE_DESCRIPTION("sisusbvga - Driver for Net2280/SiS315-based USB2VGA dongles"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/misc/sisusbvga/sisusb.h b/drivers/usb/misc/sisusbvga/sisusb.h index 1306d006a25..401ff21d788 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.h +++ b/drivers/usb/misc/sisusbvga/sisusb.h @@ -46,15 +46,36 @@ #endif #endif +/* For older kernels, support for text consoles is by default + * off. To ensable text console support, change the following: + */ +#if 0 +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13) +#define CONFIG_USB_SISUSBVGA_CON +#endif +#endif + /* Version Information */ #define SISUSB_VERSION 0 #define SISUSB_REVISION 0 -#define SISUSB_PATCHLEVEL 7 +#define SISUSB_PATCHLEVEL 8 + +/* Include console and mode switching code? */ + +#ifdef CONFIG_USB_SISUSBVGA_CON +#define INCL_SISUSB_CON 1 +#endif + +#ifdef INCL_SISUSB_CON +#include <linux/console.h> +#include <linux/vt_kern.h> +#include "sisusb_struct.h" +#endif /* USB related */ -#define SISUSB_MINOR 133 /* FIXME */ +#define SISUSB_MINOR 133 /* official */ /* Size of the sisusb input/output buffers */ #define SISUSB_IBUF_SIZE 0x01000 @@ -131,6 +152,26 @@ struct sisusb_usb_data { unsigned char gfxinit; /* graphics core initialized? */ unsigned short chipid, chipvendor; unsigned short chiprevision; +#ifdef INCL_SISUSB_CON + struct SiS_Private *SiS_Pr; + unsigned long scrbuf; + unsigned int scrbuf_size; + int haveconsole, con_first, con_last; + int havethisconsole[MAX_NR_CONSOLES]; + int textmodedestroyed; + unsigned int sisusb_num_columns; /* real number, not vt's idea */ + int cur_start_addr, con_rolled_over; + int sisusb_cursor_loc, bad_cursor_pos; + int sisusb_cursor_size_from; + int sisusb_cursor_size_to; + int current_font_height, current_font_512; + int font_backup_size, font_backup_height, font_backup_512; + char *font_backup; + int font_slot; + struct vc_data *sisusb_display_fg; + int is_gfx; + int con_blanked; +#endif }; #define to_sisusb_dev(d) container_of(d, struct sisusb_usb_data, kref) @@ -249,7 +290,9 @@ struct sisusb_info { __u32 sisusb_fbdevactive; /* != 0 if framebuffer device active */ - __u8 sisusb_reserved[32]; /* for future use */ + __u32 sisusb_conactive; /* != 0 if console driver active */ + + __u8 sisusb_reserved[28]; /* for future use */ }; struct sisusb_command { @@ -261,18 +304,24 @@ struct sisusb_command { __u32 data4; /* for future use */ }; -#define SUCMD_GET 0x01 /* for all: data0 = index, data3 = port */ -#define SUCMD_SET 0x02 /* data1 = value */ -#define SUCMD_SETOR 0x03 /* data1 = or */ -#define SUCMD_SETAND 0x04 /* data1 = and */ -#define SUCMD_SETANDOR 0x05 /* data1 = and, data2 = or */ -#define SUCMD_SETMASK 0x06 /* data1 = data, data2 = mask */ +#define SUCMD_GET 0x01 /* for all: data0 = index, data3 = port */ +#define SUCMD_SET 0x02 /* data1 = value */ +#define SUCMD_SETOR 0x03 /* data1 = or */ +#define SUCMD_SETAND 0x04 /* data1 = and */ +#define SUCMD_SETANDOR 0x05 /* data1 = and, data2 = or */ +#define SUCMD_SETMASK 0x06 /* data1 = data, data2 = mask */ -#define SUCMD_CLRSCR 0x07 /* data0:1:2 = length, data3 = address */ +#define SUCMD_CLRSCR 0x07 /* data0:1:2 = length, data3 = address */ + +#define SUCMD_HANDLETEXTMODE 0x08 /* Reset/destroy text mode */ + +#define SUCMD_SETMODE 0x09 /* Set a display mode (data3 = SiS mode) */ +#define SUCMD_SETVESAMODE 0x0a /* Set a display mode (data3 = VESA mode) */ #define SISUSB_COMMAND _IOWR(0xF3,0x3D,struct sisusb_command) -#define SISUSB_GET_CONFIG_SIZE _IOR(0xF3,0x3E,__u32) -#define SISUSB_GET_CONFIG _IOR(0xF3,0x3F,struct sisusb_info) +#define SISUSB_GET_CONFIG_SIZE _IOR(0xF3,0x3E,__u32) +#define SISUSB_GET_CONFIG _IOR(0xF3,0x3F,struct sisusb_info) + #endif /* SISUSB_H */ diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c new file mode 100644 index 00000000000..24584463553 --- /dev/null +++ b/drivers/usb/misc/sisusbvga/sisusb_con.c @@ -0,0 +1,1658 @@ +/* + * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles + * + * VGA text mode console part + * + * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria + * + * If distributed as part of the Linux kernel, this code is licensed under the + * terms of the GPL v2. + * + * Otherwise, the following license terms apply: + * + * * Redistribution and use in source and binary forms, with or without + * * modification, are permitted provided that the following conditions + * * are met: + * * 1) Redistributions of source code must retain the above copyright + * * notice, this list of conditions and the following disclaimer. + * * 2) Redistributions in binary form must reproduce the above copyright + * * notice, this list of conditions and the following disclaimer in the + * * documentation and/or other materials provided with the distribution. + * * 3) The name of the author may not be used to endorse or promote products + * * derived from this software without specific psisusbr written permission. + * * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR + * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Thomas Winischhofer <thomas@winischhofer.net> + * + * Portions based on vgacon.c which are + * Created 28 Sep 1997 by Geert Uytterhoeven + * Rewritten by Martin Mares <mj@ucw.cz>, July 1998 + * based on code Copyright (C) 1991, 1992 Linus Torvalds + * 1995 Jay Estabrook + * + * A note on using in_atomic() in here: We can't handle console + * calls from non-schedulable context due to our USB-dependend + * nature. For now, this driver just ignores any calls if it + * detects this state. + * + */ + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/string.h> +#include <linux/kd.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/vt_kern.h> +#include <linux/selection.h> +#include <linux/spinlock.h> +#include <linux/kref.h> +#include <linux/smp_lock.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/vmalloc.h> + +#include "sisusb.h" + +#ifdef INCL_SISUSB_CON +extern int sisusb_setreg(struct sisusb_usb_data *, int, u8); +extern int sisusb_getreg(struct sisusb_usb_data *, int, u8 *); +extern int sisusb_setidxreg(struct sisusb_usb_data *, int, u8, u8); +extern int sisusb_getidxreg(struct sisusb_usb_data *, int, u8, u8 *); +extern int sisusb_setidxregor(struct sisusb_usb_data *, int, u8, u8); +extern int sisusb_setidxregand(struct sisusb_usb_data *, int, u8, u8); +extern int sisusb_setidxregandor(struct sisusb_usb_data *, int, u8, u8, u8); + +extern int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data); +extern int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data); +extern int sisusb_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data); +extern int sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *data); +extern int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src, + u32 dest, int length, size_t *bytes_written); + +extern void sisusb_delete(struct kref *kref); +extern int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init); + +extern int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo); + +#define sisusbcon_writew(val, addr) (*(addr) = (val)) +#define sisusbcon_readw(addr) (*(addr)) +#define sisusbcon_memmovew(d, s, c) memmove(d, s, c) +#define sisusbcon_memcpyw(d, s, c) memcpy(d, s, c) + +/* vc_data -> sisusb conversion table */ +static struct sisusb_usb_data *mysisusbs[MAX_NR_CONSOLES]; + +/* Forward declaration */ +static const struct consw sisusb_con; + +extern struct semaphore disconnect_sem; + +static inline void +sisusbcon_memsetw(u16 *s, u16 c, unsigned int count) +{ + count /= 2; + while (count--) + sisusbcon_writew(c, s++); +} + +static inline void +sisusb_initialize(struct sisusb_usb_data *sisusb) +{ + /* Reset cursor and start address */ + if (sisusb_setidxreg(sisusb, SISCR, 0x0c, 0x00)) + return; + if (sisusb_setidxreg(sisusb, SISCR, 0x0d, 0x00)) + return; + if (sisusb_setidxreg(sisusb, SISCR, 0x0e, 0x00)) + return; + sisusb_setidxreg(sisusb, SISCR, 0x0f, 0x00); +} + +static inline void +sisusbcon_set_start_address(struct sisusb_usb_data *sisusb, struct vc_data *c) +{ + sisusb->cur_start_addr = (c->vc_visible_origin - sisusb->scrbuf) / 2; + + sisusb_setidxreg(sisusb, SISCR, 0x0c, (sisusb->cur_start_addr >> 8)); + sisusb_setidxreg(sisusb, SISCR, 0x0d, (sisusb->cur_start_addr & 0xff)); +} + +void +sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location) +{ + if (sisusb->sisusb_cursor_loc == location) + return; + + sisusb->sisusb_cursor_loc = location; + + /* Hardware bug: Text cursor appears twice or not at all + * at some positions. Work around it with the cursor skew + * bits. + */ + + if ((location & 0x0007) == 0x0007) { + sisusb->bad_cursor_pos = 1; + location--; + if (sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0x1f, 0x20)) + return; + } else if (sisusb->bad_cursor_pos) { + if (sisusb_setidxregand(sisusb, SISCR, 0x0b, 0x1f)) + return; + sisusb->bad_cursor_pos = 0; + } + + if (sisusb_setidxreg(sisusb, SISCR, 0x0e, (location >> 8))) + return; + sisusb_setidxreg(sisusb, SISCR, 0x0f, (location & 0xff)); +} + +static inline struct sisusb_usb_data * +sisusb_get_sisusb(unsigned short console) +{ + return mysisusbs[console]; +} + +static inline int +sisusb_sisusb_valid(struct sisusb_usb_data *sisusb) +{ + if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) + return 0; + + return 1; +} + +static struct sisusb_usb_data * +sisusb_get_sisusb_lock_and_check(unsigned short console) +{ + struct sisusb_usb_data *sisusb; + + /* We can't handle console calls in non-schedulable + * context due to our locks and the USB transport. + * So we simply ignore them. This should only affect + * some calls to printk. + */ + if (in_atomic()) + return NULL; + + if (!(sisusb = sisusb_get_sisusb(console))) + return NULL; + + down(&sisusb->lock); + + if (!sisusb_sisusb_valid(sisusb) || + !sisusb->havethisconsole[console]) { + up(&sisusb->lock); + return NULL; + } + + return sisusb; +} + +static int +sisusb_is_inactive(struct vc_data *c, struct sisusb_usb_data *sisusb) +{ + if (sisusb->is_gfx || + sisusb->textmodedestroyed || + c->vc_mode != KD_TEXT) + return 1; + + return 0; +} + +/* con_startup console interface routine */ +static const char * +sisusbcon_startup(void) +{ + return "SISUSBCON"; +} + +/* con_init console interface routine */ +static void +sisusbcon_init(struct vc_data *c, int init) +{ + struct sisusb_usb_data *sisusb; + int cols, rows; + + /* This is called by take_over_console(), + * ie by us/under our control. It is + * only called after text mode and fonts + * are set up/restored. + */ + + down(&disconnect_sem); + + if (!(sisusb = sisusb_get_sisusb(c->vc_num))) { + up(&disconnect_sem); + return; + } + + down(&sisusb->lock); + + if (!sisusb_sisusb_valid(sisusb)) { + up(&sisusb->lock); + up(&disconnect_sem); + return; + } + + c->vc_can_do_color = 1; + + c->vc_complement_mask = 0x7700; + + c->vc_hi_font_mask = sisusb->current_font_512 ? 0x0800 : 0; + + sisusb->haveconsole = 1; + + sisusb->havethisconsole[c->vc_num] = 1; + + /* We only support 640x400 */ + c->vc_scan_lines = 400; + + c->vc_font.height = sisusb->current_font_height; + + /* We only support width = 8 */ + cols = 80; + rows = c->vc_scan_lines / c->vc_font.height; + + /* Increment usage count for our sisusb. + * Doing so saves us from upping/downing + * the disconnect semaphore; we can't + * lose our sisusb until this is undone + * in con_deinit. For all other console + * interface functions, it suffices to + * use sisusb->lock and do a quick check + * of sisusb for device disconnection. + */ + kref_get(&sisusb->kref); + + if (!*c->vc_uni_pagedir_loc) + con_set_default_unimap(c); + + up(&sisusb->lock); + + up(&disconnect_sem); + + if (init) { + c->vc_cols = cols; + c->vc_rows = rows; + } else + vc_resize(c, cols, rows); +} + +/* con_deinit console interface routine */ +static void +sisusbcon_deinit(struct vc_data *c) +{ + struct sisusb_usb_data *sisusb; + int i; + + /* This is called by take_over_console() + * and others, ie not under our control. + */ + + down(&disconnect_sem); + + if (!(sisusb = sisusb_get_sisusb(c->vc_num))) { + up(&disconnect_sem); + return; + } + + down(&sisusb->lock); + + /* Clear ourselves in mysisusbs */ + mysisusbs[c->vc_num] = NULL; + + sisusb->havethisconsole[c->vc_num] = 0; + + /* Free our font buffer if all consoles are gone */ + if (sisusb->font_backup) { + for(i = 0; i < MAX_NR_CONSOLES; i++) { + if (sisusb->havethisconsole[c->vc_num]) + break; + } + if (i == MAX_NR_CONSOLES) { + vfree(sisusb->font_backup); + sisusb->font_backup = NULL; + } + } + + up(&sisusb->lock); + + /* decrement the usage count on our sisusb */ + kref_put(&sisusb->kref, sisusb_delete); + + up(&disconnect_sem); +} + +/* interface routine */ +static u8 +sisusbcon_build_attr(struct vc_data *c, u8 color, u8 intensity, + u8 blink, u8 underline, u8 reverse) +{ + u8 attr = color; + + if (underline) + attr = (attr & 0xf0) | c->vc_ulcolor; + else if (intensity == 0) + attr = (attr & 0xf0) | c->vc_halfcolor; + + if (reverse) + attr = ((attr) & 0x88) | + ((((attr) >> 4) | + ((attr) << 4)) & 0x77); + + if (blink) + attr ^= 0x80; + + if (intensity == 2) + attr ^= 0x08; + + return attr; +} + +/* Interface routine */ +static void +sisusbcon_invert_region(struct vc_data *vc, u16 *p, int count) +{ + /* Invert a region. This is called with a pointer + * to the console's internal screen buffer. So we + * simply do the inversion there and rely on + * a call to putc(s) to update the real screen. + */ + + while (count--) { + u16 a = sisusbcon_readw(p); + + a = ((a) & 0x88ff) | + (((a) & 0x7000) >> 4) | + (((a) & 0x0700) << 4); + + sisusbcon_writew(a, p++); + } +} + +#define SISUSB_VADDR(x,y) \ + ((u16 *)c->vc_origin + \ + (y) * sisusb->sisusb_num_columns + \ + (x)) + +#define SISUSB_HADDR(x,y) \ + ((u16 *)(sisusb->vrambase + (c->vc_origin - sisusb->scrbuf)) + \ + (y) * sisusb->sisusb_num_columns + \ + (x)) + +/* Interface routine */ +static void +sisusbcon_putc(struct vc_data *c, int ch, int y, int x) +{ + struct sisusb_usb_data *sisusb; + ssize_t written; + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return; + + /* sisusb->lock is down */ + + /* Don't need to put the character into buffer ourselves, + * because the vt does this BEFORE calling us. + */ +#if 0 + sisusbcon_writew(ch, SISUSB_VADDR(x, y)); +#endif + + if (sisusb_is_inactive(c, sisusb)) { + up(&sisusb->lock); + return; + } + + + sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y), + (u32)SISUSB_HADDR(x, y), 2, &written); + + up(&sisusb->lock); +} + +/* Interface routine */ +static void +sisusbcon_putcs(struct vc_data *c, const unsigned short *s, + int count, int y, int x) +{ + struct sisusb_usb_data *sisusb; + ssize_t written; + u16 *dest; + int i; + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return; + + /* sisusb->lock is down */ + + /* Need to put the characters into the buffer ourselves, + * because the vt does this AFTER calling us. + */ + + dest = SISUSB_VADDR(x, y); + + for (i = count; i > 0; i--) + sisusbcon_writew(sisusbcon_readw(s++), dest++); + + if (sisusb_is_inactive(c, sisusb)) { + up(&sisusb->lock); + return; + } + + sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y), + (u32)SISUSB_HADDR(x, y), count * 2, &written); + + up(&sisusb->lock); +} + +/* Interface routine */ +static void +sisusbcon_clear(struct vc_data *c, int y, int x, int height, int width) +{ + struct sisusb_usb_data *sisusb; + u16 eattr = c->vc_video_erase_char; + ssize_t written; + int i, length, cols; + u16 *dest; + + if (width <= 0 || height <= 0) + return; + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return; + + /* sisusb->lock is down */ + + /* Need to clear buffer ourselves, because the vt does + * this AFTER calling us. + */ + + dest = SISUSB_VADDR(x, y); + + cols = sisusb->sisusb_num_columns; + + if (width > cols) + width = cols; + + if (x == 0 && width >= c->vc_cols) { + + sisusbcon_memsetw(dest, eattr, height * cols * 2); + + } else { + + for (i = height; i > 0; i--, dest += cols) + sisusbcon_memsetw(dest, eattr, width * 2); + + } + + if (sisusb_is_inactive(c, sisusb)) { + up(&sisusb->lock); + return; + } + + length = ((height * cols) - x - (cols - width - x)) * 2; + + + sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(x, y), + (u32)SISUSB_HADDR(x, y), length, &written); + + up(&sisusb->lock); +} + +/* Interface routine */ +static void +sisusbcon_bmove(struct vc_data *c, int sy, int sx, + int dy, int dx, int height, int width) +{ + struct sisusb_usb_data *sisusb; + ssize_t written; + int cols, length; +#if 0 + u16 *src, *dest; + int i; +#endif + + if (width <= 0 || height <= 0) + return; + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return; + + /* sisusb->lock is down */ + + cols = sisusb->sisusb_num_columns; + + /* Don't need to move data outselves, because + * vt does this BEFORE calling us. + * This is only used by vt's insert/deletechar. + */ +#if 0 + if (sx == 0 && dx == 0 && width >= c->vc_cols && width <= cols) { + + sisusbcon_memmovew(SISUSB_VADDR(0, dy), SISUSB_VADDR(0, sy), + height * width * 2); + + } else if (dy < sy || (dy == sy && dx < sx)) { + + src = SISUSB_VADDR(sx, sy); + dest = SISUSB_VADDR(dx, dy); + + for (i = height; i > 0; i--) { + sisusbcon_memmovew(dest, src, width * 2); + src += cols; + dest += cols; + } + + } else { + + src = SISUSB_VADDR(sx, sy + height - 1); + dest = SISUSB_VADDR(dx, dy + height - 1); + + for (i = height; i > 0; i--) { + sisusbcon_memmovew(dest, src, width * 2); + src -= cols; + dest -= cols; + } + + } +#endif + + if (sisusb_is_inactive(c, sisusb)) { + up(&sisusb->lock); + return; + } + + length = ((height * cols) - dx - (cols - width - dx)) * 2; + + + sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(dx, dy), + (u32)SISUSB_HADDR(dx, dy), length, &written); + + up(&sisusb->lock); +} + +/* interface routine */ +static int +sisusbcon_switch(struct vc_data *c) +{ + struct sisusb_usb_data *sisusb; + ssize_t written; + int length; + + /* Returnvalue 0 means we have fully restored screen, + * and vt doesn't need to call do_update_region(). + * Returnvalue != 0 naturally means the opposite. + */ + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return 0; + + /* sisusb->lock is down */ + + /* Don't write to screen if in gfx mode */ + if (sisusb_is_inactive(c, sisusb)) { + up(&sisusb->lock); + return 0; + } + + /* That really should not happen. It would mean we are + * being called while the vc is using its private buffer + * as origin. + */ + if (c->vc_origin == (unsigned long)c->vc_screenbuf) { + up(&sisusb->lock); + printk(KERN_DEBUG "sisusb: ASSERT ORIGIN != SCREENBUF!\n"); + return 0; + } + + /* Check that we don't copy too much */ + length = min((int)c->vc_screenbuf_size, + (int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin)); + + /* Restore the screen contents */ + sisusbcon_memcpyw((u16 *)c->vc_origin, (u16 *)c->vc_screenbuf, + length); + + sisusb_copy_memory(sisusb, (unsigned char *)c->vc_origin, + (u32)SISUSB_HADDR(0, 0), + length, &written); + + up(&sisusb->lock); + + return 0; +} + +/* interface routine */ +static void +sisusbcon_save_screen(struct vc_data *c) +{ + struct sisusb_usb_data *sisusb; + int length; + + /* Save the current screen contents to vc's private + * buffer. + */ + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return; + + /* sisusb->lock is down */ + + if (sisusb_is_inactive(c, sisusb)) { + up(&sisusb->lock); + return; + } + + /* Check that we don't copy too much */ + length = min((int)c->vc_screenbuf_size, + (int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin)); + + /* Save the screen contents to vc's private buffer */ + sisusbcon_memcpyw((u16 *)c->vc_screenbuf, (u16 *)c->vc_origin, + length); + + up(&sisusb->lock); +} + +/* interface routine */ +static int +sisusbcon_set_palette(struct vc_data *c, unsigned char *table) +{ + struct sisusb_usb_data *sisusb; + int i, j; + + /* Return value not used by vt */ + + if (!CON_IS_VISIBLE(c)) + return -EINVAL; + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return -EINVAL; + + /* sisusb->lock is down */ + + if (sisusb_is_inactive(c, sisusb)) { + up(&sisusb->lock); + return -EINVAL; + } + + for (i = j = 0; i < 16; i++) { + if (sisusb_setreg(sisusb, SISCOLIDX, table[i])) + break; + if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2)) + break; + if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2)) + break; + if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2)) + break; + } + + up(&sisusb->lock); + + return 0; +} + +/* interface routine */ +static int +sisusbcon_blank(struct vc_data *c, int blank, int mode_switch) +{ + struct sisusb_usb_data *sisusb; + u8 sr1, cr17, pmreg, cr63; + ssize_t written; + int ret = 0; + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return 0; + + /* sisusb->lock is down */ + + if (mode_switch) + sisusb->is_gfx = blank ? 1 : 0; + + if (sisusb_is_inactive(c, sisusb)) { + up(&sisusb->lock); + return 0; + } + + switch (blank) { + + case 1: /* Normal blanking: Clear screen */ + case -1: + sisusbcon_memsetw((u16 *)c->vc_origin, + c->vc_video_erase_char, + c->vc_screenbuf_size); + sisusb_copy_memory(sisusb, + (unsigned char *)c->vc_origin, + (u32)(sisusb->vrambase + + (c->vc_origin - sisusb->scrbuf)), + c->vc_screenbuf_size, &written); + sisusb->con_blanked = 1; + ret = 1; + break; + + default: /* VESA blanking */ + switch (blank) { + case 0: /* Unblank */ + sr1 = 0x00; + cr17 = 0x80; + pmreg = 0x00; + cr63 = 0x00; + ret = 1; + sisusb->con_blanked = 0; + break; + case VESA_VSYNC_SUSPEND + 1: + sr1 = 0x20; + cr17 = 0x80; + pmreg = 0x80; + cr63 = 0x40; + break; + case VESA_HSYNC_SUSPEND + 1: + sr1 = 0x20; + cr17 = 0x80; + pmreg = 0x40; + cr63 = 0x40; + break; + case VESA_POWERDOWN + 1: + sr1 = 0x20; + cr17 = 0x00; + pmreg = 0xc0; + cr63 = 0x40; + break; + default: + up(&sisusb->lock); + return -EINVAL; + } + + sisusb_setidxregandor(sisusb, SISSR, 0x01, ~0x20, sr1); + sisusb_setidxregandor(sisusb, SISCR, 0x17, 0x7f, cr17); + sisusb_setidxregandor(sisusb, SISSR, 0x1f, 0x3f, pmreg); + sisusb_setidxregandor(sisusb, SISCR, 0x63, 0xbf, cr63); + + } + + up(&sisusb->lock); + + return ret; +} + +/* interface routine */ +static int +sisusbcon_scrolldelta(struct vc_data *c, int lines) +{ + struct sisusb_usb_data *sisusb; + int margin = c->vc_size_row * 4; + int ul, we, p, st; + + /* The return value does not seem to be used */ + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return 0; + + /* sisusb->lock is down */ + + if (sisusb_is_inactive(c, sisusb)) { + up(&sisusb->lock); + return 0; + } + + if (!lines) /* Turn scrollback off */ + c->vc_visible_origin = c->vc_origin; + else { + + if (sisusb->con_rolled_over > + (c->vc_scr_end - sisusb->scrbuf) + margin) { + + ul = c->vc_scr_end - sisusb->scrbuf; + we = sisusb->con_rolled_over + c->vc_size_row; + + } else { + + ul = 0; + we = sisusb->scrbuf_size; + + } + + p = (c->vc_visible_origin - sisusb->scrbuf - ul + we) % we + + lines * c->vc_size_row; + + st = (c->vc_origin - sisusb->scrbuf - ul + we) % we; + + if (st < 2 * margin) + margin = 0; + + if (p < margin) + p = 0; + + if (p > st - margin) + p = st; + + c->vc_visible_origin = sisusb->scrbuf + (p + ul) % we; + } + + sisusbcon_set_start_address(sisusb, c); + + up(&sisusb->lock); + + return 1; +} + +/* Interface routine */ +static void +sisusbcon_cursor(struct vc_data *c, int mode) +{ + struct sisusb_usb_data *sisusb; + int from, to, baseline; + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return; + + /* sisusb->lock is down */ + + if (sisusb_is_inactive(c, sisusb)) { + up(&sisusb->lock); + return; + } + + if (c->vc_origin != c->vc_visible_origin) { + c->vc_visible_origin = c->vc_origin; + sisusbcon_set_start_address(sisusb, c); + } + + if (mode == CM_ERASE) { + sisusb_setidxregor(sisusb, SISCR, 0x0a, 0x20); + sisusb->sisusb_cursor_size_to = -1; + up(&sisusb->lock); + return; + } + + sisusb_set_cursor(sisusb, (c->vc_pos - sisusb->scrbuf) / 2); + + baseline = c->vc_font.height - (c->vc_font.height < 10 ? 1 : 2); + + switch (c->vc_cursor_type & 0x0f) { + case CUR_BLOCK: from = 1; + to = c->vc_font.height; + break; + case CUR_TWO_THIRDS: from = c->vc_font.height / 3; + to = baseline; + break; + case CUR_LOWER_HALF: from = c->vc_font.height / 2; + to = baseline; + break; + case CUR_LOWER_THIRD: from = (c->vc_font.height * 2) / 3; + to = baseline; + break; + case CUR_NONE: from = 31; + to = 30; + break; + default: + case CUR_UNDERLINE: from = baseline - 1; + to = baseline; + break; + } + + if (sisusb->sisusb_cursor_size_from != from || + sisusb->sisusb_cursor_size_to != to) { + + sisusb_setidxreg(sisusb, SISCR, 0x0a, from); + sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0, to); + + sisusb->sisusb_cursor_size_from = from; + sisusb->sisusb_cursor_size_to = to; + } + + up(&sisusb->lock); +} + +static int +sisusbcon_scroll_area(struct vc_data *c, struct sisusb_usb_data *sisusb, + int t, int b, int dir, int lines) +{ + int cols = sisusb->sisusb_num_columns; + int length = ((b - t) * cols) * 2; + u16 eattr = c->vc_video_erase_char; + ssize_t written; + + /* sisusb->lock is down */ + + /* Scroll an area which does not match the + * visible screen's dimensions. This needs + * to be done separately, as it does not + * use hardware panning. + */ + + switch (dir) { + + case SM_UP: + sisusbcon_memmovew(SISUSB_VADDR(0, t), + SISUSB_VADDR(0, t + lines), + (b - t - lines) * cols * 2); + sisusbcon_memsetw(SISUSB_VADDR(0, b - lines), eattr, + lines * cols * 2); + break; + + case SM_DOWN: + sisusbcon_memmovew(SISUSB_VADDR(0, t + lines), + SISUSB_VADDR(0, t), + (b - t - lines) * cols * 2); + sisusbcon_memsetw(SISUSB_VADDR(0, t), eattr, + lines * cols * 2); + break; + } + + sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(0, t), + (u32)SISUSB_HADDR(0, t), length, &written); + + up(&sisusb->lock); + + return 1; +} + +/* Interface routine */ +static int +sisusbcon_scroll(struct vc_data *c, int t, int b, int dir, int lines) +{ + struct sisusb_usb_data *sisusb; + u16 eattr = c->vc_video_erase_char; + ssize_t written; + int copyall = 0; + unsigned long oldorigin; + unsigned int delta = lines * c->vc_size_row; + u32 originoffset; + + /* Returning != 0 means we have done the scrolling successfully. + * Returning 0 makes vt do the scrolling on its own. + * Note that con_scroll is only called if the console is + * visible. In that case, the origin should be our buffer, + * not the vt's private one. + */ + + if (!lines) + return 1; + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return 0; + + /* sisusb->lock is down */ + + if (sisusb_is_inactive(c, sisusb)) { + up(&sisusb->lock); + return 0; + } + + /* Special case */ + if (t || b != c->vc_rows) + return sisusbcon_scroll_area(c, sisusb, t, b, dir, lines); + + if (c->vc_origin != c->vc_visible_origin) { + c->vc_visible_origin = c->vc_origin; + sisusbcon_set_start_address(sisusb, c); + } + + /* limit amount to maximum realistic size */ + if (lines > c->vc_rows) + lines = c->vc_rows; + + oldorigin = c->vc_origin; + + switch (dir) { + + case SM_UP: + + if (c->vc_scr_end + delta >= + sisusb->scrbuf + sisusb->scrbuf_size) { + sisusbcon_memcpyw((u16 *)sisusb->scrbuf, + (u16 *)(oldorigin + delta), + c->vc_screenbuf_size - delta); + c->vc_origin = sisusb->scrbuf; + sisusb->con_rolled_over = oldorigin - sisusb->scrbuf; + copyall = 1; + } else + c->vc_origin += delta; + + sisusbcon_memsetw( + (u16 *)(c->vc_origin + c->vc_screenbuf_size - delta), + eattr, delta); + + break; + + case SM_DOWN: + + if (oldorigin - delta < sisusb->scrbuf) { + sisusbcon_memmovew((u16 *)(sisusb->scrbuf + + sisusb->scrbuf_size - + c->vc_screenbuf_size + + delta), + (u16 *)oldorigin, + c->vc_screenbuf_size - delta); + c->vc_origin = sisusb->scrbuf + + sisusb->scrbuf_size - + c->vc_screenbuf_size; + sisusb->con_rolled_over = 0; + copyall = 1; + } else + c->vc_origin -= delta; + + c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size; + + scr_memsetw((u16 *)(c->vc_origin), eattr, delta); + + break; + } + + originoffset = (u32)(c->vc_origin - sisusb->scrbuf); + + if (copyall) + sisusb_copy_memory(sisusb, + (char *)c->vc_origin, + (u32)(sisusb->vrambase + originoffset), + c->vc_screenbuf_size, &written); + else if (dir == SM_UP) + sisusb_copy_memory(sisusb, + (char *)c->vc_origin + c->vc_screenbuf_size - delta, + (u32)sisusb->vrambase + originoffset + + c->vc_screenbuf_size - delta, + delta, &written); + else + sisusb_copy_memory(sisusb, + (char *)c->vc_origin, + (u32)(sisusb->vrambase + originoffset), + delta, &written); + + c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size; + c->vc_visible_origin = c->vc_origin; + + sisusbcon_set_start_address(sisusb, c); + + c->vc_pos = c->vc_pos - oldorigin + c->vc_origin; + + up(&sisusb->lock); + + return 1; +} + +/* Interface routine */ +static int +sisusbcon_set_origin(struct vc_data *c) +{ + struct sisusb_usb_data *sisusb; + + /* Returning != 0 means we were successful. + * Returning 0 will vt make to use its own + * screenbuffer as the origin. + */ + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return 0; + + /* sisusb->lock is down */ + + if (sisusb_is_inactive(c, sisusb) || sisusb->con_blanked) { + up(&sisusb->lock); + return 0; + } + + c->vc_origin = c->vc_visible_origin = sisusb->scrbuf; + + sisusbcon_set_start_address(sisusb, c); + + sisusb->con_rolled_over = 0; + + up(&sisusb->lock); + + return 1; +} + +/* Interface routine */ +static int +sisusbcon_resize(struct vc_data *c, unsigned int newcols, unsigned int newrows) +{ + struct sisusb_usb_data *sisusb; + int fh; + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return -ENODEV; + + fh = sisusb->current_font_height; + + up(&sisusb->lock); + + /* We are quite unflexible as regards resizing. The vt code + * handles sizes where the line length isn't equal the pitch + * quite badly. As regards the rows, our panning tricks only + * work well if the number of rows equals the visible number + * of rows. + */ + + if (newcols != 80 || c->vc_scan_lines / fh != newrows) + return -EINVAL; + + return 0; +} + +int +sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot, + u8 *arg, int cmapsz, int ch512, int dorecalc, + struct vc_data *c, int fh, int uplock) +{ + int font_select = 0x00, i, err = 0; + u32 offset = 0; + u8 dummy; + + /* sisusb->lock is down */ + + /* + * The default font is kept in slot 0. + * A user font is loaded in slot 2 (256 ch) + * or 2+3 (512 ch). + */ + + if ((slot != 0 && slot != 2) || !fh) { + if (uplock) + up(&sisusb->lock); + return -EINVAL; + } + + if (set) + sisusb->font_slot = slot; + + /* Default font is always 256 */ + if (slot == 0) + ch512 = 0; + else + offset = 4 * cmapsz; + + font_select = (slot == 0) ? 0x00 : (ch512 ? 0x0e : 0x0a); + + err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x01); /* Reset */ + err |= sisusb_setidxreg(sisusb, SISSR, 0x02, 0x04); /* Write to plane 2 */ + err |= sisusb_setidxreg(sisusb, SISSR, 0x04, 0x07); /* Memory mode a0-bf */ + err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x03); /* Reset */ + + if (err) + goto font_op_error; + + err |= sisusb_setidxreg(sisusb, SISGR, 0x04, 0x03); /* Select plane read 2 */ + err |= sisusb_setidxreg(sisusb, SISGR, 0x05, 0x00); /* Disable odd/even */ + err |= sisusb_setidxreg(sisusb, SISGR, 0x06, 0x00); /* Address range a0-bf */ + + if (err) + goto font_op_error; + + if (arg) { + if (set) + for (i = 0; i < cmapsz; i++) { + err |= sisusb_writeb(sisusb, + sisusb->vrambase + offset + i, + arg[i]); + if (err) + break; + } + else + for (i = 0; i < cmapsz; i++) { + err |= sisusb_readb(sisusb, + sisusb->vrambase + offset + i, + &arg[i]); + if (err) + break; + } + + /* + * In 512-character mode, the character map is not contiguous if + * we want to remain EGA compatible -- which we do + */ + + if (ch512) { + if (set) + for (i = 0; i < cmapsz; i++) { + err |= sisusb_writeb(sisusb, + sisusb->vrambase + offset + + (2 * cmapsz) + i, + arg[cmapsz + i]); + if (err) + break; + } + else + for (i = 0; i < cmapsz; i++) { + err |= sisusb_readb(sisusb, + sisusb->vrambase + offset + + (2 * cmapsz) + i, + &arg[cmapsz + i]); + if (err) + break; + } + } + } + + if (err) + goto font_op_error; + + err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x01); /* Reset */ + err |= sisusb_setidxreg(sisusb, SISSR, 0x02, 0x03); /* Write to planes 0+1 */ + err |= sisusb_setidxreg(sisusb, SISSR, 0x04, 0x03); /* Memory mode a0-bf */ + if (set) + sisusb_setidxreg(sisusb, SISSR, 0x03, font_select); + err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x03); /* Reset end */ + + if (err) + goto font_op_error; + + err |= sisusb_setidxreg(sisusb, SISGR, 0x04, 0x00); /* Select plane read 0 */ + err |= sisusb_setidxreg(sisusb, SISGR, 0x05, 0x10); /* Enable odd/even */ + err |= sisusb_setidxreg(sisusb, SISGR, 0x06, 0x06); /* Address range b8-bf */ + + if (err) + goto font_op_error; + + if ((set) && (ch512 != sisusb->current_font_512)) { + + /* Font is shared among all our consoles. + * And so is the hi_font_mask. + */ + for (i = 0; i < MAX_NR_CONSOLES; i++) { + struct vc_data *c = vc_cons[i].d; + if (c && c->vc_sw == &sisusb_con) + c->vc_hi_font_mask = ch512 ? 0x0800 : 0; + } + + sisusb->current_font_512 = ch512; + + /* color plane enable register: + 256-char: enable intensity bit + 512-char: disable intensity bit */ + sisusb_getreg(sisusb, SISINPSTAT, &dummy); + sisusb_setreg(sisusb, SISAR, 0x12); + sisusb_setreg(sisusb, SISAR, ch512 ? 0x07 : 0x0f); + + sisusb_getreg(sisusb, SISINPSTAT, &dummy); + sisusb_setreg(sisusb, SISAR, 0x20); + sisusb_getreg(sisusb, SISINPSTAT, &dummy); + } + + if (dorecalc) { + + /* + * Adjust the screen to fit a font of a certain height + */ + + unsigned char ovr, vde, fsr; + int rows = 0, maxscan = 0; + + if (c) { + + /* Number of video rows */ + rows = c->vc_scan_lines / fh; + /* Scan lines to actually display-1 */ + maxscan = rows * fh - 1; + + /*printk(KERN_DEBUG "sisusb recalc rows %d maxscan %d fh %d sl %d\n", + rows, maxscan, fh, c->vc_scan_lines);*/ + + sisusb_getidxreg(sisusb, SISCR, 0x07, &ovr); + vde = maxscan & 0xff; + ovr = (ovr & 0xbd) | + ((maxscan & 0x100) >> 7) | + ((maxscan & 0x200) >> 3); + sisusb_setidxreg(sisusb, SISCR, 0x07, ovr); + sisusb_setidxreg(sisusb, SISCR, 0x12, vde); + + } + + sisusb_getidxreg(sisusb, SISCR, 0x09, &fsr); + fsr = (fsr & 0xe0) | (fh - 1); + sisusb_setidxreg(sisusb, SISCR, 0x09, fsr); + sisusb->current_font_height = fh; + + sisusb->sisusb_cursor_size_from = -1; + sisusb->sisusb_cursor_size_to = -1; + + } + + if (uplock) + up(&sisusb->lock); + + if (dorecalc && c) { + int i, rows = c->vc_scan_lines / fh; + + /* Now adjust our consoles' size */ + + for (i = 0; i < MAX_NR_CONSOLES; i++) { + struct vc_data *vc = vc_cons[i].d; + + if (vc && vc->vc_sw == &sisusb_con) { + if (CON_IS_VISIBLE(vc)) { + vc->vc_sw->con_cursor(vc, CM_DRAW); + } + vc->vc_font.height = fh; + vc_resize(vc, 0, rows); + } + } + } + + return 0; + +font_op_error: + if (uplock) + up(&sisusb->lock); + + return -EIO; +} + +/* Interface routine */ +static int +sisusbcon_font_set(struct vc_data *c, struct console_font *font, + unsigned flags) +{ + struct sisusb_usb_data *sisusb; + unsigned charcount = font->charcount; + + if (font->width != 8 || (charcount != 256 && charcount != 512)) + return -EINVAL; + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return -ENODEV; + + /* sisusb->lock is down */ + + /* Save the user-provided font into a buffer. This + * is used for restoring text mode after quitting + * from X and for the con_getfont routine. + */ + if (sisusb->font_backup) { + if (sisusb->font_backup_size < charcount) { + vfree(sisusb->font_backup); + sisusb->font_backup = NULL; + } + } + + if (!sisusb->font_backup) + sisusb->font_backup = vmalloc(charcount * 32); + + if (sisusb->font_backup) { + memcpy(sisusb->font_backup, font->data, charcount * 32); + sisusb->font_backup_size = charcount; + sisusb->font_backup_height = font->height; + sisusb->font_backup_512 = (charcount == 512) ? 1 : 0; + } + + /* do_font_op ups sisusb->lock */ + + return sisusbcon_do_font_op(sisusb, 1, 2, font->data, + 8192, (charcount == 512), + (!(flags & KD_FONT_FLAG_DONT_RECALC)) ? 1 : 0, + c, font->height, 1); +} + +/* Interface routine */ +static int +sisusbcon_font_get(struct vc_data *c, struct console_font *font) +{ + struct sisusb_usb_data *sisusb; + + if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + return -ENODEV; + + /* sisusb->lock is down */ + + font->width = 8; + font->height = c->vc_font.height; + font->charcount = 256; + + if (!font->data) { + up(&sisusb->lock); + return 0; + } + + if (!sisusb->font_backup) { + up(&sisusb->lock); + return -ENODEV; + } + + /* Copy 256 chars only, like vgacon */ + memcpy(font->data, sisusb->font_backup, 256 * 32); + + up(&sisusb->lock); + + return 0; +} + +/* + * The console `switch' structure for the sisusb console + */ + +static const struct consw sisusb_con = { + .owner = THIS_MODULE, + .con_startup = sisusbcon_startup, + .con_init = sisusbcon_init, + .con_deinit = sisusbcon_deinit, + .con_clear = sisusbcon_clear, + .con_putc = sisusbcon_putc, + .con_putcs = sisusbcon_putcs, + .con_cursor = sisusbcon_cursor, + .con_scroll = sisusbcon_scroll, + .con_bmove = sisusbcon_bmove, + .con_switch = sisusbcon_switch, + .con_blank = sisusbcon_blank, + .con_font_set = sisusbcon_font_set, + .con_font_get = sisusbcon_font_get, + .con_set_palette = sisusbcon_set_palette, + .con_scrolldelta = sisusbcon_scrolldelta, + .con_build_attr = sisusbcon_build_attr, + .con_invert_region = sisusbcon_invert_region, + .con_set_origin = sisusbcon_set_origin, + .con_save_screen = sisusbcon_save_screen, + .con_resize = sisusbcon_resize, +}; + +/* Our very own dummy console driver */ + +static const char *sisusbdummycon_startup(void) +{ + return "SISUSBVGADUMMY"; +} + +static void sisusbdummycon_init(struct vc_data *vc, int init) +{ + vc->vc_can_do_color = 1; + if (init) { + vc->vc_cols = 80; + vc->vc_rows = 25; + } else + vc_resize(vc, 80, 25); +} + +static int sisusbdummycon_dummy(void) +{ + return 0; +} + +#define SISUSBCONDUMMY (void *)sisusbdummycon_dummy + +const struct consw sisusb_dummy_con = { + .owner = THIS_MODULE, + .con_startup = sisusbdummycon_startup, + .con_init = sisusbdummycon_init, + .con_deinit = SISUSBCONDUMMY, + .con_clear = SISUSBCONDUMMY, + .con_putc = SISUSBCONDUMMY, + .con_putcs = SISUSBCONDUMMY, + .con_cursor = SISUSBCONDUMMY, + .con_scroll = SISUSBCONDUMMY, + .con_bmove = SISUSBCONDUMMY, + .con_switch = SISUSBCONDUMMY, + .con_blank = SISUSBCONDUMMY, + .con_font_set = SISUSBCONDUMMY, + .con_font_get = SISUSBCONDUMMY, + .con_font_default = SISUSBCONDUMMY, + .con_font_copy = SISUSBCONDUMMY, + .con_set_palette = SISUSBCONDUMMY, + .con_scrolldelta = SISUSBCONDUMMY, +}; + +int +sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last) +{ + int i, ret, minor = sisusb->minor; + + down(&disconnect_sem); + + down(&sisusb->lock); + + /* Erm.. that should not happen */ + if (sisusb->haveconsole || !sisusb->SiS_Pr) { + up(&sisusb->lock); + up(&disconnect_sem); + return 1; + } + + sisusb->con_first = first; + sisusb->con_last = last; + + if (first > last || + first > MAX_NR_CONSOLES || + last > MAX_NR_CONSOLES) { + up(&sisusb->lock); + up(&disconnect_sem); + return 1; + } + + /* If gfxcore not initialized or no consoles given, quit graciously */ + if (!sisusb->gfxinit || first < 1 || last < 1) { + up(&sisusb->lock); + up(&disconnect_sem); + return 0; + } + + sisusb->sisusb_cursor_loc = -1; + sisusb->sisusb_cursor_size_from = -1; + sisusb->sisusb_cursor_size_to = -1; + + /* Set up text mode (and upload default font) */ + if (sisusb_reset_text_mode(sisusb, 1)) { + up(&sisusb->lock); + up(&disconnect_sem); + printk(KERN_ERR + "sisusbvga[%d]: Failed to set up text mode\n", + minor); + return 1; + } + + /* Initialize some gfx registers */ + sisusb_initialize(sisusb); + + for (i = first - 1; i <= last - 1; i++) { + /* Save sisusb for our interface routines */ + mysisusbs[i] = sisusb; + } + + /* Initial console setup */ + sisusb->sisusb_num_columns = 80; + + /* Use a 32K buffer (matches b8000-bffff area) */ + sisusb->scrbuf_size = 32 * 1024; + + /* Allocate screen buffer */ + if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) { + up(&sisusb->lock); + up(&disconnect_sem); + printk(KERN_ERR + "sisusbvga[%d]: Failed to allocate screen buffer\n", + minor); + return 1; + } + + up(&sisusb->lock); + up(&disconnect_sem); + + /* Now grab the desired console(s) */ + ret = take_over_console(&sisusb_con, first - 1, last - 1, 0); + + if (!ret) + sisusb->haveconsole = 1; + else { + for (i = first - 1; i <= last - 1; i++) + mysisusbs[i] = NULL; + } + + return ret; +} + +void +sisusb_console_exit(struct sisusb_usb_data *sisusb) +{ + int i; + + /* This is called if the device is disconnected + * and while disconnect and lock semaphores + * are up. This should be save because we + * can't lose our sisusb any other way but by + * disconnection (and hence, the disconnect + * sema is for protecting all other access + * functions from disconnection, not the + * other way round). + */ + + /* Now what do we do in case of disconnection: + * One alternative would be to simply call + * give_up_console(). Nah, not a good idea. + * give_up_console() is obviously buggy as it + * only discards the consw pointer from the + * driver_map, but doesn't adapt vc->vc_sw + * of the affected consoles. Hence, the next + * call to any of the console functions will + * eventually take a trip to oops county. + * Also, give_up_console for some reason + * doesn't decrement our module refcount. + * Instead, we switch our consoles to a private + * dummy console. This, of course, keeps our + * refcount up as well, but it works perfectly. + */ + + if (sisusb->haveconsole) { + for (i = 0; i < MAX_NR_CONSOLES; i++) + if (sisusb->havethisconsole[i]) + take_over_console(&sisusb_dummy_con, i, i, 0); + /* At this point, con_deinit for all our + * consoles is executed by take_over_console(). + */ + sisusb->haveconsole = 0; + } + + vfree((void *)sisusb->scrbuf); + sisusb->scrbuf = 0; + + vfree(sisusb->font_backup); + sisusb->font_backup = NULL; +} + +void __init sisusb_init_concode(void) +{ + int i; + + for (i = 0; i < MAX_NR_CONSOLES; i++) + mysisusbs[i] = NULL; +} + +#endif /* INCL_CON */ + + + diff --git a/drivers/usb/misc/sisusbvga/sisusb_init.c b/drivers/usb/misc/sisusbvga/sisusb_init.c new file mode 100644 index 00000000000..f28bc240f9b --- /dev/null +++ b/drivers/usb/misc/sisusbvga/sisusb_init.c @@ -0,0 +1,1047 @@ +/* + * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles + * + * Display mode initializing code + * + * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria + * + * If distributed as part of the Linux kernel, this code is licensed under the + * terms of the GPL v2. + * + * Otherwise, the following license terms apply: + * + * * Redistribution and use in source and binary forms, with or without + * * modification, are permitted provided that the following conditions + * * are met: + * * 1) Redistributions of source code must retain the above copyright + * * notice, this list of conditions and the following disclaimer. + * * 2) Redistributions in binary form must reproduce the above copyright + * * notice, this list of conditions and the following disclaimer in the + * * documentation and/or other materials provided with the distribution. + * * 3) The name of the author may not be used to endorse or promote products + * * derived from this software without specific prior written permission. + * * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Thomas Winischhofer <thomas@winischhofer.net> + * + */ + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/kref.h> + +#include "sisusb.h" + +#ifdef INCL_SISUSB_CON + +#include "sisusb_init.h" + +/*********************************************/ +/* POINTER INITIALIZATION */ +/*********************************************/ + +static void +SiSUSB_InitPtr(struct SiS_Private *SiS_Pr) +{ + SiS_Pr->SiS_ModeResInfo = SiSUSB_ModeResInfo; + SiS_Pr->SiS_StandTable = SiSUSB_StandTable; + + SiS_Pr->SiS_SModeIDTable = SiSUSB_SModeIDTable; + SiS_Pr->SiS_EModeIDTable = SiSUSB_EModeIDTable; + SiS_Pr->SiS_RefIndex = SiSUSB_RefIndex; + SiS_Pr->SiS_CRT1Table = SiSUSB_CRT1Table; + + SiS_Pr->SiS_VCLKData = SiSUSB_VCLKData; +} + +/*********************************************/ +/* HELPER: Get ModeID */ +/*********************************************/ + +unsigned short +SiSUSB_GetModeID(int HDisplay, int VDisplay, int Depth) +{ + unsigned short ModeIndex = 0; + + switch (HDisplay) + { + case 320: + if (VDisplay == 200) + ModeIndex = ModeIndex_320x200[Depth]; + else if (VDisplay == 240) + ModeIndex = ModeIndex_320x240[Depth]; + break; + case 400: + if (VDisplay == 300) + ModeIndex = ModeIndex_400x300[Depth]; + break; + case 512: + if (VDisplay == 384) + ModeIndex = ModeIndex_512x384[Depth]; + break; + case 640: + if (VDisplay == 480) + ModeIndex = ModeIndex_640x480[Depth]; + else if (VDisplay == 400) + ModeIndex = ModeIndex_640x400[Depth]; + break; + case 720: + if (VDisplay == 480) + ModeIndex = ModeIndex_720x480[Depth]; + else if (VDisplay == 576) + ModeIndex = ModeIndex_720x576[Depth]; + break; + case 768: + if (VDisplay == 576) + ModeIndex = ModeIndex_768x576[Depth]; + break; + case 800: + if (VDisplay == 600) + ModeIndex = ModeIndex_800x600[Depth]; + else if (VDisplay == 480) + ModeIndex = ModeIndex_800x480[Depth]; + break; + case 848: + if (VDisplay == 480) + ModeIndex = ModeIndex_848x480[Depth]; + break; + case 856: + if (VDisplay == 480) + ModeIndex = ModeIndex_856x480[Depth]; + break; + case 960: + if (VDisplay == 540) + ModeIndex = ModeIndex_960x540[Depth]; + else if (VDisplay == 600) + ModeIndex = ModeIndex_960x600[Depth]; + break; + case 1024: + if (VDisplay == 576) + ModeIndex = ModeIndex_1024x576[Depth]; + else if (VDisplay == 768) + ModeIndex = ModeIndex_1024x768[Depth]; + break; + case 1152: + if (VDisplay == 864) + ModeIndex = ModeIndex_1152x864[Depth]; + break; + case 1280: + switch (VDisplay) { + case 720: + ModeIndex = ModeIndex_1280x720[Depth]; + break; + case 768: + ModeIndex = ModeIndex_1280x768[Depth]; + break; + case 1024: + ModeIndex = ModeIndex_1280x1024[Depth]; + break; + } + } + + return ModeIndex; +} + +/*********************************************/ +/* HELPER: SetReg, GetReg */ +/*********************************************/ + +static void +SiS_SetReg(struct SiS_Private *SiS_Pr, unsigned long port, + unsigned short index, unsigned short data) +{ + sisusb_setidxreg(SiS_Pr->sisusb, port, index, data); +} + +static void +SiS_SetRegByte(struct SiS_Private *SiS_Pr, unsigned long port, + unsigned short data) +{ + sisusb_setreg(SiS_Pr->sisusb, port, data); +} + +static unsigned char +SiS_GetReg(struct SiS_Private *SiS_Pr, unsigned long port, + unsigned short index) +{ + u8 data; + + sisusb_getidxreg(SiS_Pr->sisusb, port, index, &data); + + return data; +} + +static unsigned char +SiS_GetRegByte(struct SiS_Private *SiS_Pr, unsigned long port) +{ + u8 data; + + sisusb_getreg(SiS_Pr->sisusb, port, &data); + + return data; +} + +static void +SiS_SetRegANDOR(struct SiS_Private *SiS_Pr, unsigned long port, + unsigned short index, unsigned short DataAND, + unsigned short DataOR) +{ + sisusb_setidxregandor(SiS_Pr->sisusb, port, index, DataAND, DataOR); +} + +static void +SiS_SetRegAND(struct SiS_Private *SiS_Pr, unsigned long port, + unsigned short index, unsigned short DataAND) +{ + sisusb_setidxregand(SiS_Pr->sisusb, port, index, DataAND); +} + +static void +SiS_SetRegOR(struct SiS_Private *SiS_Pr,unsigned long port, + unsigned short index, unsigned short DataOR) +{ + sisusb_setidxregor(SiS_Pr->sisusb, port, index, DataOR); +} + +/*********************************************/ +/* HELPER: DisplayOn, DisplayOff */ +/*********************************************/ + +static void +SiS_DisplayOn(struct SiS_Private *SiS_Pr) +{ + SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x01, 0xDF); +} + +/*********************************************/ +/* HELPER: Init Port Addresses */ +/*********************************************/ + +void +SiSUSBRegInit(struct SiS_Private *SiS_Pr, unsigned long BaseAddr) +{ + SiS_Pr->SiS_P3c4 = BaseAddr + 0x14; + SiS_Pr->SiS_P3d4 = BaseAddr + 0x24; + SiS_Pr->SiS_P3c0 = BaseAddr + 0x10; + SiS_Pr->SiS_P3ce = BaseAddr + 0x1e; + SiS_Pr->SiS_P3c2 = BaseAddr + 0x12; + SiS_Pr->SiS_P3ca = BaseAddr + 0x1a; + SiS_Pr->SiS_P3c6 = BaseAddr + 0x16; + SiS_Pr->SiS_P3c7 = BaseAddr + 0x17; + SiS_Pr->SiS_P3c8 = BaseAddr + 0x18; + SiS_Pr->SiS_P3c9 = BaseAddr + 0x19; + SiS_Pr->SiS_P3cb = BaseAddr + 0x1b; + SiS_Pr->SiS_P3cc = BaseAddr + 0x1c; + SiS_Pr->SiS_P3cd = BaseAddr + 0x1d; + SiS_Pr->SiS_P3da = BaseAddr + 0x2a; + SiS_Pr->SiS_Part1Port = BaseAddr + SIS_CRT2_PORT_04; +} + +/*********************************************/ +/* HELPER: GetSysFlags */ +/*********************************************/ + +static void +SiS_GetSysFlags(struct SiS_Private *SiS_Pr) +{ + SiS_Pr->SiS_MyCR63 = 0x63; +} + +/*********************************************/ +/* HELPER: Init PCI & Engines */ +/*********************************************/ + +static void +SiSInitPCIetc(struct SiS_Private *SiS_Pr) +{ + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x20, 0xa1); + /* - Enable 2D (0x40) + * - Enable 3D (0x02) + * - Enable 3D vertex command fetch (0x10) + * - Enable 3D command parser (0x08) + * - Enable 3D G/L transformation engine (0x80) + */ + SiS_SetRegOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x1E, 0xDA); +} + +/*********************************************/ +/* HELPER: SET SEGMENT REGISTERS */ +/*********************************************/ + +static void +SiS_SetSegRegLower(struct SiS_Private *SiS_Pr, unsigned short value) +{ + unsigned short temp; + + value &= 0x00ff; + temp = SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3cb) & 0xf0; + temp |= (value >> 4); + SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cb, temp); + temp = SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd) & 0xf0; + temp |= (value & 0x0f); + SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd, temp); +} + +static void +SiS_SetSegRegUpper(struct SiS_Private *SiS_Pr, unsigned short value) +{ + unsigned short temp; + + value &= 0x00ff; + temp = SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3cb) & 0x0f; + temp |= (value & 0xf0); + SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cb, temp); + temp = SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd) & 0x0f; + temp |= (value << 4); + SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd, temp); +} + +static void +SiS_SetSegmentReg(struct SiS_Private *SiS_Pr, unsigned short value) +{ + SiS_SetSegRegLower(SiS_Pr, value); + SiS_SetSegRegUpper(SiS_Pr, value); +} + +static void +SiS_ResetSegmentReg(struct SiS_Private *SiS_Pr) +{ + SiS_SetSegmentReg(SiS_Pr, 0); +} + +static void +SiS_SetSegmentRegOver(struct SiS_Private *SiS_Pr, unsigned short value) +{ + unsigned short temp = value >> 8; + + temp &= 0x07; + temp |= (temp << 4); + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x1d, temp); + SiS_SetSegmentReg(SiS_Pr, value); +} + +static void +SiS_ResetSegmentRegOver(struct SiS_Private *SiS_Pr) +{ + SiS_SetSegmentRegOver(SiS_Pr, 0); +} + +static void +SiS_ResetSegmentRegisters(struct SiS_Private *SiS_Pr) +{ + SiS_ResetSegmentReg(SiS_Pr); + SiS_ResetSegmentRegOver(SiS_Pr); +} + +/*********************************************/ +/* HELPER: SearchModeID */ +/*********************************************/ + +static int +SiS_SearchModeID(struct SiS_Private *SiS_Pr, unsigned short *ModeNo, + unsigned short *ModeIdIndex) +{ + if ((*ModeNo) <= 0x13) { + + if ((*ModeNo) != 0x03) + return 0; + + (*ModeIdIndex) = 0; + + } else { + + for(*ModeIdIndex = 0; ;(*ModeIdIndex)++) { + + if (SiS_Pr->SiS_EModeIDTable[*ModeIdIndex].Ext_ModeID == (*ModeNo)) + break; + + if (SiS_Pr->SiS_EModeIDTable[*ModeIdIndex].Ext_ModeID == 0xFF) + return 0; + } + + } + + return 1; +} + +/*********************************************/ +/* HELPER: ENABLE CRT1 */ +/*********************************************/ + +static void +SiS_HandleCRT1(struct SiS_Private *SiS_Pr) +{ + /* Enable CRT1 gating */ + SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3d4, SiS_Pr->SiS_MyCR63, 0xbf); +} + +/*********************************************/ +/* HELPER: GetColorDepth */ +/*********************************************/ + +static unsigned short +SiS_GetColorDepth(struct SiS_Private *SiS_Pr, unsigned short ModeNo, + unsigned short ModeIdIndex) +{ + static const unsigned short ColorDepth[6] = { 1, 2, 4, 4, 6, 8}; + unsigned short modeflag; + short index; + + if (ModeNo <= 0x13) { + modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; + } else { + modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; + } + + index = (modeflag & ModeTypeMask) - ModeEGA; + if (index < 0) index = 0; + return ColorDepth[index]; +} + +/*********************************************/ +/* HELPER: GetOffset */ +/*********************************************/ + +static unsigned short +SiS_GetOffset(struct SiS_Private *SiS_Pr, unsigned short ModeNo, + unsigned short ModeIdIndex, unsigned short rrti) +{ + unsigned short xres, temp, colordepth, infoflag; + + infoflag = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag; + xres = SiS_Pr->SiS_RefIndex[rrti].XRes; + + colordepth = SiS_GetColorDepth(SiS_Pr, ModeNo, ModeIdIndex); + + temp = xres / 16; + + if (infoflag & InterlaceMode) + temp <<= 1; + + temp *= colordepth; + + if (xres % 16) + temp += (colordepth >> 1); + + return temp; +} + +/*********************************************/ +/* SEQ */ +/*********************************************/ + +static void +SiS_SetSeqRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex) +{ + unsigned char SRdata; + int i; + + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x00, 0x03); + + SRdata = SiS_Pr->SiS_StandTable[StandTableIndex].SR[0] | 0x20; + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x01, SRdata); + + for(i = 2; i <= 4; i++) { + SRdata = SiS_Pr->SiS_StandTable[StandTableIndex].SR[i-1]; + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, i, SRdata); + } +} + +/*********************************************/ +/* MISC */ +/*********************************************/ + +static void +SiS_SetMiscRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex) +{ + unsigned char Miscdata = SiS_Pr->SiS_StandTable[StandTableIndex].MISC; + + SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c2, Miscdata); +} + +/*********************************************/ +/* CRTC */ +/*********************************************/ + +static void +SiS_SetCRTCRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex) +{ + unsigned char CRTCdata; + unsigned short i; + + SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3d4, 0x11, 0x7f); + + for(i = 0; i <= 0x18; i++) { + CRTCdata = SiS_Pr->SiS_StandTable[StandTableIndex].CRTC[i]; + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, i, CRTCdata); + } +} + +/*********************************************/ +/* ATT */ +/*********************************************/ + +static void +SiS_SetATTRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex) +{ + unsigned char ARdata; + unsigned short i; + + for(i = 0; i <= 0x13; i++) { + ARdata = SiS_Pr->SiS_StandTable[StandTableIndex].ATTR[i]; + SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3da); + SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, i); + SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, ARdata); + } + SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3da); + SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, 0x14); + SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, 0x00); + + SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3da); + SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, 0x20); + SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3da); +} + +/*********************************************/ +/* GRC */ +/*********************************************/ + +static void +SiS_SetGRCRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex) +{ + unsigned char GRdata; + unsigned short i; + + for(i = 0; i <= 0x08; i++) { + GRdata = SiS_Pr->SiS_StandTable[StandTableIndex].GRC[i]; + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3ce, i, GRdata); + } + + if (SiS_Pr->SiS_ModeType > ModeVGA) { + /* 256 color disable */ + SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3ce, 0x05, 0xBF); + } +} + +/*********************************************/ +/* CLEAR EXTENDED REGISTERS */ +/*********************************************/ + +static void +SiS_ClearExt1Regs(struct SiS_Private *SiS_Pr, unsigned short ModeNo) +{ + int i; + + for(i = 0x0A; i <= 0x0E; i++) { + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, i, 0x00); + } + + SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x37, 0xFE); +} + +/*********************************************/ +/* Get rate index */ +/*********************************************/ + +static unsigned short +SiS_GetRatePtr(struct SiS_Private *SiS_Pr, unsigned short ModeNo, + unsigned short ModeIdIndex) +{ + unsigned short rrti, i, index, temp; + + if (ModeNo <= 0x13) + return 0xFFFF; + + index = SiS_GetReg(SiS_Pr,SiS_Pr->SiS_P3d4, 0x33) & 0x0F; + if (index > 0) index--; + + rrti = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].REFindex; + ModeNo = SiS_Pr->SiS_RefIndex[rrti].ModeID; + + i = 0; + do { + if (SiS_Pr->SiS_RefIndex[rrti + i].ModeID != ModeNo) + break; + + temp = SiS_Pr->SiS_RefIndex[rrti + i].Ext_InfoFlag & ModeTypeMask; + if (temp < SiS_Pr->SiS_ModeType) + break; + + i++; + index--; + } while(index != 0xFFFF); + + i--; + + return (rrti + i); +} + +/*********************************************/ +/* SYNC */ +/*********************************************/ + +static void +SiS_SetCRT1Sync(struct SiS_Private *SiS_Pr, unsigned short rrti) +{ + unsigned short sync = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag >> 8; + sync &= 0xC0; + sync |= 0x2f; + SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c2, sync); +} + +/*********************************************/ +/* CRTC/2 */ +/*********************************************/ + +static void +SiS_SetCRT1CRTC(struct SiS_Private *SiS_Pr, unsigned short ModeNo, + unsigned short ModeIdIndex, unsigned short rrti) +{ + unsigned char index; + unsigned short temp, i, j, modeflag; + + SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3d4,0x11,0x7f); + + modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; + + index = SiS_Pr->SiS_RefIndex[rrti].Ext_CRT1CRTC; + + for(i = 0,j = 0; i <= 7; i++, j++) { + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, j, + SiS_Pr->SiS_CRT1Table[index].CR[i]); + } + for(j = 0x10; i <= 10; i++, j++) { + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, j, + SiS_Pr->SiS_CRT1Table[index].CR[i]); + } + for(j = 0x15; i <= 12; i++, j++) { + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, j, + SiS_Pr->SiS_CRT1Table[index].CR[i]); + } + for(j = 0x0A; i <= 15; i++, j++) { + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, j, + SiS_Pr->SiS_CRT1Table[index].CR[i]); + } + + temp = SiS_Pr->SiS_CRT1Table[index].CR[16] & 0xE0; + SiS_SetReg(SiS_Pr,SiS_Pr->SiS_P3c4, 0x0E, temp); + + temp = ((SiS_Pr->SiS_CRT1Table[index].CR[16]) & 0x01) << 5; + if (modeflag & DoubleScanMode) temp |= 0x80; + SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3d4, 0x09, 0x5F, temp); + + if (SiS_Pr->SiS_ModeType > ModeVGA) + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x14, 0x4F); +} + +/*********************************************/ +/* OFFSET & PITCH */ +/*********************************************/ +/* (partly overruled by SetPitch() in XF86) */ +/*********************************************/ + +static void +SiS_SetCRT1Offset(struct SiS_Private *SiS_Pr, unsigned short ModeNo, + unsigned short ModeIdIndex, unsigned short rrti) +{ + unsigned short du = SiS_GetOffset(SiS_Pr, ModeNo, ModeIdIndex, rrti); + unsigned short infoflag = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag; + unsigned short temp; + + temp = (du >> 8) & 0x0f; + SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0E, 0xF0, temp); + + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x13, (du & 0xFF)); + + if (infoflag & InterlaceMode) du >>= 1; + + du <<= 5; + temp = (du >> 8) & 0xff; + if (du & 0xff) temp++; + temp++; + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x10, temp); +} + +/*********************************************/ +/* VCLK */ +/*********************************************/ + +static void +SiS_SetCRT1VCLK(struct SiS_Private *SiS_Pr, unsigned short ModeNo, + unsigned short rrti) +{ + unsigned short index = SiS_Pr->SiS_RefIndex[rrti].Ext_CRTVCLK; + unsigned short clka = SiS_Pr->SiS_VCLKData[index].SR2B; + unsigned short clkb = SiS_Pr->SiS_VCLKData[index].SR2C; + + SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4,0x31,0xCF); + + SiS_SetReg(SiS_Pr,SiS_Pr->SiS_P3c4,0x2B,clka); + SiS_SetReg(SiS_Pr,SiS_Pr->SiS_P3c4,0x2C,clkb); + SiS_SetReg(SiS_Pr,SiS_Pr->SiS_P3c4,0x2D,0x01); +} + +/*********************************************/ +/* FIFO */ +/*********************************************/ + +static void +SiS_SetCRT1FIFO_310(struct SiS_Private *SiS_Pr, unsigned short ModeNo, + unsigned short mi) +{ + unsigned short modeflag = SiS_Pr->SiS_EModeIDTable[mi].Ext_ModeFlag; + + /* disable auto-threshold */ + SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x3D, 0xFE); + + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x08, 0xAE); + SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x09, 0xF0); + + if (ModeNo <= 0x13) + return; + + if ((!(modeflag & DoubleScanMode)) || (!(modeflag & HalfDCLK))) { + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x08, 0x34); + SiS_SetRegOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x3D, 0x01); + } +} + +/*********************************************/ +/* MODE REGISTERS */ +/*********************************************/ + +static void +SiS_SetVCLKState(struct SiS_Private *SiS_Pr, unsigned short ModeNo, + unsigned short rrti) +{ + unsigned short data = 0, VCLK = 0, index = 0; + + if (ModeNo > 0x13) { + index = SiS_Pr->SiS_RefIndex[rrti].Ext_CRTVCLK; + VCLK = SiS_Pr->SiS_VCLKData[index].CLOCK; + } + + if (VCLK >= 166) data |= 0x0c; + SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x32, 0xf3, data); + + if (VCLK >= 166) + SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x1f, 0xe7); + + /* DAC speed */ + data = 0x03; + if (VCLK >= 260) + data = 0x00; + else if (VCLK >= 160) + data = 0x01; + else if (VCLK >= 135) + data = 0x02; + + SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x07, 0xF8, data); +} + +static void +SiS_SetCRT1ModeRegs(struct SiS_Private *SiS_Pr, unsigned short ModeNo, + unsigned short ModeIdIndex, unsigned short rrti) +{ + unsigned short data, infoflag = 0, modeflag; + + if (ModeNo <= 0x13) + modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; + else { + modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; + infoflag = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag; + } + + /* Disable DPMS */ + SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x1F, 0x3F); + + data = 0; + if (ModeNo > 0x13) { + if (SiS_Pr->SiS_ModeType > ModeEGA) { + data |= 0x02; + data |= ((SiS_Pr->SiS_ModeType - ModeVGA) << 2); + } + if (infoflag & InterlaceMode) data |= 0x20; + } + SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x06, 0xC0, data); + + data = 0; + if (infoflag & InterlaceMode) { + /* data = (Hsync / 8) - ((Htotal / 8) / 2) + 3 */ + unsigned short hrs = (SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x04) | + ((SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0b) & 0xc0) << 2)) - 3; + unsigned short hto = (SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x00) | + ((SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0b) & 0x03) << 8)) + 5; + data = hrs - (hto >> 1) + 3; + } + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x19, (data & 0xFF)); + SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3d4, 0x1a, 0xFC, (data >> 8)); + + if (modeflag & HalfDCLK) + SiS_SetRegOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x01, 0x08); + + data = 0; + if (modeflag & LineCompareOff) + data = 0x08; + SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0F, 0xB7, data); + + if ((SiS_Pr->SiS_ModeType == ModeEGA) && (ModeNo > 0x13)) + SiS_SetRegOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0F, 0x40); + + SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x31, 0xfb); + + data = 0x60; + if (SiS_Pr->SiS_ModeType != ModeText) { + data ^= 0x60; + if (SiS_Pr->SiS_ModeType != ModeEGA) + data ^= 0xA0; + } + SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x21, 0x1F, data); + + SiS_SetVCLKState(SiS_Pr, ModeNo, rrti); + + if (SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x31) & 0x40) + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x52, 0x2c); + else + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x52, 0x6c); +} + +/*********************************************/ +/* LOAD DAC */ +/*********************************************/ + +static void +SiS_WriteDAC(struct SiS_Private *SiS_Pr, unsigned long DACData, + unsigned short shiftflag, unsigned short dl, unsigned short ah, + unsigned short al, unsigned short dh) +{ + unsigned short d1, d2, d3; + + switch (dl) { + case 0: + d1 = dh; d2 = ah; d3 = al; + break; + case 1: + d1 = ah; d2 = al; d3 = dh; + break; + default: + d1 = al; d2 = dh; d3 = ah; + } + SiS_SetRegByte(SiS_Pr, DACData, (d1 << shiftflag)); + SiS_SetRegByte(SiS_Pr, DACData, (d2 << shiftflag)); + SiS_SetRegByte(SiS_Pr, DACData, (d3 << shiftflag)); +} + +static void +SiS_LoadDAC(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short mi) +{ + unsigned short data, data2, time, i, j, k, m, n, o; + unsigned short si, di, bx, sf; + unsigned long DACAddr, DACData; + const unsigned char *table = NULL; + + if (ModeNo < 0x13) + data = SiS_Pr->SiS_SModeIDTable[mi].St_ModeFlag; + else + data = SiS_Pr->SiS_EModeIDTable[mi].Ext_ModeFlag; + + data &= DACInfoFlag; + + j = time = 64; + if (data == 0x00) + table = SiS_MDA_DAC; + else if (data == 0x08) + table = SiS_CGA_DAC; + else if (data == 0x10) + table = SiS_EGA_DAC; + else { + j = 16; + time = 256; + table = SiS_VGA_DAC; + } + + DACAddr = SiS_Pr->SiS_P3c8; + DACData = SiS_Pr->SiS_P3c9; + sf = 0; + SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c6, 0xFF); + + SiS_SetRegByte(SiS_Pr, DACAddr, 0x00); + + for(i = 0; i < j; i++) { + data = table[i]; + for(k = 0; k < 3; k++) { + data2 = 0; + if (data & 0x01) data2 += 0x2A; + if (data & 0x02) data2 += 0x15; + SiS_SetRegByte(SiS_Pr, DACData, (data2 << sf)); + data >>= 2; + } + } + + if (time == 256) { + for(i = 16; i < 32; i++) { + data = table[i] << sf; + for(k = 0; k < 3; k++) + SiS_SetRegByte(SiS_Pr, DACData, data); + } + si = 32; + for(m = 0; m < 9; m++) { + di = si; + bx = si + 4; + for(n = 0; n < 3; n++) { + for(o = 0; o < 5; o++) { + SiS_WriteDAC(SiS_Pr, DACData, sf, n, + table[di], table[bx], table[si]); + si++; + } + si -= 2; + for(o = 0; o < 3; o++) { + SiS_WriteDAC(SiS_Pr, DACData, sf, n, + table[di], table[si], table[bx]); + si--; + } + } + si += 5; + } + } +} + +/*********************************************/ +/* SET CRT1 REGISTER GROUP */ +/*********************************************/ + +static void +SiS_SetCRT1Group(struct SiS_Private *SiS_Pr, unsigned short ModeNo, + unsigned short ModeIdIndex) +{ + unsigned short StandTableIndex, rrti; + + SiS_Pr->SiS_CRT1Mode = ModeNo; + + if (ModeNo <= 0x13) + StandTableIndex = 0; + else + StandTableIndex = 1; + + SiS_ResetSegmentRegisters(SiS_Pr); + SiS_SetSeqRegs(SiS_Pr, StandTableIndex); + SiS_SetMiscRegs(SiS_Pr, StandTableIndex); + SiS_SetCRTCRegs(SiS_Pr, StandTableIndex); + SiS_SetATTRegs(SiS_Pr, StandTableIndex); + SiS_SetGRCRegs(SiS_Pr, StandTableIndex); + SiS_ClearExt1Regs(SiS_Pr, ModeNo); + + rrti = SiS_GetRatePtr(SiS_Pr, ModeNo, ModeIdIndex); + + if (rrti != 0xFFFF) { + SiS_SetCRT1Sync(SiS_Pr, rrti); + SiS_SetCRT1CRTC(SiS_Pr, ModeNo, ModeIdIndex, rrti); + SiS_SetCRT1Offset(SiS_Pr, ModeNo, ModeIdIndex, rrti); + SiS_SetCRT1VCLK(SiS_Pr, ModeNo, rrti); + } + + SiS_SetCRT1FIFO_310(SiS_Pr, ModeNo, ModeIdIndex); + + SiS_SetCRT1ModeRegs(SiS_Pr, ModeNo, ModeIdIndex, rrti); + + SiS_LoadDAC(SiS_Pr, ModeNo, ModeIdIndex); + + SiS_DisplayOn(SiS_Pr); +} + +/*********************************************/ +/* SiSSetMode() */ +/*********************************************/ + +int +SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo) +{ + unsigned short ModeIdIndex; + unsigned long BaseAddr = SiS_Pr->IOAddress; + + SiSUSB_InitPtr(SiS_Pr); + SiSUSBRegInit(SiS_Pr, BaseAddr); + SiS_GetSysFlags(SiS_Pr); + + if (!(SiS_SearchModeID(SiS_Pr, &ModeNo, &ModeIdIndex))) + return 0; + + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x05, 0x86); + + SiSInitPCIetc(SiS_Pr); + + ModeNo &= 0x7f; + + SiS_Pr->SiS_ModeType = + SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag & ModeTypeMask; + + SiS_Pr->SiS_SetFlag = LowModeTests; + + /* Set mode on CRT1 */ + SiS_SetCRT1Group(SiS_Pr, ModeNo, ModeIdIndex); + + SiS_HandleCRT1(SiS_Pr); + + SiS_DisplayOn(SiS_Pr); + SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c6, 0xFF); + + /* Store mode number */ + SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x34, ModeNo); + + return 1; +} + +int +SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo) +{ + unsigned short ModeNo = 0; + int i; + + SiSUSB_InitPtr(SiS_Pr); + + if (VModeNo == 0x03) { + + ModeNo = 0x03; + + } else { + + i = 0; + do { + + if (SiS_Pr->SiS_EModeIDTable[i].Ext_VESAID == VModeNo) { + ModeNo = SiS_Pr->SiS_EModeIDTable[i].Ext_ModeID; + break; + } + + } while (SiS_Pr->SiS_EModeIDTable[i++].Ext_ModeID != 0xff); + + } + + if (!ModeNo) + return 0; + + return SiSUSBSetMode(SiS_Pr, ModeNo); +} + +#endif /* INCL_SISUSB_CON */ + + + + diff --git a/drivers/usb/misc/sisusbvga/sisusb_init.h b/drivers/usb/misc/sisusbvga/sisusb_init.h new file mode 100644 index 00000000000..5b11577835c --- /dev/null +++ b/drivers/usb/misc/sisusbvga/sisusb_init.h @@ -0,0 +1,830 @@ +/* $XFree86$ */ +/* $XdotOrg$ */ +/* + * Data and prototypes for init.c + * + * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria + * + * If distributed as part of the Linux kernel, the following license terms + * apply: + * + * * 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 named License, + * * or 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 + * + * Otherwise, the following license terms apply: + * + * * Redistribution and use in source and binary forms, with or without + * * modification, are permitted provided that the following conditions + * * are met: + * * 1) Redistributions of source code must retain the above copyright + * * notice, this list of conditions and the following disclaimer. + * * 2) Redistributions in binary form must reproduce the above copyright + * * notice, this list of conditions and the following disclaimer in the + * * documentation and/or other materials provided with the distribution. + * * 3) The name of the author may not be used to endorse or promote products + * * derived from this software without specific prior written permission. + * * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Thomas Winischhofer <thomas@winischhofer.net> + * + */ + +#ifndef _SISUSB_INIT_H_ +#define _SISUSB_INIT_H_ + +/* SiS_ModeType */ +#define ModeText 0x00 +#define ModeCGA 0x01 +#define ModeEGA 0x02 +#define ModeVGA 0x03 +#define Mode15Bpp 0x04 +#define Mode16Bpp 0x05 +#define Mode24Bpp 0x06 +#define Mode32Bpp 0x07 + +#define ModeTypeMask 0x07 +#define IsTextMode 0x07 + +#define DACInfoFlag 0x0018 +#define MemoryInfoFlag 0x01E0 +#define MemorySizeShift 5 + +/* modeflag */ +#define Charx8Dot 0x0200 +#define LineCompareOff 0x0400 +#define CRT2Mode 0x0800 +#define HalfDCLK 0x1000 +#define NoSupportSimuTV 0x2000 +#define NoSupportLCDScale 0x4000 /* SiS bridge: No scaling possible (no matter what panel) */ +#define DoubleScanMode 0x8000 + +/* Infoflag */ +#define SupportTV 0x0008 +#define SupportTV1024 0x0800 +#define SupportCHTV 0x0800 +#define Support64048060Hz 0x0800 /* Special for 640x480 LCD */ +#define SupportHiVision 0x0010 +#define SupportYPbPr750p 0x1000 +#define SupportLCD 0x0020 +#define SupportRAMDAC2 0x0040 /* All (<= 100Mhz) */ +#define SupportRAMDAC2_135 0x0100 /* All except DH (<= 135Mhz) */ +#define SupportRAMDAC2_162 0x0200 /* B, C (<= 162Mhz) */ +#define SupportRAMDAC2_202 0x0400 /* C (<= 202Mhz) */ +#define InterlaceMode 0x0080 +#define SyncPP 0x0000 +#define SyncPN 0x4000 +#define SyncNP 0x8000 +#define SyncNN 0xc000 + +/* SetFlag */ +#define ProgrammingCRT2 0x0001 +#define LowModeTests 0x0002 +#define LCDVESATiming 0x0008 +#define EnableLVDSDDA 0x0010 +#define SetDispDevSwitchFlag 0x0020 +#define CheckWinDos 0x0040 +#define SetDOSMode 0x0080 + +/* Index in ModeResInfo table */ +#define SIS_RI_320x200 0 +#define SIS_RI_320x240 1 +#define SIS_RI_320x400 2 +#define SIS_RI_400x300 3 +#define SIS_RI_512x384 4 +#define SIS_RI_640x400 5 +#define SIS_RI_640x480 6 +#define SIS_RI_800x600 7 +#define SIS_RI_1024x768 8 +#define SIS_RI_1280x1024 9 +#define SIS_RI_1600x1200 10 +#define SIS_RI_1920x1440 11 +#define SIS_RI_2048x1536 12 +#define SIS_RI_720x480 13 +#define SIS_RI_720x576 14 +#define SIS_RI_1280x960 15 +#define SIS_RI_800x480 16 +#define SIS_RI_1024x576 17 +#define SIS_RI_1280x720 18 +#define SIS_RI_856x480 19 +#define SIS_RI_1280x768 20 +#define SIS_RI_1400x1050 21 +#define SIS_RI_1152x864 22 /* Up to here SiS conforming */ +#define SIS_RI_848x480 23 +#define SIS_RI_1360x768 24 +#define SIS_RI_1024x600 25 +#define SIS_RI_1152x768 26 +#define SIS_RI_768x576 27 +#define SIS_RI_1360x1024 28 +#define SIS_RI_1680x1050 29 +#define SIS_RI_1280x800 30 +#define SIS_RI_1920x1080 31 +#define SIS_RI_960x540 32 +#define SIS_RI_960x600 33 + +#define SIS_VIDEO_CAPTURE 0x00 - 0x30 +#define SIS_VIDEO_PLAYBACK 0x02 - 0x30 +#define SIS_CRT2_PORT_04 0x04 - 0x30 + +/* Mode numbers */ +static const unsigned short ModeIndex_320x200[] = {0x59, 0x41, 0x00, 0x4f}; +static const unsigned short ModeIndex_320x240[] = {0x50, 0x56, 0x00, 0x53}; +static const unsigned short ModeIndex_400x300[] = {0x51, 0x57, 0x00, 0x54}; +static const unsigned short ModeIndex_512x384[] = {0x52, 0x58, 0x00, 0x5c}; +static const unsigned short ModeIndex_640x400[] = {0x2f, 0x5d, 0x00, 0x5e}; +static const unsigned short ModeIndex_640x480[] = {0x2e, 0x44, 0x00, 0x62}; +static const unsigned short ModeIndex_720x480[] = {0x31, 0x33, 0x00, 0x35}; +static const unsigned short ModeIndex_720x576[] = {0x32, 0x34, 0x00, 0x36}; +static const unsigned short ModeIndex_768x576[] = {0x5f, 0x60, 0x00, 0x61}; +static const unsigned short ModeIndex_800x480[] = {0x70, 0x7a, 0x00, 0x76}; +static const unsigned short ModeIndex_800x600[] = {0x30, 0x47, 0x00, 0x63}; +static const unsigned short ModeIndex_848x480[] = {0x39, 0x3b, 0x00, 0x3e}; +static const unsigned short ModeIndex_856x480[] = {0x3f, 0x42, 0x00, 0x45}; +static const unsigned short ModeIndex_960x540[] = {0x1d, 0x1e, 0x00, 0x1f}; +static const unsigned short ModeIndex_960x600[] = {0x20, 0x21, 0x00, 0x22}; +static const unsigned short ModeIndex_1024x768[] = {0x38, 0x4a, 0x00, 0x64}; +static const unsigned short ModeIndex_1024x576[] = {0x71, 0x74, 0x00, 0x77}; +static const unsigned short ModeIndex_1152x864[] = {0x29, 0x2a, 0x00, 0x2b}; +static const unsigned short ModeIndex_1280x720[] = {0x79, 0x75, 0x00, 0x78}; +static const unsigned short ModeIndex_1280x768[] = {0x23, 0x24, 0x00, 0x25}; +static const unsigned short ModeIndex_1280x1024[] = {0x3a, 0x4d, 0x00, 0x65}; + +static const unsigned char SiS_MDA_DAC[] = +{ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, + 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, + 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, + 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, + 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F +}; + +static const unsigned char SiS_CGA_DAC[] = +{ + 0x00,0x10,0x04,0x14,0x01,0x11,0x09,0x15, + 0x00,0x10,0x04,0x14,0x01,0x11,0x09,0x15, + 0x2A,0x3A,0x2E,0x3E,0x2B,0x3B,0x2F,0x3F, + 0x2A,0x3A,0x2E,0x3E,0x2B,0x3B,0x2F,0x3F, + 0x00,0x10,0x04,0x14,0x01,0x11,0x09,0x15, + 0x00,0x10,0x04,0x14,0x01,0x11,0x09,0x15, + 0x2A,0x3A,0x2E,0x3E,0x2B,0x3B,0x2F,0x3F, + 0x2A,0x3A,0x2E,0x3E,0x2B,0x3B,0x2F,0x3F +}; + +static const unsigned char SiS_EGA_DAC[] = +{ + 0x00,0x10,0x04,0x14,0x01,0x11,0x05,0x15, + 0x20,0x30,0x24,0x34,0x21,0x31,0x25,0x35, + 0x08,0x18,0x0C,0x1C,0x09,0x19,0x0D,0x1D, + 0x28,0x38,0x2C,0x3C,0x29,0x39,0x2D,0x3D, + 0x02,0x12,0x06,0x16,0x03,0x13,0x07,0x17, + 0x22,0x32,0x26,0x36,0x23,0x33,0x27,0x37, + 0x0A,0x1A,0x0E,0x1E,0x0B,0x1B,0x0F,0x1F, + 0x2A,0x3A,0x2E,0x3E,0x2B,0x3B,0x2F,0x3F +}; + +static const unsigned char SiS_VGA_DAC[] = +{ + 0x00,0x10,0x04,0x14,0x01,0x11,0x09,0x15, + 0x2A,0x3A,0x2E,0x3E,0x2B,0x3B,0x2F,0x3F, + 0x00,0x05,0x08,0x0B,0x0E,0x11,0x14,0x18, + 0x1C,0x20,0x24,0x28,0x2D,0x32,0x38,0x3F, + 0x00,0x10,0x1F,0x2F,0x3F,0x1F,0x27,0x2F, + 0x37,0x3F,0x2D,0x31,0x36,0x3A,0x3F,0x00, + 0x07,0x0E,0x15,0x1C,0x0E,0x11,0x15,0x18, + 0x1C,0x14,0x16,0x18,0x1A,0x1C,0x00,0x04, + 0x08,0x0C,0x10,0x08,0x0A,0x0C,0x0E,0x10, + 0x0B,0x0C,0x0D,0x0F,0x10 +}; + +static const struct SiS_St SiSUSB_SModeIDTable[] = +{ + {0x03,0x0010,0x18,0x02,0x02,0x00,0x01,0x03,0x40}, + {0xff,0x0000,0x00,0x00,0x00,0x00,0x00,0x00,0x00} +}; + +static const struct SiS_StResInfo_S SiSUSB_StResInfo[] = +{ + { 640,400}, + { 640,350}, + { 720,400}, + { 720,350}, + { 640,480} +}; + +static const struct SiS_ModeResInfo SiSUSB_ModeResInfo[] = +{ + { 320, 200, 8, 8}, /* 0x00 */ + { 320, 240, 8, 8}, /* 0x01 */ + { 320, 400, 8, 8}, /* 0x02 */ + { 400, 300, 8, 8}, /* 0x03 */ + { 512, 384, 8, 8}, /* 0x04 */ + { 640, 400, 8,16}, /* 0x05 */ + { 640, 480, 8,16}, /* 0x06 */ + { 800, 600, 8,16}, /* 0x07 */ + { 1024, 768, 8,16}, /* 0x08 */ + { 1280,1024, 8,16}, /* 0x09 */ + { 1600,1200, 8,16}, /* 0x0a */ + { 1920,1440, 8,16}, /* 0x0b */ + { 2048,1536, 8,16}, /* 0x0c */ + { 720, 480, 8,16}, /* 0x0d */ + { 720, 576, 8,16}, /* 0x0e */ + { 1280, 960, 8,16}, /* 0x0f */ + { 800, 480, 8,16}, /* 0x10 */ + { 1024, 576, 8,16}, /* 0x11 */ + { 1280, 720, 8,16}, /* 0x12 */ + { 856, 480, 8,16}, /* 0x13 */ + { 1280, 768, 8,16}, /* 0x14 */ + { 1400,1050, 8,16}, /* 0x15 */ + { 1152, 864, 8,16}, /* 0x16 */ + { 848, 480, 8,16}, /* 0x17 */ + { 1360, 768, 8,16}, /* 0x18 */ + { 1024, 600, 8,16}, /* 0x19 */ + { 1152, 768, 8,16}, /* 0x1a */ + { 768, 576, 8,16}, /* 0x1b */ + { 1360,1024, 8,16}, /* 0x1c */ + { 1680,1050, 8,16}, /* 0x1d */ + { 1280, 800, 8,16}, /* 0x1e */ + { 1920,1080, 8,16}, /* 0x1f */ + { 960, 540, 8,16}, /* 0x20 */ + { 960, 600, 8,16} /* 0x21 */ +}; + +static const struct SiS_StandTable SiSUSB_StandTable[] = +{ + /* MD_3_400 - mode 0x03 - 400 */ + { + 0x50,0x18,0x10,0x1000, + { 0x00,0x03,0x00,0x02 }, + 0x67, + { 0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f, + 0x00,0x4f,0x0d,0x0e,0x00,0x00,0x00,0x00, + 0x9c,0x8e,0x8f,0x28,0x1f,0x96,0xb9,0xa3, + 0xff }, + { 0x00,0x01,0x02,0x03,0x04,0x05,0x14,0x07, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x0c,0x00,0x0f,0x08 }, + { 0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff } + }, + /* Generic for VGA and higher */ + { + 0x00,0x00,0x00,0x0000, + { 0x01,0x0f,0x00,0x0e }, + 0x23, + { 0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e, + 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3, + 0xff }, + { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x01,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f, 0xff } + } +}; + +static const struct SiS_Ext SiSUSB_EModeIDTable[] = +{ + {0x2e,0x0a1b,0x0101,SIS_RI_640x480, 0x00,0x00,0x05,0x05,0x08, 2}, /* 640x480x8 */ + {0x2f,0x0a1b,0x0100,SIS_RI_640x400, 0x00,0x00,0x05,0x05,0x10, 0}, /* 640x400x8 */ + {0x30,0x2a1b,0x0103,SIS_RI_800x600, 0x00,0x00,0x07,0x06,0x00, 3}, /* 800x600x8 */ + {0x31,0x4a1b,0x0000,SIS_RI_720x480, 0x00,0x00,0x06,0x06,0x11,-1}, /* 720x480x8 */ + {0x32,0x4a1b,0x0000,SIS_RI_720x576, 0x00,0x00,0x06,0x06,0x12,-1}, /* 720x576x8 */ + {0x33,0x4a1d,0x0000,SIS_RI_720x480, 0x00,0x00,0x06,0x06,0x11,-1}, /* 720x480x16 */ + {0x34,0x6a1d,0x0000,SIS_RI_720x576, 0x00,0x00,0x06,0x06,0x12,-1}, /* 720x576x16 */ + {0x35,0x4a1f,0x0000,SIS_RI_720x480, 0x00,0x00,0x06,0x06,0x11,-1}, /* 720x480x32 */ + {0x36,0x6a1f,0x0000,SIS_RI_720x576, 0x00,0x00,0x06,0x06,0x12,-1}, /* 720x576x32 */ + {0x38,0x0a1b,0x0105,SIS_RI_1024x768, 0x00,0x00,0x08,0x07,0x13, 4}, /* 1024x768x8 */ + {0x3a,0x0e3b,0x0107,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x2f, 8}, /* 1280x1024x8 */ + {0x41,0x9a1d,0x010e,SIS_RI_320x200, 0x00,0x00,0x04,0x04,0x1a, 0}, /* 320x200x16 */ + {0x44,0x0a1d,0x0111,SIS_RI_640x480, 0x00,0x00,0x05,0x05,0x08, 2}, /* 640x480x16 */ + {0x47,0x2a1d,0x0114,SIS_RI_800x600, 0x00,0x00,0x07,0x06,0x00, 3}, /* 800x600x16 */ + {0x4a,0x0a3d,0x0117,SIS_RI_1024x768, 0x00,0x00,0x08,0x07,0x13, 4}, /* 1024x768x16 */ + {0x4d,0x0e7d,0x011a,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x2f, 8}, /* 1280x1024x16 */ + {0x50,0x9a1b,0x0132,SIS_RI_320x240, 0x00,0x00,0x04,0x04,0x1b, 2}, /* 320x240x8 */ + {0x51,0xba1b,0x0133,SIS_RI_400x300, 0x00,0x00,0x07,0x07,0x1c, 3}, /* 400x300x8 */ + {0x52,0xba1b,0x0134,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x1d, 4}, /* 512x384x8 */ + {0x56,0x9a1d,0x0135,SIS_RI_320x240, 0x00,0x00,0x04,0x04,0x1b, 2}, /* 320x240x16 */ + {0x57,0xba1d,0x0136,SIS_RI_400x300, 0x00,0x00,0x07,0x07,0x1c, 3}, /* 400x300x16 */ + {0x58,0xba1d,0x0137,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x1d, 4}, /* 512x384x16 */ + {0x59,0x9a1b,0x0138,SIS_RI_320x200, 0x00,0x00,0x04,0x04,0x1a, 0}, /* 320x200x8 */ + {0x5c,0xba1f,0x0000,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x1d, 4}, /* 512x384x32 */ + {0x5d,0x0a1d,0x0139,SIS_RI_640x400, 0x00,0x00,0x05,0x07,0x10, 0}, /* 640x400x16 */ + {0x5e,0x0a1f,0x0000,SIS_RI_640x400, 0x00,0x00,0x05,0x07,0x10, 0}, /* 640x400x32 */ + {0x62,0x0a3f,0x013a,SIS_RI_640x480, 0x00,0x00,0x05,0x05,0x08, 2}, /* 640x480x32 */ + {0x63,0x2a3f,0x013b,SIS_RI_800x600, 0x00,0x00,0x07,0x06,0x00, 3}, /* 800x600x32 */ + {0x64,0x0a7f,0x013c,SIS_RI_1024x768, 0x00,0x00,0x08,0x07,0x13, 4}, /* 1024x768x32 */ + {0x65,0x0eff,0x013d,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x2f, 8}, /* 1280x1024x32 */ + {0x70,0x6a1b,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x07,0x1e,-1}, /* 800x480x8 */ + {0x71,0x4a1b,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x21,-1}, /* 1024x576x8 */ + {0x74,0x4a1d,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x21,-1}, /* 1024x576x16 */ + {0x75,0x0a3d,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x24, 5}, /* 1280x720x16 */ + {0x76,0x6a1f,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x07,0x1e,-1}, /* 800x480x32 */ + {0x77,0x4a1f,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x21,-1}, /* 1024x576x32 */ + {0x78,0x0a3f,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x24, 5}, /* 1280x720x32 */ + {0x79,0x0a3b,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x24, 5}, /* 1280x720x8 */ + {0x7a,0x6a1d,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x07,0x1e,-1}, /* 800x480x16 */ + {0x23,0x0e3b,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x27, 6}, /* 1280x768x8 */ + {0x24,0x0e7d,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x27, 6}, /* 1280x768x16 */ + {0x25,0x0eff,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x27, 6}, /* 1280x768x32 */ + {0x39,0x6a1b,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x28,-1}, /* 848x480 */ + {0x3b,0x6a3d,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x28,-1}, + {0x3e,0x6a7f,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x28,-1}, + {0x3f,0x6a1b,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x2a,-1}, /* 856x480 */ + {0x42,0x6a3d,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x2a,-1}, + {0x45,0x6a7f,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x2a,-1}, + {0x4f,0x9a1f,0x0000,SIS_RI_320x200, 0x00,0x00,0x04,0x04,0x1a, 0}, /* 320x200x32 */ + {0x53,0x9a1f,0x0000,SIS_RI_320x240, 0x00,0x00,0x04,0x04,0x1b, 2}, /* 320x240x32 */ + {0x54,0xba1f,0x0000,SIS_RI_400x300, 0x00,0x00,0x07,0x07,0x1c, 3}, /* 400x300x32 */ + {0x5f,0x6a1b,0x0000,SIS_RI_768x576, 0x00,0x00,0x06,0x06,0x2c,-1}, /* 768x576 */ + {0x60,0x6a1d,0x0000,SIS_RI_768x576, 0x00,0x00,0x06,0x06,0x2c,-1}, + {0x61,0x6a3f,0x0000,SIS_RI_768x576, 0x00,0x00,0x06,0x06,0x2c,-1}, + {0x1d,0x6a1b,0x0000,SIS_RI_960x540, 0x00,0x00,0x00,0x00,0x2d,-1}, /* 960x540 */ + {0x1e,0x6a3d,0x0000,SIS_RI_960x540, 0x00,0x00,0x00,0x00,0x2d,-1}, + {0x1f,0x6a7f,0x0000,SIS_RI_960x540, 0x00,0x00,0x00,0x00,0x2d,-1}, + {0x20,0x6a1b,0x0000,SIS_RI_960x600, 0x00,0x00,0x00,0x00,0x2e,-1}, /* 960x600 */ + {0x21,0x6a3d,0x0000,SIS_RI_960x600, 0x00,0x00,0x00,0x00,0x2e,-1}, + {0x22,0x6a7f,0x0000,SIS_RI_960x600, 0x00,0x00,0x00,0x00,0x2e,-1}, + {0x29,0x4e1b,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x33,-1}, /* 1152x864 */ + {0x2a,0x4e3d,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x33,-1}, + {0x2b,0x4e7f,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x33,-1}, + {0xff,0x0000,0x0000,0, 0x00,0x00,0x00,0x00,0x00,-1} +}; + +static const struct SiS_Ext2 SiSUSB_RefIndex[] = +{ + {0x085f,0x0d,0x03,0x05,0x05,0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x0 */ + {0x0067,0x0e,0x04,0x05,0x05,0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x1 */ + {0x0067,0x0f,0x08,0x48,0x05,0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x2 */ + {0x0067,0x10,0x07,0x8b,0x05,0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x3 */ + {0x0047,0x11,0x0a,0x00,0x05,0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x4 */ + {0x0047,0x12,0x0d,0x00,0x05,0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x5 */ + {0x0047,0x13,0x13,0x00,0x05,0x30, 800, 600, 0x20, 0x00, 0x00}, /* 0x6 */ + {0x0107,0x14,0x1c,0x00,0x05,0x30, 800, 600, 0x20, 0x00, 0x00}, /* 0x7 */ + {0xc85f,0x05,0x00,0x04,0x04,0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0x8 */ + {0xc067,0x06,0x02,0x04,0x04,0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0x9 */ + {0xc067,0x07,0x02,0x47,0x04,0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xa */ + {0xc067,0x08,0x03,0x8a,0x04,0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xb */ + {0xc047,0x09,0x05,0x00,0x04,0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xc */ + {0xc047,0x0a,0x09,0x00,0x04,0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xd */ + {0xc047,0x0b,0x0e,0x00,0x04,0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xe */ + {0xc047,0x0c,0x15,0x00,0x04,0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xf */ + {0x487f,0x04,0x00,0x00,0x00,0x2f, 640, 400, 0x30, 0x55, 0x6e}, /* 0x10 */ + {0xc06f,0x3c,0x01,0x06,0x13,0x31, 720, 480, 0x30, 0x00, 0x00}, /* 0x11 */ + {0x006f,0x3d,0x6f,0x06,0x14,0x32, 720, 576, 0x30, 0x00, 0x00}, /* 0x12 (6f was 03) */ + {0x0087,0x15,0x06,0x00,0x06,0x38,1024, 768, 0x30, 0x00, 0x00}, /* 0x13 */ + {0xc877,0x16,0x0b,0x06,0x06,0x38,1024, 768, 0x20, 0x00, 0x00}, /* 0x14 */ + {0xc067,0x17,0x0f,0x49,0x06,0x38,1024, 768, 0x20, 0x00, 0x00}, /* 0x15 */ + {0x0067,0x18,0x11,0x00,0x06,0x38,1024, 768, 0x20, 0x00, 0x00}, /* 0x16 */ + {0x0047,0x19,0x16,0x8c,0x06,0x38,1024, 768, 0x20, 0x00, 0x00}, /* 0x17 */ + {0x0107,0x1a,0x1b,0x00,0x06,0x38,1024, 768, 0x10, 0x00, 0x00}, /* 0x18 */ + {0x0107,0x1b,0x1f,0x00,0x06,0x38,1024, 768, 0x10, 0x00, 0x00}, /* 0x19 */ + {0x407f,0x00,0x00,0x00,0x00,0x41, 320, 200, 0x30, 0x56, 0x4e}, /* 0x1a */ + {0xc07f,0x01,0x00,0x04,0x04,0x50, 320, 240, 0x30, 0x00, 0x00}, /* 0x1b */ + {0x007f,0x02,0x04,0x05,0x05,0x51, 400, 300, 0x30, 0x00, 0x00}, /* 0x1c */ + {0xc077,0x03,0x0b,0x06,0x06,0x52, 512, 384, 0x30, 0x00, 0x00}, /* 0x1d */ + {0x0077,0x32,0x40,0x08,0x18,0x70, 800, 480, 0x30, 0x00, 0x00}, /* 0x1e */ + {0x0047,0x33,0x07,0x08,0x18,0x70, 800, 480, 0x30, 0x00, 0x00}, /* 0x1f */ + {0x0047,0x34,0x0a,0x08,0x18,0x70, 800, 480, 0x30, 0x00, 0x00}, /* 0x20 */ + {0x0077,0x35,0x0b,0x09,0x19,0x71,1024, 576, 0x30, 0x00, 0x00}, /* 0x21 */ + {0x0047,0x36,0x11,0x09,0x19,0x71,1024, 576, 0x30, 0x00, 0x00}, /* 0x22 */ + {0x0047,0x37,0x16,0x09,0x19,0x71,1024, 576, 0x30, 0x00, 0x00}, /* 0x23 */ + {0x1137,0x38,0x19,0x0a,0x0c,0x75,1280, 720, 0x30, 0x00, 0x00}, /* 0x24 */ + {0x1107,0x39,0x1e,0x0a,0x0c,0x75,1280, 720, 0x30, 0x00, 0x00}, /* 0x25 */ + {0x1307,0x3a,0x20,0x0a,0x0c,0x75,1280, 720, 0x30, 0x00, 0x00}, /* 0x26 */ + {0x0077,0x42,0x5b,0x08,0x11,0x23,1280, 768, 0x30, 0x00, 0x00}, /* 0x27 */ + {0x0087,0x45,0x57,0x00,0x16,0x39, 848, 480, 0x30, 0x00, 0x00}, /* 0x28 38Hzi */ + {0xc067,0x46,0x55,0x0b,0x16,0x39, 848, 480, 0x30, 0x00, 0x00}, /* 0x29 848x480-60Hz */ + {0x0087,0x47,0x57,0x00,0x17,0x3f, 856, 480, 0x30, 0x00, 0x00}, /* 0x2a 856x480-38Hzi */ + {0xc067,0x48,0x57,0x00,0x17,0x3f, 856, 480, 0x30, 0x00, 0x00}, /* 0x2b 856x480-60Hz */ + {0x006f,0x4d,0x71,0x06,0x15,0x5f, 768, 576, 0x30, 0x00, 0x00}, /* 0x2c 768x576-56Hz */ + {0x0067,0x52,0x6a,0x00,0x1c,0x1d, 960, 540, 0x30, 0x00, 0x00}, /* 0x2d 960x540 60Hz */ + {0x0077,0x53,0x6b,0x0b,0x1d,0x20, 960, 600, 0x30, 0x00, 0x00}, /* 0x2e 960x600 60Hz */ + {0x0087,0x1c,0x11,0x00,0x07,0x3a,1280,1024, 0x30, 0x00, 0x00}, /* 0x2f */ + {0x0137,0x1d,0x19,0x07,0x07,0x3a,1280,1024, 0x00, 0x00, 0x00}, /* 0x30 */ + {0x0107,0x1e,0x1e,0x00,0x07,0x3a,1280,1024, 0x00, 0x00, 0x00}, /* 0x31 */ + {0x0207,0x1f,0x20,0x00,0x07,0x3a,1280,1024, 0x00, 0x00, 0x00}, /* 0x32 */ + {0x0127,0x54,0x6d,0x00,0x1a,0x29,1152, 864, 0x30, 0x00, 0x00}, /* 0x33 1152x864-60Hz */ + {0x0127,0x44,0x19,0x00,0x1a,0x29,1152, 864, 0x30, 0x00, 0x00}, /* 0x34 1152x864-75Hz */ + {0x0127,0x4a,0x1e,0x00,0x1a,0x29,1152, 864, 0x30, 0x00, 0x00}, /* 0x35 1152x864-85Hz */ + {0xffff,0x00,0x00,0x00,0x00,0x00, 0, 0, 0, 0x00, 0x00} +}; + +static const struct SiS_CRT1Table SiSUSB_CRT1Table[] = +{ + {{0x2d,0x27,0x28,0x90,0x2c,0x80,0xbf,0x1f, + 0x9c,0x8e,0x8f,0x96,0xb9,0x30,0x00,0x00, + 0x00}}, /* 0x0 */ + {{0x2d,0x27,0x28,0x90,0x2c,0x80,0x0b,0x3e, + 0xe9,0x8b,0xdf,0xe7,0x04,0x00,0x00,0x00, + 0x00}}, /* 0x1 */ + {{0x3d,0x31,0x31,0x81,0x37,0x1f,0x72,0xf0, + 0x58,0x8c,0x57,0x57,0x73,0x20,0x00,0x05, + 0x01}}, /* 0x2 */ + {{0x4f,0x3f,0x3f,0x93,0x45,0x0d,0x24,0xf5, + 0x02,0x88,0xff,0xff,0x25,0x10,0x00,0x01, + 0x01}}, /* 0x3 */ + {{0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f, + 0x9c,0x8e,0x8f,0x96,0xb9,0x30,0x00,0x05, + 0x00}}, /* 0x4 */ + {{0x5f,0x4f,0x4f,0x83,0x55,0x81,0x0b,0x3e, + 0xe9,0x8b,0xdf,0xe8,0x0c,0x00,0x00,0x05, + 0x00}}, /* 0x5 */ + {{0x63,0x4f,0x4f,0x87,0x56,0x9b,0x06,0x3e, + 0xe8,0x8a,0xdf,0xe7,0x07,0x00,0x00,0x01, + 0x00}}, /* 0x6 */ + {{0x64,0x4f,0x4f,0x88,0x55,0x9d,0xf2,0x1f, + 0xe0,0x83,0xdf,0xdf,0xf3,0x10,0x00,0x01, + 0x00}}, /* 0x7 */ + {{0x63,0x4f,0x4f,0x87,0x5a,0x81,0xfb,0x1f, + 0xe0,0x83,0xdf,0xdf,0xfc,0x10,0x00,0x05, + 0x00}}, /* 0x8 */ + {{0x65,0x4f,0x4f,0x89,0x58,0x80,0xfb,0x1f, + 0xe0,0x83,0xdf,0xdf,0xfc,0x10,0x00,0x05, + 0x61}}, /* 0x9 */ + {{0x65,0x4f,0x4f,0x89,0x58,0x80,0x01,0x3e, + 0xe0,0x83,0xdf,0xdf,0x02,0x00,0x00,0x05, + 0x61}}, /* 0xa */ + {{0x67,0x4f,0x4f,0x8b,0x58,0x81,0x0d,0x3e, + 0xe0,0x83,0xdf,0xdf,0x0e,0x00,0x00,0x05, + 0x61}}, /* 0xb */ + {{0x65,0x4f,0x4f,0x89,0x57,0x9f,0xfb,0x1f, + 0xe6,0x8a,0xdf,0xdf,0xfc,0x10,0x00,0x01, + 0x00}}, /* 0xc */ + {{0x7b,0x63,0x63,0x9f,0x6a,0x93,0x6f,0xf0, + 0x58,0x8a,0x57,0x57,0x70,0x20,0x00,0x05, + 0x01}}, /* 0xd */ + {{0x7f,0x63,0x63,0x83,0x6c,0x1c,0x72,0xf0, + 0x58,0x8c,0x57,0x57,0x73,0x20,0x00,0x06, + 0x01}}, /* 0xe */ + {{0x7d,0x63,0x63,0x81,0x6e,0x1d,0x98,0xf0, + 0x7c,0x82,0x57,0x57,0x99,0x00,0x00,0x06, + 0x01}}, /* 0xf */ + {{0x7f,0x63,0x63,0x83,0x69,0x13,0x6f,0xf0, + 0x58,0x8b,0x57,0x57,0x70,0x20,0x00,0x06, + 0x01}}, /* 0x10 */ + {{0x7e,0x63,0x63,0x82,0x6b,0x13,0x75,0xf0, + 0x58,0x8b,0x57,0x57,0x76,0x20,0x00,0x06, + 0x01}}, /* 0x11 */ + {{0x81,0x63,0x63,0x85,0x6d,0x18,0x7a,0xf0, + 0x58,0x8b,0x57,0x57,0x7b,0x20,0x00,0x06, + 0x61}}, /* 0x12 */ + {{0x83,0x63,0x63,0x87,0x6e,0x19,0x81,0xf0, + 0x58,0x8b,0x57,0x57,0x82,0x20,0x00,0x06, + 0x61}}, /* 0x13 */ + {{0x85,0x63,0x63,0x89,0x6f,0x1a,0x91,0xf0, + 0x58,0x8b,0x57,0x57,0x92,0x20,0x00,0x06, + 0x61}}, /* 0x14 */ + {{0x99,0x7f,0x7f,0x9d,0x84,0x1a,0x96,0x1f, + 0x7f,0x83,0x7f,0x7f,0x97,0x10,0x00,0x02, + 0x00}}, /* 0x15 */ + {{0xa3,0x7f,0x7f,0x87,0x86,0x97,0x24,0xf5, + 0x02,0x88,0xff,0xff,0x25,0x10,0x00,0x02, + 0x01}}, /* 0x16 */ + {{0xa1,0x7f,0x7f,0x85,0x86,0x97,0x24,0xf5, + 0x02,0x88,0xff,0xff,0x25,0x10,0x00,0x02, + 0x01}}, /* 0x17 */ + {{0x9f,0x7f,0x7f,0x83,0x85,0x91,0x1e,0xf5, + 0x00,0x83,0xff,0xff,0x1f,0x10,0x00,0x02, + 0x01}}, /* 0x18 */ + {{0xa7,0x7f,0x7f,0x8b,0x89,0x95,0x26,0xf5, + 0x00,0x83,0xff,0xff,0x27,0x10,0x00,0x02, + 0x01}}, /* 0x19 */ + {{0xa9,0x7f,0x7f,0x8d,0x8c,0x9a,0x2c,0xf5, + 0x00,0x83,0xff,0xff,0x2d,0x14,0x00,0x02, + 0x62}}, /* 0x1a */ + {{0xab,0x7f,0x7f,0x8f,0x8d,0x9b,0x35,0xf5, + 0x00,0x83,0xff,0xff,0x36,0x14,0x00,0x02, + 0x62}}, /* 0x1b */ + {{0xcf,0x9f,0x9f,0x93,0xb2,0x01,0x14,0xba, + 0x00,0x83,0xff,0xff,0x15,0x00,0x00,0x03, + 0x00}}, /* 0x1c */ + {{0xce,0x9f,0x9f,0x92,0xa9,0x17,0x28,0x5a, + 0x00,0x83,0xff,0xff,0x29,0x09,0x00,0x07, + 0x01}}, /* 0x1d */ + {{0xce,0x9f,0x9f,0x92,0xa5,0x17,0x28,0x5a, + 0x00,0x83,0xff,0xff,0x29,0x09,0x00,0x07, + 0x01}}, /* 0x1e */ + {{0xd3,0x9f,0x9f,0x97,0xab,0x1f,0x2e,0x5a, + 0x00,0x83,0xff,0xff,0x2f,0x09,0x00,0x07, + 0x01}}, /* 0x1f */ + {{0x09,0xc7,0xc7,0x8d,0xd3,0x0b,0xe0,0x10, + 0xb0,0x83,0xaf,0xaf,0xe1,0x2f,0x01,0x04, + 0x00}}, /* 0x20 */ + {{0x09,0xc7,0xc7,0x8d,0xd3,0x0b,0xe0,0x10, + 0xb0,0x83,0xaf,0xaf,0xe1,0x2f,0x01,0x04, + 0x00}}, /* 0x21 */ + {{0x09,0xc7,0xc7,0x8d,0xd3,0x0b,0xe0,0x10, + 0xb0,0x83,0xaf,0xaf,0xe1,0x2f,0x01,0x04, + 0x00}}, /* 0x22 */ + {{0x09,0xc7,0xc7,0x8d,0xd3,0x0b,0xe0,0x10, + 0xb0,0x83,0xaf,0xaf,0xe1,0x2f,0x01,0x04, + 0x00}}, /* 0x23 */ + {{0x09,0xc7,0xc7,0x8d,0xd3,0x0b,0xe0,0x10, + 0xb0,0x83,0xaf,0xaf,0xe1,0x2f,0x01,0x04, + 0x00}}, /* 0x24 */ + {{0x09,0xc7,0xc7,0x8d,0xd3,0x0b,0xe0,0x10, + 0xb0,0x83,0xaf,0xaf,0xe1,0x2f,0x01,0x04, + 0x00}}, /* 0x25 */ + {{0x09,0xc7,0xc7,0x8d,0xd3,0x0b,0xe0,0x10, + 0xb0,0x83,0xaf,0xaf,0xe1,0x2f,0x01,0x04, + 0x00}}, /* 0x26 */ + {{0x40,0xef,0xef,0x84,0x03,0x1d,0xda,0x1f, + 0xa0,0x83,0x9f,0x9f,0xdb,0x1f,0x41,0x01, + 0x00}}, /* 0x27 */ + {{0x43,0xef,0xef,0x87,0x06,0x00,0xd4,0x1f, + 0xa0,0x83,0x9f,0x9f,0xd5,0x1f,0x41,0x05, + 0x63}}, /* 0x28 */ + {{0x45,0xef,0xef,0x89,0x07,0x01,0xd9,0x1f, + 0xa0,0x83,0x9f,0x9f,0xda,0x1f,0x41,0x05, + 0x63}}, /* 0x29 */ + {{0x40,0xef,0xef,0x84,0x03,0x1d,0xda,0x1f, + 0xa0,0x83,0x9f,0x9f,0xdb,0x1f,0x41,0x01, + 0x00}}, /* 0x2a */ + {{0x40,0xef,0xef,0x84,0x03,0x1d,0xda,0x1f, + 0xa0,0x83,0x9f,0x9f,0xdb,0x1f,0x41,0x01, + 0x00}}, /* 0x2b */ + {{0x40,0xef,0xef,0x84,0x03,0x1d,0xda,0x1f, + 0xa0,0x83,0x9f,0x9f,0xdb,0x1f,0x41,0x01, + 0x00}}, /* 0x2c */ + {{0x59,0xff,0xff,0x9d,0x17,0x13,0x33,0xba, + 0x00,0x83,0xff,0xff,0x34,0x0f,0x41,0x05, + 0x44}}, /* 0x2d */ + {{0x5b,0xff,0xff,0x9f,0x18,0x14,0x38,0xba, + 0x00,0x83,0xff,0xff,0x39,0x0f,0x41,0x05, + 0x44}}, /* 0x2e */ + {{0x5b,0xff,0xff,0x9f,0x18,0x14,0x3d,0xba, + 0x00,0x83,0xff,0xff,0x3e,0x0f,0x41,0x05, + 0x44}}, /* 0x2f */ + {{0x5d,0xff,0xff,0x81,0x19,0x95,0x41,0xba, + 0x00,0x84,0xff,0xff,0x42,0x0f,0x41,0x05, + 0x44}}, /* 0x30 */ + {{0x55,0xff,0xff,0x99,0x0d,0x0c,0x3e,0xba, + 0x00,0x84,0xff,0xff,0x3f,0x0f,0x41,0x05, + 0x00}}, /* 0x31 */ + {{0x7f,0x63,0x63,0x83,0x6c,0x1c,0x72,0xba, + 0x27,0x8b,0xdf,0xdf,0x73,0x00,0x00,0x06, + 0x01}}, /* 0x32 */ + {{0x7f,0x63,0x63,0x83,0x69,0x13,0x6f,0xba, + 0x26,0x89,0xdf,0xdf,0x6f,0x00,0x00,0x06, + 0x01}}, /* 0x33 */ + {{0x7f,0x63,0x63,0x82,0x6b,0x13,0x75,0xba, + 0x29,0x8c,0xdf,0xdf,0x75,0x00,0x00,0x06, + 0x01}}, /* 0x34 */ + {{0xa3,0x7f,0x7f,0x87,0x86,0x97,0x24,0xf1, + 0xaf,0x85,0x3f,0x3f,0x25,0x30,0x00,0x02, + 0x01}}, /* 0x35 */ + {{0x9f,0x7f,0x7f,0x83,0x85,0x91,0x1e,0xf1, + 0xad,0x81,0x3f,0x3f,0x1f,0x30,0x00,0x02, + 0x01}}, /* 0x36 */ + {{0xa7,0x7f,0x7f,0x88,0x89,0x95,0x26,0xf1, + 0xb1,0x85,0x3f,0x3f,0x27,0x30,0x00,0x02, + 0x01}}, /* 0x37 */ + {{0xce,0x9f,0x9f,0x92,0xa9,0x17,0x28,0xc4, + 0x7a,0x8e,0xcf,0xcf,0x29,0x21,0x00,0x07, + 0x01}}, /* 0x38 */ + {{0xce,0x9f,0x9f,0x92,0xa5,0x17,0x28,0xd4, + 0x7a,0x8e,0xcf,0xcf,0x29,0x21,0x00,0x07, + 0x01}}, /* 0x39 */ + {{0xd3,0x9f,0x9f,0x97,0xab,0x1f,0x2e,0xd4, + 0x7d,0x81,0xcf,0xcf,0x2f,0x21,0x00,0x07, + 0x01}}, /* 0x3a */ + {{0xdc,0x9f,0x9f,0x80,0xaf,0x9d,0xe6,0xff, + 0xc0,0x83,0xbf,0xbf,0xe7,0x10,0x00,0x07, + 0x01}}, /* 0x3b */ + {{0x6b,0x59,0x59,0x8f,0x5e,0x8c,0x0b,0x3e, + 0xe9,0x8b,0xdf,0xe7,0x04,0x00,0x00,0x05, + 0x00}}, /* 0x3c */ + {{0x6d,0x59,0x59,0x91,0x60,0x89,0x53,0xf0, + 0x41,0x84,0x3f,0x3f,0x54,0x00,0x00,0x05, + 0x41}}, /* 0x3d */ + {{0x86,0x6a,0x6a,0x8a,0x74,0x06,0x8c,0x15, + 0x4f,0x83,0xef,0xef,0x8d,0x30,0x00,0x02, + 0x00}}, /* 0x3e */ + {{0x81,0x6a,0x6a,0x85,0x70,0x00,0x0f,0x3e, + 0xeb,0x8e,0xdf,0xdf,0x10,0x00,0x00,0x02, + 0x00}}, /* 0x3f */ + {{0xa3,0x7f,0x7f,0x87,0x86,0x97,0x1e,0xf1, + 0xae,0x85,0x57,0x57,0x1f,0x30,0x00,0x02, + 0x01}}, /* 0x40 */ + {{0xa3,0x7f,0x7f,0x87,0x86,0x97,0x24,0xf5, + 0x02,0x88,0xff,0xff,0x25,0x10,0x00,0x02, + 0x01}}, /* 0x41 */ + {{0xce,0x9f,0x9f,0x92,0xa9,0x17,0x20,0xf5, + 0x03,0x88,0xff,0xff,0x21,0x10,0x00,0x07, + 0x01}}, /* 0x42 */ + {{0xe6,0xae,0xae,0x8a,0xbd,0x90,0x3d,0x10, + 0x1a,0x8d,0x19,0x19,0x3e,0x2f,0x00,0x03, + 0x00}}, /* 0x43 */ + {{0xc3,0x8f,0x8f,0x87,0x9b,0x0b,0x82,0xef, + 0x60,0x83,0x5f,0x5f,0x83,0x10,0x00,0x07, + 0x01}}, /* 0x44 */ + {{0x86,0x69,0x69,0x8A,0x74,0x06,0x8C,0x15, + 0x4F,0x83,0xEF,0xEF,0x8D,0x30,0x00,0x02, + 0x00}}, /* 0x45 */ + {{0x83,0x69,0x69,0x87,0x6f,0x1d,0x03,0x3E, + 0xE5,0x8d,0xDF,0xe4,0x04,0x00,0x00,0x06, + 0x00}}, /* 0x46 */ + {{0x86,0x6A,0x6A,0x8A,0x74,0x06,0x8C,0x15, + 0x4F,0x83,0xEF,0xEF,0x8D,0x30,0x00,0x02, + 0x00}}, /* 0x47 */ + {{0x81,0x6A,0x6A,0x85,0x70,0x00,0x0F,0x3E, + 0xEB,0x8E,0xDF,0xDF,0x10,0x00,0x00,0x02, + 0x00}}, /* 0x48 */ + {{0xdd,0xa9,0xa9,0x81,0xb4,0x97,0x26,0xfd, + 0x01,0x8d,0xff,0x00,0x27,0x10,0x00,0x03, + 0x01}}, /* 0x49 */ + {{0xd9,0x8f,0x8f,0x9d,0xba,0x0a,0x8a,0xff, + 0x60,0x8b,0x5f,0x5f,0x8b,0x10,0x00,0x03, + 0x01}}, /* 0x4a */ + {{0xea,0xae,0xae,0x8e,0xba,0x82,0x40,0x10, + 0x1b,0x87,0x19,0x1a,0x41,0x0f,0x00,0x03, + 0x00}}, /* 0x4b */ + {{0xd3,0x9f,0x9f,0x97,0xab,0x1f,0xf1,0xff, + 0xc0,0x83,0xbf,0xbf,0xf2,0x10,0x00,0x07, + 0x01}}, /* 0x4c */ + {{0x75,0x5f,0x5f,0x99,0x66,0x90,0x53,0xf0, + 0x41,0x84,0x3f,0x3f,0x54,0x00,0x00,0x05, + 0x41}}, + {{0x2d,0x27,0x28,0x90,0x2c,0x80,0x0b,0x3e, + 0xe9,0x8b,0xdf,0xe7,0x04,0x00,0x00,0x00, + 0x00}}, /* 0x4e */ + {{0xcd,0x9f,0x9f,0x91,0xab,0x1c,0x3a,0xff, + 0x20,0x83,0x1f,0x1f,0x3b,0x10,0x00,0x07, + 0x21}}, /* 0x4f */ + {{0x15,0xd1,0xd1,0x99,0xe2,0x19,0x3d,0x10, + 0x1a,0x8d,0x19,0x19,0x3e,0x2f,0x01,0x0c, + 0x20}}, /* 0x50 */ + {{0x0e,0xef,0xef,0x92,0xfe,0x03,0x30,0xf0, + 0x1e,0x83,0x1b,0x1c,0x31,0x00,0x01,0x00, + 0x61}}, /* 0x51 */ + {{0x85,0x77,0x77,0x89,0x7d,0x01,0x31,0xf0, + 0x1e,0x84,0x1b,0x1c,0x32,0x00,0x00,0x02, + 0x41}}, /* 0x52 */ + {{0x87,0x77,0x77,0x8b,0x81,0x0b,0x68,0xf0, + 0x5a,0x80,0x57,0x57,0x69,0x00,0x00,0x02, + 0x01}}, /* 0x53 */ + {{0xcd,0x8f,0x8f,0x91,0x9b,0x1b,0x7a,0xff, + 0x64,0x8c,0x5f,0x62,0x7b,0x10,0x00,0x07, + 0x41}} /* 0x54 */ +}; + +static struct SiS_VCLKData SiSUSB_VCLKData[] = +{ + { 0x1b,0xe1, 25}, /* 0x00 */ + { 0x4e,0xe4, 28}, /* 0x01 */ + { 0x57,0xe4, 31}, /* 0x02 */ + { 0xc3,0xc8, 36}, /* 0x03 */ + { 0x42,0xe2, 40}, /* 0x04 */ + { 0xfe,0xcd, 43}, /* 0x05 */ + { 0x5d,0xc4, 44}, /* 0x06 */ + { 0x52,0xe2, 49}, /* 0x07 */ + { 0x53,0xe2, 50}, /* 0x08 */ + { 0x74,0x67, 52}, /* 0x09 */ + { 0x6d,0x66, 56}, /* 0x0a */ + { 0x5a,0x64, 65}, /* 0x0b */ + { 0x46,0x44, 67}, /* 0x0c */ + { 0xb1,0x46, 68}, /* 0x0d */ + { 0xd3,0x4a, 72}, /* 0x0e */ + { 0x29,0x61, 75}, /* 0x0f */ + { 0x6e,0x46, 76}, /* 0x10 */ + { 0x2b,0x61, 78}, /* 0x11 */ + { 0x31,0x42, 79}, /* 0x12 */ + { 0xab,0x44, 83}, /* 0x13 */ + { 0x46,0x25, 84}, /* 0x14 */ + { 0x78,0x29, 86}, /* 0x15 */ + { 0x62,0x44, 94}, /* 0x16 */ + { 0x2b,0x41,104}, /* 0x17 */ + { 0x3a,0x23,105}, /* 0x18 */ + { 0x70,0x44,108}, /* 0x19 */ + { 0x3c,0x23,109}, /* 0x1a */ + { 0x5e,0x43,113}, /* 0x1b */ + { 0xbc,0x44,116}, /* 0x1c */ + { 0xe0,0x46,132}, /* 0x1d */ + { 0x54,0x42,135}, /* 0x1e */ + { 0xea,0x2a,139}, /* 0x1f */ + { 0x41,0x22,157}, /* 0x20 */ + { 0x70,0x24,162}, /* 0x21 */ + { 0x30,0x21,175}, /* 0x22 */ + { 0x4e,0x22,189}, /* 0x23 */ + { 0xde,0x26,194}, /* 0x24 */ + { 0x62,0x06,202}, /* 0x25 */ + { 0x3f,0x03,229}, /* 0x26 */ + { 0xb8,0x06,234}, /* 0x27 */ + { 0x34,0x02,253}, /* 0x28 */ + { 0x58,0x04,255}, /* 0x29 */ + { 0x24,0x01,265}, /* 0x2a */ + { 0x9b,0x02,267}, /* 0x2b */ + { 0x70,0x05,270}, /* 0x2c */ + { 0x25,0x01,272}, /* 0x2d */ + { 0x9c,0x02,277}, /* 0x2e */ + { 0x27,0x01,286}, /* 0x2f */ + { 0x3c,0x02,291}, /* 0x30 */ + { 0xef,0x0a,292}, /* 0x31 */ + { 0xf6,0x0a,310}, /* 0x32 */ + { 0x95,0x01,315}, /* 0x33 */ + { 0xf0,0x09,324}, /* 0x34 */ + { 0xfe,0x0a,331}, /* 0x35 */ + { 0xf3,0x09,332}, /* 0x36 */ + { 0xea,0x08,340}, /* 0x37 */ + { 0xe8,0x07,376}, /* 0x38 */ + { 0xde,0x06,389}, /* 0x39 */ + { 0x52,0x2a, 54}, /* 0x3a 301 TV */ + { 0x52,0x6a, 27}, /* 0x3b 301 TV */ + { 0x62,0x24, 70}, /* 0x3c 301 TV */ + { 0x62,0x64, 70}, /* 0x3d 301 TV */ + { 0xa8,0x4c, 30}, /* 0x3e 301 TV */ + { 0x20,0x26, 33}, /* 0x3f 301 TV */ + { 0x31,0xc2, 39}, /* 0x40 */ + { 0x60,0x36, 30}, /* 0x41 Chrontel */ + { 0x40,0x4a, 28}, /* 0x42 Chrontel */ + { 0x9f,0x46, 44}, /* 0x43 Chrontel */ + { 0x97,0x2c, 26}, /* 0x44 */ + { 0x44,0xe4, 25}, /* 0x45 Chrontel */ + { 0x7e,0x32, 47}, /* 0x46 Chrontel */ + { 0x8a,0x24, 31}, /* 0x47 Chrontel */ + { 0x97,0x2c, 26}, /* 0x48 Chrontel */ + { 0xce,0x3c, 39}, /* 0x49 */ + { 0x52,0x4a, 36}, /* 0x4a Chrontel */ + { 0x34,0x61, 95}, /* 0x4b */ + { 0x78,0x27,108}, /* 0x4c - was 102 */ + { 0x66,0x43,123}, /* 0x4d Modes 0x26-0x28 (1400x1050) */ + { 0x41,0x4e, 21}, /* 0x4e */ + { 0xa1,0x4a, 29}, /* 0x4f Chrontel */ + { 0x19,0x42, 42}, /* 0x50 */ + { 0x54,0x46, 58}, /* 0x51 Chrontel */ + { 0x25,0x42, 61}, /* 0x52 */ + { 0x44,0x44, 66}, /* 0x53 Chrontel */ + { 0x3a,0x62, 70}, /* 0x54 Chrontel */ + { 0x62,0xc6, 34}, /* 0x55 848x480-60 */ + { 0x6a,0xc6, 37}, /* 0x56 848x480-75 - TEMP */ + { 0xbf,0xc8, 35}, /* 0x57 856x480-38i,60 */ + { 0x30,0x23, 88}, /* 0x58 1360x768-62 (is 60Hz!) */ + { 0x52,0x07,149}, /* 0x59 1280x960-85 */ + { 0x56,0x07,156}, /* 0x5a 1400x1050-75 */ + { 0x70,0x29, 81}, /* 0x5b 1280x768 LCD */ + { 0x45,0x25, 83}, /* 0x5c 1280x800 */ + { 0x70,0x0a,147}, /* 0x5d 1680x1050 */ + { 0x70,0x24,162}, /* 0x5e 1600x1200 */ + { 0x5a,0x64, 65}, /* 0x5f 1280x720 - temp */ + { 0x63,0x46, 68}, /* 0x60 1280x768_2 */ + { 0x31,0x42, 79}, /* 0x61 1280x768_3 - temp */ + { 0, 0, 0}, /* 0x62 - custom (will be filled out at run-time) */ + { 0x5a,0x64, 65}, /* 0x63 1280x720 (LCD LVDS) */ + { 0x70,0x28, 90}, /* 0x64 1152x864@60 */ + { 0x41,0xc4, 32}, /* 0x65 848x480@60 */ + { 0x5c,0xc6, 32}, /* 0x66 856x480@60 */ + { 0x76,0xe7, 27}, /* 0x67 720x480@60 */ + { 0x5f,0xc6, 33}, /* 0x68 720/768x576@60 */ + { 0x52,0x27, 75}, /* 0x69 1920x1080i 60Hz interlaced */ + { 0x7c,0x6b, 38}, /* 0x6a 960x540@60 */ + { 0xe3,0x56, 41}, /* 0x6b 960x600@60 */ + { 0x45,0x25, 83}, /* 0x6c 1280x800 */ + { 0x70,0x28, 90}, /* 0x6d 1152x864@60 */ + { 0x15,0xe1, 20}, /* 0x6e 640x400@60 (fake, not actually used) */ + { 0x5f,0xc6, 33}, /* 0x6f 720x576@60 */ + { 0x37,0x5a, 10}, /* 0x70 320x200@60 (fake, not actually used) */ + { 0x2b,0xc2, 35} /* 0x71 768@576@60 */ +}; + +void SiSUSBRegInit(struct SiS_Private *SiS_Pr, unsigned long BaseAddr); +unsigned short SiSUSB_GetModeID(int HDisplay, int VDisplay, int Depth); +int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo); +int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo); + +extern int sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data); +extern int sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data); +extern int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, + u8 index, u8 data); +extern int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, + u8 index, u8 *data); +extern int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, + u8 idx, u8 myand, u8 myor); +extern int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, + u8 index, u8 myor); +extern int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, + u8 idx, u8 myand); + +#endif + diff --git a/drivers/usb/misc/sisusbvga/sisusb_struct.h b/drivers/usb/misc/sisusbvga/sisusb_struct.h new file mode 100644 index 00000000000..94edd4726c4 --- /dev/null +++ b/drivers/usb/misc/sisusbvga/sisusb_struct.h @@ -0,0 +1,169 @@ +/* + * General structure definitions for universal mode switching modules + * + * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria + * + * If distributed as part of the Linux kernel, the following license terms + * apply: + * + * * 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 named License, + * * or 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 + * + * Otherwise, the following license terms apply: + * + * * Redistribution and use in source and binary forms, with or without + * * modification, are permitted provided that the following conditions + * * are met: + * * 1) Redistributions of source code must retain the above copyright + * * notice, this list of conditions and the following disclaimer. + * * 2) Redistributions in binary form must reproduce the above copyright + * * notice, this list of conditions and the following disclaimer in the + * * documentation and/or other materials provided with the distribution. + * * 3) The name of the author may not be used to endorse or promote products + * * derived from this software without specific prior written permission. + * * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Thomas Winischhofer <thomas@winischhofer.net> + * + */ + +#ifndef _SISUSB_STRUCT_H_ +#define _SISUSB_STRUCT_H_ + +struct SiS_St { + unsigned char St_ModeID; + unsigned short St_ModeFlag; + unsigned char St_StTableIndex; + unsigned char St_CRT2CRTC; + unsigned char St_ResInfo; + unsigned char VB_StTVFlickerIndex; + unsigned char VB_StTVEdgeIndex; + unsigned char VB_StTVYFilterIndex; + unsigned char St_PDC; +}; + +struct SiS_StandTable +{ + unsigned char CRT_COLS; + unsigned char ROWS; + unsigned char CHAR_HEIGHT; + unsigned short CRT_LEN; + unsigned char SR[4]; + unsigned char MISC; + unsigned char CRTC[0x19]; + unsigned char ATTR[0x14]; + unsigned char GRC[9]; +}; + +struct SiS_StResInfo_S { + unsigned short HTotal; + unsigned short VTotal; +}; + +struct SiS_Ext +{ + unsigned char Ext_ModeID; + unsigned short Ext_ModeFlag; + unsigned short Ext_VESAID; + unsigned char Ext_RESINFO; + unsigned char VB_ExtTVFlickerIndex; + unsigned char VB_ExtTVEdgeIndex; + unsigned char VB_ExtTVYFilterIndex; + unsigned char VB_ExtTVYFilterIndexROM661; + unsigned char REFindex; + char ROMMODEIDX661; +}; + +struct SiS_Ext2 +{ + unsigned short Ext_InfoFlag; + unsigned char Ext_CRT1CRTC; + unsigned char Ext_CRTVCLK; + unsigned char Ext_CRT2CRTC; + unsigned char Ext_CRT2CRTC_NS; + unsigned char ModeID; + unsigned short XRes; + unsigned short YRes; + unsigned char Ext_PDC; + unsigned char Ext_FakeCRT2CRTC; + unsigned char Ext_FakeCRT2Clk; +}; + +struct SiS_CRT1Table +{ + unsigned char CR[17]; +}; + +struct SiS_VCLKData +{ + unsigned char SR2B,SR2C; + unsigned short CLOCK; +}; + +struct SiS_ModeResInfo +{ + unsigned short HTotal; + unsigned short VTotal; + unsigned char XChar; + unsigned char YChar; +}; + +struct SiS_Private +{ + void *sisusb; + + unsigned long IOAddress; + + unsigned long SiS_P3c4; + unsigned long SiS_P3d4; + unsigned long SiS_P3c0; + unsigned long SiS_P3ce; + unsigned long SiS_P3c2; + unsigned long SiS_P3ca; + unsigned long SiS_P3c6; + unsigned long SiS_P3c7; + unsigned long SiS_P3c8; + unsigned long SiS_P3c9; + unsigned long SiS_P3cb; + unsigned long SiS_P3cc; + unsigned long SiS_P3cd; + unsigned long SiS_P3da; + unsigned long SiS_Part1Port; + + unsigned char SiS_MyCR63; + unsigned short SiS_CRT1Mode; + unsigned short SiS_ModeType; + unsigned short SiS_SetFlag; + + const struct SiS_StandTable *SiS_StandTable; + const struct SiS_St *SiS_SModeIDTable; + const struct SiS_Ext *SiS_EModeIDTable; + const struct SiS_Ext2 *SiS_RefIndex; + const struct SiS_CRT1Table *SiS_CRT1Table; + struct SiS_VCLKData *SiS_VCLKData; + const struct SiS_ModeResInfo *SiS_ModeResInfo; +}; + +#endif + diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index faa74436de5..03fb70ef2eb 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -3,8 +3,8 @@ /* * uss720.c -- USS720 USB Parport Cable. * - * Copyright (C) 1999 - * Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1999, 2005 + * Thomas Sailer (t.sailer@alumni.ethz.ch) * * 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 @@ -23,103 +23,240 @@ * Based on parport_pc.c * * History: - * 0.1 04.08.99 Created - * 0.2 07.08.99 Some fixes mainly suggested by Tim Waugh - * Interrupt handling currently disabled because - * usb_request_irq crashes somewhere within ohci.c - * for no apparent reason (that is for me, anyway) - * ECP currently untested - * 0.3 10.08.99 fixing merge errors - * 0.4 13.08.99 Added Vendor/Product ID of Brad Hard's cable - * 0.5 20.09.99 usb_control_msg wrapper used - * Nov01.00 usb_device_table support by Adam J. Richter - * 08.04.01 Identify version on module load. gb + * 0.1 04.08.1999 Created + * 0.2 07.08.1999 Some fixes mainly suggested by Tim Waugh + * Interrupt handling currently disabled because + * usb_request_irq crashes somewhere within ohci.c + * for no apparent reason (that is for me, anyway) + * ECP currently untested + * 0.3 10.08.1999 fixing merge errors + * 0.4 13.08.1999 Added Vendor/Product ID of Brad Hard's cable + * 0.5 20.09.1999 usb_control_msg wrapper used + * Nov01.2000 usb_device_table support by Adam J. Richter + * 08.04.2001 Identify version on module load. gb + * 0.6 02.09.2005 Fix "scheduling in interrupt" problem by making save/restore + * context asynchronous * */ /*****************************************************************************/ +#define DEBUG + #include <linux/module.h> #include <linux/socket.h> #include <linux/parport.h> #include <linux/init.h> #include <linux/usb.h> #include <linux/delay.h> +#include <linux/completion.h> +#include <linux/kref.h> /* * Version Information */ -#define DRIVER_VERSION "v0.5" -#define DRIVER_AUTHOR "Thomas M. Sailer, sailer@ife.ee.ethz.ch" +#define DRIVER_VERSION "v0.6" +#define DRIVER_AUTHOR "Thomas M. Sailer, t.sailer@alumni.ethz.ch" #define DRIVER_DESC "USB Parport Cable driver for Cables using the Lucent Technologies USS720 Chip" /* --------------------------------------------------------------------- */ struct parport_uss720_private { struct usb_device *usbdev; - void *irqhandle; - unsigned int irqpipe; - unsigned char reg[7]; /* USB registers */ + struct parport *pp; + struct kref ref_count; + __u8 reg[7]; /* USB registers */ + struct list_head asynclist; + spinlock_t asynclock; +}; + +struct uss720_async_request { + struct parport_uss720_private *priv; + struct kref ref_count; + struct list_head asynclist; + struct completion compl; + struct urb *urb; + struct usb_ctrlrequest dr; + __u8 reg[7]; }; /* --------------------------------------------------------------------- */ -static int get_1284_register(struct parport *pp, unsigned char reg, unsigned char *val) +static void destroy_priv(struct kref *kref) { - struct parport_uss720_private *priv = pp->private_data; - struct usb_device *usbdev = priv->usbdev; - static const unsigned char regindex[9] = { - 4, 0, 1, 5, 5, 0, 2, 3, 6 - }; - int ret; + struct parport_uss720_private *priv = container_of(kref, struct parport_uss720_private, ref_count); - if (!usbdev) - return -1; - ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev,0), 3, 0xc0, ((unsigned int)reg) << 8, 0, priv->reg, 7, 1000); - if (ret != 7) { - printk(KERN_DEBUG "uss720: get_1284_register(%d) failed, status 0x%x expected 7\n", - (unsigned int)reg, ret); - ret = -1; - } else { + usb_put_dev(priv->usbdev); + kfree(priv); + dbg("destroying priv datastructure"); +} + +static void destroy_async(struct kref *kref) +{ + struct uss720_async_request *rq = container_of(kref, struct uss720_async_request, ref_count); + struct parport_uss720_private *priv = rq->priv; + unsigned long flags; + + if (likely(rq->urb)) + usb_free_urb(rq->urb); + spin_lock_irqsave(&priv->asynclock, flags); + list_del_init(&rq->asynclist); + spin_unlock_irqrestore(&priv->asynclock, flags); + kfree(rq); + kref_put(&priv->ref_count, destroy_priv); +} + +/* --------------------------------------------------------------------- */ + +static void async_complete(struct urb *urb, struct pt_regs *ptregs) +{ + struct uss720_async_request *rq; + struct parport *pp; + struct parport_uss720_private *priv; + + rq = urb->context; + priv = rq->priv; + pp = priv->pp; + if (urb->status) { + err("async_complete: urb error %d", urb->status); + } else if (rq->dr.bRequest == 3) { + memcpy(priv->reg, rq->reg, sizeof(priv->reg)); #if 0 - printk(KERN_DEBUG "uss720: get_1284_register(%d) return %02x %02x %02x %02x %02x %02x %02x\n", - (unsigned int)reg, (unsigned int)priv->reg[0], (unsigned int)priv->reg[1], - (unsigned int)priv->reg[2], (unsigned int)priv->reg[3], (unsigned int)priv->reg[4], - (unsigned int)priv->reg[5], (unsigned int)priv->reg[6]); + dbg("async_complete regs %02x %02x %02x %02x %02x %02x %02x", + (unsigned int)priv->reg[0], (unsigned int)priv->reg[1], (unsigned int)priv->reg[2], + (unsigned int)priv->reg[3], (unsigned int)priv->reg[4], (unsigned int)priv->reg[5], + (unsigned int)priv->reg[6]); #endif /* if nAck interrupts are enabled and we have an interrupt, call the interrupt procedure */ - if (priv->reg[2] & priv->reg[1] & 0x10) + if (rq->reg[2] & rq->reg[1] & 0x10 && pp) parport_generic_irq(0, pp, NULL); - ret = 0; } - if (val) - *val = priv->reg[(reg >= 9) ? 0 : regindex[reg]]; - return ret; + complete(&rq->compl); + kref_put(&rq->ref_count, destroy_async); } -static int set_1284_register(struct parport *pp, unsigned char reg, unsigned char val) +static struct uss720_async_request *submit_async_request(struct parport_uss720_private *priv, + __u8 request, __u8 requesttype, __u16 value, __u16 index, + unsigned int mem_flags) { - struct parport_uss720_private *priv = pp->private_data; - struct usb_device *usbdev = priv->usbdev; + struct usb_device *usbdev; + struct uss720_async_request *rq; + unsigned long flags; int ret; + if (!priv) + return NULL; + usbdev = priv->usbdev; if (!usbdev) - return -1; - ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev,0), 4, 0x40, (((unsigned int)reg) << 8) | val, 0, NULL, 0, 1000); - if (ret) { - printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x) failed, status 0x%x\n", - (unsigned int)reg, (unsigned int)val, ret); - } else { -#if 0 - printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x)\n", - (unsigned int)reg, (unsigned int)val); -#endif + return NULL; + rq = kmalloc(sizeof(struct uss720_async_request), mem_flags); + if (!rq) { + err("submit_async_request out of memory"); + return NULL; + } + kref_init(&rq->ref_count); + INIT_LIST_HEAD(&rq->asynclist); + init_completion(&rq->compl); + kref_get(&priv->ref_count); + rq->priv = priv; + rq->urb = usb_alloc_urb(0, mem_flags); + if (!rq->urb) { + kref_put(&rq->ref_count, destroy_async); + err("submit_async_request out of memory"); + return NULL; + } + rq->dr.bRequestType = requesttype; + rq->dr.bRequest = request; + rq->dr.wValue = cpu_to_le16(value); + rq->dr.wIndex = cpu_to_le16(index); + rq->dr.wLength = cpu_to_le16((request == 3) ? sizeof(rq->reg) : 0); + usb_fill_control_urb(rq->urb, usbdev, (requesttype & 0x80) ? usb_rcvctrlpipe(usbdev, 0) : usb_sndctrlpipe(usbdev, 0), + (unsigned char *)&rq->dr, + (request == 3) ? rq->reg : NULL, (request == 3) ? sizeof(rq->reg) : 0, async_complete, rq); + /* rq->urb->transfer_flags |= URB_ASYNC_UNLINK; */ + spin_lock_irqsave(&priv->asynclock, flags); + list_add_tail(&rq->asynclist, &priv->asynclist); + spin_unlock_irqrestore(&priv->asynclock, flags); + ret = usb_submit_urb(rq->urb, mem_flags); + if (!ret) { + kref_get(&rq->ref_count); + return rq; } + kref_put(&rq->ref_count, destroy_async); + err("submit_async_request submit_urb failed with %d", ret); + return NULL; +} + +static unsigned int kill_all_async_requests_priv(struct parport_uss720_private *priv) +{ + struct uss720_async_request *rq; + unsigned long flags; + unsigned int ret = 0; + + spin_lock_irqsave(&priv->asynclock, flags); + list_for_each_entry(rq, &priv->asynclist, asynclist) { + usb_unlink_urb(rq->urb); + ret++; + } + spin_unlock_irqrestore(&priv->asynclock, flags); return ret; } /* --------------------------------------------------------------------- */ +static int get_1284_register(struct parport *pp, unsigned char reg, unsigned char *val, unsigned int mem_flags) +{ + struct parport_uss720_private *priv; + struct uss720_async_request *rq; + static const unsigned char regindex[9] = { + 4, 0, 1, 5, 5, 0, 2, 3, 6 + }; + int ret; + + if (!pp) + return -EIO; + priv = pp->private_data; + rq = submit_async_request(priv, 3, 0xc0, ((unsigned int)reg) << 8, 0, mem_flags); + if (!rq) { + err("get_1284_register(%u) failed", (unsigned int)reg); + return -EIO; + } + if (!val) { + kref_put(&rq->ref_count, destroy_async); + return 0; + } + if (wait_for_completion_timeout(&rq->compl, HZ)) { + ret = rq->urb->status; + *val = priv->reg[(reg >= 9) ? 0 : regindex[reg]]; + if (ret) + warn("get_1284_register: usb error %d", ret); + kref_put(&rq->ref_count, destroy_async); + return ret; + } + warn("get_1284_register timeout"); + kill_all_async_requests_priv(priv); + return -EIO; +} + +static int set_1284_register(struct parport *pp, unsigned char reg, unsigned char val, unsigned int mem_flags) +{ + struct parport_uss720_private *priv; + struct uss720_async_request *rq; + + if (!pp) + return -EIO; + priv = pp->private_data; + rq = submit_async_request(priv, 4, 0x40, (((unsigned int)reg) << 8) | val, 0, mem_flags); + if (!rq) { + err("set_1284_register(%u,%u) failed", (unsigned int)reg, (unsigned int)val); + return -EIO; + } + kref_put(&rq->ref_count, destroy_async); + return 0; +} + +/* --------------------------------------------------------------------- */ + /* ECR modes */ #define ECR_SPP 00 #define ECR_PS2 01 @@ -132,8 +269,9 @@ static int change_mode(struct parport *pp, int m) { struct parport_uss720_private *priv = pp->private_data; int mode; + __u8 reg; - if (get_1284_register(pp, 6, NULL)) + if (get_1284_register(pp, 6, ®, GFP_KERNEL)) return -EIO; /* Bits <7:5> contain the mode. */ mode = (priv->reg[2] >> 5) & 0x7; @@ -153,7 +291,7 @@ static int change_mode(struct parport *pp, int m) case ECR_ECP: /* ECP Parallel Port mode */ /* Poll slowly. */ for (;;) { - if (get_1284_register(pp, 6, NULL)) + if (get_1284_register(pp, 6, ®, GFP_KERNEL)) return -EIO; if (priv->reg[2] & 0x01) break; @@ -167,7 +305,9 @@ static int change_mode(struct parport *pp, int m) } } /* Set the mode. */ - if (set_1284_register(pp, 6, m << 5)) + if (set_1284_register(pp, 6, m << 5, GFP_KERNEL)) + return -EIO; + if (get_1284_register(pp, 6, ®, GFP_KERNEL)) return -EIO; return 0; } @@ -179,7 +319,7 @@ static int clear_epp_timeout(struct parport *pp) { unsigned char stat; - if (get_1284_register(pp, 1, &stat)) + if (get_1284_register(pp, 1, &stat, GFP_KERNEL)) return 1; return stat & 1; } @@ -205,14 +345,14 @@ static int uss720_irq(int usbstatus, void *buffer, int len, void *dev_id) static void parport_uss720_write_data(struct parport *pp, unsigned char d) { - set_1284_register(pp, 0, d); + set_1284_register(pp, 0, d, GFP_KERNEL); } static unsigned char parport_uss720_read_data(struct parport *pp) { unsigned char ret; - if (get_1284_register(pp, 0, &ret)) + if (get_1284_register(pp, 0, &ret, GFP_KERNEL)) return 0; return ret; } @@ -222,7 +362,7 @@ static void parport_uss720_write_control(struct parport *pp, unsigned char d) struct parport_uss720_private *priv = pp->private_data; d = (d & 0xf) | (priv->reg[1] & 0xf0); - if (set_1284_register(pp, 2, d)) + if (set_1284_register(pp, 2, d, GFP_KERNEL)) return; priv->reg[1] = d; } @@ -241,7 +381,7 @@ static unsigned char parport_uss720_frob_control(struct parport *pp, unsigned ch mask &= 0x0f; val &= 0x0f; d = (priv->reg[1] & (~mask)) ^ val; - if (set_1284_register(pp, 2, d)) + if (set_1284_register(pp, 2, d, GFP_KERNEL)) return 0; priv->reg[1] = d; return d & 0xf; @@ -251,7 +391,7 @@ static unsigned char parport_uss720_read_status(struct parport *pp) { unsigned char ret; - if (get_1284_register(pp, 1, &ret)) + if (get_1284_register(pp, 1, &ret, GFP_KERNEL)) return 0; return ret & 0xf8; } @@ -262,7 +402,7 @@ static void parport_uss720_disable_irq(struct parport *pp) unsigned char d; d = priv->reg[1] & ~0x10; - if (set_1284_register(pp, 2, d)) + if (set_1284_register(pp, 2, d, GFP_KERNEL)) return; priv->reg[1] = d; } @@ -273,7 +413,7 @@ static void parport_uss720_enable_irq(struct parport *pp) unsigned char d; d = priv->reg[1] | 0x10; - if (set_1284_register(pp, 2, d)) + if (set_1284_register(pp, 2, d, GFP_KERNEL)) return; priv->reg[1] = d; } @@ -284,7 +424,7 @@ static void parport_uss720_data_forward (struct parport *pp) unsigned char d; d = priv->reg[1] & ~0x20; - if (set_1284_register(pp, 2, d)) + if (set_1284_register(pp, 2, d, GFP_KERNEL)) return; priv->reg[1] = d; } @@ -295,7 +435,7 @@ static void parport_uss720_data_reverse (struct parport *pp) unsigned char d; d = priv->reg[1] | 0x20; - if (set_1284_register(pp, 2, d)) + if (set_1284_register(pp, 2, d, GFP_KERNEL)) return; priv->reg[1] = d; } @@ -310,17 +450,23 @@ static void parport_uss720_save_state(struct parport *pp, struct parport_state * { struct parport_uss720_private *priv = pp->private_data; - if (get_1284_register(pp, 2, NULL)) +#if 0 + if (get_1284_register(pp, 2, NULL, GFP_ATOMIC)) return; +#endif s->u.pc.ctr = priv->reg[1]; s->u.pc.ecr = priv->reg[2]; } static void parport_uss720_restore_state(struct parport *pp, struct parport_state *s) { - set_1284_register(pp, 2, s->u.pc.ctr); - set_1284_register(pp, 6, s->u.pc.ecr); - get_1284_register(pp, 2, NULL); + struct parport_uss720_private *priv = pp->private_data; + + set_1284_register(pp, 2, s->u.pc.ctr, GFP_ATOMIC); + set_1284_register(pp, 6, s->u.pc.ecr, GFP_ATOMIC); + get_1284_register(pp, 2, NULL, GFP_ATOMIC); + priv->reg[1] = s->u.pc.ctr; + priv->reg[2] = s->u.pc.ecr; } static size_t parport_uss720_epp_read_data(struct parport *pp, void *buf, size_t length, int flags) @@ -331,7 +477,7 @@ static size_t parport_uss720_epp_read_data(struct parport *pp, void *buf, size_t if (change_mode(pp, ECR_EPP)) return 0; for (; got < length; got++) { - if (get_1284_register(pp, 4, (char *)buf)) + if (get_1284_register(pp, 4, (char *)buf, GFP_KERNEL)) break; buf++; if (priv->reg[0] & 0x01) { @@ -352,10 +498,10 @@ static size_t parport_uss720_epp_write_data(struct parport *pp, const void *buf, if (change_mode(pp, ECR_EPP)) return 0; for (; written < length; written++) { - if (set_1284_register(pp, 4, (char *)buf)) + if (set_1284_register(pp, 4, (char *)buf, GFP_KERNEL)) break; ((char*)buf)++; - if (get_1284_register(pp, 1, NULL)) + if (get_1284_register(pp, 1, NULL, GFP_KERNEL)) break; if (priv->reg[0] & 0x01) { clear_epp_timeout(pp); @@ -390,7 +536,7 @@ static size_t parport_uss720_epp_read_addr(struct parport *pp, void *buf, size_t if (change_mode(pp, ECR_EPP)) return 0; for (; got < length; got++) { - if (get_1284_register(pp, 3, (char *)buf)) + if (get_1284_register(pp, 3, (char *)buf, GFP_KERNEL)) break; buf++; if (priv->reg[0] & 0x01) { @@ -410,10 +556,10 @@ static size_t parport_uss720_epp_write_addr(struct parport *pp, const void *buf, if (change_mode(pp, ECR_EPP)) return 0; for (; written < length; written++) { - if (set_1284_register(pp, 3, *(char *)buf)) + if (set_1284_register(pp, 3, *(char *)buf, GFP_KERNEL)) break; buf++; - if (get_1284_register(pp, 1, NULL)) + if (get_1284_register(pp, 1, NULL, GFP_KERNEL)) break; if (priv->reg[0] & 0x01) { clear_epp_timeout(pp); @@ -467,7 +613,7 @@ static size_t parport_uss720_ecp_write_addr(struct parport *pp, const void *buff if (change_mode(pp, ECR_ECP)) return 0; for (; written < len; written++) { - if (set_1284_register(pp, 5, *(char *)buffer)) + if (set_1284_register(pp, 5, *(char *)buffer, GFP_KERNEL)) break; buffer++; } @@ -536,93 +682,91 @@ static struct parport_operations parport_uss720_ops = static int uss720_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_device *usbdev = interface_to_usbdev(intf); + struct usb_device *usbdev = usb_get_dev(interface_to_usbdev(intf)); struct usb_host_interface *interface; struct usb_host_endpoint *endpoint; struct parport_uss720_private *priv; struct parport *pp; + unsigned char reg; int i; - printk(KERN_DEBUG "uss720: probe: vendor id 0x%x, device id 0x%x\n", - le16_to_cpu(usbdev->descriptor.idVendor), - le16_to_cpu(usbdev->descriptor.idProduct)); + dbg("probe: vendor id 0x%x, device id 0x%x\n", + le16_to_cpu(usbdev->descriptor.idVendor), + le16_to_cpu(usbdev->descriptor.idProduct)); /* our known interfaces have 3 alternate settings */ - if (intf->num_altsetting != 3) + if (intf->num_altsetting != 3) { + usb_put_dev(usbdev); return -ENODEV; - + } i = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2); - printk(KERN_DEBUG "uss720: set inteface result %d\n", i); + dbg("set inteface result %d", i); interface = intf->cur_altsetting; /* * Allocate parport interface */ - printk(KERN_INFO "uss720: (C) 1999 by Thomas Sailer, <sailer@ife.ee.ethz.ch>\n"); - - if (!(priv = kmalloc(sizeof(struct parport_uss720_private), GFP_KERNEL))) + if (!(priv = kcalloc(sizeof(struct parport_uss720_private), 1, GFP_KERNEL))) { + usb_put_dev(usbdev); return -ENOMEM; + } + priv->pp = NULL; + priv->usbdev = usbdev; + kref_init(&priv->ref_count); + spin_lock_init(&priv->asynclock); + INIT_LIST_HEAD(&priv->asynclist); if (!(pp = parport_register_port(0, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &parport_uss720_ops))) { - printk(KERN_WARNING "usb-uss720: could not register parport\n"); + warn("could not register parport"); goto probe_abort; } + priv->pp = pp; pp->private_data = priv; - priv->usbdev = usbdev; pp->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP | PARPORT_MODE_ECP | PARPORT_MODE_COMPAT; /* set the USS720 control register to manual mode, no ECP compression, enable all ints */ - set_1284_register(pp, 7, 0x00); - set_1284_register(pp, 6, 0x30); /* PS/2 mode */ - set_1284_register(pp, 2, 0x0c); + set_1284_register(pp, 7, 0x00, GFP_KERNEL); + set_1284_register(pp, 6, 0x30, GFP_KERNEL); /* PS/2 mode */ + set_1284_register(pp, 2, 0x0c, GFP_KERNEL); /* debugging */ - get_1284_register(pp, 0, NULL); - printk("uss720: reg: %02x %02x %02x %02x %02x %02x %02x\n", - priv->reg[0], priv->reg[1], priv->reg[2], priv->reg[3], priv->reg[4], priv->reg[5], priv->reg[6]); + get_1284_register(pp, 0, ®, GFP_KERNEL); + dbg("reg: %02x %02x %02x %02x %02x %02x %02x", + priv->reg[0], priv->reg[1], priv->reg[2], priv->reg[3], priv->reg[4], priv->reg[5], priv->reg[6]); endpoint = &interface->endpoint[2]; - printk(KERN_DEBUG "uss720: epaddr %d interval %d\n", endpoint->desc.bEndpointAddress, endpoint->desc.bInterval); -#if 0 - priv->irqpipe = usb_rcvctrlpipe(usbdev, endpoint->bEndpointAddress); - i = usb_request_irq(usbdev, priv->irqpipe, - uss720_irq, endpoint->bInterval, - pp, &priv->irqhandle); - if (i) { - printk (KERN_WARNING "usb-uss720: usb_request_irq failed (0x%x)\n", i); - goto probe_abort_port; - } -#endif + dbg("epaddr %d interval %d", endpoint->desc.bEndpointAddress, endpoint->desc.bInterval); parport_announce_port(pp); - usb_set_intfdata (intf, pp); + usb_set_intfdata(intf, pp); return 0; -#if 0 -probe_abort_port: - parport_put_port(pp); -#endif probe_abort: - kfree(priv); + kill_all_async_requests_priv(priv); + kref_put(&priv->ref_count, destroy_priv); return -ENODEV; } static void uss720_disconnect(struct usb_interface *intf) { - struct parport *pp = usb_get_intfdata (intf); + struct parport *pp = usb_get_intfdata(intf); struct parport_uss720_private *priv; + struct usb_device *usbdev; - usb_set_intfdata (intf, NULL); + dbg("disconnect"); + usb_set_intfdata(intf, NULL); if (pp) { priv = pp->private_data; - parport_remove_port(pp); -#if 0 - usb_release_irq(usbdev, priv->irqhandle, priv->irqpipe); -#endif + usbdev = priv->usbdev; priv->usbdev = NULL; + priv->pp = NULL; + dbg("parport_remove_port"); + parport_remove_port(pp); parport_put_port(pp); - kfree(priv); + kill_all_async_requests_priv(priv); + kref_put(&priv->ref_count, destroy_priv); } + dbg("disconnect done"); } /* table of cables that work through this driver */ @@ -647,8 +791,8 @@ static struct usb_driver uss720_driver = { /* --------------------------------------------------------------------- */ -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); static int __init uss720_init(void) @@ -659,6 +803,9 @@ static int __init uss720_init(void) goto out; info(DRIVER_VERSION ":" DRIVER_DESC); + info("NOTE: this is a special purpose driver to allow nonstandard"); + info("protocols (eg. bitbang) over USS720 usb to parallel cables"); + info("If you just want to connect to a printer, use usblp instead"); out: return retval; } diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 417464dea9f..17d0190ef64 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -79,7 +79,7 @@ static inline char mon_text_get_setup(struct mon_event_text *ep, return '-'; if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP) - return 'D'; + return mon_dmapeek(ep->setup, urb->setup_dma, SETUP_MAX); if (urb->setup_packet == NULL) return 'Z'; /* '0' would be not as pretty. */ diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index 4ace9964fc6..97c78c21e8d 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -32,7 +32,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.04" +#define DRIVER_VERSION "v0.05" #define DRIVER_DESC "Silicon Labs CP2101/CP2102 RS232 serial adaptor driver" /* @@ -54,8 +54,11 @@ static void cp2101_shutdown(struct usb_serial*); static int debug; static struct usb_device_id id_table [] = { + { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */ + { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */ + { USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */ { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */ { } /* Terminating Entry */ }; diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 05c44ae3ed3..9ee1aaff2fc 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -610,8 +610,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) timeout = max((HZ*2560)/bps,HZ/10); else timeout = 2*HZ; - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(timeout); + schedule_timeout_interruptible(timeout); dbg("%s - stopping urbs", __FUNCTION__); usb_kill_urb (port->interrupt_in_urb); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 0a6e8b474b1..4e434cb10bb 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -914,7 +914,7 @@ static void ftdi_determine_type(struct usb_serial_port *port) unsigned interfaces; /* Assume it is not the original SIO device for now. */ - priv->baud_base = 48000000 / 16; + priv->baud_base = 48000000 / 2; priv->write_offset = 0; version = le16_to_cpu(udev->descriptor.bcdDevice); diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 461474176cf..3cf245bdda5 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -95,6 +95,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) }, { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) }, { USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) }, + { USB_DEVICE( NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID ) }, { } /* Terminating entry */ }; @@ -652,8 +653,7 @@ static void pl2303_close (struct usb_serial_port *port, struct file *filp) timeout = max((HZ*2560)/bps,HZ/10); else timeout = 2*HZ; - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(timeout); + schedule_timeout_interruptible(timeout); /* shutdown our urbs */ dbg("%s - shutting down urbs", __FUNCTION__); diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index b734c4003c5..7be9644f5a0 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -58,3 +58,7 @@ #define SYNTECH_VENDOR_ID 0x0745 #define SYNTECH_PRODUCT_ID 0x0001 + +/* Nokia CA-42 Cable */ +#define NOKIA_CA42_VENDOR_ID 0x078b +#define NOKIA_CA42_PRODUCT_ID 0x1234 diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index d34dc9f417f..4837524eada 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -227,42 +227,42 @@ static int queuecommand(struct scsi_cmnd *srb, ***********************************************************************/ /* Command timeout and abort */ -/* This is always called with scsi_lock(host) held */ static int command_abort(struct scsi_cmnd *srb) { struct us_data *us = host_to_us(srb->device->host); US_DEBUGP("%s called\n", __FUNCTION__); + /* us->srb together with the TIMED_OUT, RESETTING, and ABORTING + * bits are protected by the host lock. */ + scsi_lock(us_to_host(us)); + /* Is this command still active? */ if (us->srb != srb) { + scsi_unlock(us_to_host(us)); US_DEBUGP ("-- nothing to abort\n"); return FAILED; } /* Set the TIMED_OUT bit. Also set the ABORTING bit, but only if * a device reset isn't already in progress (to avoid interfering - * with the reset). To prevent races with auto-reset, we must - * stop any ongoing USB transfers while still holding the host - * lock. */ + * with the reset). Note that we must retain the host lock while + * calling usb_stor_stop_transport(); otherwise it might interfere + * with an auto-reset that begins as soon as we release the lock. */ set_bit(US_FLIDX_TIMED_OUT, &us->flags); if (!test_bit(US_FLIDX_RESETTING, &us->flags)) { set_bit(US_FLIDX_ABORTING, &us->flags); usb_stor_stop_transport(us); } + scsi_unlock(us_to_host(us)); /* Wait for the aborted command to finish */ wait_for_completion(&us->notify); - - /* Reacquire the lock and allow USB transfers to resume */ - clear_bit(US_FLIDX_ABORTING, &us->flags); - clear_bit(US_FLIDX_TIMED_OUT, &us->flags); return SUCCESS; } /* This invokes the transport reset mechanism to reset the state of the * device */ -/* This is always called with scsi_lock(host) held */ static int device_reset(struct scsi_cmnd *srb) { struct us_data *us = host_to_us(srb->device->host); @@ -279,7 +279,6 @@ static int device_reset(struct scsi_cmnd *srb) } /* Simulate a SCSI bus reset by resetting the device's USB port. */ -/* This is always called with scsi_lock(host) held */ static int bus_reset(struct scsi_cmnd *srb) { struct us_data *us = host_to_us(srb->device->host); @@ -291,7 +290,6 @@ static int bus_reset(struct scsi_cmnd *srb) result = usb_stor_port_reset(us); up(&(us->dev_semaphore)); - /* lock the host for the return */ return result < 0 ? FAILED : SUCCESS; } diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index ad0cfd7a782..b79dad1b598 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -86,6 +86,16 @@ UNUSUAL_DEV( 0x040d, 0x6205, 0x0003, 0x0003, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), +/* Reported by Sebastian Kapfer <sebastian_kapfer@gmx.net> + * and Olaf Hering <olh@suse.de> (different bcd's, same vendor/product) + * for USB floppies that need the SINGLE_LUN enforcement. + */ +UNUSUAL_DEV( 0x0409, 0x0040, 0x0000, 0x9999, + "NEC", + "NEC USB UF000x", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + /* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au> * Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message * always fails and confuses drive. @@ -96,6 +106,13 @@ UNUSUAL_DEV( 0x0411, 0x001c, 0x0113, 0x0113, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), +/* Reported by Olaf Hering <olh@suse.de> from novell bug #105878 */ +UNUSUAL_DEV( 0x0424, 0x0fdc, 0x0210, 0x0210, + "SMSC", + "FDC GOLD-2.30", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + #ifdef CONFIG_USB_STORAGE_DPCM UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, "Microtech", @@ -103,6 +120,24 @@ UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ), #endif +/* + * Pete Zaitcev <zaitcev@yahoo.com>, from Patrick C. F. Ernzer, bz#162559. + * The key does not actually break, but it returns zero sense which + * makes our SCSI stack to print confusing messages. + */ +UNUSUAL_DEV( 0x0457, 0x0150, 0x0100, 0x0100, + "USBest Technology", /* sold by Transcend */ + "USB Mass Storage Device", + US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ), + +/* Patch submitted by Daniel Drake <dsd@gentoo.org> + * Device reports nonsense bInterfaceProtocol 6 when connected over USB2 */ +UNUSUAL_DEV( 0x0451, 0x5416, 0x0100, 0x0100, + "Neuros Audio", + "USB 2.0 HD 2.5", + US_SC_DEVICE, US_PR_BULK, NULL, + US_FL_NEED_OVERRIDE ), + /* Patch submitted by Philipp Friedrich <philipp@void.at> */ UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100, "Kyocera", diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index cb4c770baf3..f9a9bfa1aef 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -392,11 +392,16 @@ SkipForAbort: /* If an abort request was received we need to signal that * the abort has finished. The proper test for this is * the TIMED_OUT flag, not srb->result == DID_ABORT, because - * a timeout/abort request might be received after all the - * USB processing was complete. */ - if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) + * the timeout might have occurred after the command had + * already completed with a different result code. */ + if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { complete(&(us->notify)); + /* Allow USB transfers to resume */ + clear_bit(US_FLIDX_ABORTING, &us->flags); + clear_bit(US_FLIDX_TIMED_OUT, &us->flags); + } + /* finished working on this command */ us->srb = NULL; scsi_unlock(host); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 615874e03ce..31ee13eef7a 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -753,7 +753,8 @@ config FB_I810_GTF config FB_I810_I2C bool "Enable DDC Support" - depends on FB_I810 && I2C && FB_I810_GTF + depends on FB_I810 && FB_I810_GTF + select I2C select I2C_ALGOBIT help diff --git a/drivers/video/backlight/corgi_bl.c b/drivers/video/backlight/corgi_bl.c index 353cb3f73cf..a3281767855 100644 --- a/drivers/video/backlight/corgi_bl.c +++ b/drivers/video/backlight/corgi_bl.c @@ -43,18 +43,10 @@ static void corgibl_send_intensity(int intensity) intensity &= CORGI_LIMIT_MASK; } - /* Skip 0x20 as it will blank the display */ - if (intensity >= 0x20) - intensity++; - spin_lock_irqsave(&bl_lock, flags); - /* Bits 0-4 are accessed via the SSP interface */ - corgi_ssp_blduty_set(intensity & 0x1f); - /* Bit 5 is via SCOOP */ - if (intensity & 0x0020) - set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_BACKLIGHT_CONT); - else - reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_BACKLIGHT_CONT); + + corgibl_mach_set_intensity(intensity); + spin_unlock_irqrestore(&bl_lock, flags); } @@ -113,8 +105,8 @@ static int corgibl_get_power(struct backlight_device *bd) static int corgibl_set_intensity(struct backlight_device *bd, int intensity) { - if (intensity > CORGI_MAX_INTENSITY) - intensity = CORGI_MAX_INTENSITY; + if (intensity > corgibl_data.max_brightness) + intensity = corgibl_data.max_brightness; corgibl_send_intensity(intensity); current_intensity=intensity; return 0; @@ -141,7 +133,6 @@ static struct backlight_properties corgibl_data = { .owner = THIS_MODULE, .get_power = corgibl_get_power, .set_power = corgibl_set_power, - .max_brightness = CORGI_MAX_INTENSITY, .get_brightness = corgibl_get_intensity, .set_brightness = corgibl_set_intensity, }; @@ -150,12 +141,18 @@ static struct backlight_device *corgi_backlight_device; static int __init corgibl_probe(struct device *dev) { + struct corgibl_machinfo *machinfo = dev->platform_data; + + corgibl_data.max_brightness = machinfo->max_intensity; + corgibl_mach_set_intensity = machinfo->set_bl_intensity; + corgi_backlight_device = backlight_device_register ("corgi-bl", NULL, &corgibl_data); if (IS_ERR (corgi_backlight_device)) return PTR_ERR (corgi_backlight_device); corgibl_set_intensity(NULL, CORGI_DEFAULT_INTENSITY); + corgibl_limit_intensity(0); printk("Corgi Backlight Driver Initialized.\n"); return 0; diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 5fe182d6e4a..eb83a7874c7 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -137,7 +137,7 @@ config FONT_8x8 config FONT_8x16 bool "VGA 8x16 font" if FONTS - depends on FRAMEBUFFER_CONSOLE || SGI_NEWPORT_CONSOLE=y + depends on FRAMEBUFFER_CONSOLE || SGI_NEWPORT_CONSOLE=y || USB_SISUSBVGA_CON default y if !SPARC32 && !SPARC64 && !FONTS help This is the "high resolution" font for the VGA frame buffer (the one diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile index b562f6bb9d3..42c7b8dcd22 100644 --- a/drivers/video/console/Makefile +++ b/drivers/video/console/Makefile @@ -33,6 +33,10 @@ endif obj-$(CONFIG_FB_STI) += sticore.o font.o +ifeq ($(CONFIG_USB_SISUSBVGA_CON),y) +obj-$(CONFIG_USB_SISUSBVGA) += font.o +endif + # Targets that kbuild needs to know about targets := promcon_tbl.c diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 2e93224d2d5..0fc8bb499c3 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -767,7 +767,7 @@ static const char *fbcon_startup(void) const char *display_desc = "frame buffer device"; struct display *p = &fb_display[fg_console]; struct vc_data *vc = vc_cons[fg_console].d; - struct font_desc *font = NULL; + const struct font_desc *font = NULL; struct module *owner; struct fb_info *info = NULL; struct fbcon_ops *ops; @@ -841,7 +841,7 @@ static const char *fbcon_startup(void) info->var.yres); vc->vc_font.width = font->width; vc->vc_font.height = font->height; - vc->vc_font.data = p->fontdata = font->data; + vc->vc_font.data = (void *)(p->fontdata = font->data); vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */ } @@ -941,7 +941,7 @@ static void fbcon_init(struct vc_data *vc, int init) fb, copy the font from that console */ t = &fb_display[svc->vc_num]; if (!vc->vc_font.data) { - vc->vc_font.data = p->fontdata = t->fontdata; + vc->vc_font.data = (void *)(p->fontdata = t->fontdata); vc->vc_font.width = (*default_mode)->vc_font.width; vc->vc_font.height = (*default_mode)->vc_font.height; p->userfont = t->userfont; @@ -1188,7 +1188,7 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, return; t = &fb_display[svc->vc_num]; if (!vc->vc_font.data) { - vc->vc_font.data = p->fontdata = t->fontdata; + vc->vc_font.data = (void *)(p->fontdata = t->fontdata); vc->vc_font.width = (*default_mode)->vc_font.width; vc->vc_font.height = (*default_mode)->vc_font.height; p->userfont = t->userfont; @@ -1687,6 +1687,8 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, case SM_DOWN: if (count > vc->vc_rows) /* Maximum realistic size */ count = vc->vc_rows; + if (logo_shown >= 0) + goto redraw_down; switch (p->scrollmode) { case SCROLL_MOVE: ops->bmove(vc, info, t, 0, t + count, 0, @@ -2148,7 +2150,7 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) } static int fbcon_do_set_font(struct vc_data *vc, int w, int h, - u8 * data, int userfont) + const u8 * data, int userfont) { struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; @@ -2166,7 +2168,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, cnt = FNTCHARCNT(data); else cnt = 256; - vc->vc_font.data = p->fontdata = data; + vc->vc_font.data = (void *)(p->fontdata = data); if ((p->userfont = userfont)) REFCOUNT(data)++; vc->vc_font.width = w; @@ -2323,7 +2325,7 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigne tmp->vc_font.width == w && !memcmp(fb_display[i].fontdata, new_data, size)) { kfree(new_data - FONT_EXTRA_WORDS * sizeof(int)); - new_data = fb_display[i].fontdata; + new_data = (u8 *)fb_display[i].fontdata; break; } } @@ -2333,7 +2335,7 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigne static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name) { struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct font_desc *f; + const struct font_desc *f; if (!name) f = get_default_font(info->var.xres, info->var.yres); diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h index 08befafe11d..0738cd62def 100644 --- a/drivers/video/console/fbcon.h +++ b/drivers/video/console/fbcon.h @@ -30,7 +30,7 @@ struct display { /* Filled in by the frame buffer device */ u_short inverse; /* != 0 text black on white as default */ /* Filled in by the low-level console driver */ - u_char *fontdata; + const u_char *fontdata; int userfont; /* != 0 if fontdata kmalloc()ed */ u_short scrollmode; /* Scroll Method */ short yscroll; /* Hardware scrolling */ diff --git a/drivers/video/console/font_10x18.c b/drivers/video/console/font_10x18.c index ff0af96e4df..e6aa0eab5bb 100644 --- a/drivers/video/console/font_10x18.c +++ b/drivers/video/console/font_10x18.c @@ -7,7 +7,7 @@ #define FONTDATAMAX 9216 -static unsigned char fontdata_10x18[FONTDATAMAX] = { +static const unsigned char fontdata_10x18[FONTDATAMAX] = { /* 0 0x00 '^@' */ 0x00, 0x00, /* 0000000000 */ @@ -5132,7 +5132,7 @@ static unsigned char fontdata_10x18[FONTDATAMAX] = { }; -struct font_desc font_10x18 = { +const struct font_desc font_10x18 = { FONT10x18_IDX, "10x18", 10, diff --git a/drivers/video/console/font_6x11.c b/drivers/video/console/font_6x11.c index c52f1294044..89976cd9749 100644 --- a/drivers/video/console/font_6x11.c +++ b/drivers/video/console/font_6x11.c @@ -8,7 +8,7 @@ #define FONTDATAMAX (11*256) -static unsigned char fontdata_6x11[FONTDATAMAX] = { +static const unsigned char fontdata_6x11[FONTDATAMAX] = { /* 0 0x00 '^@' */ 0x00, /* 00000000 */ @@ -3341,7 +3341,7 @@ static unsigned char fontdata_6x11[FONTDATAMAX] = { }; -struct font_desc font_vga_6x11 = { +const struct font_desc font_vga_6x11 = { VGA6x11_IDX, "ProFont6x11", 6, diff --git a/drivers/video/console/font_7x14.c b/drivers/video/console/font_7x14.c index 1fa7fcf2ff7..bbf11664739 100644 --- a/drivers/video/console/font_7x14.c +++ b/drivers/video/console/font_7x14.c @@ -7,7 +7,7 @@ #define FONTDATAMAX 3584 -static unsigned char fontdata_7x14[FONTDATAMAX] = { +static const unsigned char fontdata_7x14[FONTDATAMAX] = { /* 0 0x00 '^@' */ 0x00, /* 0000000 */ @@ -4108,7 +4108,7 @@ static unsigned char fontdata_7x14[FONTDATAMAX] = { }; -struct font_desc font_7x14 = { +const struct font_desc font_7x14 = { FONT7x14_IDX, "7x14", 7, diff --git a/drivers/video/console/font_8x16.c b/drivers/video/console/font_8x16.c index e6f8dbaa122..74fe86f28ff 100644 --- a/drivers/video/console/font_8x16.c +++ b/drivers/video/console/font_8x16.c @@ -8,7 +8,7 @@ #define FONTDATAMAX 4096 -static unsigned char fontdata_8x16[FONTDATAMAX] = { +static const unsigned char fontdata_8x16[FONTDATAMAX] = { /* 0 0x00 '^@' */ 0x00, /* 00000000 */ @@ -4621,7 +4621,7 @@ static unsigned char fontdata_8x16[FONTDATAMAX] = { }; -struct font_desc font_vga_8x16 = { +const struct font_desc font_vga_8x16 = { VGA8x16_IDX, "VGA8x16", 8, diff --git a/drivers/video/console/font_8x8.c b/drivers/video/console/font_8x8.c index 57fbe266a6b..26199f8ee90 100644 --- a/drivers/video/console/font_8x8.c +++ b/drivers/video/console/font_8x8.c @@ -8,7 +8,7 @@ #define FONTDATAMAX 2048 -static unsigned char fontdata_8x8[FONTDATAMAX] = { +static const unsigned char fontdata_8x8[FONTDATAMAX] = { /* 0 0x00 '^@' */ 0x00, /* 00000000 */ @@ -2573,7 +2573,7 @@ static unsigned char fontdata_8x8[FONTDATAMAX] = { }; -struct font_desc font_vga_8x8 = { +const struct font_desc font_vga_8x8 = { VGA8x8_IDX, "VGA8x8", 8, diff --git a/drivers/video/console/font_acorn_8x8.c b/drivers/video/console/font_acorn_8x8.c index d565505e306..2d2e39632e2 100644 --- a/drivers/video/console/font_acorn_8x8.c +++ b/drivers/video/console/font_acorn_8x8.c @@ -3,7 +3,7 @@ #include <linux/config.h> #include <linux/font.h> -static unsigned char acorndata_8x8[] = { +static const unsigned char acorndata_8x8[] = { /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ^@ */ /* 01 */ 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, /* ^A */ /* 02 */ 0x7e, 0xff, 0xbd, 0xff, 0xc3, 0xe7, 0xff, 0x7e, /* ^B */ @@ -262,7 +262,7 @@ static unsigned char acorndata_8x8[] = { /* FF */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -struct font_desc font_acorn_8x8 = { +const struct font_desc font_acorn_8x8 = { ACORN8x8_IDX, "Acorn8x8", 8, diff --git a/drivers/video/console/font_mini_4x6.c b/drivers/video/console/font_mini_4x6.c index 593b95500a0..d818234fdf1 100644 --- a/drivers/video/console/font_mini_4x6.c +++ b/drivers/video/console/font_mini_4x6.c @@ -43,7 +43,7 @@ __END__; #define FONTDATAMAX 1536 -static unsigned char fontdata_mini_4x6[FONTDATAMAX] = { +static const unsigned char fontdata_mini_4x6[FONTDATAMAX] = { /*{*/ /* Char 0: ' ' */ @@ -2147,7 +2147,7 @@ static unsigned char fontdata_mini_4x6[FONTDATAMAX] = { /*}*/ }; -struct font_desc font_mini_4x6 = { +const struct font_desc font_mini_4x6 = { MINI4x6_IDX, "MINI4x6", 4, diff --git a/drivers/video/console/font_pearl_8x8.c b/drivers/video/console/font_pearl_8x8.c index 5fa95f11881..e646c88f55c 100644 --- a/drivers/video/console/font_pearl_8x8.c +++ b/drivers/video/console/font_pearl_8x8.c @@ -13,7 +13,7 @@ #define FONTDATAMAX 2048 -static unsigned char fontdata_pearl8x8[FONTDATAMAX] = { +static const unsigned char fontdata_pearl8x8[FONTDATAMAX] = { /* 0 0x00 '^@' */ 0x00, /* 00000000 */ @@ -2577,7 +2577,7 @@ static unsigned char fontdata_pearl8x8[FONTDATAMAX] = { }; -struct font_desc font_pearl_8x8 = { +const struct font_desc font_pearl_8x8 = { PEARL8x8_IDX, "PEARL8x8", 8, diff --git a/drivers/video/console/font_sun12x22.c b/drivers/video/console/font_sun12x22.c index c7bd967ea10..ab5eb93407b 100644 --- a/drivers/video/console/font_sun12x22.c +++ b/drivers/video/console/font_sun12x22.c @@ -2,7 +2,7 @@ #define FONTDATAMAX 11264 -static unsigned char fontdata_sun12x22[FONTDATAMAX] = { +static const unsigned char fontdata_sun12x22[FONTDATAMAX] = { /* 0 0x00 '^@' */ 0x00, 0x00, /* 000000000000 */ @@ -6151,7 +6151,7 @@ static unsigned char fontdata_sun12x22[FONTDATAMAX] = { }; -struct font_desc font_sun_12x22 = { +const struct font_desc font_sun_12x22 = { SUN12x22_IDX, "SUN12x22", 12, diff --git a/drivers/video/console/font_sun8x16.c b/drivers/video/console/font_sun8x16.c index 2af3ab35465..41f910f5529 100644 --- a/drivers/video/console/font_sun8x16.c +++ b/drivers/video/console/font_sun8x16.c @@ -2,7 +2,7 @@ #define FONTDATAMAX 4096 -static unsigned char fontdata_sun8x16[FONTDATAMAX] = { +static const unsigned char fontdata_sun8x16[FONTDATAMAX] = { /* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* */ 0x00,0x00,0x7e,0x81,0xa5,0x81,0x81,0xbd,0x99,0x81,0x81,0x7e,0x00,0x00,0x00,0x00, /* */ 0x00,0x00,0x7e,0xff,0xdb,0xff,0xff,0xc3,0xe7,0xff,0xff,0x7e,0x00,0x00,0x00,0x00, @@ -261,7 +261,7 @@ static unsigned char fontdata_sun8x16[FONTDATAMAX] = { /* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; -struct font_desc font_sun_8x16 = { +const struct font_desc font_sun_8x16 = { SUN8x16_IDX, "SUN8x16", 8, diff --git a/drivers/video/console/fonts.c b/drivers/video/console/fonts.c index e79b2970264..4fd07d9eca0 100644 --- a/drivers/video/console/fonts.c +++ b/drivers/video/console/fonts.c @@ -23,7 +23,7 @@ #define NO_FONTS -static struct font_desc *fonts[] = { +static const struct font_desc *fonts[] = { #ifdef CONFIG_FONT_8x8 #undef NO_FONTS &font_vga_8x8, @@ -84,7 +84,7 @@ static struct font_desc *fonts[] = { * */ -struct font_desc *find_font(char *name) +const struct font_desc *find_font(const char *name) { unsigned int i; @@ -108,10 +108,10 @@ struct font_desc *find_font(char *name) * */ -struct font_desc *get_default_font(int xres, int yres) +const struct font_desc *get_default_font(int xres, int yres) { int i, c, cc; - struct font_desc *f, *g; + const struct font_desc *f, *g; g = NULL; cc = -10000; @@ -138,7 +138,6 @@ struct font_desc *get_default_font(int xres, int yres) return g; } -EXPORT_SYMBOL(fonts); EXPORT_SYMBOL(find_font); EXPORT_SYMBOL(get_default_font); diff --git a/drivers/video/i810/i810_main.c b/drivers/video/i810/i810_main.c index 7018ffffcbc..0dbc9ddb676 100644 --- a/drivers/video/i810/i810_main.c +++ b/drivers/video/i810/i810_main.c @@ -95,18 +95,18 @@ static struct pci_driver i810fb_driver = { static char *mode_option __devinitdata = NULL; static int vram __devinitdata = 4; static int bpp __devinitdata = 8; -static int mtrr __devinitdata = 0; -static int accel __devinitdata = 0; -static int hsync1 __devinitdata = 0; -static int hsync2 __devinitdata = 0; -static int vsync1 __devinitdata = 0; -static int vsync2 __devinitdata = 0; -static int xres __devinitdata = 640; -static int yres __devinitdata = 480; -static int vyres __devinitdata = 0; -static int sync __devinitdata = 0; -static int ext_vga __devinitdata = 0; -static int dcolor __devinitdata = 0; +static int mtrr __devinitdata; +static int accel __devinitdata; +static int hsync1 __devinitdata; +static int hsync2 __devinitdata; +static int vsync1 __devinitdata; +static int vsync2 __devinitdata; +static int xres __devinitdata; +static int yres __devinitdata; +static int vyres __devinitdata; +static int sync __devinitdata; +static int extvga __devinitdata; +static int dcolor __devinitdata; /*------------------------------------------------------------*/ @@ -950,7 +950,7 @@ static int i810_check_params(struct fb_var_screeninfo *var, struct fb_info *info) { struct i810fb_par *par = (struct i810fb_par *) info->par; - int line_length, vidmem, mode_valid = 0; + int line_length, vidmem, mode_valid = 0, retval = 0; u32 vyres = var->yres_virtual, vxres = var->xres_virtual; /* * Memory limit @@ -1026,10 +1026,11 @@ static int i810_check_params(struct fb_var_screeninfo *var, printk("i810fb: invalid video mode%s\n", default_sync ? "" : ". Specifying " "vsyncN/hsyncN parameters may help"); + retval = -EINVAL; } } - return 0; + return retval; } /** @@ -1724,12 +1725,21 @@ static void __devinit i810_init_defaults(struct i810fb_par *par, if (bpp < 8) bpp = 8; + par->i810fb_ops = i810fb_ops; + + if (xres) + info->var.xres = xres; + else + info->var.xres = 640; + + if (yres) + info->var.yres = yres; + else + info->var.yres = 480; + if (!vyres) - vyres = (vram << 20)/(xres*bpp >> 3); + vyres = (vram << 20)/(info->var.xres*bpp >> 3); - par->i810fb_ops = i810fb_ops; - info->var.xres = xres; - info->var.yres = yres; info->var.yres_virtual = vyres; info->var.bits_per_pixel = bpp; @@ -1756,7 +1766,7 @@ static void __devinit i810_init_device(struct i810fb_par *par) i810_init_cursor(par); /* mvo: enable external vga-connector (for laptops) */ - if (ext_vga) { + if (extvga) { i810_writel(HVSYNC, mmio, 0); i810_writel(PWR_CLKC, mmio, 3); } @@ -1830,7 +1840,7 @@ static void __devinit i810fb_find_init_mode(struct fb_info *info) { struct fb_videomode mode; struct fb_var_screeninfo var; - struct fb_monspecs *specs = NULL; + struct fb_monspecs *specs = &info->monspecs; int found = 0; #ifdef CONFIG_FB_I810_I2C int i; @@ -1853,16 +1863,24 @@ static void __devinit i810fb_find_init_mode(struct fb_info *info) if (!err) printk("i810fb_init_pci: DDC probe successful\n"); - fb_edid_to_monspecs(par->edid, &info->monspecs); + fb_edid_to_monspecs(par->edid, specs); - if (info->monspecs.modedb == NULL) + if (specs->modedb == NULL) printk("i810fb_init_pci: Unable to get Mode Database\n"); - specs = &info->monspecs; fb_videomode_to_modelist(specs->modedb, specs->modedb_len, &info->modelist); if (specs->modedb != NULL) { - if (specs->misc & FB_MISC_1ST_DETAIL) { + if (xres && yres) { + struct fb_videomode *m; + + if ((m = fb_find_best_mode(&var, &info->modelist))) { + mode = *m; + found = 1; + } + } + + if (!found && specs->misc & FB_MISC_1ST_DETAIL) { for (i = 0; i < specs->modedb_len; i++) { if (specs->modedb[i].flag & FB_MODE_IS_FIRST) { mode = specs->modedb[i]; @@ -1903,8 +1921,8 @@ static int __devinit i810fb_setup(char *options) mtrr = 1; else if (!strncmp(this_opt, "accel", 5)) accel = 1; - else if (!strncmp(this_opt, "ext_vga", 7)) - ext_vga = 1; + else if (!strncmp(this_opt, "extvga", 6)) + extvga = 1; else if (!strncmp(this_opt, "sync", 4)) sync = 1; else if (!strncmp(this_opt, "vram:", 5)) @@ -2133,8 +2151,8 @@ module_param(accel, bool, 0); MODULE_PARM_DESC(accel, "Use Acceleration (BLIT) engine (default = 0)"); module_param(mtrr, bool, 0); MODULE_PARM_DESC(mtrr, "Use MTRR (default = 0)"); -module_param(ext_vga, bool, 0); -MODULE_PARM_DESC(ext_vga, "Enable external VGA connector (default = 0)"); +module_param(extvga, bool, 0); +MODULE_PARM_DESC(extvga, "Enable external VGA connector (default = 0)"); module_param(sync, bool, 0); MODULE_PARM_DESC(sync, "wait for accel engine to finish drawing" " (default = 0)"); diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c index 98e00d8601e..e02da41f1b2 100644 --- a/drivers/video/matrox/matroxfb_base.c +++ b/drivers/video/matrox/matroxfb_base.c @@ -1285,7 +1285,7 @@ static int matroxfb_getmemory(WPMINFO unsigned int maxSize, unsigned int *realSi vaddr_t vm; unsigned int offs; unsigned int offs2; - unsigned char store; + unsigned char store, orig; unsigned char bytes[32]; unsigned char* tmp; @@ -1298,7 +1298,8 @@ static int matroxfb_getmemory(WPMINFO unsigned int maxSize, unsigned int *realSi if (maxSize > 0x2000000) maxSize = 0x2000000; mga_outb(M_EXTVGA_INDEX, 0x03); - mga_outb(M_EXTVGA_DATA, mga_inb(M_EXTVGA_DATA) | 0x80); + orig = mga_inb(M_EXTVGA_DATA); + mga_outb(M_EXTVGA_DATA, orig | 0x80); store = mga_readb(vm, 0x1234); tmp = bytes; @@ -1323,7 +1324,7 @@ static int matroxfb_getmemory(WPMINFO unsigned int maxSize, unsigned int *realSi mga_writeb(vm, 0x1234, store); mga_outb(M_EXTVGA_INDEX, 0x03); - mga_outb(M_EXTVGA_DATA, mga_inb(M_EXTVGA_DATA) & ~0x80); + mga_outb(M_EXTVGA_DATA, orig); *realSize = offs - 0x100000; #ifdef CONFIG_FB_MATROX_MILLENIUM @@ -1858,6 +1859,8 @@ static int initMatrox2(WPMINFO struct board* b){ to yres_virtual * xres_virtual < 2^32 */ } matroxfb_init_fix(PMINFO2); + ACCESS_FBINFO(fbcon.screen_base) = vaddr_va(ACCESS_FBINFO(video.vbase)); + matroxfb_update_fix(PMINFO2); /* Normalize values (namely yres_virtual) */ matroxfb_check_var(&vesafb_defined, &ACCESS_FBINFO(fbcon)); /* And put it into "current" var. Do NOT program hardware yet, or we'll not take over @@ -2010,11 +2013,11 @@ static int matroxfb_probe(struct pci_dev* pdev, const struct pci_device_id* dumm } /* not match... */ if (!b->vendor) - return -1; + return -ENODEV; if (dev > 0) { /* not requested one... */ dev--; - return -1; + return -ENODEV; } pci_read_config_dword(pdev, PCI_COMMAND, &cmd); if (pci_enable_device(pdev)) { diff --git a/drivers/video/pm3fb.c b/drivers/video/pm3fb.c index e0dad948467..2e11b601c48 100644 --- a/drivers/video/pm3fb.c +++ b/drivers/video/pm3fb.c @@ -67,6 +67,7 @@ #include <linux/init.h> #include <linux/pci.h> #include <linux/ioport.h> +#include <linux/ctype.h> #include <video/fbcon.h> #include <video/fbcon-mfb.h> @@ -2594,7 +2595,7 @@ static char *pm3fb_boardnum_setup(char *options, unsigned long *bn) { char *next; - if (!(CHAR_IS_NUM(options[0]))) { + if (!(isdigit(options[0]))) { (*bn) = 0; return (options); } diff --git a/drivers/w1/w1_ds2433.c b/drivers/w1/w1_ds2433.c index b7c24b34d27..279e0e0363d 100644 --- a/drivers/w1/w1_ds2433.c +++ b/drivers/w1/w1_ds2433.c @@ -15,6 +15,10 @@ #include <linux/delay.h> #ifdef CONFIG_W1_F23_CRC #include <linux/crc16.h> + +#define CRC16_INIT 0 +#define CRC16_VALID 0xb001 + #endif #include "w1.h" @@ -214,7 +218,7 @@ static ssize_t w1_f23_write_bin(struct kobject *kobj, char *buf, loff_t off, #ifdef CONFIG_W1_F23_CRC /* can only write full blocks in cached mode */ if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) { - dev_err(&sl->dev, "invalid offset/count off=%d cnt=%d\n", + dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n", (int)off, count); return -EINVAL; } |