diff options
author | Jens Axboe <jens.axboe@oracle.com> | 2009-07-28 08:59:30 +0200 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2009-07-28 08:59:30 +0200 |
commit | eac57f63ca36ee475f570d5db77b09da39514996 (patch) | |
tree | c868c98dec70a3283fee612bbc3f849830525fcf /drivers | |
parent | e18001cd1b4760b03fa0ac70d93f541016c20d8f (diff) | |
parent | 4733fd328f14280900435d9dbae1487d110a4d56 (diff) |
Merge branch 'master' into for-2.6.32
Conflicts:
block/elevator.c
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'drivers')
458 files changed, 10967 insertions, 4018 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 15a23031833f..336eb1ed73cc 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -513,6 +513,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x502a), board_ahci }, /* Tolapai */ { PCI_VDEVICE(INTEL, 0x502b), board_ahci }, /* Tolapai */ { PCI_VDEVICE(INTEL, 0x3a05), board_ahci }, /* ICH10 */ + { PCI_VDEVICE(INTEL, 0x3a22), board_ahci }, /* ICH10 */ { PCI_VDEVICE(INTEL, 0x3a25), board_ahci }, /* ICH10 */ { PCI_VDEVICE(INTEL, 0x3b24), board_ahci }, /* PCH RAID */ { PCI_VDEVICE(INTEL, 0x3b25), board_ahci }, /* PCH RAID */ diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 045a486a09ea..2c6aedaef718 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -3392,17 +3392,27 @@ int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel) static int ata_dev_set_mode(struct ata_device *dev) { + struct ata_port *ap = dev->link->ap; struct ata_eh_context *ehc = &dev->link->eh_context; + const bool nosetxfer = dev->horkage & ATA_HORKAGE_NOSETXFER; const char *dev_err_whine = ""; int ign_dev_err = 0; - unsigned int err_mask; + unsigned int err_mask = 0; int rc; dev->flags &= ~ATA_DFLAG_PIO; if (dev->xfer_shift == ATA_SHIFT_PIO) dev->flags |= ATA_DFLAG_PIO; - err_mask = ata_dev_set_xfermode(dev); + if (nosetxfer && ap->flags & ATA_FLAG_SATA && ata_id_is_sata(dev->id)) + dev_err_whine = " (SET_XFERMODE skipped)"; + else { + if (nosetxfer) + ata_dev_printk(dev, KERN_WARNING, + "NOSETXFER but PATA detected - can't " + "skip SETXFER, might malfunction\n"); + err_mask = ata_dev_set_xfermode(dev); + } if (err_mask & ~AC_ERR_DEV) goto fail; @@ -4297,6 +4307,12 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { /* Devices which aren't very happy with higher link speeds */ { "WD My Book", NULL, ATA_HORKAGE_1_5_GBPS, }, + /* + * Devices which choke on SETXFER. Applies only if both the + * device and controller are SATA. + */ + { "PIONEER DVD-RW DVRTD08", "1.00", ATA_HORKAGE_NOSETXFER }, + /* End Marker */ { } }; diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index fa22f94ca415..1a07c061f644 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2517,6 +2517,10 @@ int ata_eh_reset(struct ata_link *link, int classify, ata_eh_about_to_do(link, NULL, ATA_EH_RESET); rc = ata_do_reset(link, reset, classes, deadline, true); + if (rc) { + failed_link = link; + goto fail; + } } } else { if (verbose) diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c index 4b27617be26d..8561a9f195c1 100644 --- a/drivers/ata/pata_at91.c +++ b/drivers/ata/pata_at91.c @@ -312,11 +312,12 @@ err_ide_ioremap: static int __devexit pata_at91_remove(struct platform_device *pdev) { struct ata_host *host = dev_get_drvdata(&pdev->dev); - struct at91_ide_info *info = host->private_data; + struct at91_ide_info *info; struct device *dev = &pdev->dev; if (!host) return 0; + info = host->private_data; ata_host_detach(host); diff --git a/drivers/base/devres.c b/drivers/base/devres.c index e8beb8e5b626..05dd307e8f02 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -428,6 +428,9 @@ int devres_release_all(struct device *dev) { unsigned long flags; + /* Looks like an uninitialized device structure */ + if (WARN_ON(dev->devres_head.next == NULL)) + return -ENODEV; spin_lock_irqsave(&dev->devres_lock, flags); return release_nodes(dev, dev->devres_head.next, &dev->devres_head, flags); diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index ddeb819c8f87..f285f441fab9 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -217,8 +217,10 @@ firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr, ret_count = -ENODEV; goto out; } - if (offset > fw->size) - return 0; + if (offset > fw->size) { + ret_count = 0; + goto out; + } if (count > fw->size - offset) count = fw->size - offset; @@ -357,7 +359,7 @@ static void fw_dev_release(struct device *dev) kfree(fw_priv->pages); kfree(fw_priv->fw_id); kfree(fw_priv); - put_device(dev); + kfree(dev); module_put(THIS_MODULE); } @@ -408,13 +410,11 @@ static int fw_register_device(struct device **dev_p, const char *fw_name, if (retval) { dev_err(device, "%s: device_register failed\n", __func__); put_device(f_dev); - goto error_kfree_fw_id; + return retval; } *dev_p = f_dev; return 0; -error_kfree_fw_id: - kfree(fw_priv->fw_id); error_kfree: kfree(f_dev); kfree(fw_priv); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index fae725458981..58a3e572f2c9 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -762,6 +762,7 @@ static int dpm_prepare(pm_message_t state) dev->power.status = DPM_ON; if (error == -EAGAIN) { put_device(dev); + error = 0; continue; } printk(KERN_ERR "PM: Failed to prepare device %s " diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index 668dc234b8e2..1e6b7c14f697 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -36,6 +36,7 @@ #include <linux/ioport.h> #include <linux/mm.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/proc_fs.h> #include <linux/reboot.h> #include <linux/spinlock.h> diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index bb72ada9f074..1d886e079c58 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -298,6 +298,22 @@ config BLK_DEV_NBD If unsure, say N. +config BLK_DEV_OSD + tristate "OSD object-as-blkdev support" + depends on SCSI_OSD_ULD + ---help--- + Saying Y or M here will allow the exporting of a single SCSI + OSD (object-based storage) object as a Linux block device. + + For example, if you create a 2G object on an OSD device, + you can then use this module to present that 2G object as + a Linux block device. + + To compile this driver as a module, choose M here: the + module will be called osdblk. + + If unsure, say N. + config BLK_DEV_SX8 tristate "Promise SATA SX8 support" depends on PCI diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 7755a5e2a85e..cdaa3f8fddf0 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_XILINX_SYSACE) += xsysace.o obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o obj-$(CONFIG_MG_DISK) += mg_disk.o obj-$(CONFIG_SUNVDC) += sunvdc.o +obj-$(CONFIG_BLK_DEV_OSD) += osdblk.o obj-$(CONFIG_BLK_DEV_UMEM) += umem.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c index 9c6e5b0fe894..2f07b7c99a95 100644 --- a/drivers/block/amiflop.c +++ b/drivers/block/amiflop.c @@ -1645,7 +1645,7 @@ static int __init fd_probe_drives(void) { int drive,drives,nomem; - printk(KERN_INFO "FD: probing units\n" KERN_INFO "found "); + printk(KERN_INFO "FD: probing units\nfound "); drives=0; nomem=0; for(drive=0;drive<FD_MAX_UNITS;drive++) { diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index f5e7180d7f47..3ff02941b3dd 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -1627,7 +1627,7 @@ static int fd_ioctl(struct block_device *bdev, fmode_t mode, drive, dtp->blocks, dtp->spt, dtp->stretch); /* sanity check */ - if (!dtp || setprm.track != dtp->blocks/dtp->spt/2 || + if (setprm.track != dtp->blocks/dtp->spt/2 || setprm.head != 2) { redo_fd_request(); return -EINVAL; diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 65a0655e7fc8..a52cc7fe45ea 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -26,6 +26,7 @@ #include <linux/pci.h> #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/delay.h> #include <linux/major.h> #include <linux/fs.h> diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 801f4ab83302..5757188cd1fb 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -61,7 +61,6 @@ #include <linux/blkdev.h> #include <linux/blkpg.h> #include <linux/init.h> -#include <linux/smp_lock.h> #include <linux/swap.h> #include <linux/slab.h> #include <linux/loop.h> diff --git a/drivers/block/osdblk.c b/drivers/block/osdblk.c new file mode 100644 index 000000000000..13c1aee6aa3f --- /dev/null +++ b/drivers/block/osdblk.c @@ -0,0 +1,701 @@ + +/* + osdblk.c -- Export a single SCSI OSD object as a Linux block device + + + Copyright 2009 Red Hat, Inc. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + + Instructions for use + -------------------- + + 1) Map a Linux block device to an existing OSD object. + + In this example, we will use partition id 1234, object id 5678, + OSD device /dev/osd1. + + $ echo "1234 5678 /dev/osd1" > /sys/class/osdblk/add + + + 2) List all active blkdev<->object mappings. + + In this example, we have performed step #1 twice, creating two blkdevs, + mapped to two separate OSD objects. + + $ cat /sys/class/osdblk/list + 0 174 1234 5678 /dev/osd1 + 1 179 1994 897123 /dev/osd0 + + The columns, in order, are: + - blkdev unique id + - blkdev assigned major + - OSD object partition id + - OSD object id + - OSD device + + + 3) Remove an active blkdev<->object mapping. + + In this example, we remove the mapping with blkdev unique id 1. + + $ echo 1 > /sys/class/osdblk/remove + + + NOTE: The actual creation and deletion of OSD objects is outside the scope + of this driver. + + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <scsi/osd_initiator.h> +#include <scsi/osd_attributes.h> +#include <scsi/osd_sec.h> +#include <scsi/scsi_device.h> + +#define DRV_NAME "osdblk" +#define PFX DRV_NAME ": " + +/* #define _OSDBLK_DEBUG */ +#ifdef _OSDBLK_DEBUG +#define OSDBLK_DEBUG(fmt, a...) \ + printk(KERN_NOTICE "osdblk @%s:%d: " fmt, __func__, __LINE__, ##a) +#else +#define OSDBLK_DEBUG(fmt, a...) \ + do { if (0) printk(fmt, ##a); } while (0) +#endif + +MODULE_AUTHOR("Jeff Garzik <jeff@garzik.org>"); +MODULE_DESCRIPTION("block device inside an OSD object osdblk.ko"); +MODULE_LICENSE("GPL"); + +struct osdblk_device; + +enum { + OSDBLK_MINORS_PER_MAJOR = 256, /* max minors per blkdev */ + OSDBLK_MAX_REQ = 32, /* max parallel requests */ + OSDBLK_OP_TIMEOUT = 4 * 60, /* sync OSD req timeout */ +}; + +struct osdblk_request { + struct request *rq; /* blk layer request */ + struct bio *bio; /* cloned bio */ + struct osdblk_device *osdev; /* associated blkdev */ +}; + +struct osdblk_device { + int id; /* blkdev unique id */ + + int major; /* blkdev assigned major */ + struct gendisk *disk; /* blkdev's gendisk and rq */ + struct request_queue *q; + + struct osd_dev *osd; /* associated OSD */ + + char name[32]; /* blkdev name, e.g. osdblk34 */ + + spinlock_t lock; /* queue lock */ + + struct osd_obj_id obj; /* OSD partition, obj id */ + uint8_t obj_cred[OSD_CAP_LEN]; /* OSD cred */ + + struct osdblk_request req[OSDBLK_MAX_REQ]; /* request table */ + + struct list_head node; + + char osd_path[0]; /* OSD device path */ +}; + +static struct class *class_osdblk; /* /sys/class/osdblk */ +static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */ +static LIST_HEAD(osdblkdev_list); + +static struct block_device_operations osdblk_bd_ops = { + .owner = THIS_MODULE, +}; + +static const struct osd_attr g_attr_logical_length = ATTR_DEF( + OSD_APAGE_OBJECT_INFORMATION, OSD_ATTR_OI_LOGICAL_LENGTH, 8); + +static void osdblk_make_credential(u8 cred_a[OSD_CAP_LEN], + const struct osd_obj_id *obj) +{ + osd_sec_init_nosec_doall_caps(cred_a, obj, false, true); +} + +/* copied from exofs; move to libosd? */ +/* + * Perform a synchronous OSD operation. copied from exofs; move to libosd? + */ +static int osd_sync_op(struct osd_request *or, int timeout, uint8_t *credential) +{ + int ret; + + or->timeout = timeout; + ret = osd_finalize_request(or, 0, credential, NULL); + if (ret) + return ret; + + ret = osd_execute_request(or); + + /* osd_req_decode_sense(or, ret); */ + return ret; +} + +/* + * Perform an asynchronous OSD operation. copied from exofs; move to libosd? + */ +static int osd_async_op(struct osd_request *or, osd_req_done_fn *async_done, + void *caller_context, u8 *cred) +{ + int ret; + + ret = osd_finalize_request(or, 0, cred, NULL); + if (ret) + return ret; + + ret = osd_execute_request_async(or, async_done, caller_context); + + return ret; +} + +/* copied from exofs; move to libosd? */ +static int extract_attr_from_req(struct osd_request *or, struct osd_attr *attr) +{ + struct osd_attr cur_attr = {.attr_page = 0}; /* start with zeros */ + void *iter = NULL; + int nelem; + + do { + nelem = 1; + osd_req_decode_get_attr_list(or, &cur_attr, &nelem, &iter); + if ((cur_attr.attr_page == attr->attr_page) && + (cur_attr.attr_id == attr->attr_id)) { + attr->len = cur_attr.len; + attr->val_ptr = cur_attr.val_ptr; + return 0; + } + } while (iter); + + return -EIO; +} + +static int osdblk_get_obj_size(struct osdblk_device *osdev, u64 *size_out) +{ + struct osd_request *or; + struct osd_attr attr; + int ret; + + /* start request */ + or = osd_start_request(osdev->osd, GFP_KERNEL); + if (!or) + return -ENOMEM; + + /* create a get-attributes(length) request */ + osd_req_get_attributes(or, &osdev->obj); + + osd_req_add_get_attr_list(or, &g_attr_logical_length, 1); + + /* execute op synchronously */ + ret = osd_sync_op(or, OSDBLK_OP_TIMEOUT, osdev->obj_cred); + if (ret) + goto out; + + /* extract length from returned attribute info */ + attr = g_attr_logical_length; + ret = extract_attr_from_req(or, &attr); + if (ret) + goto out; + + *size_out = get_unaligned_be64(attr.val_ptr); + +out: + osd_end_request(or); + return ret; + +} + +static void osdblk_osd_complete(struct osd_request *or, void *private) +{ + struct osdblk_request *orq = private; + struct osd_sense_info osi; + int ret = osd_req_decode_sense(or, &osi); + + if (ret) { + ret = -EIO; + OSDBLK_DEBUG("osdblk_osd_complete with err=%d\n", ret); + } + + /* complete OSD request */ + osd_end_request(or); + + /* complete request passed to osdblk by block layer */ + __blk_end_request_all(orq->rq, ret); +} + +static void bio_chain_put(struct bio *chain) +{ + struct bio *tmp; + + while (chain) { + tmp = chain; + chain = chain->bi_next; + + bio_put(tmp); + } +} + +static struct bio *bio_chain_clone(struct bio *old_chain, gfp_t gfpmask) +{ + struct bio *tmp, *new_chain = NULL, *tail = NULL; + + while (old_chain) { + tmp = bio_kmalloc(gfpmask, old_chain->bi_max_vecs); + if (!tmp) + goto err_out; + + __bio_clone(tmp, old_chain); + tmp->bi_bdev = NULL; + gfpmask &= ~__GFP_WAIT; + tmp->bi_next = NULL; + + if (!new_chain) + new_chain = tail = tmp; + else { + tail->bi_next = tmp; + tail = tmp; + } + + old_chain = old_chain->bi_next; + } + + return new_chain; + +err_out: + OSDBLK_DEBUG("bio_chain_clone with err\n"); + bio_chain_put(new_chain); + return NULL; +} + +static void osdblk_rq_fn(struct request_queue *q) +{ + struct osdblk_device *osdev = q->queuedata; + + while (1) { + struct request *rq; + struct osdblk_request *orq; + struct osd_request *or; + struct bio *bio; + bool do_write, do_flush; + + /* peek at request from block layer */ + rq = blk_fetch_request(q); + if (!rq) + break; + + /* filter out block requests we don't understand */ + if (!blk_fs_request(rq) && !blk_barrier_rq(rq)) { + blk_end_request_all(rq, 0); + continue; + } + + /* deduce our operation (read, write, flush) */ + /* I wish the block layer simplified cmd_type/cmd_flags/cmd[] + * into a clearly defined set of RPC commands: + * read, write, flush, scsi command, power mgmt req, + * driver-specific, etc. + */ + + do_flush = (rq->special == (void *) 0xdeadbeefUL); + do_write = (rq_data_dir(rq) == WRITE); + + if (!do_flush) { /* osd_flush does not use a bio */ + /* a bio clone to be passed down to OSD request */ + bio = bio_chain_clone(rq->bio, GFP_ATOMIC); + if (!bio) + break; + } else + bio = NULL; + + /* alloc internal OSD request, for OSD command execution */ + or = osd_start_request(osdev->osd, GFP_ATOMIC); + if (!or) { + bio_chain_put(bio); + OSDBLK_DEBUG("osd_start_request with err\n"); + break; + } + + orq = &osdev->req[rq->tag]; + orq->rq = rq; + orq->bio = bio; + orq->osdev = osdev; + + /* init OSD command: flush, write or read */ + if (do_flush) + osd_req_flush_object(or, &osdev->obj, + OSD_CDB_FLUSH_ALL, 0, 0); + else if (do_write) + osd_req_write(or, &osdev->obj, blk_rq_pos(rq) * 512ULL, + bio, blk_rq_bytes(rq)); + else + osd_req_read(or, &osdev->obj, blk_rq_pos(rq) * 512ULL, + bio, blk_rq_bytes(rq)); + + OSDBLK_DEBUG("%s 0x%x bytes at 0x%llx\n", + do_flush ? "flush" : do_write ? + "write" : "read", blk_rq_bytes(rq), + blk_rq_pos(rq) * 512ULL); + + /* begin OSD command execution */ + if (osd_async_op(or, osdblk_osd_complete, orq, + osdev->obj_cred)) { + osd_end_request(or); + blk_requeue_request(q, rq); + bio_chain_put(bio); + OSDBLK_DEBUG("osd_execute_request_async with err\n"); + break; + } + + /* remove the special 'flush' marker, now that the command + * is executing + */ + rq->special = NULL; + } +} + +static void osdblk_prepare_flush(struct request_queue *q, struct request *rq) +{ + /* add driver-specific marker, to indicate that this request + * is a flush command + */ + rq->special = (void *) 0xdeadbeefUL; +} + +static void osdblk_free_disk(struct osdblk_device *osdev) +{ + struct gendisk *disk = osdev->disk; + + if (!disk) + return; + + if (disk->flags & GENHD_FL_UP) + del_gendisk(disk); + if (disk->queue) + blk_cleanup_queue(disk->queue); + put_disk(disk); +} + +static int osdblk_init_disk(struct osdblk_device *osdev) +{ + struct gendisk *disk; + struct request_queue *q; + int rc; + u64 obj_size = 0; + + /* contact OSD, request size info about the object being mapped */ + rc = osdblk_get_obj_size(osdev, &obj_size); + if (rc) + return rc; + + /* create gendisk info */ + disk = alloc_disk(OSDBLK_MINORS_PER_MAJOR); + if (!disk) + return -ENOMEM; + + sprintf(disk->disk_name, DRV_NAME "%d", osdev->id); + disk->major = osdev->major; + disk->first_minor = 0; + disk->fops = &osdblk_bd_ops; + disk->private_data = osdev; + + /* init rq */ + q = blk_init_queue(osdblk_rq_fn, &osdev->lock); + if (!q) { + put_disk(disk); + return -ENOMEM; + } + + /* switch queue to TCQ mode; allocate tag map */ + rc = blk_queue_init_tags(q, OSDBLK_MAX_REQ, NULL); + if (rc) { + blk_cleanup_queue(q); + put_disk(disk); + return rc; + } + + /* Set our limits to the lower device limits, because osdblk cannot + * sleep when allocating a lower-request and therefore cannot be + * bouncing. + */ + blk_queue_stack_limits(q, osd_request_queue(osdev->osd)); + + blk_queue_prep_rq(q, blk_queue_start_tag); + blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH, osdblk_prepare_flush); + + disk->queue = q; + + q->queuedata = osdev; + + osdev->disk = disk; + osdev->q = q; + + /* finally, announce the disk to the world */ + set_capacity(disk, obj_size / 512ULL); + add_disk(disk); + + printk(KERN_INFO "%s: Added of size 0x%llx\n", + disk->disk_name, (unsigned long long)obj_size); + + return 0; +} + +/******************************************************************** + * /sys/class/osdblk/ + * add map OSD object to blkdev + * remove unmap OSD object + * list show mappings + *******************************************************************/ + +static void class_osdblk_release(struct class *cls) +{ + kfree(cls); +} + +static ssize_t class_osdblk_list(struct class *c, char *data) +{ + int n = 0; + struct list_head *tmp; + + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + + list_for_each(tmp, &osdblkdev_list) { + struct osdblk_device *osdev; + + osdev = list_entry(tmp, struct osdblk_device, node); + + n += sprintf(data+n, "%d %d %llu %llu %s\n", + osdev->id, + osdev->major, + osdev->obj.partition, + osdev->obj.id, + osdev->osd_path); + } + + mutex_unlock(&ctl_mutex); + return n; +} + +static ssize_t class_osdblk_add(struct class *c, const char *buf, size_t count) +{ + struct osdblk_device *osdev; + ssize_t rc; + int irc, new_id = 0; + struct list_head *tmp; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + /* new osdblk_device object */ + osdev = kzalloc(sizeof(*osdev) + strlen(buf) + 1, GFP_KERNEL); + if (!osdev) { + rc = -ENOMEM; + goto err_out_mod; + } + + /* static osdblk_device initialization */ + spin_lock_init(&osdev->lock); + INIT_LIST_HEAD(&osdev->node); + + /* generate unique id: find highest unique id, add one */ + + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + + list_for_each(tmp, &osdblkdev_list) { + struct osdblk_device *osdev; + + osdev = list_entry(tmp, struct osdblk_device, node); + if (osdev->id > new_id) + new_id = osdev->id + 1; + } + + osdev->id = new_id; + + /* add to global list */ + list_add_tail(&osdev->node, &osdblkdev_list); + + mutex_unlock(&ctl_mutex); + + /* parse add command */ + if (sscanf(buf, "%llu %llu %s", &osdev->obj.partition, &osdev->obj.id, + osdev->osd_path) != 3) { + rc = -EINVAL; + goto err_out_slot; + } + + /* initialize rest of new object */ + sprintf(osdev->name, DRV_NAME "%d", osdev->id); + + /* contact requested OSD */ + osdev->osd = osduld_path_lookup(osdev->osd_path); + if (IS_ERR(osdev->osd)) { + rc = PTR_ERR(osdev->osd); + goto err_out_slot; + } + + /* build OSD credential */ + osdblk_make_credential(osdev->obj_cred, &osdev->obj); + + /* register our block device */ + irc = register_blkdev(0, osdev->name); + if (irc < 0) { + rc = irc; + goto err_out_osd; + } + + osdev->major = irc; + + /* set up and announce blkdev mapping */ + rc = osdblk_init_disk(osdev); + if (rc) + goto err_out_blkdev; + + return count; + +err_out_blkdev: + unregister_blkdev(osdev->major, osdev->name); +err_out_osd: + osduld_put_device(osdev->osd); +err_out_slot: + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + list_del_init(&osdev->node); + mutex_unlock(&ctl_mutex); + + kfree(osdev); +err_out_mod: + OSDBLK_DEBUG("Error adding device %s\n", buf); + module_put(THIS_MODULE); + return rc; +} + +static ssize_t class_osdblk_remove(struct class *c, const char *buf, + size_t count) +{ + struct osdblk_device *osdev = NULL; + int target_id, rc; + unsigned long ul; + struct list_head *tmp; + + rc = strict_strtoul(buf, 10, &ul); + if (rc) + return rc; + + /* convert to int; abort if we lost anything in the conversion */ + target_id = (int) ul; + if (target_id != ul) + return -EINVAL; + + /* remove object from list immediately */ + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + + list_for_each(tmp, &osdblkdev_list) { + osdev = list_entry(tmp, struct osdblk_device, node); + if (osdev->id == target_id) { + list_del_init(&osdev->node); + break; + } + osdev = NULL; + } + + mutex_unlock(&ctl_mutex); + + if (!osdev) + return -ENOENT; + + /* clean up and free blkdev and associated OSD connection */ + osdblk_free_disk(osdev); + unregister_blkdev(osdev->major, osdev->name); + osduld_put_device(osdev->osd); + kfree(osdev); + + /* release module ref */ + module_put(THIS_MODULE); + + return count; +} + +static struct class_attribute class_osdblk_attrs[] = { + __ATTR(add, 0200, NULL, class_osdblk_add), + __ATTR(remove, 0200, NULL, class_osdblk_remove), + __ATTR(list, 0444, class_osdblk_list, NULL), + __ATTR_NULL +}; + +static int osdblk_sysfs_init(void) +{ + int ret = 0; + + /* + * create control files in sysfs + * /sys/class/osdblk/... + */ + class_osdblk = kzalloc(sizeof(*class_osdblk), GFP_KERNEL); + if (!class_osdblk) + return -ENOMEM; + + class_osdblk->name = DRV_NAME; + class_osdblk->owner = THIS_MODULE; + class_osdblk->class_release = class_osdblk_release; + class_osdblk->class_attrs = class_osdblk_attrs; + + ret = class_register(class_osdblk); + if (ret) { + kfree(class_osdblk); + class_osdblk = NULL; + printk(PFX "failed to create class osdblk\n"); + return ret; + } + + return 0; +} + +static void osdblk_sysfs_cleanup(void) +{ + if (class_osdblk) + class_destroy(class_osdblk); + class_osdblk = NULL; +} + +static int __init osdblk_init(void) +{ + int rc; + + rc = osdblk_sysfs_init(); + if (rc) + return rc; + + return 0; +} + +static void __exit osdblk_exit(void) +{ + osdblk_sysfs_cleanup(); +} + +module_init(osdblk_init); +module_exit(osdblk_exit); + diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 83650e00632d..99a506f619b7 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -1372,8 +1372,10 @@ try_next_bio: wakeup = (pd->write_congestion_on > 0 && pd->bio_queue_size <= pd->write_congestion_off); spin_unlock(&pd->lock); - if (wakeup) - clear_bdi_congested(&pd->disk->queue->backing_dev_info, WRITE); + if (wakeup) { + clear_bdi_congested(&pd->disk->queue->backing_dev_info, + BLK_RW_ASYNC); + } pkt->sleep_time = max(PACKET_WAIT_TIME, 1); pkt_set_state(pkt, PACKET_WAITING_STATE); @@ -2592,10 +2594,10 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio) spin_lock(&pd->lock); if (pd->write_congestion_on > 0 && pd->bio_queue_size >= pd->write_congestion_on) { - set_bdi_congested(&q->backing_dev_info, WRITE); + set_bdi_congested(&q->backing_dev_info, BLK_RW_ASYNC); do { spin_unlock(&pd->lock); - congestion_wait(WRITE, HZ); + congestion_wait(BLK_RW_ASYNC, HZ); spin_lock(&pd->lock); } while(pd->bio_queue_size > pd->write_congestion_off); } diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 43db3ea15b54..aa1a3d5a3e2b 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -213,7 +213,7 @@ static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, * Only allow the generic SCSI ioctls if the host can support it. */ if (!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_SCSI)) - return -ENOIOCTLCMD; + return -ENOTTY; return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, argp); } @@ -360,6 +360,9 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) blk_queue_max_phys_segments(vblk->disk->queue, vblk->sg_elems-2); blk_queue_max_hw_segments(vblk->disk->queue, vblk->sg_elems-2); + /* No need to bounce any requests */ + blk_queue_bounce_limit(vblk->disk->queue, BLK_BOUNCE_ANY); + /* No real sector limit. */ blk_queue_max_sectors(vblk->disk->queue, -1U); @@ -424,7 +427,12 @@ static unsigned int features[] = { VIRTIO_BLK_F_SCSI, VIRTIO_BLK_F_IDENTIFY }; -static struct virtio_driver virtio_blk = { +/* + * virtio_blk causes spurious section mismatch warning by + * simultaneously referring to a __devinit and a __devexit function. + * Use __refdata to avoid this warning. + */ +static struct virtio_driver __refdata virtio_blk = { .feature_table = features, .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index f08491a3a813..b20abe102a2b 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -390,9 +390,10 @@ static inline void ace_dump_mem(void *base, int len) static void ace_dump_regs(struct ace_device *ace) { - dev_info(ace->dev, " ctrl: %.8x seccnt/cmd: %.4x ver:%.4x\n" - KERN_INFO " status:%.8x mpu_lba:%.8x busmode:%4x\n" - KERN_INFO " error: %.8x cfg_lba:%.8x fatstat:%.4x\n", + dev_info(ace->dev, + " ctrl: %.8x seccnt/cmd: %.4x ver:%.4x\n" + " status:%.8x mpu_lba:%.8x busmode:%4x\n" + " error: %.8x cfg_lba:%.8x fatstat:%.4x\n", ace_in32(ace, ACE_CTRL), ace_in(ace, ACE_SECCNTCMD), ace_in(ace, ACE_VERSION), diff --git a/drivers/block/z2ram.c b/drivers/block/z2ram.c index 4575171e5beb..b2590409f25e 100644 --- a/drivers/block/z2ram.c +++ b/drivers/block/z2ram.c @@ -374,7 +374,7 @@ err: static void __exit z2_exit(void) { int i, j; - blk_unregister_region(MKDEV(Z2RAM_MAJOR, 0), 256); + blk_unregister_region(MKDEV(Z2RAM_MAJOR, 0), Z2MINOR_COUNT); unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME); del_gendisk(z2ram_gendisk); put_disk(z2ram_gendisk); diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 1df9dda2e377..d5cde6d86f89 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -28,7 +28,6 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/sched.h> diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index 72429b6b2fa8..6c32fbf07164 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -81,6 +81,7 @@ static char *serial_version = "4.30"; #include <linux/mm.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/init.h> #include <linux/bitops.h> diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index f3366d3f06cf..2dafc2da0648 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -633,6 +633,7 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/serial.h> +#include <linux/smp_lock.h> #include <linux/major.h> #include <linux/string.h> #include <linux/fcntl.h> diff --git a/drivers/char/epca.c b/drivers/char/epca.c index abef1f7d84fe..ff647ca1c489 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -36,6 +36,7 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/uaccess.h> diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 94e7e3c8c05a..d97779ef72cb 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -552,7 +552,7 @@ static int hvc_chars_in_buffer(struct tty_struct *tty) struct hvc_struct *hp = tty->driver_data; if (!hp) - return -1; + return 0; return hp->n_outbuf; } diff --git a/drivers/char/hw_random/intel-rng.c b/drivers/char/hw_random/intel-rng.c index 5dcbe603eca2..91b53eb1c053 100644 --- a/drivers/char/hw_random/intel-rng.c +++ b/drivers/char/hw_random/intel-rng.c @@ -305,10 +305,11 @@ static int __init intel_init_hw_struct(struct intel_rng_hw *intel_rng_hw, (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)) == BIOS_CNTL_LOCK_ENABLE_MASK) { static __initdata /*const*/ char warning[] = - KERN_WARNING PFX "Firmware space is locked read-only. If you can't or\n" - KERN_WARNING PFX "don't want to disable this in firmware setup, and if\n" - KERN_WARNING PFX "you are certain that your system has a functional\n" - KERN_WARNING PFX "RNG, try using the 'no_fwh_detect' option.\n"; + KERN_WARNING +PFX "Firmware space is locked read-only. If you can't or\n" +PFX "don't want to disable this in firmware setup, and if\n" +PFX "you are certain that your system has a functional\n" +PFX "RNG, try using the 'no_fwh_detect' option.\n"; if (no_fwh_detect) return -ENODEV; diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 4159292e35cf..4f1f4cd670da 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -122,6 +122,7 @@ #include <linux/fs.h> #include <linux/sched.h> #include <linux/serial.h> +#include <linux/smp_lock.h> #include <linux/mm.h> #include <linux/interrupt.h> #include <linux/timer.h> @@ -1478,10 +1479,10 @@ static int __devinit load_firmware(struct pci_dev *pdev, status = inw(base + 0x4); if (status != 0) { dev_warn(&pdev->dev, "Card%d rejected load header:\n" - KERN_WARNING "Address:0x%x\n" - KERN_WARNING "Count:0x%x\n" - KERN_WARNING "Status:0x%x\n", - index + 1, frame->addr, frame->count, status); + "Address:0x%x\n" + "Count:0x%x\n" + "Status:0x%x\n", + index + 1, frame->addr, frame->count, status); goto errrelfw; } outsw(base, frame->data, word_count); @@ -1526,10 +1527,10 @@ static int __devinit load_firmware(struct pci_dev *pdev, status = inw(base + 0x4); if (status != 0) { dev_warn(&pdev->dev, "Card%d rejected verify header:\n" - KERN_WARNING "Address:0x%x\n" - KERN_WARNING "Count:0x%x\n" - KERN_WARNING "Status: 0x%x\n", - index + 1, frame->addr, frame->count, status); + "Address:0x%x\n" + "Count:0x%x\n" + "Status: 0x%x\n", + index + 1, frame->addr, frame->count, status); goto errrelfw; } diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 0c999f5bb3db..ab2f3349c5c4 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/interrupt.h> #include <linux/tty.h> #include <linux/tty_flip.h> diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 65b6ff2442c6..dd0083bbb64a 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -34,6 +34,7 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/major.h> +#include <linux/smp_lock.h> #include <linux/string.h> #include <linux/fcntl.h> #include <linux/ptrace.h> diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 52d953eb30c3..dbf8d52f31d0 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -23,6 +23,7 @@ #include <linux/errno.h> #include <linux/signal.h> #include <linux/sched.h> +#include <linux/smp_lock.h> #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/tty.h> diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index 1c43c8cdee25..c68118efad84 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -97,6 +97,7 @@ #include <linux/slab.h> #include <linux/tty.h> #include <linux/errno.h> +#include <linux/smp_lock.h> #include <linux/string.h> /* used in new tty drivers */ #include <linux/signal.h> /* used in new tty drivers */ #include <linux/if.h> diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c index 2e99158ebb8a..6934025a1ac1 100644 --- a/drivers/char/n_r3964.c +++ b/drivers/char/n_r3964.c @@ -58,6 +58,7 @@ #include <linux/ioport.h> #include <linux/in.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/tty.h> #include <linux/errno.h> #include <linux/string.h> /* used in new tty drivers */ diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 94a5d5020abc..ff47907ff1bf 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -1331,9 +1331,6 @@ handle_newline: static void n_tty_write_wakeup(struct tty_struct *tty) { - /* Write out any echoed characters that are still pending */ - process_echoes(tty); - if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) kill_fasync(&tty->fasync, SIGIO, POLL_OUT); } diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index 574f1c79b6e6..ec58d8c387ff 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -828,7 +828,7 @@ static int receive_data(enum port_type index, struct nozomi *dc) struct port *port = &dc->port[index]; void __iomem *addr = port->dl_addr[port->toggle_dl]; struct tty_struct *tty = tty_port_tty_get(&port->port); - int i; + int i, ret; if (unlikely(!tty)) { DBG1("tty not open for port: %d?", index); @@ -844,12 +844,14 @@ static int receive_data(enum port_type index, struct nozomi *dc) /* disable interrupt in downlink... */ disable_transmit_dl(index, dc); - return 0; + ret = 0; + goto put; } if (unlikely(size == 0)) { dev_err(&dc->pdev->dev, "size == 0?\n"); - return 1; + ret = 1; + goto put; } tty_buffer_request_room(tty, size); @@ -871,8 +873,10 @@ static int receive_data(enum port_type index, struct nozomi *dc) } set_bit(index, &dc->flip); + ret = 1; +put: tty_kref_put(tty); - return 1; + return ret; } /* Debug for interrupts */ @@ -1862,16 +1866,14 @@ static s32 ntty_chars_in_buffer(struct tty_struct *tty) { struct port *port = tty->driver_data; struct nozomi *dc = get_dc_by_tty(tty); - s32 rval; + s32 rval = 0; if (unlikely(!dc || !port)) { - rval = -ENODEV; goto exit_in_buffer; } if (unlikely(!port->port.count)) { dev_err(&dc->pdev->dev, "No tty open?\n"); - rval = -ENODEV; goto exit_in_buffer; } diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c index 569f2f7743a7..674b3ab3587d 100644 --- a/drivers/char/pcmcia/ipwireless/tty.c +++ b/drivers/char/pcmcia/ipwireless/tty.c @@ -320,10 +320,10 @@ static int ipw_chars_in_buffer(struct tty_struct *linux_tty) struct ipw_tty *tty = linux_tty->driver_data; if (!tty) - return -ENODEV; + return 0; if (!tty->open_count) - return -EINVAL; + return 0; return tty->tx_bytes_queued; } diff --git a/drivers/char/pty.c b/drivers/char/pty.c index daebe1ba43d4..3850a68f265a 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -22,6 +22,7 @@ #include <linux/major.h> #include <linux/mm.h> #include <linux/init.h> +#include <linux/smp_lock.h> #include <linux/sysctl.h> #include <linux/device.h> #include <linux/uaccess.h> @@ -51,6 +52,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) return; tty->link->packet = 0; set_bit(TTY_OTHER_CLOSED, &tty->link->flags); + tty_flip_buffer_push(tty->link); wake_up_interruptible(&tty->link->read_wait); wake_up_interruptible(&tty->link->write_wait); if (tty->driver->subtype == PTY_TYPE_MASTER) { @@ -75,114 +77,88 @@ static void pty_close(struct tty_struct *tty, struct file *filp) */ static void pty_unthrottle(struct tty_struct *tty) { - struct tty_struct *o_tty = tty->link; - - if (!o_tty) - return; - - tty_wakeup(o_tty); + tty_wakeup(tty->link); set_bit(TTY_THROTTLED, &tty->flags); } -/* - * WSH 05/24/97: modified to - * (1) use space in tty->flip instead of a shared temp buffer - * The flip buffers aren't being used for a pty, so there's lots - * of space available. The buffer is protected by a per-pty - * semaphore that should almost never come under contention. - * (2) avoid redundant copying for cases where count >> receive_room - * N.B. Calls from user space may now return an error code instead of - * a count. +/** + * pty_space - report space left for writing + * @to: tty we are writing into * - * FIXME: Our pty_write method is called with our ldisc lock held but - * not our partners. We can't just wait on the other one blindly without - * risking deadlocks. At some point when everything has settled down we need - * to look into making pty_write at least able to sleep over an ldisc change. + * The tty buffers allow 64K but we sneak a peak and clip at 8K this + * allows a lot of overspill room for echo and other fun messes to + * be handled properly + */ + +static int pty_space(struct tty_struct *to) +{ + int n = 8192 - to->buf.memory_used; + if (n < 0) + return 0; + return n; +} + +/** + * pty_write - write to a pty + * @tty: the tty we write from + * @buf: kernel buffer of data + * @count: bytes to write * - * The return on no ldisc is a bit counter intuitive but the logic works - * like this. During an ldisc change the other end will flush its buffers. We - * thus return the full length which is identical to the case where we had - * proper locking and happened to queue the bytes just before the flush during - * the ldisc change. + * Our "hardware" write method. Data is coming from the ldisc which + * may be in a non sleeping state. We simply throw this at the other + * end of the link as if we were an IRQ handler receiving stuff for + * the other side of the pty/tty pair. */ + static int pty_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct tty_struct *to = tty->link; - struct tty_ldisc *ld; - int c = count; + int c; - if (!to || tty->stopped) + if (tty->stopped) return 0; - ld = tty_ldisc_ref(to); - - if (ld) { - c = to->receive_room; - if (c > count) - c = count; - ld->ops->receive_buf(to, buf, NULL, c); - tty_ldisc_deref(ld); + + /* This isn't locked but our 8K is quite sloppy so no + big deal */ + + c = pty_space(to); + if (c > count) + c = count; + if (c > 0) { + /* Stuff the data into the input queue of the other end */ + c = tty_insert_flip_string(to, buf, c); + /* And shovel */ + tty_flip_buffer_push(to); + tty_wakeup(tty); } return c; } +/** + * pty_write_room - write space + * @tty: tty we are writing from + * + * Report how many bytes the ldisc can send into the queue for + * the other device. + */ + static int pty_write_room(struct tty_struct *tty) { - struct tty_struct *to = tty->link; - - if (!to || tty->stopped) - return 0; - - return to->receive_room; + return pty_space(tty->link); } -/* - * WSH 05/24/97: Modified for asymmetric MASTER/SLAVE behavior - * The chars_in_buffer() value is used by the ldisc select() function - * to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256). - * The pty driver chars_in_buffer() Master/Slave must behave differently: - * - * The Master side needs to allow typed-ahead commands to accumulate - * while being canonicalized, so we report "our buffer" as empty until - * some threshold is reached, and then report the count. (Any count > - * WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock - * the count returned must be 0 if no canonical data is available to be - * read. (The N_TTY ldisc.chars_in_buffer now knows this.) +/** + * pty_chars_in_buffer - characters currently in our tx queue + * @tty: our tty * - * The Slave side passes all characters in raw mode to the Master side's - * buffer where they can be read immediately, so in this case we can - * return the true count in the buffer. + * Report how much we have in the transmit queue. As everything is + * instantly at the other end this is easy to implement. */ + static int pty_chars_in_buffer(struct tty_struct *tty) { - struct tty_struct *to = tty->link; - struct tty_ldisc *ld; - int count = 0; - - /* We should get the line discipline lock for "tty->link" */ - if (!to) - return 0; - /* We cannot take a sleeping reference here without deadlocking with - an ldisc change - but it doesn't really matter */ - ld = tty_ldisc_ref(to); - if (ld == NULL) - return 0; - - /* The ldisc must report 0 if no characters available to be read */ - if (ld->ops->chars_in_buffer) - count = ld->ops->chars_in_buffer(to); - - tty_ldisc_deref(ld); - - if (tty->driver->subtype == PTY_TYPE_SLAVE) - return count; - - /* Master side driver ... if the other side's read buffer is less than - * half full, return 0 to allow writers to proceed; otherwise return - * the count. This leaves a comfortable margin to avoid overflow, - * and still allows half a buffer's worth of typed-ahead commands. - */ - return (count < N_TTY_BUF_SIZE/2) ? 0 : count; + return 0; } /* Set the lock flag on a pty */ @@ -202,20 +178,10 @@ static void pty_flush_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; unsigned long flags; - struct tty_ldisc *ld; if (!to) return; - ld = tty_ldisc_ref(to); - - /* The other end is changing discipline */ - if (!ld) - return; - - if (ld->ops->flush_buffer) - to->ldisc->ops->flush_buffer(to); - tty_ldisc_deref(ld); - + /* tty_buffer_flush(to); FIXME */ if (to->packet) { spin_lock_irqsave(&tty->ctrl_lock, flags); tty->ctrl_status |= TIOCPKT_FLUSHWRITE; @@ -242,6 +208,7 @@ static int pty_open(struct tty_struct *tty, struct file *filp) clear_bit(TTY_OTHER_CLOSED, &tty->link->flags); set_bit(TTY_THROTTLED, &tty->flags); retval = 0; + tty->low_latency = 1; out: return retval; } diff --git a/drivers/char/rio/rio_linux.c b/drivers/char/rio/rio_linux.c index ce81da5b2da9..d58c2eb07f07 100644 --- a/drivers/char/rio/rio_linux.c +++ b/drivers/char/rio/rio_linux.c @@ -44,6 +44,7 @@ #include <linux/delay.h> #include <linux/pci.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/miscdevice.h> #include <linux/init.h> diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 217660451237..171711acf5cd 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -47,6 +47,7 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/tty_flip.h> +#include <linux/smp_lock.h> #include <linux/spinlock.h> #include <linux/device.h> diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 63d5b628477a..0e29a23ec4c5 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -73,6 +73,7 @@ #include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/serial.h> +#include <linux/smp_lock.h> #include <linux/string.h> #include <linux/fcntl.h> #include <linux/ptrace.h> diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index f1f24f0ee26f..51e7a46787be 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -52,6 +52,7 @@ #include <linux/interrupt.h> #include <linux/serial.h> #include <linux/serialP.h> +#include <linux/smp_lock.h> #include <linux/string.h> #include <linux/fcntl.h> #include <linux/ptrace.h> diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index e72be4190a44..268e17f9ec3f 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -87,6 +87,7 @@ #include <linux/tty_flip.h> #include <linux/mm.h> #include <linux/serial.h> +#include <linux/smp_lock.h> #include <linux/fcntl.h> #include <linux/major.h> #include <linux/delay.h> @@ -1808,10 +1809,10 @@ static int sx_tiocmset(struct tty_struct *tty, struct file *file, if (clear & TIOCM_DTR) port->MSVR &= ~MSVR_DTR; } - spin_lock_irqsave(&bp->lock, flags); + spin_lock(&bp->lock); sx_out(bp, CD186x_CAR, port_No(port)); sx_out(bp, CD186x_MSVR, port->MSVR); - spin_unlock_irqrestore(&bp->lock, flags); + spin_unlock(&bp->lock); spin_unlock_irqrestore(&port->lock, flags); func_exit(); return 0; @@ -1832,11 +1833,11 @@ static int sx_send_break(struct tty_struct *tty, int length) port->break_length = SPECIALIX_TPS / HZ * length; port->COR2 |= COR2_ETC; port->IER |= IER_TXRDY; - spin_lock_irqsave(&bp->lock, flags); + spin_lock(&bp->lock); sx_out(bp, CD186x_CAR, port_No(port)); sx_out(bp, CD186x_COR2, port->COR2); sx_out(bp, CD186x_IER, port->IER); - spin_unlock_irqrestore(&bp->lock, flags); + spin_unlock(&bp->lock); spin_unlock_irqrestore(&port->lock, flags); sx_wait_CCR(bp); spin_lock_irqsave(&bp->lock, flags); @@ -2022,9 +2023,9 @@ static void sx_unthrottle(struct tty_struct *tty) if (sx_crtscts(tty)) port->MSVR |= MSVR_DTR; /* Else clause: see remark in "sx_throttle"... */ - spin_lock_irqsave(&bp->lock, flags); + spin_lock(&bp->lock); sx_out(bp, CD186x_CAR, port_No(port)); - spin_unlock_irqrestore(&bp->lock, flags); + spin_unlock(&bp->lock); if (I_IXOFF(tty)) { spin_unlock_irqrestore(&port->lock, flags); sx_wait_CCR(bp); @@ -2034,9 +2035,9 @@ static void sx_unthrottle(struct tty_struct *tty) sx_wait_CCR(bp); spin_lock_irqsave(&port->lock, flags); } - spin_lock_irqsave(&bp->lock, flags); + spin_lock(&bp->lock); sx_out(bp, CD186x_MSVR, port->MSVR); - spin_unlock_irqrestore(&bp->lock, flags); + spin_unlock(&bp->lock); spin_unlock_irqrestore(&port->lock, flags); func_exit(); @@ -2060,10 +2061,10 @@ static void sx_stop(struct tty_struct *tty) spin_lock_irqsave(&port->lock, flags); port->IER &= ~IER_TXRDY; - spin_lock_irqsave(&bp->lock, flags); + spin_lock(&bp->lock); sx_out(bp, CD186x_CAR, port_No(port)); sx_out(bp, CD186x_IER, port->IER); - spin_unlock_irqrestore(&bp->lock, flags); + spin_unlock(&bp->lock); spin_unlock_irqrestore(&port->lock, flags); func_exit(); @@ -2088,10 +2089,10 @@ static void sx_start(struct tty_struct *tty) spin_lock_irqsave(&port->lock, flags); if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) { port->IER |= IER_TXRDY; - spin_lock_irqsave(&bp->lock, flags); + spin_lock(&bp->lock); sx_out(bp, CD186x_CAR, port_No(port)); sx_out(bp, CD186x_IER, port->IER); - spin_unlock_irqrestore(&bp->lock, flags); + spin_unlock(&bp->lock); } spin_unlock_irqrestore(&port->lock, flags); diff --git a/drivers/char/sx.c b/drivers/char/sx.c index 518f2a25d91e..a81ec4fcf6ff 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -216,6 +216,7 @@ #include <linux/eisa.h> #include <linux/pci.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/bitops.h> diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index afded3a2379c..813552f14884 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -81,6 +81,7 @@ #include <linux/mm.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/delay.h> #include <linux/netdevice.h> #include <linux/vmalloc.h> diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index a2e67e6df3a1..91f20a92fddf 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -62,6 +62,7 @@ #include <linux/mm.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/netdevice.h> #include <linux/vmalloc.h> #include <linux/init.h> diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 6f727e3c53ad..8d4a2a8a0a70 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -52,6 +52,7 @@ #include <linux/mm.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/netdevice.h> #include <linux/vmalloc.h> #include <linux/init.h> diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index ccdd828adcef..b0603b2e5684 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -26,7 +26,6 @@ #include <linux/poll.h> #include <linux/mutex.h> #include <linux/spinlock.h> -#include <linux/smp_lock.h> #include "tpm.h" diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index b24f6c6a1ea3..ad6ba4ed2808 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -21,7 +21,6 @@ #include <linux/module.h> #include <linux/bitops.h> #include <linux/mutex.h> -#include <linux/smp_lock.h> #include <asm/io.h> #include <asm/uaccess.h> diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index 913aa8d3f1c5..acd76b767d4c 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c @@ -21,7 +21,6 @@ #include <linux/proc_fs.h> #include <linux/init.h> #include <linux/module.h> -#include <linux/smp_lock.h> #include <linux/device.h> #include <linux/wait.h> #include <linux/bitops.h> @@ -791,17 +790,20 @@ void tty_ldisc_hangup(struct tty_struct *tty) * N_TTY. */ if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { - /* Avoid racing set_ldisc */ + /* Avoid racing set_ldisc or tty_ldisc_release */ mutex_lock(&tty->ldisc_mutex); - /* Switch back to N_TTY */ - tty_ldisc_halt(tty); - tty_ldisc_wait_idle(tty); - tty_ldisc_reinit(tty); - /* At this point we have a closed ldisc and we want to - reopen it. We could defer this to the next open but - it means auditing a lot of other paths so this is a FIXME */ - WARN_ON(tty_ldisc_open(tty, tty->ldisc)); - tty_ldisc_enable(tty); + if (tty->ldisc) { /* Not yet closed */ + /* Switch back to N_TTY */ + tty_ldisc_halt(tty); + tty_ldisc_wait_idle(tty); + tty_ldisc_reinit(tty); + /* At this point we have a closed ldisc and we want to + reopen it. We could defer this to the next open but + it means auditing a lot of other paths so this is + a FIXME */ + WARN_ON(tty_ldisc_open(tty, tty->ldisc)); + tty_ldisc_enable(tty); + } mutex_unlock(&tty->ldisc_mutex); tty_reset_termios(tty); } @@ -866,6 +868,7 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) tty_ldisc_wait_idle(tty); + mutex_lock(&tty->ldisc_mutex); /* * Now kill off the ldisc */ @@ -876,6 +879,7 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) /* Ensure the next open requests the N_TTY ldisc */ tty_set_termios_ldisc(tty, N_TTY); + mutex_unlock(&tty->ldisc_mutex); /* This will need doing differently if we need to lock */ if (o_tty) diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index 4e862a75f7ff..9769b1149f76 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -267,7 +267,7 @@ int tty_port_block_til_ready(struct tty_port *port, if (retval == 0) port->flags |= ASYNC_NORMAL_ACTIVE; spin_unlock_irqrestore(&port->lock, flags); - return 0; + return retval; } EXPORT_SYMBOL(tty_port_block_til_ready); diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index d94d25c12aa8..c1791a63d99d 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -495,11 +495,15 @@ void vcs_remove_sysfs(int index) int __init vcs_init(void) { + unsigned int i; + if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops)) panic("unable to get major %d for vcs device", VCS_MAJOR); vc_class = class_create(THIS_MODULE, "vc"); device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs"); device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa"); + for (i = 0; i < MIN_NR_CONSOLES; i++) + vcs_make_sysfs(i); return 0; } diff --git a/drivers/char/vt.c b/drivers/char/vt.c index d9113b4c76e3..404f4c1ee431 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -89,6 +89,7 @@ #include <linux/mutex.h> #include <linux/vt_kern.h> #include <linux/selection.h> +#include <linux/smp_lock.h> #include <linux/tiocl.h> #include <linux/kbd_kern.h> #include <linux/consolemap.h> @@ -769,14 +770,12 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ visual_init(vc, currcons, 1); if (!*vc->vc_uni_pagedir_loc) con_set_default_unimap(vc); - if (!vc->vc_kmalloced) - vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); + vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); if (!vc->vc_screenbuf) { kfree(vc); vc_cons[currcons].d = NULL; return -ENOMEM; } - vc->vc_kmalloced = 1; vc_init(vc, vc->vc_rows, vc->vc_cols, 1); vcs_make_sysfs(currcons); atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, ¶m); @@ -912,10 +911,8 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, if (new_scr_end > new_origin) scr_memsetw((void *)new_origin, vc->vc_video_erase_char, new_scr_end - new_origin); - if (vc->vc_kmalloced) - kfree(vc->vc_screenbuf); + kfree(vc->vc_screenbuf); vc->vc_screenbuf = newscreen; - vc->vc_kmalloced = 1; vc->vc_screenbuf_size = new_screen_size; set_origin(vc); @@ -994,8 +991,7 @@ void vc_deallocate(unsigned int currcons) vc->vc_sw->con_deinit(vc); put_pid(vc->vt_pid); module_put(vc->vc_sw->owner); - if (vc->vc_kmalloced) - kfree(vc->vc_screenbuf); + kfree(vc->vc_screenbuf); if (currcons >= MIN_NR_CONSOLES) kfree(vc); vc_cons[currcons].d = NULL; @@ -2880,7 +2876,6 @@ static int __init con_init(void) INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); visual_init(vc, currcons, 1); vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT); - vc->vc_kmalloced = 0; vc_init(vc, vc->vc_rows, vc->vc_cols, currcons || !vc->vc_sw->con_save_screen); } diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 7539bed0f7e0..95189f288f8c 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -25,6 +25,7 @@ #include <linux/console.h> #include <linux/consolemap.h> #include <linux/signal.h> +#include <linux/smp_lock.h> #include <linux/timex.h> #include <asm/io.h> diff --git a/drivers/connector/cn_queue.c b/drivers/connector/cn_queue.c index c769ef269fb5..408c2af25d50 100644 --- a/drivers/connector/cn_queue.c +++ b/drivers/connector/cn_queue.c @@ -1,7 +1,7 @@ /* * cn_queue.c * - * 2004-2005 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net> * All rights reserved. * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index fd336c5a9057..08b2500f21ec 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -1,7 +1,7 @@ /* * connector.c * - * 2004-2005 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net> * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ #include <net/sock.h> MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); +MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>"); MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector."); static u32 cn_idx = CN_IDX_CONNECTOR; diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 6e2ec0b18948..b90eda8b3440 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -761,6 +761,10 @@ static struct kobj_type ktype_cpufreq = { * cpufreq_add_dev - add a CPU device * * Adds the cpufreq interface for a CPU device. + * + * The Oracle says: try running cpufreq registration/unregistration concurrently + * with with cpu hotplugging and all hell will break loose. Tried to clean this + * mess up, but more thorough testing is needed. - Mathieu */ static int cpufreq_add_dev(struct sys_device *sys_dev) { @@ -772,9 +776,6 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) struct sys_device *cpu_sys_dev; unsigned long flags; unsigned int j; -#ifdef CONFIG_SMP - struct cpufreq_policy *managed_policy; -#endif if (cpu_is_offline(cpu)) return 0; @@ -804,15 +805,12 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) goto nomem_out; } if (!alloc_cpumask_var(&policy->cpus, GFP_KERNEL)) { - kfree(policy); ret = -ENOMEM; - goto nomem_out; + goto err_free_policy; } if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL)) { - free_cpumask_var(policy->cpus); - kfree(policy); ret = -ENOMEM; - goto nomem_out; + goto err_free_cpumask; } policy->cpu = cpu; @@ -820,7 +818,8 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) /* Initially set CPU itself as the policy_cpu */ per_cpu(policy_cpu, cpu) = cpu; - lock_policy_rwsem_write(cpu); + ret = (lock_policy_rwsem_write(cpu) < 0); + WARN_ON(ret); init_completion(&policy->kobj_unregister); INIT_WORK(&policy->update, handle_update); @@ -833,7 +832,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) ret = cpufreq_driver->init(policy); if (ret) { dprintk("initialization failed\n"); - goto err_out; + goto err_unlock_policy; } policy->user_policy.min = policy->min; policy->user_policy.max = policy->max; @@ -852,21 +851,29 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) #endif for_each_cpu(j, policy->cpus) { + struct cpufreq_policy *managed_policy; + if (cpu == j) continue; /* Check for existing affected CPUs. * They may not be aware of it due to CPU Hotplug. */ - managed_policy = cpufreq_cpu_get(j); /* FIXME: Where is this released? What about error paths? */ + managed_policy = cpufreq_cpu_get(j); if (unlikely(managed_policy)) { /* Set proper policy_cpu */ unlock_policy_rwsem_write(cpu); per_cpu(policy_cpu, cpu) = managed_policy->cpu; - if (lock_policy_rwsem_write(cpu) < 0) - goto err_out_driver_exit; + if (lock_policy_rwsem_write(cpu) < 0) { + /* Should not go through policy unlock path */ + if (cpufreq_driver->exit) + cpufreq_driver->exit(policy); + ret = -EBUSY; + cpufreq_cpu_put(managed_policy); + goto err_free_cpumask; + } spin_lock_irqsave(&cpufreq_driver_lock, flags); cpumask_copy(managed_policy->cpus, policy->cpus); @@ -877,12 +884,14 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) ret = sysfs_create_link(&sys_dev->kobj, &managed_policy->kobj, "cpufreq"); - if (ret) - goto err_out_driver_exit; - - cpufreq_debug_enable_ratelimit(); - ret = 0; - goto err_out_driver_exit; /* call driver->exit() */ + if (!ret) + cpufreq_cpu_put(managed_policy); + /* + * Success. We only needed to be added to the mask. + * Call driver->exit() because only the cpu parent of + * the kobj needed to call init(). + */ + goto out_driver_exit; /* call driver->exit() */ } } #endif @@ -892,25 +901,25 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, &sys_dev->kobj, "cpufreq"); if (ret) - goto err_out_driver_exit; + goto out_driver_exit; /* set up files for this cpu device */ drv_attr = cpufreq_driver->attr; while ((drv_attr) && (*drv_attr)) { ret = sysfs_create_file(&policy->kobj, &((*drv_attr)->attr)); if (ret) - goto err_out_driver_exit; + goto err_out_kobj_put; drv_attr++; } if (cpufreq_driver->get) { ret = sysfs_create_file(&policy->kobj, &cpuinfo_cur_freq.attr); if (ret) - goto err_out_driver_exit; + goto err_out_kobj_put; } if (cpufreq_driver->target) { ret = sysfs_create_file(&policy->kobj, &scaling_cur_freq.attr); if (ret) - goto err_out_driver_exit; + goto err_out_kobj_put; } spin_lock_irqsave(&cpufreq_driver_lock, flags); @@ -922,18 +931,22 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) /* symlink affected CPUs */ for_each_cpu(j, policy->cpus) { + struct cpufreq_policy *managed_policy; + if (j == cpu) continue; if (!cpu_online(j)) continue; dprintk("CPU %u already managed, adding link\n", j); - cpufreq_cpu_get(cpu); + managed_policy = cpufreq_cpu_get(cpu); cpu_sys_dev = get_cpu_sysdev(j); ret = sysfs_create_link(&cpu_sys_dev->kobj, &policy->kobj, "cpufreq"); - if (ret) + if (ret) { + cpufreq_cpu_put(managed_policy); goto err_out_unregister; + } } policy->governor = NULL; /* to assure that the starting sequence is @@ -965,17 +978,20 @@ err_out_unregister: per_cpu(cpufreq_cpu_data, j) = NULL; spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +err_out_kobj_put: kobject_put(&policy->kobj); wait_for_completion(&policy->kobj_unregister); -err_out_driver_exit: +out_driver_exit: if (cpufreq_driver->exit) cpufreq_driver->exit(policy); -err_out: +err_unlock_policy: unlock_policy_rwsem_write(cpu); +err_free_cpumask: + free_cpumask_var(policy->cpus); +err_free_policy: kfree(policy); - nomem_out: module_put(cpufreq_driver->owner); module_out: @@ -1070,8 +1086,6 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) spin_unlock_irqrestore(&cpufreq_driver_lock, flags); #endif - unlock_policy_rwsem_write(cpu); - if (cpufreq_driver->target) __cpufreq_governor(data, CPUFREQ_GOV_STOP); @@ -1088,6 +1102,8 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) if (cpufreq_driver->exit) cpufreq_driver->exit(data); + unlock_policy_rwsem_write(cpu); + free_cpumask_var(data->related_cpus); free_cpumask_var(data->cpus); kfree(data); diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 7fc58af748b4..57490502b21c 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -63,22 +63,20 @@ struct cpu_dbs_info_s { unsigned int down_skip; unsigned int requested_freq; int cpu; - unsigned int enable:1; + /* + * percpu mutex that serializes governor limit change with + * do_dbs_timer invocation. We do not want do_dbs_timer to run + * when user is changing the governor or limits. + */ + struct mutex timer_mutex; }; static DEFINE_PER_CPU(struct cpu_dbs_info_s, cpu_dbs_info); static unsigned int dbs_enable; /* number of CPUs using this policy */ /* - * DEADLOCK ALERT! There is a ordering requirement between cpu_hotplug - * lock and dbs_mutex. cpu_hotplug lock should always be held before - * dbs_mutex. If any function that can potentially take cpu_hotplug lock - * (like __cpufreq_driver_target()) is being called with dbs_mutex taken, then - * cpu_hotplug lock should be taken before that. Note that cpu_hotplug lock - * is recursive for the same process. -Venki - * DEADLOCK ALERT! (2) : do_dbs_timer() must not take the dbs_mutex, because it - * would deadlock with cancel_delayed_work_sync(), which is needed for proper - * raceless workqueue teardown. + * dbs_mutex protects data in dbs_tuners_ins from concurrent changes on + * different CPUs. It protects dbs_enable in governor start/stop. */ static DEFINE_MUTEX(dbs_mutex); @@ -143,9 +141,6 @@ dbs_cpufreq_notifier(struct notifier_block *nb, unsigned long val, struct cpufreq_policy *policy; - if (!this_dbs_info->enable) - return 0; - policy = this_dbs_info->cur_policy; /* @@ -488,18 +483,12 @@ static void do_dbs_timer(struct work_struct *work) delay -= jiffies % delay; - if (lock_policy_rwsem_write(cpu) < 0) - return; - - if (!dbs_info->enable) { - unlock_policy_rwsem_write(cpu); - return; - } + mutex_lock(&dbs_info->timer_mutex); dbs_check_cpu(dbs_info); queue_delayed_work_on(cpu, kconservative_wq, &dbs_info->work, delay); - unlock_policy_rwsem_write(cpu); + mutex_unlock(&dbs_info->timer_mutex); } static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) @@ -508,7 +497,6 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); delay -= jiffies % delay; - dbs_info->enable = 1; INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer); queue_delayed_work_on(dbs_info->cpu, kconservative_wq, &dbs_info->work, delay); @@ -516,7 +504,6 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) { - dbs_info->enable = 0; cancel_delayed_work_sync(&dbs_info->work); } @@ -535,9 +522,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, if ((!cpu_online(cpu)) || (!policy->cur)) return -EINVAL; - if (this_dbs_info->enable) /* Already enabled */ - break; - mutex_lock(&dbs_mutex); rc = sysfs_create_group(&policy->kobj, &dbs_attr_group); @@ -561,6 +545,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, this_dbs_info->down_skip = 0; this_dbs_info->requested_freq = policy->cur; + mutex_init(&this_dbs_info->timer_mutex); dbs_enable++; /* * Start the timerschedule work, when this governor @@ -590,17 +575,19 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, &dbs_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); } - dbs_timer_init(this_dbs_info); - mutex_unlock(&dbs_mutex); + dbs_timer_init(this_dbs_info); + break; case CPUFREQ_GOV_STOP: - mutex_lock(&dbs_mutex); dbs_timer_exit(this_dbs_info); + + mutex_lock(&dbs_mutex); sysfs_remove_group(&policy->kobj, &dbs_attr_group); dbs_enable--; + mutex_destroy(&this_dbs_info->timer_mutex); /* * Stop the timerschedule work, when this governor @@ -616,7 +603,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, break; case CPUFREQ_GOV_LIMITS: - mutex_lock(&dbs_mutex); + mutex_lock(&this_dbs_info->timer_mutex); if (policy->max < this_dbs_info->cur_policy->cur) __cpufreq_driver_target( this_dbs_info->cur_policy, @@ -625,7 +612,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, __cpufreq_driver_target( this_dbs_info->cur_policy, policy->min, CPUFREQ_RELATION_L); - mutex_unlock(&dbs_mutex); + mutex_unlock(&this_dbs_info->timer_mutex); break; } diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 1911d1729353..d6ba14276bb1 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -70,23 +70,21 @@ struct cpu_dbs_info_s { unsigned int freq_lo_jiffies; unsigned int freq_hi_jiffies; int cpu; - unsigned int enable:1, - sample_type:1; + unsigned int sample_type:1; + /* + * percpu mutex that serializes governor limit change with + * do_dbs_timer invocation. We do not want do_dbs_timer to run + * when user is changing the governor or limits. + */ + struct mutex timer_mutex; }; static DEFINE_PER_CPU(struct cpu_dbs_info_s, cpu_dbs_info); static unsigned int dbs_enable; /* number of CPUs using this policy */ /* - * DEADLOCK ALERT! There is a ordering requirement between cpu_hotplug - * lock and dbs_mutex. cpu_hotplug lock should always be held before - * dbs_mutex. If any function that can potentially take cpu_hotplug lock - * (like __cpufreq_driver_target()) is being called with dbs_mutex taken, then - * cpu_hotplug lock should be taken before that. Note that cpu_hotplug lock - * is recursive for the same process. -Venki - * DEADLOCK ALERT! (2) : do_dbs_timer() must not take the dbs_mutex, because it - * would deadlock with cancel_delayed_work_sync(), which is needed for proper - * raceless workqueue teardown. + * dbs_mutex protects data in dbs_tuners_ins from concurrent changes on + * different CPUs. It protects dbs_enable in governor start/stop. */ static DEFINE_MUTEX(dbs_mutex); @@ -192,13 +190,18 @@ static unsigned int powersave_bias_target(struct cpufreq_policy *policy, return freq_hi; } +static void ondemand_powersave_bias_init_cpu(int cpu) +{ + struct cpu_dbs_info_s *dbs_info = &per_cpu(cpu_dbs_info, cpu); + dbs_info->freq_table = cpufreq_frequency_get_table(cpu); + dbs_info->freq_lo = 0; +} + static void ondemand_powersave_bias_init(void) { int i; for_each_online_cpu(i) { - struct cpu_dbs_info_s *dbs_info = &per_cpu(cpu_dbs_info, i); - dbs_info->freq_table = cpufreq_frequency_get_table(i); - dbs_info->freq_lo = 0; + ondemand_powersave_bias_init_cpu(i); } } @@ -240,12 +243,10 @@ static ssize_t store_sampling_rate(struct cpufreq_policy *unused, unsigned int input; int ret; ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; mutex_lock(&dbs_mutex); - if (ret != 1) { - mutex_unlock(&dbs_mutex); - return -EINVAL; - } dbs_tuners_ins.sampling_rate = max(input, min_sampling_rate); mutex_unlock(&dbs_mutex); @@ -259,13 +260,12 @@ static ssize_t store_up_threshold(struct cpufreq_policy *unused, int ret; ret = sscanf(buf, "%u", &input); - mutex_lock(&dbs_mutex); if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || input < MIN_FREQUENCY_UP_THRESHOLD) { - mutex_unlock(&dbs_mutex); return -EINVAL; } + mutex_lock(&dbs_mutex); dbs_tuners_ins.up_threshold = input; mutex_unlock(&dbs_mutex); @@ -363,9 +363,6 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) struct cpufreq_policy *policy; unsigned int j; - if (!this_dbs_info->enable) - return; - this_dbs_info->freq_lo = 0; policy = this_dbs_info->cur_policy; @@ -493,14 +490,7 @@ static void do_dbs_timer(struct work_struct *work) int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); delay -= jiffies % delay; - - if (lock_policy_rwsem_write(cpu) < 0) - return; - - if (!dbs_info->enable) { - unlock_policy_rwsem_write(cpu); - return; - } + mutex_lock(&dbs_info->timer_mutex); /* Common NORMAL_SAMPLE setup */ dbs_info->sample_type = DBS_NORMAL_SAMPLE; @@ -517,7 +507,7 @@ static void do_dbs_timer(struct work_struct *work) dbs_info->freq_lo, CPUFREQ_RELATION_H); } queue_delayed_work_on(cpu, kondemand_wq, &dbs_info->work, delay); - unlock_policy_rwsem_write(cpu); + mutex_unlock(&dbs_info->timer_mutex); } static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) @@ -526,8 +516,6 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); delay -= jiffies % delay; - dbs_info->enable = 1; - ondemand_powersave_bias_init(); dbs_info->sample_type = DBS_NORMAL_SAMPLE; INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer); queue_delayed_work_on(dbs_info->cpu, kondemand_wq, &dbs_info->work, @@ -536,7 +524,6 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) { - dbs_info->enable = 0; cancel_delayed_work_sync(&dbs_info->work); } @@ -555,19 +542,15 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, if ((!cpu_online(cpu)) || (!policy->cur)) return -EINVAL; - if (this_dbs_info->enable) /* Already enabled */ - break; - mutex_lock(&dbs_mutex); - dbs_enable++; rc = sysfs_create_group(&policy->kobj, &dbs_attr_group); if (rc) { - dbs_enable--; mutex_unlock(&dbs_mutex); return rc; } + dbs_enable++; for_each_cpu(j, policy->cpus) { struct cpu_dbs_info_s *j_dbs_info; j_dbs_info = &per_cpu(cpu_dbs_info, j); @@ -581,6 +564,8 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, } } this_dbs_info->cpu = cpu; + ondemand_powersave_bias_init_cpu(cpu); + mutex_init(&this_dbs_info->timer_mutex); /* * Start the timerschedule work, when this governor * is used for first time @@ -598,29 +583,31 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, max(min_sampling_rate, latency * LATENCY_MULTIPLIER); } - dbs_timer_init(this_dbs_info); - mutex_unlock(&dbs_mutex); + + dbs_timer_init(this_dbs_info); break; case CPUFREQ_GOV_STOP: - mutex_lock(&dbs_mutex); dbs_timer_exit(this_dbs_info); + + mutex_lock(&dbs_mutex); sysfs_remove_group(&policy->kobj, &dbs_attr_group); + mutex_destroy(&this_dbs_info->timer_mutex); dbs_enable--; mutex_unlock(&dbs_mutex); break; case CPUFREQ_GOV_LIMITS: - mutex_lock(&dbs_mutex); + mutex_lock(&this_dbs_info->timer_mutex); if (policy->max < this_dbs_info->cur_policy->cur) __cpufreq_driver_target(this_dbs_info->cur_policy, policy->max, CPUFREQ_RELATION_H); else if (policy->min > this_dbs_info->cur_policy->cur) __cpufreq_driver_target(this_dbs_info->cur_policy, policy->min, CPUFREQ_RELATION_L); - mutex_unlock(&dbs_mutex); + mutex_unlock(&this_dbs_info->timer_mutex); break; } return 0; diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 858fe6037223..24964c1d0af9 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -970,7 +970,7 @@ static void amd64_read_dct_base_mask(struct amd64_pvt *pvt) } for (cs = 0; cs < pvt->num_dcsm; cs++) { - reg = K8_DCSB0 + (cs * 4); + reg = K8_DCSM0 + (cs * 4); err = pci_read_config_dword(pvt->dram_f2_ctl, reg, &pvt->dcsm0[cs]); if (unlikely(err)) diff --git a/drivers/gpio/vr41xx_giu.c b/drivers/gpio/vr41xx_giu.c index b70e06133e78..b16c9a8c03f5 100644 --- a/drivers/gpio/vr41xx_giu.c +++ b/drivers/gpio/vr41xx_giu.c @@ -29,7 +29,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/smp_lock.h> #include <linux/spinlock.h> #include <linux/types.h> diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index a6f73f1e99d9..3da9cfa31848 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -1090,6 +1090,8 @@ int drm_helper_resume_force_mode(struct drm_device *dev) if (ret == false) DRM_ERROR("failed to set mode on crtc %p\n", crtc); } + /* disable the unused connectors while restoring the modesetting */ + drm_helper_disable_unused_functions(dev); return 0; } EXPORT_SYMBOL(drm_helper_resume_force_mode); diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 2960b6d73456..9903f270e440 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -101,6 +101,10 @@ int drm_debugfs_create_files(struct drm_info_list *files, int count, continue; tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL); + if (tmp == NULL) { + ret = -1; + goto fail; + } ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO, root, tmp, &drm_debugfs_fops); if (!ent) { diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 8104ecaea26f..ffe8f4394d50 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -134,26 +134,29 @@ drm_gem_object_alloc(struct drm_device *dev, size_t size) BUG_ON((size & (PAGE_SIZE - 1)) != 0); obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) + goto free; obj->dev = dev; obj->filp = shmem_file_setup("drm mm object", size, VM_NORESERVE); - if (IS_ERR(obj->filp)) { - kfree(obj); - return NULL; - } + if (IS_ERR(obj->filp)) + goto free; kref_init(&obj->refcount); kref_init(&obj->handlecount); obj->size = size; if (dev->driver->gem_init_object != NULL && dev->driver->gem_init_object(obj) != 0) { - fput(obj->filp); - kfree(obj); - return NULL; + goto fput; } atomic_inc(&dev->object_count); atomic_add(obj->size, &dev->object_memory); return obj; +fput: + fput(obj->filp); +free: + kfree(obj); + return NULL; } EXPORT_SYMBOL(drm_gem_object_alloc); diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 155a5bbce680..55bb8a82d612 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -489,7 +489,7 @@ int drm_put_minor(struct drm_minor **minor_p) */ void drm_put_dev(struct drm_device *dev) { - struct drm_driver *driver = dev->driver; + struct drm_driver *driver; struct drm_map_list *r_list, *list_temp; DRM_DEBUG("\n"); @@ -498,6 +498,7 @@ void drm_put_dev(struct drm_device *dev) DRM_ERROR("cleanup called no dev\n"); return; } + driver = dev->driver; drm_vblank_cleanup(dev); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index f112c769d533..8c4783180bf6 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -846,7 +846,7 @@ static int i915_set_status_page(struct drm_device *dev, void *data, return 0; } - printk(KERN_DEBUG "set status page addr 0x%08x\n", (u32)hws->addr); + DRM_DEBUG("set status page addr 0x%08x\n", (u32)hws->addr); dev_priv->status_gfx_addr = hws->addr & (0x1ffff<<12); @@ -885,8 +885,8 @@ static int i915_set_status_page(struct drm_device *dev, void *data, * some RAM for the framebuffer at early boot. This code figures out * how much was set aside so we can use it for our own purposes. */ -static int i915_probe_agp(struct drm_device *dev, unsigned long *aperture_size, - unsigned long *preallocated_size) +static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size, + uint32_t *preallocated_size) { struct pci_dev *bridge_dev; u16 tmp = 0; @@ -984,10 +984,11 @@ static int i915_probe_agp(struct drm_device *dev, unsigned long *aperture_size, return 0; } -static int i915_load_modeset_init(struct drm_device *dev) +static int i915_load_modeset_init(struct drm_device *dev, + unsigned long prealloc_size, + unsigned long agp_size) { struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long agp_size, prealloc_size; int fb_bar = IS_I9XX(dev) ? 2 : 0; int ret = 0; @@ -1002,10 +1003,6 @@ static int i915_load_modeset_init(struct drm_device *dev) if (IS_I965G(dev) || IS_G33(dev)) dev_priv->cursor_needs_physical = false; - ret = i915_probe_agp(dev, &agp_size, &prealloc_size); - if (ret) - goto out; - /* Basic memrange allocator for stolen space (aka vram) */ drm_mm_init(&dev_priv->vram, 0, prealloc_size); @@ -1082,6 +1079,44 @@ void i915_master_destroy(struct drm_device *dev, struct drm_master *master) master->driver_priv = NULL; } +static void i915_get_mem_freq(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u32 tmp; + + if (!IS_IGD(dev)) + return; + + tmp = I915_READ(CLKCFG); + + switch (tmp & CLKCFG_FSB_MASK) { + case CLKCFG_FSB_533: + dev_priv->fsb_freq = 533; /* 133*4 */ + break; + case CLKCFG_FSB_800: + dev_priv->fsb_freq = 800; /* 200*4 */ + break; + case CLKCFG_FSB_667: + dev_priv->fsb_freq = 667; /* 167*4 */ + break; + case CLKCFG_FSB_400: + dev_priv->fsb_freq = 400; /* 100*4 */ + break; + } + + switch (tmp & CLKCFG_MEM_MASK) { + case CLKCFG_MEM_533: + dev_priv->mem_freq = 533; + break; + case CLKCFG_MEM_667: + dev_priv->mem_freq = 667; + break; + case CLKCFG_MEM_800: + dev_priv->mem_freq = 800; + break; + } +} + /** * i915_driver_load - setup chip and create an initial config * @dev: DRM device @@ -1098,6 +1133,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) struct drm_i915_private *dev_priv = dev->dev_private; resource_size_t base, size; int ret = 0, mmio_bar = IS_I9XX(dev) ? 0 : 1; + uint32_t agp_size, prealloc_size; /* i915 has 4 more counters */ dev->counters += 4; @@ -1146,9 +1182,22 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) "performance may suffer.\n"); } + ret = i915_probe_agp(dev, &agp_size, &prealloc_size); + if (ret) + goto out_iomapfree; + /* enable GEM by default */ dev_priv->has_gem = 1; + if (prealloc_size > agp_size * 3 / 4) { + DRM_ERROR("Detected broken video BIOS with %d/%dkB of video " + "memory stolen.\n", + prealloc_size / 1024, agp_size / 1024); + DRM_ERROR("Disabling GEM. (try reducing stolen memory or " + "updating the BIOS to fix).\n"); + dev_priv->has_gem = 0; + } + dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ if (IS_G4X(dev) || IS_IGDNG(dev)) { @@ -1165,6 +1214,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto out_iomapfree; } + i915_get_mem_freq(dev); + /* On the 945G/GM, the chipset reports the MSI capability on the * integrated graphics even though the support isn't actually there * according to the published specs. It doesn't appear to function @@ -1180,6 +1231,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) pci_enable_msi(dev->pdev); spin_lock_init(&dev_priv->user_irq_lock); + spin_lock_init(&dev_priv->error_lock); dev_priv->user_irq_refcount = 0; ret = drm_vblank_init(dev, I915_NUM_PIPE); @@ -1190,7 +1242,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) } if (drm_core_check_feature(dev, DRIVER_MODESET)) { - ret = i915_load_modeset_init(dev); + ret = i915_load_modeset_init(dev, prealloc_size, agp_size); if (ret < 0) { DRM_ERROR("failed to init modeset\n"); goto out_rmmap; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index e3cb4025e323..fc4b68aa2d05 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -35,6 +35,7 @@ #include "drm_pciids.h" #include <linux/console.h> +#include "drm_crtc_helper.h" static unsigned int i915_modeset = -1; module_param_named(modeset, i915_modeset, int, 0400); @@ -57,8 +58,8 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state) struct drm_i915_private *dev_priv = dev->dev_private; if (!dev || !dev_priv) { - printk(KERN_ERR "dev: %p, dev_priv: %p\n", dev, dev_priv); - printk(KERN_ERR "DRM not initialized, aborting suspend.\n"); + DRM_ERROR("dev: %p, dev_priv: %p\n", dev, dev_priv); + DRM_ERROR("DRM not initialized, aborting suspend.\n"); return -ENODEV; } @@ -115,6 +116,10 @@ static int i915_resume(struct drm_device *dev) drm_irq_install(dev); } + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + /* Resume the modeset for every activated CRTC */ + drm_helper_resume_force_mode(dev); + } return ret; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index bb4c2d387b6c..d08752875885 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -133,6 +133,22 @@ struct sdvo_device_mapping { u8 initialized; }; +struct drm_i915_error_state { + u32 eir; + u32 pgtbl_er; + u32 pipeastat; + u32 pipebstat; + u32 ipeir; + u32 ipehr; + u32 instdone; + u32 acthd; + u32 instpm; + u32 instps; + u32 instdone1; + u32 seqno; + struct timeval time; +}; + typedef struct drm_i915_private { struct drm_device *dev; @@ -209,6 +225,11 @@ typedef struct drm_i915_private { int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */ int num_fence_regs; /* 8 on pre-965, 16 otherwise */ + unsigned int fsb_freq, mem_freq; + + spinlock_t error_lock; + struct drm_i915_error_state *first_error; + /* Register state */ u8 saveLBB; u32 saveDSPACNTR; @@ -468,9 +489,6 @@ struct drm_i915_gem_object { */ int fence_reg; - /** Boolean whether this object has a valid gtt offset. */ - int gtt_bound; - /** How many users have pinned this object in GTT space */ int pin_count; @@ -655,6 +673,7 @@ void i915_gem_free_object(struct drm_gem_object *obj); int i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment); void i915_gem_object_unpin(struct drm_gem_object *obj); int i915_gem_object_unbind(struct drm_gem_object *obj); +void i915_gem_release_mmap(struct drm_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); uint32_t i915_get_gem_seqno(struct drm_device *dev); int i915_gem_object_get_fence_reg(struct drm_gem_object *obj); @@ -870,6 +889,8 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IGDNG(dev)) #define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_IGDNG(dev)) #define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev)) +/* dsparb controlled by hw only */ +#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev)) #define PRIMARY_RINGBUFFER_SIZE (128*1024) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 876b65cb7629..5bf420378b6d 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1252,6 +1252,31 @@ out_free_list: return ret; } +/** + * i915_gem_release_mmap - remove physical page mappings + * @obj: obj in question + * + * Preserve the reservation of the mmaping with the DRM core code, but + * relinquish ownership of the pages back to the system. + * + * It is vital that we remove the page mapping if we have mapped a tiled + * object through the GTT and then lose the fence register due to + * resource pressure. Similarly if the object has been moved out of the + * aperture, than pages mapped into userspace must be revoked. Removing the + * mapping will then trigger a page fault on the next user access, allowing + * fixup by i915_gem_fault(). + */ +void +i915_gem_release_mmap(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + + if (dev->dev_mapping) + unmap_mapping_range(dev->dev_mapping, + obj_priv->mmap_offset, obj->size, 1); +} + static void i915_gem_free_mmap_offset(struct drm_gem_object *obj) { @@ -1861,7 +1886,6 @@ i915_gem_object_unbind(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; struct drm_i915_gem_object *obj_priv = obj->driver_private; - loff_t offset; int ret = 0; #if WATCH_BUF @@ -1898,9 +1922,7 @@ i915_gem_object_unbind(struct drm_gem_object *obj) BUG_ON(obj_priv->active); /* blow away mappings if mapped through GTT */ - offset = ((loff_t) obj->map_list.hash.key) << PAGE_SHIFT; - if (dev->dev_mapping) - unmap_mapping_range(dev->dev_mapping, offset, obj->size, 1); + i915_gem_release_mmap(obj); if (obj_priv->fence_reg != I915_FENCE_REG_NONE) i915_gem_clear_fence_reg(obj); @@ -2222,7 +2244,6 @@ try_again: /* None available, try to steal one or wait for a user to finish */ if (i == dev_priv->num_fence_regs) { uint32_t seqno = dev_priv->mm.next_gem_seqno; - loff_t offset; if (avail == 0) return -ENOSPC; @@ -2274,10 +2295,7 @@ try_again: * Zap this virtual mapping so we can set up a fence again * for this object next time we need it. */ - offset = ((loff_t) reg->obj->map_list.hash.key) << PAGE_SHIFT; - if (dev->dev_mapping) - unmap_mapping_range(dev->dev_mapping, offset, - reg->obj->size, 1); + i915_gem_release_mmap(reg->obj); old_obj_priv->fence_reg = I915_FENCE_REG_NONE; } diff --git a/drivers/gpu/drm/i915/i915_gem_debugfs.c b/drivers/gpu/drm/i915/i915_gem_debugfs.c index 28146e405e87..9a44bfcb8139 100644 --- a/drivers/gpu/drm/i915/i915_gem_debugfs.c +++ b/drivers/gpu/drm/i915/i915_gem_debugfs.c @@ -75,11 +75,10 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data) case ACTIVE_LIST: seq_printf(m, "Active:\n"); lock = &dev_priv->mm.active_list_lock; - spin_lock(lock); head = &dev_priv->mm.active_list; break; case INACTIVE_LIST: - seq_printf(m, "Inctive:\n"); + seq_printf(m, "Inactive:\n"); head = &dev_priv->mm.inactive_list; break; case FLUSHING_LIST: @@ -91,6 +90,8 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data) return 0; } + if (lock) + spin_lock(lock); list_for_each_entry(obj_priv, head, list) { struct drm_gem_object *obj = obj_priv->obj; @@ -104,7 +105,10 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data) if (obj->name) seq_printf(m, " (name: %d)", obj->name); if (obj_priv->fence_reg != I915_FENCE_REG_NONE) - seq_printf(m, " (fence: %d)\n", obj_priv->fence_reg); + seq_printf(m, " (fence: %d)", obj_priv->fence_reg); + if (obj_priv->gtt_space != NULL) + seq_printf(m, " (gtt_offset: %08x)", obj_priv->gtt_offset); + seq_printf(m, "\n"); } @@ -323,6 +327,39 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data) return 0; } +static int i915_error_state(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_error_state *error; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->error_lock, flags); + if (!dev_priv->first_error) { + seq_printf(m, "no error state collected\n"); + goto out; + } + + error = dev_priv->first_error; + + seq_printf(m, "EIR: 0x%08x\n", error->eir); + seq_printf(m, " PGTBL_ER: 0x%08x\n", error->pgtbl_er); + seq_printf(m, " INSTPM: 0x%08x\n", error->instpm); + seq_printf(m, " IPEIR: 0x%08x\n", error->ipeir); + seq_printf(m, " IPEHR: 0x%08x\n", error->ipehr); + seq_printf(m, " INSTDONE: 0x%08x\n", error->instdone); + seq_printf(m, " ACTHD: 0x%08x\n", error->acthd); + if (IS_I965G(dev)) { + seq_printf(m, " INSTPS: 0x%08x\n", error->instps); + seq_printf(m, " INSTDONE1: 0x%08x\n", error->instdone1); + } + +out: + spin_unlock_irqrestore(&dev_priv->error_lock, flags); + + return 0; +} static struct drm_info_list i915_gem_debugfs_list[] = { {"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST}, @@ -336,6 +373,7 @@ static struct drm_info_list i915_gem_debugfs_list[] = { {"i915_ringbuffer_data", i915_ringbuffer_data, 0}, {"i915_ringbuffer_info", i915_ringbuffer_info, 0}, {"i915_batchbuffers", i915_batchbuffer_info, 0}, + {"i915_error_state", i915_error_state, 0}, }; #define I915_GEM_DEBUGFS_ENTRIES ARRAY_SIZE(i915_gem_debugfs_list) diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index daeae62e1c28..a2d527b22ec4 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -521,6 +521,12 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, goto err; } + /* If we've changed tiling, GTT-mappings of the object + * need to re-fault to ensure that the correct fence register + * setup is in place. + */ + i915_gem_release_mmap(obj); + obj_priv->tiling_mode = args->tiling_mode; obj_priv->stride = args->stride; } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 228546f6eaa4..7ba23a69a0c0 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -26,6 +26,7 @@ * */ +#include <linux/sysrq.h> #include "drmP.h" #include "drm.h" #include "i915_drm.h" @@ -41,9 +42,10 @@ * we leave them always unmasked in IMR and then control enabling them through * PIPESTAT alone. */ -#define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \ - I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) +#define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \ + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \ + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) /** Interrupts that we mask and unmask at runtime. */ #define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) @@ -288,6 +290,47 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev) return ret; } +static void i915_capture_error_state(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_error_state *error; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->error_lock, flags); + if (dev_priv->first_error) + goto out; + + error = kmalloc(sizeof(*error), GFP_ATOMIC); + if (!error) { + DRM_DEBUG("out ot memory, not capturing error state\n"); + goto out; + } + + error->eir = I915_READ(EIR); + error->pgtbl_er = I915_READ(PGTBL_ER); + error->pipeastat = I915_READ(PIPEASTAT); + error->pipebstat = I915_READ(PIPEBSTAT); + error->instpm = I915_READ(INSTPM); + if (!IS_I965G(dev)) { + error->ipeir = I915_READ(IPEIR); + error->ipehr = I915_READ(IPEHR); + error->instdone = I915_READ(INSTDONE); + error->acthd = I915_READ(ACTHD); + } else { + error->ipeir = I915_READ(IPEIR_I965); + error->ipehr = I915_READ(IPEHR_I965); + error->instdone = I915_READ(INSTDONE_I965); + error->instps = I915_READ(INSTPS); + error->instdone1 = I915_READ(INSTDONE1); + error->acthd = I915_READ(ACTHD_I965); + } + + dev_priv->first_error = error; + +out: + spin_unlock_irqrestore(&dev_priv->error_lock, flags); +} + irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; @@ -333,11 +376,15 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) * Clear the PIPE(A|B)STAT regs before the IIR */ if (pipea_stats & 0x8000ffff) { + if (pipea_stats & PIPE_FIFO_UNDERRUN_STATUS) + DRM_DEBUG("pipe a underrun\n"); I915_WRITE(PIPEASTAT, pipea_stats); irq_received = 1; } if (pipeb_stats & 0x8000ffff) { + if (pipeb_stats & PIPE_FIFO_UNDERRUN_STATUS) + DRM_DEBUG("pipe b underrun\n"); I915_WRITE(PIPEBSTAT, pipeb_stats); irq_received = 1; } @@ -362,6 +409,80 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) I915_READ(PORT_HOTPLUG_STAT); } + if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) { + u32 eir = I915_READ(EIR); + + i915_capture_error_state(dev); + + printk(KERN_ERR "render error detected, EIR: 0x%08x\n", + eir); + if (eir & I915_ERROR_PAGE_TABLE) { + u32 pgtbl_err = I915_READ(PGTBL_ER); + printk(KERN_ERR "page table error\n"); + printk(KERN_ERR " PGTBL_ER: 0x%08x\n", + pgtbl_err); + I915_WRITE(PGTBL_ER, pgtbl_err); + (void)I915_READ(PGTBL_ER); + } + if (eir & I915_ERROR_MEMORY_REFRESH) { + printk(KERN_ERR "memory refresh error\n"); + printk(KERN_ERR "PIPEASTAT: 0x%08x\n", + pipea_stats); + printk(KERN_ERR "PIPEBSTAT: 0x%08x\n", + pipeb_stats); + /* pipestat has already been acked */ + } + if (eir & I915_ERROR_INSTRUCTION) { + printk(KERN_ERR "instruction error\n"); + printk(KERN_ERR " INSTPM: 0x%08x\n", + I915_READ(INSTPM)); + if (!IS_I965G(dev)) { + u32 ipeir = I915_READ(IPEIR); + + printk(KERN_ERR " IPEIR: 0x%08x\n", + I915_READ(IPEIR)); + printk(KERN_ERR " IPEHR: 0x%08x\n", + I915_READ(IPEHR)); + printk(KERN_ERR " INSTDONE: 0x%08x\n", + I915_READ(INSTDONE)); + printk(KERN_ERR " ACTHD: 0x%08x\n", + I915_READ(ACTHD)); + I915_WRITE(IPEIR, ipeir); + (void)I915_READ(IPEIR); + } else { + u32 ipeir = I915_READ(IPEIR_I965); + + printk(KERN_ERR " IPEIR: 0x%08x\n", + I915_READ(IPEIR_I965)); + printk(KERN_ERR " IPEHR: 0x%08x\n", + I915_READ(IPEHR_I965)); + printk(KERN_ERR " INSTDONE: 0x%08x\n", + I915_READ(INSTDONE_I965)); + printk(KERN_ERR " INSTPS: 0x%08x\n", + I915_READ(INSTPS)); + printk(KERN_ERR " INSTDONE1: 0x%08x\n", + I915_READ(INSTDONE1)); + printk(KERN_ERR " ACTHD: 0x%08x\n", + I915_READ(ACTHD_I965)); + I915_WRITE(IPEIR_I965, ipeir); + (void)I915_READ(IPEIR_I965); + } + } + + I915_WRITE(EIR, eir); + (void)I915_READ(EIR); + eir = I915_READ(EIR); + if (eir) { + /* + * some errors might have become stuck, + * mask them. + */ + DRM_ERROR("EIR stuck: 0x%08x, masking\n", eir); + I915_WRITE(EMR, I915_READ(EMR) | eir); + I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); + } + } + I915_WRITE(IIR, iir); new_iir = I915_READ(IIR); /* Flush posted writes */ @@ -732,6 +853,7 @@ int i915_driver_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR; + u32 error_mask; DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); @@ -768,6 +890,21 @@ int i915_driver_irq_postinstall(struct drm_device *dev) i915_enable_irq(dev_priv, I915_DISPLAY_PORT_INTERRUPT); } + /* + * Enable some error detection, note the instruction error mask + * bit is reserved, so we leave it masked. + */ + if (IS_G4X(dev)) { + error_mask = ~(GM45_ERROR_PAGE_TABLE | + GM45_ERROR_MEM_PRIV | + GM45_ERROR_CP_PRIV | + I915_ERROR_MEMORY_REFRESH); + } else { + error_mask = ~(I915_ERROR_PAGE_TABLE | + I915_ERROR_MEMORY_REFRESH); + } + I915_WRITE(EMR, error_mask); + /* Disable pipe interrupt enables, clear pending pipe status */ I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 88bf7521405f..6c0858484094 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -206,6 +206,7 @@ /* * Instruction and interrupt control regs */ +#define PGTBL_ER 0x02024 #define PRB0_TAIL 0x02030 #define PRB0_HEAD 0x02034 #define PRB0_START 0x02038 @@ -226,11 +227,18 @@ #define PRB1_HEAD 0x02044 /* 915+ only */ #define PRB1_START 0x02048 /* 915+ only */ #define PRB1_CTL 0x0204c /* 915+ only */ +#define IPEIR_I965 0x02064 +#define IPEHR_I965 0x02068 +#define INSTDONE_I965 0x0206c +#define INSTPS 0x02070 /* 965+ only */ +#define INSTDONE1 0x0207c /* 965+ only */ #define ACTHD_I965 0x02074 #define HWS_PGA 0x02080 #define HWS_ADDRESS_MASK 0xfffff000 #define HWS_START_ADDRESS_SHIFT 4 #define IPEIR 0x02088 +#define IPEHR 0x0208c +#define INSTDONE 0x02090 #define NOPID 0x02094 #define HWSTAM 0x02098 #define SCPD0 0x0209c /* 915+ only */ @@ -258,10 +266,22 @@ #define EIR 0x020b0 #define EMR 0x020b4 #define ESR 0x020b8 +#define GM45_ERROR_PAGE_TABLE (1<<5) +#define GM45_ERROR_MEM_PRIV (1<<4) +#define I915_ERROR_PAGE_TABLE (1<<4) +#define GM45_ERROR_CP_PRIV (1<<3) +#define I915_ERROR_MEMORY_REFRESH (1<<1) +#define I915_ERROR_INSTRUCTION (1<<0) #define INSTPM 0x020c0 #define ACTHD 0x020c8 #define FW_BLC 0x020d8 +#define FW_BLC2 0x020dc #define FW_BLC_SELF 0x020e0 /* 915+ only */ +#define FW_BLC_SELF_EN (1<<15) +#define MM_BURST_LENGTH 0x00700000 +#define MM_FIFO_WATERMARK 0x0001F000 +#define LM_BURST_LENGTH 0x00000700 +#define LM_FIFO_WATERMARK 0x0000001F #define MI_ARB_STATE 0x020e4 /* 915+ only */ #define CACHE_MODE_0 0x02120 /* 915+ only */ #define CM0_MASK_SHIFT 16 @@ -571,17 +591,21 @@ /* Clocking configuration register */ #define CLKCFG 0x10c00 -#define CLKCFG_FSB_400 (0 << 0) /* hrawclk 100 */ +#define CLKCFG_FSB_400 (5 << 0) /* hrawclk 100 */ #define CLKCFG_FSB_533 (1 << 0) /* hrawclk 133 */ #define CLKCFG_FSB_667 (3 << 0) /* hrawclk 166 */ #define CLKCFG_FSB_800 (2 << 0) /* hrawclk 200 */ #define CLKCFG_FSB_1067 (6 << 0) /* hrawclk 266 */ #define CLKCFG_FSB_1333 (7 << 0) /* hrawclk 333 */ -/* this is a guess, could be 5 as well */ +/* Note, below two are guess */ #define CLKCFG_FSB_1600 (4 << 0) /* hrawclk 400 */ -#define CLKCFG_FSB_1600_ALT (5 << 0) /* hrawclk 400 */ +#define CLKCFG_FSB_1600_ALT (0 << 0) /* hrawclk 400 */ #define CLKCFG_FSB_MASK (7 << 0) - +#define CLKCFG_MEM_533 (1 << 4) +#define CLKCFG_MEM_667 (2 << 4) +#define CLKCFG_MEM_800 (3 << 4) +#define CLKCFG_MEM_MASK (7 << 4) + /** GM965 GM45 render standby register */ #define MCHBAR_RENDER_STANDBY 0x111B8 @@ -1581,6 +1605,34 @@ #define DSPARB_CSTART_SHIFT 7 #define DSPARB_BSTART_MASK (0x7f) #define DSPARB_BSTART_SHIFT 0 +#define DSPARB_BEND_SHIFT 9 /* on 855 */ +#define DSPARB_AEND_SHIFT 0 + +#define DSPFW1 0x70034 +#define DSPFW2 0x70038 +#define DSPFW3 0x7003c +#define IGD_SELF_REFRESH_EN (1<<30) + +/* FIFO watermark sizes etc */ +#define I915_FIFO_LINE_SIZE 64 +#define I830_FIFO_LINE_SIZE 32 +#define I945_FIFO_SIZE 127 /* 945 & 965 */ +#define I915_FIFO_SIZE 95 +#define I855GM_FIFO_SIZE 255 +#define I830_FIFO_SIZE 95 +#define I915_MAX_WM 0x3f + +#define IGD_DISPLAY_FIFO 512 /* in 64byte unit */ +#define IGD_FIFO_LINE_SIZE 64 +#define IGD_MAX_WM 0x1ff +#define IGD_DFT_WM 0x3f +#define IGD_DFT_HPLLOFF_WM 0 +#define IGD_GUARD_WM 10 +#define IGD_CURSOR_FIFO 64 +#define IGD_CURSOR_MAX_WM 0x3f +#define IGD_CURSOR_DFT_WM 0 +#define IGD_CURSOR_GUARD_WM 5 + /* * The two pipe frame counter registers are not synchronized, so * reading a stable value is somewhat tricky. The following code diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 8d8e083d14ab..9e1d16e5c3ea 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -222,23 +222,12 @@ static void i915_restore_vga(struct drm_device *dev) I915_WRITE8(VGA_DACMASK, dev_priv->saveDACMASK); } -int i915_save_state(struct drm_device *dev) +static void i915_save_modeset_reg(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - int i; - - pci_read_config_byte(dev->pdev, LBB, &dev_priv->saveLBB); - - /* Render Standby */ - if (IS_I965G(dev) && IS_MOBILE(dev)) - dev_priv->saveRENDERSTANDBY = I915_READ(MCHBAR_RENDER_STANDBY); - - /* Hardware status page */ - dev_priv->saveHWS = I915_READ(HWS_PGA); - - /* Display arbitration control */ - dev_priv->saveDSPARB = I915_READ(DSPARB); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return; /* Pipe & plane A info */ dev_priv->savePIPEACONF = I915_READ(PIPEACONF); dev_priv->savePIPEASRC = I915_READ(PIPEASRC); @@ -294,7 +283,122 @@ int i915_save_state(struct drm_device *dev) } i915_save_palette(dev, PIPE_B); dev_priv->savePIPEBSTAT = I915_READ(PIPEBSTAT); + return; +} +static void i915_restore_modeset_reg(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + /* Pipe & plane A info */ + /* Prime the clock */ + if (dev_priv->saveDPLL_A & DPLL_VCO_ENABLE) { + I915_WRITE(DPLL_A, dev_priv->saveDPLL_A & + ~DPLL_VCO_ENABLE); + DRM_UDELAY(150); + } + I915_WRITE(FPA0, dev_priv->saveFPA0); + I915_WRITE(FPA1, dev_priv->saveFPA1); + /* Actually enable it */ + I915_WRITE(DPLL_A, dev_priv->saveDPLL_A); + DRM_UDELAY(150); + if (IS_I965G(dev)) + I915_WRITE(DPLL_A_MD, dev_priv->saveDPLL_A_MD); + DRM_UDELAY(150); + + /* Restore mode */ + I915_WRITE(HTOTAL_A, dev_priv->saveHTOTAL_A); + I915_WRITE(HBLANK_A, dev_priv->saveHBLANK_A); + I915_WRITE(HSYNC_A, dev_priv->saveHSYNC_A); + I915_WRITE(VTOTAL_A, dev_priv->saveVTOTAL_A); + I915_WRITE(VBLANK_A, dev_priv->saveVBLANK_A); + I915_WRITE(VSYNC_A, dev_priv->saveVSYNC_A); + I915_WRITE(BCLRPAT_A, dev_priv->saveBCLRPAT_A); + + /* Restore plane info */ + I915_WRITE(DSPASIZE, dev_priv->saveDSPASIZE); + I915_WRITE(DSPAPOS, dev_priv->saveDSPAPOS); + I915_WRITE(PIPEASRC, dev_priv->savePIPEASRC); + I915_WRITE(DSPAADDR, dev_priv->saveDSPAADDR); + I915_WRITE(DSPASTRIDE, dev_priv->saveDSPASTRIDE); + if (IS_I965G(dev)) { + I915_WRITE(DSPASURF, dev_priv->saveDSPASURF); + I915_WRITE(DSPATILEOFF, dev_priv->saveDSPATILEOFF); + } + + I915_WRITE(PIPEACONF, dev_priv->savePIPEACONF); + + i915_restore_palette(dev, PIPE_A); + /* Enable the plane */ + I915_WRITE(DSPACNTR, dev_priv->saveDSPACNTR); + I915_WRITE(DSPAADDR, I915_READ(DSPAADDR)); + + /* Pipe & plane B info */ + if (dev_priv->saveDPLL_B & DPLL_VCO_ENABLE) { + I915_WRITE(DPLL_B, dev_priv->saveDPLL_B & + ~DPLL_VCO_ENABLE); + DRM_UDELAY(150); + } + I915_WRITE(FPB0, dev_priv->saveFPB0); + I915_WRITE(FPB1, dev_priv->saveFPB1); + /* Actually enable it */ + I915_WRITE(DPLL_B, dev_priv->saveDPLL_B); + DRM_UDELAY(150); + if (IS_I965G(dev)) + I915_WRITE(DPLL_B_MD, dev_priv->saveDPLL_B_MD); + DRM_UDELAY(150); + + /* Restore mode */ + I915_WRITE(HTOTAL_B, dev_priv->saveHTOTAL_B); + I915_WRITE(HBLANK_B, dev_priv->saveHBLANK_B); + I915_WRITE(HSYNC_B, dev_priv->saveHSYNC_B); + I915_WRITE(VTOTAL_B, dev_priv->saveVTOTAL_B); + I915_WRITE(VBLANK_B, dev_priv->saveVBLANK_B); + I915_WRITE(VSYNC_B, dev_priv->saveVSYNC_B); + I915_WRITE(BCLRPAT_B, dev_priv->saveBCLRPAT_B); + + /* Restore plane info */ + I915_WRITE(DSPBSIZE, dev_priv->saveDSPBSIZE); + I915_WRITE(DSPBPOS, dev_priv->saveDSPBPOS); + I915_WRITE(PIPEBSRC, dev_priv->savePIPEBSRC); + I915_WRITE(DSPBADDR, dev_priv->saveDSPBADDR); + I915_WRITE(DSPBSTRIDE, dev_priv->saveDSPBSTRIDE); + if (IS_I965G(dev)) { + I915_WRITE(DSPBSURF, dev_priv->saveDSPBSURF); + I915_WRITE(DSPBTILEOFF, dev_priv->saveDSPBTILEOFF); + } + + I915_WRITE(PIPEBCONF, dev_priv->savePIPEBCONF); + + i915_restore_palette(dev, PIPE_B); + /* Enable the plane */ + I915_WRITE(DSPBCNTR, dev_priv->saveDSPBCNTR); + I915_WRITE(DSPBADDR, I915_READ(DSPBADDR)); + return; +} +int i915_save_state(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int i; + + pci_read_config_byte(dev->pdev, LBB, &dev_priv->saveLBB); + + /* Render Standby */ + if (IS_I965G(dev) && IS_MOBILE(dev)) + dev_priv->saveRENDERSTANDBY = I915_READ(MCHBAR_RENDER_STANDBY); + + /* Hardware status page */ + dev_priv->saveHWS = I915_READ(HWS_PGA); + + /* Display arbitration control */ + dev_priv->saveDSPARB = I915_READ(DSPARB); + + /* This is only meaningful in non-KMS mode */ + /* Don't save them in KMS mode */ + i915_save_modeset_reg(dev); /* Cursor state */ dev_priv->saveCURACNTR = I915_READ(CURACNTR); dev_priv->saveCURAPOS = I915_READ(CURAPOS); @@ -430,92 +534,9 @@ int i915_restore_state(struct drm_device *dev) I915_WRITE(PIPEA_DP_LINK_N, dev_priv->savePIPEA_DP_LINK_N); I915_WRITE(PIPEB_DP_LINK_N, dev_priv->savePIPEB_DP_LINK_N); } - - /* Pipe & plane A info */ - /* Prime the clock */ - if (dev_priv->saveDPLL_A & DPLL_VCO_ENABLE) { - I915_WRITE(DPLL_A, dev_priv->saveDPLL_A & - ~DPLL_VCO_ENABLE); - DRM_UDELAY(150); - } - I915_WRITE(FPA0, dev_priv->saveFPA0); - I915_WRITE(FPA1, dev_priv->saveFPA1); - /* Actually enable it */ - I915_WRITE(DPLL_A, dev_priv->saveDPLL_A); - DRM_UDELAY(150); - if (IS_I965G(dev)) - I915_WRITE(DPLL_A_MD, dev_priv->saveDPLL_A_MD); - DRM_UDELAY(150); - - /* Restore mode */ - I915_WRITE(HTOTAL_A, dev_priv->saveHTOTAL_A); - I915_WRITE(HBLANK_A, dev_priv->saveHBLANK_A); - I915_WRITE(HSYNC_A, dev_priv->saveHSYNC_A); - I915_WRITE(VTOTAL_A, dev_priv->saveVTOTAL_A); - I915_WRITE(VBLANK_A, dev_priv->saveVBLANK_A); - I915_WRITE(VSYNC_A, dev_priv->saveVSYNC_A); - I915_WRITE(BCLRPAT_A, dev_priv->saveBCLRPAT_A); - - /* Restore plane info */ - I915_WRITE(DSPASIZE, dev_priv->saveDSPASIZE); - I915_WRITE(DSPAPOS, dev_priv->saveDSPAPOS); - I915_WRITE(PIPEASRC, dev_priv->savePIPEASRC); - I915_WRITE(DSPAADDR, dev_priv->saveDSPAADDR); - I915_WRITE(DSPASTRIDE, dev_priv->saveDSPASTRIDE); - if (IS_I965G(dev)) { - I915_WRITE(DSPASURF, dev_priv->saveDSPASURF); - I915_WRITE(DSPATILEOFF, dev_priv->saveDSPATILEOFF); - } - - I915_WRITE(PIPEACONF, dev_priv->savePIPEACONF); - - i915_restore_palette(dev, PIPE_A); - /* Enable the plane */ - I915_WRITE(DSPACNTR, dev_priv->saveDSPACNTR); - I915_WRITE(DSPAADDR, I915_READ(DSPAADDR)); - - /* Pipe & plane B info */ - if (dev_priv->saveDPLL_B & DPLL_VCO_ENABLE) { - I915_WRITE(DPLL_B, dev_priv->saveDPLL_B & - ~DPLL_VCO_ENABLE); - DRM_UDELAY(150); - } - I915_WRITE(FPB0, dev_priv->saveFPB0); - I915_WRITE(FPB1, dev_priv->saveFPB1); - /* Actually enable it */ - I915_WRITE(DPLL_B, dev_priv->saveDPLL_B); - DRM_UDELAY(150); - if (IS_I965G(dev)) - I915_WRITE(DPLL_B_MD, dev_priv->saveDPLL_B_MD); - DRM_UDELAY(150); - - /* Restore mode */ - I915_WRITE(HTOTAL_B, dev_priv->saveHTOTAL_B); - I915_WRITE(HBLANK_B, dev_priv->saveHBLANK_B); - I915_WRITE(HSYNC_B, dev_priv->saveHSYNC_B); - I915_WRITE(VTOTAL_B, dev_priv->saveVTOTAL_B); - I915_WRITE(VBLANK_B, dev_priv->saveVBLANK_B); - I915_WRITE(VSYNC_B, dev_priv->saveVSYNC_B); - I915_WRITE(BCLRPAT_B, dev_priv->saveBCLRPAT_B); - - /* Restore plane info */ - I915_WRITE(DSPBSIZE, dev_priv->saveDSPBSIZE); - I915_WRITE(DSPBPOS, dev_priv->saveDSPBPOS); - I915_WRITE(PIPEBSRC, dev_priv->savePIPEBSRC); - I915_WRITE(DSPBADDR, dev_priv->saveDSPBADDR); - I915_WRITE(DSPBSTRIDE, dev_priv->saveDSPBSTRIDE); - if (IS_I965G(dev)) { - I915_WRITE(DSPBSURF, dev_priv->saveDSPBSURF); - I915_WRITE(DSPBTILEOFF, dev_priv->saveDSPBTILEOFF); - } - - I915_WRITE(PIPEBCONF, dev_priv->savePIPEBCONF); - - i915_restore_palette(dev, PIPE_B); - /* Enable the plane */ - I915_WRITE(DSPBCNTR, dev_priv->saveDSPBCNTR); - I915_WRITE(DSPBADDR, I915_READ(DSPBADDR)); - + /* This is only meaningful in non-KMS mode */ + /* Don't restore them in KMS mode */ + i915_restore_modeset_reg(dev); /* Cursor state */ I915_WRITE(CURAPOS, dev_priv->saveCURAPOS); I915_WRITE(CURACNTR, dev_priv->saveCURACNTR); diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 716409a57244..7cc447191028 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -97,6 +97,7 @@ static void parse_lfp_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb) { + struct drm_device *dev = dev_priv->dev; struct bdb_lvds_options *lvds_options; struct bdb_lvds_lfp_data *lvds_lfp_data; struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; @@ -132,7 +133,14 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, entry = (struct bdb_lvds_lfp_data_entry *) ((uint8_t *)lvds_lfp_data->data + (lfp_data_size * lvds_options->panel_type)); - dvo_timing = &entry->dvo_timing; + + /* On IGDNG mobile, LVDS data block removes panel fitting registers. + So dec 2 dword from dvo_timing offset */ + if (IS_IGDNG(dev)) + dvo_timing = (struct lvds_dvo_timing *) + ((u8 *)&entry->dvo_timing - 8); + else + dvo_timing = &entry->dvo_timing; panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL); @@ -195,10 +203,12 @@ parse_general_features(struct drm_i915_private *dev_priv, dev_priv->lvds_use_ssc = general->enable_ssc; if (dev_priv->lvds_use_ssc) { - if (IS_I855(dev_priv->dev)) - dev_priv->lvds_ssc_freq = general->ssc_freq ? 66 : 48; - else - dev_priv->lvds_ssc_freq = general->ssc_freq ? 100 : 96; + if (IS_I85X(dev_priv->dev)) + dev_priv->lvds_ssc_freq = + general->ssc_freq ? 66 : 48; + else + dev_priv->lvds_ssc_freq = + general->ssc_freq ? 100 : 96; } } } diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 6de97fc66029..d6a1a6e5539a 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -46,7 +46,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode) temp = I915_READ(reg); temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); - temp |= ADPA_DAC_ENABLE; + temp &= ~ADPA_DAC_ENABLE; switch(mode) { case DRM_MODE_DPMS_ON: @@ -428,8 +428,34 @@ static void intel_crt_destroy(struct drm_connector *connector) static int intel_crt_get_modes(struct drm_connector *connector) { + int ret; struct intel_output *intel_output = to_intel_output(connector); - return intel_ddc_get_modes(intel_output); + struct i2c_adapter *ddcbus; + struct drm_device *dev = connector->dev; + + + ret = intel_ddc_get_modes(intel_output); + if (ret || !IS_G4X(dev)) + goto end; + + ddcbus = intel_output->ddc_bus; + /* Try to probe digital port for output in DVI-I -> VGA mode. */ + intel_output->ddc_bus = + intel_i2c_create(connector->dev, GPIOD, "CRTDDC_D"); + + if (!intel_output->ddc_bus) { + intel_output->ddc_bus = ddcbus; + dev_printk(KERN_ERR, &connector->dev->pdev->dev, + "DDC bus registration failed for CRTDDC_D.\n"); + goto end; + } + /* Try to get modes by GPIOD port */ + ret = intel_ddc_get_modes(intel_output); + intel_i2c_destroy(ddcbus); + +end: + return ret; + } static int intel_crt_set_property(struct drm_connector *connector, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 73e7b9cecac8..508838ee31e0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -25,6 +25,7 @@ */ #include <linux/i2c.h> +#include <linux/kernel.h> #include "drmP.h" #include "intel_drv.h" #include "i915_drm.h" @@ -34,6 +35,7 @@ #include "drm_crtc_helper.h" bool intel_pipe_has_type (struct drm_crtc *crtc, int type); +static void intel_update_watermarks(struct drm_device *dev); typedef struct { /* given values */ @@ -814,24 +816,21 @@ intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc, { intel_clock_t clock; if (target < 200000) { - clock.dot = 161670; - clock.p = 20; clock.p1 = 2; clock.p2 = 10; - clock.n = 0x01; - clock.m = 97; - clock.m1 = 0x10; - clock.m2 = 0x05; + clock.n = 2; + clock.m1 = 23; + clock.m2 = 8; } else { - clock.dot = 270000; - clock.p = 10; clock.p1 = 1; clock.p2 = 10; - clock.n = 0x02; - clock.m = 108; - clock.m1 = 0x12; - clock.m2 = 0x06; + clock.n = 1; + clock.m1 = 14; + clock.m2 = 2; } + clock.m = 5 * (clock.m1 + 2) + (clock.m2 + 2); + clock.p = (clock.p1 * clock.p2); + clock.dot = 96000 * clock.m / (clock.n + 2) / clock.p; memcpy(best_clock, &clock, sizeof(intel_clock_t)); return true; } @@ -1005,7 +1004,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; - int plane = intel_crtc->pipe; + int plane = intel_crtc->plane; int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B; int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; @@ -1335,8 +1334,10 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) /* Give the overlay scaler a chance to enable if it's on this pipe */ //intel_crtc_dpms_video(crtc, true); TODO + intel_update_watermarks(dev); break; case DRM_MODE_DPMS_OFF: + intel_update_watermarks(dev); /* Give the overlay scaler a chance to disable if it's on this pipe */ //intel_crtc_dpms_video(crtc, FALSE); TODO @@ -1515,7 +1516,6 @@ static int intel_get_core_clock_speed(struct drm_device *dev) return 0; /* Silence gcc warning */ } - /** * Return the pipe currently connected to the panel fitter, * or -1 if the panel fitter is not present or not in use @@ -1574,7 +1574,7 @@ igdng_compute_m_n(int bytes_per_pixel, int nlanes, temp = (u64) DATA_N * pixel_clock; temp = div_u64(temp, link_clock); - m_n->gmch_m = (temp * bytes_per_pixel) / nlanes; + m_n->gmch_m = div_u64(temp * bytes_per_pixel, nlanes); m_n->gmch_n = DATA_N; fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); @@ -1585,6 +1585,420 @@ igdng_compute_m_n(int bytes_per_pixel, int nlanes, } +struct intel_watermark_params { + unsigned long fifo_size; + unsigned long max_wm; + unsigned long default_wm; + unsigned long guard_size; + unsigned long cacheline_size; +}; + +/* IGD has different values for various configs */ +static struct intel_watermark_params igd_display_wm = { + IGD_DISPLAY_FIFO, + IGD_MAX_WM, + IGD_DFT_WM, + IGD_GUARD_WM, + IGD_FIFO_LINE_SIZE +}; +static struct intel_watermark_params igd_display_hplloff_wm = { + IGD_DISPLAY_FIFO, + IGD_MAX_WM, + IGD_DFT_HPLLOFF_WM, + IGD_GUARD_WM, + IGD_FIFO_LINE_SIZE +}; +static struct intel_watermark_params igd_cursor_wm = { + IGD_CURSOR_FIFO, + IGD_CURSOR_MAX_WM, + IGD_CURSOR_DFT_WM, + IGD_CURSOR_GUARD_WM, + IGD_FIFO_LINE_SIZE, +}; +static struct intel_watermark_params igd_cursor_hplloff_wm = { + IGD_CURSOR_FIFO, + IGD_CURSOR_MAX_WM, + IGD_CURSOR_DFT_WM, + IGD_CURSOR_GUARD_WM, + IGD_FIFO_LINE_SIZE +}; +static struct intel_watermark_params i945_wm_info = { + I915_FIFO_LINE_SIZE, + I915_MAX_WM, + 1, + 0, + IGD_FIFO_LINE_SIZE +}; +static struct intel_watermark_params i915_wm_info = { + I945_FIFO_SIZE, + I915_MAX_WM, + 1, + 0, + I915_FIFO_LINE_SIZE +}; +static struct intel_watermark_params i855_wm_info = { + I855GM_FIFO_SIZE, + I915_MAX_WM, + 1, + 0, + I830_FIFO_LINE_SIZE +}; +static struct intel_watermark_params i830_wm_info = { + I830_FIFO_SIZE, + I915_MAX_WM, + 1, + 0, + I830_FIFO_LINE_SIZE +}; + +static unsigned long intel_calculate_wm(unsigned long clock_in_khz, + struct intel_watermark_params *wm, + int pixel_size, + unsigned long latency_ns) +{ + unsigned long bytes_required, wm_size; + + bytes_required = (clock_in_khz * pixel_size * latency_ns) / 1000000; + bytes_required /= wm->cacheline_size; + wm_size = wm->fifo_size - bytes_required - wm->guard_size; + + if (wm_size > wm->max_wm) + wm_size = wm->max_wm; + if (wm_size == 0) + wm_size = wm->default_wm; + return wm_size; +} + +struct cxsr_latency { + int is_desktop; + unsigned long fsb_freq; + unsigned long mem_freq; + unsigned long display_sr; + unsigned long display_hpll_disable; + unsigned long cursor_sr; + unsigned long cursor_hpll_disable; +}; + +static struct cxsr_latency cxsr_latency_table[] = { + {1, 800, 400, 3382, 33382, 3983, 33983}, /* DDR2-400 SC */ + {1, 800, 667, 3354, 33354, 3807, 33807}, /* DDR2-667 SC */ + {1, 800, 800, 3347, 33347, 3763, 33763}, /* DDR2-800 SC */ + + {1, 667, 400, 3400, 33400, 4021, 34021}, /* DDR2-400 SC */ + {1, 667, 667, 3372, 33372, 3845, 33845}, /* DDR2-667 SC */ + {1, 667, 800, 3386, 33386, 3822, 33822}, /* DDR2-800 SC */ + + {1, 400, 400, 3472, 33472, 4173, 34173}, /* DDR2-400 SC */ + {1, 400, 667, 3443, 33443, 3996, 33996}, /* DDR2-667 SC */ + {1, 400, 800, 3430, 33430, 3946, 33946}, /* DDR2-800 SC */ + + {0, 800, 400, 3438, 33438, 4065, 34065}, /* DDR2-400 SC */ + {0, 800, 667, 3410, 33410, 3889, 33889}, /* DDR2-667 SC */ + {0, 800, 800, 3403, 33403, 3845, 33845}, /* DDR2-800 SC */ + + {0, 667, 400, 3456, 33456, 4103, 34106}, /* DDR2-400 SC */ + {0, 667, 667, 3428, 33428, 3927, 33927}, /* DDR2-667 SC */ + {0, 667, 800, 3443, 33443, 3905, 33905}, /* DDR2-800 SC */ + + {0, 400, 400, 3528, 33528, 4255, 34255}, /* DDR2-400 SC */ + {0, 400, 667, 3500, 33500, 4079, 34079}, /* DDR2-667 SC */ + {0, 400, 800, 3487, 33487, 4029, 34029}, /* DDR2-800 SC */ +}; + +static struct cxsr_latency *intel_get_cxsr_latency(int is_desktop, int fsb, + int mem) +{ + int i; + struct cxsr_latency *latency; + + if (fsb == 0 || mem == 0) + return NULL; + + for (i = 0; i < ARRAY_SIZE(cxsr_latency_table); i++) { + latency = &cxsr_latency_table[i]; + if (is_desktop == latency->is_desktop && + fsb == latency->fsb_freq && mem == latency->mem_freq) + break; + } + if (i >= ARRAY_SIZE(cxsr_latency_table)) { + DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n"); + return NULL; + } + return latency; +} + +static void igd_disable_cxsr(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 reg; + + /* deactivate cxsr */ + reg = I915_READ(DSPFW3); + reg &= ~(IGD_SELF_REFRESH_EN); + I915_WRITE(DSPFW3, reg); + DRM_INFO("Big FIFO is disabled\n"); +} + +static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock, + int pixel_size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 reg; + unsigned long wm; + struct cxsr_latency *latency; + + latency = intel_get_cxsr_latency(IS_IGDG(dev), dev_priv->fsb_freq, + dev_priv->mem_freq); + if (!latency) { + DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n"); + igd_disable_cxsr(dev); + return; + } + + /* Display SR */ + wm = intel_calculate_wm(clock, &igd_display_wm, pixel_size, + latency->display_sr); + reg = I915_READ(DSPFW1); + reg &= 0x7fffff; + reg |= wm << 23; + I915_WRITE(DSPFW1, reg); + DRM_DEBUG("DSPFW1 register is %x\n", reg); + + /* cursor SR */ + wm = intel_calculate_wm(clock, &igd_cursor_wm, pixel_size, + latency->cursor_sr); + reg = I915_READ(DSPFW3); + reg &= ~(0x3f << 24); + reg |= (wm & 0x3f) << 24; + I915_WRITE(DSPFW3, reg); + + /* Display HPLL off SR */ + wm = intel_calculate_wm(clock, &igd_display_hplloff_wm, + latency->display_hpll_disable, I915_FIFO_LINE_SIZE); + reg = I915_READ(DSPFW3); + reg &= 0xfffffe00; + reg |= wm & 0x1ff; + I915_WRITE(DSPFW3, reg); + + /* cursor HPLL off SR */ + wm = intel_calculate_wm(clock, &igd_cursor_hplloff_wm, pixel_size, + latency->cursor_hpll_disable); + reg = I915_READ(DSPFW3); + reg &= ~(0x3f << 16); + reg |= (wm & 0x3f) << 16; + I915_WRITE(DSPFW3, reg); + DRM_DEBUG("DSPFW3 register is %x\n", reg); + + /* activate cxsr */ + reg = I915_READ(DSPFW3); + reg |= IGD_SELF_REFRESH_EN; + I915_WRITE(DSPFW3, reg); + + DRM_INFO("Big FIFO is enabled\n"); + + return; +} + +const static int latency_ns = 5000; /* default for non-igd platforms */ + + +static void i965_update_wm(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + DRM_DEBUG("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR 8\n"); + + /* 965 has limitations... */ + I915_WRITE(DSPFW1, (8 << 16) | (8 << 8) | (8 << 0)); + I915_WRITE(DSPFW2, (8 << 8) | (8 << 0)); +} + +static void i9xx_update_wm(struct drm_device *dev, int planea_clock, + int planeb_clock, int sr_hdisplay, int pixel_size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t fwater_lo = I915_READ(FW_BLC) & MM_FIFO_WATERMARK; + uint32_t fwater_hi = I915_READ(FW_BLC2) & LM_FIFO_WATERMARK; + int bsize, asize, cwm, bwm = 1, awm = 1, srwm = 1; + uint32_t dsparb = I915_READ(DSPARB); + int planea_entries, planeb_entries; + struct intel_watermark_params *wm_params; + unsigned long line_time_us; + int sr_clock, sr_entries = 0; + + if (IS_I965GM(dev) || IS_I945GM(dev)) + wm_params = &i945_wm_info; + else if (IS_I9XX(dev)) + wm_params = &i915_wm_info; + else + wm_params = &i855_wm_info; + + planea_entries = intel_calculate_wm(planea_clock, wm_params, + pixel_size, latency_ns); + planeb_entries = intel_calculate_wm(planeb_clock, wm_params, + pixel_size, latency_ns); + + DRM_DEBUG("FIFO entries - A: %d, B: %d\n", planea_entries, + planeb_entries); + + if (IS_I9XX(dev)) { + asize = dsparb & 0x7f; + bsize = (dsparb >> DSPARB_CSTART_SHIFT) & 0x7f; + } else { + asize = dsparb & 0x1ff; + bsize = (dsparb >> DSPARB_BEND_SHIFT) & 0x1ff; + } + DRM_DEBUG("FIFO size - A: %d, B: %d\n", asize, bsize); + + /* Two extra entries for padding */ + awm = asize - (planea_entries + 2); + bwm = bsize - (planeb_entries + 2); + + /* Sanity check against potentially bad FIFO allocations */ + if (awm <= 0) { + /* pipe is on but has too few FIFO entries */ + if (planea_entries != 0) + DRM_DEBUG("plane A needs more FIFO entries\n"); + awm = 1; + } + if (bwm <= 0) { + if (planeb_entries != 0) + DRM_DEBUG("plane B needs more FIFO entries\n"); + bwm = 1; + } + + /* + * Overlay gets an aggressive default since video jitter is bad. + */ + cwm = 2; + + /* Calc sr entries for one pipe configs */ + if (!planea_clock || !planeb_clock) { + sr_clock = planea_clock ? planea_clock : planeb_clock; + line_time_us = (sr_hdisplay * 1000) / sr_clock; + sr_entries = (((latency_ns / line_time_us) + 1) * pixel_size * + sr_hdisplay) / 1000; + sr_entries = roundup(sr_entries / wm_params->cacheline_size, 1); + if (sr_entries < wm_params->fifo_size) + srwm = wm_params->fifo_size - sr_entries; + } + + DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n", + awm, bwm, cwm, srwm); + + fwater_lo = fwater_lo | ((bwm & 0x3f) << 16) | (awm & 0x3f); + fwater_hi = fwater_hi | (cwm & 0x1f); + + I915_WRITE(FW_BLC, fwater_lo); + I915_WRITE(FW_BLC2, fwater_hi); + if (IS_I9XX(dev)) + I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN | (srwm & 0x3f)); +} + +static void i830_update_wm(struct drm_device *dev, int planea_clock, + int pixel_size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t dsparb = I915_READ(DSPARB); + uint32_t fwater_lo = I915_READ(FW_BLC) & MM_FIFO_WATERMARK; + unsigned int asize, awm; + int planea_entries; + + planea_entries = intel_calculate_wm(planea_clock, &i830_wm_info, + pixel_size, latency_ns); + + asize = dsparb & 0x7f; + + awm = asize - planea_entries; + + fwater_lo = fwater_lo | awm; + + I915_WRITE(FW_BLC, fwater_lo); +} + +/** + * intel_update_watermarks - update FIFO watermark values based on current modes + * + * Calculate watermark values for the various WM regs based on current mode + * and plane configuration. + * + * There are several cases to deal with here: + * - normal (i.e. non-self-refresh) + * - self-refresh (SR) mode + * - lines are large relative to FIFO size (buffer can hold up to 2) + * - lines are small relative to FIFO size (buffer can hold more than 2 + * lines), so need to account for TLB latency + * + * The normal calculation is: + * watermark = dotclock * bytes per pixel * latency + * where latency is platform & configuration dependent (we assume pessimal + * values here). + * + * The SR calculation is: + * watermark = (trunc(latency/line time)+1) * surface width * + * bytes per pixel + * where + * line time = htotal / dotclock + * and latency is assumed to be high, as above. + * + * The final value programmed to the register should always be rounded up, + * and include an extra 2 entries to account for clock crossings. + * + * We don't use the sprite, so we can ignore that. And on Crestline we have + * to set the non-SR watermarks to 8. + */ +static void intel_update_watermarks(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct intel_crtc *intel_crtc; + int sr_hdisplay = 0; + unsigned long planea_clock = 0, planeb_clock = 0, sr_clock = 0; + int enabled = 0, pixel_size = 0; + + if (DSPARB_HWCONTROL(dev)) + return; + + /* Get the clock config from both planes */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + intel_crtc = to_intel_crtc(crtc); + if (crtc->enabled) { + enabled++; + if (intel_crtc->plane == 0) { + DRM_DEBUG("plane A (pipe %d) clock: %d\n", + intel_crtc->pipe, crtc->mode.clock); + planea_clock = crtc->mode.clock; + } else { + DRM_DEBUG("plane B (pipe %d) clock: %d\n", + intel_crtc->pipe, crtc->mode.clock); + planeb_clock = crtc->mode.clock; + } + sr_hdisplay = crtc->mode.hdisplay; + sr_clock = crtc->mode.clock; + if (crtc->fb) + pixel_size = crtc->fb->bits_per_pixel / 8; + else + pixel_size = 4; /* by default */ + } + } + + if (enabled <= 0) + return; + + /* Single pipe configs can enable self refresh */ + if (enabled == 1 && IS_IGD(dev)) + igd_enable_cxsr(dev, sr_clock, pixel_size); + else if (IS_IGD(dev)) + igd_disable_cxsr(dev); + + if (IS_I965G(dev)) + i965_update_wm(dev); + else if (IS_I9XX(dev) || IS_MOBILE(dev)) + i9xx_update_wm(dev, planea_clock, planeb_clock, sr_hdisplay, + pixel_size); + else + i830_update_wm(dev, planea_clock, pixel_size); +} + static int intel_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -1951,6 +2365,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* Flush the plane changes */ ret = intel_pipe_set_base(crtc, x, y, old_fb); + + intel_update_watermarks(dev); + drm_vblank_post_modeset(dev, pipe); return ret; @@ -2439,6 +2856,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256); intel_crtc->pipe = pipe; + intel_crtc->plane = pipe; for (i = 0; i < 256; i++) { intel_crtc->lut_r[i] = i; intel_crtc->lut_g[i] = i; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 8f8d37d5663a..6770ae88370d 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -246,7 +246,7 @@ intel_dp_aux_ch(struct intel_output *intel_output, } if ((status & DP_AUX_CH_CTL_DONE) == 0) { - printk(KERN_ERR "dp_aux_ch not done status 0x%08x\n", status); + DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status); return -EBUSY; } @@ -254,11 +254,14 @@ intel_dp_aux_ch(struct intel_output *intel_output, * Timeouts occur when the sink is not connected */ if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) { - printk(KERN_ERR "dp_aux_ch receive error status 0x%08x\n", status); + DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status); return -EIO; } + + /* Timeouts occur when the device isn't connected, so they're + * "normal" -- don't fill the kernel log with these */ if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) { - printk(KERN_ERR "dp_aux_ch timeout status 0x%08x\n", status); + DRM_DEBUG("dp_aux_ch timeout status 0x%08x\n", status); return -ETIMEDOUT; } @@ -411,7 +414,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, dp_priv->link_bw = bws[clock]; dp_priv->lane_count = lane_count; adjusted_mode->clock = intel_dp_link_clock(dp_priv->link_bw); - printk(KERN_ERR "link bw %02x lane count %d clock %d\n", + DRM_DEBUG("Display port link bw %02x lane count %d clock %d\n", dp_priv->link_bw, dp_priv->lane_count, adjusted_mode->clock); return true; diff --git a/drivers/gpu/drm/i915/intel_dp_i2c.c b/drivers/gpu/drm/i915/intel_dp_i2c.c index 4e60f14b1a6d..a63b6f57d2d4 100644 --- a/drivers/gpu/drm/i915/intel_dp_i2c.c +++ b/drivers/gpu/drm/i915/intel_dp_i2c.c @@ -29,6 +29,7 @@ #include <linux/sched.h> #include <linux/i2c.h> #include "intel_dp.h" +#include "drmP.h" /* Run a single AUX_CH I2C transaction, writing/reading data as necessary */ @@ -84,7 +85,7 @@ i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode, msg, msg_bytes, reply, reply_bytes); if (ret < 0) { - printk(KERN_ERR "aux_ch failed %d\n", ret); + DRM_DEBUG("aux_ch failed %d\n", ret); return ret; } switch (reply[0] & AUX_I2C_REPLY_MASK) { @@ -94,14 +95,14 @@ i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode, } return reply_bytes - 1; case AUX_I2C_REPLY_NACK: - printk(KERN_ERR "aux_ch nack\n"); + DRM_DEBUG("aux_ch nack\n"); return -EREMOTEIO; case AUX_I2C_REPLY_DEFER: - printk(KERN_ERR "aux_ch defer\n"); + DRM_DEBUG("aux_ch defer\n"); udelay(100); break; default: - printk(KERN_ERR "aux_ch invalid reply 0x%02x\n", reply[0]); + DRM_ERROR("aux_ch invalid reply 0x%02x\n", reply[0]); return -EREMOTEIO; } } @@ -223,7 +224,7 @@ i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter, if (ret >= 0) ret = num; i2c_algo_dp_aux_stop(adapter, reading); - printk(KERN_ERR "dp_aux_xfer return %d\n", ret); + DRM_DEBUG("dp_aux_xfer return %d\n", ret); return ret; } diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 1af7d68e3807..1d30802e773e 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -453,7 +453,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, size = ALIGN(size, PAGE_SIZE); fbo = drm_gem_object_alloc(dev, size); if (!fbo) { - printk(KERN_ERR "failed to allocate framebuffer\n"); + DRM_ERROR("failed to allocate framebuffer\n"); ret = -ENOMEM; goto out; } @@ -610,8 +610,8 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, par->dev = dev; /* To allow resizeing without swapping buffers */ - printk("allocated %dx%d fb: 0x%08x, bo %p\n", intel_fb->base.width, - intel_fb->base.height, obj_priv->gtt_offset, fbo); + DRM_DEBUG("allocated %dx%d fb: 0x%08x, bo %p\n", intel_fb->base.width, + intel_fb->base.height, obj_priv->gtt_offset, fbo); mutex_unlock(&dev->struct_mutex); return 0; @@ -698,13 +698,13 @@ static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc * } else intelfb_set_par(info); - printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + DRM_INFO("fb%d: %s frame buffer device\n", info->node, info->fix.id); /* Switch back to kernel console on panic */ kernelfb_mode = *modeset; atomic_notifier_chain_register(&panic_notifier_list, &paniced); - printk(KERN_INFO "registered panic notifier\n"); + DRM_DEBUG("registered panic notifier\n"); return 0; } @@ -852,13 +852,13 @@ static int intelfb_single_fb_probe(struct drm_device *dev) } else intelfb_set_par(info); - printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + DRM_INFO("fb%d: %s frame buffer device\n", info->node, info->fix.id); /* Switch back to kernel console on panic */ kernelfb_mode = *modeset; atomic_notifier_chain_register(&panic_notifier_list, &paniced); - printk(KERN_INFO "registered panic notifier\n"); + DRM_DEBUG("registered panic notifier\n"); return 0; } @@ -872,8 +872,8 @@ void intelfb_restore(void) { int ret; if ((ret = drm_crtc_helper_set_config(&kernelfb_mode)) != 0) { - printk(KERN_ERR "Failed to restore crtc configuration: %d\n", - ret); + DRM_ERROR("Failed to restore crtc configuration: %d\n", + ret); } } diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 9564ca44a977..9ab38efffecf 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -36,6 +36,7 @@ #include "intel_drv.h" #include "i915_drm.h" #include "i915_drv.h" +#include <linux/acpi.h> #define I915_LVDS "i915_lvds" @@ -252,14 +253,14 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, /* Should never happen!! */ if (!IS_I965G(dev) && intel_crtc->pipe == 0) { - printk(KERN_ERR "Can't support LVDS on pipe A\n"); + DRM_ERROR("Can't support LVDS on pipe A\n"); return false; } /* Should never happen!! */ list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, head) { if (tmp_encoder != encoder && tmp_encoder->crtc == encoder->crtc) { - printk(KERN_ERR "Can't enable LVDS and another " + DRM_ERROR("Can't enable LVDS and another " "encoder on the same pipe\n"); return false; } @@ -788,6 +789,65 @@ static const struct dmi_system_id intel_no_lvds[] = { { } /* terminating entry */ }; +#ifdef CONFIG_ACPI +/* + * check_lid_device -- check whether @handle is an ACPI LID device. + * @handle: ACPI device handle + * @level : depth in the ACPI namespace tree + * @context: the number of LID device when we find the device + * @rv: a return value to fill if desired (Not use) + */ +static acpi_status +check_lid_device(acpi_handle handle, u32 level, void *context, + void **return_value) +{ + struct acpi_device *acpi_dev; + int *lid_present = context; + + acpi_dev = NULL; + /* Get the acpi device for device handle */ + if (acpi_bus_get_device(handle, &acpi_dev) || !acpi_dev) { + /* If there is no ACPI device for handle, return */ + return AE_OK; + } + + if (!strncmp(acpi_device_hid(acpi_dev), "PNP0C0D", 7)) + *lid_present = 1; + + return AE_OK; +} + +/** + * check whether there exists the ACPI LID device by enumerating the ACPI + * device tree. + */ +static int intel_lid_present(void) +{ + int lid_present = 0; + + if (acpi_disabled) { + /* If ACPI is disabled, there is no ACPI device tree to + * check, so assume the LID device would have been present. + */ + return 1; + } + + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + check_lid_device, &lid_present, NULL); + + return lid_present; +} +#else +static int intel_lid_present(void) +{ + /* In the absence of ACPI built in, assume that the LID device would + * have been present. + */ + return 1; +} +#endif + /** * intel_lvds_init - setup LVDS connectors on this device * @dev: drm device @@ -811,6 +871,16 @@ void intel_lvds_init(struct drm_device *dev) if (dmi_check_system(intel_no_lvds)) return; + /* Assume that any device without an ACPI LID device also doesn't + * have an integrated LVDS. We would be better off parsing the BIOS + * to get a reliable indicator, but that code isn't written yet. + * + * In the case of all-in-one desktops using LVDS that we've seen, + * they're using SDVO LVDS. + */ + if (!intel_lid_present()) + return; + if (IS_IGDNG(dev)) { if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0) return; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index f03473779feb..4f0c30948bc4 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -68,12 +68,23 @@ struct intel_sdvo_priv { * This is set if we treat the device as HDMI, instead of DVI. */ bool is_hdmi; + /** * This is set if we detect output of sdvo device as LVDS. */ bool is_lvds; /** + * This is sdvo flags for input timing. + */ + uint8_t sdvo_flags; + + /** + * This is sdvo fixed pannel mode pointer + */ + struct drm_display_mode *sdvo_lvds_fixed_mode; + + /** * Returned SDTV resolutions allowed for the current format, if the * device reported it. */ @@ -592,6 +603,7 @@ intel_sdvo_create_preferred_input_timing(struct intel_output *output, uint16_t height) { struct intel_sdvo_preferred_input_timing_args args; + struct intel_sdvo_priv *sdvo_priv = output->dev_priv; uint8_t status; memset(&args, 0, sizeof(args)); @@ -599,7 +611,12 @@ intel_sdvo_create_preferred_input_timing(struct intel_output *output, args.width = width; args.height = height; args.interlace = 0; - args.scaled = 0; + + if (sdvo_priv->is_lvds && + (sdvo_priv->sdvo_lvds_fixed_mode->hdisplay != width || + sdvo_priv->sdvo_lvds_fixed_mode->vdisplay != height)) + args.scaled = 1; + intel_sdvo_write_cmd(output, SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING, &args, sizeof(args)); status = intel_sdvo_read_response(output, NULL, 0); @@ -944,12 +961,7 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, struct intel_output *output = enc_to_intel_output(encoder); struct intel_sdvo_priv *dev_priv = output->dev_priv; - if (!dev_priv->is_tv) { - /* Make the CRTC code factor in the SDVO pixel multiplier. The - * SDVO device will be told of the multiplier during mode_set. - */ - adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode); - } else { + if (dev_priv->is_tv) { struct intel_sdvo_dtd output_dtd; bool success; @@ -980,6 +992,47 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, intel_sdvo_get_preferred_input_timing(output, &input_dtd); intel_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd); + dev_priv->sdvo_flags = input_dtd.part2.sdvo_flags; + + drm_mode_set_crtcinfo(adjusted_mode, 0); + + mode->clock = adjusted_mode->clock; + + adjusted_mode->clock *= + intel_sdvo_get_pixel_multiplier(mode); + } else { + return false; + } + } else if (dev_priv->is_lvds) { + struct intel_sdvo_dtd output_dtd; + bool success; + + drm_mode_set_crtcinfo(dev_priv->sdvo_lvds_fixed_mode, 0); + /* Set output timings */ + intel_sdvo_get_dtd_from_mode(&output_dtd, + dev_priv->sdvo_lvds_fixed_mode); + + intel_sdvo_set_target_output(output, + dev_priv->controlled_output); + intel_sdvo_set_output_timing(output, &output_dtd); + + /* Set the input timing to the screen. Assume always input 0. */ + intel_sdvo_set_target_input(output, true, false); + + + success = intel_sdvo_create_preferred_input_timing( + output, + mode->clock / 10, + mode->hdisplay, + mode->vdisplay); + + if (success) { + struct intel_sdvo_dtd input_dtd; + + intel_sdvo_get_preferred_input_timing(output, + &input_dtd); + intel_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd); + dev_priv->sdvo_flags = input_dtd.part2.sdvo_flags; drm_mode_set_crtcinfo(adjusted_mode, 0); @@ -990,6 +1043,12 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, } else { return false; } + + } else { + /* Make the CRTC code factor in the SDVO pixel multiplier. The + * SDVO device will be told of the multiplier during mode_set. + */ + adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode); } return true; } @@ -1033,15 +1092,16 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, /* We have tried to get input timing in mode_fixup, and filled into adjusted_mode */ - if (sdvo_priv->is_tv) + if (sdvo_priv->is_tv || sdvo_priv->is_lvds) { intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode); - else + input_dtd.part2.sdvo_flags = sdvo_priv->sdvo_flags; + } else intel_sdvo_get_dtd_from_mode(&input_dtd, mode); /* If it's a TV, we already set the output timing in mode_fixup. * Otherwise, the output timing is equal to the input timing. */ - if (!sdvo_priv->is_tv) { + if (!sdvo_priv->is_tv && !sdvo_priv->is_lvds) { /* Set the output timing to the screen */ intel_sdvo_set_target_output(output, sdvo_priv->controlled_output); @@ -1116,6 +1176,8 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT; } + if (sdvo_priv->sdvo_flags & SDVO_NEED_TO_STALL) + sdvox |= SDVO_STALL_SELECT; intel_sdvo_write_sdvox(output, sdvox); } @@ -1276,6 +1338,17 @@ static int intel_sdvo_mode_valid(struct drm_connector *connector, if (sdvo_priv->pixel_clock_max < mode->clock) return MODE_CLOCK_HIGH; + if (sdvo_priv->is_lvds == true) { + if (sdvo_priv->sdvo_lvds_fixed_mode == NULL) + return MODE_PANEL; + + if (mode->hdisplay > sdvo_priv->sdvo_lvds_fixed_mode->hdisplay) + return MODE_PANEL; + + if (mode->vdisplay > sdvo_priv->sdvo_lvds_fixed_mode->vdisplay) + return MODE_PANEL; + } + return MODE_OK; } @@ -1549,6 +1622,8 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector) { struct intel_output *intel_output = to_intel_output(connector); struct drm_i915_private *dev_priv = connector->dev->dev_private; + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + struct drm_display_mode *newmode; /* * Attempt to get the mode list from DDC. @@ -1557,11 +1632,10 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector) */ intel_ddc_get_modes(intel_output); if (list_empty(&connector->probed_modes) == false) - return; + goto end; /* Fetch modes from VBT */ if (dev_priv->sdvo_lvds_vbt_mode != NULL) { - struct drm_display_mode *newmode; newmode = drm_mode_duplicate(connector->dev, dev_priv->sdvo_lvds_vbt_mode); if (newmode != NULL) { @@ -1571,6 +1645,16 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector) drm_mode_probed_add(connector, newmode); } } + +end: + list_for_each_entry(newmode, &connector->probed_modes, head) { + if (newmode->type & DRM_MODE_TYPE_PREFERRED) { + sdvo_priv->sdvo_lvds_fixed_mode = + drm_mode_duplicate(connector->dev, newmode); + break; + } + } + } static int intel_sdvo_get_modes(struct drm_connector *connector) @@ -1593,14 +1677,20 @@ static int intel_sdvo_get_modes(struct drm_connector *connector) static void intel_sdvo_destroy(struct drm_connector *connector) { struct intel_output *intel_output = to_intel_output(connector); + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; if (intel_output->i2c_bus) intel_i2c_destroy(intel_output->i2c_bus); if (intel_output->ddc_bus) intel_i2c_destroy(intel_output->ddc_bus); + if (sdvo_priv->sdvo_lvds_fixed_mode != NULL) + drm_mode_destroy(connector->dev, + sdvo_priv->sdvo_lvds_fixed_mode); + drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); + kfree(intel_output); } diff --git a/drivers/gpu/drm/i915/intel_sdvo_regs.h b/drivers/gpu/drm/i915/intel_sdvo_regs.h index 193938b7d7f9..ba5cdf8ae40b 100644 --- a/drivers/gpu/drm/i915/intel_sdvo_regs.h +++ b/drivers/gpu/drm/i915/intel_sdvo_regs.h @@ -715,6 +715,7 @@ struct intel_sdvo_enhancements_arg { #define SDVO_HBUF_TX_ONCE (2 << 6) #define SDVO_HBUF_TX_VSYNC (3 << 6) #define SDVO_CMD_GET_AUDIO_TX_INFO 0x9c +#define SDVO_NEED_TO_STALL (1 << 7) struct intel_sdvo_encode{ u8 dvi_rev; diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 40b75032ea47..fe949a12fe40 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -327,7 +327,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp, goto out_unref; kmap_offset = dev_offset - bo->vm_node->start; - if (unlikely(kmap_offset) >= bo->num_pages) { + if (unlikely(kmap_offset >= bo->num_pages)) { ret = -EFBIG; goto out_unref; } @@ -401,7 +401,7 @@ ssize_t ttm_bo_fbdev_io(struct ttm_buffer_object *bo, const char __user *wbuf, bool dummy; kmap_offset = (*f_pos >> PAGE_SHIFT); - if (unlikely(kmap_offset) >= bo->num_pages) + if (unlikely(kmap_offset >= bo->num_pages)) return -EFBIG; page_offset = *f_pos & ~PAGE_MASK; diff --git a/drivers/gpu/drm/via/via_irq.c b/drivers/gpu/drm/via/via_irq.c index c248c1d37268..5935b8842e86 100644 --- a/drivers/gpu/drm/via/via_irq.c +++ b/drivers/gpu/drm/via/via_irq.c @@ -183,7 +183,7 @@ int via_enable_vblank(struct drm_device *dev, int crtc) } status = VIA_READ(VIA_REG_INTERRUPT); - VIA_WRITE(VIA_REG_INTERRUPT, status & VIA_IRQ_VBLANK_ENABLE); + VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_VBLANK_ENABLE); VIA_WRITE8(0x83d4, 0x11); VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); @@ -194,6 +194,10 @@ int via_enable_vblank(struct drm_device *dev, int crtc) void via_disable_vblank(struct drm_device *dev, int crtc) { drm_via_private_t *dev_priv = dev->dev_private; + u32 status; + + status = VIA_READ(VIA_REG_INTERRUPT); + VIA_WRITE(VIA_REG_INTERRUPT, status & ~VIA_IRQ_VBLANK_ENABLE); VIA_WRITE8(0x83d4, 0x11); VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f2c21d5d24e8..5eb10c2ce665 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1075,14 +1075,16 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event); */ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt) { - struct hid_report_enum *report_enum = hid->report_enum + type; - struct hid_driver *hdrv = hid->driver; + struct hid_report_enum *report_enum; + struct hid_driver *hdrv; struct hid_report *report; unsigned int i; int ret; if (!hid || !hid->driver) return -ENODEV; + report_enum = hid->report_enum + type; + hdrv = hid->driver; if (!size) { dbg_hid("empty report\n"); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 76c4bbe9dccb..3c1fcb7640ab 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -22,7 +22,6 @@ #include <linux/list.h> #include <linux/mm.h> #include <linux/mutex.h> -#include <linux/smp_lock.h> #include <linux/spinlock.h> #include <asm/unaligned.h> #include <asm/byteorder.h> diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 9e9421525fb9..215b2addddbb 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -527,8 +527,10 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, goto goodreturn; case HIDIOCGCOLLECTIONINDEX: + i = field->usage[uref->usage_index].collection_index; + unlock_kernel(); kfree(uref_multi); - return field->usage[uref->usage_index].collection_index; + return i; case HIDIOCGUSAGES: for (i = 0; i < uref_multi->num_values; i++) uref_multi->values[i] = diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c index ad2b3431b725..7d3f15d32fdf 100644 --- a/drivers/hwmon/abituguru3.c +++ b/drivers/hwmon/abituguru3.c @@ -357,7 +357,7 @@ static const struct abituguru3_motherboard_info abituguru3_motherboards[] = { { "AUX5 Fan", 39, 2, 60, 1, 0 }, { NULL, 0, 0, 0, 0, 0 } } }, - { 0x0014, NULL /* Abit AB9 Pro, need DMI string */, { + { 0x0014, "AB9", /* + AB9 Pro */ { { "CPU Core", 0, 0, 10, 1, 0 }, { "DDR", 1, 0, 10, 1, 0 }, { "DDR VTT", 2, 0, 10, 1, 0 }, @@ -455,7 +455,7 @@ static const struct abituguru3_motherboard_info abituguru3_motherboards[] = { { "AUX3 FAN", 37, 2, 60, 1, 0 }, { NULL, 0, 0, 0, 0, 0 } } }, - { 0x0018, NULL /* Unknown, need DMI string */, { + { 0x0018, "AB9 QuadGT", { { "CPU Core", 0, 0, 10, 1, 0 }, { "DDR2", 1, 0, 20, 1, 0 }, { "DDR2 VTT", 2, 0, 10, 1, 0 }, @@ -564,7 +564,7 @@ static const struct abituguru3_motherboard_info abituguru3_motherboards[] = { { "AUX3 Fan", 36, 2, 60, 1, 0 }, { NULL, 0, 0, 0, 0, 0 } } }, - { 0x001C, NULL /* Unknown, need DMI string */, { + { 0x001C, "IX38 QuadGT", { { "CPU Core", 0, 0, 10, 1, 0 }, { "DDR2", 1, 0, 20, 1, 0 }, { "DDR2 VTT", 2, 0, 10, 1, 0 }, diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 86142a858238..58f66be61b1f 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -418,6 +418,7 @@ static ssize_t set_div(struct device *dev, struct device_attribute *devattr, data->count = 3; break; default: + mutex_unlock(&data->update_lock); dev_err(&client->dev, "illegal value for fan divider (%d)\n", div); return -EINVAL; diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 56cd6004da36..6290a259456e 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -257,7 +257,7 @@ static inline int sht15_update_single_val(struct sht15_data *data, (data->flag == SHT15_READING_NOTHING), msecs_to_jiffies(timeout_msecs)); if (ret == 0) {/* timeout occurred */ - disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data));; + disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data)); sht15_connection_reset(data); return -ETIME; } diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 3fae3a91ce5b..c89687a10835 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -187,6 +187,11 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev) davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKH_REG, clkh); davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKL_REG, clkl); + /* Respond at reserved "SMBus Host" slave address" (and zero); + * we seem to have no option to not respond... + */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_OAR_REG, 0x08); + dev_dbg(dev->dev, "input_clock = %d, CLK = %d\n", input_clock, clk); dev_dbg(dev->dev, "PSC = %d\n", davinci_i2c_read_reg(dev, DAVINCI_I2C_PSC_REG)); @@ -387,7 +392,7 @@ static void terminate_write(struct davinci_i2c_dev *dev) davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); if (!dev->terminate) - dev_err(dev->dev, "TDR IRQ while no data to send\n"); + dev_dbg(dev->dev, "TDR IRQ while no data to send\n"); } /* @@ -473,9 +478,14 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id) break; case DAVINCI_I2C_IVR_AAS: - dev_warn(dev->dev, "Address as slave interrupt\n"); - }/* switch */ - }/* while */ + dev_dbg(dev->dev, "Address as slave interrupt\n"); + break; + + default: + dev_warn(dev->dev, "Unrecognized irq stat %d\n", stat); + break; + } + } return count ? IRQ_HANDLED : IRQ_NONE; } @@ -505,7 +515,7 @@ static int davinci_i2c_probe(struct platform_device *pdev) return -ENODEV; } - ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1, + ioarea = request_mem_region(mem->start, resource_size(mem), pdev->name); if (!ioarea) { dev_err(&pdev->dev, "I2C region already claimed\n"); @@ -523,7 +533,7 @@ static int davinci_i2c_probe(struct platform_device *pdev) dev->irq = irq->start; platform_set_drvdata(pdev, dev); - dev->clk = clk_get(&pdev->dev, "I2CCLK"); + dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) { r = -ENODEV; goto err_free_mem; @@ -568,7 +578,7 @@ err_free_mem: put_device(&pdev->dev); kfree(dev); err_release_region: - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); return r; } @@ -591,7 +601,7 @@ static int davinci_i2c_remove(struct platform_device *pdev) kfree(dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); return 0; } diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index e4476743f203..b1bc6e277d2a 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -85,10 +85,11 @@ static void dump_iic_regs(const char* header, struct ibm_iic_private* dev) { volatile struct iic_regs __iomem *iic = dev->vaddr; printk(KERN_DEBUG "ibm-iic%d: %s\n", dev->idx, header); - printk(KERN_DEBUG " cntl = 0x%02x, mdcntl = 0x%02x\n" - KERN_DEBUG " sts = 0x%02x, extsts = 0x%02x\n" - KERN_DEBUG " clkdiv = 0x%02x, xfrcnt = 0x%02x\n" - KERN_DEBUG " xtcntlss = 0x%02x, directcntl = 0x%02x\n", + printk(KERN_DEBUG + " cntl = 0x%02x, mdcntl = 0x%02x\n" + " sts = 0x%02x, extsts = 0x%02x\n" + " clkdiv = 0x%02x, xfrcnt = 0x%02x\n" + " xtcntlss = 0x%02x, directcntl = 0x%02x\n", in_8(&iic->cntl), in_8(&iic->mdcntl), in_8(&iic->sts), in_8(&iic->extsts), in_8(&iic->clkdiv), in_8(&iic->xfrcnt), in_8(&iic->xtcntlss), in_8(&iic->directcntl)); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index ad8d2010c921..fdd83277c8a8 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -806,7 +806,7 @@ omap_i2c_probe(struct platform_device *pdev) return -ENODEV; } - ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1, + ioarea = request_mem_region(mem->start, resource_size(mem), pdev->name); if (!ioarea) { dev_err(&pdev->dev, "I2C region already claimed\n"); @@ -905,7 +905,7 @@ err_free_mem: platform_set_drvdata(pdev, NULL); kfree(dev); err_release_region: - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); return r; } @@ -925,7 +925,7 @@ omap_i2c_remove(struct platform_device *pdev) iounmap(dev->base); kfree(dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); return 0; } diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 1c01083b01b5..4f3d99cd1692 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -563,7 +563,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) goto err_irq; } - size = (res->end - res->start) + 1; + size = resource_size(res); pd->reg = ioremap(res->start, size); if (pd->reg == NULL) { diff --git a/drivers/i2c/busses/i2c-simtec.c b/drivers/i2c/busses/i2c-simtec.c index 042fda295f3a..6407f47bda82 100644 --- a/drivers/i2c/busses/i2c-simtec.c +++ b/drivers/i2c/busses/i2c-simtec.c @@ -92,7 +92,7 @@ static int simtec_i2c_probe(struct platform_device *dev) goto err; } - size = (res->end-res->start)+1; + size = resource_size(res); pd->ioarea = request_mem_region(res->start, size, dev->name); if (pd->ioarea == NULL) { diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 695181120cdb..7f878017b736 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -455,6 +455,7 @@ static void idedisk_prepare_flush(struct request_queue *q, struct request *rq) rq->cmd_type = REQ_TYPE_ATA_TASKFILE; rq->special = cmd; + cmd->rq = rq; } ide_devset_get(multcount, mult_count); diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 013dc595fab6..bc5fb12b913c 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1064,6 +1064,7 @@ static int idetape_blkdev_ioctl(ide_drive_t *drive, unsigned int cmd, tape->best_dsc_rw_freq = config.dsc_rw_frequency; break; case 0x0350: + memset(&config, 0, sizeof(config)); config.dsc_rw_frequency = (int) tape->best_dsc_rw_freq; config.nr_stages = 1; if (copy_to_user(argp, &config, sizeof(config))) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 114efd8dc8f5..1148140d08a1 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -608,8 +608,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, p, compat_mode); if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) - return str_to_user(dev_name(&evdev->dev), - _IOC_SIZE(cmd), p); + return str_to_user(dev->name, _IOC_SIZE(cmd), p); if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) return str_to_user(dev->phys, _IOC_SIZE(cmd), p); diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 0e12f89276a3..4cfd084fa897 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -536,7 +536,7 @@ static int joydev_ioctl_common(struct joydev *joydev, default: if ((cmd & ~IOCSIZE_MASK) == JSIOCGNAME(0)) { int len; - const char *name = dev_name(&dev->dev); + const char *name = dev->name; if (!name) return 0; diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index b868b8d5fbb3..f155ad8cdae7 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -470,20 +470,20 @@ static void xpad_irq_out(struct urb *urb) status = urb->status; switch (status) { - case 0: + case 0: /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", - __func__, status); - return; - default: - dbg("%s - nonzero urb status received: %d", - __func__, status); - goto exit; + return; + + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __func__, status); + return; + + default: + dbg("%s - nonzero urb status received: %d", __func__, status); + goto exit; } exit: diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 9d8f796c6745..a6b989a9dc07 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -12,6 +12,42 @@ menuconfig INPUT_KEYBOARD if INPUT_KEYBOARD +config KEYBOARD_AAED2000 + tristate "AAED-2000 keyboard" + depends on MACH_AAED2000 + select INPUT_POLLDEV + default y + help + Say Y here to enable the keyboard on the Agilent AAED-2000 + development board. + + To compile this driver as a module, choose M here: the + module will be called aaed2000_kbd. + +config KEYBOARD_AMIGA + tristate "Amiga keyboard" + depends on AMIGA + help + Say Y here if you are running Linux on any AMIGA and have a keyboard + attached. + + To compile this driver as a module, choose M here: the + module will be called amikbd. + +config ATARI_KBD_CORE + bool + +config KEYBOARD_ATARI + tristate "Atari keyboard" + depends on ATARI + select ATARI_KBD_CORE + help + Say Y here if you are running Linux on any Atari and have a keyboard + attached. + + To compile this driver as a module, choose M here: the + module will be called atakbd. + config KEYBOARD_ATKBD tristate "AT keyboard" if EMBEDDED || !X86 default y @@ -68,69 +104,14 @@ config KEYBOARD_ATKBD_RDI_KEYCODES right-hand column will be interpreted as the key shown in the left-hand column. -config KEYBOARD_SUNKBD - tristate "Sun Type 4 and Type 5 keyboard" - select SERIO - help - Say Y here if you want to use a Sun Type 4 or Type 5 keyboard, - connected either to the Sun keyboard connector or to an serial - (RS-232) port via a simple adapter. - - To compile this driver as a module, choose M here: the - module will be called sunkbd. - -config KEYBOARD_LKKBD - tristate "DECstation/VAXstation LK201/LK401 keyboard" - select SERIO - help - Say Y here if you want to use a LK201 or LK401 style serial - keyboard. This keyboard is also useable on PCs if you attach - it with the inputattach program. The connector pinout is - described within lkkbd.c. - - To compile this driver as a module, choose M here: the - module will be called lkkbd. - -config KEYBOARD_LOCOMO - tristate "LoCoMo Keyboard Support" - depends on SHARP_LOCOMO && INPUT_KEYBOARD - help - Say Y here if you are running Linux on a Sharp Zaurus Collie or Poodle based PDA - - To compile this driver as a module, choose M here: the - module will be called locomokbd. - -config KEYBOARD_XTKBD - tristate "XT keyboard" - select SERIO - help - Say Y here if you want to use the old IBM PC/XT keyboard (or - compatible) on your system. This is only possible with a - parallel port keyboard adapter, you cannot connect it to the - keyboard port on a PC that runs Linux. - - To compile this driver as a module, choose M here: the - module will be called xtkbd. - -config KEYBOARD_NEWTON - tristate "Newton keyboard" - select SERIO - help - Say Y here if you have a Newton keyboard on a serial port. - - To compile this driver as a module, choose M here: the - module will be called newtonkbd. - -config KEYBOARD_STOWAWAY - tristate "Stowaway keyboard" - select SERIO +config KEYBOARD_BFIN + tristate "Blackfin BF54x keypad support" + depends on (BF54x && !BF544) help - Say Y here if you have a Stowaway keyboard on a serial port. - Stowaway compatible keyboards like Dicota Input-PDA keyboard - are also supported by this driver. + Say Y here if you want to use the BF54x keypad. To compile this driver as a module, choose M here: the - module will be called stowaway. + module will be called bf54x-keys. config KEYBOARD_CORGI tristate "Corgi keyboard" @@ -143,61 +124,50 @@ 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 +config KEYBOARD_LKKBD + tristate "DECstation/VAXstation LK201/LK401 keyboard" + select SERIO help - Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000, - SL-C3000 and Sl-C3100 series of PDAs. + Say Y here if you want to use a LK201 or LK401 style serial + keyboard. This keyboard is also useable on PCs if you attach + it with the inputattach program. The connector pinout is + described within lkkbd.c. To compile this driver as a module, choose M here: the - module will be called spitzkbd. + module will be called lkkbd. -config KEYBOARD_TOSA - tristate "Tosa keyboard" - depends on MACH_TOSA - default y +config KEYBOARD_EP93XX + tristate "EP93xx Matrix Keypad support" + depends on ARCH_EP93XX help - Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa) + Say Y here to enable the matrix keypad on the Cirrus EP93XX. To compile this driver as a module, choose M here: the - module will be called tosakbd. + module will be called ep93xx_keypad. -config KEYBOARD_TOSA_USE_EXT_KEYCODES - bool "Tosa keyboard: use extended keycodes" - depends on KEYBOARD_TOSA - default n +config KEYBOARD_GPIO + tristate "GPIO Buttons" + depends on GENERIC_GPIO help - Say Y here to enable the tosa keyboard driver to generate extended - (>= 127) keycodes. Be aware, that they can't be correctly interpreted - by either console keyboard driver or by Kdrive keybd driver. - - Say Y only if you know, what you are doing! + This driver implements support for buttons connected + to GPIO pins of various CPUs (and some other chips). -config KEYBOARD_AMIGA - tristate "Amiga keyboard" - depends on AMIGA - help - Say Y here if you are running Linux on any AMIGA and have a keyboard - attached. + Say Y here if your device has buttons connected + directly to such GPIO pins. Your board-specific + setup logic must also provide a platform device, + with configuration data saying which GPIOs are used. To compile this driver as a module, choose M here: the - module will be called amikbd. + module will be called gpio_keys. -config ATARI_KBD_CORE - bool - -config KEYBOARD_ATARI - tristate "Atari keyboard" - depends on ATARI - select ATARI_KBD_CORE +config KEYBOARD_MATRIX + tristate "GPIO driven matrix keypad support" + depends on GENERIC_GPIO help - Say Y here if you are running Linux on any Atari and have a keyboard - attached. + Enable support for GPIO driven matrix keypad. To compile this driver as a module, choose M here: the - module will be called atakbd. + module will be called matrix_keypad. config KEYBOARD_HIL_OLD tristate "HP HIL keyboard support (simple driver)" @@ -261,20 +231,39 @@ config KEYBOARD_LM8323 To compile this driver as a module, choose M here: the module will be called lm8323. -config KEYBOARD_OMAP - tristate "TI OMAP keypad support" - depends on (ARCH_OMAP1 || ARCH_OMAP2) +config KEYBOARD_LOCOMO + tristate "LoCoMo Keyboard Support" + depends on SHARP_LOCOMO help - Say Y here if you want to use the OMAP keypad. + Say Y here if you are running Linux on a Sharp Zaurus Collie or Poodle based PDA To compile this driver as a module, choose M here: the - module will be called omap-keypad. + module will be called locomokbd. + +config KEYBOARD_MAPLE + tristate "Maple bus keyboard" + depends on SH_DREAMCAST && MAPLE + help + Say Y here if you have a Dreamcast console running Linux and have + a keyboard attached to its Maple bus. + + To compile this driver as a module, choose M here: the + module will be called maple_keyb. + +config KEYBOARD_NEWTON + tristate "Newton keyboard" + select SERIO + help + Say Y here if you have a Newton keyboard on a serial port. + + To compile this driver as a module, choose M here: the + module will be called newtonkbd. config KEYBOARD_PXA27x tristate "PXA27x/PXA3xx keypad support" depends on PXA27x || PXA3xx help - Enable support for PXA27x/PXA3xx keypad controller + Enable support for PXA27x/PXA3xx keypad controller. To compile this driver as a module, choose M here: the module will be called pxa27x_keypad. @@ -288,51 +277,38 @@ config KEYBOARD_PXA930_ROTARY To compile this driver as a module, choose M here: the module will be called pxa930_rotary. -config KEYBOARD_AAED2000 - tristate "AAED-2000 keyboard" - depends on MACH_AAED2000 - select INPUT_POLLDEV +config KEYBOARD_SPITZ + tristate "Spitz keyboard" + depends on PXA_SHARPSL default y help - Say Y here to enable the keyboard on the Agilent AAED-2000 - development board. - - To compile this driver as a module, choose M here: the - module will be called aaed2000_kbd. - -config KEYBOARD_GPIO - tristate "GPIO Buttons" - depends on GENERIC_GPIO - help - This driver implements support for buttons connected - to GPIO pins of various CPUs (and some other chips). - - Say Y here if your device has buttons connected - directly to such GPIO pins. Your board-specific - setup logic must also provide a platform device, - with configuration data saying which GPIOs are used. + 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 gpio-keys. + module will be called spitzkbd. -config KEYBOARD_MAPLE - tristate "Maple bus keyboard" - depends on SH_DREAMCAST && MAPLE +config KEYBOARD_STOWAWAY + tristate "Stowaway keyboard" + select SERIO help - Say Y here if you have a Dreamcast console running Linux and have - a keyboard attached to its Maple bus. + Say Y here if you have a Stowaway keyboard on a serial port. + Stowaway compatible keyboards like Dicota Input-PDA keyboard + are also supported by this driver. To compile this driver as a module, choose M here: the - module will be called maple_keyb. + module will be called stowaway. -config KEYBOARD_BFIN - tristate "Blackfin BF54x keypad support" - depends on (BF54x && !BF544) +config KEYBOARD_SUNKBD + tristate "Sun Type 4 and Type 5 keyboard" + select SERIO help - Say Y here if you want to use the BF54x keypad. + Say Y here if you want to use a Sun Type 4 or Type 5 keyboard, + connected either to the Sun keyboard connector or to an serial + (RS-232) port via a simple adapter. To compile this driver as a module, choose M here: the - module will be called bf54x-keys. + module will be called sunkbd. config KEYBOARD_SH_KEYSC tristate "SuperH KEYSC keypad support" @@ -344,13 +320,45 @@ config KEYBOARD_SH_KEYSC To compile this driver as a module, choose M here: the module will be called sh_keysc. -config KEYBOARD_EP93XX - tristate "EP93xx Matrix Keypad support" - depends on ARCH_EP93XX +config KEYBOARD_OMAP + tristate "TI OMAP keypad support" + depends on (ARCH_OMAP1 || ARCH_OMAP2) help - Say Y here to enable the matrix keypad on the Cirrus EP93XX. + Say Y here if you want to use the OMAP keypad. To compile this driver as a module, choose M here: the - module will be called ep93xx_keypad. + module will be called omap-keypad. + +config KEYBOARD_TOSA + tristate "Tosa keyboard" + depends on MACH_TOSA + default y + help + Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa) + + To compile this driver as a module, choose M here: the + module will be called tosakbd. + +config KEYBOARD_TOSA_USE_EXT_KEYCODES + bool "Tosa keyboard: use extended keycodes" + depends on KEYBOARD_TOSA + help + Say Y here to enable the tosa keyboard driver to generate extended + (>= 127) keycodes. Be aware, that they can't be correctly interpreted + by either console keyboard driver or by Kdrive keybd driver. + + Say Y only if you know, what you are doing! + +config KEYBOARD_XTKBD + tristate "XT keyboard" + select SERIO + help + Say Y here if you want to use the old IBM PC/XT keyboard (or + compatible) on your system. This is only possible with a + parallel port keyboard adapter, you cannot connect it to the + keyboard port on a PC that runs Linux. + + To compile this driver as a module, choose M here: the + module will be called xtkbd. endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 156b647a259b..b5b5eae9724f 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -4,29 +4,30 @@ # Each configuration option enables a list of files. -obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o -obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o -obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o -obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o +obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o -obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o -obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o -obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o +obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o +obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o -obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o -obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o +obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o +obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o +obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o +obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o +obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o +obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o +obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o +obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o +obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o -obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o -obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o -obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o -obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o -obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o -obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o -obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o +obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o +obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o +obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o +obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o +obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index df3f8aa68115..95fe0452dae4 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -895,6 +895,13 @@ static unsigned int atkbd_amilo_pa1510_forced_release_keys[] = { }; /* + * Amilo Pi 3525 key release for Fn+Volume keys not working + */ +static unsigned int atkbd_amilo_pi3525_forced_release_keys[] = { + 0x20, 0xa0, 0x2e, 0xae, 0x30, 0xb0, -1U +}; + +/* * Amilo Xi 3650 key release for light touch bar not working */ static unsigned int atkbd_amilo_xi3650_forced_release_keys[] = { @@ -902,6 +909,13 @@ static unsigned int atkbd_amilo_xi3650_forced_release_keys[] = { }; /* + * Soltech TA12 system with broken key release on volume keys and mute key + */ +static unsigned int atkdb_soltech_ta12_forced_release_keys[] = { + 0xa0, 0xae, 0xb0, -1U +}; + +/* * atkbd_set_keycode_table() initializes keyboard's keycode table * according to the selected scancode set */ @@ -1568,6 +1582,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_amilo_pa1510_forced_release_keys, }, { + .ident = "Fujitsu Amilo Pi 3525", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 3525"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_amilo_pi3525_forced_release_keys, + }, + { .ident = "Fujitsu Amilo Xi 3650", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), @@ -1576,6 +1599,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .callback = atkbd_setup_forced_release, .driver_data = atkbd_amilo_xi3650_forced_release_keys, }, + { + .ident = "Soltech Corporation TA12", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Soltech Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "TA12"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkdb_soltech_ta12_forced_release_keys, + }, { } }; diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 2157cd7de00c..efed0c9e242e 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -29,7 +29,8 @@ struct gpio_button_data { struct gpio_keys_button *button; struct input_dev *input; - struct delayed_work work; + struct timer_list timer; + struct work_struct work; }; struct gpio_keys_drvdata { @@ -40,7 +41,7 @@ struct gpio_keys_drvdata { static void gpio_keys_report_event(struct work_struct *work) { struct gpio_button_data *bdata = - container_of(work, struct gpio_button_data, work.work); + container_of(work, struct gpio_button_data, work); struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; @@ -50,17 +51,25 @@ static void gpio_keys_report_event(struct work_struct *work) input_sync(input); } +static void gpio_keys_timer(unsigned long _data) +{ + struct gpio_button_data *data = (struct gpio_button_data *)_data; + + schedule_work(&data->work); +} + static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; struct gpio_keys_button *button = bdata->button; - unsigned long delay; BUG_ON(irq != gpio_to_irq(button->gpio)); - delay = button->debounce_interval ? - msecs_to_jiffies(button->debounce_interval) : 0; - schedule_delayed_work(&bdata->work, delay); + if (button->debounce_interval) + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(button->debounce_interval)); + else + schedule_work(&bdata->work); return IRQ_HANDLED; } @@ -107,7 +116,9 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) bdata->input = input; bdata->button = button; - INIT_DELAYED_WORK(&bdata->work, gpio_keys_report_event); + setup_timer(&bdata->timer, + gpio_keys_timer, (unsigned long)bdata); + INIT_WORK(&bdata->work, gpio_keys_report_event); error = gpio_request(button->gpio, button->desc ?: "gpio_keys"); if (error < 0) { @@ -166,7 +177,9 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) fail2: while (--i >= 0) { free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); - cancel_delayed_work_sync(&ddata->data[i].work); + if (pdata->buttons[i].debounce_interval) + del_timer_sync(&ddata->data[i].timer); + cancel_work_sync(&ddata->data[i].work); gpio_free(pdata->buttons[i].gpio); } @@ -190,7 +203,9 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) for (i = 0; i < pdata->nbuttons; i++) { int irq = gpio_to_irq(pdata->buttons[i].gpio); free_irq(irq, &ddata->data[i]); - cancel_delayed_work_sync(&ddata->data[i].work); + if (pdata->buttons[i].debounce_interval) + del_timer_sync(&ddata->data[i].timer); + cancel_work_sync(&ddata->data[i].work); gpio_free(pdata->buttons[i].gpio); } diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c new file mode 100644 index 000000000000..e9b2e7cb05be --- /dev/null +++ b/drivers/input/keyboard/matrix_keypad.c @@ -0,0 +1,453 @@ +/* + * GPIO driven matrix keyboard driver + * + * Copyright (c) 2008 Marek Vasut <marek.vasut@gmail.com> + * + * 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/types.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/input/matrix_keypad.h> + +struct matrix_keypad { + const struct matrix_keypad_platform_data *pdata; + struct input_dev *input_dev; + unsigned short *keycodes; + + uint32_t last_key_state[MATRIX_MAX_COLS]; + struct delayed_work work; + bool scan_pending; + bool stopped; + spinlock_t lock; +}; + +/* + * NOTE: normally the GPIO has to be put into HiZ when de-activated to cause + * minmal side effect when scanning other columns, here it is configured to + * be input, and it should work on most platforms. + */ +static void __activate_col(const struct matrix_keypad_platform_data *pdata, + int col, bool on) +{ + bool level_on = !pdata->active_low; + + if (on) { + gpio_direction_output(pdata->col_gpios[col], level_on); + } else { + gpio_set_value_cansleep(pdata->col_gpios[col], !level_on); + gpio_direction_input(pdata->col_gpios[col]); + } +} + +static void activate_col(const struct matrix_keypad_platform_data *pdata, + int col, bool on) +{ + __activate_col(pdata, col, on); + + if (on && pdata->col_scan_delay_us) + udelay(pdata->col_scan_delay_us); +} + +static void activate_all_cols(const struct matrix_keypad_platform_data *pdata, + bool on) +{ + int col; + + for (col = 0; col < pdata->num_col_gpios; col++) + __activate_col(pdata, col, on); +} + +static bool row_asserted(const struct matrix_keypad_platform_data *pdata, + int row) +{ + return gpio_get_value_cansleep(pdata->row_gpios[row]) ? + !pdata->active_low : pdata->active_low; +} + +static void enable_row_irqs(struct matrix_keypad *keypad) +{ + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i; + + for (i = 0; i < pdata->num_row_gpios; i++) + enable_irq(gpio_to_irq(pdata->row_gpios[i])); +} + +static void disable_row_irqs(struct matrix_keypad *keypad) +{ + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i; + + for (i = 0; i < pdata->num_row_gpios; i++) + disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); +} + +/* + * This gets the keys from keyboard and reports it to input subsystem + */ +static void matrix_keypad_scan(struct work_struct *work) +{ + struct matrix_keypad *keypad = + container_of(work, struct matrix_keypad, work.work); + struct input_dev *input_dev = keypad->input_dev; + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + uint32_t new_state[MATRIX_MAX_COLS]; + int row, col, code; + + /* de-activate all columns for scanning */ + activate_all_cols(pdata, false); + + memset(new_state, 0, sizeof(new_state)); + + /* assert each column and read the row status out */ + for (col = 0; col < pdata->num_col_gpios; col++) { + + activate_col(pdata, col, true); + + for (row = 0; row < pdata->num_row_gpios; row++) + new_state[col] |= + row_asserted(pdata, row) ? (1 << row) : 0; + + activate_col(pdata, col, false); + } + + for (col = 0; col < pdata->num_col_gpios; col++) { + uint32_t bits_changed; + + bits_changed = keypad->last_key_state[col] ^ new_state[col]; + if (bits_changed == 0) + continue; + + for (row = 0; row < pdata->num_row_gpios; row++) { + if ((bits_changed & (1 << row)) == 0) + continue; + + code = (row << 4) + col; + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, + keypad->keycodes[code], + new_state[col] & (1 << row)); + } + } + input_sync(input_dev); + + memcpy(keypad->last_key_state, new_state, sizeof(new_state)); + + activate_all_cols(pdata, true); + + /* Enable IRQs again */ + spin_lock_irq(&keypad->lock); + keypad->scan_pending = false; + enable_row_irqs(keypad); + spin_unlock_irq(&keypad->lock); +} + +static irqreturn_t matrix_keypad_interrupt(int irq, void *id) +{ + struct matrix_keypad *keypad = id; + unsigned long flags; + + spin_lock_irqsave(&keypad->lock, flags); + + /* + * See if another IRQ beaten us to it and scheduled the + * scan already. In that case we should not try to + * disable IRQs again. + */ + if (unlikely(keypad->scan_pending || keypad->stopped)) + goto out; + + disable_row_irqs(keypad); + keypad->scan_pending = true; + schedule_delayed_work(&keypad->work, + msecs_to_jiffies(keypad->pdata->debounce_ms)); + +out: + spin_unlock_irqrestore(&keypad->lock, flags); + return IRQ_HANDLED; +} + +static int matrix_keypad_start(struct input_dev *dev) +{ + struct matrix_keypad *keypad = input_get_drvdata(dev); + + keypad->stopped = false; + mb(); + + /* + * Schedule an immediate key scan to capture current key state; + * columns will be activated and IRQs be enabled after the scan. + */ + schedule_delayed_work(&keypad->work, 0); + + return 0; +} + +static void matrix_keypad_stop(struct input_dev *dev) +{ + struct matrix_keypad *keypad = input_get_drvdata(dev); + + keypad->stopped = true; + mb(); + flush_work(&keypad->work.work); + /* + * matrix_keypad_scan() will leave IRQs enabled; + * we should disable them now. + */ + disable_row_irqs(keypad); +} + +#ifdef CONFIG_PM +static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct matrix_keypad *keypad = platform_get_drvdata(pdev); + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i; + + matrix_keypad_stop(keypad->input_dev); + + if (device_may_wakeup(&pdev->dev)) + for (i = 0; i < pdata->num_row_gpios; i++) + enable_irq_wake(gpio_to_irq(pdata->row_gpios[i])); + + return 0; +} + +static int matrix_keypad_resume(struct platform_device *pdev) +{ + struct matrix_keypad *keypad = platform_get_drvdata(pdev); + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i; + + if (device_may_wakeup(&pdev->dev)) + for (i = 0; i < pdata->num_row_gpios; i++) + disable_irq_wake(gpio_to_irq(pdata->row_gpios[i])); + + matrix_keypad_start(keypad->input_dev); + + return 0; +} +#else +#define matrix_keypad_suspend NULL +#define matrix_keypad_resume NULL +#endif + +static int __devinit init_matrix_gpio(struct platform_device *pdev, + struct matrix_keypad *keypad) +{ + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i, err = -EINVAL; + + /* initialized strobe lines as outputs, activated */ + for (i = 0; i < pdata->num_col_gpios; i++) { + err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col"); + if (err) { + dev_err(&pdev->dev, + "failed to request GPIO%d for COL%d\n", + pdata->col_gpios[i], i); + goto err_free_cols; + } + + gpio_direction_output(pdata->col_gpios[i], !pdata->active_low); + } + + for (i = 0; i < pdata->num_row_gpios; i++) { + err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row"); + if (err) { + dev_err(&pdev->dev, + "failed to request GPIO%d for ROW%d\n", + pdata->row_gpios[i], i); + goto err_free_rows; + } + + gpio_direction_input(pdata->row_gpios[i]); + } + + for (i = 0; i < pdata->num_row_gpios; i++) { + err = request_irq(gpio_to_irq(pdata->row_gpios[i]), + matrix_keypad_interrupt, + IRQF_DISABLED | + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "matrix-keypad", keypad); + if (err) { + dev_err(&pdev->dev, + "Unable to acquire interrupt for GPIO line %i\n", + pdata->row_gpios[i]); + goto err_free_irqs; + } + } + + /* initialized as disabled - enabled by input->open */ + disable_row_irqs(keypad); + return 0; + +err_free_irqs: + while (--i >= 0) + free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); + i = pdata->num_row_gpios; +err_free_rows: + while (--i >= 0) + gpio_free(pdata->row_gpios[i]); + i = pdata->num_col_gpios; +err_free_cols: + while (--i >= 0) + gpio_free(pdata->col_gpios[i]); + + return err; +} + +static int __devinit matrix_keypad_probe(struct platform_device *pdev) +{ + const struct matrix_keypad_platform_data *pdata; + const struct matrix_keymap_data *keymap_data; + struct matrix_keypad *keypad; + struct input_dev *input_dev; + unsigned short *keycodes; + int i; + int err; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + keymap_data = pdata->keymap_data; + if (!keymap_data) { + dev_err(&pdev->dev, "no keymap data defined\n"); + return -EINVAL; + } + + if (!keymap_data->max_keymap_size) { + dev_err(&pdev->dev, "invalid keymap data supplied\n"); + return -EINVAL; + } + + keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL); + keycodes = kzalloc(keymap_data->max_keymap_size * + sizeof(keypad->keycodes), + GFP_KERNEL); + input_dev = input_allocate_device(); + if (!keypad || !keycodes || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + keypad->input_dev = input_dev; + keypad->pdata = pdata; + keypad->keycodes = keycodes; + keypad->stopped = true; + INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan); + spin_lock_init(&keypad->lock); + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_dev->open = matrix_keypad_start; + input_dev->close = matrix_keypad_stop; + + input_dev->keycode = keycodes; + input_dev->keycodesize = sizeof(*keycodes); + input_dev->keycodemax = keymap_data->max_keymap_size; + + for (i = 0; i < keymap_data->keymap_size; i++) { + unsigned int key = keymap_data->keymap[i]; + unsigned int row = KEY_ROW(key); + unsigned int col = KEY_COL(key); + unsigned short code = KEY_VAL(key); + + keycodes[(row << 4) + col] = code; + __set_bit(code, input_dev->keybit); + } + __clear_bit(KEY_RESERVED, input_dev->keybit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, keypad); + + err = init_matrix_gpio(pdev, keypad); + if (err) + goto err_free_mem; + + err = input_register_device(keypad->input_dev); + if (err) + goto err_free_mem; + + device_init_wakeup(&pdev->dev, pdata->wakeup); + platform_set_drvdata(pdev, keypad); + + return 0; + +err_free_mem: + input_free_device(input_dev); + kfree(keycodes); + kfree(keypad); + return err; +} + +static int __devexit matrix_keypad_remove(struct platform_device *pdev) +{ + struct matrix_keypad *keypad = platform_get_drvdata(pdev); + const struct matrix_keypad_platform_data *pdata = keypad->pdata; + int i; + + device_init_wakeup(&pdev->dev, 0); + + for (i = 0; i < pdata->num_row_gpios; i++) { + free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); + gpio_free(pdata->row_gpios[i]); + } + + for (i = 0; i < pdata->num_col_gpios; i++) + gpio_free(pdata->col_gpios[i]); + + input_unregister_device(keypad->input_dev); + platform_set_drvdata(pdev, NULL); + kfree(keypad->keycodes); + kfree(keypad); + + return 0; +} + +static struct platform_driver matrix_keypad_driver = { + .probe = matrix_keypad_probe, + .remove = __devexit_p(matrix_keypad_remove), + .suspend = matrix_keypad_suspend, + .resume = matrix_keypad_resume, + .driver = { + .name = "matrix-keypad", + .owner = THIS_MODULE, + }, +}; + +static int __init matrix_keypad_init(void) +{ + return platform_driver_register(&matrix_keypad_driver); +} + +static void __exit matrix_keypad_exit(void) +{ + platform_driver_unregister(&matrix_keypad_driver); +} + +module_init(matrix_keypad_init); +module_exit(matrix_keypad_exit); + +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:matrix-keypad"); diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c index 6d67af5387ad..21cb755a54fb 100644 --- a/drivers/input/misc/pcspkr.c +++ b/drivers/input/misc/pcspkr.c @@ -114,7 +114,7 @@ static int __devexit pcspkr_remove(struct platform_device *dev) return 0; } -static int pcspkr_suspend(struct platform_device *dev, pm_message_t state) +static int pcspkr_suspend(struct device *dev) { pcspkr_event(NULL, EV_SND, SND_BELL, 0); @@ -127,14 +127,18 @@ static void pcspkr_shutdown(struct platform_device *dev) pcspkr_event(NULL, EV_SND, SND_BELL, 0); } +static struct dev_pm_ops pcspkr_pm_ops = { + .suspend = pcspkr_suspend, +}; + static struct platform_driver pcspkr_platform_driver = { .driver = { .name = "pcspkr", .owner = THIS_MODULE, + .pm = &pcspkr_pm_ops, }, .probe = pcspkr_probe, .remove = __devexit_p(pcspkr_remove), - .suspend = pcspkr_suspend, .shutdown = pcspkr_shutdown, }; diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 7c8957dd22c0..26e17a9a22eb 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -646,6 +646,15 @@ static struct dmi_system_id dmi_ids[] __initdata = { }, { .callback = dmi_matched, + .ident = "Maxdata Pro 7000 DX", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MAXDATA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pro 7000"), + }, + .driver_data = keymap_fs_amilo_pro_v2000 + }, + { + .callback = dmi_matched, .ident = "Fujitsu N3510", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c index 5e5eb88d8d1e..7b6ce178f1b6 100644 --- a/drivers/input/mouse/gpio_mouse.c +++ b/drivers/input/mouse/gpio_mouse.c @@ -46,7 +46,7 @@ static void gpio_mouse_scan(struct input_polled_dev *dev) input_sync(input); } -static int __init gpio_mouse_probe(struct platform_device *pdev) +static int __devinit gpio_mouse_probe(struct platform_device *pdev) { struct gpio_mouse_platform_data *pdata = pdev->dev.platform_data; struct input_polled_dev *input_poll; @@ -170,10 +170,8 @@ static int __devexit gpio_mouse_remove(struct platform_device *pdev) return 0; } -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:gpio_mouse"); - static struct platform_driver gpio_mouse_device_driver = { + .probe = gpio_mouse_probe, .remove = __devexit_p(gpio_mouse_remove), .driver = { .name = "gpio_mouse", @@ -183,8 +181,7 @@ static struct platform_driver gpio_mouse_device_driver = { static int __init gpio_mouse_init(void) { - return platform_driver_probe(&gpio_mouse_device_driver, - gpio_mouse_probe); + return platform_driver_register(&gpio_mouse_device_driver); } module_init(gpio_mouse_init); @@ -197,3 +194,5 @@ module_exit(gpio_mouse_exit); MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); MODULE_DESCRIPTION("GPIO mouse driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio_mouse"); /* work with hotplug and coldplug */ + diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index fb8a3cd3ffd0..924e8ed7f2cf 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -392,6 +392,34 @@ static struct dmi_system_id __initdata i8042_dmi_reset_table[] = { DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."), }, }, + { + .ident = "Acer Aspire One 150", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"), + }, + }, + { + .ident = "Advent 4211", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"), + DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"), + }, + }, + { + .ident = "Medion Akoya Mini E1210", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), + DMI_MATCH(DMI_PRODUCT_NAME, "E1210"), + }, + }, + { + .ident = "Mivvy M310", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"), + DMI_MATCH(DMI_PRODUCT_NAME, "N10"), + }, + }, { } }; diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index f919bf57293c..582245c497eb 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -934,10 +934,11 @@ static bool i8042_suspended; static int i8042_suspend(struct platform_device *dev, pm_message_t state) { - if (!i8042_suspended && state.event == PM_EVENT_SUSPEND) { + if (!i8042_suspended && state.event == PM_EVENT_SUSPEND) i8042_controller_reset(); - i8042_suspended = true; - } + + i8042_suspended = state.event == PM_EVENT_SUSPEND || + state.event == PM_EVENT_FREEZE; return 0; } diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index fb17573f8f2d..d66f4944f2a0 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -935,10 +935,11 @@ static int serio_suspend(struct device *dev, pm_message_t state) { struct serio *serio = to_serio_port(dev); - if (!serio->suspended && state.event == PM_EVENT_SUSPEND) { + if (!serio->suspended && state.event == PM_EVENT_SUSPEND) serio_cleanup(serio); - serio->suspended = true; - } + + serio->suspended = state.event == PM_EVENT_SUSPEND || + state.event == PM_EVENT_FREEZE; return 0; } diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 38bf86384aeb..c896d6a21b7e 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -384,6 +384,8 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo) wacom_report_key(wcombo, BTN_STYLUS2, 0); wacom_report_key(wcombo, BTN_TOUCH, 0); wacom_report_abs(wcombo, ABS_WHEEL, 0); + if (wacom->features->type >= INTUOS3S) + wacom_report_abs(wcombo, ABS_Z, 0); } wacom_report_key(wcombo, wacom->tool[idx], 0); wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */ @@ -836,6 +838,7 @@ static struct wacom_features wacom_features[] = { { "Wacom DTU710", 8, 34080, 27660, 511, 0, PL }, { "Wacom DTF521", 8, 6282, 4762, 511, 0, PL }, { "Wacom DTF720", 8, 6858, 5506, 511, 0, PL }, + { "Wacom DTF720a", 8, 6858, 5506, 511, 0, PL }, { "Wacom Cintiq Partner",8, 20480, 15360, 511, 0, PTU }, { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 31, INTUOS }, { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS }, @@ -897,8 +900,9 @@ static struct usb_device_id wacom_ids[] = { { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x37) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x38) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x39) }, - { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC0) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC4) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC0) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC2) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x41) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x42) }, diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index 1ebfcab74662..8ff7e35c7069 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -408,6 +408,8 @@ static int if_write_room(struct tty_struct *tty) return retval; } +/* FIXME: This function does not have error returns */ + static int if_chars_in_buffer(struct tty_struct *tty) { struct cardstate *cs; diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c index 8df889b0c1a9..9de54202c90c 100644 --- a/drivers/isdn/hisax/hfc_usb.c +++ b/drivers/isdn/hisax/hfc_usb.c @@ -37,7 +37,6 @@ #include <linux/kernel_stat.h> #include <linux/usb.h> #include <linux/kernel.h> -#include <linux/smp_lock.h> #include <linux/sched.h> #include <linux/moduleparam.h> #include "hisax.h" diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index b4d4522e5071..2881a66c1aa9 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -13,6 +13,7 @@ #include <linux/isdn.h> #include <linux/delay.h> +#include <linux/smp_lock.h> #include "isdn_common.h" #include "isdn_tty.h" #ifdef CONFIG_ISDN_AUDIO diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c index e2f45019ebf0..3e1532a180ff 100644 --- a/drivers/isdn/mISDN/stack.c +++ b/drivers/isdn/mISDN/stack.c @@ -17,6 +17,7 @@ #include <linux/mISDNif.h> #include <linux/kthread.h> +#include <linux/smp_lock.h> #include "core.h" static u_int *debug; diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index 9c3138265f8e..01c591923793 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -38,8 +38,6 @@ struct lguest_pages #define CHANGED_GDT_TLS 4 /* Actually a subset of CHANGED_GDT */ #define CHANGED_ALL 3 -struct lguest; - struct lg_cpu { unsigned int id; struct lguest *lg; diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 9933eb861c71..ed1038164019 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -776,7 +776,7 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io) * But don't wait if split was due to the io size restriction */ if (unlikely(out_of_pages)) - congestion_wait(WRITE, HZ/100); + congestion_wait(BLK_RW_ASYNC, HZ/100); /* * With async crypto it is unsafe to share the crypto context @@ -1318,7 +1318,7 @@ static int crypt_iterate_devices(struct dm_target *ti, { struct crypt_config *cc = ti->private; - return fn(ti, cc->dev, cc->start, data); + return fn(ti, cc->dev, cc->start, ti->len, data); } static struct target_type crypt_target = { diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c index 4e5b843cd4d7..ebe7381f47c8 100644 --- a/drivers/md/dm-delay.c +++ b/drivers/md/dm-delay.c @@ -324,12 +324,12 @@ static int delay_iterate_devices(struct dm_target *ti, struct delay_c *dc = ti->private; int ret = 0; - ret = fn(ti, dc->dev_read, dc->start_read, data); + ret = fn(ti, dc->dev_read, dc->start_read, ti->len, data); if (ret) goto out; if (dc->dev_write) - ret = fn(ti, dc->dev_write, dc->start_write, data); + ret = fn(ti, dc->dev_write, dc->start_write, ti->len, data); out: return ret; diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 9184b6deb868..82f7d6e6b1ea 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -139,7 +139,7 @@ static int linear_iterate_devices(struct dm_target *ti, { struct linear_c *lc = ti->private; - return fn(ti, lc->dev, lc->start, data); + return fn(ti, lc->dev, lc->start, ti->len, data); } static struct target_type linear_target = { diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index c70604a20897..6f0d90d4a541 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1453,7 +1453,7 @@ static int multipath_iterate_devices(struct dm_target *ti, list_for_each_entry(pg, &m->priority_groups, list) { list_for_each_entry(p, &pg->pgpaths, list) { - ret = fn(ti, p->path.dev, ti->begin, data); + ret = fn(ti, p->path.dev, ti->begin, ti->len, data); if (ret) goto out; } diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index ce8868c768cc..9726577cde49 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -638,6 +638,7 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes) spin_lock_irq(&ms->lock); bio_list_merge(&ms->writes, &requeue); spin_unlock_irq(&ms->lock); + delayed_wake(ms); } /* @@ -1292,7 +1293,7 @@ static int mirror_iterate_devices(struct dm_target *ti, for (i = 0; !ret && i < ms->nr_mirrors; i++) ret = fn(ti, ms->mirror[i].dev, - ms->mirror[i].offset, data); + ms->mirror[i].offset, ti->len, data); return ret; } diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index b240e85ae39a..4e0e5937e42a 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -320,10 +320,11 @@ static int stripe_iterate_devices(struct dm_target *ti, int ret = 0; unsigned i = 0; - do + do { ret = fn(ti, sc->stripe[i].dev, - sc->stripe[i].physical_start, data); - while (!ret && ++i < sc->stripes); + sc->stripe[i].physical_start, + sc->stripe_width, data); + } while (!ret && ++i < sc->stripes); return ret; } diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 2cba557d9e61..d952b3441913 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -346,7 +346,7 @@ static void close_dev(struct dm_dev_internal *d, struct mapped_device *md) * If possible, this checks an area of a destination device is valid. */ static int device_area_is_valid(struct dm_target *ti, struct dm_dev *dev, - sector_t start, void *data) + sector_t start, sector_t len, void *data) { struct queue_limits *limits = data; struct block_device *bdev = dev->bdev; @@ -359,7 +359,7 @@ static int device_area_is_valid(struct dm_target *ti, struct dm_dev *dev, if (!dev_size) return 1; - if ((start >= dev_size) || (start + ti->len > dev_size)) { + if ((start >= dev_size) || (start + len > dev_size)) { DMWARN("%s: %s too small for target", dm_device_name(ti->table->md), bdevname(bdev, b)); return 0; @@ -377,11 +377,11 @@ static int device_area_is_valid(struct dm_target *ti, struct dm_dev *dev, return 0; } - if (ti->len & (logical_block_size_sectors - 1)) { + if (len & (logical_block_size_sectors - 1)) { DMWARN("%s: len=%llu not aligned to h/w " "logical block size %hu of %s", dm_device_name(ti->table->md), - (unsigned long long)ti->len, + (unsigned long long)len, limits->logical_block_size, bdevname(bdev, b)); return 0; } @@ -482,7 +482,7 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti, #define min_not_zero(l, r) (l == 0) ? r : ((r == 0) ? l : min(l, r)) int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev, - sector_t start, void *data) + sector_t start, sector_t len, void *data) { struct queue_limits *limits = data; struct block_device *bdev = dev->bdev; @@ -830,11 +830,6 @@ unsigned dm_table_get_type(struct dm_table *t) return t->type; } -bool dm_table_bio_based(struct dm_table *t) -{ - return dm_table_get_type(t) == DM_TYPE_BIO_BASED; -} - bool dm_table_request_based(struct dm_table *t) { return dm_table_get_type(t) == DM_TYPE_REQUEST_BASED; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 9acd54a5cffb..8a311ea0d441 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2203,16 +2203,6 @@ int dm_swap_table(struct mapped_device *md, struct dm_table *table) goto out; } - /* - * It is enought that blk_queue_ordered() is called only once when - * the first bio-based table is bound. - * - * This setting should be moved to alloc_dev() when request-based dm - * supports barrier. - */ - if (!md->map && dm_table_bio_based(table)) - blk_queue_ordered(md->queue, QUEUE_ORDERED_DRAIN, NULL); - __unbind(md); r = __bind(md, table, &limits); diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 23278ae80f08..a7663eba17e2 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -61,7 +61,6 @@ int dm_table_any_congested(struct dm_table *t, int bdi_bits); int dm_table_any_busy_target(struct dm_table *t); int dm_table_set_type(struct dm_table *t); unsigned dm_table_get_type(struct dm_table *t); -bool dm_table_bio_based(struct dm_table *t); bool dm_table_request_based(struct dm_table *t); int dm_table_alloc_md_mempools(struct dm_table *t); void dm_table_free_md_mempools(struct dm_table *t); diff --git a/drivers/md/md.c b/drivers/md/md.c index 0f4a70c43ffc..d4351ff0849f 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1756,9 +1756,10 @@ static void print_sb_1(struct mdp_superblock_1 *sb) __u8 *uuid; uuid = sb->set_uuid; - printk(KERN_INFO "md: SB: (V:%u) (F:0x%08x) Array-ID:<%02x%02x%02x%02x" - ":%02x%02x:%02x%02x:%02x%02x:%02x%02x%02x%02x%02x%02x>\n" - KERN_INFO "md: Name: \"%s\" CT:%llu\n", + printk(KERN_INFO + "md: SB: (V:%u) (F:0x%08x) Array-ID:<%02x%02x%02x%02x" + ":%02x%02x:%02x%02x:%02x%02x:%02x%02x%02x%02x%02x%02x>\n" + "md: Name: \"%s\" CT:%llu\n", le32_to_cpu(sb->major_version), le32_to_cpu(sb->feature_map), uuid[0], uuid[1], uuid[2], uuid[3], @@ -1770,12 +1771,13 @@ static void print_sb_1(struct mdp_superblock_1 *sb) & MD_SUPERBLOCK_1_TIME_SEC_MASK); uuid = sb->device_uuid; - printk(KERN_INFO "md: L%u SZ%llu RD:%u LO:%u CS:%u DO:%llu DS:%llu SO:%llu" + printk(KERN_INFO + "md: L%u SZ%llu RD:%u LO:%u CS:%u DO:%llu DS:%llu SO:%llu" " RO:%llu\n" - KERN_INFO "md: Dev:%08x UUID: %02x%02x%02x%02x:%02x%02x:%02x%02x:%02x%02x" - ":%02x%02x%02x%02x%02x%02x\n" - KERN_INFO "md: (F:0x%08x) UT:%llu Events:%llu ResyncOffset:%llu CSUM:0x%08x\n" - KERN_INFO "md: (MaxDev:%u) \n", + "md: Dev:%08x UUID: %02x%02x%02x%02x:%02x%02x:%02x%02x:%02x%02x" + ":%02x%02x%02x%02x%02x%02x\n" + "md: (F:0x%08x) UT:%llu Events:%llu ResyncOffset:%llu CSUM:0x%08x\n" + "md: (MaxDev:%u) \n", le32_to_cpu(sb->level), (unsigned long long)le64_to_cpu(sb->size), le32_to_cpu(sb->raid_disks), diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index efb4a6c2b57a..9a6307a347b2 100644 --- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -20,8 +20,14 @@ #include "tuner-simple.h" #include "stv0297.h" + +/* Can we use the specified front-end? Remember that if we are compiled + * into the kernel we can't call code that's in modules. */ +#define FE_SUPPORTED(fe) (defined(CONFIG_DVB_##fe) || \ + (defined(CONFIG_DVB_##fe##_MODULE) && defined(MODULE))) + /* lnb control */ -#if defined(CONFIG_DVB_MT312_MODULE) || defined(CONFIG_DVB_STV0299_MODULE) +#if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299) static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { struct flexcop_device *fc = fe->dvb->priv; @@ -49,8 +55,7 @@ static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage } #endif -#if defined(CONFIG_DVB_S5H1420_MODULE) || defined(CONFIG_DVB_STV0299_MODULE) \ - || defined(CONFIG_DVB_MT312_MODULE) +#if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312) static int flexcop_sleep(struct dvb_frontend* fe) { struct flexcop_device *fc = fe->dvb->priv; @@ -61,7 +66,7 @@ static int flexcop_sleep(struct dvb_frontend* fe) #endif /* SkyStar2 DVB-S rev 2.3 */ -#if defined(CONFIG_DVB_MT312_MODULE) +#if FE_SUPPORTED(MT312) static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) { /* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */ @@ -193,10 +198,12 @@ static int skystar2_rev23_attach(struct flexcop_device *fc, } return 0; } +#else +#define skystar2_rev23_attach NULL #endif /* SkyStar2 DVB-S rev 2.6 */ -#if defined(CONFIG_DVB_STV0299_MODULE) +#if FE_SUPPORTED(STV0299) static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) { @@ -321,10 +328,12 @@ static int skystar2_rev26_attach(struct flexcop_device *fc, } return 0; } +#else +#define skystar2_rev26_attach NULL #endif /* SkyStar2 DVB-S rev 2.7 */ -#if defined(CONFIG_DVB_S5H1420_MODULE) +#if FE_SUPPORTED(S5H1420) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_ITD1000) static struct s5h1420_config skystar2_rev2_7_s5h1420_config = { .demod_address = 0x53, .invert = 1, @@ -385,10 +394,12 @@ fail: fc->fc_i2c_adap[0].no_base_addr = 0; return 0; } +#else +#define skystar2_rev27_attach NULL #endif /* SkyStar2 rev 2.8 */ -#if defined(CONFIG_DVB_CX24123_MODULE) +#if FE_SUPPORTED(CX24123) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_CX24113) static struct cx24123_config skystar2_rev2_8_cx24123_config = { .demod_address = 0x55, .dont_use_pll = 1, @@ -433,10 +444,12 @@ static int skystar2_rev28_attach(struct flexcop_device *fc, * IR-receiver (PIC16F818) - but the card has no input for that ??? */ return 1; } +#else +#define skystar2_rev28_attach NULL #endif /* AirStar DVB-T */ -#if defined(CONFIG_DVB_MT352_MODULE) +#if FE_SUPPORTED(MT352) static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe) { static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d }; @@ -495,10 +508,12 @@ static int airstar_dvbt_attach(struct flexcop_device *fc, } return 0; } +#else +#define airstar_dvbt_attach NULL #endif /* AirStar ATSC 1st generation */ -#if defined(CONFIG_DVB_BCM3510_MODULE) +#if FE_SUPPORTED(BCM3510) static int flexcop_fe_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char* name) { @@ -517,10 +532,12 @@ static int airstar_atsc1_attach(struct flexcop_device *fc, fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, i2c); return fc->fe != NULL; } +#else +#define airstar_atsc1_attach NULL #endif /* AirStar ATSC 2nd generation */ -#if defined(CONFIG_DVB_NXT200X_MODULE) +#if FE_SUPPORTED(NXT200X) && FE_SUPPORTED(PLL) static struct nxt200x_config samsung_tbmv_config = { .demod_address = 0x0a, }; @@ -535,10 +552,12 @@ static int airstar_atsc2_attach(struct flexcop_device *fc, return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, DVB_PLL_SAMSUNG_TBMV); } +#else +#define airstar_atsc2_attach NULL #endif /* AirStar ATSC 3rd generation */ -#if defined(CONFIG_DVB_LGDT330X_MODULE) +#if FE_SUPPORTED(LGDT330X) static struct lgdt330x_config air2pc_atsc_hd5000_config = { .demod_address = 0x59, .demod_chip = LGDT3303, @@ -556,10 +575,12 @@ static int airstar_atsc3_attach(struct flexcop_device *fc, return !!dvb_attach(simple_tuner_attach, fc->fe, i2c, 0x61, TUNER_LG_TDVS_H06XF); } +#else +#define airstar_atsc3_attach NULL #endif /* CableStar2 DVB-C */ -#if defined(CONFIG_DVB_STV0297_MODULE) +#if FE_SUPPORTED(STV0297) static int alps_tdee4_stv0297_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) { @@ -698,39 +719,23 @@ static int cablestar2_attach(struct flexcop_device *fc, fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params; return 1; } +#else +#define cablestar2_attach NULL #endif static struct { flexcop_device_type_t type; int (*attach)(struct flexcop_device *, struct i2c_adapter *); } flexcop_frontends[] = { -#if defined(CONFIG_DVB_S5H1420_MODULE) { FC_SKY_REV27, skystar2_rev27_attach }, -#endif -#if defined(CONFIG_DVB_CX24123_MODULE) { FC_SKY_REV28, skystar2_rev28_attach }, -#endif -#if defined(CONFIG_DVB_STV0299_MODULE) { FC_SKY_REV26, skystar2_rev26_attach }, -#endif -#if defined(CONFIG_DVB_MT352_MODULE) { FC_AIR_DVBT, airstar_dvbt_attach }, -#endif -#if defined(CONFIG_DVB_NXT200X_MODULE) { FC_AIR_ATSC2, airstar_atsc2_attach }, -#endif -#if defined(CONFIG_DVB_LGDT330X_MODULE) { FC_AIR_ATSC3, airstar_atsc3_attach }, -#endif -#if defined(CONFIG_DVB_BCM3510_MODULE) { FC_AIR_ATSC1, airstar_atsc1_attach }, -#endif -#if defined(CONFIG_DVB_STV0297_MODULE) { FC_CABLE, cablestar2_attach }, -#endif -#if defined(CONFIG_DVB_MT312_MODULE) { FC_SKY_REV23, skystar2_rev23_attach }, -#endif }; /* try to figure out the frontend */ @@ -738,6 +743,8 @@ int flexcop_frontend_init(struct flexcop_device *fc) { int i; for (i = 0; i < ARRAY_SIZE(flexcop_frontends); i++) { + if (!flexcop_frontends[i].attach) + continue; /* type needs to be set before, because of some workarounds * done based on the probed card type */ fc->dev_type = flexcop_frontends[i].type; diff --git a/drivers/media/dvb/bt8xx/dst_ca.c b/drivers/media/dvb/bt8xx/dst_ca.c index 4601b059b2b2..0e246eaad05a 100644 --- a/drivers/media/dvb/bt8xx/dst_ca.c +++ b/drivers/media/dvb/bt8xx/dst_ca.c @@ -21,6 +21,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/smp_lock.h> #include <linux/string.h> #include <linux/dvb/ca.h> #include "dvbdev.h" diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h index 79927305e84d..487919bea7ae 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.h +++ b/drivers/media/dvb/dvb-core/dvbdev.h @@ -27,7 +27,6 @@ #include <linux/poll.h> #include <linux/fs.h> #include <linux/list.h> -#include <linux/smp_lock.h> #define DVB_MAJOR 212 diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c index 136c5863d81b..12e018b4107d 100644 --- a/drivers/media/dvb/frontends/af9013.c +++ b/drivers/media/dvb/frontends/af9013.c @@ -527,6 +527,10 @@ static int af9013_set_ofdm_params(struct af9013_state *state, u8 i, buf[3] = {0, 0, 0}; *auto_mode = 0; /* set if parameters are requested to auto set */ + /* Try auto-detect transmission parameters in case of AUTO requested or + garbage parameters given by application for compatibility. + MPlayer seems to provide garbage parameters currently. */ + switch (params->transmission_mode) { case TRANSMISSION_MODE_AUTO: *auto_mode = 1; @@ -536,7 +540,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[0] |= (1 << 0); break; default: - return -EINVAL; + deb_info("%s: invalid transmission_mode\n", __func__); + *auto_mode = 1; } switch (params->guard_interval) { @@ -554,7 +559,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[0] |= (3 << 2); break; default: - return -EINVAL; + deb_info("%s: invalid guard_interval\n", __func__); + *auto_mode = 1; } switch (params->hierarchy_information) { @@ -572,7 +578,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[0] |= (3 << 4); break; default: - return -EINVAL; + deb_info("%s: invalid hierarchy_information\n", __func__); + *auto_mode = 1; }; switch (params->constellation) { @@ -587,7 +594,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[1] |= (2 << 6); break; default: - return -EINVAL; + deb_info("%s: invalid constellation\n", __func__); + *auto_mode = 1; } /* Use HP. How and which case we can switch to LP? */ @@ -611,7 +619,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[2] |= (4 << 0); break; default: - return -EINVAL; + deb_info("%s: invalid code_rate_HP\n", __func__); + *auto_mode = 1; } switch (params->code_rate_LP) { @@ -638,7 +647,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, if (params->hierarchy_information == HIERARCHY_AUTO) break; default: - return -EINVAL; + deb_info("%s: invalid code_rate_LP\n", __func__); + *auto_mode = 1; } switch (params->bandwidth) { @@ -651,7 +661,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[1] |= (2 << 2); break; default: - return -EINVAL; + deb_info("%s: invalid bandwidth\n", __func__); + buf[1] |= (2 << 2); /* cannot auto-detect BW, try 8 MHz */ } /* program */ diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c index d1d959ed37b7..8d65c652ba50 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -36,7 +36,6 @@ #include <linux/fs.h> #include <linux/timer.h> #include <linux/poll.h> -#include <linux/smp_lock.h> #include <linux/kernel.h> #include <linux/sched.h> diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index 837467f93805..575bf9d89419 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -58,6 +58,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/input.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index 46d216329611..e85f318b4d2b 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/drivers/media/radio/radio-si470x.c @@ -127,6 +127,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/input.h> #include <linux/usb.h> #include <linux/hid.h> diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index fdb4adff3d28..ca6558c394be 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -3324,8 +3324,6 @@ void __devinit bttv_init_card1(struct bttv *btv) /* initialization part two -- after registering i2c bus */ void __devinit bttv_init_card2(struct bttv *btv) { - int addr=ADDR_UNSET; - btv->tuner_type = UNSET; if (BTTV_BOARD_UNKNOWN == btv->c.type) { @@ -3470,9 +3468,6 @@ void __devinit bttv_init_card2(struct bttv *btv) btv->pll.pll_current = -1; /* tuner configuration (from card list / autodetect / insmod option) */ - if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr) - addr = bttv_tvcards[btv->c.type].tuner_addr; - if (UNSET != bttv_tvcards[btv->c.type].tuner_type) if (UNSET == btv->tuner_type) btv->tuner_type = bttv_tvcards[btv->c.type].tuner_type; @@ -3496,40 +3491,6 @@ void __devinit bttv_init_card2(struct bttv *btv) if (UNSET == btv->tuner_type) btv->tuner_type = TUNER_ABSENT; - if (btv->tuner_type != TUNER_ABSENT) { - struct tuner_setup tun_setup; - - /* Load tuner module before issuing tuner config call! */ - if (bttv_tvcards[btv->c.type].has_radio) - v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_RADIO)); - v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); - v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD)); - - tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; - tun_setup.type = btv->tuner_type; - tun_setup.addr = addr; - - if (bttv_tvcards[btv->c.type].has_radio) - tun_setup.mode_mask |= T_RADIO; - - bttv_call_all(btv, tuner, s_type_addr, &tun_setup); - } - - if (btv->tda9887_conf) { - struct v4l2_priv_tun_config tda9887_cfg; - - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &btv->tda9887_conf; - - bttv_call_all(btv, tuner, s_config, &tda9887_cfg); - } - btv->dig = bttv_tvcards[btv->c.type].has_dig_in ? bttv_tvcards[btv->c.type].video_inputs - 1 : UNSET; btv->svhs = bttv_tvcards[btv->c.type].svhs == NO_SVHS ? @@ -3540,15 +3501,15 @@ void __devinit bttv_init_card2(struct bttv *btv) btv->has_remote = remote[btv->c.nr]; if (bttv_tvcards[btv->c.type].has_radio) - btv->has_radio=1; + btv->has_radio = 1; if (bttv_tvcards[btv->c.type].has_remote) - btv->has_remote=1; + btv->has_remote = 1; if (!bttv_tvcards[btv->c.type].no_gpioirq) - btv->gpioirq=1; + btv->gpioirq = 1; if (bttv_tvcards[btv->c.type].volume_gpio) - btv->volume_gpio=bttv_tvcards[btv->c.type].volume_gpio; + btv->volume_gpio = bttv_tvcards[btv->c.type].volume_gpio; if (bttv_tvcards[btv->c.type].audio_mode_gpio) - btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio; + btv->audio_mode_gpio = bttv_tvcards[btv->c.type].audio_mode_gpio; if (btv->tuner_type == TUNER_ABSENT) return; /* no tuner or related drivers to load */ @@ -3666,6 +3627,49 @@ no_audio: } +/* initialize the tuner */ +void __devinit bttv_init_tuner(struct bttv *btv) +{ + int addr = ADDR_UNSET; + + if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr) + addr = bttv_tvcards[btv->c.type].tuner_addr; + + if (btv->tuner_type != TUNER_ABSENT) { + struct tuner_setup tun_setup; + + /* Load tuner module before issuing tuner config call! */ + if (bttv_tvcards[btv->c.type].has_radio) + v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_RADIO)); + v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD)); + + tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; + tun_setup.type = btv->tuner_type; + tun_setup.addr = addr; + + if (bttv_tvcards[btv->c.type].has_radio) + tun_setup.mode_mask |= T_RADIO; + + bttv_call_all(btv, tuner, s_type_addr, &tun_setup); + } + + if (btv->tda9887_conf) { + struct v4l2_priv_tun_config tda9887_cfg; + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &btv->tda9887_conf; + + bttv_call_all(btv, tuner, s_config, &tda9887_cfg); + } +} + /* ----------------------------------------------------------------------- */ static void modtec_eeprom(struct bttv *btv) diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 5eb1464af670..8cc6dd28d6a7 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -41,6 +41,7 @@ #include <linux/fs.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/smp_lock.h> #include <linux/interrupt.h> #include <linux/kdev_t.h> #include "bttvp.h" @@ -4418,6 +4419,7 @@ static int __devinit bttv_probe(struct pci_dev *dev, /* some card-specific stuff (needs working i2c) */ bttv_init_card2(btv); + bttv_init_tuner(btv); init_irqreg(btv); /* register video4linux + input */ diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index 3d36daf206f3..3ec2402c6b4a 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -283,6 +283,7 @@ extern struct tvcard bttv_tvcards[]; extern void bttv_idcard(struct bttv *btv); extern void bttv_init_card1(struct bttv *btv); extern void bttv_init_card2(struct bttv *btv); +extern void bttv_init_tuner(struct bttv *btv); /* card-specific funtions */ extern void tea5757_set_freq(struct bttv *btv, unsigned short freq); diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index 2943bfd32a94..e0cf21e0b1bf 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -31,6 +31,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/firmware.h> +#include <linux/smp_lock.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <media/cx2341x.h> @@ -57,7 +58,8 @@ MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages"); #define dprintk(level, fmt, arg...)\ do { if (v4l_debug >= level) \ - printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg);\ + printk(KERN_DEBUG "%s: " fmt, \ + (dev) ? dev->name : "cx23885[?]", ## arg); \ } while (0) static struct cx23885_tvnorm cx23885_tvnorms[] = { @@ -1676,6 +1678,7 @@ static struct v4l2_file_operations mpeg_fops = { .read = mpeg_read, .poll = mpeg_poll, .mmap = mpeg_mmap, + .ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 70836af3ab48..5d6093336300 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -26,6 +26,7 @@ #include <linux/kmod.h> #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/kthread.h> diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 44eacfb0d0d6..356d6896da3f 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -32,6 +32,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/firmware.h> +#include <linux/smp_lock.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <media/cx2341x.h> diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index b12770848c00..2bb54c3ef5cd 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -31,6 +31,7 @@ #include <linux/kmod.h> #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/interrupt.h> #include <linux/dma-mapping.h> #include <linux/delay.h> diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c index ec2f45dde164..0664d111085f 100644 --- a/drivers/media/video/dabusb.c +++ b/drivers/media/video/dabusb.c @@ -32,6 +32,7 @@ #include <linux/list.h> #include <linux/vmalloc.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/init.h> #include <asm/uaccess.h> #include <asm/atomic.h> diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index ebd24a25fb85..320f1f60276e 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -58,8 +58,6 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); -#define MT9V011_VERSION 0x8243 - /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */ static unsigned long em28xx_devused; @@ -159,6 +157,20 @@ static struct em28xx_reg_seq evga_indtube_digital[] = { { -1, -1, -1, -1}, }; +/* Pinnacle Hybrid Pro eb1a:2881 */ +static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = { + {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = { + {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x04, 0xff, 100},/* zl10353 reset */ + {EM2880_R04_GPO, 0x0c, 0xff, 1}, + { -1, -1, -1, -1}, +}; + + /* Callback for the most boards */ static struct em28xx_reg_seq default_tuner_gpio[] = { {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, @@ -205,13 +217,15 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = { */ struct em28xx_board em28xx_boards[] = { [EM2750_BOARD_UNKNOWN] = { - .name = "Unknown EM2750/EM2751 webcam grabber", + .name = "EM2710/EM2750/EM2751 webcam grabber", .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, - .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, .amux = EM28XX_AMUX_VIDEO, + .gpio = silvercrest_reg_seq, } }, }, [EM2800_BOARD_UNKNOWN] = { @@ -233,13 +247,15 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_UNKNOWN] = { .name = "Unknown EM2750/28xx video grabber", .tuner_type = TUNER_ABSENT, + .is_webcam = 1, /* To enable sensor probe */ }, [EM2750_BOARD_DLCW_130] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ .name = "Huaqi DLCW-130", .valid = EM28XX_BOARD_NOT_VALIDATED, .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, - .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -440,7 +456,8 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = { .name = "Videology 20K14XUSB USB2.0", .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -450,8 +467,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_SILVERCREST_WEBCAM] = { .name = "Silvercrest Webcam 1.3mpix", .tuner_type = TUNER_ABSENT, - .is_27xx = 1, - .decoder = EM28XX_MT9V011, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -500,7 +516,8 @@ struct em28xx_board em28xx_boards[] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ .name = "NetGMBH Cam", .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -1250,25 +1267,26 @@ struct em28xx_board em28xx_boards[] = { }, [EM2881_BOARD_PINNACLE_HYBRID_PRO] = { .name = "Pinnacle Hybrid Pro", - .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, + .has_dvb = 1, + .dvb_gpio = pinnacle_hybrid_pro_digital, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, - .gpio = default_analog, + .gpio = pinnacle_hybrid_pro_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, + .gpio = pinnacle_hybrid_pro_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, + .gpio = pinnacle_hybrid_pro_analog, } }, }, [EM2882_BOARD_PINNACLE_HYBRID_PRO] = { @@ -1638,6 +1656,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, + {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028}, }; /* I2C devicelist hash table for devices with generic USB IDs */ @@ -1704,6 +1723,32 @@ static inline void em28xx_set_model(struct em28xx *dev) EM28XX_I2C_FREQ_100_KHZ; } +/* FIXME: Should be replaced by a proper mt9m001 driver */ +static int em28xx_initialize_mt9m001(struct em28xx *dev) +{ + int i; + unsigned char regs[][3] = { + { 0x0d, 0x00, 0x01, }, + { 0x0d, 0x00, 0x00, }, + { 0x04, 0x05, 0x00, }, /* hres = 1280 */ + { 0x03, 0x04, 0x00, }, /* vres = 1024 */ + { 0x20, 0x11, 0x00, }, + { 0x06, 0x00, 0x10, }, + { 0x2b, 0x00, 0x24, }, + { 0x2e, 0x00, 0x24, }, + { 0x35, 0x00, 0x24, }, + { 0x2d, 0x00, 0x20, }, + { 0x2c, 0x00, 0x20, }, + { 0x09, 0x0a, 0xd4, }, + { 0x35, 0x00, 0x57, }, + }; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client, ®s[i][0], 3); + + return 0; +} + /* HINT method: webcam I2C chips * * This method work for webcams with Micron sensors @@ -1716,9 +1761,6 @@ static int em28xx_hint_sensor(struct em28xx *dev) __be16 version_be; u16 version; - if (dev->model != EM2820_BOARD_UNKNOWN) - return 0; - dev->i2c_client.addr = 0xba >> 1; cmd = 0; i2c_master_send(&dev->i2c_client, &cmd, 1); @@ -1729,16 +1771,38 @@ static int em28xx_hint_sensor(struct em28xx *dev) version = be16_to_cpu(version_be); switch (version) { - case MT9V011_VERSION: + case 0x8243: /* mt9v011 640x480 1.3 Mpix sensor */ dev->model = EM2820_BOARD_SILVERCREST_WEBCAM; sensor_name = "mt9v011"; + dev->em28xx_sensor = EM28XX_MT9V011; + dev->sensor_xres = 640; + dev->sensor_yres = 480; + dev->sensor_xtal = 6300000; + + /* probably means GRGB 16 bit bayer */ + dev->vinmode = 0x0d; + dev->vinctl = 0x00; + + break; + case 0x8431: + dev->model = EM2750_BOARD_UNKNOWN; + sensor_name = "mt9m001"; + dev->em28xx_sensor = EM28XX_MT9M001; + em28xx_initialize_mt9m001(dev); + dev->sensor_xres = 1280; + dev->sensor_yres = 1024; + + /* probably means BGGR 16 bit bayer */ + dev->vinmode = 0x0c; + dev->vinctl = 0x00; + break; default: - printk("Unknown Sensor 0x%04x\n", be16_to_cpu(version)); + printk("Unknown Micron Sensor 0x%04x\n", be16_to_cpu(version)); return -EINVAL; } - em28xx_errdev("Sensor is %s, assuming that webcam is %s\n", + em28xx_errdev("Sensor is %s, using model %s entry.\n", sensor_name, em28xx_boards[dev->model].name); return 0; @@ -1772,10 +1836,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_info("chip ID is em2750\n"); break; case CHIP_ID_EM2820: - if (dev->board.is_27xx) - em28xx_info("chip is em2710\n"); - else - em28xx_info("chip ID is em2820\n"); + em28xx_info("chip ID is em2710 or em2820\n"); break; case CHIP_ID_EM2840: em28xx_info("chip ID is em2840\n"); @@ -1929,6 +1990,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) ctl->demod = XC3028_FE_ZARLINK456; break; case EM2880_BOARD_TERRATEC_HYBRID_XS: + case EM2881_BOARD_PINNACLE_HYBRID_PRO: ctl->demod = XC3028_FE_ZARLINK456; break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: @@ -2225,6 +2287,7 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, so make the call now so the analog GPIOs are set properly before probing the i2c bus. */ + em28xx_gpio_set(dev, dev->board.tuner_gpio); em28xx_set_mode(dev, EM28XX_ANALOG_MODE); break; case EM2820_BOARD_SILVERCREST_WEBCAM: @@ -2262,9 +2325,14 @@ void em28xx_card_setup(struct em28xx *dev) v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, "tvp5150", "tvp5150", tvp5150_addrs); - if (dev->board.decoder == EM28XX_MT9V011) - v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "mt9v011", "mt9v011", mt9v011_addrs); + if (dev->em28xx_sensor == EM28XX_MT9V011) { + struct v4l2_subdev *sd; + + sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "mt9v011", "mt9v011", mt9v011_addrs); + v4l2_subdev_call(sd, core, s_config, 0, &dev->sensor_xtal); + } + if (dev->board.adecoder == EM28XX_TVAUDIO) v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, @@ -2410,7 +2478,19 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, return errCode; } - em28xx_hint_sensor(dev); + /* + * Default format, used for tvp5150 or saa711x output formats + */ + dev->vinmode = 0x10; + dev->vinctl = 0x11; + + /* + * If the device can be a webcam, seek for a sensor. + * If sensor is not found, then it isn't a webcam. + */ + if (dev->board.is_webcam) + if (em28xx_hint_sensor(dev) < 0) + dev->board.is_webcam = 0; /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 079ab4d563a6..5b78e199abd1 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -648,28 +648,17 @@ int em28xx_capture_start(struct em28xx *dev, int start) int em28xx_set_outfmt(struct em28xx *dev) { int ret; - int vinmode, vinctl, outfmt; - - outfmt = dev->format->reg; - - if (dev->board.is_27xx) { - vinmode = 0x0d; - vinctl = 0x00; - } else { - vinmode = 0x10; - vinctl = 0x11; - } ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT, - outfmt | 0x20, 0xff); + dev->format->reg | 0x20, 0xff); if (ret < 0) return ret; - ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, vinmode); + ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode); if (ret < 0) return ret; - return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctl); + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, dev->vinctl); } static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, @@ -707,10 +696,7 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) u8 mode; /* the em2800 scaler only supports scaling down to 50% */ - if (dev->board.is_27xx) { - /* FIXME: Don't use the scaler yet */ - mode = 0; - } else if (dev->board.is_em2800) { + if (dev->board.is_em2800) { mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); } else { u8 buf[2]; diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 3da97c32b8fa..cf0ac7f2a30d 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -31,6 +31,8 @@ #include "lgdt330x.h" #include "zl10353.h" #include "s5h1409.h" +#include "mt352.h" +#include "mt352_priv.h" /* FIXME */ MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); @@ -243,7 +245,7 @@ static struct s5h1409_config em28xx_s5h1409_with_xc3028 = { .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK }; -static struct zl10353_config em28xx_terratec_xs_zl10353_xc3028 = { +static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = { .demod_address = (0x1e >> 1), .no_tuner = 1, .disable_i2c_gate_ctrl = 1, @@ -258,6 +260,41 @@ static struct drx397xD_config em28xx_drx397xD_with_xc3028 = { }; #endif +static int mt352_terratec_xs_init(struct dvb_frontend *fe) +{ + /* Values extracted from a USB trace of the Terratec Windows driver */ + static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x2c }; + static u8 reset[] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0xa0 }; + static u8 input_freq_cfg[] = { INPUT_FREQ_1, 0x31, 0xb8 }; + static u8 rs_err_cfg[] = { RS_ERR_PER_1, 0x00, 0x4d }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; + static u8 trl_nom_cfg[] = { TRL_NOMINAL_RATE_1, 0x64, 0x00 }; + static u8 tps_given_cfg[] = { TPS_GIVEN_1, 0x40, 0x80, 0x50 }; + static u8 tuner_go[] = { TUNER_GO, 0x01}; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, input_freq_cfg, sizeof(input_freq_cfg)); + mt352_write(fe, rs_err_cfg, sizeof(rs_err_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + mt352_write(fe, trl_nom_cfg, sizeof(trl_nom_cfg)); + mt352_write(fe, tps_given_cfg, sizeof(tps_given_cfg)); + mt352_write(fe, tuner_go, sizeof(tuner_go)); + return 0; +} + +static struct mt352_config terratec_xs_mt352_cfg = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .if2 = 45600, + .demod_init = mt352_terratec_xs_init, +}; + /* ------------------------------------------------------------------ */ static int attach_xc3028(u8 addr, struct em28xx *dev) @@ -440,7 +477,6 @@ static int dvb_init(struct em28xx *dev) goto out_free; } break; - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: case EM2880_BOARD_KWORLD_DVB_310U: case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->frontend = dvb_attach(zl10353_attach, @@ -451,20 +487,28 @@ static int dvb_init(struct em28xx *dev) goto out_free; } break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + dvb->frontend = dvb_attach(zl10353_attach, + &em28xx_zl10353_xc3028_no_i2c_gate, + &dev->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; case EM2880_BOARD_TERRATEC_HYBRID_XS: + case EM2881_BOARD_PINNACLE_HYBRID_PRO: dvb->frontend = dvb_attach(zl10353_attach, - &em28xx_terratec_xs_zl10353_xc3028, + &em28xx_zl10353_xc3028_no_i2c_gate, &dev->i2c_adap); if (dvb->frontend == NULL) { /* This board could have either a zl10353 or a mt352. If the chip id isn't for zl10353, try mt352 */ - - /* FIXME: make support for mt352 work */ - printk(KERN_ERR "version of this board with mt352 not " - "currently supported\n"); - result = -EINVAL; - goto out_free; + dvb->frontend = dvb_attach(mt352_attach, + &terratec_xs_mt352_cfg, + &dev->i2c_adap); } + if (attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 14316c912179..ff37b4c15f44 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -657,8 +657,8 @@ static void get_scale(struct em28xx *dev, unsigned int width, unsigned int height, unsigned int *hscale, unsigned int *vscale) { - unsigned int maxw = norm_maxw(dev); - unsigned int maxh = norm_maxh(dev); + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); *hscale = (((unsigned long)maxw) << 12) / width - 4096L; if (*hscale >= 0x4000) @@ -726,11 +726,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } - if (dev->board.is_27xx) { - /* FIXME: This is the only supported fmt */ - width = 640; - height = 480; - } else if (dev->board.is_em2800) { + if (dev->board.is_em2800) { /* the em2800 can only scale down to 50% */ height = height > (3 * maxh / 4) ? maxh : maxh / 2; width = width > (3 * maxw / 4) ? maxw : maxw / 2; @@ -767,12 +763,6 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, { struct em28xx_fmt *fmt; - /* FIXME: This is the only supported fmt */ - if (dev->board.is_27xx) { - width = 640; - height = 480; - } - fmt = format_by_fourcc(fourcc); if (!fmt) return -EINVAL; diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index d90fef463764..45bd513f62dc 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -358,10 +358,15 @@ struct em28xx_input { #define INPUT(nr) (&em28xx_boards[dev->model].input[nr]) enum em28xx_decoder { - EM28XX_NODECODER, + EM28XX_NODECODER = 0, EM28XX_TVP5150, EM28XX_SAA711X, +}; + +enum em28xx_sensor { + EM28XX_NOSENSOR = 0, EM28XX_MT9V011, + EM28XX_MT9M001, }; enum em28xx_adecoder { @@ -390,7 +395,7 @@ struct em28xx_board { unsigned int max_range_640_480:1; unsigned int has_dvb:1; unsigned int has_snapshot_button:1; - unsigned int is_27xx:1; + unsigned int is_webcam:1; unsigned int valid:1; unsigned char xclk, i2c_speed; @@ -474,6 +479,14 @@ struct em28xx { struct v4l2_device v4l2_dev; struct em28xx_board board; + /* Webcam specific fields */ + enum em28xx_sensor em28xx_sensor; + int sensor_xres, sensor_yres; + int sensor_xtal; + + /* Vinmode/Vinctl used at the driver */ + int vinmode, vinctl; + unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; unsigned int has_alsa_audio:1; @@ -754,17 +767,23 @@ static inline int em28xx_gamma_set(struct em28xx *dev, s32 val) /*FIXME: maxw should be dependent of alt mode */ static inline unsigned int norm_maxw(struct em28xx *dev) { + if (dev->board.is_webcam) + return dev->sensor_xres; + if (dev->board.max_range_640_480) return 640; - else - return 720; + + return 720; } static inline unsigned int norm_maxh(struct em28xx *dev) { + if (dev->board.is_webcam) + return dev->sensor_yres; + if (dev->board.max_range_640_480) return 480; - else - return (dev->norm & V4L2_STD_625_50) ? 576 : 480; + + return (dev->norm & V4L2_STD_625_50) ? 576 : 480; } #endif diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 578dc4ffc965..34f46f2bc040 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -102,6 +102,22 @@ config USB_GSPCA_PAC7311 To compile this driver as a module, choose M here: the module will be called gspca_pac7311. +config USB_GSPCA_SN9C20X + tristate "SN9C20X USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the + sn9c20x chips (SN9C201 and SN9C202). + + To compile this driver as a module, choose M here: the + module will be called gspca_sn9c20x. + +config USB_GSPCA_SN9C20X_EVDEV + bool "Enable evdev support" + depends on USB_GSPCA_SN9C20X + ---help--- + Say Y here in order to enable evdev support for sn9c20x webcam button. + config USB_GSPCA_SONIXB tristate "SONIX Bayer USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 8a6643e8eb96..f6d3b86e9ad5 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o +obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o @@ -35,6 +36,7 @@ gspca_ov519-objs := ov519.o gspca_ov534-objs := ov534.o gspca_pac207-objs := pac207.o gspca_pac7311-objs := pac7311.o +gspca_sn9c20x-objs := sn9c20x.o gspca_sonixb-objs := sonixb.o gspca_sonixj-objs := sonixj.o gspca_spca500-objs := spca500.o diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index 219cfa6fb877..8d48ea1742c2 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -846,6 +846,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 1e89600986c8..b8561dfb6c8c 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -727,6 +727,74 @@ static int gspca_get_mode(struct gspca_dev *gspca_dev, return -EINVAL; } +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + int ret; + struct gspca_dev *gspca_dev = priv; + + if (!gspca_dev->sd_desc->get_chip_ident) + return -EINVAL; + + if (!gspca_dev->sd_desc->get_register) + return -EINVAL; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->present) + ret = gspca_dev->sd_desc->get_register(gspca_dev, reg); + else + ret = -ENODEV; + mutex_unlock(&gspca_dev->usb_lock); + + return ret; +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + int ret; + struct gspca_dev *gspca_dev = priv; + + if (!gspca_dev->sd_desc->get_chip_ident) + return -EINVAL; + + if (!gspca_dev->sd_desc->set_register) + return -EINVAL; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->present) + ret = gspca_dev->sd_desc->set_register(gspca_dev, reg); + else + ret = -ENODEV; + mutex_unlock(&gspca_dev->usb_lock); + + return ret; +} +#endif + +static int vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + int ret; + struct gspca_dev *gspca_dev = priv; + + if (!gspca_dev->sd_desc->get_chip_ident) + return -EINVAL; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->present) + ret = gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip); + else + ret = -ENODEV; + mutex_unlock(&gspca_dev->usb_lock); + + return ret; +} + static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fmtdesc) { @@ -1883,6 +1951,11 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_s_parm = vidioc_s_parm, .vidioc_s_std = vidioc_s_std, .vidioc_enum_framesizes = vidioc_enum_framesizes, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif + .vidioc_g_chip_ident = vidioc_g_chip_ident, #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, #endif diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index bd1faff88644..46c4effdfcd5 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -69,6 +69,10 @@ typedef void (*cam_v_op) (struct gspca_dev *); typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *); typedef int (*cam_jpg_op) (struct gspca_dev *, struct v4l2_jpegcompression *); +typedef int (*cam_reg_op) (struct gspca_dev *, + struct v4l2_dbg_register *); +typedef int (*cam_ident_op) (struct gspca_dev *, + struct v4l2_dbg_chip_ident *); typedef int (*cam_streamparm_op) (struct gspca_dev *, struct v4l2_streamparm *); typedef int (*cam_qmnu_op) (struct gspca_dev *, @@ -105,6 +109,11 @@ struct sd_desc { cam_qmnu_op querymenu; cam_streamparm_op get_streamparm; cam_streamparm_op set_streamparm; +#ifdef CONFIG_VIDEO_ADV_DEBUG + cam_reg_op set_register; + cam_reg_op get_register; +#endif + cam_ident_op get_chip_ident; }; /* packet types when moving from iso buf to frame buf */ diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c index 191bcd718979..0163903d1c0f 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -476,9 +476,6 @@ static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) return err; - err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); - if (err < 0) - return err; err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) @@ -524,9 +521,6 @@ static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val) err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) return err; - err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); - if (err < 0) - return err; err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index 75e8d14e4ac7..de769caf013d 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -201,6 +201,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c new file mode 100644 index 000000000000..fcfbbd329b4c --- /dev/null +++ b/drivers/media/video/gspca/sn9c20x.c @@ -0,0 +1,2434 @@ +/* + * Sonix sn9c201 sn9c202 library + * Copyright (C) 2008-2009 microdia project <microdia@googlegroups.com> + * Copyright (C) 2009 Brian Johnson <brijohn@gmail.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 + * 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 + */ + +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/usb/input.h> +#include <linux/input.h> +#endif + +#include "gspca.h" +#include "jpeg.h" + +#include <media/v4l2-chip-ident.h> + +MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, " + "microdia project <microdia@googlegroups.com>"); +MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver"); +MODULE_LICENSE("GPL"); + +#define MODULE_NAME "sn9c20x" + +#define MODE_RAW 0x10 +#define MODE_JPEG 0x20 +#define MODE_SXGA 0x80 + +#define SENSOR_OV9650 0 +#define SENSOR_OV9655 1 +#define SENSOR_SOI968 2 +#define SENSOR_OV7660 3 +#define SENSOR_OV7670 4 +#define SENSOR_MT9V011 5 +#define SENSOR_MT9V111 6 +#define SENSOR_MT9V112 7 +#define SENSOR_MT9M001 8 +#define SENSOR_MT9M111 9 +#define SENSOR_HV7131R 10 +#define SENSOR_MT9VPRB 20 + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; + +#define MIN_AVG_LUM 80 +#define MAX_AVG_LUM 130 + atomic_t avg_lum; + u8 old_step; + u8 older_step; + u8 exposure_step; + + u8 brightness; + u8 contrast; + u8 saturation; + s16 hue; + u8 gamma; + u8 red; + u8 blue; + + u8 hflip; + u8 vflip; + u8 gain; + u16 exposure; + u8 auto_exposure; + + u8 i2c_addr; + u8 sensor; + u8 hstart; + u8 vstart; + + u8 *jpeg_hdr; + u8 quality; + +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV + struct input_dev *input_dev; + u8 input_gpio; + struct task_struct *input_task; +#endif +}; + +static int sd_setbrightness(struct gspca_dev *gspca_dev, s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val); +static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val); +static int sd_sethue(struct gspca_dev *gspca_dev, s32 val); +static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val); +static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val); +static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val); +static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val); +static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val); + +static struct ctrl sd_ctrls[] = { + { +#define BRIGHTNESS_IDX 0 + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xff, + .step = 1, +#define BRIGHTNESS_DEFAULT 0x7f + .default_value = BRIGHTNESS_DEFAULT, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { +#define CONTRAST_IDX 1 + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xff, + .step = 1, +#define CONTRAST_DEFAULT 0x7f + .default_value = CONTRAST_DEFAULT, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { +#define SATURATION_IDX 2 + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 0xff, + .step = 1, +#define SATURATION_DEFAULT 0x7f + .default_value = SATURATION_DEFAULT, + }, + .set = sd_setsaturation, + .get = sd_getsaturation, + }, + { +#define HUE_IDX 3 + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -180, + .maximum = 180, + .step = 1, +#define HUE_DEFAULT 0 + .default_value = HUE_DEFAULT, + }, + .set = sd_sethue, + .get = sd_gethue, + }, + { +#define GAMMA_IDX 4 + { + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 0, + .maximum = 0xff, + .step = 1, +#define GAMMA_DEFAULT 0x10 + .default_value = GAMMA_DEFAULT, + }, + .set = sd_setgamma, + .get = sd_getgamma, + }, + { +#define BLUE_IDX 5 + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0, + .maximum = 0x7f, + .step = 1, +#define BLUE_DEFAULT 0x28 + .default_value = BLUE_DEFAULT, + }, + .set = sd_setbluebalance, + .get = sd_getbluebalance, + }, + { +#define RED_IDX 6 + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0, + .maximum = 0x7f, + .step = 1, +#define RED_DEFAULT 0x28 + .default_value = RED_DEFAULT, + }, + .set = sd_setredbalance, + .get = sd_getredbalance, + }, + { +#define HFLIP_IDX 7 + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal Flip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEFAULT 0 + .default_value = HFLIP_DEFAULT, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, + { +#define VFLIP_IDX 8 + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Flip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEFAULT 0 + .default_value = VFLIP_DEFAULT, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, + { +#define EXPOSURE_IDX 9 + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 0x1780, + .step = 1, +#define EXPOSURE_DEFAULT 0x33 + .default_value = EXPOSURE_DEFAULT, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { +#define GAIN_IDX 10 + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 28, + .step = 1, +#define GAIN_DEFAULT 0x00 + .default_value = GAIN_DEFAULT, + }, + .set = sd_setgain, + .get = sd_getgain, + }, + { +#define AUTOGAIN_IDX 11 + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTO_EXPOSURE_DEFAULT 1 + .default_value = AUTO_EXPOSURE_DEFAULT, + }, + .set = sd_setautoexposure, + .get = sd_getautoexposure, + }, +}; + +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 240, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0 | MODE_JPEG}, + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 | MODE_RAW}, + {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 240, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 480, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1 | MODE_JPEG}, + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_RAW}, + {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 480, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 960, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2 | MODE_JPEG}, + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 | MODE_RAW}, + {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 960, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, +}; + +static const struct v4l2_pix_format sxga_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 240, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0 | MODE_JPEG}, + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 | MODE_RAW}, + {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 240, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 480, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1 | MODE_JPEG}, + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_RAW}, + {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 480, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 960, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2 | MODE_JPEG}, + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 | MODE_RAW}, + {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 960, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = (1280 * 1024) + 64, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3 | MODE_RAW | MODE_SXGA}, +}; + +static const int hsv_red_x[] = { + 41, 44, 46, 48, 50, 52, 54, 56, + 58, 60, 62, 64, 66, 68, 70, 72, + 74, 76, 78, 80, 81, 83, 85, 87, + 88, 90, 92, 93, 95, 97, 98, 100, + 101, 102, 104, 105, 107, 108, 109, 110, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 123, 124, 125, 125, + 126, 127, 127, 128, 128, 129, 129, 129, + 130, 130, 130, 130, 131, 131, 131, 131, + 131, 131, 131, 131, 130, 130, 130, 130, + 129, 129, 129, 128, 128, 127, 127, 126, + 125, 125, 124, 123, 122, 122, 121, 120, + 119, 118, 117, 116, 115, 114, 112, 111, + 110, 109, 107, 106, 105, 103, 102, 101, + 99, 98, 96, 94, 93, 91, 90, 88, + 86, 84, 83, 81, 79, 77, 75, 74, + 72, 70, 68, 66, 64, 62, 60, 58, + 56, 54, 52, 49, 47, 45, 43, 41, + 39, 36, 34, 32, 30, 28, 25, 23, + 21, 19, 16, 14, 12, 9, 7, 5, + 3, 0, -1, -3, -6, -8, -10, -12, + -15, -17, -19, -22, -24, -26, -28, -30, + -33, -35, -37, -39, -41, -44, -46, -48, + -50, -52, -54, -56, -58, -60, -62, -64, + -66, -68, -70, -72, -74, -76, -78, -80, + -81, -83, -85, -87, -88, -90, -92, -93, + -95, -97, -98, -100, -101, -102, -104, -105, + -107, -108, -109, -110, -112, -113, -114, -115, + -116, -117, -118, -119, -120, -121, -122, -123, + -123, -124, -125, -125, -126, -127, -127, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -127, -127, -126, -125, -125, -124, -123, + -122, -122, -121, -120, -119, -118, -117, -116, + -115, -114, -112, -111, -110, -109, -107, -106, + -105, -103, -102, -101, -99, -98, -96, -94, + -93, -91, -90, -88, -86, -84, -83, -81, + -79, -77, -75, -74, -72, -70, -68, -66, + -64, -62, -60, -58, -56, -54, -52, -49, + -47, -45, -43, -41, -39, -36, -34, -32, + -30, -28, -25, -23, -21, -19, -16, -14, + -12, -9, -7, -5, -3, 0, 1, 3, + 6, 8, 10, 12, 15, 17, 19, 22, + 24, 26, 28, 30, 33, 35, 37, 39, 41 +}; + +static const int hsv_red_y[] = { + 82, 80, 78, 76, 74, 73, 71, 69, + 67, 65, 63, 61, 58, 56, 54, 52, + 50, 48, 46, 44, 41, 39, 37, 35, + 32, 30, 28, 26, 23, 21, 19, 16, + 14, 12, 10, 7, 5, 3, 0, -1, + -3, -6, -8, -10, -13, -15, -17, -19, + -22, -24, -26, -29, -31, -33, -35, -38, + -40, -42, -44, -46, -48, -51, -53, -55, + -57, -59, -61, -63, -65, -67, -69, -71, + -73, -75, -77, -79, -81, -82, -84, -86, + -88, -89, -91, -93, -94, -96, -98, -99, + -101, -102, -104, -105, -106, -108, -109, -110, + -112, -113, -114, -115, -116, -117, -119, -120, + -120, -121, -122, -123, -124, -125, -126, -126, + -127, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -127, -127, -126, -125, -125, -124, -123, -122, + -121, -120, -119, -118, -117, -116, -115, -114, + -113, -111, -110, -109, -107, -106, -105, -103, + -102, -100, -99, -97, -96, -94, -92, -91, + -89, -87, -85, -84, -82, -80, -78, -76, + -74, -73, -71, -69, -67, -65, -63, -61, + -58, -56, -54, -52, -50, -48, -46, -44, + -41, -39, -37, -35, -32, -30, -28, -26, + -23, -21, -19, -16, -14, -12, -10, -7, + -5, -3, 0, 1, 3, 6, 8, 10, + 13, 15, 17, 19, 22, 24, 26, 29, + 31, 33, 35, 38, 40, 42, 44, 46, + 48, 51, 53, 55, 57, 59, 61, 63, + 65, 67, 69, 71, 73, 75, 77, 79, + 81, 82, 84, 86, 88, 89, 91, 93, + 94, 96, 98, 99, 101, 102, 104, 105, + 106, 108, 109, 110, 112, 113, 114, 115, + 116, 117, 119, 120, 120, 121, 122, 123, + 124, 125, 126, 126, 127, 128, 128, 129, + 129, 130, 130, 131, 131, 131, 131, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 131, 131, 131, 130, 130, + 130, 129, 129, 128, 127, 127, 126, 125, + 125, 124, 123, 122, 121, 120, 119, 118, + 117, 116, 115, 114, 113, 111, 110, 109, + 107, 106, 105, 103, 102, 100, 99, 97, + 96, 94, 92, 91, 89, 87, 85, 84, 82 +}; + +static const int hsv_green_x[] = { + -124, -124, -125, -125, -125, -125, -125, -125, + -125, -126, -126, -125, -125, -125, -125, -125, + -125, -124, -124, -124, -123, -123, -122, -122, + -121, -121, -120, -120, -119, -118, -117, -117, + -116, -115, -114, -113, -112, -111, -110, -109, + -108, -107, -105, -104, -103, -102, -100, -99, + -98, -96, -95, -93, -92, -91, -89, -87, + -86, -84, -83, -81, -79, -77, -76, -74, + -72, -70, -69, -67, -65, -63, -61, -59, + -57, -55, -53, -51, -49, -47, -45, -43, + -41, -39, -37, -35, -33, -30, -28, -26, + -24, -22, -20, -18, -15, -13, -11, -9, + -7, -4, -2, 0, 1, 3, 6, 8, + 10, 12, 14, 17, 19, 21, 23, 25, + 27, 29, 32, 34, 36, 38, 40, 42, + 44, 46, 48, 50, 52, 54, 56, 58, + 60, 62, 64, 66, 68, 70, 71, 73, + 75, 77, 78, 80, 82, 83, 85, 87, + 88, 90, 91, 93, 94, 96, 97, 98, + 100, 101, 102, 104, 105, 106, 107, 108, + 109, 111, 112, 113, 113, 114, 115, 116, + 117, 118, 118, 119, 120, 120, 121, 122, + 122, 123, 123, 124, 124, 124, 125, 125, + 125, 125, 125, 125, 125, 126, 126, 125, + 125, 125, 125, 125, 125, 124, 124, 124, + 123, 123, 122, 122, 121, 121, 120, 120, + 119, 118, 117, 117, 116, 115, 114, 113, + 112, 111, 110, 109, 108, 107, 105, 104, + 103, 102, 100, 99, 98, 96, 95, 93, + 92, 91, 89, 87, 86, 84, 83, 81, + 79, 77, 76, 74, 72, 70, 69, 67, + 65, 63, 61, 59, 57, 55, 53, 51, + 49, 47, 45, 43, 41, 39, 37, 35, + 33, 30, 28, 26, 24, 22, 20, 18, + 15, 13, 11, 9, 7, 4, 2, 0, + -1, -3, -6, -8, -10, -12, -14, -17, + -19, -21, -23, -25, -27, -29, -32, -34, + -36, -38, -40, -42, -44, -46, -48, -50, + -52, -54, -56, -58, -60, -62, -64, -66, + -68, -70, -71, -73, -75, -77, -78, -80, + -82, -83, -85, -87, -88, -90, -91, -93, + -94, -96, -97, -98, -100, -101, -102, -104, + -105, -106, -107, -108, -109, -111, -112, -113, + -113, -114, -115, -116, -117, -118, -118, -119, + -120, -120, -121, -122, -122, -123, -123, -124, -124 +}; + +static const int hsv_green_y[] = { + -100, -99, -98, -97, -95, -94, -93, -91, + -90, -89, -87, -86, -84, -83, -81, -80, + -78, -76, -75, -73, -71, -70, -68, -66, + -64, -63, -61, -59, -57, -55, -53, -51, + -49, -48, -46, -44, -42, -40, -38, -36, + -34, -32, -30, -27, -25, -23, -21, -19, + -17, -15, -13, -11, -9, -7, -4, -2, + 0, 1, 3, 5, 7, 9, 11, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, 50, 52, 54, 56, 58, 59, 61, + 63, 65, 67, 68, 70, 72, 74, 75, + 77, 78, 80, 82, 83, 85, 86, 88, + 89, 90, 92, 93, 95, 96, 97, 98, + 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 112, 113, 114, + 115, 115, 116, 116, 117, 117, 118, 118, + 119, 119, 119, 120, 120, 120, 120, 120, + 121, 121, 121, 121, 121, 121, 120, 120, + 120, 120, 120, 119, 119, 119, 118, 118, + 117, 117, 116, 116, 115, 114, 114, 113, + 112, 111, 111, 110, 109, 108, 107, 106, + 105, 104, 103, 102, 100, 99, 98, 97, + 95, 94, 93, 91, 90, 89, 87, 86, + 84, 83, 81, 80, 78, 76, 75, 73, + 71, 70, 68, 66, 64, 63, 61, 59, + 57, 55, 53, 51, 49, 48, 46, 44, + 42, 40, 38, 36, 34, 32, 30, 27, + 25, 23, 21, 19, 17, 15, 13, 11, + 9, 7, 4, 2, 0, -1, -3, -5, + -7, -9, -11, -14, -16, -18, -20, -22, + -24, -26, -28, -30, -32, -34, -36, -38, + -40, -42, -44, -46, -48, -50, -52, -54, + -56, -58, -59, -61, -63, -65, -67, -68, + -70, -72, -74, -75, -77, -78, -80, -82, + -83, -85, -86, -88, -89, -90, -92, -93, + -95, -96, -97, -98, -100, -101, -102, -103, + -104, -105, -106, -107, -108, -109, -110, -111, + -112, -112, -113, -114, -115, -115, -116, -116, + -117, -117, -118, -118, -119, -119, -119, -120, + -120, -120, -120, -120, -121, -121, -121, -121, + -121, -121, -120, -120, -120, -120, -120, -119, + -119, -119, -118, -118, -117, -117, -116, -116, + -115, -114, -114, -113, -112, -111, -111, -110, + -109, -108, -107, -106, -105, -104, -103, -102, -100 +}; + +static const int hsv_blue_x[] = { + 112, 113, 114, 114, 115, 116, 117, 117, + 118, 118, 119, 119, 120, 120, 120, 121, + 121, 121, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 121, 121, + 121, 120, 120, 120, 119, 119, 118, 118, + 117, 116, 116, 115, 114, 113, 113, 112, + 111, 110, 109, 108, 107, 106, 105, 104, + 103, 102, 100, 99, 98, 97, 95, 94, + 93, 91, 90, 88, 87, 85, 84, 82, + 80, 79, 77, 76, 74, 72, 70, 69, + 67, 65, 63, 61, 60, 58, 56, 54, + 52, 50, 48, 46, 44, 42, 40, 38, + 36, 34, 32, 30, 28, 26, 24, 22, + 19, 17, 15, 13, 11, 9, 7, 5, + 2, 0, -1, -3, -5, -7, -9, -12, + -14, -16, -18, -20, -22, -24, -26, -28, + -31, -33, -35, -37, -39, -41, -43, -45, + -47, -49, -51, -53, -54, -56, -58, -60, + -62, -64, -66, -67, -69, -71, -73, -74, + -76, -78, -79, -81, -83, -84, -86, -87, + -89, -90, -92, -93, -94, -96, -97, -98, + -99, -101, -102, -103, -104, -105, -106, -107, + -108, -109, -110, -111, -112, -113, -114, -114, + -115, -116, -117, -117, -118, -118, -119, -119, + -120, -120, -120, -121, -121, -121, -122, -122, + -122, -122, -122, -122, -122, -122, -122, -122, + -122, -122, -121, -121, -121, -120, -120, -120, + -119, -119, -118, -118, -117, -116, -116, -115, + -114, -113, -113, -112, -111, -110, -109, -108, + -107, -106, -105, -104, -103, -102, -100, -99, + -98, -97, -95, -94, -93, -91, -90, -88, + -87, -85, -84, -82, -80, -79, -77, -76, + -74, -72, -70, -69, -67, -65, -63, -61, + -60, -58, -56, -54, -52, -50, -48, -46, + -44, -42, -40, -38, -36, -34, -32, -30, + -28, -26, -24, -22, -19, -17, -15, -13, + -11, -9, -7, -5, -2, 0, 1, 3, + 5, 7, 9, 12, 14, 16, 18, 20, + 22, 24, 26, 28, 31, 33, 35, 37, + 39, 41, 43, 45, 47, 49, 51, 53, + 54, 56, 58, 60, 62, 64, 66, 67, + 69, 71, 73, 74, 76, 78, 79, 81, + 83, 84, 86, 87, 89, 90, 92, 93, + 94, 96, 97, 98, 99, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112 +}; + +static const int hsv_blue_y[] = { + -11, -13, -15, -17, -19, -21, -23, -25, + -27, -29, -31, -33, -35, -37, -39, -41, + -43, -45, -46, -48, -50, -52, -54, -55, + -57, -59, -61, -62, -64, -66, -67, -69, + -71, -72, -74, -75, -77, -78, -80, -81, + -83, -84, -86, -87, -88, -90, -91, -92, + -93, -95, -96, -97, -98, -99, -100, -101, + -102, -103, -104, -105, -106, -106, -107, -108, + -109, -109, -110, -111, -111, -112, -112, -113, + -113, -114, -114, -114, -115, -115, -115, -115, + -116, -116, -116, -116, -116, -116, -116, -116, + -116, -115, -115, -115, -115, -114, -114, -114, + -113, -113, -112, -112, -111, -111, -110, -110, + -109, -108, -108, -107, -106, -105, -104, -103, + -102, -101, -100, -99, -98, -97, -96, -95, + -94, -93, -91, -90, -89, -88, -86, -85, + -84, -82, -81, -79, -78, -76, -75, -73, + -71, -70, -68, -67, -65, -63, -62, -60, + -58, -56, -55, -53, -51, -49, -47, -45, + -44, -42, -40, -38, -36, -34, -32, -30, + -28, -26, -24, -22, -20, -18, -16, -14, + -12, -10, -8, -6, -4, -2, 0, 1, + 3, 5, 7, 9, 11, 13, 15, 17, + 19, 21, 23, 25, 27, 29, 31, 33, + 35, 37, 39, 41, 43, 45, 46, 48, + 50, 52, 54, 55, 57, 59, 61, 62, + 64, 66, 67, 69, 71, 72, 74, 75, + 77, 78, 80, 81, 83, 84, 86, 87, + 88, 90, 91, 92, 93, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, + 106, 106, 107, 108, 109, 109, 110, 111, + 111, 112, 112, 113, 113, 114, 114, 114, + 115, 115, 115, 115, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 115, 115, 115, + 115, 114, 114, 114, 113, 113, 112, 112, + 111, 111, 110, 110, 109, 108, 108, 107, + 106, 105, 104, 103, 102, 101, 100, 99, + 98, 97, 96, 95, 94, 93, 91, 90, + 89, 88, 86, 85, 84, 82, 81, 79, + 78, 76, 75, 73, 71, 70, 68, 67, + 65, 63, 62, 60, 58, 56, 55, 53, + 51, 49, 47, 45, 44, 42, 40, 38, + 36, 34, 32, 30, 28, 26, 24, 22, + 20, 18, 16, 14, 12, 10, 8, 6, + 4, 2, 0, -1, -3, -5, -7, -9, -11 +}; + +static u16 i2c_ident[] = { + V4L2_IDENT_OV9650, + V4L2_IDENT_OV9655, + V4L2_IDENT_SOI968, + V4L2_IDENT_OV7660, + V4L2_IDENT_OV7670, + V4L2_IDENT_MT9V011, + V4L2_IDENT_MT9V111, + V4L2_IDENT_MT9V112, + V4L2_IDENT_MT9M001C12ST, + V4L2_IDENT_MT9M111, + V4L2_IDENT_HV7131R, +}; + +static u16 bridge_init[][2] = { + {0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c}, + {0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40}, + {0x1068, 0x30}, {0x1069, 0x20}, {0x106a, 0x10}, + {0x106b, 0x08}, {0x1188, 0x87}, {0x11a1, 0x00}, + {0x11a2, 0x00}, {0x11a3, 0x6a}, {0x11a4, 0x50}, + {0x11ab, 0x00}, {0x11ac, 0x00}, {0x11ad, 0x50}, + {0x11ae, 0x3c}, {0x118a, 0x04}, {0x0395, 0x04}, + {0x11b8, 0x3a}, {0x118b, 0x0e}, {0x10f7, 0x05}, + {0x10f8, 0x14}, {0x10fa, 0xff}, {0x10f9, 0x00}, + {0x11ba, 0x0a}, {0x11a5, 0x2d}, {0x11a6, 0x2d}, + {0x11a7, 0x3a}, {0x11a8, 0x05}, {0x11a9, 0x04}, + {0x11aa, 0x3f}, {0x11af, 0x28}, {0x11b0, 0xd8}, + {0x11b1, 0x14}, {0x11b2, 0xec}, {0x11b3, 0x32}, + {0x11b4, 0xdd}, {0x11b5, 0x32}, {0x11b6, 0xdd}, + {0x10e0, 0x2c}, {0x11bc, 0x40}, {0x11bd, 0x01}, + {0x11be, 0xf0}, {0x11bf, 0x00}, {0x118c, 0x1f}, + {0x118d, 0x1f}, {0x118e, 0x1f}, {0x118f, 0x1f}, + {0x1180, 0x01}, {0x1181, 0x00}, {0x1182, 0x01}, + {0x1183, 0x00}, {0x1184, 0x50}, {0x1185, 0x80} +}; + +/* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */ +static u8 ov_gain[] = { + 0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */, + 0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */, + 0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */, + 0x30 /* 4x */, 0x31 /* 4.25x */, 0x32 /* 4.5x */, 0x33 /* 4.75x */, + 0x34 /* 5x */, 0x35 /* 5.25x */, 0x36 /* 5.5x */, 0x37 /* 5.75x */, + 0x38 /* 6x */, 0x39 /* 6.25x */, 0x3a /* 6.5x */, 0x3b /* 6.75x */, + 0x3c /* 7x */, 0x3d /* 7.25x */, 0x3e /* 7.5x */, 0x3f /* 7.75x */, + 0x70 /* 8x */ +}; + +/* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */ +static u16 micron1_gain[] = { + /* 1x 1.25x 1.5x 1.75x */ + 0x0020, 0x0028, 0x0030, 0x0038, + /* 2x 2.25x 2.5x 2.75x */ + 0x00a0, 0x00a4, 0x00a8, 0x00ac, + /* 3x 3.25x 3.5x 3.75x */ + 0x00b0, 0x00b4, 0x00b8, 0x00bc, + /* 4x 4.25x 4.5x 4.75x */ + 0x00c0, 0x00c4, 0x00c8, 0x00cc, + /* 5x 5.25x 5.5x 5.75x */ + 0x00d0, 0x00d4, 0x00d8, 0x00dc, + /* 6x 6.25x 6.5x 6.75x */ + 0x00e0, 0x00e4, 0x00e8, 0x00ec, + /* 7x 7.25x 7.5x 7.75x */ + 0x00f0, 0x00f4, 0x00f8, 0x00fc, + /* 8x */ + 0x01c0 +}; + +/* mt9m001 sensor uses a different gain formula then other micron sensors */ +/* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */ +static u16 micron2_gain[] = { + /* 1x 1.25x 1.5x 1.75x */ + 0x0008, 0x000a, 0x000c, 0x000e, + /* 2x 2.25x 2.5x 2.75x */ + 0x0010, 0x0012, 0x0014, 0x0016, + /* 3x 3.25x 3.5x 3.75x */ + 0x0018, 0x001a, 0x001c, 0x001e, + /* 4x 4.25x 4.5x 4.75x */ + 0x0020, 0x0051, 0x0052, 0x0053, + /* 5x 5.25x 5.5x 5.75x */ + 0x0054, 0x0055, 0x0056, 0x0057, + /* 6x 6.25x 6.5x 6.75x */ + 0x0058, 0x0059, 0x005a, 0x005b, + /* 7x 7.25x 7.5x 7.75x */ + 0x005c, 0x005d, 0x005e, 0x005f, + /* 8x */ + 0x0060 +}; + +/* Gain = .5 + bit[7:0] / 16 */ +static u8 hv7131r_gain[] = { + 0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */, + 0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */, + 0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */, + 0x38 /* 4x */, 0x3c /* 4.25x */, 0x40 /* 4.5x */, 0x44 /* 4.75x */, + 0x48 /* 5x */, 0x4c /* 5.25x */, 0x50 /* 5.5x */, 0x54 /* 5.75x */, + 0x58 /* 6x */, 0x5c /* 6.25x */, 0x60 /* 6.5x */, 0x64 /* 6.75x */, + 0x68 /* 7x */, 0x6c /* 7.25x */, 0x70 /* 7.5x */, 0x74 /* 7.75x */, + 0x78 /* 8x */ +}; + +static u8 soi968_init[][2] = { + {0x12, 0x80}, {0x0c, 0x00}, {0x0f, 0x1f}, + {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00}, + {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c}, + {0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff}, + {0x3e, 0x00}, {0x3f, 0x00}, {0x3b, 0x20}, + {0x3a, 0x96}, {0x3d, 0x0a}, {0x14, 0x8e}, + {0x13, 0x8a}, {0x12, 0x40}, {0x17, 0x13}, + {0x18, 0x63}, {0x19, 0x01}, {0x1a, 0x79}, + {0x32, 0x24}, {0x03, 0x00}, {0x11, 0x40}, + {0x2a, 0x10}, {0x2b, 0xe0}, {0x10, 0x32}, + {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80}, +}; + +static u8 ov7660_init[][2] = { + {0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3}, + {0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40}, + {0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a}, + {0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43}, + {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0xf6}, + {0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50}, +}; + +static u8 ov7670_init[][2] = { + {0x12, 0x80}, {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, + {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00}, + {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0}, + {0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00}, + {0x0d, 0x40}, {0x14, 0x28}, {0xa5, 0x05}, {0xab, 0x07}, + {0x24, 0x95}, {0x25, 0x33}, {0x26, 0xe3}, {0x9f, 0x75}, + {0xa0, 0x65}, {0xa1, 0x0b}, {0xa6, 0xd8}, {0xa7, 0xd8}, + {0xa8, 0xf0}, {0xa9, 0x90}, {0xaa, 0x94}, {0x13, 0xe5}, + {0x0e, 0x61}, {0x0f, 0x4b}, {0x16, 0x02}, {0x1e, 0x27}, + {0x21, 0x02}, {0x22, 0x91}, {0x29, 0x07}, {0x33, 0x0b}, + {0x35, 0x0b}, {0x37, 0x1d}, {0x38, 0x71}, {0x39, 0x2a}, + {0x3c, 0x78}, {0x4d, 0x40}, {0x4e, 0x20}, {0x69, 0x00}, + {0x74, 0x19}, {0x8d, 0x4f}, {0x8e, 0x00}, {0x8f, 0x00}, + {0x90, 0x00}, {0x91, 0x00}, {0x96, 0x00}, {0x9a, 0x80}, + {0xb0, 0x84}, {0xb1, 0x0c}, {0xb2, 0x0e}, {0xb3, 0x82}, + {0xb8, 0x0a}, {0x43, 0x0a}, {0x44, 0xf0}, {0x45, 0x20}, + {0x46, 0x7d}, {0x47, 0x29}, {0x48, 0x4a}, {0x59, 0x8c}, + {0x5a, 0xa5}, {0x5b, 0xde}, {0x5c, 0x96}, {0x5d, 0x66}, + {0x5e, 0x10}, {0x6c, 0x0a}, {0x6d, 0x55}, {0x6e, 0x11}, + {0x6f, 0x9e}, {0x6a, 0x40}, {0x01, 0x40}, {0x02, 0x40}, + {0x13, 0xe7}, {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x02}, + {0x52, 0x1d}, {0x53, 0x56}, {0x54, 0x73}, {0x55, 0x0a}, + {0x56, 0x55}, {0x57, 0x80}, {0x58, 0x9e}, {0x41, 0x08}, + {0x3f, 0x02}, {0x75, 0x03}, {0x76, 0x63}, {0x4c, 0x04}, + {0x77, 0x06}, {0x3d, 0x02}, {0x4b, 0x09}, {0xc9, 0x30}, + {0x41, 0x08}, {0x56, 0x48}, {0x34, 0x11}, {0xa4, 0x88}, + {0x96, 0x00}, {0x97, 0x30}, {0x98, 0x20}, {0x99, 0x30}, + {0x9a, 0x84}, {0x9b, 0x29}, {0x9c, 0x03}, {0x9d, 0x99}, + {0x9e, 0x7f}, {0x78, 0x04}, {0x79, 0x01}, {0xc8, 0xf0}, + {0x79, 0x0f}, {0xc8, 0x00}, {0x79, 0x10}, {0xc8, 0x7e}, + {0x79, 0x0a}, {0xc8, 0x80}, {0x79, 0x0b}, {0xc8, 0x01}, + {0x79, 0x0c}, {0xc8, 0x0f}, {0x79, 0x0d}, {0xc8, 0x20}, + {0x79, 0x09}, {0xc8, 0x80}, {0x79, 0x02}, {0xc8, 0xc0}, + {0x79, 0x03}, {0xc8, 0x40}, {0x79, 0x05}, {0xc8, 0x30}, + {0x79, 0x26}, {0x62, 0x20}, {0x63, 0x00}, {0x64, 0x06}, + {0x65, 0x00}, {0x66, 0x05}, {0x94, 0x05}, {0x95, 0x0a}, + {0x17, 0x13}, {0x18, 0x01}, {0x19, 0x02}, {0x1a, 0x7a}, + {0x46, 0x59}, {0x47, 0x30}, {0x58, 0x9a}, {0x59, 0x84}, + {0x5a, 0x91}, {0x5b, 0x57}, {0x5c, 0x75}, {0x5d, 0x6d}, + {0x5e, 0x13}, {0x64, 0x07}, {0x94, 0x07}, {0x95, 0x0d}, + {0xa6, 0xdf}, {0xa7, 0xdf}, {0x48, 0x4d}, {0x51, 0x00}, + {0x6b, 0x0a}, {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, + {0x92, 0x00}, {0x93, 0x00}, {0x55, 0x0a}, {0x56, 0x60}, + {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, + {0x53, 0x56}, {0x54, 0x73}, {0x58, 0x9a}, {0x4f, 0x6e}, + {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, {0x53, 0x56}, + {0x54, 0x73}, {0x58, 0x9a}, {0x3f, 0x01}, {0x7b, 0x03}, + {0x7c, 0x09}, {0x7d, 0x16}, {0x7e, 0x38}, {0x7f, 0x47}, + {0x80, 0x53}, {0x81, 0x5e}, {0x82, 0x6a}, {0x83, 0x74}, + {0x84, 0x80}, {0x85, 0x8c}, {0x86, 0x9b}, {0x87, 0xb2}, + {0x88, 0xcc}, {0x89, 0xe5}, {0x7a, 0x24}, {0x3b, 0x00}, + {0x9f, 0x76}, {0xa0, 0x65}, {0x13, 0xe2}, {0x6b, 0x0a}, + {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, {0x92, 0x00}, + {0x93, 0x00}, +}; + +static u8 ov9650_init[][2] = { + {0x12, 0x80}, {0x00, 0x00}, {0x01, 0x78}, + {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03}, + {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00}, + {0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00}, + {0x0e, 0xa0}, {0x0f, 0x52}, {0x10, 0x7c}, + {0x11, 0x80}, {0x12, 0x45}, {0x13, 0xc2}, + {0x14, 0x2e}, {0x15, 0x00}, {0x16, 0x07}, + {0x17, 0x24}, {0x18, 0xc5}, {0x19, 0x00}, + {0x1a, 0x3c}, {0x1b, 0x00}, {0x1e, 0x04}, + {0x1f, 0x00}, {0x24, 0x78}, {0x25, 0x68}, + {0x26, 0xd4}, {0x27, 0x80}, {0x28, 0x80}, + {0x29, 0x30}, {0x2a, 0x00}, {0x2b, 0x00}, + {0x2c, 0x80}, {0x2d, 0x00}, {0x2e, 0x00}, + {0x2f, 0x00}, {0x30, 0x08}, {0x31, 0x30}, + {0x32, 0x84}, {0x33, 0xe2}, {0x34, 0xbf}, + {0x35, 0x81}, {0x36, 0xf9}, {0x37, 0x00}, + {0x38, 0x93}, {0x39, 0x50}, {0x3a, 0x01}, + {0x3b, 0x01}, {0x3c, 0x73}, {0x3d, 0x19}, + {0x3e, 0x0b}, {0x3f, 0x80}, {0x40, 0xc1}, + {0x41, 0x00}, {0x42, 0x08}, {0x67, 0x80}, + {0x68, 0x80}, {0x69, 0x40}, {0x6a, 0x00}, + {0x6b, 0x0a}, {0x8b, 0x06}, {0x8c, 0x20}, + {0x8d, 0x00}, {0x8e, 0x00}, {0x8f, 0xdf}, + {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x88}, + {0x95, 0x88}, {0x96, 0x04}, {0xa1, 0x00}, + {0xa5, 0x80}, {0xa8, 0x80}, {0xa9, 0xb8}, + {0xaa, 0x92}, {0xab, 0x0a}, +}; + +static u8 ov9655_init[][2] = { + {0x12, 0x80}, {0x12, 0x01}, {0x0d, 0x00}, {0x0e, 0x61}, + {0x11, 0x80}, {0x13, 0xba}, {0x14, 0x2e}, {0x16, 0x24}, + {0x1e, 0x04}, {0x1e, 0x04}, {0x1e, 0x04}, {0x27, 0x08}, + {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x32, 0xbf}, + {0x34, 0x3d}, {0x35, 0x00}, {0x36, 0xf8}, {0x38, 0x12}, + {0x39, 0x57}, {0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c}, + {0x3d, 0x19}, {0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40}, + {0x42, 0x80}, {0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a}, + {0x48, 0x3c}, {0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc}, + {0x4d, 0xdc}, {0x4e, 0xdc}, {0x69, 0x02}, {0x6c, 0x04}, + {0x6f, 0x9e}, {0x70, 0x05}, {0x71, 0x78}, {0x77, 0x02}, + {0x8a, 0x23}, {0x8c, 0x0d}, {0x90, 0x7e}, {0x91, 0x7c}, + {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68}, {0xa6, 0x60}, + {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92}, {0xab, 0x04}, + {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80}, {0xaf, 0x80}, + {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00}, {0xb6, 0xaf}, + {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44}, {0xbe, 0x3b}, + {0xbf, 0x3a}, {0xc0, 0xe2}, {0xc1, 0xc8}, {0xc2, 0x01}, + {0xc4, 0x00}, {0xc6, 0x85}, {0xc7, 0x81}, {0xc9, 0xe0}, + {0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x12, 0x61}, + {0x36, 0xfa}, {0x8c, 0x8d}, {0xc0, 0xaa}, {0x69, 0x0a}, + {0x03, 0x12}, {0x17, 0x14}, {0x18, 0x00}, {0x19, 0x01}, + {0x1a, 0x3d}, {0x32, 0xbf}, {0x11, 0x80}, {0x2a, 0x10}, + {0x2b, 0x0a}, {0x92, 0x00}, {0x93, 0x00}, {0x1e, 0x04}, + {0x1e, 0x04}, {0x10, 0x7c}, {0x04, 0x03}, {0xa1, 0x00}, + {0x2d, 0x00}, {0x2e, 0x00}, {0x00, 0x00}, {0x01, 0x80}, + {0x02, 0x80}, {0x12, 0x61}, {0x36, 0xfa}, {0x8c, 0x8d}, + {0xc0, 0xaa}, {0x69, 0x0a}, {0x03, 0x12}, {0x17, 0x14}, + {0x18, 0x00}, {0x19, 0x01}, {0x1a, 0x3d}, {0x32, 0xbf}, + {0x11, 0x80}, {0x2a, 0x10}, {0x2b, 0x0a}, {0x92, 0x00}, + {0x93, 0x00}, {0x04, 0x01}, {0x10, 0x1f}, {0xa1, 0x00}, + {0x00, 0x0a}, {0xa1, 0x00}, {0x10, 0x5d}, {0x04, 0x03}, + {0x00, 0x01}, {0xa1, 0x00}, {0x10, 0x7c}, {0x04, 0x03}, + {0x00, 0x03}, {0x00, 0x0a}, {0x00, 0x10}, {0x00, 0x13}, +}; + +static u16 mt9v112_init[][2] = { + {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020}, + {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b}, + {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001}, + {0x05, 0x0000}, {0x06, 0x340c}, {0x3b, 0x042a}, + {0x3c, 0x0400}, {0xf0, 0x0002}, {0x2e, 0x0c58}, + {0x5b, 0x0001}, {0xc8, 0x9f0b}, {0xf0, 0x0001}, + {0x9b, 0x5300}, {0xf0, 0x0000}, {0x2b, 0x0020}, + {0x2c, 0x002a}, {0x2d, 0x0032}, {0x2e, 0x0020}, + {0x09, 0x01dc}, {0x01, 0x000c}, {0x02, 0x0020}, + {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c}, + {0x05, 0x0098}, {0x20, 0x0703}, {0x09, 0x01f2}, + {0x2b, 0x00a0}, {0x2c, 0x00a0}, {0x2d, 0x00a0}, + {0x2e, 0x00a0}, {0x01, 0x000c}, {0x02, 0x0020}, + {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c}, + {0x05, 0x0098}, {0x09, 0x01c1}, {0x2b, 0x00ae}, + {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae}, +}; + +static u16 mt9v111_init[][2] = { + {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000}, + {0x01, 0x0001}, {0x02, 0x0016}, {0x03, 0x01e1}, + {0x04, 0x0281}, {0x05, 0x0004}, {0x07, 0x3002}, + {0x21, 0x0000}, {0x25, 0x4024}, {0x26, 0xff03}, + {0x27, 0xff10}, {0x2b, 0x7828}, {0x2c, 0xb43c}, + {0x2d, 0xf0a0}, {0x2e, 0x0c64}, {0x2f, 0x0064}, + {0x67, 0x4010}, {0x06, 0x301e}, {0x08, 0x0480}, + {0x01, 0x0004}, {0x02, 0x0016}, {0x03, 0x01e6}, + {0x04, 0x0286}, {0x05, 0x0004}, {0x06, 0x0000}, + {0x07, 0x3002}, {0x08, 0x0008}, {0x0c, 0x0000}, + {0x0d, 0x0000}, {0x0e, 0x0000}, {0x0f, 0x0000}, + {0x10, 0x0000}, {0x11, 0x0000}, {0x12, 0x00b0}, + {0x13, 0x007c}, {0x14, 0x0000}, {0x15, 0x0000}, + {0x16, 0x0000}, {0x17, 0x0000}, {0x18, 0x0000}, + {0x19, 0x0000}, {0x1a, 0x0000}, {0x1b, 0x0000}, + {0x1c, 0x0000}, {0x1d, 0x0000}, {0x30, 0x0000}, + {0x30, 0x0005}, {0x31, 0x0000}, {0x02, 0x0016}, + {0x03, 0x01e1}, {0x04, 0x0281}, {0x05, 0x0004}, + {0x06, 0x0000}, {0x07, 0x3002}, {0x06, 0x002d}, + {0x05, 0x0004}, {0x09, 0x0064}, {0x2b, 0x00a0}, + {0x2c, 0x00a0}, {0x2d, 0x00a0}, {0x2e, 0x00a0}, + {0x02, 0x0016}, {0x03, 0x01e1}, {0x04, 0x0281}, + {0x05, 0x0004}, {0x06, 0x002d}, {0x07, 0x3002}, + {0x0e, 0x0008}, {0x06, 0x002d}, {0x05, 0x0004}, +}; + +static u16 mt9v011_init[][2] = { + {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000}, + {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1}, + {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006}, + {0x0d, 0x0002}, {0x0a, 0x0000}, {0x0b, 0x0000}, + {0x0c, 0x0000}, {0x0d, 0x0000}, {0x0e, 0x0000}, + {0x0f, 0x0000}, {0x10, 0x0000}, {0x11, 0x0000}, + {0x12, 0x0000}, {0x13, 0x0000}, {0x14, 0x0000}, + {0x15, 0x0000}, {0x16, 0x0000}, {0x17, 0x0000}, + {0x18, 0x0000}, {0x19, 0x0000}, {0x1a, 0x0000}, + {0x1b, 0x0000}, {0x1c, 0x0000}, {0x1d, 0x0000}, + {0x32, 0x0000}, {0x20, 0x1101}, {0x21, 0x0000}, + {0x22, 0x0000}, {0x23, 0x0000}, {0x24, 0x0000}, + {0x25, 0x0000}, {0x26, 0x0000}, {0x27, 0x0024}, + {0x2f, 0xf7b0}, {0x30, 0x0005}, {0x31, 0x0000}, + {0x32, 0x0000}, {0x33, 0x0000}, {0x34, 0x0100}, + {0x3d, 0x068f}, {0x40, 0x01e0}, {0x41, 0x00d1}, + {0x44, 0x0082}, {0x5a, 0x0000}, {0x5b, 0x0000}, + {0x5c, 0x0000}, {0x5d, 0x0000}, {0x5e, 0x0000}, + {0x5f, 0xa31d}, {0x62, 0x0611}, {0x0a, 0x0000}, + {0x06, 0x0029}, {0x05, 0x0009}, {0x20, 0x1101}, + {0x20, 0x1101}, {0x09, 0x0064}, {0x07, 0x0003}, + {0x2b, 0x0033}, {0x2c, 0x00a0}, {0x2d, 0x00a0}, + {0x2e, 0x0033}, {0x07, 0x0002}, {0x06, 0x0000}, + {0x06, 0x0029}, {0x05, 0x0009}, +}; + +static u16 mt9m001_init[][2] = { + {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x000e}, + {0x02, 0x0014}, {0x03, 0x03c1}, {0x04, 0x0501}, + {0x05, 0x0083}, {0x06, 0x0006}, {0x0d, 0x0002}, + {0x0a, 0x0000}, {0x0c, 0x0000}, {0x11, 0x0000}, + {0x1e, 0x8000}, {0x5f, 0x8904}, {0x60, 0x0000}, + {0x61, 0x0000}, {0x62, 0x0498}, {0x63, 0x0000}, + {0x64, 0x0000}, {0x20, 0x111d}, {0x06, 0x00f2}, + {0x05, 0x0013}, {0x09, 0x10f2}, {0x07, 0x0003}, + {0x2b, 0x002a}, {0x2d, 0x002a}, {0x2c, 0x002a}, + {0x2e, 0x0029}, {0x07, 0x0002}, +}; + +static u16 mt9m111_init[][2] = { + {0xf0, 0x0000}, {0x0d, 0x0008}, {0x0d, 0x0009}, + {0x0d, 0x0008}, {0xf0, 0x0001}, {0x3a, 0x4300}, + {0x9b, 0x4300}, {0xa1, 0x0280}, {0xa4, 0x0200}, + {0x06, 0x308e}, {0xf0, 0x0000}, +}; + +static u8 hv7131r_init[][2] = { + {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08}, + {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0}, + {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08}, + {0x01, 0x08}, {0x01, 0x08}, {0x25, 0x07}, + {0x26, 0xc3}, {0x27, 0x50}, {0x30, 0x62}, + {0x31, 0x10}, {0x32, 0x06}, {0x33, 0x10}, + {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00}, + {0x23, 0x09}, {0x01, 0x08}, +}; + +int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) +{ + struct usb_device *dev = gspca_dev->dev; + int result; + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + reg, + 0x00, + gspca_dev->usb_buf, + length, + 500); + if (unlikely(result < 0 || result != length)) { + err("Read register failed 0x%02X", reg); + return -EIO; + } + return 0; +} + +int reg_w(struct gspca_dev *gspca_dev, u16 reg, const u8 *buffer, int length) +{ + struct usb_device *dev = gspca_dev->dev; + int result; + memcpy(gspca_dev->usb_buf, buffer, length); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + reg, + 0x00, + gspca_dev->usb_buf, + length, + 500); + if (unlikely(result < 0 || result != length)) { + err("Write register failed index 0x%02X", reg); + return -EIO; + } + return 0; +} + +int reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value) +{ + u8 data[1] = {value}; + return reg_w(gspca_dev, reg, data, 1); +} + +int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) +{ + int i; + reg_w(gspca_dev, 0x10c0, buffer, 8); + for (i = 0; i < 5; i++) { + reg_r(gspca_dev, 0x10c0, 1); + if (gspca_dev->usb_buf[0] & 0x04) { + if (gspca_dev->usb_buf[0] & 0x08) + return -1; + return 0; + } + msleep(1); + } + return -1; +} + +int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + u8 row[8]; + + /* + * from the point of view of the bridge, the length + * includes the address + */ + row[0] = 0x81 | (2 << 4); + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = val; + row[4] = 0x00; + row[5] = 0x00; + row[6] = 0x00; + row[7] = 0x10; + + return i2c_w(gspca_dev, row); +} + +int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 row[8]; + + /* + * from the point of view of the bridge, the length + * includes the address + */ + row[0] = 0x81 | (3 << 4); + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = (val >> 8) & 0xff; + row[4] = val & 0xff; + row[5] = 0x00; + row[6] = 0x00; + row[7] = 0x10; + + return i2c_w(gspca_dev, row); +} + +int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 row[8]; + + row[0] = 0x81 | 0x10; + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = 0; + row[4] = 0; + row[5] = 0; + row[6] = 0; + row[7] = 0x10; + reg_w(gspca_dev, 0x10c0, row, 8); + msleep(1); + row[0] = 0x81 | (2 << 4) | 0x02; + row[2] = 0; + reg_w(gspca_dev, 0x10c0, row, 8); + msleep(1); + reg_r(gspca_dev, 0x10c2, 5); + *val = gspca_dev->usb_buf[3]; + return 0; +} + +int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 row[8]; + + row[0] = 0x81 | 0x10; + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = 0; + row[4] = 0; + row[5] = 0; + row[6] = 0; + row[7] = 0x10; + reg_w(gspca_dev, 0x10c0, row, 8); + msleep(1); + row[0] = 0x81 | (3 << 4) | 0x02; + row[2] = 0; + reg_w(gspca_dev, 0x10c0, row, 8); + msleep(1); + reg_r(gspca_dev, 0x10c2, 5); + *val = (gspca_dev->usb_buf[2] << 8) | gspca_dev->usb_buf[3]; + return 0; +} + +static int ov9650_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(ov9650_init); i++) { + if (i2c_w1(gspca_dev, ov9650_init[i][0], + ov9650_init[i][1]) < 0) { + err("OV9650 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 1; + sd->vstart = 7; + return 0; +} + +static int ov9655_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(ov9655_init); i++) { + if (i2c_w1(gspca_dev, ov9655_init[i][0], + ov9655_init[i][1]) < 0) { + err("OV9655 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 0; + sd->vstart = 7; + return 0; +} + +static int soi968_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(soi968_init); i++) { + if (i2c_w1(gspca_dev, soi968_init[i][0], + soi968_init[i][1]) < 0) { + err("SOI968 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 60; + sd->vstart = 11; + return 0; +} + +static int ov7660_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(ov7660_init); i++) { + if (i2c_w1(gspca_dev, ov7660_init[i][0], + ov7660_init[i][1]) < 0) { + err("OV7660 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 1; + sd->vstart = 1; + return 0; +} + +static int ov7670_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(ov7670_init); i++) { + if (i2c_w1(gspca_dev, ov7670_init[i][0], + ov7670_init[i][1]) < 0) { + err("OV7670 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 0; + sd->vstart = 1; + return 0; +} + +static int mt9v_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + u16 value; + int ret; + + sd->i2c_addr = 0x5d; + ret = i2c_r2(gspca_dev, 0xff, &value); + if ((ret == 0) && (value == 0x8243)) { + for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) { + if (i2c_w2(gspca_dev, mt9v011_init[i][0], + mt9v011_init[i][1]) < 0) { + err("MT9V011 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 2; + sd->vstart = 2; + sd->sensor = SENSOR_MT9V011; + info("MT9V011 sensor detected"); + return 0; + } + + sd->i2c_addr = 0x5c; + i2c_w2(gspca_dev, 0x01, 0x0004); + ret = i2c_r2(gspca_dev, 0xff, &value); + if ((ret == 0) && (value == 0x823a)) { + for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) { + if (i2c_w2(gspca_dev, mt9v111_init[i][0], + mt9v111_init[i][1]) < 0) { + err("MT9V111 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 2; + sd->vstart = 2; + sd->sensor = SENSOR_MT9V111; + info("MT9V111 sensor detected"); + return 0; + } + + sd->i2c_addr = 0x5d; + ret = i2c_w2(gspca_dev, 0xf0, 0x0000); + if (ret < 0) { + sd->i2c_addr = 0x48; + i2c_w2(gspca_dev, 0xf0, 0x0000); + } + ret = i2c_r2(gspca_dev, 0x00, &value); + if ((ret == 0) && (value == 0x1229)) { + for (i = 0; i < ARRAY_SIZE(mt9v112_init); i++) { + if (i2c_w2(gspca_dev, mt9v112_init[i][0], + mt9v112_init[i][1]) < 0) { + err("MT9V112 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 6; + sd->vstart = 2; + sd->sensor = SENSOR_MT9V112; + info("MT9V112 sensor detected"); + return 0; + } + + return -ENODEV; +} + +static int mt9m111_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) { + if (i2c_w2(gspca_dev, mt9m111_init[i][0], + mt9m111_init[i][1]) < 0) { + err("MT9M111 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 0; + sd->vstart = 2; + return 0; +} + +static int mt9m001_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) { + if (i2c_w2(gspca_dev, mt9m001_init[i][0], + mt9m001_init[i][1]) < 0) { + err("MT9M001 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 2; + sd->vstart = 2; + return 0; +} + +static int hv7131r_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(hv7131r_init); i++) { + if (i2c_w1(gspca_dev, hv7131r_init[i][0], + hv7131r_init[i][1]) < 0) { + err("HV7131R Sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 0; + sd->vstart = 1; + return 0; +} + +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV +static int input_kthread(void *data) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *)data; + struct sd *sd = (struct sd *) gspca_dev; + + DECLARE_WAIT_QUEUE_HEAD(wait); + set_freezable(); + for (;;) { + if (kthread_should_stop()) + break; + + if (reg_r(gspca_dev, 0x1005, 1) < 0) + continue; + + input_report_key(sd->input_dev, + KEY_CAMERA, + gspca_dev->usb_buf[0] & sd->input_gpio); + input_sync(sd->input_dev); + + wait_event_freezable_timeout(wait, + kthread_should_stop(), + msecs_to_jiffies(100)); + } + return 0; +} + + +static int sn9c20x_input_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + if (sd->input_gpio == 0) + return 0; + + sd->input_dev = input_allocate_device(); + if (!sd->input_dev) + return -ENOMEM; + + sd->input_dev->name = "SN9C20X Webcam"; + + sd->input_dev->phys = kasprintf(GFP_KERNEL, "usb-%s-%s", + gspca_dev->dev->bus->bus_name, + gspca_dev->dev->devpath); + + if (!sd->input_dev->phys) + return -ENOMEM; + + usb_to_input_id(gspca_dev->dev, &sd->input_dev->id); + sd->input_dev->dev.parent = &gspca_dev->dev->dev; + + set_bit(EV_KEY, sd->input_dev->evbit); + set_bit(KEY_CAMERA, sd->input_dev->keybit); + + if (input_register_device(sd->input_dev)) + return -EINVAL; + + sd->input_task = kthread_run(input_kthread, gspca_dev, "sn9c20x/%d", + gspca_dev->vdev.minor); + + if (IS_ERR(sd->input_task)) + return -EINVAL; + + return 0; +} + +static void sn9c20x_input_cleanup(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + if (sd->input_task != NULL && !IS_ERR(sd->input_task)) + kthread_stop(sd->input_task); + + if (sd->input_dev != NULL) { + input_unregister_device(sd->input_dev); + kfree(sd->input_dev->phys); + input_free_device(sd->input_dev); + sd->input_dev = NULL; + } +} +#endif + +static int set_cmatrix(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 hue_coord, hue_index = 180 + sd->hue; + u8 cmatrix[21]; + memset(cmatrix, 0, 21); + + cmatrix[2] = (sd->contrast * 0x25 / 0x100) + 0x26; + cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25; + cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25; + cmatrix[18] = sd->brightness - 0x80; + + hue_coord = (hsv_red_x[hue_index] * sd->saturation) >> 8; + cmatrix[6] = (unsigned char)(hue_coord & 0xff); + cmatrix[7] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_red_y[hue_index] * sd->saturation) >> 8; + cmatrix[8] = (unsigned char)(hue_coord & 0xff); + cmatrix[9] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_green_x[hue_index] * sd->saturation) >> 8; + cmatrix[10] = (unsigned char)(hue_coord & 0xff); + cmatrix[11] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_green_y[hue_index] * sd->saturation) >> 8; + cmatrix[12] = (unsigned char)(hue_coord & 0xff); + cmatrix[13] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_blue_x[hue_index] * sd->saturation) >> 8; + cmatrix[14] = (unsigned char)(hue_coord & 0xff); + cmatrix[15] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_blue_y[hue_index] * sd->saturation) >> 8; + cmatrix[16] = (unsigned char)(hue_coord & 0xff); + cmatrix[17] = (unsigned char)((hue_coord >> 8) & 0x0f); + + return reg_w(gspca_dev, 0x10e1, cmatrix, 21); +} + +static int set_gamma(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 gamma[17]; + u8 gval = sd->gamma * 0xb8 / 0x100; + + + gamma[0] = 0x0a; + gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8); + gamma[2] = 0x25 + (gval * (0xee - 0x25) / 0xb8); + gamma[3] = 0x37 + (gval * (0xfa - 0x37) / 0xb8); + gamma[4] = 0x45 + (gval * (0xfc - 0x45) / 0xb8); + gamma[5] = 0x55 + (gval * (0xfb - 0x55) / 0xb8); + gamma[6] = 0x65 + (gval * (0xfc - 0x65) / 0xb8); + gamma[7] = 0x74 + (gval * (0xfd - 0x74) / 0xb8); + gamma[8] = 0x83 + (gval * (0xfe - 0x83) / 0xb8); + gamma[9] = 0x92 + (gval * (0xfc - 0x92) / 0xb8); + gamma[10] = 0xa1 + (gval * (0xfc - 0xa1) / 0xb8); + gamma[11] = 0xb0 + (gval * (0xfc - 0xb0) / 0xb8); + gamma[12] = 0xbf + (gval * (0xfb - 0xbf) / 0xb8); + gamma[13] = 0xce + (gval * (0xfb - 0xce) / 0xb8); + gamma[14] = 0xdf + (gval * (0xfd - 0xdf) / 0xb8); + gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8); + gamma[16] = 0xf5; + + return reg_w(gspca_dev, 0x1190, gamma, 17); +} + +static int set_redblue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + reg_w1(gspca_dev, 0x118c, sd->red); + reg_w1(gspca_dev, 0x118f, sd->blue); + return 0; +} + +static int set_hvflip(struct gspca_dev *gspca_dev) +{ + u8 value, tslb; + u16 value2; + struct sd *sd = (struct sd *) gspca_dev; + switch (sd->sensor) { + case SENSOR_OV9650: + i2c_r1(gspca_dev, 0x1e, &value); + value &= ~0x30; + tslb = 0x01; + if (sd->hflip) + value |= 0x20; + if (sd->vflip) { + value |= 0x10; + tslb = 0x49; + } + i2c_w1(gspca_dev, 0x1e, value); + i2c_w1(gspca_dev, 0x3a, tslb); + break; + case SENSOR_MT9V111: + case SENSOR_MT9V011: + i2c_r2(gspca_dev, 0x20, &value2); + value2 &= ~0xc0a0; + if (sd->hflip) + value2 |= 0x8080; + if (sd->vflip) + value2 |= 0x4020; + i2c_w2(gspca_dev, 0x20, value2); + break; + case SENSOR_MT9M111: + case SENSOR_MT9V112: + i2c_r2(gspca_dev, 0x20, &value2); + value2 &= ~0x0003; + if (sd->hflip) + value2 |= 0x0002; + if (sd->vflip) + value2 |= 0x0001; + i2c_w2(gspca_dev, 0x20, value2); + break; + case SENSOR_HV7131R: + i2c_r1(gspca_dev, 0x01, &value); + value &= ~0x03; + if (sd->vflip) + value |= 0x01; + if (sd->hflip) + value |= 0x02; + i2c_w1(gspca_dev, 0x01, value); + break; + } + return 0; +} + +static int set_exposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 exp[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e}; + switch (sd->sensor) { + case SENSOR_OV7660: + case SENSOR_OV7670: + case SENSOR_SOI968: + case SENSOR_OV9655: + case SENSOR_OV9650: + exp[0] |= (3 << 4); + exp[2] = 0x2d; + exp[3] = sd->exposure & 0xff; + exp[4] = sd->exposure >> 8; + break; + case SENSOR_MT9M001: + case SENSOR_MT9M111: + case SENSOR_MT9V112: + case SENSOR_MT9V111: + case SENSOR_MT9V011: + exp[0] |= (3 << 4); + exp[2] = 0x09; + exp[3] = sd->exposure >> 8; + exp[4] = sd->exposure & 0xff; + break; + case SENSOR_HV7131R: + exp[0] |= (4 << 4); + exp[2] = 0x25; + exp[3] = ((sd->exposure * 0xffffff) / 0xffff) >> 16; + exp[4] = ((sd->exposure * 0xffffff) / 0xffff) >> 8; + exp[5] = ((sd->exposure * 0xffffff) / 0xffff) & 0xff; + break; + } + i2c_w(gspca_dev, exp); + return 0; +} + +static int set_gain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 gain[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d}; + switch (sd->sensor) { + case SENSOR_OV7660: + case SENSOR_OV7670: + case SENSOR_SOI968: + case SENSOR_OV9655: + case SENSOR_OV9650: + gain[0] |= (2 << 4); + gain[3] = ov_gain[sd->gain]; + break; + case SENSOR_MT9V011: + case SENSOR_MT9V111: + gain[0] |= (3 << 4); + gain[2] = 0x35; + gain[3] = micron1_gain[sd->gain] >> 8; + gain[4] = micron1_gain[sd->gain] & 0xff; + break; + case SENSOR_MT9V112: + case SENSOR_MT9M111: + gain[0] |= (3 << 4); + gain[2] = 0x2f; + gain[3] = micron1_gain[sd->gain] >> 8; + gain[4] = micron1_gain[sd->gain] & 0xff; + break; + case SENSOR_MT9M001: + gain[0] |= (3 << 4); + gain[2] = 0x2f; + gain[3] = micron2_gain[sd->gain] >> 8; + gain[4] = micron2_gain[sd->gain] & 0xff; + break; + case SENSOR_HV7131R: + gain[0] |= (2 << 4); + gain[2] = 0x30; + gain[3] = hv7131r_gain[sd->gain]; + break; + } + i2c_w(gspca_dev, gain); + return 0; +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + return set_cmatrix(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->brightness; + return 0; +} + + +static int sd_setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + return set_cmatrix(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->contrast; + return 0; +} + +static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->saturation = val; + if (gspca_dev->streaming) + return set_cmatrix(gspca_dev); + return 0; +} + +static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->saturation; + return 0; +} + +static int sd_sethue(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = val; + if (gspca_dev->streaming) + return set_cmatrix(gspca_dev); + return 0; +} + +static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->hue; + return 0; +} + +static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gamma = val; + if (gspca_dev->streaming) + return set_gamma(gspca_dev); + return 0; +} + +static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->gamma; + return 0; +} + +static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->red = val; + if (gspca_dev->streaming) + return set_redblue(gspca_dev); + return 0; +} + +static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->red; + return 0; +} + +static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->blue = val; + if (gspca_dev->streaming) + return set_redblue(gspca_dev); + return 0; +} + +static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->blue; + return 0; +} + +static int sd_sethflip(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + return set_hvflip(gspca_dev); + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + if (gspca_dev->streaming) + return set_hvflip(gspca_dev); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->vflip; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + return set_exposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->exposure; + return 0; +} + +static int sd_setgain(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + return set_gain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->gain; + return 0; +} + +static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + sd->auto_exposure = val; + return 0; +} + +static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->auto_exposure; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int sd_dbg_g_register(struct gspca_dev *gspca_dev, + struct v4l2_dbg_register *reg) +{ + struct sd *sd = (struct sd *) gspca_dev; + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + if (reg->match.addr != 0) + return -EINVAL; + if (reg->reg < 0x1000 || reg->reg > 0x11ff) + return -EINVAL; + if (reg_r(gspca_dev, reg->reg, 1) < 0) + return -EINVAL; + reg->val = gspca_dev->usb_buf[0]; + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + if (reg->match.addr != sd->i2c_addr) + return -EINVAL; + if (sd->sensor >= SENSOR_MT9V011 && + sd->sensor <= SENSOR_MT9M111) { + if (i2c_r2(gspca_dev, reg->reg, (u16 *)®->val) < 0) + return -EINVAL; + } else { + if (i2c_r1(gspca_dev, reg->reg, (u8 *)®->val) < 0) + return -EINVAL; + } + return 0; + } + return -EINVAL; +} + +static int sd_dbg_s_register(struct gspca_dev *gspca_dev, + struct v4l2_dbg_register *reg) +{ + struct sd *sd = (struct sd *) gspca_dev; + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + if (reg->match.addr != 0) + return -EINVAL; + if (reg->reg < 0x1000 || reg->reg > 0x11ff) + return -EINVAL; + if (reg_w1(gspca_dev, reg->reg, reg->val) < 0) + return -EINVAL; + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + if (reg->match.addr != sd->i2c_addr) + return -EINVAL; + if (sd->sensor >= SENSOR_MT9V011 && + sd->sensor <= SENSOR_MT9M111) { + if (i2c_w2(gspca_dev, reg->reg, reg->val) < 0) + return -EINVAL; + } else { + if (i2c_w1(gspca_dev, reg->reg, reg->val) < 0) + return -EINVAL; + } + return 0; + } + return -EINVAL; +} +#endif + +static int sd_chip_ident(struct gspca_dev *gspca_dev, + struct v4l2_dbg_chip_ident *chip) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (chip->match.type) { + case V4L2_CHIP_MATCH_HOST: + if (chip->match.addr != 0) + return -EINVAL; + chip->revision = 0; + chip->ident = V4L2_IDENT_SN9C20X; + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + if (chip->match.addr != sd->i2c_addr) + return -EINVAL; + chip->revision = 0; + chip->ident = i2c_ident[sd->sensor]; + return 0; + } + return -EINVAL; +} + +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + + sd->sensor = (id->driver_info >> 8) & 0xff; + sd->i2c_addr = id->driver_info & 0xff; + + switch (sd->sensor) { + case SENSOR_OV9650: + cam->cam_mode = sxga_mode; + cam->nmodes = ARRAY_SIZE(sxga_mode); + break; + default: + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + } + + sd->old_step = 0; + sd->older_step = 0; + sd->exposure_step = 16; + + sd->brightness = BRIGHTNESS_DEFAULT; + sd->contrast = CONTRAST_DEFAULT; + sd->saturation = SATURATION_DEFAULT; + sd->hue = HUE_DEFAULT; + sd->gamma = GAMMA_DEFAULT; + sd->red = RED_DEFAULT; + sd->blue = BLUE_DEFAULT; + + sd->hflip = HFLIP_DEFAULT; + sd->vflip = VFLIP_DEFAULT; + sd->exposure = EXPOSURE_DEFAULT; + sd->gain = GAIN_DEFAULT; + sd->auto_exposure = AUTO_EXPOSURE_DEFAULT; + + sd->quality = 95; + +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV + sd->input_gpio = (id->driver_info >> 16) & 0xff; + if (sn9c20x_input_init(gspca_dev) < 0) + return -ENODEV; +#endif + return 0; +} + +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + u8 value; + u8 i2c_init[9] = + {0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}; + + for (i = 0; i < ARRAY_SIZE(bridge_init); i++) { + value = bridge_init[i][1]; + if (reg_w(gspca_dev, bridge_init[i][0], &value, 1) < 0) { + err("Device initialization failed"); + return -ENODEV; + } + } + + if (reg_w(gspca_dev, 0x10c0, i2c_init, 9) < 0) { + err("Device initialization failed"); + return -ENODEV; + } + + switch (sd->sensor) { + case SENSOR_OV9650: + if (ov9650_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("OV9650 sensor detected"); + break; + case SENSOR_OV9655: + if (ov9655_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("OV9655 sensor detected"); + break; + case SENSOR_SOI968: + if (soi968_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("SOI968 sensor detected"); + break; + case SENSOR_OV7660: + if (ov7660_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("OV7660 sensor detected"); + break; + case SENSOR_OV7670: + if (ov7670_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("OV7670 sensor detected"); + break; + case SENSOR_MT9VPRB: + if (mt9v_init_sensor(gspca_dev) < 0) + return -ENODEV; + break; + case SENSOR_MT9M111: + if (mt9m111_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("MT9M111 sensor detected"); + break; + case SENSOR_MT9M001: + if (mt9m001_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("MT9M001 sensor detected"); + break; + case SENSOR_HV7131R: + if (hv7131r_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("HV7131R sensor detected"); + break; + default: + info("Unsupported Sensor"); + return -ENODEV; + } + + return 0; +} + +static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 value; + switch (sd->sensor) { + case SENSOR_OV9650: + if (mode & MODE_SXGA) { + i2c_w1(gspca_dev, 0x17, 0x1b); + i2c_w1(gspca_dev, 0x18, 0xbc); + i2c_w1(gspca_dev, 0x19, 0x01); + i2c_w1(gspca_dev, 0x1a, 0x82); + i2c_r1(gspca_dev, 0x12, &value); + i2c_w1(gspca_dev, 0x12, value & 0x07); + } else { + i2c_w1(gspca_dev, 0x17, 0x24); + i2c_w1(gspca_dev, 0x18, 0xc5); + i2c_w1(gspca_dev, 0x19, 0x00); + i2c_w1(gspca_dev, 0x1a, 0x3c); + i2c_r1(gspca_dev, 0x12, &value); + i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40); + } + break; + } +} + +#define HW_WIN(mode, hstart, vstart) \ +((const u8 []){hstart & 0xff, hstart >> 8, \ +vstart & 0xff, vstart >> 8, \ +(mode & MODE_SXGA ? 1280 >> 4 : 640 >> 4), \ +(mode & MODE_SXGA ? 1024 >> 3 : 480 >> 3)}) + +#define CLR_WIN(width, height) \ +((const u8 [])\ +{0, width >> 2, 0, height >> 1,\ +((width >> 10) & 0x01) | ((height >> 8) & 0x6)}) + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + int width = gspca_dev->width; + int height = gspca_dev->height; + u8 fmt, scale = 0; + + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (sd->jpeg_hdr == NULL) + return -ENOMEM; + + jpeg_define(sd->jpeg_hdr, height, width, + 0x21); + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + + if (mode & MODE_RAW) + fmt = 0x2d; + else if (mode & MODE_JPEG) + fmt = 0x2c; + else + fmt = 0x2f; + + switch (mode & 0x0f) { + case 3: + scale = 0xc0; + info("Set 1280x1024"); + break; + case 2: + scale = 0x80; + info("Set 640x480"); + break; + case 1: + scale = 0x90; + info("Set 320x240"); + break; + case 0: + scale = 0xa0; + info("Set 160x120"); + break; + } + + configure_sensor_output(gspca_dev, mode); + reg_w(gspca_dev, 0x1100, sd->jpeg_hdr + JPEG_QT0_OFFSET, 64); + reg_w(gspca_dev, 0x1140, sd->jpeg_hdr + JPEG_QT1_OFFSET, 64); + reg_w(gspca_dev, 0x10fb, CLR_WIN(width, height), 5); + reg_w(gspca_dev, 0x1180, HW_WIN(mode, sd->hstart, sd->vstart), 6); + reg_w1(gspca_dev, 0x1189, scale); + reg_w1(gspca_dev, 0x10e0, fmt); + + set_cmatrix(gspca_dev); + set_gamma(gspca_dev); + set_redblue(gspca_dev); + set_gain(gspca_dev); + set_exposure(gspca_dev); + set_hvflip(gspca_dev); + + reg_r(gspca_dev, 0x1061, 1); + reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02); + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_r(gspca_dev, 0x1061, 1); + reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + kfree(sd->jpeg_hdr); +} + +static void do_autoexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum, new_exp; + + if (!sd->auto_exposure) + return; + + avg_lum = atomic_read(&sd->avg_lum); + + /* + * some hardcoded values are present + * like those for maximal/minimal exposure + * and exposure steps + */ + if (avg_lum < MIN_AVG_LUM) { + if (sd->exposure > 0x1770) + return; + + new_exp = sd->exposure + sd->exposure_step; + if (new_exp > 0x1770) + new_exp = 0x1770; + if (new_exp < 0x10) + new_exp = 0x10; + sd->exposure = new_exp; + set_exposure(gspca_dev); + + sd->older_step = sd->old_step; + sd->old_step = 1; + + if (sd->old_step ^ sd->older_step) + sd->exposure_step /= 2; + else + sd->exposure_step += 2; + } + if (avg_lum > MAX_AVG_LUM) { + if (sd->exposure < 0x10) + return; + new_exp = sd->exposure - sd->exposure_step; + if (new_exp > 0x1700) + new_exp = 0x1770; + if (new_exp < 0x10) + new_exp = 0x10; + sd->exposure = new_exp; + set_exposure(gspca_dev); + sd->older_step = sd->old_step; + sd->old_step = 0; + + if (sd->old_step ^ sd->older_step) + sd->exposure_step /= 2; + else + sd->exposure_step += 2; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum; + static unsigned char frame_header[] = + {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; + if (len == 64 && memcmp(data, frame_header, 6) == 0) { + avg_lum = ((data[35] >> 2) & 3) | + (data[20] << 2) | + (data[19] << 10); + avg_lum += ((data[35] >> 4) & 3) | + (data[22] << 2) | + (data[21] << 10); + avg_lum += ((data[35] >> 6) & 3) | + (data[24] << 2) | + (data[23] << 10); + avg_lum += (data[36] & 3) | + (data[26] << 2) | + (data[25] << 10); + avg_lum += ((data[36] >> 2) & 3) | + (data[28] << 2) | + (data[27] << 10); + avg_lum += ((data[36] >> 4) & 3) | + (data[30] << 2) | + (data[29] << 10); + avg_lum += ((data[36] >> 6) & 3) | + (data[32] << 2) | + (data[31] << 10); + avg_lum += ((data[44] >> 4) & 3) | + (data[34] << 2) | + (data[33] << 10); + avg_lum >>= 9; + atomic_set(&sd->avg_lum, avg_lum); + gspca_frame_add(gspca_dev, LAST_PACKET, + frame, data, len); + return; + } + if (gspca_dev->last_packet_type == LAST_PACKET) { + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv + & MODE_JPEG) { + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, len); + } else { + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + } + } else { + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + } +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = do_autoexposure, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .set_register = sd_dbg_s_register, + .get_register = sd_dbg_g_register, +#endif + .get_chip_ident = sd_chip_ident, +}; + +#define SN9C20X(sensor, i2c_addr, button_mask) \ + .driver_info = (button_mask << 16) \ + | (SENSOR_ ## sensor << 8) \ + | (i2c_addr) + +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0c45, 0x6240), SN9C20X(MT9M001, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6242), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, 0x10)}, + {USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6251), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6253), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6260), SN9C20X(OV7670, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x6270), SN9C20X(MT9VPRB, 0x00, 0)}, + {USB_DEVICE(0x0c45, 0x627b), SN9C20X(OV7660, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x627c), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0x0c45, 0x627f), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6280), SN9C20X(MT9M001, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6282), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6288), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x628e), SN9C20X(SOI968, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)}, + {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x62bc), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x145f, 0x013d), SN9C20X(OV7660, 0x21, 0)}, + {USB_DEVICE(0x0458, 0x7029), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0610), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0611), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0613), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0618), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0614), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0xa168, 0x0615), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0xa168, 0x0617), SN9C20X(MT9M111, 0x5d, 0)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static void sd_disconnect(struct usb_interface *intf) +{ +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + + sn9c20x_input_cleanup(gspca_dev); +#endif + + gspca_disconnect(intf); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = sd_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + info("registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + info("deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 0d02f41fa7d0..d6332ab80669 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1634,6 +1634,8 @@ static void setfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (gspca_dev->ctrl_dis & (1 << FREQ_IDX)) + return; if (sd->sensor == SENSOR_OV7660) { switch (sd->freq) { case 0: /* Banding filter disabled */ @@ -1735,6 +1737,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index 8806b2ff82be..fab7ef85a6c1 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -670,6 +670,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index f25be20cf1a6..47628964801e 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -333,6 +333,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c index 3039ec208f3a..e5024c8496ef 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c @@ -64,7 +64,7 @@ static struct v4l2_pix_format hdcs1x00_mode[] = { { HDCS_1X00_DEF_WIDTH, HDCS_1X00_DEF_HEIGHT, - V4L2_PIX_FMT_SBGGR8, + V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, .sizeimage = HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT, @@ -80,7 +80,7 @@ static struct v4l2_pix_format hdcs1020_mode[] = { { HDCS_1020_DEF_WIDTH, HDCS_1020_DEF_HEIGHT, - V4L2_PIX_FMT_SBGGR8, + V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, .sizeimage = HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT, @@ -131,9 +131,11 @@ static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len) (reg + len > 0xff))) return -EINVAL; - for (i = 0; i < len; i++, reg++) { - regs[2*i] = reg; - regs[2*i+1] = vals[i]; + for (i = 0; i < len; i++) { + regs[2 * i] = reg; + regs[2 * i + 1] = vals[i]; + /* All addresses are shifted left one bit as bit 0 toggles r/w */ + reg += 2; } return stv06xx_write_sensor_bytes(sd, regs, len); @@ -174,7 +176,9 @@ static int hdcs_set_state(struct sd *sd, enum hdcs_power_state state) } ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), val); - if (ret < 0) + + /* Update the state if the write succeeded */ + if (!ret) hdcs->state = state; return ret; diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 9623f294bdac..5127bbf9dd26 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -973,6 +973,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 08422d315e68..3d2756f7874a 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -7243,6 +7243,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c index 1fe8fc9183a7..b2260de645f0 100644 --- a/drivers/media/video/mt9v011.c +++ b/drivers/media/video/mt9v011.c @@ -8,6 +8,7 @@ #include <linux/i2c.h> #include <linux/videodev2.h> #include <linux/delay.h> +#include <asm/div64.h> #include <media/v4l2-device.h> #include "mt9v011.h" #include <media/v4l2-i2c-drv.h> @@ -57,6 +58,7 @@ static struct v4l2_queryctrl mt9v011_qctrl[] = { struct mt9v011 { struct v4l2_subdev sd; unsigned width, height; + unsigned xtal; u16 global_gain, red_bal, blue_bal; }; @@ -131,7 +133,7 @@ static const struct i2c_reg_value mt9v011_init_default[] = { { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, { R20_MT9V011_READ_MODE, 0x1000 }, - { R07_MT9V011_OUT_CTRL, 0x000a }, /* chip enable */ + { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ }; static void set_balance(struct v4l2_subdev *sd) @@ -154,6 +156,31 @@ static void set_balance(struct v4l2_subdev *sd) mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); } +static void calc_fps(struct v4l2_subdev *sd) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned height, width, hblank, vblank, speed; + unsigned row_time, t_time; + u64 frames_per_ms; + unsigned tmp; + + height = mt9v011_read(sd, R03_MT9V011_HEIGHT); + width = mt9v011_read(sd, R04_MT9V011_WIDTH); + hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); + vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); + speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED); + + row_time = (width + 113 + hblank) * (speed + 2); + t_time = row_time * (height + vblank + 1); + + frames_per_ms = core->xtal * 1000l; + do_div(frames_per_ms, t_time); + tmp = frames_per_ms; + + v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", + tmp / 1000, tmp % 1000, t_time); +} + static void set_res(struct v4l2_subdev *sd) { struct mt9v011 *core = to_mt9v011(sd); @@ -175,10 +202,12 @@ static void set_res(struct v4l2_subdev *sd) mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); - vstart = 8 + (640 - core->height) / 2; + vstart = 8 + (480 - core->height) / 2; mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); + + calc_fps(sd); }; static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) @@ -215,6 +244,23 @@ static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return -EINVAL; } +static int mt9v011_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + int i; + + v4l2_dbg(1, debug, sd, "queryctrl called\n"); + + for (i = 0; i < ARRAY_SIZE(mt9v011_qctrl); i++) + if (qc->id && qc->id == mt9v011_qctrl[i].id) { + memcpy(qc, &(mt9v011_qctrl[i]), + sizeof(*qc)); + return 0; + } + + return -EINVAL; +} + + static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct mt9v011 *core = to_mt9v011(sd); @@ -294,6 +340,22 @@ static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } +static int mt9v011_s_config(struct v4l2_subdev *sd, int dumb, void *data) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned *xtal = data; + + v4l2_dbg(1, debug, sd, "s_config called\n"); + + if (xtal) { + core->xtal = *xtal; + v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", + *xtal / 1000000, (*xtal / 1000) % 1000); + } + + return 0; +} + #ifdef CONFIG_VIDEO_ADV_DEBUG static int mt9v011_g_register(struct v4l2_subdev *sd, @@ -338,9 +400,11 @@ static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, } static const struct v4l2_subdev_core_ops mt9v011_core_ops = { + .queryctrl = mt9v011_queryctrl, .g_ctrl = mt9v011_g_ctrl, .s_ctrl = mt9v011_s_ctrl, .reset = mt9v011_reset, + .s_config = mt9v011_s_config, .g_chip_ident = mt9v011_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9v011_g_register, @@ -395,6 +459,7 @@ static int mt9v011_probe(struct i2c_client *c, core->global_gain = 0x0024; core->width = 640; core->height = 480; + core->xtal = 27000000; /* Hz */ v4l_info(c, "chip found @ 0x%02x (%s)\n", c->addr << 1, c->adapter->name); diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index db25c3034c11..8d17cf613306 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -62,6 +62,7 @@ #include <linux/module.h> #include <linux/poll.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #ifdef CONFIG_USB_PWC_INPUT_EVDEV #include <linux/usb/input.h> #endif diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 0be6f814f539..0b658dee05a4 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -29,7 +29,6 @@ #include <linux/usb.h> #include <linux/spinlock.h> #include <linux/wait.h> -#include <linux/smp_lock.h> #include <linux/version.h> #include <linux/mutex.h> #include <linux/mm.h> diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 6be845ccc7d7..9e3262c0ba37 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -48,6 +48,7 @@ #include <linux/videodev2.h> #include <linux/version.h> #include <linux/mm.h> +#include <linux/smp_lock.h> #include <media/videobuf-vmalloc.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c index 155804b061e9..b624a4c01fdc 100644 --- a/drivers/media/video/saa5246a.c +++ b/drivers/media/video/saa5246a.c @@ -43,7 +43,6 @@ #include <linux/mm.h> #include <linux/init.h> #include <linux/i2c.h> -#include <linux/smp_lock.h> #include <linux/mutex.h> #include <linux/videotext.h> #include <linux/videodev2.h> diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c index 271d6e931b75..12835fb82c95 100644 --- a/drivers/media/video/saa5249.c +++ b/drivers/media/video/saa5249.c @@ -46,7 +46,6 @@ #include <linux/mm.h> #include <linux/init.h> #include <linux/i2c.h> -#include <linux/smp_lock.h> #include <linux/mutex.h> #include <linux/delay.h> #include <linux/videotext.h> diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index add1757f8930..296788c3bf0e 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -22,6 +22,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/delay.h> #include "saa7134-reg.h" diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c index c8f05297d0f0..85ffc2cba039 100644 --- a/drivers/media/video/se401.c +++ b/drivers/media/video/se401.c @@ -31,6 +31,7 @@ static const char version[] = "0.24"; #include <linux/init.h> #include <linux/vmalloc.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/pagemap.h> #include <linux/usb.h> #include "se401.h" diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index 2e5937047278..4d6785e63455 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -27,6 +27,7 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/usb.h> #include <linux/mm.h> diff --git a/drivers/media/video/stradis.c b/drivers/media/video/stradis.c index 0eb313082c97..eaada39c76fd 100644 --- a/drivers/media/video/stradis.c +++ b/drivers/media/video/stradis.c @@ -26,6 +26,7 @@ #include <linux/kernel.h> #include <linux/major.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/mm.h> #include <linux/init.h> #include <linux/poll.h> diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c index 75f286f7a2e9..8b4e7dafce7b 100644 --- a/drivers/media/video/stv680.c +++ b/drivers/media/video/stv680.c @@ -62,6 +62,7 @@ #include <linux/init.h> #include <linux/vmalloc.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/pagemap.h> #include <linux/errno.h> #include <linux/videodev.h> diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c index 8d73979596f9..45fce39ec9ad 100644 --- a/drivers/media/video/usbvideo/vicam.c +++ b/drivers/media/video/usbvideo/vicam.c @@ -43,6 +43,7 @@ #include <linux/vmalloc.h> #include <linux/mm.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/mutex.h> #include <linux/firmware.h> #include <linux/ihex.h> diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 90b58914f984..90d9b5c0e9a7 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -50,6 +50,7 @@ #include <linux/list.h> #include <linux/timer.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/mm.h> #include <linux/utsname.h> #include <linux/highmem.h> diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 31eac66411d7..a7f1b69a7dab 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -25,7 +25,6 @@ #include <linux/init.h> #include <linux/kmod.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/system.h> diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 3d7df32a3d87..bcdefb1bcb3d 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -49,6 +49,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/pci.h> #include <linux/vmalloc.h> #include <linux/wait.h> diff --git a/drivers/mfd/dm355evm_msp.c b/drivers/mfd/dm355evm_msp.c index 7ac12cb0be4a..5b6e58a3ba46 100644 --- a/drivers/mfd/dm355evm_msp.c +++ b/drivers/mfd/dm355evm_msp.c @@ -32,8 +32,7 @@ * This driver was tested with firmware revision A4. */ -#if defined(CONFIG_KEYBOARD_DM355EVM) \ - || defined(CONFIG_KEYBOARD_DM355EVM_MODULE) +#if defined(CONFIG_INPUT_DM355EVM) || defined(CONFIG_INPUT_DM355EVM_MODULE) #define msp_has_keyboard() true #else #define msp_has_keyboard() false diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c index fa2d93a9fb8d..aed609832bc2 100644 --- a/drivers/misc/sgi-gru/grufile.c +++ b/drivers/misc/sgi-gru/grufile.c @@ -29,7 +29,6 @@ #include <linux/slab.h> #include <linux/mm.h> #include <linux/io.h> -#include <linux/smp_lock.h> #include <linux/spinlock.h> #include <linux/device.h> #include <linux/miscdevice.h> diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c index eedbf9c32760..79689b10f937 100644 --- a/drivers/misc/sgi-gru/grukservices.c +++ b/drivers/misc/sgi-gru/grukservices.c @@ -24,7 +24,6 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/mm.h> -#include <linux/smp_lock.h> #include <linux/spinlock.h> #include <linux/device.h> #include <linux/miscdevice.h> diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index 8d1c60a3f0df..5d778ec8cdb2 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c @@ -235,7 +235,7 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg) skb->ip_summed = CHECKSUM_UNNECESSARY; dev_dbg(xpnet, "passing skb to network layer\n" - KERN_DEBUG "\tskb->head=0x%p skb->data=0x%p skb->tail=0x%p " + "\tskb->head=0x%p skb->data=0x%p skb->tail=0x%p " "skb->end=0x%p skb->len=%d\n", (void *)skb->head, (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), skb->len); @@ -399,7 +399,7 @@ xpnet_send(struct sk_buff *skb, struct xpnet_pending_msg *queued_msg, msg->buf_pa = xp_pa((void *)start_addr); dev_dbg(xpnet, "sending XPC message to %d:%d\n" - KERN_DEBUG "msg->buf_pa=0x%lx, msg->size=%u, " + "msg->buf_pa=0x%lx, msg->size=%u, " "msg->leadin_ignore=%u, msg->tailout_ignore=%u\n", dest_partid, XPC_NET_CHANNEL, msg->buf_pa, msg->size, msg->leadin_ignore, msg->tailout_ignore); diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index b56d72ff06e9..34e23489811a 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -384,7 +384,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev) u16 val[2] = {0, 0}; val[0] = mvsd_read(MVSD_FIFO); val[1] = mvsd_read(MVSD_FIFO); - memcpy(p, &val, s); + memcpy(p, ((void *)&val) + 4 - s, s); s = 0; intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); } @@ -423,7 +423,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev) if (s < 4) { if (s && (intr_status & MVSD_NOR_TX_AVAIL)) { u16 val[2] = {0, 0}; - memcpy(&val, p, s); + memcpy(((void *)&val) + 4 - s, p, s); mvsd_write(MVSD_FIFO, val[0]); mvsd_write(MVSD_FIFO, val[1]); s = 0; diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index d7d7109ef47e..e55ac792d68c 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -168,12 +168,12 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) if (data->flags & MMC_DATA_READ) { host->dma_dir = DMA_FROM_DEVICE; - dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG; + dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC; DRCMR(host->dma_drcmrtx) = 0; DRCMR(host->dma_drcmrrx) = host->dma | DRCMR_MAPVLD; } else { host->dma_dir = DMA_TO_DEVICE; - dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC; + dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; DRCMR(host->dma_drcmrrx) = 0; DRCMR(host->dma_drcmrtx) = host->dma | DRCMR_MAPVLD; } diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 286ed594e5a0..e1f7d0a78b9d 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -657,6 +657,11 @@ static int io_init(struct ubi_device *ubi) if (ubi->mtd->block_isbad && ubi->mtd->block_markbad) ubi->bad_allowed = 1; + if (ubi->mtd->type == MTD_NORFLASH) { + ubi_assert(ubi->mtd->writesize == 1); + ubi->nor_flash = 1; + } + ubi->min_io_size = ubi->mtd->writesize; ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft; @@ -996,6 +1001,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ubi_msg("number of PEBs reserved for bad PEB handling: %d", ubi->beb_rsvd_pebs); ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec); + ubi_msg("image sequence number: %d", ubi->image_seq); /* * The below lock makes sure we do not race with 'ubi_thread()' which diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c index c0ed60e8ade9..54b0186915fb 100644 --- a/drivers/mtd/ubi/debug.c +++ b/drivers/mtd/ubi/debug.c @@ -44,6 +44,8 @@ void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr) be32_to_cpu(ec_hdr->vid_hdr_offset)); printk(KERN_DEBUG "\tdata_offset %d\n", be32_to_cpu(ec_hdr->data_offset)); + printk(KERN_DEBUG "\timage_seq %d\n", + be32_to_cpu(ec_hdr->image_seq)); printk(KERN_DEBUG "\thdr_crc %#08x\n", be32_to_cpu(ec_hdr->hdr_crc)); printk(KERN_DEBUG "erase counter header hexdump:\n"); diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index 13777e5beac9..a4da7a09b949 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -93,6 +93,12 @@ void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req); #define UBI_IO_DEBUG 0 #endif +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len); +#else +#define ubi_dbg_check_all_ff(ubi, pnum, offset, len) 0 +#endif + #ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT #define DBG_DISABLE_BGT 1 #else @@ -167,6 +173,7 @@ static inline int ubi_dbg_is_erase_failure(void) #define ubi_dbg_is_bitflip() 0 #define ubi_dbg_is_write_failure() 0 #define ubi_dbg_is_erase_failure() 0 +#define ubi_dbg_check_all_ff(ubi, pnum, offset, len) 0 #endif /* !CONFIG_MTD_UBI_DEBUG */ #endif /* !__UBI_DEBUG_H__ */ diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c index 95aaac03f938..b5e478fa2661 100644 --- a/drivers/mtd/ubi/gluebi.c +++ b/drivers/mtd/ubi/gluebi.c @@ -332,6 +332,7 @@ static int gluebi_create(struct ubi_device_info *di, } gluebi->vol_id = vi->vol_id; + gluebi->ubi_num = vi->ubi_num; mtd->type = MTD_UBIVOLUME; if (!di->ro_mode) mtd->flags = MTD_WRITEABLE; diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index effaff28bab1..4cb69925d8d9 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -98,17 +98,12 @@ static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum); static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, const struct ubi_vid_hdr *vid_hdr); -static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, - int len); -static int paranoid_check_empty(struct ubi_device *ubi, int pnum); #else #define paranoid_check_not_bad(ubi, pnum) 0 #define paranoid_check_peb_ec_hdr(ubi, pnum) 0 #define paranoid_check_ec_hdr(ubi, pnum, ec_hdr) 0 #define paranoid_check_peb_vid_hdr(ubi, pnum) 0 #define paranoid_check_vid_hdr(ubi, pnum, vid_hdr) 0 -#define paranoid_check_all_ff(ubi, pnum, offset, len) 0 -#define paranoid_check_empty(ubi, pnum) 0 #endif /** @@ -244,7 +239,7 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, return err > 0 ? -EINVAL : err; /* The area we are writing to has to contain all 0xFF bytes */ - err = paranoid_check_all_ff(ubi, pnum, offset, len); + err = ubi_dbg_check_all_ff(ubi, pnum, offset, len); if (err) return err > 0 ? -EINVAL : err; @@ -271,8 +266,8 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, addr = (loff_t)pnum * ubi->peb_size + offset; err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf); if (err) { - ubi_err("error %d while writing %d bytes to PEB %d:%d, written" - " %zd bytes", err, len, pnum, offset, written); + ubi_err("error %d while writing %d bytes to PEB %d:%d, written " + "%zd bytes", err, len, pnum, offset, written); ubi_dbg_dump_stack(); } else ubi_assert(written == len); @@ -350,7 +345,7 @@ retry: return -EIO; } - err = paranoid_check_all_ff(ubi, pnum, 0, ubi->peb_size); + err = ubi_dbg_check_all_ff(ubi, pnum, 0, ubi->peb_size); if (err) return err > 0 ? -EINVAL : err; @@ -459,6 +454,54 @@ out: } /** + * nor_erase_prepare - prepare a NOR flash PEB for erasure. + * @ubi: UBI device description object + * @pnum: physical eraseblock number to prepare + * + * NOR flash, or at least some of them, have peculiar embedded PEB erasure + * algorithm: the PEB is first filled with zeroes, then it is erased. And + * filling with zeroes starts from the end of the PEB. This was observed with + * Spansion S29GL512N NOR flash. + * + * This means that in case of a power cut we may end up with intact data at the + * beginning of the PEB, and all zeroes at the end of PEB. In other words, the + * EC and VID headers are OK, but a large chunk of data at the end of PEB is + * zeroed. This makes UBI mistakenly treat this PEB as used and associate it + * with an LEB, which leads to subsequent failures (e.g., UBIFS fails). + * + * This function is called before erasing NOR PEBs and it zeroes out EC and VID + * magic numbers in order to invalidate them and prevent the failures. Returns + * zero in case of success and a negative error code in case of failure. + */ +static int nor_erase_prepare(struct ubi_device *ubi, int pnum) +{ + int err; + size_t written; + loff_t addr; + uint32_t data = 0; + + addr = (loff_t)pnum * ubi->peb_size; + err = ubi->mtd->write(ubi->mtd, addr, 4, &written, (void *)&data); + if (err) { + ubi_err("error %d while writing 4 bytes to PEB %d:%d, written " + "%zd bytes", err, pnum, 0, written); + ubi_dbg_dump_stack(); + return err; + } + + addr += ubi->vid_hdr_aloffset; + err = ubi->mtd->write(ubi->mtd, addr, 4, &written, (void *)&data); + if (err) { + ubi_err("error %d while writing 4 bytes to PEB %d:%d, written " + "%zd bytes", err, pnum, ubi->vid_hdr_aloffset, written); + ubi_dbg_dump_stack(); + return err; + } + + return 0; +} + +/** * ubi_io_sync_erase - synchronously erase a physical eraseblock. * @ubi: UBI device description object * @pnum: physical eraseblock number to erase @@ -489,6 +532,12 @@ int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture) return -EROFS; } + if (ubi->nor_flash) { + err = nor_erase_prepare(ubi, pnum); + if (err) + return err; + } + if (torture) { ret = torture_peb(ubi, pnum); if (ret < 0) @@ -672,11 +721,6 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, if (read_err != -EBADMSG && check_pattern(ec_hdr, 0xFF, UBI_EC_HDR_SIZE)) { /* The physical eraseblock is supposedly empty */ - err = paranoid_check_all_ff(ubi, pnum, 0, - ubi->peb_size); - if (err) - return err > 0 ? UBI_IO_BAD_EC_HDR : err; - if (verbose) ubi_warn("no EC header found at PEB %d, " "only 0xFF bytes", pnum); @@ -752,6 +796,7 @@ int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum, ec_hdr->version = UBI_VERSION; ec_hdr->vid_hdr_offset = cpu_to_be32(ubi->vid_hdr_offset); ec_hdr->data_offset = cpu_to_be32(ubi->leb_start); + ec_hdr->image_seq = cpu_to_be32(ubi->image_seq); crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); ec_hdr->hdr_crc = cpu_to_be32(crc); @@ -947,15 +992,6 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, if (read_err != -EBADMSG && check_pattern(vid_hdr, 0xFF, UBI_VID_HDR_SIZE)) { /* The physical eraseblock is supposedly free */ - - /* - * The below is just a paranoid check, it has to be - * compiled out if paranoid checks are disabled. - */ - err = paranoid_check_empty(ubi, pnum); - if (err) - return err > 0 ? UBI_IO_BAD_VID_HDR : err; - if (verbose) ubi_warn("no VID header found at PEB %d, " "only 0xFF bytes", pnum); @@ -1229,7 +1265,7 @@ exit: } /** - * paranoid_check_all_ff - check that a region of flash is empty. + * ubi_dbg_check_all_ff - check that a region of flash is empty. * @ubi: UBI device description object * @pnum: the physical eraseblock number to check * @offset: the starting offset within the physical eraseblock to check @@ -1239,8 +1275,7 @@ exit: * @offset of the physical eraseblock @pnum, %1 if not, and a negative error * code if an error occurred. */ -static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, - int len) +int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len) { size_t read; int err; @@ -1276,74 +1311,4 @@ error: return err; } -/** - * paranoid_check_empty - whether a PEB is empty. - * @ubi: UBI device description object - * @pnum: the physical eraseblock number to check - * - * This function makes sure PEB @pnum is empty, which means it contains only - * %0xFF data bytes. Returns zero if the PEB is empty, %1 if not, and a - * negative error code in case of failure. - * - * Empty PEBs have the EC header, and do not have the VID header. The caller of - * this function should have already made sure the PEB does not have the VID - * header. However, this function re-checks that, because it is possible that - * the header and data has already been written to the PEB. - * - * Let's consider a possible scenario. Suppose there are 2 tasks - A and B. - * Task A is in 'wear_leveling_worker()'. It is reading VID header of PEB X to - * find which LEB it corresponds to. PEB X is currently unmapped, and has no - * VID header. Task B is trying to write to PEB X. - * - * Task A: in 'ubi_io_read_vid_hdr()': reads the VID header from PEB X. The - * read data contain all 0xFF bytes; - * Task B: writes VID header and some data to PEB X; - * Task A: assumes PEB X is empty, calls 'paranoid_check_empty()'. And if we - * do not re-read the VID header, and do not cancel the checking if it - * is there, we fail. - */ -static int paranoid_check_empty(struct ubi_device *ubi, int pnum) -{ - int err, offs = ubi->vid_hdr_aloffset, len = ubi->vid_hdr_alsize; - size_t read; - uint32_t magic; - const struct ubi_vid_hdr *vid_hdr; - - mutex_lock(&ubi->dbg_buf_mutex); - err = ubi->mtd->read(ubi->mtd, offs, len, &read, ubi->dbg_peb_buf); - if (err && err != -EUCLEAN) { - ubi_err("error %d while reading %d bytes from PEB %d:%d, " - "read %zd bytes", err, len, pnum, offs, read); - goto error; - } - - vid_hdr = ubi->dbg_peb_buf; - magic = be32_to_cpu(vid_hdr->magic); - if (magic == UBI_VID_HDR_MAGIC) - /* The PEB contains VID header, so it is not empty */ - goto out; - - err = check_pattern(ubi->dbg_peb_buf, 0xFF, len); - if (err == 0) { - ubi_err("flash region at PEB %d:%d, length %d does not " - "contain all 0xFF bytes", pnum, offs, len); - goto fail; - } - -out: - mutex_unlock(&ubi->dbg_buf_mutex); - return 0; - -fail: - ubi_err("paranoid check failed for PEB %d", pnum); - ubi_msg("hex dump of the %d-%d region", offs, offs + len); - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, - ubi->dbg_peb_buf, len, 1); - err = 1; -error: - ubi_dbg_dump_stack(); - mutex_unlock(&ubi->dbg_buf_mutex); - return err; -} - #endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index c3d653ba5ca0..a423131b6171 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -757,6 +757,8 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, si->is_empty = 0; if (!ec_corr) { + int image_seq; + /* Make sure UBI version is OK */ if (ech->version != UBI_VERSION) { ubi_err("this UBI version is %d, image version is %d", @@ -778,6 +780,18 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, ubi_dbg_dump_ec_hdr(ech); return -EINVAL; } + + image_seq = be32_to_cpu(ech->image_seq); + if (!si->image_seq_set) { + ubi->image_seq = image_seq; + si->image_seq_set = 1; + } else if (ubi->image_seq != image_seq) { + ubi_err("bad image sequence number %d in PEB %d, " + "expected %d", image_seq, pnum, ubi->image_seq); + ubi_dbg_dump_ec_hdr(ech); + return -EINVAL; + } + } /* OK, we've done with the EC header, let's look at the VID header */ diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h index 61df208e2f20..1017cf12def5 100644 --- a/drivers/mtd/ubi/scan.h +++ b/drivers/mtd/ubi/scan.h @@ -102,6 +102,7 @@ struct ubi_scan_volume { * @mean_ec: mean erase counter value * @ec_sum: a temporary variable used when calculating @mean_ec * @ec_count: a temporary variable used when calculating @mean_ec + * @image_seq_set: indicates @ubi->image_seq is known * * This data structure contains the result of scanning and may be used by other * UBI sub-systems to build final UBI data structures, further error-recovery @@ -124,6 +125,7 @@ struct ubi_scan_info { int mean_ec; uint64_t ec_sum; int ec_count; + int image_seq_set; }; struct ubi_device; diff --git a/drivers/mtd/ubi/ubi-media.h b/drivers/mtd/ubi/ubi-media.h index 8419fdccc79c..503ea9b27309 100644 --- a/drivers/mtd/ubi/ubi-media.h +++ b/drivers/mtd/ubi/ubi-media.h @@ -129,6 +129,7 @@ enum { * @ec: the erase counter * @vid_hdr_offset: where the VID header starts * @data_offset: where the user data start + * @image_seq: image sequence number * @padding2: reserved for future, zeroes * @hdr_crc: erase counter header CRC checksum * @@ -144,6 +145,14 @@ enum { * volume identifier header and user data, relative to the beginning of the * physical eraseblock. These values have to be the same for all physical * eraseblocks. + * + * The @image_seq field is used to validate a UBI image that has been prepared + * for a UBI device. The @image_seq value can be any value, but it must be the + * same on all eraseblocks. UBI will ensure that all new erase counter headers + * also contain this value, and will check the value when scanning at start-up. + * One way to make use of @image_seq is to increase its value by one every time + * an image is flashed over an existing image, then, if the flashing does not + * complete, UBI will detect the error when scanning. */ struct ubi_ec_hdr { __be32 magic; @@ -152,7 +161,8 @@ struct ubi_ec_hdr { __be64 ec; /* Warning: the current limit is 31-bit anyway! */ __be32 vid_hdr_offset; __be32 data_offset; - __u8 padding2[36]; + __be32 image_seq; + __u8 padding2[32]; __be32 hdr_crc; } __attribute__ ((packed)); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 28acd133c997..6a5fe9633783 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -301,6 +301,7 @@ struct ubi_wl_entry; * @vol->readers, @vol->writers, @vol->exclusive, * @vol->ref_count, @vol->mapping and @vol->eba_tbl. * @ref_count: count of references on the UBI device + * @image_seq: image sequence number recorded on EC headers * * @rsvd_pebs: count of reserved physical eraseblocks * @avail_pebs: count of available physical eraseblocks @@ -372,6 +373,7 @@ struct ubi_wl_entry; * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or * not + * @nor_flash: non-zero if working on top of NOR flash * @mtd: MTD device descriptor * * @peb_buf1: a buffer of PEB size used for different purposes @@ -390,6 +392,7 @@ struct ubi_device { struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT]; spinlock_t volumes_lock; int ref_count; + int image_seq; int rsvd_pebs; int avail_pebs; @@ -452,7 +455,8 @@ struct ubi_device { int vid_hdr_offset; int vid_hdr_aloffset; int vid_hdr_shift; - int bad_allowed; + unsigned int bad_allowed:1; + unsigned int nor_flash:1; struct mtd_info *mtd; void *peb_buf1; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 2b2472300610..600c7229d5cf 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -459,6 +459,14 @@ retry: dbg_wl("PEB %d EC %d", e->pnum, e->ec); prot_queue_add(ubi, e); spin_unlock(&ubi->wl_lock); + + err = ubi_dbg_check_all_ff(ubi, e->pnum, ubi->vid_hdr_aloffset, + ubi->peb_size - ubi->vid_hdr_aloffset); + if (err) { + ubi_err("new PEB %d does not contain all 0xFF bytes", e->pnum); + return err > 0 ? -EINVAL : err; + } + return e->pnum; } diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index 8ae72ec14456..0e2ba21d4441 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -908,6 +908,7 @@ static const struct net_device_ops rtl8139_netdev_ops = { .ndo_open = rtl8139_open, .ndo_stop = rtl8139_close, .ndo_get_stats = rtl8139_get_stats, + .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = rtl8139_set_mac_address, .ndo_start_xmit = rtl8139_start_xmit, diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index c155bd3ec9f1..5f6509a5f640 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1729,6 +1729,13 @@ config KS8842 help This platform driver is for Micrel KSZ8842 chip. +config KS8851 + tristate "Micrel KS8851 SPI" + depends on SPI + select MII + help + SPI driver for Micrel KS8851 SPI attached network chip. + config VIA_RHINE tristate "VIA Rhine support" depends on NET_PCI && PCI diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 4b58a59f211b..ead8cab3cfe1 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_SKGE) += skge.o obj-$(CONFIG_SKY2) += sky2.o obj-$(CONFIG_SKFP) += skfp/ obj-$(CONFIG_KS8842) += ks8842.o +obj-$(CONFIG_KS8851) += ks8851.o obj-$(CONFIG_VIA_RHINE) += via-rhine.o obj-$(CONFIG_VIA_VELOCITY) += via-velocity.o obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o diff --git a/drivers/net/a2065.c b/drivers/net/a2065.c index 85a18175730b..08787f5a22a3 100644 --- a/drivers/net/a2065.c +++ b/drivers/net/a2065.c @@ -569,16 +569,8 @@ static int lance_start_xmit (struct sk_buff *skb, struct net_device *dev) #ifdef DEBUG_DRIVER /* dump the packet */ - { - int i; - - for (i = 0; i < 64; i++) { - if ((i % 16) == 0) - printk("\n" KERN_DEBUG); - printk ("%2.2x ", skb->data [i]); - } - printk("\n"); - } + print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE, + 16, 1, skb->data, 64, true); #endif entry = lp->tx_new & lp->tx_ring_mod_mask; ib->btx_ring [entry].length = (-skblen) | 0xf000; diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index d6d4ab3b430c..7d227cdab9f8 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -158,15 +158,12 @@ module_exit(arcnet_exit); void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc) { - int i; + char hdr[32]; - printk(KERN_DEBUG "%6s: skb dump (%s) follows:", dev->name, desc); - for (i = 0; i < skb->len; i++) { - if (i % 16 == 0) - printk("\n" KERN_DEBUG "[%04X] ", i); - printk("%02X ", ((u_char *) skb->data)[i]); - } - printk("\n"); + /* dump the packet */ + snprintf(hdr, sizeof(hdr), "%6s:%s skb->data:", dev->name, desc); + print_hex_dump(KERN_DEBUG, hdr, DUMP_PREFIX_OFFSET, + 16, 1, skb->data, skb->len, true); } EXPORT_SYMBOL(arcnet_dump_skb); @@ -184,6 +181,7 @@ static void arcnet_dump_packet(struct net_device *dev, int bufnum, int i, length; unsigned long flags = 0; static uint8_t buf[512]; + char hdr[32]; /* hw.copy_from_card expects IRQ context so take the IRQ lock to keep it single threaded */ @@ -197,14 +195,10 @@ static void arcnet_dump_packet(struct net_device *dev, int bufnum, /* if the offset[0] byte is nonzero, this is a 256-byte packet */ length = (buf[2] ? 256 : 512); - printk(KERN_DEBUG "%6s: packet dump (%s) follows:", dev->name, desc); - for (i = 0; i < length; i++) { - if (i % 16 == 0) - printk("\n" KERN_DEBUG "[%04X] ", i); - printk("%02X ", buf[i]); - } - printk("\n"); - + /* dump the packet */ + snprintf(hdr, sizeof(hdr), "%6s:%s packet dump:", dev->name, desc); + print_hex_dump(KERN_DEBUG, hdr, DUMP_PREFIX_OFFSET, + 16, 1, buf, length, true); } #else diff --git a/drivers/net/arm/Kconfig b/drivers/net/arm/Kconfig index 2895db13bfa4..c37ee9e6b67b 100644 --- a/drivers/net/arm/Kconfig +++ b/drivers/net/arm/Kconfig @@ -63,3 +63,11 @@ config IXP4XX_ETH help Say Y here if you want to use built-in Ethernet ports on IXP4xx processor. + +config W90P910_ETH + tristate "Nuvoton w90p910 Ethernet support" + depends on ARM && ARCH_W90X900 + select PHYLIB + help + Say Y here if you want to use built-in Ethernet ports + on w90p910 processor. diff --git a/drivers/net/arm/Makefile b/drivers/net/arm/Makefile index 811a3ccd14c1..303171f589e6 100644 --- a/drivers/net/arm/Makefile +++ b/drivers/net/arm/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o +obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c index 2e7419a61191..5041d10bae9d 100644 --- a/drivers/net/arm/at91_ether.c +++ b/drivers/net/arm/at91_ether.c @@ -1228,7 +1228,6 @@ static int at91ether_resume(struct platform_device *pdev) #endif static struct platform_driver at91ether_driver = { - .probe = at91ether_probe, .remove = __devexit_p(at91ether_remove), .suspend = at91ether_suspend, .resume = at91ether_resume, @@ -1240,7 +1239,7 @@ static struct platform_driver at91ether_driver = { static int __init at91ether_init(void) { - return platform_driver_register(&at91ether_driver); + return platform_driver_probe(&at91ether_driver, at91ether_probe); } static void __exit at91ether_exit(void) diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c index 6f42ad728915..3fe09876e76d 100644 --- a/drivers/net/arm/ixp4xx_eth.c +++ b/drivers/net/arm/ixp4xx_eth.c @@ -1142,7 +1142,9 @@ static const struct net_device_ops ixp4xx_netdev_ops = { .ndo_start_xmit = eth_xmit, .ndo_set_multicast_list = eth_set_mcast_list, .ndo_do_ioctl = eth_ioctl, - + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, }; static int __devinit eth_init_one(struct platform_device *pdev) diff --git a/drivers/net/arm/w90p910_ether.c b/drivers/net/arm/w90p910_ether.c new file mode 100644 index 000000000000..616fb7985a34 --- /dev/null +++ b/drivers/net/arm/w90p910_ether.c @@ -0,0 +1,1105 @@ +/* + * Copyright (c) 2008-2009 Nuvoton technology corporation. + * + * Wan ZongShun <mcuos.com@gmail.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;version 2 of the License. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/mii.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/ethtool.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#define DRV_MODULE_NAME "w90p910-emc" +#define DRV_MODULE_VERSION "0.1" + +/* Ethernet MAC Registers */ +#define REG_CAMCMR 0x00 +#define REG_CAMEN 0x04 +#define REG_CAMM_BASE 0x08 +#define REG_CAML_BASE 0x0c +#define REG_TXDLSA 0x88 +#define REG_RXDLSA 0x8C +#define REG_MCMDR 0x90 +#define REG_MIID 0x94 +#define REG_MIIDA 0x98 +#define REG_FFTCR 0x9C +#define REG_TSDR 0xa0 +#define REG_RSDR 0xa4 +#define REG_DMARFC 0xa8 +#define REG_MIEN 0xac +#define REG_MISTA 0xb0 +#define REG_CTXDSA 0xcc +#define REG_CTXBSA 0xd0 +#define REG_CRXDSA 0xd4 +#define REG_CRXBSA 0xd8 + +/* mac controller bit */ +#define MCMDR_RXON 0x01 +#define MCMDR_ACP (0x01 << 3) +#define MCMDR_SPCRC (0x01 << 5) +#define MCMDR_TXON (0x01 << 8) +#define MCMDR_FDUP (0x01 << 18) +#define MCMDR_ENMDC (0x01 << 19) +#define MCMDR_OPMOD (0x01 << 20) +#define SWR (0x01 << 24) + +/* cam command regiser */ +#define CAMCMR_AUP 0x01 +#define CAMCMR_AMP (0x01 << 1) +#define CAMCMR_ABP (0x01 << 2) +#define CAMCMR_CCAM (0x01 << 3) +#define CAMCMR_ECMP (0x01 << 4) +#define CAM0EN 0x01 + +/* mac mii controller bit */ +#define MDCCR (0x0a << 20) +#define PHYAD (0x01 << 8) +#define PHYWR (0x01 << 16) +#define PHYBUSY (0x01 << 17) +#define PHYPRESP (0x01 << 18) +#define CAM_ENTRY_SIZE 0x08 + +/* rx and tx status */ +#define TXDS_TXCP (0x01 << 19) +#define RXDS_CRCE (0x01 << 17) +#define RXDS_PTLE (0x01 << 19) +#define RXDS_RXGD (0x01 << 20) +#define RXDS_ALIE (0x01 << 21) +#define RXDS_RP (0x01 << 22) + +/* mac interrupt status*/ +#define MISTA_EXDEF (0x01 << 19) +#define MISTA_TXBERR (0x01 << 24) +#define MISTA_TDU (0x01 << 23) +#define MISTA_RDU (0x01 << 10) +#define MISTA_RXBERR (0x01 << 11) + +#define ENSTART 0x01 +#define ENRXINTR 0x01 +#define ENRXGD (0x01 << 4) +#define ENRXBERR (0x01 << 11) +#define ENTXINTR (0x01 << 16) +#define ENTXCP (0x01 << 18) +#define ENTXABT (0x01 << 21) +#define ENTXBERR (0x01 << 24) +#define ENMDC (0x01 << 19) +#define PHYBUSY (0x01 << 17) +#define MDCCR_VAL 0xa00000 + +/* rx and tx owner bit */ +#define RX_OWEN_DMA (0x01 << 31) +#define RX_OWEN_CPU (~(0x03 << 30)) +#define TX_OWEN_DMA (0x01 << 31) +#define TX_OWEN_CPU (~(0x01 << 31)) + +/* tx frame desc controller bit */ +#define MACTXINTEN 0x04 +#define CRCMODE 0x02 +#define PADDINGMODE 0x01 + +/* fftcr controller bit */ +#define TXTHD (0x03 << 8) +#define BLENGTH (0x01 << 20) + +/* global setting for driver */ +#define RX_DESC_SIZE 50 +#define TX_DESC_SIZE 10 +#define MAX_RBUFF_SZ 0x600 +#define MAX_TBUFF_SZ 0x600 +#define TX_TIMEOUT 50 +#define DELAY 1000 +#define CAM0 0x0 + +static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg); + +struct w90p910_rxbd { + unsigned int sl; + unsigned int buffer; + unsigned int reserved; + unsigned int next; +}; + +struct w90p910_txbd { + unsigned int mode; + unsigned int buffer; + unsigned int sl; + unsigned int next; +}; + +struct recv_pdesc { + struct w90p910_rxbd desclist[RX_DESC_SIZE]; + char recv_buf[RX_DESC_SIZE][MAX_RBUFF_SZ]; +}; + +struct tran_pdesc { + struct w90p910_txbd desclist[TX_DESC_SIZE]; + char tran_buf[RX_DESC_SIZE][MAX_TBUFF_SZ]; +}; + +struct w90p910_ether { + struct recv_pdesc *rdesc; + struct recv_pdesc *rdesc_phys; + struct tran_pdesc *tdesc; + struct tran_pdesc *tdesc_phys; + struct net_device_stats stats; + struct platform_device *pdev; + struct sk_buff *skb; + struct clk *clk; + struct clk *rmiiclk; + struct mii_if_info mii; + struct timer_list check_timer; + void __iomem *reg; + unsigned int rxirq; + unsigned int txirq; + unsigned int cur_tx; + unsigned int cur_rx; + unsigned int finish_tx; + unsigned int rx_packets; + unsigned int rx_bytes; + unsigned int start_tx_ptr; + unsigned int start_rx_ptr; + unsigned int linkflag; + spinlock_t lock; +}; + +static void update_linkspeed_register(struct net_device *dev, + unsigned int speed, unsigned int duplex) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = __raw_readl(ether->reg + REG_MCMDR); + + if (speed == SPEED_100) { + /* 100 full/half duplex */ + if (duplex == DUPLEX_FULL) { + val |= (MCMDR_OPMOD | MCMDR_FDUP); + } else { + val |= MCMDR_OPMOD; + val &= ~MCMDR_FDUP; + } + } else { + /* 10 full/half duplex */ + if (duplex == DUPLEX_FULL) { + val |= MCMDR_FDUP; + val &= ~MCMDR_OPMOD; + } else { + val &= ~(MCMDR_FDUP | MCMDR_OPMOD); + } + } + + __raw_writel(val, ether->reg + REG_MCMDR); +} + +static void update_linkspeed(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + struct platform_device *pdev; + unsigned int bmsr, bmcr, lpa, speed, duplex; + + pdev = ether->pdev; + + if (!mii_link_ok(ðer->mii)) { + ether->linkflag = 0x0; + netif_carrier_off(dev); + dev_warn(&pdev->dev, "%s: Link down.\n", dev->name); + return; + } + + if (ether->linkflag == 1) + return; + + bmsr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMSR); + bmcr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMCR); + + if (bmcr & BMCR_ANENABLE) { + if (!(bmsr & BMSR_ANEGCOMPLETE)) + return; + + lpa = w90p910_mdio_read(dev, ether->mii.phy_id, MII_LPA); + + if ((lpa & LPA_100FULL) || (lpa & LPA_100HALF)) + speed = SPEED_100; + else + speed = SPEED_10; + + if ((lpa & LPA_100FULL) || (lpa & LPA_10FULL)) + duplex = DUPLEX_FULL; + else + duplex = DUPLEX_HALF; + + } else { + speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10; + duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; + } + + update_linkspeed_register(dev, speed, duplex); + + dev_info(&pdev->dev, "%s: Link now %i-%s\n", dev->name, speed, + (duplex == DUPLEX_FULL) ? "FullDuplex" : "HalfDuplex"); + ether->linkflag = 0x01; + + netif_carrier_on(dev); +} + +static void w90p910_check_link(unsigned long dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct w90p910_ether *ether = netdev_priv(dev); + + update_linkspeed(dev); + mod_timer(ðer->check_timer, jiffies + msecs_to_jiffies(1000)); +} + +static void w90p910_write_cam(struct net_device *dev, + unsigned int x, unsigned char *pval) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int msw, lsw; + + msw = (pval[0] << 24) | (pval[1] << 16) | (pval[2] << 8) | pval[3]; + + lsw = (pval[4] << 24) | (pval[5] << 16); + + __raw_writel(lsw, ether->reg + REG_CAML_BASE + x * CAM_ENTRY_SIZE); + __raw_writel(msw, ether->reg + REG_CAMM_BASE + x * CAM_ENTRY_SIZE); +} + +static void w90p910_init_desc(struct net_device *dev) +{ + struct w90p910_ether *ether; + struct w90p910_txbd *tdesc, *tdesc_phys; + struct w90p910_rxbd *rdesc, *rdesc_phys; + unsigned int i, j; + + ether = netdev_priv(dev); + + ether->tdesc = (struct tran_pdesc *) + dma_alloc_coherent(NULL, sizeof(struct tran_pdesc), + (dma_addr_t *) ðer->tdesc_phys, GFP_KERNEL); + + ether->rdesc = (struct recv_pdesc *) + dma_alloc_coherent(NULL, sizeof(struct recv_pdesc), + (dma_addr_t *) ðer->rdesc_phys, GFP_KERNEL); + + for (i = 0; i < TX_DESC_SIZE; i++) { + tdesc = &(ether->tdesc->desclist[i]); + + j = ((i + 1) / TX_DESC_SIZE); + + if (j != 0) { + tdesc_phys = &(ether->tdesc_phys->desclist[0]); + ether->start_tx_ptr = (unsigned int)tdesc_phys; + tdesc->next = (unsigned int)ether->start_tx_ptr; + } else { + tdesc_phys = &(ether->tdesc_phys->desclist[i+1]); + tdesc->next = (unsigned int)tdesc_phys; + } + + tdesc->buffer = (unsigned int)ether->tdesc_phys->tran_buf[i]; + tdesc->sl = 0; + tdesc->mode = 0; + } + + for (i = 0; i < RX_DESC_SIZE; i++) { + rdesc = &(ether->rdesc->desclist[i]); + + j = ((i + 1) / RX_DESC_SIZE); + + if (j != 0) { + rdesc_phys = &(ether->rdesc_phys->desclist[0]); + ether->start_rx_ptr = (unsigned int)rdesc_phys; + rdesc->next = (unsigned int)ether->start_rx_ptr; + } else { + rdesc_phys = &(ether->rdesc_phys->desclist[i+1]); + rdesc->next = (unsigned int)rdesc_phys; + } + + rdesc->sl = RX_OWEN_DMA; + rdesc->buffer = (unsigned int)ether->rdesc_phys->recv_buf[i]; + } +} + +static void w90p910_set_fifo_threshold(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = TXTHD | BLENGTH; + __raw_writel(val, ether->reg + REG_FFTCR); +} + +static void w90p910_return_default_idle(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = __raw_readl(ether->reg + REG_MCMDR); + val |= SWR; + __raw_writel(val, ether->reg + REG_MCMDR); +} + +static void w90p910_trigger_rx(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + __raw_writel(ENSTART, ether->reg + REG_RSDR); +} + +static void w90p910_trigger_tx(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + __raw_writel(ENSTART, ether->reg + REG_TSDR); +} + +static void w90p910_enable_mac_interrupt(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = ENTXINTR | ENRXINTR | ENRXGD | ENTXCP; + val |= ENTXBERR | ENRXBERR | ENTXABT; + + __raw_writel(val, ether->reg + REG_MIEN); +} + +static void w90p910_get_and_clear_int(struct net_device *dev, + unsigned int *val) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + *val = __raw_readl(ether->reg + REG_MISTA); + __raw_writel(*val, ether->reg + REG_MISTA); +} + +static void w90p910_set_global_maccmd(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = __raw_readl(ether->reg + REG_MCMDR); + val |= MCMDR_SPCRC | MCMDR_ENMDC | MCMDR_ACP | ENMDC; + __raw_writel(val, ether->reg + REG_MCMDR); +} + +static void w90p910_enable_cam(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + w90p910_write_cam(dev, CAM0, dev->dev_addr); + + val = __raw_readl(ether->reg + REG_CAMEN); + val |= CAM0EN; + __raw_writel(val, ether->reg + REG_CAMEN); +} + +static void w90p910_enable_cam_command(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = CAMCMR_ECMP | CAMCMR_ABP | CAMCMR_AMP; + __raw_writel(val, ether->reg + REG_CAMCMR); +} + +static void w90p910_enable_tx(struct net_device *dev, unsigned int enable) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = __raw_readl(ether->reg + REG_MCMDR); + + if (enable) + val |= MCMDR_TXON; + else + val &= ~MCMDR_TXON; + + __raw_writel(val, ether->reg + REG_MCMDR); +} + +static void w90p910_enable_rx(struct net_device *dev, unsigned int enable) +{ + struct w90p910_ether *ether = netdev_priv(dev); + unsigned int val; + + val = __raw_readl(ether->reg + REG_MCMDR); + + if (enable) + val |= MCMDR_RXON; + else + val &= ~MCMDR_RXON; + + __raw_writel(val, ether->reg + REG_MCMDR); +} + +static void w90p910_set_curdest(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + __raw_writel(ether->start_rx_ptr, ether->reg + REG_RXDLSA); + __raw_writel(ether->start_tx_ptr, ether->reg + REG_TXDLSA); +} + +static void w90p910_reset_mac(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + spin_lock(ðer->lock); + + w90p910_enable_tx(dev, 0); + w90p910_enable_rx(dev, 0); + w90p910_set_fifo_threshold(dev); + w90p910_return_default_idle(dev); + + if (!netif_queue_stopped(dev)) + netif_stop_queue(dev); + + w90p910_init_desc(dev); + + dev->trans_start = jiffies; + ether->cur_tx = 0x0; + ether->finish_tx = 0x0; + ether->cur_rx = 0x0; + + w90p910_set_curdest(dev); + w90p910_enable_cam(dev); + w90p910_enable_cam_command(dev); + w90p910_enable_mac_interrupt(dev); + w90p910_enable_tx(dev, 1); + w90p910_enable_rx(dev, 1); + w90p910_trigger_tx(dev); + w90p910_trigger_rx(dev); + + dev->trans_start = jiffies; + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + + spin_unlock(ðer->lock); +} + +static void w90p910_mdio_write(struct net_device *dev, + int phy_id, int reg, int data) +{ + struct w90p910_ether *ether = netdev_priv(dev); + struct platform_device *pdev; + unsigned int val, i; + + pdev = ether->pdev; + + __raw_writel(data, ether->reg + REG_MIID); + + val = (phy_id << 0x08) | reg; + val |= PHYBUSY | PHYWR | MDCCR_VAL; + __raw_writel(val, ether->reg + REG_MIIDA); + + for (i = 0; i < DELAY; i++) { + if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0) + break; + } + + if (i == DELAY) + dev_warn(&pdev->dev, "mdio write timed out\n"); +} + +static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg) +{ + struct w90p910_ether *ether = netdev_priv(dev); + struct platform_device *pdev; + unsigned int val, i, data; + + pdev = ether->pdev; + + val = (phy_id << 0x08) | reg; + val |= PHYBUSY | MDCCR_VAL; + __raw_writel(val, ether->reg + REG_MIIDA); + + for (i = 0; i < DELAY; i++) { + if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0) + break; + } + + if (i == DELAY) { + dev_warn(&pdev->dev, "mdio read timed out\n"); + data = 0xffff; + } else { + data = __raw_readl(ether->reg + REG_MIID); + } + + return data; +} + +static int set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *address = addr; + + if (!is_valid_ether_addr(address->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, address->sa_data, dev->addr_len); + w90p910_write_cam(dev, CAM0, dev->dev_addr); + + return 0; +} + +static int w90p910_ether_close(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + dma_free_writecombine(NULL, sizeof(struct w90p910_rxbd), + ether->rdesc, (dma_addr_t)ether->rdesc_phys); + dma_free_writecombine(NULL, sizeof(struct w90p910_txbd), + ether->tdesc, (dma_addr_t)ether->tdesc_phys); + + netif_stop_queue(dev); + + del_timer_sync(ðer->check_timer); + clk_disable(ether->rmiiclk); + clk_disable(ether->clk); + + free_irq(ether->txirq, dev); + free_irq(ether->rxirq, dev); + + return 0; +} + +static struct net_device_stats *w90p910_ether_stats(struct net_device *dev) +{ + struct w90p910_ether *ether; + + ether = netdev_priv(dev); + + return ðer->stats; +} + +static int w90p910_send_frame(struct net_device *dev, + unsigned char *data, int length) +{ + struct w90p910_ether *ether; + struct w90p910_txbd *txbd; + struct platform_device *pdev; + unsigned char *buffer; + + ether = netdev_priv(dev); + pdev = ether->pdev; + + txbd = ðer->tdesc->desclist[ether->cur_tx]; + buffer = ether->tdesc->tran_buf[ether->cur_tx]; + if (length > 1514) { + dev_err(&pdev->dev, "send data %d bytes, check it\n", length); + length = 1514; + } + + txbd->sl = length & 0xFFFF; + + memcpy(buffer, data, length); + + txbd->mode = TX_OWEN_DMA | PADDINGMODE | CRCMODE | MACTXINTEN; + + w90p910_enable_tx(dev, 1); + + w90p910_trigger_tx(dev); + + ether->cur_tx = (ether->cur_tx+1) % TX_DESC_SIZE; + txbd = ðer->tdesc->desclist[ether->cur_tx]; + + dev->trans_start = jiffies; + + if (txbd->mode & TX_OWEN_DMA) + netif_stop_queue(dev); + + return 0; +} + +static int w90p910_ether_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + if (!(w90p910_send_frame(dev, skb->data, skb->len))) { + ether->skb = skb; + dev_kfree_skb_irq(skb); + return 0; + } + return -1; +} + +static irqreturn_t w90p910_tx_interrupt(int irq, void *dev_id) +{ + struct w90p910_ether *ether; + struct w90p910_txbd *txbd; + struct platform_device *pdev; + struct tran_pdesc *tran_pdesc; + struct net_device *dev; + unsigned int cur_entry, entry, status; + + dev = (struct net_device *)dev_id; + ether = netdev_priv(dev); + pdev = ether->pdev; + + spin_lock(ðer->lock); + + w90p910_get_and_clear_int(dev, &status); + + cur_entry = __raw_readl(ether->reg + REG_CTXDSA); + + tran_pdesc = ether->tdesc_phys; + entry = (unsigned int)(&tran_pdesc->desclist[ether->finish_tx]); + + while (entry != cur_entry) { + txbd = ðer->tdesc->desclist[ether->finish_tx]; + + ether->finish_tx = (ether->finish_tx + 1) % TX_DESC_SIZE; + + if (txbd->sl & TXDS_TXCP) { + ether->stats.tx_packets++; + ether->stats.tx_bytes += txbd->sl & 0xFFFF; + } else { + ether->stats.tx_errors++; + } + + txbd->sl = 0x0; + txbd->mode = 0x0; + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + + entry = (unsigned int)(&tran_pdesc->desclist[ether->finish_tx]); + } + + if (status & MISTA_EXDEF) { + dev_err(&pdev->dev, "emc defer exceed interrupt\n"); + } else if (status & MISTA_TXBERR) { + dev_err(&pdev->dev, "emc bus error interrupt\n"); + w90p910_reset_mac(dev); + } else if (status & MISTA_TDU) { + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + } + + spin_unlock(ðer->lock); + + return IRQ_HANDLED; +} + +static void netdev_rx(struct net_device *dev) +{ + struct w90p910_ether *ether; + struct w90p910_rxbd *rxbd; + struct platform_device *pdev; + struct recv_pdesc *rdesc_phys; + struct sk_buff *skb; + unsigned char *data; + unsigned int length, status, val, entry; + + ether = netdev_priv(dev); + pdev = ether->pdev; + rdesc_phys = ether->rdesc_phys; + + rxbd = ðer->rdesc->desclist[ether->cur_rx]; + + do { + val = __raw_readl(ether->reg + REG_CRXDSA); + entry = (unsigned int)&rdesc_phys->desclist[ether->cur_rx]; + + if (val == entry) + break; + + status = rxbd->sl; + length = status & 0xFFFF; + + if (status & RXDS_RXGD) { + data = ether->rdesc->recv_buf[ether->cur_rx]; + skb = dev_alloc_skb(length+2); + if (!skb) { + dev_err(&pdev->dev, "get skb buffer error\n"); + ether->stats.rx_dropped++; + return; + } + + skb->dev = dev; + skb_reserve(skb, 2); + skb_put(skb, length); + skb_copy_to_linear_data(skb, data, length); + skb->protocol = eth_type_trans(skb, dev); + ether->stats.rx_packets++; + ether->stats.rx_bytes += length; + netif_rx(skb); + } else { + ether->stats.rx_errors++; + + if (status & RXDS_RP) { + dev_err(&pdev->dev, "rx runt err\n"); + ether->stats.rx_length_errors++; + } else if (status & RXDS_CRCE) { + dev_err(&pdev->dev, "rx crc err\n"); + ether->stats.rx_crc_errors++; + } + + if (status & RXDS_ALIE) { + dev_err(&pdev->dev, "rx aligment err\n"); + ether->stats.rx_frame_errors++; + } else if (status & RXDS_PTLE) { + dev_err(&pdev->dev, "rx longer err\n"); + ether->stats.rx_over_errors++; + } + } + + rxbd->sl = RX_OWEN_DMA; + rxbd->reserved = 0x0; + ether->cur_rx = (ether->cur_rx+1) % RX_DESC_SIZE; + rxbd = ðer->rdesc->desclist[ether->cur_rx]; + + dev->last_rx = jiffies; + } while (1); +} + +static irqreturn_t w90p910_rx_interrupt(int irq, void *dev_id) +{ + struct net_device *dev; + struct w90p910_ether *ether; + struct platform_device *pdev; + unsigned int status; + + dev = (struct net_device *)dev_id; + ether = netdev_priv(dev); + pdev = ether->pdev; + + spin_lock(ðer->lock); + + w90p910_get_and_clear_int(dev, &status); + + if (status & MISTA_RDU) { + netdev_rx(dev); + + w90p910_trigger_rx(dev); + + spin_unlock(ðer->lock); + return IRQ_HANDLED; + } else if (status & MISTA_RXBERR) { + dev_err(&pdev->dev, "emc rx bus error\n"); + w90p910_reset_mac(dev); + } + + netdev_rx(dev); + spin_unlock(ðer->lock); + return IRQ_HANDLED; +} + +static int w90p910_ether_open(struct net_device *dev) +{ + struct w90p910_ether *ether; + struct platform_device *pdev; + + ether = netdev_priv(dev); + pdev = ether->pdev; + + w90p910_reset_mac(dev); + w90p910_set_fifo_threshold(dev); + w90p910_set_curdest(dev); + w90p910_enable_cam(dev); + w90p910_enable_cam_command(dev); + w90p910_enable_mac_interrupt(dev); + w90p910_set_global_maccmd(dev); + w90p910_enable_rx(dev, 1); + + ether->rx_packets = 0x0; + ether->rx_bytes = 0x0; + + if (request_irq(ether->txirq, w90p910_tx_interrupt, + 0x0, pdev->name, dev)) { + dev_err(&pdev->dev, "register irq tx failed\n"); + return -EAGAIN; + } + + if (request_irq(ether->rxirq, w90p910_rx_interrupt, + 0x0, pdev->name, dev)) { + dev_err(&pdev->dev, "register irq rx failed\n"); + return -EAGAIN; + } + + mod_timer(ðer->check_timer, jiffies + msecs_to_jiffies(1000)); + netif_start_queue(dev); + w90p910_trigger_rx(dev); + + dev_info(&pdev->dev, "%s is OPENED\n", dev->name); + + return 0; +} + +static void w90p910_ether_set_multicast_list(struct net_device *dev) +{ + struct w90p910_ether *ether; + unsigned int rx_mode; + + ether = netdev_priv(dev); + + if (dev->flags & IFF_PROMISC) + rx_mode = CAMCMR_AUP | CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; + else if ((dev->flags & IFF_ALLMULTI) || dev->mc_list) + rx_mode = CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; + else + rx_mode = CAMCMR_ECMP | CAMCMR_ABP; + __raw_writel(rx_mode, ether->reg + REG_CAMCMR); +} + +static int w90p910_ether_ioctl(struct net_device *dev, + struct ifreq *ifr, int cmd) +{ + struct w90p910_ether *ether = netdev_priv(dev); + struct mii_ioctl_data *data = if_mii(ifr); + + return generic_mii_ioctl(ðer->mii, data, cmd, NULL); +} + +static void w90p910_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_MODULE_NAME); + strcpy(info->version, DRV_MODULE_VERSION); +} + +static int w90p910_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct w90p910_ether *ether = netdev_priv(dev); + return mii_ethtool_gset(ðer->mii, cmd); +} + +static int w90p910_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct w90p910_ether *ether = netdev_priv(dev); + return mii_ethtool_sset(ðer->mii, cmd); +} + +static int w90p910_nway_reset(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + return mii_nway_restart(ðer->mii); +} + +static u32 w90p910_get_link(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + return mii_link_ok(ðer->mii); +} + +static const struct ethtool_ops w90p910_ether_ethtool_ops = { + .get_settings = w90p910_get_settings, + .set_settings = w90p910_set_settings, + .get_drvinfo = w90p910_get_drvinfo, + .nway_reset = w90p910_nway_reset, + .get_link = w90p910_get_link, +}; + +static const struct net_device_ops w90p910_ether_netdev_ops = { + .ndo_open = w90p910_ether_open, + .ndo_stop = w90p910_ether_close, + .ndo_start_xmit = w90p910_ether_start_xmit, + .ndo_get_stats = w90p910_ether_stats, + .ndo_set_multicast_list = w90p910_ether_set_multicast_list, + .ndo_set_mac_address = set_mac_address, + .ndo_do_ioctl = w90p910_ether_ioctl, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = eth_change_mtu, +}; + +static void __init get_mac_address(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + struct platform_device *pdev; + char addr[6]; + + pdev = ether->pdev; + + addr[0] = 0x00; + addr[1] = 0x02; + addr[2] = 0xac; + addr[3] = 0x55; + addr[4] = 0x88; + addr[5] = 0xa8; + + if (is_valid_ether_addr(addr)) + memcpy(dev->dev_addr, &addr, 0x06); + else + dev_err(&pdev->dev, "invalid mac address\n"); +} + +static int w90p910_ether_setup(struct net_device *dev) +{ + struct w90p910_ether *ether = netdev_priv(dev); + + ether_setup(dev); + dev->netdev_ops = &w90p910_ether_netdev_ops; + dev->ethtool_ops = &w90p910_ether_ethtool_ops; + + dev->tx_queue_len = 16; + dev->dma = 0x0; + dev->watchdog_timeo = TX_TIMEOUT; + + get_mac_address(dev); + + spin_lock_init(ðer->lock); + + ether->cur_tx = 0x0; + ether->cur_rx = 0x0; + ether->finish_tx = 0x0; + ether->linkflag = 0x0; + ether->mii.phy_id = 0x01; + ether->mii.phy_id_mask = 0x1f; + ether->mii.reg_num_mask = 0x1f; + ether->mii.dev = dev; + ether->mii.mdio_read = w90p910_mdio_read; + ether->mii.mdio_write = w90p910_mdio_write; + + setup_timer(ðer->check_timer, w90p910_check_link, + (unsigned long)dev); + + return 0; +} + +static int __devinit w90p910_ether_probe(struct platform_device *pdev) +{ + struct w90p910_ether *ether; + struct net_device *dev; + struct resource *res; + int error; + + dev = alloc_etherdev(sizeof(struct w90p910_ether)); + if (!dev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + error = -ENXIO; + goto failed_free; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto failed_free; + } + + ether = netdev_priv(dev); + + ether->reg = ioremap(res->start, resource_size(res)); + if (ether->reg == NULL) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto failed_free_mem; + } + + ether->txirq = platform_get_irq(pdev, 0); + if (ether->txirq < 0) { + dev_err(&pdev->dev, "failed to get ether tx irq\n"); + error = -ENXIO; + goto failed_free_io; + } + + ether->rxirq = platform_get_irq(pdev, 1); + if (ether->rxirq < 0) { + dev_err(&pdev->dev, "failed to get ether rx irq\n"); + error = -ENXIO; + goto failed_free_txirq; + } + + platform_set_drvdata(pdev, dev); + + ether->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(ether->clk)) { + dev_err(&pdev->dev, "failed to get ether clock\n"); + error = PTR_ERR(ether->clk); + goto failed_free_rxirq; + } + + ether->rmiiclk = clk_get(&pdev->dev, "RMII"); + if (IS_ERR(ether->rmiiclk)) { + dev_err(&pdev->dev, "failed to get ether clock\n"); + error = PTR_ERR(ether->rmiiclk); + goto failed_put_clk; + } + + ether->pdev = pdev; + + w90p910_ether_setup(dev); + + error = register_netdev(dev); + if (error != 0) { + dev_err(&pdev->dev, "Regiter EMC w90p910 FAILED\n"); + error = -ENODEV; + goto failed_put_rmiiclk; + } + + return 0; +failed_put_rmiiclk: + clk_put(ether->rmiiclk); +failed_put_clk: + clk_put(ether->clk); +failed_free_rxirq: + free_irq(ether->rxirq, pdev); + platform_set_drvdata(pdev, NULL); +failed_free_txirq: + free_irq(ether->txirq, pdev); +failed_free_io: + iounmap(ether->reg); +failed_free_mem: + release_mem_region(res->start, resource_size(res)); +failed_free: + free_netdev(dev); + return error; +} + +static int __devexit w90p910_ether_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct w90p910_ether *ether = netdev_priv(dev); + + unregister_netdev(dev); + clk_put(ether->rmiiclk); + clk_put(ether->clk); + del_timer_sync(ðer->check_timer); + platform_set_drvdata(pdev, NULL); + free_netdev(dev); + return 0; +} + +static struct platform_driver w90p910_ether_driver = { + .probe = w90p910_ether_probe, + .remove = __devexit_p(w90p910_ether_remove), + .driver = { + .name = "w90p910-emc", + .owner = THIS_MODULE, + }, +}; + +static int __init w90p910_ether_init(void) +{ + return platform_driver_register(&w90p910_ether_driver); +} + +static void __exit w90p910_ether_exit(void) +{ + platform_driver_unregister(&w90p910_ether_driver); +} + +module_init(w90p910_ether_init); +module_exit(w90p910_ether_exit); + +MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); +MODULE_DESCRIPTION("w90p910 MAC driver!"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:w90p910-emc"); + diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c index 18b566ad4fd1..cf30e278f182 100644 --- a/drivers/net/at1700.c +++ b/drivers/net/at1700.c @@ -318,7 +318,7 @@ static int __init at1700_probe1(struct net_device *dev, int ioaddr) pos3 = mca_read_stored_pos( slot, 3 ); pos4 = mca_read_stored_pos( slot, 4 ); - for (l_i = 0; l_i < 0x09; l_i++) + for (l_i = 0; l_i < 8; l_i++) if (( pos3 & 0x07) == at1700_ioaddr_pattern[l_i]) break; ioaddr = at1700_mca_probe_list[l_i]; diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h index e1658ef3fcdf..2a1120ad2e74 100644 --- a/drivers/net/atl1c/atl1c.h +++ b/drivers/net/atl1c/atl1c.h @@ -188,14 +188,14 @@ struct atl1c_tpd_ext_desc { #define RRS_HDS_TYPE_DATA 2 #define RRS_IS_NO_HDS_TYPE(flag) \ - (((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK == 0) + ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == 0) #define RRS_IS_HDS_HEAD(flag) \ - (((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK == \ + ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \ RRS_HDS_TYPE_HEAD) #define RRS_IS_HDS_DATA(flag) \ - (((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK == \ + ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \ RRS_HDS_TYPE_DATA) /* rrs word 3 bit 0:31 */ @@ -245,7 +245,7 @@ struct atl1c_tpd_ext_desc { #define RRS_PACKET_TYPE_802_3 1 #define RRS_PACKET_TYPE_ETH 0 #define RRS_PACKET_IS_ETH(word) \ - (((word) >> RRS_PACKET_TYPE_SHIFT) & RRS_PACKET_TYPE_MASK == \ + ((((word) >> RRS_PACKET_TYPE_SHIFT) & RRS_PACKET_TYPE_MASK) == \ RRS_PACKET_TYPE_ETH) #define RRS_RXD_IS_VALID(word) \ ((((word) >> RRS_RXD_UPDATED_SHIFT) & RRS_RXD_UPDATED_MASK) == 1) diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c index cd547a205fb9..a383122679de 100644 --- a/drivers/net/atl1c/atl1c_main.c +++ b/drivers/net/atl1c/atl1c_main.c @@ -1689,7 +1689,7 @@ static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que, if (likely(RRS_RXD_IS_VALID(rrs->word3))) { rfd_num = (rrs->word0 >> RRS_RX_RFD_CNT_SHIFT) & RRS_RX_RFD_CNT_MASK; - if (unlikely(rfd_num) != 1) + if (unlikely(rfd_num != 1)) /* TODO support mul rfd*/ if (netif_msg_rx_err(adapter)) dev_warn(&pdev->dev, diff --git a/drivers/net/atlx/atl2.c b/drivers/net/atlx/atl2.c index c734b1983ec1..204db961029e 100644 --- a/drivers/net/atlx/atl2.c +++ b/drivers/net/atlx/atl2.c @@ -2071,7 +2071,7 @@ static int atl2_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE)) return -EOPNOTSUPP; - if (wol->wolopts & (WAKE_MCAST|WAKE_BCAST|WAKE_MCAST)) + if (wol->wolopts & (WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)) return -EOPNOTSUPP; /* these settings will always override what we currently have */ diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c index c43f6a119295..dea3155688bb 100644 --- a/drivers/net/benet/be_main.c +++ b/drivers/net/benet/be_main.c @@ -667,7 +667,7 @@ static void skb_fill_rx_data(struct be_adapter *adapter, struct be_queue_info *rxq = &adapter->rx_obj.q; struct be_rx_page_info *page_info; u16 rxq_idx, i, num_rcvd, j; - u32 pktsize, hdr_len, curr_frag_len; + u32 pktsize, hdr_len, curr_frag_len, size; u8 *start; rxq_idx = AMAP_GET_BITS(struct amap_eth_rx_compl, fragndx, rxcp); @@ -708,12 +708,13 @@ static void skb_fill_rx_data(struct be_adapter *adapter, } /* More frags present for this completion */ - pktsize -= curr_frag_len; /* account for above copied frag */ + size = pktsize; for (i = 1, j = 0; i < num_rcvd; i++) { + size -= curr_frag_len; index_inc(&rxq_idx, rxq->len); page_info = get_rx_page_info(adapter, rxq_idx); - curr_frag_len = min(pktsize, rx_frag_size); + curr_frag_len = min(size, rx_frag_size); /* Coalesce all frags from the same physical page in one slot */ if (page_info->page_offset == 0) { @@ -731,7 +732,6 @@ static void skb_fill_rx_data(struct be_adapter *adapter, skb_shinfo(skb)->frags[j].size += curr_frag_len; skb->len += curr_frag_len; skb->data_len += curr_frag_len; - pktsize -= curr_frag_len; memset(page_info, 0, sizeof(*page_info)); } diff --git a/drivers/net/bmac.c b/drivers/net/bmac.c index 9578a3dfac01..206144f2470f 100644 --- a/drivers/net/bmac.c +++ b/drivers/net/bmac.c @@ -428,10 +428,11 @@ bmac_init_phy(struct net_device *dev) printk(KERN_DEBUG "phy registers:"); for (addr = 0; addr < 32; ++addr) { if ((addr & 7) == 0) - printk("\n" KERN_DEBUG); - printk(" %.4x", bmac_mif_read(dev, addr)); + printk(KERN_DEBUG); + printk(KERN_CONT " %.4x", bmac_mif_read(dev, addr)); } - printk("\n"); + printk(KERN_CONT "\n"); + if (bp->is_bmac_plus) { unsigned int capable, ctrl; diff --git a/drivers/net/bnx2x_link.c b/drivers/net/bnx2x_link.c index ed648acef7cf..2ee581a2cdec 100644 --- a/drivers/net/bnx2x_link.c +++ b/drivers/net/bnx2x_link.c @@ -4212,13 +4212,14 @@ static void bnx2x_turn_off_sf(struct bnx2x *bp, u8 port) u8 bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded, u8 *version, u16 len) { - struct bnx2x *bp = params->bp; + struct bnx2x *bp; u32 ext_phy_type = 0; u32 spirom_ver = 0; u8 status = 0 ; if (version == NULL || params == NULL) return -EINVAL; + bp = params->bp; spirom_ver = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 6c67be679764..c36a5f33739f 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -484,8 +484,9 @@ static void bnx2x_fw_dump(struct bnx2x *bp) mark = REG_RD(bp, MCP_REG_MCPR_SCRATCH + 0xf104); mark = ((mark + 0x3) & ~0x3); - printk(KERN_ERR PFX "begin fw dump (mark 0x%x)\n" KERN_ERR, mark); + printk(KERN_ERR PFX "begin fw dump (mark 0x%x)\n", mark); + printk(KERN_ERR PFX); for (offset = mark - 0x08000000; offset <= 0xF900; offset += 0x8*4) { for (word = 0; word < 8; word++) data[word] = htonl(REG_RD(bp, MCP_REG_MCPR_SCRATCH + @@ -500,7 +501,7 @@ static void bnx2x_fw_dump(struct bnx2x *bp) data[8] = 0x0; printk(KERN_CONT "%s", (char *)data); } - printk("\n" KERN_ERR PFX "end of fw dump\n"); + printk(KERN_ERR PFX "end of fw dump\n"); } static void bnx2x_panic_dump(struct bnx2x *bp) @@ -7354,7 +7355,7 @@ static void bnx2x_reset_task(struct work_struct *work) #ifdef BNX2X_STOP_ON_ERROR BNX2X_ERR("reset task called but STOP_ON_ERROR defined" " so reset not done to allow debug dump,\n" - KERN_ERR " you will need to reboot when done\n"); + " you will need to reboot when done\n"); return; #endif diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index d927f71af8a3..aa1be1feceed 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1459,8 +1459,16 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) * ether type (eg ARPHRD_ETHER and ARPHRD_INFINIBAND) share the same bond */ if (bond->slave_cnt == 0) { - if (slave_dev->type != ARPHRD_ETHER) - bond_setup_by_slave(bond_dev, slave_dev); + if (bond_dev->type != slave_dev->type) { + dev_close(bond_dev); + pr_debug("%s: change device type from %d to %d\n", + bond_dev->name, bond_dev->type, slave_dev->type); + if (slave_dev->type != ARPHRD_ETHER) + bond_setup_by_slave(bond_dev, slave_dev); + else + ether_setup(bond_dev); + dev_open(bond_dev); + } } else if (bond_dev->type != slave_dev->type) { pr_err(DRV_NAME ": %s ether type (%d) is different " "from other slaves (%d), can not enslave it.\n", diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 574daddc21bf..9e4283aff828 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -346,7 +346,7 @@ void can_restart(unsigned long data) skb = dev_alloc_skb(sizeof(struct can_frame)); if (skb == NULL) { err = -ENOMEM; - goto out; + goto restart; } skb->dev = dev; skb->protocol = htons(ETH_P_CAN); @@ -361,13 +361,13 @@ void can_restart(unsigned long data) stats->rx_packets++; stats->rx_bytes += cf->can_dlc; +restart: dev_dbg(dev->dev.parent, "restarted\n"); priv->can_stats.restarts++; /* Now restart the device */ err = priv->do_set_mode(dev, CAN_MODE_START); -out: netif_carrier_on(dev); if (err) dev_err(dev->dev.parent, "Error %d during restart", err); @@ -473,6 +473,10 @@ int open_candev(struct net_device *dev) return -EINVAL; } + /* Switch carrier on if device was stopped while in bus-off state */ + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + setup_timer(&priv->restart_timer, can_restart, (unsigned long)dev); return 0; diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index 571f133a8fec..08ebee79d8a6 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c @@ -63,7 +63,6 @@ #include <linux/can.h> #include <linux/can/dev.h> #include <linux/can/error.h> -#include <linux/can/dev.h> #include "sja1000.h" diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index 4d1515f45ba2..4869d77cbe91 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c @@ -227,7 +227,7 @@ static int cnic_send_nlmsg(struct cnic_local *cp, u32 type, } rcu_read_lock(); - ulp_ops = rcu_dereference(cp->ulp_ops[CNIC_ULP_ISCSI]); + ulp_ops = rcu_dereference(cnic_ulp_tbl[CNIC_ULP_ISCSI]); if (ulp_ops) ulp_ops->iscsi_nl_send_msg(cp->dev, msg_type, buf, len); rcu_read_unlock(); @@ -319,6 +319,20 @@ static int cnic_abort_prep(struct cnic_sock *csk) return 0; } +static void cnic_uio_stop(void) +{ + struct cnic_dev *dev; + + read_lock(&cnic_dev_lock); + list_for_each_entry(dev, &cnic_dev_list, list) { + struct cnic_local *cp = dev->cnic_priv; + + if (cp->cnic_uinfo) + cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL); + } + read_unlock(&cnic_dev_lock); +} + int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops) { struct cnic_dev *dev; @@ -390,6 +404,9 @@ int cnic_unregister_driver(int ulp_type) } read_unlock(&cnic_dev_lock); + if (ulp_type == CNIC_ULP_ISCSI) + cnic_uio_stop(); + rcu_assign_pointer(cnic_ulp_tbl[ulp_type], NULL); mutex_unlock(&cnic_lock); @@ -632,7 +649,6 @@ static void cnic_free_resc(struct cnic_dev *dev) int i = 0; if (cp->cnic_uinfo) { - cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL); while (cp->uio_dev != -1 && i < 15) { msleep(100); i++; @@ -1057,6 +1073,9 @@ static void cnic_ulp_stop(struct cnic_dev *dev) struct cnic_local *cp = dev->cnic_priv; int if_type; + if (cp->cnic_uinfo) + cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL); + rcu_read_lock(); for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) { struct cnic_ulp_ops *ulp_ops; diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c index 3eee666a9cd2..55445f980f9c 100644 --- a/drivers/net/cs89x0.c +++ b/drivers/net/cs89x0.c @@ -1524,6 +1524,7 @@ static void net_timeout(struct net_device *dev) static int net_send_packet(struct sk_buff *skb, struct net_device *dev) { struct net_local *lp = netdev_priv(dev); + unsigned long flags; if (net_debug > 3) { printk("%s: sent %d byte packet of type %x\n", @@ -1535,7 +1536,7 @@ static int net_send_packet(struct sk_buff *skb, struct net_device *dev) ask the chip to start transmitting before the whole packet has been completely uploaded. */ - spin_lock_irq(&lp->lock); + spin_lock_irqsave(&lp->lock, flags); netif_stop_queue(dev); /* initiate a transmit sequence */ @@ -1549,13 +1550,13 @@ static int net_send_packet(struct sk_buff *skb, struct net_device *dev) * we're waiting for TxOk, so return 1 and requeue this packet. */ - spin_unlock_irq(&lp->lock); + spin_unlock_irqrestore(&lp->lock, flags); if (net_debug) printk("cs89x0: Tx buffer not free!\n"); return NETDEV_TX_BUSY; } /* Write the contents of the packet */ writewords(dev->base_addr, TX_FRAME_PORT,skb->data,(skb->len+1) >>1); - spin_unlock_irq(&lp->lock); + spin_unlock_irqrestore(&lp->lock, flags); lp->stats.tx_bytes += skb->len; dev->trans_start = jiffies; dev_kfree_skb (skb); diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index 538dda4422dc..fb5df5c6203e 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -642,8 +642,7 @@ static int setup_sge_qsets(struct adapter *adap) struct port_info *pi = netdev_priv(dev); pi->qs = &adap->sge.qs[pi->first_qset]; - for (j = pi->first_qset; j < pi->first_qset + pi->nqsets; - ++j, ++qset_idx) { + for (j = 0; j < pi->nqsets; ++j, ++qset_idx) { set_qset_lro(dev, qset_idx, pi->rx_offload & T3_LRO); err = t3_sge_alloc_qset(adap, qset_idx, 1, (adap->flags & USING_MSIX) ? qset_idx + 1 : diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index 2df8fb0af701..12fd446f9895 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -1820,11 +1820,19 @@ static int emac_dev_setmac_addr(struct net_device *ndev, void *addr) struct device *emac_dev = &priv->ndev->dev; struct sockaddr *sa = addr; + if (!is_valid_ether_addr(sa->sa_data)) + return -EINVAL; + /* Store mac addr in priv and rx channel and set it in EMAC hw */ memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len); - memcpy(rxch->mac_addr, sa->sa_data, ndev->addr_len); memcpy(ndev->dev_addr, sa->sa_data, ndev->addr_len); - emac_setmac(priv, EMAC_DEF_RX_CH, rxch->mac_addr); + + /* If the interface is down - rxch is NULL. */ + /* MAC address is configured only after the interface is enabled. */ + if (netif_running(ndev)) { + memcpy(rxch->mac_addr, sa->sa_data, ndev->addr_len); + emac_setmac(priv, EMAC_DEF_RX_CH, rxch->mac_addr); + } if (netif_msg_drv(priv)) dev_notice(emac_dev, "DaVinci EMAC: emac_dev_setmac_addr %pM\n", diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c index 895d72143ee0..4b6a219fecea 100644 --- a/drivers/net/dl2k.c +++ b/drivers/net/dl2k.c @@ -268,8 +268,9 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) printk(KERN_INFO "tx_coalesce:\t%d packets\n", tx_coalesce); if (np->coalesce) - printk(KERN_INFO "rx_coalesce:\t%d packets\n" - KERN_INFO "rx_timeout: \t%d ns\n", + printk(KERN_INFO + "rx_coalesce:\t%d packets\n" + "rx_timeout: \t%d ns\n", np->rx_coalesce, np->rx_timeout*640); if (np->vlan) printk(KERN_INFO "vlan(id):\t%d\n", np->vlan); @@ -1522,9 +1523,9 @@ mii_get_media (struct net_device *dev) printk (KERN_INFO "Operating at 10 Mbps, "); } if (bmcr & MII_BMCR_DUPLEX_MODE) { - printk ("Full duplex\n"); + printk (KERN_CONT "Full duplex\n"); } else { - printk ("Half duplex\n"); + printk (KERN_CONT "Half duplex\n"); } } if (np->tx_flow) @@ -1614,9 +1615,9 @@ mii_set_media (struct net_device *dev) } if (np->full_duplex) { bmcr |= MII_BMCR_DUPLEX_MODE; - printk ("Full duplex\n"); + printk (KERN_CONT "Full duplex\n"); } else { - printk ("Half duplex\n"); + printk (KERN_CONT "Half duplex\n"); } #if 0 /* Set 1000BaseT Master/Slave setting */ @@ -1669,9 +1670,9 @@ mii_get_media_pcs (struct net_device *dev) __u16 bmcr = mii_read (dev, phy_addr, PCS_BMCR); printk (KERN_INFO "Operating at 1000 Mbps, "); if (bmcr & MII_BMCR_DUPLEX_MODE) { - printk ("Full duplex\n"); + printk (KERN_CONT "Full duplex\n"); } else { - printk ("Half duplex\n"); + printk (KERN_CONT "Half duplex\n"); } } if (np->tx_flow) diff --git a/drivers/net/e100.c b/drivers/net/e100.c index efa680f4b8dd..41b648a67fec 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -1897,6 +1897,9 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx, if (ioread8(&nic->csr->scb.status) & rus_no_res) nic->ru_running = RU_SUSPENDED; + pci_dma_sync_single_for_device(nic->pdev, rx->dma_addr, + sizeof(struct rfd), + PCI_DMA_BIDIRECTIONAL); return -ENODATA; } diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c index cc2ab6412c73..4f7003485348 100644 --- a/drivers/net/eepro.c +++ b/drivers/net/eepro.c @@ -1784,7 +1784,7 @@ int __init init_module(void) printk(KERN_INFO "eepro_init_module: Auto-detecting boards (May God protect us...)\n"); } - for (i = 0; io[i] != -1 && i < MAX_EEPRO; i++) { + for (i = 0; i < MAX_EEPRO && io[i] != -1; i++) { dev = alloc_etherdev(sizeof(struct eepro_local)); if (!dev) break; diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c index 147c4b088fb3..e8d46cc1bec2 100644 --- a/drivers/net/ehea/ehea_main.c +++ b/drivers/net/ehea/ehea_main.c @@ -3080,7 +3080,9 @@ static const struct net_device_ops ehea_netdev_ops = { .ndo_poll_controller = ehea_netpoll, #endif .ndo_get_stats = ehea_get_stats, + .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = ehea_set_mac_addr, + .ndo_validate_addr = eth_validate_addr, .ndo_set_multicast_list = ehea_set_multicast_list, .ndo_change_mtu = ehea_change_mtu, .ndo_vlan_rx_register = ehea_vlan_rx_register, diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index b60e27dfcfa7..88d7ebf31220 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -338,8 +338,7 @@ static int __devinit epic_init_one (struct pci_dev *pdev, #ifndef MODULE static int printed_version; if (!printed_version++) - printk (KERN_INFO "%s" KERN_INFO "%s", - version, version2); + printk(KERN_INFO "%s%s", version, version2); #endif card_idx++; @@ -1600,7 +1599,7 @@ static int __init epic_init (void) { /* when a module, this is printed whether or not devices are found in probe */ #ifdef MODULE - printk (KERN_INFO "%s" KERN_INFO "%s", + printk (KERN_INFO "%s%s", version, version2); #endif diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c index 891be28a7d4f..160655d24581 100644 --- a/drivers/net/fealnx.c +++ b/drivers/net/fealnx.c @@ -584,7 +584,8 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev, if (np->flags == HAS_MII_XCVR) { int phy, phy_idx = 0; - for (phy = 1; phy < 32 && phy_idx < 4; phy++) { + for (phy = 1; phy < 32 && phy_idx < ARRAY_SIZE(np->phys); + phy++) { int mii_status = mdio_read(dev, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { @@ -1209,17 +1210,20 @@ static void fealnx_tx_timeout(struct net_device *dev) unsigned long flags; int i; - printk(KERN_WARNING "%s: Transmit timed out, status %8.8x," - " resetting...\n", dev->name, ioread32(ioaddr + ISR)); + printk(KERN_WARNING + "%s: Transmit timed out, status %8.8x, resetting...\n", + dev->name, ioread32(ioaddr + ISR)); { printk(KERN_DEBUG " Rx ring %p: ", np->rx_ring); for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int) np->rx_ring[i].status); - printk("\n" KERN_DEBUG " Tx ring %p: ", np->tx_ring); + printk(KERN_CONT " %8.8x", + (unsigned int) np->rx_ring[i].status); + printk(KERN_CONT "\n"); + printk(KERN_DEBUG " Tx ring %p: ", np->tx_ring); for (i = 0; i < TX_RING_SIZE; i++) - printk(" %4.4x", np->tx_ring[i].status); - printk("\n"); + printk(KERN_CONT " %4.4x", np->tx_ring[i].status); + printk(KERN_CONT "\n"); } spin_lock_irqsave(&np->lock, flags); diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 0f19b743749b..d4b98074b1b7 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -1642,6 +1642,7 @@ static const struct net_device_ops fec_netdev_ops = { .ndo_stop = fec_enet_close, .ndo_start_xmit = fec_enet_start_xmit, .ndo_set_multicast_list = set_multicast_list, + .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = fec_timeout, .ndo_set_mac_address = fec_set_mac_address, diff --git a/drivers/net/fec.h b/drivers/net/fec.h index 30b7dd671336..cc47f3f057c7 100644 --- a/drivers/net/fec.h +++ b/drivers/net/fec.h @@ -46,12 +46,12 @@ #else -#define FEC_ECNTRL; 0x000 /* Ethernet control reg */ -#define FEC_IEVENT; 0x004 /* Interrupt even reg */ -#define FEC_IMASK; 0x008 /* Interrupt mask reg */ -#define FEC_IVEC; 0x00c /* Interrupt vec status reg */ -#define FEC_R_DES_ACTIVE; 0x010 /* Receive descriptor reg */ -#define FEC_X_DES_ACTIVE; 0x01c /* Transmit descriptor reg */ +#define FEC_ECNTRL 0x000 /* Ethernet control reg */ +#define FEC_IEVENT 0x004 /* Interrupt even reg */ +#define FEC_IMASK 0x008 /* Interrupt mask reg */ +#define FEC_IVEC 0x00c /* Interrupt vec status reg */ +#define FEC_R_DES_ACTIVE 0x010 /* Receive descriptor reg */ +#define FEC_X_DES_ACTIVE 0x014 /* Transmit descriptor reg */ #define FEC_MII_DATA 0x040 /* MII manage frame reg */ #define FEC_MII_SPEED 0x044 /* MII speed control reg */ #define FEC_R_BOUND 0x08c /* FIFO receive bound reg */ diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c index b892c3ad9a74..2bc2d2b20644 100644 --- a/drivers/net/fs_enet/fs_enet-main.c +++ b/drivers/net/fs_enet/fs_enet-main.c @@ -754,17 +754,16 @@ static int fs_init_phy(struct net_device *dev) fep->oldlink = 0; fep->oldspeed = 0; fep->oldduplex = -1; - if(fep->fpi->phy_node) - phydev = of_phy_connect(dev, fep->fpi->phy_node, - &fs_adjust_link, 0, - PHY_INTERFACE_MODE_MII); - else { - printk("No phy bus ID specified in BSP code\n"); - return -EINVAL; + + phydev = of_phy_connect(dev, fep->fpi->phy_node, &fs_adjust_link, 0, + PHY_INTERFACE_MODE_MII); + if (!phydev) { + phydev = of_phy_connect_fixed_link(dev, &fs_adjust_link, + PHY_INTERFACE_MODE_MII); } - if (IS_ERR(phydev)) { - printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); - return PTR_ERR(phydev); + if (!phydev) { + dev_err(&dev->dev, "Could not attach to PHY\n"); + return -ENODEV; } fep->phydev = phydev; @@ -1005,6 +1004,7 @@ static int __devinit fs_enet_probe(struct of_device *ofdev, goto out_free_fpi; } + SET_NETDEV_DEV(ndev, &ofdev->dev); dev_set_drvdata(&ofdev->dev, ndev); fep = netdev_priv(ndev); diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 4ae1d259fced..f8ffcbf0bc39 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -156,6 +156,8 @@ static const struct net_device_ops gfar_netdev_ops = { .ndo_tx_timeout = gfar_timeout, .ndo_do_ioctl = gfar_ioctl, .ndo_vlan_rx_register = gfar_vlan_rx_register, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = gfar_netpoll, #endif @@ -262,15 +264,6 @@ static int gfar_of_init(struct net_device *dev) priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET; priv->phy_node = of_parse_phandle(np, "phy-handle", 0); - if (!priv->phy_node) { - u32 *fixed_link; - - fixed_link = (u32 *)of_get_property(np, "fixed-link", NULL); - if (!fixed_link) { - err = -ENODEV; - goto err_out; - } - } /* Find the TBI PHY. If it's not there, we don't support SGMII */ priv->tbi_node = of_parse_phandle(np, "tbi-handle", 0); @@ -657,13 +650,14 @@ static int init_phy(struct net_device *dev) interface = gfar_get_interface(dev); - if (priv->phy_node) { - priv->phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, - 0, interface); - if (!priv->phydev) { - dev_err(&dev->dev, "error: Could not attach to PHY\n"); - return -ENODEV; - } + priv->phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, 0, + interface); + if (!priv->phydev) + priv->phydev = of_phy_connect_fixed_link(dev, &adjust_link, + interface); + if (!priv->phydev) { + dev_err(&dev->dev, "could not attach to PHY\n"); + return -ENODEV; } if (interface == PHY_INTERFACE_MODE_SGMII) diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c index 9d5b62cb30f7..d62378cbc149 100644 --- a/drivers/net/hamachi.c +++ b/drivers/net/hamachi.c @@ -173,8 +173,8 @@ static int tx_params[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; static const char version[] __devinitconst = KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Written by Donald Becker\n" -KERN_INFO " Some modifications by Eric kasten <kasten@nscl.msu.edu>\n" -KERN_INFO " Further modifications by Keith Underwood <keithu@parl.clemson.edu>\n"; +" Some modifications by Eric kasten <kasten@nscl.msu.edu>\n" +" Further modifications by Keith Underwood <keithu@parl.clemson.edu>\n"; /* IP_MF appears to be only defined in <netinet/ip.h>, however, @@ -1080,11 +1080,14 @@ static void hamachi_tx_timeout(struct net_device *dev) { printk(KERN_DEBUG " Rx ring %p: ", hmp->rx_ring); for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", le32_to_cpu(hmp->rx_ring[i].status_n_length)); - printk("\n"KERN_DEBUG" Tx ring %p: ", hmp->tx_ring); + printk(KERN_CONT " %8.8x", + le32_to_cpu(hmp->rx_ring[i].status_n_length)); + printk(KERN_CONT "\n"); + printk(KERN_DEBUG" Tx ring %p: ", hmp->tx_ring); for (i = 0; i < TX_RING_SIZE; i++) - printk(" %4.4x", le32_to_cpu(hmp->tx_ring[i].status_n_length)); - printk("\n"); + printk(KERN_CONT " %4.4x", + le32_to_cpu(hmp->tx_ring[i].status_n_length)); + printk(KERN_CONT "\n"); } /* Reinit the hardware and make sure the Rx and Tx processes @@ -1753,13 +1756,13 @@ static int hamachi_close(struct net_device *dev) #ifdef __i386__ if (hamachi_debug > 2) { - printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n", + printk(KERN_DEBUG " Tx ring at %8.8x:\n", (int)hmp->tx_ring_dma); for (i = 0; i < TX_RING_SIZE; i++) - printk(" %c #%d desc. %8.8x %8.8x.\n", + printk(KERN_DEBUG " %c #%d desc. %8.8x %8.8x.\n", readl(ioaddr + TxCurPtr) == (long)&hmp->tx_ring[i] ? '>' : ' ', i, hmp->tx_ring[i].status_n_length, hmp->tx_ring[i].addr); - printk("\n"KERN_DEBUG " Rx ring %8.8x:\n", + printk(KERN_DEBUG " Rx ring %8.8x:\n", (int)hmp->rx_ring_dma); for (i = 0; i < RX_RING_SIZE; i++) { printk(KERN_DEBUG " %c #%d desc. %4.4x %8.8x\n", @@ -1770,7 +1773,7 @@ static int hamachi_close(struct net_device *dev) u16 *addr = (u16 *) hmp->rx_skbuff[i]->data; int j; - + printk(KERN_DEBUG "Addr: "); for (j = 0; j < 0x50; j++) printk(" %4.4x", addr[j]); printk("\n"); diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 155160052c8b..981ab530e9ac 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -3,7 +3,7 @@ * devices like TTY. It interfaces between a raw TTY and the * kernel's AX.25 protocol layers. * - * Authors: Andreas Könsgen <ajk@iehk.rwth-aachen.de> + * Authors: Andreas Könsgen <ajk@comnets.uni-bremen.de> * Ralf Baechle DL5RB <ralf@linux-mips.org> * * Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index 5e4b7afd0683..352703255bba 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -68,7 +68,7 @@ static const char paranoia_str[] = KERN_ERR static const char bc_drvname[] = "baycom_epp"; static const char bc_drvinfo[] = KERN_INFO "baycom_epp: (C) 1998-2000 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "baycom_epp: version 0.7 compiled " __TIME__ " " __DATE__ "\n"; +"baycom_epp: version 0.7 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c index 2e6fc4dc74b1..5f5af9a606f8 100644 --- a/drivers/net/hamradio/baycom_par.c +++ b/drivers/net/hamradio/baycom_par.c @@ -102,7 +102,7 @@ static const char bc_drvname[] = "baycom_par"; static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "baycom_par: version 0.9 compiled " __TIME__ " " __DATE__ "\n"; +"baycom_par: version 0.9 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c index b6a816e60c0f..aa4488e871b2 100644 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ b/drivers/net/hamradio/baycom_ser_fdx.c @@ -91,7 +91,7 @@ static const char bc_drvname[] = "baycom_ser_fdx"; static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "baycom_ser_fdx: version 0.10 compiled " __TIME__ " " __DATE__ "\n"; +"baycom_ser_fdx: version 0.10 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c index 3bcc57acbe6d..88c593596020 100644 --- a/drivers/net/hamradio/baycom_ser_hdx.c +++ b/drivers/net/hamradio/baycom_ser_hdx.c @@ -79,7 +79,7 @@ static const char bc_drvname[] = "baycom_ser_hdx"; static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "baycom_ser_hdx: version 0.10 compiled " __TIME__ " " __DATE__ "\n"; +"baycom_ser_hdx: version 0.10 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ diff --git a/drivers/net/ibm_newemac/rgmii.c b/drivers/net/ibm_newemac/rgmii.c index 1d5379de6900..8d76cb89dbd6 100644 --- a/drivers/net/ibm_newemac/rgmii.c +++ b/drivers/net/ibm_newemac/rgmii.c @@ -188,11 +188,12 @@ void rgmii_put_mdio(struct of_device *ofdev, int input) void rgmii_detach(struct of_device *ofdev, int input) { struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); - struct rgmii_regs __iomem *p = dev->base; - - mutex_lock(&dev->lock); + struct rgmii_regs __iomem *p; BUG_ON(!dev || dev->users == 0); + p = dev->base; + + mutex_lock(&dev->lock); RGMII_DBG(dev, "detach(%d)" NL, input); diff --git a/drivers/net/igb/e1000_82575.c b/drivers/net/igb/e1000_82575.c index efd9be214885..ac28dd5a4fd1 100644 --- a/drivers/net/igb/e1000_82575.c +++ b/drivers/net/igb/e1000_82575.c @@ -190,6 +190,10 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) phy->ops.write_reg = igb_write_phy_reg_igp; } + /* set lan id */ + hw->bus.func = (rd32(E1000_STATUS) & E1000_STATUS_FUNC_MASK) >> + E1000_STATUS_FUNC_SHIFT; + /* Set phy->phy_addr and phy->id. */ ret_val = igb_get_phy_id_82575(hw); if (ret_val) diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index be480292aba1..adb09d32625d 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -127,14 +127,48 @@ static void igb_restore_vlan(struct igb_adapter *); static void igb_ping_all_vfs(struct igb_adapter *); static void igb_msg_task(struct igb_adapter *); static int igb_rcv_msg_from_vf(struct igb_adapter *, u32); -static inline void igb_set_rah_pool(struct e1000_hw *, int , int); static void igb_set_mc_list_pools(struct igb_adapter *, int, u16); static void igb_vmm_control(struct igb_adapter *); -static inline void igb_set_vmolr(struct e1000_hw *, int); -static inline int igb_set_vf_rlpml(struct igb_adapter *, int, int); static int igb_set_vf_mac(struct igb_adapter *adapter, int, unsigned char *); static void igb_restore_vf_multicasts(struct igb_adapter *adapter); +static inline void igb_set_vmolr(struct e1000_hw *hw, int vfn) +{ + u32 reg_data; + + reg_data = rd32(E1000_VMOLR(vfn)); + reg_data |= E1000_VMOLR_BAM | /* Accept broadcast */ + E1000_VMOLR_ROPE | /* Accept packets matched in UTA */ + E1000_VMOLR_ROMPE | /* Accept packets matched in MTA */ + E1000_VMOLR_AUPE | /* Accept untagged packets */ + E1000_VMOLR_STRVLAN; /* Strip vlan tags */ + wr32(E1000_VMOLR(vfn), reg_data); +} + +static inline int igb_set_vf_rlpml(struct igb_adapter *adapter, int size, + int vfn) +{ + struct e1000_hw *hw = &adapter->hw; + u32 vmolr; + + vmolr = rd32(E1000_VMOLR(vfn)); + vmolr &= ~E1000_VMOLR_RLPML_MASK; + vmolr |= size | E1000_VMOLR_LPE; + wr32(E1000_VMOLR(vfn), vmolr); + + return 0; +} + +static inline void igb_set_rah_pool(struct e1000_hw *hw, int pool, int entry) +{ + u32 reg_data; + + reg_data = rd32(E1000_RAH(entry)); + reg_data &= ~E1000_RAH_POOL_MASK; + reg_data |= E1000_RAH_POOL_1 << pool;; + wr32(E1000_RAH(entry), reg_data); +} + #ifdef CONFIG_PM static int igb_suspend(struct pci_dev *, pm_message_t); static int igb_resume(struct pci_dev *); @@ -5418,43 +5452,6 @@ static void igb_io_resume(struct pci_dev *pdev) igb_get_hw_control(adapter); } -static inline void igb_set_vmolr(struct e1000_hw *hw, int vfn) -{ - u32 reg_data; - - reg_data = rd32(E1000_VMOLR(vfn)); - reg_data |= E1000_VMOLR_BAM | /* Accept broadcast */ - E1000_VMOLR_ROPE | /* Accept packets matched in UTA */ - E1000_VMOLR_ROMPE | /* Accept packets matched in MTA */ - E1000_VMOLR_AUPE | /* Accept untagged packets */ - E1000_VMOLR_STRVLAN; /* Strip vlan tags */ - wr32(E1000_VMOLR(vfn), reg_data); -} - -static inline int igb_set_vf_rlpml(struct igb_adapter *adapter, int size, - int vfn) -{ - struct e1000_hw *hw = &adapter->hw; - u32 vmolr; - - vmolr = rd32(E1000_VMOLR(vfn)); - vmolr &= ~E1000_VMOLR_RLPML_MASK; - vmolr |= size | E1000_VMOLR_LPE; - wr32(E1000_VMOLR(vfn), vmolr); - - return 0; -} - -static inline void igb_set_rah_pool(struct e1000_hw *hw, int pool, int entry) -{ - u32 reg_data; - - reg_data = rd32(E1000_RAH(entry)); - reg_data &= ~E1000_RAH_POOL_MASK; - reg_data |= E1000_RAH_POOL_1 << pool;; - wr32(E1000_RAH(entry), reg_data); -} - static void igb_set_mc_list_pools(struct igb_adapter *adapter, int entry_count, u16 total_rar_filters) { diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index d53aa9582137..20f9bc626688 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -31,7 +31,6 @@ #include <linux/tty.h> #include <linux/init.h> #include <asm/uaccess.h> -#include <linux/smp_lock.h> #include <linux/delay.h> #include <linux/mutex.h> diff --git a/drivers/net/isa-skeleton.c b/drivers/net/isa-skeleton.c index 73585fd8f29f..d12377b84358 100644 --- a/drivers/net/isa-skeleton.c +++ b/drivers/net/isa-skeleton.c @@ -430,7 +430,8 @@ static int net_send_packet(struct sk_buff *skb, struct net_device *dev) * hardware interrupt handler. Queue flow control is * thus managed under this lock as well. */ - spin_lock_irq(&np->lock); + unsigned long flags; + spin_lock_irqsave(&np->lock, flags); add_to_tx_ring(np, skb, length); dev->trans_start = jiffies; @@ -446,7 +447,7 @@ static int net_send_packet(struct sk_buff *skb, struct net_device *dev) * is when the transmit statistics are updated. */ - spin_unlock_irq(&np->lock); + spin_unlock_irqrestore(&np->lock, flags); #else /* This is the case for older hardware which takes * a single transmit buffer at a time, and it is diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h index cd22323cfd22..1b12c7ba275f 100644 --- a/drivers/net/ixgbe/ixgbe.h +++ b/drivers/net/ixgbe/ixgbe.h @@ -327,6 +327,7 @@ struct ixgbe_adapter { #define IXGBE_FLAG_IN_SFP_MOD_TASK (u32)(1 << 25) #define IXGBE_FLAG_FDIR_HASH_CAPABLE (u32)(1 << 26) #define IXGBE_FLAG_FDIR_PERFECT_CAPABLE (u32)(1 << 27) +#define IXGBE_FLAG_FCOE_CAPABLE (u32)(1 << 28) #define IXGBE_FLAG_FCOE_ENABLED (u32)(1 << 29) u32 flags2; diff --git a/drivers/net/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ixgbe/ixgbe_dcb_nl.c index d56890f5c9d5..1c7265732900 100644 --- a/drivers/net/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ixgbe/ixgbe_dcb_nl.c @@ -106,8 +106,6 @@ static u8 ixgbe_dcbnl_get_state(struct net_device *netdev) { struct ixgbe_adapter *adapter = netdev_priv(netdev); - DPRINTK(DRV, INFO, "Get DCB Admin Mode.\n"); - return !!(adapter->flags & IXGBE_FLAG_DCB_ENABLED); } @@ -116,8 +114,6 @@ static u8 ixgbe_dcbnl_set_state(struct net_device *netdev, u8 state) u8 err = 0; struct ixgbe_adapter *adapter = netdev_priv(netdev); - DPRINTK(DRV, INFO, "Set DCB Admin Mode.\n"); - if (state > 0) { /* Turn on DCB */ if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) @@ -138,7 +134,23 @@ static u8 ixgbe_dcbnl_set_state(struct net_device *netdev, u8 state) adapter->hw.fc.requested_mode = ixgbe_fc_none; } adapter->flags &= ~IXGBE_FLAG_RSS_ENABLED; + if (adapter->hw.mac.type == ixgbe_mac_82599EB) { + adapter->flags &= ~IXGBE_FLAG_FDIR_HASH_CAPABLE; + adapter->flags &= ~IXGBE_FLAG_FDIR_PERFECT_CAPABLE; + } adapter->flags |= IXGBE_FLAG_DCB_ENABLED; +#ifdef IXGBE_FCOE + /* Turn on FCoE offload */ + if ((adapter->flags & IXGBE_FLAG_FCOE_CAPABLE) && + (!(adapter->flags & IXGBE_FLAG_FCOE_ENABLED))) { + adapter->flags |= IXGBE_FLAG_FCOE_ENABLED; + adapter->ring_feature[RING_F_FCOE].indices = + IXGBE_FCRETA_SIZE; + netdev->features |= NETIF_F_FCOE_CRC; + netdev->features |= NETIF_F_FSO; + netdev->fcoe_ddp_xid = IXGBE_FCOE_DDP_MAX - 1; + } +#endif /* IXGBE_FCOE */ ixgbe_init_interrupt_scheme(adapter); if (netif_running(netdev)) netdev->netdev_ops->ndo_open(netdev); @@ -154,6 +166,20 @@ static u8 ixgbe_dcbnl_set_state(struct net_device *netdev, u8 state) adapter->dcb_cfg.pfc_mode_enable = false; adapter->flags &= ~IXGBE_FLAG_DCB_ENABLED; adapter->flags |= IXGBE_FLAG_RSS_ENABLED; + if (adapter->hw.mac.type == ixgbe_mac_82599EB) + adapter->flags |= IXGBE_FLAG_FDIR_HASH_CAPABLE; + +#ifdef IXGBE_FCOE + /* Turn off FCoE offload */ + if (adapter->flags & (IXGBE_FLAG_FCOE_CAPABLE | + IXGBE_FLAG_FCOE_ENABLED)) { + adapter->flags &= ~IXGBE_FLAG_FCOE_ENABLED; + adapter->ring_feature[RING_F_FCOE].indices = 0; + netdev->features &= ~NETIF_F_FCOE_CRC; + netdev->features &= ~NETIF_F_FSO; + netdev->fcoe_ddp_xid = 0; + } +#endif /* IXGBE_FCOE */ ixgbe_init_interrupt_scheme(adapter); if (netif_running(netdev)) netdev->netdev_ops->ndo_open(netdev); @@ -169,6 +195,8 @@ static void ixgbe_dcbnl_get_perm_hw_addr(struct net_device *netdev, struct ixgbe_adapter *adapter = netdev_priv(netdev); int i, j; + memset(perm_addr, 0xff, MAX_ADDR_LEN); + for (i = 0; i < netdev->addr_len; i++) perm_addr[i] = adapter->hw.mac.perm_addr[i]; diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index a3061aacffd8..200454f30f6a 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -34,6 +34,7 @@ #include <linux/in.h> #include <linux/ip.h> #include <linux/tcp.h> +#include <linux/pkt_sched.h> #include <linux/ipv6.h> #include <net/checksum.h> #include <net/ip6_checksum.h> @@ -510,8 +511,11 @@ static void ixgbe_receive_skb(struct ixgbe_q_vector *q_vector, * @skb: skb currently being received and modified **/ static inline void ixgbe_rx_checksum(struct ixgbe_adapter *adapter, - u32 status_err, struct sk_buff *skb) + union ixgbe_adv_rx_desc *rx_desc, + struct sk_buff *skb) { + u32 status_err = le32_to_cpu(rx_desc->wb.upper.status_error); + skb->ip_summed = CHECKSUM_NONE; /* Rx csum disabled */ @@ -529,6 +533,16 @@ static inline void ixgbe_rx_checksum(struct ixgbe_adapter *adapter, return; if (status_err & IXGBE_RXDADV_ERR_TCPE) { + u16 pkt_info = rx_desc->wb.lower.lo_dword.hs_rss.pkt_info; + + /* + * 82599 errata, UDP frames with a 0 checksum can be marked as + * checksum errors. + */ + if ((pkt_info & IXGBE_RXDADV_PKTTYPE_UDP) && + (adapter->hw.mac.type == ixgbe_mac_82599EB)) + return; + adapter->hw_csum_rx_error++; return; } @@ -802,7 +816,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, goto next_desc; } - ixgbe_rx_checksum(adapter, staterr, skb); + ixgbe_rx_checksum(adapter, rx_desc, skb); /* probably a little skewed due to removing CRC */ total_rx_bytes += skb->len; @@ -3130,7 +3144,11 @@ static inline bool ixgbe_set_fcoe_queues(struct ixgbe_adapter *adapter) #endif if (adapter->flags & IXGBE_FLAG_RSS_ENABLED) { DPRINTK(PROBE, INFO, "FCOE enabled with RSS \n"); - ixgbe_set_rss_queues(adapter); + if ((adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE) || + (adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE)) + ixgbe_set_fdir_queues(adapter); + else + ixgbe_set_rss_queues(adapter); } /* adding FCoE rx rings to the end */ f->mask = adapter->num_rx_queues; @@ -3388,7 +3406,12 @@ static inline bool ixgbe_cache_ring_fcoe(struct ixgbe_adapter *adapter) } #endif /* CONFIG_IXGBE_DCB */ if (adapter->flags & IXGBE_FLAG_RSS_ENABLED) { - ixgbe_cache_ring_rss(adapter); + if ((adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE) || + (adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE)) + ixgbe_cache_ring_fdir(adapter); + else + ixgbe_cache_ring_rss(adapter); + fcoe_i = f->mask; } for (i = 0; i < f->indices; i++, fcoe_i++) @@ -3797,8 +3820,9 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter) adapter->atr_sample_rate = 20; adapter->fdir_pballoc = 0; #ifdef IXGBE_FCOE - adapter->flags |= IXGBE_FLAG_FCOE_ENABLED; - adapter->ring_feature[RING_F_FCOE].indices = IXGBE_FCRETA_SIZE; + adapter->flags |= IXGBE_FLAG_FCOE_CAPABLE; + adapter->flags &= ~IXGBE_FLAG_FCOE_ENABLED; + adapter->ring_feature[RING_F_FCOE].indices = 0; #endif /* IXGBE_FCOE */ } @@ -5116,9 +5140,6 @@ static int ixgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) int count = 0; unsigned int f; - r_idx = skb->queue_mapping; - tx_ring = &adapter->tx_ring[r_idx]; - if (adapter->vlgrp && vlan_tx_tag_present(skb)) { tx_flags |= vlan_tx_tag_get(skb); if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) { @@ -5128,11 +5149,19 @@ static int ixgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT; tx_flags |= IXGBE_TX_FLAGS_VLAN; } else if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) { - tx_flags |= (skb->queue_mapping << 13); - tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT; - tx_flags |= IXGBE_TX_FLAGS_VLAN; + if (skb->priority != TC_PRIO_CONTROL) { + tx_flags |= (skb->queue_mapping << 13); + tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT; + tx_flags |= IXGBE_TX_FLAGS_VLAN; + } else { + skb->queue_mapping = + adapter->ring_feature[RING_F_DCB].indices-1; + } } + r_idx = skb->queue_mapping; + tx_ring = &adapter->tx_ring[r_idx]; + if ((adapter->flags & IXGBE_FLAG_FCOE_ENABLED) && (skb->protocol == htons(ETH_P_FCOE))) tx_flags |= IXGBE_TX_FLAGS_FCOE; @@ -5571,22 +5600,11 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, #endif #ifdef IXGBE_FCOE - if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED) { + if (adapter->flags & IXGBE_FLAG_FCOE_CAPABLE) { if (hw->mac.ops.get_device_caps) { hw->mac.ops.get_device_caps(hw, &device_caps); - if (!(device_caps & IXGBE_DEVICE_CAPS_FCOE_OFFLOADS)) { - netdev->features |= NETIF_F_FCOE_CRC; - netdev->features |= NETIF_F_FSO; - netdev->fcoe_ddp_xid = IXGBE_FCOE_DDP_MAX - 1; - DPRINTK(DRV, INFO, "FCoE enabled, " - "disabling Flow Director\n"); - adapter->flags &= ~IXGBE_FLAG_FDIR_HASH_CAPABLE; - adapter->flags &= - ~IXGBE_FLAG_FDIR_PERFECT_CAPABLE; - adapter->atr_sample_rate = 0; - } else { - adapter->flags &= ~IXGBE_FLAG_FCOE_ENABLED; - } + if (device_caps & IXGBE_DEVICE_CAPS_FCOE_OFFLOADS) + adapter->flags &= ~IXGBE_FLAG_FCOE_CAPABLE; } } #endif /* IXGBE_FCOE */ @@ -5635,7 +5653,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, adapter->wol = 0; break; } - device_init_wakeup(&adapter->pdev->dev, true); device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); /* pick up the PCI bus settings for reporting later */ diff --git a/drivers/net/jazzsonic.c b/drivers/net/jazzsonic.c index d12106b47bf2..2f286091394d 100644 --- a/drivers/net/jazzsonic.c +++ b/drivers/net/jazzsonic.c @@ -229,6 +229,7 @@ static int __init jazz_sonic_probe(struct platform_device *pdev) lp = netdev_priv(dev); lp->device = &pdev->dev; SET_NETDEV_DEV(dev, &pdev->dev); + platform_set_drvdata(pdev, dev); netdev_boot_setup_check(dev); diff --git a/drivers/net/ks8851.c b/drivers/net/ks8851.c new file mode 100644 index 000000000000..9a1dea60c1c4 --- /dev/null +++ b/drivers/net/ks8851.c @@ -0,0 +1,1322 @@ +/* drivers/net/ks8651.c + * + * Copyright 2009 Simtec Electronics + * http://www.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * 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. + */ + +#define DEBUG + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/cache.h> +#include <linux/crc32.h> +#include <linux/mii.h> + +#include <linux/spi/spi.h> + +#include "ks8851.h" + +/** + * struct ks8851_rxctrl - KS8851 driver rx control + * @mchash: Multicast hash-table data. + * @rxcr1: KS_RXCR1 register setting + * @rxcr2: KS_RXCR2 register setting + * + * Representation of the settings needs to control the receive filtering + * such as the multicast hash-filter and the receive register settings. This + * is used to make the job of working out if the receive settings change and + * then issuing the new settings to the worker that will send the necessary + * commands. + */ +struct ks8851_rxctrl { + u16 mchash[4]; + u16 rxcr1; + u16 rxcr2; +}; + +/** + * union ks8851_tx_hdr - tx header data + * @txb: The header as bytes + * @txw: The header as 16bit, little-endian words + * + * A dual representation of the tx header data to allow + * access to individual bytes, and to allow 16bit accesses + * with 16bit alignment. + */ +union ks8851_tx_hdr { + u8 txb[6]; + __le16 txw[3]; +}; + +/** + * struct ks8851_net - KS8851 driver private data + * @netdev: The network device we're bound to + * @spidev: The spi device we're bound to. + * @lock: Lock to ensure that the device is not accessed when busy. + * @statelock: Lock on this structure for tx list. + * @mii: The MII state information for the mii calls. + * @rxctrl: RX settings for @rxctrl_work. + * @tx_work: Work queue for tx packets + * @irq_work: Work queue for servicing interrupts + * @rxctrl_work: Work queue for updating RX mode and multicast lists + * @txq: Queue of packets for transmission. + * @spi_msg1: pre-setup SPI transfer with one message, @spi_xfer1. + * @spi_msg2: pre-setup SPI transfer with two messages, @spi_xfer2. + * @txh: Space for generating packet TX header in DMA-able data + * @rxd: Space for receiving SPI data, in DMA-able space. + * @txd: Space for transmitting SPI data, in DMA-able space. + * @msg_enable: The message flags controlling driver output (see ethtool). + * @fid: Incrementing frame id tag. + * @rc_ier: Cached copy of KS_IER. + * @rc_rxqcr: Cached copy of KS_RXQCR. + * + * The @lock ensures that the chip is protected when certain operations are + * in progress. When the read or write packet transfer is in progress, most + * of the chip registers are not ccessible until the transfer is finished and + * the DMA has been de-asserted. + * + * The @statelock is used to protect information in the structure which may + * need to be accessed via several sources, such as the network driver layer + * or one of the work queues. + * + * We align the buffers we may use for rx/tx to ensure that if the SPI driver + * wants to DMA map them, it will not have any problems with data the driver + * modifies. + */ +struct ks8851_net { + struct net_device *netdev; + struct spi_device *spidev; + struct mutex lock; + spinlock_t statelock; + + union ks8851_tx_hdr txh ____cacheline_aligned; + u8 rxd[8]; + u8 txd[8]; + + u32 msg_enable ____cacheline_aligned; + u16 tx_space; + u8 fid; + + u16 rc_ier; + u16 rc_rxqcr; + + struct mii_if_info mii; + struct ks8851_rxctrl rxctrl; + + struct work_struct tx_work; + struct work_struct irq_work; + struct work_struct rxctrl_work; + + struct sk_buff_head txq; + + struct spi_message spi_msg1; + struct spi_message spi_msg2; + struct spi_transfer spi_xfer1; + struct spi_transfer spi_xfer2[2]; +}; + +static int msg_enable; + +#define ks_info(_ks, _msg...) dev_info(&(_ks)->spidev->dev, _msg) +#define ks_warn(_ks, _msg...) dev_warn(&(_ks)->spidev->dev, _msg) +#define ks_dbg(_ks, _msg...) dev_dbg(&(_ks)->spidev->dev, _msg) +#define ks_err(_ks, _msg...) dev_err(&(_ks)->spidev->dev, _msg) + +/* shift for byte-enable data */ +#define BYTE_EN(_x) ((_x) << 2) + +/* turn register number and byte-enable mask into data for start of packet */ +#define MK_OP(_byteen, _reg) (BYTE_EN(_byteen) | (_reg) << (8+2) | (_reg) >> 6) + +/* SPI register read/write calls. + * + * All these calls issue SPI transactions to access the chip's registers. They + * all require that the necessary lock is held to prevent accesses when the + * chip is busy transfering packet data (RX/TX FIFO accesses). + */ + +/** + * ks8851_wrreg16 - write 16bit register value to chip + * @ks: The chip state + * @reg: The register address + * @val: The value to write + * + * Issue a write to put the value @val into the register specified in @reg. + */ +static void ks8851_wrreg16(struct ks8851_net *ks, unsigned reg, unsigned val) +{ + struct spi_transfer *xfer = &ks->spi_xfer1; + struct spi_message *msg = &ks->spi_msg1; + __le16 txb[2]; + int ret; + + txb[0] = cpu_to_le16(MK_OP(reg & 2 ? 0xC : 0x03, reg) | KS_SPIOP_WR); + txb[1] = cpu_to_le16(val); + + xfer->tx_buf = txb; + xfer->rx_buf = NULL; + xfer->len = 4; + + ret = spi_sync(ks->spidev, msg); + if (ret < 0) + ks_err(ks, "spi_sync() failed\n"); +} + +/** + * ks8851_rx_1msg - select whether to use one or two messages for spi read + * @ks: The device structure + * + * Return whether to generate a single message with a tx and rx buffer + * supplied to spi_sync(), or alternatively send the tx and rx buffers + * as separate messages. + * + * Depending on the hardware in use, a single message may be more efficient + * on interrupts or work done by the driver. + * + * This currently always returns true until we add some per-device data passed + * from the platform code to specify which mode is better. + */ +static inline bool ks8851_rx_1msg(struct ks8851_net *ks) +{ + return true; +} + +/** + * ks8851_rdreg - issue read register command and return the data + * @ks: The device state + * @op: The register address and byte enables in message format. + * @rxb: The RX buffer to return the result into + * @rxl: The length of data expected. + * + * This is the low level read call that issues the necessary spi message(s) + * to read data from the register specified in @op. + */ +static void ks8851_rdreg(struct ks8851_net *ks, unsigned op, + u8 *rxb, unsigned rxl) +{ + struct spi_transfer *xfer; + struct spi_message *msg; + __le16 *txb = (__le16 *)ks->txd; + u8 *trx = ks->rxd; + int ret; + + txb[0] = cpu_to_le16(op | KS_SPIOP_RD); + + if (ks8851_rx_1msg(ks)) { + msg = &ks->spi_msg1; + xfer = &ks->spi_xfer1; + + xfer->tx_buf = txb; + xfer->rx_buf = trx; + xfer->len = rxl + 2; + } else { + msg = &ks->spi_msg2; + xfer = ks->spi_xfer2; + + xfer->tx_buf = txb; + xfer->rx_buf = NULL; + xfer->len = 2; + + xfer++; + xfer->tx_buf = NULL; + xfer->rx_buf = trx; + xfer->len = rxl; + } + + ret = spi_sync(ks->spidev, msg); + if (ret < 0) + ks_err(ks, "read: spi_sync() failed\n"); + else if (ks8851_rx_1msg(ks)) + memcpy(rxb, trx + 2, rxl); + else + memcpy(rxb, trx, rxl); +} + +/** + * ks8851_rdreg8 - read 8 bit register from device + * @ks: The chip information + * @reg: The register address + * + * Read a 8bit register from the chip, returning the result +*/ +static unsigned ks8851_rdreg8(struct ks8851_net *ks, unsigned reg) +{ + u8 rxb[1]; + + ks8851_rdreg(ks, MK_OP(1 << (reg & 3), reg), rxb, 1); + return rxb[0]; +} + +/** + * ks8851_rdreg16 - read 16 bit register from device + * @ks: The chip information + * @reg: The register address + * + * Read a 16bit register from the chip, returning the result +*/ +static unsigned ks8851_rdreg16(struct ks8851_net *ks, unsigned reg) +{ + __le16 rx = 0; + + ks8851_rdreg(ks, MK_OP(reg & 2 ? 0xC : 0x3, reg), (u8 *)&rx, 2); + return le16_to_cpu(rx); +} + +/** + * ks8851_rdreg32 - read 32 bit register from device + * @ks: The chip information + * @reg: The register address + * + * Read a 32bit register from the chip. + * + * Note, this read requires the address be aligned to 4 bytes. +*/ +static unsigned ks8851_rdreg32(struct ks8851_net *ks, unsigned reg) +{ + __le32 rx = 0; + + WARN_ON(reg & 3); + + ks8851_rdreg(ks, MK_OP(0xf, reg), (u8 *)&rx, 4); + return le32_to_cpu(rx); +} + +/** + * ks8851_soft_reset - issue one of the soft reset to the device + * @ks: The device state. + * @op: The bit(s) to set in the GRR + * + * Issue the relevant soft-reset command to the device's GRR register + * specified by @op. + * + * Note, the delays are in there as a caution to ensure that the reset + * has time to take effect and then complete. Since the datasheet does + * not currently specify the exact sequence, we have chosen something + * that seems to work with our device. + */ +static void ks8851_soft_reset(struct ks8851_net *ks, unsigned op) +{ + ks8851_wrreg16(ks, KS_GRR, op); + mdelay(1); /* wait a short time to effect reset */ + ks8851_wrreg16(ks, KS_GRR, 0); + mdelay(1); /* wait for condition to clear */ +} + +/** + * ks8851_write_mac_addr - write mac address to device registers + * @dev: The network device + * + * Update the KS8851 MAC address registers from the address in @dev. + * + * This call assumes that the chip is not running, so there is no need to + * shutdown the RXQ process whilst setting this. +*/ +static int ks8851_write_mac_addr(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + u16 *mcp = (u16 *)dev->dev_addr; + + mutex_lock(&ks->lock); + + ks8851_wrreg16(ks, KS_MARL, mcp[0]); + ks8851_wrreg16(ks, KS_MARM, mcp[1]); + ks8851_wrreg16(ks, KS_MARH, mcp[2]); + + mutex_unlock(&ks->lock); + + return 0; +} + +/** + * ks8851_init_mac - initialise the mac address + * @ks: The device structure + * + * Get or create the initial mac address for the device and then set that + * into the station address register. Currently we assume that the device + * does not have a valid mac address in it, and so we use random_ether_addr() + * to create a new one. + * + * In future, the driver should check to see if the device has an EEPROM + * attached and whether that has a valid ethernet address in it. + */ +static void ks8851_init_mac(struct ks8851_net *ks) +{ + struct net_device *dev = ks->netdev; + + random_ether_addr(dev->dev_addr); + ks8851_write_mac_addr(dev); +} + +/** + * ks8851_irq - device interrupt handler + * @irq: Interrupt number passed from the IRQ hnalder. + * @pw: The private word passed to register_irq(), our struct ks8851_net. + * + * Disable the interrupt from happening again until we've processed the + * current status by scheduling ks8851_irq_work(). + */ +static irqreturn_t ks8851_irq(int irq, void *pw) +{ + struct ks8851_net *ks = pw; + + disable_irq_nosync(irq); + schedule_work(&ks->irq_work); + return IRQ_HANDLED; +} + +/** + * ks8851_rdfifo - read data from the receive fifo + * @ks: The device state. + * @buff: The buffer address + * @len: The length of the data to read + * + * Issue an RXQ FIFO read command and read the @len ammount of data from + * the FIFO into the buffer specified by @buff. + */ +static void ks8851_rdfifo(struct ks8851_net *ks, u8 *buff, unsigned len) +{ + struct spi_transfer *xfer = ks->spi_xfer2; + struct spi_message *msg = &ks->spi_msg2; + u8 txb[1]; + int ret; + + if (netif_msg_rx_status(ks)) + ks_dbg(ks, "%s: %d@%p\n", __func__, len, buff); + + /* set the operation we're issuing */ + txb[0] = KS_SPIOP_RXFIFO; + + xfer->tx_buf = txb; + xfer->rx_buf = NULL; + xfer->len = 1; + + xfer++; + xfer->rx_buf = buff; + xfer->tx_buf = NULL; + xfer->len = len; + + ret = spi_sync(ks->spidev, msg); + if (ret < 0) + ks_err(ks, "%s: spi_sync() failed\n", __func__); +} + +/** + * ks8851_dbg_dumpkkt - dump initial packet contents to debug + * @ks: The device state + * @rxpkt: The data for the received packet + * + * Dump the initial data from the packet to dev_dbg(). +*/ +static void ks8851_dbg_dumpkkt(struct ks8851_net *ks, u8 *rxpkt) +{ + ks_dbg(ks, "pkt %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n", + rxpkt[4], rxpkt[5], rxpkt[6], rxpkt[7], + rxpkt[8], rxpkt[9], rxpkt[10], rxpkt[11], + rxpkt[12], rxpkt[13], rxpkt[14], rxpkt[15]); +} + +/** + * ks8851_rx_pkts - receive packets from the host + * @ks: The device information. + * + * This is called from the IRQ work queue when the system detects that there + * are packets in the receive queue. Find out how many packets there are and + * read them from the FIFO. + */ +static void ks8851_rx_pkts(struct ks8851_net *ks) +{ + struct sk_buff *skb; + unsigned rxfc; + unsigned rxlen; + unsigned rxstat; + u32 rxh; + u8 *rxpkt; + + rxfc = ks8851_rdreg8(ks, KS_RXFC); + + if (netif_msg_rx_status(ks)) + ks_dbg(ks, "%s: %d packets\n", __func__, rxfc); + + /* Currently we're issuing a read per packet, but we could possibly + * improve the code by issuing a single read, getting the receive + * header, allocating the packet and then reading the packet data + * out in one go. + * + * This form of operation would require us to hold the SPI bus' + * chipselect low during the entie transaction to avoid any + * reset to the data stream comming from the chip. + */ + + for (; rxfc != 0; rxfc--) { + rxh = ks8851_rdreg32(ks, KS_RXFHSR); + rxstat = rxh & 0xffff; + rxlen = rxh >> 16; + + if (netif_msg_rx_status(ks)) + ks_dbg(ks, "rx: stat 0x%04x, len 0x%04x\n", + rxstat, rxlen); + + /* the length of the packet includes the 32bit CRC */ + + /* set dma read address */ + ks8851_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI | 0x00); + + /* start the packet dma process, and set auto-dequeue rx */ + ks8851_wrreg16(ks, KS_RXQCR, + ks->rc_rxqcr | RXQCR_SDA | RXQCR_ADRFE); + + if (rxlen > 0) { + skb = netdev_alloc_skb(ks->netdev, rxlen + 2 + 8); + if (!skb) { + /* todo - dump frame and move on */ + } + + /* two bytes to ensure ip is aligned, and four bytes + * for the status header and 4 bytes of garbage */ + skb_reserve(skb, 2 + 4 + 4); + + rxpkt = skb_put(skb, rxlen - 4) - 8; + + /* align the packet length to 4 bytes, and add 4 bytes + * as we're getting the rx status header as well */ + ks8851_rdfifo(ks, rxpkt, ALIGN(rxlen, 4) + 8); + + if (netif_msg_pktdata(ks)) + ks8851_dbg_dumpkkt(ks, rxpkt); + + skb->protocol = eth_type_trans(skb, ks->netdev); + netif_rx(skb); + + ks->netdev->stats.rx_packets++; + ks->netdev->stats.rx_bytes += rxlen - 4; + } + + ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr); + } +} + +/** + * ks8851_irq_work - work queue handler for dealing with interrupt requests + * @work: The work structure that was scheduled by schedule_work() + * + * This is the handler invoked when the ks8851_irq() is called to find out + * what happened, as we cannot allow ourselves to sleep whilst waiting for + * anything other process has the chip's lock. + * + * Read the interrupt status, work out what needs to be done and then clear + * any of the interrupts that are not needed. + */ +static void ks8851_irq_work(struct work_struct *work) +{ + struct ks8851_net *ks = container_of(work, struct ks8851_net, irq_work); + unsigned status; + unsigned handled = 0; + + mutex_lock(&ks->lock); + + status = ks8851_rdreg16(ks, KS_ISR); + + if (netif_msg_intr(ks)) + dev_dbg(&ks->spidev->dev, "%s: status 0x%04x\n", + __func__, status); + + if (status & IRQ_LCI) { + /* should do something about checking link status */ + handled |= IRQ_LCI; + } + + if (status & IRQ_LDI) { + u16 pmecr = ks8851_rdreg16(ks, KS_PMECR); + pmecr &= ~PMECR_WKEVT_MASK; + ks8851_wrreg16(ks, KS_PMECR, pmecr | PMECR_WKEVT_LINK); + + handled |= IRQ_LDI; + } + + if (status & IRQ_RXPSI) + handled |= IRQ_RXPSI; + + if (status & IRQ_TXI) { + handled |= IRQ_TXI; + + /* no lock here, tx queue should have been stopped */ + + /* update our idea of how much tx space is available to the + * system */ + ks->tx_space = ks8851_rdreg16(ks, KS_TXMIR); + + if (netif_msg_intr(ks)) + ks_dbg(ks, "%s: txspace %d\n", __func__, ks->tx_space); + } + + if (status & IRQ_RXI) + handled |= IRQ_RXI; + + if (status & IRQ_SPIBEI) { + dev_err(&ks->spidev->dev, "%s: spi bus error\n", __func__); + handled |= IRQ_SPIBEI; + } + + ks8851_wrreg16(ks, KS_ISR, handled); + + if (status & IRQ_RXI) { + /* the datasheet says to disable the rx interrupt during + * packet read-out, however we're masking the interrupt + * from the device so do not bother masking just the RX + * from the device. */ + + ks8851_rx_pkts(ks); + } + + /* if something stopped the rx process, probably due to wanting + * to change the rx settings, then do something about restarting + * it. */ + if (status & IRQ_RXPSI) { + struct ks8851_rxctrl *rxc = &ks->rxctrl; + + /* update the multicast hash table */ + ks8851_wrreg16(ks, KS_MAHTR0, rxc->mchash[0]); + ks8851_wrreg16(ks, KS_MAHTR1, rxc->mchash[1]); + ks8851_wrreg16(ks, KS_MAHTR2, rxc->mchash[2]); + ks8851_wrreg16(ks, KS_MAHTR3, rxc->mchash[3]); + + ks8851_wrreg16(ks, KS_RXCR2, rxc->rxcr2); + ks8851_wrreg16(ks, KS_RXCR1, rxc->rxcr1); + } + + mutex_unlock(&ks->lock); + + if (status & IRQ_TXI) + netif_wake_queue(ks->netdev); + + enable_irq(ks->netdev->irq); +} + +/** + * calc_txlen - calculate size of message to send packet + * @len: Lenght of data + * + * Returns the size of the TXFIFO message needed to send + * this packet. + */ +static inline unsigned calc_txlen(unsigned len) +{ + return ALIGN(len + 4, 4); +} + +/** + * ks8851_wrpkt - write packet to TX FIFO + * @ks: The device state. + * @txp: The sk_buff to transmit. + * @irq: IRQ on completion of the packet. + * + * Send the @txp to the chip. This means creating the relevant packet header + * specifying the length of the packet and the other information the chip + * needs, such as IRQ on completion. Send the header and the packet data to + * the device. + */ +static void ks8851_wrpkt(struct ks8851_net *ks, struct sk_buff *txp, bool irq) +{ + struct spi_transfer *xfer = ks->spi_xfer2; + struct spi_message *msg = &ks->spi_msg2; + unsigned fid = 0; + int ret; + + if (netif_msg_tx_queued(ks)) + dev_dbg(&ks->spidev->dev, "%s: skb %p, %d@%p, irq %d\n", + __func__, txp, txp->len, txp->data, irq); + + fid = ks->fid++; + fid &= TXFR_TXFID_MASK; + + if (irq) + fid |= TXFR_TXIC; /* irq on completion */ + + /* start header at txb[1] to align txw entries */ + ks->txh.txb[1] = KS_SPIOP_TXFIFO; + ks->txh.txw[1] = cpu_to_le16(fid); + ks->txh.txw[2] = cpu_to_le16(txp->len); + + xfer->tx_buf = &ks->txh.txb[1]; + xfer->rx_buf = NULL; + xfer->len = 5; + + xfer++; + xfer->tx_buf = txp->data; + xfer->rx_buf = NULL; + xfer->len = ALIGN(txp->len, 4); + + ret = spi_sync(ks->spidev, msg); + if (ret < 0) + ks_err(ks, "%s: spi_sync() failed\n", __func__); +} + +/** + * ks8851_done_tx - update and then free skbuff after transmitting + * @ks: The device state + * @txb: The buffer transmitted + */ +static void ks8851_done_tx(struct ks8851_net *ks, struct sk_buff *txb) +{ + struct net_device *dev = ks->netdev; + + dev->stats.tx_bytes += txb->len; + dev->stats.tx_packets++; + + dev_kfree_skb(txb); +} + +/** + * ks8851_tx_work - process tx packet(s) + * @work: The work strucutre what was scheduled. + * + * This is called when a number of packets have been scheduled for + * transmission and need to be sent to the device. + */ +static void ks8851_tx_work(struct work_struct *work) +{ + struct ks8851_net *ks = container_of(work, struct ks8851_net, tx_work); + struct sk_buff *txb; + bool last = false; + + mutex_lock(&ks->lock); + + while (!last) { + txb = skb_dequeue(&ks->txq); + last = skb_queue_empty(&ks->txq); + + ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr | RXQCR_SDA); + ks8851_wrpkt(ks, txb, last); + ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr); + ks8851_wrreg16(ks, KS_TXQCR, TXQCR_METFE); + + ks8851_done_tx(ks, txb); + } + + mutex_unlock(&ks->lock); +} + +/** + * ks8851_set_powermode - set power mode of the device + * @ks: The device state + * @pwrmode: The power mode value to write to KS_PMECR. + * + * Change the power mode of the chip. + */ +static void ks8851_set_powermode(struct ks8851_net *ks, unsigned pwrmode) +{ + unsigned pmecr; + + if (netif_msg_hw(ks)) + ks_dbg(ks, "setting power mode %d\n", pwrmode); + + pmecr = ks8851_rdreg16(ks, KS_PMECR); + pmecr &= ~PMECR_PM_MASK; + pmecr |= pwrmode; + + ks8851_wrreg16(ks, KS_PMECR, pmecr); +} + +/** + * ks8851_net_open - open network device + * @dev: The network device being opened. + * + * Called when the network device is marked active, such as a user executing + * 'ifconfig up' on the device. + */ +static int ks8851_net_open(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + + /* lock the card, even if we may not actually be doing anything + * else at the moment */ + mutex_lock(&ks->lock); + + if (netif_msg_ifup(ks)) + ks_dbg(ks, "opening %s\n", dev->name); + + /* bring chip out of any power saving mode it was in */ + ks8851_set_powermode(ks, PMECR_PM_NORMAL); + + /* issue a soft reset to the RX/TX QMU to put it into a known + * state. */ + ks8851_soft_reset(ks, GRR_QMU); + + /* setup transmission parameters */ + + ks8851_wrreg16(ks, KS_TXCR, (TXCR_TXE | /* enable transmit process */ + TXCR_TXPE | /* pad to min length */ + TXCR_TXCRC | /* add CRC */ + TXCR_TXFCE)); /* enable flow control */ + + /* auto-increment tx data, reset tx pointer */ + ks8851_wrreg16(ks, KS_TXFDPR, TXFDPR_TXFPAI); + + /* setup receiver control */ + + ks8851_wrreg16(ks, KS_RXCR1, (RXCR1_RXPAFMA | /* from mac filter */ + RXCR1_RXFCE | /* enable flow control */ + RXCR1_RXBE | /* broadcast enable */ + RXCR1_RXUE | /* unicast enable */ + RXCR1_RXE)); /* enable rx block */ + + /* transfer entire frames out in one go */ + ks8851_wrreg16(ks, KS_RXCR2, RXCR2_SRDBL_FRAME); + + /* set receive counter timeouts */ + ks8851_wrreg16(ks, KS_RXDTTR, 1000); /* 1ms after first frame to IRQ */ + ks8851_wrreg16(ks, KS_RXDBCTR, 4096); /* >4Kbytes in buffer to IRQ */ + ks8851_wrreg16(ks, KS_RXFCTR, 10); /* 10 frames to IRQ */ + + ks->rc_rxqcr = (RXQCR_RXFCTE | /* IRQ on frame count exceeded */ + RXQCR_RXDBCTE | /* IRQ on byte count exceeded */ + RXQCR_RXDTTE); /* IRQ on time exceeded */ + + ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr); + + /* clear then enable interrupts */ + +#define STD_IRQ (IRQ_LCI | /* Link Change */ \ + IRQ_TXI | /* TX done */ \ + IRQ_RXI | /* RX done */ \ + IRQ_SPIBEI | /* SPI bus error */ \ + IRQ_TXPSI | /* TX process stop */ \ + IRQ_RXPSI) /* RX process stop */ + + ks->rc_ier = STD_IRQ; + ks8851_wrreg16(ks, KS_ISR, STD_IRQ); + ks8851_wrreg16(ks, KS_IER, STD_IRQ); + + netif_start_queue(ks->netdev); + + if (netif_msg_ifup(ks)) + ks_dbg(ks, "network device %s up\n", dev->name); + + mutex_unlock(&ks->lock); + return 0; +} + +/** + * ks8851_net_stop - close network device + * @dev: The device being closed. + * + * Called to close down a network device which has been active. Cancell any + * work, shutdown the RX and TX process and then place the chip into a low + * power state whilst it is not being used. + */ +static int ks8851_net_stop(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + + if (netif_msg_ifdown(ks)) + ks_info(ks, "%s: shutting down\n", dev->name); + + netif_stop_queue(dev); + + mutex_lock(&ks->lock); + + /* stop any outstanding work */ + flush_work(&ks->irq_work); + flush_work(&ks->tx_work); + flush_work(&ks->rxctrl_work); + + /* turn off the IRQs and ack any outstanding */ + ks8851_wrreg16(ks, KS_IER, 0x0000); + ks8851_wrreg16(ks, KS_ISR, 0xffff); + + /* shutdown RX process */ + ks8851_wrreg16(ks, KS_RXCR1, 0x0000); + + /* shutdown TX process */ + ks8851_wrreg16(ks, KS_TXCR, 0x0000); + + /* set powermode to soft power down to save power */ + ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN); + + /* ensure any queued tx buffers are dumped */ + while (!skb_queue_empty(&ks->txq)) { + struct sk_buff *txb = skb_dequeue(&ks->txq); + + if (netif_msg_ifdown(ks)) + ks_dbg(ks, "%s: freeing txb %p\n", __func__, txb); + + dev_kfree_skb(txb); + } + + mutex_unlock(&ks->lock); + return 0; +} + +/** + * ks8851_start_xmit - transmit packet + * @skb: The buffer to transmit + * @dev: The device used to transmit the packet. + * + * Called by the network layer to transmit the @skb. Queue the packet for + * the device and schedule the necessary work to transmit the packet when + * it is free. + * + * We do this to firstly avoid sleeping with the network device locked, + * and secondly so we can round up more than one packet to transmit which + * means we can try and avoid generating too many transmit done interrupts. + */ +static int ks8851_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + unsigned needed = calc_txlen(skb->len); + int ret = NETDEV_TX_OK; + + if (netif_msg_tx_queued(ks)) + ks_dbg(ks, "%s: skb %p, %d@%p\n", __func__, + skb, skb->len, skb->data); + + spin_lock(&ks->statelock); + + if (needed > ks->tx_space) { + netif_stop_queue(dev); + ret = NETDEV_TX_BUSY; + } else { + ks->tx_space -= needed; + skb_queue_tail(&ks->txq, skb); + } + + spin_unlock(&ks->statelock); + schedule_work(&ks->tx_work); + + return ret; +} + +/** + * ks8851_rxctrl_work - work handler to change rx mode + * @work: The work structure this belongs to. + * + * Lock the device and issue the necessary changes to the receive mode from + * the network device layer. This is done so that we can do this without + * having to sleep whilst holding the network device lock. + * + * Since the recommendation from Micrel is that the RXQ is shutdown whilst the + * receive parameters are programmed, we issue a write to disable the RXQ and + * then wait for the interrupt handler to be triggered once the RXQ shutdown is + * complete. The interrupt handler then writes the new values into the chip. + */ +static void ks8851_rxctrl_work(struct work_struct *work) +{ + struct ks8851_net *ks = container_of(work, struct ks8851_net, rxctrl_work); + + mutex_lock(&ks->lock); + + /* need to shutdown RXQ before modifying filter parameters */ + ks8851_wrreg16(ks, KS_RXCR1, 0x00); + + mutex_unlock(&ks->lock); +} + +static void ks8851_set_rx_mode(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + struct ks8851_rxctrl rxctrl; + + memset(&rxctrl, 0, sizeof(rxctrl)); + + if (dev->flags & IFF_PROMISC) { + /* interface to receive everything */ + + rxctrl.rxcr1 = RXCR1_RXAE | RXCR1_RXINVF; + } else if (dev->flags & IFF_ALLMULTI) { + /* accept all multicast packets */ + + rxctrl.rxcr1 = (RXCR1_RXME | RXCR1_RXAE | + RXCR1_RXPAFMA | RXCR1_RXMAFMA); + } else if (dev->flags & IFF_MULTICAST && dev->mc_count > 0) { + struct dev_mc_list *mcptr = dev->mc_list; + u32 crc; + int i; + + /* accept some multicast */ + + for (i = dev->mc_count; i > 0; i--) { + crc = ether_crc(ETH_ALEN, mcptr->dmi_addr); + crc >>= (32 - 6); /* get top six bits */ + + rxctrl.mchash[crc >> 4] |= (1 << (crc & 0xf)); + mcptr = mcptr->next; + } + + rxctrl.rxcr1 = RXCR1_RXME | RXCR1_RXAE | RXCR1_RXPAFMA; + } else { + /* just accept broadcast / unicast */ + rxctrl.rxcr1 = RXCR1_RXPAFMA; + } + + rxctrl.rxcr1 |= (RXCR1_RXUE | /* unicast enable */ + RXCR1_RXBE | /* broadcast enable */ + RXCR1_RXE | /* RX process enable */ + RXCR1_RXFCE); /* enable flow control */ + + rxctrl.rxcr2 |= RXCR2_SRDBL_FRAME; + + /* schedule work to do the actual set of the data if needed */ + + spin_lock(&ks->statelock); + + if (memcmp(&rxctrl, &ks->rxctrl, sizeof(rxctrl)) != 0) { + memcpy(&ks->rxctrl, &rxctrl, sizeof(ks->rxctrl)); + schedule_work(&ks->rxctrl_work); + } + + spin_unlock(&ks->statelock); +} + +static int ks8851_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *sa = addr; + + if (netif_running(dev)) + return -EBUSY; + + if (!is_valid_ether_addr(sa->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN); + return ks8851_write_mac_addr(dev); +} + +static int ks8851_net_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + struct ks8851_net *ks = netdev_priv(dev); + + if (!netif_running(dev)) + return -EINVAL; + + return generic_mii_ioctl(&ks->mii, if_mii(req), cmd, NULL); +} + +static const struct net_device_ops ks8851_netdev_ops = { + .ndo_open = ks8851_net_open, + .ndo_stop = ks8851_net_stop, + .ndo_do_ioctl = ks8851_net_ioctl, + .ndo_start_xmit = ks8851_start_xmit, + .ndo_set_mac_address = ks8851_set_mac_address, + .ndo_set_rx_mode = ks8851_set_rx_mode, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + +/* ethtool support */ + +static void ks8851_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *di) +{ + strlcpy(di->driver, "KS8851", sizeof(di->driver)); + strlcpy(di->version, "1.00", sizeof(di->version)); + strlcpy(di->bus_info, dev_name(dev->dev.parent), sizeof(di->bus_info)); +} + +static u32 ks8851_get_msglevel(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + return ks->msg_enable; +} + +static void ks8851_set_msglevel(struct net_device *dev, u32 to) +{ + struct ks8851_net *ks = netdev_priv(dev); + ks->msg_enable = to; +} + +static int ks8851_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct ks8851_net *ks = netdev_priv(dev); + return mii_ethtool_gset(&ks->mii, cmd); +} + +static int ks8851_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct ks8851_net *ks = netdev_priv(dev); + return mii_ethtool_sset(&ks->mii, cmd); +} + +static u32 ks8851_get_link(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + return mii_link_ok(&ks->mii); +} + +static int ks8851_nway_reset(struct net_device *dev) +{ + struct ks8851_net *ks = netdev_priv(dev); + return mii_nway_restart(&ks->mii); +} + +static const struct ethtool_ops ks8851_ethtool_ops = { + .get_drvinfo = ks8851_get_drvinfo, + .get_msglevel = ks8851_get_msglevel, + .set_msglevel = ks8851_set_msglevel, + .get_settings = ks8851_get_settings, + .set_settings = ks8851_set_settings, + .get_link = ks8851_get_link, + .nway_reset = ks8851_nway_reset, +}; + +/* MII interface controls */ + +/** + * ks8851_phy_reg - convert MII register into a KS8851 register + * @reg: MII register number. + * + * Return the KS8851 register number for the corresponding MII PHY register + * if possible. Return zero if the MII register has no direct mapping to the + * KS8851 register set. + */ +static int ks8851_phy_reg(int reg) +{ + switch (reg) { + case MII_BMCR: + return KS_P1MBCR; + case MII_BMSR: + return KS_P1MBSR; + case MII_PHYSID1: + return KS_PHY1ILR; + case MII_PHYSID2: + return KS_PHY1IHR; + case MII_ADVERTISE: + return KS_P1ANAR; + case MII_LPA: + return KS_P1ANLPR; + } + + return 0x0; +} + +/** + * ks8851_phy_read - MII interface PHY register read. + * @dev: The network device the PHY is on. + * @phy_addr: Address of PHY (ignored as we only have one) + * @reg: The register to read. + * + * This call reads data from the PHY register specified in @reg. Since the + * device does not support all the MII registers, the non-existant values + * are always returned as zero. + * + * We return zero for unsupported registers as the MII code does not check + * the value returned for any error status, and simply returns it to the + * caller. The mii-tool that the driver was tested with takes any -ve error + * as real PHY capabilities, thus displaying incorrect data to the user. + */ +static int ks8851_phy_read(struct net_device *dev, int phy_addr, int reg) +{ + struct ks8851_net *ks = netdev_priv(dev); + int ksreg; + int result; + + ksreg = ks8851_phy_reg(reg); + if (!ksreg) + return 0x0; /* no error return allowed, so use zero */ + + mutex_lock(&ks->lock); + result = ks8851_rdreg16(ks, ksreg); + mutex_unlock(&ks->lock); + + return result; +} + +static void ks8851_phy_write(struct net_device *dev, + int phy, int reg, int value) +{ + struct ks8851_net *ks = netdev_priv(dev); + int ksreg; + + ksreg = ks8851_phy_reg(reg); + if (ksreg) { + mutex_lock(&ks->lock); + ks8851_wrreg16(ks, ksreg, value); + mutex_unlock(&ks->lock); + } +} + +/** + * ks8851_read_selftest - read the selftest memory info. + * @ks: The device state + * + * Read and check the TX/RX memory selftest information. + */ +static int ks8851_read_selftest(struct ks8851_net *ks) +{ + unsigned both_done = MBIR_TXMBF | MBIR_RXMBF; + int ret = 0; + unsigned rd; + + rd = ks8851_rdreg16(ks, KS_MBIR); + + if ((rd & both_done) != both_done) { + ks_warn(ks, "Memory selftest not finished\n"); + return 0; + } + + if (rd & MBIR_TXMBFA) { + ks_err(ks, "TX memory selftest fail\n"); + ret |= 1; + } + + if (rd & MBIR_RXMBFA) { + ks_err(ks, "RX memory selftest fail\n"); + ret |= 2; + } + + return 0; +} + +/* driver bus management functions */ + +static int __devinit ks8851_probe(struct spi_device *spi) +{ + struct net_device *ndev; + struct ks8851_net *ks; + int ret; + + ndev = alloc_etherdev(sizeof(struct ks8851_net)); + if (!ndev) { + dev_err(&spi->dev, "failed to alloc ethernet device\n"); + return -ENOMEM; + } + + spi->bits_per_word = 8; + + ks = netdev_priv(ndev); + + ks->netdev = ndev; + ks->spidev = spi; + ks->tx_space = 6144; + + mutex_init(&ks->lock); + spin_lock_init(&ks->statelock); + + INIT_WORK(&ks->tx_work, ks8851_tx_work); + INIT_WORK(&ks->irq_work, ks8851_irq_work); + INIT_WORK(&ks->rxctrl_work, ks8851_rxctrl_work); + + /* initialise pre-made spi transfer messages */ + + spi_message_init(&ks->spi_msg1); + spi_message_add_tail(&ks->spi_xfer1, &ks->spi_msg1); + + spi_message_init(&ks->spi_msg2); + spi_message_add_tail(&ks->spi_xfer2[0], &ks->spi_msg2); + spi_message_add_tail(&ks->spi_xfer2[1], &ks->spi_msg2); + + /* setup mii state */ + ks->mii.dev = ndev; + ks->mii.phy_id = 1, + ks->mii.phy_id_mask = 1; + ks->mii.reg_num_mask = 0xf; + ks->mii.mdio_read = ks8851_phy_read; + ks->mii.mdio_write = ks8851_phy_write; + + dev_info(&spi->dev, "message enable is %d\n", msg_enable); + + /* set the default message enable */ + ks->msg_enable = netif_msg_init(msg_enable, (NETIF_MSG_DRV | + NETIF_MSG_PROBE | + NETIF_MSG_LINK)); + + skb_queue_head_init(&ks->txq); + + SET_ETHTOOL_OPS(ndev, &ks8851_ethtool_ops); + SET_NETDEV_DEV(ndev, &spi->dev); + + dev_set_drvdata(&spi->dev, ks); + + ndev->if_port = IF_PORT_100BASET; + ndev->netdev_ops = &ks8851_netdev_ops; + ndev->irq = spi->irq; + + /* simple check for a valid chip being connected to the bus */ + + if ((ks8851_rdreg16(ks, KS_CIDER) & ~CIDER_REV_MASK) != CIDER_ID) { + dev_err(&spi->dev, "failed to read device ID\n"); + ret = -ENODEV; + goto err_id; + } + + ks8851_read_selftest(ks); + ks8851_init_mac(ks); + + ret = request_irq(spi->irq, ks8851_irq, IRQF_TRIGGER_LOW, + ndev->name, ks); + if (ret < 0) { + dev_err(&spi->dev, "failed to get irq\n"); + goto err_irq; + } + + ret = register_netdev(ndev); + if (ret) { + dev_err(&spi->dev, "failed to register network device\n"); + goto err_netdev; + } + + dev_info(&spi->dev, "revision %d, MAC %pM, IRQ %d\n", + CIDER_REV_GET(ks8851_rdreg16(ks, KS_CIDER)), + ndev->dev_addr, ndev->irq); + + return 0; + + +err_netdev: + free_irq(ndev->irq, ndev); + +err_id: +err_irq: + free_netdev(ndev); + return ret; +} + +static int __devexit ks8851_remove(struct spi_device *spi) +{ + struct ks8851_net *priv = dev_get_drvdata(&spi->dev); + + if (netif_msg_drv(priv)) + dev_info(&spi->dev, "remove"); + + unregister_netdev(priv->netdev); + free_irq(spi->irq, priv); + free_netdev(priv->netdev); + + return 0; +} + +static struct spi_driver ks8851_driver = { + .driver = { + .name = "ks8851", + .owner = THIS_MODULE, + }, + .probe = ks8851_probe, + .remove = __devexit_p(ks8851_remove), +}; + +static int __init ks8851_init(void) +{ + return spi_register_driver(&ks8851_driver); +} + +static void __exit ks8851_exit(void) +{ + spi_unregister_driver(&ks8851_driver); +} + +module_init(ks8851_init); +module_exit(ks8851_exit); + +MODULE_DESCRIPTION("KS8851 Network driver"); +MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); +MODULE_LICENSE("GPL"); + +module_param_named(message, msg_enable, int, 0); +MODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)"); diff --git a/drivers/net/ks8851.h b/drivers/net/ks8851.h new file mode 100644 index 000000000000..85abe147afbf --- /dev/null +++ b/drivers/net/ks8851.h @@ -0,0 +1,296 @@ +/* drivers/net/ks8851.h + * + * Copyright 2009 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * KS8851 register definitions + * + * 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. +*/ + +#define KS_CCR 0x08 +#define CCR_EEPROM (1 << 9) +#define CCR_SPI (1 << 8) +#define CCR_32PIN (1 << 0) + +/* MAC address registers */ +#define KS_MARL 0x10 +#define KS_MARM 0x12 +#define KS_MARH 0x14 + +#define KS_OBCR 0x20 +#define OBCR_ODS_16mA (1 << 6) + +#define KS_EEPCR 0x22 +#define EEPCR_EESA (1 << 4) +#define EEPCR_EESB (1 << 3) +#define EEPCR_EEDO (1 << 2) +#define EEPCR_EESCK (1 << 1) +#define EEPCR_EECS (1 << 0) + +#define KS_MBIR 0x24 +#define MBIR_TXMBF (1 << 12) +#define MBIR_TXMBFA (1 << 11) +#define MBIR_RXMBF (1 << 4) +#define MBIR_RXMBFA (1 << 3) + +#define KS_GRR 0x26 +#define GRR_QMU (1 << 1) +#define GRR_GSR (1 << 0) + +#define KS_WFCR 0x2A +#define WFCR_MPRXE (1 << 7) +#define WFCR_WF3E (1 << 3) +#define WFCR_WF2E (1 << 2) +#define WFCR_WF1E (1 << 1) +#define WFCR_WF0E (1 << 0) + +#define KS_WF0CRC0 0x30 +#define KS_WF0CRC1 0x32 +#define KS_WF0BM0 0x34 +#define KS_WF0BM1 0x36 +#define KS_WF0BM2 0x38 +#define KS_WF0BM3 0x3A + +#define KS_WF1CRC0 0x40 +#define KS_WF1CRC1 0x42 +#define KS_WF1BM0 0x44 +#define KS_WF1BM1 0x46 +#define KS_WF1BM2 0x48 +#define KS_WF1BM3 0x4A + +#define KS_WF2CRC0 0x50 +#define KS_WF2CRC1 0x52 +#define KS_WF2BM0 0x54 +#define KS_WF2BM1 0x56 +#define KS_WF2BM2 0x58 +#define KS_WF2BM3 0x5A + +#define KS_WF3CRC0 0x60 +#define KS_WF3CRC1 0x62 +#define KS_WF3BM0 0x64 +#define KS_WF3BM1 0x66 +#define KS_WF3BM2 0x68 +#define KS_WF3BM3 0x6A + +#define KS_TXCR 0x70 +#define TXCR_TCGICMP (1 << 8) +#define TXCR_TCGUDP (1 << 7) +#define TXCR_TCGTCP (1 << 6) +#define TXCR_TCGIP (1 << 5) +#define TXCR_FTXQ (1 << 4) +#define TXCR_TXFCE (1 << 3) +#define TXCR_TXPE (1 << 2) +#define TXCR_TXCRC (1 << 1) +#define TXCR_TXE (1 << 0) + +#define KS_TXSR 0x72 +#define TXSR_TXLC (1 << 13) +#define TXSR_TXMC (1 << 12) +#define TXSR_TXFID_MASK (0x3f << 0) +#define TXSR_TXFID_SHIFT (0) +#define TXSR_TXFID_GET(_v) (((_v) >> 0) & 0x3f) + +#define KS_RXCR1 0x74 +#define RXCR1_FRXQ (1 << 15) +#define RXCR1_RXUDPFCC (1 << 14) +#define RXCR1_RXTCPFCC (1 << 13) +#define RXCR1_RXIPFCC (1 << 12) +#define RXCR1_RXPAFMA (1 << 11) +#define RXCR1_RXFCE (1 << 10) +#define RXCR1_RXEFE (1 << 9) +#define RXCR1_RXMAFMA (1 << 8) +#define RXCR1_RXBE (1 << 7) +#define RXCR1_RXME (1 << 6) +#define RXCR1_RXUE (1 << 5) +#define RXCR1_RXAE (1 << 4) +#define RXCR1_RXINVF (1 << 1) +#define RXCR1_RXE (1 << 0) + +#define KS_RXCR2 0x76 +#define RXCR2_SRDBL_MASK (0x7 << 5) +#define RXCR2_SRDBL_SHIFT (5) +#define RXCR2_SRDBL_4B (0x0 << 5) +#define RXCR2_SRDBL_8B (0x1 << 5) +#define RXCR2_SRDBL_16B (0x2 << 5) +#define RXCR2_SRDBL_32B (0x3 << 5) +#define RXCR2_SRDBL_FRAME (0x4 << 5) +#define RXCR2_IUFFP (1 << 4) +#define RXCR2_RXIUFCEZ (1 << 3) +#define RXCR2_UDPLFE (1 << 2) +#define RXCR2_RXICMPFCC (1 << 1) +#define RXCR2_RXSAF (1 << 0) + +#define KS_TXMIR 0x78 + +#define KS_RXFHSR 0x7C +#define RXFSHR_RXFV (1 << 15) +#define RXFSHR_RXICMPFCS (1 << 13) +#define RXFSHR_RXIPFCS (1 << 12) +#define RXFSHR_RXTCPFCS (1 << 11) +#define RXFSHR_RXUDPFCS (1 << 10) +#define RXFSHR_RXBF (1 << 7) +#define RXFSHR_RXMF (1 << 6) +#define RXFSHR_RXUF (1 << 5) +#define RXFSHR_RXMR (1 << 4) +#define RXFSHR_RXFT (1 << 3) +#define RXFSHR_RXFTL (1 << 2) +#define RXFSHR_RXRF (1 << 1) +#define RXFSHR_RXCE (1 << 0) + +#define KS_RXFHBCR 0x7E +#define KS_TXQCR 0x80 +#define TXQCR_AETFE (1 << 2) +#define TXQCR_TXQMAM (1 << 1) +#define TXQCR_METFE (1 << 0) + +#define KS_RXQCR 0x82 +#define RXQCR_RXDTTS (1 << 12) +#define RXQCR_RXDBCTS (1 << 11) +#define RXQCR_RXFCTS (1 << 10) +#define RXQCR_RXIPHTOE (1 << 9) +#define RXQCR_RXDTTE (1 << 7) +#define RXQCR_RXDBCTE (1 << 6) +#define RXQCR_RXFCTE (1 << 5) +#define RXQCR_ADRFE (1 << 4) +#define RXQCR_SDA (1 << 3) +#define RXQCR_RRXEF (1 << 0) + +#define KS_TXFDPR 0x84 +#define TXFDPR_TXFPAI (1 << 14) +#define TXFDPR_TXFP_MASK (0x7ff << 0) +#define TXFDPR_TXFP_SHIFT (0) + +#define KS_RXFDPR 0x86 +#define RXFDPR_RXFPAI (1 << 14) + +#define KS_RXDTTR 0x8C +#define KS_RXDBCTR 0x8E + +#define KS_IER 0x90 +#define KS_ISR 0x92 +#define IRQ_LCI (1 << 15) +#define IRQ_TXI (1 << 14) +#define IRQ_RXI (1 << 13) +#define IRQ_RXOI (1 << 11) +#define IRQ_TXPSI (1 << 9) +#define IRQ_RXPSI (1 << 8) +#define IRQ_TXSAI (1 << 6) +#define IRQ_RXWFDI (1 << 5) +#define IRQ_RXMPDI (1 << 4) +#define IRQ_LDI (1 << 3) +#define IRQ_EDI (1 << 2) +#define IRQ_SPIBEI (1 << 1) +#define IRQ_DEDI (1 << 0) + +#define KS_RXFCTR 0x9C +#define KS_RXFC 0x9D +#define RXFCTR_RXFC_MASK (0xff << 8) +#define RXFCTR_RXFC_SHIFT (8) +#define RXFCTR_RXFC_GET(_v) (((_v) >> 8) & 0xff) +#define RXFCTR_RXFCT_MASK (0xff << 0) +#define RXFCTR_RXFCT_SHIFT (0) + +#define KS_TXNTFSR 0x9E + +#define KS_MAHTR0 0xA0 +#define KS_MAHTR1 0xA2 +#define KS_MAHTR2 0xA4 +#define KS_MAHTR3 0xA6 + +#define KS_FCLWR 0xB0 +#define KS_FCHWR 0xB2 +#define KS_FCOWR 0xB4 + +#define KS_CIDER 0xC0 +#define CIDER_ID 0x8870 +#define CIDER_REV_MASK (0x7 << 1) +#define CIDER_REV_SHIFT (1) +#define CIDER_REV_GET(_v) (((_v) >> 1) & 0x7) + +#define KS_CGCR 0xC6 + +#define KS_IACR 0xC8 +#define IACR_RDEN (1 << 12) +#define IACR_TSEL_MASK (0x3 << 10) +#define IACR_TSEL_SHIFT (10) +#define IACR_TSEL_MIB (0x3 << 10) +#define IACR_ADDR_MASK (0x1f << 0) +#define IACR_ADDR_SHIFT (0) + +#define KS_IADLR 0xD0 +#define KS_IAHDR 0xD2 + +#define KS_PMECR 0xD4 +#define PMECR_PME_DELAY (1 << 14) +#define PMECR_PME_POL (1 << 12) +#define PMECR_WOL_WAKEUP (1 << 11) +#define PMECR_WOL_MAGICPKT (1 << 10) +#define PMECR_WOL_LINKUP (1 << 9) +#define PMECR_WOL_ENERGY (1 << 8) +#define PMECR_AUTO_WAKE_EN (1 << 7) +#define PMECR_WAKEUP_NORMAL (1 << 6) +#define PMECR_WKEVT_MASK (0xf << 2) +#define PMECR_WKEVT_SHIFT (2) +#define PMECR_WKEVT_GET(_v) (((_v) >> 2) & 0xf) +#define PMECR_WKEVT_ENERGY (0x1 << 2) +#define PMECR_WKEVT_LINK (0x2 << 2) +#define PMECR_WKEVT_MAGICPKT (0x4 << 2) +#define PMECR_WKEVT_FRAME (0x8 << 2) +#define PMECR_PM_MASK (0x3 << 0) +#define PMECR_PM_SHIFT (0) +#define PMECR_PM_NORMAL (0x0 << 0) +#define PMECR_PM_ENERGY (0x1 << 0) +#define PMECR_PM_SOFTDOWN (0x2 << 0) +#define PMECR_PM_POWERSAVE (0x3 << 0) + +/* Standard MII PHY data */ +#define KS_P1MBCR 0xE4 +#define KS_P1MBSR 0xE6 +#define KS_PHY1ILR 0xE8 +#define KS_PHY1IHR 0xEA +#define KS_P1ANAR 0xEC +#define KS_P1ANLPR 0xEE + +#define KS_P1SCLMD 0xF4 +#define P1SCLMD_LEDOFF (1 << 15) +#define P1SCLMD_TXIDS (1 << 14) +#define P1SCLMD_RESTARTAN (1 << 13) +#define P1SCLMD_DISAUTOMDIX (1 << 10) +#define P1SCLMD_FORCEMDIX (1 << 9) +#define P1SCLMD_AUTONEGEN (1 << 7) +#define P1SCLMD_FORCE100 (1 << 6) +#define P1SCLMD_FORCEFDX (1 << 5) +#define P1SCLMD_ADV_FLOW (1 << 4) +#define P1SCLMD_ADV_100BT_FDX (1 << 3) +#define P1SCLMD_ADV_100BT_HDX (1 << 2) +#define P1SCLMD_ADV_10BT_FDX (1 << 1) +#define P1SCLMD_ADV_10BT_HDX (1 << 0) + +#define KS_P1CR 0xF6 +#define P1CR_HP_MDIX (1 << 15) +#define P1CR_REV_POL (1 << 13) +#define P1CR_OP_100M (1 << 10) +#define P1CR_OP_FDX (1 << 9) +#define P1CR_OP_MDI (1 << 7) +#define P1CR_AN_DONE (1 << 6) +#define P1CR_LINK_GOOD (1 << 5) +#define P1CR_PNTR_FLOW (1 << 4) +#define P1CR_PNTR_100BT_FDX (1 << 3) +#define P1CR_PNTR_100BT_HDX (1 << 2) +#define P1CR_PNTR_10BT_FDX (1 << 1) +#define P1CR_PNTR_10BT_HDX (1 << 0) + +/* TX Frame control */ + +#define TXFR_TXIC (1 << 15) +#define TXFR_TXFID_MASK (0x3f << 0) +#define TXFR_TXFID_SHIFT (0) + +/* SPI frame opcodes */ +#define KS_SPIOP_RD (0x00) +#define KS_SPIOP_WR (0x40) +#define KS_SPIOP_RXFIFO (0x80) +#define KS_SPIOP_TXFIFO (0xC0) diff --git a/drivers/net/macsonic.c b/drivers/net/macsonic.c index acd143da161d..61eabcac734c 100644 --- a/drivers/net/macsonic.c +++ b/drivers/net/macsonic.c @@ -179,7 +179,7 @@ static const struct net_device_ops macsonic_netdev_ops = { .ndo_set_mac_address = eth_mac_addr, }; -static int __init macsonic_init(struct net_device *dev) +static int __devinit macsonic_init(struct net_device *dev) { struct sonic_local* lp = netdev_priv(dev); @@ -223,7 +223,7 @@ static int __init macsonic_init(struct net_device *dev) return 0; } -static int __init mac_onboard_sonic_ethernet_addr(struct net_device *dev) +static int __devinit mac_onboard_sonic_ethernet_addr(struct net_device *dev) { struct sonic_local *lp = netdev_priv(dev); const int prom_addr = ONBOARD_SONIC_PROM_BASE; @@ -288,7 +288,7 @@ static int __init mac_onboard_sonic_ethernet_addr(struct net_device *dev) } else return 0; } -static int __init mac_onboard_sonic_probe(struct net_device *dev) +static int __devinit mac_onboard_sonic_probe(struct net_device *dev) { /* Bwahahaha */ static int once_is_more_than_enough; @@ -409,7 +409,7 @@ static int __init mac_onboard_sonic_probe(struct net_device *dev) return macsonic_init(dev); } -static int __init mac_nubus_sonic_ethernet_addr(struct net_device *dev, +static int __devinit mac_nubus_sonic_ethernet_addr(struct net_device *dev, unsigned long prom_addr, int id) { @@ -424,7 +424,7 @@ static int __init mac_nubus_sonic_ethernet_addr(struct net_device *dev, return 0; } -static int __init macsonic_ident(struct nubus_dev *ndev) +static int __devinit macsonic_ident(struct nubus_dev *ndev) { if (ndev->dr_hw == NUBUS_DRHW_ASANTE_LC && ndev->dr_sw == NUBUS_DRSW_SONIC_LC) @@ -449,7 +449,7 @@ static int __init macsonic_ident(struct nubus_dev *ndev) return -1; } -static int __init mac_nubus_sonic_probe(struct net_device *dev) +static int __devinit mac_nubus_sonic_probe(struct net_device *dev) { static int slots; struct nubus_dev* ndev = NULL; @@ -562,7 +562,7 @@ static int __init mac_nubus_sonic_probe(struct net_device *dev) return macsonic_init(dev); } -static int __init mac_sonic_probe(struct platform_device *pdev) +static int __devinit mac_sonic_probe(struct platform_device *pdev) { struct net_device *dev; struct sonic_local *lp; @@ -575,6 +575,7 @@ static int __init mac_sonic_probe(struct platform_device *pdev) lp = netdev_priv(dev); lp->device = &pdev->dev; SET_NETDEV_DEV(dev, &pdev->dev); + platform_set_drvdata(pdev, dev); /* This will catch fatal stuff like -ENOMEM as well as success */ err = mac_onboard_sonic_probe(dev); diff --git a/drivers/net/mlx4/cmd.c b/drivers/net/mlx4/cmd.c index 2845a0560b84..65ec77dc31f5 100644 --- a/drivers/net/mlx4/cmd.c +++ b/drivers/net/mlx4/cmd.c @@ -80,7 +80,9 @@ enum { /* Bad management packet (silently discarded): */ CMD_STAT_BAD_PKT = 0x30, /* More outstanding CQEs in CQ than new CQ size: */ - CMD_STAT_BAD_SIZE = 0x40 + CMD_STAT_BAD_SIZE = 0x40, + /* Multi Function device support required: */ + CMD_STAT_MULTI_FUNC_REQ = 0x50, }; enum { @@ -128,6 +130,7 @@ static int mlx4_status_to_errno(u8 status) [CMD_STAT_LAM_NOT_PRE] = -EAGAIN, [CMD_STAT_BAD_PKT] = -EINVAL, [CMD_STAT_BAD_SIZE] = -ENOMEM, + [CMD_STAT_MULTI_FUNC_REQ] = -EACCES, }; if (status >= ARRAY_SIZE(trans_table) || diff --git a/drivers/net/mlx4/en_ethtool.c b/drivers/net/mlx4/en_ethtool.c index 091f99052c91..86467b444ac6 100644 --- a/drivers/net/mlx4/en_ethtool.c +++ b/drivers/net/mlx4/en_ethtool.c @@ -220,7 +220,7 @@ static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { cmd->autoneg = AUTONEG_DISABLE; cmd->supported = SUPPORTED_10000baseT_Full; - cmd->advertising = SUPPORTED_10000baseT_Full; + cmd->advertising = ADVERTISED_1000baseT_Full; if (netif_carrier_ok(dev)) { cmd->speed = SPEED_10000; cmd->duplex = DUPLEX_FULL; diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c index 018348c01193..dac621b1e9fc 100644 --- a/drivers/net/mlx4/main.c +++ b/drivers/net/mlx4/main.c @@ -729,7 +729,10 @@ static int mlx4_init_hca(struct mlx4_dev *dev) err = mlx4_QUERY_FW(dev); if (err) { - mlx4_err(dev, "QUERY_FW command failed, aborting.\n"); + if (err == -EACCES) + mlx4_info(dev, "non-primary physical function, skipping.\n"); + else + mlx4_err(dev, "QUERY_FW command failed, aborting.\n"); return err; } @@ -1285,6 +1288,7 @@ static struct pci_device_id mlx4_pci_table[] = { { PCI_VDEVICE(MELLANOX, 0x6750) }, /* MT25408 "Hermon" EN 10GigE PCIe gen2 */ { PCI_VDEVICE(MELLANOX, 0x6372) }, /* MT25458 ConnectX EN 10GBASE-T 10GigE */ { PCI_VDEVICE(MELLANOX, 0x675a) }, /* MT25458 ConnectX EN 10GBASE-T+Gen2 10GigE */ + { PCI_VDEVICE(MELLANOX, 0x6764) }, /* MT26468 ConnectX EN 10GigE PCIe gen2*/ { 0, } }; diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index c9bfe4eea189..78c088331f57 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -130,8 +130,8 @@ static int full_duplex[MAX_UNITS]; static const char version[] __devinitconst = KERN_INFO DRV_NAME " dp8381x driver, version " DRV_VERSION ", " DRV_RELDATE "\n" - KERN_INFO " originally by Donald Becker <becker@scyld.com>\n" - KERN_INFO " 2.4.x kernel port by Jeff Garzik, Tjeerd Mulder\n"; + " originally by Donald Becker <becker@scyld.com>\n" + " 2.4.x kernel port by Jeff Garzik, Tjeerd Mulder\n"; MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); MODULE_DESCRIPTION("National Semiconductor DP8381x series PCI Ethernet driver"); diff --git a/drivers/net/ne.c b/drivers/net/ne.c index 5c3e242428f1..992dbfffdb05 100644 --- a/drivers/net/ne.c +++ b/drivers/net/ne.c @@ -321,7 +321,7 @@ static int __init ne_probe1(struct net_device *dev, unsigned long ioaddr) } if (ei_debug && version_printed++ == 0) - printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); + printk(KERN_INFO "%s%s", version1, version2); printk(KERN_INFO "NE*000 ethercard probe at %#3lx:", ioaddr); diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index e1cdba752e09..f86e05047d19 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -210,6 +210,7 @@ #define NETXEN_CTX_SIGNATURE 0xdee0 #define NETXEN_CTX_SIGNATURE_V2 0x0002dee0 #define NETXEN_CTX_RESET 0xbad0 +#define NETXEN_CTX_D3_RESET 0xacc0 #define NETXEN_RCV_PRODUCER(ringid) (ringid) #define PHAN_PEG_RCV_INITIALIZED 0xff01 @@ -773,6 +774,8 @@ struct nx_host_tx_ring { u32 crb_cmd_consumer; u32 num_desc; + struct netdev_queue *txq; + struct netxen_cmd_buffer *cmd_buf_arr; struct cmd_desc_type0 *desc_head; dma_addr_t phys_addr; diff --git a/drivers/net/netxen/netxen_nic_ctx.c b/drivers/net/netxen/netxen_nic_ctx.c index 4754f5cffad0..9f8ae4719e2f 100644 --- a/drivers/net/netxen/netxen_nic_ctx.c +++ b/drivers/net/netxen/netxen_nic_ctx.c @@ -684,10 +684,8 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter) goto err_out_free; } else { err = netxen_init_old_ctx(adapter); - if (err) { - netxen_free_hw_resources(adapter); - return err; - } + if (err) + goto err_out_free; } return 0; @@ -708,15 +706,18 @@ void netxen_free_hw_resources(struct netxen_adapter *adapter) int port = adapter->portnum; if (adapter->fw_major >= 4) { - nx_fw_cmd_destroy_tx_ctx(adapter); nx_fw_cmd_destroy_rx_ctx(adapter); + nx_fw_cmd_destroy_tx_ctx(adapter); } else { netxen_api_lock(adapter); NXWR32(adapter, CRB_CTX_SIGNATURE_REG(port), - NETXEN_CTX_RESET | port); + NETXEN_CTX_D3_RESET | port); netxen_api_unlock(adapter); } + /* Allow dma queues to drain after context reset */ + msleep(20); + recv_ctx = &adapter->recv_ctx; if (recv_ctx->hwctx != NULL) { diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c index ce3b89d2cbb6..b9123d445c96 100644 --- a/drivers/net/netxen/netxen_nic_hw.c +++ b/drivers/net/netxen/netxen_nic_hw.c @@ -461,13 +461,14 @@ netxen_send_cmd_descs(struct netxen_adapter *adapter, i = 0; tx_ring = adapter->tx_ring; - netif_tx_lock_bh(adapter->netdev); + __netif_tx_lock_bh(tx_ring->txq); producer = tx_ring->producer; consumer = tx_ring->sw_consumer; - if (nr_desc >= find_diff_among(producer, consumer, tx_ring->num_desc)) { - netif_tx_unlock_bh(adapter->netdev); + if (nr_desc >= netxen_tx_avail(tx_ring)) { + netif_tx_stop_queue(tx_ring->txq); + __netif_tx_unlock_bh(tx_ring->txq); return -EBUSY; } @@ -490,7 +491,7 @@ netxen_send_cmd_descs(struct netxen_adapter *adapter, netxen_nic_update_cmd_producer(adapter, tx_ring); - netif_tx_unlock_bh(adapter->netdev); + __netif_tx_unlock_bh(tx_ring->txq); return 0; } diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c index b899bd51fcd8..7acf204e38c9 100644 --- a/drivers/net/netxen/netxen_nic_init.c +++ b/drivers/net/netxen/netxen_nic_init.c @@ -184,6 +184,13 @@ void netxen_free_sw_resources(struct netxen_adapter *adapter) kfree(recv_ctx->rds_rings); skip_rds: + if (recv_ctx->sds_rings == NULL) + goto skip_sds; + + for(ring = 0; ring < adapter->max_sds_rings; ring++) + recv_ctx->sds_rings[ring].consumer = 0; + +skip_sds: if (adapter->tx_ring == NULL) return; @@ -214,6 +221,7 @@ int netxen_alloc_sw_resources(struct netxen_adapter *adapter) adapter->tx_ring = tx_ring; tx_ring->num_desc = adapter->num_txd; + tx_ring->txq = netdev_get_tx_queue(netdev, 0); cmd_buf_arr = vmalloc(TX_BUFF_RINGSIZE(tx_ring)); if (cmd_buf_arr == NULL) { @@ -1400,10 +1408,10 @@ int netxen_process_cmd_ring(struct netxen_adapter *adapter) smp_mb(); if (netif_queue_stopped(netdev) && netif_carrier_ok(netdev)) { - netif_tx_lock(netdev); + __netif_tx_lock(tx_ring->txq, smp_processor_id()); if (netxen_tx_avail(tx_ring) > TX_STOP_THRESH) netif_wake_queue(netdev); - netif_tx_unlock(netdev); + __netif_tx_unlock(tx_ring->txq); } } /* diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 27539ddf94c4..637ac8b89bac 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -215,9 +215,9 @@ netxen_napi_disable(struct netxen_adapter *adapter) for (ring = 0; ring < adapter->max_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; - napi_disable(&sds_ring->napi); netxen_nic_disable_int(sds_ring); - synchronize_irq(sds_ring->irq); + napi_synchronize(&sds_ring->napi); + napi_disable(&sds_ring->napi); } } @@ -833,11 +833,11 @@ netxen_nic_up(struct netxen_adapter *adapter, struct net_device *netdev) adapter->ahw.linkup = 0; - netxen_napi_enable(adapter); - if (adapter->max_sds_rings > 1) netxen_config_rss(adapter, 1); + netxen_napi_enable(adapter); + if (adapter->capabilities & NX_FW_CAPABILITY_LINK_NOTIFICATION) netxen_linkevent_request(adapter, 1); else @@ -851,8 +851,9 @@ netxen_nic_up(struct netxen_adapter *adapter, struct net_device *netdev) static void netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev) { + spin_lock(&adapter->tx_clean_lock); netif_carrier_off(netdev); - netif_stop_queue(netdev); + netif_tx_disable(netdev); if (adapter->stop_port) adapter->stop_port(adapter); @@ -863,9 +864,10 @@ netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev) netxen_napi_disable(adapter); netxen_release_tx_buffers(adapter); + spin_unlock(&adapter->tx_clean_lock); - FLUSH_SCHEDULED_WORK(); del_timer_sync(&adapter->watchdog_timer); + FLUSH_SCHEDULED_WORK(); } @@ -943,8 +945,8 @@ err_out_free_sw: static void netxen_nic_detach(struct netxen_adapter *adapter) { - netxen_release_rx_buffers(adapter); netxen_free_hw_resources(adapter); + netxen_release_rx_buffers(adapter); netxen_nic_free_irq(adapter); netxen_free_sw_resources(adapter); @@ -1533,10 +1535,12 @@ static int netxen_nic_check_temp(struct netxen_adapter *adapter) printk(KERN_ALERT "%s: Device temperature %d degrees C exceeds" " maximum allowed. Hardware has been shut down.\n", - netxen_nic_driver_name, temp_val); + netdev->name, temp_val); + + netif_device_detach(netdev); + netxen_nic_down(adapter, netdev); + netxen_nic_detach(adapter); - netif_carrier_off(netdev); - netif_stop_queue(netdev); rv = 1; } else if (temp_state == NX_TEMP_WARN) { if (adapter->temp == NX_TEMP_NORMAL) { @@ -1544,13 +1548,13 @@ static int netxen_nic_check_temp(struct netxen_adapter *adapter) "%s: Device temperature %d degrees C " "exceeds operating range." " Immediate action needed.\n", - netxen_nic_driver_name, temp_val); + netdev->name, temp_val); } } else { if (adapter->temp == NX_TEMP_WARN) { printk(KERN_INFO "%s: Device temperature is now %d degrees C" - " in normal range.\n", netxen_nic_driver_name, + " in normal range.\n", netdev->name, temp_val); } } @@ -1623,7 +1627,7 @@ void netxen_watchdog_task(struct work_struct *work) struct netxen_adapter *adapter = container_of(work, struct netxen_adapter, watchdog_task); - if ((adapter->portnum == 0) && netxen_nic_check_temp(adapter)) + if (netxen_nic_check_temp(adapter)) return; if (!adapter->has_link_events) @@ -1645,6 +1649,9 @@ static void netxen_tx_timeout_task(struct work_struct *work) struct netxen_adapter *adapter = container_of(work, struct netxen_adapter, tx_timeout_task); + if (!netif_running(adapter->netdev)) + return; + printk(KERN_ERR "%s %s: transmit timeout, resetting.\n", netxen_nic_driver_name, adapter->netdev->name); @@ -1757,7 +1764,8 @@ static int netxen_nic_poll(struct napi_struct *napi, int budget) if ((work_done < budget) && tx_complete) { napi_complete(&sds_ring->napi); - netxen_nic_enable_int(sds_ring); + if (netif_running(adapter->netdev)) + netxen_nic_enable_int(sds_ring); } return work_done; diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c index 8c1f6988f398..89f7b2ad5231 100644 --- a/drivers/net/pci-skeleton.c +++ b/drivers/net/pci-skeleton.c @@ -105,7 +105,7 @@ IVc. Errata static char version[] __devinitdata = KERN_INFO NETDRV_DRIVER_LOAD_MSG "\n" -KERN_INFO " Support available from http://foo.com/bar/baz.html\n"; +" Support available from http://foo.com/bar/baz.html\n"; /* define to 1 to enable PIO instead of MMIO */ #undef USE_IO_OPS diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c index ec7cf5ac4f05..690b9c76d34e 100644 --- a/drivers/net/pcmcia/3c589_cs.c +++ b/drivers/net/pcmcia/3c589_cs.c @@ -156,6 +156,7 @@ static struct net_device_stats *el3_get_stats(struct net_device *dev); static int el3_rx(struct net_device *dev); static int el3_close(struct net_device *dev); static void el3_tx_timeout(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); static void set_multicast_list(struct net_device *dev); static const struct ethtool_ops netdev_ethtool_ops; @@ -488,8 +489,7 @@ static void tc589_reset(struct net_device *dev) /* Switch to register set 1 for normal use. */ EL3WINDOW(1); - /* Accept b-cast and phys addr only. */ - outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); + set_rx_mode(dev); outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ @@ -700,7 +700,7 @@ static irqreturn_t el3_interrupt(int irq, void *dev_id) if (fifo_diag & 0x2000) { /* Rx underrun */ tc589_wait_for_completion(dev, RxReset); - set_multicast_list(dev); + set_rx_mode(dev); outw(RxEnable, ioaddr + EL3_CMD); } outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); @@ -905,14 +905,11 @@ static int el3_rx(struct net_device *dev) return 0; } -static void set_multicast_list(struct net_device *dev) +static void set_rx_mode(struct net_device *dev) { - struct el3_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; unsigned int ioaddr = dev->base_addr; u16 opts = SetRxFilter | RxStation | RxBroadcast; - if (!pcmcia_dev_present(link)) return; if (dev->flags & IFF_PROMISC) opts |= RxMulticast | RxProm; else if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) @@ -920,6 +917,16 @@ static void set_multicast_list(struct net_device *dev) outw(opts, ioaddr + EL3_CMD); } +static void set_multicast_list(struct net_device *dev) +{ + struct el3_private *priv = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + set_rx_mode(dev); + spin_unlock_irqrestore(&priv->lock, flags); +} + static int el3_close(struct net_device *dev) { struct el3_private *lp = netdev_priv(dev); diff --git a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c index f51944b28cfa..06618af1a468 100644 --- a/drivers/net/pcmcia/ibmtr_cs.c +++ b/drivers/net/pcmcia/ibmtr_cs.c @@ -298,14 +298,11 @@ static int __devinit ibmtr_config(struct pcmcia_device *link) strcpy(info->node.dev_name, dev->name); - printk(KERN_INFO "%s: port %#3lx, irq %d,", - dev->name, dev->base_addr, dev->irq); - printk (" mmio %#5lx,", (u_long)ti->mmio); - printk (" sram %#5lx,", (u_long)ti->sram_base << 12); - printk ("\n" KERN_INFO " hwaddr="); - for (i = 0; i < TR_ALEN; i++) - printk("%02X", dev->dev_addr[i]); - printk("\n"); + printk(KERN_INFO + "%s: port %#3lx, irq %d, mmio %#5lx, sram %#5lx, hwaddr=%pM\n", + dev->name, dev->base_addr, dev->irq, + (u_long)ti->mmio, (u_long)(ti->sram_base << 12), + dev->dev_addr); return 0; cs_failed: diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c index 02ef63ed1f99..36de91baf238 100644 --- a/drivers/net/pcmcia/nmclan_cs.c +++ b/drivers/net/pcmcia/nmclan_cs.c @@ -1425,15 +1425,12 @@ static void BuildLAF(int *ladrf, int *adr) ladrf[byte] |= (1 << (hashcode & 7)); #ifdef PCMCIA_DEBUG - if (pc_debug > 2) { - printk(KERN_DEBUG " adr ="); - for (i = 0; i < 6; i++) - printk(" %02X", adr[i]); - printk("\n" KERN_DEBUG " hashcode = %d(decimal), ladrf[0:63]" - " =", hashcode); - for (i = 0; i < 8; i++) - printk(" %02X", ladrf[i]); - printk("\n"); + if (pc_debug > 2) + printk(KERN_DEBUG " adr =%pM\n", adr); + printk(KERN_DEBUG " hashcode = %d(decimal), ladrf[0:63] =", hashcode); + for (i = 0; i < 8; i++) + printk(KERN_CONT " %02X", ladrf[i]); + printk(KERN_CONT "\n"); } #endif } /* BuildLAF */ diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 1c35e1d637a0..28368157dac4 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -485,7 +485,7 @@ static void pcnet32_realloc_tx_ring(struct net_device *dev, &new_ring_dma_addr); if (new_tx_ring == NULL) { if (netif_msg_drv(lp)) - printk("\n" KERN_ERR + printk(KERN_ERR "%s: Consistent memory allocation failed.\n", dev->name); return; @@ -496,7 +496,7 @@ static void pcnet32_realloc_tx_ring(struct net_device *dev, GFP_ATOMIC); if (!new_dma_addr_list) { if (netif_msg_drv(lp)) - printk("\n" KERN_ERR + printk(KERN_ERR "%s: Memory allocation failed.\n", dev->name); goto free_new_tx_ring; } @@ -505,7 +505,7 @@ static void pcnet32_realloc_tx_ring(struct net_device *dev, GFP_ATOMIC); if (!new_skb_list) { if (netif_msg_drv(lp)) - printk("\n" KERN_ERR + printk(KERN_ERR "%s: Memory allocation failed.\n", dev->name); goto free_new_lists; } @@ -563,7 +563,7 @@ static void pcnet32_realloc_rx_ring(struct net_device *dev, &new_ring_dma_addr); if (new_rx_ring == NULL) { if (netif_msg_drv(lp)) - printk("\n" KERN_ERR + printk(KERN_ERR "%s: Consistent memory allocation failed.\n", dev->name); return; @@ -574,7 +574,7 @@ static void pcnet32_realloc_rx_ring(struct net_device *dev, GFP_ATOMIC); if (!new_dma_addr_list) { if (netif_msg_drv(lp)) - printk("\n" KERN_ERR + printk(KERN_ERR "%s: Memory allocation failed.\n", dev->name); goto free_new_rx_ring; } @@ -583,7 +583,7 @@ static void pcnet32_realloc_rx_ring(struct net_device *dev, GFP_ATOMIC); if (!new_skb_list) { if (netif_msg_drv(lp)) - printk("\n" KERN_ERR + printk(KERN_ERR "%s: Memory allocation failed.\n", dev->name); goto free_new_lists; } @@ -1766,38 +1766,38 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) /* Version 0x2623 and 0x2624 */ if (((chip_version + 1) & 0xfffe) == 0x2624) { i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */ - printk("\n" KERN_INFO " tx_start_pt(0x%04x):", i); + printk(KERN_INFO " tx_start_pt(0x%04x):", i); switch (i >> 10) { case 0: - printk(" 20 bytes,"); + printk(KERN_CONT " 20 bytes,"); break; case 1: - printk(" 64 bytes,"); + printk(KERN_CONT " 64 bytes,"); break; case 2: - printk(" 128 bytes,"); + printk(KERN_CONT " 128 bytes,"); break; case 3: - printk("~220 bytes,"); + printk(KERN_CONT "~220 bytes,"); break; } i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */ - printk(" BCR18(%x):", i & 0xffff); + printk(KERN_CONT " BCR18(%x):", i & 0xffff); if (i & (1 << 5)) - printk("BurstWrEn "); + printk(KERN_CONT "BurstWrEn "); if (i & (1 << 6)) - printk("BurstRdEn "); + printk(KERN_CONT "BurstRdEn "); if (i & (1 << 7)) - printk("DWordIO "); + printk(KERN_CONT "DWordIO "); if (i & (1 << 11)) - printk("NoUFlow "); + printk(KERN_CONT "NoUFlow "); i = a->read_bcr(ioaddr, 25); - printk("\n" KERN_INFO " SRAMSIZE=0x%04x,", i << 8); + printk(KERN_INFO " SRAMSIZE=0x%04x,", i << 8); i = a->read_bcr(ioaddr, 26); - printk(" SRAM_BND=0x%04x,", i << 8); + printk(KERN_CONT " SRAM_BND=0x%04x,", i << 8); i = a->read_bcr(ioaddr, 27); if (i & (1 << 14)) - printk("LowLatRx"); + printk(KERN_CONT "LowLatRx"); } } @@ -1996,7 +1996,7 @@ static int pcnet32_alloc_ring(struct net_device *dev, const char *name) &lp->tx_ring_dma_addr); if (lp->tx_ring == NULL) { if (netif_msg_drv(lp)) - printk("\n" KERN_ERR PFX + printk(KERN_ERR PFX "%s: Consistent memory allocation failed.\n", name); return -ENOMEM; @@ -2008,7 +2008,7 @@ static int pcnet32_alloc_ring(struct net_device *dev, const char *name) &lp->rx_ring_dma_addr); if (lp->rx_ring == NULL) { if (netif_msg_drv(lp)) - printk("\n" KERN_ERR PFX + printk(KERN_ERR PFX "%s: Consistent memory allocation failed.\n", name); return -ENOMEM; @@ -2018,7 +2018,7 @@ static int pcnet32_alloc_ring(struct net_device *dev, const char *name) GFP_ATOMIC); if (!lp->tx_dma_addr) { if (netif_msg_drv(lp)) - printk("\n" KERN_ERR PFX + printk(KERN_ERR PFX "%s: Memory allocation failed.\n", name); return -ENOMEM; } @@ -2027,7 +2027,7 @@ static int pcnet32_alloc_ring(struct net_device *dev, const char *name) GFP_ATOMIC); if (!lp->rx_dma_addr) { if (netif_msg_drv(lp)) - printk("\n" KERN_ERR PFX + printk(KERN_ERR PFX "%s: Memory allocation failed.\n", name); return -ENOMEM; } @@ -2036,7 +2036,7 @@ static int pcnet32_alloc_ring(struct net_device *dev, const char *name) GFP_ATOMIC); if (!lp->tx_skbuff) { if (netif_msg_drv(lp)) - printk("\n" KERN_ERR PFX + printk(KERN_ERR PFX "%s: Memory allocation failed.\n", name); return -ENOMEM; } @@ -2045,7 +2045,7 @@ static int pcnet32_alloc_ring(struct net_device *dev, const char *name) GFP_ATOMIC); if (!lp->rx_skbuff) { if (netif_msg_drv(lp)) - printk("\n" KERN_ERR PFX + printk(KERN_ERR PFX "%s: Memory allocation failed.\n", name); return -ENOMEM; } diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 33984b737233..22cdd451fb82 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -30,6 +30,7 @@ #ifdef CONFIG_OF_GPIO #include <linux/of_gpio.h> +#include <linux/of_mdio.h> #include <linux/of_platform.h> #endif @@ -81,13 +82,12 @@ static struct mdiobb_ops mdio_gpio_ops = { .get_mdio_data = mdio_get, }; -static int __devinit mdio_gpio_bus_init(struct device *dev, +static struct mii_bus * __devinit mdio_gpio_bus_init(struct device *dev, struct mdio_gpio_platform_data *pdata, int bus_id) { struct mii_bus *new_bus; struct mdio_gpio_info *bitbang; - int ret = -ENOMEM; int i; bitbang = kzalloc(sizeof(*bitbang), GFP_KERNEL); @@ -104,8 +104,6 @@ static int __devinit mdio_gpio_bus_init(struct device *dev, new_bus->name = "GPIO Bitbanged MDIO", - ret = -ENODEV; - new_bus->phy_mask = pdata->phy_mask; new_bus->irq = pdata->irqs; new_bus->parent = dev; @@ -129,15 +127,8 @@ static int __devinit mdio_gpio_bus_init(struct device *dev, dev_set_drvdata(dev, new_bus); - ret = mdiobus_register(new_bus); - if (ret) - goto out_free_all; - - return 0; + return new_bus; -out_free_all: - dev_set_drvdata(dev, NULL); - gpio_free(bitbang->mdio); out_free_mdc: gpio_free(bitbang->mdc); out_free_bus: @@ -145,30 +136,47 @@ out_free_bus: out_free_bitbang: kfree(bitbang); out: - return ret; + return NULL; } -static void __devexit mdio_gpio_bus_destroy(struct device *dev) +static void __devinit mdio_gpio_bus_deinit(struct device *dev) { struct mii_bus *bus = dev_get_drvdata(dev); struct mdio_gpio_info *bitbang = bus->priv; - mdiobus_unregister(bus); - free_mdio_bitbang(bus); dev_set_drvdata(dev, NULL); - gpio_free(bitbang->mdc); gpio_free(bitbang->mdio); + gpio_free(bitbang->mdc); + free_mdio_bitbang(bus); kfree(bitbang); } +static void __devexit mdio_gpio_bus_destroy(struct device *dev) +{ + struct mii_bus *bus = dev_get_drvdata(dev); + + mdiobus_unregister(bus); + mdio_gpio_bus_deinit(dev); +} + static int __devinit mdio_gpio_probe(struct platform_device *pdev) { struct mdio_gpio_platform_data *pdata = pdev->dev.platform_data; + struct mii_bus *new_bus; + int ret; if (!pdata) return -ENODEV; - return mdio_gpio_bus_init(&pdev->dev, pdata, pdev->id); + new_bus = mdio_gpio_bus_init(&pdev->dev, pdata, pdev->id); + if (!new_bus) + return -ENODEV; + + ret = mdiobus_register(new_bus); + if (ret) + mdio_gpio_bus_deinit(&pdev->dev); + + return ret; } static int __devexit mdio_gpio_remove(struct platform_device *pdev) @@ -179,29 +187,12 @@ static int __devexit mdio_gpio_remove(struct platform_device *pdev) } #ifdef CONFIG_OF_GPIO -static void __devinit add_phy(struct mdio_gpio_platform_data *pdata, - struct device_node *np) -{ - const u32 *data; - int len, id, irq; - - data = of_get_property(np, "reg", &len); - if (!data || len != 4) - return; - - id = *data; - pdata->phy_mask &= ~(1 << id); - - irq = of_irq_to_resource(np, 0, NULL); - if (irq) - pdata->irqs[id] = irq; -} static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, const struct of_device_id *match) { - struct device_node *np = NULL; struct mdio_gpio_platform_data *pdata; + struct mii_bus *new_bus; int ret; pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); @@ -215,14 +206,18 @@ static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, ret = of_get_gpio(ofdev->node, 1); if (ret < 0) - goto out_free; + goto out_free; pdata->mdio = ret; - while ((np = of_get_next_child(ofdev->node, np))) - if (!strcmp(np->type, "ethernet-phy")) - add_phy(pdata, np); + new_bus = mdio_gpio_bus_init(&ofdev->dev, pdata, pdata->mdc); + if (!new_bus) + return -ENODEV; - return mdio_gpio_bus_init(&ofdev->dev, pdata, pdata->mdc); + ret = of_mdiobus_register(new_bus, ofdev->node); + if (ret) + mdio_gpio_bus_deinit(&ofdev->dev); + + return ret; out_free: kfree(pdata); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index eba937c46376..b10fedd82143 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -134,8 +134,10 @@ int phy_scan_fixups(struct phy_device *phydev) err = fixup->run(phydev); - if (err < 0) + if (err < 0) { + mutex_unlock(&phy_fixup_lock); return err; + } } } mutex_unlock(&phy_fixup_lock); diff --git a/drivers/net/plip.c b/drivers/net/plip.c index 7a62f781fef2..2ca8b0d84ee2 100644 --- a/drivers/net/plip.c +++ b/drivers/net/plip.c @@ -270,6 +270,9 @@ static const struct net_device_ops plip_netdev_ops = { .ndo_stop = plip_close, .ndo_start_xmit = plip_tx_packet, .ndo_do_ioctl = plip_ioctl, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, }; /* Entry point of PLIP driver. diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index 17c116bb332c..6de8399d6dd9 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -356,6 +356,7 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, if (!skb_queue_empty(&ap->rqueue)) tasklet_schedule(&ap->tsk); ap_put(ap); + tty_unthrottle(tty); } static void diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index aa3d39f38e22..d2fa2db13586 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -397,6 +397,7 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, if (!skb_queue_empty(&ap->rqueue)) tasklet_schedule(&ap->tsk); sp_put(ap); + tty_unthrottle(tty); } static void diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c index d1a5fb4d6acb..a3932c9f3406 100644 --- a/drivers/net/ps3_gelic_net.c +++ b/drivers/net/ps3_gelic_net.c @@ -1411,6 +1411,7 @@ static const struct net_device_ops gelic_netdevice_ops = { .ndo_set_multicast_list = gelic_net_set_multi, .ndo_change_mtu = gelic_net_change_mtu, .ndo_tx_timeout = gelic_net_tx_timeout, + .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = gelic_net_poll_controller, diff --git a/drivers/net/ps3_gelic_wireless.c b/drivers/net/ps3_gelic_wireless.c index b6b3ca9bdb21..6932b08d746b 100644 --- a/drivers/net/ps3_gelic_wireless.c +++ b/drivers/net/ps3_gelic_wireless.c @@ -2707,6 +2707,7 @@ static const struct net_device_ops gelic_wl_netdevice_ops = { .ndo_set_multicast_list = gelic_net_set_multi, .ndo_change_mtu = gelic_net_change_mtu, .ndo_tx_timeout = gelic_net_tx_timeout, + .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = gelic_net_poll_controller, diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c index ed63d23a6452..961b5397a531 100644 --- a/drivers/net/r6040.c +++ b/drivers/net/r6040.c @@ -49,8 +49,8 @@ #include <asm/processor.h> #define DRV_NAME "r6040" -#define DRV_VERSION "0.23" -#define DRV_RELDATE "05May2009" +#define DRV_VERSION "0.24" +#define DRV_RELDATE "08Jul2009" /* PHY CHIP Address */ #define PHY1_ADDR 1 /* For MAC1 */ @@ -704,8 +704,11 @@ static irqreturn_t r6040_interrupt(int irq, void *dev_id) /* Read MISR status and clear */ status = ioread16(ioaddr + MISR); - if (status == 0x0000 || status == 0xffff) + if (status == 0x0000 || status == 0xffff) { + /* Restore RDC MAC interrupt */ + iowrite16(misr, ioaddr + MIER); return IRQ_NONE; + } /* RX interrupt request */ if (status & RX_INTS) { diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 4b53b58d75fc..b82780d805f5 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -2060,8 +2060,6 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } } - pci_set_master(pdev); - /* ioremap MMIO region */ ioaddr = ioremap(pci_resource_start(pdev, region), R8169_REGS_SIZE); if (!ioaddr) { @@ -2089,6 +2087,8 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) RTL_W16(IntrStatus, 0xffff); + pci_set_master(pdev); + /* Identify chip attached to board */ rtl8169_get_mac_version(tp, ioaddr); @@ -3874,6 +3874,15 @@ static void rtl_shutdown(struct pci_dev *pdev) spin_unlock_irq(&tp->lock); if (system_state == SYSTEM_POWER_OFF) { + /* WoL fails with some 8168 when the receiver is disabled. */ + if (tp->features & RTL_FEATURE_WOL) { + pci_clear_master(pdev); + + RTL_W8(ChipCmd, CmdRxEnb); + /* PCI commit */ + RTL_R8(ChipCmd); + } + pci_wake_from_d3(pdev, true); pci_set_power_state(pdev, PCI_D3hot); } diff --git a/drivers/net/sc92031.c b/drivers/net/sc92031.c index 18821f217e19..e3156c97bb58 100644 --- a/drivers/net/sc92031.c +++ b/drivers/net/sc92031.c @@ -1593,6 +1593,7 @@ out: static struct pci_device_id sc92031_pci_device_id_table[] __devinitdata = { { PCI_DEVICE(PCI_VENDOR_ID_SILAN, 0x2031) }, { PCI_DEVICE(PCI_VENDOR_ID_SILAN, 0x8139) }, + { PCI_DEVICE(0x1088, 0x2031) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sc92031_pci_device_id_table); diff --git a/drivers/net/skge.c b/drivers/net/skge.c index 60d502eef4fc..543af2044f40 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -3854,8 +3854,10 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port, skge->speed = -1; skge->advertising = skge_supported_modes(hw); - if (device_may_wakeup(&hw->pdev->dev)) + if (device_can_wakeup(&hw->pdev->dev)) { skge->wol = wol_supported(hw) & WAKE_MAGIC; + device_set_wakeup_enable(&hw->pdev->dev, skge->wol); + } hw->dev[port] = dev; diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index daf961ab68bc..3550c5dcd93c 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1151,14 +1151,7 @@ stopped: /* reset the Rx prefetch unit */ sky2_write32(hw, Y2_QADDR(rxq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET); - - /* Reset the RAM Buffer receive queue */ - sky2_write8(hw, RB_ADDR(rxq, RB_CTRL), RB_RST_SET); - - /* Reset Rx MAC FIFO */ - sky2_write8(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), GMF_RST_SET); - - sky2_read8(hw, B0_CTST); + mmiowb(); } /* Clean out receive buffer area, assumes receiver hardware stopped */ @@ -1825,12 +1818,6 @@ static int sky2_down(struct net_device *dev) if (netif_msg_ifdown(sky2)) printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); - /* Disable port IRQ */ - imask = sky2_read32(hw, B0_IMSK); - imask &= ~portirq_msk[port]; - sky2_write32(hw, B0_IMSK, imask); - sky2_read32(hw, B0_IMSK); - /* Force flow control off */ sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); @@ -1870,8 +1857,6 @@ static int sky2_down(struct net_device *dev) sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET); - sky2_rx_stop(sky2); - sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET); sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET); @@ -1881,6 +1866,14 @@ static int sky2_down(struct net_device *dev) sky2_write32(hw, STAT_ISR_TIMER_CNT, 0); sky2_read8(hw, STAT_ISR_TIMER_CTRL); + sky2_rx_stop(sky2); + + /* Disable port IRQ */ + imask = sky2_read32(hw, B0_IMSK); + imask &= ~portirq_msk[port]; + sky2_write32(hw, B0_IMSK, imask); + sky2_read32(hw, B0_IMSK); + synchronize_irq(hw->pdev->irq); napi_synchronize(&hw->napi); diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index fdcbaf8dfa73..1c70e999cc50 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -1774,6 +1774,7 @@ static const struct net_device_ops smc_netdev_ops = { .ndo_start_xmit = smc_hard_start_xmit, .ndo_tx_timeout = smc_timeout, .ndo_set_multicast_list = smc_set_multicast_list, + .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index f1f773b17fe1..57a159fac99f 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -186,7 +186,8 @@ static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg) #define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l)) #define SMC_IRQ_FLAGS (-1) /* from resource */ -#elif defined(CONFIG_MACH_LOGICPD_PXA270) +#elif defined(CONFIG_MACH_LOGICPD_PXA270) \ + || defined(CONFIG_MACH_NOMADIK_8815NHK) #define SMC_CAN_USE_8BIT 0 #define SMC_CAN_USE_16BIT 1 diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index 66067f9d91c0..94b6d2658ddc 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -1779,6 +1779,7 @@ static const struct net_device_ops smsc911x_netdev_ops = { .ndo_get_stats = smsc911x_get_stats, .ndo_set_multicast_list = smsc911x_set_multicast_list, .ndo_do_ioctl = smsc911x_do_ioctl, + .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = smsc911x_set_mac_address, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index 838cce8b8fff..669253c7bd41 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -180,7 +180,7 @@ static int full_duplex[MAX_UNITS] = {0, }; /* These identify the driver base version and may not be removed. */ static const char version[] __devinitconst = KERN_INFO "starfire.c:v1.03 7/26/2000 Written by Donald Becker <becker@scyld.com>\n" -KERN_INFO " (unofficial 2.2/2.4 kernel port, version " DRV_VERSION ", " DRV_RELDATE ")\n"; +" (unofficial 2.2/2.4 kernel port, version " DRV_VERSION ", " DRV_RELDATE ")\n"; MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); MODULE_DESCRIPTION("Adaptec Starfire Ethernet driver"); diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index 545f81b34ad7..d1521c3875b2 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -1698,13 +1698,13 @@ static int netdev_close(struct net_device *dev) #ifdef __i386__ if (netif_msg_hw(np)) { - printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n", + printk(KERN_DEBUG " Tx ring at %8.8x:\n", (int)(np->tx_ring_dma)); for (i = 0; i < TX_RING_SIZE; i++) - printk(" #%d desc. %4.4x %8.8x %8.8x.\n", + printk(KERN_DEBUG " #%d desc. %4.4x %8.8x %8.8x.\n", i, np->tx_ring[i].status, np->tx_ring[i].frag[0].addr, np->tx_ring[i].frag[0].length); - printk("\n"KERN_DEBUG " Rx ring %8.8x:\n", + printk(KERN_DEBUG " Rx ring %8.8x:\n", (int)(np->rx_ring_dma)); for (i = 0; i < /*RX_RING_SIZE*/4 ; i++) { printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c index a82fb2aca4cb..f1e5e4542c2a 100644 --- a/drivers/net/sunvnet.c +++ b/drivers/net/sunvnet.c @@ -1016,7 +1016,9 @@ static const struct net_device_ops vnet_ops = { .ndo_open = vnet_open, .ndo_stop = vnet_close, .ndo_set_multicast_list = vnet_set_rx_mode, + .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = vnet_set_mac_addr, + .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = vnet_tx_timeout, .ndo_change_mtu = vnet_change_mtu, .ndo_start_xmit = vnet_start_xmit, diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c index 9d896116cf76..08a6c41c1599 100644 --- a/drivers/net/tokenring/ibmtr.c +++ b/drivers/net/tokenring/ibmtr.c @@ -1912,7 +1912,7 @@ static int __init ibmtr_init(void) find_turbo_adapters(io); - for (i = 0; io[i] && (i < IBMTR_MAX_ADAPTERS); i++) { + for (i = 0; i < IBMTR_MAX_ADAPTERS && io[i]; i++) { struct net_device *dev; irq[i] = 0; mem[i] = 0; diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c index 0f78f99f9b20..7030bd5e9848 100644 --- a/drivers/net/tsi108_eth.c +++ b/drivers/net/tsi108_eth.c @@ -1132,7 +1132,9 @@ static int tsi108_get_mac(struct net_device *dev) } if (!is_valid_ether_addr(dev->dev_addr)) { - printk("KERN_ERR: word1: %08x, word2: %08x\n", word1, word2); + printk(KERN_ERR + "%s: Invalid MAC address. word1: %08x, word2: %08x\n", + dev->name, word1, word2); return -EINVAL; } @@ -1201,8 +1203,8 @@ static void tsi108_set_rx_mode(struct net_device *dev) __set_bit(hash, &data->mc_hash[0]); } else { printk(KERN_ERR - "%s: got multicast address of length %d " - "instead of 6.\n", dev->name, + "%s: got multicast address of length %d instead of 6.\n", + dev->name, mc->dmi_addrlen); } diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c index 81f054dbb88d..ef49744a5085 100644 --- a/drivers/net/tulip/de2104x.c +++ b/drivers/net/tulip/de2104x.c @@ -944,9 +944,10 @@ static void de_set_media (struct de_private *de) macmode &= ~FullDuplex; if (netif_msg_link(de)) { - printk(KERN_INFO "%s: set link %s\n" - KERN_INFO "%s: mode 0x%x, sia 0x%x,0x%x,0x%x,0x%x\n" - KERN_INFO "%s: set mode 0x%x, set sia 0x%x,0x%x,0x%x\n", + printk(KERN_INFO + "%s: set link %s\n" + "%s: mode 0x%x, sia 0x%x,0x%x,0x%x,0x%x\n" + "%s: set mode 0x%x, set sia 0x%x,0x%x,0x%x\n", de->dev->name, media_name[media], de->dev->name, dr32(MacMode), dr32(SIAStatus), dr32(CSR13), dr32(CSR14), dr32(CSR15), diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index 2abb5d3becc6..99a63649f4fc 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -570,16 +570,18 @@ static void tulip_tx_timeout(struct net_device *dev) (unsigned int)tp->rx_ring[i].buffer2, buf[0], buf[1], buf[2]); for (j = 0; buf[j] != 0xee && j < 1600; j++) - if (j < 100) printk(" %2.2x", buf[j]); - printk(" j=%d.\n", j); + if (j < 100) + printk(KERN_CONT " %2.2x", buf[j]); + printk(KERN_CONT " j=%d.\n", j); } printk(KERN_DEBUG " Rx ring %8.8x: ", (int)tp->rx_ring); for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); - printk("\n" KERN_DEBUG " Tx ring %8.8x: ", (int)tp->tx_ring); + printk(KERN_CONT " %8.8x", + (unsigned int)tp->rx_ring[i].status); + printk(KERN_DEBUG " Tx ring %8.8x: ", (int)tp->tx_ring); for (i = 0; i < TX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); - printk("\n"); + printk(KERN_CONT " %8.8x", (unsigned int)tp->tx_ring[i].status); + printk(KERN_CONT "\n"); } #endif diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c index 842b1a2c40d4..0f15773dae52 100644 --- a/drivers/net/tulip/winbond-840.c +++ b/drivers/net/tulip/winbond-840.c @@ -142,7 +142,7 @@ static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; static const char version[] __initconst = KERN_INFO DRV_NAME ".c:v" DRV_VERSION " (2.4 port) " DRV_RELDATE " Donald Becker <becker@scyld.com>\n" - KERN_INFO " http://www.scyld.com/network/drivers.html\n"; + " http://www.scyld.com/network/drivers.html\n"; MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); MODULE_DESCRIPTION("Winbond W89c840 Ethernet driver"); @@ -939,7 +939,7 @@ static void tx_timeout(struct net_device *dev) printk(KERN_DEBUG " Rx ring %p: ", np->rx_ring); for (i = 0; i < RX_RING_SIZE; i++) printk(" %8.8x", (unsigned int)np->rx_ring[i].status); - printk("\n"KERN_DEBUG" Tx ring %p: ", np->tx_ring); + printk(KERN_DEBUG" Tx ring %p: ", np->tx_ring); for (i = 0; i < TX_RING_SIZE; i++) printk(" %8.8x", np->tx_ring[i].status); printk("\n"); @@ -1520,7 +1520,7 @@ static int netdev_close(struct net_device *dev) printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x.\n", i, np->tx_ring[i].length, np->tx_ring[i].status, np->tx_ring[i].buffer1); - printk("\n"KERN_DEBUG " Rx ring %8.8x:\n", + printk(KERN_DEBUG " Rx ring %8.8x:\n", (int)np->rx_ring); for (i = 0; i < RX_RING_SIZE; i++) { printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index 40c6eba775ce..3b957e6412ee 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -1590,13 +1590,13 @@ static int init_phy(struct net_device *dev) priv->oldspeed = 0; priv->oldduplex = -1; - if (!ug_info->phy_node) - return 0; - phydev = of_phy_connect(dev, ug_info->phy_node, &adjust_link, 0, priv->phy_interface); + if (!phydev) + phydev = of_phy_connect_fixed_link(dev, &adjust_link, + priv->phy_interface); if (!phydev) { - printk("%s: Could not attach to PHY\n", dev->name); + dev_err(&dev->dev, "Could not attach to PHY\n"); return -ENODEV; } @@ -3608,9 +3608,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma struct ucc_geth_private *ugeth = NULL; struct ucc_geth_info *ug_info; struct resource res; - struct device_node *phy; int err, ucc_num, max_speed = 0; - const u32 *fixed_link; const unsigned int *prop; const char *sprop; const void *mac_addr; @@ -3708,15 +3706,8 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma ug_info->uf_info.regs = res.start; ug_info->uf_info.irq = irq_of_parse_and_map(np, 0); - fixed_link = of_get_property(np, "fixed-link", NULL); - if (fixed_link) { - phy = NULL; - } else { - phy = of_parse_phandle(np, "phy-handle", 0); - if (phy == NULL) - return -ENODEV; - } - ug_info->phy_node = phy; + + ug_info->phy_node = of_parse_phandle(np, "phy-handle", 0); /* Find the TBI PHY node. If it's not there, we don't support SGMII */ ug_info->tbi_node = of_parse_phandle(np, "tbi-handle", 0); @@ -3725,7 +3716,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma prop = of_get_property(np, "phy-connection-type", NULL); if (!prop) { /* handle interface property present in old trees */ - prop = of_get_property(phy, "interface", NULL); + prop = of_get_property(ug_info->phy_node, "interface", NULL); if (prop != NULL) { phy_interface = enet_to_phy_interface[*prop]; max_speed = enet_to_speed[*prop]; diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index a906d3998131..c47237c2d638 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -369,4 +369,12 @@ config USB_NET_INT51X1 (Powerline Communications) solution with an Intellon INT51x1/INT5200 chip, like the "devolo dLan duo". +config USB_CDC_PHONET + tristate "CDC Phonet support" + depends on PHONET + help + Choose this option to support the Phonet interface to a Nokia + cellular modem, as found on most Nokia handsets with the + "PC suite" USB profile. + endmenu diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index b870b0b1cbe0..e17afb78f372 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -21,4 +21,5 @@ obj-$(CONFIG_USB_NET_ZAURUS) += zaurus.o obj-$(CONFIG_USB_NET_MCS7830) += mcs7830.o obj-$(CONFIG_USB_USBNET) += usbnet.o obj-$(CONFIG_USB_NET_INT51X1) += int51x1.o +obj-$(CONFIG_USB_CDC_PHONET) += cdc-phonet.o diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c new file mode 100644 index 000000000000..792af72da8ac --- /dev/null +++ b/drivers/net/usb/cdc-phonet.c @@ -0,0 +1,461 @@ +/* + * phonet.c -- USB CDC Phonet host driver + * + * Copyright (C) 2008-2009 Nokia Corporation. All rights reserved. + * + * Author: Rémi Denis-Courmont + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb/cdc.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/if_phonet.h> + +#define PN_MEDIA_USB 0x1B + +static const unsigned rxq_size = 17; + +struct usbpn_dev { + struct net_device *dev; + + struct usb_interface *intf, *data_intf; + struct usb_device *usb; + unsigned int tx_pipe, rx_pipe; + u8 active_setting; + u8 disconnected; + + unsigned tx_queue; + spinlock_t tx_lock; + + spinlock_t rx_lock; + struct sk_buff *rx_skb; + struct urb *urbs[0]; +}; + +static void tx_complete(struct urb *req); +static void rx_complete(struct urb *req); + +/* + * Network device callbacks + */ +static int usbpn_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct usbpn_dev *pnd = netdev_priv(dev); + struct urb *req = NULL; + unsigned long flags; + int err; + + if (skb->protocol != htons(ETH_P_PHONET)) + goto drop; + + req = usb_alloc_urb(0, GFP_ATOMIC); + if (!req) + goto drop; + usb_fill_bulk_urb(req, pnd->usb, pnd->tx_pipe, skb->data, skb->len, + tx_complete, skb); + req->transfer_flags = URB_ZERO_PACKET; + err = usb_submit_urb(req, GFP_ATOMIC); + if (err) { + usb_free_urb(req); + goto drop; + } + + spin_lock_irqsave(&pnd->tx_lock, flags); + pnd->tx_queue++; + if (pnd->tx_queue >= dev->tx_queue_len) + netif_stop_queue(dev); + spin_unlock_irqrestore(&pnd->tx_lock, flags); + return 0; + +drop: + dev_kfree_skb(skb); + dev->stats.tx_dropped++; + return 0; +} + +static void tx_complete(struct urb *req) +{ + struct sk_buff *skb = req->context; + struct net_device *dev = skb->dev; + struct usbpn_dev *pnd = netdev_priv(dev); + + switch (req->status) { + case 0: + dev->stats.tx_bytes += skb->len; + break; + + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + dev->stats.tx_aborted_errors++; + default: + dev->stats.tx_errors++; + dev_dbg(&dev->dev, "TX error (%d)\n", req->status); + } + dev->stats.tx_packets++; + + spin_lock(&pnd->tx_lock); + pnd->tx_queue--; + netif_wake_queue(dev); + spin_unlock(&pnd->tx_lock); + + dev_kfree_skb_any(skb); + usb_free_urb(req); +} + +static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags) +{ + struct net_device *dev = pnd->dev; + struct page *page; + int err; + + page = __netdev_alloc_page(dev, gfp_flags); + if (!page) + return -ENOMEM; + + usb_fill_bulk_urb(req, pnd->usb, pnd->rx_pipe, page_address(page), + PAGE_SIZE, rx_complete, dev); + req->transfer_flags = 0; + err = usb_submit_urb(req, gfp_flags); + if (unlikely(err)) { + dev_dbg(&dev->dev, "RX submit error (%d)\n", err); + netdev_free_page(dev, page); + } + return err; +} + +static void rx_complete(struct urb *req) +{ + struct net_device *dev = req->context; + struct usbpn_dev *pnd = netdev_priv(dev); + struct page *page = virt_to_page(req->transfer_buffer); + struct sk_buff *skb; + unsigned long flags; + + switch (req->status) { + case 0: + spin_lock_irqsave(&pnd->rx_lock, flags); + skb = pnd->rx_skb; + if (!skb) { + skb = pnd->rx_skb = netdev_alloc_skb(dev, 12); + if (likely(skb)) { + /* Can't use pskb_pull() on page in IRQ */ + memcpy(skb_put(skb, 1), page_address(page), 1); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + page, 1, req->actual_length); + page = NULL; + } + } else { + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + page, 0, req->actual_length); + page = NULL; + } + if (req->actual_length < PAGE_SIZE) + pnd->rx_skb = NULL; /* Last fragment */ + else + skb = NULL; + spin_unlock_irqrestore(&pnd->rx_lock, flags); + if (skb) { + skb->protocol = htons(ETH_P_PHONET); + skb_reset_mac_header(skb); + __skb_pull(skb, 1); + skb->dev = dev; + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + + netif_rx(skb); + } + goto resubmit; + + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + req = NULL; + break; + + case -EOVERFLOW: + dev->stats.rx_over_errors++; + dev_dbg(&dev->dev, "RX overflow\n"); + break; + + case -EILSEQ: + dev->stats.rx_crc_errors++; + break; + } + + dev->stats.rx_errors++; +resubmit: + if (page) + netdev_free_page(dev, page); + if (req) + rx_submit(pnd, req, GFP_ATOMIC); +} + +static int usbpn_close(struct net_device *dev); + +static int usbpn_open(struct net_device *dev) +{ + struct usbpn_dev *pnd = netdev_priv(dev); + int err; + unsigned i; + unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber; + + err = usb_set_interface(pnd->usb, num, pnd->active_setting); + if (err) + return err; + + for (i = 0; i < rxq_size; i++) { + struct urb *req = usb_alloc_urb(0, GFP_KERNEL); + + if (!req || rx_submit(pnd, req, GFP_KERNEL)) { + usbpn_close(dev); + return -ENOMEM; + } + pnd->urbs[i] = req; + } + + netif_wake_queue(dev); + return 0; +} + +static int usbpn_close(struct net_device *dev) +{ + struct usbpn_dev *pnd = netdev_priv(dev); + unsigned i; + unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber; + + netif_stop_queue(dev); + + for (i = 0; i < rxq_size; i++) { + struct urb *req = pnd->urbs[i]; + + if (!req) + continue; + usb_kill_urb(req); + usb_free_urb(req); + pnd->urbs[i] = NULL; + } + + return usb_set_interface(pnd->usb, num, !pnd->active_setting); +} + +static int usbpn_set_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU)) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + +static const struct net_device_ops usbpn_ops = { + .ndo_open = usbpn_open, + .ndo_stop = usbpn_close, + .ndo_start_xmit = usbpn_xmit, + .ndo_change_mtu = usbpn_set_mtu, +}; + +static void usbpn_setup(struct net_device *dev) +{ + dev->features = 0; + dev->netdev_ops = &usbpn_ops, + dev->header_ops = &phonet_header_ops; + dev->type = ARPHRD_PHONET; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->mtu = PHONET_MAX_MTU; + dev->hard_header_len = 1; + dev->dev_addr[0] = PN_MEDIA_USB; + dev->addr_len = 1; + dev->tx_queue_len = 3; + + dev->destructor = free_netdev; +} + +/* + * USB driver callbacks + */ +static struct usb_device_id usbpn_ids[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_INT_CLASS + | USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .idVendor = 0x0421, /* Nokia */ + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = 0xFE, + }, + { }, +}; + +MODULE_DEVICE_TABLE(usb, usbpn_ids); + +static struct usb_driver usbpn_driver; + +int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + static const char ifname[] = "usbpn%d"; + const struct usb_cdc_union_desc *union_header = NULL; + const struct usb_cdc_header_desc *phonet_header = NULL; + const struct usb_host_interface *data_desc; + struct usb_interface *data_intf; + struct usb_device *usbdev = interface_to_usbdev(intf); + struct net_device *dev; + struct usbpn_dev *pnd; + u8 *data; + int len, err; + + data = intf->altsetting->extra; + len = intf->altsetting->extralen; + while (len >= 3) { + u8 dlen = data[0]; + if (dlen < 3) + return -EINVAL; + + /* bDescriptorType */ + if (data[1] == USB_DT_CS_INTERFACE) { + /* bDescriptorSubType */ + switch (data[2]) { + case USB_CDC_UNION_TYPE: + if (union_header || dlen < 5) + break; + union_header = + (struct usb_cdc_union_desc *)data; + break; + case 0xAB: + if (phonet_header || dlen < 5) + break; + phonet_header = + (struct usb_cdc_header_desc *)data; + break; + } + } + data += dlen; + len -= dlen; + } + + if (!union_header || !phonet_header) + return -EINVAL; + + data_intf = usb_ifnum_to_if(usbdev, union_header->bSlaveInterface0); + if (data_intf == NULL) + return -ENODEV; + /* Data interface has one inactive and one active setting */ + if (data_intf->num_altsetting != 2) + return -EINVAL; + if (data_intf->altsetting[0].desc.bNumEndpoints == 0 + && data_intf->altsetting[1].desc.bNumEndpoints == 2) + data_desc = data_intf->altsetting + 1; + else + if (data_intf->altsetting[0].desc.bNumEndpoints == 2 + && data_intf->altsetting[1].desc.bNumEndpoints == 0) + data_desc = data_intf->altsetting; + else + return -EINVAL; + + dev = alloc_netdev(sizeof(*pnd) + sizeof(pnd->urbs[0]) * rxq_size, + ifname, usbpn_setup); + if (!dev) + return -ENOMEM; + + pnd = netdev_priv(dev); + SET_NETDEV_DEV(dev, &intf->dev); + netif_stop_queue(dev); + + pnd->dev = dev; + pnd->usb = usb_get_dev(usbdev); + pnd->intf = intf; + pnd->data_intf = data_intf; + spin_lock_init(&pnd->tx_lock); + spin_lock_init(&pnd->rx_lock); + /* Endpoints */ + if (usb_pipein(data_desc->endpoint[0].desc.bEndpointAddress)) { + pnd->rx_pipe = usb_rcvbulkpipe(usbdev, + data_desc->endpoint[0].desc.bEndpointAddress); + pnd->tx_pipe = usb_sndbulkpipe(usbdev, + data_desc->endpoint[1].desc.bEndpointAddress); + } else { + pnd->rx_pipe = usb_rcvbulkpipe(usbdev, + data_desc->endpoint[1].desc.bEndpointAddress); + pnd->tx_pipe = usb_sndbulkpipe(usbdev, + data_desc->endpoint[0].desc.bEndpointAddress); + } + pnd->active_setting = data_desc - data_intf->altsetting; + + err = usb_driver_claim_interface(&usbpn_driver, data_intf, pnd); + if (err) + goto out; + + /* Force inactive mode until the network device is brought UP */ + usb_set_interface(usbdev, union_header->bSlaveInterface0, + !pnd->active_setting); + usb_set_intfdata(intf, pnd); + + err = register_netdev(dev); + if (err) { + usb_driver_release_interface(&usbpn_driver, data_intf); + goto out; + } + + dev_dbg(&dev->dev, "USB CDC Phonet device found\n"); + return 0; + +out: + usb_set_intfdata(intf, NULL); + free_netdev(dev); + return err; +} + +static void usbpn_disconnect(struct usb_interface *intf) +{ + struct usbpn_dev *pnd = usb_get_intfdata(intf); + struct usb_device *usb = pnd->usb; + + if (pnd->disconnected) + return; + + pnd->disconnected = 1; + usb_driver_release_interface(&usbpn_driver, + (pnd->intf == intf) ? pnd->data_intf : pnd->intf); + unregister_netdev(pnd->dev); + usb_put_dev(usb); +} + +static struct usb_driver usbpn_driver = { + .name = "cdc_phonet", + .probe = usbpn_probe, + .disconnect = usbpn_disconnect, + .id_table = usbpn_ids, +}; + +static int __init usbpn_init(void) +{ + return usb_register(&usbpn_driver); +} + +static void __exit usbpn_exit(void) +{ + usb_deregister(&usbpn_driver); +} + +module_init(usbpn_init); +module_exit(usbpn_exit); + +MODULE_AUTHOR("Remi Denis-Courmont"); +MODULE_DESCRIPTION("USB CDC Phonet host interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c index cd35d50e46d4..45cebfb302cf 100644 --- a/drivers/net/usb/cdc_eem.c +++ b/drivers/net/usb/cdc_eem.c @@ -311,7 +311,7 @@ static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb) * bmCRC = 0 : CRC = 0xDEADBEEF */ if (header & BIT(14)) - crc2 = ~crc32_le(~0, skb2->data, len); + crc2 = ~crc32_le(~0, skb2->data, skb2->len); else crc2 = 0xdeadbeef; diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index e01314789718..1f9ec29fce50 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -999,6 +999,9 @@ static const struct net_device_ops kaweth_netdev_ops = { .ndo_tx_timeout = kaweth_tx_timeout, .ndo_set_multicast_list = kaweth_set_rx_mode, .ndo_get_stats = kaweth_netdev_stats, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, }; static int kaweth_probe( diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 73acbd244aa1..631d269ac980 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -1493,6 +1493,9 @@ static const struct net_device_ops pegasus_netdev_ops = { .ndo_set_multicast_list = pegasus_set_multicast, .ndo_get_stats = pegasus_netdev_stats, .ndo_tx_timeout = pegasus_tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, }; static struct usb_driver pegasus_driver = { diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index d3489a3c4c03..88c30a58b4bd 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -621,6 +621,7 @@ static const struct net_device_ops rhine_netdev_ops = { .ndo_start_xmit = rhine_start_tx, .ndo_get_stats = rhine_get_stats, .ndo_set_multicast_list = rhine_set_rx_mode, + .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, .ndo_do_ioctl = netdev_ioctl, diff --git a/drivers/net/wan/hd64570.c b/drivers/net/wan/hd64570.c index 223238de475c..1ea1ef6c3b96 100644 --- a/drivers/net/wan/hd64570.c +++ b/drivers/net/wan/hd64570.c @@ -584,8 +584,9 @@ static void sca_dump_rings(struct net_device *dev) sca_in(DSR_RX(phy_node(port)), card) & DSR_DE ? "" : "in"); for (cnt = 0; cnt < port_to_card(port)->rx_ring_buffers; cnt++) printk(" %02X", readb(&(desc_address(port, cnt, 0)->stat))); + printk(KERN_CONT "\n"); - printk("\n" KERN_DEBUG "TX ring: CDA=%u EDA=%u DSR=%02X in=%u " + printk(KERN_DEBUG "TX ring: CDA=%u EDA=%u DSR=%02X in=%u " "last=%u %sactive", sca_inw(get_dmac_tx(port) + CDAL, card), sca_inw(get_dmac_tx(port) + EDAL, card), diff --git a/drivers/net/wan/hd64572.c b/drivers/net/wan/hd64572.c index 497b003d7239..f099c34a3ae2 100644 --- a/drivers/net/wan/hd64572.c +++ b/drivers/net/wan/hd64572.c @@ -529,8 +529,9 @@ static void sca_dump_rings(struct net_device *dev) sca_in(DSR_RX(port->chan), card) & DSR_DE ? "" : "in"); for (cnt = 0; cnt < port->card->rx_ring_buffers; cnt++) printk(" %02X", readb(&(desc_address(port, cnt, 0)->stat))); + printk(KERN_CONT "\n"); - printk("\n" KERN_DEBUG "TX ring: CDA=%u EDA=%u DSR=%02X in=%u " + printk(KERN_DEBUG "TX ring: CDA=%u EDA=%u DSR=%02X in=%u " "last=%u %sactive", sca_inl(get_dmac_tx(port) + CDAL, card), sca_inl(get_dmac_tx(port) + EDAL, card), diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c index 3fb9dbc88a1a..d14e95a08d66 100644 --- a/drivers/net/wan/sbni.c +++ b/drivers/net/wan/sbni.c @@ -326,11 +326,9 @@ sbni_pci_probe( struct net_device *dev ) } if (pci_irq_line <= 0 || pci_irq_line >= nr_irqs) - printk( KERN_WARNING " WARNING: The PCI BIOS assigned " - "this PCI card to IRQ %d, which is unlikely " - "to work!.\n" - KERN_WARNING " You should use the PCI BIOS " - "setup to assign a valid IRQ line.\n", + printk( KERN_WARNING + " WARNING: The PCI BIOS assigned this PCI card to IRQ %d, which is unlikely to work!.\n" + " You should use the PCI BIOS setup to assign a valid IRQ line.\n", pci_irq_line ); /* avoiding re-enable dual adapters */ diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index d26e7b485315..eb0337c49546 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -1,5 +1,6 @@ config ATH_COMMON tristate "Atheros Wireless Cards" + depends on WLAN_80211 depends on ATH5K || ATH9K || AR9170_USB source "drivers/net/wireless/ath/ath5k/Kconfig" diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index ea045151f953..029c1bc7468f 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2970,6 +2970,9 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (modparam_nohwcrypt) return -EOPNOTSUPP; + if (sc->opmode == NL80211_IFTYPE_AP) + return -EOPNOTSUPP; + switch (key->alg) { case ALG_WEP: case ALG_TKIP: diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 1aeafb511ddd..aad259b4c197 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -478,6 +478,18 @@ void ath9k_ani_reset(struct ath_hw *ah) "Reset ANI state opmode %u\n", ah->opmode); ah->stats.ast_ani_reset++; + if (ah->opmode == NL80211_IFTYPE_AP) { + /* + * ath9k_hw_ani_control() will only process items set on + * ah->ani_function + */ + if (IS_CHAN_2GHZ(chan)) + ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL | + ATH9K_ANI_FIRSTEP_LEVEL); + else + ah->ani_function = 0; + } + ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0); ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0); ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index b61a071788a5..4ccf48e396df 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -355,7 +355,14 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, } if (bf_next == NULL) { - INIT_LIST_HEAD(&bf_head); + /* + * Make sure the last desc is reclaimed if it + * not a holding desc. + */ + if (!bf_last->bf_stale) + list_move_tail(&bf->list, &bf_head); + else + INIT_LIST_HEAD(&bf_head); } else { ASSERT(!list_empty(bf_q)); list_move_tail(&bf->list, &bf_head); diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index eef370bd1211..bf3d25ba7be1 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -474,6 +474,21 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, return 0; } +/* + * Some users have reported their EEPROM programmed with + * 0x8000 set, this is not a supported regulatory domain + * but since we have more than one user with it we need + * a solution for them. We default to 0x64, which is the + * default Atheros world regulatory domain. + */ +static void ath_regd_sanitize(struct ath_regulatory *reg) +{ + if (reg->current_rd != COUNTRY_ERD_FLAG) + return; + printk(KERN_DEBUG "ath: EEPROM regdomain sanitized\n"); + reg->current_rd = 0x64; +} + int ath_regd_init(struct ath_regulatory *reg, struct wiphy *wiphy, @@ -486,6 +501,8 @@ ath_regd_init(struct ath_regulatory *reg, if (!reg) return -EINVAL; + ath_regd_sanitize(reg); + printk(KERN_DEBUG "ath: EEPROM regdomain: 0x%0x\n", reg->current_rd); if (!ath_regd_is_eeprom_valid(reg)) { diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index f580c2812d91..40448067e4cc 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -648,6 +648,7 @@ struct b43_wl { u8 nr_devs; bool radiotap_enabled; + bool radio_enabled; /* The beacon we are currently using (AP or IBSS mode). * This beacon stuff is protected by the irq_lock. */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 6456afebdba1..e71c8d9cd706 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3497,8 +3497,8 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) if (phy->ops->set_rx_antenna) phy->ops->set_rx_antenna(dev, antenna); - if (!!conf->radio_enabled != phy->radio_on) { - if (conf->radio_enabled) { + if (wl->radio_enabled != phy->radio_on) { + if (wl->radio_enabled) { b43_software_rfkill(dev, false); b43info(dev->wl, "Radio turned on by software\n"); if (!dev->radio_hw_enable) { @@ -4339,6 +4339,7 @@ static int b43_op_start(struct ieee80211_hw *hw) wl->beacon0_uploaded = 0; wl->beacon1_uploaded = 0; wl->beacon_templates_virgin = 1; + wl->radio_enabled = 1; mutex_lock(&wl->mutex); @@ -4378,6 +4379,7 @@ static void b43_op_stop(struct ieee80211_hw *hw) if (b43_status(dev) >= B43_STAT_STARTED) b43_wireless_core_stop(dev); b43_wireless_core_exit(dev); + wl->radio_enabled = 0; mutex_unlock(&wl->mutex); cancel_work_sync(&(wl->txpower_adjust_work)); @@ -4560,6 +4562,7 @@ static int b43_wireless_core_attach(struct b43_wldev *dev) B43_WARN_ON(1); dev->phy.gmode = have_2ghz_phy; + dev->phy.radio_on = 1; tmp = dev->phy.gmode ? B43_TMSLOW_GMODE : 0; b43_wireless_core_reset(dev, tmp); diff --git a/drivers/net/wireless/b43/pcmcia.c b/drivers/net/wireless/b43/pcmcia.c index 3cfc30307a27..6c3a74964ab8 100644 --- a/drivers/net/wireless/b43/pcmcia.c +++ b/drivers/net/wireless/b43/pcmcia.c @@ -35,6 +35,7 @@ static /*const */ struct pcmcia_device_id b43_pcmcia_tbl[] = { PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448), + PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x476), PCMCIA_DEVICE_NULL, }; diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/b43legacy/b43legacy.h index 77fda148ac46..038baa8869e2 100644 --- a/drivers/net/wireless/b43legacy/b43legacy.h +++ b/drivers/net/wireless/b43legacy/b43legacy.h @@ -607,6 +607,7 @@ struct b43legacy_wl { u8 nr_devs; bool radiotap_enabled; + bool radio_enabled; /* The beacon we are currently using (AP or IBSS mode). * This beacon stuff is protected by the irq_lock. */ diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index e5136fb65ddd..c4973c1942bf 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2689,8 +2689,8 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw, /* Antennas for RX and management frame TX. */ b43legacy_mgmtframe_txantenna(dev, antenna_tx); - if (!!conf->radio_enabled != phy->radio_on) { - if (conf->radio_enabled) { + if (wl->radio_enabled != phy->radio_on) { + if (wl->radio_enabled) { b43legacy_radio_turn_on(dev); b43legacyinfo(dev->wl, "Radio turned on by software\n"); if (!dev->radio_hw_enable) @@ -3441,6 +3441,7 @@ static int b43legacy_op_start(struct ieee80211_hw *hw) wl->beacon0_uploaded = 0; wl->beacon1_uploaded = 0; wl->beacon_templates_virgin = 1; + wl->radio_enabled = 1; mutex_lock(&wl->mutex); @@ -3479,6 +3480,7 @@ static void b43legacy_op_stop(struct ieee80211_hw *hw) if (b43legacy_status(dev) >= B43legacy_STAT_STARTED) b43legacy_wireless_core_stop(dev); b43legacy_wireless_core_exit(dev); + wl->radio_enabled = 0; mutex_unlock(&wl->mutex); } @@ -3620,6 +3622,7 @@ static int b43legacy_wireless_core_attach(struct b43legacy_wldev *dev) have_bphy = 1; dev->phy.gmode = (have_gphy || have_bphy); + dev->phy.radio_on = 1; tmp = dev->phy.gmode ? B43legacy_TMSLOW_GMODE : 0; b43legacy_wireless_core_reset(dev, tmp); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 6d1519e1f011..355f50ea7fef 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2675,12 +2675,10 @@ static ssize_t show_power_level(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); - int mode = priv->power_data.user_power_setting; int level = priv->power_data.power_mode; char *p = buf; - p += sprintf(p, "INDEX:%d\t", level); - p += sprintf(p, "USER:%d\n", mode); + p += sprintf(p, "%d\n", level); return p - buf + 1; } diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 85ae7a62109c..9bbeec9427f0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -872,7 +872,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len); /* Set up entry for this TFD in Tx byte-count array */ - priv->cfg->ops->lib->txq_update_byte_cnt_tbl(priv, txq, + if (info->flags & IEEE80211_TX_CTL_AMPDU) + priv->cfg->ops->lib->txq_update_byte_cnt_tbl(priv, txq, le16_to_cpu(tx_cmd->len)); pci_dma_sync_single_for_device(priv->pci_dev, txcmd_phys, diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index cb9bd4c8f25e..956798f2c80c 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -3643,12 +3643,10 @@ static ssize_t show_power_level(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); - int mode = priv->power_data.user_power_setting; int level = priv->power_data.power_mode; char *p = buf; - p += sprintf(p, "INDEX:%d\t", level); - p += sprintf(p, "USER:%d\n", mode); + p += sprintf(p, "%d\n", level); return p - buf + 1; } diff --git a/drivers/net/wireless/iwmc3200wifi/Kconfig b/drivers/net/wireless/iwmc3200wifi/Kconfig index 1eccb6df46dd..030401d367d3 100644 --- a/drivers/net/wireless/iwmc3200wifi/Kconfig +++ b/drivers/net/wireless/iwmc3200wifi/Kconfig @@ -4,6 +4,15 @@ config IWM depends on CFG80211 select WIRELESS_EXT select FW_LOADER + help + The Intel Wireless Multicomm 3200 hardware is a combo + card with GPS, Bluetooth, WiMax and 802.11 radios. It + runs over SDIO and is typically found on Moorestown + based platform. This driver takes care of the 802.11 + part, which is a fullmac one. + + If you choose to build it as a module, it'll be called + iwmc3200wifi.ko. config IWM_DEBUG bool "Enable full debugging output in iwmc3200wifi" diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c index aaa20c6885c8..aea5ccf24ccf 100644 --- a/drivers/net/wireless/iwmc3200wifi/netdev.c +++ b/drivers/net/wireless/iwmc3200wifi/netdev.c @@ -151,8 +151,8 @@ void iwm_if_free(struct iwm_priv *iwm) return; free_netdev(iwm_to_ndev(iwm)); - iwm_wdev_free(iwm); iwm_priv_deinit(iwm); + iwm_wdev_free(iwm); } int iwm_if_add(struct iwm_priv *iwm) diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 01db705a38ec..685098148e10 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -135,8 +135,14 @@ int lbs_update_hw_spec(struct lbs_private *priv) /* Clamp region code to 8-bit since FW spec indicates that it should * only ever be 8-bit, even though the field size is 16-bit. Some firmware * returns non-zero high 8 bits here. + * + * Firmware version 4.0.102 used in CF8381 has region code shifted. We + * need to check for this problem and handle it properly. */ - priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; + if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4) + priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF; + else + priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { /* use the region code to search for the index */ diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h index 48da157d6cda..72f3479a4d70 100644 --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h @@ -234,6 +234,8 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in /** Mesh enable bit in FW capability */ #define MESH_CAPINFO_ENABLE_MASK (1<<16) +/** FW definition from Marvell v4 */ +#define MRVL_FW_V4 (0x04) /** FW definition from Marvell v5 */ #define MRVL_FW_V5 (0x05) /** FW definition from Marvell v10 */ diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index e789c6e9938c..7916ca3f84c8 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -418,6 +418,7 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, continue; if (!data2->started || !hwsim_ps_rx_ok(data2, skb) || + !data->channel || !data2->channel || data->channel->center_freq != data2->channel->center_freq || !(data->group & data2->group)) continue; @@ -708,7 +709,7 @@ static const struct ieee80211_ops mac80211_hwsim_ops = static void mac80211_hwsim_free(void) { struct list_head tmplist, *i, *tmp; - struct mac80211_hwsim_data *data; + struct mac80211_hwsim_data *data, *tmpdata; INIT_LIST_HEAD(&tmplist); @@ -717,7 +718,7 @@ static void mac80211_hwsim_free(void) list_move(i, &tmplist); spin_unlock_bh(&hwsim_radio_lock); - list_for_each_entry(data, &tmplist, list) { + list_for_each_entry_safe(data, tmpdata, &tmplist, list) { debugfs_remove(data->debugfs_group); debugfs_remove(data->debugfs_ps); debugfs_remove(data->debugfs); @@ -1166,8 +1167,8 @@ static void __exit exit_mac80211_hwsim(void) { printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n"); - unregister_netdev(hwsim_mon); mac80211_hwsim_free(); + unregister_netdev(hwsim_mon); } diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c index 345593c4accb..a370e510f19f 100644 --- a/drivers/net/wireless/orinoco/main.c +++ b/drivers/net/wireless/orinoco/main.c @@ -2521,6 +2521,8 @@ static const struct net_device_ops orinoco_netdev_ops = { .ndo_start_xmit = orinoco_xmit, .ndo_set_multicast_list = orinoco_set_multicast_list, .ndo_change_mtu = orinoco_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = orinoco_tx_timeout, .ndo_get_stats = orinoco_get_stats, }; @@ -2555,7 +2557,6 @@ struct net_device priv->wireless_data.spy_data = &priv->spy_data; dev->wireless_data = &priv->wireless_data; #endif - /* we use the default eth_mac_addr for setting the MAC addr */ /* Reserve space in skb for the SNAP header */ dev->hard_header_len += ENCAPS_OVERHEAD; diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 48d81d98e12d..22ca122bd798 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -912,13 +912,14 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb) } __skb_unlink(entry, &priv->tx_queue); - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); frame_len = entry->len; entry_hdr = (struct p54_hdr *) entry->data; entry_data = (struct p54_tx_data *) entry_hdr->data; - priv->tx_stats[entry_data->hw_queue].len--; + if (priv->tx_stats[entry_data->hw_queue].len) + priv->tx_stats[entry_data->hw_queue].len--; priv->stats.dot11ACKFailureCount += payload->tries - 1; + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); /* * Frames in P54_QUEUE_FWSCAN and P54_QUEUE_BEACON are diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 83116baeb110..72c7dbd39d0a 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -635,7 +635,7 @@ static int __devinit p54spi_probe(struct spi_device *spi) hw = p54_init_common(sizeof(*priv)); if (!hw) { - dev_err(&priv->spi->dev, "could not alloc ieee80211_hw"); + dev_err(&spi->dev, "could not alloc ieee80211_hw"); return -ENOMEM; } diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index b10b0383dfa5..698b11b1cadb 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -2427,11 +2427,10 @@ static void untranslate(ray_dev_t *local, struct sk_buff *skb, int len) #ifdef PCMCIA_DEBUG if (pc_debug > 3) { - int i; - printk(KERN_DEBUG "skb->data before untranslate"); - for (i = 0; i < 64; i++) - printk("%02x ", skb->data[i]); - printk("\n" KERN_DEBUG + print_hex_dump(KERN_DEBUG, "skb->data before untranslate: ", + DUMP_PREFIX_NONE, 16, 1, + skb->data, 64, true); + printk(KERN_DEBUG "type = %08x, xsap = %02x%02x%02x, org = %02x02x02x\n", ntohs(type), psnap->dsap, psnap->ssap, psnap->ctrl, psnap->org[0], psnap->org[1], psnap->org[2]); diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index 66daf68ff0ee..ce75426764a1 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1550,7 +1550,9 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev) rt2500usb_register_read(rt2x00dev, MAC_CSR0, ®); rt2x00_set_chip(rt2x00dev, RT2570, value, reg); - if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0)) { + if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0) || + rt2x00_check_rev(&rt2x00dev->chip, 0x0000000f, 0)) { + ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); return -ENODEV; } diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.c b/drivers/net/wireless/rtl818x/rtl8187_leds.c index b44253592243..cf9f899fe0e6 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.c +++ b/drivers/net/wireless/rtl818x/rtl8187_leds.c @@ -208,11 +208,12 @@ void rtl8187_leds_exit(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; - rtl8187_unregister_led(&priv->led_tx); /* turn the LED off before exiting */ queue_delayed_work(dev->workqueue, &priv->led_off, 0); cancel_delayed_work_sync(&priv->led_off); + cancel_delayed_work_sync(&priv->led_on); rtl8187_unregister_led(&priv->led_rx); + rtl8187_unregister_led(&priv->led_tx); } #endif /* def CONFIG_RTL8187_LED */ diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c index 6af706408ac0..c6d300666ad8 100644 --- a/drivers/net/wireless/wavelan_cs.c +++ b/drivers/net/wireless/wavelan_cs.c @@ -3556,17 +3556,8 @@ wv_82593_config(struct net_device * dev) cfblk.rcvstop = TRUE; /* Enable Receive Stop Register */ #ifdef DEBUG_I82593_SHOW - { - u_char *c = (u_char *) &cfblk; - int i; - printk(KERN_DEBUG "wavelan_cs: config block:"); - for(i = 0; i < sizeof(struct i82593_conf_block); i++,c++) - { - if((i % 16) == 0) printk("\n" KERN_DEBUG); - printk("%02x ", *c); - } - printk("\n"); - } + print_hex_dump(KERN_DEBUG, "wavelan_cs: config block: ", DUMP_PREFIX_NONE, + 16, 1, &cfblk, sizeof(struct i82593_conf_block), false); #endif /* Copy the config block to the i82593 */ diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 14a19baff214..0e6e44689cc6 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -38,7 +38,6 @@ static struct usb_device_id usb_ids[] = { /* ZD1211 */ { USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0ace, 0xa211), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 }, @@ -61,6 +60,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x157e, 0x300a), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0105, 0x145f), .driver_info = DEVICE_ZD1211 }, /* ZD1211B */ + { USB_DEVICE(0x054c, 0x0257), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0ace, 0xb215), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, @@ -87,6 +87,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x0471, 0x1237), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x07fa, 0x1196), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0df6, 0x0036), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211B }, /* "Driverless" devices that need ejecting */ { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, { USB_DEVICE(0x0ace, 0x20ff), .driver_info = DEVICE_INSTALLER }, diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c index 3c7a5053f1da..a07580138e81 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -109,7 +109,7 @@ static int gx_fix; /* These identify the driver base version and may not be removed. */ static const char version[] __devinitconst = KERN_INFO DRV_NAME ".c:v1.05 1/09/2001 Written by Donald Becker <becker@scyld.com>\n" - KERN_INFO " (unofficial 2.4.x port, " DRV_VERSION ", " DRV_RELDATE ")\n"; + " (unofficial 2.4.x port, " DRV_VERSION ", " DRV_RELDATE ")\n"; MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); MODULE_DESCRIPTION("Packet Engines Yellowfin G-NIC Gigabit Ethernet driver"); @@ -700,12 +700,15 @@ static void yellowfin_tx_timeout(struct net_device *dev) int i; printk(KERN_WARNING " Rx ring %p: ", yp->rx_ring); for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", yp->rx_ring[i].result_status); - printk("\n"KERN_WARNING" Tx ring %p: ", yp->tx_ring); + printk(KERN_CONT " %8.8x", + yp->rx_ring[i].result_status); + printk(KERN_CONT "\n"); + printk(KERN_WARNING" Tx ring %p: ", yp->tx_ring); for (i = 0; i < TX_RING_SIZE; i++) - printk(" %4.4x /%8.8x", yp->tx_status[i].tx_errs, - yp->tx_ring[i].result_status); - printk("\n"); + printk(KERN_CONT " %4.4x /%8.8x", + yp->tx_status[i].tx_errs, + yp->tx_ring[i].result_status); + printk(KERN_CONT "\n"); } /* If the hardware is found to hang regularly, we will update the code @@ -1216,20 +1219,20 @@ static int yellowfin_close(struct net_device *dev) #if defined(__i386__) if (yellowfin_debug > 2) { - printk("\n"KERN_DEBUG" Tx ring at %8.8llx:\n", + printk(KERN_DEBUG" Tx ring at %8.8llx:\n", (unsigned long long)yp->tx_ring_dma); for (i = 0; i < TX_RING_SIZE*2; i++) - printk(" %c #%d desc. %8.8x %8.8x %8.8x %8.8x.\n", + printk(KERN_DEBUG " %c #%d desc. %8.8x %8.8x %8.8x %8.8x.\n", ioread32(ioaddr + TxPtr) == (long)&yp->tx_ring[i] ? '>' : ' ', i, yp->tx_ring[i].dbdma_cmd, yp->tx_ring[i].addr, yp->tx_ring[i].branch_addr, yp->tx_ring[i].result_status); printk(KERN_DEBUG " Tx status %p:\n", yp->tx_status); for (i = 0; i < TX_RING_SIZE; i++) - printk(" #%d status %4.4x %4.4x %4.4x %4.4x.\n", + printk(KERN_DEBUG " #%d status %4.4x %4.4x %4.4x %4.4x.\n", i, yp->tx_status[i].tx_cnt, yp->tx_status[i].tx_errs, yp->tx_status[i].total_tx_cnt, yp->tx_status[i].paused); - printk("\n"KERN_DEBUG " Rx ring %8.8llx:\n", + printk(KERN_DEBUG " Rx ring %8.8llx:\n", (unsigned long long)yp->rx_ring_dma); for (i = 0; i < RX_RING_SIZE; i++) { printk(KERN_DEBUG " %c #%d desc. %8.8x %8.8x %8.8x\n", diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index aee967d7f760..bacaa536fd51 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -9,6 +9,10 @@ * out of the OpenFirmware device tree and using it to populate an mii_bus. */ +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/netdevice.h> +#include <linux/err.h> #include <linux/phy.h> #include <linux/of.h> #include <linux/of_mdio.h> @@ -137,3 +141,41 @@ struct phy_device *of_phy_connect(struct net_device *dev, return phy_connect_direct(dev, phy, hndlr, flags, iface) ? NULL : phy; } EXPORT_SYMBOL(of_phy_connect); + +/** + * of_phy_connect_fixed_link - Parse fixed-link property and return a dummy phy + * @dev: pointer to net_device claiming the phy + * @hndlr: Link state callback for the network device + * @iface: PHY data interface type + * + * This function is a temporary stop-gap and will be removed soon. It is + * only to support the fs_enet, ucc_geth and gianfar Ethernet drivers. Do + * not call this function from new drivers. + */ +struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, + void (*hndlr)(struct net_device *), + phy_interface_t iface) +{ + struct device_node *net_np; + char bus_id[MII_BUS_ID_SIZE + 3]; + struct phy_device *phy; + const u32 *phy_id; + int sz; + + if (!dev->dev.parent) + return NULL; + + net_np = dev_archdata_get_node(&dev->dev.parent->archdata); + if (!net_np) + return NULL; + + phy_id = of_get_property(net_np, "fixed-link", &sz); + if (!phy_id || sz < sizeof(*phy_id)) + return NULL; + + sprintf(bus_id, PHY_ID_FMT, "0", phy_id[0]); + + phy = phy_connect(dev, bus_id, hndlr, 0, iface); + return IS_ERR(phy) ? NULL : phy; +} +EXPORT_SYMBOL(of_phy_connect_fixed_link); diff --git a/drivers/oprofile/oprofile_stats.c b/drivers/oprofile/oprofile_stats.c index e1f6ce03705e..3c2270a8300c 100644 --- a/drivers/oprofile/oprofile_stats.c +++ b/drivers/oprofile/oprofile_stats.c @@ -33,6 +33,7 @@ void oprofile_reset_stats(void) atomic_set(&oprofile_stats.sample_lost_no_mm, 0); atomic_set(&oprofile_stats.sample_lost_no_mapping, 0); atomic_set(&oprofile_stats.event_lost_overflow, 0); + atomic_set(&oprofile_stats.bt_lost_no_mapping, 0); } diff --git a/drivers/parisc/eisa_enumerator.c b/drivers/parisc/eisa_enumerator.c index c709ecc2b7f7..0be1d50645ab 100644 --- a/drivers/parisc/eisa_enumerator.c +++ b/drivers/parisc/eisa_enumerator.c @@ -101,7 +101,7 @@ static int configure_memory(const unsigned char *buf, printk("memory %lx-%lx ", (unsigned long)res->start, (unsigned long)res->end); result = request_resource(mem_parent, res); if (result < 0) { - printk("\n" KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n"); + printk(KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n"); return result; } } @@ -191,7 +191,7 @@ static int configure_port(const unsigned char *buf, struct resource *io_parent, printk("ioports %lx-%lx ", (unsigned long)res->start, (unsigned long)res->end); result = request_resource(io_parent, res); if (result < 0) { - printk("\n" KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n"); + printk(KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n"); return result; } } @@ -224,7 +224,7 @@ static int configure_port_init(const unsigned char *buf) case HPEE_PORT_INIT_WIDTH_BYTE: s=1; if (c & HPEE_PORT_INIT_MASK) { - printk("\n" KERN_WARNING "port_init: unverified mask attribute\n"); + printk(KERN_WARNING "port_init: unverified mask attribute\n"); outb((inb(get_16(buf+len+1) & get_8(buf+len+3)) | get_8(buf+len+4)), get_16(buf+len+1)); @@ -249,7 +249,7 @@ static int configure_port_init(const unsigned char *buf) case HPEE_PORT_INIT_WIDTH_DWORD: s=4; if (c & HPEE_PORT_INIT_MASK) { - printk("\n" KERN_WARNING "port_init: unverified mask attribute\n"); + printk(KERN_WARNING "port_init: unverified mask attribute\n"); outl((inl(get_16(buf+len+1) & get_32(buf+len+3)) | get_32(buf+len+7)), get_16(buf+len+1)); @@ -259,7 +259,7 @@ static int configure_port_init(const unsigned char *buf) break; default: - printk("\n" KERN_ERR "Invalid port init word %02x\n", c); + printk(KERN_ERR "Invalid port init word %02x\n", c); return 0; } @@ -297,7 +297,7 @@ static int configure_type_string(const unsigned char *buf) /* just skip past the type field */ len = get_8(buf); if (len > 80) { - printk("\n" KERN_ERR "eisa_enumerator: type info field too long (%d, max is 80)\n", len); + printk(KERN_ERR "eisa_enumerator: type info field too long (%d, max is 80)\n", len); } return 1+len; @@ -398,7 +398,7 @@ static int parse_slot_config(int slot, } if (p0 + function_len < pos) { - printk("\n" KERN_ERR "eisa_enumerator: function %d length mis-match " + printk(KERN_ERR "eisa_enumerator: function %d length mis-match " "got %d, expected %d\n", num_func, pos-p0, function_len); res=-1; diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c index a5b9f6ae507b..d703e73fffa7 100644 --- a/drivers/pci/hotplug/cpci_hotplug_core.c +++ b/drivers/pci/hotplug/cpci_hotplug_core.c @@ -32,7 +32,6 @@ #include <linux/pci_hotplug.h> #include <linux/init.h> #include <linux/interrupt.h> -#include <linux/smp_lock.h> #include <asm/atomic.h> #include <linux/delay.h> #include <linux/kthread.h> diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c index 2fa47af992a8..0ff689afa757 100644 --- a/drivers/pci/hotplug/cpqphp_ctrl.c +++ b/drivers/pci/hotplug/cpqphp_ctrl.c @@ -34,7 +34,6 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/wait.h> -#include <linux/smp_lock.h> #include <linux/pci.h> #include <linux/pci_hotplug.h> #include <linux/kthread.h> diff --git a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c index 8450f4a6568a..e6089bdb6e5b 100644 --- a/drivers/pci/hotplug/cpqphp_sysfs.c +++ b/drivers/pci/hotplug/cpqphp_sysfs.c @@ -33,6 +33,7 @@ #include <linux/workqueue.h> #include <linux/pci.h> #include <linux/pci_hotplug.h> +#include <linux/smp_lock.h> #include <linux/debugfs.h> #include "cpqphp.h" diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index ff4034502d24..8aab8edf123e 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -30,7 +30,6 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> -#include <linux/smp_lock.h> #include <linux/pci.h> #include <linux/workqueue.h> #include "../pci.h" diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 360fb67a30d7..ebc9b8dca881 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -3600,6 +3600,9 @@ static void intel_iommu_unmap_range(struct iommu_domain *domain, { struct dmar_domain *dmar_domain = domain->priv; + if (!size) + return; + dma_pte_clear_range(dmar_domain, iova >> VTD_PAGE_SHIFT, (iova + size - 1) >> VTD_PAGE_SHIFT); diff --git a/drivers/pci/syscall.c b/drivers/pci/syscall.c index ec22284eed30..e1c1ec540893 100644 --- a/drivers/pci/syscall.c +++ b/drivers/pci/syscall.c @@ -9,7 +9,6 @@ #include <linux/errno.h> #include <linux/pci.h> -#include <linux/smp_lock.h> #include <linux/syscalls.h> #include <asm/uaccess.h> #include "pci.h" diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c index 9ad97ea836e8..8eb04230fec7 100644 --- a/drivers/pcmcia/tcic.c +++ b/drivers/pcmcia/tcic.c @@ -472,7 +472,8 @@ static int __init init_tcic(void) init_timer(&poll_timer); /* Build interrupt mask */ - printk(", %d sockets\n" KERN_INFO " irq list (", sockets); + printk(KERN_CONT ", %d sockets\n", sockets); + printk(KERN_INFO " irq list ("); if (irq_list_count == 0) mask = irq_mask; else diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index be2fd6f91639..fb45f5ee8df1 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -973,7 +973,7 @@ static int acer_rfkill_set(void *data, bool blocked) { acpi_status status; u32 cap = (unsigned long)data; - status = set_u32(!!blocked, cap); + status = set_u32(!blocked, cap); if (ACPI_FAILURE(status)) return -ENODEV; return 0; diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 4ac2311c00af..ca508564a181 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -171,7 +171,7 @@ static int hp_wmi_tablet_state(void) static int hp_wmi_set_block(void *data, bool blocked) { unsigned long b = (unsigned long) data; - int query = BIT(b + 8) | ((!!blocked) << b); + int query = BIT(b + 8) | ((!blocked) << b); return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query); } diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c index 8bde92126d34..b787335a8419 100644 --- a/drivers/power/wm97xx_battery.c +++ b/drivers/power/wm97xx_battery.c @@ -33,14 +33,14 @@ static enum power_supply_property *prop; static unsigned long wm97xx_read_bat(struct power_supply *bat_ps) { - return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, + return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev->parent), pdata->batt_aux) * pdata->batt_mult / pdata->batt_div; } static unsigned long wm97xx_read_temp(struct power_supply *bat_ps) { - return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, + return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev->parent), pdata->temp_aux) * pdata->temp_mult / pdata->temp_div; } diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index f8b1f04f26b8..c11770f5b368 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1696,8 +1696,7 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, DBF_DEV_EVENT(DBF_ERR, device, "%s", "unsolicited interrupt received " "(sense available)"); - device->discipline->dump_sense_dbf(device, NULL, irb, - "unsolicited"); + device->discipline->dump_sense_dbf(device, irb, "unsolicited"); } dasd_schedule_device_bh(device); @@ -2941,42 +2940,20 @@ dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page) } static void -dasd_eckd_dump_sense_dbf(struct dasd_device *device, struct dasd_ccw_req *req, - struct irb *irb, char *reason) +dasd_eckd_dump_sense_dbf(struct dasd_device *device, struct irb *irb, + char *reason) { u64 *sense; - int sl; - struct tsb *tsb; - sense = NULL; - tsb = NULL; - if (req && scsw_is_tm(&req->irb.scsw)) { - if (irb->scsw.tm.tcw) - tsb = tcw_get_tsb( - (struct tcw *)(unsigned long)irb->scsw.tm.tcw); - if (tsb && (irb->scsw.tm.fcxs == 0x01)) { - switch (tsb->flags & 0x07) { - case 1: /* tsa_iostat */ - sense = (u64 *)tsb->tsa.iostat.sense; - break; - case 2: /* ts_ddpc */ - sense = (u64 *)tsb->tsa.ddpc.sense; - break; - case 3: /* tsa_intrg */ - break; - } - } - } else { - if (irb->esw.esw0.erw.cons) - sense = (u64 *)irb->ecw; - } + sense = (u64 *) dasd_get_sense(irb); if (sense) { - for (sl = 0; sl < 4; sl++) { - DBF_DEV_EVENT(DBF_EMERG, device, - "%s: %016llx %016llx %016llx %016llx", - reason, sense[0], sense[1], sense[2], - sense[3]); - } + DBF_DEV_EVENT(DBF_EMERG, device, + "%s: %s %02x%02x%02x %016llx %016llx %016llx " + "%016llx", reason, + scsw_is_tm(&irb->scsw) ? "t" : "c", + scsw_cc(&irb->scsw), scsw_cstat(&irb->scsw), + scsw_dstat(&irb->scsw), sense[0], sense[1], + sense[2], sense[3]); } else { DBF_DEV_EVENT(DBF_EMERG, device, "%s", "SORRY - NO VALID SENSE AVAILABLE\n"); diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index d970ce2814be..cb8f9cef7429 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -172,7 +172,7 @@ dasd_log_sense_dbf(struct dasd_ccw_req *cqr, struct irb *irb) device = cqr->startdev; /* dump sense data to s390 debugfeature*/ if (device->discipline && device->discipline->dump_sense_dbf) - device->discipline->dump_sense_dbf(device, cqr, irb, "log"); + device->discipline->dump_sense_dbf(device, irb, "log"); } EXPORT_SYMBOL(dasd_log_sense_dbf); diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index e21ee735f926..31849ad5e59f 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -241,7 +241,7 @@ static void dasd_fba_handle_unsolicited_interrupt(struct dasd_device *device, /* check for unsolicited interrupts */ DBF_DEV_EVENT(DBF_WARNING, device, "%s", "unsolicited interrupt received"); - device->discipline->dump_sense_dbf(device, NULL, irb, "unsolicited"); + device->discipline->dump_sense_dbf(device, irb, "unsolicited"); dasd_schedule_device_bh(device); return; }; @@ -444,17 +444,20 @@ dasd_fba_fill_info(struct dasd_device * device, } static void -dasd_fba_dump_sense_dbf(struct dasd_device *device, struct dasd_ccw_req *req, - struct irb *irb, char *reason) +dasd_fba_dump_sense_dbf(struct dasd_device *device, struct irb *irb, + char *reason) { - int sl; - if (irb->esw.esw0.erw.cons) { - for (sl = 0; sl < 4; sl++) { - DBF_DEV_EVENT(DBF_EMERG, device, - "%s: %08x %08x %08x %08x", - reason, irb->ecw[8 * 0], irb->ecw[8 * 1], - irb->ecw[8 * 2], irb->ecw[8 * 3]); - } + u64 *sense; + + sense = (u64 *) dasd_get_sense(irb); + if (sense) { + DBF_DEV_EVENT(DBF_EMERG, device, + "%s: %s %02x%02x%02x %016llx %016llx %016llx " + "%016llx", reason, + scsw_is_tm(&irb->scsw) ? "t" : "c", + scsw_cc(&irb->scsw), scsw_cstat(&irb->scsw), + scsw_dstat(&irb->scsw), sense[0], sense[1], + sense[2], sense[3]); } else { DBF_DEV_EVENT(DBF_EMERG, device, "%s", "SORRY - NO VALID SENSE AVAILABLE\n"); diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index fd63b2f2bda9..b699ca356ac5 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -284,8 +284,7 @@ struct dasd_discipline { dasd_erp_fn_t(*erp_postaction) (struct dasd_ccw_req *); void (*dump_sense) (struct dasd_device *, struct dasd_ccw_req *, struct irb *); - void (*dump_sense_dbf) (struct dasd_device *, struct dasd_ccw_req *, - struct irb *, char *); + void (*dump_sense_dbf) (struct dasd_device *, struct irb *, char *); void (*handle_unsolicited_interrupt) (struct dasd_device *, struct irb *); diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 4ce3f72ee1c1..df918ef27965 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -16,6 +16,7 @@ #include <linux/major.h> #include <linux/fs.h> #include <linux/blkpg.h> +#include <linux/smp_lock.h> #include <asm/ccwdev.h> #include <asm/cmb.h> diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 016f9e9d2591..d34617682a62 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -964,7 +964,8 @@ static int dcssblk_freeze(struct device *dev) break; } if (rc) - pr_err("Suspend failed because device %s is writeable.\n", + pr_err("Suspending the system failed because DCSS device %s " + "is writable\n", dev_info->segment_name); return rc; } @@ -987,8 +988,8 @@ static int dcssblk_restore(struct device *dev) goto out_panic; } if (start != entry->start || end != entry->end) { - pr_err("Mismatch of start / end address after " - "resuming device %s\n", + pr_err("The address range of DCSS %s changed " + "while the system was suspended\n", entry->segment_name); goto out_panic; } diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index 2e9e1ecd6d82..db442cd6621e 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c @@ -443,7 +443,7 @@ fail: */ static void xpram_resume_error(const char *message) { - pr_err("Resume error: %s\n", message); + pr_err("Resuming the system failed: %s\n", message); panic("xpram resume error\n"); } diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 7892550d7932..3234e90bd7f9 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -320,7 +320,7 @@ static int mon_open(struct inode *inode, struct file *filp) goto out_path; } filp->private_data = monpriv; - dev_set_drvdata(&monreader_device, monpriv); + dev_set_drvdata(monreader_device, monpriv); unlock_kernel(); return nonseekable_open(inode, filp); @@ -463,7 +463,7 @@ static struct miscdevice mon_dev = { *****************************************************************************/ static int monreader_freeze(struct device *dev) { - struct mon_private *monpriv = dev_get_drvdata(&dev); + struct mon_private *monpriv = dev_get_drvdata(dev); int rc; if (!monpriv) diff --git a/drivers/s390/char/sclp_rw.h b/drivers/s390/char/sclp_rw.h index 85f491ea929c..7a7bfc947d97 100644 --- a/drivers/s390/char/sclp_rw.h +++ b/drivers/s390/char/sclp_rw.h @@ -92,5 +92,10 @@ void sclp_set_columns(struct sclp_buffer *, unsigned short); void sclp_set_htab(struct sclp_buffer *, unsigned short); int sclp_chars_in_buffer(struct sclp_buffer *); +#ifdef CONFIG_SCLP_CONSOLE void sclp_console_pm_event(enum sclp_pm_event sclp_pm_event); +#else +static inline void sclp_console_pm_event(enum sclp_pm_event sclp_pm_event) { } +#endif + #endif /* __SCLP_RW_H__ */ diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index cb7854c10c04..f2bc287b69e4 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c @@ -250,14 +250,14 @@ static int vmwdt_resume(void) static int vmwdt_suspend(void) { if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) { - pr_err("The watchdog is in use. " - "This prevents hibernation or suspend.\n"); + pr_err("The system cannot be suspended while the watchdog" + " is in use\n"); return NOTIFY_BAD; } if (test_bit(VMWDT_RUNNING, &vmwdt_is_open)) { clear_bit(VMWDT_OPEN, &vmwdt_is_open); - pr_err("The watchdog is running. " - "This prevents hibernation or suspend.\n"); + pr_err("The system cannot be suspended while the watchdog" + " is running\n"); return NOTIFY_BAD; } return NOTIFY_DONE; diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 727a809636d8..ed3dcdea7fe1 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1145,12 +1145,17 @@ ap_config_timeout(unsigned long ptr) */ static inline void ap_schedule_poll_timer(void) { + ktime_t hr_time; if (ap_using_interrupts() || ap_suspend_flag) return; if (hrtimer_is_queued(&ap_poll_timer)) return; - hrtimer_start(&ap_poll_timer, ktime_set(0, poll_timeout), - HRTIMER_MODE_ABS); + if (ktime_to_ns(hrtimer_expires_remaining(&ap_poll_timer)) <= 0) { + hr_time = ktime_set(0, poll_timeout); + hrtimer_forward_now(&ap_poll_timer, hr_time); + hrtimer_restart(&ap_poll_timer); + } + return; } /** diff --git a/drivers/scsi/atari_NCR5380.c b/drivers/scsi/atari_NCR5380.c index 0471f8800483..4240b05aef6d 100644 --- a/drivers/scsi/atari_NCR5380.c +++ b/drivers/scsi/atari_NCR5380.c @@ -2826,8 +2826,7 @@ int NCR5380_abort(Scsi_Cmnd *cmd) */ local_irq_restore(flags); - printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully\n" - KERN_INFO " before abortion\n", HOSTNO); + printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully before abortion\n", HOSTNO); /* Maybe it is sufficient just to release the ST-DMA lock... (if * possible at all) At least, we should check if the lock could be diff --git a/drivers/scsi/mac53c94.c b/drivers/scsi/mac53c94.c index b12ad7c7c673..18735b39b3d3 100644 --- a/drivers/scsi/mac53c94.c +++ b/drivers/scsi/mac53c94.c @@ -75,8 +75,9 @@ static int mac53c94_queue(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd * int i; printk(KERN_DEBUG "mac53c94_queue %p: command is", cmd); for (i = 0; i < cmd->cmd_len; ++i) - printk(" %.2x", cmd->cmnd[i]); - printk("\n" KERN_DEBUG "use_sg=%d request_bufflen=%d request_buffer=%p\n", + printk(KERN_CONT " %.2x", cmd->cmnd[i]); + printk(KERN_CONT "\n"); + printk(KERN_DEBUG "use_sg=%d request_bufflen=%d request_buffer=%p\n", scsi_sg_count(cmd), scsi_bufflen(cmd), scsi_sglist(cmd)); } #endif diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index 650bcef08f2a..cd78c501803a 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -9,7 +9,6 @@ #include <linux/moduleparam.h> #include <linux/vmalloc.h> -#include <linux/smp_lock.h> #include <linux/list.h> #include <scsi/scsi_tcq.h> diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index ef142fd47a83..9230402c45af 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -619,7 +619,7 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) if (strcmp(current->comm, cmd) && printk_ratelimit()) { printk(KERN_WARNING "sg_write: data in/out %d/%d bytes for SCSI command 0x%x--" - "guessing data in;\n" KERN_WARNING " " + "guessing data in;\n " "program %s not setting count and/or reply_len properly\n", old_hdr.reply_len - (int)SZ_SG_HEADER, input_size, (unsigned int) cmnd[0], @@ -1656,6 +1656,10 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) md->nr_entries = req_schp->k_use_sg; md->offset = 0; md->null_mapped = hp->dxferp ? 0 : 1; + if (dxfer_dir == SG_DXFER_TO_FROM_DEV) + md->from_user = 1; + else + md->from_user = 0; } if (iov_count) { diff --git a/drivers/scsi/sun3_NCR5380.c b/drivers/scsi/sun3_NCR5380.c index bcaba86060ab..75da6e58ce55 100644 --- a/drivers/scsi/sun3_NCR5380.c +++ b/drivers/scsi/sun3_NCR5380.c @@ -2860,8 +2860,7 @@ static int NCR5380_abort(struct scsi_cmnd *cmd) */ local_irq_restore(flags); - printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully\n" - KERN_INFO " before abortion\n", HOSTNO); + printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully before abortion\n", HOSTNO); return SCSI_ABORT_NOT_RUNNING; } diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index 6160e03f410c..e7108e75653d 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -60,11 +60,12 @@ struct serial_private { static void moan_device(const char *str, struct pci_dev *dev) { - printk(KERN_WARNING "%s: %s\n" - KERN_WARNING "Please send the output of lspci -vv, this\n" - KERN_WARNING "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" - KERN_WARNING "manufacturer and name of serial board or\n" - KERN_WARNING "modem board to rmk+serial@arm.linux.org.uk.\n", + printk(KERN_WARNING + "%s: %s\n" + "Please send the output of lspci -vv, this\n" + "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" + "manufacturer and name of serial board or\n" + "modem board to rmk+serial@arm.linux.org.uk.\n", pci_name(dev), str, dev->vendor, dev->device, dev->subsystem_vendor, dev->subsystem_device); } diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 34b4ae0fe760..c108b1a0ce98 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -236,7 +236,6 @@ static int sport_startup(struct uart_port *port) int retval; pr_debug("%s enter\n", __func__); - memset(buffer, 20, '\0'); snprintf(buffer, 20, "%s rx", up->name); retval = request_irq(up->rx_irq, sport_uart_rx_irq, IRQF_SAMPLE_RANDOM, buffer, up); if (retval) { diff --git a/drivers/serial/msm_serial.c b/drivers/serial/msm_serial.c index 698048f64f5e..f7c24baa1416 100644 --- a/drivers/serial/msm_serial.c +++ b/drivers/serial/msm_serial.c @@ -730,7 +730,6 @@ static int __devexit msm_serial_remove(struct platform_device *pdev) } static struct platform_driver msm_platform_driver = { - .probe = msm_serial_probe, .remove = msm_serial_remove, .driver = { .name = "msm_serial", diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 66f52674ca0c..8e2feb563347 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -707,24 +707,25 @@ static irqreturn_t sci_br_interrupt(int irq, void *ptr) static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) { - unsigned short ssr_status, scr_status; + unsigned short ssr_status, scr_status, err_enabled; struct uart_port *port = ptr; irqreturn_t ret = IRQ_NONE; ssr_status = sci_in(port, SCxSR); scr_status = sci_in(port, SCSCR); + err_enabled = scr_status & (SCI_CTRL_FLAGS_REIE | SCI_CTRL_FLAGS_RIE); /* Tx Interrupt */ - if ((ssr_status & 0x0020) && (scr_status & SCI_CTRL_FLAGS_TIE)) + if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCI_CTRL_FLAGS_TIE)) ret = sci_tx_interrupt(irq, ptr); /* Rx Interrupt */ - if ((ssr_status & 0x0002) && (scr_status & SCI_CTRL_FLAGS_RIE)) + if ((ssr_status & SCxSR_RDxF(port)) && (scr_status & SCI_CTRL_FLAGS_RIE)) ret = sci_rx_interrupt(irq, ptr); /* Error Interrupt */ - if ((ssr_status & 0x0080) && (scr_status & SCI_CTRL_FLAGS_REIE)) + if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled) ret = sci_er_interrupt(irq, ptr); /* Break Interrupt */ - if ((ssr_status & 0x0010) && (scr_status & SCI_CTRL_FLAGS_REIE)) + if ((ssr_status & SCxSR_BRK(port)) && err_enabled) ret = sci_br_interrupt(irq, ptr); return ret; diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c index fbfadbac67e8..100e7a5c5ea1 100644 --- a/drivers/ssb/pcmcia.c +++ b/drivers/ssb/pcmcia.c @@ -583,7 +583,7 @@ static int ssb_pcmcia_sprom_write_all(struct ssb_bus *bus, const u16 *sprom) ssb_printk("."); err = ssb_pcmcia_sprom_write(bus, i, sprom[i]); if (err) { - ssb_printk("\n" KERN_NOTICE PFX + ssb_printk(KERN_NOTICE PFX "Failed to write to SPROM.\n"); failed = 1; break; @@ -591,7 +591,7 @@ static int ssb_pcmcia_sprom_write_all(struct ssb_bus *bus, const u16 *sprom) } err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEDIS); if (err) { - ssb_printk("\n" KERN_NOTICE PFX + ssb_printk(KERN_NOTICE PFX "Could not disable SPROM write access.\n"); failed = 1; } @@ -678,7 +678,8 @@ int ssb_pcmcia_get_invariants(struct ssb_bus *bus, sprom->board_rev = tuple.TupleData[1]; break; case SSB_PCMCIA_CIS_PA: - GOTO_ERROR_ON(tuple.TupleDataLen != 9, + GOTO_ERROR_ON((tuple.TupleDataLen != 9) && + (tuple.TupleDataLen != 10), "pa tpl size"); sprom->pa0b0 = tuple.TupleData[1] | ((u16)tuple.TupleData[2] << 8); @@ -718,7 +719,8 @@ int ssb_pcmcia_get_invariants(struct ssb_bus *bus, sprom->antenna_gain.ghz5.a3 = tuple.TupleData[1]; break; case SSB_PCMCIA_CIS_BFLAGS: - GOTO_ERROR_ON(tuple.TupleDataLen != 3, + GOTO_ERROR_ON((tuple.TupleDataLen != 3) && + (tuple.TupleDataLen != 5), "bfl tpl size"); sprom->boardflags_lo = tuple.TupleData[1] | ((u16)tuple.TupleData[2] << 8); diff --git a/drivers/staging/comedi/drivers/jr3_pci.c b/drivers/staging/comedi/drivers/jr3_pci.c index baf83c6a9412..e3c3adc282e2 100644 --- a/drivers/staging/comedi/drivers/jr3_pci.c +++ b/drivers/staging/comedi/drivers/jr3_pci.c @@ -45,6 +45,8 @@ Devices: [JR3] PCI force sensor board (jr3_pci) #include <linux/delay.h> #include <linux/ctype.h> #include <linux/firmware.h> +#include <linux/jiffies.h> +#include <linux/timer.h> #include "comedi_pci.h" #include "jr3_pci.h" diff --git a/drivers/staging/comedi/drivers/s626.c b/drivers/staging/comedi/drivers/s626.c index 92121cf8c45c..5d9bab352c1d 100644 --- a/drivers/staging/comedi/drivers/s626.c +++ b/drivers/staging/comedi/drivers/s626.c @@ -111,9 +111,13 @@ static const struct s626_board s626_boards[] = { #define PCI_VENDOR_ID_S626 0x1131 #define PCI_DEVICE_ID_S626 0x7146 +/* + * For devices with vendor:device id == 0x1131:0x7146 you must specify + * also subvendor:subdevice ids, because otherwise it will conflict with + * Philips SAA7146 media/dvb based cards. + */ static DEFINE_PCI_DEVICE_TABLE(s626_pci_table) = { - {PCI_VENDOR_ID_S626, PCI_DEVICE_ID_S626, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - 0}, + {PCI_VENDOR_ID_S626, PCI_DEVICE_ID_S626, 0x6000, 0x0272, 0, 0, 0}, {0} }; @@ -499,25 +503,26 @@ static int s626_attach(struct comedi_device *dev, struct comedi_devconfig *it) resource_size_t resourceStart; dma_addr_t appdma; struct comedi_subdevice *s; - struct pci_dev *pdev; + const struct pci_device_id *ids; + struct pci_dev *pdev = NULL; if (alloc_private(dev, sizeof(struct s626_private)) < 0) return -ENOMEM; - for (pdev = pci_get_device(PCI_VENDOR_ID_S626, PCI_DEVICE_ID_S626, - NULL); pdev != NULL; - pdev = pci_get_device(PCI_VENDOR_ID_S626, - PCI_DEVICE_ID_S626, pdev)) { - if (it->options[0] || it->options[1]) { - if (pdev->bus->number == it->options[0] && - PCI_SLOT(pdev->devfn) == it->options[1]) { + for (i = 0; i < (ARRAY_SIZE(s626_pci_table) - 1) && !pdev; i++) { + ids = &s626_pci_table[i]; + do { + pdev = pci_get_subsys(ids->vendor, ids->device, ids->subvendor, + ids->subdevice, pdev); + + if ((it->options[0] || it->options[1]) && pdev) { /* matches requested bus/slot */ + if (pdev->bus->number == it->options[0] && + PCI_SLOT(pdev->devfn) == it->options[1]) + break; + } else break; - } - } else { - /* no bus/slot specified */ - break; - } + } while (1); } devpriv->pdev = pdev; diff --git a/drivers/staging/go7007/s2250-loader.c b/drivers/staging/go7007/s2250-loader.c index a5e4acab089e..bb22347af60e 100644 --- a/drivers/staging/go7007/s2250-loader.c +++ b/drivers/staging/go7007/s2250-loader.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/init.h> +#include <linux/smp_lock.h> #include <linux/usb.h> #include <dvb-usb.h> diff --git a/drivers/staging/meilhaus/TODO b/drivers/staging/meilhaus/TODO index 6ec25203089c..d6ce39823de6 100644 --- a/drivers/staging/meilhaus/TODO +++ b/drivers/staging/meilhaus/TODO @@ -7,4 +7,4 @@ TODO: - possible comedi merge Please send cleanup patches to Greg Kroah-Hartman <greg@kroah.com> -and CC: David Kiliani <mail@davidkiliani.de> +and CC: David Kiliani <mail@davidkiliani.de> and Meilhaus Support <support@meilhaus.de> diff --git a/drivers/staging/rspiusb/rspiusb.c b/drivers/staging/rspiusb/rspiusb.c index 1cdfe69585ea..2f8155c1968b 100644 --- a/drivers/staging/rspiusb/rspiusb.c +++ b/drivers/staging/rspiusb/rspiusb.c @@ -444,8 +444,7 @@ static void piusb_write_bulk_callback(struct urb *urb) __func__, status); pdx->pendingWrite = 0; - usb_buffer_free(urb->dev, urb->transfer_buffer_length, - urb->transfer_buffer, urb->transfer_dma); + kfree(urb->transfer_buffer); } int piusb_output(struct ioctl_struct *io, unsigned char *uBuf, int len, @@ -457,9 +456,7 @@ int piusb_output(struct ioctl_struct *io, unsigned char *uBuf, int len, urb = usb_alloc_urb(0, GFP_KERNEL); if (urb != NULL) { - kbuf = - usb_buffer_alloc(pdx->udev, len, GFP_KERNEL, - &urb->transfer_dma); + kbuf = kmalloc(len, GFP_KERNEL); if (!kbuf) { dev_err(&pdx->udev->dev, "buffer_alloc failed\n"); return -ENOMEM; @@ -470,7 +467,6 @@ int piusb_output(struct ioctl_struct *io, unsigned char *uBuf, int len, } usb_fill_bulk_urb(urb, pdx->udev, pdx->hEP[io->endpoint], kbuf, len, piusb_write_bulk_callback, pdx); - urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; err = usb_submit_urb(urb, GFP_KERNEL); if (err) { dev_err(&pdx->udev->dev, @@ -641,7 +637,7 @@ static int MapUserBuffer(struct ioctl_struct *io, struct device_extension *pdx) numPagesRequired = ((uaddr & ~PAGE_MASK) + count + ~PAGE_MASK) >> PAGE_SHIFT; dbg("Number of pages needed = %d", numPagesRequired); - maplist_p = vmalloc(numPagesRequired * sizeof(struct page)); + maplist_p = vmalloc(numPagesRequired * sizeof(struct page *)); if (!maplist_p) { dbg("Can't Allocate Memory for maplist_p"); return -ENOMEM; @@ -712,9 +708,7 @@ static int MapUserBuffer(struct ioctl_struct *io, struct device_extension *pdx) usb_fill_bulk_urb(pdx->PixelUrb[frameInfo][i], pdx->udev, epAddr, - (dma_addr_t *) sg_dma_address(&pdx-> - sgl[frameInfo] - [i]), + NULL, // non-DMA HC? buy a better hardware sg_dma_len(&pdx->sgl[frameInfo][i]), piusb_readPIXEL_callback, (void *)pdx); pdx->PixelUrb[frameInfo][i]->transfer_dma = diff --git a/drivers/staging/rt2870/rt2870.h b/drivers/staging/rt2870/rt2870.h index 5e5b3f2b7eb1..29e3b53e52a1 100644 --- a/drivers/staging/rt2870/rt2870.h +++ b/drivers/staging/rt2870/rt2870.h @@ -89,6 +89,7 @@ {USB_DEVICE(0x0DF6,0x002C)}, /* Sitecom */ \ {USB_DEVICE(0x0DF6,0x002D)}, /* Sitecom */ \ {USB_DEVICE(0x0DF6,0x0039)}, /* Sitecom */ \ + {USB_DEVICE(0x0DF6,0x003F)}, /* Sitecom WL-608 */ \ {USB_DEVICE(0x14B2,0x3C06)}, /* Conceptronic */ \ {USB_DEVICE(0x14B2,0x3C28)}, /* Conceptronic */ \ {USB_DEVICE(0x2019,0xED06)}, /* Planex Communications, Inc. */ \ diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c index 93af37e2d31a..54b4b718f84a 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c @@ -461,19 +461,19 @@ int ieee80211_wx_get_name(struct ieee80211_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - strcpy(wrqu->name, "802.11"); + strlcpy(wrqu->name, "802.11", IFNAMSIZ); if(ieee->modulation & IEEE80211_CCK_MODULATION){ - strcat(wrqu->name, "b"); + strlcat(wrqu->name, "b", IFNAMSIZ); if(ieee->modulation & IEEE80211_OFDM_MODULATION) - strcat(wrqu->name, "/g"); + strlcat(wrqu->name, "/g", IFNAMSIZ); }else if(ieee->modulation & IEEE80211_OFDM_MODULATION) - strcat(wrqu->name, "g"); + strlcat(wrqu->name, "g", IFNAMSIZ); if((ieee->state == IEEE80211_LINKED) || (ieee->state == IEEE80211_LINKED_SCANNING)) - strcat(wrqu->name," linked"); + strlcat(wrqu->name," link", IFNAMSIZ); else if(ieee->state != IEEE80211_NOLINK) - strcat(wrqu->name," link.."); + strlcat(wrqu->name," .....", IFNAMSIZ); return 0; diff --git a/drivers/staging/rtl8192su/Kconfig b/drivers/staging/rtl8192su/Kconfig index 4b5552c5926e..770f41280f21 100644 --- a/drivers/staging/rtl8192su/Kconfig +++ b/drivers/staging/rtl8192su/Kconfig @@ -1,6 +1,6 @@ config RTL8192SU tristate "RealTek RTL8192SU Wireless LAN NIC driver" depends on PCI - depends on WIRELESS_EXT && COMPAT_NET_DEV_OPS + depends on WIRELESS_EXT default N ---help--- diff --git a/drivers/staging/rtl8192su/ieee80211/ieee80211_module.c b/drivers/staging/rtl8192su/ieee80211/ieee80211_module.c index f408b4583b82..759032db4a34 100644 --- a/drivers/staging/rtl8192su/ieee80211/ieee80211_module.c +++ b/drivers/staging/rtl8192su/ieee80211/ieee80211_module.c @@ -118,7 +118,6 @@ struct net_device *alloc_ieee80211(int sizeof_priv) #else ieee = (struct ieee80211_device *)dev->priv; #endif - dev->hard_start_xmit = ieee80211_xmit; memset(ieee, 0, sizeof(struct ieee80211_device)+sizeof_priv); ieee->dev = dev; diff --git a/drivers/staging/rtl8192su/ieee80211/ieee80211_softmac_wx.c b/drivers/staging/rtl8192su/ieee80211/ieee80211_softmac_wx.c index 1f50c46dcb90..191dc3fbbe32 100644 --- a/drivers/staging/rtl8192su/ieee80211/ieee80211_softmac_wx.c +++ b/drivers/staging/rtl8192su/ieee80211/ieee80211_softmac_wx.c @@ -548,21 +548,21 @@ int ieee80211_wx_get_name(struct ieee80211_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - strcpy(wrqu->name, "802.11"); + strlcpy(wrqu->name, "802.11", IFNAMSIZ); if(ieee->modulation & IEEE80211_CCK_MODULATION){ - strcat(wrqu->name, "b"); + strlcat(wrqu->name, "b", IFNAMSIZ); if(ieee->modulation & IEEE80211_OFDM_MODULATION) - strcat(wrqu->name, "/g"); + strlcat(wrqu->name, "/g", IFNAMSIZ); }else if(ieee->modulation & IEEE80211_OFDM_MODULATION) - strcat(wrqu->name, "g"); + strlcat(wrqu->name, "g", IFNAMSIZ); if (ieee->mode & (IEEE_N_24G | IEEE_N_5G)) - strcat(wrqu->name, "/n"); + strlcat(wrqu->name, "/n", IFNAMSIZ); if((ieee->state == IEEE80211_LINKED) || (ieee->state == IEEE80211_LINKED_SCANNING)) - strcat(wrqu->name," linked"); + strlcat(wrqu->name, " link", IFNAMSIZ); else if(ieee->state != IEEE80211_NOLINK) - strcat(wrqu->name," link.."); + strlcat(wrqu->name, " .....", IFNAMSIZ); return 0; diff --git a/drivers/staging/rtl8192su/r8192U_core.c b/drivers/staging/rtl8192su/r8192U_core.c index f1423d714496..4ab250743e81 100644 --- a/drivers/staging/rtl8192su/r8192U_core.c +++ b/drivers/staging/rtl8192su/r8192U_core.c @@ -12132,6 +12132,19 @@ static void HalUsbSetQueuePipeMapping8192SUsb(struct usb_interface *intf, struct } #endif +static const struct net_device_ops rtl8192_netdev_ops = { + .ndo_open = rtl8192_open, + .ndo_stop = rtl8192_close, + .ndo_get_stats = rtl8192_stats, + .ndo_tx_timeout = tx_timeout, + .ndo_do_ioctl = rtl8192_ioctl, + .ndo_set_multicast_list = r8192_set_multicast, + .ndo_set_mac_address = r8192_set_mac_adr, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = eth_change_mtu, + .ndo_start_xmit = ieee80211_xmit, +}; + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) static int __devinit rtl8192_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -12186,15 +12199,7 @@ static void * __devinit rtl8192_usb_probe(struct usb_device *udev, priv->ops = &rtl8192u_ops; #endif - dev->open = rtl8192_open; - dev->stop = rtl8192_close; - //dev->hard_start_xmit = rtl8192_8023_hard_start_xmit; - dev->tx_timeout = tx_timeout; - //dev->wireless_handlers = &r8192_wx_handlers_def; - dev->do_ioctl = rtl8192_ioctl; - dev->set_multicast_list = r8192_set_multicast; - dev->set_mac_address = r8192_set_mac_adr; - dev->get_stats = rtl8192_stats; + dev->netdev_ops = &rtl8192_netdev_ops; //DMESG("Oops: i'm coming\n"); #if WIRELESS_EXT >= 12 diff --git a/drivers/staging/rtl8192su/r8192U_pm.c b/drivers/staging/rtl8192su/r8192U_pm.c index 92c95aa36638..b1531a8d0cde 100644 --- a/drivers/staging/rtl8192su/r8192U_pm.c +++ b/drivers/staging/rtl8192su/r8192U_pm.c @@ -35,7 +35,9 @@ int rtl8192U_suspend(struct usb_interface *intf, pm_message_t state) return 0; } - dev->stop(dev); + if (dev->netdev_ops->ndo_stop) + dev->netdev_ops->ndo_stop(dev); + mdelay(10); netif_device_detach(dev); @@ -61,7 +63,9 @@ int rtl8192U_resume (struct usb_interface *intf) } netif_device_attach(dev); - dev->open(dev); + + if (dev->netdev_ops->ndo_open) + dev->netdev_ops->ndo_open(dev); } return 0; diff --git a/drivers/staging/serqt_usb2/serqt_usb2.c b/drivers/staging/serqt_usb2/serqt_usb2.c index 90b29b564631..a9bd4106beb7 100644 --- a/drivers/staging/serqt_usb2/serqt_usb2.c +++ b/drivers/staging/serqt_usb2/serqt_usb2.c @@ -866,7 +866,7 @@ static void qt_release(struct usb_serial *serial) } -int qt_open(struct tty_struct *tty, +static int qt_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial; @@ -1041,17 +1041,19 @@ static void qt_block_until_empty(struct tty_struct *tty, } } -static void qt_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static void qt_close( struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct quatech_port *qt_port; struct quatech_port *port0; + struct tty_struct *tty; int status; unsigned int index; status = 0; dbg("%s - port %d\n", __func__, port->number); + + tty = tty_port_tty_get(&port->port); index = tty->index - serial->minor; qt_port = qt_get_port_private(port); diff --git a/drivers/staging/stlc45xx/stlc45xx.c b/drivers/staging/stlc45xx/stlc45xx.c index cfdaac9b747e..a137c78fac09 100644 --- a/drivers/staging/stlc45xx/stlc45xx.c +++ b/drivers/staging/stlc45xx/stlc45xx.c @@ -2235,24 +2235,6 @@ static void stlc45xx_op_remove_interface(struct ieee80211_hw *hw, stlc45xx_debug(DEBUG_FUNC, "%s", __func__); } -static int stlc45xx_op_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct stlc45xx *stlc = hw->priv; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - mutex_lock(&stlc->mutex); - - memcpy(stlc->bssid, conf->bssid, ETH_ALEN); - stlc45xx_tx_setup(stlc); - - mutex_unlock(&stlc->mutex); - - return 0; -} - static int stlc45xx_op_config(struct ieee80211_hw *hw, u32 changed) { struct stlc45xx *stlc = hw->priv; @@ -2295,6 +2277,14 @@ static void stlc45xx_op_bss_info_changed(struct ieee80211_hw *hw, { struct stlc45xx *stlc = hw->priv; + stlc45xx_debug(DEBUG_FUNC, "%s", __func__); + mutex_lock(&stlc->mutex); + + memcpy(stlc->bssid, info->bssid, ETH_ALEN); + stlc45xx_tx_setup(stlc); + + mutex_unlock(&stlc->mutex); + if (changed & BSS_CHANGED_ASSOC) { stlc->associated = info->assoc; if (info->assoc) @@ -2357,7 +2347,6 @@ static const struct ieee80211_ops stlc45xx_ops = { .add_interface = stlc45xx_op_add_interface, .remove_interface = stlc45xx_op_remove_interface, .config = stlc45xx_op_config, - .config_interface = stlc45xx_op_config_interface, .configure_filter = stlc45xx_op_configure_filter, .tx = stlc45xx_op_tx, .bss_info_changed = stlc45xx_op_bss_info_changed, diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c index 22f93dd0ba03..251220dc8851 100644 --- a/drivers/staging/usbip/usbip_common.c +++ b/drivers/staging/usbip/usbip_common.c @@ -18,6 +18,7 @@ */ #include <linux/kernel.h> +#include <linux/smp_lock.h> #include <linux/file.h> #include <linux/tcp.h> #include <linux/in.h> diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c index a10ed27acbc2..f43ca416e4a8 100644 --- a/drivers/staging/vt6655/device_main.c +++ b/drivers/staging/vt6655/device_main.c @@ -344,7 +344,7 @@ static CHIP_INFO chip_info_table[]= { }; static struct pci_device_id device_id_table[] __devinitdata = { -{ 0x1106, 0x3253, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (int)&chip_info_table[0]}, +{ 0x1106, 0x3253, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long)&chip_info_table[0]}, { 0, } }; #endif @@ -369,7 +369,7 @@ static int device_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); #ifdef CONFIG_PM static int device_notify_reboot(struct notifier_block *, unsigned long event, void *ptr); -static int viawget_suspend(struct pci_dev *pcid, u32 state); +static int viawget_suspend(struct pci_dev *pcid, pm_message_t state); static int viawget_resume(struct pci_dev *pcid); struct notifier_block device_notifier = { notifier_call: device_notify_reboot, @@ -3941,7 +3941,7 @@ device_notify_reboot(struct notifier_block *nb, unsigned long event, void *p) while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) { if(pci_dev_driver(pdev) == &device_driver) { if (pci_get_drvdata(pdev)) - viawget_suspend(pdev, 3); + viawget_suspend(pdev, PMSG_HIBERNATE); } } } @@ -3949,7 +3949,7 @@ device_notify_reboot(struct notifier_block *nb, unsigned long event, void *p) } static int -viawget_suspend(struct pci_dev *pcid, u32 state) +viawget_suspend(struct pci_dev *pcid, pm_message_t state) { int power_status; // to silence the compiler @@ -3971,7 +3971,7 @@ viawget_suspend(struct pci_dev *pcid, u32 state) memset(pMgmt->abyCurrBSSID, 0, 6); pMgmt->eCurrState = WMAC_STATE_IDLE; pci_disable_device(pcid); - power_status = pci_set_power_state(pcid, state); + power_status = pci_set_power_state(pcid, pci_choose_state(pcid, state)); spin_unlock_irq(&pDevice->lock); return 0; } diff --git a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c index a913efc69669..40de151f2789 100644 --- a/drivers/telephony/ixj.c +++ b/drivers/telephony/ixj.c @@ -257,6 +257,7 @@ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/mm.h> #include <linux/ioport.h> #include <linux/interrupt.h> diff --git a/drivers/telephony/phonedev.c b/drivers/telephony/phonedev.c index b52cc830c0b4..f3873f650bb4 100644 --- a/drivers/telephony/phonedev.c +++ b/drivers/telephony/phonedev.c @@ -23,7 +23,6 @@ #include <linux/errno.h> #include <linux/phonedev.h> #include <linux/init.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/system.h> diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 3f1045993474..e1f89416ef8c 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -387,6 +387,7 @@ static void acm_rx_tasklet(unsigned long _acm) struct acm_ru *rcv; unsigned long flags; unsigned char throttled; + struct usb_host_endpoint *ep; dbg("Entering acm_rx_tasklet"); @@ -462,11 +463,20 @@ urbs: rcv->buffer = buf; - usb_fill_bulk_urb(rcv->urb, acm->dev, - acm->rx_endpoint, - buf->base, - acm->readsize, - acm_read_bulk, rcv); + ep = (usb_pipein(acm->rx_endpoint) ? acm->dev->ep_in : acm->dev->ep_out) + [usb_pipeendpoint(acm->rx_endpoint)]; + if (usb_endpoint_xfer_int(&ep->desc)) + usb_fill_int_urb(rcv->urb, acm->dev, + acm->rx_endpoint, + buf->base, + acm->readsize, + acm_read_bulk, rcv, ep->desc.bInterval); + else + usb_fill_bulk_urb(rcv->urb, acm->dev, + acm->rx_endpoint, + buf->base, + acm->readsize, + acm_read_bulk, rcv); rcv->urb->transfer_dma = buf->dma; rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; @@ -740,7 +750,7 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty) { struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) - return -EINVAL; + return 0; /* * This is inaccurate (overcounts), but it works. */ @@ -1227,9 +1237,14 @@ made_compressed_probe: goto alloc_fail7; } - usb_fill_bulk_urb(snd->urb, usb_dev, - usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), - NULL, acm->writesize, acm_write_bulk, snd); + if (usb_endpoint_xfer_int(epwrite)) + usb_fill_int_urb(snd->urb, usb_dev, + usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), + NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval); + else + usb_fill_bulk_urb(snd->urb, usb_dev, + usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), + NULL, acm->writesize, acm_write_bulk, snd); snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; snd->instance = acm; } diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 0fe434505ac4..ba589d4ca8bc 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -15,7 +15,6 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/module.h> -#include <linux/smp_lock.h> #include <linux/mutex.h> #include <linux/uaccess.h> #include <linux/bitops.h> diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 3703789d0d2a..b09a527f7341 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -751,7 +751,7 @@ static int get_capabilities(struct usbtmc_device_data *data) { struct device *dev = &data->usb_dev->dev; char *buffer; - int rv; + int rv = 0; buffer = kmalloc(0x18, GFP_KERNEL); if (!buffer) @@ -763,7 +763,7 @@ static int get_capabilities(struct usbtmc_device_data *data) 0, 0, buffer, 0x18, USBTMC_TIMEOUT); if (rv < 0) { dev_err(dev, "usb_control_msg returned %d\n", rv); - return rv; + goto err_out; } dev_dbg(dev, "GET_CAPABILITIES returned %x\n", buffer[0]); @@ -773,7 +773,8 @@ static int get_capabilities(struct usbtmc_device_data *data) dev_dbg(dev, "USB488 device capabilities are %x\n", buffer[15]); if (buffer[0] != USBTMC_STATUS_SUCCESS) { dev_err(dev, "GET_CAPABILITIES returned %x\n", buffer[0]); - return -EPERM; + rv = -EPERM; + goto err_out; } data->capabilities.interface_capabilities = buffer[4]; @@ -781,8 +782,9 @@ static int get_capabilities(struct usbtmc_device_data *data) data->capabilities.usb488_interface_capabilities = buffer[14]; data->capabilities.usb488_device_capabilities = buffer[15]; +err_out: kfree(buffer); - return 0; + return rv; } #define capability_attribute(name) \ diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 69280c35b5cb..ad925946f869 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -28,7 +28,7 @@ comment "Miscellaneous USB options" depends on USB config USB_DEVICEFS - bool "USB device filesystem (DEPRECATED)" if EMBEDDED + bool "USB device filesystem (DEPRECATED)" depends on USB ---help--- If you say Y here (and to "/proc file system support" in the "File diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 73c108d117b4..96f11715cd26 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -136,17 +136,19 @@ static const struct class_info clas_info[] = {USB_CLASS_AUDIO, "audio"}, {USB_CLASS_COMM, "comm."}, {USB_CLASS_HID, "HID"}, - {USB_CLASS_HUB, "hub"}, {USB_CLASS_PHYSICAL, "PID"}, + {USB_CLASS_STILL_IMAGE, "still"}, {USB_CLASS_PRINTER, "print"}, {USB_CLASS_MASS_STORAGE, "stor."}, + {USB_CLASS_HUB, "hub"}, {USB_CLASS_CDC_DATA, "data"}, - {USB_CLASS_APP_SPEC, "app."}, - {USB_CLASS_VENDOR_SPEC, "vend."}, - {USB_CLASS_STILL_IMAGE, "still"}, {USB_CLASS_CSCID, "scard"}, {USB_CLASS_CONTENT_SEC, "c-sec"}, {USB_CLASS_VIDEO, "video"}, + {USB_CLASS_WIRELESS_CONTROLLER, "wlcon"}, + {USB_CLASS_MISC, "misc"}, + {USB_CLASS_APP_SPEC, "app."}, + {USB_CLASS_VENDOR_SPEC, "vend."}, {-1, "unk."} /* leave as last */ }; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 308609039c73..38b8bce782d6 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -325,21 +325,34 @@ static void async_completed(struct urb *urb) struct async *as = urb->context; struct dev_state *ps = as->ps; struct siginfo sinfo; + struct pid *pid = NULL; + uid_t uid = 0; + uid_t euid = 0; + u32 secid = 0; + int signr; spin_lock(&ps->lock); list_move_tail(&as->asynclist, &ps->async_completed); - spin_unlock(&ps->lock); as->status = urb->status; - if (as->signr) { + signr = as->signr; + if (signr) { sinfo.si_signo = as->signr; sinfo.si_errno = as->status; sinfo.si_code = SI_ASYNCIO; sinfo.si_addr = as->userurb; - kill_pid_info_as_uid(as->signr, &sinfo, as->pid, as->uid, - as->euid, as->secid); + pid = as->pid; + uid = as->uid; + euid = as->euid; + secid = as->secid; } snoop(&urb->dev->dev, "urb complete\n"); snoop_urb(urb, as->userurb); + spin_unlock(&ps->lock); + + if (signr) + kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid, + euid, secid); + wake_up(&ps->wait); } @@ -982,7 +995,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, USBDEVFS_URB_ZERO_PACKET | USBDEVFS_URB_NO_INTERRUPT)) return -EINVAL; - if (!uurb->buffer) + if (uurb->buffer_length > 0 && !uurb->buffer) return -EINVAL; if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL && (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { @@ -1038,11 +1051,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, is_in = 0; uurb->endpoint &= ~USB_DIR_IN; } - if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, - uurb->buffer, uurb->buffer_length)) { - kfree(dr); - return -EFAULT; - } snoop(&ps->dev->dev, "control urb: bRequest=%02x " "bRrequestType=%02x wValue=%04x " "wIndex=%04x wLength=%04x\n", @@ -1062,9 +1070,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, uurb->number_of_packets = 0; if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) return -EINVAL; - if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, - uurb->buffer, uurb->buffer_length)) - return -EFAULT; snoop(&ps->dev->dev, "bulk urb\n"); break; @@ -1106,28 +1111,35 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return -EINVAL; if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) return -EINVAL; - if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, - uurb->buffer, uurb->buffer_length)) - return -EFAULT; snoop(&ps->dev->dev, "interrupt urb\n"); break; default: return -EINVAL; } - as = alloc_async(uurb->number_of_packets); - if (!as) { + if (uurb->buffer_length > 0 && + !access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, + uurb->buffer, uurb->buffer_length)) { kfree(isopkt); kfree(dr); - return -ENOMEM; + return -EFAULT; } - as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL); - if (!as->urb->transfer_buffer) { + as = alloc_async(uurb->number_of_packets); + if (!as) { kfree(isopkt); kfree(dr); - free_async(as); return -ENOMEM; } + if (uurb->buffer_length > 0) { + as->urb->transfer_buffer = kmalloc(uurb->buffer_length, + GFP_KERNEL); + if (!as->urb->transfer_buffer) { + kfree(isopkt); + kfree(dr); + free_async(as); + return -ENOMEM; + } + } as->urb->dev = ps->dev; as->urb->pipe = (uurb->type << 30) | __create_pipe(ps->dev, uurb->endpoint & 0xf) | @@ -1169,7 +1181,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, kfree(isopkt); as->ps = ps; as->userurb = arg; - if (uurb->endpoint & USB_DIR_IN) + if (is_in && uurb->buffer_length > 0) as->userbuffer = uurb->buffer; else as->userbuffer = NULL; @@ -1179,9 +1191,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->uid = cred->uid; as->euid = cred->euid; security_task_getsecid(current, &as->secid); - if (!is_in) { + if (!is_in && uurb->buffer_length > 0) { if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, - as->urb->transfer_buffer_length)) { + uurb->buffer_length)) { free_async(as); return -EFAULT; } @@ -1231,22 +1243,22 @@ static int processcompl(struct async *as, void __user * __user *arg) if (as->userbuffer) if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length)) - return -EFAULT; + goto err_out; if (put_user(as->status, &userurb->status)) - return -EFAULT; + goto err_out; if (put_user(urb->actual_length, &userurb->actual_length)) - return -EFAULT; + goto err_out; if (put_user(urb->error_count, &userurb->error_count)) - return -EFAULT; + goto err_out; if (usb_endpoint_xfer_isoc(&urb->ep->desc)) { for (i = 0; i < urb->number_of_packets; i++) { if (put_user(urb->iso_frame_desc[i].actual_length, &userurb->iso_frame_desc[i].actual_length)) - return -EFAULT; + goto err_out; if (put_user(urb->iso_frame_desc[i].status, &userurb->iso_frame_desc[i].status)) - return -EFAULT; + goto err_out; } } @@ -1255,6 +1267,10 @@ static int processcompl(struct async *as, void __user * __user *arg) if (put_user(addr, (void __user * __user *)arg)) return -EFAULT; return 0; + +err_out: + free_async(as); + return -EFAULT; } static struct async *reap_as(struct dev_state *ps) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index ce3f453f02ef..95ccfa0b9fc5 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -648,7 +648,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) struct urb *urb; int length; unsigned long flags; - char buffer[4]; /* Any root hubs with > 31 ports? */ + char buffer[6]; /* Any root hubs with > 31 ports? */ if (unlikely(!hcd->rh_registered)) return; diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index d397ecfd5b17..ec5c67ea07b7 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -227,6 +227,10 @@ struct hc_driver { /* has a port been handed over to a companion? */ int (*port_handed_over)(struct usb_hcd *, int); + /* CLEAR_TT_BUFFER completion callback */ + void (*clear_tt_buffer_complete)(struct usb_hcd *, + struct usb_host_endpoint *); + /* xHCI specific functions */ /* Called by usb_alloc_dev to alloc HC device structures */ int (*alloc_dev)(struct usb_hcd *, struct usb_device *); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 2af3b4f06054..71f86c60d83c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -450,10 +450,10 @@ hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt) * talking to TTs must queue control transfers (not just bulk and iso), so * both can talk to the same hub concurrently. */ -static void hub_tt_kevent (struct work_struct *work) +static void hub_tt_work(struct work_struct *work) { struct usb_hub *hub = - container_of(work, struct usb_hub, tt.kevent); + container_of(work, struct usb_hub, tt.clear_work); unsigned long flags; int limit = 100; @@ -462,6 +462,7 @@ static void hub_tt_kevent (struct work_struct *work) struct list_head *next; struct usb_tt_clear *clear; struct usb_device *hdev = hub->hdev; + const struct hc_driver *drv; int status; next = hub->tt.clear_list.next; @@ -471,21 +472,25 @@ static void hub_tt_kevent (struct work_struct *work) /* drop lock so HCD can concurrently report other TT errors */ spin_unlock_irqrestore (&hub->tt.lock, flags); status = hub_clear_tt_buffer (hdev, clear->devinfo, clear->tt); - spin_lock_irqsave (&hub->tt.lock, flags); - if (status) dev_err (&hdev->dev, "clear tt %d (%04x) error %d\n", clear->tt, clear->devinfo, status); + + /* Tell the HCD, even if the operation failed */ + drv = clear->hcd->driver; + if (drv->clear_tt_buffer_complete) + (drv->clear_tt_buffer_complete)(clear->hcd, clear->ep); + kfree(clear); + spin_lock_irqsave(&hub->tt.lock, flags); } spin_unlock_irqrestore (&hub->tt.lock, flags); } /** - * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub - * @udev: the device whose split transaction failed - * @pipe: identifies the endpoint of the failed transaction + * usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub + * @urb: an URB associated with the failed or incomplete split transaction * * High speed HCDs use this to tell the hub driver that some split control or * bulk transaction failed in a way that requires clearing internal state of @@ -495,8 +500,10 @@ static void hub_tt_kevent (struct work_struct *work) * It may not be possible for that hub to handle additional full (or low) * speed transactions until that state is fully cleared out. */ -void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) +int usb_hub_clear_tt_buffer(struct urb *urb) { + struct usb_device *udev = urb->dev; + int pipe = urb->pipe; struct usb_tt *tt = udev->tt; unsigned long flags; struct usb_tt_clear *clear; @@ -508,7 +515,7 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) if ((clear = kmalloc (sizeof *clear, GFP_ATOMIC)) == NULL) { dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n"); /* FIXME recover somehow ... RESET_TT? */ - return; + return -ENOMEM; } /* info that CLEAR_TT_BUFFER needs */ @@ -520,14 +527,19 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) : (USB_ENDPOINT_XFER_BULK << 11); if (usb_pipein (pipe)) clear->devinfo |= 1 << 15; - + + /* info for completion callback */ + clear->hcd = bus_to_hcd(udev->bus); + clear->ep = urb->ep; + /* tell keventd to clear state for this TT */ spin_lock_irqsave (&tt->lock, flags); list_add_tail (&clear->clear_list, &tt->clear_list); - schedule_work (&tt->kevent); + schedule_work(&tt->clear_work); spin_unlock_irqrestore (&tt->lock, flags); + return 0; } -EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer); +EXPORT_SYMBOL_GPL(usb_hub_clear_tt_buffer); /* If do_delay is false, return the number of milliseconds the caller * needs to delay. @@ -818,7 +830,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) if (hub->has_indicators) cancel_delayed_work_sync(&hub->leds); if (hub->tt.hub) - cancel_work_sync(&hub->tt.kevent); + cancel_work_sync(&hub->tt.clear_work); } /* caller has locked the hub device */ @@ -935,7 +947,7 @@ static int hub_configure(struct usb_hub *hub, spin_lock_init (&hub->tt.lock); INIT_LIST_HEAD (&hub->tt.clear_list); - INIT_WORK (&hub->tt.kevent, hub_tt_kevent); + INIT_WORK(&hub->tt.clear_work, hub_tt_work); switch (hdev->descriptor.bDeviceProtocol) { case 0: break; diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 889c0f32a40b..de8081f065ed 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -188,16 +188,18 @@ struct usb_tt { /* for control/bulk error recovery (CLEAR_TT_BUFFER) */ spinlock_t lock; struct list_head clear_list; /* of usb_tt_clear */ - struct work_struct kevent; + struct work_struct clear_work; }; struct usb_tt_clear { struct list_head clear_list; unsigned tt; u16 devinfo; + struct usb_hcd *hcd; + struct usb_host_endpoint *ep; }; -extern void usb_hub_tt_clear_buffer(struct usb_device *dev, int pipe); +extern int usb_hub_clear_tt_buffer(struct urb *urb); extern void usb_ep0_reinit(struct usb_device *); #endif /* __LINUX_HUB_H */ diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 2bed83caacb1..9720e699f472 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -806,6 +806,48 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, return rc; } +static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) +{ + int err; + + if (dev->have_langid) + return 0; + + if (dev->string_langid < 0) + return -EPIPE; + + err = usb_string_sub(dev, 0, 0, tbuf); + + /* If the string was reported but is malformed, default to english + * (0x0409) */ + if (err == -ENODATA || (err > 0 && err < 4)) { + dev->string_langid = 0x0409; + dev->have_langid = 1; + dev_err(&dev->dev, + "string descriptor 0 malformed (err = %d), " + "defaulting to 0x%04x\n", + err, dev->string_langid); + return 0; + } + + /* In case of all other errors, we assume the device is not able to + * deal with strings at all. Set string_langid to -1 in order to + * prevent any string to be retrieved from the device */ + if (err < 0) { + dev_err(&dev->dev, "string descriptor 0 read error: %d\n", + err); + dev->string_langid = -1; + return -EPIPE; + } + + /* always use the first langid listed */ + dev->string_langid = tbuf[2] | (tbuf[3] << 8); + dev->have_langid = 1; + dev_dbg(&dev->dev, "default language 0x%04x\n", + dev->string_langid); + return 0; +} + /** * usb_string - returns UTF-8 version of a string descriptor * @dev: the device whose string descriptor is being retrieved @@ -837,24 +879,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) if (!tbuf) return -ENOMEM; - /* get langid for strings if it's not yet known */ - if (!dev->have_langid) { - err = usb_string_sub(dev, 0, 0, tbuf); - if (err < 0) { - dev_err(&dev->dev, - "string descriptor 0 read error: %d\n", - err); - } else if (err < 4) { - dev_err(&dev->dev, "string descriptor 0 too short\n"); - } else { - dev->string_langid = tbuf[2] | (tbuf[3] << 8); - /* always use the first langid listed */ - dev_dbg(&dev->dev, "default language 0x%04x\n", - dev->string_langid); - } - - dev->have_langid = 1; - } + err = usb_get_langid(dev, tbuf); + if (err < 0) + goto errout; err = usb_string_sub(dev, dev->string_langid, index, tbuf); if (err < 0) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 5d1ddf485d1e..7f8e83a954ac 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -286,6 +286,27 @@ config USB_S3C_HSOTG default USB_GADGET select USB_GADGET_SELECTED +config USB_GADGET_IMX + boolean "Freescale IMX USB Peripheral Controller" + depends on ARCH_MX1 + help + Freescale's IMX series include an integrated full speed + USB 1.1 device controller. The controller in the IMX series + is register-compatible. + + It has Six fixed-function endpoints, as well as endpoint + zero (for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "imx_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_IMX + tristate + depends on USB_GADGET_IMX + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_S3C2410 boolean "S3C2410 USB Device Controller" depends on ARCH_S3C2410 @@ -321,27 +342,6 @@ config USB_GADGET_MUSB_HDRC This OTG-capable silicon IP is used in dual designs including the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin -config USB_GADGET_IMX - boolean "Freescale IMX USB Peripheral Controller" - depends on ARCH_MX1 - help - Freescale's IMX series include an integrated full speed - USB 1.1 device controller. The controller in the IMX series - is register-compatible. - - It has Six fixed-function endpoints, as well as endpoint - zero (for control transfers). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "imx_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_IMX - tristate - depends on USB_GADGET_IMX - default USB_GADGET - select USB_GADGET_SELECTED - config USB_GADGET_M66592 boolean "Renesas M66592 USB Peripheral Controller" select USB_GADGET_DUALSPEED @@ -604,6 +604,7 @@ config USB_ZERO_HNPTEST config USB_AUDIO tristate "Audio Gadget (EXPERIMENTAL)" depends on SND + select SND_PCM help Gadget Audio is compatible with USB Audio Class specification 1.0. It will include at least one AudioControl interface, zero or more diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 826f3adde5d8..77352ccc245e 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -48,7 +48,6 @@ #include <linux/ioport.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/timer.h> diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index 94de7e864614..9f80f4e970bd 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -42,9 +42,9 @@ * Instead: allocate your own, using normal USB-IF procedures. */ -/* Thanks to NetChip Technologies for donating this product ID. */ -#define AUDIO_VENDOR_NUM 0x0525 /* NetChip */ -#define AUDIO_PRODUCT_NUM 0xa4a1 /* Linux-USB Audio Gadget */ +/* Thanks to Linux Foundation for donating this product ID. */ +#define AUDIO_VENDOR_NUM 0x1d6b /* Linux Foundation */ +#define AUDIO_PRODUCT_NUM 0x0101 /* Linux-USB Audio Gadget */ /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index d006dc652e02..bd102f5052ba 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -293,15 +293,16 @@ static int __init eth_bind(struct usb_composite_dev *cdev) /* CDC Subset */ eth_config_driver.label = "CDC Subset/SAFE"; - device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM), - device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM), - device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; + device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM); + device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM); + if (!has_rndis()) + device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; } if (has_rndis()) { /* RNDIS plus ECM-or-Subset */ - device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM), - device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM), + device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM); + device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM); device_desc.bNumConfigurations = 2; } diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index 6829d5961359..a3913519fd58 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -34,7 +34,6 @@ #include <linux/ioport.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/timer.h> diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 0ce4e2819847..ed21e263f832 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -139,7 +139,7 @@ static int is_vbus_present(void) { struct pxa2xx_udc_mach_info *mach = the_controller->mach; - if (mach->gpio_vbus) { + if (gpio_is_valid(mach->gpio_vbus)) { int value = gpio_get_value(mach->gpio_vbus); if (mach->gpio_vbus_inverted) @@ -158,7 +158,7 @@ static void pullup_off(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; int off_level = mach->gpio_pullup_inverted; - if (mach->gpio_pullup) + if (gpio_is_valid(mach->gpio_pullup)) gpio_set_value(mach->gpio_pullup, off_level); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); @@ -169,7 +169,7 @@ static void pullup_on(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; int on_level = !mach->gpio_pullup_inverted; - if (mach->gpio_pullup) + if (gpio_is_valid(mach->gpio_pullup)) gpio_set_value(mach->gpio_pullup, on_level); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_CONNECT); @@ -1000,7 +1000,7 @@ static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active) udc = container_of(_gadget, struct pxa25x_udc, gadget); /* not all boards support pullup control */ - if (!udc->mach->gpio_pullup && !udc->mach->udc_command) + if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command) return -EOPNOTSUPP; udc->pullup = (is_active != 0); @@ -1802,11 +1802,13 @@ pxa25x_udc_irq(int irq, void *_dev) USIR0 |= tmp; handled = 1; } +#ifndef CONFIG_USB_PXA25X_SMALL if (usir1 & tmp) { handle_ep(&dev->ep[i+8]); USIR1 |= tmp; handled = 1; } +#endif } } @@ -2160,7 +2162,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; - if (dev->mach->gpio_vbus) { + if (gpio_is_valid(dev->mach->gpio_vbus)) { if ((retval = gpio_request(dev->mach->gpio_vbus, "pxa25x_udc GPIO VBUS"))) { dev_dbg(&pdev->dev, @@ -2173,7 +2175,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) } else vbus_irq = 0; - if (dev->mach->gpio_pullup) { + if (gpio_is_valid(dev->mach->gpio_pullup)) { if ((retval = gpio_request(dev->mach->gpio_pullup, "pca25x_udc GPIO PULLUP"))) { dev_dbg(&pdev->dev, @@ -2256,10 +2258,10 @@ lubbock_fail0: #endif free_irq(irq, dev); err_irq1: - if (dev->mach->gpio_pullup) + if (gpio_is_valid(dev->mach->gpio_pullup)) gpio_free(dev->mach->gpio_pullup); err_gpio_pullup: - if (dev->mach->gpio_vbus) + if (gpio_is_valid(dev->mach->gpio_vbus)) gpio_free(dev->mach->gpio_vbus); err_gpio_vbus: clk_put(dev->clk); @@ -2294,11 +2296,11 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev) free_irq(LUBBOCK_USB_IRQ, dev); } #endif - if (dev->mach->gpio_vbus) { + if (gpio_is_valid(dev->mach->gpio_vbus)) { free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev); gpio_free(dev->mach->gpio_vbus); } - if (dev->mach->gpio_pullup) + if (gpio_is_valid(dev->mach->gpio_pullup)) gpio_free(dev->mach->gpio_pullup); clk_put(dev->clk); @@ -2329,7 +2331,7 @@ static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state) struct pxa25x_udc *udc = platform_get_drvdata(dev); unsigned long flags; - if (!udc->mach->gpio_pullup && !udc->mach->udc_command) + if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command) WARNING("USB host won't detect disconnect!\n"); udc->suspended = 1; diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 2b4660e08c4d..ca41b0b5afb3 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -442,6 +442,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, case OID_802_3_MAC_OPTIONS: pr_debug("%s: OID_802_3_MAC_OPTIONS\n", __func__); + *outbuf = cpu_to_le32(0); + retval = 0; break; /* ieee802.3 statistics OIDs (table 4-4) */ diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 9a2b8920532d..a9b452fe6221 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -28,7 +28,6 @@ #include <linux/ioport.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/timer.h> diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 1576a0520adf..1a920c70b5a1 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -181,26 +181,27 @@ config USB_OHCI_HCD_PPC_SOC Enables support for the USB controller on the MPC52xx or STB03xxx processor chip. If unsure, say Y. -config USB_OHCI_HCD_PPC_OF - bool "OHCI support for PPC USB controller on OF platform bus" - depends on USB_OHCI_HCD && PPC_OF - default y - ---help--- - Enables support for the USB controller PowerPC present on the - OpenFirmware platform bus. - config USB_OHCI_HCD_PPC_OF_BE - bool "Support big endian HC" - depends on USB_OHCI_HCD_PPC_OF - default y + bool "OHCI support for OF platform bus (big endian)" + depends on USB_OHCI_HCD && PPC_OF select USB_OHCI_BIG_ENDIAN_DESC select USB_OHCI_BIG_ENDIAN_MMIO + ---help--- + Enables support for big-endian USB controllers present on the + OpenFirmware platform bus. config USB_OHCI_HCD_PPC_OF_LE - bool "Support little endian HC" - depends on USB_OHCI_HCD_PPC_OF - default n + bool "OHCI support for OF platform bus (little endian)" + depends on USB_OHCI_HCD && PPC_OF select USB_OHCI_LITTLE_ENDIAN + ---help--- + Enables support for little-endian USB controllers present on the + OpenFirmware platform bus. + +config USB_OHCI_HCD_PPC_OF + bool + depends on USB_OHCI_HCD && PPC_OF + default USB_OHCI_HCD_PPC_OF_BE || USB_OHCI_HCD_PPC_OF_LE config USB_OHCI_HCD_PCI bool "OHCI support for PCI-bus USB controllers" @@ -337,10 +338,10 @@ config USB_R8A66597_HCD config SUPERH_ON_CHIP_R8A66597 boolean "Enable SuperH on-chip R8A66597 USB" - depends on USB_R8A66597_HCD && (CPU_SUBTYPE_SH7366 || CPU_SUBTYPE_SH7723) + depends on USB_R8A66597_HCD && (CPU_SUBTYPE_SH7366 || CPU_SUBTYPE_SH7723 || CPU_SUBTYPE_SH7724) help This driver enables support for the on-chip R8A66597 in the - SH7366 and SH7723 processors. + SH7366, SH7723 and SH7724 processors. config USB_WHCI_HCD tristate "Wireless USB Host Controller Interface (WHCI) driver (EXPERIMENTAL)" diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index c3a778bd359c..59d208d94d4e 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -113,6 +113,8 @@ static const struct hc_driver ehci_au1xxx_hc_driver = { .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index bf86809c5120..991174937db3 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -325,6 +325,8 @@ static const struct hc_driver ehci_fsl_hc_driver = { .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int ehci_fsl_drv_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2b72473544d3..7d03549c3339 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1003,6 +1003,8 @@ idle_timeout: schedule_timeout_uninterruptible(1); goto rescan; case QH_STATE_IDLE: /* fully unlinked */ + if (qh->clearing_tt) + goto idle_timeout; if (list_empty (&qh->qtd_list)) { qh_put (qh); break; @@ -1030,12 +1032,14 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct ehci_qh *qh; int eptype = usb_endpoint_type(&ep->desc); + int epnum = usb_endpoint_num(&ep->desc); + int is_out = usb_endpoint_dir_out(&ep->desc); + unsigned long flags; if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT) return; - rescan: - spin_lock_irq(&ehci->lock); + spin_lock_irqsave(&ehci->lock, flags); qh = ep->hcpriv; /* For Bulk and Interrupt endpoints we maintain the toggle state @@ -1044,29 +1048,24 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) * the toggle bit in the QH. */ if (qh) { + usb_settoggle(qh->dev, epnum, is_out, 0); if (!list_empty(&qh->qtd_list)) { WARN_ONCE(1, "clear_halt for a busy endpoint\n"); - } else if (qh->qh_state == QH_STATE_IDLE) { - qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE); - } else { - /* It's not safe to write into the overlay area - * while the QH is active. Unlink it first and - * wait for the unlink to complete. + } else if (qh->qh_state == QH_STATE_LINKED) { + + /* The toggle value in the QH can't be updated + * while the QH is active. Unlink it now; + * re-linking will call qh_refresh(). */ - if (qh->qh_state == QH_STATE_LINKED) { - if (eptype == USB_ENDPOINT_XFER_BULK) { - unlink_async(ehci, qh); - } else { - intr_deschedule(ehci, qh); - (void) qh_schedule(ehci, qh); - } + if (eptype == USB_ENDPOINT_XFER_BULK) { + unlink_async(ehci, qh); + } else { + intr_deschedule(ehci, qh); + (void) qh_schedule(ehci, qh); } - spin_unlock_irq(&ehci->lock); - schedule_timeout_uninterruptible(1); - goto rescan; } } - spin_unlock_irq(&ehci->lock); + spin_unlock_irqrestore(&ehci->lock, flags); } static int ehci_get_frame (struct usb_hcd *hcd) diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c index a44bb4a94954..89b7c70c6ed6 100644 --- a/drivers/usb/host/ehci-ixp4xx.c +++ b/drivers/usb/host/ehci-ixp4xx.c @@ -61,6 +61,8 @@ static const struct hc_driver ixp4xx_ehci_hc_driver = { #endif .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int ixp4xx_ehci_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 770dd9aba62a..dc2ac613a9d1 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -165,6 +165,8 @@ static const struct hc_driver ehci_orion_hc_driver = { .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static void __init diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index f3683e1da161..c2f1b7df918c 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -404,6 +404,8 @@ static const struct hc_driver ehci_pci_hc_driver = { .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index fbd272288fc2..36f96da129f5 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -79,6 +79,8 @@ static const struct hc_driver ehci_ppc_of_hc_driver = { #endif .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 93f7035d00a1..1dee33b9139e 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -75,6 +75,8 @@ static const struct hc_driver ps3_ehci_hc_driver = { #endif .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int __devinit ps3_ehci_probe(struct ps3_system_bus_device *dev) diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 3192f683f807..9a1384747f3b 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -93,6 +93,22 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma); qh->hw_alt_next = EHCI_LIST_END(ehci); + /* Except for control endpoints, we make hardware maintain data + * toggle (like OHCI) ... here (re)initialize the toggle in the QH, + * and set the pseudo-toggle in udev. Only usb_clear_halt() will + * ever clear it. + */ + if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) { + unsigned is_out, epnum; + + is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8)); + epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f; + if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) { + qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE); + usb_settoggle (qh->dev, epnum, is_out, 1); + } + } + /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ wmb (); qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING); @@ -123,6 +139,55 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh) /*-------------------------------------------------------------------------*/ +static void qh_link_async(struct ehci_hcd *ehci, struct ehci_qh *qh); + +static void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct ehci_qh *qh = ep->hcpriv; + unsigned long flags; + + spin_lock_irqsave(&ehci->lock, flags); + qh->clearing_tt = 0; + if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) + && HC_IS_RUNNING(hcd->state)) + qh_link_async(ehci, qh); + spin_unlock_irqrestore(&ehci->lock, flags); +} + +static void ehci_clear_tt_buffer(struct ehci_hcd *ehci, struct ehci_qh *qh, + struct urb *urb, u32 token) +{ + + /* If an async split transaction gets an error or is unlinked, + * the TT buffer may be left in an indeterminate state. We + * have to clear the TT buffer. + * + * Note: this routine is never called for Isochronous transfers. + */ + if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { +#ifdef DEBUG + struct usb_device *tt = urb->dev->tt->hub; + dev_dbg(&tt->dev, + "clear tt buffer port %d, a%d ep%d t%08x\n", + urb->dev->ttport, urb->dev->devnum, + usb_pipeendpoint(urb->pipe), token); +#endif /* DEBUG */ + if (!ehci_is_TDI(ehci) + || urb->dev->tt->hub != + ehci_to_hcd(ehci)->self.root_hub) { + if (usb_hub_clear_tt_buffer(urb) == 0) + qh->clearing_tt = 1; + } else { + + /* REVISIT ARC-derived cores don't clear the root + * hub TT buffer in this way... + */ + } + } +} + static int qtd_copy_status ( struct ehci_hcd *ehci, struct urb *urb, @@ -149,6 +214,14 @@ static int qtd_copy_status ( if (token & QTD_STS_BABBLE) { /* FIXME "must" disable babbling device's port too */ status = -EOVERFLOW; + /* CERR nonzero + halt --> stall */ + } else if (QTD_CERR(token)) { + status = -EPIPE; + + /* In theory, more than one of the following bits can be set + * since they are sticky and the transaction is retried. + * Which to test first is rather arbitrary. + */ } else if (token & QTD_STS_MMF) { /* fs/ls interrupt xfer missed the complete-split */ status = -EPROTO; @@ -157,21 +230,15 @@ static int qtd_copy_status ( ? -ENOSR /* hc couldn't read data */ : -ECOMM; /* hc couldn't write data */ } else if (token & QTD_STS_XACT) { - /* timeout, bad crc, wrong PID, etc; retried */ - if (QTD_CERR (token)) - status = -EPIPE; - else { - ehci_dbg (ehci, "devpath %s ep%d%s 3strikes\n", - urb->dev->devpath, - usb_pipeendpoint (urb->pipe), - usb_pipein (urb->pipe) ? "in" : "out"); - status = -EPROTO; - } - /* CERR nonzero + no errors + halt --> stall */ - } else if (QTD_CERR (token)) - status = -EPIPE; - else /* unknown */ + /* timeout, bad CRC, wrong PID, etc */ + ehci_dbg(ehci, "devpath %s ep%d%s 3strikes\n", + urb->dev->devpath, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out"); + status = -EPROTO; + } else { /* unknown */ status = -EPROTO; + } ehci_vdbg (ehci, "dev%d ep%d%s qtd token %08x --> status %d\n", @@ -179,28 +246,6 @@ static int qtd_copy_status ( usb_pipeendpoint (urb->pipe), usb_pipein (urb->pipe) ? "in" : "out", token, status); - - /* if async CSPLIT failed, try cleaning out the TT buffer */ - if (status != -EPIPE - && urb->dev->tt - && !usb_pipeint(urb->pipe) - && ((token & QTD_STS_MMF) != 0 - || QTD_CERR(token) == 0) - && (!ehci_is_TDI(ehci) - || urb->dev->tt->hub != - ehci_to_hcd(ehci)->self.root_hub)) { -#ifdef DEBUG - struct usb_device *tt = urb->dev->tt->hub; - dev_dbg (&tt->dev, - "clear tt buffer port %d, a%d ep%d t%08x\n", - urb->dev->ttport, urb->dev->devnum, - usb_pipeendpoint (urb->pipe), token); -#endif /* DEBUG */ - /* REVISIT ARC-derived cores don't clear the root - * hub TT buffer in this way... - */ - usb_hub_tt_clear_buffer (urb->dev, urb->pipe); - } } return status; @@ -391,9 +436,16 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) /* qh unlinked; token in overlay may be most current */ if (state == QH_STATE_IDLE && cpu_to_hc32(ehci, qtd->qtd_dma) - == qh->hw_current) + == qh->hw_current) { token = hc32_to_cpu(ehci, qh->hw_token); + /* An unlink may leave an incomplete + * async transaction in the TT buffer. + * We have to clear it. + */ + ehci_clear_tt_buffer(ehci, qh, urb, token); + } + /* force halt for unlinked or blocked qh, so we'll * patch the qh later and so that completions can't * activate it while we "know" it's stopped. @@ -419,6 +471,13 @@ halt: && (qtd->hw_alt_next & EHCI_LIST_END(ehci))) last_status = -EINPROGRESS; + + /* As part of low/full-speed endpoint-halt processing + * we must clear the TT buffer (11.17.5). + */ + if (unlikely(last_status != -EINPROGRESS && + last_status != -EREMOTEIO)) + ehci_clear_tt_buffer(ehci, qh, urb, token); } /* if we're removing something not at the queue head, @@ -834,6 +893,7 @@ done: qh->qh_state = QH_STATE_IDLE; qh->hw_info1 = cpu_to_hc32(ehci, info1); qh->hw_info2 = cpu_to_hc32(ehci, info2); + usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); qh_refresh (ehci, qh); return qh; } @@ -847,6 +907,10 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) __hc32 dma = QH_NEXT(ehci, qh->qh_dma); struct ehci_qh *head; + /* Don't link a QH if there's a Clear-TT-Buffer pending */ + if (unlikely(qh->clearing_tt)) + return; + /* (re)start the async schedule? */ head = ehci->async; timer_action_done (ehci, TIMER_ASYNC_OFF); @@ -864,7 +928,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) } } - /* clear halt and maybe recover from silicon quirk */ + /* clear halt and/or toggle; and maybe recover from silicon quirk */ if (qh->qh_state == QH_STATE_IDLE) qh_refresh (ehci, qh); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 9d1babc7ff65..74f7f83b29ad 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1619,11 +1619,14 @@ itd_complete ( desc->status = -EPROTO; /* HC need not update length with this error */ - if (!(t & EHCI_ISOC_BABBLE)) - desc->actual_length = EHCI_ITD_LENGTH (t); + if (!(t & EHCI_ISOC_BABBLE)) { + desc->actual_length = EHCI_ITD_LENGTH(t); + urb->actual_length += desc->actual_length; + } } else if (likely ((t & EHCI_ISOC_ACTIVE) == 0)) { desc->status = 0; - desc->actual_length = EHCI_ITD_LENGTH (t); + desc->actual_length = EHCI_ITD_LENGTH(t); + urb->actual_length += desc->actual_length; } else { /* URB was too late */ desc->status = -EXDEV; @@ -2014,7 +2017,8 @@ sitd_complete ( desc->status = -EPROTO; } else { desc->status = 0; - desc->actual_length = desc->length - SITD_LENGTH (t); + desc->actual_length = desc->length - SITD_LENGTH(t); + urb->actual_length += desc->actual_length; } stream->depth -= stream->interval << 3; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 90ad3395bb21..2bfff30f4704 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -354,7 +354,9 @@ struct ehci_qh { unsigned short period; /* polling interval */ unsigned short start; /* where polling starts */ #define NO_FRAME ((unsigned short)~0) /* pick new start */ + struct usb_device *dev; /* access to TT */ + unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index bb63b68ddb77..62a226b61670 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -576,9 +576,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd) out_be16(&usb->fhci->regs->usb_event, usb->saved_msk); } else if (usb->port_status == FHCI_PORT_DISABLED) { - if (fhci_ioports_check_bus_state(fhci) == 1 && - usb->port_status != FHCI_PORT_LOW && - usb->port_status != FHCI_PORT_FULL) + if (fhci_ioports_check_bus_state(fhci) == 1) fhci_device_connected_interrupt(fhci); } usb_er &= ~USB_E_RESET_MASK; @@ -605,9 +603,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd) } if (usb_er & USB_E_IDLE_MASK) { - if (usb->port_status == FHCI_PORT_DISABLED && - usb->port_status != FHCI_PORT_LOW && - usb->port_status != FHCI_PORT_FULL) { + if (usb->port_status == FHCI_PORT_DISABLED) { usb_er &= ~USB_E_RESET_MASK; fhci_device_connected_interrupt(fhci); } else if (usb->port_status == diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 3fa3a1702796..d4feebfc63bd 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -361,7 +361,7 @@ static int __devexit isp1760_plat_remove(struct platform_device *pdev) static struct platform_driver isp1760_plat_driver = { .probe = isp1760_plat_probe, - .remove = isp1760_plat_remove, + .remove = __devexit_p(isp1760_plat_remove), .driver = { .name = "isp1760", }, diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 56976cc0352a..e18f74946e68 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -26,7 +26,6 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/timer.h> diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 3c5fe5cee05a..90e1a8dedfa9 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -18,6 +18,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/sched.h> +#include <linux/smp_lock.h> #include <linux/poll.h> #include <linux/usb/iowarrior.h> diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index deb95bb49fd1..d645f3899fe1 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -32,6 +32,7 @@ #include <linux/kernel.h> #include <linux/signal.h> #include <linux/sched.h> +#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/random.h> #include <linux/poll.h> diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index e0ff9ccd866b..29092b8e59ce 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -16,6 +16,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/mutex.h> #include <asm/uaccess.h> diff --git a/drivers/usb/musb/cppi_dma.h b/drivers/usb/musb/cppi_dma.h index 8a39de3e6e47..59bf949e589b 100644 --- a/drivers/usb/musb/cppi_dma.h +++ b/drivers/usb/musb/cppi_dma.h @@ -5,7 +5,6 @@ #include <linux/slab.h> #include <linux/list.h> -#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/dmapool.h> diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 180d7daa4099..e16ff605c458 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -35,13 +35,14 @@ #include <mach/hardware.h> #include <mach/memory.h> #include <mach/gpio.h> +#include <mach/cputype.h> #include <asm/mach-types.h> #include "musb_core.h" #ifdef CONFIG_MACH_DAVINCI_EVM -#define GPIO_nVBUS_DRV 87 +#define GPIO_nVBUS_DRV 144 #endif #include "davinci.h" @@ -329,7 +330,6 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); WARNING("VBUS error workaround (delay coming)\n"); } else if (is_host_enabled(musb) && drvvbus) { - musb->is_active = 1; MUSB_HST_MODE(musb); musb->xceiv->default_a = 1; musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; @@ -343,7 +343,9 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) portstate(musb->port1_status &= ~USB_PORT_STAT_POWER); } - /* NOTE: this must complete poweron within 100 msec */ + /* NOTE: this must complete poweron within 100 msec + * (OTG_TIME_A_WAIT_VRISE) but we don't check for that. + */ davinci_source_power(musb, drvvbus, 0); DBG(2, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", @@ -411,6 +413,21 @@ int __init musb_platform_init(struct musb *musb) __raw_writel(phy_ctrl, USB_PHY_CTRL); } + /* On dm355, the default-A state machine needs DRVVBUS control. + * If we won't be a host, there's no need to turn it on. + */ + if (cpu_is_davinci_dm355()) { + u32 deepsleep = __raw_readl(DM355_DEEPSLEEP); + + if (is_host_enabled(musb)) { + deepsleep &= ~DRVVBUS_OVERRIDE; + } else { + deepsleep &= ~DRVVBUS_FORCE; + deepsleep |= DRVVBUS_OVERRIDE; + } + __raw_writel(deepsleep, DM355_DEEPSLEEP); + } + /* reset the controller */ musb_writel(tibase, DAVINCI_USB_CTRL_REG, 0x1); @@ -437,6 +454,15 @@ int musb_platform_exit(struct musb *musb) if (is_host_enabled(musb)) del_timer_sync(&otg_workaround); + /* force VBUS off */ + if (cpu_is_davinci_dm355()) { + u32 deepsleep = __raw_readl(DM355_DEEPSLEEP); + + deepsleep &= ~DRVVBUS_FORCE; + deepsleep |= DRVVBUS_OVERRIDE; + __raw_writel(deepsleep, DM355_DEEPSLEEP); + } + davinci_source_power(musb, 0 /*off*/, 1); /* delay, to avoid problems with module reload */ diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index f3772ca3b2cf..381d648a36b8 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -38,7 +38,6 @@ #include <linux/slab.h> #include <linux/list.h> #include <linux/interrupt.h> -#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/timer.h> #include <linux/clk.h> diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 94a2a350a414..cf94511485f2 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -373,7 +373,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, musb_save_toggle(qh, is_in, urb); break; case USB_ENDPOINT_XFER_ISOC: - if (urb->error_count) + if (status == 0 && urb->error_count) status = -EXDEV; break; } @@ -2235,13 +2235,30 @@ static void musb_h_stop(struct usb_hcd *hcd) static int musb_bus_suspend(struct usb_hcd *hcd) { struct musb *musb = hcd_to_musb(hcd); + u8 devctl; - if (musb->xceiv->state == OTG_STATE_A_SUSPEND) + if (!is_host_active(musb)) return 0; - if (is_host_active(musb) && musb->is_active) { - WARNING("trying to suspend as %s is_active=%i\n", - otg_state_string(musb), musb->is_active); + switch (musb->xceiv->state) { + case OTG_STATE_A_SUSPEND: + return 0; + case OTG_STATE_A_WAIT_VRISE: + /* ID could be grounded even if there's no device + * on the other end of the cable. NOTE that the + * A_WAIT_VRISE timers are messy with MUSB... + */ + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) + musb->xceiv->state = OTG_STATE_A_WAIT_BCON; + break; + default: + break; + } + + if (musb->is_active) { + WARNING("trying to suspend as %s while active\n", + otg_state_string(musb)); return -EBUSY; } else return 0; diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 69feeec1628c..aa884d072f0b 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -59,18 +59,4 @@ config NOP_USB_XCEIV built-in with usb ip or which are autonomous and doesn't require any phy programming such as ISP1x04 etc. -config USB_LANGWELL_OTG - tristate "Intel Langwell USB OTG dual-role support" - depends on USB && MRST - select USB_OTG - select USB_OTG_UTILS - help - Say Y here if you want to build Intel Langwell USB OTG - transciever driver in kernel. This driver implements role - switch between EHCI host driver and Langwell USB OTG - client driver. - - To compile this driver as a module, choose M here: the - module will be called langwell_otg. - endif # USB || OTG diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index 6d1abdd3c0ac..208167856529 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o -obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c deleted file mode 100644 index 6f628d0e9f39..000000000000 --- a/drivers/usb/otg/langwell_otg.c +++ /dev/null @@ -1,1915 +0,0 @@ -/* - * Intel Langwell USB OTG transceiver driver - * Copyright (C) 2008 - 2009, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - */ -/* This driver helps to switch Langwell OTG controller function between host - * and peripheral. It works with EHCI driver and Langwell client controller - * driver together. - */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/errno.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/device.h> -#include <linux/moduleparam.h> -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> -#include <linux/usb.h> -#include <linux/usb/otg.h> -#include <linux/notifier.h> -#include <asm/ipc_defs.h> -#include <linux/delay.h> -#include "../core/hcd.h" - -#include <linux/usb/langwell_otg.h> - -#define DRIVER_DESC "Intel Langwell USB OTG transceiver driver" -#define DRIVER_VERSION "3.0.0.32L.0002" - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Henry Yuan <hang.yuan@intel.com>, Hao Wu <hao.wu@intel.com>"); -MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL"); - -static const char driver_name[] = "langwell_otg"; - -static int langwell_otg_probe(struct pci_dev *pdev, - const struct pci_device_id *id); -static void langwell_otg_remove(struct pci_dev *pdev); -static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message); -static int langwell_otg_resume(struct pci_dev *pdev); - -static int langwell_otg_set_host(struct otg_transceiver *otg, - struct usb_bus *host); -static int langwell_otg_set_peripheral(struct otg_transceiver *otg, - struct usb_gadget *gadget); -static int langwell_otg_start_srp(struct otg_transceiver *otg); - -static const struct pci_device_id pci_ids[] = {{ - .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), - .class_mask = ~0, - .vendor = 0x8086, - .device = 0x0811, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, -}, { /* end: all zeroes */ } -}; - -static struct pci_driver otg_pci_driver = { - .name = (char *) driver_name, - .id_table = pci_ids, - - .probe = langwell_otg_probe, - .remove = langwell_otg_remove, - - .suspend = langwell_otg_suspend, - .resume = langwell_otg_resume, -}; - -static const char *state_string(enum usb_otg_state state) -{ - switch (state) { - case OTG_STATE_A_IDLE: - return "a_idle"; - case OTG_STATE_A_WAIT_VRISE: - return "a_wait_vrise"; - case OTG_STATE_A_WAIT_BCON: - return "a_wait_bcon"; - case OTG_STATE_A_HOST: - return "a_host"; - case OTG_STATE_A_SUSPEND: - return "a_suspend"; - case OTG_STATE_A_PERIPHERAL: - return "a_peripheral"; - case OTG_STATE_A_WAIT_VFALL: - return "a_wait_vfall"; - case OTG_STATE_A_VBUS_ERR: - return "a_vbus_err"; - case OTG_STATE_B_IDLE: - return "b_idle"; - case OTG_STATE_B_SRP_INIT: - return "b_srp_init"; - case OTG_STATE_B_PERIPHERAL: - return "b_peripheral"; - case OTG_STATE_B_WAIT_ACON: - return "b_wait_acon"; - case OTG_STATE_B_HOST: - return "b_host"; - default: - return "UNDEFINED"; - } -} - -/* HSM timers */ -static inline struct langwell_otg_timer *otg_timer_initializer -(void (*function)(unsigned long), unsigned long expires, unsigned long data) -{ - struct langwell_otg_timer *timer; - timer = kmalloc(sizeof(struct langwell_otg_timer), GFP_KERNEL); - timer->function = function; - timer->expires = expires; - timer->data = data; - return timer; -} - -static struct langwell_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, - *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_res_tmr, - *b_bus_suspend_tmr; - -static struct list_head active_timers; - -static struct langwell_otg *the_transceiver; - -/* host/client notify transceiver when event affects HNP state */ -void langwell_update_transceiver() -{ - otg_dbg("transceiver driver is notified\n"); - queue_work(the_transceiver->qwork, &the_transceiver->work); -} -EXPORT_SYMBOL(langwell_update_transceiver); - -static int langwell_otg_set_host(struct otg_transceiver *otg, - struct usb_bus *host) -{ - otg->host = host; - - return 0; -} - -static int langwell_otg_set_peripheral(struct otg_transceiver *otg, - struct usb_gadget *gadget) -{ - otg->gadget = gadget; - - return 0; -} - -static int langwell_otg_set_power(struct otg_transceiver *otg, - unsigned mA) -{ - return 0; -} - -/* A-device drives vbus, controlled through PMIC CHRGCNTL register*/ -static void langwell_otg_drv_vbus(int on) -{ - struct ipc_pmic_reg_data pmic_data = {0}; - struct ipc_pmic_reg_data battery_data; - - /* Check if battery is attached or not */ - battery_data.pmic_reg_data[0].register_address = 0xd2; - battery_data.ioc = 0; - battery_data.num_entries = 1; - if (ipc_pmic_register_read(&battery_data)) { - otg_dbg("Failed to read PMIC register 0xd2.\n"); - return; - } - - if ((battery_data.pmic_reg_data[0].value & 0x20) == 0) { - otg_dbg("no battery attached\n"); - return; - } - - /* Workaround for battery attachment issue */ - if (battery_data.pmic_reg_data[0].value == 0x34) { - otg_dbg("battery \n"); - return; - } - - otg_dbg("battery attached\n"); - - pmic_data.ioc = 0; - pmic_data.pmic_reg_data[0].register_address = 0xD4; - pmic_data.num_entries = 1; - if (on) - pmic_data.pmic_reg_data[0].value = 0x20; - else - pmic_data.pmic_reg_data[0].value = 0xc0; - - if (ipc_pmic_register_write(&pmic_data, TRUE)) - otg_dbg("Failed to write PMIC.\n"); - -} - -/* charge vbus or discharge vbus through a resistor to ground */ -static void langwell_otg_chrg_vbus(int on) -{ - - u32 val; - - val = readl(the_transceiver->regs + CI_OTGSC); - - if (on) - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VC, - the_transceiver->regs + CI_OTGSC); - else - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VD, - the_transceiver->regs + CI_OTGSC); - -} - -/* Start SRP */ -static int langwell_otg_start_srp(struct otg_transceiver *otg) -{ - u32 val; - - otg_dbg("Start SRP ->\n"); - - val = readl(the_transceiver->regs + CI_OTGSC); - - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP, - the_transceiver->regs + CI_OTGSC); - - /* Check if the data plus is finished or not */ - msleep(8); - val = readl(the_transceiver->regs + CI_OTGSC); - if (val & (OTGSC_HADP | OTGSC_DP)) - otg_dbg("DataLine SRP Error\n"); - - /* FIXME: VBus SRP */ - - return 0; -} - - -/* stop SOF via bus_suspend */ -static void langwell_otg_loc_sof(int on) -{ - struct usb_hcd *hcd; - int err; - - otg_dbg("loc_sof -> %d\n", on); - - hcd = bus_to_hcd(the_transceiver->otg.host); - if (on) - err = hcd->driver->bus_resume(hcd); - else - err = hcd->driver->bus_suspend(hcd); - - if (err) - otg_dbg("Failed to resume/suspend bus - %d\n", err); -} - -static void langwell_otg_phy_low_power(int on) -{ - u32 val; - - otg_dbg("phy low power mode-> %d\n", on); - - val = readl(the_transceiver->regs + CI_HOSTPC1); - if (on) - writel(val | HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1); - else - writel(val & ~HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1); -} - -/* Enable/Disable OTG interrupt */ -static void langwell_otg_intr(int on) -{ - u32 val; - - otg_dbg("interrupt -> %d\n", on); - - val = readl(the_transceiver->regs + CI_OTGSC); - if (on) { - val = val | (OTGSC_INTEN_MASK | OTGSC_IDPU); - writel(val, the_transceiver->regs + CI_OTGSC); - } else { - val = val & ~(OTGSC_INTEN_MASK | OTGSC_IDPU); - writel(val, the_transceiver->regs + CI_OTGSC); - } -} - -/* set HAAR: Hardware Assist Auto-Reset */ -static void langwell_otg_HAAR(int on) -{ - u32 val; - - otg_dbg("HAAR -> %d\n", on); - - val = readl(the_transceiver->regs + CI_OTGSC); - if (on) - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HAAR, - the_transceiver->regs + CI_OTGSC); - else - writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HAAR, - the_transceiver->regs + CI_OTGSC); -} - -/* set HABA: Hardware Assist B-Disconnect to A-Connect */ -static void langwell_otg_HABA(int on) -{ - u32 val; - - otg_dbg("HABA -> %d\n", on); - - val = readl(the_transceiver->regs + CI_OTGSC); - if (on) - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HABA, - the_transceiver->regs + CI_OTGSC); - else - writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HABA, - the_transceiver->regs + CI_OTGSC); -} - -static int langwell_otg_check_se0_srp(int on) -{ - u32 val; - - int delay_time = TB_SE0_SRP * 10; /* step is 100us */ - - otg_dbg("check_se0_srp -> \n"); - - do { - udelay(100); - if (!delay_time--) - break; - val = readl(the_transceiver->regs + CI_PORTSC1); - val &= PORTSC_LS; - } while (!val); - - otg_dbg("check_se0_srp <- \n"); - return val; -} - -/* The timeout callback function to set time out bit */ -static void set_tmout(unsigned long indicator) -{ - *(int *)indicator = 1; -} - -void langwell_otg_nsf_msg(unsigned long indicator) -{ - switch (indicator) { - case 2: - case 4: - case 6: - case 7: - printk(KERN_ERR "OTG:NSF-%lu - deivce not responding\n", - indicator); - break; - case 3: - printk(KERN_ERR "OTG:NSF-%lu - deivce not supported\n", - indicator); - break; - default: - printk(KERN_ERR "Do not have this kind of NSF\n"); - break; - } -} - -/* Initialize timers */ -static void langwell_otg_init_timers(struct otg_hsm *hsm) -{ - /* HSM used timers */ - a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, - (unsigned long)&hsm->a_wait_vrise_tmout); - a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, - (unsigned long)&hsm->a_wait_bcon_tmout); - a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, - (unsigned long)&hsm->a_aidl_bdis_tmout); - b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, - (unsigned long)&hsm->b_ase0_brst_tmout); - b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, - (unsigned long)&hsm->b_se0_srp); - b_srp_res_tmr = otg_timer_initializer(&set_tmout, TB_SRP_RES, - (unsigned long)&hsm->b_srp_res_tmout); - b_bus_suspend_tmr = otg_timer_initializer(&set_tmout, TB_BUS_SUSPEND, - (unsigned long)&hsm->b_bus_suspend_tmout); -} - -/* Free timers */ -static void langwell_otg_free_timers(void) -{ - kfree(a_wait_vrise_tmr); - kfree(a_wait_bcon_tmr); - kfree(a_aidl_bdis_tmr); - kfree(b_ase0_brst_tmr); - kfree(b_se0_srp_tmr); - kfree(b_srp_res_tmr); - kfree(b_bus_suspend_tmr); -} - -/* Add timer to timer list */ -static void langwell_otg_add_timer(void *gtimer) -{ - struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; - struct langwell_otg_timer *tmp_timer; - u32 val32; - - /* Check if the timer is already in the active list, - * if so update timer count - */ - list_for_each_entry(tmp_timer, &active_timers, list) - if (tmp_timer == timer) { - timer->count = timer->expires; - return; - } - timer->count = timer->expires; - - if (list_empty(&active_timers)) { - val32 = readl(the_transceiver->regs + CI_OTGSC); - writel(val32 | OTGSC_1MSE, the_transceiver->regs + CI_OTGSC); - } - - list_add_tail(&timer->list, &active_timers); -} - -/* Remove timer from the timer list; clear timeout status */ -static void langwell_otg_del_timer(void *gtimer) -{ - struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; - struct langwell_otg_timer *tmp_timer, *del_tmp; - u32 val32; - - list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) - if (tmp_timer == timer) - list_del(&timer->list); - - if (list_empty(&active_timers)) { - val32 = readl(the_transceiver->regs + CI_OTGSC); - writel(val32 & ~OTGSC_1MSE, the_transceiver->regs + CI_OTGSC); - } -} - -/* Reduce timer count by 1, and find timeout conditions.*/ -static int langwell_otg_tick_timer(u32 *int_sts) -{ - struct langwell_otg_timer *tmp_timer, *del_tmp; - int expired = 0; - - list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { - tmp_timer->count--; - /* check if timer expires */ - if (!tmp_timer->count) { - list_del(&tmp_timer->list); - tmp_timer->function(tmp_timer->data); - expired = 1; - } - } - - if (list_empty(&active_timers)) { - otg_dbg("tick timer: disable 1ms int\n"); - *int_sts = *int_sts & ~OTGSC_1MSE; - } - return expired; -} - -static void reset_otg(void) -{ - u32 val; - int delay_time = 1000; - - otg_dbg("reseting OTG controller ...\n"); - val = readl(the_transceiver->regs + CI_USBCMD); - writel(val | USBCMD_RST, the_transceiver->regs + CI_USBCMD); - do { - udelay(100); - if (!delay_time--) - otg_dbg("reset timeout\n"); - val = readl(the_transceiver->regs + CI_USBCMD); - val &= USBCMD_RST; - } while (val != 0); - otg_dbg("reset done.\n"); -} - -static void set_host_mode(void) -{ - u32 val; - - reset_otg(); - val = readl(the_transceiver->regs + CI_USBMODE); - val = (val & (~USBMODE_CM)) | USBMODE_HOST; - writel(val, the_transceiver->regs + CI_USBMODE); -} - -static void set_client_mode(void) -{ - u32 val; - - reset_otg(); - val = readl(the_transceiver->regs + CI_USBMODE); - val = (val & (~USBMODE_CM)) | USBMODE_DEVICE; - writel(val, the_transceiver->regs + CI_USBMODE); -} - -static void init_hsm(void) -{ - struct langwell_otg *langwell = the_transceiver; - u32 val32; - - /* read OTGSC after reset */ - val32 = readl(langwell->regs + CI_OTGSC); - otg_dbg("%s: OTGSC init value = 0x%x\n", __func__, val32); - - /* set init state */ - if (val32 & OTGSC_ID) { - langwell->hsm.id = 1; - langwell->otg.default_a = 0; - set_client_mode(); - langwell->otg.state = OTG_STATE_B_IDLE; - langwell_otg_drv_vbus(0); - } else { - langwell->hsm.id = 0; - langwell->otg.default_a = 1; - set_host_mode(); - langwell->otg.state = OTG_STATE_A_IDLE; - } - - /* set session indicator */ - if (val32 & OTGSC_BSE) - langwell->hsm.b_sess_end = 1; - if (val32 & OTGSC_BSV) - langwell->hsm.b_sess_vld = 1; - if (val32 & OTGSC_ASV) - langwell->hsm.a_sess_vld = 1; - if (val32 & OTGSC_AVV) - langwell->hsm.a_vbus_vld = 1; - - /* defautly power the bus */ - langwell->hsm.a_bus_req = 1; - langwell->hsm.a_bus_drop = 0; - /* defautly don't request bus as B device */ - langwell->hsm.b_bus_req = 0; - /* no system error */ - langwell->hsm.a_clr_err = 0; -} - -static irqreturn_t otg_dummy_irq(int irq, void *_dev) -{ - void __iomem *reg_base = _dev; - u32 val; - u32 int_mask = 0; - - val = readl(reg_base + CI_USBMODE); - if ((val & USBMODE_CM) != USBMODE_DEVICE) - return IRQ_NONE; - - val = readl(reg_base + CI_USBSTS); - int_mask = val & INTR_DUMMY_MASK; - - if (int_mask == 0) - return IRQ_NONE; - - /* clear hsm.b_conn here since host driver can't detect it - * otg_dummy_irq called means B-disconnect happened. - */ - if (the_transceiver->hsm.b_conn) { - the_transceiver->hsm.b_conn = 0; - if (spin_trylock(&the_transceiver->wq_lock)) { - queue_work(the_transceiver->qwork, - &the_transceiver->work); - spin_unlock(&the_transceiver->wq_lock); - } - } - /* Clear interrupts */ - writel(int_mask, reg_base + CI_USBSTS); - return IRQ_HANDLED; -} - -static irqreturn_t otg_irq(int irq, void *_dev) -{ - struct langwell_otg *langwell = _dev; - u32 int_sts, int_en; - u32 int_mask = 0; - int flag = 0; - - int_sts = readl(langwell->regs + CI_OTGSC); - int_en = (int_sts & OTGSC_INTEN_MASK) >> 8; - int_mask = int_sts & int_en; - if (int_mask == 0) - return IRQ_NONE; - - if (int_mask & OTGSC_IDIS) { - otg_dbg("%s: id change int\n", __func__); - langwell->hsm.id = (int_sts & OTGSC_ID) ? 1 : 0; - flag = 1; - } - if (int_mask & OTGSC_DPIS) { - otg_dbg("%s: data pulse int\n", __func__); - langwell->hsm.a_srp_det = (int_sts & OTGSC_DPS) ? 1 : 0; - flag = 1; - } - if (int_mask & OTGSC_BSEIS) { - otg_dbg("%s: b session end int\n", __func__); - langwell->hsm.b_sess_end = (int_sts & OTGSC_BSE) ? 1 : 0; - flag = 1; - } - if (int_mask & OTGSC_BSVIS) { - otg_dbg("%s: b session valid int\n", __func__); - langwell->hsm.b_sess_vld = (int_sts & OTGSC_BSV) ? 1 : 0; - flag = 1; - } - if (int_mask & OTGSC_ASVIS) { - otg_dbg("%s: a session valid int\n", __func__); - langwell->hsm.a_sess_vld = (int_sts & OTGSC_ASV) ? 1 : 0; - flag = 1; - } - if (int_mask & OTGSC_AVVIS) { - otg_dbg("%s: a vbus valid int\n", __func__); - langwell->hsm.a_vbus_vld = (int_sts & OTGSC_AVV) ? 1 : 0; - flag = 1; - } - - if (int_mask & OTGSC_1MSS) { - /* need to schedule otg_work if any timer is expired */ - if (langwell_otg_tick_timer(&int_sts)) - flag = 1; - } - - writel((int_sts & ~OTGSC_INTSTS_MASK) | int_mask, - langwell->regs + CI_OTGSC); - if (flag) - queue_work(langwell->qwork, &langwell->work); - - return IRQ_HANDLED; -} - -static void langwell_otg_work(struct work_struct *work) -{ - struct langwell_otg *langwell = container_of(work, - struct langwell_otg, work); - int retval; - - otg_dbg("%s: old state = %s\n", __func__, - state_string(langwell->otg.state)); - - switch (langwell->otg.state) { - case OTG_STATE_UNDEFINED: - case OTG_STATE_B_IDLE: - if (!langwell->hsm.id) { - langwell_otg_del_timer(b_srp_res_tmr); - langwell->otg.default_a = 1; - langwell->hsm.a_srp_det = 0; - - langwell_otg_chrg_vbus(0); - langwell_otg_drv_vbus(0); - - set_host_mode(); - langwell->otg.state = OTG_STATE_A_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.b_srp_res_tmout) { - langwell->hsm.b_srp_res_tmout = 0; - langwell->hsm.b_bus_req = 0; - langwell_otg_nsf_msg(6); - } else if (langwell->hsm.b_sess_vld) { - langwell_otg_del_timer(b_srp_res_tmr); - langwell->hsm.b_sess_end = 0; - langwell->hsm.a_bus_suspend = 0; - - langwell_otg_chrg_vbus(0); - if (langwell->client_ops) { - langwell->client_ops->resume(langwell->pdev); - langwell->otg.state = OTG_STATE_B_PERIPHERAL; - } else - otg_dbg("client driver not loaded.\n"); - - } else if (langwell->hsm.b_bus_req && - (langwell->hsm.b_sess_end)) { - /* workaround for b_se0_srp detection */ - retval = langwell_otg_check_se0_srp(0); - if (retval) { - langwell->hsm.b_bus_req = 0; - otg_dbg("LS is not SE0, try again later\n"); - } else { - /* Start SRP */ - langwell_otg_start_srp(&langwell->otg); - langwell_otg_add_timer(b_srp_res_tmr); - } - } - break; - case OTG_STATE_B_SRP_INIT: - if (!langwell->hsm.id) { - langwell->otg.default_a = 1; - langwell->hsm.a_srp_det = 0; - - langwell_otg_drv_vbus(0); - langwell_otg_chrg_vbus(0); - - langwell->otg.state = OTG_STATE_A_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.b_sess_vld) { - langwell_otg_chrg_vbus(0); - if (langwell->client_ops) { - langwell->client_ops->resume(langwell->pdev); - langwell->otg.state = OTG_STATE_B_PERIPHERAL; - } else - otg_dbg("client driver not loaded.\n"); - } - break; - case OTG_STATE_B_PERIPHERAL: - if (!langwell->hsm.id) { - langwell->otg.default_a = 1; - langwell->hsm.a_srp_det = 0; - - langwell_otg_drv_vbus(0); - langwell_otg_chrg_vbus(0); - set_host_mode(); - - if (langwell->client_ops) { - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - } else - otg_dbg("client driver has been removed.\n"); - - langwell->otg.state = OTG_STATE_A_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (!langwell->hsm.b_sess_vld) { - langwell->hsm.b_hnp_enable = 0; - - if (langwell->client_ops) { - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - } else - otg_dbg("client driver has been removed.\n"); - - langwell->otg.state = OTG_STATE_B_IDLE; - } else if (langwell->hsm.b_bus_req && langwell->hsm.b_hnp_enable - && langwell->hsm.a_bus_suspend) { - - if (langwell->client_ops) { - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - } else - otg_dbg("client driver has been removed.\n"); - - langwell_otg_HAAR(1); - langwell->hsm.a_conn = 0; - - if (langwell->host_ops) { - langwell->host_ops->probe(langwell->pdev, - langwell->host_ops->id_table); - langwell->otg.state = OTG_STATE_B_WAIT_ACON; - } else - otg_dbg("host driver not loaded.\n"); - - langwell->hsm.a_bus_resume = 0; - langwell->hsm.b_ase0_brst_tmout = 0; - langwell_otg_add_timer(b_ase0_brst_tmr); - } - break; - - case OTG_STATE_B_WAIT_ACON: - if (!langwell->hsm.id) { - langwell_otg_del_timer(b_ase0_brst_tmr); - langwell->otg.default_a = 1; - langwell->hsm.a_srp_det = 0; - - langwell_otg_drv_vbus(0); - langwell_otg_chrg_vbus(0); - set_host_mode(); - - langwell_otg_HAAR(0); - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell->otg.state = OTG_STATE_A_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (!langwell->hsm.b_sess_vld) { - langwell_otg_del_timer(b_ase0_brst_tmr); - langwell->hsm.b_hnp_enable = 0; - langwell->hsm.b_bus_req = 0; - langwell_otg_chrg_vbus(0); - langwell_otg_HAAR(0); - - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell->otg.state = OTG_STATE_B_IDLE; - } else if (langwell->hsm.a_conn) { - langwell_otg_del_timer(b_ase0_brst_tmr); - langwell_otg_HAAR(0); - langwell->otg.state = OTG_STATE_B_HOST; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.a_bus_resume || - langwell->hsm.b_ase0_brst_tmout) { - langwell_otg_del_timer(b_ase0_brst_tmr); - langwell_otg_HAAR(0); - langwell_otg_nsf_msg(7); - - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - - langwell->hsm.a_bus_suspend = 0; - langwell->hsm.b_bus_req = 0; - - if (langwell->client_ops) - langwell->client_ops->resume(langwell->pdev); - else - otg_dbg("client driver not loaded.\n"); - - langwell->otg.state = OTG_STATE_B_PERIPHERAL; - } - break; - - case OTG_STATE_B_HOST: - if (!langwell->hsm.id) { - langwell->otg.default_a = 1; - langwell->hsm.a_srp_det = 0; - - langwell_otg_drv_vbus(0); - langwell_otg_chrg_vbus(0); - set_host_mode(); - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell->otg.state = OTG_STATE_A_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (!langwell->hsm.b_sess_vld) { - langwell->hsm.b_hnp_enable = 0; - langwell->hsm.b_bus_req = 0; - langwell_otg_chrg_vbus(0); - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell->otg.state = OTG_STATE_B_IDLE; - } else if ((!langwell->hsm.b_bus_req) || - (!langwell->hsm.a_conn)) { - langwell->hsm.b_bus_req = 0; - langwell_otg_loc_sof(0); - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - - langwell->hsm.a_bus_suspend = 0; - - if (langwell->client_ops) - langwell->client_ops->resume(langwell->pdev); - else - otg_dbg("client driver not loaded.\n"); - - langwell->otg.state = OTG_STATE_B_PERIPHERAL; - } - break; - - case OTG_STATE_A_IDLE: - langwell->otg.default_a = 1; - if (langwell->hsm.id) { - langwell->otg.default_a = 0; - langwell->hsm.b_bus_req = 0; - langwell_otg_drv_vbus(0); - langwell_otg_chrg_vbus(0); - - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.a_sess_vld) { - langwell_otg_drv_vbus(1); - langwell->hsm.a_srp_det = 1; - langwell->hsm.a_wait_vrise_tmout = 0; - langwell_otg_add_timer(a_wait_vrise_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_VRISE; - queue_work(langwell->qwork, &langwell->work); - } else if (!langwell->hsm.a_bus_drop && - (langwell->hsm.a_srp_det || langwell->hsm.a_bus_req)) { - langwell_otg_drv_vbus(1); - langwell->hsm.a_wait_vrise_tmout = 0; - langwell_otg_add_timer(a_wait_vrise_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_VRISE; - queue_work(langwell->qwork, &langwell->work); - } - break; - case OTG_STATE_A_WAIT_VRISE: - if (langwell->hsm.id) { - langwell_otg_del_timer(a_wait_vrise_tmr); - langwell->hsm.b_bus_req = 0; - langwell->otg.default_a = 0; - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_B_IDLE; - } else if (langwell->hsm.a_vbus_vld) { - langwell_otg_del_timer(a_wait_vrise_tmr); - if (langwell->host_ops) - langwell->host_ops->probe(langwell->pdev, - langwell->host_ops->id_table); - else - otg_dbg("host driver not loaded.\n"); - langwell->hsm.b_conn = 0; - langwell->hsm.a_set_b_hnp_en = 0; - langwell->hsm.a_wait_bcon_tmout = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_BCON; - } else if (langwell->hsm.a_wait_vrise_tmout) { - if (langwell->hsm.a_vbus_vld) { - if (langwell->host_ops) - langwell->host_ops->probe( - langwell->pdev, - langwell->host_ops->id_table); - else - otg_dbg("host driver not loaded.\n"); - langwell->hsm.b_conn = 0; - langwell->hsm.a_set_b_hnp_en = 0; - langwell->hsm.a_wait_bcon_tmout = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_BCON; - } else { - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_VBUS_ERR; - } - } - break; - case OTG_STATE_A_WAIT_BCON: - if (langwell->hsm.id) { - langwell_otg_del_timer(a_wait_bcon_tmr); - - langwell->otg.default_a = 0; - langwell->hsm.b_bus_req = 0; - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (!langwell->hsm.a_vbus_vld) { - langwell_otg_del_timer(a_wait_bcon_tmr); - - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_VBUS_ERR; - } else if (langwell->hsm.a_bus_drop || - (langwell->hsm.a_wait_bcon_tmout && - !langwell->hsm.a_bus_req)) { - langwell_otg_del_timer(a_wait_bcon_tmr); - - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - } else if (langwell->hsm.b_conn) { - langwell_otg_del_timer(a_wait_bcon_tmr); - - langwell->hsm.a_suspend_req = 0; - langwell->otg.state = OTG_STATE_A_HOST; - if (!langwell->hsm.a_bus_req && - langwell->hsm.a_set_b_hnp_en) { - /* It is not safe enough to do a fast - * transistion from A_WAIT_BCON to - * A_SUSPEND */ - msleep(10000); - if (langwell->hsm.a_bus_req) - break; - - if (request_irq(langwell->pdev->irq, - otg_dummy_irq, IRQF_SHARED, - driver_name, langwell->regs) != 0) { - otg_dbg("request interrupt %d fail\n", - langwell->pdev->irq); - } - - langwell_otg_HABA(1); - langwell->hsm.b_bus_resume = 0; - langwell->hsm.a_aidl_bdis_tmout = 0; - langwell_otg_add_timer(a_aidl_bdis_tmr); - - langwell_otg_loc_sof(0); - langwell->otg.state = OTG_STATE_A_SUSPEND; - } else if (!langwell->hsm.a_bus_req && - !langwell->hsm.a_set_b_hnp_en) { - struct pci_dev *pdev = langwell->pdev; - if (langwell->host_ops) - langwell->host_ops->remove(pdev); - else - otg_dbg("host driver removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - } - } - break; - case OTG_STATE_A_HOST: - if (langwell->hsm.id) { - langwell->otg.default_a = 0; - langwell->hsm.b_bus_req = 0; - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.a_bus_drop || - (!langwell->hsm.a_set_b_hnp_en && !langwell->hsm.a_bus_req)) { - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - } else if (!langwell->hsm.a_vbus_vld) { - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_VBUS_ERR; - } else if (langwell->hsm.a_set_b_hnp_en - && !langwell->hsm.a_bus_req) { - /* Set HABA to enable hardware assistance to signal - * A-connect after receiver B-disconnect. Hardware - * will then set client mode and enable URE, SLE and - * PCE after the assistance. otg_dummy_irq is used to - * clean these ints when client driver is not resumed. - */ - if (request_irq(langwell->pdev->irq, - otg_dummy_irq, IRQF_SHARED, driver_name, - langwell->regs) != 0) { - otg_dbg("request interrupt %d failed\n", - langwell->pdev->irq); - } - - /* set HABA */ - langwell_otg_HABA(1); - langwell->hsm.b_bus_resume = 0; - langwell->hsm.a_aidl_bdis_tmout = 0; - langwell_otg_add_timer(a_aidl_bdis_tmr); - langwell_otg_loc_sof(0); - langwell->otg.state = OTG_STATE_A_SUSPEND; - } else if (!langwell->hsm.b_conn || !langwell->hsm.a_bus_req) { - langwell->hsm.a_wait_bcon_tmout = 0; - langwell->hsm.a_set_b_hnp_en = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_BCON; - } - break; - case OTG_STATE_A_SUSPEND: - if (langwell->hsm.id) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(langwell->pdev->irq, langwell->regs); - langwell->otg.default_a = 0; - langwell->hsm.b_bus_req = 0; - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.a_bus_req || - langwell->hsm.b_bus_resume) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(langwell->pdev->irq, langwell->regs); - langwell->hsm.a_suspend_req = 0; - langwell_otg_loc_sof(1); - langwell->otg.state = OTG_STATE_A_HOST; - } else if (langwell->hsm.a_aidl_bdis_tmout || - langwell->hsm.a_bus_drop) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(langwell->pdev->irq, langwell->regs); - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - } else if (!langwell->hsm.b_conn && - langwell->hsm.a_set_b_hnp_en) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(langwell->pdev->irq, langwell->regs); - - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - - langwell->hsm.b_bus_suspend = 0; - langwell->hsm.b_bus_suspend_vld = 0; - langwell->hsm.b_bus_suspend_tmout = 0; - - /* msleep(200); */ - if (langwell->client_ops) - langwell->client_ops->resume(langwell->pdev); - else - otg_dbg("client driver not loaded.\n"); - - langwell_otg_add_timer(b_bus_suspend_tmr); - langwell->otg.state = OTG_STATE_A_PERIPHERAL; - break; - } else if (!langwell->hsm.a_vbus_vld) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(langwell->pdev->irq, langwell->regs); - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_VBUS_ERR; - } - break; - case OTG_STATE_A_PERIPHERAL: - if (langwell->hsm.id) { - langwell_otg_del_timer(b_bus_suspend_tmr); - langwell->otg.default_a = 0; - langwell->hsm.b_bus_req = 0; - if (langwell->client_ops) - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - else - otg_dbg("client driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (!langwell->hsm.a_vbus_vld) { - langwell_otg_del_timer(b_bus_suspend_tmr); - if (langwell->client_ops) - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - else - otg_dbg("client driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_VBUS_ERR; - } else if (langwell->hsm.a_bus_drop) { - langwell_otg_del_timer(b_bus_suspend_tmr); - if (langwell->client_ops) - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - else - otg_dbg("client driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - } else if (langwell->hsm.b_bus_suspend) { - langwell_otg_del_timer(b_bus_suspend_tmr); - if (langwell->client_ops) - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - else - otg_dbg("client driver has been removed.\n"); - - if (langwell->host_ops) - langwell->host_ops->probe(langwell->pdev, - langwell->host_ops->id_table); - else - otg_dbg("host driver not loaded.\n"); - langwell->hsm.a_set_b_hnp_en = 0; - langwell->hsm.a_wait_bcon_tmout = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_BCON; - } else if (langwell->hsm.b_bus_suspend_tmout) { - u32 val; - val = readl(langwell->regs + CI_PORTSC1); - if (!(val & PORTSC_SUSP)) - break; - if (langwell->client_ops) - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - else - otg_dbg("client driver has been removed.\n"); - if (langwell->host_ops) - langwell->host_ops->probe(langwell->pdev, - langwell->host_ops->id_table); - else - otg_dbg("host driver not loaded.\n"); - langwell->hsm.a_set_b_hnp_en = 0; - langwell->hsm.a_wait_bcon_tmout = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_BCON; - } - break; - case OTG_STATE_A_VBUS_ERR: - if (langwell->hsm.id) { - langwell->otg.default_a = 0; - langwell->hsm.a_clr_err = 0; - langwell->hsm.a_srp_det = 0; - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.a_clr_err) { - langwell->hsm.a_clr_err = 0; - langwell->hsm.a_srp_det = 0; - reset_otg(); - init_hsm(); - if (langwell->otg.state == OTG_STATE_A_IDLE) - queue_work(langwell->qwork, &langwell->work); - } - break; - case OTG_STATE_A_WAIT_VFALL: - if (langwell->hsm.id) { - langwell->otg.default_a = 0; - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.a_bus_req) { - langwell_otg_drv_vbus(1); - langwell->hsm.a_wait_vrise_tmout = 0; - langwell_otg_add_timer(a_wait_vrise_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_VRISE; - } else if (!langwell->hsm.a_sess_vld) { - langwell->hsm.a_srp_det = 0; - langwell_otg_drv_vbus(0); - set_host_mode(); - langwell->otg.state = OTG_STATE_A_IDLE; - } - break; - default: - ; - } - - otg_dbg("%s: new state = %s\n", __func__, - state_string(langwell->otg.state)); -} - - static ssize_t -show_registers(struct device *_dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *langwell; - char *next; - unsigned size; - unsigned t; - - langwell = the_transceiver; - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, - "\n" - "USBCMD = 0x%08x \n" - "USBSTS = 0x%08x \n" - "USBINTR = 0x%08x \n" - "ASYNCLISTADDR = 0x%08x \n" - "PORTSC1 = 0x%08x \n" - "HOSTPC1 = 0x%08x \n" - "OTGSC = 0x%08x \n" - "USBMODE = 0x%08x \n", - readl(langwell->regs + 0x30), - readl(langwell->regs + 0x34), - readl(langwell->regs + 0x38), - readl(langwell->regs + 0x48), - readl(langwell->regs + 0x74), - readl(langwell->regs + 0xb4), - readl(langwell->regs + 0xf4), - readl(langwell->regs + 0xf8) - ); - size -= t; - next += t; - - return PAGE_SIZE - size; -} -static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL); - -static ssize_t -show_hsm(struct device *_dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *langwell; - char *next; - unsigned size; - unsigned t; - - langwell = the_transceiver; - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, - "\n" - "current state = %s\n" - "a_bus_resume = \t%d\n" - "a_bus_suspend = \t%d\n" - "a_conn = \t%d\n" - "a_sess_vld = \t%d\n" - "a_srp_det = \t%d\n" - "a_vbus_vld = \t%d\n" - "b_bus_resume = \t%d\n" - "b_bus_suspend = \t%d\n" - "b_conn = \t%d\n" - "b_se0_srp = \t%d\n" - "b_sess_end = \t%d\n" - "b_sess_vld = \t%d\n" - "id = \t%d\n" - "a_set_b_hnp_en = \t%d\n" - "b_srp_done = \t%d\n" - "b_hnp_enable = \t%d\n" - "a_wait_vrise_tmout = \t%d\n" - "a_wait_bcon_tmout = \t%d\n" - "a_aidl_bdis_tmout = \t%d\n" - "b_ase0_brst_tmout = \t%d\n" - "a_bus_drop = \t%d\n" - "a_bus_req = \t%d\n" - "a_clr_err = \t%d\n" - "a_suspend_req = \t%d\n" - "b_bus_req = \t%d\n" - "b_bus_suspend_tmout = \t%d\n" - "b_bus_suspend_vld = \t%d\n", - state_string(langwell->otg.state), - langwell->hsm.a_bus_resume, - langwell->hsm.a_bus_suspend, - langwell->hsm.a_conn, - langwell->hsm.a_sess_vld, - langwell->hsm.a_srp_det, - langwell->hsm.a_vbus_vld, - langwell->hsm.b_bus_resume, - langwell->hsm.b_bus_suspend, - langwell->hsm.b_conn, - langwell->hsm.b_se0_srp, - langwell->hsm.b_sess_end, - langwell->hsm.b_sess_vld, - langwell->hsm.id, - langwell->hsm.a_set_b_hnp_en, - langwell->hsm.b_srp_done, - langwell->hsm.b_hnp_enable, - langwell->hsm.a_wait_vrise_tmout, - langwell->hsm.a_wait_bcon_tmout, - langwell->hsm.a_aidl_bdis_tmout, - langwell->hsm.b_ase0_brst_tmout, - langwell->hsm.a_bus_drop, - langwell->hsm.a_bus_req, - langwell->hsm.a_clr_err, - langwell->hsm.a_suspend_req, - langwell->hsm.b_bus_req, - langwell->hsm.b_bus_suspend_tmout, - langwell->hsm.b_bus_suspend_vld - ); - size -= t; - next += t; - - return PAGE_SIZE - size; -} -static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL); - -static ssize_t -get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *langwell; - char *next; - unsigned size; - unsigned t; - - langwell = the_transceiver; - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, "%d", langwell->hsm.a_bus_req); - size -= t; - next += t; - - return PAGE_SIZE - size; -} - -static ssize_t -set_a_bus_req(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct langwell_otg *langwell; - langwell = the_transceiver; - if (!langwell->otg.default_a) - return -1; - if (count > 2) - return -1; - - if (buf[0] == '0') { - langwell->hsm.a_bus_req = 0; - otg_dbg("a_bus_req = 0\n"); - } else if (buf[0] == '1') { - /* If a_bus_drop is TRUE, a_bus_req can't be set */ - if (langwell->hsm.a_bus_drop) - return -1; - langwell->hsm.a_bus_req = 1; - otg_dbg("a_bus_req = 1\n"); - } - if (spin_trylock(&langwell->wq_lock)) { - queue_work(langwell->qwork, &langwell->work); - spin_unlock(&langwell->wq_lock); - } - return count; -} -static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUGO, get_a_bus_req, set_a_bus_req); - -static ssize_t -get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *langwell; - char *next; - unsigned size; - unsigned t; - - langwell = the_transceiver; - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, "%d", langwell->hsm.a_bus_drop); - size -= t; - next += t; - - return PAGE_SIZE - size; -} - -static ssize_t -set_a_bus_drop(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct langwell_otg *langwell; - langwell = the_transceiver; - if (!langwell->otg.default_a) - return -1; - if (count > 2) - return -1; - - if (buf[0] == '0') { - langwell->hsm.a_bus_drop = 0; - otg_dbg("a_bus_drop = 0\n"); - } else if (buf[0] == '1') { - langwell->hsm.a_bus_drop = 1; - langwell->hsm.a_bus_req = 0; - otg_dbg("a_bus_drop = 1, then a_bus_req = 0\n"); - } - if (spin_trylock(&langwell->wq_lock)) { - queue_work(langwell->qwork, &langwell->work); - spin_unlock(&langwell->wq_lock); - } - return count; -} -static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUGO, - get_a_bus_drop, set_a_bus_drop); - -static ssize_t -get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *langwell; - char *next; - unsigned size; - unsigned t; - - langwell = the_transceiver; - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, "%d", langwell->hsm.b_bus_req); - size -= t; - next += t; - - return PAGE_SIZE - size; -} - -static ssize_t -set_b_bus_req(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct langwell_otg *langwell; - langwell = the_transceiver; - - if (langwell->otg.default_a) - return -1; - - if (count > 2) - return -1; - - if (buf[0] == '0') { - langwell->hsm.b_bus_req = 0; - otg_dbg("b_bus_req = 0\n"); - } else if (buf[0] == '1') { - langwell->hsm.b_bus_req = 1; - otg_dbg("b_bus_req = 1\n"); - } - if (spin_trylock(&langwell->wq_lock)) { - queue_work(langwell->qwork, &langwell->work); - spin_unlock(&langwell->wq_lock); - } - return count; -} -static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUGO, get_b_bus_req, set_b_bus_req); - -static ssize_t -set_a_clr_err(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct langwell_otg *langwell; - langwell = the_transceiver; - - if (!langwell->otg.default_a) - return -1; - if (count > 2) - return -1; - - if (buf[0] == '1') { - langwell->hsm.a_clr_err = 1; - otg_dbg("a_clr_err = 1\n"); - } - if (spin_trylock(&langwell->wq_lock)) { - queue_work(langwell->qwork, &langwell->work); - spin_unlock(&langwell->wq_lock); - } - return count; -} -static DEVICE_ATTR(a_clr_err, S_IWUGO, NULL, set_a_clr_err); - -static struct attribute *inputs_attrs[] = { - &dev_attr_a_bus_req.attr, - &dev_attr_a_bus_drop.attr, - &dev_attr_b_bus_req.attr, - &dev_attr_a_clr_err.attr, - NULL, -}; - -static struct attribute_group debug_dev_attr_group = { - .name = "inputs", - .attrs = inputs_attrs, -}; - -int langwell_register_host(struct pci_driver *host_driver) -{ - int ret = 0; - - the_transceiver->host_ops = host_driver; - queue_work(the_transceiver->qwork, &the_transceiver->work); - otg_dbg("host controller driver is registered\n"); - - return ret; -} -EXPORT_SYMBOL(langwell_register_host); - -void langwell_unregister_host(struct pci_driver *host_driver) -{ - if (the_transceiver->host_ops) - the_transceiver->host_ops->remove(the_transceiver->pdev); - the_transceiver->host_ops = NULL; - the_transceiver->hsm.a_bus_drop = 1; - queue_work(the_transceiver->qwork, &the_transceiver->work); - otg_dbg("host controller driver is unregistered\n"); -} -EXPORT_SYMBOL(langwell_unregister_host); - -int langwell_register_peripheral(struct pci_driver *client_driver) -{ - int ret = 0; - - if (client_driver) - ret = client_driver->probe(the_transceiver->pdev, - client_driver->id_table); - if (!ret) { - the_transceiver->client_ops = client_driver; - queue_work(the_transceiver->qwork, &the_transceiver->work); - otg_dbg("client controller driver is registered\n"); - } - - return ret; -} -EXPORT_SYMBOL(langwell_register_peripheral); - -void langwell_unregister_peripheral(struct pci_driver *client_driver) -{ - if (the_transceiver->client_ops) - the_transceiver->client_ops->remove(the_transceiver->pdev); - the_transceiver->client_ops = NULL; - the_transceiver->hsm.b_bus_req = 0; - queue_work(the_transceiver->qwork, &the_transceiver->work); - otg_dbg("client controller driver is unregistered\n"); -} -EXPORT_SYMBOL(langwell_unregister_peripheral); - -static int langwell_otg_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - unsigned long resource, len; - void __iomem *base = NULL; - int retval; - u32 val32; - struct langwell_otg *langwell; - char qname[] = "langwell_otg_queue"; - - retval = 0; - otg_dbg("\notg controller is detected.\n"); - if (pci_enable_device(pdev) < 0) { - retval = -ENODEV; - goto done; - } - - langwell = kzalloc(sizeof *langwell, GFP_KERNEL); - if (langwell == NULL) { - retval = -ENOMEM; - goto done; - } - the_transceiver = langwell; - - /* control register: BAR 0 */ - resource = pci_resource_start(pdev, 0); - len = pci_resource_len(pdev, 0); - if (!request_mem_region(resource, len, driver_name)) { - retval = -EBUSY; - goto err; - } - langwell->region = 1; - - base = ioremap_nocache(resource, len); - if (base == NULL) { - retval = -EFAULT; - goto err; - } - langwell->regs = base; - - if (!pdev->irq) { - otg_dbg("No IRQ.\n"); - retval = -ENODEV; - goto err; - } - - langwell->qwork = create_workqueue(qname); - if (!langwell->qwork) { - otg_dbg("cannot create workqueue %s\n", qname); - retval = -ENOMEM; - goto err; - } - INIT_WORK(&langwell->work, langwell_otg_work); - - /* OTG common part */ - langwell->pdev = pdev; - langwell->otg.dev = &pdev->dev; - langwell->otg.label = driver_name; - langwell->otg.set_host = langwell_otg_set_host; - langwell->otg.set_peripheral = langwell_otg_set_peripheral; - langwell->otg.set_power = langwell_otg_set_power; - langwell->otg.start_srp = langwell_otg_start_srp; - langwell->otg.state = OTG_STATE_UNDEFINED; - if (otg_set_transceiver(&langwell->otg)) { - otg_dbg("can't set transceiver\n"); - retval = -EBUSY; - goto err; - } - - reset_otg(); - init_hsm(); - - spin_lock_init(&langwell->lock); - spin_lock_init(&langwell->wq_lock); - INIT_LIST_HEAD(&active_timers); - langwell_otg_init_timers(&langwell->hsm); - - if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, - driver_name, langwell) != 0) { - otg_dbg("request interrupt %d failed\n", pdev->irq); - retval = -EBUSY; - goto err; - } - - /* enable OTGSC int */ - val32 = OTGSC_DPIE | OTGSC_BSEIE | OTGSC_BSVIE | - OTGSC_ASVIE | OTGSC_AVVIE | OTGSC_IDIE | OTGSC_IDPU; - writel(val32, langwell->regs + CI_OTGSC); - - retval = device_create_file(&pdev->dev, &dev_attr_registers); - if (retval < 0) { - otg_dbg("Can't register sysfs attribute: %d\n", retval); - goto err; - } - - retval = device_create_file(&pdev->dev, &dev_attr_hsm); - if (retval < 0) { - otg_dbg("Can't hsm sysfs attribute: %d\n", retval); - goto err; - } - - retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group); - if (retval < 0) { - otg_dbg("Can't register sysfs attr group: %d\n", retval); - goto err; - } - - if (langwell->otg.state == OTG_STATE_A_IDLE) - queue_work(langwell->qwork, &langwell->work); - - return 0; - -err: - if (the_transceiver) - langwell_otg_remove(pdev); -done: - return retval; -} - -static void langwell_otg_remove(struct pci_dev *pdev) -{ - struct langwell_otg *langwell; - - langwell = the_transceiver; - - if (langwell->qwork) { - flush_workqueue(langwell->qwork); - destroy_workqueue(langwell->qwork); - } - langwell_otg_free_timers(); - - /* disable OTGSC interrupt as OTGSC doesn't change in reset */ - writel(0, langwell->regs + CI_OTGSC); - - if (pdev->irq) - free_irq(pdev->irq, langwell); - if (langwell->regs) - iounmap(langwell->regs); - if (langwell->region) - release_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - - otg_set_transceiver(NULL); - pci_disable_device(pdev); - sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group); - device_remove_file(&pdev->dev, &dev_attr_hsm); - device_remove_file(&pdev->dev, &dev_attr_registers); - kfree(langwell); - langwell = NULL; -} - -static void transceiver_suspend(struct pci_dev *pdev) -{ - pci_save_state(pdev); - pci_set_power_state(pdev, PCI_D3hot); - langwell_otg_phy_low_power(1); -} - -static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) -{ - int ret = 0; - struct langwell_otg *langwell; - - langwell = the_transceiver; - - /* Disbale OTG interrupts */ - langwell_otg_intr(0); - - if (pdev->irq) - free_irq(pdev->irq, langwell); - - /* Prevent more otg_work */ - flush_workqueue(langwell->qwork); - spin_lock(&langwell->wq_lock); - - /* start actions */ - switch (langwell->otg.state) { - case OTG_STATE_A_IDLE: - case OTG_STATE_B_IDLE: - case OTG_STATE_A_WAIT_VFALL: - case OTG_STATE_A_VBUS_ERR: - transceiver_suspend(pdev); - break; - case OTG_STATE_A_WAIT_VRISE: - langwell_otg_del_timer(a_wait_vrise_tmr); - langwell->hsm.a_srp_det = 0; - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_IDLE; - transceiver_suspend(pdev); - break; - case OTG_STATE_A_WAIT_BCON: - langwell_otg_del_timer(a_wait_bcon_tmr); - if (langwell->host_ops) - ret = langwell->host_ops->suspend(pdev, message); - langwell_otg_drv_vbus(0); - break; - case OTG_STATE_A_HOST: - if (langwell->host_ops) - ret = langwell->host_ops->suspend(pdev, message); - langwell_otg_drv_vbus(0); - langwell_otg_phy_low_power(1); - break; - case OTG_STATE_A_SUSPEND: - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - if (langwell->host_ops) - langwell->host_ops->remove(pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - transceiver_suspend(pdev); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - break; - case OTG_STATE_A_PERIPHERAL: - if (langwell->client_ops) - ret = langwell->client_ops->suspend(pdev, message); - else - otg_dbg("client driver has been removed.\n"); - langwell_otg_drv_vbus(0); - transceiver_suspend(pdev); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - break; - case OTG_STATE_B_HOST: - if (langwell->host_ops) - langwell->host_ops->remove(pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell->hsm.b_bus_req = 0; - transceiver_suspend(pdev); - langwell->otg.state = OTG_STATE_B_IDLE; - break; - case OTG_STATE_B_PERIPHERAL: - if (langwell->client_ops) - ret = langwell->client_ops->suspend(pdev, message); - else - otg_dbg("client driver has been removed.\n"); - break; - case OTG_STATE_B_WAIT_ACON: - langwell_otg_del_timer(b_ase0_brst_tmr); - langwell_otg_HAAR(0); - if (langwell->host_ops) - langwell->host_ops->remove(pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell->hsm.b_bus_req = 0; - langwell->otg.state = OTG_STATE_B_IDLE; - transceiver_suspend(pdev); - break; - default: - otg_dbg("error state before suspend\n "); - break; - } - spin_unlock(&langwell->wq_lock); - - return ret; -} - -static void transceiver_resume(struct pci_dev *pdev) -{ - pci_restore_state(pdev); - pci_set_power_state(pdev, PCI_D0); - langwell_otg_phy_low_power(0); -} - -static int langwell_otg_resume(struct pci_dev *pdev) -{ - int ret = 0; - struct langwell_otg *langwell; - - langwell = the_transceiver; - - spin_lock(&langwell->wq_lock); - - switch (langwell->otg.state) { - case OTG_STATE_A_IDLE: - case OTG_STATE_B_IDLE: - case OTG_STATE_A_WAIT_VFALL: - case OTG_STATE_A_VBUS_ERR: - transceiver_resume(pdev); - break; - case OTG_STATE_A_WAIT_BCON: - langwell_otg_add_timer(a_wait_bcon_tmr); - langwell_otg_drv_vbus(1); - if (langwell->host_ops) - ret = langwell->host_ops->resume(pdev); - break; - case OTG_STATE_A_HOST: - langwell_otg_drv_vbus(1); - langwell_otg_phy_low_power(0); - if (langwell->host_ops) - ret = langwell->host_ops->resume(pdev); - break; - case OTG_STATE_B_PERIPHERAL: - if (langwell->client_ops) - ret = langwell->client_ops->resume(pdev); - else - otg_dbg("client driver not loaded.\n"); - break; - default: - otg_dbg("error state before suspend\n "); - break; - } - - if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, - driver_name, the_transceiver) != 0) { - otg_dbg("request interrupt %d failed\n", pdev->irq); - ret = -EBUSY; - } - - /* enable OTG interrupts */ - langwell_otg_intr(1); - - spin_unlock(&langwell->wq_lock); - - queue_work(langwell->qwork, &langwell->work); - - - return ret; -} - -static int __init langwell_otg_init(void) -{ - return pci_register_driver(&otg_pci_driver); -} -module_init(langwell_otg_init); - -static void __exit langwell_otg_cleanup(void) -{ - pci_unregister_driver(&otg_pci_driver); -} -module_exit(langwell_otg_cleanup); diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 9ed5ea568679..af456b48985f 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -53,6 +53,7 @@ EXPORT_SYMBOL(usb_nop_xceiv_register); void usb_nop_xceiv_unregister(void) { platform_device_unregister(pd); + pd = NULL; } EXPORT_SYMBOL(usb_nop_xceiv_unregister); diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 247b61bfb7f4..0e4f2e41ace5 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -169,9 +169,11 @@ static int usb_console_setup(struct console *co, char *options) kfree(tty); } } - /* So we know not to kill the hardware on a hangup on this - port. We have also bumped the use count by one so it won't go - idle */ + /* Now that any required fake tty operations are completed restore + * the tty port count */ + --port->port.count; + /* The console is special in terms of closing the device so + * indicate this port is now acting as a system console. */ port->console = 1; retval = 0; @@ -204,7 +206,7 @@ static void usb_console_write(struct console *co, dbg("%s - port %d, %d byte(s)", __func__, port->number, count); - if (!port->port.count) { + if (!port->console) { dbg("%s - port not opened", __func__); return; } @@ -300,8 +302,7 @@ void usb_serial_console_exit(void) { if (usbcons_info.port) { unregister_console(&usbcons); - if (usbcons_info.port->port.count) - usbcons_info.port->port.count--; + usbcons_info.port->console = 0; usbcons_info.port = NULL; } } diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 2b9eeda62bfe..e9a40b820fd4 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -67,6 +67,8 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */ { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */ { USB_DEVICE(0x10C4, 0x0F91) }, /* Vstabi */ + { USB_DEVICE(0x10C4, 0x1101) }, /* Arkham Technology DS101 Bus Monitor */ + { USB_DEVICE(0x10C4, 0x1601) }, /* Arkham Technology DS101 Adapter */ { USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */ { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 9734085fd2fe..59adfe123110 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -1228,8 +1228,8 @@ static void cypress_read_int_callback(struct urb *urb) /* precursor to disconnect so just go away */ return; case -EPIPE: - usb_clear_halt(port->serial->dev, 0x81); - break; + /* Can't call usb_clear_halt while in_interrupt */ + /* FALLS THROUGH */ default: /* something ugly is going on... */ dev_err(&urb->dev->dev, diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 3dc3768ca71c..60c64cc5be2a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -33,6 +33,7 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> @@ -107,6 +108,7 @@ struct ftdi_sio_quirk { static int ftdi_jtag_probe(struct usb_serial *serial); static int ftdi_mtxorb_hack_setup(struct usb_serial *serial); +static int ftdi_NDI_device_setup(struct usb_serial *serial); static void ftdi_USB_UIRT_setup(struct ftdi_private *priv); static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv); @@ -118,6 +120,10 @@ static struct ftdi_sio_quirk ftdi_mtxorb_hack_quirk = { .probe = ftdi_mtxorb_hack_setup, }; +static struct ftdi_sio_quirk ftdi_NDI_device_quirk = { + .probe = ftdi_NDI_device_setup, +}; + static struct ftdi_sio_quirk ftdi_USB_UIRT_quirk = { .port_probe = ftdi_USB_UIRT_setup, }; @@ -191,6 +197,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_MTXORB_4_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MTXORB_5_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MTXORB_6_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_R2000KU_TRUE_RNG) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0100_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0101_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0102_PID) }, @@ -579,6 +586,9 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_CCSICDU20_0_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CCSICDU40_1_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CCSMACHX_2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSLOAD_N_GO_3_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSICDU64_4_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSPRIME8_5_PID) }, { USB_DEVICE(FTDI_VID, INSIDE_ACCESSO) }, { USB_DEVICE(INTREPID_VID, INTREPID_VALUECAN_PID) }, { USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) }, @@ -644,6 +654,16 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) }, { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) }, { USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID), + .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_SPECTRA_SCU_PID), + .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_2_PID), + .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_3_PID), + .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_AURORA_SCU_PID), + .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MAXSTREAM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PHI_FISCO_PID) }, @@ -660,6 +680,8 @@ static struct usb_device_id id_table_combined [] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, { USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) }, { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO4x4_PID) }, @@ -667,7 +689,6 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) }, { USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) }, { USB_DEVICE(FTDI_VID, DIEBOLD_BCS_SE923_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID) }, { USB_DEVICE(ATMEL_VID, STK541_PID) }, { USB_DEVICE(DE_VID, STB_PID) }, { USB_DEVICE(DE_VID, WHT_PID) }, @@ -1023,6 +1044,16 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty, case FT2232C: /* FT2232C chip */ case FT232RL: if (baud <= 3000000) { + __u16 product_id = le16_to_cpu( + port->serial->dev->descriptor.idProduct); + if (((FTDI_NDI_HUC_PID == product_id) || + (FTDI_NDI_SPECTRA_SCU_PID == product_id) || + (FTDI_NDI_FUTURE_2_PID == product_id) || + (FTDI_NDI_FUTURE_3_PID == product_id) || + (FTDI_NDI_AURORA_SCU_PID == product_id)) && + (baud == 19200)) { + baud = 1200000; + } div_value = ftdi_232bm_baud_to_divisor(baud); } else { dbg("%s - Baud rate too high!", __func__); @@ -1554,6 +1585,39 @@ static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv) } /* ftdi_HE_TIRA1_setup */ /* + * Module parameter to control latency timer for NDI FTDI-based USB devices. + * If this value is not set in modprobe.conf.local its value will be set to 1ms. + */ +static int ndi_latency_timer = 1; + +/* Setup for the NDI FTDI-based USB devices, which requires hardwired + * baudrate (19200 gets mapped to 1200000). + * + * Called from usbserial:serial_probe. + */ +static int ftdi_NDI_device_setup(struct usb_serial *serial) +{ + struct usb_device *udev = serial->dev; + int latency = ndi_latency_timer; + int rv = 0; + char buf[1]; + + if (latency == 0) + latency = 1; + if (latency > 99) + latency = 99; + + dbg("%s setting NDI device latency to %d", __func__, latency); + dev_info(&udev->dev, "NDI device with a latency value of %d", latency); + + rv = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + FTDI_SIO_SET_LATENCY_TIMER_REQUEST, + FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE, + latency, 0, buf, 0, WDR_TIMEOUT); + return 0; +} + +/* * First port on JTAG adaptors such as Olimex arm-usb-ocd or the FIC/OpenMoko * Neo1973 Debug Board is reserved for JTAG interface and can be accessed from * userspace using openocd. @@ -2121,7 +2185,7 @@ static void ftdi_process_read(struct work_struct *work) /* Note that the error flag is duplicated for every character received since we don't know which character it applied to */ - if (!usb_serial_handle_sysrq_char(port, + if (!usb_serial_handle_sysrq_char(tty, port, data[packet_offset + i])) tty_insert_flip_char(tty, data[packet_offset + i], @@ -2622,3 +2686,5 @@ MODULE_PARM_DESC(vendor, "User specified vendor ID (default=" module_param(product, ushort, 0); MODULE_PARM_DESC(product, "User specified product ID"); +module_param(ndi_latency_timer, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(ndi_latency_timer, "NDI device latency timer override"); diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index f1d440a728a3..c9fbd7415092 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -506,6 +506,7 @@ * * Armin Laeuger originally sent the PID for the UM 100 module. */ +#define FTDI_R2000KU_TRUE_RNG 0xFB80 /* R2000KU TRUE RNG */ #define FTDI_ELV_UR100_PID 0xFB58 /* USB-RS232-Umsetzer (UR 100) */ #define FTDI_ELV_UM100_PID 0xFB5A /* USB-Modul UM 100 */ #define FTDI_ELV_UO100_PID 0xFB5B /* USB-Modul UO 100 */ @@ -614,6 +615,9 @@ #define FTDI_CCSICDU20_0_PID 0xF9D0 #define FTDI_CCSICDU40_1_PID 0xF9D1 #define FTDI_CCSMACHX_2_PID 0xF9D2 +#define FTDI_CCSLOAD_N_GO_3_PID 0xF9D3 +#define FTDI_CCSICDU64_4_PID 0xF9D4 +#define FTDI_CCSPRIME8_5_PID 0xF9D5 /* Inside Accesso contactless reader (http://www.insidefr.com) */ #define INSIDE_ACCESSO 0xFAD0 @@ -736,6 +740,15 @@ #define FTDI_PYRAMID_PID 0xE6C8 /* Pyramid Appliance Display */ /* + * NDI (www.ndigital.com) product ids + */ +#define FTDI_NDI_HUC_PID 0xDA70 /* NDI Host USB Converter */ +#define FTDI_NDI_SPECTRA_SCU_PID 0xDA71 /* NDI Spectra SCU */ +#define FTDI_NDI_FUTURE_2_PID 0xDA72 /* NDI future device #2 */ +#define FTDI_NDI_FUTURE_3_PID 0xDA73 /* NDI future device #3 */ +#define FTDI_NDI_AURORA_SCU_PID 0xDA74 /* NDI Aurora SCU */ + +/* * Posiflex inc retail equipment (http://www.posiflex.com.tw) */ #define POSIFLEX_VID 0x0d3a /* Vendor ID */ @@ -848,9 +861,6 @@ #define TML_VID 0x1B91 /* Vendor ID */ #define TML_USB_SERIAL_PID 0x0064 /* USB - Serial Converter */ -/* NDI Polaris System */ -#define FTDI_NDI_HUC_PID 0xDA70 - /* Propox devices */ #define FTDI_PROPOX_JTAGCABLEII_PID 0xD738 @@ -934,6 +944,8 @@ #define MARVELL_VID 0x9e88 #define MARVELL_SHEEVAPLUG_PID 0x9e8f +#define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmBH */ + /* * BmRequestType: 1100 0000b * bRequest: FTDI_E2_READ diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 932d6241b787..ce57f6a32bdf 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -424,10 +424,17 @@ static void flush_and_resubmit_read_urb(struct usb_serial_port *port) if (!tty) goto done; - /* Push data to tty */ - for (i = 0; i < urb->actual_length; i++, ch++) { - if (!usb_serial_handle_sysrq_char(port, *ch)) - tty_insert_flip_char(tty, *ch, TTY_NORMAL); + /* The per character mucking around with sysrq path it too slow for + stuff like 3G modems, so shortcircuit it in the 99.9999999% of cases + where the USB serial is not a console anyway */ + if (!port->console || !port->sysrq) + tty_insert_flip_string(tty, ch, urb->actual_length); + else { + /* Push data to tty */ + for (i = 0; i < urb->actual_length; i++, ch++) { + if (!usb_serial_handle_sysrq_char(tty, port, *ch)) + tty_insert_flip_char(tty, *ch, TTY_NORMAL); + } } tty_flip_buffer_push(tty); tty_kref_put(tty); @@ -527,11 +534,12 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty) } } -int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch) +int usb_serial_handle_sysrq_char(struct tty_struct *tty, + struct usb_serial_port *port, unsigned int ch) { if (port->sysrq && port->console) { if (ch && time_before(jiffies, port->sysrq)) { - handle_sysrq(ch, tty_port_tty_get(&port->port)); + handle_sysrq(ch, tty); port->sysrq = 0; return 1; } diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index bfc5ce000ef9..ccd4dd340d2c 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -521,7 +521,7 @@ static int mos7720_chars_in_buffer(struct tty_struct *tty) mos7720_port = usb_get_serial_port_data(port); if (mos7720_port == NULL) { dbg("%s:leaving ...........", __func__); - return -ENODEV; + return 0; } for (i = 0; i < NUM_URBS; ++i) { diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index c40f95c1951c..c31940a307f8 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -26,6 +26,7 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 575816e6ba37..98262dd552bb 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -206,6 +206,7 @@ static int option_resume(struct usb_serial *serial); #define NOVATELWIRELESS_PRODUCT_MC950D 0x4400 #define NOVATELWIRELESS_PRODUCT_U727 0x5010 #define NOVATELWIRELESS_PRODUCT_MC760 0x6000 +#define NOVATELWIRELESS_PRODUCT_OVMC760 0x6002 /* FUTURE NOVATEL PRODUCTS */ #define NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED 0X6001 @@ -307,11 +308,20 @@ static int option_resume(struct usb_serial *serial); #define DLINK_VENDOR_ID 0x1186 #define DLINK_PRODUCT_DWM_652 0x3e04 +#define QISDA_VENDOR_ID 0x1da5 +#define QISDA_PRODUCT_H21_4512 0x4512 +#define QISDA_PRODUCT_H21_4523 0x4523 +#define QISDA_PRODUCT_H20_4515 0x4515 +#define QISDA_PRODUCT_H20_4519 0x4519 + /* TOSHIBA PRODUCTS */ #define TOSHIBA_VENDOR_ID 0x0930 #define TOSHIBA_PRODUCT_HSDPA_MINICARD 0x1302 +#define ALINK_VENDOR_ID 0x1e0e +#define ALINK_PRODUCT_3GU 0x9200 + static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -430,6 +440,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC727) }, /* Novatel MC727/U727/USB727 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U727) }, /* Novatel MC727/U727/USB727 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC760) }, /* Novatel MC760/U760/USB760 */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_OVMC760) }, /* Novatel Ovation MC760 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_FULLSPEED) }, /* Novatel HSPA product */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED) }, /* Novatel EVDO Embedded product */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED) }, /* Novatel HSPA Embedded product */ @@ -529,8 +540,13 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH) }, { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) }, { USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) }, - { USB_DEVICE(0x1da5, 0x4515) }, /* BenQ H20 */ + { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4512) }, + { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4523) }, + { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4515) }, + { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4519) }, { USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_HSDPA_MINICARD ) }, /* Toshiba 3G HSDPA == Novatel Expedite EU870D MiniCard */ + { USB_DEVICE(ALINK_VENDOR_ID, 0x9000) }, + { USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); @@ -732,7 +748,6 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port, memcpy(this_urb->transfer_buffer, buf, todo); this_urb->transfer_buffer_length = todo; - this_urb->dev = port->serial->dev; err = usb_submit_urb(this_urb, GFP_ATOMIC); if (err) { dbg("usb_submit_urb %p (write bulk) failed " @@ -860,7 +875,6 @@ static void option_instat_callback(struct urb *urb) /* Resubmit urb so we continue receiving IRQ data */ if (status != -ESHUTDOWN && status != -ENOENT) { - urb->dev = serial->dev; err = usb_submit_urb(urb, GFP_ATOMIC); if (err) dbg("%s: resubmit intr urb failed. (%d)", @@ -921,23 +935,11 @@ static int option_open(struct tty_struct *tty, dbg("%s", __func__); - /* Reset low level data toggle and start reading from endpoints */ + /* Start reading from the IN endpoint */ for (i = 0; i < N_IN_URB; i++) { urb = portdata->in_urbs[i]; if (!urb) continue; - if (urb->dev != serial->dev) { - dbg("%s: dev %p != %p", __func__, - urb->dev, serial->dev); - continue; - } - - /* - * make sure endpoint data toggle is synchronized with the - * device - */ - usb_clear_halt(urb->dev, urb->pipe); - err = usb_submit_urb(urb, GFP_KERNEL); if (err) { dbg("%s: submit urb %d failed (%d) %d", @@ -946,16 +948,6 @@ static int option_open(struct tty_struct *tty, } } - /* Reset low level data toggle on out endpoints */ - for (i = 0; i < N_OUT_URB; i++) { - urb = portdata->out_urbs[i]; - if (!urb) - continue; - urb->dev = serial->dev; - /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), 0); */ - } - option_send_setup(port); return 0; @@ -1218,7 +1210,6 @@ static int option_resume(struct usb_serial *serial) dbg("%s: No interrupt URB for port %d\n", __func__, i); continue; } - port->interrupt_in_urb->dev = serial->dev; err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); dbg("Submitted interrupt URB for port %d (result %d)", i, err); if (err < 0) { diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index ec6c132a25b5..7d15bfa7c2db 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -94,6 +94,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) }, { USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) }, { USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) }, + { USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) }, { } /* Terminating entry */ }; @@ -971,18 +972,46 @@ exit: __func__, retval); } +static void pl2303_push_data(struct tty_struct *tty, + struct usb_serial_port *port, struct urb *urb, + u8 line_status) +{ + unsigned char *data = urb->transfer_buffer; + /* get tty_flag from status */ + char tty_flag = TTY_NORMAL; + /* break takes precedence over parity, */ + /* which takes precedence over framing errors */ + if (line_status & UART_BREAK_ERROR) + tty_flag = TTY_BREAK; + else if (line_status & UART_PARITY_ERROR) + tty_flag = TTY_PARITY; + else if (line_status & UART_FRAME_ERROR) + tty_flag = TTY_FRAME; + dbg("%s - tty_flag = %d", __func__, tty_flag); + + tty_buffer_request_room(tty, urb->actual_length + 1); + /* overrun is special, not associated with a char */ + if (line_status & UART_OVERRUN_ERROR) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + if (port->console && port->sysrq) { + int i; + for (i = 0; i < urb->actual_length; ++i) + if (!usb_serial_handle_sysrq_char(tty, port, data[i])) + tty_insert_flip_char(tty, data[i], tty_flag); + } else + tty_insert_flip_string(tty, data, urb->actual_length); + tty_flip_buffer_push(tty); +} + static void pl2303_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct pl2303_private *priv = usb_get_serial_port_data(port); struct tty_struct *tty; - unsigned char *data = urb->transfer_buffer; unsigned long flags; - int i; int result; int status = urb->status; u8 line_status; - char tty_flag; dbg("%s - port %d", __func__, port->number); @@ -1010,10 +1039,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) } usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, data); - - /* get tty_flag from status */ - tty_flag = TTY_NORMAL; + urb->actual_length, urb->transfer_buffer); spin_lock_irqsave(&priv->lock, flags); line_status = priv->line_status; @@ -1021,26 +1047,9 @@ static void pl2303_read_bulk_callback(struct urb *urb) spin_unlock_irqrestore(&priv->lock, flags); wake_up_interruptible(&priv->delta_msr_wait); - /* break takes precedence over parity, */ - /* which takes precedence over framing errors */ - if (line_status & UART_BREAK_ERROR) - tty_flag = TTY_BREAK; - else if (line_status & UART_PARITY_ERROR) - tty_flag = TTY_PARITY; - else if (line_status & UART_FRAME_ERROR) - tty_flag = TTY_FRAME; - dbg("%s - tty_flag = %d", __func__, tty_flag); - tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { - tty_buffer_request_room(tty, urb->actual_length + 1); - /* overrun is special, not associated with a char */ - if (line_status & UART_OVERRUN_ERROR) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - for (i = 0; i < urb->actual_length; ++i) - if (!usb_serial_handle_sysrq_char(port, data[i])) - tty_insert_flip_char(tty, data[i], tty_flag); - tty_flip_buffer_push(tty); + pl2303_push_data(tty, port, urb, line_status); } tty_kref_put(tty); /* Schedule the next read _if_ we are still open */ diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 1d7a22e3a9fd..12aac7d2462d 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -122,3 +122,7 @@ /* Hewlett-Packard LD220-HP POS Pole Display */ #define HP_VENDOR_ID 0x03f0 #define HP_LD220_PRODUCT_ID 0x3524 + +/* Cressi Edy (diving computer) PC interface */ +#define CRESSI_VENDOR_ID 0x04b8 +#define CRESSI_EDY_PRODUCT_ID 0x0521 diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 032f7aeb40a4..f48d05e0acc1 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -181,35 +181,50 @@ static const struct sierra_iface_info direct_ip_interface_blacklist = { }; static struct usb_device_id id_table [] = { + { USB_DEVICE(0x0F3D, 0x0112) }, /* Airprime/Sierra PC 5220 */ + { USB_DEVICE(0x03F0, 0x1B1D) }, /* HP ev2200 a.k.a MC5720 */ + { USB_DEVICE(0x03F0, 0x1E1D) }, /* HP hs2300 a.k.a MC8775 */ + { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */ { USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */ { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */ - { USB_DEVICE(0x03f0, 0x1b1d) }, /* HP ev2200 a.k.a MC5720 */ { USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */ - { USB_DEVICE(0x1199, 0x0024) }, /* Sierra Wireless MC5727 */ { USB_DEVICE(0x1199, 0x0220) }, /* Sierra Wireless MC5725 */ + { USB_DEVICE(0x1199, 0x0022) }, /* Sierra Wireless EM5725 */ + { USB_DEVICE(0x1199, 0x0024) }, /* Sierra Wireless MC5727 */ + { USB_DEVICE(0x1199, 0x0224) }, /* Sierra Wireless MC5727 */ { USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */ { USB_DEVICE(0x1199, 0x0021) }, /* Sierra Wireless AirCard 597E */ + { USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */ { USB_DEVICE(0x1199, 0x0120) }, /* Sierra Wireless USB Dongle 595U */ - /* Sierra Wireless C597 */ + /* Sierra Wireless C597 */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x0023, 0xFF, 0xFF, 0xFF) }, - /* Sierra Wireless Device */ + /* Sierra Wireless T598 */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x0025, 0xFF, 0xFF, 0xFF) }, - { USB_DEVICE(0x1199, 0x0026) }, /* Sierra Wireless Device */ - { USB_DEVICE(0x1199, 0x0027) }, /* Sierra Wireless Device */ - { USB_DEVICE(0x1199, 0x0028) }, /* Sierra Wireless Device */ + { USB_DEVICE(0x1199, 0x0026) }, /* Sierra Wireless T11 */ + { USB_DEVICE(0x1199, 0x0027) }, /* Sierra Wireless AC402 */ + { USB_DEVICE(0x1199, 0x0028) }, /* Sierra Wireless MC5728 */ + { USB_DEVICE(0x1199, 0x0029) }, /* Sierra Wireless Device */ { USB_DEVICE(0x1199, 0x6802) }, /* Sierra Wireless MC8755 */ - { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 */ { USB_DEVICE(0x1199, 0x6803) }, /* Sierra Wireless MC8765 */ + { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 */ + { USB_DEVICE(0x1199, 0x6805) }, /* Sierra Wireless MC8765 */ + { USB_DEVICE(0x1199, 0x6808) }, /* Sierra Wireless MC8755 */ + { USB_DEVICE(0x1199, 0x6809) }, /* Sierra Wireless MC8765 */ { USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 & AC 875U */ - { USB_DEVICE(0x1199, 0x6813) }, /* Sierra Wireless MC8775 (Lenovo) */ + { USB_DEVICE(0x1199, 0x6813) }, /* Sierra Wireless MC8775 */ { USB_DEVICE(0x1199, 0x6815) }, /* Sierra Wireless MC8775 */ - { USB_DEVICE(0x03f0, 0x1e1d) }, /* HP hs2300 a.k.a MC8775 */ + { USB_DEVICE(0x1199, 0x6816) }, /* Sierra Wireless MC8775 */ { USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */ { USB_DEVICE(0x1199, 0x6821) }, /* Sierra Wireless AirCard 875U */ + { USB_DEVICE(0x1199, 0x6822) }, /* Sierra Wireless AirCard 875E */ { USB_DEVICE(0x1199, 0x6832) }, /* Sierra Wireless MC8780 */ { USB_DEVICE(0x1199, 0x6833) }, /* Sierra Wireless MC8781 */ + { USB_DEVICE(0x1199, 0x6834) }, /* Sierra Wireless MC8780 */ + { USB_DEVICE(0x1199, 0x6835) }, /* Sierra Wireless MC8781 */ + { USB_DEVICE(0x1199, 0x6838) }, /* Sierra Wireless MC8780 */ + { USB_DEVICE(0x1199, 0x6839) }, /* Sierra Wireless MC8781 */ { USB_DEVICE(0x1199, 0x683A) }, /* Sierra Wireless MC8785 */ { USB_DEVICE(0x1199, 0x683B) }, /* Sierra Wireless MC8785 Composite */ /* Sierra Wireless MC8790, MC8791, MC8792 Composite */ @@ -227,16 +242,13 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x1199, 0x685A) }, /* Sierra Wireless AirCard 885 E */ /* Sierra Wireless C885 */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6880, 0xFF, 0xFF, 0xFF)}, - /* Sierra Wireless Device */ + /* Sierra Wireless C888, Air Card 501, USB 303, USB 304 */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6890, 0xFF, 0xFF, 0xFF)}, - /* Sierra Wireless Device */ + /* Sierra Wireless C22/C33 */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6891, 0xFF, 0xFF, 0xFF)}, - /* Sierra Wireless Device */ + /* Sierra Wireless HSPA Non-Composite Device */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6892, 0xFF, 0xFF, 0xFF)}, - - { USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */ - { USB_DEVICE(0x0F3D, 0x0112) }, /* Airprime/Sierra PC 5220 */ - + { USB_DEVICE(0x1199, 0x6893) }, /* Sierra Wireless Device */ { USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless Direct IP modems */ .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist }, @@ -814,7 +826,7 @@ static int sierra_startup(struct usb_serial *serial) return 0; } -static void sierra_disconnect(struct usb_serial *serial) +static void sierra_release(struct usb_serial *serial) { int i; struct usb_serial_port *port; @@ -830,7 +842,6 @@ static void sierra_disconnect(struct usb_serial *serial) if (!portdata) continue; kfree(portdata); - usb_set_serial_port_data(port, NULL); } } @@ -853,7 +864,7 @@ static struct usb_serial_driver sierra_device = { .tiocmget = sierra_tiocmget, .tiocmset = sierra_tiocmset, .attach = sierra_startup, - .disconnect = sierra_disconnect, + .release = sierra_release, .read_int_callback = sierra_instat_callback, }; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 991d8232e376..3bc609fe2242 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -191,7 +191,6 @@ static struct usb_device_id ti_id_table_5052[5+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) }, - { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, }; static struct usb_device_id ti_id_table_combined[14+2*TI_EXTRA_VID_PID_COUNT+1] = { @@ -728,7 +727,7 @@ static int ti_write_room(struct tty_struct *tty) dbg("%s - port %d", __func__, port->number); if (tport == NULL) - return -ENODEV; + return 0; spin_lock_irqsave(&tport->tp_lock, flags); room = ti_buf_space_avail(tport->tp_write_buf); @@ -749,7 +748,7 @@ static int ti_chars_in_buffer(struct tty_struct *tty) dbg("%s - port %d", __func__, port->number); if (tport == NULL) - return -ENODEV; + return 0; spin_lock_irqsave(&tport->tp_lock, flags); chars = ti_buf_data_avail(tport->tp_write_buf); @@ -1658,7 +1657,7 @@ static int ti_do_download(struct usb_device *dev, int pipe, u8 cs = 0; int done; struct ti_firmware_header *header; - int status; + int status = 0; int len; for (pos = sizeof(struct ti_firmware_header); pos < size; pos++) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index a84216464ca0..99188c92068b 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -21,6 +21,7 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> @@ -31,6 +32,7 @@ #include <linux/mutex.h> #include <linux/list.h> #include <linux/uaccess.h> +#include <linux/serial.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "pl2303.h" @@ -183,6 +185,7 @@ static int serial_open (struct tty_struct *tty, struct file *filp) struct usb_serial_port *port; unsigned int portNumber; int retval = 0; + int first = 0; dbg("%s", __func__); @@ -220,8 +223,9 @@ static int serial_open (struct tty_struct *tty, struct file *filp) tty->driver_data = port; tty_port_tty_set(&port->port, tty); - if (port->port.count == 1) { - + /* If the console is attached, the device is already open */ + if (port->port.count == 1 && !port->console) { + first = 1; /* lock this module before we call it * this may fail, which means we must bail out, * safe because we are called with BKL held */ @@ -244,13 +248,21 @@ static int serial_open (struct tty_struct *tty, struct file *filp) if (retval) goto bailout_interface_put; mutex_unlock(&serial->disc_mutex); + set_bit(ASYNCB_INITIALIZED, &port->port.flags); } mutex_unlock(&port->mutex); /* Now do the correct tty layer semantics */ retval = tty_port_block_til_ready(&port->port, tty, filp); - if (retval == 0) + if (retval == 0) { + if (!first) + usb_serial_put(serial); return 0; - + } + mutex_lock(&port->mutex); + if (first == 0) + goto bailout_mutex_unlock; + /* Undo the initial port actions */ + mutex_lock(&serial->disc_mutex); bailout_interface_put: usb_autopm_put_interface(serial->interface); bailout_module_put: @@ -338,6 +350,22 @@ static void serial_close(struct tty_struct *tty, struct file *filp) dbg("%s - port %d", __func__, port->number); + /* FIXME: + This leaves a very narrow race. Really we should do the + serial_do_free() on tty->shutdown(), but tty->shutdown can + be called from IRQ context and serial_do_free can sleep. + + The right fix is probably to make the tty free (which is rare) + and thus tty->shutdown() occur via a work queue and simplify all + the drivers that use it. + */ + if (tty_hung_up_p(filp)) { + /* serial_hangup already called serial_down at this point. + Another user may have already reopened the port but + serial_do_free is refcounted */ + serial_do_free(port); + return; + } if (tty_port_close_start(&port->port, tty, filp) == 0) return; @@ -353,7 +381,8 @@ static void serial_hangup(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; serial_do_down(port); tty_port_hangup(&port->port); - serial_do_free(port); + /* We must not free port yet - the USB serial layer depends on it's + continued existence */ } static int serial_write(struct tty_struct *tty, const unsigned char *buf, @@ -392,7 +421,6 @@ static int serial_chars_in_buffer(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; dbg("%s = port %d", __func__, port->number); - WARN_ON(!port->port.count); /* if the device was unplugged then any remaining characters fell out of the connector ;) */ if (port->serial->disconnected) diff --git a/drivers/usb/storage/option_ms.c b/drivers/usb/storage/option_ms.c index d41cc0a970f7..773a5cd38c5a 100644 --- a/drivers/usb/storage/option_ms.c +++ b/drivers/usb/storage/option_ms.c @@ -118,6 +118,9 @@ static int option_inquiry(struct us_data *us) result = memcmp(buffer+8, "Option", 6); + if (result != 0) + result = memcmp(buffer+8, "ZCOPTION", 8); + /* Read the CSW */ usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 8afcf08eba98..3b54b3940178 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1119,12 +1119,13 @@ config FB_CARILLO_RANCH config FB_INTEL tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G/945GM/965G/965GM support (EXPERIMENTAL)" - depends on EXPERIMENTAL && FB && PCI && X86 && AGP_INTEL + depends on EXPERIMENTAL && FB && PCI && X86 && AGP_INTEL && EMBEDDED select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_BOOT_VESA_SUPPORT if FB_INTEL = y + depends on !DRM_I915 help This driver supports the on-board graphics built in to the Intel 830M/845G/852GM/855GM/865G/915G/915GM/945G/945GM/965G/965GM chipsets. diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c index fb8163d181ab..a21efcd10b78 100644 --- a/drivers/video/amba-clcd.c +++ b/drivers/video/amba-clcd.c @@ -226,9 +226,10 @@ static int clcdfb_set_par(struct fb_info *info) clcdfb_enable(fb, regs.cntl); #ifdef DEBUG - printk(KERN_INFO "CLCD: Registers set to\n" - KERN_INFO " %08x %08x %08x %08x\n" - KERN_INFO " %08x %08x %08x %08x\n", + printk(KERN_INFO + "CLCD: Registers set to\n" + " %08x %08x %08x %08x\n" + " %08x %08x %08x %08x\n", readl(fb->regs + CLCD_TIM0), readl(fb->regs + CLCD_TIM1), readl(fb->regs + CLCD_TIM2), readl(fb->regs + CLCD_TIM3), readl(fb->regs + CLCD_UBAS), readl(fb->regs + CLCD_LBAS), diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c index 497ff8af03ed..8cd279be74e5 100644 --- a/drivers/video/atafb.c +++ b/drivers/video/atafb.c @@ -2405,6 +2405,9 @@ static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive) return 0; } +/* fbhw->encode_fix() must be called with fb_info->mm_lock held + * if it is called after the register_framebuffer() - not a case here + */ static int atafb_get_fix(struct fb_fix_screeninfo *fix, struct fb_info *info) { struct atafb_par par; @@ -2414,9 +2417,7 @@ static int atafb_get_fix(struct fb_fix_screeninfo *fix, struct fb_info *info) if (err) return err; memset(fix, 0, sizeof(struct fb_fix_screeninfo)); - mutex_lock(&info->mm_lock); err = fbhw->encode_fix(fix, &par); - mutex_unlock(&info->mm_lock); return err; } diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index cb88394ba995..da05f0801bb7 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -261,6 +261,9 @@ static inline void atmel_lcdfb_free_video_memory(struct atmel_lcdfb_info *sinfo) /** * atmel_lcdfb_alloc_video_memory - Allocate framebuffer memory * @sinfo: the frame buffer to allocate memory for + * + * This function is called only from the atmel_lcdfb_probe() + * so no locking by fb_info->mm_lock around smem_len setting is needed. */ static int atmel_lcdfb_alloc_video_memory(struct atmel_lcdfb_info *sinfo) { @@ -270,9 +273,7 @@ static int atmel_lcdfb_alloc_video_memory(struct atmel_lcdfb_info *sinfo) smem_len = (var->xres_virtual * var->yres_virtual * ((var->bits_per_pixel + 7) / 8)); - mutex_lock(&info->mm_lock); info->fix.smem_len = max(smem_len, sinfo->smem_len); - mutex_unlock(&info->mm_lock); info->screen_base = dma_alloc_writecombine(info->device, info->fix.smem_len, (dma_addr_t *)&info->fix.smem_start, GFP_KERNEL); diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index e641584e212e..887166267443 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -145,6 +145,8 @@ static int pwm_backlight_suspend(struct platform_device *pdev, struct backlight_device *bl = platform_get_drvdata(pdev); struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + if (pb->notify) + pb->notify(0); pwm_config(pb->pwm, 0, pb->period); pwm_disable(pb->pwm); return 0; diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 53eb39652791..a85c818be945 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -16,7 +16,6 @@ #include <linux/compat.h> #include <linux/types.h> #include <linux/errno.h> -#include <linux/smp_lock.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/slab.h> @@ -1513,6 +1512,8 @@ register_framebuffer(struct fb_info *fb_info) if (!registered_fb[i]) break; fb_info->node = i; + mutex_init(&fb_info->lock); + mutex_init(&fb_info->mm_lock); fb_info->dev = device_create(fb_class, fb_info->device, MKDEV(FB_MAJOR, i), NULL, "fb%d", i); diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 5c1a2c01778f..9ae9cd32bd06 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -256,8 +256,8 @@ static void fix_edid(unsigned char *edid, int fix) static int edid_checksum(unsigned char *edid) { - unsigned char i, csum = 0, all_null = 0; - int err = 0, fix = check_edid(edid); + unsigned char csum = 0, all_null = 0; + int i, err = 0, fix = check_edid(edid); if (fix) fix_edid(edid, fix); diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index afc04df39a03..d4a2c11d9809 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -62,9 +62,6 @@ struct fb_info *framebuffer_alloc(size_t size, struct device *dev) mutex_init(&info->bl_curve_mutex); #endif - mutex_init(&info->lock); - mutex_init(&info->mm_lock); - return info; #undef PADDING #undef BYTES_PER_LONG diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 0bf2190928d0..72d68b3dc478 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -1223,12 +1223,6 @@ static int __devinit install_fb(struct fb_info *info) return -EINVAL; } - if (fsl_diu_set_par(info)) { - printk(KERN_ERR "fb_set_par failed"); - fb_dealloc_cmap(&info->cmap); - return -EINVAL; - } - if (register_framebuffer(info) < 0) { printk(KERN_ERR "register_framebuffer failed"); unmap_video_memory(info); diff --git a/drivers/video/hitfb.c b/drivers/video/hitfb.c index 020db7fc9153..e7116a6d82d3 100644 --- a/drivers/video/hitfb.c +++ b/drivers/video/hitfb.c @@ -44,9 +44,6 @@ static struct fb_fix_screeninfo hitfb_fix __initdata = { .accel = FB_ACCEL_NONE, }; -static u32 pseudo_palette[16]; -static struct fb_info fb_info; - static inline void hitfb_accel_wait(void) { while (fb_readw(HD64461_GRCFGR) & HD64461_GRCFGR_ACCSTATUS) ; @@ -331,6 +328,8 @@ static struct fb_ops hitfb_ops = { static int __init hitfb_probe(struct platform_device *dev) { unsigned short lcdclor, ldr3, ldvndr; + struct fb_info *info; + int ret; if (fb_get_options("hitfb", NULL)) return -ENODEV; @@ -384,32 +383,53 @@ static int __init hitfb_probe(struct platform_device *dev) break; } - fb_info.fbops = &hitfb_ops; - fb_info.var = hitfb_var; - fb_info.fix = hitfb_fix; - fb_info.pseudo_palette = pseudo_palette; - fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN | + info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev); + if (unlikely(!info)) + return -ENOMEM; + + info->fbops = &hitfb_ops; + info->var = hitfb_var; + info->fix = hitfb_fix; + info->pseudo_palette = info->par; + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA; - fb_info.screen_base = (void *)hitfb_fix.smem_start; + info->screen_base = (void *)hitfb_fix.smem_start; - fb_alloc_cmap(&fb_info.cmap, 256, 0); + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (unlikely(ret < 0)) + goto err_fb; - if (register_framebuffer(&fb_info) < 0) - return -EINVAL; + ret = register_framebuffer(info); + if (unlikely(ret < 0)) + goto err; + + platform_set_drvdata(dev, info); printk(KERN_INFO "fb%d: %s frame buffer device\n", - fb_info.node, fb_info.fix.id); + info->node, info->fix.id); + return 0; + +err: + fb_dealloc_cmap(&info->cmap); +err_fb: + framebuffer_release(info); + return ret; } static int __exit hitfb_remove(struct platform_device *dev) { - return unregister_framebuffer(&fb_info); + struct fb_info *info = platform_get_drvdata(dev); + + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + + return 0; } -#ifdef CONFIG_PM -static int hitfb_suspend(struct platform_device *dev, pm_message_t state) +static int hitfb_suspend(struct device *dev) { u16 v; @@ -421,7 +441,7 @@ static int hitfb_suspend(struct platform_device *dev, pm_message_t state) return 0; } -static int hitfb_resume(struct platform_device *dev) +static int hitfb_resume(struct device *dev) { u16 v; @@ -435,17 +455,19 @@ static int hitfb_resume(struct platform_device *dev) return 0; } -#endif + +static struct dev_pm_ops hitfb_dev_pm_ops = { + .suspend = hitfb_suspend, + .resume = hitfb_resume, +}; static struct platform_driver hitfb_driver = { .probe = hitfb_probe, .remove = __exit_p(hitfb_remove), -#ifdef CONFIG_PM - .suspend = hitfb_suspend, - .resume = hitfb_resume, -#endif .driver = { .name = "hitfb", + .owner = THIS_MODULE, + .pm = &hitfb_dev_pm_ops, }, }; diff --git a/drivers/video/i810/i810_main.c b/drivers/video/i810/i810_main.c index 71960672d721..5743ea25e818 100644 --- a/drivers/video/i810/i810_main.c +++ b/drivers/video/i810/i810_main.c @@ -2060,8 +2060,7 @@ static int __devinit i810fb_init_pci (struct pci_dev *dev, fb_var_to_videomode(&mode, &info->var); fb_add_videomode(&mode, &info->modelist); - encode_fix(&info->fix, info); - + i810fb_init_ringbuffer(info); err = register_framebuffer(info); diff --git a/drivers/video/matrox/matroxfb_DAC1064.c b/drivers/video/matrox/matroxfb_DAC1064.c index 0ce3b0a89798..a74e5da17aa0 100644 --- a/drivers/video/matrox/matroxfb_DAC1064.c +++ b/drivers/video/matrox/matroxfb_DAC1064.c @@ -454,9 +454,9 @@ static void DAC1064_restore_2(WPMINFO2) { dprintk(KERN_DEBUG "DAC1064regs "); for (i = 0; i < sizeof(MGA1064_DAC_regs); i++) { dprintk("R%02X=%02X ", MGA1064_DAC_regs[i], ACCESS_FBINFO(hw).DACreg[i]); - if ((i & 0x7) == 0x7) dprintk("\n" KERN_DEBUG "continuing... "); + if ((i & 0x7) == 0x7) dprintk(KERN_DEBUG "continuing... "); } - dprintk("\n" KERN_DEBUG "DAC1064clk "); + dprintk(KERN_DEBUG "DAC1064clk "); for (i = 0; i < 6; i++) dprintk("C%02X=%02X ", i, ACCESS_FBINFO(hw).DACclk[i]); dprintk("\n"); diff --git a/drivers/video/matrox/matroxfb_Ti3026.c b/drivers/video/matrox/matroxfb_Ti3026.c index 13524821e242..4e825112a601 100644 --- a/drivers/video/matrox/matroxfb_Ti3026.c +++ b/drivers/video/matrox/matroxfb_Ti3026.c @@ -651,9 +651,9 @@ static void Ti3026_restore(WPMINFO2) { dprintk(KERN_DEBUG "3026DACregs "); for (i = 0; i < 21; i++) { dprintk("R%02X=%02X ", DACseq[i], hw->DACreg[i]); - if ((i & 0x7) == 0x7) dprintk("\n" KERN_DEBUG "continuing... "); + if ((i & 0x7) == 0x7) dprintk(KERN_DEBUG "continuing... "); } - dprintk("\n" KERN_DEBUG "DACclk "); + dprintk(KERN_DEBUG "DACclk "); for (i = 0; i < 6; i++) dprintk("C%02X=%02X ", i, hw->DACclk[i]); dprintk("\n"); diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c index 76bc51b616d1..0c1049b308bf 100644 --- a/drivers/video/matrox/matroxfb_base.c +++ b/drivers/video/matrox/matroxfb_base.c @@ -1876,7 +1876,6 @@ static int initMatrox2(WPMINFO struct board* b){ } 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 @@ -2083,7 +2082,6 @@ static int matroxfb_probe(struct pci_dev* pdev, const struct pci_device_id* dumm spin_lock_init(&ACCESS_FBINFO(lock.accel)); init_rwsem(&ACCESS_FBINFO(crtc2.lock)); init_rwsem(&ACCESS_FBINFO(altout.lock)); - mutex_init(&ACCESS_FBINFO(fbcon).lock); mutex_init(&ACCESS_FBINFO(fbcon).mm_lock); ACCESS_FBINFO(irq_flags) = 0; init_waitqueue_head(&ACCESS_FBINFO(crtc1.vsync.wait)); diff --git a/drivers/video/matrox/matroxfb_crtc2.c b/drivers/video/matrox/matroxfb_crtc2.c index 909e10a11898..ebcb5c6b4962 100644 --- a/drivers/video/matrox/matroxfb_crtc2.c +++ b/drivers/video/matrox/matroxfb_crtc2.c @@ -289,16 +289,18 @@ static int matroxfb_dh_release(struct fb_info* info, int user) { #undef m2info } +/* + * This function is called before the register_framebuffer so + * no locking is needed. + */ static void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info *m2info) { struct fb_fix_screeninfo *fix = &m2info->fbcon.fix; strcpy(fix->id, "MATROX DH"); - mutex_lock(&m2info->fbcon.mm_lock); fix->smem_start = m2info->video.base; fix->smem_len = m2info->video.len_usable; - mutex_unlock(&m2info->fbcon.mm_lock); fix->ypanstep = 1; fix->ywrapstep = 0; fix->xpanstep = 8; /* TBD */ diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index 567fb944bd2a..f8778cde2183 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -1365,11 +1365,6 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan) init_completion(&mx3fbi->flip_cmpl); disable_irq(ichan->eof_irq); dev_dbg(mx3fb->dev, "disabling irq %d\n", ichan->eof_irq); - ret = mx3fb_set_par(fbi); - if (ret < 0) - goto esetpar; - - mx3fb_blank(FB_BLANK_UNBLANK, fbi); dev_info(dev, "registered, using mode %s\n", fb_mode); diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index 4ea99bfc37b4..8862233d57b6 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -1254,7 +1254,7 @@ static struct fb_ops omapfb_ops = { static ssize_t omapfb_show_caps_num(struct device *dev, struct device_attribute *attr, char *buf) { - struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + struct omapfb_device *fbdev = dev_get_drvdata(dev); int plane; size_t size; struct omapfb_caps caps; @@ -1274,7 +1274,7 @@ static ssize_t omapfb_show_caps_num(struct device *dev, static ssize_t omapfb_show_caps_text(struct device *dev, struct device_attribute *attr, char *buf) { - struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + struct omapfb_device *fbdev = dev_get_drvdata(dev); int i; struct omapfb_caps caps; int plane; @@ -1321,7 +1321,7 @@ static DEVICE_ATTR(caps_text, 0444, omapfb_show_caps_text, NULL); static ssize_t omapfb_show_panel_name(struct device *dev, struct device_attribute *attr, char *buf) { - struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + struct omapfb_device *fbdev = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->panel->name); } @@ -1330,7 +1330,7 @@ static ssize_t omapfb_show_bklight_level(struct device *dev, struct device_attribute *attr, char *buf) { - struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + struct omapfb_device *fbdev = dev_get_drvdata(dev); int r; if (fbdev->panel->get_bklight_level) { @@ -1345,7 +1345,7 @@ static ssize_t omapfb_store_bklight_level(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + struct omapfb_device *fbdev = dev_get_drvdata(dev); int r; if (fbdev->panel->set_bklight_level) { @@ -1364,7 +1364,7 @@ static ssize_t omapfb_store_bklight_level(struct device *dev, static ssize_t omapfb_show_bklight_max(struct device *dev, struct device_attribute *attr, char *buf) { - struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + struct omapfb_device *fbdev = dev_get_drvdata(dev); int r; if (fbdev->panel->get_bklight_level) { @@ -1397,7 +1397,7 @@ static struct attribute_group panel_attr_grp = { static ssize_t omapfb_show_ctrl_name(struct device *dev, struct device_attribute *attr, char *buf) { - struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + struct omapfb_device *fbdev = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->ctrl->name); } diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index da983b720f08..8f24564f77b0 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -31,7 +31,7 @@ struct sh_mobile_lcdc_chan { unsigned long enabled; /* ME and SE in LDCNT2R */ struct sh_mobile_lcdc_chan_cfg cfg; u32 pseudo_palette[PALETTE_NR]; - struct fb_info info; + struct fb_info *info; dma_addr_t dma_handle; struct fb_deferred_io defio; struct scatterlist *sglist; @@ -442,22 +442,22 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) /* set bpp format in PKF[4:0] */ tmp = lcdc_read_chan(ch, LDDFR); tmp &= ~(0x0001001f); - tmp |= (priv->ch[k].info.var.bits_per_pixel == 16) ? 3 : 0; + tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0; lcdc_write_chan(ch, LDDFR, tmp); /* point out our frame buffer */ - lcdc_write_chan(ch, LDSA1R, ch->info.fix.smem_start); + lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start); /* set line size */ - lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length); + lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length); /* setup deferred io if SYS bus */ tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; if (ch->ldmt1r_value & (1 << 12) && tmp) { ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; ch->defio.delay = msecs_to_jiffies(tmp); - ch->info.fbdefio = &ch->defio; - fb_deferred_io_init(&ch->info); + ch->info->fbdefio = &ch->defio; + fb_deferred_io_init(ch->info); /* one-shot mode */ lcdc_write_chan(ch, LDSM1R, 1); @@ -503,12 +503,12 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) * flush frame, and wait for frame end interrupt * clean up deferred io and enable clock */ - if (ch->info.fbdefio) { + if (ch->info->fbdefio) { ch->frame_end = 0; - schedule_delayed_work(&ch->info.deferred_work, 0); + schedule_delayed_work(&ch->info->deferred_work, 0); wait_event(ch->frame_end_wait, ch->frame_end); - fb_deferred_io_cleanup(&ch->info); - ch->info.fbdefio = NULL; + fb_deferred_io_cleanup(ch->info); + ch->info->fbdefio = NULL; sh_mobile_lcdc_clk_on(priv); } @@ -817,9 +817,16 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1); for (i = 0; i < j; i++) { - info = &priv->ch[i].info; cfg = &priv->ch[i].cfg; + priv->ch[i].info = framebuffer_alloc(0, &pdev->dev); + if (!priv->ch[i].info) { + dev_err(&pdev->dev, "unable to allocate fb_info\n"); + error = -ENOMEM; + break; + } + + info = priv->ch[i].info; info->fbops = &sh_mobile_lcdc_ops; info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres; info->var.yres = info->var.yres_virtual = cfg->lcd_cfg.yres; @@ -872,7 +879,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) for (i = 0; i < j; i++) { struct sh_mobile_lcdc_chan *ch = priv->ch + i; - info = &ch->info; + info = ch->info; if (info->fbdefio) { priv->ch->sglist = vmalloc(sizeof(struct scatterlist) * @@ -915,15 +922,15 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) int i; for (i = 0; i < ARRAY_SIZE(priv->ch); i++) - if (priv->ch[i].info.dev) - unregister_framebuffer(&priv->ch[i].info); + if (priv->ch[i].info->dev) + unregister_framebuffer(priv->ch[i].info); sh_mobile_lcdc_stop(priv); for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { - info = &priv->ch[i].info; + info = priv->ch[i].info; - if (!info->device) + if (!info || !info->device) continue; if (priv->ch[i].sglist) @@ -932,6 +939,7 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) dma_free_coherent(&pdev->dev, info->fix.smem_len, info->screen_base, priv->ch[i].dma_handle); fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); } #ifdef CONFIG_HAVE_CLK diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c index 98f24f0ec00d..924d79462780 100644 --- a/drivers/video/sm501fb.c +++ b/drivers/video/sm501fb.c @@ -1540,9 +1540,6 @@ static int sm501fb_init_fb(struct fb_info *fb, if (ret) dev_err(info->dev, "check_var() failed on initial setup?\n"); - /* ensure we've activated our new configuration */ - (fb->fbops->fb_set_par)(fb); - return 0; } @@ -1624,6 +1621,8 @@ static int __devinit sm501fb_start_one(struct sm501fb_info *info, if (!fbi) return 0; + mutex_init(&info->fb[head]->mm_lock); + ret = sm501fb_init_fb(info->fb[head], head, drvname); if (ret) { dev_err(info->dev, "cannot initialise fb %s\n", drvname); diff --git a/drivers/video/stifb.c b/drivers/video/stifb.c index eec9dcb7f599..6120f0c526fe 100644 --- a/drivers/video/stifb.c +++ b/drivers/video/stifb.c @@ -1115,10 +1115,9 @@ static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref) if the device name contains the string "DX" and tell the user how to reconfigure the card. */ if (strstr(sti->outptr.dev_name, "DX")) { - printk(KERN_WARNING "WARNING: stifb framebuffer driver does not " - "support '%s' in double-buffer mode.\n" - KERN_WARNING "WARNING: Please disable the double-buffer mode " - "in IPL menu (the PARISC-BIOS).\n", + printk(KERN_WARNING +"WARNING: stifb framebuffer driver does not support '%s' in double-buffer mode.\n" +"WARNING: Please disable the double-buffer mode in IPL menu (the PARISC-BIOS).\n", sti->outptr.dev_name); goto out_err0; } diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c index 8a141c2c637b..2376f688ec8b 100644 --- a/drivers/video/w100fb.c +++ b/drivers/video/w100fb.c @@ -748,8 +748,6 @@ int __init w100fb_probe(struct platform_device *pdev) goto out; } - w100fb_set_par(info); - if (register_framebuffer(info) < 0) { err = -EINVAL; goto out; diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 193c8f0e5cc5..bcec78ffc765 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -669,7 +669,7 @@ static int __init virtio_pci_init(void) err = pci_register_driver(&virtio_pci_driver); if (err) - device_unregister(virtio_pci_root); + root_device_unregister(virtio_pci_root); return err; } diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c index 5c7011cda6a6..751c003864ad 100644 --- a/drivers/watchdog/bcm47xx_wdt.c +++ b/drivers/watchdog/bcm47xx_wdt.c @@ -161,7 +161,7 @@ static long bcm47xx_wdt_ioctl(struct file *file, { void __user *argp = (void __user *)arg; int __user *p = argp; - int new_value, retval = -EINVAL;; + int new_value, retval = -EINVAL; switch (cmd) { case WDIOC_GETSUPPORT: diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c index e9f950ff86ea..cdd55e0d09f8 100644 --- a/drivers/watchdog/ep93xx_wdt.c +++ b/drivers/watchdog/ep93xx_wdt.c @@ -29,6 +29,7 @@ #include <linux/watchdog.h> #include <linux/timer.h> #include <linux/uaccess.h> +#include <linux/io.h> #include <mach/hardware.h> #define WDT_VERSION "0.3" diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c index ee1caae4d33b..016245419fad 100644 --- a/drivers/watchdog/sa1100_wdt.c +++ b/drivers/watchdog/sa1100_wdt.c @@ -38,7 +38,7 @@ static unsigned long oscr_freq; static unsigned long sa1100wdt_users; -static int pre_margin; +static unsigned int pre_margin; static int boot_status; /* @@ -84,6 +84,7 @@ static const struct watchdog_info ident = { .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, .identity = "SA1100/PXA255 Watchdog", + .firmware_version = 1, }; static long sa1100dog_ioctl(struct file *file, unsigned int cmd, @@ -118,7 +119,7 @@ static long sa1100dog_ioctl(struct file *file, unsigned int cmd, if (ret) break; - if (time <= 0 || time > 255) { + if (time <= 0 || (oscr_freq * (long long)time >= 0xffffffff)) { ret = -EINVAL; break; } diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index 916890abffdd..f201accc4e3d 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -89,6 +89,11 @@ static void w83627hf_select_wd_register(void) c = ((inb_p(WDT_EFDR) & 0xf7) | 0x04); /* select WDT0 */ outb_p(0x2b, WDT_EFER); outb_p(c, WDT_EFDR); /* set GPIO3 to WDT0 */ + } else if (c == 0x88) { /* W83627EHF */ + outb_p(0x2d, WDT_EFER); /* select GPIO5 */ + c = inb_p(WDT_EFDR) & ~0x01; /* PIN77 -> WDT0# */ + outb_p(0x2d, WDT_EFER); + outb_p(c, WDT_EFDR); /* set GPIO5 to WDT0 */ } outb_p(0x07, WDT_EFER); /* point to logical device number reg */ diff --git a/drivers/watchdog/w83697ug_wdt.c b/drivers/watchdog/w83697ug_wdt.c index 883b5f79673a..a6c12dec91a1 100644 --- a/drivers/watchdog/w83697ug_wdt.c +++ b/drivers/watchdog/w83697ug_wdt.c @@ -149,8 +149,10 @@ static void wdt_ctrl(int timeout) { spin_lock(&io_lock); - if (w83697ug_select_wd_register() < 0) + if (w83697ug_select_wd_register() < 0) { + spin_unlock(&io_lock); return; + } outb_p(0xF4, WDT_EFER); /* Select CRF4 */ outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF4 */ |