diff options
author | Jeff Garzik <jeff@garzik.org> | 2006-09-30 23:45:41 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-09-30 23:45:41 -0400 |
commit | 1c7da74c4aab595a994beb5fe728ebf0d0b41f59 (patch) | |
tree | 64128abdf9550ebb51d8f3ee6732d7350b9c62f2 /drivers | |
parent | aebb1153ac54ddbbd3d3f0481a193f4bf0ead53b (diff) | |
parent | 1bdfd554be94def718323659173517c5d4a69d25 (diff) |
Merge branch 'master' into upstream
Diffstat (limited to 'drivers')
523 files changed, 23054 insertions, 12131 deletions
diff --git a/drivers/base/class.c b/drivers/base/class.c index b06b0e2b9c6..b32b77ff2dc 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -228,7 +228,7 @@ error: /** * class_destroy - destroys a struct class structure - * @cs: pointer to the struct class that is to be destroyed + * @cls: pointer to the struct class that is to be destroyed * * Note, the pointer to be destroyed must have been created with a call * to class_create(). @@ -658,9 +658,9 @@ int class_device_register(struct class_device *class_dev) /** * class_device_create - creates a class device and registers it with sysfs - * @cs: pointer to the struct class that this device should be registered to. + * @cls: pointer to the struct class that this device should be registered to. * @parent: pointer to the parent struct class_device of this new device, if any. - * @dev: the dev_t for the char device to be added. + * @devt: the dev_t for the char device to be added. * @device: a pointer to a struct device that is assiociated with this class device. * @fmt: string for the class device's name * @@ -766,7 +766,7 @@ void class_device_unregister(struct class_device *class_dev) /** * class_device_destroy - removes a class device that was created with class_device_create() * @cls: the pointer to the struct class that this device was registered * with. - * @dev: the dev_t of the device that was previously registered. + * @devt: the dev_t of the device that was previously registered. * * This call unregisters and cleans up a class device that was created with a * call to class_device_create() diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 77bf8826e2f..14615694ae9 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -16,6 +16,7 @@ #include <linux/interrupt.h> #include <linux/bitops.h> #include <linux/mutex.h> +#include <linux/kthread.h> #include <linux/firmware.h> #include "base.h" @@ -511,7 +512,6 @@ request_firmware_work_func(void *arg) WARN_ON(1); return 0; } - daemonize("%s/%s", "firmware", fw_work->name); ret = _request_firmware(&fw, fw_work->name, fw_work->device, fw_work->uevent); if (ret < 0) @@ -546,9 +546,9 @@ request_firmware_nowait( const char *name, struct device *device, void *context, void (*cont)(const struct firmware *fw, void *context)) { + struct task_struct *task; struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work), GFP_ATOMIC); - int ret; if (!fw_work) return -ENOMEM; @@ -566,14 +566,14 @@ request_firmware_nowait( .uevent = uevent, }; - ret = kernel_thread(request_firmware_work_func, fw_work, - CLONE_FS | CLONE_FILES); + task = kthread_run(request_firmware_work_func, fw_work, + "firmware/%s", name); - if (ret < 0) { + if (IS_ERR(task)) { fw_work->cont(NULL, fw_work->context); module_put(fw_work->module); kfree(fw_work); - return ret; + return PTR_ERR(task); } return 0; } diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index a360215dbce..2568640430f 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -3331,7 +3331,7 @@ static int DAC960_process_queue(DAC960_Controller_T *Controller, struct request_ Command->DmaDirection = PCI_DMA_TODEVICE; Command->CommandType = DAC960_WriteCommand; } - Command->Completion = Request->waiting; + Command->Completion = Request->end_io_data; Command->LogicalDriveNumber = (long)Request->rq_disk->private_data; Command->BlockNumber = Request->sector; Command->BlockCount = Request->nr_sectors; diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index b5382cedf0c..422e31d5f8e 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -2,6 +2,8 @@ # Block device driver configuration # +if BLOCK + menu "Block devices" config BLK_DEV_FD @@ -468,3 +470,5 @@ config ATA_OVER_ETH devices like the Coraid EtherDrive (R) Storage Blade. endmenu + +endif diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 2cd3391ff87..c211065ad82 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1229,7 +1229,6 @@ static inline void complete_buffers(struct bio *bio, int status) int nr_sectors = bio_sectors(bio); bio->bi_next = NULL; - blk_finished_io(len); bio_endio(bio, nr_sectors << 9, status ? 0 : -EIO); bio = xbh; } diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 78082edc14b..4abc193314e 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -989,7 +989,6 @@ static inline void complete_buffers(struct bio *bio, int ok) xbh = bio->bi_next; bio->bi_next = NULL; - blk_finished_io(nr_sectors); bio_endio(bio, nr_sectors << 9, ok ? 0 : -EIO); bio = xbh; diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index ad1d7065a1b..629c5769d99 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -2991,8 +2991,8 @@ static void do_fd_request(request_queue_t * q) if (usage_count == 0) { printk("warning: usage count=0, current_req=%p exiting\n", current_req); - printk("sect=%ld flags=%lx\n", (long)current_req->sector, - current_req->flags); + printk("sect=%ld type=%x flags=%x\n", (long)current_req->sector, + current_req->cmd_type, current_req->cmd_flags); return; } if (test_bit(0, &fdc_busy)) { diff --git a/drivers/block/loop.c b/drivers/block/loop.c index c774121684d..d6bb8da955a 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -66,12 +66,14 @@ #include <linux/swap.h> #include <linux/slab.h> #include <linux/loop.h> +#include <linux/compat.h> #include <linux/suspend.h> #include <linux/writeback.h> #include <linux/buffer_head.h> /* for invalidate_bdev() */ #include <linux/completion.h> #include <linux/highmem.h> #include <linux/gfp.h> +#include <linux/kthread.h> #include <asm/uaccess.h> @@ -522,15 +524,12 @@ static int loop_make_request(request_queue_t *q, struct bio *old_bio) goto out; if (unlikely(rw == WRITE && (lo->lo_flags & LO_FLAGS_READ_ONLY))) goto out; - lo->lo_pending++; loop_add_bio(lo, old_bio); + wake_up(&lo->lo_event); spin_unlock_irq(&lo->lo_lock); - complete(&lo->lo_bh_done); return 0; out: - if (lo->lo_pending == 0) - complete(&lo->lo_bh_done); spin_unlock_irq(&lo->lo_lock); bio_io_error(old_bio, old_bio->bi_size); return 0; @@ -570,14 +569,18 @@ static inline void loop_handle_bio(struct loop_device *lo, struct bio *bio) * to avoid blocking in our make_request_fn. it also does loop decrypting * on reads for block backed loop, as that is too heavy to do from * b_end_io context where irqs may be disabled. + * + * Loop explanation: loop_clr_fd() sets lo_state to Lo_rundown before + * calling kthread_stop(). Therefore once kthread_should_stop() is + * true, make_request will not place any more requests. Therefore + * once kthread_should_stop() is true and lo_bio is NULL, we are + * done with the loop. */ static int loop_thread(void *data) { struct loop_device *lo = data; struct bio *bio; - daemonize("loop%d", lo->lo_number); - /* * loop can be used in an encrypted device, * hence, it mustn't be stopped at all @@ -587,47 +590,21 @@ static int loop_thread(void *data) set_user_nice(current, -20); - lo->lo_state = Lo_bound; - lo->lo_pending = 1; + while (!kthread_should_stop() || lo->lo_bio) { - /* - * complete it, we are running - */ - complete(&lo->lo_done); - - for (;;) { - int pending; + wait_event_interruptible(lo->lo_event, + lo->lo_bio || kthread_should_stop()); - if (wait_for_completion_interruptible(&lo->lo_bh_done)) + if (!lo->lo_bio) continue; - spin_lock_irq(&lo->lo_lock); - - /* - * could be completed because of tear-down, not pending work - */ - if (unlikely(!lo->lo_pending)) { - spin_unlock_irq(&lo->lo_lock); - break; - } - bio = loop_get_bio(lo); - lo->lo_pending--; - pending = lo->lo_pending; spin_unlock_irq(&lo->lo_lock); BUG_ON(!bio); loop_handle_bio(lo, bio); - - /* - * upped both for pending work and tear-down, lo_pending - * will hit zero then - */ - if (unlikely(!pending)) - break; } - complete(&lo->lo_done); return 0; } @@ -840,12 +817,26 @@ static int loop_set_fd(struct loop_device *lo, struct file *lo_file, set_blocksize(bdev, lo_blocksize); - error = kernel_thread(loop_thread, lo, CLONE_KERNEL); - if (error < 0) - goto out_putf; - wait_for_completion(&lo->lo_done); + lo->lo_thread = kthread_create(loop_thread, lo, "loop%d", + lo->lo_number); + if (IS_ERR(lo->lo_thread)) { + error = PTR_ERR(lo->lo_thread); + goto out_clr; + } + lo->lo_state = Lo_bound; + wake_up_process(lo->lo_thread); return 0; +out_clr: + lo->lo_thread = NULL; + lo->lo_device = NULL; + lo->lo_backing_file = NULL; + lo->lo_flags = 0; + set_capacity(disks[lo->lo_number], 0); + invalidate_bdev(bdev, 0); + bd_set_size(bdev, 0); + mapping_set_gfp_mask(mapping, lo->old_gfp_mask); + lo->lo_state = Lo_unbound; out_putf: fput(file); out: @@ -907,12 +898,9 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) spin_lock_irq(&lo->lo_lock); lo->lo_state = Lo_rundown; - lo->lo_pending--; - if (!lo->lo_pending) - complete(&lo->lo_bh_done); spin_unlock_irq(&lo->lo_lock); - wait_for_completion(&lo->lo_done); + kthread_stop(lo->lo_thread); lo->lo_backing_file = NULL; @@ -925,6 +913,7 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) lo->lo_sizelimit = 0; lo->lo_encrypt_key_size = 0; lo->lo_flags = 0; + lo->lo_thread = NULL; memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE); memset(lo->lo_crypt_name, 0, LO_NAME_SIZE); memset(lo->lo_file_name, 0, LO_NAME_SIZE); @@ -1177,6 +1166,162 @@ static int lo_ioctl(struct inode * inode, struct file * file, return err; } +#ifdef CONFIG_COMPAT +struct compat_loop_info { + compat_int_t lo_number; /* ioctl r/o */ + compat_dev_t lo_device; /* ioctl r/o */ + compat_ulong_t lo_inode; /* ioctl r/o */ + compat_dev_t lo_rdevice; /* ioctl r/o */ + compat_int_t lo_offset; + compat_int_t lo_encrypt_type; + compat_int_t lo_encrypt_key_size; /* ioctl w/o */ + compat_int_t lo_flags; /* ioctl r/o */ + char lo_name[LO_NAME_SIZE]; + unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ + compat_ulong_t lo_init[2]; + char reserved[4]; +}; + +/* + * Transfer 32-bit compatibility structure in userspace to 64-bit loop info + * - noinlined to reduce stack space usage in main part of driver + */ +static noinline int +loop_info64_from_compat(const struct compat_loop_info *arg, + struct loop_info64 *info64) +{ + struct compat_loop_info info; + + if (copy_from_user(&info, arg, sizeof(info))) + return -EFAULT; + + memset(info64, 0, sizeof(*info64)); + info64->lo_number = info.lo_number; + info64->lo_device = info.lo_device; + info64->lo_inode = info.lo_inode; + info64->lo_rdevice = info.lo_rdevice; + info64->lo_offset = info.lo_offset; + info64->lo_sizelimit = 0; + info64->lo_encrypt_type = info.lo_encrypt_type; + info64->lo_encrypt_key_size = info.lo_encrypt_key_size; + info64->lo_flags = info.lo_flags; + info64->lo_init[0] = info.lo_init[0]; + info64->lo_init[1] = info.lo_init[1]; + if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI) + memcpy(info64->lo_crypt_name, info.lo_name, LO_NAME_SIZE); + else + memcpy(info64->lo_file_name, info.lo_name, LO_NAME_SIZE); + memcpy(info64->lo_encrypt_key, info.lo_encrypt_key, LO_KEY_SIZE); + return 0; +} + +/* + * Transfer 64-bit loop info to 32-bit compatibility structure in userspace + * - noinlined to reduce stack space usage in main part of driver + */ +static noinline int +loop_info64_to_compat(const struct loop_info64 *info64, + struct compat_loop_info __user *arg) +{ + struct compat_loop_info info; + + memset(&info, 0, sizeof(info)); + info.lo_number = info64->lo_number; + info.lo_device = info64->lo_device; + info.lo_inode = info64->lo_inode; + info.lo_rdevice = info64->lo_rdevice; + info.lo_offset = info64->lo_offset; + info.lo_encrypt_type = info64->lo_encrypt_type; + info.lo_encrypt_key_size = info64->lo_encrypt_key_size; + info.lo_flags = info64->lo_flags; + info.lo_init[0] = info64->lo_init[0]; + info.lo_init[1] = info64->lo_init[1]; + if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI) + memcpy(info.lo_name, info64->lo_crypt_name, LO_NAME_SIZE); + else + memcpy(info.lo_name, info64->lo_file_name, LO_NAME_SIZE); + memcpy(info.lo_encrypt_key, info64->lo_encrypt_key, LO_KEY_SIZE); + + /* error in case values were truncated */ + if (info.lo_device != info64->lo_device || + info.lo_rdevice != info64->lo_rdevice || + info.lo_inode != info64->lo_inode || + info.lo_offset != info64->lo_offset || + info.lo_init[0] != info64->lo_init[0] || + info.lo_init[1] != info64->lo_init[1]) + return -EOVERFLOW; + + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int +loop_set_status_compat(struct loop_device *lo, + const struct compat_loop_info __user *arg) +{ + struct loop_info64 info64; + int ret; + + ret = loop_info64_from_compat(arg, &info64); + if (ret < 0) + return ret; + return loop_set_status(lo, &info64); +} + +static int +loop_get_status_compat(struct loop_device *lo, + struct compat_loop_info __user *arg) +{ + struct loop_info64 info64; + int err = 0; + + if (!arg) + err = -EINVAL; + if (!err) + err = loop_get_status(lo, &info64); + if (!err) + err = loop_info64_to_compat(&info64, arg); + return err; +} + +static long lo_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct inode *inode = file->f_dentry->d_inode; + struct loop_device *lo = inode->i_bdev->bd_disk->private_data; + int err; + + lock_kernel(); + switch(cmd) { + case LOOP_SET_STATUS: + mutex_lock(&lo->lo_ctl_mutex); + err = loop_set_status_compat( + lo, (const struct compat_loop_info __user *) arg); + mutex_unlock(&lo->lo_ctl_mutex); + break; + case LOOP_GET_STATUS: + mutex_lock(&lo->lo_ctl_mutex); + err = loop_get_status_compat( + lo, (struct compat_loop_info __user *) arg); + mutex_unlock(&lo->lo_ctl_mutex); + break; + case LOOP_CLR_FD: + case LOOP_GET_STATUS64: + case LOOP_SET_STATUS64: + arg = (unsigned long) compat_ptr(arg); + case LOOP_SET_FD: + case LOOP_CHANGE_FD: + err = lo_ioctl(inode, file, cmd, arg); + break; + default: + err = -ENOIOCTLCMD; + break; + } + unlock_kernel(); + return err; +} +#endif + static int lo_open(struct inode *inode, struct file *file) { struct loop_device *lo = inode->i_bdev->bd_disk->private_data; @@ -1204,6 +1349,9 @@ static struct block_device_operations lo_fops = { .open = lo_open, .release = lo_release, .ioctl = lo_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = lo_compat_ioctl, +#endif }; /* @@ -1287,9 +1435,9 @@ static int __init loop_init(void) if (!lo->lo_queue) goto out_mem4; mutex_init(&lo->lo_ctl_mutex); - init_completion(&lo->lo_done); - init_completion(&lo->lo_bh_done); lo->lo_number = i; + lo->lo_thread = NULL; + init_waitqueue_head(&lo->lo_event); spin_lock_init(&lo->lo_lock); disk->major = LOOP_MAJOR; disk->first_minor = i; diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index bdbade9a5cf..9d1035e8d9d 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -407,10 +407,10 @@ static void do_nbd_request(request_queue_t * q) struct nbd_device *lo; blkdev_dequeue_request(req); - dprintk(DBG_BLKDEV, "%s: request %p: dequeued (flags=%lx)\n", - req->rq_disk->disk_name, req, req->flags); + dprintk(DBG_BLKDEV, "%s: request %p: dequeued (flags=%x)\n", + req->rq_disk->disk_name, req, req->cmd_type); - if (!(req->flags & REQ_CMD)) + if (!blk_fs_request(req)) goto error_out; lo = req->rq_disk->private_data; @@ -489,7 +489,7 @@ static int nbd_ioctl(struct inode *inode, struct file *file, switch (cmd) { case NBD_DISCONNECT: printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name); - sreq.flags = REQ_SPECIAL; + sreq.cmd_type = REQ_TYPE_SPECIAL; nbd_cmd(&sreq) = NBD_CMD_DISC; /* * Set these to sane values in case server implementation diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c index 2403721f9db..38578b9dbfd 100644 --- a/drivers/block/paride/pd.c +++ b/drivers/block/paride/pd.c @@ -437,7 +437,7 @@ static char *pd_buf; /* buffer for request in progress */ static enum action do_pd_io_start(void) { - if (pd_req->flags & REQ_SPECIAL) { + if (blk_special_request(pd_req)) { phase = pd_special; return pd_special(); } @@ -719,14 +719,12 @@ static int pd_special_command(struct pd_unit *disk, memset(&rq, 0, sizeof(rq)); rq.errors = 0; - rq.rq_status = RQ_ACTIVE; rq.rq_disk = disk->gd; rq.ref_count = 1; - rq.waiting = &wait; + rq.end_io_data = &wait; rq.end_io = blk_end_sync_rq; blk_insert_request(disk->gd->queue, &rq, 0, func); wait_for_completion(&wait); - rq.waiting = NULL; if (rq.errors) err = -EIO; blk_put_request(&rq); diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 451b996bba9..888d1aceeef 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -365,17 +365,17 @@ static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command * rq->sense = sense; memset(sense, 0, sizeof(sense)); rq->sense_len = 0; - rq->flags |= REQ_BLOCK_PC | REQ_HARDBARRIER; + rq->cmd_type = REQ_TYPE_BLOCK_PC; + rq->cmd_flags |= REQ_HARDBARRIER; if (cgc->quiet) - rq->flags |= REQ_QUIET; + rq->cmd_flags |= REQ_QUIET; memcpy(rq->cmd, cgc->cmd, CDROM_PACKET_SIZE); if (sizeof(rq->cmd) > CDROM_PACKET_SIZE) memset(rq->cmd + CDROM_PACKET_SIZE, 0, sizeof(rq->cmd) - CDROM_PACKET_SIZE); rq->cmd_len = COMMAND_SIZE(rq->cmd[0]); rq->ref_count++; - rq->flags |= REQ_NOMERGE; - rq->waiting = &wait; + rq->end_io_data = &wait; rq->end_io = blk_end_sync_rq; elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1); generic_unplug_device(q); diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index cc42e762396..f2305ee792a 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -319,8 +319,8 @@ static void start_request(struct floppy_state *fs) printk("do_fd_req: dev=%s cmd=%d sec=%ld nr_sec=%ld buf=%p\n", req->rq_disk->disk_name, req->cmd, (long)req->sector, req->nr_sectors, req->buffer); - printk(" rq_status=%d errors=%d current_nr_sectors=%ld\n", - req->rq_status, req->errors, req->current_nr_sectors); + printk(" errors=%d current_nr_sectors=%ld\n", + req->errors, req->current_nr_sectors); #endif if (req->sector < 0 || req->sector >= fs->total_secs) { diff --git a/drivers/block/swim_iop.c b/drivers/block/swim_iop.c index 89e3c2f8b77..dfda796eba5 100644 --- a/drivers/block/swim_iop.c +++ b/drivers/block/swim_iop.c @@ -529,8 +529,8 @@ static void start_request(struct floppy_state *fs) printk("do_fd_req: dev=%s cmd=%d sec=%ld nr_sec=%ld buf=%p\n", CURRENT->rq_disk->disk_name, CURRENT->cmd, CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer); - printk(" rq_status=%d errors=%d current_nr_sectors=%ld\n", - CURRENT->rq_status, CURRENT->errors, CURRENT->current_nr_sectors); + printk(" errors=%d current_nr_sectors=%ld\n", + CURRENT->errors, CURRENT->current_nr_sectors); #endif if (CURRENT->sector < 0 || CURRENT->sector >= fs->total_secs) { diff --git a/drivers/block/xd.c b/drivers/block/xd.c index e828e4cbd3e..ebf3025721d 100644 --- a/drivers/block/xd.c +++ b/drivers/block/xd.c @@ -313,7 +313,7 @@ static void do_xd_request (request_queue_t * q) int res = 0; int retry; - if (!(req->flags & REQ_CMD)) { + if (!blk_fs_request(req)) { end_request(req, 0); continue; } diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 23f96213f4a..efcc28ec9d9 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -2,7 +2,7 @@ * * AVM BlueFRITZ! USB driver * - * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org> + * Copyright (C) 2003-2006 Marcel Holtmann <marcel@holtmann.org> * * * This program is free software; you can redistribute it and/or modify @@ -59,7 +59,6 @@ static struct usb_device_id bfusb_table[] = { MODULE_DEVICE_TABLE(usb, bfusb_table); - #define BFUSB_MAX_BLOCK_SIZE 256 #define BFUSB_BLOCK_TIMEOUT 3000 @@ -70,7 +69,7 @@ MODULE_DEVICE_TABLE(usb, bfusb_table); #define BFUSB_MAX_BULK_TX 2 #define BFUSB_MAX_BULK_RX 2 -struct bfusb { +struct bfusb_data { struct hci_dev *hdev; unsigned long state; @@ -92,137 +91,136 @@ struct bfusb { struct sk_buff_head completed_q; }; -struct bfusb_scb { +struct bfusb_data_scb { struct urb *urb; }; static void bfusb_tx_complete(struct urb *urb, struct pt_regs *regs); static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs); -static struct urb *bfusb_get_completed(struct bfusb *bfusb) +static struct urb *bfusb_get_completed(struct bfusb_data *data) { struct sk_buff *skb; struct urb *urb = NULL; - BT_DBG("bfusb %p", bfusb); + BT_DBG("bfusb %p", data); - skb = skb_dequeue(&bfusb->completed_q); + skb = skb_dequeue(&data->completed_q); if (skb) { - urb = ((struct bfusb_scb *) skb->cb)->urb; + urb = ((struct bfusb_data_scb *) skb->cb)->urb; kfree_skb(skb); } return urb; } -static void bfusb_unlink_urbs(struct bfusb *bfusb) +static void bfusb_unlink_urbs(struct bfusb_data *data) { struct sk_buff *skb; struct urb *urb; - BT_DBG("bfusb %p", bfusb); + BT_DBG("bfusb %p", data); - while ((skb = skb_dequeue(&bfusb->pending_q))) { - urb = ((struct bfusb_scb *) skb->cb)->urb; + while ((skb = skb_dequeue(&data->pending_q))) { + urb = ((struct bfusb_data_scb *) skb->cb)->urb; usb_kill_urb(urb); - skb_queue_tail(&bfusb->completed_q, skb); + skb_queue_tail(&data->completed_q, skb); } - while ((urb = bfusb_get_completed(bfusb))) + while ((urb = bfusb_get_completed(data))) usb_free_urb(urb); } - -static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb) +static int bfusb_send_bulk(struct bfusb_data *data, struct sk_buff *skb) { - struct bfusb_scb *scb = (void *) skb->cb; - struct urb *urb = bfusb_get_completed(bfusb); + struct bfusb_data_scb *scb = (void *) skb->cb; + struct urb *urb = bfusb_get_completed(data); int err, pipe; - BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len); + BT_DBG("bfusb %p skb %p len %d", data, skb, skb->len); if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC))) return -ENOMEM; - pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep); + pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep); - usb_fill_bulk_urb(urb, bfusb->udev, pipe, skb->data, skb->len, + usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len, bfusb_tx_complete, skb); scb->urb = urb; - skb_queue_tail(&bfusb->pending_q, skb); + skb_queue_tail(&data->pending_q, skb); err = usb_submit_urb(urb, GFP_ATOMIC); if (err) { BT_ERR("%s bulk tx submit failed urb %p err %d", - bfusb->hdev->name, urb, err); - skb_unlink(skb, &bfusb->pending_q); + data->hdev->name, urb, err); + skb_unlink(skb, &data->pending_q); usb_free_urb(urb); } else - atomic_inc(&bfusb->pending_tx); + atomic_inc(&data->pending_tx); return err; } -static void bfusb_tx_wakeup(struct bfusb *bfusb) +static void bfusb_tx_wakeup(struct bfusb_data *data) { struct sk_buff *skb; - BT_DBG("bfusb %p", bfusb); + BT_DBG("bfusb %p", data); - if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) { - set_bit(BFUSB_TX_WAKEUP, &bfusb->state); + if (test_and_set_bit(BFUSB_TX_PROCESS, &data->state)) { + set_bit(BFUSB_TX_WAKEUP, &data->state); return; } do { - clear_bit(BFUSB_TX_WAKEUP, &bfusb->state); + clear_bit(BFUSB_TX_WAKEUP, &data->state); - while ((atomic_read(&bfusb->pending_tx) < BFUSB_MAX_BULK_TX) && - (skb = skb_dequeue(&bfusb->transmit_q))) { - if (bfusb_send_bulk(bfusb, skb) < 0) { - skb_queue_head(&bfusb->transmit_q, skb); + while ((atomic_read(&data->pending_tx) < BFUSB_MAX_BULK_TX) && + (skb = skb_dequeue(&data->transmit_q))) { + if (bfusb_send_bulk(data, skb) < 0) { + skb_queue_head(&data->transmit_q, skb); break; } } - } while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state)); + } while (test_bit(BFUSB_TX_WAKEUP, &data->state)); - clear_bit(BFUSB_TX_PROCESS, &bfusb->state); + clear_bit(BFUSB_TX_PROCESS, &data->state); } static void bfusb_tx_complete(struct urb *urb, struct pt_regs *regs) { struct sk_buff *skb = (struct sk_buff *) urb->context; - struct bfusb *bfusb = (struct bfusb *) skb->dev; + struct bfusb_data *data = (struct bfusb_data *) skb->dev; - BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len); + BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len); - atomic_dec(&bfusb->pending_tx); + atomic_dec(&data->pending_tx); - if (!test_bit(HCI_RUNNING, &bfusb->hdev->flags)) + if (!test_bit(HCI_RUNNING, &data->hdev->flags)) return; if (!urb->status) - bfusb->hdev->stat.byte_tx += skb->len; + data->hdev->stat.byte_tx += skb->len; else - bfusb->hdev->stat.err_tx++; + data->hdev->stat.err_tx++; - read_lock(&bfusb->lock); + read_lock(&data->lock); - skb_unlink(skb, &bfusb->pending_q); - skb_queue_tail(&bfusb->completed_q, skb); + skb_unlink(skb, &data->pending_q); + skb_queue_tail(&data->completed_q, skb); - bfusb_tx_wakeup(bfusb); + bfusb_tx_wakeup(data); - read_unlock(&bfusb->lock); + read_unlock(&data->lock); } -static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb) +static int bfusb_rx_submit(struct bfusb_data *data, struct urb *urb) { - struct bfusb_scb *scb; + struct bfusb_data_scb *scb; struct sk_buff *skb; int err, pipe, size = HCI_MAX_FRAME_SIZE + 32; @@ -231,28 +229,29 @@ static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb) if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC))) return -ENOMEM; - if (!(skb = bt_skb_alloc(size, GFP_ATOMIC))) { + skb = bt_skb_alloc(size, GFP_ATOMIC); + if (!skb) { usb_free_urb(urb); return -ENOMEM; } - skb->dev = (void *) bfusb; + skb->dev = (void *) data; - scb = (struct bfusb_scb *) skb->cb; + scb = (struct bfusb_data_scb *) skb->cb; scb->urb = urb; - pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep); + pipe = usb_rcvbulkpipe(data->udev, data->bulk_in_ep); - usb_fill_bulk_urb(urb, bfusb->udev, pipe, skb->data, size, + usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, size, bfusb_rx_complete, skb); - skb_queue_tail(&bfusb->pending_q, skb); + skb_queue_tail(&data->pending_q, skb); err = usb_submit_urb(urb, GFP_ATOMIC); if (err) { BT_ERR("%s bulk rx submit failed urb %p err %d", - bfusb->hdev->name, urb, err); - skb_unlink(skb, &bfusb->pending_q); + data->hdev->name, urb, err); + skb_unlink(skb, &data->pending_q); kfree_skb(skb); usb_free_urb(urb); } @@ -260,15 +259,15 @@ static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb) return err; } -static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *data, int len) +static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned char *buf, int len) { - BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len); + BT_DBG("bfusb %p hdr 0x%02x data %p len %d", data, hdr, buf, len); if (hdr & 0x10) { - BT_ERR("%s error in block", bfusb->hdev->name); - if (bfusb->reassembly) - kfree_skb(bfusb->reassembly); - bfusb->reassembly = NULL; + BT_ERR("%s error in block", data->hdev->name); + if (data->reassembly) + kfree_skb(data->reassembly); + data->reassembly = NULL; return -EIO; } @@ -277,46 +276,46 @@ static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char * unsigned char pkt_type; int pkt_len = 0; - if (bfusb->reassembly) { - BT_ERR("%s unexpected start block", bfusb->hdev->name); - kfree_skb(bfusb->reassembly); - bfusb->reassembly = NULL; + if (data->reassembly) { + BT_ERR("%s unexpected start block", data->hdev->name); + kfree_skb(data->reassembly); + data->reassembly = NULL; } if (len < 1) { - BT_ERR("%s no packet type found", bfusb->hdev->name); + BT_ERR("%s no packet type found", data->hdev->name); return -EPROTO; } - pkt_type = *data++; len--; + pkt_type = *buf++; len--; switch (pkt_type) { case HCI_EVENT_PKT: if (len >= HCI_EVENT_HDR_SIZE) { - struct hci_event_hdr *hdr = (struct hci_event_hdr *) data; + struct hci_event_hdr *hdr = (struct hci_event_hdr *) buf; pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen; } else { - BT_ERR("%s event block is too short", bfusb->hdev->name); + BT_ERR("%s event block is too short", data->hdev->name); return -EILSEQ; } break; case HCI_ACLDATA_PKT: if (len >= HCI_ACL_HDR_SIZE) { - struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) data; + struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) buf; pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen); } else { - BT_ERR("%s data block is too short", bfusb->hdev->name); + BT_ERR("%s data block is too short", data->hdev->name); return -EILSEQ; } break; case HCI_SCODATA_PKT: if (len >= HCI_SCO_HDR_SIZE) { - struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) data; + struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) buf; pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen; } else { - BT_ERR("%s audio block is too short", bfusb->hdev->name); + BT_ERR("%s audio block is too short", data->hdev->name); return -EILSEQ; } break; @@ -324,27 +323,27 @@ static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char * skb = bt_skb_alloc(pkt_len, GFP_ATOMIC); if (!skb) { - BT_ERR("%s no memory for the packet", bfusb->hdev->name); + BT_ERR("%s no memory for the packet", data->hdev->name); return -ENOMEM; } - skb->dev = (void *) bfusb->hdev; + skb->dev = (void *) data->hdev; bt_cb(skb)->pkt_type = pkt_type; - bfusb->reassembly = skb; + data->reassembly = skb; } else { - if (!bfusb->reassembly) { - BT_ERR("%s unexpected continuation block", bfusb->hdev->name); + if (!data->reassembly) { + BT_ERR("%s unexpected continuation block", data->hdev->name); return -EIO; } } if (len > 0) - memcpy(skb_put(bfusb->reassembly, len), data, len); + memcpy(skb_put(data->reassembly, len), buf, len); if (hdr & 0x08) { - hci_recv_frame(bfusb->reassembly); - bfusb->reassembly = NULL; + hci_recv_frame(data->reassembly); + data->reassembly = NULL; } return 0; @@ -353,22 +352,22 @@ static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char * static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs) { struct sk_buff *skb = (struct sk_buff *) urb->context; - struct bfusb *bfusb = (struct bfusb *) skb->dev; + struct bfusb_data *data = (struct bfusb_data *) skb->dev; unsigned char *buf = urb->transfer_buffer; int count = urb->actual_length; int err, hdr, len; BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len); - read_lock(&bfusb->lock); + read_lock(&data->lock); - if (!test_bit(HCI_RUNNING, &bfusb->hdev->flags)) + if (!test_bit(HCI_RUNNING, &data->hdev->flags)) goto unlock; if (urb->status || !count) goto resubmit; - bfusb->hdev->stat.byte_rx += count; + data->hdev->stat.byte_rx += count; skb_put(skb, count); @@ -387,90 +386,89 @@ static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs) if (count < len) { BT_ERR("%s block extends over URB buffer ranges", - bfusb->hdev->name); + data->hdev->name); } if ((hdr & 0xe1) == 0xc1) - bfusb_recv_block(bfusb, hdr, buf, len); + bfusb_recv_block(data, hdr, buf, len); count -= len; buf += len; } - skb_unlink(skb, &bfusb->pending_q); + skb_unlink(skb, &data->pending_q); kfree_skb(skb); - bfusb_rx_submit(bfusb, urb); + bfusb_rx_submit(data, urb); - read_unlock(&bfusb->lock); + read_unlock(&data->lock); return; resubmit: - urb->dev = bfusb->udev; + urb->dev = data->udev; err = usb_submit_urb(urb, GFP_ATOMIC); if (err) { BT_ERR("%s bulk resubmit failed urb %p err %d", - bfusb->hdev->name, urb, err); + data->hdev->name, urb, err); } unlock: - read_unlock(&bfusb->lock); + read_unlock(&data->lock); } - static int bfusb_open(struct hci_dev *hdev) { - struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; + struct bfusb_data *data = hdev->driver_data; unsigned long flags; int i, err; - BT_DBG("hdev %p bfusb %p", hdev, bfusb); + BT_DBG("hdev %p bfusb %p", hdev, data); if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) return 0; - write_lock_irqsave(&bfusb->lock, flags); + write_lock_irqsave(&data->lock, flags); - err = bfusb_rx_submit(bfusb, NULL); + err = bfusb_rx_submit(data, NULL); if (!err) { for (i = 1; i < BFUSB_MAX_BULK_RX; i++) - bfusb_rx_submit(bfusb, NULL); + bfusb_rx_submit(data, NULL); } else { clear_bit(HCI_RUNNING, &hdev->flags); } - write_unlock_irqrestore(&bfusb->lock, flags); + write_unlock_irqrestore(&data->lock, flags); return err; } static int bfusb_flush(struct hci_dev *hdev) { - struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; + struct bfusb_data *data = hdev->driver_data; - BT_DBG("hdev %p bfusb %p", hdev, bfusb); + BT_DBG("hdev %p bfusb %p", hdev, data); - skb_queue_purge(&bfusb->transmit_q); + skb_queue_purge(&data->transmit_q); return 0; } static int bfusb_close(struct hci_dev *hdev) { - struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; + struct bfusb_data *data = hdev->driver_data; unsigned long flags; - BT_DBG("hdev %p bfusb %p", hdev, bfusb); + BT_DBG("hdev %p bfusb %p", hdev, data); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; - write_lock_irqsave(&bfusb->lock, flags); - write_unlock_irqrestore(&bfusb->lock, flags); + write_lock_irqsave(&data->lock, flags); + write_unlock_irqrestore(&data->lock, flags); - bfusb_unlink_urbs(bfusb); + bfusb_unlink_urbs(data); bfusb_flush(hdev); return 0; @@ -479,7 +477,7 @@ static int bfusb_close(struct hci_dev *hdev) static int bfusb_send_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; - struct bfusb *bfusb; + struct bfusb_data *data; struct sk_buff *nskb; unsigned char buf[3]; int sent = 0, size, count; @@ -494,7 +492,7 @@ static int bfusb_send_frame(struct sk_buff *skb) if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; - bfusb = (struct bfusb *) hdev->driver_data; + data = hdev->driver_data; switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: @@ -514,12 +512,13 @@ static int bfusb_send_frame(struct sk_buff *skb) count = skb->len; /* Max HCI frame size seems to be 1511 + 1 */ - if (!(nskb = bt_skb_alloc(count + 32, GFP_ATOMIC))) { + nskb = bt_skb_alloc(count + 32, GFP_ATOMIC); + if (!nskb) { BT_ERR("Can't allocate memory for new packet"); return -ENOMEM; } - nskb->dev = (void *) bfusb; + nskb->dev = (void *) data; while (count) { size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE); @@ -536,18 +535,18 @@ static int bfusb_send_frame(struct sk_buff *skb) } /* Don't send frame with multiple size of bulk max packet */ - if ((nskb->len % bfusb->bulk_pkt_size) == 0) { + if ((nskb->len % data->bulk_pkt_size) == 0) { buf[0] = 0xdd; buf[1] = 0x00; memcpy(skb_put(nskb, 2), buf, 2); } - read_lock(&bfusb->lock); + read_lock(&data->lock); - skb_queue_tail(&bfusb->transmit_q, nskb); - bfusb_tx_wakeup(bfusb); + skb_queue_tail(&data->transmit_q, nskb); + bfusb_tx_wakeup(data); - read_unlock(&bfusb->lock); + read_unlock(&data->lock); kfree_skb(skb); @@ -556,11 +555,11 @@ static int bfusb_send_frame(struct sk_buff *skb) static void bfusb_destruct(struct hci_dev *hdev) { - struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; + struct bfusb_data *data = hdev->driver_data; - BT_DBG("hdev %p bfusb %p", hdev, bfusb); + BT_DBG("hdev %p bfusb %p", hdev, data); - kfree(bfusb); + kfree(data); } static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) @@ -568,25 +567,24 @@ static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg return -ENOIOCTLCMD; } - -static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count) +static int bfusb_load_firmware(struct bfusb_data *data, unsigned char *firmware, int count) { unsigned char *buf; int err, pipe, len, size, sent = 0; - BT_DBG("bfusb %p udev %p", bfusb, bfusb->udev); + BT_DBG("bfusb %p udev %p", data, data->udev); BT_INFO("BlueFRITZ! USB loading firmware"); - pipe = usb_sndctrlpipe(bfusb->udev, 0); + pipe = usb_sndctrlpipe(data->udev, 0); - if (usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION, + if (usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION, 0, 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT) < 0) { BT_ERR("Can't change to loading configuration"); return -EBUSY; } - bfusb->udev->toggle[0] = bfusb->udev->toggle[1] = 0; + data->udev->toggle[0] = data->udev->toggle[1] = 0; buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC); if (!buf) { @@ -594,14 +592,14 @@ static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int return -ENOMEM; } - pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep); + pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep); while (count) { size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3); memcpy(buf, firmware + sent, size); - err = usb_bulk_msg(bfusb->udev, pipe, buf, size, + err = usb_bulk_msg(data->udev, pipe, buf, size, &len, BFUSB_BLOCK_TIMEOUT); if (err || (len != size)) { @@ -613,21 +611,23 @@ static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count -= size; } - if ((err = usb_bulk_msg(bfusb->udev, pipe, NULL, 0, - &len, BFUSB_BLOCK_TIMEOUT)) < 0) { + err = usb_bulk_msg(data->udev, pipe, NULL, 0, + &len, BFUSB_BLOCK_TIMEOUT); + if (err < 0) { BT_ERR("Error in null packet request"); goto error; } - pipe = usb_sndctrlpipe(bfusb->udev, 0); + pipe = usb_sndctrlpipe(data->udev, 0); - if ((err = usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION, - 0, 2, 0, NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0) { + err = usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION, + 0, 2, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + if (err < 0) { BT_ERR("Can't change to running configuration"); goto error; } - bfusb->udev->toggle[0] = bfusb->udev->toggle[1] = 0; + data->udev->toggle[0] = data->udev->toggle[1] = 0; BT_INFO("BlueFRITZ! USB device ready"); @@ -637,9 +637,9 @@ static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int error: kfree(buf); - pipe = usb_sndctrlpipe(bfusb->udev, 0); + pipe = usb_sndctrlpipe(data->udev, 0); - usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION, + usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION, 0, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); return err; @@ -652,7 +652,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i struct usb_host_endpoint *bulk_out_ep; struct usb_host_endpoint *bulk_in_ep; struct hci_dev *hdev; - struct bfusb *bfusb; + struct bfusb_data *data; BT_DBG("intf %p id %p", intf, id); @@ -672,23 +672,24 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i } /* Initialize control structure and load firmware */ - if (!(bfusb = kzalloc(sizeof(struct bfusb), GFP_KERNEL))) { + data = kzalloc(sizeof(struct bfusb_data), GFP_KERNEL); + if (!data) { BT_ERR("Can't allocate memory for control structure"); goto done; } - bfusb->udev = udev; - bfusb->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress; - bfusb->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress; - bfusb->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize); + data->udev = udev; + data->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress; + data->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress; + data->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize); - rwlock_init(&bfusb->lock); + rwlock_init(&data->lock); - bfusb->reassembly = NULL; + data->reassembly = NULL; - skb_queue_head_init(&bfusb->transmit_q); - skb_queue_head_init(&bfusb->pending_q); - skb_queue_head_init(&bfusb->completed_q); + skb_queue_head_init(&data->transmit_q); + skb_queue_head_init(&data->pending_q); + skb_queue_head_init(&data->completed_q); if (request_firmware(&firmware, "bfubase.frm", &udev->dev) < 0) { BT_ERR("Firmware request failed"); @@ -697,7 +698,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i BT_DBG("firmware data %p size %d", firmware->data, firmware->size); - if (bfusb_load_firmware(bfusb, firmware->data, firmware->size) < 0) { + if (bfusb_load_firmware(data, firmware->data, firmware->size) < 0) { BT_ERR("Firmware loading failed"); goto release; } @@ -711,10 +712,10 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i goto error; } - bfusb->hdev = hdev; + data->hdev = hdev; hdev->type = HCI_USB; - hdev->driver_data = bfusb; + hdev->driver_data = data; SET_HCIDEV_DEV(hdev, &intf->dev); hdev->open = bfusb_open; @@ -732,7 +733,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i goto error; } - usb_set_intfdata(intf, bfusb); + usb_set_intfdata(intf, data); return 0; @@ -740,7 +741,7 @@ release: release_firmware(firmware); error: - kfree(bfusb); + kfree(data); done: return -EIO; @@ -748,8 +749,8 @@ done: static void bfusb_disconnect(struct usb_interface *intf) { - struct bfusb *bfusb = usb_get_intfdata(intf); - struct hci_dev *hdev = bfusb->hdev; + struct bfusb_data *data = usb_get_intfdata(intf); + struct hci_dev *hdev = data->hdev; BT_DBG("intf %p", intf); @@ -779,7 +780,8 @@ static int __init bfusb_init(void) BT_INFO("BlueFRITZ! USB driver ver %s", VERSION); - if ((err = usb_register(&bfusb_driver)) < 0) + err = usb_register(&bfusb_driver); + if (err < 0) BT_ERR("Failed to register BlueFRITZ! USB driver"); return err; diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 93ba25b7ea3..420b645c4c9 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -241,15 +241,11 @@ static int hci_uart_send_frame(struct sk_buff *skb) static void hci_uart_destruct(struct hci_dev *hdev) { - struct hci_uart *hu; - if (!hdev) return; BT_DBG("%s", hdev->name); - - hu = (struct hci_uart *) hdev->driver_data; - kfree(hu); + kfree(hdev->driver_data); } /* ------ LDISC part ------ */ @@ -272,7 +268,7 @@ static int hci_uart_tty_open(struct tty_struct *tty) return -EEXIST; if (!(hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL))) { - BT_ERR("Can't allocate controll structure"); + BT_ERR("Can't allocate control structure"); return -ENFILE; } @@ -360,7 +356,7 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty) * * Return Value: None */ -static void hci_uart_tty_receive(struct tty_struct *tty, const __u8 *data, char *flags, int count) +static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count) { struct hci_uart *hu = (void *)tty->disc_data; @@ -375,7 +371,8 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const __u8 *data, char hu->hdev->stat.byte_rx += count; spin_unlock(&hu->rx_lock); - if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver->unthrottle) + if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && + tty->driver->unthrottle) tty->driver->unthrottle(tty); } diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c index e2d4beac742..0801af4ad2b 100644 --- a/drivers/bluetooth/hci_usb.c +++ b/drivers/bluetooth/hci_usb.c @@ -96,6 +96,9 @@ static struct usb_device_id bluetooth_ids[] = { /* Ericsson with non-standard id */ { USB_DEVICE(0x0bdb, 0x1002) }, + /* Canyon CN-BTU1 with HID interfaces */ + { USB_DEVICE(0x0c10, 0x0000), .driver_info = HCI_RESET }, + { } /* Terminating entry */ }; diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index aac67a3a601..a278d98a915 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -2,9 +2,9 @@ * * Bluetooth virtual HCI driver * - * Copyright (C) 2000-2001 Qualcomm Incorporated - * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> - * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org> + * Copyright (C) 2000-2001 Qualcomm Incorporated + * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> + * Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org> * * * This program is free software; you can redistribute it and/or modify @@ -72,21 +72,21 @@ static int vhci_open_dev(struct hci_dev *hdev) static int vhci_close_dev(struct hci_dev *hdev) { - struct vhci_data *vhci = hdev->driver_data; + struct vhci_data *data = hdev->driver_data; if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; - skb_queue_purge(&vhci->readq); + skb_queue_purge(&data->readq); return 0; } static int vhci_flush(struct hci_dev *hdev) { - struct vhci_data *vhci = hdev->driver_data; + struct vhci_data *data = hdev->driver_data; - skb_queue_purge(&vhci->readq); + skb_queue_purge(&data->readq); return 0; } @@ -94,7 +94,7 @@ static int vhci_flush(struct hci_dev *hdev) static int vhci_send_frame(struct sk_buff *skb) { struct hci_dev* hdev = (struct hci_dev *) skb->dev; - struct vhci_data *vhci; + struct vhci_data *data; if (!hdev) { BT_ERR("Frame for unknown HCI device (hdev=NULL)"); @@ -104,15 +104,15 @@ static int vhci_send_frame(struct sk_buff *skb) if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; - vhci = hdev->driver_data; + data = hdev->driver_data; memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); - skb_queue_tail(&vhci->readq, skb); + skb_queue_tail(&data->readq, skb); - if (vhci->flags & VHCI_FASYNC) - kill_fasync(&vhci->fasync, SIGIO, POLL_IN); + if (data->flags & VHCI_FASYNC) + kill_fasync(&data->fasync, SIGIO, POLL_IN); - wake_up_interruptible(&vhci->read_wait); + wake_up_interruptible(&data->read_wait); return 0; } @@ -122,7 +122,7 @@ static void vhci_destruct(struct hci_dev *hdev) kfree(hdev->driver_data); } -static inline ssize_t vhci_get_user(struct vhci_data *vhci, +static inline ssize_t vhci_get_user(struct vhci_data *data, const char __user *buf, size_t count) { struct sk_buff *skb; @@ -139,7 +139,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *vhci, return -EFAULT; } - skb->dev = (void *) vhci->hdev; + skb->dev = (void *) data->hdev; bt_cb(skb)->pkt_type = *((__u8 *) skb->data); skb_pull(skb, 1); @@ -148,7 +148,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *vhci, return count; } -static inline ssize_t vhci_put_user(struct vhci_data *vhci, +static inline ssize_t vhci_put_user(struct vhci_data *data, struct sk_buff *skb, char __user *buf, int count) { char __user *ptr = buf; @@ -161,42 +161,43 @@ static inline ssize_t vhci_put_user(struct vhci_data *vhci, total += len; - vhci->hdev->stat.byte_tx += len; + data->hdev->stat.byte_tx += len; switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: - vhci->hdev->stat.cmd_tx++; + data->hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: - vhci->hdev->stat.acl_tx++; + data->hdev->stat.acl_tx++; break; case HCI_SCODATA_PKT: - vhci->hdev->stat.cmd_tx++; + data->hdev->stat.cmd_tx++; break; }; return total; } -static loff_t vhci_llseek(struct file * file, loff_t offset, int origin) +static loff_t vhci_llseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; } -static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, loff_t *pos) +static ssize_t vhci_read(struct file *file, + char __user *buf, size_t count, loff_t *pos) { DECLARE_WAITQUEUE(wait, current); - struct vhci_data *vhci = file->private_data; + struct vhci_data *data = file->private_data; struct sk_buff *skb; ssize_t ret = 0; - add_wait_queue(&vhci->read_wait, &wait); + add_wait_queue(&data->read_wait, &wait); while (count) { set_current_state(TASK_INTERRUPTIBLE); - skb = skb_dequeue(&vhci->readq); + skb = skb_dequeue(&data->readq); if (!skb) { if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; @@ -213,7 +214,7 @@ static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, lo } if (access_ok(VERIFY_WRITE, buf, count)) - ret = vhci_put_user(vhci, skb, buf, count); + ret = vhci_put_user(data, skb, buf, count); else ret = -EFAULT; @@ -221,7 +222,7 @@ static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, lo break; } set_current_state(TASK_RUNNING); - remove_wait_queue(&vhci->read_wait, &wait); + remove_wait_queue(&data->read_wait, &wait); return ret; } @@ -229,21 +230,21 @@ static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, lo static ssize_t vhci_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { - struct vhci_data *vhci = file->private_data; + struct vhci_data *data = file->private_data; if (!access_ok(VERIFY_READ, buf, count)) return -EFAULT; - return vhci_get_user(vhci, buf, count); + return vhci_get_user(data, buf, count); } static unsigned int vhci_poll(struct file *file, poll_table *wait) { - struct vhci_data *vhci = file->private_data; + struct vhci_data *data = file->private_data; - poll_wait(file, &vhci->read_wait, wait); + poll_wait(file, &data->read_wait, wait); - if (!skb_queue_empty(&vhci->readq)) + if (!skb_queue_empty(&data->readq)) return POLLIN | POLLRDNORM; return POLLOUT | POLLWRNORM; @@ -257,26 +258,26 @@ static int vhci_ioctl(struct inode *inode, struct file *file, static int vhci_open(struct inode *inode, struct file *file) { - struct vhci_data *vhci; + struct vhci_data *data; struct hci_dev *hdev; - vhci = kzalloc(sizeof(struct vhci_data), GFP_KERNEL); - if (!vhci) + data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL); + if (!data) return -ENOMEM; - skb_queue_head_init(&vhci->readq); - init_waitqueue_head(&vhci->read_wait); + skb_queue_head_init(&data->readq); + init_waitqueue_head(&data->read_wait); hdev = hci_alloc_dev(); if (!hdev) { - kfree(vhci); + kfree(data); return -ENOMEM; } - vhci->hdev = hdev; + data->hdev = hdev; - hdev->type = HCI_VHCI; - hdev->driver_data = vhci; + hdev->type = HCI_VIRTUAL; + hdev->driver_data = data; hdev->open = vhci_open_dev; hdev->close = vhci_close_dev; @@ -288,20 +289,20 @@ static int vhci_open(struct inode *inode, struct file *file) if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); - kfree(vhci); + kfree(data); hci_free_dev(hdev); return -EBUSY; } - file->private_data = vhci; + file->private_data = data; return nonseekable_open(inode, file); } static int vhci_release(struct inode *inode, struct file *file) { - struct vhci_data *vhci = file->private_data; - struct hci_dev *hdev = vhci->hdev; + struct vhci_data *data = file->private_data; + struct hci_dev *hdev = data->hdev; if (hci_unregister_dev(hdev) < 0) { BT_ERR("Can't unregister HCI device %s", hdev->name); @@ -316,17 +317,17 @@ static int vhci_release(struct inode *inode, struct file *file) static int vhci_fasync(int fd, struct file *file, int on) { - struct vhci_data *vhci = file->private_data; + struct vhci_data *data = file->private_data; int err; - err = fasync_helper(fd, file, on, &vhci->fasync); + err = fasync_helper(fd, file, on, &data->fasync); if (err < 0) return err; if (on) - vhci->flags |= VHCI_FASYNC; + data->flags |= VHCI_FASYNC; else - vhci->flags &= ~VHCI_FASYNC; + data->flags &= ~VHCI_FASYNC; return 0; } diff --git a/drivers/cdrom/Kconfig b/drivers/cdrom/Kconfig index ff5652d4061..4b12e9031fb 100644 --- a/drivers/cdrom/Kconfig +++ b/drivers/cdrom/Kconfig @@ -3,7 +3,7 @@ # menu "Old CD-ROM drivers (not SCSI, not IDE)" - depends on ISA + depends on ISA && BLOCK config CD_NO_IDESCSI bool "Support non-SCSI/IDE/ATAPI CDROM drives" diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index d239cf8b20b..b38c84a7a8e 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -2129,7 +2129,7 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, rq->cmd[9] = 0xf8; rq->cmd_len = 12; - rq->flags |= REQ_BLOCK_PC; + rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->timeout = 60 * HZ; bio = rq->bio; diff --git a/drivers/cdrom/cdu31a.c b/drivers/cdrom/cdu31a.c index 37bdb0163f0..ccd91c1a84b 100644 --- a/drivers/cdrom/cdu31a.c +++ b/drivers/cdrom/cdu31a.c @@ -1338,8 +1338,10 @@ static void do_cdu31a_request(request_queue_t * q) } /* WTF??? */ - if (!(req->flags & REQ_CMD)) + if (!blk_fs_request(req)) { + end_request(req, 0); continue; + } if (rq_data_dir(req) == WRITE) { end_request(req, 0); continue; diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 1b21c3a911d..bde1c665d9f 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -831,14 +831,6 @@ config DS1302 will get access to the real time clock (or hardware clock) built into your computer. -config S3C2410_RTC - bool "S3C2410 RTC Driver" - depends on ARCH_S3C2410 - help - RTC (Realtime Clock) driver for the clock inbuilt into the - Samsung S3C2410. This can provide periodic interrupt rates - from 1Hz to 64Hz for user programs, and wakeup from Alarm. - config COBALT_LCD bool "Support for Cobalt LCD" depends on MIPS_COBALT @@ -1014,6 +1006,7 @@ config GPIO_VR41XX config RAW_DRIVER tristate "RAW driver (/dev/raw/rawN) (OBSOLETE)" + depends on BLOCK help The raw driver permits block devices to be bound to /dev/raw/rawN. Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O. diff --git a/drivers/char/Makefile b/drivers/char/Makefile index b583d0cd9fb..19114df59bb 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -69,7 +69,6 @@ obj-$(CONFIG_EFI_RTC) += efirtc.o obj-$(CONFIG_SGI_DS1286) += ds1286.o obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o obj-$(CONFIG_DS1302) += ds1302.o -obj-$(CONFIG_S3C2410_RTC) += s3c2410-rtc.o ifeq ($(CONFIG_GENERIC_NVRAM),y) obj-$(CONFIG_NVRAM) += generic_nvram.o else diff --git a/drivers/char/drm/Kconfig b/drivers/char/drm/Kconfig index 5278c388d3e..ef833a1c27e 100644 --- a/drivers/char/drm/Kconfig +++ b/drivers/char/drm/Kconfig @@ -60,7 +60,9 @@ config DRM_I830 Choose this option if you have a system that has Intel 830M, 845G, 852GM, 855GM or 865G integrated graphics. If M is selected, the module will be called i830. AGP support is required for this driver - to work. This driver will eventually be replaced by the i915 one. + to work. This driver is used by the older X releases X.org 6.7 and + XFree86 4.3. If unsure, build this and i915 as modules and the X server + will load the correct one. config DRM_I915 tristate "i915 driver" @@ -68,8 +70,9 @@ config DRM_I915 Choose this option if you have a system that has Intel 830M, 845G, 852GM, 855GM 865G or 915G integrated graphics. If M is selected, the module will be called i915. AGP support is required for this driver - to work. This driver will eventually replace the I830 driver, when - later release of X start to use the new DDX and DRI. + to work. This driver is used by the Intel driver in X.org 6.8 and + XFree86 4.4 and above. If unsure, build this and i830 as modules and + the X server will load the correct one. endchoice diff --git a/drivers/char/drm/Makefile b/drivers/char/drm/Makefile index 9d180c42816..3ad0f648c6b 100644 --- a/drivers/char/drm/Makefile +++ b/drivers/char/drm/Makefile @@ -6,7 +6,7 @@ drm-objs := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \ drm_drv.o drm_fops.o drm_ioctl.o drm_irq.o \ drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \ drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \ - drm_sysfs.o + drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o tdfx-objs := tdfx_drv.o r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o @@ -16,9 +16,9 @@ i830-objs := i830_drv.o i830_dma.o i830_irq.o i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o ffb-objs := ffb_drv.o ffb_context.o -sis-objs := sis_drv.o sis_ds.o sis_mm.o +sis-objs := sis_drv.o sis_mm.o savage-objs := savage_drv.o savage_bci.o savage_state.o -via-objs := via_irq.o via_drv.o via_ds.o via_map.o via_mm.o via_dma.o via_verifier.o via_video.o via_dmablit.o +via-objs := via_irq.o via_drv.o via_map.o via_mm.o via_dma.o via_verifier.o via_video.o via_dmablit.o ifeq ($(CONFIG_COMPAT),y) drm-objs += drm_ioc32.o diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index d2a56182bc3..7690a59ace0 100644 --- a/drivers/char/drm/drmP.h +++ b/drivers/char/drm/drmP.h @@ -79,6 +79,7 @@ #define __OS_HAS_MTRR (defined(CONFIG_MTRR)) #include "drm_os_linux.h" +#include "drm_hashtab.h" /***********************************************************************/ /** \name DRM template customization defaults */ @@ -104,7 +105,7 @@ #define DRM_DEBUG_CODE 2 /**< Include debugging code if > 1, then also include looping detection. */ -#define DRM_HASH_SIZE 16 /**< Size of key hash table. Must be power of 2. */ +#define DRM_MAGIC_HASH_ORDER 4 /**< Size of key hash table. Must be power of 2. */ #define DRM_KERNEL_CONTEXT 0 /**< Change drm_resctx if changed */ #define DRM_RESERVED_CONTEXTS 1 /**< Change drm_resctx if changed */ #define DRM_LOOPING_LIMIT 5000000 @@ -134,19 +135,12 @@ #define DRM_MEM_CTXBITMAP 18 #define DRM_MEM_STUB 19 #define DRM_MEM_SGLISTS 20 -#define DRM_MEM_CTXLIST 21 +#define DRM_MEM_CTXLIST 21 +#define DRM_MEM_MM 22 +#define DRM_MEM_HASHTAB 23 #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) - -/*@}*/ - -/***********************************************************************/ -/** \name Backward compatibility section */ -/*@{*/ - -#define DRM_RPR_ARG(vma) vma, - -#define VM_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT) +#define DRM_MAP_HASH_OFFSET 0x10000000 /*@}*/ @@ -211,8 +205,6 @@ /*@{*/ #define DRM_ARRAY_SIZE(x) ARRAY_SIZE(x) -#define DRM_MIN(a,b) min(a,b) -#define DRM_MAX(a,b) max(a,b) #define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1)) #define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x)) @@ -286,7 +278,8 @@ typedef struct drm_devstate { } drm_devstate_t; typedef struct drm_magic_entry { - drm_magic_t magic; + drm_hash_item_t hash_item; + struct list_head head; struct drm_file *priv; struct drm_magic_entry *next; } drm_magic_entry_t; @@ -493,6 +486,7 @@ typedef struct drm_sigdata { */ typedef struct drm_map_list { struct list_head head; /**< list head */ + drm_hash_item_t hash; drm_map_t *map; /**< mapping */ unsigned int user_token; } drm_map_list_t; @@ -527,6 +521,22 @@ typedef struct ati_pcigart_info { drm_local_map_t mapping; } drm_ati_pcigart_info; +/* + * Generic memory manager structs + */ +typedef struct drm_mm_node { + struct list_head fl_entry; + struct list_head ml_entry; + int free; + unsigned long start; + unsigned long size; + void *private; +} drm_mm_node_t; + +typedef struct drm_mm { + drm_mm_node_t root_node; +} drm_mm_t; + /** * DRM driver structure. This structure represent the common code for * a family of cards. There will one drm_device for each card present @@ -646,13 +656,15 @@ typedef struct drm_device { /*@{ */ drm_file_t *file_first; /**< file list head */ drm_file_t *file_last; /**< file list tail */ - drm_magic_head_t magiclist[DRM_HASH_SIZE]; /**< magic hash table */ + drm_open_hash_t magiclist; /**< magic hash table */ + struct list_head magicfree; /*@} */ /** \name Memory management */ /*@{ */ drm_map_list_t *maplist; /**< Linked list of regions */ int map_count; /**< Number of mappable regions */ + drm_open_hash_t map_hash; /**< User token hash table for maps */ /** \name Context handle management */ /*@{ */ @@ -711,10 +723,8 @@ typedef struct drm_device { drm_agp_head_t *agp; /**< AGP data */ struct pci_dev *pdev; /**< PCI device structure */ - int pci_domain; /**< PCI bus domain number */ - int pci_bus; /**< PCI bus number */ - int pci_slot; /**< PCI slot number */ - int pci_func; /**< PCI function number */ + int pci_vendor; /**< PCI vendor id */ + int pci_device; /**< PCI device id */ #ifdef __alpha__ struct pci_controller *hose; #endif @@ -736,6 +746,12 @@ static __inline__ int drm_core_check_feature(struct drm_device *dev, return ((dev->driver->driver_features & feature) ? 1 : 0); } +#ifdef __alpha__ +#define drm_get_pci_domain(dev) dev->hose->bus->number +#else +#define drm_get_pci_domain(dev) 0 +#endif + #if __OS_HAS_AGP static inline int drm_core_has_AGP(struct drm_device *dev) { @@ -1011,6 +1027,18 @@ extern struct class_device *drm_sysfs_device_add(struct class *cs, drm_head_t *head); extern void drm_sysfs_device_remove(struct class_device *class_dev); +/* + * Basic memory manager support (drm_mm.c) + */ +extern drm_mm_node_t *drm_mm_get_block(drm_mm_node_t * parent, + unsigned long size, + unsigned alignment); +extern void drm_mm_put_block(drm_mm_t *mm, drm_mm_node_t *cur); +extern drm_mm_node_t *drm_mm_search_free(const drm_mm_t *mm, unsigned long size, + unsigned alignment, int best_match); +extern int drm_mm_init(drm_mm_t *mm, unsigned long start, unsigned long size); +extern void drm_mm_takedown(drm_mm_t *mm); + /* Inline replacements for DRM_IOREMAP macros */ static __inline__ void drm_core_ioremap(struct drm_map *map, struct drm_device *dev) diff --git a/drivers/char/drm/drm_auth.c b/drivers/char/drm/drm_auth.c index 2a37586a7ee..c7b19d35bcd 100644 --- a/drivers/char/drm/drm_auth.c +++ b/drivers/char/drm/drm_auth.c @@ -36,20 +36,6 @@ #include "drmP.h" /** - * Generate a hash key from a magic. - * - * \param magic magic. - * \return hash key. - * - * The key is the modulus of the hash table size, #DRM_HASH_SIZE, which must be - * a power of 2. - */ -static int drm_hash_magic(drm_magic_t magic) -{ - return magic & (DRM_HASH_SIZE - 1); -} - -/** * Find the file with the given magic number. * * \param dev DRM device. @@ -63,14 +49,12 @@ static drm_file_t *drm_find_file(drm_device_t * dev, drm_magic_t magic) { drm_file_t *retval = NULL; drm_magic_entry_t *pt; - int hash = drm_hash_magic(magic); + drm_hash_item_t *hash; mutex_lock(&dev->struct_mutex); - for (pt = dev->magiclist[hash].head; pt; pt = pt->next) { - if (pt->magic == magic) { - retval = pt->priv; - break; - } + if (!drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) { + pt = drm_hash_entry(hash, drm_magic_entry_t, hash_item); + retval = pt->priv; } mutex_unlock(&dev->struct_mutex); return retval; @@ -90,28 +74,20 @@ static drm_file_t *drm_find_file(drm_device_t * dev, drm_magic_t magic) static int drm_add_magic(drm_device_t * dev, drm_file_t * priv, drm_magic_t magic) { - int hash; drm_magic_entry_t *entry; DRM_DEBUG("%d\n", magic); - hash = drm_hash_magic(magic); entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC); if (!entry) return -ENOMEM; memset(entry, 0, sizeof(*entry)); - entry->magic = magic; entry->priv = priv; - entry->next = NULL; + entry->hash_item.key = (unsigned long)magic; mutex_lock(&dev->struct_mutex); - if (dev->magiclist[hash].tail) { - dev->magiclist[hash].tail->next = entry; - dev->magiclist[hash].tail = entry; - } else { - dev->magiclist[hash].head = entry; - dev->magiclist[hash].tail = entry; - } + drm_ht_insert_item(&dev->magiclist, &entry->hash_item); + list_add_tail(&entry->head, &dev->magicfree); mutex_unlock(&dev->struct_mutex); return 0; @@ -128,34 +104,24 @@ static int drm_add_magic(drm_device_t * dev, drm_file_t * priv, */ static int drm_remove_magic(drm_device_t * dev, drm_magic_t magic) { - drm_magic_entry_t *prev = NULL; drm_magic_entry_t *pt; - int hash; + drm_hash_item_t *hash; DRM_DEBUG("%d\n", magic); - hash = drm_hash_magic(magic); mutex_lock(&dev->struct_mutex); - for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) { - if (pt->magic == magic) { - if (dev->magiclist[hash].head == pt) { - dev->magiclist[hash].head = pt->next; - } - if (dev->magiclist[hash].tail == pt) { - dev->magiclist[hash].tail = prev; - } - if (prev) { - prev->next = pt->next; - } - mutex_unlock(&dev->struct_mutex); - return 0; - } + if (drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) { + mutex_unlock(&dev->struct_mutex); + return -EINVAL; } + pt = drm_hash_entry(hash, drm_magic_entry_t, hash_item); + drm_ht_remove_item(&dev->magiclist, hash); + list_del(&pt->head); mutex_unlock(&dev->struct_mutex); drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); - return -EINVAL; + return 0; } /** diff --git a/drivers/char/drm/drm_bufs.c b/drivers/char/drm/drm_bufs.c index 006b06d2972..029baea33b6 100644 --- a/drivers/char/drm/drm_bufs.c +++ b/drivers/char/drm/drm_bufs.c @@ -65,43 +65,29 @@ static drm_map_list_t *drm_find_matching_map(drm_device_t *dev, return NULL; } -/* - * Used to allocate 32-bit handles for mappings. - */ -#define START_RANGE 0x10000000 -#define END_RANGE 0x40000000 - -#ifdef _LP64 -static __inline__ unsigned int HandleID(unsigned long lhandle, - drm_device_t *dev) +static int drm_map_handle(drm_device_t *dev, drm_hash_item_t *hash, + unsigned long user_token, int hashed_handle) { - static unsigned int map32_handle = START_RANGE; - unsigned int hash; - - if (lhandle & 0xffffffff00000000) { - hash = map32_handle; - map32_handle += PAGE_SIZE; - if (map32_handle > END_RANGE) - map32_handle = START_RANGE; - } else - hash = lhandle; - - while (1) { - drm_map_list_t *_entry; - list_for_each_entry(_entry, &dev->maplist->head, head) { - if (_entry->user_token == hash) - break; - } - if (&_entry->head == &dev->maplist->head) - return hash; + int use_hashed_handle; +#if (BITS_PER_LONG == 64) + use_hashed_handle = ((user_token & 0xFFFFFFFF00000000UL) || hashed_handle); +#elif (BITS_PER_LONG == 32) + use_hashed_handle = hashed_handle; +#else +#error Unsupported long size. Neither 64 nor 32 bits. +#endif - hash += PAGE_SIZE; - map32_handle += PAGE_SIZE; + if (!use_hashed_handle) { + int ret; + hash->key = user_token; + ret = drm_ht_insert_item(&dev->map_hash, hash); + if (ret != -EINVAL) + return ret; } + return drm_ht_just_insert_please(&dev->map_hash, hash, + user_token, 32 - PAGE_SHIFT - 3, + PAGE_SHIFT, DRM_MAP_HASH_OFFSET); } -#else -# define HandleID(x,dev) (unsigned int)(x) -#endif /** * Ioctl to specify a range of memory that is available for mapping by a non-root process. @@ -123,6 +109,8 @@ static int drm_addmap_core(drm_device_t * dev, unsigned int offset, drm_map_t *map; drm_map_list_t *list; drm_dma_handle_t *dmah; + unsigned long user_token; + int ret; map = drm_alloc(sizeof(*map), DRM_MEM_MAPS); if (!map) @@ -257,11 +245,20 @@ static int drm_addmap_core(drm_device_t * dev, unsigned int offset, mutex_lock(&dev->struct_mutex); list_add(&list->head, &dev->maplist->head); + /* Assign a 32-bit handle */ /* We do it here so that dev->struct_mutex protects the increment */ - list->user_token = HandleID(map->type == _DRM_SHM - ? (unsigned long)map->handle - : map->offset, dev); + user_token = (map->type == _DRM_SHM) ? (unsigned long)map->handle : + map->offset; + ret = drm_map_handle(dev, &list->hash, user_token, 0); + if (ret) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + drm_free(list, sizeof(*list), DRM_MEM_MAPS); + mutex_unlock(&dev->struct_mutex); + return ret; + } + + list->user_token = list->hash.key; mutex_unlock(&dev->struct_mutex); *maplist = list; @@ -346,6 +343,7 @@ int drm_rmmap_locked(drm_device_t *dev, drm_local_map_t *map) if (r_list->map == map) { list_del(list); + drm_ht_remove_key(&dev->map_hash, r_list->user_token); drm_free(list, sizeof(*list), DRM_MEM_MAPS); break; } @@ -441,8 +439,10 @@ int drm_rmmap_ioctl(struct inode *inode, struct file *filp, return -EINVAL; } - if (!map) + if (!map) { + mutex_unlock(&dev->struct_mutex); return -EINVAL; + } /* Register and framebuffer maps are permanent */ if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) { diff --git a/drivers/char/drm/drm_drv.c b/drivers/char/drm/drm_drv.c index 3c0b882a8e7..b366c5b1bd1 100644 --- a/drivers/char/drm/drm_drv.c +++ b/drivers/char/drm/drm_drv.c @@ -118,7 +118,7 @@ static drm_ioctl_desc_t drm_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)] = {drm_wait_vblank, 0}, }; -#define DRIVER_IOCTL_COUNT DRM_ARRAY_SIZE( drm_ioctls ) +#define DRIVER_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) /** * Take down the DRM device. @@ -155,12 +155,13 @@ int drm_lastclose(drm_device_t * dev) del_timer(&dev->timer); /* Clear pid list */ - for (i = 0; i < DRM_HASH_SIZE; i++) { - for (pt = dev->magiclist[i].head; pt; pt = next) { - next = pt->next; + if (dev->magicfree.next) { + list_for_each_entry_safe(pt, next, &dev->magicfree, head) { + list_del(&pt->head); + drm_ht_remove_item(&dev->magiclist, &pt->hash_item); drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); } - dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + drm_ht_remove(&dev->magiclist); } /* Clear AGP information */ @@ -299,6 +300,7 @@ static void drm_cleanup(drm_device_t * dev) if (dev->maplist) { drm_free(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS); dev->maplist = NULL; + drm_ht_remove(&dev->map_hash); } drm_ctxbitmap_cleanup(dev); diff --git a/drivers/char/drm/drm_fops.c b/drivers/char/drm/drm_fops.c index b7f7951c458..898f47dafec 100644 --- a/drivers/char/drm/drm_fops.c +++ b/drivers/char/drm/drm_fops.c @@ -53,6 +53,8 @@ static int drm_setup(drm_device_t * dev) return ret; } + dev->magicfree.next = NULL; + /* prebuild the SAREA */ i = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM, _DRM_CONTAINS_LOCK, &map); if (i != 0) @@ -69,13 +71,11 @@ static int drm_setup(drm_device_t * dev) return i; } - for (i = 0; i < DRM_ARRAY_SIZE(dev->counts); i++) + for (i = 0; i < ARRAY_SIZE(dev->counts); i++) atomic_set(&dev->counts[i], 0); - for (i = 0; i < DRM_HASH_SIZE; i++) { - dev->magiclist[i].head = NULL; - dev->magiclist[i].tail = NULL; - } + drm_ht_create(&dev->magiclist, DRM_MAGIC_HASH_ORDER); + INIT_LIST_HEAD(&dev->magicfree); dev->ctxlist = drm_alloc(sizeof(*dev->ctxlist), DRM_MEM_CTXLIST); if (dev->ctxlist == NULL) diff --git a/drivers/char/drm/drm_hashtab.c b/drivers/char/drm/drm_hashtab.c new file mode 100644 index 00000000000..a0b2d6802ae --- /dev/null +++ b/drivers/char/drm/drm_hashtab.c @@ -0,0 +1,190 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ +/* + * Simple open hash tab implementation. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drmP.h" +#include "drm_hashtab.h" +#include <linux/hash.h> + +int drm_ht_create(drm_open_hash_t *ht, unsigned int order) +{ + unsigned int i; + + ht->size = 1 << order; + ht->order = order; + ht->fill = 0; + ht->table = vmalloc(ht->size*sizeof(*ht->table)); + if (!ht->table) { + DRM_ERROR("Out of memory for hash table\n"); + return -ENOMEM; + } + for (i=0; i< ht->size; ++i) { + INIT_HLIST_HEAD(&ht->table[i]); + } + return 0; +} + +void drm_ht_verbose_list(drm_open_hash_t *ht, unsigned long key) +{ + drm_hash_item_t *entry; + struct hlist_head *h_list; + struct hlist_node *list; + unsigned int hashed_key; + int count = 0; + + hashed_key = hash_long(key, ht->order); + DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key); + h_list = &ht->table[hashed_key]; + hlist_for_each(list, h_list) { + entry = hlist_entry(list, drm_hash_item_t, head); + DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key); + } +} + +static struct hlist_node *drm_ht_find_key(drm_open_hash_t *ht, + unsigned long key) +{ + drm_hash_item_t *entry; + struct hlist_head *h_list; + struct hlist_node *list; + unsigned int hashed_key; + + hashed_key = hash_long(key, ht->order); + h_list = &ht->table[hashed_key]; + hlist_for_each(list, h_list) { + entry = hlist_entry(list, drm_hash_item_t, head); + if (entry->key == key) + return list; + if (entry->key > key) + break; + } + return NULL; +} + + +int drm_ht_insert_item(drm_open_hash_t *ht, drm_hash_item_t *item) +{ + drm_hash_item_t *entry; + struct hlist_head *h_list; + struct hlist_node *list, *parent; + unsigned int hashed_key; + unsigned long key = item->key; + + hashed_key = hash_long(key, ht->order); + h_list = &ht->table[hashed_key]; + parent = NULL; + hlist_for_each(list, h_list) { + entry = hlist_entry(list, drm_hash_item_t, head); + if (entry->key == key) + return -EINVAL; + if (entry->key > key) + break; + parent = list; + } + if (parent) { + hlist_add_after(parent, &item->head); + } else { + hlist_add_head(&item->head, h_list); + } + return 0; +} + +/* + * Just insert an item and return any "bits" bit key that hasn't been + * used before. + */ +int drm_ht_just_insert_please(drm_open_hash_t *ht, drm_hash_item_t *item, + unsigned long seed, int bits, int shift, + unsigned long add) +{ + int ret; + unsigned long mask = (1 << bits) - 1; + unsigned long first, unshifted_key; + + unshifted_key = hash_long(seed, bits); + first = unshifted_key; + do { + item->key = (unshifted_key << shift) + add; + ret = drm_ht_insert_item(ht, item); + if (ret) + unshifted_key = (unshifted_key + 1) & mask; + } while(ret && (unshifted_key != first)); + + if (ret) { + DRM_ERROR("Available key bit space exhausted\n"); + return -EINVAL; + } + return 0; +} + +int drm_ht_find_item(drm_open_hash_t *ht, unsigned long key, + drm_hash_item_t **item) +{ + struct hlist_node *list; + + list = drm_ht_find_key(ht, key); + if (!list) + return -EINVAL; + + *item = hlist_entry(list, drm_hash_item_t, head); + return 0; +} + +int drm_ht_remove_key(drm_open_hash_t *ht, unsigned long key) +{ + struct hlist_node *list; + + list = drm_ht_find_key(ht, key); + if (list) { + hlist_del_init(list); + ht->fill--; + return 0; + } + return -EINVAL; +} + +int drm_ht_remove_item(drm_open_hash_t *ht, drm_hash_item_t *item) +{ + hlist_del_init(&item->head); + ht->fill--; + return 0; +} + +void drm_ht_remove(drm_open_hash_t *ht) +{ + if (ht->table) { + vfree(ht->table); + ht->table = NULL; + } +} + diff --git a/drivers/char/drm/drm_hashtab.h b/drivers/char/drm/drm_hashtab.h new file mode 100644 index 00000000000..40afec05bff --- /dev/null +++ b/drivers/char/drm/drm_hashtab.h @@ -0,0 +1,67 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismack, ND. USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ +/* + * Simple open hash tab implementation. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#ifndef DRM_HASHTAB_H +#define DRM_HASHTAB_H + +#define drm_hash_entry(_ptr, _type, _member) container_of(_ptr, _type, _member) + +typedef struct drm_hash_item{ + struct hlist_node head; + unsigned long key; +} drm_hash_item_t; + +typedef struct drm_open_hash{ + unsigned int size; + unsigned int order; + unsigned int fill; + struct hlist_head *table; +} drm_open_hash_t; + + +extern int drm_ht_create(drm_open_hash_t *ht, unsigned int order); +extern int drm_ht_insert_item(drm_open_hash_t *ht, drm_hash_item_t *item); +extern int drm_ht_just_insert_please(drm_open_hash_t *ht, drm_hash_item_t *item, + unsigned long seed, int bits, int shift, + unsigned long add); +extern int drm_ht_find_item(drm_open_hash_t *ht, unsigned long key, drm_hash_item_t **item); + +extern void drm_ht_verbose_list(drm_open_hash_t *ht, unsigned long key); +extern int drm_ht_remove_key(drm_open_hash_t *ht, unsigned long key); +extern int drm_ht_remove_item(drm_open_hash_t *ht, drm_hash_item_t *item); +extern void drm_ht_remove(drm_open_hash_t *ht); + + +#endif + diff --git a/drivers/char/drm/drm_ioc32.c b/drivers/char/drm/drm_ioc32.c index e9e2db18952..d4f87452008 100644 --- a/drivers/char/drm/drm_ioc32.c +++ b/drivers/char/drm/drm_ioc32.c @@ -1051,7 +1051,7 @@ long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) drm_ioctl_compat_t *fn; int ret; - if (nr >= DRM_ARRAY_SIZE(drm_compat_ioctls)) + if (nr >= ARRAY_SIZE(drm_compat_ioctls)) return -ENOTTY; fn = drm_compat_ioctls[nr]; diff --git a/drivers/char/drm/drm_ioctl.c b/drivers/char/drm/drm_ioctl.c index 555f323b8a3..565895547d7 100644 --- a/drivers/char/drm/drm_ioctl.c +++ b/drivers/char/drm/drm_ioctl.c @@ -127,9 +127,10 @@ int drm_setunique(struct inode *inode, struct file *filp, domain = bus >> 8; bus &= 0xff; - if ((domain != dev->pci_domain) || - (bus != dev->pci_bus) || - (slot != dev->pci_slot) || (func != dev->pci_func)) + if ((domain != drm_get_pci_domain(dev)) || + (bus != dev->pdev->bus->number) || + (slot != PCI_SLOT(dev->pdev->devfn)) || + (func != PCI_FUNC(dev->pdev->devfn))) return -EINVAL; return 0; @@ -140,15 +141,17 @@ static int drm_set_busid(drm_device_t * dev) int len; if (dev->unique != NULL) - return EBUSY; + return 0; dev->unique_len = 40; dev->unique = drm_alloc(dev->unique_len + 1, DRM_MEM_DRIVER); if (dev->unique == NULL) - return ENOMEM; + return -ENOMEM; len = snprintf(dev->unique, dev->unique_len, "pci:%04x:%02x:%02x.%d", - dev->pci_domain, dev->pci_bus, dev->pci_slot, dev->pci_func); + drm_get_pci_domain(dev), dev->pdev->bus->number, + PCI_SLOT(dev->pdev->devfn), + PCI_FUNC(dev->pdev->devfn)); if (len > dev->unique_len) DRM_ERROR("Unique buffer overflowed\n"); @@ -157,7 +160,7 @@ static int drm_set_busid(drm_device_t * dev) drm_alloc(strlen(dev->driver->pci_driver.name) + dev->unique_len + 2, DRM_MEM_DRIVER); if (dev->devname == NULL) - return ENOMEM; + return -ENOMEM; sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, dev->unique); @@ -330,27 +333,32 @@ int drm_setversion(DRM_IOCTL_ARGS) drm_set_version_t retv; int if_version; drm_set_version_t __user *argp = (void __user *)data; + int ret; - DRM_COPY_FROM_USER_IOCTL(sv, argp, sizeof(sv)); + if (copy_from_user(&sv, argp, sizeof(sv))) + return -EFAULT; retv.drm_di_major = DRM_IF_MAJOR; retv.drm_di_minor = DRM_IF_MINOR; retv.drm_dd_major = dev->driver->major; retv.drm_dd_minor = dev->driver->minor; - DRM_COPY_TO_USER_IOCTL(argp, retv, sizeof(sv)); + if (copy_to_user(argp, &retv, sizeof(retv))) + return -EFAULT; if (sv.drm_di_major != -1) { if (sv.drm_di_major != DRM_IF_MAJOR || sv.drm_di_minor < 0 || sv.drm_di_minor > DRM_IF_MINOR) - return EINVAL; + return -EINVAL; if_version = DRM_IF_VERSION(sv.drm_di_major, sv.drm_di_minor); - dev->if_version = DRM_MAX(if_version, dev->if_version); + dev->if_version = max(if_version, dev->if_version); if (sv.drm_di_minor >= 1) { /* * Version 1.1 includes tying of DRM to specific device */ - drm_set_busid(dev); + ret = drm_set_busid(dev); + if (ret) + return ret; } } @@ -358,7 +366,7 @@ int drm_setversion(DRM_IOCTL_ARGS) if (sv.drm_dd_major != dev->driver->major || sv.drm_dd_minor < 0 || sv.drm_dd_minor > dev->driver->minor) - return EINVAL; + return -EINVAL; if (dev->driver->set_version) dev->driver->set_version(dev, &sv); diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c index ebdb7182c4f..4553a3a1e49 100644 --- a/drivers/char/drm/drm_irq.c +++ b/drivers/char/drm/drm_irq.c @@ -64,9 +64,9 @@ int drm_irq_by_busid(struct inode *inode, struct file *filp, if (copy_from_user(&p, argp, sizeof(p))) return -EFAULT; - if ((p.busnum >> 8) != dev->pci_domain || - (p.busnum & 0xff) != dev->pci_bus || - p.devnum != dev->pci_slot || p.funcnum != dev->pci_func) + if ((p.busnum >> 8) != drm_get_pci_domain(dev) || + (p.busnum & 0xff) != dev->pdev->bus->number || + p.devnum != PCI_SLOT(dev->pdev->devfn) || p.funcnum != PCI_FUNC(dev->pdev->devfn)) return -EINVAL; p.irq = dev->irq; @@ -255,7 +255,8 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) if (!dev->irq) return -EINVAL; - DRM_COPY_FROM_USER_IOCTL(vblwait, argp, sizeof(vblwait)); + if (copy_from_user(&vblwait, argp, sizeof(vblwait))) + return -EFAULT; switch (vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK) { case _DRM_VBLANK_RELATIVE: @@ -329,7 +330,8 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) } done: - DRM_COPY_TO_USER_IOCTL(argp, vblwait, sizeof(vblwait)); + if (copy_to_user(argp, &vblwait, sizeof(vblwait))) + return -EFAULT; return ret; } diff --git a/drivers/char/drm/drm_mm.c b/drivers/char/drm/drm_mm.c new file mode 100644 index 00000000000..617526bd5b0 --- /dev/null +++ b/drivers/char/drm/drm_mm.c @@ -0,0 +1,201 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ + +/* + * Generic simple memory manager implementation. Intended to be used as a base + * class implementation for more advanced memory managers. + * + * Note that the algorithm used is quite simple and there might be substantial + * performance gains if a smarter free list is implemented. Currently it is just an + * unordered stack of free regions. This could easily be improved if an RB-tree + * is used instead. At least if we expect heavy fragmentation. + * + * Aligned allocations can also see improvement. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drmP.h" + +drm_mm_node_t *drm_mm_get_block(drm_mm_node_t * parent, + unsigned long size, unsigned alignment) +{ + + drm_mm_node_t *child; + + if (alignment) + size += alignment - 1; + + if (parent->size == size) { + list_del_init(&parent->fl_entry); + parent->free = 0; + return parent; + } else { + child = (drm_mm_node_t *) drm_alloc(sizeof(*child), DRM_MEM_MM); + if (!child) + return NULL; + + INIT_LIST_HEAD(&child->ml_entry); + INIT_LIST_HEAD(&child->fl_entry); + + child->free = 0; + child->size = size; + child->start = parent->start; + + list_add_tail(&child->ml_entry, &parent->ml_entry); + parent->size -= size; + parent->start += size; + } + return child; +} + +/* + * Put a block. Merge with the previous and / or next block if they are free. + * Otherwise add to the free stack. + */ + +void drm_mm_put_block(drm_mm_t * mm, drm_mm_node_t * cur) +{ + + drm_mm_node_t *list_root = &mm->root_node; + struct list_head *cur_head = &cur->ml_entry; + struct list_head *root_head = &list_root->ml_entry; + drm_mm_node_t *prev_node = NULL; + drm_mm_node_t *next_node; + + int merged = 0; + + if (cur_head->prev != root_head) { + prev_node = list_entry(cur_head->prev, drm_mm_node_t, ml_entry); + if (prev_node->free) { + prev_node->size += cur->size; + merged = 1; + } + } + if (cur_head->next != root_head) { + next_node = list_entry(cur_head->next, drm_mm_node_t, ml_entry); + if (next_node->free) { + if (merged) { + prev_node->size += next_node->size; + list_del(&next_node->ml_entry); + list_del(&next_node->fl_entry); + drm_free(next_node, sizeof(*next_node), + DRM_MEM_MM); + } else { + next_node->size += cur->size; + next_node->start = cur->start; + merged = 1; + } + } + } + if (!merged) { + cur->free = 1; + list_add(&cur->fl_entry, &list_root->fl_entry); + } else { + list_del(&cur->ml_entry); + drm_free(cur, sizeof(*cur), DRM_MEM_MM); + } +} + +drm_mm_node_t *drm_mm_search_free(const drm_mm_t * mm, + unsigned long size, + unsigned alignment, int best_match) +{ + struct list_head *list; + const struct list_head *free_stack = &mm->root_node.fl_entry; + drm_mm_node_t *entry; + drm_mm_node_t *best; + unsigned long best_size; + + best = NULL; + best_size = ~0UL; + + if (alignment) + size += alignment - 1; + + list_for_each(list, free_stack) { + entry = list_entry(list, drm_mm_node_t, fl_entry); + if (entry->size >= size) { + if (!best_match) + return entry; + if (size < best_size) { + best = entry; + best_size = entry->size; + } + } + } + + return best; +} + +int drm_mm_init(drm_mm_t * mm, unsigned long start, unsigned long size) +{ + drm_mm_node_t *child; + + INIT_LIST_HEAD(&mm->root_node.ml_entry); + INIT_LIST_HEAD(&mm->root_node.fl_entry); + child = (drm_mm_node_t *) drm_alloc(sizeof(*child), DRM_MEM_MM); + if (!child) + return -ENOMEM; + + INIT_LIST_HEAD(&child->ml_entry); + INIT_LIST_HEAD(&child->fl_entry); + + child->start = start; + child->size = size; + child->free = 1; + + list_add(&child->fl_entry, &mm->root_node.fl_entry); + list_add(&child->ml_entry, &mm->root_node.ml_entry); + + return 0; +} + +EXPORT_SYMBOL(drm_mm_init); + +void drm_mm_takedown(drm_mm_t * mm) +{ + struct list_head *bnode = mm->root_node.fl_entry.next; + drm_mm_node_t *entry; + + entry = list_entry(bnode, drm_mm_node_t, fl_entry); + + if (entry->ml_entry.next != &mm->root_node.ml_entry || + entry->fl_entry.next != &mm->root_node.fl_entry) { + DRM_ERROR("Memory manager not clean. Delaying takedown\n"); + return; + } + + list_del(&entry->fl_entry); + list_del(&entry->ml_entry); + + drm_free(entry, sizeof(*entry), DRM_MEM_MM); +} + +EXPORT_SYMBOL(drm_mm_takedown); diff --git a/drivers/char/drm/drm_pciids.h b/drivers/char/drm/drm_pciids.h index b1bb3c7b568..09398d5fbd3 100644 --- a/drivers/char/drm/drm_pciids.h +++ b/drivers/char/drm/drm_pciids.h @@ -3,13 +3,13 @@ Please contact dri-devel@lists.sf.net to add new cards to this list */ #define radeon_PCI_IDS \ - {0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \ - {0x1002, 0x3152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x3154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x3E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x3E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|CHIP_IS_IGP}, \ - {0x1002, 0x4137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP}, \ + {0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \ + {0x1002, 0x3152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x3154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x3E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x3E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP}, \ + {0x1002, 0x4137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \ {0x1002, 0x4144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ @@ -25,35 +25,35 @@ {0x1002, 0x4154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ {0x1002, 0x4155, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ {0x1002, 0x4156, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ - {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP}, \ + {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \ {0x1002, 0x4242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ {0x1002, 0x4243, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \ + {0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ {0x1002, 0x4966, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \ {0x1002, 0x4967, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \ - {0x1002, 0x4A48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A4D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A4E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A4F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4A54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4B49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4B4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4B4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4B4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x4C57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C58, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C59, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C5A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \ + {0x1002, 0x4A48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A4D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A4E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A4F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4A54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4B49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4B4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4B4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4B4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x4C57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4C58, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4C59, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4C5A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \ {0x1002, 0x4E44, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4E45, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4E46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ @@ -62,16 +62,16 @@ {0x1002, 0x4E49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ {0x1002, 0x4E4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ {0x1002, 0x4E4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ - {0x1002, 0x4E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4E51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4E52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4E53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4E56, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \ - {0x1002, 0x5145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \ - {0x1002, 0x5146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \ - {0x1002, 0x5147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \ + {0x1002, 0x4E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4E51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4E52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4E53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \ + {0x1002, 0x4E56, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \ + {0x1002, 0x5144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \ + {0x1002, 0x5145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \ + {0x1002, 0x5146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \ + {0x1002, 0x5147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \ {0x1002, 0x5148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ {0x1002, 0x514C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ {0x1002, 0x514D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ @@ -80,59 +80,59 @@ {0x1002, 0x5159, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \ {0x1002, 0x515A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \ {0x1002, 0x515E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \ - {0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5464, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5548, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5549, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x554A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x554B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x554C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x554D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x554E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5551, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5554, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x564A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x564B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x564F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP}, \ - {0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \ + {0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \ + {0x1002, 0x5462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \ + {0x1002, 0x5464, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \ + {0x1002, 0x5548, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5549, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x554A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x554B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x554C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x554D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x554E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5551, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5554, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x564A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x564B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x564F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP}, \ + {0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ {0x1002, 0x5960, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5961, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5962, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5965, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \ - {0x1002, 0x5b60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5b62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5b63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5b64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5b65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5c61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5c63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5d48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5d57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5e48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5e4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x7834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_NEW_MEMMAP}, \ - {0x1002, 0x7835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5b60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5b62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5b63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5b64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5b65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5c61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \ + {0x1002, 0x5c63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \ + {0x1002, 0x5d48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5d57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5e48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5e4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x7834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x7835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0, 0, 0} #define r128_PCI_IDS \ @@ -209,6 +209,7 @@ {0x1039, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x1039, 0x5300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x1039, 0x6300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1039, 0x6330, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_CHIP_315}, \ {0x1039, 0x7300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0, 0, 0} @@ -227,6 +228,10 @@ {0x1106, 0x3122, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x1106, 0x7205, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x1106, 0x3108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1106, 0x3304, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1106, 0x3157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1106, 0x3344, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1106, 0x7204, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0, 0, 0} #define i810_PCI_IDS \ @@ -285,5 +290,9 @@ {0x8086, 0x2592, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x8086, 0x2772, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x8086, 0x27a2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2972, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2982, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2992, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x29a2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0, 0, 0} diff --git a/drivers/char/drm/drm_proc.c b/drivers/char/drm/drm_proc.c index 362a270af0f..62d5fe15f04 100644 --- a/drivers/char/drm/drm_proc.c +++ b/drivers/char/drm/drm_proc.c @@ -510,7 +510,7 @@ static int drm__vma_info(char *buf, char **start, off_t offset, int request, vma->vm_flags & VM_MAYSHARE ? 's' : 'p', vma->vm_flags & VM_LOCKED ? 'l' : '-', vma->vm_flags & VM_IO ? 'i' : '-', - VM_OFFSET(vma)); + vma->vm_pgoff << PAGE_SHIFT); #if defined(__i386__) pgprot = pgprot_val(vma->vm_page_prot); diff --git a/drivers/char/drm/drm_sman.c b/drivers/char/drm/drm_sman.c new file mode 100644 index 00000000000..425c82336ee --- /dev/null +++ b/drivers/char/drm/drm_sman.c @@ -0,0 +1,352 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck., ND., USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * + **************************************************************************/ +/* + * Simple memory manager interface that keeps track on allocate regions on a + * per "owner" basis. All regions associated with an "owner" can be released + * with a simple call. Typically if the "owner" exists. The owner is any + * "unsigned long" identifier. Can typically be a pointer to a file private + * struct or a context identifier. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drm_sman.h" + +typedef struct drm_owner_item { + drm_hash_item_t owner_hash; + struct list_head sman_list; + struct list_head mem_blocks; +} drm_owner_item_t; + +void drm_sman_takedown(drm_sman_t * sman) +{ + drm_ht_remove(&sman->user_hash_tab); + drm_ht_remove(&sman->owner_hash_tab); + if (sman->mm) + drm_free(sman->mm, sman->num_managers * sizeof(*sman->mm), + DRM_MEM_MM); +} + +EXPORT_SYMBOL(drm_sman_takedown); + +int +drm_sman_init(drm_sman_t * sman, unsigned int num_managers, + unsigned int user_order, unsigned int owner_order) +{ + int ret = 0; + + sman->mm = (drm_sman_mm_t *) drm_calloc(num_managers, sizeof(*sman->mm), + DRM_MEM_MM); + if (!sman->mm) { + ret = -ENOMEM; + goto out; + } + sman->num_managers = num_managers; + INIT_LIST_HEAD(&sman->owner_items); + ret = drm_ht_create(&sman->owner_hash_tab, owner_order); + if (ret) + goto out1; + ret = drm_ht_create(&sman->user_hash_tab, user_order); + if (!ret) + goto out; + + drm_ht_remove(&sman->owner_hash_tab); +out1: + drm_free(sman->mm, num_managers * sizeof(*sman->mm), DRM_MEM_MM); +out: + return ret; +} + +EXPORT_SYMBOL(drm_sman_init); + +static void *drm_sman_mm_allocate(void *private, unsigned long size, + unsigned alignment) +{ + drm_mm_t *mm = (drm_mm_t *) private; + drm_mm_node_t *tmp; + + tmp = drm_mm_search_free(mm, size, alignment, 1); + if (!tmp) { + return NULL; + } + tmp = drm_mm_get_block(tmp, size, alignment); + return tmp; +} + +static void drm_sman_mm_free(void *private, void *ref) +{ + drm_mm_t *mm = (drm_mm_t *) private; + drm_mm_node_t *node = (drm_mm_node_t *) ref; + + drm_mm_put_block(mm, node); +} + +static void drm_sman_mm_destroy(void *private) +{ + drm_mm_t *mm = (drm_mm_t *) private; + drm_mm_takedown(mm); + drm_free(mm, sizeof(*mm), DRM_MEM_MM); +} + +static unsigned long drm_sman_mm_offset(void *private, void *ref) +{ + drm_mm_node_t *node = (drm_mm_node_t *) ref; + return node->start; +} + +int +drm_sman_set_range(drm_sman_t * sman, unsigned int manager, + unsigned long start, unsigned long size) +{ + drm_sman_mm_t *sman_mm; + drm_mm_t *mm; + int ret; + + BUG_ON(manager >= sman->num_managers); + + sman_mm = &sman->mm[manager]; + mm = drm_calloc(1, sizeof(*mm), DRM_MEM_MM); + if (!mm) { + return -ENOMEM; + } + sman_mm->private = mm; + ret = drm_mm_init(mm, start, size); + + if (ret) { + drm_free(mm, sizeof(*mm), DRM_MEM_MM); + return ret; + } + + sman_mm->allocate = drm_sman_mm_allocate; + sman_mm->free = drm_sman_mm_free; + sman_mm->destroy = drm_sman_mm_destroy; + sman_mm->offset = drm_sman_mm_offset; + + return 0; +} + +EXPORT_SYMBOL(drm_sman_set_range); + +int +drm_sman_set_manager(drm_sman_t * sman, unsigned int manager, + drm_sman_mm_t * allocator) +{ + BUG_ON(manager >= sman->num_managers); + sman->mm[manager] = *allocator; + + return 0; +} + +static drm_owner_item_t *drm_sman_get_owner_item(drm_sman_t * sman, + unsigned long owner) +{ + int ret; + drm_hash_item_t *owner_hash_item; + drm_owner_item_t *owner_item; + + ret = drm_ht_find_item(&sman->owner_hash_tab, owner, &owner_hash_item); + if (!ret) { + return drm_hash_entry(owner_hash_item, drm_owner_item_t, + owner_hash); + } + + owner_item = drm_calloc(1, sizeof(*owner_item), DRM_MEM_MM); + if (!owner_item) + goto out; + + INIT_LIST_HEAD(&owner_item->mem_blocks); + owner_item->owner_hash.key = owner; + if (drm_ht_insert_item(&sman->owner_hash_tab, &owner_item->owner_hash)) + goto out1; + + list_add_tail(&owner_item->sman_list, &sman->owner_items); + return owner_item; + +out1: + drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM); +out: + return NULL; +} + +drm_memblock_item_t *drm_sman_alloc(drm_sman_t *sman, unsigned int manager, + unsigned long size, unsigned alignment, + unsigned long owner) +{ + void *tmp; + drm_sman_mm_t *sman_mm; + drm_owner_item_t *owner_item; + drm_memblock_item_t *memblock; + + BUG_ON(manager >= sman->num_managers); + + sman_mm = &sman->mm[manager]; + tmp = sman_mm->allocate(sman_mm->private, size, alignment); + + if (!tmp) { + return NULL; + } + + memblock = drm_calloc(1, sizeof(*memblock), DRM_MEM_MM); + + if (!memblock) + goto out; + + memblock->mm_info = tmp; + memblock->mm = sman_mm; + memblock->sman = sman; + + if (drm_ht_just_insert_please + (&sman->user_hash_tab, &memblock->user_hash, + (unsigned long)memblock, 32, 0, 0)) + goto out1; + + owner_item = drm_sman_get_owner_item(sman, owner); + if (!owner_item) + goto out2; + + list_add_tail(&memblock->owner_list, &owner_item->mem_blocks); + + return memblock; + +out2: + drm_ht_remove_item(&sman->user_hash_tab, &memblock->user_hash); +out1: + drm_free(memblock, sizeof(*memblock), DRM_MEM_MM); +out: + sman_mm->free(sman_mm->private, tmp); + + return NULL; +} + +EXPORT_SYMBOL(drm_sman_alloc); + +static void drm_sman_free(drm_memblock_item_t *item) +{ + drm_sman_t *sman = item->sman; + + list_del(&item->owner_list); + drm_ht_remove_item(&sman->user_hash_tab, &item->user_hash); + item->mm->free(item->mm->private, item->mm_info); + drm_free(item, sizeof(*item), DRM_MEM_MM); +} + +int drm_sman_free_key(drm_sman_t *sman, unsigned int key) +{ + drm_hash_item_t *hash_item; + drm_memblock_item_t *memblock_item; + + if (drm_ht_find_item(&sman->user_hash_tab, key, &hash_item)) + return -EINVAL; + + memblock_item = drm_hash_entry(hash_item, drm_memblock_item_t, user_hash); + drm_sman_free(memblock_item); + return 0; +} + +EXPORT_SYMBOL(drm_sman_free_key); + +static void drm_sman_remove_owner(drm_sman_t *sman, + drm_owner_item_t *owner_item) +{ + list_del(&owner_item->sman_list); + drm_ht_remove_item(&sman->owner_hash_tab, &owner_item->owner_hash); + drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM); +} + +int drm_sman_owner_clean(drm_sman_t *sman, unsigned long owner) +{ + + drm_hash_item_t *hash_item; + drm_owner_item_t *owner_item; + + if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) { + return -1; + } + + owner_item = drm_hash_entry(hash_item, drm_owner_item_t, owner_hash); + if (owner_item->mem_blocks.next == &owner_item->mem_blocks) { + drm_sman_remove_owner(sman, owner_item); + return -1; + } + + return 0; +} + +EXPORT_SYMBOL(drm_sman_owner_clean); + +static void drm_sman_do_owner_cleanup(drm_sman_t *sman, + drm_owner_item_t *owner_item) +{ + drm_memblock_item_t *entry, *next; + + list_for_each_entry_safe(entry, next, &owner_item->mem_blocks, + owner_list) { + drm_sman_free(entry); + } + drm_sman_remove_owner(sman, owner_item); +} + +void drm_sman_owner_cleanup(drm_sman_t *sman, unsigned long owner) +{ + + drm_hash_item_t *hash_item; + drm_owner_item_t *owner_item; + + if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) { + + return; + } + + owner_item = drm_hash_entry(hash_item, drm_owner_item_t, owner_hash); + drm_sman_do_owner_cleanup(sman, owner_item); +} + +EXPORT_SYMBOL(drm_sman_owner_cleanup); + +void drm_sman_cleanup(drm_sman_t *sman) +{ + drm_owner_item_t *entry, *next; + unsigned int i; + drm_sman_mm_t *sman_mm; + + list_for_each_entry_safe(entry, next, &sman->owner_items, sman_list) { + drm_sman_do_owner_cleanup(sman, entry); + } + if (sman->mm) { + for (i = 0; i < sman->num_managers; ++i) { + sman_mm = &sman->mm[i]; + if (sman_mm->private) { + sman_mm->destroy(sman_mm->private); + sman_mm->private = NULL; + } + } + } +} + +EXPORT_SYMBOL(drm_sman_cleanup); diff --git a/drivers/char/drm/drm_sman.h b/drivers/char/drm/drm_sman.h new file mode 100644 index 00000000000..ddc732a1bf2 --- /dev/null +++ b/drivers/char/drm/drm_sman.h @@ -0,0 +1,176 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ +/* + * Simple memory MANager interface that keeps track on allocate regions on a + * per "owner" basis. All regions associated with an "owner" can be released + * with a simple call. Typically if the "owner" exists. The owner is any + * "unsigned long" identifier. Can typically be a pointer to a file private + * struct or a context identifier. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#ifndef DRM_SMAN_H +#define DRM_SMAN_H + +#include "drmP.h" +#include "drm_hashtab.h" + +/* + * A class that is an abstration of a simple memory allocator. + * The sman implementation provides a default such allocator + * using the drm_mm.c implementation. But the user can replace it. + * See the SiS implementation, which may use the SiS FB kernel module + * for memory management. + */ + +typedef struct drm_sman_mm { + /* private info. If allocated, needs to be destroyed by the destroy + function */ + void *private; + + /* Allocate a memory block with given size and alignment. + Return an opaque reference to the memory block */ + + void *(*allocate) (void *private, unsigned long size, + unsigned alignment); + + /* Free a memory block. "ref" is the opaque reference that we got from + the "alloc" function */ + + void (*free) (void *private, void *ref); + + /* Free all resources associated with this allocator */ + + void (*destroy) (void *private); + + /* Return a memory offset from the opaque reference returned from the + "alloc" function */ + + unsigned long (*offset) (void *private, void *ref); +} drm_sman_mm_t; + +typedef struct drm_memblock_item { + struct list_head owner_list; + drm_hash_item_t user_hash; + void *mm_info; + drm_sman_mm_t *mm; + struct drm_sman *sman; +} drm_memblock_item_t; + +typedef struct drm_sman { + drm_sman_mm_t *mm; + int num_managers; + drm_open_hash_t owner_hash_tab; + drm_open_hash_t user_hash_tab; + struct list_head owner_items; +} drm_sman_t; + +/* + * Take down a memory manager. This function should only be called after a + * successful init and after a call to drm_sman_cleanup. + */ + +extern void drm_sman_takedown(drm_sman_t * sman); + +/* + * Allocate structures for a manager. + * num_managers are the number of memory pools to manage. (VRAM, AGP, ....) + * user_order is the log2 of the number of buckets in the user hash table. + * set this to approximately log2 of the max number of memory regions + * that will be allocated for _all_ pools together. + * owner_order is the log2 of the number of buckets in the owner hash table. + * set this to approximately log2 of + * the number of client file connections that will + * be using the manager. + * + */ + +extern int drm_sman_init(drm_sman_t * sman, unsigned int num_managers, + unsigned int user_order, unsigned int owner_order); + +/* + * Initialize a drm_mm.c allocator. Should be called only once for each + * manager unless a customized allogator is used. + */ + +extern int drm_sman_set_range(drm_sman_t * sman, unsigned int manager, + unsigned long start, unsigned long size); + +/* + * Initialize a customized allocator for one of the managers. + * (See the SiS module). The object pointed to by "allocator" is copied, + * so it can be destroyed after this call. + */ + +extern int drm_sman_set_manager(drm_sman_t * sman, unsigned int mananger, + drm_sman_mm_t * allocator); + +/* + * Allocate a memory block. Aligment is not implemented yet. + */ + +extern drm_memblock_item_t *drm_sman_alloc(drm_sman_t * sman, + unsigned int manager, + unsigned long size, + unsigned alignment, + unsigned long owner); +/* + * Free a memory block identified by its user hash key. + */ + +extern int drm_sman_free_key(drm_sman_t * sman, unsigned int key); + +/* + * returns 1 iff there are no stale memory blocks associated with this owner. + * Typically called to determine if we need to idle the hardware and call + * drm_sman_owner_cleanup. If there are no stale memory blocks, it removes all + * resources associated with owner. + */ + +extern int drm_sman_owner_clean(drm_sman_t * sman, unsigned long owner); + +/* + * Frees all stale memory blocks associated with this owner. Note that this + * requires that the hardware is finished with all blocks, so the graphics engine + * should be idled before this call is made. This function also frees + * any resources associated with "owner" and should be called when owner + * is not going to be referenced anymore. + */ + +extern void drm_sman_owner_cleanup(drm_sman_t * sman, unsigned long owner); + +/* + * Frees all stale memory blocks associated with the memory manager. + * See idling above. + */ + +extern void drm_sman_cleanup(drm_sman_t * sman); + +#endif diff --git a/drivers/char/drm/drm_stub.c b/drivers/char/drm/drm_stub.c index 9a842a36bb2..7b1d4e8659b 100644 --- a/drivers/char/drm/drm_stub.c +++ b/drivers/char/drm/drm_stub.c @@ -65,22 +65,22 @@ static int drm_fill_in_dev(drm_device_t * dev, struct pci_dev *pdev, mutex_init(&dev->ctxlist_mutex); dev->pdev = pdev; + dev->pci_device = pdev->device; + dev->pci_vendor = pdev->vendor; #ifdef __alpha__ dev->hose = pdev->sysdata; - dev->pci_domain = dev->hose->bus->number; -#else - dev->pci_domain = 0; #endif - dev->pci_bus = pdev->bus->number; - dev->pci_slot = PCI_SLOT(pdev->devfn); - dev->pci_func = PCI_FUNC(pdev->devfn); dev->irq = pdev->irq; dev->maplist = drm_calloc(1, sizeof(*dev->maplist), DRM_MEM_MAPS); if (dev->maplist == NULL) return -ENOMEM; INIT_LIST_HEAD(&dev->maplist->head); + if (drm_ht_create(&dev->map_hash, 12)) { + drm_free(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS); + return -ENOMEM; + } /* the DRM has 6 basic counters */ dev->counters = 6; diff --git a/drivers/char/drm/drm_vm.c b/drivers/char/drm/drm_vm.c index ffd0800ed60..b40ae438f53 100644 --- a/drivers/char/drm/drm_vm.c +++ b/drivers/char/drm/drm_vm.c @@ -59,7 +59,7 @@ static __inline__ struct page *drm_do_vm_nopage(struct vm_area_struct *vma, drm_device_t *dev = priv->head->dev; drm_map_t *map = NULL; drm_map_list_t *r_list; - struct list_head *list; + drm_hash_item_t *hash; /* * Find the right map @@ -70,14 +70,11 @@ static __inline__ struct page *drm_do_vm_nopage(struct vm_area_struct *vma, if (!dev->agp || !dev->agp->cant_use_aperture) goto vm_nopage_error; - list_for_each(list, &dev->maplist->head) { - r_list = list_entry(list, drm_map_list_t, head); - map = r_list->map; - if (!map) - continue; - if (r_list->user_token == VM_OFFSET(vma)) - break; - } + if (drm_ht_find_item(&dev->map_hash, vma->vm_pgoff << PAGE_SHIFT, &hash)) + goto vm_nopage_error; + + r_list = drm_hash_entry(hash, drm_map_list_t, hash); + map = r_list->map; if (map && map->type == _DRM_AGP) { unsigned long offset = address - vma->vm_start; @@ -467,7 +464,7 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) dev = priv->head->dev; dma = dev->dma; DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", - vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + vma->vm_start, vma->vm_end, vma->vm_pgoff << PAGE_SHIFT); /* Length must match exact page count */ if (!dma || (length >> PAGE_SHIFT) != dma->page_count) { @@ -521,12 +518,11 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma) drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->head->dev; drm_map_t *map = NULL; - drm_map_list_t *r_list; unsigned long offset = 0; - struct list_head *list; + drm_hash_item_t *hash; DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", - vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + vma->vm_start, vma->vm_end, vma->vm_pgoff << PAGE_SHIFT); if (!priv->authenticated) return -EACCES; @@ -535,7 +531,7 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma) * the AGP mapped at physical address 0 * --BenH. */ - if (!VM_OFFSET(vma) + if (!(vma->vm_pgoff << PAGE_SHIFT) #if __OS_HAS_AGP && (!dev->agp || dev->agp->agp_info.device->vendor != PCI_VENDOR_ID_APPLE) @@ -543,23 +539,12 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma) ) return drm_mmap_dma(filp, vma); - /* A sequential search of a linked list is - fine here because: 1) there will only be - about 5-10 entries in the list and, 2) a - DRI client only has to do this mapping - once, so it doesn't have to be optimized - for performance, even if the list was a - bit longer. */ - list_for_each(list, &dev->maplist->head) { - - r_list = list_entry(list, drm_map_list_t, head); - map = r_list->map; - if (!map) - continue; - if (r_list->user_token == VM_OFFSET(vma)) - break; + if (drm_ht_find_item(&dev->map_hash, vma->vm_pgoff << PAGE_SHIFT, &hash)) { + DRM_ERROR("Could not find map\n"); + return -EINVAL; } + map = drm_hash_entry(hash, drm_map_list_t, hash)->map; if (!map || ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) return -EPERM; @@ -620,7 +605,7 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma) offset = dev->driver->get_reg_ofs(dev); #ifdef __sparc__ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - if (io_remap_pfn_range(DRM_RPR_ARG(vma) vma->vm_start, + if (io_remap_pfn_range(vma, vma->vm_start, (map->offset + offset) >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)) diff --git a/drivers/char/drm/i810_dma.c b/drivers/char/drm/i810_dma.c index c658dde3633..fa2de70f740 100644 --- a/drivers/char/drm/i810_dma.c +++ b/drivers/char/drm/i810_dma.c @@ -106,7 +106,7 @@ static int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma) unlock_kernel(); if (io_remap_pfn_range(vma, vma->vm_start, - VM_OFFSET(vma) >> PAGE_SHIFT, + vma->vm_pgoff, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; return 0; @@ -141,10 +141,10 @@ static int i810_map_buffer(drm_buf_t * buf, struct file *filp) MAP_SHARED, buf->bus_address); dev_priv->mmap_buffer = NULL; filp->f_op = old_fops; - if ((unsigned long)buf_priv->virtual > -1024UL) { + if (IS_ERR(buf_priv->virtual)) { /* Real error */ DRM_ERROR("mmap error\n"); - retcode = (signed int)buf_priv->virtual; + retcode = PTR_ERR(buf_priv->virtual); buf_priv->virtual = NULL; } up_write(¤t->mm->mmap_sem); @@ -808,7 +808,7 @@ static void i810_dma_dispatch_vertex(drm_device_t * dev, ((GFX_OP_PRIMITIVE | prim | ((used / 4) - 2))); if (used & 4) { - *(u32 *) ((u32) buf_priv->kernel_virtual + used) = 0; + *(u32 *) ((char *) buf_priv->kernel_virtual + used) = 0; used += 4; } @@ -1166,7 +1166,7 @@ static void i810_dma_dispatch_mc(drm_device_t * dev, drm_buf_t * buf, int used, if (buf_priv->currently_mapped == I810_BUF_MAPPED) { if (used & 4) { - *(u32 *) ((u32) buf_priv->virtual + used) = 0; + *(u32 *) ((char *) buf_priv->virtual + used) = 0; used += 4; } diff --git a/drivers/char/drm/i830_dma.c b/drivers/char/drm/i830_dma.c index b0f815d8cea..4f0e5746ab3 100644 --- a/drivers/char/drm/i830_dma.c +++ b/drivers/char/drm/i830_dma.c @@ -108,7 +108,7 @@ static int i830_mmap_buffers(struct file *filp, struct vm_area_struct *vma) unlock_kernel(); if (io_remap_pfn_range(vma, vma->vm_start, - VM_OFFSET(vma) >> PAGE_SHIFT, + vma->vm_pgoff, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; return 0; @@ -146,7 +146,7 @@ static int i830_map_buffer(drm_buf_t * buf, struct file *filp) if (IS_ERR((void *)virtual)) { /* ugh */ /* Real error */ DRM_ERROR("mmap error\n"); - retcode = virtual; + retcode = PTR_ERR((void *)virtual); buf_priv->virtual = NULL; } else { buf_priv->virtual = (void __user *)virtual; diff --git a/drivers/char/drm/i915_dma.c b/drivers/char/drm/i915_dma.c index a94233bdbc0..fb7913ff528 100644 --- a/drivers/char/drm/i915_dma.c +++ b/drivers/char/drm/i915_dma.c @@ -31,6 +31,11 @@ #include "i915_drm.h" #include "i915_drv.h" +#define IS_I965G(dev) (dev->pci_device == 0x2972 || \ + dev->pci_device == 0x2982 || \ + dev->pci_device == 0x2992 || \ + dev->pci_device == 0x29A2) + /* Really want an OS-independent resettable timer. Would like to have * this loop run for (eg) 3 sec, but have the timer reset every time * the head pointer changes, so that EBUSY only happens if the ring @@ -255,7 +260,7 @@ static int i915_dma_init(DRM_IOCTL_ARGS) retcode = i915_dma_resume(dev); break; default: - retcode = -EINVAL; + retcode = DRM_ERR(EINVAL); break; } @@ -347,7 +352,7 @@ static int i915_emit_cmds(drm_device_t * dev, int __user * buffer, int dwords) if ((dwords+1) * sizeof(int) >= dev_priv->ring.Size - 8) return DRM_ERR(EINVAL); - BEGIN_LP_RING(((dwords+1)&~1)); + BEGIN_LP_RING((dwords+1)&~1); for (i = 0; i < dwords;) { int cmd, sz; @@ -386,7 +391,7 @@ static int i915_emit_box(drm_device_t * dev, RING_LOCALS; if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) { - return EFAULT; + return DRM_ERR(EFAULT); } if (box.y2 <= box.y1 || box.x2 <= box.x1 || box.y2 <= 0 || box.x2 <= 0) { @@ -395,24 +400,40 @@ static int i915_emit_box(drm_device_t * dev, return DRM_ERR(EINVAL); } - BEGIN_LP_RING(6); - OUT_RING(GFX_OP_DRAWRECT_INFO); - OUT_RING(DR1); - OUT_RING((box.x1 & 0xffff) | (box.y1 << 16)); - OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16)); - OUT_RING(DR4); - OUT_RING(0); - ADVANCE_LP_RING(); + if (IS_I965G(dev)) { + BEGIN_LP_RING(4); + OUT_RING(GFX_OP_DRAWRECT_INFO_I965); + OUT_RING((box.x1 & 0xffff) | (box.y1 << 16)); + OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16)); + OUT_RING(DR4); + ADVANCE_LP_RING(); + } else { + BEGIN_LP_RING(6); + OUT_RING(GFX_OP_DRAWRECT_INFO); + OUT_RING(DR1); + OUT_RING((box.x1 & 0xffff) | (box.y1 << 16)); + OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16)); + OUT_RING(DR4); + OUT_RING(0); + ADVANCE_LP_RING(); + } return 0; } +/* XXX: Emitting the counter should really be moved to part of the IRQ + * emit. For now, do it in both places: + */ + static void i915_emit_breadcrumb(drm_device_t *dev) { drm_i915_private_t *dev_priv = dev->dev_private; RING_LOCALS; - dev_priv->sarea_priv->last_enqueue = dev_priv->counter++; + dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter; + + if (dev_priv->counter > 0x7FFFFFFFUL) + dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1; BEGIN_LP_RING(4); OUT_RING(CMD_STORE_DWORD_IDX); diff --git a/drivers/char/drm/i915_drm.h b/drivers/char/drm/i915_drm.h index 5aa3e0e3bb4..6af83e613f2 100644 --- a/drivers/char/drm/i915_drm.h +++ b/drivers/char/drm/i915_drm.h @@ -98,6 +98,12 @@ typedef struct _drm_i915_sarea { int rotated_size; int rotated_pitch; int virtualX, virtualY; + + unsigned int front_tiled; + unsigned int back_tiled; + unsigned int depth_tiled; + unsigned int rotated_tiled; + unsigned int rotated2_tiled; } drm_i915_sarea_t; /* Flags for perf_boxes diff --git a/drivers/char/drm/i915_drv.h b/drivers/char/drm/i915_drv.h index 2d565031c00..fdc2bf19271 100644 --- a/drivers/char/drm/i915_drv.h +++ b/drivers/char/drm/i915_drv.h @@ -146,9 +146,9 @@ extern void i915_mem_release(drm_device_t * dev, #define BEGIN_LP_RING(n) do { \ if (I915_VERBOSE) \ DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n", \ - n, __FUNCTION__); \ - if (dev_priv->ring.space < n*4) \ - i915_wait_ring(dev, n*4, __FUNCTION__); \ + (n), __FUNCTION__); \ + if (dev_priv->ring.space < (n)*4) \ + i915_wait_ring(dev, (n)*4, __FUNCTION__); \ outcount = 0; \ outring = dev_priv->ring.tail; \ ringmask = dev_priv->ring.tail_mask; \ @@ -157,7 +157,7 @@ extern void i915_mem_release(drm_device_t * dev, #define OUT_RING(n) do { \ if (I915_VERBOSE) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \ - *(volatile unsigned int *)(virt + outring) = n; \ + *(volatile unsigned int *)(virt + outring) = (n); \ outcount++; \ outring += 4; \ outring &= ringmask; \ @@ -254,6 +254,8 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller); #define GFX_OP_DESTBUFFER_VARS ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0) #define GFX_OP_DRAWRECT_INFO ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3)) +#define GFX_OP_DRAWRECT_INFO_I965 ((0x7900<<16)|0x2) + #define MI_BATCH_BUFFER ((0x30<<23)|1) #define MI_BATCH_BUFFER_START (0x31<<23) #define MI_BATCH_BUFFER_END (0xA<<23) diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index cd96cfa430d..0d4a162aa38 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c @@ -71,21 +71,27 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) static int i915_emit_irq(drm_device_t * dev) { drm_i915_private_t *dev_priv = dev->dev_private; - u32 ret; RING_LOCALS; i915_kernel_lost_context(dev); DRM_DEBUG("%s\n", __FUNCTION__); - ret = dev_priv->counter; + dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter; - BEGIN_LP_RING(2); + if (dev_priv->counter > 0x7FFFFFFFUL) + dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1; + + BEGIN_LP_RING(6); + OUT_RING(CMD_STORE_DWORD_IDX); + OUT_RING(20); + OUT_RING(dev_priv->counter); + OUT_RING(0); OUT_RING(0); OUT_RING(GFX_OP_USER_INTERRUPT); ADVANCE_LP_RING(); - - return ret; + + return dev_priv->counter; } static int i915_wait_irq(drm_device_t * dev, int irq_nr) diff --git a/drivers/char/drm/radeon_cp.c b/drivers/char/drm/radeon_cp.c index 5ad43ba7b5a..5ed96568829 100644 --- a/drivers/char/drm/radeon_cp.c +++ b/drivers/char/drm/radeon_cp.c @@ -864,13 +864,13 @@ static int radeon_do_pixcache_flush(drm_radeon_private_t * dev_priv) dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; - tmp = RADEON_READ(RADEON_RB2D_DSTCACHE_CTLSTAT); - tmp |= RADEON_RB2D_DC_FLUSH_ALL; - RADEON_WRITE(RADEON_RB2D_DSTCACHE_CTLSTAT, tmp); + tmp = RADEON_READ(RADEON_RB3D_DSTCACHE_CTLSTAT); + tmp |= RADEON_RB3D_DC_FLUSH_ALL; + RADEON_WRITE(RADEON_RB3D_DSTCACHE_CTLSTAT, tmp); for (i = 0; i < dev_priv->usec_timeout; i++) { - if (!(RADEON_READ(RADEON_RB2D_DSTCACHE_CTLSTAT) - & RADEON_RB2D_DC_BUSY)) { + if (!(RADEON_READ(RADEON_RB3D_DSTCACHE_CTLSTAT) + & RADEON_RB3D_DC_BUSY)) { return 0; } DRM_UDELAY(1); @@ -1130,7 +1130,7 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev, | (dev_priv->fb_location >> 16)); #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { RADEON_WRITE(RADEON_AGP_BASE, (unsigned int)dev->agp->base); RADEON_WRITE(RADEON_MC_AGP_LOCATION, (((dev_priv->gart_vm_start - 1 + @@ -1158,7 +1158,7 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev, dev_priv->ring.tail = cur_read_ptr; #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { RADEON_WRITE(RADEON_CP_RB_RPTR_ADDR, dev_priv->ring_rptr->offset - dev->agp->base + dev_priv->gart_vm_start); @@ -1258,6 +1258,13 @@ static void radeon_test_writeback(drm_radeon_private_t * dev_priv) dev_priv->writeback_works = 0; DRM_INFO("writeback forced off\n"); } + + if (!dev_priv->writeback_works) { + /* Disable writeback to avoid unnecessary bus master transfer */ + RADEON_WRITE(RADEON_CP_RB_CNTL, RADEON_READ(RADEON_CP_RB_CNTL) | + RADEON_RB_NO_UPDATE); + RADEON_WRITE(RADEON_SCRATCH_UMSK, 0); + } } /* Enable or disable PCI-E GART on the chip */ @@ -1295,7 +1302,7 @@ static void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on) { u32 tmp; - if (dev_priv->flags & CHIP_IS_PCIE) { + if (dev_priv->flags & RADEON_IS_PCIE) { radeon_set_pciegart(dev_priv, on); return; } @@ -1333,20 +1340,22 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) DRM_DEBUG("\n"); /* if we require new memory map but we don't have it fail */ - if ((dev_priv->flags & CHIP_NEW_MEMMAP) && !dev_priv->new_memmap) - { - DRM_ERROR("Cannot initialise DRM on this card\nThis card requires a new X.org DDX\n"); + if ((dev_priv->flags & RADEON_NEW_MEMMAP) && !dev_priv->new_memmap) { + DRM_ERROR("Cannot initialise DRM on this card\nThis card requires a new X.org DDX for 3D\n"); radeon_do_cleanup_cp(dev); return DRM_ERR(EINVAL); } - if (init->is_pci && (dev_priv->flags & CHIP_IS_AGP)) - { + if (init->is_pci && (dev_priv->flags & RADEON_IS_AGP)) { DRM_DEBUG("Forcing AGP card to PCI mode\n"); - dev_priv->flags &= ~CHIP_IS_AGP; + dev_priv->flags &= ~RADEON_IS_AGP; + } else if (!(dev_priv->flags & (RADEON_IS_AGP | RADEON_IS_PCI | RADEON_IS_PCIE)) + && !init->is_pci) { + DRM_DEBUG("Restoring AGP flag\n"); + dev_priv->flags |= RADEON_IS_AGP; } - if ((!(dev_priv->flags & CHIP_IS_AGP)) && !dev->sg) { + if ((!(dev_priv->flags & RADEON_IS_AGP)) && !dev->sg) { DRM_ERROR("PCI GART memory not allocated!\n"); radeon_do_cleanup_cp(dev); return DRM_ERR(EINVAL); @@ -1489,7 +1498,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) init->sarea_priv_offset); #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { drm_core_ioremap(dev_priv->cp_ring, dev); drm_core_ioremap(dev_priv->ring_rptr, dev); drm_core_ioremap(dev->agp_buffer_map, dev); @@ -1548,7 +1557,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) * align it down. */ #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { base = dev->agp->base; /* Check if valid */ if ((base + dev_priv->gart_size) > dev_priv->fb_location && @@ -1578,7 +1587,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) } #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) + if (dev_priv->flags & RADEON_IS_AGP) dev_priv->gart_buffers_offset = (dev->agp_buffer_map->offset - dev->agp->base + dev_priv->gart_vm_start); @@ -1604,7 +1613,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) dev_priv->ring.high_mark = RADEON_RING_HIGH_MARK; #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { /* Turn off PCI GART */ radeon_set_pcigart(dev_priv, 0); } else @@ -1624,7 +1633,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) dev_priv->gart_info.mapping.handle; dev_priv->gart_info.is_pcie = - !!(dev_priv->flags & CHIP_IS_PCIE); + !!(dev_priv->flags & RADEON_IS_PCIE); dev_priv->gart_info.gart_table_location = DRM_ATI_GART_FB; @@ -1636,7 +1645,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) DRM_ATI_GART_MAIN; dev_priv->gart_info.addr = NULL; dev_priv->gart_info.bus_addr = 0; - if (dev_priv->flags & CHIP_IS_PCIE) { + if (dev_priv->flags & RADEON_IS_PCIE) { DRM_ERROR ("Cannot use PCI Express without GART in FB memory\n"); radeon_do_cleanup_cp(dev); @@ -1678,7 +1687,7 @@ static int radeon_do_cleanup_cp(drm_device_t * dev) drm_irq_uninstall(dev); #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { if (dev_priv->cp_ring != NULL) { drm_core_ioremapfree(dev_priv->cp_ring, dev); dev_priv->cp_ring = NULL; @@ -1733,7 +1742,7 @@ static int radeon_do_resume_cp(drm_device_t * dev) DRM_DEBUG("Starting radeon_do_resume_cp()\n"); #if __OS_HAS_AGP - if (dev_priv->flags & CHIP_IS_AGP) { + if (dev_priv->flags & RADEON_IS_AGP) { /* Turn off PCI GART */ radeon_set_pcigart(dev_priv, 0); } else @@ -2177,13 +2186,15 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags) dev->dev_private = (void *)dev_priv; dev_priv->flags = flags; - switch (flags & CHIP_FAMILY_MASK) { + switch (flags & RADEON_FAMILY_MASK) { case CHIP_R100: case CHIP_RV200: case CHIP_R200: case CHIP_R300: + case CHIP_R350: case CHIP_R420: - dev_priv->flags |= CHIP_HAS_HIERZ; + case CHIP_RV410: + dev_priv->flags |= RADEON_HAS_HIERZ; break; default: /* all other chips have no hierarchical z buffer */ @@ -2191,13 +2202,14 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags) } if (drm_device_is_agp(dev)) - dev_priv->flags |= CHIP_IS_AGP; - - if (drm_device_is_pcie(dev)) - dev_priv->flags |= CHIP_IS_PCIE; + dev_priv->flags |= RADEON_IS_AGP; + else if (drm_device_is_pcie(dev)) + dev_priv->flags |= RADEON_IS_PCIE; + else + dev_priv->flags |= RADEON_IS_PCI; DRM_DEBUG("%s card detected\n", - ((dev_priv->flags & CHIP_IS_AGP) ? "AGP" : (((dev_priv->flags & CHIP_IS_PCIE) ? "PCIE" : "PCI")))); + ((dev_priv->flags & RADEON_IS_AGP) ? "AGP" : (((dev_priv->flags & RADEON_IS_PCIE) ? "PCIE" : "PCI")))); return ret; } diff --git a/drivers/char/drm/radeon_drv.c b/drivers/char/drm/radeon_drv.c index eb985c2a31e..2eb652ec674 100644 --- a/drivers/char/drm/radeon_drv.c +++ b/drivers/char/drm/radeon_drv.c @@ -44,7 +44,7 @@ module_param_named(no_wb, radeon_no_wb, int, 0444); static int dri_library_name(struct drm_device *dev, char *buf) { drm_radeon_private_t *dev_priv = dev->dev_private; - int family = dev_priv->flags & CHIP_FAMILY_MASK; + int family = dev_priv->flags & RADEON_FAMILY_MASK; return snprintf(buf, PAGE_SIZE, "%s\n", (family < CHIP_R200) ? "radeon" : diff --git a/drivers/char/drm/radeon_drv.h b/drivers/char/drm/radeon_drv.h index e5a256f5429..f45cd7f147a 100644 --- a/drivers/char/drm/radeon_drv.h +++ b/drivers/char/drm/radeon_drv.h @@ -133,15 +133,16 @@ enum radeon_cp_microcode_version { * Chip flags */ enum radeon_chip_flags { - CHIP_FAMILY_MASK = 0x0000ffffUL, - CHIP_FLAGS_MASK = 0xffff0000UL, - CHIP_IS_MOBILITY = 0x00010000UL, - CHIP_IS_IGP = 0x00020000UL, - CHIP_SINGLE_CRTC = 0x00040000UL, - CHIP_IS_AGP = 0x00080000UL, - CHIP_HAS_HIERZ = 0x00100000UL, - CHIP_IS_PCIE = 0x00200000UL, - CHIP_NEW_MEMMAP = 0x00400000UL, + RADEON_FAMILY_MASK = 0x0000ffffUL, + RADEON_FLAGS_MASK = 0xffff0000UL, + RADEON_IS_MOBILITY = 0x00010000UL, + RADEON_IS_IGP = 0x00020000UL, + RADEON_SINGLE_CRTC = 0x00040000UL, + RADEON_IS_AGP = 0x00080000UL, + RADEON_HAS_HIERZ = 0x00100000UL, + RADEON_IS_PCIE = 0x00200000UL, + RADEON_NEW_MEMMAP = 0x00400000UL, + RADEON_IS_PCI = 0x00800000UL, }; #define GET_RING_HEAD(dev_priv) (dev_priv->writeback_works ? \ @@ -424,6 +425,8 @@ extern int r300_do_cp_cmdbuf(drm_device_t * dev, DRMFILE filp, #define RADEON_RB3D_COLOROFFSET 0x1c40 #define RADEON_RB3D_COLORPITCH 0x1c48 +#define RADEON_SRC_X_Y 0x1590 + #define RADEON_DP_GUI_MASTER_CNTL 0x146c # define RADEON_GMC_SRC_PITCH_OFFSET_CNTL (1 << 0) # define RADEON_GMC_DST_PITCH_OFFSET_CNTL (1 << 1) @@ -441,6 +444,7 @@ extern int r300_do_cp_cmdbuf(drm_device_t * dev, DRMFILE filp, # define RADEON_ROP3_S 0x00cc0000 # define RADEON_ROP3_P 0x00f00000 #define RADEON_DP_WRITE_MASK 0x16cc +#define RADEON_SRC_PITCH_OFFSET 0x1428 #define RADEON_DST_PITCH_OFFSET 0x142c #define RADEON_DST_PITCH_OFFSET_C 0x1c80 # define RADEON_DST_TILE_LINEAR (0 << 30) @@ -545,6 +549,11 @@ extern int r300_do_cp_cmdbuf(drm_device_t * dev, DRMFILE filp, # define RADEON_RB3D_ZC_FREE (1 << 2) # define RADEON_RB3D_ZC_FLUSH_ALL 0x5 # define RADEON_RB3D_ZC_BUSY (1 << 31) +#define RADEON_RB3D_DSTCACHE_CTLSTAT 0x325c +# define RADEON_RB3D_DC_FLUSH (3 << 0) +# define RADEON_RB3D_DC_FREE (3 << 2) +# define RADEON_RB3D_DC_FLUSH_ALL 0xf +# define RADEON_RB3D_DC_BUSY (1 << 31) #define RADEON_RB3D_ZSTENCILCNTL 0x1c2c # define RADEON_Z_TEST_MASK (7 << 4) # define RADEON_Z_TEST_ALWAYS (7 << 4) @@ -681,6 +690,7 @@ extern int r300_do_cp_cmdbuf(drm_device_t * dev, DRMFILE filp, #define RADEON_CP_RB_BASE 0x0700 #define RADEON_CP_RB_CNTL 0x0704 # define RADEON_BUF_SWAP_32BIT (2 << 16) +# define RADEON_RB_NO_UPDATE (1 << 27) #define RADEON_CP_RB_RPTR_ADDR 0x070c #define RADEON_CP_RB_RPTR 0x0710 #define RADEON_CP_RB_WPTR 0x0714 @@ -986,13 +996,13 @@ do { \ } while (0) #define RADEON_FLUSH_CACHE() do { \ - OUT_RING( CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ) ); \ - OUT_RING( RADEON_RB2D_DC_FLUSH ); \ + OUT_RING( CP_PACKET0( RADEON_RB3D_DSTCACHE_CTLSTAT, 0 ) ); \ + OUT_RING( RADEON_RB3D_DC_FLUSH ); \ } while (0) #define RADEON_PURGE_CACHE() do { \ - OUT_RING( CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ) ); \ - OUT_RING( RADEON_RB2D_DC_FLUSH_ALL ); \ + OUT_RING( CP_PACKET0( RADEON_RB3D_DSTCACHE_CTLSTAT, 0 ) ); \ + OUT_RING( RADEON_RB3D_DC_FLUSH_ALL ); \ } while (0) #define RADEON_FLUSH_ZCACHE() do { \ diff --git a/drivers/char/drm/radeon_state.c b/drivers/char/drm/radeon_state.c index 39a7f685e3f..feac5f005d4 100644 --- a/drivers/char/drm/radeon_state.c +++ b/drivers/char/drm/radeon_state.c @@ -42,7 +42,11 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t * drm_file_t * filp_priv, u32 *offset) { - u32 off = *offset; + u64 off = *offset; + u32 fb_start = dev_priv->fb_location; + u32 fb_end = fb_start + dev_priv->fb_size - 1; + u32 gart_start = dev_priv->gart_vm_start; + u32 gart_end = gart_start + dev_priv->gart_size - 1; struct drm_radeon_driver_file_fields *radeon_priv; /* Hrm ... the story of the offset ... So this function converts @@ -62,10 +66,8 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t * /* First, the best case, the offset already lands in either the * framebuffer or the GART mapped space */ - if ((off >= dev_priv->fb_location && - off < (dev_priv->fb_location + dev_priv->fb_size)) || - (off >= dev_priv->gart_vm_start && - off < (dev_priv->gart_vm_start + dev_priv->gart_size))) + if ((off >= fb_start && off <= fb_end) || + (off >= gart_start && off <= gart_end)) return 0; /* Ok, that didn't happen... now check if we have a zero based @@ -78,16 +80,13 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t * } /* Finally, assume we aimed at a GART offset if beyond the fb */ - if (off > (dev_priv->fb_location + dev_priv->fb_size)) - off = off - (dev_priv->fb_location + dev_priv->fb_size) + - dev_priv->gart_vm_start; + if (off > fb_end) + off = off - fb_end - 1 + gart_start; /* Now recheck and fail if out of bounds */ - if ((off >= dev_priv->fb_location && - off < (dev_priv->fb_location + dev_priv->fb_size)) || - (off >= dev_priv->gart_vm_start && - off < (dev_priv->gart_vm_start + dev_priv->gart_size))) { - DRM_DEBUG("offset fixed up to 0x%x\n", off); + if ((off >= fb_start && off <= fb_end) || + (off >= gart_start && off <= gart_end)) { + DRM_DEBUG("offset fixed up to 0x%x\n", (unsigned int)off); *offset = off; return 0; } @@ -869,7 +868,7 @@ static void radeon_cp_dispatch_clear(drm_device_t * dev, */ dev_priv->sarea_priv->ctx_owner = 0; - if ((dev_priv->flags & CHIP_HAS_HIERZ) + if ((dev_priv->flags & RADEON_HAS_HIERZ) && (flags & RADEON_USE_HIERZ)) { /* FIXME : reverse engineer that for Rx00 cards */ /* FIXME : the mask supposedly contains low-res z values. So can't set @@ -914,7 +913,7 @@ static void radeon_cp_dispatch_clear(drm_device_t * dev, for (i = 0; i < nbox; i++) { int tileoffset, nrtilesx, nrtilesy, j; /* it looks like r200 needs rv-style clears, at least if hierz is not enabled? */ - if ((dev_priv->flags & CHIP_HAS_HIERZ) + if ((dev_priv->flags & RADEON_HAS_HIERZ) && !(dev_priv->microcode_version == UCODE_R200)) { /* FIXME : figure this out for r200 (when hierz is enabled). Or maybe r200 actually doesn't need to put the low-res z value into @@ -998,7 +997,7 @@ static void radeon_cp_dispatch_clear(drm_device_t * dev, } /* TODO don't always clear all hi-level z tiles */ - if ((dev_priv->flags & CHIP_HAS_HIERZ) + if ((dev_priv->flags & RADEON_HAS_HIERZ) && (dev_priv->microcode_version == UCODE_R200) && (flags & RADEON_USE_HIERZ)) /* r100 and cards without hierarchical z-buffer have no high-level z-buffer */ @@ -1270,9 +1269,9 @@ static void radeon_cp_dispatch_swap(drm_device_t * dev) DRM_DEBUG("dispatch swap %d,%d-%d,%d\n", x, y, w, h); - BEGIN_RING(7); + BEGIN_RING(9); - OUT_RING(CP_PACKET3(RADEON_CNTL_BITBLT_MULTI, 5)); + OUT_RING(CP_PACKET0(RADEON_DP_GUI_MASTER_CNTL, 0)); OUT_RING(RADEON_GMC_SRC_PITCH_OFFSET_CNTL | RADEON_GMC_DST_PITCH_OFFSET_CNTL | RADEON_GMC_BRUSH_NONE | @@ -1284,6 +1283,7 @@ static void radeon_cp_dispatch_swap(drm_device_t * dev) /* Make this work even if front & back are flipped: */ + OUT_RING(CP_PACKET0(RADEON_SRC_PITCH_OFFSET, 1)); if (dev_priv->current_page == 0) { OUT_RING(dev_priv->back_pitch_offset); OUT_RING(dev_priv->front_pitch_offset); @@ -1292,6 +1292,7 @@ static void radeon_cp_dispatch_swap(drm_device_t * dev) OUT_RING(dev_priv->back_pitch_offset); } + OUT_RING(CP_PACKET0(RADEON_SRC_X_Y, 2)); OUT_RING((x << 16) | y); OUT_RING((x << 16) | y); OUT_RING((w << 16) | h); @@ -2987,16 +2988,21 @@ static int radeon_cp_getparam(DRM_IOCTL_ARGS) case RADEON_PARAM_GART_TEX_HANDLE: value = dev_priv->gart_textures_offset; break; - + case RADEON_PARAM_SCRATCH_OFFSET: + if (!dev_priv->writeback_works) + return DRM_ERR(EINVAL); + value = RADEON_SCRATCH_REG_OFFSET; + break; case RADEON_PARAM_CARD_TYPE: - if (dev_priv->flags & CHIP_IS_PCIE) + if (dev_priv->flags & RADEON_IS_PCIE) value = RADEON_CARD_PCIE; - else if (dev_priv->flags & CHIP_IS_AGP) + else if (dev_priv->flags & RADEON_IS_AGP) value = RADEON_CARD_AGP; else value = RADEON_CARD_PCI; break; default: + DRM_DEBUG("Invalid parameter %d\n", param.param); return DRM_ERR(EINVAL); } diff --git a/drivers/char/drm/sis_drv.c b/drivers/char/drm/sis_drv.c index 5e9dc86f295..3d5b3218b6f 100644 --- a/drivers/char/drm/sis_drv.c +++ b/drivers/char/drm/sis_drv.c @@ -35,11 +35,44 @@ static struct pci_device_id pciidlist[] = { sisdrv_PCI_IDS }; +static int sis_driver_load(drm_device_t *dev, unsigned long chipset) +{ + drm_sis_private_t *dev_priv; + int ret; + + dev_priv = drm_calloc(1, sizeof(drm_sis_private_t), DRM_MEM_DRIVER); + if (dev_priv == NULL) + return DRM_ERR(ENOMEM); + + dev->dev_private = (void *)dev_priv; + dev_priv->chipset = chipset; + ret = drm_sman_init(&dev_priv->sman, 2, 12, 8); + if (ret) { + drm_free(dev_priv, sizeof(dev_priv), DRM_MEM_DRIVER); + } + + return ret; +} + +static int sis_driver_unload(drm_device_t *dev) +{ + drm_sis_private_t *dev_priv = dev->dev_private; + + drm_sman_takedown(&dev_priv->sman); + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + + return 0; +} + static struct drm_driver driver = { .driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR, - .context_ctor = sis_init_context, - .context_dtor = sis_final_context, - .reclaim_buffers = drm_core_reclaim_buffers, + .load = sis_driver_load, + .unload = sis_driver_unload, + .context_dtor = NULL, + .dma_quiescent = sis_idle, + .reclaim_buffers = NULL, + .reclaim_buffers_locked = sis_reclaim_buffers_locked, + .lastclose = sis_lastclose, .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, .ioctls = sis_ioctls, diff --git a/drivers/char/drm/sis_drv.h b/drivers/char/drm/sis_drv.h index e218e526950..2b8d6f6ed7c 100644 --- a/drivers/char/drm/sis_drv.h +++ b/drivers/char/drm/sis_drv.h @@ -31,23 +31,39 @@ /* General customization: */ -#define DRIVER_AUTHOR "SIS" +#define DRIVER_AUTHOR "SIS, Tungsten Graphics" #define DRIVER_NAME "sis" #define DRIVER_DESC "SIS 300/630/540" -#define DRIVER_DATE "20030826" +#define DRIVER_DATE "20060704" #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 1 -#define DRIVER_PATCHLEVEL 0 +#define DRIVER_MINOR 2 +#define DRIVER_PATCHLEVEL 1 -#include "sis_ds.h" +enum sis_family { + SIS_OTHER = 0, + SIS_CHIP_315 = 1, +}; + +#include "drm_sman.h" + +#define SIS_BASE (dev_priv->mmio) +#define SIS_READ(reg) DRM_READ32(SIS_BASE, reg); +#define SIS_WRITE(reg, val) DRM_WRITE32(SIS_BASE, reg, val); typedef struct drm_sis_private { - memHeap_t *AGPHeap; - memHeap_t *FBHeap; + drm_local_map_t *mmio; + unsigned int idle_fault; + drm_sman_t sman; + unsigned int chipset; + int vram_initialized; + int agp_initialized; + unsigned long vram_offset; + unsigned long agp_offset; } drm_sis_private_t; -extern int sis_init_context(drm_device_t * dev, int context); -extern int sis_final_context(drm_device_t * dev, int context); +extern int sis_idle(drm_device_t *dev); +extern void sis_reclaim_buffers_locked(drm_device_t *dev, struct file *filp); +extern void sis_lastclose(drm_device_t *dev); extern drm_ioctl_desc_t sis_ioctls[]; extern int sis_max_ioctl; diff --git a/drivers/char/drm/sis_ds.c b/drivers/char/drm/sis_ds.c deleted file mode 100644 index 2e485d48294..00000000000 --- a/drivers/char/drm/sis_ds.c +++ /dev/null @@ -1,299 +0,0 @@ -/* sis_ds.c -- Private header for Direct Rendering Manager -*- linux-c -*- - * Created: Mon Jan 4 10:05:05 1999 by sclin@sis.com.tw - * - * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan. - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Sung-Ching Lin <sclin@sis.com.tw> - * - */ - -#include "drmP.h" -#include "drm.h" -#include "sis_ds.h" - -/* Set Data Structure, not check repeated value - * temporarily used - */ - -set_t *setInit(void) -{ - int i; - set_t *set; - - set = (set_t *) drm_alloc(sizeof(set_t), DRM_MEM_DRIVER); - if (set != NULL) { - for (i = 0; i < SET_SIZE; i++) { - set->list[i].free_next = i + 1; - set->list[i].alloc_next = -1; - } - set->list[SET_SIZE - 1].free_next = -1; - set->free = 0; - set->alloc = -1; - set->trace = -1; - } - return set; -} - -int setAdd(set_t * set, ITEM_TYPE item) -{ - int free = set->free; - - if (free != -1) { - set->list[free].val = item; - set->free = set->list[free].free_next; - } else { - return 0; - } - - set->list[free].alloc_next = set->alloc; - set->alloc = free; - set->list[free].free_next = -1; - - return 1; -} - -int setDel(set_t * set, ITEM_TYPE item) -{ - int alloc = set->alloc; - int prev = -1; - - while (alloc != -1) { - if (set->list[alloc].val == item) { - if (prev != -1) - set->list[prev].alloc_next = - set->list[alloc].alloc_next; - else - set->alloc = set->list[alloc].alloc_next; - break; - } - prev = alloc; - alloc = set->list[alloc].alloc_next; - } - - if (alloc == -1) - return 0; - - set->list[alloc].free_next = set->free; - set->free = alloc; - set->list[alloc].alloc_next = -1; - - return 1; -} - -/* setFirst -> setAdd -> setNext is wrong */ - -int setFirst(set_t * set, ITEM_TYPE * item) -{ - if (set->alloc == -1) - return 0; - - *item = set->list[set->alloc].val; - set->trace = set->list[set->alloc].alloc_next; - - return 1; -} - -int setNext(set_t * set, ITEM_TYPE * item) -{ - if (set->trace == -1) - return 0; - - *item = set->list[set->trace].val; - set->trace = set->list[set->trace].alloc_next; - - return 1; -} - -int setDestroy(set_t * set) -{ - drm_free(set, sizeof(set_t), DRM_MEM_DRIVER); - - return 1; -} - -/* - * GLX Hardware Device Driver common code - * Copyright (C) 1999 Wittawat Yamwong - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * WITTAWAT YAMWONG, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#define ISFREE(bptr) ((bptr)->free) - -memHeap_t *mmInit(int ofs, int size) -{ - PMemBlock blocks; - - if (size <= 0) - return NULL; - - blocks = (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), DRM_MEM_DRIVER); - if (blocks != NULL) { - blocks->ofs = ofs; - blocks->size = size; - blocks->free = 1; - return (memHeap_t *) blocks; - } else - return NULL; -} - -/* Checks if a pointer 'b' is part of the heap 'heap' */ -int mmBlockInHeap(memHeap_t * heap, PMemBlock b) -{ - TMemBlock *p; - - if (heap == NULL || b == NULL) - return 0; - - p = heap; - while (p != NULL && p != b) { - p = p->next; - } - if (p == b) - return 1; - else - return 0; -} - -static TMemBlock *SliceBlock(TMemBlock * p, - int startofs, int size, - int reserved, int alignment) -{ - TMemBlock *newblock; - - /* break left */ - if (startofs > p->ofs) { - newblock = (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), - DRM_MEM_DRIVER); - newblock->ofs = startofs; - newblock->size = p->size - (startofs - p->ofs); - newblock->free = 1; - newblock->next = p->next; - p->size -= newblock->size; - p->next = newblock; - p = newblock; - } - - /* break right */ - if (size < p->size) { - newblock = (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), - DRM_MEM_DRIVER); - newblock->ofs = startofs + size; - newblock->size = p->size - size; - newblock->free = 1; - newblock->next = p->next; - p->size = size; - p->next = newblock; - } - - /* p = middle block */ - p->align = alignment; - p->free = 0; - p->reserved = reserved; - return p; -} - -PMemBlock mmAllocMem(memHeap_t * heap, int size, int align2, int startSearch) -{ - int mask, startofs, endofs; - TMemBlock *p; - - if (heap == NULL || align2 < 0 || size <= 0) - return NULL; - - mask = (1 << align2) - 1; - startofs = 0; - p = (TMemBlock *) heap; - while (p != NULL) { - if (ISFREE(p)) { - startofs = (p->ofs + mask) & ~mask; - if (startofs < startSearch) { - startofs = startSearch; - } - endofs = startofs + size; - if (endofs <= (p->ofs + p->size)) - break; - } - p = p->next; - } - if (p == NULL) - return NULL; - p = SliceBlock(p, startofs, size, 0, mask + 1); - p->heap = heap; - return p; -} - -static __inline__ int Join2Blocks(TMemBlock * p) -{ - if (p->free && p->next && p->next->free) { - TMemBlock *q = p->next; - p->size += q->size; - p->next = q->next; - drm_free(q, sizeof(TMemBlock), DRM_MEM_DRIVER); - return 1; - } - return 0; -} - -int mmFreeMem(PMemBlock b) -{ - TMemBlock *p, *prev; - - if (b == NULL) - return 0; - if (b->heap == NULL) - return -1; - - p = b->heap; - prev = NULL; - while (p != NULL && p != b) { - prev = p; - p = p->next; - } - if (p == NULL || p->free || p->reserved) - return -1; - - p->free = 1; - Join2Blocks(p); - if (prev) - Join2Blocks(prev); - return 0; -} diff --git a/drivers/char/drm/sis_ds.h b/drivers/char/drm/sis_ds.h deleted file mode 100644 index 94f2b4728b6..00000000000 --- a/drivers/char/drm/sis_ds.h +++ /dev/null @@ -1,146 +0,0 @@ -/* sis_ds.h -- Private header for Direct Rendering Manager -*- linux-c -*- - * Created: Mon Jan 4 10:05:05 1999 by sclin@sis.com.tw - */ -/* - * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan. - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Sung-Ching Lin <sclin@sis.com.tw> - * - */ - -#ifndef __SIS_DS_H__ -#define __SIS_DS_H__ - -/* Set Data Structure */ - -#define SET_SIZE 5000 - -typedef unsigned long ITEM_TYPE; - -typedef struct { - ITEM_TYPE val; - int alloc_next, free_next; -} list_item_t; - -typedef struct { - int alloc; - int free; - int trace; - list_item_t list[SET_SIZE]; -} set_t; - -set_t *setInit(void); -int setAdd(set_t * set, ITEM_TYPE item); -int setDel(set_t * set, ITEM_TYPE item); -int setFirst(set_t * set, ITEM_TYPE * item); -int setNext(set_t * set, ITEM_TYPE * item); -int setDestroy(set_t * set); - -/* - * GLX Hardware Device Driver common code - * Copyright (C) 1999 Wittawat Yamwong - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * WITTAWAT YAMWONG, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -struct mem_block_t { - struct mem_block_t *next; - struct mem_block_t *heap; - int ofs, size; - int align; - unsigned int free:1; - unsigned int reserved:1; -}; -typedef struct mem_block_t TMemBlock; -typedef struct mem_block_t *PMemBlock; - -/* a heap is just the first block in a chain */ -typedef struct mem_block_t memHeap_t; - -static __inline__ int mmBlockSize(PMemBlock b) -{ - return b->size; -} - -static __inline__ int mmOffset(PMemBlock b) -{ - return b->ofs; -} - -static __inline__ void mmMarkReserved(PMemBlock b) -{ - b->reserved = 1; -} - -/* - * input: total size in bytes - * return: a heap pointer if OK, NULL if error - */ -memHeap_t *mmInit(int ofs, int size); - -/* - * Allocate 'size' bytes with 2^align2 bytes alignment, - * restrict the search to free memory after 'startSearch' - * depth and back buffers should be in different 4mb banks - * to get better page hits if possible - * input: size = size of block - * align2 = 2^align2 bytes alignment - * startSearch = linear offset from start of heap to begin search - * return: pointer to the allocated block, 0 if error - */ -PMemBlock mmAllocMem(memHeap_t * heap, int size, int align2, int startSearch); - -/* - * Returns 1 if the block 'b' is part of the heap 'heap' - */ -int mmBlockInHeap(PMemBlock heap, PMemBlock b); - -/* - * Free block starts at offset - * input: pointer to a block - * return: 0 if OK, -1 if error - */ -int mmFreeMem(PMemBlock b); - -/* For debuging purpose. */ -void mmDumpMemInfo(memHeap_t * mmInit); - -#endif /* __SIS_DS_H__ */ diff --git a/drivers/char/drm/sis_mm.c b/drivers/char/drm/sis_mm.c index 5e9936bc307..d26f5dbb785 100644 --- a/drivers/char/drm/sis_mm.c +++ b/drivers/char/drm/sis_mm.c @@ -1,414 +1,348 @@ -/* sis_mm.c -- Private header for Direct Rendering Manager -*- linux-c -*- - * Created: Mon Jan 4 10:05:05 1999 by sclin@sis.com.tw +/************************************************************************** * - * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan. - * All rights reserved. + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. * - * Authors: - * Sung-Ching Lin <sclin@sis.com.tw> * + **************************************************************************/ + +/* + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> */ #include "drmP.h" #include "sis_drm.h" #include "sis_drv.h" -#include "sis_ds.h" -#if defined(__linux__) && defined(CONFIG_FB_SIS) + #include <video/sisfb.h> -#endif -#define MAX_CONTEXT 100 #define VIDEO_TYPE 0 #define AGP_TYPE 1 -typedef struct { - int used; - int context; - set_t *sets[2]; /* 0 for video, 1 for AGP */ -} sis_context_t; -static sis_context_t global_ppriv[MAX_CONTEXT]; +#if defined(CONFIG_FB_SIS) +/* fb management via fb device */ -static int add_alloc_set(int context, int type, unsigned int val) -{ - int i, retval = 0; +#define SIS_MM_ALIGN_SHIFT 0 +#define SIS_MM_ALIGN_MASK 0 - for (i = 0; i < MAX_CONTEXT; i++) { - if (global_ppriv[i].used && global_ppriv[i].context == context) { - retval = setAdd(global_ppriv[i].sets[type], val); - break; - } - } - return retval; -} - -static int del_alloc_set(int context, int type, unsigned int val) +static void *sis_sman_mm_allocate(void *private, unsigned long size, + unsigned alignment) { - int i, retval = 0; + struct sis_memreq req; - for (i = 0; i < MAX_CONTEXT; i++) { - if (global_ppriv[i].used && global_ppriv[i].context == context) { - retval = setDel(global_ppriv[i].sets[type], val); - break; - } - } - return retval; + req.size = size; + sis_malloc(&req); + if (req.size == 0) + return NULL; + else + return (void *)~req.offset; } -/* fb management via fb device */ -#if defined(__linux__) && defined(CONFIG_FB_SIS) - -static int sis_fb_init(DRM_IOCTL_ARGS) +static void sis_sman_mm_free(void *private, void *ref) { - return 0; + sis_free(~((unsigned long)ref)); } -static int sis_fb_alloc(DRM_IOCTL_ARGS) +static void sis_sman_mm_destroy(void *private) { - drm_sis_mem_t fb; - struct sis_memreq req; - drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *)data; - int retval = 0; - - DRM_COPY_FROM_USER_IOCTL(fb, argp, sizeof(fb)); - - req.size = fb.size; - sis_malloc(&req); - if (req.offset) { - /* TODO */ - fb.offset = req.offset; - fb.free = req.offset; - if (!add_alloc_set(fb.context, VIDEO_TYPE, fb.free)) { - DRM_DEBUG("adding to allocation set fails\n"); - sis_free(req.offset); - retval = DRM_ERR(EINVAL); - } - } else { - fb.offset = 0; - fb.size = 0; - fb.free = 0; - } - - DRM_COPY_TO_USER_IOCTL(argp, fb, sizeof(fb)); - - DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size, req.offset); - - return retval; + ; } -static int sis_fb_free(DRM_IOCTL_ARGS) +static unsigned long sis_sman_mm_offset(void *private, void *ref) { - drm_sis_mem_t fb; - int retval = 0; - - DRM_COPY_FROM_USER_IOCTL(fb, (drm_sis_mem_t __user *) data, sizeof(fb)); - - if (!fb.free) - return DRM_ERR(EINVAL); + return ~((unsigned long)ref); +} - if (!del_alloc_set(fb.context, VIDEO_TYPE, fb.free)) - retval = DRM_ERR(EINVAL); - sis_free(fb.free); +#else /* CONFIG_FB_SIS */ - DRM_DEBUG("free fb, offset = 0x%lx\n", fb.free); +#define SIS_MM_ALIGN_SHIFT 4 +#define SIS_MM_ALIGN_MASK ( (1 << SIS_MM_ALIGN_SHIFT) - 1) - return retval; -} +#endif /* CONFIG_FB_SIS */ -#else - -/* Called by the X Server to initialize the FB heap. Allocations will fail - * unless this is called. Offset is the beginning of the heap from the - * framebuffer offset (MaxXFBMem in XFree86). - * - * Memory layout according to Thomas Winischofer: - * |------------------|DDDDDDDDDDDDDDDDDDDDDDDDDDDDD|HHHH|CCCCCCCCCCC| - * - * X driver/sisfb HW- Command- - * framebuffer memory DRI heap Cursor queue - */ static int sis_fb_init(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_sis_private_t *dev_priv = dev->dev_private; drm_sis_fb_t fb; + int ret; DRM_COPY_FROM_USER_IOCTL(fb, (drm_sis_fb_t __user *) data, sizeof(fb)); - if (dev_priv == NULL) { - dev->dev_private = drm_calloc(1, sizeof(drm_sis_private_t), - DRM_MEM_DRIVER); - dev_priv = dev->dev_private; - if (dev_priv == NULL) - return ENOMEM; + mutex_lock(&dev->struct_mutex); +#if defined(CONFIG_FB_SIS) + { + drm_sman_mm_t sman_mm; + sman_mm.private = (void *)0xFFFFFFFF; + sman_mm.allocate = sis_sman_mm_allocate; + sman_mm.free = sis_sman_mm_free; + sman_mm.destroy = sis_sman_mm_destroy; + sman_mm.offset = sis_sman_mm_offset; + ret = + drm_sman_set_manager(&dev_priv->sman, VIDEO_TYPE, &sman_mm); } +#else + ret = drm_sman_set_range(&dev_priv->sman, VIDEO_TYPE, 0, + fb.size >> SIS_MM_ALIGN_SHIFT); +#endif - if (dev_priv->FBHeap != NULL) - return DRM_ERR(EINVAL); + if (ret) { + DRM_ERROR("VRAM memory manager initialisation error\n"); + mutex_unlock(&dev->struct_mutex); + return ret; + } - dev_priv->FBHeap = mmInit(fb.offset, fb.size); + dev_priv->vram_initialized = 1; + dev_priv->vram_offset = fb.offset; + mutex_unlock(&dev->struct_mutex); DRM_DEBUG("offset = %u, size = %u", fb.offset, fb.size); return 0; } -static int sis_fb_alloc(DRM_IOCTL_ARGS) +static int sis_drm_alloc(drm_device_t * dev, drm_file_t * priv, + unsigned long data, int pool) { - DRM_DEVICE; drm_sis_private_t *dev_priv = dev->dev_private; - drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *)data; - drm_sis_mem_t fb; - PMemBlock block; + drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *) data; + drm_sis_mem_t mem; int retval = 0; + drm_memblock_item_t *item; + + DRM_COPY_FROM_USER_IOCTL(mem, argp, sizeof(mem)); - if (dev_priv == NULL || dev_priv->FBHeap == NULL) + mutex_lock(&dev->struct_mutex); + + if (0 == ((pool == 0) ? dev_priv->vram_initialized : + dev_priv->agp_initialized)) { + DRM_ERROR + ("Attempt to allocate from uninitialized memory manager.\n"); return DRM_ERR(EINVAL); + } - DRM_COPY_FROM_USER_IOCTL(fb, argp, sizeof(fb)); - - block = mmAllocMem(dev_priv->FBHeap, fb.size, 0, 0); - if (block) { - /* TODO */ - fb.offset = block->ofs; - fb.free = (unsigned long)block; - if (!add_alloc_set(fb.context, VIDEO_TYPE, fb.free)) { - DRM_DEBUG("adding to allocation set fails\n"); - mmFreeMem((PMemBlock) fb.free); - retval = DRM_ERR(EINVAL); - } + mem.size = (mem.size + SIS_MM_ALIGN_MASK) >> SIS_MM_ALIGN_SHIFT; + item = drm_sman_alloc(&dev_priv->sman, pool, mem.size, 0, + (unsigned long)priv); + + mutex_unlock(&dev->struct_mutex); + if (item) { + mem.offset = ((pool == 0) ? + dev_priv->vram_offset : dev_priv->agp_offset) + + (item->mm-> + offset(item->mm, item->mm_info) << SIS_MM_ALIGN_SHIFT); + mem.free = item->user_hash.key; + mem.size = mem.size << SIS_MM_ALIGN_SHIFT; } else { - fb.offset = 0; - fb.size = 0; - fb.free = 0; + mem.offset = 0; + mem.size = 0; + mem.free = 0; + retval = DRM_ERR(ENOMEM); } - DRM_COPY_TO_USER_IOCTL(argp, fb, sizeof(fb)); + DRM_COPY_TO_USER_IOCTL(argp, mem, sizeof(mem)); - DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size, fb.offset); + DRM_DEBUG("alloc %d, size = %d, offset = %d\n", pool, mem.size, + mem.offset); return retval; } -static int sis_fb_free(DRM_IOCTL_ARGS) +static int sis_drm_free(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_sis_private_t *dev_priv = dev->dev_private; - drm_sis_mem_t fb; + drm_sis_mem_t mem; + int ret; - if (dev_priv == NULL || dev_priv->FBHeap == NULL) - return DRM_ERR(EINVAL); + DRM_COPY_FROM_USER_IOCTL(mem, (drm_sis_mem_t __user *) data, + sizeof(mem)); - DRM_COPY_FROM_USER_IOCTL(fb, (drm_sis_mem_t __user *) data, sizeof(fb)); + mutex_lock(&dev->struct_mutex); + ret = drm_sman_free_key(&dev_priv->sman, mem.free); + mutex_unlock(&dev->struct_mutex); + DRM_DEBUG("free = 0x%lx\n", mem.free); - if (!mmBlockInHeap(dev_priv->FBHeap, (PMemBlock) fb.free)) - return DRM_ERR(EINVAL); - - if (!del_alloc_set(fb.context, VIDEO_TYPE, fb.free)) - return DRM_ERR(EINVAL); - mmFreeMem((PMemBlock) fb.free); - - DRM_DEBUG("free fb, free = 0x%lx\n", fb.free); - - return 0; + return ret; } -#endif - -/* agp memory management */ +static int sis_fb_alloc(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + return sis_drm_alloc(dev, priv, data, VIDEO_TYPE); +} static int sis_ioctl_agp_init(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_sis_private_t *dev_priv = dev->dev_private; drm_sis_agp_t agp; - - if (dev_priv == NULL) { - dev->dev_private = drm_calloc(1, sizeof(drm_sis_private_t), - DRM_MEM_DRIVER); - dev_priv = dev->dev_private; - if (dev_priv == NULL) - return ENOMEM; - } - - if (dev_priv->AGPHeap != NULL) - return DRM_ERR(EINVAL); + int ret; + dev_priv = dev->dev_private; DRM_COPY_FROM_USER_IOCTL(agp, (drm_sis_agp_t __user *) data, sizeof(agp)); + mutex_lock(&dev->struct_mutex); + ret = drm_sman_set_range(&dev_priv->sman, AGP_TYPE, 0, + agp.size >> SIS_MM_ALIGN_SHIFT); + + if (ret) { + DRM_ERROR("AGP memory manager initialisation error\n"); + mutex_unlock(&dev->struct_mutex); + return ret; + } - dev_priv->AGPHeap = mmInit(agp.offset, agp.size); + dev_priv->agp_initialized = 1; + dev_priv->agp_offset = agp.offset; + mutex_unlock(&dev->struct_mutex); DRM_DEBUG("offset = %u, size = %u", agp.offset, agp.size); - return 0; } static int sis_ioctl_agp_alloc(DRM_IOCTL_ARGS) { DRM_DEVICE; - drm_sis_private_t *dev_priv = dev->dev_private; - drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *)data; - drm_sis_mem_t agp; - PMemBlock block; - int retval = 0; - if (dev_priv == NULL || dev_priv->AGPHeap == NULL) - return DRM_ERR(EINVAL); + return sis_drm_alloc(dev, priv, data, AGP_TYPE); +} - DRM_COPY_FROM_USER_IOCTL(agp, argp, sizeof(agp)); - - block = mmAllocMem(dev_priv->AGPHeap, agp.size, 0, 0); - if (block) { - /* TODO */ - agp.offset = block->ofs; - agp.free = (unsigned long)block; - if (!add_alloc_set(agp.context, AGP_TYPE, agp.free)) { - DRM_DEBUG("adding to allocation set fails\n"); - mmFreeMem((PMemBlock) agp.free); - retval = -1; +static drm_local_map_t *sis_reg_init(drm_device_t *dev) +{ + drm_map_list_t *entry; + drm_local_map_t *map; + + list_for_each_entry(entry, &dev->maplist->head, head) { + map = entry->map; + if (!map) + continue; + if (map->type == _DRM_REGISTERS) { + return map; } - } else { - agp.offset = 0; - agp.size = 0; - agp.free = 0; } - - DRM_COPY_TO_USER_IOCTL(argp, agp, sizeof(agp)); - - DRM_DEBUG("alloc agp, size = %d, offset = %d\n", agp.size, agp.offset); - - return retval; + return NULL; } -static int sis_ioctl_agp_free(DRM_IOCTL_ARGS) +int sis_idle(drm_device_t *dev) { - DRM_DEVICE; drm_sis_private_t *dev_priv = dev->dev_private; - drm_sis_mem_t agp; - - if (dev_priv == NULL || dev_priv->AGPHeap == NULL) - return DRM_ERR(EINVAL); + uint32_t idle_reg; + unsigned long end; + int i; - DRM_COPY_FROM_USER_IOCTL(agp, (drm_sis_mem_t __user *) data, - sizeof(agp)); + if (dev_priv->idle_fault) + return 0; - if (!mmBlockInHeap(dev_priv->AGPHeap, (PMemBlock) agp.free)) - return DRM_ERR(EINVAL); + if (dev_priv->mmio == NULL) { + dev_priv->mmio = sis_reg_init(dev); + if (dev_priv->mmio == NULL) { + DRM_ERROR("Could not find register map.\n"); + return 0; + } + } + + /* + * Implement a device switch here if needed + */ + + if (dev_priv->chipset != SIS_CHIP_315) + return 0; + + /* + * Timeout after 3 seconds. We cannot use DRM_WAIT_ON here + * because its polling frequency is too low. + */ + + end = jiffies + (DRM_HZ * 3); + + for (i=0; i<4; ++i) { + do { + idle_reg = SIS_READ(0x85cc); + } while ( !time_after_eq(jiffies, end) && + ((idle_reg & 0x80000000) != 0x80000000)); + } - mmFreeMem((PMemBlock) agp.free); - if (!del_alloc_set(agp.context, AGP_TYPE, agp.free)) - return DRM_ERR(EINVAL); + if (time_after_eq(jiffies, end)) { + DRM_ERROR("Graphics engine idle timeout. " + "Disabling idle check\n"); + dev_priv->idle_fault = 1; + } - DRM_DEBUG("free agp, free = 0x%lx\n", agp.free); + /* + * The caller never sees an error code. It gets trapped + * in libdrm. + */ return 0; } -int sis_init_context(struct drm_device *dev, int context) -{ - int i; - for (i = 0; i < MAX_CONTEXT; i++) { - if (global_ppriv[i].used && - (global_ppriv[i].context == context)) - break; - } +void sis_lastclose(struct drm_device *dev) +{ + drm_sis_private_t *dev_priv = dev->dev_private; - if (i >= MAX_CONTEXT) { - for (i = 0; i < MAX_CONTEXT; i++) { - if (!global_ppriv[i].used) { - global_ppriv[i].context = context; - global_ppriv[i].used = 1; - global_ppriv[i].sets[0] = setInit(); - global_ppriv[i].sets[1] = setInit(); - DRM_DEBUG("init allocation set, socket=%d, " - "context = %d\n", i, context); - break; - } - } - if ((i >= MAX_CONTEXT) || (global_ppriv[i].sets[0] == NULL) || - (global_ppriv[i].sets[1] == NULL)) { - return 0; - } - } + if (!dev_priv) + return; - return 1; + mutex_lock(&dev->struct_mutex); + drm_sman_cleanup(&dev_priv->sman); + dev_priv->vram_initialized = 0; + dev_priv->agp_initialized = 0; + dev_priv->mmio = NULL; + mutex_unlock(&dev->struct_mutex); } -int sis_final_context(struct drm_device *dev, int context) +void sis_reclaim_buffers_locked(drm_device_t * dev, struct file *filp) { - int i; + drm_sis_private_t *dev_priv = dev->dev_private; + drm_file_t *priv = filp->private_data; - for (i = 0; i < MAX_CONTEXT; i++) { - if (global_ppriv[i].used && - (global_ppriv[i].context == context)) - break; + mutex_lock(&dev->struct_mutex); + if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)priv)) { + mutex_unlock(&dev->struct_mutex); + return; } - if (i < MAX_CONTEXT) { - set_t *set; - ITEM_TYPE item; - int retval; - - DRM_DEBUG("find socket %d, context = %d\n", i, context); - - /* Video Memory */ - set = global_ppriv[i].sets[0]; - retval = setFirst(set, &item); - while (retval) { - DRM_DEBUG("free video memory 0x%lx\n", item); -#if defined(__linux__) && defined(CONFIG_FB_SIS) - sis_free(item); -#else - mmFreeMem((PMemBlock) item); -#endif - retval = setNext(set, &item); - } - setDestroy(set); - - /* AGP Memory */ - set = global_ppriv[i].sets[1]; - retval = setFirst(set, &item); - while (retval) { - DRM_DEBUG("free agp memory 0x%lx\n", item); - mmFreeMem((PMemBlock) item); - retval = setNext(set, &item); - } - setDestroy(set); - - global_ppriv[i].used = 0; + if (dev->driver->dma_quiescent) { + dev->driver->dma_quiescent(dev); } - return 1; + drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)priv); + mutex_unlock(&dev->struct_mutex); + return; } drm_ioctl_desc_t sis_ioctls[] = { [DRM_IOCTL_NR(DRM_SIS_FB_ALLOC)] = {sis_fb_alloc, DRM_AUTH}, - [DRM_IOCTL_NR(DRM_SIS_FB_FREE)] = {sis_fb_free, DRM_AUTH}, - [DRM_IOCTL_NR(DRM_SIS_AGP_INIT)] = {sis_ioctl_agp_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_SIS_FB_FREE)] = {sis_drm_free, DRM_AUTH}, + [DRM_IOCTL_NR(DRM_SIS_AGP_INIT)] = + {sis_ioctl_agp_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_SIS_AGP_ALLOC)] = {sis_ioctl_agp_alloc, DRM_AUTH}, - [DRM_IOCTL_NR(DRM_SIS_AGP_FREE)] = {sis_ioctl_agp_free, DRM_AUTH}, - [DRM_IOCTL_NR(DRM_SIS_FB_INIT)] = {sis_fb_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY} + [DRM_IOCTL_NR(DRM_SIS_AGP_FREE)] = {sis_drm_free, DRM_AUTH}, + [DRM_IOCTL_NR(DRM_SIS_FB_INIT)] = + {sis_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY} }; int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls); diff --git a/drivers/char/drm/via_dmablit.c b/drivers/char/drm/via_dmablit.c index 78a81a4a99c..60c1695db30 100644 --- a/drivers/char/drm/via_dmablit.c +++ b/drivers/char/drm/via_dmablit.c @@ -41,9 +41,9 @@ #include <linux/pagemap.h> -#define VIA_PGDN(x) (((unsigned long)(x)) & PAGE_MASK) -#define VIA_PGOFF(x) (((unsigned long)(x)) & ~PAGE_MASK) -#define VIA_PFN(x) ((unsigned long)(x) >> PAGE_SHIFT) +#define VIA_PGDN(x) (((unsigned long)(x)) & PAGE_MASK) +#define VIA_PGOFF(x) (((unsigned long)(x)) & ~PAGE_MASK) +#define VIA_PFN(x) ((unsigned long)(x) >> PAGE_SHIFT) typedef struct _drm_via_descriptor { uint32_t mem_addr; @@ -121,19 +121,19 @@ via_map_blit_for_device(struct pci_dev *pdev, while (line_len > 0) { - remaining_len = min(PAGE_SIZE-VIA_PGOFF(cur_mem), line_len); + remaining_len = min(PAGE_SIZE-VIA_PGOFF(cur_mem), line_len); line_len -= remaining_len; if (mode == 1) { - desc_ptr->mem_addr = + desc_ptr->mem_addr = dma_map_page(&pdev->dev, vsg->pages[VIA_PFN(cur_mem) - VIA_PFN(first_addr)], VIA_PGOFF(cur_mem), remaining_len, vsg->direction); - desc_ptr->dev_addr = cur_fb; + desc_ptr->dev_addr = cur_fb; - desc_ptr->size = remaining_len; + desc_ptr->size = remaining_len; desc_ptr->next = (uint32_t) next; next = dma_map_single(&pdev->dev, desc_ptr, sizeof(*desc_ptr), DMA_TO_DEVICE); @@ -162,7 +162,7 @@ via_map_blit_for_device(struct pci_dev *pdev, /* * Function that frees up all resources for a blit. It is usable even if the - * blit info has only be partially built as long as the status enum is consistent + * blit info has only been partially built as long as the status enum is consistent * with the actual status of the used resources. */ @@ -238,8 +238,11 @@ via_lock_all_dma_pages(drm_via_sg_info_t *vsg, drm_via_dmablit_t *xfer) return DRM_ERR(ENOMEM); memset(vsg->pages, 0, sizeof(struct page *) * vsg->num_pages); down_read(¤t->mm->mmap_sem); - ret = get_user_pages(current, current->mm, (unsigned long) xfer->mem_addr, - vsg->num_pages, vsg->direction, 0, vsg->pages, NULL); + ret = get_user_pages(current, current->mm, + (unsigned long)xfer->mem_addr, + vsg->num_pages, + (vsg->direction == DMA_FROM_DEVICE), + 0, vsg->pages, NULL); up_read(¤t->mm->mmap_sem); if (ret != vsg->num_pages) { @@ -475,9 +478,15 @@ via_dmablit_timer(unsigned long data) if (!timer_pending(&blitq->poll_timer)) { blitq->poll_timer.expires = jiffies+1; add_timer(&blitq->poll_timer); - } - via_dmablit_handler(dev, engine, 0); + /* + * Rerun handler to delete timer if engines are off, and + * to shorten abort latency. This is a little nasty. + */ + + via_dmablit_handler(dev, engine, 0); + + } } @@ -597,15 +606,27 @@ via_build_sg_info(drm_device_t *dev, drm_via_sg_info_t *vsg, drm_via_dmablit_t * * (Not a big limitation anyway.) */ - if (((xfer->mem_stride - xfer->line_length) >= PAGE_SIZE) || - (xfer->mem_stride > 2048*4)) { + if ((xfer->mem_stride - xfer->line_length) >= PAGE_SIZE) { DRM_ERROR("Too large system memory stride. Stride: %d, " "Length: %d\n", xfer->mem_stride, xfer->line_length); return DRM_ERR(EINVAL); } - if (xfer->num_lines > 2048) { - DRM_ERROR("Too many PCI DMA bitblt lines.\n"); + if ((xfer->mem_stride == xfer->line_length) && + (xfer->fb_stride == xfer->line_length)) { + xfer->mem_stride *= xfer->num_lines; + xfer->line_length = xfer->mem_stride; + xfer->fb_stride = xfer->mem_stride; + xfer->num_lines = 1; + } + + /* + * Don't lock an arbitrary large number of pages, since that causes a + * DOS security hole. + */ + + if (xfer->num_lines > 2048 || (xfer->num_lines*xfer->mem_stride > (2048*2048*4))) { + DRM_ERROR("Too large PCI DMA bitblt.\n"); return DRM_ERR(EINVAL); } @@ -628,16 +649,17 @@ via_build_sg_info(drm_device_t *dev, drm_via_sg_info_t *vsg, drm_via_dmablit_t * #ifdef VIA_BUGFREE if ((((unsigned long)xfer->mem_addr & 3) != ((unsigned long)xfer->fb_addr & 3)) || - ((xfer->mem_stride & 3) != (xfer->fb_stride & 3))) { + ((xfer->num_lines > 1) && ((xfer->mem_stride & 3) != (xfer->fb_stride & 3)))) { DRM_ERROR("Invalid DRM bitblt alignment.\n"); - return DRM_ERR(EINVAL); + return DRM_ERR(EINVAL); } #else if ((((unsigned long)xfer->mem_addr & 15) || - ((unsigned long)xfer->fb_addr & 3)) || (xfer->mem_stride & 15) || - (xfer->fb_stride & 3)) { + ((unsigned long)xfer->fb_addr & 3)) || + ((xfer->num_lines > 1) && + ((xfer->mem_stride & 15) || (xfer->fb_stride & 3)))) { DRM_ERROR("Invalid DRM bitblt alignment.\n"); - return DRM_ERR(EINVAL); + return DRM_ERR(EINVAL); } #endif @@ -715,7 +737,7 @@ via_dmablit(drm_device_t *dev, drm_via_dmablit_t *xfer) drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; drm_via_sg_info_t *vsg; drm_via_blitq_t *blitq; - int ret; + int ret; int engine; unsigned long irqsave; @@ -756,7 +778,7 @@ via_dmablit(drm_device_t *dev, drm_via_dmablit_t *xfer) /* * Sync on a previously submitted blit. Note that the X server use signals extensively, and - * that there is a very big proability that this IOCTL will be interrupted by a signal. In that + * that there is a very big probability that this IOCTL will be interrupted by a signal. In that * case it returns with -EAGAIN for the signal to be delivered. * The caller should then reissue the IOCTL. This is similar to what is being done for drmGetLock(). */ diff --git a/drivers/char/drm/via_drm.h b/drivers/char/drm/via_drm.h index 47f0b5b2637..e4ee97d7156 100644 --- a/drivers/char/drm/via_drm.h +++ b/drivers/char/drm/via_drm.h @@ -250,6 +250,12 @@ typedef struct drm_via_blitsync { unsigned engine; } drm_via_blitsync_t; +/* - * Below,"flags" is currently unused but will be used for possible future + * extensions like kernel space bounce buffers for bad alignments and + * blit engine busy-wait polling for better latency in the absence of + * interrupts. + */ + typedef struct drm_via_dmablit { uint32_t num_lines; uint32_t line_length; @@ -260,7 +266,7 @@ typedef struct drm_via_dmablit { unsigned char *mem_addr; uint32_t mem_stride; - int bounce_buffer; + uint32_t flags; int to_fb; drm_via_blitsync_t sync; diff --git a/drivers/char/drm/via_drv.c b/drivers/char/drm/via_drv.c index b3d364d793d..bb9dde8b191 100644 --- a/drivers/char/drm/via_drv.c +++ b/drivers/char/drm/via_drv.c @@ -43,7 +43,6 @@ static struct drm_driver driver = { DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL, .load = via_driver_load, .unload = via_driver_unload, - .context_ctor = via_init_context, .context_dtor = via_final_context, .vblank_wait = via_driver_vblank_wait, .irq_preinstall = via_driver_irq_preinstall, @@ -53,6 +52,8 @@ static struct drm_driver driver = { .dma_quiescent = via_driver_dma_quiescent, .dri_library_name = dri_library_name, .reclaim_buffers = drm_core_reclaim_buffers, + .reclaim_buffers_locked = via_reclaim_buffers_locked, + .lastclose = via_lastclose, .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, .ioctls = via_ioctls, diff --git a/drivers/char/drm/via_drv.h b/drivers/char/drm/via_drv.h index 52bcc7b1ba4..d21b5b75da0 100644 --- a/drivers/char/drm/via_drv.h +++ b/drivers/char/drm/via_drv.h @@ -24,15 +24,16 @@ #ifndef _VIA_DRV_H_ #define _VIA_DRV_H_ +#include "drm_sman.h" #define DRIVER_AUTHOR "Various" #define DRIVER_NAME "via" #define DRIVER_DESC "VIA Unichrome / Pro" -#define DRIVER_DATE "20051116" +#define DRIVER_DATE "20060529" #define DRIVER_MAJOR 2 -#define DRIVER_MINOR 7 -#define DRIVER_PATCHLEVEL 4 +#define DRIVER_MINOR 10 +#define DRIVER_PATCHLEVEL 0 #include "via_verifier.h" @@ -85,6 +86,12 @@ typedef struct drm_via_private { uint32_t irq_enable_mask; uint32_t irq_pending_mask; int *irq_map; + unsigned int idle_fault; + drm_sman_t sman; + int vram_initialized; + int agp_initialized; + unsigned long vram_offset; + unsigned long agp_offset; drm_via_blitq_t blit_queues[VIA_NUM_BLIT_ENGINES]; } drm_via_private_t; @@ -135,6 +142,9 @@ extern void via_init_futex(drm_via_private_t * dev_priv); extern void via_cleanup_futex(drm_via_private_t * dev_priv); extern void via_release_futex(drm_via_private_t * dev_priv, int context); +extern void via_reclaim_buffers_locked(drm_device_t *dev, struct file *filp); +extern void via_lastclose(drm_device_t *dev); + extern void via_dmablit_handler(drm_device_t *dev, int engine, int from_irq); extern void via_init_dmablit(drm_device_t *dev); diff --git a/drivers/char/drm/via_ds.c b/drivers/char/drm/via_ds.c deleted file mode 100644 index 9429736b3b9..00000000000 --- a/drivers/char/drm/via_ds.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. - * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. - * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#include "drmP.h" - -#include "via_ds.h" -extern unsigned int VIA_DEBUG; - -set_t *via_setInit(void) -{ - int i; - set_t *set; - set = (set_t *) drm_alloc(sizeof(set_t), DRM_MEM_DRIVER); - for (i = 0; i < SET_SIZE; i++) { - set->list[i].free_next = i + 1; - set->list[i].alloc_next = -1; - } - set->list[SET_SIZE - 1].free_next = -1; - set->free = 0; - set->alloc = -1; - set->trace = -1; - return set; -} - -int via_setAdd(set_t * set, ITEM_TYPE item) -{ - int free = set->free; - if (free != -1) { - set->list[free].val = item; - set->free = set->list[free].free_next; - } else { - return 0; - } - set->list[free].alloc_next = set->alloc; - set->alloc = free; - set->list[free].free_next = -1; - return 1; -} - -int via_setDel(set_t * set, ITEM_TYPE item) -{ - int alloc = set->alloc; - int prev = -1; - - while (alloc != -1) { - if (set->list[alloc].val == item) { - if (prev != -1) - set->list[prev].alloc_next = - set->list[alloc].alloc_next; - else - set->alloc = set->list[alloc].alloc_next; - break; - } - prev = alloc; - alloc = set->list[alloc].alloc_next; - } - - if (alloc == -1) - return 0; - - set->list[alloc].free_next = set->free; - set->free = alloc; - set->list[alloc].alloc_next = -1; - - return 1; -} - -/* setFirst -> setAdd -> setNext is wrong */ - -int via_setFirst(set_t * set, ITEM_TYPE * item) -{ - if (set->alloc == -1) - return 0; - - *item = set->list[set->alloc].val; - set->trace = set->list[set->alloc].alloc_next; - - return 1; -} - -int via_setNext(set_t * set, ITEM_TYPE * item) -{ - if (set->trace == -1) - return 0; - - *item = set->list[set->trace].val; - set->trace = set->list[set->trace].alloc_next; - - return 1; -} - -int via_setDestroy(set_t * set) -{ - drm_free(set, sizeof(set_t), DRM_MEM_DRIVER); - - return 1; -} - -#define ISFREE(bptr) ((bptr)->free) - -#define fprintf(fmt, arg...) do{}while(0) - -memHeap_t *via_mmInit(int ofs, int size) -{ - PMemBlock blocks; - - if (size <= 0) - return NULL; - - blocks = (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), DRM_MEM_DRIVER); - - if (blocks) { - blocks->ofs = ofs; - blocks->size = size; - blocks->free = 1; - return (memHeap_t *) blocks; - } else - return NULL; -} - -static TMemBlock *SliceBlock(TMemBlock * p, - int startofs, int size, - int reserved, int alignment) -{ - TMemBlock *newblock; - - /* break left */ - if (startofs > p->ofs) { - newblock = - (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), - DRM_MEM_DRIVER); - newblock->ofs = startofs; - newblock->size = p->size - (startofs - p->ofs); - newblock->free = 1; - newblock->next = p->next; - p->size -= newblock->size; - p->next = newblock; - p = newblock; - } - - /* break right */ - if (size < p->size) { - newblock = - (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), - DRM_MEM_DRIVER); - newblock->ofs = startofs + size; - newblock->size = p->size - size; - newblock->free = 1; - newblock->next = p->next; - p->size = size; - p->next = newblock; - } - - /* p = middle block */ - p->align = alignment; - p->free = 0; - p->reserved = reserved; - return p; -} - -PMemBlock via_mmAllocMem(memHeap_t * heap, int size, int align2, - int startSearch) -{ - int mask, startofs, endofs; - TMemBlock *p; - - if (!heap || align2 < 0 || size <= 0) - return NULL; - - mask = (1 << align2) - 1; - startofs = 0; - p = (TMemBlock *) heap; - - while (p) { - if (ISFREE(p)) { - startofs = (p->ofs + mask) & ~mask; - - if (startofs < startSearch) - startofs = startSearch; - - endofs = startofs + size; - - if (endofs <= (p->ofs + p->size)) - break; - } - - p = p->next; - } - - if (!p) - return NULL; - - p = SliceBlock(p, startofs, size, 0, mask + 1); - p->heap = heap; - - return p; -} - -static __inline__ int Join2Blocks(TMemBlock * p) -{ - if (p->free && p->next && p->next->free) { - TMemBlock *q = p->next; - p->size += q->size; - p->next = q->next; - drm_free(q, sizeof(TMemBlock), DRM_MEM_DRIVER); - - return 1; - } - - return 0; -} - -int via_mmFreeMem(PMemBlock b) -{ - TMemBlock *p, *prev; - - if (!b) - return 0; - - if (!b->heap) { - fprintf(stderr, "no heap\n"); - - return -1; - } - - p = b->heap; - prev = NULL; - - while (p && p != b) { - prev = p; - p = p->next; - } - - if (!p || p->free || p->reserved) { - if (!p) - fprintf(stderr, "block not found in heap\n"); - else if (p->free) - fprintf(stderr, "block already free\n"); - else - fprintf(stderr, "block is reserved\n"); - - return -1; - } - - p->free = 1; - Join2Blocks(p); - - if (prev) - Join2Blocks(prev); - - return 0; -} diff --git a/drivers/char/drm/via_ds.h b/drivers/char/drm/via_ds.h deleted file mode 100644 index d2bb9f37ca3..00000000000 --- a/drivers/char/drm/via_ds.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. - * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. - * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan. - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#ifndef _via_ds_h_ -#define _via_ds_h_ - -#include "drmP.h" - -/* Set Data Structure */ -#define SET_SIZE 5000 -typedef unsigned long ITEM_TYPE; - -typedef struct { - ITEM_TYPE val; - int alloc_next, free_next; -} list_item_t; - -typedef struct { - int alloc; - int free; - int trace; - list_item_t list[SET_SIZE]; -} set_t; - -set_t *via_setInit(void); -int via_setAdd(set_t * set, ITEM_TYPE item); -int via_setDel(set_t * set, ITEM_TYPE item); -int via_setFirst(set_t * set, ITEM_TYPE * item); -int via_setNext(set_t * set, ITEM_TYPE * item); -int via_setDestroy(set_t * set); - -#endif - -#ifndef MM_INC -#define MM_INC - -struct mem_block_t { - struct mem_block_t *next; - struct mem_block_t *heap; - int ofs, size; - int align; - unsigned int free:1; - unsigned int reserved:1; -}; -typedef struct mem_block_t TMemBlock; -typedef struct mem_block_t *PMemBlock; - -/* a heap is just the first block in a chain */ -typedef struct mem_block_t memHeap_t; - -static __inline__ int mmBlockSize(PMemBlock b) -{ - return b->size; -} - -static __inline__ int mmOffset(PMemBlock b) -{ - return b->ofs; -} - -static __inline__ void mmMarkReserved(PMemBlock b) -{ - b->reserved = 1; -} - -/* - * input: total size in bytes - * return: a heap pointer if OK, NULL if error - */ -memHeap_t *via_mmInit(int ofs, int size); - -PMemBlock via_mmAllocMem(memHeap_t * heap, int size, int align2, - int startSearch); - -/* - * Free block starts at offset - * input: pointer to a block - * return: 0 if OK, -1 if error - */ -int via_mmFreeMem(PMemBlock b); - -#endif diff --git a/drivers/char/drm/via_map.c b/drivers/char/drm/via_map.c index c6a08e96285..782011e0a58 100644 --- a/drivers/char/drm/via_map.c +++ b/drivers/char/drm/via_map.c @@ -98,6 +98,7 @@ int via_map_init(DRM_IOCTL_ARGS) int via_driver_load(drm_device_t *dev, unsigned long chipset) { drm_via_private_t *dev_priv; + int ret = 0; dev_priv = drm_calloc(1, sizeof(drm_via_private_t), DRM_MEM_DRIVER); if (dev_priv == NULL) @@ -108,13 +109,19 @@ int via_driver_load(drm_device_t *dev, unsigned long chipset) if (chipset == VIA_PRO_GROUP_A) dev_priv->pro_group_a = 1; - return 0; + ret = drm_sman_init(&dev_priv->sman, 2, 12, 8); + if (ret) { + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + } + return ret; } int via_driver_unload(drm_device_t *dev) { drm_via_private_t *dev_priv = dev->dev_private; + drm_sman_takedown(&dev_priv->sman); + drm_free(dev_priv, sizeof(drm_via_private_t), DRM_MEM_DRIVER); return 0; diff --git a/drivers/char/drm/via_mm.c b/drivers/char/drm/via_mm.c index 33e0cb12e4c..2fcf0577a7a 100644 --- a/drivers/char/drm/via_mm.c +++ b/drivers/char/drm/via_mm.c @@ -1,6 +1,6 @@ /* - * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. - * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. + * Copyright 2006 Tungsten Graphics Inc., Bismarck, ND., USA. + * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -16,347 +16,194 @@ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * THE AUTHORS OR COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +/* + * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + #include "drmP.h" #include "via_drm.h" #include "via_drv.h" -#include "via_ds.h" -#include "via_mm.h" - -#define MAX_CONTEXT 100 - -typedef struct { - int used; - int context; - set_t *sets[2]; /* 0 for frame buffer, 1 for AGP , 2 for System */ -} via_context_t; - -static via_context_t global_ppriv[MAX_CONTEXT]; +#include "drm_sman.h" -static int via_agp_alloc(drm_via_mem_t * mem); -static int via_agp_free(drm_via_mem_t * mem); -static int via_fb_alloc(drm_via_mem_t * mem); -static int via_fb_free(drm_via_mem_t * mem); - -static int add_alloc_set(int context, int type, unsigned long val) -{ - int i, retval = 0; - - for (i = 0; i < MAX_CONTEXT; i++) { - if (global_ppriv[i].used && global_ppriv[i].context == context) { - retval = via_setAdd(global_ppriv[i].sets[type], val); - break; - } - } - - return retval; -} - -static int del_alloc_set(int context, int type, unsigned long val) -{ - int i, retval = 0; - - for (i = 0; i < MAX_CONTEXT; i++) - if (global_ppriv[i].used && global_ppriv[i].context == context) { - retval = via_setDel(global_ppriv[i].sets[type], val); - break; - } - - return retval; -} - -/* agp memory management */ -static memHeap_t *AgpHeap = NULL; +#define VIA_MM_ALIGN_SHIFT 4 +#define VIA_MM_ALIGN_MASK ( (1 << VIA_MM_ALIGN_SHIFT) - 1) int via_agp_init(DRM_IOCTL_ARGS) { + DRM_DEVICE; drm_via_agp_t agp; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + int ret; DRM_COPY_FROM_USER_IOCTL(agp, (drm_via_agp_t __user *) data, sizeof(agp)); + mutex_lock(&dev->struct_mutex); + ret = drm_sman_set_range(&dev_priv->sman, VIA_MEM_AGP, 0, + agp.size >> VIA_MM_ALIGN_SHIFT); + + if (ret) { + DRM_ERROR("AGP memory manager initialisation error\n"); + mutex_unlock(&dev->struct_mutex); + return ret; + } - AgpHeap = via_mmInit(agp.offset, agp.size); - - DRM_DEBUG("offset = %lu, size = %lu", (unsigned long)agp.offset, - (unsigned long)agp.size); + dev_priv->agp_initialized = 1; + dev_priv->agp_offset = agp.offset; + mutex_unlock(&dev->struct_mutex); + DRM_DEBUG("offset = %u, size = %u", agp.offset, agp.size); return 0; } -/* fb memory management */ -static memHeap_t *FBHeap = NULL; - int via_fb_init(DRM_IOCTL_ARGS) { + DRM_DEVICE; drm_via_fb_t fb; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + int ret; DRM_COPY_FROM_USER_IOCTL(fb, (drm_via_fb_t __user *) data, sizeof(fb)); - FBHeap = via_mmInit(fb.offset, fb.size); + mutex_lock(&dev->struct_mutex); + ret = drm_sman_set_range(&dev_priv->sman, VIA_MEM_VIDEO, 0, + fb.size >> VIA_MM_ALIGN_SHIFT); - DRM_DEBUG("offset = %lu, size = %lu", (unsigned long)fb.offset, - (unsigned long)fb.size); + if (ret) { + DRM_ERROR("VRAM memory manager initialisation error\n"); + mutex_unlock(&dev->struct_mutex); + return ret; + } - return 0; -} + dev_priv->vram_initialized = 1; + dev_priv->vram_offset = fb.offset; -int via_init_context(struct drm_device *dev, int context) -{ - int i; - - for (i = 0; i < MAX_CONTEXT; i++) - if (global_ppriv[i].used && - (global_ppriv[i].context == context)) - break; - - if (i >= MAX_CONTEXT) { - for (i = 0; i < MAX_CONTEXT; i++) { - if (!global_ppriv[i].used) { - global_ppriv[i].context = context; - global_ppriv[i].used = 1; - global_ppriv[i].sets[0] = via_setInit(); - global_ppriv[i].sets[1] = via_setInit(); - DRM_DEBUG("init allocation set, socket=%d," - " context = %d\n", i, context); - break; - } - } - - if ((i >= MAX_CONTEXT) || (global_ppriv[i].sets[0] == NULL) || - (global_ppriv[i].sets[1] == NULL)) { - return 0; - } - } + mutex_unlock(&dev->struct_mutex); + DRM_DEBUG("offset = %u, size = %u", fb.offset, fb.size); + + return 0; - return 1; } int via_final_context(struct drm_device *dev, int context) { - int i; drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - for (i = 0; i < MAX_CONTEXT; i++) - if (global_ppriv[i].used && - (global_ppriv[i].context == context)) - break; - - if (i < MAX_CONTEXT) { - set_t *set; - ITEM_TYPE item; - int retval; - - DRM_DEBUG("find socket %d, context = %d\n", i, context); - - /* Video Memory */ - set = global_ppriv[i].sets[0]; - retval = via_setFirst(set, &item); - while (retval) { - DRM_DEBUG("free video memory 0x%lx\n", item); - via_mmFreeMem((PMemBlock) item); - retval = via_setNext(set, &item); - } - via_setDestroy(set); - - /* AGP Memory */ - set = global_ppriv[i].sets[1]; - retval = via_setFirst(set, &item); - while (retval) { - DRM_DEBUG("free agp memory 0x%lx\n", item); - via_mmFreeMem((PMemBlock) item); - retval = via_setNext(set, &item); - } - via_setDestroy(set); - global_ppriv[i].used = 0; - } via_release_futex(dev_priv, context); -#if defined(__linux__) /* Linux specific until context tracking code gets ported to BSD */ /* Last context, perform cleanup */ if (dev->ctx_count == 1 && dev->dev_private) { DRM_DEBUG("Last Context\n"); if (dev->irq) drm_irq_uninstall(dev); - via_cleanup_futex(dev_priv); via_do_cleanup_map(dev); } -#endif - return 1; } +void via_lastclose(struct drm_device *dev) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + + if (!dev_priv) + return; + + mutex_lock(&dev->struct_mutex); + drm_sman_cleanup(&dev_priv->sman); + dev_priv->vram_initialized = 0; + dev_priv->agp_initialized = 0; + mutex_unlock(&dev->struct_mutex); +} + int via_mem_alloc(DRM_IOCTL_ARGS) { + DRM_DEVICE; + drm_via_mem_t mem; + int retval = 0; + drm_memblock_item_t *item; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + unsigned long tmpSize; DRM_COPY_FROM_USER_IOCTL(mem, (drm_via_mem_t __user *) data, sizeof(mem)); - switch (mem.type) { - case VIA_MEM_VIDEO: - if (via_fb_alloc(&mem) < 0) - return -EFAULT; - DRM_COPY_TO_USER_IOCTL((drm_via_mem_t __user *) data, mem, - sizeof(mem)); - return 0; - case VIA_MEM_AGP: - if (via_agp_alloc(&mem) < 0) - return -EFAULT; - DRM_COPY_TO_USER_IOCTL((drm_via_mem_t __user *) data, mem, - sizeof(mem)); - return 0; + if (mem.type > VIA_MEM_AGP) { + DRM_ERROR("Unknown memory type allocation\n"); + return DRM_ERR(EINVAL); } - - return -EFAULT; -} - -static int via_fb_alloc(drm_via_mem_t * mem) -{ - drm_via_mm_t fb; - PMemBlock block; - int retval = 0; - - if (!FBHeap) - return -1; - - fb.size = mem->size; - fb.context = mem->context; - - block = via_mmAllocMem(FBHeap, fb.size, 5, 0); - if (block) { - fb.offset = block->ofs; - fb.free = (unsigned long)block; - if (!add_alloc_set(fb.context, VIA_MEM_VIDEO, fb.free)) { - DRM_DEBUG("adding to allocation set fails\n"); - via_mmFreeMem((PMemBlock) fb.free); - retval = -1; - } - } else { - fb.offset = 0; - fb.size = 0; - fb.free = 0; - retval = -1; + mutex_lock(&dev->struct_mutex); + if (0 == ((mem.type == VIA_MEM_VIDEO) ? dev_priv->vram_initialized : + dev_priv->agp_initialized)) { + DRM_ERROR + ("Attempt to allocate from uninitialized memory manager.\n"); + mutex_unlock(&dev->struct_mutex); + return DRM_ERR(EINVAL); } - mem->offset = fb.offset; - mem->index = fb.free; - - DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size, - (int)fb.offset); - - return retval; -} - -static int via_agp_alloc(drm_via_mem_t * mem) -{ - drm_via_mm_t agp; - PMemBlock block; - int retval = 0; - - if (!AgpHeap) - return -1; - - agp.size = mem->size; - agp.context = mem->context; - - block = via_mmAllocMem(AgpHeap, agp.size, 5, 0); - if (block) { - agp.offset = block->ofs; - agp.free = (unsigned long)block; - if (!add_alloc_set(agp.context, VIA_MEM_AGP, agp.free)) { - DRM_DEBUG("adding to allocation set fails\n"); - via_mmFreeMem((PMemBlock) agp.free); - retval = -1; - } + tmpSize = (mem.size + VIA_MM_ALIGN_MASK) >> VIA_MM_ALIGN_SHIFT; + item = drm_sman_alloc(&dev_priv->sman, mem.type, tmpSize, 0, + (unsigned long)priv); + mutex_unlock(&dev->struct_mutex); + if (item) { + mem.offset = ((mem.type == VIA_MEM_VIDEO) ? + dev_priv->vram_offset : dev_priv->agp_offset) + + (item->mm-> + offset(item->mm, item->mm_info) << VIA_MM_ALIGN_SHIFT); + mem.index = item->user_hash.key; } else { - agp.offset = 0; - agp.size = 0; - agp.free = 0; + mem.offset = 0; + mem.size = 0; + mem.index = 0; + DRM_DEBUG("Video memory allocation failed\n"); + retval = DRM_ERR(ENOMEM); } + DRM_COPY_TO_USER_IOCTL((drm_via_mem_t __user *) data, mem, sizeof(mem)); - mem->offset = agp.offset; - mem->index = agp.free; - - DRM_DEBUG("alloc agp, size = %d, offset = %d\n", agp.size, - (unsigned int)agp.offset); return retval; } int via_mem_free(DRM_IOCTL_ARGS) { + DRM_DEVICE; + drm_via_private_t *dev_priv = dev->dev_private; drm_via_mem_t mem; + int ret; DRM_COPY_FROM_USER_IOCTL(mem, (drm_via_mem_t __user *) data, sizeof(mem)); - switch (mem.type) { + mutex_lock(&dev->struct_mutex); + ret = drm_sman_free_key(&dev_priv->sman, mem.index); + mutex_unlock(&dev->struct_mutex); + DRM_DEBUG("free = 0x%lx\n", mem.index); - case VIA_MEM_VIDEO: - if (via_fb_free(&mem) == 0) - return 0; - break; - case VIA_MEM_AGP: - if (via_agp_free(&mem) == 0) - return 0; - break; - } - - return -EFAULT; + return ret; } -static int via_fb_free(drm_via_mem_t * mem) -{ - drm_via_mm_t fb; - int retval = 0; - - if (!FBHeap) { - return -1; - } - - fb.free = mem->index; - fb.context = mem->context; - - if (!fb.free) { - return -1; - - } - - via_mmFreeMem((PMemBlock) fb.free); - - if (!del_alloc_set(fb.context, VIA_MEM_VIDEO, fb.free)) { - retval = -1; - } - - DRM_DEBUG("free fb, free = %ld\n", fb.free); - return retval; -} - -static int via_agp_free(drm_via_mem_t * mem) +void via_reclaim_buffers_locked(drm_device_t * dev, struct file *filp) { - drm_via_mm_t agp; - - int retval = 0; + drm_via_private_t *dev_priv = dev->dev_private; + drm_file_t *priv = filp->private_data; - agp.free = mem->index; - agp.context = mem->context; - - if (!agp.free) - return -1; - - via_mmFreeMem((PMemBlock) agp.free); - - if (!del_alloc_set(agp.context, VIA_MEM_AGP, agp.free)) { - retval = -1; + mutex_lock(&dev->struct_mutex); + if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)priv)) { + mutex_unlock(&dev->struct_mutex); + return; } - DRM_DEBUG("free agp, free = %ld\n", agp.free); + if (dev->driver->dma_quiescent) { + dev->driver->dma_quiescent(dev); + } - return retval; + drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)priv); + mutex_unlock(&dev->struct_mutex); + return; } diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 5e59c0b4273..4711d9b3a59 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -746,11 +746,9 @@ void gs_set_termios (struct tty_struct * tty, gs_dprintk (GS_DEBUG_TERMIOS, "termios structure (%p):\n", tiosp); } -#if 0 /* This is an optimization that is only allowed for dumb cards */ /* Smart cards require knowledge of iflags and oflags too: that might change hardware cooking mode.... */ -#endif if (old_termios) { if( (tiosp->c_iflag == old_termios->c_iflag) && (tiosp->c_oflag == old_termios->c_oflag) @@ -774,14 +772,7 @@ void gs_set_termios (struct tty_struct * tty, if(!memcmp(tiosp->c_cc, old_termios->c_cc, NCC)) printk("c_cc changed\n"); } - baudrate = tiosp->c_cflag & CBAUD; - if (baudrate & CBAUDEX) { - baudrate &= ~CBAUDEX; - if ((baudrate < 1) || (baudrate > 4)) - tiosp->c_cflag &= ~CBAUDEX; - else - baudrate += 15; - } + baudrate = tty_get_baud_rate(tty); baudrate = gs_baudrates[baudrate]; if ((tiosp->c_cflag & CBAUD) == B38400) { diff --git a/drivers/char/hw_random/intel-rng.c b/drivers/char/hw_random/intel-rng.c index ccd7e710223..8efbc9c0e54 100644 --- a/drivers/char/hw_random/intel-rng.c +++ b/drivers/char/hw_random/intel-rng.c @@ -50,6 +50,43 @@ #define INTEL_RNG_ADDR_LEN 3 /* + * LPC bridge PCI config space registers + */ +#define FWH_DEC_EN1_REG_OLD 0xe3 +#define FWH_DEC_EN1_REG_NEW 0xd9 /* high byte of 16-bit register */ +#define FWH_F8_EN_MASK 0x80 + +#define BIOS_CNTL_REG_OLD 0x4e +#define BIOS_CNTL_REG_NEW 0xdc +#define BIOS_CNTL_WRITE_ENABLE_MASK 0x01 +#define BIOS_CNTL_LOCK_ENABLE_MASK 0x02 + +/* + * Magic address at which Intel Firmware Hubs get accessed + */ +#define INTEL_FWH_ADDR 0xffff0000 +#define INTEL_FWH_ADDR_LEN 2 + +/* + * Intel Firmware Hub command codes (write to any address inside the device) + */ +#define INTEL_FWH_RESET_CMD 0xff /* aka READ_ARRAY */ +#define INTEL_FWH_READ_ID_CMD 0x90 + +/* + * Intel Firmware Hub Read ID command result addresses + */ +#define INTEL_FWH_MANUFACTURER_CODE_ADDRESS 0x000000 +#define INTEL_FWH_DEVICE_CODE_ADDRESS 0x000001 + +/* + * Intel Firmware Hub Read ID command result values + */ +#define INTEL_FWH_MANUFACTURER_CODE 0x89 +#define INTEL_FWH_DEVICE_CODE_8M 0xac +#define INTEL_FWH_DEVICE_CODE_4M 0xad + +/* * Data for PCI driver interface * * This data only exists for exporting the supported @@ -58,12 +95,50 @@ * want to register another driver on the same PCI id. */ static const struct pci_device_id pci_tbl[] = { - { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, +/* AA + { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ + { 0x8086, 0x2410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* AA */ +/* AB + { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ + { 0x8086, 0x2420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* AB */ +/* ?? + { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ +/* BAM, CAM, DBM, FBM, GxM + { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ + { 0x8086, 0x244c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* BAM */ + { 0x8086, 0x248c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CAM */ + { 0x8086, 0x24cc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* DBM */ + { 0x8086, 0x2641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* FBM */ + { 0x8086, 0x27b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* GxM */ + { 0x8086, 0x27bd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* GxM DH */ +/* BA, CA, DB, Ex, 6300, Fx, 631x/632x, Gx + { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ + { 0x8086, 0x2440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* BA */ + { 0x8086, 0x2480, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CA */ + { 0x8086, 0x24c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* DB */ + { 0x8086, 0x24d0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Ex */ + { 0x8086, 0x25a1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 6300 */ + { 0x8086, 0x2640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Fx */ + { 0x8086, 0x2670, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2671, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2672, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2673, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2674, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2675, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2676, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2677, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2678, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x2679, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x267a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x267b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x267c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x267d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x267e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x267f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ + { 0x8086, 0x27b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Gx */ +/* E + { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ + { 0x8086, 0x2450, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* E */ { 0, }, /* terminate list */ }; MODULE_DEVICE_TABLE(pci, pci_tbl); @@ -138,22 +213,115 @@ static struct hwrng intel_rng = { }; +#ifdef CONFIG_SMP +static char __initdata waitflag; + +static void __init intel_init_wait(void *unused) +{ + while (waitflag) + cpu_relax(); +} +#endif + static int __init mod_init(void) { int err = -ENODEV; + unsigned i; + struct pci_dev *dev = NULL; void __iomem *mem; - u8 hw_status; + unsigned long flags; + u8 bios_cntl_off, fwh_dec_en1_off; + u8 bios_cntl_val = 0xff, fwh_dec_en1_val = 0xff; + u8 hw_status, mfc, dvc; - if (!pci_dev_present(pci_tbl)) + for (i = 0; !dev && pci_tbl[i].vendor; ++i) + dev = pci_get_device(pci_tbl[i].vendor, pci_tbl[i].device, NULL); + + if (!dev) goto out; /* Device not found. */ + /* Check for Intel 82802 */ + if (dev->device < 0x2640) { + fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD; + bios_cntl_off = BIOS_CNTL_REG_OLD; + } else { + fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW; + bios_cntl_off = BIOS_CNTL_REG_NEW; + } + + pci_read_config_byte(dev, fwh_dec_en1_off, &fwh_dec_en1_val); + pci_read_config_byte(dev, bios_cntl_off, &bios_cntl_val); + + mem = ioremap_nocache(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN); + if (mem == NULL) { + pci_dev_put(dev); + err = -EBUSY; + goto out; + } + + /* + * Since the BIOS code/data is going to disappear from its normal + * location with the Read ID command, all activity on the system + * must be stopped until the state is back to normal. + */ +#ifdef CONFIG_SMP + set_mb(waitflag, 1); + if (smp_call_function(intel_init_wait, NULL, 1, 0) != 0) { + set_mb(waitflag, 0); + pci_dev_put(dev); + printk(KERN_ERR PFX "cannot run on all processors\n"); + err = -EAGAIN; + goto err_unmap; + } +#endif + local_irq_save(flags); + + if (!(fwh_dec_en1_val & FWH_F8_EN_MASK)) + pci_write_config_byte(dev, + fwh_dec_en1_off, + fwh_dec_en1_val | FWH_F8_EN_MASK); + if (!(bios_cntl_val & + (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))) + pci_write_config_byte(dev, + bios_cntl_off, + bios_cntl_val | BIOS_CNTL_WRITE_ENABLE_MASK); + + writeb(INTEL_FWH_RESET_CMD, mem); + writeb(INTEL_FWH_READ_ID_CMD, mem); + mfc = readb(mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS); + dvc = readb(mem + INTEL_FWH_DEVICE_CODE_ADDRESS); + writeb(INTEL_FWH_RESET_CMD, mem); + + if (!(bios_cntl_val & + (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))) + pci_write_config_byte(dev, bios_cntl_off, bios_cntl_val); + if (!(fwh_dec_en1_val & FWH_F8_EN_MASK)) + pci_write_config_byte(dev, fwh_dec_en1_off, fwh_dec_en1_val); + + local_irq_restore(flags); +#ifdef CONFIG_SMP + /* Tell other CPUs to resume. */ + set_mb(waitflag, 0); +#endif + + iounmap(mem); + pci_dev_put(dev); + + if (mfc != INTEL_FWH_MANUFACTURER_CODE || + (dvc != INTEL_FWH_DEVICE_CODE_8M && + dvc != INTEL_FWH_DEVICE_CODE_4M)) { + printk(KERN_ERR PFX "FWH not detected\n"); + err = -ENODEV; + goto out; + } + err = -ENOMEM; mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN); if (!mem) goto out; intel_rng.priv = (unsigned long)mem; - /* Check for Intel 82802 */ + /* Check for Random Number Generator */ err = -ENODEV; hw_status = hwstatus_get(mem); if ((hw_status & INTEL_RNG_PRESENT) == 0) diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 8c09997cc3d..6b4d82a4565 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -612,16 +612,6 @@ MODULE_DEVICE_TABLE(pci, istallion_pci_tbl); #define MINOR2BRD(min) (((min) & 0xc0) >> 6) #define MINOR2PORT(min) ((min) & 0x3f) -/* - * Define a baud rate table that converts termios baud rate selector - * into the actual baud rate value. All baud rate calculations are based - * on the actual baud rate required. - */ -static unsigned int stli_baudrates[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 -}; - /*****************************************************************************/ /* @@ -2747,15 +2737,7 @@ static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tio /* * Start of by setting the baud, char size, parity and stop bit info. */ - pp->baudout = tiosp->c_cflag & CBAUD; - if (pp->baudout & CBAUDEX) { - pp->baudout &= ~CBAUDEX; - if ((pp->baudout < 1) || (pp->baudout > 4)) - tiosp->c_cflag &= ~CBAUDEX; - else - pp->baudout += 15; - } - pp->baudout = stli_baudrates[pp->baudout]; + pp->baudout = tty_get_baud_rate(portp->tty); if ((tiosp->c_cflag & CBAUD) == B38400) { if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) pp->baudout = 57600; diff --git a/drivers/char/lp.c b/drivers/char/lp.c index f875fda3b08..1ecea7d448f 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -906,7 +906,7 @@ static int __init lp_init (void) lp_class = class_create(THIS_MODULE, "printer"); if (IS_ERR(lp_class)) { err = PTR_ERR(lp_class); - goto out_devfs; + goto out_reg; } if (parport_register_driver (&lp_driver)) { @@ -927,7 +927,7 @@ static int __init lp_init (void) out_class: class_destroy(lp_class); -out_devfs: +out_reg: unregister_chrdev(LP_MAJOR, "lp"); return err; } diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 4ac70ec697f..6511012cbdc 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -551,7 +551,7 @@ static ssize_t write_kmem(struct file * file, const char __user * buf, return virtr + wrote; } -#if defined(CONFIG_ISA) || !defined(__mc68000__) +#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__) static ssize_t read_port(struct file * file, char __user * buf, size_t count, loff_t *ppos) { @@ -830,7 +830,7 @@ static const struct file_operations null_fops = { .splice_write = splice_write_null, }; -#if defined(CONFIG_ISA) || !defined(__mc68000__) +#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__) static const struct file_operations port_fops = { .llseek = memory_lseek, .read = read_port, @@ -908,7 +908,7 @@ static int memory_open(struct inode * inode, struct file * filp) case 3: filp->f_op = &null_fops; break; -#if defined(CONFIG_ISA) || !defined(__mc68000__) +#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__) case 4: filp->f_op = &port_fops; break; @@ -955,7 +955,7 @@ static const struct { {1, "mem", S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops}, {2, "kmem", S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops}, {3, "null", S_IRUGO | S_IWUGO, &null_fops}, -#if defined(CONFIG_ISA) || !defined(__mc68000__) +#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__) {4, "port", S_IRUSR | S_IWUSR | S_IRGRP, &port_fops}, #endif {5, "zero", S_IRUGO | S_IWUGO, &zero_fops}, diff --git a/drivers/char/pc8736x_gpio.c b/drivers/char/pc8736x_gpio.c index 84e5a68635f..ecfaf180e5b 100644 --- a/drivers/char/pc8736x_gpio.c +++ b/drivers/char/pc8736x_gpio.c @@ -188,16 +188,6 @@ static void pc8736x_gpio_set(unsigned minor, int val) pc8736x_gpio_shadow[port] = val; } -static void pc8736x_gpio_set_high(unsigned index) -{ - pc8736x_gpio_set(index, 1); -} - -static void pc8736x_gpio_set_low(unsigned index) -{ - pc8736x_gpio_set(index, 0); -} - static int pc8736x_gpio_current(unsigned minor) { int port, bit; diff --git a/drivers/char/random.c b/drivers/char/random.c index 4c3a5ca9d8f..b430a12eb81 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -655,6 +655,7 @@ void add_interrupt_randomness(int irq) add_timer_randomness(irq_timer_state[irq], 0x100 + irq); } +#ifdef CONFIG_BLOCK void add_disk_randomness(struct gendisk *disk) { if (!disk || !disk->random) @@ -667,6 +668,7 @@ void add_disk_randomness(struct gendisk *disk) } EXPORT_SYMBOL(add_disk_randomness); +#endif #define EXTRACT_SIZE 10 @@ -918,6 +920,7 @@ void rand_initialize_irq(int irq) } } +#ifdef CONFIG_BLOCK void rand_initialize_disk(struct gendisk *disk) { struct timer_rand_state *state; @@ -932,6 +935,7 @@ void rand_initialize_disk(struct gendisk *disk) disk->random = state; } } +#endif static ssize_t random_read(struct file * file, char __user * buf, size_t nbytes, loff_t *ppos) diff --git a/drivers/char/raw.c b/drivers/char/raw.c index 579868af4a5..c596a08c07b 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -288,31 +288,34 @@ static struct cdev raw_cdev = { static int __init raw_init(void) { dev_t dev = MKDEV(RAW_MAJOR, 0); + int ret; - if (register_chrdev_region(dev, MAX_RAW_MINORS, "raw")) + ret = register_chrdev_region(dev, MAX_RAW_MINORS, "raw"); + if (ret) goto error; cdev_init(&raw_cdev, &raw_fops); - if (cdev_add(&raw_cdev, dev, MAX_RAW_MINORS)) { + ret = cdev_add(&raw_cdev, dev, MAX_RAW_MINORS); + if (ret) { kobject_put(&raw_cdev.kobj); - unregister_chrdev_region(dev, MAX_RAW_MINORS); - goto error; + goto error_region; } raw_class = class_create(THIS_MODULE, "raw"); if (IS_ERR(raw_class)) { printk(KERN_ERR "Error creating raw class.\n"); cdev_del(&raw_cdev); - unregister_chrdev_region(dev, MAX_RAW_MINORS); - goto error; + ret = PTR_ERR(raw_class); + goto error_region; } class_device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl"); return 0; +error_region: + unregister_chrdev_region(dev, MAX_RAW_MINORS); error: - printk(KERN_ERR "error register raw device\n"); - return 1; + return ret; } static void __exit raw_exit(void) diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index ab6429b4a84..656f8c0ca52 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -1262,10 +1262,8 @@ void rtc_get_rtc_time(struct rtc_time *rtc_tm) * Once the read clears, read the RTC time (again via ioctl). Easy. */ - while (rtc_is_updating() != 0 && jiffies - uip_watchdog < 2*HZ/100) { - barrier(); + while (rtc_is_updating() != 0 && jiffies - uip_watchdog < 2*HZ/100) cpu_relax(); - } /* * Only the values that we read from the RTC are set. We leave diff --git a/drivers/char/s3c2410-rtc.c b/drivers/char/s3c2410-rtc.c deleted file mode 100644 index 5458ef1634e..00000000000 --- a/drivers/char/s3c2410-rtc.c +++ /dev/null @@ -1,591 +0,0 @@ -/* drivers/char/s3c2410_rtc.c - * - * Copyright (c) 2004 Simtec Electronics <linux@simtec.co.uk> - * http://www.simtec.co.uk/products/SWLINUX/ - * - * 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. - * - * S3C2410 Internal RTC Driver - * - * Changelog: - * 08-Nov-2004 BJD Initial creation - * 12-Nov-2004 BJD Added periodic IRQ and PM code - * 22-Nov-2004 BJD Sign-test on alarm code to check for <0 - * 10-Mar-2005 LCVR Changed S3C2410_VA_RTC to S3C24XX_VA_RTC -*/ - -#include <linux/module.h> -#include <linux/fs.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/rtc.h> -#include <linux/bcd.h> -#include <linux/clk.h> - -#include <asm/hardware.h> -#include <asm/uaccess.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/rtc.h> - -#include <asm/mach/time.h> - -#include <asm/arch/regs-rtc.h> - -/* need this for the RTC_AF definitions */ -#include <linux/mc146818rtc.h> - -#undef S3C24XX_VA_RTC -#define S3C24XX_VA_RTC s3c2410_rtc_base - -static struct resource *s3c2410_rtc_mem; - -static void __iomem *s3c2410_rtc_base; -static int s3c2410_rtc_alarmno = NO_IRQ; -static int s3c2410_rtc_tickno = NO_IRQ; -static int s3c2410_rtc_freq = 1; - -static DEFINE_SPINLOCK(s3c2410_rtc_pie_lock); - -/* IRQ Handlers */ - -static irqreturn_t s3c2410_rtc_alarmirq(int irq, void *id, struct pt_regs *r) -{ - rtc_update(1, RTC_AF | RTC_IRQF); - return IRQ_HANDLED; -} - -static irqreturn_t s3c2410_rtc_tickirq(int irq, void *id, struct pt_regs *r) -{ - rtc_update(1, RTC_PF | RTC_IRQF); - return IRQ_HANDLED; -} - -/* Update control registers */ -static void s3c2410_rtc_setaie(int to) -{ - unsigned int tmp; - - pr_debug("%s: aie=%d\n", __FUNCTION__, to); - - tmp = readb(S3C2410_RTCALM); - - if (to) - tmp |= S3C2410_RTCALM_ALMEN; - else - tmp &= ~S3C2410_RTCALM_ALMEN; - - - writeb(tmp, S3C2410_RTCALM); -} - -static void s3c2410_rtc_setpie(int to) -{ - unsigned int tmp; - - pr_debug("%s: pie=%d\n", __FUNCTION__, to); - - spin_lock_irq(&s3c2410_rtc_pie_lock); - tmp = readb(S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE; - - if (to) - tmp |= S3C2410_TICNT_ENABLE; - - writeb(tmp, S3C2410_TICNT); - spin_unlock_irq(&s3c2410_rtc_pie_lock); -} - -static void s3c2410_rtc_setfreq(int freq) -{ - unsigned int tmp; - - spin_lock_irq(&s3c2410_rtc_pie_lock); - tmp = readb(S3C2410_TICNT) & S3C2410_TICNT_ENABLE; - - s3c2410_rtc_freq = freq; - - tmp |= (128 / freq)-1; - - writeb(tmp, S3C2410_TICNT); - spin_unlock_irq(&s3c2410_rtc_pie_lock); -} - -/* Time read/write */ - -static int s3c2410_rtc_gettime(struct rtc_time *rtc_tm) -{ - unsigned int have_retried = 0; - - retry_get_time: - rtc_tm->tm_min = readb(S3C2410_RTCMIN); - rtc_tm->tm_hour = readb(S3C2410_RTCHOUR); - rtc_tm->tm_mday = readb(S3C2410_RTCDATE); - rtc_tm->tm_mon = readb(S3C2410_RTCMON); - rtc_tm->tm_year = readb(S3C2410_RTCYEAR); - rtc_tm->tm_sec = readb(S3C2410_RTCSEC); - - /* the only way to work out wether the system was mid-update - * when we read it is to check the second counter, and if it - * is zero, then we re-try the entire read - */ - - if (rtc_tm->tm_sec == 0 && !have_retried) { - have_retried = 1; - goto retry_get_time; - } - - pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n", - rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, - rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); - - BCD_TO_BIN(rtc_tm->tm_sec); - BCD_TO_BIN(rtc_tm->tm_min); - BCD_TO_BIN(rtc_tm->tm_hour); - BCD_TO_BIN(rtc_tm->tm_mday); - BCD_TO_BIN(rtc_tm->tm_mon); - BCD_TO_BIN(rtc_tm->tm_year); - - rtc_tm->tm_year += 100; - rtc_tm->tm_mon -= 1; - - return 0; -} - - -static int s3c2410_rtc_settime(struct rtc_time *tm) -{ - /* the rtc gets round the y2k problem by just not supporting it */ - - if (tm->tm_year < 100) - return -EINVAL; - - writeb(BIN2BCD(tm->tm_sec), S3C2410_RTCSEC); - writeb(BIN2BCD(tm->tm_min), S3C2410_RTCMIN); - writeb(BIN2BCD(tm->tm_hour), S3C2410_RTCHOUR); - writeb(BIN2BCD(tm->tm_mday), S3C2410_RTCDATE); - writeb(BIN2BCD(tm->tm_mon + 1), S3C2410_RTCMON); - writeb(BIN2BCD(tm->tm_year - 100), S3C2410_RTCYEAR); - - return 0; -} - -static int s3c2410_rtc_getalarm(struct rtc_wkalrm *alrm) -{ - struct rtc_time *alm_tm = &alrm->time; - unsigned int alm_en; - - alm_tm->tm_sec = readb(S3C2410_ALMSEC); - alm_tm->tm_min = readb(S3C2410_ALMMIN); - alm_tm->tm_hour = readb(S3C2410_ALMHOUR); - alm_tm->tm_mon = readb(S3C2410_ALMMON); - alm_tm->tm_mday = readb(S3C2410_ALMDATE); - alm_tm->tm_year = readb(S3C2410_ALMYEAR); - - alm_en = readb(S3C2410_RTCALM); - - pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n", - alm_en, - alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, - alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); - - - /* decode the alarm enable field */ - - if (alm_en & S3C2410_RTCALM_SECEN) { - BCD_TO_BIN(alm_tm->tm_sec); - } else { - alm_tm->tm_sec = 0xff; - } - - if (alm_en & S3C2410_RTCALM_MINEN) { - BCD_TO_BIN(alm_tm->tm_min); - } else { - alm_tm->tm_min = 0xff; - } - - if (alm_en & S3C2410_RTCALM_HOUREN) { - BCD_TO_BIN(alm_tm->tm_hour); - } else { - alm_tm->tm_hour = 0xff; - } - - if (alm_en & S3C2410_RTCALM_DAYEN) { - BCD_TO_BIN(alm_tm->tm_mday); - } else { - alm_tm->tm_mday = 0xff; - } - - if (alm_en & S3C2410_RTCALM_MONEN) { - BCD_TO_BIN(alm_tm->tm_mon); - alm_tm->tm_mon -= 1; - } else { - alm_tm->tm_mon = 0xff; - } - - if (alm_en & S3C2410_RTCALM_YEAREN) { - BCD_TO_BIN(alm_tm->tm_year); - } else { - alm_tm->tm_year = 0xffff; - } - - /* todo - set alrm->enabled ? */ - - return 0; -} - -static int s3c2410_rtc_setalarm(struct rtc_wkalrm *alrm) -{ - struct rtc_time *tm = &alrm->time; - unsigned int alrm_en; - - pr_debug("s3c2410_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n", - alrm->enabled, - tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff, - tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec); - - if (alrm->enabled || 1) { - alrm_en = readb(S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; - writeb(0x00, S3C2410_RTCALM); - - if (tm->tm_sec < 60 && tm->tm_sec >= 0) { - alrm_en |= S3C2410_RTCALM_SECEN; - writeb(BIN2BCD(tm->tm_sec), S3C2410_ALMSEC); - } - - if (tm->tm_min < 60 && tm->tm_min >= 0) { - alrm_en |= S3C2410_RTCALM_MINEN; - writeb(BIN2BCD(tm->tm_min), S3C2410_ALMMIN); - } - - if (tm->tm_hour < 24 && tm->tm_hour >= 0) { - alrm_en |= S3C2410_RTCALM_HOUREN; - writeb(BIN2BCD(tm->tm_hour), S3C2410_ALMHOUR); - } - - pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en); - - writeb(alrm_en, S3C2410_RTCALM); - enable_irq_wake(s3c2410_rtc_alarmno); - } else { - alrm_en = readb(S3C2410_RTCALM); - alrm_en &= ~S3C2410_RTCALM_ALMEN; - writeb(alrm_en, S3C2410_RTCALM); - disable_irq_wake(s3c2410_rtc_alarmno); - } - - return 0; -} - -static int s3c2410_rtc_ioctl(unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case RTC_AIE_OFF: - case RTC_AIE_ON: - s3c2410_rtc_setaie((cmd == RTC_AIE_ON) ? 1 : 0); - return 0; - - case RTC_PIE_OFF: - case RTC_PIE_ON: - s3c2410_rtc_setpie((cmd == RTC_PIE_ON) ? 1 : 0); - return 0; - - case RTC_IRQP_READ: - return put_user(s3c2410_rtc_freq, (unsigned long __user *)arg); - - case RTC_IRQP_SET: - if (arg < 1 || arg > 64) - return -EINVAL; - - if (!capable(CAP_SYS_RESOURCE)) - return -EACCES; - - /* check for power of 2 */ - - if ((arg & (arg-1)) != 0) - return -EINVAL; - - pr_debug("s3c2410_rtc: setting frequency %ld\n", arg); - - s3c2410_rtc_setfreq(arg); - return 0; - - case RTC_UIE_ON: - case RTC_UIE_OFF: - return -EINVAL; - } - - return -EINVAL; -} - -static int s3c2410_rtc_proc(char *buf) -{ - unsigned int rtcalm = readb(S3C2410_RTCALM); - unsigned int ticnt = readb (S3C2410_TICNT); - char *p = buf; - - p += sprintf(p, "alarm_IRQ\t: %s\n", - (rtcalm & S3C2410_RTCALM_ALMEN) ? "yes" : "no" ); - p += sprintf(p, "periodic_IRQ\t: %s\n", - (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" ); - p += sprintf(p, "periodic_freq\t: %d\n", s3c2410_rtc_freq); - - return p - buf; -} - -static int s3c2410_rtc_open(void) -{ - int ret; - - ret = request_irq(s3c2410_rtc_alarmno, s3c2410_rtc_alarmirq, - IRQF_DISABLED, "s3c2410-rtc alarm", NULL); - - if (ret) - printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_alarmno); - - ret = request_irq(s3c2410_rtc_tickno, s3c2410_rtc_tickirq, - IRQF_DISABLED, "s3c2410-rtc tick", NULL); - - if (ret) { - printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_tickno); - goto tick_err; - } - - return ret; - - tick_err: - free_irq(s3c2410_rtc_alarmno, NULL); - return ret; -} - -static void s3c2410_rtc_release(void) -{ - /* do not clear AIE here, it may be needed for wake */ - - s3c2410_rtc_setpie(0); - free_irq(s3c2410_rtc_alarmno, NULL); - free_irq(s3c2410_rtc_tickno, NULL); -} - -static struct rtc_ops s3c2410_rtcops = { - .owner = THIS_MODULE, - .open = s3c2410_rtc_open, - .release = s3c2410_rtc_release, - .ioctl = s3c2410_rtc_ioctl, - .read_time = s3c2410_rtc_gettime, - .set_time = s3c2410_rtc_settime, - .read_alarm = s3c2410_rtc_getalarm, - .set_alarm = s3c2410_rtc_setalarm, - .proc = s3c2410_rtc_proc, -}; - -static void s3c2410_rtc_enable(struct platform_device *pdev, int en) -{ - unsigned int tmp; - - if (s3c2410_rtc_base == NULL) - return; - - if (!en) { - tmp = readb(S3C2410_RTCCON); - writeb(tmp & ~S3C2410_RTCCON_RTCEN, S3C2410_RTCCON); - - tmp = readb(S3C2410_TICNT); - writeb(tmp & ~S3C2410_TICNT_ENABLE, S3C2410_TICNT); - } else { - /* re-enable the device, and check it is ok */ - - if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){ - dev_info(&pdev->dev, "rtc disabled, re-enabling\n"); - - tmp = readb(S3C2410_RTCCON); - writeb(tmp | S3C2410_RTCCON_RTCEN , S3C2410_RTCCON); - } - - if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){ - dev_info(&pdev->dev, "removing S3C2410_RTCCON_CNTSEL\n"); - - tmp = readb(S3C2410_RTCCON); - writeb(tmp& ~S3C2410_RTCCON_CNTSEL , S3C2410_RTCCON); - } - - if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){ - dev_info(&pdev->dev, "removing S3C2410_RTCCON_CLKRST\n"); - - tmp = readb(S3C2410_RTCCON); - writeb(tmp & ~S3C2410_RTCCON_CLKRST, S3C2410_RTCCON); - } - } -} - -static int s3c2410_rtc_remove(struct platform_device *dev) -{ - unregister_rtc(&s3c2410_rtcops); - - s3c2410_rtc_setpie(0); - s3c2410_rtc_setaie(0); - - if (s3c2410_rtc_mem != NULL) { - pr_debug("s3c2410_rtc: releasing s3c2410_rtc_mem\n"); - iounmap(s3c2410_rtc_base); - release_resource(s3c2410_rtc_mem); - kfree(s3c2410_rtc_mem); - } - - return 0; -} - -static int s3c2410_rtc_probe(struct platform_device *pdev) -{ - struct resource *res; - int ret; - - pr_debug("%s: probe=%p\n", __FUNCTION__, pdev); - - /* find the IRQs */ - - s3c2410_rtc_tickno = platform_get_irq(pdev, 1); - if (s3c2410_rtc_tickno < 0) { - dev_err(&pdev->dev, "no irq for rtc tick\n"); - return -ENOENT; - } - - s3c2410_rtc_alarmno = platform_get_irq(pdev, 0); - if (s3c2410_rtc_alarmno < 0) { - dev_err(&pdev->dev, "no irq for alarm\n"); - return -ENOENT; - } - - pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n", - s3c2410_rtc_tickno, s3c2410_rtc_alarmno); - - /* get the memory region */ - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get memory region resource\n"); - return -ENOENT; - } - - s3c2410_rtc_mem = request_mem_region(res->start, res->end-res->start+1, - pdev->name); - - if (s3c2410_rtc_mem == NULL) { - dev_err(&pdev->dev, "failed to reserve memory region\n"); - ret = -ENOENT; - goto exit_err; - } - - s3c2410_rtc_base = ioremap(res->start, res->end - res->start + 1); - if (s3c2410_rtc_base == NULL) { - dev_err(&pdev->dev, "failed ioremap()\n"); - ret = -EINVAL; - goto exit_err; - } - - s3c2410_rtc_mem = res; - pr_debug("s3c2410_rtc_base=%p\n", s3c2410_rtc_base); - - pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON)); - - /* check to see if everything is setup correctly */ - - s3c2410_rtc_enable(pdev, 1); - - pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON)); - - s3c2410_rtc_setfreq(s3c2410_rtc_freq); - - /* register RTC and exit */ - - register_rtc(&s3c2410_rtcops); - return 0; - - exit_err: - dev_err(&pdev->dev, "error %d during initialisation\n", ret); - - return ret; -} - -#ifdef CONFIG_PM - -/* S3C2410 RTC Power management control */ - -static struct timespec s3c2410_rtc_delta; - -static int ticnt_save; - -static int s3c2410_rtc_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct rtc_time tm; - struct timespec time; - - time.tv_nsec = 0; - - /* save TICNT for anyone using periodic interrupts */ - - ticnt_save = readb(S3C2410_TICNT); - - /* calculate time delta for suspend */ - - s3c2410_rtc_gettime(&tm); - rtc_tm_to_time(&tm, &time.tv_sec); - save_time_delta(&s3c2410_rtc_delta, &time); - s3c2410_rtc_enable(pdev, 0); - - return 0; -} - -static int s3c2410_rtc_resume(struct platform_device *pdev) -{ - struct rtc_time tm; - struct timespec time; - - time.tv_nsec = 0; - - s3c2410_rtc_enable(pdev, 1); - s3c2410_rtc_gettime(&tm); - rtc_tm_to_time(&tm, &time.tv_sec); - restore_time_delta(&s3c2410_rtc_delta, &time); - - writeb(ticnt_save, S3C2410_TICNT); - return 0; -} -#else -#define s3c2410_rtc_suspend NULL -#define s3c2410_rtc_resume NULL -#endif - -static struct platform_driver s3c2410_rtcdrv = { - .probe = s3c2410_rtc_probe, - .remove = s3c2410_rtc_remove, - .suspend = s3c2410_rtc_suspend, - .resume = s3c2410_rtc_resume, - .driver = { - .name = "s3c2410-rtc", - .owner = THIS_MODULE, - }, -}; - -static char __initdata banner[] = "S3C2410 RTC, (c) 2004 Simtec Electronics\n"; - -static int __init s3c2410_rtc_init(void) -{ - printk(banner); - return platform_driver_register(&s3c2410_rtcdrv); -} - -static void __exit s3c2410_rtc_exit(void) -{ - platform_driver_unregister(&s3c2410_rtcdrv); -} - -module_init(s3c2410_rtc_init); -module_exit(s3c2410_rtc_exit); - -MODULE_DESCRIPTION("S3C24XX RTC Driver"); -MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/scx200_gpio.c b/drivers/char/scx200_gpio.c index b956c7babd1..99e5272e3c5 100644 --- a/drivers/char/scx200_gpio.c +++ b/drivers/char/scx200_gpio.c @@ -44,7 +44,7 @@ struct nsc_gpio_ops scx200_gpio_ops = { .gpio_change = scx200_gpio_change, .gpio_current = scx200_gpio_current }; -EXPORT_SYMBOL(scx200_gpio_ops); +EXPORT_SYMBOL_GPL(scx200_gpio_ops); static int scx200_gpio_open(struct inode *inode, struct file *file) { @@ -69,7 +69,7 @@ static const struct file_operations scx200_gpio_fileops = { .release = scx200_gpio_release, }; -struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */ +static struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */ static int __init scx200_gpio_init(void) { diff --git a/drivers/char/selection.c b/drivers/char/selection.c index 71093a9fc46..74cff839c85 100644 --- a/drivers/char/selection.c +++ b/drivers/char/selection.c @@ -33,7 +33,7 @@ extern void poke_blanked_console(void); /* Variables for selection control. */ /* Use a dynamic buffer, instead of static (Dec 1994) */ -struct vc_data *sel_cons; /* must not be disallocated */ +struct vc_data *sel_cons; /* must not be deallocated */ static volatile int sel_start = -1; /* cleared by clear_selection */ static int sel_end; static int sel_buffer_lth; diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index a1d303f9a33..c0ef0f0e580 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -1087,24 +1087,16 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS); spin_unlock_irqrestore(&bp->lock, flags); dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR); - baud = C_BAUD(tty); + baud = tty_get_baud_rate(tty); - if (baud & CBAUDEX) { - baud &= ~CBAUDEX; - if (baud < 1 || baud > 2) - port->tty->termios->c_cflag &= ~CBAUDEX; - else - baud += 15; - } - if (baud == 15) { + if (baud == 38400) { if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baud ++; if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baud += 2; } - - if (!baud_table[baud]) { + if (!baud) { /* Drop DTR & exit */ dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n"); if (!SX_CRTSCTS (tty)) { @@ -1134,7 +1126,7 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p "This is an untested option, please be carefull.\n", port_No (port), tmp); else - tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] + + tmp = (((SX_OSCFREQ + baud/2) / baud + CD186x_TPC/2) / CD186x_TPC); if ((tmp < 0x10) && time_before(again, jiffies)) { diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index ee3ca8f1768..0ad6cb081db 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -208,7 +208,7 @@ static void send_sig_all(int sig) struct task_struct *p; for_each_process(p) { - if (p->mm && p->pid != 1) + if (p->mm && !is_init(p)) /* Not swapper, init nor kernel thread */ force_sig(sig, p); } diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index bb0d9199e99..333741770f1 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -129,6 +129,7 @@ LIST_HEAD(tty_drivers); /* linked list of tty drivers */ /* Semaphore to protect creating and releasing a tty. This is shared with vt.c for deeply disgusting hack reasons */ DEFINE_MUTEX(tty_mutex); +EXPORT_SYMBOL(tty_mutex); #ifdef CONFIG_UNIX98_PTYS extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */ @@ -160,17 +161,11 @@ static void release_mem(struct tty_struct *tty, int idx); * been initialized in any way but has been zeroed * * Locking: none - * FIXME: use kzalloc */ static struct tty_struct *alloc_tty_struct(void) { - struct tty_struct *tty; - - tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL); - if (tty) - memset(tty, 0, sizeof(struct tty_struct)); - return tty; + return kzalloc(sizeof(struct tty_struct), GFP_KERNEL); } static void tty_buffer_free_all(struct tty_struct *); @@ -483,10 +478,9 @@ int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, tb->used += space; copied += space; chars += space; - } - /* There is a small chance that we need to split the data over - several buffers. If this is the case we must loop */ - while (unlikely(size > copied)); + /* There is a small chance that we need to split the data over + several buffers. If this is the case we must loop */ + } while (unlikely(size > copied)); return copied; } EXPORT_SYMBOL(tty_insert_flip_string); @@ -521,10 +515,9 @@ int tty_insert_flip_string_flags(struct tty_struct *tty, copied += space; chars += space; flags += space; - } - /* There is a small chance that we need to split the data over - several buffers. If this is the case we must loop */ - while (unlikely(size > copied)); + /* There is a small chance that we need to split the data over + several buffers. If this is the case we must loop */ + } while (unlikely(size > copied)); return copied; } EXPORT_SYMBOL(tty_insert_flip_string_flags); @@ -626,9 +619,9 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); static void tty_set_termios_ldisc(struct tty_struct *tty, int num) { - down(&tty->termios_sem); + mutex_lock(&tty->termios_mutex); tty->termios->c_line = num; - up(&tty->termios_sem); + mutex_unlock(&tty->termios_mutex); } /* @@ -1346,9 +1339,9 @@ static void do_tty_hangup(void *data) */ if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { - down(&tty->termios_sem); + mutex_lock(&tty->termios_mutex); *tty->termios = tty->driver->init_termios; - up(&tty->termios_sem); + mutex_unlock(&tty->termios_mutex); } /* Defer ldisc switch */ @@ -2072,8 +2065,9 @@ fail_no_mem: /* call the tty release_mem routine to clean out this slot */ release_mem_out: - printk(KERN_INFO "init_dev: ldisc open failed, " - "clearing slot %d\n", idx); + if (printk_ratelimit()) + printk(KERN_INFO "init_dev: ldisc open failed, " + "clearing slot %d\n", idx); release_mem(tty, idx); goto end_init; } @@ -2726,6 +2720,8 @@ static int tty_fasync(int fd, struct file * filp, int on) * Locking: * Called functions take tty_ldisc_lock * current->signal->tty check is safe without locks + * + * FIXME: may race normal receive processing */ static int tiocsti(struct tty_struct *tty, char __user *p) @@ -2748,18 +2744,21 @@ static int tiocsti(struct tty_struct *tty, char __user *p) * @tty; tty * @arg: user buffer for result * - * Copies the kernel idea of the window size into the user buffer. No - * locking is done. + * Copies the kernel idea of the window size into the user buffer. * - * FIXME: Returning random values racing a window size set is wrong - * should lock here against that + * Locking: tty->termios_sem is taken to ensure the winsize data + * is consistent. */ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg) { - if (copy_to_user(arg, &tty->winsize, sizeof(*arg))) - return -EFAULT; - return 0; + int err; + + mutex_lock(&tty->termios_mutex); + err = copy_to_user(arg, &tty->winsize, sizeof(*arg)); + mutex_unlock(&tty->termios_mutex); + + return err ? -EFAULT: 0; } /** @@ -2772,12 +2771,11 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg) * actually has driver level meaning and triggers a VC resize. * * Locking: - * The console_sem is used to ensure we do not try and resize - * the console twice at once. - * FIXME: Two racing size sets may leave the console and kernel - * parameters disagreeing. Is this exploitable ? - * FIXME: Random values racing a window size get is wrong - * should lock here against that + * Called function use the console_sem is used to ensure we do + * not try and resize the console twice at once. + * The tty->termios_sem is used to ensure we don't double + * resize and get confused. Lock order - tty->termios.sem before + * console sem */ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, @@ -2787,17 +2785,18 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) return -EFAULT; + + mutex_lock(&tty->termios_mutex); if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg))) - return 0; + goto done; + #ifdef CONFIG_VT if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) { - int rc; - - acquire_console_sem(); - rc = vc_resize(tty->driver_data, tmp_ws.ws_col, tmp_ws.ws_row); - release_console_sem(); - if (rc) - return -ENXIO; + if (vc_lock_resize(tty->driver_data, tmp_ws.ws_col, + tmp_ws.ws_row)) { + mutex_unlock(&tty->termios_mutex); + return -ENXIO; + } } #endif if (tty->pgrp > 0) @@ -2806,6 +2805,8 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, kill_pg(real_tty->pgrp, SIGWINCH, 1); tty->winsize = tmp_ws; real_tty->winsize = tmp_ws; +done: + mutex_unlock(&tty->termios_mutex); return 0; } @@ -2880,9 +2881,7 @@ static int fionbio(struct file *file, int __user *p) * Locking: * Takes tasklist lock internally to walk sessions * Takes task_lock() when updating signal->tty - * - * FIXME: tty_mutex is needed to protect signal->tty references. - * FIXME: why task_lock on the signal->tty reference ?? + * Takes tty_mutex() to protect tty instance * */ @@ -2917,9 +2916,11 @@ static int tiocsctty(struct tty_struct *tty, int arg) } else return -EPERM; } + mutex_lock(&tty_mutex); task_lock(current); current->signal->tty = tty; task_unlock(current); + mutex_unlock(&tty_mutex); current->signal->tty_old_pgrp = 0; tty->session = current->signal->session; tty->pgrp = process_group(current); @@ -2959,8 +2960,6 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t * permitted where the tty session is our session. * * Locking: None - * - * FIXME: current->signal->tty referencing is unsafe. */ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) @@ -3039,19 +3038,20 @@ static int tiocsetd(struct tty_struct *tty, int __user *p) * timed break functionality. * * Locking: - * None + * atomic_write_lock serializes * - * FIXME: - * What if two overlap */ static int send_break(struct tty_struct *tty, unsigned int duration) { + if (mutex_lock_interruptible(&tty->atomic_write_lock)) + return -EINTR; tty->driver->break_ctl(tty, -1); if (!signal_pending(current)) { msleep_interruptible(duration); } tty->driver->break_ctl(tty, 0); + mutex_unlock(&tty->atomic_write_lock); if (signal_pending(current)) return -EINTR; return 0; @@ -3144,6 +3144,8 @@ int tty_ioctl(struct inode * inode, struct file * file, if (tty_paranoia_check(tty, inode, "tty_ioctl")) return -EINVAL; + /* CHECKME: is this safe as one end closes ? */ + real_tty = tty; if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) @@ -3580,7 +3582,7 @@ static void initialize_tty_struct(struct tty_struct *tty) tty_buffer_init(tty); INIT_WORK(&tty->buf.work, flush_to_ldisc, tty); init_MUTEX(&tty->buf.pty_sem); - init_MUTEX(&tty->termios_sem); + mutex_init(&tty->termios_mutex); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); INIT_WORK(&tty->hangup_work, do_tty_hangup, tty); diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 4ad47d321bd..3b6fa7b0be8 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -20,6 +20,7 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/bitops.h> +#include <linux/mutex.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -131,7 +132,7 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios /* FIXME: we need to decide on some locking/ordering semantics for the set_termios notification eventually */ - down(&tty->termios_sem); + mutex_lock(&tty->termios_mutex); *tty->termios = *new_termios; unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); @@ -176,7 +177,7 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios (ld->set_termios)(tty, &old_termios); tty_ldisc_deref(ld); } - up(&tty->termios_sem); + mutex_unlock(&tty->termios_mutex); } /** @@ -284,13 +285,13 @@ static int get_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb) { struct sgttyb tmp; - down(&tty->termios_sem); + mutex_lock(&tty->termios_mutex); tmp.sg_ispeed = 0; tmp.sg_ospeed = 0; tmp.sg_erase = tty->termios->c_cc[VERASE]; tmp.sg_kill = tty->termios->c_cc[VKILL]; tmp.sg_flags = get_sgflags(tty); - up(&tty->termios_sem); + mutex_unlock(&tty->termios_mutex); return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -345,12 +346,12 @@ static int set_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb) if (copy_from_user(&tmp, sgttyb, sizeof(tmp))) return -EFAULT; - down(&tty->termios_sem); + mutex_lock(&tty->termios_mutex); termios = *tty->termios; termios.c_cc[VERASE] = tmp.sg_erase; termios.c_cc[VKILL] = tmp.sg_kill; set_sgflags(&termios, tmp.sg_flags); - up(&tty->termios_sem); + mutex_unlock(&tty->termios_mutex); change_termios(tty, &termios); return 0; } @@ -422,24 +423,28 @@ static int set_ltchars(struct tty_struct * tty, struct ltchars __user * ltchars) * * Send a high priority character to the tty even if stopped * - * Locking: none - * - * FIXME: overlapping calls with start/stop tty lose state of tty + * Locking: none for xchar method, write ordering for write method. */ -static void send_prio_char(struct tty_struct *tty, char ch) +static int send_prio_char(struct tty_struct *tty, char ch) { int was_stopped = tty->stopped; if (tty->driver->send_xchar) { tty->driver->send_xchar(tty, ch); - return; + return 0; } + + if (mutex_lock_interruptible(&tty->atomic_write_lock)) + return -ERESTARTSYS; + if (was_stopped) start_tty(tty); tty->driver->write(tty, &ch, 1); if (was_stopped) stop_tty(tty); + mutex_unlock(&tty->atomic_write_lock); + return 0; } int n_tty_ioctl(struct tty_struct * tty, struct file * file, @@ -513,11 +518,11 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file, break; case TCIOFF: if (STOP_CHAR(tty) != __DISABLED_CHAR) - send_prio_char(tty, STOP_CHAR(tty)); + return send_prio_char(tty, STOP_CHAR(tty)); break; case TCION: if (START_CHAR(tty) != __DISABLED_CHAR) - send_prio_char(tty, START_CHAR(tty)); + return send_prio_char(tty, START_CHAR(tty)); break; default: return -EINVAL; @@ -592,11 +597,11 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file, case TIOCSSOFTCAR: if (get_user(arg, (unsigned int __user *) arg)) return -EFAULT; - down(&tty->termios_sem); + mutex_lock(&tty->termios_mutex); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); - up(&tty->termios_sem); + mutex_unlock(&tty->termios_mutex); return 0; default: return -ENOIOCTLCMD; diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index a9247b5213d..bd7a98c6ea7 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -474,14 +474,15 @@ static const struct file_operations vcs_fops = { static struct class *vc_class; -void vcs_make_devfs(struct tty_struct *tty) +void vcs_make_sysfs(struct tty_struct *tty) { class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 1), NULL, "vcs%u", tty->index + 1); class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 129), NULL, "vcsa%u", tty->index + 1); } -void vcs_remove_devfs(struct tty_struct *tty) + +void vcs_remove_sysfs(struct tty_struct *tty) { class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 1)); class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 129)); diff --git a/drivers/char/vt.c b/drivers/char/vt.c index da7e66a2a38..fb75da940b5 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -63,6 +63,13 @@ * * Removed console_lock, enabled interrupts across all console operations * 13 March 2001, Andrew Morton + * + * Fixed UTF-8 mode so alternate charset modes always work according + * to control sequences interpreted in do_con_trol function + * preserving backward VT100 semigraphics compatibility, + * malformed UTF sequences represented as sequences of replacement glyphs, + * original codes or '?' as a last resort if replacement glyph is undefined + * by Adam Tla/lka <atlka@pg.gda.pl>, Aug 2006 */ #include <linux/module.h> @@ -128,8 +135,8 @@ const struct consw *conswitchp; #define DEFAULT_BELL_PITCH 750 #define DEFAULT_BELL_DURATION (HZ/8) -extern void vcs_make_devfs(struct tty_struct *tty); -extern void vcs_remove_devfs(struct tty_struct *tty); +extern void vcs_make_sysfs(struct tty_struct *tty); +extern void vcs_remove_sysfs(struct tty_struct *tty); extern void console_map_init(void); #ifdef CONFIG_PROM_CONSOLE @@ -730,7 +737,8 @@ 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); - vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); + if (!vc->vc_kmalloced) + vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); if (!vc->vc_screenbuf) { kfree(vc); vc_cons[currcons].d = NULL; @@ -878,8 +886,17 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) return err; } +int vc_lock_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) +{ + int rc; + + acquire_console_sem(); + rc = vc_resize(vc, cols, lines); + release_console_sem(); + return rc; +} -void vc_disallocate(unsigned int currcons) +void vc_deallocate(unsigned int currcons) { WARN_CONSOLE_UNLOCKED(); @@ -2005,17 +2022,23 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co /* Do no translation at all in control states */ if (vc->vc_state != ESnormal) { tc = c; - } else if (vc->vc_utf) { + } else if (vc->vc_utf && !vc->vc_disp_ctrl) { /* Combine UTF-8 into Unicode */ - /* Incomplete characters silently ignored */ + /* Malformed sequences as sequences of replacement glyphs */ +rescan_last_byte: if(c > 0x7f) { - if (vc->vc_utf_count > 0 && (c & 0xc0) == 0x80) { - vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f); - vc->vc_utf_count--; - if (vc->vc_utf_count == 0) - tc = c = vc->vc_utf_char; - else continue; + if (vc->vc_utf_count) { + if ((c & 0xc0) == 0x80) { + vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f); + if (--vc->vc_utf_count) { + vc->vc_npar++; + continue; + } + tc = c = vc->vc_utf_char; + } else + goto replacement_glyph; } else { + vc->vc_npar = 0; if ((c & 0xe0) == 0xc0) { vc->vc_utf_count = 1; vc->vc_utf_char = (c & 0x1f); @@ -2032,14 +2055,15 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co vc->vc_utf_count = 5; vc->vc_utf_char = (c & 0x01); } else - vc->vc_utf_count = 0; + goto replacement_glyph; continue; } } else { + if (vc->vc_utf_count) + goto replacement_glyph; tc = c; - vc->vc_utf_count = 0; } - } else { /* no utf */ + } else { /* no utf or alternate charset mode */ tc = vc->vc_translate[vc->vc_toggle_meta ? (c | 0x80) : c]; } @@ -2054,31 +2078,33 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co * direct-to-font zone in UTF-8 mode. */ ok = tc && (c >= 32 || - (!vc->vc_utf && !(((vc->vc_disp_ctrl ? CTRL_ALWAYS - : CTRL_ACTION) >> c) & 1))) + !(vc->vc_disp_ctrl ? (CTRL_ALWAYS >> c) & 1 : + vc->vc_utf || ((CTRL_ACTION >> c) & 1))) && (c != 127 || vc->vc_disp_ctrl) && (c != 128+27); if (vc->vc_state == ESnormal && ok) { /* Now try to find out how to display it */ tc = conv_uni_to_pc(vc, tc); - if ( tc == -4 ) { + if (tc & ~charmask) { + if ( tc == -4 ) { /* If we got -4 (not found) then see if we have defined a replacement character (U+FFFD) */ - tc = conv_uni_to_pc(vc, 0xfffd); - - /* One reason for the -4 can be that we just - did a clear_unimap(); - try at least to show something. */ - if (tc == -4) - tc = c; - } else if ( tc == -3 ) { - /* Bad hash table -- hope for the best */ - tc = c; - } - if (tc & ~charmask) - continue; /* Conversion failed */ +replacement_glyph: + tc = conv_uni_to_pc(vc, 0xfffd); + if (!(tc & ~charmask)) + goto display_glyph; + } else if ( tc != -3 ) + continue; /* nothing to display */ + /* no hash table or no replacement -- + * hope for the best */ + if ( c & ~charmask ) + tc = '?'; + else + tc = c; + } +display_glyph: if (vc->vc_need_wrap || vc->vc_decim) FLUSH if (vc->vc_need_wrap) { @@ -2102,6 +2128,15 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co vc->vc_x++; draw_to = (vc->vc_pos += 2); } + if (vc->vc_utf_count) { + if (vc->vc_npar) { + vc->vc_npar--; + goto display_glyph; + } + vc->vc_utf_count = 0; + c = orig; + goto rescan_last_byte; + } continue; } FLUSH @@ -2498,7 +2533,7 @@ static int con_open(struct tty_struct *tty, struct file *filp) tty->winsize.ws_col = vc_cons[currcons].d->vc_cols; } release_console_sem(); - vcs_make_devfs(tty); + vcs_make_sysfs(tty); return ret; } } @@ -2511,7 +2546,7 @@ static int con_open(struct tty_struct *tty, struct file *filp) * and taking a ref against the tty while we're in the process of forgetting * about it and cleaning things up. * - * This is because vcs_remove_devfs() can sleep and will drop the BKL. + * This is because vcs_remove_sysfs() can sleep and will drop the BKL. */ static void con_close(struct tty_struct *tty, struct file *filp) { @@ -2524,7 +2559,7 @@ static void con_close(struct tty_struct *tty, struct file *filp) vc->vc_tty = NULL; tty->driver_data = NULL; release_console_sem(); - vcs_remove_devfs(tty); + vcs_remove_sysfs(tty); mutex_unlock(&tty_mutex); /* * tty_mutex is released, but we still hold BKL, so there is @@ -3765,6 +3800,7 @@ EXPORT_SYMBOL(default_blu); EXPORT_SYMBOL(update_region); EXPORT_SYMBOL(redraw_screen); EXPORT_SYMBOL(vc_resize); +EXPORT_SYMBOL(vc_lock_resize); EXPORT_SYMBOL(fg_console); EXPORT_SYMBOL(console_blank_hook); EXPORT_SYMBOL(console_blanked); diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index a5628a8b662..a53e382cc10 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -96,7 +96,7 @@ do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, struct kbd_str if (!perm) return -EPERM; if (!i && v == K_NOSUCHMAP) { - /* disallocate map */ + /* deallocate map */ key_map = key_maps[s]; if (s && key_map) { key_maps[s] = NULL; @@ -819,20 +819,20 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, if (arg > MAX_NR_CONSOLES) return -ENXIO; if (arg == 0) { - /* disallocate all unused consoles, but leave 0 */ + /* deallocate all unused consoles, but leave 0 */ acquire_console_sem(); for (i=1; i<MAX_NR_CONSOLES; i++) if (! VT_BUSY(i)) - vc_disallocate(i); + vc_deallocate(i); release_console_sem(); } else { - /* disallocate a single console, if possible */ + /* deallocate a single console, if possible */ arg--; if (VT_BUSY(arg)) return -EBUSY; if (arg) { /* leave 0 */ acquire_console_sem(); - vc_disallocate(arg); + vc_deallocate(arg); release_console_sem(); } } @@ -847,11 +847,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, if (get_user(ll, &vtsizes->v_rows) || get_user(cc, &vtsizes->v_cols)) return -EFAULT; - for (i = 0; i < MAX_NR_CONSOLES; i++) { - acquire_console_sem(); - vc_resize(vc_cons[i].d, cc, ll); - release_console_sem(); - } + for (i = 0; i < MAX_NR_CONSOLES; i++) + vc_lock_resize(vc_cons[i].d, cc, ll); return 0; } diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index f114d7b5bb2..77ab7e020da 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -165,6 +165,13 @@ config EP93XX_WATCHDOG To compile this driver as a module, choose M here: the module will be called ep93xx_wdt. +config OMAP_WATCHDOG + tristate "OMAP Watchdog" + depends on WATCHDOG && (ARCH_OMAP16XX || ARCH_OMAP24XX) + help + Support for TI OMAP1610/OMAP1710/OMAP2420 watchdog. Say 'Y' here to + enable the OMAP1610/OMAP1710 watchdog timer. + # X86 (i386 + ia64 + x86_64) Architecture config ACQUIRE_WDT diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index 6ab77b61a64..5099f8be8cc 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o # ARM Architecture obj-$(CONFIG_AT91_WATCHDOG) += at91_wdt.o +obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o diff --git a/drivers/char/watchdog/omap_wdt.c b/drivers/char/watchdog/omap_wdt.c new file mode 100644 index 00000000000..8f90b90a502 --- /dev/null +++ b/drivers/char/watchdog/omap_wdt.c @@ -0,0 +1,391 @@ +/* + * linux/drivers/char/watchdog/omap_wdt.c + * + * Watchdog driver for the TI OMAP 16xx & 24xx 32KHz (non-secure) watchdog + * + * Author: MontaVista Software, Inc. + * <gdavis@mvista.com> or <source@mvista.com> + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + * + * History: + * + * 20030527: George G. Davis <gdavis@mvista.com> + * Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c + * (c) Copyright 2000 Oleg Drokin <green@crimea.edu> + * Based on SoftDog driver by Alan Cox <alan@redhat.com> + * + * Copyright (c) 2004 Texas Instruments. + * 1. Modified to support OMAP1610 32-KHz watchdog timer + * 2. Ported to 2.6 kernel + * + * Copyright (c) 2005 David Brownell + * Use the driver model and standard identifiers; handle bigger timeouts. + */ + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/reboot.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/moduleparam.h> +#include <linux/clk.h> + +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/hardware.h> +#include <asm/bitops.h> + +#include <asm/arch/prcm.h> + +#include "omap_wdt.h" + +static unsigned timer_margin; +module_param(timer_margin, uint, 0); +MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); + +static int omap_wdt_users; +static struct clk *armwdt_ck = NULL; +static struct clk *mpu_wdt_ick = NULL; +static struct clk *mpu_wdt_fck = NULL; + +static unsigned int wdt_trgr_pattern = 0x1234; + +static void omap_wdt_ping(void) +{ + /* wait for posted write to complete */ + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) + cpu_relax(); + wdt_trgr_pattern = ~wdt_trgr_pattern; + omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR)); + /* wait for posted write to complete */ + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) + cpu_relax(); + /* reloaded WCRR from WLDR */ +} + +static void omap_wdt_enable(void) +{ + /* Sequence to enable the watchdog */ + omap_writel(0xBBBB, OMAP_WATCHDOG_SPR); + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) + cpu_relax(); + omap_writel(0x4444, OMAP_WATCHDOG_SPR); + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) + cpu_relax(); +} + +static void omap_wdt_disable(void) +{ + /* sequence required to disable watchdog */ + omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) + cpu_relax(); + omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) + cpu_relax(); +} + +static void omap_wdt_adjust_timeout(unsigned new_timeout) +{ + if (new_timeout < TIMER_MARGIN_MIN) + new_timeout = TIMER_MARGIN_DEFAULT; + if (new_timeout > TIMER_MARGIN_MAX) + new_timeout = TIMER_MARGIN_MAX; + timer_margin = new_timeout; +} + +static void omap_wdt_set_timeout(void) +{ + u32 pre_margin = GET_WLDR_VAL(timer_margin); + + /* just count up at 32 KHz */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) + cpu_relax(); + omap_writel(pre_margin, OMAP_WATCHDOG_LDR); + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) + cpu_relax(); +} + +/* + * Allow only one task to hold it open + */ + +static int omap_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(1, (unsigned long *)&omap_wdt_users)) + return -EBUSY; + + if (cpu_is_omap16xx()) + clk_enable(armwdt_ck); /* Enable the clock */ + + if (cpu_is_omap24xx()) { + clk_enable(mpu_wdt_ick); /* Enable the interface clock */ + clk_enable(mpu_wdt_fck); /* Enable the functional clock */ + } + + /* initialize prescaler */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) + cpu_relax(); + omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL); + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) + cpu_relax(); + + omap_wdt_set_timeout(); + omap_wdt_enable(); + return 0; +} + +static int omap_wdt_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer unless NOWAYOUT is defined. + */ +#ifndef CONFIG_WATCHDOG_NOWAYOUT + omap_wdt_disable(); + + if (cpu_is_omap16xx()) { + clk_disable(armwdt_ck); /* Disable the clock */ + clk_put(armwdt_ck); + armwdt_ck = NULL; + } + + if (cpu_is_omap24xx()) { + clk_disable(mpu_wdt_ick); /* Disable the clock */ + clk_disable(mpu_wdt_fck); /* Disable the clock */ + clk_put(mpu_wdt_ick); + clk_put(mpu_wdt_fck); + mpu_wdt_ick = NULL; + mpu_wdt_fck = NULL; + } +#else + printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); +#endif + omap_wdt_users = 0; + return 0; +} + +static ssize_t +omap_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + /* Refresh LOAD_TIME. */ + if (len) + omap_wdt_ping(); + return len; +} + +static int +omap_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int new_margin; + static struct watchdog_info ident = { + .identity = "OMAP Watchdog", + .options = WDIOF_SETTIMEOUT, + .firmware_version = 0, + }; + + switch (cmd) { + default: + return -ENOIOCTLCMD; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info __user *)arg, &ident, + sizeof(ident)); + case WDIOC_GETSTATUS: + return put_user(0, (int __user *)arg); + case WDIOC_GETBOOTSTATUS: + if (cpu_is_omap16xx()) + return put_user(omap_readw(ARM_SYSST), + (int __user *)arg); + if (cpu_is_omap24xx()) + return put_user(omap_prcm_get_reset_sources(), + (int __user *)arg); + case WDIOC_KEEPALIVE: + omap_wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int __user *)arg)) + return -EFAULT; + omap_wdt_adjust_timeout(new_margin); + + omap_wdt_disable(); + omap_wdt_set_timeout(); + omap_wdt_enable(); + + omap_wdt_ping(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(timer_margin, (int __user *)arg); + } +} + +static struct file_operations omap_wdt_fops = { + .owner = THIS_MODULE, + .write = omap_wdt_write, + .ioctl = omap_wdt_ioctl, + .open = omap_wdt_open, + .release = omap_wdt_release, +}; + +static struct miscdevice omap_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &omap_wdt_fops +}; + +static int __init omap_wdt_probe(struct platform_device *pdev) +{ + struct resource *res, *mem; + int ret; + + /* reserve static register mappings */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + mem = request_mem_region(res->start, res->end - res->start + 1, + pdev->name); + if (mem == NULL) + return -EBUSY; + + platform_set_drvdata(pdev, mem); + + omap_wdt_users = 0; + + if (cpu_is_omap16xx()) { + armwdt_ck = clk_get(&pdev->dev, "armwdt_ck"); + if (IS_ERR(armwdt_ck)) { + ret = PTR_ERR(armwdt_ck); + armwdt_ck = NULL; + goto fail; + } + } + + if (cpu_is_omap24xx()) { + mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick"); + if (IS_ERR(mpu_wdt_ick)) { + ret = PTR_ERR(mpu_wdt_ick); + mpu_wdt_ick = NULL; + goto fail; + } + mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck"); + if (IS_ERR(mpu_wdt_fck)) { + ret = PTR_ERR(mpu_wdt_fck); + mpu_wdt_fck = NULL; + goto fail; + } + } + + omap_wdt_disable(); + omap_wdt_adjust_timeout(timer_margin); + + omap_wdt_miscdev.dev = &pdev->dev; + ret = misc_register(&omap_wdt_miscdev); + if (ret) + goto fail; + + pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin); + + /* autogate OCP interface clock */ + omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG); + return 0; + +fail: + if (armwdt_ck) + clk_put(armwdt_ck); + if (mpu_wdt_ick) + clk_put(mpu_wdt_ick); + if (mpu_wdt_fck) + clk_put(mpu_wdt_fck); + release_resource(mem); + return ret; +} + +static void omap_wdt_shutdown(struct platform_device *pdev) +{ + omap_wdt_disable(); +} + +static int omap_wdt_remove(struct platform_device *pdev) +{ + struct resource *mem = platform_get_drvdata(pdev); + misc_deregister(&omap_wdt_miscdev); + release_resource(mem); + if (armwdt_ck) + clk_put(armwdt_ck); + if (mpu_wdt_ick) + clk_put(mpu_wdt_ick); + if (mpu_wdt_fck) + clk_put(mpu_wdt_fck); + return 0; +} + +#ifdef CONFIG_PM + +/* REVISIT ... not clear this is the best way to handle system suspend; and + * it's very inappropriate for selective device suspend (e.g. suspending this + * through sysfs rather than by stopping the watchdog daemon). Also, this + * may not play well enough with NOWAYOUT... + */ + +static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) +{ + if (omap_wdt_users) + omap_wdt_disable(); + return 0; +} + +static int omap_wdt_resume(struct platform_device *pdev) +{ + if (omap_wdt_users) { + omap_wdt_enable(); + omap_wdt_ping(); + } + return 0; +} + +#else +#define omap_wdt_suspend NULL +#define omap_wdt_resume NULL +#endif + +static struct platform_driver omap_wdt_driver = { + .probe = omap_wdt_probe, + .remove = omap_wdt_remove, + .shutdown = omap_wdt_shutdown, + .suspend = omap_wdt_suspend, + .resume = omap_wdt_resume, + .driver = { + .owner = THIS_MODULE, + .name = "omap_wdt", + }, +}; + +static int __init omap_wdt_init(void) +{ + return platform_driver_register(&omap_wdt_driver); +} + +static void __exit omap_wdt_exit(void) +{ + platform_driver_unregister(&omap_wdt_driver); +} + +module_init(omap_wdt_init); +module_exit(omap_wdt_exit); + +MODULE_AUTHOR("George G. Davis"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/omap_wdt.h b/drivers/char/watchdog/omap_wdt.h new file mode 100644 index 00000000000..52a532a5114 --- /dev/null +++ b/drivers/char/watchdog/omap_wdt.h @@ -0,0 +1,64 @@ +/* + * linux/drivers/char/watchdog/omap_wdt.h + * + * BRIEF MODULE DESCRIPTION + * OMAP Watchdog timer register definitions + * + * Copyright (C) 2004 Texas Instruments. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _OMAP_WATCHDOG_H +#define _OMAP_WATCHDOG_H + +#define OMAP1610_WATCHDOG_BASE 0xfffeb000 +#define OMAP2420_WATCHDOG_BASE 0x48022000 /*WDT Timer 2 */ + +#ifdef CONFIG_ARCH_OMAP24XX +#define OMAP_WATCHDOG_BASE OMAP2420_WATCHDOG_BASE +#else +#define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE +#define RM_RSTST_WKUP 0 +#endif + +#define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00) +#define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10) +#define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14) +#define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24) +#define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28) +#define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c) +#define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30) +#define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34) +#define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48) + +/* Using the prescaler, the OMAP watchdog could go for many + * months before firing. These limits work without scaling, + * with the 60 second default assumed by most tools and docs. + */ +#define TIMER_MARGIN_MAX (24 * 60 * 60) /* 1 day */ +#define TIMER_MARGIN_DEFAULT 60 /* 60 secs */ +#define TIMER_MARGIN_MIN 1 + +#define PTV 0 /* prescale */ +#define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<<PTV))) + 1) + +#endif /* _OMAP_WATCHDOG_H */ diff --git a/drivers/fc4/fc.c b/drivers/fc4/fc.c index 1a159e8843c..22d17474755 100644 --- a/drivers/fc4/fc.c +++ b/drivers/fc4/fc.c @@ -974,7 +974,6 @@ int fcp_scsi_dev_reset(Scsi_Cmnd *SCpnt) */ fc->rst_pkt->device->host->eh_action = &sem; - fc->rst_pkt->request->rq_status = RQ_SCSI_BUSY; fc->rst_pkt->done = fcp_scsi_reset_done; diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index b9e3886d9e1..b8b596d5778 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -123,6 +123,26 @@ static void __init dmi_save_devices(struct dmi_header *dm) dev->type = *d++ & 0x7f; dev->name = dmi_string(dm, *d); dev->device_data = NULL; + list_add(&dev->list, &dmi_devices); + } +} + +static void __init dmi_save_oem_strings_devices(struct dmi_header *dm) +{ + int i, count = *(u8 *)(dm + 1); + struct dmi_device *dev; + + for (i = 1; i <= count; i++) { + dev = dmi_alloc(sizeof(*dev)); + if (!dev) { + printk(KERN_ERR + "dmi_save_oem_strings_devices: out of memory.\n"); + break; + } + + dev->type = DMI_DEV_TYPE_OEM_STRING; + dev->name = dmi_string(dm, i); + dev->device_data = NULL; list_add(&dev->list, &dmi_devices); } @@ -181,6 +201,9 @@ static void __init dmi_decode(struct dmi_header *dm) case 10: /* Onboard Devices Information */ dmi_save_devices(dm); break; + case 11: /* OEM Strings */ + dmi_save_oem_strings_devices(dm); + break; case 38: /* IPMI Device Information */ dmi_save_ipmi_device(dm); } diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 0e31a0c496e..9b88b25b6ed 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -53,7 +53,7 @@ config SENSORS_ADM1021 config SENSORS_ADM1025 tristate "Analog Devices ADM1025 and compatibles" - depends on HWMON && I2C && EXPERIMENTAL + depends on HWMON && I2C select HWMON_VID help If you say yes here you get support for Analog Devices ADM1025 @@ -94,6 +94,16 @@ config SENSORS_ADM9240 This driver can also be built as a module. If so, the module will be called adm9240. +config SENSORS_K8TEMP + tristate "AMD K8 processor sensor" + depends on HWMON && X86 && PCI && EXPERIMENTAL + help + If you say yes here you get support for the temperature + sensor(s) inside your AMD K8 CPU. + + This driver can also be built as a module. If so, the module + will be called k8temp. + config SENSORS_ASB100 tristate "Asus ASB100 Bach" depends on HWMON && I2C && EXPERIMENTAL @@ -121,7 +131,7 @@ config SENSORS_ATXP1 config SENSORS_DS1621 tristate "Dallas Semiconductor DS1621 and DS1625" - depends on HWMON && I2C && EXPERIMENTAL + depends on HWMON && I2C help If you say yes here you get support for Dallas Semiconductor DS1621 and DS1625 sensor chips. @@ -141,7 +151,7 @@ config SENSORS_F71805F config SENSORS_FSCHER tristate "FSC Hermes" - depends on HWMON && I2C && EXPERIMENTAL + depends on HWMON && I2C help If you say yes here you get support for Fujitsu Siemens Computers Hermes sensor chips. @@ -151,7 +161,7 @@ config SENSORS_FSCHER config SENSORS_FSCPOS tristate "FSC Poseidon" - depends on HWMON && I2C && EXPERIMENTAL + depends on HWMON && I2C help If you say yes here you get support for Fujitsu Siemens Computers Poseidon sensor chips. @@ -171,7 +181,7 @@ config SENSORS_GL518SM config SENSORS_GL520SM tristate "Genesys Logic GL520SM" - depends on HWMON && I2C && EXPERIMENTAL + depends on HWMON && I2C select HWMON_VID help If you say yes here you get support for Genesys Logic GL520SM @@ -186,15 +196,15 @@ config SENSORS_IT87 select I2C_ISA select HWMON_VID help - If you say yes here you get support for ITE IT87xx sensor chips - and clones: SiS960. + If you say yes here you get support for ITE IT8705F, IT8712F, + IT8716F and IT8718F sensor chips, and the SiS960 clone. This driver can also be built as a module. If so, the module will be called it87. config SENSORS_LM63 tristate "National Semiconductor LM63" - depends on HWMON && I2C && EXPERIMENTAL + depends on HWMON && I2C help If you say yes here you get support for the National Semiconductor LM63 remote diode digital temperature sensor with integrated fan @@ -231,7 +241,7 @@ config SENSORS_LM75 config SENSORS_LM77 tristate "National Semiconductor LM77" - depends on HWMON && I2C && EXPERIMENTAL + depends on HWMON && I2C help If you say yes here you get support for National Semiconductor LM77 sensor chips. @@ -241,7 +251,7 @@ config SENSORS_LM77 config SENSORS_LM78 tristate "National Semiconductor LM78 and compatibles" - depends on HWMON && I2C && EXPERIMENTAL + depends on HWMON && I2C select I2C_ISA select HWMON_VID help @@ -284,7 +294,7 @@ config SENSORS_LM85 config SENSORS_LM87 tristate "National Semiconductor LM87" - depends on HWMON && I2C && EXPERIMENTAL + depends on HWMON && I2C select HWMON_VID help If you say yes here you get support for National Semiconductor LM87 @@ -309,7 +319,7 @@ config SENSORS_LM90 config SENSORS_LM92 tristate "National Semiconductor LM92 and compatibles" - depends on HWMON && I2C && EXPERIMENTAL + depends on HWMON && I2C help If you say yes here you get support for National Semiconductor LM92 and Maxim MAX6635 sensor chips. @@ -319,7 +329,7 @@ config SENSORS_LM92 config SENSORS_MAX1619 tristate "Maxim MAX1619 sensor chip" - depends on HWMON && I2C && EXPERIMENTAL + depends on HWMON && I2C help If you say yes here you get support for MAX1619 sensor chip. @@ -354,7 +364,7 @@ config SENSORS_SIS5595 config SENSORS_SMSC47M1 tristate "SMSC LPC47M10x and compatibles" - depends on HWMON && I2C && EXPERIMENTAL + depends on HWMON && I2C select I2C_ISA help If you say yes here you get support for the integrated fan @@ -407,8 +417,19 @@ config SENSORS_VIA686A This driver can also be built as a module. If so, the module will be called via686a. +config SENSORS_VT1211 + tristate "VIA VT1211" + depends on HWMON && EXPERIMENTAL + select HWMON_VID + help + If you say yes here then you get support for hardware monitoring + features of the VIA VT1211 Super-I/O chip. + + This driver can also be built as a module. If so, the module + will be called vt1211. + config SENSORS_VT8231 - tristate "VT8231" + tristate "VIA VT8231" depends on HWMON && I2C && PCI && EXPERIMENTAL select HWMON_VID select I2C_ISA diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 31415843a91..af01cc64f7d 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_SENSORS_IT87) += it87.o +obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o obj-$(CONFIG_SENSORS_LM63) += lm63.o obj-$(CONFIG_SENSORS_LM70) += lm70.o obj-$(CONFIG_SENSORS_LM75) += lm75.o @@ -45,6 +46,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o +obj-$(CONFIG_SENSORS_VT1211) += vt1211.o obj-$(CONFIG_SENSORS_VT8231) += vt8231.o obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c index 35ad1b03272..e5cb0fdab9b 100644 --- a/drivers/hwmon/abituguru.c +++ b/drivers/hwmon/abituguru.c @@ -1354,13 +1354,39 @@ LEAVE_UPDATE: return NULL; } +#ifdef CONFIG_PM +static int abituguru_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct abituguru_data *data = platform_get_drvdata(pdev); + /* make sure all communications with the uguru are done and no new + ones are started */ + mutex_lock(&data->update_lock); + return 0; +} + +static int abituguru_resume(struct platform_device *pdev) +{ + struct abituguru_data *data = platform_get_drvdata(pdev); + /* See if the uGuru is still ready */ + if (inb_p(data->addr + ABIT_UGURU_DATA) != ABIT_UGURU_STATUS_INPUT) + data->uguru_ready = 0; + mutex_unlock(&data->update_lock); + return 0; +} +#else +#define abituguru_suspend NULL +#define abituguru_resume NULL +#endif /* CONFIG_PM */ + static struct platform_driver abituguru_driver = { .driver = { .owner = THIS_MODULE, .name = ABIT_UGURU_NAME, }, - .probe = abituguru_probe, - .remove = __devexit_p(abituguru_remove), + .probe = abituguru_probe, + .remove = __devexit_p(abituguru_remove), + .suspend = abituguru_suspend, + .resume = abituguru_resume, }; static int __init abituguru_detect(void) diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c index 2b6e74dd4a8..c466329b2ef 100644 --- a/drivers/hwmon/adm1021.c +++ b/drivers/hwmon/adm1021.c @@ -190,6 +190,21 @@ static int adm1021_attach_adapter(struct i2c_adapter *adapter) return i2c_probe(adapter, &addr_data, adm1021_detect); } +static struct attribute *adm1021_attributes[] = { + &dev_attr_temp1_max.attr, + &dev_attr_temp1_min.attr, + &dev_attr_temp1_input.attr, + &dev_attr_temp2_max.attr, + &dev_attr_temp2_min.attr, + &dev_attr_temp2_input.attr, + &dev_attr_alarms.attr, + NULL +}; + +static const struct attribute_group adm1021_group = { + .attrs = adm1021_attributes, +}; + static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind) { int i; @@ -287,22 +302,19 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind) adm1021_init_client(new_client); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1021_group))) + goto error2; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto error2; + goto error3; } - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_temp2_min); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_alarms); - return 0; +error3: + sysfs_remove_group(&new_client->dev.kobj, &adm1021_group); error2: i2c_detach_client(new_client); error1: @@ -326,6 +338,7 @@ static int adm1021_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &adm1021_group); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c index a4c859c9fbf..8c562885b54 100644 --- a/drivers/hwmon/adm1025.c +++ b/drivers/hwmon/adm1025.c @@ -315,6 +315,49 @@ static int adm1025_attach_adapter(struct i2c_adapter *adapter) return i2c_probe(adapter, &addr_data, adm1025_detect); } +static struct attribute *adm1025_attributes[] = { + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + &dev_attr_in2_input.attr, + &dev_attr_in3_input.attr, + &dev_attr_in5_input.attr, + &dev_attr_in0_min.attr, + &dev_attr_in1_min.attr, + &dev_attr_in2_min.attr, + &dev_attr_in3_min.attr, + &dev_attr_in5_min.attr, + &dev_attr_in0_max.attr, + &dev_attr_in1_max.attr, + &dev_attr_in2_max.attr, + &dev_attr_in3_max.attr, + &dev_attr_in5_max.attr, + &dev_attr_temp1_input.attr, + &dev_attr_temp2_input.attr, + &dev_attr_temp1_min.attr, + &dev_attr_temp2_min.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp2_max.attr, + &dev_attr_alarms.attr, + &dev_attr_cpu0_vid.attr, + &dev_attr_vrm.attr, + NULL +}; + +static const struct attribute_group adm1025_group = { + .attrs = adm1025_attributes, +}; + +static struct attribute *adm1025_attributes_opt[] = { + &dev_attr_in4_input.attr, + &dev_attr_in4_min.attr, + &dev_attr_in4_max.attr, + NULL +}; + +static const struct attribute_group adm1025_group_opt = { + .attrs = adm1025_attributes_opt, +}; + /* * The following function does more than just detection. If detection * succeeds, it also registers the new chip. @@ -415,46 +458,31 @@ static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind) adm1025_init_client(new_client); /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); + if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1025_group))) goto exit_detach; - } - - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in5_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in5_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_in5_max); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp2_min); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - device_create_file(&new_client->dev, &dev_attr_vrm); /* Pin 11 is either in4 (+12V) or VID4 */ if (!(config & 0x20)) { - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in4_max); + if ((err = device_create_file(&new_client->dev, + &dev_attr_in4_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in4_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in4_max))) + goto exit_remove; + } + + data->class_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove; } return 0; +exit_remove: + sysfs_remove_group(&new_client->dev.kobj, &adm1025_group); + sysfs_remove_group(&new_client->dev.kobj, &adm1025_group_opt); exit_detach: i2c_detach_client(new_client); exit_free: @@ -511,6 +539,8 @@ static int adm1025_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &adm1025_group); + sysfs_remove_group(&client->dev.kobj, &adm1025_group_opt); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index 6d4f8b8d358..b4618b2705f 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -323,15 +323,6 @@ static int adm1026_attach_adapter(struct i2c_adapter *adapter) return i2c_probe(adapter, &addr_data, adm1026_detect); } -static int adm1026_detach_client(struct i2c_client *client) -{ - struct adm1026_data *data = i2c_get_clientdata(client); - hwmon_device_unregister(data->class_dev); - i2c_detach_client(client); - kfree(data); - return 0; -} - static int adm1026_read_value(struct i2c_client *client, u8 reg) { int res; @@ -1450,6 +1441,135 @@ static DEVICE_ATTR(temp1_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL); static DEVICE_ATTR(temp2_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL); static DEVICE_ATTR(temp3_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL); +static struct attribute *adm1026_attributes[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in8_max.dev_attr.attr, + &sensor_dev_attr_in8_min.dev_attr.attr, + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in9_max.dev_attr.attr, + &sensor_dev_attr_in9_min.dev_attr.attr, + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in10_max.dev_attr.attr, + &sensor_dev_attr_in10_min.dev_attr.attr, + &sensor_dev_attr_in11_input.dev_attr.attr, + &sensor_dev_attr_in11_max.dev_attr.attr, + &sensor_dev_attr_in11_min.dev_attr.attr, + &sensor_dev_attr_in12_input.dev_attr.attr, + &sensor_dev_attr_in12_max.dev_attr.attr, + &sensor_dev_attr_in12_min.dev_attr.attr, + &sensor_dev_attr_in13_input.dev_attr.attr, + &sensor_dev_attr_in13_max.dev_attr.attr, + &sensor_dev_attr_in13_min.dev_attr.attr, + &sensor_dev_attr_in14_input.dev_attr.attr, + &sensor_dev_attr_in14_max.dev_attr.attr, + &sensor_dev_attr_in14_min.dev_attr.attr, + &sensor_dev_attr_in15_input.dev_attr.attr, + &sensor_dev_attr_in15_max.dev_attr.attr, + &sensor_dev_attr_in15_min.dev_attr.attr, + &sensor_dev_attr_in16_input.dev_attr.attr, + &sensor_dev_attr_in16_max.dev_attr.attr, + &sensor_dev_attr_in16_min.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_div.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_div.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan3_div.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan4_div.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan5_div.dev_attr.attr, + &sensor_dev_attr_fan5_min.dev_attr.attr, + &sensor_dev_attr_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan6_div.dev_attr.attr, + &sensor_dev_attr_fan6_min.dev_attr.attr, + &sensor_dev_attr_fan7_input.dev_attr.attr, + &sensor_dev_attr_fan7_div.dev_attr.attr, + &sensor_dev_attr_fan7_min.dev_attr.attr, + &sensor_dev_attr_fan8_input.dev_attr.attr, + &sensor_dev_attr_fan8_div.dev_attr.attr, + &sensor_dev_attr_fan8_min.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp1_offset.dev_attr.attr, + &sensor_dev_attr_temp2_offset.dev_attr.attr, + &sensor_dev_attr_temp3_offset.dev_attr.attr, + &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_temp3_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_temp3_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &dev_attr_temp1_crit_enable.attr, + &dev_attr_temp2_crit_enable.attr, + &dev_attr_temp3_crit_enable.attr, + &dev_attr_cpu0_vid.attr, + &dev_attr_vrm.attr, + &dev_attr_alarms.attr, + &dev_attr_alarm_mask.attr, + &dev_attr_gpio.attr, + &dev_attr_gpio_mask.attr, + &dev_attr_pwm1.attr, + &dev_attr_pwm2.attr, + &dev_attr_pwm3.attr, + &dev_attr_pwm1_enable.attr, + &dev_attr_pwm2_enable.attr, + &dev_attr_pwm3_enable.attr, + &dev_attr_temp1_auto_point1_pwm.attr, + &dev_attr_temp2_auto_point1_pwm.attr, + &dev_attr_temp3_auto_point1_pwm.attr, + &dev_attr_temp1_auto_point2_pwm.attr, + &dev_attr_temp2_auto_point2_pwm.attr, + &dev_attr_temp3_auto_point2_pwm.attr, + &dev_attr_analog_out.attr, + NULL +}; + +static const struct attribute_group adm1026_group = { + .attrs = adm1026_attributes, +}; + static int adm1026_detect(struct i2c_adapter *adapter, int address, int kind) { @@ -1554,145 +1674,20 @@ static int adm1026_detect(struct i2c_adapter *adapter, int address, adm1026_init_client(new_client); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1026_group))) + goto exitdetach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exitdetach; + goto exitremove; } - device_create_file(&new_client->dev, &sensor_dev_attr_in0_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in0_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in0_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in1_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in1_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in1_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in2_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in2_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in2_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in3_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in3_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in3_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in4_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in4_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in4_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in5_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in5_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in5_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in6_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in6_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in6_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in7_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in7_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in7_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in8_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in8_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in8_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in9_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in9_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in9_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in10_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in10_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in10_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in11_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in11_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in11_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in12_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in12_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in12_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in13_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in13_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in13_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in14_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in14_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in14_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in15_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in15_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in15_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in16_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in16_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in16_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan1_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan1_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan1_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan2_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan2_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan2_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan3_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan3_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan3_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan4_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan4_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan4_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan5_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan5_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan5_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan6_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan6_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan6_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan7_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan7_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan7_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan8_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan8_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan8_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_offset.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_offset.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_offset.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_auto_point1_temp.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_auto_point1_temp.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_auto_point1_temp.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_auto_point1_temp_hyst.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_auto_point1_temp_hyst.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_auto_point2_temp.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_auto_point2_temp.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_auto_point2_temp.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_crit.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_crit.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_crit.dev_attr); - device_create_file(&new_client->dev, &dev_attr_temp1_crit_enable); - device_create_file(&new_client->dev, &dev_attr_temp2_crit_enable); - device_create_file(&new_client->dev, &dev_attr_temp3_crit_enable); - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - device_create_file(&new_client->dev, &dev_attr_vrm); - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_alarm_mask); - device_create_file(&new_client->dev, &dev_attr_gpio); - device_create_file(&new_client->dev, &dev_attr_gpio_mask); - device_create_file(&new_client->dev, &dev_attr_pwm1); - device_create_file(&new_client->dev, &dev_attr_pwm2); - device_create_file(&new_client->dev, &dev_attr_pwm3); - device_create_file(&new_client->dev, &dev_attr_pwm1_enable); - device_create_file(&new_client->dev, &dev_attr_pwm2_enable); - device_create_file(&new_client->dev, &dev_attr_pwm3_enable); - device_create_file(&new_client->dev, &dev_attr_temp1_auto_point1_pwm); - device_create_file(&new_client->dev, &dev_attr_temp2_auto_point1_pwm); - device_create_file(&new_client->dev, &dev_attr_temp3_auto_point1_pwm); - device_create_file(&new_client->dev, &dev_attr_temp1_auto_point2_pwm); - device_create_file(&new_client->dev, &dev_attr_temp2_auto_point2_pwm); - device_create_file(&new_client->dev, &dev_attr_temp3_auto_point2_pwm); - device_create_file(&new_client->dev, &dev_attr_analog_out); return 0; /* Error out and cleanup code */ +exitremove: + sysfs_remove_group(&new_client->dev.kobj, &adm1026_group); exitdetach: i2c_detach_client(new_client); exitfree: @@ -1700,6 +1695,17 @@ exitfree: exit: return err; } + +static int adm1026_detach_client(struct i2c_client *client) +{ + struct adm1026_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &adm1026_group); + i2c_detach_client(client); + kfree(data); + return 0; +} + static int __init sm_adm1026_init(void) { return i2c_add_driver(&adm1026_driver); diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c index 3bf2da621ae..122683fc91d 100644 --- a/drivers/hwmon/adm1031.c +++ b/drivers/hwmon/adm1031.c @@ -730,6 +730,61 @@ static int adm1031_attach_adapter(struct i2c_adapter *adapter) return i2c_probe(adapter, &addr_data, adm1031_detect); } +static struct attribute *adm1031_attributes[] = { + &dev_attr_fan1_input.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan1_min.attr, + &dev_attr_pwm1.attr, + &dev_attr_auto_fan1_channel.attr, + &dev_attr_temp1_input.attr, + &dev_attr_temp1_min.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp1_crit.attr, + &dev_attr_temp2_input.attr, + &dev_attr_temp2_min.attr, + &dev_attr_temp2_max.attr, + &dev_attr_temp2_crit.attr, + + &dev_attr_auto_temp1_off.attr, + &dev_attr_auto_temp1_min.attr, + &dev_attr_auto_temp1_max.attr, + + &dev_attr_auto_temp2_off.attr, + &dev_attr_auto_temp2_min.attr, + &dev_attr_auto_temp2_max.attr, + + &dev_attr_auto_fan1_min_pwm.attr, + + &dev_attr_alarms.attr, + + NULL +}; + +static const struct attribute_group adm1031_group = { + .attrs = adm1031_attributes, +}; + +static struct attribute *adm1031_attributes_opt[] = { + &dev_attr_fan2_input.attr, + &dev_attr_fan2_div.attr, + &dev_attr_fan2_min.attr, + &dev_attr_pwm2.attr, + &dev_attr_auto_fan2_channel.attr, + &dev_attr_temp3_input.attr, + &dev_attr_temp3_min.attr, + &dev_attr_temp3_max.attr, + &dev_attr_temp3_crit.attr, + &dev_attr_auto_temp3_off.attr, + &dev_attr_auto_temp3_min.attr, + &dev_attr_auto_temp3_max.attr, + &dev_attr_auto_fan2_min_pwm.attr, + NULL +}; + +static const struct attribute_group adm1031_group_opt = { + .attrs = adm1031_attributes_opt, +}; + /* This function is called by i2c_probe */ static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind) { @@ -789,57 +844,26 @@ static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind) adm1031_init_client(new_client); /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); + if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1031_group))) goto exit_detach; - } - - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_pwm1); - device_create_file(&new_client->dev, &dev_attr_auto_fan1_channel); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_crit); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp2_min); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_temp2_crit); - - device_create_file(&new_client->dev, &dev_attr_auto_temp1_off); - device_create_file(&new_client->dev, &dev_attr_auto_temp1_min); - device_create_file(&new_client->dev, &dev_attr_auto_temp1_max); - - device_create_file(&new_client->dev, &dev_attr_auto_temp2_off); - device_create_file(&new_client->dev, &dev_attr_auto_temp2_min); - device_create_file(&new_client->dev, &dev_attr_auto_temp2_max); - - device_create_file(&new_client->dev, &dev_attr_auto_fan1_min_pwm); - - device_create_file(&new_client->dev, &dev_attr_alarms); if (kind == adm1031) { - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_pwm2); - device_create_file(&new_client->dev, - &dev_attr_auto_fan2_channel); - device_create_file(&new_client->dev, &dev_attr_temp3_input); - device_create_file(&new_client->dev, &dev_attr_temp3_min); - device_create_file(&new_client->dev, &dev_attr_temp3_max); - device_create_file(&new_client->dev, &dev_attr_temp3_crit); - device_create_file(&new_client->dev, &dev_attr_auto_temp3_off); - device_create_file(&new_client->dev, &dev_attr_auto_temp3_min); - device_create_file(&new_client->dev, &dev_attr_auto_temp3_max); - device_create_file(&new_client->dev, &dev_attr_auto_fan2_min_pwm); + if ((err = sysfs_create_group(&new_client->dev.kobj, + &adm1031_group_opt))) + goto exit_remove; + } + + data->class_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove; } return 0; +exit_remove: + sysfs_remove_group(&new_client->dev.kobj, &adm1031_group); + sysfs_remove_group(&new_client->dev.kobj, &adm1031_group_opt); exit_detach: i2c_detach_client(new_client); exit_free: @@ -854,6 +878,8 @@ static int adm1031_detach_client(struct i2c_client *client) int ret; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &adm1031_group); + sysfs_remove_group(&client->dev.kobj, &adm1031_group_opt); if ((ret = i2c_detach_client(client)) != 0) { return ret; } diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index 43f6991b588..377961c4a41 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -465,6 +465,45 @@ static ssize_t chassis_clear(struct device *dev, } static DEVICE_ATTR(chassis_clear, S_IWUSR, NULL, chassis_clear); +static struct attribute *adm9240_attributes[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &dev_attr_temp1_input.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_div.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_div.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &dev_attr_alarms.attr, + &dev_attr_aout_output.attr, + &dev_attr_chassis_clear.attr, + &dev_attr_cpu0_vid.attr, + NULL +}; + +static const struct attribute_group adm9240_group = { + .attrs = adm9240_attributes, +}; + /*** sensor chip detect and driver install ***/ @@ -548,72 +587,19 @@ static int adm9240_detect(struct i2c_adapter *adapter, int address, int kind) adm9240_init_client(new_client); /* populate sysfs filesystem */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &adm9240_group))) + goto exit_detach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove; } - device_create_file(&new_client->dev, - &sensor_dev_attr_in0_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in0_min.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in0_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in1_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in1_min.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in1_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in2_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in2_min.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in2_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in3_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in3_min.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in3_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in4_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in4_min.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in4_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in5_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in5_min.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_in5_max.dev_attr); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_max_hyst.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_fan1_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_fan1_div.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_fan1_min.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_fan2_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_fan2_div.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_fan2_min.dev_attr); - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_aout_output); - device_create_file(&new_client->dev, &dev_attr_chassis_clear); - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - return 0; +exit_remove: + sysfs_remove_group(&new_client->dev.kobj, &adm9240_group); exit_detach: i2c_detach_client(new_client); exit_free: @@ -635,6 +621,7 @@ static int adm9240_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &adm9240_group); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index facc1ccb833..57b1c7b7ac3 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -298,12 +298,6 @@ sysfs_in(4); sysfs_in(5); sysfs_in(6); -#define device_create_file_in(client, offset) do { \ - device_create_file(&client->dev, &dev_attr_in##offset##_input); \ - device_create_file(&client->dev, &dev_attr_in##offset##_min); \ - device_create_file(&client->dev, &dev_attr_in##offset##_max); \ -} while (0) - /* 3 Fans */ static ssize_t show_fan(struct device *dev, char *buf, int nr) { @@ -421,12 +415,6 @@ sysfs_fan(1); sysfs_fan(2); sysfs_fan(3); -#define device_create_file_fan(client, offset) do { \ - device_create_file(&client->dev, &dev_attr_fan##offset##_input); \ - device_create_file(&client->dev, &dev_attr_fan##offset##_min); \ - device_create_file(&client->dev, &dev_attr_fan##offset##_div); \ -} while (0) - /* 4 Temp. Sensors */ static int sprintf_temp_from_reg(u16 reg, char *buf, int nr) { @@ -515,12 +503,6 @@ sysfs_temp(3); sysfs_temp(4); /* VID */ -#define device_create_file_temp(client, num) do { \ - device_create_file(&client->dev, &dev_attr_temp##num##_input); \ - device_create_file(&client->dev, &dev_attr_temp##num##_max); \ - device_create_file(&client->dev, &dev_attr_temp##num##_max_hyst); \ -} while (0) - static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) { struct asb100_data *data = asb100_update_device(dev); @@ -528,8 +510,6 @@ static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); -#define device_create_file_vid(client) \ -device_create_file(&client->dev, &dev_attr_cpu0_vid) /* VRM */ static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) @@ -549,8 +529,6 @@ static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const /* Alarms */ static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); -#define device_create_file_vrm(client) \ -device_create_file(&client->dev, &dev_attr_vrm); static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) { @@ -559,8 +537,6 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, ch } static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); -#define device_create_file_alarms(client) \ -device_create_file(&client->dev, &dev_attr_alarms) /* 1 PWM */ static ssize_t show_pwm1(struct device *dev, struct device_attribute *attr, char *buf) @@ -607,10 +583,65 @@ static ssize_t set_pwm_enable1(struct device *dev, struct device_attribute *attr static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm1, set_pwm1); static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable1, set_pwm_enable1); -#define device_create_file_pwm1(client) do { \ - device_create_file(&new_client->dev, &dev_attr_pwm1); \ - device_create_file(&new_client->dev, &dev_attr_pwm1_enable); \ -} while (0) + +static struct attribute *asb100_attributes[] = { + &dev_attr_in0_input.attr, + &dev_attr_in0_min.attr, + &dev_attr_in0_max.attr, + &dev_attr_in1_input.attr, + &dev_attr_in1_min.attr, + &dev_attr_in1_max.attr, + &dev_attr_in2_input.attr, + &dev_attr_in2_min.attr, + &dev_attr_in2_max.attr, + &dev_attr_in3_input.attr, + &dev_attr_in3_min.attr, + &dev_attr_in3_max.attr, + &dev_attr_in4_input.attr, + &dev_attr_in4_min.attr, + &dev_attr_in4_max.attr, + &dev_attr_in5_input.attr, + &dev_attr_in5_min.attr, + &dev_attr_in5_max.attr, + &dev_attr_in6_input.attr, + &dev_attr_in6_min.attr, + &dev_attr_in6_max.attr, + + &dev_attr_fan1_input.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan2_input.attr, + &dev_attr_fan2_min.attr, + &dev_attr_fan2_div.attr, + &dev_attr_fan3_input.attr, + &dev_attr_fan3_min.attr, + &dev_attr_fan3_div.attr, + + &dev_attr_temp1_input.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp1_max_hyst.attr, + &dev_attr_temp2_input.attr, + &dev_attr_temp2_max.attr, + &dev_attr_temp2_max_hyst.attr, + &dev_attr_temp3_input.attr, + &dev_attr_temp3_max.attr, + &dev_attr_temp3_max_hyst.attr, + &dev_attr_temp4_input.attr, + &dev_attr_temp4_max.attr, + &dev_attr_temp4_max_hyst.attr, + + &dev_attr_cpu0_vid.attr, + &dev_attr_vrm.attr, + &dev_attr_alarms.attr, + &dev_attr_pwm1.attr, + &dev_attr_pwm1_enable.attr, + + NULL +}; + +static const struct attribute_group asb100_group = { + .attrs = asb100_attributes, +}; /* This function is called when: asb100_driver is inserted (when this module is loaded), for each @@ -810,38 +841,19 @@ static int asb100_detect(struct i2c_adapter *adapter, int address, int kind) data->fan_min[2] = asb100_read_value(new_client, ASB100_REG_FAN_MIN(2)); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &asb100_group))) + goto ERROR3; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto ERROR3; + goto ERROR4; } - device_create_file_in(new_client, 0); - device_create_file_in(new_client, 1); - device_create_file_in(new_client, 2); - device_create_file_in(new_client, 3); - device_create_file_in(new_client, 4); - device_create_file_in(new_client, 5); - device_create_file_in(new_client, 6); - - device_create_file_fan(new_client, 1); - device_create_file_fan(new_client, 2); - device_create_file_fan(new_client, 3); - - device_create_file_temp(new_client, 1); - device_create_file_temp(new_client, 2); - device_create_file_temp(new_client, 3); - device_create_file_temp(new_client, 4); - - device_create_file_vid(new_client); - device_create_file_vrm(new_client); - - device_create_file_alarms(new_client); - - device_create_file_pwm1(new_client); - return 0; +ERROR4: + sysfs_remove_group(&new_client->dev.kobj, &asb100_group); ERROR3: i2c_detach_client(data->lm75[1]); i2c_detach_client(data->lm75[0]); @@ -861,8 +873,10 @@ static int asb100_detach_client(struct i2c_client *client) int err; /* main client */ - if (data) + if (data) { hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &asb100_group); + } if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c index 728a1e8b919..0ccdd0750c4 100644 --- a/drivers/hwmon/atxp1.c +++ b/drivers/hwmon/atxp1.c @@ -27,6 +27,7 @@ #include <linux/hwmon-vid.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/sysfs.h> MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("System voltages control via Attansic ATXP1"); @@ -116,8 +117,7 @@ static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *att { struct atxp1_data *data; struct i2c_client *client; - char vid; - char cvid; + int vid, cvid; unsigned int vcore; client = to_i2c_client(dev); @@ -251,6 +251,17 @@ static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *att */ static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2); +static struct attribute *atxp1_attributes[] = { + &dev_attr_gpio1.attr, + &dev_attr_gpio2.attr, + &dev_attr_cpu0_vid.attr, + NULL +}; + +static const struct attribute_group atxp1_group = { + .attrs = atxp1_attributes, +}; + static int atxp1_attach_adapter(struct i2c_adapter *adapter) { @@ -320,21 +331,23 @@ static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind) goto exit_free; } + /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &atxp1_group))) + goto exit_detach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove_files; } - device_create_file(&new_client->dev, &dev_attr_gpio1); - device_create_file(&new_client->dev, &dev_attr_gpio2); - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - dev_info(&new_client->dev, "Using VRM: %d.%d\n", data->vrm / 10, data->vrm % 10); return 0; +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &atxp1_group); exit_detach: i2c_detach_client(new_client); exit_free: @@ -349,6 +362,7 @@ static int atxp1_detach_client(struct i2c_client * client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &atxp1_group); err = i2c_detach_client(client); diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index 478eb4bb857..c849c0c6ee9 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -29,6 +29,7 @@ #include <linux/hwmon.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/sysfs.h> #include "lm75.h" /* Addresses to scan */ @@ -178,6 +179,18 @@ static DEVICE_ATTR(temp1_input, S_IRUGO , show_temp, NULL); static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO , show_temp_min, set_temp_min); static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max); +static struct attribute *ds1621_attributes[] = { + &dev_attr_temp1_input.attr, + &dev_attr_temp1_min.attr, + &dev_attr_temp1_max.attr, + &dev_attr_alarms.attr, + NULL +}; + +static const struct attribute_group ds1621_group = { + .attrs = ds1621_attributes, +}; + static int ds1621_attach_adapter(struct i2c_adapter *adapter) { @@ -253,21 +266,19 @@ static int ds1621_detect(struct i2c_adapter *adapter, int address, ds1621_init_client(new_client); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &ds1621_group))) + goto exit_detach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove_files; } - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - return 0; -/* OK, this is not exactly good programming practice, usually. But it is - very code-efficient in this case. */ + exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &ds1621_group); exit_detach: i2c_detach_client(new_client); exit_free: @@ -282,6 +293,7 @@ static int ds1621_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &ds1621_group); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index fd72440faf7..de17a72149d 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -1,7 +1,7 @@ /* * f71805f.c - driver for the Fintek F71805F/FG Super-I/O chip integrated * hardware monitoring features - * Copyright (C) 2005 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2005-2006 Jean Delvare <khali@linux-fr.org> * * The F71805F/FG is a LPC Super-I/O chip made by Fintek. It integrates * complete hardware monitoring features: voltage, fan and temperature @@ -31,6 +31,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/sysfs.h> #include <asm/io.h> static struct platform_device *pdev; @@ -147,7 +148,7 @@ struct f71805f_data { u8 temp_high[3]; u8 temp_hyst[3]; u8 temp_mode; - u8 alarms[3]; + unsigned long alarms; }; static inline long in_from_reg(u8 reg) @@ -311,10 +312,9 @@ static struct f71805f_data *f71805f_update_device(struct device *dev) data->temp[nr] = f71805f_read8(data, F71805F_REG_TEMP(nr)); } - for (nr = 0; nr < 3; nr++) { - data->alarms[nr] = f71805f_read8(data, - F71805F_REG_STATUS(nr)); - } + data->alarms = f71805f_read8(data, F71805F_REG_STATUS(0)) + + (f71805f_read8(data, F71805F_REG_STATUS(1)) << 8) + + (f71805f_read8(data, F71805F_REG_STATUS(2)) << 16); data->last_updated = jiffies; data->valid = 1; @@ -557,8 +557,7 @@ static ssize_t show_alarms_in(struct device *dev, struct device_attribute { struct f71805f_data *data = f71805f_update_device(dev); - return sprintf(buf, "%d\n", data->alarms[0] | - ((data->alarms[1] & 0x01) << 8)); + return sprintf(buf, "%lu\n", data->alarms & 0x1ff); } static ssize_t show_alarms_fan(struct device *dev, struct device_attribute @@ -566,7 +565,7 @@ static ssize_t show_alarms_fan(struct device *dev, struct device_attribute { struct f71805f_data *data = f71805f_update_device(dev); - return sprintf(buf, "%d\n", data->alarms[2] & 0x07); + return sprintf(buf, "%lu\n", (data->alarms >> 16) & 0x07); } static ssize_t show_alarms_temp(struct device *dev, struct device_attribute @@ -574,7 +573,17 @@ static ssize_t show_alarms_temp(struct device *dev, struct device_attribute { struct f71805f_data *data = f71805f_update_device(dev); - return sprintf(buf, "%d\n", (data->alarms[1] >> 3) & 0x07); + return sprintf(buf, "%lu\n", (data->alarms >> 11) & 0x07); +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int bitnr = attr->index; + + return sprintf(buf, "%lu\n", (data->alarms >> bitnr) & 1); } static ssize_t show_name(struct device *dev, struct device_attribute @@ -585,88 +594,189 @@ static ssize_t show_name(struct device *dev, struct device_attribute return sprintf(buf, "%s\n", data->name); } -static struct device_attribute f71805f_dev_attr[] = { - __ATTR(in0_input, S_IRUGO, show_in0, NULL), - __ATTR(in0_max, S_IRUGO| S_IWUSR, show_in0_max, set_in0_max), - __ATTR(in0_min, S_IRUGO| S_IWUSR, show_in0_min, set_in0_min), - __ATTR(alarms_in, S_IRUGO, show_alarms_in, NULL), - __ATTR(alarms_fan, S_IRUGO, show_alarms_fan, NULL), - __ATTR(alarms_temp, S_IRUGO, show_alarms_temp, NULL), - __ATTR(name, S_IRUGO, show_name, NULL), +static DEVICE_ATTR(in0_input, S_IRUGO, show_in0, NULL); +static DEVICE_ATTR(in0_max, S_IRUGO| S_IWUSR, show_in0_max, set_in0_max); +static DEVICE_ATTR(in0_min, S_IRUGO| S_IWUSR, show_in0_min, set_in0_min); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1); +static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR, + show_in_max, set_in_max, 1); +static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO | S_IWUSR, + show_in_min, set_in_min, 1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2); +static SENSOR_DEVICE_ATTR(in2_max, S_IRUGO | S_IWUSR, + show_in_max, set_in_max, 2); +static SENSOR_DEVICE_ATTR(in2_min, S_IRUGO | S_IWUSR, + show_in_min, set_in_min, 2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3); +static SENSOR_DEVICE_ATTR(in3_max, S_IRUGO | S_IWUSR, + show_in_max, set_in_max, 3); +static SENSOR_DEVICE_ATTR(in3_min, S_IRUGO | S_IWUSR, + show_in_min, set_in_min, 3); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4); +static SENSOR_DEVICE_ATTR(in4_max, S_IRUGO | S_IWUSR, + show_in_max, set_in_max, 4); +static SENSOR_DEVICE_ATTR(in4_min, S_IRUGO | S_IWUSR, + show_in_min, set_in_min, 4); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in, NULL, 5); +static SENSOR_DEVICE_ATTR(in5_max, S_IRUGO | S_IWUSR, + show_in_max, set_in_max, 5); +static SENSOR_DEVICE_ATTR(in5_min, S_IRUGO | S_IWUSR, + show_in_min, set_in_min, 5); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in, NULL, 6); +static SENSOR_DEVICE_ATTR(in6_max, S_IRUGO | S_IWUSR, + show_in_max, set_in_max, 6); +static SENSOR_DEVICE_ATTR(in6_min, S_IRUGO | S_IWUSR, + show_in_min, set_in_min, 6); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_in, NULL, 7); +static SENSOR_DEVICE_ATTR(in7_max, S_IRUGO | S_IWUSR, + show_in_max, set_in_max, 7); +static SENSOR_DEVICE_ATTR(in7_min, S_IRUGO | S_IWUSR, + show_in_min, set_in_min, 7); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_in, NULL, 8); +static SENSOR_DEVICE_ATTR(in8_max, S_IRUGO | S_IWUSR, + show_in_max, set_in_max, 8); +static SENSOR_DEVICE_ATTR(in8_min, S_IRUGO | S_IWUSR, + show_in_min, set_in_min, 8); + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, + show_fan_min, set_fan_min, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR, + show_fan_min, set_fan_min, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan3_min, S_IRUGO | S_IWUSR, + show_fan_min, set_fan_min, 2); + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, + show_temp_max, set_temp_max, 0); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, + show_temp_hyst, set_temp_hyst, 0); +static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, + show_temp_max, set_temp_max, 1); +static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, + show_temp_hyst, set_temp_hyst, 1); +static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR, + show_temp_max, set_temp_max, 2); +static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, + show_temp_hyst, set_temp_hyst, 2); +static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2); + +static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7); +static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8); +static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 11); +static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 12); +static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13); +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 16); +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 17); +static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 18); +static DEVICE_ATTR(alarms_in, S_IRUGO, show_alarms_in, NULL); +static DEVICE_ATTR(alarms_fan, S_IRUGO, show_alarms_fan, NULL); +static DEVICE_ATTR(alarms_temp, S_IRUGO, show_alarms_temp, NULL); + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static struct attribute *f71805f_attributes[] = { + &dev_attr_in0_input.attr, + &dev_attr_in0_max.attr, + &dev_attr_in0_min.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in8_max.dev_attr.attr, + &sensor_dev_attr_in8_min.dev_attr.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_type.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_type.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_type.dev_attr.attr, + + &sensor_dev_attr_in0_alarm.dev_attr.attr, + &sensor_dev_attr_in1_alarm.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + &sensor_dev_attr_in6_alarm.dev_attr.attr, + &sensor_dev_attr_in7_alarm.dev_attr.attr, + &sensor_dev_attr_in8_alarm.dev_attr.attr, + &dev_attr_alarms_in.attr, + &sensor_dev_attr_temp1_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_alarm.dev_attr.attr, + &dev_attr_alarms_temp.attr, + &dev_attr_alarms_fan.attr, + + &dev_attr_name.attr, + NULL }; -static struct sensor_device_attribute f71805f_sensor_attr[] = { - SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1), - SENSOR_ATTR(in1_max, S_IRUGO | S_IWUSR, - show_in_max, set_in_max, 1), - SENSOR_ATTR(in1_min, S_IRUGO | S_IWUSR, - show_in_min, set_in_min, 1), - SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2), - SENSOR_ATTR(in2_max, S_IRUGO | S_IWUSR, - show_in_max, set_in_max, 2), - SENSOR_ATTR(in2_min, S_IRUGO | S_IWUSR, - show_in_min, set_in_min, 2), - SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3), - SENSOR_ATTR(in3_max, S_IRUGO | S_IWUSR, - show_in_max, set_in_max, 3), - SENSOR_ATTR(in3_min, S_IRUGO | S_IWUSR, - show_in_min, set_in_min, 3), - SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4), - SENSOR_ATTR(in4_max, S_IRUGO | S_IWUSR, - show_in_max, set_in_max, 4), - SENSOR_ATTR(in4_min, S_IRUGO | S_IWUSR, - show_in_min, set_in_min, 4), - SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5), - SENSOR_ATTR(in5_max, S_IRUGO | S_IWUSR, - show_in_max, set_in_max, 5), - SENSOR_ATTR(in5_min, S_IRUGO | S_IWUSR, - show_in_min, set_in_min, 5), - SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6), - SENSOR_ATTR(in6_max, S_IRUGO | S_IWUSR, - show_in_max, set_in_max, 6), - SENSOR_ATTR(in6_min, S_IRUGO | S_IWUSR, - show_in_min, set_in_min, 6), - SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7), - SENSOR_ATTR(in7_max, S_IRUGO | S_IWUSR, - show_in_max, set_in_max, 7), - SENSOR_ATTR(in7_min, S_IRUGO | S_IWUSR, - show_in_min, set_in_min, 7), - SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8), - SENSOR_ATTR(in8_max, S_IRUGO | S_IWUSR, - show_in_max, set_in_max, 8), - SENSOR_ATTR(in8_min, S_IRUGO | S_IWUSR, - show_in_min, set_in_min, 8), - - SENSOR_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0), - SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR, - show_temp_max, set_temp_max, 0), - SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, - show_temp_hyst, set_temp_hyst, 0), - SENSOR_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0), - SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1), - SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR, - show_temp_max, set_temp_max, 1), - SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, - show_temp_hyst, set_temp_hyst, 1), - SENSOR_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1), - SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2), - SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, - show_temp_max, set_temp_max, 2), - SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, - show_temp_hyst, set_temp_hyst, 2), - SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2), +static const struct attribute_group f71805f_group = { + .attrs = f71805f_attributes, }; -static struct sensor_device_attribute f71805f_fan_attr[] = { - SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0), - SENSOR_ATTR(fan1_min, S_IRUGO | S_IWUSR, - show_fan_min, set_fan_min, 0), - SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1), - SENSOR_ATTR(fan2_min, S_IRUGO | S_IWUSR, - show_fan_min, set_fan_min, 1), - SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2), - SENSOR_ATTR(fan3_min, S_IRUGO | S_IWUSR, - show_fan_min, set_fan_min, 2), +static struct attribute *f71805f_attributes_fan[3][4] = { + { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan3_alarm.dev_attr.attr, + NULL + } +}; + +static const struct attribute_group f71805f_group_fan[3] = { + { .attrs = f71805f_attributes_fan[0] }, + { .attrs = f71805f_attributes_fan[1] }, + { .attrs = f71805f_attributes_fan[2] }, }; /* @@ -714,43 +824,35 @@ static int __devinit f71805f_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); - data->class_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); - dev_err(&pdev->dev, "Class registration failed (%d)\n", err); - goto exit_free; - } - /* Initialize the F71805F chip */ f71805f_init_device(data); /* Register sysfs interface files */ - for (i = 0; i < ARRAY_SIZE(f71805f_dev_attr); i++) { - err = device_create_file(&pdev->dev, &f71805f_dev_attr[i]); - if (err) - goto exit_class; - } - for (i = 0; i < ARRAY_SIZE(f71805f_sensor_attr); i++) { - err = device_create_file(&pdev->dev, - &f71805f_sensor_attr[i].dev_attr); - if (err) - goto exit_class; - } - for (i = 0; i < ARRAY_SIZE(f71805f_fan_attr); i++) { - if (!(data->fan_enabled & (1 << (i / 2)))) + if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group))) + goto exit_free; + for (i = 0; i < 3; i++) { + if (!(data->fan_enabled & (1 << i))) continue; - err = device_create_file(&pdev->dev, - &f71805f_fan_attr[i].dev_attr); - if (err) - goto exit_class; + if ((err = sysfs_create_group(&pdev->dev.kobj, + &f71805f_group_fan[i]))) + goto exit_remove_files; + } + + data->class_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + dev_err(&pdev->dev, "Class registration failed (%d)\n", err); + goto exit_remove_files; } return 0; -exit_class: - dev_err(&pdev->dev, "Sysfs interface creation failed\n"); - hwmon_device_unregister(data->class_dev); +exit_remove_files: + sysfs_remove_group(&pdev->dev.kobj, &f71805f_group); + for (i = 0; i < 3; i++) + sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]); exit_free: + platform_set_drvdata(pdev, NULL); kfree(data); exit: return err; @@ -759,9 +861,13 @@ exit: static int __devexit f71805f_remove(struct platform_device *pdev) { struct f71805f_data *data = platform_get_drvdata(pdev); + int i; platform_set_drvdata(pdev, NULL); hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&pdev->dev.kobj, &f71805f_group); + for (i = 0; i < 3; i++) + sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]); kfree(data); return 0; diff --git a/drivers/hwmon/fscher.c b/drivers/hwmon/fscher.c index 6bc76b40763..19717752cfc 100644 --- a/drivers/hwmon/fscher.c +++ b/drivers/hwmon/fscher.c @@ -34,6 +34,7 @@ #include <linux/hwmon.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/sysfs.h> /* * Addresses to scan @@ -240,47 +241,45 @@ sysfs_alarms(FSCHER_REG_EVENTS) sysfs_control(FSCHER_REG_CONTROL) sysfs_watchdog(FSCHER_REG_WDOG_CONTROL, FSCHER_REG_WDOG_STATE, FSCHER_REG_WDOG_PRESET) -#define device_create_file_fan(client, offset) \ -do { \ - device_create_file(&client->dev, &dev_attr_fan##offset##_status); \ - device_create_file(&client->dev, &dev_attr_pwm##offset); \ - device_create_file(&client->dev, &dev_attr_fan##offset##_div); \ - device_create_file(&client->dev, &dev_attr_fan##offset##_input); \ -} while (0) - -#define device_create_file_temp(client, offset) \ -do { \ - device_create_file(&client->dev, &dev_attr_temp##offset##_status); \ - device_create_file(&client->dev, &dev_attr_temp##offset##_input); \ -} while (0) - -#define device_create_file_in(client, offset) \ -do { \ - device_create_file(&client->dev, &dev_attr_in##offset##_input); \ -} while (0) - -#define device_create_file_revision(client) \ -do { \ - device_create_file(&client->dev, &dev_attr_revision); \ -} while (0) - -#define device_create_file_alarms(client) \ -do { \ - device_create_file(&client->dev, &dev_attr_alarms); \ -} while (0) - -#define device_create_file_control(client) \ -do { \ - device_create_file(&client->dev, &dev_attr_control); \ -} while (0) - -#define device_create_file_watchdog(client) \ -do { \ - device_create_file(&client->dev, &dev_attr_watchdog_status); \ - device_create_file(&client->dev, &dev_attr_watchdog_control); \ - device_create_file(&client->dev, &dev_attr_watchdog_preset); \ -} while (0) - +static struct attribute *fscher_attributes[] = { + &dev_attr_revision.attr, + &dev_attr_alarms.attr, + &dev_attr_control.attr, + + &dev_attr_watchdog_status.attr, + &dev_attr_watchdog_control.attr, + &dev_attr_watchdog_preset.attr, + + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + &dev_attr_in2_input.attr, + + &dev_attr_fan1_status.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan1_input.attr, + &dev_attr_pwm1.attr, + &dev_attr_fan2_status.attr, + &dev_attr_fan2_div.attr, + &dev_attr_fan2_input.attr, + &dev_attr_pwm2.attr, + &dev_attr_fan3_status.attr, + &dev_attr_fan3_div.attr, + &dev_attr_fan3_input.attr, + &dev_attr_pwm3.attr, + + &dev_attr_temp1_status.attr, + &dev_attr_temp1_input.attr, + &dev_attr_temp2_status.attr, + &dev_attr_temp2_input.attr, + &dev_attr_temp3_status.attr, + &dev_attr_temp3_input.attr, + NULL +}; + +static const struct attribute_group fscher_group = { + .attrs = fscher_attributes, +}; + /* * Real code */ @@ -342,31 +341,19 @@ static int fscher_detect(struct i2c_adapter *adapter, int address, int kind) fscher_init_client(new_client); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &fscher_group))) + goto exit_detach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove_files; } - device_create_file_revision(new_client); - device_create_file_alarms(new_client); - device_create_file_control(new_client); - device_create_file_watchdog(new_client); - - device_create_file_in(new_client, 0); - device_create_file_in(new_client, 1); - device_create_file_in(new_client, 2); - - device_create_file_fan(new_client, 1); - device_create_file_fan(new_client, 2); - device_create_file_fan(new_client, 3); - - device_create_file_temp(new_client, 1); - device_create_file_temp(new_client, 2); - device_create_file_temp(new_client, 3); - return 0; +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &fscher_group); exit_detach: i2c_detach_client(new_client); exit_free: @@ -381,6 +368,7 @@ static int fscher_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &fscher_group); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/fscpos.c b/drivers/hwmon/fscpos.c index 6dc4846b9ee..ea506a77f9c 100644 --- a/drivers/hwmon/fscpos.c +++ b/drivers/hwmon/fscpos.c @@ -38,6 +38,7 @@ #include <linux/hwmon.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/sysfs.h> /* * Addresses to scan @@ -432,6 +433,44 @@ static DEVICE_ATTR(in0_input, S_IRUGO, show_volt_12, NULL); static DEVICE_ATTR(in1_input, S_IRUGO, show_volt_5, NULL); static DEVICE_ATTR(in2_input, S_IRUGO, show_volt_batt, NULL); +static struct attribute *fscpos_attributes[] = { + &dev_attr_event.attr, + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + &dev_attr_in2_input.attr, + + &dev_attr_wdog_control.attr, + &dev_attr_wdog_preset.attr, + &dev_attr_wdog_state.attr, + + &dev_attr_temp1_input.attr, + &dev_attr_temp1_status.attr, + &dev_attr_temp1_reset.attr, + &dev_attr_temp2_input.attr, + &dev_attr_temp2_status.attr, + &dev_attr_temp2_reset.attr, + &dev_attr_temp3_input.attr, + &dev_attr_temp3_status.attr, + &dev_attr_temp3_reset.attr, + + &dev_attr_fan1_input.attr, + &dev_attr_fan1_status.attr, + &dev_attr_fan1_ripple.attr, + &dev_attr_pwm1.attr, + &dev_attr_fan2_input.attr, + &dev_attr_fan2_status.attr, + &dev_attr_fan2_ripple.attr, + &dev_attr_pwm2.attr, + &dev_attr_fan3_input.attr, + &dev_attr_fan3_status.attr, + &dev_attr_fan3_ripple.attr, + NULL +}; + +static const struct attribute_group fscpos_group = { + .attrs = fscpos_attributes, +}; + static int fscpos_attach_adapter(struct i2c_adapter *adapter) { if (!(adapter->class & I2C_CLASS_HWMON)) @@ -497,42 +536,19 @@ static int fscpos_detect(struct i2c_adapter *adapter, int address, int kind) dev_info(&new_client->dev, "Found fscpos chip, rev %u\n", data->revision); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &fscpos_group))) + goto exit_detach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove_files; } - device_create_file(&new_client->dev, &dev_attr_event); - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_wdog_control); - device_create_file(&new_client->dev, &dev_attr_wdog_preset); - device_create_file(&new_client->dev, &dev_attr_wdog_state); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_status); - device_create_file(&new_client->dev, &dev_attr_temp1_reset); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp2_status); - device_create_file(&new_client->dev, &dev_attr_temp2_reset); - device_create_file(&new_client->dev, &dev_attr_temp3_input); - device_create_file(&new_client->dev, &dev_attr_temp3_status); - device_create_file(&new_client->dev, &dev_attr_temp3_reset); - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_status); - device_create_file(&new_client->dev, &dev_attr_fan1_ripple); - device_create_file(&new_client->dev, &dev_attr_pwm1); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan2_status); - device_create_file(&new_client->dev, &dev_attr_fan2_ripple); - device_create_file(&new_client->dev, &dev_attr_pwm2); - device_create_file(&new_client->dev, &dev_attr_fan3_input); - device_create_file(&new_client->dev, &dev_attr_fan3_status); - device_create_file(&new_client->dev, &dev_attr_fan3_ripple); - return 0; +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &fscpos_group); exit_detach: i2c_detach_client(new_client); exit_free: @@ -547,6 +563,7 @@ static int fscpos_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &fscpos_group); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c index 6606aabdb49..c103640455a 100644 --- a/drivers/hwmon/gl518sm.c +++ b/drivers/hwmon/gl518sm.c @@ -44,6 +44,7 @@ #include <linux/hwmon.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/sysfs.h> /* Addresses to scan */ static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; @@ -340,6 +341,42 @@ static DEVICE_ATTR(beep_enable, S_IWUSR|S_IRUGO, static DEVICE_ATTR(beep_mask, S_IWUSR|S_IRUGO, show_beep_mask, set_beep_mask); +static struct attribute *gl518_attributes[] = { + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + &dev_attr_in2_input.attr, + &dev_attr_in3_input.attr, + &dev_attr_in0_min.attr, + &dev_attr_in1_min.attr, + &dev_attr_in2_min.attr, + &dev_attr_in3_min.attr, + &dev_attr_in0_max.attr, + &dev_attr_in1_max.attr, + &dev_attr_in2_max.attr, + &dev_attr_in3_max.attr, + + &dev_attr_fan1_auto.attr, + &dev_attr_fan1_input.attr, + &dev_attr_fan2_input.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan2_min.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan2_div.attr, + + &dev_attr_temp1_input.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp1_max_hyst.attr, + + &dev_attr_alarms.attr, + &dev_attr_beep_enable.attr, + &dev_attr_beep_mask.attr, + NULL +}; + +static const struct attribute_group gl518_group = { + .attrs = gl518_attributes, +}; + /* * Real code */ @@ -420,43 +457,19 @@ static int gl518_detect(struct i2c_adapter *adapter, int address, int kind) gl518_init_client((struct i2c_client *) new_client); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &gl518_group))) + goto exit_detach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove_files; } - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_fan1_auto); - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_beep_enable); - device_create_file(&new_client->dev, &dev_attr_beep_mask); - return 0; -/* OK, this is not exactly good programming practice, usually. But it is - very code-efficient in this case. */ - +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &gl518_group); exit_detach: i2c_detach_client(new_client); exit_free: @@ -490,6 +503,7 @@ static int gl518_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &gl518_group); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c index 14e810f3c2c..ebe7b9aaa91 100644 --- a/drivers/hwmon/gl520sm.c +++ b/drivers/hwmon/gl520sm.c @@ -30,6 +30,7 @@ #include <linux/hwmon-vid.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/sysfs.h> /* Type of the extra sensor */ static unsigned short extra_sensor_type; @@ -190,55 +191,29 @@ static DEVICE_ATTR(type##item, S_IRUGO, get_##type##0##item, NULL); #define sysfs_vid(n) \ sysfs_ro_n(cpu, n, _vid, GL520_REG_VID_INPUT) -#define device_create_file_vid(client, n) \ -device_create_file(&client->dev, &dev_attr_cpu##n##_vid) - #define sysfs_in(n) \ sysfs_ro_n(in, n, _input, GL520_REG_IN##n##INPUT) \ sysfs_rw_n(in, n, _min, GL520_REG_IN##n##_MIN) \ sysfs_rw_n(in, n, _max, GL520_REG_IN##n##_MAX) \ -#define device_create_file_in(client, n) \ -({device_create_file(&client->dev, &dev_attr_in##n##_input); \ -device_create_file(&client->dev, &dev_attr_in##n##_min); \ -device_create_file(&client->dev, &dev_attr_in##n##_max);}) - #define sysfs_fan(n) \ sysfs_ro_n(fan, n, _input, GL520_REG_FAN_INPUT) \ sysfs_rw_n(fan, n, _min, GL520_REG_FAN_MIN) \ sysfs_rw_n(fan, n, _div, GL520_REG_FAN_DIV) -#define device_create_file_fan(client, n) \ -({device_create_file(&client->dev, &dev_attr_fan##n##_input); \ -device_create_file(&client->dev, &dev_attr_fan##n##_min); \ -device_create_file(&client->dev, &dev_attr_fan##n##_div);}) - #define sysfs_fan_off(n) \ sysfs_rw_n(fan, n, _off, GL520_REG_FAN_OFF) \ -#define device_create_file_fan_off(client, n) \ -device_create_file(&client->dev, &dev_attr_fan##n##_off) - #define sysfs_temp(n) \ sysfs_ro_n(temp, n, _input, GL520_REG_TEMP##n##_INPUT) \ sysfs_rw_n(temp, n, _max, GL520_REG_TEMP##n##_MAX) \ sysfs_rw_n(temp, n, _max_hyst, GL520_REG_TEMP##n##_MAX_HYST) -#define device_create_file_temp(client, n) \ -({device_create_file(&client->dev, &dev_attr_temp##n##_input); \ -device_create_file(&client->dev, &dev_attr_temp##n##_max); \ -device_create_file(&client->dev, &dev_attr_temp##n##_max_hyst);}) - #define sysfs_alarms() \ sysfs_ro(alarms, , GL520_REG_ALARMS) \ sysfs_rw(beep_enable, , GL520_REG_BEEP_ENABLE) \ sysfs_rw(beep_mask, , GL520_REG_BEEP_MASK) -#define device_create_file_alarms(client) \ -({device_create_file(&client->dev, &dev_attr_alarms); \ -device_create_file(&client->dev, &dev_attr_beep_enable); \ -device_create_file(&client->dev, &dev_attr_beep_mask);}) - sysfs_vid(0) @@ -511,6 +486,59 @@ static ssize_t set_beep_mask(struct i2c_client *client, struct gl520_data *data, return count; } +static struct attribute *gl520_attributes[] = { + &dev_attr_cpu0_vid.attr, + + &dev_attr_in0_input.attr, + &dev_attr_in0_min.attr, + &dev_attr_in0_max.attr, + &dev_attr_in1_input.attr, + &dev_attr_in1_min.attr, + &dev_attr_in1_max.attr, + &dev_attr_in2_input.attr, + &dev_attr_in2_min.attr, + &dev_attr_in2_max.attr, + &dev_attr_in3_input.attr, + &dev_attr_in3_min.attr, + &dev_attr_in3_max.attr, + + &dev_attr_fan1_input.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan1_off.attr, + &dev_attr_fan2_input.attr, + &dev_attr_fan2_min.attr, + &dev_attr_fan2_div.attr, + + &dev_attr_temp1_input.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp1_max_hyst.attr, + + &dev_attr_alarms.attr, + &dev_attr_beep_enable.attr, + &dev_attr_beep_mask.attr, + NULL +}; + +static const struct attribute_group gl520_group = { + .attrs = gl520_attributes, +}; + +static struct attribute *gl520_attributes_opt[] = { + &dev_attr_in4_input.attr, + &dev_attr_in4_min.attr, + &dev_attr_in4_max.attr, + + &dev_attr_temp2_input.attr, + &dev_attr_temp2_max.attr, + &dev_attr_temp2_max_hyst.attr, + NULL +}; + +static const struct attribute_group gl520_group_opt = { + .attrs = gl520_attributes_opt, +}; + /* * Real code @@ -572,33 +600,39 @@ static int gl520_detect(struct i2c_adapter *adapter, int address, int kind) gl520_init_client(new_client); /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); + if ((err = sysfs_create_group(&new_client->dev.kobj, &gl520_group))) goto exit_detach; - } - - device_create_file_vid(new_client, 0); - device_create_file_in(new_client, 0); - device_create_file_in(new_client, 1); - device_create_file_in(new_client, 2); - device_create_file_in(new_client, 3); - if (!data->two_temps) - device_create_file_in(new_client, 4); - - device_create_file_fan(new_client, 1); - device_create_file_fan(new_client, 2); - device_create_file_fan_off(new_client, 1); + if (data->two_temps) { + if ((err = device_create_file(&new_client->dev, + &dev_attr_temp2_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp2_max)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp2_max_hyst))) + goto exit_remove_files; + } else { + if ((err = device_create_file(&new_client->dev, + &dev_attr_in4_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in4_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in4_max))) + goto exit_remove_files; + } - device_create_file_temp(new_client, 1); - if (data->two_temps) - device_create_file_temp(new_client, 2); - device_create_file_alarms(new_client); + data->class_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove_files; + } return 0; +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &gl520_group); + sysfs_remove_group(&new_client->dev.kobj, &gl520_group_opt); exit_detach: i2c_detach_client(new_client); exit_free: @@ -652,6 +686,8 @@ static int gl520_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &gl520_group); + sysfs_remove_group(&client->dev.kobj, &gl520_group_opt); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c index 42b632889dd..8e7b5607f5a 100644 --- a/drivers/hwmon/hdaps.c +++ b/drivers/hwmon/hdaps.c @@ -587,7 +587,9 @@ static int __init hdaps_init(void) input_set_abs_params(hdaps_idev, ABS_Y, -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); - input_register_device(hdaps_idev); + ret = input_register_device(hdaps_idev); + if (ret) + goto out_idev; /* start up our timer for the input device */ init_timer(&hdaps_timer); @@ -598,6 +600,8 @@ static int __init hdaps_init(void) printk(KERN_INFO "hdaps: driver successfully loaded.\n"); return 0; +out_idev: + input_free_device(hdaps_idev); out_group: sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); out_device: diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index b0ee5749222..323ef06719c 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -4,10 +4,12 @@ Supports: IT8705F Super I/O chip w/LPC interface IT8712F Super I/O chip w/LPC interface & SMBus + IT8716F Super I/O chip w/LPC interface + IT8718F Super I/O chip w/LPC interface Sis950 A clone of the IT8705F Copyright (C) 2001 Chris Gauthron <chrisg@0-in.com> - Largely inspired by lm78.c of the same package + Copyright (C) 2005-2006 Jean Delvare <khali@linux-fr.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,13 +26,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* - djg@pdp8.net David Gesswein 7/18/01 - Modified to fix bug with not all alarms enabled. - Added ability to read battery voltage and select temperature sensor - type at module load time. -*/ - #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -42,6 +37,7 @@ #include <linux/hwmon-vid.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/sysfs.h> #include <asm/io.h> @@ -50,12 +46,13 @@ static unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END }; static unsigned short isa_address; /* Insmod parameters */ -I2C_CLIENT_INSMOD_2(it87, it8712); +I2C_CLIENT_INSMOD_4(it87, it8712, it8716, it8718); #define REG 0x2e /* The register to read/write */ #define DEV 0x07 /* Register: Logical device select */ #define VAL 0x2f /* The value to read/write */ #define PME 0x04 /* The device with the fan registers in it */ +#define GPIO 0x07 /* The device with the IT8718F VID value in it */ #define DEVID 0x20 /* Register: Device ID */ #define DEVREV 0x22 /* Register: Device Revision */ @@ -77,10 +74,10 @@ static int superio_inw(int reg) } static inline void -superio_select(void) +superio_select(int ldn) { outb(DEV, REG); - outb(PME, VAL); + outb(ldn, VAL); } static inline void @@ -99,20 +96,27 @@ superio_exit(void) outb(0x02, VAL); } +/* Logical device 4 registers */ #define IT8712F_DEVID 0x8712 #define IT8705F_DEVID 0x8705 +#define IT8716F_DEVID 0x8716 +#define IT8718F_DEVID 0x8718 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 +/* Logical device 7 registers (IT8712F and later) */ +#define IT87_SIO_PINX2_REG 0x2c /* Pin selection */ +#define IT87_SIO_VID_REG 0xfc /* VID value */ + /* Update battery voltage after every reading if true */ static int update_vbat; /* Not all BIOSes properly configure the PWM registers */ static int fix_pwm_polarity; -/* Chip Type */ - +/* Values read from Super-I/O config space */ static u16 chip_type; +static u8 vid_value; /* Many IT87 constants specified below */ @@ -131,13 +135,21 @@ static u16 chip_type; #define IT87_REG_ALARM2 0x02 #define IT87_REG_ALARM3 0x03 +/* The IT8718F has the VID value in a different register, in Super-I/O + configuration space. */ #define IT87_REG_VID 0x0a +/* Warning: register 0x0b is used for something completely different in + new chips/revisions. I suspect only 16-bit tachometer mode will work + for these. */ #define IT87_REG_FAN_DIV 0x0b +#define IT87_REG_FAN_16BIT 0x0c /* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */ #define IT87_REG_FAN(nr) (0x0d + (nr)) #define IT87_REG_FAN_MIN(nr) (0x10 + (nr)) +#define IT87_REG_FANX(nr) (0x18 + (nr)) +#define IT87_REG_FANX_MIN(nr) (0x1b + (nr)) #define IT87_REG_FAN_MAIN_CTRL 0x13 #define IT87_REG_FAN_CTL 0x14 #define IT87_REG_PWM(nr) (0x15 + (nr)) @@ -169,7 +181,16 @@ static inline u8 FAN_TO_REG(long rpm, int div) 254); } +static inline u16 FAN16_TO_REG(long rpm) +{ + if (rpm == 0) + return 0xffff; + return SENSORS_LIMIT((1350000 + rpm) / (rpm * 2), 1, 0xfffe); +} + #define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) +/* The divider is fixed to 2 in 16-bit mode */ +#define FAN16_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:1350000/((val)*2)) #define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-500)/1000):\ ((val)+500)/1000),-128,127)) @@ -181,7 +202,7 @@ static inline u8 FAN_TO_REG(long rpm, int div) static int DIV_TO_REG(int val) { int answer = 0; - while ((val >>= 1) != 0) + while (answer < 7 && (val >>= 1)) answer++; return answer; } @@ -203,10 +224,11 @@ struct it87_data { unsigned long last_updated; /* In jiffies */ u8 in[9]; /* Register value */ - u8 in_max[9]; /* Register value */ - u8 in_min[9]; /* Register value */ - u8 fan[3]; /* Register value */ - u8 fan_min[3]; /* Register value */ + u8 in_max[8]; /* Register value */ + u8 in_min[8]; /* Register value */ + u8 has_fan; /* Bitfield, fans enabled */ + u16 fan[3]; /* Register values, possibly combined */ + u16 fan_min[3]; /* Register values, possibly combined */ u8 temp[3]; /* Register value */ u8 temp_high[3]; /* Register value */ u8 temp_low[3]; /* Register value */ @@ -545,15 +567,15 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct it87_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - int i, min[3]; + unsigned long val = simple_strtoul(buf, NULL, 10); + int min; u8 old; mutex_lock(&data->update_lock); old = it87_read_value(client, IT87_REG_FAN_DIV); - for (i = 0; i < 3; i++) - min[i] = FAN_FROM_REG(data->fan_min[i], DIV_FROM_REG(data->fan_div[i])); + /* Save fan min limit */ + min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); switch (nr) { case 0: @@ -573,10 +595,10 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, val |= 0x1 << 6; it87_write_value(client, IT87_REG_FAN_DIV, val); - for (i = 0; i < 3; i++) { - data->fan_min[i]=FAN_TO_REG(min[i], DIV_FROM_REG(data->fan_div[i])); - it87_write_value(client, IT87_REG_FAN_MIN(i), data->fan_min[i]); - } + /* Restore fan min limit */ + data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); + it87_write_value(client, IT87_REG_FAN_MIN(nr), data->fan_min[nr]); + mutex_unlock(&data->update_lock); return count; } @@ -657,6 +679,59 @@ show_pwm_offset(1); show_pwm_offset(2); show_pwm_offset(3); +/* A different set of callbacks for 16-bit fans */ +static ssize_t show_fan16(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%d\n", FAN16_FROM_REG(data->fan[nr])); +} + +static ssize_t show_fan16_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%d\n", FAN16_FROM_REG(data->fan_min[nr])); +} + +static ssize_t set_fan16_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->fan_min[nr] = FAN16_TO_REG(val); + it87_write_value(client, IT87_REG_FAN_MIN(nr), + data->fan_min[nr] & 0xff); + it87_write_value(client, IT87_REG_FANX_MIN(nr), + data->fan_min[nr] >> 8); + mutex_unlock(&data->update_lock); + return count; +} + +/* We want to use the same sysfs file names as 8-bit fans, but we need + different variable names, so we have to use SENSOR_ATTR instead of + SENSOR_DEVICE_ATTR. */ +#define show_fan16_offset(offset) \ +static struct sensor_device_attribute sensor_dev_attr_fan##offset##_input16 \ + = SENSOR_ATTR(fan##offset##_input, S_IRUGO, \ + show_fan16, NULL, offset - 1); \ +static struct sensor_device_attribute sensor_dev_attr_fan##offset##_min16 \ + = SENSOR_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_fan16_min, set_fan16_min, offset - 1) + +show_fan16_offset(1); +show_fan16_offset(2); +show_fan16_offset(3); + /* Alarms */ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) { @@ -684,8 +759,6 @@ store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf return count; } static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); -#define device_create_file_vrm(client) \ -device_create_file(&client->dev, &dev_attr_vrm) static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) @@ -694,8 +767,88 @@ show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); -#define device_create_file_vid(client) \ -device_create_file(&client->dev, &dev_attr_cpu0_vid) + +static struct attribute *it87_attributes[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp1_type.dev_attr.attr, + &sensor_dev_attr_temp2_type.dev_attr.attr, + &sensor_dev_attr_temp3_type.dev_attr.attr, + + &dev_attr_alarms.attr, + NULL +}; + +static const struct attribute_group it87_group = { + .attrs = it87_attributes, +}; + +static struct attribute *it87_attributes_opt[] = { + &sensor_dev_attr_fan1_input16.dev_attr.attr, + &sensor_dev_attr_fan1_min16.dev_attr.attr, + &sensor_dev_attr_fan2_input16.dev_attr.attr, + &sensor_dev_attr_fan2_min16.dev_attr.attr, + &sensor_dev_attr_fan3_input16.dev_attr.attr, + &sensor_dev_attr_fan3_min16.dev_attr.attr, + + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan1_div.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan2_div.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan3_div.dev_attr.attr, + + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + + &dev_attr_vrm.attr, + &dev_attr_cpu0_vid.attr, + NULL +}; + +static const struct attribute_group it87_group_opt = { + .attrs = it87_attributes_opt, +}; /* This function is called when: * it87_driver is inserted (when this module is loaded), for each @@ -721,10 +874,12 @@ static int __init it87_find(unsigned short *address) superio_enter(); chip_type = superio_inw(DEVID); if (chip_type != IT8712F_DEVID + && chip_type != IT8716F_DEVID + && chip_type != IT8718F_DEVID && chip_type != IT8705F_DEVID) goto exit; - superio_select(); + superio_select(PME); if (!(superio_inb(IT87_ACT_REG) & 0x01)) { pr_info("it87: Device not activated, skipping\n"); goto exit; @@ -740,6 +895,21 @@ static int __init it87_find(unsigned short *address) pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n", chip_type, *address, superio_inb(DEVREV) & 0x0f); + /* Read GPIO config and VID value from LDN 7 (GPIO) */ + if (chip_type != IT8705F_DEVID) { + int reg; + + superio_select(GPIO); + if (chip_type == it8718) + vid_value = superio_inb(IT87_SIO_VID_REG); + + reg = superio_inb(IT87_SIO_PINX2_REG); + if (reg & (1 << 0)) + pr_info("it87: in3 is VCC (+5V)\n"); + if (reg & (1 << 1)) + pr_info("it87: in7 is VCCH (+5V Stand-By)\n"); + } + exit: superio_exit(); return err; @@ -800,8 +970,19 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind) i = it87_read_value(new_client, IT87_REG_CHIPID); if (i == 0x90) { kind = it87; - if ((is_isa) && (chip_type == IT8712F_DEVID)) - kind = it8712; + if (is_isa) { + switch (chip_type) { + case IT8712F_DEVID: + kind = it8712; + break; + case IT8716F_DEVID: + kind = it8716; + break; + case IT8718F_DEVID: + kind = it8718; + break; + } + } } else { if (kind == 0) @@ -818,6 +999,10 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind) name = "it87"; } else if (kind == it8712) { name = "it8712"; + } else if (kind == it8716) { + name = "it8716"; + } else if (kind == it8718) { + name = "it8718"; } /* Fill in the remaining client fields and put it into the global list */ @@ -842,76 +1027,103 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind) it87_init_client(new_client, data); /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); + if ((err = sysfs_create_group(&new_client->dev.kobj, &it87_group))) goto ERROR3; + + /* Do not create fan files for disabled fans */ + if (data->type == it8716 || data->type == it8718) { + /* 16-bit tachometers */ + if (data->has_fan & (1 << 0)) { + if ((err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan1_input16.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan1_min16.dev_attr))) + goto ERROR4; + } + if (data->has_fan & (1 << 1)) { + if ((err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan2_input16.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan2_min16.dev_attr))) + goto ERROR4; + } + if (data->has_fan & (1 << 2)) { + if ((err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan3_input16.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan3_min16.dev_attr))) + goto ERROR4; + } + } else { + /* 8-bit tachometers with clock divider */ + if (data->has_fan & (1 << 0)) { + if ((err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan1_input.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan1_min.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan1_div.dev_attr))) + goto ERROR4; + } + if (data->has_fan & (1 << 1)) { + if ((err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan2_input.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan2_min.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan2_div.dev_attr))) + goto ERROR4; + } + if (data->has_fan & (1 << 2)) { + if ((err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan3_input.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan3_min.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan3_div.dev_attr))) + goto ERROR4; + } } - device_create_file(&new_client->dev, &sensor_dev_attr_in0_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in1_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in2_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in3_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in4_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in5_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in6_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in7_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in8_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in0_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in1_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in2_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in3_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in4_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in5_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in6_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in7_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in0_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in1_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in2_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in3_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in4_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in5_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in6_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in7_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_type.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_type.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_type.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan1_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan2_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan3_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan1_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan2_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan3_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan1_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan2_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan3_div.dev_attr); - device_create_file(&new_client->dev, &dev_attr_alarms); if (enable_pwm_interface) { - device_create_file(&new_client->dev, &sensor_dev_attr_pwm1_enable.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_pwm2_enable.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_pwm3_enable.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_pwm1.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_pwm2.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_pwm3.dev_attr); + if ((err = device_create_file(&new_client->dev, + &sensor_dev_attr_pwm1_enable.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_pwm2_enable.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_pwm3_enable.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_pwm1.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_pwm2.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_pwm3.dev_attr))) + goto ERROR4; } - if (data->type == it8712) { + if (data->type == it8712 || data->type == it8716 + || data->type == it8718) { data->vrm = vid_which_vrm(); - device_create_file_vrm(new_client); - device_create_file_vid(new_client); + /* VID reading from Super-I/O config space if available */ + data->vid = vid_value; + if ((err = device_create_file(&new_client->dev, + &dev_attr_vrm)) + || (err = device_create_file(&new_client->dev, + &dev_attr_cpu0_vid))) + goto ERROR4; + } + + data->class_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto ERROR4; } return 0; +ERROR4: + sysfs_remove_group(&new_client->dev.kobj, &it87_group); + sysfs_remove_group(&new_client->dev.kobj, &it87_group_opt); ERROR3: i2c_detach_client(new_client); ERROR2: @@ -929,6 +1141,8 @@ static int it87_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &it87_group); + sysfs_remove_group(&client->dev.kobj, &it87_group_opt); if ((err = i2c_detach_client(client))) return err; @@ -1045,6 +1259,22 @@ static void it87_init_client(struct i2c_client *client, struct it87_data *data) data->manual_pwm_ctl[i] = 0xff; } + /* Some chips seem to have default value 0xff for all limit + * registers. For low voltage limits it makes no sense and triggers + * alarms, so change to 0 instead. For high temperature limits, it + * means -1 degree C, which surprisingly doesn't trigger an alarm, + * but is still confusing, so change to 127 degrees C. */ + for (i = 0; i < 8; i++) { + tmp = it87_read_value(client, IT87_REG_VIN_MIN(i)); + if (tmp == 0xff) + it87_write_value(client, IT87_REG_VIN_MIN(i), 0); + } + for (i = 0; i < 3; i++) { + tmp = it87_read_value(client, IT87_REG_TEMP_HIGH(i)); + if (tmp == 0xff) + it87_write_value(client, IT87_REG_TEMP_HIGH(i), 127); + } + /* Check if temperature channnels are reset manually or by some reason */ tmp = it87_read_value(client, IT87_REG_TEMP_ENABLE); if ((tmp & 0x3f) == 0) { @@ -1068,6 +1298,18 @@ static void it87_init_client(struct i2c_client *client, struct it87_data *data) data->fan_main_ctrl |= 0x70; it87_write_value(client, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); } + data->has_fan = (data->fan_main_ctrl >> 4) & 0x07; + + /* Set tachometers to 16-bit mode if needed */ + if (data->type == it8716 || data->type == it8718) { + tmp = it87_read_value(client, IT87_REG_FAN_16BIT); + if (~tmp & 0x07 & data->has_fan) { + dev_dbg(&client->dev, + "Setting fan1-3 to 16-bit mode\n"); + it87_write_value(client, IT87_REG_FAN_16BIT, + tmp | 0x07); + } + } /* Set current fan mode registers and the default settings for the * other mode registers */ @@ -1118,18 +1360,26 @@ static struct it87_data *it87_update_device(struct device *dev) data->in_max[i] = it87_read_value(client, IT87_REG_VIN_MAX(i)); } + /* in8 (battery) has no limit registers */ data->in[8] = it87_read_value(client, IT87_REG_VIN(8)); - /* Temperature sensor doesn't have limit registers, set - to min and max value */ - data->in_min[8] = 0; - data->in_max[8] = 255; for (i = 0; i < 3; i++) { - data->fan[i] = - it87_read_value(client, IT87_REG_FAN(i)); + /* Skip disabled fans */ + if (!(data->has_fan & (1 << i))) + continue; + data->fan_min[i] = it87_read_value(client, IT87_REG_FAN_MIN(i)); + data->fan[i] = it87_read_value(client, + IT87_REG_FAN(i)); + /* Add high byte if in 16-bit mode */ + if (data->type == it8716 || data->type == it8718) { + data->fan[i] |= it87_read_value(client, + IT87_REG_FANX(i)) << 8; + data->fan_min[i] |= it87_read_value(client, + IT87_REG_FANX_MIN(i)) << 8; + } } for (i = 0; i < 3; i++) { data->temp[i] = @@ -1140,10 +1390,14 @@ static struct it87_data *it87_update_device(struct device *dev) it87_read_value(client, IT87_REG_TEMP_LOW(i)); } - i = it87_read_value(client, IT87_REG_FAN_DIV); - data->fan_div[0] = i & 0x07; - data->fan_div[1] = (i >> 3) & 0x07; - data->fan_div[2] = (i & 0x40) ? 3 : 1; + /* Newer chips don't have clock dividers */ + if ((data->has_fan & 0x07) && data->type != it8716 + && data->type != it8718) { + i = it87_read_value(client, IT87_REG_FAN_DIV); + data->fan_div[0] = i & 0x07; + data->fan_div[1] = (i >> 3) & 0x07; + data->fan_div[2] = (i & 0x40) ? 3 : 1; + } data->alarms = it87_read_value(client, IT87_REG_ALARM1) | @@ -1153,9 +1407,11 @@ static struct it87_data *it87_update_device(struct device *dev) data->sensor = it87_read_value(client, IT87_REG_TEMP_ENABLE); /* The 8705 does not have VID capability */ - if (data->type == it8712) { + if (data->type == it8712 || data->type == it8716) { data->vid = it87_read_value(client, IT87_REG_VID); - data->vid &= 0x1f; + /* The older IT8712F revisions had only 5 VID pins, + but we assume it is always safe to read 6 bits. */ + data->vid &= 0x3f; } data->last_updated = jiffies; data->valid = 1; @@ -1193,8 +1449,9 @@ static void __exit sm_it87_exit(void) } -MODULE_AUTHOR("Chris Gauthron <chrisg@0-in.com>"); -MODULE_DESCRIPTION("IT8705F, IT8712F, Sis950 driver"); +MODULE_AUTHOR("Chris Gauthron <chrisg@0-in.com>, " + "Jean Delvare <khali@linux-fr.org>"); +MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F, SiS950 driver"); module_param(update_vbat, bool, 0); MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); module_param(fix_pwm_polarity, bool, 0); diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c new file mode 100644 index 00000000000..f58b64ed09e --- /dev/null +++ b/drivers/hwmon/k8temp.c @@ -0,0 +1,294 @@ +/* + * k8temp.c - Linux kernel module for hardware monitoring + * + * Copyright (C) 2006 Rudolf Marek <r.marek@sh.cvut.cz> + * + * Inspired from the w83785 and amd756 drivers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/pci.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> + +#define TEMP_FROM_REG(val) (((((val) >> 16) & 0xff) - 49) * 1000) +#define REG_TEMP 0xe4 +#define SEL_PLACE 0x40 +#define SEL_CORE 0x04 + +struct k8temp_data { + struct class_device *class_dev; + struct mutex update_lock; + const char *name; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* registers values */ + u8 sensorsp; /* sensor presence bits - SEL_CORE & SEL_PLACE */ + u32 temp[2][2]; /* core, place */ +}; + +static struct k8temp_data *k8temp_update_device(struct device *dev) +{ + struct k8temp_data *data = dev_get_drvdata(dev); + struct pci_dev *pdev = to_pci_dev(dev); + u8 tmp; + + mutex_lock(&data->update_lock); + + if (!data->valid + || time_after(jiffies, data->last_updated + HZ)) { + pci_read_config_byte(pdev, REG_TEMP, &tmp); + tmp &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ + pci_write_config_byte(pdev, REG_TEMP, tmp); + pci_read_config_dword(pdev, REG_TEMP, &data->temp[0][0]); + + if (data->sensorsp & SEL_PLACE) { + tmp |= SEL_PLACE; /* Select sensor 1, core0 */ + pci_write_config_byte(pdev, REG_TEMP, tmp); + pci_read_config_dword(pdev, REG_TEMP, + &data->temp[0][1]); + } + + if (data->sensorsp & SEL_CORE) { + tmp &= ~SEL_PLACE; /* Select sensor 0, core1 */ + tmp |= SEL_CORE; + pci_write_config_byte(pdev, REG_TEMP, tmp); + pci_read_config_dword(pdev, REG_TEMP, + &data->temp[1][0]); + + if (data->sensorsp & SEL_PLACE) { + tmp |= SEL_PLACE; /* Select sensor 1, core1 */ + pci_write_config_byte(pdev, REG_TEMP, tmp); + pci_read_config_dword(pdev, REG_TEMP, + &data->temp[1][1]); + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + return data; +} + +/* + * Sysfs stuff + */ + +static ssize_t show_name(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct k8temp_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + + +static ssize_t show_temp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute_2 *attr = + to_sensor_dev_attr_2(devattr); + int core = attr->nr; + int place = attr->index; + struct k8temp_data *data = k8temp_update_device(dev); + + return sprintf(buf, "%d\n", + TEMP_FROM_REG(data->temp[core][place])); +} + +/* core, place */ + +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1); +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 1, 0); +static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 1, 1); +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static struct pci_device_id k8temp_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) }, + { 0 }, +}; + +MODULE_DEVICE_TABLE(pci, k8temp_ids); + +static int __devinit k8temp_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int err; + u8 scfg; + u32 temp; + struct k8temp_data *data; + u32 cpuid = cpuid_eax(1); + + /* this feature should be available since SH-C0 core */ + if ((cpuid == 0xf40) || (cpuid == 0xf50) || (cpuid == 0xf51)) { + err = -ENODEV; + goto exit; + } + + if (!(data = kzalloc(sizeof(struct k8temp_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + pci_read_config_byte(pdev, REG_TEMP, &scfg); + scfg &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ + pci_write_config_byte(pdev, REG_TEMP, scfg); + pci_read_config_byte(pdev, REG_TEMP, &scfg); + + if (scfg & (SEL_PLACE | SEL_CORE)) { + dev_err(&pdev->dev, "Configuration bit(s) stuck at 1!\n"); + err = -ENODEV; + goto exit_free; + } + + scfg |= (SEL_PLACE | SEL_CORE); + pci_write_config_byte(pdev, REG_TEMP, scfg); + + /* now we know if we can change core and/or sensor */ + pci_read_config_byte(pdev, REG_TEMP, &data->sensorsp); + + if (data->sensorsp & SEL_PLACE) { + scfg &= ~SEL_CORE; /* Select sensor 1, core0 */ + pci_write_config_byte(pdev, REG_TEMP, scfg); + pci_read_config_dword(pdev, REG_TEMP, &temp); + scfg |= SEL_CORE; /* prepare for next selection */ + if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is not likely */ + data->sensorsp &= ~SEL_PLACE; + } + + if (data->sensorsp & SEL_CORE) { + scfg &= ~SEL_PLACE; /* Select sensor 0, core1 */ + pci_write_config_byte(pdev, REG_TEMP, scfg); + pci_read_config_dword(pdev, REG_TEMP, &temp); + if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is not likely */ + data->sensorsp &= ~SEL_CORE; + } + + data->name = "k8temp"; + mutex_init(&data->update_lock); + dev_set_drvdata(&pdev->dev, data); + + /* Register sysfs hooks */ + err = device_create_file(&pdev->dev, + &sensor_dev_attr_temp1_input.dev_attr); + if (err) + goto exit_remove; + + /* sensor can be changed and reports something */ + if (data->sensorsp & SEL_PLACE) { + err = device_create_file(&pdev->dev, + &sensor_dev_attr_temp2_input.dev_attr); + if (err) + goto exit_remove; + } + + /* core can be changed and reports something */ + if (data->sensorsp & SEL_CORE) { + err = device_create_file(&pdev->dev, + &sensor_dev_attr_temp3_input.dev_attr); + if (err) + goto exit_remove; + if (data->sensorsp & SEL_PLACE) + err = device_create_file(&pdev->dev, + &sensor_dev_attr_temp4_input. + dev_attr); + if (err) + goto exit_remove; + } + + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + goto exit_remove; + + data->class_dev = hwmon_device_register(&pdev->dev); + + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + device_remove_file(&pdev->dev, + &sensor_dev_attr_temp1_input.dev_attr); + device_remove_file(&pdev->dev, + &sensor_dev_attr_temp2_input.dev_attr); + device_remove_file(&pdev->dev, + &sensor_dev_attr_temp3_input.dev_attr); + device_remove_file(&pdev->dev, + &sensor_dev_attr_temp4_input.dev_attr); + device_remove_file(&pdev->dev, &dev_attr_name); +exit_free: + dev_set_drvdata(&pdev->dev, NULL); + kfree(data); +exit: + return err; +} + +static void __devexit k8temp_remove(struct pci_dev *pdev) +{ + struct k8temp_data *data = dev_get_drvdata(&pdev->dev); + + hwmon_device_unregister(data->class_dev); + device_remove_file(&pdev->dev, + &sensor_dev_attr_temp1_input.dev_attr); + device_remove_file(&pdev->dev, + &sensor_dev_attr_temp2_input.dev_attr); + device_remove_file(&pdev->dev, + &sensor_dev_attr_temp3_input.dev_attr); + device_remove_file(&pdev->dev, + &sensor_dev_attr_temp4_input.dev_attr); + device_remove_file(&pdev->dev, &dev_attr_name); + dev_set_drvdata(&pdev->dev, NULL); + kfree(data); +} + +static struct pci_driver k8temp_driver = { + .name = "k8temp", + .id_table = k8temp_ids, + .probe = k8temp_probe, + .remove = __devexit_p(k8temp_remove), +}; + +static int __init k8temp_init(void) +{ + return pci_register_driver(&k8temp_driver); +} + +static void __exit k8temp_exit(void) +{ + pci_unregister_driver(&k8temp_driver); +} + +MODULE_AUTHOR("Rudolf Marek <r.marek@sh.cvut.cz>"); +MODULE_DESCRIPTION("AMD K8 core temperature monitor"); +MODULE_LICENSE("GPL"); + +module_init(k8temp_init) +module_exit(k8temp_exit) diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 071f0fc6ade..d69f3cf0712 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -1,7 +1,7 @@ /* * lm63.c - driver for the National Semiconductor LM63 temperature sensor * with integrated fan control - * Copyright (C) 2004-2005 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2004-2006 Jean Delvare <khali@linux-fr.org> * Based on the lm90 driver. * * The LM63 is a sensor chip made by National Semiconductor. It measures @@ -46,6 +46,7 @@ #include <linux/hwmon.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/sysfs.h> /* * Addresses to scan @@ -330,6 +331,16 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, return sprintf(buf, "%u\n", data->alarms); } +static ssize_t show_alarm(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm63_data *data = lm63_update_device(dev); + int bitnr = attr->index; + + return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); +} + static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, set_fan, 1); @@ -350,8 +361,52 @@ static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp8, NULL, 2); static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst, set_temp2_crit_hyst); +/* Individual alarm files */ +static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6); +/* Raw alarm file for compatibility */ static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static struct attribute *lm63_attributes[] = { + &dev_attr_pwm1.attr, + &dev_attr_pwm1_enable.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &dev_attr_temp2_crit_hyst.attr, + + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_input_fault.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &dev_attr_alarms.attr, + NULL +}; + +static const struct attribute_group lm63_group = { + .attrs = lm63_attributes, +}; + +static struct attribute *lm63_attributes_fan1[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + + &sensor_dev_attr_fan1_min_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm63_group_fan1 = { + .attrs = lm63_attributes_fan1, +}; + /* * Real code */ @@ -438,37 +493,26 @@ static int lm63_detect(struct i2c_adapter *adapter, int address, int kind) lm63_init_client(new_client); /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); + if ((err = sysfs_create_group(&new_client->dev.kobj, + &lm63_group))) goto exit_detach; + if (data->config & 0x04) { /* tachometer enabled */ + if ((err = sysfs_create_group(&new_client->dev.kobj, + &lm63_group_fan1))) + goto exit_remove_files; } - if (data->config & 0x04) { /* tachometer enabled */ - device_create_file(&new_client->dev, - &sensor_dev_attr_fan1_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_fan1_min.dev_attr); + data->class_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove_files; } - device_create_file(&new_client->dev, &dev_attr_pwm1); - device_create_file(&new_client->dev, &dev_attr_pwm1_enable); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_min.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_crit.dev_attr); - device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst); - device_create_file(&new_client->dev, &dev_attr_alarms); return 0; +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &lm63_group); + sysfs_remove_group(&new_client->dev.kobj, &lm63_group_fan1); exit_detach: i2c_detach_client(new_client); exit_free: @@ -518,6 +562,8 @@ static int lm63_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &lm63_group); + sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index fc25b90ec24..7c65b8bb6d7 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -112,6 +112,18 @@ static int lm75_attach_adapter(struct i2c_adapter *adapter) return i2c_probe(adapter, &addr_data, lm75_detect); } +static struct attribute *lm75_attributes[] = { + &dev_attr_temp1_input.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp1_max_hyst.attr, + + NULL +}; + +static const struct attribute_group lm75_group = { + .attrs = lm75_attributes, +}; + /* This function is called by i2c_probe */ static int lm75_detect(struct i2c_adapter *adapter, int address, int kind) { @@ -199,18 +211,19 @@ static int lm75_detect(struct i2c_adapter *adapter, int address, int kind) lm75_init_client(new_client); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &lm75_group))) + goto exit_detach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove; } - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - return 0; +exit_remove: + sysfs_remove_group(&new_client->dev.kobj, &lm75_group); exit_detach: i2c_detach_client(new_client); exit_free: @@ -223,6 +236,7 @@ static int lm75_detach_client(struct i2c_client *client) { struct lm75_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &lm75_group); i2c_detach_client(client); kfree(data); return 0; diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c index 459cc977380..dd969f1e841 100644 --- a/drivers/hwmon/lm77.c +++ b/drivers/hwmon/lm77.c @@ -212,6 +212,23 @@ static int lm77_attach_adapter(struct i2c_adapter *adapter) return i2c_probe(adapter, &addr_data, lm77_detect); } +static struct attribute *lm77_attributes[] = { + &dev_attr_temp1_input.attr, + &dev_attr_temp1_crit.attr, + &dev_attr_temp1_min.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp1_crit_hyst.attr, + &dev_attr_temp1_min_hyst.attr, + &dev_attr_temp1_max_hyst.attr, + &dev_attr_alarms.attr, + + NULL +}; + +static const struct attribute_group lm77_group = { + .attrs = lm77_attributes, +}; + /* This function is called by i2c_probe */ static int lm77_detect(struct i2c_adapter *adapter, int address, int kind) { @@ -317,22 +334,19 @@ static int lm77_detect(struct i2c_adapter *adapter, int address, int kind) lm77_init_client(new_client); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &lm77_group))) + goto exit_detach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove; } - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_crit); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst); - device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_alarms); return 0; +exit_remove: + sysfs_remove_group(&new_client->dev.kobj, &lm77_group); exit_detach: i2c_detach_client(new_client); exit_free: @@ -345,6 +359,7 @@ static int lm77_detach_client(struct i2c_client *client) { struct lm77_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &lm77_group); i2c_detach_client(client); kfree(data); return 0; diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index fa1715b9a99..ac1b746df6d 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -482,6 +482,50 @@ static int lm78_isa_attach_adapter(struct i2c_adapter *adapter) return lm78_detect(adapter, isa_address, -1); } +static struct attribute *lm78_attributes[] = { + &dev_attr_in0_input.attr, + &dev_attr_in0_min.attr, + &dev_attr_in0_max.attr, + &dev_attr_in1_input.attr, + &dev_attr_in1_min.attr, + &dev_attr_in1_max.attr, + &dev_attr_in2_input.attr, + &dev_attr_in2_min.attr, + &dev_attr_in2_max.attr, + &dev_attr_in3_input.attr, + &dev_attr_in3_min.attr, + &dev_attr_in3_max.attr, + &dev_attr_in4_input.attr, + &dev_attr_in4_min.attr, + &dev_attr_in4_max.attr, + &dev_attr_in5_input.attr, + &dev_attr_in5_min.attr, + &dev_attr_in5_max.attr, + &dev_attr_in6_input.attr, + &dev_attr_in6_min.attr, + &dev_attr_in6_max.attr, + &dev_attr_temp1_input.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp1_max_hyst.attr, + &dev_attr_fan1_input.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan2_input.attr, + &dev_attr_fan2_min.attr, + &dev_attr_fan2_div.attr, + &dev_attr_fan3_input.attr, + &dev_attr_fan3_min.attr, + &dev_attr_fan3_div.attr, + &dev_attr_alarms.attr, + &dev_attr_cpu0_vid.attr, + + NULL +}; + +static const struct attribute_group lm78_group = { + .attrs = lm78_attributes, +}; + /* This function is called by i2c_probe */ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) { @@ -616,50 +660,19 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) } /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &lm78_group))) + goto ERROR3; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto ERROR3; + goto ERROR4; } - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in4_max); - device_create_file(&new_client->dev, &dev_attr_in5_input); - device_create_file(&new_client->dev, &dev_attr_in5_min); - device_create_file(&new_client->dev, &dev_attr_in5_max); - device_create_file(&new_client->dev, &dev_attr_in6_input); - device_create_file(&new_client->dev, &dev_attr_in6_min); - device_create_file(&new_client->dev, &dev_attr_in6_max); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - device_create_file(&new_client->dev, &dev_attr_fan3_input); - device_create_file(&new_client->dev, &dev_attr_fan3_min); - device_create_file(&new_client->dev, &dev_attr_fan3_div); - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - return 0; +ERROR4: + sysfs_remove_group(&new_client->dev.kobj, &lm78_group); ERROR3: i2c_detach_client(new_client); ERROR2: @@ -677,6 +690,7 @@ static int lm78_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &lm78_group); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c index b4ccdfc0120..064516d824a 100644 --- a/drivers/hwmon/lm80.c +++ b/drivers/hwmon/lm80.c @@ -394,6 +394,48 @@ static int lm80_attach_adapter(struct i2c_adapter *adapter) return i2c_probe(adapter, &addr_data, lm80_detect); } +static struct attribute *lm80_attributes[] = { + &dev_attr_in0_min.attr, + &dev_attr_in1_min.attr, + &dev_attr_in2_min.attr, + &dev_attr_in3_min.attr, + &dev_attr_in4_min.attr, + &dev_attr_in5_min.attr, + &dev_attr_in6_min.attr, + &dev_attr_in0_max.attr, + &dev_attr_in1_max.attr, + &dev_attr_in2_max.attr, + &dev_attr_in3_max.attr, + &dev_attr_in4_max.attr, + &dev_attr_in5_max.attr, + &dev_attr_in6_max.attr, + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + &dev_attr_in2_input.attr, + &dev_attr_in3_input.attr, + &dev_attr_in4_input.attr, + &dev_attr_in5_input.attr, + &dev_attr_in6_input.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan2_min.attr, + &dev_attr_fan1_input.attr, + &dev_attr_fan2_input.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan2_div.attr, + &dev_attr_temp1_input.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp1_max_hyst.attr, + &dev_attr_temp1_crit.attr, + &dev_attr_temp1_crit_hyst.attr, + &dev_attr_alarms.attr, + + NULL +}; + +static const struct attribute_group lm80_group = { + .attrs = lm80_attributes, +}; + static int lm80_detect(struct i2c_adapter *adapter, int address, int kind) { int i, cur; @@ -452,48 +494,19 @@ static int lm80_detect(struct i2c_adapter *adapter, int address, int kind) data->fan_min[1] = lm80_read_value(new_client, LM80_REG_FAN_MIN(2)); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &lm80_group))) + goto error_detach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto error_detach; + goto error_remove; } - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in5_min); - device_create_file(&new_client->dev, &dev_attr_in6_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_in4_max); - device_create_file(&new_client->dev, &dev_attr_in5_max); - device_create_file(&new_client->dev, &dev_attr_in6_max); - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in5_input); - device_create_file(&new_client->dev, &dev_attr_in6_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_temp1_crit); - device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst); - device_create_file(&new_client->dev, &dev_attr_alarms); - return 0; +error_remove: + sysfs_remove_group(&new_client->dev.kobj, &lm80_group); error_detach: i2c_detach_client(new_client); error_free: @@ -508,7 +521,7 @@ static int lm80_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); - + sysfs_remove_group(&client->dev.kobj, &lm80_group); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c index 2137d7879df..feb87b41e98 100644 --- a/drivers/hwmon/lm83.c +++ b/drivers/hwmon/lm83.c @@ -1,7 +1,7 @@ /* * lm83.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2005 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2003-2006 Jean Delvare <khali@linux-fr.org> * * Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is * a sensor chip made by National Semiconductor. It reports up to four @@ -40,6 +40,7 @@ #include <linux/hwmon.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/sysfs.h> /* * Addresses to scan @@ -191,6 +192,16 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, return sprintf(buf, "%d\n", data->alarms); } +static ssize_t show_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm83_data *data = lm83_update_device(dev); + int bitnr = attr->index; + + return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); +} + static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); @@ -208,8 +219,64 @@ static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp, NULL, 8); static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp, set_temp, 8); static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp, NULL, 8); + +/* Individual alarm files */ +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input_fault, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 8); +static SENSOR_DEVICE_ATTR(temp4_crit_alarm, S_IRUGO, show_alarm, NULL, 9); +static SENSOR_DEVICE_ATTR(temp4_input_fault, S_IRUGO, show_alarm, NULL, 10); +static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 12); +static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 13); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 15); +/* Raw alarm file for compatibility */ static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static struct attribute *lm83_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_input_fault.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &dev_attr_alarms.attr, + NULL +}; + +static const struct attribute_group lm83_group = { + .attrs = lm83_attributes, +}; + +static struct attribute *lm83_attributes_opt[] = { + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_input_fault.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_input_fault.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm83_group_opt = { + .attrs = lm83_attributes_opt, +}; + /* * Real code */ @@ -318,59 +385,32 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind) goto exit_free; /* - * Initialize the LM83 chip - * (Nothing to do for this one.) - */ - - /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); - goto exit_detach; - } - - /* + * Register sysfs hooks * The LM82 can only monitor one external diode which is * at the same register as the LM83 temp3 entry - so we * declare 1 and 3 common, and then 2 and 4 only for the LM83. */ - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_input.dev_attr); - - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_max.dev_attr); - - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_crit.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_crit.dev_attr); - - device_create_file(&new_client->dev, &dev_attr_alarms); + if ((err = sysfs_create_group(&new_client->dev.kobj, &lm83_group))) + goto exit_detach; if (kind == lm83) { - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp4_input.dev_attr); - - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp4_max.dev_attr); - - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_crit.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp4_crit.dev_attr); + if ((err = sysfs_create_group(&new_client->dev.kobj, + &lm83_group_opt))) + goto exit_remove_files; + } + + data->class_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove_files; } return 0; +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &lm83_group); + sysfs_remove_group(&new_client->dev.kobj, &lm83_group_opt); exit_detach: i2c_detach_client(new_client); exit_free: @@ -385,6 +425,8 @@ static int lm83_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &lm83_group); + sysfs_remove_group(&client->dev.kobj, &lm83_group_opt); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 342e9663119..2c3293cf69d 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -1025,6 +1025,89 @@ static int lm85_attach_adapter(struct i2c_adapter *adapter) return i2c_probe(adapter, &addr_data, lm85_detect); } +static struct attribute *lm85_attributes[] = { + &dev_attr_fan1_input.attr, + &dev_attr_fan2_input.attr, + &dev_attr_fan3_input.attr, + &dev_attr_fan4_input.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan2_min.attr, + &dev_attr_fan3_min.attr, + &dev_attr_fan4_min.attr, + &dev_attr_pwm1.attr, + &dev_attr_pwm2.attr, + &dev_attr_pwm3.attr, + &dev_attr_pwm1_enable.attr, + &dev_attr_pwm2_enable.attr, + &dev_attr_pwm3_enable.attr, + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + &dev_attr_in2_input.attr, + &dev_attr_in3_input.attr, + &dev_attr_in0_min.attr, + &dev_attr_in1_min.attr, + &dev_attr_in2_min.attr, + &dev_attr_in3_min.attr, + &dev_attr_in0_max.attr, + &dev_attr_in1_max.attr, + &dev_attr_in2_max.attr, + &dev_attr_in3_max.attr, + &dev_attr_temp1_input.attr, + &dev_attr_temp2_input.attr, + &dev_attr_temp3_input.attr, + &dev_attr_temp1_min.attr, + &dev_attr_temp2_min.attr, + &dev_attr_temp3_min.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp2_max.attr, + &dev_attr_temp3_max.attr, + &dev_attr_vrm.attr, + &dev_attr_cpu0_vid.attr, + &dev_attr_alarms.attr, + &dev_attr_pwm1_auto_channels.attr, + &dev_attr_pwm2_auto_channels.attr, + &dev_attr_pwm3_auto_channels.attr, + &dev_attr_pwm1_auto_pwm_min.attr, + &dev_attr_pwm2_auto_pwm_min.attr, + &dev_attr_pwm3_auto_pwm_min.attr, + &dev_attr_pwm1_auto_pwm_minctl.attr, + &dev_attr_pwm2_auto_pwm_minctl.attr, + &dev_attr_pwm3_auto_pwm_minctl.attr, + &dev_attr_pwm1_auto_pwm_freq.attr, + &dev_attr_pwm2_auto_pwm_freq.attr, + &dev_attr_pwm3_auto_pwm_freq.attr, + &dev_attr_temp1_auto_temp_off.attr, + &dev_attr_temp2_auto_temp_off.attr, + &dev_attr_temp3_auto_temp_off.attr, + &dev_attr_temp1_auto_temp_min.attr, + &dev_attr_temp2_auto_temp_min.attr, + &dev_attr_temp3_auto_temp_min.attr, + &dev_attr_temp1_auto_temp_max.attr, + &dev_attr_temp2_auto_temp_max.attr, + &dev_attr_temp3_auto_temp_max.attr, + &dev_attr_temp1_auto_temp_crit.attr, + &dev_attr_temp2_auto_temp_crit.attr, + &dev_attr_temp3_auto_temp_crit.attr, + + NULL +}; + +static const struct attribute_group lm85_group = { + .attrs = lm85_attributes, +}; + +static struct attribute *lm85_attributes_opt[] = { + &dev_attr_in4_input.attr, + &dev_attr_in4_min.attr, + &dev_attr_in4_max.attr, + + NULL +}; + +static const struct attribute_group lm85_group_opt = { + .attrs = lm85_attributes_opt, +}; + static int lm85_detect(struct i2c_adapter *adapter, int address, int kind) { @@ -1163,87 +1246,33 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, lm85_init_client(new_client); /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); + if ((err = sysfs_create_group(&new_client->dev.kobj, &lm85_group))) goto ERROR2; - } - - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan3_input); - device_create_file(&new_client->dev, &dev_attr_fan4_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan3_min); - device_create_file(&new_client->dev, &dev_attr_fan4_min); - device_create_file(&new_client->dev, &dev_attr_pwm1); - device_create_file(&new_client->dev, &dev_attr_pwm2); - device_create_file(&new_client->dev, &dev_attr_pwm3); - device_create_file(&new_client->dev, &dev_attr_pwm1_enable); - device_create_file(&new_client->dev, &dev_attr_pwm2_enable); - device_create_file(&new_client->dev, &dev_attr_pwm3_enable); - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp3_input); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp2_min); - device_create_file(&new_client->dev, &dev_attr_temp3_min); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_temp3_max); - device_create_file(&new_client->dev, &dev_attr_vrm); - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_pwm1_auto_channels); - device_create_file(&new_client->dev, &dev_attr_pwm2_auto_channels); - device_create_file(&new_client->dev, &dev_attr_pwm3_auto_channels); - device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_min); - device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_min); - device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_min); - device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_minctl); - device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_minctl); - device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_minctl); - device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_freq); - device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_freq); - device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_freq); - device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_off); - device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_off); - device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_off); - device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_min); - device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_min); - device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_min); - device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_max); - device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_max); - device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_max); - device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_crit); - device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_crit); - device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_crit); /* The ADT7463 has an optional VRM 10 mode where pin 21 is used as a sixth digital VID input rather than an analog input. */ data->vid = lm85_read_value(new_client, LM85_REG_VID); - if (!(kind == adt7463 && (data->vid & 0x80))) { - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in4_max); + if (!(kind == adt7463 && (data->vid & 0x80))) + if ((err = device_create_file(&new_client->dev, + &dev_attr_in4_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in4_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in4_max))) + goto ERROR3; + + data->class_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto ERROR3; } return 0; /* Error out and cleanup code */ + ERROR3: + sysfs_remove_group(&new_client->dev.kobj, &lm85_group); + sysfs_remove_group(&new_client->dev.kobj, &lm85_group_opt); ERROR2: i2c_detach_client(new_client); ERROR1: @@ -1256,6 +1285,8 @@ static int lm85_detach_client(struct i2c_client *client) { struct lm85_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &lm85_group); + sysfs_remove_group(&client->dev.kobj, &lm85_group_opt); i2c_detach_client(client); kfree(data); return 0; diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index e6c1b638c97..3ce825489e3 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -542,6 +542,78 @@ static int lm87_attach_adapter(struct i2c_adapter *adapter) return i2c_probe(adapter, &addr_data, lm87_detect); } +static struct attribute *lm87_attributes[] = { + &dev_attr_in1_input.attr, + &dev_attr_in1_min.attr, + &dev_attr_in1_max.attr, + &dev_attr_in2_input.attr, + &dev_attr_in2_min.attr, + &dev_attr_in2_max.attr, + &dev_attr_in3_input.attr, + &dev_attr_in3_min.attr, + &dev_attr_in3_max.attr, + &dev_attr_in4_input.attr, + &dev_attr_in4_min.attr, + &dev_attr_in4_max.attr, + + &dev_attr_temp1_input.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp1_min.attr, + &dev_attr_temp1_crit.attr, + &dev_attr_temp2_input.attr, + &dev_attr_temp2_max.attr, + &dev_attr_temp2_min.attr, + &dev_attr_temp2_crit.attr, + + &dev_attr_alarms.attr, + &dev_attr_aout_output.attr, + + NULL +}; + +static const struct attribute_group lm87_group = { + .attrs = lm87_attributes, +}; + +static struct attribute *lm87_attributes_opt[] = { + &dev_attr_in6_input.attr, + &dev_attr_in6_min.attr, + &dev_attr_in6_max.attr, + + &dev_attr_fan1_input.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan1_div.attr, + + &dev_attr_in7_input.attr, + &dev_attr_in7_min.attr, + &dev_attr_in7_max.attr, + + &dev_attr_fan2_input.attr, + &dev_attr_fan2_min.attr, + &dev_attr_fan2_div.attr, + + &dev_attr_temp3_input.attr, + &dev_attr_temp3_max.attr, + &dev_attr_temp3_min.attr, + &dev_attr_temp3_crit.attr, + + &dev_attr_in0_input.attr, + &dev_attr_in0_min.attr, + &dev_attr_in0_max.attr, + &dev_attr_in5_input.attr, + &dev_attr_in5_min.attr, + &dev_attr_in5_max.attr, + + &dev_attr_cpu0_vid.attr, + &dev_attr_vrm.attr, + + NULL +}; + +static const struct attribute_group lm87_group_opt = { + .attrs = lm87_attributes_opt, +}; + /* * The following function does more than just detection. If detection * succeeds, it also registers the new chip. @@ -609,77 +681,90 @@ static int lm87_detect(struct i2c_adapter *adapter, int address, int kind) data->in_scale[7] = 1875; /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); + if ((err = sysfs_create_group(&new_client->dev.kobj, &lm87_group))) goto exit_detach; - } - - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in4_max); if (data->channel & CHAN_NO_FAN(0)) { - device_create_file(&new_client->dev, &dev_attr_in6_input); - device_create_file(&new_client->dev, &dev_attr_in6_min); - device_create_file(&new_client->dev, &dev_attr_in6_max); + if ((err = device_create_file(&new_client->dev, + &dev_attr_in6_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in6_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in6_max))) + goto exit_remove; } else { - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan1_div); + if ((err = device_create_file(&new_client->dev, + &dev_attr_fan1_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan1_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan1_div))) + goto exit_remove; } + if (data->channel & CHAN_NO_FAN(1)) { - device_create_file(&new_client->dev, &dev_attr_in7_input); - device_create_file(&new_client->dev, &dev_attr_in7_min); - device_create_file(&new_client->dev, &dev_attr_in7_max); + if ((err = device_create_file(&new_client->dev, + &dev_attr_in7_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in7_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in7_max))) + goto exit_remove; } else { - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan2_div); + if ((err = device_create_file(&new_client->dev, + &dev_attr_fan2_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan2_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan2_div))) + goto exit_remove; } - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp1_crit); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_temp2_min); - device_create_file(&new_client->dev, &dev_attr_temp2_crit); - if (data->channel & CHAN_TEMP3) { - device_create_file(&new_client->dev, &dev_attr_temp3_input); - device_create_file(&new_client->dev, &dev_attr_temp3_max); - device_create_file(&new_client->dev, &dev_attr_temp3_min); - device_create_file(&new_client->dev, &dev_attr_temp3_crit); + if ((err = device_create_file(&new_client->dev, + &dev_attr_temp3_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp3_max)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp3_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp3_crit))) + goto exit_remove; } else { - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in5_input); - device_create_file(&new_client->dev, &dev_attr_in5_min); - device_create_file(&new_client->dev, &dev_attr_in5_max); + if ((err = device_create_file(&new_client->dev, + &dev_attr_in0_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in0_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in0_max)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in5_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in5_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in5_max))) + goto exit_remove; } if (!(data->channel & CHAN_NO_VID)) { - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - device_create_file(&new_client->dev, &dev_attr_vrm); + if ((err = device_create_file(&new_client->dev, + &dev_attr_cpu0_vid)) + || (err = device_create_file(&new_client->dev, + &dev_attr_vrm))) + goto exit_remove; } - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_aout_output); + data->class_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove; + } return 0; +exit_remove: + sysfs_remove_group(&new_client->dev.kobj, &lm87_group); + sysfs_remove_group(&new_client->dev.kobj, &lm87_group_opt); exit_detach: i2c_detach_client(new_client); exit_free: @@ -732,6 +817,8 @@ static int lm87_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &lm87_group); + sysfs_remove_group(&client->dev.kobj, &lm87_group_opt); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index d9eeaf7585b..6882ce75fee 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1,7 +1,7 @@ /* * lm90.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2005 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2003-2006 Jean Delvare <khali@linux-fr.org> * * Based on the lm83 driver. The LM90 is a sensor chip made by National * Semiconductor. It reports up to two temperatures (its own plus up to @@ -79,6 +79,7 @@ #include <linux/hwmon.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/sysfs.h> /* * Addresses to scan @@ -327,6 +328,16 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, return sprintf(buf, "%d\n", data->alarms); } +static ssize_t show_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm90_data *data = lm90_update_device(dev); + int bitnr = attr->index; + + return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); +} + static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0); static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, @@ -344,8 +355,45 @@ static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst, set_temphyst, 3); static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4); + +/* Individual alarm files */ +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6); +/* Raw alarm file for compatibility */ static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static struct attribute *lm90_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, + + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_input_fault.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &dev_attr_alarms.attr, + NULL +}; + +static const struct attribute_group lm90_group = { + .attrs = lm90_attributes, +}; + /* pec used for ADM1032 only */ static ssize_t show_pec(struct device *dev, struct device_attribute *dummy, char *buf) @@ -569,39 +617,25 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind) lm90_init_client(new_client); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &lm90_group))) + goto exit_detach; + if (new_client->flags & I2C_CLIENT_PEC) { + if ((err = device_create_file(&new_client->dev, + &dev_attr_pec))) + goto exit_remove_files; + } + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove_files; } - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_min.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_min.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_crit.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_crit.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_crit_hyst.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_crit_hyst.dev_attr); - device_create_file(&new_client->dev, &dev_attr_alarms); - - if (new_client->flags & I2C_CLIENT_PEC) - device_create_file(&new_client->dev, &dev_attr_pec); - return 0; +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &lm90_group); + device_remove_file(&new_client->dev, &dev_attr_pec); exit_detach: i2c_detach_client(new_client); exit_free: @@ -634,6 +668,8 @@ static int lm90_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &lm90_group); + device_remove_file(&client->dev, &dev_attr_pec); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index 197f77226dc..30b536333f1 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -288,6 +288,23 @@ static int max6635_check(struct i2c_client *client) return 1; } +static struct attribute *lm92_attributes[] = { + &dev_attr_temp1_input.attr, + &dev_attr_temp1_crit.attr, + &dev_attr_temp1_crit_hyst.attr, + &dev_attr_temp1_min.attr, + &dev_attr_temp1_min_hyst.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp1_max_hyst.attr, + &dev_attr_alarms.attr, + + NULL +}; + +static const struct attribute_group lm92_group = { + .attrs = lm92_attributes, +}; + /* The following function does more than just detection. If detection succeeds, it also registers the new chip. */ static int lm92_detect(struct i2c_adapter *adapter, int address, int kind) @@ -359,23 +376,19 @@ static int lm92_detect(struct i2c_adapter *adapter, int address, int kind) lm92_init_client(new_client); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &lm92_group))) + goto exit_detach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove; } - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_crit); - device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_alarms); - return 0; +exit_remove: + sysfs_remove_group(&new_client->dev.kobj, &lm92_group); exit_detach: i2c_detach_client(new_client); exit_free: @@ -397,6 +410,7 @@ static int lm92_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &lm92_group); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index b4135b5971f..2f58f651f03 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -34,6 +34,7 @@ #include <linux/hwmon.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/sysfs.h> static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, @@ -172,6 +173,22 @@ static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst2, set_temp_hyst2); static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static struct attribute *max1619_attributes[] = { + &dev_attr_temp1_input.attr, + &dev_attr_temp2_input.attr, + &dev_attr_temp2_min.attr, + &dev_attr_temp2_max.attr, + &dev_attr_temp2_crit.attr, + &dev_attr_temp2_crit_hyst.attr, + + &dev_attr_alarms.attr, + NULL +}; + +static const struct attribute_group max1619_group = { + .attrs = max1619_attributes, +}; + /* * Real code */ @@ -273,22 +290,19 @@ static int max1619_detect(struct i2c_adapter *adapter, int address, int kind) max1619_init_client(new_client); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &max1619_group))) + goto exit_detach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove_files; } - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp2_min); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_temp2_crit); - device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst); - device_create_file(&new_client->dev, &dev_attr_alarms); - return 0; +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &max1619_group); exit_detach: i2c_detach_client(new_client); exit_free: @@ -318,6 +332,7 @@ static int max1619_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &max1619_group); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 236f9f29c62..3b8b81984ad 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -328,6 +328,12 @@ static struct sensor_device_attribute fan_min[] = { SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 2), }; +#define FAN_UNIT_ATTRS(X) \ + &fan_input[X].dev_attr.attr, \ + &fan_status[X].dev_attr.attr, \ + &fan_div[X].dev_attr.attr, \ + &fan_min[X].dev_attr.attr + static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); @@ -360,6 +366,19 @@ static struct sensor_device_attribute pwm[] = { SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2), }; +static struct attribute * pc8736x_fan_attr_array[] = { + FAN_UNIT_ATTRS(0), + FAN_UNIT_ATTRS(1), + FAN_UNIT_ATTRS(2), + &pwm[0].dev_attr.attr, + &pwm[1].dev_attr.attr, + &pwm[2].dev_attr.attr, + NULL +}; +static const struct attribute_group pc8736x_fan_group = { + .attrs = pc8736x_fan_attr_array, +}; + static ssize_t show_in_input(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); @@ -472,6 +491,61 @@ static struct sensor_device_attribute in_max[] = { SENSOR_ATTR(in10_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 10), }; +#define VIN_UNIT_ATTRS(X) \ + &in_input[X].dev_attr.attr, \ + &in_status[X].dev_attr.attr, \ + &in_min[X].dev_attr.attr, \ + &in_max[X].dev_attr.attr + +static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm)); +} +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + +static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", data->vrm); +} +static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pc87360_data *data = i2c_get_clientdata(client); + data->vrm = simple_strtoul(buf, NULL, 10); + return count; +} +static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); + +static ssize_t show_in_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", data->in_alarms); +} +static DEVICE_ATTR(alarms_in, S_IRUGO, show_in_alarms, NULL); + +static struct attribute *pc8736x_vin_attr_array[] = { + VIN_UNIT_ATTRS(0), + VIN_UNIT_ATTRS(1), + VIN_UNIT_ATTRS(2), + VIN_UNIT_ATTRS(3), + VIN_UNIT_ATTRS(4), + VIN_UNIT_ATTRS(5), + VIN_UNIT_ATTRS(6), + VIN_UNIT_ATTRS(7), + VIN_UNIT_ATTRS(8), + VIN_UNIT_ATTRS(9), + VIN_UNIT_ATTRS(10), + &dev_attr_cpu0_vid.attr, + &dev_attr_vrm.attr, + &dev_attr_alarms_in.attr, + NULL +}; +static const struct attribute_group pc8736x_vin_group = { + .attrs = pc8736x_vin_attr_array, +}; + static ssize_t show_therm_input(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); @@ -590,33 +664,22 @@ static struct sensor_device_attribute therm_crit[] = { show_therm_crit, set_therm_crit, 2+11), }; -static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm)); -} -static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); - -static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", data->vrm); -} -static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pc87360_data *data = i2c_get_clientdata(client); - data->vrm = simple_strtoul(buf, NULL, 10); - return count; -} -static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); - -static ssize_t show_in_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", data->in_alarms); -} -static DEVICE_ATTR(alarms_in, S_IRUGO, show_in_alarms, NULL); +#define THERM_UNIT_ATTRS(X) \ + &therm_input[X].dev_attr.attr, \ + &therm_status[X].dev_attr.attr, \ + &therm_min[X].dev_attr.attr, \ + &therm_max[X].dev_attr.attr, \ + &therm_crit[X].dev_attr.attr + +static struct attribute * pc8736x_therm_attr_array[] = { + THERM_UNIT_ATTRS(0), + THERM_UNIT_ATTRS(1), + THERM_UNIT_ATTRS(2), + NULL +}; +static const struct attribute_group pc8736x_therm_group = { + .attrs = pc8736x_therm_attr_array, +}; static ssize_t show_temp_input(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -736,6 +799,25 @@ static ssize_t show_temp_alarms(struct device *dev, struct device_attribute *att } static DEVICE_ATTR(alarms_temp, S_IRUGO, show_temp_alarms, NULL); +#define TEMP_UNIT_ATTRS(X) \ + &temp_input[X].dev_attr.attr, \ + &temp_status[X].dev_attr.attr, \ + &temp_min[X].dev_attr.attr, \ + &temp_max[X].dev_attr.attr, \ + &temp_crit[X].dev_attr.attr + +static struct attribute * pc8736x_temp_attr_array[] = { + TEMP_UNIT_ATTRS(0), + TEMP_UNIT_ATTRS(1), + TEMP_UNIT_ATTRS(2), + /* include the few miscellaneous atts here */ + &dev_attr_alarms_temp.attr, + NULL +}; +static const struct attribute_group pc8736x_temp_group = { + .attrs = pc8736x_temp_attr_array, +}; + /* * Device detection, registration and update */ @@ -936,60 +1018,69 @@ static int pc87360_detect(struct i2c_adapter *adapter) pc87360_init_client(client, use_thermistors); } - /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); + /* Register all-or-nothing sysfs groups */ + + if (data->innr && + (err = sysfs_create_group(&client->dev.kobj, + &pc8736x_vin_group))) goto ERROR3; - } - if (data->innr) { - for (i = 0; i < 11; i++) { - device_create_file(dev, &in_input[i].dev_attr); - device_create_file(dev, &in_min[i].dev_attr); - device_create_file(dev, &in_max[i].dev_attr); - device_create_file(dev, &in_status[i].dev_attr); - } - device_create_file(dev, &dev_attr_cpu0_vid); - device_create_file(dev, &dev_attr_vrm); - device_create_file(dev, &dev_attr_alarms_in); - } + if (data->innr == 14 && + (err = sysfs_create_group(&client->dev.kobj, + &pc8736x_therm_group))) + goto ERROR3; + + /* create device attr-files for varying sysfs groups */ if (data->tempnr) { for (i = 0; i < data->tempnr; i++) { - device_create_file(dev, &temp_input[i].dev_attr); - device_create_file(dev, &temp_min[i].dev_attr); - device_create_file(dev, &temp_max[i].dev_attr); - device_create_file(dev, &temp_crit[i].dev_attr); - device_create_file(dev, &temp_status[i].dev_attr); - } - device_create_file(dev, &dev_attr_alarms_temp); - } - - if (data->innr == 14) { - for (i = 0; i < 3; i++) { - device_create_file(dev, &therm_input[i].dev_attr); - device_create_file(dev, &therm_min[i].dev_attr); - device_create_file(dev, &therm_max[i].dev_attr); - device_create_file(dev, &therm_crit[i].dev_attr); - device_create_file(dev, &therm_status[i].dev_attr); + if ((err = device_create_file(dev, + &temp_input[i].dev_attr)) + || (err = device_create_file(dev, + &temp_min[i].dev_attr)) + || (err = device_create_file(dev, + &temp_max[i].dev_attr)) + || (err = device_create_file(dev, + &temp_crit[i].dev_attr)) + || (err = device_create_file(dev, + &temp_status[i].dev_attr))) + goto ERROR3; } + if ((err = device_create_file(dev, &dev_attr_alarms_temp))) + goto ERROR3; } for (i = 0; i < data->fannr; i++) { - if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { - device_create_file(dev, &fan_input[i].dev_attr); - device_create_file(dev, &fan_min[i].dev_attr); - device_create_file(dev, &fan_div[i].dev_attr); - device_create_file(dev, &fan_status[i].dev_attr); - } - if (FAN_CONFIG_CONTROL(data->fan_conf, i)) - device_create_file(dev, &pwm[i].dev_attr); + if (FAN_CONFIG_MONITOR(data->fan_conf, i) + && ((err = device_create_file(dev, + &fan_input[i].dev_attr)) + || (err = device_create_file(dev, + &fan_min[i].dev_attr)) + || (err = device_create_file(dev, + &fan_div[i].dev_attr)) + || (err = device_create_file(dev, + &fan_status[i].dev_attr)))) + goto ERROR3; + + if (FAN_CONFIG_CONTROL(data->fan_conf, i) + && (err = device_create_file(dev, &pwm[i].dev_attr))) + goto ERROR3; } + data->class_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto ERROR3; + } return 0; ERROR3: + /* can still remove groups whose members were added individually */ + sysfs_remove_group(&client->dev.kobj, &pc8736x_temp_group); + sysfs_remove_group(&client->dev.kobj, &pc8736x_fan_group); + sysfs_remove_group(&client->dev.kobj, &pc8736x_therm_group); + sysfs_remove_group(&client->dev.kobj, &pc8736x_vin_group); + i2c_detach_client(client); ERROR2: for (i = 0; i < 3; i++) { @@ -1009,6 +1100,11 @@ static int pc87360_detach_client(struct i2c_client *client) hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &pc8736x_temp_group); + sysfs_remove_group(&client->dev.kobj, &pc8736x_fan_group); + sysfs_remove_group(&client->dev.kobj, &pc8736x_therm_group); + sysfs_remove_group(&client->dev.kobj, &pc8736x_vin_group); + if ((i = i2c_detach_client(client))) return i; diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c index 3783af4195b..95a4b5d9eaf 100644 --- a/drivers/hwmon/sis5595.c +++ b/drivers/hwmon/sis5595.c @@ -61,6 +61,7 @@ #include <linux/init.h> #include <linux/jiffies.h> #include <linux/mutex.h> +#include <linux/sysfs.h> #include <asm/io.h> @@ -473,6 +474,50 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, ch return sprintf(buf, "%d\n", data->alarms); } static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +static struct attribute *sis5595_attributes[] = { + &dev_attr_in0_input.attr, + &dev_attr_in0_min.attr, + &dev_attr_in0_max.attr, + &dev_attr_in1_input.attr, + &dev_attr_in1_min.attr, + &dev_attr_in1_max.attr, + &dev_attr_in2_input.attr, + &dev_attr_in2_min.attr, + &dev_attr_in2_max.attr, + &dev_attr_in3_input.attr, + &dev_attr_in3_min.attr, + &dev_attr_in3_max.attr, + + &dev_attr_fan1_input.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan2_input.attr, + &dev_attr_fan2_min.attr, + &dev_attr_fan2_div.attr, + + &dev_attr_alarms.attr, + NULL +}; + +static const struct attribute_group sis5595_group = { + .attrs = sis5595_attributes, +}; + +static struct attribute *sis5595_attributes_opt[] = { + &dev_attr_in4_input.attr, + &dev_attr_in4_min.attr, + &dev_attr_in4_max.attr, + + &dev_attr_temp1_input.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp1_max_hyst.attr, + NULL +}; + +static const struct attribute_group sis5595_group_opt = { + .attrs = sis5595_attributes_opt, +}; /* This is called when the module is loaded */ static int sis5595_detect(struct i2c_adapter *adapter) @@ -566,43 +611,37 @@ static int sis5595_detect(struct i2c_adapter *adapter) } /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &sis5595_group))) + goto exit_detach; + if (data->maxins == 4) { + if ((err = device_create_file(&new_client->dev, + &dev_attr_in4_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in4_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in4_max))) + goto exit_remove_files; + } else { + if ((err = device_create_file(&new_client->dev, + &dev_attr_temp1_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp1_max)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp1_max_hyst))) + goto exit_remove_files; + } + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove_files; } - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in3_max); - if (data->maxins == 4) { - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in4_max); - } - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - device_create_file(&new_client->dev, &dev_attr_alarms); - if (data->maxins == 3) { - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - } return 0; +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &sis5595_group); + sysfs_remove_group(&new_client->dev.kobj, &sis5595_group_opt); exit_detach: i2c_detach_client(new_client); exit_free: @@ -619,6 +658,8 @@ static int sis5595_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &sis5595_group); + sysfs_remove_group(&client->dev.kobj, &sis5595_group_opt); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c index a85869393ba..72b0e2d8650 100644 --- a/drivers/hwmon/smsc47b397.c +++ b/drivers/hwmon/smsc47b397.c @@ -176,9 +176,6 @@ sysfs_temp(2); sysfs_temp(3); sysfs_temp(4); -#define device_create_file_temp(client, num) \ - device_create_file(&client->dev, &dev_attr_temp##num##_input) - /* FAN: 1 RPM/bit REG: count of 90kHz pulses / revolution */ static int fan_from_reg(u16 reg) @@ -205,8 +202,22 @@ sysfs_fan(2); sysfs_fan(3); sysfs_fan(4); -#define device_create_file_fan(client, num) \ - device_create_file(&client->dev, &dev_attr_fan##num##_input) +static struct attribute *smsc47b397_attributes[] = { + &dev_attr_temp1_input.attr, + &dev_attr_temp2_input.attr, + &dev_attr_temp3_input.attr, + &dev_attr_temp4_input.attr, + &dev_attr_fan1_input.attr, + &dev_attr_fan2_input.attr, + &dev_attr_fan3_input.attr, + &dev_attr_fan4_input.attr, + + NULL +}; + +static const struct attribute_group smsc47b397_group = { + .attrs = smsc47b397_attributes, +}; static int smsc47b397_detach_client(struct i2c_client *client) { @@ -214,6 +225,7 @@ static int smsc47b397_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &smsc47b397_group); if ((err = i2c_detach_client(client))) return err; @@ -268,24 +280,19 @@ static int smsc47b397_detect(struct i2c_adapter *adapter) if ((err = i2c_attach_client(new_client))) goto error_free; + if ((err = sysfs_create_group(&new_client->dev.kobj, &smsc47b397_group))) + goto error_detach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto error_detach; + goto error_remove; } - device_create_file_temp(new_client, 1); - device_create_file_temp(new_client, 2); - device_create_file_temp(new_client, 3); - device_create_file_temp(new_client, 4); - - device_create_file_fan(new_client, 1); - device_create_file_fan(new_client, 2); - device_create_file_fan(new_client, 3); - device_create_file_fan(new_client, 4); - return 0; +error_remove: + sysfs_remove_group(&new_client->dev.kobj, &smsc47b397_group); error_detach: i2c_detach_client(new_client); error_free: diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index 6c81b843d83..47132fd26b1 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -35,6 +35,7 @@ #include <linux/err.h> #include <linux/init.h> #include <linux/mutex.h> +#include <linux/sysfs.h> #include <asm/io.h> /* Address is autodetected, there is no default value */ @@ -347,6 +348,30 @@ fan_present(2); static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL); +/* Almost all sysfs files may or may not be created depending on the chip + setup so we create them individually. It is still convenient to define a + group to remove them all at once. */ +static struct attribute *smsc47m1_attributes[] = { + &dev_attr_fan1_input.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan2_input.attr, + &dev_attr_fan2_min.attr, + &dev_attr_fan2_div.attr, + + &dev_attr_pwm1.attr, + &dev_attr_pwm1_enable.attr, + &dev_attr_pwm2.attr, + &dev_attr_pwm2_enable.attr, + + &dev_attr_alarms.attr, + NULL +}; + +static const struct attribute_group smsc47m1_group = { + .attrs = smsc47m1_attributes, +}; + static int __init smsc47m1_find(unsigned short *addr) { u8 val; @@ -429,7 +454,8 @@ static int smsc47m1_detect(struct i2c_adapter *adapter) pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05) == 0x04; if (!(fan1 || fan2 || pwm1 || pwm2)) { - dev_warn(&new_client->dev, "Device is not configured, will not use\n"); + dev_warn(&adapter->dev, "Device at 0x%x is not configured, " + "will not use\n", new_client->addr); err = -ENODEV; goto error_free; } @@ -446,46 +472,62 @@ static int smsc47m1_detect(struct i2c_adapter *adapter) smsc47m1_update_device(&new_client->dev, 1); /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); - goto error_detach; - } - if (fan1) { - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan1_div); + if ((err = device_create_file(&new_client->dev, + &dev_attr_fan1_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan1_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan1_div))) + goto error_remove_files; } else dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, " "skipping\n"); if (fan2) { - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan2_div); + if ((err = device_create_file(&new_client->dev, + &dev_attr_fan2_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan2_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan2_div))) + goto error_remove_files; } else dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, " "skipping\n"); if (pwm1) { - device_create_file(&new_client->dev, &dev_attr_pwm1); - device_create_file(&new_client->dev, &dev_attr_pwm1_enable); + if ((err = device_create_file(&new_client->dev, + &dev_attr_pwm1)) + || (err = device_create_file(&new_client->dev, + &dev_attr_pwm1_enable))) + goto error_remove_files; } else dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, " "skipping\n"); if (pwm2) { - device_create_file(&new_client->dev, &dev_attr_pwm2); - device_create_file(&new_client->dev, &dev_attr_pwm2_enable); + if ((err = device_create_file(&new_client->dev, + &dev_attr_pwm2)) + || (err = device_create_file(&new_client->dev, + &dev_attr_pwm2_enable))) + goto error_remove_files; } else dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, " "skipping\n"); - device_create_file(&new_client->dev, &dev_attr_alarms); + if ((err = device_create_file(&new_client->dev, &dev_attr_alarms))) + goto error_remove_files; + + data->class_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto error_remove_files; + } return 0; -error_detach: +error_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &smsc47m1_group); i2c_detach_client(new_client); error_free: kfree(data); @@ -500,6 +542,7 @@ static int smsc47m1_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &smsc47m1_group); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c index bdc4570acf9..a6833f43739 100644 --- a/drivers/hwmon/smsc47m192.c +++ b/drivers/hwmon/smsc47m192.c @@ -30,6 +30,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/hwmon-vid.h> #include <linux/err.h> +#include <linux/sysfs.h> /* Addresses to scan */ static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; @@ -370,6 +371,75 @@ static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 0x0200); static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 0x0400); static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 0x0800); +static struct attribute *smsc47m192_attributes[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_alarm.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in6_alarm.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, + &sensor_dev_attr_in7_alarm.dev_attr.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_offset.dev_attr.attr, + &sensor_dev_attr_temp1_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_offset.dev_attr.attr, + &sensor_dev_attr_temp2_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_input_fault.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_offset.dev_attr.attr, + &sensor_dev_attr_temp3_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_input_fault.dev_attr.attr, + + &dev_attr_cpu0_vid.attr, + &dev_attr_vrm.attr, + NULL +}; + +static const struct attribute_group smsc47m192_group = { + .attrs = smsc47m192_attributes, +}; + +static struct attribute *smsc47m192_attributes_in4[] = { + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group smsc47m192_group_in4 = { + .attrs = smsc47m192_attributes_in4, +}; + /* This function is called when: * smsc47m192_driver is inserted (when this module is loaded), for each available adapter @@ -471,80 +541,28 @@ static int smsc47m192_detect(struct i2c_adapter *adapter, int address, smsc47m192_init_client(client); /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); + if ((err = sysfs_create_group(&client->dev.kobj, &smsc47m192_group))) goto exit_detach; - } - - device_create_file(&client->dev, &sensor_dev_attr_in0_input.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in0_min.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in0_max.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in0_alarm.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in1_input.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in1_min.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in1_max.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in1_alarm.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in2_input.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in2_min.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in2_max.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in2_alarm.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in3_input.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in3_min.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in3_max.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in3_alarm.dev_attr); /* Pin 110 is either in4 (+12V) or VID4 */ config = i2c_smbus_read_byte_data(client, SMSC47M192_REG_CONFIG); if (!(config & 0x20)) { - device_create_file(&client->dev, - &sensor_dev_attr_in4_input.dev_attr); - device_create_file(&client->dev, - &sensor_dev_attr_in4_min.dev_attr); - device_create_file(&client->dev, - &sensor_dev_attr_in4_max.dev_attr); - device_create_file(&client->dev, - &sensor_dev_attr_in4_alarm.dev_attr); + if ((err = sysfs_create_group(&client->dev.kobj, + &smsc47m192_group_in4))) + goto exit_remove_files; + } + + data->class_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove_files; } - device_create_file(&client->dev, &sensor_dev_attr_in5_input.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in5_min.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in5_max.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in5_alarm.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in6_input.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in6_min.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in6_max.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in6_alarm.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in7_input.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in7_min.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in7_max.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_in7_alarm.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_temp1_input.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_temp1_max.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_temp1_min.dev_attr); - device_create_file(&client->dev, - &sensor_dev_attr_temp1_offset.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_temp1_alarm.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_temp2_input.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_temp2_max.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_temp2_min.dev_attr); - device_create_file(&client->dev, - &sensor_dev_attr_temp2_offset.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_temp2_alarm.dev_attr); - device_create_file(&client->dev, - &sensor_dev_attr_temp2_input_fault.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_temp3_input.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_temp3_max.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_temp3_min.dev_attr); - device_create_file(&client->dev, - &sensor_dev_attr_temp3_offset.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_temp3_alarm.dev_attr); - device_create_file(&client->dev, - &sensor_dev_attr_temp3_input_fault.dev_attr); - device_create_file(&client->dev, &dev_attr_cpu0_vid); - device_create_file(&client->dev, &dev_attr_vrm); return 0; +exit_remove_files: + sysfs_remove_group(&client->dev.kobj, &smsc47m192_group); + sysfs_remove_group(&client->dev.kobj, &smsc47m192_group_in4); exit_detach: i2c_detach_client(client); exit_free: @@ -559,6 +577,8 @@ static int smsc47m192_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &smsc47m192_group); + sysfs_remove_group(&client->dev.kobj, &smsc47m192_group_in4); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c index 95ae056e5a9..f8acada0537 100644 --- a/drivers/hwmon/via686a.c +++ b/drivers/hwmon/via686a.c @@ -40,6 +40,7 @@ #include <linux/err.h> #include <linux/init.h> #include <linux/mutex.h> +#include <linux/sysfs.h> #include <asm/io.h> @@ -570,6 +571,48 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, ch } static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static struct attribute *via686a_attributes[] = { + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + &dev_attr_in2_input.attr, + &dev_attr_in3_input.attr, + &dev_attr_in4_input.attr, + &dev_attr_in0_min.attr, + &dev_attr_in1_min.attr, + &dev_attr_in2_min.attr, + &dev_attr_in3_min.attr, + &dev_attr_in4_min.attr, + &dev_attr_in0_max.attr, + &dev_attr_in1_max.attr, + &dev_attr_in2_max.attr, + &dev_attr_in3_max.attr, + &dev_attr_in4_max.attr, + + &dev_attr_temp1_input.attr, + &dev_attr_temp2_input.attr, + &dev_attr_temp3_input.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp2_max.attr, + &dev_attr_temp3_max.attr, + &dev_attr_temp1_max_hyst.attr, + &dev_attr_temp2_max_hyst.attr, + &dev_attr_temp3_max_hyst.attr, + + &dev_attr_fan1_input.attr, + &dev_attr_fan2_input.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan2_min.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan2_div.attr, + + &dev_attr_alarms.attr, + NULL +}; + +static const struct attribute_group via686a_group = { + .attrs = via686a_attributes, +}; + /* The driver. I choose to use type i2c_driver, as at is identical to both smbus_driver and isa_driver, and clients could be of either kind */ static struct i2c_driver via686a_driver = { @@ -650,46 +693,19 @@ static int via686a_detect(struct i2c_adapter *adapter) via686a_init_client(new_client); /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &via686a_group))) + goto exit_detach; + data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove_files; } - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_in4_max); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp3_input); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_temp3_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_temp2_max_hyst); - device_create_file(&new_client->dev, &dev_attr_temp3_max_hyst); - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - device_create_file(&new_client->dev, &dev_attr_alarms); - return 0; +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &via686a_group); exit_detach: i2c_detach_client(new_client); exit_free: @@ -705,6 +721,7 @@ static int via686a_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &via686a_group); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c new file mode 100644 index 00000000000..25cc56003d7 --- /dev/null +++ b/drivers/hwmon/vt1211.c @@ -0,0 +1,1355 @@ +/* + * vt1211.c - driver for the VIA VT1211 Super-I/O chip integrated hardware + * monitoring features + * Copyright (C) 2006 Juerg Haefliger <juergh@gmail.com> + * + * This driver is based on the driver for kernel 2.4 by Mark D. Studebaker + * and its port to kernel 2.6 by Lars Ekman. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/hwmon-vid.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <asm/io.h> + +static int uch_config = -1; +module_param(uch_config, int, 0); +MODULE_PARM_DESC(uch_config, "Initialize the universal channel configuration"); + +static int int_mode = -1; +module_param(int_mode, int, 0); +MODULE_PARM_DESC(int_mode, "Force the temperature interrupt mode"); + +static struct platform_device *pdev; + +#define DRVNAME "vt1211" + +/* --------------------------------------------------------------------- + * Registers + * + * The sensors are defined as follows. + * + * Sensor Voltage Mode Temp Mode Notes (from the datasheet) + * -------- ------------ --------- -------------------------- + * Reading 1 temp1 Intel thermal diode + * Reading 3 temp2 Internal thermal diode + * UCH1/Reading2 in0 temp3 NTC type thermistor + * UCH2 in1 temp4 +2.5V + * UCH3 in2 temp5 VccP + * UCH4 in3 temp6 +5V + * UCH5 in4 temp7 +12V + * 3.3V in5 Internal VDD (+3.3V) + * + * --------------------------------------------------------------------- */ + +/* Voltages (in) numbered 0-5 (ix) */ +#define VT1211_REG_IN(ix) (0x21 + (ix)) +#define VT1211_REG_IN_MIN(ix) ((ix) == 0 ? 0x3e : 0x2a + 2 * (ix)) +#define VT1211_REG_IN_MAX(ix) ((ix) == 0 ? 0x3d : 0x29 + 2 * (ix)) + +/* Temperatures (temp) numbered 0-6 (ix) */ +static u8 regtemp[] = {0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25}; +static u8 regtempmax[] = {0x39, 0x1d, 0x3d, 0x2b, 0x2d, 0x2f, 0x31}; +static u8 regtemphyst[] = {0x3a, 0x1e, 0x3e, 0x2c, 0x2e, 0x30, 0x32}; + +/* Fans numbered 0-1 (ix) */ +#define VT1211_REG_FAN(ix) (0x29 + (ix)) +#define VT1211_REG_FAN_MIN(ix) (0x3b + (ix)) +#define VT1211_REG_FAN_DIV 0x47 + +/* PWMs numbered 0-1 (ix) */ +/* Auto points numbered 0-3 (ap) */ +#define VT1211_REG_PWM(ix) (0x60 + (ix)) +#define VT1211_REG_PWM_CLK 0x50 +#define VT1211_REG_PWM_CTL 0x51 +#define VT1211_REG_PWM_AUTO_TEMP(ap) (0x55 - (ap)) +#define VT1211_REG_PWM_AUTO_PWM(ix, ap) (0x58 + 2 * (ix) - (ap)) + +/* Miscellaneous registers */ +#define VT1211_REG_CONFIG 0x40 +#define VT1211_REG_ALARM1 0x41 +#define VT1211_REG_ALARM2 0x42 +#define VT1211_REG_VID 0x45 +#define VT1211_REG_UCH_CONFIG 0x4a +#define VT1211_REG_TEMP1_CONFIG 0x4b +#define VT1211_REG_TEMP2_CONFIG 0x4c + +/* In, temp & fan alarm bits */ +static const u8 bitalarmin[] = {11, 0, 1, 3, 8, 2, 9}; +static const u8 bitalarmtemp[] = {4, 15, 11, 0, 1, 3, 8}; +static const u8 bitalarmfan[] = {6, 7}; + +/* --------------------------------------------------------------------- + * Data structures and manipulation thereof + * --------------------------------------------------------------------- */ + +struct vt1211_data { + unsigned short addr; + const char *name; + struct class_device *class_dev; + + struct mutex update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + /* Register values */ + u8 in[6]; + u8 in_max[6]; + u8 in_min[6]; + u8 temp[7]; + u8 temp_max[7]; + u8 temp_hyst[7]; + u8 fan[2]; + u8 fan_min[2]; + u8 fan_div[2]; + u8 fan_ctl; + u8 pwm[2]; + u8 pwm_ctl[2]; + u8 pwm_clk; + u8 pwm_auto_temp[4]; + u8 pwm_auto_pwm[2][4]; + u8 vid; /* Read once at init time */ + u8 vrm; + u8 uch_config; /* Read once at init time */ + u16 alarms; +}; + +/* ix = [0-5] */ +#define ISVOLT(ix, uch_config) ((ix) > 4 ? 1 : \ + !(((uch_config) >> ((ix) + 2)) & 1)) + +/* ix = [0-6] */ +#define ISTEMP(ix, uch_config) ((ix) < 2 ? 1 : \ + ((uch_config) >> (ix)) & 1) + +/* in5 (ix = 5) is special. It's the internal 3.3V so it's scaled in the + driver according to the VT1211 BIOS porting guide */ +#define IN_FROM_REG(ix, reg) ((reg) < 3 ? 0 : (ix) == 5 ? \ + (((reg) - 3) * 15882 + 479) / 958 : \ + (((reg) - 3) * 10000 + 479) / 958) +#define IN_TO_REG(ix, val) (SENSORS_LIMIT((ix) == 5 ? \ + ((val) * 958 + 7941) / 15882 + 3 : \ + ((val) * 958 + 5000) / 10000 + 3, 0, 255)) + +/* temp1 (ix = 0) is an intel thermal diode which is scaled in user space. + temp2 (ix = 1) is the internal temp diode so it's scaled in the driver + according to some measurements that I took on an EPIA M10000. + temp3-7 are thermistor based so the driver returns the voltage measured at + the pin (range 0V - 2.2V). */ +#define TEMP_FROM_REG(ix, reg) ((ix) == 0 ? (reg) * 1000 : \ + (ix) == 1 ? (reg) < 51 ? 0 : \ + ((reg) - 51) * 1000 : \ + ((253 - (reg)) * 2200 + 105) / 210) +#define TEMP_TO_REG(ix, val) SENSORS_LIMIT( \ + ((ix) == 0 ? ((val) + 500) / 1000 : \ + (ix) == 1 ? ((val) + 500) / 1000 + 51 : \ + 253 - ((val) * 210 + 1100) / 2200), 0, 255) + +#define DIV_FROM_REG(reg) (1 << (reg)) + +#define RPM_FROM_REG(reg, div) (((reg) == 0) || ((reg) == 255) ? 0 : \ + 1310720 / (reg) / DIV_FROM_REG(div)) +#define RPM_TO_REG(val, div) ((val) == 0 ? 255 : \ + SENSORS_LIMIT((1310720 / (val) / \ + DIV_FROM_REG(div)), 1, 254)) + +/* --------------------------------------------------------------------- + * Super-I/O constants and functions + * --------------------------------------------------------------------- */ + +/* Configuration & data index port registers */ +#define SIO_REG_CIP 0x2e +#define SIO_REG_DIP 0x2f + +/* Configuration registers */ +#define SIO_VT1211_LDN 0x07 /* logical device number */ +#define SIO_VT1211_DEVID 0x20 /* device ID */ +#define SIO_VT1211_DEVREV 0x21 /* device revision */ +#define SIO_VT1211_ACTIVE 0x30 /* HW monitor active */ +#define SIO_VT1211_BADDR 0x60 /* base I/O address */ +#define SIO_VT1211_ID 0x3c /* VT1211 device ID */ + +/* VT1211 logical device numbers */ +#define SIO_VT1211_LDN_HWMON 0x0b /* HW monitor */ + +static inline void superio_outb(int reg, int val) +{ + outb(reg, SIO_REG_CIP); + outb(val, SIO_REG_DIP); +} + +static inline int superio_inb(int reg) +{ + outb(reg, SIO_REG_CIP); + return inb(SIO_REG_DIP); +} + +static inline void superio_select(int ldn) +{ + outb(SIO_VT1211_LDN, SIO_REG_CIP); + outb(ldn, SIO_REG_DIP); +} + +static inline void superio_enter(void) +{ + outb(0x87, SIO_REG_CIP); + outb(0x87, SIO_REG_CIP); +} + +static inline void superio_exit(void) +{ + outb(0xaa, SIO_REG_CIP); +} + +/* --------------------------------------------------------------------- + * Device I/O access + * --------------------------------------------------------------------- */ + +static inline u8 vt1211_read8(struct vt1211_data *data, u8 reg) +{ + return inb(data->addr + reg); +} + +static inline void vt1211_write8(struct vt1211_data *data, u8 reg, u8 val) +{ + outb(val, data->addr + reg); +} + +static struct vt1211_data *vt1211_update_device(struct device *dev) +{ + struct vt1211_data *data = dev_get_drvdata(dev); + int ix, val; + + mutex_lock(&data->update_lock); + + /* registers cache is refreshed after 1 second */ + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + /* read VID */ + data->vid = vt1211_read8(data, VT1211_REG_VID) & 0x1f; + + /* voltage (in) registers */ + for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) { + if (ISVOLT(ix, data->uch_config)) { + data->in[ix] = vt1211_read8(data, + VT1211_REG_IN(ix)); + data->in_min[ix] = vt1211_read8(data, + VT1211_REG_IN_MIN(ix)); + data->in_max[ix] = vt1211_read8(data, + VT1211_REG_IN_MAX(ix)); + } + } + + /* temp registers */ + for (ix = 0; ix < ARRAY_SIZE(data->temp); ix++) { + if (ISTEMP(ix, data->uch_config)) { + data->temp[ix] = vt1211_read8(data, + regtemp[ix]); + data->temp_max[ix] = vt1211_read8(data, + regtempmax[ix]); + data->temp_hyst[ix] = vt1211_read8(data, + regtemphyst[ix]); + } + } + + /* fan & pwm registers */ + for (ix = 0; ix < ARRAY_SIZE(data->fan); ix++) { + data->fan[ix] = vt1211_read8(data, + VT1211_REG_FAN(ix)); + data->fan_min[ix] = vt1211_read8(data, + VT1211_REG_FAN_MIN(ix)); + data->pwm[ix] = vt1211_read8(data, + VT1211_REG_PWM(ix)); + } + val = vt1211_read8(data, VT1211_REG_FAN_DIV); + data->fan_div[0] = (val >> 4) & 3; + data->fan_div[1] = (val >> 6) & 3; + data->fan_ctl = val & 0xf; + + val = vt1211_read8(data, VT1211_REG_PWM_CTL); + data->pwm_ctl[0] = val & 0xf; + data->pwm_ctl[1] = (val >> 4) & 0xf; + + data->pwm_clk = vt1211_read8(data, VT1211_REG_PWM_CLK); + + /* pwm & temp auto point registers */ + data->pwm_auto_pwm[0][1] = vt1211_read8(data, + VT1211_REG_PWM_AUTO_PWM(0, 1)); + data->pwm_auto_pwm[0][2] = vt1211_read8(data, + VT1211_REG_PWM_AUTO_PWM(0, 2)); + data->pwm_auto_pwm[1][1] = vt1211_read8(data, + VT1211_REG_PWM_AUTO_PWM(1, 1)); + data->pwm_auto_pwm[1][2] = vt1211_read8(data, + VT1211_REG_PWM_AUTO_PWM(1, 2)); + for (ix = 0; ix < ARRAY_SIZE(data->pwm_auto_temp); ix++) { + data->pwm_auto_temp[ix] = vt1211_read8(data, + VT1211_REG_PWM_AUTO_TEMP(ix)); + } + + /* alarm registers */ + data->alarms = (vt1211_read8(data, VT1211_REG_ALARM2) << 8) | + vt1211_read8(data, VT1211_REG_ALARM1); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +/* --------------------------------------------------------------------- + * Voltage sysfs interfaces + * ix = [0-5] + * --------------------------------------------------------------------- */ + +#define SHOW_IN_INPUT 0 +#define SHOW_SET_IN_MIN 1 +#define SHOW_SET_IN_MAX 2 +#define SHOW_IN_ALARM 3 + +static ssize_t show_in(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct vt1211_data *data = vt1211_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr_2 = + to_sensor_dev_attr_2(attr); + int ix = sensor_attr_2->index; + int fn = sensor_attr_2->nr; + int res; + + switch (fn) { + case SHOW_IN_INPUT: + res = IN_FROM_REG(ix, data->in[ix]); + break; + case SHOW_SET_IN_MIN: + res = IN_FROM_REG(ix, data->in_min[ix]); + break; + case SHOW_SET_IN_MAX: + res = IN_FROM_REG(ix, data->in_max[ix]); + break; + case SHOW_IN_ALARM: + res = (data->alarms >> bitalarmin[ix]) & 1; + break; + default: + res = 0; + dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); + } + + return sprintf(buf, "%d\n", res); +} + +static ssize_t set_in(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vt1211_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sensor_attr_2 = + to_sensor_dev_attr_2(attr); + int ix = sensor_attr_2->index; + int fn = sensor_attr_2->nr; + long val = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + switch (fn) { + case SHOW_SET_IN_MIN: + data->in_min[ix] = IN_TO_REG(ix, val); + vt1211_write8(data, VT1211_REG_IN_MIN(ix), data->in_min[ix]); + break; + case SHOW_SET_IN_MAX: + data->in_max[ix] = IN_TO_REG(ix, val); + vt1211_write8(data, VT1211_REG_IN_MAX(ix), data->in_max[ix]); + break; + default: + dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); + } + mutex_unlock(&data->update_lock); + + return count; +} + +/* --------------------------------------------------------------------- + * Temperature sysfs interfaces + * ix = [0-6] + * --------------------------------------------------------------------- */ + +#define SHOW_TEMP_INPUT 0 +#define SHOW_SET_TEMP_MAX 1 +#define SHOW_SET_TEMP_MAX_HYST 2 +#define SHOW_TEMP_ALARM 3 + +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct vt1211_data *data = vt1211_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr_2 = + to_sensor_dev_attr_2(attr); + int ix = sensor_attr_2->index; + int fn = sensor_attr_2->nr; + int res; + + switch (fn) { + case SHOW_TEMP_INPUT: + res = TEMP_FROM_REG(ix, data->temp[ix]); + break; + case SHOW_SET_TEMP_MAX: + res = TEMP_FROM_REG(ix, data->temp_max[ix]); + break; + case SHOW_SET_TEMP_MAX_HYST: + res = TEMP_FROM_REG(ix, data->temp_hyst[ix]); + break; + case SHOW_TEMP_ALARM: + res = (data->alarms >> bitalarmtemp[ix]) & 1; + break; + default: + res = 0; + dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); + } + + return sprintf(buf, "%d\n", res); +} + +static ssize_t set_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vt1211_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sensor_attr_2 = + to_sensor_dev_attr_2(attr); + int ix = sensor_attr_2->index; + int fn = sensor_attr_2->nr; + long val = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + switch (fn) { + case SHOW_SET_TEMP_MAX: + data->temp_max[ix] = TEMP_TO_REG(ix, val); + vt1211_write8(data, regtempmax[ix], + data->temp_max[ix]); + break; + case SHOW_SET_TEMP_MAX_HYST: + data->temp_hyst[ix] = TEMP_TO_REG(ix, val); + vt1211_write8(data, regtemphyst[ix], + data->temp_hyst[ix]); + break; + default: + dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); + } + mutex_unlock(&data->update_lock); + + return count; +} + +/* --------------------------------------------------------------------- + * Fan sysfs interfaces + * ix = [0-1] + * --------------------------------------------------------------------- */ + +#define SHOW_FAN_INPUT 0 +#define SHOW_SET_FAN_MIN 1 +#define SHOW_SET_FAN_DIV 2 +#define SHOW_FAN_ALARM 3 + +static ssize_t show_fan(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct vt1211_data *data = vt1211_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr_2 = + to_sensor_dev_attr_2(attr); + int ix = sensor_attr_2->index; + int fn = sensor_attr_2->nr; + int res; + + switch (fn) { + case SHOW_FAN_INPUT: + res = RPM_FROM_REG(data->fan[ix], data->fan_div[ix]); + break; + case SHOW_SET_FAN_MIN: + res = RPM_FROM_REG(data->fan_min[ix], data->fan_div[ix]); + break; + case SHOW_SET_FAN_DIV: + res = DIV_FROM_REG(data->fan_div[ix]); + break; + case SHOW_FAN_ALARM: + res = (data->alarms >> bitalarmfan[ix]) & 1; + break; + default: + res = 0; + dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); + } + + return sprintf(buf, "%d\n", res); +} + +static ssize_t set_fan(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vt1211_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sensor_attr_2 = + to_sensor_dev_attr_2(attr); + int ix = sensor_attr_2->index; + int fn = sensor_attr_2->nr; + long val = simple_strtol(buf, NULL, 10); + int reg; + + mutex_lock(&data->update_lock); + + /* sync the data cache */ + reg = vt1211_read8(data, VT1211_REG_FAN_DIV); + data->fan_div[0] = (reg >> 4) & 3; + data->fan_div[1] = (reg >> 6) & 3; + data->fan_ctl = reg & 0xf; + + switch (fn) { + case SHOW_SET_FAN_MIN: + data->fan_min[ix] = RPM_TO_REG(val, data->fan_div[ix]); + vt1211_write8(data, VT1211_REG_FAN_MIN(ix), + data->fan_min[ix]); + break; + case SHOW_SET_FAN_DIV: + switch (val) { + case 1: data->fan_div[ix] = 0; break; + case 2: data->fan_div[ix] = 1; break; + case 4: data->fan_div[ix] = 2; break; + case 8: data->fan_div[ix] = 3; break; + default: + count = -EINVAL; + dev_warn(dev, "fan div value %ld not " + "supported. Choose one of 1, 2, " + "4, or 8.\n", val); + goto EXIT; + } + vt1211_write8(data, VT1211_REG_FAN_DIV, + ((data->fan_div[1] << 6) | + (data->fan_div[0] << 4) | + data->fan_ctl)); + break; + default: + dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); + } + +EXIT: + mutex_unlock(&data->update_lock); + return count; +} + +/* --------------------------------------------------------------------- + * PWM sysfs interfaces + * ix = [0-1] + * --------------------------------------------------------------------- */ + +#define SHOW_PWM 0 +#define SHOW_SET_PWM_ENABLE 1 +#define SHOW_SET_PWM_FREQ 2 +#define SHOW_SET_PWM_AUTO_CHANNELS_TEMP 3 + +static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct vt1211_data *data = vt1211_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr_2 = + to_sensor_dev_attr_2(attr); + int ix = sensor_attr_2->index; + int fn = sensor_attr_2->nr; + int res; + + switch (fn) { + case SHOW_PWM: + res = data->pwm[ix]; + break; + case SHOW_SET_PWM_ENABLE: + res = ((data->pwm_ctl[ix] >> 3) & 1) ? 2 : 0; + break; + case SHOW_SET_PWM_FREQ: + res = 90000 >> (data->pwm_clk & 7); + break; + case SHOW_SET_PWM_AUTO_CHANNELS_TEMP: + res = (data->pwm_ctl[ix] & 7) + 1; + break; + default: + res = 0; + dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); + } + + return sprintf(buf, "%d\n", res); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vt1211_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sensor_attr_2 = + to_sensor_dev_attr_2(attr); + int ix = sensor_attr_2->index; + int fn = sensor_attr_2->nr; + long val = simple_strtol(buf, NULL, 10); + int tmp, reg; + + mutex_lock(&data->update_lock); + + switch (fn) { + case SHOW_SET_PWM_ENABLE: + /* sync the data cache */ + reg = vt1211_read8(data, VT1211_REG_FAN_DIV); + data->fan_div[0] = (reg >> 4) & 3; + data->fan_div[1] = (reg >> 6) & 3; + data->fan_ctl = reg & 0xf; + reg = vt1211_read8(data, VT1211_REG_PWM_CTL); + data->pwm_ctl[0] = reg & 0xf; + data->pwm_ctl[1] = (reg >> 4) & 0xf; + switch (val) { + case 0: + data->pwm_ctl[ix] &= 7; + /* disable SmartGuardian if both PWM outputs are + * disabled */ + if ((data->pwm_ctl[ix ^ 1] & 1) == 0) { + data->fan_ctl &= 0xe; + } + break; + case 2: + data->pwm_ctl[ix] |= 8; + data->fan_ctl |= 1; + break; + default: + count = -EINVAL; + dev_warn(dev, "pwm mode %ld not supported. " + "Choose one of 0 or 2.\n", val); + goto EXIT; + } + vt1211_write8(data, VT1211_REG_PWM_CTL, + ((data->pwm_ctl[1] << 4) | + data->pwm_ctl[0])); + vt1211_write8(data, VT1211_REG_FAN_DIV, + ((data->fan_div[1] << 6) | + (data->fan_div[0] << 4) | + data->fan_ctl)); + break; + case SHOW_SET_PWM_FREQ: + val = 135000 / SENSORS_LIMIT(val, 135000 >> 7, 135000); + /* calculate tmp = log2(val) */ + tmp = 0; + for (val >>= 1; val > 0; val >>= 1) { + tmp++; + } + /* sync the data cache */ + reg = vt1211_read8(data, VT1211_REG_PWM_CLK); + data->pwm_clk = (reg & 0xf8) | tmp; + vt1211_write8(data, VT1211_REG_PWM_CLK, data->pwm_clk); + break; + case SHOW_SET_PWM_AUTO_CHANNELS_TEMP: + if ((val < 1) || (val > 7)) { + count = -EINVAL; + dev_warn(dev, "temp channel %ld not supported. " + "Choose a value between 1 and 7.\n", val); + goto EXIT; + } + if (!ISTEMP(val - 1, data->uch_config)) { + count = -EINVAL; + dev_warn(dev, "temp channel %ld is not available.\n", + val); + goto EXIT; + } + /* sync the data cache */ + reg = vt1211_read8(data, VT1211_REG_PWM_CTL); + data->pwm_ctl[0] = reg & 0xf; + data->pwm_ctl[1] = (reg >> 4) & 0xf; + data->pwm_ctl[ix] = (data->pwm_ctl[ix] & 8) | (val - 1); + vt1211_write8(data, VT1211_REG_PWM_CTL, + ((data->pwm_ctl[1] << 4) | data->pwm_ctl[0])); + break; + default: + dev_dbg(dev, "Unknown attr fetch (%d)\n", fn); + } + +EXIT: + mutex_unlock(&data->update_lock); + return count; +} + +/* --------------------------------------------------------------------- + * PWM auto point definitions + * ix = [0-1] + * ap = [0-3] + * --------------------------------------------------------------------- */ + +/* + * pwm[ix+1]_auto_point[ap+1]_temp mapping table: + * Note that there is only a single set of temp auto points that controls both + * PWM controllers. We still create 2 sets of sysfs files to make it look + * more consistent even though they map to the same registers. + * + * ix ap : description + * ------------------- + * 0 0 : pwm1/2 off temperature (pwm_auto_temp[0]) + * 0 1 : pwm1/2 low speed temperature (pwm_auto_temp[1]) + * 0 2 : pwm1/2 high speed temperature (pwm_auto_temp[2]) + * 0 3 : pwm1/2 full speed temperature (pwm_auto_temp[3]) + * 1 0 : pwm1/2 off temperature (pwm_auto_temp[0]) + * 1 1 : pwm1/2 low speed temperature (pwm_auto_temp[1]) + * 1 2 : pwm1/2 high speed temperature (pwm_auto_temp[2]) + * 1 3 : pwm1/2 full speed temperature (pwm_auto_temp[3]) + */ + +static ssize_t show_pwm_auto_point_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct vt1211_data *data = vt1211_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr_2 = + to_sensor_dev_attr_2(attr); + int ix = sensor_attr_2->index; + int ap = sensor_attr_2->nr; + + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->pwm_ctl[ix] & 7, + data->pwm_auto_temp[ap])); +} + +static ssize_t set_pwm_auto_point_temp(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vt1211_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sensor_attr_2 = + to_sensor_dev_attr_2(attr); + int ix = sensor_attr_2->index; + int ap = sensor_attr_2->nr; + long val = simple_strtol(buf, NULL, 10); + int reg; + + mutex_lock(&data->update_lock); + + /* sync the data cache */ + reg = vt1211_read8(data, VT1211_REG_PWM_CTL); + data->pwm_ctl[0] = reg & 0xf; + data->pwm_ctl[1] = (reg >> 4) & 0xf; + + data->pwm_auto_temp[ap] = TEMP_TO_REG(data->pwm_ctl[ix] & 7, val); + vt1211_write8(data, VT1211_REG_PWM_AUTO_TEMP(ap), + data->pwm_auto_temp[ap]); + mutex_unlock(&data->update_lock); + + return count; +} + +/* + * pwm[ix+1]_auto_point[ap+1]_pwm mapping table: + * Note that the PWM auto points 0 & 3 are hard-wired in the VT1211 and can't + * be changed. + * + * ix ap : description + * ------------------- + * 0 0 : pwm1 off (pwm_auto_pwm[0][0], hard-wired to 0) + * 0 1 : pwm1 low speed duty cycle (pwm_auto_pwm[0][1]) + * 0 2 : pwm1 high speed duty cycle (pwm_auto_pwm[0][2]) + * 0 3 : pwm1 full speed (pwm_auto_pwm[0][3], hard-wired to 255) + * 1 0 : pwm2 off (pwm_auto_pwm[1][0], hard-wired to 0) + * 1 1 : pwm2 low speed duty cycle (pwm_auto_pwm[1][1]) + * 1 2 : pwm2 high speed duty cycle (pwm_auto_pwm[1][2]) + * 1 3 : pwm2 full speed (pwm_auto_pwm[1][3], hard-wired to 255) +*/ + +static ssize_t show_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct vt1211_data *data = vt1211_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr_2 = + to_sensor_dev_attr_2(attr); + int ix = sensor_attr_2->index; + int ap = sensor_attr_2->nr; + + return sprintf(buf, "%d\n", data->pwm_auto_pwm[ix][ap]); +} + +static ssize_t set_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vt1211_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sensor_attr_2 = + to_sensor_dev_attr_2(attr); + int ix = sensor_attr_2->index; + int ap = sensor_attr_2->nr; + long val = simple_strtol(buf, NULL, 10); + + if ((val < 0) || (val > 255)) { + dev_err(dev, "pwm value %ld is out of range. " + "Choose a value between 0 and 255." , val); + return -EINVAL; + } + + mutex_lock(&data->update_lock); + data->pwm_auto_pwm[ix][ap] = val; + vt1211_write8(data, VT1211_REG_PWM_AUTO_PWM(ix, ap), + data->pwm_auto_pwm[ix][ap]); + mutex_unlock(&data->update_lock); + + return count; +} + +/* --------------------------------------------------------------------- + * Miscellaneous sysfs interfaces (VRM, VID, name, and (legacy) alarms) + * --------------------------------------------------------------------- */ + +static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct vt1211_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", data->vrm); +} + +static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vt1211_data *data = dev_get_drvdata(dev); + long val = simple_strtol(buf, NULL, 10); + + data->vrm = val; + + return count; +} + +static ssize_t show_vid(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct vt1211_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); +} + +static ssize_t show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vt1211_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + +static ssize_t show_alarms(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vt1211_data *data = vt1211_update_device(dev); + + return sprintf(buf, "%d\n", data->alarms); +} + +/* --------------------------------------------------------------------- + * Device attribute structs + * --------------------------------------------------------------------- */ + +#define SENSOR_ATTR_IN_INPUT(ix) \ + SENSOR_ATTR_2(in##ix##_input, S_IRUGO, \ + show_in, NULL, SHOW_IN_INPUT, ix) + +static struct sensor_device_attribute_2 vt1211_sysfs_in_input[] = { + SENSOR_ATTR_IN_INPUT(0), + SENSOR_ATTR_IN_INPUT(1), + SENSOR_ATTR_IN_INPUT(2), + SENSOR_ATTR_IN_INPUT(3), + SENSOR_ATTR_IN_INPUT(4), + SENSOR_ATTR_IN_INPUT(5), +}; + +#define SENSOR_ATTR_IN_MIN(ix) \ + SENSOR_ATTR_2(in##ix##_min, S_IRUGO | S_IWUSR, \ + show_in, set_in, SHOW_SET_IN_MIN, ix) + +static struct sensor_device_attribute_2 vt1211_sysfs_in_min[] = { + SENSOR_ATTR_IN_MIN(0), + SENSOR_ATTR_IN_MIN(1), + SENSOR_ATTR_IN_MIN(2), + SENSOR_ATTR_IN_MIN(3), + SENSOR_ATTR_IN_MIN(4), + SENSOR_ATTR_IN_MIN(5), +}; + +#define SENSOR_ATTR_IN_MAX(ix) \ + SENSOR_ATTR_2(in##ix##_max, S_IRUGO | S_IWUSR, \ + show_in, set_in, SHOW_SET_IN_MAX, ix) + +static struct sensor_device_attribute_2 vt1211_sysfs_in_max[] = { + SENSOR_ATTR_IN_MAX(0), + SENSOR_ATTR_IN_MAX(1), + SENSOR_ATTR_IN_MAX(2), + SENSOR_ATTR_IN_MAX(3), + SENSOR_ATTR_IN_MAX(4), + SENSOR_ATTR_IN_MAX(5), +}; + +#define SENSOR_ATTR_IN_ALARM(ix) \ + SENSOR_ATTR_2(in##ix##_alarm, S_IRUGO, \ + show_in, NULL, SHOW_IN_ALARM, ix) + +static struct sensor_device_attribute_2 vt1211_sysfs_in_alarm[] = { + SENSOR_ATTR_IN_ALARM(0), + SENSOR_ATTR_IN_ALARM(1), + SENSOR_ATTR_IN_ALARM(2), + SENSOR_ATTR_IN_ALARM(3), + SENSOR_ATTR_IN_ALARM(4), + SENSOR_ATTR_IN_ALARM(5), +}; + +#define SENSOR_ATTR_TEMP_INPUT(ix) \ + SENSOR_ATTR_2(temp##ix##_input, S_IRUGO, \ + show_temp, NULL, SHOW_TEMP_INPUT, ix-1) + +static struct sensor_device_attribute_2 vt1211_sysfs_temp_input[] = { + SENSOR_ATTR_TEMP_INPUT(1), + SENSOR_ATTR_TEMP_INPUT(2), + SENSOR_ATTR_TEMP_INPUT(3), + SENSOR_ATTR_TEMP_INPUT(4), + SENSOR_ATTR_TEMP_INPUT(5), + SENSOR_ATTR_TEMP_INPUT(6), + SENSOR_ATTR_TEMP_INPUT(7), +}; + +#define SENSOR_ATTR_TEMP_MAX(ix) \ + SENSOR_ATTR_2(temp##ix##_max, S_IRUGO | S_IWUSR, \ + show_temp, set_temp, SHOW_SET_TEMP_MAX, ix-1) + +static struct sensor_device_attribute_2 vt1211_sysfs_temp_max[] = { + SENSOR_ATTR_TEMP_MAX(1), + SENSOR_ATTR_TEMP_MAX(2), + SENSOR_ATTR_TEMP_MAX(3), + SENSOR_ATTR_TEMP_MAX(4), + SENSOR_ATTR_TEMP_MAX(5), + SENSOR_ATTR_TEMP_MAX(6), + SENSOR_ATTR_TEMP_MAX(7), +}; + +#define SENSOR_ATTR_TEMP_MAX_HYST(ix) \ + SENSOR_ATTR_2(temp##ix##_max_hyst, S_IRUGO | S_IWUSR, \ + show_temp, set_temp, SHOW_SET_TEMP_MAX_HYST, ix-1) + +static struct sensor_device_attribute_2 vt1211_sysfs_temp_max_hyst[] = { + SENSOR_ATTR_TEMP_MAX_HYST(1), + SENSOR_ATTR_TEMP_MAX_HYST(2), + SENSOR_ATTR_TEMP_MAX_HYST(3), + SENSOR_ATTR_TEMP_MAX_HYST(4), + SENSOR_ATTR_TEMP_MAX_HYST(5), + SENSOR_ATTR_TEMP_MAX_HYST(6), + SENSOR_ATTR_TEMP_MAX_HYST(7), +}; + +#define SENSOR_ATTR_TEMP_ALARM(ix) \ + SENSOR_ATTR_2(temp##ix##_alarm, S_IRUGO, \ + show_temp, NULL, SHOW_TEMP_ALARM, ix-1) + +static struct sensor_device_attribute_2 vt1211_sysfs_temp_alarm[] = { + SENSOR_ATTR_TEMP_ALARM(1), + SENSOR_ATTR_TEMP_ALARM(2), + SENSOR_ATTR_TEMP_ALARM(3), + SENSOR_ATTR_TEMP_ALARM(4), + SENSOR_ATTR_TEMP_ALARM(5), + SENSOR_ATTR_TEMP_ALARM(6), + SENSOR_ATTR_TEMP_ALARM(7), +}; + +#define SENSOR_ATTR_FAN(ix) \ + SENSOR_ATTR_2(fan##ix##_input, S_IRUGO, \ + show_fan, NULL, SHOW_FAN_INPUT, ix-1), \ + SENSOR_ATTR_2(fan##ix##_min, S_IRUGO | S_IWUSR, \ + show_fan, set_fan, SHOW_SET_FAN_MIN, ix-1), \ + SENSOR_ATTR_2(fan##ix##_div, S_IRUGO | S_IWUSR, \ + show_fan, set_fan, SHOW_SET_FAN_DIV, ix-1), \ + SENSOR_ATTR_2(fan##ix##_alarm, S_IRUGO, \ + show_fan, NULL, SHOW_FAN_ALARM, ix-1) + +#define SENSOR_ATTR_PWM(ix) \ + SENSOR_ATTR_2(pwm##ix, S_IRUGO, \ + show_pwm, NULL, SHOW_PWM, ix-1), \ + SENSOR_ATTR_2(pwm##ix##_enable, S_IRUGO | S_IWUSR, \ + show_pwm, set_pwm, SHOW_SET_PWM_ENABLE, ix-1), \ + SENSOR_ATTR_2(pwm##ix##_auto_channels_temp, S_IRUGO | S_IWUSR, \ + show_pwm, set_pwm, SHOW_SET_PWM_AUTO_CHANNELS_TEMP, ix-1) + +#define SENSOR_ATTR_PWM_FREQ(ix) \ + SENSOR_ATTR_2(pwm##ix##_freq, S_IRUGO | S_IWUSR, \ + show_pwm, set_pwm, SHOW_SET_PWM_FREQ, ix-1) + +#define SENSOR_ATTR_PWM_FREQ_RO(ix) \ + SENSOR_ATTR_2(pwm##ix##_freq, S_IRUGO, \ + show_pwm, NULL, SHOW_SET_PWM_FREQ, ix-1) + +#define SENSOR_ATTR_PWM_AUTO_POINT_TEMP(ix, ap) \ + SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_temp, S_IRUGO | S_IWUSR, \ + show_pwm_auto_point_temp, set_pwm_auto_point_temp, \ + ap-1, ix-1) + +#define SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(ix, ap) \ + SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_temp, S_IRUGO, \ + show_pwm_auto_point_temp, NULL, \ + ap-1, ix-1) + +#define SENSOR_ATTR_PWM_AUTO_POINT_PWM(ix, ap) \ + SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_pwm, S_IRUGO | S_IWUSR, \ + show_pwm_auto_point_pwm, set_pwm_auto_point_pwm, \ + ap-1, ix-1) + +#define SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(ix, ap) \ + SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_pwm, S_IRUGO, \ + show_pwm_auto_point_pwm, NULL, \ + ap-1, ix-1) + +static struct sensor_device_attribute_2 vt1211_sysfs_fan_pwm[] = { + SENSOR_ATTR_FAN(1), + SENSOR_ATTR_FAN(2), + SENSOR_ATTR_PWM(1), + SENSOR_ATTR_PWM(2), + SENSOR_ATTR_PWM_FREQ(1), + SENSOR_ATTR_PWM_FREQ_RO(2), + SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 1), + SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 2), + SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 3), + SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 4), + SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 1), + SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 2), + SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 3), + SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 4), + SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(1, 1), + SENSOR_ATTR_PWM_AUTO_POINT_PWM(1, 2), + SENSOR_ATTR_PWM_AUTO_POINT_PWM(1, 3), + SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(1, 4), + SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(2, 1), + SENSOR_ATTR_PWM_AUTO_POINT_PWM(2, 2), + SENSOR_ATTR_PWM_AUTO_POINT_PWM(2, 3), + SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(2, 4), +}; + +static struct device_attribute vt1211_sysfs_misc[] = { + __ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm), + __ATTR(cpu0_vid, S_IRUGO, show_vid, NULL), + __ATTR(name, S_IRUGO, show_name, NULL), + __ATTR(alarms, S_IRUGO, show_alarms, NULL), +}; + +/* --------------------------------------------------------------------- + * Device registration and initialization + * --------------------------------------------------------------------- */ + +static void __devinit vt1211_init_device(struct vt1211_data *data) +{ + /* set VRM */ + data->vrm = vid_which_vrm(); + + /* Read (and initialize) UCH config */ + data->uch_config = vt1211_read8(data, VT1211_REG_UCH_CONFIG); + if (uch_config > -1) { + data->uch_config = (data->uch_config & 0x83) | + (uch_config << 2); + vt1211_write8(data, VT1211_REG_UCH_CONFIG, data->uch_config); + } + + /* Initialize the interrupt mode (if request at module load time). + * The VT1211 implements 3 different modes for clearing interrupts: + * 0: Clear INT when status register is read. Regenerate INT as long + * as temp stays above hysteresis limit. + * 1: Clear INT when status register is read. DON'T regenerate INT + * until temp falls below hysteresis limit and exceeds hot limit + * again. + * 2: Clear INT when temp falls below max limit. + * + * The driver only allows to force mode 0 since that's the only one + * that makes sense for 'sensors' */ + if (int_mode == 0) { + vt1211_write8(data, VT1211_REG_TEMP1_CONFIG, 0); + vt1211_write8(data, VT1211_REG_TEMP2_CONFIG, 0); + } + + /* Fill in some hard wired values into our data struct */ + data->pwm_auto_pwm[0][3] = 255; + data->pwm_auto_pwm[1][3] = 255; +} + +static void vt1211_remove_sysfs(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int i; + + for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_in_input); i++) { + device_remove_file(dev, + &vt1211_sysfs_in_input[i].dev_attr); + device_remove_file(dev, + &vt1211_sysfs_in_min[i].dev_attr); + device_remove_file(dev, + &vt1211_sysfs_in_max[i].dev_attr); + device_remove_file(dev, + &vt1211_sysfs_in_alarm[i].dev_attr); + } + for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_temp_input); i++) { + device_remove_file(dev, + &vt1211_sysfs_temp_input[i].dev_attr); + device_remove_file(dev, + &vt1211_sysfs_temp_max[i].dev_attr); + device_remove_file(dev, + &vt1211_sysfs_temp_max_hyst[i].dev_attr); + device_remove_file(dev, + &vt1211_sysfs_temp_alarm[i].dev_attr); + } + for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) { + device_remove_file(dev, + &vt1211_sysfs_fan_pwm[i].dev_attr); + } + for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++) { + device_remove_file(dev, &vt1211_sysfs_misc[i]); + } +} + +static int __devinit vt1211_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct vt1211_data *data; + struct resource *res; + int i, err; + + if (!(data = kzalloc(sizeof(struct vt1211_data), GFP_KERNEL))) { + err = -ENOMEM; + dev_err(dev, "Out of memory\n"); + goto EXIT; + } + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + data->addr = res->start; + data->name = DRVNAME; + mutex_init(&data->update_lock); + + platform_set_drvdata(pdev, data); + + /* Initialize the VT1211 chip */ + vt1211_init_device(data); + + /* Create sysfs interface files */ + for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_in_input); i++) { + if (ISVOLT(i, data->uch_config)) { + if ((err = device_create_file(dev, + &vt1211_sysfs_in_input[i].dev_attr)) || + (err = device_create_file(dev, + &vt1211_sysfs_in_min[i].dev_attr)) || + (err = device_create_file(dev, + &vt1211_sysfs_in_max[i].dev_attr)) || + (err = device_create_file(dev, + &vt1211_sysfs_in_alarm[i].dev_attr))) { + goto EXIT_DEV_REMOVE; + } + } + } + for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_temp_input); i++) { + if (ISTEMP(i, data->uch_config)) { + if ((err = device_create_file(dev, + &vt1211_sysfs_temp_input[i].dev_attr)) || + (err = device_create_file(dev, + &vt1211_sysfs_temp_max[i].dev_attr)) || + (err = device_create_file(dev, + &vt1211_sysfs_temp_max_hyst[i].dev_attr)) || + (err = device_create_file(dev, + &vt1211_sysfs_temp_alarm[i].dev_attr))) { + goto EXIT_DEV_REMOVE; + } + } + } + for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) { + err = device_create_file(dev, + &vt1211_sysfs_fan_pwm[i].dev_attr); + if (err) { + goto EXIT_DEV_REMOVE; + } + } + for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++) { + err = device_create_file(dev, + &vt1211_sysfs_misc[i]); + if (err) { + goto EXIT_DEV_REMOVE; + } + } + + /* Register device */ + data->class_dev = hwmon_device_register(dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + dev_err(dev, "Class registration failed (%d)\n", err); + goto EXIT_DEV_REMOVE_SILENT; + } + + return 0; + +EXIT_DEV_REMOVE: + dev_err(dev, "Sysfs interface creation failed (%d)\n", err); +EXIT_DEV_REMOVE_SILENT: + vt1211_remove_sysfs(pdev); + platform_set_drvdata(pdev, NULL); + kfree(data); +EXIT: + return err; +} + +static int __devexit vt1211_remove(struct platform_device *pdev) +{ + struct vt1211_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->class_dev); + vt1211_remove_sysfs(pdev); + platform_set_drvdata(pdev, NULL); + kfree(data); + + return 0; +} + +static struct platform_driver vt1211_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, + .probe = vt1211_probe, + .remove = __devexit_p(vt1211_remove), +}; + +static int __init vt1211_device_add(unsigned short address) +{ + struct resource res = { + .start = address, + .end = address + 0x7f, + .flags = IORESOURCE_IO, + }; + int err; + + pdev = platform_device_alloc(DRVNAME, address); + if (!pdev) { + err = -ENOMEM; + printk(KERN_ERR DRVNAME ": Device allocation failed (%d)\n", + err); + goto EXIT; + } + + res.name = pdev->name; + err = platform_device_add_resources(pdev, &res, 1); + if (err) { + printk(KERN_ERR DRVNAME ": Device resource addition failed " + "(%d)\n", err); + goto EXIT_DEV_PUT; + } + + err = platform_device_add(pdev); + if (err) { + printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", + err); + goto EXIT_DEV_PUT; + } + + return 0; + +EXIT_DEV_PUT: + platform_device_put(pdev); +EXIT: + return err; +} + +static int __init vt1211_find(unsigned short *address) +{ + int err = -ENODEV; + + superio_enter(); + + if (superio_inb(SIO_VT1211_DEVID) != SIO_VT1211_ID) { + goto EXIT; + } + + superio_select(SIO_VT1211_LDN_HWMON); + + if ((superio_inb(SIO_VT1211_ACTIVE) & 1) == 0) { + printk(KERN_WARNING DRVNAME ": HW monitor is disabled, " + "skipping\n"); + goto EXIT; + } + + *address = ((superio_inb(SIO_VT1211_BADDR) << 8) | + (superio_inb(SIO_VT1211_BADDR + 1))) & 0xff00; + if (*address == 0) { + printk(KERN_WARNING DRVNAME ": Base address is not set, " + "skipping\n"); + goto EXIT; + } + + err = 0; + printk(KERN_INFO DRVNAME ": Found VT1211 chip at 0x%04x, " + "revision %u\n", *address, superio_inb(SIO_VT1211_DEVREV)); + +EXIT: + superio_exit(); + return err; +} + +static int __init vt1211_init(void) +{ + int err; + unsigned short address = 0; + + err = vt1211_find(&address); + if (err) { + goto EXIT; + } + + if ((uch_config < -1) || (uch_config > 31)) { + err = -EINVAL; + printk(KERN_WARNING DRVNAME ": Invalid UCH configuration %d. " + "Choose a value between 0 and 31.\n", uch_config); + goto EXIT; + } + + if ((int_mode < -1) || (int_mode > 0)) { + err = -EINVAL; + printk(KERN_WARNING DRVNAME ": Invalid interrupt mode %d. " + "Only mode 0 is supported.\n", int_mode); + goto EXIT; + } + + err = platform_driver_register(&vt1211_driver); + if (err) { + goto EXIT; + } + + /* Sets global pdev as a side effect */ + err = vt1211_device_add(address); + if (err) { + goto EXIT_DRV_UNREGISTER; + } + + return 0; + +EXIT_DRV_UNREGISTER: + platform_driver_unregister(&vt1211_driver); +EXIT: + return err; +} + +static void __exit vt1211_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&vt1211_driver); +} + +MODULE_AUTHOR("Juerg Haefliger <juergh@gmail.com>"); +MODULE_DESCRIPTION("VT1211 sensors"); +MODULE_LICENSE("GPL"); + +module_init(vt1211_init); +module_exit(vt1211_exit); diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c index 236ccf0e915..93f93d4fb8a 100644 --- a/drivers/hwmon/vt8231.c +++ b/drivers/hwmon/vt8231.c @@ -451,37 +451,6 @@ define_temperature_sysfs(4); define_temperature_sysfs(5); define_temperature_sysfs(6); -#define CFG_INFO_TEMP(id) { &sensor_dev_attr_temp##id##_input.dev_attr, \ - &sensor_dev_attr_temp##id##_max_hyst.dev_attr, \ - &sensor_dev_attr_temp##id##_max.dev_attr } -#define CFG_INFO_VOLT(id) { &sensor_dev_attr_in##id##_input.dev_attr, \ - &sensor_dev_attr_in##id##_min.dev_attr, \ - &sensor_dev_attr_in##id##_max.dev_attr } - -struct str_device_attr_table { - struct device_attribute *input; - struct device_attribute *min; - struct device_attribute *max; -}; - -static struct str_device_attr_table cfg_info_temp[] = { - { &dev_attr_temp1_input, &dev_attr_temp1_max_hyst, &dev_attr_temp1_max }, - CFG_INFO_TEMP(2), - CFG_INFO_TEMP(3), - CFG_INFO_TEMP(4), - CFG_INFO_TEMP(5), - CFG_INFO_TEMP(6) -}; - -static struct str_device_attr_table cfg_info_volt[] = { - CFG_INFO_VOLT(0), - CFG_INFO_VOLT(1), - CFG_INFO_VOLT(2), - CFG_INFO_VOLT(3), - CFG_INFO_VOLT(4), - { &dev_attr_in5_input, &dev_attr_in5_min, &dev_attr_in5_max } -}; - /* Fans */ static ssize_t show_fan(struct device *dev, struct device_attribute *attr, char *buf) @@ -585,6 +554,107 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static struct attribute *vt8231_attributes_temps[6][4] = { + { + &dev_attr_temp1_input.attr, + &dev_attr_temp1_max_hyst.attr, + &dev_attr_temp1_max.attr, + NULL + }, { + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp5_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp6_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp6_max.dev_attr.attr, + NULL + } +}; + +static const struct attribute_group vt8231_group_temps[6] = { + { .attrs = vt8231_attributes_temps[0] }, + { .attrs = vt8231_attributes_temps[1] }, + { .attrs = vt8231_attributes_temps[2] }, + { .attrs = vt8231_attributes_temps[3] }, + { .attrs = vt8231_attributes_temps[4] }, + { .attrs = vt8231_attributes_temps[5] }, +}; + +static struct attribute *vt8231_attributes_volts[6][4] = { + { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + NULL + }, { + &dev_attr_in5_input.attr, + &dev_attr_in5_min.attr, + &dev_attr_in5_max.attr, + NULL + } +}; + +static const struct attribute_group vt8231_group_volts[6] = { + { .attrs = vt8231_attributes_volts[0] }, + { .attrs = vt8231_attributes_volts[1] }, + { .attrs = vt8231_attributes_volts[2] }, + { .attrs = vt8231_attributes_volts[3] }, + { .attrs = vt8231_attributes_volts[4] }, + { .attrs = vt8231_attributes_volts[5] }, +}; + +static struct attribute *vt8231_attributes[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan1_div.dev_attr.attr, + &sensor_dev_attr_fan2_div.dev_attr.attr, + &dev_attr_alarms.attr, + NULL +}; + +static const struct attribute_group vt8231_group = { + .attrs = vt8231_attributes, +}; + static struct i2c_driver vt8231_driver = { .driver = { .owner = THIS_MODULE, @@ -671,43 +741,43 @@ int vt8231_detect(struct i2c_adapter *adapter) vt8231_init_client(client); /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); + if ((err = sysfs_create_group(&client->dev.kobj, &vt8231_group))) goto exit_detach; - } /* Must update device information to find out the config field */ data->uch_config = vt8231_read_value(client, VT8231_REG_UCH_CONFIG); - for (i = 0; i < ARRAY_SIZE(cfg_info_temp); i++) { + for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++) { if (ISTEMP(i, data->uch_config)) { - device_create_file(&client->dev, - cfg_info_temp[i].input); - device_create_file(&client->dev, cfg_info_temp[i].max); - device_create_file(&client->dev, cfg_info_temp[i].min); + if ((err = sysfs_create_group(&client->dev.kobj, + &vt8231_group_temps[i]))) + goto exit_remove_files; } } - for (i = 0; i < ARRAY_SIZE(cfg_info_volt); i++) { + for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++) { if (ISVOLT(i, data->uch_config)) { - device_create_file(&client->dev, - cfg_info_volt[i].input); - device_create_file(&client->dev, cfg_info_volt[i].max); - device_create_file(&client->dev, cfg_info_volt[i].min); + if ((err = sysfs_create_group(&client->dev.kobj, + &vt8231_group_volts[i]))) + goto exit_remove_files; } } - device_create_file(&client->dev, &sensor_dev_attr_fan1_input.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_fan2_input.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_fan1_min.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_fan2_min.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_fan1_div.dev_attr); - device_create_file(&client->dev, &sensor_dev_attr_fan2_div.dev_attr); - - device_create_file(&client->dev, &dev_attr_alarms); + data->class_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove_files; + } return 0; +exit_remove_files: + for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++) + sysfs_remove_group(&client->dev.kobj, &vt8231_group_volts[i]); + + for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++) + sysfs_remove_group(&client->dev.kobj, &vt8231_group_temps[i]); + + sysfs_remove_group(&client->dev.kobj, &vt8231_group); exit_detach: i2c_detach_client(client); exit_free: @@ -720,10 +790,18 @@ exit_release: static int vt8231_detach_client(struct i2c_client *client) { struct vt8231_data *data = i2c_get_clientdata(client); - int err; + int err, i; hwmon_device_unregister(data->class_dev); + for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++) + sysfs_remove_group(&client->dev.kobj, &vt8231_group_volts[i]); + + for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++) + sysfs_remove_group(&client->dev.kobj, &vt8231_group_temps[i]); + + sysfs_remove_group(&client->dev.kobj, &vt8231_group); + if ((err = i2c_detach_client(client))) { return err; } diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index b21d6b9d7ea..833faa275ff 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -2,6 +2,9 @@ w83627ehf - Driver for the hardware monitoring functionality of the Winbond W83627EHF Super-I/O chip Copyright (C) 2005 Jean Delvare <khali@linux-fr.org> + Copyright (C) 2006 Yuan Mu (Winbond), + Rudolf Marek <r.marek@sh.cvut.cz> + David Hubbard <david.c.hubbard@gmail.com> Shamelessly ripped from the w83627hf driver Copyright (C) 2003 Mark Studebaker @@ -29,8 +32,8 @@ Supports the following chips: - Chip #vin #fan #pwm #temp chip_id man_id - w83627ehf 10 5 - 3 0x88 0x5ca3 + Chip #vin #fan #pwm #temp chip_id man_id + w83627ehf 10 5 4 3 0x88,0xa1 0x5ca3 */ #include <linux/module.h> @@ -145,10 +148,44 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 }; #define W83627EHF_REG_ALARM2 0x45A #define W83627EHF_REG_ALARM3 0x45B +/* SmartFan registers */ +/* DC or PWM output fan configuration */ +static const u8 W83627EHF_REG_PWM_ENABLE[] = { + 0x04, /* SYS FAN0 output mode and PWM mode */ + 0x04, /* CPU FAN0 output mode and PWM mode */ + 0x12, /* AUX FAN mode */ + 0x62, /* CPU fan1 mode */ +}; + +static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 }; +static const u8 W83627EHF_PWM_ENABLE_SHIFT[] = { 2, 4, 1, 4 }; + +/* FAN Duty Cycle, be used to control */ +static const u8 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 }; +static const u8 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 }; +static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 }; + + +/* Advanced Fan control, some values are common for all fans */ +static const u8 W83627EHF_REG_FAN_MIN_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 }; +static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0C, 0x0D, 0x17, 0x66 }; + /* * Conversions */ +/* 1 is PWM mode, output in ms */ +static inline unsigned int step_time_from_reg(u8 reg, u8 mode) +{ + return mode ? 100 * reg : 400 * reg; +} + +static inline u8 step_time_to_reg(unsigned int msec, u8 mode) +{ + return SENSORS_LIMIT((mode ? (msec + 50) / 100 : + (msec + 200) / 400), 1, 255); +} + static inline unsigned int fan_from_reg(u8 reg, unsigned int div) { @@ -170,12 +207,12 @@ temp1_from_reg(s8 reg) } static inline s8 -temp1_to_reg(int temp) +temp1_to_reg(int temp, int min, int max) { - if (temp <= -128000) - return -128; - if (temp >= 127000) - return 127; + if (temp <= min) + return min / 1000; + if (temp >= max) + return max / 1000; if (temp < 0) return (temp - 500) / 1000; return (temp + 500) / 1000; @@ -223,6 +260,16 @@ struct w83627ehf_data { s16 temp_max[2]; s16 temp_max_hyst[2]; u32 alarms; + + u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */ + u8 pwm_enable[4]; /* 1->manual + 2->thermal cruise (also called SmartFan I) */ + u8 pwm[4]; + u8 target_temp[4]; + u8 tolerance[4]; + + u8 fan_min_output[4]; /* minimum fan speed */ + u8 fan_stop_time[4]; }; static inline int is_word_sized(u16 reg) @@ -349,6 +396,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct w83627ehf_data *data = i2c_get_clientdata(client); + int pwmcfg = 0, tolerance = 0; /* shut up the compiler */ int i; mutex_lock(&data->update_lock); @@ -416,6 +464,34 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) } } + for (i = 0; i < 4; i++) { + /* pwmcfg, tolarance mapped for i=0, i=1 to same reg */ + if (i != 1) { + pwmcfg = w83627ehf_read_value(client, + W83627EHF_REG_PWM_ENABLE[i]); + tolerance = w83627ehf_read_value(client, + W83627EHF_REG_TOLERANCE[i]); + } + data->pwm_mode[i] = + ((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1) + ? 0 : 1; + data->pwm_enable[i] = + ((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i]) + & 3) + 1; + data->pwm[i] = w83627ehf_read_value(client, + W83627EHF_REG_PWM[i]); + data->fan_min_output[i] = w83627ehf_read_value(client, + W83627EHF_REG_FAN_MIN_OUTPUT[i]); + data->fan_stop_time[i] = w83627ehf_read_value(client, + W83627EHF_REG_FAN_STOP_TIME[i]); + data->target_temp[i] = + w83627ehf_read_value(client, + W83627EHF_REG_TARGET[i]) & + (data->pwm_mode[i] == 1 ? 0x7f : 0xff); + data->tolerance[i] = (tolerance >> (i == 1 ? 4 : 0)) + & 0x0f; + } + /* Measured temperatures and limits */ data->temp1 = w83627ehf_read_value(client, W83627EHF_REG_TEMP1); @@ -546,14 +622,6 @@ static struct sensor_device_attribute sda_in_max[] = { SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9), }; -static void device_create_file_in(struct device *dev, int i) -{ - device_create_file(dev, &sda_in_input[i].dev_attr); - device_create_file(dev, &sda_in_alarm[i].dev_attr); - device_create_file(dev, &sda_in_min[i].dev_attr); - device_create_file(dev, &sda_in_max[i].dev_attr); -} - #define show_fan_reg(reg) \ static ssize_t \ show_##reg(struct device *dev, struct device_attribute *attr, \ @@ -681,14 +749,6 @@ static struct sensor_device_attribute sda_fan_div[] = { SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4), }; -static void device_create_file_fan(struct device *dev, int i) -{ - device_create_file(dev, &sda_fan_input[i].dev_attr); - device_create_file(dev, &sda_fan_alarm[i].dev_attr); - device_create_file(dev, &sda_fan_div[i].dev_attr); - device_create_file(dev, &sda_fan_min[i].dev_attr); -} - #define show_temp1_reg(reg) \ static ssize_t \ show_##reg(struct device *dev, struct device_attribute *attr, \ @@ -711,7 +771,7 @@ store_temp1_##reg(struct device *dev, struct device_attribute *attr, \ u32 val = simple_strtoul(buf, NULL, 10); \ \ mutex_lock(&data->update_lock); \ - data->temp1_##reg = temp1_to_reg(val); \ + data->temp1_##reg = temp1_to_reg(val, -128000, 127000); \ w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \ data->temp1_##reg); \ mutex_unlock(&data->update_lock); \ @@ -777,10 +837,309 @@ static struct sensor_device_attribute sda_temp[] = { SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13), }; +#define show_pwm_reg(reg) \ +static ssize_t show_##reg (struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct w83627ehf_data *data = w83627ehf_update_device(dev); \ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + int nr = sensor_attr->index; \ + return sprintf(buf, "%d\n", data->reg[nr]); \ +} + +show_pwm_reg(pwm_mode) +show_pwm_reg(pwm_enable) +show_pwm_reg(pwm) + +static ssize_t +store_pwm_mode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627ehf_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + u32 val = simple_strtoul(buf, NULL, 10); + u16 reg; + + if (val > 1) + return -EINVAL; + mutex_lock(&data->update_lock); + reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]); + data->pwm_mode[nr] = val; + reg &= ~(1 << W83627EHF_PWM_MODE_SHIFT[nr]); + if (!val) + reg |= 1 << W83627EHF_PWM_MODE_SHIFT[nr]; + w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +store_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627ehf_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255); + + mutex_lock(&data->update_lock); + data->pwm[nr] = val; + w83627ehf_write_value(client, W83627EHF_REG_PWM[nr], val); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +store_pwm_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627ehf_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + u32 val = simple_strtoul(buf, NULL, 10); + u16 reg; + + if (!val || (val > 2)) /* only modes 1 and 2 are supported */ + return -EINVAL; + mutex_lock(&data->update_lock); + reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]); + data->pwm_enable[nr] = val; + reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]); + reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr]; + w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg); + mutex_unlock(&data->update_lock); + return count; +} + + +#define show_tol_temp(reg) \ +static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct w83627ehf_data *data = w83627ehf_update_device(dev); \ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + int nr = sensor_attr->index; \ + return sprintf(buf, "%d\n", temp1_from_reg(data->reg[nr])); \ +} + +show_tol_temp(tolerance) +show_tol_temp(target_temp) + +static ssize_t +store_target_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627ehf_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 127000); + + mutex_lock(&data->update_lock); + data->target_temp[nr] = val; + w83627ehf_write_value(client, W83627EHF_REG_TARGET[nr], val); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +store_tolerance(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627ehf_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + u16 reg; + /* Limit the temp to 0C - 15C */ + u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 15000); + + mutex_lock(&data->update_lock); + reg = w83627ehf_read_value(client, W83627EHF_REG_TOLERANCE[nr]); + data->tolerance[nr] = val; + if (nr == 1) + reg = (reg & 0x0f) | (val << 4); + else + reg = (reg & 0xf0) | val; + w83627ehf_write_value(client, W83627EHF_REG_TOLERANCE[nr], reg); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_pwm[] = { + SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0), + SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1), + SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2), + SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3), +}; + +static struct sensor_device_attribute sda_pwm_mode[] = { + SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 0), + SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 1), + SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 2), + SENSOR_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 3), +}; + +static struct sensor_device_attribute sda_pwm_enable[] = { + SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 0), + SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 1), + SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 2), + SENSOR_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 3), +}; + +static struct sensor_device_attribute sda_target_temp[] = { + SENSOR_ATTR(pwm1_target, S_IWUSR | S_IRUGO, show_target_temp, + store_target_temp, 0), + SENSOR_ATTR(pwm2_target, S_IWUSR | S_IRUGO, show_target_temp, + store_target_temp, 1), + SENSOR_ATTR(pwm3_target, S_IWUSR | S_IRUGO, show_target_temp, + store_target_temp, 2), + SENSOR_ATTR(pwm4_target, S_IWUSR | S_IRUGO, show_target_temp, + store_target_temp, 3), +}; + +static struct sensor_device_attribute sda_tolerance[] = { + SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO, show_tolerance, + store_tolerance, 0), + SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO, show_tolerance, + store_tolerance, 1), + SENSOR_ATTR(pwm3_tolerance, S_IWUSR | S_IRUGO, show_tolerance, + store_tolerance, 2), + SENSOR_ATTR(pwm4_tolerance, S_IWUSR | S_IRUGO, show_tolerance, + store_tolerance, 3), +}; + +/* Smart Fan registers */ + +#define fan_functions(reg, REG) \ +static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct w83627ehf_data *data = w83627ehf_update_device(dev); \ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + int nr = sensor_attr->index; \ + return sprintf(buf, "%d\n", data->reg[nr]); \ +}\ +static ssize_t \ +store_##reg(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{\ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83627ehf_data *data = i2c_get_clientdata(client); \ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + int nr = sensor_attr->index; \ + u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \ + mutex_lock(&data->update_lock); \ + data->reg[nr] = val; \ + w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \ + mutex_unlock(&data->update_lock); \ + return count; \ +} + +fan_functions(fan_min_output, FAN_MIN_OUTPUT) + +#define fan_time_functions(reg, REG) \ +static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct w83627ehf_data *data = w83627ehf_update_device(dev); \ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + int nr = sensor_attr->index; \ + return sprintf(buf, "%d\n", \ + step_time_from_reg(data->reg[nr], data->pwm_mode[nr])); \ +} \ +\ +static ssize_t \ +store_##reg(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83627ehf_data *data = i2c_get_clientdata(client); \ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + int nr = sensor_attr->index; \ + u8 val = step_time_to_reg(simple_strtoul(buf, NULL, 10), \ + data->pwm_mode[nr]); \ + mutex_lock(&data->update_lock); \ + data->reg[nr] = val; \ + w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \ + mutex_unlock(&data->update_lock); \ + return count; \ +} \ + +fan_time_functions(fan_stop_time, FAN_STOP_TIME) + + +static struct sensor_device_attribute sda_sf3_arrays_fan4[] = { + SENSOR_ATTR(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, + store_fan_stop_time, 3), + SENSOR_ATTR(pwm4_min_output, S_IWUSR | S_IRUGO, show_fan_min_output, + store_fan_min_output, 3), +}; + +static struct sensor_device_attribute sda_sf3_arrays[] = { + SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, + store_fan_stop_time, 0), + SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, + store_fan_stop_time, 1), + SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, + store_fan_stop_time, 2), + SENSOR_ATTR(pwm1_min_output, S_IWUSR | S_IRUGO, show_fan_min_output, + store_fan_min_output, 0), + SENSOR_ATTR(pwm2_min_output, S_IWUSR | S_IRUGO, show_fan_min_output, + store_fan_min_output, 1), + SENSOR_ATTR(pwm3_min_output, S_IWUSR | S_IRUGO, show_fan_min_output, + store_fan_min_output, 2), +}; + /* * Driver and client management */ +static void w83627ehf_device_remove_files(struct device *dev) +{ + /* some entries in the following arrays may not have been used in + * device_create_file(), but device_remove_file() will ignore them */ + int i; + + for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++) + device_remove_file(dev, &sda_sf3_arrays[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) + device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr); + for (i = 0; i < 10; i++) { + device_remove_file(dev, &sda_in_input[i].dev_attr); + device_remove_file(dev, &sda_in_alarm[i].dev_attr); + device_remove_file(dev, &sda_in_min[i].dev_attr); + device_remove_file(dev, &sda_in_max[i].dev_attr); + } + for (i = 0; i < 5; i++) { + device_remove_file(dev, &sda_fan_input[i].dev_attr); + device_remove_file(dev, &sda_fan_alarm[i].dev_attr); + device_remove_file(dev, &sda_fan_div[i].dev_attr); + device_remove_file(dev, &sda_fan_min[i].dev_attr); + } + for (i = 0; i < 4; i++) { + device_remove_file(dev, &sda_pwm[i].dev_attr); + device_remove_file(dev, &sda_pwm_mode[i].dev_attr); + device_remove_file(dev, &sda_pwm_enable[i].dev_attr); + device_remove_file(dev, &sda_target_temp[i].dev_attr); + device_remove_file(dev, &sda_tolerance[i].dev_attr); + } + for (i = 0; i < ARRAY_SIZE(sda_temp); i++) + device_remove_file(dev, &sda_temp[i].dev_attr); +} + static struct i2c_driver w83627ehf_driver; static void w83627ehf_init_client(struct i2c_client *client) @@ -810,6 +1169,7 @@ static int w83627ehf_detect(struct i2c_adapter *adapter) struct i2c_client *client; struct w83627ehf_data *data; struct device *dev; + u8 fan4pin, fan5pin; int i, err = 0; if (!request_region(address + REGION_OFFSET, REGION_LENGTH, @@ -848,35 +1208,87 @@ static int w83627ehf_detect(struct i2c_adapter *adapter) data->fan_min[i] = w83627ehf_read_value(client, W83627EHF_REG_FAN_MIN[i]); + /* fan4 and fan5 share some pins with the GPIO and serial flash */ + + superio_enter(); + fan5pin = superio_inb(0x24) & 0x2; + fan4pin = superio_inb(0x29) & 0x6; + superio_exit(); + /* It looks like fan4 and fan5 pins can be alternatively used as fan on/off switches */ + data->has_fan = 0x07; /* fan1, fan2 and fan3 */ i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1); - if (i & (1 << 2)) + if ((i & (1 << 2)) && (!fan4pin)) data->has_fan |= (1 << 3); - if (i & (1 << 0)) + if ((i & (1 << 0)) && (!fan5pin)) data->has_fan |= (1 << 4); /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); - goto exit_detach; - } + for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++) + if ((err = device_create_file(dev, + &sda_sf3_arrays[i].dev_attr))) + goto exit_remove; + + /* if fan4 is enabled create the sf3 files for it */ + if (data->has_fan & (1 << 3)) + for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) { + if ((err = device_create_file(dev, + &sda_sf3_arrays_fan4[i].dev_attr))) + goto exit_remove; + } for (i = 0; i < 10; i++) - device_create_file_in(dev, i); + if ((err = device_create_file(dev, &sda_in_input[i].dev_attr)) + || (err = device_create_file(dev, + &sda_in_alarm[i].dev_attr)) + || (err = device_create_file(dev, + &sda_in_min[i].dev_attr)) + || (err = device_create_file(dev, + &sda_in_max[i].dev_attr))) + goto exit_remove; for (i = 0; i < 5; i++) { - if (data->has_fan & (1 << i)) - device_create_file_fan(dev, i); + if (data->has_fan & (1 << i)) { + if ((err = device_create_file(dev, + &sda_fan_input[i].dev_attr)) + || (err = device_create_file(dev, + &sda_fan_alarm[i].dev_attr)) + || (err = device_create_file(dev, + &sda_fan_div[i].dev_attr)) + || (err = device_create_file(dev, + &sda_fan_min[i].dev_attr))) + goto exit_remove; + if (i < 4 && /* w83627ehf only has 4 pwm */ + ((err = device_create_file(dev, + &sda_pwm[i].dev_attr)) + || (err = device_create_file(dev, + &sda_pwm_mode[i].dev_attr)) + || (err = device_create_file(dev, + &sda_pwm_enable[i].dev_attr)) + || (err = device_create_file(dev, + &sda_target_temp[i].dev_attr)) + || (err = device_create_file(dev, + &sda_tolerance[i].dev_attr)))) + goto exit_remove; + } } + for (i = 0; i < ARRAY_SIZE(sda_temp); i++) - device_create_file(dev, &sda_temp[i].dev_attr); + if ((err = device_create_file(dev, &sda_temp[i].dev_attr))) + goto exit_remove; + + data->class_dev = hwmon_device_register(dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove; + } return 0; -exit_detach: +exit_remove: + w83627ehf_device_remove_files(dev); i2c_detach_client(client); exit_free: kfree(data); @@ -892,6 +1304,7 @@ static int w83627ehf_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); + w83627ehf_device_remove_files(&client->dev); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 30295028ea9..dfdc29c7712 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -512,13 +512,6 @@ static DEVICE_ATTR(in0_min, S_IRUGO | S_IWUSR, static DEVICE_ATTR(in0_max, S_IRUGO | S_IWUSR, show_regs_in_max0, store_regs_in_max0); -#define device_create_file_in(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_in##offset##_input); \ -device_create_file(&client->dev, &dev_attr_in##offset##_min); \ -device_create_file(&client->dev, &dev_attr_in##offset##_max); \ -} while (0) - #define show_fan_reg(reg) \ static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ { \ @@ -576,12 +569,6 @@ sysfs_fan_min_offset(2); sysfs_fan_offset(3); sysfs_fan_min_offset(3); -#define device_create_file_fan(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_fan##offset##_input); \ -device_create_file(&client->dev, &dev_attr_fan##offset##_min); \ -} while (0) - #define show_temp_reg(reg) \ static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ { \ @@ -656,13 +643,6 @@ sysfs_temp_offsets(1); sysfs_temp_offsets(2); sysfs_temp_offsets(3); -#define device_create_file_temp(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_temp##offset##_input); \ -device_create_file(&client->dev, &dev_attr_temp##offset##_max); \ -device_create_file(&client->dev, &dev_attr_temp##offset##_max_hyst); \ -} while (0) - static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) { @@ -670,8 +650,6 @@ show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); -#define device_create_file_vid(client) \ -device_create_file(&client->dev, &dev_attr_cpu0_vid) static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) @@ -692,8 +670,6 @@ store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf return count; } static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); -#define device_create_file_vrm(client) \ -device_create_file(&client->dev, &dev_attr_vrm) static ssize_t show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) @@ -702,8 +678,6 @@ show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%ld\n", (long) data->alarms); } static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); -#define device_create_file_alarms(client) \ -device_create_file(&client->dev, &dev_attr_alarms) #define show_beep_reg(REG, reg) \ static ssize_t show_beep_##reg (struct device *dev, struct device_attribute *attr, char *buf) \ @@ -766,12 +740,6 @@ static DEVICE_ATTR(beep_##reg, S_IRUGO | S_IWUSR, \ sysfs_beep(ENABLE, enable); sysfs_beep(MASK, mask); -#define device_create_file_beep(client) \ -do { \ -device_create_file(&client->dev, &dev_attr_beep_enable); \ -device_create_file(&client->dev, &dev_attr_beep_mask); \ -} while (0) - static ssize_t show_fan_div_reg(struct device *dev, char *buf, int nr) { @@ -837,11 +805,6 @@ sysfs_fan_div(1); sysfs_fan_div(2); sysfs_fan_div(3); -#define device_create_file_fan_div(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_fan##offset##_div); \ -} while (0) - static ssize_t show_pwm_reg(struct device *dev, char *buf, int nr) { @@ -896,11 +859,6 @@ sysfs_pwm(1); sysfs_pwm(2); sysfs_pwm(3); -#define device_create_file_pwm(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_pwm##offset); \ -} while (0) - static ssize_t show_sensor_reg(struct device *dev, char *buf, int nr) { @@ -972,12 +930,6 @@ sysfs_sensor(1); sysfs_sensor(2); sysfs_sensor(3); -#define device_create_file_sensor(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_temp##offset##_type); \ -} while (0) - - static int __init w83627hf_find(int sioaddr, unsigned short *addr) { u16 val; @@ -1009,6 +961,85 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr) return 0; } +static struct attribute *w83627hf_attributes[] = { + &dev_attr_in0_input.attr, + &dev_attr_in0_min.attr, + &dev_attr_in0_max.attr, + &dev_attr_in2_input.attr, + &dev_attr_in2_min.attr, + &dev_attr_in2_max.attr, + &dev_attr_in3_input.attr, + &dev_attr_in3_min.attr, + &dev_attr_in3_max.attr, + &dev_attr_in4_input.attr, + &dev_attr_in4_min.attr, + &dev_attr_in4_max.attr, + &dev_attr_in7_input.attr, + &dev_attr_in7_min.attr, + &dev_attr_in7_max.attr, + &dev_attr_in8_input.attr, + &dev_attr_in8_min.attr, + &dev_attr_in8_max.attr, + + &dev_attr_fan1_input.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan2_input.attr, + &dev_attr_fan2_min.attr, + &dev_attr_fan2_div.attr, + + &dev_attr_temp1_input.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp1_max_hyst.attr, + &dev_attr_temp1_type.attr, + &dev_attr_temp2_input.attr, + &dev_attr_temp2_max.attr, + &dev_attr_temp2_max_hyst.attr, + &dev_attr_temp2_type.attr, + + &dev_attr_alarms.attr, + &dev_attr_beep_enable.attr, + &dev_attr_beep_mask.attr, + + &dev_attr_pwm1.attr, + &dev_attr_pwm2.attr, + + NULL +}; + +static const struct attribute_group w83627hf_group = { + .attrs = w83627hf_attributes, +}; + +static struct attribute *w83627hf_attributes_opt[] = { + &dev_attr_in1_input.attr, + &dev_attr_in1_min.attr, + &dev_attr_in1_max.attr, + &dev_attr_in5_input.attr, + &dev_attr_in5_min.attr, + &dev_attr_in5_max.attr, + &dev_attr_in6_input.attr, + &dev_attr_in6_min.attr, + &dev_attr_in6_max.attr, + + &dev_attr_fan3_input.attr, + &dev_attr_fan3_min.attr, + &dev_attr_fan3_div.attr, + + &dev_attr_temp3_input.attr, + &dev_attr_temp3_max.attr, + &dev_attr_temp3_max_hyst.attr, + &dev_attr_temp3_type.attr, + + &dev_attr_pwm3.attr, + + NULL +}; + +static const struct attribute_group w83627hf_group_opt = { + .attrs = w83627hf_attributes_opt, +}; + static int w83627hf_detect(struct i2c_adapter *adapter) { int val, kind; @@ -1108,62 +1139,72 @@ static int w83627hf_detect(struct i2c_adapter *adapter) data->fan_min[1] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(2)); data->fan_min[2] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(3)); - /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); + /* Register common device attributes */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &w83627hf_group))) goto ERROR3; - } - - device_create_file_in(new_client, 0); - if (kind != w83697hf) - device_create_file_in(new_client, 1); - device_create_file_in(new_client, 2); - device_create_file_in(new_client, 3); - device_create_file_in(new_client, 4); - if (kind == w83627hf || kind == w83697hf) { - device_create_file_in(new_client, 5); - device_create_file_in(new_client, 6); - } - device_create_file_in(new_client, 7); - device_create_file_in(new_client, 8); - - device_create_file_fan(new_client, 1); - device_create_file_fan(new_client, 2); - if (kind != w83697hf) - device_create_file_fan(new_client, 3); - - device_create_file_temp(new_client, 1); - device_create_file_temp(new_client, 2); - if (kind != w83697hf) - device_create_file_temp(new_client, 3); - if (kind != w83697hf && data->vid != 0xff) { - device_create_file_vid(new_client); - device_create_file_vrm(new_client); - } + /* Register chip-specific device attributes */ + if (kind == w83627hf || kind == w83697hf) + if ((err = device_create_file(&new_client->dev, + &dev_attr_in5_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in5_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in5_max)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in6_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in6_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in6_max))) + goto ERROR4; - device_create_file_fan_div(new_client, 1); - device_create_file_fan_div(new_client, 2); if (kind != w83697hf) - device_create_file_fan_div(new_client, 3); - - device_create_file_alarms(new_client); - - device_create_file_beep(new_client); + if ((err = device_create_file(&new_client->dev, + &dev_attr_in1_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in1_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in1_max)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan3_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan3_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan3_div)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp3_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp3_max)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp3_max_hyst)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp3_type))) + goto ERROR4; + + if (kind != w83697hf && data->vid != 0xff) + if ((err = device_create_file(&new_client->dev, + &dev_attr_cpu0_vid)) + || (err = device_create_file(&new_client->dev, + &dev_attr_vrm))) + goto ERROR4; - device_create_file_pwm(new_client, 1); - device_create_file_pwm(new_client, 2); if (kind == w83627thf || kind == w83637hf || kind == w83687thf) - device_create_file_pwm(new_client, 3); + if ((err = device_create_file(&new_client->dev, + &dev_attr_pwm3))) + goto ERROR4; - device_create_file_sensor(new_client, 1); - device_create_file_sensor(new_client, 2); - if (kind != w83697hf) - device_create_file_sensor(new_client, 3); + data->class_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto ERROR4; + } return 0; + ERROR4: + sysfs_remove_group(&new_client->dev.kobj, &w83627hf_group); + sysfs_remove_group(&new_client->dev.kobj, &w83627hf_group_opt); ERROR3: i2c_detach_client(new_client); ERROR2: @@ -1181,6 +1222,9 @@ static int w83627hf_detach_client(struct i2c_client *client) hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &w83627hf_group); + sysfs_remove_group(&client->dev.kobj, &w83627hf_group_opt); + if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 95221b14e13..a4584ec6984 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -41,6 +41,7 @@ #include <linux/i2c-isa.h> #include <linux/hwmon.h> #include <linux/hwmon-vid.h> +#include <linux/sysfs.h> #include <linux/err.h> #include <linux/mutex.h> #include <asm/io.h> @@ -360,13 +361,6 @@ sysfs_in_offsets(6); sysfs_in_offsets(7); sysfs_in_offsets(8); -#define device_create_file_in(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_in##offset##_input); \ -device_create_file(&client->dev, &dev_attr_in##offset##_min); \ -device_create_file(&client->dev, &dev_attr_in##offset##_max); \ -} while (0) - #define show_fan_reg(reg) \ static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ { \ @@ -421,12 +415,6 @@ sysfs_fan_min_offset(2); sysfs_fan_offset(3); sysfs_fan_min_offset(3); -#define device_create_file_fan(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_fan##offset##_input); \ -device_create_file(&client->dev, &dev_attr_fan##offset##_min); \ -} while (0) - #define show_temp_reg(reg) \ static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ { \ @@ -497,13 +485,6 @@ sysfs_temp_offsets(1); sysfs_temp_offsets(2); sysfs_temp_offsets(3); -#define device_create_file_temp(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_temp##offset##_input); \ -device_create_file(&client->dev, &dev_attr_temp##offset##_max); \ -device_create_file(&client->dev, &dev_attr_temp##offset##_max_hyst); \ -} while (0) - static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) { @@ -511,10 +492,8 @@ show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); } -static -DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); -#define device_create_file_vid(client) \ -device_create_file(&client->dev, &dev_attr_cpu0_vid); +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); + static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) { @@ -535,10 +514,8 @@ store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf return count; } -static -DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); -#define device_create_file_vrm(client) \ -device_create_file(&client->dev, &dev_attr_vrm); +static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); + static ssize_t show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) { @@ -546,10 +523,8 @@ show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%u\n", data->alarms); } -static -DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); -#define device_create_file_alarms(client) \ -device_create_file(&client->dev, &dev_attr_alarms); +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); + static ssize_t show_beep_mask (struct device *dev, struct device_attribute *attr, char *buf) { struct w83781d_data *data = w83781d_update_device(dev); @@ -615,12 +590,6 @@ static DEVICE_ATTR(beep_##reg, S_IRUGO | S_IWUSR, show_regs_beep_##reg, store_re sysfs_beep(ENABLE, enable); sysfs_beep(MASK, mask); -#define device_create_file_beep(client) \ -do { \ -device_create_file(&client->dev, &dev_attr_beep_enable); \ -device_create_file(&client->dev, &dev_attr_beep_mask); \ -} while (0) - static ssize_t show_fan_div_reg(struct device *dev, char *buf, int nr) { @@ -686,11 +655,6 @@ sysfs_fan_div(1); sysfs_fan_div(2); sysfs_fan_div(3); -#define device_create_file_fan_div(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_fan##offset##_div); \ -} while (0) - static ssize_t show_pwm_reg(struct device *dev, char *buf, int nr) { @@ -787,16 +751,6 @@ sysfs_pwmenable(2); /* only PWM2 can be enabled/disabled */ sysfs_pwm(3); sysfs_pwm(4); -#define device_create_file_pwm(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_pwm##offset); \ -} while (0) - -#define device_create_file_pwmenable(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_pwm##offset##_enable); \ -} while (0) - static ssize_t show_sensor_reg(struct device *dev, char *buf, int nr) { @@ -865,11 +819,6 @@ sysfs_sensor(1); sysfs_sensor(2); sysfs_sensor(3); -#define device_create_file_sensor(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_temp##offset##_type); \ -} while (0) - /* This function is called when: * w83781d_driver is inserted (when this module is loaded), for each available adapter @@ -994,11 +943,69 @@ ERROR_SC_0: return err; } +#define IN_UNIT_ATTRS(X) \ + &dev_attr_in##X##_input.attr, \ + &dev_attr_in##X##_min.attr, \ + &dev_attr_in##X##_max.attr + +#define FAN_UNIT_ATTRS(X) \ + &dev_attr_fan##X##_input.attr, \ + &dev_attr_fan##X##_min.attr, \ + &dev_attr_fan##X##_div.attr + +#define TEMP_UNIT_ATTRS(X) \ + &dev_attr_temp##X##_input.attr, \ + &dev_attr_temp##X##_max.attr, \ + &dev_attr_temp##X##_max_hyst.attr + +static struct attribute* w83781d_attributes[] = { + IN_UNIT_ATTRS(0), + IN_UNIT_ATTRS(2), + IN_UNIT_ATTRS(3), + IN_UNIT_ATTRS(4), + IN_UNIT_ATTRS(5), + IN_UNIT_ATTRS(6), + FAN_UNIT_ATTRS(1), + FAN_UNIT_ATTRS(2), + FAN_UNIT_ATTRS(3), + TEMP_UNIT_ATTRS(1), + TEMP_UNIT_ATTRS(2), + &dev_attr_cpu0_vid.attr, + &dev_attr_vrm.attr, + &dev_attr_alarms.attr, + &dev_attr_beep_mask.attr, + &dev_attr_beep_enable.attr, + NULL +}; +static const struct attribute_group w83781d_group = { + .attrs = w83781d_attributes, +}; + +static struct attribute *w83781d_attributes_opt[] = { + IN_UNIT_ATTRS(1), + IN_UNIT_ATTRS(7), + IN_UNIT_ATTRS(8), + TEMP_UNIT_ATTRS(3), + &dev_attr_pwm1.attr, + &dev_attr_pwm2.attr, + &dev_attr_pwm2_enable.attr, + &dev_attr_pwm3.attr, + &dev_attr_pwm4.attr, + &dev_attr_temp1_type.attr, + &dev_attr_temp2_type.attr, + &dev_attr_temp3_type.attr, + NULL +}; +static const struct attribute_group w83781d_group_opt = { + .attrs = w83781d_attributes_opt, +}; + static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind) { int i = 0, val1 = 0, val2; - struct i2c_client *new_client; + struct i2c_client *client; + struct device *dev; struct w83781d_data *data; int err; const char *client_name = ""; @@ -1075,13 +1082,14 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) goto ERROR1; } - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; + client = &data->client; + i2c_set_clientdata(client, data); + client->addr = address; mutex_init(&data->lock); - new_client->adapter = adapter; - new_client->driver = is_isa ? &w83781d_isa_driver : &w83781d_driver; - new_client->flags = 0; + client->adapter = adapter; + client->driver = is_isa ? &w83781d_isa_driver : &w83781d_driver; + client->flags = 0; + dev = &client->dev; /* Now, we do the remaining detection. */ @@ -1090,20 +1098,18 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) force_*=... parameter, and the Winbond will be reset to the right bank. */ if (kind < 0) { - if (w83781d_read_value(new_client, W83781D_REG_CONFIG) & 0x80) { - dev_dbg(&new_client->dev, "Detection failed at step " - "3\n"); + if (w83781d_read_value(client, W83781D_REG_CONFIG) & 0x80) { + dev_dbg(dev, "Detection failed at step 3\n"); err = -ENODEV; goto ERROR2; } - val1 = w83781d_read_value(new_client, W83781D_REG_BANK); - val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN); + val1 = w83781d_read_value(client, W83781D_REG_BANK); + val2 = w83781d_read_value(client, W83781D_REG_CHIPMAN); /* Check for Winbond or Asus ID if in bank 0 */ if ((!(val1 & 0x07)) && (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3)) || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) { - dev_dbg(&new_client->dev, "Detection failed at step " - "4\n"); + dev_dbg(dev, "Detection failed at step 4\n"); err = -ENODEV; goto ERROR2; } @@ -1112,9 +1118,8 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) || ((val1 & 0x80) && (val2 == 0x5c)))) { if (w83781d_read_value - (new_client, W83781D_REG_I2C_ADDR) != address) { - dev_dbg(&new_client->dev, "Detection failed " - "at step 5\n"); + (client, W83781D_REG_I2C_ADDR) != address) { + dev_dbg(dev, "Detection failed at step 5\n"); err = -ENODEV; goto ERROR2; } @@ -1123,27 +1128,26 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) /* We have either had a force parameter, or we have already detected the Winbond. Put it now into bank 0 and Vendor ID High Byte */ - w83781d_write_value(new_client, W83781D_REG_BANK, - (w83781d_read_value(new_client, - W83781D_REG_BANK) & 0x78) | - 0x80); + w83781d_write_value(client, W83781D_REG_BANK, + (w83781d_read_value(client, W83781D_REG_BANK) + & 0x78) | 0x80); /* Determine the chip type. */ if (kind <= 0) { /* get vendor ID */ - val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN); + val2 = w83781d_read_value(client, W83781D_REG_CHIPMAN); if (val2 == 0x5c) vendid = winbond; else if (val2 == 0x12) vendid = asus; else { - dev_dbg(&new_client->dev, "Chip was made by neither " + dev_dbg(dev, "Chip was made by neither " "Winbond nor Asus?\n"); err = -ENODEV; goto ERROR2; } - val1 = w83781d_read_value(new_client, W83781D_REG_WCHIPID); + val1 = w83781d_read_value(client, W83781D_REG_WCHIPID); if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond) kind = w83781d; else if (val1 == 0x30 && vendid == winbond) @@ -1157,7 +1161,7 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) kind = as99127f; else { if (kind == 0) - dev_warn(&new_client->dev, "Ignoring 'force' " + dev_warn(dev, "Ignoring 'force' " "parameter for unknown chip at " "adapter %d, address 0x%02x\n", i2c_adapter_id(adapter), address); @@ -1179,20 +1183,20 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) } /* Fill in the remaining client fields and put into the global list */ - strlcpy(new_client->name, client_name, I2C_NAME_SIZE); + strlcpy(client->name, client_name, I2C_NAME_SIZE); data->type = kind; data->valid = 0; mutex_init(&data->update_lock); /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) + if ((err = i2c_attach_client(client))) goto ERROR2; /* attach secondary i2c lm75-like clients */ if (!is_isa) { if ((err = w83781d_detect_subclients(adapter, address, - kind, new_client))) + kind, client))) goto ERROR3; } else { data->lm75[0] = NULL; @@ -1200,11 +1204,11 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) } /* Initialize the chip */ - w83781d_init_client(new_client); + w83781d_init_client(client); /* A few vars need to be filled upon startup */ for (i = 1; i <= 3; i++) { - data->fan_min[i - 1] = w83781d_read_value(new_client, + data->fan_min[i - 1] = w83781d_read_value(client, W83781D_REG_FAN_MIN(i)); } if (kind != w83781d && kind != as99127f) @@ -1212,65 +1216,68 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) data->pwmenable[i] = 1; /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); + if ((err = sysfs_create_group(&dev->kobj, &w83781d_group))) goto ERROR4; - } - device_create_file_in(new_client, 0); - if (kind != w83783s) - device_create_file_in(new_client, 1); - device_create_file_in(new_client, 2); - device_create_file_in(new_client, 3); - device_create_file_in(new_client, 4); - device_create_file_in(new_client, 5); - device_create_file_in(new_client, 6); + if (kind != w83783s) { + if ((err = device_create_file(dev, &dev_attr_in1_input)) + || (err = device_create_file(dev, &dev_attr_in1_min)) + || (err = device_create_file(dev, &dev_attr_in1_max))) + goto ERROR4; + } if (kind != as99127f && kind != w83781d && kind != w83783s) { - device_create_file_in(new_client, 7); - device_create_file_in(new_client, 8); + if ((err = device_create_file(dev, &dev_attr_in7_input)) + || (err = device_create_file(dev, &dev_attr_in7_min)) + || (err = device_create_file(dev, &dev_attr_in7_max)) + || (err = device_create_file(dev, &dev_attr_in8_input)) + || (err = device_create_file(dev, &dev_attr_in8_min)) + || (err = device_create_file(dev, &dev_attr_in8_max))) + goto ERROR4; + } + if (kind != w83783s) { + if ((err = device_create_file(dev, &dev_attr_temp3_input)) + || (err = device_create_file(dev, &dev_attr_temp3_max)) + || (err = device_create_file(dev, + &dev_attr_temp3_max_hyst))) + goto ERROR4; } - - device_create_file_fan(new_client, 1); - device_create_file_fan(new_client, 2); - device_create_file_fan(new_client, 3); - - device_create_file_temp(new_client, 1); - device_create_file_temp(new_client, 2); - if (kind != w83783s) - device_create_file_temp(new_client, 3); - - device_create_file_vid(new_client); - device_create_file_vrm(new_client); - - device_create_file_fan_div(new_client, 1); - device_create_file_fan_div(new_client, 2); - device_create_file_fan_div(new_client, 3); - - device_create_file_alarms(new_client); - - device_create_file_beep(new_client); if (kind != w83781d && kind != as99127f) { - device_create_file_pwm(new_client, 1); - device_create_file_pwm(new_client, 2); - device_create_file_pwmenable(new_client, 2); + if ((err = device_create_file(dev, &dev_attr_pwm1)) + || (err = device_create_file(dev, &dev_attr_pwm2)) + || (err = device_create_file(dev, &dev_attr_pwm2_enable))) + goto ERROR4; } if (kind == w83782d && !is_isa) { - device_create_file_pwm(new_client, 3); - device_create_file_pwm(new_client, 4); + if ((err = device_create_file(dev, &dev_attr_pwm3)) + || (err = device_create_file(dev, &dev_attr_pwm4))) + goto ERROR4; } if (kind != as99127f && kind != w83781d) { - device_create_file_sensor(new_client, 1); - device_create_file_sensor(new_client, 2); - if (kind != w83783s) - device_create_file_sensor(new_client, 3); + if ((err = device_create_file(dev, &dev_attr_temp1_type)) + || (err = device_create_file(dev, + &dev_attr_temp2_type))) + goto ERROR4; + if (kind != w83783s) { + if ((err = device_create_file(dev, + &dev_attr_temp3_type))) + goto ERROR4; + } + } + + data->class_dev = hwmon_device_register(dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto ERROR4; } return 0; ERROR4: + sysfs_remove_group(&dev->kobj, &w83781d_group); + sysfs_remove_group(&dev->kobj, &w83781d_group_opt); + if (data->lm75[1]) { i2c_detach_client(data->lm75[1]); kfree(data->lm75[1]); @@ -1280,7 +1287,7 @@ ERROR4: kfree(data->lm75[0]); } ERROR3: - i2c_detach_client(new_client); + i2c_detach_client(client); ERROR2: kfree(data); ERROR1: @@ -1297,9 +1304,11 @@ w83781d_detach_client(struct i2c_client *client) int err; /* main client */ - if (data) + if (data) { hwmon_device_unregister(data->class_dev); - + sysfs_remove_group(&client->dev.kobj, &w83781d_group); + sysfs_remove_group(&client->dev.kobj, &w83781d_group_opt); + } if (i2c_is_isa_client(client)) release_region(client->addr, W83781D_EXTENT); diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index eec43abd57f..d965d074cd6 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -27,9 +27,9 @@ The w83791d chip appears to be part way between the 83781d and the 83792d. Thus, this file is derived from both the w83792d.c and - w83781d.c files, but its output is more along the lines of the - 83781d (which means there are no changes to the user-mode sensors - program which treats the 83791d as an 83781d). + w83781d.c files. + + The w83791g chip is the same as the w83791d but lead-free. */ #include <linux/config.h> @@ -1172,6 +1172,7 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) (w83791d_read(client, W83791D_REG_BEEP_CTRL[1]) << 8) + (w83791d_read(client, W83791D_REG_BEEP_CTRL[2]) << 16); + /* Extract global beep enable flag */ data->beep_enable = (data->beep_mask >> GLOBAL_BEEP_ENABLE_SHIFT) & 0x01; diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index 7576ec9426a..4e108262576 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -43,6 +43,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/sysfs.h> /* Addresses to scan */ static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; @@ -381,41 +382,6 @@ static ssize_t store_in_##reg (struct device *dev, \ store_in_reg(MIN, min); store_in_reg(MAX, max); -static struct sensor_device_attribute sda_in_input[] = { - SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0), - SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1), - SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2), - SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3), - SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4), - SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5), - SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6), - SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7), - SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8), -}; -static struct sensor_device_attribute sda_in_min[] = { - SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0), - SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1), - SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2), - SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3), - SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4), - SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5), - SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6), - SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7), - SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8), -}; -static struct sensor_device_attribute sda_in_max[] = { - SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0), - SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1), - SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2), - SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3), - SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4), - SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5), - SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6), - SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7), - SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8), -}; - - #define show_fan_reg(reg) \ static ssize_t show_##reg (struct device *dev, struct device_attribute *attr, \ char *buf) \ @@ -499,35 +465,6 @@ store_fan_div(struct device *dev, struct device_attribute *attr, return count; } -static struct sensor_device_attribute sda_fan_input[] = { - SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 1), - SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 2), - SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 3), - SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 4), - SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 5), - SENSOR_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 6), - SENSOR_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 7), -}; -static struct sensor_device_attribute sda_fan_min[] = { - SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 1), - SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 2), - SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 3), - SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 4), - SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 5), - SENSOR_ATTR(fan6_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 6), - SENSOR_ATTR(fan7_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 7), -}; -static struct sensor_device_attribute sda_fan_div[] = { - SENSOR_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 1), - SENSOR_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 2), - SENSOR_ATTR(fan3_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 3), - SENSOR_ATTR(fan4_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 4), - SENSOR_ATTR(fan5_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 5), - SENSOR_ATTR(fan6_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 6), - SENSOR_ATTR(fan7_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 7), -}; - - /* read/write the temperature1, includes measured value and limits */ static ssize_t show_temp1(struct device *dev, struct device_attribute *attr, @@ -595,24 +532,6 @@ static ssize_t store_temp23(struct device *dev, struct device_attribute *attr, return count; } -static struct sensor_device_attribute_2 sda_temp_input[] = { - SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp1, NULL, 0, 0), - SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp23, NULL, 0, 0), - SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp23, NULL, 1, 0), -}; - -static struct sensor_device_attribute_2 sda_temp_max[] = { - SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp1, store_temp1, 0, 1), - SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp23, store_temp23, 0, 2), - SENSOR_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp23, store_temp23, 1, 2), -}; - -static struct sensor_device_attribute_2 sda_temp_max_hyst[] = { - SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp1, store_temp1, 0, 2), - SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp23, store_temp23, 0, 4), - SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp23, store_temp23, 1, 4), -}; - /* get reatime status of all sensors items: voltage, temp, fan */ static ssize_t show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) @@ -621,9 +540,6 @@ show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%d\n", data->alarms); } -static -DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); - static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, char *buf) @@ -715,21 +631,6 @@ store_pwmenable(struct device *dev, struct device_attribute *attr, return count; } -static struct sensor_device_attribute sda_pwm[] = { - SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0), - SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1), - SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2), -}; -static struct sensor_device_attribute sda_pwm_enable[] = { - SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, - show_pwmenable, store_pwmenable, 1), - SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, - show_pwmenable, store_pwmenable, 2), - SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, - show_pwmenable, store_pwmenable, 3), -}; - - static ssize_t show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf) @@ -767,16 +668,6 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, return count; } -static struct sensor_device_attribute sda_pwm_mode[] = { - SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, - show_pwm_mode, store_pwm_mode, 0), - SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, - show_pwm_mode, store_pwm_mode, 1), - SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, - show_pwm_mode, store_pwm_mode, 2), -}; - - static ssize_t show_regs_chassis(struct device *dev, struct device_attribute *attr, char *buf) @@ -785,8 +676,6 @@ show_regs_chassis(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", data->chassis); } -static DEVICE_ATTR(chassis, S_IRUGO, show_regs_chassis, NULL); - static ssize_t show_chassis_clear(struct device *dev, struct device_attribute *attr, char *buf) { @@ -815,9 +704,6 @@ store_chassis_clear(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(chassis_clear, S_IRUGO | S_IWUSR, - show_chassis_clear, store_chassis_clear); - /* For Smart Fan I / Thermal Cruise */ static ssize_t show_thermal_cruise(struct device *dev, struct device_attribute *attr, @@ -853,15 +739,6 @@ store_thermal_cruise(struct device *dev, struct device_attribute *attr, return count; } -static struct sensor_device_attribute sda_thermal_cruise[] = { - SENSOR_ATTR(thermal_cruise1, S_IWUSR | S_IRUGO, - show_thermal_cruise, store_thermal_cruise, 1), - SENSOR_ATTR(thermal_cruise2, S_IWUSR | S_IRUGO, - show_thermal_cruise, store_thermal_cruise, 2), - SENSOR_ATTR(thermal_cruise3, S_IWUSR | S_IRUGO, - show_thermal_cruise, store_thermal_cruise, 3), -}; - /* For Smart Fan I/Thermal Cruise and Smart Fan II */ static ssize_t show_tolerance(struct device *dev, struct device_attribute *attr, @@ -901,15 +778,6 @@ store_tolerance(struct device *dev, struct device_attribute *attr, return count; } -static struct sensor_device_attribute sda_tolerance[] = { - SENSOR_ATTR(tolerance1, S_IWUSR | S_IRUGO, - show_tolerance, store_tolerance, 1), - SENSOR_ATTR(tolerance2, S_IWUSR | S_IRUGO, - show_tolerance, store_tolerance, 2), - SENSOR_ATTR(tolerance3, S_IWUSR | S_IRUGO, - show_tolerance, store_tolerance, 3), -}; - /* For Smart Fan II */ static ssize_t show_sf2_point(struct device *dev, struct device_attribute *attr, @@ -946,36 +814,6 @@ store_sf2_point(struct device *dev, struct device_attribute *attr, return count; } -static struct sensor_device_attribute_2 sda_sf2_point[] = { - SENSOR_ATTR_2(sf2_point1_fan1, S_IRUGO | S_IWUSR, - show_sf2_point, store_sf2_point, 1, 1), - SENSOR_ATTR_2(sf2_point2_fan1, S_IRUGO | S_IWUSR, - show_sf2_point, store_sf2_point, 2, 1), - SENSOR_ATTR_2(sf2_point3_fan1, S_IRUGO | S_IWUSR, - show_sf2_point, store_sf2_point, 3, 1), - SENSOR_ATTR_2(sf2_point4_fan1, S_IRUGO | S_IWUSR, - show_sf2_point, store_sf2_point, 4, 1), - - SENSOR_ATTR_2(sf2_point1_fan2, S_IRUGO | S_IWUSR, - show_sf2_point, store_sf2_point, 1, 2), - SENSOR_ATTR_2(sf2_point2_fan2, S_IRUGO | S_IWUSR, - show_sf2_point, store_sf2_point, 2, 2), - SENSOR_ATTR_2(sf2_point3_fan2, S_IRUGO | S_IWUSR, - show_sf2_point, store_sf2_point, 3, 2), - SENSOR_ATTR_2(sf2_point4_fan2, S_IRUGO | S_IWUSR, - show_sf2_point, store_sf2_point, 4, 2), - - SENSOR_ATTR_2(sf2_point1_fan3, S_IRUGO | S_IWUSR, - show_sf2_point, store_sf2_point, 1, 3), - SENSOR_ATTR_2(sf2_point2_fan3, S_IRUGO | S_IWUSR, - show_sf2_point, store_sf2_point, 2, 3), - SENSOR_ATTR_2(sf2_point3_fan3, S_IRUGO | S_IWUSR, - show_sf2_point, store_sf2_point, 3, 3), - SENSOR_ATTR_2(sf2_point4_fan3, S_IRUGO | S_IWUSR, - show_sf2_point, store_sf2_point, 4, 3), -}; - - static ssize_t show_sf2_level(struct device *dev, struct device_attribute *attr, char *buf) @@ -1016,29 +854,6 @@ store_sf2_level(struct device *dev, struct device_attribute *attr, return count; } -static struct sensor_device_attribute_2 sda_sf2_level[] = { - SENSOR_ATTR_2(sf2_level1_fan1, S_IRUGO | S_IWUSR, - show_sf2_level, store_sf2_level, 1, 1), - SENSOR_ATTR_2(sf2_level2_fan1, S_IRUGO | S_IWUSR, - show_sf2_level, store_sf2_level, 2, 1), - SENSOR_ATTR_2(sf2_level3_fan1, S_IRUGO | S_IWUSR, - show_sf2_level, store_sf2_level, 3, 1), - - SENSOR_ATTR_2(sf2_level1_fan2, S_IRUGO | S_IWUSR, - show_sf2_level, store_sf2_level, 1, 2), - SENSOR_ATTR_2(sf2_level2_fan2, S_IRUGO | S_IWUSR, - show_sf2_level, store_sf2_level, 2, 2), - SENSOR_ATTR_2(sf2_level3_fan2, S_IRUGO | S_IWUSR, - show_sf2_level, store_sf2_level, 3, 2), - - SENSOR_ATTR_2(sf2_level1_fan3, S_IRUGO | S_IWUSR, - show_sf2_level, store_sf2_level, 1, 3), - SENSOR_ATTR_2(sf2_level2_fan3, S_IRUGO | S_IWUSR, - show_sf2_level, store_sf2_level, 2, 3), - SENSOR_ATTR_2(sf2_level3_fan3, S_IRUGO | S_IWUSR, - show_sf2_level, store_sf2_level, 3, 3), -}; - /* This function is called when: * w83792d_driver is inserted (when this module is loaded), for each available adapter @@ -1139,12 +954,297 @@ ERROR_SC_0: return err; } -static void device_create_file_fan(struct device *dev, int i) -{ - device_create_file(dev, &sda_fan_input[i].dev_attr); - device_create_file(dev, &sda_fan_div[i].dev_attr); - device_create_file(dev, &sda_fan_min[i].dev_attr); -} +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in, NULL, 6); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_in, NULL, 7); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_in, NULL, 8); +static SENSOR_DEVICE_ATTR(in0_min, S_IWUSR | S_IRUGO, + show_in_min, store_in_min, 0); +static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, + show_in_min, store_in_min, 1); +static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, + show_in_min, store_in_min, 2); +static SENSOR_DEVICE_ATTR(in3_min, S_IWUSR | S_IRUGO, + show_in_min, store_in_min, 3); +static SENSOR_DEVICE_ATTR(in4_min, S_IWUSR | S_IRUGO, + show_in_min, store_in_min, 4); +static SENSOR_DEVICE_ATTR(in5_min, S_IWUSR | S_IRUGO, + show_in_min, store_in_min, 5); +static SENSOR_DEVICE_ATTR(in6_min, S_IWUSR | S_IRUGO, + show_in_min, store_in_min, 6); +static SENSOR_DEVICE_ATTR(in7_min, S_IWUSR | S_IRUGO, + show_in_min, store_in_min, 7); +static SENSOR_DEVICE_ATTR(in8_min, S_IWUSR | S_IRUGO, + show_in_min, store_in_min, 8); +static SENSOR_DEVICE_ATTR(in0_max, S_IWUSR | S_IRUGO, + show_in_max, store_in_max, 0); +static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, + show_in_max, store_in_max, 1); +static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, + show_in_max, store_in_max, 2); +static SENSOR_DEVICE_ATTR(in3_max, S_IWUSR | S_IRUGO, + show_in_max, store_in_max, 3); +static SENSOR_DEVICE_ATTR(in4_max, S_IWUSR | S_IRUGO, + show_in_max, store_in_max, 4); +static SENSOR_DEVICE_ATTR(in5_max, S_IWUSR | S_IRUGO, + show_in_max, store_in_max, 5); +static SENSOR_DEVICE_ATTR(in6_max, S_IWUSR | S_IRUGO, + show_in_max, store_in_max, 6); +static SENSOR_DEVICE_ATTR(in7_max, S_IWUSR | S_IRUGO, + show_in_max, store_in_max, 7); +static SENSOR_DEVICE_ATTR(in8_max, S_IWUSR | S_IRUGO, + show_in_max, store_in_max, 8); +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp1, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp23, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp23, NULL, 1, 0); +static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, + show_temp1, store_temp1, 0, 1); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp23, + store_temp23, 0, 2); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp23, + store_temp23, 1, 2); +static SENSOR_DEVICE_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR, + show_temp1, store_temp1, 0, 2); +static SENSOR_DEVICE_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR, + show_temp23, store_temp23, 0, 4); +static SENSOR_DEVICE_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR, + show_temp23, store_temp23, 1, 4); +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); +static DEVICE_ATTR(chassis, S_IRUGO, show_regs_chassis, NULL); +static DEVICE_ATTR(chassis_clear, S_IRUGO | S_IWUSR, + show_chassis_clear, store_chassis_clear); +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0); +static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1); +static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2); +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 1); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 2); +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 3); +static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, + show_pwm_mode, store_pwm_mode, 0); +static SENSOR_DEVICE_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, + show_pwm_mode, store_pwm_mode, 1); +static SENSOR_DEVICE_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, + show_pwm_mode, store_pwm_mode, 2); +static SENSOR_DEVICE_ATTR(tolerance1, S_IWUSR | S_IRUGO, + show_tolerance, store_tolerance, 1); +static SENSOR_DEVICE_ATTR(tolerance2, S_IWUSR | S_IRUGO, + show_tolerance, store_tolerance, 2); +static SENSOR_DEVICE_ATTR(tolerance3, S_IWUSR | S_IRUGO, + show_tolerance, store_tolerance, 3); +static SENSOR_DEVICE_ATTR(thermal_cruise1, S_IWUSR | S_IRUGO, + show_thermal_cruise, store_thermal_cruise, 1); +static SENSOR_DEVICE_ATTR(thermal_cruise2, S_IWUSR | S_IRUGO, + show_thermal_cruise, store_thermal_cruise, 2); +static SENSOR_DEVICE_ATTR(thermal_cruise3, S_IWUSR | S_IRUGO, + show_thermal_cruise, store_thermal_cruise, 3); +static SENSOR_DEVICE_ATTR_2(sf2_point1_fan1, S_IRUGO | S_IWUSR, + show_sf2_point, store_sf2_point, 1, 1); +static SENSOR_DEVICE_ATTR_2(sf2_point2_fan1, S_IRUGO | S_IWUSR, + show_sf2_point, store_sf2_point, 2, 1); +static SENSOR_DEVICE_ATTR_2(sf2_point3_fan1, S_IRUGO | S_IWUSR, + show_sf2_point, store_sf2_point, 3, 1); +static SENSOR_DEVICE_ATTR_2(sf2_point4_fan1, S_IRUGO | S_IWUSR, + show_sf2_point, store_sf2_point, 4, 1); +static SENSOR_DEVICE_ATTR_2(sf2_point1_fan2, S_IRUGO | S_IWUSR, + show_sf2_point, store_sf2_point, 1, 2); +static SENSOR_DEVICE_ATTR_2(sf2_point2_fan2, S_IRUGO | S_IWUSR, + show_sf2_point, store_sf2_point, 2, 2); +static SENSOR_DEVICE_ATTR_2(sf2_point3_fan2, S_IRUGO | S_IWUSR, + show_sf2_point, store_sf2_point, 3, 2); +static SENSOR_DEVICE_ATTR_2(sf2_point4_fan2, S_IRUGO | S_IWUSR, + show_sf2_point, store_sf2_point, 4, 2); +static SENSOR_DEVICE_ATTR_2(sf2_point1_fan3, S_IRUGO | S_IWUSR, + show_sf2_point, store_sf2_point, 1, 3); +static SENSOR_DEVICE_ATTR_2(sf2_point2_fan3, S_IRUGO | S_IWUSR, + show_sf2_point, store_sf2_point, 2, 3); +static SENSOR_DEVICE_ATTR_2(sf2_point3_fan3, S_IRUGO | S_IWUSR, + show_sf2_point, store_sf2_point, 3, 3); +static SENSOR_DEVICE_ATTR_2(sf2_point4_fan3, S_IRUGO | S_IWUSR, + show_sf2_point, store_sf2_point, 4, 3); +static SENSOR_DEVICE_ATTR_2(sf2_level1_fan1, S_IRUGO | S_IWUSR, + show_sf2_level, store_sf2_level, 1, 1); +static SENSOR_DEVICE_ATTR_2(sf2_level2_fan1, S_IRUGO | S_IWUSR, + show_sf2_level, store_sf2_level, 2, 1); +static SENSOR_DEVICE_ATTR_2(sf2_level3_fan1, S_IRUGO | S_IWUSR, + show_sf2_level, store_sf2_level, 3, 1); +static SENSOR_DEVICE_ATTR_2(sf2_level1_fan2, S_IRUGO | S_IWUSR, + show_sf2_level, store_sf2_level, 1, 2); +static SENSOR_DEVICE_ATTR_2(sf2_level2_fan2, S_IRUGO | S_IWUSR, + show_sf2_level, store_sf2_level, 2, 2); +static SENSOR_DEVICE_ATTR_2(sf2_level3_fan2, S_IRUGO | S_IWUSR, + show_sf2_level, store_sf2_level, 3, 2); +static SENSOR_DEVICE_ATTR_2(sf2_level1_fan3, S_IRUGO | S_IWUSR, + show_sf2_level, store_sf2_level, 1, 3); +static SENSOR_DEVICE_ATTR_2(sf2_level2_fan3, S_IRUGO | S_IWUSR, + show_sf2_level, store_sf2_level, 2, 3); +static SENSOR_DEVICE_ATTR_2(sf2_level3_fan3, S_IRUGO | S_IWUSR, + show_sf2_level, store_sf2_level, 3, 3); +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 3); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 4); +static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 5); +static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 6); +static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 7); +static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, + show_fan_min, store_fan_min, 1); +static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, + show_fan_min, store_fan_min, 2); +static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, + show_fan_min, store_fan_min, 3); +static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO, + show_fan_min, store_fan_min, 4); +static SENSOR_DEVICE_ATTR(fan5_min, S_IWUSR | S_IRUGO, + show_fan_min, store_fan_min, 5); +static SENSOR_DEVICE_ATTR(fan6_min, S_IWUSR | S_IRUGO, + show_fan_min, store_fan_min, 6); +static SENSOR_DEVICE_ATTR(fan7_min, S_IWUSR | S_IRUGO, + show_fan_min, store_fan_min, 7); +static SENSOR_DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, + show_fan_div, store_fan_div, 1); +static SENSOR_DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO, + show_fan_div, store_fan_div, 2); +static SENSOR_DEVICE_ATTR(fan3_div, S_IWUSR | S_IRUGO, + show_fan_div, store_fan_div, 3); +static SENSOR_DEVICE_ATTR(fan4_div, S_IWUSR | S_IRUGO, + show_fan_div, store_fan_div, 4); +static SENSOR_DEVICE_ATTR(fan5_div, S_IWUSR | S_IRUGO, + show_fan_div, store_fan_div, 5); +static SENSOR_DEVICE_ATTR(fan6_div, S_IWUSR | S_IRUGO, + show_fan_div, store_fan_div, 6); +static SENSOR_DEVICE_ATTR(fan7_div, S_IWUSR | S_IRUGO, + show_fan_div, store_fan_div, 7); + +static struct attribute *w83792d_attributes_fan[4][4] = { + { + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, + &sensor_dev_attr_fan4_div.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan5_min.dev_attr.attr, + &sensor_dev_attr_fan5_div.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan6_min.dev_attr.attr, + &sensor_dev_attr_fan6_div.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_fan7_input.dev_attr.attr, + &sensor_dev_attr_fan7_min.dev_attr.attr, + &sensor_dev_attr_fan7_div.dev_attr.attr, + NULL + } +}; + +static const struct attribute_group w83792d_group_fan[4] = { + { .attrs = w83792d_attributes_fan[0] }, + { .attrs = w83792d_attributes_fan[1] }, + { .attrs = w83792d_attributes_fan[2] }, + { .attrs = w83792d_attributes_fan[3] }, +}; + +static struct attribute *w83792d_attributes[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in8_max.dev_attr.attr, + &sensor_dev_attr_in8_min.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm1_mode.dev_attr.attr, + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm2_mode.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm3_mode.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &dev_attr_alarms.attr, + &dev_attr_chassis.attr, + &dev_attr_chassis_clear.attr, + &sensor_dev_attr_tolerance1.dev_attr.attr, + &sensor_dev_attr_thermal_cruise1.dev_attr.attr, + &sensor_dev_attr_tolerance2.dev_attr.attr, + &sensor_dev_attr_thermal_cruise2.dev_attr.attr, + &sensor_dev_attr_tolerance3.dev_attr.attr, + &sensor_dev_attr_thermal_cruise3.dev_attr.attr, + &sensor_dev_attr_sf2_point1_fan1.dev_attr.attr, + &sensor_dev_attr_sf2_point2_fan1.dev_attr.attr, + &sensor_dev_attr_sf2_point3_fan1.dev_attr.attr, + &sensor_dev_attr_sf2_point4_fan1.dev_attr.attr, + &sensor_dev_attr_sf2_point1_fan2.dev_attr.attr, + &sensor_dev_attr_sf2_point2_fan2.dev_attr.attr, + &sensor_dev_attr_sf2_point3_fan2.dev_attr.attr, + &sensor_dev_attr_sf2_point4_fan2.dev_attr.attr, + &sensor_dev_attr_sf2_point1_fan3.dev_attr.attr, + &sensor_dev_attr_sf2_point2_fan3.dev_attr.attr, + &sensor_dev_attr_sf2_point3_fan3.dev_attr.attr, + &sensor_dev_attr_sf2_point4_fan3.dev_attr.attr, + &sensor_dev_attr_sf2_level1_fan1.dev_attr.attr, + &sensor_dev_attr_sf2_level2_fan1.dev_attr.attr, + &sensor_dev_attr_sf2_level3_fan1.dev_attr.attr, + &sensor_dev_attr_sf2_level1_fan2.dev_attr.attr, + &sensor_dev_attr_sf2_level2_fan2.dev_attr.attr, + &sensor_dev_attr_sf2_level3_fan2.dev_attr.attr, + &sensor_dev_attr_sf2_level1_fan3.dev_attr.attr, + &sensor_dev_attr_sf2_level2_fan3.dev_attr.attr, + &sensor_dev_attr_sf2_level3_fan3.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan1_div.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan2_div.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan3_div.dev_attr.attr, + NULL +}; + +static const struct attribute_group w83792d_group = { + .attrs = w83792d_attributes, +}; static int w83792d_detect(struct i2c_adapter *adapter, int address, int kind) @@ -1268,59 +1368,46 @@ w83792d_detect(struct i2c_adapter *adapter, int address, int kind) } /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(dev); - if (IS_ERR(data->class_dev)) { - err = PTR_ERR(data->class_dev); + if ((err = sysfs_create_group(&dev->kobj, &w83792d_group))) goto ERROR3; - } - for (i = 0; i < 9; i++) { - device_create_file(dev, &sda_in_input[i].dev_attr); - device_create_file(dev, &sda_in_max[i].dev_attr); - device_create_file(dev, &sda_in_min[i].dev_attr); - } - for (i = 0; i < 3; i++) - device_create_file_fan(dev, i); /* Read GPIO enable register to check if pins for fan 4,5 are used as GPIO */ val1 = w83792d_read_value(client, W83792D_REG_GPIO_EN); + if (!(val1 & 0x40)) - device_create_file_fan(dev, 3); + if ((err = sysfs_create_group(&dev->kobj, + &w83792d_group_fan[0]))) + goto exit_remove_files; + if (!(val1 & 0x20)) - device_create_file_fan(dev, 4); + if ((err = sysfs_create_group(&dev->kobj, + &w83792d_group_fan[1]))) + goto exit_remove_files; val1 = w83792d_read_value(client, W83792D_REG_PIN); if (val1 & 0x40) - device_create_file_fan(dev, 5); + if ((err = sysfs_create_group(&dev->kobj, + &w83792d_group_fan[2]))) + goto exit_remove_files; + if (val1 & 0x04) - device_create_file_fan(dev, 6); - - for (i = 0; i < 3; i++) { - device_create_file(dev, &sda_temp_input[i].dev_attr); - device_create_file(dev, &sda_temp_max[i].dev_attr); - device_create_file(dev, &sda_temp_max_hyst[i].dev_attr); - device_create_file(dev, &sda_thermal_cruise[i].dev_attr); - device_create_file(dev, &sda_tolerance[i].dev_attr); - } + if ((err = sysfs_create_group(&dev->kobj, + &w83792d_group_fan[3]))) + goto exit_remove_files; - for (i = 0; i < ARRAY_SIZE(sda_pwm); i++) { - device_create_file(dev, &sda_pwm[i].dev_attr); - device_create_file(dev, &sda_pwm_enable[i].dev_attr); - device_create_file(dev, &sda_pwm_mode[i].dev_attr); + data->class_dev = hwmon_device_register(dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove_files; } - device_create_file(dev, &dev_attr_alarms); - device_create_file(dev, &dev_attr_chassis); - device_create_file(dev, &dev_attr_chassis_clear); - - for (i = 0; i < ARRAY_SIZE(sda_sf2_point); i++) - device_create_file(dev, &sda_sf2_point[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(sda_sf2_level); i++) - device_create_file(dev, &sda_sf2_level[i].dev_attr); - return 0; +exit_remove_files: + sysfs_remove_group(&dev->kobj, &w83792d_group); + for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++) + sysfs_remove_group(&dev->kobj, &w83792d_group_fan[i]); ERROR3: if (data->lm75[0] != NULL) { i2c_detach_client(data->lm75[0]); @@ -1342,11 +1429,16 @@ static int w83792d_detach_client(struct i2c_client *client) { struct w83792d_data *data = i2c_get_clientdata(client); - int err; + int err, i; /* main client */ - if (data) + if (data) { hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &w83792d_group); + for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++) + sysfs_remove_group(&client->dev.kobj, + &w83792d_group_fan[i]); + } if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c index 3f2bac125fb..a3fcace412f 100644 --- a/drivers/hwmon/w83l785ts.c +++ b/drivers/hwmon/w83l785ts.c @@ -236,21 +236,30 @@ static int w83l785ts_detect(struct i2c_adapter *adapter, int address, int kind) * Nothing yet, assume it is already started. */ + err = device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_input.dev_attr); + if (err) + goto exit_remove; + + err = device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_max.dev_attr); + if (err) + goto exit_remove; + /* Register sysfs hooks */ data->class_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->class_dev)) { err = PTR_ERR(data->class_dev); - goto exit_detach; + goto exit_remove; } - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_max.dev_attr); - return 0; -exit_detach: +exit_remove: + device_remove_file(&new_client->dev, + &sensor_dev_attr_temp1_input.dev_attr); + device_remove_file(&new_client->dev, + &sensor_dev_attr_temp1_max.dev_attr); i2c_detach_client(new_client); exit_free: kfree(data); @@ -264,7 +273,10 @@ static int w83l785ts_detach_client(struct i2c_client *client) int err; hwmon_device_unregister(data->class_dev); - + device_remove_file(&client->dev, + &sensor_dev_attr_temp1_input.dev_attr); + device_remove_file(&client->dev, + &sensor_dev_attr_temp1_max.dev_attr); if ((err = i2c_detach_client(client))) return err; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 9e56c3989d6..0d9667921f6 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -196,7 +196,7 @@ config I2C_IBM_IIC config I2C_IOP3XX tristate "Intel IOP3xx and IXP4xx on-chip I2C interface" - depends on (ARCH_IOP3XX || ARCH_IXP4XX) && I2C + depends on (ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX) && I2C help Say Y here if you want to use the IIC bus controller on the Intel IOP3xx I/O Processors or IXP4xx Network Processors. diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index 8e413150af3..4436c89be58 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -82,14 +82,16 @@ iop3xx_i2c_enable(struct i2c_algo_iop3xx_data *iop3xx_adap) /* * Every time unit enable is asserted, GPOD needs to be cleared - * on IOP321 to avoid data corruption on the bus. + * on IOP3XX to avoid data corruption on the bus. */ -#ifdef CONFIG_ARCH_IOP321 -#define IOP321_GPOD_I2C0 0x00c0 /* clear these bits to enable ch0 */ -#define IOP321_GPOD_I2C1 0x0030 /* clear these bits to enable ch1 */ - - *IOP321_GPOD &= (iop3xx_adap->id == 0) ? ~IOP321_GPOD_I2C0 : - ~IOP321_GPOD_I2C1; +#ifdef CONFIG_PLAT_IOP + if (iop3xx_adap->id == 0) { + gpio_line_set(IOP3XX_GPIO_LINE(7), GPIO_LOW); + gpio_line_set(IOP3XX_GPIO_LINE(6), GPIO_LOW); + } else { + gpio_line_set(IOP3XX_GPIO_LINE(5), GPIO_LOW); + gpio_line_set(IOP3XX_GPIO_LINE(4), GPIO_LOW); + } #endif /* NB SR bits not same position as CR IE bits :-( */ iop3xx_adap->SR_enabled = diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c index 8f2b1f0deb8..0ca599d3b40 100644 --- a/drivers/i2c/busses/i2c-sibyte.c +++ b/drivers/i2c/busses/i2c-sibyte.c @@ -210,8 +210,8 @@ static int __init i2c_sibyte_init(void) static void __exit i2c_sibyte_exit(void) { - i2c_del_bus(&sibyte_board_adapter[0]); - i2c_del_bus(&sibyte_board_adapter[1]); + i2c_del_adapter(&sibyte_board_adapter[0]); + i2c_del_adapter(&sibyte_board_adapter[1]); } module_init(i2c_sibyte_init); diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 01233f0f777..7ca81f42d14 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -420,14 +420,6 @@ int i2c_attach_client(struct i2c_client *client) } list_add_tail(&client->list,&adapter->clients); - if (adapter->client_register) { - if (adapter->client_register(client)) { - dev_dbg(&adapter->dev, "client_register " - "failed for client [%s] at 0x%02x\n", - client->name, client->addr); - } - } - client->usage_count = 0; client->dev.parent = &client->adapter->dev; @@ -445,10 +437,17 @@ int i2c_attach_client(struct i2c_client *client) res = device_create_file(&client->dev, &dev_attr_client_name); if (res) goto out_unregister; - -out_unlock: mutex_unlock(&adapter->clist_lock); - return res; + + if (adapter->client_register) { + if (adapter->client_register(client)) { + dev_dbg(&adapter->dev, "client_register " + "failed for client [%s] at 0x%02x\n", + client->name, client->addr); + } + } + + return 0; out_unregister: init_completion(&client->released); /* Needed? */ @@ -458,7 +457,9 @@ out_list: list_del(&client->list); dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x " "(%d)\n", client->name, client->addr, res); - goto out_unlock; +out_unlock: + mutex_unlock(&adapter->clist_lock); + return res; } diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index b6fb167e20f..69d627bd537 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -4,6 +4,8 @@ # Andre Hedrick <andre@linux-ide.org> # +if BLOCK + menu "ATA/ATAPI/MFM/RLL support" config IDE @@ -1082,3 +1084,5 @@ config BLK_DEV_HD endif endmenu + +endif diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 654d4cd0984..69bbb6206a0 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -372,7 +372,7 @@ static int cdrom_log_sense(ide_drive_t *drive, struct request *rq, { int log = 0; - if (!sense || !rq || (rq->flags & REQ_QUIET)) + if (!sense || !rq || (rq->cmd_flags & REQ_QUIET)) return 0; switch (sense->sense_key) { @@ -597,7 +597,7 @@ static void cdrom_prepare_request(ide_drive_t *drive, struct request *rq) struct cdrom_info *cd = drive->driver_data; ide_init_drive_cmd(rq); - rq->flags = REQ_PC; + rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->rq_disk = cd->disk; } @@ -617,7 +617,7 @@ static void cdrom_queue_request_sense(ide_drive_t *drive, void *sense, rq->cmd[0] = GPCMD_REQUEST_SENSE; rq->cmd[4] = rq->data_len = 18; - rq->flags = REQ_SENSE; + rq->cmd_type = REQ_TYPE_SENSE; /* NOTE! Save the failed command in "rq->buffer" */ rq->buffer = (void *) failed_command; @@ -630,10 +630,10 @@ static void cdrom_end_request (ide_drive_t *drive, int uptodate) struct request *rq = HWGROUP(drive)->rq; int nsectors = rq->hard_cur_sectors; - if ((rq->flags & REQ_SENSE) && uptodate) { + if (blk_sense_request(rq) && uptodate) { /* - * For REQ_SENSE, "rq->buffer" points to the original failed - * request + * For REQ_TYPE_SENSE, "rq->buffer" points to the original + * failed request */ struct request *failed = (struct request *) rq->buffer; struct cdrom_info *info = drive->driver_data; @@ -706,17 +706,17 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) return 1; } - if (rq->flags & REQ_SENSE) { + if (blk_sense_request(rq)) { /* We got an error trying to get sense info from the drive (probably while trying to recover from a former error). Just give up. */ - rq->flags |= REQ_FAILED; + rq->cmd_flags |= REQ_FAILED; cdrom_end_request(drive, 0); ide_error(drive, "request sense failure", stat); return 1; - } else if (rq->flags & (REQ_PC | REQ_BLOCK_PC)) { + } else if (blk_pc_request(rq)) { /* All other functions, except for READ. */ unsigned long flags; @@ -724,7 +724,7 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) * if we have an error, pass back CHECK_CONDITION as the * scsi status byte */ - if ((rq->flags & REQ_BLOCK_PC) && !rq->errors) + if (!rq->errors) rq->errors = SAM_STAT_CHECK_CONDITION; /* Check for tray open. */ @@ -735,12 +735,12 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) cdrom_saw_media_change (drive); /*printk("%s: media changed\n",drive->name);*/ return 0; - } else if (!(rq->flags & REQ_QUIET)) { + } else if (!(rq->cmd_flags & REQ_QUIET)) { /* Otherwise, print an error. */ ide_dump_status(drive, "packet command error", stat); } - rq->flags |= REQ_FAILED; + rq->cmd_flags |= REQ_FAILED; /* * instead of playing games with moving completions around, @@ -881,7 +881,7 @@ static int cdrom_timer_expiry(ide_drive_t *drive) wait = ATAPI_WAIT_PC; break; default: - if (!(rq->flags & REQ_QUIET)) + if (!(rq->cmd_flags & REQ_QUIET)) printk(KERN_INFO "ide-cd: cmd 0x%x timed out\n", rq->cmd[0]); wait = 0; break; @@ -1124,7 +1124,7 @@ static ide_startstop_t cdrom_read_intr (ide_drive_t *drive) if (rq->current_nr_sectors > 0) { printk (KERN_ERR "%s: cdrom_read_intr: data underrun (%d blocks)\n", drive->name, rq->current_nr_sectors); - rq->flags |= REQ_FAILED; + rq->cmd_flags |= REQ_FAILED; cdrom_end_request(drive, 0); } else cdrom_end_request(drive, 1); @@ -1456,7 +1456,7 @@ static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive) printk ("%s: cdrom_pc_intr: data underrun %d\n", drive->name, pc->buflen); */ - rq->flags |= REQ_FAILED; + rq->cmd_flags |= REQ_FAILED; cdrom_end_request(drive, 0); } return ide_stopped; @@ -1509,7 +1509,7 @@ static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive) rq->data += thislen; rq->data_len -= thislen; - if (rq->flags & REQ_SENSE) + if (blk_sense_request(rq)) rq->sense_len += thislen; } else { confused: @@ -1517,7 +1517,7 @@ confused: "appears confused (ireason = 0x%02x). " "Trying to recover by ending request.\n", drive->name, ireason); - rq->flags |= REQ_FAILED; + rq->cmd_flags |= REQ_FAILED; cdrom_end_request(drive, 0); return ide_stopped; } @@ -1546,7 +1546,7 @@ static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive) struct cdrom_info *info = drive->driver_data; info->dma = 0; - rq->flags &= ~REQ_FAILED; + rq->cmd_flags &= ~REQ_FAILED; len = rq->data_len; /* Start sending the command to the drive. */ @@ -1558,7 +1558,7 @@ static int cdrom_queue_packet_command(ide_drive_t *drive, struct request *rq) { struct request_sense sense; int retries = 10; - unsigned int flags = rq->flags; + unsigned int flags = rq->cmd_flags; if (rq->sense == NULL) rq->sense = &sense; @@ -1567,14 +1567,14 @@ static int cdrom_queue_packet_command(ide_drive_t *drive, struct request *rq) do { int error; unsigned long time = jiffies; - rq->flags = flags; + rq->cmd_flags = flags; error = ide_do_drive_cmd(drive, rq, ide_wait); time = jiffies - time; /* FIXME: we should probably abort/retry or something * in case of failure */ - if (rq->flags & REQ_FAILED) { + if (rq->cmd_flags & REQ_FAILED) { /* The request failed. Retry if it was due to a unit attention status (usually means media was changed). */ @@ -1596,10 +1596,10 @@ static int cdrom_queue_packet_command(ide_drive_t *drive, struct request *rq) } /* End of retry loop. */ - } while ((rq->flags & REQ_FAILED) && retries >= 0); + } while ((rq->cmd_flags & REQ_FAILED) && retries >= 0); /* Return an error if the command failed. */ - return (rq->flags & REQ_FAILED) ? -EIO : 0; + return (rq->cmd_flags & REQ_FAILED) ? -EIO : 0; } /* @@ -1963,7 +1963,7 @@ static ide_startstop_t cdrom_do_block_pc(ide_drive_t *drive, struct request *rq) { struct cdrom_info *info = drive->driver_data; - rq->flags |= REQ_QUIET; + rq->cmd_flags |= REQ_QUIET; info->dma = 0; @@ -2023,11 +2023,11 @@ ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, sector_t block) } info->last_block = block; return action; - } else if (rq->flags & (REQ_PC | REQ_SENSE)) { + } else if (rq->cmd_type == REQ_TYPE_SENSE) { return cdrom_do_packet_command(drive); - } else if (rq->flags & REQ_BLOCK_PC) { + } else if (blk_pc_request(rq)) { return cdrom_do_block_pc(drive, rq); - } else if (rq->flags & REQ_SPECIAL) { + } else if (blk_special_request(rq)) { /* * right now this can only be a reset... */ @@ -2105,7 +2105,7 @@ static int cdrom_check_status(ide_drive_t *drive, struct request_sense *sense) req.sense = sense; req.cmd[0] = GPCMD_TEST_UNIT_READY; - req.flags |= REQ_QUIET; + req.cmd_flags |= REQ_QUIET; #if ! STANDARD_ATAPI /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to @@ -2207,7 +2207,7 @@ static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity, req.cmd[0] = GPCMD_READ_CDVD_CAPACITY; req.data = (char *)&capbuf; req.data_len = sizeof(capbuf); - req.flags |= REQ_QUIET; + req.cmd_flags |= REQ_QUIET; stat = cdrom_queue_packet_command(drive, &req); if (stat == 0) { @@ -2230,7 +2230,7 @@ static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag, req.sense = sense; req.data = buf; req.data_len = buflen; - req.flags |= REQ_QUIET; + req.cmd_flags |= REQ_QUIET; req.cmd[0] = GPCMD_READ_TOC_PMA_ATIP; req.cmd[6] = trackno; req.cmd[7] = (buflen >> 8); @@ -2531,7 +2531,7 @@ static int ide_cdrom_packet(struct cdrom_device_info *cdi, req.timeout = cgc->timeout; if (cgc->quiet) - req.flags |= REQ_QUIET; + req.cmd_flags |= REQ_QUIET; req.sense = cgc->sense; cgc->stat = cdrom_queue_packet_command(drive, &req); @@ -2629,7 +2629,8 @@ int ide_cdrom_reset (struct cdrom_device_info *cdi) int ret; cdrom_prepare_request(drive, &req); - req.flags = REQ_SPECIAL | REQ_QUIET; + req.cmd_type = REQ_TYPE_SPECIAL; + req.cmd_flags = REQ_QUIET; ret = ide_do_drive_cmd(drive, &req, ide_wait); /* @@ -3116,9 +3117,9 @@ static int ide_cdrom_prep_pc(struct request *rq) static int ide_cdrom_prep_fn(request_queue_t *q, struct request *rq) { - if (rq->flags & REQ_CMD) + if (blk_fs_request(rq)) return ide_cdrom_prep_fs(q, rq); - else if (rq->flags & REQ_BLOCK_PC) + else if (blk_pc_request(rq)) return ide_cdrom_prep_pc(rq); return 0; diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 7cf3eb02352..0a05a377d66 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -699,7 +699,8 @@ static void idedisk_prepare_flush(request_queue_t *q, struct request *rq) rq->cmd[0] = WIN_FLUSH_CACHE; - rq->flags |= REQ_DRIVE_TASK; + rq->cmd_type = REQ_TYPE_ATA_TASK; + rq->cmd_flags |= REQ_SOFTBARRIER; rq->buffer = rq->cmd; } @@ -740,7 +741,7 @@ static int set_multcount(ide_drive_t *drive, int arg) if (drive->special.b.set_multmode) return -EBUSY; ide_init_drive_cmd (&rq); - rq.flags = REQ_DRIVE_CMD; + rq.cmd_type = REQ_TYPE_ATA_CMD; drive->mult_req = arg; drive->special.b.set_multmode = 1; (void) ide_do_drive_cmd (drive, &rq, ide_wait); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 7c3a13e1cf6..c3546fe9af6 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -205,7 +205,7 @@ int ide_build_sglist(ide_drive_t *drive, struct request *rq) ide_hwif_t *hwif = HWIF(drive); struct scatterlist *sg = hwif->sg_table; - BUG_ON((rq->flags & REQ_DRIVE_TASKFILE) && rq->nr_sectors > 256); + BUG_ON((rq->cmd_type == REQ_TYPE_ATA_TASKFILE) && rq->nr_sectors > 256); ide_map_sg(drive, rq); diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index adbe9f76a50..8ccee9c769f 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -588,7 +588,7 @@ static int idefloppy_do_end_request(ide_drive_t *drive, int uptodate, int nsecs) /* Why does this happen? */ if (!rq) return 0; - if (!(rq->flags & REQ_SPECIAL)) { //if (!IDEFLOPPY_RQ_CMD (rq->cmd)) { + if (!blk_special_request(rq)) { /* our real local end request function */ ide_end_request(drive, uptodate, nsecs); return 0; @@ -689,7 +689,7 @@ static void idefloppy_queue_pc_head (ide_drive_t *drive,idefloppy_pc_t *pc,struc ide_init_drive_cmd(rq); rq->buffer = (char *) pc; - rq->flags = REQ_SPECIAL; //rq->cmd = IDEFLOPPY_PC_RQ; + rq->cmd_type = REQ_TYPE_SPECIAL; rq->rq_disk = floppy->disk; (void) ide_do_drive_cmd(drive, rq, ide_preempt); } @@ -1250,7 +1250,7 @@ static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t pc->callback = &idefloppy_rw_callback; pc->rq = rq; pc->b_count = cmd == READ ? 0 : rq->bio->bi_size; - if (rq->flags & REQ_RW) + if (rq->cmd_flags & REQ_RW) set_bit(PC_WRITING, &pc->flags); pc->buffer = NULL; pc->request_transfer = pc->buffer_size = blocks * floppy->block_size; @@ -1281,8 +1281,7 @@ static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request idefloppy_pc_t *pc; unsigned long block = (unsigned long)block_s; - debug_log(KERN_INFO "rq_status: %d, dev: %s, flags: %lx, errors: %d\n", - rq->rq_status, + debug_log(KERN_INFO "dev: %s, flags: %lx, errors: %d\n", rq->rq_disk ? rq->rq_disk->disk_name : "?", rq->flags, rq->errors); debug_log(KERN_INFO "sector: %ld, nr_sectors: %ld, " @@ -1303,7 +1302,7 @@ static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request idefloppy_do_end_request(drive, 0, 0); return ide_stopped; } - if (rq->flags & REQ_CMD) { + if (blk_fs_request(rq)) { if (((long)rq->sector % floppy->bs_factor) || (rq->nr_sectors % floppy->bs_factor)) { printk("%s: unsupported r/w request size\n", @@ -1313,9 +1312,9 @@ static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request } pc = idefloppy_next_pc_storage(drive); idefloppy_create_rw_cmd(floppy, pc, rq, block); - } else if (rq->flags & REQ_SPECIAL) { + } else if (blk_special_request(rq)) { pc = (idefloppy_pc_t *) rq->buffer; - } else if (rq->flags & REQ_BLOCK_PC) { + } else if (blk_pc_request(rq)) { pc = idefloppy_next_pc_storage(drive); if (idefloppy_blockpc_cmd(floppy, pc, rq)) { idefloppy_do_end_request(drive, 0, 0); @@ -1343,7 +1342,7 @@ static int idefloppy_queue_pc_tail (ide_drive_t *drive,idefloppy_pc_t *pc) ide_init_drive_cmd (&rq); rq.buffer = (char *) pc; - rq.flags = REQ_SPECIAL; // rq.cmd = IDEFLOPPY_PC_RQ; + rq.cmd_type = REQ_TYPE_SPECIAL; rq.rq_disk = floppy->disk; return ide_do_drive_cmd(drive, &rq, ide_wait); diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index fb6795236e7..38479a29d3e 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -59,7 +59,7 @@ static int __ide_end_request(ide_drive_t *drive, struct request *rq, { int ret = 1; - BUG_ON(!(rq->flags & REQ_STARTED)); + BUG_ON(!blk_rq_started(rq)); /* * if failfast is set on a request, override number of sectors and @@ -141,7 +141,7 @@ enum { static void ide_complete_power_step(ide_drive_t *drive, struct request *rq, u8 stat, u8 error) { - struct request_pm_state *pm = rq->end_io_data; + struct request_pm_state *pm = rq->data; if (drive->media != ide_disk) return; @@ -164,7 +164,7 @@ static void ide_complete_power_step(ide_drive_t *drive, struct request *rq, u8 s static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq) { - struct request_pm_state *pm = rq->end_io_data; + struct request_pm_state *pm = rq->data; ide_task_t *args = rq->special; memset(args, 0, sizeof(*args)); @@ -244,7 +244,7 @@ int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq, spin_lock_irqsave(&ide_lock, flags); - BUG_ON(!(rq->flags & REQ_STARTED)); + BUG_ON(!blk_rq_started(rq)); /* * if failfast is set on a request, override number of sectors and @@ -366,7 +366,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) rq = HWGROUP(drive)->rq; spin_unlock_irqrestore(&ide_lock, flags); - if (rq->flags & REQ_DRIVE_CMD) { + if (rq->cmd_type == REQ_TYPE_ATA_CMD) { u8 *args = (u8 *) rq->buffer; if (rq->errors == 0) rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); @@ -376,7 +376,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) args[1] = err; args[2] = hwif->INB(IDE_NSECTOR_REG); } - } else if (rq->flags & REQ_DRIVE_TASK) { + } else if (rq->cmd_type == REQ_TYPE_ATA_TASK) { u8 *args = (u8 *) rq->buffer; if (rq->errors == 0) rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); @@ -390,7 +390,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) args[5] = hwif->INB(IDE_HCYL_REG); args[6] = hwif->INB(IDE_SELECT_REG); } - } else if (rq->flags & REQ_DRIVE_TASKFILE) { + } else if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) { ide_task_t *args = (ide_task_t *) rq->special; if (rq->errors == 0) rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); @@ -421,7 +421,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) } } } else if (blk_pm_request(rq)) { - struct request_pm_state *pm = rq->end_io_data; + struct request_pm_state *pm = rq->data; #ifdef DEBUG_PM printk("%s: complete_power_step(step: %d, stat: %x, err: %x)\n", drive->name, rq->pm->pm_step, stat, err); @@ -587,7 +587,7 @@ ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, u8 stat) return ide_stopped; /* retry only "normal" I/O: */ - if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK | REQ_DRIVE_TASKFILE)) { + if (!blk_fs_request(rq)) { rq->errors = 1; ide_end_drive_cmd(drive, stat, err); return ide_stopped; @@ -638,7 +638,7 @@ ide_startstop_t ide_abort(ide_drive_t *drive, const char *msg) return ide_stopped; /* retry only "normal" I/O: */ - if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK | REQ_DRIVE_TASKFILE)) { + if (!blk_fs_request(rq)) { rq->errors = 1; ide_end_drive_cmd(drive, BUSY_STAT, 0); return ide_stopped; @@ -808,7 +808,7 @@ void ide_map_sg(ide_drive_t *drive, struct request *rq) if (hwif->sg_mapped) /* needed by ide-scsi */ return; - if ((rq->flags & REQ_DRIVE_TASKFILE) == 0) { + if (rq->cmd_type != REQ_TYPE_ATA_TASKFILE) { hwif->sg_nents = blk_rq_map_sg(drive->queue, rq, sg); } else { sg_init_one(sg, rq->buffer, rq->nr_sectors * SECTOR_SIZE); @@ -844,7 +844,7 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq) { ide_hwif_t *hwif = HWIF(drive); - if (rq->flags & REQ_DRIVE_TASKFILE) { + if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) { ide_task_t *args = rq->special; if (!args) @@ -866,7 +866,7 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, if (args->tf_out_flags.all != 0) return flagged_taskfile(drive, args); return do_rw_taskfile(drive, args); - } else if (rq->flags & REQ_DRIVE_TASK) { + } else if (rq->cmd_type == REQ_TYPE_ATA_TASK) { u8 *args = rq->buffer; u8 sel; @@ -892,7 +892,7 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, hwif->OUTB(sel, IDE_SELECT_REG); ide_cmd(drive, args[0], args[2], &drive_cmd_intr); return ide_started; - } else if (rq->flags & REQ_DRIVE_CMD) { + } else if (rq->cmd_type == REQ_TYPE_ATA_CMD) { u8 *args = rq->buffer; if (!args) @@ -933,7 +933,7 @@ done: static void ide_check_pm_state(ide_drive_t *drive, struct request *rq) { - struct request_pm_state *pm = rq->end_io_data; + struct request_pm_state *pm = rq->data; if (blk_pm_suspend_request(rq) && pm->pm_step == ide_pm_state_start_suspend) @@ -980,7 +980,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) ide_startstop_t startstop; sector_t block; - BUG_ON(!(rq->flags & REQ_STARTED)); + BUG_ON(!blk_rq_started(rq)); #ifdef DEBUG printk("%s: start_request: current=0x%08lx\n", @@ -1013,12 +1013,12 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) if (!drive->special.all) { ide_driver_t *drv; - if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK)) - return execute_drive_cmd(drive, rq); - else if (rq->flags & REQ_DRIVE_TASKFILE) + if (rq->cmd_type == REQ_TYPE_ATA_CMD || + rq->cmd_type == REQ_TYPE_ATA_TASK || + rq->cmd_type == REQ_TYPE_ATA_TASKFILE) return execute_drive_cmd(drive, rq); else if (blk_pm_request(rq)) { - struct request_pm_state *pm = rq->end_io_data; + struct request_pm_state *pm = rq->data; #ifdef DEBUG_PM printk("%s: start_power_step(step: %d)\n", drive->name, rq->pm->pm_step); @@ -1264,7 +1264,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) * We count how many times we loop here to make sure we service * all drives in the hwgroup without looping for ever */ - if (drive->blocked && !blk_pm_request(rq) && !(rq->flags & REQ_PREEMPT)) { + if (drive->blocked && !blk_pm_request(rq) && !(rq->cmd_flags & REQ_PREEMPT)) { drive = drive->next ? drive->next : hwgroup->drive; if (loops++ < 4 && !blk_queue_plugged(drive->queue)) goto again; @@ -1670,7 +1670,7 @@ irqreturn_t ide_intr (int irq, void *dev_id, struct pt_regs *regs) void ide_init_drive_cmd (struct request *rq) { memset(rq, 0, sizeof(*rq)); - rq->flags = REQ_DRIVE_CMD; + rq->cmd_type = REQ_TYPE_ATA_CMD; rq->ref_count = 1; } @@ -1710,7 +1710,6 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio int must_wait = (action == ide_wait || action == ide_head_wait); rq->errors = 0; - rq->rq_status = RQ_ACTIVE; /* * we need to hold an extra reference to request for safe inspection @@ -1718,7 +1717,7 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio */ if (must_wait) { rq->ref_count++; - rq->waiting = &wait; + rq->end_io_data = &wait; rq->end_io = blk_end_sync_rq; } @@ -1727,7 +1726,7 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio hwgroup->rq = NULL; if (action == ide_preempt || action == ide_head_wait) { where = ELEVATOR_INSERT_FRONT; - rq->flags |= REQ_PREEMPT; + rq->cmd_flags |= REQ_PREEMPT; } __elv_add_request(drive->queue, rq, where, 0); ide_do_request(hwgroup, IDE_NO_IRQ); @@ -1736,7 +1735,6 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio err = 0; if (must_wait) { wait_for_completion(&wait); - rq->waiting = NULL; if (rq->errors) err = -EIO; diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 1feff23487d..850ef63cc98 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -456,13 +456,14 @@ static void ide_dump_opcode(ide_drive_t *drive) spin_unlock(&ide_lock); if (!rq) return; - if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK)) { + if (rq->cmd_type == REQ_TYPE_ATA_CMD || + rq->cmd_type == REQ_TYPE_ATA_TASK) { char *args = rq->buffer; if (args) { opcode = args[0]; found = 1; } - } else if (rq->flags & REQ_DRIVE_TASKFILE) { + } else if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) { ide_task_t *args = rq->special; if (args) { task_struct_t *tf = (task_struct_t *) args->tfRegister; diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 7067ab99792..2ebc3760f26 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1776,7 +1776,7 @@ static void idetape_create_request_sense_cmd (idetape_pc_t *pc) static void idetape_init_rq(struct request *rq, u8 cmd) { memset(rq, 0, sizeof(*rq)); - rq->flags = REQ_SPECIAL; + rq->cmd_type = REQ_TYPE_SPECIAL; rq->cmd[0] = cmd; } @@ -2423,8 +2423,8 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, #if IDETAPE_DEBUG_LOG #if 0 if (tape->debug_level >= 5) - printk(KERN_INFO "ide-tape: rq_status: %d, " - "dev: %s, cmd: %ld, errors: %d\n", rq->rq_status, + printk(KERN_INFO "ide-tape: %d, " + "dev: %s, cmd: %ld, errors: %d\n", rq->rq_disk->disk_name, rq->cmd[0], rq->errors); #endif if (tape->debug_level >= 2) @@ -2433,12 +2433,12 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, rq->sector, rq->nr_sectors, rq->current_nr_sectors); #endif /* IDETAPE_DEBUG_LOG */ - if ((rq->flags & REQ_SPECIAL) == 0) { + if (!blk_special_request(rq)) { /* * We do not support buffer cache originated requests. */ printk(KERN_NOTICE "ide-tape: %s: Unsupported request in " - "request queue (%ld)\n", drive->name, rq->flags); + "request queue (%d)\n", drive->name, rq->cmd_type); ide_end_request(drive, 0, 0); return ide_stopped; } @@ -2768,12 +2768,12 @@ static void idetape_wait_for_request (ide_drive_t *drive, struct request *rq) idetape_tape_t *tape = drive->driver_data; #if IDETAPE_DEBUG_BUGS - if (rq == NULL || (rq->flags & REQ_SPECIAL) == 0) { + if (rq == NULL || !blk_special_request(rq)) { printk (KERN_ERR "ide-tape: bug: Trying to sleep on non-valid request\n"); return; } #endif /* IDETAPE_DEBUG_BUGS */ - rq->waiting = &wait; + rq->end_io_data = &wait; rq->end_io = blk_end_sync_rq; spin_unlock_irq(&tape->spinlock); wait_for_completion(&wait); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 97a9244312f..1d0470c1f95 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -363,7 +363,7 @@ static ide_startstop_t task_error(ide_drive_t *drive, struct request *rq, static void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat) { - if (rq->flags & REQ_DRIVE_TASKFILE) { + if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) { ide_task_t *task = rq->special; if (task->tf_out_flags.all) { @@ -474,7 +474,7 @@ static int ide_diag_taskfile(ide_drive_t *drive, ide_task_t *args, unsigned long struct request rq; memset(&rq, 0, sizeof(rq)); - rq.flags = REQ_DRIVE_TASKFILE; + rq.cmd_type = REQ_TYPE_ATA_TASKFILE; rq.buffer = buf; /* @@ -499,7 +499,7 @@ static int ide_diag_taskfile(ide_drive_t *drive, ide_task_t *args, unsigned long rq.hard_cur_sectors = rq.current_nr_sectors = rq.nr_sectors; if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE) - rq.flags |= REQ_RW; + rq.cmd_flags |= REQ_RW; } rq.special = args; @@ -737,7 +737,7 @@ static int ide_wait_cmd_task(ide_drive_t *drive, u8 *buf) struct request rq; ide_init_drive_cmd(&rq); - rq.flags = REQ_DRIVE_TASK; + rq.cmd_type = REQ_TYPE_ATA_TASK; rq.buffer = buf; return ide_do_drive_cmd(drive, &rq, ide_wait); } diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 9c8468de1a7..2b1a1389c31 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -1217,9 +1217,9 @@ static int generic_ide_suspend(struct device *dev, pm_message_t mesg) memset(&rq, 0, sizeof(rq)); memset(&rqpm, 0, sizeof(rqpm)); memset(&args, 0, sizeof(args)); - rq.flags = REQ_PM_SUSPEND; + rq.cmd_type = REQ_TYPE_PM_SUSPEND; rq.special = &args; - rq.end_io_data = &rqpm; + rq.data = &rqpm; rqpm.pm_step = ide_pm_state_start_suspend; if (mesg.event == PM_EVENT_PRETHAW) mesg.event = PM_EVENT_FREEZE; @@ -1238,9 +1238,9 @@ static int generic_ide_resume(struct device *dev) memset(&rq, 0, sizeof(rq)); memset(&rqpm, 0, sizeof(rqpm)); memset(&args, 0, sizeof(args)); - rq.flags = REQ_PM_RESUME; + rq.cmd_type = REQ_TYPE_PM_RESUME; rq.special = &args; - rq.end_io_data = &rqpm; + rq.data = &rqpm; rqpm.pm_step = ide_pm_state_start_resume; rqpm.pm_state = PM_EVENT_ON; diff --git a/drivers/ide/legacy/hd.c b/drivers/ide/legacy/hd.c index aebecd8f51c..4ab93114567 100644 --- a/drivers/ide/legacy/hd.c +++ b/drivers/ide/legacy/hd.c @@ -626,7 +626,7 @@ repeat: req->rq_disk->disk_name, (req->cmd == READ)?"read":"writ", cyl, head, sec, nsect, req->buffer); #endif - if (req->flags & REQ_CMD) { + if (blk_fs_request(req)) { switch (rq_data_dir(req)) { case READ: hd_out(disk,nsect,sec,head,cyl,WIN_READ,&read_intr); diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c index a574de5f083..d55b938b1ae 100644 --- a/drivers/ide/pci/atiixp.c +++ b/drivers/ide/pci/atiixp.c @@ -318,6 +318,20 @@ static void __devinit init_hwif_atiixp(ide_hwif_t *hwif) hwif->drives[0].autodma = hwif->autodma; } +static void __devinit init_hwif_sb600_legacy(ide_hwif_t *hwif) +{ + + hwif->atapi_dma = 1; + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + static ide_pci_device_t atiixp_pci_info[] __devinitdata = { { /* 0 */ .name = "ATIIXP", @@ -326,6 +340,12 @@ static ide_pci_device_t atiixp_pci_info[] __devinitdata = { .autodma = AUTODMA, .enablebits = {{0x48,0x01,0x00}, {0x48,0x08,0x00}}, .bootable = ON_BOARD, + },{ /* 1 */ + .name = "ATI SB600 SATA Legacy IDE", + .init_hwif = init_hwif_sb600_legacy, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, } }; @@ -348,6 +368,7 @@ static struct pci_device_id atiixp_pci_tbl[] = { { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, PCI_ANY_ID, PCI_ANY_ID, (PCI_CLASS_STORAGE_IDE<<8)|0x8a, 0xffff05, 1}, { 0, }, }; MODULE_DEVICE_TABLE(pci, atiixp_pci_tbl); diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig index 186737539cf..2769e505f05 100644 --- a/drivers/ieee1394/Kconfig +++ b/drivers/ieee1394/Kconfig @@ -120,12 +120,19 @@ config IEEE1394_VIDEO1394 this option only if you have an IEEE 1394 video device connected to an OHCI-1394 card. +comment "SBP-2 support (for storage devices) requires SCSI" + depends on IEEE1394 && SCSI=n + config IEEE1394_SBP2 tristate "SBP-2 support (Harddisks etc.)" depends on IEEE1394 && SCSI && (PCI || BROKEN) help - This option enables you to use SBP-2 devices connected to your IEEE - 1394 bus. SBP-2 devices include harddrives and DVD devices. + This option enables you to use SBP-2 devices connected to an IEEE + 1394 bus. SBP-2 devices include storage devices like harddisks and + DVD drives, also some other FireWire devices like scanners. + + You should also enable support for disks, CD-ROMs, etc. in the SCSI + configuration section. config IEEE1394_SBP2_PHYS_DMA bool "Enable replacement for physical DMA in SBP2" diff --git a/drivers/ieee1394/csr.c b/drivers/ieee1394/csr.c index 149573db91c..ab0c80f61b9 100644 --- a/drivers/ieee1394/csr.c +++ b/drivers/ieee1394/csr.c @@ -17,11 +17,13 @@ * */ -#include <linux/string.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/param.h> #include <linux/spinlock.h> +#include <linux/string.h> #include "csr1212.h" #include "ieee1394_types.h" @@ -149,31 +151,18 @@ static void host_reset(struct hpsb_host *host) /* * HI == seconds (bits 0:2) - * LO == fraction units of 1/8000 of a second, as per 1394 (bits 19:31) - * - * Convert to units and then to HZ, for comparison to jiffies. - * - * By default this will end up being 800 units, or 100ms (125usec per - * unit). + * LO == fractions of a second in units of 125usec (bits 19:31) * - * NOTE: The spec says 1/8000, but also says we can compute based on 1/8192 - * like CSR specifies. Should make our math less complex. + * Convert SPLIT_TIMEOUT to jiffies. + * The default and minimum as per 1394a-2000 clause 8.3.2.2.6 is 100ms. */ static inline void calculate_expire(struct csr_control *csr) { - unsigned long units; - - /* Take the seconds, and convert to units */ - units = (unsigned long)(csr->split_timeout_hi & 0x07) << 13; - - /* Add in the fractional units */ - units += (unsigned long)(csr->split_timeout_lo >> 19); - - /* Convert to jiffies */ - csr->expire = (unsigned long)(units * HZ) >> 13UL; + unsigned long usecs = + (csr->split_timeout_hi & 0x07) * USEC_PER_SEC + + (csr->split_timeout_lo >> 19) * 125L; - /* Just to keep from rounding low */ - csr->expire++; + csr->expire = usecs_to_jiffies(usecs > 100000L ? usecs : 100000L); HPSB_VERBOSE("CSR: setting expire to %lu, HZ=%u", csr->expire, HZ); } diff --git a/drivers/ieee1394/csr.h b/drivers/ieee1394/csr.h index ea9aa4f53ab..f11546550d8 100644 --- a/drivers/ieee1394/csr.h +++ b/drivers/ieee1394/csr.h @@ -1,75 +1,73 @@ - #ifndef _IEEE1394_CSR_H #define _IEEE1394_CSR_H -#ifdef CONFIG_PREEMPT -#include <linux/sched.h> -#endif +#include <linux/spinlock_types.h> #include "csr1212.h" +#include "ieee1394_types.h" -#define CSR_REGISTER_BASE 0xfffff0000000ULL +#define CSR_REGISTER_BASE 0xfffff0000000ULL /* register offsets relative to CSR_REGISTER_BASE */ -#define CSR_STATE_CLEAR 0x0 -#define CSR_STATE_SET 0x4 -#define CSR_NODE_IDS 0x8 -#define CSR_RESET_START 0xc -#define CSR_SPLIT_TIMEOUT_HI 0x18 -#define CSR_SPLIT_TIMEOUT_LO 0x1c -#define CSR_CYCLE_TIME 0x200 -#define CSR_BUS_TIME 0x204 -#define CSR_BUSY_TIMEOUT 0x210 -#define CSR_BUS_MANAGER_ID 0x21c -#define CSR_BANDWIDTH_AVAILABLE 0x220 -#define CSR_CHANNELS_AVAILABLE 0x224 -#define CSR_CHANNELS_AVAILABLE_HI 0x224 -#define CSR_CHANNELS_AVAILABLE_LO 0x228 -#define CSR_BROADCAST_CHANNEL 0x234 -#define CSR_CONFIG_ROM 0x400 -#define CSR_CONFIG_ROM_END 0x800 -#define CSR_FCP_COMMAND 0xB00 -#define CSR_FCP_RESPONSE 0xD00 -#define CSR_FCP_END 0xF00 -#define CSR_TOPOLOGY_MAP 0x1000 -#define CSR_TOPOLOGY_MAP_END 0x1400 -#define CSR_SPEED_MAP 0x2000 -#define CSR_SPEED_MAP_END 0x3000 +#define CSR_STATE_CLEAR 0x0 +#define CSR_STATE_SET 0x4 +#define CSR_NODE_IDS 0x8 +#define CSR_RESET_START 0xc +#define CSR_SPLIT_TIMEOUT_HI 0x18 +#define CSR_SPLIT_TIMEOUT_LO 0x1c +#define CSR_CYCLE_TIME 0x200 +#define CSR_BUS_TIME 0x204 +#define CSR_BUSY_TIMEOUT 0x210 +#define CSR_BUS_MANAGER_ID 0x21c +#define CSR_BANDWIDTH_AVAILABLE 0x220 +#define CSR_CHANNELS_AVAILABLE 0x224 +#define CSR_CHANNELS_AVAILABLE_HI 0x224 +#define CSR_CHANNELS_AVAILABLE_LO 0x228 +#define CSR_BROADCAST_CHANNEL 0x234 +#define CSR_CONFIG_ROM 0x400 +#define CSR_CONFIG_ROM_END 0x800 +#define CSR_FCP_COMMAND 0xB00 +#define CSR_FCP_RESPONSE 0xD00 +#define CSR_FCP_END 0xF00 +#define CSR_TOPOLOGY_MAP 0x1000 +#define CSR_TOPOLOGY_MAP_END 0x1400 +#define CSR_SPEED_MAP 0x2000 +#define CSR_SPEED_MAP_END 0x3000 /* IEEE 1394 bus specific Configuration ROM Key IDs */ #define IEEE1394_KV_ID_POWER_REQUIREMENTS (0x30) -/* IEEE 1394 Bus Inforamation Block specifics */ +/* IEEE 1394 Bus Information Block specifics */ #define CSR_BUS_INFO_SIZE (5 * sizeof(quadlet_t)) -#define CSR_IRMC_SHIFT 31 -#define CSR_CMC_SHIFT 30 -#define CSR_ISC_SHIFT 29 -#define CSR_BMC_SHIFT 28 -#define CSR_PMC_SHIFT 27 -#define CSR_CYC_CLK_ACC_SHIFT 16 -#define CSR_MAX_REC_SHIFT 12 -#define CSR_MAX_ROM_SHIFT 8 -#define CSR_GENERATION_SHIFT 4 +#define CSR_IRMC_SHIFT 31 +#define CSR_CMC_SHIFT 30 +#define CSR_ISC_SHIFT 29 +#define CSR_BMC_SHIFT 28 +#define CSR_PMC_SHIFT 27 +#define CSR_CYC_CLK_ACC_SHIFT 16 +#define CSR_MAX_REC_SHIFT 12 +#define CSR_MAX_ROM_SHIFT 8 +#define CSR_GENERATION_SHIFT 4 #define CSR_SET_BUS_INFO_GENERATION(csr, gen) \ ((csr)->bus_info_data[2] = \ cpu_to_be32((be32_to_cpu((csr)->bus_info_data[2]) & \ - ~(0xf << CSR_GENERATION_SHIFT)) | \ + ~(0xf << CSR_GENERATION_SHIFT)) | \ (gen) << CSR_GENERATION_SHIFT)) struct csr_control { - spinlock_t lock; - - quadlet_t state; - quadlet_t node_ids; - quadlet_t split_timeout_hi, split_timeout_lo; - unsigned long expire; // Calculated from split_timeout - quadlet_t cycle_time; - quadlet_t bus_time; - quadlet_t bus_manager_id; - quadlet_t bandwidth_available; - quadlet_t channels_available_hi, channels_available_lo; + spinlock_t lock; + + quadlet_t state; + quadlet_t node_ids; + quadlet_t split_timeout_hi, split_timeout_lo; + unsigned long expire; /* Calculated from split_timeout */ + quadlet_t cycle_time; + quadlet_t bus_time; + quadlet_t bus_manager_id; + quadlet_t bandwidth_available; + quadlet_t channels_available_hi, channels_available_lo; quadlet_t broadcast_channel; /* Bus Info */ @@ -84,8 +82,8 @@ struct csr_control { struct csr1212_csr *rom; - quadlet_t topology_map[256]; - quadlet_t speed_map[1024]; + quadlet_t topology_map[256]; + quadlet_t speed_map[1024]; }; extern struct csr1212_bus_ops csr_bus_ops; @@ -93,4 +91,9 @@ extern struct csr1212_bus_ops csr_bus_ops; int init_csr(void); void cleanup_csr(void); +/* hpsb_update_config_rom() is deprecated */ +struct hpsb_host; +int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom, + size_t size, unsigned char rom_version); + #endif /* _IEEE1394_CSR_H */ diff --git a/drivers/ieee1394/dma.c b/drivers/ieee1394/dma.c index ca5167de707..c68f328e1a2 100644 --- a/drivers/ieee1394/dma.c +++ b/drivers/ieee1394/dma.c @@ -7,10 +7,13 @@ * directory of the kernel sources for details. */ +#include <linux/mm.h> #include <linux/module.h> -#include <linux/vmalloc.h> +#include <linux/pci.h> #include <linux/slab.h> -#include <linux/mm.h> +#include <linux/vmalloc.h> +#include <asm/scatterlist.h> + #include "dma.h" /* dma_prog_region */ diff --git a/drivers/ieee1394/dma.h b/drivers/ieee1394/dma.h index 061550a6fb9..a1682aba71c 100644 --- a/drivers/ieee1394/dma.h +++ b/drivers/ieee1394/dma.h @@ -10,69 +10,91 @@ #ifndef IEEE1394_DMA_H #define IEEE1394_DMA_H -#include <linux/pci.h> -#include <asm/scatterlist.h> - -/* struct dma_prog_region - - a small, physically-contiguous DMA buffer with random-access, - synchronous usage characteristics -*/ - +#include <asm/types.h> + +struct pci_dev; +struct scatterlist; +struct vm_area_struct; + +/** + * struct dma_prog_region - small contiguous DMA buffer + * @kvirt: kernel virtual address + * @dev: PCI device + * @n_pages: number of kernel pages + * @bus_addr: base bus address + * + * a small, physically contiguous DMA buffer with random-access, synchronous + * usage characteristics + */ struct dma_prog_region { - unsigned char *kvirt; /* kernel virtual address */ - struct pci_dev *dev; /* PCI device */ - unsigned int n_pages; /* # of kernel pages */ - dma_addr_t bus_addr; /* base bus address */ + unsigned char *kvirt; + struct pci_dev *dev; + unsigned int n_pages; + dma_addr_t bus_addr; }; /* clear out all fields but do not allocate any memory */ void dma_prog_region_init(struct dma_prog_region *prog); -int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, struct pci_dev *dev); +int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, + struct pci_dev *dev); void dma_prog_region_free(struct dma_prog_region *prog); -static inline dma_addr_t dma_prog_region_offset_to_bus(struct dma_prog_region *prog, unsigned long offset) +static inline dma_addr_t dma_prog_region_offset_to_bus( + struct dma_prog_region *prog, unsigned long offset) { return prog->bus_addr + offset; } -/* struct dma_region - - a large, non-physically-contiguous DMA buffer with streaming, - asynchronous usage characteristics -*/ - +/** + * struct dma_region - large non-contiguous DMA buffer + * @virt: kernel virtual address + * @dev: PCI device + * @n_pages: number of kernel pages + * @n_dma_pages: number of IOMMU pages + * @sglist: IOMMU mapping + * @direction: PCI_DMA_TODEVICE, etc. + * + * a large, non-physically-contiguous DMA buffer with streaming, asynchronous + * usage characteristics + */ struct dma_region { - unsigned char *kvirt; /* kernel virtual address */ - struct pci_dev *dev; /* PCI device */ - unsigned int n_pages; /* # of kernel pages */ - unsigned int n_dma_pages; /* # of IOMMU pages */ - struct scatterlist *sglist; /* IOMMU mapping */ - int direction; /* PCI_DMA_TODEVICE, etc */ + unsigned char *kvirt; + struct pci_dev *dev; + unsigned int n_pages; + unsigned int n_dma_pages; + struct scatterlist *sglist; + int direction; }; /* clear out all fields but do not allocate anything */ void dma_region_init(struct dma_region *dma); /* allocate the buffer and map it to the IOMMU */ -int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, struct pci_dev *dev, int direction); +int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, + struct pci_dev *dev, int direction); /* unmap and free the buffer */ void dma_region_free(struct dma_region *dma); /* sync the CPU's view of the buffer */ -void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, unsigned long len); +void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, + unsigned long len); + /* sync the IO bus' view of the buffer */ -void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, unsigned long len); +void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, + unsigned long len); /* map the buffer into a user space process */ -int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma); +int dma_region_mmap(struct dma_region *dma, struct file *file, + struct vm_area_struct *vma); /* macro to index into a DMA region (or dma_prog_region) */ -#define dma_region_i(_dma, _type, _index) ( ((_type*) ((_dma)->kvirt)) + (_index) ) +#define dma_region_i(_dma, _type, _index) \ + ( ((_type*) ((_dma)->kvirt)) + (_index) ) /* return the DMA bus address of the byte with the given offset - relative to the beginning of the dma_region */ -dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, unsigned long offset); + * relative to the beginning of the dma_region */ +dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, + unsigned long offset); #endif /* IEEE1394_DMA_H */ diff --git a/drivers/ieee1394/dv1394-private.h b/drivers/ieee1394/dv1394-private.h index 80b5ac7fe38..7d1d2845b42 100644 --- a/drivers/ieee1394/dv1394-private.h +++ b/drivers/ieee1394/dv1394-private.h @@ -460,7 +460,7 @@ struct video_card { int dma_running; /* - 3) the sleeping semaphore 'sem' - this is used from process context only, + 3) the sleeping mutex 'mtx' - this is used from process context only, to serialize various operations on the video_card. Even though only one open() is allowed, we still need to prevent multiple threads of execution from entering calls like read, write, ioctl, etc. @@ -468,9 +468,9 @@ struct video_card { I honestly can't think of a good reason to use dv1394 from several threads at once, but we need to serialize anyway to prevent oopses =). - NOTE: if you need both spinlock and sem, take sem first to avoid deadlock! + NOTE: if you need both spinlock and mtx, take mtx first to avoid deadlock! */ - struct semaphore sem; + struct mutex mtx; /* people waiting for buffer space, please form a line here... */ wait_queue_head_t waitq; diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c index 87532dd4337..6c72f04b2b5 100644 --- a/drivers/ieee1394/dv1394.c +++ b/drivers/ieee1394/dv1394.c @@ -95,6 +95,7 @@ #include <linux/fs.h> #include <linux/poll.h> #include <linux/smp_lock.h> +#include <linux/mutex.h> #include <linux/bitops.h> #include <asm/byteorder.h> #include <asm/atomic.h> @@ -110,15 +111,15 @@ #include <linux/compat.h> #include <linux/cdev.h> +#include "dv1394.h" +#include "dv1394-private.h" +#include "highlevel.h" +#include "hosts.h" #include "ieee1394.h" +#include "ieee1394_core.h" +#include "ieee1394_hotplug.h" #include "ieee1394_types.h" #include "nodemgr.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "highlevel.h" -#include "dv1394.h" -#include "dv1394-private.h" - #include "ohci1394.h" /* DEBUG LEVELS: @@ -136,13 +137,13 @@ #if DV1394_DEBUG_LEVEL >= 2 #define irq_printk( args... ) printk( args ) #else -#define irq_printk( args... ) +#define irq_printk( args... ) do {} while (0) #endif #if DV1394_DEBUG_LEVEL >= 1 #define debug_printk( args... ) printk( args) #else -#define debug_printk( args... ) +#define debug_printk( args... ) do {} while (0) #endif /* issue a dummy PCI read to force the preceding write @@ -247,7 +248,7 @@ static void frame_delete(struct frame *f) Frame_prepare() must be called OUTSIDE the video->spinlock. However, frame_prepare() must still be serialized, so - it should be called WITH the video->sem taken. + it should be called WITH the video->mtx taken. */ static void frame_prepare(struct video_card *video, unsigned int this_frame) @@ -1271,7 +1272,7 @@ static int dv1394_mmap(struct file *file, struct vm_area_struct *vma) int retval = -EINVAL; /* serialize mmap */ - down(&video->sem); + mutex_lock(&video->mtx); if ( ! video_card_initialized(video) ) { retval = do_dv1394_init_default(video); @@ -1281,7 +1282,7 @@ static int dv1394_mmap(struct file *file, struct vm_area_struct *vma) retval = dma_region_mmap(&video->dv_buf, file, vma); out: - up(&video->sem); + mutex_unlock(&video->mtx); return retval; } @@ -1337,17 +1338,17 @@ static ssize_t dv1394_write(struct file *file, const char __user *buffer, size_t /* serialize this to prevent multi-threaded mayhem */ if (file->f_flags & O_NONBLOCK) { - if (down_trylock(&video->sem)) + if (!mutex_trylock(&video->mtx)) return -EAGAIN; } else { - if (down_interruptible(&video->sem)) + if (mutex_lock_interruptible(&video->mtx)) return -ERESTARTSYS; } if ( !video_card_initialized(video) ) { ret = do_dv1394_init_default(video); if (ret) { - up(&video->sem); + mutex_unlock(&video->mtx); return ret; } } @@ -1418,7 +1419,7 @@ static ssize_t dv1394_write(struct file *file, const char __user *buffer, size_t remove_wait_queue(&video->waitq, &wait); set_current_state(TASK_RUNNING); - up(&video->sem); + mutex_unlock(&video->mtx); return ret; } @@ -1434,17 +1435,17 @@ static ssize_t dv1394_read(struct file *file, char __user *buffer, size_t count /* serialize this to prevent multi-threaded mayhem */ if (file->f_flags & O_NONBLOCK) { - if (down_trylock(&video->sem)) + if (!mutex_trylock(&video->mtx)) return -EAGAIN; } else { - if (down_interruptible(&video->sem)) + if (mutex_lock_interruptible(&video->mtx)) return -ERESTARTSYS; } if ( !video_card_initialized(video) ) { ret = do_dv1394_init_default(video); if (ret) { - up(&video->sem); + mutex_unlock(&video->mtx); return ret; } video->continuity_counter = -1; @@ -1526,7 +1527,7 @@ static ssize_t dv1394_read(struct file *file, char __user *buffer, size_t count remove_wait_queue(&video->waitq, &wait); set_current_state(TASK_RUNNING); - up(&video->sem); + mutex_unlock(&video->mtx); return ret; } @@ -1547,12 +1548,12 @@ static long dv1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg) /* serialize this to prevent multi-threaded mayhem */ if (file->f_flags & O_NONBLOCK) { - if (down_trylock(&video->sem)) { + if (!mutex_trylock(&video->mtx)) { unlock_kernel(); return -EAGAIN; } } else { - if (down_interruptible(&video->sem)) { + if (mutex_lock_interruptible(&video->mtx)) { unlock_kernel(); return -ERESTARTSYS; } @@ -1778,7 +1779,7 @@ static long dv1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } out: - up(&video->sem); + mutex_unlock(&video->mtx); unlock_kernel(); return ret; } @@ -2253,7 +2254,7 @@ static int dv1394_init(struct ti_ohci *ohci, enum pal_or_ntsc format, enum modes clear_bit(0, &video->open); spin_lock_init(&video->spinlock); video->dma_running = 0; - init_MUTEX(&video->sem); + mutex_init(&video->mtx); init_waitqueue_head(&video->waitq); video->fasync = NULL; diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c index 2d5b57be98c..8a7b8fab623 100644 --- a/drivers/ieee1394/eth1394.c +++ b/drivers/ieee1394/eth1394.c @@ -64,19 +64,19 @@ #include <linux/ethtool.h> #include <asm/uaccess.h> #include <asm/delay.h> -#include <asm/semaphore.h> #include <net/arp.h> +#include "config_roms.h" #include "csr1212.h" -#include "ieee1394_types.h" +#include "eth1394.h" +#include "highlevel.h" +#include "ieee1394.h" #include "ieee1394_core.h" +#include "ieee1394_hotplug.h" #include "ieee1394_transactions.h" -#include "ieee1394.h" -#include "highlevel.h" +#include "ieee1394_types.h" #include "iso.h" #include "nodemgr.h" -#include "eth1394.h" -#include "config_roms.h" #define ETH1394_PRINT_G(level, fmt, args...) \ printk(level "%s: " fmt, driver_name, ## args) diff --git a/drivers/ieee1394/highlevel.h b/drivers/ieee1394/highlevel.h index e119fb87e5b..50f2dd2c7e2 100644 --- a/drivers/ieee1394/highlevel.h +++ b/drivers/ieee1394/highlevel.h @@ -1,60 +1,61 @@ - #ifndef IEEE1394_HIGHLEVEL_H #define IEEE1394_HIGHLEVEL_H +#include <linux/list.h> +#include <linux/spinlock_types.h> +#include <linux/types.h> -struct hpsb_address_serve { - struct list_head host_list; /* per host list */ +struct module; - struct list_head hl_list; /* hpsb_highlevel list */ +#include "ieee1394_types.h" - struct hpsb_address_ops *op; +struct hpsb_host; +/* internal to ieee1394 core */ +struct hpsb_address_serve { + struct list_head host_list; /* per host list */ + struct list_head hl_list; /* hpsb_highlevel list */ + struct hpsb_address_ops *op; struct hpsb_host *host; - - /* first address handled and first address behind, quadlet aligned */ - u64 start, end; + u64 start; /* first address handled, quadlet aligned */ + u64 end; /* first address behind, quadlet aligned */ }; - -/* - * The above structs are internal to highlevel driver handling. Only the - * following structures are of interest to actual highlevel drivers. - */ +/* Only the following structures are of interest to actual highlevel drivers. */ struct hpsb_highlevel { struct module *owner; const char *name; - /* Any of the following pointers can legally be NULL, except for - * iso_receive which can only be NULL when you don't request - * channels. */ + /* Any of the following pointers can legally be NULL, except for + * iso_receive which can only be NULL when you don't request + * channels. */ - /* New host initialized. Will also be called during - * hpsb_register_highlevel for all hosts already installed. */ - void (*add_host) (struct hpsb_host *host); + /* New host initialized. Will also be called during + * hpsb_register_highlevel for all hosts already installed. */ + void (*add_host)(struct hpsb_host *host); - /* Host about to be removed. Will also be called during - * hpsb_unregister_highlevel once for each host. */ - void (*remove_host) (struct hpsb_host *host); + /* Host about to be removed. Will also be called during + * hpsb_unregister_highlevel once for each host. */ + void (*remove_host)(struct hpsb_host *host); - /* Host experienced bus reset with possible configuration changes. + /* Host experienced bus reset with possible configuration changes. * Note that this one may occur during interrupt/bottom half handling. * You can not expect to be able to do stock hpsb_reads. */ - void (*host_reset) (struct hpsb_host *host); + void (*host_reset)(struct hpsb_host *host); - /* An isochronous packet was received. Channel contains the channel - * number for your convenience, it is also contained in the included - * packet header (first quadlet, CRCs are missing). You may get called - * for channel/host combinations you did not request. */ - void (*iso_receive) (struct hpsb_host *host, int channel, - quadlet_t *data, size_t length); + /* An isochronous packet was received. Channel contains the channel + * number for your convenience, it is also contained in the included + * packet header (first quadlet, CRCs are missing). You may get called + * for channel/host combinations you did not request. */ + void (*iso_receive)(struct hpsb_host *host, int channel, + quadlet_t *data, size_t length); - /* A write request was received on either the FCP_COMMAND (direction = - * 0) or the FCP_RESPONSE (direction = 1) register. The cts arg - * contains the cts field (first byte of data). */ - void (*fcp_request) (struct hpsb_host *host, int nodeid, int direction, - int cts, u8 *data, size_t length); + /* A write request was received on either the FCP_COMMAND (direction = + * 0) or the FCP_RESPONSE (direction = 1) register. The cts arg + * contains the cts field (first byte of data). */ + void (*fcp_request)(struct hpsb_host *host, int nodeid, int direction, + int cts, u8 *data, size_t length); /* These are initialized by the subsystem when the * hpsb_higlevel is registered. */ @@ -67,61 +68,62 @@ struct hpsb_highlevel { }; struct hpsb_address_ops { - /* - * Null function pointers will make the respective operation complete - * with RCODE_TYPE_ERROR. Makes for easy to implement read-only - * registers (just leave everything but read NULL). - * - * All functions shall return appropriate IEEE 1394 rcodes. - */ - - /* These functions have to implement block reads for themselves. */ - /* These functions either return a response code - or a negative number. In the first case a response will be generated; in the - later case, no response will be sent and the driver, that handled the request - will send the response itself - */ - int (*read) (struct hpsb_host *host, int nodeid, quadlet_t *buffer, - u64 addr, size_t length, u16 flags); - int (*write) (struct hpsb_host *host, int nodeid, int destid, - quadlet_t *data, u64 addr, size_t length, u16 flags); - - /* Lock transactions: write results of ext_tcode operation into - * *store. */ - int (*lock) (struct hpsb_host *host, int nodeid, quadlet_t *store, - u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, u16 flags); - int (*lock64) (struct hpsb_host *host, int nodeid, octlet_t *store, - u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags); + /* + * Null function pointers will make the respective operation complete + * with RCODE_TYPE_ERROR. Makes for easy to implement read-only + * registers (just leave everything but read NULL). + * + * All functions shall return appropriate IEEE 1394 rcodes. + */ + + /* These functions have to implement block reads for themselves. + * + * These functions either return a response code or a negative number. + * In the first case a response will be generated. In the latter case, + * no response will be sent and the driver which handled the request + * will send the response itself. */ + int (*read)(struct hpsb_host *host, int nodeid, quadlet_t *buffer, + u64 addr, size_t length, u16 flags); + int (*write)(struct hpsb_host *host, int nodeid, int destid, + quadlet_t *data, u64 addr, size_t length, u16 flags); + + /* Lock transactions: write results of ext_tcode operation into + * *store. */ + int (*lock)(struct hpsb_host *host, int nodeid, quadlet_t *store, + u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, + u16 flags); + int (*lock64)(struct hpsb_host *host, int nodeid, octlet_t *store, + u64 addr, octlet_t data, octlet_t arg, int ext_tcode, + u16 flags); }; - void highlevel_add_host(struct hpsb_host *host); void highlevel_remove_host(struct hpsb_host *host); void highlevel_host_reset(struct hpsb_host *host); - -/* these functions are called to handle transactions. They are called, when - a packet arrives. The flags argument contains the second word of the first header - quadlet of the incoming packet (containing transaction label, retry code, - transaction code and priority). These functions either return a response code - or a negative number. In the first case a response will be generated; in the - later case, no response will be sent and the driver, that handled the request - will send the response itself. -*/ -int highlevel_read(struct hpsb_host *host, int nodeid, void *data, - u64 addr, unsigned int length, u16 flags); -int highlevel_write(struct hpsb_host *host, int nodeid, int destid, - void *data, u64 addr, unsigned int length, u16 flags); +/* + * These functions are called to handle transactions. They are called when a + * packet arrives. The flags argument contains the second word of the first + * header quadlet of the incoming packet (containing transaction label, retry + * code, transaction code and priority). These functions either return a + * response code or a negative number. In the first case a response will be + * generated. In the latter case, no response will be sent and the driver which + * handled the request will send the response itself. + */ +int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr, + unsigned int length, u16 flags); +int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data, + u64 addr, unsigned int length, u16 flags); int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store, - u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, u16 flags); + u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, + u16 flags); int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store, - u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags); + u64 addr, octlet_t data, octlet_t arg, int ext_tcode, + u16 flags); -void highlevel_iso_receive(struct hpsb_host *host, void *data, - size_t length); +void highlevel_iso_receive(struct hpsb_host *host, void *data, size_t length); void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction, - void *data, size_t length); - + void *data, size_t length); /* * Register highlevel driver. The name pointer has to stay valid at all times @@ -132,13 +134,15 @@ void hpsb_unregister_highlevel(struct hpsb_highlevel *hl); /* * Register handlers for host address spaces. Start and end are 48 bit pointers - * and have to be quadlet aligned (end points to the first address behind the - * handled addresses. This function can be called multiple times for a single - * hpsb_highlevel to implement sparse register sets. The requested region must - * not overlap any previously allocated region, otherwise registering will fail. + * and have to be quadlet aligned. Argument "end" points to the first address + * behind the handled addresses. This function can be called multiple times for + * a single hpsb_highlevel to implement sparse register sets. The requested + * region must not overlap any previously allocated region, otherwise + * registering will fail. * - * It returns true for successful allocation. There is no unregister function, - * all address spaces are deallocated together with the hpsb_highlevel. + * It returns true for successful allocation. Address spaces can be + * unregistered with hpsb_unregister_addrspace. All remaining address spaces + * are automatically deallocated together with the hpsb_highlevel. */ u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, @@ -146,20 +150,18 @@ u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl, u64 size, u64 alignment, u64 start, u64 end); int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, - struct hpsb_address_ops *ops, u64 start, u64 end); - + struct hpsb_address_ops *ops, u64 start, u64 end); int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, - u64 start); + u64 start); /* * Enable or disable receving a certain isochronous channel through the * iso_receive op. */ int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, - unsigned int channel); + unsigned int channel); void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, - unsigned int channel); - + unsigned int channel); /* Retrieve a hostinfo pointer bound to this driver/host */ void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); @@ -172,19 +174,24 @@ void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); /* Set an alternate lookup key for the hostinfo bound to this driver/host */ -void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned long key); +void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, + unsigned long key); -/* Retrieve the alternate lookup key for the hostinfo bound to this driver/host */ -unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host); +/* Retrieve the alternate lookup key for the hostinfo bound to this + * driver/host */ +unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl, + struct hpsb_host *host); /* Retrieve a hostinfo pointer bound to this driver using its alternate key */ void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key); /* Set the hostinfo pointer to something useful. Usually follows a call to * hpsb_create_hostinfo, where the size is 0. */ -int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, void *data); +int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, + void *data); /* Retrieve hpsb_host using a highlevel handle and a key */ -struct hpsb_host *hpsb_get_host_bykey(struct hpsb_highlevel *hl, unsigned long key); +struct hpsb_host *hpsb_get_host_bykey(struct hpsb_highlevel *hl, + unsigned long key); #endif /* IEEE1394_HIGHLEVEL_H */ diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c index 4feead4a35c..d90a3a1898c 100644 --- a/drivers/ieee1394/hosts.c +++ b/drivers/ieee1394/hosts.c @@ -90,6 +90,16 @@ static int alloc_hostnum_cb(struct hpsb_host *host, void *__data) return 0; } +/* + * The pending_packet_queue is special in that it's processed + * from hardirq context too (such as hpsb_bus_reset()). Hence + * split the lock class from the usual networking skb-head + * lock class by using a separate key for it: + */ +static struct lock_class_key pending_packet_queue_key; + +static DEFINE_MUTEX(host_num_alloc); + /** * hpsb_alloc_host - allocate a new host controller. * @drv: the driver that will manage the host controller @@ -105,16 +115,6 @@ static int alloc_hostnum_cb(struct hpsb_host *host, void *__data) * Return Value: a pointer to the &hpsb_host if successful, %NULL if * no memory was available. */ -static DEFINE_MUTEX(host_num_alloc); - -/* - * The pending_packet_queue is special in that it's processed - * from hardirq context too (such as hpsb_bus_reset()). Hence - * split the lock class from the usual networking skb-head - * lock class by using a separate key for it: - */ -static struct lock_class_key pending_packet_queue_key; - struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra, struct device *dev) { @@ -143,9 +143,6 @@ struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra, for (i = 2; i < 16; i++) h->csr.gen_timestamp[i] = jiffies - 60 * HZ; - for (i = 0; i < ARRAY_SIZE(h->tpool); i++) - HPSB_TPOOL_INIT(&h->tpool[i]); - atomic_set(&h->generation, 0); INIT_WORK(&h->delayed_reset, delayed_reset_bus, h); diff --git a/drivers/ieee1394/hosts.h b/drivers/ieee1394/hosts.h index 9ad4b246307..bc6dbfadb89 100644 --- a/drivers/ieee1394/hosts.h +++ b/drivers/ieee1394/hosts.h @@ -2,17 +2,19 @@ #define _IEEE1394_HOSTS_H #include <linux/device.h> -#include <linux/wait.h> #include <linux/list.h> -#include <linux/timer.h> #include <linux/skbuff.h> +#include <linux/timer.h> +#include <linux/types.h> +#include <linux/workqueue.h> +#include <asm/atomic.h> -#include <asm/semaphore.h> +struct pci_dev; +struct module; #include "ieee1394_types.h" #include "csr.h" - struct hpsb_packet; struct hpsb_iso; @@ -33,7 +35,6 @@ struct hpsb_host { int node_count; /* number of identified nodes on this bus */ int selfid_count; /* total number of SelfIDs received */ int nodes_active; /* number of nodes with active link layer */ - u8 speed[ALL_NODES]; /* speed between each node and local node */ nodeid_t node_id; /* node ID of this host */ nodeid_t irm_id; /* ID of this bus' isochronous resource manager */ @@ -53,31 +54,29 @@ struct hpsb_host { int reset_retries; quadlet_t *topology_map; u8 *speed_map; - struct csr_control csr; - - /* Per node tlabel pool allocation */ - struct hpsb_tlabel_pool tpool[ALL_NODES]; + int id; struct hpsb_host_driver *driver; - struct pci_dev *pdev; - - int id; - struct device device; struct class_device class_dev; int update_config_rom; struct work_struct delayed_reset; - unsigned int config_roms; struct list_head addr_space; u64 low_addr_space; /* upper bound of physical DMA area */ u64 middle_addr_space; /* upper bound of posted write area */ -}; + u8 speed[ALL_NODES]; /* speed between each node and local node */ + + /* per node tlabel allocation */ + u8 next_tl[ALL_NODES]; + struct { DECLARE_BITMAP(map, 64); } tl_pool[ALL_NODES]; + struct csr_control csr; +}; enum devctl_cmd { /* Host is requested to reset its bus and cancel all outstanding async @@ -112,7 +111,7 @@ enum devctl_cmd { enum isoctl_cmd { /* rawiso API - see iso.h for the meanings of these commands - (they correspond exactly to the hpsb_iso_* API functions) + * (they correspond exactly to the hpsb_iso_* API functions) * INIT = allocate resources * START = begin transmission/reception * STOP = halt transmission/reception @@ -160,7 +159,8 @@ struct hpsb_host_driver { /* The hardware driver may optionally support a function that is used * to set the hardware ConfigROM if the hardware supports handling * reads to the ConfigROM on its own. */ - void (*set_hw_config_rom) (struct hpsb_host *host, quadlet_t *config_rom); + void (*set_hw_config_rom)(struct hpsb_host *host, + quadlet_t *config_rom); /* This function shall implement packet transmission based on * packet->type. It shall CRC both parts of the packet (unless @@ -170,20 +170,21 @@ struct hpsb_host_driver { * called. Return 0 on success, negative errno on failure. * NOTE: The function must be callable in interrupt context. */ - int (*transmit_packet) (struct hpsb_host *host, - struct hpsb_packet *packet); + int (*transmit_packet)(struct hpsb_host *host, + struct hpsb_packet *packet); /* This function requests miscellanous services from the driver, see * above for command codes and expected actions. Return -1 for unknown * command, though that should never happen. */ - int (*devctl) (struct hpsb_host *host, enum devctl_cmd command, int arg); + int (*devctl)(struct hpsb_host *host, enum devctl_cmd command, int arg); /* ISO transmission/reception functions. Return 0 on success, -1 * (or -EXXX errno code) on failure. If the low-level driver does not * support the new ISO API, set isoctl to NULL. */ - int (*isoctl) (struct hpsb_iso *iso, enum isoctl_cmd command, unsigned long arg); + int (*isoctl)(struct hpsb_iso *iso, enum isoctl_cmd command, + unsigned long arg); /* This function is mainly to redirect local CSR reads/locks to the iso * management registers (bus manager id, bandwidth available, channels @@ -196,19 +197,11 @@ struct hpsb_host_driver { quadlet_t data, quadlet_t compare); }; - struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra, struct device *dev); int hpsb_add_host(struct hpsb_host *host); void hpsb_remove_host(struct hpsb_host *h); -/* The following 2 functions are deprecated and will be removed when the - * raw1394/libraw1394 update is complete. */ -int hpsb_update_config_rom(struct hpsb_host *host, - const quadlet_t *new_rom, size_t size, unsigned char rom_version); -int hpsb_get_config_rom(struct hpsb_host *host, quadlet_t *buffer, - size_t buffersize, size_t *rom_size, unsigned char *rom_version); - /* Updates the configuration rom image of a host. rom_version must be the * current version, otherwise it will fail with return value -1. If this * host does not support config-rom-update, it will return -EINVAL. diff --git a/drivers/ieee1394/ieee1394-ioctl.h b/drivers/ieee1394/ieee1394-ioctl.h index 15670398634..8f207508ed1 100644 --- a/drivers/ieee1394/ieee1394-ioctl.h +++ b/drivers/ieee1394/ieee1394-ioctl.h @@ -1,5 +1,7 @@ -/* Base file for all ieee1394 ioctl's. Linux-1394 has allocated base '#' - * with a range of 0x00-0x3f. */ +/* + * Base file for all ieee1394 ioctl's. + * Linux-1394 has allocated base '#' with a range of 0x00-0x3f. + */ #ifndef __IEEE1394_IOCTL_H #define __IEEE1394_IOCTL_H @@ -96,8 +98,7 @@ _IOW ('#', 0x27, struct raw1394_iso_packets) #define RAW1394_IOC_ISO_XMIT_SYNC \ _IO ('#', 0x28) -#define RAW1394_IOC_ISO_RECV_FLUSH \ +#define RAW1394_IOC_ISO_RECV_FLUSH \ _IO ('#', 0x29) - #endif /* __IEEE1394_IOCTL_H */ diff --git a/drivers/ieee1394/ieee1394.h b/drivers/ieee1394/ieee1394.h index 936d776de00..40492074c01 100644 --- a/drivers/ieee1394/ieee1394.h +++ b/drivers/ieee1394/ieee1394.h @@ -5,77 +5,78 @@ #ifndef _IEEE1394_IEEE1394_H #define _IEEE1394_IEEE1394_H -#define TCODE_WRITEQ 0x0 -#define TCODE_WRITEB 0x1 -#define TCODE_WRITE_RESPONSE 0x2 -#define TCODE_READQ 0x4 -#define TCODE_READB 0x5 -#define TCODE_READQ_RESPONSE 0x6 -#define TCODE_READB_RESPONSE 0x7 -#define TCODE_CYCLE_START 0x8 -#define TCODE_LOCK_REQUEST 0x9 -#define TCODE_ISO_DATA 0xa -#define TCODE_STREAM_DATA 0xa -#define TCODE_LOCK_RESPONSE 0xb - -#define RCODE_COMPLETE 0x0 -#define RCODE_CONFLICT_ERROR 0x4 -#define RCODE_DATA_ERROR 0x5 -#define RCODE_TYPE_ERROR 0x6 -#define RCODE_ADDRESS_ERROR 0x7 - -#define EXTCODE_MASK_SWAP 0x1 -#define EXTCODE_COMPARE_SWAP 0x2 -#define EXTCODE_FETCH_ADD 0x3 -#define EXTCODE_LITTLE_ADD 0x4 -#define EXTCODE_BOUNDED_ADD 0x5 -#define EXTCODE_WRAP_ADD 0x6 - -#define ACK_COMPLETE 0x1 -#define ACK_PENDING 0x2 -#define ACK_BUSY_X 0x4 -#define ACK_BUSY_A 0x5 -#define ACK_BUSY_B 0x6 -#define ACK_TARDY 0xb -#define ACK_CONFLICT_ERROR 0xc -#define ACK_DATA_ERROR 0xd -#define ACK_TYPE_ERROR 0xe -#define ACK_ADDRESS_ERROR 0xf +#define TCODE_WRITEQ 0x0 +#define TCODE_WRITEB 0x1 +#define TCODE_WRITE_RESPONSE 0x2 +#define TCODE_READQ 0x4 +#define TCODE_READB 0x5 +#define TCODE_READQ_RESPONSE 0x6 +#define TCODE_READB_RESPONSE 0x7 +#define TCODE_CYCLE_START 0x8 +#define TCODE_LOCK_REQUEST 0x9 +#define TCODE_ISO_DATA 0xa +#define TCODE_STREAM_DATA 0xa +#define TCODE_LOCK_RESPONSE 0xb + +#define RCODE_COMPLETE 0x0 +#define RCODE_CONFLICT_ERROR 0x4 +#define RCODE_DATA_ERROR 0x5 +#define RCODE_TYPE_ERROR 0x6 +#define RCODE_ADDRESS_ERROR 0x7 + +#define EXTCODE_MASK_SWAP 0x1 +#define EXTCODE_COMPARE_SWAP 0x2 +#define EXTCODE_FETCH_ADD 0x3 +#define EXTCODE_LITTLE_ADD 0x4 +#define EXTCODE_BOUNDED_ADD 0x5 +#define EXTCODE_WRAP_ADD 0x6 + +#define ACK_COMPLETE 0x1 +#define ACK_PENDING 0x2 +#define ACK_BUSY_X 0x4 +#define ACK_BUSY_A 0x5 +#define ACK_BUSY_B 0x6 +#define ACK_TARDY 0xb +#define ACK_CONFLICT_ERROR 0xc +#define ACK_DATA_ERROR 0xd +#define ACK_TYPE_ERROR 0xe +#define ACK_ADDRESS_ERROR 0xf /* Non-standard "ACK codes" for internal use */ -#define ACKX_NONE (-1) -#define ACKX_SEND_ERROR (-2) -#define ACKX_ABORTED (-3) -#define ACKX_TIMEOUT (-4) - - -#define IEEE1394_SPEED_100 0x00 -#define IEEE1394_SPEED_200 0x01 -#define IEEE1394_SPEED_400 0x02 -#define IEEE1394_SPEED_800 0x03 -#define IEEE1394_SPEED_1600 0x04 -#define IEEE1394_SPEED_3200 0x05 +#define ACKX_NONE (-1) +#define ACKX_SEND_ERROR (-2) +#define ACKX_ABORTED (-3) +#define ACKX_TIMEOUT (-4) + +#define IEEE1394_SPEED_100 0x00 +#define IEEE1394_SPEED_200 0x01 +#define IEEE1394_SPEED_400 0x02 +#define IEEE1394_SPEED_800 0x03 +#define IEEE1394_SPEED_1600 0x04 +#define IEEE1394_SPEED_3200 0x05 + /* The current highest tested speed supported by the subsystem */ -#define IEEE1394_SPEED_MAX IEEE1394_SPEED_800 +#define IEEE1394_SPEED_MAX IEEE1394_SPEED_800 /* Maps speed values above to a string representation */ extern const char *hpsb_speedto_str[]; - /* 1394a cable PHY packets */ -#define SELFID_PWRCL_NO_POWER 0x0 -#define SELFID_PWRCL_PROVIDE_15W 0x1 -#define SELFID_PWRCL_PROVIDE_30W 0x2 -#define SELFID_PWRCL_PROVIDE_45W 0x3 -#define SELFID_PWRCL_USE_1W 0x4 -#define SELFID_PWRCL_USE_3W 0x5 -#define SELFID_PWRCL_USE_6W 0x6 -#define SELFID_PWRCL_USE_10W 0x7 - -#define SELFID_PORT_CHILD 0x3 -#define SELFID_PORT_PARENT 0x2 -#define SELFID_PORT_NCONN 0x1 -#define SELFID_PORT_NONE 0x0 +#define SELFID_PWRCL_NO_POWER 0x0 +#define SELFID_PWRCL_PROVIDE_15W 0x1 +#define SELFID_PWRCL_PROVIDE_30W 0x2 +#define SELFID_PWRCL_PROVIDE_45W 0x3 +#define SELFID_PWRCL_USE_1W 0x4 +#define SELFID_PWRCL_USE_3W 0x5 +#define SELFID_PWRCL_USE_6W 0x6 +#define SELFID_PWRCL_USE_10W 0x7 + +#define SELFID_PORT_CHILD 0x3 +#define SELFID_PORT_PARENT 0x2 +#define SELFID_PORT_NCONN 0x1 +#define SELFID_PORT_NONE 0x0 + +#define SELFID_SPEED_UNKNOWN 0x3 /* 1394b PHY */ #define PHYPACKET_LINKON 0x40000000 #define PHYPACKET_PHYCONFIG_R 0x00800000 @@ -91,76 +92,76 @@ extern const char *hpsb_speedto_str[]; #define EXTPHYPACKET_TYPEMASK 0xC0FC0000 -#define PHYPACKET_PORT_SHIFT 24 -#define PHYPACKET_GAPCOUNT_SHIFT 16 +#define PHYPACKET_PORT_SHIFT 24 +#define PHYPACKET_GAPCOUNT_SHIFT 16 /* 1394a PHY register map bitmasks */ -#define PHY_00_PHYSICAL_ID 0xFC -#define PHY_00_R 0x02 /* Root */ -#define PHY_00_PS 0x01 /* Power Status*/ -#define PHY_01_RHB 0x80 /* Root Hold-Off */ -#define PHY_01_IBR 0x80 /* Initiate Bus Reset */ -#define PHY_01_GAP_COUNT 0x3F -#define PHY_02_EXTENDED 0xE0 /* 0x7 for 1394a-compliant PHY */ -#define PHY_02_TOTAL_PORTS 0x1F -#define PHY_03_MAX_SPEED 0xE0 -#define PHY_03_DELAY 0x0F -#define PHY_04_LCTRL 0x80 /* Link Active Report Control */ -#define PHY_04_CONTENDER 0x40 -#define PHY_04_JITTER 0x38 -#define PHY_04_PWR_CLASS 0x07 /* Power Class */ -#define PHY_05_WATCHDOG 0x80 -#define PHY_05_ISBR 0x40 /* Initiate Short Bus Reset */ -#define PHY_05_LOOP 0x20 /* Loop Detect */ -#define PHY_05_PWR_FAIL 0x10 /* Cable Power Failure Detect */ -#define PHY_05_TIMEOUT 0x08 /* Arbitration State Machine Timeout */ -#define PHY_05_PORT_EVENT 0x04 /* Port Event Detect */ -#define PHY_05_ENAB_ACCEL 0x02 /* Enable Arbitration Acceleration */ -#define PHY_05_ENAB_MULTI 0x01 /* Ena. Multispeed Packet Concatenation */ +#define PHY_00_PHYSICAL_ID 0xFC +#define PHY_00_R 0x02 /* Root */ +#define PHY_00_PS 0x01 /* Power Status*/ +#define PHY_01_RHB 0x80 /* Root Hold-Off */ +#define PHY_01_IBR 0x80 /* Initiate Bus Reset */ +#define PHY_01_GAP_COUNT 0x3F +#define PHY_02_EXTENDED 0xE0 /* 0x7 for 1394a-compliant PHY */ +#define PHY_02_TOTAL_PORTS 0x1F +#define PHY_03_MAX_SPEED 0xE0 +#define PHY_03_DELAY 0x0F +#define PHY_04_LCTRL 0x80 /* Link Active Report Control */ +#define PHY_04_CONTENDER 0x40 +#define PHY_04_JITTER 0x38 +#define PHY_04_PWR_CLASS 0x07 /* Power Class */ +#define PHY_05_WATCHDOG 0x80 +#define PHY_05_ISBR 0x40 /* Initiate Short Bus Reset */ +#define PHY_05_LOOP 0x20 /* Loop Detect */ +#define PHY_05_PWR_FAIL 0x10 /* Cable Power Failure Detect */ +#define PHY_05_TIMEOUT 0x08 /* Arbitration State Machine Timeout */ +#define PHY_05_PORT_EVENT 0x04 /* Port Event Detect */ +#define PHY_05_ENAB_ACCEL 0x02 /* Enable Arbitration Acceleration */ +#define PHY_05_ENAB_MULTI 0x01 /* Ena. Multispeed Packet Concatenation */ #include <asm/byteorder.h> #ifdef __BIG_ENDIAN_BITFIELD struct selfid { - u32 packet_identifier:2; /* always binary 10 */ - u32 phy_id:6; - /* byte */ - u32 extended:1; /* if true is struct ext_selfid */ - u32 link_active:1; - u32 gap_count:6; - /* byte */ - u32 speed:2; - u32 phy_delay:2; - u32 contender:1; - u32 power_class:3; - /* byte */ - u32 port0:2; - u32 port1:2; - u32 port2:2; - u32 initiated_reset:1; - u32 more_packets:1; + u32 packet_identifier:2; /* always binary 10 */ + u32 phy_id:6; + /* byte */ + u32 extended:1; /* if true is struct ext_selfid */ + u32 link_active:1; + u32 gap_count:6; + /* byte */ + u32 speed:2; + u32 phy_delay:2; + u32 contender:1; + u32 power_class:3; + /* byte */ + u32 port0:2; + u32 port1:2; + u32 port2:2; + u32 initiated_reset:1; + u32 more_packets:1; } __attribute__((packed)); struct ext_selfid { - u32 packet_identifier:2; /* always binary 10 */ - u32 phy_id:6; - /* byte */ - u32 extended:1; /* if false is struct selfid */ - u32 seq_nr:3; - u32 reserved:2; - u32 porta:2; - /* byte */ - u32 portb:2; - u32 portc:2; - u32 portd:2; - u32 porte:2; - /* byte */ - u32 portf:2; - u32 portg:2; - u32 porth:2; - u32 reserved2:1; - u32 more_packets:1; + u32 packet_identifier:2; /* always binary 10 */ + u32 phy_id:6; + /* byte */ + u32 extended:1; /* if false is struct selfid */ + u32 seq_nr:3; + u32 reserved:2; + u32 porta:2; + /* byte */ + u32 portb:2; + u32 portc:2; + u32 portd:2; + u32 porte:2; + /* byte */ + u32 portf:2; + u32 portg:2; + u32 porth:2; + u32 reserved2:1; + u32 more_packets:1; } __attribute__((packed)); #elif defined __LITTLE_ENDIAN_BITFIELD /* __BIG_ENDIAN_BITFIELD */ @@ -171,49 +172,48 @@ struct ext_selfid { */ struct selfid { - u32 phy_id:6; - u32 packet_identifier:2; /* always binary 10 */ - /* byte */ - u32 gap_count:6; - u32 link_active:1; - u32 extended:1; /* if true is struct ext_selfid */ - /* byte */ - u32 power_class:3; - u32 contender:1; - u32 phy_delay:2; - u32 speed:2; - /* byte */ - u32 more_packets:1; - u32 initiated_reset:1; - u32 port2:2; - u32 port1:2; - u32 port0:2; + u32 phy_id:6; + u32 packet_identifier:2; /* always binary 10 */ + /* byte */ + u32 gap_count:6; + u32 link_active:1; + u32 extended:1; /* if true is struct ext_selfid */ + /* byte */ + u32 power_class:3; + u32 contender:1; + u32 phy_delay:2; + u32 speed:2; + /* byte */ + u32 more_packets:1; + u32 initiated_reset:1; + u32 port2:2; + u32 port1:2; + u32 port0:2; } __attribute__((packed)); struct ext_selfid { - u32 phy_id:6; - u32 packet_identifier:2; /* always binary 10 */ - /* byte */ - u32 porta:2; - u32 reserved:2; - u32 seq_nr:3; - u32 extended:1; /* if false is struct selfid */ - /* byte */ - u32 porte:2; - u32 portd:2; - u32 portc:2; - u32 portb:2; - /* byte */ - u32 more_packets:1; - u32 reserved2:1; - u32 porth:2; - u32 portg:2; - u32 portf:2; + u32 phy_id:6; + u32 packet_identifier:2; /* always binary 10 */ + /* byte */ + u32 porta:2; + u32 reserved:2; + u32 seq_nr:3; + u32 extended:1; /* if false is struct selfid */ + /* byte */ + u32 porte:2; + u32 portd:2; + u32 portc:2; + u32 portb:2; + /* byte */ + u32 more_packets:1; + u32 reserved2:1; + u32 porth:2; + u32 portg:2; + u32 portf:2; } __attribute__((packed)); #else #error What? PDP endian? #endif /* __BIG_ENDIAN_BITFIELD */ - #endif /* _IEEE1394_IEEE1394_H */ diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c index f43739c5cab..5fccf9f7a1d 100644 --- a/drivers/ieee1394/ieee1394_core.c +++ b/drivers/ieee1394/ieee1394_core.c @@ -35,7 +35,6 @@ #include <linux/kthread.h> #include <asm/byteorder.h> -#include <asm/semaphore.h> #include "ieee1394_types.h" #include "ieee1394.h" @@ -86,7 +85,7 @@ static void dump_packet(const char *text, quadlet_t *data, int size, int speed) printk("\n"); } #else -#define dump_packet(a,b,c,d) +#define dump_packet(a,b,c,d) do {} while (0) #endif static void abort_requests(struct hpsb_host *host); @@ -355,10 +354,12 @@ static void build_speed_map(struct hpsb_host *host, int nodecount) } } +#if SELFID_SPEED_UNKNOWN != IEEE1394_SPEED_MAX /* assume maximum speed for 1394b PHYs, nodemgr will correct it */ for (n = 0; n < nodecount; n++) - if (speedcap[n] == 3) + if (speedcap[n] == SELFID_SPEED_UNKNOWN) speedcap[n] = IEEE1394_SPEED_MAX; +#endif } @@ -1169,7 +1170,7 @@ static void __exit ieee1394_cleanup(void) unregister_chrdev_region(IEEE1394_CORE_DEV, 256); } -module_init(ieee1394_init); +fs_initcall(ieee1394_init); /* same as ohci1394 */ module_exit(ieee1394_cleanup); /* Exported symbols */ diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h index 0ecbf335c64..af4a78a8ef3 100644 --- a/drivers/ieee1394/ieee1394_core.h +++ b/drivers/ieee1394/ieee1394_core.h @@ -1,12 +1,15 @@ - #ifndef _IEEE1394_CORE_H #define _IEEE1394_CORE_H -#include <linux/slab.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/list.h> +#include <linux/skbuff.h> +#include <linux/types.h> #include <asm/atomic.h> -#include <asm/semaphore.h> -#include "hosts.h" +#include "hosts.h" +#include "ieee1394_types.h" struct hpsb_packet { /* This struct is basically read-only for hosts with the exception of @@ -58,7 +61,6 @@ struct hpsb_packet { size_t header_size; size_t data_size; - struct hpsb_host *host; unsigned int generation; @@ -80,7 +82,7 @@ struct hpsb_packet { /* Set a task for when a packet completes */ void hpsb_set_packet_complete_task(struct hpsb_packet *packet, - void (*routine)(void *), void *data); + void (*routine)(void *), void *data); static inline struct hpsb_packet *driver_packet(struct list_head *l) { @@ -92,7 +94,6 @@ void abort_timedouts(unsigned long __opaque); struct hpsb_packet *hpsb_alloc_packet(size_t data_size); void hpsb_free_packet(struct hpsb_packet *packet); - /* * Generation counter for the complete 1394 subsystem. Generation gets * incremented on every change in the subsystem (e.g. bus reset). @@ -204,10 +205,14 @@ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, #define IEEE1394_MINOR_BLOCK_EXPERIMENTAL 15 #define IEEE1394_CORE_DEV MKDEV(IEEE1394_MAJOR, 0) -#define IEEE1394_RAW1394_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_RAW1394 * 16) -#define IEEE1394_VIDEO1394_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_VIDEO1394 * 16) -#define IEEE1394_DV1394_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_DV1394 * 16) -#define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16) +#define IEEE1394_RAW1394_DEV MKDEV(IEEE1394_MAJOR, \ + IEEE1394_MINOR_BLOCK_RAW1394 * 16) +#define IEEE1394_VIDEO1394_DEV MKDEV(IEEE1394_MAJOR, \ + IEEE1394_MINOR_BLOCK_VIDEO1394 * 16) +#define IEEE1394_DV1394_DEV MKDEV(IEEE1394_MAJOR, \ + IEEE1394_MINOR_BLOCK_DV1394 * 16) +#define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, \ + IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16) /* return the index (within a minor number block) of a file */ static inline unsigned char ieee1394_file_to_instance(struct file *file) @@ -223,4 +228,3 @@ extern struct class hpsb_host_class; extern struct class *hpsb_protocol_class; #endif /* _IEEE1394_CORE_H */ - diff --git a/drivers/ieee1394/ieee1394_hotplug.h b/drivers/ieee1394/ieee1394_hotplug.h index 5be70d31b00..dd5500ed832 100644 --- a/drivers/ieee1394/ieee1394_hotplug.h +++ b/drivers/ieee1394/ieee1394_hotplug.h @@ -1,33 +1,19 @@ #ifndef _IEEE1394_HOTPLUG_H #define _IEEE1394_HOTPLUG_H -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/mod_devicetable.h> - /* Unit spec id and sw version entry for some protocols */ #define AVC_UNIT_SPEC_ID_ENTRY 0x0000A02D #define AVC_SW_VERSION_ENTRY 0x00010001 #define CAMERA_UNIT_SPEC_ID_ENTRY 0x0000A02D #define CAMERA_SW_VERSION_ENTRY 0x00000100 -/* Check to make sure this all isn't already defined */ -#ifndef IEEE1394_MATCH_VENDOR_ID - -#define IEEE1394_MATCH_VENDOR_ID 0x0001 -#define IEEE1394_MATCH_MODEL_ID 0x0002 -#define IEEE1394_MATCH_SPECIFIER_ID 0x0004 -#define IEEE1394_MATCH_VERSION 0x0008 - -struct ieee1394_device_id { - u32 match_flags; - u32 vendor_id; - u32 model_id; - u32 specifier_id; - u32 version; - void *driver_data; -}; - -#endif +/* /include/linux/mod_devicetable.h defines: + * IEEE1394_MATCH_VENDOR_ID + * IEEE1394_MATCH_MODEL_ID + * IEEE1394_MATCH_SPECIFIER_ID + * IEEE1394_MATCH_VERSION + * struct ieee1394_device_id + */ +#include <linux/mod_devicetable.h> #endif /* _IEEE1394_HOTPLUG_H */ diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c index a114b91d606..0833fc9f50c 100644 --- a/drivers/ieee1394/ieee1394_transactions.c +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -9,19 +9,17 @@ * directory of the kernel sources for details. */ -#include <linux/sched.h> #include <linux/bitops.h> -#include <linux/smp_lock.h> -#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <asm/bug.h> #include <asm/errno.h> #include "ieee1394.h" #include "ieee1394_types.h" #include "hosts.h" #include "ieee1394_core.h" -#include "highlevel.h" -#include "nodemgr.h" #include "ieee1394_transactions.h" #define PREP_ASYNC_HEAD_ADDRESS(tc) \ @@ -31,6 +29,13 @@ packet->header[1] = (packet->host->node_id << 16) | (addr >> 32); \ packet->header[2] = addr & 0xffffffff +#ifndef HPSB_DEBUG_TLABELS +static +#endif +spinlock_t hpsb_tlabel_lock = SPIN_LOCK_UNLOCKED; + +static DECLARE_WAIT_QUEUE_HEAD(tlabel_wq); + static void fill_async_readquad(struct hpsb_packet *packet, u64 addr) { PREP_ASYNC_HEAD_ADDRESS(TCODE_READQ); @@ -114,9 +119,41 @@ static void fill_async_stream_packet(struct hpsb_packet *packet, int length, packet->tcode = TCODE_ISO_DATA; } +/* same as hpsb_get_tlabel, except that it returns immediately */ +static int hpsb_get_tlabel_atomic(struct hpsb_packet *packet) +{ + unsigned long flags, *tp; + u8 *next; + int tlabel, n = NODEID_TO_NODE(packet->node_id); + + /* Broadcast transactions are complete once the request has been sent. + * Use the same transaction label for all broadcast transactions. */ + if (unlikely(n == ALL_NODES)) { + packet->tlabel = 0; + return 0; + } + tp = packet->host->tl_pool[n].map; + next = &packet->host->next_tl[n]; + + spin_lock_irqsave(&hpsb_tlabel_lock, flags); + tlabel = find_next_zero_bit(tp, 64, *next); + if (tlabel > 63) + tlabel = find_first_zero_bit(tp, 64); + if (tlabel > 63) { + spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); + return -EAGAIN; + } + __set_bit(tlabel, tp); + *next = (tlabel + 1) & 63; + spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); + + packet->tlabel = tlabel; + return 0; +} + /** * hpsb_get_tlabel - allocate a transaction label - * @packet: the packet who's tlabel/tpool we set + * @packet: the packet whose tlabel and tl_pool we set * * Every asynchronous transaction on the 1394 bus needs a transaction * label to match the response to the request. This label has to be @@ -130,42 +167,25 @@ static void fill_async_stream_packet(struct hpsb_packet *packet, int length, * Return value: Zero on success, otherwise non-zero. A non-zero return * generally means there are no available tlabels. If this is called out * of interrupt or atomic context, then it will sleep until can return a - * tlabel. + * tlabel or a signal is received. */ int hpsb_get_tlabel(struct hpsb_packet *packet) { - unsigned long flags; - struct hpsb_tlabel_pool *tp; - int n = NODEID_TO_NODE(packet->node_id); - - if (unlikely(n == ALL_NODES)) - return 0; - tp = &packet->host->tpool[n]; - - if (irqs_disabled() || in_atomic()) { - if (down_trylock(&tp->count)) - return 1; - } else { - down(&tp->count); - } - - spin_lock_irqsave(&tp->lock, flags); - - packet->tlabel = find_next_zero_bit(tp->pool, 64, tp->next); - if (packet->tlabel > 63) - packet->tlabel = find_first_zero_bit(tp->pool, 64); - tp->next = (packet->tlabel + 1) % 64; - /* Should _never_ happen */ - BUG_ON(test_and_set_bit(packet->tlabel, tp->pool)); - tp->allocations++; - spin_unlock_irqrestore(&tp->lock, flags); - - return 0; + if (irqs_disabled() || in_atomic()) + return hpsb_get_tlabel_atomic(packet); + + /* NB: The macro wait_event_interruptible() is called with a condition + * argument with side effect. This is only possible because the side + * effect does not occur until the condition became true, and + * wait_event_interruptible() won't evaluate the condition again after + * that. */ + return wait_event_interruptible(tlabel_wq, + !hpsb_get_tlabel_atomic(packet)); } /** * hpsb_free_tlabel - free an allocated transaction label - * @packet: packet whos tlabel/tpool needs to be cleared + * @packet: packet whose tlabel and tl_pool needs to be cleared * * Frees the transaction label allocated with hpsb_get_tlabel(). The * tlabel has to be freed after the transaction is complete (i.e. response @@ -176,21 +196,20 @@ int hpsb_get_tlabel(struct hpsb_packet *packet) */ void hpsb_free_tlabel(struct hpsb_packet *packet) { - unsigned long flags; - struct hpsb_tlabel_pool *tp; - int n = NODEID_TO_NODE(packet->node_id); + unsigned long flags, *tp; + int tlabel, n = NODEID_TO_NODE(packet->node_id); if (unlikely(n == ALL_NODES)) return; - tp = &packet->host->tpool[n]; + tp = packet->host->tl_pool[n].map; + tlabel = packet->tlabel; + BUG_ON(tlabel > 63 || tlabel < 0); - BUG_ON(packet->tlabel > 63 || packet->tlabel < 0); + spin_lock_irqsave(&hpsb_tlabel_lock, flags); + BUG_ON(!__test_and_clear_bit(tlabel, tp)); + spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); - spin_lock_irqsave(&tp->lock, flags); - BUG_ON(!test_and_clear_bit(packet->tlabel, tp->pool)); - spin_unlock_irqrestore(&tp->lock, flags); - - up(&tp->count); + wake_up_interruptible(&tlabel_wq); } int hpsb_packet_success(struct hpsb_packet *packet) @@ -214,7 +233,7 @@ int hpsb_packet_success(struct hpsb_packet *packet) packet->node_id); return -EAGAIN; } - HPSB_PANIC("reached unreachable code 1 in %s", __FUNCTION__); + BUG(); case ACK_BUSY_X: case ACK_BUSY_A: @@ -261,8 +280,7 @@ int hpsb_packet_success(struct hpsb_packet *packet) packet->ack_code, packet->node_id, packet->tcode); return -EAGAIN; } - - HPSB_PANIC("reached unreachable code 2 in %s", __FUNCTION__); + BUG(); } struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node, diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h index 45ba784fe6d..c1369c41469 100644 --- a/drivers/ieee1394/ieee1394_transactions.h +++ b/drivers/ieee1394/ieee1394_transactions.h @@ -1,32 +1,32 @@ #ifndef _IEEE1394_TRANSACTIONS_H #define _IEEE1394_TRANSACTIONS_H -#include "ieee1394_core.h" +#include <linux/types.h> +#include "ieee1394_types.h" + +struct hpsb_packet; +struct hpsb_host; -/* - * Get and free transaction labels. - */ int hpsb_get_tlabel(struct hpsb_packet *packet); void hpsb_free_tlabel(struct hpsb_packet *packet); - struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node, u64 addr, size_t length); struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node, - u64 addr, int extcode, quadlet_t *data, + u64 addr, int extcode, quadlet_t *data, quadlet_t arg); -struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host, nodeid_t node, - u64 addr, int extcode, octlet_t *data, - octlet_t arg); -struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, - quadlet_t data) ; -struct hpsb_packet *hpsb_make_isopacket(struct hpsb_host *host, - int length, int channel, - int tag, int sync); -struct hpsb_packet *hpsb_make_writepacket (struct hpsb_host *host, nodeid_t node, - u64 addr, quadlet_t *buffer, size_t length); +struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host, + nodeid_t node, u64 addr, int extcode, + octlet_t *data, octlet_t arg); +struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data); +struct hpsb_packet *hpsb_make_isopacket(struct hpsb_host *host, int length, + int channel, int tag, int sync); +struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host, + nodeid_t node, u64 addr, + quadlet_t *buffer, size_t length); struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer, - int length, int channel, int tag, int sync); + int length, int channel, int tag, + int sync); /* * hpsb_packet_success - Make sense of the ack and reply codes and @@ -40,9 +40,8 @@ struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer, */ int hpsb_packet_success(struct hpsb_packet *packet); - /* - * The generic read, write and lock functions. All recognize the local node ID + * The generic read and write functions. All recognize the local node ID * and act accordingly. Read and write automatically use quadlet commands if * length == 4 and and block commands otherwise (however, they do not yet * support lengths that are not a multiple of 4). You must explicitly specifiy @@ -54,4 +53,8 @@ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t *buffer, size_t length); +#ifdef HPSB_DEBUG_TLABELS +extern spinlock_t hpsb_tlabel_lock; +#endif + #endif /* _IEEE1394_TRANSACTIONS_H */ diff --git a/drivers/ieee1394/ieee1394_types.h b/drivers/ieee1394/ieee1394_types.h index 3165609ec1e..9803aaa15be 100644 --- a/drivers/ieee1394/ieee1394_types.h +++ b/drivers/ieee1394/ieee1394_types.h @@ -1,37 +1,11 @@ - #ifndef _IEEE1394_TYPES_H #define _IEEE1394_TYPES_H #include <linux/kernel.h> -#include <linux/types.h> -#include <linux/list.h> -#include <linux/init.h> -#include <linux/spinlock.h> #include <linux/string.h> - -#include <asm/semaphore.h> +#include <linux/types.h> #include <asm/byteorder.h> - -/* Transaction Label handling */ -struct hpsb_tlabel_pool { - DECLARE_BITMAP(pool, 64); - spinlock_t lock; - u8 next; - u32 allocations; - struct semaphore count; -}; - -#define HPSB_TPOOL_INIT(_tp) \ -do { \ - bitmap_zero((_tp)->pool, 64); \ - spin_lock_init(&(_tp)->lock); \ - (_tp)->next = 0; \ - (_tp)->allocations = 0; \ - sema_init(&(_tp)->count, 63); \ -} while (0) - - typedef u32 quadlet_t; typedef u64 octlet_t; typedef u16 nodeid_t; @@ -54,46 +28,40 @@ typedef u16 arm_length_t; #define NODE_BUS_ARGS(__host, __nodeid) \ __host->id, NODEID_TO_NODE(__nodeid), NODEID_TO_BUS(__nodeid) -#define HPSB_PRINT(level, fmt, args...) printk(level "ieee1394: " fmt "\n" , ## args) +#define HPSB_PRINT(level, fmt, args...) \ + printk(level "ieee1394: " fmt "\n" , ## args) -#define HPSB_DEBUG(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args) -#define HPSB_INFO(fmt, args...) HPSB_PRINT(KERN_INFO, fmt , ## args) -#define HPSB_NOTICE(fmt, args...) HPSB_PRINT(KERN_NOTICE, fmt , ## args) -#define HPSB_WARN(fmt, args...) HPSB_PRINT(KERN_WARNING, fmt , ## args) -#define HPSB_ERR(fmt, args...) HPSB_PRINT(KERN_ERR, fmt , ## args) +#define HPSB_DEBUG(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args) +#define HPSB_INFO(fmt, args...) HPSB_PRINT(KERN_INFO, fmt , ## args) +#define HPSB_NOTICE(fmt, args...) HPSB_PRINT(KERN_NOTICE, fmt , ## args) +#define HPSB_WARN(fmt, args...) HPSB_PRINT(KERN_WARNING, fmt , ## args) +#define HPSB_ERR(fmt, args...) HPSB_PRINT(KERN_ERR, fmt , ## args) #ifdef CONFIG_IEEE1394_VERBOSEDEBUG -#define HPSB_VERBOSE(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args) +#define HPSB_VERBOSE(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args) +#define HPSB_DEBUG_TLABELS #else -#define HPSB_VERBOSE(fmt, args...) +#define HPSB_VERBOSE(fmt, args...) do {} while (0) #endif -#define HPSB_PANIC(fmt, args...) panic("ieee1394: " fmt "\n" , ## args) - -#define HPSB_TRACE() HPSB_PRINT(KERN_INFO, "TRACE - %s, %s(), line %d", __FILE__, __FUNCTION__, __LINE__) - - #ifdef __BIG_ENDIAN -static __inline__ void *memcpy_le32(u32 *dest, const u32 *__src, size_t count) +static inline void *memcpy_le32(u32 *dest, const u32 *__src, size_t count) { - void *tmp = dest; + void *tmp = dest; u32 *src = (u32 *)__src; - count /= 4; - - while (count--) { - *dest++ = swab32p(src++); - } - - return tmp; + count /= 4; + while (count--) + *dest++ = swab32p(src++); + return tmp; } #else static __inline__ void *memcpy_le32(u32 *dest, const u32 *src, size_t count) { - return memcpy(dest, src, count); + return memcpy(dest, src, count); } #endif /* __BIG_ENDIAN */ diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c index f26680ebef7..08bd15d2a7b 100644 --- a/drivers/ieee1394/iso.c +++ b/drivers/ieee1394/iso.c @@ -9,8 +9,11 @@ * directory of the kernel sources for details. */ -#include <linux/slab.h> +#include <linux/pci.h> #include <linux/sched.h> +#include <linux/slab.h> + +#include "hosts.h" #include "iso.h" void hpsb_iso_stop(struct hpsb_iso *iso) diff --git a/drivers/ieee1394/iso.h b/drivers/ieee1394/iso.h index 3efc60b33a8..1210a97e868 100644 --- a/drivers/ieee1394/iso.h +++ b/drivers/ieee1394/iso.h @@ -12,33 +12,40 @@ #ifndef IEEE1394_ISO_H #define IEEE1394_ISO_H -#include "hosts.h" +#include <linux/spinlock_types.h> +#include <asm/atomic.h> +#include <asm/types.h> + #include "dma.h" -/* high-level ISO interface */ +struct hpsb_host; -/* This API sends and receives isochronous packets on a large, - virtually-contiguous kernel memory buffer. The buffer may be mapped - into a user-space process for zero-copy transmission and reception. +/* high-level ISO interface */ - There are no explicit boundaries between packets in the buffer. A - packet may be transmitted or received at any location. However, - low-level drivers may impose certain restrictions on alignment or - size of packets. (e.g. in OHCI no packet may cross a page boundary, - and packets should be quadlet-aligned) -*/ +/* + * This API sends and receives isochronous packets on a large, + * virtually-contiguous kernel memory buffer. The buffer may be mapped + * into a user-space process for zero-copy transmission and reception. + * + * There are no explicit boundaries between packets in the buffer. A + * packet may be transmitted or received at any location. However, + * low-level drivers may impose certain restrictions on alignment or + * size of packets. (e.g. in OHCI no packet may cross a page boundary, + * and packets should be quadlet-aligned) + */ /* Packet descriptor - the API maintains a ring buffer of these packet - descriptors in kernel memory (hpsb_iso.infos[]). */ - + * descriptors in kernel memory (hpsb_iso.infos[]). */ struct hpsb_iso_packet_info { /* offset of data payload relative to the first byte of the buffer */ __u32 offset; - /* length of the data payload, in bytes (not including the isochronous header) */ + /* length of the data payload, in bytes (not including the isochronous + * header) */ __u16 len; - /* (recv only) the cycle number (mod 8000) on which the packet was received */ + /* (recv only) the cycle number (mod 8000) on which the packet was + * received */ __u16 cycle; /* (recv only) channel on which the packet was received */ @@ -48,12 +55,10 @@ struct hpsb_iso_packet_info { __u8 tag; __u8 sy; - /* - * length in bytes of the packet including header/trailer. - * MUST be at structure end, since the first part of this structure is also - * defined in raw1394.h (i.e. struct raw1394_iso_packet_info), is copied to - * userspace and is accessed there through libraw1394. - */ + /* length in bytes of the packet including header/trailer. + * MUST be at structure end, since the first part of this structure is + * also defined in raw1394.h (i.e. struct raw1394_iso_packet_info), is + * copied to userspace and is accessed there through libraw1394. */ __u16 total_len; }; @@ -75,8 +80,8 @@ struct hpsb_iso { void *hostdata; /* a function to be called (from interrupt context) after - outgoing packets have been sent, or incoming packets have - arrived */ + * outgoing packets have been sent, or incoming packets have + * arrived */ void (*callback)(struct hpsb_iso*); /* wait for buffer space */ @@ -88,7 +93,7 @@ struct hpsb_iso { /* greatest # of packets between interrupts - controls - the maximum latency of the buffer */ + * the maximum latency of the buffer */ int irq_interval; /* the buffer for packet data payloads */ @@ -112,8 +117,8 @@ struct hpsb_iso { int pkt_dma; /* how many packets, starting at first_packet: - (transmit) are ready to be filled with data - (receive) contain received data */ + * (transmit) are ready to be filled with data + * (receive) contain received data */ int n_ready_packets; /* how many times the buffer has overflowed or underflowed */ @@ -134,7 +139,7 @@ struct hpsb_iso { int start_cycle; /* cycle at which next packet will be transmitted, - -1 if not known */ + * -1 if not known */ int xmit_cycle; /* ringbuffer of packet descriptors in regular kernel memory @@ -170,25 +175,30 @@ int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel); int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask); /* start/stop DMA */ -int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle, int prebuffer); -int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle, int tag_mask, int sync); +int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle, + int prebuffer); +int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle, + int tag_mask, int sync); void hpsb_iso_stop(struct hpsb_iso *iso); /* deallocate buffer and DMA context */ void hpsb_iso_shutdown(struct hpsb_iso *iso); -/* queue a packet for transmission. 'offset' is relative to the beginning of the - DMA buffer, where the packet's data payload should already have been placed */ -int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, u8 tag, u8 sy); +/* queue a packet for transmission. + * 'offset' is relative to the beginning of the DMA buffer, where the packet's + * data payload should already have been placed. */ +int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, + u8 tag, u8 sy); /* wait until all queued packets have been transmitted to the bus */ int hpsb_iso_xmit_sync(struct hpsb_iso *iso); /* N packets have been read out of the buffer, re-use the buffer space */ -int hpsb_iso_recv_release_packets(struct hpsb_iso *recv, unsigned int n_packets); +int hpsb_iso_recv_release_packets(struct hpsb_iso *recv, + unsigned int n_packets); /* check for arrival of new packets immediately (even if irq_interval - has not yet been reached) */ + * has not yet been reached) */ int hpsb_iso_recv_flush(struct hpsb_iso *iso); /* returns # of packets ready to send or receive */ @@ -197,14 +207,15 @@ int hpsb_iso_n_ready(struct hpsb_iso *iso); /* the following are callbacks available to low-level drivers */ /* call after a packet has been transmitted to the bus (interrupt context is OK) - 'cycle' is the _exact_ cycle the packet was sent on - 'error' should be non-zero if some sort of error occurred when sending the packet -*/ + * 'cycle' is the _exact_ cycle the packet was sent on + * 'error' should be non-zero if some sort of error occurred when sending the + * packet */ void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error); /* call after a packet has been received (interrupt context OK) */ void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len, - u16 total_len, u16 cycle, u8 channel, u8 tag, u8 sy); + u16 total_len, u16 cycle, u8 channel, u8 tag, + u8 sy); /* call to wake waiting processes after buffer space has opened up. */ void hpsb_iso_wake(struct hpsb_iso *iso); diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index d541b508a15..3e7974c5744 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -12,26 +12,23 @@ #include <linux/kernel.h> #include <linux/list.h> #include <linux/slab.h> -#include <linux/smp_lock.h> -#include <linux/interrupt.h> -#include <linux/kmod.h> -#include <linux/completion.h> #include <linux/delay.h> -#include <linux/pci.h> +#include <linux/kthread.h> #include <linux/moduleparam.h> #include <asm/atomic.h> -#include "ieee1394_types.h" +#include "csr.h" +#include "highlevel.h" +#include "hosts.h" #include "ieee1394.h" #include "ieee1394_core.h" -#include "hosts.h" +#include "ieee1394_hotplug.h" +#include "ieee1394_types.h" #include "ieee1394_transactions.h" -#include "highlevel.h" -#include "csr.h" #include "nodemgr.h" static int ignore_drivers; -module_param(ignore_drivers, int, 0444); +module_param(ignore_drivers, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ignore_drivers, "Disable automatic probing for drivers."); struct nodemgr_csr_info { @@ -71,7 +68,7 @@ static int nodemgr_check_speed(struct nodemgr_csr_info *ci, u64 addr, u8 i, *speed, old_speed, good_speed; int ret; - speed = ci->host->speed + NODEID_TO_NODE(ci->nodeid); + speed = &(ci->host->speed[NODEID_TO_NODE(ci->nodeid)]); old_speed = *speed; good_speed = IEEE1394_SPEED_MAX + 1; @@ -161,16 +158,12 @@ static struct csr1212_bus_ops nodemgr_csr_ops = { * but now we are much simpler because of the LDM. */ -static DECLARE_MUTEX(nodemgr_serialize); +static DEFINE_MUTEX(nodemgr_serialize); struct host_info { struct hpsb_host *host; struct list_head list; - struct completion exited; - struct semaphore reset_sem; - int pid; - char daemon_name[15]; - int kill_me; + struct task_struct *thread; }; static int nodemgr_bus_match(struct device * dev, struct device_driver * drv); @@ -334,34 +327,44 @@ static ssize_t fw_show_ne_bus_options(struct device *dev, struct device_attribut static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL); -/* tlabels_free, tlabels_allocations, tlabels_mask are read non-atomically - * here, therefore displayed values may be occasionally wrong. */ -static ssize_t fw_show_ne_tlabels_free(struct device *dev, struct device_attribute *attr, char *buf) +#ifdef HPSB_DEBUG_TLABELS +static ssize_t fw_show_ne_tlabels_free(struct device *dev, + struct device_attribute *attr, char *buf) { struct node_entry *ne = container_of(dev, struct node_entry, device); - return sprintf(buf, "%d\n", 64 - bitmap_weight(ne->tpool->pool, 64)); -} -static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL); + unsigned long flags; + unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map; + int tf; + spin_lock_irqsave(&hpsb_tlabel_lock, flags); + tf = 64 - bitmap_weight(tp, 64); + spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); -static ssize_t fw_show_ne_tlabels_allocations(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct node_entry *ne = container_of(dev, struct node_entry, device); - return sprintf(buf, "%u\n", ne->tpool->allocations); + return sprintf(buf, "%d\n", tf); } -static DEVICE_ATTR(tlabels_allocations,S_IRUGO,fw_show_ne_tlabels_allocations,NULL); +static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL); -static ssize_t fw_show_ne_tlabels_mask(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t fw_show_ne_tlabels_mask(struct device *dev, + struct device_attribute *attr, char *buf) { struct node_entry *ne = container_of(dev, struct node_entry, device); + unsigned long flags; + unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map; + u64 tm; + + spin_lock_irqsave(&hpsb_tlabel_lock, flags); #if (BITS_PER_LONG <= 32) - return sprintf(buf, "0x%08lx%08lx\n", ne->tpool->pool[0], ne->tpool->pool[1]); + tm = ((u64)tp[0] << 32) + tp[1]; #else - return sprintf(buf, "0x%016lx\n", ne->tpool->pool[0]); + tm = tp[0]; #endif + spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); + + return sprintf(buf, "0x%016llx\n", tm); } static DEVICE_ATTR(tlabels_mask, S_IRUGO, fw_show_ne_tlabels_mask, NULL); +#endif /* HPSB_DEBUG_TLABELS */ static ssize_t fw_set_ignore_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -408,26 +411,11 @@ static ssize_t fw_get_destroy_node(struct bus_type *bus, char *buf) } static BUS_ATTR(destroy_node, S_IWUSR | S_IRUGO, fw_get_destroy_node, fw_set_destroy_node); -static int nodemgr_rescan_bus_thread(void *__unused) -{ - /* No userlevel access needed */ - daemonize("kfwrescan"); - - bus_rescan_devices(&ieee1394_bus_type); - - return 0; -} static ssize_t fw_set_rescan(struct bus_type *bus, const char *buf, size_t count) { - int state = simple_strtoul(buf, NULL, 10); - - /* Don't wait for this, or care about errors. Root could do - * something stupid and spawn this a lot of times, but that's - * root's fault. */ - if (state == 1) - kernel_thread(nodemgr_rescan_bus_thread, NULL, CLONE_KERNEL); - + if (simple_strtoul(buf, NULL, 10) == 1) + bus_rescan_devices(&ieee1394_bus_type); return count; } static ssize_t fw_get_rescan(struct bus_type *bus, char *buf) @@ -483,9 +471,10 @@ static struct device_attribute *const fw_ne_attrs[] = { &dev_attr_ne_vendor_id, &dev_attr_ne_nodeid, &dev_attr_bus_options, +#ifdef HPSB_DEBUG_TLABELS &dev_attr_tlabels_free, - &dev_attr_tlabels_allocations, &dev_attr_tlabels_mask, +#endif }; @@ -804,8 +793,6 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, struct csr1212_csr if (!ne) return NULL; - ne->tpool = &host->tpool[nodeid & NODE_MASK]; - ne->host = host; ne->nodeid = nodeid; ne->generation = generation; @@ -1251,6 +1238,7 @@ static void nodemgr_node_scan_one(struct host_info *hi, octlet_t guid; struct csr1212_csr *csr; struct nodemgr_csr_info *ci; + u8 *speed; ci = kmalloc(sizeof(*ci), GFP_KERNEL); if (!ci) @@ -1259,8 +1247,12 @@ static void nodemgr_node_scan_one(struct host_info *hi, ci->host = host; ci->nodeid = nodeid; ci->generation = generation; - ci->speed_unverified = - host->speed[NODEID_TO_NODE(nodeid)] > IEEE1394_SPEED_100; + + /* Prepare for speed probe which occurs when reading the ROM */ + speed = &(host->speed[NODEID_TO_NODE(nodeid)]); + if (*speed > host->csr.lnk_spd) + *speed = host->csr.lnk_spd; + ci->speed_unverified = *speed > IEEE1394_SPEED_100; /* We need to detect when the ConfigROM's generation has changed, * so we only update the node's info when it needs to be. */ @@ -1300,8 +1292,6 @@ static void nodemgr_node_scan_one(struct host_info *hi, nodemgr_create_node(guid, csr, hi, nodeid, generation); else nodemgr_update_node(ne, csr, hi, nodeid, generation); - - return; } @@ -1326,6 +1316,7 @@ static void nodemgr_node_scan(struct host_info *hi, int generation) } +/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader. */ static void nodemgr_suspend_ne(struct node_entry *ne) { struct class_device *cdev; @@ -1361,6 +1352,7 @@ static void nodemgr_resume_ne(struct node_entry *ne) ne->in_limbo = 0; device_remove_file(&ne->device, &dev_attr_ne_in_limbo); + down_read(&nodemgr_ud_class.subsys.rwsem); down_read(&ne->device.bus->subsys.rwsem); list_for_each_entry(cdev, &nodemgr_ud_class.children, node) { ud = container_of(cdev, struct unit_directory, class_dev); @@ -1372,21 +1364,21 @@ static void nodemgr_resume_ne(struct node_entry *ne) ud->device.driver->resume(&ud->device); } up_read(&ne->device.bus->subsys.rwsem); + up_read(&nodemgr_ud_class.subsys.rwsem); HPSB_DEBUG("Node resumed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid); } +/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader. */ static void nodemgr_update_pdrv(struct node_entry *ne) { struct unit_directory *ud; struct hpsb_protocol_driver *pdrv; - struct class *class = &nodemgr_ud_class; struct class_device *cdev; - down_read(&class->subsys.rwsem); - list_for_each_entry(cdev, &class->children, node) { + list_for_each_entry(cdev, &nodemgr_ud_class.children, node) { ud = container_of(cdev, struct unit_directory, class_dev); if (ud->ne != ne || !ud->device.driver) continue; @@ -1399,7 +1391,6 @@ static void nodemgr_update_pdrv(struct node_entry *ne) up_write(&ud->device.bus->subsys.rwsem); } } - up_read(&class->subsys.rwsem); } @@ -1430,6 +1421,8 @@ static void nodemgr_irm_write_bc(struct node_entry *ne, int generation) } +/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader because the + * calls to nodemgr_update_pdrv() and nodemgr_suspend_ne() here require it. */ static void nodemgr_probe_ne(struct host_info *hi, struct node_entry *ne, int generation) { struct device *dev; @@ -1492,9 +1485,8 @@ static void nodemgr_node_probe(struct host_info *hi, int generation) /* If we had a bus reset while we were scanning the bus, it is * possible that we did not probe all nodes. In that case, we * skip the clean up for now, since we could remove nodes that - * were still on the bus. The bus reset increased hi->reset_sem, - * so there's a bus scan pending which will do the clean up - * eventually. + * were still on the bus. Another bus scan is pending which will + * do the clean up eventually. * * Now let's tell the bus to rescan our devices. This may seem * like overhead, but the driver-model core will only scan a @@ -1622,41 +1614,37 @@ static int nodemgr_host_thread(void *__hi) { struct host_info *hi = (struct host_info *)__hi; struct hpsb_host *host = hi->host; - int reset_cycles = 0; - - /* No userlevel access needed */ - daemonize(hi->daemon_name); + unsigned int g, generation = get_hpsb_generation(host) - 1; + int i, reset_cycles = 0; /* Setup our device-model entries */ nodemgr_create_host_dev_files(host); - /* Sit and wait for a signal to probe the nodes on the bus. This - * happens when we get a bus reset. */ - while (1) { - unsigned int generation = 0; - int i; + for (;;) { + /* Sleep until next bus reset */ + set_current_state(TASK_INTERRUPTIBLE); + if (get_hpsb_generation(host) == generation) + schedule(); + __set_current_state(TASK_RUNNING); + + /* Thread may have been woken up to freeze or to exit */ + if (try_to_freeze()) + continue; + if (kthread_should_stop()) + goto exit; - if (down_interruptible(&hi->reset_sem) || - down_interruptible(&nodemgr_serialize)) { + if (mutex_lock_interruptible(&nodemgr_serialize)) { if (try_to_freeze()) continue; - printk("NodeMgr: received unexpected signal?!\n" ); - break; - } - - if (hi->kill_me) { - up(&nodemgr_serialize); - break; + goto exit; } /* Pause for 1/4 second in 1/16 second intervals, * to make sure things settle down. */ + g = get_hpsb_generation(host); for (i = 0; i < 4 ; i++) { - set_current_state(TASK_INTERRUPTIBLE); - if (msleep_interruptible(63)) { - up(&nodemgr_serialize); - goto caught_signal; - } + if (msleep_interruptible(63) || kthread_should_stop()) + goto unlock_exit; /* Now get the generation in which the node ID's we collect * are valid. During the bus scan we will use this generation @@ -1667,20 +1655,14 @@ static int nodemgr_host_thread(void *__hi) /* If we get a reset before we are done waiting, then * start the the waiting over again */ - while (!down_trylock(&hi->reset_sem)) - i = 0; - - /* Check the kill_me again */ - if (hi->kill_me) { - up(&nodemgr_serialize); - goto caught_signal; - } + if (generation != g) + g = generation, i = 0; } if (!nodemgr_check_irm_capability(host, reset_cycles) || !nodemgr_do_irm_duties(host, reset_cycles)) { reset_cycles++; - up(&nodemgr_serialize); + mutex_unlock(&nodemgr_serialize); continue; } reset_cycles = 0; @@ -1698,13 +1680,13 @@ static int nodemgr_host_thread(void *__hi) /* Update some of our sysfs symlinks */ nodemgr_update_host_dev_links(host); - up(&nodemgr_serialize); + mutex_unlock(&nodemgr_serialize); } - -caught_signal: +unlock_exit: + mutex_unlock(&nodemgr_serialize); +exit: HPSB_VERBOSE("NodeMgr: Exiting thread"); - - complete_and_exit(&hi->exited, 0); + return 0; } int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *)) @@ -1764,41 +1746,27 @@ static void nodemgr_add_host(struct hpsb_host *host) struct host_info *hi; hi = hpsb_create_hostinfo(&nodemgr_highlevel, host, sizeof(*hi)); - if (!hi) { - HPSB_ERR ("NodeMgr: out of memory in add host"); + HPSB_ERR("NodeMgr: out of memory in add host"); return; } - hi->host = host; - init_completion(&hi->exited); - sema_init(&hi->reset_sem, 0); - - sprintf(hi->daemon_name, "knodemgrd_%d", host->id); - - hi->pid = kernel_thread(nodemgr_host_thread, hi, CLONE_KERNEL); - - if (hi->pid < 0) { - HPSB_ERR ("NodeMgr: failed to start %s thread for %s", - hi->daemon_name, host->driver->name); + hi->thread = kthread_run(nodemgr_host_thread, hi, "knodemgrd_%d", + host->id); + if (IS_ERR(hi->thread)) { + HPSB_ERR("NodeMgr: cannot start thread for host %d", host->id); hpsb_destroy_hostinfo(&nodemgr_highlevel, host); - return; } - - return; } static void nodemgr_host_reset(struct hpsb_host *host) { struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host); - if (hi != NULL) { - HPSB_VERBOSE("NodeMgr: Processing host reset for %s", hi->daemon_name); - up(&hi->reset_sem); - } else - HPSB_ERR ("NodeMgr: could not process reset of unused host"); - - return; + if (hi) { + HPSB_VERBOSE("NodeMgr: Processing reset for host %d", host->id); + wake_up_process(hi->thread); + } } static void nodemgr_remove_host(struct hpsb_host *host) @@ -1806,18 +1774,9 @@ static void nodemgr_remove_host(struct hpsb_host *host) struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host); if (hi) { - if (hi->pid >= 0) { - hi->kill_me = 1; - mb(); - up(&hi->reset_sem); - wait_for_completion(&hi->exited); - nodemgr_remove_host_dev(&host->device); - } - } else - HPSB_ERR("NodeMgr: host %s does not exist, cannot remove", - host->driver->name); - - return; + kthread_stop(hi->thread); + nodemgr_remove_host_dev(&host->device); + } } static struct hpsb_highlevel nodemgr_highlevel = { diff --git a/drivers/ieee1394/nodemgr.h b/drivers/ieee1394/nodemgr.h index 0b26616e16c..0e1e7d93078 100644 --- a/drivers/ieee1394/nodemgr.h +++ b/drivers/ieee1394/nodemgr.h @@ -21,9 +21,15 @@ #define _IEEE1394_NODEMGR_H #include <linux/device.h> -#include "csr1212.h" +#include <asm/types.h> + #include "ieee1394_core.h" -#include "ieee1394_hotplug.h" +#include "ieee1394_types.h" + +struct csr1212_csr; +struct csr1212_keyval; +struct hpsb_host; +struct ieee1394_device_id; /* '1' '3' '9' '4' in ASCII */ #define IEEE1394_BUSID_MAGIC __constant_cpu_to_be32(0x31333934) @@ -44,7 +50,6 @@ struct bus_options { u16 max_rec; /* Maximum packet size node can receive */ }; - #define UNIT_DIRECTORY_VENDOR_ID 0x01 #define UNIT_DIRECTORY_MODEL_ID 0x02 #define UNIT_DIRECTORY_SPECIFIER_ID 0x04 @@ -59,8 +64,8 @@ struct bus_options { * unit directory for each of these protocols. */ struct unit_directory { - struct node_entry *ne; /* The node which this directory belongs to */ - octlet_t address; /* Address of the unit directory on the node */ + struct node_entry *ne; /* The node which this directory belongs to */ + octlet_t address; /* Address of the unit directory on the node */ u8 flags; /* Indicates which entries were read */ quadlet_t vendor_id; @@ -79,11 +84,10 @@ struct unit_directory { int length; /* Number of quadlets */ struct device device; - struct class_device class_dev; struct csr1212_keyval *ud_kv; - u32 lun; /* logical unit number immediate value */ + u32 lun; /* logical unit number immediate value */ }; struct node_entry { @@ -103,10 +107,8 @@ struct node_entry { const char *vendor_oui; u32 capabilities; - struct hpsb_tlabel_pool *tpool; struct device device; - struct class_device class_dev; /* Means this node is not attached anymore */ @@ -153,8 +155,8 @@ static inline int hpsb_node_entry_valid(struct node_entry *ne) /* * This will fill in the given, pre-initialised hpsb_packet with the current * information from the node entry (host, node ID, generation number). It will - * return false if the node owning the GUID is not accessible (and not modify the - * hpsb_packet) and return true otherwise. + * return false if the node owning the GUID is not accessible (and not modify + * the hpsb_packet) and return true otherwise. * * Note that packet sending may still fail in hpsb_send_packet if a bus reset * happens while you are trying to set up the packet (due to obsolete generation @@ -170,16 +172,13 @@ int hpsb_node_write(struct node_entry *ne, u64 addr, int hpsb_node_lock(struct node_entry *ne, u64 addr, int extcode, quadlet_t *data, quadlet_t arg); - /* Iterate the hosts, calling a given function with supplied data for each * host. */ int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *)); - int init_ieee1394_nodemgr(void); void cleanup_ieee1394_nodemgr(void); - /* The template for a host device */ extern struct device nodemgr_dev_template_host; diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c index 448df277337..8fd0030475b 100644 --- a/drivers/ieee1394/ohci1394.c +++ b/drivers/ieee1394/ohci1394.c @@ -136,7 +136,7 @@ #define DBGMSG(fmt, args...) \ printk(KERN_INFO "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args) #else -#define DBGMSG(fmt, args...) +#define DBGMSG(fmt, args...) do {} while (0) #endif #ifdef CONFIG_IEEE1394_OHCI_DMA_DEBUG @@ -148,8 +148,8 @@ printk(KERN_INFO "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host-> --global_outstanding_dmas, ## args) static int global_outstanding_dmas = 0; #else -#define OHCI_DMA_ALLOC(fmt, args...) -#define OHCI_DMA_FREE(fmt, args...) +#define OHCI_DMA_ALLOC(fmt, args...) do {} while (0) +#define OHCI_DMA_FREE(fmt, args...) do {} while (0) #endif /* print general (card independent) information */ @@ -181,36 +181,35 @@ static int alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d, static void ohci1394_pci_remove(struct pci_dev *pdev); #ifndef __LITTLE_ENDIAN -static unsigned hdr_sizes[] = -{ +const static size_t hdr_sizes[] = { 3, /* TCODE_WRITEQ */ 4, /* TCODE_WRITEB */ 3, /* TCODE_WRITE_RESPONSE */ - 0, /* ??? */ + 0, /* reserved */ 3, /* TCODE_READQ */ 4, /* TCODE_READB */ 3, /* TCODE_READQ_RESPONSE */ 4, /* TCODE_READB_RESPONSE */ - 1, /* TCODE_CYCLE_START (???) */ + 1, /* TCODE_CYCLE_START */ 4, /* TCODE_LOCK_REQUEST */ 2, /* TCODE_ISO_DATA */ 4, /* TCODE_LOCK_RESPONSE */ + /* rest is reserved or link-internal */ }; -/* Swap headers */ -static inline void packet_swab(quadlet_t *data, int tcode) +static inline void header_le32_to_cpu(quadlet_t *data, unsigned char tcode) { - size_t size = hdr_sizes[tcode]; + size_t size; - if (tcode > TCODE_LOCK_RESPONSE || hdr_sizes[tcode] == 0) + if (unlikely(tcode >= ARRAY_SIZE(hdr_sizes))) return; + size = hdr_sizes[tcode]; while (size--) - data[size] = swab32(data[size]); + data[size] = le32_to_cpu(data[size]); } #else -/* Don't waste cycles on same sex byte swaps */ -#define packet_swab(w,x) +#define header_le32_to_cpu(w,x) do {} while (0) #endif /* !LITTLE_ENDIAN */ /*********************************** @@ -701,7 +700,7 @@ static void insert_packet(struct ti_ohci *ohci, d->prg_cpu[idx]->data[2] = packet->header[2]; d->prg_cpu[idx]->data[3] = packet->header[3]; } - packet_swab(d->prg_cpu[idx]->data, packet->tcode); + header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode); } if (packet->data_size) { /* block transmit */ @@ -777,7 +776,7 @@ static void insert_packet(struct ti_ohci *ohci, d->prg_cpu[idx]->data[0] = packet->speed_code<<16 | (packet->header[0] & 0xFFFF); d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000; - packet_swab(d->prg_cpu[idx]->data, packet->tcode); + header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode); d->prg_cpu[idx]->begin.control = cpu_to_le32(DMA_CTL_OUTPUT_MORE | @@ -2598,8 +2597,9 @@ static const int TCODE_SIZE[16] = {20, 0, 16, -1, 16, 20, 20, 0, * Determine the length of a packet in the buffer * Optimization suggested by Pascal Drolet <pascal.drolet@informission.ca> */ -static __inline__ int packet_length(struct dma_rcv_ctx *d, int idx, quadlet_t *buf_ptr, - int offset, unsigned char tcode, int noswap) +static inline int packet_length(struct dma_rcv_ctx *d, int idx, + quadlet_t *buf_ptr, int offset, + unsigned char tcode, int noswap) { int length = -1; @@ -2730,7 +2730,7 @@ static void dma_rcv_tasklet (unsigned long data) * bus reset. We always ignore it. */ if (tcode != OHCI1394_TCODE_PHY) { if (!ohci->no_swap_incoming) - packet_swab(d->spb, tcode); + header_le32_to_cpu(d->spb, tcode); DBGMSG("Packet received from node" " %d ack=0x%02X spd=%d tcode=0x%X" " length=%d ctx=%d tlabel=%d", @@ -2738,7 +2738,7 @@ static void dma_rcv_tasklet (unsigned long data) (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f, (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>21)&0x3, tcode, length, d->ctx, - (cond_le32_to_cpu(d->spb[0], ohci->no_swap_incoming)>>10)&0x3f); + (d->spb[0]>>10)&0x3f); ack = (((cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f) == 0x11) ? 1 : 0; @@ -3529,9 +3529,10 @@ static void ohci1394_pci_remove(struct pci_dev *pdev) put_device(dev); } - +#ifdef CONFIG_PM static int ohci1394_pci_resume (struct pci_dev *pdev) { +/* PowerMac resume code comes first */ #ifdef CONFIG_PPC_PMAC if (machine_is(powermac)) { struct device_node *of_node; @@ -3543,17 +3544,23 @@ static int ohci1394_pci_resume (struct pci_dev *pdev) } #endif /* CONFIG_PPC_PMAC */ + pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); - pci_enable_device(pdev); - - return 0; + return pci_enable_device(pdev); } - static int ohci1394_pci_suspend (struct pci_dev *pdev, pm_message_t state) { - pci_save_state(pdev); + int err; + + err = pci_save_state(pdev); + if (err) + goto out; + err = pci_set_power_state(pdev, pci_choose_state(pdev, state)); + if (err) + goto out; +/* PowerMac suspend code comes last */ #ifdef CONFIG_PPC_PMAC if (machine_is(powermac)) { struct device_node *of_node; @@ -3563,11 +3570,11 @@ static int ohci1394_pci_suspend (struct pci_dev *pdev, pm_message_t state) if (of_node) pmac_call_feature(PMAC_FTR_1394_ENABLE, of_node, 0, 0); } -#endif - - return 0; +#endif /* CONFIG_PPC_PMAC */ +out: + return err; } - +#endif /* CONFIG_PM */ #define PCI_CLASS_FIREWIRE_OHCI ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10) @@ -3590,8 +3597,10 @@ static struct pci_driver ohci1394_pci_driver = { .id_table = ohci1394_pci_tbl, .probe = ohci1394_pci_probe, .remove = ohci1394_pci_remove, +#ifdef CONFIG_PM .resume = ohci1394_pci_resume, .suspend = ohci1394_pci_suspend, +#endif }; /*********************************** @@ -3718,5 +3727,7 @@ static int __init ohci1394_init(void) return pci_register_driver(&ohci1394_pci_driver); } -module_init(ohci1394_init); +/* Register before most other device drivers. + * Useful for remote debugging via physical DMA, e.g. using firescope. */ +fs_initcall(ohci1394_init); module_exit(ohci1394_cleanup); diff --git a/drivers/ieee1394/raw1394-private.h b/drivers/ieee1394/raw1394-private.h index c93587be9ca..c7731d1bcd8 100644 --- a/drivers/ieee1394/raw1394-private.h +++ b/drivers/ieee1394/raw1394-private.h @@ -29,9 +29,8 @@ struct file_info { struct list_head req_pending; struct list_head req_complete; - struct semaphore complete_sem; spinlock_t reqlists_lock; - wait_queue_head_t poll_wait_complete; + wait_queue_head_t wait_complete; struct list_head addr_list; diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index 571ea68c0cf..5ec4f5eb6b1 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -44,14 +44,15 @@ #include <linux/compat.h> #include "csr1212.h" +#include "highlevel.h" +#include "hosts.h" #include "ieee1394.h" -#include "ieee1394_types.h" #include "ieee1394_core.h" -#include "nodemgr.h" -#include "hosts.h" -#include "highlevel.h" -#include "iso.h" +#include "ieee1394_hotplug.h" #include "ieee1394_transactions.h" +#include "ieee1394_types.h" +#include "iso.h" +#include "nodemgr.h" #include "raw1394.h" #include "raw1394-private.h" @@ -66,7 +67,7 @@ #define DBGMSG(fmt, args...) \ printk(KERN_INFO "raw1394:" fmt "\n" , ## args) #else -#define DBGMSG(fmt, args...) +#define DBGMSG(fmt, args...) do {} while (0) #endif static LIST_HEAD(host_info_list); @@ -132,10 +133,9 @@ static void free_pending_request(struct pending_request *req) static void __queue_complete_req(struct pending_request *req) { struct file_info *fi = req->file_info; - list_move_tail(&req->list, &fi->req_complete); - up(&fi->complete_sem); - wake_up_interruptible(&fi->poll_wait_complete); + list_move_tail(&req->list, &fi->req_complete); + wake_up(&fi->wait_complete); } static void queue_complete_req(struct pending_request *req) @@ -463,13 +463,36 @@ raw1394_compat_read(const char __user *buf, struct raw1394_request *r) #endif +/* get next completed request (caller must hold fi->reqlists_lock) */ +static inline struct pending_request *__next_complete_req(struct file_info *fi) +{ + struct list_head *lh; + struct pending_request *req = NULL; + + if (!list_empty(&fi->req_complete)) { + lh = fi->req_complete.next; + list_del(lh); + req = list_entry(lh, struct pending_request, list); + } + return req; +} + +/* atomically get next completed request */ +static struct pending_request *next_complete_req(struct file_info *fi) +{ + unsigned long flags; + struct pending_request *req; + + spin_lock_irqsave(&fi->reqlists_lock, flags); + req = __next_complete_req(fi); + spin_unlock_irqrestore(&fi->reqlists_lock, flags); + return req; +} static ssize_t raw1394_read(struct file *file, char __user * buffer, size_t count, loff_t * offset_is_ignored) { - unsigned long flags; struct file_info *fi = (struct file_info *)file->private_data; - struct list_head *lh; struct pending_request *req; ssize_t ret; @@ -487,22 +510,21 @@ static ssize_t raw1394_read(struct file *file, char __user * buffer, } if (file->f_flags & O_NONBLOCK) { - if (down_trylock(&fi->complete_sem)) { + if (!(req = next_complete_req(fi))) return -EAGAIN; - } } else { - if (down_interruptible(&fi->complete_sem)) { + /* + * NB: We call the macro wait_event_interruptible() with a + * condition argument with side effect. This is only possible + * because the side effect does not occur until the condition + * became true, and wait_event_interruptible() won't evaluate + * the condition again after that. + */ + if (wait_event_interruptible(fi->wait_complete, + (req = next_complete_req(fi)))) return -ERESTARTSYS; - } } - spin_lock_irqsave(&fi->reqlists_lock, flags); - lh = fi->req_complete.next; - list_del(lh); - spin_unlock_irqrestore(&fi->reqlists_lock, flags); - - req = list_entry(lh, struct pending_request, list); - if (req->req.length) { if (copy_to_user(int2ptr(req->req.recvb), req->data, req->req.length)) { @@ -1752,6 +1774,7 @@ static int arm_register(struct file_info *fi, struct pending_request *req) addr->notification_options |= addr->client_transactions; addr->recvb = req->req.recvb; addr->rec_length = (u16) ((req->req.misc >> 16) & 0xFFFF); + spin_lock_irqsave(&host_info_lock, flags); hi = find_host_info(fi->host); same_host = 0; @@ -1777,9 +1800,9 @@ static int arm_register(struct file_info *fi, struct pending_request *req) } if (same_host) { /* addressrange occupied by same host */ + spin_unlock_irqrestore(&host_info_lock, flags); vfree(addr->addr_space_buffer); kfree(addr); - spin_unlock_irqrestore(&host_info_lock, flags); return (-EALREADY); } /* another host with valid address-entry containing same addressrange */ @@ -1807,6 +1830,8 @@ static int arm_register(struct file_info *fi, struct pending_request *req) } } } + spin_unlock_irqrestore(&host_info_lock, flags); + if (another_host) { DBGMSG("another hosts entry is valid -> SUCCESS"); if (copy_to_user(int2ptr(req->req.recvb), @@ -1815,11 +1840,11 @@ static int arm_register(struct file_info *fi, struct pending_request *req) " address-range-entry is invalid -> EFAULT !!!\n"); vfree(addr->addr_space_buffer); kfree(addr); - spin_unlock_irqrestore(&host_info_lock, flags); return (-EFAULT); } free_pending_request(req); /* immediate success or fail */ /* INSERT ENTRY */ + spin_lock_irqsave(&host_info_lock, flags); list_add_tail(&addr->addr_list, &fi->addr_list); spin_unlock_irqrestore(&host_info_lock, flags); return sizeof(struct raw1394_request); @@ -1830,15 +1855,15 @@ static int arm_register(struct file_info *fi, struct pending_request *req) req->req.address + req->req.length); if (retval) { /* INSERT ENTRY */ + spin_lock_irqsave(&host_info_lock, flags); list_add_tail(&addr->addr_list, &fi->addr_list); + spin_unlock_irqrestore(&host_info_lock, flags); } else { DBGMSG("arm_register failed errno: %d \n", retval); vfree(addr->addr_space_buffer); kfree(addr); - spin_unlock_irqrestore(&host_info_lock, flags); return (-EALREADY); } - spin_unlock_irqrestore(&host_info_lock, flags); free_pending_request(req); /* immediate success or fail */ return sizeof(struct raw1394_request); } @@ -1904,10 +1929,10 @@ static int arm_unregister(struct file_info *fi, struct pending_request *req) if (another_host) { DBGMSG("delete entry from list -> success"); list_del(&addr->addr_list); + spin_unlock_irqrestore(&host_info_lock, flags); vfree(addr->addr_space_buffer); kfree(addr); free_pending_request(req); /* immediate success or fail */ - spin_unlock_irqrestore(&host_info_lock, flags); return sizeof(struct raw1394_request); } retval = @@ -1949,23 +1974,19 @@ static int arm_get_buf(struct file_info *fi, struct pending_request *req) (arm_addr->end > req->req.address)) { if (req->req.address + req->req.length <= arm_addr->end) { offset = req->req.address - arm_addr->start; + spin_unlock_irqrestore(&host_info_lock, flags); DBGMSG ("arm_get_buf copy_to_user( %08X, %p, %u )", (u32) req->req.recvb, arm_addr->addr_space_buffer + offset, (u32) req->req.length); - if (copy_to_user (int2ptr(req->req.recvb), arm_addr->addr_space_buffer + offset, - req->req.length)) { - spin_unlock_irqrestore(&host_info_lock, - flags); + req->req.length)) return (-EFAULT); - } - spin_unlock_irqrestore(&host_info_lock, flags); /* We have to free the request, because we * queue no response, and therefore nobody * will free it. */ @@ -2005,24 +2026,23 @@ static int arm_set_buf(struct file_info *fi, struct pending_request *req) (arm_addr->end > req->req.address)) { if (req->req.address + req->req.length <= arm_addr->end) { offset = req->req.address - arm_addr->start; + spin_unlock_irqrestore(&host_info_lock, flags); DBGMSG ("arm_set_buf copy_from_user( %p, %08X, %u )", arm_addr->addr_space_buffer + offset, (u32) req->req.sendb, (u32) req->req.length); - if (copy_from_user (arm_addr->addr_space_buffer + offset, int2ptr(req->req.sendb), - req->req.length)) { - spin_unlock_irqrestore(&host_info_lock, - flags); + req->req.length)) return (-EFAULT); - } - spin_unlock_irqrestore(&host_info_lock, flags); - free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ + /* We have to free the request, because we + * queue no response, and therefore nobody + * will free it. */ + free_pending_request(req); return sizeof(struct raw1394_request); } else { DBGMSG("arm_set_buf request exceeded mapping"); @@ -2744,7 +2764,7 @@ static unsigned int raw1394_poll(struct file *file, poll_table * pt) unsigned int mask = POLLOUT | POLLWRNORM; unsigned long flags; - poll_wait(file, &fi->poll_wait_complete, pt); + poll_wait(file, &fi->wait_complete, pt); spin_lock_irqsave(&fi->reqlists_lock, flags); if (!list_empty(&fi->req_complete)) { @@ -2769,9 +2789,8 @@ static int raw1394_open(struct inode *inode, struct file *file) fi->state = opened; INIT_LIST_HEAD(&fi->req_pending); INIT_LIST_HEAD(&fi->req_complete); - sema_init(&fi->complete_sem, 0); spin_lock_init(&fi->reqlists_lock); - init_waitqueue_head(&fi->poll_wait_complete); + init_waitqueue_head(&fi->wait_complete); INIT_LIST_HEAD(&fi->addr_list); file->private_data = fi; @@ -2784,7 +2803,7 @@ static int raw1394_release(struct inode *inode, struct file *file) struct file_info *fi = file->private_data; struct list_head *lh; struct pending_request *req; - int done = 0, i, fail = 0; + int i, fail; int retval = 0; struct list_head *entry; struct arm_addr *addr = NULL; @@ -2864,25 +2883,28 @@ static int raw1394_release(struct inode *inode, struct file *file) "error(s) occurred \n"); } - while (!done) { + for (;;) { + /* This locked section guarantees that neither + * complete nor pending requests exist once i!=0 */ spin_lock_irqsave(&fi->reqlists_lock, flags); - - while (!list_empty(&fi->req_complete)) { - lh = fi->req_complete.next; - list_del(lh); - - req = list_entry(lh, struct pending_request, list); - + while ((req = __next_complete_req(fi))) free_pending_request(req); - } - - if (list_empty(&fi->req_pending)) - done = 1; + i = list_empty(&fi->req_pending); spin_unlock_irqrestore(&fi->reqlists_lock, flags); - if (!done) - down_interruptible(&fi->complete_sem); + if (i) + break; + /* + * Sleep until more requests can be freed. + * + * NB: We call the macro wait_event() with a condition argument + * with side effect. This is only possible because the side + * effect does not occur until the condition became true, and + * wait_event() won't evaluate the condition again after that. + */ + wait_event(fi->wait_complete, (req = next_complete_req(fi))); + free_pending_request(req); } /* Remove any sub-trees left by user space programs */ diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index b08755e2e68..6986ac18828 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -38,31 +38,36 @@ * but the code needs additional debugging. */ +#include <linux/blkdev.h> +#include <linux/compiler.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/gfp.h> +#include <linux/init.h> #include <linux/kernel.h> #include <linux/list.h> -#include <linux/string.h> -#include <linux/stringify.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/fs.h> -#include <linux/poll.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/types.h> -#include <linux/delay.h> -#include <linux/sched.h> -#include <linux/blkdev.h> -#include <linux/smp_lock.h> -#include <linux/init.h> #include <linux/pci.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/stat.h> +#include <linux/string.h> +#include <linux/stringify.h> +#include <linux/types.h> +#include <linux/wait.h> -#include <asm/current.h> -#include <asm/uaccess.h> -#include <asm/io.h> #include <asm/byteorder.h> -#include <asm/atomic.h> -#include <asm/system.h> +#include <asm/errno.h> +#include <asm/param.h> #include <asm/scatterlist.h> +#include <asm/system.h> +#include <asm/types.h> + +#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA +#include <asm/io.h> /* for bus_to_virt */ +#endif #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> @@ -71,13 +76,14 @@ #include <scsi/scsi_host.h> #include "csr1212.h" +#include "highlevel.h" +#include "hosts.h" #include "ieee1394.h" -#include "ieee1394_types.h" #include "ieee1394_core.h" -#include "nodemgr.h" -#include "hosts.h" -#include "highlevel.h" +#include "ieee1394_hotplug.h" #include "ieee1394_transactions.h" +#include "ieee1394_types.h" +#include "nodemgr.h" #include "sbp2.h" /* @@ -173,11 +179,6 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0" ", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE) ", or a combination)"); -/* legacy parameter */ -static int force_inquiry_hack; -module_param(force_inquiry_hack, int, 0644); -MODULE_PARM_DESC(force_inquiry_hack, "Deprecated, use 'workarounds'"); - /* * Export information about protocols/devices supported by this driver. */ @@ -208,9 +209,9 @@ static u32 global_outstanding_command_orbs = 0; #define outstanding_orb_incr global_outstanding_command_orbs++ #define outstanding_orb_decr global_outstanding_command_orbs-- #else -#define SBP2_ORB_DEBUG(fmt, args...) -#define outstanding_orb_incr -#define outstanding_orb_decr +#define SBP2_ORB_DEBUG(fmt, args...) do {} while (0) +#define outstanding_orb_incr do {} while (0) +#define outstanding_orb_decr do {} while (0) #endif #ifdef CONFIG_IEEE1394_SBP2_DEBUG_DMA @@ -222,8 +223,8 @@ static u32 global_outstanding_command_orbs = 0; --global_outstanding_dmas, ## args) static u32 global_outstanding_dmas = 0; #else -#define SBP2_DMA_ALLOC(fmt, args...) -#define SBP2_DMA_FREE(fmt, args...) +#define SBP2_DMA_ALLOC(fmt, args...) do {} while (0) +#define SBP2_DMA_FREE(fmt, args...) do {} while (0) #endif #if CONFIG_IEEE1394_SBP2_DEBUG >= 2 @@ -237,7 +238,7 @@ static u32 global_outstanding_dmas = 0; #define SBP2_NOTICE(fmt, args...) HPSB_NOTICE("sbp2: "fmt, ## args) #define SBP2_WARN(fmt, args...) HPSB_WARN("sbp2: "fmt, ## args) #else -#define SBP2_DEBUG(fmt, args...) +#define SBP2_DEBUG(fmt, args...) do {} while (0) #define SBP2_INFO(fmt, args...) HPSB_INFO("sbp2: "fmt, ## args) #define SBP2_NOTICE(fmt, args...) HPSB_NOTICE("sbp2: "fmt, ## args) #define SBP2_WARN(fmt, args...) HPSB_WARN("sbp2: "fmt, ## args) @@ -356,7 +357,7 @@ static const struct { /* * Converts a buffer from be32 to cpu byte ordering. Length is in bytes. */ -static __inline__ void sbp2util_be32_to_cpu_buffer(void *buffer, int length) +static inline void sbp2util_be32_to_cpu_buffer(void *buffer, int length) { u32 *temp = buffer; @@ -369,7 +370,7 @@ static __inline__ void sbp2util_be32_to_cpu_buffer(void *buffer, int length) /* * Converts a buffer from cpu to be32 byte ordering. Length is in bytes. */ -static __inline__ void sbp2util_cpu_to_be32_buffer(void *buffer, int length) +static inline void sbp2util_cpu_to_be32_buffer(void *buffer, int length) { u32 *temp = buffer; @@ -380,8 +381,8 @@ static __inline__ void sbp2util_cpu_to_be32_buffer(void *buffer, int length) } #else /* BIG_ENDIAN */ /* Why waste the cpu cycles? */ -#define sbp2util_be32_to_cpu_buffer(x,y) -#define sbp2util_cpu_to_be32_buffer(x,y) +#define sbp2util_be32_to_cpu_buffer(x,y) do {} while (0) +#define sbp2util_cpu_to_be32_buffer(x,y) do {} while (0) #endif #ifdef CONFIG_IEEE1394_SBP2_PACKET_DUMP @@ -417,24 +418,26 @@ static void sbp2util_packet_dump(void *buffer, int length, char *dump_name, return; } #else -#define sbp2util_packet_dump(w,x,y,z) +#define sbp2util_packet_dump(w,x,y,z) do {} while (0) #endif +static DECLARE_WAIT_QUEUE_HEAD(access_wq); + /* - * Goofy routine that basically does a down_timeout function. + * Waits for completion of an SBP-2 access request. + * Returns nonzero if timed out or prematurely interrupted. */ -static int sbp2util_down_timeout(atomic_t *done, int timeout) +static int sbp2util_access_timeout(struct scsi_id_instance_data *scsi_id, + int timeout) { - int i; + long leftover = wait_event_interruptible_timeout( + access_wq, scsi_id->access_complete, timeout); - for (i = timeout; (i > 0 && atomic_read(done) == 0); i-= HZ/10) { - if (msleep_interruptible(100)) /* 100ms */ - return 1; - } - return (i > 0) ? 0 : 1; + scsi_id->access_complete = 0; + return leftover <= 0; } -/* Free's an allocated packet */ +/* Frees an allocated packet */ static void sbp2_free_packet(struct hpsb_packet *packet) { hpsb_free_tlabel(packet); @@ -468,6 +471,44 @@ static int sbp2util_node_write_no_wait(struct node_entry *ne, u64 addr, return 0; } +static void sbp2util_notify_fetch_agent(struct scsi_id_instance_data *scsi_id, + u64 offset, quadlet_t *data, size_t len) +{ + /* + * There is a small window after a bus reset within which the node + * entry's generation is current but the reconnect wasn't completed. + */ + if (unlikely(atomic_read(&scsi_id->state) == SBP2LU_STATE_IN_RESET)) + return; + + if (hpsb_node_write(scsi_id->ne, + scsi_id->sbp2_command_block_agent_addr + offset, + data, len)) + SBP2_ERR("sbp2util_notify_fetch_agent failed."); + /* + * Now accept new SCSI commands, unless a bus reset happended during + * hpsb_node_write. + */ + if (likely(atomic_read(&scsi_id->state) != SBP2LU_STATE_IN_RESET)) + scsi_unblock_requests(scsi_id->scsi_host); +} + +static void sbp2util_write_orb_pointer(void *p) +{ + quadlet_t data[2]; + + data[0] = ORB_SET_NODE_ID( + ((struct scsi_id_instance_data *)p)->hi->host->node_id); + data[1] = ((struct scsi_id_instance_data *)p)->last_orb_dma; + sbp2util_cpu_to_be32_buffer(data, 8); + sbp2util_notify_fetch_agent(p, SBP2_ORB_POINTER_OFFSET, data, 8); +} + +static void sbp2util_write_doorbell(void *p) +{ + sbp2util_notify_fetch_agent(p, SBP2_DOORBELL_OFFSET, NULL, 4); +} + /* * This function is called to create a pool of command orbs used for * command processing. It is called when a new sbp2 device is detected. @@ -492,7 +533,7 @@ static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_i command->command_orb_dma = pci_map_single(hi->host->pdev, &command->command_orb, sizeof(struct sbp2_command_orb), - PCI_DMA_BIDIRECTIONAL); + PCI_DMA_TODEVICE); SBP2_DMA_ALLOC("single command orb DMA"); command->sge_dma = pci_map_single(hi->host->pdev, @@ -525,7 +566,7 @@ static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_ /* Release our generic DMA's */ pci_unmap_single(host->pdev, command->command_orb_dma, sizeof(struct sbp2_command_orb), - PCI_DMA_BIDIRECTIONAL); + PCI_DMA_TODEVICE); SBP2_DMA_FREE("single command orb DMA"); pci_unmap_single(host->pdev, command->sge_dma, sizeof(command->scatter_gather_element), @@ -715,6 +756,7 @@ static int sbp2_remove(struct device *dev) sbp2scsi_complete_all_commands(scsi_id, DID_NO_CONNECT); /* scsi_remove_device() will trigger shutdown functions of SCSI * highlevel drivers which would deadlock if blocked. */ + atomic_set(&scsi_id->state, SBP2LU_STATE_IN_SHUTDOWN); scsi_unblock_requests(scsi_id->scsi_host); } sdev = scsi_id->sdev; @@ -766,10 +808,12 @@ static int sbp2_update(struct unit_directory *ud) */ sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY); - /* Make sure we unblock requests (since this is likely after a bus - * reset). */ - scsi_unblock_requests(scsi_id->scsi_host); - + /* Accept new commands unless there was another bus reset in the + * meantime. */ + if (hpsb_node_entry_valid(scsi_id->ne)) { + atomic_set(&scsi_id->state, SBP2LU_STATE_RUNNING); + scsi_unblock_requests(scsi_id->scsi_host); + } return 0; } @@ -794,11 +838,12 @@ static struct scsi_id_instance_data *sbp2_alloc_device(struct unit_directory *ud scsi_id->speed_code = IEEE1394_SPEED_100; scsi_id->max_payload_size = sbp2_speedto_max_payload[IEEE1394_SPEED_100]; scsi_id->status_fifo_addr = CSR1212_INVALID_ADDR_SPACE; - atomic_set(&scsi_id->sbp2_login_complete, 0); INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_inuse); INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_completed); INIT_LIST_HEAD(&scsi_id->scsi_list); spin_lock_init(&scsi_id->sbp2_command_orb_lock); + atomic_set(&scsi_id->state, SBP2LU_STATE_RUNNING); + INIT_WORK(&scsi_id->protocol_work, NULL, NULL); ud->device.driver_data = scsi_id; @@ -881,11 +926,14 @@ static void sbp2_host_reset(struct hpsb_host *host) struct scsi_id_instance_data *scsi_id; hi = hpsb_get_hostinfo(&sbp2_highlevel, host); - - if (hi) { - list_for_each_entry(scsi_id, &hi->scsi_ids, scsi_list) + if (!hi) + return; + list_for_each_entry(scsi_id, &hi->scsi_ids, scsi_list) + if (likely(atomic_read(&scsi_id->state) != + SBP2LU_STATE_IN_SHUTDOWN)) { + atomic_set(&scsi_id->state, SBP2LU_STATE_IN_RESET); scsi_block_requests(scsi_id->scsi_host); - } + } } /* @@ -970,8 +1018,7 @@ static int sbp2_start_device(struct scsi_id_instance_data *scsi_id) * connected to the sbp2 device being removed. That host would * have a certain amount of time to relogin before the sbp2 device * allows someone else to login instead. One second makes sense. */ - msleep_interruptible(1000); - if (signal_pending(current)) { + if (msleep_interruptible(1000)) { sbp2_remove_device(scsi_id); return -EINTR; } @@ -1036,7 +1083,7 @@ static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id) scsi_remove_host(scsi_id->scsi_host); scsi_host_put(scsi_id->scsi_host); } - + flush_scheduled_work(); sbp2util_remove_command_orb_pool(scsi_id); list_del(&scsi_id->scsi_list); @@ -1182,17 +1229,14 @@ static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id) "sbp2 query logins orb", scsi_id->query_logins_orb_dma); memset(scsi_id->query_logins_response, 0, sizeof(struct sbp2_query_logins_response)); - memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block)); data[0] = ORB_SET_NODE_ID(hi->host->node_id); data[1] = scsi_id->query_logins_orb_dma; sbp2util_cpu_to_be32_buffer(data, 8); - atomic_set(&scsi_id->sbp2_login_complete, 0); - hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8); - if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, 2*HZ)) { + if (sbp2util_access_timeout(scsi_id, 2*HZ)) { SBP2_INFO("Error querying logins to SBP-2 device - timed out"); return -EIO; } @@ -1202,11 +1246,8 @@ static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id) return -EIO; } - if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) || - STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) || - STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) { - - SBP2_INFO("Error querying logins to SBP-2 device - timed out"); + if (STATUS_TEST_RDS(scsi_id->status_block.ORB_offset_hi_misc)) { + SBP2_INFO("Error querying logins to SBP-2 device - failed"); return -EIO; } @@ -1278,21 +1319,18 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id) "sbp2 login orb", scsi_id->login_orb_dma); memset(scsi_id->login_response, 0, sizeof(struct sbp2_login_response)); - memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block)); data[0] = ORB_SET_NODE_ID(hi->host->node_id); data[1] = scsi_id->login_orb_dma; sbp2util_cpu_to_be32_buffer(data, 8); - atomic_set(&scsi_id->sbp2_login_complete, 0); - hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8); /* * Wait for login status (up to 20 seconds)... */ - if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, 20*HZ)) { - SBP2_ERR("Error logging into SBP-2 device - login timed-out"); + if (sbp2util_access_timeout(scsi_id, 20*HZ)) { + SBP2_ERR("Error logging into SBP-2 device - timed out"); return -EIO; } @@ -1300,18 +1338,12 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id) * Sanity. Make sure status returned matches login orb. */ if (scsi_id->status_block.ORB_offset_lo != scsi_id->login_orb_dma) { - SBP2_ERR("Error logging into SBP-2 device - login timed-out"); + SBP2_ERR("Error logging into SBP-2 device - timed out"); return -EIO; } - /* - * Check status - */ - if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) || - STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) || - STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) { - - SBP2_ERR("Error logging into SBP-2 device - login failed"); + if (STATUS_TEST_RDS(scsi_id->status_block.ORB_offset_hi_misc)) { + SBP2_ERR("Error logging into SBP-2 device - failed"); return -EIO; } @@ -1335,9 +1367,7 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id) scsi_id->sbp2_command_block_agent_addr &= 0x0000ffffffffffffULL; SBP2_INFO("Logged into SBP-2 device"); - return 0; - } /* @@ -1387,21 +1417,17 @@ static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id) data[1] = scsi_id->logout_orb_dma; sbp2util_cpu_to_be32_buffer(data, 8); - atomic_set(&scsi_id->sbp2_login_complete, 0); - error = hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8); if (error) return error; /* Wait for device to logout...1 second. */ - if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, HZ)) + if (sbp2util_access_timeout(scsi_id, HZ)) return -EIO; SBP2_INFO("Logged out of SBP-2 device"); - return 0; - } /* @@ -1445,20 +1471,10 @@ static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id) sbp2util_packet_dump(scsi_id->reconnect_orb, sizeof(struct sbp2_reconnect_orb), "sbp2 reconnect orb", scsi_id->reconnect_orb_dma); - /* - * Initialize status fifo - */ - memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block)); - - /* - * Ok, let's write to the target's management agent register - */ data[0] = ORB_SET_NODE_ID(hi->host->node_id); data[1] = scsi_id->reconnect_orb_dma; sbp2util_cpu_to_be32_buffer(data, 8); - atomic_set(&scsi_id->sbp2_login_complete, 0); - error = hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8); if (error) @@ -1467,8 +1483,8 @@ static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id) /* * Wait for reconnect status (up to 1 second)... */ - if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, HZ)) { - SBP2_ERR("Error reconnecting to SBP-2 device - reconnect timed-out"); + if (sbp2util_access_timeout(scsi_id, HZ)) { + SBP2_ERR("Error reconnecting to SBP-2 device - timed out"); return -EIO; } @@ -1476,25 +1492,17 @@ static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id) * Sanity. Make sure status returned matches reconnect orb. */ if (scsi_id->status_block.ORB_offset_lo != scsi_id->reconnect_orb_dma) { - SBP2_ERR("Error reconnecting to SBP-2 device - reconnect timed-out"); + SBP2_ERR("Error reconnecting to SBP-2 device - timed out"); return -EIO; } - /* - * Check status - */ - if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) || - STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) || - STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) { - - SBP2_ERR("Error reconnecting to SBP-2 device - reconnect failed"); + if (STATUS_TEST_RDS(scsi_id->status_block.ORB_offset_hi_misc)) { + SBP2_ERR("Error reconnecting to SBP-2 device - failed"); return -EIO; } HPSB_DEBUG("Reconnected to SBP-2 device"); - return 0; - } /* @@ -1592,11 +1600,6 @@ static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id, } workarounds = sbp2_default_workarounds; - if (force_inquiry_hack) { - SBP2_WARN("force_inquiry_hack is deprecated. " - "Use parameter 'workarounds' instead."); - workarounds |= SBP2_WORKAROUND_INQUIRY_36; - } if (!(workarounds & SBP2_WORKAROUND_OVERRIDE)) for (i = 0; i < ARRAY_SIZE(sbp2_workarounds_table); i++) { @@ -1705,9 +1708,14 @@ static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait) quadlet_t data; u64 addr; int retval; + unsigned long flags; SBP2_DEBUG_ENTER(); + cancel_delayed_work(&scsi_id->protocol_work); + if (wait) + flush_scheduled_work(); + data = ntohl(SBP2_AGENT_RESET_DATA); addr = scsi_id->sbp2_command_block_agent_addr + SBP2_AGENT_RESET_OFFSET; @@ -1724,7 +1732,9 @@ static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait) /* * Need to make sure orb pointer is written on next command */ + spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); scsi_id->last_orb = NULL; + spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); return 0; } @@ -1961,13 +1971,17 @@ static void sbp2_create_command_orb(struct scsi_id_instance_data *scsi_id, /* * This function is called in order to begin a regular SBP-2 command. */ -static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id, +static void sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id, struct sbp2_command_info *command) { struct sbp2scsi_host_info *hi = scsi_id->hi; struct sbp2_command_orb *command_orb = &command->command_orb; - struct node_entry *ne = scsi_id->ne; - u64 addr; + struct sbp2_command_orb *last_orb; + dma_addr_t last_orb_dma; + u64 addr = scsi_id->sbp2_command_block_agent_addr; + quadlet_t data[2]; + size_t length; + unsigned long flags; outstanding_orb_incr; SBP2_ORB_DEBUG("sending command orb %p, total orbs = %x", @@ -1975,73 +1989,70 @@ static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id, pci_dma_sync_single_for_device(hi->host->pdev, command->command_orb_dma, sizeof(struct sbp2_command_orb), - PCI_DMA_BIDIRECTIONAL); + PCI_DMA_TODEVICE); pci_dma_sync_single_for_device(hi->host->pdev, command->sge_dma, sizeof(command->scatter_gather_element), PCI_DMA_BIDIRECTIONAL); /* * Check to see if there are any previous orbs to use */ - if (scsi_id->last_orb == NULL) { - quadlet_t data[2]; - + spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); + last_orb = scsi_id->last_orb; + last_orb_dma = scsi_id->last_orb_dma; + if (!last_orb) { /* - * Ok, let's write to the target's management agent register + * last_orb == NULL means: We know that the target's fetch agent + * is not active right now. */ - addr = scsi_id->sbp2_command_block_agent_addr + SBP2_ORB_POINTER_OFFSET; + addr += SBP2_ORB_POINTER_OFFSET; data[0] = ORB_SET_NODE_ID(hi->host->node_id); data[1] = command->command_orb_dma; sbp2util_cpu_to_be32_buffer(data, 8); - - SBP2_ORB_DEBUG("write command agent, command orb %p", command_orb); - - if (sbp2util_node_write_no_wait(ne, addr, data, 8) < 0) { - SBP2_ERR("sbp2util_node_write_no_wait failed.\n"); - return -EIO; - } - - SBP2_ORB_DEBUG("write command agent complete"); - - scsi_id->last_orb = command_orb; - scsi_id->last_orb_dma = command->command_orb_dma; - + length = 8; } else { - quadlet_t data; - /* - * We have an orb already sent (maybe or maybe not - * processed) that we can append this orb to. So do so, - * and ring the doorbell. Have to be very careful - * modifying these next orb pointers, as they are accessed - * both by the sbp2 device and us. + * last_orb != NULL means: We know that the target's fetch agent + * is (very probably) not dead or in reset state right now. + * We have an ORB already sent that we can append a new one to. + * The target's fetch agent may or may not have read this + * previous ORB yet. */ - scsi_id->last_orb->next_ORB_lo = - cpu_to_be32(command->command_orb_dma); + pci_dma_sync_single_for_cpu(hi->host->pdev, last_orb_dma, + sizeof(struct sbp2_command_orb), + PCI_DMA_TODEVICE); + last_orb->next_ORB_lo = cpu_to_be32(command->command_orb_dma); + wmb(); /* Tells hardware that this pointer is valid */ - scsi_id->last_orb->next_ORB_hi = 0x0; - pci_dma_sync_single_for_device(hi->host->pdev, - scsi_id->last_orb_dma, + last_orb->next_ORB_hi = 0; + pci_dma_sync_single_for_device(hi->host->pdev, last_orb_dma, sizeof(struct sbp2_command_orb), - PCI_DMA_BIDIRECTIONAL); + PCI_DMA_TODEVICE); + addr += SBP2_DOORBELL_OFFSET; + data[0] = 0; + length = 4; + } + scsi_id->last_orb = command_orb; + scsi_id->last_orb_dma = command->command_orb_dma; + spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); + SBP2_ORB_DEBUG("write to %s register, command orb %p", + last_orb ? "DOORBELL" : "ORB_POINTER", command_orb); + if (sbp2util_node_write_no_wait(scsi_id->ne, addr, data, length)) { /* - * Ring the doorbell + * sbp2util_node_write_no_wait failed. We certainly ran out + * of transaction labels, perhaps just because there were no + * context switches which gave khpsbpkt a chance to collect + * free tlabels. Try again in non-atomic context. If necessary, + * the workqueue job will sleep to guaranteedly get a tlabel. + * We do not accept new commands until the job is over. */ - data = cpu_to_be32(command->command_orb_dma); - addr = scsi_id->sbp2_command_block_agent_addr + SBP2_DOORBELL_OFFSET; - - SBP2_ORB_DEBUG("ring doorbell, command orb %p", command_orb); - - if (sbp2util_node_write_no_wait(ne, addr, &data, 4) < 0) { - SBP2_ERR("sbp2util_node_write_no_wait failed"); - return -EIO; - } - - scsi_id->last_orb = command_orb; - scsi_id->last_orb_dma = command->command_orb_dma; - + scsi_block_requests(scsi_id->scsi_host); + PREPARE_WORK(&scsi_id->protocol_work, + last_orb ? sbp2util_write_doorbell: + sbp2util_write_orb_pointer, + scsi_id); + schedule_work(&scsi_id->protocol_work); } - return 0; } /* @@ -2078,11 +2089,6 @@ static int sbp2_send_command(struct scsi_id_instance_data *scsi_id, "sbp2 command orb", command->command_orb_dma); /* - * Initialize status fifo - */ - memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block)); - - /* * Link up the orb, and ring the doorbell if needed */ sbp2_link_orb_command(scsi_id, command); @@ -2123,12 +2129,14 @@ static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status, unchar *sense /* * This function deals with status writes from the SBP-2 device */ -static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int destid, - quadlet_t *data, u64 addr, size_t length, u16 fl) +static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, + int destid, quadlet_t *data, u64 addr, + size_t length, u16 fl) { struct sbp2scsi_host_info *hi; struct scsi_id_instance_data *scsi_id = NULL, *scsi_id_tmp; struct scsi_cmnd *SCpnt = NULL; + struct sbp2_status_block *sb; u32 scsi_status = SBP2_SCSI_STATUS_GOOD; struct sbp2_command_info *command; unsigned long flags; @@ -2137,18 +2145,19 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest sbp2util_packet_dump(data, length, "sbp2 status write by device", (u32)addr); - if (!host) { + if (unlikely(length < 8 || length > sizeof(struct sbp2_status_block))) { + SBP2_ERR("Wrong size of status block"); + return RCODE_ADDRESS_ERROR; + } + if (unlikely(!host)) { SBP2_ERR("host is NULL - this is bad!"); return RCODE_ADDRESS_ERROR; } - hi = hpsb_get_hostinfo(&sbp2_highlevel, host); - - if (!hi) { + if (unlikely(!hi)) { SBP2_ERR("host info is NULL - this is bad!"); return RCODE_ADDRESS_ERROR; } - /* * Find our scsi_id structure by looking at the status fifo address * written to by the sbp2 device. @@ -2160,32 +2169,35 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest break; } } - - if (!scsi_id) { + if (unlikely(!scsi_id)) { SBP2_ERR("scsi_id is NULL - device is gone?"); return RCODE_ADDRESS_ERROR; } /* - * Put response into scsi_id status fifo... + * Put response into scsi_id status fifo buffer. The first two bytes + * come in big endian bit order. Often the target writes only a + * truncated status block, minimally the first two quadlets. The rest + * is implied to be zeros. */ - memcpy(&scsi_id->status_block, data, length); + sb = &scsi_id->status_block; + memset(sb->command_set_dependent, 0, sizeof(sb->command_set_dependent)); + memcpy(sb, data, length); + sbp2util_be32_to_cpu_buffer(sb, 8); /* - * Byte swap first two quadlets (8 bytes) of status for processing + * Ignore unsolicited status. Handle command ORB status. */ - sbp2util_be32_to_cpu_buffer(&scsi_id->status_block, 8); - - /* - * Handle command ORB status here if necessary. First, need to match status with command. - */ - command = sbp2util_find_command_for_orb(scsi_id, scsi_id->status_block.ORB_offset_lo); + if (unlikely(STATUS_GET_SRC(sb->ORB_offset_hi_misc) == 2)) + command = NULL; + else + command = sbp2util_find_command_for_orb(scsi_id, + sb->ORB_offset_lo); if (command) { - SBP2_DEBUG("Found status for command ORB"); pci_dma_sync_single_for_cpu(hi->host->pdev, command->command_orb_dma, sizeof(struct sbp2_command_orb), - PCI_DMA_BIDIRECTIONAL); + PCI_DMA_TODEVICE); pci_dma_sync_single_for_cpu(hi->host->pdev, command->sge_dma, sizeof(command->scatter_gather_element), PCI_DMA_BIDIRECTIONAL); @@ -2194,7 +2206,12 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest outstanding_orb_decr; /* - * Matched status with command, now grab scsi command pointers and check status + * Matched status with command, now grab scsi command pointers + * and check status. + */ + /* + * FIXME: If the src field in the status is 1, the ORB DMA must + * not be reused until status for a subsequent ORB is received. */ SCpnt = command->Current_SCpnt; spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); @@ -2202,61 +2219,64 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); if (SCpnt) { - + u32 h = sb->ORB_offset_hi_misc; + u32 r = STATUS_GET_RESP(h); + + if (r != RESP_STATUS_REQUEST_COMPLETE) { + SBP2_WARN("resp 0x%x, sbp_status 0x%x", + r, STATUS_GET_SBP_STATUS(h)); + scsi_status = + r == RESP_STATUS_TRANSPORT_FAILURE ? + SBP2_SCSI_STATUS_BUSY : + SBP2_SCSI_STATUS_COMMAND_TERMINATED; + } /* - * See if the target stored any scsi status information + * See if the target stored any scsi status information. */ - if (STATUS_GET_LENGTH(scsi_id->status_block.ORB_offset_hi_misc) > 1) { - /* - * Translate SBP-2 status to SCSI sense data - */ + if (STATUS_GET_LEN(h) > 1) { SBP2_DEBUG("CHECK CONDITION"); - scsi_status = sbp2_status_to_sense_data((unchar *)&scsi_id->status_block, SCpnt->sense_buffer); + scsi_status = sbp2_status_to_sense_data( + (unchar *)sb, SCpnt->sense_buffer); } - /* - * Check to see if the dead bit is set. If so, we'll have to initiate - * a fetch agent reset. + * Check to see if the dead bit is set. If so, we'll + * have to initiate a fetch agent reset. */ - if (STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc)) { - - /* - * Initiate a fetch agent reset. - */ - SBP2_DEBUG("Dead bit set - initiating fetch agent reset"); + if (STATUS_TEST_DEAD(h)) { + SBP2_DEBUG("Dead bit set - " + "initiating fetch agent reset"); sbp2_agent_reset(scsi_id, 0); } - SBP2_ORB_DEBUG("completing command orb %p", &command->command_orb); } /* - * Check here to see if there are no commands in-use. If there are none, we can - * null out last orb so that next time around we write directly to the orb pointer... - * Quick start saves one 1394 bus transaction. + * Check here to see if there are no commands in-use. If there + * are none, we know that the fetch agent left the active state + * _and_ that we did not reactivate it yet. Therefore clear + * last_orb so that next time we write directly to the + * ORB_POINTER register. That way the fetch agent does not need + * to refetch the next_ORB. */ spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); - if (list_empty(&scsi_id->sbp2_command_orb_inuse)) { + if (list_empty(&scsi_id->sbp2_command_orb_inuse)) scsi_id->last_orb = NULL; - } spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); } else { - /* * It's probably a login/logout/reconnect status. */ - if ((scsi_id->login_orb_dma == scsi_id->status_block.ORB_offset_lo) || - (scsi_id->query_logins_orb_dma == scsi_id->status_block.ORB_offset_lo) || - (scsi_id->reconnect_orb_dma == scsi_id->status_block.ORB_offset_lo) || - (scsi_id->logout_orb_dma == scsi_id->status_block.ORB_offset_lo)) { - atomic_set(&scsi_id->sbp2_login_complete, 1); + if ((sb->ORB_offset_lo == scsi_id->reconnect_orb_dma) || + (sb->ORB_offset_lo == scsi_id->login_orb_dma) || + (sb->ORB_offset_lo == scsi_id->query_logins_orb_dma) || + (sb->ORB_offset_lo == scsi_id->logout_orb_dma)) { + scsi_id->access_complete = 1; + wake_up_interruptible(&access_wq); } } if (SCpnt) { - - /* Complete the SCSI command. */ SBP2_DEBUG("Completing SCSI command"); sbp2scsi_complete_command(scsi_id, scsi_status, SCpnt, command->Current_done); @@ -2372,7 +2392,7 @@ static void sbp2scsi_complete_all_commands(struct scsi_id_instance_data *scsi_id command = list_entry(lh, struct sbp2_command_info, list); pci_dma_sync_single_for_cpu(hi->host->pdev, command->command_orb_dma, sizeof(struct sbp2_command_orb), - PCI_DMA_BIDIRECTIONAL); + PCI_DMA_TODEVICE); pci_dma_sync_single_for_cpu(hi->host->pdev, command->sge_dma, sizeof(command->scatter_gather_element), PCI_DMA_BIDIRECTIONAL); @@ -2495,6 +2515,7 @@ static int sbp2scsi_slave_alloc(struct scsi_device *sdev) (struct scsi_id_instance_data *)sdev->host->hostdata[0]; scsi_id->sdev = sdev; + sdev->allow_restart = 1; if (scsi_id->workarounds & SBP2_WORKAROUND_INQUIRY_36) sdev->inquiry_len = 36; @@ -2508,16 +2529,12 @@ static int sbp2scsi_slave_configure(struct scsi_device *sdev) blk_queue_dma_alignment(sdev->request_queue, (512 - 1)); sdev->use_10_for_rw = 1; - sdev->use_10_for_ms = 1; if (sdev->type == TYPE_DISK && scsi_id->workarounds & SBP2_WORKAROUND_MODE_SENSE_8) sdev->skip_ms_page_8 = 1; if (scsi_id->workarounds & SBP2_WORKAROUND_FIX_CAPACITY) sdev->fix_capacity = 1; - if (scsi_id->ne->guid_vendor_id == 0x0010b9 && /* Maxtor's OUI */ - (sdev->type == TYPE_DISK || sdev->type == TYPE_RBC)) - sdev->allow_restart = 1; return 0; } @@ -2555,7 +2572,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt) pci_dma_sync_single_for_cpu(hi->host->pdev, command->command_orb_dma, sizeof(struct sbp2_command_orb), - PCI_DMA_BIDIRECTIONAL); + PCI_DMA_TODEVICE); pci_dma_sync_single_for_cpu(hi->host->pdev, command->sge_dma, sizeof(command->scatter_gather_element), @@ -2571,7 +2588,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt) /* * Initiate a fetch agent reset. */ - sbp2_agent_reset(scsi_id, 0); + sbp2_agent_reset(scsi_id, 1); sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY); } @@ -2590,7 +2607,7 @@ static int sbp2scsi_reset(struct scsi_cmnd *SCpnt) if (sbp2util_node_is_available(scsi_id)) { SBP2_ERR("Generating sbp2 fetch agent reset"); - sbp2_agent_reset(scsi_id, 0); + sbp2_agent_reset(scsi_id, 1); } return SUCCESS; diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h index b22ce1aa8fe..abbe48e646c 100644 --- a/drivers/ieee1394/sbp2.h +++ b/drivers/ieee1394/sbp2.h @@ -46,8 +46,8 @@ #define ORB_SET_DIRECTION(value) ((value & 0x1) << 27) struct sbp2_command_orb { - volatile u32 next_ORB_hi; - volatile u32 next_ORB_lo; + u32 next_ORB_hi; + u32 next_ORB_lo; u32 data_descriptor_hi; u32 data_descriptor_lo; u32 misc; @@ -180,12 +180,14 @@ struct sbp2_unrestricted_page_table { #define SBP2_SCSI_STATUS_SELECTION_TIMEOUT 0xff -#define STATUS_GET_ORB_OFFSET_HI(value) (value & 0xffff) -#define STATUS_GET_SBP_STATUS(value) ((value >> 16) & 0xff) -#define STATUS_GET_LENGTH(value) ((value >> 24) & 0x7) -#define STATUS_GET_DEAD_BIT(value) ((value >> 27) & 0x1) -#define STATUS_GET_RESP(value) ((value >> 28) & 0x3) -#define STATUS_GET_SRC(value) ((value >> 30) & 0x3) +#define STATUS_GET_SRC(value) (((value) >> 30) & 0x3) +#define STATUS_GET_RESP(value) (((value) >> 28) & 0x3) +#define STATUS_GET_LEN(value) (((value) >> 24) & 0x7) +#define STATUS_GET_SBP_STATUS(value) (((value) >> 16) & 0xff) +#define STATUS_GET_ORB_OFFSET_HI(value) ((value) & 0x0000ffff) +#define STATUS_TEST_DEAD(value) ((value) & 0x08000000) +/* test 'resp' | 'dead' | 'sbp2_status' */ +#define STATUS_TEST_RDS(value) ((value) & 0x38ff0000) struct sbp2_status_block { u32 ORB_offset_hi_misc; @@ -318,9 +320,9 @@ struct scsi_id_instance_data { u64 status_fifo_addr; /* - * Variable used for logins, reconnects, logouts, query logins + * Waitqueue flag for logins, reconnects, logouts, query logins */ - atomic_t sbp2_login_complete; + int access_complete:1; /* * Pool of command orbs, so we can have more than overlapped command per id @@ -344,6 +346,16 @@ struct scsi_id_instance_data { /* Device specific workarounds/brokeness */ unsigned workarounds; + + atomic_t state; + struct work_struct protocol_work; +}; + +/* For use in scsi_id_instance_data.state */ +enum sbp2lu_state_types { + SBP2LU_STATE_RUNNING, /* all normal */ + SBP2LU_STATE_IN_RESET, /* between bus reset and reconnect */ + SBP2LU_STATE_IN_SHUTDOWN /* when sbp2_remove was called */ }; /* Sbp2 host data structure (one per IEEE1394 host) */ @@ -390,11 +402,6 @@ static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id); static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int destid, quadlet_t *data, u64 addr, size_t length, u16 flags); static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait); -static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id, - struct sbp2_command_info *command); -static int sbp2_send_command(struct scsi_id_instance_data *scsi_id, - struct scsi_cmnd *SCpnt, - void (*done)(struct scsi_cmnd *)); static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status, unchar *sense_data); static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id, diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c index c6e3f02bc6d..9bc65059cc6 100644 --- a/drivers/ieee1394/video1394.c +++ b/drivers/ieee1394/video1394.c @@ -49,16 +49,16 @@ #include <linux/compat.h> #include <linux/cdev.h> -#include "ieee1394.h" -#include "ieee1394_types.h" +#include "dma.h" +#include "highlevel.h" #include "hosts.h" +#include "ieee1394.h" #include "ieee1394_core.h" -#include "highlevel.h" -#include "video1394.h" +#include "ieee1394_hotplug.h" +#include "ieee1394_types.h" #include "nodemgr.h" -#include "dma.h" - #include "ohci1394.h" +#include "video1394.h" #define ISO_CHANNELS 64 @@ -129,7 +129,7 @@ struct file_ctx { #define DBGMSG(card, fmt, args...) \ printk(KERN_INFO "video1394_%d: " fmt "\n" , card , ## args) #else -#define DBGMSG(card, fmt, args...) +#define DBGMSG(card, fmt, args...) do {} while (0) #endif /* print general (card independent) information */ @@ -1181,7 +1181,8 @@ static int video1394_mmap(struct file *file, struct vm_area_struct *vma) lock_kernel(); if (ctx->current_ctx == NULL) { - PRINT(KERN_ERR, ctx->ohci->host->id, "Current iso context not set"); + PRINT(KERN_ERR, ctx->ohci->host->id, + "Current iso context not set"); } else res = dma_region_mmap(&ctx->current_ctx->dma, file, vma); unlock_kernel(); @@ -1189,6 +1190,40 @@ static int video1394_mmap(struct file *file, struct vm_area_struct *vma) return res; } +static unsigned int video1394_poll(struct file *file, poll_table *pt) +{ + struct file_ctx *ctx; + unsigned int mask = 0; + unsigned long flags; + struct dma_iso_ctx *d; + int i; + + lock_kernel(); + ctx = file->private_data; + d = ctx->current_ctx; + if (d == NULL) { + PRINT(KERN_ERR, ctx->ohci->host->id, + "Current iso context not set"); + mask = POLLERR; + goto done; + } + + poll_wait(file, &d->waitq, pt); + + spin_lock_irqsave(&d->lock, flags); + for (i = 0; i < d->num_desc; i++) { + if (d->buffer_status[i] == VIDEO1394_BUFFER_READY) { + mask |= POLLIN | POLLRDNORM; + break; + } + } + spin_unlock_irqrestore(&d->lock, flags); +done: + unlock_kernel(); + + return mask; +} + static int video1394_open(struct inode *inode, struct file *file) { int i = ieee1394_file_to_instance(file); @@ -1257,6 +1292,7 @@ static struct file_operations video1394_fops= #ifdef CONFIG_COMPAT .compat_ioctl = video1394_compat_ioctl, #endif + .poll = video1394_poll, .mmap = video1394_mmap, .open = video1394_open, .release = video1394_release diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 9cbf09e2052..60d3fbdd216 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -86,7 +86,7 @@ EXPORT_SYMBOL(rdma_copy_addr); int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr) { struct net_device *dev; - u32 ip = ((struct sockaddr_in *) addr)->sin_addr.s_addr; + __be32 ip = ((struct sockaddr_in *) addr)->sin_addr.s_addr; int ret; dev = ip_dev_find(ip); @@ -239,7 +239,7 @@ static int addr_resolve_local(struct sockaddr_in *src_in, { struct net_device *dev; u32 src_ip = src_in->sin_addr.s_addr; - u32 dst_ip = dst_in->sin_addr.s_addr; + __be32 dst_ip = dst_in->sin_addr.s_addr; int ret; dev = ip_dev_find(dst_ip); diff --git a/drivers/infiniband/hw/amso1100/c2_ae.c b/drivers/infiniband/hw/amso1100/c2_ae.c index 08f46c83a3a..3aae4978e1c 100644 --- a/drivers/infiniband/hw/amso1100/c2_ae.c +++ b/drivers/infiniband/hw/amso1100/c2_ae.c @@ -197,7 +197,7 @@ void c2_ae_event(struct c2_dev *c2dev, u32 mq_index) "resource=%x, qp_state=%s\n", __FUNCTION__, to_event_str(event_id), - be64_to_cpu(wr->ae.ae_generic.user_context), + (unsigned long long) be64_to_cpu(wr->ae.ae_generic.user_context), be32_to_cpu(wr->ae.ae_generic.resource_type), be32_to_cpu(wr->ae.ae_generic.resource), to_qp_state_str(be32_to_cpu(wr->ae.ae_generic.qp_state))); diff --git a/drivers/infiniband/hw/amso1100/c2_alloc.c b/drivers/infiniband/hw/amso1100/c2_alloc.c index 1d2529992c0..028a60bbfca 100644 --- a/drivers/infiniband/hw/amso1100/c2_alloc.c +++ b/drivers/infiniband/hw/amso1100/c2_alloc.c @@ -115,7 +115,7 @@ u16 *c2_alloc_mqsp(struct c2_dev *c2dev, struct sp_chunk *head, ((unsigned long) &(head->shared_ptr[mqsp]) - (unsigned long) head); pr_debug("%s addr %p dma_addr %llx\n", __FUNCTION__, - &(head->shared_ptr[mqsp]), (u64)*dma_addr); + &(head->shared_ptr[mqsp]), (unsigned long long) *dma_addr); return &(head->shared_ptr[mqsp]); } return NULL; diff --git a/drivers/infiniband/hw/amso1100/c2_cm.c b/drivers/infiniband/hw/amso1100/c2_cm.c index 485254efdd1..75b93e9b881 100644 --- a/drivers/infiniband/hw/amso1100/c2_cm.c +++ b/drivers/infiniband/hw/amso1100/c2_cm.c @@ -302,7 +302,7 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) vq_req = vq_req_alloc(c2dev); if (!vq_req) { err = -ENOMEM; - goto bail1; + goto bail0; } vq_req->qp = qp; vq_req->cm_id = cm_id; @@ -311,7 +311,7 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) wr = kmalloc(c2dev->req_vq.msg_size, GFP_KERNEL); if (!wr) { err = -ENOMEM; - goto bail2; + goto bail1; } /* Build the WR */ @@ -331,7 +331,7 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) /* Validate private_data length */ if (iw_param->private_data_len > C2_MAX_PRIVATE_DATA_SIZE) { err = -EINVAL; - goto bail2; + goto bail1; } if (iw_param->private_data) { @@ -348,19 +348,19 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) err = vq_send_wr(c2dev, (union c2wr *) wr); if (err) { vq_req_put(c2dev, vq_req); - goto bail2; + goto bail1; } /* Wait for reply from adapter */ err = vq_wait_for_reply(c2dev, vq_req); if (err) - goto bail2; + goto bail1; /* Check that reply is present */ reply = (struct c2wr_cr_accept_rep *) (unsigned long) vq_req->reply_msg; if (!reply) { err = -ENOMEM; - goto bail2; + goto bail1; } err = c2_errno(reply); @@ -368,9 +368,8 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) if (!err) c2_set_qp_state(qp, C2_QP_STATE_RTS); - bail2: - kfree(wr); bail1: + kfree(wr); vq_req_free(c2dev, vq_req); bail0: if (err) { diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c index dd6af551108..da98d9f7142 100644 --- a/drivers/infiniband/hw/amso1100/c2_provider.c +++ b/drivers/infiniband/hw/amso1100/c2_provider.c @@ -390,14 +390,18 @@ static struct ib_mr *c2_reg_phys_mr(struct ib_pd *ib_pd, } mr = kmalloc(sizeof(*mr), GFP_KERNEL); - if (!mr) + if (!mr) { + vfree(page_list); return ERR_PTR(-ENOMEM); + } mr->pd = to_c2pd(ib_pd); pr_debug("%s - page shift %d, pbl_depth %d, total_len %u, " "*iova_start %llx, first pa %llx, last pa %llx\n", __FUNCTION__, page_shift, pbl_depth, total_len, - *iova_start, page_list[0], page_list[pbl_depth-1]); + (unsigned long long) *iova_start, + (unsigned long long) page_list[0], + (unsigned long long) page_list[pbl_depth-1]); err = c2_nsmr_register_phys_kern(to_c2dev(ib_pd->device), page_list, (1 << page_shift), pbl_depth, total_len, 0, iova_start, diff --git a/drivers/infiniband/hw/amso1100/c2_rnic.c b/drivers/infiniband/hw/amso1100/c2_rnic.c index f49a32b7a8f..e37c5688c21 100644 --- a/drivers/infiniband/hw/amso1100/c2_rnic.c +++ b/drivers/infiniband/hw/amso1100/c2_rnic.c @@ -527,7 +527,7 @@ int c2_rnic_init(struct c2_dev *c2dev) DMA_FROM_DEVICE); pci_unmap_addr_set(&c2dev->rep_vq, mapping, c2dev->rep_vq.host_dma); pr_debug("%s rep_vq va %p dma %llx\n", __FUNCTION__, q1_pages, - (u64)c2dev->rep_vq.host_dma); + (unsigned long long) c2dev->rep_vq.host_dma); c2_mq_rep_init(&c2dev->rep_vq, 1, qsize, @@ -550,7 +550,7 @@ int c2_rnic_init(struct c2_dev *c2dev) DMA_FROM_DEVICE); pci_unmap_addr_set(&c2dev->aeq, mapping, c2dev->aeq.host_dma); pr_debug("%s aeq va %p dma %llx\n", __FUNCTION__, q1_pages, - (u64)c2dev->rep_vq.host_dma); + (unsigned long long) c2dev->rep_vq.host_dma); c2_mq_rep_init(&c2dev->aeq, 2, qsize, diff --git a/drivers/infiniband/hw/ipath/ipath_common.h b/drivers/infiniband/hw/ipath/ipath_common.h index f577905e3ac..54139d39818 100644 --- a/drivers/infiniband/hw/ipath/ipath_common.h +++ b/drivers/infiniband/hw/ipath/ipath_common.h @@ -141,8 +141,9 @@ struct infinipath_stats { * packets if ipath not configured, etc.) */ __u64 sps_krdrops; + __u64 sps_txeparity; /* PIO buffer parity error, recovered */ /* pad for future growth */ - __u64 __sps_pad[46]; + __u64 __sps_pad[45]; }; /* @@ -185,6 +186,9 @@ typedef enum _ipath_ureg { #define IPATH_RUNTIME_PCIE 0x2 #define IPATH_RUNTIME_FORCE_WC_ORDER 0x4 #define IPATH_RUNTIME_RCVHDR_COPY 0x8 +#define IPATH_RUNTIME_MASTER 0x10 +#define IPATH_RUNTIME_PBC_REWRITE 0x20 +#define IPATH_RUNTIME_LOOSE_DMA_ALIGN 0x40 /* * This structure is returned by ipath_userinit() immediately after @@ -202,7 +206,8 @@ struct ipath_base_info { /* version of software, for feature checking. */ __u32 spi_sw_version; /* InfiniPath port assigned, goes into sent packets */ - __u32 spi_port; + __u16 spi_port; + __u16 spi_subport; /* * IB MTU, packets IB data must be less than this. * The MTU is in bytes, and will be a multiple of 4 bytes. @@ -218,7 +223,7 @@ struct ipath_base_info { __u32 spi_tidcnt; /* size of the TID Eager list in infinipath, in entries */ __u32 spi_tidegrcnt; - /* size of a single receive header queue entry. */ + /* size of a single receive header queue entry in words. */ __u32 spi_rcvhdrent_size; /* * Count of receive header queue entries allocated. @@ -310,6 +315,12 @@ struct ipath_base_info { __u32 spi_filler_for_align; /* address of readonly memory copy of the rcvhdrq tail register. */ __u64 spi_rcvhdr_tailaddr; + + /* shared memory pages for subports if IPATH_RUNTIME_MASTER is set */ + __u64 spi_subport_uregbase; + __u64 spi_subport_rcvegrbuf; + __u64 spi_subport_rcvhdr_base; + } __attribute__ ((aligned(8))); @@ -328,12 +339,12 @@ struct ipath_base_info { /* * Minor version differences are always compatible - * a within a major version, however if if user software is larger + * a within a major version, however if user software is larger * than driver software, some new features and/or structure fields * may not be implemented; the user code must deal with this if it - * cares, or it must abort after initialization reports the difference + * cares, or it must abort after initialization reports the difference. */ -#define IPATH_USER_SWMINOR 2 +#define IPATH_USER_SWMINOR 3 #define IPATH_USER_SWVERSION ((IPATH_USER_SWMAJOR<<16) | IPATH_USER_SWMINOR) @@ -379,7 +390,16 @@ struct ipath_user_info { */ __u32 spu_rcvhdrsize; - __u64 spu_unused; /* kept for compatible layout */ + /* + * If two or more processes wish to share a port, each process + * must set the spu_subport_cnt and spu_subport_id to the same + * values. The only restriction on the spu_subport_id is that + * it be unique for a given node. + */ + __u16 spu_subport_cnt; + __u16 spu_subport_id; + + __u32 spu_unused; /* kept for compatible layout */ /* * address of struct base_info to write to @@ -392,19 +412,25 @@ struct ipath_user_info { #define IPATH_CMD_MIN 16 -#define IPATH_CMD_USER_INIT 16 /* set up userspace */ +#define __IPATH_CMD_USER_INIT 16 /* old set up userspace (for old user code) */ #define IPATH_CMD_PORT_INFO 17 /* find out what resources we got */ #define IPATH_CMD_RECV_CTRL 18 /* control receipt of packets */ #define IPATH_CMD_TID_UPDATE 19 /* update expected TID entries */ #define IPATH_CMD_TID_FREE 20 /* free expected TID entries */ #define IPATH_CMD_SET_PART_KEY 21 /* add partition key */ +#define IPATH_CMD_SLAVE_INFO 22 /* return info on slave processes */ +#define IPATH_CMD_ASSIGN_PORT 23 /* allocate HCA and port */ +#define IPATH_CMD_USER_INIT 24 /* set up userspace */ -#define IPATH_CMD_MAX 21 +#define IPATH_CMD_MAX 24 struct ipath_port_info { __u32 num_active; /* number of active units */ __u32 unit; /* unit (chip) assigned to caller */ - __u32 port; /* port on unit assigned to caller */ + __u16 port; /* port on unit assigned to caller */ + __u16 subport; /* subport on unit assigned to caller */ + __u16 num_ports; /* number of ports available on unit */ + __u16 num_subports; /* number of subport slaves opened on port */ }; struct ipath_tid_info { @@ -435,6 +461,8 @@ struct ipath_cmd { __u32 recv_ctrl; /* partition key to set */ __u16 part_key; + /* user address of __u32 bitmask of active slaves */ + __u64 slave_mask_addr; } cmd; }; @@ -596,6 +624,10 @@ struct infinipath_counters { /* K_PktFlags bits */ #define INFINIPATH_KPF_INTR 0x1 +#define INFINIPATH_KPF_SUBPORT_MASK 0x3 +#define INFINIPATH_KPF_SUBPORT_SHIFT 1 + +#define INFINIPATH_MAX_SUBPORT 4 /* SendPIO per-buffer control */ #define INFINIPATH_SP_TEST 0x40 @@ -610,7 +642,7 @@ struct ipath_header { /* * Version - 4 bits, Port - 4 bits, TID - 10 bits and Offset - * 14 bits before ECO change ~28 Dec 03. After that, Vers 4, - * Port 3, TID 11, offset 14. + * Port 4, TID 11, offset 13. */ __le32 ver_port_tid_offset; __le16 chksum; diff --git a/drivers/infiniband/hw/ipath/ipath_cq.c b/drivers/infiniband/hw/ipath/ipath_cq.c index 049221bc590..87462e0cb4d 100644 --- a/drivers/infiniband/hw/ipath/ipath_cq.c +++ b/drivers/infiniband/hw/ipath/ipath_cq.c @@ -46,7 +46,7 @@ */ void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited) { - struct ipath_cq_wc *wc = cq->queue; + struct ipath_cq_wc *wc; unsigned long flags; u32 head; u32 next; @@ -57,6 +57,7 @@ void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited) * Note that the head pointer might be writable by user processes. * Take care to verify it is a sane value. */ + wc = cq->queue; head = wc->head; if (head >= (unsigned) cq->ibcq.cqe) { head = cq->ibcq.cqe; @@ -109,21 +110,27 @@ void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited) int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) { struct ipath_cq *cq = to_icq(ibcq); - struct ipath_cq_wc *wc = cq->queue; + struct ipath_cq_wc *wc; unsigned long flags; int npolled; + u32 tail; spin_lock_irqsave(&cq->lock, flags); + wc = cq->queue; + tail = wc->tail; + if (tail > (u32) cq->ibcq.cqe) + tail = (u32) cq->ibcq.cqe; for (npolled = 0; npolled < num_entries; ++npolled, ++entry) { - if (wc->tail == wc->head) + if (tail == wc->head) break; - *entry = wc->queue[wc->tail]; - if (wc->tail >= cq->ibcq.cqe) - wc->tail = 0; + *entry = wc->queue[tail]; + if (tail >= cq->ibcq.cqe) + tail = 0; else - wc->tail++; + tail++; } + wc->tail = tail; spin_unlock_irqrestore(&cq->lock, flags); @@ -177,11 +184,6 @@ struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries, goto done; } - if (dev->n_cqs_allocated == ib_ipath_max_cqs) { - ret = ERR_PTR(-ENOMEM); - goto done; - } - /* Allocate the completion queue structure. */ cq = kmalloc(sizeof(*cq), GFP_KERNEL); if (!cq) { @@ -237,6 +239,16 @@ struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries, } else cq->ip = NULL; + spin_lock(&dev->n_cqs_lock); + if (dev->n_cqs_allocated == ib_ipath_max_cqs) { + spin_unlock(&dev->n_cqs_lock); + ret = ERR_PTR(-ENOMEM); + goto bail_wc; + } + + dev->n_cqs_allocated++; + spin_unlock(&dev->n_cqs_lock); + /* * ib_create_cq() will initialize cq->ibcq except for cq->ibcq.cqe. * The number of entries should be >= the number requested or return @@ -253,7 +265,6 @@ struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries, ret = &cq->ibcq; - dev->n_cqs_allocated++; goto done; bail_wc: @@ -280,7 +291,9 @@ int ipath_destroy_cq(struct ib_cq *ibcq) struct ipath_cq *cq = to_icq(ibcq); tasklet_kill(&cq->comptask); + spin_lock(&dev->n_cqs_lock); dev->n_cqs_allocated--; + spin_unlock(&dev->n_cqs_lock); if (cq->ip) kref_put(&cq->ip->ref, ipath_release_mmap_info); else @@ -316,10 +329,16 @@ int ipath_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify notify) return 0; } +/** + * ipath_resize_cq - change the size of the CQ + * @ibcq: the completion queue + * + * Returns 0 for success. + */ int ipath_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) { struct ipath_cq *cq = to_icq(ibcq); - struct ipath_cq_wc *old_wc = cq->queue; + struct ipath_cq_wc *old_wc; struct ipath_cq_wc *wc; u32 head, tail, n; int ret; @@ -355,6 +374,7 @@ int ipath_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) * Make sure head and tail are sane since they * might be user writable. */ + old_wc = cq->queue; head = old_wc->head; if (head > (u32) cq->ibcq.cqe) head = (u32) cq->ibcq.cqe; diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c index 2108466c7e3..12cefa658f3 100644 --- a/drivers/infiniband/hw/ipath/ipath_driver.c +++ b/drivers/infiniband/hw/ipath/ipath_driver.c @@ -95,16 +95,6 @@ const char *ipath_ibcstatus_str[] = { "RecovIdle", }; -/* - * These variables are initialized in the chip-specific files - * but are defined here. - */ -u16 ipath_gpio_sda_num, ipath_gpio_scl_num; -u64 ipath_gpio_sda, ipath_gpio_scl; -u64 infinipath_i_bitsextant; -ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant; -u32 infinipath_i_rcvavail_mask, infinipath_i_rcvurg_mask; - static void __devexit ipath_remove_one(struct pci_dev *); static int __devinit ipath_init_one(struct pci_dev *, const struct pci_device_id *); @@ -527,28 +517,146 @@ bail: return ret; } +static void __devexit cleanup_device(struct ipath_devdata *dd) +{ + int port; + + ipath_shutdown_device(dd); + + if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) { + /* can't do anything more with chip; needs re-init */ + *dd->ipath_statusp &= ~IPATH_STATUS_CHIP_PRESENT; + if (dd->ipath_kregbase) { + /* + * if we haven't already cleaned up before these are + * to ensure any register reads/writes "fail" until + * re-init + */ + dd->ipath_kregbase = NULL; + dd->ipath_uregbase = 0; + dd->ipath_sregbase = 0; + dd->ipath_cregbase = 0; + dd->ipath_kregsize = 0; + } + ipath_disable_wc(dd); + } + + if (dd->ipath_pioavailregs_dma) { + dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE, + (void *) dd->ipath_pioavailregs_dma, + dd->ipath_pioavailregs_phys); + dd->ipath_pioavailregs_dma = NULL; + } + if (dd->ipath_dummy_hdrq) { + dma_free_coherent(&dd->pcidev->dev, + dd->ipath_pd[0]->port_rcvhdrq_size, + dd->ipath_dummy_hdrq, dd->ipath_dummy_hdrq_phys); + dd->ipath_dummy_hdrq = NULL; + } + + if (dd->ipath_pageshadow) { + struct page **tmpp = dd->ipath_pageshadow; + dma_addr_t *tmpd = dd->ipath_physshadow; + int i, cnt = 0; + + ipath_cdbg(VERBOSE, "Unlocking any expTID pages still " + "locked\n"); + for (port = 0; port < dd->ipath_cfgports; port++) { + int port_tidbase = port * dd->ipath_rcvtidcnt; + int maxtid = port_tidbase + dd->ipath_rcvtidcnt; + for (i = port_tidbase; i < maxtid; i++) { + if (!tmpp[i]) + continue; + pci_unmap_page(dd->pcidev, tmpd[i], + PAGE_SIZE, PCI_DMA_FROMDEVICE); + ipath_release_user_pages(&tmpp[i], 1); + tmpp[i] = NULL; + cnt++; + } + } + if (cnt) { + ipath_stats.sps_pageunlocks += cnt; + ipath_cdbg(VERBOSE, "There were still %u expTID " + "entries locked\n", cnt); + } + if (ipath_stats.sps_pagelocks || + ipath_stats.sps_pageunlocks) + ipath_cdbg(VERBOSE, "%llu pages locked, %llu " + "unlocked via ipath_m{un}lock\n", + (unsigned long long) + ipath_stats.sps_pagelocks, + (unsigned long long) + ipath_stats.sps_pageunlocks); + + ipath_cdbg(VERBOSE, "Free shadow page tid array at %p\n", + dd->ipath_pageshadow); + vfree(dd->ipath_pageshadow); + dd->ipath_pageshadow = NULL; + } + + /* + * free any resources still in use (usually just kernel ports) + * at unload; we do for portcnt, not cfgports, because cfgports + * could have changed while we were loaded. + */ + for (port = 0; port < dd->ipath_portcnt; port++) { + struct ipath_portdata *pd = dd->ipath_pd[port]; + dd->ipath_pd[port] = NULL; + ipath_free_pddata(dd, pd); + } + kfree(dd->ipath_pd); + /* + * debuggability, in case some cleanup path tries to use it + * after this + */ + dd->ipath_pd = NULL; +} + static void __devexit ipath_remove_one(struct pci_dev *pdev) { - struct ipath_devdata *dd; + struct ipath_devdata *dd = pci_get_drvdata(pdev); - ipath_cdbg(VERBOSE, "removing, pdev=%p\n", pdev); - if (!pdev) - return; + ipath_cdbg(VERBOSE, "removing, pdev=%p, dd=%p\n", pdev, dd); + + if (dd->verbs_dev) + ipath_unregister_ib_device(dd->verbs_dev); - dd = pci_get_drvdata(pdev); - ipath_unregister_ib_device(dd->verbs_dev); ipath_diag_remove(dd); ipath_user_remove(dd); ipathfs_remove_device(dd); ipath_device_remove_group(&pdev->dev, dd); + ipath_cdbg(VERBOSE, "Releasing pci memory regions, dd %p, " "unit %u\n", dd, (u32) dd->ipath_unit); - if (dd->ipath_kregbase) { - ipath_cdbg(VERBOSE, "Unmapping kregbase %p\n", - dd->ipath_kregbase); - iounmap((volatile void __iomem *) dd->ipath_kregbase); - dd->ipath_kregbase = NULL; - } + + cleanup_device(dd); + + /* + * turn off rcv, send, and interrupts for all ports, all drivers + * should also hard reset the chip here? + * free up port 0 (kernel) rcvhdr, egr bufs, and eventually tid bufs + * for all versions of the driver, if they were allocated + */ + if (pdev->irq) { + ipath_cdbg(VERBOSE, + "unit %u free_irq of irq %x\n", + dd->ipath_unit, pdev->irq); + free_irq(pdev->irq, dd); + } else + ipath_dbg("irq is 0, not doing free_irq " + "for unit %u\n", dd->ipath_unit); + /* + * we check for NULL here, because it's outside + * the kregbase check, and we need to call it + * after the free_irq. Thus it's possible that + * the function pointers were never initialized. + */ + if (dd->ipath_f_cleanup) + /* clean up chip-specific stuff */ + dd->ipath_f_cleanup(dd); + + ipath_cdbg(VERBOSE, "Unmapping kregbase %p\n", dd->ipath_kregbase); + iounmap((volatile void __iomem *) dd->ipath_kregbase); pci_release_regions(pdev); ipath_cdbg(VERBOSE, "calling pci_disable_device\n"); pci_disable_device(pdev); @@ -760,8 +868,8 @@ static void get_rhf_errstring(u32 err, char *msg, size_t len) static inline void *ipath_get_egrbuf(struct ipath_devdata *dd, u32 bufnum, int err) { - return dd->ipath_port0_skbs ? - (void *)dd->ipath_port0_skbs[bufnum]->data : NULL; + return dd->ipath_port0_skbinfo ? + (void *) dd->ipath_port0_skbinfo[bufnum].skb->data : NULL; } /** @@ -783,31 +891,34 @@ struct sk_buff *ipath_alloc_skb(struct ipath_devdata *dd, */ /* - * We need 4 extra bytes for unaligned transfer copying + * We need 2 extra bytes for ipath_ether data sent in the + * key header. In order to keep everything dword aligned, + * we'll reserve 4 bytes. */ + len = dd->ipath_ibmaxlen + 4; + if (dd->ipath_flags & IPATH_4BYTE_TID) { - /* we need a 4KB multiple alignment, and there is no way + /* We need a 2KB multiple alignment, and there is no way * to do it except to allocate extra and then skb_reserve * enough to bring it up to the right alignment. */ - len = dd->ipath_ibmaxlen + 4 + (1 << 11) - 1; + len += 2047; } - else - len = dd->ipath_ibmaxlen + 4; + skb = __dev_alloc_skb(len, gfp_mask); if (!skb) { ipath_dev_err(dd, "Failed to allocate skbuff, length %u\n", len); goto bail; } + + skb_reserve(skb, 4); + if (dd->ipath_flags & IPATH_4BYTE_TID) { - u32 una = ((1 << 11) - 1) & (unsigned long)(skb->data + 4); + u32 una = (unsigned long)skb->data & 2047; if (una) - skb_reserve(skb, 4 + (1 << 11) - una); - else - skb_reserve(skb, 4); - } else - skb_reserve(skb, 4); + skb_reserve(skb, 2048 - una); + } bail: return skb; @@ -1326,6 +1437,9 @@ int ipath_create_rcvhdrq(struct ipath_devdata *dd, "for port %u rcvhdrqtailaddr failed\n", pd->port_port); ret = -ENOMEM; + dma_free_coherent(&dd->pcidev->dev, amt, + pd->port_rcvhdrq, pd->port_rcvhdrq_phys); + pd->port_rcvhdrq = NULL; goto bail; } pd->port_rcvhdrqtailaddr_phys = phys_hdrqtail; @@ -1347,12 +1461,13 @@ int ipath_create_rcvhdrq(struct ipath_devdata *dd, ipath_cdbg(VERBOSE, "reuse port %d rcvhdrq @%p %llx phys; " "hdrtailaddr@%p %llx physical\n", pd->port_port, pd->port_rcvhdrq, - pd->port_rcvhdrq_phys, pd->port_rcvhdrtail_kvaddr, - (unsigned long long)pd->port_rcvhdrqtailaddr_phys); + (unsigned long long) pd->port_rcvhdrq_phys, + pd->port_rcvhdrtail_kvaddr, (unsigned long long) + pd->port_rcvhdrqtailaddr_phys); /* clear for security and sanity on each use */ memset(pd->port_rcvhdrq, 0, pd->port_rcvhdrq_size); - memset((void *)pd->port_rcvhdrtail_kvaddr, 0, PAGE_SIZE); + memset(pd->port_rcvhdrtail_kvaddr, 0, PAGE_SIZE); /* * tell chip each time we init it, even if we are re-using previous @@ -1805,7 +1920,7 @@ void ipath_free_pddata(struct ipath_devdata *dd, struct ipath_portdata *pd) pd->port_rcvhdrq = NULL; if (pd->port_rcvhdrtail_kvaddr) { dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE, - (void *)pd->port_rcvhdrtail_kvaddr, + pd->port_rcvhdrtail_kvaddr, pd->port_rcvhdrqtailaddr_phys); pd->port_rcvhdrtail_kvaddr = NULL; } @@ -1824,24 +1939,32 @@ void ipath_free_pddata(struct ipath_devdata *dd, struct ipath_portdata *pd) dma_free_coherent(&dd->pcidev->dev, size, base, pd->port_rcvegrbuf_phys[e]); } - vfree(pd->port_rcvegrbuf); + kfree(pd->port_rcvegrbuf); pd->port_rcvegrbuf = NULL; - vfree(pd->port_rcvegrbuf_phys); + kfree(pd->port_rcvegrbuf_phys); pd->port_rcvegrbuf_phys = NULL; pd->port_rcvegrbuf_chunks = 0; - } else if (pd->port_port == 0 && dd->ipath_port0_skbs) { + } else if (pd->port_port == 0 && dd->ipath_port0_skbinfo) { unsigned e; - struct sk_buff **skbs = dd->ipath_port0_skbs; + struct ipath_skbinfo *skbinfo = dd->ipath_port0_skbinfo; - dd->ipath_port0_skbs = NULL; - ipath_cdbg(VERBOSE, "free closed port %d ipath_port0_skbs " - "@ %p\n", pd->port_port, skbs); + dd->ipath_port0_skbinfo = NULL; + ipath_cdbg(VERBOSE, "free closed port %d " + "ipath_port0_skbinfo @ %p\n", pd->port_port, + skbinfo); for (e = 0; e < dd->ipath_rcvegrcnt; e++) - if (skbs[e]) - dev_kfree_skb(skbs[e]); - vfree(skbs); + if (skbinfo[e].skb) { + pci_unmap_single(dd->pcidev, skbinfo[e].phys, + dd->ipath_ibmaxlen, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(skbinfo[e].skb); + } + vfree(skbinfo); } kfree(pd->port_tid_pg_list); + vfree(pd->subport_uregbase); + vfree(pd->subport_rcvegrbuf); + vfree(pd->subport_rcvhdr_base); kfree(pd); } @@ -1907,150 +2030,12 @@ bail: return ret; } -static void cleanup_device(struct ipath_devdata *dd) -{ - int port; - - ipath_shutdown_device(dd); - - if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) { - /* can't do anything more with chip; needs re-init */ - *dd->ipath_statusp &= ~IPATH_STATUS_CHIP_PRESENT; - if (dd->ipath_kregbase) { - /* - * if we haven't already cleaned up before these are - * to ensure any register reads/writes "fail" until - * re-init - */ - dd->ipath_kregbase = NULL; - dd->ipath_uregbase = 0; - dd->ipath_sregbase = 0; - dd->ipath_cregbase = 0; - dd->ipath_kregsize = 0; - } - ipath_disable_wc(dd); - } - - if (dd->ipath_pioavailregs_dma) { - dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE, - (void *) dd->ipath_pioavailregs_dma, - dd->ipath_pioavailregs_phys); - dd->ipath_pioavailregs_dma = NULL; - } - if (dd->ipath_dummy_hdrq) { - dma_free_coherent(&dd->pcidev->dev, - dd->ipath_pd[0]->port_rcvhdrq_size, - dd->ipath_dummy_hdrq, dd->ipath_dummy_hdrq_phys); - dd->ipath_dummy_hdrq = NULL; - } - - if (dd->ipath_pageshadow) { - struct page **tmpp = dd->ipath_pageshadow; - int i, cnt = 0; - - ipath_cdbg(VERBOSE, "Unlocking any expTID pages still " - "locked\n"); - for (port = 0; port < dd->ipath_cfgports; port++) { - int port_tidbase = port * dd->ipath_rcvtidcnt; - int maxtid = port_tidbase + dd->ipath_rcvtidcnt; - for (i = port_tidbase; i < maxtid; i++) { - if (!tmpp[i]) - continue; - ipath_release_user_pages(&tmpp[i], 1); - tmpp[i] = NULL; - cnt++; - } - } - if (cnt) { - ipath_stats.sps_pageunlocks += cnt; - ipath_cdbg(VERBOSE, "There were still %u expTID " - "entries locked\n", cnt); - } - if (ipath_stats.sps_pagelocks || - ipath_stats.sps_pageunlocks) - ipath_cdbg(VERBOSE, "%llu pages locked, %llu " - "unlocked via ipath_m{un}lock\n", - (unsigned long long) - ipath_stats.sps_pagelocks, - (unsigned long long) - ipath_stats.sps_pageunlocks); - - ipath_cdbg(VERBOSE, "Free shadow page tid array at %p\n", - dd->ipath_pageshadow); - vfree(dd->ipath_pageshadow); - dd->ipath_pageshadow = NULL; - } - - /* - * free any resources still in use (usually just kernel ports) - * at unload; we do for portcnt, not cfgports, because cfgports - * could have changed while we were loaded. - */ - for (port = 0; port < dd->ipath_portcnt; port++) { - struct ipath_portdata *pd = dd->ipath_pd[port]; - dd->ipath_pd[port] = NULL; - ipath_free_pddata(dd, pd); - } - kfree(dd->ipath_pd); - /* - * debuggability, in case some cleanup path tries to use it - * after this - */ - dd->ipath_pd = NULL; -} - static void __exit infinipath_cleanup(void) { - struct ipath_devdata *dd, *tmp; - unsigned long flags; - - ipath_diagpkt_remove(); - ipath_exit_ipathfs(); ipath_driver_remove_group(&ipath_driver.driver); - spin_lock_irqsave(&ipath_devs_lock, flags); - - /* - * turn off rcv, send, and interrupts for all ports, all drivers - * should also hard reset the chip here? - * free up port 0 (kernel) rcvhdr, egr bufs, and eventually tid bufs - * for all versions of the driver, if they were allocated - */ - list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) { - spin_unlock_irqrestore(&ipath_devs_lock, flags); - - if (dd->ipath_kregbase) - cleanup_device(dd); - - if (dd->pcidev) { - if (dd->pcidev->irq) { - ipath_cdbg(VERBOSE, - "unit %u free_irq of irq %x\n", - dd->ipath_unit, dd->pcidev->irq); - free_irq(dd->pcidev->irq, dd); - } else - ipath_dbg("irq is 0, not doing free_irq " - "for unit %u\n", dd->ipath_unit); - - /* - * we check for NULL here, because it's outside - * the kregbase check, and we need to call it - * after the free_irq. Thus it's possible that - * the function pointers were never initialized. - */ - if (dd->ipath_f_cleanup) - /* clean up chip-specific stuff */ - dd->ipath_f_cleanup(dd); - - dd->pcidev = NULL; - } - spin_lock_irqsave(&ipath_devs_lock, flags); - } - - spin_unlock_irqrestore(&ipath_devs_lock, flags); - ipath_cdbg(VERBOSE, "Unregistering pci driver\n"); pci_unregister_driver(&ipath_driver); diff --git a/drivers/infiniband/hw/ipath/ipath_eeprom.c b/drivers/infiniband/hw/ipath/ipath_eeprom.c index 3313356ab93..a4019a6b756 100644 --- a/drivers/infiniband/hw/ipath/ipath_eeprom.c +++ b/drivers/infiniband/hw/ipath/ipath_eeprom.c @@ -100,9 +100,9 @@ static int i2c_gpio_set(struct ipath_devdata *dd, gpioval = &dd->ipath_gpio_out; read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extctrl); if (line == i2c_line_scl) - mask = ipath_gpio_scl; + mask = dd->ipath_gpio_scl; else - mask = ipath_gpio_sda; + mask = dd->ipath_gpio_sda; if (new_line_state == i2c_line_high) /* tri-state the output rather than force high */ @@ -119,12 +119,12 @@ static int i2c_gpio_set(struct ipath_devdata *dd, write_val = 0x0UL; if (line == i2c_line_scl) { - write_val <<= ipath_gpio_scl_num; - *gpioval = *gpioval & ~(1UL << ipath_gpio_scl_num); + write_val <<= dd->ipath_gpio_scl_num; + *gpioval = *gpioval & ~(1UL << dd->ipath_gpio_scl_num); *gpioval |= write_val; } else { - write_val <<= ipath_gpio_sda_num; - *gpioval = *gpioval & ~(1UL << ipath_gpio_sda_num); + write_val <<= dd->ipath_gpio_sda_num; + *gpioval = *gpioval & ~(1UL << dd->ipath_gpio_sda_num); *gpioval |= write_val; } ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_out, *gpioval); @@ -157,9 +157,9 @@ static int i2c_gpio_get(struct ipath_devdata *dd, read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extctrl); /* config line to be an input */ if (line == i2c_line_scl) - mask = ipath_gpio_scl; + mask = dd->ipath_gpio_scl; else - mask = ipath_gpio_sda; + mask = dd->ipath_gpio_sda; write_val = read_val & ~mask; ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, write_val); read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extstatus); @@ -187,6 +187,7 @@ bail: static void i2c_wait_for_writes(struct ipath_devdata *dd) { (void)ipath_read_kreg32(dd, dd->ipath_kregs->kr_scratch); + rmb(); } static void scl_out(struct ipath_devdata *dd, u8 bit) diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c index 29930e22318..a9ddc6911f6 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c @@ -41,6 +41,12 @@ #include "ipath_kernel.h" #include "ipath_common.h" +/* + * mmap64 doesn't allow all 64 bits for 32-bit applications + * so only use the low 43 bits. + */ +#define MMAP64_MASK 0x7FFFFFFFFFFUL + static int ipath_open(struct inode *, struct file *); static int ipath_close(struct inode *, struct file *); static ssize_t ipath_write(struct file *, const char __user *, size_t, @@ -57,18 +63,35 @@ static struct file_operations ipath_file_ops = { .mmap = ipath_mmap }; -static int ipath_get_base_info(struct ipath_portdata *pd, +static int ipath_get_base_info(struct file *fp, void __user *ubase, size_t ubase_size) { + struct ipath_portdata *pd = port_fp(fp); int ret = 0; struct ipath_base_info *kinfo = NULL; struct ipath_devdata *dd = pd->port_dd; + unsigned subport_cnt; + int shared, master; + size_t sz; + + subport_cnt = pd->port_subport_cnt; + if (!subport_cnt) { + shared = 0; + master = 0; + subport_cnt = 1; + } else { + shared = 1; + master = !subport_fp(fp); + } - if (ubase_size < sizeof(*kinfo)) { + sz = sizeof(*kinfo); + /* If port sharing is not requested, allow the old size structure */ + if (!shared) + sz -= 3 * sizeof(u64); + if (ubase_size < sz) { ipath_cdbg(PROC, - "Base size %lu, need %lu (version mismatch?)\n", - (unsigned long) ubase_size, - (unsigned long) sizeof(*kinfo)); + "Base size %zu, need %zu (version mismatch?)\n", + ubase_size, sz); ret = -EINVAL; goto bail; } @@ -95,7 +118,9 @@ static int ipath_get_base_info(struct ipath_portdata *pd, kinfo->spi_rcv_egrperchunk = pd->port_rcvegrbufs_perchunk; kinfo->spi_rcv_egrchunksize = kinfo->spi_rcv_egrbuftotlen / pd->port_rcvegrbuf_chunks; - kinfo->spi_tidcnt = dd->ipath_rcvtidcnt; + kinfo->spi_tidcnt = dd->ipath_rcvtidcnt / subport_cnt; + if (master) + kinfo->spi_tidcnt += dd->ipath_rcvtidcnt % subport_cnt; /* * for this use, may be ipath_cfgports summed over all chips that * are are configured and present @@ -118,31 +143,75 @@ static int ipath_get_base_info(struct ipath_portdata *pd, * page_address() macro worked, but in 2.6.11, even that returns the * full 64 bit address (upper bits all 1's). So far, using the * physical addresses (or chip offsets, for chip mapping) works, but - * no doubt some future kernel release will chang that, and we'll be - * on to yet another method of dealing with this + * no doubt some future kernel release will change that, and we'll be + * on to yet another method of dealing with this. */ kinfo->spi_rcvhdr_base = (u64) pd->port_rcvhdrq_phys; - kinfo->spi_rcvhdr_tailaddr = (u64)pd->port_rcvhdrqtailaddr_phys; + kinfo->spi_rcvhdr_tailaddr = (u64) pd->port_rcvhdrqtailaddr_phys; kinfo->spi_rcv_egrbufs = (u64) pd->port_rcvegr_phys; kinfo->spi_pioavailaddr = (u64) dd->ipath_pioavailregs_phys; kinfo->spi_status = (u64) kinfo->spi_pioavailaddr + (void *) dd->ipath_statusp - (void *) dd->ipath_pioavailregs_dma; - kinfo->spi_piobufbase = (u64) pd->port_piobufs; - kinfo->__spi_uregbase = - dd->ipath_uregbase + dd->ipath_palign * pd->port_port; + if (!shared) { + kinfo->spi_piocnt = dd->ipath_pbufsport; + kinfo->spi_piobufbase = (u64) pd->port_piobufs; + kinfo->__spi_uregbase = (u64) dd->ipath_uregbase + + dd->ipath_palign * pd->port_port; + } else if (master) { + kinfo->spi_piocnt = (dd->ipath_pbufsport / subport_cnt) + + (dd->ipath_pbufsport % subport_cnt); + /* Master's PIO buffers are after all the slave's */ + kinfo->spi_piobufbase = (u64) pd->port_piobufs + + dd->ipath_palign * + (dd->ipath_pbufsport - kinfo->spi_piocnt); + kinfo->__spi_uregbase = (u64) dd->ipath_uregbase + + dd->ipath_palign * pd->port_port; + } else { + unsigned slave = subport_fp(fp) - 1; + + kinfo->spi_piocnt = dd->ipath_pbufsport / subport_cnt; + kinfo->spi_piobufbase = (u64) pd->port_piobufs + + dd->ipath_palign * kinfo->spi_piocnt * slave; + kinfo->__spi_uregbase = ((u64) pd->subport_uregbase + + PAGE_SIZE * slave) & MMAP64_MASK; - kinfo->spi_pioindex = dd->ipath_pbufsport * (pd->port_port - 1); - kinfo->spi_piocnt = dd->ipath_pbufsport; + kinfo->spi_rcvhdr_base = ((u64) pd->subport_rcvhdr_base + + pd->port_rcvhdrq_size * slave) & MMAP64_MASK; + kinfo->spi_rcvhdr_tailaddr = + (u64) pd->port_rcvhdrqtailaddr_phys & MMAP64_MASK; + kinfo->spi_rcv_egrbufs = ((u64) pd->subport_rcvegrbuf + + dd->ipath_rcvegrcnt * dd->ipath_rcvegrbufsize * slave) & + MMAP64_MASK; + } + + kinfo->spi_pioindex = (kinfo->spi_piobufbase - dd->ipath_piobufbase) / + dd->ipath_palign; kinfo->spi_pioalign = dd->ipath_palign; kinfo->spi_qpair = IPATH_KD_QP; kinfo->spi_piosize = dd->ipath_ibmaxlen; kinfo->spi_mtu = dd->ipath_ibmaxlen; /* maxlen, not ibmtu */ kinfo->spi_port = pd->port_port; + kinfo->spi_subport = subport_fp(fp); kinfo->spi_sw_version = IPATH_KERN_SWVERSION; kinfo->spi_hw_version = dd->ipath_revision; + if (master) { + kinfo->spi_runtime_flags |= IPATH_RUNTIME_MASTER; + kinfo->spi_subport_uregbase = + (u64) pd->subport_uregbase & MMAP64_MASK; + kinfo->spi_subport_rcvegrbuf = + (u64) pd->subport_rcvegrbuf & MMAP64_MASK; + kinfo->spi_subport_rcvhdr_base = + (u64) pd->subport_rcvhdr_base & MMAP64_MASK; + ipath_cdbg(PROC, "port %u flags %x %llx %llx %llx\n", + kinfo->spi_port, kinfo->spi_runtime_flags, + (unsigned long long) kinfo->spi_subport_uregbase, + (unsigned long long) kinfo->spi_subport_rcvegrbuf, + (unsigned long long) kinfo->spi_subport_rcvhdr_base); + } + if (copy_to_user(ubase, kinfo, sizeof(*kinfo))) ret = -EFAULT; @@ -154,6 +223,7 @@ bail: /** * ipath_tid_update - update a port TID * @pd: the port + * @fp: the ipath device file * @ti: the TID information * * The new implementation as of Oct 2004 is that the driver assigns @@ -176,11 +246,11 @@ bail: * virtually contiguous pages, that should change to improve * performance. */ -static int ipath_tid_update(struct ipath_portdata *pd, +static int ipath_tid_update(struct ipath_portdata *pd, struct file *fp, const struct ipath_tid_info *ti) { int ret = 0, ntids; - u32 tid, porttid, cnt, i, tidcnt; + u32 tid, porttid, cnt, i, tidcnt, tidoff; u16 *tidlist; struct ipath_devdata *dd = pd->port_dd; u64 physaddr; @@ -188,6 +258,7 @@ static int ipath_tid_update(struct ipath_portdata *pd, u64 __iomem *tidbase; unsigned long tidmap[8]; struct page **pagep = NULL; + unsigned subport = subport_fp(fp); if (!dd->ipath_pageshadow) { ret = -ENOMEM; @@ -204,20 +275,34 @@ static int ipath_tid_update(struct ipath_portdata *pd, ret = -EFAULT; goto done; } - tidcnt = dd->ipath_rcvtidcnt; - if (cnt >= tidcnt) { + porttid = pd->port_port * dd->ipath_rcvtidcnt; + if (!pd->port_subport_cnt) { + tidcnt = dd->ipath_rcvtidcnt; + tid = pd->port_tidcursor; + tidoff = 0; + } else if (!subport) { + tidcnt = (dd->ipath_rcvtidcnt / pd->port_subport_cnt) + + (dd->ipath_rcvtidcnt % pd->port_subport_cnt); + tidoff = dd->ipath_rcvtidcnt - tidcnt; + porttid += tidoff; + tid = tidcursor_fp(fp); + } else { + tidcnt = dd->ipath_rcvtidcnt / pd->port_subport_cnt; + tidoff = tidcnt * (subport - 1); + porttid += tidoff; + tid = tidcursor_fp(fp); + } + if (cnt > tidcnt) { /* make sure it all fits in port_tid_pg_list */ dev_info(&dd->pcidev->dev, "Process tried to allocate %u " "TIDs, only trying max (%u)\n", cnt, tidcnt); cnt = tidcnt; } - pagep = (struct page **)pd->port_tid_pg_list; - tidlist = (u16 *) (&pagep[cnt]); + pagep = &((struct page **) pd->port_tid_pg_list)[tidoff]; + tidlist = &((u16 *) &pagep[dd->ipath_rcvtidcnt])[tidoff]; memset(tidmap, 0, sizeof(tidmap)); - tid = pd->port_tidcursor; /* before decrement; chip actual # */ - porttid = pd->port_port * tidcnt; ntids = tidcnt; tidbase = (u64 __iomem *) (((char __iomem *) dd->ipath_kregbase) + dd->ipath_rcvtidbase + @@ -274,16 +359,19 @@ static int ipath_tid_update(struct ipath_portdata *pd, ret = -ENOMEM; break; } - tidlist[i] = tid; + tidlist[i] = tid + tidoff; ipath_cdbg(VERBOSE, "Updating idx %u to TID %u, " - "vaddr %lx\n", i, tid, vaddr); + "vaddr %lx\n", i, tid + tidoff, vaddr); /* we "know" system pages and TID pages are same size */ dd->ipath_pageshadow[porttid + tid] = pagep[i]; + dd->ipath_physshadow[porttid + tid] = ipath_map_page( + dd->pcidev, pagep[i], 0, PAGE_SIZE, + PCI_DMA_FROMDEVICE); /* * don't need atomic or it's overhead */ __set_bit(tid, tidmap); - physaddr = page_to_phys(pagep[i]); + physaddr = dd->ipath_physshadow[porttid + tid]; ipath_stats.sps_pagelocks++; ipath_cdbg(VERBOSE, "TID %u, vaddr %lx, physaddr %llx pgp %p\n", @@ -317,6 +405,9 @@ static int ipath_tid_update(struct ipath_portdata *pd, tid); dd->ipath_f_put_tid(dd, &tidbase[tid], 1, dd->ipath_tidinvalid); + pci_unmap_page(dd->pcidev, + dd->ipath_physshadow[porttid + tid], + PAGE_SIZE, PCI_DMA_FROMDEVICE); dd->ipath_pageshadow[porttid + tid] = NULL; ipath_stats.sps_pageunlocks++; } @@ -341,7 +432,10 @@ static int ipath_tid_update(struct ipath_portdata *pd, } if (tid == tidcnt) tid = 0; - pd->port_tidcursor = tid; + if (!pd->port_subport_cnt) + pd->port_tidcursor = tid; + else + tidcursor_fp(fp) = tid; } done: @@ -354,6 +448,7 @@ done: /** * ipath_tid_free - free a port TID * @pd: the port + * @subport: the subport * @ti: the TID info * * right now we are unlocking one page at a time, but since @@ -367,7 +462,7 @@ done: * they pass in to us. */ -static int ipath_tid_free(struct ipath_portdata *pd, +static int ipath_tid_free(struct ipath_portdata *pd, unsigned subport, const struct ipath_tid_info *ti) { int ret = 0; @@ -388,11 +483,20 @@ static int ipath_tid_free(struct ipath_portdata *pd, } porttid = pd->port_port * dd->ipath_rcvtidcnt; + if (!pd->port_subport_cnt) + tidcnt = dd->ipath_rcvtidcnt; + else if (!subport) { + tidcnt = (dd->ipath_rcvtidcnt / pd->port_subport_cnt) + + (dd->ipath_rcvtidcnt % pd->port_subport_cnt); + porttid += dd->ipath_rcvtidcnt - tidcnt; + } else { + tidcnt = dd->ipath_rcvtidcnt / pd->port_subport_cnt; + porttid += tidcnt * (subport - 1); + } tidbase = (u64 __iomem *) ((char __iomem *)(dd->ipath_kregbase) + dd->ipath_rcvtidbase + porttid * sizeof(*tidbase)); - tidcnt = dd->ipath_rcvtidcnt; limit = sizeof(tidmap) * BITS_PER_BYTE; if (limit > tidcnt) /* just in case size changes in future */ @@ -417,6 +521,9 @@ static int ipath_tid_free(struct ipath_portdata *pd, pd->port_pid, tid); dd->ipath_f_put_tid(dd, &tidbase[tid], 1, dd->ipath_tidinvalid); + pci_unmap_page(dd->pcidev, + dd->ipath_physshadow[porttid + tid], + PAGE_SIZE, PCI_DMA_FROMDEVICE); ipath_release_user_pages( &dd->ipath_pageshadow[porttid + tid], 1); dd->ipath_pageshadow[porttid + tid] = NULL; @@ -581,20 +688,24 @@ bail: /** * ipath_manage_rcvq - manage a port's receive queue * @pd: the port + * @subport: the subport * @start_stop: action to carry out * * start_stop == 0 disables receive on the port, for use in queue * overflow conditions. start_stop==1 re-enables, to be used to * re-init the software copy of the head register */ -static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop) +static int ipath_manage_rcvq(struct ipath_portdata *pd, unsigned subport, + int start_stop) { struct ipath_devdata *dd = pd->port_dd; u64 tval; - ipath_cdbg(PROC, "%sabling rcv for unit %u port %u\n", + ipath_cdbg(PROC, "%sabling rcv for unit %u port %u:%u\n", start_stop ? "en" : "dis", dd->ipath_unit, - pd->port_port); + pd->port_port, subport); + if (subport) + goto bail; /* atomically clear receive enable port. */ if (start_stop) { /* @@ -609,7 +720,7 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop) * updated and correct itself, even in the face of software * bugs. */ - *pd->port_rcvhdrtail_kvaddr = 0; + *(volatile u64 *)pd->port_rcvhdrtail_kvaddr = 0; set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port, &dd->ipath_rcvctrl); } else @@ -630,6 +741,7 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop) tval = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port); } /* always; new head should be equal to new tail; see above */ +bail: return 0; } @@ -687,6 +799,36 @@ static void ipath_clean_part_key(struct ipath_portdata *pd, } } +/* + * Initialize the port data with the receive buffer sizes + * so this can be done while the master port is locked. + * Otherwise, there is a race with a slave opening the port + * and seeing these fields uninitialized. + */ +static void init_user_egr_sizes(struct ipath_portdata *pd) +{ + struct ipath_devdata *dd = pd->port_dd; + unsigned egrperchunk, egrcnt, size; + + /* + * to avoid wasting a lot of memory, we allocate 32KB chunks of + * physically contiguous memory, advance through it until used up + * and then allocate more. Of course, we need memory to store those + * extra pointers, now. Started out with 256KB, but under heavy + * memory pressure (creating large files and then copying them over + * NFS while doing lots of MPI jobs), we hit some allocation + * failures, even though we can sleep... (2.6.10) Still get + * failures at 64K. 32K is the lowest we can go without wasting + * additional memory. + */ + size = 0x8000; + egrperchunk = size / dd->ipath_rcvegrbufsize; + egrcnt = dd->ipath_rcvegrcnt; + pd->port_rcvegrbuf_chunks = (egrcnt + egrperchunk - 1) / egrperchunk; + pd->port_rcvegrbufs_perchunk = egrperchunk; + pd->port_rcvegrbuf_size = size; +} + /** * ipath_create_user_egr - allocate eager TID buffers * @pd: the port to allocate TID buffers for @@ -702,7 +844,7 @@ static void ipath_clean_part_key(struct ipath_portdata *pd, static int ipath_create_user_egr(struct ipath_portdata *pd) { struct ipath_devdata *dd = pd->port_dd; - unsigned e, egrcnt, alloced, egrperchunk, chunk, egrsize, egroff; + unsigned e, egrcnt, egrperchunk, chunk, egrsize, egroff; size_t size; int ret; gfp_t gfp_flags; @@ -722,31 +864,18 @@ static int ipath_create_user_egr(struct ipath_portdata *pd) ipath_cdbg(VERBOSE, "Allocating %d egr buffers, at egrtid " "offset %x, egrsize %u\n", egrcnt, egroff, egrsize); - /* - * to avoid wasting a lot of memory, we allocate 32KB chunks of - * physically contiguous memory, advance through it until used up - * and then allocate more. Of course, we need memory to store those - * extra pointers, now. Started out with 256KB, but under heavy - * memory pressure (creating large files and then copying them over - * NFS while doing lots of MPI jobs), we hit some allocation - * failures, even though we can sleep... (2.6.10) Still get - * failures at 64K. 32K is the lowest we can go without wasting - * additional memory. - */ - size = 0x8000; - alloced = ALIGN(egrsize * egrcnt, size); - egrperchunk = size / egrsize; - chunk = (egrcnt + egrperchunk - 1) / egrperchunk; - pd->port_rcvegrbuf_chunks = chunk; - pd->port_rcvegrbufs_perchunk = egrperchunk; - pd->port_rcvegrbuf_size = size; - pd->port_rcvegrbuf = vmalloc(chunk * sizeof(pd->port_rcvegrbuf[0])); + chunk = pd->port_rcvegrbuf_chunks; + egrperchunk = pd->port_rcvegrbufs_perchunk; + size = pd->port_rcvegrbuf_size; + pd->port_rcvegrbuf = kmalloc(chunk * sizeof(pd->port_rcvegrbuf[0]), + GFP_KERNEL); if (!pd->port_rcvegrbuf) { ret = -ENOMEM; goto bail; } pd->port_rcvegrbuf_phys = - vmalloc(chunk * sizeof(pd->port_rcvegrbuf_phys[0])); + kmalloc(chunk * sizeof(pd->port_rcvegrbuf_phys[0]), + GFP_KERNEL); if (!pd->port_rcvegrbuf_phys) { ret = -ENOMEM; goto bail_rcvegrbuf; @@ -791,105 +920,23 @@ bail_rcvegrbuf_phys: pd->port_rcvegrbuf_phys[e]); } - vfree(pd->port_rcvegrbuf_phys); + kfree(pd->port_rcvegrbuf_phys); pd->port_rcvegrbuf_phys = NULL; bail_rcvegrbuf: - vfree(pd->port_rcvegrbuf); + kfree(pd->port_rcvegrbuf); pd->port_rcvegrbuf = NULL; bail: return ret; } -static int ipath_do_user_init(struct ipath_portdata *pd, - const struct ipath_user_info *uinfo) -{ - int ret = 0; - struct ipath_devdata *dd = pd->port_dd; - u32 head32; - - /* for now, if major version is different, bail */ - if ((uinfo->spu_userversion >> 16) != IPATH_USER_SWMAJOR) { - dev_info(&dd->pcidev->dev, - "User major version %d not same as driver " - "major %d\n", uinfo->spu_userversion >> 16, - IPATH_USER_SWMAJOR); - ret = -ENODEV; - goto done; - } - - if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR) - ipath_dbg("User minor version %d not same as driver " - "minor %d\n", uinfo->spu_userversion & 0xffff, - IPATH_USER_SWMINOR); - - if (uinfo->spu_rcvhdrsize) { - ret = ipath_setrcvhdrsize(dd, uinfo->spu_rcvhdrsize); - if (ret) - goto done; - } - - /* for now we do nothing with rcvhdrcnt: uinfo->spu_rcvhdrcnt */ - - /* for right now, kernel piobufs are at end, so port 1 is at 0 */ - pd->port_piobufs = dd->ipath_piobufbase + - dd->ipath_pbufsport * (pd->port_port - - 1) * dd->ipath_palign; - ipath_cdbg(VERBOSE, "Set base of piobufs for port %u to 0x%x\n", - pd->port_port, pd->port_piobufs); - - /* - * Now allocate the rcvhdr Q and eager TIDs; skip the TID - * array for time being. If pd->port_port > chip-supported, - * we need to do extra stuff here to handle by handling overflow - * through port 0, someday - */ - ret = ipath_create_rcvhdrq(dd, pd); - if (!ret) - ret = ipath_create_user_egr(pd); - if (ret) - goto done; - - /* - * set the eager head register for this port to the current values - * of the tail pointers, since we don't know if they were - * updated on last use of the port. - */ - head32 = ipath_read_ureg32(dd, ur_rcvegrindextail, pd->port_port); - ipath_write_ureg(dd, ur_rcvegrindexhead, head32, pd->port_port); - dd->ipath_lastegrheads[pd->port_port] = -1; - dd->ipath_lastrcvhdrqtails[pd->port_port] = -1; - ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n", - pd->port_port, head32); - pd->port_tidcursor = 0; /* start at beginning after open */ - /* - * now enable the port; the tail registers will be written to memory - * by the chip as soon as it sees the write to - * dd->ipath_kregs->kr_rcvctrl. The update only happens on - * transition from 0 to 1, so clear it first, then set it as part of - * enabling the port. This will (very briefly) affect any other - * open ports, but it shouldn't be long enough to be an issue. - * We explictly set the in-memory copy to 0 beforehand, so we don't - * have to wait to be sure the DMA update has happened. - */ - *pd->port_rcvhdrtail_kvaddr = 0ULL; - set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port, - &dd->ipath_rcvctrl); - ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, - dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD); - ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, - dd->ipath_rcvctrl); -done: - return ret; -} - /* common code for the mappings on dma_alloc_coherent mem */ static int ipath_mmap_mem(struct vm_area_struct *vma, - struct ipath_portdata *pd, unsigned len, - int write_ok, dma_addr_t addr, char *what) + struct ipath_portdata *pd, unsigned len, int write_ok, + void *kvaddr, char *what) { struct ipath_devdata *dd = pd->port_dd; - unsigned pfn = (unsigned long)addr >> PAGE_SHIFT; + unsigned long pfn; int ret; if ((vma->vm_end - vma->vm_start) > len) { @@ -912,17 +959,17 @@ static int ipath_mmap_mem(struct vm_area_struct *vma, vma->vm_flags &= ~VM_MAYWRITE; } + pfn = virt_to_phys(kvaddr) >> PAGE_SHIFT; ret = remap_pfn_range(vma, vma->vm_start, pfn, len, vma->vm_page_prot); if (ret) - dev_info(&dd->pcidev->dev, - "%s port%u mmap of %lx, %x bytes r%c failed: %d\n", - what, pd->port_port, (unsigned long)addr, len, - write_ok?'w':'o', ret); + dev_info(&dd->pcidev->dev, "%s port%u mmap of %lx, %x " + "bytes r%c failed: %d\n", what, pd->port_port, + pfn, len, write_ok?'w':'o', ret); else - ipath_cdbg(VERBOSE, "%s port%u mmaped %lx, %x bytes r%c\n", - what, pd->port_port, (unsigned long)addr, len, - write_ok?'w':'o'); + ipath_cdbg(VERBOSE, "%s port%u mmaped %lx, %x bytes " + "r%c\n", what, pd->port_port, pfn, len, + write_ok?'w':'o'); bail: return ret; } @@ -957,7 +1004,8 @@ static int mmap_ureg(struct vm_area_struct *vma, struct ipath_devdata *dd, static int mmap_piobufs(struct vm_area_struct *vma, struct ipath_devdata *dd, - struct ipath_portdata *pd) + struct ipath_portdata *pd, + unsigned piobufs, unsigned piocnt) { unsigned long phys; int ret; @@ -968,16 +1016,15 @@ static int mmap_piobufs(struct vm_area_struct *vma, * process data, and catches users who might try to read the i/o * space due to a bug. */ - if ((vma->vm_end - vma->vm_start) > - (dd->ipath_pbufsport * dd->ipath_palign)) { + if ((vma->vm_end - vma->vm_start) > (piocnt * dd->ipath_palign)) { dev_info(&dd->pcidev->dev, "FAIL mmap piobufs: " "reqlen %lx > PAGE\n", vma->vm_end - vma->vm_start); - ret = -EFAULT; + ret = -EINVAL; goto bail; } - phys = dd->ipath_physaddr + pd->port_piobufs; + phys = dd->ipath_physaddr + piobufs; /* * Don't mark this as non-cached, or we don't get the @@ -1011,7 +1058,7 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma, struct ipath_devdata *dd = pd->port_dd; unsigned long start, size; size_t total_size, i; - dma_addr_t *phys; + unsigned long pfn; int ret; size = pd->port_rcvegrbuf_size; @@ -1021,7 +1068,7 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma, "reqlen %lx > actual %lx\n", vma->vm_end - vma->vm_start, (unsigned long) total_size); - ret = -EFAULT; + ret = -EINVAL; goto bail; } @@ -1035,11 +1082,11 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma, vma->vm_flags &= ~VM_MAYWRITE; start = vma->vm_start; - phys = pd->port_rcvegrbuf_phys; for (i = 0; i < pd->port_rcvegrbuf_chunks; i++, start += size) { - ret = remap_pfn_range(vma, start, phys[i] >> PAGE_SHIFT, - size, vma->vm_page_prot); + pfn = virt_to_phys(pd->port_rcvegrbuf[i]) >> PAGE_SHIFT; + ret = remap_pfn_range(vma, start, pfn, size, + vma->vm_page_prot); if (ret < 0) goto bail; } @@ -1049,6 +1096,122 @@ bail: return ret; } +/* + * ipath_file_vma_nopage - handle a VMA page fault. + */ +static struct page *ipath_file_vma_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + unsigned long offset = address - vma->vm_start; + struct page *page = NOPAGE_SIGBUS; + void *pageptr; + + /* + * Convert the vmalloc address into a struct page. + */ + pageptr = (void *)(offset + (vma->vm_pgoff << PAGE_SHIFT)); + page = vmalloc_to_page(pageptr); + if (!page) + goto out; + + /* Increment the reference count. */ + get_page(page); + if (type) + *type = VM_FAULT_MINOR; +out: + return page; +} + +static struct vm_operations_struct ipath_file_vm_ops = { + .nopage = ipath_file_vma_nopage, +}; + +static int mmap_kvaddr(struct vm_area_struct *vma, u64 pgaddr, + struct ipath_portdata *pd, unsigned subport) +{ + unsigned long len; + struct ipath_devdata *dd; + void *addr; + size_t size; + int ret; + + /* If the port is not shared, all addresses should be physical */ + if (!pd->port_subport_cnt) { + ret = -EINVAL; + goto bail; + } + + dd = pd->port_dd; + size = pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size; + + /* + * Master has all the slave uregbase, rcvhdrq, and + * rcvegrbufs mmapped. + */ + if (subport == 0) { + unsigned num_slaves = pd->port_subport_cnt - 1; + + if (pgaddr == ((u64) pd->subport_uregbase & MMAP64_MASK)) { + addr = pd->subport_uregbase; + size = PAGE_SIZE * num_slaves; + } else if (pgaddr == ((u64) pd->subport_rcvhdr_base & + MMAP64_MASK)) { + addr = pd->subport_rcvhdr_base; + size = pd->port_rcvhdrq_size * num_slaves; + } else if (pgaddr == ((u64) pd->subport_rcvegrbuf & + MMAP64_MASK)) { + addr = pd->subport_rcvegrbuf; + size *= num_slaves; + } else { + ret = -EINVAL; + goto bail; + } + } else if (pgaddr == (((u64) pd->subport_uregbase + + PAGE_SIZE * (subport - 1)) & MMAP64_MASK)) { + addr = pd->subport_uregbase + PAGE_SIZE * (subport - 1); + size = PAGE_SIZE; + } else if (pgaddr == (((u64) pd->subport_rcvhdr_base + + pd->port_rcvhdrq_size * (subport - 1)) & + MMAP64_MASK)) { + addr = pd->subport_rcvhdr_base + + pd->port_rcvhdrq_size * (subport - 1); + size = pd->port_rcvhdrq_size; + } else if (pgaddr == (((u64) pd->subport_rcvegrbuf + + size * (subport - 1)) & MMAP64_MASK)) { + addr = pd->subport_rcvegrbuf + size * (subport - 1); + /* rcvegrbufs are read-only on the slave */ + if (vma->vm_flags & VM_WRITE) { + dev_info(&dd->pcidev->dev, + "Can't map eager buffers as " + "writable (flags=%lx)\n", vma->vm_flags); + ret = -EPERM; + goto bail; + } + /* + * Don't allow permission to later change to writeable + * with mprotect. + */ + vma->vm_flags &= ~VM_MAYWRITE; + } else { + ret = -EINVAL; + goto bail; + } + len = vma->vm_end - vma->vm_start; + if (len > size) { + ipath_cdbg(MM, "FAIL: reqlen %lx > %zx\n", len, size); + ret = -EINVAL; + goto bail; + } + + vma->vm_pgoff = (unsigned long) addr >> PAGE_SHIFT; + vma->vm_ops = &ipath_file_vm_ops; + vma->vm_flags |= VM_RESERVED | VM_DONTEXPAND; + ret = 0; + +bail: + return ret; +} + /** * ipath_mmap - mmap various structures into user space * @fp: the file pointer @@ -1064,73 +1227,99 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma) struct ipath_portdata *pd; struct ipath_devdata *dd; u64 pgaddr, ureg; + unsigned piobufs, piocnt; int ret; pd = port_fp(fp); + if (!pd) { + ret = -EINVAL; + goto bail; + } dd = pd->port_dd; /* * This is the ipath_do_user_init() code, mapping the shared buffers * into the user process. The address referred to by vm_pgoff is the - * virtual, not physical, address; we only do one mmap for each - * space mapped. + * file offset passed via mmap(). For shared ports, this is the + * kernel vmalloc() address of the pages to share with the master. + * For non-shared or master ports, this is a physical address. + * We only do one mmap for each space mapped. */ pgaddr = vma->vm_pgoff << PAGE_SHIFT; /* - * Must fit in 40 bits for our hardware; some checked elsewhere, - * but we'll be paranoid. Check for 0 is mostly in case one of the - * allocations failed, but user called mmap anyway. We want to catch - * that before it can match. + * Check for 0 in case one of the allocations failed, but user + * called mmap anyway. */ - if (!pgaddr || pgaddr >= (1ULL<<40)) { - ipath_dev_err(dd, "Bad phys addr %llx, start %lx, end %lx\n", - (unsigned long long)pgaddr, vma->vm_start, vma->vm_end); - return -EINVAL; + if (!pgaddr) { + ret = -EINVAL; + goto bail; } - /* just the offset of the port user registers, not physical addr */ - ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port; - - ipath_cdbg(MM, "ushare: pgaddr %llx vm_start=%lx, vmlen %lx\n", + ipath_cdbg(MM, "pgaddr %llx vm_start=%lx len %lx port %u:%u:%u\n", (unsigned long long) pgaddr, vma->vm_start, - vma->vm_end - vma->vm_start); + vma->vm_end - vma->vm_start, dd->ipath_unit, + pd->port_port, subport_fp(fp)); - if (vma->vm_start & (PAGE_SIZE-1)) { - ipath_dev_err(dd, - "vm_start not aligned: %lx, end=%lx phys %lx\n", - vma->vm_start, vma->vm_end, (unsigned long)pgaddr); - ret = -EINVAL; + /* + * Physical addresses must fit in 40 bits for our hardware. + * Check for kernel virtual addresses first, anything else must + * match a HW or memory address. + */ + if (pgaddr >= (1ULL<<40)) { + ret = mmap_kvaddr(vma, pgaddr, pd, subport_fp(fp)); + goto bail; } - else if (pgaddr == ureg) + + if (!pd->port_subport_cnt) { + /* port is not shared */ + ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port; + piocnt = dd->ipath_pbufsport; + piobufs = pd->port_piobufs; + } else if (!subport_fp(fp)) { + /* caller is the master */ + ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port; + piocnt = (dd->ipath_pbufsport / pd->port_subport_cnt) + + (dd->ipath_pbufsport % pd->port_subport_cnt); + piobufs = pd->port_piobufs + + dd->ipath_palign * (dd->ipath_pbufsport - piocnt); + } else { + unsigned slave = subport_fp(fp) - 1; + + /* caller is a slave */ + ureg = 0; + piocnt = dd->ipath_pbufsport / pd->port_subport_cnt; + piobufs = pd->port_piobufs + dd->ipath_palign * piocnt * slave; + } + + if (pgaddr == ureg) ret = mmap_ureg(vma, dd, ureg); - else if (pgaddr == pd->port_piobufs) - ret = mmap_piobufs(vma, dd, pd); - else if (pgaddr == (u64) pd->port_rcvegr_phys) + else if (pgaddr == piobufs) + ret = mmap_piobufs(vma, dd, pd, piobufs, piocnt); + else if (pgaddr == dd->ipath_pioavailregs_phys) + /* in-memory copy of pioavail registers */ + ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0, + (void *) dd->ipath_pioavailregs_dma, + "pioavail registers"); + else if (subport_fp(fp)) + /* Subports don't mmap the physical receive buffers */ + ret = -EINVAL; + else if (pgaddr == pd->port_rcvegr_phys) ret = mmap_rcvegrbufs(vma, pd); - else if (pgaddr == (u64) pd->port_rcvhdrq_phys) { + else if (pgaddr == (u64) pd->port_rcvhdrq_phys) /* * The rcvhdrq itself; readonly except on HT (so have * to allow writable mapping), multiple pages, contiguous * from an i/o perspective. */ - unsigned total_size = - ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize - * sizeof(u32), PAGE_SIZE); - ret = ipath_mmap_mem(vma, pd, total_size, 1, - pd->port_rcvhdrq_phys, + ret = ipath_mmap_mem(vma, pd, pd->port_rcvhdrq_size, 1, + pd->port_rcvhdrq, "rcvhdrq"); - } - else if (pgaddr == (u64)pd->port_rcvhdrqtailaddr_phys) + else if (pgaddr == (u64) pd->port_rcvhdrqtailaddr_phys) /* in-memory copy of rcvhdrq tail register */ ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0, - pd->port_rcvhdrqtailaddr_phys, + pd->port_rcvhdrtail_kvaddr, "rcvhdrq tail"); - else if (pgaddr == dd->ipath_pioavailregs_phys) - /* in-memory copy of pioavail registers */ - ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0, - dd->ipath_pioavailregs_phys, - "pioavail registers"); else ret = -EINVAL; @@ -1138,9 +1327,10 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma) if (ret < 0) dev_info(&dd->pcidev->dev, - "Failure %d on addr %lx, off %lx\n", - -ret, vma->vm_start, vma->vm_pgoff); - + "Failure %d on off %llx len %lx\n", + -ret, (unsigned long long)pgaddr, + vma->vm_end - vma->vm_start); +bail: return ret; } @@ -1154,6 +1344,8 @@ static unsigned int ipath_poll(struct file *fp, struct ipath_devdata *dd; pd = port_fp(fp); + if (!pd) + goto bail; dd = pd->port_dd; bit = pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT; @@ -1176,7 +1368,7 @@ static unsigned int ipath_poll(struct file *fp, if (tail == head) { set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag); - if(dd->ipath_rhdrhead_intr_off) /* arm rcv interrupt */ + if (dd->ipath_rhdrhead_intr_off) /* arm rcv interrupt */ (void)ipath_write_ureg(dd, ur_rcvhdrhead, dd->ipath_rhdrhead_intr_off | head, pd->port_port); @@ -1200,18 +1392,80 @@ static unsigned int ipath_poll(struct file *fp, ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, dd->ipath_rcvctrl); +bail: return pollflag; } +static int init_subports(struct ipath_devdata *dd, + struct ipath_portdata *pd, + const struct ipath_user_info *uinfo) +{ + int ret = 0; + unsigned num_slaves; + size_t size; + + /* Old user binaries don't know about subports */ + if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR) + goto bail; + /* + * If the user is requesting zero or one port, + * skip the subport allocation. + */ + if (uinfo->spu_subport_cnt <= 1) + goto bail; + if (uinfo->spu_subport_cnt > 4) { + ret = -EINVAL; + goto bail; + } + + num_slaves = uinfo->spu_subport_cnt - 1; + pd->subport_uregbase = vmalloc(PAGE_SIZE * num_slaves); + if (!pd->subport_uregbase) { + ret = -ENOMEM; + goto bail; + } + /* Note: pd->port_rcvhdrq_size isn't initialized yet. */ + size = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize * + sizeof(u32), PAGE_SIZE) * num_slaves; + pd->subport_rcvhdr_base = vmalloc(size); + if (!pd->subport_rcvhdr_base) { + ret = -ENOMEM; + goto bail_ureg; + } + + pd->subport_rcvegrbuf = vmalloc(pd->port_rcvegrbuf_chunks * + pd->port_rcvegrbuf_size * + num_slaves); + if (!pd->subport_rcvegrbuf) { + ret = -ENOMEM; + goto bail_rhdr; + } + + pd->port_subport_cnt = uinfo->spu_subport_cnt; + pd->port_subport_id = uinfo->spu_subport_id; + pd->active_slaves = 1; + goto bail; + +bail_rhdr: + vfree(pd->subport_rcvhdr_base); +bail_ureg: + vfree(pd->subport_uregbase); + pd->subport_uregbase = NULL; +bail: + return ret; +} + static int try_alloc_port(struct ipath_devdata *dd, int port, - struct file *fp) + struct file *fp, + const struct ipath_user_info *uinfo) { + struct ipath_portdata *pd; int ret; - if (!dd->ipath_pd[port]) { - void *p, *ptmp; + if (!(pd = dd->ipath_pd[port])) { + void *ptmp; - p = kzalloc(sizeof(struct ipath_portdata), GFP_KERNEL); + pd = kzalloc(sizeof(struct ipath_portdata), GFP_KERNEL); /* * Allocate memory for use in ipath_tid_update() just once @@ -1221,34 +1475,36 @@ static int try_alloc_port(struct ipath_devdata *dd, int port, ptmp = kmalloc(dd->ipath_rcvtidcnt * sizeof(u16) + dd->ipath_rcvtidcnt * sizeof(struct page **), GFP_KERNEL); - if (!p || !ptmp) { + if (!pd || !ptmp) { ipath_dev_err(dd, "Unable to allocate portdata " "memory, failing open\n"); ret = -ENOMEM; - kfree(p); + kfree(pd); kfree(ptmp); goto bail; } - dd->ipath_pd[port] = p; + dd->ipath_pd[port] = pd; dd->ipath_pd[port]->port_port = port; dd->ipath_pd[port]->port_dd = dd; dd->ipath_pd[port]->port_tid_pg_list = ptmp; init_waitqueue_head(&dd->ipath_pd[port]->port_wait); } - if (!dd->ipath_pd[port]->port_cnt) { - dd->ipath_pd[port]->port_cnt = 1; - fp->private_data = (void *) dd->ipath_pd[port]; + if (!pd->port_cnt) { + pd->userversion = uinfo->spu_userversion; + init_user_egr_sizes(pd); + if ((ret = init_subports(dd, pd, uinfo)) != 0) + goto bail; ipath_cdbg(PROC, "%s[%u] opened unit:port %u:%u\n", current->comm, current->pid, dd->ipath_unit, port); - dd->ipath_pd[port]->port_pid = current->pid; - strncpy(dd->ipath_pd[port]->port_comm, current->comm, - sizeof(dd->ipath_pd[port]->port_comm)); + pd->port_cnt = 1; + port_fp(fp) = pd; + pd->port_pid = current->pid; + strncpy(pd->port_comm, current->comm, sizeof(pd->port_comm)); ipath_stats.sps_ports++; ret = 0; - goto bail; - } - ret = -EBUSY; + } else + ret = -EBUSY; bail: return ret; @@ -1264,7 +1520,8 @@ static inline int usable(struct ipath_devdata *dd) | IPATH_LINKUNK)); } -static int find_free_port(int unit, struct file *fp) +static int find_free_port(int unit, struct file *fp, + const struct ipath_user_info *uinfo) { struct ipath_devdata *dd = ipath_lookup(unit); int ret, i; @@ -1279,8 +1536,8 @@ static int find_free_port(int unit, struct file *fp) goto bail; } - for (i = 0; i < dd->ipath_cfgports; i++) { - ret = try_alloc_port(dd, i, fp); + for (i = 1; i < dd->ipath_cfgports; i++) { + ret = try_alloc_port(dd, i, fp, uinfo); if (ret != -EBUSY) goto bail; } @@ -1290,13 +1547,14 @@ bail: return ret; } -static int find_best_unit(struct file *fp) +static int find_best_unit(struct file *fp, + const struct ipath_user_info *uinfo) { int ret = 0, i, prefunit = -1, devmax; int maxofallports, npresent, nup; int ndev; - (void) ipath_count_units(&npresent, &nup, &maxofallports); + devmax = ipath_count_units(&npresent, &nup, &maxofallports); /* * This code is present to allow a knowledgeable person to @@ -1343,8 +1601,6 @@ static int find_best_unit(struct file *fp) if (prefunit != -1) devmax = prefunit + 1; - else - devmax = ipath_count_units(NULL, NULL, NULL); recheck: for (i = 1; i < maxofallports; i++) { for (ndev = prefunit != -1 ? prefunit : 0; ndev < devmax; @@ -1359,7 +1615,7 @@ recheck: * next. */ continue; - ret = try_alloc_port(dd, i, fp); + ret = try_alloc_port(dd, i, fp, uinfo); if (!ret) goto done; } @@ -1395,22 +1651,183 @@ done: return ret; } +static int find_shared_port(struct file *fp, + const struct ipath_user_info *uinfo) +{ + int devmax, ndev, i; + int ret = 0; + + devmax = ipath_count_units(NULL, NULL, NULL); + + for (ndev = 0; ndev < devmax; ndev++) { + struct ipath_devdata *dd = ipath_lookup(ndev); + + if (!dd) + continue; + for (i = 1; i < dd->ipath_cfgports; i++) { + struct ipath_portdata *pd = dd->ipath_pd[i]; + + /* Skip ports which are not yet open */ + if (!pd || !pd->port_cnt) + continue; + /* Skip port if it doesn't match the requested one */ + if (pd->port_subport_id != uinfo->spu_subport_id) + continue; + /* Verify the sharing process matches the master */ + if (pd->port_subport_cnt != uinfo->spu_subport_cnt || + pd->userversion != uinfo->spu_userversion || + pd->port_cnt >= pd->port_subport_cnt) { + ret = -EINVAL; + goto done; + } + port_fp(fp) = pd; + subport_fp(fp) = pd->port_cnt++; + tidcursor_fp(fp) = 0; + pd->active_slaves |= 1 << subport_fp(fp); + ipath_cdbg(PROC, + "%s[%u] %u sharing %s[%u] unit:port %u:%u\n", + current->comm, current->pid, + subport_fp(fp), + pd->port_comm, pd->port_pid, + dd->ipath_unit, pd->port_port); + ret = 1; + goto done; + } + } + +done: + return ret; +} + static int ipath_open(struct inode *in, struct file *fp) { - int ret, user_minor; + /* The real work is performed later in ipath_assign_port() */ + fp->private_data = kzalloc(sizeof(struct ipath_filedata), GFP_KERNEL); + return fp->private_data ? 0 : -ENOMEM; +} + + +/* Get port early, so can set affinity prior to memory allocation */ +static int ipath_assign_port(struct file *fp, + const struct ipath_user_info *uinfo) +{ + int ret; + int i_minor; + unsigned swminor; + + /* Check to be sure we haven't already initialized this file */ + if (port_fp(fp)) { + ret = -EINVAL; + goto done; + } + + /* for now, if major version is different, bail */ + if ((uinfo->spu_userversion >> 16) != IPATH_USER_SWMAJOR) { + ipath_dbg("User major version %d not same as driver " + "major %d\n", uinfo->spu_userversion >> 16, + IPATH_USER_SWMAJOR); + ret = -ENODEV; + goto done; + } + + swminor = uinfo->spu_userversion & 0xffff; + if (swminor != IPATH_USER_SWMINOR) + ipath_dbg("User minor version %d not same as driver " + "minor %d\n", swminor, IPATH_USER_SWMINOR); mutex_lock(&ipath_mutex); - user_minor = iminor(in) - IPATH_USER_MINOR_BASE; + if (swminor == IPATH_USER_SWMINOR && uinfo->spu_subport_cnt && + (ret = find_shared_port(fp, uinfo))) { + mutex_unlock(&ipath_mutex); + if (ret > 0) + ret = 0; + goto done; + } + + i_minor = iminor(fp->f_dentry->d_inode) - IPATH_USER_MINOR_BASE; ipath_cdbg(VERBOSE, "open on dev %lx (minor %d)\n", - (long)in->i_rdev, user_minor); + (long)fp->f_dentry->d_inode->i_rdev, i_minor); - if (user_minor) - ret = find_free_port(user_minor - 1, fp); + if (i_minor) + ret = find_free_port(i_minor - 1, fp, uinfo); else - ret = find_best_unit(fp); + ret = find_best_unit(fp, uinfo); mutex_unlock(&ipath_mutex); + +done: + return ret; +} + + +static int ipath_do_user_init(struct file *fp, + const struct ipath_user_info *uinfo) +{ + int ret; + struct ipath_portdata *pd; + struct ipath_devdata *dd; + u32 head32; + + pd = port_fp(fp); + dd = pd->port_dd; + + if (uinfo->spu_rcvhdrsize) { + ret = ipath_setrcvhdrsize(dd, uinfo->spu_rcvhdrsize); + if (ret) + goto done; + } + + /* for now we do nothing with rcvhdrcnt: uinfo->spu_rcvhdrcnt */ + + /* for right now, kernel piobufs are at end, so port 1 is at 0 */ + pd->port_piobufs = dd->ipath_piobufbase + + dd->ipath_pbufsport * (pd->port_port - 1) * dd->ipath_palign; + ipath_cdbg(VERBOSE, "Set base of piobufs for port %u to 0x%x\n", + pd->port_port, pd->port_piobufs); + + /* + * Now allocate the rcvhdr Q and eager TIDs; skip the TID + * array for time being. If pd->port_port > chip-supported, + * we need to do extra stuff here to handle by handling overflow + * through port 0, someday + */ + ret = ipath_create_rcvhdrq(dd, pd); + if (!ret) + ret = ipath_create_user_egr(pd); + if (ret) + goto done; + + /* + * set the eager head register for this port to the current values + * of the tail pointers, since we don't know if they were + * updated on last use of the port. + */ + head32 = ipath_read_ureg32(dd, ur_rcvegrindextail, pd->port_port); + ipath_write_ureg(dd, ur_rcvegrindexhead, head32, pd->port_port); + dd->ipath_lastegrheads[pd->port_port] = -1; + dd->ipath_lastrcvhdrqtails[pd->port_port] = -1; + ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n", + pd->port_port, head32); + pd->port_tidcursor = 0; /* start at beginning after open */ + /* + * now enable the port; the tail registers will be written to memory + * by the chip as soon as it sees the write to + * dd->ipath_kregs->kr_rcvctrl. The update only happens on + * transition from 0 to 1, so clear it first, then set it as part of + * enabling the port. This will (very briefly) affect any other + * open ports, but it shouldn't be long enough to be an issue. + * We explictly set the in-memory copy to 0 beforehand, so we don't + * have to wait to be sure the DMA update has happened. + */ + *(volatile u64 *)pd->port_rcvhdrtail_kvaddr = 0ULL; + set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port, + &dd->ipath_rcvctrl); + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, + dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD); + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, + dd->ipath_rcvctrl); +done: return ret; } @@ -1433,6 +1850,8 @@ static void unlock_expected_tids(struct ipath_portdata *pd) if (!dd->ipath_pageshadow[i]) continue; + pci_unmap_page(dd->pcidev, dd->ipath_physshadow[i], + PAGE_SIZE, PCI_DMA_FROMDEVICE); ipath_release_user_pages_on_close(&dd->ipath_pageshadow[i], 1); dd->ipath_pageshadow[i] = NULL; @@ -1453,6 +1872,7 @@ static void unlock_expected_tids(struct ipath_portdata *pd) static int ipath_close(struct inode *in, struct file *fp) { int ret = 0; + struct ipath_filedata *fd; struct ipath_portdata *pd; struct ipath_devdata *dd; unsigned port; @@ -1462,9 +1882,24 @@ static int ipath_close(struct inode *in, struct file *fp) mutex_lock(&ipath_mutex); - pd = port_fp(fp); - port = pd->port_port; + fd = (struct ipath_filedata *) fp->private_data; fp->private_data = NULL; + pd = fd->pd; + if (!pd) { + mutex_unlock(&ipath_mutex); + goto bail; + } + if (--pd->port_cnt) { + /* + * XXX If the master closes the port before the slave(s), + * revoke the mmap for the eager receive queue so + * the slave(s) don't wait for receive data forever. + */ + pd->active_slaves &= ~(1 << fd->subport); + mutex_unlock(&ipath_mutex); + goto bail; + } + port = pd->port_port; dd = pd->port_dd; if (pd->port_hdrqfull) { @@ -1503,8 +1938,6 @@ static int ipath_close(struct inode *in, struct file *fp) /* clean up the pkeys for this port user */ ipath_clean_part_key(pd, dd); - - /* * be paranoid, and never write 0's to these, just use an * unused part of the port 0 tail page. Of course, @@ -1523,39 +1956,49 @@ static int ipath_close(struct inode *in, struct file *fp) i = dd->ipath_pbufsport * (port - 1); ipath_disarm_piobufs(dd, i, dd->ipath_pbufsport); + dd->ipath_f_clear_tids(dd, pd->port_port); + if (dd->ipath_pageshadow) unlock_expected_tids(pd); ipath_stats.sps_ports--; ipath_cdbg(PROC, "%s[%u] closed port %u:%u\n", pd->port_comm, pd->port_pid, dd->ipath_unit, port); - - dd->ipath_f_clear_tids(dd, pd->port_port); } - pd->port_cnt = 0; pd->port_pid = 0; - dd->ipath_pd[pd->port_port] = NULL; /* before releasing mutex */ mutex_unlock(&ipath_mutex); ipath_free_pddata(dd, pd); /* after releasing the mutex */ +bail: + kfree(fd); return ret; } -static int ipath_port_info(struct ipath_portdata *pd, +static int ipath_port_info(struct ipath_portdata *pd, u16 subport, struct ipath_port_info __user *uinfo) { struct ipath_port_info info; int nup; int ret; + size_t sz; (void) ipath_count_units(NULL, &nup, NULL); info.num_active = nup; info.unit = pd->port_dd->ipath_unit; info.port = pd->port_port; + info.subport = subport; + /* Don't return new fields if old library opened the port. */ + if ((pd->userversion & 0xffff) == IPATH_USER_SWMINOR) { + /* Number of user ports available for this device. */ + info.num_ports = pd->port_dd->ipath_cfgports - 1; + info.num_subports = pd->port_subport_cnt; + sz = sizeof(info); + } else + sz = sizeof(info) - 2 * sizeof(u16); - if (copy_to_user(uinfo, &info, sizeof(info))) { + if (copy_to_user(uinfo, &info, sz)) { ret = -EFAULT; goto bail; } @@ -1565,6 +2008,16 @@ bail: return ret; } +static int ipath_get_slave_info(struct ipath_portdata *pd, + void __user *slave_mask_addr) +{ + int ret = 0; + + if (copy_to_user(slave_mask_addr, &pd->active_slaves, sizeof(u32))) + ret = -EFAULT; + return ret; +} + static ssize_t ipath_write(struct file *fp, const char __user *data, size_t count, loff_t *off) { @@ -1591,6 +2044,8 @@ static ssize_t ipath_write(struct file *fp, const char __user *data, consumed = sizeof(cmd.type); switch (cmd.type) { + case IPATH_CMD_ASSIGN_PORT: + case __IPATH_CMD_USER_INIT: case IPATH_CMD_USER_INIT: copy = sizeof(cmd.cmd.user_info); dest = &cmd.cmd.user_info; @@ -1617,6 +2072,11 @@ static ssize_t ipath_write(struct file *fp, const char __user *data, dest = &cmd.cmd.part_key; src = &ucmd->cmd.part_key; break; + case IPATH_CMD_SLAVE_INFO: + copy = sizeof(cmd.cmd.slave_mask_addr); + dest = &cmd.cmd.slave_mask_addr; + src = &ucmd->cmd.slave_mask_addr; + break; default: ret = -EINVAL; goto bail; @@ -1634,34 +2094,55 @@ static ssize_t ipath_write(struct file *fp, const char __user *data, consumed += copy; pd = port_fp(fp); + if (!pd && cmd.type != __IPATH_CMD_USER_INIT && + cmd.type != IPATH_CMD_ASSIGN_PORT) { + ret = -EINVAL; + goto bail; + } switch (cmd.type) { + case IPATH_CMD_ASSIGN_PORT: + ret = ipath_assign_port(fp, &cmd.cmd.user_info); + if (ret) + goto bail; + break; + case __IPATH_CMD_USER_INIT: + /* backwards compatibility, get port first */ + ret = ipath_assign_port(fp, &cmd.cmd.user_info); + if (ret) + goto bail; + /* and fall through to current version. */ case IPATH_CMD_USER_INIT: - ret = ipath_do_user_init(pd, &cmd.cmd.user_info); - if (ret < 0) + ret = ipath_do_user_init(fp, &cmd.cmd.user_info); + if (ret) goto bail; ret = ipath_get_base_info( - pd, (void __user *) (unsigned long) + fp, (void __user *) (unsigned long) cmd.cmd.user_info.spu_base_info, cmd.cmd.user_info.spu_base_info_size); break; case IPATH_CMD_RECV_CTRL: - ret = ipath_manage_rcvq(pd, cmd.cmd.recv_ctrl); + ret = ipath_manage_rcvq(pd, subport_fp(fp), cmd.cmd.recv_ctrl); break; case IPATH_CMD_PORT_INFO: - ret = ipath_port_info(pd, + ret = ipath_port_info(pd, subport_fp(fp), (struct ipath_port_info __user *) (unsigned long) cmd.cmd.port_info); break; case IPATH_CMD_TID_UPDATE: - ret = ipath_tid_update(pd, &cmd.cmd.tid_info); + ret = ipath_tid_update(pd, fp, &cmd.cmd.tid_info); break; case IPATH_CMD_TID_FREE: - ret = ipath_tid_free(pd, &cmd.cmd.tid_info); + ret = ipath_tid_free(pd, subport_fp(fp), &cmd.cmd.tid_info); break; case IPATH_CMD_SET_PART_KEY: ret = ipath_set_part_key(pd, cmd.cmd.part_key); break; + case IPATH_CMD_SLAVE_INFO: + ret = ipath_get_slave_info(pd, + (void __user *) (unsigned long) + cmd.cmd.slave_mask_addr); + break; } if (ret >= 0) @@ -1858,4 +2339,3 @@ void ipath_user_remove(struct ipath_devdata *dd) bail: return; } - diff --git a/drivers/infiniband/hw/ipath/ipath_fs.c b/drivers/infiniband/hw/ipath/ipath_fs.c index c8a8af0fe47..a507d0b5be6 100644 --- a/drivers/infiniband/hw/ipath/ipath_fs.c +++ b/drivers/infiniband/hw/ipath/ipath_fs.c @@ -356,19 +356,16 @@ static ssize_t flash_write(struct file *file, const char __user *buf, pos = *ppos; - if ( pos < 0) { + if (pos != 0) { ret = -EINVAL; goto bail; } - if (pos >= sizeof(struct ipath_flash)) { - ret = 0; + if (count != sizeof(struct ipath_flash)) { + ret = -EINVAL; goto bail; } - if (count > sizeof(struct ipath_flash) - pos) - count = sizeof(struct ipath_flash) - pos; - tmp = kmalloc(count, GFP_KERNEL); if (!tmp) { ret = -ENOMEM; diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c index 5c9b509e40e..9e4e8d4c6e2 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba6110.c +++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c @@ -252,8 +252,8 @@ static const struct ipath_cregs ipath_ht_cregs = { }; /* kr_intstatus, kr_intclear, kr_intmask bits */ -#define INFINIPATH_I_RCVURG_MASK 0x1FF -#define INFINIPATH_I_RCVAVAIL_MASK 0x1FF +#define INFINIPATH_I_RCVURG_MASK ((1U<<9)-1) +#define INFINIPATH_I_RCVAVAIL_MASK ((1U<<9)-1) /* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */ #define INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT 0 @@ -338,7 +338,7 @@ static void hwerr_crcbits(struct ipath_devdata *dd, ipath_err_t hwerrs, if (crcbits) { u16 ctrl0, ctrl1; snprintf(bitsmsg, sizeof bitsmsg, - "[HT%s lane %s CRC (%llx); ignore till reload]", + "[HT%s lane %s CRC (%llx); powercycle to completely clear]", !(crcbits & _IPATH_HTLINK1_CRCBITS) ? "0 (A)" : (!(crcbits & _IPATH_HTLINK0_CRCBITS) ? "1 (B)" : "0+1 (A+B)"), @@ -389,17 +389,28 @@ static void hwerr_crcbits(struct ipath_devdata *dd, ipath_err_t hwerrs, _IPATH_HTLINK1_CRCBITS))); } +/* 6110 specific hardware errors... */ +static const struct ipath_hwerror_msgs ipath_6110_hwerror_msgs[] = { + INFINIPATH_HWE_MSG(HTCBUSIREQPARITYERR, "HTC Ireq Parity"), + INFINIPATH_HWE_MSG(HTCBUSTREQPARITYERR, "HTC Treq Parity"), + INFINIPATH_HWE_MSG(HTCBUSTRESPPARITYERR, "HTC Tresp Parity"), + INFINIPATH_HWE_MSG(HTCMISCERR5, "HT core Misc5"), + INFINIPATH_HWE_MSG(HTCMISCERR6, "HT core Misc6"), + INFINIPATH_HWE_MSG(HTCMISCERR7, "HT core Misc7"), + INFINIPATH_HWE_MSG(RXDSYNCMEMPARITYERR, "Rx Dsync"), + INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"), +}; + /** - * ipath_ht_handle_hwerrors - display hardware errors + * ipath_ht_handle_hwerrors - display hardware errors. * @dd: the infinipath device * @msg: the output buffer * @msgl: the size of the output buffer * - * Use same msg buffer as regular errors to avoid - * excessive stack use. Most hardware errors are catastrophic, but for - * right now, we'll print them and continue. - * We reuse the same message buffer as ipath_handle_errors() to avoid - * excessive stack usage. + * Use same msg buffer as regular errors to avoid excessive stack + * use. Most hardware errors are catastrophic, but for right now, + * we'll print them and continue. We reuse the same message buffer as + * ipath_handle_errors() to avoid excessive stack usage. */ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg, size_t msgl) @@ -440,19 +451,49 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg, * make sure we get this much out, unless told to be quiet, * or it's occurred within the last 5 seconds */ - if ((hwerrs & ~dd->ipath_lasthwerror) || + if ((hwerrs & ~(dd->ipath_lasthwerror | + ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | + INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) + << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT))) || (ipath_debug & __IPATH_VERBDBG)) dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx " "(cleared)\n", (unsigned long long) hwerrs); dd->ipath_lasthwerror |= hwerrs; - if (hwerrs & ~infinipath_hwe_bitsextant) + if (hwerrs & ~dd->ipath_hwe_bitsextant) ipath_dev_err(dd, "hwerror interrupt with unknown errors " "%llx set\n", (unsigned long long) - (hwerrs & ~infinipath_hwe_bitsextant)); + (hwerrs & ~dd->ipath_hwe_bitsextant)); ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control); if (ctrl & INFINIPATH_C_FREEZEMODE) { + /* + * parity errors in send memory are recoverable, + * just cancel the send (if indicated in * sendbuffererror), + * count the occurrence, unfreeze (if no other handled + * hardware error bits are set), and continue. They can + * occur if a processor speculative read is done to the PIO + * buffer while we are sending a packet, for example. + */ + if (hwerrs & ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | + INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) + << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) { + ipath_stats.sps_txeparity++; + ipath_dbg("Recovering from TXE parity error (%llu), " + "hwerrstatus=%llx\n", + (unsigned long long) ipath_stats.sps_txeparity, + (unsigned long long) hwerrs); + ipath_disarm_senderrbufs(dd); + hwerrs &= ~((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | + INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) + << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT); + if (!hwerrs) { /* else leave in freeze mode */ + ipath_write_kreg(dd, + dd->ipath_kregs->kr_control, + dd->ipath_control); + return; + } + } if (hwerrs) { /* * if any set that we aren't ignoring; only @@ -499,44 +540,16 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg, bits); strlcat(msg, bitsmsg, msgl); } - if (hwerrs & (INFINIPATH_HWE_RXEMEMPARITYERR_MASK - << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT)) { - bits = (u32) ((hwerrs >> - INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) & - INFINIPATH_HWE_RXEMEMPARITYERR_MASK); - snprintf(bitsmsg, sizeof bitsmsg, "[RXE Parity Errs %x] ", - bits); - strlcat(msg, bitsmsg, msgl); - } - if (hwerrs & (INFINIPATH_HWE_TXEMEMPARITYERR_MASK - << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) { - bits = (u32) ((hwerrs >> - INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) & - INFINIPATH_HWE_TXEMEMPARITYERR_MASK); - snprintf(bitsmsg, sizeof bitsmsg, "[TXE Parity Errs %x] ", - bits); - strlcat(msg, bitsmsg, msgl); - } - if (hwerrs & INFINIPATH_HWE_IBCBUSTOSPCPARITYERR) - strlcat(msg, "[IB2IPATH Parity]", msgl); - if (hwerrs & INFINIPATH_HWE_IBCBUSFRSPCPARITYERR) - strlcat(msg, "[IPATH2IB Parity]", msgl); - if (hwerrs & INFINIPATH_HWE_HTCBUSIREQPARITYERR) - strlcat(msg, "[HTC Ireq Parity]", msgl); - if (hwerrs & INFINIPATH_HWE_HTCBUSTREQPARITYERR) - strlcat(msg, "[HTC Treq Parity]", msgl); - if (hwerrs & INFINIPATH_HWE_HTCBUSTRESPPARITYERR) - strlcat(msg, "[HTC Tresp Parity]", msgl); + + ipath_format_hwerrors(hwerrs, + ipath_6110_hwerror_msgs, + sizeof(ipath_6110_hwerror_msgs) / + sizeof(ipath_6110_hwerror_msgs[0]), + msg, msgl); if (hwerrs & (_IPATH_HTLINK0_CRCBITS | _IPATH_HTLINK1_CRCBITS)) hwerr_crcbits(dd, hwerrs, msg, msgl); - if (hwerrs & INFINIPATH_HWE_HTCMISCERR5) - strlcat(msg, "[HT core Misc5]", msgl); - if (hwerrs & INFINIPATH_HWE_HTCMISCERR6) - strlcat(msg, "[HT core Misc6]", msgl); - if (hwerrs & INFINIPATH_HWE_HTCMISCERR7) - strlcat(msg, "[HT core Misc7]", msgl); if (hwerrs & INFINIPATH_HWE_MEMBISTFAILED) { strlcat(msg, "[Memory BIST test failed, InfiniPath hardware unusable]", msgl); @@ -573,11 +586,6 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg, dd->ipath_hwerrmask); } - if (hwerrs & INFINIPATH_HWE_RXDSYNCMEMPARITYERR) - strlcat(msg, "[Rx Dsync]", msgl); - if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED) - strlcat(msg, "[SerDes PLL]", msgl); - ipath_dev_err(dd, "%s hardware error\n", msg); if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) /* @@ -1080,21 +1088,21 @@ static void ipath_setup_ht_setextled(struct ipath_devdata *dd, ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, extctl); } -static void ipath_init_ht_variables(void) +static void ipath_init_ht_variables(struct ipath_devdata *dd) { - ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM; - ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM; - ipath_gpio_sda = IPATH_GPIO_SDA; - ipath_gpio_scl = IPATH_GPIO_SCL; + dd->ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM; + dd->ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM; + dd->ipath_gpio_sda = IPATH_GPIO_SDA; + dd->ipath_gpio_scl = IPATH_GPIO_SCL; - infinipath_i_bitsextant = + dd->ipath_i_bitsextant = (INFINIPATH_I_RCVURG_MASK << INFINIPATH_I_RCVURG_SHIFT) | (INFINIPATH_I_RCVAVAIL_MASK << INFINIPATH_I_RCVAVAIL_SHIFT) | INFINIPATH_I_ERROR | INFINIPATH_I_SPIOSENT | INFINIPATH_I_SPIOBUFAVAIL | INFINIPATH_I_GPIO; - infinipath_e_bitsextant = + dd->ipath_e_bitsextant = INFINIPATH_E_RFORMATERR | INFINIPATH_E_RVCRC | INFINIPATH_E_RICRC | INFINIPATH_E_RMINPKTLEN | INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RLONGPKTLEN | @@ -1112,7 +1120,7 @@ static void ipath_init_ht_variables(void) INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET | INFINIPATH_E_HARDWARE; - infinipath_hwe_bitsextant = + dd->ipath_hwe_bitsextant = (INFINIPATH_HWE_HTCMEMPARITYERR_MASK << INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT) | (INFINIPATH_HWE_TXEMEMPARITYERR_MASK << @@ -1141,8 +1149,8 @@ static void ipath_init_ht_variables(void) INFINIPATH_HWE_IBCBUSTOSPCPARITYERR | INFINIPATH_HWE_IBCBUSFRSPCPARITYERR; - infinipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK; - infinipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK; + dd->ipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK; + dd->ipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK; } /** @@ -1607,5 +1615,5 @@ void ipath_init_iba6110_funcs(struct ipath_devdata *dd) * do very early init that is needed before ipath_f_bus is * called */ - ipath_init_ht_variables(); + ipath_init_ht_variables(dd); } diff --git a/drivers/infiniband/hw/ipath/ipath_iba6120.c b/drivers/infiniband/hw/ipath/ipath_iba6120.c index d86516d23df..a72ab9de386 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba6120.c +++ b/drivers/infiniband/hw/ipath/ipath_iba6120.c @@ -263,8 +263,8 @@ static const struct ipath_cregs ipath_pe_cregs = { }; /* kr_intstatus, kr_intclear, kr_intmask bits */ -#define INFINIPATH_I_RCVURG_MASK 0x1F -#define INFINIPATH_I_RCVAVAIL_MASK 0x1F +#define INFINIPATH_I_RCVURG_MASK ((1U<<5)-1) +#define INFINIPATH_I_RCVAVAIL_MASK ((1U<<5)-1) /* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */ #define INFINIPATH_HWE_PCIEMEMPARITYERR_MASK 0x000000000000003fULL @@ -294,6 +294,33 @@ static const struct ipath_cregs ipath_pe_cregs = { #define IPATH_GPIO_SCL (1ULL << \ (_IPATH_GPIO_SCL_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT)) +/* + * Rev2 silicon allows suppressing check for ArmLaunch errors. + * this can speed up short packet sends on systems that do + * not guaranteee write-order. + */ +#define INFINIPATH_XGXS_SUPPRESS_ARMLAUNCH_ERR (1ULL<<63) + +/* 6120 specific hardware errors... */ +static const struct ipath_hwerror_msgs ipath_6120_hwerror_msgs[] = { + INFINIPATH_HWE_MSG(PCIEPOISONEDTLP, "PCIe Poisoned TLP"), + INFINIPATH_HWE_MSG(PCIECPLTIMEOUT, "PCIe completion timeout"), + /* + * In practice, it's unlikely wthat we'll see PCIe PLL, or bus + * parity or memory parity error failures, because most likely we + * won't be able to talk to the core of the chip. Nonetheless, we + * might see them, if they are in parts of the PCIe core that aren't + * essential. + */ + INFINIPATH_HWE_MSG(PCIE1PLLFAILED, "PCIePLL1"), + INFINIPATH_HWE_MSG(PCIE0PLLFAILED, "PCIePLL0"), + INFINIPATH_HWE_MSG(PCIEBUSPARITYXTLH, "PCIe XTLH core parity"), + INFINIPATH_HWE_MSG(PCIEBUSPARITYXADM, "PCIe ADM TX core parity"), + INFINIPATH_HWE_MSG(PCIEBUSPARITYRADM, "PCIe ADM RX core parity"), + INFINIPATH_HWE_MSG(RXDSYNCMEMPARITYERR, "Rx Dsync"), + INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"), +}; + /** * ipath_pe_handle_hwerrors - display hardware errors. * @dd: the infinipath device @@ -343,19 +370,49 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg, * make sure we get this much out, unless told to be quiet, * or it's occurred within the last 5 seconds */ - if ((hwerrs & ~dd->ipath_lasthwerror) || + if ((hwerrs & ~(dd->ipath_lasthwerror | + ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | + INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) + << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT))) || (ipath_debug & __IPATH_VERBDBG)) dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx " "(cleared)\n", (unsigned long long) hwerrs); dd->ipath_lasthwerror |= hwerrs; - if (hwerrs & ~infinipath_hwe_bitsextant) + if (hwerrs & ~dd->ipath_hwe_bitsextant) ipath_dev_err(dd, "hwerror interrupt with unknown errors " "%llx set\n", (unsigned long long) - (hwerrs & ~infinipath_hwe_bitsextant)); + (hwerrs & ~dd->ipath_hwe_bitsextant)); ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control); if (ctrl & INFINIPATH_C_FREEZEMODE) { + /* + * parity errors in send memory are recoverable, + * just cancel the send (if indicated in * sendbuffererror), + * count the occurrence, unfreeze (if no other handled + * hardware error bits are set), and continue. They can + * occur if a processor speculative read is done to the PIO + * buffer while we are sending a packet, for example. + */ + if (hwerrs & ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | + INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) + << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) { + ipath_stats.sps_txeparity++; + ipath_dbg("Recovering from TXE parity error (%llu), " + "hwerrstatus=%llx\n", + (unsigned long long) ipath_stats.sps_txeparity, + (unsigned long long) hwerrs); + ipath_disarm_senderrbufs(dd); + hwerrs &= ~((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | + INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) + << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT); + if (!hwerrs) { /* else leave in freeze mode */ + ipath_write_kreg(dd, + dd->ipath_kregs->kr_control, + dd->ipath_control); + return; + } + } if (hwerrs) { /* * if any set that we aren't ignoring only make the @@ -379,9 +436,8 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg, } else { ipath_dbg("Clearing freezemode on ignored hardware " "error\n"); - ctrl &= ~INFINIPATH_C_FREEZEMODE; ipath_write_kreg(dd, dd->ipath_kregs->kr_control, - ctrl); + dd->ipath_control); } } @@ -396,24 +452,13 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg, ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, dd->ipath_hwerrmask); } - if (hwerrs & (INFINIPATH_HWE_RXEMEMPARITYERR_MASK - << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT)) { - bits = (u32) ((hwerrs >> - INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) & - INFINIPATH_HWE_RXEMEMPARITYERR_MASK); - snprintf(bitsmsg, sizeof bitsmsg, "[RXE Parity Errs %x] ", - bits); - strlcat(msg, bitsmsg, msgl); - } - if (hwerrs & (INFINIPATH_HWE_TXEMEMPARITYERR_MASK - << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) { - bits = (u32) ((hwerrs >> - INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) & - INFINIPATH_HWE_TXEMEMPARITYERR_MASK); - snprintf(bitsmsg, sizeof bitsmsg, "[TXE Parity Errs %x] ", - bits); - strlcat(msg, bitsmsg, msgl); - } + + ipath_format_hwerrors(hwerrs, + ipath_6120_hwerror_msgs, + sizeof(ipath_6120_hwerror_msgs)/ + sizeof(ipath_6120_hwerror_msgs[0]), + msg, msgl); + if (hwerrs & (INFINIPATH_HWE_PCIEMEMPARITYERR_MASK << INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT)) { bits = (u32) ((hwerrs >> @@ -423,10 +468,6 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg, "[PCIe Mem Parity Errs %x] ", bits); strlcat(msg, bitsmsg, msgl); } - if (hwerrs & INFINIPATH_HWE_IBCBUSTOSPCPARITYERR) - strlcat(msg, "[IB2IPATH Parity]", msgl); - if (hwerrs & INFINIPATH_HWE_IBCBUSFRSPCPARITYERR) - strlcat(msg, "[IPATH2IB Parity]", msgl); #define _IPATH_PLL_FAIL (INFINIPATH_HWE_COREPLL_FBSLIP | \ INFINIPATH_HWE_COREPLL_RFSLIP ) @@ -452,34 +493,6 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg, dd->ipath_hwerrmask); } - if (hwerrs & INFINIPATH_HWE_PCIEPOISONEDTLP) - strlcat(msg, "[PCIe Poisoned TLP]", msgl); - if (hwerrs & INFINIPATH_HWE_PCIECPLTIMEOUT) - strlcat(msg, "[PCIe completion timeout]", msgl); - - /* - * In practice, it's unlikely wthat we'll see PCIe PLL, or bus - * parity or memory parity error failures, because most likely we - * won't be able to talk to the core of the chip. Nonetheless, we - * might see them, if they are in parts of the PCIe core that aren't - * essential. - */ - if (hwerrs & INFINIPATH_HWE_PCIE1PLLFAILED) - strlcat(msg, "[PCIePLL1]", msgl); - if (hwerrs & INFINIPATH_HWE_PCIE0PLLFAILED) - strlcat(msg, "[PCIePLL0]", msgl); - if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYXTLH) - strlcat(msg, "[PCIe XTLH core parity]", msgl); - if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYXADM) - strlcat(msg, "[PCIe ADM TX core parity]", msgl); - if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYRADM) - strlcat(msg, "[PCIe ADM RX core parity]", msgl); - - if (hwerrs & INFINIPATH_HWE_RXDSYNCMEMPARITYERR) - strlcat(msg, "[Rx Dsync]", msgl); - if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED) - strlcat(msg, "[SerDes PLL]", msgl); - ipath_dev_err(dd, "%s hardware error\n", msg); if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) { /* @@ -525,6 +538,9 @@ static int ipath_pe_boardname(struct ipath_devdata *dd, char *name, case 5: n = "InfiniPath_QMH7140"; break; + case 6: + n = "InfiniPath_QLE7142"; + break; default: ipath_dev_err(dd, "Don't yet know about board with ID %u\n", @@ -571,9 +587,12 @@ static void ipath_pe_init_hwerrors(struct ipath_devdata *dd) if (!dd->ipath_boardrev) // no PLL for Emulator val &= ~INFINIPATH_HWE_SERDESPLLFAILED; - /* workaround bug 9460 in internal interface bus parity checking */ - val &= ~INFINIPATH_HWE_PCIEBUSPARITYRADM; - + if (dd->ipath_minrev < 2) { + /* workaround bug 9460 in internal interface bus parity + * checking. Fixed (HW bug 9490) in Rev2. + */ + val &= ~INFINIPATH_HWE_PCIEBUSPARITYRADM; + } dd->ipath_hwerrmask = val; } @@ -583,8 +602,8 @@ static void ipath_pe_init_hwerrors(struct ipath_devdata *dd) */ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd) { - u64 val, tmp, config1; - int ret = 0, change = 0; + u64 val, tmp, config1, prev_val; + int ret = 0; ipath_dbg("Trying to bringup serdes\n"); @@ -641,6 +660,7 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd) val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch); val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig); + prev_val = val; if (((val >> INFINIPATH_XGXS_MDIOADDR_SHIFT) & INFINIPATH_XGXS_MDIOADDR_MASK) != 3) { val &= @@ -648,11 +668,9 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd) INFINIPATH_XGXS_MDIOADDR_SHIFT); /* MDIO address 3 */ val |= 3ULL << INFINIPATH_XGXS_MDIOADDR_SHIFT; - change = 1; } if (val & INFINIPATH_XGXS_RESET) { val &= ~INFINIPATH_XGXS_RESET; - change = 1; } if (((val >> INFINIPATH_XGXS_RX_POL_SHIFT) & INFINIPATH_XGXS_RX_POL_MASK) != dd->ipath_rx_pol_inv ) { @@ -661,9 +679,19 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd) INFINIPATH_XGXS_RX_POL_SHIFT); val |= dd->ipath_rx_pol_inv << INFINIPATH_XGXS_RX_POL_SHIFT; - change = 1; } - if (change) + if (dd->ipath_minrev >= 2) { + /* Rev 2. can tolerate multiple writes to PBC, and + * allowing them can provide lower latency on some + * CPUs, but this feature is off by default, only + * turned on by setting D63 of XGXSconfig reg. + * May want to make this conditional more + * fine-grained in future. This is not exactly + * related to XGXS, but where the bit ended up. + */ + val |= INFINIPATH_XGXS_SUPPRESS_ARMLAUNCH_ERR; + } + if (val != prev_val) ipath_write_kreg(dd, dd->ipath_kregs->kr_xgxsconfig, val); val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0); @@ -717,9 +745,25 @@ static void ipath_pe_quiet_serdes(struct ipath_devdata *dd) ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val); } -/* this is not yet needed on this chip, so just return 0. */ static int ipath_pe_intconfig(struct ipath_devdata *dd) { + u64 val; + u32 chiprev; + + /* + * If the chip supports added error indication via GPIO pins, + * enable interrupts on those bits so the interrupt routine + * can count the events. Also set flag so interrupt routine + * can know they are expected. + */ + chiprev = dd->ipath_revision >> INFINIPATH_R_CHIPREVMINOR_SHIFT; + if ((chiprev & INFINIPATH_R_CHIPREVMINOR_MASK) > 1) { + /* Rev2+ reports extra errors via internal GPIO pins */ + dd->ipath_flags |= IPATH_GPIO_ERRINTRS; + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_gpio_mask); + val |= IPATH_GPIO_ERRINTR_MASK; + ipath_write_kreg( dd, dd->ipath_kregs->kr_gpio_mask, val); + } return 0; } @@ -853,21 +897,23 @@ static int ipath_setup_pe_config(struct ipath_devdata *dd, return 0; } -static void ipath_init_pe_variables(void) +static void ipath_init_pe_variables(struct ipath_devdata *dd) { /* * bits for selecting i2c direction and values, * used for I2C serial flash */ - ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM; - ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM; - ipath_gpio_sda = IPATH_GPIO_SDA; - ipath_gpio_scl = IPATH_GPIO_SCL; + dd->ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM; + dd->ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM; + dd->ipath_gpio_sda = IPATH_GPIO_SDA; + dd->ipath_gpio_scl = IPATH_GPIO_SCL; /* variables for sanity checking interrupt and errors */ - infinipath_hwe_bitsextant = + dd->ipath_hwe_bitsextant = (INFINIPATH_HWE_RXEMEMPARITYERR_MASK << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) | + (INFINIPATH_HWE_TXEMEMPARITYERR_MASK << + INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) | (INFINIPATH_HWE_PCIEMEMPARITYERR_MASK << INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT) | INFINIPATH_HWE_PCIE1PLLFAILED | @@ -883,13 +929,13 @@ static void ipath_init_pe_variables(void) INFINIPATH_HWE_SERDESPLLFAILED | INFINIPATH_HWE_IBCBUSTOSPCPARITYERR | INFINIPATH_HWE_IBCBUSFRSPCPARITYERR; - infinipath_i_bitsextant = + dd->ipath_i_bitsextant = (INFINIPATH_I_RCVURG_MASK << INFINIPATH_I_RCVURG_SHIFT) | (INFINIPATH_I_RCVAVAIL_MASK << INFINIPATH_I_RCVAVAIL_SHIFT) | INFINIPATH_I_ERROR | INFINIPATH_I_SPIOSENT | INFINIPATH_I_SPIOBUFAVAIL | INFINIPATH_I_GPIO; - infinipath_e_bitsextant = + dd->ipath_e_bitsextant = INFINIPATH_E_RFORMATERR | INFINIPATH_E_RVCRC | INFINIPATH_E_RICRC | INFINIPATH_E_RMINPKTLEN | INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RLONGPKTLEN | @@ -907,8 +953,8 @@ static void ipath_init_pe_variables(void) INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET | INFINIPATH_E_HARDWARE; - infinipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK; - infinipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK; + dd->ipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK; + dd->ipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK; } /* setup the MSI stuff again after a reset. I'd like to just call @@ -1082,6 +1128,45 @@ static void ipath_pe_put_tid(struct ipath_devdata *dd, u64 __iomem *tidptr, mmiowb(); spin_unlock_irqrestore(&dd->ipath_tid_lock, flags); } +/** + * ipath_pe_put_tid_2 - write a TID in chip, Revision 2 or higher + * @dd: the infinipath device + * @tidptr: pointer to the expected TID (in chip) to udpate + * @tidtype: 0 for eager, 1 for expected + * @pa: physical address of in memory buffer; ipath_tidinvalid if freeing + * + * This exists as a separate routine to allow for selection of the + * appropriate "flavor". The static calls in cleanup just use the + * revision-agnostic form, as they are not performance critical. + */ +static void ipath_pe_put_tid_2(struct ipath_devdata *dd, u64 __iomem *tidptr, + u32 type, unsigned long pa) +{ + u32 __iomem *tidp32 = (u32 __iomem *)tidptr; + + if (pa != dd->ipath_tidinvalid) { + if (pa & ((1U << 11) - 1)) { + dev_info(&dd->pcidev->dev, "BUG: physaddr %lx " + "not 2KB aligned!\n", pa); + return; + } + pa >>= 11; + /* paranoia check */ + if (pa & (7<<29)) + ipath_dev_err(dd, + "BUG: Physical page address 0x%lx " + "has bits set in 31-29\n", pa); + + if (type == 0) + pa |= dd->ipath_tidtemplate; + else /* for now, always full 4KB page */ + pa |= 2 << 29; + } + if (dd->ipath_kregbase) + writel(pa, tidp32); + mmiowb(); +} + /** * ipath_pe_clear_tid - clear all TID entries for a port, expected and eager @@ -1203,7 +1288,7 @@ int __attribute__((weak)) ipath_unordered_wc(void) /** * ipath_init_pe_get_base_info - set chip-specific flags for user code - * @dd: the infinipath device + * @pd: the infinipath port * @kbase: ipath_base_info pointer * * We set the PCIE flag because the lower bandwidth on PCIe vs @@ -1212,6 +1297,7 @@ int __attribute__((weak)) ipath_unordered_wc(void) static int ipath_pe_get_base_info(struct ipath_portdata *pd, void *kbase) { struct ipath_base_info *kinfo = kbase; + struct ipath_devdata *dd; if (ipath_unordered_wc()) { kinfo->spi_runtime_flags |= IPATH_RUNTIME_FORCE_WC_ORDER; @@ -1220,8 +1306,20 @@ static int ipath_pe_get_base_info(struct ipath_portdata *pd, void *kbase) else ipath_cdbg(PROC, "Not Intel processor, WC ordered\n"); - kinfo->spi_runtime_flags |= IPATH_RUNTIME_PCIE; + if (pd == NULL) + goto done; + dd = pd->port_dd; + + if (dd != NULL && dd->ipath_minrev >= 2) { + ipath_cdbg(PROC, "IBA6120 Rev2, allow multiple PBC write\n"); + kinfo->spi_runtime_flags |= IPATH_RUNTIME_PBC_REWRITE; + ipath_cdbg(PROC, "IBA6120 Rev2, allow loose DMA alignment\n"); + kinfo->spi_runtime_flags |= IPATH_RUNTIME_LOOSE_DMA_ALIGN; + } + +done: + kinfo->spi_runtime_flags |= IPATH_RUNTIME_PCIE; return 0; } @@ -1244,7 +1342,10 @@ void ipath_init_iba6120_funcs(struct ipath_devdata *dd) dd->ipath_f_quiet_serdes = ipath_pe_quiet_serdes; dd->ipath_f_bringup_serdes = ipath_pe_bringup_serdes; dd->ipath_f_clear_tids = ipath_pe_clear_tids; - dd->ipath_f_put_tid = ipath_pe_put_tid; + if (dd->ipath_minrev >= 2) + dd->ipath_f_put_tid = ipath_pe_put_tid_2; + else + dd->ipath_f_put_tid = ipath_pe_put_tid; dd->ipath_f_cleanup = ipath_setup_pe_cleanup; dd->ipath_f_setextled = ipath_setup_pe_setextled; dd->ipath_f_get_base_info = ipath_pe_get_base_info; @@ -1259,6 +1360,6 @@ void ipath_init_iba6120_funcs(struct ipath_devdata *dd) dd->ipath_kregs = &ipath_pe_kregs; dd->ipath_cregs = &ipath_pe_cregs; - ipath_init_pe_variables(); + ipath_init_pe_variables(dd); } diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c index 44669dc2e22..d819cca524c 100644 --- a/drivers/infiniband/hw/ipath/ipath_init_chip.c +++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c @@ -88,13 +88,13 @@ MODULE_PARM_DESC(kpiobufs, "Set number of PIO buffers for driver"); static int create_port0_egr(struct ipath_devdata *dd) { unsigned e, egrcnt; - struct sk_buff **skbs; + struct ipath_skbinfo *skbinfo; int ret; egrcnt = dd->ipath_rcvegrcnt; - skbs = vmalloc(sizeof(*dd->ipath_port0_skbs) * egrcnt); - if (skbs == NULL) { + skbinfo = vmalloc(sizeof(*dd->ipath_port0_skbinfo) * egrcnt); + if (skbinfo == NULL) { ipath_dev_err(dd, "allocation error for eager TID " "skb array\n"); ret = -ENOMEM; @@ -109,13 +109,13 @@ static int create_port0_egr(struct ipath_devdata *dd) * 4 bytes so that the data buffer stays word aligned. * See ipath_kreceive() for more details. */ - skbs[e] = ipath_alloc_skb(dd, GFP_KERNEL); - if (!skbs[e]) { + skbinfo[e].skb = ipath_alloc_skb(dd, GFP_KERNEL); + if (!skbinfo[e].skb) { ipath_dev_err(dd, "SKB allocation error for " "eager TID %u\n", e); while (e != 0) - dev_kfree_skb(skbs[--e]); - vfree(skbs); + dev_kfree_skb(skbinfo[--e].skb); + vfree(skbinfo); ret = -ENOMEM; goto bail; } @@ -124,14 +124,17 @@ static int create_port0_egr(struct ipath_devdata *dd) * After loop above, so we can test non-NULL to see if ready * to use at receive, etc. */ - dd->ipath_port0_skbs = skbs; + dd->ipath_port0_skbinfo = skbinfo; for (e = 0; e < egrcnt; e++) { - unsigned long phys = - virt_to_phys(dd->ipath_port0_skbs[e]->data); + dd->ipath_port0_skbinfo[e].phys = + ipath_map_single(dd->pcidev, + dd->ipath_port0_skbinfo[e].skb->data, + dd->ipath_ibmaxlen, PCI_DMA_FROMDEVICE); dd->ipath_f_put_tid(dd, e + (u64 __iomem *) ((char __iomem *) dd->ipath_kregbase + - dd->ipath_rcvegrbase), 0, phys); + dd->ipath_rcvegrbase), 0, + dd->ipath_port0_skbinfo[e].phys); } ret = 0; @@ -432,16 +435,33 @@ done: */ static void init_shadow_tids(struct ipath_devdata *dd) { - dd->ipath_pageshadow = (struct page **) - vmalloc(dd->ipath_cfgports * dd->ipath_rcvtidcnt * + struct page **pages; + dma_addr_t *addrs; + + pages = vmalloc(dd->ipath_cfgports * dd->ipath_rcvtidcnt * sizeof(struct page *)); - if (!dd->ipath_pageshadow) + if (!pages) { ipath_dev_err(dd, "failed to allocate shadow page * " "array, no expected sends!\n"); - else - memset(dd->ipath_pageshadow, 0, - dd->ipath_cfgports * dd->ipath_rcvtidcnt * - sizeof(struct page *)); + dd->ipath_pageshadow = NULL; + return; + } + + addrs = vmalloc(dd->ipath_cfgports * dd->ipath_rcvtidcnt * + sizeof(dma_addr_t)); + if (!addrs) { + ipath_dev_err(dd, "failed to allocate shadow dma handle " + "array, no expected sends!\n"); + vfree(dd->ipath_pageshadow); + dd->ipath_pageshadow = NULL; + return; + } + + memset(pages, 0, dd->ipath_cfgports * dd->ipath_rcvtidcnt * + sizeof(struct page *)); + + dd->ipath_pageshadow = pages; + dd->ipath_physshadow = addrs; } static void enable_chip(struct ipath_devdata *dd, diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c index 49bf7bb15b0..6bee53ce5f3 100644 --- a/drivers/infiniband/hw/ipath/ipath_intr.c +++ b/drivers/infiniband/hw/ipath/ipath_intr.c @@ -37,6 +37,50 @@ #include "ipath_verbs.h" #include "ipath_common.h" +/* + * Called when we might have an error that is specific to a particular + * PIO buffer, and may need to cancel that buffer, so it can be re-used. + */ +void ipath_disarm_senderrbufs(struct ipath_devdata *dd) +{ + u32 piobcnt; + unsigned long sbuf[4]; + /* + * it's possible that sendbuffererror could have bits set; might + * have already done this as a result of hardware error handling + */ + piobcnt = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k; + /* read these before writing errorclear */ + sbuf[0] = ipath_read_kreg64( + dd, dd->ipath_kregs->kr_sendbuffererror); + sbuf[1] = ipath_read_kreg64( + dd, dd->ipath_kregs->kr_sendbuffererror + 1); + if (piobcnt > 128) { + sbuf[2] = ipath_read_kreg64( + dd, dd->ipath_kregs->kr_sendbuffererror + 2); + sbuf[3] = ipath_read_kreg64( + dd, dd->ipath_kregs->kr_sendbuffererror + 3); + } + + if (sbuf[0] || sbuf[1] || (piobcnt > 128 && (sbuf[2] || sbuf[3]))) { + int i; + if (ipath_debug & (__IPATH_PKTDBG|__IPATH_DBG)) { + __IPATH_DBG_WHICH(__IPATH_PKTDBG|__IPATH_DBG, + "SendbufErrs %lx %lx", sbuf[0], + sbuf[1]); + if (ipath_debug & __IPATH_PKTDBG && piobcnt > 128) + printk(" %lx %lx ", sbuf[2], sbuf[3]); + printk("\n"); + } + + for (i = 0; i < piobcnt; i++) + if (test_bit(i, sbuf)) + ipath_disarm_piobufs(dd, i, 1); + dd->ipath_lastcancel = jiffies+3; /* no armlaunch for a bit */ + } +} + + /* These are all rcv-related errors which we want to count for stats */ #define E_SUM_PKTERRS \ (INFINIPATH_E_RHDRLEN | INFINIPATH_E_RBADTID | \ @@ -68,53 +112,9 @@ static u64 handle_e_sum_errs(struct ipath_devdata *dd, ipath_err_t errs) { - unsigned long sbuf[4]; u64 ignore_this_time = 0; - u32 piobcnt; - /* if possible that sendbuffererror could be valid */ - piobcnt = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k; - /* read these before writing errorclear */ - sbuf[0] = ipath_read_kreg64( - dd, dd->ipath_kregs->kr_sendbuffererror); - sbuf[1] = ipath_read_kreg64( - dd, dd->ipath_kregs->kr_sendbuffererror + 1); - if (piobcnt > 128) { - sbuf[2] = ipath_read_kreg64( - dd, dd->ipath_kregs->kr_sendbuffererror + 2); - sbuf[3] = ipath_read_kreg64( - dd, dd->ipath_kregs->kr_sendbuffererror + 3); - } - - if (sbuf[0] || sbuf[1] || (piobcnt > 128 && (sbuf[2] || sbuf[3]))) { - int i; - - ipath_cdbg(PKT, "SendbufErrs %lx %lx ", sbuf[0], sbuf[1]); - if (ipath_debug & __IPATH_PKTDBG && piobcnt > 128) - printk("%lx %lx ", sbuf[2], sbuf[3]); - for (i = 0; i < piobcnt; i++) { - if (test_bit(i, sbuf)) { - u32 __iomem *piobuf; - if (i < dd->ipath_piobcnt2k) - piobuf = (u32 __iomem *) - (dd->ipath_pio2kbase + - i * dd->ipath_palign); - else - piobuf = (u32 __iomem *) - (dd->ipath_pio4kbase + - (i - dd->ipath_piobcnt2k) * - dd->ipath_4kalign); - - ipath_cdbg(PKT, - "PIObuf[%u] @%p pbc is %x; ", - i, piobuf, readl(piobuf)); - - ipath_disarm_piobufs(dd, i, 1); - } - } - if (ipath_debug & __IPATH_PKTDBG) - printk("\n"); - } + ipath_disarm_senderrbufs(dd); if ((errs & E_SUM_LINK_PKTERRS) && !(dd->ipath_flags & IPATH_LINKACTIVE)) { /* @@ -132,6 +132,82 @@ static u64 handle_e_sum_errs(struct ipath_devdata *dd, ipath_err_t errs) return ignore_this_time; } +/* generic hw error messages... */ +#define INFINIPATH_HWE_TXEMEMPARITYERR_MSG(a) \ + { \ + .mask = ( INFINIPATH_HWE_TXEMEMPARITYERR_##a << \ + INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT ), \ + .msg = "TXE " #a " Memory Parity" \ + } +#define INFINIPATH_HWE_RXEMEMPARITYERR_MSG(a) \ + { \ + .mask = ( INFINIPATH_HWE_RXEMEMPARITYERR_##a << \ + INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT ), \ + .msg = "RXE " #a " Memory Parity" \ + } + +static const struct ipath_hwerror_msgs ipath_generic_hwerror_msgs[] = { + INFINIPATH_HWE_MSG(IBCBUSFRSPCPARITYERR, "IPATH2IB Parity"), + INFINIPATH_HWE_MSG(IBCBUSTOSPCPARITYERR, "IB2IPATH Parity"), + + INFINIPATH_HWE_TXEMEMPARITYERR_MSG(PIOBUF), + INFINIPATH_HWE_TXEMEMPARITYERR_MSG(PIOPBC), + INFINIPATH_HWE_TXEMEMPARITYERR_MSG(PIOLAUNCHFIFO), + + INFINIPATH_HWE_RXEMEMPARITYERR_MSG(RCVBUF), + INFINIPATH_HWE_RXEMEMPARITYERR_MSG(LOOKUPQ), + INFINIPATH_HWE_RXEMEMPARITYERR_MSG(EAGERTID), + INFINIPATH_HWE_RXEMEMPARITYERR_MSG(EXPTID), + INFINIPATH_HWE_RXEMEMPARITYERR_MSG(FLAGBUF), + INFINIPATH_HWE_RXEMEMPARITYERR_MSG(DATAINFO), + INFINIPATH_HWE_RXEMEMPARITYERR_MSG(HDRINFO), +}; + +/** + * ipath_format_hwmsg - format a single hwerror message + * @msg message buffer + * @msgl length of message buffer + * @hwmsg message to add to message buffer + */ +static void ipath_format_hwmsg(char *msg, size_t msgl, const char *hwmsg) +{ + strlcat(msg, "[", msgl); + strlcat(msg, hwmsg, msgl); + strlcat(msg, "]", msgl); +} + +/** + * ipath_format_hwerrors - format hardware error messages for display + * @hwerrs hardware errors bit vector + * @hwerrmsgs hardware error descriptions + * @nhwerrmsgs number of hwerrmsgs + * @msg message buffer + * @msgl message buffer length + */ +void ipath_format_hwerrors(u64 hwerrs, + const struct ipath_hwerror_msgs *hwerrmsgs, + size_t nhwerrmsgs, + char *msg, size_t msgl) +{ + int i; + const int glen = + sizeof(ipath_generic_hwerror_msgs) / + sizeof(ipath_generic_hwerror_msgs[0]); + + for (i=0; i<glen; i++) { + if (hwerrs & ipath_generic_hwerror_msgs[i].mask) { + ipath_format_hwmsg(msg, msgl, + ipath_generic_hwerror_msgs[i].msg); + } + } + + for (i=0; i<nhwerrmsgs; i++) { + if (hwerrs & hwerrmsgs[i].mask) { + ipath_format_hwmsg(msg, msgl, hwerrmsgs[i].msg); + } + } +} + /* return the strings for the most common link states */ static char *ib_linkstate(u32 linkstate) { @@ -404,10 +480,10 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs) dd->ipath_f_handle_hwerrors(dd, msg, sizeof msg); } - if (!noprint && (errs & ~infinipath_e_bitsextant)) + if (!noprint && (errs & ~dd->ipath_e_bitsextant)) ipath_dev_err(dd, "error interrupt with unknown errors " "%llx set\n", (unsigned long long) - (errs & ~infinipath_e_bitsextant)); + (errs & ~dd->ipath_e_bitsextant)); if (errs & E_SUM_ERRS) ignore_this_time = handle_e_sum_errs(dd, errs); @@ -478,6 +554,14 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs) ~(INFINIPATH_E_HARDWARE | INFINIPATH_E_IBSTATUSCHANGED); } + + /* likely due to cancel, so suppress */ + if ((errs & (INFINIPATH_E_SPKTLEN | INFINIPATH_E_SPIOARMLAUNCH)) && + dd->ipath_lastcancel > jiffies) { + ipath_dbg("Suppressed armlaunch/spktlen after error send cancel\n"); + errs &= ~(INFINIPATH_E_SPIOARMLAUNCH | INFINIPATH_E_SPKTLEN); + } + if (!errs) return 0; @@ -529,7 +613,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs) * don't report same point multiple times, * except kernel */ - tl = (u32) * pd->port_rcvhdrtail_kvaddr; + tl = *(u64 *) pd->port_rcvhdrtail_kvaddr; if (tl == dd->ipath_lastrcvhdrqtails[i]) continue; hd = ipath_read_ureg32(dd, ur_rcvhdrhead, @@ -729,9 +813,9 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat) int rcvdint = 0; portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) & - infinipath_i_rcvavail_mask) + dd->ipath_i_rcvavail_mask) | ((istat >> INFINIPATH_I_RCVURG_SHIFT) & - infinipath_i_rcvurg_mask); + dd->ipath_i_rcvurg_mask); for (i = 1; i < dd->ipath_cfgports; i++) { struct ipath_portdata *pd = dd->ipath_pd[i]; if (portr & (1 << i) && pd && pd->port_cnt && @@ -808,7 +892,7 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs) if (oldhead != curtail) { if (dd->ipath_flags & IPATH_GPIO_INTR) { ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear, - (u64) (1 << 2)); + (u64) (1 << IPATH_GPIO_PORT0_BIT)); istat = port0rbits | INFINIPATH_I_GPIO; } else @@ -838,10 +922,10 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs) if (unexpected) unexpected = 0; - if (unlikely(istat & ~infinipath_i_bitsextant)) + if (unlikely(istat & ~dd->ipath_i_bitsextant)) ipath_dev_err(dd, "interrupt with unknown interrupts %x set\n", - istat & (u32) ~ infinipath_i_bitsextant); + istat & (u32) ~ dd->ipath_i_bitsextant); else ipath_cdbg(VERBOSE, "intr stat=0x%x\n", istat); @@ -867,26 +951,80 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs) if (istat & INFINIPATH_I_GPIO) { /* - * Packets are available in the port 0 rcv queue. - * Eventually this needs to be generalized to check - * IPATH_GPIO_INTR, and the specific GPIO bit, if - * GPIO interrupts are used for anything else. + * GPIO interrupts fall in two broad classes: + * GPIO_2 indicates (on some HT4xx boards) that a packet + * has arrived for Port 0. Checking for this + * is controlled by flag IPATH_GPIO_INTR. + * GPIO_3..5 on IBA6120 Rev2 chips indicate errors + * that we need to count. Checking for this + * is controlled by flag IPATH_GPIO_ERRINTRS. */ - if (unlikely(!(dd->ipath_flags & IPATH_GPIO_INTR))) { - u32 gpiostatus; - gpiostatus = ipath_read_kreg32( - dd, dd->ipath_kregs->kr_gpio_status); - ipath_dbg("Unexpected GPIO interrupt bits %x\n", - gpiostatus); - ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear, - gpiostatus); + u32 gpiostatus; + u32 to_clear = 0; + + gpiostatus = ipath_read_kreg32( + dd, dd->ipath_kregs->kr_gpio_status); + /* First the error-counter case. + */ + if ((gpiostatus & IPATH_GPIO_ERRINTR_MASK) && + (dd->ipath_flags & IPATH_GPIO_ERRINTRS)) { + /* want to clear the bits we see asserted. */ + to_clear |= (gpiostatus & IPATH_GPIO_ERRINTR_MASK); + + /* + * Count appropriately, clear bits out of our copy, + * as they have been "handled". + */ + if (gpiostatus & (1 << IPATH_GPIO_RXUVL_BIT)) { + ipath_dbg("FlowCtl on UnsupVL\n"); + dd->ipath_rxfc_unsupvl_errs++; + } + if (gpiostatus & (1 << IPATH_GPIO_OVRUN_BIT)) { + ipath_dbg("Overrun Threshold exceeded\n"); + dd->ipath_overrun_thresh_errs++; + } + if (gpiostatus & (1 << IPATH_GPIO_LLI_BIT)) { + ipath_dbg("Local Link Integrity error\n"); + dd->ipath_lli_errs++; + } + gpiostatus &= ~IPATH_GPIO_ERRINTR_MASK; } - else { - /* Clear GPIO status bit 2 */ - ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear, - (u64) (1 << 2)); + /* Now the Port0 Receive case */ + if ((gpiostatus & (1 << IPATH_GPIO_PORT0_BIT)) && + (dd->ipath_flags & IPATH_GPIO_INTR)) { + /* + * GPIO status bit 2 is set, and we expected it. + * clear it and indicate in p0bits. + * This probably only happens if a Port0 pkt + * arrives at _just_ the wrong time, and we + * handle that by seting chk0rcv; + */ + to_clear |= (1 << IPATH_GPIO_PORT0_BIT); + gpiostatus &= ~(1 << IPATH_GPIO_PORT0_BIT); chk0rcv = 1; } + if (unlikely(gpiostatus)) { + /* + * Some unexpected bits remain. If they could have + * caused the interrupt, complain and clear. + * MEA: this is almost certainly non-ideal. + * we should look into auto-disable of unexpected + * GPIO interrupts, possibly on a "three strikes" + * basis. + */ + u32 mask; + mask = ipath_read_kreg32( + dd, dd->ipath_kregs->kr_gpio_mask); + if (mask & gpiostatus) { + ipath_dbg("Unexpected GPIO IRQ bits %x\n", + gpiostatus & mask); + to_clear |= (gpiostatus & mask); + } + } + if (to_clear) { + ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear, + (u64) to_clear); + } } chk0rcv |= istat & port0rbits; @@ -911,9 +1049,9 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs) istat &= ~port0rbits; } - if (istat & ((infinipath_i_rcvavail_mask << + if (istat & ((dd->ipath_i_rcvavail_mask << INFINIPATH_I_RCVAVAIL_SHIFT) - | (infinipath_i_rcvurg_mask << + | (dd->ipath_i_rcvurg_mask << INFINIPATH_I_RCVURG_SHIFT))) handle_urcv(dd, istat); diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h index a8a56276ff1..d7540b71b45 100644 --- a/drivers/infiniband/hw/ipath/ipath_kernel.h +++ b/drivers/infiniband/hw/ipath/ipath_kernel.h @@ -39,6 +39,8 @@ */ #include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> #include <asm/io.h> #include "ipath_common.h" @@ -62,7 +64,7 @@ struct ipath_portdata { /* rcvhdrq base, needs mmap before useful */ void *port_rcvhdrq; /* kernel virtual address where hdrqtail is updated */ - volatile __le64 *port_rcvhdrtail_kvaddr; + void *port_rcvhdrtail_kvaddr; /* * temp buffer for expected send setup, allocated at open, instead * of each setup call @@ -79,8 +81,8 @@ struct ipath_portdata { dma_addr_t port_rcvhdrq_phys; dma_addr_t port_rcvhdrqtailaddr_phys; /* - * number of opens on this instance (0 or 1; ignoring forks, dup, - * etc. for now) + * number of opens (including slave subports) on this instance + * (ignoring forks, dup, etc. for now) */ int port_cnt; /* @@ -89,6 +91,10 @@ struct ipath_portdata { */ /* instead of calculating it */ unsigned port_port; + /* non-zero if port is being shared. */ + u16 port_subport_cnt; + /* non-zero if port is being shared. */ + u16 port_subport_id; /* chip offset of PIO buffers for this port */ u32 port_piobufs; /* how many alloc_pages() chunks in port_rcvegrbuf_pages */ @@ -121,6 +127,16 @@ struct ipath_portdata { u16 port_pkeys[4]; /* so file ops can get at unit */ struct ipath_devdata *port_dd; + /* A page of memory for rcvhdrhead, rcvegrhead, rcvegrtail * N */ + void *subport_uregbase; + /* An array of pages for the eager receive buffers * N */ + void *subport_rcvegrbuf; + /* An array of pages for the eager header queue entries * N */ + void *subport_rcvhdr_base; + /* The version of the library which opened this port */ + u32 userversion; + /* Bitmask of active slaves */ + u32 active_slaves; }; struct sk_buff; @@ -132,6 +148,11 @@ struct _ipath_layer { void *l_arg; }; +struct ipath_skbinfo { + struct sk_buff *skb; + dma_addr_t phys; +}; + struct ipath_devdata { struct list_head ipath_list; @@ -154,7 +175,7 @@ struct ipath_devdata { /* ipath_cfgports pointers */ struct ipath_portdata **ipath_pd; /* sk_buffs used by port 0 eager receive queue */ - struct sk_buff **ipath_port0_skbs; + struct ipath_skbinfo *ipath_port0_skbinfo; /* kvirt address of 1st 2k pio buffer */ void __iomem *ipath_pio2kbase; /* kvirt address of 1st 4k pio buffer */ @@ -315,12 +336,16 @@ struct ipath_devdata { u8 ipath_ht_slave_off; /* for write combining settings */ unsigned long ipath_wc_cookie; + unsigned long ipath_wc_base; + unsigned long ipath_wc_len; /* ref count for each pkey */ atomic_t ipath_pkeyrefs[4]; /* shadow copy of all exptids physaddr; used only by funcsim */ u64 *ipath_tidsimshadow; /* shadow copy of struct page *'s for exp tid pages */ struct page **ipath_pageshadow; + /* shadow copy of dma handles for exp tid pages */ + dma_addr_t *ipath_physshadow; /* lock to workaround chip bug 9437 */ spinlock_t ipath_tid_lock; @@ -402,6 +427,9 @@ struct ipath_devdata { unsigned long ipath_rcvctrl; /* shadow kr_sendctrl */ unsigned long ipath_sendctrl; + /* ports waiting for PIOavail intr */ + unsigned long ipath_portpiowait; + unsigned long ipath_lastcancel; /* to not count armlaunch after cancel */ /* value we put in kr_rcvhdrcnt */ u32 ipath_rcvhdrcnt; @@ -465,8 +493,6 @@ struct ipath_devdata { u32 ipath_htwidth; /* HT speed (200,400,800,1000) from HT config */ u32 ipath_htspeed; - /* ports waiting for PIOavail intr */ - unsigned long ipath_portpiowait; /* * number of sequential ibcstatus change for polling active/quiet * (i.e., link not coming up). @@ -510,8 +536,47 @@ struct ipath_devdata { u32 ipath_lli_counter; /* local link integrity errors */ u32 ipath_lli_errors; + /* + * Above counts only cases where _successive_ LocalLinkIntegrity + * errors were seen in the receive headers of kern-packets. + * Below are the three (monotonically increasing) counters + * maintained via GPIO interrupts on iba6120-rev2. + */ + u32 ipath_rxfc_unsupvl_errs; + u32 ipath_overrun_thresh_errs; + u32 ipath_lli_errs; + + /* + * Not all devices managed by a driver instance are the same + * type, so these fields must be per-device. + */ + u64 ipath_i_bitsextant; + ipath_err_t ipath_e_bitsextant; + ipath_err_t ipath_hwe_bitsextant; + + /* + * Below should be computable from number of ports, + * since they are never modified. + */ + u32 ipath_i_rcvavail_mask; + u32 ipath_i_rcvurg_mask; + + /* + * Register bits for selecting i2c direction and values, used for + * I2C serial flash. + */ + u16 ipath_gpio_sda_num; + u16 ipath_gpio_scl_num; + u64 ipath_gpio_sda; + u64 ipath_gpio_scl; }; +/* Private data for file operations */ +struct ipath_filedata { + struct ipath_portdata *pd; + unsigned subport; + unsigned tidcursor; +}; extern struct list_head ipath_dev_list; extern spinlock_t ipath_devs_lock; extern struct ipath_devdata *ipath_lookup(int unit); @@ -521,6 +586,7 @@ int ipath_enable_wc(struct ipath_devdata *dd); void ipath_disable_wc(struct ipath_devdata *dd); int ipath_count_units(int *npresentp, int *nupp, u32 *maxportsp); void ipath_shutdown_device(struct ipath_devdata *); +void ipath_disarm_senderrbufs(struct ipath_devdata *); struct file_operations; int ipath_cdev_init(int minor, char *name, struct file_operations *fops, @@ -572,7 +638,11 @@ int ipath_set_lid(struct ipath_devdata *, u32, u8); int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv); /* for use in system calls, where we want to know device type, etc. */ -#define port_fp(fp) ((struct ipath_portdata *) (fp)->private_data) +#define port_fp(fp) ((struct ipath_filedata *)(fp)->private_data)->pd +#define subport_fp(fp) \ + ((struct ipath_filedata *)(fp)->private_data)->subport +#define tidcursor_fp(fp) \ + ((struct ipath_filedata *)(fp)->private_data)->tidcursor /* * values for ipath_flags @@ -612,6 +682,15 @@ int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv); /* can miss port0 rx interrupts */ #define IPATH_POLL_RX_INTR 0x40000 #define IPATH_DISABLED 0x80000 /* administratively disabled */ + /* Use GPIO interrupts for new counters */ +#define IPATH_GPIO_ERRINTRS 0x100000 + +/* Bits in GPIO for the added interrupts */ +#define IPATH_GPIO_PORT0_BIT 2 +#define IPATH_GPIO_RXUVL_BIT 3 +#define IPATH_GPIO_OVRUN_BIT 4 +#define IPATH_GPIO_LLI_BIT 5 +#define IPATH_GPIO_ERRINTR_MASK 0x38 /* portdata flag bit offsets */ /* waiting for a packet to arrive */ @@ -799,6 +878,13 @@ int ipathfs_add_device(struct ipath_devdata *); int ipathfs_remove_device(struct ipath_devdata *); /* + * dma_addr wrappers - all 0's invalid for hw + */ +dma_addr_t ipath_map_page(struct pci_dev *, struct page *, unsigned long, + size_t, int); +dma_addr_t ipath_map_single(struct pci_dev *, void *, size_t, int); + +/* * Flush write combining store buffers (if present) and perform a write * barrier. */ @@ -855,4 +941,20 @@ extern struct mutex ipath_mutex; #endif /* _IPATH_DEBUGGING */ +/* + * this is used for formatting hw error messages... + */ +struct ipath_hwerror_msgs { + u64 mask; + const char *msg; +}; + +#define INFINIPATH_HWE_MSG(a, b) { .mask = INFINIPATH_HWE_##a, .msg = b } + +/* in ipath_intr.c... */ +void ipath_format_hwerrors(u64 hwerrs, + const struct ipath_hwerror_msgs *hwerrmsgs, + size_t nhwerrmsgs, + char *msg, size_t lmsg); + #endif /* _IPATH_KERNEL_H */ diff --git a/drivers/infiniband/hw/ipath/ipath_keys.c b/drivers/infiniband/hw/ipath/ipath_keys.c index ba1b93226ca..9a6cbd05adc 100644 --- a/drivers/infiniband/hw/ipath/ipath_keys.c +++ b/drivers/infiniband/hw/ipath/ipath_keys.c @@ -118,9 +118,10 @@ void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey) * Check the IB SGE for validity and initialize our internal version * of it. */ -int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge, +int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge, struct ib_sge *sge, int acc) { + struct ipath_lkey_table *rkt = &to_idev(qp->ibqp.device)->lk_table; struct ipath_mregion *mr; unsigned n, m; size_t off; @@ -140,7 +141,8 @@ int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge, goto bail; } mr = rkt->table[(sge->lkey >> (32 - ib_ipath_lkey_table_size))]; - if (unlikely(mr == NULL || mr->lkey != sge->lkey)) { + if (unlikely(mr == NULL || mr->lkey != sge->lkey || + qp->ibqp.pd != mr->pd)) { ret = 0; goto bail; } @@ -188,9 +190,10 @@ bail: * * Return 1 if successful, otherwise 0. */ -int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss, +int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss, u32 len, u64 vaddr, u32 rkey, int acc) { + struct ipath_ibdev *dev = to_idev(qp->ibqp.device); struct ipath_lkey_table *rkt = &dev->lk_table; struct ipath_sge *sge = &ss->sge; struct ipath_mregion *mr; @@ -214,7 +217,8 @@ int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss, } mr = rkt->table[(rkey >> (32 - ib_ipath_lkey_table_size))]; - if (unlikely(mr == NULL || mr->lkey != rkey)) { + if (unlikely(mr == NULL || mr->lkey != rkey || + qp->ibqp.pd != mr->pd)) { ret = 0; goto bail; } diff --git a/drivers/infiniband/hw/ipath/ipath_mad.c b/drivers/infiniband/hw/ipath/ipath_mad.c index 72d1db89db8..25908b02fbe 100644 --- a/drivers/infiniband/hw/ipath/ipath_mad.c +++ b/drivers/infiniband/hw/ipath/ipath_mad.c @@ -87,7 +87,8 @@ static int recv_subn_get_nodeinfo(struct ib_smp *smp, struct ipath_devdata *dd = to_idev(ibdev)->dd; u32 vendor, majrev, minrev; - if (smp->attr_mod) + /* GUID 0 is illegal */ + if (smp->attr_mod || (dd->ipath_guid == 0)) smp->status |= IB_SMP_INVALID_FIELD; nip->base_version = 1; @@ -131,10 +132,15 @@ static int recv_subn_get_guidinfo(struct ib_smp *smp, * We only support one GUID for now. If this changes, the * portinfo.guid_cap field needs to be updated too. */ - if (startgx == 0) - /* The first is a copy of the read-only HW GUID. */ - *p = to_idev(ibdev)->dd->ipath_guid; - else + if (startgx == 0) { + __be64 g = to_idev(ibdev)->dd->ipath_guid; + if (g == 0) + /* GUID 0 is illegal */ + smp->status |= IB_SMP_INVALID_FIELD; + else + /* The first is a copy of the read-only HW GUID. */ + *p = g; + } else smp->status |= IB_SMP_INVALID_FIELD; return reply(smp); diff --git a/drivers/infiniband/hw/ipath/ipath_mr.c b/drivers/infiniband/hw/ipath/ipath_mr.c index b36f6fb3e37..a0673c1eef7 100644 --- a/drivers/infiniband/hw/ipath/ipath_mr.c +++ b/drivers/infiniband/hw/ipath/ipath_mr.c @@ -138,6 +138,7 @@ struct ib_mr *ipath_reg_phys_mr(struct ib_pd *pd, goto bail; } + mr->mr.pd = pd; mr->mr.user_base = *iova_start; mr->mr.iova = *iova_start; mr->mr.length = 0; @@ -197,6 +198,7 @@ struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, goto bail; } + mr->mr.pd = pd; mr->mr.user_base = region->user_base; mr->mr.iova = region->virt_base; mr->mr.length = region->length; @@ -289,6 +291,7 @@ struct ib_fmr *ipath_alloc_fmr(struct ib_pd *pd, int mr_access_flags, * Resources are allocated but no valid mapping (RKEY can't be * used). */ + fmr->mr.pd = pd; fmr->mr.user_base = 0; fmr->mr.iova = 0; fmr->mr.length = 0; diff --git a/drivers/infiniband/hw/ipath/ipath_qp.c b/drivers/infiniband/hw/ipath/ipath_qp.c index 224b0f40767..46c1c89bf6a 100644 --- a/drivers/infiniband/hw/ipath/ipath_qp.c +++ b/drivers/infiniband/hw/ipath/ipath_qp.c @@ -335,6 +335,7 @@ static void ipath_reset_qp(struct ipath_qp *qp) qp->s_ack_state = IB_OPCODE_RC_ACKNOWLEDGE; qp->r_ack_state = IB_OPCODE_RC_ACKNOWLEDGE; qp->r_nak_state = 0; + qp->r_wrid_valid = 0; qp->s_rnr_timeout = 0; qp->s_head = 0; qp->s_tail = 0; @@ -342,6 +343,7 @@ static void ipath_reset_qp(struct ipath_qp *qp) qp->s_last = 0; qp->s_ssn = 1; qp->s_lsn = 0; + qp->s_wait_credit = 0; if (qp->r_rq.wq) { qp->r_rq.wq->head = 0; qp->r_rq.wq->tail = 0; @@ -352,12 +354,13 @@ static void ipath_reset_qp(struct ipath_qp *qp) /** * ipath_error_qp - put a QP into an error state * @qp: the QP to put into an error state + * @err: the receive completion error to signal if a RWQE is active * * Flushes both send and receive work queues. * QP s_lock should be held and interrupts disabled. */ -void ipath_error_qp(struct ipath_qp *qp) +void ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err) { struct ipath_ibdev *dev = to_idev(qp->ibqp.device); struct ib_wc wc; @@ -373,7 +376,6 @@ void ipath_error_qp(struct ipath_qp *qp) list_del_init(&qp->piowait); spin_unlock(&dev->pending_lock); - wc.status = IB_WC_WR_FLUSH_ERR; wc.vendor_err = 0; wc.byte_len = 0; wc.imm_data = 0; @@ -385,6 +387,12 @@ void ipath_error_qp(struct ipath_qp *qp) wc.sl = 0; wc.dlid_path_bits = 0; wc.port_num = 0; + if (qp->r_wrid_valid) { + qp->r_wrid_valid = 0; + wc.status = err; + ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 1); + } + wc.status = IB_WC_WR_FLUSH_ERR; while (qp->s_last != qp->s_head) { struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last); @@ -501,7 +509,7 @@ int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, break; case IB_QPS_ERR: - ipath_error_qp(qp); + ipath_error_qp(qp, IB_WC_GENERAL_ERR); break; default: @@ -516,7 +524,7 @@ int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, qp->remote_qpn = attr->dest_qp_num; if (attr_mask & IB_QP_SQ_PSN) { - qp->s_next_psn = attr->sq_psn; + qp->s_psn = qp->s_next_psn = attr->sq_psn; qp->s_last_psn = qp->s_next_psn - 1; } diff --git a/drivers/infiniband/hw/ipath/ipath_rc.c b/drivers/infiniband/hw/ipath/ipath_rc.c index a08654042c0..a504cf67f27 100644 --- a/drivers/infiniband/hw/ipath/ipath_rc.c +++ b/drivers/infiniband/hw/ipath/ipath_rc.c @@ -201,6 +201,18 @@ int ipath_make_rc_req(struct ipath_qp *qp, qp->s_rnr_timeout) goto done; + /* Limit the number of packets sent without an ACK. */ + if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT) > 0) { + qp->s_wait_credit = 1; + dev->n_rc_stalls++; + spin_lock(&dev->pending_lock); + if (list_empty(&qp->timerwait)) + list_add_tail(&qp->timerwait, + &dev->pending[dev->pending_index]); + spin_unlock(&dev->pending_lock); + goto done; + } + /* header size in 32-bit words LRH+BTH = (8+12)/4. */ hwords = 5; bth0 = 0; @@ -221,7 +233,7 @@ int ipath_make_rc_req(struct ipath_qp *qp, /* Check if send work queue is empty. */ if (qp->s_tail == qp->s_head) goto done; - qp->s_psn = wqe->psn = qp->s_next_psn; + wqe->psn = qp->s_next_psn; newreq = 1; } /* @@ -393,12 +405,6 @@ int ipath_make_rc_req(struct ipath_qp *qp, ss = &qp->s_sge; len = qp->s_len; if (len > pmtu) { - /* - * Request an ACK every 1/2 MB to avoid retransmit - * timeouts. - */ - if (((wqe->length - len) % (512 * 1024)) == 0) - bth2 |= 1 << 31; len = pmtu; break; } @@ -435,12 +441,6 @@ int ipath_make_rc_req(struct ipath_qp *qp, ss = &qp->s_sge; len = qp->s_len; if (len > pmtu) { - /* - * Request an ACK every 1/2 MB to avoid retransmit - * timeouts. - */ - if (((wqe->length - len) % (512 * 1024)) == 0) - bth2 |= 1 << 31; len = pmtu; break; } @@ -498,6 +498,8 @@ int ipath_make_rc_req(struct ipath_qp *qp, */ goto done; } + if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT - 1) >= 0) + bth2 |= 1 << 31; /* Request ACK. */ qp->s_len -= len; qp->s_hdrwords = hwords; qp->s_cur_sge = ss; @@ -737,6 +739,15 @@ bail: return; } +static inline void update_last_psn(struct ipath_qp *qp, u32 psn) +{ + if (qp->s_wait_credit) { + qp->s_wait_credit = 0; + tasklet_hi_schedule(&qp->s_task); + } + qp->s_last_psn = psn; +} + /** * do_rc_ack - process an incoming RC ACK * @qp: the QP the ACK came in on @@ -805,7 +816,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode) * The last valid PSN seen is the previous * request's. */ - qp->s_last_psn = wqe->psn - 1; + update_last_psn(qp, wqe->psn - 1); /* Retry this request. */ ipath_restart_rc(qp, wqe->psn, &wc); /* @@ -864,7 +875,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode) ipath_get_credit(qp, aeth); qp->s_rnr_retry = qp->s_rnr_retry_cnt; qp->s_retry = qp->s_retry_cnt; - qp->s_last_psn = psn; + update_last_psn(qp, psn); ret = 1; goto bail; @@ -883,7 +894,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode) goto bail; /* The last valid PSN is the previous PSN. */ - qp->s_last_psn = psn - 1; + update_last_psn(qp, psn - 1); dev->n_rc_resends += (int)qp->s_psn - (int)psn; @@ -898,7 +909,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode) case 3: /* NAK */ /* The last valid PSN seen is the previous request's. */ if (qp->s_last != qp->s_tail) - qp->s_last_psn = wqe->psn - 1; + update_last_psn(qp, wqe->psn - 1); switch ((aeth >> IPATH_AETH_CREDIT_SHIFT) & IPATH_AETH_CREDIT_MASK) { case 0: /* PSN sequence error */ @@ -1071,7 +1082,7 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev, * since we don't want s_sge modified. */ qp->s_len -= pmtu; - qp->s_last_psn = psn; + update_last_psn(qp, psn); spin_unlock_irqrestore(&qp->s_lock, flags); ipath_copy_sge(&qp->s_sge, data, pmtu); goto bail; @@ -1223,7 +1234,7 @@ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev, * Address range must be a subset of the original * request and start on pmtu boundaries. */ - ok = ipath_rkey_ok(dev, &qp->s_rdma_sge, + ok = ipath_rkey_ok(qp, &qp->s_rdma_sge, qp->s_rdma_len, vaddr, rkey, IB_ACCESS_REMOTE_READ); if (unlikely(!ok)) { @@ -1282,6 +1293,14 @@ done: return 1; } +static void ipath_rc_error(struct ipath_qp *qp, enum ib_wc_status err) +{ + spin_lock_irq(&qp->s_lock); + qp->state = IB_QPS_ERR; + ipath_error_qp(qp, err); + spin_unlock_irq(&qp->s_lock); +} + /** * ipath_rc_rcv - process an incoming RC packet * @dev: the device this packet came in on @@ -1309,6 +1328,10 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, struct ib_reth *reth; int header_in_data; + /* Validate the SLID. See Ch. 9.6.1.5 */ + if (unlikely(be16_to_cpu(hdr->lrh[3]) != qp->remote_ah_attr.dlid)) + goto done; + /* Check for GRH */ if (!has_grh) { ohdr = &hdr->u.oth; @@ -1370,8 +1393,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, */ if (qp->r_ack_state >= OP(COMPARE_SWAP)) goto send_ack; - /* XXX Flush WQEs */ - qp->state = IB_QPS_ERR; + ipath_rc_error(qp, IB_WC_REM_INV_REQ_ERR); qp->r_ack_state = OP(SEND_ONLY); qp->r_nak_state = IB_NAK_INVALID_REQUEST; qp->r_ack_psn = qp->r_psn; @@ -1477,9 +1499,9 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, goto nack_inv; ipath_copy_sge(&qp->r_sge, data, tlen); qp->r_msn++; - if (opcode == OP(RDMA_WRITE_LAST) || - opcode == OP(RDMA_WRITE_ONLY)) + if (!qp->r_wrid_valid) break; + qp->r_wrid_valid = 0; wc.wr_id = qp->r_wr_id; wc.status = IB_WC_SUCCESS; wc.opcode = IB_WC_RECV; @@ -1517,7 +1539,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, int ok; /* Check rkey & NAK */ - ok = ipath_rkey_ok(dev, &qp->r_sge, + ok = ipath_rkey_ok(qp, &qp->r_sge, qp->r_len, vaddr, rkey, IB_ACCESS_REMOTE_WRITE); if (unlikely(!ok)) @@ -1559,7 +1581,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, int ok; /* Check rkey & NAK */ - ok = ipath_rkey_ok(dev, &qp->s_rdma_sge, + ok = ipath_rkey_ok(qp, &qp->s_rdma_sge, qp->s_rdma_len, vaddr, rkey, IB_ACCESS_REMOTE_READ); if (unlikely(!ok)) { @@ -1618,7 +1640,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, goto nack_inv; rkey = be32_to_cpu(ateth->rkey); /* Check rkey & NAK */ - if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge, + if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, sizeof(u64), vaddr, rkey, IB_ACCESS_REMOTE_ATOMIC))) goto nack_acc; @@ -1670,8 +1692,7 @@ nack_acc: * is pending though. */ if (qp->r_ack_state < OP(COMPARE_SWAP)) { - /* XXX Flush WQEs */ - qp->state = IB_QPS_ERR; + ipath_rc_error(qp, IB_WC_REM_ACCESS_ERR); qp->r_ack_state = OP(RDMA_WRITE_ONLY); qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR; qp->r_ack_psn = qp->r_psn; diff --git a/drivers/infiniband/hw/ipath/ipath_registers.h b/drivers/infiniband/hw/ipath/ipath_registers.h index 6e23b3d632b..dffc76016d3 100644 --- a/drivers/infiniband/hw/ipath/ipath_registers.h +++ b/drivers/infiniband/hw/ipath/ipath_registers.h @@ -134,10 +134,24 @@ #define INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT 40 #define INFINIPATH_HWE_RXEMEMPARITYERR_MASK 0x7FULL #define INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT 44 -#define INFINIPATH_HWE_RXDSYNCMEMPARITYERR 0x0000000400000000ULL -#define INFINIPATH_HWE_MEMBISTFAILED 0x0040000000000000ULL #define INFINIPATH_HWE_IBCBUSTOSPCPARITYERR 0x4000000000000000ULL #define INFINIPATH_HWE_IBCBUSFRSPCPARITYERR 0x8000000000000000ULL +/* txe mem parity errors (shift by INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) */ +#define INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF 0x1ULL +#define INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC 0x2ULL +#define INFINIPATH_HWE_TXEMEMPARITYERR_PIOLAUNCHFIFO 0x4ULL +/* rxe mem parity errors (shift by INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) */ +#define INFINIPATH_HWE_RXEMEMPARITYERR_RCVBUF 0x01ULL +#define INFINIPATH_HWE_RXEMEMPARITYERR_LOOKUPQ 0x02ULL +#define INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID 0x04ULL +#define INFINIPATH_HWE_RXEMEMPARITYERR_EXPTID 0x08ULL +#define INFINIPATH_HWE_RXEMEMPARITYERR_FLAGBUF 0x10ULL +#define INFINIPATH_HWE_RXEMEMPARITYERR_DATAINFO 0x20ULL +#define INFINIPATH_HWE_RXEMEMPARITYERR_HDRINFO 0x40ULL +/* waldo specific -- find the rest in ipath_6110.c */ +#define INFINIPATH_HWE_RXDSYNCMEMPARITYERR 0x0000000400000000ULL +/* monty specific -- find the rest in ipath_6120.c */ +#define INFINIPATH_HWE_MEMBISTFAILED 0x0040000000000000ULL /* kr_hwdiagctrl bits */ #define INFINIPATH_DC_FORCETXEMEMPARITYERR_MASK 0xFULL @@ -209,9 +223,9 @@ /* combination link status states that we use with some frequency */ #define IPATH_IBSTATE_MASK ((INFINIPATH_IBCS_LINKTRAININGSTATE_MASK \ - << INFINIPATH_IBCS_LINKSTATE_SHIFT) | \ + << INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) | \ (INFINIPATH_IBCS_LINKSTATE_MASK \ - <<INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT)) + <<INFINIPATH_IBCS_LINKSTATE_SHIFT)) #define IPATH_IBSTATE_INIT ((INFINIPATH_IBCS_L_STATE_INIT \ << INFINIPATH_IBCS_LINKSTATE_SHIFT) | \ (INFINIPATH_IBCS_LT_STATE_LINKUP \ @@ -302,6 +316,17 @@ typedef u64 ipath_err_t; +/* The following change with the type of device, so + * need to be part of the ipath_devdata struct, or + * we could have problems plugging in devices of + * different types (e.g. one HT, one PCIE) + * in one system, to be managed by one driver. + * On the other hand, this file is may also be included + * by other code, so leave the declarations here + * temporarily. Minor footprint issue if common-model + * linker used, none if C89+ linker used. + */ + /* mask of defined bits for various registers */ extern u64 infinipath_i_bitsextant; extern ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant; @@ -310,13 +335,6 @@ extern ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant; extern u32 infinipath_i_rcvavail_mask, infinipath_i_rcvurg_mask; /* - * register bits for selecting i2c direction and values, used for I2C serial - * flash - */ -extern u16 ipath_gpio_sda_num, ipath_gpio_scl_num; -extern u64 ipath_gpio_sda, ipath_gpio_scl; - -/* * These are the infinipath general register numbers (not offsets). * The kernel registers are used directly, those beyond the kernel * registers are calculated from one of the base registers. The use of diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c index 5c1da2d25e0..f7530512045 100644 --- a/drivers/infiniband/hw/ipath/ipath_ruc.c +++ b/drivers/infiniband/hw/ipath/ipath_ruc.c @@ -108,7 +108,6 @@ void ipath_insert_rnr_queue(struct ipath_qp *qp) static int init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe) { - struct ipath_ibdev *dev = to_idev(qp->ibqp.device); int user = to_ipd(qp->ibqp.pd)->user; int i, j, ret; struct ib_wc wc; @@ -119,8 +118,7 @@ static int init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe) continue; /* Check LKEY */ if ((user && wqe->sg_list[i].lkey == 0) || - !ipath_lkey_ok(&dev->lk_table, - &qp->r_sg_list[j], &wqe->sg_list[i], + !ipath_lkey_ok(qp, &qp->r_sg_list[j], &wqe->sg_list[i], IB_ACCESS_LOCAL_WRITE)) goto bad_lkey; qp->r_len += wqe->sg_list[i].length; @@ -231,6 +229,7 @@ int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only) } } spin_unlock_irqrestore(&rq->lock, flags); + qp->r_wrid_valid = 1; bail: return ret; @@ -326,7 +325,7 @@ again: case IB_WR_RDMA_WRITE: if (wqe->length == 0) break; - if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge, wqe->length, + if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, wqe->length, wqe->wr.wr.rdma.remote_addr, wqe->wr.wr.rdma.rkey, IB_ACCESS_REMOTE_WRITE))) { @@ -350,7 +349,7 @@ again: break; case IB_WR_RDMA_READ: - if (unlikely(!ipath_rkey_ok(dev, &sqp->s_sge, wqe->length, + if (unlikely(!ipath_rkey_ok(qp, &sqp->s_sge, wqe->length, wqe->wr.wr.rdma.remote_addr, wqe->wr.wr.rdma.rkey, IB_ACCESS_REMOTE_READ))) @@ -365,7 +364,7 @@ again: case IB_WR_ATOMIC_CMP_AND_SWP: case IB_WR_ATOMIC_FETCH_AND_ADD: - if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge, sizeof(u64), + if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, sizeof(u64), wqe->wr.wr.rdma.remote_addr, wqe->wr.wr.rdma.rkey, IB_ACCESS_REMOTE_ATOMIC))) @@ -575,8 +574,7 @@ int ipath_post_ruc_send(struct ipath_qp *qp, struct ib_send_wr *wr) } if (wr->sg_list[i].length == 0) continue; - if (!ipath_lkey_ok(&to_idev(qp->ibqp.device)->lk_table, - &wqe->sg_list[j], &wr->sg_list[i], + if (!ipath_lkey_ok(qp, &wqe->sg_list[j], &wr->sg_list[i], acc)) { spin_unlock_irqrestore(&qp->s_lock, flags); ret = -EINVAL; diff --git a/drivers/infiniband/hw/ipath/ipath_srq.c b/drivers/infiniband/hw/ipath/ipath_srq.c index 941e866d951..94033503400 100644 --- a/drivers/infiniband/hw/ipath/ipath_srq.c +++ b/drivers/infiniband/hw/ipath/ipath_srq.c @@ -104,11 +104,6 @@ struct ib_srq *ipath_create_srq(struct ib_pd *ibpd, u32 sz; struct ib_srq *ret; - if (dev->n_srqs_allocated == ib_ipath_max_srqs) { - ret = ERR_PTR(-ENOMEM); - goto done; - } - if (srq_init_attr->attr.max_wr == 0) { ret = ERR_PTR(-EINVAL); goto done; @@ -180,10 +175,17 @@ struct ib_srq *ipath_create_srq(struct ib_pd *ibpd, spin_lock_init(&srq->rq.lock); srq->rq.wq->head = 0; srq->rq.wq->tail = 0; - srq->rq.max_sge = srq_init_attr->attr.max_sge; srq->limit = srq_init_attr->attr.srq_limit; - dev->n_srqs_allocated++; + spin_lock(&dev->n_srqs_lock); + if (dev->n_srqs_allocated == ib_ipath_max_srqs) { + spin_unlock(&dev->n_srqs_lock); + ret = ERR_PTR(-ENOMEM); + goto bail_wq; + } + + dev->n_srqs_allocated++; + spin_unlock(&dev->n_srqs_lock); ret = &srq->ibsrq; goto done; @@ -351,8 +353,13 @@ int ipath_destroy_srq(struct ib_srq *ibsrq) struct ipath_srq *srq = to_isrq(ibsrq); struct ipath_ibdev *dev = to_idev(ibsrq->device); + spin_lock(&dev->n_srqs_lock); dev->n_srqs_allocated--; - vfree(srq->rq.wq); + spin_unlock(&dev->n_srqs_lock); + if (srq->ip) + kref_put(&srq->ip->ref, ipath_release_mmap_info); + else + vfree(srq->rq.wq); kfree(srq); return 0; diff --git a/drivers/infiniband/hw/ipath/ipath_sysfs.c b/drivers/infiniband/hw/ipath/ipath_sysfs.c index e299148c4b6..182de34f9f4 100644 --- a/drivers/infiniband/hw/ipath/ipath_sysfs.c +++ b/drivers/infiniband/hw/ipath/ipath_sysfs.c @@ -257,7 +257,7 @@ static ssize_t store_guid(struct device *dev, struct ipath_devdata *dd = dev_get_drvdata(dev); ssize_t ret; unsigned short guid[8]; - __be64 nguid; + __be64 new_guid; u8 *ng; int i; @@ -266,7 +266,7 @@ static ssize_t store_guid(struct device *dev, &guid[4], &guid[5], &guid[6], &guid[7]) != 8) goto invalid; - ng = (u8 *) &nguid; + ng = (u8 *) &new_guid; for (i = 0; i < 8; i++) { if (guid[i] > 0xff) @@ -274,7 +274,10 @@ static ssize_t store_guid(struct device *dev, ng[i] = guid[i]; } - dd->ipath_guid = nguid; + if (new_guid == 0) + goto invalid; + + dd->ipath_guid = new_guid; dd->ipath_nguid = 1; ret = strlen(buf); @@ -297,6 +300,16 @@ static ssize_t show_nguid(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_nguid); } +static ssize_t show_nports(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + + /* Return the number of user ports available. */ + return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_cfgports - 1); +} + static ssize_t show_serial(struct device *dev, struct device_attribute *attr, char *buf) @@ -608,6 +621,7 @@ static DEVICE_ATTR(mlid, S_IWUSR | S_IRUGO, show_mlid, store_mlid); static DEVICE_ATTR(mtu, S_IWUSR | S_IRUGO, show_mtu, store_mtu); static DEVICE_ATTR(enabled, S_IWUSR | S_IRUGO, show_enabled, store_enabled); static DEVICE_ATTR(nguid, S_IRUGO, show_nguid, NULL); +static DEVICE_ATTR(nports, S_IRUGO, show_nports, NULL); static DEVICE_ATTR(reset, S_IWUSR, NULL, store_reset); static DEVICE_ATTR(serial, S_IRUGO, show_serial, NULL); static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); @@ -623,6 +637,7 @@ static struct attribute *dev_attributes[] = { &dev_attr_mlid.attr, &dev_attr_mtu.attr, &dev_attr_nguid.attr, + &dev_attr_nports.attr, &dev_attr_serial.attr, &dev_attr_status.attr, &dev_attr_status_str.attr, diff --git a/drivers/infiniband/hw/ipath/ipath_uc.c b/drivers/infiniband/hw/ipath/ipath_uc.c index 0fd3cded16b..e636cfd67a8 100644 --- a/drivers/infiniband/hw/ipath/ipath_uc.c +++ b/drivers/infiniband/hw/ipath/ipath_uc.c @@ -246,6 +246,10 @@ void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, struct ib_reth *reth; int header_in_data; + /* Validate the SLID. See Ch. 9.6.1.5 */ + if (unlikely(be16_to_cpu(hdr->lrh[3]) != qp->remote_ah_attr.dlid)) + goto done; + /* Check for GRH */ if (!has_grh) { ohdr = &hdr->u.oth; @@ -440,7 +444,7 @@ void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, int ok; /* Check rkey */ - ok = ipath_rkey_ok(dev, &qp->r_sge, qp->r_len, + ok = ipath_rkey_ok(qp, &qp->r_sge, qp->r_len, vaddr, rkey, IB_ACCESS_REMOTE_WRITE); if (unlikely(!ok)) { diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c index 6991d1d74e3..49f1102af8b 100644 --- a/drivers/infiniband/hw/ipath/ipath_ud.c +++ b/drivers/infiniband/hw/ipath/ipath_ud.c @@ -39,7 +39,6 @@ static int init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe, u32 *lengthp, struct ipath_sge_state *ss) { - struct ipath_ibdev *dev = to_idev(qp->ibqp.device); int user = to_ipd(qp->ibqp.pd)->user; int i, j, ret; struct ib_wc wc; @@ -50,8 +49,7 @@ static int init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe, continue; /* Check LKEY */ if ((user && wqe->sg_list[i].lkey == 0) || - !ipath_lkey_ok(&dev->lk_table, - j ? &ss->sg_list[j - 1] : &ss->sge, + !ipath_lkey_ok(qp, j ? &ss->sg_list[j - 1] : &ss->sge, &wqe->sg_list[i], IB_ACCESS_LOCAL_WRITE)) goto bad_lkey; *lengthp += wqe->sg_list[i].length; @@ -343,7 +341,7 @@ int ipath_post_ud_send(struct ipath_qp *qp, struct ib_send_wr *wr) if (wr->sg_list[i].length == 0) continue; - if (!ipath_lkey_ok(&dev->lk_table, ss.num_sge ? + if (!ipath_lkey_ok(qp, ss.num_sge ? sg_list + ss.num_sge - 1 : &ss.sge, &wr->sg_list[i], 0)) { ret = -EINVAL; diff --git a/drivers/infiniband/hw/ipath/ipath_user_pages.c b/drivers/infiniband/hw/ipath/ipath_user_pages.c index e32fca9faf8..413754b1d8a 100644 --- a/drivers/infiniband/hw/ipath/ipath_user_pages.c +++ b/drivers/infiniband/hw/ipath/ipath_user_pages.c @@ -90,6 +90,62 @@ bail: } /** + * ipath_map_page - a safety wrapper around pci_map_page() + * + * A dma_addr of all 0's is interpreted by the chip as "disabled". + * Unfortunately, it can also be a valid dma_addr returned on some + * architectures. + * + * The powerpc iommu assigns dma_addrs in ascending order, so we don't + * have to bother with retries or mapping a dummy page to insure we + * don't just get the same mapping again. + * + * I'm sure we won't be so lucky with other iommu's, so FIXME. + */ +dma_addr_t ipath_map_page(struct pci_dev *hwdev, struct page *page, + unsigned long offset, size_t size, int direction) +{ + dma_addr_t phys; + + phys = pci_map_page(hwdev, page, offset, size, direction); + + if (phys == 0) { + pci_unmap_page(hwdev, phys, size, direction); + phys = pci_map_page(hwdev, page, offset, size, direction); + /* + * FIXME: If we get 0 again, we should keep this page, + * map another, then free the 0 page. + */ + } + + return phys; +} + +/** + * ipath_map_single - a safety wrapper around pci_map_single() + * + * Same idea as ipath_map_page(). + */ +dma_addr_t ipath_map_single(struct pci_dev *hwdev, void *ptr, size_t size, + int direction) +{ + dma_addr_t phys; + + phys = pci_map_single(hwdev, ptr, size, direction); + + if (phys == 0) { + pci_unmap_single(hwdev, phys, size, direction); + phys = pci_map_single(hwdev, ptr, size, direction); + /* + * FIXME: If we get 0 again, we should keep this page, + * map another, then free the 0 page. + */ + } + + return phys; +} + +/** * ipath_get_user_pages - lock user pages into memory * @start_page: the start page * @num_pages: the number of pages diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c index b8381c5e72b..42eaed88c28 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.c +++ b/drivers/infiniband/hw/ipath/ipath_verbs.c @@ -898,7 +898,8 @@ int ipath_get_counters(struct ipath_devdata *dd, ipath_snap_cntr(dd, dd->ipath_cregs->cr_erricrccnt) + ipath_snap_cntr(dd, dd->ipath_cregs->cr_errvcrccnt) + ipath_snap_cntr(dd, dd->ipath_cregs->cr_errlpcrccnt) + - ipath_snap_cntr(dd, dd->ipath_cregs->cr_badformatcnt); + ipath_snap_cntr(dd, dd->ipath_cregs->cr_badformatcnt) + + dd->ipath_rxfc_unsupvl_errs; cntrs->port_rcv_remphys_errors = ipath_snap_cntr(dd, dd->ipath_cregs->cr_rcvebpcnt); cntrs->port_xmit_discards = @@ -911,8 +912,10 @@ int ipath_get_counters(struct ipath_devdata *dd, ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktsendcnt); cntrs->port_rcv_packets = ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktrcvcnt); - cntrs->local_link_integrity_errors = dd->ipath_lli_errors; - cntrs->excessive_buffer_overrun_errors = 0; /* XXX */ + cntrs->local_link_integrity_errors = + (dd->ipath_flags & IPATH_GPIO_ERRINTRS) ? + dd->ipath_lli_errs : dd->ipath_lli_errors; + cntrs->excessive_buffer_overrun_errors = dd->ipath_overrun_thresh_errs; ret = 0; @@ -1199,6 +1202,7 @@ static struct ib_ah *ipath_create_ah(struct ib_pd *pd, struct ipath_ah *ah; struct ib_ah *ret; struct ipath_ibdev *dev = to_idev(pd->device); + unsigned long flags; /* A multicast address requires a GRH (see ch. 8.4.1). */ if (ah_attr->dlid >= IPATH_MULTICAST_LID_BASE && @@ -1225,16 +1229,16 @@ static struct ib_ah *ipath_create_ah(struct ib_pd *pd, goto bail; } - spin_lock(&dev->n_ahs_lock); + spin_lock_irqsave(&dev->n_ahs_lock, flags); if (dev->n_ahs_allocated == ib_ipath_max_ahs) { - spin_unlock(&dev->n_ahs_lock); + spin_unlock_irqrestore(&dev->n_ahs_lock, flags); kfree(ah); ret = ERR_PTR(-ENOMEM); goto bail; } dev->n_ahs_allocated++; - spin_unlock(&dev->n_ahs_lock); + spin_unlock_irqrestore(&dev->n_ahs_lock, flags); /* ib_create_ah() will initialize ah->ibah. */ ah->attr = *ah_attr; @@ -1255,10 +1259,11 @@ static int ipath_destroy_ah(struct ib_ah *ibah) { struct ipath_ibdev *dev = to_idev(ibah->device); struct ipath_ah *ah = to_iah(ibah); + unsigned long flags; - spin_lock(&dev->n_ahs_lock); + spin_lock_irqsave(&dev->n_ahs_lock, flags); dev->n_ahs_allocated--; - spin_unlock(&dev->n_ahs_lock); + spin_unlock_irqrestore(&dev->n_ahs_lock, flags); kfree(ah); @@ -1380,11 +1385,13 @@ static int enable_timer(struct ipath_devdata *dd) * processing. */ if (dd->ipath_flags & IPATH_GPIO_INTR) { + u64 val; ipath_write_kreg(dd, dd->ipath_kregs->kr_debugportselect, 0x2074076542310ULL); /* Enable GPIO bit 2 interrupt */ - ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_mask, - (u64) (1 << 2)); + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_gpio_mask); + val |= (u64) (1 << IPATH_GPIO_PORT0_BIT); + ipath_write_kreg( dd, dd->ipath_kregs->kr_gpio_mask, val); } init_timer(&dd->verbs_timer); @@ -1399,8 +1406,17 @@ static int enable_timer(struct ipath_devdata *dd) static int disable_timer(struct ipath_devdata *dd) { /* Disable GPIO bit 2 interrupt */ - if (dd->ipath_flags & IPATH_GPIO_INTR) - ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_mask, 0); + if (dd->ipath_flags & IPATH_GPIO_INTR) { + u64 val; + /* Disable GPIO bit 2 interrupt */ + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_gpio_mask); + val &= ~((u64) (1 << IPATH_GPIO_PORT0_BIT)); + ipath_write_kreg( dd, dd->ipath_kregs->kr_gpio_mask, val); + /* + * We might want to undo changes to debugportselect, + * but how? + */ + } del_timer_sync(&dd->verbs_timer); @@ -1683,6 +1699,7 @@ static ssize_t show_stats(struct class_device *cdev, char *buf) "RC OTH NAKs %d\n" "RC timeouts %d\n" "RC RDMA dup %d\n" + "RC stalls %d\n" "piobuf wait %d\n" "no piobuf %d\n" "PKT drops %d\n" @@ -1690,7 +1707,7 @@ static ssize_t show_stats(struct class_device *cdev, char *buf) dev->n_rc_resends, dev->n_rc_qacks, dev->n_rc_acks, dev->n_seq_naks, dev->n_rdma_seq, dev->n_rnr_naks, dev->n_other_naks, dev->n_timeouts, - dev->n_rdma_dup_busy, dev->n_piowait, + dev->n_rdma_dup_busy, dev->n_rc_stalls, dev->n_piowait, dev->n_no_piobuf, dev->n_pkt_drops, dev->n_wqe_errs); for (i = 0; i < ARRAY_SIZE(dev->opstats); i++) { const struct ipath_opcode_stats *si = &dev->opstats[i]; diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h index 09bbb3f9a21..8039f6e5f0c 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.h +++ b/drivers/infiniband/hw/ipath/ipath_verbs.h @@ -220,6 +220,7 @@ struct ipath_segarray { }; struct ipath_mregion { + struct ib_pd *pd; /* shares refcnt of ibmr.pd */ u64 user_base; /* User's address for this region */ u64 iova; /* IB start address of this region */ size_t length; @@ -364,12 +365,14 @@ struct ipath_qp { u8 r_min_rnr_timer; /* retry timeout value for RNR NAKs */ u8 r_reuse_sge; /* for UC receive errors */ u8 r_sge_inx; /* current index into sg_list */ + u8 r_wrid_valid; /* r_wrid set but CQ entry not yet made */ u8 qp_access_flags; u8 s_max_sge; /* size of s_wq->sg_list */ u8 s_retry_cnt; /* number of times to retry */ u8 s_rnr_retry_cnt; u8 s_retry; /* requester retry counter */ u8 s_rnr_retry; /* requester RNR retry counter */ + u8 s_wait_credit; /* limit number of unacked packets sent */ u8 s_pkey_index; /* PKEY index to use */ u8 timeout; /* Timeout for this QP */ enum ib_mtu path_mtu; @@ -393,6 +396,8 @@ struct ipath_qp { #define IPATH_S_BUSY 0 #define IPATH_S_SIGNAL_REQ_WR 1 +#define IPATH_PSN_CREDIT 2048 + /* * Since struct ipath_swqe is not a fixed size, we can't simply index into * struct ipath_qp.s_wq. This function does the array index computation. @@ -521,6 +526,7 @@ struct ipath_ibdev { u32 n_rnr_naks; u32 n_other_naks; u32 n_timeouts; + u32 n_rc_stalls; u32 n_pkt_drops; u32 n_vl15_dropped; u32 n_wqe_errs; @@ -634,6 +640,8 @@ struct ib_qp *ipath_create_qp(struct ib_pd *ibpd, int ipath_destroy_qp(struct ib_qp *ibqp); +void ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err); + int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, struct ib_udata *udata); @@ -653,12 +661,6 @@ int ipath_verbs_send(struct ipath_devdata *dd, u32 hdrwords, void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig); -int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss, - u32 len, u64 vaddr, u32 rkey, int acc); - -int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge, - struct ib_sge *sge, int acc); - void ipath_copy_sge(struct ipath_sge_state *ss, void *data, u32 length); void ipath_skip_sge(struct ipath_sge_state *ss, u32 length); @@ -683,10 +685,10 @@ int ipath_alloc_lkey(struct ipath_lkey_table *rkt, void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey); -int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge, +int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge, struct ib_sge *sge, int acc); -int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss, +int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss, u32 len, u64 vaddr, u32 rkey, int acc); int ipath_post_srq_receive(struct ib_srq *ibsrq, struct ib_recv_wr *wr, diff --git a/drivers/infiniband/hw/ipath/ipath_wc_ppc64.c b/drivers/infiniband/hw/ipath/ipath_wc_ppc64.c index 036fde662aa..0095bb70f34 100644 --- a/drivers/infiniband/hw/ipath/ipath_wc_ppc64.c +++ b/drivers/infiniband/hw/ipath/ipath_wc_ppc64.c @@ -38,13 +38,23 @@ #include "ipath_kernel.h" /** - * ipath_unordered_wc - indicate whether write combining is ordered + * ipath_enable_wc - enable write combining for MMIO writes to the device + * @dd: infinipath device * - * PowerPC systems (at least those in the 970 processor family) - * write partially filled store buffers in address order, but will write - * completely filled store buffers in "random" order, and therefore must - * have serialization for correctness with current InfiniPath chips. + * Nothing to do on PowerPC, so just return without error. + */ +int ipath_enable_wc(struct ipath_devdata *dd) +{ + return 0; +} + +/** + * ipath_unordered_wc - indicate whether write combining is unordered * + * Because our performance depends on our ability to do write + * combining mmio writes in the most efficient way, we need to + * know if we are on a processor that may reorder stores when + * write combining. */ int ipath_unordered_wc(void) { diff --git a/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c b/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c index f8f9e2e8cbd..04696e62da8 100644 --- a/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c +++ b/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c @@ -123,6 +123,8 @@ int ipath_enable_wc(struct ipath_devdata *dd) ipath_cdbg(VERBOSE, "Set mtrr for chip to WC, " "cookie is %d\n", cookie); dd->ipath_wc_cookie = cookie; + dd->ipath_wc_base = (unsigned long) pioaddr; + dd->ipath_wc_len = (unsigned long) piolen; } } @@ -136,9 +138,16 @@ int ipath_enable_wc(struct ipath_devdata *dd) void ipath_disable_wc(struct ipath_devdata *dd) { if (dd->ipath_wc_cookie) { + int r; ipath_cdbg(VERBOSE, "undoing WCCOMB on pio buffers\n"); - mtrr_del(dd->ipath_wc_cookie, 0, 0); - dd->ipath_wc_cookie = 0; + r = mtrr_del(dd->ipath_wc_cookie, dd->ipath_wc_base, + dd->ipath_wc_len); + if (r < 0) + dev_info(&dd->pcidev->dev, + "mtrr_del(%lx, %lx, %lx) failed: %d\n", + dd->ipath_wc_cookie, dd->ipath_wc_base, + dd->ipath_wc_len, r); + dd->ipath_wc_cookie = 0; /* even on failure */ } } diff --git a/drivers/infiniband/ulp/iser/Kconfig b/drivers/infiniband/ulp/iser/Kconfig index 365a1b5f19e..aecbb9083f0 100644 --- a/drivers/infiniband/ulp/iser/Kconfig +++ b/drivers/infiniband/ulp/iser/Kconfig @@ -1,11 +1,12 @@ config INFINIBAND_ISER - tristate "ISCSI RDMA Protocol" + tristate "iSCSI Extensions for RDMA (iSER)" depends on INFINIBAND && SCSI && INET select SCSI_ISCSI_ATTRS ---help--- - Support for the ISCSI RDMA Protocol over InfiniBand. This - allows you to access storage devices that speak ISER/ISCSI - over InfiniBand. + Support for the iSCSI Extensions for RDMA (iSER) Protocol + over InfiniBand. This allows you to access storage devices + that speak iSCSI over iSER over InfiniBand. - The ISER protocol is defined by IETF. - See <http://www.ietf.org/>. + The iSER protocol is defined by IETF. + See <http://www.ietf.org/internet-drafts/draft-ietf-ips-iser-05.txt> + and <http://www.infinibandta.org/members/spec/iser_annex_060418.pdf> diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index 2a14fe2e322..eb6f98d8228 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c @@ -317,6 +317,8 @@ iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn) struct iscsi_iser_conn *iser_conn = conn->dd_data; iscsi_conn_teardown(cls_conn); + if (iser_conn->ib_conn) + iser_conn->ib_conn->iser_conn = NULL; kfree(iser_conn); } diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h index 2cf9ae0def1..9c53916f28c 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.h +++ b/drivers/infiniband/ulp/iser/iscsi_iser.h @@ -192,7 +192,7 @@ struct iser_regd_buf { struct iser_dto { struct iscsi_iser_cmd_task *ctask; - struct iscsi_iser_conn *conn; + struct iser_conn *ib_conn; int notify_enable; /* vector of registered buffers */ @@ -355,4 +355,11 @@ int iser_post_send(struct iser_desc *tx_desc); int iser_conn_state_comp(struct iser_conn *ib_conn, enum iser_ib_conn_state comp); + +int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask, + struct iser_data_buf *data, + enum iser_data_dir iser_dir, + enum dma_data_direction dma_dir); + +void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask); #endif diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c index ccf56f6f723..9b3d79c796c 100644 --- a/drivers/infiniband/ulp/iser/iser_initiator.c +++ b/drivers/infiniband/ulp/iser/iser_initiator.c @@ -66,42 +66,6 @@ static void iser_dto_add_regd_buff(struct iser_dto *dto, dto->regd_vector_len++; } -static int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask, - struct iser_data_buf *data, - enum iser_data_dir iser_dir, - enum dma_data_direction dma_dir) -{ - struct device *dma_device; - - iser_ctask->dir[iser_dir] = 1; - dma_device = iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device; - - data->dma_nents = dma_map_sg(dma_device, data->buf, data->size, dma_dir); - if (data->dma_nents == 0) { - iser_err("dma_map_sg failed!!!\n"); - return -EINVAL; - } - return 0; -} - -static void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask) -{ - struct device *dma_device; - struct iser_data_buf *data; - - dma_device = iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device; - - if (iser_ctask->dir[ISER_DIR_IN]) { - data = &iser_ctask->data[ISER_DIR_IN]; - dma_unmap_sg(dma_device, data->buf, data->size, DMA_FROM_DEVICE); - } - - if (iser_ctask->dir[ISER_DIR_OUT]) { - data = &iser_ctask->data[ISER_DIR_OUT]; - dma_unmap_sg(dma_device, data->buf, data->size, DMA_TO_DEVICE); - } -} - /* Register user buffer memory and initialize passive rdma * dto descriptor. Total data size is stored in * iser_ctask->data[ISER_DIR_IN].data_len @@ -249,7 +213,7 @@ static int iser_post_receive_control(struct iscsi_conn *conn) } recv_dto = &rx_desc->dto; - recv_dto->conn = iser_conn; + recv_dto->ib_conn = iser_conn->ib_conn; recv_dto->regd_vector_len = 0; regd_hdr = &rx_desc->hdr_regd_buf; @@ -296,7 +260,7 @@ static void iser_create_send_desc(struct iscsi_iser_conn *iser_conn, regd_hdr->virt_addr = tx_desc; /* == &tx_desc->iser_header */ regd_hdr->data_size = ISER_TOTAL_HEADERS_LEN; - send_dto->conn = iser_conn; + send_dto->ib_conn = iser_conn->ib_conn; send_dto->notify_enable = 1; send_dto->regd_vector_len = 0; @@ -588,7 +552,7 @@ void iser_rcv_completion(struct iser_desc *rx_desc, unsigned long dto_xfer_len) { struct iser_dto *dto = &rx_desc->dto; - struct iscsi_iser_conn *conn = dto->conn; + struct iscsi_iser_conn *conn = dto->ib_conn->iser_conn; struct iscsi_session *session = conn->iscsi_conn->session; struct iscsi_cmd_task *ctask; struct iscsi_iser_cmd_task *iser_ctask; @@ -641,7 +605,8 @@ void iser_rcv_completion(struct iser_desc *rx_desc, void iser_snd_completion(struct iser_desc *tx_desc) { struct iser_dto *dto = &tx_desc->dto; - struct iscsi_iser_conn *iser_conn = dto->conn; + struct iser_conn *ib_conn = dto->ib_conn; + struct iscsi_iser_conn *iser_conn = ib_conn->iser_conn; struct iscsi_conn *conn = iser_conn->iscsi_conn; struct iscsi_mgmt_task *mtask; @@ -652,7 +617,7 @@ void iser_snd_completion(struct iser_desc *tx_desc) if (tx_desc->type == ISCSI_TX_DATAOUT) kmem_cache_free(ig.desc_cache, tx_desc); - atomic_dec(&iser_conn->ib_conn->post_send_buf_count); + atomic_dec(&ib_conn->post_send_buf_count); write_lock(conn->recv_lock); if (conn->suspend_tx) { @@ -698,14 +663,19 @@ void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *iser_ctask) void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask) { int deferred; + int is_rdma_aligned = 1; /* if we were reading, copy back to unaligned sglist, * anyway dma_unmap and free the copy */ - if (iser_ctask->data_copy[ISER_DIR_IN].copy_buf != NULL) + if (iser_ctask->data_copy[ISER_DIR_IN].copy_buf != NULL) { + is_rdma_aligned = 0; iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_IN); - if (iser_ctask->data_copy[ISER_DIR_OUT].copy_buf != NULL) + } + if (iser_ctask->data_copy[ISER_DIR_OUT].copy_buf != NULL) { + is_rdma_aligned = 0; iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_OUT); + } if (iser_ctask->dir[ISER_DIR_IN]) { deferred = iser_regd_buff_release @@ -725,7 +695,9 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask) } } - iser_dma_unmap_task_data(iser_ctask); + /* if the data was unaligned, it was already unmapped and then copied */ + if (is_rdma_aligned) + iser_dma_unmap_task_data(iser_ctask); } void iser_dto_buffs_release(struct iser_dto *dto) diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c index d0b03f42658..0606744c3f8 100644 --- a/drivers/infiniband/ulp/iser/iser_memory.c +++ b/drivers/infiniband/ulp/iser/iser_memory.c @@ -369,6 +369,44 @@ static void iser_page_vec_build(struct iser_data_buf *data, } } +int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask, + struct iser_data_buf *data, + enum iser_data_dir iser_dir, + enum dma_data_direction dma_dir) +{ + struct device *dma_device; + + iser_ctask->dir[iser_dir] = 1; + dma_device = + iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device; + + data->dma_nents = dma_map_sg(dma_device, data->buf, data->size, dma_dir); + if (data->dma_nents == 0) { + iser_err("dma_map_sg failed!!!\n"); + return -EINVAL; + } + return 0; +} + +void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask) +{ + struct device *dma_device; + struct iser_data_buf *data; + + dma_device = + iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device; + + if (iser_ctask->dir[ISER_DIR_IN]) { + data = &iser_ctask->data[ISER_DIR_IN]; + dma_unmap_sg(dma_device, data->buf, data->size, DMA_FROM_DEVICE); + } + + if (iser_ctask->dir[ISER_DIR_OUT]) { + data = &iser_ctask->data[ISER_DIR_OUT]; + dma_unmap_sg(dma_device, data->buf, data->size, DMA_TO_DEVICE); + } +} + /** * iser_reg_rdma_mem - Registers memory intended for RDMA, * obtaining rkey and va @@ -394,6 +432,10 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask, iser_err("rdma alignment violation %d/%d aligned\n", aligned_len, mem->size); iser_data_buf_dump(mem); + + /* unmap the command data before accessing it */ + iser_dma_unmap_task_data(iser_ctask); + /* allocate copy buf, if we are writing, copy the */ /* unaligned scatterlist, dma map the copy */ if (iser_start_rdma_unaligned_sg(iser_ctask, cmd_dir) != 0) diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index ecdca7fc1e4..18a00003499 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -571,6 +571,8 @@ void iser_conn_release(struct iser_conn *ib_conn) /* on EVENT_ADDR_ERROR there's no device yet for this conn */ if (device != NULL) iser_device_try_release(device); + if (ib_conn->iser_conn) + ib_conn->iser_conn->ib_conn = NULL; kfree(ib_conn); } @@ -694,7 +696,7 @@ int iser_post_recv(struct iser_desc *rx_desc) struct iser_dto *recv_dto = &rx_desc->dto; /* Retrieve conn */ - ib_conn = recv_dto->conn->ib_conn; + ib_conn = recv_dto->ib_conn; iser_dto_to_iov(recv_dto, iov, 2); @@ -727,7 +729,7 @@ int iser_post_send(struct iser_desc *tx_desc) struct iser_conn *ib_conn; struct iser_dto *dto = &tx_desc->dto; - ib_conn = dto->conn->ib_conn; + ib_conn = dto->ib_conn; iser_dto_to_iov(dto, iov, MAX_REGD_BUF_VECTOR_LEN); @@ -774,7 +776,7 @@ static void iser_comp_error_worker(void *data) static void iser_handle_comp_error(struct iser_desc *desc) { struct iser_dto *dto = &desc->dto; - struct iser_conn *ib_conn = dto->conn->ib_conn; + struct iser_conn *ib_conn = dto->ib_conn; iser_dto_buffs_release(dto); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index a9dda56f62c..83eac3a66bc 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -183,4 +183,13 @@ config KEYBOARD_HIL This driver implements support for HIL-keyboards attached to your machine, so normally you should say Y here. +config KEYBOARD_OMAP + tristate "TI OMAP keypad support" + depends on (ARCH_OMAP1 || ARCH_OMAP2) + help + 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 omap-keypad. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 2708167ba17..b265391f1f1 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -15,4 +15,5 @@ obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o +obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c new file mode 100644 index 00000000000..d436287d1d2 --- /dev/null +++ b/drivers/input/keyboard/omap-keypad.c @@ -0,0 +1,492 @@ +/* + * linux/drivers/input/keyboard/omap-keypad.c + * + * OMAP Keypad Driver + * + * Copyright (C) 2003 Nokia Corporation + * Written by Timo Teräs <ext-timo.teras@nokia.com> + * + * Added support for H2 & H3 Keypad + * Copyright (C) 2004 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/types.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/errno.h> +#include <asm/arch/gpio.h> +#include <asm/arch/keypad.h> +#include <asm/arch/menelaus.h> +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/mach-types.h> +#include <asm/arch/mux.h> + +#undef NEW_BOARD_LEARNING_MODE + +static void omap_kp_tasklet(unsigned long); +static void omap_kp_timer(unsigned long); + +static unsigned char keypad_state[8]; +static DEFINE_MUTEX(kp_enable_mutex); +static int kp_enable = 1; +static int kp_cur_group = -1; + +struct omap_kp { + struct input_dev *input; + struct timer_list timer; + int irq; + unsigned int rows; + unsigned int cols; + unsigned long delay; + unsigned int debounce; +}; + +DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0); + +static int *keymap; +static unsigned int *row_gpios; +static unsigned int *col_gpios; + +#ifdef CONFIG_ARCH_OMAP2 +static void set_col_gpio_val(struct omap_kp *omap_kp, u8 value) +{ + int col; + for (col = 0; col < omap_kp->cols; col++) { + if (value & (1 << col)) + omap_set_gpio_dataout(col_gpios[col], 1); + else + omap_set_gpio_dataout(col_gpios[col], 0); + } +} + +static u8 get_row_gpio_val(struct omap_kp *omap_kp) +{ + int row; + u8 value = 0; + + for (row = 0; row < omap_kp->rows; row++) { + if (omap_get_gpio_datain(row_gpios[row])) + value |= (1 << row); + } + return value; +} +#else +#define set_col_gpio_val(x, y) do {} while (0) +#define get_row_gpio_val(x) 0 +#endif + +static irqreturn_t omap_kp_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct omap_kp *omap_kp = dev_id; + + /* disable keyboard interrupt and schedule for handling */ + if (cpu_is_omap24xx()) { + int i; + for (i = 0; i < omap_kp->rows; i++) + disable_irq(OMAP_GPIO_IRQ(row_gpios[i])); + } else + /* disable keyboard interrupt and schedule for handling */ + omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + + tasklet_schedule(&kp_tasklet); + + return IRQ_HANDLED; +} + +static void omap_kp_timer(unsigned long data) +{ + tasklet_schedule(&kp_tasklet); +} + +static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state) +{ + int col = 0; + + /* read the keypad status */ + if (cpu_is_omap24xx()) { + int i; + for (i = 0; i < omap_kp->rows; i++) + disable_irq(OMAP_GPIO_IRQ(row_gpios[i])); + + /* read the keypad status */ + for (col = 0; col < omap_kp->cols; col++) { + set_col_gpio_val(omap_kp, ~(1 << col)); + state[col] = ~(get_row_gpio_val(omap_kp)) & 0x3f; + } + set_col_gpio_val(omap_kp, 0); + + } else { + /* disable keyboard interrupt and schedule for handling */ + omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + + /* read the keypad status */ + omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC); + for (col = 0; col < omap_kp->cols; col++) { + omap_writew(~(1 << col) & 0xff, + OMAP_MPUIO_BASE + OMAP_MPUIO_KBC); + + udelay(omap_kp->delay); + + state[col] = ~omap_readw(OMAP_MPUIO_BASE + + OMAP_MPUIO_KBR_LATCH) & 0xff; + } + omap_writew(0x00, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC); + udelay(2); + } +} + +static inline int omap_kp_find_key(int col, int row) +{ + int i, key; + + key = KEY(col, row, 0); + for (i = 0; keymap[i] != 0; i++) + if ((keymap[i] & 0xff000000) == key) + return keymap[i] & 0x00ffffff; + return -1; +} + +static void omap_kp_tasklet(unsigned long data) +{ + struct omap_kp *omap_kp_data = (struct omap_kp *) data; + unsigned char new_state[8], changed, key_down = 0; + int col, row; + int spurious = 0; + + /* check for any changes */ + omap_kp_scan_keypad(omap_kp_data, new_state); + + /* check for changes and print those */ + for (col = 0; col < omap_kp_data->cols; col++) { + changed = new_state[col] ^ keypad_state[col]; + key_down |= new_state[col]; + if (changed == 0) + continue; + + for (row = 0; row < omap_kp_data->rows; row++) { + int key; + if (!(changed & (1 << row))) + continue; +#ifdef NEW_BOARD_LEARNING_MODE + printk(KERN_INFO "omap-keypad: key %d-%d %s\n", col, + row, (new_state[col] & (1 << row)) ? + "pressed" : "released"); +#else + key = omap_kp_find_key(col, row); + if (key < 0) { + printk(KERN_WARNING + "omap-keypad: Spurious key event %d-%d\n", + col, row); + /* We scan again after a couple of seconds */ + spurious = 1; + continue; + } + + if (!(kp_cur_group == (key & GROUP_MASK) || + kp_cur_group == -1)) + continue; + + kp_cur_group = key & GROUP_MASK; + input_report_key(omap_kp_data->input, key & ~GROUP_MASK, + new_state[col] & (1 << row)); +#endif + } + } + memcpy(keypad_state, new_state, sizeof(keypad_state)); + + if (key_down) { + int delay = HZ / 20; + /* some key is pressed - keep irq disabled and use timer + * to poll the keypad */ + if (spurious) + delay = 2 * HZ; + mod_timer(&omap_kp_data->timer, jiffies + delay); + } else { + /* enable interrupts */ + if (cpu_is_omap24xx()) { + int i; + for (i = 0; i < omap_kp_data->rows; i++) + enable_irq(OMAP_GPIO_IRQ(row_gpios[i])); + } else { + omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + kp_cur_group = -1; + } + } +} + +static ssize_t omap_kp_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", kp_enable); +} + +static ssize_t omap_kp_enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int state; + + if (sscanf(buf, "%u", &state) != 1) + return -EINVAL; + + if ((state != 1) && (state != 0)) + return -EINVAL; + + mutex_lock(&kp_enable_mutex); + if (state != kp_enable) { + if (state) + enable_irq(INT_KEYBOARD); + else + disable_irq(INT_KEYBOARD); + kp_enable = state; + } + mutex_unlock(&kp_enable_mutex); + + return strnlen(buf, count); +} + +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, omap_kp_enable_show, omap_kp_enable_store); + +#ifdef CONFIG_PM +static int omap_kp_suspend(struct platform_device *dev, pm_message_t state) +{ + /* Nothing yet */ + + return 0; +} + +static int omap_kp_resume(struct platform_device *dev) +{ + /* Nothing yet */ + + return 0; +} +#else +#define omap_kp_suspend NULL +#define omap_kp_resume NULL +#endif + +static int __init omap_kp_probe(struct platform_device *pdev) +{ + struct omap_kp *omap_kp; + struct input_dev *input_dev; + struct omap_kp_platform_data *pdata = pdev->dev.platform_data; + int i, col_idx, row_idx, irq_idx, ret; + + if (!pdata->rows || !pdata->cols || !pdata->keymap) { + printk(KERN_ERR "No rows, cols or keymap from pdata\n"); + return -EINVAL; + } + + omap_kp = kzalloc(sizeof(struct omap_kp), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!omap_kp || !input_dev) { + kfree(omap_kp); + input_free_device(input_dev); + return -ENOMEM; + } + + platform_set_drvdata(pdev, omap_kp); + + omap_kp->input = input_dev; + + /* Disable the interrupt for the MPUIO keyboard */ + if (!cpu_is_omap24xx()) + omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + + keymap = pdata->keymap; + + if (pdata->rep) + set_bit(EV_REP, input_dev->evbit); + + if (pdata->delay) + omap_kp->delay = pdata->delay; + + if (pdata->row_gpios && pdata->col_gpios) { + row_gpios = pdata->row_gpios; + col_gpios = pdata->col_gpios; + } + + omap_kp->rows = pdata->rows; + omap_kp->cols = pdata->cols; + + if (cpu_is_omap24xx()) { + /* Cols: outputs */ + for (col_idx = 0; col_idx < omap_kp->cols; col_idx++) { + if (omap_request_gpio(col_gpios[col_idx]) < 0) { + printk(KERN_ERR "Failed to request" + "GPIO%d for keypad\n", + col_gpios[col_idx]); + goto err1; + } + omap_set_gpio_direction(col_gpios[col_idx], 0); + } + /* Rows: inputs */ + for (row_idx = 0; row_idx < omap_kp->rows; row_idx++) { + if (omap_request_gpio(row_gpios[row_idx]) < 0) { + printk(KERN_ERR "Failed to request" + "GPIO%d for keypad\n", + row_gpios[row_idx]); + goto err2; + } + omap_set_gpio_direction(row_gpios[row_idx], 1); + } + } + + setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp); + + /* get the irq and init timer*/ + tasklet_enable(&kp_tasklet); + kp_tasklet.data = (unsigned long) omap_kp; + + ret = device_create_file(&pdev->dev, &dev_attr_enable); + if (ret < 0) + goto err2; + + /* setup input device */ + set_bit(EV_KEY, input_dev->evbit); + for (i = 0; keymap[i] != 0; i++) + set_bit(keymap[i] & KEY_MAX, input_dev->keybit); + input_dev->name = "omap-keypad"; + input_dev->phys = "omap-keypad/input0"; + input_dev->cdev.dev = &pdev->dev; + input_dev->private = omap_kp; + + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + + input_dev->keycode = keymap; + input_dev->keycodesize = sizeof(unsigned int); + input_dev->keycodemax = pdata->keymapsize; + + ret = input_register_device(omap_kp->input); + if (ret < 0) { + printk(KERN_ERR "Unable to register omap-keypad input device\n"); + goto err3; + } + + if (pdata->dbounce) + omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING); + + /* scan current status and enable interrupt */ + omap_kp_scan_keypad(omap_kp, keypad_state); + if (!cpu_is_omap24xx()) { + omap_kp->irq = platform_get_irq(pdev, 0); + if (omap_kp->irq >= 0) { + if (request_irq(omap_kp->irq, omap_kp_interrupt, 0, + "omap-keypad", omap_kp) < 0) + goto err4; + } + omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + } else { + for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) { + if (request_irq(OMAP_GPIO_IRQ(row_gpios[irq_idx]), + omap_kp_interrupt, + IRQF_TRIGGER_FALLING, + "omap-keypad", omap_kp) < 0) + goto err5; + } + } + return 0; +err5: + for (i = irq_idx-1; i >=0; i--) + free_irq(row_gpios[i], 0); +err4: + input_unregister_device(omap_kp->input); + input_dev = NULL; +err3: + device_remove_file(&pdev->dev, &dev_attr_enable); +err2: + for (i = row_idx-1; i >=0; i--) + omap_free_gpio(row_gpios[i]); +err1: + for (i = col_idx-1; i >=0; i--) + omap_free_gpio(col_gpios[i]); + + kfree(omap_kp); + input_free_device(input_dev); + + return -EINVAL; +} + +static int omap_kp_remove(struct platform_device *pdev) +{ + struct omap_kp *omap_kp = platform_get_drvdata(pdev); + + /* disable keypad interrupt handling */ + tasklet_disable(&kp_tasklet); + if (cpu_is_omap24xx()) { + int i; + for (i = 0; i < omap_kp->cols; i++) + omap_free_gpio(col_gpios[i]); + for (i = 0; i < omap_kp->rows; i++) { + omap_free_gpio(row_gpios[i]); + free_irq(OMAP_GPIO_IRQ(row_gpios[i]), 0); + } + } else { + omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + free_irq(omap_kp->irq, 0); + } + + del_timer_sync(&omap_kp->timer); + tasklet_kill(&kp_tasklet); + + /* unregister everything */ + input_unregister_device(omap_kp->input); + + kfree(omap_kp); + + return 0; +} + +static struct platform_driver omap_kp_driver = { + .probe = omap_kp_probe, + .remove = omap_kp_remove, + .suspend = omap_kp_suspend, + .resume = omap_kp_resume, + .driver = { + .name = "omap-keypad", + }, +}; + +static int __devinit omap_kp_init(void) +{ + printk(KERN_INFO "OMAP Keypad Driver\n"); + return platform_driver_register(&omap_kp_driver); +} + +static void __exit omap_kp_exit(void) +{ + platform_driver_unregister(&omap_kp_driver); +} + +module_init(omap_kp_init); +module_exit(omap_kp_exit); + +MODULE_AUTHOR("Timo Teräs"); +MODULE_DESCRIPTION("OMAP Keypad Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index 43da8ae1b2a..1f8d6ae66b4 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -1614,8 +1614,8 @@ isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp) struct sk_buff *skb; unsigned char *p; struct in_device *in_dev = NULL; - u32 addr = 0; /* local ipv4 address */ - u32 mask = 0; /* local netmask */ + __be32 addr = 0; /* local ipv4 address */ + __be32 mask = 0; /* local netmask */ if ((in_dev = lp->netdev->dev.ip_ptr) != NULL) { /* take primary(first) address of interface */ diff --git a/drivers/leds/leds-net48xx.c b/drivers/leds/leds-net48xx.c index 713c4a8aa77..45ba3d45bcb 100644 --- a/drivers/leds/leds-net48xx.c +++ b/drivers/leds/leds-net48xx.c @@ -16,6 +16,7 @@ #include <linux/leds.h> #include <linux/err.h> #include <asm/io.h> +#include <linux/nsc_gpio.h> #include <linux/scx200_gpio.h> #define DRVNAME "net48xx-led" @@ -26,10 +27,7 @@ static struct platform_device *pdev; static void net48xx_error_led_set(struct led_classdev *led_cdev, enum led_brightness value) { - if (value) - scx200_gpio_set_high(NET48XX_ERROR_LED_GPIO); - else - scx200_gpio_set_low(NET48XX_ERROR_LED_GPIO); + scx200_gpio_ops.gpio_set(NET48XX_ERROR_LED_GPIO, value ? 1 : 0); } static struct led_classdev net48xx_error_led = { @@ -81,7 +79,8 @@ static int __init net48xx_led_init(void) { int ret; - if (!scx200_gpio_present()) { + /* small hack, but scx200_gpio doesn't set .dev if the probe fails */ + if (!scx200_gpio_ops.dev) { ret = -ENODEV; goto out; } diff --git a/drivers/macintosh/via-pmu-backlight.c b/drivers/macintosh/via-pmu-backlight.c index a82f313d9dc..6c29fe727c0 100644 --- a/drivers/macintosh/via-pmu-backlight.c +++ b/drivers/macintosh/via-pmu-backlight.c @@ -16,7 +16,7 @@ #define MAX_PMU_LEVEL 0xFF static struct backlight_properties pmu_backlight_data; -static spinlock_t pmu_backlight_lock; +static DEFINE_SPINLOCK(pmu_backlight_lock); static int sleeping; static u8 bl_curve[FB_BACKLIGHT_LEVELS]; diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c index aceb61d9fbc..83f79de7174 100644 --- a/drivers/macintosh/windfarm_smu_sat.c +++ b/drivers/macintosh/windfarm_smu_sat.c @@ -397,12 +397,7 @@ static int wf_sat_detach(struct i2c_client *client) static int __init sat_sensors_init(void) { - int err; - - err = i2c_add_driver(&wf_sat_driver); - if (err < 0) - return err; - return 0; + return i2c_add_driver(&wf_sat_driver); } static void __exit sat_sensors_exit(void) diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index bf869ed03ee..6dd31a291d8 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -2,6 +2,8 @@ # Block device driver configuration # +if BLOCK + menu "Multi-device support (RAID and LVM)" config MD @@ -251,3 +253,4 @@ config DM_MULTIPATH_EMC endmenu +endif diff --git a/drivers/md/dm-emc.c b/drivers/md/dm-emc.c index 2a374ccb30d..2b2d45d7baa 100644 --- a/drivers/md/dm-emc.c +++ b/drivers/md/dm-emc.c @@ -126,7 +126,8 @@ static struct request *get_failover_req(struct emc_handler *h, memset(&rq->cmd, 0, BLK_MAX_CDB); rq->timeout = EMC_FAILOVER_TIMEOUT; - rq->flags |= (REQ_BLOCK_PC | REQ_FAILFAST | REQ_NOMERGE); + rq->cmd_type = REQ_TYPE_BLOCK_PC; + rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE; return rq; } diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index 1a04db4552d..f33e5d97341 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -4,7 +4,6 @@ config VIDEO_SAA7146 config VIDEO_SAA7146_VV tristate - select VIDEO_V4L2 select VIDEO_BUF select VIDEO_VIDEOBUF select VIDEO_SAA7146 diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index ca98d947894..db753443587 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -32,6 +32,37 @@ IR_KEYTAB_TYPE ir_codes_empty[IR_KEYTAB_SIZE] = { EXPORT_SYMBOL_GPL(ir_codes_empty); +/* Michal Majchrowicz <mmajchrowicz@gmail.com> */ +IR_KEYTAB_TYPE ir_codes_proteus_2309[IR_KEYTAB_SIZE] = { + /* numeric */ + [ 0x00 ] = KEY_0, + [ 0x01 ] = KEY_1, + [ 0x02 ] = KEY_2, + [ 0x03 ] = KEY_3, + [ 0x04 ] = KEY_4, + [ 0x05 ] = KEY_5, + [ 0x06 ] = KEY_6, + [ 0x07 ] = KEY_7, + [ 0x08 ] = KEY_8, + [ 0x09 ] = KEY_9, + + [ 0x5c ] = KEY_POWER, /* power */ + [ 0x20 ] = KEY_F, /* full screen */ + [ 0x0f ] = KEY_BACKSPACE, /* recall */ + [ 0x1b ] = KEY_ENTER, /* mute */ + [ 0x41 ] = KEY_RECORD, /* record */ + [ 0x43 ] = KEY_STOP, /* stop */ + [ 0x16 ] = KEY_S, + [ 0x1a ] = KEY_Q, /* off */ + [ 0x2e ] = KEY_RED, + [ 0x1f ] = KEY_DOWN, /* channel - */ + [ 0x1c ] = KEY_UP, /* channel + */ + [ 0x10 ] = KEY_LEFT, /* volume - */ + [ 0x1e ] = KEY_RIGHT, /* volume + */ + [ 0x14 ] = KEY_F1, +}; + +EXPORT_SYMBOL_GPL(ir_codes_proteus_2309); /* Matt Jesson <dvb@jesson.eclipse.co.uk */ IR_KEYTAB_TYPE ir_codes_avermedia_dvbt[IR_KEYTAB_SIZE] = { [ 0x28 ] = KEY_0, //'0' / 'enter' @@ -1473,3 +1504,51 @@ IR_KEYTAB_TYPE ir_codes_npgtech[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_npgtech); + +/* Norwood Micro (non-Pro) TV Tuner + By Peter Naulls <peter@chocky.org> + Key comments are the functions given in the manual */ +IR_KEYTAB_TYPE ir_codes_norwood[IR_KEYTAB_SIZE] = { + /* Keys 0 to 9 */ + [ 0x20 ] = KEY_0, + [ 0x21 ] = KEY_1, + [ 0x22 ] = KEY_2, + [ 0x23 ] = KEY_3, + [ 0x24 ] = KEY_4, + [ 0x25 ] = KEY_5, + [ 0x26 ] = KEY_6, + [ 0x27 ] = KEY_7, + [ 0x28 ] = KEY_8, + [ 0x29 ] = KEY_9, + + [ 0x78 ] = KEY_TUNER, /* Video Source */ + [ 0x2c ] = KEY_EXIT, /* Open/Close software */ + [ 0x2a ] = KEY_SELECT, /* 2 Digit Select */ + [ 0x69 ] = KEY_AGAIN, /* Recall */ + + [ 0x32 ] = KEY_BRIGHTNESSUP, /* Brightness increase */ + [ 0x33 ] = KEY_BRIGHTNESSDOWN, /* Brightness decrease */ + [ 0x6b ] = KEY_KPPLUS, /* (not named >>>>>) */ + [ 0x6c ] = KEY_KPMINUS, /* (not named <<<<<) */ + + [ 0x2d ] = KEY_MUTE, /* Mute */ + [ 0x30 ] = KEY_VOLUMEUP, /* Volume up */ + [ 0x31 ] = KEY_VOLUMEDOWN, /* Volume down */ + [ 0x60 ] = KEY_CHANNELUP, /* Channel up */ + [ 0x61 ] = KEY_CHANNELDOWN, /* Channel down */ + + [ 0x3f ] = KEY_RECORD, /* Record */ + [ 0x37 ] = KEY_PLAY, /* Play */ + [ 0x36 ] = KEY_PAUSE, /* Pause */ + [ 0x2b ] = KEY_STOP, /* Stop */ + [ 0x67 ] = KEY_FASTFORWARD, /* Foward */ + [ 0x66 ] = KEY_REWIND, /* Rewind */ + [ 0x3e ] = KEY_SEARCH, /* Auto Scan */ + [ 0x2e ] = KEY_CAMERA, /* Capture Video */ + [ 0x6d ] = KEY_MENU, /* Show/Hide Control */ + [ 0x2f ] = KEY_ZOOM, /* Full Screen */ + [ 0x34 ] = KEY_RADIO, /* FM */ + [ 0x65 ] = KEY_POWER, /* Computer power */ +}; + +EXPORT_SYMBOL_GPL(ir_codes_norwood); diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 0027acc5b8e..d867a6a9e43 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -455,7 +455,6 @@ static void vv_callback(struct saa7146_dev *dev, unsigned long status) static struct video_device device_template = { - .hardware = VID_HARDWARE_SAA7146, .fops = &video_fops, .minor = -1, }; diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig index 49a06fc54c5..a0dcd59da76 100644 --- a/drivers/media/dvb/b2c2/Kconfig +++ b/drivers/media/dvb/b2c2/Kconfig @@ -2,13 +2,13 @@ config DVB_B2C2_FLEXCOP tristate "Technisat/B2C2 FlexCopII(b) and FlexCopIII adapters" depends on DVB_CORE && I2C select DVB_PLL - select DVB_STV0299 - select DVB_MT352 - select DVB_MT312 - select DVB_NXT200X - select DVB_STV0297 - select DVB_BCM3510 - select DVB_LGDT330X + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_MT312 if !DVB_FE_CUSTOMISE + select DVB_NXT200X if !DVB_FE_CUSTOMISE + select DVB_STV0297 if !DVB_FE_CUSTOMISE + select DVB_BCM3510 if !DVB_FE_CUSTOMISE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE help Support for the digital TV receiver chip made by B2C2 Inc. included in Technisats PCI cards and USB boxes. diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 3be87c72e37..b8ba8786345 100644 --- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -505,7 +505,7 @@ int flexcop_frontend_init(struct flexcop_device *fc) struct dvb_frontend_ops *ops; /* try the sky v2.6 (stv0299/Samsung tbmu24112(sl1935)) */ - if ((fc->fe = stv0299_attach(&samsung_tbmu24112_config, &fc->i2c_adap)) != NULL) { + if ((fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, &fc->i2c_adap)) != NULL) { ops = &fc->fe->ops; ops->tuner_ops.set_params = samsung_tbmu24112_tuner_set_params; @@ -519,36 +519,36 @@ int flexcop_frontend_init(struct flexcop_device *fc) info("found the stv0299 at i2c address: 0x%02x",samsung_tbmu24112_config.demod_address); } else /* try the air dvb-t (mt352/Samsung tdtc9251dh0(??)) */ - if ((fc->fe = mt352_attach(&samsung_tdtc9251dh0_config, &fc->i2c_adap)) != NULL ) { + if ((fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, &fc->i2c_adap)) != NULL ) { fc->dev_type = FC_AIR_DVB; fc->fe->ops.tuner_ops.calc_regs = samsung_tdtc9251dh0_calc_regs; info("found the mt352 at i2c address: 0x%02x",samsung_tdtc9251dh0_config.demod_address); } else /* try the air atsc 2nd generation (nxt2002) */ - if ((fc->fe = nxt200x_attach(&samsung_tbmv_config, &fc->i2c_adap)) != NULL) { + if ((fc->fe = dvb_attach(nxt200x_attach, &samsung_tbmv_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_AIR_ATSC2; - dvb_pll_attach(fc->fe, 0x61, &fc->i2c_adap, &dvb_pll_samsung_tbmv); + dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, &dvb_pll_samsung_tbmv); info("found the nxt2002 at i2c address: 0x%02x",samsung_tbmv_config.demod_address); } else /* try the air atsc 3nd generation (lgdt3303) */ - if ((fc->fe = lgdt330x_attach(&air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) { + if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_AIR_ATSC3; fc->fe->ops.tuner_ops.set_params = lgdt3303_tuner_set_params; info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address); } else /* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */ - if ((fc->fe = bcm3510_attach(&air2pc_atsc_first_gen_config, &fc->i2c_adap)) != NULL) { + if ((fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_AIR_ATSC1; info("found the bcm3510 at i2c address: 0x%02x",air2pc_atsc_first_gen_config.demod_address); } else /* try the cable dvb (stv0297) */ - if ((fc->fe = stv0297_attach(&alps_tdee4_stv0297_config, &fc->i2c_adap)) != NULL) { + if ((fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_CABLE; fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params; info("found the stv0297 at i2c address: 0x%02x",alps_tdee4_stv0297_config.demod_address); } else /* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */ - if ((fc->fe = vp310_mt312_attach(&skystar23_samsung_tbdu18132_config, &fc->i2c_adap)) != NULL) { + if ((fc->fe = dvb_attach(vp310_mt312_attach, &skystar23_samsung_tbdu18132_config, &fc->i2c_adap)) != NULL) { ops = &fc->fe->ops; ops->tuner_ops.set_params = skystar23_samsung_tbdu18132_tuner_set_params; @@ -571,9 +571,7 @@ int flexcop_frontend_init(struct flexcop_device *fc) } else { if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) { err("frontend registration failed!"); - ops = &fc->fe->ops; - if (ops->release != NULL) - ops->release(fc->fe); + dvb_frontend_detach(fc->fe); fc->fe = NULL; return -EINVAL; } @@ -584,8 +582,10 @@ int flexcop_frontend_init(struct flexcop_device *fc) void flexcop_frontend_exit(struct flexcop_device *fc) { - if (fc->init_state & FC_STATE_FE_INIT) + if (fc->init_state & FC_STATE_FE_INIT) { dvb_unregister_frontend(fc->fe); + dvb_frontend_detach(fc->fe); + } fc->init_state &= ~FC_STATE_FE_INIT; } diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig index 7d0ee1ab290..ae2ff5dc238 100644 --- a/drivers/media/dvb/bt8xx/Kconfig +++ b/drivers/media/dvb/bt8xx/Kconfig @@ -2,13 +2,13 @@ config DVB_BT8XX tristate "BT8xx based PCI cards" depends on DVB_CORE && PCI && I2C && VIDEO_BT848 select DVB_PLL - select DVB_MT352 - select DVB_SP887X - select DVB_NXT6000 - select DVB_CX24110 - select DVB_OR51211 - select DVB_LGDT330X - select DVB_ZL10353 + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_SP887X if !DVB_FE_CUSTOMISE + select DVB_NXT6000 if !DVB_FE_CUSTOMISE + select DVB_CX24110 if !DVB_FE_CUSTOMISE + select DVB_OR51211 if !DVB_FE_CUSTOMISE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE select FW_LOADER help Support for PCI cards based on the Bt8xx PCI bridge. Examples are diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c index 06ac899a9a2..9f72b7000c0 100644 --- a/drivers/media/dvb/bt8xx/dst.c +++ b/drivers/media/dvb/bt8xx/dst.c @@ -1715,6 +1715,15 @@ static int dst_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_paramet static void dst_release(struct dvb_frontend *fe) { struct dst_state *state = fe->demodulator_priv; + if (state->dst_ca) { + dvb_unregister_device(state->dst_ca); +#ifdef CONFIG_DVB_CORE_ATTACH + symbol_put(dst_ca_attach); +#endif + } +#ifdef CONFIG_DVB_CORE_ATTACH + symbol_put(dst_attach); +#endif kfree(state); } diff --git a/drivers/media/dvb/bt8xx/dst_ca.c b/drivers/media/dvb/bt8xx/dst_ca.c index fa923b9b346..240ad084fa7 100644 --- a/drivers/media/dvb/bt8xx/dst_ca.c +++ b/drivers/media/dvb/bt8xx/dst_ca.c @@ -699,12 +699,17 @@ static struct dvb_device dvbdev_ca = { .fops = &dst_ca_fops }; -int dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter) +struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter) { struct dvb_device *dvbdev; + dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device"); - dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA); - return 0; + if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) { + dst->dst_ca = dvbdev; + return dst->dst_ca; + } + + return NULL; } EXPORT_SYMBOL(dst_ca_attach); diff --git a/drivers/media/dvb/bt8xx/dst_common.h b/drivers/media/dvb/bt8xx/dst_common.h index 0677b047b3a..3bf084f2e52 100644 --- a/drivers/media/dvb/bt8xx/dst_common.h +++ b/drivers/media/dvb/bt8xx/dst_common.h @@ -140,6 +140,7 @@ struct dst_state { char *tuner_name; struct mutex dst_mutex; u8 fw_name[8]; + struct dvb_device *dst_ca; }; struct tuner_types { @@ -178,7 +179,7 @@ int write_dst(struct dst_state *state, u8 * data, u8 len); int read_dst(struct dst_state *state, u8 * ret, u8 len); u8 dst_check_sum(u8 * buf, u32 len); struct dst_state* dst_attach(struct dst_state* state, struct dvb_adapter *dvb_adapter); -int dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter); +struct dvb_device *dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter); int dst_gpio_outb(struct dst_state* state, u32 mask, u32 enbb, u32 outhigh, int delay); int dst_command(struct dst_state* state, u8 * data, u8 len); diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c index b715b972d2f..fb6c4cc8477 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -67,7 +67,7 @@ static void dvb_bt8xx_task(unsigned long data) static int dvb_bt8xx_start_feed(struct dvb_demux_feed *dvbdmxfeed) { - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct dvb_demux*dvbdmx = dvbdmxfeed->demux; struct dvb_bt8xx_card *card = dvbdmx->priv; int rc; @@ -595,15 +595,14 @@ static void lgdt330x_reset(struct dvb_bt8xx_card *bt) static void frontend_init(struct dvb_bt8xx_card *card, u32 type) { - int ret; struct dst_state* state = NULL; switch(type) { case BTTV_BOARD_DVICO_DVBT_LITE: - card->fe = mt352_attach(&thomson_dtt7579_config, card->i2c_adapter); + card->fe = dvb_attach(mt352_attach, &thomson_dtt7579_config, card->i2c_adapter); if (card->fe == NULL) - card->fe = zl10353_attach(&thomson_dtt7579_zl10353_config, + card->fe = dvb_attach(zl10353_attach, &thomson_dtt7579_zl10353_config, card->i2c_adapter); if (card->fe != NULL) { @@ -615,7 +614,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE: lgdt330x_reset(card); - card->fe = lgdt330x_attach(&tdvs_tua6034_config, card->i2c_adapter); + card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter); if (card->fe != NULL) { card->fe->ops.tuner_ops.set_params = tdvs_tua6034_tuner_set_params; dprintk ("dvb_bt8xx: lgdt330x detected\n"); @@ -630,7 +629,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) /* Old Nebula (marked (c)2003 on high profile pci card) has nxt6000 demod */ digitv_alps_tded4_reset(card); - card->fe = nxt6000_attach(&vp3021_alps_tded4_config, card->i2c_adapter); + card->fe = dvb_attach(nxt6000_attach, &vp3021_alps_tded4_config, card->i2c_adapter); if (card->fe != NULL) { card->fe->ops.tuner_ops.set_params = vp3021_alps_tded4_tuner_set_params; dprintk ("dvb_bt8xx: an nxt6000 was detected on your digitv card\n"); @@ -639,7 +638,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) /* New Nebula (marked (c)2005 on low profile pci card) has mt352 demod */ digitv_alps_tded4_reset(card); - card->fe = mt352_attach(&digitv_alps_tded4_config, card->i2c_adapter); + card->fe = dvb_attach(mt352_attach, &digitv_alps_tded4_config, card->i2c_adapter); if (card->fe != NULL) { card->fe->ops.tuner_ops.calc_regs = digitv_alps_tded4_tuner_calc_regs; @@ -648,14 +647,14 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) break; case BTTV_BOARD_AVDVBT_761: - card->fe = sp887x_attach(µtune_mt7202dtf_config, card->i2c_adapter); + card->fe = dvb_attach(sp887x_attach, µtune_mt7202dtf_config, card->i2c_adapter); if (card->fe) { card->fe->ops.tuner_ops.set_params = microtune_mt7202dtf_tuner_set_params; } break; case BTTV_BOARD_AVDVBT_771: - card->fe = mt352_attach(&advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter); + card->fe = dvb_attach(mt352_attach, &advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter); if (card->fe != NULL) { card->fe->ops.tuner_ops.calc_regs = advbt771_samsung_tdtc9251dh0_tuner_calc_regs; card->fe->ops.info.frequency_min = 174000000; @@ -670,22 +669,21 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) state->config = &dst_config; state->i2c = card->i2c_adapter; state->bt = card->bt; - + state->dst_ca = NULL; /* DST is not a frontend, attaching the ASIC */ - if ((dst_attach(state, &card->dvb_adapter)) == NULL) { + if (dvb_attach(dst_attach, state, &card->dvb_adapter) == NULL) { printk("%s: Could not find a Twinhan DST.\n", __FUNCTION__); break; } - card->fe = &state->frontend; - /* Attach other DST peripherals if any */ /* Conditional Access device */ + card->fe = &state->frontend; if (state->dst_hw_cap & DST_TYPE_HAS_CA) - ret = dst_ca_attach(state, &card->dvb_adapter); + dvb_attach(dst_ca_attach, state, &card->dvb_adapter); break; case BTTV_BOARD_PINNACLESAT: - card->fe = cx24110_attach(&pctvsat_config, card->i2c_adapter); + card->fe = dvb_attach(cx24110_attach, &pctvsat_config, card->i2c_adapter); if (card->fe) { card->fe->ops.tuner_ops.init = pinnsat_tuner_init; card->fe->ops.tuner_ops.sleep = pinnsat_tuner_sleep; @@ -694,7 +692,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) break; case BTTV_BOARD_PC_HDTV: - card->fe = or51211_attach(&or51211_config, card->i2c_adapter); + card->fe = dvb_attach(or51211_attach, &or51211_config, card->i2c_adapter); break; } @@ -707,8 +705,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) else if (dvb_register_frontend(&card->dvb_adapter, card->fe)) { printk("dvb-bt8xx: Frontend registration failed!\n"); - if (card->fe->ops.release) - card->fe->ops.release(card->fe); + dvb_frontend_detach(card->fe); card->fe = NULL; } } @@ -925,8 +922,10 @@ static void dvb_bt8xx_remove(struct bttv_sub_device *sub) card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); dvb_dmxdev_release(&card->dmxdev); dvb_dmx_release(&card->demux); - if (card->fe) + if (card->fe) { dvb_unregister_frontend(card->fe); + dvb_frontend_detach(card->fe); + } dvb_unregister_adapter(&card->dvb_adapter); kfree(card); diff --git a/drivers/media/dvb/dvb-core/Kconfig b/drivers/media/dvb/dvb-core/Kconfig index 12ee912a570..e46eae3b9be 100644 --- a/drivers/media/dvb/dvb-core/Kconfig +++ b/drivers/media/dvb/dvb-core/Kconfig @@ -9,3 +9,16 @@ config DVB_CORE in-kernel drivers will select this automatically if needed. If unsure say N. +config DVB_CORE_ATTACH + bool "Load and attach frontend modules as needed" + depends on DVB_CORE + depends on MODULES + help + Remove the static dependency of DVB card drivers on all + frontend modules for all possible card variants. Instead, + allow the card drivers to only load the frontend modules + they require. This saves several KBytes of memory. + + Note: You will need moudule-init-tools v3.2 or later for this feature. + + If unsure say Y. diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 57b34cda99f..3dd5dbafb33 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1105,18 +1105,42 @@ int dvb_unregister_frontend(struct dvb_frontend* fe) mutex_lock(&frontend_mutex); dvb_unregister_device (fepriv->dvbdev); dvb_frontend_stop (fe); - if (fe->ops.tuner_ops.release) { - fe->ops.tuner_ops.release(fe); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - } - if (fe->ops.release) - fe->ops.release(fe); - else - printk("dvb_frontend: Demodulator (%s) does not have a release callback!\n", fe->ops.info.name); + /* fe is invalid now */ kfree(fepriv); mutex_unlock(&frontend_mutex); return 0; } EXPORT_SYMBOL(dvb_unregister_frontend); + +#ifdef CONFIG_DVB_CORE_ATTACH +void dvb_frontend_detach(struct dvb_frontend* fe) +{ + void *ptr; + + if (fe->ops.release_sec) { + fe->ops.release_sec(fe); + symbol_put_addr(fe->ops.release_sec); + } + if (fe->ops.tuner_ops.release) { + fe->ops.tuner_ops.release(fe); + symbol_put_addr(fe->ops.tuner_ops.release); + } + ptr = (void*)fe->ops.release; + if (ptr) { + fe->ops.release(fe); + symbol_put_addr(ptr); + } +} +#else +void dvb_frontend_detach(struct dvb_frontend* fe) +{ + if (fe->ops.release_sec) + fe->ops.release_sec(fe); + if (fe->ops.tuner_ops.release) + fe->ops.tuner_ops.release(fe); + if (fe->ops.release) + fe->ops.release(fe); +} +#endif +EXPORT_SYMBOL(dvb_frontend_detach); diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index 2887e2b862a..e5d5028b369 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -92,10 +92,13 @@ struct dvb_frontend_ops { struct dvb_frontend_info info; void (*release)(struct dvb_frontend* fe); + void (*release_sec)(struct dvb_frontend* fe); int (*init)(struct dvb_frontend* fe); int (*sleep)(struct dvb_frontend* fe); + int (*write)(struct dvb_frontend* fe, u8* buf, int len); + /* if this is set, it overrides the default swzigzag */ int (*tune)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, @@ -147,7 +150,7 @@ struct dvb_frontend { void* demodulator_priv; void* tuner_priv; void* frontend_priv; - void* misc_priv; + void* sec_priv; }; extern int dvb_register_frontend(struct dvb_adapter* dvb, @@ -155,6 +158,8 @@ extern int dvb_register_frontend(struct dvb_adapter* dvb, extern int dvb_unregister_frontend(struct dvb_frontend* fe); +extern void dvb_frontend_detach(struct dvb_frontend* fe); + extern void dvb_frontend_reinitialise(struct dvb_frontend *fe); extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec); diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h index 7a7f75fd168..620e7887b3d 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.h +++ b/drivers/media/dvb/dvb-core/dvbdev.h @@ -102,4 +102,26 @@ extern int dvb_usercopy(struct inode *inode, struct file *file, int (*func)(struct inode *inode, struct file *file, unsigned int cmd, void *arg)); +/** generic DVB attach function. */ +#ifdef CONFIG_DVB_CORE_ATTACH +#define dvb_attach(FUNCTION, ARGS...) ({ \ + void *__r = NULL; \ + typeof(&FUNCTION) __a = symbol_request(FUNCTION); \ + if (__a) { \ + __r = (void *) __a(ARGS); \ + if (__r == NULL) \ + symbol_put(FUNCTION); \ + } else { \ + printk(KERN_ERR "DVB: Unable to find symbol "#FUNCTION"()\n"); \ + } \ + __r; \ +}) + +#else +#define dvb_attach(FUNCTION, ARGS...) ({ \ + FUNCTION(ARGS); \ +}) + +#endif + #endif /* #ifndef _DVBDEV_H_ */ diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 75824b77198..0a3c35399be 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -26,6 +26,7 @@ config DVB_USB_A800 tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)" depends on DVB_USB select DVB_DIB3000MC + select DVB_TUNER_MT2060 help Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver. @@ -33,6 +34,7 @@ config DVB_USB_DIBUSB_MB tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)" depends on DVB_USB select DVB_DIB3000MB + select DVB_TUNER_MT2060 help Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-B demodulator. @@ -65,6 +67,7 @@ config DVB_USB_DIBUSB_MC tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)" depends on DVB_USB select DVB_DIB3000MC + select DVB_TUNER_MT2060 help Support for 2.0 DVB-T receivers based on reference designs made by DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-C/P demodulator. @@ -80,16 +83,17 @@ config DVB_USB_UMT_010 tristate "HanfTek UMT-010 DVB-T USB2.0 support" depends on DVB_USB select DVB_DIB3000MC + select DVB_TUNER_MT2060 help Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver. config DVB_USB_CXUSB tristate "Conexant USB2.0 hybrid reference design support" depends on DVB_USB - select DVB_CX22702 - select DVB_LGDT330X - select DVB_MT352 - select DVB_ZL10353 + select DVB_CX22702 if !DVB_FE_CUSTOMISE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE help Say Y here to support the Conexant USB2.0 hybrid reference design. Currently, only DVB and ATSC modes are supported, analog mode @@ -101,8 +105,8 @@ config DVB_USB_CXUSB config DVB_USB_DIGITV tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support" depends on DVB_USB - select DVB_NXT6000 - select DVB_MT352 + select DVB_NXT6000 if !DVB_FE_CUSTOMISE + select DVB_MT352 if !DVB_FE_CUSTOMISE help Say Y here to support the Nebula Electronics uDigitV USB2.0 DVB-T receiver. @@ -145,6 +149,7 @@ config DVB_USB_NOVA_T_USB2 tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support" depends on DVB_USB select DVB_DIB3000MC + select DVB_TUNER_MT2060 help Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c index ce44aa6bbb8..df0c384bd4c 100644 --- a/drivers/media/dvb/dvb-usb/a800.c +++ b/drivers/media/dvb/dvb-usb/a800.c @@ -26,6 +26,13 @@ static int a800_power_ctrl(struct dvb_usb_device *d, int onoff) return 0; } +/* assure to put cold to 0 for iManufacturer == 1 */ +static int a800_identify_state(struct usb_device *udev, struct dvb_usb_properties *props,struct dvb_usb_device_description **desc, int *cold) +{ + *cold = udev->descriptor.iManufacturer != 1; + return 0; +} + static struct dvb_usb_rc_key a800_rc_keys[] = { { 0x02, 0x01, KEY_PROG1 }, /* SOURCE */ { 0x02, 0x00, KEY_POWER }, /* POWER */ @@ -113,6 +120,7 @@ static struct dvb_usb_properties a800_properties = { .power_ctrl = a800_power_ctrl, .frontend_attach = dibusb_dib3000mc_frontend_attach, .tuner_attach = dibusb_dib3000mc_tuner_attach, + .identify_state = a800_identify_state, .rc_interval = DEFAULT_RC_INTERVAL, .rc_key_map = a800_rc_keys, diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index ae23bdde42a..c710c0176e0 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -349,6 +349,7 @@ static struct mt352_config cxusb_dee1601_config = { static struct zl10353_config cxusb_zl10353_dee1601_config = { .demod_address = 0x0f, + .parallel_ts = 1, }; static struct mt352_config cxusb_mt352_config = { @@ -409,7 +410,7 @@ static int cxusb_cx22702_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, &b, 1); - if ((d->fe = cx22702_attach(&cxusb_cx22702_config, &d->i2c_adap)) != NULL) + if ((d->fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config, &d->i2c_adap)) != NULL) return 0; return -EIO; @@ -422,7 +423,7 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0); - if ((d->fe = lgdt330x_attach(&cxusb_lgdt3303_config, &d->i2c_adap)) != NULL) + if ((d->fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config, &d->i2c_adap)) != NULL) return 0; return -EIO; @@ -435,7 +436,7 @@ static int cxusb_mt352_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0); - if ((d->fe = mt352_attach(&cxusb_mt352_config, &d->i2c_adap)) != NULL) + if ((d->fe = dvb_attach(mt352_attach, &cxusb_mt352_config, &d->i2c_adap)) != NULL) return 0; return -EIO; @@ -448,8 +449,8 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0); - if (((d->fe = mt352_attach(&cxusb_dee1601_config, &d->i2c_adap)) != NULL) || - ((d->fe = zl10353_attach(&cxusb_zl10353_dee1601_config, &d->i2c_adap)) != NULL)) + if (((d->fe = dvb_attach(mt352_attach, &cxusb_dee1601_config, &d->i2c_adap)) != NULL) || + ((d->fe = dvb_attach(zl10353_attach, &cxusb_zl10353_dee1601_config, &d->i2c_adap)) != NULL)) return 0; return -EIO; diff --git a/drivers/media/dvb/dvb-usb/dibusb-common.c b/drivers/media/dvb/dvb-usb/dibusb-common.c index abd75b4a350..124e25ac53b 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-common.c +++ b/drivers/media/dvb/dvb-usb/dibusb-common.c @@ -131,9 +131,6 @@ static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EAGAIN; - if (num > 2) - warn("more than 2 i2c messages at a time is not handled yet. TODO."); - for (i = 0; i < num; i++) { /* write/read request */ if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { @@ -168,31 +165,137 @@ int dibusb_read_eeprom_byte(struct dvb_usb_device *d, u8 offs, u8 *val) } EXPORT_SYMBOL(dibusb_read_eeprom_byte); +/* 3000MC/P stuff */ +// Config Adjacent channels Perf -cal22 +static struct dibx000_agc_config dib3000p_mt2060_agc_config = { + .band_caps = BAND_VHF | BAND_UHF, + .setup = (0 << 15) | (0 << 14) | (1 << 13) | (1 << 12) | (29 << 0), + + .agc1_max = 48497, + .agc1_min = 23593, + .agc2_max = 46531, + .agc2_min = 24904, + + .agc1_pt1 = 0x65, + .agc1_pt2 = 0x69, + + .agc1_slope1 = 0x51, + .agc1_slope2 = 0x27, + + .agc2_pt1 = 0, + .agc2_pt2 = 0x33, + + .agc2_slope1 = 0x35, + .agc2_slope2 = 0x37, +}; + +static struct dib3000mc_config stk3000p_dib3000p_config = { + &dib3000p_mt2060_agc_config, + + .max_time = 0x196, + .ln_adc_level = 0x1cc7, + + .output_mpeg2_in_188_bytes = 1, +}; + +static struct dibx000_agc_config dib3000p_panasonic_agc_config = { + .setup = (0 << 15) | (0 << 14) | (1 << 13) | (1 << 12) | (29 << 0), + + .agc1_max = 56361, + .agc1_min = 22282, + .agc2_max = 47841, + .agc2_min = 36045, + + .agc1_pt1 = 0x3b, + .agc1_pt2 = 0x6b, + + .agc1_slope1 = 0x55, + .agc1_slope2 = 0x1d, + + .agc2_pt1 = 0, + .agc2_pt2 = 0x0a, + + .agc2_slope1 = 0x95, + .agc2_slope2 = 0x1e, +}; + +static struct dib3000mc_config mod3000p_dib3000p_config = { + &dib3000p_panasonic_agc_config, + + .max_time = 0x51, + .ln_adc_level = 0x1cc7, + + .output_mpeg2_in_188_bytes = 1, +}; + int dibusb_dib3000mc_frontend_attach(struct dvb_usb_device *d) { - struct dib3000_config demod_cfg; - struct dibusb_state *st = d->priv; - - for (demod_cfg.demod_address = 0x8; demod_cfg.demod_address < 0xd; demod_cfg.demod_address++) - if ((d->fe = dib3000mc_attach(&demod_cfg,&d->i2c_adap,&st->ops)) != NULL) { - d->fe->ops.tuner_ops.init = dvb_usb_tuner_init_i2c; - d->fe->ops.tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; - d->tuner_pass_ctrl = st->ops.tuner_pass_ctrl; - return 0; + if (dib3000mc_attach(&d->i2c_adap, 1, DEFAULT_DIB3000P_I2C_ADDRESS, 0, &mod3000p_dib3000p_config, &d->fe) == 0 || + dib3000mc_attach(&d->i2c_adap, 1, DEFAULT_DIB3000MC_I2C_ADDRESS, 0, &mod3000p_dib3000p_config, &d->fe) == 0) { + if (d->priv != NULL) { + struct dibusb_state *st = d->priv; + st->ops.pid_parse = dib3000mc_pid_parse; + st->ops.pid_ctrl = dib3000mc_pid_control; } - + return 0; + } return -ENODEV; } EXPORT_SYMBOL(dibusb_dib3000mc_frontend_attach); +static struct mt2060_config stk3000p_mt2060_config = { + 0x60 +}; + int dibusb_dib3000mc_tuner_attach (struct dvb_usb_device *d) { - d->pll_addr = 0x60; - d->pll_desc = &dvb_pll_env57h1xd5; - - d->fe->ops.tuner_ops.init = dvb_usb_tuner_init_i2c; - d->fe->ops.tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; + struct dibusb_state *st = d->priv; + int ret; + u8 a,b; + u16 if1 = 1220; + struct i2c_adapter *tun_i2c; + + // First IF calibration for Liteon Sticks + if (d->udev->descriptor.idVendor == USB_VID_LITEON && + d->udev->descriptor.idProduct == USB_PID_LITEON_DVB_T_WARM) { + + dibusb_read_eeprom_byte(d,0x7E,&a); + dibusb_read_eeprom_byte(d,0x7F,&b); + + if (a == 0x00) + if1 += b; + else if (a == 0x80) + if1 -= b; + else + warn("LITE-ON DVB-T: Strange IF1 calibration :%2X %2X\n", a, b); + + } else if (d->udev->descriptor.idVendor == USB_VID_DIBCOM && + d->udev->descriptor.idProduct == USB_PID_DIBCOM_MOD3001_WARM) { + u8 desc; + dibusb_read_eeprom_byte(d, 7, &desc); + if (desc == 2) { + a = 127; + do { + dibusb_read_eeprom_byte(d, a, &desc); + a--; + } while (a > 7 && (desc == 0xff || desc == 0x00)); + if (desc & 0x80) + if1 -= (0xff - desc); + else + if1 += desc; + } + } + tun_i2c = dib3000mc_get_tuner_i2c_master(d->fe, 1); + if ((ret = mt2060_attach(d->fe, tun_i2c, &stk3000p_mt2060_config, if1)) != 0) { + /* not found - use panasonic pll parameters */ + if (dvb_pll_attach(d->fe, 0x60, tun_i2c, &dvb_pll_env57h1xd5) == NULL) + return -ENOMEM; + } else { + st->mt2060_present = 1; + /* set the correct parameters for the dib3000p */ + dib3000mc_set_config(d->fe, &stk3000p_dib3000p_config); + } return 0; } EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach); @@ -267,6 +370,67 @@ struct dvb_usb_rc_key dibusb_rc_keys[] = { { 0x86, 0x1e, KEY_DOWN }, { 0x86, 0x1f, KEY_LEFT }, { 0x86, 0x1b, KEY_RIGHT }, + + /* Key codes for the DiBcom MOD3000 remote. */ + { 0x80, 0x00, KEY_MUTE }, + { 0x80, 0x01, KEY_TEXT }, + { 0x80, 0x02, KEY_HOME }, + { 0x80, 0x03, KEY_POWER }, + + { 0x80, 0x04, KEY_RED }, + { 0x80, 0x05, KEY_GREEN }, + { 0x80, 0x06, KEY_YELLOW }, + { 0x80, 0x07, KEY_BLUE }, + + { 0x80, 0x08, KEY_DVD }, + { 0x80, 0x09, KEY_AUDIO }, + { 0x80, 0x0a, KEY_MEDIA }, /* Pictures */ + { 0x80, 0x0b, KEY_VIDEO }, + + { 0x80, 0x0c, KEY_BACK }, + { 0x80, 0x0d, KEY_UP }, + { 0x80, 0x0e, KEY_RADIO }, + { 0x80, 0x0f, KEY_EPG }, + + { 0x80, 0x10, KEY_LEFT }, + { 0x80, 0x11, KEY_OK }, + { 0x80, 0x12, KEY_RIGHT }, + { 0x80, 0x13, KEY_UNKNOWN }, /* SAP */ + + { 0x80, 0x14, KEY_TV }, + { 0x80, 0x15, KEY_DOWN }, + { 0x80, 0x16, KEY_MENU }, /* DVD Menu */ + { 0x80, 0x17, KEY_LAST }, + + { 0x80, 0x18, KEY_RECORD }, + { 0x80, 0x19, KEY_STOP }, + { 0x80, 0x1a, KEY_PAUSE }, + { 0x80, 0x1b, KEY_PLAY }, + + { 0x80, 0x1c, KEY_PREVIOUS }, + { 0x80, 0x1d, KEY_REWIND }, + { 0x80, 0x1e, KEY_FASTFORWARD }, + { 0x80, 0x1f, KEY_NEXT}, + + { 0x80, 0x40, KEY_1 }, + { 0x80, 0x41, KEY_2 }, + { 0x80, 0x42, KEY_3 }, + { 0x80, 0x43, KEY_CHANNELUP }, + + { 0x80, 0x44, KEY_4 }, + { 0x80, 0x45, KEY_5 }, + { 0x80, 0x46, KEY_6 }, + { 0x80, 0x47, KEY_CHANNELDOWN }, + + { 0x80, 0x48, KEY_7 }, + { 0x80, 0x49, KEY_8 }, + { 0x80, 0x4a, KEY_9 }, + { 0x80, 0x4b, KEY_VOLUMEUP }, + + { 0x80, 0x4c, KEY_CLEAR }, + { 0x80, 0x4d, KEY_0 }, + { 0x80, 0x4e, KEY_ENTER }, + { 0x80, 0x4f, KEY_VOLUMEDOWN }, }; EXPORT_SYMBOL(dibusb_rc_keys); diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c index f4c45f386eb..effd34cc4b0 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mb.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c @@ -21,11 +21,11 @@ static int dibusb_dib3000mb_frontend_attach(struct dvb_usb_device *d) demod_cfg.demod_address = 0x8; - if ((d->fe = dib3000mb_attach(&demod_cfg,&d->i2c_adap,&st->ops)) == NULL) { - d->fe->ops.tuner_ops.init = dvb_usb_tuner_init_i2c; - d->fe->ops.tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; + if ((d->fe = dib3000mb_attach(&demod_cfg,&d->i2c_adap,&st->ops)) == NULL) return -ENODEV; - } + + d->fe->ops.tuner_ops.init = dvb_usb_tuner_init_i2c; + d->fe->ops.tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; d->tuner_pass_ctrl = st->ops.tuner_pass_ctrl; @@ -169,7 +169,7 @@ static struct dvb_usb_properties dibusb1_1_properties = { .rc_interval = DEFAULT_RC_INTERVAL, .rc_key_map = dibusb_rc_keys, - .rc_key_map_size = 63, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ .rc_query = dibusb_rc_query, .i2c_algo = &dibusb_i2c_algo, @@ -247,7 +247,7 @@ static struct dvb_usb_properties dibusb1_1_an2235_properties = { .rc_interval = DEFAULT_RC_INTERVAL, .rc_key_map = dibusb_rc_keys, - .rc_key_map_size = 63, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ .rc_query = dibusb_rc_query, .i2c_algo = &dibusb_i2c_algo, @@ -272,8 +272,8 @@ static struct dvb_usb_properties dibusb1_1_an2235_properties = { #endif .devices = { { "Artec T1 USB1.1 TVBOX with AN2235", - { &dibusb_dib3000mb_table[20], NULL }, { &dibusb_dib3000mb_table[21], NULL }, + { &dibusb_dib3000mb_table[22], NULL }, }, #ifdef CONFIG_DVB_USB_DIBUSB_MB_FAULTY { "Artec T1 USB1.1 TVBOX with AN2235 (faulty USB IDs)", @@ -304,7 +304,7 @@ static struct dvb_usb_properties dibusb2_0b_properties = { .rc_interval = DEFAULT_RC_INTERVAL, .rc_key_map = dibusb_rc_keys, - .rc_key_map_size = 63, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ .rc_query = dibusb_rc_query, .i2c_algo = &dibusb_i2c_algo, @@ -355,7 +355,7 @@ static struct dvb_usb_properties artec_t1_usb2_properties = { .rc_interval = DEFAULT_RC_INTERVAL, .rc_key_map = dibusb_rc_keys, - .rc_key_map_size = 63, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ .rc_query = dibusb_rc_query, .i2c_algo = &dibusb_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c index 55802fba3c2..eca4082a61a 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mc.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c @@ -28,6 +28,17 @@ static struct usb_device_id dibusb_dib3000mc_table [] = { /* 00 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_COLD) }, /* 01 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_WARM) }, /* 02 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) }, +/* 03 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_WARM) }, // ( ? ) +/* 04 */ { USB_DEVICE(USB_VID_LITEON, USB_PID_LITEON_DVB_T_COLD) }, +/* 05 */ { USB_DEVICE(USB_VID_LITEON, USB_PID_LITEON_DVB_T_WARM) }, +/* 06 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_DIGIVOX_MINI_SL_COLD) }, +/* 07 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_DIGIVOX_MINI_SL_WARM) }, +/* 08 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB2_COLD) }, +/* 09 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB2_WARM) }, +/* 10 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14_COLD) }, +/* 11 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14_WARM) }, +/* 12 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_COLD) }, +/* 13 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_WARM) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, dibusb_dib3000mc_table); @@ -50,7 +61,7 @@ static struct dvb_usb_properties dibusb_mc_properties = { .rc_interval = DEFAULT_RC_INTERVAL, .rc_key_map = dibusb_rc_keys, - .rc_key_map_size = 63, /* FIXME */ + .rc_key_map_size = 111, /* FIXME */ .rc_query = dibusb_rc_query, .i2c_algo = &dibusb_i2c_algo, @@ -68,16 +79,38 @@ static struct dvb_usb_properties dibusb_mc_properties = { } }, - .num_device_descs = 2, + .num_device_descs = 7, .devices = { { "DiBcom USB2.0 DVB-T reference design (MOD3000P)", { &dibusb_dib3000mc_table[0], NULL }, { &dibusb_dib3000mc_table[1], NULL }, }, - { "Artec T1 USB2.0 TVBOX (please report the warm ID)", + { "Artec T1 USB2.0 TVBOX (please check the warm ID)", { &dibusb_dib3000mc_table[2], NULL }, - { NULL }, + { &dibusb_dib3000mc_table[3], NULL }, }, + { "LITE-ON USB2.0 DVB-T Tuner", + /* Also rebranded as Intuix S800, Toshiba */ + { &dibusb_dib3000mc_table[4], NULL }, + { &dibusb_dib3000mc_table[5], NULL }, + }, + { "MSI Digivox Mini SL", + { &dibusb_dib3000mc_table[6], NULL }, + { &dibusb_dib3000mc_table[7], NULL }, + }, + { "GRAND - USB2.0 DVB-T adapter", + { &dibusb_dib3000mc_table[8], NULL }, + { &dibusb_dib3000mc_table[9], NULL }, + }, + { "Artec T14 - USB2.0 DVB-T", + { &dibusb_dib3000mc_table[10], NULL }, + { &dibusb_dib3000mc_table[11], NULL }, + }, + { "Leadtek - USB2.0 Winfast DTV dongle", + { &dibusb_dib3000mc_table[12], NULL }, + { &dibusb_dib3000mc_table[13], NULL }, + }, + { NULL }, } }; diff --git a/drivers/media/dvb/dvb-usb/dibusb.h b/drivers/media/dvb/dvb-usb/dibusb.h index 2d99d05c7ea..a43f87480cf 100644 --- a/drivers/media/dvb/dvb-usb/dibusb.h +++ b/drivers/media/dvb/dvb-usb/dibusb.h @@ -17,6 +17,8 @@ #include "dvb-usb.h" #include "dib3000.h" +#include "dib3000mc.h" +#include "mt2060.h" /* * protocol of all dibusb related devices @@ -96,6 +98,7 @@ struct dibusb_state { struct dib_fe_xfer_ops ops; + int mt2060_present; /* for RC5 remote control */ int old_toggle; diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c index c14d9efb48f..01585448730 100644 --- a/drivers/media/dvb/dvb-usb/digitv.c +++ b/drivers/media/dvb/dvb-usb/digitv.c @@ -128,11 +128,11 @@ static struct nxt6000_config digitv_nxt6000_config = { static int digitv_frontend_attach(struct dvb_usb_device *d) { - if ((d->fe = mt352_attach(&digitv_mt352_config, &d->i2c_adap)) != NULL) { + if ((d->fe = dvb_attach(mt352_attach, &digitv_mt352_config, &d->i2c_adap)) != NULL) { d->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; return 0; } - if ((d->fe = nxt6000_attach(&digitv_nxt6000_config, &d->i2c_adap)) != NULL) { + if ((d->fe = dvb_attach(nxt6000_attach, &digitv_nxt6000_config, &d->i2c_adap)) != NULL) { d->fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params; return 0; } @@ -147,21 +147,91 @@ static int digitv_tuner_attach(struct dvb_usb_device *d) } static struct dvb_usb_rc_key digitv_rc_keys[] = { - { 0x00, 0x16, KEY_POWER }, /* dummy key */ + { 0x5f, 0x55, KEY_0 }, + { 0x6f, 0x55, KEY_1 }, + { 0x9f, 0x55, KEY_2 }, + { 0xaf, 0x55, KEY_3 }, + { 0x5f, 0x56, KEY_4 }, + { 0x6f, 0x56, KEY_5 }, + { 0x9f, 0x56, KEY_6 }, + { 0xaf, 0x56, KEY_7 }, + { 0x5f, 0x59, KEY_8 }, + { 0x6f, 0x59, KEY_9 }, + { 0x9f, 0x59, KEY_TV }, + { 0xaf, 0x59, KEY_AUX }, + { 0x5f, 0x5a, KEY_DVD }, + { 0x6f, 0x5a, KEY_POWER }, + { 0x9f, 0x5a, KEY_MHP }, /* labelled 'Picture' */ + { 0xaf, 0x5a, KEY_AUDIO }, + { 0x5f, 0x65, KEY_INFO }, + { 0x6f, 0x65, KEY_F13 }, /* 16:9 */ + { 0x9f, 0x65, KEY_F14 }, /* 14:9 */ + { 0xaf, 0x65, KEY_EPG }, + { 0x5f, 0x66, KEY_EXIT }, + { 0x6f, 0x66, KEY_MENU }, + { 0x9f, 0x66, KEY_UP }, + { 0xaf, 0x66, KEY_DOWN }, + { 0x5f, 0x69, KEY_LEFT }, + { 0x6f, 0x69, KEY_RIGHT }, + { 0x9f, 0x69, KEY_ENTER }, + { 0xaf, 0x69, KEY_CHANNELUP }, + { 0x5f, 0x6a, KEY_CHANNELDOWN }, + { 0x6f, 0x6a, KEY_VOLUMEUP }, + { 0x9f, 0x6a, KEY_VOLUMEDOWN }, + { 0xaf, 0x6a, KEY_RED }, + { 0x5f, 0x95, KEY_GREEN }, + { 0x6f, 0x95, KEY_YELLOW }, + { 0x9f, 0x95, KEY_BLUE }, + { 0xaf, 0x95, KEY_SUBTITLE }, + { 0x5f, 0x96, KEY_F15 }, /* AD */ + { 0x6f, 0x96, KEY_TEXT }, + { 0x9f, 0x96, KEY_MUTE }, + { 0xaf, 0x96, KEY_REWIND }, + { 0x5f, 0x99, KEY_STOP }, + { 0x6f, 0x99, KEY_PLAY }, + { 0x9f, 0x99, KEY_FASTFORWARD }, + { 0xaf, 0x99, KEY_F16 }, /* chapter */ + { 0x5f, 0x9a, KEY_PAUSE }, + { 0x6f, 0x9a, KEY_PLAY }, + { 0x9f, 0x9a, KEY_RECORD }, + { 0xaf, 0x9a, KEY_F17 }, /* picture in picture */ + { 0x5f, 0xa5, KEY_KPPLUS }, /* zoom in */ + { 0x6f, 0xa5, KEY_KPMINUS }, /* zoom out */ + { 0x9f, 0xa5, KEY_F18 }, /* capture */ + { 0xaf, 0xa5, KEY_F19 }, /* web */ + { 0x5f, 0xa6, KEY_EMAIL }, + { 0x6f, 0xa6, KEY_PHONE }, + { 0x9f, 0xa6, KEY_PC }, }; -/* TODO is it really the NEC protocol ? */ static int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { + int i; u8 key[5]; + u8 b[4] = { 0 }; + + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; digitv_ctrl_msg(d,USB_READ_REMOTE,0,NULL,0,&key[1],4); - /* TODO state, maybe it is VV ? */ + + /* Tell the device we've read the remote. Not sure how necessary + this is, but the Nebula SDK does it. */ + digitv_ctrl_msg(d,USB_WRITE_REMOTE,0,b,4,NULL,0); + + /* if something is inside the buffer, simulate key press */ if (key[1] != 0) - key[0] = 0x01; /* if something is inside the buffer, simulate key press */ + { + for (i = 0; i < d->props.rc_key_map_size; i++) { + if (d->props.rc_key_map[i].custom == key[1] && + d->props.rc_key_map[i].data == key[2]) { + *event = d->props.rc_key_map[i].event; + *state = REMOTE_KEY_PRESSED; + return 0; + } + } + } - /* call the universal NEC remote processor, to find out the key's state and event */ - dvb_usb_nec_rc_key_to_event(d,key,event,state); if (key[0] != 0) deb_rc("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]); return 0; diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c index 70afcfd141c..27af4e43647 100644 --- a/drivers/media/dvb/dvb-usb/dtt200u.c +++ b/drivers/media/dvb/dvb-usb/dtt200u.c @@ -93,6 +93,7 @@ static int dtt200u_frontend_attach(struct dvb_usb_device *d) } static struct dvb_usb_properties dtt200u_properties; +static struct dvb_usb_properties wt220u_fc_properties; static struct dvb_usb_properties wt220u_properties; static struct dvb_usb_properties wt220u_zl0353_properties; @@ -101,6 +102,7 @@ static int dtt200u_usb_probe(struct usb_interface *intf, { if (dvb_usb_device_init(intf,&dtt200u_properties,THIS_MODULE,NULL) == 0 || dvb_usb_device_init(intf,&wt220u_properties,THIS_MODULE,NULL) == 0 || + dvb_usb_device_init(intf,&wt220u_fc_properties,THIS_MODULE,NULL) == 0 || dvb_usb_device_init(intf,&wt220u_zl0353_properties,THIS_MODULE,NULL) == 0) return 0; @@ -114,6 +116,9 @@ static struct usb_device_id dtt200u_usb_table [] = { { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_WARM) }, { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_COLD) }, { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_WARM) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_COLD) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_WARM) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZAP250_COLD) }, { 0 }, }; MODULE_DEVICE_TABLE(usb, dtt200u_usb_table); @@ -193,13 +198,54 @@ static struct dvb_usb_properties wt220u_properties = { .num_device_descs = 1, .devices = { { .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)", - .cold_ids = { &dtt200u_usb_table[2], NULL }, + .cold_ids = { &dtt200u_usb_table[2], &dtt200u_usb_table[8], NULL }, .warm_ids = { &dtt200u_usb_table[3], NULL }, }, { NULL }, } }; +static struct dvb_usb_properties wt220u_fc_properties = { + .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_NEED_PID_FILTERING, + .pid_filter_count = 15, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-wt220u-fc03.fw", + + .power_ctrl = dtt200u_power_ctrl, + .streaming_ctrl = dtt200u_streaming_ctrl, + .pid_filter = dtt200u_pid_filter, + .frontend_attach = dtt200u_frontend_attach, + + .rc_interval = 300, + .rc_key_map = dtt200u_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys), + .rc_query = dtt200u_rc_query, + + .generic_bulk_ctrl_endpoint = 0x01, + + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 7, + .endpoint = 0x86, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + + .num_device_descs = 1, + .devices = { + { .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)", + .cold_ids = { &dtt200u_usb_table[6], NULL }, + .warm_ids = { &dtt200u_usb_table[7], NULL }, + }, + { NULL }, + } +}; + static struct dvb_usb_properties wt220u_zl0353_properties = { .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_NEED_PID_FILTERING, .pid_filter_count = 15, @@ -271,6 +317,6 @@ module_init(dtt200u_usb_module_init); module_exit(dtt200u_usb_module_exit); MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>"); -MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon DVB-T USB2.0 devices"); +MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon/Club3D DVB-T USB2.0 devices"); MODULE_VERSION("1.0"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c index ec631708c39..fe6208ada90 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c @@ -175,36 +175,36 @@ static int dvb_usb_fe_sleep(struct dvb_frontend *fe) int dvb_usb_fe_init(struct dvb_usb_device* d) { if (d->props.frontend_attach == NULL) { - err("strange '%s' doesn't want to attach a frontend.",d->desc->name); + err("strange: '%s' doesn't want to attach a frontend.",d->desc->name); return 0; } - d->props.frontend_attach(d); - /* re-assign sleep and wakeup functions */ - if (d->fe != NULL) { + if (d->props.frontend_attach(d) == 0 && d->fe != NULL) { d->fe_init = d->fe->ops.init; d->fe->ops.init = dvb_usb_fe_wakeup; d->fe_sleep = d->fe->ops.sleep; d->fe->ops.sleep = dvb_usb_fe_sleep; if (dvb_register_frontend(&d->dvb_adap, d->fe)) { err("Frontend registration failed."); - if (d->fe->ops.release) - d->fe->ops.release(d->fe); + dvb_frontend_detach(d->fe); d->fe = NULL; return -ENODEV; } + + /* only attach the tuner if the demod is there */ + if (d->props.tuner_attach != NULL) + d->props.tuner_attach(d); } else err("no frontend was attached by '%s'",d->desc->name); - if (d->props.tuner_attach != NULL) - d->props.tuner_attach(d); - return 0; } int dvb_usb_fe_exit(struct dvb_usb_device *d) { - if (d->fe != NULL) + if (d->fe != NULL) { dvb_unregister_frontend(d->fe); + dvb_frontend_detach(d->fe); + } return 0; } diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 95698918bc1..57a10de1d3d 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -10,51 +10,53 @@ #define _DVB_USB_IDS_H_ /* Vendor IDs */ -#define USB_VID_ADSTECH 0x06e1 -#define USB_VID_ANCHOR 0x0547 -#define USB_VID_WIDEVIEW 0x14aa -#define USB_VID_AVERMEDIA 0x07ca -#define USB_VID_COMPRO 0x185b -#define USB_VID_COMPRO_UNK 0x145f -#define USB_VID_CYPRESS 0x04b4 -#define USB_VID_DIBCOM 0x10b8 -#define USB_VID_DVICO 0x0fe9 -#define USB_VID_EMPIA 0xeb1a -#define USB_VID_GRANDTEC 0x5032 -#define USB_VID_HANFTEK 0x15f4 -#define USB_VID_HAUPPAUGE 0x2040 -#define USB_VID_HYPER_PALTEK 0x1025 -#define USB_VID_KWORLD 0xeb2a -#define USB_VID_KYE 0x0458 -#define USB_VID_MEDION 0x1660 -#define USB_VID_PINNACLE 0x2304 -#define USB_VID_VISIONPLUS 0x13d3 -#define USB_VID_TWINHAN 0x1822 -#define USB_VID_ULTIMA_ELECTRONIC 0x05d8 -#define USB_VID_GENPIX 0x09c0 +#define USB_VID_ADSTECH 0x06e1 +#define USB_VID_ANCHOR 0x0547 +#define USB_VID_AVERMEDIA 0x07ca +#define USB_VID_COMPRO 0x185b +#define USB_VID_COMPRO_UNK 0x145f +#define USB_VID_CYPRESS 0x04b4 +#define USB_VID_DIBCOM 0x10b8 +#define USB_VID_DVICO 0x0fe9 +#define USB_VID_EMPIA 0xeb1a +#define USB_VID_GENPIX 0x09c0 +#define USB_VID_GRANDTEC 0x5032 +#define USB_VID_HANFTEK 0x15f4 +#define USB_VID_HAUPPAUGE 0x2040 +#define USB_VID_HYPER_PALTEK 0x1025 +#define USB_VID_KWORLD 0xeb2a +#define USB_VID_KYE 0x0458 +#define USB_VID_LEADTEK 0x0413 +#define USB_VID_LITEON 0x04ca +#define USB_VID_MEDION 0x1660 +#define USB_VID_PINNACLE 0x2304 +#define USB_VID_VISIONPLUS 0x13d3 +#define USB_VID_TWINHAN 0x1822 +#define USB_VID_ULTIMA_ELECTRONIC 0x05d8 +#define USB_VID_WIDEVIEW 0x14aa /* Product IDs */ #define USB_PID_ADSTECH_USB2_COLD 0xa333 #define USB_PID_ADSTECH_USB2_WARM 0xa334 -#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 -#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 -#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800 -#define USB_PID_AVERMEDIA_DVBT_USB2_WARM 0xa801 -#define USB_PID_COMPRO_DVBU2000_COLD 0xd000 -#define USB_PID_COMPRO_DVBU2000_WARM 0xd001 -#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c -#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d +#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 +#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 +#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800 +#define USB_PID_AVERMEDIA_DVBT_USB2_WARM 0xa801 +#define USB_PID_COMPRO_DVBU2000_COLD 0xd000 +#define USB_PID_COMPRO_DVBU2000_WARM 0xd001 +#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c +#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d #define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064 -#define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065 +#define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065 #define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8 #define USB_PID_DIBCOM_MOD3000_WARM 0x0bb9 #define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6 #define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7 #define USB_PID_DIBCOM_STK7700 0x1e14 -#define USB_PID_DIBCOM_STK7700_REENUM 0x1e15 -#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 -#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 -#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 +#define USB_PID_DIBCOM_STK7700_REENUM 0x1e15 +#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 +#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 +#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 #define USB_PID_KWORLD_VSTREAM_COLD 0x17de #define USB_PID_KWORLD_VSTREAM_WARM 0x17df #define USB_PID_TWINHAN_VP7041_COLD 0x3201 @@ -69,25 +71,30 @@ #define USB_PID_DNTV_TINYUSB2_WARM 0x3224 #define USB_PID_ULTIMA_TVBOX_COLD 0x8105 #define USB_PID_ULTIMA_TVBOX_WARM 0x8106 -#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107 -#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108 -#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235 -#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109 -#define USB_PID_ULTIMA_TVBOX_USB2_WARM 0x810a -#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613 -#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002 -#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e -#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f -#define USB_PID_HANFTEK_UMT_010_COLD 0x0001 -#define USB_PID_HANFTEK_UMT_010_WARM 0x0015 +#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107 +#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108 +#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235 +#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109 +#define USB_PID_ULTIMA_TVBOX_USB2_WARM 0x810a +#define USB_PID_ARTEC_T14_COLD 0x810b +#define USB_PID_ARTEC_T14_WARM 0x810c +#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613 +#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002 +#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e +#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f +#define USB_PID_HANFTEK_UMT_010_COLD 0x0001 +#define USB_PID_HANFTEK_UMT_010_WARM 0x0015 #define USB_PID_DTT200U_COLD 0x0201 #define USB_PID_DTT200U_WARM 0x0301 -#define USB_PID_WT220U_COLD 0x0222 -#define USB_PID_WT220U_WARM 0x0221 +#define USB_PID_WT220U_ZAP250_COLD 0x0220 +#define USB_PID_WT220U_COLD 0x0222 +#define USB_PID_WT220U_WARM 0x0221 +#define USB_PID_WT220U_FC_COLD 0x0225 +#define USB_PID_WT220U_FC_WARM 0x0226 #define USB_PID_WT220U_ZL0353_COLD 0x022a #define USB_PID_WT220U_ZL0353_WARM 0x022b -#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300 -#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301 +#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300 +#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301 #define USB_PID_NEBULA_DIGITV 0x0201 #define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820 #define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500 @@ -103,8 +110,17 @@ #define USB_PID_MEDION_MD95700 0x0932 #define USB_PID_KYE_DVB_T_COLD 0x701e #define USB_PID_KYE_DVB_T_WARM 0x701f -#define USB_PID_PCTV_200E 0x020e -#define USB_PID_PCTV_400E 0x020f -#define USB_PID_GENPIX_8PSK_COLD 0x0200 -#define USB_PID_GENPIX_8PSK_WARM 0x0201 +#define USB_PID_PCTV_200E 0x020e +#define USB_PID_PCTV_400E 0x020f +#define USB_PID_LITEON_DVB_T_COLD 0xf000 +#define USB_PID_LITEON_DVB_T_WARM 0xf001 +#define USB_PID_DIGIVOX_MINI_SL_COLD 0xe360 +#define USB_PID_DIGIVOX_MINI_SL_WARM 0xe361 +#define USB_PID_GRANDTEC_DVBT_USB2_COLD 0x0bc6 +#define USB_PID_GRANDTEC_DVBT_USB2_WARM 0x0bc7 +#define USB_PID_WINFAST_DTV_DONGLE_COLD 0x6025 +#define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026 +#define USB_PID_GENPIX_8PSK_COLD 0x0200 +#define USB_PID_GENPIX_8PSK_WARM 0x0201 + #endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index e5c6d9835e0..380b2a45ee4 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -6,6 +6,7 @@ * This file contains functions for initializing the the input-device and for handling remote-control-queries. */ #include "dvb-usb-common.h" +#include <linux/usb/input.h> /* Remote-control poll function - called every dib->rc_query_interval ms to see * whether the remote control has received anything. @@ -96,7 +97,7 @@ int dvb_usb_remote_init(struct dvb_usb_device *d) return 0; usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys)); - strlcpy(d->rc_phys, "/ir0", sizeof(d->rc_phys)); + strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys)); d->rc_input_dev = input_allocate_device(); if (!d->rc_input_dev) @@ -107,6 +108,8 @@ int dvb_usb_remote_init(struct dvb_usb_device *d) d->rc_input_dev->keycodemax = KEY_MAX; d->rc_input_dev->name = "IR-receiver inside an USB DVB receiver"; d->rc_input_dev->phys = d->rc_phys; + usb_to_input_id(d->udev, &d->rc_input_dev->id); + d->rc_input_dev->cdev.dev = &d->udev->dev; /* set the bits for the keys */ deb_rc("key map size: %d\n", d->props.rc_key_map_size); diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c index 412039d8dba..79f0a02ce98 100644 --- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c +++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c @@ -156,7 +156,7 @@ static struct dvb_usb_properties nova_t_properties = { .pid_filter_count = 32, .usb_ctrl = CYPRESS_FX2, - .firmware = "dvb-usb-nova-t-usb2-01.fw", + .firmware = "dvb-usb-nova-t-usb2-02.fw", .size_of_priv = sizeof(struct dibusb_state), diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c index 97d74da0dad..418a0b70715 100644 --- a/drivers/media/dvb/dvb-usb/umt-010.c +++ b/drivers/media/dvb/dvb-usb/umt-010.c @@ -58,7 +58,7 @@ static int umt_mt352_frontend_attach(struct dvb_usb_device *d) umt_config.demod_init = umt_mt352_demod_init; umt_config.demod_address = 0xf; - d->fe = mt352_attach(&umt_config, &d->i2c_adap); + d->fe = dvb_attach(mt352_attach, &umt_config, &d->i2c_adap); return 0; } diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index db978555b1e..080fa257a0b 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -1,48 +1,73 @@ menu "Customise DVB Frontends" depends on DVB_CORE +config DVB_FE_CUSTOMISE + bool "Customise the frontend modules to build" + default N + help + This allows the user to deselect frontend drivers unnecessary + for their hardware from the build. Use this option with care + as deselecting frontends which are in fact necessary will result + in DVB devices which cannot be tuned due to lack of driver support. + + If unsure say N. + comment "DVB-S (satellite) frontends" depends on DVB_CORE config DVB_STV0299 tristate "ST STV0299 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_CX24110 tristate "Conexant CX24110 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_CX24123 tristate "Conexant CX24123 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_TDA8083 tristate "Philips TDA8083 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_MT312 tristate "Zarlink VP310/MT312 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_VES1X93 tristate "VLSI VES1893 or VES1993 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_S5H1420 tristate "Samsung S5H1420 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_TDA10086 + tristate "Philips TDA10086 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-S tuner module. Say Y when you want to support this frontend. @@ -52,6 +77,7 @@ comment "DVB-T (terrestrial) frontends" config DVB_SP8870 tristate "Spase sp8870 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE select FW_LOADER help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -64,6 +90,7 @@ config DVB_SP8870 config DVB_SP887X tristate "Spase sp887x based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE select FW_LOADER help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -76,24 +103,28 @@ config DVB_SP887X config DVB_CX22700 tristate "Conexant CX22700 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_CX22702 tristate "Conexant cx22702 demodulator (OFDM)" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_L64781 tristate "LSI L64781" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_TDA1004X tristate "Philips TDA10045H/TDA10046H based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE select FW_LOADER help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -107,24 +138,28 @@ config DVB_TDA1004X config DVB_NXT6000 tristate "NxtWave Communications NXT6000 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_MT352 tristate "Zarlink MT352 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_ZL10353 tristate "Zarlink ZL10353 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_DIB3000MB tristate "DiBcom 3000M-B" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. @@ -132,6 +167,7 @@ config DVB_DIB3000MB config DVB_DIB3000MC tristate "DiBcom 3000P/M-C" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. @@ -142,18 +178,21 @@ comment "DVB-C (cable) frontends" config DVB_VES1820 tristate "VLSI VES1820 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-C tuner module. Say Y when you want to support this frontend. config DVB_TDA10021 tristate "Philips TDA10021 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-C tuner module. Say Y when you want to support this frontend. config DVB_STV0297 tristate "ST STV0297 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help A DVB-C tuner module. Say Y when you want to support this frontend. @@ -163,6 +202,7 @@ comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends" config DVB_NXT200X tristate "NxtWave Communications NXT2002/NXT2004 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE select FW_LOADER help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want @@ -177,6 +217,7 @@ config DVB_NXT200X config DVB_OR51211 tristate "Oren OR51211 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE select FW_LOADER help An ATSC 8VSB tuner module. Say Y when you want to support this frontend. @@ -189,6 +230,7 @@ config DVB_OR51211 config DVB_OR51132 tristate "Oren OR51132 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE select FW_LOADER help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want @@ -204,6 +246,7 @@ config DVB_OR51132 config DVB_BCM3510 tristate "Broadcom BCM3510" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE select FW_LOADER help An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to @@ -212,28 +255,52 @@ config DVB_BCM3510 config DVB_LGDT330X tristate "LG Electronics LGDT3302/LGDT3303 based" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. - -comment "Miscellaneous devices" +comment "Tuners/PLL support" depends on DVB_CORE config DVB_PLL tristate depends on DVB_CORE && I2C +config DVB_TDA826X + tristate "Philips TDA826X silicon tuner" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S silicon tuner module. Say Y when you want to support this tuner. + +config DVB_TUNER_MT2060 + tristate "Microtune MT2060 silicon IF tuner" + help + A driver for the silicon IF tuner MT2060 from Microtune. + +comment "Miscellaneous devices" + depends on DVB_CORE + config DVB_LNBP21 tristate "LNBP21 SEC controller" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help An SEC control chip. config DVB_ISL6421 tristate "ISL6421 SEC controller" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE help An SEC control chip. +config DVB_TUA6100 + tristate "TUA6100 PLL" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVBS PLL chip. + endmenu diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 0e4880b6db1..dce9cf0c75c 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -11,8 +11,8 @@ obj-$(CONFIG_DVB_CX22700) += cx22700.o obj-$(CONFIG_DVB_CX24110) += cx24110.o obj-$(CONFIG_DVB_TDA8083) += tda8083.o obj-$(CONFIG_DVB_L64781) += l64781.o -obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o dib3000-common.o -obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dib3000-common.o +obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o +obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o obj-$(CONFIG_DVB_MT312) += mt312.o obj-$(CONFIG_DVB_VES1820) += ves1820.o obj-$(CONFIG_DVB_VES1X93) += ves1x93.o @@ -33,3 +33,7 @@ obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o obj-$(CONFIG_DVB_CX24123) += cx24123.o obj-$(CONFIG_DVB_LNBP21) += lnbp21.o obj-$(CONFIG_DVB_ISL6421) += isl6421.o +obj-$(CONFIG_DVB_TDA10086) += tda10086.o +obj-$(CONFIG_DVB_TDA826X) += tda826x.o +obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o +obj-$(CONFIG_DVB_TUA6100) += tua6100.o diff --git a/drivers/media/dvb/frontends/bcm3510.h b/drivers/media/dvb/frontends/bcm3510.h index 80f5d0953d0..6dfa839a702 100644 --- a/drivers/media/dvb/frontends/bcm3510.h +++ b/drivers/media/dvb/frontends/bcm3510.h @@ -34,7 +34,16 @@ struct bcm3510_config int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; +#if defined(CONFIG_DVB_BCM3510) || defined(CONFIG_DVB_BCM3510_MODULE) extern struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_BCM3510 #endif diff --git a/drivers/media/dvb/frontends/cx22700.h b/drivers/media/dvb/frontends/cx22700.h index dcd8979c1a1..10286cc29fb 100644 --- a/drivers/media/dvb/frontends/cx22700.h +++ b/drivers/media/dvb/frontends/cx22700.h @@ -31,7 +31,16 @@ struct cx22700_config u8 demod_address; }; +#if defined(CONFIG_DVB_CX22700) || defined(CONFIG_DVB_CX22700_MODULE) extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_CX22700 #endif // CX22700_H diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c index 4106d46c957..335219ebce2 100644 --- a/drivers/media/dvb/frontends/cx22702.c +++ b/drivers/media/dvb/frontends/cx22702.c @@ -399,7 +399,9 @@ static int cx22702_read_signal_strength(struct dvb_frontend* fe, u16* signal_str { struct cx22702_state* state = fe->demodulator_priv; - *signal_strength = cx22702_readreg (state, 0x23); + u16 rs_ber = 0; + rs_ber = cx22702_readreg (state, 0x23); + *signal_strength = (rs_ber << 8) | rs_ber; return 0; } diff --git a/drivers/media/dvb/frontends/cx22702.h b/drivers/media/dvb/frontends/cx22702.h index 7f2f241e5d4..bc217ddf02c 100644 --- a/drivers/media/dvb/frontends/cx22702.h +++ b/drivers/media/dvb/frontends/cx22702.h @@ -41,7 +41,16 @@ struct cx22702_config u8 output_mode; }; +#if defined(CONFIG_DVB_CX22702) || defined(CONFIG_DVB_CX22702_MODULE) extern struct dvb_frontend* cx22702_attach(const struct cx22702_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* cx22702_attach(const struct cx22702_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_CX22702 #endif // CX22702_H diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c index ce3c7398bac..ae96395217a 100644 --- a/drivers/media/dvb/frontends/cx24110.c +++ b/drivers/media/dvb/frontends/cx24110.c @@ -1,4 +1,4 @@ -/* + /* cx24110 - Single Chip Satellite Channel Receiver driver module Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de> based on @@ -311,16 +311,17 @@ static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate) } -int cx24110_pll_write (struct dvb_frontend* fe, u32 data) +static int _cx24110_pll_write (struct dvb_frontend* fe, u8 *buf, int len) { struct cx24110_state *state = fe->demodulator_priv; + if (len != 3) + return -EINVAL; + /* tuner data is 21 bits long, must be left-aligned in data */ /* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */ /* FIXME (low): add error handling, avoid infinite loops if HW fails... */ - dprintk("cx24110 debug: cx24108_write(%8.8x)\n",data); - cx24110_writereg(state,0x6d,0x30); /* auto mode at 62kHz */ cx24110_writereg(state,0x70,0x15); /* auto mode 21 bits */ @@ -329,19 +330,19 @@ int cx24110_pll_write (struct dvb_frontend* fe, u32 data) cx24110_writereg(state,0x72,0); /* write the topmost 8 bits */ - cx24110_writereg(state,0x72,(data>>24)&0xff); + cx24110_writereg(state,0x72,buf[0]); /* wait for the send to be completed */ while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) ; /* send another 8 bytes */ - cx24110_writereg(state,0x72,(data>>16)&0xff); + cx24110_writereg(state,0x72,buf[1]); while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) ; /* and the topmost 5 bits of this byte */ - cx24110_writereg(state,0x72,(data>>8)&0xff); + cx24110_writereg(state,0x72,buf[2]); while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) ; @@ -642,6 +643,7 @@ static struct dvb_frontend_ops cx24110_ops = { .release = cx24110_release, .init = cx24110_initfe, + .write = _cx24110_pll_write, .set_frontend = cx24110_set_frontend, .get_frontend = cx24110_get_frontend, .read_status = cx24110_read_status, @@ -664,4 +666,3 @@ MODULE_AUTHOR("Peter Hettkamp"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(cx24110_attach); -EXPORT_SYMBOL(cx24110_pll_write); diff --git a/drivers/media/dvb/frontends/cx24110.h b/drivers/media/dvb/frontends/cx24110.h index b354a64e0e7..c9d5ae250eb 100644 --- a/drivers/media/dvb/frontends/cx24110.h +++ b/drivers/media/dvb/frontends/cx24110.h @@ -33,9 +33,24 @@ struct cx24110_config u8 demod_address; }; +static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val) { + int r = 0; + u8 buf[] = {(u8) (val>>24), (u8) (val>>16), (u8) (val>>8)}; + if (fe->ops.write) + r = fe->ops.write(fe, buf, 3); + return r; +} + +#if defined(CONFIG_DVB_CX24110) || defined(CONFIG_DVB_CX24110_MODULE) extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, struct i2c_adapter* i2c); - -extern int cx24110_pll_write(struct dvb_frontend* fe, u32 data); +#else +static inline struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_CX24110 #endif // CX24110_H diff --git a/drivers/media/dvb/frontends/cx24123.c b/drivers/media/dvb/frontends/cx24123.c index 274a87b7a5d..62d69a6ea69 100644 --- a/drivers/media/dvb/frontends/cx24123.c +++ b/drivers/media/dvb/frontends/cx24123.c @@ -45,9 +45,6 @@ struct cx24123_state struct dvb_frontend frontend; - u32 lastber; - u16 snr; - /* Some PLL specifics for tuning */ u32 VCAarg; u32 VGAarg; @@ -194,7 +191,7 @@ static struct { {0x06, 0x31}, /* MPEG (default) */ {0x0b, 0x00}, /* Freq search start point (default) */ {0x0c, 0x00}, /* Demodulator sample gain (default) */ - {0x0d, 0x02}, /* Frequency search range = Fsymbol / 4 (default) */ + {0x0d, 0x7f}, /* Force driver to shift until the maximum (+-10 MHz) */ {0x0e, 0x03}, /* Default non-inverted, FEC 3/4 (default) */ {0x0f, 0xfe}, /* FEC search mask (all supported codes) */ {0x10, 0x01}, /* Default search inversion, no repeat (default) */ @@ -223,8 +220,9 @@ static struct { {0x44, 0x00}, /* Constellation (default) */ {0x45, 0x00}, /* Symbol count (default) */ {0x46, 0x0d}, /* Symbol rate estimator on (default) */ - {0x56, 0x41}, /* Various (default) */ + {0x56, 0xc1}, /* Error Counter = Viterbi BER */ {0x57, 0xff}, /* Error Counter Window (default) */ + {0x5c, 0x20}, /* Acquisition AFC Expiration window (default is 0x10) */ {0x67, 0x83}, /* Non-DCII symbol clock */ }; @@ -321,6 +319,12 @@ static int cx24123_set_fec(struct cx24123_state* state, fe_code_rate_t fec) if ( (fec < FEC_NONE) || (fec > FEC_AUTO) ) fec = FEC_AUTO; + /* Set the soft decision threshold */ + if(fec == FEC_1_2) + cx24123_writereg(state, 0x43, cx24123_readreg(state, 0x43) | 0x01); + else + cx24123_writereg(state, 0x43, cx24123_readreg(state, 0x43) & ~0x01); + switch (fec) { case FEC_1_2: dprintk("%s: set FEC to 1/2\n",__FUNCTION__); @@ -657,6 +661,10 @@ static int cx24123_initfe(struct dvb_frontend* fe) for (i = 0; i < sizeof(cx24123_regdata) / sizeof(cx24123_regdata[0]); i++) cx24123_writereg(state, cx24123_regdata[i].reg, cx24123_regdata[i].data); + /* Set the LNB polarity */ + if(state->config->lnb_polarity) + cx24123_writereg(state, 0x32, cx24123_readreg(state, 0x32) | 0x02); + return 0; } @@ -674,6 +682,9 @@ static int cx24123_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage case SEC_VOLTAGE_18: dprintk("%s: setting voltage 18V\n", __FUNCTION__); return cx24123_writereg(state, 0x29, val | 0x80); + case SEC_VOLTAGE_OFF: + /* already handled in cx88-dvb */ + return 0; default: return -EINVAL; }; @@ -776,13 +787,15 @@ static int cx24123_read_status(struct dvb_frontend* fe, fe_status_t* status) if (lock & 0x01) *status |= FE_HAS_SIGNAL; if (sync & 0x02) - *status |= FE_HAS_CARRIER; + *status |= FE_HAS_CARRIER; /* Phase locked */ if (sync & 0x04) *status |= FE_HAS_VITERBI; + + /* Reed-Solomon Status */ if (sync & 0x08) *status |= FE_HAS_SYNC; if (sync & 0x80) - *status |= FE_HAS_LOCK; + *status |= FE_HAS_LOCK; /*Full Sync */ return 0; } @@ -795,29 +808,13 @@ static int cx24123_read_ber(struct dvb_frontend* fe, u32* ber) { struct cx24123_state *state = fe->demodulator_priv; - state->lastber = - ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) | + /* The true bit error rate is this value divided by + the window size (set as 256 * 255) */ + *ber = ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) | (cx24123_readreg(state, 0x1d) << 8 | - cx24123_readreg(state, 0x1e)); - - /* Do the signal quality processing here, it's derived from the BER. */ - /* Scale the BER from a 24bit to a SNR 16 bit where higher = better */ - if (state->lastber < 5000) - state->snr = 655*100; - else if ( (state->lastber >= 5000) && (state->lastber < 55000) ) - state->snr = 655*90; - else if ( (state->lastber >= 55000) && (state->lastber < 150000) ) - state->snr = 655*80; - else if ( (state->lastber >= 150000) && (state->lastber < 250000) ) - state->snr = 655*70; - else if ( (state->lastber >= 250000) && (state->lastber < 450000) ) - state->snr = 655*65; - else - state->snr = 0; - - dprintk("%s: BER = %d, S/N index = %d\n",__FUNCTION__,state->lastber, state->snr); + cx24123_readreg(state, 0x1e)); - *ber = state->lastber; + dprintk("%s: BER = %d\n",__FUNCTION__,*ber); return 0; } @@ -825,6 +822,7 @@ static int cx24123_read_ber(struct dvb_frontend* fe, u32* ber) static int cx24123_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) { struct cx24123_state *state = fe->demodulator_priv; + *signal_strength = cx24123_readreg(state, 0x3b) << 8; /* larger = better */ dprintk("%s: Signal strength = %d\n",__FUNCTION__,*signal_strength); @@ -835,19 +833,13 @@ static int cx24123_read_signal_strength(struct dvb_frontend* fe, u16* signal_str static int cx24123_read_snr(struct dvb_frontend* fe, u16* snr) { struct cx24123_state *state = fe->demodulator_priv; - *snr = state->snr; - - dprintk("%s: read S/N index = %d\n",__FUNCTION__,*snr); - return 0; -} + /* Inverted raw Es/N0 count, totally bogus but better than the + BER threshold. */ + *snr = 65535 - (((u16)cx24123_readreg(state, 0x18) << 8) | + (u16)cx24123_readreg(state, 0x19)); -static int cx24123_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) -{ - struct cx24123_state *state = fe->demodulator_priv; - *ucblocks = state->lastber; - - dprintk("%s: ucblocks (ber) = %d\n",__FUNCTION__,*ucblocks); + dprintk("%s: read S/N index = %d\n",__FUNCTION__,*snr); return 0; } @@ -922,6 +914,29 @@ static int cx24123_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) return 0; } +static int cx24123_tune(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params, + unsigned int mode_flags, + int *delay, + fe_status_t *status) +{ + int retval = 0; + + if (params != NULL) + retval = cx24123_set_frontend(fe, params); + + if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) + cx24123_read_status(fe, status); + *delay = HZ/10; + + return retval; +} + +static int cx24123_get_algo(struct dvb_frontend *fe) +{ + return 1; //FE_ALGO_HW +} + static void cx24123_release(struct dvb_frontend* fe) { struct cx24123_state* state = fe->demodulator_priv; @@ -949,8 +964,6 @@ struct dvb_frontend* cx24123_attach(const struct cx24123_config* config, /* setup the state */ state->config = config; state->i2c = i2c; - state->lastber = 0; - state->snr = 0; state->VCAarg = 0; state->VGAarg = 0; state->bandselectarg = 0; @@ -1003,11 +1016,12 @@ static struct dvb_frontend_ops cx24123_ops = { .read_ber = cx24123_read_ber, .read_signal_strength = cx24123_read_signal_strength, .read_snr = cx24123_read_snr, - .read_ucblocks = cx24123_read_ucblocks, .diseqc_send_master_cmd = cx24123_send_diseqc_msg, .diseqc_send_burst = cx24123_diseqc_send_burst, .set_tone = cx24123_set_tone, .set_voltage = cx24123_set_voltage, + .tune = cx24123_tune, + .get_frontend_algo = cx24123_get_algo, }; module_param(debug, int, 0644); diff --git a/drivers/media/dvb/frontends/cx24123.h b/drivers/media/dvb/frontends/cx24123.h index 9606f825935..57a1dae1dc4 100644 --- a/drivers/media/dvb/frontends/cx24123.h +++ b/drivers/media/dvb/frontends/cx24123.h @@ -30,9 +30,21 @@ struct cx24123_config /* Need to set device param for start_dma */ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); + + /* 0 = LNB voltage normal, 1 = LNB voltage inverted */ + int lnb_polarity; }; +#if defined(CONFIG_DVB_CX24123) || defined(CONFIG_DVB_CX24123_MODULE) extern struct dvb_frontend* cx24123_attach(const struct cx24123_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* cx24123_attach(const struct cx24123_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_CX24123 #endif /* CX24123_H */ diff --git a/drivers/media/dvb/frontends/dib3000-common.c b/drivers/media/dvb/frontends/dib3000-common.c deleted file mode 100644 index 1a4f1f7c228..00000000000 --- a/drivers/media/dvb/frontends/dib3000-common.c +++ /dev/null @@ -1,83 +0,0 @@ -#include "dib3000-common.h" - -#ifdef CONFIG_DVB_DIBCOM_DEBUG -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c,4=srch (|-able))."); -#endif -#define deb_info(args...) dprintk(0x01,args) -#define deb_i2c(args...) dprintk(0x02,args) -#define deb_srch(args...) dprintk(0x04,args) - - -int dib3000_read_reg(struct dib3000_state *state, u16 reg) -{ - u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff }; - u8 rb[2]; - struct i2c_msg msg[] = { - { .addr = state->config.demod_address, .flags = 0, .buf = wb, .len = 2 }, - { .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 }, - }; - - if (i2c_transfer(state->i2c, msg, 2) != 2) - deb_i2c("i2c read error\n"); - - deb_i2c("reading i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg, - (rb[0] << 8) | rb[1],(rb[0] << 8) | rb[1]); - - return (rb[0] << 8) | rb[1]; -} - -int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val) -{ - u8 b[] = { - (reg >> 8) & 0xff, reg & 0xff, - (val >> 8) & 0xff, val & 0xff, - }; - struct i2c_msg msg[] = { - { .addr = state->config.demod_address, .flags = 0, .buf = b, .len = 4 } - }; - deb_i2c("writing i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,val,val); - - return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0; -} - -int dib3000_search_status(u16 irq,u16 lock) -{ - if (irq & 0x02) { - if (lock & 0x01) { - deb_srch("auto search succeeded\n"); - return 1; // auto search succeeded - } else { - deb_srch("auto search not successful\n"); - return 0; // auto search failed - } - } else if (irq & 0x01) { - deb_srch("auto search failed\n"); - return 0; // auto search failed - } - return -1; // try again -} - -/* for auto search */ -u16 dib3000_seq[2][2][2] = /* fft,gua, inv */ - { /* fft */ - { /* gua */ - { 0, 1 }, /* 0 0 { 0,1 } */ - { 3, 9 }, /* 0 1 { 0,1 } */ - }, - { - { 2, 5 }, /* 1 0 { 0,1 } */ - { 6, 11 }, /* 1 1 { 0,1 } */ - } - }; - -MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de"); -MODULE_DESCRIPTION("Common functions for the dib3000mb/dib3000mc dvb frontend drivers"); -MODULE_LICENSE("GPL"); - -EXPORT_SYMBOL(dib3000_seq); - -EXPORT_SYMBOL(dib3000_read_reg); -EXPORT_SYMBOL(dib3000_write_reg); -EXPORT_SYMBOL(dib3000_search_status); diff --git a/drivers/media/dvb/frontends/dib3000-common.h b/drivers/media/dvb/frontends/dib3000-common.h deleted file mode 100644 index be1c0d3e138..00000000000 --- a/drivers/media/dvb/frontends/dib3000-common.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * .h-files for the common use of the frontend drivers made by DiBcom - * DiBcom 3000M-B/C, 3000P - * - * DiBcom (http://www.dibcom.fr/) - * - * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) - * - * based on GPL code from DibCom, which has - * - * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) - * - * 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. - * - * Acknowledgements - * - * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver - * sources, on which this driver (and the dvb-dibusb) are based. - * - * see Documentation/dvb/README.dibusb for more information - * - */ - -#ifndef DIB3000_COMMON_H -#define DIB3000_COMMON_H - -#include "dvb_frontend.h" -#include "dib3000.h" - -/* info and err, taken from usb.h, if there is anything available like by default. */ -#define err(format, arg...) printk(KERN_ERR "dib3000: " format "\n" , ## arg) -#define info(format, arg...) printk(KERN_INFO "dib3000: " format "\n" , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "dib3000: " format "\n" , ## arg) - -/* frontend state */ -struct dib3000_state { - struct i2c_adapter* i2c; - -/* configuration settings */ - struct dib3000_config config; - - struct dvb_frontend frontend; - int timing_offset; - int timing_offset_comp_done; - - fe_bandwidth_t last_tuned_bw; - u32 last_tuned_freq; -}; - -/* commonly used methods by the dib3000mb/mc/p frontend */ -extern int dib3000_read_reg(struct dib3000_state *state, u16 reg); -extern int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val); - -extern int dib3000_search_status(u16 irq,u16 lock); - -/* handy shortcuts */ -#define rd(reg) dib3000_read_reg(state,reg) - -#define wr(reg,val) if (dib3000_write_reg(state,reg,val)) \ - { err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; } - -#define wr_foreach(a,v) { int i; \ - if (sizeof(a) != sizeof(v)) \ - err("sizeof: %zu %zu is different",sizeof(a),sizeof(v));\ - for (i=0; i < sizeof(a)/sizeof(u16); i++) \ - wr(a[i],v[i]); \ - } - -#define set_or(reg,val) wr(reg,rd(reg) | val) - -#define set_and(reg,val) wr(reg,rd(reg) & val) - - -/* debug */ - -#ifdef CONFIG_DVB_DIBCOM_DEBUG -#define dprintk(level,args...) \ - do { if ((debug & level)) { printk(args); } } while (0) -#else -#define dprintk(args...) do { } while (0) -#endif - -/* mask for enabling a specific pid for the pid_filter */ -#define DIB3000_ACTIVATE_PID_FILTERING (0x2000) - -/* common values for tuning */ -#define DIB3000_ALPHA_0 ( 0) -#define DIB3000_ALPHA_1 ( 1) -#define DIB3000_ALPHA_2 ( 2) -#define DIB3000_ALPHA_4 ( 4) - -#define DIB3000_CONSTELLATION_QPSK ( 0) -#define DIB3000_CONSTELLATION_16QAM ( 1) -#define DIB3000_CONSTELLATION_64QAM ( 2) - -#define DIB3000_GUARD_TIME_1_32 ( 0) -#define DIB3000_GUARD_TIME_1_16 ( 1) -#define DIB3000_GUARD_TIME_1_8 ( 2) -#define DIB3000_GUARD_TIME_1_4 ( 3) - -#define DIB3000_TRANSMISSION_MODE_2K ( 0) -#define DIB3000_TRANSMISSION_MODE_8K ( 1) - -#define DIB3000_SELECT_LP ( 0) -#define DIB3000_SELECT_HP ( 1) - -#define DIB3000_FEC_1_2 ( 1) -#define DIB3000_FEC_2_3 ( 2) -#define DIB3000_FEC_3_4 ( 3) -#define DIB3000_FEC_5_6 ( 5) -#define DIB3000_FEC_7_8 ( 7) - -#define DIB3000_HRCH_OFF ( 0) -#define DIB3000_HRCH_ON ( 1) - -#define DIB3000_DDS_INVERSION_OFF ( 0) -#define DIB3000_DDS_INVERSION_ON ( 1) - -#define DIB3000_TUNER_WRITE_ENABLE(a) (0xffff & (a << 8)) -#define DIB3000_TUNER_WRITE_DISABLE(a) (0xffff & ((a << 8) | (1 << 7))) - -/* for auto search */ -extern u16 dib3000_seq[2][2][2]; - -#define DIB3000_REG_MANUFACTOR_ID ( 1025) -#define DIB3000_I2C_ID_DIBCOM (0x01b3) - -#define DIB3000_REG_DEVICE_ID ( 1026) -#define DIB3000MB_DEVICE_ID (0x3000) -#define DIB3000MC_DEVICE_ID (0x3001) -#define DIB3000P_DEVICE_ID (0x3002) - -#endif // DIB3000_COMMON_H diff --git a/drivers/media/dvb/frontends/dib3000.h b/drivers/media/dvb/frontends/dib3000.h index ec927628d27..0caac3f0f27 100644 --- a/drivers/media/dvb/frontends/dib3000.h +++ b/drivers/media/dvb/frontends/dib3000.h @@ -41,9 +41,16 @@ struct dib_fe_xfer_ops int (*tuner_pass_ctrl)(struct dvb_frontend *fe, int onoff, u8 pll_ctrl); }; +#if defined(CONFIG_DVB_DIB3000MB) || defined(CONFIG_DVB_DIB3000MB_MODULE) extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops); +#else +static inline struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, + struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_DIB3000MB -extern struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config, - struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops); #endif // DIB3000_H diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c index 5302e11883a..adbabfdb04a 100644 --- a/drivers/media/dvb/frontends/dib3000mb.c +++ b/drivers/media/dvb/frontends/dib3000mb.c @@ -29,9 +29,10 @@ #include <linux/string.h> #include <linux/slab.h> -#include "dib3000-common.h" -#include "dib3000mb_priv.h" +#include "dvb_frontend.h" + #include "dib3000.h" +#include "dib3000mb_priv.h" /* Version information */ #define DRIVER_VERSION "0.1" @@ -44,10 +45,81 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-able))."); #endif #define deb_info(args...) dprintk(0x01,args) +#define deb_i2c(args...) dprintk(0x02,args) +#define deb_srch(args...) dprintk(0x04,args) +#define deb_info(args...) dprintk(0x01,args) #define deb_xfer(args...) dprintk(0x02,args) #define deb_setf(args...) dprintk(0x04,args) #define deb_getf(args...) dprintk(0x08,args) +#ifdef CONFIG_DVB_DIBCOM_DEBUG +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c,4=srch (|-able))."); +#endif + +static int dib3000_read_reg(struct dib3000_state *state, u16 reg) +{ + u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff }; + u8 rb[2]; + struct i2c_msg msg[] = { + { .addr = state->config.demod_address, .flags = 0, .buf = wb, .len = 2 }, + { .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 }, + }; + + if (i2c_transfer(state->i2c, msg, 2) != 2) + deb_i2c("i2c read error\n"); + + deb_i2c("reading i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg, + (rb[0] << 8) | rb[1],(rb[0] << 8) | rb[1]); + + return (rb[0] << 8) | rb[1]; +} + +static int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val) +{ + u8 b[] = { + (reg >> 8) & 0xff, reg & 0xff, + (val >> 8) & 0xff, val & 0xff, + }; + struct i2c_msg msg[] = { + { .addr = state->config.demod_address, .flags = 0, .buf = b, .len = 4 } + }; + deb_i2c("writing i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,val,val); + + return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0; +} + +static int dib3000_search_status(u16 irq,u16 lock) +{ + if (irq & 0x02) { + if (lock & 0x01) { + deb_srch("auto search succeeded\n"); + return 1; // auto search succeeded + } else { + deb_srch("auto search not successful\n"); + return 0; // auto search failed + } + } else if (irq & 0x01) { + deb_srch("auto search failed\n"); + return 0; // auto search failed + } + return -1; // try again +} + +/* for auto search */ +static u16 dib3000_seq[2][2][2] = /* fft,gua, inv */ + { /* fft */ + { /* gua */ + { 0, 1 }, /* 0 0 { 0,1 } */ + { 3, 9 }, /* 0 1 { 0,1 } */ + }, + { + { 2, 5 }, /* 1 0 { 0,1 } */ + { 6, 11 }, /* 1 1 { 0,1 } */ + } + }; + static int dib3000mb_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep); diff --git a/drivers/media/dvb/frontends/dib3000mb_priv.h b/drivers/media/dvb/frontends/dib3000mb_priv.h index 999b1904781..1a12747fdc9 100644 --- a/drivers/media/dvb/frontends/dib3000mb_priv.h +++ b/drivers/media/dvb/frontends/dib3000mb_priv.h @@ -13,6 +13,99 @@ #ifndef __DIB3000MB_PRIV_H_INCLUDED__ #define __DIB3000MB_PRIV_H_INCLUDED__ +/* info and err, taken from usb.h, if there is anything available like by default. */ +#define err(format, arg...) printk(KERN_ERR "dib3000: " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO "dib3000: " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "dib3000: " format "\n" , ## arg) + +/* handy shortcuts */ +#define rd(reg) dib3000_read_reg(state,reg) + +#define wr(reg,val) if (dib3000_write_reg(state,reg,val)) \ + { err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; } + +#define wr_foreach(a,v) { int i; \ + if (sizeof(a) != sizeof(v)) \ + err("sizeof: %zu %zu is different",sizeof(a),sizeof(v));\ + for (i=0; i < sizeof(a)/sizeof(u16); i++) \ + wr(a[i],v[i]); \ + } + +#define set_or(reg,val) wr(reg,rd(reg) | val) + +#define set_and(reg,val) wr(reg,rd(reg) & val) + +/* debug */ + +#ifdef CONFIG_DVB_DIBCOM_DEBUG +#define dprintk(level,args...) \ + do { if ((debug & level)) { printk(args); } } while (0) +#else +#define dprintk(args...) do { } while (0) +#endif + +/* mask for enabling a specific pid for the pid_filter */ +#define DIB3000_ACTIVATE_PID_FILTERING (0x2000) + +/* common values for tuning */ +#define DIB3000_ALPHA_0 ( 0) +#define DIB3000_ALPHA_1 ( 1) +#define DIB3000_ALPHA_2 ( 2) +#define DIB3000_ALPHA_4 ( 4) + +#define DIB3000_CONSTELLATION_QPSK ( 0) +#define DIB3000_CONSTELLATION_16QAM ( 1) +#define DIB3000_CONSTELLATION_64QAM ( 2) + +#define DIB3000_GUARD_TIME_1_32 ( 0) +#define DIB3000_GUARD_TIME_1_16 ( 1) +#define DIB3000_GUARD_TIME_1_8 ( 2) +#define DIB3000_GUARD_TIME_1_4 ( 3) + +#define DIB3000_TRANSMISSION_MODE_2K ( 0) +#define DIB3000_TRANSMISSION_MODE_8K ( 1) + +#define DIB3000_SELECT_LP ( 0) +#define DIB3000_SELECT_HP ( 1) + +#define DIB3000_FEC_1_2 ( 1) +#define DIB3000_FEC_2_3 ( 2) +#define DIB3000_FEC_3_4 ( 3) +#define DIB3000_FEC_5_6 ( 5) +#define DIB3000_FEC_7_8 ( 7) + +#define DIB3000_HRCH_OFF ( 0) +#define DIB3000_HRCH_ON ( 1) + +#define DIB3000_DDS_INVERSION_OFF ( 0) +#define DIB3000_DDS_INVERSION_ON ( 1) + +#define DIB3000_TUNER_WRITE_ENABLE(a) (0xffff & (a << 8)) +#define DIB3000_TUNER_WRITE_DISABLE(a) (0xffff & ((a << 8) | (1 << 7))) + +#define DIB3000_REG_MANUFACTOR_ID ( 1025) +#define DIB3000_I2C_ID_DIBCOM (0x01b3) + +#define DIB3000_REG_DEVICE_ID ( 1026) +#define DIB3000MB_DEVICE_ID (0x3000) +#define DIB3000MC_DEVICE_ID (0x3001) +#define DIB3000P_DEVICE_ID (0x3002) + +/* frontend state */ +struct dib3000_state { + struct i2c_adapter* i2c; + +/* configuration settings */ + struct dib3000_config config; + + struct dvb_frontend frontend; + int timing_offset; + int timing_offset_comp_done; + + fe_bandwidth_t last_tuned_bw; + u32 last_tuned_freq; +}; + /* register addresses and some of their default values */ /* restart subsystems */ diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c index 98673474a14..cc28417fa33 100644 --- a/drivers/media/dvb/frontends/dib3000mc.c +++ b/drivers/media/dvb/frontends/dib3000mc.c @@ -1,913 +1,921 @@ /* - * Frontend driver for mobile DVB-T demodulator DiBcom 3000P/M-C - * DiBcom (http://www.dibcom.fr/) + * Driver for DiBcom DiB3000MC/P-demodulator. * + * Copyright (C) 2004-6 DiBcom (http://www.dibcom.fr/) * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) * - * based on GPL code from DiBCom, which has + * This code is partially based on the previous dib3000mc.c . * - * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) - * - * This program is free software; you can redistribute it and/or + * 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. - * - * Acknowledgements - * - * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver - * sources, on which this driver (and the dvb-dibusb) are based. - * - * see Documentation/dvb/README.dibusb for more information - * */ + #include <linux/kernel.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/string.h> -#include <linux/slab.h> - -#include "dib3000-common.h" -#include "dib3000mc_priv.h" -#include "dib3000.h" - -/* Version information */ -#define DRIVER_VERSION "0.1" -#define DRIVER_DESC "DiBcom 3000M-C DVB-T demodulator" -#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de" - -#ifdef CONFIG_DVB_DIBCOM_DEBUG +#include <linux/i2c.h> +//#include <linux/init.h> +//#include <linux/delay.h> +//#include <linux/string.h> +//#include <linux/slab.h> + +#include "dvb_frontend.h" + +#include "dib3000mc.h" + static int debug; module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe,16=stat (|-able))."); -#endif -#define deb_info(args...) dprintk(0x01,args) -#define deb_xfer(args...) dprintk(0x02,args) -#define deb_setf(args...) dprintk(0x04,args) -#define deb_getf(args...) dprintk(0x08,args) -#define deb_stat(args...) dprintk(0x10,args) - -static int dib3000mc_set_impulse_noise(struct dib3000_state * state, int mode, - fe_transmit_mode_t transmission_mode, fe_bandwidth_t bandwidth) -{ - switch (transmission_mode) { - case TRANSMISSION_MODE_2K: - wr_foreach(dib3000mc_reg_fft,dib3000mc_fft_modes[0]); - break; - case TRANSMISSION_MODE_8K: - wr_foreach(dib3000mc_reg_fft,dib3000mc_fft_modes[1]); - break; - default: - break; - } +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); - switch (bandwidth) { -/* case BANDWIDTH_5_MHZ: - wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[0]); - break; */ - case BANDWIDTH_6_MHZ: - wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[1]); - break; - case BANDWIDTH_7_MHZ: - wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[2]); - break; - case BANDWIDTH_8_MHZ: - wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[3]); - break; - default: - break; +#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB3000MC/P:"); printk(args); } } while (0) + +struct dib3000mc_state { + struct dvb_frontend demod; + struct dib3000mc_config *cfg; + + u8 i2c_addr; + struct i2c_adapter *i2c_adap; + + struct dibx000_i2c_master i2c_master; + + fe_bandwidth_t current_bandwidth; + + u16 dev_id; +}; + +static u16 dib3000mc_read_word(struct dib3000mc_state *state, u16 reg) +{ + u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff }; + u8 rb[2]; + struct i2c_msg msg[2] = { + { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 }, + { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 }, + }; + + if (i2c_transfer(state->i2c_adap, msg, 2) != 2) + dprintk("i2c read error on %d\n",reg); + + return (rb[0] << 8) | rb[1]; +} + +static int dib3000mc_write_word(struct dib3000mc_state *state, u16 reg, u16 val) +{ + u8 b[4] = { + (reg >> 8) & 0xff, reg & 0xff, + (val >> 8) & 0xff, val & 0xff, + }; + struct i2c_msg msg = { + .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4 + }; + return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; +} + + +static int dib3000mc_identify(struct dib3000mc_state *state) +{ + u16 value; + if ((value = dib3000mc_read_word(state, 1025)) != 0x01b3) { + dprintk("-E- DiB3000MC/P: wrong Vendor ID (read=0x%x)\n",value); + return -EREMOTEIO; } - switch (mode) { - case 0: /* no impulse */ /* fall through */ - wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[0]); - break; - case 1: /* new algo */ - wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[1]); - set_or(DIB3000MC_REG_IMP_NOISE_55,DIB3000MC_IMP_NEW_ALGO(0)); /* gives 1<<10 */ - break; - default: /* old algo */ - wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[3]); - break; + value = dib3000mc_read_word(state, 1026); + if (value != 0x3001 && value != 0x3002) { + dprintk("-E- DiB3000MC/P: wrong Device ID (%x)\n",value); + return -EREMOTEIO; } + state->dev_id = value; + + dprintk("-I- found DiB3000MC/P: %x\n",state->dev_id); + return 0; } -static int dib3000mc_set_timing(struct dib3000_state *state, int upd_offset, - fe_transmit_mode_t fft, fe_bandwidth_t bw) +static int dib3000mc_set_timing(struct dib3000mc_state *state, s16 nfft, u8 bw, u8 update_offset) { - u16 timf_msb,timf_lsb; - s32 tim_offset,tim_sgn; - u64 comp1,comp2,comp=0; - - switch (bw) { - case BANDWIDTH_8_MHZ: comp = DIB3000MC_CLOCK_REF*8; break; - case BANDWIDTH_7_MHZ: comp = DIB3000MC_CLOCK_REF*7; break; - case BANDWIDTH_6_MHZ: comp = DIB3000MC_CLOCK_REF*6; break; - default: err("unknown bandwidth (%d)",bw); break; - } - timf_msb = (comp >> 16) & 0xff; - timf_lsb = (comp & 0xffff); +/* + u32 timf_msb, timf_lsb, i; + int tim_sgn ; + LUInt comp1, comp2, comp ; +// u32 tim_offset ; + comp = 27700 * BW_INDEX_TO_KHZ(bw) / 1000; + timf_msb = (comp >> 16) & 0x00FF; + timf_lsb = comp & 0xFFFF; // Update the timing offset ; - if (upd_offset > 0) { - if (!state->timing_offset_comp_done) { - msleep(200); + if (update_offset) { + if (state->timing_offset_comp_done == 0) { + usleep(200000); state->timing_offset_comp_done = 1; } - tim_offset = rd(DIB3000MC_REG_TIMING_OFFS_MSB); + tim_offset = dib3000mc_read_word(state, 416); if ((tim_offset & 0x2000) == 0x2000) - tim_offset |= 0xC000; - if (fft == TRANSMISSION_MODE_2K) - tim_offset <<= 2; + tim_offset |= 0xC000; // PB: This only works if tim_offset is s16 - weird + + if (nfft == 0) + tim_offset = tim_offset << 2; // PB: Do not store the offset for different things in one variable state->timing_offset += tim_offset; } - tim_offset = state->timing_offset; + if (tim_offset < 0) { tim_sgn = 1; tim_offset = -tim_offset; } else tim_sgn = 0; - comp1 = (u32)tim_offset * (u32)timf_lsb ; - comp2 = (u32)tim_offset * (u32)timf_msb ; + comp1 = tim_offset * timf_lsb; + comp2 = tim_offset * timf_msb; comp = ((comp1 >> 16) + comp2) >> 7; if (tim_sgn == 0) - comp = (u32)(timf_msb << 16) + (u32) timf_lsb + comp; + comp = timf_msb * (1<<16) + timf_lsb + comp; else - comp = (u32)(timf_msb << 16) + (u32) timf_lsb - comp ; + comp = timf_msb * (1<<16) + timf_lsb - comp; + + timf_msb = (comp>>16)&0xFF ; + timf_lsb = comp&0xFFFF; +*/ + u32 timf = 1384402 * (BW_INDEX_TO_KHZ(bw) / 1000); - timf_msb = (comp >> 16) & 0xff; - timf_lsb = comp & 0xffff; + dib3000mc_write_word(state, 23, timf >> 16); + dib3000mc_write_word(state, 24, timf & 0xffff); - wr(DIB3000MC_REG_TIMING_FREQ_MSB,timf_msb); - wr(DIB3000MC_REG_TIMING_FREQ_LSB,timf_lsb); return 0; } -static int dib3000mc_init_auto_scan(struct dib3000_state *state, fe_bandwidth_t bw, int boost) +static int dib3000mc_setup_pwm3_state(struct dib3000mc_state *state) { - if (boost) { - wr(DIB3000MC_REG_SCAN_BOOST,DIB3000MC_SCAN_BOOST_ON); + if (state->cfg->pwm3_inversion) { + dib3000mc_write_word(state, 51, (2 << 14) | (0 << 10) | (7 << 6) | (2 << 2) | (2 << 0)); + dib3000mc_write_word(state, 52, (0 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (2 << 0)); } else { - wr(DIB3000MC_REG_SCAN_BOOST,DIB3000MC_SCAN_BOOST_OFF); - } - switch (bw) { - case BANDWIDTH_8_MHZ: - wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_8mhz); - break; - case BANDWIDTH_7_MHZ: - wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_7mhz); - break; - case BANDWIDTH_6_MHZ: - wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_6mhz); - break; -/* case BANDWIDTH_5_MHZ: - wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_5mhz); - break;*/ - case BANDWIDTH_AUTO: - return -EOPNOTSUPP; - default: - err("unknown bandwidth value (%d).",bw); - return -EINVAL; - } - if (boost) { - u32 timeout = (rd(DIB3000MC_REG_BW_TIMOUT_MSB) << 16) + - rd(DIB3000MC_REG_BW_TIMOUT_LSB); - timeout *= 85; timeout >>= 7; - wr(DIB3000MC_REG_BW_TIMOUT_MSB,(timeout >> 16) & 0xffff); - wr(DIB3000MC_REG_BW_TIMOUT_LSB,timeout & 0xffff); + dib3000mc_write_word(state, 51, (2 << 14) | (4 << 10) | (7 << 6) | (2 << 2) | (2 << 0)); + dib3000mc_write_word(state, 52, (1 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (2 << 0)); } + + if (state->cfg->use_pwm3) + dib3000mc_write_word(state, 245, (1 << 3) | (1 << 0)); + else + dib3000mc_write_word(state, 245, 0); + + dib3000mc_write_word(state, 1040, 0x3); return 0; } -static int dib3000mc_set_adp_cfg(struct dib3000_state *state, fe_modulation_t con) +static int dib3000mc_set_output_mode(struct dib3000mc_state *state, int mode) { - switch (con) { - case QAM_64: - wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[2]); - break; - case QAM_16: - wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[1]); - break; - case QPSK: - wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[0]); - break; - case QAM_AUTO: + int ret = 0; + u16 fifo_threshold = 1792; + u16 outreg = 0; + u16 outmode = 0; + u16 elecout = 1; + u16 smo_reg = dib3000mc_read_word(state, 206) & 0x0010; /* keep the pid_parse bit */ + + dprintk("-I- Setting output mode for demod %p to %d\n", + &state->demod, mode); + + switch (mode) { + case OUTMODE_HIGH_Z: // disable + elecout = 0; + break; + case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + outmode = 0; + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + outmode = 1; + break; + case OUTMODE_MPEG2_SERIAL: // STBs with serial input + outmode = 2; + break; + case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + elecout = 3; + /*ADDR @ 206 : + P_smo_error_discard [1;6:6] = 0 + P_smo_rs_discard [1;5:5] = 0 + P_smo_pid_parse [1;4:4] = 0 + P_smo_fifo_flush [1;3:3] = 0 + P_smo_mode [2;2:1] = 11 + P_smo_ovf_prot [1;0:0] = 0 + */ + smo_reg |= 3 << 1; + fifo_threshold = 512; + outmode = 5; + break; + case OUTMODE_DIVERSITY: + outmode = 4; + elecout = 1; break; default: - warn("unkown constellation."); + dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod); + outmode = 0; break; } - return 0; + + if ((state->cfg->output_mpeg2_in_188_bytes)) + smo_reg |= (1 << 5); // P_smo_rs_discard [1;5:5] = 1 + + outreg = dib3000mc_read_word(state, 244) & 0x07FF; + outreg |= (outmode << 11); + ret |= dib3000mc_write_word(state, 244, outreg); + ret |= dib3000mc_write_word(state, 206, smo_reg); /*smo_ mode*/ + ret |= dib3000mc_write_word(state, 207, fifo_threshold); /* synchronous fread */ + ret |= dib3000mc_write_word(state, 1040, elecout); /* P_out_cfg */ + return ret; } -static int dib3000mc_set_general_cfg(struct dib3000_state *state, struct dvb_frontend_parameters *fep, int *auto_val) +static int dib3000mc_set_bandwidth(struct dvb_frontend *demod, u8 bw) { - struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm; - fe_code_rate_t fe_cr = FEC_NONE; - u8 fft=0, guard=0, qam=0, alpha=0, sel_hp=0, cr=0, hrch=0; - int seq; + struct dib3000mc_state *state = demod->demodulator_priv; + u16 bw_cfg[6] = { 0 }; + u16 imp_bw_cfg[3] = { 0 }; + u16 reg; - switch (ofdm->transmission_mode) { - case TRANSMISSION_MODE_2K: fft = DIB3000_TRANSMISSION_MODE_2K; break; - case TRANSMISSION_MODE_8K: fft = DIB3000_TRANSMISSION_MODE_8K; break; - case TRANSMISSION_MODE_AUTO: break; - default: return -EINVAL; - } - switch (ofdm->guard_interval) { - case GUARD_INTERVAL_1_32: guard = DIB3000_GUARD_TIME_1_32; break; - case GUARD_INTERVAL_1_16: guard = DIB3000_GUARD_TIME_1_16; break; - case GUARD_INTERVAL_1_8: guard = DIB3000_GUARD_TIME_1_8; break; - case GUARD_INTERVAL_1_4: guard = DIB3000_GUARD_TIME_1_4; break; - case GUARD_INTERVAL_AUTO: break; - default: return -EINVAL; - } - switch (ofdm->constellation) { - case QPSK: qam = DIB3000_CONSTELLATION_QPSK; break; - case QAM_16: qam = DIB3000_CONSTELLATION_16QAM; break; - case QAM_64: qam = DIB3000_CONSTELLATION_64QAM; break; - case QAM_AUTO: break; - default: return -EINVAL; - } - switch (ofdm->hierarchy_information) { - case HIERARCHY_NONE: /* fall through */ - case HIERARCHY_1: alpha = DIB3000_ALPHA_1; break; - case HIERARCHY_2: alpha = DIB3000_ALPHA_2; break; - case HIERARCHY_4: alpha = DIB3000_ALPHA_4; break; - case HIERARCHY_AUTO: break; - default: return -EINVAL; - } - if (ofdm->hierarchy_information == HIERARCHY_NONE) { - hrch = DIB3000_HRCH_OFF; - sel_hp = DIB3000_SELECT_HP; - fe_cr = ofdm->code_rate_HP; - } else if (ofdm->hierarchy_information != HIERARCHY_AUTO) { - hrch = DIB3000_HRCH_ON; - sel_hp = DIB3000_SELECT_LP; - fe_cr = ofdm->code_rate_LP; - } - switch (fe_cr) { - case FEC_1_2: cr = DIB3000_FEC_1_2; break; - case FEC_2_3: cr = DIB3000_FEC_2_3; break; - case FEC_3_4: cr = DIB3000_FEC_3_4; break; - case FEC_5_6: cr = DIB3000_FEC_5_6; break; - case FEC_7_8: cr = DIB3000_FEC_7_8; break; - case FEC_NONE: break; - case FEC_AUTO: break; - default: return -EINVAL; - } +/* settings here are for 27.7MHz */ + switch (bw) { + case BANDWIDTH_8_MHZ: + bw_cfg[0] = 0x0019; bw_cfg[1] = 0x5c30; bw_cfg[2] = 0x0054; bw_cfg[3] = 0x88a0; bw_cfg[4] = 0x01a6; bw_cfg[5] = 0xab20; + imp_bw_cfg[0] = 0x04db; imp_bw_cfg[1] = 0x00db; imp_bw_cfg[2] = 0x00b7; + break; - wr(DIB3000MC_REG_DEMOD_PARM,DIB3000MC_DEMOD_PARM(alpha,qam,guard,fft)); - wr(DIB3000MC_REG_HRCH_PARM,DIB3000MC_HRCH_PARM(sel_hp,cr,hrch)); + case BANDWIDTH_7_MHZ: + bw_cfg[0] = 0x001c; bw_cfg[1] = 0xfba5; bw_cfg[2] = 0x0060; bw_cfg[3] = 0x9c25; bw_cfg[4] = 0x01e3; bw_cfg[5] = 0x0cb7; + imp_bw_cfg[0] = 0x04c0; imp_bw_cfg[1] = 0x00c0; imp_bw_cfg[2] = 0x00a0; + break; - switch (fep->inversion) { - case INVERSION_OFF: - wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF); + case BANDWIDTH_6_MHZ: + bw_cfg[0] = 0x0021; bw_cfg[1] = 0xd040; bw_cfg[2] = 0x0070; bw_cfg[3] = 0xb62b; bw_cfg[4] = 0x0233; bw_cfg[5] = 0x8ed5; + imp_bw_cfg[0] = 0x04a5; imp_bw_cfg[1] = 0x00a5; imp_bw_cfg[2] = 0x0089; break; - case INVERSION_AUTO: /* fall through */ - case INVERSION_ON: - wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_ON); + + case 255 /* BANDWIDTH_5_MHZ */: + bw_cfg[0] = 0x0028; bw_cfg[1] = 0x9380; bw_cfg[2] = 0x0087; bw_cfg[3] = 0x4100; bw_cfg[4] = 0x02a4; bw_cfg[5] = 0x4500; + imp_bw_cfg[0] = 0x0489; imp_bw_cfg[1] = 0x0089; imp_bw_cfg[2] = 0x0072; break; - default: - return -EINVAL; + + default: return -EINVAL; } - seq = dib3000_seq - [ofdm->transmission_mode == TRANSMISSION_MODE_AUTO] - [ofdm->guard_interval == GUARD_INTERVAL_AUTO] - [fep->inversion == INVERSION_AUTO]; - - deb_setf("seq? %d\n", seq); - wr(DIB3000MC_REG_SEQ_TPS,DIB3000MC_SEQ_TPS(seq,1)); - *auto_val = ofdm->constellation == QAM_AUTO || - ofdm->hierarchy_information == HIERARCHY_AUTO || - ofdm->guard_interval == GUARD_INTERVAL_AUTO || - ofdm->transmission_mode == TRANSMISSION_MODE_AUTO || - fe_cr == FEC_AUTO || - fep->inversion == INVERSION_AUTO; - return 0; -} + for (reg = 6; reg < 12; reg++) + dib3000mc_write_word(state, reg, bw_cfg[reg - 6]); + dib3000mc_write_word(state, 12, 0x0000); + dib3000mc_write_word(state, 13, 0x03e8); + dib3000mc_write_word(state, 14, 0x0000); + dib3000mc_write_word(state, 15, 0x03f2); + dib3000mc_write_word(state, 16, 0x0001); + dib3000mc_write_word(state, 17, 0xb0d0); + // P_sec_len + dib3000mc_write_word(state, 18, 0x0393); + dib3000mc_write_word(state, 19, 0x8700); -static int dib3000mc_get_frontend(struct dvb_frontend* fe, - struct dvb_frontend_parameters *fep) -{ - struct dib3000_state* state = fe->demodulator_priv; - struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm; - fe_code_rate_t *cr; - u16 tps_val,cr_val; - int inv_test1,inv_test2; - u32 dds_val, threshold = 0x1000000; - - if (!(rd(DIB3000MC_REG_LOCK_507) & DIB3000MC_LOCK_507)) - return 0; - - dds_val = (rd(DIB3000MC_REG_DDS_FREQ_MSB) << 16) + rd(DIB3000MC_REG_DDS_FREQ_LSB); - deb_getf("DDS_FREQ: %6x\n",dds_val); - if (dds_val < threshold) - inv_test1 = 0; - else if (dds_val == threshold) - inv_test1 = 1; - else - inv_test1 = 2; - - dds_val = (rd(DIB3000MC_REG_SET_DDS_FREQ_MSB) << 16) + rd(DIB3000MC_REG_SET_DDS_FREQ_LSB); - deb_getf("DDS_SET_FREQ: %6x\n",dds_val); - if (dds_val < threshold) - inv_test2 = 0; - else if (dds_val == threshold) - inv_test2 = 1; - else - inv_test2 = 2; + for (reg = 55; reg < 58; reg++) + dib3000mc_write_word(state, reg, imp_bw_cfg[reg - 55]); - fep->inversion = - ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) || - ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ? - INVERSION_ON : INVERSION_OFF; + // Timing configuration + dib3000mc_set_timing(state, 0, bw, 0); - deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, fep->inversion); + return 0; +} - fep->frequency = state->last_tuned_freq; - fep->u.ofdm.bandwidth= state->last_tuned_bw; +static u16 impulse_noise_val[29] = - tps_val = rd(DIB3000MC_REG_TUNING_PARM); +{ + 0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, 0x3ffe, 0x7f3, + 0x2d94, 0x76, 0x53d, 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, 0x3feb, 0x7d2, + 0x365e, 0x76, 0x48c, 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0000, 0xd +}; - switch (DIB3000MC_TP_QAM(tps_val)) { - case DIB3000_CONSTELLATION_QPSK: - deb_getf("QPSK "); - ofdm->constellation = QPSK; - break; - case DIB3000_CONSTELLATION_16QAM: - deb_getf("QAM16 "); - ofdm->constellation = QAM_16; - break; - case DIB3000_CONSTELLATION_64QAM: - deb_getf("QAM64 "); - ofdm->constellation = QAM_64; - break; - default: - err("Unexpected constellation returned by TPS (%d)", tps_val); - break; +static void dib3000mc_set_impulse_noise(struct dib3000mc_state *state, u8 mode, s16 nfft) +{ + u16 i; + for (i = 58; i < 87; i++) + dib3000mc_write_word(state, i, impulse_noise_val[i-58]); + + if (nfft == 1) { + dib3000mc_write_word(state, 58, 0x3b); + dib3000mc_write_word(state, 84, 0x00); + dib3000mc_write_word(state, 85, 0x8200); } - if (DIB3000MC_TP_HRCH(tps_val)) { - deb_getf("HRCH ON "); - cr = &ofdm->code_rate_LP; - ofdm->code_rate_HP = FEC_NONE; - switch (DIB3000MC_TP_ALPHA(tps_val)) { - case DIB3000_ALPHA_0: - deb_getf("HIERARCHY_NONE "); - ofdm->hierarchy_information = HIERARCHY_NONE; - break; - case DIB3000_ALPHA_1: - deb_getf("HIERARCHY_1 "); - ofdm->hierarchy_information = HIERARCHY_1; - break; - case DIB3000_ALPHA_2: - deb_getf("HIERARCHY_2 "); - ofdm->hierarchy_information = HIERARCHY_2; - break; - case DIB3000_ALPHA_4: - deb_getf("HIERARCHY_4 "); - ofdm->hierarchy_information = HIERARCHY_4; - break; - default: - err("Unexpected ALPHA value returned by TPS (%d)", tps_val); - break; - } - cr_val = DIB3000MC_TP_FEC_CR_LP(tps_val); + dib3000mc_write_word(state, 34, 0x1294); + dib3000mc_write_word(state, 35, 0x1ff8); + if (mode == 1) + dib3000mc_write_word(state, 55, dib3000mc_read_word(state, 55) | (1 << 10)); +} + +static int dib3000mc_init(struct dvb_frontend *demod) +{ + struct dib3000mc_state *state = demod->demodulator_priv; + struct dibx000_agc_config *agc = state->cfg->agc; + + // Restart Configuration + dib3000mc_write_word(state, 1027, 0x8000); + dib3000mc_write_word(state, 1027, 0x0000); + + // power up the demod + mobility configuration + dib3000mc_write_word(state, 140, 0x0000); + dib3000mc_write_word(state, 1031, 0); + + if (state->cfg->mobile_mode) { + dib3000mc_write_word(state, 139, 0x0000); + dib3000mc_write_word(state, 141, 0x0000); + dib3000mc_write_word(state, 175, 0x0002); + dib3000mc_write_word(state, 1032, 0x0000); } else { - deb_getf("HRCH OFF "); - cr = &ofdm->code_rate_HP; - ofdm->code_rate_LP = FEC_NONE; - ofdm->hierarchy_information = HIERARCHY_NONE; - cr_val = DIB3000MC_TP_FEC_CR_HP(tps_val); + dib3000mc_write_word(state, 139, 0x0001); + dib3000mc_write_word(state, 141, 0x0000); + dib3000mc_write_word(state, 175, 0x0000); + dib3000mc_write_word(state, 1032, 0x012C); } + dib3000mc_write_word(state, 1033, 0); - switch (cr_val) { - case DIB3000_FEC_1_2: - deb_getf("FEC_1_2 "); - *cr = FEC_1_2; - break; - case DIB3000_FEC_2_3: - deb_getf("FEC_2_3 "); - *cr = FEC_2_3; - break; - case DIB3000_FEC_3_4: - deb_getf("FEC_3_4 "); - *cr = FEC_3_4; - break; - case DIB3000_FEC_5_6: - deb_getf("FEC_5_6 "); - *cr = FEC_4_5; - break; - case DIB3000_FEC_7_8: - deb_getf("FEC_7_8 "); - *cr = FEC_7_8; - break; - default: - err("Unexpected FEC returned by TPS (%d)", tps_val); - break; - } + // P_clk_cfg + dib3000mc_write_word(state, 1037, 12592); - switch (DIB3000MC_TP_GUARD(tps_val)) { - case DIB3000_GUARD_TIME_1_32: - deb_getf("GUARD_INTERVAL_1_32 "); - ofdm->guard_interval = GUARD_INTERVAL_1_32; - break; - case DIB3000_GUARD_TIME_1_16: - deb_getf("GUARD_INTERVAL_1_16 "); - ofdm->guard_interval = GUARD_INTERVAL_1_16; - break; - case DIB3000_GUARD_TIME_1_8: - deb_getf("GUARD_INTERVAL_1_8 "); - ofdm->guard_interval = GUARD_INTERVAL_1_8; - break; - case DIB3000_GUARD_TIME_1_4: - deb_getf("GUARD_INTERVAL_1_4 "); - ofdm->guard_interval = GUARD_INTERVAL_1_4; - break; - default: - err("Unexpected Guard Time returned by TPS (%d)", tps_val); - break; - } + // other configurations - switch (DIB3000MC_TP_FFT(tps_val)) { - case DIB3000_TRANSMISSION_MODE_2K: - deb_getf("TRANSMISSION_MODE_2K "); - ofdm->transmission_mode = TRANSMISSION_MODE_2K; - break; - case DIB3000_TRANSMISSION_MODE_8K: - deb_getf("TRANSMISSION_MODE_8K "); - ofdm->transmission_mode = TRANSMISSION_MODE_8K; - break; - default: - err("unexpected transmission mode return by TPS (%d)", tps_val); - break; - } - deb_getf("\n"); + // P_ctrl_sfreq + dib3000mc_write_word(state, 33, (5 << 0)); + dib3000mc_write_word(state, 88, (1 << 10) | (0x10 << 0)); + + // Phase noise control + // P_fft_phacor_inh, P_fft_phacor_cpe, P_fft_powrange + dib3000mc_write_word(state, 99, (1 << 9) | (0x20 << 0)); + + if (state->cfg->phase_noise_mode == 0) + dib3000mc_write_word(state, 111, 0x00); + else + dib3000mc_write_word(state, 111, 0x02); + + // P_agc_global + dib3000mc_write_word(state, 50, 0x8000); + + // agc setup misc + dib3000mc_setup_pwm3_state(state); + + // P_agc_counter_lock + dib3000mc_write_word(state, 53, 0x87); + // P_agc_counter_unlock + dib3000mc_write_word(state, 54, 0x87); + + /* agc */ + dib3000mc_write_word(state, 36, state->cfg->max_time); + dib3000mc_write_word(state, 37, agc->setup); + dib3000mc_write_word(state, 38, state->cfg->pwm3_value); + dib3000mc_write_word(state, 39, state->cfg->ln_adc_level); + + // set_agc_loop_Bw + dib3000mc_write_word(state, 40, 0x0179); + dib3000mc_write_word(state, 41, 0x03f0); + + dib3000mc_write_word(state, 42, agc->agc1_max); + dib3000mc_write_word(state, 43, agc->agc1_min); + dib3000mc_write_word(state, 44, agc->agc2_max); + dib3000mc_write_word(state, 45, agc->agc2_min); + dib3000mc_write_word(state, 46, (agc->agc1_pt1 << 8) | agc->agc1_pt2); + dib3000mc_write_word(state, 47, (agc->agc1_slope1 << 8) | agc->agc1_slope2); + dib3000mc_write_word(state, 48, (agc->agc2_pt1 << 8) | agc->agc2_pt2); + dib3000mc_write_word(state, 49, (agc->agc2_slope1 << 8) | agc->agc2_slope2); + +// Begin: TimeOut registers + // P_pha3_thres + dib3000mc_write_word(state, 110, 3277); + // P_timf_alpha = 6, P_corm_alpha = 6, P_corm_thres = 0x80 + dib3000mc_write_word(state, 26, 0x6680); + // lock_mask0 + dib3000mc_write_word(state, 1, 4); + // lock_mask1 + dib3000mc_write_word(state, 2, 4); + // lock_mask2 + dib3000mc_write_word(state, 3, 0x1000); + // P_search_maxtrial=1 + dib3000mc_write_word(state, 5, 1); + + dib3000mc_set_bandwidth(&state->demod, BANDWIDTH_8_MHZ); + + // div_lock_mask + dib3000mc_write_word(state, 4, 0x814); + + dib3000mc_write_word(state, 21, (1 << 9) | 0x164); + dib3000mc_write_word(state, 22, 0x463d); + + // Spurious rm cfg + // P_cspu_regul, P_cspu_win_cut + dib3000mc_write_word(state, 120, 0x200f); + // P_adp_selec_monit + dib3000mc_write_word(state, 134, 0); + + // Fec cfg + dib3000mc_write_word(state, 195, 0x10); + + // diversity register: P_dvsy_sync_wait.. + dib3000mc_write_word(state, 180, 0x2FF0); + + // Impulse noise configuration + dib3000mc_set_impulse_noise(state, 0, 1); + + // output mode set-up + dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z); + + /* close the i2c-gate */ + dib3000mc_write_word(state, 769, (1 << 7) ); return 0; } -static int dib3000mc_set_frontend(struct dvb_frontend* fe, - struct dvb_frontend_parameters *fep, int tuner) +static int dib3000mc_sleep(struct dvb_frontend *demod) { - struct dib3000_state* state = fe->demodulator_priv; - struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm; - int search_state,auto_val; - u16 val; + struct dib3000mc_state *state = demod->demodulator_priv; - if (tuner && fe->ops.tuner_ops.set_params) { /* initial call from dvb */ - fe->ops.tuner_ops.set_params(fe, fep); - if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); - - state->last_tuned_freq = fep->frequency; - // if (!scanboost) { - dib3000mc_set_timing(state,0,ofdm->transmission_mode,ofdm->bandwidth); - dib3000mc_init_auto_scan(state, ofdm->bandwidth, 0); - state->last_tuned_bw = ofdm->bandwidth; - - wr_foreach(dib3000mc_reg_agc_bandwidth,dib3000mc_agc_bandwidth); - wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_AGC); - wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_OFF); - - /* Default cfg isi offset adp */ - wr_foreach(dib3000mc_reg_offset,dib3000mc_offset[0]); - - wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT | DIB3000MC_ISI_INHIBIT); - dib3000mc_set_adp_cfg(state,ofdm->constellation); - wr(DIB3000MC_REG_UNK_133,DIB3000MC_UNK_133); - - wr_foreach(dib3000mc_reg_bandwidth_general,dib3000mc_bandwidth_general); - /* power smoothing */ - if (ofdm->bandwidth != BANDWIDTH_8_MHZ) { - wr_foreach(dib3000mc_reg_bw,dib3000mc_bw[0]); - } else { - wr_foreach(dib3000mc_reg_bw,dib3000mc_bw[3]); - } - auto_val = 0; - dib3000mc_set_general_cfg(state,fep,&auto_val); - dib3000mc_set_impulse_noise(state,0,ofdm->constellation,ofdm->bandwidth); - - val = rd(DIB3000MC_REG_DEMOD_PARM); - wr(DIB3000MC_REG_DEMOD_PARM,val|DIB3000MC_DEMOD_RST_DEMOD_ON); - wr(DIB3000MC_REG_DEMOD_PARM,val); - // } - msleep(70); - - /* something has to be auto searched */ - if (auto_val) { - int as_count=0; - - deb_setf("autosearch enabled.\n"); - - val = rd(DIB3000MC_REG_DEMOD_PARM); - wr(DIB3000MC_REG_DEMOD_PARM,val | DIB3000MC_DEMOD_RST_AUTO_SRCH_ON); - wr(DIB3000MC_REG_DEMOD_PARM,val); - - while ((search_state = dib3000_search_status( - rd(DIB3000MC_REG_AS_IRQ),1)) < 0 && as_count++ < 100) - msleep(10); - - deb_info("search_state after autosearch %d after %d checks\n",search_state,as_count); - - if (search_state == 1) { - struct dvb_frontend_parameters feps; - if (dib3000mc_get_frontend(fe, &feps) == 0) { - deb_setf("reading tuning data from frontend succeeded.\n"); - return dib3000mc_set_frontend(fe, &feps, 0); - } - } - } else { - dib3000mc_set_impulse_noise(state,0,ofdm->transmission_mode,ofdm->bandwidth); - wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT|DIB3000MC_ISI_ACTIVATE); - dib3000mc_set_adp_cfg(state,ofdm->constellation); - - /* set_offset_cfg */ - wr_foreach(dib3000mc_reg_offset, - dib3000mc_offset[(ofdm->transmission_mode == TRANSMISSION_MODE_8K)+1]); - } - } else { /* second call, after autosearch (fka: set_WithKnownParams) */ -// dib3000mc_set_timing(state,1,ofdm->transmission_mode,ofdm->bandwidth); - - auto_val = 0; - dib3000mc_set_general_cfg(state,fep,&auto_val); - if (auto_val) - deb_info("auto_val is true, even though an auto search was already performed.\n"); - - dib3000mc_set_impulse_noise(state,0,ofdm->constellation,ofdm->bandwidth); - - val = rd(DIB3000MC_REG_DEMOD_PARM); - wr(DIB3000MC_REG_DEMOD_PARM,val | DIB3000MC_DEMOD_RST_AUTO_SRCH_ON); - wr(DIB3000MC_REG_DEMOD_PARM,val); + dib3000mc_write_word(state, 1037, dib3000mc_read_word(state, 1037) | 0x0003); + dib3000mc_write_word(state, 1031, 0xFFFF); + dib3000mc_write_word(state, 1032, 0xFFFF); + dib3000mc_write_word(state, 1033, 0xFFF4); // **** Bin2 - msleep(30); + return 0; +} - wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT|DIB3000MC_ISI_ACTIVATE); - dib3000mc_set_adp_cfg(state,ofdm->constellation); - wr_foreach(dib3000mc_reg_offset, - dib3000mc_offset[(ofdm->transmission_mode == TRANSMISSION_MODE_8K)+1]); +static void dib3000mc_set_adp_cfg(struct dib3000mc_state *state, s16 qam) +{ + u16 cfg[4] = { 0 },reg; + switch (qam) { + case 0: + cfg[0] = 0x099a; cfg[1] = 0x7fae; cfg[2] = 0x0333; cfg[3] = 0x7ff0; + break; + case 1: + cfg[0] = 0x023d; cfg[1] = 0x7fdf; cfg[2] = 0x00a4; cfg[3] = 0x7ff0; + break; + case 2: + cfg[0] = 0x0148; cfg[1] = 0x7ff0; cfg[2] = 0x00a4; cfg[3] = 0x7ff8; + break; } - return 0; + for (reg = 129; reg < 133; reg++) + dib3000mc_write_word(state, reg, cfg[reg - 129]); } -static int dib3000mc_fe_init(struct dvb_frontend* fe, int mobile_mode) +static void dib3000mc_set_channel_cfg(struct dib3000mc_state *state, struct dibx000_ofdm_channel *chan, u16 seq) { - struct dib3000_state *state = fe->demodulator_priv; - deb_info("init start\n"); - - state->timing_offset = 0; - state->timing_offset_comp_done = 0; + u16 tmp; - wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_CONFIG); - wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_OFF); - wr(DIB3000MC_REG_CLK_CFG_1,DIB3000MC_CLK_CFG_1_POWER_UP); - wr(DIB3000MC_REG_CLK_CFG_2,DIB3000MC_CLK_CFG_2_PUP_MOBILE); - wr(DIB3000MC_REG_CLK_CFG_3,DIB3000MC_CLK_CFG_3_POWER_UP); - wr(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_INIT); + dib3000mc_set_timing(state, chan->nfft, chan->Bw, 0); - wr(DIB3000MC_REG_RST_UNC,DIB3000MC_RST_UNC_OFF); - wr(DIB3000MC_REG_UNK_19,DIB3000MC_UNK_19); +// if (boost) +// dib3000mc_write_word(state, 100, (11 << 6) + 6); +// else + dib3000mc_write_word(state, 100, (16 << 6) + 9); - wr(33,5); - wr(36,81); - wr(DIB3000MC_REG_UNK_88,DIB3000MC_UNK_88); + dib3000mc_write_word(state, 1027, 0x0800); + dib3000mc_write_word(state, 1027, 0x0000); - wr(DIB3000MC_REG_UNK_99,DIB3000MC_UNK_99); - wr(DIB3000MC_REG_UNK_111,DIB3000MC_UNK_111_PH_N_MODE_0); /* phase noise algo off */ + //Default cfg isi offset adp + dib3000mc_write_word(state, 26, 0x6680); + dib3000mc_write_word(state, 29, 0x1273); + dib3000mc_write_word(state, 33, 5); + dib3000mc_set_adp_cfg(state, 1); + dib3000mc_write_word(state, 133, 15564); - /* mobile mode - portable reception */ - wr_foreach(dib3000mc_reg_mobile_mode,dib3000mc_mobile_mode[1]); + dib3000mc_write_word(state, 12 , 0x0); + dib3000mc_write_word(state, 13 , 0x3e8); + dib3000mc_write_word(state, 14 , 0x0); + dib3000mc_write_word(state, 15 , 0x3f2); -/* TUNER_PANASONIC_ENV57H12D5: */ - wr_foreach(dib3000mc_reg_agc_bandwidth,dib3000mc_agc_bandwidth); - wr_foreach(dib3000mc_reg_agc_bandwidth_general,dib3000mc_agc_bandwidth_general); - wr_foreach(dib3000mc_reg_agc,dib3000mc_agc_tuner[1]); + dib3000mc_write_word(state, 93,0); + dib3000mc_write_word(state, 94,0); + dib3000mc_write_word(state, 95,0); + dib3000mc_write_word(state, 96,0); + dib3000mc_write_word(state, 97,0); + dib3000mc_write_word(state, 98,0); - wr(DIB3000MC_REG_UNK_110,DIB3000MC_UNK_110); - wr(26,0x6680); - wr(DIB3000MC_REG_UNK_1,DIB3000MC_UNK_1); - wr(DIB3000MC_REG_UNK_2,DIB3000MC_UNK_2); - wr(DIB3000MC_REG_UNK_3,DIB3000MC_UNK_3); - wr(DIB3000MC_REG_SEQ_TPS,DIB3000MC_SEQ_TPS_DEFAULT); + dib3000mc_set_impulse_noise(state, 0, chan->nfft); - wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_8mhz); - wr_foreach(dib3000mc_reg_bandwidth_general,dib3000mc_bandwidth_general); + tmp = ((chan->nfft & 0x1) << 7) | (chan->guard << 5) | (chan->nqam << 3) | chan->vit_alpha; + dib3000mc_write_word(state, 0, tmp); - wr(DIB3000MC_REG_UNK_4,DIB3000MC_UNK_4); + dib3000mc_write_word(state, 5, seq); - wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF); - wr(DIB3000MC_REG_SET_DDS_FREQ_LSB,DIB3000MC_DDS_FREQ_LSB); + tmp = (chan->vit_hrch << 4) | (chan->vit_select_hp); + if (!chan->vit_hrch || (chan->vit_hrch && chan->vit_select_hp)) + tmp |= chan->vit_code_rate_hp << 1; + else + tmp |= chan->vit_code_rate_lp << 1; + dib3000mc_write_word(state, 181, tmp); - dib3000mc_set_timing(state,0,TRANSMISSION_MODE_8K,BANDWIDTH_8_MHZ); -// wr_foreach(dib3000mc_reg_timing_freq,dib3000mc_timing_freq[3]); + // diversity synchro delay + tmp = dib3000mc_read_word(state, 180) & 0x000f; + tmp |= ((chan->nfft == 0) ? 64 : 256) * ((1 << (chan->guard)) * 3 / 2) << 4; // add 50% SFN margin + dib3000mc_write_word(state, 180, tmp); - wr(DIB3000MC_REG_UNK_120,DIB3000MC_UNK_120); - wr(DIB3000MC_REG_UNK_134,DIB3000MC_UNK_134); - wr(DIB3000MC_REG_FEC_CFG,DIB3000MC_FEC_CFG); + // restart demod + tmp = dib3000mc_read_word(state, 0); + dib3000mc_write_word(state, 0, tmp | (1 << 9)); + dib3000mc_write_word(state, 0, tmp); - wr(DIB3000MC_REG_DIVERSITY3,DIB3000MC_DIVERSITY3_IN_OFF); + msleep(30); - dib3000mc_set_impulse_noise(state,0,TRANSMISSION_MODE_8K,BANDWIDTH_8_MHZ); + dib3000mc_set_impulse_noise(state, state->cfg->impulse_noise_mode, chan->nfft); +} -/* output mode control, just the MPEG2_SLAVE */ -// set_or(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_SLAVE); - wr(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_SLAVE); - wr(DIB3000MC_REG_SMO_MODE,DIB3000MC_SMO_MODE_SLAVE); - wr(DIB3000MC_REG_FIFO_THRESHOLD,DIB3000MC_FIFO_THRESHOLD_SLAVE); - wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_SLAVE); +static int dib3000mc_autosearch_start(struct dvb_frontend *demod, struct dibx000_ofdm_channel *chan) +{ + struct dib3000mc_state *state = demod->demodulator_priv; + u16 reg; +// u32 val; + struct dibx000_ofdm_channel fchan; -/* MPEG2_PARALLEL_CONTINUOUS_CLOCK - wr(DIB3000MC_REG_OUTMODE, - DIB3000MC_SET_OUTMODE(DIB3000MC_OM_PAR_CONT_CLK, - rd(DIB3000MC_REG_OUTMODE))); + INIT_OFDM_CHANNEL(&fchan); + fchan = *chan; - wr(DIB3000MC_REG_SMO_MODE, - DIB3000MC_SMO_MODE_DEFAULT | - DIB3000MC_SMO_MODE_188); - wr(DIB3000MC_REG_FIFO_THRESHOLD,DIB3000MC_FIFO_THRESHOLD_DEFAULT); - wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_DIV_OUT_ON); -*/ + /* a channel for autosearch */ + reg = 0; + if (chan->nfft == -1 && chan->guard == -1) reg = 7; + if (chan->nfft == -1 && chan->guard != -1) reg = 2; + if (chan->nfft != -1 && chan->guard == -1) reg = 3; -/* diversity */ - wr(DIB3000MC_REG_DIVERSITY1,DIB3000MC_DIVERSITY1_DEFAULT); - wr(DIB3000MC_REG_DIVERSITY2,DIB3000MC_DIVERSITY2_DEFAULT); + fchan.nfft = 1; fchan.guard = 0; fchan.nqam = 2; + fchan.vit_alpha = 1; fchan.vit_code_rate_hp = 2; fchan.vit_code_rate_lp = 2; + fchan.vit_hrch = 0; fchan.vit_select_hp = 1; - set_and(DIB3000MC_REG_DIVERSITY3,DIB3000MC_DIVERSITY3_IN_OFF); + dib3000mc_set_channel_cfg(state, &fchan, reg); - set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_DIV_IN_OFF); + reg = dib3000mc_read_word(state, 0); + dib3000mc_write_word(state, 0, reg | (1 << 8)); + dib3000mc_write_word(state, 0, reg); - deb_info("init end\n"); return 0; } -static int dib3000mc_read_status(struct dvb_frontend* fe, fe_status_t *stat) + +static int dib3000mc_autosearch_is_irq(struct dvb_frontend *demod) { - struct dib3000_state* state = fe->demodulator_priv; - u16 lock = rd(DIB3000MC_REG_LOCKING); + struct dib3000mc_state *state = demod->demodulator_priv; + u16 irq_pending = dib3000mc_read_word(state, 511); - *stat = 0; - if (DIB3000MC_AGC_LOCK(lock)) - *stat |= FE_HAS_SIGNAL; - if (DIB3000MC_CARRIER_LOCK(lock)) - *stat |= FE_HAS_CARRIER; - if (DIB3000MC_TPS_LOCK(lock)) - *stat |= FE_HAS_VITERBI; - if (DIB3000MC_MPEG_SYNC_LOCK(lock)) - *stat |= (FE_HAS_SYNC | FE_HAS_LOCK); + if (irq_pending & 0x1) // failed + return 1; - deb_stat("actual status is %2x fifo_level: %x,244: %x, 206: %x, 207: %x, 1040: %x\n",*stat,rd(510),rd(244),rd(206),rd(207),rd(1040)); + if (irq_pending & 0x2) // succeeded + return 2; - return 0; + return 0; // still pending } -static int dib3000mc_read_ber(struct dvb_frontend* fe, u32 *ber) +static int dib3000mc_tune(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch) { - struct dib3000_state* state = fe->demodulator_priv; - *ber = ((rd(DIB3000MC_REG_BER_MSB) << 16) | rd(DIB3000MC_REG_BER_LSB)); + struct dib3000mc_state *state = demod->demodulator_priv; + + // ** configure demod ** + dib3000mc_set_channel_cfg(state, ch, 0); + + // activates isi + dib3000mc_write_word(state, 29, 0x1073); + + dib3000mc_set_adp_cfg(state, (u8)ch->nqam); + + if (ch->nfft == 1) { + dib3000mc_write_word(state, 26, 38528); + dib3000mc_write_word(state, 33, 8); + } else { + dib3000mc_write_word(state, 26, 30336); + dib3000mc_write_word(state, 33, 6); + } + + // if (lock) + // dib3000mc_set_timing(state, ch->nfft, ch->Bw, 1); + return 0; } -static int dib3000mc_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +static int dib3000mc_demod_output_mode(struct dvb_frontend *demod, int mode) { - struct dib3000_state* state = fe->demodulator_priv; - - *unc = rd(DIB3000MC_REG_PACKET_ERRORS); - return 0; + struct dib3000mc_state *state = demod->demodulator_priv; + return dib3000mc_set_output_mode(state, mode); } -/* see dib3000mb.c for calculation comments */ -static int dib3000mc_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +static int dib3000mc_i2c_enumeration(struct dvb_frontend *demod[], int no_of_demods, u8 default_addr) { - struct dib3000_state* state = fe->demodulator_priv; - u16 val = rd(DIB3000MC_REG_SIGNAL_NOISE_LSB); - *strength = (((val >> 6) & 0xff) << 8) + (val & 0x3f); + struct dib3000mc_state *st; + int k,ret=0; + u8 new_addr; + + static u8 DIB3000MC_I2C_ADDRESS[] = {20,22,24,26}; + + for (k = no_of_demods-1; k >= 0; k--) { + st = demod[k]->demodulator_priv; + + /* designated i2c address */ + new_addr = DIB3000MC_I2C_ADDRESS[k]; + + st->i2c_addr = new_addr; + if (dib3000mc_identify(st) != 0) { + st->i2c_addr = default_addr; + if (dib3000mc_identify(st) != 0) { + dprintk("-E- DiB3000P/MC #%d: not identified\n", k); + return -EINVAL; + } + } + + /* turn on div_out */ + dib3000mc_demod_output_mode(demod[k], OUTMODE_MPEG2_PAR_CONT_CLK); + + // set new i2c address and force divstr (Bit 1) to value 0 (Bit 0) + ret |= dib3000mc_write_word(st, 1024, (new_addr << 3) | 0x1); + st->i2c_addr = new_addr; + } + + for (k = 0; k < no_of_demods; k++) { + st = demod[k]->demodulator_priv; + + ret |= dib3000mc_write_word(st, 1024, st->i2c_addr << 3); - deb_stat("signal: mantisse = %d, exponent = %d\n",(*strength >> 8) & 0xff, *strength & 0xff); + /* turn off data output */ + dib3000mc_demod_output_mode(demod[k],OUTMODE_HIGH_Z); + dib3000mc_write_word(st, 769, (1 << 7) ); + + } return 0; } -/* see dib3000mb.c for calculation comments */ -static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr) +struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating) { - struct dib3000_state* state = fe->demodulator_priv; - u16 val = rd(DIB3000MC_REG_SIGNAL_NOISE_LSB), - val2 = rd(DIB3000MC_REG_SIGNAL_NOISE_MSB); - u16 sig,noise; + struct dib3000mc_state *st = demod->demodulator_priv; + return dibx000_get_i2c_adapter(&st->i2c_master, DIBX000_I2C_INTERFACE_TUNER, gating); +} - sig = (((val >> 6) & 0xff) << 8) + (val & 0x3f); - noise = (((val >> 4) & 0xff) << 8) + ((val & 0xf) << 2) + ((val2 >> 14) & 0x3); - if (noise == 0) - *snr = 0xffff; - else - *snr = (u16) sig/noise; +EXPORT_SYMBOL(dib3000mc_get_tuner_i2c_master); + +static int dib3000mc_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct dib3000mc_state *state = fe->demodulator_priv; + u16 tps = dib3000mc_read_word(state,458); + + fep->inversion = INVERSION_AUTO; + + fep->u.ofdm.bandwidth = state->current_bandwidth; + + switch ((tps >> 8) & 0x1) { + case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break; + case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break; + } + + switch (tps & 0x3) { + case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break; + case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break; + case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break; + case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break; + } + + switch ((tps >> 13) & 0x3) { + case 0: fep->u.ofdm.constellation = QPSK; break; + case 1: fep->u.ofdm.constellation = QAM_16; break; + case 2: + default: fep->u.ofdm.constellation = QAM_64; break; + } + + /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ + /* (tps >> 12) & 0x1 == hrch is used, (tps >> 9) & 0x7 == alpha */ + + fep->u.ofdm.hierarchy_information = HIERARCHY_NONE; + switch ((tps >> 5) & 0x7) { + case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break; + case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break; + case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break; + case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break; + case 7: + default: fep->u.ofdm.code_rate_HP = FEC_7_8; break; + + } + + switch ((tps >> 2) & 0x7) { + case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break; + case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break; + case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break; + case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break; + case 7: + default: fep->u.ofdm.code_rate_LP = FEC_7_8; break; + } - deb_stat("signal: mantisse = %d, exponent = %d\n",(sig >> 8) & 0xff, sig & 0xff); - deb_stat("noise: mantisse = %d, exponent = %d\n",(noise >> 8) & 0xff, noise & 0xff); - deb_stat("snr: %d\n",*snr); return 0; } -static int dib3000mc_sleep(struct dvb_frontend* fe) +static int dib3000mc_set_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) { - struct dib3000_state* state = fe->demodulator_priv; + struct dib3000mc_state *state = fe->demodulator_priv; + struct dibx000_ofdm_channel ch; - set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_PWR_DOWN); - wr(DIB3000MC_REG_CLK_CFG_1,DIB3000MC_CLK_CFG_1_POWER_DOWN); - wr(DIB3000MC_REG_CLK_CFG_2,DIB3000MC_CLK_CFG_2_POWER_DOWN); - wr(DIB3000MC_REG_CLK_CFG_3,DIB3000MC_CLK_CFG_3_POWER_DOWN); - return 0; + INIT_OFDM_CHANNEL(&ch); + FEP2DIB(fep,&ch); + + state->current_bandwidth = fep->u.ofdm.bandwidth; + dib3000mc_set_bandwidth(fe, fep->u.ofdm.bandwidth); + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe, fep); + msleep(100); + } + + if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO || + fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO || + fep->u.ofdm.constellation == QAM_AUTO || + fep->u.ofdm.code_rate_HP == FEC_AUTO) { + int i = 100, found; + + dib3000mc_autosearch_start(fe, &ch); + do { + msleep(1); + found = dib3000mc_autosearch_is_irq(fe); + } while (found == 0 && i--); + + dprintk("autosearch returns: %d\n",found); + if (found == 0 || found == 1) + return 0; // no channel found + + dib3000mc_get_frontend(fe, fep); + FEP2DIB(fep,&ch); + } + + /* make this a config parameter */ + dib3000mc_set_output_mode(state, OUTMODE_MPEG2_FIFO); + + return dib3000mc_tune(fe, &ch); } -static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +static int dib3000mc_read_status(struct dvb_frontend *fe, fe_status_t *stat) { - tune->min_delay_ms = 1000; + struct dib3000mc_state *state = fe->demodulator_priv; + u16 lock = dib3000mc_read_word(state, 509); + + *stat = 0; + + if (lock & 0x8000) + *stat |= FE_HAS_SIGNAL; + if (lock & 0x3000) + *stat |= FE_HAS_CARRIER; + if (lock & 0x0100) + *stat |= FE_HAS_VITERBI; + if (lock & 0x0010) + *stat |= FE_HAS_SYNC; + if (lock & 0x0008) + *stat |= FE_HAS_LOCK; + return 0; } -static int dib3000mc_fe_init_nonmobile(struct dvb_frontend* fe) +static int dib3000mc_read_ber(struct dvb_frontend *fe, u32 *ber) { - return dib3000mc_fe_init(fe, 0); + struct dib3000mc_state *state = fe->demodulator_priv; + *ber = (dib3000mc_read_word(state, 500) << 16) | dib3000mc_read_word(state, 501); + return 0; } -static int dib3000mc_set_frontend_and_tuner(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) +static int dib3000mc_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) { - return dib3000mc_set_frontend(fe, fep, 1); + struct dib3000mc_state *state = fe->demodulator_priv; + *unc = dib3000mc_read_word(state, 508); + return 0; } -static void dib3000mc_release(struct dvb_frontend* fe) +static int dib3000mc_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { - struct dib3000_state *state = fe->demodulator_priv; - kfree(state); + struct dib3000mc_state *state = fe->demodulator_priv; + u16 val = dib3000mc_read_word(state, 392); + *strength = 65535 - val; + return 0; } -/* pid filter and transfer stuff */ -static int dib3000mc_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff) +static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr) { - struct dib3000_state *state = fe->demodulator_priv; - pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0); - wr(index+DIB3000MC_REG_FIRST_PID,pid); + *snr = 0x0000; return 0; } -static int dib3000mc_fifo_control(struct dvb_frontend *fe, int onoff) +static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) { - struct dib3000_state *state = fe->demodulator_priv; - u16 tmp = rd(DIB3000MC_REG_SMO_MODE); - - deb_xfer("%s fifo\n",onoff ? "enabling" : "disabling"); - - if (onoff) { - deb_xfer("%d %x\n",tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH,tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH); - wr(DIB3000MC_REG_SMO_MODE,tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH); - } else { - deb_xfer("%d %x\n",tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH,tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH); - wr(DIB3000MC_REG_SMO_MODE,tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH); - } + tune->min_delay_ms = 1000; return 0; } -static int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff) +static void dib3000mc_release(struct dvb_frontend *fe) { - struct dib3000_state *state = fe->demodulator_priv; - u16 tmp = rd(DIB3000MC_REG_SMO_MODE); - - deb_xfer("%s pid parsing\n",onoff ? "enabling" : "disabling"); - - if (onoff) { - wr(DIB3000MC_REG_SMO_MODE,tmp | DIB3000MC_SMO_MODE_PID_PARSE); - } else { - wr(DIB3000MC_REG_SMO_MODE,tmp & DIB3000MC_SMO_MODE_NO_PID_PARSE); - } - return 0; + struct dib3000mc_state *state = fe->demodulator_priv; + dibx000_exit_i2c_master(&state->i2c_master); + kfree(state); } -static int dib3000mc_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr) +int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff) { - struct dib3000_state *state = fe->demodulator_priv; - if (onoff) { - wr(DIB3000MC_REG_TUNER, DIB3000_TUNER_WRITE_ENABLE(pll_addr)); - } else { - wr(DIB3000MC_REG_TUNER, DIB3000_TUNER_WRITE_DISABLE(pll_addr)); - } + struct dib3000mc_state *state = fe->demodulator_priv; + dib3000mc_write_word(state, 212 + index, onoff ? (1 << 13) | pid : 0); return 0; } +EXPORT_SYMBOL(dib3000mc_pid_control); -static int dib3000mc_demod_init(struct dib3000_state *state) +int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff) { - u16 default_addr = 0x0a; - /* first init */ - if (state->config.demod_address != default_addr) { - deb_info("initializing the demod the first time. Setting demod addr to 0x%x\n",default_addr); - wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_DIV_OUT_ON); - wr(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_PAR_CONT_CLK); - - wr(DIB3000MC_REG_RST_I2C_ADDR, - DIB3000MC_DEMOD_ADDR(default_addr) | - DIB3000MC_DEMOD_ADDR_ON); - - state->config.demod_address = default_addr; - - wr(DIB3000MC_REG_RST_I2C_ADDR, - DIB3000MC_DEMOD_ADDR(default_addr)); - } else - deb_info("demod is already initialized. Demod addr: 0x%x\n",state->config.demod_address); - return 0; + struct dib3000mc_state *state = fe->demodulator_priv; + u16 tmp = dib3000mc_read_word(state, 206) & ~(1 << 4); + tmp |= (onoff << 4); + return dib3000mc_write_word(state, 206, tmp); } +EXPORT_SYMBOL(dib3000mc_pid_parse); +void dib3000mc_set_config(struct dvb_frontend *fe, struct dib3000mc_config *cfg) +{ + struct dib3000mc_state *state = fe->demodulator_priv; + state->cfg = cfg; +} +EXPORT_SYMBOL(dib3000mc_set_config); static struct dvb_frontend_ops dib3000mc_ops; -struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config, - struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops) +int dib3000mc_attach(struct i2c_adapter *i2c_adap, int no_of_demods, u8 default_addr, u8 do_i2c_enum, struct dib3000mc_config cfg[], struct dvb_frontend *demod[]) { - struct dib3000_state* state = NULL; - u16 devid; + struct dib3000mc_state *st; + int k, num=0; - /* allocate memory for the internal state */ - state = kzalloc(sizeof(struct dib3000_state), GFP_KERNEL); - if (state == NULL) - goto error; + if (no_of_demods < 1) + return -EINVAL; - /* setup the state */ - state->i2c = i2c; - memcpy(&state->config,config,sizeof(struct dib3000_config)); + for (k = 0; k < no_of_demods; k++) { + st = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL); + if (st == NULL) + goto error; - /* check for the correct demod */ - if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM) - goto error; + num++; - devid = rd(DIB3000_REG_DEVICE_ID); - if (devid != DIB3000MC_DEVICE_ID && devid != DIB3000P_DEVICE_ID) - goto error; + st->cfg = &cfg[k]; + // st->gpio_val = cfg[k].gpio_val; + // st->gpio_dir = cfg[k].gpio_dir; + st->i2c_adap = i2c_adap; - switch (devid) { - case DIB3000MC_DEVICE_ID: - info("Found a DiBcom 3000M-C, interesting..."); - break; - case DIB3000P_DEVICE_ID: - info("Found a DiBcom 3000P."); - break; - } + demod[k] = &st->demod; + demod[k]->demodulator_priv = st; + memcpy(&st->demod.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops)); - /* create dvb_frontend */ - memcpy(&state->frontend.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops)); - state->frontend.demodulator_priv = state; +// INIT_COMPONENT_REGISTER_ACCESS(&st->register_access, 12, 16, dib7000p_register_read, dib7000p_register_write, st); +// demod[k]->register_access = &st->register_access; + } - /* set the xfer operations */ - xfer_ops->pid_parse = dib3000mc_pid_parse; - xfer_ops->fifo_ctrl = dib3000mc_fifo_control; - xfer_ops->pid_ctrl = dib3000mc_pid_control; - xfer_ops->tuner_pass_ctrl = dib3000mc_tuner_pass_ctrl; + if (do_i2c_enum) { + if (dib3000mc_i2c_enumeration(demod,no_of_demods,default_addr) != 0) + goto error; + } else { + st = demod[0]->demodulator_priv; + st->i2c_addr = default_addr; + if (dib3000mc_identify(st) != 0) + goto error; + } - dib3000mc_demod_init(state); + for (k = 0; k < num; k++) { + st = demod[k]->demodulator_priv; + dibx000_init_i2c_master(&st->i2c_master, DIB3000MC, st->i2c_adap, st->i2c_addr); + } - return &state->frontend; + return 0; error: - kfree(state); - return NULL; + for (k = 0; k < num; k++) { + kfree(demod[k]->demodulator_priv); + demod[k] = NULL; + } + return -EINVAL; } + EXPORT_SYMBOL(dib3000mc_attach); static struct dvb_frontend_ops dib3000mc_ops = { - .info = { - .name = "DiBcom 3000P/M-C DVB-T", - .type = FE_OFDM, - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, + .name = "DiBcom 3000MC/P", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 62500, .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_RECOVER | - FE_CAN_HIERARCHY_AUTO, + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, }, - .release = dib3000mc_release, + .release = dib3000mc_release, - .init = dib3000mc_fe_init_nonmobile, - .sleep = dib3000mc_sleep, + .init = dib3000mc_init, + .sleep = dib3000mc_sleep, - .set_frontend = dib3000mc_set_frontend_and_tuner, - .get_frontend = dib3000mc_get_frontend, - .get_tune_settings = dib3000mc_fe_get_tune_settings, + .set_frontend = dib3000mc_set_frontend, + .get_tune_settings = dib3000mc_fe_get_tune_settings, + .get_frontend = dib3000mc_get_frontend, - .read_status = dib3000mc_read_status, - .read_ber = dib3000mc_read_ber, + .read_status = dib3000mc_read_status, + .read_ber = dib3000mc_read_ber, .read_signal_strength = dib3000mc_read_signal_strength, - .read_snr = dib3000mc_read_snr, - .read_ucblocks = dib3000mc_read_unc_blocks, + .read_snr = dib3000mc_read_snr, + .read_ucblocks = dib3000mc_read_unc_blocks, }; -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>"); +MODULE_DESCRIPTION("Driver for the DiBcom 3000MC/P COFDM demodulator"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/dib3000mc.h b/drivers/media/dvb/frontends/dib3000mc.h new file mode 100644 index 00000000000..fd0b2e75599 --- /dev/null +++ b/drivers/media/dvb/frontends/dib3000mc.h @@ -0,0 +1,58 @@ +/* + * Driver for DiBcom DiB3000MC/P-demodulator. + * + * Copyright (C) 2004-6 DiBcom (http://www.dibcom.fr/) + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher\@desy.de) + * + * This code is partially based on the previous dib3000mc.c . + * + * 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. + */ +#ifndef DIB3000MC_H +#define DIB3000MC_H + +#include "dibx000_common.h" + +struct dib3000mc_config { + struct dibx000_agc_config *agc; + + u8 phase_noise_mode; + u8 impulse_noise_mode; + + u8 pwm3_inversion; + u8 use_pwm3; + u16 pwm3_value; + + u16 max_time; + u16 ln_adc_level; + + u8 mobile_mode; + + u8 output_mpeg2_in_188_bytes; +}; + +#define DEFAULT_DIB3000MC_I2C_ADDRESS 16 +#define DEFAULT_DIB3000P_I2C_ADDRESS 24 + +#if defined(CONFIG_DVB_DIB3000MC) || defined(CONFIG_DVB_DIB3000MC_MODULE) +extern int dib3000mc_attach(struct i2c_adapter *i2c_adap, int no_of_demods, u8 default_addr, + u8 do_i2c_enum, struct dib3000mc_config cfg[], struct dvb_frontend *demod[]); +#else +static inline struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config, + struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_DIB3000MC + +extern struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating); + +extern int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff); +extern int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff); + +extern void dib3000mc_set_config(struct dvb_frontend *, struct dib3000mc_config *); + +#endif diff --git a/drivers/media/dvb/frontends/dib3000mc_priv.h b/drivers/media/dvb/frontends/dib3000mc_priv.h deleted file mode 100644 index 2930aac7591..00000000000 --- a/drivers/media/dvb/frontends/dib3000mc_priv.h +++ /dev/null @@ -1,428 +0,0 @@ -/* - * dib3000mc_priv.h - * - * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de) - * - * 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. - * - * for more information see dib3000mc.c . - */ - -#ifndef __DIB3000MC_PRIV_H__ -#define __DIB3000MC_PRIV_H__ - -/* - * Demodulator parameters - * reg: 0 1 1 1 11 11 111 - * | | | | | | - * | | | | | +-- alpha (000=0, 001=1, 010=2, 100=4) - * | | | | +----- constellation (00=QPSK, 01=16QAM, 10=64QAM) - * | | | +-------- guard (00=1/32, 01=1/16, 10=1/8, 11=1/4) - * | | +----------- transmission mode (0=2k, 1=8k) - * | | - * | +-------------- restart autosearch for parameters - * +---------------- restart the demodulator - * reg: 181 1 111 1 - * | | | - * | | +- FEC applies for HP or LP (0=LP, 1=HP) - * | +---- FEC rate (001=1/2, 010=2/3, 011=3/4, 101=5/6, 111=7/8) - * +------- hierarchy on (0=no, 1=yes) - */ - -/* demodulator tuning parameter and restart options */ -#define DIB3000MC_REG_DEMOD_PARM ( 0) -#define DIB3000MC_DEMOD_PARM(a,c,g,t) ( \ - (0x7 & a) | \ - ((0x3 & c) << 3) | \ - ((0x3 & g) << 5) | \ - ((0x1 & t) << 7) ) -#define DIB3000MC_DEMOD_RST_AUTO_SRCH_ON (1 << 8) -#define DIB3000MC_DEMOD_RST_AUTO_SRCH_OFF (0 << 8) -#define DIB3000MC_DEMOD_RST_DEMOD_ON (1 << 9) -#define DIB3000MC_DEMOD_RST_DEMOD_OFF (0 << 9) - -/* register for hierarchy parameters */ -#define DIB3000MC_REG_HRCH_PARM ( 181) -#define DIB3000MC_HRCH_PARM(s,f,h) ( \ - (0x1 & s) | \ - ((0x7 & f) << 1) | \ - ((0x1 & h) << 4) ) - -/* timeout ??? */ -#define DIB3000MC_REG_UNK_1 ( 1) -#define DIB3000MC_UNK_1 ( 0x04) - -/* timeout ??? */ -#define DIB3000MC_REG_UNK_2 ( 2) -#define DIB3000MC_UNK_2 ( 0x04) - -/* timeout ??? */ -#define DIB3000MC_REG_UNK_3 ( 3) -#define DIB3000MC_UNK_3 (0x1000) - -#define DIB3000MC_REG_UNK_4 ( 4) -#define DIB3000MC_UNK_4 (0x0814) - -/* timeout ??? */ -#define DIB3000MC_REG_SEQ_TPS ( 5) -#define DIB3000MC_SEQ_TPS_DEFAULT ( 1) -#define DIB3000MC_SEQ_TPS(s,t) ( \ - ((s & 0x0f) << 4) | \ - ((t & 0x01) << 8) ) -#define DIB3000MC_IS_TPS(v) ((v << 8) & 0x1) -#define DIB3000MC_IS_AS(v) ((v >> 4) & 0xf) - -/* parameters for the bandwidth */ -#define DIB3000MC_REG_BW_TIMOUT_MSB ( 6) -#define DIB3000MC_REG_BW_TIMOUT_LSB ( 7) - -static u16 dib3000mc_reg_bandwidth[] = { 6,7,8,9,10,11,16,17 }; - -/*static u16 dib3000mc_bandwidth_5mhz[] = - { 0x28, 0x9380, 0x87, 0x4100, 0x2a4, 0x4500, 0x1, 0xb0d0 };*/ - -static u16 dib3000mc_bandwidth_6mhz[] = - { 0x21, 0xd040, 0x70, 0xb62b, 0x233, 0x8ed5, 0x1, 0xb0d0 }; - -static u16 dib3000mc_bandwidth_7mhz[] = - { 0x1c, 0xfba5, 0x60, 0x9c25, 0x1e3, 0x0cb7, 0x1, 0xb0d0 }; - -static u16 dib3000mc_bandwidth_8mhz[] = - { 0x19, 0x5c30, 0x54, 0x88a0, 0x1a6, 0xab20, 0x1, 0xb0d0 }; - -static u16 dib3000mc_reg_bandwidth_general[] = { 12,13,14,15 }; -static u16 dib3000mc_bandwidth_general[] = { 0x0000, 0x03e8, 0x0000, 0x03f2 }; - -/* lock mask */ -#define DIB3000MC_REG_LOCK_MASK ( 15) -#define DIB3000MC_ACTIVATE_LOCK_MASK (0x0800) - -/* reset the uncorrected packet count (??? do it 5 times) */ -#define DIB3000MC_REG_RST_UNC ( 18) -#define DIB3000MC_RST_UNC_ON ( 1) -#define DIB3000MC_RST_UNC_OFF ( 0) - -#define DIB3000MC_REG_UNK_19 ( 19) -#define DIB3000MC_UNK_19 ( 0) - -/* DDS frequency value (IF position) and inversion bit */ -#define DIB3000MC_REG_INVERSION ( 21) -#define DIB3000MC_REG_SET_DDS_FREQ_MSB ( 21) -#define DIB3000MC_DDS_FREQ_MSB_INV_OFF (0x0164) -#define DIB3000MC_DDS_FREQ_MSB_INV_ON (0x0364) - -#define DIB3000MC_REG_SET_DDS_FREQ_LSB ( 22) -#define DIB3000MC_DDS_FREQ_LSB (0x463d) - -/* timing frequencies setting */ -#define DIB3000MC_REG_TIMING_FREQ_MSB ( 23) -#define DIB3000MC_REG_TIMING_FREQ_LSB ( 24) -#define DIB3000MC_CLOCK_REF (0x151fd1) - -//static u16 dib3000mc_reg_timing_freq[] = { 23,24 }; - -//static u16 dib3000mc_timing_freq[][2] = { -// { 0x69, 0x9f18 }, /* 5 MHz */ -// { 0x7e ,0xbee9 }, /* 6 MHz */ -// { 0x93 ,0xdebb }, /* 7 MHz */ -// { 0xa8 ,0xfe8c }, /* 8 MHz */ -//}; - -/* timeout ??? */ -static u16 dib3000mc_reg_offset[] = { 26,33 }; - -static u16 dib3000mc_offset[][2] = { - { 26240, 5 }, /* default */ - { 30336, 6 }, /* 8K */ - { 38528, 8 }, /* 2K */ -}; - -#define DIB3000MC_REG_ISI ( 29) -#define DIB3000MC_ISI_DEFAULT (0x1073) -#define DIB3000MC_ISI_ACTIVATE (0x0000) -#define DIB3000MC_ISI_INHIBIT (0x0200) - -/* impulse noise control */ -static u16 dib3000mc_reg_imp_noise_ctl[] = { 34,35 }; - -static u16 dib3000mc_imp_noise_ctl[][2] = { - { 0x1294, 0x1ff8 }, /* mode 0 */ - { 0x1294, 0x1ff8 }, /* mode 1 */ - { 0x1294, 0x1ff8 }, /* mode 2 */ - { 0x1294, 0x1ff8 }, /* mode 3 */ - { 0x1294, 0x1ff8 }, /* mode 4 */ -}; - -/* AGC registers */ -static u16 dib3000mc_reg_agc[] = { - 36,37,38,39,42,43,44,45,46,47,48,49 -}; - -static u16 dib3000mc_agc_tuner[][12] = { - { 0x0051, 0x301d, 0x0000, 0x1cc7, 0xcf5c, 0x6666, - 0xbae1, 0xa148, 0x3b5e, 0x3c1c, 0x001a, 0x2019 - }, /* TUNER_PANASONIC_ENV77H04D5, */ - - { 0x0051, 0x301d, 0x0000, 0x1cc7, 0xdc29, 0x570a, - 0xbae1, 0x8ccd, 0x3b6d, 0x551d, 0x000a, 0x951e - }, /* TUNER_PANASONIC_ENV57H13D5, TUNER_PANASONIC_ENV57H12D5 */ - - { 0x0051, 0x301d, 0x0000, 0x1cc7, 0xffff, 0xffff, - 0xffff, 0x0000, 0xfdfd, 0x4040, 0x00fd, 0x4040 - }, /* TUNER_SAMSUNG_DTOS333IH102, TUNER_RFAGCIN_UNKNOWN */ - - { 0x0196, 0x301d, 0x0000, 0x1cc7, 0xbd71, 0x5c29, - 0xb5c3, 0x6148, 0x6569, 0x5127, 0x0033, 0x3537 - }, /* TUNER_PROVIDER_X */ - /* TODO TUNER_PANASONIC_ENV57H10D8, TUNER_PANASONIC_ENV57H11D8 */ -}; - -/* AGC loop bandwidth */ -static u16 dib3000mc_reg_agc_bandwidth[] = { 40,41 }; -static u16 dib3000mc_agc_bandwidth[] = { 0x119,0x330 }; - -static u16 dib3000mc_reg_agc_bandwidth_general[] = { 50,51,52,53,54 }; -static u16 dib3000mc_agc_bandwidth_general[] = - { 0x8000, 0x91ca, 0x01ba, 0x0087, 0x0087 }; - -#define DIB3000MC_REG_IMP_NOISE_55 ( 55) -#define DIB3000MC_IMP_NEW_ALGO(w) (w | (1<<10)) - -/* Impulse noise params */ -static u16 dib3000mc_reg_impulse_noise[] = { 55,56,57 }; -static u16 dib3000mc_impluse_noise[][3] = { - { 0x489, 0x89, 0x72 }, /* 5 MHz */ - { 0x4a5, 0xa5, 0x89 }, /* 6 MHz */ - { 0x4c0, 0xc0, 0xa0 }, /* 7 MHz */ - { 0x4db, 0xdb, 0xb7 }, /* 8 Mhz */ -}; - -static u16 dib3000mc_reg_fft[] = { - 58,59,60,61,62,63,64,65,66,67,68,69, - 70,71,72,73,74,75,76,77,78,79,80,81, - 82,83,84,85,86 -}; - -static u16 dib3000mc_fft_modes[][29] = { - { 0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, - 0x3ffe, 0x7f3, 0x2d94, 0x76, 0x53d, - 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, - 0x3feb, 0x7d2, 0x365e, 0x76, 0x48c, - 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0, 0xd - }, /* fft mode 0 */ - { 0x3b, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, - 0x3ffe, 0x7f3, 0x2d94, 0x76, 0x53d, - 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, - 0x3feb, 0x7d2, 0x365e, 0x76, 0x48c, - 0x3ffe, 0x5b3, 0x3feb, 0x0, 0x8200, 0xd - }, /* fft mode 1 */ -}; - -#define DIB3000MC_REG_UNK_88 ( 88) -#define DIB3000MC_UNK_88 (0x0410) - -static u16 dib3000mc_reg_bw[] = { 93,94,95,96,97,98 }; -static u16 dib3000mc_bw[][6] = { - { 0,0,0,0,0,0 }, /* 5 MHz */ - { 0,0,0,0,0,0 }, /* 6 MHz */ - { 0,0,0,0,0,0 }, /* 7 MHz */ - { 0x20, 0x21, 0x20, 0x23, 0x20, 0x27 }, /* 8 MHz */ -}; - - -/* phase noise control */ -#define DIB3000MC_REG_UNK_99 ( 99) -#define DIB3000MC_UNK_99 (0x0220) - -#define DIB3000MC_REG_SCAN_BOOST ( 100) -#define DIB3000MC_SCAN_BOOST_ON ((11 << 6) + 6) -#define DIB3000MC_SCAN_BOOST_OFF ((16 << 6) + 9) - -/* timeout ??? */ -#define DIB3000MC_REG_UNK_110 ( 110) -#define DIB3000MC_UNK_110 ( 3277) - -#define DIB3000MC_REG_UNK_111 ( 111) -#define DIB3000MC_UNK_111_PH_N_MODE_0 ( 0) -#define DIB3000MC_UNK_111_PH_N_MODE_1 (1 << 1) - -/* superious rm config */ -#define DIB3000MC_REG_UNK_120 ( 120) -#define DIB3000MC_UNK_120 ( 8207) - -#define DIB3000MC_REG_UNK_133 ( 133) -#define DIB3000MC_UNK_133 ( 15564) - -#define DIB3000MC_REG_UNK_134 ( 134) -#define DIB3000MC_UNK_134 ( 0) - -/* adapter config for constellation */ -static u16 dib3000mc_reg_adp_cfg[] = { 129, 130, 131, 132 }; - -static u16 dib3000mc_adp_cfg[][4] = { - { 0x99a, 0x7fae, 0x333, 0x7ff0 }, /* QPSK */ - { 0x23d, 0x7fdf, 0x0a4, 0x7ff0 }, /* 16-QAM */ - { 0x148, 0x7ff0, 0x0a4, 0x7ff8 }, /* 64-QAM */ -}; - -static u16 dib3000mc_reg_mobile_mode[] = { 139, 140, 141, 175, 1032 }; - -static u16 dib3000mc_mobile_mode[][5] = { - { 0x01, 0x0, 0x0, 0x00, 0x12c }, /* fixed */ - { 0x01, 0x0, 0x0, 0x00, 0x12c }, /* portable */ - { 0x00, 0x0, 0x0, 0x02, 0x000 }, /* mobile */ - { 0x00, 0x0, 0x0, 0x02, 0x000 }, /* auto */ -}; - -#define DIB3000MC_REG_DIVERSITY1 ( 177) -#define DIB3000MC_DIVERSITY1_DEFAULT ( 1) - -#define DIB3000MC_REG_DIVERSITY2 ( 178) -#define DIB3000MC_DIVERSITY2_DEFAULT ( 1) - -#define DIB3000MC_REG_DIVERSITY3 ( 180) -#define DIB3000MC_DIVERSITY3_IN_OFF (0xfff0) -#define DIB3000MC_DIVERSITY3_IN_ON (0xfff6) - -#define DIB3000MC_REG_FEC_CFG ( 195) -#define DIB3000MC_FEC_CFG ( 0x10) - -/* - * reg 206, output mode - * 1111 1111 - * |||| |||| - * |||| |||+- unk - * |||| ||+-- unk - * |||| |+--- unk (on by default) - * |||| +---- fifo_ctrl (1 = inhibit (flushed), 0 = active (unflushed)) - * |||+------ pid_parse (1 = enabled, 0 = disabled) - * ||+------- outp_188 (1 = TS packet size 188, 0 = packet size 204) - * |+-------- unk - * +--------- unk - */ - -#define DIB3000MC_REG_SMO_MODE ( 206) -#define DIB3000MC_SMO_MODE_DEFAULT (1 << 2) -#define DIB3000MC_SMO_MODE_FIFO_FLUSH (1 << 3) -#define DIB3000MC_SMO_MODE_FIFO_UNFLUSH (0xfff7) -#define DIB3000MC_SMO_MODE_PID_PARSE (1 << 4) -#define DIB3000MC_SMO_MODE_NO_PID_PARSE (0xffef) -#define DIB3000MC_SMO_MODE_188 (1 << 5) -#define DIB3000MC_SMO_MODE_SLAVE (DIB3000MC_SMO_MODE_DEFAULT | \ - DIB3000MC_SMO_MODE_188 | DIB3000MC_SMO_MODE_PID_PARSE | (1<<1)) - -#define DIB3000MC_REG_FIFO_THRESHOLD ( 207) -#define DIB3000MC_FIFO_THRESHOLD_DEFAULT ( 1792) -#define DIB3000MC_FIFO_THRESHOLD_SLAVE ( 512) -/* - * pidfilter - * it is not a hardware pidfilter but a filter which drops all pids - * except the ones set. When connected to USB1.1 bandwidth this is important. - * DiB3000P/M-C can filter up to 32 PIDs - */ -#define DIB3000MC_REG_FIRST_PID ( 212) -#define DIB3000MC_NUM_PIDS ( 32) - -#define DIB3000MC_REG_OUTMODE ( 244) -#define DIB3000MC_OM_PARALLEL_GATED_CLK ( 0) -#define DIB3000MC_OM_PAR_CONT_CLK (1 << 11) -#define DIB3000MC_OM_SERIAL (2 << 11) -#define DIB3000MC_OM_DIVOUT_ON (4 << 11) -#define DIB3000MC_OM_SLAVE (DIB3000MC_OM_DIVOUT_ON | DIB3000MC_OM_PAR_CONT_CLK) - -#define DIB3000MC_REG_RF_POWER ( 392) - -#define DIB3000MC_REG_FFT_POSITION ( 407) - -#define DIB3000MC_REG_DDS_FREQ_MSB ( 414) -#define DIB3000MC_REG_DDS_FREQ_LSB ( 415) - -#define DIB3000MC_REG_TIMING_OFFS_MSB ( 416) -#define DIB3000MC_REG_TIMING_OFFS_LSB ( 417) - -#define DIB3000MC_REG_TUNING_PARM ( 458) -#define DIB3000MC_TP_QAM(v) ((v >> 13) & 0x03) -#define DIB3000MC_TP_HRCH(v) ((v >> 12) & 0x01) -#define DIB3000MC_TP_ALPHA(v) ((v >> 9) & 0x07) -#define DIB3000MC_TP_FFT(v) ((v >> 8) & 0x01) -#define DIB3000MC_TP_FEC_CR_HP(v) ((v >> 5) & 0x07) -#define DIB3000MC_TP_FEC_CR_LP(v) ((v >> 2) & 0x07) -#define DIB3000MC_TP_GUARD(v) (v & 0x03) - -#define DIB3000MC_REG_SIGNAL_NOISE_MSB ( 483) -#define DIB3000MC_REG_SIGNAL_NOISE_LSB ( 484) - -#define DIB3000MC_REG_MER ( 485) - -#define DIB3000MC_REG_BER_MSB ( 500) -#define DIB3000MC_REG_BER_LSB ( 501) - -#define DIB3000MC_REG_PACKET_ERRORS ( 503) - -#define DIB3000MC_REG_PACKET_ERROR_COUNT ( 506) - -#define DIB3000MC_REG_LOCK_507 ( 507) -#define DIB3000MC_LOCK_507 (0x0002) // ? name correct ? - -#define DIB3000MC_REG_LOCKING ( 509) -#define DIB3000MC_AGC_LOCK(v) (v & 0x8000) -#define DIB3000MC_CARRIER_LOCK(v) (v & 0x2000) -#define DIB3000MC_MPEG_SYNC_LOCK(v) (v & 0x0080) -#define DIB3000MC_MPEG_DATA_LOCK(v) (v & 0x0040) -#define DIB3000MC_TPS_LOCK(v) (v & 0x0004) - -#define DIB3000MC_REG_AS_IRQ ( 511) -#define DIB3000MC_AS_IRQ_SUCCESS (1 << 1) -#define DIB3000MC_AS_IRQ_FAIL ( 1) - -#define DIB3000MC_REG_TUNER ( 769) - -#define DIB3000MC_REG_RST_I2C_ADDR ( 1024) -#define DIB3000MC_DEMOD_ADDR_ON ( 1) -#define DIB3000MC_DEMOD_ADDR(a) ((a << 4) & 0x03F0) - -#define DIB3000MC_REG_RESTART ( 1027) -#define DIB3000MC_RESTART_OFF (0x0000) -#define DIB3000MC_RESTART_AGC (0x0800) -#define DIB3000MC_RESTART_CONFIG (0x8000) - -#define DIB3000MC_REG_RESTART_VIT ( 1028) -#define DIB3000MC_RESTART_VIT_OFF ( 0) -#define DIB3000MC_RESTART_VIT_ON ( 1) - -#define DIB3000MC_REG_CLK_CFG_1 ( 1031) -#define DIB3000MC_CLK_CFG_1_POWER_UP ( 0) -#define DIB3000MC_CLK_CFG_1_POWER_DOWN (0xffff) - -#define DIB3000MC_REG_CLK_CFG_2 ( 1032) -#define DIB3000MC_CLK_CFG_2_PUP_FIXED (0x012c) -#define DIB3000MC_CLK_CFG_2_PUP_PORT (0x0104) -#define DIB3000MC_CLK_CFG_2_PUP_MOBILE (0x0000) -#define DIB3000MC_CLK_CFG_2_POWER_DOWN (0xffff) - -#define DIB3000MC_REG_CLK_CFG_3 ( 1033) -#define DIB3000MC_CLK_CFG_3_POWER_UP ( 0) -#define DIB3000MC_CLK_CFG_3_POWER_DOWN (0xfff5) - -#define DIB3000MC_REG_CLK_CFG_7 ( 1037) -#define DIB3000MC_CLK_CFG_7_INIT ( 12592) -#define DIB3000MC_CLK_CFG_7_POWER_UP (~0x0003) -#define DIB3000MC_CLK_CFG_7_PWR_DOWN (0x0003) -#define DIB3000MC_CLK_CFG_7_DIV_IN_OFF (1 << 8) - -/* was commented out ??? */ -#define DIB3000MC_REG_CLK_CFG_8 ( 1038) -#define DIB3000MC_CLK_CFG_8_POWER_UP (0x160c) - -#define DIB3000MC_REG_CLK_CFG_9 ( 1039) -#define DIB3000MC_CLK_CFG_9_POWER_UP ( 0) - -/* also clock ??? */ -#define DIB3000MC_REG_ELEC_OUT ( 1040) -#define DIB3000MC_ELEC_OUT_HIGH_Z ( 0) -#define DIB3000MC_ELEC_OUT_DIV_OUT_ON ( 1) -#define DIB3000MC_ELEC_OUT_SLAVE ( 3) - -#endif diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c new file mode 100644 index 00000000000..a18c8f45a2e --- /dev/null +++ b/drivers/media/dvb/frontends/dibx000_common.c @@ -0,0 +1,152 @@ +#include <linux/i2c.h> + +#include "dibx000_common.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiBX000: "); printk(args); } } while (0) + +static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val) +{ + u8 b[4] = { + (reg >> 8) & 0xff, reg & 0xff, + (val >> 8) & 0xff, val & 0xff, + }; + struct i2c_msg msg = { + .addr = mst->i2c_addr, .flags = 0, .buf = b, .len = 4 + }; + return i2c_transfer(mst->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; +} + + +static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf) +{ + if (mst->device_rev > DIB3000MC && mst->selected_interface != intf) { + dprintk("selecting interface: %d\n",intf); + mst->selected_interface = intf; + return dibx000_write_word(mst, mst->base_reg + 4, intf); + } + return 0; +} + +static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], u8 addr, int onoff) +{ + u16 val; + + + if (onoff) + val = addr << 8; // bit 7 = use master or not, if 0, the gate is open + else + val = 1 << 7; + + if (mst->device_rev > DIB7000) + val <<= 1; + + tx[0] = (((mst->base_reg + 1) >> 8) & 0xff); + tx[1] = ( (mst->base_reg + 1) & 0xff); + tx[2] = val >> 8; + tx[3] = val & 0xff; + + return 0; +} + +static u32 dibx000_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); + struct i2c_msg m[2 + num]; + u8 tx_open[4], tx_close[4]; + + memset(m,0, sizeof(struct i2c_msg) * (2 + num)); + + dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); + + dibx000_i2c_gate_ctrl(mst, tx_open, msg[0].addr, 1); + m[0].addr = mst->i2c_addr; + m[0].buf = tx_open; + m[0].len = 4; + + memcpy(&m[1], msg, sizeof(struct i2c_msg) * num); + + dibx000_i2c_gate_ctrl(mst, tx_close, 0, 0); + m[num+1].addr = mst->i2c_addr; + m[num+1].buf = tx_close; + m[num+1].len = 4; + + return i2c_transfer(mst->i2c_adap, m, 2+num) == 2 + num ? num : -EIO; +} + +static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = { + .master_xfer = dibx000_i2c_gated_tuner_xfer, + .functionality = dibx000_i2c_func, +}; + +struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating) +{ + struct i2c_adapter *i2c = NULL; + + switch (intf) { + case DIBX000_I2C_INTERFACE_TUNER: + if (gating) + i2c = &mst->gated_tuner_i2c_adap; + break; + default: + printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n"); + break; + } + + return i2c; +} +EXPORT_SYMBOL(dibx000_get_i2c_adapter); + +static int i2c_adapter_init(struct i2c_adapter *i2c_adap, struct i2c_algorithm *algo, const char name[I2C_NAME_SIZE], struct dibx000_i2c_master *mst) +{ + strncpy(i2c_adap->name, name, I2C_NAME_SIZE); + i2c_adap->class = I2C_CLASS_TV_DIGITAL, + i2c_adap->algo = algo; + i2c_adap->algo_data = NULL; + i2c_set_adapdata(i2c_adap, mst); + if (i2c_add_adapter(i2c_adap) < 0) + return -ENODEV; + return 0; +} + +int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, struct i2c_adapter *i2c_adap, u8 i2c_addr) +{ + u8 tx[4]; + struct i2c_msg m = { .addr = i2c_addr >> 1, .buf = tx, .len = 4 }; + + mst->device_rev = device_rev; + mst->i2c_adap = i2c_adap; + mst->i2c_addr = i2c_addr >> 1; + + if (device_rev == DIB7000P) + mst->base_reg = 1024; + else + mst->base_reg = 768; + + if (i2c_adapter_init(&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, "DiBX000 tuner I2C bus", mst) != 0) + printk(KERN_ERR "DiBX000: could not initialize the tuner i2c_adapter\n"); + + /* initialize the i2c-master by closing the gate */ + dibx000_i2c_gate_ctrl(mst, tx, 0, 0); + + return i2c_transfer(i2c_adap, &m, 1) == 1; +} +EXPORT_SYMBOL(dibx000_init_i2c_master); + +void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst) +{ + i2c_del_adapter(&mst->gated_tuner_i2c_adap); +} +EXPORT_SYMBOL(dibx000_exit_i2c_master); + +MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>"); +MODULE_DESCRIPTION("Common function the DiBcom demodulator family"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h new file mode 100644 index 00000000000..bb0c65f8aee --- /dev/null +++ b/drivers/media/dvb/frontends/dibx000_common.h @@ -0,0 +1,166 @@ +#ifndef DIBX000_COMMON_H +#define DIBX000_COMMON_H + +enum dibx000_i2c_interface { + DIBX000_I2C_INTERFACE_TUNER = 0, + DIBX000_I2C_INTERFACE_GPIO_1_2 = 1, + DIBX000_I2C_INTERFACE_GPIO_3_4 = 2 +}; + +struct dibx000_i2c_master { +#define DIB3000MC 1 +#define DIB7000 2 +#define DIB7000P 11 +#define DIB7000MC 12 + u16 device_rev; + + enum dibx000_i2c_interface selected_interface; + +// struct i2c_adapter tuner_i2c_adap; + struct i2c_adapter gated_tuner_i2c_adap; + + struct i2c_adapter *i2c_adap; + u8 i2c_addr; + + u16 base_reg; +}; + +extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, struct i2c_adapter *i2c_adap, u8 i2c_addr); +extern struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating); +extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst); + +#define BAND_LBAND 0x01 +#define BAND_UHF 0x02 +#define BAND_VHF 0x04 + +struct dibx000_agc_config { + /* defines the capabilities of this AGC-setting - using the BAND_-defines*/ + u8 band_caps; + + u16 setup; + + u16 inv_gain; + u16 time_stabiliz; + + u8 alpha_level; + u16 thlock; + + u8 wbd_inv; + u16 wbd_ref; + u8 wbd_sel; + u8 wbd_alpha; + + u16 agc1_max; + u16 agc1_min; + u16 agc2_max; + u16 agc2_min; + + u8 agc1_pt1; + u8 agc1_pt2; + u8 agc1_pt3; + + u8 agc1_slope1; + u8 agc1_slope2; + + u8 agc2_pt1; + u8 agc2_pt2; + + u8 agc2_slope1; + u8 agc2_slope2; + + u8 alpha_mant; + u8 alpha_exp; + + u8 beta_mant; + u8 beta_exp; + + u8 perform_agc_softsplit; + + struct { + u16 min; + u16 max; + u16 min_thres; + u16 max_thres; + } split; +}; + +struct dibx000_bandwidth_config { + u32 internal; + u32 sampling; + + u8 pll_prediv; + u8 pll_ratio; + u8 pll_range; + u8 pll_reset; + u8 pll_bypass; + + u8 enable_refdiv; + u8 bypclk_div; + u8 IO_CLK_en_core; + u8 ADClkSrc; + u8 modulo; + + u16 sad_cfg; + + u32 ifreq; + u32 timf; +}; + +enum dibx000_adc_states { + DIBX000_SLOW_ADC_ON = 0, + DIBX000_SLOW_ADC_OFF, + DIBX000_ADC_ON, + DIBX000_ADC_OFF, + DIBX000_VBG_ENABLE, + DIBX000_VBG_DISABLE, +}; + +#define BW_INDEX_TO_KHZ(v) ( (v) == BANDWIDTH_8_MHZ ? 8000 : \ + (v) == BANDWIDTH_7_MHZ ? 7000 : \ + (v) == BANDWIDTH_6_MHZ ? 6000 : 8000 ) + +/* Chip output mode. */ +#define OUTMODE_HIGH_Z 0 +#define OUTMODE_MPEG2_PAR_GATED_CLK 1 +#define OUTMODE_MPEG2_PAR_CONT_CLK 2 +#define OUTMODE_MPEG2_SERIAL 7 +#define OUTMODE_DIVERSITY 4 +#define OUTMODE_MPEG2_FIFO 5 + +/* I hope I can get rid of the following kludge in the near future */ +struct dibx000_ofdm_channel { + u8 Bw; + s16 nfft; + s16 guard; + s16 nqam; + s16 vit_hrch; + s16 vit_select_hp; + s16 vit_alpha; + s16 vit_code_rate_hp; + s16 vit_code_rate_lp; +}; + +#define FEP2DIB(fep,ch) \ + (ch)->Bw = (fep)->u.ofdm.bandwidth; \ + (ch)->nfft = (fep)->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ? -1 : (fep)->u.ofdm.transmission_mode; \ + (ch)->guard = (fep)->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ? -1 : (fep)->u.ofdm.guard_interval; \ + (ch)->nqam = (fep)->u.ofdm.constellation == QAM_AUTO ? -1 : (fep)->u.ofdm.constellation == QAM_64 ? 2 : (fep)->u.ofdm.constellation; \ + (ch)->vit_hrch = 0; /* linux-dvb is not prepared for HIERARCHICAL TRANSMISSION */ \ + (ch)->vit_select_hp = 1; \ + (ch)->vit_alpha = 1; \ + (ch)->vit_code_rate_hp = (fep)->u.ofdm.code_rate_HP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_HP; \ + (ch)->vit_code_rate_lp = (fep)->u.ofdm.code_rate_LP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_LP; + +#define INIT_OFDM_CHANNEL(ch) do {\ + (ch)->Bw = 0; \ + (ch)->nfft = -1; \ + (ch)->guard = -1; \ + (ch)->nqam = -1; \ + (ch)->vit_hrch = -1; \ + (ch)->vit_select_hp = -1; \ + (ch)->vit_alpha = -1; \ + (ch)->vit_code_rate_hp = -1; \ + (ch)->vit_code_rate_lp = -1; \ +} while (0) + +#endif diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c index 2be33f27c69..b7e7108ee5b 100644 --- a/drivers/media/dvb/frontends/dvb-pll.c +++ b/drivers/media/dvb/frontends/dvb-pll.c @@ -493,6 +493,9 @@ static int dvb_pll_sleep(struct dvb_frontend *fe) int i; int result; + if (priv->i2c == NULL) + return -EINVAL; + for (i = 0; i < priv->pll_desc->count; i++) { if (priv->pll_desc->entries[i].limit == 0) break; @@ -611,7 +614,7 @@ static struct dvb_tuner_ops dvb_pll_tuner_ops = { .get_bandwidth = dvb_pll_get_bandwidth, }; -int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc) +struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc) { u8 b1 [] = { 0 }; struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }; @@ -624,14 +627,14 @@ int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2 ret = i2c_transfer (i2c, &msg, 1); if (ret != 1) - return -1; + return NULL; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); } priv = kzalloc(sizeof(struct dvb_pll_priv), GFP_KERNEL); if (priv == NULL) - return -ENOMEM; + return NULL; priv->pll_i2c_address = pll_addr; priv->i2c = i2c; @@ -643,7 +646,7 @@ int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2 fe->ops.tuner_ops.info.frequency_min = desc->max; fe->tuner_priv = priv; - return 0; + return fe; } EXPORT_SYMBOL(dvb_pll_attach); diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h index 66361cd1880..ed5ac5a361a 100644 --- a/drivers/media/dvb/frontends/dvb-pll.h +++ b/drivers/media/dvb/frontends/dvb-pll.h @@ -57,8 +57,8 @@ extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, * @param pll_addr i2c address of the PLL (if used). * @param i2c i2c adapter to use (set to NULL if not used). * @param desc dvb_pll_desc to use. - * @return 0 on success, nonzero on failure. + * @return Frontend pointer on success, NULL on failure */ -extern int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc); +extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc); #endif diff --git a/drivers/media/dvb/frontends/isl6421.c b/drivers/media/dvb/frontends/isl6421.c index 58c34db3107..ef319369ec2 100644 --- a/drivers/media/dvb/frontends/isl6421.c +++ b/drivers/media/dvb/frontends/isl6421.c @@ -42,12 +42,11 @@ struct isl6421 { u8 override_and; struct i2c_adapter *i2c; u8 i2c_addr; - void (*release_chain)(struct dvb_frontend* fe); }; static int isl6421_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { - struct isl6421 *isl6421 = (struct isl6421 *) fe->misc_priv; + struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, .buf = &isl6421->config, .len = sizeof(isl6421->config) }; @@ -75,7 +74,7 @@ static int isl6421_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) { - struct isl6421 *isl6421 = (struct isl6421 *) fe->misc_priv; + struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, .buf = &isl6421->config, .len = sizeof(isl6421->config) }; @@ -93,31 +92,26 @@ static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) static void isl6421_release(struct dvb_frontend *fe) { - struct isl6421 *isl6421 = (struct isl6421 *) fe->misc_priv; - /* power off */ isl6421_set_voltage(fe, SEC_VOLTAGE_OFF); - /* free data & call next release routine */ - fe->ops.release = isl6421->release_chain; - kfree(fe->misc_priv); - fe->misc_priv = NULL; - if (fe->ops.release) - fe->ops.release(fe); + /* free */ + kfree(fe->sec_priv); + fe->sec_priv = NULL; } -int isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, +struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, u8 override_set, u8 override_clear) { struct isl6421 *isl6421 = kmalloc(sizeof(struct isl6421), GFP_KERNEL); if (!isl6421) - return -ENOMEM; + return NULL; /* default configuration */ isl6421->config = ISL6421_ISEL1; isl6421->i2c = i2c; isl6421->i2c_addr = i2c_addr; - fe->misc_priv = isl6421; + fe->sec_priv = isl6421; /* bits which should be forced to '1' */ isl6421->override_or = override_set; @@ -128,19 +122,17 @@ int isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr /* detect if it is present or not */ if (isl6421_set_voltage(fe, SEC_VOLTAGE_OFF)) { kfree(isl6421); - fe->misc_priv = NULL; - return -EIO; + return NULL; } /* install release callback */ - isl6421->release_chain = fe->ops.release; - fe->ops.release = isl6421_release; + fe->ops.release_sec = isl6421_release; /* override frontend ops */ fe->ops.set_voltage = isl6421_set_voltage; fe->ops.enable_high_lnb_voltage = isl6421_enable_high_lnb_voltage; - return 0; + return fe; } EXPORT_SYMBOL(isl6421_attach); diff --git a/drivers/media/dvb/frontends/isl6421.h b/drivers/media/dvb/frontends/isl6421.h index 675f80a19b9..1916e3eb2df 100644 --- a/drivers/media/dvb/frontends/isl6421.h +++ b/drivers/media/dvb/frontends/isl6421.h @@ -39,8 +39,17 @@ #define ISL6421_ISEL1 0x20 #define ISL6421_DCL 0x40 +#if defined(CONFIG_DVB_ISL6421) || defined(CONFIG_DVB_ISL6421_MODULE) /* override_set and override_clear control which system register bits (above) to always set & clear */ -extern int isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, +extern struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, u8 override_set, u8 override_clear); +#else +static inline struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, + u8 override_set, u8 override_clear) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_ISL6421 #endif diff --git a/drivers/media/dvb/frontends/l64781.h b/drivers/media/dvb/frontends/l64781.h index 83b8bc21027..21ba4a23076 100644 --- a/drivers/media/dvb/frontends/l64781.h +++ b/drivers/media/dvb/frontends/l64781.h @@ -31,8 +31,16 @@ struct l64781_config u8 demod_address; }; - +#if defined(CONFIG_DVB_L64781) || defined(CONFIG_DVB_L64781_MODULE) extern struct dvb_frontend* l64781_attach(const struct l64781_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* l64781_attach(const struct l64781_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_L64781 #endif // L64781_H diff --git a/drivers/media/dvb/frontends/lgdt330x.h b/drivers/media/dvb/frontends/lgdt330x.h index bad903c6f0f..3f96b485584 100644 --- a/drivers/media/dvb/frontends/lgdt330x.h +++ b/drivers/media/dvb/frontends/lgdt330x.h @@ -52,8 +52,17 @@ struct lgdt330x_config int clock_polarity_flip; }; +#if defined(CONFIG_DVB_LGDT330X) || defined(CONFIG_DVB_LGDT330X_MODULE) extern struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_LGDT330X #endif /* LGDT330X_H */ diff --git a/drivers/media/dvb/frontends/lnbp21.c b/drivers/media/dvb/frontends/lnbp21.c index e933edc8dd2..2d2f58c2622 100644 --- a/drivers/media/dvb/frontends/lnbp21.c +++ b/drivers/media/dvb/frontends/lnbp21.c @@ -40,12 +40,11 @@ struct lnbp21 { u8 override_or; u8 override_and; struct i2c_adapter *i2c; - void (*release_chain)(struct dvb_frontend* fe); }; static int lnbp21_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { - struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv; + struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; struct i2c_msg msg = { .addr = 0x08, .flags = 0, .buf = &lnbp21->config, .len = sizeof(lnbp21->config) }; @@ -73,7 +72,7 @@ static int lnbp21_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) { - struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv; + struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; struct i2c_msg msg = { .addr = 0x08, .flags = 0, .buf = &lnbp21->config, .len = sizeof(lnbp21->config) }; @@ -91,29 +90,24 @@ static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) static void lnbp21_release(struct dvb_frontend *fe) { - struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv; - /* LNBP power off */ lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF); - /* free data & call next release routine */ - fe->ops.release = lnbp21->release_chain; - kfree(fe->misc_priv); - fe->misc_priv = NULL; - if (fe->ops.release) - fe->ops.release(fe); + /* free data */ + kfree(fe->sec_priv); + fe->sec_priv = NULL; } -int lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear) +struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear) { struct lnbp21 *lnbp21 = kmalloc(sizeof(struct lnbp21), GFP_KERNEL); if (!lnbp21) - return -ENOMEM; + return NULL; /* default configuration */ lnbp21->config = LNBP21_ISEL; lnbp21->i2c = i2c; - fe->misc_priv = lnbp21; + fe->sec_priv = lnbp21; /* bits which should be forced to '1' */ lnbp21->override_or = override_set; @@ -124,19 +118,17 @@ int lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_ /* detect if it is present or not */ if (lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF)) { kfree(lnbp21); - fe->misc_priv = NULL; - return -EIO; + return NULL; } /* install release callback */ - lnbp21->release_chain = fe->ops.release; - fe->ops.release = lnbp21_release; + fe->ops.release_sec = lnbp21_release; /* override frontend ops */ fe->ops.set_voltage = lnbp21_set_voltage; fe->ops.enable_high_lnb_voltage = lnbp21_enable_high_lnb_voltage; - return 0; + return fe; } EXPORT_SYMBOL(lnbp21_attach); diff --git a/drivers/media/dvb/frontends/lnbp21.h b/drivers/media/dvb/frontends/lnbp21.h index 047a4ab68c0..1fe1dd17931 100644 --- a/drivers/media/dvb/frontends/lnbp21.h +++ b/drivers/media/dvb/frontends/lnbp21.h @@ -39,7 +39,15 @@ #include <linux/dvb/frontend.h> +#if defined(CONFIG_DVB_LNBP21) || defined(CONFIG_DVB_LNBP21_MODULE) /* override_set and override_clear control which system register bits (above) to always set & clear */ -extern int lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear); +extern struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear); +#else +static inline struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_LNBP21 -#endif +#endif // _LNBP21_H diff --git a/drivers/media/dvb/frontends/mt2060.c b/drivers/media/dvb/frontends/mt2060.c new file mode 100644 index 00000000000..508ec1b6d1f --- /dev/null +++ b/drivers/media/dvb/frontends/mt2060.c @@ -0,0 +1,367 @@ +/* + * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" + * + * Copyright (c) 2006 Olivier DANET <odanet@caramail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= + */ + +/* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/delay.h> +#include <linux/dvb/frontend.h> +#include <linux/i2c.h> + +#include "dvb_frontend.h" + +#include "mt2060.h" +#include "mt2060_priv.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0) + +// Reads a single register +static int mt2060_readreg(struct mt2060_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + printk(KERN_WARNING "mt2060 I2C read failed\n"); + return -EREMOTEIO; + } + return 0; +} + +// Writes a single register +static int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + struct i2c_msg msg = { + .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 + }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "mt2060 I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +// Writes a set of consecutive registers +static int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len) +{ + struct i2c_msg msg = { + .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len + }; + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n",(int)len); + return -EREMOTEIO; + } + return 0; +} + +// Initialisation sequences +// LNABAND=3, NUM1=0x3C, DIV1=0x74, NUM2=0x1080, DIV2=0x49 +static u8 mt2060_config1[] = { + REG_LO1C1, + 0x3F, 0x74, 0x00, 0x08, 0x93 +}; + +// FMCG=2, GP2=0, GP1=0 +static u8 mt2060_config2[] = { + REG_MISC_CTRL, + 0x20, 0x1E, 0x30, 0xff, 0x80, 0xff, 0x00, 0x2c, 0x42 +}; + +// VGAG=3, V1CSE=1 + +#ifdef MT2060_SPURCHECK +/* The function below calculates the frequency offset between the output frequency if2 + and the closer cross modulation subcarrier between lo1 and lo2 up to the tenth harmonic */ +static int mt2060_spurcalc(u32 lo1,u32 lo2,u32 if2) +{ + int I,J; + int dia,diamin,diff; + diamin=1000000; + for (I = 1; I < 10; I++) { + J = ((2*I*lo1)/lo2+1)/2; + diff = I*(int)lo1-J*(int)lo2; + if (diff < 0) diff=-diff; + dia = (diff-(int)if2); + if (dia < 0) dia=-dia; + if (diamin > dia) diamin=dia; + } + return diamin; +} + +#define BANDWIDTH 4000 // kHz + +/* Calculates the frequency offset to add to avoid spurs. Returns 0 if no offset is needed */ +static int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2) +{ + u32 Spur,Sp1,Sp2; + int I,J; + I=0; + J=1000; + + Spur=mt2060_spurcalc(lo1,lo2,if2); + if (Spur < BANDWIDTH) { + /* Potential spurs detected */ + dprintk("Spurs before : f_lo1: %d f_lo2: %d (kHz)", + (int)lo1,(int)lo2); + I=1000; + Sp1 = mt2060_spurcalc(lo1+I,lo2+I,if2); + Sp2 = mt2060_spurcalc(lo1-I,lo2-I,if2); + + if (Sp1 < Sp2) { + J=-J; I=-I; Spur=Sp2; + } else + Spur=Sp1; + + while (Spur < BANDWIDTH) { + I += J; + Spur = mt2060_spurcalc(lo1+I,lo2+I,if2); + } + dprintk("Spurs after : f_lo1: %d f_lo2: %d (kHz)", + (int)(lo1+I),(int)(lo2+I)); + } + return I; +} +#endif + +#define IF2 36150 // IF2 frequency = 36.150 MHz +#define FREF 16000 // Quartz oscillator 16 MHz + +static int mt2060_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct mt2060_priv *priv; + int ret=0; + int i=0; + u32 freq; + u8 lnaband; + u32 f_lo1,f_lo2; + u32 div1,num1,div2,num2; + u8 b[8]; + u32 if1; + + priv = fe->tuner_priv; + + if1 = priv->if1_freq; + b[0] = REG_LO1B1; + b[1] = 0xFF; + + mt2060_writeregs(priv,b,2); + + freq = params->frequency / 1000; // Hz -> kHz + priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; + + f_lo1 = freq + if1 * 1000; + f_lo1 = (f_lo1 / 250) * 250; + f_lo2 = f_lo1 - freq - IF2; + // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise + f_lo2 = ((f_lo2 + 25) / 50) * 50; + priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000, + +#ifdef MT2060_SPURCHECK + // LO-related spurs detection and correction + num1 = mt2060_spurcheck(f_lo1,f_lo2,IF2); + f_lo1 += num1; + f_lo2 += num1; +#endif + //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 ) + num1 = f_lo1 / (FREF / 64); + div1 = num1 / 64; + num1 &= 0x3f; + + // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) + num2 = f_lo2 * 64 / (FREF / 128); + div2 = num2 / 8192; + num2 &= 0x1fff; + + if (freq <= 95000) lnaband = 0xB0; else + if (freq <= 180000) lnaband = 0xA0; else + if (freq <= 260000) lnaband = 0x90; else + if (freq <= 335000) lnaband = 0x80; else + if (freq <= 425000) lnaband = 0x70; else + if (freq <= 480000) lnaband = 0x60; else + if (freq <= 570000) lnaband = 0x50; else + if (freq <= 645000) lnaband = 0x40; else + if (freq <= 730000) lnaband = 0x30; else + if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10; + + b[0] = REG_LO1C1; + b[1] = lnaband | ((num1 >>2) & 0x0F); + b[2] = div1; + b[3] = (num2 & 0x0F) | ((num1 & 3) << 4); + b[4] = num2 >> 4; + b[5] = ((num2 >>12) & 1) | (div2 << 1); + + dprintk("IF1: %dMHz",(int)if1); + dprintk("PLL freq=%dkHz f_lo1=%dkHz f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2); + dprintk("PLL div1=%d num1=%d div2=%d num2=%d",(int)div1,(int)num1,(int)div2,(int)num2); + dprintk("PLL [1..5]: %2x %2x %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3],(int)b[4],(int)b[5]); + + mt2060_writeregs(priv,b,6); + + //Waits for pll lock or timeout + i = 0; + do { + mt2060_readreg(priv,REG_LO_STATUS,b); + if ((b[0] & 0x88)==0x88) + break; + msleep(4); + i++; + } while (i<10); + + return ret; +} + +static void mt2060_calibrate(struct mt2060_priv *priv) +{ + u8 b = 0; + int i = 0; + + if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1))) + return; + if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2))) + return; + + do { + b |= (1 << 6); // FM1SS; + mt2060_writereg(priv, REG_LO2C1,b); + msleep(20); + + if (i == 0) { + b |= (1 << 7); // FM1CA; + mt2060_writereg(priv, REG_LO2C1,b); + b &= ~(1 << 7); // FM1CA; + msleep(20); + } + + b &= ~(1 << 6); // FM1SS + mt2060_writereg(priv, REG_LO2C1,b); + + msleep(20); + i++; + } while (i < 9); + + i = 0; + while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0) + msleep(20); + + if (i < 10) { + mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :) + dprintk("calibration was successful: %d", (int)priv->fmfreq); + } else + dprintk("FMCAL timed out"); +} + +static int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mt2060_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int mt2060_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct mt2060_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +static int mt2060_init(struct dvb_frontend *fe) +{ + struct mt2060_priv *priv = fe->tuner_priv; + return mt2060_writereg(priv, REG_VGAG,0x33); +} + +static int mt2060_sleep(struct dvb_frontend *fe) +{ + struct mt2060_priv *priv = fe->tuner_priv; + return mt2060_writereg(priv, REG_VGAG,0x30); +} + +static int mt2060_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static const struct dvb_tuner_ops mt2060_tuner_ops = { + .info = { + .name = "Microtune MT2060", + .frequency_min = 48000000, + .frequency_max = 860000000, + .frequency_step = 50000, + }, + + .release = mt2060_release, + + .init = mt2060_init, + .sleep = mt2060_sleep, + + .set_params = mt2060_set_params, + .get_frequency = mt2060_get_frequency, + .get_bandwidth = mt2060_get_bandwidth +}; + +/* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */ +int mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) +{ + struct mt2060_priv *priv = NULL; + u8 id = 0; + + priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + priv->cfg = cfg; + priv->i2c = i2c; + priv->if1_freq = if1; + + if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) { + kfree(priv); + return -ENODEV; + } + + if (id != PART_REV) { + kfree(priv); + return -ENODEV; + } + printk(KERN_INFO "MT2060: successfully identified (IF1 = %d)\n", if1); + memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + + mt2060_calibrate(priv); + + return 0; +} +EXPORT_SYMBOL(mt2060_attach); + +MODULE_AUTHOR("Olivier DANET"); +MODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/mt2060.h b/drivers/media/dvb/frontends/mt2060.h new file mode 100644 index 00000000000..c58b03e8234 --- /dev/null +++ b/drivers/media/dvb/frontends/mt2060.h @@ -0,0 +1,35 @@ +/* + * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" + * + * Copyright (c) 2006 Olivier DANET <odanet@caramail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= + */ + +#ifndef MT2060_H +#define MT2060_H + +struct dvb_frontend; +struct i2c_adapter; + +struct mt2060_config { + u8 i2c_address; + /* Shall we add settings for the discrete outputs ? */ +}; + +extern int mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1); + +#endif diff --git a/drivers/media/dvb/frontends/mt2060_priv.h b/drivers/media/dvb/frontends/mt2060_priv.h new file mode 100644 index 00000000000..5eaccdefd0b --- /dev/null +++ b/drivers/media/dvb/frontends/mt2060_priv.h @@ -0,0 +1,105 @@ +/* + * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" + * + * Copyright (c) 2006 Olivier DANET <odanet@caramail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= + */ + +#ifndef MT2060_PRIV_H +#define MT2060_PRIV_H + +// Uncomment the #define below to enable spurs checking. The results where quite unconvincing. +// #define MT2060_SPURCHECK + +/* This driver is based on the information available in the datasheet of the + "Comtech SDVBT-3K6M" tuner ( K1000737843.pdf ) which features the MT2060 register map : + + I2C Address : 0x60 + + Reg.No | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 | ( defaults ) + -------------------------------------------------------------------------------- + 00 | [ PART ] | [ REV ] | R = 0x63 + 01 | [ LNABAND ] | [ NUM1(5:2) ] | RW = 0x3F + 02 | [ DIV1 ] | RW = 0x74 + 03 | FM1CA | FM1SS | [ NUM1(1:0) ] | [ NUM2(3:0) ] | RW = 0x00 + 04 | NUM2(11:4) ] | RW = 0x08 + 05 | [ DIV2 ] |NUM2(12)| RW = 0x93 + 06 | L1LK | [ TAD1 ] | L2LK | [ TAD2 ] | R + 07 | [ FMF ] | R + 08 | ? | FMCAL | ? | ? | ? | ? | ? | TEMP | R + 09 | 0 | 0 | [ FMGC ] | 0 | GP02 | GP01 | 0 | RW = 0x20 + 0A | ?? + 0B | 0 | 0 | 1 | 1 | 0 | 0 | [ VGAG ] | RW = 0x30 + 0C | V1CSE | 1 | 1 | 1 | 1 | 1 | 1 | 1 | RW = 0xFF + 0D | 1 | 0 | [ V1CS ] | RW = 0xB0 + 0E | ?? + 0F | ?? + 10 | ?? + 11 | [ LOTO ] | 0 | 0 | 1 | 0 | RW = 0x42 + + PART : Part code : 6 for MT2060 + REV : Revision code : 3 for current revision + LNABAND : Input frequency range : ( See code for details ) + NUM1 / DIV1 / NUM2 / DIV2 : Frequencies programming ( See code for details ) + FM1CA : Calibration Start Bit + FM1SS : Calibration Single Step bit + L1LK : LO1 Lock Detect + TAD1 : Tune Line ADC ( ? ) + L2LK : LO2 Lock Detect + TAD2 : Tune Line ADC ( ? ) + FMF : Estimated first IF Center frequency Offset ( ? ) + FM1CAL : Calibration done bit + TEMP : On chip temperature sensor + FMCG : Mixer 1 Cap Gain ( ? ) + GP01 / GP02 : Programmable digital outputs. Unconnected pins ? + V1CSE : LO1 VCO Automatic Capacitor Select Enable ( ? ) + V1CS : LO1 Capacitor Selection Value ( ? ) + LOTO : LO Timeout ( ? ) + VGAG : Tuner Output gain +*/ + +#define I2C_ADDRESS 0x60 + +#define REG_PART_REV 0 +#define REG_LO1C1 1 +#define REG_LO1C2 2 +#define REG_LO2C1 3 +#define REG_LO2C2 4 +#define REG_LO2C3 5 +#define REG_LO_STATUS 6 +#define REG_FM_FREQ 7 +#define REG_MISC_STAT 8 +#define REG_MISC_CTRL 9 +#define REG_RESERVED_A 0x0A +#define REG_VGAG 0x0B +#define REG_LO1B1 0x0C +#define REG_LO1B2 0x0D +#define REG_LOTO 0x11 + +#define PART_REV 0x63 // The current driver works only with PART=6 and REV=3 chips + +struct mt2060_priv { + struct mt2060_config *cfg; + struct i2c_adapter *i2c; + + u32 frequency; + u32 bandwidth; + u16 if1_freq; + u8 fmfreq; +}; + +#endif diff --git a/drivers/media/dvb/frontends/mt312.h b/drivers/media/dvb/frontends/mt312.h index 666a1bd1c24..7112fb4d58a 100644 --- a/drivers/media/dvb/frontends/mt312.h +++ b/drivers/media/dvb/frontends/mt312.h @@ -34,8 +34,16 @@ struct mt312_config u8 demod_address; }; +#if defined(CONFIG_DVB_MT312) || defined(CONFIG_DVB_MT312_MODULE) struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config, struct i2c_adapter* i2c); - +#else +static inline struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_MT312 #endif // MT312_H diff --git a/drivers/media/dvb/frontends/mt352.c b/drivers/media/dvb/frontends/mt352.c index 5de7376c94c..87e31ca7e10 100644 --- a/drivers/media/dvb/frontends/mt352.c +++ b/drivers/media/dvb/frontends/mt352.c @@ -70,7 +70,7 @@ static int mt352_single_write(struct dvb_frontend *fe, u8 reg, u8 val) return 0; } -int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen) +static int _mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen) { int err,i; for (i=0; i < ilen-1; i++) @@ -107,7 +107,7 @@ static int mt352_sleep(struct dvb_frontend* fe) { static u8 mt352_softdown[] = { CLOCK_CTL, 0x20, 0x08 }; - mt352_write(fe, mt352_softdown, sizeof(mt352_softdown)); + _mt352_write(fe, mt352_softdown, sizeof(mt352_softdown)); return 0; } @@ -293,14 +293,14 @@ static int mt352_set_parameters(struct dvb_frontend* fe, fe->ops.i2c_gate_ctrl(fe, 0); } - mt352_write(fe, buf, 8); - mt352_write(fe, fsm_go, 2); + _mt352_write(fe, buf, 8); + _mt352_write(fe, fsm_go, 2); } else { if (fe->ops.tuner_ops.calc_regs) { fe->ops.tuner_ops.calc_regs(fe, param, buf+8, 5); buf[8] <<= 1; - mt352_write(fe, buf, sizeof(buf)); - mt352_write(fe, tuner_go, 2); + _mt352_write(fe, buf, sizeof(buf)); + _mt352_write(fe, tuner_go, 2); } } @@ -522,7 +522,7 @@ static int mt352_init(struct dvb_frontend* fe) (mt352_read_register(state, CONFIG) & 0x20) == 0) { /* Do a "hard" reset */ - mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach)); + _mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach)); return state->config.demod_init(fe); } @@ -585,6 +585,7 @@ static struct dvb_frontend_ops mt352_ops = { .init = mt352_init, .sleep = mt352_sleep, + .write = _mt352_write, .set_frontend = mt352_set_parameters, .get_frontend = mt352_get_parameters, @@ -605,4 +606,3 @@ MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(mt352_attach); -EXPORT_SYMBOL(mt352_write); diff --git a/drivers/media/dvb/frontends/mt352.h b/drivers/media/dvb/frontends/mt352.h index 9e7ff4b8fe5..0035c2e2d7c 100644 --- a/drivers/media/dvb/frontends/mt352.h +++ b/drivers/media/dvb/frontends/mt352.h @@ -51,9 +51,23 @@ struct mt352_config int (*demod_init)(struct dvb_frontend* fe); }; +#if defined(CONFIG_DVB_MT352) || defined(CONFIG_DVB_MT352_MODULE) extern struct dvb_frontend* mt352_attach(const struct mt352_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* mt352_attach(const struct mt352_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_MT352 -extern int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen); +static inline int mt352_write(struct dvb_frontend *fe, u8 *buf, int len) { + int r = 0; + if (fe->ops.write) + r = fe->ops.write(fe, buf, len); + return r; +} #endif // MT352_H diff --git a/drivers/media/dvb/frontends/nxt200x.h b/drivers/media/dvb/frontends/nxt200x.h index 34d61735845..2eb220e9806 100644 --- a/drivers/media/dvb/frontends/nxt200x.h +++ b/drivers/media/dvb/frontends/nxt200x.h @@ -45,8 +45,17 @@ struct nxt200x_config int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); }; +#if defined(CONFIG_DVB_NXT200X) || defined(CONFIG_DVB_NXT200X_MODULE) extern struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_NXT200X #endif /* NXT200X_H */ diff --git a/drivers/media/dvb/frontends/nxt6000.h b/drivers/media/dvb/frontends/nxt6000.h index 117031d1170..9397393a6bd 100644 --- a/drivers/media/dvb/frontends/nxt6000.h +++ b/drivers/media/dvb/frontends/nxt6000.h @@ -33,7 +33,16 @@ struct nxt6000_config u8 clock_inversion:1; }; +#if defined(CONFIG_DVB_NXT6000) || defined(CONFIG_DVB_NXT6000_MODULE) extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_NXT6000 #endif // NXT6000_H diff --git a/drivers/media/dvb/frontends/or51132.h b/drivers/media/dvb/frontends/or51132.h index 89658883abf..9718be4fb83 100644 --- a/drivers/media/dvb/frontends/or51132.h +++ b/drivers/media/dvb/frontends/or51132.h @@ -34,8 +34,17 @@ struct or51132_config int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); }; +#if defined(CONFIG_DVB_OR51132) || defined(CONFIG_DVB_OR51132_MODULE) extern struct dvb_frontend* or51132_attach(const struct or51132_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* or51132_attach(const struct or51132_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_OR51132 #endif // OR51132_H diff --git a/drivers/media/dvb/frontends/or51211.h b/drivers/media/dvb/frontends/or51211.h index 13a5a3afbf8..10a5419f9e0 100644 --- a/drivers/media/dvb/frontends/or51211.h +++ b/drivers/media/dvb/frontends/or51211.h @@ -37,8 +37,17 @@ struct or51211_config void (*sleep)(struct dvb_frontend * fe); }; +#if defined(CONFIG_DVB_OR51211) || defined(CONFIG_DVB_OR51211_MODULE) extern struct dvb_frontend* or51211_attach(const struct or51211_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* or51211_attach(const struct or51211_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_OR51211 #endif // OR51211_H diff --git a/drivers/media/dvb/frontends/s5h1420.h b/drivers/media/dvb/frontends/s5h1420.h index 4e39015fa67..efc54d7f3c5 100644 --- a/drivers/media/dvb/frontends/s5h1420.h +++ b/drivers/media/dvb/frontends/s5h1420.h @@ -34,7 +34,16 @@ struct s5h1420_config u8 invert:1; }; +#if defined(CONFIG_DVB_S5H1420) || defined(CONFIG_DVB_S5H1420_MODULE) extern struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_S5H1420 #endif // S5H1420_H diff --git a/drivers/media/dvb/frontends/sp8870.h b/drivers/media/dvb/frontends/sp8870.h index 93afbb969d6..4cf27d3b10f 100644 --- a/drivers/media/dvb/frontends/sp8870.h +++ b/drivers/media/dvb/frontends/sp8870.h @@ -35,7 +35,16 @@ struct sp8870_config int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; +#if defined(CONFIG_DVB_SP8870) || defined(CONFIG_DVB_SP8870_MODULE) extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_SP8870 #endif // SP8870_H diff --git a/drivers/media/dvb/frontends/sp887x.h b/drivers/media/dvb/frontends/sp887x.h index c44b0ebdf1e..cab7ea644df 100644 --- a/drivers/media/dvb/frontends/sp887x.h +++ b/drivers/media/dvb/frontends/sp887x.h @@ -17,7 +17,16 @@ struct sp887x_config int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; +#if defined(CONFIG_DVB_SP887X) || defined(CONFIG_DVB_SP887X_MODULE) extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_SP887X #endif // SP887X_H diff --git a/drivers/media/dvb/frontends/stv0297.h b/drivers/media/dvb/frontends/stv0297.h index 1da5384fb98..760b80db43a 100644 --- a/drivers/media/dvb/frontends/stv0297.h +++ b/drivers/media/dvb/frontends/stv0297.h @@ -42,7 +42,16 @@ struct stv0297_config u8 stop_during_read:1; }; +#if defined(CONFIG_DVB_STV0297) || defined(CONFIG_DVB_STV0297_MODULE) extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_STV0297 #endif // STV0297_H diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c index 96648a75440..93483769eca 100644 --- a/drivers/media/dvb/frontends/stv0299.c +++ b/drivers/media/dvb/frontends/stv0299.c @@ -92,11 +92,14 @@ static int stv0299_writeregI (struct stv0299_state* state, u8 reg, u8 data) return (ret != 1) ? -EREMOTEIO : 0; } -int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data) +int stv0299_write(struct dvb_frontend* fe, u8 *buf, int len) { struct stv0299_state* state = fe->demodulator_priv; - return stv0299_writeregI(state, reg, data); + if (len != 2) + return -EINVAL; + + return stv0299_writeregI(state, buf[0], buf[1]); } static u8 stv0299_readreg (struct stv0299_state* state, u8 reg) @@ -694,6 +697,7 @@ static struct dvb_frontend_ops stv0299_ops = { .init = stv0299_init, .sleep = stv0299_sleep, + .write = stv0299_write, .i2c_gate_ctrl = stv0299_i2c_gate_ctrl, .set_frontend = stv0299_set_frontend, @@ -724,5 +728,4 @@ MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, " "Andreas Oberritter, Andrew de Quincey, Kenneth Aafly"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(stv0299_writereg); EXPORT_SYMBOL(stv0299_attach); diff --git a/drivers/media/dvb/frontends/stv0299.h b/drivers/media/dvb/frontends/stv0299.h index 1504828e423..7ef25207081 100644 --- a/drivers/media/dvb/frontends/stv0299.h +++ b/drivers/media/dvb/frontends/stv0299.h @@ -89,9 +89,24 @@ struct stv0299_config int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio); }; -extern int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data); - +#if defined(CONFIG_DVB_STV0299) || defined(CONFIG_DVB_STV0299_MODULE) extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_STV0299 + +static inline int stv0299_writereg(struct dvb_frontend *fe, u8 reg, u8 val) { + int r = 0; + u8 buf[] = {reg, val}; + if (fe->ops.write) + r = fe->ops.write(fe, buf, 2); + return r; +} #endif // STV0299_H diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c index 9cbd164aa28..dca89171be1 100644 --- a/drivers/media/dvb/frontends/tda10021.c +++ b/drivers/media/dvb/frontends/tda10021.c @@ -72,7 +72,7 @@ static u8 tda10021_inittab[0x40]= 0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00, }; -static int tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data) +static int _tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data) { u8 buf[] = { reg, data }; struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; @@ -88,14 +88,6 @@ static int tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data) return (ret != 1) ? -EREMOTEIO : 0; } -int tda10021_write_byte(struct dvb_frontend* fe, int reg, int data) -{ - struct tda10021_state* state = fe->demodulator_priv; - - return tda10021_writereg(state, reg, data); -} -EXPORT_SYMBOL(tda10021_write_byte); - static u8 tda10021_readreg (struct tda10021_state* state, u8 reg) { u8 b0 [] = { reg }; @@ -149,8 +141,8 @@ static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0, else if (INVERSION_OFF == inversion) DISABLE_INVERSION(reg0); - tda10021_writereg (state, 0x00, reg0 & 0xfe); - tda10021_writereg (state, 0x00, reg0 | 0x01); + _tda10021_writereg (state, 0x00, reg0 & 0xfe); + _tda10021_writereg (state, 0x00, reg0 | 0x01); state->reg0 = reg0; return 0; @@ -198,17 +190,27 @@ static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate NDEC = (NDEC << 6) | tda10021_inittab[0x03]; - tda10021_writereg (state, 0x03, NDEC); - tda10021_writereg (state, 0x0a, BDR&0xff); - tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff); - tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f); + _tda10021_writereg (state, 0x03, NDEC); + _tda10021_writereg (state, 0x0a, BDR&0xff); + _tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff); + _tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f); - tda10021_writereg (state, 0x0d, BDRI); - tda10021_writereg (state, 0x0e, SFIL); + _tda10021_writereg (state, 0x0d, BDRI); + _tda10021_writereg (state, 0x0e, SFIL); return 0; } +int tda10021_write(struct dvb_frontend* fe, u8 *buf, int len) +{ + struct tda10021_state* state = fe->demodulator_priv; + + if (len != 2) + return -EINVAL; + + return _tda10021_writereg(state, buf[0], buf[1]); +} + static int tda10021_init (struct dvb_frontend *fe) { struct tda10021_state* state = fe->demodulator_priv; @@ -216,12 +218,12 @@ static int tda10021_init (struct dvb_frontend *fe) dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num); - //tda10021_writereg (fe, 0, 0); + //_tda10021_writereg (fe, 0, 0); for (i=0; i<tda10021_inittab_size; i++) - tda10021_writereg (state, i, tda10021_inittab[i]); + _tda10021_writereg (state, i, tda10021_inittab[i]); - tda10021_writereg (state, 0x34, state->pwm); + _tda10021_writereg (state, 0x34, state->pwm); //Comment by markus //0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0) @@ -230,7 +232,7 @@ static int tda10021_init (struct dvb_frontend *fe) //0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0) //Activate PLL - tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef); + _tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef); return 0; } @@ -264,12 +266,12 @@ static int tda10021_set_parameters (struct dvb_frontend *fe, } tda10021_set_symbolrate (state, p->u.qam.symbol_rate); - tda10021_writereg (state, 0x34, state->pwm); + _tda10021_writereg (state, 0x34, state->pwm); - tda10021_writereg (state, 0x01, reg0x01[qam]); - tda10021_writereg (state, 0x05, reg0x05[qam]); - tda10021_writereg (state, 0x08, reg0x08[qam]); - tda10021_writereg (state, 0x09, reg0x09[qam]); + _tda10021_writereg (state, 0x01, reg0x01[qam]); + _tda10021_writereg (state, 0x05, reg0x05[qam]); + _tda10021_writereg (state, 0x08, reg0x08[qam]); + _tda10021_writereg (state, 0x09, reg0x09[qam]); tda10021_setup_reg0 (state, reg0x00[qam], p->inversion); @@ -342,8 +344,8 @@ static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) *ucblocks = 0xffffffff; /* reset uncorrected block counter */ - tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf); - tda10021_writereg (state, 0x10, tda10021_inittab[0x10]); + _tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf); + _tda10021_writereg (state, 0x10, tda10021_inittab[0x10]); return 0; } @@ -392,8 +394,8 @@ static int tda10021_sleep(struct dvb_frontend* fe) { struct tda10021_state* state = fe->demodulator_priv; - tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */ - tda10021_writereg (state, 0x00, 0x80); /* standby */ + _tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */ + _tda10021_writereg (state, 0x00, 0x80); /* standby */ return 0; } @@ -459,6 +461,7 @@ static struct dvb_frontend_ops tda10021_ops = { .init = tda10021_init, .sleep = tda10021_sleep, + .write = tda10021_write, .i2c_gate_ctrl = tda10021_i2c_gate_ctrl, .set_frontend = tda10021_set_parameters, diff --git a/drivers/media/dvb/frontends/tda10021.h b/drivers/media/dvb/frontends/tda10021.h index b1df4259bee..d68ae20c841 100644 --- a/drivers/media/dvb/frontends/tda10021.h +++ b/drivers/media/dvb/frontends/tda10021.h @@ -32,9 +32,24 @@ struct tda10021_config u8 demod_address; }; +#if defined(CONFIG_DVB_TDA10021) || defined(CONFIG_DVB_TDA10021_MODULE) extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, struct i2c_adapter* i2c, u8 pwm); - -extern int tda10021_write_byte(struct dvb_frontend* fe, int reg, int data); +#else +static inline struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, + struct i2c_adapter* i2c, u8 pwm) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_TDA10021 + +static inline int tda10021_writereg(struct dvb_frontend *fe, u8 reg, u8 val) { + int r = 0; + u8 buf[] = {reg, val}; + if (fe->ops.write) + r = fe->ops.write(fe, buf, 2); + return r; +} #endif // TDA10021_H diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index 59a2ed614fc..11e0dca9a2d 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/drivers/media/dvb/frontends/tda1004x.c @@ -579,11 +579,14 @@ static int tda1004x_decode_fec(int tdafec) return -1; } -int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data) +int tda1004x_write(struct dvb_frontend* fe, u8 *buf, int len) { struct tda1004x_state* state = fe->demodulator_priv; - return tda1004x_write_byteI(state, reg, data); + if (len != 2) + return -EINVAL; + + return tda1004x_write_byteI(state, buf[0], buf[1]); } static int tda10045_init(struct dvb_frontend* fe) @@ -1216,6 +1219,7 @@ static struct dvb_frontend_ops tda10045_ops = { .init = tda10045_init, .sleep = tda1004x_sleep, + .write = tda1004x_write, .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl, .set_frontend = tda1004x_set_fe, @@ -1274,6 +1278,7 @@ static struct dvb_frontend_ops tda10046_ops = { .init = tda10046_init, .sleep = tda1004x_sleep, + .write = tda1004x_write, .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl, .set_frontend = tda1004x_set_fe, @@ -1323,4 +1328,3 @@ MODULE_LICENSE("GPL"); EXPORT_SYMBOL(tda10045_attach); EXPORT_SYMBOL(tda10046_attach); -EXPORT_SYMBOL(tda1004x_write_byte); diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h index b877b23ed73..e28fca05734 100644 --- a/drivers/media/dvb/frontends/tda1004x.h +++ b/drivers/media/dvb/frontends/tda1004x.h @@ -71,12 +71,33 @@ struct tda1004x_config int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; +#if defined(CONFIG_DVB_TDA1004X) || defined(CONFIG_DVB_TDA1004X_MODULE) extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c); extern struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c); - -extern int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data); +#else +static inline struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +static inline struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_TDA1004X + +static inline int tda1004x_writereg(struct dvb_frontend *fe, u8 reg, u8 val) { + int r = 0; + u8 buf[] = {reg, val}; + if (fe->ops.write) + r = fe->ops.write(fe, buf, 2); + return r; +} #endif // TDA1004X_H diff --git a/drivers/media/dvb/frontends/tda10086.c b/drivers/media/dvb/frontends/tda10086.c new file mode 100644 index 00000000000..7456b0b9976 --- /dev/null +++ b/drivers/media/dvb/frontends/tda10086.c @@ -0,0 +1,740 @@ + /* + Driver for Philips tda10086 DVBS Demodulator + + (c) 2006 Andrew de Quincey + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/jiffies.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include "dvb_frontend.h" +#include "tda10086.h" + +#define SACLK 96000000 + +struct tda10086_state { + struct i2c_adapter* i2c; + const struct tda10086_config* config; + struct dvb_frontend frontend; + + /* private demod data */ + u32 frequency; + u32 symbol_rate; +}; + +static int debug = 0; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "tda10086: " args); \ + } while (0) + +static int tda10086_write_byte(struct tda10086_state *state, int reg, int data) +{ + int ret; + u8 b0[] = { reg, data }; + struct i2c_msg msg = { .flags = 0, .buf = b0, .len = 2 }; + + msg.addr = state->config->demod_address; + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", + __FUNCTION__, reg, data, ret); + + return (ret != 1) ? ret : 0; +} + +static int tda10086_read_byte(struct tda10086_state *state, int reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = {{ .flags = 0, .buf = b0, .len = 1 }, + { .flags = I2C_M_RD, .buf = b1, .len = 1 }}; + + msg[0].addr = state->config->demod_address; + msg[1].addr = state->config->demod_address; + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + dprintk("%s: error reg=0x%x, ret=%i\n", __FUNCTION__, reg, + ret); + return ret; + } + + return b1[0]; +} + +static int tda10086_write_mask(struct tda10086_state *state, int reg, int mask, int data) +{ + int val; + + // read a byte and check + val = tda10086_read_byte(state, reg); + if (val < 0) + return val; + + // mask if off + val = val & ~mask; + val |= data & 0xff; + + // write it out again + return tda10086_write_byte(state, reg, val); +} + +static int tda10086_init(struct dvb_frontend* fe) +{ + struct tda10086_state* state = fe->demodulator_priv; + + dprintk ("%s\n", __FUNCTION__); + + // reset + tda10086_write_byte(state, 0x00, 0x00); + msleep(10); + + // misc setup + tda10086_write_byte(state, 0x01, 0x94); + tda10086_write_byte(state, 0x02, 0x35); // NOTE: TT drivers appear to disable CSWP + tda10086_write_byte(state, 0x03, 0x64); + tda10086_write_byte(state, 0x04, 0x43); + tda10086_write_byte(state, 0x0c, 0x0c); + tda10086_write_byte(state, 0x1b, 0xb0); // noise threshold + tda10086_write_byte(state, 0x20, 0x89); // misc + tda10086_write_byte(state, 0x30, 0x04); // acquisition period length + tda10086_write_byte(state, 0x32, 0x00); // irq off + tda10086_write_byte(state, 0x31, 0x56); // setup AFC + + // setup PLL (assumes 16Mhz XIN) + tda10086_write_byte(state, 0x55, 0x2c); // misc PLL setup + tda10086_write_byte(state, 0x3a, 0x0b); // M=12 + tda10086_write_byte(state, 0x3b, 0x01); // P=2 + tda10086_write_mask(state, 0x55, 0x20, 0x00); // powerup PLL + + // setup TS interface + tda10086_write_byte(state, 0x11, 0x81); + tda10086_write_byte(state, 0x12, 0x81); + tda10086_write_byte(state, 0x19, 0x40); // parallel mode A + MSBFIRST + tda10086_write_byte(state, 0x56, 0x80); // powerdown WPLL - unused in the mode we use + tda10086_write_byte(state, 0x57, 0x08); // bypass WPLL - unused in the mode we use + tda10086_write_byte(state, 0x10, 0x2a); + + // setup ADC + tda10086_write_byte(state, 0x58, 0x61); // ADC setup + tda10086_write_mask(state, 0x58, 0x01, 0x00); // powerup ADC + + // setup AGC + tda10086_write_byte(state, 0x05, 0x0B); + tda10086_write_byte(state, 0x37, 0x63); + tda10086_write_byte(state, 0x3f, 0x03); // NOTE: flydvb uses 0x0a and varies it + tda10086_write_byte(state, 0x40, 0x64); + tda10086_write_byte(state, 0x41, 0x4f); + tda10086_write_byte(state, 0x42, 0x43); + + // setup viterbi + tda10086_write_byte(state, 0x1a, 0x11); // VBER 10^6, DVB, QPSK + + // setup carrier recovery + tda10086_write_byte(state, 0x3d, 0x80); + + // setup SEC + tda10086_write_byte(state, 0x36, 0x00); // all SEC off + tda10086_write_byte(state, 0x34, (((1<<19) * (22000/1000)) / (SACLK/1000))); // } tone frequency + tda10086_write_byte(state, 0x35, (((1<<19) * (22000/1000)) / (SACLK/1000)) >> 8); // } + + return 0; +} + +static void tda10086_diseqc_wait(struct tda10086_state *state) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(200); + while (!(tda10086_read_byte(state, 0x50) & 0x01)) { + if(time_after(jiffies, timeout)) { + printk("%s: diseqc queue not ready, command may be lost.\n", __FUNCTION__); + break; + } + msleep(10); + } +} + +static int tda10086_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct tda10086_state* state = fe->demodulator_priv; + + dprintk ("%s\n", __FUNCTION__); + + switch(tone) { + case SEC_TONE_OFF: + tda10086_write_byte(state, 0x36, 0x00); + break; + + case SEC_TONE_ON: + tda10086_write_byte(state, 0x36, 0x01); + break; + } + + return 0; +} + +static int tda10086_send_master_cmd (struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd* cmd) +{ + struct tda10086_state* state = fe->demodulator_priv; + int i; + u8 oldval; + + dprintk ("%s\n", __FUNCTION__); + + if (cmd->msg_len > 6) + return -EINVAL; + oldval = tda10086_read_byte(state, 0x36); + + for(i=0; i< cmd->msg_len; i++) { + tda10086_write_byte(state, 0x48+i, cmd->msg[i]); + } + tda10086_write_byte(state, 0x36, 0x08 | ((cmd->msg_len + 1) << 4)); + + tda10086_diseqc_wait(state); + + tda10086_write_byte(state, 0x36, oldval); + + return 0; +} + +static int tda10086_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +{ + struct tda10086_state* state = fe->demodulator_priv; + u8 oldval = tda10086_read_byte(state, 0x36); + + dprintk ("%s\n", __FUNCTION__); + + switch(minicmd) { + case SEC_MINI_A: + tda10086_write_byte(state, 0x36, 0x04); + break; + + case SEC_MINI_B: + tda10086_write_byte(state, 0x36, 0x06); + break; + } + + tda10086_diseqc_wait(state); + + tda10086_write_byte(state, 0x36, oldval); + + return 0; +} + +static int tda10086_set_inversion(struct tda10086_state *state, + struct dvb_frontend_parameters *fe_params) +{ + u8 invval = 0x80; + + dprintk ("%s %i %i\n", __FUNCTION__, fe_params->inversion, state->config->invert); + + switch(fe_params->inversion) { + case INVERSION_OFF: + if (state->config->invert) + invval = 0x40; + break; + case INVERSION_ON: + if (!state->config->invert) + invval = 0x40; + break; + case INVERSION_AUTO: + invval = 0x00; + break; + } + tda10086_write_mask(state, 0x0c, 0xc0, invval); + + return 0; +} + +static int tda10086_set_symbol_rate(struct tda10086_state *state, + struct dvb_frontend_parameters *fe_params) +{ + u8 dfn = 0; + u8 afs = 0; + u8 byp = 0; + u8 reg37 = 0x43; + u8 reg42 = 0x43; + u64 big; + u32 tmp; + u32 bdr; + u32 bdri; + u32 symbol_rate = fe_params->u.qpsk.symbol_rate; + + dprintk ("%s %i\n", __FUNCTION__, symbol_rate); + + // setup the decimation and anti-aliasing filters.. + if (symbol_rate < (u32) (SACLK * 0.0137)) { + dfn=4; + afs=1; + } else if (symbol_rate < (u32) (SACLK * 0.0208)) { + dfn=4; + afs=0; + } else if (symbol_rate < (u32) (SACLK * 0.0270)) { + dfn=3; + afs=1; + } else if (symbol_rate < (u32) (SACLK * 0.0416)) { + dfn=3; + afs=0; + } else if (symbol_rate < (u32) (SACLK * 0.0550)) { + dfn=2; + afs=1; + } else if (symbol_rate < (u32) (SACLK * 0.0833)) { + dfn=2; + afs=0; + } else if (symbol_rate < (u32) (SACLK * 0.1100)) { + dfn=1; + afs=1; + } else if (symbol_rate < (u32) (SACLK * 0.1666)) { + dfn=1; + afs=0; + } else if (symbol_rate < (u32) (SACLK * 0.2200)) { + dfn=0; + afs=1; + } else if (symbol_rate < (u32) (SACLK * 0.3333)) { + dfn=0; + afs=0; + } else { + reg37 = 0x63; + reg42 = 0x4f; + byp=1; + } + + // calculate BDR + big = (1ULL<<21) * ((u64) symbol_rate/1000ULL) * (1ULL<<dfn); + big += ((SACLK/1000ULL)-1ULL); + do_div(big, (SACLK/1000ULL)); + bdr = big & 0xfffff; + + // calculate BDRI + tmp = (1<<dfn)*(symbol_rate/1000); + bdri = ((32 * (SACLK/1000)) + (tmp-1)) / tmp; + + tda10086_write_byte(state, 0x21, (afs << 7) | dfn); + tda10086_write_mask(state, 0x20, 0x08, byp << 3); + tda10086_write_byte(state, 0x06, bdr); + tda10086_write_byte(state, 0x07, bdr >> 8); + tda10086_write_byte(state, 0x08, bdr >> 16); + tda10086_write_byte(state, 0x09, bdri); + tda10086_write_byte(state, 0x37, reg37); + tda10086_write_byte(state, 0x42, reg42); + + return 0; +} + +static int tda10086_set_fec(struct tda10086_state *state, + struct dvb_frontend_parameters *fe_params) +{ + u8 fecval; + + dprintk ("%s %i\n", __FUNCTION__, fe_params->u.qpsk.fec_inner); + + switch(fe_params->u.qpsk.fec_inner) { + case FEC_1_2: + fecval = 0x00; + break; + case FEC_2_3: + fecval = 0x01; + break; + case FEC_3_4: + fecval = 0x02; + break; + case FEC_4_5: + fecval = 0x03; + break; + case FEC_5_6: + fecval = 0x04; + break; + case FEC_6_7: + fecval = 0x05; + break; + case FEC_7_8: + fecval = 0x06; + break; + case FEC_8_9: + fecval = 0x07; + break; + case FEC_AUTO: + fecval = 0x08; + break; + default: + return -1; + } + tda10086_write_byte(state, 0x0d, fecval); + + return 0; +} + +static int tda10086_set_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fe_params) +{ + struct tda10086_state *state = fe->demodulator_priv; + int ret; + u32 freq = 0; + int freqoff; + + dprintk ("%s\n", __FUNCTION__); + + // set params + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe, fe_params); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (fe->ops.tuner_ops.get_frequency) + fe->ops.tuner_ops.get_frequency(fe, &freq); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + // calcluate the frequency offset (in *Hz* not kHz) + freqoff = fe_params->frequency - freq; + freqoff = ((1<<16) * freqoff) / (SACLK/1000); + tda10086_write_byte(state, 0x3d, 0x80 | ((freqoff >> 8) & 0x7f)); + tda10086_write_byte(state, 0x3e, freqoff); + + if ((ret = tda10086_set_inversion(state, fe_params)) < 0) + return ret; + if ((ret = tda10086_set_symbol_rate(state, fe_params)) < 0) + return ret; + if ((ret = tda10086_set_fec(state, fe_params)) < 0) + return ret; + + // soft reset + disable TS output until lock + tda10086_write_mask(state, 0x10, 0x40, 0x40); + tda10086_write_mask(state, 0x00, 0x01, 0x00); + + state->symbol_rate = fe_params->u.qpsk.symbol_rate; + state->frequency = fe_params->frequency; + return 0; +} + +static int tda10086_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *fe_params) +{ + struct tda10086_state* state = fe->demodulator_priv; + u8 val; + int tmp; + u64 tmp64; + + dprintk ("%s\n", __FUNCTION__); + + // calculate the updated frequency (note: we convert from Hz->kHz) + tmp64 = tda10086_read_byte(state, 0x52); + tmp64 |= (tda10086_read_byte(state, 0x51) << 8); + if (tmp64 & 0x8000) + tmp64 |= 0xffffffffffff0000ULL; + tmp64 = (tmp64 * (SACLK/1000ULL)); + do_div(tmp64, (1ULL<<15) * (1ULL<<1)); + fe_params->frequency = (int) state->frequency + (int) tmp64; + + // the inversion + val = tda10086_read_byte(state, 0x0c); + if (val & 0x80) { + switch(val & 0x40) { + case 0x00: + fe_params->inversion = INVERSION_OFF; + if (state->config->invert) + fe_params->inversion = INVERSION_ON; + break; + default: + fe_params->inversion = INVERSION_ON; + if (state->config->invert) + fe_params->inversion = INVERSION_OFF; + break; + } + } else { + tda10086_read_byte(state, 0x0f); + switch(val & 0x02) { + case 0x00: + fe_params->inversion = INVERSION_OFF; + if (state->config->invert) + fe_params->inversion = INVERSION_ON; + break; + default: + fe_params->inversion = INVERSION_ON; + if (state->config->invert) + fe_params->inversion = INVERSION_OFF; + break; + } + } + + // calculate the updated symbol rate + tmp = tda10086_read_byte(state, 0x1d); + if (tmp & 0x80) + tmp |= 0xffffff00; + tmp = (tmp * 480 * (1<<1)) / 128; + tmp = ((state->symbol_rate/1000) * tmp) / (1000000/1000); + fe_params->u.qpsk.symbol_rate = state->symbol_rate + tmp; + + // the FEC + val = (tda10086_read_byte(state, 0x0d) & 0x70) >> 4; + switch(val) { + case 0x00: + fe_params->u.qpsk.fec_inner = FEC_1_2; + break; + case 0x01: + fe_params->u.qpsk.fec_inner = FEC_2_3; + break; + case 0x02: + fe_params->u.qpsk.fec_inner = FEC_3_4; + break; + case 0x03: + fe_params->u.qpsk.fec_inner = FEC_4_5; + break; + case 0x04: + fe_params->u.qpsk.fec_inner = FEC_5_6; + break; + case 0x05: + fe_params->u.qpsk.fec_inner = FEC_6_7; + break; + case 0x06: + fe_params->u.qpsk.fec_inner = FEC_7_8; + break; + case 0x07: + fe_params->u.qpsk.fec_inner = FEC_8_9; + break; + } + + return 0; +} + +static int tda10086_read_status(struct dvb_frontend* fe, fe_status_t *fe_status) +{ + struct tda10086_state* state = fe->demodulator_priv; + u8 val; + + dprintk ("%s\n", __FUNCTION__); + + val = tda10086_read_byte(state, 0x0e); + *fe_status = 0; + if (val & 0x01) + *fe_status |= FE_HAS_SIGNAL; + if (val & 0x02) + *fe_status |= FE_HAS_CARRIER; + if (val & 0x04) + *fe_status |= FE_HAS_VITERBI; + if (val & 0x08) + *fe_status |= FE_HAS_SYNC; + if (val & 0x10) + *fe_status |= FE_HAS_LOCK; + + return 0; +} + +static int tda10086_read_signal_strength(struct dvb_frontend* fe, u16 * signal) +{ + struct tda10086_state* state = fe->demodulator_priv; + u8 _str; + + dprintk ("%s\n", __FUNCTION__); + + _str = tda10086_read_byte(state, 0x43); + *signal = (_str << 8) | _str; + + return 0; +} + +static int tda10086_read_snr(struct dvb_frontend* fe, u16 * snr) +{ + struct tda10086_state* state = fe->demodulator_priv; + u8 _snr; + + dprintk ("%s\n", __FUNCTION__); + + _snr = tda10086_read_byte(state, 0x1c); + *snr = (_snr << 8) | _snr; + + return 0; +} + +static int tda10086_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda10086_state* state = fe->demodulator_priv; + + dprintk ("%s\n", __FUNCTION__); + + // read it + *ucblocks = tda10086_read_byte(state, 0x18) & 0x7f; + + // reset counter + tda10086_write_byte(state, 0x18, 0x00); + tda10086_write_byte(state, 0x18, 0x80); + + return 0; +} + +static int tda10086_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda10086_state* state = fe->demodulator_priv; + + dprintk ("%s\n", __FUNCTION__); + + // read it + *ber = 0; + *ber |= tda10086_read_byte(state, 0x15); + *ber |= tda10086_read_byte(state, 0x16) << 8; + *ber |= (tda10086_read_byte(state, 0x17) & 0xf) << 16; + + return 0; +} + +static int tda10086_sleep(struct dvb_frontend* fe) +{ + struct tda10086_state* state = fe->demodulator_priv; + + dprintk ("%s\n", __FUNCTION__); + + tda10086_write_mask(state, 0x00, 0x08, 0x08); + + return 0; +} + +static int tda10086_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct tda10086_state* state = fe->demodulator_priv; + + dprintk ("%s\n", __FUNCTION__); + + if (enable) { + tda10086_write_mask(state, 0x00, 0x10, 0x10); + } else { + tda10086_write_mask(state, 0x00, 0x10, 0x00); + } + + return 0; +} + +static int tda10086_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + if (fesettings->parameters.u.qpsk.symbol_rate > 20000000) { + fesettings->min_delay_ms = 50; + fesettings->step_size = 2000; + fesettings->max_drift = 8000; + } else if (fesettings->parameters.u.qpsk.symbol_rate > 12000000) { + fesettings->min_delay_ms = 100; + fesettings->step_size = 1500; + fesettings->max_drift = 9000; + } else if (fesettings->parameters.u.qpsk.symbol_rate > 8000000) { + fesettings->min_delay_ms = 100; + fesettings->step_size = 1000; + fesettings->max_drift = 8000; + } else if (fesettings->parameters.u.qpsk.symbol_rate > 4000000) { + fesettings->min_delay_ms = 100; + fesettings->step_size = 500; + fesettings->max_drift = 7000; + } else if (fesettings->parameters.u.qpsk.symbol_rate > 2000000) { + fesettings->min_delay_ms = 200; + fesettings->step_size = (fesettings->parameters.u.qpsk.symbol_rate / 8000); + fesettings->max_drift = 14 * fesettings->step_size; + } else { + fesettings->min_delay_ms = 200; + fesettings->step_size = (fesettings->parameters.u.qpsk.symbol_rate / 8000); + fesettings->max_drift = 18 * fesettings->step_size; + } + + return 0; +} + +static void tda10086_release(struct dvb_frontend* fe) +{ + struct tda10086_state *state = fe->demodulator_priv; + tda10086_sleep(fe); + kfree(state); +} + +static struct dvb_frontend_ops tda10086_ops = { + + .info = { + .name = "Philips TDA10086 DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 125, /* kHz for QPSK frontends */ + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK + }, + + .release = tda10086_release, + + .init = tda10086_init, + .sleep = tda10086_sleep, + .i2c_gate_ctrl = tda10086_i2c_gate_ctrl, + + .set_frontend = tda10086_set_frontend, + .get_frontend = tda10086_get_frontend, + .get_tune_settings = tda10086_get_tune_settings, + + .read_status = tda10086_read_status, + .read_ber = tda10086_read_ber, + .read_signal_strength = tda10086_read_signal_strength, + .read_snr = tda10086_read_snr, + .read_ucblocks = tda10086_read_ucblocks, + + .diseqc_send_master_cmd = tda10086_send_master_cmd, + .diseqc_send_burst = tda10086_send_burst, + .set_tone = tda10086_set_tone, +}; + +struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, + struct i2c_adapter* i2c) +{ + struct tda10086_state *state; + + dprintk ("%s\n", __FUNCTION__); + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct tda10086_state), GFP_KERNEL); + if (!state) + return NULL; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + /* check if the demod is there */ + if (tda10086_read_byte(state, 0x1e) != 0xe1) { + kfree(state); + return NULL; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &tda10086_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; +} + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Philips TDA10086 DVB-S Demodulator"); +MODULE_AUTHOR("Andrew de Quincey"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(tda10086_attach); diff --git a/drivers/media/dvb/frontends/tda10086.h b/drivers/media/dvb/frontends/tda10086.h new file mode 100644 index 00000000000..e8061db1112 --- /dev/null +++ b/drivers/media/dvb/frontends/tda10086.h @@ -0,0 +1,41 @@ + /* + Driver for Philips tda10086 DVBS Frontend + + (c) 2006 Andrew de Quincey + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#ifndef TDA10086_H +#define TDA10086_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +struct tda10086_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* does the "inversion" need inverted? */ + u8 invert; +}; + +extern struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, + struct i2c_adapter* i2c); + +#endif // TDA10086_H diff --git a/drivers/media/dvb/frontends/tda8083.h b/drivers/media/dvb/frontends/tda8083.h index e7a48f61ea2..aae15bdce6e 100644 --- a/drivers/media/dvb/frontends/tda8083.h +++ b/drivers/media/dvb/frontends/tda8083.h @@ -35,7 +35,16 @@ struct tda8083_config u8 demod_address; }; +#if defined(CONFIG_DVB_TDA8083) || defined(CONFIG_DVB_TDA8083_MODULE) extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_TDA8083 #endif // TDA8083_H diff --git a/drivers/media/dvb/frontends/tda826x.c b/drivers/media/dvb/frontends/tda826x.c new file mode 100644 index 00000000000..eeab26bd36e --- /dev/null +++ b/drivers/media/dvb/frontends/tda826x.c @@ -0,0 +1,173 @@ + /* + Driver for Philips tda8262/tda8263 DVBS Silicon tuners + + (c) 2006 Andrew de Quincey + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#include <linux/module.h> +#include <linux/dvb/frontend.h> +#include <asm/types.h> + +#include "tda826x.h" + +static int debug = 0; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "tda826x: " args); \ + } while (0) + +struct tda826x_priv { + /* i2c details */ + int i2c_address; + struct i2c_adapter *i2c; + u8 has_loopthrough:1; + u32 frequency; +}; + +static int tda826x_release(struct dvb_frontend *fe) +{ + if (fe->tuner_priv) + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int tda826x_sleep(struct dvb_frontend *fe) +{ + struct tda826x_priv *priv = fe->tuner_priv; + int ret; + u8 buf [] = { 0x00, 0x8d }; + struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 2 }; + + dprintk("%s:\n", __FUNCTION__); + + if (!priv->has_loopthrough) + buf[1] = 0xad; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { + dprintk("%s: i2c error\n", __FUNCTION__); + } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return (ret == 1) ? 0 : ret; +} + +static int tda826x_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct tda826x_priv *priv = fe->tuner_priv; + int ret; + u32 div; + u8 buf [11]; + struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 11 }; + + dprintk("%s:\n", __FUNCTION__); + + div = (params->frequency + (1000-1)) / 1000; + + buf[0] = 0x00; // subaddress + buf[1] = 0x09; // powerdown RSSI + the magic value 1 + if (!priv->has_loopthrough) + buf[1] |= 0x20; // power down loopthrough if not needed + buf[2] = (1<<5) | 0x0b; // 1Mhz + 0.45 VCO + buf[3] = div >> 7; + buf[4] = div << 1; + buf[5] = 0xff; // basedband filter to max + buf[6] = 0xfe; // gains at max + no RF attenuation + buf[7] = 0x83; // charge pumps at high, tests off + buf[8] = 0x80; // recommended value 4 for AMPVCO + disable ports. + buf[9] = 0x1a; // normal caltime + recommended values for SELTH + SELVTL + buf[10] = 0xd4; // recommended value 13 for BBIAS + unknown bit set on + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { + dprintk("%s: i2c error\n", __FUNCTION__); + } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + priv->frequency = div * 1000; + + return (ret == 1) ? 0 : ret; +} + +static int tda826x_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tda826x_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static struct dvb_tuner_ops tda826x_tuner_ops = { + .info = { + .name = "Philips TDA826X", + .frequency_min = 950000, + .frequency_min = 2175000 + }, + .release = tda826x_release, + .sleep = tda826x_sleep, + .set_params = tda826x_set_params, + .get_frequency = tda826x_get_frequency, +}; + +struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, int has_loopthrough) +{ + struct tda826x_priv *priv = NULL; + u8 b1 [] = { 0, 0 }; + struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 }; + int ret; + + dprintk("%s:\n", __FUNCTION__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + ret = i2c_transfer (i2c, &msg, 1); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 1) + return NULL; + if (!(b1[1] & 0x80)) + return NULL; + + priv = kzalloc(sizeof(struct tda826x_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c_address = addr; + priv->i2c = i2c; + priv->has_loopthrough = has_loopthrough; + + memcpy(&fe->ops.tuner_ops, &tda826x_tuner_ops, sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + + return fe; +} +EXPORT_SYMBOL(tda826x_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("DVB TDA826x driver"); +MODULE_AUTHOR("Andrew de Quincey"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/tda826x.h b/drivers/media/dvb/frontends/tda826x.h new file mode 100644 index 00000000000..3307607632b --- /dev/null +++ b/drivers/media/dvb/frontends/tda826x.h @@ -0,0 +1,40 @@ + /* + Driver for Philips tda8262/tda8263 DVBS Silicon tuners + + (c) 2006 Andrew de Quincey + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#ifndef __DVB_TDA826X_H__ +#define __DVB_TDA826X_H__ + +#include <linux/i2c.h> +#include "dvb_frontend.h" + +/** + * Attach a tda826x tuner to the supplied frontend structure. + * + * @param fe Frontend to attach to. + * @param addr i2c address of the tuner. + * @param i2c i2c adapter to use. + * @param has_loopthrough Set to 1 if the card has a loopthrough RF connector. + * @return FE pointer on success, NULL on failure. + */ +extern struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, int has_loopthrough); + +#endif diff --git a/drivers/media/dvb/frontends/tua6100.c b/drivers/media/dvb/frontends/tua6100.c new file mode 100644 index 00000000000..88554393a9b --- /dev/null +++ b/drivers/media/dvb/frontends/tua6100.c @@ -0,0 +1,205 @@ +/** + * Driver for Infineon tua6100 pll. + * + * (c) 2006 Andrew de Quincey + * + * Based on code found in budget-av.c, which has the following: + * Compiled from various sources by Michael Hunold <michael@mihu.de> + * + * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> & + * Andrew de Quincey <adq_dvb@lidskialf.net> + * + * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de> + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/dvb/frontend.h> +#include <asm/types.h> + +#include "tua6100.h" + +struct tua6100_priv { + /* i2c details */ + int i2c_address; + struct i2c_adapter *i2c; + u32 frequency; +}; + +static int tua6100_release(struct dvb_frontend *fe) +{ + if (fe->tuner_priv) + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int tua6100_sleep(struct dvb_frontend *fe) +{ + struct tua6100_priv *priv = fe->tuner_priv; + int ret; + u8 reg0[] = { 0x00, 0x00 }; + struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { + printk("%s: i2c error\n", __FUNCTION__); + } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return (ret == 1) ? 0 : ret; +} + +static int tua6100_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct tua6100_priv *priv = fe->tuner_priv; + u32 div; + u32 prediv; + u8 reg0[] = { 0x00, 0x00 }; + u8 reg1[] = { 0x01, 0x00, 0x00, 0x00 }; + u8 reg2[] = { 0x02, 0x00, 0x00 }; + struct i2c_msg msg0 = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 }; + struct i2c_msg msg1 = { .addr = priv->i2c_address, .flags = 0, .buf = reg1, .len = 4 }; + struct i2c_msg msg2 = { .addr = priv->i2c_address, .flags = 0, .buf = reg2, .len = 3 }; + +#define _R 4 +#define _P 32 +#define _ri 4000000 + + // setup register 0 + if (params->frequency < 2000000) { + reg0[1] = 0x03; + } else { + reg0[1] = 0x07; + } + + // setup register 1 + if (params->frequency < 1630000) { + reg1[1] = 0x2c; + } else { + reg1[1] = 0x0c; + } + if (_P == 64) + reg1[1] |= 0x40; + if (params->frequency >= 1525000) + reg1[1] |= 0x80; + + // register 2 + reg2[1] = (_R >> 8) & 0x03; + reg2[2] = _R; + if (params->frequency < 1455000) { + reg2[1] |= 0x1c; + } else if (params->frequency < 1630000) { + reg2[1] |= 0x0c; + } else { + reg2[1] |= 0x1c; + } + + // The N divisor ratio (note: params->frequency is in kHz, but we need it in Hz) + prediv = (params->frequency * _R) / (_ri / 1000); + div = prediv / _P; + reg1[1] |= (div >> 9) & 0x03; + reg1[2] = div >> 1; + reg1[3] = (div << 7); + priv->frequency = ((div * _P) * (_ri / 1000)) / _R; + + // Finally, calculate and store the value for A + reg1[3] |= (prediv - (div*_P)) & 0x7f; + +#undef _R +#undef _P +#undef _ri + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(priv->i2c, &msg0, 1) != 1) + return -EIO; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(priv->i2c, &msg2, 1) != 1) + return -EIO; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(priv->i2c, &msg1, 1) != 1) + return -EIO; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int tua6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tua6100_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static struct dvb_tuner_ops tua6100_tuner_ops = { + .info = { + .name = "Infineon TUA6100", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_step = 1000, + }, + .release = tua6100_release, + .sleep = tua6100_sleep, + .set_params = tua6100_set_params, + .get_frequency = tua6100_get_frequency, +}; + +struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c) +{ + struct tua6100_priv *priv = NULL; + u8 b1 [] = { 0x80 }; + u8 b2 [] = { 0x00 }; + struct i2c_msg msg [] = { { .addr = addr, .flags = 0, .buf = b1, .len = 1 }, + { .addr = addr, .flags = I2C_M_RD, .buf = b2, .len = 1 } }; + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + ret = i2c_transfer (i2c, msg, 2); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 2) + return NULL; + + priv = kzalloc(sizeof(struct tua6100_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c_address = addr; + priv->i2c = i2c; + + memcpy(&fe->ops.tuner_ops, &tua6100_tuner_ops, sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + return fe; +} +EXPORT_SYMBOL(tua6100_attach); + +MODULE_DESCRIPTION("DVB tua6100 driver"); +MODULE_AUTHOR("Andrew de Quincey"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/tua6100.h b/drivers/media/dvb/frontends/tua6100.h new file mode 100644 index 00000000000..8f98033ffa7 --- /dev/null +++ b/drivers/media/dvb/frontends/tua6100.h @@ -0,0 +1,47 @@ +/** + * Driver for Infineon tua6100 PLL. + * + * (c) 2006 Andrew de Quincey + * + * Based on code found in budget-av.c, which has the following: + * Compiled from various sources by Michael Hunold <michael@mihu.de> + * + * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> & + * Andrew de Quincey <adq_dvb@lidskialf.net> + * + * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de> + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __DVB_TUA6100_H__ +#define __DVB_TUA6100_H__ + +#include <linux/i2c.h> +#include "dvb_frontend.h" + +#if defined(CONFIG_DVB_TUA6100) || defined(CONFIG_DVB_TUA6100_MODULE) +extern struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend* tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_TUA6100 + +#endif diff --git a/drivers/media/dvb/frontends/ves1820.h b/drivers/media/dvb/frontends/ves1820.h index 520f09522fb..f0c9dded39d 100644 --- a/drivers/media/dvb/frontends/ves1820.h +++ b/drivers/media/dvb/frontends/ves1820.h @@ -41,7 +41,16 @@ struct ves1820_config u8 selagc:1; }; +#if defined(CONFIG_DVB_VES1820) || defined(CONFIG_DVB_VES1820_MODULE) extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, struct i2c_adapter* i2c, u8 pwm); +#else +static inline struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, + struct i2c_adapter* i2c, u8 pwm) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_VES1820 #endif // VES1820_H diff --git a/drivers/media/dvb/frontends/ves1x93.h b/drivers/media/dvb/frontends/ves1x93.h index ba88ae0855c..395fed39b28 100644 --- a/drivers/media/dvb/frontends/ves1x93.h +++ b/drivers/media/dvb/frontends/ves1x93.h @@ -40,7 +40,16 @@ struct ves1x93_config u8 invert_pwm:1; }; +#if defined(CONFIG_DVB_VES1X93) || defined(CONFIG_DVB_VES1X93_MODULE) extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_VES1X93 #endif // VES1X93_H diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c index 2b95e8b6cd3..0e9b59af271 100644 --- a/drivers/media/dvb/frontends/zl10353.c +++ b/drivers/media/dvb/frontends/zl10353.c @@ -140,6 +140,8 @@ static int zl10353_set_parameters(struct dvb_frontend *fe, zl10353_single_write(fe, 0x5E, 0x00); zl10353_single_write(fe, 0x65, 0x5A); zl10353_single_write(fe, 0x66, 0xE9); + zl10353_single_write(fe, 0x6C, 0xCD); + zl10353_single_write(fe, 0x6D, 0x7E); zl10353_single_write(fe, 0x62, 0x0A); // if there is no attached secondary tuner, we call set_params to program @@ -168,6 +170,7 @@ static int zl10353_set_parameters(struct dvb_frontend *fe, // even if there isn't a PLL attached to the secondary bus zl10353_write(fe, pllbuf, sizeof(pllbuf)); + zl10353_single_write(fe, 0x5F, 0x13); zl10353_single_write(fe, 0x70, 0x01); udelay(250); zl10353_single_write(fe, 0xE4, 0x00); @@ -243,9 +246,12 @@ static int zl10353_init(struct dvb_frontend *fe) if (debug_regs) zl10353_dump_regs(fe); + if (state->config.parallel_ts) + zl10353_reset_attach[2] &= ~0x20; /* Do a "hard" reset if not already done */ - if (zl10353_read_register(state, 0x50) != 0x03) { + if (zl10353_read_register(state, 0x50) != zl10353_reset_attach[1] || + zl10353_read_register(state, 0x51) != zl10353_reset_attach[2]) { rc = zl10353_write(fe, zl10353_reset_attach, sizeof(zl10353_reset_attach)); if (debug_regs) @@ -258,7 +264,6 @@ static int zl10353_init(struct dvb_frontend *fe) static void zl10353_release(struct dvb_frontend *fe) { struct zl10353_state *state = fe->demodulator_priv; - kfree(state); } @@ -314,6 +319,7 @@ static struct dvb_frontend_ops zl10353_ops = { .init = zl10353_init, .sleep = zl10353_sleep, + .write = zl10353_write, .set_frontend = zl10353_set_parameters, .get_tune_settings = zl10353_get_tune_settings, @@ -330,4 +336,3 @@ MODULE_AUTHOR("Chris Pascoe"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(zl10353_attach); -EXPORT_SYMBOL(zl10353_write); diff --git a/drivers/media/dvb/frontends/zl10353.h b/drivers/media/dvb/frontends/zl10353.h index 9770cb840cf..79a947215c4 100644 --- a/drivers/media/dvb/frontends/zl10353.h +++ b/drivers/media/dvb/frontends/zl10353.h @@ -31,11 +31,21 @@ struct zl10353_config /* set if no pll is connected to the secondary i2c bus */ int no_tuner; + + /* set if parallel ts output is required */ + int parallel_ts; }; +#if defined(CONFIG_DVB_ZL10353) || defined(CONFIG_DVB_ZL10353_MODULE) extern struct dvb_frontend* zl10353_attach(const struct zl10353_config *config, struct i2c_adapter *i2c); - -extern int zl10353_write(struct dvb_frontend *fe, u8 *ibuf, int ilen); +#else +static inline struct dvb_frontend* zl10353_attach(const struct zl10353_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_ZL10353 #endif /* ZL10353_H */ diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index 5fb097595cf..95531a62499 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -1,17 +1,17 @@ config DVB_AV7110 tristate "AV7110 cards" depends on DVB_CORE && PCI && I2C && VIDEO_V4L1 - select FW_LOADER + select FW_LOADER if !DVB_AV7110_FIRMWARE select VIDEO_SAA7146_VV select DVB_PLL - select DVB_VES1820 - select DVB_VES1X93 - select DVB_STV0299 - select DVB_TDA8083 - select DVB_SP8870 - select DVB_STV0297 - select DVB_L64781 - select DVB_LNBP21 + select DVB_VES1820 if !DVB_FE_CUSTOMISE + select DVB_VES1X93 if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_TDA8083 if !DVB_FE_CUSTOMISE + select DVB_SP8870 if !DVB_FE_CUSTOMISE + select DVB_STV0297 if !DVB_FE_CUSTOMISE + select DVB_L64781 if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE help Support for SAA7146 and AV7110 based DVB cards as produced by Fujitsu-Siemens, Technotrend, Hauppauge and others. @@ -63,14 +63,16 @@ config DVB_BUDGET depends on DVB_CORE && PCI && I2C && VIDEO_V4L1 select VIDEO_SAA7146 select DVB_PLL - select DVB_STV0299 - select DVB_VES1X93 - select DVB_VES1820 - select DVB_L64781 - select DVB_TDA8083 - select DVB_TDA10021 - select DVB_S5H1420 - select DVB_LNBP21 + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_VES1X93 if !DVB_FE_CUSTOMISE + select DVB_VES1820 if !DVB_FE_CUSTOMISE + select DVB_L64781 if !DVB_FE_CUSTOMISE + select DVB_TDA8083 if !DVB_FE_CUSTOMISE + select DVB_TDA10021 if !DVB_FE_CUSTOMISE + select DVB_S5H1420 if !DVB_FE_CUSTOMISE + select DVB_TDA10086 if !DVB_FE_CUSTOMISE + select DVB_TDA826X if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard @@ -86,10 +88,10 @@ config DVB_BUDGET_CI depends on DVB_CORE && PCI && I2C && VIDEO_V4L1 select VIDEO_SAA7146 select DVB_PLL - select DVB_STV0297 - select DVB_STV0299 - select DVB_TDA1004X - select DVB_LNBP21 + select DVB_STV0297 if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_TDA1004X if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard @@ -108,9 +110,10 @@ config DVB_BUDGET_AV depends on DVB_CORE && PCI && I2C && VIDEO_V4L1 select VIDEO_SAA7146_VV select DVB_PLL - select DVB_STV0299 - select DVB_TDA1004X - select DVB_TDA10021 + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_TDA1004X if !DVB_FE_CUSTOMISE + select DVB_TDA10021 if !DVB_FE_CUSTOMISE + select DVB_TUA6100 if !DVB_FE_CUSTOMISE select FW_LOADER help Support for simple SAA7146 based DVB cards @@ -127,9 +130,9 @@ config DVB_BUDGET_PATCH depends on DVB_CORE && DVB_BUDGET && VIDEO_V4L1 select DVB_AV7110 select DVB_PLL - select DVB_STV0299 - select DVB_VES1X93 - select DVB_TDA8083 + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_VES1X93 if !DVB_FE_CUSTOMISE + select DVB_TDA8083 if !DVB_FE_CUSTOMISE help Support for Budget Patch (full TS) modification on SAA7146+AV7110 based cards (DVB-S cards). This diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c index 4506165c5de..bba23bcd1b1 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -1383,8 +1383,10 @@ static void dvb_unregister(struct av7110 *av7110) dvb_dmxdev_release(&av7110->dmxdev); dvb_dmx_release(&av7110->demux); - if (av7110->fe != NULL) + if (av7110->fe != NULL) { dvb_unregister_frontend(av7110->fe); + dvb_frontend_detach(av7110->fe); + } dvb_unregister_device(av7110->osd_dev); av7110_av_unregister(av7110); av7110_ca_unregister(av7110); @@ -1699,9 +1701,13 @@ static int alps_tdlb7_tuner_set_params(struct dvb_frontend* fe, struct dvb_front static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) { +#if defined(CONFIG_DVB_SP8870) || defined(CONFIG_DVB_SP8870_MODULE) struct av7110* av7110 = (struct av7110*) fe->dvb->priv; return request_firmware(fw, name, &av7110->dev->pci->dev); +#else + return -EINVAL; +#endif } static struct sp8870_config alps_tdlb7_config = { @@ -2077,7 +2083,7 @@ static int frontend_init(struct av7110 *av7110) if (av7110->dev->pci->subsystem_vendor == 0x110a) { switch(av7110->dev->pci->subsystem_device) { case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) - av7110->fe = ves1820_attach(&philips_cd1516_config, + av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap, read_pwm(av7110)); if (av7110->fe) { av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; @@ -2092,7 +2098,7 @@ static int frontend_init(struct av7110 *av7110) case 0x1002: // Hauppauge/TT WinTV DVB-S rev1.3SE // try the ALPS BSRV2 first of all - av7110->fe = ves1x93_attach(&alps_bsrv2_config, &av7110->i2c_adap); + av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); if (av7110->fe) { av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; @@ -2103,7 +2109,7 @@ static int frontend_init(struct av7110 *av7110) } // try the ALPS BSRU6 now - av7110->fe = stv0299_attach(&alps_bsru6_config, &av7110->i2c_adap); + av7110->fe = dvb_attach(stv0299_attach, &alps_bsru6_config, &av7110->i2c_adap); if (av7110->fe) { av7110->fe->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; av7110->fe->tuner_priv = &av7110->i2c_adap; @@ -2116,7 +2122,7 @@ static int frontend_init(struct av7110 *av7110) } // Try the grundig 29504-451 - av7110->fe = tda8083_attach(&grundig_29504_451_config, &av7110->i2c_adap); + av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); if (av7110->fe) { av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; @@ -2130,7 +2136,7 @@ static int frontend_init(struct av7110 *av7110) switch(av7110->dev->pci->subsystem_device) { case 0x0000: /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ - av7110->fe = ves1820_attach(&philips_cd1516_config, &av7110->i2c_adap, + av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap, read_pwm(av7110)); if (av7110->fe) { av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; @@ -2138,7 +2144,7 @@ static int frontend_init(struct av7110 *av7110) break; case 0x0003: /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */ - av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, + av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); if (av7110->fe) { av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; @@ -2148,17 +2154,24 @@ static int frontend_init(struct av7110 *av7110) break; case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X - - // ALPS TDLB7 - av7110->fe = sp8870_attach(&alps_tdlb7_config, &av7110->i2c_adap); + // try ALPS TDLB7 first, then Grundig 29504-401 + av7110->fe = dvb_attach(sp8870_attach, &alps_tdlb7_config, &av7110->i2c_adap); if (av7110->fe) { av7110->fe->ops.tuner_ops.set_params = alps_tdlb7_tuner_set_params; + break; } + /* fall-thru */ + + case 0x0008: // Hauppauge/TT DVB-T + // Grundig 29504-401 + av7110->fe = dvb_attach(l64781_attach, &grundig_29504_401_config, &av7110->i2c_adap); + if (av7110->fe) + av7110->fe->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; break; case 0x0002: // Hauppauge/TT DVB-C premium rev2.X - av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); + av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); if (av7110->fe) { av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; } @@ -2166,7 +2179,7 @@ static int frontend_init(struct av7110 *av7110) case 0x0004: // Galaxis DVB-S rev1.3 /* ALPS BSRV2 */ - av7110->fe = ves1x93_attach(&alps_bsrv2_config, &av7110->i2c_adap); + av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); if (av7110->fe) { av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; @@ -2178,7 +2191,7 @@ static int frontend_init(struct av7110 *av7110) case 0x0006: /* Fujitsu-Siemens DVB-S rev 1.6 */ /* Grundig 29504-451 */ - av7110->fe = tda8083_attach(&grundig_29504_451_config, &av7110->i2c_adap); + av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); if (av7110->fe) { av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; @@ -2188,17 +2201,9 @@ static int frontend_init(struct av7110 *av7110) } break; - case 0x0008: // Hauppauge/TT DVB-T - - av7110->fe = l64781_attach(&grundig_29504_401_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; - } - break; - case 0x000A: // Hauppauge/TT Nexus-CA rev1.X - av7110->fe = stv0297_attach(&nexusca_stv0297_config, &av7110->i2c_adap); + av7110->fe = dvb_attach(stv0297_attach, &nexusca_stv0297_config, &av7110->i2c_adap); if (av7110->fe) { av7110->fe->ops.tuner_ops.set_params = nexusca_stv0297_tuner_set_params; @@ -2214,12 +2219,12 @@ static int frontend_init(struct av7110 *av7110) case 0x000E: /* Hauppauge/TT Nexus-S rev 2.3 */ /* ALPS BSBE1 */ - av7110->fe = stv0299_attach(&alps_bsbe1_config, &av7110->i2c_adap); + av7110->fe = dvb_attach(stv0299_attach, &alps_bsbe1_config, &av7110->i2c_adap); if (av7110->fe) { av7110->fe->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; av7110->fe->tuner_priv = &av7110->i2c_adap; - if (lnbp21_attach(av7110->fe, &av7110->i2c_adap, 0, 0)) { + if (dvb_attach(lnbp21_attach, av7110->fe, &av7110->i2c_adap, 0, 0) == NULL) { printk("dvb-ttpci: LNBP21 not found!\n"); if (av7110->fe->ops.release) av7110->fe->ops.release(av7110->fe); @@ -2255,8 +2260,7 @@ static int frontend_init(struct av7110 *av7110) ret = dvb_register_frontend(&av7110->dvb_adapter, av7110->fe); if (ret < 0) { printk("av7110: Frontend registration failed!\n"); - if (av7110->fe->ops.release) - av7110->fe->ops.release(av7110->fe); + dvb_frontend_detach(av7110->fe); av7110->fe = NULL; } } @@ -2823,7 +2827,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl); static struct saa7146_extension av7110_extension = { - .name = "dvb\0", + .name = "dvb", .flags = SAA7146_I2C_SHORT_DELAY, .module = THIS_MODULE, diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c index 0f3a044aeb1..8c577cf30fb 100644 --- a/drivers/media/dvb/ttpci/av7110_av.c +++ b/drivers/media/dvb/ttpci/av7110_av.c @@ -33,7 +33,6 @@ #include <linux/string.h> #include <linux/sched.h> #include <linux/delay.h> -#include <linux/byteorder/swabb.h> #include <linux/smp_lock.h> #include <linux/fs.h> diff --git a/drivers/media/dvb/ttpci/av7110_ca.c b/drivers/media/dvb/ttpci/av7110_ca.c index 6079e8865d5..dd9aee314e0 100644 --- a/drivers/media/dvb/ttpci/av7110_ca.c +++ b/drivers/media/dvb/ttpci/av7110_ca.c @@ -35,7 +35,6 @@ #include <linux/fs.h> #include <linux/timer.h> #include <linux/poll.h> -#include <linux/byteorder/swabb.h> #include <linux/smp_lock.h> #include "av7110.h" diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c index 75736f2fe83..37de2e88a27 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.c +++ b/drivers/media/dvb/ttpci/av7110_hw.c @@ -34,7 +34,6 @@ #include <linux/string.h> #include <linux/sched.h> #include <linux/delay.h> -#include <linux/byteorder/swabb.h> #include <linux/smp_lock.h> #include <linux/fs.h> diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c index 6ffe53fdcf5..10cfe3131e7 100644 --- a/drivers/media/dvb/ttpci/av7110_v4l.c +++ b/drivers/media/dvb/ttpci/av7110_v4l.c @@ -32,7 +32,6 @@ #include <linux/fs.h> #include <linux/timer.h> #include <linux/poll.h> -#include <linux/byteorder/swabb.h> #include <linux/smp_lock.h> #include "av7110.h" @@ -922,7 +921,7 @@ static struct saa7146_ext_vv av7110_vv_data_st = { static struct saa7146_ext_vv av7110_vv_data_c = { .inputs = 1, .audios = 1, - .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT, + .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT, .flags = SAA7146_USE_PORT_B_FOR_VBI, .stds = &standard[0], diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 2d21fec23b4..2235ff8b8a1 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -37,6 +37,7 @@ #include "stv0299.h" #include "tda10021.h" #include "tda1004x.h" +#include "tua6100.h" #include "dvb-pll.h" #include <media/saa7146_vv.h> #include <linux/module.h> @@ -235,7 +236,7 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) /* set tda10021 back to original clock configuration on reset */ if (budget_av->tda10021_poclkp) { - tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa0); + tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0); budget_av->tda10021_ts_enabled = 0; } @@ -257,7 +258,7 @@ static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) /* set tda10021 back to original clock configuration when cam removed */ if (budget_av->tda10021_poclkp) { - tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa0); + tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0); budget_av->tda10021_ts_enabled = 0; } return 0; @@ -277,7 +278,7 @@ static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) /* tda10021 seems to need a different TS clock config when data is routed to the CAM */ if (budget_av->tda10021_poclkp) { - tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa1); + tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa1); budget_av->tda10021_ts_enabled = 1; } @@ -548,144 +549,6 @@ static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe, return 0; } -#define MIN2(a,b) ((a) < (b) ? (a) : (b)) -#define MIN3(a,b,c) MIN2(MIN2(a,b),c) - -static int philips_su1278sh2_tua6100_tuner_set_params(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params) -{ - u8 reg0 [2] = { 0x00, 0x00 }; - u8 reg1 [4] = { 0x01, 0x00, 0x00, 0x00 }; - u8 reg2 [3] = { 0x02, 0x00, 0x00 }; - int _fband; - int first_ZF; - int R, A, N, P, M; - struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = NULL,.len = 0 }; - int freq = params->frequency; - struct budget *budget = (struct budget *) fe->dvb->priv; - - first_ZF = (freq) / 1000; - - if (abs(MIN2(abs(first_ZF-1190),abs(first_ZF-1790))) < - abs(MIN3(abs(first_ZF-1202),abs(first_ZF-1542),abs(first_ZF-1890)))) - _fband = 2; - else - _fband = 3; - - if (_fband == 2) { - if (((first_ZF >= 950) && (first_ZF < 1350)) || - ((first_ZF >= 1430) && (first_ZF < 1950))) - reg0[1] = 0x07; - else if (((first_ZF >= 1350) && (first_ZF < 1430)) || - ((first_ZF >= 1950) && (first_ZF < 2150))) - reg0[1] = 0x0B; - } - - if(_fband == 3) { - if (((first_ZF >= 950) && (first_ZF < 1350)) || - ((first_ZF >= 1455) && (first_ZF < 1950))) - reg0[1] = 0x07; - else if (((first_ZF >= 1350) && (first_ZF < 1420)) || - ((first_ZF >= 1950) && (first_ZF < 2150))) - reg0[1] = 0x0B; - else if ((first_ZF >= 1420) && (first_ZF < 1455)) - reg0[1] = 0x0F; - } - - if (first_ZF > 1525) - reg1[1] |= 0x80; - else - reg1[1] &= 0x7F; - - if (_fband == 2) { - if (first_ZF > 1430) { /* 1430MHZ */ - reg1[1] &= 0xCF; /* N2 */ - reg2[1] &= 0xCF; /* R2 */ - reg2[1] |= 0x10; - } else { - reg1[1] &= 0xCF; /* N2 */ - reg1[1] |= 0x20; - reg2[1] &= 0xCF; /* R2 */ - reg2[1] |= 0x10; - } - } - - if (_fband == 3) { - if ((first_ZF >= 1455) && - (first_ZF < 1630)) { - reg1[1] &= 0xCF; /* N2 */ - reg1[1] |= 0x20; - reg2[1] &= 0xCF; /* R2 */ - } else { - if (first_ZF < 1455) { - reg1[1] &= 0xCF; /* N2 */ - reg1[1] |= 0x20; - reg2[1] &= 0xCF; /* R2 */ - reg2[1] |= 0x10; - } else { - if (first_ZF >= 1630) { - reg1[1] &= 0xCF; /* N2 */ - reg2[1] &= 0xCF; /* R2 */ - reg2[1] |= 0x10; - } - } - } - } - - /* set ports, enable P0 for symbol rates > 4Ms/s */ - if (params->u.qpsk.symbol_rate >= 4000000) - reg1[1] |= 0x0c; - else - reg1[1] |= 0x04; - - reg2[1] |= 0x0c; - - R = 64; - A = 64; - P = 64; //32 - - M = (freq * R) / 4; /* in Mhz */ - N = (M - A * 1000) / (P * 1000); - - reg1[1] |= (N >> 9) & 0x03; - reg1[2] = (N >> 1) & 0xff; - reg1[3] = (N << 7) & 0x80; - - reg2[1] |= (R >> 8) & 0x03; - reg2[2] = R & 0xFF; /* R */ - - reg1[3] |= A & 0x7f; /* A */ - - if (P == 64) - reg1[1] |= 0x40; /* Prescaler 64/65 */ - - reg0[1] |= 0x03; - - /* already enabled - do not reenable i2c repeater or TX fails */ - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - msg.buf = reg0; - msg.len = sizeof(reg0); - if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - msg.buf = reg1; - msg.len = sizeof(reg1); - if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - msg.buf = reg2; - msg.len = sizeof(reg2); - if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - - return 0; -} - static u8 typhoon_cinergy1200s_inittab[] = { 0x01, 0x15, 0x02, 0x30, @@ -1068,9 +931,9 @@ static int tda10021_set_frontend(struct dvb_frontend *fe, result = budget_av->tda10021_set_frontend(fe, p); if (budget_av->tda10021_ts_enabled) { - tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa1); + tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa1); } else { - tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa0); + tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0); } return result; @@ -1096,15 +959,16 @@ static void frontend_init(struct budget_av *budget_av) switch (saa->pci->subsystem_device) { case SUBID_DVBS_KNC1: + case SUBID_DVBS_KNC1_PLUS: case SUBID_DVBS_EASYWATCH_1: if (saa->pci->subsystem_vendor == 0x1894) { - fe = stv0299_attach(&cinergy_1200s_1894_0010_config, + fe = dvb_attach(stv0299_attach, &cinergy_1200s_1894_0010_config, &budget_av->budget.i2c_adap); if (fe) { - fe->ops.tuner_ops.set_params = philips_su1278sh2_tua6100_tuner_set_params; + dvb_attach(tua6100_attach, fe, 0x60, &budget_av->budget.i2c_adap); } } else { - fe = stv0299_attach(&typhoon_config, + fe = dvb_attach(stv0299_attach, &typhoon_config, &budget_av->budget.i2c_adap); if (fe) { fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; @@ -1116,16 +980,15 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBS_TV_STAR_CI: case SUBID_DVBS_CYNERGY1200N: case SUBID_DVBS_EASYWATCH: - fe = stv0299_attach(&philips_sd1878_config, + fe = dvb_attach(stv0299_attach, &philips_sd1878_config, &budget_av->budget.i2c_adap); if (fe) { fe->ops.tuner_ops.set_params = philips_sd1878_tda8261_tuner_set_params; } break; - case SUBID_DVBS_KNC1_PLUS: case SUBID_DVBS_TYPHOON: - fe = stv0299_attach(&typhoon_config, + fe = dvb_attach(stv0299_attach, &typhoon_config, &budget_av->budget.i2c_adap); if (fe) { fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; @@ -1133,7 +996,7 @@ static void frontend_init(struct budget_av *budget_av) break; case SUBID_DVBS_CINERGY1200: - fe = stv0299_attach(&cinergy_1200s_config, + fe = dvb_attach(stv0299_attach, &cinergy_1200s_config, &budget_av->budget.i2c_adap); if (fe) { fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; @@ -1141,19 +1004,10 @@ static void frontend_init(struct budget_av *budget_av) break; case SUBID_DVBC_KNC1: - budget_av->reinitialise_demod = 1; - fe = tda10021_attach(&philips_cu1216_config, - &budget_av->budget.i2c_adap, - read_pwm(budget_av)); - if (fe) { - fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; - } - break; - case SUBID_DVBC_KNC1_PLUS: case SUBID_DVBC_CINERGY1200: budget_av->reinitialise_demod = 1; - fe = tda10021_attach(&philips_cu1216_config, + fe = dvb_attach(tda10021_attach, &philips_cu1216_config, &budget_av->budget.i2c_adap, read_pwm(budget_av)); if (fe) { @@ -1168,7 +1022,7 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBT_KNC1_PLUS: case SUBID_DVBT_CINERGY1200: budget_av->reinitialise_demod = 1; - fe = tda10046_attach(&philips_tu1216_config, + fe = dvb_attach(tda10046_attach, &philips_tu1216_config, &budget_av->budget.i2c_adap); if (fe) { fe->ops.tuner_ops.init = philips_tu1216_tuner_init; @@ -1192,8 +1046,7 @@ static void frontend_init(struct budget_av *budget_av) if (dvb_register_frontend(&budget_av->budget.dvb_adapter, budget_av->budget.dvb_frontend)) { printk(KERN_ERR "budget-av: Frontend registration failed!\n"); - if (budget_av->budget.dvb_frontend->ops.release) - budget_av->budget.dvb_frontend->ops.release(budget_av->budget.dvb_frontend); + dvb_frontend_detach(budget_av->budget.dvb_frontend); budget_av->budget.dvb_frontend = NULL; } } @@ -1227,8 +1080,10 @@ static int budget_av_detach(struct saa7146_dev *dev) if (budget_av->budget.ci_present) ciintf_deinit(budget_av); - if (budget_av->budget.dvb_frontend != NULL) + if (budget_av->budget.dvb_frontend != NULL) { dvb_unregister_frontend(budget_av->budget.dvb_frontend); + dvb_frontend_detach(budget_av->budget.dvb_frontend); + } err = ttpci_budget_deinit(&budget_av->budget); kfree(budget_av); @@ -1400,6 +1255,7 @@ static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x0010), MAKE_EXTENSION_PCI(knc1s, 0x1894, 0x0010), MAKE_EXTENSION_PCI(knc1sp, 0x1131, 0x0011), + MAKE_EXTENSION_PCI(knc1sp, 0x1894, 0x0011), MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014), MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016), MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index ffbbb3e34be..2a2e9b40061 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -749,17 +749,17 @@ static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe, struct dvb // setup PLL filter and TDA9889 switch (params->u.ofdm.bandwidth) { case BANDWIDTH_6_MHZ: - tda1004x_write_byte(fe, 0x0C, 0x14); + tda1004x_writereg(fe, 0x0C, 0x14); filter = 0; break; case BANDWIDTH_7_MHZ: - tda1004x_write_byte(fe, 0x0C, 0x80); + tda1004x_writereg(fe, 0x0C, 0x80); filter = 0; break; case BANDWIDTH_8_MHZ: - tda1004x_write_byte(fe, 0x0C, 0x14); + tda1004x_writereg(fe, 0x0C, 0x14); filter = 1; break; @@ -988,7 +988,7 @@ static void frontend_init(struct budget_ci *budget_ci) switch (budget_ci->budget.dev->pci->subsystem_device) { case 0x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059)) budget_ci->budget.dvb_frontend = - stv0299_attach(&alps_bsru6_config, &budget_ci->budget.i2c_adap); + dvb_attach(stv0299_attach, &alps_bsru6_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; @@ -998,7 +998,7 @@ static void frontend_init(struct budget_ci *budget_ci) case 0x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059)) budget_ci->budget.dvb_frontend = - stv0299_attach(&philips_su1278_tt_config, &budget_ci->budget.i2c_adap); + dvb_attach(stv0299_attach, &philips_su1278_tt_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_su1278_tt_tuner_set_params; break; @@ -1008,7 +1008,7 @@ static void frontend_init(struct budget_ci *budget_ci) case 0x1010: // TT DVB-C CI budget (stv0297/Philips tdm1316l(tda6651tt)) budget_ci->tuner_pll_address = 0x61; budget_ci->budget.dvb_frontend = - stv0297_attach(&dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap); + dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; break; @@ -1018,7 +1018,7 @@ static void frontend_init(struct budget_ci *budget_ci) case 0x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889) budget_ci->tuner_pll_address = 0x63; budget_ci->budget.dvb_frontend = - tda10045_attach(&philips_tdm1316l_config, &budget_ci->budget.i2c_adap); + dvb_attach(tda10045_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; @@ -1029,7 +1029,7 @@ static void frontend_init(struct budget_ci *budget_ci) case 0x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt)) budget_ci->tuner_pll_address = 0x60; budget_ci->budget.dvb_frontend = - tda10046_attach(&philips_tdm1316l_config, &budget_ci->budget.i2c_adap); + dvb_attach(tda10046_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; @@ -1038,16 +1038,15 @@ static void frontend_init(struct budget_ci *budget_ci) break; case 0x1017: // TT S-1500 PCI - budget_ci->budget.dvb_frontend = stv0299_attach(&alps_bsbe1_config, &budget_ci->budget.i2c_adap); + budget_ci->budget.dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; budget_ci->budget.dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; - if (lnbp21_attach(budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0)) { + if (dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0) == NULL) { printk("%s: No LNBP21 found!\n", __FUNCTION__); - if (budget_ci->budget.dvb_frontend->ops.release) - budget_ci->budget.dvb_frontend->ops.release(budget_ci->budget.dvb_frontend); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); budget_ci->budget.dvb_frontend = NULL; } } @@ -1065,8 +1064,7 @@ static void frontend_init(struct budget_ci *budget_ci) if (dvb_register_frontend (&budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) { printk("budget-ci: Frontend registration failed!\n"); - if (budget_ci->budget.dvb_frontend->ops.release) - budget_ci->budget.dvb_frontend->ops.release(budget_ci->budget.dvb_frontend); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); budget_ci->budget.dvb_frontend = NULL; } } @@ -1114,8 +1112,10 @@ static int budget_ci_detach(struct saa7146_dev *dev) if (budget_ci->budget.ci_present) ciintf_deinit(budget_ci); - if (budget_ci->budget.dvb_frontend) + if (budget_ci->budget.dvb_frontend) { dvb_unregister_frontend(budget_ci->budget.dvb_frontend); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + } err = ttpci_budget_deinit(&budget_ci->budget); tasklet_kill(&budget_ci->msp430_irq_tasklet); @@ -1153,7 +1153,7 @@ static struct pci_device_id pci_tbl[] = { MODULE_DEVICE_TABLE(pci, pci_tbl); static struct saa7146_extension budget_extension = { - .name = "budget_ci dvb\0", + .name = "budget_ci dvb", .flags = SAA7146_I2C_SHORT_DELAY, .module = THIS_MODULE, diff --git a/drivers/media/dvb/ttpci/budget-patch.c b/drivers/media/dvb/ttpci/budget-patch.c index 57227441891..fc1267b8c89 100644 --- a/drivers/media/dvb/ttpci/budget-patch.c +++ b/drivers/media/dvb/ttpci/budget-patch.c @@ -325,7 +325,7 @@ static void frontend_init(struct budget_patch* budget) case 0x1013: // SATELCO Multimedia PCI // try the ALPS BSRV2 first of all - budget->dvb_frontend = ves1x93_attach(&alps_bsrv2_config, &budget->i2c_adap); + budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); if (budget->dvb_frontend) { budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd; @@ -335,7 +335,7 @@ static void frontend_init(struct budget_patch* budget) } // try the ALPS BSRU6 now - budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap); + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); if (budget->dvb_frontend) { budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; budget->dvb_frontend->tuner_priv = &budget->i2c_adap; @@ -347,7 +347,7 @@ static void frontend_init(struct budget_patch* budget) } // Try the grundig 29504-451 - budget->dvb_frontend = tda8083_attach(&grundig_29504_451_config, &budget->i2c_adap); + budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); if (budget->dvb_frontend) { budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; @@ -367,8 +367,7 @@ static void frontend_init(struct budget_patch* budget) } else { if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) { printk("budget-av: Frontend registration failed!\n"); - if (budget->dvb_frontend->ops.release) - budget->dvb_frontend->ops.release(budget->dvb_frontend); + dvb_frontend_detach(budget->dvb_frontend); budget->dvb_frontend = NULL; } } @@ -627,8 +626,10 @@ static int budget_patch_detach (struct saa7146_dev* dev) struct budget_patch *budget = (struct budget_patch*) dev->ext_priv; int err; - if (budget->dvb_frontend) dvb_unregister_frontend(budget->dvb_frontend); - + if (budget->dvb_frontend) { + dvb_unregister_frontend(budget->dvb_frontend); + dvb_frontend_detach(budget->dvb_frontend); + } err = ttpci_budget_deinit (budget); kfree (budget); @@ -647,7 +648,7 @@ static void __exit budget_patch_exit(void) } static struct saa7146_extension budget_extension = { - .name = "budget_patch dvb\0", + .name = "budget_patch dvb", .flags = 0, .module = THIS_MODULE, diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c index 863dffb4ed8..e58f0391e9d 100644 --- a/drivers/media/dvb/ttpci/budget.c +++ b/drivers/media/dvb/ttpci/budget.c @@ -41,6 +41,8 @@ #include "l64781.h" #include "tda8083.h" #include "s5h1420.h" +#include "tda10086.h" +#include "tda826x.h" #include "lnbp21.h" #include "bsru6.h" @@ -342,6 +344,11 @@ static struct s5h1420_config s5h1420_config = { .invert = 1, }; +static struct tda10086_config tda10086_config = { + .demod_address = 0x0e, + .invert = 0, +}; + static u8 read_pwm(struct budget* budget) { u8 b = 0xff; @@ -361,7 +368,7 @@ static void frontend_init(struct budget *budget) case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659)) case 0x1013: // try the ALPS BSRV2 first of all - budget->dvb_frontend = ves1x93_attach(&alps_bsrv2_config, &budget->i2c_adap); + budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); if (budget->dvb_frontend) { budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; @@ -371,7 +378,7 @@ static void frontend_init(struct budget *budget) } // try the ALPS BSRU6 now - budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap); + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); if (budget->dvb_frontend) { budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; budget->dvb_frontend->tuner_priv = &budget->i2c_adap; @@ -381,7 +388,7 @@ static void frontend_init(struct budget *budget) case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) - budget->dvb_frontend = ves1820_attach(&alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget)); + budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget)); if (budget->dvb_frontend) { budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; break; @@ -390,7 +397,7 @@ static void frontend_init(struct budget *budget) case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060)) - budget->dvb_frontend = l64781_attach(&grundig_29504_401_config, &budget->i2c_adap); + budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap); if (budget->dvb_frontend) { budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; break; @@ -398,7 +405,7 @@ static void frontend_init(struct budget *budget) break; case 0x4f60: // Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/ALPS BSRU6(tsa5059)) - budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap); + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); if (budget->dvb_frontend) { budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; budget->dvb_frontend->tuner_priv = &budget->i2c_adap; @@ -408,7 +415,7 @@ static void frontend_init(struct budget *budget) break; case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522)) - budget->dvb_frontend = tda8083_attach(&grundig_29504_451_config, &budget->i2c_adap); + budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); if (budget->dvb_frontend) { budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; @@ -417,10 +424,28 @@ static void frontend_init(struct budget *budget) break; case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260)) - budget->dvb_frontend = s5h1420_attach(&s5h1420_config, &budget->i2c_adap); + budget->dvb_frontend = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap); if (budget->dvb_frontend) { budget->dvb_frontend->ops.tuner_ops.set_params = s5h1420_tuner_set_params; - if (lnbp21_attach(budget->dvb_frontend, &budget->i2c_adap, 0, 0)) { + if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) { + printk("%s: No LNBP21 found!\n", __FUNCTION__); + goto error_out; + } + break; + } + + case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262) + // gpio2 is connected to CLB - reset it + leave it high + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); + msleep(1); + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); + msleep(1); + + budget->dvb_frontend = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + if (dvb_attach(tda826x_attach, budget->dvb_frontend, 0x60, &budget->i2c_adap, 0) == NULL) + printk("%s: No tda826x found!\n", __FUNCTION__); + if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) { printk("%s: No LNBP21 found!\n", __FUNCTION__); goto error_out; } @@ -442,8 +467,7 @@ static void frontend_init(struct budget *budget) error_out: printk("budget: Frontend registration failed!\n"); - if (budget->dvb_frontend->ops.release) - budget->dvb_frontend->ops.release(budget->dvb_frontend); + dvb_frontend_detach(budget->dvb_frontend); budget->dvb_frontend = NULL; return; } @@ -481,7 +505,10 @@ static int budget_detach (struct saa7146_dev* dev) struct budget *budget = (struct budget*) dev->ext_priv; int err; - if (budget->dvb_frontend) dvb_unregister_frontend(budget->dvb_frontend); + if (budget->dvb_frontend) { + dvb_unregister_frontend(budget->dvb_frontend); + dvb_frontend_detach(budget->dvb_frontend); + } err = ttpci_budget_deinit (budget); @@ -497,6 +524,7 @@ MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT); MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT); MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC); +MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT); MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY); MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY); @@ -506,6 +534,7 @@ static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005), MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016), + MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018), MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60), MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61), { @@ -516,7 +545,7 @@ static struct pci_device_id pci_tbl[] = { MODULE_DEVICE_TABLE(pci, pci_tbl); static struct saa7146_extension budget_extension = { - .name = "budget dvb\0", + .name = "budget dvb", .flags = SAA7146_I2C_SHORT_DELAY, .module = THIS_MODULE, diff --git a/drivers/media/dvb/ttusb-budget/Kconfig b/drivers/media/dvb/ttusb-budget/Kconfig index 46a6a60d2ab..e78ea9227b0 100644 --- a/drivers/media/dvb/ttusb-budget/Kconfig +++ b/drivers/media/dvb/ttusb-budget/Kconfig @@ -2,13 +2,13 @@ config DVB_TTUSB_BUDGET tristate "Technotrend/Hauppauge Nova-USB devices" depends on DVB_CORE && USB && I2C select DVB_PLL - select DVB_CX22700 - select DVB_TDA1004X - select DVB_VES1820 - select DVB_TDA8083 - select DVB_STV0299 - select DVB_STV0297 - select DVB_LNBP21 + select DVB_CX22700 if !DVB_FE_CUSTOMISE + select DVB_TDA1004X if !DVB_FE_CUSTOMISE + select DVB_VES1820 if !DVB_FE_CUSTOMISE + select DVB_TDA8083 if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_STV0297 if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE help Support for external USB adapters designed by Technotrend and produced by Hauppauge, shipped under the brand name 'Nova-USB'. diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 04cef302345..234199875f5 100644 --- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -1107,17 +1107,17 @@ static int philips_tdm1316l_tuner_set_params(struct dvb_frontend* fe, struct dvb // setup PLL filter switch (params->u.ofdm.bandwidth) { case BANDWIDTH_6_MHZ: - tda1004x_write_byte(fe, 0x0C, 0); + tda1004x_writereg(fe, 0x0C, 0); filter = 0; break; case BANDWIDTH_7_MHZ: - tda1004x_write_byte(fe, 0x0C, 0); + tda1004x_writereg(fe, 0x0C, 0); filter = 0; break; case BANDWIDTH_8_MHZ: - tda1004x_write_byte(fe, 0x0C, 0xFF); + tda1004x_writereg(fe, 0x0C, 0xFF); filter = 1; break; @@ -1564,13 +1564,13 @@ static void frontend_init(struct ttusb* ttusb) switch(le16_to_cpu(ttusb->dev->descriptor.idProduct)) { case 0x1003: // Hauppauge/TT Nova-USB-S budget (stv0299/ALPS BSRU6|BSBE1(tsa5059)) // try the stv0299 based first - ttusb->fe = stv0299_attach(&alps_stv0299_config, &ttusb->i2c_adap); + ttusb->fe = dvb_attach(stv0299_attach, &alps_stv0299_config, &ttusb->i2c_adap); if (ttusb->fe != NULL) { ttusb->fe->ops.tuner_ops.set_params = philips_tsa5059_tuner_set_params; if(ttusb->revision == TTUSB_REV_2_2) { // ALPS BSBE1 alps_stv0299_config.inittab = alps_bsbe1_inittab; - lnbp21_attach(ttusb->fe, &ttusb->i2c_adap, 0, 0); + dvb_attach(lnbp21_attach, ttusb->fe, &ttusb->i2c_adap, 0, 0); } else { // ALPS BSRU6 ttusb->fe->ops.set_voltage = ttusb_set_voltage; } @@ -1578,7 +1578,7 @@ static void frontend_init(struct ttusb* ttusb) } // Grundig 29504-491 - ttusb->fe = tda8083_attach(&ttusb_novas_grundig_29504_491_config, &ttusb->i2c_adap); + ttusb->fe = dvb_attach(tda8083_attach, &ttusb_novas_grundig_29504_491_config, &ttusb->i2c_adap); if (ttusb->fe != NULL) { ttusb->fe->ops.tuner_ops.set_params = ttusb_novas_grundig_29504_491_tuner_set_params; ttusb->fe->ops.set_voltage = ttusb_set_voltage; @@ -1587,13 +1587,13 @@ static void frontend_init(struct ttusb* ttusb) break; case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) - ttusb->fe = ves1820_attach(&alps_tdbe2_config, &ttusb->i2c_adap, read_pwm(ttusb)); + ttusb->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &ttusb->i2c_adap, read_pwm(ttusb)); if (ttusb->fe != NULL) { ttusb->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; break; } - ttusb->fe = stv0297_attach(&dvbc_philips_tdm1316l_config, &ttusb->i2c_adap); + ttusb->fe = dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &ttusb->i2c_adap); if (ttusb->fe != NULL) { ttusb->fe->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; break; @@ -1602,14 +1602,14 @@ static void frontend_init(struct ttusb* ttusb) case 0x1005: // Hauppauge/TT Nova-USB-t budget (tda10046/Philips td1316(tda6651tt) OR cx22700/ALPS TDMB7(??)) // try the ALPS TDMB7 first - ttusb->fe = cx22700_attach(&alps_tdmb7_config, &ttusb->i2c_adap); + ttusb->fe = dvb_attach(cx22700_attach, &alps_tdmb7_config, &ttusb->i2c_adap); if (ttusb->fe != NULL) { ttusb->fe->ops.tuner_ops.set_params = alps_tdmb7_tuner_set_params; break; } // Philips td1316 - ttusb->fe = tda10046_attach(&philips_tdm1316l_config, &ttusb->i2c_adap); + ttusb->fe = dvb_attach(tda10046_attach, &philips_tdm1316l_config, &ttusb->i2c_adap); if (ttusb->fe != NULL) { ttusb->fe->ops.tuner_ops.init = philips_tdm1316l_tuner_init; ttusb->fe->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; @@ -1625,8 +1625,7 @@ static void frontend_init(struct ttusb* ttusb) } else { if (dvb_register_frontend(&ttusb->adapter, ttusb->fe)) { printk("dvb-ttusb-budget: Frontend registration failed!\n"); - if (ttusb->fe->ops.release) - ttusb->fe->ops.release(ttusb->fe); + dvb_frontend_detach(ttusb->fe); ttusb->fe = NULL; } } @@ -1763,7 +1762,10 @@ static void ttusb_disconnect(struct usb_interface *intf) dvb_net_release(&ttusb->dvbnet); dvb_dmxdev_release(&ttusb->dmxdev); dvb_dmx_release(&ttusb->dvb_demux); - if (ttusb->fe != NULL) dvb_unregister_frontend(ttusb->fe); + if (ttusb->fe != NULL) { + dvb_unregister_frontend(ttusb->fe); + dvb_frontend_detach(ttusb->fe); + } i2c_del_adapter(&ttusb->i2c_adap); dvb_unregister_adapter(&ttusb->adapter); diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c index c9d663549df..de077a75719 100644 --- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c @@ -1512,7 +1512,11 @@ static void ttusb_dec_exit_dvb(struct ttusb_dec *dec) dec->demux.dmx.remove_frontend(&dec->demux.dmx, &dec->frontend); dvb_dmxdev_release(&dec->dmxdev); dvb_dmx_release(&dec->demux); - if (dec->fe) dvb_unregister_frontend(dec->fe); + if (dec->fe) { + dvb_unregister_frontend(dec->fe); + if (dec->fe->ops.release) + dec->fe->ops.release(dec->fe); + } dvb_unregister_adapter(&dec->adapter); } diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 220076b1b95..7015517e2c1 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -7,7 +7,7 @@ menu "Radio Adapters" config RADIO_CADET tristate "ADS Cadet AM/FM Tuner" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 ---help--- Choose Y here if you have one of these AM/FM radio cards, and then fill in the port address below. @@ -25,7 +25,7 @@ config RADIO_CADET config RADIO_RTRACK tristate "AIMSlab RadioTrack (aka RadioReveal) support" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. @@ -59,7 +59,7 @@ config RADIO_RTRACK_PORT config RADIO_RTRACK2 tristate "AIMSlab RadioTrack II support" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 ---help--- Choose Y here if you have this FM radio card, and then fill in the port address below. @@ -82,7 +82,7 @@ config RADIO_RTRACK2_PORT config RADIO_AZTECH tristate "Aztech/Packard Bell Radio" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. @@ -106,7 +106,7 @@ config RADIO_AZTECH_PORT config RADIO_GEMTEK tristate "GemTek Radio Card support" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 ---help--- Choose Y here if you have this FM radio card, and then fill in the port address below. @@ -131,7 +131,7 @@ config RADIO_GEMTEK_PORT config RADIO_GEMTEK_PCI tristate "GemTek PCI Radio Card support" - depends on VIDEO_V4L1 && PCI + depends on VIDEO_V4L2 && PCI ---help--- Choose Y here if you have this PCI FM radio card. @@ -145,7 +145,7 @@ config RADIO_GEMTEK_PCI config RADIO_MAXIRADIO tristate "Guillemot MAXI Radio FM 2000 radio" - depends on VIDEO_V4L1 && PCI + depends on VIDEO_V4L2 && PCI ---help--- Choose Y here if you have this radio card. This card may also be found as Gemtek PCI FM. @@ -160,7 +160,7 @@ config RADIO_MAXIRADIO config RADIO_MAESTRO tristate "Maestro on board radio" - depends on VIDEO_V4L1 + depends on VIDEO_V4L2 && PCI ---help--- Say Y here to directly support the on-board radio tuner on the Maestro 2 or 2E sound card. @@ -208,7 +208,7 @@ config RADIO_MIROPCM20_RDS config RADIO_SF16FMI tristate "SF16FMI Radio" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 ---help--- Choose Y here if you have one of these FM radio cards. If you compile the driver into the kernel and your card is not PnP one, you @@ -225,7 +225,7 @@ config RADIO_SF16FMI config RADIO_SF16FMR2 tristate "SF16FMR2 Radio" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 ---help--- Choose Y here if you have one of these FM radio cards. @@ -239,7 +239,7 @@ config RADIO_SF16FMR2 config RADIO_TERRATEC tristate "TerraTec ActiveRadio ISA Standalone" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 ---help--- Choose Y here if you have this FM radio card, and then fill in the port address below. (TODO) @@ -268,7 +268,7 @@ config RADIO_TERRATEC_PORT config RADIO_TRUST tristate "Trust FM radio card" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 help This is a driver for the Trust FM radio cards. Say Y if you have such a card and want to use it under Linux. @@ -286,7 +286,7 @@ config RADIO_TRUST_PORT config RADIO_TYPHOON tristate "Typhoon Radio (a.k.a. EcoRadio)" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address and the frequency used for muting below. @@ -330,7 +330,7 @@ config RADIO_TYPHOON_MUTEFREQ config RADIO_ZOLTRIX tristate "Zoltrix Radio" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. @@ -352,7 +352,7 @@ config RADIO_ZOLTRIX_PORT config USB_DSBR tristate "D-Link USB FM radio support (EXPERIMENTAL)" - depends on USB && VIDEO_V4L1 && EXPERIMENTAL + depends on USB && VIDEO_V4L2 && EXPERIMENTAL ---help--- Say Y here if you want to connect this type of radio to your computer's USB port. Note that the audio is not digital, and diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index f7e33f9ee8e..db865a0667e 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c @@ -33,8 +33,14 @@ History: + Version 0.41-ac1: + Alan Cox: Some cleanups and fixes + + Version 0.41: + Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> + Version 0.40: - Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing + Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing Version 0.30: Markus: Updates for 2.5.x kernel and more ISO compliant source @@ -65,13 +71,12 @@ */ - #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/input.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <linux/usb.h> #include <linux/smp_lock.h> @@ -79,7 +84,22 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.40" +#include <linux/version.h> /* for KERNEL_VERSION MACRO */ + +#define DRIVER_VERSION "v0.41" +#define RADIO_VERSION KERNEL_VERSION(0,4,1) + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + } +}; + #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>" #define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver" @@ -111,7 +131,7 @@ static int radio_nr = -1; module_param(radio_nr, int, 0); /* Data for one (physical) device */ -typedef struct { +struct dsbr100_device { struct usb_device *usbdev; struct video_device *videodev; unsigned char transfer_buffer[TB_LEN]; @@ -119,7 +139,8 @@ typedef struct { int stereo; int users; int removed; -} dsbr100_device; + int muted; +}; /* File system interface */ @@ -138,7 +159,6 @@ static struct video_device dsbr100_videodev_template= .owner = THIS_MODULE, .name = "D-Link DSB-R 100", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_AZTECH, .fops = &usb_dsbr100_fops, .release = video_device_release, }; @@ -161,7 +181,7 @@ static struct usb_driver usb_dsbr100_driver = { /* Low-level device interface begins here */ /* switch on radio */ -static int dsbr100_start(dsbr100_device *radio) +static int dsbr100_start(struct dsbr100_device *radio) { if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), USB_REQ_GET_STATUS, @@ -172,12 +192,13 @@ static int dsbr100_start(dsbr100_device *radio) USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 0x01, 0x00, radio->transfer_buffer, 8, 300)<0) return -1; + radio->muted=0; return (radio->transfer_buffer)[0]; } /* switch off radio */ -static int dsbr100_stop(dsbr100_device *radio) +static int dsbr100_stop(struct dsbr100_device *radio) { if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), USB_REQ_GET_STATUS, @@ -188,11 +209,12 @@ static int dsbr100_stop(dsbr100_device *radio) USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 0x00, 0x00, radio->transfer_buffer, 8, 300)<0) return -1; + radio->muted=1; return (radio->transfer_buffer)[0]; } /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ -static int dsbr100_setfreq(dsbr100_device *radio, int freq) +static int dsbr100_setfreq(struct dsbr100_device *radio, int freq) { freq = (freq/16*80)/1000+856; if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), @@ -217,7 +239,7 @@ static int dsbr100_setfreq(dsbr100_device *radio, int freq) /* return the device status. This is, in effect, just whether it sees a stereo signal or not. Pity. */ -static void dsbr100_getstat(dsbr100_device *radio) +static void dsbr100_getstat(struct dsbr100_device *radio) { if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), USB_REQ_GET_STATUS, @@ -236,9 +258,9 @@ usb if it is */ static int usb_dsbr100_probe(struct usb_interface *intf, const struct usb_device_id *id) { - dsbr100_device *radio; + struct dsbr100_device *radio; - if (!(radio = kmalloc(sizeof(dsbr100_device), GFP_KERNEL))) + if (!(radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL))) return -ENOMEM; if (!(radio->videodev = video_device_alloc())) { kfree(radio); @@ -271,7 +293,7 @@ code I'd expect I better did that, but if there's a memory leak here it's tiny (~50 bytes per disconnect) */ static void usb_dsbr100_disconnect(struct usb_interface *intf) { - dsbr100_device *radio = usb_get_intfdata(intf); + struct dsbr100_device *radio = usb_get_intfdata(intf); usb_set_intfdata (intf, NULL); if (radio) { @@ -291,89 +313,121 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf) static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { - dsbr100_device *radio=video_get_drvdata(video_devdata(file)); + struct dsbr100_device *radio=video_get_drvdata(video_devdata(file)); if (!radio) return -EIO; switch(cmd) { - case VIDIOCGCAP: { - struct video_capability *v = arg; - - memset(v, 0, sizeof(*v)); - v->type = VID_TYPE_TUNER; - v->channels = 1; - v->audios = 1; - strcpy(v->name, "D-Link R-100 USB FM Radio"); + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *v = arg; + memset(v,0,sizeof(*v)); + strlcpy(v->driver, "dsbr100", sizeof (v->driver)); + strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; } - case VIDIOCGTUNER: { - struct video_tuner *v = arg; + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *v = arg; - dsbr100_getstat(radio); - if(v->tuner) /* Only 1 tuner */ + if (v->index > 0) return -EINVAL; + + dsbr100_getstat(radio); + + memset(v,0,sizeof(*v)); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = FREQ_MIN*FREQ_MUL; v->rangehigh = FREQ_MAX*FREQ_MUL; - v->flags = VIDEO_TUNER_LOW; - v->mode = VIDEO_MODE_AUTO; - v->signal = radio->stereo*0x7000; - /* Don't know how to get signal strength */ - v->flags |= VIDEO_TUNER_STEREO_ON*radio->stereo; - strcpy(v->name, "DSB R-100"); - return 0; - } - case VIDIOCSTUNER: { - struct video_tuner *v = arg; + v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability=V4L2_TUNER_CAP_LOW; + if(radio->stereo) + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xFFFF; /* We can't get the signal strength */ - if(v->tuner!=0) - return -EINVAL; - /* Only 1 tuner so no setting needed ! */ return 0; } - case VIDIOCGFREQ: { - int *freq = arg; + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *v = arg; - if (radio->curfreq==-1) + if (v->index > 0) return -EINVAL; - *freq = radio->curfreq; + return 0; } - case VIDIOCSFREQ: { - int *freq = arg; + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *f = arg; - radio->curfreq = *freq; + radio->curfreq = f->frequency; if (dsbr100_setfreq(radio, radio->curfreq)==-1) warn("Set frequency failed"); return 0; } - case VIDIOCGAUDIO: { - struct video_audio *v = arg; - - memset(v, 0, sizeof(*v)); - v->flags |= VIDEO_AUDIO_MUTABLE; - v->mode = VIDEO_SOUND_STEREO; - v->volume = 1; - v->step = 1; - strcpy(v->name, "Radio"); + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + f->type = V4L2_TUNER_RADIO; + f->frequency = radio->curfreq; + return 0; } - case VIDIOCSAUDIO: { - struct video_audio *v = arg; - - if (v->audio) - return -EINVAL; - if (v->flags&VIDEO_AUDIO_MUTE) { - if (dsbr100_stop(radio)==-1) - warn("Radio did not respond properly"); + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return 0; + } } - else - if (dsbr100_start(radio)==-1) - warn("Radio did not respond properly"); - return 0; + return -EINVAL; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=radio->muted; + return 0; + } + return -EINVAL; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + if (dsbr100_stop(radio)==-1) + warn("Radio did not respond properly"); + } else { + if (dsbr100_start(radio)==-1) + warn("Radio did not respond properly"); + } + return 0; + } + return -EINVAL; } default: - return -ENOIOCTLCMD; + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + usb_dsbr100_do_ioctl); } } @@ -385,9 +439,11 @@ static int usb_dsbr100_ioctl(struct inode *inode, struct file *file, static int usb_dsbr100_open(struct inode *inode, struct file *file) { - dsbr100_device *radio=video_get_drvdata(video_devdata(file)); + struct dsbr100_device *radio=video_get_drvdata(video_devdata(file)); radio->users = 1; + radio->muted = 1; + if (dsbr100_start(radio)<0) { warn("Radio did not start up properly"); radio->users = 0; @@ -399,7 +455,7 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file) static int usb_dsbr100_close(struct inode *inode, struct file *file) { - dsbr100_device *radio=video_get_drvdata(video_devdata(file)); + struct dsbr100_device *radio=video_get_drvdata(video_devdata(file)); if (!radio) return -ENODEV; diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c index df22a582e7a..3368a89bfad 100644 --- a/drivers/media/radio/radio-aimslab.c +++ b/drivers/media/radio/radio-aimslab.c @@ -1,5 +1,6 @@ /* radiotrack (radioreveal) driver for Linux radio support * (c) 1997 M. Kirkwood + * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> * Converted to new API by Alan Cox <Alan.Cox@linux.org> * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> * @@ -33,11 +34,13 @@ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ -#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> -#include <linux/config.h> /* CONFIG_RADIO_RTRACK_PORT */ #include <asm/semaphore.h> /* Lock for the I/O */ +#include <linux/version.h> /* for KERNEL_VERSION MACRO */ +#define RADIO_VERSION KERNEL_VERSION(0,0,2) + #ifndef CONFIG_RADIO_RTRACK_PORT #define CONFIG_RADIO_RTRACK_PORT -1 #endif @@ -209,6 +212,25 @@ static int rt_getsigstr(struct rt_device *dev) return 1; /* signal present */ } +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0xff, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + static int rt_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { @@ -217,73 +239,114 @@ static int rt_do_ioctl(struct inode *inode, struct file *file, switch(cmd) { - case VIDIOCGCAP: + case VIDIOC_QUERYCAP: { - struct video_capability *v = arg; + struct v4l2_capability *v = arg; memset(v,0,sizeof(*v)); - v->type=VID_TYPE_TUNER; - v->channels=1; - v->audios=1; - strcpy(v->name, "RadioTrack"); + strlcpy(v->driver, "radio-aimslab", sizeof (v->driver)); + strlcpy(v->card, "RadioTrack", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; } - case VIDIOCGTUNER: + case VIDIOC_G_TUNER: { - struct video_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; + + memset(v,0,sizeof(*v)); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow=(87*16000); v->rangehigh=(108*16000); - v->flags=VIDEO_TUNER_LOW; - v->mode=VIDEO_MODE_AUTO; - strcpy(v->name, "FM"); + v->rxsubchans =V4L2_TUNER_SUB_MONO; + v->capability=V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; v->signal=0xFFFF*rt_getsigstr(rt); + return 0; } - case VIDIOCSTUNER: + case VIDIOC_S_TUNER: { - struct video_tuner *v = arg; - if(v->tuner!=0) + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; - /* Only 1 tuner so no setting needed ! */ + return 0; } - case VIDIOCGFREQ: + case VIDIOC_S_FREQUENCY: { - unsigned long *freq = arg; - *freq = rt->curfreq; + struct v4l2_frequency *f = arg; + + rt->curfreq = f->frequency; + rt_setfreq(rt, rt->curfreq); return 0; } - case VIDIOCSFREQ: + case VIDIOC_G_FREQUENCY: { - unsigned long *freq = arg; - rt->curfreq = *freq; - rt_setfreq(rt, rt->curfreq); + struct v4l2_frequency *f = arg; + + f->type = V4L2_TUNER_RADIO; + f->frequency = rt->curfreq; + return 0; } - case VIDIOCGAUDIO: + case VIDIOC_QUERYCTRL: { - struct video_audio *v = arg; - memset(v,0, sizeof(*v)); - v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; - v->volume=rt->curvol * 6554; - v->step=6554; - strcpy(v->name, "Radio"); - return 0; + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); + } + } + return -EINVAL; } - case VIDIOCSAUDIO: + case VIDIOC_G_CTRL: { - struct video_audio *v = arg; - if(v->audio) - return -EINVAL; - if(v->flags&VIDEO_AUDIO_MUTE) - rt_mute(rt); - else - rt_setvol(rt,v->volume/6554); - return 0; + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=rt->muted; + return (0); + case V4L2_CID_AUDIO_VOLUME: + ctrl->value=rt->curvol * 6554; + return (0); + } + return -EINVAL; } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + rt_mute(rt); + } else { + rt_setvol(rt,rt->curvol); + } + return (0); + case V4L2_CID_AUDIO_VOLUME: + rt_setvol(rt,ctrl->value); + return (0); + } + return -EINVAL; + } + default: - return -ENOIOCTLCMD; + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + rt_do_ioctl); } } @@ -309,7 +372,7 @@ static struct video_device rtrack_radio= .owner = THIS_MODULE, .name = "RadioTrack radio", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_RTRACK, + .hardware = 0, .fops = &rtrack_fops, }; diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c index 95e6322133e..3ba5fa8cf7e 100644 --- a/drivers/media/radio/radio-aztech.c +++ b/drivers/media/radio/radio-aztech.c @@ -1,5 +1,6 @@ /* radio-aztech.c - Aztech radio card driver for Linux 2.2 * + * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> * Adapted to support the Video for Linux API by * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by: * @@ -30,9 +31,30 @@ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ -#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> -#include <linux/config.h> /* CONFIG_RADIO_AZTECH_PORT */ + +#include <linux/version.h> /* for KERNEL_VERSION MACRO */ +#define RADIO_VERSION KERNEL_VERSION(0,0,2) + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0xff, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ @@ -166,81 +188,121 @@ static int az_do_ioctl(struct inode *inode, struct file *file, switch(cmd) { - case VIDIOCGCAP: + case VIDIOC_QUERYCAP: { - struct video_capability *v = arg; + struct v4l2_capability *v = arg; memset(v,0,sizeof(*v)); - v->type=VID_TYPE_TUNER; - v->channels=1; - v->audios=1; - strcpy(v->name, "Aztech Radio"); + strlcpy(v->driver, "radio-aztech", sizeof (v->driver)); + strlcpy(v->card, "Aztech Radio", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; } - case VIDIOCGTUNER: + case VIDIOC_G_TUNER: { - struct video_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; + + memset(v,0,sizeof(*v)); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow=(87*16000); v->rangehigh=(108*16000); - v->flags=VIDEO_TUNER_LOW; - v->mode=VIDEO_MODE_AUTO; - v->signal=0xFFFF*az_getsigstr(az); + v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability=V4L2_TUNER_CAP_LOW; if(az_getstereo(az)) - v->flags|=VIDEO_TUNER_STEREO_ON; - strcpy(v->name, "FM"); + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal=0xFFFF*az_getsigstr(az); + return 0; } - case VIDIOCSTUNER: + case VIDIOC_S_TUNER: { - struct video_tuner *v = arg; - if(v->tuner!=0) + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; + return 0; } - case VIDIOCGFREQ: + case VIDIOC_S_FREQUENCY: { - unsigned long *freq = arg; - *freq = az->curfreq; + struct v4l2_frequency *f = arg; + + az->curfreq = f->frequency; + az_setfreq(az, az->curfreq); return 0; } - case VIDIOCSFREQ: + case VIDIOC_G_FREQUENCY: { - unsigned long *freq = arg; - az->curfreq = *freq; - az_setfreq(az, az->curfreq); + struct v4l2_frequency *f = arg; + + f->type = V4L2_TUNER_RADIO; + f->frequency = az->curfreq; + return 0; } - case VIDIOCGAUDIO: + + case VIDIOC_QUERYCTRL: { - struct video_audio *v = arg; - memset(v,0, sizeof(*v)); - v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; - if(az->stereo) - v->mode=VIDEO_SOUND_STEREO; - else - v->mode=VIDEO_SOUND_MONO; - v->volume=az->curvol; - v->step=16384; - strcpy(v->name, "Radio"); - return 0; + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); + } + } + return -EINVAL; } - case VIDIOCSAUDIO: + case VIDIOC_G_CTRL: { - struct video_audio *v = arg; - if(v->audio) - return -EINVAL; - az->curvol=v->volume; - - az->stereo=(v->mode&VIDEO_SOUND_STEREO)?1:0; - if(v->flags&VIDEO_AUDIO_MUTE) - az_setvol(az,0); - else - az_setvol(az,az->curvol); - return 0; + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (az->curvol==0) + ctrl->value=1; + else + ctrl->value=0; + return (0); + case V4L2_CID_AUDIO_VOLUME: + ctrl->value=az->curvol * 6554; + return (0); + } + return -EINVAL; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + az_setvol(az,0); + } else { + az_setvol(az,az->curvol); + } + return (0); + case V4L2_CID_AUDIO_VOLUME: + az_setvol(az,ctrl->value); + return (0); + } + return -EINVAL; } + default: - return -ENOIOCTLCMD; + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + az_do_ioctl); } } @@ -266,7 +328,7 @@ static struct video_device aztech_radio= .owner = THIS_MODULE, .name = "Aztech radio", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_AZTECH, + .hardware = 0, .fops = &aztech_fops, }; diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c index 8641aec7baf..69d4b7919c5 100644 --- a/drivers/media/radio/radio-cadet.c +++ b/drivers/media/radio/radio-cadet.c @@ -25,20 +25,28 @@ * * 2003-01-31 Alan Cox <alan@redhat.com> * Cleaned up locking, delay code, general odds and ends + * + * 2006-07-30 Hans J. Koch <koch@hjk-az.de> + * Changed API to V4L2 */ +#include <linux/version.h> #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ #include <linux/ioport.h> /* request_region */ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ -#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/videodev2.h> /* V4L2 API defs */ #include <media/v4l2-common.h> #include <linux/param.h> #include <linux/pnp.h> #define RDS_BUFFER 256 +#define RDS_RX_FLAG 1 +#define MBS_RX_FLAG 2 + +#define CADET_VERSION KERNEL_VERSION(0,3,3) static int io=-1; /* default to isapnp activation */ static int radio_nr = -1; @@ -61,44 +69,24 @@ static int cadet_probe(void); */ static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}}; -static int cadet_getrds(void) -{ - int rdsstat=0; - - spin_lock(&cadet_io_lock); - outb(3,io); /* Select Decoder Control/Status */ - outb(inb(io+1)&0x7f,io+1); /* Reset RDS detection */ - spin_unlock(&cadet_io_lock); - - msleep(100); - - spin_lock(&cadet_io_lock); - outb(3,io); /* Select Decoder Control/Status */ - if((inb(io+1)&0x80)!=0) { - rdsstat|=VIDEO_TUNER_RDS_ON; - } - if((inb(io+1)&0x10)!=0) { - rdsstat|=VIDEO_TUNER_MBS_ON; - } - spin_unlock(&cadet_io_lock); - return rdsstat; -} -static int cadet_getstereo(void) +static int +cadet_getstereo(void) { - int ret = 0; + int ret = V4L2_TUNER_SUB_MONO; if(curtuner != 0) /* Only FM has stereo capability! */ - return 0; + return V4L2_TUNER_SUB_MONO; spin_lock(&cadet_io_lock); outb(7,io); /* Select tuner control */ if( (inb(io+1) & 0x40) == 0) - ret = 1; + ret = V4L2_TUNER_SUB_STEREO; spin_unlock(&cadet_io_lock); return ret; } -static unsigned cadet_gettune(void) +static unsigned +cadet_gettune(void) { int curvol,i; unsigned fifo=0; @@ -135,7 +123,8 @@ static unsigned cadet_gettune(void) return fifo; } -static unsigned cadet_getfreq(void) +static unsigned +cadet_getfreq(void) { int i; unsigned freq=0,test,fifo=0; @@ -167,7 +156,8 @@ static unsigned cadet_getfreq(void) return freq; } -static void cadet_settune(unsigned fifo) +static void +cadet_settune(unsigned fifo) { int i; unsigned test; @@ -195,7 +185,8 @@ static void cadet_settune(unsigned fifo) spin_unlock(&cadet_io_lock); } -static void cadet_setfreq(unsigned freq) +static void +cadet_setfreq(unsigned freq) { unsigned fifo; int i,j,test; @@ -255,7 +246,8 @@ static void cadet_setfreq(unsigned freq) } -static int cadet_getvol(void) +static int +cadet_getvol(void) { int ret = 0; @@ -270,7 +262,8 @@ static int cadet_getvol(void) } -static void cadet_setvol(int vol) +static void +cadet_setvol(int vol) { spin_lock(&cadet_io_lock); outb(7,io); /* Select tuner control */ @@ -281,7 +274,8 @@ static void cadet_setvol(int vol) spin_unlock(&cadet_io_lock); } -static void cadet_handler(unsigned long data) +static void +cadet_handler(unsigned long data) { /* * Service the RDS fifo @@ -322,8 +316,8 @@ static void cadet_handler(unsigned long data) -static ssize_t cadet_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) +static ssize_t +cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { int i=0; unsigned char readbuf[RDS_BUFFER]; @@ -359,128 +353,156 @@ static int cadet_do_ioctl(struct inode *inode, struct file *file, { switch(cmd) { - case VIDIOCGCAP: + case VIDIOC_QUERYCAP: { - struct video_capability *v = arg; - memset(v,0,sizeof(*v)); - v->type=VID_TYPE_TUNER; - v->channels=2; - v->audios=1; - strcpy(v->name, "ADS Cadet"); + struct v4l2_capability *cap = arg; + memset(cap,0,sizeof(*cap)); + cap->capabilities = + V4L2_CAP_TUNER | + V4L2_CAP_READWRITE; + cap->version = CADET_VERSION; + strcpy(cap->driver, "ADS Cadet"); + strcpy(cap->card, "ADS Cadet"); return 0; } - case VIDIOCGTUNER: + case VIDIOC_G_TUNER: { - struct video_tuner *v = arg; - if((v->tuner<0)||(v->tuner>1)) { - return -EINVAL; - } - switch(v->tuner) { - case 0: - strcpy(v->name,"FM"); - v->rangelow=1400; /* 87.5 MHz */ - v->rangehigh=1728; /* 108.0 MHz */ - v->flags=0; - v->mode=0; - v->mode|=VIDEO_MODE_AUTO; - v->signal=sigstrength; - if(cadet_getstereo()==1) { - v->flags|=VIDEO_TUNER_STEREO_ON; - } - v->flags|=cadet_getrds(); - break; - case 1: - strcpy(v->name,"AM"); - v->rangelow=8320; /* 520 kHz */ - v->rangehigh=26400; /* 1650 kHz */ - v->flags=0; - v->flags|=VIDEO_TUNER_LOW; - v->mode=0; - v->mode|=VIDEO_MODE_AUTO; - v->signal=sigstrength; - break; + struct v4l2_tuner *t = arg; + memset(t,0,sizeof(*t)); + t->type = V4L2_TUNER_RADIO; + switch (t->index) + { + case 0: strcpy(t->name, "FM"); + t->capability = V4L2_TUNER_CAP_STEREO; + t->rangelow = 1400; /* 87.5 MHz */ + t->rangehigh = 1728; /* 108.0 MHz */ + t->rxsubchans=cadet_getstereo(); + switch (t->rxsubchans){ + case V4L2_TUNER_SUB_MONO: + t->audmode = V4L2_TUNER_MODE_MONO; + break; + case V4L2_TUNER_SUB_STEREO: + t->audmode = V4L2_TUNER_MODE_STEREO; + break; + default: ; + } + break; + case 1: strcpy(t->name, "AM"); + t->capability = V4L2_TUNER_CAP_LOW; + t->rangelow = 8320; /* 520 kHz */ + t->rangehigh = 26400; /* 1650 kHz */ + t->rxsubchans = V4L2_TUNER_SUB_MONO; + t->audmode = V4L2_TUNER_MODE_MONO; + break; + default: + return -EINVAL; } + + t->signal = sigstrength; /* We might need to modify scaling of this */ return 0; } - case VIDIOCSTUNER: + case VIDIOC_S_TUNER: { - struct video_tuner *v = arg; - if((v->tuner<0)||(v->tuner>1)) { + struct v4l2_tuner *t = arg; + if((t->index != 0)&&(t->index != 1)) return -EINVAL; - } - curtuner=v->tuner; + + curtuner = t->index; return 0; } - case VIDIOCGFREQ: + case VIDIOC_G_FREQUENCY: { - unsigned long *freq = arg; - *freq = cadet_getfreq(); + struct v4l2_frequency *f = arg; + memset(f,0,sizeof(*f)); + f->tuner = curtuner; + f->type = V4L2_TUNER_RADIO; + f->frequency = cadet_getfreq(); return 0; } - case VIDIOCSFREQ: + case VIDIOC_S_FREQUENCY: { - unsigned long *freq = arg; - if((curtuner==0)&&((*freq<1400)||(*freq>1728))) { + struct v4l2_frequency *f = arg; + if (f->type != V4L2_TUNER_RADIO){ + return -EINVAL; + } + if((curtuner==0)&&((f->frequency<1400)||(f->frequency>1728))) { return -EINVAL; } - if((curtuner==1)&&((*freq<8320)||(*freq>26400))) { + if((curtuner==1)&&((f->frequency<8320)||(f->frequency>26400))) { return -EINVAL; } - cadet_setfreq(*freq); + cadet_setfreq(f->frequency); return 0; } - case VIDIOCGAUDIO: + case VIDIOC_G_CTRL: { - struct video_audio *v = arg; - memset(v,0, sizeof(*v)); - v->flags=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; - if(cadet_getstereo()==0) { - v->mode=VIDEO_SOUND_MONO; - } else { - v->mode=VIDEO_SOUND_STEREO; + struct v4l2_control *c = arg; + switch (c->id){ + case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */ + c->value = (cadet_getvol() == 0); + break; + case V4L2_CID_AUDIO_VOLUME: + c->value = cadet_getvol(); + break; + default: + return -EINVAL; } - v->volume=cadet_getvol(); - v->step=0xffff; - strcpy(v->name, "Radio"); return 0; } - case VIDIOCSAUDIO: + case VIDIOC_S_CTRL: { - struct video_audio *v = arg; - if(v->audio) - return -EINVAL; - cadet_setvol(v->volume); - if(v->flags&VIDEO_AUDIO_MUTE) - cadet_setvol(0); - else - cadet_setvol(0xffff); + struct v4l2_control *c = arg; + switch (c->id){ + case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */ + if (c->value) cadet_setvol(0); + else cadet_setvol(0xffff); + break; + case V4L2_CID_AUDIO_VOLUME: + cadet_setvol(c->value); + break; + default: + return -EINVAL; + } return 0; } + default: return -ENOIOCTLCMD; } } -static int cadet_ioctl(struct inode *inode, struct file *file, +static int +cadet_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { return video_usercopy(inode, file, cmd, arg, cadet_do_ioctl); } -static int cadet_open(struct inode *inode, struct file *file) +static int +cadet_open(struct inode *inode, struct file *file) { - if(users) - return -EBUSY; users++; - init_waitqueue_head(&read_queue); + if (1 == users) init_waitqueue_head(&read_queue); return 0; } -static int cadet_release(struct inode *inode, struct file *file) +static int +cadet_release(struct inode *inode, struct file *file) { - del_timer_sync(&readtimer); - rdsstat=0; users--; + if (0 == users){ + del_timer_sync(&readtimer); + rdsstat=0; + } + return 0; +} + +static unsigned int +cadet_poll(struct file *file, struct poll_table_struct *wait) +{ + poll_wait(file,&read_queue,wait); + if(rdsin != rdsout) + return POLLIN | POLLRDNORM; return 0; } @@ -491,6 +513,7 @@ static struct file_operations cadet_fops = { .release = cadet_release, .read = cadet_read, .ioctl = cadet_ioctl, + .poll = cadet_poll, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -500,7 +523,6 @@ static struct video_device cadet_radio= .owner = THIS_MODULE, .name = "Cadet radio", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_CADET, .fops = &cadet_fops, }; diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c index 4c82956390c..cfab57d6bc4 100644 --- a/drivers/media/radio/radio-gemtek-pci.c +++ b/drivers/media/radio/radio-gemtek-pci.c @@ -34,6 +34,8 @@ * * TODO: multiple device support and portability were not tested * + * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> + * *************************************************************************** */ @@ -42,10 +44,32 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/pci.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <linux/errno.h> +#include <linux/version.h> /* for KERNEL_VERSION MACRO */ +#define RADIO_VERSION KERNEL_VERSION(0,0,2) + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 65535, + .default_value = 0xff, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + #include <asm/io.h> #include <asm/uaccess.h> @@ -183,91 +207,117 @@ static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file, struct gemtek_pci_card *card = dev->priv; switch ( cmd ) { - case VIDIOCGCAP: + case VIDIOC_QUERYCAP: { - struct video_capability *c = arg; + struct v4l2_capability *v = arg; + memset(v,0,sizeof(*v)); + strlcpy(v->driver, "radio-gemtek-pci", sizeof (v->driver)); + strlcpy(v->card, "GemTek PCI Radio", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; - memset(c,0,sizeof(*c)); - c->type = VID_TYPE_TUNER; - c->channels = 1; - c->audios = 1; - strcpy( c->name, "Gemtek PCI Radio" ); return 0; } - - case VIDIOCGTUNER: + case VIDIOC_G_TUNER: { - struct video_tuner *t = arg; + struct v4l2_tuner *v = arg; - if ( t->tuner ) + if (v->index > 0) return -EINVAL; - t->rangelow = GEMTEK_PCI_RANGE_LOW; - t->rangehigh = GEMTEK_PCI_RANGE_HIGH; - t->flags = VIDEO_TUNER_LOW; - t->mode = VIDEO_MODE_AUTO; - t->signal = 0xFFFF * gemtek_pci_getsignal( card ); - strcpy( t->name, "FM" ); + memset(v,0,sizeof(*v)); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + + v->rangelow = GEMTEK_PCI_RANGE_LOW; + v->rangehigh = GEMTEK_PCI_RANGE_HIGH; + v->rxsubchans =V4L2_TUNER_SUB_MONO; + v->capability=V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal=0xFFFF*gemtek_pci_getsignal( card ); + return 0; } - - case VIDIOCSTUNER: + case VIDIOC_S_TUNER: { - struct video_tuner *t = arg; - if ( t->tuner ) + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; - return 0; - } - case VIDIOCGFREQ: - { - unsigned long *freq = arg; - *freq = card->current_frequency; return 0; } - case VIDIOCSFREQ: + case VIDIOC_S_FREQUENCY: { - unsigned long *freq = arg; + struct v4l2_frequency *f = arg; - if ( (*freq < GEMTEK_PCI_RANGE_LOW) || - (*freq > GEMTEK_PCI_RANGE_HIGH) ) + if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) || + (f->frequency > GEMTEK_PCI_RANGE_HIGH) ) return -EINVAL; - gemtek_pci_setfrequency( card, *freq ); - card->current_frequency = *freq; - card->mute = FALSE; + gemtek_pci_setfrequency( card, f->frequency ); + card->current_frequency = f->frequency; + card->mute = FALSE; return 0; } - - case VIDIOCGAUDIO: + case VIDIOC_QUERYCTRL: { - struct video_audio *a = arg; - - memset( a, 0, sizeof( *a ) ); - a->flags |= VIDEO_AUDIO_MUTABLE; - a->volume = 1; - a->step = 65535; - strcpy( a->name, "Radio" ); - return 0; + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); + } + } + return -EINVAL; } - - case VIDIOCSAUDIO: + case VIDIOC_G_CTRL: { - struct video_audio *a = arg; - - if ( a->audio ) - return -EINVAL; - - if ( a->flags & VIDEO_AUDIO_MUTE ) - gemtek_pci_mute( card ); - else - gemtek_pci_unmute( card ); - return 0; + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=card->mute; + return (0); + case V4L2_CID_AUDIO_VOLUME: + if (card->mute) + ctrl->value=0; + else + ctrl->value=65535; + return (0); + } + return -EINVAL; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + gemtek_pci_mute(card); + } else { + gemtek_pci_unmute(card); + } + return (0); + case V4L2_CID_AUDIO_VOLUME: + if (ctrl->value) { + gemtek_pci_unmute(card); + } else { + gemtek_pci_mute(card); + } + return (0); + } + return -EINVAL; } - default: - return -ENOIOCTLCMD; + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + gemtek_pci_do_ioctl); } } @@ -309,7 +359,7 @@ static struct video_device vdev_template = { .owner = THIS_MODULE, .name = "Gemtek PCI Radio", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_GEMTEK, + .hardware = 0, .fops = &gemtek_pci_fops, }; diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 162f37d8bf9..730fe16126c 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -13,6 +13,7 @@ * * TODO: Allow for more than one of these foolish entities :-) * + * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> */ #include <linux/module.h> /* Modules */ @@ -21,11 +22,32 @@ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ -#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> -#include <linux/config.h> /* CONFIG_RADIO_GEMTEK_PORT */ #include <linux/spinlock.h> +#include <linux/version.h> /* for KERNEL_VERSION MACRO */ +#define RADIO_VERSION KERNEL_VERSION(0,0,2) + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 65535, + .default_value = 0xff, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + #ifndef CONFIG_RADIO_GEMTEK_PORT #define CONFIG_RADIO_GEMTEK_PORT -1 #endif @@ -147,77 +169,122 @@ static int gemtek_do_ioctl(struct inode *inode, struct file *file, switch(cmd) { - case VIDIOCGCAP: + case VIDIOC_QUERYCAP: { - struct video_capability *v = arg; + struct v4l2_capability *v = arg; memset(v,0,sizeof(*v)); - v->type=VID_TYPE_TUNER; - v->channels=1; - v->audios=1; - strcpy(v->name, "GemTek"); + strlcpy(v->driver, "radio-gemtek", sizeof (v->driver)); + strlcpy(v->card, "GemTek", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; } - case VIDIOCGTUNER: + case VIDIOC_G_TUNER: { - struct video_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; - v->rangelow=87*16000; - v->rangehigh=108*16000; - v->flags=VIDEO_TUNER_LOW; - v->mode=VIDEO_MODE_AUTO; - v->signal=0xFFFF*gemtek_getsigstr(rt); + + memset(v,0,sizeof(*v)); strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + + v->rangelow=(87*16000); + v->rangehigh=(108*16000); + v->rxsubchans =V4L2_TUNER_SUB_MONO; + v->capability=V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal=0xFFFF*gemtek_getsigstr(rt); + return 0; } - case VIDIOCSTUNER: + case VIDIOC_S_TUNER: { - struct video_tuner *v = arg; - if(v->tuner!=0) + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; - /* Only 1 tuner so no setting needed ! */ - return 0; - } - case VIDIOCGFREQ: - { - unsigned long *freq = arg; - *freq = rt->curfreq; + return 0; } - case VIDIOCSFREQ: + case VIDIOC_S_FREQUENCY: { - unsigned long *freq = arg; - rt->curfreq = *freq; + struct v4l2_frequency *f = arg; + + rt->curfreq = f->frequency; /* needs to be called twice in order for getsigstr to work */ gemtek_setfreq(rt, rt->curfreq); gemtek_setfreq(rt, rt->curfreq); return 0; } - case VIDIOCGAUDIO: - { - struct video_audio *v = arg; - memset(v,0, sizeof(*v)); - v->flags|=VIDEO_AUDIO_MUTABLE; - v->volume=1; - v->step=65535; - strcpy(v->name, "Radio"); - return 0; - } - case VIDIOCSAUDIO: + case VIDIOC_G_FREQUENCY: { - struct video_audio *v = arg; - if(v->audio) - return -EINVAL; + struct v4l2_frequency *f = arg; - if(v->flags&VIDEO_AUDIO_MUTE) - gemtek_mute(rt); - else - gemtek_unmute(rt); + f->type = V4L2_TUNER_RADIO; + f->frequency = rt->curfreq; return 0; } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); + } + } + return -EINVAL; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=rt->muted; + return (0); + case V4L2_CID_AUDIO_VOLUME: + if (rt->muted) + ctrl->value=0; + else + ctrl->value=65535; + return (0); + } + return -EINVAL; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + gemtek_mute(rt); + } else { + gemtek_unmute(rt); + } + return (0); + case V4L2_CID_AUDIO_VOLUME: + if (ctrl->value) { + gemtek_unmute(rt); + } else { + gemtek_mute(rt); + } + return (0); + } + return -EINVAL; + } default: - return -ENOIOCTLCMD; + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + gemtek_do_ioctl); } } @@ -243,7 +310,7 @@ static struct video_device gemtek_radio= .owner = THIS_MODULE, .name = "GemTek radio", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_GEMTEK, + .hardware = 0, .fops = &gemtek_fops, }; diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c index fcfa6c9fe22..e8ce5f75cf1 100644 --- a/drivers/media/radio/radio-maestro.c +++ b/drivers/media/radio/radio-maestro.c @@ -14,6 +14,8 @@ * version 0.04 * + code improvements * + VIDEO_TUNER_LOW is permanent + * + * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> */ #include <linux/module.h> @@ -25,10 +27,23 @@ #include <asm/uaccess.h> #include <linux/mutex.h> #include <linux/pci.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> -#define DRIVER_VERSION "0.05" +#include <linux/version.h> /* for KERNEL_VERSION MACRO */ +#define RADIO_VERSION KERNEL_VERSION(0,0,6) +#define DRIVER_VERSION "0.06" + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + } +}; #define GPIO_DATA 0x60 /* port offset from ESS_IO_BASE */ @@ -96,7 +111,7 @@ static struct file_operations maestro_fops = { static struct video_device maestro_radio = { .name = "Maestro radio", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_SF16MI, + .hardware = 0, .fops = &maestro_fops, }; @@ -130,7 +145,7 @@ static u32 radio_bits_get(struct radio_device *dev) rdata = inw(io); if(!l) dev->stereo = rdata & STR_MOST ? - 0 : VIDEO_TUNER_STEREO_ON; + 0 : 1; else if(rdata & STR_DATA) data++; @@ -183,72 +198,120 @@ static inline int radio_function(struct inode *inode, struct file *file, struct radio_device *card = video_get_drvdata(dev); switch (cmd) { - case VIDIOCGCAP: { - struct video_capability *v = arg; - memset(v, 0, sizeof(*v)); - strcpy(v->name, "Maestro radio"); - v->type = VID_TYPE_TUNER; - v->channels = v->audios = 1; - return 0; - } case VIDIOCGTUNER: { - struct video_tuner *v = arg; - if (v->tuner) - return -EINVAL; - (void)radio_bits_get(card); - v->flags = VIDEO_TUNER_LOW | card->stereo; - v->signal = card->tuned; - strcpy(v->name, "FM"); - v->rangelow = FREQ_LO; - v->rangehigh = FREQ_HI; - v->mode = VIDEO_MODE_AUTO; - return 0; - } case VIDIOCSTUNER: { - struct video_tuner *v = arg; - if (v->tuner != 0) - return -EINVAL; - return 0; - } case VIDIOCGFREQ: { - unsigned long *freq = arg; - *freq = BITS2FREQ(radio_bits_get(card)); - return 0; - } case VIDIOCSFREQ: { - unsigned long *freq = arg; - if (*freq < FREQ_LO || *freq > FREQ_HI) + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *v = arg; + memset(v,0,sizeof(*v)); + strlcpy(v->driver, "radio-maestro", sizeof (v->driver)); + strlcpy(v->card, "Maestro Radio", sizeof (v->card)); + sprintf(v->bus_info,"PCI"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + + return 0; + } + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *v = arg; + + if (v->index > 0) + return -EINVAL; + + (void)radio_bits_get(card); + + memset(v,0,sizeof(*v)); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + + v->rangelow = FREQ_LO; + v->rangehigh = FREQ_HI; + v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability=V4L2_TUNER_CAP_LOW; + if(card->stereo) + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal=card->tuned; + + return 0; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *v = arg; + + if (v->index > 0) + return -EINVAL; + + return 0; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) + return -EINVAL; + radio_bits_set(card, FREQ2BITS(f->frequency)); + + return 0; + } + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + f->type = V4L2_TUNER_RADIO; + f->frequency = BITS2FREQ(radio_bits_get(card)); + + return 0; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); + } + } return -EINVAL; - radio_bits_set(card, FREQ2BITS(*freq)); - return 0; - } case VIDIOCGAUDIO: { - struct video_audio *v = arg; - memset(v, 0, sizeof(*v)); - strcpy(v->name, "Radio"); - v->flags = VIDEO_AUDIO_MUTABLE | card->muted; - v->mode = VIDEO_SOUND_STEREO; - return 0; - } case VIDIOCSAUDIO: { - struct video_audio *v = arg; - if (v->audio) + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=card->muted; + return (0); + } return -EINVAL; + } + case VIDIOC_S_CTRL: { - register u16 io = card->io; - register u16 omask = inw(io + IO_MASK); - outw(~STR_WREN, io + IO_MASK); - outw((card->muted = v->flags & VIDEO_AUDIO_MUTE) ? - STR_WREN : 0, io); - udelay(4); - outw(omask, io + IO_MASK); - msleep(125); - return 0; + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + { + register u16 io = card->io; + register u16 omask = inw(io + IO_MASK); + outw(~STR_WREN, io + IO_MASK); + outw((card->muted = ctrl->value ) ? + STR_WREN : 0, io); + udelay(4); + outw(omask, io + IO_MASK); + msleep(125); + + return (0); + } + } + return -EINVAL; } - } case VIDIOCGUNIT: { - struct video_unit *v = arg; - v->video = VIDEO_NO_UNIT; - v->vbi = VIDEO_NO_UNIT; - v->radio = dev->minor; - v->audio = 0; - v->teletext = VIDEO_NO_UNIT; - return 0; - } default: - return -ENOIOCTLCMD; + default: + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + radio_function); } } @@ -275,7 +338,7 @@ static u16 __devinit radio_power_on(struct radio_device *dev) omask = inw(io + IO_MASK); odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN); outw(odir & ~STR_WREN, io + IO_DIR); - dev->muted = inw(io) & STR_WREN ? 0 : VIDEO_AUDIO_MUTE; + dev->muted = inw(io) & STR_WREN ? 0 : 1; outw(odir, io + IO_DIR); outw(~(STR_WREN | STR_CLK), io + IO_MASK); outw(dev->muted ? 0 : STR_WREN, io); diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c index f93d7afe730..c2eeae7a10d 100644 --- a/drivers/media/radio/radio-maxiradio.c +++ b/drivers/media/radio/radio-maxiradio.c @@ -20,13 +20,14 @@ * 0.75b * - better pci interface thanks to Francois Romieu <romieu@cogenit.fr> * - * 0.75 + * 0.75 Sun Feb 4 22:51:27 EET 2001 * - tiding up * - removed support for multiple devices as it didn't work anyway * * BUGS: * - card unmutes if you change frequency * + * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> */ @@ -40,11 +41,24 @@ #include <linux/mutex.h> #include <linux/pci.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> -/* version 0.75 Sun Feb 4 22:51:27 EET 2001 */ -#define DRIVER_VERSION "0.75" +#define DRIVER_VERSION "0.76" + +#include <linux/version.h> /* for KERNEL_VERSION MACRO */ +#define RADIO_VERSION KERNEL_VERSION(0,7,6) + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + } +}; #ifndef PCI_VENDOR_ID_GUILLEMOT #define PCI_VENDOR_ID_GUILLEMOT 0x5046 @@ -90,7 +104,6 @@ static struct video_device maxiradio_radio = .owner = THIS_MODULE, .name = "Maxi Radio FM2000 radio", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_SF16MI, .fops = &maxiradio_fops, }; @@ -176,89 +189,116 @@ static inline int radio_function(struct inode *inode, struct file *file, struct radio_device *card=dev->priv; switch(cmd) { - case VIDIOCGCAP: { - struct video_capability *v = arg; - + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *v = arg; memset(v,0,sizeof(*v)); - strcpy(v->name, "Maxi Radio FM2000 radio"); - v->type=VID_TYPE_TUNER; - v->channels=v->audios=1; + strlcpy(v->driver, "radio-maxiradio", sizeof (v->driver)); + strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; } - case VIDIOCGTUNER: { - struct video_tuner *v = arg; + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *v = arg; - if(v->tuner) + if (v->index > 0) return -EINVAL; - card->stereo = 0xffff * get_stereo(card->io); - card->tuned = 0xffff * get_tune(card->io); - - v->flags = VIDEO_TUNER_LOW | card->stereo; - v->signal = card->tuned; - + memset(v,0,sizeof(*v)); strcpy(v->name, "FM"); - - v->rangelow = FREQ_LO; - v->rangehigh = FREQ_HI; - v->mode = VIDEO_MODE_AUTO; + v->type = V4L2_TUNER_RADIO; + + v->rangelow=FREQ_LO; + v->rangehigh=FREQ_HI; + v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability=V4L2_TUNER_CAP_LOW; + if(get_stereo(card->io)) + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal=0xffff*get_tune(card->io); return 0; } - case VIDIOCSTUNER: { - struct video_tuner *v = arg; - if(v->tuner!=0) + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; - return 0; - } - case VIDIOCGFREQ: { - unsigned long *freq = arg; - *freq = card->freq; return 0; } - case VIDIOCSFREQ: { - unsigned long *freq = arg; + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *f = arg; - if (*freq < FREQ_LO || *freq > FREQ_HI) + if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) return -EINVAL; - card->freq = *freq; + + card->freq = f->frequency; set_freq(card->io, FREQ2BITS(card->freq)); msleep(125); return 0; } - case VIDIOCGAUDIO: { - struct video_audio *v = arg; - memset(v,0,sizeof(*v)); - strcpy(v->name, "Radio"); - v->flags=VIDEO_AUDIO_MUTABLE | card->muted; - v->mode=VIDEO_SOUND_STEREO; - return 0; - } + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *f = arg; - case VIDIOCSAUDIO: { - struct video_audio *v = arg; + f->type = V4L2_TUNER_RADIO; + f->frequency = card->freq; - if(v->audio) - return -EINVAL; - card->muted = v->flags & VIDEO_AUDIO_MUTE; - if(card->muted) - turn_power(card->io, 0); - else - set_freq(card->io, FREQ2BITS(card->freq)); return 0; } - case VIDIOCGUNIT: { - struct video_unit *v = arg; - - v->video=VIDEO_NO_UNIT; - v->vbi=VIDEO_NO_UNIT; - v->radio=dev->minor; - v->audio=0; - v->teletext=VIDEO_NO_UNIT; - return 0; + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); + } + } + return -EINVAL; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=card->muted; + return (0); + } + return -EINVAL; } - default: return -ENOIOCTLCMD; + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + card->muted = ctrl->value; + if(card->muted) + turn_power(card->io, 0); + else + set_freq(card->io, FREQ2BITS(card->freq)); + return 0; + } + return -EINVAL; + } + + default: + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + radio_function); + } } diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c index 5b68ac4c732..b9e98483e58 100644 --- a/drivers/media/radio/radio-rtrack2.c +++ b/drivers/media/radio/radio-rtrack2.c @@ -6,6 +6,7 @@ * * TODO: Allow for more than one of these foolish entities :-) * + * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> */ #include <linux/module.h> /* Modules */ @@ -14,11 +15,32 @@ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ -#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> -#include <linux/config.h> /* CONFIG_RADIO_RTRACK2_PORT */ #include <linux/spinlock.h> +#include <linux/version.h> /* for KERNEL_VERSION MACRO */ +#define RADIO_VERSION KERNEL_VERSION(0,0,2) + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 65535, + .default_value = 0xff, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + #ifndef CONFIG_RADIO_RTRACK2_PORT #define CONFIG_RADIO_RTRACK2_PORT -1 #endif @@ -115,75 +137,120 @@ static int rt_do_ioctl(struct inode *inode, struct file *file, switch(cmd) { - case VIDIOCGCAP: + case VIDIOC_QUERYCAP: { - struct video_capability *v = arg; + struct v4l2_capability *v = arg; memset(v,0,sizeof(*v)); - v->type=VID_TYPE_TUNER; - v->channels=1; - v->audios=1; - strcpy(v->name, "RadioTrack II"); + strlcpy(v->driver, "radio-rtrack2", sizeof (v->driver)); + strlcpy(v->card, "RadioTrack II", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; } - case VIDIOCGTUNER: + case VIDIOC_G_TUNER: { - struct video_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; - v->rangelow=88*16000; - v->rangehigh=108*16000; - v->flags=VIDEO_TUNER_LOW; - v->mode=VIDEO_MODE_AUTO; - v->signal=0xFFFF*rt_getsigstr(rt); + + memset(v,0,sizeof(*v)); strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + + v->rangelow=(88*16000); + v->rangehigh=(108*16000); + v->rxsubchans =V4L2_TUNER_SUB_MONO; + v->capability=V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal=0xFFFF*rt_getsigstr(rt); + return 0; } - case VIDIOCSTUNER: + case VIDIOC_S_TUNER: { - struct video_tuner *v = arg; - if(v->tuner!=0) + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; - /* Only 1 tuner so no setting needed ! */ + return 0; } - case VIDIOCGFREQ: + case VIDIOC_S_FREQUENCY: { - unsigned long *freq = arg; - *freq = rt->curfreq; + struct v4l2_frequency *f = arg; + + rt->curfreq = f->frequency; + rt_setfreq(rt, rt->curfreq); return 0; } - case VIDIOCSFREQ: + case VIDIOC_G_FREQUENCY: { - unsigned long *freq = arg; - rt->curfreq = *freq; - rt_setfreq(rt, rt->curfreq); + struct v4l2_frequency *f = arg; + + f->type = V4L2_TUNER_RADIO; + f->frequency = rt->curfreq; + return 0; } - case VIDIOCGAUDIO: + case VIDIOC_QUERYCTRL: { - struct video_audio *v = arg; - memset(v,0, sizeof(*v)); - v->flags|=VIDEO_AUDIO_MUTABLE; - v->volume=1; - v->step=65535; - strcpy(v->name, "Radio"); - return 0; + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); + } + } + return -EINVAL; } - case VIDIOCSAUDIO: + case VIDIOC_G_CTRL: { - struct video_audio *v = arg; - if(v->audio) - return -EINVAL; - - if(v->flags&VIDEO_AUDIO_MUTE) - rt_mute(rt); - else - rt_unmute(rt); - - return 0; + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=rt->muted; + return (0); + case V4L2_CID_AUDIO_VOLUME: + if (rt->muted) + ctrl->value=0; + else + ctrl->value=65535; + return (0); + } + return -EINVAL; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + rt_mute(rt); + } else { + rt_unmute(rt); + } + return (0); + case V4L2_CID_AUDIO_VOLUME: + if (ctrl->value) { + rt_unmute(rt); + } else { + rt_mute(rt); + } + return (0); + } + return -EINVAL; } default: - return -ENOIOCTLCMD; + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + rt_do_ioctl); } } @@ -209,7 +276,7 @@ static struct video_device rtrack2_radio= .owner = THIS_MODULE, .name = "RadioTrack II radio", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_RTRACK2, + .hardware = 0, .fops = &rtrack2_fops, }; diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index efee6e339d1..ecc854b4ba3 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -13,20 +13,35 @@ * No volume control - only mute/unmute - you have to use line volume * control on SB-part of SF16FMI * + * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> */ +#include <linux/version.h> #include <linux/kernel.h> /* __setup */ #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ #include <linux/ioport.h> /* request_region */ #include <linux/delay.h> /* udelay */ -#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> #include <linux/isapnp.h> #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/mutex.h> +#define RADIO_VERSION KERNEL_VERSION(0,0,2) + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + } +}; + struct fmi_device { int port; @@ -123,93 +138,122 @@ static int fmi_do_ioctl(struct inode *inode, struct file *file, switch(cmd) { - case VIDIOCGCAP: + case VIDIOC_QUERYCAP: { - struct video_capability *v = arg; + struct v4l2_capability *v = arg; memset(v,0,sizeof(*v)); - strcpy(v->name, "SF16-FMx radio"); - v->type=VID_TYPE_TUNER; - v->channels=1; - v->audios=1; + strlcpy(v->driver, "radio-sf16fmi", sizeof (v->driver)); + strlcpy(v->card, "SF16-FMx radio", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; } - case VIDIOCGTUNER: + case VIDIOC_G_TUNER: { - struct video_tuner *v = arg; + struct v4l2_tuner *v = arg; int mult; - if(v->tuner) /* Only 1 tuner */ + if (v->index > 0) return -EINVAL; + + memset(v,0,sizeof(*v)); strcpy(v->name, "FM"); - mult = (fmi->flags & VIDEO_TUNER_LOW) ? 1 : 1000; + v->type = V4L2_TUNER_RADIO; + + mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; v->rangelow = RSF16_MINFREQ/mult; v->rangehigh = RSF16_MAXFREQ/mult; - v->flags=fmi->flags; - v->mode=VIDEO_MODE_AUTO; + v->rxsubchans =V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; + v->capability=fmi->flags&V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_STEREO; v->signal = fmi_getsigstr(fmi); + return 0; } - case VIDIOCSTUNER: + case VIDIOC_S_TUNER: { - struct video_tuner *v = arg; - if(v->tuner!=0) + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; - fmi->flags = v->flags & VIDEO_TUNER_LOW; - /* Only 1 tuner so no setting needed ! */ - return 0; - } - case VIDIOCGFREQ: - { - unsigned long *freq = arg; - *freq = fmi->curfreq; - if (!(fmi->flags & VIDEO_TUNER_LOW)) - *freq /= 1000; + return 0; } - case VIDIOCSFREQ: + case VIDIOC_S_FREQUENCY: { - unsigned long *freq = arg; - if (!(fmi->flags & VIDEO_TUNER_LOW)) - *freq *= 1000; - if (*freq < RSF16_MINFREQ || *freq > RSF16_MAXFREQ ) + struct v4l2_frequency *f = arg; + + if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) + f->frequency *= 1000; + if (f->frequency < RSF16_MINFREQ || + f->frequency > RSF16_MAXFREQ ) return -EINVAL; /*rounding in steps of 800 to match th freq that will be used */ - fmi->curfreq = (*freq/800)*800; + fmi->curfreq = (f->frequency/800)*800; fmi_setfreq(fmi); + return 0; } - case VIDIOCGAUDIO: + case VIDIOC_G_FREQUENCY: { - struct video_audio *v = arg; - memset(v,0,sizeof(*v)); - v->flags=( (!fmi->curvol)*VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE); - strcpy(v->name, "Radio"); - v->mode=VIDEO_SOUND_STEREO; + struct v4l2_frequency *f = arg; + + f->type = V4L2_TUNER_RADIO; + f->frequency = fmi->curfreq; + if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) + f->frequency /= 1000; + return 0; } - case VIDIOCSAUDIO: + case VIDIOC_QUERYCTRL: { - struct video_audio *v = arg; - if(v->audio) - return -EINVAL; - fmi->curvol= v->flags&VIDEO_AUDIO_MUTE ? 0 : 1; - fmi->curvol ? - fmi_unmute(fmi->port) : fmi_mute(fmi->port); - return 0; + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); + } + } + return -EINVAL; } - case VIDIOCGUNIT: + case VIDIOC_G_CTRL: { - struct video_unit *v = arg; - v->video=VIDEO_NO_UNIT; - v->vbi=VIDEO_NO_UNIT; - v->radio=dev->minor; - v->audio=0; /* How do we find out this??? */ - v->teletext=VIDEO_NO_UNIT; - return 0; + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=fmi->curvol; + return (0); + } + return -EINVAL; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + { + if (ctrl->value) + fmi_mute(fmi->port); + else + fmi_unmute(fmi->port); + + fmi->curvol=ctrl->value; + return (0); + } + } + return -EINVAL; } default: - return -ENOIOCTLCMD; + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + fmi_do_ioctl); } } @@ -235,7 +279,7 @@ static struct video_device fmi_radio= .owner = THIS_MODULE, .name = "SF16FMx radio", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_SF16MI, + .hardware = 0, .fops = &fmi_fops, }; @@ -294,7 +338,7 @@ static int __init fmi_init(void) fmi_unit.port = io; fmi_unit.curvol = 0; fmi_unit.curfreq = 0; - fmi_unit.flags = VIDEO_TUNER_LOW; + fmi_unit.flags = V4L2_TUNER_CAP_LOW; fmi_radio.priv = &fmi_unit; mutex_init(&lock); diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index 3483b2c7bc9..4444dce864a 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -10,6 +10,8 @@ * For read stereo/mono you must wait 0.1 sec after set frequency and * card unmuted so I set frequency on unmute * Signal handling seem to work only on autoscanning (not implemented) + * + * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> */ #include <linux/module.h> /* Modules */ @@ -18,12 +20,34 @@ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ -#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> #include <linux/mutex.h> static struct mutex lock; +#include <linux/version.h> /* for KERNEL_VERSION MACRO */ +#define RADIO_VERSION KERNEL_VERSION(0,0,2) + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 1<<12, + .default_value = 0xff, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + #undef DEBUG //#define DEBUG 1 @@ -214,63 +238,65 @@ static int fmr2_do_ioctl(struct inode *inode, struct file *file, switch(cmd) { - case VIDIOCGCAP: + case VIDIOC_QUERYCAP: { - struct video_capability *v = arg; + struct v4l2_capability *v = arg; memset(v,0,sizeof(*v)); - strcpy(v->name, "SF16-FMR2 radio"); - v->type=VID_TYPE_TUNER; - v->channels=1; - v->audios=1; + strlcpy(v->driver, "radio-sf16fmr2", sizeof (v->driver)); + strlcpy(v->card, "SF16-FMR2 radio", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; } - case VIDIOCGTUNER: + case VIDIOC_G_TUNER: { - struct video_tuner *v = arg; + struct v4l2_tuner *v = arg; int mult; - if(v->tuner) /* Only 1 tuner */ + if (v->index > 0) return -EINVAL; + + memset(v,0,sizeof(*v)); strcpy(v->name, "FM"); - mult = (fmr2->flags & VIDEO_TUNER_LOW) ? 1 : 1000; + v->type = V4L2_TUNER_RADIO; + + mult = (fmr2->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; v->rangelow = RSF16_MINFREQ/mult; v->rangehigh = RSF16_MAXFREQ/mult; - v->flags = fmr2->flags | VIDEO_AUDIO_MUTABLE; - if (fmr2->mute) - v->flags |= VIDEO_AUDIO_MUTE; - v->mode=VIDEO_MODE_AUTO; + v->rxsubchans =V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; + v->capability=fmr2->flags&V4L2_TUNER_CAP_LOW; + + v->audmode = fmr2->stereo ? V4L2_TUNER_MODE_STEREO: + V4L2_TUNER_MODE_MONO; mutex_lock(&lock); v->signal = fmr2_getsigstr(fmr2); mutex_unlock(&lock); + return 0; } - case VIDIOCSTUNER: + case VIDIOC_S_TUNER: { - struct video_tuner *v = arg; - if (v->tuner!=0) + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; - fmr2->flags = v->flags & VIDEO_TUNER_LOW; - return 0; - } - case VIDIOCGFREQ: - { - unsigned long *freq = arg; - *freq = fmr2->curfreq; - if (!(fmr2->flags & VIDEO_TUNER_LOW)) - *freq /= 1000; + return 0; } - case VIDIOCSFREQ: + case VIDIOC_S_FREQUENCY: { - unsigned long *freq = arg; - if (!(fmr2->flags & VIDEO_TUNER_LOW)) - *freq *= 1000; - if ( *freq < RSF16_MINFREQ || *freq > RSF16_MAXFREQ ) + struct v4l2_frequency *f = arg; + + if (!(fmr2->flags & V4L2_TUNER_CAP_LOW)) + f->frequency *= 1000; + if (f->frequency < RSF16_MINFREQ || + f->frequency > RSF16_MAXFREQ ) return -EINVAL; - /* rounding in steps of 200 to match th freq - * that will be used - */ - fmr2->curfreq = (*freq/200)*200; + /*rounding in steps of 200 to match th freq + that will be used */ + fmr2->curfreq = (f->frequency/200)*200; /* set card freq (if not muted) */ if (fmr2->curvol && !fmr2->mute) @@ -279,40 +305,81 @@ static int fmr2_do_ioctl(struct inode *inode, struct file *file, fmr2_setfreq(fmr2); mutex_unlock(&lock); } + return 0; } - case VIDIOCGAUDIO: + case VIDIOC_G_FREQUENCY: { - struct video_audio *v = arg; - memset(v,0,sizeof(*v)); - /* !!! do not return VIDEO_AUDIO_MUTE */ - v->flags = VIDEO_AUDIO_MUTABLE; - strcpy(v->name, "Radio"); - /* get current stereo mode */ - v->mode = fmr2->stereo ? VIDEO_SOUND_STEREO: VIDEO_SOUND_MONO; - /* volume supported ? */ - if (fmr2->card_type == 11) - { - v->flags |= VIDEO_AUDIO_VOLUME; - v->step = 1 << 12; - v->volume = fmr2->curvol; - } - debug_print((KERN_DEBUG "Get flags %d vol %d\n", v->flags, v->volume)); + struct v4l2_frequency *f = arg; + + f->type = V4L2_TUNER_RADIO; + f->frequency = fmr2->curfreq; + if (!(fmr2->flags & V4L2_TUNER_CAP_LOW)) + f->frequency /= 1000; + return 0; } - case VIDIOCSAUDIO: + case VIDIOC_QUERYCTRL: { - struct video_audio *v = arg; - if(v->audio) - return -EINVAL; - debug_print((KERN_DEBUG "Set flags %d vol %d\n", v->flags, v->volume)); - /* set volume */ - if (v->flags & VIDEO_AUDIO_VOLUME) - fmr2->curvol = v->volume; /* !!! set with precision */ - if (fmr2->card_type != 11) fmr2->curvol = 65535; - fmr2->mute = 0; - if (v->flags & VIDEO_AUDIO_MUTE) - fmr2->mute = 1; + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if ((fmr2->card_type != 11) + && V4L2_CID_AUDIO_VOLUME) + radio_qctrl[i].step=65535; + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); + } + } + return -EINVAL; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=fmr2->mute; + return (0); + case V4L2_CID_AUDIO_VOLUME: + ctrl->value=fmr2->curvol; + return (0); + } + return -EINVAL; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + fmr2->mute=ctrl->value; + if (fmr2->card_type != 11) { + if (!fmr2->mute) { + fmr2->curvol = 65535; + } else { + fmr2->curvol = 0; + } + } + break; + case V4L2_CID_AUDIO_VOLUME: + fmr2->curvol = ctrl->value; + if (fmr2->card_type != 11) { + if (fmr2->curvol) { + fmr2->curvol = 65535; + fmr2->mute = 0; + } else { + fmr2->curvol = 0; + fmr2->mute = 1; + } + } + break; + default: + return -EINVAL; + } #ifdef DEBUG if (fmr2->curvol && !fmr2->mute) printk(KERN_DEBUG "unmute\n"); @@ -320,27 +387,18 @@ static int fmr2_do_ioctl(struct inode *inode, struct file *file, printk(KERN_DEBUG "mute\n"); #endif mutex_lock(&lock); - if (fmr2->curvol && !fmr2->mute) - { + if (fmr2->curvol && !fmr2->mute) { fmr2_setvolume(fmr2); fmr2_setfreq(fmr2); - } - else fmr2_mute(fmr2->port); + } else + fmr2_mute(fmr2->port); mutex_unlock(&lock); - return 0; - } - case VIDIOCGUNIT: - { - struct video_unit *v = arg; - v->video=VIDEO_NO_UNIT; - v->vbi=VIDEO_NO_UNIT; - v->radio=dev->minor; - v->audio=0; /* How do we find out this??? */ - v->teletext=VIDEO_NO_UNIT; - return 0; + return (0); } default: - return -ENOIOCTLCMD; + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + fmr2_do_ioctl); + } } @@ -366,7 +424,7 @@ static struct video_device fmr2_radio= .owner = THIS_MODULE, .name = "SF16FMR2 radio", . type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_SF16FMR2, + .hardware = 0, .fops = &fmr2_fops, }; @@ -377,7 +435,7 @@ static int __init fmr2_init(void) fmr2_unit.mute = 0; fmr2_unit.curfreq = 0; fmr2_unit.stereo = 1; - fmr2_unit.flags = VIDEO_TUNER_LOW; + fmr2_unit.flags = V4L2_TUNER_CAP_LOW; fmr2_unit.card_type = 0; fmr2_radio.priv = &fmr2_unit; @@ -396,7 +454,6 @@ static int __init fmr2_init(void) } printk(KERN_INFO "SF16FMR2 radio card driver at 0x%x.\n", io); - debug_print((KERN_DEBUG "Mute %d Low %d\n",VIDEO_AUDIO_MUTE,VIDEO_TUNER_LOW)); /* mute card - prevents noisy bootups */ mutex_lock(&lock); fmr2_mute(io); diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c index dfba4ae596c..f539491a0d7 100644 --- a/drivers/media/radio/radio-terratec.c +++ b/drivers/media/radio/radio-terratec.c @@ -21,6 +21,7 @@ * If you can help me out with that, please contact me!! * * + * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> */ #include <linux/module.h> /* Modules */ @@ -29,11 +30,32 @@ #include <linux/delay.h> /* udelay */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ -#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> -#include <linux/config.h> /* CONFIG_RADIO_TERRATEC_PORT */ #include <linux/spinlock.h> +#include <linux/version.h> /* for KERNEL_VERSION MACRO */ +#define RADIO_VERSION KERNEL_VERSION(0,0,2) + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0xff, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + #ifndef CONFIG_RADIO_TERRATEC_PORT #define CONFIG_RADIO_TERRATEC_PORT 0x590 #endif @@ -194,73 +216,117 @@ static int tt_do_ioctl(struct inode *inode, struct file *file, switch(cmd) { - case VIDIOCGCAP: + case VIDIOC_QUERYCAP: { - struct video_capability *v = arg; + struct v4l2_capability *v = arg; memset(v,0,sizeof(*v)); - v->type=VID_TYPE_TUNER; - v->channels=1; - v->audios=1; - strcpy(v->name, "ActiveRadio"); + strlcpy(v->driver, "radio-terratec", sizeof (v->driver)); + strlcpy(v->card, "ActiveRadio", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; } - case VIDIOCGTUNER: + case VIDIOC_G_TUNER: { - struct video_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; + + memset(v,0,sizeof(*v)); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow=(87*16000); v->rangehigh=(108*16000); - v->flags=VIDEO_TUNER_LOW; - v->mode=VIDEO_MODE_AUTO; - strcpy(v->name, "FM"); + v->rxsubchans =V4L2_TUNER_SUB_MONO; + v->capability=V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; v->signal=0xFFFF*tt_getsigstr(tt); + return 0; } - case VIDIOCSTUNER: + case VIDIOC_S_TUNER: { - struct video_tuner *v = arg; - if(v->tuner!=0) + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; - /* Only 1 tuner so no setting needed ! */ + return 0; } - case VIDIOCGFREQ: + case VIDIOC_S_FREQUENCY: { - unsigned long *freq = arg; - *freq = tt->curfreq; + struct v4l2_frequency *f = arg; + + tt->curfreq = f->frequency; + tt_setfreq(tt, tt->curfreq); return 0; } - case VIDIOCSFREQ: + case VIDIOC_G_FREQUENCY: { - unsigned long *freq = arg; - tt->curfreq = *freq; - tt_setfreq(tt, tt->curfreq); + struct v4l2_frequency *f = arg; + + f->type = V4L2_TUNER_RADIO; + f->frequency = tt->curfreq; + return 0; } - case VIDIOCGAUDIO: + case VIDIOC_QUERYCTRL: { - struct video_audio *v = arg; - memset(v,0, sizeof(*v)); - v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; - v->volume=tt->curvol * 6554; - v->step=6554; - strcpy(v->name, "Radio"); - return 0; + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); + } + } + return -EINVAL; } - case VIDIOCSAUDIO: + case VIDIOC_G_CTRL: { - struct video_audio *v = arg; - if(v->audio) - return -EINVAL; - if(v->flags&VIDEO_AUDIO_MUTE) - tt_mute(tt); - else - tt_setvol(tt,v->volume/6554); - return 0; + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (tt->muted) + ctrl->value=1; + else + ctrl->value=0; + return (0); + case V4L2_CID_AUDIO_VOLUME: + ctrl->value=tt->curvol * 6554; + return (0); + } + return -EINVAL; } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + tt_mute(tt); + } else { + tt_setvol(tt,tt->curvol); + } + return (0); + case V4L2_CID_AUDIO_VOLUME: + tt_setvol(tt,ctrl->value); + return (0); + } + return -EINVAL; + } + default: - return -ENOIOCTLCMD; + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + tt_do_ioctl); } } @@ -286,7 +352,7 @@ static struct video_device terratec_radio= .owner = THIS_MODULE, .name = "TerraTec ActiveRadio", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_TERRATEC, + .hardware = 0, .fops = &terratec_fops, }; diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c index 8da4badc22b..bb03ad5a203 100644 --- a/drivers/media/radio/radio-trust.c +++ b/drivers/media/radio/radio-trust.c @@ -12,7 +12,7 @@ * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) * William McGrath (wmcgrath@twilight.vtc.vsc.edu) * - * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/ + * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> */ #include <stdarg.h> @@ -21,9 +21,46 @@ #include <linux/ioport.h> #include <asm/io.h> #include <asm/uaccess.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> -#include <linux/config.h> /* CONFIG_RADIO_TRUST_PORT */ + +#include <linux/version.h> /* for KERNEL_VERSION MACRO */ +#define RADIO_VERSION KERNEL_VERSION(0,0,2) + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 2048, + .default_value = 65535, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_BASS, + .name = "Bass", + .minimum = 0, + .maximum = 65535, + .step = 4370, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_TREBLE, + .name = "Treble", + .minimum = 0, + .maximum = 65535, + .step = 4370, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + }, +}; /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ @@ -160,88 +197,125 @@ static int tr_do_ioctl(struct inode *inode, struct file *file, { switch(cmd) { - case VIDIOCGCAP: + case VIDIOC_QUERYCAP: { - struct video_capability *v = arg; - + struct v4l2_capability *v = arg; memset(v,0,sizeof(*v)); - v->type=VID_TYPE_TUNER; - v->channels=1; - v->audios=1; - strcpy(v->name, "Trust FM Radio"); + strlcpy(v->driver, "radio-trust", sizeof (v->driver)); + strlcpy(v->card, "Trust FM Radio", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; return 0; } - case VIDIOCGTUNER: + case VIDIOC_G_TUNER: { - struct video_tuner *v = arg; + struct v4l2_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ + if (v->index > 0) return -EINVAL; - v->rangelow = 87500 * 16; - v->rangehigh = 108000 * 16; - v->flags = VIDEO_TUNER_LOW; - v->mode = VIDEO_MODE_AUTO; + memset(v,0,sizeof(*v)); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; - v->signal = tr_getsigstr(); + v->rangelow=(87.5*16000); + v->rangehigh=(108*16000); + v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability=V4L2_TUNER_CAP_LOW; if(tr_getstereo()) - v->flags |= VIDEO_TUNER_STEREO_ON; - - strcpy(v->name, "FM"); + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal=tr_getsigstr(); return 0; } - case VIDIOCSTUNER: + case VIDIOC_S_TUNER: { - struct video_tuner *v = arg; - if(v->tuner != 0) + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; + return 0; } - case VIDIOCGFREQ: + case VIDIOC_S_FREQUENCY: { - unsigned long *freq = arg; - *freq = curfreq; + struct v4l2_frequency *f = arg; + + curfreq = f->frequency; + tr_setfreq(curfreq); return 0; } - case VIDIOCSFREQ: + case VIDIOC_G_FREQUENCY: { - unsigned long *freq = arg; - tr_setfreq(*freq); + struct v4l2_frequency *f = arg; + + f->type = V4L2_TUNER_RADIO; + f->frequency = curfreq; + return 0; } - case VIDIOCGAUDIO: + case VIDIOC_QUERYCTRL: { - struct video_audio *v = arg; - - memset(v,0, sizeof(*v)); - v->flags = VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME | - VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE; - v->mode = curstereo? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO; - v->volume = curvol * 2048; - v->step = 2048; - v->bass = curbass * 4370; - v->treble = curtreble * 4370; - - strcpy(v->name, "Trust FM Radio"); - return 0; + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); + } + } + return -EINVAL; } - case VIDIOCSAUDIO: + case VIDIOC_G_CTRL: { - struct video_audio *v = arg; - - if(v->audio) - return -EINVAL; - tr_setvol(v->volume); - tr_setbass(v->bass); - tr_settreble(v->treble); - tr_setstereo(v->mode & VIDEO_SOUND_STEREO); - tr_setmute(v->flags & VIDEO_AUDIO_MUTE); - return 0; + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=curmute; + return (0); + case V4L2_CID_AUDIO_VOLUME: + ctrl->value= curvol * 2048; + return (0); + case V4L2_CID_AUDIO_BASS: + ctrl->value= curbass * 4370; + return (0); + case V4L2_CID_AUDIO_TREBLE: + ctrl->value= curtreble * 4370; + return (0); + } + return -EINVAL; } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + tr_setmute(ctrl->value); + return 0; + case V4L2_CID_AUDIO_VOLUME: + tr_setvol(ctrl->value); + return 0; + case V4L2_CID_AUDIO_BASS: + tr_setbass(ctrl->value); + return 0; + case V4L2_CID_AUDIO_TREBLE: + tr_settreble(ctrl->value); + return (0); + } + return -EINVAL; + } + default: - return -ENOIOCTLCMD; + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + tr_do_ioctl); } } @@ -265,7 +339,7 @@ static struct video_device trust_radio= .owner = THIS_MODULE, .name = "Trust FM Radio", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_TRUST, + .hardware = 0, .fops = &trust_fops, }; diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c index edd01228866..4a72b4d4e62 100644 --- a/drivers/media/radio/radio-typhoon.c +++ b/drivers/media/radio/radio-typhoon.c @@ -27,6 +27,8 @@ * value where I do expect just noise and turn the speaker volume down. * The frequency change is necessary since the card never seems to be * completely silent. + * + * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> */ #include <linux/module.h> /* Modules */ @@ -35,11 +37,32 @@ #include <linux/proc_fs.h> /* radio card status report */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ -#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> -#include <linux/config.h> /* CONFIG_RADIO_TYPHOON_* */ -#define BANNER "Typhoon Radio Card driver v0.1\n" +#include <linux/version.h> /* for KERNEL_VERSION MACRO */ +#define RADIO_VERSION KERNEL_VERSION(0,1,1) +#define BANNER "Typhoon Radio Card driver v0.1.1\n" + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 1<<14, + .default_value = 0xff, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + #ifndef CONFIG_RADIO_TYPHOON_PORT #define CONFIG_RADIO_TYPHOON_PORT -1 @@ -171,76 +194,114 @@ static int typhoon_do_ioctl(struct inode *inode, struct file *file, struct typhoon_device *typhoon = dev->priv; switch (cmd) { - case VIDIOCGCAP: + case VIDIOC_QUERYCAP: { - struct video_capability *v = arg; + struct v4l2_capability *v = arg; memset(v,0,sizeof(*v)); - v->type = VID_TYPE_TUNER; - v->channels = 1; - v->audios = 1; - strcpy(v->name, "Typhoon Radio"); + strlcpy(v->driver, "radio-typhoon", sizeof (v->driver)); + strlcpy(v->card, "Typhoon Radio", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; } - case VIDIOCGTUNER: + case VIDIOC_G_TUNER: { - struct video_tuner *v = arg; - if (v->tuner) /* Only 1 tuner */ + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; - v->rangelow = 875 * 1600; - v->rangehigh = 1080 * 1600; - v->flags = VIDEO_TUNER_LOW; - v->mode = VIDEO_MODE_AUTO; - v->signal = 0xFFFF; /* We can't get the signal strength */ + + memset(v,0,sizeof(*v)); strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + + v->rangelow=(87.5*16000); + v->rangehigh=(108*16000); + v->rxsubchans =V4L2_TUNER_SUB_MONO; + v->capability=V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xFFFF; /* We can't get the signal strength */ + return 0; } - case VIDIOCSTUNER: + case VIDIOC_S_TUNER: { - struct video_tuner *v = arg; - if (v->tuner != 0) + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; - /* Only 1 tuner so no setting needed ! */ + return 0; } - case VIDIOCGFREQ: - { - unsigned long *freq = arg; - *freq = typhoon->curfreq; - return 0; - } - case VIDIOCSFREQ: - { - unsigned long *freq = arg; - typhoon->curfreq = *freq; - typhoon_setfreq(typhoon, typhoon->curfreq); - return 0; - } - case VIDIOCGAUDIO: + case VIDIOC_S_FREQUENCY: { - struct video_audio *v = arg; - memset(v, 0, sizeof(*v)); - v->flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME; - v->mode |= VIDEO_SOUND_MONO; - v->volume = typhoon->curvol; - v->step = 1 << 14; - strcpy(v->name, "Typhoon Radio"); + struct v4l2_frequency *f = arg; + + typhoon->curfreq = f->frequency; + typhoon_setfreq(typhoon, typhoon->curfreq); return 0; } - case VIDIOCSAUDIO: + case VIDIOC_G_FREQUENCY: { - struct video_audio *v = arg; - if (v->audio) - return -EINVAL; - if (v->flags & VIDEO_AUDIO_MUTE) - typhoon_mute(typhoon); - else - typhoon_unmute(typhoon); - if (v->flags & VIDEO_AUDIO_VOLUME) - typhoon_setvol(typhoon, v->volume); + struct v4l2_frequency *f = arg; + + f->type = V4L2_TUNER_RADIO; + f->frequency = typhoon->curfreq; + return 0; } - default: - return -ENOIOCTLCMD; + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); + } + } + return -EINVAL; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=typhoon->muted; + return (0); + case V4L2_CID_AUDIO_VOLUME: + ctrl->value=typhoon->curvol; + return (0); + } + return -EINVAL; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + typhoon_mute(typhoon); + } else { + typhoon_unmute(typhoon); + } + return (0); + case V4L2_CID_AUDIO_VOLUME: + typhoon_setvol(typhoon, ctrl->value); + return (0); + } + return -EINVAL; + } + + default: + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + typhoon_do_ioctl); } } @@ -271,7 +332,7 @@ static struct video_device typhoon_radio = .owner = THIS_MODULE, .name = "Typhoon Radio", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_TYPHOON, + .hardware = 0, .fops = &typhoon_fops, }; diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c index 59b86a6b4b0..671fe1b1e5b 100644 --- a/drivers/media/radio/radio-zoltrix.c +++ b/drivers/media/radio/radio-zoltrix.c @@ -24,6 +24,9 @@ * - Added unmute function * - Reworked ioctl functions * 2002-07-15 - Fix Stereo typo + * + * 2006-07-24 - Converted to V4L2 API + * by Mauro Carvalho Chehab <mchehab@infradead.org> */ #include <linux/module.h> /* Modules */ @@ -32,9 +35,30 @@ #include <linux/delay.h> /* udelay, msleep */ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ -#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> -#include <linux/config.h> /* CONFIG_RADIO_ZOLTRIX_PORT */ + +#include <linux/version.h> /* for KERNEL_VERSION MACRO */ +#define RADIO_VERSION KERNEL_VERSION(0,0,2) + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 4096, + .default_value = 0xff, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; #ifndef CONFIG_RADIO_ZOLTRIX_PORT #define CONFIG_RADIO_ZOLTRIX_PORT -1 @@ -213,78 +237,116 @@ static int zol_do_ioctl(struct inode *inode, struct file *file, struct zol_device *zol = dev->priv; switch (cmd) { - case VIDIOCGCAP: + case VIDIOC_QUERYCAP: { - struct video_capability *v = arg; - + struct v4l2_capability *v = arg; memset(v,0,sizeof(*v)); - v->type = VID_TYPE_TUNER; - v->channels = 1 + zol->stereo; - v->audios = 1; - strcpy(v->name, "Zoltrix Radio"); + strlcpy(v->driver, "radio-zoltrix", sizeof (v->driver)); + strlcpy(v->card, "Zoltrix Radio", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; } - case VIDIOCGTUNER: + case VIDIOC_G_TUNER: { - struct video_tuner *v = arg; - if (v->tuner) + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; + + memset(v,0,sizeof(*v)); strcpy(v->name, "FM"); - v->rangelow = (int) (88.0 * 16000); - v->rangehigh = (int) (108.0 * 16000); - v->flags = zol_is_stereo(zol) - ? VIDEO_TUNER_STEREO_ON : 0; - v->flags |= VIDEO_TUNER_LOW; - v->mode = VIDEO_MODE_AUTO; - v->signal = 0xFFFF * zol_getsigstr(zol); + v->type = V4L2_TUNER_RADIO; + + v->rangelow=(88*16000); + v->rangehigh=(108*16000); + v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability=V4L2_TUNER_CAP_LOW; + if(zol_is_stereo(zol)) + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal=0xFFFF*zol_getsigstr(zol); + return 0; } - case VIDIOCSTUNER: + case VIDIOC_S_TUNER: { - struct video_tuner *v = arg; - if (v->tuner != 0) + struct v4l2_tuner *v = arg; + + if (v->index > 0) return -EINVAL; - /* Only 1 tuner so no setting needed ! */ + return 0; } - case VIDIOCGFREQ: - { - unsigned long *freq = arg; - *freq = zol->curfreq; - return 0; - } - case VIDIOCSFREQ: - { - unsigned long *freq = arg; - zol->curfreq = *freq; - zol_setfreq(zol, zol->curfreq); - return 0; - } - case VIDIOCGAUDIO: + case VIDIOC_S_FREQUENCY: { - struct video_audio *v = arg; - memset(v, 0, sizeof(*v)); - v->flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME; - v->mode |= zol_is_stereo(zol) - ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO; - v->volume = zol->curvol * 4096; - v->step = 4096; - strcpy(v->name, "Zoltrix Radio"); + struct v4l2_frequency *f = arg; + + zol->curfreq = f->frequency; + zol_setfreq(zol, zol->curfreq); return 0; } - case VIDIOCSAUDIO: + case VIDIOC_G_FREQUENCY: { - struct video_audio *v = arg; - if (v->audio) - return -EINVAL; + struct v4l2_frequency *f = arg; - if (v->flags & VIDEO_AUDIO_MUTE) - zol_mute(zol); - else { - zol_unmute(zol); - zol_setvol(zol, v->volume / 4096); - } + f->type = V4L2_TUNER_RADIO; + f->frequency = zol->curfreq; + return 0; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); + } + } + return -EINVAL; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=zol->muted; + return (0); + case V4L2_CID_AUDIO_VOLUME: + ctrl->value=zol->curvol * 4096; + return (0); + } + return -EINVAL; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl= arg; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + zol_mute(zol); + } else { + zol_unmute(zol); + zol_setvol(zol,zol->curvol); + } + return (0); + case V4L2_CID_AUDIO_VOLUME: + zol_setvol(zol,ctrl->value/4096); + return (0); + } + zol->stereo = 1; + zol_setfreq(zol, zol->curfreq); +#if 0 +/* FIXME: Implement stereo/mono switch on V4L2 */ if (v->mode & VIDEO_SOUND_STEREO) { zol->stereo = 1; zol_setfreq(zol, zol->curfreq); @@ -293,10 +355,13 @@ static int zol_do_ioctl(struct inode *inode, struct file *file, zol->stereo = 0; zol_setfreq(zol, zol->curfreq); } - return 0; +#endif + return -EINVAL; } - default: - return -ENOIOCTLCMD; + + default: + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + zol_do_ioctl); } } @@ -323,7 +388,7 @@ static struct video_device zoltrix_radio = .owner = THIS_MODULE, .name = "Zoltrix Radio Plus", .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_ZOLTRIX, + .hardware = 0, .fops = &zoltrix_fops, }; diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 94d078b77ba..d1183c93922 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -16,6 +16,320 @@ config VIDEO_ADV_DEBUG V4L devices. In doubt, say N. +config VIDEO_HELPER_CHIPS_AUTO + bool "Autoselect pertinent encoders/decoders and other helper chips" + default y + ---help--- + Most video cards may require additional modules to encode or + decode audio/video standards. This option will autoselect + all pertinent modules to each selected video module. + + Unselect this only if you know exaclty what you are doing, since + it may break support on some boards. + + In doubt, say Y. + +# +# Encoder / Decoder module configuration +# + +menu "Encoders/decoders and other helper chips" + depends on VIDEO_DEV && !VIDEO_HELPER_CHIPS_AUTO + +comment "Audio Decoders" + +config VIDEO_TVAUDIO + tristate "Simple audio decoder chips" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for several audio decoder chips found on some bt8xx boards: + Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300, + tea6320, tea6420, tda8425, ta8874z. + Microchip: pic16c54 based design on ProVideo PV951 board. + + To compile this driver as a module, choose M here: the + module will be called tvaudio. + +config VIDEO_TDA7432 + tristate "Philips TDA7432 audio processor chip" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for tda7432 audio decoder chip found on some bt8xx boards. + + To compile this driver as a module, choose M here: the + module will be called tda7432. + +config VIDEO_TDA9840 + tristate "Philips TDA9840 audio processor chip" + depends on VIDEO_DEV && I2C + ---help--- + Support for tda9840 audio decoder chip found on some Zoran boards. + + To compile this driver as a module, choose M here: the + module will be called tda9840. + +config VIDEO_TDA9875 + tristate "Philips TDA9875 audio processor chip" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for tda9875 audio decoder chip found on some bt8xx boards. + + To compile this driver as a module, choose M here: the + module will be called tda9875. + +config VIDEO_TEA6415C + tristate "Philips TEA6415C audio processor chip" + depends on VIDEO_DEV && I2C + ---help--- + Support for tea6415c audio decoder chip found on some bt8xx boards. + + To compile this driver as a module, choose M here: the + module will be called tea6415c. + +config VIDEO_TEA6420 + tristate "Philips TEA6420 audio processor chip" + depends on VIDEO_DEV && I2C + ---help--- + Support for tea6420 audio decoder chip found on some bt8xx boards. + + To compile this driver as a module, choose M here: the + module will be called tea6420. + +config VIDEO_MSP3400 + tristate "Micronas MSP34xx audio decoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Micronas MSP34xx series of audio decoders. + + To compile this driver as a module, choose M here: the + module will be called msp3400. + +config VIDEO_CS53L32A + tristate "Cirrus Logic CS53L32A audio ADC" + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + ---help--- + Support for the Cirrus Logic CS53L32A low voltage + stereo A/D converter. + + To compile this driver as a module, choose M here: the + module will be called cs53l32a. + +config VIDEO_TLV320AIC23B + tristate "Texas Instruments TLV320AIC23B audio codec" + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + ---help--- + Support for the Texas Instruments TLV320AIC23B audio codec. + + To compile this driver as a module, choose M here: the + module will be called tlv320aic23b. + +config VIDEO_WM8775 + tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + ---help--- + Support for the Wolfson Microelectronics WM8775 high + performance stereo A/D Converter with a 4 channel input mixer. + + To compile this driver as a module, choose M here: the + module will be called wm8775. + +config VIDEO_WM8739 + tristate "Wolfson Microelectronics WM8739 stereo audio ADC" + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + ---help--- + Support for the Wolfson Microelectronics WM8739 + stereo A/D Converter. + + To compile this driver as a module, choose M here: the + module will be called wm8739. + +comment "MPEG video encoders" + +config VIDEO_CX2341X + tristate "Conexant CX2341x MPEG encoders" + depends on VIDEO_V4L2 && EXPERIMENTAL + ---help--- + Support for the Conexant CX23416 MPEG encoders + and CX23415 MPEG encoder/decoders. + + This module currently supports the encoding functions only. + + To compile this driver as a module, choose M here: the + module will be called cx2341x. + +source "drivers/media/video/cx25840/Kconfig" + +comment "Video encoders" + +config VIDEO_SAA7127 + tristate "Philips SAA7127/9 digital video encoders" + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + ---help--- + Support for the Philips SAA7127/9 digital video encoders. + + To compile this driver as a module, choose M here: the + module will be called saa7127. + +config VIDEO_SAA7185 + tristate "Philips SAA7185 video encoder" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for the Philips SAA7185 video encoder. + + To compile this driver as a module, choose M here: the + module will be called saa7185. + +config VIDEO_ADV7170 + tristate "Analog Devices ADV7170 video encoder driver" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for the Analog Devices ADV7170 video encoder driver + + To compile this driver as a module, choose M here: the + module will be called adv7170. + +config VIDEO_ADV7175 + tristate "Analog Devices ADV7175 video encoder driver" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for the Analog Devices ADV7175 video encoder driver + + To compile this driver as a module, choose M here: the + module will be called adv7175. + +comment "Video decoders" + +config VIDEO_BT819 + tristate "BT819A VideoStream Decoder" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for BT819A video decoder. + + To compile this driver as a module, choose M here: the + module will be called bt819. + +config VIDEO_BT856 + tristate "BT856 VideoStream Decoder" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for BT856 video decoder. + + To compile this driver as a module, choose M here: the + module will be called bt856. + +config VIDEO_BT866 + tristate "BT866 VideoStream Decoder" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for BT866 video decoder. + + To compile this driver as a module, choose M here: the + module will be called bt866. + +config VIDEO_KS0127 + tristate "KS0127 video decoder" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for KS0127 video decoder. + + This chip is used on AverMedia AVS6EYES Zoran-based MJPEG + cards. + + To compile this driver as a module, choose M here: the + module will be called ks0127. + +config VIDEO_SAA7110 + tristate "Philips SAA7110 video decoder" + depends on VIDEO_V4L1 + ---help--- + Support for the Philips SAA7110 video decoders. + + To compile this driver as a module, choose M here: the + module will be called saa7110. + +config VIDEO_SAA7111 + tristate "Philips SAA7111 video decoder" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for the Philips SAA711 video decoder. + + To compile this driver as a module, choose M here: the + module will be called saa7111. + +config VIDEO_SAA7114 + tristate "Philips SAA7114 video decoder" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for the Philips SAA7114 video decoder. This driver + is used only on Zoran driver and should be moved soon to + SAA711x module. + + To compile this driver as a module, choose M here: the + module will be called saa7114. + +config VIDEO_SAA711X + tristate "Philips SAA7113/4/5 video decoders" + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + ---help--- + Support for the Philips SAA7113/4/5 video decoders. + + To compile this driver as a module, choose M here: the + module will be called saa7115. + +config VIDEO_SAA7191 + tristate "Philips SAA7191 video decoder" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for the Philips SAA7191 video decoder. + + To compile this driver as a module, choose M here: the + module will be called saa7191. + +config VIDEO_TVP5150 + tristate "Texas Instruments TVP5150 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Texas Instruments TVP5150 video decoder. + + To compile this driver as a module, choose M here: the + module will be called tvp5150. + +config VIDEO_VPX3220 + tristate "vpx3220a, vpx3216b & vpx3214c video decoder driver" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for VPX322x video decoders. + + To compile this driver as a module, choose M here: the + module will be called vpx3220. + +comment "Video improvement chips" + +config VIDEO_UPD64031A + tristate "NEC Electronics uPD64031A Ghost Reduction" + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + ---help--- + Support for the NEC Electronics uPD64031A Ghost Reduction + video chip. It is most often found in NTSC TV cards made for + Japan and is used to reduce the 'ghosting' effect that can + be present in analog TV broadcasts. + + To compile this driver as a module, choose M here: the + module will be called upd64031a. + +config VIDEO_UPD64083 + tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + ---help--- + Support for the NEC Electronics uPD64083 3-Dimensional Y/C + separation video chip. It is used to improve the quality of + the colors of a composite signal. + + To compile this driver as a module, choose M here: the + module will be called upd64083. + +endmenu # encoder / decoder chips + config VIDEO_VIVI tristate "Virtual Video Driver" depends on VIDEO_V4L2 && !SPARC32 && !SPARC64 @@ -135,7 +449,7 @@ source "drivers/media/video/cpia2/Kconfig" config VIDEO_SAA5246A tristate "SAA5246A, SAA5281 Teletext processor" - depends on I2C && VIDEO_V4L1 + depends on I2C && VIDEO_V4L2 help Support for I2C bus based teletext using the SAA5246A or SAA5281 chip. Useful only if you live in Europe. @@ -145,7 +459,7 @@ config VIDEO_SAA5246A config VIDEO_SAA5249 tristate "SAA5249 Teletext processor" - depends on VIDEO_DEV && I2C && VIDEO_V4L1 + depends on VIDEO_DEV && I2C && VIDEO_V4L2 help Support for I2C bus based teletext using the SAA5249 chip. At the moment this is only useful on some European WinTV cards. @@ -162,8 +476,9 @@ config TUNER_3036 config VIDEO_VINO tristate "SGI Vino Video For Linux (EXPERIMENTAL)" - depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L1 + depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L2 select I2C_ALGO_SGI + select VIDEO_SAA7191 if VIDEO_HELPER_CHIPS_AUTO help Say Y here to build in support for the Vino video input system found on SGI Indy machines. @@ -176,6 +491,9 @@ config VIDEO_STRADIS driver for PCI. There is a product page at <http://www.stradis.com/>. +config VIDEO_ZORAN_ZR36060 + tristate + config VIDEO_ZORAN tristate "Zoran ZR36057/36067 Video For Linux" depends on PCI && I2C_ALGOBIT && VIDEO_V4L1 && !PPC64 @@ -192,12 +510,18 @@ config VIDEO_ZORAN config VIDEO_ZORAN_BUZ tristate "Iomega Buz support" depends on VIDEO_ZORAN + select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA7185 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_ZORAN_ZR36060 help Support for the Iomega Buz MJPEG capture/playback card. config VIDEO_ZORAN_DC10 tristate "Pinnacle/Miro DC10(+) support" depends on VIDEO_ZORAN + select VIDEO_SAA7110 + select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_ZORAN_ZR36060 help Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback card. @@ -205,6 +529,8 @@ config VIDEO_ZORAN_DC10 config VIDEO_ZORAN_DC30 tristate "Pinnacle/Miro DC30(+) support" depends on VIDEO_ZORAN + select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_VPX3220 if VIDEO_HELPER_CHIPS_AUTO help Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback card. This also supports really old DC10 cards based on the @@ -213,6 +539,9 @@ config VIDEO_ZORAN_DC30 config VIDEO_ZORAN_LML33 tristate "Linux Media Labs LML33 support" depends on VIDEO_ZORAN + select VIDEO_BT819 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_ZORAN_ZR36060 help Support for the Linux Media Labs LML33 MJPEG capture/playback card. @@ -220,6 +549,9 @@ config VIDEO_ZORAN_LML33 config VIDEO_ZORAN_LML33R10 tristate "Linux Media Labs LML33R10 support" depends on VIDEO_ZORAN + select VIDEO_SAA7114 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_ADV7170 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_ZORAN_ZR36060 help support for the Linux Media Labs LML33R10 MJPEG capture/playback card. @@ -227,6 +559,9 @@ config VIDEO_ZORAN_LML33R10 config VIDEO_ZORAN_AVS6EYES tristate "AverMedia 6 Eyes support (EXPERIMENTAL)" depends on VIDEO_ZORAN && EXPERIMENTAL && VIDEO_V4L1 + select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_KS0127 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_ZORAN_ZR36060 help Support for the AverMedia 6 Eyes video surveillance card. @@ -263,6 +598,10 @@ config VIDEO_MXB depends on PCI && VIDEO_V4L1 && I2C select VIDEO_SAA7146_VV select VIDEO_TUNER + select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_TEA6420 if VIDEO_HELPER_CHIPS_AUTO ---help--- This is a video4linux driver for the 'Multimedia eXtension Board' TV card by Siemens-Nixdorf. @@ -274,7 +613,7 @@ config VIDEO_DPC tristate "Philips-Semiconductors 'dpc7146 demonstration board'" depends on PCI && VIDEO_V4L1 && I2C select VIDEO_SAA7146_VV - select VIDEO_V4L2 + select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO ---help--- This is a video4linux driver for the 'dpc7146 demonstration board' by Philips-Semiconductors. It's the reference design @@ -287,9 +626,8 @@ config VIDEO_DPC config VIDEO_HEXIUM_ORION tristate "Hexium HV-PCI6 and Orion frame grabber" - depends on PCI && VIDEO_V4L1 && I2C + depends on PCI && VIDEO_V4L2 && I2C select VIDEO_SAA7146_VV - select VIDEO_V4L2 ---help--- This is a video4linux driver for the Hexium HV-PCI6 and Orion frame grabber cards by Hexium. @@ -299,9 +637,8 @@ config VIDEO_HEXIUM_ORION config VIDEO_HEXIUM_GEMINI tristate "Hexium Gemini frame grabber" - depends on PCI && VIDEO_V4L1 && I2C + depends on PCI && VIDEO_V4L2 && I2C select VIDEO_SAA7146_VV - select VIDEO_V4L2 ---help--- This is a video4linux driver for the Hexium Gemini frame grabber card by Hexium. Please note that the Gemini Dual @@ -320,123 +657,16 @@ config VIDEO_M32R_AR camera module. config VIDEO_M32R_AR_M64278 - tristate "Use Colour AR module M64278(VGA)" - depends on VIDEO_M32R_AR && PLAT_M32700UT - ---help--- - Say Y here to use the Renesas M64278E-800 camera module, - which supports VGA(640x480 pixcels) size of images. - -# -# Encoder / Decoder module configuration -# - -menu "Encoders and Decoders" - depends on VIDEO_DEV - -config VIDEO_MSP3400 - tristate "Micronas MSP34xx audio decoders" - depends on VIDEO_DEV && I2C - ---help--- - Support for the Micronas MSP34xx series of audio decoders. - - To compile this driver as a module, choose M here: the - module will be called msp3400. - -config VIDEO_CS53L32A - tristate "Cirrus Logic CS53L32A audio ADC" - depends on VIDEO_DEV && I2C && EXPERIMENTAL - ---help--- - Support for the Cirrus Logic CS53L32A low voltage - stereo A/D converter. - - To compile this driver as a module, choose M here: the - module will be called cs53l32a. - -config VIDEO_TLV320AIC23B - tristate "Texas Instruments TLV320AIC23B audio codec" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + tristate "AR device with color module M64278(VGA)" + depends on PLAT_M32700UT + select VIDEO_M32R_AR ---help--- - Support for the Texas Instruments TLV320AIC23B audio codec. - - To compile this driver as a module, choose M here: the - module will be called tlv320aic23b. - -config VIDEO_WM8775 - tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" - depends on VIDEO_DEV && I2C && EXPERIMENTAL - ---help--- - Support for the Wolfson Microelectronics WM8775 high - performance stereo A/D Converter with a 4 channel input mixer. + This is a video4linux driver for the Renesas AR (Artificial + Retina) with M64278E-800 camera module. + This module supports VGA(640x480 pixels) resolutions. To compile this driver as a module, choose M here: the - module will be called wm8775. - -config VIDEO_WM8739 - tristate "Wolfson Microelectronics WM8739 stereo audio ADC" - depends on VIDEO_DEV && I2C && EXPERIMENTAL - ---help--- - Support for the Wolfson Microelectronics WM8739 - stereo A/D Converter. - - To compile this driver as a module, choose M here: the - module will be called wm8739. - -config VIDEO_CX2341X - tristate "Conexant CX2341x MPEG encoders" - depends on VIDEO_V4L2 && EXPERIMENTAL - ---help--- - Support for the Conexant CX23416 MPEG encoders - and CX23415 MPEG encoder/decoders. - - This module currently supports the encoding functions only. - - To compile this driver as a module, choose M here: the - module will be called cx2341x. - -source "drivers/media/video/cx25840/Kconfig" - -config VIDEO_SAA711X - tristate "Philips SAA7113/4/5 video decoders" - depends on VIDEO_DEV && I2C && EXPERIMENTAL - ---help--- - Support for the Philips SAA7113/4/5 video decoders. - - To compile this driver as a module, choose M here: the - module will be called saa7115. - -config VIDEO_SAA7127 - tristate "Philips SAA7127/9 digital video encoders" - depends on VIDEO_V4L2 && I2C && EXPERIMENTAL - ---help--- - Support for the Philips SAA7127/9 digital video encoders. - - To compile this driver as a module, choose M here: the - module will be called saa7127. - -config VIDEO_UPD64031A - tristate "NEC Electronics uPD64031A Ghost Reduction" - depends on VIDEO_V4L2 && I2C && EXPERIMENTAL - ---help--- - Support for the NEC Electronics uPD64031A Ghost Reduction - video chip. It is most often found in NTSC TV cards made for - Japan and is used to reduce the 'ghosting' effect that can - be present in analog TV broadcasts. - - To compile this driver as a module, choose M here: the - module will be called upd64031a. - -config VIDEO_UPD64083 - tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" - depends on VIDEO_V4L2 && I2C && EXPERIMENTAL - ---help--- - Support for the NEC Electronics uPD64083 3-Dimensional Y/C - separation video chip. It is used to improve the quality of - the colors of a composite signal. - - To compile this driver as a module, choose M here: the - module will be called upd64083. - -endmenu # encoder / decoder chips + module will be called arv. # # USB Multimedia device configuration @@ -445,8 +675,6 @@ endmenu # encoder / decoder chips menu "V4L USB devices" depends on USB && VIDEO_DEV -source "drivers/media/video/pvrusb2/Kconfig" - source "drivers/media/video/em28xx/Kconfig" source "drivers/media/video/usbvideo/Kconfig" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index e82e511f2a7..af57abce8a6 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -17,7 +17,10 @@ ifeq ($(CONFIG_VIDEO_V4L1_COMPAT),y) endif obj-$(CONFIG_VIDEO_BT848) += bt8xx/ -obj-$(CONFIG_VIDEO_BT848) += tvaudio.o tda7432.o tda9875.o ir-kbd-i2c.o +obj-$(CONFIG_VIDEO_BT848) += ir-kbd-i2c.o +obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o +obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o +obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o obj-$(CONFIG_VIDEO_ZR36120) += zoran.o @@ -27,17 +30,32 @@ obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o obj-$(CONFIG_VIDEO_W9966) += w9966.o -obj-$(CONFIG_VIDEO_ZORAN_BUZ) += saa7111.o saa7185.o zr36060.o -obj-$(CONFIG_VIDEO_ZORAN_DC10) += saa7110.o adv7175.o zr36060.o -obj-$(CONFIG_VIDEO_ZORAN_DC30) += adv7175.o vpx3220.o zr36050.o \ - zr36016.o -obj-$(CONFIG_VIDEO_ZORAN_LML33) += bt819.o bt856.o zr36060.o -obj-$(CONFIG_VIDEO_ZORAN_LML33R10) += saa7114.o adv7170.o zr36060.o -obj-$(CONFIG_VIDEO_ZORAN_AVS6EYES) += bt866.o ks0127.o zr36060.o + +obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o +obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o +obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o +obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o +obj-$(CONFIG_VIDEO_SAA7111) += saa7111.o +obj-$(CONFIG_VIDEO_SAA7114) += saa7114.o +obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o +obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o +obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o +obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o +obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o +obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o +obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o +obj-$(CONFIG_VIDEO_BT819) += bt819.o +obj-$(CONFIG_VIDEO_BT856) += bt856.o +obj-$(CONFIG_VIDEO_BT866) += bt866.o +obj-$(CONFIG_VIDEO_KS0127) += ks0127.o + obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o videocodec.o +obj-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o +obj-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o + obj-$(CONFIG_VIDEO_PMS) += pms.o obj-$(CONFIG_VIDEO_PLANB) += planb.o -obj-$(CONFIG_VIDEO_VINO) += vino.o saa7191.o indycam.o +obj-$(CONFIG_VIDEO_VINO) += vino.o indycam.o obj-$(CONFIG_VIDEO_STRADIS) += stradis.o obj-$(CONFIG_VIDEO_CPIA) += cpia.o obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o @@ -46,7 +64,7 @@ obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ -obj-$(CONFIG_VIDEO_EM28XX) += tvp5150.o +obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o @@ -55,10 +73,10 @@ obj-$(CONFIG_VIDEO_WM8775) += wm8775.o obj-$(CONFIG_VIDEO_WM8739) += wm8739.o obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ -obj-$(CONFIG_VIDEO_MXB) += saa7111.o tda9840.o tea6415c.o tea6420.o mxb.o +obj-$(CONFIG_VIDEO_MXB) += mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o -obj-$(CONFIG_VIDEO_DPC) += saa7111.o dpc7146.o +obj-$(CONFIG_VIDEO_DPC) += dpc7146.o obj-$(CONFIG_TUNER_3036) += tuner-3036.o obj-$(CONFIG_VIDEO_TUNER) += tuner.o @@ -70,8 +88,6 @@ obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o obj-$(CONFIG_VIDEO_CX25840) += cx25840/ -obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o -obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o @@ -96,4 +112,3 @@ obj-$(CONFIG_VIDEO_VIVI) += vivi.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core extra-cflags-$(CONFIG_VIDEO_V4L1_COMPAT) += -DCONFIG_VIDEO_V4L1_COMPAT - diff --git a/drivers/media/video/bt866.c b/drivers/media/video/bt866.c index 05e42bbcfc3..772fd52d551 100644 --- a/drivers/media/video/bt866.c +++ b/drivers/media/video/bt866.c @@ -65,7 +65,7 @@ MODULE_LICENSE("GPL"); struct bt866 { struct i2c_client *i2c; int addr; - unsigned char reg[128]; + unsigned char reg[256]; int norm; int enable; diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig index cdcf5565071..58eae887a62 100644 --- a/drivers/media/video/bt8xx/Kconfig +++ b/drivers/media/video/bt8xx/Kconfig @@ -8,7 +8,10 @@ config VIDEO_BT848 select VIDEO_IR select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEO_MSP3400 + select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_TDA7432 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_TDA9875 if VIDEO_HELPER_CHIPS_AUTO ---help--- Support for BT848 based frame grabber/overlay boards. This includes the Miro, Hauppauge and STB boards. Please read the material in diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index de14818d5cc..d23a42b1504 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -4991,7 +4991,7 @@ void __devinit bttv_check_chipset(void) int pcipci_fail = 0; struct pci_dev *dev = NULL; - if (pci_pci_problems & PCIPCI_FAIL) + if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) /* should check if target is AGP */ pcipci_fail = 1; if (pci_pci_problems & (PCIPCI_TRITON|PCIPCI_NATOMA|PCIPCI_VIAETBF)) triton1 = 1; diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 20dff7c316e..50dde82844e 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -2431,6 +2431,14 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, fbuf->bytesperline = btv->fbuf.fmt.bytesperline; if (fh->ovfmt) fbuf->depth = fh->ovfmt->depth; + else { + if (fbuf->width) + fbuf->depth = ((fbuf->bytesperline<<3) + + (fbuf->width-1) ) + /fbuf->width; + else + fbuf->depth = 0; + } return 0; } case VIDIOCSFBUF: @@ -4186,6 +4194,7 @@ static void __devexit bttv_remove(struct pci_dev *pci_dev) return; } +#ifdef CONFIG_PM static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state) { struct bttv *btv = pci_get_drvdata(pci_dev); @@ -4266,6 +4275,7 @@ static int bttv_resume(struct pci_dev *pci_dev) spin_unlock_irqrestore(&btv->s_lock,flags); return 0; } +#endif static struct pci_device_id bttv_pci_tbl[] = { {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, @@ -4286,8 +4296,10 @@ static struct pci_driver bttv_pci_driver = { .id_table = bttv_pci_tbl, .probe = bttv_probe, .remove = __devexit_p(bttv_remove), +#ifdef CONFIG_PM .suspend = bttv_suspend, .resume = bttv_resume, +#endif }; static int bttv_init_module(void) diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c index 0dfbcc85ebb..70de6c96e20 100644 --- a/drivers/media/video/bt8xx/bttv-i2c.c +++ b/drivers/media/video/bt8xx/bttv-i2c.c @@ -8,6 +8,9 @@ & Marcus Metzler (mocm@thp.uni-koeln.de) (c) 1999-2003 Gerd Knorr <kraxel@bytesex.org> + (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org> + - Multituner support and i2c address binding + 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 @@ -45,10 +48,18 @@ static int i2c_debug; static int i2c_hw; static int i2c_scan; module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_hw,"configure i2c debug level"); module_param(i2c_hw, int, 0444); +MODULE_PARM_DESC(i2c_hw,"force use of hardware i2c support, " + "instead of software bitbang"); module_param(i2c_scan, int, 0444); MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); +static unsigned int i2c_udelay = 5; +module_param(i2c_udelay, int, 0444); +MODULE_PARM_DESC(i2c_udelay,"soft i2c delay at insmod time, in usecs " + "(should be 5 or higher). Lower value means higher bus speed."); + /* ----------------------------------------------------------------------- */ /* I2C functions - bitbanging adapter (software i2c) */ @@ -425,6 +436,11 @@ int __devinit init_bttv_i2c(struct bttv *btv) sizeof(bttv_i2c_adap_hw_template)); } else { /* bt848 */ + /* Prevents usage of invalid delay values */ + if (i2c_udelay<5) + i2c_udelay=5; + bttv_i2c_algo_bit_template.udelay=i2c_udelay; + memcpy(&btv->c.i2c_adap, &bttv_i2c_adap_sw_template, sizeof(bttv_i2c_adap_sw_template)); memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template, diff --git a/drivers/media/video/compat_ioctl32.c b/drivers/media/video/compat_ioctl32.c index b69ee119481..d82a488f12a 100644 --- a/drivers/media/video/compat_ioctl32.c +++ b/drivers/media/video/compat_ioctl32.c @@ -58,6 +58,7 @@ static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user return 0; } + struct video_buffer32 { compat_caddr_t base; compat_int_t height, width, depth, bytesperline; @@ -618,6 +619,7 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg struct video_buffer vb; struct video_window vw; struct video_code vc; + struct video_audio va; #endif struct v4l2_format v2f; struct v4l2_buffer v2b; @@ -635,31 +637,31 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg /* First, convert the command. */ switch(cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break; - case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break; - case VIDIOCGWIN32: cmd = VIDIOCGWIN; break; - case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break; - case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break; - case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break; - case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break; - case VIDIOCSMICROCODE32: cmd = VIDIOCSMICROCODE; break; + case VIDIOCGTUNER32: realcmd = cmd = VIDIOCGTUNER; break; + case VIDIOCSTUNER32: realcmd = cmd = VIDIOCSTUNER; break; + case VIDIOCGWIN32: realcmd = cmd = VIDIOCGWIN; break; + case VIDIOCGFBUF32: realcmd = cmd = VIDIOCGFBUF; break; + case VIDIOCSFBUF32: realcmd = cmd = VIDIOCSFBUF; break; + case VIDIOCGFREQ32: realcmd = cmd = VIDIOCGFREQ; break; + case VIDIOCSFREQ32: realcmd = cmd = VIDIOCSFREQ; break; + case VIDIOCSMICROCODE32: realcmd = cmd = VIDIOCSMICROCODE; break; #endif - case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break; - case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break; - case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break; - case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break; - case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break; - case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break; - case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; - case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break; - case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break; - case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break; + case VIDIOC_G_FMT32: realcmd = cmd = VIDIOC_G_FMT; break; + case VIDIOC_S_FMT32: realcmd = cmd = VIDIOC_S_FMT; break; + case VIDIOC_QUERYBUF32: realcmd = cmd = VIDIOC_QUERYBUF; break; + case VIDIOC_QBUF32: realcmd = cmd = VIDIOC_QBUF; break; + case VIDIOC_DQBUF32: realcmd = cmd = VIDIOC_DQBUF; break; + case VIDIOC_STREAMON32: realcmd = cmd = VIDIOC_STREAMON; break; + case VIDIOC_STREAMOFF32: realcmd = cmd = VIDIOC_STREAMOFF; break; + case VIDIOC_G_FBUF32: realcmd = cmd = VIDIOC_G_FBUF; break; + case VIDIOC_S_FBUF32: realcmd = cmd = VIDIOC_S_FBUF; break; + case VIDIOC_OVERLAY32: realcmd = cmd = VIDIOC_OVERLAY; break; case VIDIOC_ENUMSTD32: realcmd = VIDIOC_ENUMSTD; break; case VIDIOC_ENUMINPUT32: realcmd = VIDIOC_ENUMINPUT; break; - case VIDIOC_S_CTRL32: cmd = VIDIOC_S_CTRL; break; - case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; - case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break; - case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break; + case VIDIOC_S_CTRL32: realcmd = cmd = VIDIOC_S_CTRL; break; + case VIDIOC_G_INPUT32: realcmd = cmd = VIDIOC_G_INPUT; break; + case VIDIOC_S_INPUT32: realcmd = cmd = VIDIOC_S_INPUT; break; + case VIDIOC_TRY_FMT32: realcmd = cmd = VIDIOC_TRY_FMT; break; }; switch(cmd) { @@ -676,6 +678,7 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg compatible_arg = 0; break; + case VIDIOCSFREQ: #endif case VIDIOC_S_INPUT: @@ -683,7 +686,7 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: err = get_user(karg.vx, (u32 __user *)up); - compatible_arg = 0; + compatible_arg = 1; break; case VIDIOC_S_FBUF: @@ -739,6 +742,7 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg case VIDIOC_G_FBUF: case VIDIOC_G_INPUT: compatible_arg = 0; + break; #ifdef CONFIG_VIDEO_V4L1_COMPAT case VIDIOCSMICROCODE: err = microcode32(&karg.vc, up); @@ -755,7 +759,7 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg mm_segment_t old_fs = get_fs(); set_fs(KERNEL_DS); - err = native_ioctl(file, realcmd, (unsigned long)&karg); + err = native_ioctl(file, realcmd, (unsigned long) &karg); set_fs(old_fs); } if(err == 0) { @@ -772,6 +776,7 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg case VIDIOCGFBUF: err = put_video_buffer32(&karg.vb, up); break; + #endif case VIDIOC_G_FBUF: err = put_v4l2_framebuffer32(&karg.v2fb, up); @@ -841,10 +846,14 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOCSFBUF32: case VIDIOCGFREQ32: case VIDIOCSFREQ32: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: #endif case VIDIOC_QUERYCAP: case VIDIOC_ENUM_FMT: case VIDIOC_G_FMT32: + case VIDIOC_CROPCAP: + case VIDIOC_S_CROP: case VIDIOC_S_FMT32: case VIDIOC_REQBUFS: case VIDIOC_QUERYBUF32: @@ -882,8 +891,6 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOCSPICT: case VIDIOCCAPTURE: case VIDIOCKEY: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: case VIDIOCSYNC: case VIDIOCMCAPTURE: case VIDIOCGMBUF: diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index 65f00fc08fa..657e0b96914 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -691,7 +691,7 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) .video_luma_spatial_filter_type = V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR, .video_chroma_spatial_filter_type = V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, .video_temporal_filter_mode = V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, - .video_temporal_filter = 0, + .video_temporal_filter = 8, .video_median_filter_type = V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, .video_luma_median_filter_top = 255, .video_luma_median_filter_bottom = 0, @@ -731,6 +731,7 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func, }; int err = 0; + u16 temporal = new->video_temporal_filter; cx2341x_api(priv, func, CX2341X_ENC_SET_OUTPUT_PORT, 2, new->port, 0); @@ -741,6 +742,7 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func, if (old == NULL || old->width != new->width || old->height != new->height || old->video_encoding != new->video_encoding) { + int is_scaling; u16 w = new->width; u16 h = new->height; @@ -750,6 +752,20 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func, } err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w); if (err) return err; + + /* Adjust temporal filter if necessary. The problem with the temporal + filter is that it works well with full resolution capturing, but + not when the capture window is scaled (the filter introduces + a ghosting effect). So if the capture window changed, and there is + no updated filter value, then the filter is set depending on whether + the new window is full resolution or not. + + For full resolution a setting of 8 really improves the video + quality, especially if the original video quality is suboptimal. */ + is_scaling = new->width != 720 || new->height != (new->is_50hz ? 576 : 480); + if (old && old->video_temporal_filter == temporal) { + temporal = is_scaling ? 0 : 8; + } } if (old == NULL || old->stream_type != new->stream_type) { @@ -815,9 +831,9 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func, } if (old == NULL || old->video_spatial_filter != new->video_spatial_filter || - old->video_temporal_filter != new->video_temporal_filter) { + old->video_temporal_filter != temporal) { err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2, - new->video_spatial_filter, new->video_temporal_filter); + new->video_spatial_filter, temporal); if (err) return err; } if (old == NULL || old->video_temporal_decimation != new->video_temporal_decimation) { @@ -855,6 +871,9 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix) printk(KERN_INFO "%s: Stream: %s\n", prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE)); + printk(KERN_INFO "%s: VBI Format: %s\n", + prefix, + cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT)); /* Video */ printk(KERN_INFO "%s: Video: %dx%d, %d fps\n", diff --git a/drivers/media/video/cx25840/Kconfig b/drivers/media/video/cx25840/Kconfig index 854264e42ec..7cf29a03ed6 100644 --- a/drivers/media/video/cx25840/Kconfig +++ b/drivers/media/video/cx25840/Kconfig @@ -1,6 +1,6 @@ config VIDEO_CX25840 tristate "Conexant CX2584x audio/video decoders" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL select FW_LOADER ---help--- Support for the Conexant CX2584x audio/video decoders. diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c index 6cc8bf215e8..48014a254e1 100644 --- a/drivers/media/video/cx25840/cx25840-vbi.c +++ b/drivers/media/video/cx25840/cx25840-vbi.c @@ -111,6 +111,10 @@ void cx25840_vbi_setup(struct i2c_client *client) uv_lpf=0; comb=0; sc=0x0a425f; + } else if (std == V4L2_STD_PAL_Nc) { + uv_lpf=1; + comb=0x20; + sc=556453; } else { uv_lpf=1; comb=0x20; diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index 7a94e6a1192..51d68f32aa0 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -1,7 +1,3 @@ -config VIDEO_CX88_VP3054 - tristate - depends on VIDEO_CX88_DVB && DVB_MT352 - config VIDEO_CX88 tristate "Conexant 2388x (bt878 successor) support" depends on VIDEO_DEV && PCI && I2C @@ -52,6 +48,14 @@ config VIDEO_CX88_DVB depends on VIDEO_CX88 && DVB_CORE select VIDEO_BUF_DVB select DVB_PLL + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_OR51132 if !DVB_FE_CUSTOMISE + select DVB_CX22702 if !DVB_FE_CUSTOMISE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_NXT200X if !DVB_FE_CUSTOMISE + select DVB_CX24123 if !DVB_FE_CUSTOMISE + select DVB_ISL6421 if !DVB_FE_CUSTOMISE ---help--- This adds support for DVB/ATSC cards based on the Conexant 2388x chip. @@ -59,101 +63,12 @@ config VIDEO_CX88_DVB To compile this driver as a module, choose M here: the module will be called cx88-dvb. - You must also select one or more DVB/ATSC demodulators. - If you are unsure which you need, choose all of them. - -config VIDEO_CX88_DVB_ALL_FRONTENDS - bool "Build all supported frontends for cx2388x based TV cards" - default y - depends on VIDEO_CX88_DVB - select DVB_MT352 - select VIDEO_CX88_VP3054 - select DVB_ZL10353 - select DVB_OR51132 - select DVB_CX22702 - select DVB_LGDT330X - select DVB_NXT200X - select DVB_CX24123 - select DVB_ISL6421 - ---help--- - This builds cx88-dvb with all currently supported frontend - demodulators. If you wish to tweak your configuration, and - only include support for the hardware that you need, choose N here. - - If you are unsure, choose Y. - -config VIDEO_CX88_DVB_MT352 - bool "Zarlink MT352 DVB-T Support" - default y - depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS - select DVB_MT352 - ---help--- - This adds DVB-T support for cards based on the - Connexant 2388x chip and the MT352 demodulator. - -config VIDEO_CX88_DVB_VP3054 - bool "VP-3054 Secondary I2C Bus Support" - default y - depends on VIDEO_CX88_DVB_MT352 - select VIDEO_CX88_VP3054 +config VIDEO_CX88_VP3054 + tristate "VP-3054 Secondary I2C Bus Support" + default m + depends on VIDEO_CX88_DVB && DVB_MT352 ---help--- This adds DVB-T support for cards based on the Connexant 2388x chip and the MT352 demodulator, which also require support for the VP-3054 Secondary I2C bus, such at DNTV Live! DVB-T Pro. - -config VIDEO_CX88_DVB_ZL10353 - bool "Zarlink ZL10353 DVB-T Support" - default y - depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS - select DVB_ZL10353 - ---help--- - This adds DVB-T support for cards based on the - Connexant 2388x chip and the ZL10353 demodulator, - successor to the Zarlink MT352. - -config VIDEO_CX88_DVB_OR51132 - bool "OR51132 ATSC Support" - default y - depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS - select DVB_OR51132 - ---help--- - This adds ATSC 8VSB and QAM64/256 support for cards based on the - Connexant 2388x chip and the OR51132 demodulator. - -config VIDEO_CX88_DVB_CX22702 - bool "Conexant CX22702 DVB-T Support" - default y - depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS - select DVB_CX22702 - ---help--- - This adds DVB-T support for cards based on the - Connexant 2388x chip and the CX22702 demodulator. - -config VIDEO_CX88_DVB_LGDT330X - bool "LG Electronics DT3302/DT3303 ATSC Support" - default y - depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS - select DVB_LGDT330X - ---help--- - This adds ATSC 8VSB and QAM64/256 support for cards based on the - Connexant 2388x chip and the LGDT3302/LGDT3303 demodulator. - -config VIDEO_CX88_DVB_NXT200X - bool "NXT2002/NXT2004 ATSC Support" - default y - depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS - select DVB_NXT200X - ---help--- - This adds ATSC 8VSB and QAM64/256 support for cards based on the - Connexant 2388x chip and the NXT2002/NXT2004 demodulator. - -config VIDEO_CX88_DVB_CX24123 - bool "Conexant CX24123 DVB-S Support" - default y - depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS - select DVB_CX24123 - select DVB_ISL6421 - ---help--- - This adds DVB-S support for cards based on the - Connexant 2388x chip and the CX24123 demodulator. diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile index 352b919f30c..639c3b659d0 100644 --- a/drivers/media/video/cx88/Makefile +++ b/drivers/media/video/cx88/Makefile @@ -14,13 +14,6 @@ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends extra-cflags-$(CONFIG_VIDEO_BUF_DVB) += -DHAVE_VIDEO_BUF_DVB=1 -extra-cflags-$(CONFIG_DVB_CX22702) += -DHAVE_CX22702=1 -extra-cflags-$(CONFIG_DVB_OR51132) += -DHAVE_OR51132=1 -extra-cflags-$(CONFIG_DVB_LGDT330X) += -DHAVE_LGDT330X=1 -extra-cflags-$(CONFIG_DVB_MT352) += -DHAVE_MT352=1 -extra-cflags-$(CONFIG_DVB_ZL10353) += -DHAVE_ZL10353=1 -extra-cflags-$(CONFIG_DVB_NXT200X) += -DHAVE_NXT200X=1 -extra-cflags-$(CONFIG_DVB_CX24123) += -DHAVE_CX24123=1 extra-cflags-$(CONFIG_VIDEO_CX88_VP3054)+= -DHAVE_VP3054_I2C=1 EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index b60177f173c..a7921f9d45d 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -1160,8 +1160,10 @@ static struct pci_driver blackbird_pci_driver = { .id_table = cx8802_pci_tbl, .probe = blackbird_probe, .remove = __devexit_p(blackbird_remove), +#ifdef CONFIG_PM .suspend = cx8802_suspend_common, .resume = cx8802_resume_common, +#endif }; static int blackbird_init(void) diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 14bd4863d15..6214eb823b2 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1041,11 +1041,11 @@ struct cx88_board cx88_boards[] = { .input = {{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - .gpio0 = 0x000027df, + .gpio0 = 0x000067df, },{ .type = CX88_VMUX_SVIDEO, .vmux = 2, - .gpio0 = 0x000027df, + .gpio0 = 0x000067df, }}, .dvb = 1, }, @@ -1209,6 +1209,100 @@ struct cx88_board cx88_boards[] = { }}, .dvb = 1, }, + [CX88_BOARD_HAUPPAUGE_HVR3000] = { + /* FIXME: Add dvb & radio support */ + .name = "Hauppauge WinTV-HVR3000 TriMode Analog/DVB-S/DVB-T", + .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x84bf, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x84bf, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x84bf, + }}, + }, + [CX88_BOARD_NORWOOD_MICRO] = { + .name = "Norwood Micro TV Tuner", + .tuner_type = TUNER_TNF_5335MF, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x0709, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x070b, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x070b, + }}, + }, + [CX88_BOARD_TE_DTV_250_OEM_SWANN] = { + .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM", + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x003fffff, + .gpio1 = 0x00e00000, + .gpio2 = 0x003fffff, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x003fffff, + .gpio1 = 0x00e00000, + .gpio2 = 0x003fffff, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x003fffff, + .gpio1 = 0x00e00000, + .gpio2 = 0x003fffff, + .gpio3 = 0x02000000, + }}, + }, + [CX88_BOARD_HAUPPAUGE_HVR1300] = { + .name = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder", + .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0xe780, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0xe780, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0xe780, + }}, + /* fixme: Add radio support */ + .dvb = 1, + }, }; const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards); @@ -1254,7 +1348,7 @@ struct cx88_subid cx88_subids[] = { .card = CX88_BOARD_LEADTEK_PVR2000, },{ .subvendor = 0x107d, - .subdevice = 0x663C, + .subdevice = 0x663c, .card = CX88_BOARD_LEADTEK_PVR2000, },{ .subvendor = 0x1461, @@ -1458,6 +1552,35 @@ struct cx88_subid cx88_subids[] = { .subvendor = 0x14f1, .subdevice = 0x0084, .card = CX88_BOARD_GENIATECH_DVBS, + },{ + .subvendor = 0x0070, + .subdevice = 0x1404, + .card = CX88_BOARD_HAUPPAUGE_HVR3000, + },{ + .subvendor = 0x1461, + .subdevice = 0xc111, /* AverMedia M150-D */ + /* This board is known to work with the ASUS PVR416 config */ + .card = CX88_BOARD_ASUS_PVR_416, + },{ + .subvendor = 0xc180, + .subdevice = 0xc980, + .card = CX88_BOARD_TE_DTV_250_OEM_SWANN, + },{ + .subvendor = 0x0070, + .subdevice = 0x9600, + .card = CX88_BOARD_HAUPPAUGE_HVR1300, + },{ + .subvendor = 0x0070, + .subdevice = 0x9601, + .card = CX88_BOARD_HAUPPAUGE_HVR1300, + },{ + .subvendor = 0x0070, + .subdevice = 0x9602, + .card = CX88_BOARD_HAUPPAUGE_HVR1300, + },{ + .subvendor = 0x107d, + .subdevice = 0x6632, + .card = CX88_BOARD_LEADTEK_PVR2000, }, }; const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); @@ -1501,6 +1624,7 @@ static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data) /* Make sure we support the board model */ switch (tv.model) { + case 14569: /* WinTV-HVR3000 (OEM, no IR, no back panel video) */ case 28552: /* WinTV-PVR 'Roslyn' (No IR) */ case 34519: /* WinTV-PCI-FM */ case 90002: /* Nova-T-PCI (9002) */ @@ -1512,6 +1636,11 @@ static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data) case 92000: /* Nova-SE2 (OEM, No Video or IR) */ case 94009: /* WinTV-HVR1100 (Video and IR Retail) */ case 94501: /* WinTV-HVR1100 (Video and IR OEM) */ + case 96009: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX) */ + case 96019: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX/TX) */ + case 96559: /* WinTV-HVR1300 (PAL Video, MPEG Video no IR) */ + case 96569: /* WinTV-HVR1300 () */ + case 96659: /* WinTV-HVR1300 () */ case 98559: /* WinTV-HVR1100LP (Video no IR, Retail - Low Profile) */ /* known */ break; @@ -1638,6 +1767,22 @@ void cx88_card_list(struct cx88_core *core, struct pci_dev *pci) core->name, i, cx88_boards[i].name); } +void cx88_card_setup_pre_i2c(struct cx88_core *core) +{ + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_HVR1300: + /* Bring the 702 demod up before i2c scanning/attach or devices are hidden */ + /* We leave here with the 702 on the bus */ + cx_write(MO_GP0_IO, 0x0000e780); + udelay(1000); + cx_clear(MO_GP0_IO, 0x00000080); + udelay(50); + cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */ + udelay(1000); + break; + } +} + void cx88_card_setup(struct cx88_core *core) { static u8 eeprom[256]; @@ -1666,6 +1811,8 @@ void cx88_card_setup(struct cx88_core *core) case CX88_BOARD_HAUPPAUGE_DVB_T1: case CX88_BOARD_HAUPPAUGE_HVR1100: case CX88_BOARD_HAUPPAUGE_HVR1100LP: + case CX88_BOARD_HAUPPAUGE_HVR3000: + case CX88_BOARD_HAUPPAUGE_HVR1300: if (0 == core->i2c_rc) hauppauge_eeprom(core,eeprom); break; @@ -1673,9 +1820,15 @@ void cx88_card_setup(struct cx88_core *core) cx_write(MO_GP0_IO, 0x000007f8); cx_write(MO_GP1_IO, 0x00000001); break; + case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: + /* GPIO0:6 is hooked to FX2 reset pin */ + cx_set(MO_GP0_IO, 0x00004040); + cx_clear(MO_GP0_IO, 0x00000040); + msleep(1000); + cx_set(MO_GP0_IO, 0x00004040); + /* FALLTHROUGH */ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: - case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID: /* GPIO0:0 is hooked to mt352 reset pin */ cx_set(MO_GP0_IO, 0x00000101); diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index 973d3f39b2d..f379ede3049 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -105,7 +105,7 @@ static u32* cx88_risc_field(u32 *rp, struct scatterlist *sglist, *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset); offset+=bpl; } else { - /* scanline needs to be splitted */ + /* scanline needs to be split */ todo = bpl; *(rp++)=cpu_to_le32(RISC_WRITE|RISC_SOL| (sg_dma_len(sg)-offset)); @@ -792,6 +792,11 @@ int cx88_start_audio_dma(struct cx88_core *core) { /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */ int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4; + + /* If downstream RISC is enabled, bail out; ALSA is managing DMA */ + if (cx_read(MO_AUD_DMACNTRL) & 0x10) + return 0; + /* setup fifo + format */ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0); cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0); @@ -801,11 +806,16 @@ int cx88_start_audio_dma(struct cx88_core *core) /* start dma */ cx_write(MO_AUD_DMACNTRL, 0x0003); /* Up and Down fifo enable */ + return 0; } int cx88_stop_audio_dma(struct cx88_core *core) { + /* If downstream RISC is enabled, bail out; ALSA is managing DMA */ + if (cx_read(MO_AUD_DMACNTRL) & 0x10) + return 0; + /* stop dma */ cx_write(MO_AUD_DMACNTRL, 0x0000); @@ -1123,6 +1133,7 @@ struct cx88_core* cx88_core_get(struct pci_dev *pci) /* init hardware */ cx88_reset(core); + cx88_card_setup_pre_i2c(core); cx88_i2c_init(core,pci); cx88_call_i2c_clients (core, TUNER_SET_STANDBY, NULL); cx88_card_setup(core); diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index afde3789d70..c87041dee21 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -33,32 +33,18 @@ #include "dvb-pll.h" #include <media/v4l2-common.h> -#ifdef HAVE_MT352 -# include "mt352.h" -# include "mt352_priv.h" -# ifdef HAVE_VP3054_I2C -# include "cx88-vp3054-i2c.h" -# endif -#endif -#ifdef HAVE_ZL10353 -# include "zl10353.h" -#endif -#ifdef HAVE_CX22702 -# include "cx22702.h" -#endif -#ifdef HAVE_OR51132 -# include "or51132.h" -#endif -#ifdef HAVE_LGDT330X -# include "lgdt330x.h" -# include "lg_h06xf.h" -#endif -#ifdef HAVE_NXT200X -# include "nxt200x.h" -#endif -#ifdef HAVE_CX24123 -# include "cx24123.h" +#include "mt352.h" +#include "mt352_priv.h" +#ifdef HAVE_VP3054_I2C +# include "cx88-vp3054-i2c.h" #endif +#include "zl10353.h" +#include "cx22702.h" +#include "or51132.h" +#include "lgdt330x.h" +#include "lg_h06xf.h" +#include "nxt200x.h" +#include "cx24123.h" #include "isl6421.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); @@ -114,8 +100,6 @@ static struct videobuf_queue_ops dvb_qops = { }; /* ------------------------------------------------------------------ */ - -#ifdef HAVE_MT352 static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe) { static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x39 }; @@ -181,7 +165,7 @@ static int dntv_live_dvbt_demod_init(struct dvb_frontend* fe) } static struct mt352_config dvico_fusionhdtv = { - .demod_address = 0x0F, + .demod_address = 0x0f, .demod_init = dvico_fusionhdtv_demod_init, }; @@ -191,7 +175,7 @@ static struct mt352_config dntv_live_dvbt_config = { }; static struct mt352_config dvico_fusionhdtv_dual = { - .demod_address = 0x0F, + .demod_address = 0x0f, .demod_init = dvico_dual_demod_init, }; @@ -266,8 +250,8 @@ static int dntv_live_dvbt_pro_tuner_set_params(struct dvb_frontend* fe, if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) { printk(KERN_WARNING "cx88-dvb: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, dev->core->pll_addr, buf[0], err); + "(addr %02x <- %02x, err = %i)\n", + __FUNCTION__, dev->core->pll_addr, buf[0], err); if (err < 0) return err; else @@ -283,9 +267,7 @@ static struct mt352_config dntv_live_dvbt_pro_config = { .demod_init = dntv_live_dvbt_pro_demod_init, }; #endif -#endif -#ifdef HAVE_ZL10353 static int dvico_hybrid_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { @@ -304,8 +286,8 @@ static int dvico_hybrid_tuner_set_params(struct dvb_frontend *fe, fe->ops.i2c_gate_ctrl(fe, 1); if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) { printk(KERN_WARNING "cx88-dvb: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, pllbuf[0], pllbuf[1], err); + "(addr %02x <- %02x, err = %i)\n", + __FUNCTION__, pllbuf[0], pllbuf[1], err); if (err < 0) return err; else @@ -316,16 +298,14 @@ static int dvico_hybrid_tuner_set_params(struct dvb_frontend *fe, } static struct zl10353_config dvico_fusionhdtv_hybrid = { - .demod_address = 0x0F, + .demod_address = 0x0f, .no_tuner = 1, }; static struct zl10353_config dvico_fusionhdtv_plus_v1_1 = { - .demod_address = 0x0F, + .demod_address = 0x0f, }; -#endif -#ifdef HAVE_CX22702 static struct cx22702_config connexant_refboard_config = { .demod_address = 0x43, .output_mode = CX22702_SERIAL_OUTPUT, @@ -339,9 +319,11 @@ static struct cx22702_config hauppauge_hvr1100_config = { .demod_address = 0x63, .output_mode = CX22702_SERIAL_OUTPUT, }; -#endif +static struct cx22702_config hauppauge_hvr1300_config = { + .demod_address = 0x63, + .output_mode = CX22702_SERIAL_OUTPUT, +}; -#ifdef HAVE_OR51132 static int or51132_set_ts_param(struct dvb_frontend* fe, int is_punctured) { @@ -351,12 +333,10 @@ static int or51132_set_ts_param(struct dvb_frontend* fe, } static struct or51132_config pchdtv_hd3000 = { - .demod_address = 0x15, - .set_ts_params = or51132_set_ts_param, + .demod_address = 0x15, + .set_ts_params = or51132_set_ts_param, }; -#endif -#ifdef HAVE_LGDT330X static int lgdt3302_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { @@ -373,14 +353,14 @@ static int lgdt3302_tuner_set_params(struct dvb_frontend* fe, dvb_pll_configure(core->pll_desc, buf, params->frequency, 0); dprintk(1, "%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n", - __FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]); + __FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); if ((err = i2c_transfer(&core->i2c_adap, &msg, 1)) != 1) { printk(KERN_WARNING "cx88-dvb: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); + "(addr %02x <- %02x, err = %i)\n", + __FUNCTION__, buf[0], buf[1], err); if (err < 0) return err; else @@ -425,28 +405,26 @@ static int lgdt330x_set_ts_param(struct dvb_frontend* fe, int is_punctured) } static struct lgdt330x_config fusionhdtv_3_gold = { - .demod_address = 0x0e, - .demod_chip = LGDT3302, - .serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */ - .set_ts_params = lgdt330x_set_ts_param, + .demod_address = 0x0e, + .demod_chip = LGDT3302, + .serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */ + .set_ts_params = lgdt330x_set_ts_param, }; static struct lgdt330x_config fusionhdtv_5_gold = { - .demod_address = 0x0e, - .demod_chip = LGDT3303, - .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ - .set_ts_params = lgdt330x_set_ts_param, + .demod_address = 0x0e, + .demod_chip = LGDT3303, + .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ + .set_ts_params = lgdt330x_set_ts_param, }; static struct lgdt330x_config pchdtv_hd5500 = { - .demod_address = 0x59, - .demod_chip = LGDT3303, - .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ - .set_ts_params = lgdt330x_set_ts_param, + .demod_address = 0x59, + .demod_chip = LGDT3303, + .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ + .set_ts_params = lgdt330x_set_ts_param, }; -#endif -#ifdef HAVE_NXT200X static int nxt200x_set_ts_param(struct dvb_frontend* fe, int is_punctured) { @@ -465,28 +443,27 @@ static int nxt200x_set_pll_input(u8* buf, int input) } static struct nxt200x_config ati_hdtvwonder = { - .demod_address = 0x0a, - .set_pll_input = nxt200x_set_pll_input, - .set_ts_params = nxt200x_set_ts_param, + .demod_address = 0x0a, + .set_pll_input = nxt200x_set_pll_input, + .set_ts_params = nxt200x_set_ts_param, }; -#endif -#ifdef HAVE_CX24123 static int cx24123_set_ts_param(struct dvb_frontend* fe, int is_punctured) { struct cx8802_dev *dev= fe->dvb->priv; - dev->ts_gen_cntrl = 0x2; + dev->ts_gen_cntrl = 0x02; return 0; } -static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe, + fe_sec_voltage_t voltage) { struct cx8802_dev *dev= fe->dvb->priv; struct cx88_core *core = dev->core; if (voltage == SEC_VOLTAGE_OFF) { - cx_write(MO_GP0_IO, 0x000006fB); + cx_write(MO_GP0_IO, 0x000006fb); } else { cx_write(MO_GP0_IO, 0x000006f9); } @@ -496,7 +473,8 @@ static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t return 0; } -static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) { struct cx8802_dev *dev= fe->dvb->priv; struct cx88_core *core = dev->core; @@ -512,20 +490,20 @@ static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t } static struct cx24123_config geniatech_dvbs_config = { - .demod_address = 0x55, - .set_ts_params = cx24123_set_ts_param, + .demod_address = 0x55, + .set_ts_params = cx24123_set_ts_param, }; static struct cx24123_config hauppauge_novas_config = { - .demod_address = 0x55, - .set_ts_params = cx24123_set_ts_param, + .demod_address = 0x55, + .set_ts_params = cx24123_set_ts_param, }; static struct cx24123_config kworld_dvbs_100_config = { - .demod_address = 0x15, - .set_ts_params = cx24123_set_ts_param, + .demod_address = 0x15, + .set_ts_params = cx24123_set_ts_param, + .lnb_polarity = 1, }; -#endif static int dvb_register(struct cx8802_dev *dev) { @@ -535,114 +513,114 @@ static int dvb_register(struct cx8802_dev *dev) /* init frontend */ switch (dev->core->board) { -#ifdef HAVE_CX22702 case CX88_BOARD_HAUPPAUGE_DVB_T1: - dev->dvb.frontend = cx22702_attach(&hauppauge_novat_config, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_novat_config, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_pll_attach(dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_thomson_dtt759x); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_thomson_dtt759x); } break; case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: case CX88_BOARD_CONEXANT_DVB_T1: case CX88_BOARD_KWORLD_DVB_T_CX22702: case CX88_BOARD_WINFAST_DTV1000: - dev->dvb.frontend = cx22702_attach(&connexant_refboard_config, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(cx22702_attach, + &connexant_refboard_config, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_pll_attach(dev->dvb.frontend, 0x60, - &dev->core->i2c_adap, - &dvb_pll_thomson_dtt7579); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60, + &dev->core->i2c_adap, + &dvb_pll_thomson_dtt7579); } break; case CX88_BOARD_WINFAST_DTV2000H: case CX88_BOARD_HAUPPAUGE_HVR1100: case CX88_BOARD_HAUPPAUGE_HVR1100LP: - dev->dvb.frontend = cx22702_attach(&hauppauge_hvr1100_config, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr1100_config, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_pll_attach(dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_fmd1216me); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_fmd1216me); + } + break; + case CX88_BOARD_HAUPPAUGE_HVR1300: + dev->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr1300_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_fmd1216me); } break; -#endif -#if defined(HAVE_MT352) || defined(HAVE_ZL10353) case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: -#ifdef HAVE_MT352 - dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(mt352_attach, + &dvico_fusionhdtv, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_pll_attach(dev->dvb.frontend, 0x60, - &dev->core->i2c_adap, - &dvb_pll_thomson_dtt7579); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60, + NULL, &dvb_pll_thomson_dtt7579); break; } -#endif -#ifdef HAVE_ZL10353 /* ZL10353 replaces MT352 on later cards */ - dev->dvb.frontend = zl10353_attach(&dvico_fusionhdtv_plus_v1_1, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(zl10353_attach, + &dvico_fusionhdtv_plus_v1_1, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_pll_attach(dev->dvb.frontend, 0x60, - &dev->core->i2c_adap, - &dvb_pll_thomson_dtt7579); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60, + NULL, &dvb_pll_thomson_dtt7579); } -#endif break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: -#ifdef HAVE_MT352 /* The tin box says DEE1601, but it seems to be DTT7579 * compatible, with a slightly different MT352 AGC gain. */ - dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv_dual, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(mt352_attach, + &dvico_fusionhdtv_dual, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_pll_attach(dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_thomson_dtt7579); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + NULL, &dvb_pll_thomson_dtt7579); break; } -#endif -#ifdef HAVE_ZL10353 /* ZL10353 replaces MT352 on later cards */ - dev->dvb.frontend = zl10353_attach(&dvico_fusionhdtv_plus_v1_1, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(zl10353_attach, + &dvico_fusionhdtv_plus_v1_1, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_pll_attach(dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_thomson_dtt7579); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + NULL, &dvb_pll_thomson_dtt7579); } -#endif break; -#endif /* HAVE_MT352 || HAVE_ZL10353 */ -#ifdef HAVE_MT352 case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: - dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(mt352_attach, + &dvico_fusionhdtv, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_pll_attach(dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_lg_z201); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + NULL, &dvb_pll_lg_z201); } break; case CX88_BOARD_KWORLD_DVB_T: case CX88_BOARD_DNTV_LIVE_DVB_T: case CX88_BOARD_ADSTECH_DVB_T_PCI: - dev->dvb.frontend = mt352_attach(&dntv_live_dvbt_config, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(mt352_attach, + &dntv_live_dvbt_config, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_pll_attach(dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_unknown_1); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + NULL, &dvb_pll_unknown_1); } break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: #ifdef HAVE_VP3054_I2C dev->core->pll_addr = 0x61; dev->core->pll_desc = &dvb_pll_fmd1216me; - dev->dvb.frontend = mt352_attach(&dntv_live_dvbt_pro_config, + dev->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config, &((struct vp3054_i2c_state *)dev->card_priv)->adap); if (dev->dvb.frontend != NULL) { dev->dvb.frontend->ops.tuner_ops.set_params = dntv_live_dvbt_pro_tuner_set_params; @@ -651,30 +629,26 @@ static int dvb_register(struct cx8802_dev *dev) printk("%s: built without vp3054 support\n", dev->core->name); #endif break; -#endif -#ifdef HAVE_ZL10353 case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID: dev->core->pll_addr = 0x61; dev->core->pll_desc = &dvb_pll_thomson_fe6600; - dev->dvb.frontend = zl10353_attach(&dvico_fusionhdtv_hybrid, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(zl10353_attach, + &dvico_fusionhdtv_hybrid, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dev->dvb.frontend->ops.tuner_ops.set_params = dvico_hybrid_tuner_set_params; } break; -#endif -#ifdef HAVE_OR51132 case CX88_BOARD_PCHDTV_HD3000: - dev->dvb.frontend = or51132_attach(&pchdtv_hd3000, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(or51132_attach, + &pchdtv_hd3000, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_pll_attach(dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_thomson_dtt761x); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_thomson_dtt761x); } break; -#endif -#ifdef HAVE_LGDT330X case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q: dev->ts_gen_cntrl = 0x08; { @@ -690,8 +664,9 @@ static int dvb_register(struct cx8802_dev *dev) fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set; dev->core->pll_addr = 0x61; dev->core->pll_desc = &dvb_pll_microtune_4042; - dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_3_gold, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(lgdt330x_attach, + &fusionhdtv_3_gold, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3302_tuner_set_params; } @@ -709,8 +684,9 @@ static int dvb_register(struct cx8802_dev *dev) mdelay(200); dev->core->pll_addr = 0x61; dev->core->pll_desc = &dvb_pll_thomson_dtt761x; - dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_3_gold, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(lgdt330x_attach, + &fusionhdtv_3_gold, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3302_tuner_set_params; } @@ -726,8 +702,9 @@ static int dvb_register(struct cx8802_dev *dev) mdelay(100); cx_set(MO_GP0_IO, 1); mdelay(200); - dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_5_gold, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(lgdt330x_attach, + &fusionhdtv_5_gold, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3303_tuner_set_params; } @@ -743,52 +720,51 @@ static int dvb_register(struct cx8802_dev *dev) mdelay(100); cx_set(MO_GP0_IO, 1); mdelay(200); - dev->dvb.frontend = lgdt330x_attach(&pchdtv_hd5500, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(lgdt330x_attach, + &pchdtv_hd5500, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3303_tuner_set_params; } } break; -#endif -#ifdef HAVE_NXT200X case CX88_BOARD_ATI_HDTVWONDER: - dev->dvb.frontend = nxt200x_attach(&ati_hdtvwonder, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(nxt200x_attach, + &ati_hdtvwonder, + &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_pll_attach(dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_tuv1236d); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + NULL, &dvb_pll_tuv1236d); } break; -#endif -#ifdef HAVE_CX24123 case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: - dev->dvb.frontend = cx24123_attach(&hauppauge_novas_config, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(cx24123_attach, + &hauppauge_novas_config, + &dev->core->i2c_adap); if (dev->dvb.frontend) { - isl6421_attach(dev->dvb.frontend, &dev->core->i2c_adap, - 0x08, 0x00, 0x00); + dvb_attach(isl6421_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x08, 0x00, 0x00); } break; case CX88_BOARD_KWORLD_DVBS_100: - dev->dvb.frontend = cx24123_attach(&kworld_dvbs_100_config, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(cx24123_attach, + &kworld_dvbs_100_config, + &dev->core->i2c_adap); if (dev->dvb.frontend) { dev->core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage; dev->dvb.frontend->ops.set_voltage = kworld_dvbs_100_set_voltage; } break; case CX88_BOARD_GENIATECH_DVBS: - dev->dvb.frontend = cx24123_attach(&geniatech_dvbs_config, - &dev->core->i2c_adap); + dev->dvb.frontend = dvb_attach(cx24123_attach, + &geniatech_dvbs_config, + &dev->core->i2c_adap); if (dev->dvb.frontend) { dev->core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage; dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage; } break; -#endif default: printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n", dev->core->name); @@ -908,8 +884,10 @@ static struct pci_driver dvb_pci_driver = { .id_table = cx8802_pci_tbl, .probe = dvb_probe, .remove = __devexit_p(dvb_remove), +#ifdef CONFIG_PM .suspend = cx8802_suspend_common, .resume = cx8802_resume_common, +#endif }; static int dvb_init(void) diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c index 7bea3471486..27b5dbb2ca1 100644 --- a/drivers/media/video/cx88/cx88-i2c.c +++ b/drivers/media/video/cx88/cx88-i2c.c @@ -7,6 +7,9 @@ (c) 2002 Yurij Sysoev <yurij@naturesoft.net> (c) 1999-2003 Gerd Knorr <kraxel@bytesex.org> + (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org> + - Multituner support and i2c address binding + 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 @@ -40,6 +43,11 @@ static unsigned int i2c_scan = 0; module_param(i2c_scan, int, 0444); MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); +static unsigned int i2c_udelay = 5; +module_param(i2c_udelay, int, 0644); +MODULE_PARM_DESC(i2c_udelay,"i2c delay at insmod time, in usecs " + "(should be 5 or higher). Lower value means higher bus speed."); + #define dprintk(level,fmt, arg...) if (i2c_debug >= level) \ printk(KERN_DEBUG "%s: " fmt, core->name , ## arg) @@ -198,6 +206,11 @@ static void do_i2c_scan(char *name, struct i2c_client *c) /* init + register i2c algo-bit adapter */ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) { + /* Prevents usage of invalid delay values */ + if (i2c_udelay<5) + i2c_udelay=5; + cx8800_i2c_algo_template.udelay=i2c_udelay; + memcpy(&core->i2c_adap, &cx8800_i2c_adap_template, sizeof(core->i2c_adap)); memcpy(&core->i2c_algo, &cx8800_i2c_algo_template, diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index c2556464899..83ebf7a3c05 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -107,7 +107,15 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) (gpio & ir->mask_keydown) ? " down" : "", (gpio & ir->mask_keyup) ? " up" : ""); - if (ir->mask_keydown) { + if (ir->core->board == CX88_BOARD_NORWOOD_MICRO) { + u32 gpio_key = cx_read(MO_GP0_IO); + + data = (data << 4) | ((gpio_key & 0xf0) >> 4); + + ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_nokey(ir->input, &ir->ir); + + } else if (ir->mask_keydown) { /* bit set on keydown */ if (gpio & ir->mask_keydown) { ir_input_keydown(ir->input, &ir->ir, data, data); @@ -187,6 +195,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: case CX88_BOARD_HAUPPAUGE_HVR1100: + case CX88_BOARD_HAUPPAUGE_HVR1300: ir_codes = ir_codes_hauppauge_new; ir_type = IR_TYPE_RC5; ir->sampling = 1; @@ -248,6 +257,13 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir_type = IR_TYPE_PD; ir->sampling = 0xff00; /* address */ break; + case CX88_BOARD_NORWOOD_MICRO: + ir_codes = ir_codes_norwood; + ir->gpio_addr = MO_GP1_IO; + ir->mask_keycode = 0x0e; + ir->mask_keyup = 0x80; + ir->polling = 50; /* ms */ + break; case CX88_BOARD_NPGTECH_REALTV_TOP10FM: ir_codes = ir_codes_npgtech; ir->gpio_addr = MO_GP0_IO; @@ -402,6 +418,7 @@ void cx88_ir_irq(struct cx88_core *core) case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: case CX88_BOARD_HAUPPAUGE_HVR1100: + case CX88_BOARD_HAUPPAUGE_HVR1300: ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7); ir_dprintk("biphase decoded: %x\n", ircode); if ((ircode & 0xfffff000) != 0x3000) diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c index 5785c348157..741e7c5e69e 100644 --- a/drivers/media/video/cx88/cx88-tvaudio.c +++ b/drivers/media/video/cx88/cx88-tvaudio.c @@ -52,7 +52,6 @@ #include <linux/init.h> #include <linux/smp_lock.h> #include <linux/delay.h> -#include <linux/config.h> #include <linux/kthread.h> #include "cx88.h" @@ -138,14 +137,10 @@ static void set_audio_finish(struct cx88_core *core, u32 ctl) { u32 volume; -#ifndef CONFIG_VIDEO_CX88_ALSA /* restart dma; This avoids buzz in NICAM and is good in others */ cx88_stop_audio_dma(core); -#endif cx_write(AUD_RATE_THRES_DMD, 0x000000C0); -#ifndef CONFIG_VIDEO_CX88_ALSA cx88_start_audio_dma(core); -#endif if (cx88_boards[core->board].blackbird) { /* sets sound input from external adc */ diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 94c92bacc34..fbc79e9842a 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -497,6 +497,7 @@ static int start_video_dma(struct cx8800_dev *dev, return 0; } +#ifdef CONFIG_PM static int stop_video_dma(struct cx8800_dev *dev) { struct cx88_core *core = dev->core; @@ -512,6 +513,7 @@ static int stop_video_dma(struct cx8800_dev *dev) cx_clear(MO_VID_INTMSK, 0x0f0011); return 0; } +#endif static int restart_video_queue(struct cx8800_dev *dev, struct cx88_dmaqueue *q) @@ -2017,6 +2019,7 @@ static void __devexit cx8800_finidev(struct pci_dev *pci_dev) kfree(dev); } +#ifdef CONFIG_PM static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) { struct cx8800_dev *dev = pci_get_drvdata(pci_dev); @@ -2092,6 +2095,7 @@ static int cx8800_resume(struct pci_dev *pci_dev) return 0; } +#endif /* ----------------------------------------------------------- */ @@ -2112,9 +2116,10 @@ static struct pci_driver cx8800_pci_driver = { .id_table = cx8800_pci_tbl, .probe = cx8800_initdev, .remove = __devexit_p(cx8800_finidev), - +#ifdef CONFIG_PM .suspend = cx8800_suspend, .resume = cx8800_resume, +#endif }; static int cx8800_init(void) diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index e7810955dd4..89f12e273b7 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -197,6 +197,10 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_NPGTECH_REALTV_TOP10FM 50 #define CX88_BOARD_WINFAST_DTV2000H 51 #define CX88_BOARD_GENIATECH_DVBS 52 +#define CX88_BOARD_HAUPPAUGE_HVR3000 53 +#define CX88_BOARD_NORWOOD_MICRO 54 +#define CX88_BOARD_TE_DTV_250_OEM_SWANN 55 +#define CX88_BOARD_HAUPPAUGE_HVR1300 56 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, @@ -545,6 +549,7 @@ extern const unsigned int cx88_idcount; extern void cx88_card_list(struct cx88_core *core, struct pci_dev *pci); extern void cx88_card_setup(struct cx88_core *core); +extern void cx88_card_setup_pre_i2c(struct cx88_core *core); /* ----------------------------------------------------------- */ /* cx88-tvaudio.c */ diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index dfb15bfb83d..9285a58e47a 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -5,7 +5,8 @@ config VIDEO_EM28XX select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_IR - select VIDEO_SAA711X + select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO ---help--- This is a video4linux driver for Empia 28xx based TV cards. diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 2a461dde480..20df657b70c 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -174,7 +174,7 @@ static void em28xx_config_i2c(struct em28xx *dev) route.input = INPUT(dev->ctl_input)->vmux; route.output = 0; - em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, NULL); + em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, 0); em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL); diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c index 3bf7ac4f528..c1a377f797d 100644 --- a/drivers/media/video/ks0127.c +++ b/drivers/media/video/ks0127.c @@ -832,8 +832,7 @@ static int ks0127_detach(struct i2c_client *client) static int __devinit ks0127_init_module(void) { init_reg_defaults(); - i2c_add_driver(&i2c_driver_ks0127); - return 0; + return i2c_add_driver(&i2c_driver_ks0127); } static void __devexit ks0127_cleanup_module(void) diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig index 7e727fe14b3..a52171ef613 100644 --- a/drivers/media/video/pvrusb2/Kconfig +++ b/drivers/media/video/pvrusb2/Kconfig @@ -5,8 +5,6 @@ config VIDEO_PVRUSB2 select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_CX2341X - select VIDEO_SAA711X - select VIDEO_MSP3400 ---help--- This is a video4linux driver for Conexant 23416 based usb2 personal video recorder devices. @@ -14,6 +12,20 @@ config VIDEO_PVRUSB2 To compile this driver as a module, choose M here: the module will be called pvrusb2 +config VIDEO_PVRUSB2_29XXX + bool "Hauppauge WinTV-PVR USB2 support for 29xxx model series" + depends on VIDEO_PVRUSB2 && EXPERIMENTAL + select VIDEO_SAA711X + select VIDEO_MSP3400 + ---help--- + This option enables support for WinTV-PVR USB2 devices whose + model number is of the form "29xxx" (leading prefix of "29" + followed by 3 digits). + To see if you may need this option, examine the white + sticker on the underside of your device. + + If you are in doubt, say Y. + config VIDEO_PVRUSB2_24XXX bool "Hauppauge WinTV-PVR USB2 support for 24xxx model series" depends on VIDEO_PVRUSB2 && EXPERIMENTAL @@ -60,3 +72,5 @@ config VIDEO_PVRUSB2_DEBUGIFC You do not need to select this option unless you plan on debugging the driver or performing a manual firmware extraction. + + If you are in doubt, say N. diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile index 02e414210da..69b3e43cd0e 100644 --- a/drivers/media/video/pvrusb2/Makefile +++ b/drivers/media/video/pvrusb2/Makefile @@ -1,10 +1,6 @@ obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o -obj-pvrusb2-24xxx-$(CONFIG_VIDEO_PVRUSB2_24XXX) := \ - pvrusb2-cx2584x-v4l.o \ - pvrusb2-wm8775.o - pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \ pvrusb2-encoder.o pvrusb2-video-v4l.o \ @@ -12,7 +8,7 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \ pvrusb2-ctrl.o pvrusb2-std.o \ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ - $(obj-pvrusb2-24xxx-y) \ + pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c index fb6198f1df9..c77de859cc8 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c +++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -43,12 +43,17 @@ int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val) if (cptr->info->type == pvr2_ctl_bitmask) { mask &= cptr->info->def.type_bitmask.valid_bits; } else if (cptr->info->type == pvr2_ctl_int) { - if (val < cptr->info->def.type_int.min_value) { - break; + int lim; + lim = cptr->info->def.type_int.min_value; + if (cptr->info->get_min_value) { + cptr->info->get_min_value(cptr,&lim); } - if (val > cptr->info->def.type_int.max_value) { - break; + if (val < lim) break; + lim = cptr->info->def.type_int.max_value; + if (cptr->info->get_max_value) { + cptr->info->get_max_value(cptr,&lim); } + if (val > lim) break; } else if (cptr->info->type == pvr2_ctl_enum) { if (val >= cptr->info->def.type_enum.count) { break; @@ -91,7 +96,9 @@ int pvr2_ctrl_get_max(struct pvr2_ctrl *cptr) int ret = 0; if (!cptr) return 0; LOCK_TAKE(cptr->hdw->big_lock); do { - if (cptr->info->type == pvr2_ctl_int) { + if (cptr->info->get_max_value) { + cptr->info->get_max_value(cptr,&ret); + } else if (cptr->info->type == pvr2_ctl_int) { ret = cptr->info->def.type_int.max_value; } } while(0); LOCK_GIVE(cptr->hdw->big_lock); @@ -105,7 +112,9 @@ int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr) int ret = 0; if (!cptr) return 0; LOCK_TAKE(cptr->hdw->big_lock); do { - if (cptr->info->type == pvr2_ctl_int) { + if (cptr->info->get_min_value) { + cptr->info->get_min_value(cptr,&ret); + } else if (cptr->info->type == pvr2_ctl_int) { ret = cptr->info->def.type_int.min_value; } } while(0); LOCK_GIVE(cptr->hdw->big_lock); diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index c80c26be6e4..df8feac16ae 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -221,7 +221,7 @@ static unsigned int decoder_describe(struct pvr2_v4l_cx2584x *ctxt, static void decoder_reset(struct pvr2_v4l_cx2584x *ctxt) { int ret; - ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_RESET,NULL); + ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_RESET,0); pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_reset (ret=%d)",ret); } diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 18a7073501c..c94f97b7939 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -317,7 +317,7 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Failed to configure cx32416"); + "Failed to configure cx23416"); return ret; } @@ -337,7 +337,7 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Failed to initialize cx32416 video input"); + "Failed to initialize cx23416 video input"); return ret; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 0d6dc33ca32..34b08fbcc6e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -33,7 +33,6 @@ */ -#include <linux/config.h> #include <linux/videodev2.h> #include <linux/i2c.h> #include <linux/mutex.h> @@ -41,32 +40,6 @@ #include "pvrusb2-io.h" #include <media/cx2341x.h> -/* Legal values for the SRATE state variable */ -#define PVR2_CVAL_SRATE_48 0 -#define PVR2_CVAL_SRATE_44_1 1 - -/* Legal values for the AUDIOBITRATE state variable */ -#define PVR2_CVAL_AUDIOBITRATE_384 0 -#define PVR2_CVAL_AUDIOBITRATE_320 1 -#define PVR2_CVAL_AUDIOBITRATE_256 2 -#define PVR2_CVAL_AUDIOBITRATE_224 3 -#define PVR2_CVAL_AUDIOBITRATE_192 4 -#define PVR2_CVAL_AUDIOBITRATE_160 5 -#define PVR2_CVAL_AUDIOBITRATE_128 6 -#define PVR2_CVAL_AUDIOBITRATE_112 7 -#define PVR2_CVAL_AUDIOBITRATE_96 8 -#define PVR2_CVAL_AUDIOBITRATE_80 9 -#define PVR2_CVAL_AUDIOBITRATE_64 10 -#define PVR2_CVAL_AUDIOBITRATE_56 11 -#define PVR2_CVAL_AUDIOBITRATE_48 12 -#define PVR2_CVAL_AUDIOBITRATE_32 13 -#define PVR2_CVAL_AUDIOBITRATE_VBR 14 - -/* Legal values for the AUDIOEMPHASIS state variable */ -#define PVR2_CVAL_AUDIOEMPHASIS_NONE 0 -#define PVR2_CVAL_AUDIOEMPHASIS_50_15 1 -#define PVR2_CVAL_AUDIOEMPHASIS_CCITT 2 - /* Legal values for PVR2_CID_HSM */ #define PVR2_CVAL_HSM_FAIL 0 #define PVR2_CVAL_HSM_FULL 1 @@ -107,6 +80,8 @@ struct pvr2_ctl_info { /* Control's implementation */ pvr2_ctlf_get_value get_value; /* Get its value */ + pvr2_ctlf_get_value get_min_value; /* Get minimum allowed value */ + pvr2_ctlf_get_value get_max_value; /* Get maximum allowed value */ pvr2_ctlf_set_value set_value; /* Set its value */ pvr2_ctlf_val_to_sym val_to_sym; /* Custom convert value->symbol */ pvr2_ctlf_sym_to_val sym_to_val; /* Custom convert symbol->value */ @@ -193,9 +168,7 @@ struct pvr2_decoder_ctrl { /* Known major hardware variants, keyed from device ID */ #define PVR2_HDW_TYPE_29XXX 0 -#ifdef CONFIG_VIDEO_PVRUSB2_24XXX #define PVR2_HDW_TYPE_24XXX 1 -#endif typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16); #define PVR2_I2C_FUNC_CNT 128 diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index be1e5cc7808..88604365777 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -38,9 +38,7 @@ struct usb_device_id pvr2_device_table[] = { [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) }, -#ifdef CONFIG_VIDEO_PVRUSB2_24XXX [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) }, -#endif { } }; @@ -48,9 +46,7 @@ MODULE_DEVICE_TABLE(usb, pvr2_device_table); static const char *pvr2_device_names[] = { [PVR2_HDW_TYPE_29XXX] = "WinTV PVR USB2 Model Category 29xxxx", -#ifdef CONFIG_VIDEO_PVRUSB2_24XXX [PVR2_HDW_TYPE_24XXX] = "WinTV PVR USB2 Model Category 24xxxx", -#endif }; struct pvr2_string_table { @@ -58,14 +54,12 @@ struct pvr2_string_table { unsigned int cnt; }; -#ifdef CONFIG_VIDEO_PVRUSB2_24XXX // Names of other client modules to request for 24xxx model hardware static const char *pvr2_client_24xxx[] = { "cx25840", "tuner", "wm8775", }; -#endif // Names of other client modules to request for 29xxx model hardware static const char *pvr2_client_29xxx[] = { @@ -79,12 +73,10 @@ static struct pvr2_string_table pvr2_client_lists[] = { pvr2_client_29xxx, sizeof(pvr2_client_29xxx)/sizeof(pvr2_client_29xxx[0]), }, -#ifdef CONFIG_VIDEO_PVRUSB2_24XXX [PVR2_HDW_TYPE_24XXX] = { pvr2_client_24xxx, sizeof(pvr2_client_24xxx)/sizeof(pvr2_client_24xxx[0]), }, -#endif }; static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL}; @@ -221,14 +213,15 @@ static const struct pvr2_mpeg_ids mpeg_ids[] = { }; #define MPEGDEF_COUNT (sizeof(mpeg_ids)/sizeof(mpeg_ids[0])) + static const char *control_values_srate[] = { - [PVR2_CVAL_SRATE_48] = "48KHz", - [PVR2_CVAL_SRATE_44_1] = "44.1KHz", + [V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100] = "44.1 kHz", + [V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000] = "48 kHz", + [V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000] = "32 kHz", }; - static const char *control_values_input[] = { [PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/ [PVR2_CVAL_INPUT_RADIO] = "radio", @@ -362,6 +355,50 @@ static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v) return 0; } +static int ctrl_hres_max_get(struct pvr2_ctrl *cptr,int *vp) +{ + /* If we're dealing with a 24xxx device, force the horizontal + maximum to be 720 no matter what, since we can't get the device + to work properly with any other value. Otherwise just return + the normal value. */ + *vp = cptr->info->def.type_int.max_value; + if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) *vp = 720; + return 0; +} + +static int ctrl_hres_min_get(struct pvr2_ctrl *cptr,int *vp) +{ + /* If we're dealing with a 24xxx device, force the horizontal + minimum to be 720 no matter what, since we can't get the device + to work properly with any other value. Otherwise just return + the normal value. */ + *vp = cptr->info->def.type_int.min_value; + if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) *vp = 720; + return 0; +} + +static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp) +{ + /* Actual maximum depends on the video standard in effect. */ + if (cptr->hdw->std_mask_cur & V4L2_STD_525_60) { + *vp = 480; + } else { + *vp = 576; + } + return 0; +} + +static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp) +{ + /* Actual minimum depends on device type. */ + if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { + *vp = 75; + } else { + *vp = 17; + } + return 0; +} + static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr) { return cptr->hdw->enc_stale != 0; @@ -719,19 +756,27 @@ static const struct pvr2_ctl_info control_defs[] = { .internal_id = PVR2_CID_HRES, .default_value = 720, DEFREF(res_hor), - DEFINT(320,720), + DEFINT(19,720), + /* Hook in check for clamp on horizontal resolution in + order to avoid unsolved problem involving cx25840. */ + .get_max_value = ctrl_hres_max_get, + .get_min_value = ctrl_hres_min_get, },{ .desc = "Vertical capture resolution", .name = "resolution_ver", .internal_id = PVR2_CID_VRES, .default_value = 480, DEFREF(res_ver), - DEFINT(200,625), + DEFINT(17,576), + /* Hook in check for video standard and adjust maximum + depending on the standard. */ + .get_max_value = ctrl_vres_max_get, + .get_min_value = ctrl_vres_min_get, },{ .v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, - .desc = "Sample rate", + .default_value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, + .desc = "Audio Sampling Frequency", .name = "srate", - .default_value = PVR2_CVAL_SRATE_48, DEFREF(srate), DEFENUM(control_values_srate), },{ @@ -935,22 +980,18 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) static const char *fw_files_29xxx[] = { "v4l-pvrusb2-29xxx-01.fw", }; -#ifdef CONFIG_VIDEO_PVRUSB2_24XXX static const char *fw_files_24xxx[] = { "v4l-pvrusb2-24xxx-01.fw", }; -#endif static const struct pvr2_string_table fw_file_defs[] = { [PVR2_HDW_TYPE_29XXX] = { fw_files_29xxx, sizeof(fw_files_29xxx)/sizeof(fw_files_29xxx[0]), }, -#ifdef CONFIG_VIDEO_PVRUSB2_24XXX [PVR2_HDW_TYPE_24XXX] = { fw_files_24xxx, sizeof(fw_files_24xxx)/sizeof(fw_files_24xxx[0]), }, -#endif }; hdw->fw1_state = FW1_STATE_FAILED; // default result @@ -2237,11 +2278,14 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) } if (hdw->std_dirty || + hdw->enc_stale || + hdw->srate_dirty || + hdw->res_ver_dirty || + hdw->res_hor_dirty || 0) { /* If any of this changes, then the encoder needs to be reconfigured, and we need to reset the stream. */ stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG); - stale_subsys_mask |= hdw->subsys_stream_mask; } if (hdw->srate_dirty) { diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index fbe6039aeb6..ed3e8105292 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -26,10 +26,8 @@ #include "pvrusb2-audio.h" #include "pvrusb2-tuner.h" #include "pvrusb2-video-v4l.h" -#ifdef CONFIG_VIDEO_PVRUSB2_24XXX #include "pvrusb2-cx2584x-v4l.h" #include "pvrusb2-wm8775.h" -#endif #define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) @@ -71,7 +69,6 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) return; } } -#ifdef CONFIG_VIDEO_PVRUSB2_24XXX if (id == I2C_DRIVERID_CX25840) { if (pvr2_i2c_cx2584x_v4l_setup(hdw,cp)) { return; @@ -82,7 +79,6 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) return; } } -#endif if (id == I2C_DRIVERID_SAA711X) { if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) { return; diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index 8a9933dec91..05ea17afe90 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -31,7 +31,7 @@ static void set_standard(struct pvr2_hdw *hdw) v4l2_std_id vs; vs = hdw->std_mask_cur; pvr2_trace(PVR2_TRACE_CHIPS, - "i2c v4l2 set_standard(0x%llx)",(__u64)vs); + "i2c v4l2 set_standard(0x%llx)",(long long unsigned)vs); pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs); } diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 7fca4798227..3b9012f8e38 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -185,8 +185,6 @@ static int pvr2_i2c_basic_op(struct pvr2_hdw *hdw, } } -#ifdef CONFIG_VIDEO_PVRUSB2_24XXX - /* This is a special entry point that is entered if an I2C operation is attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this part doesn't work, but we know it is really there. So let's look for @@ -289,8 +287,6 @@ static int i2c_hack_cx25840(struct pvr2_hdw *hdw, return -EIO; } -#endif /* CONFIG_VIDEO_PVRUSB2_24XXX */ - /* This is a very, very limited I2C adapter implementation. We can only support what we actually know will work on the device... */ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, @@ -897,14 +893,12 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) hdw->i2c_func[idx] = pvr2_i2c_basic_op; } -#ifdef CONFIG_VIDEO_PVRUSB2_24XXX // If however we're dealing with new hardware, insert some hacks in // the I2C transfer stack to let things work better. if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { hdw->i2c_func[0x1b] = i2c_hack_wm8775; hdw->i2c_func[0x44] = i2c_hack_cx25840; } -#endif // Configure the adapter and set up everything else related to it. memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap)); diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c index 8f1a5afdd34..e976c484c05 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -20,7 +20,6 @@ * */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index d1dda5caf40..c294f46db9b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -19,7 +19,6 @@ * */ -#include <linux/config.h> #include <linux/string.h> #include <linux/slab.h> #include <asm/semaphore.h> @@ -40,8 +39,6 @@ struct pvr2_sysfs { #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ struct pvr2_sysfs_ctl_item *item_first; struct pvr2_sysfs_ctl_item *item_last; - struct sysfs_ops kops; - struct kobj_type ktype; struct class_device_attribute attr_v4l_minor_number; struct class_device_attribute attr_unit_number; int v4l_minor_number_created_ok; diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 0caf70b8c0d..3608c2f81df 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -459,18 +459,26 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, ret = 0; switch(vf->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + int lmin,lmax; + struct pvr2_ctrl *hcp,*vcp; int h = vf->fmt.pix.height; int w = vf->fmt.pix.width; - - if (h < 200) { - h = 200; - } else if (h > 625) { - h = 625; + hcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES); + vcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES); + + lmin = pvr2_ctrl_get_min(hcp); + lmax = pvr2_ctrl_get_max(hcp); + if (w < lmin) { + w = lmin; + } else if (w > lmax) { + w = lmax; } - if (w < 320) { - w = 320; - } else if (w > 720) { - w = 720; + lmin = pvr2_ctrl_get_min(vcp); + lmax = pvr2_ctrl_get_max(vcp); + if (h < lmin) { + h = lmin; + } else if (h > lmax) { + h = lmax; } memcpy(vf, &pvr_format[PVR_FORMAT_PIX], @@ -479,14 +487,8 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, vf->fmt.pix.height = h; if (cmd == VIDIOC_S_FMT) { - pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, - PVR2_CID_HRES), - vf->fmt.pix.width); - pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, - PVR2_CID_VRES), - vf->fmt.pix.height); + pvr2_ctrl_set_value(hcp,vf->fmt.pix.width); + pvr2_ctrl_set_value(vcp,vf->fmt.pix.height); } } break; case V4L2_BUF_TYPE_VBI_CAPTURE: diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c index 59a187272c8..77bb940a1a4 100644 --- a/drivers/media/video/saa5246a.c +++ b/drivers/media/video/saa5246a.c @@ -830,7 +830,6 @@ static struct video_device saa_template = .owner = THIS_MODULE, .name = IF_NAME, .type = VID_TYPE_TELETEXT, - .hardware = VID_HARDWARE_SAA5249, .fops = &saa_fops, .release = video_device_release, .minor = -1, diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c index 19a8d65699f..bb3fb4387f6 100644 --- a/drivers/media/video/saa5249.c +++ b/drivers/media/video/saa5249.c @@ -713,7 +713,6 @@ static struct video_device saa_template = .owner = THIS_MODULE, .name = IF_NAME, .type = VID_TYPE_TELETEXT, /*| VID_TYPE_TUNER ?? */ - .hardware = VID_HARDWARE_SAA5249, .fops = &saa_fops, }; diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index b59c1171727..974179d4d38 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -1,4 +1,6 @@ -/* saa7115 - Philips SAA7113/SAA7114/SAA7115 video decoder driver +/* saa711x - Philips SAA711x video decoder driver + * This driver can work with saa7111, saa7111a, saa7113, saa7114, + * saa7115 and saa7118. * * Based on saa7114 driver by Maxim Yevtyushkin, which is based on * the saa7111 driver by Dave Perks. @@ -16,7 +18,9 @@ * (2/17/2003) * * VBI support (2004) and cleanups (2005) by Hans Verkuil <hverkuil@xs4all.nl> - * SAA7113 support by Mauro Carvalho Chehab <mchehab@infradead.org> + * + * Copyright (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org> + * SAA7111, SAA7113 and SAA7118 support * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -33,6 +37,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "saa711x_regs.h" #include <linux/kernel.h> #include <linux/module.h> @@ -43,7 +48,9 @@ #include <media/saa7115.h> #include <asm/div64.h> -MODULE_DESCRIPTION("Philips SAA7113/SAA7114/SAA7115 video decoder driver"); +#define VRES_60HZ (480+16) + +MODULE_DESCRIPTION("Philips SAA7111/SAA7113/SAA7114/SAA7115/SAA7118 video decoder driver"); MODULE_AUTHOR( "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, " "Hans Verkuil, Mauro Carvalho Chehab"); MODULE_LICENSE("GPL"); @@ -54,14 +61,14 @@ module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); static unsigned short normal_i2c[] = { - 0x4a >> 1, 0x48 >> 1, /* SAA7113 */ - 0x42 >> 1, 0x40 >> 1, /* SAA7114 and SAA7115 */ + 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ + 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ I2C_CLIENT_END }; I2C_CLIENT_INSMOD; -struct saa7115_state { +struct saa711x_state { v4l2_std_id std; int input; int enable; @@ -70,6 +77,8 @@ struct saa7115_state { int contrast; int hue; int sat; + int width; + int height; enum v4l2_chip_ident ident; u32 audclk_freq; u32 crystal_freq; @@ -80,420 +89,508 @@ struct saa7115_state { /* ----------------------------------------------------------------------- */ -static inline int saa7115_write(struct i2c_client *client, u8 reg, u8 value) +static inline int saa711x_write(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); } -static int saa7115_writeregs(struct i2c_client *client, const unsigned char *regs) +/* Sanity routine to check if a register is present */ +static int saa711x_has_reg(const int id, const u8 reg) { + if (id == V4L2_IDENT_SAA7111) + return reg < 0x20 && reg != 0x01 && reg != 0x0f && + (reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e; + + /* common for saa7113/4/5/8 */ + if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f || + reg == 0xa3 || reg == 0xa7 || reg == 0xab || reg == 0xaf || (reg >= 0xb5 && reg <= 0xb7) || + reg == 0xd3 || reg == 0xd7 || reg == 0xdb || reg == 0xdf || (reg >= 0xe5 && reg <= 0xe7) || + reg == 0x82 || (reg >= 0x89 && reg <= 0x8e))) + return 0; + + switch (id) { + case V4L2_IDENT_SAA7113: + return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) && + reg != 0x5d && reg < 0x63; + case V4L2_IDENT_SAA7114: + return (reg < 0x1a || reg > 0x1e) && (reg < 0x20 || reg > 0x2f) && + (reg < 0x63 || reg > 0x7f) && reg != 0x33 && reg != 0x37 && + reg != 0x81 && reg < 0xf0; + case V4L2_IDENT_SAA7115: + return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe); + case V4L2_IDENT_SAA7118: + return (reg < 0x1a || reg > 0x1d) && (reg < 0x20 || reg > 0x22) && + (reg < 0x26 || reg > 0x28) && reg != 0x33 && reg != 0x37 && + (reg < 0x63 || reg > 0x7f) && reg != 0x81 && reg < 0xf0; + } + return 1; +} + +static int saa711x_writeregs(struct i2c_client *client, const unsigned char *regs) +{ + struct saa711x_state *state = i2c_get_clientdata(client); unsigned char reg, data; while (*regs != 0x00) { reg = *(regs++); data = *(regs++); - if (saa7115_write(client, reg, data) < 0) - return -1; + + /* According with datasheets, reserved regs should be + filled with 0 - seems better not to touch on they */ + if (saa711x_has_reg(state->ident,reg)) { + if (saa711x_write(client, reg, data) < 0) + return -1; + } else { + v4l_dbg(1, debug, client, "tried to access reserved reg 0x%02x\n", reg); + } } return 0; } -static inline int saa7115_read(struct i2c_client *client, u8 reg) +static inline int saa711x_read(struct i2c_client *client, u8 reg) { return i2c_smbus_read_byte_data(client, reg); } /* ----------------------------------------------------------------------- */ +/* SAA7111 initialization table */ +static const unsigned char saa7111_init[] = { + R_01_INC_DELAY, 0x00, /* reserved */ + + /*front end */ + R_02_INPUT_CNTL_1, 0xd0, /* FUSE=3, GUDL=2, MODE=0 */ + R_03_INPUT_CNTL_2, 0x23, /* HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, + * GAFIX=0, GAI1=256, GAI2=256 */ + R_04_INPUT_CNTL_3, 0x00, /* GAI1=256 */ + R_05_INPUT_CNTL_4, 0x00, /* GAI2=256 */ + + /* decoder */ + R_06_H_SYNC_START, 0xf3, /* HSB at 13(50Hz) / 17(60Hz) + * pixels after end of last line */ + R_07_H_SYNC_STOP, 0xe8, /* HSS seems to be needed to + * work with NTSC, too */ + R_08_SYNC_CNTL, 0xc8, /* AUFD=1, FSEL=1, EXFIL=0, + * VTRC=1, HPLL=0, VNOI=0 */ + R_09_LUMA_CNTL, 0x01, /* BYPS=0, PREF=0, BPSS=0, + * VBLB=0, UPTCV=0, APER=1 */ + R_0A_LUMA_BRIGHT_CNTL, 0x80, + R_0B_LUMA_CONTRAST_CNTL, 0x47, /* 0b - CONT=1.109 */ + R_0C_CHROMA_SAT_CNTL, 0x40, + R_0D_CHROMA_HUE_CNTL, 0x00, + R_0E_CHROMA_CNTL_1, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, + * FCTC=0, CHBW=1 */ + R_0F_CHROMA_GAIN_CNTL, 0x00, /* reserved */ + R_10_CHROMA_CNTL_2, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ + R_11_MODE_DELAY_CNTL, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, + * OEYC=1, OEHV=1, VIPB=0, COLO=0 */ + R_12_RT_SIGNAL_CNTL, 0x00, /* 12 - output control 2 */ + R_13_RT_X_PORT_OUT_CNTL, 0x00, /* 13 - output control 3 */ + R_14_ANAL_ADC_COMPAT_CNTL, 0x00, + R_15_VGATE_START_FID_CHG, 0x00, + R_16_VGATE_STOP, 0x00, + R_17_MISC_VGATE_CONF_AND_MSB, 0x00, + + 0x00, 0x00 +}; + +/* SAA7113 init codes */ +static const unsigned char saa7113_init[] = { + R_01_INC_DELAY, 0x08, + R_02_INPUT_CNTL_1, 0xc2, + R_03_INPUT_CNTL_2, 0x30, + R_04_INPUT_CNTL_3, 0x00, + R_05_INPUT_CNTL_4, 0x00, + R_06_H_SYNC_START, 0x89, + R_07_H_SYNC_STOP, 0x0d, + R_08_SYNC_CNTL, 0x88, + R_09_LUMA_CNTL, 0x01, + R_0A_LUMA_BRIGHT_CNTL, 0x80, + R_0B_LUMA_CONTRAST_CNTL, 0x47, + R_0C_CHROMA_SAT_CNTL, 0x40, + R_0D_CHROMA_HUE_CNTL, 0x00, + R_0E_CHROMA_CNTL_1, 0x01, + R_0F_CHROMA_GAIN_CNTL, 0x2a, + R_10_CHROMA_CNTL_2, 0x08, + R_11_MODE_DELAY_CNTL, 0x0c, + R_12_RT_SIGNAL_CNTL, 0x07, + R_13_RT_X_PORT_OUT_CNTL, 0x00, + R_14_ANAL_ADC_COMPAT_CNTL, 0x00, + R_15_VGATE_START_FID_CHG, 0x00, + R_16_VGATE_STOP, 0x00, + R_17_MISC_VGATE_CONF_AND_MSB, 0x00, + + 0x00, 0x00 +}; + /* If a value differs from the Hauppauge driver values, then the comment starts with 'was 0xXX' to denote the Hauppauge value. Otherwise the value is identical to what the Hauppauge driver sets. */ +/* SAA7114 and SAA7115 initialization table */ static const unsigned char saa7115_init_auto_input[] = { /* Front-End Part */ - 0x01, 0x48, /* white peak control disabled */ - 0x03, 0x20, /* was 0x30. 0x20: long vertical blanking */ - 0x04, 0x90, /* analog gain set to 0 */ - 0x05, 0x90, /* analog gain set to 0 */ + R_01_INC_DELAY, 0x48, /* white peak control disabled */ + R_03_INPUT_CNTL_2, 0x20, /* was 0x30. 0x20: long vertical blanking */ + R_04_INPUT_CNTL_3, 0x90, /* analog gain set to 0 */ + R_05_INPUT_CNTL_4, 0x90, /* analog gain set to 0 */ /* Decoder Part */ - 0x06, 0xeb, /* horiz sync begin = -21 */ - 0x07, 0xe0, /* horiz sync stop = -17 */ - 0x0a, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */ - 0x0b, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */ - 0x0c, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */ - 0x0d, 0x00, /* chrominance hue control */ - 0x0f, 0x00, /* chrominance gain control: use automicatic mode */ - 0x10, 0x06, /* chrominance/luminance control: active adaptive combfilter */ - 0x11, 0x00, /* delay control */ - 0x12, 0x9d, /* RTS0 output control: VGATE */ - 0x13, 0x80, /* X-port output control: ITU656 standard mode, RTCO output enable RTCE */ - 0x14, 0x00, /* analog/ADC/auto compatibility control */ - 0x18, 0x40, /* raw data gain 0x00 = nominal */ - 0x19, 0x80, /* raw data offset 0x80 = 0 LSB */ - 0x1a, 0x77, /* color killer level control 0x77 = recommended */ - 0x1b, 0x42, /* misc chroma control 0x42 = recommended */ - 0x1c, 0xa9, /* combfilter control 0xA9 = recommended */ - 0x1d, 0x01, /* combfilter control 0x01 = recommended */ + R_06_H_SYNC_START, 0xeb, /* horiz sync begin = -21 */ + R_07_H_SYNC_STOP, 0xe0, /* horiz sync stop = -17 */ + R_09_LUMA_CNTL, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */ + R_0A_LUMA_BRIGHT_CNTL, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */ + R_0B_LUMA_CONTRAST_CNTL, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */ + R_0C_CHROMA_SAT_CNTL, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */ + R_0D_CHROMA_HUE_CNTL, 0x00, + R_0F_CHROMA_GAIN_CNTL, 0x00, /* use automatic gain */ + R_10_CHROMA_CNTL_2, 0x06, /* chroma: active adaptive combfilter */ + R_11_MODE_DELAY_CNTL, 0x00, + R_12_RT_SIGNAL_CNTL, 0x9d, /* RTS0 output control: VGATE */ + R_13_RT_X_PORT_OUT_CNTL, 0x80, /* ITU656 standard mode, RTCO output enable RTCE */ + R_14_ANAL_ADC_COMPAT_CNTL, 0x00, + R_18_RAW_DATA_GAIN_CNTL, 0x40, /* gain 0x00 = nominal */ + R_19_RAW_DATA_OFF_CNTL, 0x80, + R_1A_COLOR_KILL_LVL_CNTL, 0x77, /* recommended value */ + R_1B_MISC_TVVCRDET, 0x42, /* recommended value */ + R_1C_ENHAN_COMB_CTRL1, 0xa9, /* recommended value */ + R_1D_ENHAN_COMB_CTRL2, 0x01, /* recommended value */ + + + R_80_GLOBAL_CNTL_1, 0x0, /* No tasks enabled at init */ /* Power Device Control */ - 0x88, 0xd0, /* reset device */ - 0x88, 0xf0, /* set device programmed, all in operational mode */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset device */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* set device programmed, all in operational mode */ 0x00, 0x00 }; +/* Used to reset saa7113, saa7114 and saa7115 */ static const unsigned char saa7115_cfg_reset_scaler[] = { - 0x87, 0x00, /* disable I-port output */ - 0x88, 0xd0, /* reset scaler */ - 0x88, 0xf0, /* activate scaler */ - 0x87, 0x01, /* enable I-port output */ + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x00, /* disable I-port output */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* enable I-port output */ 0x00, 0x00 }; /* ============== SAA7715 VIDEO templates ============= */ -static const unsigned char saa7115_cfg_60hz_fullres_x[] = { - 0xcc, 0xd0, /* hsize low (output), hor. output window size = 0x2d0 = 720 */ - 0xcd, 0x02, /* hsize hi (output) */ +static const unsigned char saa7115_cfg_60hz_video[] = { + R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ - /* Why not in 60hz-Land, too? */ - 0xd0, 0x01, /* downscale = 1 */ - 0xd8, 0x00, /* hor lum scaling 0x0400 = 1 */ - 0xd9, 0x04, - 0xdc, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */ - 0xdd, 0x02, /* H-scaling incr chroma */ + R_15_VGATE_START_FID_CHG, 0x03, + R_16_VGATE_STOP, 0x11, + R_17_MISC_VGATE_CONF_AND_MSB, 0x9c, - 0x00, 0x00 -}; -static const unsigned char saa7115_cfg_60hz_fullres_y[] = { - 0xce, 0xf8, /* vsize low (output), ver. output window size = 248 (but 60hz is 240?) */ - 0xcf, 0x00, /* vsize hi (output) */ + R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */ + R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */ - /* Why not in 60hz-Land, too? */ - 0xd5, 0x40, /* Lum contrast, nominal value = 0x40 */ - 0xd6, 0x40, /* Chroma satur. nominal value = 0x80 */ + R_5A_V_OFF_FOR_SLICER, 0x06, /* standard 60hz value for ITU656 line counting */ - 0xe0, 0x00, /* V-scaling incr luma low */ - 0xe1, 0x04, /* " hi */ - 0xe2, 0x00, /* V-scaling incr chroma low */ - 0xe3, 0x04, /* " hi */ + /* Task A */ + R_90_A_TASK_HANDLING_CNTL, 0x80, + R_91_A_X_PORT_FORMATS_AND_CONF, 0x48, + R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40, + R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84, - 0x00, 0x00 -}; + /* hoffset low (input), 0x0002 is minimum */ + R_94_A_HORIZ_INPUT_WINDOW_START, 0x01, + R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00, -static const unsigned char saa7115_cfg_60hz_video[] = { - 0x80, 0x00, /* reset tasks */ - 0x88, 0xd0, /* reset scaler */ + /* hsize low (input), 0x02d0 = 720 */ + R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, + R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, - 0x15, 0x03, /* VGATE pulse start */ - 0x16, 0x11, /* VGATE pulse stop */ - 0x17, 0x9c, /* VGATE MSB and other values */ + R_98_A_VERT_INPUT_WINDOW_START, 0x05, + R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00, - 0x08, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */ - 0x0e, 0x07, /* lots of different stuff... video autodetection is on */ + R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x0c, + R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00, - 0x5a, 0x06, /* Vertical offset, standard 60hz value for ITU656 line counting */ + R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0, + R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, - /* Task A */ - 0x90, 0x80, /* Task Handling Control */ - 0x91, 0x48, /* X-port formats/config */ - 0x92, 0x40, /* Input Ref. signal Def. */ - 0x93, 0x84, /* I-port config */ - 0x94, 0x01, /* hoffset low (input), 0x0002 is minimum */ - 0x95, 0x00, /* hoffset hi (input) */ - 0x96, 0xd0, /* hsize low (input), 0x02d0 = 720 */ - 0x97, 0x02, /* hsize hi (input) */ - 0x98, 0x05, /* voffset low (input) */ - 0x99, 0x00, /* voffset hi (input) */ - 0x9a, 0x0c, /* vsize low (input), 0x0c = 12 */ - 0x9b, 0x00, /* vsize hi (input) */ - 0x9c, 0xa0, /* hsize low (output), 0x05a0 = 1440 */ - 0x9d, 0x05, /* hsize hi (output) */ - 0x9e, 0x0c, /* vsize low (output), 0x0c = 12 */ - 0x9f, 0x00, /* vsize hi (output) */ + R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x0c, + R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, /* Task B */ - 0xc0, 0x00, /* Task Handling Control */ - 0xc1, 0x08, /* X-port formats/config */ - 0xc2, 0x00, /* Input Ref. signal Def. */ - 0xc3, 0x80, /* I-port config */ - 0xc4, 0x02, /* hoffset low (input), 0x0002 is minimum */ - 0xc5, 0x00, /* hoffset hi (input) */ - 0xc6, 0xd0, /* hsize low (input), 0x02d0 = 720 */ - 0xc7, 0x02, /* hsize hi (input) */ - 0xc8, 0x12, /* voffset low (input), 0x12 = 18 */ - 0xc9, 0x00, /* voffset hi (input) */ - 0xca, 0xf8, /* vsize low (input), 0xf8 = 248 */ - 0xcb, 0x00, /* vsize hi (input) */ - 0xcc, 0xd0, /* hsize low (output), 0x02d0 = 720 */ - 0xcd, 0x02, /* hsize hi (output) */ - - 0xf0, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */ - 0xf1, 0x05, /* low bit with 0xF0 */ - 0xf5, 0xad, /* Set pulse generator register */ - 0xf6, 0x01, - - 0x87, 0x00, /* Disable I-port output */ - 0x88, 0xd0, /* reset scaler */ - 0x80, 0x20, /* Activate only task "B", continuous mode (was 0xA0) */ - 0x88, 0xf0, /* activate scaler */ - 0x87, 0x01, /* Enable I-port output */ - 0x00, 0x00 -}; + R_C0_B_TASK_HANDLING_CNTL, 0x00, + R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08, + R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00, + R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80, -static const unsigned char saa7115_cfg_50hz_fullres_x[] = { - 0xcc, 0xd0, /* hsize low (output), 720 same as 60hz */ - 0xcd, 0x02, /* hsize hi (output) */ + /* 0x0002 is minimum */ + R_C4_B_HORIZ_INPUT_WINDOW_START, 0x02, + R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00, - 0xd0, 0x01, /* down scale = 1 */ - 0xd8, 0x00, /* hor lum scaling 0x0400 = 1 */ - 0xd9, 0x04, - 0xdc, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */ - 0xdd, 0x02, /* H-scaling incr chroma */ + /* 0x02d0 = 720 */ + R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, + R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, - 0x00, 0x00 -}; -static const unsigned char saa7115_cfg_50hz_fullres_y[] = { - 0xce, 0x20, /* vsize low (output), 0x0120 = 288 */ - 0xcf, 0x01, /* vsize hi (output) */ + /* vwindow start 0x12 = 18 */ + R_C8_B_VERT_INPUT_WINDOW_START, 0x12, + R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00, + + /* vwindow length 0xf8 = 248 */ + R_CA_B_VERT_INPUT_WINDOW_LENGTH, VRES_60HZ>>1, + R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, VRES_60HZ>>9, - 0xd5, 0x40, /* Lum contrast, nominal value = 0x40 */ - 0xd6, 0x40, /* Chroma satur. nominal value = 0x80 */ + /* hwindow 0x02d0 = 720 */ + R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0, + R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02, - 0xe0, 0x00, /* V-scaling incr luma low */ - 0xe1, 0x04, /* " hi */ - 0xe2, 0x00, /* V-scaling incr chroma low */ - 0xe3, 0x04, /* " hi */ + R_F0_LFCO_PER_LINE, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */ + R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0 */ + R_F5_PULSGEN_LINE_LENGTH, 0xad, + R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01, 0x00, 0x00 }; static const unsigned char saa7115_cfg_50hz_video[] = { - 0x80, 0x00, /* reset tasks */ - 0x88, 0xd0, /* reset scaler */ + R_80_GLOBAL_CNTL_1, 0x00, + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ - 0x15, 0x37, /* VGATE start */ - 0x16, 0x16, /* VGATE stop */ - 0x17, 0x99, /* VGATE MSB and other values */ + R_15_VGATE_START_FID_CHG, 0x37, /* VGATE start */ + R_16_VGATE_STOP, 0x16, + R_17_MISC_VGATE_CONF_AND_MSB, 0x99, - 0x08, 0x28, /* 0x28 = PAL */ - 0x0e, 0x07, /* chrominance control 1 */ + R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */ + R_0E_CHROMA_CNTL_1, 0x07, - 0x5a, 0x03, /* Vertical offset, standard 50hz value */ + R_5A_V_OFF_FOR_SLICER, 0x03, /* standard 50hz value */ /* Task A */ - 0x90, 0x81, /* Task Handling Control */ - 0x91, 0x48, /* X-port formats/config */ - 0x92, 0x40, /* Input Ref. signal Def. */ - 0x93, 0x84, /* I-port config */ + R_90_A_TASK_HANDLING_CNTL, 0x81, + R_91_A_X_PORT_FORMATS_AND_CONF, 0x48, + R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40, + R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84, + /* This is weird: the datasheet says that you should use 2 as the minimum value, */ /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */ - 0x94, 0x00, /* hoffset low (input), 0x0002 is minimum */ - 0x95, 0x00, /* hoffset hi (input) */ - 0x96, 0xd0, /* hsize low (input), 0x02d0 = 720 */ - 0x97, 0x02, /* hsize hi (input) */ - 0x98, 0x03, /* voffset low (input) */ - 0x99, 0x00, /* voffset hi (input) */ - 0x9a, 0x12, /* vsize low (input), 0x12 = 18 */ - 0x9b, 0x00, /* vsize hi (input) */ - 0x9c, 0xa0, /* hsize low (output), 0x05a0 = 1440 */ - 0x9d, 0x05, /* hsize hi (output) */ - 0x9e, 0x12, /* vsize low (output), 0x12 = 18 */ - 0x9f, 0x00, /* vsize hi (output) */ + /* hoffset low (input), 0x0002 is minimum */ + R_94_A_HORIZ_INPUT_WINDOW_START, 0x00, + R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00, + + /* hsize low (input), 0x02d0 = 720 */ + R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, + R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, + + R_98_A_VERT_INPUT_WINDOW_START, 0x03, + R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00, + + /* vsize 0x12 = 18 */ + R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x12, + R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00, + + /* hsize 0x05a0 = 1440 */ + R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0, + R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, /* hsize hi (output) */ + R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x12, /* vsize low (output), 0x12 = 18 */ + R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, /* vsize hi (output) */ /* Task B */ - 0xc0, 0x00, /* Task Handling Control */ - 0xc1, 0x08, /* X-port formats/config */ - 0xc2, 0x00, /* Input Ref. signal Def. */ - 0xc3, 0x80, /* I-port config */ - 0xc4, 0x00, /* hoffset low (input), 0x0002 is minimum. See comment at 0x94 above. */ - 0xc5, 0x00, /* hoffset hi (input) */ - 0xc6, 0xd0, /* hsize low (input), 0x02d0 = 720 */ - 0xc7, 0x02, /* hsize hi (input) */ - 0xc8, 0x16, /* voffset low (input), 0x16 = 22 */ - 0xc9, 0x00, /* voffset hi (input) */ - 0xca, 0x20, /* vsize low (input), 0x0120 = 288 */ - 0xcb, 0x01, /* vsize hi (input) */ - 0xcc, 0xd0, /* hsize low (output), 0x02d0 = 720 */ - 0xcd, 0x02, /* hsize hi (output) */ - 0xce, 0x20, /* vsize low (output), 0x0120 = 288 */ - 0xcf, 0x01, /* vsize hi (output) */ - - 0xf0, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */ - 0xf1, 0x05, /* low bit with 0xF0, (was 0x05) */ - 0xf5, 0xb0, /* Set pulse generator register */ - 0xf6, 0x01, - - 0x87, 0x00, /* Disable I-port output */ - 0x88, 0xd0, /* reset scaler (was 0xD0) */ - 0x80, 0x20, /* Activate only task "B" */ - 0x88, 0xf0, /* activate scaler */ - 0x87, 0x01, /* Enable I-port output */ + R_C0_B_TASK_HANDLING_CNTL, 0x00, + R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08, + R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00, + R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80, + + /* This is weird: the datasheet says that you should use 2 as the minimum value, */ + /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */ + /* hoffset low (input), 0x0002 is minimum. See comment above. */ + R_C4_B_HORIZ_INPUT_WINDOW_START, 0x00, + R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00, + + /* hsize 0x02d0 = 720 */ + R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, + R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, + + /* voffset 0x16 = 22 */ + R_C8_B_VERT_INPUT_WINDOW_START, 0x16, + R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00, + + /* vsize 0x0120 = 288 */ + R_CA_B_VERT_INPUT_WINDOW_LENGTH, 0x20, + R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, 0x01, + + /* hsize 0x02d0 = 720 */ + R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0, + R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02, + + R_F0_LFCO_PER_LINE, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */ + R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0, (was 0x05) */ + R_F5_PULSGEN_LINE_LENGTH, 0xb0, + R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01, + 0x00, 0x00 }; /* ============== SAA7715 VIDEO templates (end) ======= */ static const unsigned char saa7115_cfg_vbi_on[] = { - 0x80, 0x00, /* reset tasks */ - 0x88, 0xd0, /* reset scaler */ - 0x80, 0x30, /* Activate both tasks */ - 0x88, 0xf0, /* activate scaler */ - 0x87, 0x01, /* Enable I-port output */ + R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ + R_80_GLOBAL_CNTL_1, 0x30, /* Activate both tasks */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */ + 0x00, 0x00 }; static const unsigned char saa7115_cfg_vbi_off[] = { - 0x80, 0x00, /* reset tasks */ - 0x88, 0xd0, /* reset scaler */ - 0x80, 0x20, /* Activate only task "B" */ - 0x88, 0xf0, /* activate scaler */ - 0x87, 0x01, /* Enable I-port output */ - 0x00, 0x00 -}; + R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ + R_80_GLOBAL_CNTL_1, 0x20, /* Activate only task "B" */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */ -static const unsigned char saa7113_init_auto_input[] = { - 0x01, 0x08, /* PH7113_INCREMENT_DELAY - (1) (1) (1) (1) IDEL3 IDEL2 IDELL1 IDEL0 */ - 0x02, 0xc2, /* PH7113_ANALOG_INPUT_CONTR_1 - FUSE1 FUSE0 GUDL1 GUDL0 MODE3 MODE2 MODE1 MODE0 */ - 0x03, 0x30, /* PH7113_ANALOG_INPUT_CONTR_2 - (1) HLNRS VBSL WPOFF HOLDG GAFIX GAI28 GAI18 */ - 0x04, 0x00, /* PH7113_ANALOG_INPUT_CONTR_3 - GAI17 GAI16 GAI15 GAI14 GAI13 GAI12 GAI11 GAI10 */ - 0x05, 0x00, /* PH7113_ANALOG_INPUT_CONTR_4 - GAI27 GAI26 GAI25 GAI24 GAI23 GAI22 GAI21 GAI20 */ - 0x06, 0x89, /* PH7113_HORIZONTAL_SYNC_START - HSB7 HSB6 HSB5 HSB4 HSB3 HSB2 HSB1 HSB0 */ - 0x07, 0x0d, /* PH7113_HORIZONTAL_SYNC_STOP - HSS7 HSS6 HSS5 HSS4 HSS3 HSS2 HSS1 HSS0 */ - 0x08, 0x88, /* PH7113_SYNC_CONTROL - AUFD FSEL FOET HTC1 HTC0 HPLL VNOI1 VNOI0 */ - 0x09, 0x01, /* PH7113_LUMINANCE_CONTROL - BYPS PREF BPSS1 BPSS0 VBLB UPTCV APER1 APER0 */ - 0x0a, 0x80, /* PH7113_LUMINANCE_BRIGHTNESS - BRIG7 BRIG6 BRIG5 BRIG4 BRIG3 BRIG2 BRIG1 BRIG0 */ - 0x0b, 0x47, /* PH7113_LUMINANCE_CONTRAST - CONT7 CONT6 CONT5 CONT4 CONT3 CONT2 CONT1 CONT0 */ - 0x0c, 0x40, /* PH7113_CHROMA_SATURATION - SATN7 SATN6 SATN5 SATN4 SATN3 SATN2 SATN1 SATN0 */ - 0x0d, 0x00, /* PH7113_CHROMA_HUE_CONTROL - HUEC7 HUEC6 HUEC5 HUEC4 HUEC3 HUEC2 HUEC1 HUEC0 */ - 0x0e, 0x01, /* PH7113_CHROMA_CONTROL - CDTO CSTD2 CSTD1 CSTD0 DCCF FCTC CHBW1 CHBW0 */ - 0x0f, 0x2a, /* PH7113_CHROMA_GAIN_CONTROL - ACGC CGAIN6 CGAIN5 CGAIN4 CGAIN3 CGAIN2 CGAIN1 CGAIN0 */ - 0x10, 0x08, /* PH7113_FORMAT_DELAY_CONTROL - OFTS1 OFTS0 HDEL1 HDEL0 VRLN YDEL2 YDEL1 YDEL0 */ - 0x11, 0x0c, /* PH7113_OUTPUT_CONTROL_1 - GPSW1 CM99 GPSW0 HLSEL OEYC OERT VIPB COLO */ - 0x12, 0x07, /* PH7113_OUTPUT_CONTROL_2 - RTSE13 RTSE12 RTSE11 RTSE10 RTSE03 RTSE02 RTSE01 RTSE00 */ - 0x13, 0x00, /* PH7113_OUTPUT_CONTROL_3 - ADLSB (1) (1) OLDSB FIDP (1) AOSL1 AOSL0 */ - 0x14, 0x00, /* RESERVED 14 - (1) (1) (1) (1) (1) (1) (1) (1) */ - 0x15, 0x00, /* PH7113_V_GATE1_START - VSTA7 VSTA6 VSTA5 VSTA4 VSTA3 VSTA2 VSTA1 VSTA0 */ - 0x16, 0x00, /* PH7113_V_GATE1_STOP - VSTO7 VSTO6 VSTO5 VSTO4 VSTO3 VSTO2 VSTO1 VSTO0 */ - 0x17, 0x00, /* PH7113_V_GATE1_MSB - (1) (1) (1) (1) (1) (1) VSTO8 VSTA8 */ 0x00, 0x00 }; + static const unsigned char saa7115_init_misc[] = { - 0x81, 0x01, /* reg 0x15,0x16 define blanking window */ - 0x82, 0x00, - 0x83, 0x01, /* I port settings */ - 0x84, 0x20, - 0x85, 0x21, - 0x86, 0xc5, - 0x87, 0x01, + R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F, 0x01, + R_83_X_PORT_I_O_ENA_AND_OUT_CLK, 0x01, + R_84_I_PORT_SIGNAL_DEF, 0x20, + R_85_I_PORT_SIGNAL_POLAR, 0x21, + R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT, 0xc5, + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Task A */ - 0xa0, 0x01, /* down scale = 1 */ - 0xa1, 0x00, /* prescale accumulation length = 1 */ - 0xa2, 0x00, /* dc gain and fir prefilter control */ - 0xa4, 0x80, /* Lum Brightness, nominal value = 0x80 */ - 0xa5, 0x40, /* Lum contrast, nominal value = 0x40 */ - 0xa6, 0x40, /* Chroma satur. nominal value = 0x80 */ - 0xa8, 0x00, /* hor lum scaling 0x0200 = 2 zoom */ - 0xa9, 0x02, /* note: 2 x zoom ensures that VBI lines have same length as video lines. */ - 0xaa, 0x00, /* H-phase offset Luma = 0 */ - 0xac, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */ - 0xad, 0x01, /* H-scaling incr chroma */ - 0xae, 0x00, /* H-phase offset chroma. must be offset luma / 2 */ - - 0xb0, 0x00, /* V-scaling incr luma low */ - 0xb1, 0x04, /* " hi */ - 0xb2, 0x00, /* V-scaling incr chroma low */ - 0xb3, 0x04, /* " hi */ - 0xb4, 0x01, /* V-scaling mode control */ - 0xb8, 0x00, /* V-phase offset chroma 00 */ - 0xb9, 0x00, /* V-phase offset chroma 01 */ - 0xba, 0x00, /* V-phase offset chroma 10 */ - 0xbb, 0x00, /* V-phase offset chroma 11 */ - 0xbc, 0x00, /* V-phase offset luma 00 */ - 0xbd, 0x00, /* V-phase offset luma 01 */ - 0xbe, 0x00, /* V-phase offset luma 10 */ - 0xbf, 0x00, /* V-phase offset luma 11 */ + R_A0_A_HORIZ_PRESCALING, 0x01, + R_A1_A_ACCUMULATION_LENGTH, 0x00, + R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00, + + /* Configure controls at nominal value*/ + R_A4_A_LUMA_BRIGHTNESS_CNTL, 0x80, + R_A5_A_LUMA_CONTRAST_CNTL, 0x40, + R_A6_A_CHROMA_SATURATION_CNTL, 0x40, + + /* note: 2 x zoom ensures that VBI lines have same length as video lines. */ + R_A8_A_HORIZ_LUMA_SCALING_INC, 0x00, + R_A9_A_HORIZ_LUMA_SCALING_INC_MSB, 0x02, + + R_AA_A_HORIZ_LUMA_PHASE_OFF, 0x00, + + /* must be horiz lum scaling / 2 */ + R_AC_A_HORIZ_CHROMA_SCALING_INC, 0x00, + R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB, 0x01, + + /* must be offset luma / 2 */ + R_AE_A_HORIZ_CHROMA_PHASE_OFF, 0x00, + + R_B0_A_VERT_LUMA_SCALING_INC, 0x00, + R_B1_A_VERT_LUMA_SCALING_INC_MSB, 0x04, + + R_B2_A_VERT_CHROMA_SCALING_INC, 0x00, + R_B3_A_VERT_CHROMA_SCALING_INC_MSB, 0x04, + + R_B4_A_VERT_SCALING_MODE_CNTL, 0x01, + + R_B8_A_VERT_CHROMA_PHASE_OFF_00, 0x00, + R_B9_A_VERT_CHROMA_PHASE_OFF_01, 0x00, + R_BA_A_VERT_CHROMA_PHASE_OFF_10, 0x00, + R_BB_A_VERT_CHROMA_PHASE_OFF_11, 0x00, + + R_BC_A_VERT_LUMA_PHASE_OFF_00, 0x00, + R_BD_A_VERT_LUMA_PHASE_OFF_01, 0x00, + R_BE_A_VERT_LUMA_PHASE_OFF_10, 0x00, + R_BF_A_VERT_LUMA_PHASE_OFF_11, 0x00, /* Task B */ - 0xd0, 0x01, /* down scale = 1 */ - 0xd1, 0x00, /* prescale accumulation length = 1 */ - 0xd2, 0x00, /* dc gain and fir prefilter control */ - 0xd4, 0x80, /* Lum Brightness, nominal value = 0x80 */ - 0xd5, 0x40, /* Lum contrast, nominal value = 0x40 */ - 0xd6, 0x40, /* Chroma satur. nominal value = 0x80 */ - 0xd8, 0x00, /* hor lum scaling 0x0400 = 1 */ - 0xd9, 0x04, - 0xda, 0x00, /* H-phase offset Luma = 0 */ - 0xdc, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */ - 0xdd, 0x02, /* H-scaling incr chroma */ - 0xde, 0x00, /* H-phase offset chroma. must be offset luma / 2 */ - - 0xe0, 0x00, /* V-scaling incr luma low */ - 0xe1, 0x04, /* " hi */ - 0xe2, 0x00, /* V-scaling incr chroma low */ - 0xe3, 0x04, /* " hi */ - 0xe4, 0x01, /* V-scaling mode control */ - 0xe8, 0x00, /* V-phase offset chroma 00 */ - 0xe9, 0x00, /* V-phase offset chroma 01 */ - 0xea, 0x00, /* V-phase offset chroma 10 */ - 0xeb, 0x00, /* V-phase offset chroma 11 */ - 0xec, 0x00, /* V-phase offset luma 00 */ - 0xed, 0x00, /* V-phase offset luma 01 */ - 0xee, 0x00, /* V-phase offset luma 10 */ - 0xef, 0x00, /* V-phase offset luma 11 */ - - 0xf2, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */ - 0xf3, 0x46, - 0xf4, 0x00, - 0xf7, 0x4b, /* not the recommended settings! */ - 0xf8, 0x00, - 0xf9, 0x4b, - 0xfa, 0x00, - 0xfb, 0x4b, - 0xff, 0x88, /* PLL2 lock detection settings: 71 lines 50% phase error */ + R_D0_B_HORIZ_PRESCALING, 0x01, + R_D1_B_ACCUMULATION_LENGTH, 0x00, + R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00, + + /* Configure controls at nominal value*/ + R_D4_B_LUMA_BRIGHTNESS_CNTL, 0x80, + R_D5_B_LUMA_CONTRAST_CNTL, 0x40, + R_D6_B_CHROMA_SATURATION_CNTL, 0x40, + + /* hor lum scaling 0x0400 = 1 */ + R_D8_B_HORIZ_LUMA_SCALING_INC, 0x00, + R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, 0x04, + + R_DA_B_HORIZ_LUMA_PHASE_OFF, 0x00, + + /* must be hor lum scaling / 2 */ + R_DC_B_HORIZ_CHROMA_SCALING, 0x00, + R_DD_B_HORIZ_CHROMA_SCALING_MSB, 0x02, + + /* must be offset luma / 2 */ + R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA, 0x00, + + R_E0_B_VERT_LUMA_SCALING_INC, 0x00, + R_E1_B_VERT_LUMA_SCALING_INC_MSB, 0x04, + + R_E2_B_VERT_CHROMA_SCALING_INC, 0x00, + R_E3_B_VERT_CHROMA_SCALING_INC_MSB, 0x04, + + R_E4_B_VERT_SCALING_MODE_CNTL, 0x01, + + R_E8_B_VERT_CHROMA_PHASE_OFF_00, 0x00, + R_E9_B_VERT_CHROMA_PHASE_OFF_01, 0x00, + R_EA_B_VERT_CHROMA_PHASE_OFF_10, 0x00, + R_EB_B_VERT_CHROMA_PHASE_OFF_11, 0x00, + + R_EC_B_VERT_LUMA_PHASE_OFF_00, 0x00, + R_ED_B_VERT_LUMA_PHASE_OFF_01, 0x00, + R_EE_B_VERT_LUMA_PHASE_OFF_10, 0x00, + R_EF_B_VERT_LUMA_PHASE_OFF_11, 0x00, + + R_F2_NOMINAL_PLL2_DTO, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */ + R_F3_PLL_INCREMENT, 0x46, + R_F4_PLL2_STATUS, 0x00, + R_F7_PULSE_A_POS_MSB, 0x4b, /* not the recommended settings! */ + R_F8_PULSE_B_POS, 0x00, + R_F9_PULSE_B_POS_MSB, 0x4b, + R_FA_PULSE_C_POS, 0x00, + R_FB_PULSE_C_POS_MSB, 0x4b, + + /* PLL2 lock detection settings: 71 lines 50% phase error */ + R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES, 0x88, /* Turn off VBI */ - 0x40, 0x20, /* No framing code errors allowed. */ - 0x41, 0xff, - 0x42, 0xff, - 0x43, 0xff, - 0x44, 0xff, - 0x45, 0xff, - 0x46, 0xff, - 0x47, 0xff, - 0x48, 0xff, - 0x49, 0xff, - 0x4a, 0xff, - 0x4b, 0xff, - 0x4c, 0xff, - 0x4d, 0xff, - 0x4e, 0xff, - 0x4f, 0xff, - 0x50, 0xff, - 0x51, 0xff, - 0x52, 0xff, - 0x53, 0xff, - 0x54, 0xff, - 0x55, 0xff, - 0x56, 0xff, - 0x57, 0xff, - 0x58, 0x40, - 0x59, 0x47, - 0x5b, 0x83, - 0x5d, 0xbd, - 0x5e, 0x35, - - 0x02, 0x84, /* input tuner -> input 4, amplifier active */ - 0x09, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */ - - 0x80, 0x20, /* enable task B */ - 0x88, 0xd0, - 0x88, 0xf0, + R_40_SLICER_CNTL_1, 0x20, /* No framing code errors allowed. */ + R_41_LCR_BASE, 0xff, + R_41_LCR_BASE+1, 0xff, + R_41_LCR_BASE+2, 0xff, + R_41_LCR_BASE+3, 0xff, + R_41_LCR_BASE+4, 0xff, + R_41_LCR_BASE+5, 0xff, + R_41_LCR_BASE+6, 0xff, + R_41_LCR_BASE+7, 0xff, + R_41_LCR_BASE+8, 0xff, + R_41_LCR_BASE+9, 0xff, + R_41_LCR_BASE+10, 0xff, + R_41_LCR_BASE+11, 0xff, + R_41_LCR_BASE+12, 0xff, + R_41_LCR_BASE+13, 0xff, + R_41_LCR_BASE+14, 0xff, + R_41_LCR_BASE+15, 0xff, + R_41_LCR_BASE+16, 0xff, + R_41_LCR_BASE+17, 0xff, + R_41_LCR_BASE+18, 0xff, + R_41_LCR_BASE+19, 0xff, + R_41_LCR_BASE+20, 0xff, + R_41_LCR_BASE+21, 0xff, + R_41_LCR_BASE+22, 0xff, + R_58_PROGRAM_FRAMING_CODE, 0x40, + R_59_H_OFF_FOR_SLICER, 0x47, + R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF, 0x83, + R_5D_DID, 0xbd, + R_5E_SDID, 0x35, + + R_02_INPUT_CNTL_1, 0x84, /* input tuner -> input 4, amplifier active */ + + R_80_GLOBAL_CNTL_1, 0x20, /* enable task B */ + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, + R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, 0x00, 0x00 }; -static int saa7115_odd_parity(u8 c) +static int saa711x_odd_parity(u8 c) { c ^= (c >> 4); c ^= (c >> 2); @@ -502,7 +599,7 @@ static int saa7115_odd_parity(u8 c) return c & 1; } -static int saa7115_decode_vps(u8 * dst, u8 * p) +static int saa711x_decode_vps(u8 * dst, u8 * p) { static const u8 biphase_tbl[] = { 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, @@ -549,7 +646,7 @@ static int saa7115_decode_vps(u8 * dst, u8 * p) return err & 0xf0; } -static int saa7115_decode_wss(u8 * p) +static int saa711x_decode_wss(u8 * p) { static const int wss_bits[8] = { 0, 0, 0, 1, 0, 1, 1, 1 @@ -576,26 +673,25 @@ static int saa7115_decode_wss(u8 * p) return wss; } - -static int saa7115_set_audio_clock_freq(struct i2c_client *client, u32 freq) +static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq) { - struct saa7115_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = i2c_get_clientdata(client); u32 acpf; u32 acni; u32 hz; u64 f; u8 acc = 0; /* reg 0x3a, audio clock control */ + /* Checks for chips that don't have audio clock (saa7111, saa7113) */ + if (!saa711x_has_reg(state->ident,R_30_AUD_MAST_CLK_CYCLES_PER_FIELD)) + return 0; + v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq); /* sanity check */ if (freq < 32000 || freq > 48000) return -EINVAL; - /* The saa7113 has no audio clock */ - if (state->ident == V4L2_IDENT_SAA7113) - return 0; - /* hz is the refresh rate times 100 */ hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000; /* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */ @@ -617,22 +713,26 @@ static int saa7115_set_audio_clock_freq(struct i2c_client *client, u32 freq) if (state->apll) acc |= 0x08; - saa7115_write(client, 0x38, 0x03); - saa7115_write(client, 0x39, 0x10); - saa7115_write(client, 0x3a, acc); - saa7115_write(client, 0x30, acpf & 0xff); - saa7115_write(client, 0x31, (acpf >> 8) & 0xff); - saa7115_write(client, 0x32, (acpf >> 16) & 0x03); - saa7115_write(client, 0x34, acni & 0xff); - saa7115_write(client, 0x35, (acni >> 8) & 0xff); - saa7115_write(client, 0x36, (acni >> 16) & 0x3f); + saa711x_write(client, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); + saa711x_write(client, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); + saa711x_write(client, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); + + saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); + saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1, + (acpf >> 8) & 0xff); + saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, + (acpf >> 16) & 0x03); + + saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff); + saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff); + saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f); state->audclk_freq = freq; return 0; } -static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) { - struct saa7115_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = i2c_get_clientdata(client); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -642,7 +742,7 @@ static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c } state->bright = ctrl->value; - saa7115_write(client, 0x0a, state->bright); + saa711x_write(client, R_0A_LUMA_BRIGHT_CNTL, state->bright); break; case V4L2_CID_CONTRAST: @@ -652,7 +752,7 @@ static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c } state->contrast = ctrl->value; - saa7115_write(client, 0x0b, state->contrast); + saa711x_write(client, R_0B_LUMA_CONTRAST_CNTL, state->contrast); break; case V4L2_CID_SATURATION: @@ -662,7 +762,7 @@ static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c } state->sat = ctrl->value; - saa7115_write(client, 0x0c, state->sat); + saa711x_write(client, R_0C_CHROMA_SAT_CNTL, state->sat); break; case V4L2_CID_HUE: @@ -672,7 +772,7 @@ static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c } state->hue = ctrl->value; - saa7115_write(client, 0x0d, state->hue); + saa711x_write(client, R_0D_CHROMA_HUE_CNTL, state->hue); break; default: @@ -682,9 +782,9 @@ static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c return 0; } -static int saa7115_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) { - struct saa7115_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = i2c_get_clientdata(client); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -706,10 +806,115 @@ static int saa7115_get_v4lctrl(struct i2c_client *client, struct v4l2_control *c return 0; } -static void saa7115_set_v4lstd(struct i2c_client *client, v4l2_std_id std) +static int saa711x_set_size(struct i2c_client *client, int width, int height) +{ + struct saa711x_state *state = i2c_get_clientdata(client); + int HPSC, HFSC; + int VSCY; + int res; + int is_50hz = state->std & V4L2_STD_625_50; + int Vsrc = is_50hz ? 576 : 480; + + v4l_dbg(1, debug, client, "decoder set size to %ix%i\n",width,height); + + /* FIXME need better bounds checking here */ + if ((width < 1) || (width > 1440)) + return -EINVAL; + if ((height < 1) || (height > Vsrc)) + return -EINVAL; + + if (!saa711x_has_reg(state->ident,R_D0_B_HORIZ_PRESCALING)) { + /* Decoder only supports 720 columns and 480 or 576 lines */ + if (width != 720) + return -EINVAL; + if (height != Vsrc) + return -EINVAL; + } + + state->width = width; + state->height = height; + + if (!saa711x_has_reg(state->ident, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH)) + return 0; + + /* probably have a valid size, let's set it */ + /* Set output width/height */ + /* width */ + + saa711x_write(client, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, + (u8) (width & 0xff)); + saa711x_write(client, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, + (u8) ((width >> 8) & 0xff)); + + /* Vertical Scaling uses height/2 */ + res=height/2; + + /* On 60Hz, it is using a higher Vertical Output Size */ + if (!is_50hz) + res+=(VRES_60HZ-480)>>1; + + /* height */ + saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, + (u8) (res & 0xff)); + saa711x_write(client, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, + (u8) ((res >> 8) & 0xff)); + + /* Scaling settings */ + /* Hprescaler is floor(inres/outres) */ + HPSC = (int)(720 / width); + /* 0 is not allowed (div. by zero) */ + HPSC = HPSC ? HPSC : 1; + HFSC = (int)((1024 * 720) / (HPSC * width)); + /* FIXME hardcodes to "Task B" + * write H prescaler integer */ + saa711x_write(client, R_D0_B_HORIZ_PRESCALING, + (u8) (HPSC & 0x3f)); + + v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); + /* write H fine-scaling (luminance) */ + saa711x_write(client, R_D8_B_HORIZ_LUMA_SCALING_INC, + (u8) (HFSC & 0xff)); + saa711x_write(client, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, + (u8) ((HFSC >> 8) & 0xff)); + /* write H fine-scaling (chrominance) + * must be lum/2, so i'll just bitshift :) */ + saa711x_write(client, R_DC_B_HORIZ_CHROMA_SCALING, + (u8) ((HFSC >> 1) & 0xff)); + saa711x_write(client, R_DD_B_HORIZ_CHROMA_SCALING_MSB, + (u8) ((HFSC >> 9) & 0xff)); + + VSCY = (int)((1024 * Vsrc) / height); + v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); + + /* Correct Contrast and Luminance */ + saa711x_write(client, R_D5_B_LUMA_CONTRAST_CNTL, + (u8) (64 * 1024 / VSCY)); + saa711x_write(client, R_D6_B_CHROMA_SATURATION_CNTL, + (u8) (64 * 1024 / VSCY)); + + /* write V fine-scaling (luminance) */ + saa711x_write(client, R_E0_B_VERT_LUMA_SCALING_INC, + (u8) (VSCY & 0xff)); + saa711x_write(client, R_E1_B_VERT_LUMA_SCALING_INC_MSB, + (u8) ((VSCY >> 8) & 0xff)); + /* write V fine-scaling (chrominance) */ + saa711x_write(client, R_E2_B_VERT_CHROMA_SCALING_INC, + (u8) (VSCY & 0xff)); + saa711x_write(client, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, + (u8) ((VSCY >> 8) & 0xff)); + + saa711x_writeregs(client, saa7115_cfg_reset_scaler); + + /* Activates task "B" */ + saa711x_write(client, R_80_GLOBAL_CNTL_1, + saa711x_read(client,R_80_GLOBAL_CNTL_1)|0x20); + + return 0; +} + +static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) { - struct saa7115_state *state = i2c_get_clientdata(client); - int taskb = saa7115_read(client, 0x80) & 0x10; + struct saa711x_state *state = i2c_get_clientdata(client); /* Prevent unnecessary standard changes. During a standard change the I-Port is temporarily disabled. Any devices @@ -721,17 +926,21 @@ static void saa7115_set_v4lstd(struct i2c_client *client, v4l2_std_id std) if (std == state->std) return; + state->std = std; + // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. if (std & V4L2_STD_525_60) { v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n"); - saa7115_writeregs(client, saa7115_cfg_60hz_video); + saa711x_writeregs(client, saa7115_cfg_60hz_video); + saa711x_set_size(client,720,480); } else { v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n"); - saa7115_writeregs(client, saa7115_cfg_50hz_video); + saa711x_writeregs(client, saa7115_cfg_50hz_video); + saa711x_set_size(client,720,576); } /* Register 0E - Bits D6-D4 on NO-AUTO mode - (SAA7113 doesn't have auto mode) + (SAA7111 and SAA7113 doesn't have auto mode) 50 Hz / 625 lines 60 Hz / 525 lines 000 PAL BGDHI (4.43Mhz) NTSC M (3.58MHz) 001 NTSC 4.43 (50 Hz) PAL 4.43 (60 Hz) @@ -739,8 +948,9 @@ static void saa7115_set_v4lstd(struct i2c_client *client, v4l2_std_id std) 011 NTSC N (3.58MHz) PAL M (3.58MHz) 100 reserved NTSC-Japan (3.58MHz) */ - if (state->ident == V4L2_IDENT_SAA7113) { - u8 reg = saa7115_read(client, 0x0e) & 0x8f; + if (state->ident == V4L2_IDENT_SAA7111 || + state->ident == V4L2_IDENT_SAA7113) { + u8 reg = saa711x_read(client, R_0E_CHROMA_CNTL_1) & 0x8f; if (std == V4L2_STD_PAL_M) { reg |= 0x30; @@ -751,31 +961,30 @@ static void saa7115_set_v4lstd(struct i2c_client *client, v4l2_std_id std) } else if (std == V4L2_STD_NTSC_M_JP) { reg |= 0x40; } - saa7115_write(client, 0x0e, reg); - } - + saa711x_write(client, R_0E_CHROMA_CNTL_1, reg); + } else { + /* restart task B if needed */ + int taskb = saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10; - state->std = std; + if (taskb && state->ident == V4L2_IDENT_SAA7114) { + saa711x_writeregs(client, saa7115_cfg_vbi_on); + } - /* restart task B if needed */ - if (taskb && state->ident != V4L2_IDENT_SAA7115) { - saa7115_writeregs(client, saa7115_cfg_vbi_on); + /* switch audio mode too! */ + saa711x_set_audio_clock_freq(client, state->audclk_freq); } - - /* switch audio mode too! */ - saa7115_set_audio_clock_freq(client, state->audclk_freq); } -static v4l2_std_id saa7115_get_v4lstd(struct i2c_client *client) +static v4l2_std_id saa711x_get_v4lstd(struct i2c_client *client) { - struct saa7115_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = i2c_get_clientdata(client); return state->std; } -static void saa7115_log_status(struct i2c_client *client) +static void saa711x_log_status(struct i2c_client *client) { - struct saa7115_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = i2c_get_clientdata(client); int reg1e, reg1f; int signalOk; int vcr; @@ -783,7 +992,7 @@ static void saa7115_log_status(struct i2c_client *client) v4l_info(client, "Audio frequency: %d Hz\n", state->audclk_freq); if (state->ident != V4L2_IDENT_SAA7115) { /* status for the saa7114 */ - reg1f = saa7115_read(client, 0x1f); + reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); signalOk = (reg1f & 0xc1) == 0x81; v4l_info(client, "Video signal: %s\n", signalOk ? "ok" : "bad"); v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); @@ -791,8 +1000,8 @@ static void saa7115_log_status(struct i2c_client *client) } /* status for the saa7115 */ - reg1e = saa7115_read(client, 0x1e); - reg1f = saa7115_read(client, 0x1f); + reg1e = saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC); + reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; vcr = !(reg1f & 0x10); @@ -819,19 +1028,27 @@ static void saa7115_log_status(struct i2c_client *client) v4l_info(client, "Detected format: BW/No color\n"); break; } + v4l_info(client, "Width, Height: %d, %d\n", state->width, state->height); } /* setup the sliced VBI lcr registers according to the sliced VBI format */ -static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt) +static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt) { - struct saa7115_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = i2c_get_clientdata(client); int is_50hz = (state->std & V4L2_STD_625_50); u8 lcr[24]; int i, x; - /* saa7113/7114 doesn't yet support VBI */ +#if 1 + /* saa7113/7114/7118 VBI support are experimental */ + if (!saa711x_has_reg(state->ident,R_41_LCR_BASE)) + return; + +#else + /* SAA7113 and SAA7118 also should support VBI - Need testing */ if (state->ident != V4L2_IDENT_SAA7115) return; +#endif for (i = 0; i <= 23; i++) lcr[i] = 0xff; @@ -888,14 +1105,16 @@ static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo /* write the lcr registers */ for (i = 2; i <= 23; i++) { - saa7115_write(client, i - 2 + 0x41, lcr[i]); + saa711x_write(client, i - 2 + R_41_LCR_BASE, lcr[i]); } /* enable/disable raw VBI capturing */ - saa7115_writeregs(client, fmt->service_set == 0 ? saa7115_cfg_vbi_on : saa7115_cfg_vbi_off); + saa711x_writeregs(client, fmt->service_set == 0 ? + saa7115_cfg_vbi_on : + saa7115_cfg_vbi_off); } -static int saa7115_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) { static u16 lcr2vbi[] = { 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ @@ -911,10 +1130,10 @@ static int saa7115_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt return -EINVAL; memset(sliced, 0, sizeof(*sliced)); /* done if using raw VBI */ - if (saa7115_read(client, 0x80) & 0x10) + if (saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10) return 0; for (i = 2; i <= 23; i++) { - u8 v = saa7115_read(client, i - 2 + 0x41); + u8 v = saa711x_read(client, i - 2 + R_41_LCR_BASE); sliced->service_lines[0][i] = lcr2vbi[v >> 4]; sliced->service_lines[1][i] = lcr2vbi[v & 0xf]; @@ -924,114 +1143,31 @@ static int saa7115_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt return 0; } -static int saa7115_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) { - struct saa7115_state *state = i2c_get_clientdata(client); - struct v4l2_pix_format *pix; - int HPSC, HFSC; - int VSCY, Vsrc; - int is_50hz = state->std & V4L2_STD_625_50; - if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - saa7115_set_lcr(client, &fmt->fmt.sliced); + saa711x_set_lcr(client, &fmt->fmt.sliced); return 0; } if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - pix = &(fmt->fmt.pix); - - v4l_dbg(1, debug, client, "decoder set size\n"); - - /* FIXME need better bounds checking here */ - if ((pix->width < 1) || (pix->width > 1440)) - return -EINVAL; - if ((pix->height < 1) || (pix->height > 960)) - return -EINVAL; - - /* probably have a valid size, let's set it */ - /* Set output width/height */ - /* width */ - saa7115_write(client, 0xcc, (u8) (pix->width & 0xff)); - saa7115_write(client, 0xcd, (u8) ((pix->width >> 8) & 0xff)); - /* height */ - saa7115_write(client, 0xce, (u8) (pix->height & 0xff)); - saa7115_write(client, 0xcf, (u8) ((pix->height >> 8) & 0xff)); - - /* Scaling settings */ - /* Hprescaler is floor(inres/outres) */ - /* FIXME hardcoding input res */ - if (pix->width != 720) { - HPSC = (int)(720 / pix->width); - /* 0 is not allowed (div. by zero) */ - HPSC = HPSC ? HPSC : 1; - HFSC = (int)((1024 * 720) / (HPSC * pix->width)); - - v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); - /* FIXME hardcodes to "Task B" - * write H prescaler integer */ - saa7115_write(client, 0xd0, (u8) (HPSC & 0x3f)); - - /* write H fine-scaling (luminance) */ - saa7115_write(client, 0xd8, (u8) (HFSC & 0xff)); - saa7115_write(client, 0xd9, (u8) ((HFSC >> 8) & 0xff)); - /* write H fine-scaling (chrominance) - * must be lum/2, so i'll just bitshift :) */ - saa7115_write(client, 0xDC, (u8) ((HFSC >> 1) & 0xff)); - saa7115_write(client, 0xDD, (u8) ((HFSC >> 9) & 0xff)); - } else { - if (is_50hz) { - v4l_dbg(1, debug, client, "Setting full 50hz width\n"); - saa7115_writeregs(client, saa7115_cfg_50hz_fullres_x); - } else { - v4l_dbg(1, debug, client, "Setting full 60hz width\n"); - saa7115_writeregs(client, saa7115_cfg_60hz_fullres_x); - } - } - - Vsrc = is_50hz ? 576 : 480; - - if (pix->height != Vsrc) { - VSCY = (int)((1024 * Vsrc) / pix->height); - v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); - - /* Correct Contrast and Luminance */ - saa7115_write(client, 0xd5, (u8) (64 * 1024 / VSCY)); - saa7115_write(client, 0xd6, (u8) (64 * 1024 / VSCY)); - - /* write V fine-scaling (luminance) */ - saa7115_write(client, 0xe0, (u8) (VSCY & 0xff)); - saa7115_write(client, 0xe1, (u8) ((VSCY >> 8) & 0xff)); - /* write V fine-scaling (chrominance) */ - saa7115_write(client, 0xe2, (u8) (VSCY & 0xff)); - saa7115_write(client, 0xe3, (u8) ((VSCY >> 8) & 0xff)); - } else { - if (is_50hz) { - v4l_dbg(1, debug, client, "Setting full 50Hz height\n"); - saa7115_writeregs(client, saa7115_cfg_50hz_fullres_y); - } else { - v4l_dbg(1, debug, client, "Setting full 60hz height\n"); - saa7115_writeregs(client, saa7115_cfg_60hz_fullres_y); - } - } - - saa7115_writeregs(client, saa7115_cfg_reset_scaler); - return 0; + return saa711x_set_size(client,fmt->fmt.pix.width,fmt->fmt.pix.height); } /* Decode the sliced VBI data stream as created by the saa7115. The format is described in the saa7115 datasheet in Tables 25 and 26 and in Figure 33. The current implementation uses SAV/EAV codes and not the ancillary data - headers. The vbi->p pointer points to the SDID byte right after the SAV + headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV code. */ -static void saa7115_decode_vbi_line(struct i2c_client *client, +static void saa711x_decode_vbi_line(struct i2c_client *client, struct v4l2_decode_vbi_line *vbi) { static const char vbi_no_data_pattern[] = { 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0 }; - struct saa7115_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = i2c_get_clientdata(client); u8 *p = vbi->p; u32 wss; int id1, id2; /* the ID1 and ID2 bytes from the internal header */ @@ -1066,12 +1202,12 @@ static void saa7115_decode_vbi_line(struct i2c_client *client, vbi->type = V4L2_SLICED_TELETEXT_B; break; case 4: - if (!saa7115_odd_parity(p[0]) || !saa7115_odd_parity(p[1])) + if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1])) return; vbi->type = V4L2_SLICED_CAPTION_525; break; case 5: - wss = saa7115_decode_wss(p); + wss = saa711x_decode_wss(p); if (wss == -1) return; p[0] = wss & 0xff; @@ -1079,7 +1215,7 @@ static void saa7115_decode_vbi_line(struct i2c_client *client, vbi->type = V4L2_SLICED_WSS_625; break; case 7: - if (saa7115_decode_vps(p, p) != 0) + if (saa711x_decode_vps(p, p) != 0) return; vbi->type = V4L2_SLICED_VPS; break; @@ -1090,21 +1226,21 @@ static void saa7115_decode_vbi_line(struct i2c_client *client, /* ============ SAA7115 AUDIO settings (end) ============= */ -static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *arg) +static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *arg) { - struct saa7115_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = i2c_get_clientdata(client); int *iarg = arg; /* ioctls to allow direct access to the saa7115 registers for testing */ switch (cmd) { case VIDIOC_S_FMT: - return saa7115_set_v4lfmt(client, (struct v4l2_format *)arg); + return saa711x_set_v4lfmt(client, (struct v4l2_format *)arg); case VIDIOC_G_FMT: - return saa7115_get_v4lfmt(client, (struct v4l2_format *)arg); + return saa711x_get_v4lfmt(client, (struct v4l2_format *)arg); case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return saa7115_set_audio_clock_freq(client, *(u32 *)arg); + return saa711x_set_audio_clock_freq(client, *(u32 *)arg); case VIDIOC_G_TUNER: { @@ -1113,7 +1249,7 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar if (state->radio) break; - status = saa7115_read(client, 0x1f); + status = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); v4l_dbg(1, debug, client, "status: 0x%02x\n", status); vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; @@ -1121,14 +1257,14 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar } case VIDIOC_LOG_STATUS: - saa7115_log_status(client); + saa711x_log_status(client); break; case VIDIOC_G_CTRL: - return saa7115_get_v4lctrl(client, (struct v4l2_control *)arg); + return saa711x_get_v4lctrl(client, (struct v4l2_control *)arg); case VIDIOC_S_CTRL: - return saa7115_set_v4lctrl(client, (struct v4l2_control *)arg); + return saa711x_set_v4lctrl(client, (struct v4l2_control *)arg); case VIDIOC_QUERYCTRL: { @@ -1146,12 +1282,12 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar } case VIDIOC_G_STD: - *(v4l2_std_id *)arg = saa7115_get_v4lstd(client); + *(v4l2_std_id *)arg = saa711x_get_v4lstd(client); break; case VIDIOC_S_STD: state->radio = 0; - saa7115_set_v4lstd(client, *(v4l2_std_id *)arg); + saa711x_set_v4lstd(client, *(v4l2_std_id *)arg); break; case AUDC_SET_RADIO: @@ -1187,13 +1323,13 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar state->input = route->input; /* select mode */ - saa7115_write(client, 0x02, - (saa7115_read(client, 0x02) & 0xf0) | + saa711x_write(client, R_02_INPUT_CNTL_1, + (saa711x_read(client, R_02_INPUT_CNTL_1) & 0xf0) | state->input); /* bypass chrominance trap for S-Video modes */ - saa7115_write(client, 0x09, - (saa7115_read(client, 0x09) & 0x7f) | + saa711x_write(client, R_09_LUMA_CNTL, + (saa711x_read(client, R_09_LUMA_CNTL) & 0x7f) | (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); break; } @@ -1205,7 +1341,9 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar if (state->enable != (cmd == VIDIOC_STREAMON)) { state->enable = (cmd == VIDIOC_STREAMON); - saa7115_write(client, 0x87, state->enable); + saa711x_write(client, + R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, + state->enable); } break; @@ -1220,17 +1358,17 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; - saa7115_set_audio_clock_freq(client, state->audclk_freq); + saa711x_set_audio_clock_freq(client, state->audclk_freq); break; } case VIDIOC_INT_DECODE_VBI_LINE: - saa7115_decode_vbi_line(client, arg); + saa711x_decode_vbi_line(client, arg); break; case VIDIOC_INT_RESET: v4l_dbg(1, debug, client, "decoder RESET\n"); - saa7115_writeregs(client, saa7115_cfg_reset_scaler); + saa711x_writeregs(client, saa7115_cfg_reset_scaler); break; case VIDIOC_INT_G_VBI_DATA: @@ -1239,25 +1377,25 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar switch (data->id) { case V4L2_SLICED_WSS_625: - if (saa7115_read(client, 0x6b) & 0xc0) + if (saa711x_read(client, 0x6b) & 0xc0) return -EIO; - data->data[0] = saa7115_read(client, 0x6c); - data->data[1] = saa7115_read(client, 0x6d); + data->data[0] = saa711x_read(client, 0x6c); + data->data[1] = saa711x_read(client, 0x6d); return 0; case V4L2_SLICED_CAPTION_525: if (data->field == 0) { /* CC */ - if (saa7115_read(client, 0x66) & 0xc0) + if (saa711x_read(client, 0x66) & 0xc0) return -EIO; - data->data[0] = saa7115_read(client, 0x67); - data->data[1] = saa7115_read(client, 0x68); + data->data[0] = saa711x_read(client, 0x67); + data->data[1] = saa711x_read(client, 0x68); return 0; } /* XDS */ - if (saa7115_read(client, 0x66) & 0x30) + if (saa711x_read(client, 0x66) & 0x30) return -EIO; - data->data[0] = saa7115_read(client, 0x69); - data->data[1] = saa7115_read(client, 0x6a); + data->data[0] = saa711x_read(client, 0x69); + data->data[1] = saa711x_read(client, 0x6a); return 0; default: return -EINVAL; @@ -1272,7 +1410,7 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar if (reg->i2c_id != I2C_DRIVERID_SAA711X) return -EINVAL; - reg->val = saa7115_read(client, reg->reg & 0xff); + reg->val = saa711x_read(client, reg->reg & 0xff); break; } @@ -1284,7 +1422,7 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - saa7115_write(client, reg->reg & 0xff, reg->val & 0xff); + saa711x_write(client, reg->reg & 0xff, reg->val & 0xff); break; } #endif @@ -1302,12 +1440,14 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar /* ----------------------------------------------------------------------- */ -static struct i2c_driver i2c_driver_saa7115; +static struct i2c_driver i2c_driver_saa711x; -static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind) +static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind) { struct i2c_client *client; - struct saa7115_state *state; + struct saa711x_state *state; + int i; + char name[17]; u8 chip_id; /* Check if the adapter supports the needed features */ @@ -1319,28 +1459,31 @@ static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind) return -ENOMEM; client->addr = address; client->adapter = adapter; - client->driver = &i2c_driver_saa7115; + client->driver = &i2c_driver_saa711x; snprintf(client->name, sizeof(client->name) - 1, "saa7115"); v4l_dbg(1, debug, client, "detecting saa7115 client on address 0x%x\n", address << 1); - saa7115_write(client, 0, 5); - chip_id = saa7115_read(client, 0) & 0x0f; - if (chip_id < 3 && chip_id > 5) { - v4l_dbg(1, debug, client, "saa7115 not found\n"); - kfree(client); - return 0; + for (i=0;i<0x0f;i++) { + saa711x_write(client, 0, i); + name[i] = (saa711x_read(client, 0) &0x0f) +'0'; + if (name[i]>'9') + name[i]+='a'-'9'-1; } + name[i]='\0'; + + saa711x_write(client, 0, 5); + chip_id = saa711x_read(client, 0) & 0x0f; + snprintf(client->name, sizeof(client->name) - 1, "saa711%d",chip_id); - v4l_info(client, "saa711%d found @ 0x%x (%s)\n", chip_id, address << 1, adapter->name); + v4l_info(client, "saa711%d found (%s) @ 0x%x (%s)\n", chip_id, name, address << 1, adapter->name); - state = kzalloc(sizeof(struct saa7115_state), GFP_KERNEL); + state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL); i2c_set_clientdata(client, state); if (state == NULL) { kfree(client); return -ENOMEM; } - state->std = V4L2_STD_NTSC; state->input = -1; state->enable = 1; state->radio = 0; @@ -1349,15 +1492,25 @@ static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind) state->hue = 0; state->sat = 64; switch (chip_id) { + case 1: + state->ident = V4L2_IDENT_SAA7111; + break; case 3: state->ident = V4L2_IDENT_SAA7113; break; case 4: state->ident = V4L2_IDENT_SAA7114; break; - default: + case 5: state->ident = V4L2_IDENT_SAA7115; break; + case 8: + state->ident = V4L2_IDENT_SAA7118; + break; + default: + state->ident = V4L2_IDENT_SAA7111; + v4l_info(client, "WARNING: Chip is not known - Falling back to saa7111\n"); + } state->audclk_freq = 48000; @@ -1365,38 +1518,39 @@ static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind) v4l_dbg(1, debug, client, "writing init values\n"); /* init to 60hz/48khz */ - if (state->ident == V4L2_IDENT_SAA7113) { - state->crystal_freq = SAA7115_FREQ_24_576_MHZ; - saa7115_writeregs(client, saa7113_init_auto_input); - } else { + state->crystal_freq = SAA7115_FREQ_24_576_MHZ; + switch (state->ident) { + case V4L2_IDENT_SAA7111: + saa711x_writeregs(client, saa7111_init); + break; + case V4L2_IDENT_SAA7113: + saa711x_writeregs(client, saa7113_init); + break; + default: state->crystal_freq = SAA7115_FREQ_32_11_MHZ; - saa7115_writeregs(client, saa7115_init_auto_input); + saa711x_writeregs(client, saa7115_init_auto_input); } - saa7115_writeregs(client, saa7115_init_misc); - saa7115_writeregs(client, saa7115_cfg_60hz_fullres_x); - saa7115_writeregs(client, saa7115_cfg_60hz_fullres_y); - saa7115_writeregs(client, saa7115_cfg_60hz_video); - saa7115_set_audio_clock_freq(client, state->audclk_freq); - saa7115_writeregs(client, saa7115_cfg_reset_scaler); + saa711x_writeregs(client, saa7115_init_misc); + saa711x_set_v4lstd(client, V4L2_STD_NTSC); i2c_attach_client(client); v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n", - saa7115_read(client, 0x1e), saa7115_read(client, 0x1f)); + saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC), saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC)); return 0; } -static int saa7115_probe(struct i2c_adapter *adapter) +static int saa711x_probe(struct i2c_adapter *adapter) { if (adapter->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adapter, &addr_data, &saa7115_attach); + return i2c_probe(adapter, &addr_data, &saa711x_attach); return 0; } -static int saa7115_detach(struct i2c_client *client) +static int saa711x_detach(struct i2c_client *client) { - struct saa7115_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = i2c_get_clientdata(client); int err; err = i2c_detach_client(client); @@ -1412,26 +1566,26 @@ static int saa7115_detach(struct i2c_client *client) /* ----------------------------------------------------------------------- */ /* i2c implementation */ -static struct i2c_driver i2c_driver_saa7115 = { +static struct i2c_driver i2c_driver_saa711x = { .driver = { .name = "saa7115", }, .id = I2C_DRIVERID_SAA711X, - .attach_adapter = saa7115_probe, - .detach_client = saa7115_detach, - .command = saa7115_command, + .attach_adapter = saa711x_probe, + .detach_client = saa711x_detach, + .command = saa711x_command, }; -static int __init saa7115_init_module(void) +static int __init saa711x_init_module(void) { - return i2c_add_driver(&i2c_driver_saa7115); + return i2c_add_driver(&i2c_driver_saa711x); } -static void __exit saa7115_cleanup_module(void) +static void __exit saa711x_cleanup_module(void) { - i2c_del_driver(&i2c_driver_saa7115); + i2c_del_driver(&i2c_driver_saa711x); } -module_init(saa7115_init_module); -module_exit(saa7115_cleanup_module); +module_init(saa711x_init_module); +module_exit(saa711x_cleanup_module); diff --git a/drivers/media/video/saa711x_regs.h b/drivers/media/video/saa711x_regs.h new file mode 100644 index 00000000000..4e5f2eb0a2c --- /dev/null +++ b/drivers/media/video/saa711x_regs.h @@ -0,0 +1,549 @@ +/* saa711x - Philips SAA711x video decoder register specifications + * + * Copyright (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define R_00_CHIP_VERSION 0x00 +/* Video Decoder */ + /* Video Decoder - Frontend part */ +#define R_01_INC_DELAY 0x01 +#define R_02_INPUT_CNTL_1 0x02 +#define R_03_INPUT_CNTL_2 0x03 +#define R_04_INPUT_CNTL_3 0x04 +#define R_05_INPUT_CNTL_4 0x05 + /* Video Decoder - Decoder part */ +#define R_06_H_SYNC_START 0x06 +#define R_07_H_SYNC_STOP 0x07 +#define R_08_SYNC_CNTL 0x08 +#define R_09_LUMA_CNTL 0x09 +#define R_0A_LUMA_BRIGHT_CNTL 0x0a +#define R_0B_LUMA_CONTRAST_CNTL 0x0b +#define R_0C_CHROMA_SAT_CNTL 0x0c +#define R_0D_CHROMA_HUE_CNTL 0x0d +#define R_0E_CHROMA_CNTL_1 0x0e +#define R_0F_CHROMA_GAIN_CNTL 0x0f +#define R_10_CHROMA_CNTL_2 0x10 +#define R_11_MODE_DELAY_CNTL 0x11 +#define R_12_RT_SIGNAL_CNTL 0x12 +#define R_13_RT_X_PORT_OUT_CNTL 0x13 +#define R_14_ANAL_ADC_COMPAT_CNTL 0x14 +#define R_15_VGATE_START_FID_CHG 0x15 +#define R_16_VGATE_STOP 0x16 +#define R_17_MISC_VGATE_CONF_AND_MSB 0x17 +#define R_18_RAW_DATA_GAIN_CNTL 0x18 +#define R_19_RAW_DATA_OFF_CNTL 0x19 +#define R_1A_COLOR_KILL_LVL_CNTL 0x1a +#define R_1B_MISC_TVVCRDET 0x1b +#define R_1C_ENHAN_COMB_CTRL1 0x1c +#define R_1D_ENHAN_COMB_CTRL2 0x1d +#define R_1E_STATUS_BYTE_1_VD_DEC 0x1e +#define R_1F_STATUS_BYTE_2_VD_DEC 0x1f + +/* Component processing and interrupt masking part */ +#define R_23_INPUT_CNTL_5 0x23 +#define R_24_INPUT_CNTL_6 0x24 +#define R_25_INPUT_CNTL_7 0x25 +#define R_29_COMP_DELAY 0x29 +#define R_2A_COMP_BRIGHT_CNTL 0x2a +#define R_2B_COMP_CONTRAST_CNTL 0x2b +#define R_2C_COMP_SAT_CNTL 0x2c +#define R_2D_INTERRUPT_MASK_1 0x2d +#define R_2E_INTERRUPT_MASK_2 0x2e +#define R_2F_INTERRUPT_MASK_3 0x2f + +/* Audio clock generator part */ +#define R_30_AUD_MAST_CLK_CYCLES_PER_FIELD 0x30 +#define R_34_AUD_MAST_CLK_NOMINAL_INC 0x34 +#define R_38_CLK_RATIO_AMXCLK_TO_ASCLK 0x38 +#define R_39_CLK_RATIO_ASCLK_TO_ALRCLK 0x39 +#define R_3A_AUD_CLK_GEN_BASIC_SETUP 0x3a + +/* General purpose VBI data slicer part */ +#define R_40_SLICER_CNTL_1 0x40 +#define R_41_LCR_BASE 0x41 +#define R_58_PROGRAM_FRAMING_CODE 0x58 +#define R_59_H_OFF_FOR_SLICER 0x59 +#define R_5A_V_OFF_FOR_SLICER 0x5a +#define R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF 0x5b +#define R_5D_DID 0x5d +#define R_5E_SDID 0x5e +#define R_60_SLICER_STATUS_BYTE_0 0x60 +#define R_61_SLICER_STATUS_BYTE_1 0x61 +#define R_62_SLICER_STATUS_BYTE_2 0x62 + +/* X port, I port and the scaler part */ + /* Task independent global settings */ +#define R_80_GLOBAL_CNTL_1 0x80 +#define R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F 0x81 +#define R_83_X_PORT_I_O_ENA_AND_OUT_CLK 0x83 +#define R_84_I_PORT_SIGNAL_DEF 0x84 +#define R_85_I_PORT_SIGNAL_POLAR 0x85 +#define R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT 0x86 +#define R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED 0x87 +#define R_88_POWER_SAVE_ADC_PORT_CNTL 0x88 +#define R_8F_STATUS_INFO_SCALER 0x8f + /* Task A definition */ + /* Basic settings and acquisition window definition */ +#define R_90_A_TASK_HANDLING_CNTL 0x90 +#define R_91_A_X_PORT_FORMATS_AND_CONF 0x91 +#define R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL 0x92 +#define R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF 0x93 +#define R_94_A_HORIZ_INPUT_WINDOW_START 0x94 +#define R_95_A_HORIZ_INPUT_WINDOW_START_MSB 0x95 +#define R_96_A_HORIZ_INPUT_WINDOW_LENGTH 0x96 +#define R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB 0x97 +#define R_98_A_VERT_INPUT_WINDOW_START 0x98 +#define R_99_A_VERT_INPUT_WINDOW_START_MSB 0x99 +#define R_9A_A_VERT_INPUT_WINDOW_LENGTH 0x9a +#define R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB 0x9b +#define R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH 0x9c +#define R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0x9d +#define R_9E_A_VERT_OUTPUT_WINDOW_LENGTH 0x9e +#define R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB 0x9f + /* FIR filtering and prescaling */ +#define R_A0_A_HORIZ_PRESCALING 0xa0 +#define R_A1_A_ACCUMULATION_LENGTH 0xa1 +#define R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xa2 +#define R_A4_A_LUMA_BRIGHTNESS_CNTL 0xa4 +#define R_A5_A_LUMA_CONTRAST_CNTL 0xa5 +#define R_A6_A_CHROMA_SATURATION_CNTL 0xa6 + /* Horizontal phase scaling */ +#define R_A8_A_HORIZ_LUMA_SCALING_INC 0xa8 +#define R_A9_A_HORIZ_LUMA_SCALING_INC_MSB 0xa9 +#define R_AA_A_HORIZ_LUMA_PHASE_OFF 0xaa +#define R_AC_A_HORIZ_CHROMA_SCALING_INC 0xac +#define R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB 0xad +#define R_AE_A_HORIZ_CHROMA_PHASE_OFF 0xae +#define R_AF_A_HORIZ_CHROMA_PHASE_OFF_MSB 0xaf + /* Vertical scaling */ +#define R_B0_A_VERT_LUMA_SCALING_INC 0xb0 +#define R_B1_A_VERT_LUMA_SCALING_INC_MSB 0xb1 +#define R_B2_A_VERT_CHROMA_SCALING_INC 0xb2 +#define R_B3_A_VERT_CHROMA_SCALING_INC_MSB 0xb3 +#define R_B4_A_VERT_SCALING_MODE_CNTL 0xb4 +#define R_B8_A_VERT_CHROMA_PHASE_OFF_00 0xb8 +#define R_B9_A_VERT_CHROMA_PHASE_OFF_01 0xb9 +#define R_BA_A_VERT_CHROMA_PHASE_OFF_10 0xba +#define R_BB_A_VERT_CHROMA_PHASE_OFF_11 0xbb +#define R_BC_A_VERT_LUMA_PHASE_OFF_00 0xbc +#define R_BD_A_VERT_LUMA_PHASE_OFF_01 0xbd +#define R_BE_A_VERT_LUMA_PHASE_OFF_10 0xbe +#define R_BF_A_VERT_LUMA_PHASE_OFF_11 0xbf + /* Task B definition */ + /* Basic settings and acquisition window definition */ +#define R_C0_B_TASK_HANDLING_CNTL 0xc0 +#define R_C1_B_X_PORT_FORMATS_AND_CONF 0xc1 +#define R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION 0xc2 +#define R_C3_B_I_PORT_FORMATS_AND_CONF 0xc3 +#define R_C4_B_HORIZ_INPUT_WINDOW_START 0xc4 +#define R_C5_B_HORIZ_INPUT_WINDOW_START_MSB 0xc5 +#define R_C6_B_HORIZ_INPUT_WINDOW_LENGTH 0xc6 +#define R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB 0xc7 +#define R_C8_B_VERT_INPUT_WINDOW_START 0xc8 +#define R_C9_B_VERT_INPUT_WINDOW_START_MSB 0xc9 +#define R_CA_B_VERT_INPUT_WINDOW_LENGTH 0xca +#define R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB 0xcb +#define R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH 0xcc +#define R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0xcd +#define R_CE_B_VERT_OUTPUT_WINDOW_LENGTH 0xce +#define R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB 0xcf + /* FIR filtering and prescaling */ +#define R_D0_B_HORIZ_PRESCALING 0xd0 +#define R_D1_B_ACCUMULATION_LENGTH 0xd1 +#define R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xd2 +#define R_D4_B_LUMA_BRIGHTNESS_CNTL 0xd4 +#define R_D5_B_LUMA_CONTRAST_CNTL 0xd5 +#define R_D6_B_CHROMA_SATURATION_CNTL 0xd6 + /* Horizontal phase scaling */ +#define R_D8_B_HORIZ_LUMA_SCALING_INC 0xd8 +#define R_D9_B_HORIZ_LUMA_SCALING_INC_MSB 0xd9 +#define R_DA_B_HORIZ_LUMA_PHASE_OFF 0xda +#define R_DC_B_HORIZ_CHROMA_SCALING 0xdc +#define R_DD_B_HORIZ_CHROMA_SCALING_MSB 0xdd +#define R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA 0xde + /* Vertical scaling */ +#define R_E0_B_VERT_LUMA_SCALING_INC 0xe0 +#define R_E1_B_VERT_LUMA_SCALING_INC_MSB 0xe1 +#define R_E2_B_VERT_CHROMA_SCALING_INC 0xe2 +#define R_E3_B_VERT_CHROMA_SCALING_INC_MSB 0xe3 +#define R_E4_B_VERT_SCALING_MODE_CNTL 0xe4 +#define R_E8_B_VERT_CHROMA_PHASE_OFF_00 0xe8 +#define R_E9_B_VERT_CHROMA_PHASE_OFF_01 0xe9 +#define R_EA_B_VERT_CHROMA_PHASE_OFF_10 0xea +#define R_EB_B_VERT_CHROMA_PHASE_OFF_11 0xeb +#define R_EC_B_VERT_LUMA_PHASE_OFF_00 0xec +#define R_ED_B_VERT_LUMA_PHASE_OFF_01 0xed +#define R_EE_B_VERT_LUMA_PHASE_OFF_10 0xee +#define R_EF_B_VERT_LUMA_PHASE_OFF_11 0xef + +/* second PLL (PLL2) and Pulsegenerator Programming */ +#define R_F0_LFCO_PER_LINE 0xf0 +#define R_F1_P_I_PARAM_SELECT 0xf1 +#define R_F2_NOMINAL_PLL2_DTO 0xf2 +#define R_F3_PLL_INCREMENT 0xf3 +#define R_F4_PLL2_STATUS 0xf4 +#define R_F5_PULSGEN_LINE_LENGTH 0xf5 +#define R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG 0xf6 +#define R_F7_PULSE_A_POS_MSB 0xf7 +#define R_F8_PULSE_B_POS 0xf8 +#define R_F9_PULSE_B_POS_MSB 0xf9 +#define R_FA_PULSE_C_POS 0xfa +#define R_FB_PULSE_C_POS_MSB 0xfb +#define R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES 0xff + +#if 0 +/* Those structs will be used in the future for debug purposes */ +struct saa711x_reg_descr { + u8 reg; + int count; + char *name; +}; + +struct saa711x_reg_descr saa711x_regs[] = { + /* REG COUNT NAME */ + {R_00_CHIP_VERSION,1, + "Chip version"}, + + /* Video Decoder: R_01_INC_DELAY to R_1F_STATUS_BYTE_2_VD_DEC */ + + /* Video Decoder - Frontend part: R_01_INC_DELAY to R_05_INPUT_CNTL_4 */ + {R_01_INC_DELAY,1, + "Increment delay"}, + {R_02_INPUT_CNTL_1,1, + "Analog input control 1"}, + {R_03_INPUT_CNTL_2,1, + "Analog input control 2"}, + {R_04_INPUT_CNTL_3,1, + "Analog input control 3"}, + {R_05_INPUT_CNTL_4,1, + "Analog input control 4"}, + + /* Video Decoder - Decoder part: R_06_H_SYNC_START to R_1F_STATUS_BYTE_2_VD_DEC */ + {R_06_H_SYNC_START,1, + "Horizontal sync start"}, + {R_07_H_SYNC_STOP,1, + "Horizontal sync stop"}, + {R_08_SYNC_CNTL,1, + "Sync control"}, + {R_09_LUMA_CNTL,1, + "Luminance control"}, + {R_0A_LUMA_BRIGHT_CNTL,1, + "Luminance brightness control"}, + {R_0B_LUMA_CONTRAST_CNTL,1, + "Luminance contrast control"}, + {R_0C_CHROMA_SAT_CNTL,1, + "Chrominance saturation control"}, + {R_0D_CHROMA_HUE_CNTL,1, + "Chrominance hue control"}, + {R_0E_CHROMA_CNTL_1,1, + "Chrominance control 1"}, + {R_0F_CHROMA_GAIN_CNTL,1, + "Chrominance gain control"}, + {R_10_CHROMA_CNTL_2,1, + "Chrominance control 2"}, + {R_11_MODE_DELAY_CNTL,1, + "Mode/delay control"}, + {R_12_RT_SIGNAL_CNTL,1, + "RT signal control"}, + {R_13_RT_X_PORT_OUT_CNTL,1, + "RT/X port output control"}, + {R_14_ANAL_ADC_COMPAT_CNTL,1, + "Analog/ADC/compatibility control"}, + {R_15_VGATE_START_FID_CHG, 1, + "VGATE start FID change"}, + {R_16_VGATE_STOP,1, + "VGATE stop"}, + {R_17_MISC_VGATE_CONF_AND_MSB, 1, + "Miscellaneous VGATE configuration and MSBs"}, + {R_18_RAW_DATA_GAIN_CNTL,1, + "Raw data gain control",}, + {R_19_RAW_DATA_OFF_CNTL,1, + "Raw data offset control",}, + {R_1A_COLOR_KILL_LVL_CNTL,1, + "Color Killer Level Control"}, + { R_1B_MISC_TVVCRDET, 1, + "MISC /TVVCRDET"}, + { R_1C_ENHAN_COMB_CTRL1, 1, + "Enhanced comb ctrl1"}, + { R_1D_ENHAN_COMB_CTRL2, 1, + "Enhanced comb ctrl1"}, + {R_1E_STATUS_BYTE_1_VD_DEC,1, + "Status byte 1 video decoder"}, + {R_1F_STATUS_BYTE_2_VD_DEC,1, + "Status byte 2 video decoder"}, + + /* Component processing and interrupt masking part: 0x20h to R_2F_INTERRUPT_MASK_3 */ + /* 0x20 to 0x22 - Reserved */ + {R_23_INPUT_CNTL_5,1, + "Analog input control 5"}, + {R_24_INPUT_CNTL_6,1, + "Analog input control 6"}, + {R_25_INPUT_CNTL_7,1, + "Analog input control 7"}, + /* 0x26 to 0x28 - Reserved */ + {R_29_COMP_DELAY,1, + "Component delay"}, + {R_2A_COMP_BRIGHT_CNTL,1, + "Component brightness control"}, + {R_2B_COMP_CONTRAST_CNTL,1, + "Component contrast control"}, + {R_2C_COMP_SAT_CNTL,1, + "Component saturation control"}, + {R_2D_INTERRUPT_MASK_1,1, + "Interrupt mask 1"}, + {R_2E_INTERRUPT_MASK_2,1, + "Interrupt mask 2"}, + {R_2F_INTERRUPT_MASK_3,1, + "Interrupt mask 3"}, + + /* Audio clock generator part: R_30_AUD_MAST_CLK_CYCLES_PER_FIELD to 0x3f */ + {R_30_AUD_MAST_CLK_CYCLES_PER_FIELD,3, + "Audio master clock cycles per field"}, + /* 0x33 - Reserved */ + {R_34_AUD_MAST_CLK_NOMINAL_INC,3, + "Audio master clock nominal increment"}, + /* 0x37 - Reserved */ + {R_38_CLK_RATIO_AMXCLK_TO_ASCLK,1, + "Clock ratio AMXCLK to ASCLK"}, + {R_39_CLK_RATIO_ASCLK_TO_ALRCLK,1, + "Clock ratio ASCLK to ALRCLK"}, + {R_3A_AUD_CLK_GEN_BASIC_SETUP,1, + "Audio clock generator basic setup"}, + /* 0x3b-0x3f - Reserved */ + + /* General purpose VBI data slicer part: R_40_SLICER_CNTL_1 to 0x7f */ + {R_40_SLICER_CNTL_1,1, + "Slicer control 1"}, + {R_41_LCR,23, + "R_41_LCR"}, + {R_58_PROGRAM_FRAMING_CODE,1, + "Programmable framing code"}, + {R_59_H_OFF_FOR_SLICER,1, + "Horizontal offset for slicer"}, + {R_5A_V_OFF_FOR_SLICER,1, + "Vertical offset for slicer"}, + {R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF,1, + "Field offset and MSBs for horizontal and vertical offset"}, + {R_5D_DID,1, + "Header and data identification (R_5D_DID)"}, + {R_5E_SDID,1, + "Sliced data identification (R_5E_SDID) code"}, + {R_60_SLICER_STATUS_BYTE_0,1, + "Slicer status byte 0"}, + {R_61_SLICER_STATUS_BYTE_1,1, + "Slicer status byte 1"}, + {R_62_SLICER_STATUS_BYTE_2,1, + "Slicer status byte 2"}, + /* 0x63-0x7f - Reserved */ + + /* X port, I port and the scaler part: R_80_GLOBAL_CNTL_1 to R_EF_B_VERT_LUMA_PHASE_OFF_11 */ + /* Task independent global settings: R_80_GLOBAL_CNTL_1 to R_8F_STATUS_INFO_SCALER */ + {R_80_GLOBAL_CNTL_1,1, + "Global control 1"}, + {R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F,1, + "Vertical sync and Field ID source selection, retimed V and F signals"}, + /* 0x82 - Reserved */ + {R_83_X_PORT_I_O_ENA_AND_OUT_CLK,1, + "X port I/O enable and output clock"}, + {R_84_I_PORT_SIGNAL_DEF,1, + "I port signal definitions"}, + {R_85_I_PORT_SIGNAL_POLAR,1, + "I port signal polarities"}, + {R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT,1, + "I port FIFO flag control and arbitration"}, + {R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 1, + "I port I/O enable output clock and gated"}, + {R_88_POWER_SAVE_ADC_PORT_CNTL,1, + "Power save/ADC port control"}, + /* 089-0x8e - Reserved */ + {R_8F_STATUS_INFO_SCALER,1, + "Status information scaler part"}, + + /* Task A definition: R_90_A_TASK_HANDLING_CNTL to R_BF_A_VERT_LUMA_PHASE_OFF_11 */ + /* Task A: Basic settings and acquisition window definition */ + {R_90_A_TASK_HANDLING_CNTL,1, + "Task A: Task handling control"}, + {R_91_A_X_PORT_FORMATS_AND_CONF,1, + "Task A: X port formats and configuration"}, + {R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL,1, + "Task A: X port input reference signal definition"}, + {R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF,1, + "Task A: I port output formats and configuration"}, + {R_94_A_HORIZ_INPUT_WINDOW_START,2, + "Task A: Horizontal input window start"}, + {R_96_A_HORIZ_INPUT_WINDOW_LENGTH,2, + "Task A: Horizontal input window length"}, + {R_98_A_VERT_INPUT_WINDOW_START,2, + "Task A: Vertical input window start"}, + {R_9A_A_VERT_INPUT_WINDOW_LENGTH,2, + "Task A: Vertical input window length"}, + {R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH,2, + "Task A: Horizontal output window length"}, + {R_9E_A_VERT_OUTPUT_WINDOW_LENGTH,2, + "Task A: Vertical output window length"}, + + /* Task A: FIR filtering and prescaling */ + {R_A0_A_HORIZ_PRESCALING,1, + "Task A: Horizontal prescaling"}, + {R_A1_A_ACCUMULATION_LENGTH,1, + "Task A: Accumulation length"}, + {R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1, + "Task A: Prescaler DC gain and FIR prefilter"}, + /* 0xa3 - Reserved */ + {R_A4_A_LUMA_BRIGHTNESS_CNTL,1, + "Task A: Luminance brightness control"}, + {R_A5_A_LUMA_CONTRAST_CNTL,1, + "Task A: Luminance contrast control"}, + {R_A6_A_CHROMA_SATURATION_CNTL,1, + "Task A: Chrominance saturation control"}, + /* 0xa7 - Reserved */ + + /* Task A: Horizontal phase scaling */ + {R_A8_A_HORIZ_LUMA_SCALING_INC,2, + "Task A: Horizontal luminance scaling increment"}, + {R_AA_A_HORIZ_LUMA_PHASE_OFF,1, + "Task A: Horizontal luminance phase offset"}, + /* 0xab - Reserved */ + {R_AC_A_HORIZ_CHROMA_SCALING_INC,2, + "Task A: Horizontal chrominance scaling increment"}, + {R_AE_A_HORIZ_CHROMA_PHASE_OFF,1, + "Task A: Horizontal chrominance phase offset"}, + /* 0xaf - Reserved */ + + /* Task A: Vertical scaling */ + {R_B0_A_VERT_LUMA_SCALING_INC,2, + "Task A: Vertical luminance scaling increment"}, + {R_B2_A_VERT_CHROMA_SCALING_INC,2, + "Task A: Vertical chrominance scaling increment"}, + {R_B4_A_VERT_SCALING_MODE_CNTL,1, + "Task A: Vertical scaling mode control"}, + /* 0xb5-0xb7 - Reserved */ + {R_B8_A_VERT_CHROMA_PHASE_OFF_00,1, + "Task A: Vertical chrominance phase offset '00'"}, + {R_B9_A_VERT_CHROMA_PHASE_OFF_01,1, + "Task A: Vertical chrominance phase offset '01'"}, + {R_BA_A_VERT_CHROMA_PHASE_OFF_10,1, + "Task A: Vertical chrominance phase offset '10'"}, + {R_BB_A_VERT_CHROMA_PHASE_OFF_11,1, + "Task A: Vertical chrominance phase offset '11'"}, + {R_BC_A_VERT_LUMA_PHASE_OFF_00,1, + "Task A: Vertical luminance phase offset '00'"}, + {R_BD_A_VERT_LUMA_PHASE_OFF_01,1, + "Task A: Vertical luminance phase offset '01'"}, + {R_BE_A_VERT_LUMA_PHASE_OFF_10,1, + "Task A: Vertical luminance phase offset '10'"}, + {R_BF_A_VERT_LUMA_PHASE_OFF_11,1, + "Task A: Vertical luminance phase offset '11'"}, + + /* Task B definition: R_C0_B_TASK_HANDLING_CNTL to R_EF_B_VERT_LUMA_PHASE_OFF_11 */ + /* Task B: Basic settings and acquisition window definition */ + {R_C0_B_TASK_HANDLING_CNTL,1, + "Task B: Task handling control"}, + {R_C1_B_X_PORT_FORMATS_AND_CONF,1, + "Task B: X port formats and configuration"}, + {R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION,1, + "Task B: Input reference signal definition"}, + {R_C3_B_I_PORT_FORMATS_AND_CONF,1, + "Task B: I port formats and configuration"}, + {R_C4_B_HORIZ_INPUT_WINDOW_START,2, + "Task B: Horizontal input window start"}, + {R_C6_B_HORIZ_INPUT_WINDOW_LENGTH,2, + "Task B: Horizontal input window length"}, + {R_C8_B_VERT_INPUT_WINDOW_START,2, + "Task B: Vertical input window start"}, + {R_CA_B_VERT_INPUT_WINDOW_LENGTH,2, + "Task B: Vertical input window length"}, + {R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,2, + "Task B: Horizontal output window length"}, + {R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,2, + "Task B: Vertical output window length"}, + + /* Task B: FIR filtering and prescaling */ + {R_D0_B_HORIZ_PRESCALING,1, + "Task B: Horizontal prescaling"}, + {R_D1_B_ACCUMULATION_LENGTH,1, + "Task B: Accumulation length"}, + {R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1, + "Task B: Prescaler DC gain and FIR prefilter"}, + /* 0xd3 - Reserved */ + {R_D4_B_LUMA_BRIGHTNESS_CNTL,1, + "Task B: Luminance brightness control"}, + {R_D5_B_LUMA_CONTRAST_CNTL,1, + "Task B: Luminance contrast control"}, + {R_D6_B_CHROMA_SATURATION_CNTL,1, + "Task B: Chrominance saturation control"}, + /* 0xd7 - Reserved */ + + /* Task B: Horizontal phase scaling */ + {R_D8_B_HORIZ_LUMA_SCALING_INC,2, + "Task B: Horizontal luminance scaling increment"}, + {R_DA_B_HORIZ_LUMA_PHASE_OFF,1, + "Task B: Horizontal luminance phase offset"}, + /* 0xdb - Reserved */ + {R_DC_B_HORIZ_CHROMA_SCALING,2, + "Task B: Horizontal chrominance scaling"}, + {R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA,1, + "Task B: Horizontal Phase Offset Chroma"}, + /* 0xdf - Reserved */ + + /* Task B: Vertical scaling */ + {R_E0_B_VERT_LUMA_SCALING_INC,2, + "Task B: Vertical luminance scaling increment"}, + {R_E2_B_VERT_CHROMA_SCALING_INC,2, + "Task B: Vertical chrominance scaling increment"}, + {R_E4_B_VERT_SCALING_MODE_CNTL,1, + "Task B: Vertical scaling mode control"}, + /* 0xe5-0xe7 - Reserved */ + {R_E8_B_VERT_CHROMA_PHASE_OFF_00,1, + "Task B: Vertical chrominance phase offset '00'"}, + {R_E9_B_VERT_CHROMA_PHASE_OFF_01,1, + "Task B: Vertical chrominance phase offset '01'"}, + {R_EA_B_VERT_CHROMA_PHASE_OFF_10,1, + "Task B: Vertical chrominance phase offset '10'"}, + {R_EB_B_VERT_CHROMA_PHASE_OFF_11,1, + "Task B: Vertical chrominance phase offset '11'"}, + {R_EC_B_VERT_LUMA_PHASE_OFF_00,1, + "Task B: Vertical luminance phase offset '00'"}, + {R_ED_B_VERT_LUMA_PHASE_OFF_01,1, + "Task B: Vertical luminance phase offset '01'"}, + {R_EE_B_VERT_LUMA_PHASE_OFF_10,1, + "Task B: Vertical luminance phase offset '10'"}, + {R_EF_B_VERT_LUMA_PHASE_OFF_11,1, + "Task B: Vertical luminance phase offset '11'"}, + + /* second PLL (PLL2) and Pulsegenerator Programming */ + { R_F0_LFCO_PER_LINE, 1, + "LFCO's per line"}, + { R_F1_P_I_PARAM_SELECT,1, + "P-/I- Param. Select., PLL Mode, PLL H-Src., LFCO's per line"}, + { R_F2_NOMINAL_PLL2_DTO,1, + "Nominal PLL2 DTO"}, + {R_F3_PLL_INCREMENT,1, + "PLL2 Increment"}, + {R_F4_PLL2_STATUS,1, + "PLL2 Status"}, + {R_F5_PULSGEN_LINE_LENGTH,1, + "Pulsgen. line length"}, + {R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG,1, + "Pulse A Position, Pulsgen Resync., Pulsgen. H-Src., Pulsgen. line length"}, + {R_F7_PULSE_A_POS_MSB,1, + "Pulse A Position"}, + {R_F8_PULSE_B_POS,2, + "Pulse B Position"}, + {R_FA_PULSE_C_POS,2, + "Pulse C Position"}, + /* 0xfc to 0xfe - Reserved */ + {R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES,1, + "S_PLL max. phase, error threshold, PLL2 no. of lines, threshold"}, +}; +#endif diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index f5543166d19..59da79ce2ef 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -41,53 +41,15 @@ config VIDEO_SAA7134_DVB select VIDEO_BUF_DVB select FW_LOADER select DVB_PLL + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_TDA1004X if !DVB_FE_CUSTOMISE + select DVB_NXT200X if !DVB_FE_CUSTOMISE + select DVB_TDA10086 if !DVB_FE_CUSTOMISE + select DVB_TDA826X if !DVB_FE_CUSTOMISE + select DVB_ISL6421 if !DVB_FE_CUSTOMISE ---help--- This adds support for DVB cards based on the Philips saa7134 chip. To compile this driver as a module, choose M here: the module will be called saa7134-dvb. - - You must also select one or more DVB demodulators. - If you are unsure which you need, choose all of them. - -config VIDEO_SAA7134_DVB_ALL_FRONTENDS - bool "Build all supported frontends for saa7134 based TV cards" - default y - depends on VIDEO_SAA7134_DVB - select DVB_MT352 - select DVB_TDA1004X - select DVB_NXT200X - ---help--- - This builds saa7134-dvb with all currently supported frontend - demodulators. If you wish to tweak your configuration, and - only include support for the hardware that you need, choose N here. - - If you are unsure, choose Y. - -config VIDEO_SAA7134_DVB_MT352 - bool "Zarlink MT352 DVB-T Support" - default y - depends on VIDEO_SAA7134_DVB && !VIDEO_SAA7134_DVB_ALL_FRONTENDS - select DVB_MT352 - ---help--- - This adds DVB-T support for cards based on the - Philips saa7134 chip and the MT352 demodulator. - -config VIDEO_SAA7134_DVB_TDA1004X - bool "Phillips TDA10045H/TDA10046H DVB-T Support" - default y - depends on VIDEO_SAA7134_DVB && !VIDEO_SAA7134_DVB_ALL_FRONTENDS - select DVB_TDA1004X - ---help--- - This adds DVB-T support for cards based on the - Philips saa7134 chip and the TDA10045H/TDA10046H demodulator. - -config VIDEO_SAA7134_DVB_NXT200X - bool "NXT2002/NXT2004 ATSC Support" - default y - depends on VIDEO_SAA7134_DVB && !VIDEO_SAA7134_DVB_ALL_FRONTENDS - select DVB_NXT200X - ---help--- - This adds ATSC 8VSB and QAM64/256 support for cards based on the - Philips saa7134 chip and the NXT2002/NXT2004 demodulator. diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile index be7b9ee697d..89a1565b425 100644 --- a/drivers/media/video/saa7134/Makefile +++ b/drivers/media/video/saa7134/Makefile @@ -16,8 +16,5 @@ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends extra-cflags-$(CONFIG_VIDEO_BUF_DVB) += -DHAVE_VIDEO_BUF_DVB=1 -extra-cflags-$(CONFIG_DVB_MT352) += -DHAVE_MT352=1 -extra-cflags-$(CONFIG_DVB_TDA1004X) += -DHAVE_TDA1004X=1 -extra-cflags-$(CONFIG_DVB_NXT200X) += -DHAVE_NXT200X=1 EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index d73cff1970a..a39e0136ce3 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -590,6 +590,11 @@ static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream) static int snd_card_saa7134_capture_close(struct snd_pcm_substream * substream) { + snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); + struct saa7134_dev *dev = saa7134->dev; + + dev->ctl_mute = 1; + saa7134_tvaudio_setmute(dev); return 0; } @@ -631,6 +636,9 @@ static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream) runtime->private_free = snd_card_saa7134_runtime_free; runtime->hw = snd_card_saa7134_capture; + dev->ctl_mute = 0; + saa7134_tvaudio_setmute(dev); + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 927413aded1..aa1db509f3d 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -1911,7 +1911,7 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_FLYDVBT_DUO_CARDBUS] = { - .name = "LifeView/Typhoon FlyDVB-T Duo Cardbus", + .name = "LifeView/Typhoon/Genius FlyDVB-T Duo Cardbus", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -2891,6 +2891,80 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x8000, }, }, + [SAA7134_BOARD_MEDION_MD8800_QUADRO] = { + .name = "Medion Md8800 Quadro", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_FLYDVBS_LR300] = { + /* LifeView FlyDVB-s */ + /* Igor M. Liplianin <liplianin@tut.by> */ + .name = "LifeView FlyDVB-S /Acorp TV134DS", + .audio_clock = 0x00200000, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_comp1, /* Composite input */ + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, /* S-Video signal on S-Video input */ + .vmux = 8, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_PROTEUS_2309] = { + .name = "Proteus Pro 2309", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3375,7 +3449,7 @@ struct pci_device_id saa7134_pci_tbl[] = { .driver_data = SAA7134_BOARD_FLYDVB_TRIO, },{ .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, /* SAA 7131E */ + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, .subvendor = 0x1461, .subdevice = 0x2c05, .driver_data = SAA7134_BOARD_AVERMEDIA_777, @@ -3422,6 +3496,18 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x0005, .driver_data = SAA7134_BOARD_MD7134_BRIDGE_2, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x5168, + .subdevice = 0x0300, + .driver_data = SAA7134_BOARD_FLYDVBS_LR300, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x4e42, + .subdevice = 0x0300,/* LR300 */ + .driver_data = SAA7134_BOARD_FLYDVBS_LR300, + },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, .subvendor = 0x1489, @@ -3446,6 +3532,36 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x3502, /* whats the difference to 0x3306 ?*/ .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x16be, + .subdevice = 0x0007, + .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x16be, + .subdevice = 0x0008, + .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, + .subdevice = 0x2c05, + .driver_data = SAA7134_BOARD_AVERMEDIA_777, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1489, + .subdevice = 0x0502, /* Cardbus version */ + .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x0919, /* Philips Proteus PRO 2309 */ + .subdevice = 0x2003, + .driver_data = SAA7134_BOARD_PROTEUS_2309, + },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -3548,6 +3664,12 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: case SAA7134_BOARD_FLYDVBT_LR301: case SAA7134_BOARD_FLYDVBTDUO: + case SAA7134_BOARD_PROTEUS_2309: + dev->has_remote = SAA7134_REMOTE_GPIO; + break; + case SAA7134_BOARD_FLYDVBS_LR300: + saa_writeb(SAA7134_GPIO_GPMODE3, 0x80); + saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x40); dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_MD5044: @@ -3732,6 +3854,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) case SAA7134_BOARD_PHILIPS_TIGER: case SAA7134_BOARD_TEVION_DVBT_220RF: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: + case SAA7134_BOARD_MEDION_MD8800_QUADRO: /* this is a hybrid board, initialize to analog mode * and configure firmware eeprom address */ diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index be3a81fc90a..09aa62f61af 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -843,7 +843,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, latency = 0x0A; } #endif - if (pci_pci_problems & PCIPCI_FAIL) { + if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) { printk(KERN_INFO "%s: quirk: this driver and your " "chipset may not work together" " in overlay mode.\n",dev->name); diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 279828b8f29..b6881541e70 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -34,17 +34,14 @@ #include <media/v4l2-common.h> #include "dvb-pll.h" -#ifdef HAVE_MT352 -# include "mt352.h" -# include "mt352_priv.h" /* FIXME */ -#endif -#ifdef HAVE_TDA1004X -# include "tda1004x.h" -#endif -#ifdef HAVE_NXT200X -# include "nxt200x.h" -#endif - +#include "mt352.h" +#include "mt352_priv.h" /* FIXME */ +#include "tda1004x.h" +#include "nxt200x.h" + +#include "tda10086.h" +#include "tda826x.h" +#include "isl6421.h" MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); @@ -54,8 +51,6 @@ module_param(antenna_pwr, int, 0444); MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)"); /* ------------------------------------------------------------------ */ - -#ifdef HAVE_MT352 static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on) { u32 ok; @@ -185,12 +180,8 @@ static struct mt352_config avermedia_777 = { .demod_address = 0xf, .demod_init = mt352_aver777_init, }; -#endif /* ------------------------------------------------------------------ */ - -#ifdef HAVE_TDA1004X - static int philips_tda6651_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct saa7134_dev *dev = fe->dvb->priv; @@ -969,11 +960,58 @@ static struct tda1004x_config tevion_dvbt220rf_config = { .request_firmware = NULL, }; -#endif +/* ------------------------------------------------------------------ */ + +static int md8800_dvbt_analog_mode(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + static u8 data[] = { 0x3c, 0x33, 0x68}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + + i2c_transfer(&dev->i2c_adap, &msg, 1); + philips_tda827xa_tuner_sleep( 0x61, fe); + return 0; +} + +static int md8800_dvbt_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + int ret; + struct saa7134_dev *dev = fe->dvb->priv; + static u8 tda8290_close[] = { 0x21, 0xc0}; + static u8 tda8290_open[] = { 0x21, 0x80}; + struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2}; + /* close tda8290 i2c bridge */ + tda8290_msg.buf = tda8290_close; + ret = i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); + if (ret != 1) + return -EIO; + msleep(20); + ret = philips_tda827xa_pll_set(0x60, fe, params); + if (ret != 0) + return ret; + /* open tda8290 i2c bridge */ + tda8290_msg.buf = tda8290_open; + i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); + return ret; +} + +static struct tda1004x_config md8800_dvbt_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .if_freq = TDA10046_FREQ_045, + .request_firmware = NULL, +}; + +static struct tda10086_config flydvbs = { + .demod_address = 0x0e, + .invert = 0, +}; /* ------------------------------------------------------------------ */ -#ifdef HAVE_NXT200X static struct nxt200x_config avertvhda180 = { .demod_address = 0x0a, }; @@ -991,7 +1029,6 @@ static struct nxt200x_config kworldatsc110 = { .demod_address = 0x0a, .set_pll_input = nxt200x_set_pll_input, }; -#endif /* ------------------------------------------------------------------ */ @@ -1009,29 +1046,26 @@ static int dvb_init(struct saa7134_dev *dev) dev); switch (dev->board) { -#ifdef HAVE_MT352 case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL: printk("%s: pinnacle 300i dvb setup\n",dev->name); - dev->dvb.frontend = mt352_attach(&pinnacle_300i, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(mt352_attach, &pinnacle_300i, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.set_params = mt352_pinnacle_tuner_set_params; } break; - case SAA7134_BOARD_AVERMEDIA_777: printk("%s: avertv 777 dvb setup\n",dev->name); - dev->dvb.frontend = mt352_attach(&avermedia_777, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.calc_regs = mt352_aver777_tuner_calc_regs; } break; -#endif -#ifdef HAVE_TDA1004X case SAA7134_BOARD_MD7134: - dev->dvb.frontend = tda10046_attach(&medion_cardbus, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &medion_cardbus, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.init = philips_fmd1216_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = philips_fmd1216_tuner_sleep; @@ -1039,16 +1073,18 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_PHILIPS_TOUGH: - dev->dvb.frontend = tda10046_attach(&philips_tu1216_60_config, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &philips_tu1216_60_config, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_tuner_60_init; dev->dvb.frontend->ops.tuner_ops.set_params = philips_tu1216_tuner_60_set_params; } break; case SAA7134_BOARD_FLYDVBTDUO: - dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &tda827x_lifeview_config, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep; @@ -1056,8 +1092,9 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: - dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &tda827x_lifeview_config, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep; @@ -1065,8 +1102,9 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_PHILIPS_EUROPA: - dev->dvb.frontend = tda10046_attach(&philips_europa_config, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &philips_europa_config, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->original_demod_sleep = dev->dvb.frontend->ops.sleep; dev->dvb.frontend->ops.sleep = philips_europa_demod_sleep; @@ -1076,8 +1114,9 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_VIDEOMATE_DVBT_300: - dev->dvb.frontend = tda10046_attach(&philips_europa_config, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &philips_europa_config, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep; @@ -1085,16 +1124,18 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_VIDEOMATE_DVBT_200: - dev->dvb.frontend = tda10046_attach(&philips_tu1216_61_config, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &philips_tu1216_61_config, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_tuner_61_init; dev->dvb.frontend->ops.tuner_ops.set_params = philips_tu1216_tuner_61_set_params; } break; case SAA7134_BOARD_PHILIPS_TIGER: - dev->dvb.frontend = tda10046_attach(&philips_tiger_config, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &philips_tiger_config, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; @@ -1102,8 +1143,9 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: - dev->dvb.frontend = tda10046_attach(&philips_tiger_config, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &philips_tiger_config, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; @@ -1111,8 +1153,9 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_FLYDVBT_LR301: - dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &tda827x_lifeview_config, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep; @@ -1120,16 +1163,18 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_FLYDVB_TRIO: - dev->dvb.frontend = tda10046_attach(&lifeview_trio_config, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &lifeview_trio_config, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep; dev->dvb.frontend->ops.tuner_ops.set_params = lifeview_trio_tuner_set_params; } break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: - dev->dvb.frontend = tda10046_attach(&ads_tech_duo_config, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &ads_tech_duo_config, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.init = ads_duo_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = ads_duo_tuner_sleep; @@ -1137,37 +1182,63 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_TEVION_DVBT_220RF: - dev->dvb.frontend = tda10046_attach(&tevion_dvbt220rf_config, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &tevion_dvbt220rf_config, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.sleep = tevion_dvb220rf_tuner_sleep; dev->dvb.frontend->ops.tuner_ops.set_params = tevion_dvb220rf_tuner_set_params; } break; case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: - dev->dvb.frontend = tda10046_attach(&ads_tech_duo_config, - &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, + &ads_tech_duo_config, + &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.tuner_ops.init = ads_duo_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = ads_duo_tuner_sleep; dev->dvb.frontend->ops.tuner_ops.set_params = ads_duo_tuner_set_params; } break; -#endif -#ifdef HAVE_NXT200X + case SAA7134_BOARD_MEDION_MD8800_QUADRO: + dev->dvb.frontend = tda10046_attach(&md8800_dvbt_config, + &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; + dev->dvb.frontend->ops.tuner_ops.sleep = md8800_dvbt_analog_mode; + dev->dvb.frontend->ops.tuner_ops.set_params = md8800_dvbt_pll_set; + } + break; case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180: - dev->dvb.frontend = nxt200x_attach(&avertvhda180, &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180, + &dev->i2c_adap); if (dev->dvb.frontend) { - dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->i2c_adap, &dvb_pll_tdhu2); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + NULL, &dvb_pll_tdhu2); } break; case SAA7134_BOARD_KWORLD_ATSC110: - dev->dvb.frontend = nxt200x_attach(&kworldatsc110, &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(nxt200x_attach, &kworldatsc110, + &dev->i2c_adap); + if (dev->dvb.frontend) { + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + NULL, &dvb_pll_tuv1236d); + } + break; + case SAA7134_BOARD_FLYDVBS_LR300: + dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, + &dev->i2c_adap); if (dev->dvb.frontend) { - dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->i2c_adap, &dvb_pll_tuv1236d); + if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x60, + &dev->i2c_adap, 0) == NULL) { + printk("%s: No tda826x found!\n", __FUNCTION__); + } + if (dvb_attach(isl6421_attach, dev->dvb.frontend, + &dev->i2c_adap, 0x08, 0, 0) == NULL) { + printk("%s: No ISL6421 found!\n", __FUNCTION__); + } } break; -#endif default: printk("%s: Huh? unknown DVB card?\n",dev->name); break; diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 7c595492c56..f7ea857d5d7 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -228,6 +228,12 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keyup = 0x400000; polling = 50; // ms break; + case SAA7134_BOARD_PROTEUS_2309: + ir_codes = ir_codes_proteus_2309; + mask_keycode = 0x00007F; + mask_keyup = 0x000080; + polling = 50; // ms + break; case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_VIDEOMATE_DVBT_200: ir_codes = ir_codes_videomate_tv_pvr; diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c index 0db53d192b2..d31220d2049 100644 --- a/drivers/media/video/saa7134/saa7134-tvaudio.c +++ b/drivers/media/video/saa7134/saa7134-tvaudio.c @@ -1046,6 +1046,7 @@ int saa7134_tvaudio_do_scan(struct saa7134_dev *dev) } EXPORT_SYMBOL(saa_dsp_writel); +EXPORT_SYMBOL(saa7134_tvaudio_setmute); /* ----------------------------------------------------------- */ /* diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index c04ce6152fd..7db7b970595 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -223,6 +223,9 @@ struct saa7134_format { #define SAA7134_BOARD_MD7134_BRIDGE_2 93 #define SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS 94 #define SAA7134_BOARD_FLYVIDEO3000_NTSC 95 +#define SAA7134_BOARD_MEDION_MD8800_QUADRO 96 +#define SAA7134_BOARD_FLYDVBS_LR300 97 +#define SAA7134_BOARD_PROTEUS_2309 98 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c index 8dab481d384..87ffb0e84a7 100644 --- a/drivers/media/video/tda9887.c +++ b/drivers/media/video/tda9887.c @@ -480,6 +480,8 @@ static int tda9887_set_config(struct tuner *t, char *buf) } if ((t->tda9887_config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC)) buf[1] &= ~cQSS; + if (t->tda9887_config & TDA9887_GATING_18) + buf[3] &= ~cGating_36; return 0; } diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c index abe37cf632c..63db4e97ae6 100644 --- a/drivers/media/video/tuner-simple.c +++ b/drivers/media/video/tuner-simple.c @@ -10,7 +10,7 @@ #include <media/v4l2-common.h> static int offset = 0; -module_param(offset, int, 0666); +module_param(offset, int, 0664); MODULE_PARM_DESC(offset,"Allows to specify an offset for tuner"); /* ---------------------------------------------------------------------- */ @@ -331,6 +331,8 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq) else if (params->default_top_high) config |= TDA9887_TOP(params->default_top_high); } + if (params->default_pll_gating_18) + config |= TDA9887_GATING_18; i2c_clients_command(c->adapter, TDA9887_SET_CONFIG, &config); } tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", @@ -439,8 +441,6 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq) buffer[3] = 0xa4; break; } - buffer[0] = (div>>8) & 0x7f; - buffer[1] = div & 0xff; if (params->cb_first_if_lower_freq && div < t->last_div) { buffer[0] = buffer[2]; buffer[1] = buffer[3]; diff --git a/drivers/media/video/tuner-types.c b/drivers/media/video/tuner-types.c index 8b542599ed4..8fff642fad5 100644 --- a/drivers/media/video/tuner-types.c +++ b/drivers/media/video/tuner-types.c @@ -650,6 +650,7 @@ static struct tuner_params tuner_microtune_4049_fm5_params[] = { .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), .has_tda9887 = 1, .port1_invert_for_secam_lc = 1, + .default_pll_gating_18 = 1, }, }; diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 936e3f746fb..fcaef4bf828 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -28,6 +28,7 @@ #include <linux/i2c-algo-bit.h> #include <linux/init.h> #include <linux/smp_lock.h> +#include <linux/kthread.h> #include <media/tvaudio.h> #include <media/v4l2-common.h> @@ -124,11 +125,8 @@ struct CHIPSTATE { int input; /* thread */ - pid_t tpid; - struct completion texit; - wait_queue_head_t wq; + struct task_struct *thread; struct timer_list wt; - int done; int watch_stereo; int audmode; }; @@ -264,28 +262,23 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) static void chip_thread_wake(unsigned long data) { struct CHIPSTATE *chip = (struct CHIPSTATE*)data; - wake_up_interruptible(&chip->wq); + wake_up_process(chip->thread); } static int chip_thread(void *data) { - DECLARE_WAITQUEUE(wait, current); struct CHIPSTATE *chip = data; struct CHIPDESC *desc = chiplist + chip->type; - daemonize("%s", chip->c.name); - allow_signal(SIGTERM); v4l_dbg(1, debug, &chip->c, "%s: thread started\n", chip->c.name); for (;;) { - add_wait_queue(&chip->wq, &wait); - if (!chip->done) { - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_INTERRUPTIBLE); + if (!kthread_should_stop()) schedule(); - } - remove_wait_queue(&chip->wq, &wait); + set_current_state(TASK_RUNNING); try_to_freeze(); - if (chip->done || signal_pending(current)) + if (kthread_should_stop()) break; v4l_dbg(1, debug, &chip->c, "%s: thread wakeup\n", chip->c.name); @@ -301,7 +294,6 @@ static int chip_thread(void *data) } v4l_dbg(1, debug, &chip->c, "%s: thread exiting\n", chip->c.name); - complete_and_exit(&chip->texit, 0); return 0; } @@ -1536,19 +1528,18 @@ static int chip_attach(struct i2c_adapter *adap, int addr, int kind) chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); } - chip->tpid = -1; + chip->thread = NULL; if (desc->checkmode) { /* start async thread */ init_timer(&chip->wt); chip->wt.function = chip_thread_wake; chip->wt.data = (unsigned long)chip; - init_waitqueue_head(&chip->wq); - init_completion(&chip->texit); - chip->tpid = kernel_thread(chip_thread,(void *)chip,0); - if (chip->tpid < 0) - v4l_warn(&chip->c, "%s: kernel_thread() failed\n", + chip->thread = kthread_run(chip_thread, chip, chip->c.name); + if (IS_ERR(chip->thread)) { + v4l_warn(&chip->c, "%s: failed to create kthread\n", chip->c.name); - wake_up_interruptible(&chip->wq); + chip->thread = NULL; + } } return 0; } @@ -1569,11 +1560,10 @@ static int chip_detach(struct i2c_client *client) struct CHIPSTATE *chip = i2c_get_clientdata(client); del_timer_sync(&chip->wt); - if (chip->tpid >= 0) { + if (chip->thread) { /* shutdown async thread */ - chip->done = 1; - wake_up_interruptible(&chip->wq); - wait_for_completion(&chip->texit); + kthread_stop(chip->thread); + chip->thread = NULL; } i2c_detach_client(&chip->c); diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index d95529e8e51..cd1502ac956 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -605,6 +605,8 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, tvee->tuner_formats |= hauppauge_tuner_fmt[i].id; t_fmt_name1[j++] = hauppauge_tuner_fmt[i].name; } + } + for (i = j = 0; i < 8; i++) { if (t_format2 & (1 << i)) { tvee->tuner2_formats |= hauppauge_tuner_fmt[i].id; t_fmt_name2[j++] = hauppauge_tuner_fmt[i].name; diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index b167ffab252..bc0a4fc27b2 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -294,7 +294,7 @@ static inline void tvp5150_selmux(struct i2c_client *c) if ((decoder->route.output & TVP5150_BLACK_SCREEN) || !decoder->enable) input = 8; - switch (input) { + switch (decoder->route.input) { case TVP5150_COMPOSITE1: input |= 2; /* fall through */ @@ -308,6 +308,11 @@ static inline void tvp5150_selmux(struct i2c_client *c) break; } + tvp5150_dbg( 1, "Selecting video route: route input=%i, output=%i " + "=> tvp5150 input=%i, opmode=%i\n", + decoder->route.input,decoder->route.output, + input, opmode ); + tvp5150_write(c, TVP5150_OP_MODE_CTL, opmode); tvp5150_write(c, TVP5150_VD_IN_SRC_SEL_1, input); }; diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c index 6f31ecc8884..4eee8be8831 100644 --- a/drivers/media/video/usbvideo/konicawc.c +++ b/drivers/media/video/usbvideo/konicawc.c @@ -222,6 +222,7 @@ static void konicawc_adjust_picture(struct uvd *uvd) static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev) { struct input_dev *input_dev; + int error; usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); strncat(cam->input_physname, "/input0", sizeof(cam->input_physname)); @@ -242,7 +243,13 @@ static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev input_dev->private = cam; - input_register_device(cam->input); + error = input_register_device(cam->input); + if (error) { + warn("Failed to register camera's input device, err: %d\n", + error); + input_free_device(cam->input); + cam->input = NULL; + } } static void konicawc_unregister_input(struct konicawc *cam) diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c index 90d48e8510b..08f9559a6bf 100644 --- a/drivers/media/video/usbvideo/vicam.c +++ b/drivers/media/video/usbvideo/vicam.c @@ -7,6 +7,7 @@ * Monroe Williams (monroe@pobox.com) * * Supports 3COM HomeConnect PC Digital WebCam + * Supports Compro PS39U WebCam * * 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 @@ -60,6 +61,8 @@ /* Define these values to match your device */ #define USB_VICAM_VENDOR_ID 0x04c1 #define USB_VICAM_PRODUCT_ID 0x009d +#define USB_COMPRO_VENDOR_ID 0x0602 +#define USB_COMPRO_PRODUCT_ID 0x1001 #define VICAM_BYTES_PER_PIXEL 3 #define VICAM_MAX_READ_SIZE (512*242+128) @@ -1254,6 +1257,7 @@ static struct video_device vicam_template = { /* table of devices that work with this driver */ static struct usb_device_id vicam_table[] = { {USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID)}, + {USB_DEVICE(USB_COMPRO_VENDOR_ID, USB_COMPRO_PRODUCT_ID)}, {} /* Terminating entry */ }; diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index d7c3fcbc80f..1d899e2db39 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -349,6 +349,8 @@ v4l_compat_translate_ioctl(struct inode *inode, { struct video_buffer *buffer = arg; + memset(buffer, 0, sizeof(*buffer)); + err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2); if (err < 0) { dprintk("VIDIOCGFBUF / VIDIOC_G_FBUF: %d\n",err); @@ -361,7 +363,7 @@ v4l_compat_translate_ioctl(struct inode *inode, switch (fbuf2.fmt.pixelformat) { case V4L2_PIX_FMT_RGB332: buffer->depth = 8; - break; + break; case V4L2_PIX_FMT_RGB555: buffer->depth = 15; break; @@ -377,9 +379,13 @@ v4l_compat_translate_ioctl(struct inode *inode, default: buffer->depth = 0; } - if (0 != fbuf2.fmt.bytesperline) + if (fbuf2.fmt.bytesperline) { buffer->bytesperline = fbuf2.fmt.bytesperline; - else { + if (!buffer->depth && buffer->width) + buffer->depth = ((fbuf2.fmt.bytesperline<<3) + + (buffer->width-1) ) + /buffer->width; + } else { buffer->bytesperline = (buffer->width * buffer->depth + 7) & 7; buffer->bytesperline >>= 3; diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 8d972ffdaf9..78d28b03ec9 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -938,6 +938,7 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg) case VIDIOC_INT_AUDIO_CLOCK_FREQ: case VIDIOC_INT_I2S_CLOCK_FREQ: case VIDIOC_INT_S_STANDBY: + case VIDIOC_INT_RESET: { u32 *p=arg; diff --git a/drivers/media/video/video-buf-dvb.c b/drivers/media/video/video-buf-dvb.c index 7ee8a53cd33..f53edf1923b 100644 --- a/drivers/media/video/video-buf-dvb.c +++ b/drivers/media/video/video-buf-dvb.c @@ -223,6 +223,7 @@ fail_dmxdev: fail_dmx: dvb_unregister_frontend(dvb->frontend); fail_frontend: + dvb_frontend_detach(dvb->frontend); dvb_unregister_adapter(&dvb->adapter); fail_adapter: return result; @@ -236,6 +237,7 @@ void videobuf_dvb_unregister(struct videobuf_dvb *dvb) dvb_dmxdev_release(&dvb->dmxdev); dvb_dmx_release(&dvb->demux); dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); dvb_unregister_adapter(&dvb->adapter); } diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index edd7b83c346..479a0675cf6 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -739,13 +739,13 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_DQBUF: { struct v4l2_buffer *p=arg; - if (!vfd->vidioc_qbuf) + if (!vfd->vidioc_dqbuf) break; ret = check_fmt (vfd, p->type); if (ret) break; - ret=vfd->vidioc_qbuf(file, fh, p); + ret=vfd->vidioc_dqbuf(file, fh, p); if (!ret) dbgbuf(cmd,vfd,p); break; @@ -836,7 +836,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, break; } - if (index < 0 || index >= vfd->tvnormsize) { + if (index<0 || index >= vfd->tvnormsize) { ret=-EINVAL; break; } @@ -1283,9 +1283,29 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_PARM: { struct v4l2_streamparm *p=arg; - if (!vfd->vidioc_g_parm) - break; - ret=vfd->vidioc_g_parm(file, fh, p); + if (vfd->vidioc_g_parm) { + ret=vfd->vidioc_g_parm(file, fh, p); + } else { + struct v4l2_standard s; + + if (!vfd->tvnormsize) { + printk (KERN_WARNING "%s: no TV norms defined!\n", + vfd->name); + break; + } + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + v4l2_video_std_construct(&s, vfd->tvnorms[vfd->current_norm].id, + vfd->tvnorms[vfd->current_norm].name); + + memset(p,0,sizeof(*p)); + + p->parm.capture.timeperframe = s.frameperiod; + ret=0; + } + dbgarg (cmd, "type=%d\n", p->type); break; } diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c index 268e69fdefc..d1e04f7c530 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -4404,7 +4404,6 @@ static struct video_device v4l_device_template = { .name = "NOT SET", //.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE | // VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY - .hardware = VID_HARDWARE_VINO, .fops = &vino_fops, .minor = -1, }; diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 841884af0cc..e7c01d560b6 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -992,7 +992,8 @@ static int vidiocgmbuf (struct file *file, void *priv, struct video_mbuf *mbuf) struct vivi_fh *fh=priv; struct videobuf_queue *q=&fh->vb_vidq; struct v4l2_requestbuffers req; - unsigned int i, ret; + unsigned int i; + int ret; req.type = q->type; req.count = 8; @@ -1359,6 +1360,8 @@ static int __init vivi_init(void) dev->vidq.timeout.data = (unsigned long)dev; init_timer(&dev->vidq.timeout); + vivi.current_norm = tvnorms[0].id; + ret = video_register_device(&vivi, VFL_TYPE_GRABBER, video_nr); printk(KERN_INFO "Video Technology Magazine Virtual Video Capture Board (Load status: %d)\n", ret); return ret; diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c index 1eca7e65d23..8ef31ed7d3f 100644 --- a/drivers/media/video/vpx3220.c +++ b/drivers/media/video/vpx3220.c @@ -744,6 +744,6 @@ vpx3220_cleanup (void) module_init(vpx3220_init); module_exit(vpx3220_cleanup); -MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video encoder driver"); +MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver"); MODULE_AUTHOR("Laurent Pinchart"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/zoran_card.c b/drivers/media/video/zoran_card.c index 29f59c36f00..9f21d0ba0f0 100644 --- a/drivers/media/video/zoran_card.c +++ b/drivers/media/video/zoran_card.c @@ -1620,10 +1620,10 @@ init_dc10_cards (void) dprintk(5, KERN_DEBUG "Jotti is een held!\n"); /* some mainboards might not do PCI-PCI data transfer well */ - if (pci_pci_problems & PCIPCI_FAIL) { + if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL|PCIPCI_ALIMAGIK)) { dprintk(1, KERN_WARNING - "%s: chipset may not support reliable PCI-PCI DMA\n", + "%s: chipset does not support reliable PCI-PCI DMA\n", ZORAN_NAME); } @@ -1631,7 +1631,7 @@ init_dc10_cards (void) for (i = 0; i < zoran_num; i++) { struct zoran *zr = &zoran[i]; - if (pci_pci_problems & PCIPCI_NATOMA && zr->revision <= 1) { + if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) { zr->jpg_buffers.need_contiguous = 1; dprintk(1, KERN_INFO diff --git a/drivers/media/video/zoran_driver.c b/drivers/media/video/zoran_driver.c index 5f90db27892..862a984c215 100644 --- a/drivers/media/video/zoran_driver.c +++ b/drivers/media/video/zoran_driver.c @@ -1512,6 +1512,13 @@ setup_fbuffer (struct file *file, if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) return -EPERM; + /* Don't allow frame buffer overlay if PCI or AGP is buggy, or on + ALi Magik (that needs very low latency while the card needs a + higher value always) */ + + if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK)) + return -ENXIO; + /* we need a bytesperline value, even if not given */ if (!bytesperline) bytesperline = width * ((fmt->depth + 7) & ~7) / 8; diff --git a/drivers/media/video/zr36120.c b/drivers/media/video/zr36120.c index 50437383ed6..9240638a013 100644 --- a/drivers/media/video/zr36120.c +++ b/drivers/media/video/zr36120.c @@ -987,6 +987,8 @@ int zoran_ioctl(struct video_device* dev, unsigned int cmd, void *arg) VID_TYPE_SCALES; if (ztv->have_tuner) c.type |= VID_TYPE_TUNER; + if (pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) + c.type &= ~VID_TYPE_OVERLAY; if (ztv->have_decoder) { c.channels = ztv->card->video_inputs; c.audios = ztv->card->audio_inputs; @@ -1284,6 +1286,8 @@ int zoran_ioctl(struct video_device* dev, unsigned int cmd, void *arg) struct video_buffer v; if(!capable(CAP_SYS_ADMIN)) return -EPERM; + if (pcipci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) + return -ENXIO; if (copy_from_user(&v, arg,sizeof(v))) return -EFAULT; DEBUG(printk(CARD_DEBUG "VIDIOCSFBUF(%p,%d,%d,%d,%d)\n",CARD,v.base, v.width,v.height,v.depth,v.bytesperline)); @@ -2030,7 +2034,7 @@ void release_zoran(int max) /* free it */ free_irq(ztv->dev->irq,ztv); - /* unregister i2c_bus */ + /* unregister i2c_bus */ i2c_unregister_bus((&ztv->i2c)); /* unmap and free memory */ diff --git a/drivers/message/i2o/Kconfig b/drivers/message/i2o/Kconfig index fef67710388..6443392bfff 100644 --- a/drivers/message/i2o/Kconfig +++ b/drivers/message/i2o/Kconfig @@ -88,7 +88,7 @@ config I2O_BUS config I2O_BLOCK tristate "I2O Block OSM" - depends on I2O + depends on I2O && BLOCK ---help--- Include support for the I2O Block OSM. The Block OSM presents disk and other structured block devices to the operating system. If you diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c index 1ddc2fb429d..eaba81bf2ec 100644 --- a/drivers/message/i2o/i2o_block.c +++ b/drivers/message/i2o/i2o_block.c @@ -390,9 +390,9 @@ static int i2o_block_prep_req_fn(struct request_queue *q, struct request *req) } /* request is already processed by us, so return */ - if (req->flags & REQ_SPECIAL) { + if (blk_special_request(req)) { osm_debug("REQ_SPECIAL already set!\n"); - req->flags |= REQ_DONTPREP; + req->cmd_flags |= REQ_DONTPREP; return BLKPREP_OK; } @@ -411,7 +411,8 @@ static int i2o_block_prep_req_fn(struct request_queue *q, struct request *req) ireq = req->special; /* do not come back here */ - req->flags |= REQ_DONTPREP | REQ_SPECIAL; + req->cmd_type = REQ_TYPE_SPECIAL; + req->cmd_flags |= REQ_DONTPREP; return BLKPREP_OK; }; diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 02776814443..82938ad6ddb 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -58,6 +58,7 @@ static int adcsync; static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) { struct input_dev *idev = ts->idev; + input_report_abs(idev, ABS_X, x); input_report_abs(idev, ABS_Y, y); input_report_abs(idev, ABS_PRESSURE, pressure); @@ -67,6 +68,7 @@ static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) { struct input_dev *idev = ts->idev; + input_report_abs(idev, ABS_PRESSURE, 0); input_sync(idev); } @@ -189,6 +191,7 @@ static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts) static inline int ucb1x00_ts_pen_down(struct ucb1x00_ts *ts) { unsigned int val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR); + if (machine_is_collie()) return (!(val & (UCB_TS_CR_TSPX_LOW))); else @@ -291,6 +294,7 @@ static int ucb1x00_thread(void *_ts) static void ucb1x00_ts_irq(int idx, void *id) { struct ucb1x00_ts *ts = id; + ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); wake_up(&ts->irq_wait); } @@ -372,36 +376,43 @@ static int ucb1x00_ts_resume(struct ucb1x00_dev *dev) static int ucb1x00_ts_add(struct ucb1x00_dev *dev) { struct ucb1x00_ts *ts; + struct input_dev *idev; + int err; ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL); - if (!ts) - return -ENOMEM; - - ts->idev = input_allocate_device(); - if (!ts->idev) { - kfree(ts); - return -ENOMEM; + idev = input_allocate_device(); + if (!ts || !idev) { + err = -ENOMEM; + goto fail; } ts->ucb = dev->ucb; + ts->idev = idev; ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; - ts->idev->private = ts; - ts->idev->name = "Touchscreen panel"; - ts->idev->id.product = ts->ucb->id; - ts->idev->open = ucb1x00_ts_open; - ts->idev->close = ucb1x00_ts_close; + idev->private = ts; + idev->name = "Touchscreen panel"; + idev->id.product = ts->ucb->id; + idev->open = ucb1x00_ts_open; + idev->close = ucb1x00_ts_close; - __set_bit(EV_ABS, ts->idev->evbit); - __set_bit(ABS_X, ts->idev->absbit); - __set_bit(ABS_Y, ts->idev->absbit); - __set_bit(ABS_PRESSURE, ts->idev->absbit); + __set_bit(EV_ABS, idev->evbit); + __set_bit(ABS_X, idev->absbit); + __set_bit(ABS_Y, idev->absbit); + __set_bit(ABS_PRESSURE, idev->absbit); - input_register_device(ts->idev); + err = input_register_device(idev); + if (err) + goto fail; dev->priv = ts; return 0; + + fail: + input_free_device(idev); + kfree(ts); + return err; } static void ucb1x00_ts_remove(struct ucb1x00_dev *dev) diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 45bcf098e76..f540bd88dc5 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -21,7 +21,7 @@ config MMC_DEBUG config MMC_BLOCK tristate "MMC block device driver" - depends on MMC + depends on MMC && BLOCK default y help Say Y here to enable the MMC block device driver support. diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index d2957e35cc6..b1f6e03e7aa 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -24,7 +24,8 @@ obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_OMAP) += omap.o obj-$(CONFIG_MMC_AT91RM9200) += at91_mci.o -mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o +mmc_core-y := mmc.o mmc_sysfs.o +mmc_core-$(CONFIG_BLOCK) += mmc_queue.o ifeq ($(CONFIG_MMC_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/mmc/at91_mci.c b/drivers/mmc/at91_mci.c index 6b7638b8429..cb142a66098 100644 --- a/drivers/mmc/at91_mci.c +++ b/drivers/mmc/at91_mci.c @@ -822,6 +822,7 @@ static int at91_mci_probe(struct platform_device *pdev) mmc->f_min = 375000; mmc->f_max = 25000000; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_BYTEBLOCK; host = mmc_priv(mmc); host->mmc = mmc; @@ -850,7 +851,7 @@ static int at91_mci_probe(struct platform_device *pdev) /* * Allocate the MCI interrupt */ - ret = request_irq(AT91_ID_MCI, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host); + ret = request_irq(AT91RM9200_ID_MCI, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host); if (ret) { printk(KERN_ERR "Failed to request MCI interrupt\n"); clk_disable(mci_clk); @@ -906,7 +907,7 @@ static int at91_mci_remove(struct platform_device *pdev) mmc_remove_host(mmc); at91_mci_disable(); - free_irq(AT91_ID_MCI, host); + free_irq(AT91RM9200_ID_MCI, host); mmc_free_host(mmc); clk_disable(mci_clk); /* Disable the peripheral clock */ diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c index fb6565b98f3..1b79dd271aa 100644 --- a/drivers/mmc/imxmmc.c +++ b/drivers/mmc/imxmmc.c @@ -956,7 +956,7 @@ static int imxmci_probe(struct platform_device *pdev) mmc->f_min = 150000; mmc->f_max = CLK_RATE/2; mmc->ocr_avail = MMC_VDD_32_33; - mmc->caps |= MMC_CAP_4_BIT_DATA; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_BYTEBLOCK; /* MMC core transfer sizes tunable parameters */ mmc->max_hw_segs = 64; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 74eaaee66de..5b9caa7978d 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -996,7 +996,6 @@ static void mmc_read_scrs(struct mmc_host *host) mmc_set_data_timeout(&data, card, 0); - data.blksz_bits = 3; data.blksz = 1 << 3; data.blocks = 1; data.flags = MMC_DATA_READ; diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index a0e0dad1b41..db0e8ad439a 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -32,6 +32,7 @@ #include <linux/mmc/card.h> #include <linux/mmc/host.h> #include <linux/mmc/protocol.h> +#include <linux/mmc/host.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -165,6 +166,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) do { struct mmc_blk_request brq; struct mmc_command cmd; + u32 readcmd, writecmd; memset(&brq, 0, sizeof(struct mmc_blk_request)); brq.mrq.cmd = &brq.cmd; @@ -172,7 +174,6 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) brq.cmd.arg = req->sector << 9; brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - brq.data.blksz_bits = md->block_bits; brq.data.blksz = 1 << md->block_bits; brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); brq.stop.opcode = MMC_STOP_TRANSMISSION; @@ -181,20 +182,31 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ); - if (rq_data_dir(req) == READ) { - brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK; - brq.data.flags |= MMC_DATA_READ; - } else { - brq.cmd.opcode = MMC_WRITE_BLOCK; - brq.data.flags |= MMC_DATA_WRITE; + /* + * If the host doesn't support multiple block writes, force + * block writes to single block. + */ + if (rq_data_dir(req) != READ && + !(card->host->caps & MMC_CAP_MULTIWRITE)) brq.data.blocks = 1; - } if (brq.data.blocks > 1) { brq.data.flags |= MMC_DATA_MULTI; brq.mrq.stop = &brq.stop; + readcmd = MMC_READ_MULTIPLE_BLOCK; + writecmd = MMC_WRITE_MULTIPLE_BLOCK; } else { brq.mrq.stop = NULL; + readcmd = MMC_READ_SINGLE_BLOCK; + writecmd = MMC_WRITE_BLOCK; + } + + if (rq_data_dir(req) == READ) { + brq.cmd.opcode = readcmd; + brq.data.flags |= MMC_DATA_READ; + } else { + brq.cmd.opcode = writecmd; + brq.data.flags |= MMC_DATA_WRITE; } brq.data.sg = mq->sg; @@ -219,27 +231,29 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) goto cmd_err; } - do { - int err; - - cmd.opcode = MMC_SEND_STATUS; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(card->host, &cmd, 5); - if (err) { - printk(KERN_ERR "%s: error %d requesting status\n", - req->rq_disk->disk_name, err); - goto cmd_err; - } - } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); + if (rq_data_dir(req) != READ) { + do { + int err; + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 5); + if (err) { + printk(KERN_ERR "%s: error %d requesting status\n", + req->rq_disk->disk_name, err); + goto cmd_err; + } + } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); #if 0 - if (cmd.resp[0] & ~0x00000900) - printk(KERN_ERR "%s: status = %08x\n", - req->rq_disk->disk_name, cmd.resp[0]); - if (mmc_decode_status(cmd.resp)) - goto cmd_err; + if (cmd.resp[0] & ~0x00000900) + printk(KERN_ERR "%s: status = %08x\n", + req->rq_disk->disk_name, cmd.resp[0]); + if (mmc_decode_status(cmd.resp)) + goto cmd_err; #endif + } /* * A block was successfully transferred. diff --git a/drivers/mmc/mmc_queue.c b/drivers/mmc/mmc_queue.c index 74f8cdeeff0..4ccdd82b680 100644 --- a/drivers/mmc/mmc_queue.c +++ b/drivers/mmc/mmc_queue.c @@ -28,7 +28,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req) struct mmc_queue *mq = q->queuedata; int ret = BLKPREP_KILL; - if (req->flags & REQ_SPECIAL) { + if (blk_special_request(req)) { /* * Special commands already have the command * blocks already setup in req->special. @@ -36,7 +36,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req) BUG_ON(!req->special); ret = BLKPREP_OK; - } else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) { + } else if (blk_fs_request(req) || blk_pc_request(req)) { /* * Block I/O requests need translating according * to the protocol. @@ -50,7 +50,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req) } if (ret == BLKPREP_OK) - req->flags |= REQ_DONTPREP; + req->cmd_flags |= REQ_DONTPREP; return ret; } diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c index 1886562abdd..2b5a0cc9ea5 100644 --- a/drivers/mmc/mmci.c +++ b/drivers/mmc/mmci.c @@ -69,12 +69,13 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) unsigned int datactrl, timeout, irqmask; unsigned long long clks; void __iomem *base; + int blksz_bits; DBG(host, "blksz %04x blks %04x flags %08x\n", - 1 << data->blksz_bits, data->blocks, data->flags); + data->blksz, data->blocks, data->flags); host->data = data; - host->size = data->blocks << data->blksz_bits; + host->size = data->blksz; host->data_xfered = 0; mmci_init_sg(host, data); @@ -88,7 +89,10 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) writel(timeout, base + MMCIDATATIMER); writel(host->size, base + MMCIDATALENGTH); - datactrl = MCI_DPSM_ENABLE | data->blksz_bits << 4; + blksz_bits = ffs(data->blksz) - 1; + BUG_ON(1 << blksz_bits != data->blksz); + + datactrl = MCI_DPSM_ENABLE | blksz_bits << 4; if (data->flags & MMC_DATA_READ) { datactrl |= MCI_DPSM_DIRECTION; irqmask = MCI_RXFIFOHALFFULLMASK; @@ -145,7 +149,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, unsigned int status) { if (status & MCI_DATABLOCKEND) { - host->data_xfered += 1 << data->blksz_bits; + host->data_xfered += data->blksz; } if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { if (status & MCI_DATACRCFAIL) @@ -505,6 +509,7 @@ static int mmci_probe(struct amba_device *dev, void *id) mmc->f_min = (host->mclk + 511) / 512; mmc->f_max = min(host->mclk, fmax); mmc->ocr_avail = plat->ocr_mask; + mmc->caps = MMC_CAP_MULTIWRITE; /* * We can do SGIO diff --git a/drivers/mmc/omap.c b/drivers/mmc/omap.c index ddf06b32c15..52c9e52e6b7 100644 --- a/drivers/mmc/omap.c +++ b/drivers/mmc/omap.c @@ -1034,13 +1034,14 @@ static int __init mmc_omap_probe(struct platform_device *pdev) host->irq = pdev->resource[1].start; host->base = (void __iomem*)IO_ADDRESS(r->start); - if (minfo->wire4) - mmc->caps |= MMC_CAP_4_BIT_DATA; - mmc->ops = &mmc_omap_ops; mmc->f_min = 400000; mmc->f_max = 24000000; mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; + mmc->caps = MMC_CAP_BYTEBLOCK; + + if (minfo->wire4) + mmc->caps |= MMC_CAP_4_BIT_DATA; /* Use scatterlist DMA to reduce per-transfer costs. * NOTE max_seg_size assumption that small blocks aren't diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 4e21b3b9d33..fdfc3838dd7 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -1262,7 +1262,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) mmc->ops = &sdhci_ops; mmc->f_min = host->max_clk / 256; mmc->f_max = host->max_clk; - mmc->caps = MMC_CAP_4_BIT_DATA; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; mmc->ocr_avail = 0; if (caps & SDHCI_CAN_VDD_330) diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index c351c6d1a18..6435a6822ad 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -1323,7 +1323,7 @@ static int __devinit wbsd_alloc_mmc(struct device *dev) mmc->f_min = 375000; mmc->f_max = 24000000; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_4_BIT_DATA; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; spin_lock_init(&host->lock); diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index a03e862851d..a304b34c263 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -166,7 +166,7 @@ config MTD_CHAR config MTD_BLOCK tristate "Caching block device access to MTD devices" - depends on MTD + depends on MTD && BLOCK ---help--- Although most flash chips have an erase size too large to be useful as block devices, it is possible to use MTD devices which are based @@ -188,7 +188,7 @@ config MTD_BLOCK config MTD_BLOCK_RO tristate "Readonly block device access to MTD devices" - depends on MTD_BLOCK!=y && MTD + depends on MTD_BLOCK!=y && MTD && BLOCK help This allows you to mount read-only file systems (such as cramfs) from an MTD device, without the overhead (and danger) of the caching @@ -199,7 +199,7 @@ config MTD_BLOCK_RO config FTL tristate "FTL (Flash Translation Layer) support" - depends on MTD + depends on MTD && BLOCK ---help--- This provides support for the original Flash Translation Layer which is part of the PCMCIA specification. It uses a kind of pseudo- @@ -215,7 +215,7 @@ config FTL config NFTL tristate "NFTL (NAND Flash Translation Layer) support" - depends on MTD + depends on MTD && BLOCK ---help--- This provides support for the NAND Flash Translation Layer which is used on M-Systems' DiskOnChip devices. It uses a kind of pseudo- @@ -238,7 +238,7 @@ config NFTL_RW config INFTL tristate "INFTL (Inverse NAND Flash Translation Layer) support" - depends on MTD + depends on MTD && BLOCK ---help--- This provides support for the Inverse NAND Flash Translation Layer which is used on M-Systems' newer DiskOnChip devices. It @@ -255,7 +255,7 @@ config INFTL config RFD_FTL tristate "Resident Flash Disk (Flash Translation Layer) support" - depends on MTD + depends on MTD && BLOCK ---help--- This provides support for the flash translation layer known as the Resident Flash Disk (RFD), as used by the Embedded BIOS diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 16c02b5ccf7..440f6851da6 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -136,7 +136,7 @@ config MTDRAM_ABS_POS config MTD_BLOCK2MTD tristate "MTD using block device" - depends on MTD + depends on MTD && BLOCK help This driver allows a block device to appear as an MTD. It would generally be used in the following cases: diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 458d3c8ae1e..6baf5fe1423 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -46,7 +46,7 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, nsect = req->current_nr_sectors; buf = req->buffer; - if (!(req->flags & REQ_CMD)) + if (!blk_fs_request(req)) return 0; if (block + nsect > get_capacity(req->rq_disk)) diff --git a/drivers/net/8390.c b/drivers/net/8390.c index 5b6b05ed8f3..9d34056147a 100644 --- a/drivers/net/8390.c +++ b/drivers/net/8390.c @@ -299,7 +299,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) * Slow phase with lock held. */ - disable_irq_nosync_lockdep(dev->irq); + disable_irq_nosync_lockdep_irqsave(dev->irq, &flags); spin_lock(&ei_local->page_lock); @@ -338,7 +338,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); outb_p(ENISR_ALL, e8390_base + EN0_IMR); spin_unlock(&ei_local->page_lock); - enable_irq_lockdep(dev->irq); + enable_irq_lockdep_irqrestore(dev->irq, &flags); ei_local->stat.tx_errors++; return 1; } @@ -379,7 +379,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) outb_p(ENISR_ALL, e8390_base + EN0_IMR); spin_unlock(&ei_local->page_lock); - enable_irq_lockdep(dev->irq); + enable_irq_lockdep_irqrestore(dev->irq, &flags); dev_kfree_skb (skb); ei_local->stat.tx_bytes += send_length; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 63154774c25..ff8a8c0a26d 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -24,6 +24,9 @@ config NETDEVICES If unsure, say Y. +# All the following symbols are dependent on NETDEVICES - do not repeat +# that for each of the symbols. +if NETDEVICES config IFB tristate "Intermediate Functional Block support" @@ -2852,6 +2855,8 @@ config NETCONSOLE If you want to log kernel messages over the network, enable this. See <file:Documentation/networking/netconsole.txt> for details. +endif #NETDEVICES + config NETPOLL def_bool NETCONSOLE diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c index 7f7dd450226..b98592a8bac 100644 --- a/drivers/net/appletalk/ipddp.c +++ b/drivers/net/appletalk/ipddp.c @@ -145,9 +145,7 @@ static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev) /* Create the Extended DDP header */ ddp = (struct ddpehdr *)skb->data; - ddp->deh_len = skb->len; - ddp->deh_hops = 1; - ddp->deh_pad = 0; + ddp->deh_len_hops = htons(skb->len + (1<<10)); ddp->deh_sum = 0; /* @@ -170,7 +168,6 @@ static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev) ddp->deh_sport = 72; *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ - *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* fix up length field */ skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */ diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c index 95b28aa01f4..3ecf2cc53a7 100644 --- a/drivers/net/arm/at91_ether.c +++ b/drivers/net/arm/at91_ether.c @@ -947,7 +947,7 @@ static int __init at91ether_setup(unsigned long phy_type, unsigned short phy_add return -ENOMEM; dev->base_addr = AT91_VA_BASE_EMAC; - dev->irq = AT91_ID_EMAC; + dev->irq = AT91RM9200_ID_EMAC; SET_MODULE_OWNER(dev); /* Install the interrupt handler */ diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 7fcf015021e..6b4edb63c4c 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -56,8 +56,8 @@ #define DRV_MODULE_NAME "bnx2" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "1.4.44" -#define DRV_MODULE_RELDATE "August 10, 2006" +#define DRV_MODULE_VERSION "1.4.45" +#define DRV_MODULE_RELDATE "September 29, 2006" #define RUN_AT(x) (jiffies + (x)) @@ -5805,6 +5805,34 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) bp->cmd_ticks_int = bp->cmd_ticks; } + /* Disable MSI on 5706 if AMD 8132 bridge is found. + * + * MSI is defined to be 32-bit write. The 5706 does 64-bit MSI writes + * with byte enables disabled on the unused 32-bit word. This is legal + * but causes problems on the AMD 8132 which will eventually stop + * responding after a while. + * + * AMD believes this incompatibility is unique to the 5706, and + * prefers to locally disable MSI rather than globally disabling it + * using pci_msi_quirk. + */ + if (CHIP_NUM(bp) == CHIP_NUM_5706 && disable_msi == 0) { + struct pci_dev *amd_8132 = NULL; + + while ((amd_8132 = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_8132_BRIDGE, + amd_8132))) { + u8 rev; + + pci_read_config_byte(amd_8132, PCI_REVISION_ID, &rev); + if (rev >= 0x10 && rev <= 0x13) { + disable_msi = 1; + pci_dev_put(amd_8132); + break; + } + } + } + bp->autoneg = AUTONEG_SPEED | AUTONEG_FLOW_CTRL; bp->req_line_speed = 0; if (bp->phy_flags & PHY_SERDES_FLAG) { diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 0fb5f653d3c..c0bbddae4ec 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2252,7 +2252,7 @@ static u32 bond_glean_dev_ip(struct net_device *dev) { struct in_device *idev; struct in_ifaddr *ifa; - u32 addr = 0; + __be32 addr = 0; if (!dev) return 0; diff --git a/drivers/net/fec_8xx/fec_main.c b/drivers/net/fec_8xx/fec_main.c index 22ac2df1aeb..e17a1449ee1 100644 --- a/drivers/net/fec_8xx/fec_main.c +++ b/drivers/net/fec_8xx/fec_main.c @@ -30,6 +30,7 @@ #include <linux/mii.h> #include <linux/ethtool.h> #include <linux/bitops.h> +#include <linux/dma-mapping.h> #include <asm/8xx_immap.h> #include <asm/pgtable.h> @@ -37,7 +38,6 @@ #include <asm/irq.h> #include <asm/uaccess.h> #include <asm/commproc.h> -#include <asm/dma-mapping.h> #include "fec_8xx.h" diff --git a/drivers/net/fs_enet/fs_enet.h b/drivers/net/fs_enet/fs_enet.h index 95022c005f7..92590d8fc24 100644 --- a/drivers/net/fs_enet/fs_enet.h +++ b/drivers/net/fs_enet/fs_enet.h @@ -6,11 +6,10 @@ #include <linux/types.h> #include <linux/list.h> #include <linux/phy.h> +#include <linux/dma-mapping.h> #include <linux/fs_enet_pd.h> -#include <asm/dma-mapping.h> - #ifdef CONFIG_CPM1 #include <asm/commproc.h> diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig index e9e6d99a9ad..7c8ccc09b60 100644 --- a/drivers/net/irda/Kconfig +++ b/drivers/net/irda/Kconfig @@ -287,6 +287,7 @@ comment "FIR device drivers" config USB_IRDA tristate "IrDA USB dongles" depends on IRDA && USB + select FW_LOADER ---help--- Say Y here if you want to build support for the USB IrDA FIR Dongle device driver. To compile it as a module, choose M here: the module diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index cb62f2a9676..7185a4ee3c1 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c @@ -110,7 +110,7 @@ static nsc_chip_t chips[] = { { "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8, nsc_ircc_probe_338, nsc_ircc_init_338 }, /* Contributed by Steffen Pingel - IBM X40 */ - { "PC8738x", { 0x164e, 0x4e, 0x0 }, 0x20, 0xf4, 0xff, + { "PC8738x", { 0x164e, 0x4e, 0x2e }, 0x20, 0xf4, 0xff, nsc_ircc_probe_39x, nsc_ircc_init_39x }, /* Contributed by Jan Frey - IBM A30/A31 */ { "PC8739x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xea, 0xff, diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c index 2eff45bedc7..22358ff68c4 100644 --- a/drivers/net/irda/smsc-ircc2.c +++ b/drivers/net/irda/smsc-ircc2.c @@ -2354,6 +2354,26 @@ static int __init smsc_superio_lpc(unsigned short cfg_base) #define PCIID_VENDOR_INTEL 0x8086 #define PCIID_VENDOR_ALI 0x10b9 static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __initdata = { + /* + * Subsystems needing entries: + * 0x10b9:0x1533 0x103c:0x0850 HP nx9010 family + * 0x10b9:0x1533 0x0e11:0x005a Compaq nc4000 family + * 0x8086:0x24cc 0x0e11:0x002a HP nx9000 family + */ + { + /* Guessed entry */ + .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */ + .device = 0x24cc, + .subvendor = 0x103c, + .subdevice = 0x08bc, + .sir_io = 0x02f8, + .fir_io = 0x0130, + .fir_irq = 0x05, + .fir_dma = 0x03, + .cfg_base = 0x004e, + .preconfigure = preconfigure_through_82801, + .name = "HP nx5000 family", + }, { .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */ .device = 0x24cc, @@ -2366,7 +2386,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini .fir_dma = 0x03, .cfg_base = 0x004e, .preconfigure = preconfigure_through_82801, - .name = "HP nc8000", + .name = "HP nc8000 family", }, { .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */ @@ -2379,7 +2399,21 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini .fir_dma = 0x03, .cfg_base = 0x004e, .preconfigure = preconfigure_through_82801, - .name = "HP nc6000", + .name = "HP nc6000 family", + }, + { + .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */ + .device = 0x24cc, + .subvendor = 0x0e11, + .subdevice = 0x0860, + /* I assume these are the same for x1000 as for the others */ + .sir_io = 0x02e8, + .fir_io = 0x02f8, + .fir_irq = 0x07, + .fir_dma = 0x03, + .cfg_base = 0x002e, + .preconfigure = preconfigure_through_82801, + .name = "Compaq x1000 family", }, { /* Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge */ diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index d61b208b52a..12103c93f7e 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -149,8 +149,6 @@ enum StirFifoCtlMask { FIFOCTL_DIR = 0x10, FIFOCTL_CLR = 0x08, FIFOCTL_EMPTY = 0x04, - FIFOCTL_RXERR = 0x02, - FIFOCTL_TXERR = 0x01, }; enum StirDiagMask { @@ -615,19 +613,6 @@ static int fifo_txwait(struct stir_cb *stir, int space) pr_debug("fifo status 0x%lx count %lu\n", status, count); - /* error when receive/transmit fifo gets confused */ - if (status & FIFOCTL_RXERR) { - stir->stats.rx_fifo_errors++; - stir->stats.rx_errors++; - break; - } - - if (status & FIFOCTL_TXERR) { - stir->stats.tx_fifo_errors++; - stir->stats.tx_errors++; - break; - } - /* is fifo receiving already, or empty */ if (!(status & FIFOCTL_DIR) || (status & FIFOCTL_EMPTY)) diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c index 79b85f32750..d916e1257c4 100644 --- a/drivers/net/irda/via-ircc.c +++ b/drivers/net/irda/via-ircc.c @@ -1223,8 +1223,13 @@ static int upload_rxdata(struct via_ircc_cb *self, int iobase) IRDA_DEBUG(2, "%s(): len=%x\n", __FUNCTION__, len); + if ((len - 4) < 2) { + self->stats.rx_dropped++; + return FALSE; + } + skb = dev_alloc_skb(len + 1); - if ((skb == NULL) || ((len - 4) < 2)) { + if (skb == NULL) { self->stats.rx_dropped++; return FALSE; } diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index f429b19bf62..4178b4b1d2d 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -161,15 +161,13 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev) return(0); } +static struct net_device_stats loopback_stats; + static struct net_device_stats *get_stats(struct net_device *dev) { - struct net_device_stats *stats = dev->priv; + struct net_device_stats *stats = &loopback_stats; int i; - if (!stats) { - return NULL; - } - memset(stats, 0, sizeof(struct net_device_stats)); for_each_possible_cpu(i) { @@ -185,19 +183,28 @@ static struct net_device_stats *get_stats(struct net_device *dev) return stats; } -static u32 loopback_get_link(struct net_device *dev) +static u32 always_on(struct net_device *dev) { return 1; } static const struct ethtool_ops loopback_ethtool_ops = { - .get_link = loopback_get_link, + .get_link = always_on, .get_tso = ethtool_op_get_tso, .set_tso = ethtool_op_set_tso, + .get_tx_csum = always_on, + .get_sg = always_on, + .get_rx_csum = always_on, }; +/* + * The loopback device is special. There is only one instance and + * it is statically allocated. Don't do this for other devices. + */ struct net_device loopback_dev = { .name = "lo", + .get_stats = &get_stats, + .priv = &loopback_stats, .mtu = (16 * 1024) + 20 + 20 + 12, .hard_start_xmit = loopback_xmit, .hard_header = eth_header, @@ -221,16 +228,6 @@ struct net_device loopback_dev = { /* Setup and register the loopback device. */ int __init loopback_init(void) { - struct net_device_stats *stats; - - /* Can survive without statistics */ - stats = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL); - if (stats) { - memset(stats, 0, sizeof(struct net_device_stats)); - loopback_dev.priv = stats; - loopback_dev.get_stats = &get_stats; - } - return register_netdev(&loopback_dev); }; diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index 5666ed99814..0adee733b76 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c @@ -600,6 +600,7 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, po->chan.hdrlen = (sizeof(struct pppoe_hdr) + dev->hard_header_len); + po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr); po->chan.private = sk; po->chan.ops = &pppoe_chan_ops; diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index c660e33f43a..fedd1a37bc3 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -195,6 +195,7 @@ SMC_outw(u16 val, void __iomem *ioaddr, int reg) #define SMC_IRQ_FLAGS (( \ machine_is_omap_h2() \ || machine_is_omap_h3() \ + || machine_is_omap_h4() \ || (machine_is_omap_innovator() && !cpu_is_omap1510()) \ ) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING) diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index aaf45b907a7..c25ba273b74 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -68,8 +68,8 @@ #define DRV_MODULE_NAME "tg3" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "3.65" -#define DRV_MODULE_RELDATE "August 07, 2006" +#define DRV_MODULE_VERSION "3.66" +#define DRV_MODULE_RELDATE "September 23, 2006" #define TG3_DEF_MAC_MODE 0 #define TG3_DEF_RX_MODE 0 @@ -173,6 +173,7 @@ static struct pci_device_id tg3_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705F)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5720)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5721)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5722)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750M)}, @@ -187,6 +188,7 @@ static struct pci_device_id tg3_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5754M)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5755)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5755M)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5756)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5786)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5787)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5787M)}, @@ -197,6 +199,8 @@ static struct pci_device_id tg3_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5780)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5780S)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5781)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5906)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5906M)}, {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)}, {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX)}, {PCI_DEVICE(PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000)}, @@ -424,6 +428,16 @@ static void tg3_write32_tx_mbox(struct tg3 *tp, u32 off, u32 val) readl(mbox); } +static u32 tg3_read32_mbox_5906(struct tg3 *tp, u32 off) +{ + return (readl(tp->regs + off + GRCMBOX_BASE)); +} + +static void tg3_write32_mbox_5906(struct tg3 *tp, u32 off, u32 val) +{ + writel(val, tp->regs + off + GRCMBOX_BASE); +} + #define tw32_mailbox(reg, val) tp->write32_mbox(tp, reg, val) #define tw32_mailbox_f(reg, val) tw32_mailbox_flush(tp, (reg), (val)) #define tw32_rx_mbox(reg, val) tp->write32_rx_mbox(tp, reg, val) @@ -439,6 +453,10 @@ static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val) { unsigned long flags; + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) && + (off >= NIC_SRAM_STATS_BLK) && (off < NIC_SRAM_TX_BUFFER_DESC)) + return; + spin_lock_irqsave(&tp->indirect_lock, flags); if (tp->tg3_flags & TG3_FLAG_SRAM_USE_CONFIG) { pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off); @@ -460,6 +478,12 @@ static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val) { unsigned long flags; + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) && + (off >= NIC_SRAM_STATS_BLK) && (off < NIC_SRAM_TX_BUFFER_DESC)) { + *val = 0; + return; + } + spin_lock_irqsave(&tp->indirect_lock, flags); if (tp->tg3_flags & TG3_FLAG_SRAM_USE_CONFIG) { pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off); @@ -489,6 +513,9 @@ static inline void tg3_cond_int(struct tg3 *tp) if (!(tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) && (tp->hw_status->status & SD_STATUS_UPDATED)) tw32(GRC_LOCAL_CTRL, tp->grc_local_ctrl | GRC_LCLCTRL_SETINT); + else + tw32(HOSTCC_MODE, tp->coalesce_mode | + (HOSTCC_MODE_ENABLE | HOSTCC_MODE_NOW)); } static void tg3_enable_ints(struct tg3 *tp) @@ -654,6 +681,10 @@ static int tg3_writephy(struct tg3 *tp, int reg, u32 val) unsigned int loops; int ret; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906 && + (reg == MII_TG3_CTRL || reg == MII_TG3_AUX_CTRL)) + return 0; + if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { tw32_f(MAC_MI_MODE, (tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL)); @@ -1004,6 +1035,24 @@ out: phy_reg | MII_TG3_EXT_CTRL_FIFO_ELASTIC); } + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { + u32 phy_reg; + + /* adjust output voltage */ + tg3_writephy(tp, MII_TG3_EPHY_PTEST, 0x12); + + if (!tg3_readphy(tp, MII_TG3_EPHY_TEST, &phy_reg)) { + u32 phy_reg2; + + tg3_writephy(tp, MII_TG3_EPHY_TEST, + phy_reg | MII_TG3_EPHY_SHADOW_EN); + /* Enable auto-MDIX */ + if (!tg3_readphy(tp, 0x10, &phy_reg2)) + tg3_writephy(tp, 0x10, phy_reg2 | 0x4000); + tg3_writephy(tp, MII_TG3_EPHY_TEST, phy_reg); + } + } + tg3_phy_set_wirespeed(tp); return 0; } @@ -1117,6 +1166,15 @@ static void tg3_nvram_unlock(struct tg3 *); static void tg3_power_down_phy(struct tg3 *tp) { + if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) + return; + + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906) { + tg3_writephy(tp, MII_TG3_EXT_CTRL, + MII_TG3_EXT_CTRL_FORCE_LED_OFF); + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x01b2); + } + /* The PHY should not be powered down on some chips because * of bugs. */ @@ -1199,7 +1257,12 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state) tg3_setup_phy(tp, 0); } - if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { + u32 val; + + val = tr32(GRC_VCPU_EXT_CTRL); + tw32(GRC_VCPU_EXT_CTRL, val | GRC_VCPU_EXT_CTRL_DISABLE_WOL); + } else if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) { int i; u32 val; @@ -1223,7 +1286,10 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state) tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x5a); udelay(40); - mac_mode = MAC_MODE_PORT_MODE_MII; + if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) + mac_mode = MAC_MODE_PORT_MODE_GMII; + else + mac_mode = MAC_MODE_PORT_MODE_MII; if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 || !(tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB)) @@ -1301,15 +1367,8 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state) } if (!(tp->tg3_flags & TG3_FLAG_WOL_ENABLE) && - !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) { - /* Turn off the PHY */ - if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) { - tg3_writephy(tp, MII_TG3_EXT_CTRL, - MII_TG3_EXT_CTRL_FORCE_LED_OFF); - tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x01b2); - tg3_power_down_phy(tp); - } - } + !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) + tg3_power_down_phy(tp); tg3_frob_aux_power(tp); @@ -1467,6 +1526,13 @@ static void tg3_aux_stat_to_speed_duplex(struct tg3 *tp, u32 val, u16 *speed, u8 break; default: + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { + *speed = (val & MII_TG3_AUX_STAT_100) ? SPEED_100 : + SPEED_10; + *duplex = (val & MII_TG3_AUX_STAT_FULL) ? DUPLEX_FULL : + DUPLEX_HALF; + break; + } *speed = SPEED_INVALID; *duplex = DUPLEX_INVALID; break; @@ -1749,7 +1815,7 @@ static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset) if (tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT) tg3_writephy(tp, MII_TG3_IMASK, ~MII_TG3_INT_LINKCHG); - else + else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906) tg3_writephy(tp, MII_TG3_IMASK, ~0); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || @@ -2406,24 +2472,27 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status) expected_sg_dig_ctrl |= (1 << 12); if (sg_dig_ctrl != expected_sg_dig_ctrl) { + if ((tp->tg3_flags2 & TG3_FLG2_PARALLEL_DETECT) && + tp->serdes_counter && + ((mac_status & (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_RCVD_CFG)) == + MAC_STATUS_PCS_SYNCED)) { + tp->serdes_counter--; + current_link_up = 1; + goto out; + } +restart_autoneg: if (workaround) tw32_f(MAC_SERDES_CFG, serdes_cfg | 0xc011000); tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl | (1 << 30)); udelay(5); tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl); - tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED; + tp->serdes_counter = SERDES_AN_TIMEOUT_5704S; + tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT; } else if (mac_status & (MAC_STATUS_PCS_SYNCED | MAC_STATUS_SIGNAL_DET)) { - int i; - - /* Giver time to negotiate (~200ms) */ - for (i = 0; i < 40000; i++) { - sg_dig_status = tr32(SG_DIG_STATUS); - if (sg_dig_status & (0x3)) - break; - udelay(5); - } + sg_dig_status = tr32(SG_DIG_STATUS); mac_status = tr32(MAC_STATUS); if ((sg_dig_status & (1 << 1)) && @@ -2439,10 +2508,11 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status) tg3_setup_flow_control(tp, local_adv, remote_adv); current_link_up = 1; - tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED; + tp->serdes_counter = 0; + tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT; } else if (!(sg_dig_status & (1 << 1))) { - if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED) - tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED; + if (tp->serdes_counter) + tp->serdes_counter--; else { if (workaround) { u32 val = serdes_cfg; @@ -2466,9 +2536,17 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status) !(mac_status & MAC_STATUS_RCVD_CFG)) { tg3_setup_flow_control(tp, 0, 0); current_link_up = 1; - } + tp->tg3_flags2 |= + TG3_FLG2_PARALLEL_DETECT; + tp->serdes_counter = + SERDES_PARALLEL_DET_TIMEOUT; + } else + goto restart_autoneg; } } + } else { + tp->serdes_counter = SERDES_AN_TIMEOUT_5704S; + tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT; } out: @@ -2599,14 +2677,16 @@ static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset) MAC_STATUS_CFG_CHANGED)); udelay(5); if ((tr32(MAC_STATUS) & (MAC_STATUS_SYNC_CHANGED | - MAC_STATUS_CFG_CHANGED)) == 0) + MAC_STATUS_CFG_CHANGED | + MAC_STATUS_LNKSTATE_CHANGED)) == 0) break; } mac_status = tr32(MAC_STATUS); if ((mac_status & MAC_STATUS_PCS_SYNCED) == 0) { current_link_up = 0; - if (tp->link_config.autoneg == AUTONEG_ENABLE) { + if (tp->link_config.autoneg == AUTONEG_ENABLE && + tp->serdes_counter == 0) { tw32_f(MAC_MODE, (tp->mac_mode | MAC_MODE_SEND_CONFIGS)); udelay(1); @@ -2711,7 +2791,7 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset) tg3_writephy(tp, MII_BMCR, bmcr); tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); - tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED; + tp->serdes_counter = SERDES_AN_TIMEOUT_5714S; tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT; return err; @@ -2816,9 +2896,9 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset) static void tg3_serdes_parallel_detect(struct tg3 *tp) { - if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED) { + if (tp->serdes_counter) { /* Give autoneg time to complete. */ - tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED; + tp->serdes_counter--; return; } if (!netif_carrier_ok(tp->dev) && @@ -3535,8 +3615,7 @@ static irqreturn_t tg3_test_isr(int irq, void *dev_id, if ((sblk->status & SD_STATUS_UPDATED) || !(tr32(TG3PCI_PCISTATE) & PCISTATE_INT_NOT_ACTIVE)) { - tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, - 0x00000001); + tg3_disable_ints(tp); return IRQ_RETVAL(1); } return IRQ_RETVAL(0); @@ -4644,6 +4723,44 @@ static void tg3_write_sig_legacy(struct tg3 *tp, int kind) } } +static int tg3_poll_fw(struct tg3 *tp) +{ + int i; + u32 val; + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { + for (i = 0; i < 400; i++) { + if (tr32(VCPU_STATUS) & VCPU_STATUS_INIT_DONE) + return 0; + udelay(10); + } + return -ENODEV; + } + + /* Wait for firmware initialization to complete. */ + for (i = 0; i < 100000; i++) { + tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val); + if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1) + break; + udelay(10); + } + + /* Chip might not be fitted with firmware. Some Sun onboard + * parts are configured like that. So don't signal the timeout + * of the above loop as an error, but do report the lack of + * running firmware once. + */ + if (i >= 100000 && + !(tp->tg3_flags2 & TG3_FLG2_NO_FWARE_REPORTED)) { + tp->tg3_flags2 |= TG3_FLG2_NO_FWARE_REPORTED; + + printk(KERN_INFO PFX "%s: No firmware running.\n", + tp->dev->name); + } + + return 0; +} + static void tg3_stop_fw(struct tg3 *); /* tp->lock is held. */ @@ -4651,7 +4768,7 @@ static int tg3_chip_reset(struct tg3 *tp) { u32 val; void (*write_op)(struct tg3 *, u32, u32); - int i; + int err; tg3_nvram_lock(tp); @@ -4688,6 +4805,12 @@ static int tg3_chip_reset(struct tg3 *tp) } } + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { + tw32(VCPU_STATUS, tr32(VCPU_STATUS) | VCPU_STATUS_DRV_RESET); + tw32(GRC_VCPU_EXT_CTRL, + tr32(GRC_VCPU_EXT_CTRL) & ~GRC_VCPU_EXT_CTRL_HALT_CPU); + } + if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) val |= GRC_MISC_CFG_KEEP_GPHY_POWER; tw32(GRC_MISC_CFG, val); @@ -4811,26 +4934,9 @@ static int tg3_chip_reset(struct tg3 *tp) tw32_f(MAC_MODE, 0); udelay(40); - /* Wait for firmware initialization to complete. */ - for (i = 0; i < 100000; i++) { - tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val); - if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1) - break; - udelay(10); - } - - /* Chip might not be fitted with firmare. Some Sun onboard - * parts are configured like that. So don't signal the timeout - * of the above loop as an error, but do report the lack of - * running firmware once. - */ - if (i >= 100000 && - !(tp->tg3_flags2 & TG3_FLG2_NO_FWARE_REPORTED)) { - tp->tg3_flags2 |= TG3_FLG2_NO_FWARE_REPORTED; - - printk(KERN_INFO PFX "%s: No firmware running.\n", - tp->dev->name); - } + err = tg3_poll_fw(tp); + if (err) + return err; if ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) && tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) { @@ -5036,6 +5142,12 @@ static int tg3_halt_cpu(struct tg3 *tp, u32 offset) BUG_ON(offset == TX_CPU_BASE && (tp->tg3_flags2 & TG3_FLG2_5705_PLUS)); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { + u32 val = tr32(GRC_VCPU_EXT_CTRL); + + tw32(GRC_VCPU_EXT_CTRL, val | GRC_VCPU_EXT_CTRL_HALT_CPU); + return 0; + } if (offset == RX_CPU_BASE) { for (i = 0; i < 10000; i++) { tw32(offset + CPU_STATE, 0xffffffff); @@ -6040,6 +6152,13 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) val = 1; else if (val > tp->rx_std_max_post) val = tp->rx_std_max_post; + else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { + if (tp->pci_chip_rev_id == CHIPREV_ID_5906_A1) + tw32(ISO_PKT_TX, (tr32(ISO_PKT_TX) & ~0x3) | 0x2); + + if (val > (TG3_RX_INTERNAL_RING_SZ_5906 / 2)) + val = TG3_RX_INTERNAL_RING_SZ_5906 / 2; + } tw32(RCVBDI_STD_THRESH, val); @@ -6460,7 +6579,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) if (err) return err; - if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) { + if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906) { u32 tmp; /* Clear CRC stats. */ @@ -6660,12 +6780,14 @@ static void tg3_timer(unsigned long __opaque) need_setup = 1; } if (need_setup) { - tw32_f(MAC_MODE, - (tp->mac_mode & - ~MAC_MODE_PORT_MODE_MASK)); - udelay(40); - tw32_f(MAC_MODE, tp->mac_mode); - udelay(40); + if (!tp->serdes_counter) { + tw32_f(MAC_MODE, + (tp->mac_mode & + ~MAC_MODE_PORT_MODE_MASK)); + udelay(40); + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + } tg3_setup_phy(tp, 0); } } else if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) @@ -6674,13 +6796,29 @@ static void tg3_timer(unsigned long __opaque) tp->timer_counter = tp->timer_multiplier; } - /* Heartbeat is only sent once every 2 seconds. */ + /* Heartbeat is only sent once every 2 seconds. + * + * The heartbeat is to tell the ASF firmware that the host + * driver is still alive. In the event that the OS crashes, + * ASF needs to reset the hardware to free up the FIFO space + * that may be filled with rx packets destined for the host. + * If the FIFO is full, ASF will no longer function properly. + * + * Unintended resets have been reported on real time kernels + * where the timer doesn't run on time. Netpoll will also have + * same problem. + * + * The new FWCMD_NICDRV_ALIVE3 command tells the ASF firmware + * to check the ring condition when the heartbeat is expiring + * before doing the reset. This will prevent most unintended + * resets. + */ if (!--tp->asf_counter) { if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) { u32 val; tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX, - FWCMD_NICDRV_ALIVE2); + FWCMD_NICDRV_ALIVE3); tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 4); /* 5 seconds timeout */ tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX, 5); @@ -6721,8 +6859,7 @@ static int tg3_request_irq(struct tg3 *tp) static int tg3_test_interrupt(struct tg3 *tp) { struct net_device *dev = tp->dev; - int err, i; - u32 int_mbox = 0; + int err, i, intr_ok = 0; if (!netif_running(dev)) return -ENODEV; @@ -6743,10 +6880,18 @@ static int tg3_test_interrupt(struct tg3 *tp) HOSTCC_MODE_NOW); for (i = 0; i < 5; i++) { + u32 int_mbox, misc_host_ctrl; + int_mbox = tr32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW); - if (int_mbox != 0) + misc_host_ctrl = tr32(TG3PCI_MISC_HOST_CTRL); + + if ((int_mbox != 0) || + (misc_host_ctrl & MISC_HOST_CTRL_MASK_PCI_INT)) { + intr_ok = 1; break; + } + msleep(10); } @@ -6759,7 +6904,7 @@ static int tg3_test_interrupt(struct tg3 *tp) if (err) return err; - if (int_mbox != 0) + if (intr_ok) return 0; return -EIO; @@ -6936,9 +7081,10 @@ static int tg3_open(struct net_device *dev) if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) { if (tp->tg3_flags2 & TG3_FLG2_1SHOT_MSI) { - u32 val = tr32(0x7c04); + u32 val = tr32(PCIE_TRANSACTION_CFG); - tw32(0x7c04, val | (1 << 29)); + tw32(PCIE_TRANSACTION_CFG, + val | PCIE_TRANS_CFG_1SHOT_MSI); } } } @@ -7857,7 +8003,7 @@ static int tg3_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) if (wol->wolopts & ~WAKE_MAGIC) return -EINVAL; if ((wol->wolopts & WAKE_MAGIC) && - tp->tg3_flags2 & TG3_FLG2_PHY_SERDES && + tp->tg3_flags2 & TG3_FLG2_ANY_SERDES && !(tp->tg3_flags & TG3_FLAG_SERDES_WOL_CAP)) return -EINVAL; @@ -7893,7 +8039,8 @@ static int tg3_set_tso(struct net_device *dev, u32 value) return -EINVAL; return 0; } - if (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) { + if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) && + (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)) { if (value) dev->features |= NETIF_F_TSO6; else @@ -8147,6 +8294,8 @@ static void tg3_get_ethtool_stats (struct net_device *dev, #define NVRAM_TEST_SIZE 0x100 #define NVRAM_SELFBOOT_FORMAT1_SIZE 0x14 +#define NVRAM_SELFBOOT_HW_SIZE 0x20 +#define NVRAM_SELFBOOT_DATA_SIZE 0x1c static int tg3_test_nvram(struct tg3 *tp) { @@ -8158,12 +8307,14 @@ static int tg3_test_nvram(struct tg3 *tp) if (magic == TG3_EEPROM_MAGIC) size = NVRAM_TEST_SIZE; - else if ((magic & 0xff000000) == 0xa5000000) { + else if ((magic & TG3_EEPROM_MAGIC_FW_MSK) == TG3_EEPROM_MAGIC_FW) { if ((magic & 0xe00000) == 0x200000) size = NVRAM_SELFBOOT_FORMAT1_SIZE; else return 0; - } else + } else if ((magic & TG3_EEPROM_MAGIC_HW_MSK) == TG3_EEPROM_MAGIC_HW) + size = NVRAM_SELFBOOT_HW_SIZE; + else return -EIO; buf = kmalloc(size, GFP_KERNEL); @@ -8182,7 +8333,8 @@ static int tg3_test_nvram(struct tg3 *tp) goto out; /* Selfboot format */ - if (cpu_to_be32(buf[0]) != TG3_EEPROM_MAGIC) { + if ((cpu_to_be32(buf[0]) & TG3_EEPROM_MAGIC_FW_MSK) == + TG3_EEPROM_MAGIC_FW) { u8 *buf8 = (u8 *) buf, csum8 = 0; for (i = 0; i < size; i++) @@ -8197,6 +8349,51 @@ static int tg3_test_nvram(struct tg3 *tp) goto out; } + if ((cpu_to_be32(buf[0]) & TG3_EEPROM_MAGIC_HW_MSK) == + TG3_EEPROM_MAGIC_HW) { + u8 data[NVRAM_SELFBOOT_DATA_SIZE]; + u8 parity[NVRAM_SELFBOOT_DATA_SIZE]; + u8 *buf8 = (u8 *) buf; + int j, k; + + /* Separate the parity bits and the data bytes. */ + for (i = 0, j = 0, k = 0; i < NVRAM_SELFBOOT_HW_SIZE; i++) { + if ((i == 0) || (i == 8)) { + int l; + u8 msk; + + for (l = 0, msk = 0x80; l < 7; l++, msk >>= 1) + parity[k++] = buf8[i] & msk; + i++; + } + else if (i == 16) { + int l; + u8 msk; + + for (l = 0, msk = 0x20; l < 6; l++, msk >>= 1) + parity[k++] = buf8[i] & msk; + i++; + + for (l = 0, msk = 0x80; l < 8; l++, msk >>= 1) + parity[k++] = buf8[i] & msk; + i++; + } + data[j++] = buf8[i]; + } + + err = -EIO; + for (i = 0; i < NVRAM_SELFBOOT_DATA_SIZE; i++) { + u8 hw8 = hweight8(data[i]); + + if ((hw8 & 0x1) && parity[i]) + goto out; + else if (!(hw8 & 0x1) && !parity[i]) + goto out; + } + err = 0; + goto out; + } + /* Bootstrap checksum at offset 0x10 */ csum = calc_crc((unsigned char *) buf, 0x10); if(csum != cpu_to_le32(buf[0x10/4])) @@ -8243,7 +8440,7 @@ static int tg3_test_link(struct tg3 *tp) /* Only test the commonly used registers */ static int tg3_test_registers(struct tg3 *tp) { - int i, is_5705; + int i, is_5705, is_5750; u32 offset, read_mask, write_mask, val, save_val, read_val; static struct { u16 offset; @@ -8251,6 +8448,7 @@ static int tg3_test_registers(struct tg3 *tp) #define TG3_FL_5705 0x1 #define TG3_FL_NOT_5705 0x2 #define TG3_FL_NOT_5788 0x4 +#define TG3_FL_NOT_5750 0x8 u32 read_mask; u32 write_mask; } reg_tbl[] = { @@ -8361,9 +8559,9 @@ static int tg3_test_registers(struct tg3 *tp) 0xffffffff, 0x00000000 }, /* Buffer Manager Control Registers. */ - { BUFMGR_MB_POOL_ADDR, 0x0000, + { BUFMGR_MB_POOL_ADDR, TG3_FL_NOT_5750, 0x00000000, 0x007fff80 }, - { BUFMGR_MB_POOL_SIZE, 0x0000, + { BUFMGR_MB_POOL_SIZE, TG3_FL_NOT_5750, 0x00000000, 0x007fffff }, { BUFMGR_MB_RDMA_LOW_WATER, 0x0000, 0x00000000, 0x0000003f }, @@ -8389,10 +8587,12 @@ static int tg3_test_registers(struct tg3 *tp) { 0xffff, 0x0000, 0x00000000, 0x00000000 }, }; - if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) + is_5705 = is_5750 = 0; + if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) { is_5705 = 1; - else - is_5705 = 0; + if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) + is_5750 = 1; + } for (i = 0; reg_tbl[i].offset != 0xffff; i++) { if (is_5705 && (reg_tbl[i].flags & TG3_FL_NOT_5705)) @@ -8405,6 +8605,9 @@ static int tg3_test_registers(struct tg3 *tp) (reg_tbl[i].flags & TG3_FL_NOT_5788)) continue; + if (is_5750 && (reg_tbl[i].flags & TG3_FL_NOT_5750)) + continue; + offset = (u32) reg_tbl[i].offset; read_mask = reg_tbl[i].read_mask; write_mask = reg_tbl[i].write_mask; @@ -8496,6 +8699,13 @@ static int tg3_test_memory(struct tg3 *tp) { 0x00008000, 0x02000}, { 0x00010000, 0x0c000}, { 0xffffffff, 0x00000} + }, mem_tbl_5906[] = { + { 0x00000200, 0x00008}, + { 0x00004000, 0x00400}, + { 0x00006000, 0x00400}, + { 0x00008000, 0x01000}, + { 0x00010000, 0x01000}, + { 0xffffffff, 0x00000} }; struct mem_entry *mem_tbl; int err = 0; @@ -8505,6 +8715,8 @@ static int tg3_test_memory(struct tg3 *tp) if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) mem_tbl = mem_tbl_5755; + else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) + mem_tbl = mem_tbl_5906; else mem_tbl = mem_tbl_5705; } else @@ -8541,13 +8753,41 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode) return 0; mac_mode = (tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK) | - MAC_MODE_PORT_INT_LPBACK | MAC_MODE_LINK_POLARITY | - MAC_MODE_PORT_MODE_GMII; + MAC_MODE_PORT_INT_LPBACK | MAC_MODE_LINK_POLARITY; + if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) + mac_mode |= MAC_MODE_PORT_MODE_MII; + else + mac_mode |= MAC_MODE_PORT_MODE_GMII; tw32(MAC_MODE, mac_mode); } else if (loopback_mode == TG3_PHY_LOOPBACK) { - tg3_writephy(tp, MII_BMCR, BMCR_LOOPBACK | BMCR_FULLDPLX | - BMCR_SPEED1000); + u32 val; + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { + u32 phytest; + + if (!tg3_readphy(tp, MII_TG3_EPHY_TEST, &phytest)) { + u32 phy; + + tg3_writephy(tp, MII_TG3_EPHY_TEST, + phytest | MII_TG3_EPHY_SHADOW_EN); + if (!tg3_readphy(tp, 0x1b, &phy)) + tg3_writephy(tp, 0x1b, phy & ~0x20); + if (!tg3_readphy(tp, 0x10, &phy)) + tg3_writephy(tp, 0x10, phy & ~0x4000); + tg3_writephy(tp, MII_TG3_EPHY_TEST, phytest); + } + } + val = BMCR_LOOPBACK | BMCR_FULLDPLX; + if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) + val |= BMCR_SPEED100; + else + val |= BMCR_SPEED1000; + + tg3_writephy(tp, MII_BMCR, val); udelay(40); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) + tg3_writephy(tp, MII_TG3_EPHY_PTEST, 0x1800); + /* reset to prevent losing 1st rx packet intermittently */ if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) { tw32_f(MAC_RX_MODE, RX_MODE_RESET); @@ -8555,7 +8795,11 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode) tw32_f(MAC_RX_MODE, tp->rx_mode); } mac_mode = (tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK) | - MAC_MODE_LINK_POLARITY | MAC_MODE_PORT_MODE_GMII; + MAC_MODE_LINK_POLARITY; + if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) + mac_mode |= MAC_MODE_PORT_MODE_MII; + else + mac_mode |= MAC_MODE_PORT_MODE_GMII; if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { mac_mode &= ~MAC_MODE_LINK_POLARITY; tg3_writephy(tp, MII_TG3_EXT_CTRL, @@ -8604,7 +8848,8 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode) udelay(10); - for (i = 0; i < 10; i++) { + /* 250 usec to allow enough time on some 10/100 Mbps devices. */ + for (i = 0; i < 25; i++) { tw32_f(HOSTCC_MODE, tp->coalesce_mode | HOSTCC_MODE_ENABLE | HOSTCC_MODE_NOW); @@ -8956,7 +9201,9 @@ static void __devinit tg3_get_eeprom_size(struct tg3 *tp) if (tg3_nvram_read_swab(tp, 0, &magic) != 0) return; - if ((magic != TG3_EEPROM_MAGIC) && ((magic & 0xff000000) != 0xa5000000)) + if ((magic != TG3_EEPROM_MAGIC) && + ((magic & TG3_EEPROM_MAGIC_FW_MSK) != TG3_EEPROM_MAGIC_FW) && + ((magic & TG3_EEPROM_MAGIC_HW_MSK) != TG3_EEPROM_MAGIC_HW)) return; /* @@ -9194,6 +9441,13 @@ static void __devinit tg3_get_5787_nvram_info(struct tg3 *tp) } } +static void __devinit tg3_get_5906_nvram_info(struct tg3 *tp) +{ + tp->nvram_jedecnum = JEDEC_ATMEL; + tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; + tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE; +} + /* Chips other than 5700/5701 use the NVRAM for fetching info. */ static void __devinit tg3_nvram_init(struct tg3 *tp) { @@ -9230,6 +9484,8 @@ static void __devinit tg3_nvram_init(struct tg3 *tp) tg3_get_5755_nvram_info(tp); else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) tg3_get_5787_nvram_info(tp); + else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) + tg3_get_5906_nvram_info(tp); else tg3_get_nvram_info(tp); @@ -9703,6 +9959,12 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp) /* Assume an onboard device by default. */ tp->tg3_flags |= TG3_FLAG_EEPROM_WRITE_PROT; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { + if (!(tr32(PCIE_TRANSACTION_CFG) & PCIE_TRANS_CFG_LOM)) + tp->tg3_flags &= ~TG3_FLAG_EEPROM_WRITE_PROT; + return; + } + tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val); if (val == NIC_SRAM_DATA_SIG_MAGIC) { u32 nic_cfg, led_cfg; @@ -10034,7 +10296,10 @@ static void __devinit tg3_read_partno(struct tg3 *tp) } out_not_found: - strcpy(tp->board_part_number, "none"); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) + strcpy(tp->board_part_number, "BCM95906"); + else + strcpy(tp->board_part_number, "none"); } static void __devinit tg3_read_fw_ver(struct tg3 *tp) @@ -10236,6 +10501,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906 || (tp->tg3_flags2 & TG3_FLG2_5780_CLASS)) tp->tg3_flags2 |= TG3_FLG2_5750_PLUS; @@ -10245,7 +10511,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) { if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) { + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2; tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI; } else { @@ -10262,7 +10529,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5752 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5755 && - GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787) + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906) tp->tg3_flags2 |= TG3_FLG2_JUMBO_CAPABLE; if (pci_find_capability(tp->pdev, PCI_CAP_ID_EXP) != 0) @@ -10392,6 +10660,12 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) pci_cmd &= ~PCI_COMMAND_MEMORY; pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd); } + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { + tp->read32_mbox = tg3_read32_mbox_5906; + tp->write32_mbox = tg3_write32_mbox_5906; + tp->write32_tx_mbox = tg3_write32_mbox_5906; + tp->write32_rx_mbox = tg3_write32_mbox_5906; + } if (tp->write32 == tg3_write_indirect_reg32 || ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) && @@ -10463,6 +10737,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) && (tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) && (tp->pci_chip_rev_id != CHIPREV_ID_5705_A1)) || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) || (tp->tg3_flags2 & TG3_FLG2_ANY_SERDES)) tp->tg3_flags2 |= TG3_FLG2_NO_ETH_WIRE_SPEED; @@ -10476,7 +10751,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) tp->tg3_flags2 |= TG3_FLG2_PHY_JITTER_BUG; - else + else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906) tp->tg3_flags2 |= TG3_FLG2_PHY_BER_BUG; } @@ -10566,7 +10841,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) tp->pdev->device == PCI_DEVICE_ID_TIGON3_5705F)) || (tp->pdev->vendor == PCI_VENDOR_ID_BROADCOM && (tp->pdev->device == PCI_DEVICE_ID_TIGON3_5751F || - tp->pdev->device == PCI_DEVICE_ID_TIGON3_5753F))) + tp->pdev->device == PCI_DEVICE_ID_TIGON3_5753F)) || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) tp->tg3_flags |= TG3_FLAG_10_100_ONLY; err = tg3_phy_probe(tp); @@ -10617,7 +10893,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) * straddle the 4GB address boundary in some cases. */ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) tp->dev->hard_start_xmit = tg3_start_xmit; else tp->dev->hard_start_xmit = tg3_start_xmit_dma_bug; @@ -10698,6 +10975,8 @@ static int __devinit tg3_get_device_address(struct tg3 *tp) else tg3_nvram_unlock(tp); } + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) + mac_offset = 0x10; /* First try to get it from MAC address mailbox. */ tg3_read_mem(tp, NIC_SRAM_MAC_ADDR_HIGH_MBOX, &hi); @@ -11181,6 +11460,12 @@ static void __devinit tg3_init_bufmgr_config(struct tg3 *tp) DEFAULT_MB_MACRX_LOW_WATER_5705; tp->bufmgr_config.mbuf_high_water = DEFAULT_MB_HIGH_WATER_5705; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { + tp->bufmgr_config.mbuf_mac_rx_low_water = + DEFAULT_MB_MACRX_LOW_WATER_5906; + tp->bufmgr_config.mbuf_high_water = + DEFAULT_MB_HIGH_WATER_5906; + } tp->bufmgr_config.mbuf_read_dma_low_water_jumbo = DEFAULT_MB_RDMA_LOW_WATER_JUMBO_5780; @@ -11224,6 +11509,8 @@ static char * __devinit tg3_phy_string(struct tg3 *tp) case PHY_ID_BCM5780: return "5780"; case PHY_ID_BCM5755: return "5755"; case PHY_ID_BCM5787: return "5787"; + case PHY_ID_BCM5756: return "5722/5756"; + case PHY_ID_BCM5906: return "5906"; case PHY_ID_BCM8002: return "8002/serdes"; case 0: return "serdes"; default: return "unknown"; @@ -11526,7 +11813,8 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, */ if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) { dev->features |= NETIF_F_TSO; - if (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) + if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) && + (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)) dev->features |= NETIF_F_TSO6; } diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 3ecf356cfb0..92f53000bce 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -24,6 +24,8 @@ #define RX_COPY_THRESHOLD 256 +#define TG3_RX_INTERNAL_RING_SZ_5906 32 + #define RX_STD_MAX_SIZE 1536 #define RX_STD_MAX_SIZE_5705 512 #define RX_JUMBO_MAX_SIZE 0xdeadbeef /* XXX */ @@ -129,6 +131,7 @@ #define CHIPREV_ID_5752_A0_HW 0x5000 #define CHIPREV_ID_5752_A0 0x6000 #define CHIPREV_ID_5752_A1 0x6001 +#define CHIPREV_ID_5906_A1 0xc001 #define GET_ASIC_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 12) #define ASIC_REV_5700 0x07 #define ASIC_REV_5701 0x00 @@ -141,6 +144,7 @@ #define ASIC_REV_5714 0x09 #define ASIC_REV_5755 0x0a #define ASIC_REV_5787 0x0b +#define ASIC_REV_5906 0x0c #define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8) #define CHIPREV_5700_AX 0x70 #define CHIPREV_5700_BX 0x71 @@ -646,7 +650,8 @@ #define SNDDATAI_SCTRL_FORCE_ZERO 0x00000010 #define SNDDATAI_STATSENAB 0x00000c0c #define SNDDATAI_STATSINCMASK 0x00000c10 -/* 0xc14 --> 0xc80 unused */ +#define ISO_PKT_TX 0x00000c20 +/* 0xc24 --> 0xc80 unused */ #define SNDDATAI_COS_CNT_0 0x00000c80 #define SNDDATAI_COS_CNT_1 0x00000c84 #define SNDDATAI_COS_CNT_2 0x00000c88 @@ -997,11 +1002,13 @@ #define BUFMGR_MB_MACRX_LOW_WATER 0x00004414 #define DEFAULT_MB_MACRX_LOW_WATER 0x00000020 #define DEFAULT_MB_MACRX_LOW_WATER_5705 0x00000010 +#define DEFAULT_MB_MACRX_LOW_WATER_5906 0x00000004 #define DEFAULT_MB_MACRX_LOW_WATER_JUMBO 0x00000098 #define DEFAULT_MB_MACRX_LOW_WATER_JUMBO_5780 0x0000004b #define BUFMGR_MB_HIGH_WATER 0x00004418 #define DEFAULT_MB_HIGH_WATER 0x00000060 #define DEFAULT_MB_HIGH_WATER_5705 0x00000060 +#define DEFAULT_MB_HIGH_WATER_5906 0x00000010 #define DEFAULT_MB_HIGH_WATER_JUMBO 0x0000017c #define DEFAULT_MB_HIGH_WATER_JUMBO_5780 0x00000096 #define BUFMGR_RX_MB_ALLOC_REQ 0x0000441c @@ -1138,7 +1145,12 @@ #define TX_CPU_STATE 0x00005404 #define TX_CPU_PGMCTR 0x0000541c +#define VCPU_STATUS 0x00005100 +#define VCPU_STATUS_INIT_DONE 0x04000000 +#define VCPU_STATUS_DRV_RESET 0x08000000 + /* Mailboxes */ +#define GRCMBOX_BASE 0x00005600 #define GRCMBOX_INTERRUPT_0 0x00005800 /* 64-bit */ #define GRCMBOX_INTERRUPT_1 0x00005808 /* 64-bit */ #define GRCMBOX_INTERRUPT_2 0x00005810 /* 64-bit */ @@ -1398,7 +1410,10 @@ #define GRC_EEPROM_CTRL 0x00006840 #define GRC_MDI_CTRL 0x00006844 #define GRC_SEEPROM_DELAY 0x00006848 -/* 0x684c --> 0x6c00 unused */ +/* 0x684c --> 0x6890 unused */ +#define GRC_VCPU_EXT_CTRL 0x00006890 +#define GRC_VCPU_EXT_CTRL_HALT_CPU 0x00400000 +#define GRC_VCPU_EXT_CTRL_DISABLE_WOL 0x20000000 #define GRC_FASTBOOT_PC 0x00006894 /* 5752, 5755, 5787 */ /* 0x6c00 --> 0x7000 unused */ @@ -1485,9 +1500,17 @@ #define NVRAM_WRITE1 0x00007028 /* 0x702c --> 0x7400 unused */ -/* 0x7400 --> 0x8000 unused */ +/* 0x7400 --> 0x7c00 unused */ +#define PCIE_TRANSACTION_CFG 0x00007c04 +#define PCIE_TRANS_CFG_1SHOT_MSI 0x20000000 +#define PCIE_TRANS_CFG_LOM 0x00000020 + #define TG3_EEPROM_MAGIC 0x669955aa +#define TG3_EEPROM_MAGIC_FW 0xa5000000 +#define TG3_EEPROM_MAGIC_FW_MSK 0xff000000 +#define TG3_EEPROM_MAGIC_HW 0xabcd +#define TG3_EEPROM_MAGIC_HW_MSK 0xffff /* 32K Window into NIC internal memory */ #define NIC_SRAM_WIN_BASE 0x00008000 @@ -1537,6 +1560,7 @@ #define FWCMD_NICDRV_FIX_DMAR 0x00000005 #define FWCMD_NICDRV_FIX_DMAW 0x00000006 #define FWCMD_NICDRV_ALIVE2 0x0000000d +#define FWCMD_NICDRV_ALIVE3 0x0000000e #define NIC_SRAM_FW_CMD_LEN_MBOX 0x00000b7c #define NIC_SRAM_FW_CMD_DATA_MBOX 0x00000b80 #define NIC_SRAM_FW_ASF_STATUS_MBOX 0x00000c00 @@ -1604,6 +1628,7 @@ #define MII_TG3_DSP_RW_PORT 0x15 /* DSP coefficient read/write port */ #define MII_TG3_DSP_ADDRESS 0x17 /* DSP address register */ +#define MII_TG3_EPHY_PTEST 0x17 /* 5906 PHY register */ #define MII_TG3_AUX_CTRL 0x18 /* auxilliary control register */ @@ -1617,6 +1642,8 @@ #define MII_TG3_AUX_STAT_100FULL 0x0500 #define MII_TG3_AUX_STAT_1000HALF 0x0600 #define MII_TG3_AUX_STAT_1000FULL 0x0700 +#define MII_TG3_AUX_STAT_100 0x0008 +#define MII_TG3_AUX_STAT_FULL 0x0001 #define MII_TG3_ISTAT 0x1a /* IRQ status register */ #define MII_TG3_IMASK 0x1b /* IRQ mask register */ @@ -1627,6 +1654,9 @@ #define MII_TG3_INT_DUPLEXCHG 0x0008 #define MII_TG3_INT_ANEG_PAGE_RX 0x0400 +#define MII_TG3_EPHY_TEST 0x1f /* 5906 PHY register */ +#define MII_TG3_EPHY_SHADOW_EN 0x80 + /* There are two ways to manage the TX descriptors on the tigon3. * Either the descriptors are in host DMA'able memory, or they * exist only in the cards on-chip SRAM. All 16 send bds are under @@ -2203,7 +2233,6 @@ struct tg3 { #define TG3_FLG2_PCI_EXPRESS 0x00000200 #define TG3_FLG2_ASF_NEW_HANDSHAKE 0x00000400 #define TG3_FLG2_HW_AUTONEG 0x00000800 -#define TG3_FLG2_PHY_JUST_INITTED 0x00001000 #define TG3_FLG2_PHY_SERDES 0x00002000 #define TG3_FLG2_CAPACITIVE_COUPLING 0x00004000 #define TG3_FLG2_FLASH 0x00008000 @@ -2236,6 +2265,12 @@ struct tg3 { u16 asf_counter; u16 asf_multiplier; + /* 1 second counter for transient serdes link events */ + u32 serdes_counter; +#define SERDES_AN_TIMEOUT_5704S 2 +#define SERDES_PARALLEL_DET_TIMEOUT 1 +#define SERDES_AN_TIMEOUT_5714S 1 + struct tg3_link_config link_config; struct tg3_bufmgr_config bufmgr_config; @@ -2276,6 +2311,8 @@ struct tg3 { #define PHY_ID_BCM5780 0x60008350 #define PHY_ID_BCM5755 0xbc050cc0 #define PHY_ID_BCM5787 0xbc050ce0 +#define PHY_ID_BCM5756 0xbc050ed0 +#define PHY_ID_BCM5906 0xdc00ac40 #define PHY_ID_BCM8002 0x60010140 #define PHY_ID_INVALID 0xffffffff #define PHY_ID_REV_MASK 0x0000000f @@ -2302,7 +2339,8 @@ struct tg3 { (X) == PHY_ID_BCM5705 || (X) == PHY_ID_BCM5750 || \ (X) == PHY_ID_BCM5752 || (X) == PHY_ID_BCM5714 || \ (X) == PHY_ID_BCM5780 || (X) == PHY_ID_BCM5787 || \ - (X) == PHY_ID_BCM5755 || (X) == PHY_ID_BCM8002) + (X) == PHY_ID_BCM5755 || (X) == PHY_ID_BCM5756 || \ + (X) == PHY_ID_BCM5906 || (X) == PHY_ID_BCM8002) struct tg3_hw_stats *hw_stats; dma_addr_t stats_mapping; diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c index 7ec2b2f9b7e..b0bc5ddcf1b 100644 --- a/drivers/net/wan/hdlc_cisco.c +++ b/drivers/net/wan/hdlc_cisco.c @@ -161,7 +161,7 @@ static int cisco_rx(struct sk_buff *skb) struct hdlc_header *data = (struct hdlc_header*)skb->data; struct cisco_packet *cisco_data; struct in_device *in_dev; - u32 addr, mask; + __be32 addr, mask; if (skb->len < sizeof(struct hdlc_header)) goto rx_error; diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c index c13b459a013..218f7b574ab 100644 --- a/drivers/net/wan/syncppp.c +++ b/drivers/net/wan/syncppp.c @@ -469,7 +469,7 @@ static void sppp_lcp_input (struct sppp *sp, struct sk_buff *skb) struct net_device *dev = sp->pp_if; int len = skb->len; u8 *p, opt[6]; - u32 rmagic; + u32 rmagic = 0; if (!pskb_may_pull(skb, sizeof(struct lcp_header))) { if (sp->pp_flags & PP_DEBUG) @@ -763,7 +763,7 @@ static void sppp_cisco_input (struct sppp *sp, struct sk_buff *skb) { struct in_device *in_dev; struct in_ifaddr *ifa; - u32 addr = 0, mask = ~0; /* FIXME: is the mask correct? */ + __be32 addr = 0, mask = ~0; /* FIXME: is the mask correct? */ #ifdef CONFIG_INET rcu_read_lock(); if ((in_dev = __in_dev_get_rcu(dev)) != NULL) diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c index ccaf28e8db0..337c692f6fd 100644 --- a/drivers/net/wireless/strip.c +++ b/drivers/net/wireless/strip.c @@ -1342,7 +1342,7 @@ static unsigned char *strip_make_packet(unsigned char *buffer, * 'broadcast hub' radio (First byte of address being 0xFF means broadcast) */ if (haddr.c[0] == 0xFF) { - u32 brd = 0; + __be32 brd = 0; struct in_device *in_dev; rcu_read_lock(); @@ -1406,7 +1406,7 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb) int doreset = (long) jiffies - strip_info->watchdog_doreset >= 0; int doprobe = (long) jiffies - strip_info->watchdog_doprobe >= 0 && !doreset; - u32 addr, brd; + __be32 addr, brd; /* * 1. If we have a packet, encapsulate it and put it in the buffer diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index 98b83a85c60..78c0a269a2b 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -374,6 +374,7 @@ static void __devexit parport_serial_pci_remove (struct pci_dev *dev) return; } +#ifdef CONFIG_PM static int parport_serial_pci_suspend(struct pci_dev *dev, pm_message_t state) { struct parport_serial_private *priv = pci_get_drvdata(dev); @@ -407,14 +408,17 @@ static int parport_serial_pci_resume(struct pci_dev *dev) return 0; } +#endif static struct pci_driver parport_serial_pci_driver = { .name = "parport_serial", .id_table = parport_serial_pci_tbl, .probe = parport_serial_pci_probe, .remove = __devexit_p(parport_serial_pci_remove), +#ifdef CONFIG_PM .suspend = parport_serial_pci_suspend, .resume = parport_serial_pci_resume, +#endif }; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 33a7b720539..62c804af9fb 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -27,7 +27,7 @@ config RTC_HCTOSYS help If you say yes here, the system time will be set using the value read from the specified RTC device. This is useful - in order to avoid unnecessary fschk runs. + in order to avoid unnecessary fsck runs. config RTC_HCTOSYS_DEVICE string "The RTC to read the time from" diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c index 0964d1dba92..25589061f93 100644 --- a/drivers/rtc/rtc-rs5c348.c +++ b/drivers/rtc/rtc-rs5c348.c @@ -23,7 +23,7 @@ #include <linux/workqueue.h> #include <linux/spi/spi.h> -#define DRV_VERSION "0.1" +#define DRV_VERSION "0.2" #define RS5C348_REG_SECS 0 #define RS5C348_REG_MINS 1 @@ -175,8 +175,15 @@ static int __devinit rs5c348_probe(struct spi_device *spi) goto kfree_exit; if (ret & (RS5C348_BIT_XSTP | RS5C348_BIT_VDET)) { u8 buf[2]; + struct rtc_time tm; if (ret & RS5C348_BIT_VDET) dev_warn(&spi->dev, "voltage-low detected.\n"); + if (ret & RS5C348_BIT_XSTP) + dev_warn(&spi->dev, "oscillator-stop detected.\n"); + rtc_time_to_tm(0, &tm); /* 1970/1/1 */ + ret = rs5c348_rtc_set_time(&spi->dev, &tm); + if (ret < 0) + goto kfree_exit; buf[0] = RS5C348_CMD_W(RS5C348_REG_CTL2); buf[1] = 0; ret = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0); diff --git a/drivers/s390/block/Kconfig b/drivers/s390/block/Kconfig index 929d6fff615..b250c535450 100644 --- a/drivers/s390/block/Kconfig +++ b/drivers/s390/block/Kconfig @@ -1,4 +1,4 @@ -if S390 +if S390 && BLOCK comment "S/390 block device drivers" depends on S390 diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 23fa0b28917..222a8a71a5e 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -63,44 +63,26 @@ static const u8 DASD_DIAG_CMS1[] = { 0xc3, 0xd4, 0xe2, 0xf1 };/* EBCDIC CMS1 */ * and function code cmd. * In case of an exception return 3. Otherwise return result of bitwise OR of * resulting condition code and DIAG return code. */ -static __inline__ int -dia250(void *iob, int cmd) +static inline int dia250(void *iob, int cmd) { + register unsigned long reg0 asm ("0") = (unsigned long) iob; typedef union { struct dasd_diag_init_io init_io; struct dasd_diag_rw_io rw_io; } addr_type; int rc; - __asm__ __volatile__( -#ifdef CONFIG_64BIT - " lghi %0,3\n" - " lgr 0,%3\n" - " diag 0,%2,0x250\n" - "0: ipm %0\n" - " srl %0,28\n" - " or %0,1\n" - "1:\n" - ".section __ex_table,\"a\"\n" - " .align 8\n" - " .quad 0b,1b\n" - ".previous\n" -#else - " lhi %0,3\n" - " lr 0,%3\n" + rc = 3; + asm volatile( " diag 0,%2,0x250\n" "0: ipm %0\n" " srl %0,28\n" " or %0,1\n" "1:\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 0b,1b\n" - ".previous\n" -#endif - : "=&d" (rc), "=m" (*(addr_type *) iob) - : "d" (cmd), "d" (iob), "m" (*(addr_type *) iob) - : "0", "1", "cc"); + EX_TABLE(0b,1b) + : "+d" (rc), "=m" (*(addr_type *) iob) + : "d" (cmd), "d" (reg0), "m" (*(addr_type *) iob) + : "1", "cc"); return rc; } @@ -547,7 +529,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) } cqr->retries = DIAG_MAX_RETRIES; cqr->buildclk = get_clock(); - if (req->flags & REQ_FAILFAST) + if (req->cmd_flags & REQ_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->device = device; cqr->expires = DIAG_TIMEOUT; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index b7a7fac3f7c..5ecea3e4fde 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1266,7 +1266,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) recid++; } } - if (req->flags & REQ_FAILFAST) + if (req->cmd_flags & REQ_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->device = device; cqr->expires = 5 * 60 * HZ; /* 5 minutes */ diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index e85015be109..80926c54822 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -344,7 +344,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) recid++; } } - if (req->flags & REQ_FAILFAST) + if (req->cmd_flags & REQ_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->device = device; cqr->expires = 5 * 60 * HZ; /* 5 minutes */ diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index cab2c736683..a04d9120cef 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c @@ -89,28 +89,15 @@ MODULE_LICENSE("GPL"); */ static int xpram_page_in (unsigned long page_addr, unsigned int xpage_index) { - int cc; + int cc = 2; /* return unused cc 2 if pgin traps */ - __asm__ __volatile__ ( - " lhi %0,2\n" /* return unused cc 2 if pgin traps */ - " .insn rre,0xb22e0000,%1,%2\n" /* pgin %1,%2 */ - "0: ipm %0\n" - " srl %0,28\n" + asm volatile( + " .insn rre,0xb22e0000,%1,%2\n" /* pgin %1,%2 */ + "0: ipm %0\n" + " srl %0,28\n" "1:\n" -#ifndef CONFIG_64BIT - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 0b,1b\n" - ".previous" -#else - ".section __ex_table,\"a\"\n" - " .align 8\n" - " .quad 0b,1b\n" - ".previous" -#endif - : "=&d" (cc) - : "a" (__pa(page_addr)), "a" (xpage_index) - : "cc" ); + EX_TABLE(0b,1b) + : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc"); if (cc == 3) return -ENXIO; if (cc == 2) { @@ -137,28 +124,15 @@ static int xpram_page_in (unsigned long page_addr, unsigned int xpage_index) */ static long xpram_page_out (unsigned long page_addr, unsigned int xpage_index) { - int cc; + int cc = 2; /* return unused cc 2 if pgin traps */ - __asm__ __volatile__ ( - " lhi %0,2\n" /* return unused cc 2 if pgout traps */ - " .insn rre,0xb22f0000,%1,%2\n" /* pgout %1,%2 */ - "0: ipm %0\n" - " srl %0,28\n" + asm volatile( + " .insn rre,0xb22f0000,%1,%2\n" /* pgout %1,%2 */ + "0: ipm %0\n" + " srl %0,28\n" "1:\n" -#ifndef CONFIG_64BIT - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 0b,1b\n" - ".previous" -#else - ".section __ex_table,\"a\"\n" - " .align 8\n" - " .quad 0b,1b\n" - ".previous" -#endif - : "=&d" (cc) - : "a" (__pa(page_addr)), "a" (xpage_index) - : "cc" ); + EX_TABLE(0b,1b) + : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc"); if (cc == 3) return -ENXIO; if (cc == 2) { diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index ef004d08971..b4557fa3085 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -17,7 +17,6 @@ #include <asm/ccwdev.h> #include <asm/cio.h> -#include <asm/cpcmd.h> #include <asm/ebcdic.h> #include <asm/idals.h> diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 985d1613baa..31e335751d6 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -100,13 +100,12 @@ service_call(sclp_cmdw_t command, void *sccb) { int cc; - __asm__ __volatile__( - " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ - " ipm %0\n" - " srl %0,28" - : "=&d" (cc) - : "d" (command), "a" (__pa(sccb)) - : "cc", "memory" ); + asm volatile( + " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ + " ipm %0\n" + " srl %0,28" + : "=&d" (cc) : "d" (command), "a" (__pa(sccb)) + : "cc", "memory"); if (cc == 3) return -EIO; if (cc == 2) @@ -360,16 +359,6 @@ sclp_interrupt_handler(struct pt_regs *regs, __u16 code) sclp_process_queue(); } -/* Return current Time-Of-Day clock. */ -static inline u64 -sclp_get_clock(void) -{ - u64 result; - - asm volatile ("STCK 0(%1)" : "=m" (result) : "a" (&(result)) : "cc"); - return result; -} - /* Convert interval in jiffies to TOD ticks. */ static inline u64 sclp_tod_from_jiffies(unsigned long jiffies) @@ -382,7 +371,6 @@ sclp_tod_from_jiffies(unsigned long jiffies) void sclp_sync_wait(void) { - unsigned long psw_mask; unsigned long flags; unsigned long cr0, cr0_sync; u64 timeout; @@ -392,7 +380,7 @@ sclp_sync_wait(void) timeout = 0; if (timer_pending(&sclp_request_timer)) { /* Get timeout TOD value */ - timeout = sclp_get_clock() + + timeout = get_clock() + sclp_tod_from_jiffies(sclp_request_timer.expires - jiffies); } @@ -406,13 +394,12 @@ sclp_sync_wait(void) cr0_sync |= 0x00000200; cr0_sync &= 0xFFFFF3AC; __ctl_load(cr0_sync, 0, 0); - asm volatile ("STOSM 0(%1),0x01" - : "=m" (psw_mask) : "a" (&psw_mask) : "memory"); + __raw_local_irq_stosm(0x01); /* Loop until driver state indicates finished request */ while (sclp_running_state != sclp_running_state_idle) { /* Check for expired request timer */ if (timer_pending(&sclp_request_timer) && - sclp_get_clock() > timeout && + get_clock() > timeout && del_timer(&sclp_request_timer)) sclp_request_timer.function(sclp_request_timer.data); barrier(); diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 29718042c6c..06e2eeec847 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -698,7 +698,6 @@ tty3270_alloc_view(void) if (!tp->freemem_pages) goto out_tp; INIT_LIST_HEAD(&tp->freemem); - init_timer(&tp->timer); for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) { tp->freemem_pages[pages] = (void *) __get_free_pages(GFP_KERNEL|GFP_DMA, 0); diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index 807320a41fa..4b868f72fe8 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c @@ -54,48 +54,20 @@ enum vmwdt_func { static int __diag288(enum vmwdt_func func, unsigned int timeout, char *cmd, size_t len) { - register unsigned long __func asm("2"); - register unsigned long __timeout asm("3"); - register unsigned long __cmdp asm("4"); - register unsigned long __cmdl asm("5"); + register unsigned long __func asm("2") = func; + register unsigned long __timeout asm("3") = timeout; + register unsigned long __cmdp asm("4") = virt_to_phys(cmd); + register unsigned long __cmdl asm("5") = len; int err; - __func = func; - __timeout = timeout; - __cmdp = virt_to_phys(cmd); - __cmdl = len; - err = 0; - asm volatile ( -#ifdef CONFIG_64BIT - "diag %2,%4,0x288\n" - "1: \n" - ".section .fixup,\"ax\"\n" - "2: lghi %0,%1\n" - " jg 1b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 8\n" - " .quad 1b,2b\n" - ".previous\n" -#else - "diag %2,%4,0x288\n" - "1: \n" - ".section .fixup,\"ax\"\n" - "2: lhi %0,%1\n" - " bras 1,3f\n" - " .long 1b\n" - "3: l 1,0(1)\n" - " br 1\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,2b\n" - ".previous\n" -#endif - : "+&d"(err) - : "i"(-EINVAL), "d"(__func), "d"(__timeout), - "d"(__cmdp), "d"(__cmdl) - : "1", "cc"); + err = -EINVAL; + asm volatile( + " diag %1,%3,0x288\n" + "0: la %0,0\n" + "1:\n" + EX_TABLE(0b,1b) + : "=d" (err) : "d"(__func), "d"(__timeout), + "d"(__cmdp), "d"(__cmdl), "0" (-EINVAL) : "1", "cc"); return err; } diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c index 438db483035..1398367b5f6 100644 --- a/drivers/s390/cio/device_id.c +++ b/drivers/s390/cio/device_id.c @@ -42,18 +42,15 @@ diag210(struct diag210 * addr) spin_lock_irqsave(&diag210_lock, flags); diag210_tmp = *addr; - asm volatile ( - " lhi %0,-1\n" - " sam31\n" - " diag %1,0,0x210\n" - "0: ipm %0\n" - " srl %0,28\n" - "1: sam64\n" - ".section __ex_table,\"a\"\n" - " .align 8\n" - " .quad 0b,1b\n" - ".previous" - : "=&d" (ccode) : "a" (__pa(&diag210_tmp)) : "cc", "memory" ); + asm volatile( + " lhi %0,-1\n" + " sam31\n" + " diag %1,0,0x210\n" + "0: ipm %0\n" + " srl %0,28\n" + "1: sam64\n" + EX_TABLE(0b,1b) + : "=&d" (ccode) : "a" (__pa(&diag210_tmp)) : "cc", "memory"); *addr = diag210_tmp; spin_unlock_irqrestore(&diag210_lock, flags); @@ -66,17 +63,14 @@ diag210(struct diag210 * addr) { int ccode; - asm volatile ( - " lhi %0,-1\n" - " diag %1,0,0x210\n" - "0: ipm %0\n" - " srl %0,28\n" + asm volatile( + " lhi %0,-1\n" + " diag %1,0,0x210\n" + "0: ipm %0\n" + " srl %0,28\n" "1:\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 0b,1b\n" - ".previous" - : "=&d" (ccode) : "a" (__pa(addr)) : "cc", "memory" ); + EX_TABLE(0b,1b) + : "=&d" (ccode) : "a" (__pa(addr)) : "cc", "memory"); return ccode; } diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h index 95a9462f9a9..ad6d8294006 100644 --- a/drivers/s390/cio/ioasm.h +++ b/drivers/s390/cio/ioasm.h @@ -25,106 +25,74 @@ struct tpi_info { static inline int stsch(struct subchannel_id schid, volatile struct schib *addr) { + register struct subchannel_id reg1 asm ("1") = schid; int ccode; - __asm__ __volatile__( - " lr 1,%1\n" - " stsch 0(%2)\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "d" (schid), "a" (addr), "m" (*addr) - : "cc", "1" ); + asm volatile( + " stsch 0(%2)\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc"); return ccode; } static inline int stsch_err(struct subchannel_id schid, volatile struct schib *addr) { - int ccode; + register struct subchannel_id reg1 asm ("1") = schid; + int ccode = -EIO; - __asm__ __volatile__( - " lhi %0,%3\n" - " lr 1,%1\n" - " stsch 0(%2)\n" - "0: ipm %0\n" - " srl %0,28\n" + asm volatile( + " stsch 0(%2)\n" + "0: ipm %0\n" + " srl %0,28\n" "1:\n" -#ifdef CONFIG_64BIT - ".section __ex_table,\"a\"\n" - " .align 8\n" - " .quad 0b,1b\n" - ".previous" -#else - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 0b,1b\n" - ".previous" -#endif - : "=&d" (ccode) - : "d" (schid), "a" (addr), "K" (-EIO), "m" (*addr) - : "cc", "1" ); + EX_TABLE(0b,1b) + : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc"); return ccode; } static inline int msch(struct subchannel_id schid, volatile struct schib *addr) { + register struct subchannel_id reg1 asm ("1") = schid; int ccode; - __asm__ __volatile__( - " lr 1,%1\n" - " msch 0(%2)\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "d" (schid), "a" (addr), "m" (*addr) - : "cc", "1" ); + asm volatile( + " msch 0(%2)\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc"); return ccode; } static inline int msch_err(struct subchannel_id schid, volatile struct schib *addr) { - int ccode; + register struct subchannel_id reg1 asm ("1") = schid; + int ccode = -EIO; - __asm__ __volatile__( - " lhi %0,%3\n" - " lr 1,%1\n" - " msch 0(%2)\n" - "0: ipm %0\n" - " srl %0,28\n" + asm volatile( + " msch 0(%2)\n" + "0: ipm %0\n" + " srl %0,28\n" "1:\n" -#ifdef CONFIG_64BIT - ".section __ex_table,\"a\"\n" - " .align 8\n" - " .quad 0b,1b\n" - ".previous" -#else - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 0b,1b\n" - ".previous" -#endif - : "=&d" (ccode) - : "d" (schid), "a" (addr), "K" (-EIO), "m" (*addr) - : "cc", "1" ); + EX_TABLE(0b,1b) + : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc"); return ccode; } static inline int tsch(struct subchannel_id schid, volatile struct irb *addr) { + register struct subchannel_id reg1 asm ("1") = schid; int ccode; - __asm__ __volatile__( - " lr 1,%1\n" - " tsch 0(%2)\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "d" (schid), "a" (addr), "m" (*addr) - : "cc", "1" ); + asm volatile( + " tsch 0(%2)\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc"); return ccode; } @@ -132,89 +100,77 @@ static inline int tpi( volatile struct tpi_info *addr) { int ccode; - __asm__ __volatile__( - " tpi 0(%1)\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "a" (addr), "m" (*addr) - : "cc", "1" ); + asm volatile( + " tpi 0(%1)\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "a" (addr), "m" (*addr) : "cc"); return ccode; } static inline int ssch(struct subchannel_id schid, volatile struct orb *addr) { + register struct subchannel_id reg1 asm ("1") = schid; int ccode; - __asm__ __volatile__( - " lr 1,%1\n" - " ssch 0(%2)\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "d" (schid), "a" (addr), "m" (*addr) - : "cc", "1" ); + asm volatile( + " ssch 0(%2)\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc"); return ccode; } static inline int rsch(struct subchannel_id schid) { + register struct subchannel_id reg1 asm ("1") = schid; int ccode; - __asm__ __volatile__( - " lr 1,%1\n" - " rsch\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "d" (schid) - : "cc", "1" ); + asm volatile( + " rsch\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1) : "cc"); return ccode; } static inline int csch(struct subchannel_id schid) { + register struct subchannel_id reg1 asm ("1") = schid; int ccode; - __asm__ __volatile__( - " lr 1,%1\n" - " csch\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "d" (schid) - : "cc", "1" ); + asm volatile( + " csch\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1) : "cc"); return ccode; } static inline int hsch(struct subchannel_id schid) { + register struct subchannel_id reg1 asm ("1") = schid; int ccode; - __asm__ __volatile__( - " lr 1,%1\n" - " hsch\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "d" (schid) - : "cc", "1" ); + asm volatile( + " hsch\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1) : "cc"); return ccode; } static inline int xsch(struct subchannel_id schid) { + register struct subchannel_id reg1 asm ("1") = schid; int ccode; - __asm__ __volatile__( - " lr 1,%1\n" - " .insn rre,0xb2760000,%1,0\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "d" (schid) - : "cc", "1" ); + asm volatile( + " .insn rre,0xb2760000,%1,0\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1) : "cc"); return ccode; } @@ -223,41 +179,27 @@ static inline int chsc(void *chsc_area) typedef struct { char _[4096]; } addr_type; int cc; - __asm__ __volatile__ ( - ".insn rre,0xb25f0000,%2,0 \n\t" - "ipm %0 \n\t" - "srl %0,28 \n\t" + asm volatile( + " .insn rre,0xb25f0000,%2,0\n" + " ipm %0\n" + " srl %0,28\n" : "=d" (cc), "=m" (*(addr_type *) chsc_area) : "d" (chsc_area), "m" (*(addr_type *) chsc_area) - : "cc" ); - + : "cc"); return cc; } -static inline int iac( void) -{ - int ccode; - - __asm__ __volatile__( - " iac 1\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) : : "cc", "1" ); - return ccode; -} - static inline int rchp(int chpid) { + register unsigned int reg1 asm ("1") = chpid; int ccode; - __asm__ __volatile__( - " lr 1,%1\n" - " rchp\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "d" (chpid) - : "cc", "1" ); + asm volatile( + " lr 1,%1\n" + " rchp\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1) : "cc"); return ccode; } diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 124569362f0..49bb9e371c3 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -274,12 +274,11 @@ do_sqbs(unsigned long sch, unsigned char state, int queue, register unsigned long _sch asm ("1") = sch; unsigned long _queuestart = ((unsigned long)queue << 32) | *start; - asm volatile ( - " .insn rsy,0xeb000000008A,%1,0,0(%2)\n\t" - : "+d" (_ccq), "+d" (_queuestart) - : "d" ((unsigned long)state), "d" (_sch) - : "memory", "cc" - ); + asm volatile( + " .insn rsy,0xeb000000008A,%1,0,0(%2)" + : "+d" (_ccq), "+d" (_queuestart) + : "d" ((unsigned long)state), "d" (_sch) + : "memory", "cc"); *count = _ccq & 0xff; *start = _queuestart & 0xff; @@ -299,12 +298,11 @@ do_eqbs(unsigned long sch, unsigned char *state, int queue, unsigned long _queuestart = ((unsigned long)queue << 32) | *start; unsigned long _state = 0; - asm volatile ( - " .insn rrf,0xB99c0000,%1,%2,0,0 \n\t" - : "+d" (_ccq), "+d" (_queuestart), "+d" (_state) - : "d" (_sch) - : "memory", "cc" - ); + asm volatile( + " .insn rrf,0xB99c0000,%1,%2,0,0" + : "+d" (_ccq), "+d" (_queuestart), "+d" (_state) + : "d" (_sch) + : "memory", "cc" ); *count = _ccq & 0xff; *start = _queuestart & 0xff; *state = _state & 0xff; @@ -319,69 +317,35 @@ do_eqbs(unsigned long sch, unsigned char *state, int queue, static inline int do_siga_sync(struct subchannel_id schid, unsigned int mask1, unsigned int mask2) { + register unsigned long reg0 asm ("0") = 2; + register struct subchannel_id reg1 asm ("1") = schid; + register unsigned long reg2 asm ("2") = mask1; + register unsigned long reg3 asm ("3") = mask2; int cc; -#ifndef CONFIG_64BIT - asm volatile ( - "lhi 0,2 \n\t" - "lr 1,%1 \n\t" - "lr 2,%2 \n\t" - "lr 3,%3 \n\t" - "siga 0 \n\t" - "ipm %0 \n\t" - "srl %0,28 \n\t" + asm volatile( + " siga 0\n" + " ipm %0\n" + " srl %0,28\n" : "=d" (cc) - : "d" (schid), "d" (mask1), "d" (mask2) - : "cc", "0", "1", "2", "3" - ); -#else /* CONFIG_64BIT */ - asm volatile ( - "lghi 0,2 \n\t" - "llgfr 1,%1 \n\t" - "llgfr 2,%2 \n\t" - "llgfr 3,%3 \n\t" - "siga 0 \n\t" - "ipm %0 \n\t" - "srl %0,28 \n\t" - : "=d" (cc) - : "d" (schid), "d" (mask1), "d" (mask2) - : "cc", "0", "1", "2", "3" - ); -#endif /* CONFIG_64BIT */ + : "d" (reg0), "d" (reg1), "d" (reg2), "d" (reg3) : "cc"); return cc; } static inline int do_siga_input(struct subchannel_id schid, unsigned int mask) { + register unsigned long reg0 asm ("0") = 1; + register struct subchannel_id reg1 asm ("1") = schid; + register unsigned long reg2 asm ("2") = mask; int cc; -#ifndef CONFIG_64BIT - asm volatile ( - "lhi 0,1 \n\t" - "lr 1,%1 \n\t" - "lr 2,%2 \n\t" - "siga 0 \n\t" - "ipm %0 \n\t" - "srl %0,28 \n\t" - : "=d" (cc) - : "d" (schid), "d" (mask) - : "cc", "0", "1", "2", "memory" - ); -#else /* CONFIG_64BIT */ - asm volatile ( - "lghi 0,1 \n\t" - "llgfr 1,%1 \n\t" - "llgfr 2,%2 \n\t" - "siga 0 \n\t" - "ipm %0 \n\t" - "srl %0,28 \n\t" + asm volatile( + " siga 0\n" + " ipm %0\n" + " srl %0,28\n" : "=d" (cc) - : "d" (schid), "d" (mask) - : "cc", "0", "1", "2", "memory" - ); -#endif /* CONFIG_64BIT */ - + : "d" (reg0), "d" (reg1), "d" (reg2) : "cc", "memory"); return cc; } @@ -389,93 +353,35 @@ static inline int do_siga_output(unsigned long schid, unsigned long mask, __u32 *bb, unsigned int fc) { + register unsigned long __fc asm("0") = fc; + register unsigned long __schid asm("1") = schid; + register unsigned long __mask asm("2") = mask; int cc; - __u32 busy_bit; - -#ifndef CONFIG_64BIT - asm volatile ( - "lhi 0,0 \n\t" - "lr 1,%2 \n\t" - "lr 2,%3 \n\t" - "siga 0 \n\t" - "0:" - "ipm %0 \n\t" - "srl %0,28 \n\t" - "srl 0,31 \n\t" - "lr %1,0 \n\t" - "1: \n\t" - ".section .fixup,\"ax\"\n\t" - "2: \n\t" - "lhi %0,%4 \n\t" - "bras 1,3f \n\t" - ".long 1b \n\t" - "3: \n\t" - "l 1,0(1) \n\t" - "br 1 \n\t" - ".previous \n\t" - ".section __ex_table,\"a\"\n\t" - ".align 4 \n\t" - ".long 0b,2b \n\t" - ".previous \n\t" - : "=d" (cc), "=d" (busy_bit) - : "d" (schid), "d" (mask), - "i" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION) - : "cc", "0", "1", "2", "memory" - ); -#else /* CONFIG_64BIT */ - asm volatile ( - "llgfr 0,%5 \n\t" - "lgr 1,%2 \n\t" - "llgfr 2,%3 \n\t" - "siga 0 \n\t" - "0:" - "ipm %0 \n\t" - "srl %0,28 \n\t" - "srl 0,31 \n\t" - "llgfr %1,0 \n\t" - "1: \n\t" - ".section .fixup,\"ax\"\n\t" - "lghi %0,%4 \n\t" - "jg 1b \n\t" - ".previous\n\t" - ".section __ex_table,\"a\"\n\t" - ".align 8 \n\t" - ".quad 0b,1b \n\t" - ".previous \n\t" - : "=d" (cc), "=d" (busy_bit) - : "d" (schid), "d" (mask), - "i" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION), "d" (fc) - : "cc", "0", "1", "2", "memory" - ); -#endif /* CONFIG_64BIT */ - - (*bb) = busy_bit; + + asm volatile( + " siga 0\n" + "0: ipm %0\n" + " srl %0,28\n" + "1:\n" + EX_TABLE(0b,1b) + : "=d" (cc), "+d" (__fc), "+d" (__schid), "+d" (__mask) + : "0" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION) + : "cc", "memory"); + (*bb) = ((unsigned int) __fc) >> 31; return cc; } static inline unsigned long do_clear_global_summary(void) { - - unsigned long time; - -#ifndef CONFIG_64BIT - asm volatile ( - "lhi 1,3 \n\t" - ".insn rre,0xb2650000,2,0 \n\t" - "lr %0,3 \n\t" - : "=d" (time) : : "cc", "1", "2", "3" - ); -#else /* CONFIG_64BIT */ - asm volatile ( - "lghi 1,3 \n\t" - ".insn rre,0xb2650000,2,0 \n\t" - "lgr %0,3 \n\t" - : "=d" (time) : : "cc", "1", "2", "3" - ); -#endif /* CONFIG_64BIT */ - - return time; + register unsigned long __fn asm("1") = 3; + register unsigned long __tmp asm("2"); + register unsigned long __time asm("3"); + + asm volatile( + " .insn rre,0xb2650000,2,0" + : "+d" (__fn), "=d" (__tmp), "=d" (__time)); + return __time; } /* diff --git a/drivers/s390/net/iucv.c b/drivers/s390/net/iucv.c index 821dde86e24..809dd8d7f47 100644 --- a/drivers/s390/net/iucv.c +++ b/drivers/s390/net/iucv.c @@ -534,19 +534,15 @@ iucv_add_handler (handler *new) * * Returns: return code from CP's IUCV call */ -static __inline__ ulong -b2f0(__u32 code, void *parm) +static inline ulong b2f0(__u32 code, void *parm) { + register unsigned long reg0 asm ("0"); + register unsigned long reg1 asm ("1"); iucv_dumpit("iparml before b2f0 call:", parm, sizeof(iucv_param)); - asm volatile ( - "LRA 1,0(%1)\n\t" - "LR 0,%0\n\t" - ".long 0xb2f01000" - : - : "d" (code), "a" (parm) - : "0", "1" - ); + reg0 = code; + reg1 = virt_to_phys(parm); + asm volatile(".long 0xb2f01000" : : "d" (reg0), "a" (reg1)); iucv_dumpit("iparml after b2f0 call:", parm, sizeof(iucv_param)); @@ -1248,6 +1244,8 @@ iucv_purge (__u16 pathid, __u32 msgid, __u32 srccls, __u32 *audit) static int iucv_query_generic(int want_maxconn) { + register unsigned long reg0 asm ("0"); + register unsigned long reg1 asm ("1"); iparml_purge *parm = (iparml_purge *)grab_param(); int bufsize, maxconn; int ccode; @@ -1256,18 +1254,15 @@ iucv_query_generic(int want_maxconn) * Call b2f0 and store R0 (max buffer size), * R1 (max connections) and CC. */ - asm volatile ( - "LRA 1,0(%4)\n\t" - "LR 0,%3\n\t" - ".long 0xb2f01000\n\t" - "IPM %0\n\t" - "SRL %0,28\n\t" - "ST 0,%1\n\t" - "ST 1,%2\n\t" - : "=d" (ccode), "=m" (bufsize), "=m" (maxconn) - : "d" (QUERY), "a" (parm) - : "0", "1", "cc" - ); + reg0 = QUERY; + reg1 = virt_to_phys(parm); + asm volatile( + " .long 0xb2f01000\n" + " ipm %0\n" + " srl %0,28\n" + : "=d" (ccode), "+d" (reg0), "+d" (reg1) : : "cc"); + bufsize = reg0; + maxconn = reg1; release_param(parm); if (ccode) diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index 5613b4564fa..8364d5475ac 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c @@ -8067,7 +8067,7 @@ qeth_arp_constructor(struct neighbour *neigh) neigh->parms = neigh_parms_clone(parms); rcu_read_unlock(); - neigh->type = inet_addr_type(*(u32 *) neigh->primary_key); + neigh->type = inet_addr_type(*(__be32 *) neigh->primary_key); neigh->nud_state = NUD_NOARP; neigh->ops = arp_direct_ops; neigh->output = neigh->ops->queue_xmit; diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c index a914129a4da..479364d0332 100644 --- a/drivers/s390/s390mach.c +++ b/drivers/s390/s390mach.c @@ -253,11 +253,12 @@ s390_revalidate_registers(struct mci *mci) kill_task = 1; #ifndef CONFIG_64BIT - asm volatile("ld 0,0(%0)\n" - "ld 2,8(%0)\n" - "ld 4,16(%0)\n" - "ld 6,24(%0)" - : : "a" (&S390_lowcore.floating_pt_save_area)); + asm volatile( + " ld 0,0(%0)\n" + " ld 2,8(%0)\n" + " ld 4,16(%0)\n" + " ld 6,24(%0)" + : : "a" (&S390_lowcore.floating_pt_save_area)); #endif if (MACHINE_HAS_IEEE) { @@ -274,37 +275,36 @@ s390_revalidate_registers(struct mci *mci) * Floating point control register can't be restored. * Task will be terminated. */ - asm volatile ("lfpc 0(%0)" : : "a" (&zero), "m" (zero)); + asm volatile("lfpc 0(%0)" : : "a" (&zero), "m" (zero)); kill_task = 1; - } - else - asm volatile ( - "lfpc 0(%0)" - : : "a" (fpt_creg_save_area)); - - asm volatile("ld 0,0(%0)\n" - "ld 1,8(%0)\n" - "ld 2,16(%0)\n" - "ld 3,24(%0)\n" - "ld 4,32(%0)\n" - "ld 5,40(%0)\n" - "ld 6,48(%0)\n" - "ld 7,56(%0)\n" - "ld 8,64(%0)\n" - "ld 9,72(%0)\n" - "ld 10,80(%0)\n" - "ld 11,88(%0)\n" - "ld 12,96(%0)\n" - "ld 13,104(%0)\n" - "ld 14,112(%0)\n" - "ld 15,120(%0)\n" - : : "a" (fpt_save_area)); + } else + asm volatile("lfpc 0(%0)" : : "a" (fpt_creg_save_area)); + + asm volatile( + " ld 0,0(%0)\n" + " ld 1,8(%0)\n" + " ld 2,16(%0)\n" + " ld 3,24(%0)\n" + " ld 4,32(%0)\n" + " ld 5,40(%0)\n" + " ld 6,48(%0)\n" + " ld 7,56(%0)\n" + " ld 8,64(%0)\n" + " ld 9,72(%0)\n" + " ld 10,80(%0)\n" + " ld 11,88(%0)\n" + " ld 12,96(%0)\n" + " ld 13,104(%0)\n" + " ld 14,112(%0)\n" + " ld 15,120(%0)\n" + : : "a" (fpt_save_area)); } /* Revalidate access registers */ - asm volatile("lam 0,15,0(%0)" - : : "a" (&S390_lowcore.access_regs_save_area)); + asm volatile( + " lam 0,15,0(%0)" + : : "a" (&S390_lowcore.access_regs_save_area)); if (!mci->ar) /* * Access registers have unknown contents. @@ -321,11 +321,13 @@ s390_revalidate_registers(struct mci *mci) s390_handle_damage("invalid control registers."); else #ifdef CONFIG_64BIT - asm volatile("lctlg 0,15,0(%0)" - : : "a" (&S390_lowcore.cregs_save_area)); + asm volatile( + " lctlg 0,15,0(%0)" + : : "a" (&S390_lowcore.cregs_save_area)); #else - asm volatile("lctl 0,15,0(%0)" - : : "a" (&S390_lowcore.cregs_save_area)); + asm volatile( + " lctl 0,15,0(%0)" + : : "a" (&S390_lowcore.cregs_save_area)); #endif /* @@ -339,20 +341,23 @@ s390_revalidate_registers(struct mci *mci) * old contents (should be zero) otherwise set it to zero. */ if (!mci->pr) - asm volatile("sr 0,0\n" - "sckpf" - : : : "0", "cc"); + asm volatile( + " sr 0,0\n" + " sckpf" + : : : "0", "cc"); else asm volatile( - "l 0,0(%0)\n" - "sckpf" - : : "a" (&S390_lowcore.tod_progreg_save_area) : "0", "cc"); + " l 0,0(%0)\n" + " sckpf" + : : "a" (&S390_lowcore.tod_progreg_save_area) + : "0", "cc"); #endif /* Revalidate clock comparator register */ - asm volatile ("stck 0(%1)\n" - "sckc 0(%1)" - : "=m" (tmpclock) : "a" (&(tmpclock)) : "cc", "memory"); + asm volatile( + " stck 0(%1)\n" + " sckc 0(%1)" + : "=m" (tmpclock) : "a" (&(tmpclock)) : "cc", "memory"); /* Check if old PSW is valid */ if (!mci->wp) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index c4dfcc91ddd..dab082002e6 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -3,11 +3,13 @@ menu "SCSI device support" config RAID_ATTRS tristate "RAID Transport Class" default n + depends on BLOCK ---help--- Provides RAID config SCSI tristate "SCSI device support" + depends on BLOCK ---help--- If you want to use a SCSI hard disk, SCSI tape drive, SCSI CD-ROM or any other SCSI device under Linux, say Y and make sure that you know diff --git a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c index 5dcef48d414..10353379a07 100644 --- a/drivers/scsi/aic7xxx_old.c +++ b/drivers/scsi/aic7xxx_old.c @@ -2862,7 +2862,7 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb) aic_dev->r_total++; ptr = aic_dev->r_bins; } - if(cmd->device->simple_tags && cmd->request->flags & REQ_HARDBARRIER) + if(cmd->device->simple_tags && cmd->request->cmd_flags & REQ_HARDBARRIER) { aic_dev->barrier_total++; if(scb->tag_action == MSG_ORDERED_Q_TAG) @@ -10158,7 +10158,7 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, /* We always force TEST_UNIT_READY to untagged */ if (cmd->cmnd[0] != TEST_UNIT_READY && sdptr->simple_tags) { - if (req->flags & REQ_HARDBARRIER) + if (req->cmd_flags & REQ_HARDBARRIER) { if(sdptr->ordered_tags) { diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 94d1de55607..1427a41e844 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -344,7 +344,7 @@ static int idescsi_check_condition(ide_drive_t *drive, struct request *failed_co pc->buffer = buf; pc->c[0] = REQUEST_SENSE; pc->c[4] = pc->request_transfer = pc->buffer_size = SCSI_SENSE_BUFFERSIZE; - rq->flags = REQ_SENSE; + rq->cmd_type = REQ_TYPE_SENSE; pc->timeout = jiffies + WAIT_READY; /* NOTE! Save the failed packet command in "rq->buffer" */ rq->buffer = (void *) failed_command->special; @@ -398,12 +398,12 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) int errors = rq->errors; unsigned long flags; - if (!(rq->flags & (REQ_SPECIAL|REQ_SENSE))) { + if (!blk_special_request(rq) && !blk_sense_request(rq)) { ide_end_request(drive, uptodate, nrsecs); return 0; } ide_end_drive_cmd (drive, 0, 0); - if (rq->flags & REQ_SENSE) { + if (blk_sense_request(rq)) { idescsi_pc_t *opc = (idescsi_pc_t *) rq->buffer; if (log) { printk ("ide-scsi: %s: wrap up check %lu, rst = ", drive->name, opc->scsi_cmd->serial_number); @@ -708,11 +708,11 @@ static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc) static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *rq, sector_t block) { #if IDESCSI_DEBUG_LOG - printk (KERN_INFO "rq_status: %d, dev: %s, cmd: %x, errors: %d\n",rq->rq_status, rq->rq_disk->disk_name,rq->cmd[0],rq->errors); + printk (KERN_INFO "dev: %s, cmd: %x, errors: %d\n", rq->rq_disk->disk_name,rq->cmd[0],rq->errors); printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %d\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors); #endif /* IDESCSI_DEBUG_LOG */ - if (rq->flags & (REQ_SPECIAL|REQ_SENSE)) { + if (blk_sense_request(rq) || blk_special_request(rq)) { return idescsi_issue_pc (drive, (idescsi_pc_t *) rq->special); } blk_dump_rq_flags(rq, "ide-scsi: unsup command"); @@ -938,7 +938,7 @@ static int idescsi_queue (struct scsi_cmnd *cmd, ide_init_drive_cmd (rq); rq->special = (char *) pc; - rq->flags = REQ_SPECIAL; + rq->cmd_type = REQ_TYPE_SPECIAL; spin_unlock_irq(host->host_lock); rq->rq_disk = scsi->disk; (void) ide_do_drive_cmd (drive, rq, ide_end); @@ -992,7 +992,7 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd) */ printk (KERN_ERR "ide-scsi: cmd aborted!\n"); - if (scsi->pc->rq->flags & REQ_SENSE) + if (blk_sense_request(scsi->pc->rq)) kfree(scsi->pc->buffer); kfree(scsi->pc->rq); kfree(scsi->pc); @@ -1042,7 +1042,7 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd) /* kill current request */ blkdev_dequeue_request(req); end_that_request_last(req, 0); - if (req->flags & REQ_SENSE) + if (blk_sense_request(req)) kfree(scsi->pc->buffer); kfree(scsi->pc); scsi->pc = NULL; diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 7f9e89bcac7..e46e79355b7 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -126,7 +126,7 @@ static enum task_attribute sas_scsi_get_task_attr(struct scsi_cmnd *cmd) enum task_attribute ta = TASK_ATTR_SIMPLE; if (cmd->request && blk_rq_tagged(cmd->request)) { if (cmd->device->ordered_tags && - (cmd->request->flags & REQ_HARDBARRIER)) + (cmd->request->cmd_flags & REQ_HARDBARRIER)) ta = TASK_ATTR_HOQ; } return ta; diff --git a/drivers/scsi/pluto.c b/drivers/scsi/pluto.c index 0bd9c60e645..aa60a5f1fbc 100644 --- a/drivers/scsi/pluto.c +++ b/drivers/scsi/pluto.c @@ -67,7 +67,6 @@ static void __init pluto_detect_done(Scsi_Cmnd *SCpnt) static void __init pluto_detect_scsi_done(Scsi_Cmnd *SCpnt) { - SCpnt->request->rq_status = RQ_SCSI_DONE; PLND(("Detect done %08lx\n", (long)SCpnt)) if (atomic_dec_and_test (&fcss)) up(&fc_sem); @@ -166,7 +165,7 @@ int __init pluto_detect(struct scsi_host_template *tpnt) SCpnt->cmd_len = COMMAND_SIZE(INQUIRY); - SCpnt->request->rq_status = RQ_SCSI_BUSY; + SCpnt->request->cmd_flags &= ~REQ_STARTED; SCpnt->done = pluto_detect_done; SCpnt->request_bufflen = 256; @@ -178,7 +177,8 @@ int __init pluto_detect(struct scsi_host_template *tpnt) for (retry = 0; retry < 5; retry++) { for (i = 0; i < fcscount; i++) { if (!fcs[i].fc) break; - if (fcs[i].cmd.request->rq_status != RQ_SCSI_DONE) { + if (!(fcs[i].cmd.request->cmd_flags & REQ_STARTED)) { + fcs[i].cmd.request->cmd_flags |= REQ_STARTED; disable_irq(fcs[i].fc->irq); PLND(("queuecommand %d %d\n", retry, i)) fcp_scsi_queuecommand (&(fcs[i].cmd), diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 7a054f9d1ee..da95bce907d 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -592,12 +592,6 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) return rtn; } - -/* - * Per-CPU I/O completion queue. - */ -static DEFINE_PER_CPU(struct list_head, scsi_done_q); - /** * scsi_req_abort_cmd -- Request command recovery for the specified command * cmd: pointer to the SCSI command of interest @@ -1065,7 +1059,7 @@ int scsi_device_cancel(struct scsi_device *sdev, int recovery) spin_lock_irqsave(&sdev->list_lock, flags); list_for_each_entry(scmd, &sdev->cmd_list, list) { - if (scmd->request && scmd->request->rq_status != RQ_INACTIVE) { + if (scmd->request) { /* * If we are unable to remove the timer, it means * that the command has already timed out or @@ -1102,7 +1096,7 @@ MODULE_PARM_DESC(scsi_logging_level, "a bit mask of logging levels"); static int __init init_scsi(void) { - int error, i; + int error; error = scsi_init_queue(); if (error) @@ -1123,9 +1117,6 @@ static int __init init_scsi(void) if (error) goto cleanup_sysctl; - for_each_possible_cpu(i) - INIT_LIST_HEAD(&per_cpu(scsi_done_q, i)); - scsi_netlink_init(); printk(KERN_NOTICE "SCSI subsystem initialized\n"); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index d6743b959a7..71084728eb4 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -82,7 +82,7 @@ static void scsi_unprep_request(struct request *req) { struct scsi_cmnd *cmd = req->special; - req->flags &= ~REQ_DONTPREP; + req->cmd_flags &= ~REQ_DONTPREP; req->special = NULL; scsi_put_command(cmd); @@ -196,7 +196,8 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, req->sense_len = 0; req->retries = retries; req->timeout = timeout; - req->flags |= flags | REQ_BLOCK_PC | REQ_SPECIAL | REQ_QUIET; + req->cmd_type = REQ_TYPE_BLOCK_PC; + req->cmd_flags |= flags | REQ_QUIET | REQ_PREEMPT; /* * head injection *required* here otherwise quiesce won't work @@ -397,7 +398,8 @@ int scsi_execute_async(struct scsi_device *sdev, const unsigned char *cmd, req = blk_get_request(sdev->request_queue, write, gfp); if (!req) goto free_sense; - req->flags |= REQ_BLOCK_PC | REQ_QUIET; + req->cmd_type = REQ_TYPE_BLOCK_PC; + req->cmd_flags |= REQ_QUIET; if (use_sg) err = scsi_req_map_sg(req, buffer, use_sg, bufflen, gfp); @@ -933,7 +935,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) break; } } - if (!(req->flags & REQ_QUIET)) { + if (!(req->cmd_flags & REQ_QUIET)) { scmd_printk(KERN_INFO, cmd, "Device not ready: "); scsi_print_sense_hdr("", &sshdr); @@ -941,7 +943,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) scsi_end_request(cmd, 0, this_count, 1); return; case VOLUME_OVERFLOW: - if (!(req->flags & REQ_QUIET)) { + if (!(req->cmd_flags & REQ_QUIET)) { scmd_printk(KERN_INFO, cmd, "Volume overflow, CDB: "); __scsi_print_command(cmd->cmnd); @@ -963,7 +965,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) return; } if (result) { - if (!(req->flags & REQ_QUIET)) { + if (!(req->cmd_flags & REQ_QUIET)) { scmd_printk(KERN_INFO, cmd, "SCSI error: return code = 0x%08x\n", result); @@ -995,7 +997,7 @@ static int scsi_init_io(struct scsi_cmnd *cmd) /* * if this is a rq->data based REQ_BLOCK_PC, setup for a non-sg xfer */ - if ((req->flags & REQ_BLOCK_PC) && !req->bio) { + if (blk_pc_request(req) && !req->bio) { cmd->request_bufflen = req->data_len; cmd->request_buffer = req->data; req->buffer = req->data; @@ -1139,13 +1141,12 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) * these two cases differently. We differentiate by looking * at request->cmd, as this tells us the real story. */ - if (req->flags & REQ_SPECIAL && req->special) { + if (blk_special_request(req) && req->special) cmd = req->special; - } else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) { - - if(unlikely(specials_only) && !(req->flags & REQ_SPECIAL)) { - if(specials_only == SDEV_QUIESCE || - specials_only == SDEV_BLOCK) + else if (blk_pc_request(req) || blk_fs_request(req)) { + if (unlikely(specials_only) && !(req->cmd_flags & REQ_PREEMPT)){ + if (specials_only == SDEV_QUIESCE || + specials_only == SDEV_BLOCK) goto defer; sdev_printk(KERN_ERR, sdev, @@ -1153,7 +1154,6 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) goto kill; } - /* * Now try and find a command block that we can use. */ @@ -1184,7 +1184,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) * lock. We hope REQ_STARTED prevents anything untoward from * happening now. */ - if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) { + if (blk_fs_request(req) || blk_pc_request(req)) { int ret; /* @@ -1216,7 +1216,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) /* * Initialize the actual SCSI command for this request. */ - if (req->flags & REQ_BLOCK_PC) { + if (blk_pc_request(req)) { scsi_setup_blk_pc_cmnd(cmd); } else if (req->rq_disk) { struct scsi_driver *drv; @@ -1233,7 +1233,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) /* * The request is now prepped, no need to come back here */ - req->flags |= REQ_DONTPREP; + req->cmd_flags |= REQ_DONTPREP; return BLKPREP_OK; defer: @@ -1454,8 +1454,9 @@ static void scsi_request_fn(struct request_queue *q) if (unlikely(cmd == NULL)) { printk(KERN_CRIT "impossible request in %s.\n" "please mail a stack trace to " - "linux-scsi@vger.kernel.org", + "linux-scsi@vger.kernel.org\n", __FUNCTION__); + blk_dump_rq_flags(req, "foo"); BUG(); } spin_lock(shost->host_lock); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 638cff41d43..10bc99c911f 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -443,8 +443,7 @@ static int sd_init_command(struct scsi_cmnd * SCpnt) SCpnt->cmnd[0] = READ_6; SCpnt->sc_data_direction = DMA_FROM_DEVICE; } else { - printk(KERN_ERR "sd: Unknown command %lx\n", rq->flags); -/* overkill panic("Unknown sd command %lx\n", rq->flags); */ + printk(KERN_ERR "sd: Unknown command %x\n", rq->cmd_flags); return 0; } @@ -840,7 +839,7 @@ static int sd_issue_flush(struct device *dev, sector_t *error_sector) static void sd_prepare_flush(request_queue_t *q, struct request *rq) { memset(rq->cmd, 0, sizeof(rq->cmd)); - rq->flags |= REQ_BLOCK_PC; + rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->timeout = SD_TIMEOUT; rq->cmd[0] = SYNCHRONIZE_CACHE; rq->cmd_len = 10; diff --git a/drivers/scsi/sun3_NCR5380.c b/drivers/scsi/sun3_NCR5380.c index 2f8073b73bf..7f9bcef6adf 100644 --- a/drivers/scsi/sun3_NCR5380.c +++ b/drivers/scsi/sun3_NCR5380.c @@ -2017,7 +2017,7 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done != cmd)) { - if(cmd->request->flags & REQ_CMD) { + if(blk_fs_request(cmd->request)) { sun3scsi_dma_setup(d, count, rq_data_dir(cmd->request)); sun3_dma_setup_done = cmd; diff --git a/drivers/scsi/sun3_scsi.c b/drivers/scsi/sun3_scsi.c index 837173415d4..44a99aeb818 100644 --- a/drivers/scsi/sun3_scsi.c +++ b/drivers/scsi/sun3_scsi.c @@ -524,7 +524,7 @@ static inline unsigned long sun3scsi_dma_residual(struct Scsi_Host *instance) static inline unsigned long sun3scsi_dma_xfer_len(unsigned long wanted, Scsi_Cmnd *cmd, int write_flag) { - if(cmd->request->flags & REQ_CMD) + if(blk_fs_request(cmd->request)) return wanted; else return 0; diff --git a/drivers/scsi/sun3_scsi_vme.c b/drivers/scsi/sun3_scsi_vme.c index 008a82ab852..f5742b84b27 100644 --- a/drivers/scsi/sun3_scsi_vme.c +++ b/drivers/scsi/sun3_scsi_vme.c @@ -458,7 +458,7 @@ static inline unsigned long sun3scsi_dma_residual(struct Scsi_Host *instance) static inline unsigned long sun3scsi_dma_xfer_len(unsigned long wanted, Scsi_Cmnd *cmd, int write_flag) { - if(cmd->request->flags & REQ_CMD) + if(blk_fs_request(cmd->request)) return wanted; else return 0; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 261eaa44295..d926272a40d 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -295,7 +295,7 @@ config SERIAL_AMBA_PL011_CONSOLE Even if you say Y here, the currently visible framebuffer console (/dev/tty0) will still be used as the system console by default, but you can alter that using a kernel command line option such as - "console=ttyAM0". (Try "man bootparam" or see the documentation of + "console=ttyAMA0". (Try "man bootparam" or see the documentation of your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) diff --git a/drivers/serial/at91_serial.c b/drivers/serial/at91_serial.c index 54c6b2adf7b..bf4bf103e5a 100644 --- a/drivers/serial/at91_serial.c +++ b/drivers/serial/at91_serial.c @@ -139,7 +139,7 @@ static void at91_set_mctrl(struct uart_port *port, u_int mctrl) * AT91RM9200 Errata #39: RTS0 is not internally connected to PA21. * We need to drive the pin manually. */ - if (port->mapbase == AT91_BASE_US0) { + if (port->mapbase == AT91RM9200_BASE_US0) { if (mctrl & TIOCM_RTS) at91_set_gpio_value(AT91_PIN_PA21, 0); else diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c index d34f336d53d..0da3ebfff82 100644 --- a/drivers/serial/sunzilog.c +++ b/drivers/serial/sunzilog.c @@ -1270,7 +1270,7 @@ static void __init sunzilog_register_serio(struct uart_sunzilog_port *up) } #endif -static void __init sunzilog_init_hw(struct uart_sunzilog_port *up) +static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up) { struct zilog_channel __iomem *channel; unsigned long flags; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index b1046324441..113e484c763 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -24,6 +24,7 @@ #include <linux/device.h> #include <linux/usb.h> +#include <linux/workqueue.h> #include "hcd.h" #include "usb.h" @@ -302,11 +303,11 @@ int usb_driver_claim_interface(struct usb_driver *driver, dev->driver = &driver->drvwrap.driver; usb_set_intfdata(iface, priv); - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); iface->condition = USB_INTERFACE_BOUND; mark_active(iface); iface->pm_usage_cnt = !(driver->supports_autosuspend); - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); /* if interface was already added, bind now; else let * the future device_add() bind it, bypassing probe() @@ -355,11 +356,11 @@ void usb_driver_release_interface(struct usb_driver *driver, dev->driver = NULL; usb_set_intfdata(iface, NULL); - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); iface->condition = USB_INTERFACE_UNBOUND; mark_quiesced(iface); iface->needs_remote_wakeup = 0; - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); } EXPORT_SYMBOL(usb_driver_release_interface); @@ -788,7 +789,7 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_deregister); #ifdef CONFIG_PM -/* Caller has locked udev->pm_mutex */ +/* Caller has locked udev's pm_mutex */ static int suspend_device(struct usb_device *udev, pm_message_t msg) { struct usb_device_driver *udriver; @@ -815,7 +816,7 @@ done: return status; } -/* Caller has locked udev->pm_mutex */ +/* Caller has locked udev's pm_mutex */ static int resume_device(struct usb_device *udev) { struct usb_device_driver *udriver; @@ -841,7 +842,7 @@ done: return status; } -/* Caller has locked intf's usb_device's pm_mutex */ +/* Caller has locked intf's usb_device's pm mutex */ static int suspend_interface(struct usb_interface *intf, pm_message_t msg) { struct usb_driver *driver; @@ -1063,7 +1064,7 @@ int usb_resume_both(struct usb_device *udev) /* Propagate the resume up the tree, if necessary */ if (udev->state == USB_STATE_SUSPENDED) { if (parent) { - mutex_lock_nested(&parent->pm_mutex, parent->level); + usb_pm_lock(parent); parent->auto_pm = 1; status = usb_resume_both(parent); } else { @@ -1078,7 +1079,7 @@ int usb_resume_both(struct usb_device *udev) if (status == 0) status = resume_device(udev); if (parent) - mutex_unlock(&parent->pm_mutex); + usb_pm_unlock(parent); } else { /* Needed only for setting udev->dev.power.power_state.event @@ -1103,8 +1104,8 @@ int usb_resume_both(struct usb_device *udev) /** * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces - * @udev - the usb_device to autosuspend - * @dec_usage_cnt - flag to decrement @udev's PM-usage counter + * @udev: the usb_device to autosuspend + * @dec_usage_cnt: flag to decrement @udev's PM-usage counter * * This routine should be called when a core subsystem is finished using * @udev and wants to allow it to autosuspend. Examples would be when @@ -1128,20 +1129,20 @@ int usb_resume_both(struct usb_device *udev) */ void usb_autosuspend_device(struct usb_device *udev, int dec_usage_cnt) { - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); udev->pm_usage_cnt -= dec_usage_cnt; if (udev->pm_usage_cnt <= 0) - schedule_delayed_work(&udev->autosuspend, + queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, USB_AUTOSUSPEND_DELAY); - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); // dev_dbg(&udev->dev, "%s: cnt %d\n", // __FUNCTION__, udev->pm_usage_cnt); } /** * usb_autoresume_device - immediately autoresume a USB device and its interfaces - * @udev - the usb_device to autoresume - * @inc_usage_cnt - flag to increment @udev's PM-usage counter + * @udev: the usb_device to autoresume + * @inc_usage_cnt: flag to increment @udev's PM-usage counter * * This routine should be called when a core subsystem wants to use @udev * and needs to guarantee that it is not suspended. In addition, the @@ -1167,13 +1168,13 @@ int usb_autoresume_device(struct usb_device *udev, int inc_usage_cnt) { int status; - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); udev->pm_usage_cnt += inc_usage_cnt; udev->auto_pm = 1; status = usb_resume_both(udev); if (status != 0) udev->pm_usage_cnt -= inc_usage_cnt; - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); // dev_dbg(&udev->dev, "%s: status %d cnt %d\n", // __FUNCTION__, status, udev->pm_usage_cnt); return status; @@ -1181,7 +1182,7 @@ int usb_autoresume_device(struct usb_device *udev, int inc_usage_cnt) /** * usb_autopm_put_interface - decrement a USB interface's PM-usage counter - * @intf - the usb_interface whose counter should be decremented + * @intf: the usb_interface whose counter should be decremented * * This routine should be called by an interface driver when it is * finished using @intf and wants to allow it to autosuspend. A typical @@ -1214,13 +1215,13 @@ void usb_autopm_put_interface(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); - mutex_lock_nested(&udev->pm_mutex, udev->level); - if (intf->condition != USB_INTERFACE_UNBOUND) { - if (--intf->pm_usage_cnt <= 0) - schedule_delayed_work(&udev->autosuspend, - USB_AUTOSUSPEND_DELAY); + usb_pm_lock(udev); + if (intf->condition != USB_INTERFACE_UNBOUND && + --intf->pm_usage_cnt <= 0) { + queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, + USB_AUTOSUSPEND_DELAY); } - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); // dev_dbg(&intf->dev, "%s: cnt %d\n", // __FUNCTION__, intf->pm_usage_cnt); } @@ -1228,7 +1229,7 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface); /** * usb_autopm_get_interface - increment a USB interface's PM-usage counter - * @intf - the usb_interface whose counter should be incremented + * @intf: the usb_interface whose counter should be incremented * * This routine should be called by an interface driver when it wants to * use @intf and needs to guarantee that it is not suspended. In addition, @@ -1262,7 +1263,7 @@ int usb_autopm_get_interface(struct usb_interface *intf) struct usb_device *udev = interface_to_usbdev(intf); int status; - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); if (intf->condition == USB_INTERFACE_UNBOUND) status = -ENODEV; else { @@ -1272,7 +1273,7 @@ int usb_autopm_get_interface(struct usb_interface *intf) if (status != 0) --intf->pm_usage_cnt; } - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", // __FUNCTION__, status, intf->pm_usage_cnt); return status; @@ -1288,10 +1289,10 @@ static int usb_suspend(struct device *dev, pm_message_t message) if (is_usb_device(dev)) { struct usb_device *udev = to_usb_device(dev); - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); udev->auto_pm = 0; status = usb_suspend_both(udev, message); - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); } else status = 0; return status; @@ -1304,10 +1305,10 @@ static int usb_resume(struct device *dev) if (is_usb_device(dev)) { struct usb_device *udev = to_usb_device(dev); - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); udev->auto_pm = 0; status = usb_resume_both(udev); - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); /* Rebind drivers that had no suspend method? */ } else diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index e86f6295708..37f9f5e7425 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -345,7 +345,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) struct usb_ctrlrequest *cmd; u16 typeReq, wValue, wIndex, wLength; u8 *ubuf = urb->transfer_buffer; - u8 tbuf [sizeof (struct usb_hub_descriptor)]; + u8 tbuf [sizeof (struct usb_hub_descriptor)] + __attribute__((aligned(4))); const u8 *bufp = tbuf; int len = 0; int patch_wakeup = 0; @@ -835,8 +836,7 @@ void usb_enable_root_hub_irq (struct usb_bus *bus) struct usb_hcd *hcd; hcd = container_of (bus, struct usb_hcd, self); - if (hcd->driver->hub_irq_enable && !hcd->poll_rh && - hcd->state != HC_STATE_HALT) + if (hcd->driver->hub_irq_enable && hcd->state != HC_STATE_HALT) hcd->driver->hub_irq_enable (hcd); } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 2a8cb3c2b19..7676690a038 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1779,7 +1779,7 @@ static int remote_wakeup(struct usb_device *udev) * to the parent hub! */ usb_lock_device(udev); - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); if (udev->state == USB_STATE_SUSPENDED) { dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); /* TRSMRCY = 10 msec */ @@ -1788,7 +1788,7 @@ static int remote_wakeup(struct usb_device *udev) if (status == 0) udev->dev.power.power_state.event = PM_EVENT_ON; } - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); if (status == 0) usb_autoresume_device(udev, 0); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 60ef4ef0101..e4df9edf1bc 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -33,6 +33,7 @@ #include <linux/smp_lock.h> #include <linux/usb.h> #include <linux/mutex.h> +#include <linux/workqueue.h> #include <asm/io.h> #include <asm/scatterlist.h> @@ -47,6 +48,8 @@ const char *usbcore_name = "usbcore"; static int nousb; /* Disable USB when built into kernel image */ +struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */ + /** * usb_ifnum_to_if - get the interface object with a given interface number @@ -170,9 +173,9 @@ static void usb_release_dev(struct device *dev) udev = to_usb_device(dev); -#ifdef CONFIG_PM +#ifdef CONFIG_USB_SUSPEND cancel_delayed_work(&udev->autosuspend); - flush_scheduled_work(); + flush_workqueue(ksuspend_usb_wq); #endif usb_destroy_configuration(udev); usb_put_hcd(bus_to_hcd(udev->bus)); @@ -184,17 +187,44 @@ static void usb_release_dev(struct device *dev) #ifdef CONFIG_PM +static int ksuspend_usb_init(void) +{ + ksuspend_usb_wq = create_singlethread_workqueue("ksuspend_usbd"); + if (!ksuspend_usb_wq) + return -ENOMEM; + return 0; +} + +static void ksuspend_usb_cleanup(void) +{ + destroy_workqueue(ksuspend_usb_wq); +} + +#else + +#define ksuspend_usb_init() 0 +#define ksuspend_usb_cleanup() do {} while (0) + +#endif + +#ifdef CONFIG_USB_SUSPEND + /* usb_autosuspend_work - callback routine to autosuspend a USB device */ static void usb_autosuspend_work(void *_udev) { struct usb_device *udev = _udev; - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); udev->auto_pm = 1; usb_suspend_both(udev, PMSG_SUSPEND); - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); } +#else + +static void usb_autosuspend_work(void *_udev) +{} + #endif /** @@ -976,9 +1006,12 @@ static int __init usb_init(void) return 0; } + retval = ksuspend_usb_init(); + if (retval) + goto out; retval = bus_register(&usb_bus_type); if (retval) - goto out; + goto bus_register_failed; retval = usb_host_init(); if (retval) goto host_init_failed; @@ -1014,6 +1047,8 @@ major_init_failed: usb_host_cleanup(); host_init_failed: bus_unregister(&usb_bus_type); +bus_register_failed: + ksuspend_usb_cleanup(); out: return retval; } @@ -1035,6 +1070,7 @@ static void __exit usb_exit(void) usb_hub_cleanup(); usb_host_cleanup(); bus_unregister(&usb_bus_type); + ksuspend_usb_cleanup(); } subsys_initcall(usb_init); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 0c09ecced6e..f69df137ec0 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -36,6 +36,16 @@ extern int usb_resume_both(struct usb_device *udev); extern int usb_port_suspend(struct usb_device *dev); extern int usb_port_resume(struct usb_device *dev); +static inline void usb_pm_lock(struct usb_device *udev) +{ + mutex_lock_nested(&udev->pm_mutex, udev->level); +} + +static inline void usb_pm_unlock(struct usb_device *udev) +{ + mutex_unlock(&udev->pm_mutex); +} + #else #define usb_suspend_both(udev, msg) 0 @@ -45,6 +55,8 @@ static inline int usb_resume_both(struct usb_device *udev) } #define usb_port_suspend(dev) 0 #define usb_port_resume(dev) 0 +static inline void usb_pm_lock(struct usb_device *udev) {} +static inline void usb_pm_unlock(struct usb_device *udev) {} #endif @@ -58,10 +70,15 @@ extern int usb_autoresume_device(struct usb_device *udev, int inc_busy_cnt); #else #define usb_autosuspend_device(udev, dec_busy_cnt) do {} while (0) -#define usb_autoresume_device(udev, inc_busy_cnt) 0 +static inline int usb_autoresume_device(struct usb_device *udev, + int inc_busy_cnt) +{ + return 0; +} #endif +extern struct workqueue_struct *ksuspend_usb_wq; extern struct bus_type usb_bus_type; extern struct usb_device_driver usb_generic_driver; diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index d00958a01cf..77beba485a8 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1658,7 +1658,7 @@ static int __devinit at91udc_probe(struct platform_device *pdev) return -ENODEV; } - if (!request_mem_region(AT91_BASE_UDP, SZ_16K, driver_name)) { + if (!request_mem_region(AT91RM9200_BASE_UDP, SZ_16K, driver_name)) { DBG("someone's using UDC memory\n"); return -EBUSY; } @@ -1720,7 +1720,7 @@ static int __devinit at91udc_probe(struct platform_device *pdev) fail1: device_unregister(&udc->gadget.dev); fail0: - release_mem_region(AT91_BASE_UDP, SZ_16K); + release_mem_region(AT91RM9200_BASE_UDP, SZ_16K); DBG("%s probe failed, %d\n", driver_name, retval); return retval; } @@ -1742,7 +1742,7 @@ static int __devexit at91udc_remove(struct platform_device *pdev) free_irq(udc->board.vbus_pin, udc); free_irq(udc->udp_irq, udc); device_unregister(&udc->gadget.dev); - release_mem_region(AT91_BASE_UDP, SZ_16K); + release_mem_region(AT91RM9200_BASE_UDP, SZ_16K); clk_put(udc->iclk); clk_put(udc->fclk); diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index fdab97a27c0..4d2946e540c 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -816,15 +816,14 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) dum->gadget.dev.driver = &driver->driver; dev_dbg (udc_dev(dum), "binding gadget driver '%s'\n", driver->driver.name); - if ((retval = driver->bind (&dum->gadget)) != 0) { - dum->driver = NULL; - dum->gadget.dev.driver = NULL; - return retval; - } + if ((retval = driver->bind (&dum->gadget)) != 0) + goto err_bind_gadget; driver->driver.bus = dum->gadget.dev.parent->bus; - driver_register (&driver->driver); - device_bind_driver (&dum->gadget.dev); + if ((retval = driver_register (&driver->driver)) != 0) + goto err_register; + if ((retval = device_bind_driver (&dum->gadget.dev)) != 0) + goto err_bind_driver; /* khubd will enumerate this in a while */ spin_lock_irq (&dum->lock); @@ -834,6 +833,19 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) usb_hcd_poll_rh_status (dummy_to_hcd (dum)); return 0; + +err_bind_driver: + driver_unregister (&driver->driver); +err_register: + driver->unbind (&dum->gadget); + spin_lock_irq (&dum->lock); + dum->pullup = 0; + set_link_state (dum); + spin_unlock_irq (&dum->lock); +err_bind_gadget: + dum->driver = NULL; + dum->gadget.dev.driver = NULL; + return retval; } EXPORT_SYMBOL (usb_gadget_register_driver); @@ -916,7 +928,9 @@ static int dummy_udc_probe (struct platform_device *pdev) usb_get_hcd (dummy_to_hcd (dum)); platform_set_drvdata (pdev, dum); - device_create_file (&dum->gadget.dev, &dev_attr_function); + rc = device_create_file (&dum->gadget.dev, &dev_attr_function); + if (rc < 0) + device_unregister (&dum->gadget.dev); return rc; } @@ -1864,8 +1878,7 @@ static int dummy_start (struct usb_hcd *hcd) #endif /* FIXME 'urbs' should be a per-device thing, maybe in usbcore */ - device_create_file (dummy_dev(dum), &dev_attr_urbs); - return 0; + return device_create_file (dummy_dev(dum), &dev_attr_urbs); } static void dummy_stop (struct usb_hcd *hcd) diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 8d7f1e84cd7..c83d3b6c68f 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -567,6 +567,7 @@ struct lun { unsigned int ro : 1; unsigned int prevent_medium_removal : 1; unsigned int registered : 1; + unsigned int info_valid : 1; u32 sense_data; u32 sense_data_info; @@ -1656,6 +1657,7 @@ static int do_read(struct fsg_dev *fsg) curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; bh->inreq->length = 0; bh->state = BUF_STATE_FULL; break; @@ -1691,6 +1693,7 @@ static int do_read(struct fsg_dev *fsg) if (nread < amount) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; break; } @@ -1785,6 +1788,7 @@ static int do_write(struct fsg_dev *fsg) curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; curlun->sense_data_info = usb_offset >> 9; + curlun->info_valid = 1; continue; } amount -= (amount & 511); @@ -1827,6 +1831,7 @@ static int do_write(struct fsg_dev *fsg) if (bh->outreq->status != 0) { curlun->sense_data = SS_COMMUNICATION_FAILURE; curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; break; } @@ -1868,6 +1873,7 @@ static int do_write(struct fsg_dev *fsg) if (nwritten < amount) { curlun->sense_data = SS_WRITE_ERROR; curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; break; } @@ -2010,6 +2016,7 @@ static int do_verify(struct fsg_dev *fsg) curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; break; } @@ -2036,6 +2043,7 @@ static int do_verify(struct fsg_dev *fsg) if (nread == 0) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; break; } file_offset += nread; @@ -2079,6 +2087,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) struct lun *curlun = fsg->curlun; u8 *buf = (u8 *) bh->buf; u32 sd, sdinfo; + int valid; /* * From the SCSI-2 spec., section 7.9 (Unit attention condition): @@ -2106,15 +2115,18 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) fsg->bad_lun_okay = 1; sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; sdinfo = 0; + valid = 0; } else { sd = curlun->sense_data; sdinfo = curlun->sense_data_info; + valid = curlun->info_valid << 7; curlun->sense_data = SS_NO_SENSE; curlun->sense_data_info = 0; + curlun->info_valid = 0; } memset(buf, 0, 18); - buf[0] = 0x80 | 0x70; // Valid, current error + buf[0] = valid | 0x70; // Valid, current error buf[2] = SK(sd); put_be32(&buf[3], sdinfo); // Sense information buf[7] = 18 - 8; // Additional sense length @@ -2703,6 +2715,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, if (fsg->cmnd[0] != SC_REQUEST_SENSE) { curlun->sense_data = SS_NO_SENSE; curlun->sense_data_info = 0; + curlun->info_valid = 0; } } else { fsg->curlun = curlun = NULL; @@ -3332,6 +3345,7 @@ static void handle_exception(struct fsg_dev *fsg) curlun->sense_data = curlun->unit_attention_data = SS_NO_SENSE; curlun->sense_data_info = 0; + curlun->info_valid = 0; } fsg->state = FSG_STATE_IDLE; } @@ -3873,21 +3887,26 @@ static int __init fsg_bind(struct usb_gadget *gadget) for (i = 0; i < fsg->nluns; ++i) { curlun = &fsg->luns[i]; curlun->ro = mod_data.ro[i]; + curlun->dev.release = lun_release; curlun->dev.parent = &gadget->dev; curlun->dev.driver = &fsg_driver.driver; dev_set_drvdata(&curlun->dev, fsg); snprintf(curlun->dev.bus_id, BUS_ID_SIZE, "%s-lun%d", gadget->dev.bus_id, i); - if ((rc = device_register(&curlun->dev)) != 0) + if ((rc = device_register(&curlun->dev)) != 0) { INFO(fsg, "failed to register LUN%d: %d\n", i, rc); - else { - curlun->registered = 1; - curlun->dev.release = lun_release; - device_create_file(&curlun->dev, &dev_attr_ro); - device_create_file(&curlun->dev, &dev_attr_file); - kref_get(&fsg->ref); + goto out; + } + if ((rc = device_create_file(&curlun->dev, + &dev_attr_ro)) != 0 || + (rc = device_create_file(&curlun->dev, + &dev_attr_file)) != 0) { + device_unregister(&curlun->dev); + goto out; } + curlun->registered = 1; + kref_get(&fsg->ref); if (mod_data.file[i] && *mod_data.file[i]) { if ((rc = open_backing_file(curlun, diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 1027aa04583..d1d68c40225 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -715,17 +715,8 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) return IRQ_NOTMINE; } - /* NOTE: vendors didn't always make the same implementation - * choices for RHSC. Sometimes it triggers on an edge (like - * setting and maybe clearing a port status change bit); and - * it's level-triggered on other silicon, active until khubd - * clears all active port status change bits. Poll by timer - * til it's fully debounced and the difference won't matter. - */ if (ints & OHCI_INTR_RHSC) { ohci_vdbg (ohci, "rhsc\n"); - ohci_writel (ohci, OHCI_INTR_RHSC, ®s->intrdisable); - hcd->poll_rh = 1; ohci->next_statechange = jiffies + STATECHANGE_DELAY; ohci_writel (ohci, OHCI_INTR_RHSC, ®s->intrstatus); usb_hcd_poll_rh_status(hcd); @@ -743,13 +734,18 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) if (ints & OHCI_INTR_RD) { ohci_vdbg (ohci, "resume detect\n"); ohci_writel (ohci, OHCI_INTR_RD, ®s->intrstatus); - if (hcd->state != HC_STATE_QUIESCING) + hcd->poll_rh = 1; + if (ohci->autostop) { + spin_lock (&ohci->lock); + ohci_rh_resume (ohci); + spin_unlock (&ohci->lock); + } else usb_hcd_resume_root_hub(hcd); } if (ints & OHCI_INTR_WDH) { if (HC_IS_RUNNING(hcd->state)) - ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrdisable); + ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrdisable); spin_lock (&ohci->lock); dl_done_list (ohci, ptregs); spin_unlock (&ohci->lock); diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 0b899339cac..ec75774abea 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -44,27 +44,17 @@ static void ohci_rhsc_enable (struct usb_hcd *hcd) ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); } -#ifdef CONFIG_PM - #define OHCI_SCHED_ENABLES \ (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) static void dl_done_list (struct ohci_hcd *, struct pt_regs *); static void finish_unlinks (struct ohci_hcd *, u16 , struct pt_regs *); -static int ohci_restart (struct ohci_hcd *ohci); -static int ohci_bus_suspend (struct usb_hcd *hcd) +static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop) +__releases(ohci->lock) +__acquires(ohci->lock) { - struct ohci_hcd *ohci = hcd_to_ohci (hcd); int status = 0; - unsigned long flags; - - spin_lock_irqsave (&ohci->lock, flags); - - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { - spin_unlock_irqrestore (&ohci->lock, flags); - return -ESHUTDOWN; - } ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); switch (ohci->hc_control & OHCI_CTRL_HCFS) { @@ -80,15 +70,16 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) ohci_dbg (ohci, "needs reinit!\n"); goto done; case OHCI_USB_SUSPEND: - ohci_dbg (ohci, "already suspended\n"); - goto done; + if (!ohci->autostop) { + ohci_dbg (ohci, "already suspended\n"); + goto done; + } } - ohci_dbg (ohci, "suspend root hub\n"); + ohci_dbg (ohci, "%s root hub\n", + autostop ? "auto-stop" : "suspend"); /* First stop any processing */ - if (ohci->hc_control & OHCI_SCHED_ENABLES) { - int limit; - + if (!autostop && (ohci->hc_control & OHCI_SCHED_ENABLES)) { ohci->hc_control &= ~OHCI_SCHED_ENABLES; ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); @@ -98,27 +89,22 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) * then the last WDH could take 6+ msec */ ohci_dbg (ohci, "stopping schedules ...\n"); - limit = 2000; - while (limit > 0) { - udelay (250); - limit =- 250; - if (ohci_readl (ohci, &ohci->regs->intrstatus) - & OHCI_INTR_SF) - break; - } - dl_done_list (ohci, NULL); - mdelay (7); + ohci->autostop = 0; + spin_unlock_irq (&ohci->lock); + msleep (8); + spin_lock_irq (&ohci->lock); } dl_done_list (ohci, NULL); finish_unlinks (ohci, ohci_frame_no(ohci), NULL); - ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus), - &ohci->regs->intrstatus); /* maybe resume can wake root hub */ - if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev)) + if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev) || + autostop) ohci->hc_control |= OHCI_CTRL_RWE; - else + else { + ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable); ohci->hc_control &= ~OHCI_CTRL_RWE; + } /* Suspend hub ... this is the "global (to this bus) suspend" mode, * which doesn't imply ports will first be individually suspended. @@ -129,13 +115,12 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) (void) ohci_readl (ohci, &ohci->regs->control); /* no resumes until devices finish suspending */ - ohci->next_statechange = jiffies + msecs_to_jiffies (5); - - /* no timer polling */ - hcd->poll_rh = 0; + if (!autostop) { + ohci->next_statechange = jiffies + msecs_to_jiffies (5); + ohci->autostop = 0; + } done: - spin_unlock_irqrestore (&ohci->lock, flags); return status; } @@ -147,25 +132,19 @@ static inline struct ed *find_head (struct ed *ed) return ed; } +static int ohci_restart (struct ohci_hcd *ohci); + /* caller has locked the root hub */ -static int ohci_bus_resume (struct usb_hcd *hcd) +static int ohci_rh_resume (struct ohci_hcd *ohci) +__releases(ohci->lock) +__acquires(ohci->lock) { - struct ohci_hcd *ohci = hcd_to_ohci (hcd); + struct usb_hcd *hcd = ohci_to_hcd (ohci); u32 temp, enables; int status = -EINPROGRESS; - unsigned long flags; - - if (time_before (jiffies, ohci->next_statechange)) - msleep(5); - - spin_lock_irqsave (&ohci->lock, flags); - - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { - spin_unlock_irqrestore (&ohci->lock, flags); - return -ESHUTDOWN; - } - + int autostopped = ohci->autostop; + ohci->autostop = 0; ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { @@ -185,7 +164,8 @@ static int ohci_bus_resume (struct usb_hcd *hcd) ohci->hc_control |= OHCI_USB_RESUME; ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); (void) ohci_readl (ohci, &ohci->regs->control); - ohci_dbg (ohci, "resume root hub\n"); + ohci_dbg (ohci, "%s root hub\n", + autostopped ? "auto-start" : "resume"); break; case OHCI_USB_RESUME: /* HCFS changes sometime after INTR_RD */ @@ -200,16 +180,24 @@ static int ohci_bus_resume (struct usb_hcd *hcd) ohci_dbg (ohci, "lost power\n"); status = -EBUSY; } - spin_unlock_irqrestore (&ohci->lock, flags); +#ifdef CONFIG_PM if (status == -EBUSY) { - (void) ohci_init (ohci); - return ohci_restart (ohci); + if (!autostopped) { + spin_unlock_irq (&ohci->lock); + (void) ohci_init (ohci); + status = ohci_restart (ohci); + spin_lock_irq (&ohci->lock); + } + return status; } +#endif if (status != -EINPROGRESS) return status; + if (autostopped) + goto skip_resume; + spin_unlock_irq (&ohci->lock); temp = ohci->num_ports; - enables = 0; while (temp--) { u32 stat = ohci_readl (ohci, &ohci->regs->roothub.portstatus [temp]); @@ -242,17 +230,21 @@ static int ohci_bus_resume (struct usb_hcd *hcd) /* Sometimes PCI D3 suspend trashes frame timings ... */ periodic_reinit (ohci); + /* the following code is executed with ohci->lock held and + * irqs disabled if and only if autostopped is true + */ + +skip_resume: /* interrupts might have been disabled */ ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable); if (ohci->ed_rm_list) ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable); - ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus), - &ohci->regs->intrstatus); /* Then re-enable operations */ ohci_writel (ohci, OHCI_USB_OPER, &ohci->regs->control); (void) ohci_readl (ohci, &ohci->regs->control); - msleep (3); + if (!autostopped) + msleep (3); temp = ohci->hc_control; temp &= OHCI_CTRL_RWC; @@ -262,7 +254,11 @@ static int ohci_bus_resume (struct usb_hcd *hcd) (void) ohci_readl (ohci, &ohci->regs->control); /* TRSMRCY */ - msleep (10); + if (!autostopped) { + msleep (10); + spin_lock_irq (&ohci->lock); + } + /* now ohci->lock is always held and irqs are always disabled */ /* keep it alive for more than ~5x suspend + resume costs */ ohci->next_statechange = jiffies + STATECHANGE_DELAY; @@ -299,6 +295,45 @@ static int ohci_bus_resume (struct usb_hcd *hcd) return 0; } +#ifdef CONFIG_PM + +static int ohci_bus_suspend (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int rc; + + spin_lock_irq (&ohci->lock); + + if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) + rc = -ESHUTDOWN; + else + rc = ohci_rh_suspend (ohci, 0); + spin_unlock_irq (&ohci->lock); + return rc; +} + +static int ohci_bus_resume (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int rc; + + if (time_before (jiffies, ohci->next_statechange)) + msleep(5); + + spin_lock_irq (&ohci->lock); + + if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) + rc = -ESHUTDOWN; + else + rc = ohci_rh_resume (ohci); + spin_unlock_irq (&ohci->lock); + + /* poll until we know a device is connected or we autostop */ + if (rc == 0) + usb_hcd_poll_rh_status(hcd); + return rc; +} + #endif /* CONFIG_PM */ /*-------------------------------------------------------------------------*/ @@ -310,21 +345,11 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); int i, changed = 0, length = 1; - int can_suspend; + int any_connected = 0, rhsc_enabled = 1; unsigned long flags; - can_suspend = device_may_wakeup(&hcd->self.root_hub->dev); spin_lock_irqsave (&ohci->lock, flags); - /* handle autosuspended root: finish resuming before - * letting khubd or root hub timer see state changes. - */ - if (unlikely((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER - || !HC_IS_RUNNING(hcd->state))) { - can_suspend = 0; - goto done; - } - /* undocumented erratum seen on at least rev D */ if ((ohci->flags & OHCI_QUIRK_AMD756) && (roothub_a (ohci) & RH_A_NDP) > MAX_ROOT_PORTS) { @@ -348,9 +373,8 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) for (i = 0; i < ohci->num_ports; i++) { u32 status = roothub_portstatus (ohci, i); - /* can't autosuspend with active ports */ - if ((status & RH_PS_PES) && !(status & RH_PS_PSS)) - can_suspend = 0; + /* can't autostop if ports are connected */ + any_connected |= (status & RH_PS_CCS); if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC | RH_PS_PRSC)) { @@ -359,49 +383,73 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) buf [0] |= 1 << (i + 1); else buf [1] |= 1 << (i - 7); - continue; } } - /* after root hub changes, stop polling after debouncing - * for a while and maybe kicking in autosuspend + /* NOTE: vendors didn't always make the same implementation + * choices for RHSC. Sometimes it triggers on an edge (like + * setting and maybe clearing a port status change bit); and + * it's level-triggered on other silicon, active until khubd + * clears all active port status change bits. If it's still + * set (level-triggered) we must disable it and rely on + * polling until khubd re-enables it. */ - if (changed) { - ohci->next_statechange = jiffies + STATECHANGE_DELAY; - can_suspend = 0; - } else if (time_before (jiffies, ohci->next_statechange)) { - can_suspend = 0; - } else { -#ifdef CONFIG_PM - can_suspend = can_suspend - && !ohci->ed_rm_list - && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES) - & ohci->hc_control) - == OHCI_USB_OPER; -#endif - if (hcd->uses_new_polling) { + if (ohci_readl (ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC) { + ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable); + (void) ohci_readl (ohci, &ohci->regs->intrdisable); + rhsc_enabled = 0; + } + hcd->poll_rh = 1; + + /* carry out appropriate state changes */ + switch (ohci->hc_control & OHCI_CTRL_HCFS) { + + case OHCI_USB_OPER: + /* keep on polling until we know a device is connected + * and RHSC is enabled */ + if (!ohci->autostop) { + if (any_connected) { + if (rhsc_enabled) + hcd->poll_rh = 0; + } else { + ohci->autostop = 1; + ohci->next_statechange = jiffies + HZ; + } + + /* if no devices have been attached for one second, autostop */ + } else { + if (changed || any_connected) { + ohci->autostop = 0; + ohci->next_statechange = jiffies + + STATECHANGE_DELAY; + } else if (time_after_eq (jiffies, + ohci->next_statechange) + && !ohci->ed_rm_list + && !(ohci->hc_control & + OHCI_SCHED_ENABLES)) { + ohci_rh_suspend (ohci, 1); + } + } + break; + + /* if there is a port change, autostart or ask to be resumed */ + case OHCI_USB_SUSPEND: + case OHCI_USB_RESUME: + if (changed) { + if (ohci->autostop) + ohci_rh_resume (ohci); + else + usb_hcd_resume_root_hub (hcd); + } else { + /* everything is idle, no need for polling */ hcd->poll_rh = 0; - /* use INTR_RHSC iff INTR_RD won't apply */ - if (!can_suspend) - ohci_writel (ohci, OHCI_INTR_RHSC, - &ohci->regs->intrenable); } + break; } done: spin_unlock_irqrestore (&ohci->lock, flags); -#ifdef CONFIG_PM - /* save power by autosuspending idle root hubs; - * INTR_RD wakes us when there's work - */ - if (can_suspend && usb_trylock_device (hcd->self.root_hub) == 0) { - ohci_vdbg (ohci, "autosuspend\n"); - (void) ohci_bus_suspend (hcd); - usb_unlock_device (hcd->self.root_hub); - } -#endif - return changed ? length : 0; } @@ -572,9 +620,6 @@ static int ohci_hub_control ( break; case USB_PORT_FEAT_SUSPEND: temp = RH_PS_POCI; - if ((ohci->hc_control & OHCI_CTRL_HCFS) - != OHCI_USB_OPER) - usb_hcd_resume_root_hub(hcd); break; case USB_PORT_FEAT_C_SUSPEND: temp = RH_PS_PSSC; diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 3732db7d68e..87441855278 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -73,13 +73,14 @@ ohci_pci_start (struct usb_hcd *hcd) else if (pdev->vendor == PCI_VENDOR_ID_NS) { struct pci_dev *b; - b = pci_find_slot (pdev->bus->number, + b = pci_get_slot (pdev->bus, PCI_DEVFN (PCI_SLOT (pdev->devfn), 1)); if (b && b->device == PCI_DEVICE_ID_NS_87560_LIO && b->vendor == PCI_VENDOR_ID_NS) { ohci->flags |= OHCI_QUIRK_SUPERIO; ohci_dbg (ohci, "Using NSC SuperIO setup\n"); } + pci_dev_put(b); } /* Check for Compaq's ZFMicro chipset, which needs short diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 93fdc3c3534..a2f42a2f47c 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -388,6 +388,7 @@ struct ohci_hcd { u32 hc_control; /* copy of hc control reg */ unsigned long next_statechange; /* suspend/resume */ u32 fminterval; /* saved register */ + unsigned autostop:1; /* rh auto stopping/stopped */ unsigned long flags; /* for HC bugs */ #define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index b2bafc37c41..5f861331932 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -225,7 +225,7 @@ static inline void mts_debug_dump(struct mts_desc* desc) { } -static inline void mts_show_command(Scsi_Cmnd *srb) +static inline void mts_show_command(struct scsi_cmnd *srb) { char *what = NULL; @@ -309,7 +309,7 @@ static inline void mts_show_command(Scsi_Cmnd *srb) #else -static inline void mts_show_command(Scsi_Cmnd * dummy) +static inline void mts_show_command(struct scsi_cmnd * dummy) { } @@ -338,7 +338,7 @@ static int mts_slave_configure (struct scsi_device *s) return 0; } -static int mts_scsi_abort (Scsi_Cmnd *srb) +static int mts_scsi_abort(struct scsi_cmnd *srb) { struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); @@ -349,7 +349,7 @@ static int mts_scsi_abort (Scsi_Cmnd *srb) return FAILED; } -static int mts_scsi_host_reset (Scsi_Cmnd *srb) +static int mts_scsi_host_reset(struct scsi_cmnd *srb) { struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); int result, rc; @@ -366,8 +366,8 @@ static int mts_scsi_host_reset (Scsi_Cmnd *srb) return result ? FAILED : SUCCESS; } -static -int mts_scsi_queuecommand (Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback ); +static int +mts_scsi_queuecommand(struct scsi_cmnd *srb, mts_scsi_cmnd_callback callback); static void mts_transfer_cleanup( struct urb *transfer ); static void mts_do_sg(struct urb * transfer, struct pt_regs *regs); @@ -537,7 +537,7 @@ static const unsigned char mts_direction[256/8] = { #define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1) static void -mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc ) +mts_build_transfer_context(struct scsi_cmnd *srb, struct mts_desc* desc) { int pipe; struct scatterlist * sg; @@ -588,8 +588,8 @@ mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc ) } -static -int mts_scsi_queuecommand( Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback ) +static int +mts_scsi_queuecommand(struct scsi_cmnd *srb, mts_scsi_cmnd_callback callback) { struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); int err = 0; diff --git a/drivers/usb/image/microtek.h b/drivers/usb/image/microtek.h index 926d4bdc674..d5d62a93905 100644 --- a/drivers/usb/image/microtek.h +++ b/drivers/usb/image/microtek.h @@ -8,14 +8,14 @@ * */ -typedef void (*mts_scsi_cmnd_callback)(Scsi_Cmnd *); +typedef void (*mts_scsi_cmnd_callback)(struct scsi_cmnd *); struct mts_transfer_context { struct mts_desc* instance; mts_scsi_cmnd_callback final_callback; - Scsi_Cmnd *srb; + struct scsi_cmnd *srb; void* data; unsigned data_length; diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 2a3e9e9b4b3..81b1ea01a17 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -1600,6 +1600,9 @@ void hid_init_reports(struct hid_device *hid) #define USB_VENDOR_ID_ALCOR 0x058f #define USB_DEVICE_ID_ALCOR_USBRS232 0x9720 +#define USB_VENDOR_ID_SUN 0x0430 +#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab + /* * Alphabetically sorted blacklist by quirk type. */ @@ -1729,6 +1732,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, diff --git a/drivers/usb/misc/phidgetkit.c b/drivers/usb/misc/phidgetkit.c index 9a8d137d39f..78e419904ab 100644 --- a/drivers/usb/misc/phidgetkit.c +++ b/drivers/usb/misc/phidgetkit.c @@ -44,20 +44,25 @@ struct driver_interfacekit { int inputs; int outputs; int has_lcd; + int amnesiac; }; -#define ifkit(_sensors, _inputs, _outputs, _lcd) \ -static struct driver_interfacekit ph_##_sensors##_inputs##_outputs = { \ + +#define ifkit(_sensors, _inputs, _outputs, _lcd, _amnesiac) \ +{ \ .sensors = _sensors, \ .inputs = _inputs, \ .outputs = _outputs, \ .has_lcd = _lcd, \ + .amnesiac = _amnesiac \ }; -ifkit(0, 0, 4, 0); -ifkit(8, 8, 8, 0); -ifkit(0, 4, 7, 1); -ifkit(8, 8, 4, 0); -ifkit(0, 8, 8, 1); -ifkit(0, 16, 16, 0); + +static const struct driver_interfacekit ph_004 = ifkit(0, 0, 4, 0, 0); +static const struct driver_interfacekit ph_888n = ifkit(8, 8, 8, 0, 1); +static const struct driver_interfacekit ph_888o = ifkit(8, 8, 8, 0, 0); +static const struct driver_interfacekit ph_047 = ifkit(0, 4, 7, 1, 0); +static const struct driver_interfacekit ph_884 = ifkit(8, 8, 4, 0, 0); +static const struct driver_interfacekit ph_088 = ifkit(0, 8, 8, 1, 0); +static const struct driver_interfacekit ph_01616 = ifkit(0, 16, 16, 0, 0); static unsigned long device_no; @@ -77,6 +82,7 @@ struct interfacekit { dma_addr_t data_dma; struct work_struct do_notify; + struct work_struct do_resubmit; unsigned long input_events; unsigned long sensor_events; }; @@ -84,8 +90,10 @@ struct interfacekit { static struct usb_device_id id_table[] = { {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT004), .driver_info = (kernel_ulong_t)&ph_004}, - {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT888), - .driver_info = (kernel_ulong_t)&ph_888}, + {USB_DEVICE_VER(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT888, 0, 0x814), + .driver_info = (kernel_ulong_t)&ph_888o}, + {USB_DEVICE_VER(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT888, 0x0815, 0xffff), + .driver_info = (kernel_ulong_t)&ph_888n}, {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT047), .driver_info = (kernel_ulong_t)&ph_047}, {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT088), @@ -98,16 +106,11 @@ static struct usb_device_id id_table[] = { }; MODULE_DEVICE_TABLE(usb, id_table); -static int change_outputs(struct interfacekit *kit, int output_num, int enable) +static int set_outputs(struct interfacekit *kit) { u8 *buffer; int retval; - if (enable) - set_bit(output_num, &kit->outputs); - else - clear_bit(output_num, &kit->outputs); - buffer = kzalloc(4, GFP_KERNEL); if (!buffer) { dev_err(&kit->udev->dev, "%s - out of memory\n", __FUNCTION__); @@ -127,6 +130,9 @@ static int change_outputs(struct interfacekit *kit, int output_num, int enable) retval); kfree(buffer); + if (kit->ifkit->amnesiac) + schedule_delayed_work(&kit->do_resubmit, HZ / 2); + return retval < 0 ? retval : 0; } @@ -399,19 +405,29 @@ static void do_notify(void *data) } } +static void do_resubmit(void *data) +{ + set_outputs(data); +} + #define show_set_output(value) \ static ssize_t set_output##value(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ { \ struct interfacekit *kit = dev_get_drvdata(dev); \ - int enabled; \ + int enable; \ int retval; \ \ - if (sscanf(buf, "%d", &enabled) < 1) \ + if (sscanf(buf, "%d", &enable) < 1) \ return -EINVAL; \ \ - retval = change_outputs(kit, value - 1, enabled); \ + if (enable) \ + set_bit(value - 1, &kit->outputs); \ + else \ + clear_bit(value - 1, &kit->outputs); \ + \ + retval = set_outputs(kit); \ \ return retval ? retval : count; \ } \ @@ -560,6 +576,7 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic kit->udev = usb_get_dev(dev); kit->intf = intf; INIT_WORK(&kit->do_notify, do_notify, kit); + INIT_WORK(&kit->do_resubmit, do_resubmit, kit); usb_fill_int_urb(kit->irq, kit->udev, pipe, kit->data, maxp > URB_INT_SIZE ? URB_INT_SIZE : maxp, interfacekit_irq, kit, endpoint->bInterval); @@ -663,6 +680,7 @@ static void interfacekit_disconnect(struct usb_interface *interface) usb_buffer_free(kit->udev, URB_INT_SIZE, kit->data, kit->data_dma); cancel_delayed_work(&kit->do_notify); + cancel_delayed_work(&kit->do_resubmit); for (i=0; i<kit->ifkit->outputs; i++) device_remove_file(kit->dev, &dev_output_attrs[i]); diff --git a/drivers/usb/net/asix.c b/drivers/usb/net/asix.c index 9b97aa6384c..9c0eacf7055 100644 --- a/drivers/usb/net/asix.c +++ b/drivers/usb/net/asix.c @@ -1456,6 +1456,10 @@ static const struct usb_device_id products [] = { USB_DEVICE (0x07d1, 0x3c05), .driver_info = (unsigned long) &ax88772_info, }, { + // DLink DUB-E100 H/W Ver B1 Alternate + USB_DEVICE (0x2001, 0x3c05), + .driver_info = (unsigned long) &ax88772_info, +}, { // Linksys USB1000 USB_DEVICE (0x1737, 0x0039), .driver_info = (unsigned long) &ax88178_info, diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c index def3bb8e229..544d41fe9b9 100644 --- a/drivers/usb/net/kaweth.c +++ b/drivers/usb/net/kaweth.c @@ -165,6 +165,7 @@ static struct usb_device_id usb_klsi_table[] = { { USB_DEVICE(0x1645, 0x0005) }, /* Entrega E45 */ { USB_DEVICE(0x1645, 0x0008) }, /* Entrega USB Ethernet Adapter */ { USB_DEVICE(0x1645, 0x8005) }, /* PortGear Ethernet Adapter */ + { USB_DEVICE(0x1668, 0x0323) }, /* Actiontec USB Ethernet */ { USB_DEVICE(0x2001, 0x4000) }, /* D-link DSB-650C */ {} /* Null terminator */ }; diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c index b8e25af13f0..918cf5a77c0 100644 --- a/drivers/usb/net/pegasus.c +++ b/drivers/usb/net/pegasus.c @@ -45,7 +45,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.6.13 (2005/11/13)" +#define DRIVER_VERSION "v0.6.14 (2006/09/27)" #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>" #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" @@ -339,7 +339,7 @@ static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd) } fail: if (netif_msg_drv(pegasus)) - dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__); + dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__); return ret; } @@ -376,7 +376,7 @@ static int write_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 regd) fail: if (netif_msg_drv(pegasus)) - dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__); + dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__); return -ETIMEDOUT; } @@ -413,7 +413,7 @@ static int read_eprom_word(pegasus_t * pegasus, __u8 index, __u16 * retdata) fail: if (netif_msg_drv(pegasus)) - dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__); + dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__); return -ETIMEDOUT; } @@ -461,7 +461,7 @@ static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data) return ret; fail: if (netif_msg_drv(pegasus)) - dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__); + dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__); return -ETIMEDOUT; } #endif /* PEGASUS_WRITE_EEPROM */ @@ -481,8 +481,12 @@ static void set_ethernet_addr(pegasus_t * pegasus) { __u8 node_id[6]; - get_node_id(pegasus, node_id); - set_registers(pegasus, EthID, sizeof (node_id), node_id); + if (pegasus->features & PEGASUS_II) { + get_registers(pegasus, 0x10, sizeof(node_id), node_id); + } else { + get_node_id(pegasus, node_id); + set_registers(pegasus, EthID, sizeof (node_id), node_id); + } memcpy(pegasus->net->dev_addr, node_id, sizeof (node_id)); } diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 6286aba86fa..d954ec34b01 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -214,14 +214,14 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b return (0); } - spin_lock(&port->lock); + spin_lock_bh(&port->lock); if (port->write_urb_busy) { - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); dbg("%s - already writing", __FUNCTION__); return 0; } port->write_urb_busy = 1; - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); spin_lock_irqsave(&priv->lock, flags); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 1f7b72553f3..e774a27c6c9 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -344,6 +344,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2104_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2106_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_1_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_2_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_1_PID) }, @@ -507,6 +508,9 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_THORLABS_PID) }, { USB_DEVICE(TESTO_VID, TESTO_USB_INTERFACE_PID) }, { USB_DEVICE(FTDI_VID, FTDI_GAMMA_SCOUT_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13M_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 77299996f7e..f0edb87d2dd 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -111,6 +111,7 @@ #define SEALEVEL_2102_PID 0x2102 /* SeaLINK+485 (2102) */ #define SEALEVEL_2103_PID 0x2103 /* SeaLINK+232I (2103) */ #define SEALEVEL_2104_PID 0x2104 /* SeaLINK+485I (2104) */ +#define SEALEVEL_2106_PID 0x9020 /* SeaLINK+422 (2106) */ #define SEALEVEL_2201_1_PID 0x2211 /* SeaPORT+2/232 (2201) Port 1 */ #define SEALEVEL_2201_2_PID 0x2221 /* SeaPORT+2/232 (2201) Port 2 */ #define SEALEVEL_2202_1_PID 0x2212 /* SeaPORT+2/485 (2202) Port 1 */ @@ -472,6 +473,15 @@ */ #define FTDI_GAMMA_SCOUT_PID 0xD678 /* Gamma Scout online */ +/* + * Tactrix OpenPort (ECU) devices. + * OpenPort 1.3M submitted by Donour Sizemore. + * OpenPort 1.3S and 1.3U submitted by Ian Abbott. + */ +#define FTDI_TACTRIX_OPENPORT_13M_PID 0xCC48 /* OpenPort 1.3 Mitsubishi */ +#define FTDI_TACTRIX_OPENPORT_13S_PID 0xCC49 /* OpenPort 1.3 Subaru */ +#define FTDI_TACTRIX_OPENPORT_13U_PID 0xCC4A /* OpenPort 1.3 Universal */ + /* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 17271355639..21cbaa0fb96 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -175,14 +175,14 @@ int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char * /* only do something if we have a bulk out endpoint */ if (serial->num_bulk_out) { - spin_lock(&port->lock); + spin_lock_bh(&port->lock); if (port->write_urb_busy) { - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); dbg("%s - already writing", __FUNCTION__); return 0; } port->write_urb_busy = 1; - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); count = (count > port->bulk_out_size) ? port->bulk_out_size : count; diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index bfc6998cd16..cbc725a6c58 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -479,6 +479,7 @@ static struct usb_device_id ipaq_id_table [] = { { USB_DEVICE(0x0BB4, 0x0A9D) }, /* SmartPhone USB Sync */ { USB_DEVICE(0x0BB4, 0x0A9E) }, /* SmartPhone USB Sync */ { USB_DEVICE(0x0BB4, 0x0A9F) }, /* SmartPhone USB Sync */ + { USB_DEVICE(0x0BB4, 0x0BCE) }, /* "High Tech Computer Corp" */ { USB_DEVICE(0x0BF8, 0x1001) }, /* Fujitsu Siemens Computers USB Sync */ { USB_DEVICE(0x0C44, 0x03A2) }, /* Motorola iDEN Smartphone */ { USB_DEVICE(0x0C8E, 0x6000) }, /* Cesscom Luxian Series */ diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index 87306cb6f9f..812bc213a96 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -394,14 +394,14 @@ static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int return 0; } - spin_lock(&port->lock); + spin_lock_bh(&port->lock); if (port->write_urb_busy) { - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); dbg("%s - already writing", __FUNCTION__); return 0; } port->write_urb_busy = 1; - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); count = min(count, port->bulk_out_size); memcpy(port->bulk_out_buffer, buf, count); diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 1738b0b6a37..1b348df388e 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -342,14 +342,14 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int if (count == 0) return 0; - spin_lock(&port->lock); + spin_lock_bh(&port->lock); if (port->write_urb_busy) { - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); dbg("%s - already writing", __FUNCTION__); return 0; } port->write_urb_busy = 1; - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); transfer_buffer = port->write_urb->transfer_buffer; transfer_size = min(count, port->bulk_out_size - 1); diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 49b8dc039d1..59e777f1e8f 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -518,13 +518,13 @@ static int keyspan_pda_write(struct usb_serial_port *port, the TX urb is in-flight (wait until it completes) the device is full (wait until it says there is room) */ - spin_lock(&port->lock); + spin_lock_bh(&port->lock); if (port->write_urb_busy || priv->tx_throttled) { - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); return 0; } port->write_urb_busy = 1; - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); /* At this point the URB is in our control, nobody else can submit it again (the only sudden transition was the one from EINPROGRESS to diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index e49f40913c2..a764ff4e326 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -256,14 +256,14 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf return (0); } - spin_lock(&wport->lock); + spin_lock_bh(&wport->lock); if (wport->write_urb_busy) { - spin_unlock(&wport->lock); + spin_unlock_bh(&wport->lock); dbg("%s - already writing", __FUNCTION__); return 0; } wport->write_urb_busy = 1; - spin_unlock(&wport->lock); + spin_unlock_bh(&wport->lock); count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count; diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 1036d436ed2..9c18173e33f 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -82,6 +82,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) }, { USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) }, { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) }, + { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 762cc290ef5..65a5039665e 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -93,3 +93,7 @@ /* Alcor Micro Corp. USB 2.0 TO RS-232 */ #define ALCOR_VENDOR_ID 0x058F #define ALCOR_PRODUCT_ID 0x9720 + +/* Huawei E620 UMTS/HSDPA card (ID: 12d1:1001) */ +#define HUAWEI_VENDOR_ID 0x12d1 +#define HUAWEI_PRODUCT_ID 0x1001 diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 789771ecdb1..1e07dfad685 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -298,14 +298,14 @@ static int safe_write (struct usb_serial_port *port, const unsigned char *buf, i dbg ("%s - write request of 0 bytes", __FUNCTION__); return (0); } - spin_lock(&port->lock); + spin_lock_bh(&port->lock); if (port->write_urb_busy) { - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); dbg("%s - already writing", __FUNCTION__); return 0; } port->write_urb_busy = 1; - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); packet_length = port->bulk_out_size; // get max packetsize diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 86e48c42d6a..422a4b288e3 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -8,8 +8,7 @@ comment "may also be needed; see USB_STORAGE Help for more information" config USB_STORAGE tristate "USB Mass Storage support" - depends on USB - select SCSI + depends on USB && SCSI ---help--- Say Y here if you want to connect USB mass storage devices to your computer's USB port. This is the driver you need for USB @@ -18,7 +17,7 @@ config USB_STORAGE similar devices. This driver may also be used for some cameras and card readers. - This option 'selects' (turns on, enables) 'SCSI', but you + This option depends on 'SCSI' support being enabled, but you probably also need 'SCSI device support: SCSI disk support' (BLK_DEV_SD) for most USB storage devices. diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 40bf159f7d5..c9a8d50106d 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -152,6 +152,13 @@ UNUSUAL_DEV( 0x0421, 0x042e, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ), +/* Reported by Jon Hart <Jon.Hart@web.de> */ +UNUSUAL_DEV( 0x0421, 0x0434, 0x0100, 0x0100, + "Nokia", + "E60", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ), + /* Reported by Sumedha Swamy <sumedhaswamy@gmail.com> and * Einar Th. Einarsson <einarthered@gmail.com> */ UNUSUAL_DEV( 0x0421, 0x0444, 0x0100, 0x0100, @@ -1277,6 +1284,13 @@ UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), +/* Reported by Jan Mate <mate@fiit.stuba.sk> */ +UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000, + "Sony Ericsson", + "P990i", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + /* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu> * Tested on hardware version 1.10. * Entry is needed only for the initializer function override. diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 702eb933cf8..e0ef3328942 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -825,30 +825,48 @@ config FB_I810_I2C help config FB_INTEL - tristate "Intel 830M/845G/852GM/855GM/865G support (EXPERIMENTAL)" + tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G support (EXPERIMENTAL)" depends on FB && EXPERIMENTAL && PCI && X86 select AGP select AGP_INTEL + select I2C_ALGOBIT if FB_INTEL_I2C + select I2C if FB_INTEL_I2C select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT help This driver supports the on-board graphics built in to the Intel - 830M/845G/852GM/855GM/865G chipsets. + 830M/845G/852GM/855GM/865G/915G/915GM/945G/945GM chipsets. Say Y if you have and plan to use such a board. - To compile this driver as a module, choose M here: the + If you say Y here and want DDC/I2C support you must first say Y to + "I2C support" and "I2C bit-banging support" in the character devices + section. + + If you say M here then "I2C support" and "I2C bit-banging support" + can be build either as modules or built-in. + + To compile this driver as a module, choose M here: the module will be called intelfb. + For more information, please read <file:Documentation/fb/intelfb.txt> + config FB_INTEL_DEBUG - bool "Intel driver Debug Messages" + bool "Intel driver Debug Messages" depends on FB_INTEL ---help--- Say Y here if you want the Intel driver to output all sorts of debugging informations to provide to the maintainer when something goes wrong. +config FB_INTEL_I2C + bool "DDC/I2C for Intel framebuffer support" + depends on FB_INTEL + default y + help + Say Y here if you want DDC/I2C support for your on-board Intel graphics. + config FB_MATROX tristate "Matrox acceleration" depends on FB && PCI diff --git a/drivers/video/backlight/locomolcd.c b/drivers/video/backlight/locomolcd.c index caf1eca199b..628571c63ba 100644 --- a/drivers/video/backlight/locomolcd.c +++ b/drivers/video/backlight/locomolcd.c @@ -33,19 +33,19 @@ static unsigned long locomolcd_flags; static void locomolcd_on(int comadj) { - locomo_gpio_set_dir(locomolcd_dev, LOCOMO_GPIO_LCD_VSHA_ON, 0); - locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VSHA_ON, 1); + locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 0); + locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 1); mdelay(2); - locomo_gpio_set_dir(locomolcd_dev, LOCOMO_GPIO_LCD_VSHD_ON, 0); - locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VSHD_ON, 1); + locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 0); + locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 1); mdelay(2); locomo_m62332_senddata(locomolcd_dev, comadj, 0); mdelay(5); - locomo_gpio_set_dir(locomolcd_dev, LOCOMO_GPIO_LCD_VEE_ON, 0); - locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VEE_ON, 1); + locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 0); + locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 1); mdelay(10); /* TFTCRST | CPSOUT=0 | CPSEN */ @@ -58,8 +58,8 @@ static void locomolcd_on(int comadj) locomo_writel((0x04 | 0x01), locomolcd_dev->mapbase + LOCOMO_TC); mdelay(10); - locomo_gpio_set_dir(locomolcd_dev, LOCOMO_GPIO_LCD_MOD, 0); - locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_MOD, 1); + locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 0); + locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 1); } static void locomolcd_off(int comadj) @@ -68,16 +68,16 @@ static void locomolcd_off(int comadj) locomo_writel(0x06, locomolcd_dev->mapbase + LOCOMO_TC); mdelay(1); - locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VSHA_ON, 0); + locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 0); mdelay(110); - locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VEE_ON, 0); + locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 0); mdelay(700); /* TFTCRST=0 | CPSOUT=0 | CPSEN = 0 */ locomo_writel(0, locomolcd_dev->mapbase + LOCOMO_TC); - locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_MOD, 0); - locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VSHD_ON, 0); + locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 0); + locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 0); } void locomolcd_power(int on) @@ -167,14 +167,14 @@ static int locomolcd_resume(struct locomo_dev *dev) #define locomolcd_resume NULL #endif -static int locomolcd_probe(struct locomo_dev *dev) +static int locomolcd_probe(struct locomo_dev *ldev) { unsigned long flags; local_irq_save(flags); - locomolcd_dev = dev; + locomolcd_dev = ldev; - locomo_gpio_set_dir(dev, LOCOMO_GPIO_FL_VR, 0); + locomo_gpio_set_dir(ldev->dev.parent, LOCOMO_GPIO_FL_VR, 0); /* the poodle_lcd_power function is called for the first time * from fs_initcall, which is before locomo is activated. diff --git a/drivers/video/intelfb/Makefile b/drivers/video/intelfb/Makefile index 722d21d6e5c..6c782d3ae1b 100644 --- a/drivers/video/intelfb/Makefile +++ b/drivers/video/intelfb/Makefile @@ -1,6 +1,8 @@ obj-$(CONFIG_FB_INTEL) += intelfb.o -intelfb-objs := intelfbdrv.o intelfbhw.o +intelfb-y := intelfbdrv.o intelfbhw.o +intelfb-$(CONFIG_FB_INTEL_I2C) += intelfb_i2c.o +intelfb-objs := $(intelfb-y) ifdef CONFIG_FB_INTEL_DEBUG #EXTRA_CFLAGS += -DDEBUG -DVERBOSE -DREGDUMP diff --git a/drivers/video/intelfb/intelfb.h b/drivers/video/intelfb/intelfb.h index e290d7460e1..80b94c19a9f 100644 --- a/drivers/video/intelfb/intelfb.h +++ b/drivers/video/intelfb/intelfb.h @@ -6,6 +6,10 @@ #include <linux/agp_backend.h> #include <linux/fb.h> +#ifdef CONFIG_FB_INTEL_I2C +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#endif /*** Version/name ***/ #define INTELFB_VERSION "0.9.4" @@ -115,6 +119,29 @@ /* Intel agpgart driver */ #define AGP_PHYSICAL_MEMORY 2 +/* store information about an Ixxx DVO */ +/* The i830->i865 use multiple DVOs with multiple i2cs */ +/* the i915, i945 have a single sDVO i2c bus - which is different */ +#define MAX_OUTPUTS 6 + +/* these are outputs from the chip - integrated only + external chips are via DVO or SDVO output */ +#define INTELFB_OUTPUT_UNUSED 0 +#define INTELFB_OUTPUT_ANALOG 1 +#define INTELFB_OUTPUT_DVO 2 +#define INTELFB_OUTPUT_SDVO 3 +#define INTELFB_OUTPUT_LVDS 4 +#define INTELFB_OUTPUT_TVOUT 5 + +#define INTELFB_DVO_CHIP_NONE 0 +#define INTELFB_DVO_CHIP_LVDS 1 +#define INTELFB_DVO_CHIP_TMDS 2 +#define INTELFB_DVO_CHIP_TVOUT 4 + +#define INTELFB_OUTPUT_PIPE_NC 0 +#define INTELFB_OUTPUT_PIPE_A 1 +#define INTELFB_OUTPUT_PIPE_B 2 + /*** Data Types ***/ /* supported chipsets */ @@ -195,6 +222,10 @@ struct intelfb_hwstate { u32 mem_mode; u32 fw_blc_0; u32 fw_blc_1; + u16 hwstam; + u16 ier; + u16 iir; + u16 imr; }; struct intelfb_heap_data { @@ -204,6 +235,33 @@ struct intelfb_heap_data { u32 size; // in bytes }; +#ifdef CONFIG_FB_INTEL_I2C +struct intelfb_i2c_chan { + struct intelfb_info *dinfo; + u32 reg; + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo; +}; +#endif + +struct intelfb_output_rec { + int type; + int pipe; + int flags; + +#ifdef CONFIG_FB_INTEL_I2C + struct intelfb_i2c_chan i2c_bus; + struct intelfb_i2c_chan ddc_bus; +#endif +}; + +struct intelfb_vsync { + wait_queue_head_t wait; + unsigned int count; + int pan_display; + u32 pan_offset; +}; + struct intelfb_info { struct fb_info *info; struct fb_ops *fbops; @@ -220,7 +278,7 @@ struct intelfb_info { u8 fbmem_gart; /* mtrr support */ - u32 mtrr_reg; + int mtrr_reg; u32 has_mtrr; /* heap data */ @@ -267,6 +325,12 @@ struct intelfb_info { int fixed_mode; int ring_active; int flag; + unsigned long irq_flags; + int open; + + /* vsync */ + struct intelfb_vsync vsync; + spinlock_t int_lock; /* hw cursor */ int cursor_on; @@ -285,12 +349,25 @@ struct intelfb_info { /* index into plls */ int pll_index; + + /* outputs */ + int num_outputs; + struct intelfb_output_rec output[MAX_OUTPUTS]; }; #define IS_I9XX(dinfo) (((dinfo)->chipset == INTEL_915G)||(dinfo->chipset == INTEL_915GM)||((dinfo)->chipset == INTEL_945G)||(dinfo->chipset==INTEL_945GM)) +#ifndef FBIO_WAITFORVSYNC +#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) +#endif + /*** function prototypes ***/ extern int intelfb_var_to_depth(const struct fb_var_screeninfo *var); +#ifdef CONFIG_FB_INTEL_I2C +extern void intelfb_create_i2c_busses(struct intelfb_info *dinfo); +extern void intelfb_delete_i2c_busses(struct intelfb_info *dinfo); +#endif + #endif /* _INTELFB_H */ diff --git a/drivers/video/intelfb/intelfb_i2c.c b/drivers/video/intelfb/intelfb_i2c.c new file mode 100644 index 00000000000..c1113d6e941 --- /dev/null +++ b/drivers/video/intelfb/intelfb_i2c.c @@ -0,0 +1,200 @@ +/************************************************************************** + + Copyright 2006 Dave Airlie <airlied@linux.ie> + +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +on the rights to use, copy, modify, merge, publish, distribute, sub +license, and/or sell copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL +THE COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. + +**************************************************************************/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/fb.h> + +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <linux/i2c-algo-bit.h> + +#include <asm/io.h> + +#include "intelfb.h" +#include "intelfbhw.h" + +/* bit locations in the registers */ +#define SCL_DIR_MASK 0x0001 +#define SCL_DIR 0x0002 +#define SCL_VAL_MASK 0x0004 +#define SCL_VAL_OUT 0x0008 +#define SCL_VAL_IN 0x0010 +#define SDA_DIR_MASK 0x0100 +#define SDA_DIR 0x0200 +#define SDA_VAL_MASK 0x0400 +#define SDA_VAL_OUT 0x0800 +#define SDA_VAL_IN 0x1000 + +static void intelfb_gpio_setscl(void *data, int state) +{ + struct intelfb_i2c_chan *chan = data; + struct intelfb_info *dinfo = chan->dinfo; + u32 val; + + OUTREG(chan->reg, (state ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK); + val = INREG(chan->reg); +} + +static void intelfb_gpio_setsda(void *data, int state) +{ + struct intelfb_i2c_chan *chan = data; + struct intelfb_info *dinfo = chan->dinfo; + u32 val; + + OUTREG(chan->reg, (state ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK); + val = INREG(chan->reg); +} + +static int intelfb_gpio_getscl(void *data) +{ + struct intelfb_i2c_chan *chan = data; + struct intelfb_info *dinfo = chan->dinfo; + u32 val; + + OUTREG(chan->reg, SCL_DIR_MASK); + OUTREG(chan->reg, 0); + val = INREG(chan->reg); + return ((val & SCL_VAL_IN) != 0); +} + +static int intelfb_gpio_getsda(void *data) +{ + struct intelfb_i2c_chan *chan = data; + struct intelfb_info *dinfo = chan->dinfo; + u32 val; + + OUTREG(chan->reg, SDA_DIR_MASK); + OUTREG(chan->reg, 0); + val = INREG(chan->reg); + return ((val & SDA_VAL_IN) != 0); +} + +static int intelfb_setup_i2c_bus(struct intelfb_info *dinfo, + struct intelfb_i2c_chan *chan, + const u32 reg, const char *name) +{ + int rc; + + chan->dinfo = dinfo; + chan->reg = reg; + snprintf(chan->adapter.name, I2C_NAME_SIZE, "intelfb %s", name); + chan->adapter.owner = THIS_MODULE; + chan->adapter.id = I2C_HW_B_INTELFB; + chan->adapter.algo_data = &chan->algo; + chan->adapter.dev.parent = &chan->dinfo->pdev->dev; + chan->algo.setsda = intelfb_gpio_setsda; + chan->algo.setscl = intelfb_gpio_setscl; + chan->algo.getsda = intelfb_gpio_getsda; + chan->algo.getscl = intelfb_gpio_getscl; + chan->algo.udelay = 40; + chan->algo.timeout = 20; + chan->algo.data = chan; + + i2c_set_adapdata(&chan->adapter, chan); + + /* Raise SCL and SDA */ + intelfb_gpio_setsda(chan, 1); + intelfb_gpio_setscl(chan, 1); + udelay(20); + + rc = i2c_bit_add_bus(&chan->adapter); + if (rc == 0) + DBG_MSG("I2C bus %s registered.\n", name); + else + WRN_MSG("Failed to register I2C bus %s.\n", name); + return rc; +} + +void intelfb_create_i2c_busses(struct intelfb_info *dinfo) +{ + int i = 0; + + /* everyone has at least a single analog output */ + dinfo->num_outputs = 1; + dinfo->output[i].type = INTELFB_OUTPUT_ANALOG; + + /* setup the DDC bus for analog output */ + intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOA, "CRTDDC_A"); + i++; + + /* need to add the output busses for each device + - this function is very incomplete + - i915GM has LVDS and TVOUT for example + */ + switch(dinfo->chipset) { + case INTEL_830M: + case INTEL_845G: + case INTEL_855GM: + case INTEL_865G: + dinfo->output[i].type = INTELFB_OUTPUT_DVO; + intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOD, "DVODDC_D"); + intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, GPIOE, "DVOI2C_E"); + i++; + break; + case INTEL_915G: + case INTEL_915GM: + /* has some LVDS + tv-out */ + case INTEL_945G: + case INTEL_945GM: + /* SDVO ports have a single control bus - 2 devices */ + dinfo->output[i].type = INTELFB_OUTPUT_SDVO; + intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, GPIOE, "SDVOCTRL_E"); + /* TODO: initialize the SDVO */ +// I830SDVOInit(pScrn, i, DVOB); + i++; + + /* set up SDVOC */ + dinfo->output[i].type = INTELFB_OUTPUT_SDVO; + dinfo->output[i].i2c_bus = dinfo->output[i - 1].i2c_bus; + /* TODO: initialize the SDVO */ +// I830SDVOInit(pScrn, i, DVOC); + i++; + break; + } + dinfo->num_outputs = i; +} + +void intelfb_delete_i2c_busses(struct intelfb_info *dinfo) +{ + int i; + + for (i = 0; i < MAX_OUTPUTS; i++) { + if (dinfo->output[i].i2c_bus.dinfo) { + i2c_bit_del_bus(&dinfo->output[i].i2c_bus.adapter); + dinfo->output[i].i2c_bus.dinfo = NULL; + } + if (dinfo->output[i].ddc_bus.dinfo) { + i2c_bit_del_bus(&dinfo->output[i].ddc_bus.adapter); + dinfo->output[i].ddc_bus.dinfo = NULL; + } + } +} diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c index 06af89d44a0..6f9de04193d 100644 --- a/drivers/video/intelfb/intelfbdrv.c +++ b/drivers/video/intelfb/intelfbdrv.c @@ -136,6 +136,8 @@ static void __devinit get_initial_mode(struct intelfb_info *dinfo); static void update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var); +static int intelfb_open(struct fb_info *info, int user); +static int intelfb_release(struct fb_info *info, int user); static int intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info); static int intelfb_set_par(struct fb_info *info); @@ -194,6 +196,8 @@ static int num_registered = 0; /* fb ops */ static struct fb_ops intel_fb_ops = { .owner = THIS_MODULE, + .fb_open = intelfb_open, + .fb_release = intelfb_release, .fb_check_var = intelfb_check_var, .fb_set_par = intelfb_set_par, .fb_setcolreg = intelfb_setcolreg, @@ -446,6 +450,8 @@ cleanup(struct intelfb_info *dinfo) if (!dinfo) return; + intelfbhw_disable_irq(dinfo); + fb_dealloc_cmap(&dinfo->info->cmap); kfree(dinfo->info->pixmap.addr); @@ -467,6 +473,11 @@ cleanup(struct intelfb_info *dinfo) agp_free_memory(dinfo->gtt_ring_mem); } +#ifdef CONFIG_FB_INTEL_I2C + /* un-register I2C bus */ + intelfb_delete_i2c_busses(dinfo); +#endif + if (dinfo->mmio_base) iounmap((void __iomem *)dinfo->mmio_base); if (dinfo->aperture.virtual) @@ -844,6 +855,11 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent) if (bailearly == 5) bailout(dinfo); +#ifdef CONFIG_FB_INTEL_I2C + /* register I2C bus */ + intelfb_create_i2c_busses(dinfo); +#endif + if (bailearly == 6) bailout(dinfo); @@ -888,6 +904,13 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent) } dinfo->registered = 1; + dinfo->open = 0; + + init_waitqueue_head(&dinfo->vsync.wait); + spin_lock_init(&dinfo->int_lock); + dinfo->irq_flags = 0; + dinfo->vsync.pan_display = 0; + dinfo->vsync.pan_offset = 0; return 0; @@ -1188,6 +1211,34 @@ update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var) ***************************************************************/ static int +intelfb_open(struct fb_info *info, int user) +{ + struct intelfb_info *dinfo = GET_DINFO(info); + + if (user) { + dinfo->open++; + } + + return 0; +} + +static int +intelfb_release(struct fb_info *info, int user) +{ + struct intelfb_info *dinfo = GET_DINFO(info); + + if (user) { + dinfo->open--; + msleep(1); + if (!dinfo->open) { + intelfbhw_disable_irq(dinfo); + } + } + + return 0; +} + +static int intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { int change_var = 0; @@ -1433,6 +1484,19 @@ static int intelfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { int retval = 0; + struct intelfb_info *dinfo = GET_DINFO(info); + u32 pipe = 0; + + switch (cmd) { + case FBIO_WAITFORVSYNC: + if (get_user(pipe, (__u32 __user *)arg)) + return -EFAULT; + + retval = intelfbhw_wait_for_vsync(dinfo, pipe); + break; + default: + break; + } return retval; } diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c index 2a9322f9cfd..f887f1efd3f 100644 --- a/drivers/video/intelfb/intelfbhw.c +++ b/drivers/video/intelfb/intelfbhw.c @@ -32,6 +32,7 @@ #include <linux/pci.h> #include <linux/vmalloc.h> #include <linux/pagemap.h> +#include <linux/interrupt.h> #include <asm/io.h> @@ -368,7 +369,13 @@ intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) offset += dinfo->fb.offset << 12; - OUTREG(DSPABASE, offset); + dinfo->vsync.pan_offset = offset; + if ((var->activate & FB_ACTIVATE_VBL) && !intelfbhw_enable_irq(dinfo, 0)) { + dinfo->vsync.pan_display = 1; + } else { + dinfo->vsync.pan_display = 0; + OUTREG(DSPABASE, offset); + } return 0; } @@ -585,6 +592,11 @@ intelfbhw_read_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw, hw->fw_blc_0 = INREG(FW_BLC_0); hw->fw_blc_1 = INREG(FW_BLC_1); + hw->hwstam = INREG16(HWSTAM); + hw->ier = INREG16(IER); + hw->iir = INREG16(IIR); + hw->imr = INREG16(IMR); + return 0; } @@ -613,6 +625,7 @@ static int calc_vclock(int index, int m1, int m2, int n, int p1, int p2, int lvd return vco / p; } +#if REGDUMP static void intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, int *o_p1, int *o_p2) { @@ -638,6 +651,7 @@ intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, int *o_p1, int *o_p2) *o_p1 = p1; *o_p2 = p2; } +#endif void @@ -794,6 +808,10 @@ intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw) printk(" FW_BLC_0 0x%08x\n", hw->fw_blc_0); printk(" FW_BLC_1 0x%08x\n", hw->fw_blc_1); + printk(" HWSTAM 0x%04x\n", hw->hwstam); + printk(" IER 0x%04x\n", hw->ier); + printk(" IIR 0x%04x\n", hw->iir); + printk(" IMR 0x%04x\n", hw->imr); printk("hw state dump end\n"); #endif } @@ -1932,3 +1950,119 @@ intelfbhw_cursor_reset(struct intelfb_info *dinfo) { addr += 16; } } + +static irqreturn_t +intelfbhw_irq(int irq, void *dev_id, struct pt_regs *fp) { + int handled = 0; + u16 tmp; + struct intelfb_info *dinfo = (struct intelfb_info *)dev_id; + + spin_lock(&dinfo->int_lock); + + tmp = INREG16(IIR); + tmp &= VSYNC_PIPE_A_INTERRUPT; + + if (tmp == 0) { + spin_unlock(&dinfo->int_lock); + return IRQ_RETVAL(handled); + } + + OUTREG16(IIR, tmp); + + if (tmp & VSYNC_PIPE_A_INTERRUPT) { + dinfo->vsync.count++; + if (dinfo->vsync.pan_display) { + dinfo->vsync.pan_display = 0; + OUTREG(DSPABASE, dinfo->vsync.pan_offset); + } + wake_up_interruptible(&dinfo->vsync.wait); + handled = 1; + } + + spin_unlock(&dinfo->int_lock); + + return IRQ_RETVAL(handled); +} + +int +intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable) { + + if (!test_and_set_bit(0, &dinfo->irq_flags)) { + if (request_irq(dinfo->pdev->irq, intelfbhw_irq, SA_SHIRQ, "intelfb", dinfo)) { + clear_bit(0, &dinfo->irq_flags); + return -EINVAL; + } + + spin_lock_irq(&dinfo->int_lock); + OUTREG16(HWSTAM, 0xfffe); + OUTREG16(IMR, 0x0); + OUTREG16(IER, VSYNC_PIPE_A_INTERRUPT); + spin_unlock_irq(&dinfo->int_lock); + } else if (reenable) { + u16 ier; + + spin_lock_irq(&dinfo->int_lock); + ier = INREG16(IER); + if ((ier & VSYNC_PIPE_A_INTERRUPT)) { + DBG_MSG("someone disabled the IRQ [%08X]\n", ier); + OUTREG(IER, VSYNC_PIPE_A_INTERRUPT); + } + spin_unlock_irq(&dinfo->int_lock); + } + return 0; +} + +void +intelfbhw_disable_irq(struct intelfb_info *dinfo) { + u16 tmp; + + if (test_and_clear_bit(0, &dinfo->irq_flags)) { + if (dinfo->vsync.pan_display) { + dinfo->vsync.pan_display = 0; + OUTREG(DSPABASE, dinfo->vsync.pan_offset); + } + spin_lock_irq(&dinfo->int_lock); + OUTREG16(HWSTAM, 0xffff); + OUTREG16(IMR, 0xffff); + OUTREG16(IER, 0x0); + + tmp = INREG16(IIR); + OUTREG16(IIR, tmp); + spin_unlock_irq(&dinfo->int_lock); + + free_irq(dinfo->pdev->irq, dinfo); + } +} + +int +intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe) { + struct intelfb_vsync *vsync; + unsigned int count; + int ret; + + switch (pipe) { + case 0: + vsync = &dinfo->vsync; + break; + default: + return -ENODEV; + } + + ret = intelfbhw_enable_irq(dinfo, 0); + if (ret) { + return ret; + } + + count = vsync->count; + ret = wait_event_interruptible_timeout(vsync->wait, count != vsync->count, HZ/10); + if (ret < 0) { + return ret; + } + if (ret == 0) { + intelfbhw_enable_irq(dinfo, 1); + DBG_MSG("wait_for_vsync timed out!\n"); + return -ETIMEDOUT; + } + + return 0; +} diff --git a/drivers/video/intelfb/intelfbhw.h b/drivers/video/intelfb/intelfbhw.h index 10acda098b7..8c54ba8fbdd 100644 --- a/drivers/video/intelfb/intelfbhw.h +++ b/drivers/video/intelfb/intelfbhw.h @@ -88,6 +88,19 @@ #define INSTDONE 0x2090 #define PRI_RING_EMPTY 1 +#define HWSTAM 0x2098 +#define IER 0x20A0 +#define IIR 0x20A4 +#define IMR 0x20A8 +#define VSYNC_PIPE_A_INTERRUPT (1 << 7) +#define PIPE_A_EVENT_INTERRUPT (1 << 4) +#define VSYNC_PIPE_B_INTERRUPT (1 << 5) +#define PIPE_B_EVENT_INTERRUPT (1 << 4) +#define HOST_PORT_EVENT_INTERRUPT (1 << 3) +#define CAPTURE_EVENT_INTERRUPT (1 << 2) +#define USER_DEFINED_INTERRUPT (1 << 1) +#define BREAKPOINT_INTERRUPT 1 + #define INSTPM 0x20c0 #define SYNC_FLUSH_ENABLE (1 << 5) @@ -113,6 +126,12 @@ #define FW_DISPC_BL_SHIFT 8 #define FW_DISPC_BL_MASK 0x7 +#define GPIOA 0x5010 +#define GPIOB 0x5014 +#define GPIOC 0x5018 // this may be external DDC on i830 +#define GPIOD 0x501C // this is DVO DDC +#define GPIOE 0x5020 // this is DVO i2C +#define GPIOF 0x5024 /* PLL registers */ #define VGA0_DIVISOR 0x06000 @@ -468,9 +487,12 @@ /* I/O macros */ #define INREG8(addr) readb((u8 __iomem *)(dinfo->mmio_base + (addr))) +#define INREG16(addr) readw((u16 __iomem *)(dinfo->mmio_base + (addr))) #define INREG(addr) readl((u32 __iomem *)(dinfo->mmio_base + (addr))) #define OUTREG8(addr, val) writeb((val),(u8 __iomem *)(dinfo->mmio_base + \ (addr))) +#define OUTREG16(addr, val) writew((val),(u16 __iomem *)(dinfo->mmio_base + \ + (addr))) #define OUTREG(addr, val) writel((val),(u32 __iomem *)(dinfo->mmio_base + \ (addr))) @@ -545,5 +567,8 @@ extern void intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg, extern void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width, int height, u8 *data); extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo); +extern int intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable); +extern void intelfbhw_disable_irq(struct intelfb_info *dinfo); +extern int intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe); #endif /* _INTELFBHW_H */ |