diff options
Diffstat (limited to 'drivers')
412 files changed, 29974 insertions, 15941 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index bfc918633fd9..805c432c9439 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -148,4 +148,6 @@ source "drivers/iio/Kconfig" source "drivers/vme/Kconfig" +source "drivers/pwm/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 2ba29ffef2cb..bd36f09f2246 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -8,6 +8,7 @@ # GPIO must come after pinctrl as gpios may need to mux pins etc obj-y += pinctrl/ obj-y += gpio/ +obj-y += pwm/ obj-$(CONFIG_PCI) += pci/ obj-$(CONFIG_PARISC) += parisc/ obj-$(CONFIG_RAPIDIO) += rapidio/ diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 45d8097ef4cf..b728880ef10e 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -132,6 +132,33 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK; } +/* Force to use vendor driver when the ACPI device is known to be + * buggy */ +static int video_detect_force_vendor(const struct dmi_system_id *d) +{ + acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; + return 0; +} + +static struct dmi_system_id video_detect_dmi_table[] = { + /* On Samsung X360, the BIOS will set a flag (VDRV) if generic + * ACPI backlight device is used. This flag will definitively break + * the backlight interface (even the vendor interface) untill next + * reboot. It's why we should prevent video.ko from being used here + * and we can't rely on a later call to acpi_video_unregister(). + */ + { + .callback = video_detect_force_vendor, + .ident = "X360", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "X360"), + DMI_MATCH(DMI_BOARD_NAME, "X360"), + }, + }, + { }, +}; + /* * Returns the video capabilities of a specific ACPI graphics device * @@ -164,6 +191,8 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle) * ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; *} */ + + dmi_check_system(video_detect_dmi_table); } else { status = acpi_bus_get_device(graphics_handle, &tmp_dev); if (ACPI_FAILURE(status)) { @@ -182,8 +211,7 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle) } EXPORT_SYMBOL(acpi_video_get_capabilities); -/* Returns true if video.ko can do backlight switching */ -int acpi_video_backlight_support(void) +static void acpi_video_caps_check(void) { /* * We must check whether the ACPI graphics device is physically plugged @@ -191,6 +219,34 @@ int acpi_video_backlight_support(void) */ if (!acpi_video_caps_checked) acpi_video_get_capabilities(NULL); +} + +/* Promote the vendor interface instead of the generic video module. + * This function allow DMI blacklists to be implemented by externals + * platform drivers instead of putting a big blacklist in video_detect.c + * After calling this function you will probably want to call + * acpi_video_unregister() to make sure the video module is not loaded + */ +void acpi_video_dmi_promote_vendor(void) +{ + acpi_video_caps_check(); + acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; +} +EXPORT_SYMBOL(acpi_video_dmi_promote_vendor); + +/* To be called when a driver who previously promoted the vendor + * interface */ +void acpi_video_dmi_demote_vendor(void) +{ + acpi_video_caps_check(); + acpi_video_support &= ~ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; +} +EXPORT_SYMBOL(acpi_video_dmi_demote_vendor); + +/* Returns true if video.ko can do backlight switching */ +int acpi_video_backlight_support(void) +{ + acpi_video_caps_check(); /* First check for boot param -> highest prio */ if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR) diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c index ac6a5beb28f3..bfaa5cb1629a 100644 --- a/drivers/ata/pata_arasan_cf.c +++ b/drivers/ata/pata_arasan_cf.c @@ -184,10 +184,8 @@ struct arasan_cf_dev { /* pointer to ata_host structure */ struct ata_host *host; - /* clk structure, only if HAVE_CLK is defined */ -#ifdef CONFIG_HAVE_CLK + /* clk structure */ struct clk *clk; -#endif /* physical base address of controller */ dma_addr_t pbase; @@ -312,13 +310,11 @@ static int cf_init(struct arasan_cf_dev *acdev) unsigned long flags; int ret = 0; -#ifdef CONFIG_HAVE_CLK ret = clk_enable(acdev->clk); if (ret) { dev_dbg(acdev->host->dev, "clock enable failed"); return ret; } -#endif spin_lock_irqsave(&acdev->host->lock, flags); /* configure CF interface clock */ @@ -344,9 +340,7 @@ static void cf_exit(struct arasan_cf_dev *acdev) writel(readl(acdev->vbase + OP_MODE) & ~CFHOST_ENB, acdev->vbase + OP_MODE); spin_unlock_irqrestore(&acdev->host->lock, flags); -#ifdef CONFIG_HAVE_CLK clk_disable(acdev->clk); -#endif } static void dma_callback(void *dev) @@ -828,13 +822,11 @@ static int __devinit arasan_cf_probe(struct platform_device *pdev) return -ENOMEM; } -#ifdef CONFIG_HAVE_CLK acdev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(acdev->clk)) { dev_warn(&pdev->dev, "Clock not found\n"); return PTR_ERR(acdev->clk); } -#endif /* allocate host */ host = ata_host_alloc(&pdev->dev, 1); @@ -899,9 +891,7 @@ static int __devinit arasan_cf_probe(struct platform_device *pdev) &arasan_cf_sht); free_clk: -#ifdef CONFIG_HAVE_CLK clk_put(acdev->clk); -#endif return ret; } @@ -912,9 +902,7 @@ static int __devexit arasan_cf_remove(struct platform_device *pdev) ata_host_detach(host); cf_exit(acdev); -#ifdef CONFIG_HAVE_CLK clk_put(acdev->clk); -#endif return 0; } diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c index 6f3676f1559f..3fbedc75e7c5 100644 --- a/drivers/base/dma-mapping.c +++ b/drivers/base/dma-mapping.c @@ -10,6 +10,7 @@ #include <linux/dma-mapping.h> #include <linux/export.h> #include <linux/gfp.h> +#include <asm-generic/dma-coherent.h> /* * Managed DMA API @@ -217,4 +218,52 @@ void dmam_release_declared_memory(struct device *dev) } EXPORT_SYMBOL(dmam_release_declared_memory); +/* + * Create scatter-list for the already allocated DMA buffer. + */ +int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t handle, size_t size) +{ + struct page *page = virt_to_page(cpu_addr); + int ret; + + ret = sg_alloc_table(sgt, 1, GFP_KERNEL); + if (unlikely(ret)) + return ret; + + sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0); + return 0; +} +EXPORT_SYMBOL(dma_common_get_sgtable); + #endif + +/* + * Create userspace mapping for the DMA-coherent memory. + */ +int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size) +{ + int ret = -ENXIO; +#ifdef CONFIG_MMU + unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT; + unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr)); + unsigned long off = vma->vm_pgoff; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret)) + return ret; + + if (off < count && user_count <= (count - off)) { + ret = remap_pfn_range(vma, vma->vm_start, + pfn + off, + user_count << PAGE_SHIFT, + vma->vm_page_prot); + } +#endif /* CONFIG_MMU */ + + return ret; +} +EXPORT_SYMBOL(dma_common_mmap); diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 8f428a8ab003..9917943a3572 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -55,8 +55,6 @@ #define RBD_MINORS_PER_MAJOR 256 /* max minors per blkdev */ -#define RBD_MAX_MD_NAME_LEN (RBD_MAX_OBJ_NAME_LEN + sizeof(RBD_SUFFIX)) -#define RBD_MAX_POOL_NAME_LEN 64 #define RBD_MAX_SNAP_NAME_LEN 32 #define RBD_MAX_OPT_LEN 1024 @@ -78,13 +76,12 @@ */ struct rbd_image_header { u64 image_size; - char block_name[32]; + char *object_prefix; __u8 obj_order; __u8 crypt_type; __u8 comp_type; struct ceph_snap_context *snapc; size_t snap_names_len; - u64 snap_seq; u32 total_snaps; char *snap_names; @@ -150,7 +147,7 @@ struct rbd_snap { * a single device */ struct rbd_device { - int id; /* blkdev unique id */ + int dev_id; /* blkdev unique id */ int major; /* blkdev assigned major */ struct gendisk *disk; /* blkdev's gendisk and rq */ @@ -163,20 +160,24 @@ struct rbd_device { spinlock_t lock; /* queue lock */ struct rbd_image_header header; - char obj[RBD_MAX_OBJ_NAME_LEN]; /* rbd image name */ - int obj_len; - char obj_md_name[RBD_MAX_MD_NAME_LEN]; /* hdr nm. */ - char pool_name[RBD_MAX_POOL_NAME_LEN]; - int poolid; + char *image_name; + size_t image_name_len; + char *header_name; + char *pool_name; + int pool_id; struct ceph_osd_event *watch_event; struct ceph_osd_request *watch_request; /* protects updating the header */ struct rw_semaphore header_rwsem; - char snap_name[RBD_MAX_SNAP_NAME_LEN]; + /* name of the snapshot this device reads from */ + char *snap_name; + /* id of the snapshot this device reads from */ u64 snap_id; /* current snapshot id */ - int read_only; + /* whether the snap_id this device reads from still exists */ + bool snap_exists; + int read_only; struct list_head node; @@ -201,8 +202,7 @@ static ssize_t rbd_snap_add(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); -static void __rbd_remove_snap_dev(struct rbd_device *rbd_dev, - struct rbd_snap *snap); +static void __rbd_remove_snap_dev(struct rbd_snap *snap); static ssize_t rbd_add(struct bus_type *bus, const char *buf, size_t count); @@ -240,7 +240,7 @@ static void rbd_put_dev(struct rbd_device *rbd_dev) put_device(&rbd_dev->dev); } -static int __rbd_refresh_header(struct rbd_device *rbd_dev); +static int rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver); static int rbd_open(struct block_device *bdev, fmode_t mode) { @@ -273,9 +273,9 @@ static const struct block_device_operations rbd_bd_ops = { /* * Initialize an rbd client instance. - * We own *opt. + * We own *ceph_opts. */ -static struct rbd_client *rbd_client_create(struct ceph_options *opt, +static struct rbd_client *rbd_client_create(struct ceph_options *ceph_opts, struct rbd_options *rbd_opts) { struct rbd_client *rbdc; @@ -291,10 +291,10 @@ static struct rbd_client *rbd_client_create(struct ceph_options *opt, mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - rbdc->client = ceph_create_client(opt, rbdc, 0, 0); + rbdc->client = ceph_create_client(ceph_opts, rbdc, 0, 0); if (IS_ERR(rbdc->client)) goto out_mutex; - opt = NULL; /* Now rbdc->client is responsible for opt */ + ceph_opts = NULL; /* Now rbdc->client is responsible for ceph_opts */ ret = ceph_open_session(rbdc->client); if (ret < 0) @@ -317,23 +317,23 @@ out_mutex: mutex_unlock(&ctl_mutex); kfree(rbdc); out_opt: - if (opt) - ceph_destroy_options(opt); + if (ceph_opts) + ceph_destroy_options(ceph_opts); return ERR_PTR(ret); } /* * Find a ceph client with specific addr and configuration. */ -static struct rbd_client *__rbd_client_find(struct ceph_options *opt) +static struct rbd_client *__rbd_client_find(struct ceph_options *ceph_opts) { struct rbd_client *client_node; - if (opt->flags & CEPH_OPT_NOSHARE) + if (ceph_opts->flags & CEPH_OPT_NOSHARE) return NULL; list_for_each_entry(client_node, &rbd_client_list, node) - if (ceph_compare_options(opt, client_node->client) == 0) + if (!ceph_compare_options(ceph_opts, client_node->client)) return client_node; return NULL; } @@ -349,7 +349,7 @@ enum { /* string args above */ }; -static match_table_t rbdopt_tokens = { +static match_table_t rbd_opts_tokens = { {Opt_notify_timeout, "notify_timeout=%d"}, /* int args above */ /* string args above */ @@ -358,11 +358,11 @@ static match_table_t rbdopt_tokens = { static int parse_rbd_opts_token(char *c, void *private) { - struct rbd_options *rbdopt = private; + struct rbd_options *rbd_opts = private; substring_t argstr[MAX_OPT_ARGS]; int token, intval, ret; - token = match_token(c, rbdopt_tokens, argstr); + token = match_token(c, rbd_opts_tokens, argstr); if (token < 0) return -EINVAL; @@ -383,7 +383,7 @@ static int parse_rbd_opts_token(char *c, void *private) switch (token) { case Opt_notify_timeout: - rbdopt->notify_timeout = intval; + rbd_opts->notify_timeout = intval; break; default: BUG_ON(token); @@ -400,7 +400,7 @@ static struct rbd_client *rbd_get_client(const char *mon_addr, char *options) { struct rbd_client *rbdc; - struct ceph_options *opt; + struct ceph_options *ceph_opts; struct rbd_options *rbd_opts; rbd_opts = kzalloc(sizeof(*rbd_opts), GFP_KERNEL); @@ -409,29 +409,29 @@ static struct rbd_client *rbd_get_client(const char *mon_addr, rbd_opts->notify_timeout = RBD_NOTIFY_TIMEOUT_DEFAULT; - opt = ceph_parse_options(options, mon_addr, - mon_addr + mon_addr_len, - parse_rbd_opts_token, rbd_opts); - if (IS_ERR(opt)) { + ceph_opts = ceph_parse_options(options, mon_addr, + mon_addr + mon_addr_len, + parse_rbd_opts_token, rbd_opts); + if (IS_ERR(ceph_opts)) { kfree(rbd_opts); - return ERR_CAST(opt); + return ERR_CAST(ceph_opts); } spin_lock(&rbd_client_list_lock); - rbdc = __rbd_client_find(opt); + rbdc = __rbd_client_find(ceph_opts); if (rbdc) { /* using an existing client */ kref_get(&rbdc->kref); spin_unlock(&rbd_client_list_lock); - ceph_destroy_options(opt); + ceph_destroy_options(ceph_opts); kfree(rbd_opts); return rbdc; } spin_unlock(&rbd_client_list_lock); - rbdc = rbd_client_create(opt, rbd_opts); + rbdc = rbd_client_create(ceph_opts, rbd_opts); if (IS_ERR(rbdc)) kfree(rbd_opts); @@ -480,46 +480,60 @@ static void rbd_coll_release(struct kref *kref) kfree(coll); } +static bool rbd_dev_ondisk_valid(struct rbd_image_header_ondisk *ondisk) +{ + return !memcmp(&ondisk->text, + RBD_HEADER_TEXT, sizeof (RBD_HEADER_TEXT)); +} + /* * Create a new header structure, translate header format from the on-disk * header. */ static int rbd_header_from_disk(struct rbd_image_header *header, struct rbd_image_header_ondisk *ondisk, - u32 allocated_snaps, - gfp_t gfp_flags) + u32 allocated_snaps) { - u32 i, snap_count; + u32 snap_count; - if (memcmp(ondisk, RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT))) + if (!rbd_dev_ondisk_valid(ondisk)) return -ENXIO; snap_count = le32_to_cpu(ondisk->snap_count); - if (snap_count > (UINT_MAX - sizeof(struct ceph_snap_context)) - / sizeof (*ondisk)) + if (snap_count > (SIZE_MAX - sizeof(struct ceph_snap_context)) + / sizeof (u64)) return -EINVAL; header->snapc = kmalloc(sizeof(struct ceph_snap_context) + snap_count * sizeof(u64), - gfp_flags); + GFP_KERNEL); if (!header->snapc) return -ENOMEM; - header->snap_names_len = le64_to_cpu(ondisk->snap_names_len); if (snap_count) { + header->snap_names_len = le64_to_cpu(ondisk->snap_names_len); header->snap_names = kmalloc(header->snap_names_len, - gfp_flags); + GFP_KERNEL); if (!header->snap_names) goto err_snapc; header->snap_sizes = kmalloc(snap_count * sizeof(u64), - gfp_flags); + GFP_KERNEL); if (!header->snap_sizes) goto err_names; } else { + WARN_ON(ondisk->snap_names_len); + header->snap_names_len = 0; header->snap_names = NULL; header->snap_sizes = NULL; } - memcpy(header->block_name, ondisk->block_name, + + header->object_prefix = kmalloc(sizeof (ondisk->block_name) + 1, + GFP_KERNEL); + if (!header->object_prefix) + goto err_sizes; + + memcpy(header->object_prefix, ondisk->block_name, sizeof(ondisk->block_name)); + header->object_prefix[sizeof (ondisk->block_name)] = '\0'; header->image_size = le64_to_cpu(ondisk->image_size); header->obj_order = ondisk->options.order; @@ -527,11 +541,13 @@ static int rbd_header_from_disk(struct rbd_image_header *header, header->comp_type = ondisk->options.comp_type; atomic_set(&header->snapc->nref, 1); - header->snap_seq = le64_to_cpu(ondisk->snap_seq); + header->snapc->seq = le64_to_cpu(ondisk->snap_seq); header->snapc->num_snaps = snap_count; header->total_snaps = snap_count; if (snap_count && allocated_snaps == snap_count) { + int i; + for (i = 0; i < snap_count; i++) { header->snapc->snaps[i] = le64_to_cpu(ondisk->snaps[i].id); @@ -540,16 +556,22 @@ static int rbd_header_from_disk(struct rbd_image_header *header, } /* copy snapshot names */ - memcpy(header->snap_names, &ondisk->snaps[i], + memcpy(header->snap_names, &ondisk->snaps[snap_count], header->snap_names_len); } return 0; +err_sizes: + kfree(header->snap_sizes); + header->snap_sizes = NULL; err_names: kfree(header->snap_names); + header->snap_names = NULL; err_snapc: kfree(header->snapc); + header->snapc = NULL; + return -ENOMEM; } @@ -575,52 +597,50 @@ static int snap_by_name(struct rbd_image_header *header, const char *snap_name, return -ENOENT; } -static int rbd_header_set_snap(struct rbd_device *dev, u64 *size) +static int rbd_header_set_snap(struct rbd_device *rbd_dev, u64 *size) { - struct rbd_image_header *header = &dev->header; - struct ceph_snap_context *snapc = header->snapc; - int ret = -ENOENT; - - BUILD_BUG_ON(sizeof (dev->snap_name) < sizeof (RBD_SNAP_HEAD_NAME)); + int ret; - down_write(&dev->header_rwsem); + down_write(&rbd_dev->header_rwsem); - if (!memcmp(dev->snap_name, RBD_SNAP_HEAD_NAME, + if (!memcmp(rbd_dev->snap_name, RBD_SNAP_HEAD_NAME, sizeof (RBD_SNAP_HEAD_NAME))) { - if (header->total_snaps) - snapc->seq = header->snap_seq; - else - snapc->seq = 0; - dev->snap_id = CEPH_NOSNAP; - dev->read_only = 0; + rbd_dev->snap_id = CEPH_NOSNAP; + rbd_dev->snap_exists = false; + rbd_dev->read_only = 0; if (size) - *size = header->image_size; + *size = rbd_dev->header.image_size; } else { - ret = snap_by_name(header, dev->snap_name, &snapc->seq, size); + u64 snap_id = 0; + + ret = snap_by_name(&rbd_dev->header, rbd_dev->snap_name, + &snap_id, size); if (ret < 0) goto done; - dev->snap_id = snapc->seq; - dev->read_only = 1; + rbd_dev->snap_id = snap_id; + rbd_dev->snap_exists = true; + rbd_dev->read_only = 1; } ret = 0; done: - up_write(&dev->header_rwsem); + up_write(&rbd_dev->header_rwsem); return ret; } static void rbd_header_free(struct rbd_image_header *header) { - kfree(header->snapc); - kfree(header->snap_names); + kfree(header->object_prefix); kfree(header->snap_sizes); + kfree(header->snap_names); + ceph_put_snap_context(header->snapc); } /* * get the actual striped segment name, offset and length */ static u64 rbd_get_segment(struct rbd_image_header *header, - const char *block_name, + const char *object_prefix, u64 ofs, u64 len, char *seg_name, u64 *segofs) { @@ -628,7 +648,7 @@ static u64 rbd_get_segment(struct rbd_image_header *header, if (seg_name) snprintf(seg_name, RBD_MAX_SEG_NAME_LEN, - "%s.%012llx", block_name, seg); + "%s.%012llx", object_prefix, seg); ofs = ofs & ((1 << header->obj_order) - 1); len = min_t(u64, len, (1 << header->obj_order) - ofs); @@ -726,9 +746,8 @@ static struct bio *bio_chain_clone(struct bio **old, struct bio **next, * split_bio will BUG_ON if this is not the case */ dout("bio_chain_clone split! total=%d remaining=%d" - "bi_size=%d\n", - (int)total, (int)len-total, - (int)old_chain->bi_size); + "bi_size=%u\n", + total, len - total, old_chain->bi_size); /* split the bio. We'll release it either in the next call, or it will have to be released outside */ @@ -777,22 +796,24 @@ err_out: /* * helpers for osd request op vectors. */ -static int rbd_create_rw_ops(struct ceph_osd_req_op **ops, - int num_ops, - int opcode, - u32 payload_len) -{ - *ops = kzalloc(sizeof(struct ceph_osd_req_op) * (num_ops + 1), - GFP_NOIO); - if (!*ops) - return -ENOMEM; - (*ops)[0].op = opcode; +static struct ceph_osd_req_op *rbd_create_rw_ops(int num_ops, + int opcode, u32 payload_len) +{ + struct ceph_osd_req_op *ops; + + ops = kzalloc(sizeof (*ops) * (num_ops + 1), GFP_NOIO); + if (!ops) + return NULL; + + ops[0].op = opcode; + /* * op extent offset and length will be set later on * in calc_raw_layout() */ - (*ops)[0].payload_len = payload_len; - return 0; + ops[0].payload_len = payload_len; + + return ops; } static void rbd_destroy_ops(struct ceph_osd_req_op *ops) @@ -808,8 +829,8 @@ static void rbd_coll_end_req_index(struct request *rq, struct request_queue *q; int min, max, i; - dout("rbd_coll_end_req_index %p index %d ret %d len %lld\n", - coll, index, ret, len); + dout("rbd_coll_end_req_index %p index %d ret %d len %llu\n", + coll, index, ret, (unsigned long long) len); if (!rq) return; @@ -848,16 +869,15 @@ static void rbd_coll_end_req(struct rbd_request *req, * Send ceph osd request */ static int rbd_do_request(struct request *rq, - struct rbd_device *dev, + struct rbd_device *rbd_dev, struct ceph_snap_context *snapc, u64 snapid, - const char *obj, u64 ofs, u64 len, + const char *object_name, u64 ofs, u64 len, struct bio *bio, struct page **pages, int num_pages, int flags, struct ceph_osd_req_op *ops, - int num_reply, struct rbd_req_coll *coll, int coll_index, void (*rbd_cb)(struct ceph_osd_request *req, @@ -887,15 +907,13 @@ static int rbd_do_request(struct request *rq, req_data->coll_index = coll_index; } - dout("rbd_do_request obj=%s ofs=%lld len=%lld\n", obj, len, ofs); - - down_read(&dev->header_rwsem); + dout("rbd_do_request object_name=%s ofs=%llu len=%llu\n", object_name, + (unsigned long long) ofs, (unsigned long long) len); - osdc = &dev->rbd_client->client->osdc; + osdc = &rbd_dev->rbd_client->client->osdc; req = ceph_osdc_alloc_request(osdc, flags, snapc, ops, false, GFP_NOIO, pages, bio); if (!req) { - up_read(&dev->header_rwsem); ret = -ENOMEM; goto done_pages; } @@ -912,7 +930,7 @@ static int rbd_do_request(struct request *rq, reqhead = req->r_request->front.iov_base; reqhead->snapid = cpu_to_le64(CEPH_NOSNAP); - strncpy(req->r_oid, obj, sizeof(req->r_oid)); + strncpy(req->r_oid, object_name, sizeof(req->r_oid)); req->r_oid_len = strlen(req->r_oid); layout = &req->r_file_layout; @@ -920,7 +938,7 @@ static int rbd_do_request(struct request *rq, layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); layout->fl_stripe_count = cpu_to_le32(1); layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); - layout->fl_pg_pool = cpu_to_le32(dev->poolid); + layout->fl_pg_pool = cpu_to_le32(rbd_dev->pool_id); ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno, req, ops); @@ -929,7 +947,6 @@ static int rbd_do_request(struct request *rq, snapc, &mtime, req->r_oid, req->r_oid_len); - up_read(&dev->header_rwsem); if (linger_req) { ceph_osdc_set_request_linger(osdc, req); @@ -944,8 +961,9 @@ static int rbd_do_request(struct request *rq, ret = ceph_osdc_wait_request(osdc, req); if (ver) *ver = le64_to_cpu(req->r_reassert_version.version); - dout("reassert_ver=%lld\n", - le64_to_cpu(req->r_reassert_version.version)); + dout("reassert_ver=%llu\n", + (unsigned long long) + le64_to_cpu(req->r_reassert_version.version)); ceph_osdc_put_request(req); } return ret; @@ -979,7 +997,8 @@ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) bytes = le64_to_cpu(op->extent.length); read_op = (le16_to_cpu(op->op) == CEPH_OSD_OP_READ); - dout("rbd_req_cb bytes=%lld readop=%d rc=%d\n", bytes, read_op, rc); + dout("rbd_req_cb bytes=%llu readop=%d rc=%d\n", + (unsigned long long) bytes, read_op, (int) rc); if (rc == -ENOENT && read_op) { zero_bio_chain(req_data->bio, 0); @@ -1006,14 +1025,12 @@ static void rbd_simple_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg /* * Do a synchronous ceph osd operation */ -static int rbd_req_sync_op(struct rbd_device *dev, +static int rbd_req_sync_op(struct rbd_device *rbd_dev, struct ceph_snap_context *snapc, u64 snapid, - int opcode, int flags, - struct ceph_osd_req_op *orig_ops, - int num_reply, - const char *obj, + struct ceph_osd_req_op *ops, + const char *object_name, u64 ofs, u64 len, char *buf, struct ceph_osd_request **linger_req, @@ -1022,45 +1039,28 @@ static int rbd_req_sync_op(struct rbd_device *dev, int ret; struct page **pages; int num_pages; - struct ceph_osd_req_op *ops = orig_ops; - u32 payload_len; + + BUG_ON(ops == NULL); num_pages = calc_pages_for(ofs , len); pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL); if (IS_ERR(pages)) return PTR_ERR(pages); - if (!orig_ops) { - payload_len = (flags & CEPH_OSD_FLAG_WRITE ? len : 0); - ret = rbd_create_rw_ops(&ops, 1, opcode, payload_len); - if (ret < 0) - goto done; - - if ((flags & CEPH_OSD_FLAG_WRITE) && buf) { - ret = ceph_copy_to_page_vector(pages, buf, ofs, len); - if (ret < 0) - goto done_ops; - } - } - - ret = rbd_do_request(NULL, dev, snapc, snapid, - obj, ofs, len, NULL, + ret = rbd_do_request(NULL, rbd_dev, snapc, snapid, + object_name, ofs, len, NULL, pages, num_pages, flags, ops, - 2, NULL, 0, NULL, linger_req, ver); if (ret < 0) - goto done_ops; + goto done; if ((flags & CEPH_OSD_FLAG_READ) && buf) ret = ceph_copy_from_page_vector(pages, buf, ofs, ret); -done_ops: - if (!orig_ops) - rbd_destroy_ops(ops); done: ceph_release_page_vector(pages, num_pages); return ret; @@ -1070,10 +1070,10 @@ done: * Do an asynchronous ceph osd operation */ static int rbd_do_op(struct request *rq, - struct rbd_device *rbd_dev , + struct rbd_device *rbd_dev, struct ceph_snap_context *snapc, u64 snapid, - int opcode, int flags, int num_reply, + int opcode, int flags, u64 ofs, u64 len, struct bio *bio, struct rbd_req_coll *coll, @@ -1091,14 +1091,15 @@ static int rbd_do_op(struct request *rq, return -ENOMEM; seg_len = rbd_get_segment(&rbd_dev->header, - rbd_dev->header.block_name, + rbd_dev->header.object_prefix, ofs, len, seg_name, &seg_ofs); payload_len = (flags & CEPH_OSD_FLAG_WRITE ? seg_len : 0); - ret = rbd_create_rw_ops(&ops, 1, opcode, payload_len); - if (ret < 0) + ret = -ENOMEM; + ops = rbd_create_rw_ops(1, opcode, payload_len); + if (!ops) goto done; /* we've taken care of segment sizes earlier when we @@ -1112,7 +1113,6 @@ static int rbd_do_op(struct request *rq, NULL, 0, flags, ops, - num_reply, coll, coll_index, rbd_req_cb, 0, NULL); @@ -1136,7 +1136,6 @@ static int rbd_req_write(struct request *rq, return rbd_do_op(rq, rbd_dev, snapc, CEPH_NOSNAP, CEPH_OSD_OP_WRITE, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - 2, ofs, len, bio, coll, coll_index); } @@ -1155,55 +1154,58 @@ static int rbd_req_read(struct request *rq, snapid, CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ, - 2, ofs, len, bio, coll, coll_index); } /* * Request sync osd read */ -static int rbd_req_sync_read(struct rbd_device *dev, - struct ceph_snap_context *snapc, +static int rbd_req_sync_read(struct rbd_device *rbd_dev, u64 snapid, - const char *obj, + const char *object_name, u64 ofs, u64 len, char *buf, u64 *ver) { - return rbd_req_sync_op(dev, NULL, + struct ceph_osd_req_op *ops; + int ret; + + ops = rbd_create_rw_ops(1, CEPH_OSD_OP_READ, 0); + if (!ops) + return -ENOMEM; + + ret = rbd_req_sync_op(rbd_dev, NULL, snapid, - CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ, - NULL, - 1, obj, ofs, len, buf, NULL, ver); + ops, object_name, ofs, len, buf, NULL, ver); + rbd_destroy_ops(ops); + + return ret; } /* * Request sync osd watch */ -static int rbd_req_sync_notify_ack(struct rbd_device *dev, +static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, u64 ver, - u64 notify_id, - const char *obj) + u64 notify_id) { struct ceph_osd_req_op *ops; - struct page **pages = NULL; int ret; - ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY_ACK, 0); - if (ret < 0) - return ret; + ops = rbd_create_rw_ops(1, CEPH_OSD_OP_NOTIFY_ACK, 0); + if (!ops) + return -ENOMEM; - ops[0].watch.ver = cpu_to_le64(dev->header.obj_version); + ops[0].watch.ver = cpu_to_le64(ver); ops[0].watch.cookie = notify_id; ops[0].watch.flag = 0; - ret = rbd_do_request(NULL, dev, NULL, CEPH_NOSNAP, - obj, 0, 0, NULL, - pages, 0, + ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, + rbd_dev->header_name, 0, 0, NULL, + NULL, 0, CEPH_OSD_FLAG_READ, ops, - 1, NULL, 0, rbd_simple_req_cb, 0, NULL); @@ -1213,54 +1215,53 @@ static int rbd_req_sync_notify_ack(struct rbd_device *dev, static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) { - struct rbd_device *dev = (struct rbd_device *)data; + struct rbd_device *rbd_dev = (struct rbd_device *)data; + u64 hver; int rc; - if (!dev) + if (!rbd_dev) return; - dout("rbd_watch_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name, - notify_id, (int)opcode); - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - rc = __rbd_refresh_header(dev); - mutex_unlock(&ctl_mutex); + dout("rbd_watch_cb %s notify_id=%llu opcode=%u\n", + rbd_dev->header_name, (unsigned long long) notify_id, + (unsigned int) opcode); + rc = rbd_refresh_header(rbd_dev, &hver); if (rc) pr_warning(RBD_DRV_NAME "%d got notification but failed to " - " update snaps: %d\n", dev->major, rc); + " update snaps: %d\n", rbd_dev->major, rc); - rbd_req_sync_notify_ack(dev, ver, notify_id, dev->obj_md_name); + rbd_req_sync_notify_ack(rbd_dev, hver, notify_id); } /* * Request sync osd watch */ -static int rbd_req_sync_watch(struct rbd_device *dev, - const char *obj, - u64 ver) +static int rbd_req_sync_watch(struct rbd_device *rbd_dev) { struct ceph_osd_req_op *ops; - struct ceph_osd_client *osdc = &dev->rbd_client->client->osdc; + struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; + int ret; - int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0); - if (ret < 0) - return ret; + ops = rbd_create_rw_ops(1, CEPH_OSD_OP_WATCH, 0); + if (!ops) + return -ENOMEM; ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, - (void *)dev, &dev->watch_event); + (void *)rbd_dev, &rbd_dev->watch_event); if (ret < 0) goto fail; - ops[0].watch.ver = cpu_to_le64(ver); - ops[0].watch.cookie = cpu_to_le64(dev->watch_event->cookie); + ops[0].watch.ver = cpu_to_le64(rbd_dev->header.obj_version); + ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); ops[0].watch.flag = 1; - ret = rbd_req_sync_op(dev, NULL, + ret = rbd_req_sync_op(rbd_dev, NULL, CEPH_NOSNAP, - 0, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, ops, - 1, obj, 0, 0, NULL, - &dev->watch_request, NULL); + rbd_dev->header_name, + 0, 0, NULL, + &rbd_dev->watch_request, NULL); if (ret < 0) goto fail_event; @@ -1269,8 +1270,8 @@ static int rbd_req_sync_watch(struct rbd_device *dev, return 0; fail_event: - ceph_osdc_cancel_event(dev->watch_event); - dev->watch_event = NULL; + ceph_osdc_cancel_event(rbd_dev->watch_event); + rbd_dev->watch_event = NULL; fail: rbd_destroy_ops(ops); return ret; @@ -1279,64 +1280,65 @@ fail: /* * Request sync osd unwatch */ -static int rbd_req_sync_unwatch(struct rbd_device *dev, - const char *obj) +static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev) { struct ceph_osd_req_op *ops; + int ret; - int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0); - if (ret < 0) - return ret; + ops = rbd_create_rw_ops(1, CEPH_OSD_OP_WATCH, 0); + if (!ops) + return -ENOMEM; ops[0].watch.ver = 0; - ops[0].watch.cookie = cpu_to_le64(dev->watch_event->cookie); + ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); ops[0].watch.flag = 0; - ret = rbd_req_sync_op(dev, NULL, + ret = rbd_req_sync_op(rbd_dev, NULL, CEPH_NOSNAP, - 0, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, ops, - 1, obj, 0, 0, NULL, NULL, NULL); + rbd_dev->header_name, + 0, 0, NULL, NULL, NULL); + rbd_destroy_ops(ops); - ceph_osdc_cancel_event(dev->watch_event); - dev->watch_event = NULL; + ceph_osdc_cancel_event(rbd_dev->watch_event); + rbd_dev->watch_event = NULL; return ret; } struct rbd_notify_info { - struct rbd_device *dev; + struct rbd_device *rbd_dev; }; static void rbd_notify_cb(u64 ver, u64 notify_id, u8 opcode, void *data) { - struct rbd_device *dev = (struct rbd_device *)data; - if (!dev) + struct rbd_device *rbd_dev = (struct rbd_device *)data; + if (!rbd_dev) return; - dout("rbd_notify_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name, - notify_id, (int)opcode); + dout("rbd_notify_cb %s notify_id=%llu opcode=%u\n", + rbd_dev->header_name, (unsigned long long) notify_id, + (unsigned int) opcode); } /* * Request sync osd notify */ -static int rbd_req_sync_notify(struct rbd_device *dev, - const char *obj) +static int rbd_req_sync_notify(struct rbd_device *rbd_dev) { struct ceph_osd_req_op *ops; - struct ceph_osd_client *osdc = &dev->rbd_client->client->osdc; + struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; struct ceph_osd_event *event; struct rbd_notify_info info; int payload_len = sizeof(u32) + sizeof(u32); int ret; - ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY, payload_len); - if (ret < 0) - return ret; + ops = rbd_create_rw_ops(1, CEPH_OSD_OP_NOTIFY, payload_len); + if (!ops) + return -ENOMEM; - info.dev = dev; + info.rbd_dev = rbd_dev; ret = ceph_osdc_create_event(osdc, rbd_notify_cb, 1, (void *)&info, &event); @@ -1349,12 +1351,12 @@ static int rbd_req_sync_notify(struct rbd_device *dev, ops[0].watch.prot_ver = RADOS_NOTIFY_VER; ops[0].watch.timeout = 12; - ret = rbd_req_sync_op(dev, NULL, + ret = rbd_req_sync_op(rbd_dev, NULL, CEPH_NOSNAP, - 0, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, ops, - 1, obj, 0, 0, NULL, NULL, NULL); + rbd_dev->header_name, + 0, 0, NULL, NULL, NULL); if (ret < 0) goto fail_event; @@ -1373,36 +1375,37 @@ fail: /* * Request sync osd read */ -static int rbd_req_sync_exec(struct rbd_device *dev, - const char *obj, - const char *cls, - const char *method, +static int rbd_req_sync_exec(struct rbd_device *rbd_dev, + const char *object_name, + const char *class_name, + const char *method_name, const char *data, int len, u64 *ver) { struct ceph_osd_req_op *ops; - int cls_len = strlen(cls); - int method_len = strlen(method); - int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_CALL, - cls_len + method_len + len); - if (ret < 0) - return ret; + int class_name_len = strlen(class_name); + int method_name_len = strlen(method_name); + int ret; - ops[0].cls.class_name = cls; - ops[0].cls.class_len = (__u8)cls_len; - ops[0].cls.method_name = method; - ops[0].cls.method_len = (__u8)method_len; + ops = rbd_create_rw_ops(1, CEPH_OSD_OP_CALL, + class_name_len + method_name_len + len); + if (!ops) + return -ENOMEM; + + ops[0].cls.class_name = class_name; + ops[0].cls.class_len = (__u8) class_name_len; + ops[0].cls.method_name = method_name; + ops[0].cls.method_len = (__u8) method_name_len; ops[0].cls.argc = 0; ops[0].cls.indata = data; ops[0].cls.indata_len = len; - ret = rbd_req_sync_op(dev, NULL, + ret = rbd_req_sync_op(rbd_dev, NULL, CEPH_NOSNAP, - 0, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, ops, - 1, obj, 0, 0, NULL, NULL, ver); + object_name, 0, 0, NULL, NULL, ver); rbd_destroy_ops(ops); @@ -1437,10 +1440,12 @@ static void rbd_rq_fn(struct request_queue *q) struct bio *bio; struct bio *rq_bio, *next_bio = NULL; bool do_write; - int size, op_size = 0; + unsigned int size; + u64 op_size = 0; u64 ofs; int num_segs, cur_seg = 0; struct rbd_req_coll *coll; + struct ceph_snap_context *snapc; /* peek at request from block layer */ if (!rq) @@ -1467,23 +1472,38 @@ static void rbd_rq_fn(struct request_queue *q) spin_unlock_irq(q->queue_lock); + down_read(&rbd_dev->header_rwsem); + + if (rbd_dev->snap_id != CEPH_NOSNAP && !rbd_dev->snap_exists) { + up_read(&rbd_dev->header_rwsem); + dout("request for non-existent snapshot"); + spin_lock_irq(q->queue_lock); + __blk_end_request_all(rq, -ENXIO); + continue; + } + + snapc = ceph_get_snap_context(rbd_dev->header.snapc); + + up_read(&rbd_dev->header_rwsem); + dout("%s 0x%x bytes at 0x%llx\n", do_write ? "write" : "read", - size, blk_rq_pos(rq) * SECTOR_SIZE); + size, (unsigned long long) blk_rq_pos(rq) * SECTOR_SIZE); num_segs = rbd_get_num_segments(&rbd_dev->header, ofs, size); coll = rbd_alloc_coll(num_segs); if (!coll) { spin_lock_irq(q->queue_lock); __blk_end_request_all(rq, -ENOMEM); + ceph_put_snap_context(snapc); continue; } do { /* a bio clone to be passed down to OSD req */ - dout("rq->bio->bi_vcnt=%d\n", rq->bio->bi_vcnt); + dout("rq->bio->bi_vcnt=%hu\n", rq->bio->bi_vcnt); op_size = rbd_get_segment(&rbd_dev->header, - rbd_dev->header.block_name, + rbd_dev->header.object_prefix, ofs, size, NULL, NULL); kref_get(&coll->kref); @@ -1499,7 +1519,7 @@ static void rbd_rq_fn(struct request_queue *q) /* init OSD command: write or read */ if (do_write) rbd_req_write(rq, rbd_dev, - rbd_dev->header.snapc, + snapc, ofs, op_size, bio, coll, cur_seg); @@ -1522,6 +1542,8 @@ next_seg: if (bp) bio_pair_release(bp); spin_lock_irq(q->queue_lock); + + ceph_put_snap_context(snapc); } } @@ -1592,18 +1614,19 @@ static int rbd_read_header(struct rbd_device *rbd_dev, return -ENOMEM; rc = rbd_req_sync_read(rbd_dev, - NULL, CEPH_NOSNAP, - rbd_dev->obj_md_name, + CEPH_NOSNAP, + rbd_dev->header_name, 0, len, (char *)dh, &ver); if (rc < 0) goto out_dh; - rc = rbd_header_from_disk(header, dh, snap_count, GFP_KERNEL); + rc = rbd_header_from_disk(header, dh, snap_count); if (rc < 0) { if (rc == -ENXIO) pr_warning("unrecognized header format" - " for image %s", rbd_dev->obj); + " for image %s\n", + rbd_dev->image_name); goto out_dh; } @@ -1628,7 +1651,7 @@ out_dh: /* * create a snapshot */ -static int rbd_header_add_snap(struct rbd_device *dev, +static int rbd_header_add_snap(struct rbd_device *rbd_dev, const char *snap_name, gfp_t gfp_flags) { @@ -1636,16 +1659,15 @@ static int rbd_header_add_snap(struct rbd_device *dev, u64 new_snapid; int ret; void *data, *p, *e; - u64 ver; struct ceph_mon_client *monc; /* we should create a snapshot only if we're pointing at the head */ - if (dev->snap_id != CEPH_NOSNAP) + if (rbd_dev->snap_id != CEPH_NOSNAP) return -EINVAL; - monc = &dev->rbd_client->client->monc; - ret = ceph_monc_create_snapid(monc, dev->poolid, &new_snapid); - dout("created snapid=%lld\n", new_snapid); + monc = &rbd_dev->rbd_client->client->monc; + ret = ceph_monc_create_snapid(monc, rbd_dev->pool_id, &new_snapid); + dout("created snapid=%llu\n", (unsigned long long) new_snapid); if (ret < 0) return ret; @@ -1659,19 +1681,13 @@ static int rbd_header_add_snap(struct rbd_device *dev, ceph_encode_string_safe(&p, e, snap_name, name_len, bad); ceph_encode_64_safe(&p, e, new_snapid, bad); - ret = rbd_req_sync_exec(dev, dev->obj_md_name, "rbd", "snap_add", - data, p - data, &ver); + ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + "rbd", "snap_add", + data, p - data, NULL); kfree(data); - if (ret < 0) - return ret; - - down_write(&dev->header_rwsem); - dev->header.snapc->seq = new_snapid; - up_write(&dev->header_rwsem); - - return 0; + return ret < 0 ? ret : 0; bad: return -ERANGE; } @@ -1679,52 +1695,52 @@ bad: static void __rbd_remove_all_snaps(struct rbd_device *rbd_dev) { struct rbd_snap *snap; + struct rbd_snap *next; - while (!list_empty(&rbd_dev->snaps)) { - snap = list_first_entry(&rbd_dev->snaps, struct rbd_snap, node); - __rbd_remove_snap_dev(rbd_dev, snap); - } + list_for_each_entry_safe(snap, next, &rbd_dev->snaps, node) + __rbd_remove_snap_dev(snap); } /* * only read the first part of the ondisk header, without the snaps info */ -static int __rbd_refresh_header(struct rbd_device *rbd_dev) +static int __rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver) { int ret; struct rbd_image_header h; - u64 snap_seq; - int follow_seq = 0; ret = rbd_read_header(rbd_dev, &h); if (ret < 0) return ret; - /* resized? */ - set_capacity(rbd_dev->disk, h.image_size / SECTOR_SIZE); - down_write(&rbd_dev->header_rwsem); - snap_seq = rbd_dev->header.snapc->seq; - if (rbd_dev->header.total_snaps && - rbd_dev->header.snapc->snaps[0] == snap_seq) - /* pointing at the head, will need to follow that - if head moves */ - follow_seq = 1; + /* resized? */ + if (rbd_dev->snap_id == CEPH_NOSNAP) { + sector_t size = (sector_t) h.image_size / SECTOR_SIZE; - kfree(rbd_dev->header.snapc); - kfree(rbd_dev->header.snap_names); + dout("setting size to %llu sectors", (unsigned long long) size); + set_capacity(rbd_dev->disk, size); + } + + /* rbd_dev->header.object_prefix shouldn't change */ kfree(rbd_dev->header.snap_sizes); + kfree(rbd_dev->header.snap_names); + /* osd requests may still refer to snapc */ + ceph_put_snap_context(rbd_dev->header.snapc); + if (hver) + *hver = h.obj_version; + rbd_dev->header.obj_version = h.obj_version; + rbd_dev->header.image_size = h.image_size; rbd_dev->header.total_snaps = h.total_snaps; rbd_dev->header.snapc = h.snapc; rbd_dev->header.snap_names = h.snap_names; rbd_dev->header.snap_names_len = h.snap_names_len; rbd_dev->header.snap_sizes = h.snap_sizes; - if (follow_seq) - rbd_dev->header.snapc->seq = rbd_dev->header.snapc->snaps[0]; - else - rbd_dev->header.snapc->seq = snap_seq; + /* Free the extra copy of the object prefix */ + WARN_ON(strcmp(rbd_dev->header.object_prefix, h.object_prefix)); + kfree(h.object_prefix); ret = __rbd_init_snaps_header(rbd_dev); @@ -1733,6 +1749,17 @@ static int __rbd_refresh_header(struct rbd_device *rbd_dev) return ret; } +static int rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver) +{ + int ret; + + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + ret = __rbd_refresh_header(rbd_dev, hver); + mutex_unlock(&ctl_mutex); + + return ret; +} + static int rbd_init_disk(struct rbd_device *rbd_dev) { struct gendisk *disk; @@ -1762,7 +1789,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) goto out; snprintf(disk->disk_name, sizeof(disk->disk_name), RBD_DRV_NAME "%d", - rbd_dev->id); + rbd_dev->dev_id); disk->major = rbd_dev->major; disk->first_minor = 0; disk->fops = &rbd_bd_ops; @@ -1819,8 +1846,13 @@ static ssize_t rbd_size_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); + sector_t size; + + down_read(&rbd_dev->header_rwsem); + size = get_capacity(rbd_dev->disk); + up_read(&rbd_dev->header_rwsem); - return sprintf(buf, "%llu\n", (unsigned long long)rbd_dev->header.image_size); + return sprintf(buf, "%llu\n", (unsigned long long) size * SECTOR_SIZE); } static ssize_t rbd_major_show(struct device *dev, @@ -1848,12 +1880,20 @@ static ssize_t rbd_pool_show(struct device *dev, return sprintf(buf, "%s\n", rbd_dev->pool_name); } +static ssize_t rbd_pool_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); + + return sprintf(buf, "%d\n", rbd_dev->pool_id); +} + static ssize_t rbd_name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - return sprintf(buf, "%s\n", rbd_dev->obj); + return sprintf(buf, "%s\n", rbd_dev->image_name); } static ssize_t rbd_snap_show(struct device *dev, @@ -1871,23 +1911,18 @@ static ssize_t rbd_image_refresh(struct device *dev, size_t size) { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - int rc; - int ret = size; - - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + int ret; - rc = __rbd_refresh_header(rbd_dev); - if (rc < 0) - ret = rc; + ret = rbd_refresh_header(rbd_dev, NULL); - mutex_unlock(&ctl_mutex); - return ret; + return ret < 0 ? ret : size; } static DEVICE_ATTR(size, S_IRUGO, rbd_size_show, NULL); static DEVICE_ATTR(major, S_IRUGO, rbd_major_show, NULL); static DEVICE_ATTR(client_id, S_IRUGO, rbd_client_id_show, NULL); static DEVICE_ATTR(pool, S_IRUGO, rbd_pool_show, NULL); +static DEVICE_ATTR(pool_id, S_IRUGO, rbd_pool_id_show, NULL); static DEVICE_ATTR(name, S_IRUGO, rbd_name_show, NULL); static DEVICE_ATTR(refresh, S_IWUSR, NULL, rbd_image_refresh); static DEVICE_ATTR(current_snap, S_IRUGO, rbd_snap_show, NULL); @@ -1898,6 +1933,7 @@ static struct attribute *rbd_attrs[] = { &dev_attr_major.attr, &dev_attr_client_id.attr, &dev_attr_pool.attr, + &dev_attr_pool_id.attr, &dev_attr_name.attr, &dev_attr_current_snap.attr, &dev_attr_refresh.attr, @@ -1977,15 +2013,13 @@ static struct device_type rbd_snap_device_type = { .release = rbd_snap_dev_release, }; -static void __rbd_remove_snap_dev(struct rbd_device *rbd_dev, - struct rbd_snap *snap) +static void __rbd_remove_snap_dev(struct rbd_snap *snap) { list_del(&snap->node); device_unregister(&snap->dev); } -static int rbd_register_snap_dev(struct rbd_device *rbd_dev, - struct rbd_snap *snap, +static int rbd_register_snap_dev(struct rbd_snap *snap, struct device *parent) { struct device *dev = &snap->dev; @@ -2000,29 +2034,36 @@ static int rbd_register_snap_dev(struct rbd_device *rbd_dev, return ret; } -static int __rbd_add_snap_dev(struct rbd_device *rbd_dev, - int i, const char *name, - struct rbd_snap **snapp) +static struct rbd_snap *__rbd_add_snap_dev(struct rbd_device *rbd_dev, + int i, const char *name) { + struct rbd_snap *snap; int ret; - struct rbd_snap *snap = kzalloc(sizeof(*snap), GFP_KERNEL); + + snap = kzalloc(sizeof (*snap), GFP_KERNEL); if (!snap) - return -ENOMEM; + return ERR_PTR(-ENOMEM); + + ret = -ENOMEM; snap->name = kstrdup(name, GFP_KERNEL); + if (!snap->name) + goto err; + snap->size = rbd_dev->header.snap_sizes[i]; snap->id = rbd_dev->header.snapc->snaps[i]; if (device_is_registered(&rbd_dev->dev)) { - ret = rbd_register_snap_dev(rbd_dev, snap, - &rbd_dev->dev); + ret = rbd_register_snap_dev(snap, &rbd_dev->dev); if (ret < 0) goto err; } - *snapp = snap; - return 0; + + return snap; + err: kfree(snap->name); kfree(snap); - return ret; + + return ERR_PTR(ret); } /* @@ -2055,7 +2096,6 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev) const char *name, *first_name; int i = rbd_dev->header.total_snaps; struct rbd_snap *snap, *old_snap = NULL; - int ret; struct list_head *p, *n; first_name = rbd_dev->header.snap_names; @@ -2070,8 +2110,15 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev) cur_id = rbd_dev->header.snapc->snaps[i - 1]; if (!i || old_snap->id < cur_id) { - /* old_snap->id was skipped, thus was removed */ - __rbd_remove_snap_dev(rbd_dev, old_snap); + /* + * old_snap->id was skipped, thus was + * removed. If this rbd_dev is mapped to + * the removed snapshot, record that it no + * longer exists, to prevent further I/O. + */ + if (rbd_dev->snap_id == old_snap->id) + rbd_dev->snap_exists = false; + __rbd_remove_snap_dev(old_snap); continue; } if (old_snap->id == cur_id) { @@ -2091,9 +2138,9 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev) if (cur_id >= old_snap->id) break; /* a new snapshot */ - ret = __rbd_add_snap_dev(rbd_dev, i - 1, name, &snap); - if (ret < 0) - return ret; + snap = __rbd_add_snap_dev(rbd_dev, i - 1, name); + if (IS_ERR(snap)) + return PTR_ERR(snap); /* note that we add it backward so using n and not p */ list_add(&snap->node, n); @@ -2107,9 +2154,9 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev) WARN_ON(1); return -EINVAL; } - ret = __rbd_add_snap_dev(rbd_dev, i - 1, name, &snap); - if (ret < 0) - return ret; + snap = __rbd_add_snap_dev(rbd_dev, i - 1, name); + if (IS_ERR(snap)) + return PTR_ERR(snap); list_add(&snap->node, &rbd_dev->snaps); } @@ -2129,14 +2176,13 @@ static int rbd_bus_add_dev(struct rbd_device *rbd_dev) dev->type = &rbd_device_type; dev->parent = &rbd_root_dev; dev->release = rbd_dev_release; - dev_set_name(dev, "%d", rbd_dev->id); + dev_set_name(dev, "%d", rbd_dev->dev_id); ret = device_register(dev); if (ret < 0) goto out; list_for_each_entry(snap, &rbd_dev->snaps, node) { - ret = rbd_register_snap_dev(rbd_dev, snap, - &rbd_dev->dev); + ret = rbd_register_snap_dev(snap, &rbd_dev->dev); if (ret < 0) break; } @@ -2155,12 +2201,9 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev) int ret, rc; do { - ret = rbd_req_sync_watch(rbd_dev, rbd_dev->obj_md_name, - rbd_dev->header.obj_version); + ret = rbd_req_sync_watch(rbd_dev); if (ret == -ERANGE) { - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - rc = __rbd_refresh_header(rbd_dev); - mutex_unlock(&ctl_mutex); + rc = rbd_refresh_header(rbd_dev, NULL); if (rc < 0) return rc; } @@ -2177,7 +2220,7 @@ static atomic64_t rbd_id_max = ATOMIC64_INIT(0); */ static void rbd_id_get(struct rbd_device *rbd_dev) { - rbd_dev->id = atomic64_inc_return(&rbd_id_max); + rbd_dev->dev_id = atomic64_inc_return(&rbd_id_max); spin_lock(&rbd_dev_list_lock); list_add_tail(&rbd_dev->node, &rbd_dev_list); @@ -2191,7 +2234,7 @@ static void rbd_id_get(struct rbd_device *rbd_dev) static void rbd_id_put(struct rbd_device *rbd_dev) { struct list_head *tmp; - int rbd_id = rbd_dev->id; + int rbd_id = rbd_dev->dev_id; int max_id; BUG_ON(rbd_id < 1); @@ -2282,19 +2325,58 @@ static inline size_t copy_token(const char **buf, } /* - * This fills in the pool_name, obj, obj_len, snap_name, obj_len, + * Finds the next token in *buf, dynamically allocates a buffer big + * enough to hold a copy of it, and copies the token into the new + * buffer. The copy is guaranteed to be terminated with '\0'. Note + * that a duplicate buffer is created even for a zero-length token. + * + * Returns a pointer to the newly-allocated duplicate, or a null + * pointer if memory for the duplicate was not available. If + * the lenp argument is a non-null pointer, the length of the token + * (not including the '\0') is returned in *lenp. + * + * If successful, the *buf pointer will be updated to point beyond + * the end of the found token. + * + * Note: uses GFP_KERNEL for allocation. + */ +static inline char *dup_token(const char **buf, size_t *lenp) +{ + char *dup; + size_t len; + + len = next_token(buf); + dup = kmalloc(len + 1, GFP_KERNEL); + if (!dup) + return NULL; + + memcpy(dup, *buf, len); + *(dup + len) = '\0'; + *buf += len; + + if (lenp) + *lenp = len; + + return dup; +} + +/* + * This fills in the pool_name, image_name, image_name_len, snap_name, * rbd_dev, rbd_md_name, and name fields of the given rbd_dev, based * on the list of monitor addresses and other options provided via * /sys/bus/rbd/add. + * + * Note: rbd_dev is assumed to have been initially zero-filled. */ static int rbd_add_parse_args(struct rbd_device *rbd_dev, const char *buf, const char **mon_addrs, size_t *mon_addrs_size, char *options, - size_t options_size) + size_t options_size) { - size_t len; + size_t len; + int ret; /* The first four tokens are required */ @@ -2310,56 +2392,74 @@ static int rbd_add_parse_args(struct rbd_device *rbd_dev, if (!len || len >= options_size) return -EINVAL; - len = copy_token(&buf, rbd_dev->pool_name, sizeof (rbd_dev->pool_name)); - if (!len || len >= sizeof (rbd_dev->pool_name)) - return -EINVAL; - - len = copy_token(&buf, rbd_dev->obj, sizeof (rbd_dev->obj)); - if (!len || len >= sizeof (rbd_dev->obj)) - return -EINVAL; + ret = -ENOMEM; + rbd_dev->pool_name = dup_token(&buf, NULL); + if (!rbd_dev->pool_name) + goto out_err; - /* We have the object length in hand, save it. */ + rbd_dev->image_name = dup_token(&buf, &rbd_dev->image_name_len); + if (!rbd_dev->image_name) + goto out_err; - rbd_dev->obj_len = len; + /* Create the name of the header object */ - BUILD_BUG_ON(RBD_MAX_MD_NAME_LEN - < RBD_MAX_OBJ_NAME_LEN + sizeof (RBD_SUFFIX)); - sprintf(rbd_dev->obj_md_name, "%s%s", rbd_dev->obj, RBD_SUFFIX); + rbd_dev->header_name = kmalloc(rbd_dev->image_name_len + + sizeof (RBD_SUFFIX), + GFP_KERNEL); + if (!rbd_dev->header_name) + goto out_err; + sprintf(rbd_dev->header_name, "%s%s", rbd_dev->image_name, RBD_SUFFIX); /* - * The snapshot name is optional, but it's an error if it's - * too long. If no snapshot is supplied, fill in the default. + * The snapshot name is optional. If none is is supplied, + * we use the default value. */ - len = copy_token(&buf, rbd_dev->snap_name, sizeof (rbd_dev->snap_name)); - if (!len) + rbd_dev->snap_name = dup_token(&buf, &len); + if (!rbd_dev->snap_name) + goto out_err; + if (!len) { + /* Replace the empty name with the default */ + kfree(rbd_dev->snap_name); + rbd_dev->snap_name + = kmalloc(sizeof (RBD_SNAP_HEAD_NAME), GFP_KERNEL); + if (!rbd_dev->snap_name) + goto out_err; + memcpy(rbd_dev->snap_name, RBD_SNAP_HEAD_NAME, sizeof (RBD_SNAP_HEAD_NAME)); - else if (len >= sizeof (rbd_dev->snap_name)) - return -EINVAL; + } return 0; + +out_err: + kfree(rbd_dev->header_name); + kfree(rbd_dev->image_name); + kfree(rbd_dev->pool_name); + rbd_dev->pool_name = NULL; + + return ret; } static ssize_t rbd_add(struct bus_type *bus, const char *buf, size_t count) { - struct rbd_device *rbd_dev; + char *options; + struct rbd_device *rbd_dev = NULL; const char *mon_addrs = NULL; size_t mon_addrs_size = 0; - char *options = NULL; struct ceph_osd_client *osdc; int rc = -ENOMEM; if (!try_module_get(THIS_MODULE)) return -ENODEV; - rbd_dev = kzalloc(sizeof(*rbd_dev), GFP_KERNEL); - if (!rbd_dev) - goto err_nomem; options = kmalloc(count, GFP_KERNEL); if (!options) goto err_nomem; + rbd_dev = kzalloc(sizeof(*rbd_dev), GFP_KERNEL); + if (!rbd_dev) + goto err_nomem; /* static rbd_device initialization */ spin_lock_init(&rbd_dev->lock); @@ -2367,15 +2467,13 @@ static ssize_t rbd_add(struct bus_type *bus, INIT_LIST_HEAD(&rbd_dev->snaps); init_rwsem(&rbd_dev->header_rwsem); - init_rwsem(&rbd_dev->header_rwsem); - /* generate unique id: find highest unique id, add one */ rbd_id_get(rbd_dev); /* Fill in the device name, now that we have its id. */ BUILD_BUG_ON(DEV_NAME_LEN < sizeof (RBD_DRV_NAME) + MAX_INT_FORMAT_WIDTH); - sprintf(rbd_dev->name, "%s%d", RBD_DRV_NAME, rbd_dev->id); + sprintf(rbd_dev->name, "%s%d", RBD_DRV_NAME, rbd_dev->dev_id); /* parse add command */ rc = rbd_add_parse_args(rbd_dev, buf, &mon_addrs, &mon_addrs_size, @@ -2395,7 +2493,7 @@ static ssize_t rbd_add(struct bus_type *bus, rc = ceph_pg_poolid_by_name(osdc->osdmap, rbd_dev->pool_name); if (rc < 0) goto err_out_client; - rbd_dev->poolid = rc; + rbd_dev->pool_id = rc; /* register our block device */ rc = register_blkdev(0, rbd_dev->name); @@ -2435,10 +2533,16 @@ err_out_blkdev: err_out_client: rbd_put_client(rbd_dev); err_put_id: + if (rbd_dev->pool_name) { + kfree(rbd_dev->snap_name); + kfree(rbd_dev->header_name); + kfree(rbd_dev->image_name); + kfree(rbd_dev->pool_name); + } rbd_id_put(rbd_dev); err_nomem: - kfree(options); kfree(rbd_dev); + kfree(options); dout("Error adding device %s\n", buf); module_put(THIS_MODULE); @@ -2446,7 +2550,7 @@ err_nomem: return (ssize_t) rc; } -static struct rbd_device *__rbd_get_dev(unsigned long id) +static struct rbd_device *__rbd_get_dev(unsigned long dev_id) { struct list_head *tmp; struct rbd_device *rbd_dev; @@ -2454,7 +2558,7 @@ static struct rbd_device *__rbd_get_dev(unsigned long id) spin_lock(&rbd_dev_list_lock); list_for_each(tmp, &rbd_dev_list) { rbd_dev = list_entry(tmp, struct rbd_device, node); - if (rbd_dev->id == id) { + if (rbd_dev->dev_id == dev_id) { spin_unlock(&rbd_dev_list_lock); return rbd_dev; } @@ -2474,7 +2578,7 @@ static void rbd_dev_release(struct device *dev) rbd_dev->watch_request); } if (rbd_dev->watch_event) - rbd_req_sync_unwatch(rbd_dev, rbd_dev->obj_md_name); + rbd_req_sync_unwatch(rbd_dev); rbd_put_client(rbd_dev); @@ -2483,6 +2587,10 @@ static void rbd_dev_release(struct device *dev) unregister_blkdev(rbd_dev->major, rbd_dev->name); /* done with the id, and with the rbd_dev */ + kfree(rbd_dev->snap_name); + kfree(rbd_dev->header_name); + kfree(rbd_dev->pool_name); + kfree(rbd_dev->image_name); rbd_id_put(rbd_dev); kfree(rbd_dev); @@ -2544,7 +2652,7 @@ static ssize_t rbd_snap_add(struct device *dev, if (ret < 0) goto err_unlock; - ret = __rbd_refresh_header(rbd_dev); + ret = __rbd_refresh_header(rbd_dev, NULL); if (ret < 0) goto err_unlock; @@ -2553,7 +2661,7 @@ static ssize_t rbd_snap_add(struct device *dev, mutex_unlock(&ctl_mutex); /* make a best effort, don't error if failed */ - rbd_req_sync_notify(rbd_dev, rbd_dev->obj_md_name); + rbd_req_sync_notify(rbd_dev); ret = count; kfree(name); diff --git a/drivers/block/rbd_types.h b/drivers/block/rbd_types.h index 950708688f17..0924e9e41a60 100644 --- a/drivers/block/rbd_types.h +++ b/drivers/block/rbd_types.h @@ -31,7 +31,6 @@ #define RBD_MIN_OBJ_ORDER 16 #define RBD_MAX_OBJ_ORDER 30 -#define RBD_MAX_OBJ_NAME_LEN 96 #define RBD_MAX_SEG_NAME_LEN 128 #define RBD_COMP_NONE 0 diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 693187df7601..c0bbeb470754 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -21,8 +21,6 @@ struct workqueue_struct *virtblk_wq; struct virtio_blk { - spinlock_t lock; - struct virtio_device *vdev; struct virtqueue *vq; @@ -65,7 +63,7 @@ static void blk_done(struct virtqueue *vq) unsigned int len; unsigned long flags; - spin_lock_irqsave(&vblk->lock, flags); + spin_lock_irqsave(vblk->disk->queue->queue_lock, flags); while ((vbr = virtqueue_get_buf(vblk->vq, &len)) != NULL) { int error; @@ -99,7 +97,7 @@ static void blk_done(struct virtqueue *vq) } /* In case queue is stopped waiting for more buffers. */ blk_start_queue(vblk->disk->queue); - spin_unlock_irqrestore(&vblk->lock, flags); + spin_unlock_irqrestore(vblk->disk->queue->queue_lock, flags); } static bool do_req(struct request_queue *q, struct virtio_blk *vblk, @@ -397,6 +395,83 @@ static int virtblk_name_format(char *prefix, int index, char *buf, int buflen) return 0; } +static int virtblk_get_cache_mode(struct virtio_device *vdev) +{ + u8 writeback; + int err; + + err = virtio_config_val(vdev, VIRTIO_BLK_F_CONFIG_WCE, + offsetof(struct virtio_blk_config, wce), + &writeback); + if (err) + writeback = virtio_has_feature(vdev, VIRTIO_BLK_F_WCE); + + return writeback; +} + +static void virtblk_update_cache_mode(struct virtio_device *vdev) +{ + u8 writeback = virtblk_get_cache_mode(vdev); + struct virtio_blk *vblk = vdev->priv; + + if (writeback) + blk_queue_flush(vblk->disk->queue, REQ_FLUSH); + else + blk_queue_flush(vblk->disk->queue, 0); + + revalidate_disk(vblk->disk); +} + +static const char *const virtblk_cache_types[] = { + "write through", "write back" +}; + +static ssize_t +virtblk_cache_type_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gendisk *disk = dev_to_disk(dev); + struct virtio_blk *vblk = disk->private_data; + struct virtio_device *vdev = vblk->vdev; + int i; + u8 writeback; + + BUG_ON(!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_CONFIG_WCE)); + for (i = ARRAY_SIZE(virtblk_cache_types); --i >= 0; ) + if (sysfs_streq(buf, virtblk_cache_types[i])) + break; + + if (i < 0) + return -EINVAL; + + writeback = i; + vdev->config->set(vdev, + offsetof(struct virtio_blk_config, wce), + &writeback, sizeof(writeback)); + + virtblk_update_cache_mode(vdev); + return count; +} + +static ssize_t +virtblk_cache_type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gendisk *disk = dev_to_disk(dev); + struct virtio_blk *vblk = disk->private_data; + u8 writeback = virtblk_get_cache_mode(vblk->vdev); + + BUG_ON(writeback >= ARRAY_SIZE(virtblk_cache_types)); + return snprintf(buf, 40, "%s\n", virtblk_cache_types[writeback]); +} + +static const struct device_attribute dev_attr_cache_type_ro = + __ATTR(cache_type, S_IRUGO, + virtblk_cache_type_show, NULL); +static const struct device_attribute dev_attr_cache_type_rw = + __ATTR(cache_type, S_IRUGO|S_IWUSR, + virtblk_cache_type_show, virtblk_cache_type_store); + static int __devinit virtblk_probe(struct virtio_device *vdev) { struct virtio_blk *vblk; @@ -431,7 +506,6 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) goto out_free_index; } - spin_lock_init(&vblk->lock); vblk->vdev = vdev; vblk->sg_elems = sg_elems; sg_init_table(vblk->sg, vblk->sg_elems); @@ -456,7 +530,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) goto out_mempool; } - q = vblk->disk->queue = blk_init_queue(do_virtblk_request, &vblk->lock); + q = vblk->disk->queue = blk_init_queue(do_virtblk_request, NULL); if (!q) { err = -ENOMEM; goto out_put_disk; @@ -474,8 +548,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) vblk->index = index; /* configure queue flush support */ - if (virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH)) - blk_queue_flush(q, REQ_FLUSH); + virtblk_update_cache_mode(vdev); /* If disk is read-only in the host, the guest should obey */ if (virtio_has_feature(vdev, VIRTIO_BLK_F_RO)) @@ -553,6 +626,14 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) if (err) goto out_del_disk; + if (virtio_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE)) + err = device_create_file(disk_to_dev(vblk->disk), + &dev_attr_cache_type_rw); + else + err = device_create_file(disk_to_dev(vblk->disk), + &dev_attr_cache_type_ro); + if (err) + goto out_del_disk; return 0; out_del_disk: @@ -576,30 +657,20 @@ static void __devexit virtblk_remove(struct virtio_device *vdev) { struct virtio_blk *vblk = vdev->priv; int index = vblk->index; - struct virtblk_req *vbr; - unsigned long flags; /* Prevent config work handler from accessing the device. */ mutex_lock(&vblk->config_lock); vblk->config_enable = false; mutex_unlock(&vblk->config_lock); + del_gendisk(vblk->disk); + blk_cleanup_queue(vblk->disk->queue); + /* Stop all the virtqueues. */ vdev->config->reset(vdev); flush_work(&vblk->config_work); - del_gendisk(vblk->disk); - - /* Abort requests dispatched to driver. */ - spin_lock_irqsave(&vblk->lock, flags); - while ((vbr = virtqueue_detach_unused_buf(vblk->vq))) { - __blk_end_request_all(vbr->req, -EIO); - mempool_free(vbr, vblk->pool); - } - spin_unlock_irqrestore(&vblk->lock, flags); - - blk_cleanup_queue(vblk->disk->queue); put_disk(vblk->disk); mempool_destroy(vblk->pool); vdev->config->del_vqs(vdev); @@ -655,7 +726,7 @@ static const struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE, VIRTIO_BLK_F_SCSI, - VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY + VIRTIO_BLK_F_WCE, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE }; /* diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index b01d67328243..7c0d391996b5 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -73,6 +73,20 @@ config HW_RANDOM_ATMEL If unsure, say Y. +config HW_RANDOM_BCM63XX + tristate "Broadcom BCM63xx Random Number Generator support" + depends on HW_RANDOM && BCM63XX + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on the Broadcom BCM63xx SoCs. + + To compile this driver as a module, choose M here: the + module will be called bcm63xx-rng + + If unusure, say Y. + + config HW_RANDOM_GEODE tristate "AMD Geode HW Random Number Generator support" depends on HW_RANDOM && X86_32 && PCI diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 8d6d173b65e6..39a757ca15b6 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o +obj-$(CONFIG_HW_RANDOM_BCM63XX) += bcm63xx-rng.o obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o n2-rng-y := n2-drv.o n2-asm.o diff --git a/drivers/char/hw_random/bcm63xx-rng.c b/drivers/char/hw_random/bcm63xx-rng.c new file mode 100644 index 000000000000..aec6a4277caa --- /dev/null +++ b/drivers/char/hw_random/bcm63xx-rng.c @@ -0,0 +1,175 @@ +/* + * Broadcom BCM63xx Random Number Generator support + * + * Copyright (C) 2011, Florian Fainelli <florian@openwrt.org> + * Copyright (C) 2009, Broadcom Corporation + * + */ +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/hw_random.h> + +#include <bcm63xx_io.h> +#include <bcm63xx_regs.h> + +struct bcm63xx_rng_priv { + struct clk *clk; + void __iomem *regs; +}; + +#define to_rng_priv(rng) ((struct bcm63xx_rng_priv *)rng->priv) + +static int bcm63xx_rng_init(struct hwrng *rng) +{ + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + u32 val; + + val = bcm_readl(priv->regs + RNG_CTRL); + val |= RNG_EN; + bcm_writel(val, priv->regs + RNG_CTRL); + + return 0; +} + +static void bcm63xx_rng_cleanup(struct hwrng *rng) +{ + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + u32 val; + + val = bcm_readl(priv->regs + RNG_CTRL); + val &= ~RNG_EN; + bcm_writel(val, priv->regs + RNG_CTRL); +} + +static int bcm63xx_rng_data_present(struct hwrng *rng, int wait) +{ + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + + return bcm_readl(priv->regs + RNG_STAT) & RNG_AVAIL_MASK; +} + +static int bcm63xx_rng_data_read(struct hwrng *rng, u32 *data) +{ + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + + *data = bcm_readl(priv->regs + RNG_DATA); + + return 4; +} + +static int __devinit bcm63xx_rng_probe(struct platform_device *pdev) +{ + struct resource *r; + struct clk *clk; + int ret; + struct bcm63xx_rng_priv *priv; + struct hwrng *rng; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no iomem resource\n"); + ret = -ENXIO; + goto out; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "no memory for private structure\n"); + ret = -ENOMEM; + goto out; + } + + rng = kzalloc(sizeof(*rng), GFP_KERNEL); + if (!rng) { + dev_err(&pdev->dev, "no memory for rng structure\n"); + ret = -ENOMEM; + goto out_free_priv; + } + + platform_set_drvdata(pdev, rng); + rng->priv = (unsigned long)priv; + rng->name = pdev->name; + rng->init = bcm63xx_rng_init; + rng->cleanup = bcm63xx_rng_cleanup; + rng->data_present = bcm63xx_rng_data_present; + rng->data_read = bcm63xx_rng_data_read; + + clk = clk_get(&pdev->dev, "ipsec"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "no clock for device\n"); + ret = PTR_ERR(clk); + goto out_free_rng; + } + + priv->clk = clk; + + if (!devm_request_mem_region(&pdev->dev, r->start, + resource_size(r), pdev->name)) { + dev_err(&pdev->dev, "request mem failed"); + ret = -ENOMEM; + goto out_free_rng; + } + + priv->regs = devm_ioremap_nocache(&pdev->dev, r->start, + resource_size(r)); + if (!priv->regs) { + dev_err(&pdev->dev, "ioremap failed"); + ret = -ENOMEM; + goto out_free_rng; + } + + clk_enable(clk); + + ret = hwrng_register(rng); + if (ret) { + dev_err(&pdev->dev, "failed to register rng device\n"); + goto out_clk_disable; + } + + dev_info(&pdev->dev, "registered RNG driver\n"); + + return 0; + +out_clk_disable: + clk_disable(clk); +out_free_rng: + platform_set_drvdata(pdev, NULL); + kfree(rng); +out_free_priv: + kfree(priv); +out: + return ret; +} + +static int __devexit bcm63xx_rng_remove(struct platform_device *pdev) +{ + struct hwrng *rng = platform_get_drvdata(pdev); + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + + hwrng_unregister(rng); + clk_disable(priv->clk); + kfree(priv); + kfree(rng); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver bcm63xx_rng_driver = { + .probe = bcm63xx_rng_probe, + .remove = __devexit_p(bcm63xx_rng_remove), + .driver = { + .name = "bcm63xx-rng", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(bcm63xx_rng_driver); + +MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); +MODULE_DESCRIPTION("Broadcom BCM63xx RNG driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 723725bbb96b..5708299507d0 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -55,6 +55,7 @@ static void register_buffer(u8 *buf, size_t size) static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) { + int ret; if (!busy) { busy = true; @@ -65,7 +66,9 @@ static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) if (!wait) return 0; - wait_for_completion(&have_data); + ret = wait_for_completion_killable(&have_data); + if (ret < 0) + return ret; busy = false; @@ -85,7 +88,7 @@ static struct hwrng virtio_hwrng = { .read = virtio_read, }; -static int virtrng_probe(struct virtio_device *vdev) +static int probe_common(struct virtio_device *vdev) { int err; @@ -103,13 +106,37 @@ static int virtrng_probe(struct virtio_device *vdev) return 0; } -static void __devexit virtrng_remove(struct virtio_device *vdev) +static void remove_common(struct virtio_device *vdev) { vdev->config->reset(vdev); + busy = false; hwrng_unregister(&virtio_hwrng); vdev->config->del_vqs(vdev); } +static int virtrng_probe(struct virtio_device *vdev) +{ + return probe_common(vdev); +} + +static void __devexit virtrng_remove(struct virtio_device *vdev) +{ + remove_common(vdev); +} + +#ifdef CONFIG_PM +static int virtrng_freeze(struct virtio_device *vdev) +{ + remove_common(vdev); + return 0; +} + +static int virtrng_restore(struct virtio_device *vdev) +{ + return probe_common(vdev); +} +#endif + static struct virtio_device_id id_table[] = { { VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID }, { 0 }, @@ -121,6 +148,10 @@ static struct virtio_driver virtio_rng_driver = { .id_table = id_table, .probe = virtrng_probe, .remove = __devexit_p(virtrng_remove), +#ifdef CONFIG_PM + .freeze = virtrng_freeze, + .restore = virtrng_restore, +#endif }; static int __init init(void) diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index 8b78750f1efe..845f97fd1832 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -283,7 +283,7 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma, vdata->flags = flags; vdata->type = type; spin_lock_init(&vdata->lock); - vdata->refcnt = ATOMIC_INIT(1); + atomic_set(&vdata->refcnt, 1); vma->vm_private_data = vdata; vma->vm_flags |= (VM_IO | VM_RESERVED | VM_PFNMAP | VM_DONTEXPAND); diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 3f99b9099658..7f0b5ca78516 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -25,7 +25,6 @@ menu "Common Clock Framework" config COMMON_CLK_DEBUG bool "DebugFS representation of clock tree" - depends on COMMON_CLK select DEBUG_FS ---help--- Creates a directory hierchy in debugfs for visualizing the clk diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index c87fdd710560..efdfd009c270 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -465,6 +465,9 @@ static void __clk_disable(struct clk *clk) if (!clk) return; + if (WARN_ON(IS_ERR(clk))) + return; + if (WARN_ON(clk->enable_count == 0)) return; diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c index a88331644ebf..e64c253cb169 100644 --- a/drivers/cpufreq/exynos5250-cpufreq.c +++ b/drivers/cpufreq/exynos5250-cpufreq.c @@ -65,20 +65,20 @@ static unsigned int clkdiv_cpu0_5250[CPUFREQ_LEVEL_END][8] = { * Clock divider value for following * { ARM, CPUD, ACP, PERIPH, ATB, PCLK_DBG, APLL, ARM2 } */ - { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1700 MHz - N/A */ - { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1600 MHz - N/A */ - { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1500 MHz - N/A */ - { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1400 MHz */ - { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1300 MHz */ - { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1200 MHz */ - { 0, 2, 7, 7, 5, 1, 2, 0 }, /* 1100 MHz */ - { 0, 2, 7, 7, 4, 1, 2, 0 }, /* 1000 MHz */ - { 0, 2, 7, 7, 4, 1, 2, 0 }, /* 900 MHz */ - { 0, 2, 7, 7, 3, 1, 1, 0 }, /* 800 MHz */ + { 0, 3, 7, 7, 7, 3, 5, 0 }, /* 1700 MHz */ + { 0, 3, 7, 7, 7, 1, 4, 0 }, /* 1600 MHz */ + { 0, 2, 7, 7, 7, 1, 4, 0 }, /* 1500 MHz */ + { 0, 2, 7, 7, 6, 1, 4, 0 }, /* 1400 MHz */ + { 0, 2, 7, 7, 6, 1, 3, 0 }, /* 1300 MHz */ + { 0, 2, 7, 7, 5, 1, 3, 0 }, /* 1200 MHz */ + { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1100 MHz */ + { 0, 1, 7, 7, 4, 1, 2, 0 }, /* 1000 MHz */ + { 0, 1, 7, 7, 4, 1, 2, 0 }, /* 900 MHz */ + { 0, 1, 7, 7, 4, 1, 2, 0 }, /* 800 MHz */ { 0, 1, 7, 7, 3, 1, 1, 0 }, /* 700 MHz */ - { 0, 1, 7, 7, 2, 1, 1, 0 }, /* 600 MHz */ + { 0, 1, 7, 7, 3, 1, 1, 0 }, /* 600 MHz */ { 0, 1, 7, 7, 2, 1, 1, 0 }, /* 500 MHz */ - { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 400 MHz */ + { 0, 1, 7, 7, 2, 1, 1, 0 }, /* 400 MHz */ { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 300 MHz */ { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 200 MHz */ }; @@ -87,9 +87,9 @@ static unsigned int clkdiv_cpu1_5250[CPUFREQ_LEVEL_END][2] = { /* Clock divider value for following * { COPY, HPM } */ - { 0, 2 }, /* 1700 MHz - N/A */ - { 0, 2 }, /* 1600 MHz - N/A */ - { 0, 2 }, /* 1500 MHz - N/A */ + { 0, 2 }, /* 1700 MHz */ + { 0, 2 }, /* 1600 MHz */ + { 0, 2 }, /* 1500 MHz */ { 0, 2 }, /* 1400 MHz */ { 0, 2 }, /* 1300 MHz */ { 0, 2 }, /* 1200 MHz */ @@ -106,10 +106,10 @@ static unsigned int clkdiv_cpu1_5250[CPUFREQ_LEVEL_END][2] = { }; static unsigned int exynos5_apll_pms_table[CPUFREQ_LEVEL_END] = { - (0), /* 1700 MHz - N/A */ - (0), /* 1600 MHz - N/A */ - (0), /* 1500 MHz - N/A */ - (0), /* 1400 MHz */ + ((425 << 16) | (6 << 8) | 0), /* 1700 MHz */ + ((200 << 16) | (3 << 8) | 0), /* 1600 MHz */ + ((250 << 16) | (4 << 8) | 0), /* 1500 MHz */ + ((175 << 16) | (3 << 8) | 0), /* 1400 MHz */ ((325 << 16) | (6 << 8) | 0), /* 1300 MHz */ ((200 << 16) | (4 << 8) | 0), /* 1200 MHz */ ((275 << 16) | (6 << 8) | 0), /* 1100 MHz */ @@ -126,9 +126,10 @@ static unsigned int exynos5_apll_pms_table[CPUFREQ_LEVEL_END] = { /* ASV group voltage table */ static const unsigned int asv_voltage_5250[CPUFREQ_LEVEL_END] = { - 0, 0, 0, 0, 0, 0, 0, /* 1700 MHz ~ 1100 MHz Not supported */ - 1175000, 1125000, 1075000, 1050000, 1000000, - 950000, 925000, 925000, 900000 + 1300000, 1250000, 1225000, 1200000, 1150000, + 1125000, 1100000, 1075000, 1050000, 1025000, + 1012500, 1000000, 975000, 950000, 937500, + 925000 }; static void set_clkdiv(unsigned int div_index) @@ -248,15 +249,7 @@ static void __init set_volt_table(void) { unsigned int i; - exynos5250_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID; - exynos5250_freq_table[L1].frequency = CPUFREQ_ENTRY_INVALID; - exynos5250_freq_table[L2].frequency = CPUFREQ_ENTRY_INVALID; - exynos5250_freq_table[L3].frequency = CPUFREQ_ENTRY_INVALID; - exynos5250_freq_table[L4].frequency = CPUFREQ_ENTRY_INVALID; - exynos5250_freq_table[L5].frequency = CPUFREQ_ENTRY_INVALID; - exynos5250_freq_table[L6].frequency = CPUFREQ_ENTRY_INVALID; - - max_support_idx = L7; + max_support_idx = L0; for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++) exynos5250_volt_table[i] = asv_voltage_5250[i]; diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index fdffa1beca17..409b92b8d346 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -7,7 +7,7 @@ menuconfig EDAC bool "EDAC (Error Detection And Correction) reporting" depends on HAS_IOMEM - depends on X86 || PPC || TILE + depends on X86 || PPC || TILE || ARM help EDAC is designed to report errors in the core system. These are low-level errors that are reported in the CPU or @@ -31,6 +31,14 @@ if EDAC comment "Reporting subsystems" +config EDAC_LEGACY_SYSFS + bool "EDAC legacy sysfs" + default y + help + Enable the compatibility sysfs nodes. + Use 'Y' if your edac utilities aren't ported to work with the newer + structures. + config EDAC_DEBUG bool "Debugging" help @@ -294,4 +302,18 @@ config EDAC_TILE Support for error detection and correction on the Tilera memory controller. +config EDAC_HIGHBANK_MC + tristate "Highbank Memory Controller" + depends on EDAC_MM_EDAC && ARCH_HIGHBANK + help + Support for error detection and correction on the + Calxeda Highbank memory controller. + +config EDAC_HIGHBANK_L2 + tristate "Highbank L2 Cache" + depends on EDAC_MM_EDAC && ARCH_HIGHBANK + help + Support for error detection and correction on the + Calxeda Highbank memory controller. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 196a63dd37c5..7e5129a733f8 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -55,3 +55,6 @@ obj-$(CONFIG_EDAC_AMD8111) += amd8111_edac.o obj-$(CONFIG_EDAC_AMD8131) += amd8131_edac.o obj-$(CONFIG_EDAC_TILE) += tile_edac.o + +obj-$(CONFIG_EDAC_HIGHBANK_MC) += highbank_mc_edac.o +obj-$(CONFIG_EDAC_HIGHBANK_L2) += highbank_l2_edac.o diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 7be9b7288e90..5a297a26211d 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -321,8 +321,8 @@ found: return edac_mc_find((int)node_id); err_no_match: - debugf2("sys_addr 0x%lx doesn't match any node\n", - (unsigned long)sys_addr); + edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n", + (unsigned long)sys_addr); return NULL; } @@ -393,15 +393,15 @@ static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr) mask = ~mask; if ((input_addr & mask) == (base & mask)) { - debugf2("InputAddr 0x%lx matches csrow %d (node %d)\n", - (unsigned long)input_addr, csrow, - pvt->mc_node_id); + edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n", + (unsigned long)input_addr, csrow, + pvt->mc_node_id); return csrow; } } - debugf2("no matching csrow for InputAddr 0x%lx (MC node %d)\n", - (unsigned long)input_addr, pvt->mc_node_id); + edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n", + (unsigned long)input_addr, pvt->mc_node_id); return -1; } @@ -430,20 +430,20 @@ int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, /* only revE and later have the DRAM Hole Address Register */ if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_E) { - debugf1(" revision %d for node %d does not support DHAR\n", - pvt->ext_model, pvt->mc_node_id); + edac_dbg(1, " revision %d for node %d does not support DHAR\n", + pvt->ext_model, pvt->mc_node_id); return 1; } /* valid for Fam10h and above */ if (boot_cpu_data.x86 >= 0x10 && !dhar_mem_hoist_valid(pvt)) { - debugf1(" Dram Memory Hoisting is DISABLED on this system\n"); + edac_dbg(1, " Dram Memory Hoisting is DISABLED on this system\n"); return 1; } if (!dhar_valid(pvt)) { - debugf1(" Dram Memory Hoisting is DISABLED on this node %d\n", - pvt->mc_node_id); + edac_dbg(1, " Dram Memory Hoisting is DISABLED on this node %d\n", + pvt->mc_node_id); return 1; } @@ -475,9 +475,9 @@ int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, else *hole_offset = k8_dhar_offset(pvt); - debugf1(" DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n", - pvt->mc_node_id, (unsigned long)*hole_base, - (unsigned long)*hole_offset, (unsigned long)*hole_size); + edac_dbg(1, " DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n", + pvt->mc_node_id, (unsigned long)*hole_base, + (unsigned long)*hole_offset, (unsigned long)*hole_size); return 0; } @@ -528,10 +528,9 @@ static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr) /* use DHAR to translate SysAddr to DramAddr */ dram_addr = sys_addr - hole_offset; - debugf2("using DHAR to translate SysAddr 0x%lx to " - "DramAddr 0x%lx\n", - (unsigned long)sys_addr, - (unsigned long)dram_addr); + edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n", + (unsigned long)sys_addr, + (unsigned long)dram_addr); return dram_addr; } @@ -548,9 +547,8 @@ static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr) */ dram_addr = (sys_addr & GENMASK(0, 39)) - dram_base; - debugf2("using DRAM Base register to translate SysAddr 0x%lx to " - "DramAddr 0x%lx\n", (unsigned long)sys_addr, - (unsigned long)dram_addr); + edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n", + (unsigned long)sys_addr, (unsigned long)dram_addr); return dram_addr; } @@ -586,9 +584,9 @@ static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr) input_addr = ((dram_addr >> intlv_shift) & GENMASK(12, 35)) + (dram_addr & 0xfff); - debugf2(" Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n", - intlv_shift, (unsigned long)dram_addr, - (unsigned long)input_addr); + edac_dbg(2, " Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n", + intlv_shift, (unsigned long)dram_addr, + (unsigned long)input_addr); return input_addr; } @@ -604,8 +602,8 @@ static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr) input_addr = dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr)); - debugf2("SysAdddr 0x%lx translates to InputAddr 0x%lx\n", - (unsigned long)sys_addr, (unsigned long)input_addr); + edac_dbg(2, "SysAdddr 0x%lx translates to InputAddr 0x%lx\n", + (unsigned long)sys_addr, (unsigned long)input_addr); return input_addr; } @@ -637,8 +635,8 @@ static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr) intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0)); if (intlv_shift == 0) { - debugf1(" InputAddr 0x%lx translates to DramAddr of " - "same value\n", (unsigned long)input_addr); + edac_dbg(1, " InputAddr 0x%lx translates to DramAddr of same value\n", + (unsigned long)input_addr); return input_addr; } @@ -649,9 +647,9 @@ static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr) intlv_sel = dram_intlv_sel(pvt, node_id) & ((1 << intlv_shift) - 1); dram_addr = bits + (intlv_sel << 12); - debugf1("InputAddr 0x%lx translates to DramAddr 0x%lx " - "(%d node interleave bits)\n", (unsigned long)input_addr, - (unsigned long)dram_addr, intlv_shift); + edac_dbg(1, "InputAddr 0x%lx translates to DramAddr 0x%lx (%d node interleave bits)\n", + (unsigned long)input_addr, + (unsigned long)dram_addr, intlv_shift); return dram_addr; } @@ -673,9 +671,9 @@ static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr) (dram_addr < (hole_base + hole_size))) { sys_addr = dram_addr + hole_offset; - debugf1("using DHAR to translate DramAddr 0x%lx to " - "SysAddr 0x%lx\n", (unsigned long)dram_addr, - (unsigned long)sys_addr); + edac_dbg(1, "using DHAR to translate DramAddr 0x%lx to SysAddr 0x%lx\n", + (unsigned long)dram_addr, + (unsigned long)sys_addr); return sys_addr; } @@ -697,9 +695,9 @@ static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr) */ sys_addr |= ~((sys_addr & (1ull << 39)) - 1); - debugf1(" Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n", - pvt->mc_node_id, (unsigned long)dram_addr, - (unsigned long)sys_addr); + edac_dbg(1, " Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n", + pvt->mc_node_id, (unsigned long)dram_addr, + (unsigned long)sys_addr); return sys_addr; } @@ -768,49 +766,48 @@ static void amd64_debug_display_dimm_sizes(struct amd64_pvt *, u8); static void amd64_dump_dramcfg_low(u32 dclr, int chan) { - debugf1("F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr); + edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr); - debugf1(" DIMM type: %sbuffered; all DIMMs support ECC: %s\n", - (dclr & BIT(16)) ? "un" : "", - (dclr & BIT(19)) ? "yes" : "no"); + edac_dbg(1, " DIMM type: %sbuffered; all DIMMs support ECC: %s\n", + (dclr & BIT(16)) ? "un" : "", + (dclr & BIT(19)) ? "yes" : "no"); - debugf1(" PAR/ERR parity: %s\n", - (dclr & BIT(8)) ? "enabled" : "disabled"); + edac_dbg(1, " PAR/ERR parity: %s\n", + (dclr & BIT(8)) ? "enabled" : "disabled"); if (boot_cpu_data.x86 == 0x10) - debugf1(" DCT 128bit mode width: %s\n", - (dclr & BIT(11)) ? "128b" : "64b"); + edac_dbg(1, " DCT 128bit mode width: %s\n", + (dclr & BIT(11)) ? "128b" : "64b"); - debugf1(" x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n", - (dclr & BIT(12)) ? "yes" : "no", - (dclr & BIT(13)) ? "yes" : "no", - (dclr & BIT(14)) ? "yes" : "no", - (dclr & BIT(15)) ? "yes" : "no"); + edac_dbg(1, " x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n", + (dclr & BIT(12)) ? "yes" : "no", + (dclr & BIT(13)) ? "yes" : "no", + (dclr & BIT(14)) ? "yes" : "no", + (dclr & BIT(15)) ? "yes" : "no"); } /* Display and decode various NB registers for debug purposes. */ static void dump_misc_regs(struct amd64_pvt *pvt) { - debugf1("F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap); + edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap); - debugf1(" NB two channel DRAM capable: %s\n", - (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no"); + edac_dbg(1, " NB two channel DRAM capable: %s\n", + (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no"); - debugf1(" ECC capable: %s, ChipKill ECC capable: %s\n", - (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no", - (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no"); + edac_dbg(1, " ECC capable: %s, ChipKill ECC capable: %s\n", + (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no", + (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no"); amd64_dump_dramcfg_low(pvt->dclr0, 0); - debugf1("F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare); + edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare); - debugf1("F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, " - "offset: 0x%08x\n", - pvt->dhar, dhar_base(pvt), - (boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt) - : f10_dhar_offset(pvt)); + edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n", + pvt->dhar, dhar_base(pvt), + (boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt) + : f10_dhar_offset(pvt)); - debugf1(" DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no"); + edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no"); amd64_debug_display_dimm_sizes(pvt, 0); @@ -857,15 +854,15 @@ static void read_dct_base_mask(struct amd64_pvt *pvt) u32 *base1 = &pvt->csels[1].csbases[cs]; if (!amd64_read_dct_pci_cfg(pvt, reg0, base0)) - debugf0(" DCSB0[%d]=0x%08x reg: F2x%x\n", - cs, *base0, reg0); + edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n", + cs, *base0, reg0); if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt)) continue; if (!amd64_read_dct_pci_cfg(pvt, reg1, base1)) - debugf0(" DCSB1[%d]=0x%08x reg: F2x%x\n", - cs, *base1, reg1); + edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n", + cs, *base1, reg1); } for_each_chip_select_mask(cs, 0, pvt) { @@ -875,15 +872,15 @@ static void read_dct_base_mask(struct amd64_pvt *pvt) u32 *mask1 = &pvt->csels[1].csmasks[cs]; if (!amd64_read_dct_pci_cfg(pvt, reg0, mask0)) - debugf0(" DCSM0[%d]=0x%08x reg: F2x%x\n", - cs, *mask0, reg0); + edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n", + cs, *mask0, reg0); if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt)) continue; if (!amd64_read_dct_pci_cfg(pvt, reg1, mask1)) - debugf0(" DCSM1[%d]=0x%08x reg: F2x%x\n", - cs, *mask1, reg1); + edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n", + cs, *mask1, reg1); } } @@ -1049,24 +1046,22 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, if (!src_mci) { amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n", (unsigned long)sys_addr); - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, page, offset, syndrome, -1, -1, -1, - EDAC_MOD_STR, "failed to map error addr to a node", - NULL); + ""); return; } /* Now map the sys_addr to a CSROW */ csrow = sys_addr_to_csrow(src_mci, sys_addr); if (csrow < 0) { - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, page, offset, syndrome, -1, -1, -1, - EDAC_MOD_STR, "failed to map error addr to a csrow", - NULL); + ""); return; } @@ -1082,12 +1077,11 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, amd64_mc_warn(src_mci, "unknown syndrome 0x%04x - " "possible error reporting race\n", syndrome); - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, page, offset, syndrome, csrow, -1, -1, - EDAC_MOD_STR, "unknown syndrome - possible error reporting race", - NULL); + ""); return; } } else { @@ -1102,10 +1096,10 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, channel = ((sys_addr & BIT(3)) != 0); } - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, src_mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, src_mci, 1, page, offset, syndrome, csrow, channel, -1, - EDAC_MOD_STR, "", NULL); + "", ""); } static int ddr2_cs_size(unsigned i, bool dct_width) @@ -1193,7 +1187,7 @@ static int f1x_early_channel_count(struct amd64_pvt *pvt) * Need to check DCT0[0] and DCT1[0] to see if only one of them has * their CSEnable bit on. If so, then SINGLE DIMM case. */ - debugf0("Data width is not 128 bits - need more decoding\n"); + edac_dbg(0, "Data width is not 128 bits - need more decoding\n"); /* * Check DRAM Bank Address Mapping values for each DIMM to see if there @@ -1272,25 +1266,24 @@ static void read_dram_ctl_register(struct amd64_pvt *pvt) return; if (!amd64_read_dct_pci_cfg(pvt, DCT_SEL_LO, &pvt->dct_sel_lo)) { - debugf0("F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n", - pvt->dct_sel_lo, dct_sel_baseaddr(pvt)); + edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n", + pvt->dct_sel_lo, dct_sel_baseaddr(pvt)); - debugf0(" DCTs operate in %s mode.\n", - (dct_ganging_enabled(pvt) ? "ganged" : "unganged")); + edac_dbg(0, " DCTs operate in %s mode\n", + (dct_ganging_enabled(pvt) ? "ganged" : "unganged")); if (!dct_ganging_enabled(pvt)) - debugf0(" Address range split per DCT: %s\n", - (dct_high_range_enabled(pvt) ? "yes" : "no")); + edac_dbg(0, " Address range split per DCT: %s\n", + (dct_high_range_enabled(pvt) ? "yes" : "no")); - debugf0(" data interleave for ECC: %s, " - "DRAM cleared since last warm reset: %s\n", - (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"), - (dct_memory_cleared(pvt) ? "yes" : "no")); + edac_dbg(0, " data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n", + (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"), + (dct_memory_cleared(pvt) ? "yes" : "no")); - debugf0(" channel interleave: %s, " - "interleave bits selector: 0x%x\n", - (dct_interleave_enabled(pvt) ? "enabled" : "disabled"), - dct_sel_interleave_addr(pvt)); + edac_dbg(0, " channel interleave: %s, " + "interleave bits selector: 0x%x\n", + (dct_interleave_enabled(pvt) ? "enabled" : "disabled"), + dct_sel_interleave_addr(pvt)); } amd64_read_dct_pci_cfg(pvt, DCT_SEL_HI, &pvt->dct_sel_hi); @@ -1428,7 +1421,7 @@ static int f1x_lookup_addr_in_dct(u64 in_addr, u32 nid, u8 dct) pvt = mci->pvt_info; - debugf1("input addr: 0x%llx, DCT: %d\n", in_addr, dct); + edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct); for_each_chip_select(csrow, dct, pvt) { if (!csrow_enabled(csrow, dct, pvt)) @@ -1436,19 +1429,18 @@ static int f1x_lookup_addr_in_dct(u64 in_addr, u32 nid, u8 dct) get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask); - debugf1(" CSROW=%d CSBase=0x%llx CSMask=0x%llx\n", - csrow, cs_base, cs_mask); + edac_dbg(1, " CSROW=%d CSBase=0x%llx CSMask=0x%llx\n", + csrow, cs_base, cs_mask); cs_mask = ~cs_mask; - debugf1(" (InputAddr & ~CSMask)=0x%llx " - "(CSBase & ~CSMask)=0x%llx\n", - (in_addr & cs_mask), (cs_base & cs_mask)); + edac_dbg(1, " (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n", + (in_addr & cs_mask), (cs_base & cs_mask)); if ((in_addr & cs_mask) == (cs_base & cs_mask)) { cs_found = f10_process_possible_spare(pvt, dct, csrow); - debugf1(" MATCH csrow=%d\n", cs_found); + edac_dbg(1, " MATCH csrow=%d\n", cs_found); break; } } @@ -1505,8 +1497,8 @@ static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range, u8 intlv_en = dram_intlv_en(pvt, range); u32 intlv_sel = dram_intlv_sel(pvt, range); - debugf1("(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", - range, sys_addr, get_dram_limit(pvt, range)); + edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", + range, sys_addr, get_dram_limit(pvt, range)); if (dhar_valid(pvt) && dhar_base(pvt) <= sys_addr && @@ -1562,7 +1554,7 @@ static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range, (chan_addr & 0xfff); } - debugf1(" Normalized DCT addr: 0x%llx\n", chan_addr); + edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr); cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel); @@ -1616,12 +1608,11 @@ static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &nid, &chan); if (csrow < 0) { - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, page, offset, syndrome, -1, -1, -1, - EDAC_MOD_STR, "failed to map error addr to a csrow", - NULL); + ""); return; } @@ -1633,10 +1624,10 @@ static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, if (dct_ganging_enabled(pvt)) chan = get_channel_from_ecc_syndrome(mci, syndrome); - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, page, offset, syndrome, csrow, chan, -1, - EDAC_MOD_STR, "", NULL); + "", ""); } /* @@ -1664,7 +1655,8 @@ static void amd64_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->csels[1].csbases : pvt->csels[0].csbases; - debugf1("F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n", ctrl, dbam); + edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n", + ctrl, dbam); edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl); @@ -1840,7 +1832,7 @@ static int decode_syndrome(u16 syndrome, u16 *vectors, unsigned num_vecs, } } - debugf0("syndrome(%x) not found\n", syndrome); + edac_dbg(0, "syndrome(%x) not found\n", syndrome); return -1; } @@ -1917,12 +1909,11 @@ static void amd64_handle_ce(struct mem_ctl_info *mci, struct mce *m) /* Ensure that the Error Address is VALID */ if (!(m->status & MCI_STATUS_ADDRV)) { amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n"); - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, -1, -1, -1, - EDAC_MOD_STR, "HW has no ERROR_ADDRESS available", - NULL); + ""); return; } @@ -1946,12 +1937,11 @@ static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m) if (!(m->status & MCI_STATUS_ADDRV)) { amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n"); - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1, - EDAC_MOD_STR, "HW has no ERROR_ADDRESS available", - NULL); + ""); return; } @@ -1966,11 +1956,11 @@ static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m) if (!src_mci) { amd64_mc_err(mci, "ERROR ADDRESS (0x%lx) NOT mapped to a MC\n", (unsigned long)sys_addr); - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, page, offset, 0, -1, -1, -1, - EDAC_MOD_STR, - "ERROR ADDRESS NOT mapped to a MC", NULL); + "ERROR ADDRESS NOT mapped to a MC", + ""); return; } @@ -1980,17 +1970,16 @@ static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m) if (csrow < 0) { amd64_mc_err(mci, "ERROR_ADDRESS (0x%lx) NOT mapped to CS\n", (unsigned long)sys_addr); - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, page, offset, 0, -1, -1, -1, - EDAC_MOD_STR, "ERROR ADDRESS NOT mapped to CS", - NULL); + ""); } else { - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, page, offset, 0, csrow, -1, -1, - EDAC_MOD_STR, "", NULL); + "", ""); } } @@ -2047,9 +2036,9 @@ static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f3_id) return -ENODEV; } - debugf1("F1: %s\n", pci_name(pvt->F1)); - debugf1("F2: %s\n", pci_name(pvt->F2)); - debugf1("F3: %s\n", pci_name(pvt->F3)); + edac_dbg(1, "F1: %s\n", pci_name(pvt->F1)); + edac_dbg(1, "F2: %s\n", pci_name(pvt->F2)); + edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); return 0; } @@ -2076,15 +2065,15 @@ static void read_mc_regs(struct amd64_pvt *pvt) * those are Read-As-Zero */ rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem); - debugf0(" TOP_MEM: 0x%016llx\n", pvt->top_mem); + edac_dbg(0, " TOP_MEM: 0x%016llx\n", pvt->top_mem); /* check first whether TOP_MEM2 is enabled */ rdmsrl(MSR_K8_SYSCFG, msr_val); if (msr_val & (1U << 21)) { rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2); - debugf0(" TOP_MEM2: 0x%016llx\n", pvt->top_mem2); + edac_dbg(0, " TOP_MEM2: 0x%016llx\n", pvt->top_mem2); } else - debugf0(" TOP_MEM2 disabled.\n"); + edac_dbg(0, " TOP_MEM2 disabled\n"); amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap); @@ -2100,17 +2089,17 @@ static void read_mc_regs(struct amd64_pvt *pvt) if (!rw) continue; - debugf1(" DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n", - range, - get_dram_base(pvt, range), - get_dram_limit(pvt, range)); + edac_dbg(1, " DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n", + range, + get_dram_base(pvt, range), + get_dram_limit(pvt, range)); - debugf1(" IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n", - dram_intlv_en(pvt, range) ? "Enabled" : "Disabled", - (rw & 0x1) ? "R" : "-", - (rw & 0x2) ? "W" : "-", - dram_intlv_sel(pvt, range), - dram_dst_node(pvt, range)); + edac_dbg(1, " IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n", + dram_intlv_en(pvt, range) ? "Enabled" : "Disabled", + (rw & 0x1) ? "R" : "-", + (rw & 0x2) ? "W" : "-", + dram_intlv_sel(pvt, range), + dram_dst_node(pvt, range)); } read_dct_base_mask(pvt); @@ -2191,9 +2180,9 @@ static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr) nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT); - debugf0(" (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode); - debugf0(" nr_pages/channel= %u channel-count = %d\n", - nr_pages, pvt->channel_count); + edac_dbg(0, " (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode); + edac_dbg(0, " nr_pages/channel= %u channel-count = %d\n", + nr_pages, pvt->channel_count); return nr_pages; } @@ -2205,6 +2194,7 @@ static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr) static int init_csrows(struct mem_ctl_info *mci) { struct csrow_info *csrow; + struct dimm_info *dimm; struct amd64_pvt *pvt = mci->pvt_info; u64 base, mask; u32 val; @@ -2217,22 +2207,19 @@ static int init_csrows(struct mem_ctl_info *mci) pvt->nbcfg = val; - debugf0("node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n", - pvt->mc_node_id, val, - !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); + edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n", + pvt->mc_node_id, val, + !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); for_each_chip_select(i, 0, pvt) { - csrow = &mci->csrows[i]; + csrow = mci->csrows[i]; if (!csrow_enabled(i, 0, pvt) && !csrow_enabled(i, 1, pvt)) { - debugf1("----CSROW %d EMPTY for node %d\n", i, - pvt->mc_node_id); + edac_dbg(1, "----CSROW %d VALID for MC node %d\n", + i, pvt->mc_node_id); continue; } - debugf1("----CSROW %d VALID for MC node %d\n", - i, pvt->mc_node_id); - empty = 0; if (csrow_enabled(i, 0, pvt)) nr_pages = amd64_csrow_nr_pages(pvt, 0, i); @@ -2244,8 +2231,9 @@ static int init_csrows(struct mem_ctl_info *mci) mtype = amd64_determine_memory_type(pvt, i); - debugf1(" for MC node %d csrow %d:\n", pvt->mc_node_id, i); - debugf1(" nr_pages: %u\n", nr_pages * pvt->channel_count); + edac_dbg(1, " for MC node %d csrow %d:\n", pvt->mc_node_id, i); + edac_dbg(1, " nr_pages: %u\n", + nr_pages * pvt->channel_count); /* * determine whether CHIPKILL or JUST ECC or NO ECC is operating @@ -2257,9 +2245,10 @@ static int init_csrows(struct mem_ctl_info *mci) edac_mode = EDAC_NONE; for (j = 0; j < pvt->channel_count; j++) { - csrow->channels[j].dimm->mtype = mtype; - csrow->channels[j].dimm->edac_mode = edac_mode; - csrow->channels[j].dimm->nr_pages = nr_pages; + dimm = csrow->channels[j]->dimm; + dimm->mtype = mtype; + dimm->edac_mode = edac_mode; + dimm->nr_pages = nr_pages; } } @@ -2296,9 +2285,9 @@ static bool amd64_nb_mce_bank_enabled_on_node(unsigned nid) struct msr *reg = per_cpu_ptr(msrs, cpu); nbe = reg->l & MSR_MCGCTL_NBE; - debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n", - cpu, reg->q, - (nbe ? "enabled" : "disabled")); + edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n", + cpu, reg->q, + (nbe ? "enabled" : "disabled")); if (!nbe) goto out; @@ -2369,8 +2358,8 @@ static bool enable_ecc_error_reporting(struct ecc_settings *s, u8 nid, amd64_read_pci_cfg(F3, NBCFG, &value); - debugf0("1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", - nid, value, !!(value & NBCFG_ECC_ENABLE)); + edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", + nid, value, !!(value & NBCFG_ECC_ENABLE)); if (!(value & NBCFG_ECC_ENABLE)) { amd64_warn("DRAM ECC disabled on this node, enabling...\n"); @@ -2394,8 +2383,8 @@ static bool enable_ecc_error_reporting(struct ecc_settings *s, u8 nid, s->flags.nb_ecc_prev = 1; } - debugf0("2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", - nid, value, !!(value & NBCFG_ECC_ENABLE)); + edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", + nid, value, !!(value & NBCFG_ECC_ENABLE)); return ret; } @@ -2463,26 +2452,29 @@ static bool ecc_enabled(struct pci_dev *F3, u8 nid) return true; } -struct mcidev_sysfs_attribute sysfs_attrs[ARRAY_SIZE(amd64_dbg_attrs) + - ARRAY_SIZE(amd64_inj_attrs) + - 1]; - -struct mcidev_sysfs_attribute terminator = { .attr = { .name = NULL } }; - -static void set_mc_sysfs_attrs(struct mem_ctl_info *mci) +static int set_mc_sysfs_attrs(struct mem_ctl_info *mci) { - unsigned int i = 0, j = 0; + int rc; - for (; i < ARRAY_SIZE(amd64_dbg_attrs); i++) - sysfs_attrs[i] = amd64_dbg_attrs[i]; + rc = amd64_create_sysfs_dbg_files(mci); + if (rc < 0) + return rc; - if (boot_cpu_data.x86 >= 0x10) - for (j = 0; j < ARRAY_SIZE(amd64_inj_attrs); j++, i++) - sysfs_attrs[i] = amd64_inj_attrs[j]; + if (boot_cpu_data.x86 >= 0x10) { + rc = amd64_create_sysfs_inject_files(mci); + if (rc < 0) + return rc; + } + + return 0; +} - sysfs_attrs[i] = terminator; +static void del_mc_sysfs_attrs(struct mem_ctl_info *mci) +{ + amd64_remove_sysfs_dbg_files(mci); - mci->mc_driver_sysfs_attributes = sysfs_attrs; + if (boot_cpu_data.x86 >= 0x10) + amd64_remove_sysfs_inject_files(mci); } static void setup_mci_misc_attrs(struct mem_ctl_info *mci, @@ -2601,20 +2593,22 @@ static int amd64_init_one_instance(struct pci_dev *F2) goto err_siblings; mci->pvt_info = pvt; - mci->dev = &pvt->F2->dev; + mci->pdev = &pvt->F2->dev; setup_mci_misc_attrs(mci, fam_type); if (init_csrows(mci)) mci->edac_cap = EDAC_FLAG_NONE; - set_mc_sysfs_attrs(mci); - ret = -ENODEV; if (edac_mc_add_mc(mci)) { - debugf1("failed edac_mc_add_mc()\n"); + edac_dbg(1, "failed edac_mc_add_mc()\n"); goto err_add_mc; } + if (set_mc_sysfs_attrs(mci)) { + edac_dbg(1, "failed edac_mc_add_mc()\n"); + goto err_add_sysfs; + } /* register stuff with EDAC MCE */ if (report_gart_errors) @@ -2628,6 +2622,8 @@ static int amd64_init_one_instance(struct pci_dev *F2) return 0; +err_add_sysfs: + edac_mc_del_mc(mci->pdev); err_add_mc: edac_mc_free(mci); @@ -2651,7 +2647,7 @@ static int __devinit amd64_probe_one_instance(struct pci_dev *pdev, ret = pci_enable_device(pdev); if (ret < 0) { - debugf0("ret=%d\n", ret); + edac_dbg(0, "ret=%d\n", ret); return -EIO; } @@ -2698,6 +2694,8 @@ static void __devexit amd64_remove_one_instance(struct pci_dev *pdev) struct pci_dev *F3 = node_to_amd_nb(nid)->misc; struct ecc_settings *s = ecc_stngs[nid]; + mci = find_mci_by_dev(&pdev->dev); + del_mc_sysfs_attrs(mci); /* Remove from EDAC CORE tracking list */ mci = edac_mc_del_mc(&pdev->dev); if (!mci) diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h index 9a666cb985b2..8d4804732bac 100644 --- a/drivers/edac/amd64_edac.h +++ b/drivers/edac/amd64_edac.h @@ -413,20 +413,33 @@ struct ecc_settings { }; #ifdef CONFIG_EDAC_DEBUG -#define NUM_DBG_ATTRS 5 +int amd64_create_sysfs_dbg_files(struct mem_ctl_info *mci); +void amd64_remove_sysfs_dbg_files(struct mem_ctl_info *mci); + #else -#define NUM_DBG_ATTRS 0 +static inline int amd64_create_sysfs_dbg_files(struct mem_ctl_info *mci) +{ + return 0; +} +static void inline amd64_remove_sysfs_dbg_files(struct mem_ctl_info *mci) +{ +} #endif #ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION -#define NUM_INJ_ATTRS 5 +int amd64_create_sysfs_inject_files(struct mem_ctl_info *mci); +void amd64_remove_sysfs_inject_files(struct mem_ctl_info *mci); + #else -#define NUM_INJ_ATTRS 0 +static inline int amd64_create_sysfs_inject_files(struct mem_ctl_info *mci) +{ + return 0; +} +static inline void amd64_remove_sysfs_inject_files(struct mem_ctl_info *mci) +{ +} #endif -extern struct mcidev_sysfs_attribute amd64_dbg_attrs[NUM_DBG_ATTRS], - amd64_inj_attrs[NUM_INJ_ATTRS]; - /* * Each of the PCI Device IDs types have their own set of hardware accessor * functions and per device encoding/decoding logic. @@ -460,3 +473,5 @@ int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset, int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, u64 *hole_offset, u64 *hole_size); + +#define to_mci(k) container_of(k, struct mem_ctl_info, dev) diff --git a/drivers/edac/amd64_edac_dbg.c b/drivers/edac/amd64_edac_dbg.c index e3562288f4ce..2c1bbf740605 100644 --- a/drivers/edac/amd64_edac_dbg.c +++ b/drivers/edac/amd64_edac_dbg.c @@ -1,8 +1,11 @@ #include "amd64_edac.h" #define EDAC_DCT_ATTR_SHOW(reg) \ -static ssize_t amd64_##reg##_show(struct mem_ctl_info *mci, char *data) \ +static ssize_t amd64_##reg##_show(struct device *dev, \ + struct device_attribute *mattr, \ + char *data) \ { \ + struct mem_ctl_info *mci = to_mci(dev); \ struct amd64_pvt *pvt = mci->pvt_info; \ return sprintf(data, "0x%016llx\n", (u64)pvt->reg); \ } @@ -12,8 +15,12 @@ EDAC_DCT_ATTR_SHOW(dbam0); EDAC_DCT_ATTR_SHOW(top_mem); EDAC_DCT_ATTR_SHOW(top_mem2); -static ssize_t amd64_hole_show(struct mem_ctl_info *mci, char *data) +static ssize_t amd64_hole_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + u64 hole_base = 0; u64 hole_offset = 0; u64 hole_size = 0; @@ -27,46 +34,40 @@ static ssize_t amd64_hole_show(struct mem_ctl_info *mci, char *data) /* * update NUM_DBG_ATTRS in case you add new members */ -struct mcidev_sysfs_attribute amd64_dbg_attrs[] = { +static DEVICE_ATTR(dhar, S_IRUGO, amd64_dhar_show, NULL); +static DEVICE_ATTR(dbam, S_IRUGO, amd64_dbam0_show, NULL); +static DEVICE_ATTR(topmem, S_IRUGO, amd64_top_mem_show, NULL); +static DEVICE_ATTR(topmem2, S_IRUGO, amd64_top_mem2_show, NULL); +static DEVICE_ATTR(dram_hole, S_IRUGO, amd64_hole_show, NULL); + +int amd64_create_sysfs_dbg_files(struct mem_ctl_info *mci) +{ + int rc; + + rc = device_create_file(&mci->dev, &dev_attr_dhar); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_dbam); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_topmem); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_topmem2); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_dram_hole); + if (rc < 0) + return rc; - { - .attr = { - .name = "dhar", - .mode = (S_IRUGO) - }, - .show = amd64_dhar_show, - .store = NULL, - }, - { - .attr = { - .name = "dbam", - .mode = (S_IRUGO) - }, - .show = amd64_dbam0_show, - .store = NULL, - }, - { - .attr = { - .name = "topmem", - .mode = (S_IRUGO) - }, - .show = amd64_top_mem_show, - .store = NULL, - }, - { - .attr = { - .name = "topmem2", - .mode = (S_IRUGO) - }, - .show = amd64_top_mem2_show, - .store = NULL, - }, - { - .attr = { - .name = "dram_hole", - .mode = (S_IRUGO) - }, - .show = amd64_hole_show, - .store = NULL, - }, -}; + return 0; +} + +void amd64_remove_sysfs_dbg_files(struct mem_ctl_info *mci) +{ + device_remove_file(&mci->dev, &dev_attr_dhar); + device_remove_file(&mci->dev, &dev_attr_dbam); + device_remove_file(&mci->dev, &dev_attr_topmem); + device_remove_file(&mci->dev, &dev_attr_topmem2); + device_remove_file(&mci->dev, &dev_attr_dram_hole); +} diff --git a/drivers/edac/amd64_edac_inj.c b/drivers/edac/amd64_edac_inj.c index 303f10e03dda..53d972e00dfb 100644 --- a/drivers/edac/amd64_edac_inj.c +++ b/drivers/edac/amd64_edac_inj.c @@ -1,7 +1,10 @@ #include "amd64_edac.h" -static ssize_t amd64_inject_section_show(struct mem_ctl_info *mci, char *buf) +static ssize_t amd64_inject_section_show(struct device *dev, + struct device_attribute *mattr, + char *buf) { + struct mem_ctl_info *mci = to_mci(dev); struct amd64_pvt *pvt = mci->pvt_info; return sprintf(buf, "0x%x\n", pvt->injection.section); } @@ -12,9 +15,11 @@ static ssize_t amd64_inject_section_show(struct mem_ctl_info *mci, char *buf) * * range: 0..3 */ -static ssize_t amd64_inject_section_store(struct mem_ctl_info *mci, +static ssize_t amd64_inject_section_store(struct device *dev, + struct device_attribute *mattr, const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); struct amd64_pvt *pvt = mci->pvt_info; unsigned long value; int ret = 0; @@ -33,8 +38,11 @@ static ssize_t amd64_inject_section_store(struct mem_ctl_info *mci, return ret; } -static ssize_t amd64_inject_word_show(struct mem_ctl_info *mci, char *buf) +static ssize_t amd64_inject_word_show(struct device *dev, + struct device_attribute *mattr, + char *buf) { + struct mem_ctl_info *mci = to_mci(dev); struct amd64_pvt *pvt = mci->pvt_info; return sprintf(buf, "0x%x\n", pvt->injection.word); } @@ -45,9 +53,11 @@ static ssize_t amd64_inject_word_show(struct mem_ctl_info *mci, char *buf) * * range: 0..8 */ -static ssize_t amd64_inject_word_store(struct mem_ctl_info *mci, - const char *data, size_t count) +static ssize_t amd64_inject_word_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); struct amd64_pvt *pvt = mci->pvt_info; unsigned long value; int ret = 0; @@ -66,8 +76,11 @@ static ssize_t amd64_inject_word_store(struct mem_ctl_info *mci, return ret; } -static ssize_t amd64_inject_ecc_vector_show(struct mem_ctl_info *mci, char *buf) +static ssize_t amd64_inject_ecc_vector_show(struct device *dev, + struct device_attribute *mattr, + char *buf) { + struct mem_ctl_info *mci = to_mci(dev); struct amd64_pvt *pvt = mci->pvt_info; return sprintf(buf, "0x%x\n", pvt->injection.bit_map); } @@ -77,9 +90,11 @@ static ssize_t amd64_inject_ecc_vector_show(struct mem_ctl_info *mci, char *buf) * corresponding bit within the error injection word above. When used during a * DRAM ECC read, it holds the contents of the of the DRAM ECC bits. */ -static ssize_t amd64_inject_ecc_vector_store(struct mem_ctl_info *mci, - const char *data, size_t count) +static ssize_t amd64_inject_ecc_vector_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); struct amd64_pvt *pvt = mci->pvt_info; unsigned long value; int ret = 0; @@ -103,9 +118,11 @@ static ssize_t amd64_inject_ecc_vector_store(struct mem_ctl_info *mci, * Do a DRAM ECC read. Assemble staged values in the pvt area, format into * fields needed by the injection registers and read the NB Array Data Port. */ -static ssize_t amd64_inject_read_store(struct mem_ctl_info *mci, - const char *data, size_t count) +static ssize_t amd64_inject_read_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); struct amd64_pvt *pvt = mci->pvt_info; unsigned long value; u32 section, word_bits; @@ -125,7 +142,8 @@ static ssize_t amd64_inject_read_store(struct mem_ctl_info *mci, /* Issue 'word' and 'bit' along with the READ request */ amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); - debugf0("section=0x%x word_bits=0x%x\n", section, word_bits); + edac_dbg(0, "section=0x%x word_bits=0x%x\n", + section, word_bits); return count; } @@ -136,9 +154,11 @@ static ssize_t amd64_inject_read_store(struct mem_ctl_info *mci, * Do a DRAM ECC write. Assemble staged values in the pvt area and format into * fields needed by the injection registers. */ -static ssize_t amd64_inject_write_store(struct mem_ctl_info *mci, +static ssize_t amd64_inject_write_store(struct device *dev, + struct device_attribute *mattr, const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); struct amd64_pvt *pvt = mci->pvt_info; unsigned long value; u32 section, word_bits; @@ -158,7 +178,8 @@ static ssize_t amd64_inject_write_store(struct mem_ctl_info *mci, /* Issue 'word' and 'bit' along with the READ request */ amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); - debugf0("section=0x%x word_bits=0x%x\n", section, word_bits); + edac_dbg(0, "section=0x%x word_bits=0x%x\n", + section, word_bits); return count; } @@ -168,46 +189,47 @@ static ssize_t amd64_inject_write_store(struct mem_ctl_info *mci, /* * update NUM_INJ_ATTRS in case you add new members */ -struct mcidev_sysfs_attribute amd64_inj_attrs[] = { - - { - .attr = { - .name = "inject_section", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = amd64_inject_section_show, - .store = amd64_inject_section_store, - }, - { - .attr = { - .name = "inject_word", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = amd64_inject_word_show, - .store = amd64_inject_word_store, - }, - { - .attr = { - .name = "inject_ecc_vector", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = amd64_inject_ecc_vector_show, - .store = amd64_inject_ecc_vector_store, - }, - { - .attr = { - .name = "inject_write", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = NULL, - .store = amd64_inject_write_store, - }, - { - .attr = { - .name = "inject_read", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = NULL, - .store = amd64_inject_read_store, - }, -}; + +static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR, + amd64_inject_section_show, amd64_inject_section_store); +static DEVICE_ATTR(inject_word, S_IRUGO | S_IWUSR, + amd64_inject_word_show, amd64_inject_word_store); +static DEVICE_ATTR(inject_ecc_vector, S_IRUGO | S_IWUSR, + amd64_inject_ecc_vector_show, amd64_inject_ecc_vector_store); +static DEVICE_ATTR(inject_write, S_IRUGO | S_IWUSR, + NULL, amd64_inject_write_store); +static DEVICE_ATTR(inject_read, S_IRUGO | S_IWUSR, + NULL, amd64_inject_read_store); + + +int amd64_create_sysfs_inject_files(struct mem_ctl_info *mci) +{ + int rc; + + rc = device_create_file(&mci->dev, &dev_attr_inject_section); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_inject_word); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_inject_ecc_vector); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_inject_write); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_inject_read); + if (rc < 0) + return rc; + + return 0; +} + +void amd64_remove_sysfs_inject_files(struct mem_ctl_info *mci) +{ + device_remove_file(&mci->dev, &dev_attr_inject_section); + device_remove_file(&mci->dev, &dev_attr_inject_word); + device_remove_file(&mci->dev, &dev_attr_inject_ecc_vector); + device_remove_file(&mci->dev, &dev_attr_inject_write); + device_remove_file(&mci->dev, &dev_attr_inject_read); +} diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c index 9774d443fa57..29eeb68a200c 100644 --- a/drivers/edac/amd76x_edac.c +++ b/drivers/edac/amd76x_edac.c @@ -105,7 +105,7 @@ static void amd76x_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); pci_read_config_dword(pdev, AMD76X_ECC_MODE_STATUS, &info->ecc_mode_status); @@ -145,10 +145,10 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci, if (handle_errors) { row = (info->ecc_mode_status >> 4) & 0xf; - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, - mci->csrows[row].first_page, 0, 0, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, + mci->csrows[row]->first_page, 0, 0, row, 0, -1, - mci->ctl_name, "", NULL); + mci->ctl_name, ""); } } @@ -160,10 +160,10 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci, if (handle_errors) { row = info->ecc_mode_status & 0xf; - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, - mci->csrows[row].first_page, 0, 0, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, + mci->csrows[row]->first_page, 0, 0, row, 0, -1, - mci->ctl_name, "", NULL); + mci->ctl_name, ""); } } @@ -180,7 +180,7 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci, static void amd76x_check(struct mem_ctl_info *mci) { struct amd76x_error_info info; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); amd76x_get_error_info(mci, &info); amd76x_process_error_info(mci, &info, 1); } @@ -194,8 +194,8 @@ static void amd76x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, int index; for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; /* find the DRAM Chip Select Base address and mask */ pci_read_config_dword(pdev, @@ -241,7 +241,7 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx) u32 ems_mode; struct amd76x_error_info discard; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); pci_read_config_dword(pdev, AMD76X_ECC_MODE_STATUS, &ems); ems_mode = (ems >> 10) & 0x3; @@ -256,8 +256,8 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx) if (mci == NULL) return -ENOMEM; - debugf0("%s(): mci = %p\n", __func__, mci); - mci->dev = &pdev->dev; + edac_dbg(0, "mci = %p\n", mci); + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_RDDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; mci->edac_cap = ems_mode ? @@ -276,7 +276,7 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx) * type of memory controller. The ID is therefore hardcoded to 0. */ if (edac_mc_add_mc(mci)) { - debugf3("%s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(3, "failed edac_mc_add_mc()\n"); goto fail; } @@ -292,7 +292,7 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx) } /* get this far and it's successful */ - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); return 0; fail: @@ -304,7 +304,7 @@ fail: static int __devinit amd76x_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* don't need to call pci_enable_device() */ return amd76x_probe1(pdev, ent->driver_data); @@ -322,7 +322,7 @@ static void __devexit amd76x_remove_one(struct pci_dev *pdev) { struct mem_ctl_info *mci; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); if (amd76x_pci) edac_pci_release_generic_ctl(amd76x_pci); diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c index 69ee6aab5c71..a1bbd8edd257 100644 --- a/drivers/edac/cell_edac.c +++ b/drivers/edac/cell_edac.c @@ -33,10 +33,10 @@ struct cell_edac_priv static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar) { struct cell_edac_priv *priv = mci->pvt_info; - struct csrow_info *csrow = &mci->csrows[0]; + struct csrow_info *csrow = mci->csrows[0]; unsigned long address, pfn, offset, syndrome; - dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n", + dev_dbg(mci->pdev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n", priv->node, chan, ar); /* Address decoding is likely a bit bogus, to dbl check */ @@ -48,18 +48,18 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar) syndrome = (ar & 0x000000001fe00000ul) >> 21; /* TODO: Decoding of the error address */ - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, csrow->first_page + pfn, offset, syndrome, - 0, chan, -1, "", "", NULL); + 0, chan, -1, "", ""); } static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar) { struct cell_edac_priv *priv = mci->pvt_info; - struct csrow_info *csrow = &mci->csrows[0]; + struct csrow_info *csrow = mci->csrows[0]; unsigned long address, pfn, offset; - dev_dbg(mci->dev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n", + dev_dbg(mci->pdev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n", priv->node, chan, ar); /* Address decoding is likely a bit bogus, to dbl check */ @@ -70,9 +70,9 @@ static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar) offset = address & ~PAGE_MASK; /* TODO: Decoding of the error address */ - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, csrow->first_page + pfn, offset, 0, - 0, chan, -1, "", "", NULL); + 0, chan, -1, "", ""); } static void cell_edac_check(struct mem_ctl_info *mci) @@ -83,7 +83,7 @@ static void cell_edac_check(struct mem_ctl_info *mci) fir = in_be64(&priv->regs->mic_fir); #ifdef DEBUG if (fir != priv->prev_fir) { - dev_dbg(mci->dev, "fir change : 0x%016lx\n", fir); + dev_dbg(mci->pdev, "fir change : 0x%016lx\n", fir); priv->prev_fir = fir; } #endif @@ -119,14 +119,14 @@ static void cell_edac_check(struct mem_ctl_info *mci) mb(); /* sync up */ #ifdef DEBUG fir = in_be64(&priv->regs->mic_fir); - dev_dbg(mci->dev, "fir clear : 0x%016lx\n", fir); + dev_dbg(mci->pdev, "fir clear : 0x%016lx\n", fir); #endif } } static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci) { - struct csrow_info *csrow = &mci->csrows[0]; + struct csrow_info *csrow = mci->csrows[0]; struct dimm_info *dimm; struct cell_edac_priv *priv = mci->pvt_info; struct device_node *np; @@ -150,12 +150,12 @@ static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci) csrow->last_page = csrow->first_page + nr_pages - 1; for (j = 0; j < csrow->nr_channels; j++) { - dimm = csrow->channels[j].dimm; + dimm = csrow->channels[j]->dimm; dimm->mtype = MEM_XDR; dimm->edac_mode = EDAC_SECDED; dimm->nr_pages = nr_pages / csrow->nr_channels; } - dev_dbg(mci->dev, + dev_dbg(mci->pdev, "Initialized on node %d, chanmask=0x%x," " first_page=0x%lx, nr_pages=0x%x\n", priv->node, priv->chanmask, @@ -212,7 +212,7 @@ static int __devinit cell_edac_probe(struct platform_device *pdev) priv->regs = regs; priv->node = pdev->id; priv->chanmask = chanmask; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_XDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; mci->edac_cap = EDAC_FLAG_EC | EDAC_FLAG_SECDED; diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c index e22030a9de66..c2ef13495873 100644 --- a/drivers/edac/cpc925_edac.c +++ b/drivers/edac/cpc925_edac.c @@ -316,13 +316,12 @@ static void get_total_mem(struct cpc925_mc_pdata *pdata) reg += aw; size = of_read_number(reg, sw); reg += sw; - debugf1("%s: start 0x%lx, size 0x%lx\n", __func__, - start, size); + edac_dbg(1, "start 0x%lx, size 0x%lx\n", start, size); pdata->total_mem += size; } while (reg < reg_end); of_node_put(np); - debugf0("%s: total_mem 0x%lx\n", __func__, pdata->total_mem); + edac_dbg(0, "total_mem 0x%lx\n", pdata->total_mem); } static void cpc925_init_csrows(struct mem_ctl_info *mci) @@ -330,8 +329,9 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci) struct cpc925_mc_pdata *pdata = mci->pvt_info; struct csrow_info *csrow; struct dimm_info *dimm; + enum dev_type dtype; int index, j; - u32 mbmr, mbbar, bba; + u32 mbmr, mbbar, bba, grain; unsigned long row_size, nr_pages, last_nr_pages = 0; get_total_mem(pdata); @@ -347,7 +347,7 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci) if (bba == 0) continue; /* not populated */ - csrow = &mci->csrows[index]; + csrow = mci->csrows[index]; row_size = bba * (1UL << 28); /* 256M */ csrow->first_page = last_nr_pages; @@ -355,37 +355,36 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci) csrow->last_page = csrow->first_page + nr_pages - 1; last_nr_pages = csrow->last_page + 1; + switch (csrow->nr_channels) { + case 1: /* Single channel */ + grain = 32; /* four-beat burst of 32 bytes */ + break; + case 2: /* Dual channel */ + default: + grain = 64; /* four-beat burst of 64 bytes */ + break; + } + switch ((mbmr & MBMR_MODE_MASK) >> MBMR_MODE_SHIFT) { + case 6: /* 0110, no way to differentiate X8 VS X16 */ + case 5: /* 0101 */ + case 8: /* 1000 */ + dtype = DEV_X16; + break; + case 7: /* 0111 */ + case 9: /* 1001 */ + dtype = DEV_X8; + break; + default: + dtype = DEV_UNKNOWN; + break; + } for (j = 0; j < csrow->nr_channels; j++) { - dimm = csrow->channels[j].dimm; - + dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / csrow->nr_channels; dimm->mtype = MEM_RDDR; dimm->edac_mode = EDAC_SECDED; - - switch (csrow->nr_channels) { - case 1: /* Single channel */ - dimm->grain = 32; /* four-beat burst of 32 bytes */ - break; - case 2: /* Dual channel */ - default: - dimm->grain = 64; /* four-beat burst of 64 bytes */ - break; - } - - switch ((mbmr & MBMR_MODE_MASK) >> MBMR_MODE_SHIFT) { - case 6: /* 0110, no way to differentiate X8 VS X16 */ - case 5: /* 0101 */ - case 8: /* 1000 */ - dimm->dtype = DEV_X16; - break; - case 7: /* 0111 */ - case 9: /* 1001 */ - dimm->dtype = DEV_X8; - break; - default: - dimm->dtype = DEV_UNKNOWN; - break; - } + dimm->grain = grain; + dimm->dtype = dtype; } } } @@ -463,7 +462,7 @@ static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear, *csrow = rank; #ifdef CONFIG_EDAC_DEBUG - if (mci->csrows[rank].first_page == 0) { + if (mci->csrows[rank]->first_page == 0) { cpc925_mc_printk(mci, KERN_ERR, "ECC occurs in a " "non-populated csrow, broken hardware?\n"); return; @@ -471,7 +470,7 @@ static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear, #endif /* Revert csrow number */ - pa = mci->csrows[rank].first_page << PAGE_SHIFT; + pa = mci->csrows[rank]->first_page << PAGE_SHIFT; /* Revert column address */ col += bcnt; @@ -512,7 +511,7 @@ static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear, *offset = pa & (PAGE_SIZE - 1); *pfn = pa >> PAGE_SHIFT; - debugf0("%s: ECC physical address 0x%lx\n", __func__, pa); + edac_dbg(0, "ECC physical address 0x%lx\n", pa); } static int cpc925_mc_find_channel(struct mem_ctl_info *mci, u16 syndrome) @@ -555,18 +554,18 @@ static void cpc925_mc_check(struct mem_ctl_info *mci) if (apiexcp & CECC_EXCP_DETECTED) { cpc925_mc_printk(mci, KERN_INFO, "DRAM CECC Fault\n"); channel = cpc925_mc_find_channel(mci, syndrome); - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, pfn, offset, syndrome, csrow, channel, -1, - mci->ctl_name, "", NULL); + mci->ctl_name, ""); } if (apiexcp & UECC_EXCP_DETECTED) { cpc925_mc_printk(mci, KERN_INFO, "DRAM UECC Fault\n"); - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, pfn, offset, 0, csrow, -1, -1, - mci->ctl_name, "", NULL); + mci->ctl_name, ""); } cpc925_mc_printk(mci, KERN_INFO, "Dump registers:\n"); @@ -852,8 +851,8 @@ static void cpc925_add_edac_devices(void __iomem *vbase) goto err2; } - debugf0("%s: Successfully added edac device for %s\n", - __func__, dev_info->ctl_name); + edac_dbg(0, "Successfully added edac device for %s\n", + dev_info->ctl_name); continue; @@ -884,8 +883,8 @@ static void cpc925_del_edac_devices(void) if (dev_info->exit) dev_info->exit(dev_info); - debugf0("%s: Successfully deleted edac device for %s\n", - __func__, dev_info->ctl_name); + edac_dbg(0, "Successfully deleted edac device for %s\n", + dev_info->ctl_name); } } @@ -900,7 +899,7 @@ static int cpc925_get_sdram_scrub_rate(struct mem_ctl_info *mci) mscr = __raw_readl(pdata->vbase + REG_MSCR_OFFSET); si = (mscr & MSCR_SI_MASK) >> MSCR_SI_SHIFT; - debugf0("%s, Mem Scrub Ctrl Register 0x%x\n", __func__, mscr); + edac_dbg(0, "Mem Scrub Ctrl Register 0x%x\n", mscr); if (((mscr & MSCR_SCRUB_MOD_MASK) != MSCR_BACKGR_SCRUB) || (si == 0)) { @@ -928,8 +927,7 @@ static int cpc925_mc_get_channels(void __iomem *vbase) ((mbcr & MBCR_64BITBUS_MASK) == 0)) dual = 1; - debugf0("%s: %s channel\n", __func__, - (dual > 0) ? "Dual" : "Single"); + edac_dbg(0, "%s channel\n", (dual > 0) ? "Dual" : "Single"); return dual; } @@ -944,7 +942,7 @@ static int __devinit cpc925_probe(struct platform_device *pdev) struct resource *r; int res = 0, nr_channels; - debugf0("%s: %s platform device found!\n", __func__, pdev->name); + edac_dbg(0, "%s platform device found!\n", pdev->name); if (!devres_open_group(&pdev->dev, cpc925_probe, GFP_KERNEL)) { res = -ENOMEM; @@ -995,7 +993,7 @@ static int __devinit cpc925_probe(struct platform_device *pdev) pdata->edac_idx = edac_mc_idx++; pdata->name = pdev->name; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; platform_set_drvdata(pdev, mci); mci->dev_name = dev_name(&pdev->dev); mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR; @@ -1026,7 +1024,7 @@ static int __devinit cpc925_probe(struct platform_device *pdev) cpc925_add_edac_devices(vbase); /* get this far and it's successful */ - debugf0("%s: success\n", __func__); + edac_dbg(0, "success\n"); res = 0; goto out; diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index 3186512c9739..a5ed6b795fd4 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -309,7 +309,7 @@ static unsigned long ctl_page_to_phys(struct mem_ctl_info *mci, u32 remap; struct e752x_pvt *pvt = (struct e752x_pvt *)mci->pvt_info; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); if (page < pvt->tolm) return page; @@ -335,7 +335,7 @@ static void do_process_ce(struct mem_ctl_info *mci, u16 error_one, int i; struct e752x_pvt *pvt = (struct e752x_pvt *)mci->pvt_info; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); /* convert the addr to 4k page */ page = sec1_add >> (PAGE_SHIFT - 4); @@ -371,10 +371,10 @@ static void do_process_ce(struct mem_ctl_info *mci, u16 error_one, channel = !(error_one & 1); /* e752x mc reads 34:6 of the DRAM linear address */ - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, page, offset_in_page(sec1_add << 4), sec1_syndrome, row, channel, -1, - "e752x CE", "", NULL); + "e752x CE", ""); } static inline void process_ce(struct mem_ctl_info *mci, u16 error_one, @@ -394,7 +394,7 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one, int row; struct e752x_pvt *pvt = (struct e752x_pvt *)mci->pvt_info; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); if (error_one & 0x0202) { error_2b = ded_add; @@ -408,11 +408,11 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one, edac_mc_find_csrow_by_page(mci, block_page); /* e752x mc reads 34:6 of the DRAM linear address */ - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, block_page, offset_in_page(error_2b << 4), 0, row, -1, -1, - "e752x UE from Read", "", NULL); + "e752x UE from Read", ""); } if (error_one & 0x0404) { @@ -427,11 +427,11 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one, edac_mc_find_csrow_by_page(mci, block_page); /* e752x mc reads 34:6 of the DRAM linear address */ - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, block_page, offset_in_page(error_2b << 4), 0, row, -1, -1, - "e752x UE from Scruber", "", NULL); + "e752x UE from Scruber", ""); } } @@ -453,10 +453,10 @@ static inline void process_ue_no_info_wr(struct mem_ctl_info *mci, if (!handle_error) return; - debugf3("%s()\n", __func__); - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, + edac_dbg(3, "\n"); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1, - "e752x UE log memory write", "", NULL); + "e752x UE log memory write", ""); } static void do_process_ded_retry(struct mem_ctl_info *mci, u16 error, @@ -982,7 +982,7 @@ static void e752x_check(struct mem_ctl_info *mci) { struct e752x_error_info info; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); e752x_get_error_info(mci, &info); e752x_process_error_info(mci, &info, 1); } @@ -1069,6 +1069,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, u16 ddrcsr) { struct csrow_info *csrow; + enum edac_type edac_mode; unsigned long last_cumul_size; int index, mem_dev, drc_chan; int drc_drbg; /* DRB granularity 0=64mb, 1=128mb */ @@ -1095,14 +1096,13 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, for (last_cumul_size = index = 0; index < mci->nr_csrows; index++) { /* mem_dev 0=x8, 1=x4 */ mem_dev = (dra >> (index * 4 + 2)) & 0x3; - csrow = &mci->csrows[remap_csrow_index(mci, index)]; + csrow = mci->csrows[remap_csrow_index(mci, index)]; mem_dev = (mem_dev == 2); pci_read_config_byte(pdev, E752X_DRB + index, &value); /* convert a 128 or 64 MiB DRB to a page size. */ cumul_size = value << (25 + drc_drbg - PAGE_SHIFT); - debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index, - cumul_size); + edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size); if (cumul_size == last_cumul_size) continue; /* not populated */ @@ -1111,29 +1111,29 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, nr_pages = cumul_size - last_cumul_size; last_cumul_size = cumul_size; + /* + * if single channel or x8 devices then SECDED + * if dual channel and x4 then S4ECD4ED + */ + if (drc_ddim) { + if (drc_chan && mem_dev) { + edac_mode = EDAC_S4ECD4ED; + mci->edac_cap |= EDAC_FLAG_S4ECD4ED; + } else { + edac_mode = EDAC_SECDED; + mci->edac_cap |= EDAC_FLAG_SECDED; + } + } else + edac_mode = EDAC_NONE; for (i = 0; i < csrow->nr_channels; i++) { - struct dimm_info *dimm = csrow->channels[i].dimm; + struct dimm_info *dimm = csrow->channels[i]->dimm; - debugf3("Initializing rank at (%i,%i)\n", index, i); + edac_dbg(3, "Initializing rank at (%i,%i)\n", index, i); dimm->nr_pages = nr_pages / csrow->nr_channels; dimm->grain = 1 << 12; /* 4KiB - resolution of CELOG */ dimm->mtype = MEM_RDDR; /* only one type supported */ dimm->dtype = mem_dev ? DEV_X4 : DEV_X8; - - /* - * if single channel or x8 devices then SECDED - * if dual channel and x4 then S4ECD4ED - */ - if (drc_ddim) { - if (drc_chan && mem_dev) { - dimm->edac_mode = EDAC_S4ECD4ED; - mci->edac_cap |= EDAC_FLAG_S4ECD4ED; - } else { - dimm->edac_mode = EDAC_SECDED; - mci->edac_cap |= EDAC_FLAG_SECDED; - } - } else - dimm->edac_mode = EDAC_NONE; + dimm->edac_mode = edac_mode; } } } @@ -1269,8 +1269,8 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) int drc_chan; /* Number of channels 0=1chan,1=2chan */ struct e752x_error_info discard; - debugf0("%s(): mci\n", __func__); - debugf0("Starting Probe1\n"); + edac_dbg(0, "mci\n"); + edac_dbg(0, "Starting Probe1\n"); /* check to see if device 0 function 1 is enabled; if it isn't, we * assume the BIOS has reserved it for a reason and is expecting @@ -1300,7 +1300,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) if (mci == NULL) return -ENOMEM; - debugf3("%s(): init mci\n", __func__); + edac_dbg(3, "init mci\n"); mci->mtype_cap = MEM_FLAG_RDDR; /* 3100 IMCH supports SECDEC only */ mci->edac_ctl_cap = (dev_idx == I3100) ? EDAC_FLAG_SECDED : @@ -1308,9 +1308,9 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) /* FIXME - what if different memory types are in different csrows? */ mci->mod_name = EDAC_MOD_STR; mci->mod_ver = E752X_REVISION; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; - debugf3("%s(): init pvt\n", __func__); + edac_dbg(3, "init pvt\n"); pvt = (struct e752x_pvt *)mci->pvt_info; pvt->dev_info = &e752x_devs[dev_idx]; pvt->mc_symmetric = ((ddrcsr & 0x10) != 0); @@ -1320,7 +1320,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) return -ENODEV; } - debugf3("%s(): more mci init\n", __func__); + edac_dbg(3, "more mci init\n"); mci->ctl_name = pvt->dev_info->ctl_name; mci->dev_name = pci_name(pdev); mci->edac_check = e752x_check; @@ -1342,7 +1342,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) mci->edac_cap = EDAC_FLAG_SECDED; /* the only mode supported */ else mci->edac_cap |= EDAC_FLAG_NONE; - debugf3("%s(): tolm, remapbase, remaplimit\n", __func__); + edac_dbg(3, "tolm, remapbase, remaplimit\n"); /* load the top of low memory, remap base, and remap limit vars */ pci_read_config_word(pdev, E752X_TOLM, &pci_data); @@ -1359,7 +1359,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) * type of memory controller. The ID is therefore hardcoded to 0. */ if (edac_mc_add_mc(mci)) { - debugf3("%s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(3, "failed edac_mc_add_mc()\n"); goto fail; } @@ -1377,7 +1377,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) } /* get this far and it's successful */ - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); return 0; fail: @@ -1393,7 +1393,7 @@ fail: static int __devinit e752x_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* wake up and enable device */ if (pci_enable_device(pdev) < 0) @@ -1407,7 +1407,7 @@ static void __devexit e752x_remove_one(struct pci_dev *pdev) struct mem_ctl_info *mci; struct e752x_pvt *pvt; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); if (e752x_pci) edac_pci_release_generic_ctl(e752x_pci); @@ -1453,7 +1453,7 @@ static int __init e752x_init(void) { int pci_rc; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); @@ -1464,7 +1464,7 @@ static int __init e752x_init(void) static void __exit e752x_exit(void) { - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); pci_unregister_driver(&e752x_driver); } diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c index 9a9c1a546797..9ff57f361a43 100644 --- a/drivers/edac/e7xxx_edac.c +++ b/drivers/edac/e7xxx_edac.c @@ -166,7 +166,7 @@ static const struct e7xxx_dev_info e7xxx_devs[] = { /* FIXME - is this valid for both SECDED and S4ECD4ED? */ static inline int e7xxx_find_channel(u16 syndrome) { - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); if ((syndrome & 0xff00) == 0) return 0; @@ -186,7 +186,7 @@ static unsigned long ctl_page_to_phys(struct mem_ctl_info *mci, u32 remap; struct e7xxx_pvt *pvt = (struct e7xxx_pvt *)mci->pvt_info; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); if ((page < pvt->tolm) || ((page >= 0x100000) && (page < pvt->remapbase))) @@ -208,7 +208,7 @@ static void process_ce(struct mem_ctl_info *mci, struct e7xxx_error_info *info) int row; int channel; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); /* read the error address */ error_1b = info->dram_celog_add; /* FIXME - should use PAGE_SHIFT */ @@ -219,15 +219,15 @@ static void process_ce(struct mem_ctl_info *mci, struct e7xxx_error_info *info) row = edac_mc_find_csrow_by_page(mci, page); /* convert syndrome to channel */ channel = e7xxx_find_channel(syndrome); - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, page, 0, syndrome, - row, channel, -1, "e7xxx CE", "", NULL); + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, page, 0, syndrome, + row, channel, -1, "e7xxx CE", ""); } static void process_ce_no_info(struct mem_ctl_info *mci) { - debugf3("%s()\n", __func__); - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, -1, -1, -1, - "e7xxx CE log register overflow", "", NULL); + edac_dbg(3, "\n"); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1, + "e7xxx CE log register overflow", ""); } static void process_ue(struct mem_ctl_info *mci, struct e7xxx_error_info *info) @@ -235,23 +235,23 @@ static void process_ue(struct mem_ctl_info *mci, struct e7xxx_error_info *info) u32 error_2b, block_page; int row; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); /* read the error address */ error_2b = info->dram_uelog_add; /* FIXME - should use PAGE_SHIFT */ block_page = error_2b >> 6; /* convert to 4k address */ row = edac_mc_find_csrow_by_page(mci, block_page); - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, block_page, 0, 0, - row, -1, -1, "e7xxx UE", "", NULL); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, block_page, 0, 0, + row, -1, -1, "e7xxx UE", ""); } static void process_ue_no_info(struct mem_ctl_info *mci) { - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, -1, -1, -1, - "e7xxx UE log register overflow", "", NULL); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1, + "e7xxx UE log register overflow", ""); } static void e7xxx_get_error_info(struct mem_ctl_info *mci, @@ -334,7 +334,7 @@ static void e7xxx_check(struct mem_ctl_info *mci) { struct e7xxx_error_info info; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); e7xxx_get_error_info(mci, &info); e7xxx_process_error_info(mci, &info, 1); } @@ -362,6 +362,7 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, int drc_chan, drc_drbg, drc_ddim, mem_dev; struct csrow_info *csrow; struct dimm_info *dimm; + enum edac_type edac_mode; pci_read_config_dword(pdev, E7XXX_DRA, &dra); drc_chan = dual_channel_active(drc, dev_idx); @@ -377,13 +378,12 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, for (index = 0; index < mci->nr_csrows; index++) { /* mem_dev 0=x8, 1=x4 */ mem_dev = (dra >> (index * 4 + 3)) & 0x1; - csrow = &mci->csrows[index]; + csrow = mci->csrows[index]; pci_read_config_byte(pdev, E7XXX_DRB + index, &value); /* convert a 64 or 32 MiB DRB to a page size. */ cumul_size = value << (25 + drc_drbg - PAGE_SHIFT); - debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index, - cumul_size); + edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size); if (cumul_size == last_cumul_size) continue; /* not populated */ @@ -392,28 +392,29 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, nr_pages = cumul_size - last_cumul_size; last_cumul_size = cumul_size; + /* + * if single channel or x8 devices then SECDED + * if dual channel and x4 then S4ECD4ED + */ + if (drc_ddim) { + if (drc_chan && mem_dev) { + edac_mode = EDAC_S4ECD4ED; + mci->edac_cap |= EDAC_FLAG_S4ECD4ED; + } else { + edac_mode = EDAC_SECDED; + mci->edac_cap |= EDAC_FLAG_SECDED; + } + } else + edac_mode = EDAC_NONE; + for (j = 0; j < drc_chan + 1; j++) { - dimm = csrow->channels[j].dimm; + dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / (drc_chan + 1); dimm->grain = 1 << 12; /* 4KiB - resolution of CELOG */ dimm->mtype = MEM_RDDR; /* only one type supported */ dimm->dtype = mem_dev ? DEV_X4 : DEV_X8; - - /* - * if single channel or x8 devices then SECDED - * if dual channel and x4 then S4ECD4ED - */ - if (drc_ddim) { - if (drc_chan && mem_dev) { - dimm->edac_mode = EDAC_S4ECD4ED; - mci->edac_cap |= EDAC_FLAG_S4ECD4ED; - } else { - dimm->edac_mode = EDAC_SECDED; - mci->edac_cap |= EDAC_FLAG_SECDED; - } - } else - dimm->edac_mode = EDAC_NONE; + dimm->edac_mode = edac_mode; } } } @@ -428,7 +429,7 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx) int drc_chan; struct e7xxx_error_info discard; - debugf0("%s(): mci\n", __func__); + edac_dbg(0, "mci\n"); pci_read_config_dword(pdev, E7XXX_DRC, &drc); @@ -451,15 +452,15 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx) if (mci == NULL) return -ENOMEM; - debugf3("%s(): init mci\n", __func__); + edac_dbg(3, "init mci\n"); mci->mtype_cap = MEM_FLAG_RDDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED | EDAC_FLAG_S4ECD4ED; /* FIXME - what if different memory types are in different csrows? */ mci->mod_name = EDAC_MOD_STR; mci->mod_ver = E7XXX_REVISION; - mci->dev = &pdev->dev; - debugf3("%s(): init pvt\n", __func__); + mci->pdev = &pdev->dev; + edac_dbg(3, "init pvt\n"); pvt = (struct e7xxx_pvt *)mci->pvt_info; pvt->dev_info = &e7xxx_devs[dev_idx]; pvt->bridge_ck = pci_get_device(PCI_VENDOR_ID_INTEL, @@ -472,14 +473,14 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx) goto fail0; } - debugf3("%s(): more mci init\n", __func__); + edac_dbg(3, "more mci init\n"); mci->ctl_name = pvt->dev_info->ctl_name; mci->dev_name = pci_name(pdev); mci->edac_check = e7xxx_check; mci->ctl_page_to_phys = ctl_page_to_phys; e7xxx_init_csrows(mci, pdev, dev_idx, drc); mci->edac_cap |= EDAC_FLAG_NONE; - debugf3("%s(): tolm, remapbase, remaplimit\n", __func__); + edac_dbg(3, "tolm, remapbase, remaplimit\n"); /* load the top of low memory, remap base, and remap limit vars */ pci_read_config_word(pdev, E7XXX_TOLM, &pci_data); pvt->tolm = ((u32) pci_data) << 4; @@ -498,7 +499,7 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx) * type of memory controller. The ID is therefore hardcoded to 0. */ if (edac_mc_add_mc(mci)) { - debugf3("%s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(3, "failed edac_mc_add_mc()\n"); goto fail1; } @@ -514,7 +515,7 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx) } /* get this far and it's successful */ - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); return 0; fail1: @@ -530,7 +531,7 @@ fail0: static int __devinit e7xxx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* wake up and enable device */ return pci_enable_device(pdev) ? @@ -542,7 +543,7 @@ static void __devexit e7xxx_remove_one(struct pci_dev *pdev) struct mem_ctl_info *mci; struct e7xxx_pvt *pvt; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); if (e7xxx_pci) edac_pci_release_generic_ctl(e7xxx_pci); diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index 117490d4f835..23bb99fa44f1 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h @@ -71,26 +71,21 @@ extern const char *edac_mem_types[]; #ifdef CONFIG_EDAC_DEBUG extern int edac_debug_level; -#define edac_debug_printk(level, fmt, arg...) \ - do { \ - if (level <= edac_debug_level) \ - edac_printk(KERN_DEBUG, EDAC_DEBUG, \ - "%s: " fmt, __func__, ##arg); \ - } while (0) - -#define debugf0( ... ) edac_debug_printk(0, __VA_ARGS__ ) -#define debugf1( ... ) edac_debug_printk(1, __VA_ARGS__ ) -#define debugf2( ... ) edac_debug_printk(2, __VA_ARGS__ ) -#define debugf3( ... ) edac_debug_printk(3, __VA_ARGS__ ) -#define debugf4( ... ) edac_debug_printk(4, __VA_ARGS__ ) +#define edac_dbg(level, fmt, ...) \ +do { \ + if (level <= edac_debug_level) \ + edac_printk(KERN_DEBUG, EDAC_DEBUG, \ + "%s: " fmt, __func__, ##__VA_ARGS__); \ +} while (0) #else /* !CONFIG_EDAC_DEBUG */ -#define debugf0( ... ) -#define debugf1( ... ) -#define debugf2( ... ) -#define debugf3( ... ) -#define debugf4( ... ) +#define edac_dbg(level, fmt, ...) \ +do { \ + if (0) \ + edac_printk(KERN_DEBUG, EDAC_DEBUG, \ + "%s: " fmt, __func__, ##__VA_ARGS__); \ +} while (0) #endif /* !CONFIG_EDAC_DEBUG */ @@ -460,15 +455,15 @@ extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page); void edac_mc_handle_error(const enum hw_event_mc_err_type type, struct mem_ctl_info *mci, + const u16 error_count, const unsigned long page_frame_number, const unsigned long offset_in_page, const unsigned long syndrome, - const int layer0, - const int layer1, - const int layer2, + const int top_layer, + const int mid_layer, + const int low_layer, const char *msg, - const char *other_detail, - const void *mcelog); + const char *other_detail); /* * edac_device APIs diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c index ee3f1f810c1e..211021dfec73 100644 --- a/drivers/edac/edac_device.c +++ b/drivers/edac/edac_device.c @@ -40,12 +40,13 @@ static LIST_HEAD(edac_device_list); #ifdef CONFIG_EDAC_DEBUG static void edac_device_dump_device(struct edac_device_ctl_info *edac_dev) { - debugf3("\tedac_dev = %p dev_idx=%d \n", edac_dev, edac_dev->dev_idx); - debugf4("\tedac_dev->edac_check = %p\n", edac_dev->edac_check); - debugf3("\tdev = %p\n", edac_dev->dev); - debugf3("\tmod_name:ctl_name = %s:%s\n", - edac_dev->mod_name, edac_dev->ctl_name); - debugf3("\tpvt_info = %p\n\n", edac_dev->pvt_info); + edac_dbg(3, "\tedac_dev = %p dev_idx=%d\n", + edac_dev, edac_dev->dev_idx); + edac_dbg(4, "\tedac_dev->edac_check = %p\n", edac_dev->edac_check); + edac_dbg(3, "\tdev = %p\n", edac_dev->dev); + edac_dbg(3, "\tmod_name:ctl_name = %s:%s\n", + edac_dev->mod_name, edac_dev->ctl_name); + edac_dbg(3, "\tpvt_info = %p\n\n", edac_dev->pvt_info); } #endif /* CONFIG_EDAC_DEBUG */ @@ -82,8 +83,7 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( void *pvt, *p; int err; - debugf4("%s() instances=%d blocks=%d\n", - __func__, nr_instances, nr_blocks); + edac_dbg(4, "instances=%d blocks=%d\n", nr_instances, nr_blocks); /* Calculate the size of memory we need to allocate AND * determine the offsets of the various item arrays @@ -156,8 +156,8 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( /* Name of this edac device */ snprintf(dev_ctl->name,sizeof(dev_ctl->name),"%s",edac_device_name); - debugf4("%s() edac_dev=%p next after end=%p\n", - __func__, dev_ctl, pvt + sz_private ); + edac_dbg(4, "edac_dev=%p next after end=%p\n", + dev_ctl, pvt + sz_private); /* Initialize every Instance */ for (instance = 0; instance < nr_instances; instance++) { @@ -178,10 +178,8 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( snprintf(blk->name, sizeof(blk->name), "%s%d", edac_block_name, block+offset_value); - debugf4("%s() instance=%d inst_p=%p block=#%d " - "block_p=%p name='%s'\n", - __func__, instance, inst, block, - blk, blk->name); + edac_dbg(4, "instance=%d inst_p=%p block=#%d block_p=%p name='%s'\n", + instance, inst, block, blk, blk->name); /* if there are NO attributes OR no attribute pointer * then continue on to next block iteration @@ -194,8 +192,8 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( attrib_p = &dev_attrib[block*nr_instances*nr_attrib]; blk->block_attributes = attrib_p; - debugf4("%s() THIS BLOCK_ATTRIB=%p\n", - __func__, blk->block_attributes); + edac_dbg(4, "THIS BLOCK_ATTRIB=%p\n", + blk->block_attributes); /* Initialize every user specified attribute in this * block with the data the caller passed in @@ -214,11 +212,10 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( attrib->block = blk; /* up link */ - debugf4("%s() alloc-attrib=%p attrib_name='%s' " - "attrib-spec=%p spec-name=%s\n", - __func__, attrib, attrib->attr.name, - &attrib_spec[attr], - attrib_spec[attr].attr.name + edac_dbg(4, "alloc-attrib=%p attrib_name='%s' attrib-spec=%p spec-name=%s\n", + attrib, attrib->attr.name, + &attrib_spec[attr], + attrib_spec[attr].attr.name ); } } @@ -273,7 +270,7 @@ static struct edac_device_ctl_info *find_edac_device_by_dev(struct device *dev) struct edac_device_ctl_info *edac_dev; struct list_head *item; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); list_for_each(item, &edac_device_list) { edac_dev = list_entry(item, struct edac_device_ctl_info, link); @@ -408,7 +405,7 @@ static void edac_device_workq_function(struct work_struct *work_req) void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev, unsigned msec) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* take the arg 'msec' and set it into the control structure * to used in the time period calculation @@ -496,7 +493,7 @@ EXPORT_SYMBOL_GPL(edac_device_alloc_index); */ int edac_device_add_device(struct edac_device_ctl_info *edac_dev) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); #ifdef CONFIG_EDAC_DEBUG if (edac_debug_level >= 3) @@ -570,7 +567,7 @@ struct edac_device_ctl_info *edac_device_del_device(struct device *dev) { struct edac_device_ctl_info *edac_dev; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); mutex_lock(&device_ctls_mutex); diff --git a/drivers/edac/edac_device_sysfs.c b/drivers/edac/edac_device_sysfs.c index b4ea185ccebf..fb68a06ad683 100644 --- a/drivers/edac/edac_device_sysfs.c +++ b/drivers/edac/edac_device_sysfs.c @@ -202,7 +202,7 @@ static void edac_device_ctrl_master_release(struct kobject *kobj) { struct edac_device_ctl_info *edac_dev = to_edacdev(kobj); - debugf4("%s() control index=%d\n", __func__, edac_dev->dev_idx); + edac_dbg(4, "control index=%d\n", edac_dev->dev_idx); /* decrement the EDAC CORE module ref count */ module_put(edac_dev->owner); @@ -233,12 +233,12 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev) struct bus_type *edac_subsys; int err; - debugf1("%s()\n", __func__); + edac_dbg(1, "\n"); /* get the /sys/devices/system/edac reference */ edac_subsys = edac_get_sysfs_subsys(); if (edac_subsys == NULL) { - debugf1("%s() no edac_subsys error\n", __func__); + edac_dbg(1, "no edac_subsys error\n"); err = -ENODEV; goto err_out; } @@ -264,8 +264,8 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev) &edac_subsys->dev_root->kobj, "%s", edac_dev->name); if (err) { - debugf1("%s()Failed to register '.../edac/%s'\n", - __func__, edac_dev->name); + edac_dbg(1, "Failed to register '.../edac/%s'\n", + edac_dev->name); goto err_kobj_reg; } kobject_uevent(&edac_dev->kobj, KOBJ_ADD); @@ -274,8 +274,7 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev) * edac_device_unregister_sysfs_main_kobj() must be used */ - debugf4("%s() Registered '.../edac/%s' kobject\n", - __func__, edac_dev->name); + edac_dbg(4, "Registered '.../edac/%s' kobject\n", edac_dev->name); return 0; @@ -296,9 +295,8 @@ err_out: */ void edac_device_unregister_sysfs_main_kobj(struct edac_device_ctl_info *dev) { - debugf0("%s()\n", __func__); - debugf4("%s() name of kobject is: %s\n", - __func__, kobject_name(&dev->kobj)); + edac_dbg(0, "\n"); + edac_dbg(4, "name of kobject is: %s\n", kobject_name(&dev->kobj)); /* * Unregister the edac device's kobject and @@ -336,7 +334,7 @@ static void edac_device_ctrl_instance_release(struct kobject *kobj) { struct edac_device_instance *instance; - debugf1("%s()\n", __func__); + edac_dbg(1, "\n"); /* map from this kobj to the main control struct * and then dec the main kobj count @@ -442,7 +440,7 @@ static void edac_device_ctrl_block_release(struct kobject *kobj) { struct edac_device_block *block; - debugf1("%s()\n", __func__); + edac_dbg(1, "\n"); /* get the container of the kobj */ block = to_block(kobj); @@ -524,10 +522,10 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev, struct edac_dev_sysfs_block_attribute *sysfs_attrib; struct kobject *main_kobj; - debugf4("%s() Instance '%s' inst_p=%p block '%s' block_p=%p\n", - __func__, instance->name, instance, block->name, block); - debugf4("%s() block kobj=%p block kobj->parent=%p\n", - __func__, &block->kobj, &block->kobj.parent); + edac_dbg(4, "Instance '%s' inst_p=%p block '%s' block_p=%p\n", + instance->name, instance, block->name, block); + edac_dbg(4, "block kobj=%p block kobj->parent=%p\n", + &block->kobj, &block->kobj.parent); /* init this block's kobject */ memset(&block->kobj, 0, sizeof(struct kobject)); @@ -546,8 +544,7 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev, &instance->kobj, "%s", block->name); if (err) { - debugf1("%s() Failed to register instance '%s'\n", - __func__, block->name); + edac_dbg(1, "Failed to register instance '%s'\n", block->name); kobject_put(main_kobj); err = -ENODEV; goto err_out; @@ -560,11 +557,9 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev, if (sysfs_attrib && block->nr_attribs) { for (i = 0; i < block->nr_attribs; i++, sysfs_attrib++) { - debugf4("%s() creating block attrib='%s' " - "attrib->%p to kobj=%p\n", - __func__, - sysfs_attrib->attr.name, - sysfs_attrib, &block->kobj); + edac_dbg(4, "creating block attrib='%s' attrib->%p to kobj=%p\n", + sysfs_attrib->attr.name, + sysfs_attrib, &block->kobj); /* Create each block_attribute file */ err = sysfs_create_file(&block->kobj, @@ -647,14 +642,14 @@ static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev, err = kobject_init_and_add(&instance->kobj, &ktype_instance_ctrl, &edac_dev->kobj, "%s", instance->name); if (err != 0) { - debugf2("%s() Failed to register instance '%s'\n", - __func__, instance->name); + edac_dbg(2, "Failed to register instance '%s'\n", + instance->name); kobject_put(main_kobj); goto err_out; } - debugf4("%s() now register '%d' blocks for instance %d\n", - __func__, instance->nr_blocks, idx); + edac_dbg(4, "now register '%d' blocks for instance %d\n", + instance->nr_blocks, idx); /* register all blocks of this instance */ for (i = 0; i < instance->nr_blocks; i++) { @@ -670,8 +665,8 @@ static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev, } kobject_uevent(&instance->kobj, KOBJ_ADD); - debugf4("%s() Registered instance %d '%s' kobject\n", - __func__, idx, instance->name); + edac_dbg(4, "Registered instance %d '%s' kobject\n", + idx, instance->name); return 0; @@ -715,7 +710,7 @@ static int edac_device_create_instances(struct edac_device_ctl_info *edac_dev) int i, j; int err; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* iterate over creation of the instances */ for (i = 0; i < edac_dev->nr_instances; i++) { @@ -817,12 +812,12 @@ int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev) int err; struct kobject *edac_kobj = &edac_dev->kobj; - debugf0("%s() idx=%d\n", __func__, edac_dev->dev_idx); + edac_dbg(0, "idx=%d\n", edac_dev->dev_idx); /* go create any main attributes callers wants */ err = edac_device_add_main_sysfs_attributes(edac_dev); if (err) { - debugf0("%s() failed to add sysfs attribs\n", __func__); + edac_dbg(0, "failed to add sysfs attribs\n"); goto err_out; } @@ -832,8 +827,7 @@ int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev) err = sysfs_create_link(edac_kobj, &edac_dev->dev->kobj, EDAC_DEVICE_SYMLINK); if (err) { - debugf0("%s() sysfs_create_link() returned err= %d\n", - __func__, err); + edac_dbg(0, "sysfs_create_link() returned err= %d\n", err); goto err_remove_main_attribs; } @@ -843,14 +837,13 @@ int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev) */ err = edac_device_create_instances(edac_dev); if (err) { - debugf0("%s() edac_device_create_instances() " - "returned err= %d\n", __func__, err); + edac_dbg(0, "edac_device_create_instances() returned err= %d\n", + err); goto err_remove_link; } - debugf4("%s() create-instances done, idx=%d\n", - __func__, edac_dev->dev_idx); + edac_dbg(4, "create-instances done, idx=%d\n", edac_dev->dev_idx); return 0; @@ -873,7 +866,7 @@ err_out: */ void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* remove any main attributes for this device */ edac_device_remove_main_sysfs_attributes(edac_dev); diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index de5ba86e8b89..616d90bcb3a4 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -27,70 +27,95 @@ #include <linux/list.h> #include <linux/ctype.h> #include <linux/edac.h> +#include <linux/bitops.h> #include <asm/uaccess.h> #include <asm/page.h> #include <asm/edac.h> #include "edac_core.h" #include "edac_module.h" +#define CREATE_TRACE_POINTS +#define TRACE_INCLUDE_PATH ../../include/ras +#include <ras/ras_event.h> + /* lock to memory controller's control array */ static DEFINE_MUTEX(mem_ctls_mutex); static LIST_HEAD(mc_devices); +unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf, + unsigned len) +{ + struct mem_ctl_info *mci = dimm->mci; + int i, n, count = 0; + char *p = buf; + + for (i = 0; i < mci->n_layers; i++) { + n = snprintf(p, len, "%s %d ", + edac_layer_name[mci->layers[i].type], + dimm->location[i]); + p += n; + len -= n; + count += n; + if (!len) + break; + } + + return count; +} + #ifdef CONFIG_EDAC_DEBUG static void edac_mc_dump_channel(struct rank_info *chan) { - debugf4("\tchannel = %p\n", chan); - debugf4("\tchannel->chan_idx = %d\n", chan->chan_idx); - debugf4("\tchannel->csrow = %p\n\n", chan->csrow); - debugf4("\tchannel->dimm = %p\n", chan->dimm); + edac_dbg(4, " channel->chan_idx = %d\n", chan->chan_idx); + edac_dbg(4, " channel = %p\n", chan); + edac_dbg(4, " channel->csrow = %p\n", chan->csrow); + edac_dbg(4, " channel->dimm = %p\n", chan->dimm); } -static void edac_mc_dump_dimm(struct dimm_info *dimm) +static void edac_mc_dump_dimm(struct dimm_info *dimm, int number) { - int i; - - debugf4("\tdimm = %p\n", dimm); - debugf4("\tdimm->label = '%s'\n", dimm->label); - debugf4("\tdimm->nr_pages = 0x%x\n", dimm->nr_pages); - debugf4("\tdimm location "); - for (i = 0; i < dimm->mci->n_layers; i++) { - printk(KERN_CONT "%d", dimm->location[i]); - if (i < dimm->mci->n_layers - 1) - printk(KERN_CONT "."); - } - printk(KERN_CONT "\n"); - debugf4("\tdimm->grain = %d\n", dimm->grain); - debugf4("\tdimm->nr_pages = 0x%x\n", dimm->nr_pages); + char location[80]; + + edac_dimm_info_location(dimm, location, sizeof(location)); + + edac_dbg(4, "%s%i: %smapped as virtual row %d, chan %d\n", + dimm->mci->mem_is_per_rank ? "rank" : "dimm", + number, location, dimm->csrow, dimm->cschannel); + edac_dbg(4, " dimm = %p\n", dimm); + edac_dbg(4, " dimm->label = '%s'\n", dimm->label); + edac_dbg(4, " dimm->nr_pages = 0x%x\n", dimm->nr_pages); + edac_dbg(4, " dimm->grain = %d\n", dimm->grain); + edac_dbg(4, " dimm->nr_pages = 0x%x\n", dimm->nr_pages); } static void edac_mc_dump_csrow(struct csrow_info *csrow) { - debugf4("\tcsrow = %p\n", csrow); - debugf4("\tcsrow->csrow_idx = %d\n", csrow->csrow_idx); - debugf4("\tcsrow->first_page = 0x%lx\n", csrow->first_page); - debugf4("\tcsrow->last_page = 0x%lx\n", csrow->last_page); - debugf4("\tcsrow->page_mask = 0x%lx\n", csrow->page_mask); - debugf4("\tcsrow->nr_channels = %d\n", csrow->nr_channels); - debugf4("\tcsrow->channels = %p\n", csrow->channels); - debugf4("\tcsrow->mci = %p\n\n", csrow->mci); + edac_dbg(4, "csrow->csrow_idx = %d\n", csrow->csrow_idx); + edac_dbg(4, " csrow = %p\n", csrow); + edac_dbg(4, " csrow->first_page = 0x%lx\n", csrow->first_page); + edac_dbg(4, " csrow->last_page = 0x%lx\n", csrow->last_page); + edac_dbg(4, " csrow->page_mask = 0x%lx\n", csrow->page_mask); + edac_dbg(4, " csrow->nr_channels = %d\n", csrow->nr_channels); + edac_dbg(4, " csrow->channels = %p\n", csrow->channels); + edac_dbg(4, " csrow->mci = %p\n", csrow->mci); } static void edac_mc_dump_mci(struct mem_ctl_info *mci) { - debugf3("\tmci = %p\n", mci); - debugf3("\tmci->mtype_cap = %lx\n", mci->mtype_cap); - debugf3("\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap); - debugf3("\tmci->edac_cap = %lx\n", mci->edac_cap); - debugf4("\tmci->edac_check = %p\n", mci->edac_check); - debugf3("\tmci->nr_csrows = %d, csrows = %p\n", - mci->nr_csrows, mci->csrows); - debugf3("\tmci->nr_dimms = %d, dimms = %p\n", - mci->tot_dimms, mci->dimms); - debugf3("\tdev = %p\n", mci->dev); - debugf3("\tmod_name:ctl_name = %s:%s\n", mci->mod_name, mci->ctl_name); - debugf3("\tpvt_info = %p\n\n", mci->pvt_info); + edac_dbg(3, "\tmci = %p\n", mci); + edac_dbg(3, "\tmci->mtype_cap = %lx\n", mci->mtype_cap); + edac_dbg(3, "\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap); + edac_dbg(3, "\tmci->edac_cap = %lx\n", mci->edac_cap); + edac_dbg(4, "\tmci->edac_check = %p\n", mci->edac_check); + edac_dbg(3, "\tmci->nr_csrows = %d, csrows = %p\n", + mci->nr_csrows, mci->csrows); + edac_dbg(3, "\tmci->nr_dimms = %d, dimms = %p\n", + mci->tot_dimms, mci->dimms); + edac_dbg(3, "\tdev = %p\n", mci->pdev); + edac_dbg(3, "\tmod_name:ctl_name = %s:%s\n", + mci->mod_name, mci->ctl_name); + edac_dbg(3, "\tpvt_info = %p\n\n", mci->pvt_info); } #endif /* CONFIG_EDAC_DEBUG */ @@ -205,15 +230,15 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, { struct mem_ctl_info *mci; struct edac_mc_layer *layer; - struct csrow_info *csi, *csr; - struct rank_info *chi, *chp, *chan; + struct csrow_info *csr; + struct rank_info *chan; struct dimm_info *dimm; u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS]; unsigned pos[EDAC_MAX_LAYERS]; unsigned size, tot_dimms = 1, count = 1; unsigned tot_csrows = 1, tot_channels = 1, tot_errcount = 0; void *pvt, *p, *ptr = NULL; - int i, j, err, row, chn, n, len; + int i, j, row, chn, n, len, off; bool per_rank = false; BUG_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0); @@ -239,26 +264,24 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, */ mci = edac_align_ptr(&ptr, sizeof(*mci), 1); layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers); - csi = edac_align_ptr(&ptr, sizeof(*csi), tot_csrows); - chi = edac_align_ptr(&ptr, sizeof(*chi), tot_csrows * tot_channels); - dimm = edac_align_ptr(&ptr, sizeof(*dimm), tot_dimms); for (i = 0; i < n_layers; i++) { count *= layers[i].size; - debugf4("%s: errcount layer %d size %d\n", __func__, i, count); + edac_dbg(4, "errcount layer %d size %d\n", i, count); ce_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count); ue_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count); tot_errcount += 2 * count; } - debugf4("%s: allocating %d error counters\n", __func__, tot_errcount); + edac_dbg(4, "allocating %d error counters\n", tot_errcount); pvt = edac_align_ptr(&ptr, sz_pvt, 1); size = ((unsigned long)pvt) + sz_pvt; - debugf1("%s(): allocating %u bytes for mci data (%d %s, %d csrows/channels)\n", - __func__, size, - tot_dimms, - per_rank ? "ranks" : "dimms", - tot_csrows * tot_channels); + edac_dbg(1, "allocating %u bytes for mci data (%d %s, %d csrows/channels)\n", + size, + tot_dimms, + per_rank ? "ranks" : "dimms", + tot_csrows * tot_channels); + mci = kzalloc(size, GFP_KERNEL); if (mci == NULL) return NULL; @@ -267,9 +290,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, * rather than an imaginary chunk of memory located at address 0. */ layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer)); - csi = (struct csrow_info *)(((char *)mci) + ((unsigned long)csi)); - chi = (struct rank_info *)(((char *)mci) + ((unsigned long)chi)); - dimm = (struct dimm_info *)(((char *)mci) + ((unsigned long)dimm)); for (i = 0; i < n_layers; i++) { mci->ce_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ce_per_layer[i])); mci->ue_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ue_per_layer[i])); @@ -278,8 +298,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, /* setup index and various internal pointers */ mci->mc_idx = mc_num; - mci->csrows = csi; - mci->dimms = dimm; mci->tot_dimms = tot_dimms; mci->pvt_info = pvt; mci->n_layers = n_layers; @@ -290,40 +308,57 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, mci->mem_is_per_rank = per_rank; /* - * Fill the csrow struct + * Alocate and fill the csrow/channels structs */ + mci->csrows = kcalloc(sizeof(*mci->csrows), tot_csrows, GFP_KERNEL); + if (!mci->csrows) + goto error; for (row = 0; row < tot_csrows; row++) { - csr = &csi[row]; + csr = kzalloc(sizeof(**mci->csrows), GFP_KERNEL); + if (!csr) + goto error; + mci->csrows[row] = csr; csr->csrow_idx = row; csr->mci = mci; csr->nr_channels = tot_channels; - chp = &chi[row * tot_channels]; - csr->channels = chp; + csr->channels = kcalloc(sizeof(*csr->channels), tot_channels, + GFP_KERNEL); + if (!csr->channels) + goto error; for (chn = 0; chn < tot_channels; chn++) { - chan = &chp[chn]; + chan = kzalloc(sizeof(**csr->channels), GFP_KERNEL); + if (!chan) + goto error; + csr->channels[chn] = chan; chan->chan_idx = chn; chan->csrow = csr; } } /* - * Fill the dimm struct + * Allocate and fill the dimm structs */ + mci->dimms = kcalloc(sizeof(*mci->dimms), tot_dimms, GFP_KERNEL); + if (!mci->dimms) + goto error; + memset(&pos, 0, sizeof(pos)); row = 0; chn = 0; - debugf4("%s: initializing %d %s\n", __func__, tot_dimms, - per_rank ? "ranks" : "dimms"); for (i = 0; i < tot_dimms; i++) { - chan = &csi[row].channels[chn]; - dimm = EDAC_DIMM_PTR(layer, mci->dimms, n_layers, - pos[0], pos[1], pos[2]); - dimm->mci = mci; + chan = mci->csrows[row]->channels[chn]; + off = EDAC_DIMM_OFF(layer, n_layers, pos[0], pos[1], pos[2]); + if (off < 0 || off >= tot_dimms) { + edac_mc_printk(mci, KERN_ERR, "EDAC core bug: EDAC_DIMM_OFF is trying to do an illegal data access\n"); + goto error; + } - debugf2("%s: %d: %s%zd (%d:%d:%d): row %d, chan %d\n", __func__, - i, per_rank ? "rank" : "dimm", (dimm - mci->dimms), - pos[0], pos[1], pos[2], row, chn); + dimm = kzalloc(sizeof(**mci->dimms), GFP_KERNEL); + if (!dimm) + goto error; + mci->dimms[off] = dimm; + dimm->mci = mci; /* * Copy DIMM location and initialize it. @@ -367,16 +402,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, } mci->op_state = OP_ALLOC; - INIT_LIST_HEAD(&mci->grp_kobj_list); - - /* - * Initialize the 'root' kobj for the edac_mc controller - */ - err = edac_mc_register_sysfs_main_kobj(mci); - if (err) { - kfree(mci); - return NULL; - } /* at this point, the root kobj is valid, and in order to * 'free' the object, then the function: @@ -384,7 +409,30 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, * which will perform kobj unregistration and the actual free * will occur during the kobject callback operation */ + return mci; + +error: + if (mci->dimms) { + for (i = 0; i < tot_dimms; i++) + kfree(mci->dimms[i]); + kfree(mci->dimms); + } + if (mci->csrows) { + for (chn = 0; chn < tot_channels; chn++) { + csr = mci->csrows[chn]; + if (csr) { + for (chn = 0; chn < tot_channels; chn++) + kfree(csr->channels[chn]); + kfree(csr); + } + kfree(mci->csrows[i]); + } + kfree(mci->csrows); + } + kfree(mci); + + return NULL; } EXPORT_SYMBOL_GPL(edac_mc_alloc); @@ -395,12 +443,10 @@ EXPORT_SYMBOL_GPL(edac_mc_alloc); */ void edac_mc_free(struct mem_ctl_info *mci) { - debugf1("%s()\n", __func__); + edac_dbg(1, "\n"); - edac_mc_unregister_sysfs_main_kobj(mci); - - /* free the mci instance memory here */ - kfree(mci); + /* the mci instance is freed here, when the sysfs object is dropped */ + edac_unregister_sysfs(mci); } EXPORT_SYMBOL_GPL(edac_mc_free); @@ -417,12 +463,12 @@ struct mem_ctl_info *find_mci_by_dev(struct device *dev) struct mem_ctl_info *mci; struct list_head *item; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); list_for_each(item, &mc_devices) { mci = list_entry(item, struct mem_ctl_info, link); - if (mci->dev == dev) + if (mci->pdev == dev) return mci; } @@ -485,7 +531,7 @@ static void edac_mc_workq_function(struct work_struct *work_req) */ static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* if this instance is not in the POLL state, then simply return */ if (mci->op_state != OP_RUNNING_POLL) @@ -512,8 +558,7 @@ static void edac_mc_workq_teardown(struct mem_ctl_info *mci) status = cancel_delayed_work(&mci->work); if (status == 0) { - debugf0("%s() not canceled, flush the queue\n", - __func__); + edac_dbg(0, "not canceled, flush the queue\n"); /* workq instance might be running, wait for it */ flush_workqueue(edac_workqueue); @@ -574,7 +619,7 @@ static int add_mc_to_global_list(struct mem_ctl_info *mci) insert_before = &mc_devices; - p = find_mci_by_dev(mci->dev); + p = find_mci_by_dev(mci->pdev); if (unlikely(p != NULL)) goto fail0; @@ -596,7 +641,7 @@ static int add_mc_to_global_list(struct mem_ctl_info *mci) fail0: edac_printk(KERN_WARNING, EDAC_MC, - "%s (%s) %s %s already assigned %d\n", dev_name(p->dev), + "%s (%s) %s %s already assigned %d\n", dev_name(p->pdev), edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx); return 1; @@ -660,7 +705,7 @@ EXPORT_SYMBOL(edac_mc_find); /* FIXME - should a warning be printed if no error detection? correction? */ int edac_mc_add_mc(struct mem_ctl_info *mci) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); #ifdef CONFIG_EDAC_DEBUG if (edac_debug_level >= 3) @@ -670,15 +715,22 @@ int edac_mc_add_mc(struct mem_ctl_info *mci) int i; for (i = 0; i < mci->nr_csrows; i++) { + struct csrow_info *csrow = mci->csrows[i]; + u32 nr_pages = 0; int j; - edac_mc_dump_csrow(&mci->csrows[i]); - for (j = 0; j < mci->csrows[i].nr_channels; j++) - edac_mc_dump_channel(&mci->csrows[i]. - channels[j]); + for (j = 0; j < csrow->nr_channels; j++) + nr_pages += csrow->channels[j]->dimm->nr_pages; + if (!nr_pages) + continue; + edac_mc_dump_csrow(csrow); + for (j = 0; j < csrow->nr_channels; j++) + if (csrow->channels[j]->dimm->nr_pages) + edac_mc_dump_channel(csrow->channels[j]); } for (i = 0; i < mci->tot_dimms; i++) - edac_mc_dump_dimm(&mci->dimms[i]); + if (mci->dimms[i]->nr_pages) + edac_mc_dump_dimm(mci->dimms[i], i); } #endif mutex_lock(&mem_ctls_mutex); @@ -732,7 +784,7 @@ struct mem_ctl_info *edac_mc_del_mc(struct device *dev) { struct mem_ctl_info *mci; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); mutex_lock(&mem_ctls_mutex); @@ -770,7 +822,7 @@ static void edac_mc_scrub_block(unsigned long page, unsigned long offset, void *virt_addr; unsigned long flags = 0; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); /* ECC error page was not in our memory. Ignore it. */ if (!pfn_valid(page)) @@ -797,26 +849,26 @@ static void edac_mc_scrub_block(unsigned long page, unsigned long offset, /* FIXME - should return -1 */ int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page) { - struct csrow_info *csrows = mci->csrows; + struct csrow_info **csrows = mci->csrows; int row, i, j, n; - debugf1("MC%d: %s(): 0x%lx\n", mci->mc_idx, __func__, page); + edac_dbg(1, "MC%d: 0x%lx\n", mci->mc_idx, page); row = -1; for (i = 0; i < mci->nr_csrows; i++) { - struct csrow_info *csrow = &csrows[i]; + struct csrow_info *csrow = csrows[i]; n = 0; for (j = 0; j < csrow->nr_channels; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; n += dimm->nr_pages; } if (n == 0) continue; - debugf3("MC%d: %s(): first(0x%lx) page(0x%lx) last(0x%lx) " - "mask(0x%lx)\n", mci->mc_idx, __func__, - csrow->first_page, page, csrow->last_page, - csrow->page_mask); + edac_dbg(3, "MC%d: first(0x%lx) page(0x%lx) last(0x%lx) mask(0x%lx)\n", + mci->mc_idx, + csrow->first_page, page, csrow->last_page, + csrow->page_mask); if ((page >= csrow->first_page) && (page <= csrow->last_page) && @@ -845,15 +897,16 @@ const char *edac_layer_name[] = { EXPORT_SYMBOL_GPL(edac_layer_name); static void edac_inc_ce_error(struct mem_ctl_info *mci, - bool enable_per_layer_report, - const int pos[EDAC_MAX_LAYERS]) + bool enable_per_layer_report, + const int pos[EDAC_MAX_LAYERS], + const u16 count) { int i, index = 0; - mci->ce_mc++; + mci->ce_mc += count; if (!enable_per_layer_report) { - mci->ce_noinfo_count++; + mci->ce_noinfo_count += count; return; } @@ -861,7 +914,7 @@ static void edac_inc_ce_error(struct mem_ctl_info *mci, if (pos[i] < 0) break; index += pos[i]; - mci->ce_per_layer[i][index]++; + mci->ce_per_layer[i][index] += count; if (i < mci->n_layers - 1) index *= mci->layers[i + 1].size; @@ -870,14 +923,15 @@ static void edac_inc_ce_error(struct mem_ctl_info *mci, static void edac_inc_ue_error(struct mem_ctl_info *mci, bool enable_per_layer_report, - const int pos[EDAC_MAX_LAYERS]) + const int pos[EDAC_MAX_LAYERS], + const u16 count) { int i, index = 0; - mci->ue_mc++; + mci->ue_mc += count; if (!enable_per_layer_report) { - mci->ce_noinfo_count++; + mci->ce_noinfo_count += count; return; } @@ -885,7 +939,7 @@ static void edac_inc_ue_error(struct mem_ctl_info *mci, if (pos[i] < 0) break; index += pos[i]; - mci->ue_per_layer[i][index]++; + mci->ue_per_layer[i][index] += count; if (i < mci->n_layers - 1) index *= mci->layers[i + 1].size; @@ -893,6 +947,7 @@ static void edac_inc_ue_error(struct mem_ctl_info *mci, } static void edac_ce_error(struct mem_ctl_info *mci, + const u16 error_count, const int pos[EDAC_MAX_LAYERS], const char *msg, const char *location, @@ -902,23 +957,25 @@ static void edac_ce_error(struct mem_ctl_info *mci, const bool enable_per_layer_report, const unsigned long page_frame_number, const unsigned long offset_in_page, - u32 grain) + long grain) { unsigned long remapped_page; if (edac_mc_get_log_ce()) { if (other_detail && *other_detail) edac_mc_printk(mci, KERN_WARNING, - "CE %s on %s (%s%s - %s)\n", + "%d CE %s on %s (%s %s - %s)\n", + error_count, msg, label, location, detail, other_detail); else edac_mc_printk(mci, KERN_WARNING, - "CE %s on %s (%s%s)\n", + "%d CE %s on %s (%s %s)\n", + error_count, msg, label, location, detail); } - edac_inc_ce_error(mci, enable_per_layer_report, pos); + edac_inc_ce_error(mci, enable_per_layer_report, pos, error_count); if (mci->scrub_mode & SCRUB_SW_SRC) { /* @@ -942,6 +999,7 @@ static void edac_ce_error(struct mem_ctl_info *mci, } static void edac_ue_error(struct mem_ctl_info *mci, + const u16 error_count, const int pos[EDAC_MAX_LAYERS], const char *msg, const char *location, @@ -953,12 +1011,14 @@ static void edac_ue_error(struct mem_ctl_info *mci, if (edac_mc_get_log_ue()) { if (other_detail && *other_detail) edac_mc_printk(mci, KERN_WARNING, - "UE %s on %s (%s%s - %s)\n", + "%d UE %s on %s (%s %s - %s)\n", + error_count, msg, label, location, detail, other_detail); else edac_mc_printk(mci, KERN_WARNING, - "UE %s on %s (%s%s)\n", + "%d UE %s on %s (%s %s)\n", + error_count, msg, label, location, detail); } @@ -971,33 +1031,53 @@ static void edac_ue_error(struct mem_ctl_info *mci, msg, label, location, detail); } - edac_inc_ue_error(mci, enable_per_layer_report, pos); + edac_inc_ue_error(mci, enable_per_layer_report, pos, error_count); } #define OTHER_LABEL " or " + +/** + * edac_mc_handle_error - reports a memory event to userspace + * + * @type: severity of the error (CE/UE/Fatal) + * @mci: a struct mem_ctl_info pointer + * @error_count: Number of errors of the same type + * @page_frame_number: mem page where the error occurred + * @offset_in_page: offset of the error inside the page + * @syndrome: ECC syndrome + * @top_layer: Memory layer[0] position + * @mid_layer: Memory layer[1] position + * @low_layer: Memory layer[2] position + * @msg: Message meaningful to the end users that + * explains the event + * @other_detail: Technical details about the event that + * may help hardware manufacturers and + * EDAC developers to analyse the event + */ void edac_mc_handle_error(const enum hw_event_mc_err_type type, struct mem_ctl_info *mci, + const u16 error_count, const unsigned long page_frame_number, const unsigned long offset_in_page, const unsigned long syndrome, - const int layer0, - const int layer1, - const int layer2, + const int top_layer, + const int mid_layer, + const int low_layer, const char *msg, - const char *other_detail, - const void *mcelog) + const char *other_detail) { /* FIXME: too much for stack: move it to some pre-alocated area */ char detail[80], location[80]; char label[(EDAC_MC_LABEL_LEN + 1 + sizeof(OTHER_LABEL)) * mci->tot_dimms]; char *p; int row = -1, chan = -1; - int pos[EDAC_MAX_LAYERS] = { layer0, layer1, layer2 }; + int pos[EDAC_MAX_LAYERS] = { top_layer, mid_layer, low_layer }; int i; - u32 grain; + long grain; bool enable_per_layer_report = false; + u8 grain_bits; - debugf3("MC%d: %s()\n", mci->mc_idx, __func__); + edac_dbg(3, "MC%d\n", mci->mc_idx); /* * Check if the event report is consistent and if the memory @@ -1043,13 +1123,13 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, p = label; *p = '\0'; for (i = 0; i < mci->tot_dimms; i++) { - struct dimm_info *dimm = &mci->dimms[i]; + struct dimm_info *dimm = mci->dimms[i]; - if (layer0 >= 0 && layer0 != dimm->location[0]) + if (top_layer >= 0 && top_layer != dimm->location[0]) continue; - if (layer1 >= 0 && layer1 != dimm->location[1]) + if (mid_layer >= 0 && mid_layer != dimm->location[1]) continue; - if (layer2 >= 0 && layer2 != dimm->location[2]) + if (low_layer >= 0 && low_layer != dimm->location[2]) continue; /* get the max grain, over the error match range */ @@ -1075,11 +1155,9 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, * get csrow/channel of the DIMM, in order to allow * incrementing the compat API counters */ - debugf4("%s: %s csrows map: (%d,%d)\n", - __func__, - mci->mem_is_per_rank ? "rank" : "dimm", - dimm->csrow, dimm->cschannel); - + edac_dbg(4, "%s csrows map: (%d,%d)\n", + mci->mem_is_per_rank ? "rank" : "dimm", + dimm->csrow, dimm->cschannel); if (row == -1) row = dimm->csrow; else if (row >= 0 && row != dimm->csrow) @@ -1095,19 +1173,18 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, if (!enable_per_layer_report) { strcpy(label, "any memory"); } else { - debugf4("%s: csrow/channel to increment: (%d,%d)\n", - __func__, row, chan); + edac_dbg(4, "csrow/channel to increment: (%d,%d)\n", row, chan); if (p == label) strcpy(label, "unknown memory"); if (type == HW_EVENT_ERR_CORRECTED) { if (row >= 0) { - mci->csrows[row].ce_count++; + mci->csrows[row]->ce_count += error_count; if (chan >= 0) - mci->csrows[row].channels[chan].ce_count++; + mci->csrows[row]->channels[chan]->ce_count += error_count; } } else if (row >= 0) - mci->csrows[row].ue_count++; + mci->csrows[row]->ue_count += error_count; } /* Fill the RAM location data */ @@ -1120,23 +1197,33 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, edac_layer_name[mci->layers[i].type], pos[i]); } + if (p > location) + *(p - 1) = '\0'; + + /* Report the error via the trace interface */ + + grain_bits = fls_long(grain) + 1; + trace_mc_event(type, msg, label, error_count, + mci->mc_idx, top_layer, mid_layer, low_layer, + PAGES_TO_MiB(page_frame_number) | offset_in_page, + grain_bits, syndrome, other_detail); /* Memory type dependent details about the error */ if (type == HW_EVENT_ERR_CORRECTED) { snprintf(detail, sizeof(detail), - "page:0x%lx offset:0x%lx grain:%d syndrome:0x%lx", + "page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx", page_frame_number, offset_in_page, grain, syndrome); - edac_ce_error(mci, pos, msg, location, label, detail, - other_detail, enable_per_layer_report, + edac_ce_error(mci, error_count, pos, msg, location, label, + detail, other_detail, enable_per_layer_report, page_frame_number, offset_in_page, grain); } else { snprintf(detail, sizeof(detail), - "page:0x%lx offset:0x%lx grain:%d", + "page:0x%lx offset:0x%lx grain:%ld", page_frame_number, offset_in_page, grain); - edac_ue_error(mci, pos, msg, location, label, detail, - other_detail, enable_per_layer_report); + edac_ue_error(mci, error_count, pos, msg, location, label, + detail, other_detail, enable_per_layer_report); } } EXPORT_SYMBOL_GPL(edac_mc_handle_error); diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index f6a29b0eedc8..ed0bc07b8503 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -7,17 +7,21 @@ * * Written Doug Thompson <norsk5@xmission.com> www.softwarebitmaker.com * + * (c) 2012 - Mauro Carvalho Chehab <mchehab@redhat.com> + * The entire API were re-written, and ported to use struct device + * */ #include <linux/ctype.h> #include <linux/slab.h> #include <linux/edac.h> #include <linux/bug.h> +#include <linux/pm_runtime.h> +#include <linux/uaccess.h> #include "edac_core.h" #include "edac_module.h" - /* MC EDAC Controls, setable by module parameter, and sysfs */ static int edac_mc_log_ue = 1; static int edac_mc_log_ce = 1; @@ -78,6 +82,8 @@ module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int, &edac_mc_poll_msec, 0644); MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds"); +static struct device *mci_pdev; + /* * various constants for Memory Controllers */ @@ -125,317 +131,526 @@ static const char *edac_caps[] = { [EDAC_S16ECD16ED] = "S16ECD16ED" }; -/* EDAC sysfs CSROW data structures and methods +#ifdef CONFIG_EDAC_LEGACY_SYSFS +/* + * EDAC sysfs CSROW data structures and methods + */ + +#define to_csrow(k) container_of(k, struct csrow_info, dev) + +/* + * We need it to avoid namespace conflicts between the legacy API + * and the per-dimm/per-rank one */ +#define DEVICE_ATTR_LEGACY(_name, _mode, _show, _store) \ + struct device_attribute dev_attr_legacy_##_name = __ATTR(_name, _mode, _show, _store) + +struct dev_ch_attribute { + struct device_attribute attr; + int channel; +}; + +#define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \ + struct dev_ch_attribute dev_attr_legacy_##_name = \ + { __ATTR(_name, _mode, _show, _store), (_var) } + +#define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel) /* Set of more default csrow<id> attribute show/store functions */ -static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_ue_count_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct csrow_info *csrow = to_csrow(dev); + return sprintf(data, "%u\n", csrow->ue_count); } -static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_ce_count_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct csrow_info *csrow = to_csrow(dev); + return sprintf(data, "%u\n", csrow->ce_count); } -static ssize_t csrow_size_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_size_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct csrow_info *csrow = to_csrow(dev); int i; u32 nr_pages = 0; for (i = 0; i < csrow->nr_channels; i++) - nr_pages += csrow->channels[i].dimm->nr_pages; - + nr_pages += csrow->channels[i]->dimm->nr_pages; return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages)); } -static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_mem_type_show(struct device *dev, + struct device_attribute *mattr, char *data) { - return sprintf(data, "%s\n", mem_types[csrow->channels[0].dimm->mtype]); + struct csrow_info *csrow = to_csrow(dev); + + return sprintf(data, "%s\n", mem_types[csrow->channels[0]->dimm->mtype]); } -static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_dev_type_show(struct device *dev, + struct device_attribute *mattr, char *data) { - return sprintf(data, "%s\n", dev_types[csrow->channels[0].dimm->dtype]); + struct csrow_info *csrow = to_csrow(dev); + + return sprintf(data, "%s\n", dev_types[csrow->channels[0]->dimm->dtype]); } -static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_edac_mode_show(struct device *dev, + struct device_attribute *mattr, + char *data) { - return sprintf(data, "%s\n", edac_caps[csrow->channels[0].dimm->edac_mode]); + struct csrow_info *csrow = to_csrow(dev); + + return sprintf(data, "%s\n", edac_caps[csrow->channels[0]->dimm->edac_mode]); } /* show/store functions for DIMM Label attributes */ -static ssize_t channel_dimm_label_show(struct csrow_info *csrow, - char *data, int channel) +static ssize_t channel_dimm_label_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct csrow_info *csrow = to_csrow(dev); + unsigned chan = to_channel(mattr); + struct rank_info *rank = csrow->channels[chan]; + /* if field has not been initialized, there is nothing to send */ - if (!csrow->channels[channel].dimm->label[0]) + if (!rank->dimm->label[0]) return 0; return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", - csrow->channels[channel].dimm->label); + rank->dimm->label); } -static ssize_t channel_dimm_label_store(struct csrow_info *csrow, - const char *data, - size_t count, int channel) +static ssize_t channel_dimm_label_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) { + struct csrow_info *csrow = to_csrow(dev); + unsigned chan = to_channel(mattr); + struct rank_info *rank = csrow->channels[chan]; + ssize_t max_size = 0; max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); - strncpy(csrow->channels[channel].dimm->label, data, max_size); - csrow->channels[channel].dimm->label[max_size] = '\0'; + strncpy(rank->dimm->label, data, max_size); + rank->dimm->label[max_size] = '\0'; return max_size; } /* show function for dynamic chX_ce_count attribute */ -static ssize_t channel_ce_count_show(struct csrow_info *csrow, - char *data, int channel) +static ssize_t channel_ce_count_show(struct device *dev, + struct device_attribute *mattr, char *data) { - return sprintf(data, "%u\n", csrow->channels[channel].ce_count); + struct csrow_info *csrow = to_csrow(dev); + unsigned chan = to_channel(mattr); + struct rank_info *rank = csrow->channels[chan]; + + return sprintf(data, "%u\n", rank->ce_count); } -/* csrow specific attribute structure */ -struct csrowdev_attribute { - struct attribute attr; - ssize_t(*show) (struct csrow_info *, char *, int); - ssize_t(*store) (struct csrow_info *, const char *, size_t, int); - int private; -}; +/* cwrow<id>/attribute files */ +DEVICE_ATTR_LEGACY(size_mb, S_IRUGO, csrow_size_show, NULL); +DEVICE_ATTR_LEGACY(dev_type, S_IRUGO, csrow_dev_type_show, NULL); +DEVICE_ATTR_LEGACY(mem_type, S_IRUGO, csrow_mem_type_show, NULL); +DEVICE_ATTR_LEGACY(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL); +DEVICE_ATTR_LEGACY(ue_count, S_IRUGO, csrow_ue_count_show, NULL); +DEVICE_ATTR_LEGACY(ce_count, S_IRUGO, csrow_ce_count_show, NULL); -#define to_csrow(k) container_of(k, struct csrow_info, kobj) -#define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr) +/* default attributes of the CSROW<id> object */ +static struct attribute *csrow_attrs[] = { + &dev_attr_legacy_dev_type.attr, + &dev_attr_legacy_mem_type.attr, + &dev_attr_legacy_edac_mode.attr, + &dev_attr_legacy_size_mb.attr, + &dev_attr_legacy_ue_count.attr, + &dev_attr_legacy_ce_count.attr, + NULL, +}; -/* Set of show/store higher level functions for default csrow attributes */ -static ssize_t csrowdev_show(struct kobject *kobj, - struct attribute *attr, char *buffer) -{ - struct csrow_info *csrow = to_csrow(kobj); - struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); +static struct attribute_group csrow_attr_grp = { + .attrs = csrow_attrs, +}; - if (csrowdev_attr->show) - return csrowdev_attr->show(csrow, - buffer, csrowdev_attr->private); - return -EIO; -} +static const struct attribute_group *csrow_attr_groups[] = { + &csrow_attr_grp, + NULL +}; -static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) +static void csrow_attr_release(struct device *dev) { - struct csrow_info *csrow = to_csrow(kobj); - struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); - - if (csrowdev_attr->store) - return csrowdev_attr->store(csrow, - buffer, - count, csrowdev_attr->private); - return -EIO; -} + struct csrow_info *csrow = container_of(dev, struct csrow_info, dev); -static const struct sysfs_ops csrowfs_ops = { - .show = csrowdev_show, - .store = csrowdev_store -}; + edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev)); + kfree(csrow); +} -#define CSROWDEV_ATTR(_name,_mode,_show,_store,_private) \ -static struct csrowdev_attribute attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode }, \ - .show = _show, \ - .store = _store, \ - .private = _private, \ +static struct device_type csrow_attr_type = { + .groups = csrow_attr_groups, + .release = csrow_attr_release, }; -/* default cwrow<id>/attribute files */ -CSROWDEV_ATTR(size_mb, S_IRUGO, csrow_size_show, NULL, 0); -CSROWDEV_ATTR(dev_type, S_IRUGO, csrow_dev_type_show, NULL, 0); -CSROWDEV_ATTR(mem_type, S_IRUGO, csrow_mem_type_show, NULL, 0); -CSROWDEV_ATTR(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL, 0); -CSROWDEV_ATTR(ue_count, S_IRUGO, csrow_ue_count_show, NULL, 0); -CSROWDEV_ATTR(ce_count, S_IRUGO, csrow_ce_count_show, NULL, 0); +/* + * possible dynamic channel DIMM Label attribute files + * + */ -/* default attributes of the CSROW<id> object */ -static struct csrowdev_attribute *default_csrow_attr[] = { - &attr_dev_type, - &attr_mem_type, - &attr_edac_mode, - &attr_size_mb, - &attr_ue_count, - &attr_ce_count, - NULL, -}; +#define EDAC_NR_CHANNELS 6 -/* possible dynamic channel DIMM Label attribute files */ -CSROWDEV_ATTR(ch0_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch0_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 0); -CSROWDEV_ATTR(ch1_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch1_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 1); -CSROWDEV_ATTR(ch2_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch2_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 2); -CSROWDEV_ATTR(ch3_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch3_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 3); -CSROWDEV_ATTR(ch4_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch4_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 4); -CSROWDEV_ATTR(ch5_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch5_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 5); /* Total possible dynamic DIMM Label attribute file table */ -static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = { - &attr_ch0_dimm_label, - &attr_ch1_dimm_label, - &attr_ch2_dimm_label, - &attr_ch3_dimm_label, - &attr_ch4_dimm_label, - &attr_ch5_dimm_label +static struct device_attribute *dynamic_csrow_dimm_attr[] = { + &dev_attr_legacy_ch0_dimm_label.attr, + &dev_attr_legacy_ch1_dimm_label.attr, + &dev_attr_legacy_ch2_dimm_label.attr, + &dev_attr_legacy_ch3_dimm_label.attr, + &dev_attr_legacy_ch4_dimm_label.attr, + &dev_attr_legacy_ch5_dimm_label.attr }; /* possible dynamic channel ce_count attribute files */ -CSROWDEV_ATTR(ch0_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 0); -CSROWDEV_ATTR(ch1_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 1); -CSROWDEV_ATTR(ch2_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 2); -CSROWDEV_ATTR(ch3_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 3); -CSROWDEV_ATTR(ch4_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 4); -CSROWDEV_ATTR(ch5_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 5); +DEVICE_CHANNEL(ch0_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 0); +DEVICE_CHANNEL(ch1_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 1); +DEVICE_CHANNEL(ch2_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 2); +DEVICE_CHANNEL(ch3_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 3); +DEVICE_CHANNEL(ch4_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 4); +DEVICE_CHANNEL(ch5_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 5); /* Total possible dynamic ce_count attribute file table */ -static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = { - &attr_ch0_ce_count, - &attr_ch1_ce_count, - &attr_ch2_ce_count, - &attr_ch3_ce_count, - &attr_ch4_ce_count, - &attr_ch5_ce_count +static struct device_attribute *dynamic_csrow_ce_count_attr[] = { + &dev_attr_legacy_ch0_ce_count.attr, + &dev_attr_legacy_ch1_ce_count.attr, + &dev_attr_legacy_ch2_ce_count.attr, + &dev_attr_legacy_ch3_ce_count.attr, + &dev_attr_legacy_ch4_ce_count.attr, + &dev_attr_legacy_ch5_ce_count.attr }; -#define EDAC_NR_CHANNELS 6 +static inline int nr_pages_per_csrow(struct csrow_info *csrow) +{ + int chan, nr_pages = 0; + + for (chan = 0; chan < csrow->nr_channels; chan++) + nr_pages += csrow->channels[chan]->dimm->nr_pages; + + return nr_pages; +} -/* Create dynamic CHANNEL files, indexed by 'chan', under specifed CSROW */ -static int edac_create_channel_files(struct kobject *kobj, int chan) +/* Create a CSROW object under specifed edac_mc_device */ +static int edac_create_csrow_object(struct mem_ctl_info *mci, + struct csrow_info *csrow, int index) { - int err = -ENODEV; + int err, chan; + + if (csrow->nr_channels >= EDAC_NR_CHANNELS) + return -ENODEV; + + csrow->dev.type = &csrow_attr_type; + csrow->dev.bus = &mci->bus; + device_initialize(&csrow->dev); + csrow->dev.parent = &mci->dev; + dev_set_name(&csrow->dev, "csrow%d", index); + dev_set_drvdata(&csrow->dev, csrow); - if (chan >= EDAC_NR_CHANNELS) + edac_dbg(0, "creating (virtual) csrow node %s\n", + dev_name(&csrow->dev)); + + err = device_add(&csrow->dev); + if (err < 0) return err; - /* create the DIMM label attribute file */ - err = sysfs_create_file(kobj, - (struct attribute *) - dynamic_csrow_dimm_attr[chan]); - - if (!err) { - /* create the CE Count attribute file */ - err = sysfs_create_file(kobj, - (struct attribute *) - dynamic_csrow_ce_count_attr[chan]); - } else { - debugf1("%s() dimm labels and ce_count files created", - __func__); + for (chan = 0; chan < csrow->nr_channels; chan++) { + /* Only expose populated DIMMs */ + if (!csrow->channels[chan]->dimm->nr_pages) + continue; + err = device_create_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + if (err < 0) + goto error; + err = device_create_file(&csrow->dev, + dynamic_csrow_ce_count_attr[chan]); + if (err < 0) { + device_remove_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + goto error; + } + } + + return 0; + +error: + for (--chan; chan >= 0; chan--) { + device_remove_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + device_remove_file(&csrow->dev, + dynamic_csrow_ce_count_attr[chan]); } + put_device(&csrow->dev); return err; } -/* No memory to release for this kobj */ -static void edac_csrow_instance_release(struct kobject *kobj) +/* Create a CSROW object under specifed edac_mc_device */ +static int edac_create_csrow_objects(struct mem_ctl_info *mci) { - struct mem_ctl_info *mci; - struct csrow_info *cs; + int err, i, chan; + struct csrow_info *csrow; + + for (i = 0; i < mci->nr_csrows; i++) { + csrow = mci->csrows[i]; + if (!nr_pages_per_csrow(csrow)) + continue; + err = edac_create_csrow_object(mci, mci->csrows[i], i); + if (err < 0) + goto error; + } + return 0; - debugf1("%s()\n", __func__); +error: + for (--i; i >= 0; i--) { + csrow = mci->csrows[i]; + if (!nr_pages_per_csrow(csrow)) + continue; + for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { + if (!csrow->channels[chan]->dimm->nr_pages) + continue; + device_remove_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + device_remove_file(&csrow->dev, + dynamic_csrow_ce_count_attr[chan]); + } + put_device(&mci->csrows[i]->dev); + } - cs = container_of(kobj, struct csrow_info, kobj); - mci = cs->mci; + return err; +} - kobject_put(&mci->edac_mci_kobj); +static void edac_delete_csrow_objects(struct mem_ctl_info *mci) +{ + int i, chan; + struct csrow_info *csrow; + + for (i = mci->nr_csrows - 1; i >= 0; i--) { + csrow = mci->csrows[i]; + if (!nr_pages_per_csrow(csrow)) + continue; + for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { + if (!csrow->channels[chan]->dimm->nr_pages) + continue; + edac_dbg(1, "Removing csrow %d channel %d sysfs nodes\n", + i, chan); + device_remove_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + device_remove_file(&csrow->dev, + dynamic_csrow_ce_count_attr[chan]); + } + put_device(&mci->csrows[i]->dev); + device_del(&mci->csrows[i]->dev); + } } +#endif -/* the kobj_type instance for a CSROW */ -static struct kobj_type ktype_csrow = { - .release = edac_csrow_instance_release, - .sysfs_ops = &csrowfs_ops, - .default_attrs = (struct attribute **)default_csrow_attr, +/* + * Per-dimm (or per-rank) devices + */ + +#define to_dimm(k) container_of(k, struct dimm_info, dev) + +/* show/store functions for DIMM Label attributes */ +static ssize_t dimmdev_location_show(struct device *dev, + struct device_attribute *mattr, char *data) +{ + struct dimm_info *dimm = to_dimm(dev); + + return edac_dimm_info_location(dimm, data, PAGE_SIZE); +} + +static ssize_t dimmdev_label_show(struct device *dev, + struct device_attribute *mattr, char *data) +{ + struct dimm_info *dimm = to_dimm(dev); + + /* if field has not been initialized, there is nothing to send */ + if (!dimm->label[0]) + return 0; + + return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", dimm->label); +} + +static ssize_t dimmdev_label_store(struct device *dev, + struct device_attribute *mattr, + const char *data, + size_t count) +{ + struct dimm_info *dimm = to_dimm(dev); + + ssize_t max_size = 0; + + max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); + strncpy(dimm->label, data, max_size); + dimm->label[max_size] = '\0'; + + return max_size; +} + +static ssize_t dimmdev_size_show(struct device *dev, + struct device_attribute *mattr, char *data) +{ + struct dimm_info *dimm = to_dimm(dev); + + return sprintf(data, "%u\n", PAGES_TO_MiB(dimm->nr_pages)); +} + +static ssize_t dimmdev_mem_type_show(struct device *dev, + struct device_attribute *mattr, char *data) +{ + struct dimm_info *dimm = to_dimm(dev); + + return sprintf(data, "%s\n", mem_types[dimm->mtype]); +} + +static ssize_t dimmdev_dev_type_show(struct device *dev, + struct device_attribute *mattr, char *data) +{ + struct dimm_info *dimm = to_dimm(dev); + + return sprintf(data, "%s\n", dev_types[dimm->dtype]); +} + +static ssize_t dimmdev_edac_mode_show(struct device *dev, + struct device_attribute *mattr, + char *data) +{ + struct dimm_info *dimm = to_dimm(dev); + + return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]); +} + +/* dimm/rank attribute files */ +static DEVICE_ATTR(dimm_label, S_IRUGO | S_IWUSR, + dimmdev_label_show, dimmdev_label_store); +static DEVICE_ATTR(dimm_location, S_IRUGO, dimmdev_location_show, NULL); +static DEVICE_ATTR(size, S_IRUGO, dimmdev_size_show, NULL); +static DEVICE_ATTR(dimm_mem_type, S_IRUGO, dimmdev_mem_type_show, NULL); +static DEVICE_ATTR(dimm_dev_type, S_IRUGO, dimmdev_dev_type_show, NULL); +static DEVICE_ATTR(dimm_edac_mode, S_IRUGO, dimmdev_edac_mode_show, NULL); + +/* attributes of the dimm<id>/rank<id> object */ +static struct attribute *dimm_attrs[] = { + &dev_attr_dimm_label.attr, + &dev_attr_dimm_location.attr, + &dev_attr_size.attr, + &dev_attr_dimm_mem_type.attr, + &dev_attr_dimm_dev_type.attr, + &dev_attr_dimm_edac_mode.attr, + NULL, }; -/* Create a CSROW object under specifed edac_mc_device */ -static int edac_create_csrow_object(struct mem_ctl_info *mci, - struct csrow_info *csrow, int index) +static struct attribute_group dimm_attr_grp = { + .attrs = dimm_attrs, +}; + +static const struct attribute_group *dimm_attr_groups[] = { + &dimm_attr_grp, + NULL +}; + +static void dimm_attr_release(struct device *dev) { - struct kobject *kobj_mci = &mci->edac_mci_kobj; - struct kobject *kobj; - int chan; - int err; + struct dimm_info *dimm = container_of(dev, struct dimm_info, dev); - /* generate ..../edac/mc/mc<id>/csrow<index> */ - memset(&csrow->kobj, 0, sizeof(csrow->kobj)); - csrow->mci = mci; /* include container up link */ + edac_dbg(1, "Releasing dimm device %s\n", dev_name(dev)); + kfree(dimm); +} - /* bump the mci instance's kobject's ref count */ - kobj = kobject_get(&mci->edac_mci_kobj); - if (!kobj) { - err = -ENODEV; - goto err_out; - } +static struct device_type dimm_attr_type = { + .groups = dimm_attr_groups, + .release = dimm_attr_release, +}; + +/* Create a DIMM object under specifed memory controller device */ +static int edac_create_dimm_object(struct mem_ctl_info *mci, + struct dimm_info *dimm, + int index) +{ + int err; + dimm->mci = mci; - /* Instanstiate the csrow object */ - err = kobject_init_and_add(&csrow->kobj, &ktype_csrow, kobj_mci, - "csrow%d", index); - if (err) - goto err_release_top_kobj; + dimm->dev.type = &dimm_attr_type; + dimm->dev.bus = &mci->bus; + device_initialize(&dimm->dev); - /* At this point, to release a csrow kobj, one must - * call the kobject_put and allow that tear down - * to work the releasing - */ + dimm->dev.parent = &mci->dev; + if (mci->mem_is_per_rank) + dev_set_name(&dimm->dev, "rank%d", index); + else + dev_set_name(&dimm->dev, "dimm%d", index); + dev_set_drvdata(&dimm->dev, dimm); + pm_runtime_forbid(&mci->dev); - /* Create the dyanmic attribute files on this csrow, - * namely, the DIMM labels and the channel ce_count - */ - for (chan = 0; chan < csrow->nr_channels; chan++) { - err = edac_create_channel_files(&csrow->kobj, chan); - if (err) { - /* special case the unregister here */ - kobject_put(&csrow->kobj); - goto err_out; - } - } - kobject_uevent(&csrow->kobj, KOBJ_ADD); - return 0; + err = device_add(&dimm->dev); - /* error unwind stack */ -err_release_top_kobj: - kobject_put(&mci->edac_mci_kobj); + edac_dbg(0, "creating rank/dimm device %s\n", dev_name(&dimm->dev)); -err_out: return err; } -/* default sysfs methods and data structures for the main MCI kobject */ +/* + * Memory controller device + */ + +#define to_mci(k) container_of(k, struct mem_ctl_info, dev) -static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, +static ssize_t mci_reset_counters_store(struct device *dev, + struct device_attribute *mattr, const char *data, size_t count) { - int row, chan; - - mci->ue_noinfo_count = 0; - mci->ce_noinfo_count = 0; + struct mem_ctl_info *mci = to_mci(dev); + int cnt, row, chan, i; mci->ue_mc = 0; mci->ce_mc = 0; + mci->ue_noinfo_count = 0; + mci->ce_noinfo_count = 0; for (row = 0; row < mci->nr_csrows; row++) { - struct csrow_info *ri = &mci->csrows[row]; + struct csrow_info *ri = mci->csrows[row]; ri->ue_count = 0; ri->ce_count = 0; for (chan = 0; chan < ri->nr_channels; chan++) - ri->channels[chan].ce_count = 0; + ri->channels[chan]->ce_count = 0; + } + + cnt = 1; + for (i = 0; i < mci->n_layers; i++) { + cnt *= mci->layers[i].size; + memset(mci->ce_per_layer[i], 0, cnt * sizeof(u32)); + memset(mci->ue_per_layer[i], 0, cnt * sizeof(u32)); } mci->start_time = jiffies; @@ -451,9 +666,11 @@ static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, * Negative value still means that an error has occurred while setting * the scrub rate. */ -static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, +static ssize_t mci_sdram_scrub_rate_store(struct device *dev, + struct device_attribute *mattr, const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); unsigned long bandwidth = 0; int new_bw = 0; @@ -476,8 +693,11 @@ static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, /* * ->get_sdram_scrub_rate() return value semantics same as above. */ -static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_sdram_scrub_rate_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); int bandwidth = 0; if (!mci->get_sdram_scrub_rate) @@ -493,45 +713,72 @@ static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) } /* default attribute files for the MCI object */ -static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ue_count_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%d\n", mci->ue_mc); } -static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ce_count_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%d\n", mci->ce_mc); } -static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ce_noinfo_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%d\n", mci->ce_noinfo_count); } -static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ue_noinfo_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%d\n", mci->ue_noinfo_count); } -static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_seconds_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ); } -static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ctl_name_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%s\n", mci->ctl_name); } -static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_size_mb_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); int total_pages = 0, csrow_idx, j; for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) { - struct csrow_info *csrow = &mci->csrows[csrow_idx]; + struct csrow_info *csrow = mci->csrows[csrow_idx]; for (j = 0; j < csrow->nr_channels; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; total_pages += dimm->nr_pages; } @@ -540,361 +787,187 @@ static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages)); } -#define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj) -#define to_mcidev_attr(a) container_of(a,struct mcidev_sysfs_attribute,attr) - -/* MCI show/store functions for top most object */ -static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, - char *buffer) +static ssize_t mci_max_location_show(struct device *dev, + struct device_attribute *mattr, + char *data) { - struct mem_ctl_info *mem_ctl_info = to_mci(kobj); - struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); - - debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); + struct mem_ctl_info *mci = to_mci(dev); + int i; + char *p = data; - if (mcidev_attr->show) - return mcidev_attr->show(mem_ctl_info, buffer); + for (i = 0; i < mci->n_layers; i++) { + p += sprintf(p, "%s %d ", + edac_layer_name[mci->layers[i].type], + mci->layers[i].size - 1); + } - return -EIO; + return p - data; } -static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) +#ifdef CONFIG_EDAC_DEBUG +static ssize_t edac_fake_inject_write(struct file *file, + const char __user *data, + size_t count, loff_t *ppos) { - struct mem_ctl_info *mem_ctl_info = to_mci(kobj); - struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); - - debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); - - if (mcidev_attr->store) - return mcidev_attr->store(mem_ctl_info, buffer, count); + struct device *dev = file->private_data; + struct mem_ctl_info *mci = to_mci(dev); + static enum hw_event_mc_err_type type; + u16 errcount = mci->fake_inject_count; + + if (!errcount) + errcount = 1; + + type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED + : HW_EVENT_ERR_CORRECTED; + + printk(KERN_DEBUG + "Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n", + errcount, + (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE", + errcount > 1 ? "s" : "", + mci->fake_inject_layer[0], + mci->fake_inject_layer[1], + mci->fake_inject_layer[2] + ); + edac_mc_handle_error(type, mci, errcount, 0, 0, 0, + mci->fake_inject_layer[0], + mci->fake_inject_layer[1], + mci->fake_inject_layer[2], + "FAKE ERROR", "for EDAC testing only"); - return -EIO; + return count; } -/* Intermediate show/store table */ -static const struct sysfs_ops mci_ops = { - .show = mcidev_show, - .store = mcidev_store -}; +static int debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} -#define MCIDEV_ATTR(_name,_mode,_show,_store) \ -static struct mcidev_sysfs_attribute mci_attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode }, \ - .show = _show, \ - .store = _store, \ +static const struct file_operations debug_fake_inject_fops = { + .open = debugfs_open, + .write = edac_fake_inject_write, + .llseek = generic_file_llseek, }; +#endif /* default Control file */ -MCIDEV_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); +DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); /* default Attribute files */ -MCIDEV_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); -MCIDEV_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); -MCIDEV_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); -MCIDEV_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); -MCIDEV_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); -MCIDEV_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); -MCIDEV_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); +DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); +DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); +DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); +DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); +DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); +DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); +DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); +DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL); /* memory scrubber attribute file */ -MCIDEV_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show, +DEVICE_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show, mci_sdram_scrub_rate_store); -static struct mcidev_sysfs_attribute *mci_attr[] = { - &mci_attr_reset_counters, - &mci_attr_mc_name, - &mci_attr_size_mb, - &mci_attr_seconds_since_reset, - &mci_attr_ue_noinfo_count, - &mci_attr_ce_noinfo_count, - &mci_attr_ue_count, - &mci_attr_ce_count, - &mci_attr_sdram_scrub_rate, +static struct attribute *mci_attrs[] = { + &dev_attr_reset_counters.attr, + &dev_attr_mc_name.attr, + &dev_attr_size_mb.attr, + &dev_attr_seconds_since_reset.attr, + &dev_attr_ue_noinfo_count.attr, + &dev_attr_ce_noinfo_count.attr, + &dev_attr_ue_count.attr, + &dev_attr_ce_count.attr, + &dev_attr_sdram_scrub_rate.attr, + &dev_attr_max_location.attr, NULL }; +static struct attribute_group mci_attr_grp = { + .attrs = mci_attrs, +}; -/* - * Release of a MC controlling instance - * - * each MC control instance has the following resources upon entry: - * a) a ref count on the top memctl kobj - * b) a ref count on this module - * - * this function must decrement those ref counts and then - * issue a free on the instance's memory - */ -static void edac_mci_control_release(struct kobject *kobj) -{ - struct mem_ctl_info *mci; - - mci = to_mci(kobj); +static const struct attribute_group *mci_attr_groups[] = { + &mci_attr_grp, + NULL +}; - debugf0("%s() mci instance idx=%d releasing\n", __func__, mci->mc_idx); +static void mci_attr_release(struct device *dev) +{ + struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); - /* decrement the module ref count */ - module_put(mci->owner); + edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev)); + kfree(mci); } -static struct kobj_type ktype_mci = { - .release = edac_mci_control_release, - .sysfs_ops = &mci_ops, - .default_attrs = (struct attribute **)mci_attr, +static struct device_type mci_attr_type = { + .groups = mci_attr_groups, + .release = mci_attr_release, }; -/* EDAC memory controller sysfs kset: - * /sys/devices/system/edac/mc - */ -static struct kset *mc_kset; +#ifdef CONFIG_EDAC_DEBUG +static struct dentry *edac_debugfs; -/* - * edac_mc_register_sysfs_main_kobj - * - * setups and registers the main kobject for each mci - */ -int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci) +int __init edac_debugfs_init(void) { - struct kobject *kobj_mci; - int err; - - debugf1("%s()\n", __func__); - - kobj_mci = &mci->edac_mci_kobj; - - /* Init the mci's kobject */ - memset(kobj_mci, 0, sizeof(*kobj_mci)); - - /* Record which module 'owns' this control structure - * and bump the ref count of the module - */ - mci->owner = THIS_MODULE; - - /* bump ref count on this module */ - if (!try_module_get(mci->owner)) { - err = -ENODEV; - goto fail_out; - } - - /* this instance become part of the mc_kset */ - kobj_mci->kset = mc_kset; - - /* register the mc<id> kobject to the mc_kset */ - err = kobject_init_and_add(kobj_mci, &ktype_mci, NULL, - "mc%d", mci->mc_idx); - if (err) { - debugf1("%s()Failed to register '.../edac/mc%d'\n", - __func__, mci->mc_idx); - goto kobj_reg_fail; + edac_debugfs = debugfs_create_dir("edac", NULL); + if (IS_ERR(edac_debugfs)) { + edac_debugfs = NULL; + return -ENOMEM; } - kobject_uevent(kobj_mci, KOBJ_ADD); - - /* At this point, to 'free' the control struct, - * edac_mc_unregister_sysfs_main_kobj() must be used - */ - - debugf1("%s() Registered '.../edac/mc%d' kobject\n", - __func__, mci->mc_idx); - return 0; - - /* Error exit stack */ - -kobj_reg_fail: - module_put(mci->owner); - -fail_out: - return err; -} - -/* - * edac_mc_register_sysfs_main_kobj - * - * tears down and the main mci kobject from the mc_kset - */ -void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) -{ - debugf1("%s()\n", __func__); - - /* delete the kobj from the mc_kset */ - kobject_put(&mci->edac_mci_kobj); -} - -#define EDAC_DEVICE_SYMLINK "device" - -#define grp_to_mci(k) (container_of(k, struct mcidev_sysfs_group_kobj, kobj)->mci) - -/* MCI show/store functions for top most object */ -static ssize_t inst_grp_show(struct kobject *kobj, struct attribute *attr, - char *buffer) -{ - struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj); - struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); - - debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); - - if (mcidev_attr->show) - return mcidev_attr->show(mem_ctl_info, buffer); - - return -EIO; } -static ssize_t inst_grp_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) +void __exit edac_debugfs_exit(void) { - struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj); - struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); - - debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); - - if (mcidev_attr->store) - return mcidev_attr->store(mem_ctl_info, buffer, count); - - return -EIO; + debugfs_remove(edac_debugfs); } -/* No memory to release for this kobj */ -static void edac_inst_grp_release(struct kobject *kobj) +int edac_create_debug_nodes(struct mem_ctl_info *mci) { - struct mcidev_sysfs_group_kobj *grp; - struct mem_ctl_info *mci; - - debugf1("%s()\n", __func__); - - grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj); - mci = grp->mci; -} - -/* Intermediate show/store table */ -static struct sysfs_ops inst_grp_ops = { - .show = inst_grp_show, - .store = inst_grp_store -}; - -/* the kobj_type instance for a instance group */ -static struct kobj_type ktype_inst_grp = { - .release = edac_inst_grp_release, - .sysfs_ops = &inst_grp_ops, -}; - + struct dentry *d, *parent; + char name[80]; + int i; -/* - * edac_create_mci_instance_attributes - * create MC driver specific attributes bellow an specified kobj - * This routine calls itself recursively, in order to create an entire - * object tree. - */ -static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, - const struct mcidev_sysfs_attribute *sysfs_attrib, - struct kobject *kobj) -{ - int err; + if (!edac_debugfs) + return -ENODEV; - debugf4("%s()\n", __func__); - - while (sysfs_attrib) { - debugf4("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib); - if (sysfs_attrib->grp) { - struct mcidev_sysfs_group_kobj *grp_kobj; - - grp_kobj = kzalloc(sizeof(*grp_kobj), GFP_KERNEL); - if (!grp_kobj) - return -ENOMEM; - - grp_kobj->grp = sysfs_attrib->grp; - grp_kobj->mci = mci; - list_add_tail(&grp_kobj->list, &mci->grp_kobj_list); - - debugf0("%s() grp %s, mci %p\n", __func__, - sysfs_attrib->grp->name, mci); - - err = kobject_init_and_add(&grp_kobj->kobj, - &ktype_inst_grp, - &mci->edac_mci_kobj, - sysfs_attrib->grp->name); - if (err < 0) { - printk(KERN_ERR "kobject_init_and_add failed: %d\n", err); - return err; - } - err = edac_create_mci_instance_attributes(mci, - grp_kobj->grp->mcidev_attr, - &grp_kobj->kobj); - - if (err < 0) - return err; - } else if (sysfs_attrib->attr.name) { - debugf4("%s() file %s\n", __func__, - sysfs_attrib->attr.name); - - err = sysfs_create_file(kobj, &sysfs_attrib->attr); - if (err < 0) { - printk(KERN_ERR "sysfs_create_file failed: %d\n", err); - return err; - } - } else - break; - - sysfs_attrib++; + d = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs); + if (!d) + return -ENOMEM; + parent = d; + + for (i = 0; i < mci->n_layers; i++) { + sprintf(name, "fake_inject_%s", + edac_layer_name[mci->layers[i].type]); + d = debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent, + &mci->fake_inject_layer[i]); + if (!d) + goto nomem; } - return 0; -} + d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent, + &mci->fake_inject_ue); + if (!d) + goto nomem; -/* - * edac_remove_mci_instance_attributes - * remove MC driver specific attributes at the topmost level - * directory of this mci instance. - */ -static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, - const struct mcidev_sysfs_attribute *sysfs_attrib, - struct kobject *kobj, int count) -{ - struct mcidev_sysfs_group_kobj *grp_kobj, *tmp; + d = debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent, + &mci->fake_inject_count); + if (!d) + goto nomem; - debugf1("%s()\n", __func__); - - /* - * loop if there are attributes and until we hit a NULL entry - * Remove first all the attributes - */ - while (sysfs_attrib) { - debugf4("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib); - if (sysfs_attrib->grp) { - debugf4("%s() seeking for group %s\n", - __func__, sysfs_attrib->grp->name); - list_for_each_entry(grp_kobj, - &mci->grp_kobj_list, list) { - debugf4("%s() grp_kobj->grp = %p\n",__func__, grp_kobj->grp); - if (grp_kobj->grp == sysfs_attrib->grp) { - edac_remove_mci_instance_attributes(mci, - grp_kobj->grp->mcidev_attr, - &grp_kobj->kobj, count + 1); - debugf4("%s() group %s\n", __func__, - sysfs_attrib->grp->name); - kobject_put(&grp_kobj->kobj); - } - } - debugf4("%s() end of seeking for group %s\n", - __func__, sysfs_attrib->grp->name); - } else if (sysfs_attrib->attr.name) { - debugf4("%s() file %s\n", __func__, - sysfs_attrib->attr.name); - sysfs_remove_file(kobj, &sysfs_attrib->attr); - } else - break; - sysfs_attrib++; - } + d = debugfs_create_file("fake_inject", S_IWUSR, parent, + &mci->dev, + &debug_fake_inject_fops); + if (!d) + goto nomem; - /* Remove the group objects */ - if (count) - return; - list_for_each_entry_safe(grp_kobj, tmp, - &mci->grp_kobj_list, list) { - list_del(&grp_kobj->list); - kfree(grp_kobj); - } + mci->debugfs = parent; + return 0; +nomem: + debugfs_remove(mci->debugfs); + return -ENOMEM; } - +#endif /* * Create a new Memory Controller kobject instance, @@ -906,77 +979,87 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, */ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) { - int i, j; - int err; - struct csrow_info *csrow; - struct kobject *kobj_mci = &mci->edac_mci_kobj; + int i, err; - debugf0("%s() idx=%d\n", __func__, mci->mc_idx); - - INIT_LIST_HEAD(&mci->grp_kobj_list); + /* + * The memory controller needs its own bus, in order to avoid + * namespace conflicts at /sys/bus/edac. + */ + mci->bus.name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx); + if (!mci->bus.name) + return -ENOMEM; + edac_dbg(0, "creating bus %s\n", mci->bus.name); + err = bus_register(&mci->bus); + if (err < 0) + return err; - /* create a symlink for the device */ - err = sysfs_create_link(kobj_mci, &mci->dev->kobj, - EDAC_DEVICE_SYMLINK); - if (err) { - debugf1("%s() failure to create symlink\n", __func__); - goto fail0; + /* get the /sys/devices/system/edac subsys reference */ + mci->dev.type = &mci_attr_type; + device_initialize(&mci->dev); + + mci->dev.parent = mci_pdev; + mci->dev.bus = &mci->bus; + dev_set_name(&mci->dev, "mc%d", mci->mc_idx); + dev_set_drvdata(&mci->dev, mci); + pm_runtime_forbid(&mci->dev); + + edac_dbg(0, "creating device %s\n", dev_name(&mci->dev)); + err = device_add(&mci->dev); + if (err < 0) { + bus_unregister(&mci->bus); + kfree(mci->bus.name); + return err; } - /* If the low level driver desires some attributes, - * then create them now for the driver. + /* + * Create the dimm/rank devices */ - if (mci->mc_driver_sysfs_attributes) { - err = edac_create_mci_instance_attributes(mci, - mci->mc_driver_sysfs_attributes, - &mci->edac_mci_kobj); + for (i = 0; i < mci->tot_dimms; i++) { + struct dimm_info *dimm = mci->dimms[i]; + /* Only expose populated DIMMs */ + if (dimm->nr_pages == 0) + continue; +#ifdef CONFIG_EDAC_DEBUG + edac_dbg(1, "creating dimm%d, located at ", i); + if (edac_debug_level >= 1) { + int lay; + for (lay = 0; lay < mci->n_layers; lay++) + printk(KERN_CONT "%s %d ", + edac_layer_name[mci->layers[lay].type], + dimm->location[lay]); + printk(KERN_CONT "\n"); + } +#endif + err = edac_create_dimm_object(mci, dimm, i); if (err) { - debugf1("%s() failure to create mci attributes\n", - __func__); - goto fail0; + edac_dbg(1, "failure: create dimm %d obj\n", i); + goto fail; } } - /* Make directories for each CSROW object under the mc<id> kobject - */ - for (i = 0; i < mci->nr_csrows; i++) { - int nr_pages = 0; - - csrow = &mci->csrows[i]; - for (j = 0; j < csrow->nr_channels; j++) - nr_pages += csrow->channels[j].dimm->nr_pages; - - if (nr_pages > 0) { - err = edac_create_csrow_object(mci, csrow, i); - if (err) { - debugf1("%s() failure: create csrow %d obj\n", - __func__, i); - goto fail1; - } - } - } +#ifdef CONFIG_EDAC_LEGACY_SYSFS + err = edac_create_csrow_objects(mci); + if (err < 0) + goto fail; +#endif +#ifdef CONFIG_EDAC_DEBUG + edac_create_debug_nodes(mci); +#endif return 0; -fail1: +fail: for (i--; i >= 0; i--) { - int nr_pages = 0; - - csrow = &mci->csrows[i]; - for (j = 0; j < csrow->nr_channels; j++) - nr_pages += csrow->channels[j].dimm->nr_pages; - if (nr_pages > 0) - kobject_put(&mci->csrows[i].kobj); + struct dimm_info *dimm = mci->dimms[i]; + if (dimm->nr_pages == 0) + continue; + put_device(&dimm->dev); + device_del(&dimm->dev); } - - /* remove the mci instance's attributes, if any */ - edac_remove_mci_instance_attributes(mci, - mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj, 0); - - /* remove the symlink */ - sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK); - -fail0: + put_device(&mci->dev); + device_del(&mci->dev); + bus_unregister(&mci->bus); + kfree(mci->bus.name); return err; } @@ -985,98 +1068,84 @@ fail0: */ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) { - struct csrow_info *csrow; - int i, j; - - debugf0("%s()\n", __func__); - - /* remove all csrow kobjects */ - debugf4("%s() unregister this mci kobj\n", __func__); - for (i = 0; i < mci->nr_csrows; i++) { - int nr_pages = 0; - - csrow = &mci->csrows[i]; - for (j = 0; j < csrow->nr_channels; j++) - nr_pages += csrow->channels[j].dimm->nr_pages; - if (nr_pages > 0) { - debugf0("%s() unreg csrow-%d\n", __func__, i); - kobject_put(&mci->csrows[i].kobj); - } - } + int i; - /* remove this mci instance's attribtes */ - if (mci->mc_driver_sysfs_attributes) { - debugf4("%s() unregister mci private attributes\n", __func__); - edac_remove_mci_instance_attributes(mci, - mci->mc_driver_sysfs_attributes, - &mci->edac_mci_kobj, 0); + edac_dbg(0, "\n"); + +#ifdef CONFIG_EDAC_DEBUG + debugfs_remove(mci->debugfs); +#endif +#ifdef CONFIG_EDAC_LEGACY_SYSFS + edac_delete_csrow_objects(mci); +#endif + + for (i = 0; i < mci->tot_dimms; i++) { + struct dimm_info *dimm = mci->dimms[i]; + if (dimm->nr_pages == 0) + continue; + edac_dbg(0, "removing device %s\n", dev_name(&dimm->dev)); + put_device(&dimm->dev); + device_del(&dimm->dev); } - - /* remove the symlink */ - debugf4("%s() remove_link\n", __func__); - sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); - - /* unregister this instance's kobject */ - debugf4("%s() remove_mci_instance\n", __func__); - kobject_put(&mci->edac_mci_kobj); } +void edac_unregister_sysfs(struct mem_ctl_info *mci) +{ + edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev)); + put_device(&mci->dev); + device_del(&mci->dev); + bus_unregister(&mci->bus); + kfree(mci->bus.name); +} +static void mc_attr_release(struct device *dev) +{ + /* + * There's no container structure here, as this is just the mci + * parent device, used to create the /sys/devices/mc sysfs node. + * So, there are no attributes on it. + */ + edac_dbg(1, "Releasing device %s\n", dev_name(dev)); + kfree(dev); +} - +static struct device_type mc_attr_type = { + .release = mc_attr_release, +}; /* - * edac_setup_sysfs_mc_kset(void) - * - * Initialize the mc_kset for the 'mc' entry - * This requires creating the top 'mc' directory with a kset - * and its controls/attributes. - * - * To this 'mc' kset, instance 'mci' will be grouped as children. - * - * Return: 0 SUCCESS - * !0 FAILURE error code + * Init/exit code for the module. Basically, creates/removes /sys/class/rc */ -int edac_sysfs_setup_mc_kset(void) +int __init edac_mc_sysfs_init(void) { - int err = -EINVAL; struct bus_type *edac_subsys; - - debugf1("%s()\n", __func__); + int err; /* get the /sys/devices/system/edac subsys reference */ edac_subsys = edac_get_sysfs_subsys(); if (edac_subsys == NULL) { - debugf1("%s() no edac_subsys error=%d\n", __func__, err); - goto fail_out; + edac_dbg(1, "no edac_subsys\n"); + return -EINVAL; } - /* Init the MC's kobject */ - mc_kset = kset_create_and_add("mc", NULL, &edac_subsys->dev_root->kobj); - if (!mc_kset) { - err = -ENOMEM; - debugf1("%s() Failed to register '.../edac/mc'\n", __func__); - goto fail_kset; - } + mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL); - debugf1("%s() Registered '.../edac/mc' kobject\n", __func__); + mci_pdev->bus = edac_subsys; + mci_pdev->type = &mc_attr_type; + device_initialize(mci_pdev); + dev_set_name(mci_pdev, "mc"); - return 0; + err = device_add(mci_pdev); + if (err < 0) + return err; -fail_kset: - edac_put_sysfs_subsys(); + edac_dbg(0, "device %s created\n", dev_name(mci_pdev)); -fail_out: - return err; + return 0; } -/* - * edac_sysfs_teardown_mc_kset - * - * deconstruct the mc_ket for memory controllers - */ -void edac_sysfs_teardown_mc_kset(void) +void __exit edac_mc_sysfs_exit(void) { - kset_unregister(mc_kset); + put_device(mci_pdev); + device_del(mci_pdev); edac_put_sysfs_subsys(); } - diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c index 5ddaa86d6a6e..58a28d838f37 100644 --- a/drivers/edac/edac_module.c +++ b/drivers/edac/edac_module.c @@ -15,7 +15,7 @@ #include "edac_core.h" #include "edac_module.h" -#define EDAC_VERSION "Ver: 2.1.0" +#define EDAC_VERSION "Ver: 3.0.0" #ifdef CONFIG_EDAC_DEBUG /* Values of 0 to 4 will generate output */ @@ -90,26 +90,21 @@ static int __init edac_init(void) */ edac_pci_clear_parity_errors(); - /* - * now set up the mc_kset under the edac class object - */ - err = edac_sysfs_setup_mc_kset(); + err = edac_mc_sysfs_init(); if (err) goto error; + edac_debugfs_init(); + /* Setup/Initialize the workq for this core */ err = edac_workqueue_setup(); if (err) { edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n"); - goto workq_fail; + goto error; } return 0; - /* Error teardown stack */ -workq_fail: - edac_sysfs_teardown_mc_kset(); - error: return err; } @@ -120,11 +115,12 @@ error: */ static void __exit edac_exit(void) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* tear down the various subsystems */ edac_workqueue_teardown(); - edac_sysfs_teardown_mc_kset(); + edac_mc_sysfs_exit(); + edac_debugfs_exit(); } /* diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h index 0ea7d14cb930..3d139c6e7fe3 100644 --- a/drivers/edac/edac_module.h +++ b/drivers/edac/edac_module.h @@ -19,12 +19,12 @@ * * edac_mc objects */ -extern int edac_sysfs_setup_mc_kset(void); -extern void edac_sysfs_teardown_mc_kset(void); -extern int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci); -extern void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci); + /* on edac_mc_sysfs.c */ +int edac_mc_sysfs_init(void); +void edac_mc_sysfs_exit(void); extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci); extern void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci); +void edac_unregister_sysfs(struct mem_ctl_info *mci); extern int edac_get_log_ue(void); extern int edac_get_log_ce(void); extern int edac_get_panic_on_ue(void); @@ -34,6 +34,10 @@ extern int edac_mc_get_panic_on_ue(void); extern int edac_get_poll_msec(void); extern int edac_mc_get_poll_msec(void); +unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf, + unsigned len); + + /* on edac_device.c */ extern int edac_device_register_sysfs_main_kobj( struct edac_device_ctl_info *edac_dev); extern void edac_device_unregister_sysfs_main_kobj( @@ -53,6 +57,20 @@ extern void edac_mc_reset_delay_period(int value); extern void *edac_align_ptr(void **p, unsigned size, int n_elems); /* + * EDAC debugfs functions + */ +#ifdef CONFIG_EDAC_DEBUG +int edac_debugfs_init(void); +void edac_debugfs_exit(void); +#else +static inline int edac_debugfs_init(void) +{ + return -ENODEV; +} +static inline void edac_debugfs_exit(void) {} +#endif + +/* * EDAC PCI functions */ #ifdef CONFIG_PCI diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c index f1ac86649886..ee87ef972ead 100644 --- a/drivers/edac/edac_pci.c +++ b/drivers/edac/edac_pci.c @@ -45,7 +45,7 @@ struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt, void *p = NULL, *pvt; unsigned int size; - debugf1("%s()\n", __func__); + edac_dbg(1, "\n"); pci = edac_align_ptr(&p, sizeof(*pci), 1); pvt = edac_align_ptr(&p, 1, sz_pvt); @@ -80,7 +80,7 @@ EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info); */ void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci) { - debugf1("%s()\n", __func__); + edac_dbg(1, "\n"); edac_pci_remove_sysfs(pci); } @@ -97,7 +97,7 @@ static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev) struct edac_pci_ctl_info *pci; struct list_head *item; - debugf1("%s()\n", __func__); + edac_dbg(1, "\n"); list_for_each(item, &edac_pci_list) { pci = list_entry(item, struct edac_pci_ctl_info, link); @@ -122,7 +122,7 @@ static int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci) struct list_head *item, *insert_before; struct edac_pci_ctl_info *rover; - debugf1("%s()\n", __func__); + edac_dbg(1, "\n"); insert_before = &edac_pci_list; @@ -226,7 +226,7 @@ static void edac_pci_workq_function(struct work_struct *work_req) int msec; unsigned long delay; - debugf3("%s() checking\n", __func__); + edac_dbg(3, "checking\n"); mutex_lock(&edac_pci_ctls_mutex); @@ -261,7 +261,7 @@ static void edac_pci_workq_function(struct work_struct *work_req) static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci, unsigned int msec) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function); queue_delayed_work(edac_workqueue, &pci->work, @@ -276,7 +276,7 @@ static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci) { int status; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); status = cancel_delayed_work(&pci->work); if (status == 0) @@ -293,7 +293,7 @@ static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci) void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci, unsigned long value) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); edac_pci_workq_teardown(pci); @@ -333,7 +333,7 @@ EXPORT_SYMBOL_GPL(edac_pci_alloc_index); */ int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); pci->pci_idx = edac_idx; pci->start_time = jiffies; @@ -393,7 +393,7 @@ struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev) { struct edac_pci_ctl_info *pci; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); mutex_lock(&edac_pci_ctls_mutex); @@ -430,7 +430,7 @@ EXPORT_SYMBOL_GPL(edac_pci_del_device); */ static void edac_pci_generic_check(struct edac_pci_ctl_info *pci) { - debugf4("%s()\n", __func__); + edac_dbg(4, "\n"); edac_pci_do_parity_check(); } @@ -475,7 +475,7 @@ struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev, pdata->edac_idx = edac_pci_idx++; if (edac_pci_add_device(pci, pdata->edac_idx) > 0) { - debugf3("%s(): failed edac_pci_add_device()\n", __func__); + edac_dbg(3, "failed edac_pci_add_device()\n"); edac_pci_free_ctl_info(pci); return NULL; } @@ -491,7 +491,7 @@ EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl); */ void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci) { - debugf0("%s() pci mod=%s\n", __func__, pci->mod_name); + edac_dbg(0, "pci mod=%s\n", pci->mod_name); edac_pci_del_device(pci->dev); edac_pci_free_ctl_info(pci); diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c index 97f5064e3992..e164c555a337 100644 --- a/drivers/edac/edac_pci_sysfs.c +++ b/drivers/edac/edac_pci_sysfs.c @@ -78,7 +78,7 @@ static void edac_pci_instance_release(struct kobject *kobj) { struct edac_pci_ctl_info *pci; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* Form pointer to containing struct, the pci control struct */ pci = to_instance(kobj); @@ -161,7 +161,7 @@ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx) struct kobject *main_kobj; int err; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* First bump the ref count on the top main kobj, which will * track the number of PCI instances we have, and thus nest @@ -177,14 +177,13 @@ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx) err = kobject_init_and_add(&pci->kobj, &ktype_pci_instance, edac_pci_top_main_kobj, "pci%d", idx); if (err != 0) { - debugf2("%s() failed to register instance pci%d\n", - __func__, idx); + edac_dbg(2, "failed to register instance pci%d\n", idx); kobject_put(edac_pci_top_main_kobj); goto error_out; } kobject_uevent(&pci->kobj, KOBJ_ADD); - debugf1("%s() Register instance 'pci%d' kobject\n", __func__, idx); + edac_dbg(1, "Register instance 'pci%d' kobject\n", idx); return 0; @@ -201,7 +200,7 @@ error_out: static void edac_pci_unregister_sysfs_instance_kobj( struct edac_pci_ctl_info *pci) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* Unregister the instance kobject and allow its release * function release the main reference count and then @@ -317,7 +316,7 @@ static struct edac_pci_dev_attribute *edac_pci_attr[] = { */ static void edac_pci_release_main_kobj(struct kobject *kobj) { - debugf0("%s() here to module_put(THIS_MODULE)\n", __func__); + edac_dbg(0, "here to module_put(THIS_MODULE)\n"); kfree(kobj); @@ -345,7 +344,7 @@ static int edac_pci_main_kobj_setup(void) int err; struct bus_type *edac_subsys; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* check and count if we have already created the main kobject */ if (atomic_inc_return(&edac_pci_sysfs_refcount) != 1) @@ -356,7 +355,7 @@ static int edac_pci_main_kobj_setup(void) */ edac_subsys = edac_get_sysfs_subsys(); if (edac_subsys == NULL) { - debugf1("%s() no edac_subsys\n", __func__); + edac_dbg(1, "no edac_subsys\n"); err = -ENODEV; goto decrement_count_fail; } @@ -366,14 +365,14 @@ static int edac_pci_main_kobj_setup(void) * level main kobj for EDAC PCI */ if (!try_module_get(THIS_MODULE)) { - debugf1("%s() try_module_get() failed\n", __func__); + edac_dbg(1, "try_module_get() failed\n"); err = -ENODEV; goto mod_get_fail; } edac_pci_top_main_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL); if (!edac_pci_top_main_kobj) { - debugf1("Failed to allocate\n"); + edac_dbg(1, "Failed to allocate\n"); err = -ENOMEM; goto kzalloc_fail; } @@ -383,7 +382,7 @@ static int edac_pci_main_kobj_setup(void) &ktype_edac_pci_main_kobj, &edac_subsys->dev_root->kobj, "pci"); if (err) { - debugf1("Failed to register '.../edac/pci'\n"); + edac_dbg(1, "Failed to register '.../edac/pci'\n"); goto kobject_init_and_add_fail; } @@ -392,7 +391,7 @@ static int edac_pci_main_kobj_setup(void) * must be used, for resources to be cleaned up properly */ kobject_uevent(edac_pci_top_main_kobj, KOBJ_ADD); - debugf1("Registered '.../edac/pci' kobject\n"); + edac_dbg(1, "Registered '.../edac/pci' kobject\n"); return 0; @@ -421,15 +420,14 @@ decrement_count_fail: */ static void edac_pci_main_kobj_teardown(void) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* Decrement the count and only if no more controller instances * are connected perform the unregisteration of the top level * main kobj */ if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) { - debugf0("%s() called kobject_put on main kobj\n", - __func__); + edac_dbg(0, "called kobject_put on main kobj\n"); kobject_put(edac_pci_top_main_kobj); } edac_put_sysfs_subsys(); @@ -446,7 +444,7 @@ int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci) int err; struct kobject *edac_kobj = &pci->kobj; - debugf0("%s() idx=%d\n", __func__, pci->pci_idx); + edac_dbg(0, "idx=%d\n", pci->pci_idx); /* create the top main EDAC PCI kobject, IF needed */ err = edac_pci_main_kobj_setup(); @@ -460,8 +458,7 @@ int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci) err = sysfs_create_link(edac_kobj, &pci->dev->kobj, EDAC_PCI_SYMLINK); if (err) { - debugf0("%s() sysfs_create_link() returned err= %d\n", - __func__, err); + edac_dbg(0, "sysfs_create_link() returned err= %d\n", err); goto symlink_fail; } @@ -484,7 +481,7 @@ unregister_cleanup: */ void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci) { - debugf0("%s() index=%d\n", __func__, pci->pci_idx); + edac_dbg(0, "index=%d\n", pci->pci_idx); /* Remove the symlink */ sysfs_remove_link(&pci->kobj, EDAC_PCI_SYMLINK); @@ -496,7 +493,7 @@ void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci) * if this 'pci' is the last instance. * If it is, the main kobject will be unregistered as a result */ - debugf0("%s() calling edac_pci_main_kobj_teardown()\n", __func__); + edac_dbg(0, "calling edac_pci_main_kobj_teardown()\n"); edac_pci_main_kobj_teardown(); } @@ -572,7 +569,7 @@ static void edac_pci_dev_parity_test(struct pci_dev *dev) local_irq_restore(flags); - debugf4("PCI STATUS= 0x%04x %s\n", status, dev_name(&dev->dev)); + edac_dbg(4, "PCI STATUS= 0x%04x %s\n", status, dev_name(&dev->dev)); /* check the status reg for errors on boards NOT marked as broken * if broken, we cannot trust any of the status bits @@ -603,13 +600,15 @@ static void edac_pci_dev_parity_test(struct pci_dev *dev) } - debugf4("PCI HEADER TYPE= 0x%02x %s\n", header_type, dev_name(&dev->dev)); + edac_dbg(4, "PCI HEADER TYPE= 0x%02x %s\n", + header_type, dev_name(&dev->dev)); if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* On bridges, need to examine secondary status register */ status = get_pci_parity_status(dev, 1); - debugf4("PCI SEC_STATUS= 0x%04x %s\n", status, dev_name(&dev->dev)); + edac_dbg(4, "PCI SEC_STATUS= 0x%04x %s\n", + status, dev_name(&dev->dev)); /* check the secondary status reg for errors, * on NOT broken boards @@ -671,7 +670,7 @@ void edac_pci_do_parity_check(void) { int before_count; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); /* if policy has PCI check off, leave now */ if (!check_pci_errors) diff --git a/drivers/edac/highbank_l2_edac.c b/drivers/edac/highbank_l2_edac.c new file mode 100644 index 000000000000..e599b00c05a8 --- /dev/null +++ b/drivers/edac/highbank_l2_edac.c @@ -0,0 +1,149 @@ +/* + * Copyright 2011-2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/ctype.h> +#include <linux/edac.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/of_platform.h> + +#include "edac_core.h" +#include "edac_module.h" + +#define SR_CLR_SB_ECC_INTR 0x0 +#define SR_CLR_DB_ECC_INTR 0x4 + +struct hb_l2_drvdata { + void __iomem *base; + int sb_irq; + int db_irq; +}; + +static irqreturn_t highbank_l2_err_handler(int irq, void *dev_id) +{ + struct edac_device_ctl_info *dci = dev_id; + struct hb_l2_drvdata *drvdata = dci->pvt_info; + + if (irq == drvdata->sb_irq) { + writel(1, drvdata->base + SR_CLR_SB_ECC_INTR); + edac_device_handle_ce(dci, 0, 0, dci->ctl_name); + } + if (irq == drvdata->db_irq) { + writel(1, drvdata->base + SR_CLR_DB_ECC_INTR); + edac_device_handle_ue(dci, 0, 0, dci->ctl_name); + } + + return IRQ_HANDLED; +} + +static int __devinit highbank_l2_err_probe(struct platform_device *pdev) +{ + struct edac_device_ctl_info *dci; + struct hb_l2_drvdata *drvdata; + struct resource *r; + int res = 0; + + dci = edac_device_alloc_ctl_info(sizeof(*drvdata), "cpu", + 1, "L", 1, 2, NULL, 0, 0); + if (!dci) + return -ENOMEM; + + drvdata = dci->pvt_info; + dci->dev = &pdev->dev; + platform_set_drvdata(pdev, dci); + + if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "Unable to get mem resource\n"); + res = -ENODEV; + goto err; + } + + if (!devm_request_mem_region(&pdev->dev, r->start, + resource_size(r), dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "Error while requesting mem region\n"); + res = -EBUSY; + goto err; + } + + drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); + if (!drvdata->base) { + dev_err(&pdev->dev, "Unable to map regs\n"); + res = -ENOMEM; + goto err; + } + + drvdata->db_irq = platform_get_irq(pdev, 0); + res = devm_request_irq(&pdev->dev, drvdata->db_irq, + highbank_l2_err_handler, + 0, dev_name(&pdev->dev), dci); + if (res < 0) + goto err; + + drvdata->sb_irq = platform_get_irq(pdev, 1); + res = devm_request_irq(&pdev->dev, drvdata->sb_irq, + highbank_l2_err_handler, + 0, dev_name(&pdev->dev), dci); + if (res < 0) + goto err; + + dci->mod_name = dev_name(&pdev->dev); + dci->dev_name = dev_name(&pdev->dev); + + if (edac_device_add_device(dci)) + goto err; + + devres_close_group(&pdev->dev, NULL); + return 0; +err: + devres_release_group(&pdev->dev, NULL); + edac_device_free_ctl_info(dci); + return res; +} + +static int highbank_l2_err_remove(struct platform_device *pdev) +{ + struct edac_device_ctl_info *dci = platform_get_drvdata(pdev); + + edac_device_del_device(&pdev->dev); + edac_device_free_ctl_info(dci); + return 0; +} + +static const struct of_device_id hb_l2_err_of_match[] = { + { .compatible = "calxeda,hb-sregs-l2-ecc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, hb_l2_err_of_match); + +static struct platform_driver highbank_l2_edac_driver = { + .probe = highbank_l2_err_probe, + .remove = highbank_l2_err_remove, + .driver = { + .name = "hb_l2_edac", + .of_match_table = hb_l2_err_of_match, + }, +}; + +module_platform_driver(highbank_l2_edac_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Calxeda, Inc."); +MODULE_DESCRIPTION("EDAC Driver for Calxeda Highbank L2 Cache"); diff --git a/drivers/edac/highbank_mc_edac.c b/drivers/edac/highbank_mc_edac.c new file mode 100644 index 000000000000..c769f477fd22 --- /dev/null +++ b/drivers/edac/highbank_mc_edac.c @@ -0,0 +1,264 @@ +/* + * Copyright 2011-2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/ctype.h> +#include <linux/edac.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/uaccess.h> + +#include "edac_core.h" +#include "edac_module.h" + +/* DDR Ctrlr Error Registers */ +#define HB_DDR_ECC_OPT 0x128 +#define HB_DDR_ECC_U_ERR_ADDR 0x130 +#define HB_DDR_ECC_U_ERR_STAT 0x134 +#define HB_DDR_ECC_U_ERR_DATAL 0x138 +#define HB_DDR_ECC_U_ERR_DATAH 0x13c +#define HB_DDR_ECC_C_ERR_ADDR 0x140 +#define HB_DDR_ECC_C_ERR_STAT 0x144 +#define HB_DDR_ECC_C_ERR_DATAL 0x148 +#define HB_DDR_ECC_C_ERR_DATAH 0x14c +#define HB_DDR_ECC_INT_STATUS 0x180 +#define HB_DDR_ECC_INT_ACK 0x184 +#define HB_DDR_ECC_U_ERR_ID 0x424 +#define HB_DDR_ECC_C_ERR_ID 0x428 + +#define HB_DDR_ECC_INT_STAT_CE 0x8 +#define HB_DDR_ECC_INT_STAT_DOUBLE_CE 0x10 +#define HB_DDR_ECC_INT_STAT_UE 0x20 +#define HB_DDR_ECC_INT_STAT_DOUBLE_UE 0x40 + +#define HB_DDR_ECC_OPT_MODE_MASK 0x3 +#define HB_DDR_ECC_OPT_FWC 0x100 +#define HB_DDR_ECC_OPT_XOR_SHIFT 16 + +struct hb_mc_drvdata { + void __iomem *mc_vbase; +}; + +static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id) +{ + struct mem_ctl_info *mci = dev_id; + struct hb_mc_drvdata *drvdata = mci->pvt_info; + u32 status, err_addr; + + /* Read the interrupt status register */ + status = readl(drvdata->mc_vbase + HB_DDR_ECC_INT_STATUS); + + if (status & HB_DDR_ECC_INT_STAT_UE) { + err_addr = readl(drvdata->mc_vbase + HB_DDR_ECC_U_ERR_ADDR); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, + err_addr >> PAGE_SHIFT, + err_addr & ~PAGE_MASK, 0, + 0, 0, -1, + mci->ctl_name, ""); + } + if (status & HB_DDR_ECC_INT_STAT_CE) { + u32 syndrome = readl(drvdata->mc_vbase + HB_DDR_ECC_C_ERR_STAT); + syndrome = (syndrome >> 8) & 0xff; + err_addr = readl(drvdata->mc_vbase + HB_DDR_ECC_C_ERR_ADDR); + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, + err_addr >> PAGE_SHIFT, + err_addr & ~PAGE_MASK, syndrome, + 0, 0, -1, + mci->ctl_name, ""); + } + + /* clear the error, clears the interrupt */ + writel(status, drvdata->mc_vbase + HB_DDR_ECC_INT_ACK); + return IRQ_HANDLED; +} + +#ifdef CONFIG_EDAC_DEBUG +static ssize_t highbank_mc_err_inject_write(struct file *file, + const char __user *data, + size_t count, loff_t *ppos) +{ + struct mem_ctl_info *mci = file->private_data; + struct hb_mc_drvdata *pdata = mci->pvt_info; + char buf[32]; + size_t buf_size; + u32 reg; + u8 synd; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, data, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + if (!kstrtou8(buf, 16, &synd)) { + reg = readl(pdata->mc_vbase + HB_DDR_ECC_OPT); + reg &= HB_DDR_ECC_OPT_MODE_MASK; + reg |= (synd << HB_DDR_ECC_OPT_XOR_SHIFT) | HB_DDR_ECC_OPT_FWC; + writel(reg, pdata->mc_vbase + HB_DDR_ECC_OPT); + } + + return count; +} + +static int debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations highbank_mc_debug_inject_fops = { + .open = debugfs_open, + .write = highbank_mc_err_inject_write, + .llseek = generic_file_llseek, +}; + +static void __devinit highbank_mc_create_debugfs_nodes(struct mem_ctl_info *mci) +{ + if (mci->debugfs) + debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci, + &highbank_mc_debug_inject_fops); +; +} +#else +static void __devinit highbank_mc_create_debugfs_nodes(struct mem_ctl_info *mci) +{} +#endif + +static int __devinit highbank_mc_probe(struct platform_device *pdev) +{ + struct edac_mc_layer layers[2]; + struct mem_ctl_info *mci; + struct hb_mc_drvdata *drvdata; + struct dimm_info *dimm; + struct resource *r; + u32 control; + int irq; + int res = 0; + + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; + layers[0].size = 1; + layers[0].is_virt_csrow = true; + layers[1].type = EDAC_MC_LAYER_CHANNEL; + layers[1].size = 1; + layers[1].is_virt_csrow = false; + mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, + sizeof(struct hb_mc_drvdata)); + if (!mci) + return -ENOMEM; + + mci->pdev = &pdev->dev; + drvdata = mci->pvt_info; + platform_set_drvdata(pdev, mci); + + if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "Unable to get mem resource\n"); + res = -ENODEV; + goto err; + } + + if (!devm_request_mem_region(&pdev->dev, r->start, + resource_size(r), dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "Error while requesting mem region\n"); + res = -EBUSY; + goto err; + } + + drvdata->mc_vbase = devm_ioremap(&pdev->dev, + r->start, resource_size(r)); + if (!drvdata->mc_vbase) { + dev_err(&pdev->dev, "Unable to map regs\n"); + res = -ENOMEM; + goto err; + } + + control = readl(drvdata->mc_vbase + HB_DDR_ECC_OPT) & 0x3; + if (!control || (control == 0x2)) { + dev_err(&pdev->dev, "No ECC present, or ECC disabled\n"); + res = -ENODEV; + goto err; + } + + irq = platform_get_irq(pdev, 0); + res = devm_request_irq(&pdev->dev, irq, highbank_mc_err_handler, + 0, dev_name(&pdev->dev), mci); + if (res < 0) { + dev_err(&pdev->dev, "Unable to request irq %d\n", irq); + goto err; + } + + mci->mtype_cap = MEM_FLAG_DDR3; + mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; + mci->edac_cap = EDAC_FLAG_SECDED; + mci->mod_name = dev_name(&pdev->dev); + mci->mod_ver = "1"; + mci->ctl_name = dev_name(&pdev->dev); + mci->scrub_mode = SCRUB_SW_SRC; + + /* Only a single 4GB DIMM is supported */ + dimm = *mci->dimms; + dimm->nr_pages = (~0UL >> PAGE_SHIFT) + 1; + dimm->grain = 8; + dimm->dtype = DEV_X8; + dimm->mtype = MEM_DDR3; + dimm->edac_mode = EDAC_SECDED; + + res = edac_mc_add_mc(mci); + if (res < 0) + goto err; + + highbank_mc_create_debugfs_nodes(mci); + + devres_close_group(&pdev->dev, NULL); + return 0; +err: + devres_release_group(&pdev->dev, NULL); + edac_mc_free(mci); + return res; +} + +static int highbank_mc_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci = platform_get_drvdata(pdev); + + edac_mc_del_mc(&pdev->dev); + edac_mc_free(mci); + return 0; +} + +static const struct of_device_id hb_ddr_ctrl_of_match[] = { + { .compatible = "calxeda,hb-ddr-ctrl", }, + {}, +}; +MODULE_DEVICE_TABLE(of, hb_ddr_ctrl_of_match); + +static struct platform_driver highbank_mc_edac_driver = { + .probe = highbank_mc_probe, + .remove = highbank_mc_remove, + .driver = { + .name = "hb_mc_edac", + .of_match_table = hb_ddr_ctrl_of_match, + }, +}; + +module_platform_driver(highbank_mc_edac_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Calxeda, Inc."); +MODULE_DESCRIPTION("EDAC Driver for Calxeda Highbank"); diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c index 8ad1744faacd..d3d19cc4e9a1 100644 --- a/drivers/edac/i3000_edac.c +++ b/drivers/edac/i3000_edac.c @@ -194,7 +194,7 @@ static void i3000_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -236,7 +236,7 @@ static int i3000_process_error_info(struct mem_ctl_info *mci, int row, multi_chan, channel; unsigned long pfn, offset; - multi_chan = mci->csrows[0].nr_channels - 1; + multi_chan = mci->csrows[0]->nr_channels - 1; if (!(info->errsts & I3000_ERRSTS_BITS)) return 0; @@ -245,9 +245,9 @@ static int i3000_process_error_info(struct mem_ctl_info *mci, return 1; if ((info->errsts ^ info->errsts2) & I3000_ERRSTS_BITS) { - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1, - "UE overwrote CE", "", NULL); + "UE overwrote CE", ""); info->errsts = info->errsts2; } @@ -258,15 +258,15 @@ static int i3000_process_error_info(struct mem_ctl_info *mci, row = edac_mc_find_csrow_by_page(mci, pfn); if (info->errsts & I3000_ERRSTS_UE) - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, pfn, offset, 0, row, -1, -1, - "i3000 UE", "", NULL); + "i3000 UE", ""); else - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, pfn, offset, info->derrsyn, row, multi_chan ? channel : 0, -1, - "i3000 CE", "", NULL); + "i3000 CE", ""); return 1; } @@ -275,7 +275,7 @@ static void i3000_check(struct mem_ctl_info *mci) { struct i3000_error_info info; - debugf1("MC%d: %s()\n", mci->mc_idx, __func__); + edac_dbg(1, "MC%d\n", mci->mc_idx); i3000_get_error_info(mci, &info); i3000_process_error_info(mci, &info, 1); } @@ -322,7 +322,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) unsigned long mchbar; void __iomem *window; - debugf0("MC: %s()\n", __func__); + edac_dbg(0, "MC:\n"); pci_read_config_dword(pdev, I3000_MCHBAR, (u32 *) & mchbar); mchbar &= I3000_MCHBAR_MASK; @@ -366,9 +366,9 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) if (!mci) return -ENOMEM; - debugf3("MC: %s(): init mci\n", __func__); + edac_dbg(3, "MC: init mci\n"); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_SECDED; @@ -393,14 +393,13 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) for (last_cumul_size = i = 0; i < mci->nr_csrows; i++) { u8 value; u32 cumul_size; - struct csrow_info *csrow = &mci->csrows[i]; + struct csrow_info *csrow = mci->csrows[i]; value = drb[i]; cumul_size = value << (I3000_DRB_SHIFT - PAGE_SHIFT); if (interleaved) cumul_size <<= 1; - debugf3("MC: %s(): (%d) cumul_size 0x%x\n", - __func__, i, cumul_size); + edac_dbg(3, "MC: (%d) cumul_size 0x%x\n", i, cumul_size); if (cumul_size == last_cumul_size) continue; @@ -410,7 +409,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) last_cumul_size = cumul_size; for (j = 0; j < nr_channels; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / nr_channels; dimm->grain = I3000_DEAP_GRAIN; @@ -429,7 +428,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) rc = -ENODEV; if (edac_mc_add_mc(mci)) { - debugf3("MC: %s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(3, "MC: failed edac_mc_add_mc()\n"); goto fail; } @@ -445,7 +444,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) } /* get this far and it's successful */ - debugf3("MC: %s(): success\n", __func__); + edac_dbg(3, "MC: success\n"); return 0; fail: @@ -461,7 +460,7 @@ static int __devinit i3000_init_one(struct pci_dev *pdev, { int rc; - debugf0("MC: %s()\n", __func__); + edac_dbg(0, "MC:\n"); if (pci_enable_device(pdev) < 0) return -EIO; @@ -477,7 +476,7 @@ static void __devexit i3000_remove_one(struct pci_dev *pdev) { struct mem_ctl_info *mci; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); if (i3000_pci) edac_pci_release_generic_ctl(i3000_pci); @@ -511,7 +510,7 @@ static int __init i3000_init(void) { int pci_rc; - debugf3("MC: %s()\n", __func__); + edac_dbg(3, "MC:\n"); /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); @@ -525,14 +524,14 @@ static int __init i3000_init(void) mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_3000_HB, NULL); if (!mci_pdev) { - debugf0("i3000 pci_get_device fail\n"); + edac_dbg(0, "i3000 pci_get_device fail\n"); pci_rc = -ENODEV; goto fail1; } pci_rc = i3000_init_one(mci_pdev, i3000_pci_tbl); if (pci_rc < 0) { - debugf0("i3000 init fail\n"); + edac_dbg(0, "i3000 init fail\n"); pci_rc = -ENODEV; goto fail1; } @@ -552,7 +551,7 @@ fail0: static void __exit i3000_exit(void) { - debugf3("MC: %s()\n", __func__); + edac_dbg(3, "MC:\n"); pci_unregister_driver(&i3000_driver); if (!i3000_registered) { diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c index bbe43ef71823..47180a08edad 100644 --- a/drivers/edac/i3200_edac.c +++ b/drivers/edac/i3200_edac.c @@ -110,10 +110,10 @@ static int how_many_channels(struct pci_dev *pdev) pci_read_config_byte(pdev, I3200_CAPID0 + 8, &capid0_8b); if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */ - debugf0("In single channel mode.\n"); + edac_dbg(0, "In single channel mode\n"); return 1; } else { - debugf0("In dual channel mode.\n"); + edac_dbg(0, "In dual channel mode\n"); return 2; } } @@ -159,7 +159,7 @@ static void i3200_clear_error_info(struct mem_ctl_info *mci) { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * Clear any error bits. @@ -176,7 +176,7 @@ static void i3200_get_and_clear_error_info(struct mem_ctl_info *mci, struct i3200_priv *priv = mci->pvt_info; void __iomem *window = priv->window; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -218,25 +218,25 @@ static void i3200_process_error_info(struct mem_ctl_info *mci, return; if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) { - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, - -1, -1, -1, "UE overwrote CE", "", NULL); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, + -1, -1, -1, "UE overwrote CE", ""); info->errsts = info->errsts2; } for (channel = 0; channel < nr_channels; channel++) { log = info->eccerrlog[channel]; if (log & I3200_ECCERRLOG_UE) { - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, eccerrlog_row(channel, log), -1, -1, - "i3000 UE", "", NULL); + "i3000 UE", ""); } else if (log & I3200_ECCERRLOG_CE) { - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, eccerrlog_syndrome(log), eccerrlog_row(channel, log), -1, -1, - "i3000 UE", "", NULL); + "i3000 UE", ""); } } } @@ -245,7 +245,7 @@ static void i3200_check(struct mem_ctl_info *mci) { struct i3200_error_info info; - debugf1("MC%d: %s()\n", mci->mc_idx, __func__); + edac_dbg(1, "MC%d\n", mci->mc_idx); i3200_get_and_clear_error_info(mci, &info); i3200_process_error_info(mci, &info); } @@ -332,7 +332,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx) void __iomem *window; struct i3200_priv *priv; - debugf0("MC: %s()\n", __func__); + edac_dbg(0, "MC:\n"); window = i3200_map_mchbar(pdev); if (!window) @@ -352,9 +352,9 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx) if (!mci) return -ENOMEM; - debugf3("MC: %s(): init mci\n", __func__); + edac_dbg(3, "MC: init mci\n"); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_SECDED; @@ -379,7 +379,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx) */ for (i = 0; i < mci->nr_csrows; i++) { unsigned long nr_pages; - struct csrow_info *csrow = &mci->csrows[i]; + struct csrow_info *csrow = mci->csrows[i]; nr_pages = drb_to_nr_pages(drbs, stacked, i / I3200_RANKS_PER_CHANNEL, @@ -389,7 +389,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx) continue; for (j = 0; j < nr_channels; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / nr_channels; dimm->grain = nr_pages << PAGE_SHIFT; @@ -403,12 +403,12 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx) rc = -ENODEV; if (edac_mc_add_mc(mci)) { - debugf3("MC: %s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(3, "MC: failed edac_mc_add_mc()\n"); goto fail; } /* get this far and it's successful */ - debugf3("MC: %s(): success\n", __func__); + edac_dbg(3, "MC: success\n"); return 0; fail: @@ -424,7 +424,7 @@ static int __devinit i3200_init_one(struct pci_dev *pdev, { int rc; - debugf0("MC: %s()\n", __func__); + edac_dbg(0, "MC:\n"); if (pci_enable_device(pdev) < 0) return -EIO; @@ -441,7 +441,7 @@ static void __devexit i3200_remove_one(struct pci_dev *pdev) struct mem_ctl_info *mci; struct i3200_priv *priv; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); mci = edac_mc_del_mc(&pdev->dev); if (!mci) @@ -475,7 +475,7 @@ static int __init i3200_init(void) { int pci_rc; - debugf3("MC: %s()\n", __func__); + edac_dbg(3, "MC:\n"); /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); @@ -489,14 +489,14 @@ static int __init i3200_init(void) mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_3200_HB, NULL); if (!mci_pdev) { - debugf0("i3200 pci_get_device fail\n"); + edac_dbg(0, "i3200 pci_get_device fail\n"); pci_rc = -ENODEV; goto fail1; } pci_rc = i3200_init_one(mci_pdev, i3200_pci_tbl); if (pci_rc < 0) { - debugf0("i3200 init fail\n"); + edac_dbg(0, "i3200 init fail\n"); pci_rc = -ENODEV; goto fail1; } @@ -516,7 +516,7 @@ fail0: static void __exit i3200_exit(void) { - debugf3("MC: %s()\n", __func__); + edac_dbg(3, "MC:\n"); pci_unregister_driver(&i3200_driver); if (!i3200_registered) { diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index 11ea835f155a..39c63757c2a1 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -273,7 +273,7 @@ #define CHANNELS_PER_BRANCH 2 #define MAX_BRANCHES 2 -/* Defines to extract the vaious fields from the +/* Defines to extract the various fields from the * MTRx - Memory Technology Registers */ #define MTR_DIMMS_PRESENT(mtr) ((mtr) & (0x1 << 8)) @@ -287,22 +287,6 @@ #define MTR_DIMM_COLS(mtr) ((mtr) & 0x3) #define MTR_DIMM_COLS_ADDR_BITS(mtr) (MTR_DIMM_COLS(mtr) + 10) -#ifdef CONFIG_EDAC_DEBUG -static char *numrow_toString[] = { - "8,192 - 13 rows", - "16,384 - 14 rows", - "32,768 - 15 rows", - "reserved" -}; - -static char *numcol_toString[] = { - "1,024 - 10 columns", - "2,048 - 11 columns", - "4,096 - 12 columns", - "reserved" -}; -#endif - /* enables the report of miscellaneous messages as CE errors - default off */ static int misc_messages; @@ -344,7 +328,13 @@ struct i5000_pvt { struct pci_dev *branch_1; /* 22.0 */ u16 tolm; /* top of low memory */ - u64 ambase; /* AMB BAR */ + union { + u64 ambase; /* AMB BAR */ + struct { + u32 ambase_bottom; + u32 ambase_top; + } u __packed; + }; u16 mir0, mir1, mir2; @@ -494,10 +484,9 @@ static void i5000_process_fatal_error_info(struct mem_ctl_info *mci, ras = NREC_RAS(info->nrecmemb); cas = NREC_CAS(info->nrecmemb); - debugf0("\t\tCSROW= %d Channel= %d " - "(DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n", - rank, channel, bank, - rdwr ? "Write" : "Read", ras, cas); + edac_dbg(0, "\t\tCSROW= %d Channel= %d (DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n", + rank, channel, bank, + rdwr ? "Write" : "Read", ras, cas); /* Only 1 bit will be on */ switch (allErrors) { @@ -536,10 +525,10 @@ static void i5000_process_fatal_error_info(struct mem_ctl_info *mci, bank, ras, cas, allErrors, specific); /* Call the helper to output message */ - edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 0, 0, 0, + edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 1, 0, 0, 0, channel >> 1, channel & 1, rank, rdwr ? "Write error" : "Read error", - msg, NULL); + msg); } /* @@ -574,7 +563,7 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci, /* ONLY ONE of the possible error bits will be set, as per the docs */ ue_errors = allErrors & FERR_NF_UNCORRECTABLE; if (ue_errors) { - debugf0("\tUncorrected bits= 0x%x\n", ue_errors); + edac_dbg(0, "\tUncorrected bits= 0x%x\n", ue_errors); branch = EXTRACT_FBDCHAN_INDX(info->ferr_nf_fbd); @@ -590,11 +579,9 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci, ras = NREC_RAS(info->nrecmemb); cas = NREC_CAS(info->nrecmemb); - debugf0 - ("\t\tCSROW= %d Channels= %d,%d (Branch= %d " - "DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n", - rank, channel, channel + 1, branch >> 1, bank, - rdwr ? "Write" : "Read", ras, cas); + edac_dbg(0, "\t\tCSROW= %d Channels= %d,%d (Branch= %d DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n", + rank, channel, channel + 1, branch >> 1, bank, + rdwr ? "Write" : "Read", ras, cas); switch (ue_errors) { case FERR_NF_M12ERR: @@ -637,16 +624,16 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci, rank, bank, ras, cas, ue_errors, specific); /* Call the helper to output message */ - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, channel >> 1, -1, rank, rdwr ? "Write error" : "Read error", - msg, NULL); + msg); } /* Check correctable errors */ ce_errors = allErrors & FERR_NF_CORRECTABLE; if (ce_errors) { - debugf0("\tCorrected bits= 0x%x\n", ce_errors); + edac_dbg(0, "\tCorrected bits= 0x%x\n", ce_errors); branch = EXTRACT_FBDCHAN_INDX(info->ferr_nf_fbd); @@ -664,10 +651,9 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci, ras = REC_RAS(info->recmemb); cas = REC_CAS(info->recmemb); - debugf0("\t\tCSROW= %d Channel= %d (Branch %d " - "DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n", - rank, channel, branch >> 1, bank, - rdwr ? "Write" : "Read", ras, cas); + edac_dbg(0, "\t\tCSROW= %d Channel= %d (Branch %d DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n", + rank, channel, branch >> 1, bank, + rdwr ? "Write" : "Read", ras, cas); switch (ce_errors) { case FERR_NF_M17ERR: @@ -692,10 +678,10 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci, specific); /* Call the helper to output message */ - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, channel >> 1, channel % 2, rank, rdwr ? "Write error" : "Read error", - msg, NULL); + msg); } if (!misc_messages) @@ -738,9 +724,9 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci, "Err=%#x (%s)", misc_errors, specific); /* Call the helper to output message */ - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, branch >> 1, -1, -1, - "Misc error", msg, NULL); + "Misc error", msg); } } @@ -779,7 +765,7 @@ static void i5000_clear_error(struct mem_ctl_info *mci) static void i5000_check_error(struct mem_ctl_info *mci) { struct i5000_error_info info; - debugf4("MC%d: %s: %s()\n", mci->mc_idx, __FILE__, __func__); + edac_dbg(4, "MC%d\n", mci->mc_idx); i5000_get_error_info(mci, &info); i5000_process_error_info(mci, &info, 1); } @@ -850,15 +836,16 @@ static int i5000_get_devices(struct mem_ctl_info *mci, int dev_idx) pvt->fsb_error_regs = pdev; - debugf1("System Address, processor bus- PCI Bus ID: %s %x:%x\n", - pci_name(pvt->system_address), - pvt->system_address->vendor, pvt->system_address->device); - debugf1("Branchmap, control and errors - PCI Bus ID: %s %x:%x\n", - pci_name(pvt->branchmap_werrors), - pvt->branchmap_werrors->vendor, pvt->branchmap_werrors->device); - debugf1("FSB Error Regs - PCI Bus ID: %s %x:%x\n", - pci_name(pvt->fsb_error_regs), - pvt->fsb_error_regs->vendor, pvt->fsb_error_regs->device); + edac_dbg(1, "System Address, processor bus- PCI Bus ID: %s %x:%x\n", + pci_name(pvt->system_address), + pvt->system_address->vendor, pvt->system_address->device); + edac_dbg(1, "Branchmap, control and errors - PCI Bus ID: %s %x:%x\n", + pci_name(pvt->branchmap_werrors), + pvt->branchmap_werrors->vendor, + pvt->branchmap_werrors->device); + edac_dbg(1, "FSB Error Regs - PCI Bus ID: %s %x:%x\n", + pci_name(pvt->fsb_error_regs), + pvt->fsb_error_regs->vendor, pvt->fsb_error_regs->device); pdev = NULL; pdev = pci_get_device(PCI_VENDOR_ID_INTEL, @@ -981,16 +968,25 @@ static void decode_mtr(int slot_row, u16 mtr) ans = MTR_DIMMS_PRESENT(mtr); - debugf2("\tMTR%d=0x%x: DIMMs are %s\n", slot_row, mtr, - ans ? "Present" : "NOT Present"); + edac_dbg(2, "\tMTR%d=0x%x: DIMMs are %sPresent\n", + slot_row, mtr, ans ? "" : "NOT "); if (!ans) return; - debugf2("\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr)); - debugf2("\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr)); - debugf2("\t\tNUMRANK: %s\n", MTR_DIMM_RANK(mtr) ? "double" : "single"); - debugf2("\t\tNUMROW: %s\n", numrow_toString[MTR_DIMM_ROWS(mtr)]); - debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]); + edac_dbg(2, "\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr)); + edac_dbg(2, "\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr)); + edac_dbg(2, "\t\tNUMRANK: %s\n", + MTR_DIMM_RANK(mtr) ? "double" : "single"); + edac_dbg(2, "\t\tNUMROW: %s\n", + MTR_DIMM_ROWS(mtr) == 0 ? "8,192 - 13 rows" : + MTR_DIMM_ROWS(mtr) == 1 ? "16,384 - 14 rows" : + MTR_DIMM_ROWS(mtr) == 2 ? "32,768 - 15 rows" : + "reserved"); + edac_dbg(2, "\t\tNUMCOL: %s\n", + MTR_DIMM_COLS(mtr) == 0 ? "1,024 - 10 columns" : + MTR_DIMM_COLS(mtr) == 1 ? "2,048 - 11 columns" : + MTR_DIMM_COLS(mtr) == 2 ? "4,096 - 12 columns" : + "reserved"); } static void handle_channel(struct i5000_pvt *pvt, int slot, int channel, @@ -1061,7 +1057,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt) "--------------------------------"); p += n; space -= n; - debugf2("%s\n", mem_buffer); + edac_dbg(2, "%s\n", mem_buffer); p = mem_buffer; space = PAGE_SIZE; } @@ -1082,7 +1078,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt) } p += n; space -= n; - debugf2("%s\n", mem_buffer); + edac_dbg(2, "%s\n", mem_buffer); p = mem_buffer; space = PAGE_SIZE; } @@ -1092,7 +1088,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt) "--------------------------------"); p += n; space -= n; - debugf2("%s\n", mem_buffer); + edac_dbg(2, "%s\n", mem_buffer); p = mem_buffer; space = PAGE_SIZE; @@ -1105,7 +1101,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt) p += n; space -= n; } - debugf2("%s\n", mem_buffer); + edac_dbg(2, "%s\n", mem_buffer); p = mem_buffer; space = PAGE_SIZE; @@ -1118,7 +1114,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt) } /* output the last message and free buffer */ - debugf2("%s\n", mem_buffer); + edac_dbg(2, "%s\n", mem_buffer); kfree(mem_buffer); } @@ -1141,24 +1137,25 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci) pvt = mci->pvt_info; pci_read_config_dword(pvt->system_address, AMBASE, - (u32 *) & pvt->ambase); + &pvt->u.ambase_bottom); pci_read_config_dword(pvt->system_address, AMBASE + sizeof(u32), - ((u32 *) & pvt->ambase) + sizeof(u32)); + &pvt->u.ambase_top); maxdimmperch = pvt->maxdimmperch; maxch = pvt->maxch; - debugf2("AMBASE= 0x%lx MAXCH= %d MAX-DIMM-Per-CH= %d\n", - (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch); + edac_dbg(2, "AMBASE= 0x%lx MAXCH= %d MAX-DIMM-Per-CH= %d\n", + (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch); /* Get the Branch Map regs */ pci_read_config_word(pvt->branchmap_werrors, TOLM, &pvt->tolm); pvt->tolm >>= 12; - debugf2("\nTOLM (number of 256M regions) =%u (0x%x)\n", pvt->tolm, - pvt->tolm); + edac_dbg(2, "TOLM (number of 256M regions) =%u (0x%x)\n", + pvt->tolm, pvt->tolm); actual_tolm = pvt->tolm << 28; - debugf2("Actual TOLM byte addr=%u (0x%x)\n", actual_tolm, actual_tolm); + edac_dbg(2, "Actual TOLM byte addr=%u (0x%x)\n", + actual_tolm, actual_tolm); pci_read_config_word(pvt->branchmap_werrors, MIR0, &pvt->mir0); pci_read_config_word(pvt->branchmap_werrors, MIR1, &pvt->mir1); @@ -1168,15 +1165,18 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci) limit = (pvt->mir0 >> 4) & 0x0FFF; way0 = pvt->mir0 & 0x1; way1 = pvt->mir0 & 0x2; - debugf2("MIR0: limit= 0x%x WAY1= %u WAY0= %x\n", limit, way1, way0); + edac_dbg(2, "MIR0: limit= 0x%x WAY1= %u WAY0= %x\n", + limit, way1, way0); limit = (pvt->mir1 >> 4) & 0x0FFF; way0 = pvt->mir1 & 0x1; way1 = pvt->mir1 & 0x2; - debugf2("MIR1: limit= 0x%x WAY1= %u WAY0= %x\n", limit, way1, way0); + edac_dbg(2, "MIR1: limit= 0x%x WAY1= %u WAY0= %x\n", + limit, way1, way0); limit = (pvt->mir2 >> 4) & 0x0FFF; way0 = pvt->mir2 & 0x1; way1 = pvt->mir2 & 0x2; - debugf2("MIR2: limit= 0x%x WAY1= %u WAY0= %x\n", limit, way1, way0); + edac_dbg(2, "MIR2: limit= 0x%x WAY1= %u WAY0= %x\n", + limit, way1, way0); /* Get the MTR[0-3] regs */ for (slot_row = 0; slot_row < NUM_MTRS; slot_row++) { @@ -1185,31 +1185,31 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci) pci_read_config_word(pvt->branch_0, where, &pvt->b0_mtr[slot_row]); - debugf2("MTR%d where=0x%x B0 value=0x%x\n", slot_row, where, - pvt->b0_mtr[slot_row]); + edac_dbg(2, "MTR%d where=0x%x B0 value=0x%x\n", + slot_row, where, pvt->b0_mtr[slot_row]); if (pvt->maxch >= CHANNELS_PER_BRANCH) { pci_read_config_word(pvt->branch_1, where, &pvt->b1_mtr[slot_row]); - debugf2("MTR%d where=0x%x B1 value=0x%x\n", slot_row, - where, pvt->b1_mtr[slot_row]); + edac_dbg(2, "MTR%d where=0x%x B1 value=0x%x\n", + slot_row, where, pvt->b1_mtr[slot_row]); } else { pvt->b1_mtr[slot_row] = 0; } } /* Read and dump branch 0's MTRs */ - debugf2("\nMemory Technology Registers:\n"); - debugf2(" Branch 0:\n"); + edac_dbg(2, "Memory Technology Registers:\n"); + edac_dbg(2, " Branch 0:\n"); for (slot_row = 0; slot_row < NUM_MTRS; slot_row++) { decode_mtr(slot_row, pvt->b0_mtr[slot_row]); } pci_read_config_word(pvt->branch_0, AMB_PRESENT_0, &pvt->b0_ambpresent0); - debugf2("\t\tAMB-Branch 0-present0 0x%x:\n", pvt->b0_ambpresent0); + edac_dbg(2, "\t\tAMB-Branch 0-present0 0x%x:\n", pvt->b0_ambpresent0); pci_read_config_word(pvt->branch_0, AMB_PRESENT_1, &pvt->b0_ambpresent1); - debugf2("\t\tAMB-Branch 0-present1 0x%x:\n", pvt->b0_ambpresent1); + edac_dbg(2, "\t\tAMB-Branch 0-present1 0x%x:\n", pvt->b0_ambpresent1); /* Only if we have 2 branchs (4 channels) */ if (pvt->maxch < CHANNELS_PER_BRANCH) { @@ -1217,18 +1217,18 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci) pvt->b1_ambpresent1 = 0; } else { /* Read and dump branch 1's MTRs */ - debugf2(" Branch 1:\n"); + edac_dbg(2, " Branch 1:\n"); for (slot_row = 0; slot_row < NUM_MTRS; slot_row++) { decode_mtr(slot_row, pvt->b1_mtr[slot_row]); } pci_read_config_word(pvt->branch_1, AMB_PRESENT_0, &pvt->b1_ambpresent0); - debugf2("\t\tAMB-Branch 1-present0 0x%x:\n", - pvt->b1_ambpresent0); + edac_dbg(2, "\t\tAMB-Branch 1-present0 0x%x:\n", + pvt->b1_ambpresent0); pci_read_config_word(pvt->branch_1, AMB_PRESENT_1, &pvt->b1_ambpresent1); - debugf2("\t\tAMB-Branch 1-present1 0x%x:\n", - pvt->b1_ambpresent1); + edac_dbg(2, "\t\tAMB-Branch 1-present1 0x%x:\n", + pvt->b1_ambpresent1); } /* Go and determine the size of each DIMM and place in an @@ -1363,10 +1363,9 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx) int num_channels; int num_dimms_per_channel; - debugf0("MC: %s: %s(), pdev bus %u dev=0x%x fn=0x%x\n", - __FILE__, __func__, - pdev->bus->number, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + edac_dbg(0, "MC: pdev bus %u dev=0x%x fn=0x%x\n", + pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); /* We only are looking for func 0 of the set */ if (PCI_FUNC(pdev->devfn) != 0) @@ -1388,8 +1387,8 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx) i5000_get_dimm_and_channel_counts(pdev, &num_dimms_per_channel, &num_channels); - debugf0("MC: %s(): Number of Branches=2 Channels= %d DIMMS= %d\n", - __func__, num_channels, num_dimms_per_channel); + edac_dbg(0, "MC: Number of Branches=2 Channels= %d DIMMS= %d\n", + num_channels, num_dimms_per_channel); /* allocate a new MC control structure */ @@ -1406,10 +1405,9 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx) if (mci == NULL) return -ENOMEM; - kobject_get(&mci->edac_mci_kobj); - debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci); + edac_dbg(0, "MC: mci = %p\n", mci); - mci->dev = &pdev->dev; /* record ptr to the generic device */ + mci->pdev = &pdev->dev; /* record ptr to the generic device */ pvt = mci->pvt_info; pvt->system_address = pdev; /* Record this device in our private */ @@ -1439,19 +1437,16 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx) /* initialize the MC control structure 'csrows' table * with the mapping and control information */ if (i5000_init_csrows(mci)) { - debugf0("MC: Setting mci->edac_cap to EDAC_FLAG_NONE\n" - " because i5000_init_csrows() returned nonzero " - "value\n"); + edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i5000_init_csrows() returned nonzero value\n"); mci->edac_cap = EDAC_FLAG_NONE; /* no csrows found */ } else { - debugf1("MC: Enable error reporting now\n"); + edac_dbg(1, "MC: Enable error reporting now\n"); i5000_enable_error_reporting(mci); } /* add this new MC control structure to EDAC's list of MCs */ if (edac_mc_add_mc(mci)) { - debugf0("MC: %s: %s(): failed edac_mc_add_mc()\n", - __FILE__, __func__); + edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); /* FIXME: perhaps some code should go here that disables error * reporting if we just enabled it */ @@ -1479,7 +1474,6 @@ fail1: i5000_put_devices(mci); fail0: - kobject_put(&mci->edac_mci_kobj); edac_mc_free(mci); return -ENODEV; } @@ -1496,7 +1490,7 @@ static int __devinit i5000_init_one(struct pci_dev *pdev, { int rc; - debugf0("MC: %s: %s()\n", __FILE__, __func__); + edac_dbg(0, "MC:\n"); /* wake up device */ rc = pci_enable_device(pdev); @@ -1515,7 +1509,7 @@ static void __devexit i5000_remove_one(struct pci_dev *pdev) { struct mem_ctl_info *mci; - debugf0("%s: %s()\n", __FILE__, __func__); + edac_dbg(0, "\n"); if (i5000_pci) edac_pci_release_generic_ctl(i5000_pci); @@ -1525,7 +1519,6 @@ static void __devexit i5000_remove_one(struct pci_dev *pdev) /* retrieve references to resources, and free those resources */ i5000_put_devices(mci); - kobject_put(&mci->edac_mci_kobj); edac_mc_free(mci); } @@ -1562,7 +1555,7 @@ static int __init i5000_init(void) { int pci_rc; - debugf2("MC: %s: %s()\n", __FILE__, __func__); + edac_dbg(2, "MC:\n"); /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); @@ -1578,7 +1571,7 @@ static int __init i5000_init(void) */ static void __exit i5000_exit(void) { - debugf2("MC: %s: %s()\n", __FILE__, __func__); + edac_dbg(2, "MC:\n"); pci_unregister_driver(&i5000_driver); } diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c index e9e7c2a29dc3..c4b5e5f868e8 100644 --- a/drivers/edac/i5100_edac.c +++ b/drivers/edac/i5100_edac.c @@ -431,10 +431,10 @@ static void i5100_handle_ce(struct mem_ctl_info *mci, "bank %u, cas %u, ras %u\n", bank, cas, ras); - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, syndrome, chan, rank, -1, - msg, detail, NULL); + msg, detail); } static void i5100_handle_ue(struct mem_ctl_info *mci, @@ -453,10 +453,10 @@ static void i5100_handle_ue(struct mem_ctl_info *mci, "bank %u, cas %u, ras %u\n", bank, cas, ras); - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, syndrome, chan, rank, -1, - msg, detail, NULL); + msg, detail); } static void i5100_read_log(struct mem_ctl_info *mci, int chan, @@ -859,8 +859,8 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci) i5100_rank_to_slot(mci, chan, rank)); } - debugf2("dimm channel %d, rank %d, size %ld\n", - chan, rank, (long)PAGES_TO_MiB(npages)); + edac_dbg(2, "dimm channel %d, rank %d, size %ld\n", + chan, rank, (long)PAGES_TO_MiB(npages)); } } @@ -943,7 +943,7 @@ static int __devinit i5100_init_one(struct pci_dev *pdev, goto bail_disable_ch1; } - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; priv = mci->pvt_info; priv->ranksperchan = ranksperch; diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index 6640c29e1885..277246998b80 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -300,24 +300,6 @@ static inline int extract_fbdchan_indx(u32 x) return (x>>28) & 0x3; } -#ifdef CONFIG_EDAC_DEBUG -/* MTR NUMROW */ -static const char *numrow_toString[] = { - "8,192 - 13 rows", - "16,384 - 14 rows", - "32,768 - 15 rows", - "65,536 - 16 rows" -}; - -/* MTR NUMCOL */ -static const char *numcol_toString[] = { - "1,024 - 10 columns", - "2,048 - 11 columns", - "4,096 - 12 columns", - "reserved" -}; -#endif - /* Device name and register DID (Device ID) */ struct i5400_dev_info { const char *ctl_name; /* name for this device */ @@ -345,7 +327,13 @@ struct i5400_pvt { struct pci_dev *branch_1; /* 22.0 */ u16 tolm; /* top of low memory */ - u64 ambase; /* AMB BAR */ + union { + u64 ambase; /* AMB BAR */ + struct { + u32 ambase_bottom; + u32 ambase_top; + } u __packed; + }; u16 mir0, mir1; @@ -560,10 +548,9 @@ static void i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci, ras = nrec_ras(info); cas = nrec_cas(info); - debugf0("\t\tDIMM= %d Channels= %d,%d (Branch= %d " - "DRAM Bank= %d Buffer ID = %d rdwr= %s ras= %d cas= %d)\n", - rank, channel, channel + 1, branch >> 1, bank, - buf_id, rdwr_str(rdwr), ras, cas); + edac_dbg(0, "\t\tDIMM= %d Channels= %d,%d (Branch= %d DRAM Bank= %d Buffer ID = %d rdwr= %s ras= %d cas= %d)\n", + rank, channel, channel + 1, branch >> 1, bank, + buf_id, rdwr_str(rdwr), ras, cas); /* Only 1 bit will be on */ errnum = find_first_bit(&allErrors, ARRAY_SIZE(error_name)); @@ -573,10 +560,10 @@ static void i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci, "Bank=%d Buffer ID = %d RAS=%d CAS=%d Err=0x%lx (%s)", bank, buf_id, ras, cas, allErrors, error_name[errnum]); - edac_mc_handle_error(tp_event, mci, 0, 0, 0, + edac_mc_handle_error(tp_event, mci, 1, 0, 0, 0, branch >> 1, -1, rank, rdwr ? "Write error" : "Read error", - msg, NULL); + msg); } /* @@ -613,7 +600,7 @@ static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci, /* Correctable errors */ if (allErrors & ERROR_NF_CORRECTABLE) { - debugf0("\tCorrected bits= 0x%lx\n", allErrors); + edac_dbg(0, "\tCorrected bits= 0x%lx\n", allErrors); branch = extract_fbdchan_indx(info->ferr_nf_fbd); @@ -634,10 +621,9 @@ static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci, /* Only 1 bit will be on */ errnum = find_first_bit(&allErrors, ARRAY_SIZE(error_name)); - debugf0("\t\tDIMM= %d Channel= %d (Branch %d " - "DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n", - rank, channel, branch >> 1, bank, - rdwr_str(rdwr), ras, cas); + edac_dbg(0, "\t\tDIMM= %d Channel= %d (Branch %d DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n", + rank, channel, branch >> 1, bank, + rdwr_str(rdwr), ras, cas); /* Form out message */ snprintf(msg, sizeof(msg), @@ -646,10 +632,10 @@ static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci, branch >> 1, bank, rdwr_str(rdwr), ras, cas, allErrors, error_name[errnum]); - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, branch >> 1, channel % 2, rank, rdwr ? "Write error" : "Read error", - msg, NULL); + msg); return; } @@ -700,7 +686,7 @@ static void i5400_clear_error(struct mem_ctl_info *mci) static void i5400_check_error(struct mem_ctl_info *mci) { struct i5400_error_info info; - debugf4("MC%d: %s: %s()\n", mci->mc_idx, __FILE__, __func__); + edac_dbg(4, "MC%d\n", mci->mc_idx); i5400_get_error_info(mci, &info); i5400_process_error_info(mci, &info); } @@ -786,15 +772,16 @@ static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx) } pvt->fsb_error_regs = pdev; - debugf1("System Address, processor bus- PCI Bus ID: %s %x:%x\n", - pci_name(pvt->system_address), - pvt->system_address->vendor, pvt->system_address->device); - debugf1("Branchmap, control and errors - PCI Bus ID: %s %x:%x\n", - pci_name(pvt->branchmap_werrors), - pvt->branchmap_werrors->vendor, pvt->branchmap_werrors->device); - debugf1("FSB Error Regs - PCI Bus ID: %s %x:%x\n", - pci_name(pvt->fsb_error_regs), - pvt->fsb_error_regs->vendor, pvt->fsb_error_regs->device); + edac_dbg(1, "System Address, processor bus- PCI Bus ID: %s %x:%x\n", + pci_name(pvt->system_address), + pvt->system_address->vendor, pvt->system_address->device); + edac_dbg(1, "Branchmap, control and errors - PCI Bus ID: %s %x:%x\n", + pci_name(pvt->branchmap_werrors), + pvt->branchmap_werrors->vendor, + pvt->branchmap_werrors->device); + edac_dbg(1, "FSB Error Regs - PCI Bus ID: %s %x:%x\n", + pci_name(pvt->fsb_error_regs), + pvt->fsb_error_regs->vendor, pvt->fsb_error_regs->device); pvt->branch_0 = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_FBD0, NULL); @@ -882,8 +869,8 @@ static int determine_mtr(struct i5400_pvt *pvt, int dimm, int channel) n = dimm; if (n >= DIMMS_PER_CHANNEL) { - debugf0("ERROR: trying to access an invalid dimm: %d\n", - dimm); + edac_dbg(0, "ERROR: trying to access an invalid dimm: %d\n", + dimm); return 0; } @@ -903,20 +890,29 @@ static void decode_mtr(int slot_row, u16 mtr) ans = MTR_DIMMS_PRESENT(mtr); - debugf2("\tMTR%d=0x%x: DIMMs are %s\n", slot_row, mtr, - ans ? "Present" : "NOT Present"); + edac_dbg(2, "\tMTR%d=0x%x: DIMMs are %sPresent\n", + slot_row, mtr, ans ? "" : "NOT "); if (!ans) return; - debugf2("\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr)); - - debugf2("\t\tELECTRICAL THROTTLING is %s\n", - MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled"); - - debugf2("\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr)); - debugf2("\t\tNUMRANK: %s\n", MTR_DIMM_RANK(mtr) ? "double" : "single"); - debugf2("\t\tNUMROW: %s\n", numrow_toString[MTR_DIMM_ROWS(mtr)]); - debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]); + edac_dbg(2, "\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr)); + + edac_dbg(2, "\t\tELECTRICAL THROTTLING is %s\n", + MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled"); + + edac_dbg(2, "\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr)); + edac_dbg(2, "\t\tNUMRANK: %s\n", + MTR_DIMM_RANK(mtr) ? "double" : "single"); + edac_dbg(2, "\t\tNUMROW: %s\n", + MTR_DIMM_ROWS(mtr) == 0 ? "8,192 - 13 rows" : + MTR_DIMM_ROWS(mtr) == 1 ? "16,384 - 14 rows" : + MTR_DIMM_ROWS(mtr) == 2 ? "32,768 - 15 rows" : + "65,536 - 16 rows"); + edac_dbg(2, "\t\tNUMCOL: %s\n", + MTR_DIMM_COLS(mtr) == 0 ? "1,024 - 10 columns" : + MTR_DIMM_COLS(mtr) == 1 ? "2,048 - 11 columns" : + MTR_DIMM_COLS(mtr) == 2 ? "4,096 - 12 columns" : + "reserved"); } static void handle_channel(struct i5400_pvt *pvt, int dimm, int channel, @@ -989,7 +985,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt) "-------------------------------"); p += n; space -= n; - debugf2("%s\n", mem_buffer); + edac_dbg(2, "%s\n", mem_buffer); p = mem_buffer; space = PAGE_SIZE; } @@ -1004,7 +1000,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt) p += n; space -= n; } - debugf2("%s\n", mem_buffer); + edac_dbg(2, "%s\n", mem_buffer); p = mem_buffer; space = PAGE_SIZE; } @@ -1014,7 +1010,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt) "-------------------------------"); p += n; space -= n; - debugf2("%s\n", mem_buffer); + edac_dbg(2, "%s\n", mem_buffer); p = mem_buffer; space = PAGE_SIZE; @@ -1029,7 +1025,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt) } space -= n; - debugf2("%s\n", mem_buffer); + edac_dbg(2, "%s\n", mem_buffer); p = mem_buffer; space = PAGE_SIZE; @@ -1042,7 +1038,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt) } /* output the last message and free buffer */ - debugf2("%s\n", mem_buffer); + edac_dbg(2, "%s\n", mem_buffer); kfree(mem_buffer); } @@ -1065,25 +1061,25 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci) pvt = mci->pvt_info; pci_read_config_dword(pvt->system_address, AMBASE, - (u32 *) &pvt->ambase); + &pvt->u.ambase_bottom); pci_read_config_dword(pvt->system_address, AMBASE + sizeof(u32), - ((u32 *) &pvt->ambase) + sizeof(u32)); + &pvt->u.ambase_top); maxdimmperch = pvt->maxdimmperch; maxch = pvt->maxch; - debugf2("AMBASE= 0x%lx MAXCH= %d MAX-DIMM-Per-CH= %d\n", - (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch); + edac_dbg(2, "AMBASE= 0x%lx MAXCH= %d MAX-DIMM-Per-CH= %d\n", + (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch); /* Get the Branch Map regs */ pci_read_config_word(pvt->branchmap_werrors, TOLM, &pvt->tolm); pvt->tolm >>= 12; - debugf2("\nTOLM (number of 256M regions) =%u (0x%x)\n", pvt->tolm, - pvt->tolm); + edac_dbg(2, "\nTOLM (number of 256M regions) =%u (0x%x)\n", + pvt->tolm, pvt->tolm); actual_tolm = (u32) ((1000l * pvt->tolm) >> (30 - 28)); - debugf2("Actual TOLM byte addr=%u.%03u GB (0x%x)\n", - actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28); + edac_dbg(2, "Actual TOLM byte addr=%u.%03u GB (0x%x)\n", + actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28); pci_read_config_word(pvt->branchmap_werrors, MIR0, &pvt->mir0); pci_read_config_word(pvt->branchmap_werrors, MIR1, &pvt->mir1); @@ -1092,11 +1088,13 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci) limit = (pvt->mir0 >> 4) & 0x0fff; way0 = pvt->mir0 & 0x1; way1 = pvt->mir0 & 0x2; - debugf2("MIR0: limit= 0x%x WAY1= %u WAY0= %x\n", limit, way1, way0); + edac_dbg(2, "MIR0: limit= 0x%x WAY1= %u WAY0= %x\n", + limit, way1, way0); limit = (pvt->mir1 >> 4) & 0xfff; way0 = pvt->mir1 & 0x1; way1 = pvt->mir1 & 0x2; - debugf2("MIR1: limit= 0x%x WAY1= %u WAY0= %x\n", limit, way1, way0); + edac_dbg(2, "MIR1: limit= 0x%x WAY1= %u WAY0= %x\n", + limit, way1, way0); /* Get the set of MTR[0-3] regs by each branch */ for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++) { @@ -1106,8 +1104,8 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci) pci_read_config_word(pvt->branch_0, where, &pvt->b0_mtr[slot_row]); - debugf2("MTR%d where=0x%x B0 value=0x%x\n", slot_row, where, - pvt->b0_mtr[slot_row]); + edac_dbg(2, "MTR%d where=0x%x B0 value=0x%x\n", + slot_row, where, pvt->b0_mtr[slot_row]); if (pvt->maxch < CHANNELS_PER_BRANCH) { pvt->b1_mtr[slot_row] = 0; @@ -1117,22 +1115,22 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci) /* Branch 1 set of MTR registers */ pci_read_config_word(pvt->branch_1, where, &pvt->b1_mtr[slot_row]); - debugf2("MTR%d where=0x%x B1 value=0x%x\n", slot_row, where, - pvt->b1_mtr[slot_row]); + edac_dbg(2, "MTR%d where=0x%x B1 value=0x%x\n", + slot_row, where, pvt->b1_mtr[slot_row]); } /* Read and dump branch 0's MTRs */ - debugf2("\nMemory Technology Registers:\n"); - debugf2(" Branch 0:\n"); + edac_dbg(2, "Memory Technology Registers:\n"); + edac_dbg(2, " Branch 0:\n"); for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++) decode_mtr(slot_row, pvt->b0_mtr[slot_row]); pci_read_config_word(pvt->branch_0, AMBPRESENT_0, &pvt->b0_ambpresent0); - debugf2("\t\tAMB-Branch 0-present0 0x%x:\n", pvt->b0_ambpresent0); + edac_dbg(2, "\t\tAMB-Branch 0-present0 0x%x:\n", pvt->b0_ambpresent0); pci_read_config_word(pvt->branch_0, AMBPRESENT_1, &pvt->b0_ambpresent1); - debugf2("\t\tAMB-Branch 0-present1 0x%x:\n", pvt->b0_ambpresent1); + edac_dbg(2, "\t\tAMB-Branch 0-present1 0x%x:\n", pvt->b0_ambpresent1); /* Only if we have 2 branchs (4 channels) */ if (pvt->maxch < CHANNELS_PER_BRANCH) { @@ -1140,18 +1138,18 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci) pvt->b1_ambpresent1 = 0; } else { /* Read and dump branch 1's MTRs */ - debugf2(" Branch 1:\n"); + edac_dbg(2, " Branch 1:\n"); for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++) decode_mtr(slot_row, pvt->b1_mtr[slot_row]); pci_read_config_word(pvt->branch_1, AMBPRESENT_0, &pvt->b1_ambpresent0); - debugf2("\t\tAMB-Branch 1-present0 0x%x:\n", - pvt->b1_ambpresent0); + edac_dbg(2, "\t\tAMB-Branch 1-present0 0x%x:\n", + pvt->b1_ambpresent0); pci_read_config_word(pvt->branch_1, AMBPRESENT_1, &pvt->b1_ambpresent1); - debugf2("\t\tAMB-Branch 1-present1 0x%x:\n", - pvt->b1_ambpresent1); + edac_dbg(2, "\t\tAMB-Branch 1-present1 0x%x:\n", + pvt->b1_ambpresent1); } /* Go and determine the size of each DIMM and place in an @@ -1203,10 +1201,9 @@ static int i5400_init_dimms(struct mem_ctl_info *mci) size_mb = pvt->dimm_info[slot][channel].megabytes; - debugf2("%s: dimm%zd (branch %d channel %d slot %d): %d.%03d GB\n", - __func__, dimm - mci->dimms, - channel / 2, channel % 2, slot, - size_mb / 1000, size_mb % 1000); + edac_dbg(2, "dimm (branch %d channel %d slot %d): %d.%03d GB\n", + channel / 2, channel % 2, slot, + size_mb / 1000, size_mb % 1000); dimm->nr_pages = size_mb << 8; dimm->grain = 8; @@ -1227,7 +1224,7 @@ static int i5400_init_dimms(struct mem_ctl_info *mci) * With such single-DIMM mode, the SDCC algorithm degrades to SECDEC+. */ if (ndimms == 1) - mci->dimms[0].edac_mode = EDAC_SECDED; + mci->dimms[0]->edac_mode = EDAC_SECDED; return (ndimms == 0); } @@ -1270,10 +1267,9 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx) if (dev_idx >= ARRAY_SIZE(i5400_devs)) return -EINVAL; - debugf0("MC: %s: %s(), pdev bus %u dev=0x%x fn=0x%x\n", - __FILE__, __func__, - pdev->bus->number, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + edac_dbg(0, "MC: pdev bus %u dev=0x%x fn=0x%x\n", + pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); /* We only are looking for func 0 of the set */ if (PCI_FUNC(pdev->devfn) != 0) @@ -1297,9 +1293,9 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx) if (mci == NULL) return -ENOMEM; - debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci); + edac_dbg(0, "MC: mci = %p\n", mci); - mci->dev = &pdev->dev; /* record ptr to the generic device */ + mci->pdev = &pdev->dev; /* record ptr to the generic device */ pvt = mci->pvt_info; pvt->system_address = pdev; /* Record this device in our private */ @@ -1329,19 +1325,16 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx) /* initialize the MC control structure 'dimms' table * with the mapping and control information */ if (i5400_init_dimms(mci)) { - debugf0("MC: Setting mci->edac_cap to EDAC_FLAG_NONE\n" - " because i5400_init_dimms() returned nonzero " - "value\n"); + edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i5400_init_dimms() returned nonzero value\n"); mci->edac_cap = EDAC_FLAG_NONE; /* no dimms found */ } else { - debugf1("MC: Enable error reporting now\n"); + edac_dbg(1, "MC: Enable error reporting now\n"); i5400_enable_error_reporting(mci); } /* add this new MC control structure to EDAC's list of MCs */ if (edac_mc_add_mc(mci)) { - debugf0("MC: %s: %s(): failed edac_mc_add_mc()\n", - __FILE__, __func__); + edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); /* FIXME: perhaps some code should go here that disables error * reporting if we just enabled it */ @@ -1385,7 +1378,7 @@ static int __devinit i5400_init_one(struct pci_dev *pdev, { int rc; - debugf0("MC: %s: %s()\n", __FILE__, __func__); + edac_dbg(0, "MC:\n"); /* wake up device */ rc = pci_enable_device(pdev); @@ -1404,7 +1397,7 @@ static void __devexit i5400_remove_one(struct pci_dev *pdev) { struct mem_ctl_info *mci; - debugf0("%s: %s()\n", __FILE__, __func__); + edac_dbg(0, "\n"); if (i5400_pci) edac_pci_release_generic_ctl(i5400_pci); @@ -1450,7 +1443,7 @@ static int __init i5400_init(void) { int pci_rc; - debugf2("MC: %s: %s()\n", __FILE__, __func__); + edac_dbg(2, "MC:\n"); /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); @@ -1466,7 +1459,7 @@ static int __init i5400_init(void) */ static void __exit i5400_exit(void) { - debugf2("MC: %s: %s()\n", __FILE__, __func__); + edac_dbg(2, "MC:\n"); pci_unregister_driver(&i5400_driver); } diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c index 97c22fd650ee..a09d0667f72a 100644 --- a/drivers/edac/i7300_edac.c +++ b/drivers/edac/i7300_edac.c @@ -182,24 +182,6 @@ static const u16 mtr_regs[MAX_SLOTS] = { #define MTR_DIMM_COLS(mtr) ((mtr) & 0x3) #define MTR_DIMM_COLS_ADDR_BITS(mtr) (MTR_DIMM_COLS(mtr) + 10) -#ifdef CONFIG_EDAC_DEBUG -/* MTR NUMROW */ -static const char *numrow_toString[] = { - "8,192 - 13 rows", - "16,384 - 14 rows", - "32,768 - 15 rows", - "65,536 - 16 rows" -}; - -/* MTR NUMCOL */ -static const char *numcol_toString[] = { - "1,024 - 10 columns", - "2,048 - 11 columns", - "4,096 - 12 columns", - "reserved" -}; -#endif - /************************************************ * i7300 Register definitions for error detection ************************************************/ @@ -467,10 +449,10 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci) "Bank=%d RAS=%d CAS=%d Err=0x%lx (%s))", bank, ras, cas, errors, specific); - edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 0, 0, 0, + edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 1, 0, 0, 0, branch, -1, rank, is_wr ? "Write error" : "Read error", - pvt->tmp_prt_buffer, NULL); + pvt->tmp_prt_buffer); } @@ -513,11 +495,11 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci) "DRAM-Bank=%d RAS=%d CAS=%d, Err=0x%lx (%s))", bank, ras, cas, errors, specific); - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, syndrome, branch >> 1, channel % 2, rank, is_wr ? "Write error" : "Read error", - pvt->tmp_prt_buffer, NULL); + pvt->tmp_prt_buffer); } return; } @@ -614,9 +596,8 @@ static int decode_mtr(struct i7300_pvt *pvt, mtr = pvt->mtr[slot][branch]; ans = MTR_DIMMS_PRESENT(mtr) ? 1 : 0; - debugf2("\tMTR%d CH%d: DIMMs are %s (mtr)\n", - slot, channel, - ans ? "Present" : "NOT Present"); + edac_dbg(2, "\tMTR%d CH%d: DIMMs are %sPresent (mtr)\n", + slot, channel, ans ? "" : "NOT "); /* Determine if there is a DIMM present in this DIMM slot */ if (!ans) @@ -638,16 +619,25 @@ static int decode_mtr(struct i7300_pvt *pvt, dinfo->megabytes = 1 << addrBits; - debugf2("\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr)); - - debugf2("\t\tELECTRICAL THROTTLING is %s\n", - MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled"); - - debugf2("\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr)); - debugf2("\t\tNUMRANK: %s\n", MTR_DIMM_RANKS(mtr) ? "double" : "single"); - debugf2("\t\tNUMROW: %s\n", numrow_toString[MTR_DIMM_ROWS(mtr)]); - debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]); - debugf2("\t\tSIZE: %d MB\n", dinfo->megabytes); + edac_dbg(2, "\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr)); + + edac_dbg(2, "\t\tELECTRICAL THROTTLING is %s\n", + MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled"); + + edac_dbg(2, "\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr)); + edac_dbg(2, "\t\tNUMRANK: %s\n", + MTR_DIMM_RANKS(mtr) ? "double" : "single"); + edac_dbg(2, "\t\tNUMROW: %s\n", + MTR_DIMM_ROWS(mtr) == 0 ? "8,192 - 13 rows" : + MTR_DIMM_ROWS(mtr) == 1 ? "16,384 - 14 rows" : + MTR_DIMM_ROWS(mtr) == 2 ? "32,768 - 15 rows" : + "65,536 - 16 rows"); + edac_dbg(2, "\t\tNUMCOL: %s\n", + MTR_DIMM_COLS(mtr) == 0 ? "1,024 - 10 columns" : + MTR_DIMM_COLS(mtr) == 1 ? "2,048 - 11 columns" : + MTR_DIMM_COLS(mtr) == 2 ? "4,096 - 12 columns" : + "reserved"); + edac_dbg(2, "\t\tSIZE: %d MB\n", dinfo->megabytes); /* * The type of error detection actually depends of the @@ -663,9 +653,9 @@ static int decode_mtr(struct i7300_pvt *pvt, dimm->mtype = MEM_FB_DDR2; if (IS_SINGLE_MODE(pvt->mc_settings_a)) { dimm->edac_mode = EDAC_SECDED; - debugf2("\t\tECC code is 8-byte-over-32-byte SECDED+ code\n"); + edac_dbg(2, "\t\tECC code is 8-byte-over-32-byte SECDED+ code\n"); } else { - debugf2("\t\tECC code is on Lockstep mode\n"); + edac_dbg(2, "\t\tECC code is on Lockstep mode\n"); if (MTR_DRAM_WIDTH(mtr) == 8) dimm->edac_mode = EDAC_S8ECD8ED; else @@ -674,9 +664,9 @@ static int decode_mtr(struct i7300_pvt *pvt, /* ask what device type on this row */ if (MTR_DRAM_WIDTH(mtr) == 8) { - debugf2("\t\tScrub algorithm for x8 is on %s mode\n", - IS_SCRBALGO_ENHANCED(pvt->mc_settings) ? - "enhanced" : "normal"); + edac_dbg(2, "\t\tScrub algorithm for x8 is on %s mode\n", + IS_SCRBALGO_ENHANCED(pvt->mc_settings) ? + "enhanced" : "normal"); dimm->dtype = DEV_X8; } else @@ -710,14 +700,14 @@ static void print_dimm_size(struct i7300_pvt *pvt) p += n; space -= n; } - debugf2("%s\n", pvt->tmp_prt_buffer); + edac_dbg(2, "%s\n", pvt->tmp_prt_buffer); p = pvt->tmp_prt_buffer; space = PAGE_SIZE; n = snprintf(p, space, "-------------------------------" "------------------------------"); p += n; space -= n; - debugf2("%s\n", pvt->tmp_prt_buffer); + edac_dbg(2, "%s\n", pvt->tmp_prt_buffer); p = pvt->tmp_prt_buffer; space = PAGE_SIZE; @@ -733,7 +723,7 @@ static void print_dimm_size(struct i7300_pvt *pvt) space -= n; } - debugf2("%s\n", pvt->tmp_prt_buffer); + edac_dbg(2, "%s\n", pvt->tmp_prt_buffer); p = pvt->tmp_prt_buffer; space = PAGE_SIZE; } @@ -742,7 +732,7 @@ static void print_dimm_size(struct i7300_pvt *pvt) "------------------------------"); p += n; space -= n; - debugf2("%s\n", pvt->tmp_prt_buffer); + edac_dbg(2, "%s\n", pvt->tmp_prt_buffer); p = pvt->tmp_prt_buffer; space = PAGE_SIZE; #endif @@ -765,7 +755,7 @@ static int i7300_init_csrows(struct mem_ctl_info *mci) pvt = mci->pvt_info; - debugf2("Memory Technology Registers:\n"); + edac_dbg(2, "Memory Technology Registers:\n"); /* Get the AMB present registers for the four channels */ for (branch = 0; branch < MAX_BRANCHES; branch++) { @@ -774,15 +764,15 @@ static int i7300_init_csrows(struct mem_ctl_info *mci) pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch], AMBPRESENT_0, &pvt->ambpresent[channel]); - debugf2("\t\tAMB-present CH%d = 0x%x:\n", - channel, pvt->ambpresent[channel]); + edac_dbg(2, "\t\tAMB-present CH%d = 0x%x:\n", + channel, pvt->ambpresent[channel]); channel = to_channel(1, branch); pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch], AMBPRESENT_1, &pvt->ambpresent[channel]); - debugf2("\t\tAMB-present CH%d = 0x%x:\n", - channel, pvt->ambpresent[channel]); + edac_dbg(2, "\t\tAMB-present CH%d = 0x%x:\n", + channel, pvt->ambpresent[channel]); } /* Get the set of MTR[0-7] regs by each branch */ @@ -824,12 +814,11 @@ static int i7300_init_csrows(struct mem_ctl_info *mci) static void decode_mir(int mir_no, u16 mir[MAX_MIR]) { if (mir[mir_no] & 3) - debugf2("MIR%d: limit= 0x%x Branch(es) that participate:" - " %s %s\n", - mir_no, - (mir[mir_no] >> 4) & 0xfff, - (mir[mir_no] & 1) ? "B0" : "", - (mir[mir_no] & 2) ? "B1" : ""); + edac_dbg(2, "MIR%d: limit= 0x%x Branch(es) that participate: %s %s\n", + mir_no, + (mir[mir_no] >> 4) & 0xfff, + (mir[mir_no] & 1) ? "B0" : "", + (mir[mir_no] & 2) ? "B1" : ""); } /** @@ -849,17 +838,17 @@ static int i7300_get_mc_regs(struct mem_ctl_info *mci) pci_read_config_dword(pvt->pci_dev_16_0_fsb_ctlr, AMBASE, (u32 *) &pvt->ambase); - debugf2("AMBASE= 0x%lx\n", (long unsigned int)pvt->ambase); + edac_dbg(2, "AMBASE= 0x%lx\n", (long unsigned int)pvt->ambase); /* Get the Branch Map regs */ pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, TOLM, &pvt->tolm); pvt->tolm >>= 12; - debugf2("TOLM (number of 256M regions) =%u (0x%x)\n", pvt->tolm, - pvt->tolm); + edac_dbg(2, "TOLM (number of 256M regions) =%u (0x%x)\n", + pvt->tolm, pvt->tolm); actual_tolm = (u32) ((1000l * pvt->tolm) >> (30 - 28)); - debugf2("Actual TOLM byte addr=%u.%03u GB (0x%x)\n", - actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28); + edac_dbg(2, "Actual TOLM byte addr=%u.%03u GB (0x%x)\n", + actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28); /* Get memory controller settings */ pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, MC_SETTINGS, @@ -868,15 +857,15 @@ static int i7300_get_mc_regs(struct mem_ctl_info *mci) &pvt->mc_settings_a); if (IS_SINGLE_MODE(pvt->mc_settings_a)) - debugf0("Memory controller operating on single mode\n"); + edac_dbg(0, "Memory controller operating on single mode\n"); else - debugf0("Memory controller operating on %s mode\n", - IS_MIRRORED(pvt->mc_settings) ? "mirrored" : "non-mirrored"); + edac_dbg(0, "Memory controller operating on %smirrored mode\n", + IS_MIRRORED(pvt->mc_settings) ? "" : "non-"); - debugf0("Error detection is %s\n", - IS_ECC_ENABLED(pvt->mc_settings) ? "enabled" : "disabled"); - debugf0("Retry is %s\n", - IS_RETRY_ENABLED(pvt->mc_settings) ? "enabled" : "disabled"); + edac_dbg(0, "Error detection is %s\n", + IS_ECC_ENABLED(pvt->mc_settings) ? "enabled" : "disabled"); + edac_dbg(0, "Retry is %s\n", + IS_RETRY_ENABLED(pvt->mc_settings) ? "enabled" : "disabled"); /* Get Memory Interleave Range registers */ pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR0, @@ -970,18 +959,18 @@ static int __devinit i7300_get_devices(struct mem_ctl_info *mci) } } - debugf1("System Address, processor bus- PCI Bus ID: %s %x:%x\n", - pci_name(pvt->pci_dev_16_0_fsb_ctlr), - pvt->pci_dev_16_0_fsb_ctlr->vendor, - pvt->pci_dev_16_0_fsb_ctlr->device); - debugf1("Branchmap, control and errors - PCI Bus ID: %s %x:%x\n", - pci_name(pvt->pci_dev_16_1_fsb_addr_map), - pvt->pci_dev_16_1_fsb_addr_map->vendor, - pvt->pci_dev_16_1_fsb_addr_map->device); - debugf1("FSB Error Regs - PCI Bus ID: %s %x:%x\n", - pci_name(pvt->pci_dev_16_2_fsb_err_regs), - pvt->pci_dev_16_2_fsb_err_regs->vendor, - pvt->pci_dev_16_2_fsb_err_regs->device); + edac_dbg(1, "System Address, processor bus- PCI Bus ID: %s %x:%x\n", + pci_name(pvt->pci_dev_16_0_fsb_ctlr), + pvt->pci_dev_16_0_fsb_ctlr->vendor, + pvt->pci_dev_16_0_fsb_ctlr->device); + edac_dbg(1, "Branchmap, control and errors - PCI Bus ID: %s %x:%x\n", + pci_name(pvt->pci_dev_16_1_fsb_addr_map), + pvt->pci_dev_16_1_fsb_addr_map->vendor, + pvt->pci_dev_16_1_fsb_addr_map->device); + edac_dbg(1, "FSB Error Regs - PCI Bus ID: %s %x:%x\n", + pci_name(pvt->pci_dev_16_2_fsb_err_regs), + pvt->pci_dev_16_2_fsb_err_regs->vendor, + pvt->pci_dev_16_2_fsb_err_regs->device); pvt->pci_dev_2x_0_fbd_branch[0] = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7300_MCH_FB0, @@ -1032,10 +1021,9 @@ static int __devinit i7300_init_one(struct pci_dev *pdev, if (rc == -EIO) return rc; - debugf0("MC: " __FILE__ ": %s(), pdev bus %u dev=0x%x fn=0x%x\n", - __func__, - pdev->bus->number, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + edac_dbg(0, "MC: pdev bus %u dev=0x%x fn=0x%x\n", + pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); /* We only are looking for func 0 of the set */ if (PCI_FUNC(pdev->devfn) != 0) @@ -1055,9 +1043,9 @@ static int __devinit i7300_init_one(struct pci_dev *pdev, if (mci == NULL) return -ENOMEM; - debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); + edac_dbg(0, "MC: mci = %p\n", mci); - mci->dev = &pdev->dev; /* record ptr to the generic device */ + mci->pdev = &pdev->dev; /* record ptr to the generic device */ pvt = mci->pvt_info; pvt->pci_dev_16_0_fsb_ctlr = pdev; /* Record this device in our private */ @@ -1088,19 +1076,16 @@ static int __devinit i7300_init_one(struct pci_dev *pdev, /* initialize the MC control structure 'csrows' table * with the mapping and control information */ if (i7300_get_mc_regs(mci)) { - debugf0("MC: Setting mci->edac_cap to EDAC_FLAG_NONE\n" - " because i7300_init_csrows() returned nonzero " - "value\n"); + edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i7300_init_csrows() returned nonzero value\n"); mci->edac_cap = EDAC_FLAG_NONE; /* no csrows found */ } else { - debugf1("MC: Enable error reporting now\n"); + edac_dbg(1, "MC: Enable error reporting now\n"); i7300_enable_error_reporting(mci); } /* add this new MC control structure to EDAC's list of MCs */ if (edac_mc_add_mc(mci)) { - debugf0("MC: " __FILE__ - ": %s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); /* FIXME: perhaps some code should go here that disables error * reporting if we just enabled it */ @@ -1142,7 +1127,7 @@ static void __devexit i7300_remove_one(struct pci_dev *pdev) struct mem_ctl_info *mci; char *tmp; - debugf0(__FILE__ ": %s()\n", __func__); + edac_dbg(0, "\n"); if (i7300_pci) edac_pci_release_generic_ctl(i7300_pci); @@ -1189,7 +1174,7 @@ static int __init i7300_init(void) { int pci_rc; - debugf2("MC: " __FILE__ ": %s()\n", __func__); + edac_dbg(2, "\n"); /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); @@ -1204,7 +1189,7 @@ static int __init i7300_init(void) */ static void __exit i7300_exit(void) { - debugf2("MC: " __FILE__ ": %s()\n", __func__); + edac_dbg(2, "\n"); pci_unregister_driver(&i7300_driver); } diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index a499c7ed820a..3672101023bd 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -248,6 +248,8 @@ struct i7core_dev { }; struct i7core_pvt { + struct device *addrmatch_dev, *chancounts_dev; + struct pci_dev *pci_noncore; struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1]; struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1]; @@ -514,29 +516,28 @@ static int get_dimm_config(struct mem_ctl_info *mci) pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod); pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map); - debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n", - pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status, - pvt->info.max_dod, pvt->info.ch_map); + edac_dbg(0, "QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n", + pvt->i7core_dev->socket, pvt->info.mc_control, + pvt->info.mc_status, pvt->info.max_dod, pvt->info.ch_map); if (ECC_ENABLED(pvt)) { - debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4); + edac_dbg(0, "ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4); if (ECCx8(pvt)) mode = EDAC_S8ECD8ED; else mode = EDAC_S4ECD4ED; } else { - debugf0("ECC disabled\n"); + edac_dbg(0, "ECC disabled\n"); mode = EDAC_NONE; } /* FIXME: need to handle the error codes */ - debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked " - "x%x x 0x%x\n", - numdimms(pvt->info.max_dod), - numrank(pvt->info.max_dod >> 2), - numbank(pvt->info.max_dod >> 4), - numrow(pvt->info.max_dod >> 6), - numcol(pvt->info.max_dod >> 9)); + edac_dbg(0, "DOD Max limits: DIMMS: %d, %d-ranked, %d-banked x%x x 0x%x\n", + numdimms(pvt->info.max_dod), + numrank(pvt->info.max_dod >> 2), + numbank(pvt->info.max_dod >> 4), + numrow(pvt->info.max_dod >> 6), + numcol(pvt->info.max_dod >> 9)); for (i = 0; i < NUM_CHANS; i++) { u32 data, dimm_dod[3], value[8]; @@ -545,11 +546,11 @@ static int get_dimm_config(struct mem_ctl_info *mci) continue; if (!CH_ACTIVE(pvt, i)) { - debugf0("Channel %i is not active\n", i); + edac_dbg(0, "Channel %i is not active\n", i); continue; } if (CH_DISABLED(pvt, i)) { - debugf0("Channel %i is disabled\n", i); + edac_dbg(0, "Channel %i is disabled\n", i); continue; } @@ -580,15 +581,14 @@ static int get_dimm_config(struct mem_ctl_info *mci) pci_read_config_dword(pvt->pci_ch[i][1], MC_DOD_CH_DIMM2, &dimm_dod[2]); - debugf0("Ch%d phy rd%d, wr%d (0x%08x): " - "%s%s%s%cDIMMs\n", - i, - RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i), - data, - pvt->channel[i].is_3dimms_present ? "3DIMMS " : "", - pvt->channel[i].is_3dimms_present ? "SINGLE_4R " : "", - pvt->channel[i].has_4rank ? "HAS_4R " : "", - (data & REGISTERED_DIMM) ? 'R' : 'U'); + edac_dbg(0, "Ch%d phy rd%d, wr%d (0x%08x): %s%s%s%cDIMMs\n", + i, + RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i), + data, + pvt->channel[i].is_3dimms_present ? "3DIMMS " : "", + pvt->channel[i].is_3dimms_present ? "SINGLE_4R " : "", + pvt->channel[i].has_4rank ? "HAS_4R " : "", + (data & REGISTERED_DIMM) ? 'R' : 'U'); for (j = 0; j < 3; j++) { u32 banks, ranks, rows, cols; @@ -607,11 +607,10 @@ static int get_dimm_config(struct mem_ctl_info *mci) /* DDR3 has 8 I/O banks */ size = (rows * cols * banks * ranks) >> (20 - 3); - debugf0("\tdimm %d %d Mb offset: %x, " - "bank: %d, rank: %d, row: %#x, col: %#x\n", - j, size, - RANKOFFSET(dimm_dod[j]), - banks, ranks, rows, cols); + edac_dbg(0, "\tdimm %d %d Mb offset: %x, bank: %d, rank: %d, row: %#x, col: %#x\n", + j, size, + RANKOFFSET(dimm_dod[j]), + banks, ranks, rows, cols); npages = MiB_TO_PAGES(size); @@ -647,12 +646,12 @@ static int get_dimm_config(struct mem_ctl_info *mci) pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]); pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]); pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]); - debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i); + edac_dbg(1, "\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i); for (j = 0; j < 8; j++) - debugf1("\t\t%#x\t%#x\t%#x\n", - (value[j] >> 27) & 0x1, - (value[j] >> 24) & 0x7, - (value[j] & ((1 << 24) - 1))); + edac_dbg(1, "\t\t%#x\t%#x\t%#x\n", + (value[j] >> 27) & 0x1, + (value[j] >> 24) & 0x7, + (value[j] & ((1 << 24) - 1))); } return 0; @@ -662,6 +661,8 @@ static int get_dimm_config(struct mem_ctl_info *mci) Error insertion routines ****************************************************************************/ +#define to_mci(k) container_of(k, struct mem_ctl_info, dev) + /* The i7core has independent error injection features per channel. However, to have a simpler code, we don't allow enabling error injection on more than one channel. @@ -691,9 +692,11 @@ static int disable_inject(const struct mem_ctl_info *mci) * bit 0 - refers to the lower 32-byte half cacheline * bit 1 - refers to the upper 32-byte half cacheline */ -static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci, +static ssize_t i7core_inject_section_store(struct device *dev, + struct device_attribute *mattr, const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); struct i7core_pvt *pvt = mci->pvt_info; unsigned long value; int rc; @@ -709,9 +712,11 @@ static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci, return count; } -static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci, - char *data) +static ssize_t i7core_inject_section_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); struct i7core_pvt *pvt = mci->pvt_info; return sprintf(data, "0x%08x\n", pvt->inject.section); } @@ -724,10 +729,12 @@ static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci, * bit 1 - inject ECC error * bit 2 - inject parity error */ -static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci, +static ssize_t i7core_inject_type_store(struct device *dev, + struct device_attribute *mattr, const char *data, size_t count) { - struct i7core_pvt *pvt = mci->pvt_info; + struct mem_ctl_info *mci = to_mci(dev); +struct i7core_pvt *pvt = mci->pvt_info; unsigned long value; int rc; @@ -742,10 +749,13 @@ static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci, return count; } -static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci, - char *data) +static ssize_t i7core_inject_type_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); struct i7core_pvt *pvt = mci->pvt_info; + return sprintf(data, "0x%08x\n", pvt->inject.type); } @@ -759,9 +769,11 @@ static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci, * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an * uncorrectable error to be injected. */ -static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci, - const char *data, size_t count) +static ssize_t i7core_inject_eccmask_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); struct i7core_pvt *pvt = mci->pvt_info; unsigned long value; int rc; @@ -777,10 +789,13 @@ static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci, return count; } -static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci, - char *data) +static ssize_t i7core_inject_eccmask_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); struct i7core_pvt *pvt = mci->pvt_info; + return sprintf(data, "0x%08x\n", pvt->inject.eccmask); } @@ -797,14 +812,16 @@ static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci, #define DECLARE_ADDR_MATCH(param, limit) \ static ssize_t i7core_inject_store_##param( \ - struct mem_ctl_info *mci, \ - const char *data, size_t count) \ + struct device *dev, \ + struct device_attribute *mattr, \ + const char *data, size_t count) \ { \ + struct mem_ctl_info *mci = to_mci(dev); \ struct i7core_pvt *pvt; \ long value; \ int rc; \ \ - debugf1("%s()\n", __func__); \ + edac_dbg(1, "\n"); \ pvt = mci->pvt_info; \ \ if (pvt->inject.enable) \ @@ -824,13 +841,15 @@ static ssize_t i7core_inject_store_##param( \ } \ \ static ssize_t i7core_inject_show_##param( \ - struct mem_ctl_info *mci, \ - char *data) \ + struct device *dev, \ + struct device_attribute *mattr, \ + char *data) \ { \ + struct mem_ctl_info *mci = to_mci(dev); \ struct i7core_pvt *pvt; \ \ pvt = mci->pvt_info; \ - debugf1("%s() pvt=%p\n", __func__, pvt); \ + edac_dbg(1, "pvt=%p\n", pvt); \ if (pvt->inject.param < 0) \ return sprintf(data, "any\n"); \ else \ @@ -838,14 +857,9 @@ static ssize_t i7core_inject_show_##param( \ } #define ATTR_ADDR_MATCH(param) \ - { \ - .attr = { \ - .name = #param, \ - .mode = (S_IRUGO | S_IWUSR) \ - }, \ - .show = i7core_inject_show_##param, \ - .store = i7core_inject_store_##param, \ - } + static DEVICE_ATTR(param, S_IRUGO | S_IWUSR, \ + i7core_inject_show_##param, \ + i7core_inject_store_##param) DECLARE_ADDR_MATCH(channel, 3); DECLARE_ADDR_MATCH(dimm, 3); @@ -854,14 +868,21 @@ DECLARE_ADDR_MATCH(bank, 32); DECLARE_ADDR_MATCH(page, 0x10000); DECLARE_ADDR_MATCH(col, 0x4000); +ATTR_ADDR_MATCH(channel); +ATTR_ADDR_MATCH(dimm); +ATTR_ADDR_MATCH(rank); +ATTR_ADDR_MATCH(bank); +ATTR_ADDR_MATCH(page); +ATTR_ADDR_MATCH(col); + static int write_and_test(struct pci_dev *dev, const int where, const u32 val) { u32 read; int count; - debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n", - dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), - where, val); + edac_dbg(0, "setting pci %02x:%02x.%x reg=%02x value=%08x\n", + dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + where, val); for (count = 0; count < 10; count++) { if (count) @@ -899,9 +920,11 @@ static int write_and_test(struct pci_dev *dev, const int where, const u32 val) * is reliable enough to check if the MC is using the * three channels. However, this is not clear at the datasheet. */ -static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, - const char *data, size_t count) +static ssize_t i7core_inject_enable_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); struct i7core_pvt *pvt = mci->pvt_info; u32 injectmask; u64 mask = 0; @@ -994,17 +1017,18 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 8); - debugf0("Error inject addr match 0x%016llx, ecc 0x%08x," - " inject 0x%08x\n", - mask, pvt->inject.eccmask, injectmask); + edac_dbg(0, "Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n", + mask, pvt->inject.eccmask, injectmask); return count; } -static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, - char *data) +static ssize_t i7core_inject_enable_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); struct i7core_pvt *pvt = mci->pvt_info; u32 injectmask; @@ -1014,7 +1038,7 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ERROR_INJECT, &injectmask); - debugf0("Inject error read: 0x%018x\n", injectmask); + edac_dbg(0, "Inject error read: 0x%018x\n", injectmask); if (injectmask & 0x0c) pvt->inject.enable = 1; @@ -1024,12 +1048,14 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, #define DECLARE_COUNTER(param) \ static ssize_t i7core_show_counter_##param( \ - struct mem_ctl_info *mci, \ - char *data) \ + struct device *dev, \ + struct device_attribute *mattr, \ + char *data) \ { \ + struct mem_ctl_info *mci = to_mci(dev); \ struct i7core_pvt *pvt = mci->pvt_info; \ \ - debugf1("%s() \n", __func__); \ + edac_dbg(1, "\n"); \ if (!pvt->ce_count_available || (pvt->is_registered)) \ return sprintf(data, "data unavailable\n"); \ return sprintf(data, "%lu\n", \ @@ -1037,121 +1063,179 @@ static ssize_t i7core_show_counter_##param( \ } #define ATTR_COUNTER(param) \ - { \ - .attr = { \ - .name = __stringify(udimm##param), \ - .mode = (S_IRUGO | S_IWUSR) \ - }, \ - .show = i7core_show_counter_##param \ - } + static DEVICE_ATTR(udimm##param, S_IRUGO | S_IWUSR, \ + i7core_show_counter_##param, \ + NULL) DECLARE_COUNTER(0); DECLARE_COUNTER(1); DECLARE_COUNTER(2); +ATTR_COUNTER(0); +ATTR_COUNTER(1); +ATTR_COUNTER(2); + /* - * Sysfs struct + * inject_addrmatch device sysfs struct */ -static const struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = { - ATTR_ADDR_MATCH(channel), - ATTR_ADDR_MATCH(dimm), - ATTR_ADDR_MATCH(rank), - ATTR_ADDR_MATCH(bank), - ATTR_ADDR_MATCH(page), - ATTR_ADDR_MATCH(col), - { } /* End of list */ +static struct attribute *i7core_addrmatch_attrs[] = { + &dev_attr_channel.attr, + &dev_attr_dimm.attr, + &dev_attr_rank.attr, + &dev_attr_bank.attr, + &dev_attr_page.attr, + &dev_attr_col.attr, + NULL }; -static const struct mcidev_sysfs_group i7core_inject_addrmatch = { - .name = "inject_addrmatch", - .mcidev_attr = i7core_addrmatch_attrs, +static struct attribute_group addrmatch_grp = { + .attrs = i7core_addrmatch_attrs, }; -static const struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = { - ATTR_COUNTER(0), - ATTR_COUNTER(1), - ATTR_COUNTER(2), - { .attr = { .name = NULL } } +static const struct attribute_group *addrmatch_groups[] = { + &addrmatch_grp, + NULL }; -static const struct mcidev_sysfs_group i7core_udimm_counters = { - .name = "all_channel_counts", - .mcidev_attr = i7core_udimm_counters_attrs, +static void addrmatch_release(struct device *device) +{ + edac_dbg(1, "Releasing device %s\n", dev_name(device)); + kfree(device); +} + +static struct device_type addrmatch_type = { + .groups = addrmatch_groups, + .release = addrmatch_release, }; -static const struct mcidev_sysfs_attribute i7core_sysfs_rdimm_attrs[] = { - { - .attr = { - .name = "inject_section", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = i7core_inject_section_show, - .store = i7core_inject_section_store, - }, { - .attr = { - .name = "inject_type", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = i7core_inject_type_show, - .store = i7core_inject_type_store, - }, { - .attr = { - .name = "inject_eccmask", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = i7core_inject_eccmask_show, - .store = i7core_inject_eccmask_store, - }, { - .grp = &i7core_inject_addrmatch, - }, { - .attr = { - .name = "inject_enable", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = i7core_inject_enable_show, - .store = i7core_inject_enable_store, - }, - { } /* End of list */ +/* + * all_channel_counts sysfs struct + */ + +static struct attribute *i7core_udimm_counters_attrs[] = { + &dev_attr_udimm0.attr, + &dev_attr_udimm1.attr, + &dev_attr_udimm2.attr, + NULL }; -static const struct mcidev_sysfs_attribute i7core_sysfs_udimm_attrs[] = { - { - .attr = { - .name = "inject_section", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = i7core_inject_section_show, - .store = i7core_inject_section_store, - }, { - .attr = { - .name = "inject_type", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = i7core_inject_type_show, - .store = i7core_inject_type_store, - }, { - .attr = { - .name = "inject_eccmask", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = i7core_inject_eccmask_show, - .store = i7core_inject_eccmask_store, - }, { - .grp = &i7core_inject_addrmatch, - }, { - .attr = { - .name = "inject_enable", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = i7core_inject_enable_show, - .store = i7core_inject_enable_store, - }, { - .grp = &i7core_udimm_counters, - }, - { } /* End of list */ +static struct attribute_group all_channel_counts_grp = { + .attrs = i7core_udimm_counters_attrs, }; +static const struct attribute_group *all_channel_counts_groups[] = { + &all_channel_counts_grp, + NULL +}; + +static void all_channel_counts_release(struct device *device) +{ + edac_dbg(1, "Releasing device %s\n", dev_name(device)); + kfree(device); +} + +static struct device_type all_channel_counts_type = { + .groups = all_channel_counts_groups, + .release = all_channel_counts_release, +}; + +/* + * inject sysfs attributes + */ + +static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR, + i7core_inject_section_show, i7core_inject_section_store); + +static DEVICE_ATTR(inject_type, S_IRUGO | S_IWUSR, + i7core_inject_type_show, i7core_inject_type_store); + + +static DEVICE_ATTR(inject_eccmask, S_IRUGO | S_IWUSR, + i7core_inject_eccmask_show, i7core_inject_eccmask_store); + +static DEVICE_ATTR(inject_enable, S_IRUGO | S_IWUSR, + i7core_inject_enable_show, i7core_inject_enable_store); + +static int i7core_create_sysfs_devices(struct mem_ctl_info *mci) +{ + struct i7core_pvt *pvt = mci->pvt_info; + int rc; + + rc = device_create_file(&mci->dev, &dev_attr_inject_section); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_inject_type); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_inject_eccmask); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_inject_enable); + if (rc < 0) + return rc; + + pvt->addrmatch_dev = kzalloc(sizeof(*pvt->addrmatch_dev), GFP_KERNEL); + if (!pvt->addrmatch_dev) + return rc; + + pvt->addrmatch_dev->type = &addrmatch_type; + pvt->addrmatch_dev->bus = mci->dev.bus; + device_initialize(pvt->addrmatch_dev); + pvt->addrmatch_dev->parent = &mci->dev; + dev_set_name(pvt->addrmatch_dev, "inject_addrmatch"); + dev_set_drvdata(pvt->addrmatch_dev, mci); + + edac_dbg(1, "creating %s\n", dev_name(pvt->addrmatch_dev)); + + rc = device_add(pvt->addrmatch_dev); + if (rc < 0) + return rc; + + if (!pvt->is_registered) { + pvt->chancounts_dev = kzalloc(sizeof(*pvt->chancounts_dev), + GFP_KERNEL); + if (!pvt->chancounts_dev) { + put_device(pvt->addrmatch_dev); + device_del(pvt->addrmatch_dev); + return rc; + } + + pvt->chancounts_dev->type = &all_channel_counts_type; + pvt->chancounts_dev->bus = mci->dev.bus; + device_initialize(pvt->chancounts_dev); + pvt->chancounts_dev->parent = &mci->dev; + dev_set_name(pvt->chancounts_dev, "all_channel_counts"); + dev_set_drvdata(pvt->chancounts_dev, mci); + + edac_dbg(1, "creating %s\n", dev_name(pvt->chancounts_dev)); + + rc = device_add(pvt->chancounts_dev); + if (rc < 0) + return rc; + } + return 0; +} + +static void i7core_delete_sysfs_devices(struct mem_ctl_info *mci) +{ + struct i7core_pvt *pvt = mci->pvt_info; + + edac_dbg(1, "\n"); + + device_remove_file(&mci->dev, &dev_attr_inject_section); + device_remove_file(&mci->dev, &dev_attr_inject_type); + device_remove_file(&mci->dev, &dev_attr_inject_eccmask); + device_remove_file(&mci->dev, &dev_attr_inject_enable); + + if (!pvt->is_registered) { + put_device(pvt->chancounts_dev); + device_del(pvt->chancounts_dev); + } + put_device(pvt->addrmatch_dev); + device_del(pvt->addrmatch_dev); +} + /**************************************************************************** Device initialization routines: put/get, init/exit ****************************************************************************/ @@ -1164,14 +1248,14 @@ static void i7core_put_devices(struct i7core_dev *i7core_dev) { int i; - debugf0(__FILE__ ": %s()\n", __func__); + edac_dbg(0, "\n"); for (i = 0; i < i7core_dev->n_devs; i++) { struct pci_dev *pdev = i7core_dev->pdev[i]; if (!pdev) continue; - debugf0("Removing dev %02x:%02x.%d\n", - pdev->bus->number, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + edac_dbg(0, "Removing dev %02x:%02x.%d\n", + pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); pci_dev_put(pdev); } } @@ -1214,12 +1298,12 @@ static unsigned i7core_pci_lastbus(void) while ((b = pci_find_next_bus(b)) != NULL) { bus = b->number; - debugf0("Found bus %d\n", bus); + edac_dbg(0, "Found bus %d\n", bus); if (bus > last_bus) last_bus = bus; } - debugf0("Last bus %d\n", last_bus); + edac_dbg(0, "Last bus %d\n", last_bus); return last_bus; } @@ -1326,10 +1410,10 @@ static int i7core_get_onedevice(struct pci_dev **prev, return -ENODEV; } - debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n", - socket, bus, dev_descr->dev, - dev_descr->func, - PCI_VENDOR_ID_INTEL, dev_descr->dev_id); + edac_dbg(0, "Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n", + socket, bus, dev_descr->dev, + dev_descr->func, + PCI_VENDOR_ID_INTEL, dev_descr->dev_id); /* * As stated on drivers/pci/search.c, the reference count for @@ -1427,13 +1511,13 @@ static int mci_bind_devs(struct mem_ctl_info *mci, family = "unknown"; pvt->enable_scrub = false; } - debugf0("Detected a processor type %s\n", family); + edac_dbg(0, "Detected a processor type %s\n", family); } else goto error; - debugf0("Associated fn %d.%d, dev = %p, socket %d\n", - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), - pdev, i7core_dev->socket); + edac_dbg(0, "Associated fn %d.%d, dev = %p, socket %d\n", + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + pdev, i7core_dev->socket); if (PCI_SLOT(pdev->devfn) == 3 && PCI_FUNC(pdev->devfn) == 2) @@ -1452,18 +1536,6 @@ error: /**************************************************************************** Error check routines ****************************************************************************/ -static void i7core_rdimm_update_errcount(struct mem_ctl_info *mci, - const int chan, - const int dimm, - const int add) -{ - int i; - - for (i = 0; i < add; i++) { - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0, - chan, dimm, -1, "error", "", NULL); - } -} static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci, const int chan, @@ -1502,12 +1574,17 @@ static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci, /*updated the edac core */ if (add0 != 0) - i7core_rdimm_update_errcount(mci, chan, 0, add0); + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add0, + 0, 0, 0, + chan, 0, -1, "error", ""); if (add1 != 0) - i7core_rdimm_update_errcount(mci, chan, 1, add1); + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add1, + 0, 0, 0, + chan, 1, -1, "error", ""); if (add2 != 0) - i7core_rdimm_update_errcount(mci, chan, 2, add2); - + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add2, + 0, 0, 0, + chan, 2, -1, "error", ""); } static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci) @@ -1530,8 +1607,8 @@ static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci) pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5, &rcv[2][1]); for (i = 0 ; i < 3; i++) { - debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n", - (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]); + edac_dbg(3, "MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n", + (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]); /*if the channel has 3 dimms*/ if (pvt->channel[i].dimms > 2) { new0 = DIMM_BOT_COR_ERR(rcv[i][0]); @@ -1562,7 +1639,7 @@ static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci) int new0, new1, new2; if (!pvt->pci_mcr[4]) { - debugf0("%s MCR registers not found\n", __func__); + edac_dbg(0, "MCR registers not found\n"); return; } @@ -1626,7 +1703,7 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, const struct mce *m) { struct i7core_pvt *pvt = mci->pvt_info; - char *type, *optype, *err, msg[80]; + char *type, *optype, *err; enum hw_event_mc_err_type tp_event; unsigned long error = m->status & 0x1ff0000l; bool uncorrected_error = m->mcgstatus & 1ll << 61; @@ -1704,20 +1781,18 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, err = "unknown"; } - snprintf(msg, sizeof(msg), "count=%d %s", core_err_cnt, optype); - /* * Call the helper to output message * FIXME: what to do if core_err_cnt > 1? Currently, it generates * only one event */ if (uncorrected_error || !pvt->is_registered) - edac_mc_handle_error(tp_event, mci, + edac_mc_handle_error(tp_event, mci, core_err_cnt, m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, syndrome, channel, dimm, -1, - err, msg, m); + err, optype); } /* @@ -2094,8 +2169,7 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev) struct i7core_pvt *pvt; if (unlikely(!mci || !mci->pvt_info)) { - debugf0("MC: " __FILE__ ": %s(): dev = %p\n", - __func__, &i7core_dev->pdev[0]->dev); + edac_dbg(0, "MC: dev = %p\n", &i7core_dev->pdev[0]->dev); i7core_printk(KERN_ERR, "Couldn't find mci handler\n"); return; @@ -2103,8 +2177,7 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev) pvt = mci->pvt_info; - debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n", - __func__, mci, &i7core_dev->pdev[0]->dev); + edac_dbg(0, "MC: mci = %p, dev = %p\n", mci, &i7core_dev->pdev[0]->dev); /* Disable scrubrate setting */ if (pvt->enable_scrub) @@ -2114,9 +2187,10 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev) i7core_pci_ctl_release(pvt); /* Remove MC sysfs nodes */ - edac_mc_del_mc(mci->dev); + i7core_delete_sysfs_devices(mci); + edac_mc_del_mc(mci->pdev); - debugf1("%s: free mci struct\n", mci->ctl_name); + edac_dbg(1, "%s: free mci struct\n", mci->ctl_name); kfree(mci->ctl_name); edac_mc_free(mci); i7core_dev->mci = NULL; @@ -2142,8 +2216,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev) if (unlikely(!mci)) return -ENOMEM; - debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n", - __func__, mci, &i7core_dev->pdev[0]->dev); + edac_dbg(0, "MC: mci = %p, dev = %p\n", mci, &i7core_dev->pdev[0]->dev); pvt = mci->pvt_info; memset(pvt, 0, sizeof(*pvt)); @@ -2172,15 +2245,11 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev) if (unlikely(rc < 0)) goto fail0; - if (pvt->is_registered) - mci->mc_driver_sysfs_attributes = i7core_sysfs_rdimm_attrs; - else - mci->mc_driver_sysfs_attributes = i7core_sysfs_udimm_attrs; /* Get dimm basic config */ get_dimm_config(mci); /* record ptr to the generic device */ - mci->dev = &i7core_dev->pdev[0]->dev; + mci->pdev = &i7core_dev->pdev[0]->dev; /* Set the function pointer to an actual operation function */ mci->edac_check = i7core_check_error; @@ -2190,8 +2259,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev) /* add this new MC control structure to EDAC's list of MCs */ if (unlikely(edac_mc_add_mc(mci))) { - debugf0("MC: " __FILE__ - ": %s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); /* FIXME: perhaps some code should go here that disables error * reporting if we just enabled it */ @@ -2199,6 +2267,12 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev) rc = -EINVAL; goto fail0; } + if (i7core_create_sysfs_devices(mci)) { + edac_dbg(0, "MC: failed to create sysfs nodes\n"); + edac_mc_del_mc(mci->pdev); + rc = -EINVAL; + goto fail0; + } /* Default error mask is any memory */ pvt->inject.channel = 0; @@ -2298,7 +2372,7 @@ static void __devexit i7core_remove(struct pci_dev *pdev) { struct i7core_dev *i7core_dev; - debugf0(__FILE__ ": %s()\n", __func__); + edac_dbg(0, "\n"); /* * we have a trouble here: pdev value for removal will be wrong, since @@ -2347,7 +2421,7 @@ static int __init i7core_init(void) { int pci_rc; - debugf2("MC: " __FILE__ ": %s()\n", __func__); + edac_dbg(2, "\n"); /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); @@ -2374,7 +2448,7 @@ static int __init i7core_init(void) */ static void __exit i7core_exit(void) { - debugf2("MC: " __FILE__ ": %s()\n", __func__); + edac_dbg(2, "\n"); pci_unregister_driver(&i7core_driver); mce_unregister_decode_chain(&i7_mce_dec); } diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c index 52072c28a8a6..90f303db5d1d 100644 --- a/drivers/edac/i82443bxgx_edac.c +++ b/drivers/edac/i82443bxgx_edac.c @@ -124,7 +124,7 @@ static void i82443bxgx_edacmc_get_error_info(struct mem_ctl_info *mci, *info) { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); pci_read_config_dword(pdev, I82443BXGX_EAP, &info->eap); if (info->eap & I82443BXGX_EAP_OFFSET_SBE) /* Clear error to allow next error to be reported [p.61] */ @@ -156,19 +156,19 @@ static int i82443bxgx_edacmc_process_error_info(struct mem_ctl_info *mci, if (info->eap & I82443BXGX_EAP_OFFSET_SBE) { error_found = 1; if (handle_errors) - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, page, pageoffset, 0, edac_mc_find_csrow_by_page(mci, page), - 0, -1, mci->ctl_name, "", NULL); + 0, -1, mci->ctl_name, ""); } if (info->eap & I82443BXGX_EAP_OFFSET_MBE) { error_found = 1; if (handle_errors) - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, page, pageoffset, 0, edac_mc_find_csrow_by_page(mci, page), - 0, -1, mci->ctl_name, "", NULL); + 0, -1, mci->ctl_name, ""); } return error_found; @@ -178,7 +178,7 @@ static void i82443bxgx_edacmc_check(struct mem_ctl_info *mci) { struct i82443bxgx_edacmc_error_info info; - debugf1("MC%d: %s: %s()\n", mci->mc_idx, __FILE__, __func__); + edac_dbg(1, "MC%d\n", mci->mc_idx); i82443bxgx_edacmc_get_error_info(mci, &info); i82443bxgx_edacmc_process_error_info(mci, &info, 1); } @@ -197,18 +197,17 @@ static void i82443bxgx_init_csrows(struct mem_ctl_info *mci, pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc); row_high_limit_last = 0; for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; pci_read_config_byte(pdev, I82443BXGX_DRB + index, &drbar); - debugf1("MC%d: %s: %s() Row=%d DRB = %#0x\n", - mci->mc_idx, __FILE__, __func__, index, drbar); + edac_dbg(1, "MC%d: Row=%d DRB = %#0x\n", + mci->mc_idx, index, drbar); row_high_limit = ((u32) drbar << 23); /* find the DRAM Chip Select Base address and mask */ - debugf1("MC%d: %s: %s() Row=%d, " - "Boundary Address=%#0x, Last = %#0x\n", - mci->mc_idx, __FILE__, __func__, index, row_high_limit, - row_high_limit_last); + edac_dbg(1, "MC%d: Row=%d, Boundary Address=%#0x, Last = %#0x\n", + mci->mc_idx, index, row_high_limit, + row_high_limit_last); /* 440GX goes to 2GB, represented with a DRB of 0. */ if (row_high_limit_last && !row_high_limit) @@ -241,7 +240,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx) enum mem_type mtype; enum edac_type edac_mode; - debugf0("MC: %s: %s()\n", __FILE__, __func__); + edac_dbg(0, "MC:\n"); /* Something is really hosed if PCI config space reads from * the MC aren't working. @@ -259,8 +258,8 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx) if (mci == NULL) return -ENOMEM; - debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci); - mci->dev = &pdev->dev; + edac_dbg(0, "MC: mci = %p\n", mci); + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_EDO | MEM_FLAG_SDR | MEM_FLAG_RDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc); @@ -275,8 +274,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx) mtype = MEM_RDR; break; default: - debugf0("Unknown/reserved DRAM type value " - "in DRAMC register!\n"); + edac_dbg(0, "Unknown/reserved DRAM type value in DRAMC register!\n"); mtype = -MEM_UNKNOWN; } @@ -305,8 +303,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx) edac_mode = EDAC_SECDED; break; default: - debugf0("%s(): Unknown/reserved ECC state " - "in NBXCFG register!\n", __func__); + edac_dbg(0, "Unknown/reserved ECC state in NBXCFG register!\n"); edac_mode = EDAC_UNKNOWN; break; } @@ -330,7 +327,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx) mci->ctl_page_to_phys = NULL; if (edac_mc_add_mc(mci)) { - debugf3("%s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(3, "failed edac_mc_add_mc()\n"); goto fail; } @@ -345,7 +342,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx) __func__); } - debugf3("MC: %s: %s(): success\n", __FILE__, __func__); + edac_dbg(3, "MC: success\n"); return 0; fail: @@ -361,7 +358,7 @@ static int __devinit i82443bxgx_edacmc_init_one(struct pci_dev *pdev, { int rc; - debugf0("MC: %s: %s()\n", __FILE__, __func__); + edac_dbg(0, "MC:\n"); /* don't need to call pci_enable_device() */ rc = i82443bxgx_edacmc_probe1(pdev, ent->driver_data); @@ -376,7 +373,7 @@ static void __devexit i82443bxgx_edacmc_remove_one(struct pci_dev *pdev) { struct mem_ctl_info *mci; - debugf0("%s: %s()\n", __FILE__, __func__); + edac_dbg(0, "\n"); if (i82443bxgx_pci) edac_pci_release_generic_ctl(i82443bxgx_pci); @@ -428,7 +425,7 @@ static int __init i82443bxgx_edacmc_init(void) id = &i82443bxgx_pci_tbl[i]; } if (!mci_pdev) { - debugf0("i82443bxgx pci_get_device fail\n"); + edac_dbg(0, "i82443bxgx pci_get_device fail\n"); pci_rc = -ENODEV; goto fail1; } @@ -436,7 +433,7 @@ static int __init i82443bxgx_edacmc_init(void) pci_rc = i82443bxgx_edacmc_init_one(mci_pdev, i82443bxgx_pci_tbl); if (pci_rc < 0) { - debugf0("i82443bxgx init fail\n"); + edac_dbg(0, "i82443bxgx init fail\n"); pci_rc = -ENODEV; goto fail1; } diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c index 08045059d10b..1faa74971513 100644 --- a/drivers/edac/i82860_edac.c +++ b/drivers/edac/i82860_edac.c @@ -67,7 +67,7 @@ static void i82860_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -109,25 +109,25 @@ static int i82860_process_error_info(struct mem_ctl_info *mci, return 1; if ((info->errsts ^ info->errsts2) & 0x0003) { - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, - -1, -1, -1, "UE overwrote CE", "", NULL); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, + -1, -1, -1, "UE overwrote CE", ""); info->errsts = info->errsts2; } info->eap >>= PAGE_SHIFT; row = edac_mc_find_csrow_by_page(mci, info->eap); - dimm = mci->csrows[row].channels[0].dimm; + dimm = mci->csrows[row]->channels[0]->dimm; if (info->errsts & 0x0002) - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, info->eap, 0, 0, dimm->location[0], dimm->location[1], -1, - "i82860 UE", "", NULL); + "i82860 UE", ""); else - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, info->eap, 0, info->derrsyn, dimm->location[0], dimm->location[1], -1, - "i82860 CE", "", NULL); + "i82860 CE", ""); return 1; } @@ -136,7 +136,7 @@ static void i82860_check(struct mem_ctl_info *mci) { struct i82860_error_info info; - debugf1("MC%d: %s()\n", mci->mc_idx, __func__); + edac_dbg(1, "MC%d\n", mci->mc_idx); i82860_get_error_info(mci, &info); i82860_process_error_info(mci, &info, 1); } @@ -161,14 +161,13 @@ static void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev) * in all eight rows. */ for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; pci_read_config_word(pdev, I82860_GBA + index * 2, &value); cumul_size = (value & I82860_GBA_MASK) << (I82860_GBA_SHIFT - PAGE_SHIFT); - debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index, - cumul_size); + edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size); if (cumul_size == last_cumul_size) continue; /* not populated */ @@ -210,8 +209,8 @@ static int i82860_probe1(struct pci_dev *pdev, int dev_idx) if (!mci) return -ENOMEM; - debugf3("%s(): init mci\n", __func__); - mci->dev = &pdev->dev; + edac_dbg(3, "init mci\n"); + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; /* I"m not sure about this but I think that all RDRAM is SECDED */ @@ -229,7 +228,7 @@ static int i82860_probe1(struct pci_dev *pdev, int dev_idx) * type of memory controller. The ID is therefore hardcoded to 0. */ if (edac_mc_add_mc(mci)) { - debugf3("%s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(3, "failed edac_mc_add_mc()\n"); goto fail; } @@ -245,7 +244,7 @@ static int i82860_probe1(struct pci_dev *pdev, int dev_idx) } /* get this far and it's successful */ - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); return 0; @@ -260,7 +259,7 @@ static int __devinit i82860_init_one(struct pci_dev *pdev, { int rc; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); i82860_printk(KERN_INFO, "i82860 init one\n"); if (pci_enable_device(pdev) < 0) @@ -278,7 +277,7 @@ static void __devexit i82860_remove_one(struct pci_dev *pdev) { struct mem_ctl_info *mci; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); if (i82860_pci) edac_pci_release_generic_ctl(i82860_pci); @@ -311,7 +310,7 @@ static int __init i82860_init(void) { int pci_rc; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); @@ -324,7 +323,7 @@ static int __init i82860_init(void) PCI_DEVICE_ID_INTEL_82860_0, NULL); if (mci_pdev == NULL) { - debugf0("860 pci_get_device fail\n"); + edac_dbg(0, "860 pci_get_device fail\n"); pci_rc = -ENODEV; goto fail1; } @@ -332,7 +331,7 @@ static int __init i82860_init(void) pci_rc = i82860_init_one(mci_pdev, i82860_pci_tbl); if (pci_rc < 0) { - debugf0("860 init fail\n"); + edac_dbg(0, "860 init fail\n"); pci_rc = -ENODEV; goto fail1; } @@ -352,7 +351,7 @@ fail0: static void __exit i82860_exit(void) { - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); pci_unregister_driver(&i82860_driver); diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index b613e31c16e5..3e416b1a6b53 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -189,7 +189,7 @@ static void i82875p_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -227,7 +227,7 @@ static int i82875p_process_error_info(struct mem_ctl_info *mci, { int row, multi_chan; - multi_chan = mci->csrows[0].nr_channels - 1; + multi_chan = mci->csrows[0]->nr_channels - 1; if (!(info->errsts & 0x0081)) return 0; @@ -236,9 +236,9 @@ static int i82875p_process_error_info(struct mem_ctl_info *mci, return 1; if ((info->errsts ^ info->errsts2) & 0x0081) { - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1, - "UE overwrote CE", "", NULL); + "UE overwrote CE", ""); info->errsts = info->errsts2; } @@ -246,15 +246,15 @@ static int i82875p_process_error_info(struct mem_ctl_info *mci, row = edac_mc_find_csrow_by_page(mci, info->eap); if (info->errsts & 0x0080) - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, info->eap, 0, 0, row, -1, -1, - "i82875p UE", "", NULL); + "i82875p UE", ""); else - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, info->eap, 0, info->derrsyn, row, multi_chan ? (info->des & 0x1) : 0, - -1, "i82875p CE", "", NULL); + -1, "i82875p CE", ""); return 1; } @@ -263,7 +263,7 @@ static void i82875p_check(struct mem_ctl_info *mci) { struct i82875p_error_info info; - debugf1("MC%d: %s()\n", mci->mc_idx, __func__); + edac_dbg(1, "MC%d\n", mci->mc_idx); i82875p_get_error_info(mci, &info); i82875p_process_error_info(mci, &info, 1); } @@ -367,12 +367,11 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci, */ for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; + csrow = mci->csrows[index]; value = readb(ovrfl_window + I82875P_DRB + index); cumul_size = value << (I82875P_DRB_SHIFT - PAGE_SHIFT); - debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index, - cumul_size); + edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size); if (cumul_size == last_cumul_size) continue; /* not populated */ @@ -382,7 +381,7 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci, last_cumul_size = cumul_size; for (j = 0; j < nr_chans; j++) { - dimm = csrow->channels[j].dimm; + dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / nr_chans; dimm->grain = 1 << 12; /* I82875P_EAP has 4KiB reolution */ @@ -405,7 +404,7 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) u32 nr_chans; struct i82875p_error_info discard; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); ovrfl_pdev = pci_get_device(PCI_VEND_DEV(INTEL, 82875_6), NULL); @@ -426,11 +425,8 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) goto fail0; } - /* Keeps mci available after edac_mc_del_mc() till edac_mc_free() */ - kobject_get(&mci->edac_mci_kobj); - - debugf3("%s(): init mci\n", __func__); - mci->dev = &pdev->dev; + edac_dbg(3, "init mci\n"); + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; mci->edac_cap = EDAC_FLAG_UNKNOWN; @@ -440,7 +436,7 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) mci->dev_name = pci_name(pdev); mci->edac_check = i82875p_check; mci->ctl_page_to_phys = NULL; - debugf3("%s(): init pvt\n", __func__); + edac_dbg(3, "init pvt\n"); pvt = (struct i82875p_pvt *)mci->pvt_info; pvt->ovrfl_pdev = ovrfl_pdev; pvt->ovrfl_window = ovrfl_window; @@ -451,7 +447,7 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) * type of memory controller. The ID is therefore hardcoded to 0. */ if (edac_mc_add_mc(mci)) { - debugf3("%s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(3, "failed edac_mc_add_mc()\n"); goto fail1; } @@ -467,11 +463,10 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) } /* get this far and it's successful */ - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); return 0; fail1: - kobject_put(&mci->edac_mci_kobj); edac_mc_free(mci); fail0: @@ -489,7 +484,7 @@ static int __devinit i82875p_init_one(struct pci_dev *pdev, { int rc; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); i82875p_printk(KERN_INFO, "i82875p init one\n"); if (pci_enable_device(pdev) < 0) @@ -508,7 +503,7 @@ static void __devexit i82875p_remove_one(struct pci_dev *pdev) struct mem_ctl_info *mci; struct i82875p_pvt *pvt = NULL; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); if (i82875p_pci) edac_pci_release_generic_ctl(i82875p_pci); @@ -554,7 +549,7 @@ static int __init i82875p_init(void) { int pci_rc; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); @@ -569,7 +564,7 @@ static int __init i82875p_init(void) PCI_DEVICE_ID_INTEL_82875_0, NULL); if (!mci_pdev) { - debugf0("875p pci_get_device fail\n"); + edac_dbg(0, "875p pci_get_device fail\n"); pci_rc = -ENODEV; goto fail1; } @@ -577,7 +572,7 @@ static int __init i82875p_init(void) pci_rc = i82875p_init_one(mci_pdev, i82875p_pci_tbl); if (pci_rc < 0) { - debugf0("875p init fail\n"); + edac_dbg(0, "875p init fail\n"); pci_rc = -ENODEV; goto fail1; } @@ -597,7 +592,7 @@ fail0: static void __exit i82875p_exit(void) { - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); i82875p_remove_one(mci_pdev); pci_dev_put(mci_pdev); diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c index 433332c7cdba..069e26c11c4f 100644 --- a/drivers/edac/i82975x_edac.c +++ b/drivers/edac/i82975x_edac.c @@ -241,7 +241,7 @@ static void i82975x_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -288,8 +288,8 @@ static int i82975x_process_error_info(struct mem_ctl_info *mci, return 1; if ((info->errsts ^ info->errsts2) & 0x0003) { - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, - -1, -1, -1, "UE overwrote CE", "", NULL); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, + -1, -1, -1, "UE overwrote CE", ""); info->errsts = info->errsts2; } @@ -308,21 +308,21 @@ static int i82975x_process_error_info(struct mem_ctl_info *mci, (info->xeap & 1) ? 1 : 0, info->eap, (unsigned int) page); return 0; } - chan = (mci->csrows[row].nr_channels == 1) ? 0 : info->eap & 1; + chan = (mci->csrows[row]->nr_channels == 1) ? 0 : info->eap & 1; offst = info->eap & ((1 << PAGE_SHIFT) - - (1 << mci->csrows[row].channels[chan].dimm->grain)); + (1 << mci->csrows[row]->channels[chan]->dimm->grain)); if (info->errsts & 0x0002) - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, page, offst, 0, row, -1, -1, - "i82975x UE", "", NULL); + "i82975x UE", ""); else - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, page, offst, info->derrsyn, row, chan ? chan : 0, -1, - "i82975x CE", "", NULL); + "i82975x CE", ""); return 1; } @@ -331,7 +331,7 @@ static void i82975x_check(struct mem_ctl_info *mci) { struct i82975x_error_info info; - debugf1("MC%d: %s()\n", mci->mc_idx, __func__); + edac_dbg(1, "MC%d\n", mci->mc_idx); i82975x_get_error_info(mci, &info); i82975x_process_error_info(mci, &info, 1); } @@ -394,7 +394,7 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci, */ for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; + csrow = mci->csrows[index]; value = readb(mch_window + I82975X_DRB + index + ((index >= 4) ? 0x80 : 0)); @@ -406,8 +406,7 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci, */ if (csrow->nr_channels > 1) cumul_size <<= 1; - debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index, - cumul_size); + edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size); nr_pages = cumul_size - last_cumul_size; if (!nr_pages) @@ -421,10 +420,10 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci, */ dtype = i82975x_dram_type(mch_window, index); for (chan = 0; chan < csrow->nr_channels; chan++) { - dimm = mci->csrows[index].channels[chan].dimm; + dimm = mci->csrows[index]->channels[chan]->dimm; dimm->nr_pages = nr_pages / csrow->nr_channels; - strncpy(csrow->channels[chan].dimm->label, + strncpy(csrow->channels[chan]->dimm->label, labels[(index >> 1) + (chan * 2)], EDAC_MC_LABEL_LEN); dimm->grain = 1 << 7; /* 128Byte cache-line resolution */ @@ -489,11 +488,11 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx) u8 c1drb[4]; #endif - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); pci_read_config_dword(pdev, I82975X_MCHBAR, &mchbar); if (!(mchbar & 1)) { - debugf3("%s(): failed, MCHBAR disabled!\n", __func__); + edac_dbg(3, "failed, MCHBAR disabled!\n"); goto fail0; } mchbar &= 0xffffc000; /* bits 31:14 used for 16K window */ @@ -558,8 +557,8 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx) goto fail1; } - debugf3("%s(): init mci\n", __func__); - mci->dev = &pdev->dev; + edac_dbg(3, "init mci\n"); + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; mci->edac_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; @@ -569,7 +568,7 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx) mci->dev_name = pci_name(pdev); mci->edac_check = i82975x_check; mci->ctl_page_to_phys = NULL; - debugf3("%s(): init pvt\n", __func__); + edac_dbg(3, "init pvt\n"); pvt = (struct i82975x_pvt *) mci->pvt_info; pvt->mch_window = mch_window; i82975x_init_csrows(mci, pdev, mch_window); @@ -578,12 +577,12 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx) /* finalize this instance of memory controller with edac core */ if (edac_mc_add_mc(mci)) { - debugf3("%s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(3, "failed edac_mc_add_mc()\n"); goto fail2; } /* get this far and it's successful */ - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); return 0; fail2: @@ -601,7 +600,7 @@ static int __devinit i82975x_init_one(struct pci_dev *pdev, { int rc; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); if (pci_enable_device(pdev) < 0) return -EIO; @@ -619,7 +618,7 @@ static void __devexit i82975x_remove_one(struct pci_dev *pdev) struct mem_ctl_info *mci; struct i82975x_pvt *pvt; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); mci = edac_mc_del_mc(&pdev->dev); if (mci == NULL) @@ -655,7 +654,7 @@ static int __init i82975x_init(void) { int pci_rc; - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); @@ -669,7 +668,7 @@ static int __init i82975x_init(void) PCI_DEVICE_ID_INTEL_82975_0, NULL); if (!mci_pdev) { - debugf0("i82975x pci_get_device fail\n"); + edac_dbg(0, "i82975x pci_get_device fail\n"); pci_rc = -ENODEV; goto fail1; } @@ -677,7 +676,7 @@ static int __init i82975x_init(void) pci_rc = i82975x_init_one(mci_pdev, i82975x_pci_tbl); if (pci_rc < 0) { - debugf0("i82975x init fail\n"); + edac_dbg(0, "i82975x init fail\n"); pci_rc = -ENODEV; goto fail1; } @@ -697,7 +696,7 @@ fail0: static void __exit i82975x_exit(void) { - debugf3("%s()\n", __func__); + edac_dbg(3, "\n"); pci_unregister_driver(&i82975x_driver); diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index 0e374625f6f8..a1e791ec25d3 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -49,34 +49,45 @@ static u32 orig_hid1[2]; /************************ MC SYSFS parts ***********************************/ -static ssize_t mpc85xx_mc_inject_data_hi_show(struct mem_ctl_info *mci, +#define to_mci(k) container_of(k, struct mem_ctl_info, dev) + +static ssize_t mpc85xx_mc_inject_data_hi_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct mem_ctl_info *mci = to_mci(dev); struct mpc85xx_mc_pdata *pdata = mci->pvt_info; return sprintf(data, "0x%08x", in_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_HI)); } -static ssize_t mpc85xx_mc_inject_data_lo_show(struct mem_ctl_info *mci, +static ssize_t mpc85xx_mc_inject_data_lo_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct mem_ctl_info *mci = to_mci(dev); struct mpc85xx_mc_pdata *pdata = mci->pvt_info; return sprintf(data, "0x%08x", in_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_LO)); } -static ssize_t mpc85xx_mc_inject_ctrl_show(struct mem_ctl_info *mci, char *data) +static ssize_t mpc85xx_mc_inject_ctrl_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); struct mpc85xx_mc_pdata *pdata = mci->pvt_info; return sprintf(data, "0x%08x", in_be32(pdata->mc_vbase + MPC85XX_MC_ECC_ERR_INJECT)); } -static ssize_t mpc85xx_mc_inject_data_hi_store(struct mem_ctl_info *mci, +static ssize_t mpc85xx_mc_inject_data_hi_store(struct device *dev, + struct device_attribute *mattr, const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); struct mpc85xx_mc_pdata *pdata = mci->pvt_info; if (isdigit(*data)) { out_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_HI, @@ -86,9 +97,11 @@ static ssize_t mpc85xx_mc_inject_data_hi_store(struct mem_ctl_info *mci, return 0; } -static ssize_t mpc85xx_mc_inject_data_lo_store(struct mem_ctl_info *mci, +static ssize_t mpc85xx_mc_inject_data_lo_store(struct device *dev, + struct device_attribute *mattr, const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); struct mpc85xx_mc_pdata *pdata = mci->pvt_info; if (isdigit(*data)) { out_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_LO, @@ -98,9 +111,11 @@ static ssize_t mpc85xx_mc_inject_data_lo_store(struct mem_ctl_info *mci, return 0; } -static ssize_t mpc85xx_mc_inject_ctrl_store(struct mem_ctl_info *mci, - const char *data, size_t count) +static ssize_t mpc85xx_mc_inject_ctrl_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); struct mpc85xx_mc_pdata *pdata = mci->pvt_info; if (isdigit(*data)) { out_be32(pdata->mc_vbase + MPC85XX_MC_ECC_ERR_INJECT, @@ -110,38 +125,35 @@ static ssize_t mpc85xx_mc_inject_ctrl_store(struct mem_ctl_info *mci, return 0; } -static struct mcidev_sysfs_attribute mpc85xx_mc_sysfs_attributes[] = { - { - .attr = { - .name = "inject_data_hi", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = mpc85xx_mc_inject_data_hi_show, - .store = mpc85xx_mc_inject_data_hi_store}, - { - .attr = { - .name = "inject_data_lo", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = mpc85xx_mc_inject_data_lo_show, - .store = mpc85xx_mc_inject_data_lo_store}, - { - .attr = { - .name = "inject_ctrl", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = mpc85xx_mc_inject_ctrl_show, - .store = mpc85xx_mc_inject_ctrl_store}, +DEVICE_ATTR(inject_data_hi, S_IRUGO | S_IWUSR, + mpc85xx_mc_inject_data_hi_show, mpc85xx_mc_inject_data_hi_store); +DEVICE_ATTR(inject_data_lo, S_IRUGO | S_IWUSR, + mpc85xx_mc_inject_data_lo_show, mpc85xx_mc_inject_data_lo_store); +DEVICE_ATTR(inject_ctrl, S_IRUGO | S_IWUSR, + mpc85xx_mc_inject_ctrl_show, mpc85xx_mc_inject_ctrl_store); - /* End of list */ - { - .attr = {.name = NULL} - } -}; +static int mpc85xx_create_sysfs_attributes(struct mem_ctl_info *mci) +{ + int rc; + + rc = device_create_file(&mci->dev, &dev_attr_inject_data_hi); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_inject_data_lo); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_inject_ctrl); + if (rc < 0) + return rc; -static void mpc85xx_set_mc_sysfs_attributes(struct mem_ctl_info *mci) + return 0; +} + +static void mpc85xx_remove_sysfs_attributes(struct mem_ctl_info *mci) { - mci->mc_driver_sysfs_attributes = mpc85xx_mc_sysfs_attributes; + device_remove_file(&mci->dev, &dev_attr_inject_data_hi); + device_remove_file(&mci->dev, &dev_attr_inject_data_lo); + device_remove_file(&mci->dev, &dev_attr_inject_ctrl); } /**************************** PCI Err device ***************************/ @@ -268,7 +280,7 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *op) out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, ~0); if (edac_pci_add_device(pci, pdata->edac_idx) > 0) { - debugf3("%s(): failed edac_pci_add_device()\n", __func__); + edac_dbg(3, "failed edac_pci_add_device()\n"); goto err; } @@ -291,7 +303,7 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *op) } devres_remove_group(&op->dev, mpc85xx_pci_err_probe); - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); printk(KERN_INFO EDAC_MOD_STR " PCI err registered\n"); return 0; @@ -309,7 +321,7 @@ static int mpc85xx_pci_err_remove(struct platform_device *op) struct edac_pci_ctl_info *pci = dev_get_drvdata(&op->dev); struct mpc85xx_pci_pdata *pdata = pci->pvt_info; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR, orig_pci_err_cap_dr); @@ -570,7 +582,7 @@ static int __devinit mpc85xx_l2_err_probe(struct platform_device *op) pdata->edac_idx = edac_dev_idx++; if (edac_device_add_device(edac_dev) > 0) { - debugf3("%s(): failed edac_device_add_device()\n", __func__); + edac_dbg(3, "failed edac_device_add_device()\n"); goto err; } @@ -598,7 +610,7 @@ static int __devinit mpc85xx_l2_err_probe(struct platform_device *op) devres_remove_group(&op->dev, mpc85xx_l2_err_probe); - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); printk(KERN_INFO EDAC_MOD_STR " L2 err registered\n"); return 0; @@ -616,7 +628,7 @@ static int mpc85xx_l2_err_remove(struct platform_device *op) struct edac_device_ctl_info *edac_dev = dev_get_drvdata(&op->dev); struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); if (edac_op_state == EDAC_OPSTATE_INT) { out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINTEN, 0); @@ -813,7 +825,7 @@ static void mpc85xx_mc_check(struct mem_ctl_info *mci) pfn = err_addr >> PAGE_SHIFT; for (row_index = 0; row_index < mci->nr_csrows; row_index++) { - csrow = &mci->csrows[row_index]; + csrow = mci->csrows[row_index]; if ((pfn >= csrow->first_page) && (pfn <= csrow->last_page)) break; } @@ -854,16 +866,16 @@ static void mpc85xx_mc_check(struct mem_ctl_info *mci) mpc85xx_mc_printk(mci, KERN_ERR, "PFN out of range!\n"); if (err_detect & DDR_EDE_SBE) - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, pfn, err_addr & ~PAGE_MASK, syndrome, row_index, 0, -1, - mci->ctl_name, "", NULL); + mci->ctl_name, ""); if (err_detect & DDR_EDE_MBE) - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, pfn, err_addr & ~PAGE_MASK, syndrome, row_index, 0, -1, - mci->ctl_name, "", NULL); + mci->ctl_name, ""); out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, err_detect); } @@ -933,8 +945,8 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci) u32 start; u32 end; - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; cs_bnds = in_be32(pdata->mc_vbase + MPC85XX_MC_CS_BNDS_0 + (index * MPC85XX_MC_CS_BNDS_OFS)); @@ -990,9 +1002,9 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op) pdata = mci->pvt_info; pdata->name = "mpc85xx_mc_err"; pdata->irq = NO_IRQ; - mci->dev = &op->dev; + mci->pdev = &op->dev; pdata->edac_idx = edac_mc_idx++; - dev_set_drvdata(mci->dev, mci); + dev_set_drvdata(mci->pdev, mci); mci->ctl_name = pdata->name; mci->dev_name = pdata->name; @@ -1026,7 +1038,7 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op) goto err; } - debugf3("%s(): init mci\n", __func__); + edac_dbg(3, "init mci\n"); mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_RDDR2 | MEM_FLAG_DDR | MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; @@ -1041,8 +1053,6 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op) mci->scrub_mode = SCRUB_SW_SRC; - mpc85xx_set_mc_sysfs_attributes(mci); - mpc85xx_init_csrows(mci); /* store the original error disable bits */ @@ -1054,7 +1064,13 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op) out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, ~0); if (edac_mc_add_mc(mci)) { - debugf3("%s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(3, "failed edac_mc_add_mc()\n"); + goto err; + } + + if (mpc85xx_create_sysfs_attributes(mci)) { + edac_mc_del_mc(mci->pdev); + edac_dbg(3, "failed edac_mc_add_mc()\n"); goto err; } @@ -1088,7 +1104,7 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op) } devres_remove_group(&op->dev, mpc85xx_mc_err_probe); - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); printk(KERN_INFO EDAC_MOD_STR " MC err registered\n"); return 0; @@ -1106,7 +1122,7 @@ static int mpc85xx_mc_err_remove(struct platform_device *op) struct mem_ctl_info *mci = dev_get_drvdata(&op->dev); struct mpc85xx_mc_pdata *pdata = mci->pvt_info; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); if (edac_op_state == EDAC_OPSTATE_INT) { out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_INT_EN, 0); @@ -1117,6 +1133,7 @@ static int mpc85xx_mc_err_remove(struct platform_device *op) orig_ddr_err_disable); out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_SBE, orig_ddr_err_sbe); + mpc85xx_remove_sysfs_attributes(mci); edac_mc_del_mc(&op->dev); edac_mc_free(mci); return 0; diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c index b0bb5a3d2527..2b315c2edc3c 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c @@ -169,7 +169,7 @@ static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev) MV64X60_PCIx_ERR_MASK_VAL); if (edac_pci_add_device(pci, pdata->edac_idx) > 0) { - debugf3("%s(): failed edac_pci_add_device()\n", __func__); + edac_dbg(3, "failed edac_pci_add_device()\n"); goto err; } @@ -194,7 +194,7 @@ static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev) devres_remove_group(&pdev->dev, mv64x60_pci_err_probe); /* get this far and it's successful */ - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); return 0; @@ -210,7 +210,7 @@ static int mv64x60_pci_err_remove(struct platform_device *pdev) { struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev); - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); edac_pci_del_device(&pdev->dev); @@ -336,7 +336,7 @@ static int __devinit mv64x60_sram_err_probe(struct platform_device *pdev) pdata->edac_idx = edac_dev_idx++; if (edac_device_add_device(edac_dev) > 0) { - debugf3("%s(): failed edac_device_add_device()\n", __func__); + edac_dbg(3, "failed edac_device_add_device()\n"); goto err; } @@ -363,7 +363,7 @@ static int __devinit mv64x60_sram_err_probe(struct platform_device *pdev) devres_remove_group(&pdev->dev, mv64x60_sram_err_probe); /* get this far and it's successful */ - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); return 0; @@ -379,7 +379,7 @@ static int mv64x60_sram_err_remove(struct platform_device *pdev) { struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev); - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); edac_device_del_device(&pdev->dev); edac_device_free_ctl_info(edac_dev); @@ -531,7 +531,7 @@ static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev) pdata->edac_idx = edac_dev_idx++; if (edac_device_add_device(edac_dev) > 0) { - debugf3("%s(): failed edac_device_add_device()\n", __func__); + edac_dbg(3, "failed edac_device_add_device()\n"); goto err; } @@ -558,7 +558,7 @@ static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev) devres_remove_group(&pdev->dev, mv64x60_cpu_err_probe); /* get this far and it's successful */ - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); return 0; @@ -574,7 +574,7 @@ static int mv64x60_cpu_err_remove(struct platform_device *pdev) { struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev); - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); edac_device_del_device(&pdev->dev); edac_device_free_ctl_info(edac_dev); @@ -611,17 +611,17 @@ static void mv64x60_mc_check(struct mem_ctl_info *mci) /* first bit clear in ECC Err Reg, 1 bit error, correctable by HW */ if (!(reg & 0x1)) - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, err_addr >> PAGE_SHIFT, err_addr & PAGE_MASK, syndrome, 0, 0, -1, - mci->ctl_name, "", NULL); + mci->ctl_name, ""); else /* 2 bit error, UE */ - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, err_addr >> PAGE_SHIFT, err_addr & PAGE_MASK, 0, 0, 0, -1, - mci->ctl_name, "", NULL); + mci->ctl_name, ""); /* clear the error */ out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0); @@ -670,8 +670,8 @@ static void mv64x60_init_csrows(struct mem_ctl_info *mci, ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG); - csrow = &mci->csrows[0]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[0]; + dimm = csrow->channels[0]->dimm; dimm->nr_pages = pdata->total_mem >> PAGE_SHIFT; dimm->grain = 8; @@ -724,7 +724,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev) } pdata = mci->pvt_info; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; platform_set_drvdata(pdev, mci); pdata->name = "mv64x60_mc_err"; pdata->irq = NO_IRQ; @@ -766,7 +766,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev) goto err2; } - debugf3("%s(): init mci\n", __func__); + edac_dbg(3, "init mci\n"); mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; mci->edac_cap = EDAC_FLAG_SECDED; @@ -790,7 +790,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev) out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL, ctl); if (edac_mc_add_mc(mci)) { - debugf3("%s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(3, "failed edac_mc_add_mc()\n"); goto err; } @@ -815,7 +815,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev) } /* get this far and it's successful */ - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); return 0; @@ -831,7 +831,7 @@ static int mv64x60_mc_err_remove(struct platform_device *pdev) { struct mem_ctl_info *mci = platform_get_drvdata(pdev); - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); edac_mc_del_mc(&pdev->dev); edac_mc_free(mci); diff --git a/drivers/edac/pasemi_edac.c b/drivers/edac/pasemi_edac.c index b095a906a994..2d35b78ada3c 100644 --- a/drivers/edac/pasemi_edac.c +++ b/drivers/edac/pasemi_edac.c @@ -74,7 +74,7 @@ static int system_mmc_id; static u32 pasemi_edac_get_error_info(struct mem_ctl_info *mci) { - struct pci_dev *pdev = to_pci_dev(mci->dev); + struct pci_dev *pdev = to_pci_dev(mci->pdev); u32 tmp; pci_read_config_dword(pdev, MCDEBUG_ERRSTA, @@ -95,7 +95,7 @@ static u32 pasemi_edac_get_error_info(struct mem_ctl_info *mci) static void pasemi_edac_process_error_info(struct mem_ctl_info *mci, u32 errsta) { - struct pci_dev *pdev = to_pci_dev(mci->dev); + struct pci_dev *pdev = to_pci_dev(mci->pdev); u32 errlog1a; u32 cs; @@ -110,16 +110,16 @@ static void pasemi_edac_process_error_info(struct mem_ctl_info *mci, u32 errsta) /* uncorrectable/multi-bit errors */ if (errsta & (MCDEBUG_ERRSTA_MBE_STATUS | MCDEBUG_ERRSTA_RFL_STATUS)) { - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, - mci->csrows[cs].first_page, 0, 0, - cs, 0, -1, mci->ctl_name, "", NULL); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, + mci->csrows[cs]->first_page, 0, 0, + cs, 0, -1, mci->ctl_name, ""); } /* correctable/single-bit errors */ if (errsta & MCDEBUG_ERRSTA_SBE_STATUS) - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, - mci->csrows[cs].first_page, 0, 0, - cs, 0, -1, mci->ctl_name, "", NULL); + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, + mci->csrows[cs]->first_page, 0, 0, + cs, 0, -1, mci->ctl_name, ""); } static void pasemi_edac_check(struct mem_ctl_info *mci) @@ -141,8 +141,8 @@ static int pasemi_edac_init_csrows(struct mem_ctl_info *mci, int index; for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; pci_read_config_dword(pdev, MCDRAM_RANKCFG + (index * 12), @@ -225,7 +225,7 @@ static int __devinit pasemi_edac_probe(struct pci_dev *pdev, MCCFG_ERRCOR_ECC_GEN_EN | MCCFG_ERRCOR_ECC_CRR_EN; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR | MEM_FLAG_RDDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; mci->edac_cap = (errcor & MCCFG_ERRCOR_ECC_GEN_EN) ? diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c index f3f9fed06ad7..bf0957635991 100644 --- a/drivers/edac/ppc4xx_edac.c +++ b/drivers/edac/ppc4xx_edac.c @@ -727,10 +727,10 @@ ppc4xx_edac_handle_ce(struct mem_ctl_info *mci, for (row = 0; row < mci->nr_csrows; row++) if (ppc4xx_edac_check_bank_error(status, row)) - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, row, 0, -1, - message, "", NULL); + message, ""); } /** @@ -758,10 +758,10 @@ ppc4xx_edac_handle_ue(struct mem_ctl_info *mci, for (row = 0; row < mci->nr_csrows; row++) if (ppc4xx_edac_check_bank_error(status, row)) - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, page, offset, 0, row, 0, -1, - message, "", NULL); + message, ""); } /** @@ -1027,9 +1027,9 @@ ppc4xx_edac_mc_init(struct mem_ctl_info *mci, /* Initial driver pointers and private data */ - mci->dev = &op->dev; + mci->pdev = &op->dev; - dev_set_drvdata(mci->dev, mci); + dev_set_drvdata(mci->pdev, mci); pdata = mci->pvt_info; @@ -1334,7 +1334,7 @@ static int __devinit ppc4xx_edac_probe(struct platform_device *op) return 0; fail1: - edac_mc_del_mc(mci->dev); + edac_mc_del_mc(mci->pdev); fail: edac_mc_free(mci); @@ -1368,7 +1368,7 @@ ppc4xx_edac_remove(struct platform_device *op) dcr_unmap(pdata->dcr_host, SDRAM_DCR_RESOURCE_LEN); - edac_mc_del_mc(mci->dev); + edac_mc_del_mc(mci->pdev); edac_mc_free(mci); return 0; diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c index e1cacd164f31..f854debd5533 100644 --- a/drivers/edac/r82600_edac.c +++ b/drivers/edac/r82600_edac.c @@ -140,7 +140,7 @@ static void r82600_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); pci_read_config_dword(pdev, R82600_EAP, &info->eapr); if (info->eapr & BIT(0)) @@ -179,11 +179,11 @@ static int r82600_process_error_info(struct mem_ctl_info *mci, error_found = 1; if (handle_errors) - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, page, 0, syndrome, edac_mc_find_csrow_by_page(mci, page), 0, -1, - mci->ctl_name, "", NULL); + mci->ctl_name, ""); } if (info->eapr & BIT(1)) { /* UE? */ @@ -191,11 +191,11 @@ static int r82600_process_error_info(struct mem_ctl_info *mci, if (handle_errors) /* 82600 doesn't give enough info */ - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, page, 0, 0, edac_mc_find_csrow_by_page(mci, page), 0, -1, - mci->ctl_name, "", NULL); + mci->ctl_name, ""); } return error_found; @@ -205,7 +205,7 @@ static void r82600_check(struct mem_ctl_info *mci) { struct r82600_error_info info; - debugf1("MC%d: %s()\n", mci->mc_idx, __func__); + edac_dbg(1, "MC%d\n", mci->mc_idx); r82600_get_error_info(mci, &info); r82600_process_error_info(mci, &info, 1); } @@ -230,19 +230,19 @@ static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, row_high_limit_last = 0; for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; /* find the DRAM Chip Select Base address and mask */ pci_read_config_byte(pdev, R82600_DRBA + index, &drbar); - debugf1("%s() Row=%d DRBA = %#0x\n", __func__, index, drbar); + edac_dbg(1, "Row=%d DRBA = %#0x\n", index, drbar); row_high_limit = ((u32) drbar << 24); /* row_high_limit = ((u32)drbar << 24) | 0xffffffUL; */ - debugf1("%s() Row=%d, Boundary Address=%#0x, Last = %#0x\n", - __func__, index, row_high_limit, row_high_limit_last); + edac_dbg(1, "Row=%d, Boundary Address=%#0x, Last = %#0x\n", + index, row_high_limit, row_high_limit_last); /* Empty row [p.57] */ if (row_high_limit == row_high_limit_last) @@ -277,14 +277,13 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx) u32 sdram_refresh_rate; struct r82600_error_info discard; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); pci_read_config_byte(pdev, R82600_DRAMC, &dramcr); pci_read_config_dword(pdev, R82600_EAP, &eapr); scrub_disabled = eapr & BIT(31); sdram_refresh_rate = dramcr & (BIT(0) | BIT(1)); - debugf2("%s(): sdram refresh rate = %#0x\n", __func__, - sdram_refresh_rate); - debugf2("%s(): DRAMC register = %#0x\n", __func__, dramcr); + edac_dbg(2, "sdram refresh rate = %#0x\n", sdram_refresh_rate); + edac_dbg(2, "DRAMC register = %#0x\n", dramcr); layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; layers[0].size = R82600_NR_CSROWS; layers[0].is_virt_csrow = true; @@ -295,8 +294,8 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx) if (mci == NULL) return -ENOMEM; - debugf0("%s(): mci = %p\n", __func__, mci); - mci->dev = &pdev->dev; + edac_dbg(0, "mci = %p\n", mci); + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; /* FIXME try to work out if the chip leads have been used for COM2 @@ -311,8 +310,8 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx) if (ecc_enabled(dramcr)) { if (scrub_disabled) - debugf3("%s(): mci = %p - Scrubbing disabled! EAP: " - "%#0x\n", __func__, mci, eapr); + edac_dbg(3, "mci = %p - Scrubbing disabled! EAP: %#0x\n", + mci, eapr); } else mci->edac_cap = EDAC_FLAG_NONE; @@ -329,15 +328,14 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx) * type of memory controller. The ID is therefore hardcoded to 0. */ if (edac_mc_add_mc(mci)) { - debugf3("%s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(3, "failed edac_mc_add_mc()\n"); goto fail; } /* get this far and it's successful */ if (disable_hardware_scrub) { - debugf3("%s(): Disabling Hardware Scrub (scrub on error)\n", - __func__); + edac_dbg(3, "Disabling Hardware Scrub (scrub on error)\n"); pci_write_bits32(pdev, R82600_EAP, BIT(31), BIT(31)); } @@ -352,7 +350,7 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx) __func__); } - debugf3("%s(): success\n", __func__); + edac_dbg(3, "success\n"); return 0; fail: @@ -364,7 +362,7 @@ fail: static int __devinit r82600_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); /* don't need to call pci_enable_device() */ return r82600_probe1(pdev, ent->driver_data); @@ -374,7 +372,7 @@ static void __devexit r82600_remove_one(struct pci_dev *pdev) { struct mem_ctl_info *mci; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); if (r82600_pci) edac_pci_release_generic_ctl(r82600_pci); diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index 36ad17e79d61..f3b1f9fafa4b 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -381,8 +381,8 @@ static inline int numrank(u32 mtr) int ranks = (1 << RANK_CNT_BITS(mtr)); if (ranks > 4) { - debugf0("Invalid number of ranks: %d (max = 4) raw value = %x (%04x)", - ranks, (unsigned int)RANK_CNT_BITS(mtr), mtr); + edac_dbg(0, "Invalid number of ranks: %d (max = 4) raw value = %x (%04x)\n", + ranks, (unsigned int)RANK_CNT_BITS(mtr), mtr); return -EINVAL; } @@ -394,8 +394,8 @@ static inline int numrow(u32 mtr) int rows = (RANK_WIDTH_BITS(mtr) + 12); if (rows < 13 || rows > 18) { - debugf0("Invalid number of rows: %d (should be between 14 and 17) raw value = %x (%04x)", - rows, (unsigned int)RANK_WIDTH_BITS(mtr), mtr); + edac_dbg(0, "Invalid number of rows: %d (should be between 14 and 17) raw value = %x (%04x)\n", + rows, (unsigned int)RANK_WIDTH_BITS(mtr), mtr); return -EINVAL; } @@ -407,8 +407,8 @@ static inline int numcol(u32 mtr) int cols = (COL_WIDTH_BITS(mtr) + 10); if (cols > 12) { - debugf0("Invalid number of cols: %d (max = 4) raw value = %x (%04x)", - cols, (unsigned int)COL_WIDTH_BITS(mtr), mtr); + edac_dbg(0, "Invalid number of cols: %d (max = 4) raw value = %x (%04x)\n", + cols, (unsigned int)COL_WIDTH_BITS(mtr), mtr); return -EINVAL; } @@ -475,8 +475,8 @@ static struct pci_dev *get_pdev_slot_func(u8 bus, unsigned slot, if (PCI_SLOT(sbridge_dev->pdev[i]->devfn) == slot && PCI_FUNC(sbridge_dev->pdev[i]->devfn) == func) { - debugf1("Associated %02x.%02x.%d with %p\n", - bus, slot, func, sbridge_dev->pdev[i]); + edac_dbg(1, "Associated %02x.%02x.%d with %p\n", + bus, slot, func, sbridge_dev->pdev[i]); return sbridge_dev->pdev[i]; } } @@ -523,45 +523,45 @@ static int get_dimm_config(struct mem_ctl_info *mci) pci_read_config_dword(pvt->pci_br, SAD_CONTROL, ®); pvt->sbridge_dev->node_id = NODE_ID(reg); - debugf0("mc#%d: Node ID: %d, source ID: %d\n", - pvt->sbridge_dev->mc, - pvt->sbridge_dev->node_id, - pvt->sbridge_dev->source_id); + edac_dbg(0, "mc#%d: Node ID: %d, source ID: %d\n", + pvt->sbridge_dev->mc, + pvt->sbridge_dev->node_id, + pvt->sbridge_dev->source_id); pci_read_config_dword(pvt->pci_ras, RASENABLES, ®); if (IS_MIRROR_ENABLED(reg)) { - debugf0("Memory mirror is enabled\n"); + edac_dbg(0, "Memory mirror is enabled\n"); pvt->is_mirrored = true; } else { - debugf0("Memory mirror is disabled\n"); + edac_dbg(0, "Memory mirror is disabled\n"); pvt->is_mirrored = false; } pci_read_config_dword(pvt->pci_ta, MCMTR, &pvt->info.mcmtr); if (IS_LOCKSTEP_ENABLED(pvt->info.mcmtr)) { - debugf0("Lockstep is enabled\n"); + edac_dbg(0, "Lockstep is enabled\n"); mode = EDAC_S8ECD8ED; pvt->is_lockstep = true; } else { - debugf0("Lockstep is disabled\n"); + edac_dbg(0, "Lockstep is disabled\n"); mode = EDAC_S4ECD4ED; pvt->is_lockstep = false; } if (IS_CLOSE_PG(pvt->info.mcmtr)) { - debugf0("address map is on closed page mode\n"); + edac_dbg(0, "address map is on closed page mode\n"); pvt->is_close_pg = true; } else { - debugf0("address map is on open page mode\n"); + edac_dbg(0, "address map is on open page mode\n"); pvt->is_close_pg = false; } pci_read_config_dword(pvt->pci_ddrio, RANK_CFG_A, ®); if (IS_RDIMM_ENABLED(reg)) { /* FIXME: Can also be LRDIMM */ - debugf0("Memory is registered\n"); + edac_dbg(0, "Memory is registered\n"); mtype = MEM_RDDR3; } else { - debugf0("Memory is unregistered\n"); + edac_dbg(0, "Memory is unregistered\n"); mtype = MEM_DDR3; } @@ -576,7 +576,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) i, j, 0); pci_read_config_dword(pvt->pci_tad[i], mtr_regs[j], &mtr); - debugf4("Channel #%d MTR%d = %x\n", i, j, mtr); + edac_dbg(4, "Channel #%d MTR%d = %x\n", i, j, mtr); if (IS_DIMM_PRESENT(mtr)) { pvt->channel[i].dimms++; @@ -588,10 +588,10 @@ static int get_dimm_config(struct mem_ctl_info *mci) size = (rows * cols * banks * ranks) >> (20 - 3); npages = MiB_TO_PAGES(size); - debugf0("mc#%d: channel %d, dimm %d, %d Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n", - pvt->sbridge_dev->mc, i, j, - size, npages, - banks, ranks, rows, cols); + edac_dbg(0, "mc#%d: channel %d, dimm %d, %d Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n", + pvt->sbridge_dev->mc, i, j, + size, npages, + banks, ranks, rows, cols); dimm->nr_pages = npages; dimm->grain = 32; @@ -629,8 +629,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci) tmp_mb = (1 + pvt->tolm) >> 20; mb = div_u64_rem(tmp_mb, 1000, &kb); - debugf0("TOLM: %u.%03u GB (0x%016Lx)\n", - mb, kb, (u64)pvt->tolm); + edac_dbg(0, "TOLM: %u.%03u GB (0x%016Lx)\n", mb, kb, (u64)pvt->tolm); /* Address range is already 45:25 */ pci_read_config_dword(pvt->pci_sad1, TOHM, @@ -639,8 +638,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci) tmp_mb = (1 + pvt->tohm) >> 20; mb = div_u64_rem(tmp_mb, 1000, &kb); - debugf0("TOHM: %u.%03u GB (0x%016Lx)", - mb, kb, (u64)pvt->tohm); + edac_dbg(0, "TOHM: %u.%03u GB (0x%016Lx)", mb, kb, (u64)pvt->tohm); /* * Step 2) Get SAD range and SAD Interleave list @@ -663,13 +661,13 @@ static void get_memory_layout(const struct mem_ctl_info *mci) tmp_mb = (limit + 1) >> 20; mb = div_u64_rem(tmp_mb, 1000, &kb); - debugf0("SAD#%d %s up to %u.%03u GB (0x%016Lx) %s reg=0x%08x\n", - n_sads, - get_dram_attr(reg), - mb, kb, - ((u64)tmp_mb) << 20L, - INTERLEAVE_MODE(reg) ? "Interleave: 8:6" : "Interleave: [8:6]XOR[18:16]", - reg); + edac_dbg(0, "SAD#%d %s up to %u.%03u GB (0x%016Lx) Interleave: %s reg=0x%08x\n", + n_sads, + get_dram_attr(reg), + mb, kb, + ((u64)tmp_mb) << 20L, + INTERLEAVE_MODE(reg) ? "8:6" : "[8:6]XOR[18:16]", + reg); prv = limit; pci_read_config_dword(pvt->pci_sad0, interleave_list[n_sads], @@ -679,8 +677,8 @@ static void get_memory_layout(const struct mem_ctl_info *mci) if (j > 0 && sad_interl == sad_pkg(reg, j)) break; - debugf0("SAD#%d, interleave #%d: %d\n", - n_sads, j, sad_pkg(reg, j)); + edac_dbg(0, "SAD#%d, interleave #%d: %d\n", + n_sads, j, sad_pkg(reg, j)); } } @@ -697,16 +695,16 @@ static void get_memory_layout(const struct mem_ctl_info *mci) tmp_mb = (limit + 1) >> 20; mb = div_u64_rem(tmp_mb, 1000, &kb); - debugf0("TAD#%d: up to %u.%03u GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n", - n_tads, mb, kb, - ((u64)tmp_mb) << 20L, - (u32)TAD_SOCK(reg), - (u32)TAD_CH(reg), - (u32)TAD_TGT0(reg), - (u32)TAD_TGT1(reg), - (u32)TAD_TGT2(reg), - (u32)TAD_TGT3(reg), - reg); + edac_dbg(0, "TAD#%d: up to %u.%03u GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n", + n_tads, mb, kb, + ((u64)tmp_mb) << 20L, + (u32)TAD_SOCK(reg), + (u32)TAD_CH(reg), + (u32)TAD_TGT0(reg), + (u32)TAD_TGT1(reg), + (u32)TAD_TGT2(reg), + (u32)TAD_TGT3(reg), + reg); prv = limit; } @@ -722,11 +720,11 @@ static void get_memory_layout(const struct mem_ctl_info *mci) ®); tmp_mb = TAD_OFFSET(reg) >> 20; mb = div_u64_rem(tmp_mb, 1000, &kb); - debugf0("TAD CH#%d, offset #%d: %u.%03u GB (0x%016Lx), reg=0x%08x\n", - i, j, - mb, kb, - ((u64)tmp_mb) << 20L, - reg); + edac_dbg(0, "TAD CH#%d, offset #%d: %u.%03u GB (0x%016Lx), reg=0x%08x\n", + i, j, + mb, kb, + ((u64)tmp_mb) << 20L, + reg); } } @@ -747,12 +745,12 @@ static void get_memory_layout(const struct mem_ctl_info *mci) tmp_mb = RIR_LIMIT(reg) >> 20; rir_way = 1 << RIR_WAY(reg); mb = div_u64_rem(tmp_mb, 1000, &kb); - debugf0("CH#%d RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d, reg=0x%08x\n", - i, j, - mb, kb, - ((u64)tmp_mb) << 20L, - rir_way, - reg); + edac_dbg(0, "CH#%d RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d, reg=0x%08x\n", + i, j, + mb, kb, + ((u64)tmp_mb) << 20L, + rir_way, + reg); for (k = 0; k < rir_way; k++) { pci_read_config_dword(pvt->pci_tad[i], @@ -761,12 +759,12 @@ static void get_memory_layout(const struct mem_ctl_info *mci) tmp_mb = RIR_OFFSET(reg) << 6; mb = div_u64_rem(tmp_mb, 1000, &kb); - debugf0("CH#%d RIR#%d INTL#%d, offset %u.%03u GB (0x%016Lx), tgt: %d, reg=0x%08x\n", - i, j, k, - mb, kb, - ((u64)tmp_mb) << 20L, - (u32)RIR_RNK_TGT(reg), - reg); + edac_dbg(0, "CH#%d RIR#%d INTL#%d, offset %u.%03u GB (0x%016Lx), tgt: %d, reg=0x%08x\n", + i, j, k, + mb, kb, + ((u64)tmp_mb) << 20L, + (u32)RIR_RNK_TGT(reg), + reg); } } } @@ -853,16 +851,16 @@ static int get_memory_error_data(struct mem_ctl_info *mci, if (sad_way > 0 && sad_interl == sad_pkg(reg, sad_way)) break; sad_interleave[sad_way] = sad_pkg(reg, sad_way); - debugf0("SAD interleave #%d: %d\n", - sad_way, sad_interleave[sad_way]); + edac_dbg(0, "SAD interleave #%d: %d\n", + sad_way, sad_interleave[sad_way]); } - debugf0("mc#%d: Error detected on SAD#%d: address 0x%016Lx < 0x%016Lx, Interleave [%d:6]%s\n", - pvt->sbridge_dev->mc, - n_sads, - addr, - limit, - sad_way + 7, - interleave_mode ? "" : "XOR[18:16]"); + edac_dbg(0, "mc#%d: Error detected on SAD#%d: address 0x%016Lx < 0x%016Lx, Interleave [%d:6]%s\n", + pvt->sbridge_dev->mc, + n_sads, + addr, + limit, + sad_way + 7, + interleave_mode ? "" : "XOR[18:16]"); if (interleave_mode) idx = ((addr >> 6) ^ (addr >> 16)) & 7; else @@ -884,8 +882,8 @@ static int get_memory_error_data(struct mem_ctl_info *mci, return -EINVAL; } *socket = sad_interleave[idx]; - debugf0("SAD interleave index: %d (wayness %d) = CPU socket %d\n", - idx, sad_way, *socket); + edac_dbg(0, "SAD interleave index: %d (wayness %d) = CPU socket %d\n", + idx, sad_way, *socket); /* * Move to the proper node structure, in order to access the @@ -972,16 +970,16 @@ static int get_memory_error_data(struct mem_ctl_info *mci, offset = TAD_OFFSET(tad_offset); - debugf0("TAD#%d: address 0x%016Lx < 0x%016Lx, socket interleave %d, channel interleave %d (offset 0x%08Lx), index %d, base ch: %d, ch mask: 0x%02lx\n", - n_tads, - addr, - limit, - (u32)TAD_SOCK(reg), - ch_way, - offset, - idx, - base_ch, - *channel_mask); + edac_dbg(0, "TAD#%d: address 0x%016Lx < 0x%016Lx, socket interleave %d, channel interleave %d (offset 0x%08Lx), index %d, base ch: %d, ch mask: 0x%02lx\n", + n_tads, + addr, + limit, + (u32)TAD_SOCK(reg), + ch_way, + offset, + idx, + base_ch, + *channel_mask); /* Calculate channel address */ /* Remove the TAD offset */ @@ -1017,11 +1015,11 @@ static int get_memory_error_data(struct mem_ctl_info *mci, limit = RIR_LIMIT(reg); mb = div_u64_rem(limit >> 20, 1000, &kb); - debugf0("RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d\n", - n_rir, - mb, kb, - limit, - 1 << RIR_WAY(reg)); + edac_dbg(0, "RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d\n", + n_rir, + mb, kb, + limit, + 1 << RIR_WAY(reg)); if (ch_addr <= limit) break; } @@ -1042,12 +1040,12 @@ static int get_memory_error_data(struct mem_ctl_info *mci, ®); *rank = RIR_RNK_TGT(reg); - debugf0("RIR#%d: channel address 0x%08Lx < 0x%08Lx, RIR interleave %d, index %d\n", - n_rir, - ch_addr, - limit, - rir_way, - idx); + edac_dbg(0, "RIR#%d: channel address 0x%08Lx < 0x%08Lx, RIR interleave %d, index %d\n", + n_rir, + ch_addr, + limit, + rir_way, + idx); return 0; } @@ -1064,14 +1062,14 @@ static void sbridge_put_devices(struct sbridge_dev *sbridge_dev) { int i; - debugf0(__FILE__ ": %s()\n", __func__); + edac_dbg(0, "\n"); for (i = 0; i < sbridge_dev->n_devs; i++) { struct pci_dev *pdev = sbridge_dev->pdev[i]; if (!pdev) continue; - debugf0("Removing dev %02x:%02x.%d\n", - pdev->bus->number, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + edac_dbg(0, "Removing dev %02x:%02x.%d\n", + pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); pci_dev_put(pdev); } } @@ -1177,10 +1175,9 @@ static int sbridge_get_onedevice(struct pci_dev **prev, return -ENODEV; } - debugf0("Detected dev %02x:%d.%d PCI ID %04x:%04x\n", - bus, dev_descr->dev, - dev_descr->func, - PCI_VENDOR_ID_INTEL, dev_descr->dev_id); + edac_dbg(0, "Detected dev %02x:%d.%d PCI ID %04x:%04x\n", + bus, dev_descr->dev, dev_descr->func, + PCI_VENDOR_ID_INTEL, dev_descr->dev_id); /* * As stated on drivers/pci/search.c, the reference count for @@ -1297,10 +1294,10 @@ static int mci_bind_devs(struct mem_ctl_info *mci, goto error; } - debugf0("Associated PCI %02x.%02d.%d with dev = %p\n", - sbridge_dev->bus, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), - pdev); + edac_dbg(0, "Associated PCI %02x.%02d.%d with dev = %p\n", + sbridge_dev->bus, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + pdev); } /* Check if everything were registered */ @@ -1435,8 +1432,7 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci, * to the group of dimm's where the error may be happening. */ snprintf(msg, sizeof(msg), - "count:%d%s%s area:%s err_code:%04x:%04x socket:%d channel_mask:%ld rank:%d", - core_err_cnt, + "%s%s area:%s err_code:%04x:%04x socket:%d channel_mask:%ld rank:%d", overflow ? " OVERFLOW" : "", (uncorrected_error && recoverable) ? " recoverable" : "", area_type, @@ -1445,20 +1441,20 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci, channel_mask, rank); - debugf0("%s", msg); + edac_dbg(0, "%s\n", msg); /* FIXME: need support for channel mask */ /* Call the helper to output message */ - edac_mc_handle_error(tp_event, mci, + edac_mc_handle_error(tp_event, mci, core_err_cnt, m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0, channel, dimm, -1, - optype, msg, m); + optype, msg); return; err_parsing: - edac_mc_handle_error(tp_event, mci, 0, 0, 0, + edac_mc_handle_error(tp_event, mci, core_err_cnt, 0, 0, 0, -1, -1, -1, - msg, "", m); + msg, ""); } @@ -1592,8 +1588,7 @@ static void sbridge_unregister_mci(struct sbridge_dev *sbridge_dev) struct sbridge_pvt *pvt; if (unlikely(!mci || !mci->pvt_info)) { - debugf0("MC: " __FILE__ ": %s(): dev = %p\n", - __func__, &sbridge_dev->pdev[0]->dev); + edac_dbg(0, "MC: dev = %p\n", &sbridge_dev->pdev[0]->dev); sbridge_printk(KERN_ERR, "Couldn't find mci handler\n"); return; @@ -1601,13 +1596,13 @@ static void sbridge_unregister_mci(struct sbridge_dev *sbridge_dev) pvt = mci->pvt_info; - debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n", - __func__, mci, &sbridge_dev->pdev[0]->dev); + edac_dbg(0, "MC: mci = %p, dev = %p\n", + mci, &sbridge_dev->pdev[0]->dev); /* Remove MC sysfs nodes */ - edac_mc_del_mc(mci->dev); + edac_mc_del_mc(mci->pdev); - debugf1("%s: free mci struct\n", mci->ctl_name); + edac_dbg(1, "%s: free mci struct\n", mci->ctl_name); kfree(mci->ctl_name); edac_mc_free(mci); sbridge_dev->mci = NULL; @@ -1638,8 +1633,8 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev) if (unlikely(!mci)) return -ENOMEM; - debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n", - __func__, mci, &sbridge_dev->pdev[0]->dev); + edac_dbg(0, "MC: mci = %p, dev = %p\n", + mci, &sbridge_dev->pdev[0]->dev); pvt = mci->pvt_info; memset(pvt, 0, sizeof(*pvt)); @@ -1670,12 +1665,11 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev) get_memory_layout(mci); /* record ptr to the generic device */ - mci->dev = &sbridge_dev->pdev[0]->dev; + mci->pdev = &sbridge_dev->pdev[0]->dev; /* add this new MC control structure to EDAC's list of MCs */ if (unlikely(edac_mc_add_mc(mci))) { - debugf0("MC: " __FILE__ - ": %s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); rc = -EINVAL; goto fail0; } @@ -1722,7 +1716,8 @@ static int __devinit sbridge_probe(struct pci_dev *pdev, mc = 0; list_for_each_entry(sbridge_dev, &sbridge_edac_list, list) { - debugf0("Registering MC#%d (%d of %d)\n", mc, mc + 1, num_mc); + edac_dbg(0, "Registering MC#%d (%d of %d)\n", + mc, mc + 1, num_mc); sbridge_dev->mc = mc++; rc = sbridge_register_mci(sbridge_dev); if (unlikely(rc < 0)) @@ -1752,7 +1747,7 @@ static void __devexit sbridge_remove(struct pci_dev *pdev) { struct sbridge_dev *sbridge_dev; - debugf0(__FILE__ ": %s()\n", __func__); + edac_dbg(0, "\n"); /* * we have a trouble here: pdev value for removal will be wrong, since @@ -1801,7 +1796,7 @@ static int __init sbridge_init(void) { int pci_rc; - debugf2("MC: " __FILE__ ": %s()\n", __func__); + edac_dbg(2, "\n"); /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); @@ -1825,7 +1820,7 @@ static int __init sbridge_init(void) */ static void __exit sbridge_exit(void) { - debugf2("MC: " __FILE__ ": %s()\n", __func__); + edac_dbg(2, "\n"); pci_unregister_driver(&sbridge_driver); mce_unregister_decode_chain(&sbridge_mce_dec); } diff --git a/drivers/edac/tile_edac.c b/drivers/edac/tile_edac.c index 7bb4614730db..1e904b7b79a0 100644 --- a/drivers/edac/tile_edac.c +++ b/drivers/edac/tile_edac.c @@ -69,12 +69,12 @@ static void tile_edac_check(struct mem_ctl_info *mci) /* Check if the current error count is different from the saved one. */ if (mem_error.sbe_count != priv->ce_count) { - dev_dbg(mci->dev, "ECC CE err on node %d\n", priv->node); + dev_dbg(mci->pdev, "ECC CE err on node %d\n", priv->node); priv->ce_count = mem_error.sbe_count; - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, 0, 0, -1, - mci->ctl_name, "", NULL); + mci->ctl_name, ""); } } @@ -84,10 +84,10 @@ static void tile_edac_check(struct mem_ctl_info *mci) */ static int __devinit tile_edac_init_csrows(struct mem_ctl_info *mci) { - struct csrow_info *csrow = &mci->csrows[0]; + struct csrow_info *csrow = mci->csrows[0]; struct tile_edac_priv *priv = mci->pvt_info; struct mshim_mem_info mem_info; - struct dimm_info *dimm = csrow->channels[0].dimm; + struct dimm_info *dimm = csrow->channels[0]->dimm; if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&mem_info, sizeof(struct mshim_mem_info), MSHIM_MEM_INFO_OFF) != @@ -149,7 +149,7 @@ static int __devinit tile_edac_mc_probe(struct platform_device *pdev) priv->node = pdev->id; priv->hv_devhdl = hv_devhdl; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_SECDED; diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c index 1ac7962d63ea..08a992693e62 100644 --- a/drivers/edac/x38_edac.c +++ b/drivers/edac/x38_edac.c @@ -103,10 +103,10 @@ static int how_many_channel(struct pci_dev *pdev) pci_read_config_byte(pdev, X38_CAPID0 + 8, &capid0_8b); if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */ - debugf0("In single channel mode.\n"); + edac_dbg(0, "In single channel mode\n"); x38_channel_num = 1; } else { - debugf0("In dual channel mode.\n"); + edac_dbg(0, "In dual channel mode\n"); x38_channel_num = 2; } @@ -151,7 +151,7 @@ static void x38_clear_error_info(struct mem_ctl_info *mci) { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * Clear any error bits. @@ -172,7 +172,7 @@ static void x38_get_and_clear_error_info(struct mem_ctl_info *mci, struct pci_dev *pdev; void __iomem *window = mci->pvt_info; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -215,26 +215,26 @@ static void x38_process_error_info(struct mem_ctl_info *mci, return; if ((info->errsts ^ info->errsts2) & X38_ERRSTS_BITS) { - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1, - "UE overwrote CE", "", NULL); + "UE overwrote CE", ""); info->errsts = info->errsts2; } for (channel = 0; channel < x38_channel_num; channel++) { log = info->eccerrlog[channel]; if (log & X38_ECCERRLOG_UE) { - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, eccerrlog_row(channel, log), -1, -1, - "x38 UE", "", NULL); + "x38 UE", ""); } else if (log & X38_ECCERRLOG_CE) { - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, eccerrlog_syndrome(log), eccerrlog_row(channel, log), -1, -1, - "x38 CE", "", NULL); + "x38 CE", ""); } } } @@ -243,7 +243,7 @@ static void x38_check(struct mem_ctl_info *mci) { struct x38_error_info info; - debugf1("MC%d: %s()\n", mci->mc_idx, __func__); + edac_dbg(1, "MC%d\n", mci->mc_idx); x38_get_and_clear_error_info(mci, &info); x38_process_error_info(mci, &info); } @@ -331,7 +331,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx) bool stacked; void __iomem *window; - debugf0("MC: %s()\n", __func__); + edac_dbg(0, "MC:\n"); window = x38_map_mchbar(pdev); if (!window) @@ -352,9 +352,9 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx) if (!mci) return -ENOMEM; - debugf3("MC: %s(): init mci\n", __func__); + edac_dbg(3, "MC: init mci\n"); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_SECDED; @@ -378,7 +378,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx) */ for (i = 0; i < mci->nr_csrows; i++) { unsigned long nr_pages; - struct csrow_info *csrow = &mci->csrows[i]; + struct csrow_info *csrow = mci->csrows[i]; nr_pages = drb_to_nr_pages(drbs, stacked, i / X38_RANKS_PER_CHANNEL, @@ -388,7 +388,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx) continue; for (j = 0; j < x38_channel_num; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / x38_channel_num; dimm->grain = nr_pages << PAGE_SHIFT; @@ -402,12 +402,12 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx) rc = -ENODEV; if (edac_mc_add_mc(mci)) { - debugf3("MC: %s(): failed edac_mc_add_mc()\n", __func__); + edac_dbg(3, "MC: failed edac_mc_add_mc()\n"); goto fail; } /* get this far and it's successful */ - debugf3("MC: %s(): success\n", __func__); + edac_dbg(3, "MC: success\n"); return 0; fail: @@ -423,7 +423,7 @@ static int __devinit x38_init_one(struct pci_dev *pdev, { int rc; - debugf0("MC: %s()\n", __func__); + edac_dbg(0, "MC:\n"); if (pci_enable_device(pdev) < 0) return -EIO; @@ -439,7 +439,7 @@ static void __devexit x38_remove_one(struct pci_dev *pdev) { struct mem_ctl_info *mci; - debugf0("%s()\n", __func__); + edac_dbg(0, "\n"); mci = edac_mc_del_mc(&pdev->dev); if (!mci) @@ -472,7 +472,7 @@ static int __init x38_init(void) { int pci_rc; - debugf3("MC: %s()\n", __func__); + edac_dbg(3, "MC:\n"); /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); @@ -486,14 +486,14 @@ static int __init x38_init(void) mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X38_HB, NULL); if (!mci_pdev) { - debugf0("x38 pci_get_device fail\n"); + edac_dbg(0, "x38 pci_get_device fail\n"); pci_rc = -ENODEV; goto fail1; } pci_rc = x38_init_one(mci_pdev, x38_pci_tbl); if (pci_rc < 0) { - debugf0("x38 init fail\n"); + edac_dbg(0, "x38 init fail\n"); pci_rc = -ENODEV; goto fail1; } @@ -513,7 +513,7 @@ fail0: static void __exit x38_exit(void) { - debugf3("MC: %s()\n", __func__); + edac_dbg(3, "MC:\n"); pci_unregister_driver(&x38_driver); if (!x38_registered) { diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 16716356d1fe..e175c8ed4ec4 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -33,7 +33,7 @@ config EXTCON_MAX77693 config EXTCON_MAX8997 tristate "MAX8997 EXTCON Support" - depends on MFD_MAX8997 + depends on MFD_MAX8997 && IRQ_DOMAIN help If you say yes here you get support for the MUIC device of Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index a4ed30bd9a41..ef9090a4271d 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -26,6 +26,7 @@ #include <linux/mfd/max8997.h> #include <linux/mfd/max8997-private.h> #include <linux/extcon.h> +#include <linux/irqdomain.h> #define DEV_NAME "max8997-muic" @@ -77,6 +78,7 @@ struct max8997_muic_irq { unsigned int irq; const char *name; + unsigned int virq; }; static struct max8997_muic_irq muic_irqs[] = { @@ -343,12 +345,10 @@ static void max8997_muic_irq_work(struct work_struct *work) { struct max8997_muic_info *info = container_of(work, struct max8997_muic_info, irq_work); - struct max8997_dev *max8997 = i2c_get_clientdata(info->muic); u8 status[2]; u8 adc, chg_type; - - int irq_type = info->irq - max8997->irq_base; - int ret; + int irq_type = 0; + int i, ret; mutex_lock(&info->mutex); @@ -363,6 +363,10 @@ static void max8997_muic_irq_work(struct work_struct *work) dev_dbg(info->dev, "%s: STATUS1:0x%x, 2:0x%x\n", __func__, status[0], status[1]); + for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++) + if (info->irq == muic_irqs[i].virq) + irq_type = muic_irqs[i].irq; + switch (irq_type) { case MAX8997_MUICIRQ_ADC: adc = status[0] & STATUS1_ADC_MASK; @@ -448,11 +452,15 @@ static int __devinit max8997_muic_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) { struct max8997_muic_irq *muic_irq = &muic_irqs[i]; + int virq = 0; + + virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq); + if (!virq) + goto err_irq; + muic_irq->virq = virq; - ret = request_threaded_irq(pdata->irq_base + muic_irq->irq, - NULL, max8997_muic_irq_handler, - 0, muic_irq->name, - info); + ret = request_threaded_irq(virq, NULL,max8997_muic_irq_handler, + 0, muic_irq->name, info); if (ret) { dev_err(&pdev->dev, "failed: irq request (IRQ: %d," @@ -496,7 +504,7 @@ err_extcon: kfree(info->edev); err_irq: while (--i >= 0) - free_irq(pdata->irq_base + muic_irqs[i].irq, info); + free_irq(muic_irqs[i].virq, info); kfree(info); err_kfree: return ret; @@ -505,11 +513,10 @@ err_kfree: static int __devexit max8997_muic_remove(struct platform_device *pdev) { struct max8997_muic_info *info = platform_get_drvdata(pdev); - struct max8997_dev *max8997 = i2c_get_clientdata(info->muic); int i; for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) - free_irq(max8997->irq_base + muic_irqs[i].irq, info); + free_irq(muic_irqs[i].virq, info); cancel_work_sync(&info->irq_work); extcon_dev_unregister(info->edev); diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 4d460ef87161..7a05fd24d68b 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -398,6 +398,14 @@ static ssize_t guid_show(struct device *dev, return ret; } +static ssize_t is_local_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fw_device *device = fw_device(dev); + + return sprintf(buf, "%u\n", device->is_local); +} + static int units_sprintf(char *buf, const u32 *directory) { struct fw_csr_iterator ci; @@ -447,6 +455,7 @@ static ssize_t units_show(struct device *dev, static struct device_attribute fw_device_attributes[] = { __ATTR_RO(config_rom), __ATTR_RO(guid), + __ATTR_RO(is_local), __ATTR_RO(units), __ATTR_NULL, }; diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 8382e27e9a27..38c0aa60b2cb 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -146,7 +146,7 @@ EXPORT_SYMBOL(fw_iso_buffer_destroy); /* Convert DMA address to offset into virtually contiguous buffer. */ size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed) { - int i; + size_t i; dma_addr_t address; ssize_t offset; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 780708dc6e25..87d6f2d2f02d 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -525,9 +525,10 @@ const struct fw_address_region fw_high_memory_region = { .start = 0x000100000000ULL, .end = 0xffffe0000000ULL, }; EXPORT_SYMBOL(fw_high_memory_region); -#if 0 -const struct fw_address_region fw_low_memory_region = +static const struct fw_address_region low_memory_region = { .start = 0x000000000000ULL, .end = 0x000100000000ULL, }; + +#if 0 const struct fw_address_region fw_private_region = { .start = 0xffffe0000000ULL, .end = 0xfffff0000000ULL, }; const struct fw_address_region fw_csr_region = @@ -1198,6 +1199,23 @@ static struct fw_address_handler registers = { .address_callback = handle_registers, }; +static void handle_low_memory(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, int generation, + unsigned long long offset, void *payload, size_t length, + void *callback_data) +{ + /* + * This catches requests not handled by the physical DMA unit, + * i.e., wrong transaction types or unauthorized source nodes. + */ + fw_send_response(card, request, RCODE_TYPE_ERROR); +} + +static struct fw_address_handler low_memory = { + .length = 0x000100000000ULL, + .address_callback = handle_low_memory, +}; + MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>"); MODULE_DESCRIPTION("Core IEEE1394 transaction logic"); MODULE_LICENSE("GPL"); @@ -1259,6 +1277,7 @@ static int __init fw_core_init(void) fw_core_add_address_handler(&topology_map, &topology_map_region); fw_core_add_address_handler(®isters, ®isters_region); + fw_core_add_address_handler(&low_memory, &low_memory_region); fw_core_add_descriptor(&vendor_id_descriptor); fw_core_add_descriptor(&model_id_descriptor); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index c1af05e834b6..c788dbdaf3bc 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -191,6 +191,7 @@ struct fw_ohci { unsigned quirks; unsigned int pri_req_max; u32 bus_time; + bool bus_time_running; bool is_root; bool csr_state_setclear_abdicate; int n_ir; @@ -1726,6 +1727,13 @@ static u32 update_bus_time(struct fw_ohci *ohci) { u32 cycle_time_seconds = get_cycle_time(ohci) >> 25; + if (unlikely(!ohci->bus_time_running)) { + reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_cycle64Seconds); + ohci->bus_time = (lower_32_bits(get_seconds()) & ~0x7f) | + (cycle_time_seconds & 0x40); + ohci->bus_time_running = true; + } + if ((ohci->bus_time & 0x40) != (cycle_time_seconds & 0x40)) ohci->bus_time += 0x40; @@ -2213,7 +2221,7 @@ static int ohci_enable(struct fw_card *card, { struct fw_ohci *ohci = fw_ohci(card); struct pci_dev *dev = to_pci_dev(card->device); - u32 lps, seconds, version, irqs; + u32 lps, version, irqs; int i, ret; if (software_reset(ohci)) { @@ -2269,9 +2277,12 @@ static int ohci_enable(struct fw_card *card, (OHCI1394_MAX_PHYS_RESP_RETRIES << 8) | (200 << 16)); - seconds = lower_32_bits(get_seconds()); - reg_write(ohci, OHCI1394_IsochronousCycleTimer, seconds << 25); - ohci->bus_time = seconds & ~0x3f; + ohci->bus_time_running = false; + + for (i = 0; i < 32; i++) + if (ohci->ir_context_support & (1 << i)) + reg_write(ohci, OHCI1394_IsoRcvContextControlClear(i), + IR_CONTEXT_MULTI_CHANNEL_MODE); version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; if (version >= OHCI_VERSION_1_1) { @@ -2369,7 +2380,6 @@ static int ohci_enable(struct fw_card *card, OHCI1394_postedWriteErr | OHCI1394_selfIDComplete | OHCI1394_regAccessFail | - OHCI1394_cycle64Seconds | OHCI1394_cycleInconsistent | OHCI1394_unrecoverableError | OHCI1394_cycleTooLong | @@ -2658,7 +2668,8 @@ static void ohci_write_csr(struct fw_card *card, int csr_offset, u32 value) case CSR_BUS_TIME: spin_lock_irqsave(&ohci->lock, flags); - ohci->bus_time = (ohci->bus_time & 0x7f) | (value & ~0x7f); + ohci->bus_time = (update_bus_time(ohci) & 0x40) | + (value & ~0x7f); spin_unlock_irqrestore(&ohci->lock, flags); break; @@ -3539,6 +3550,13 @@ static int __devinit pci_probe(struct pci_dev *dev, INIT_WORK(&ohci->bus_reset_work, bus_reset_work); + if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM) || + pci_resource_len(dev, 0) < OHCI1394_REGISTER_SIZE) { + dev_err(&dev->dev, "invalid MMIO resource\n"); + err = -ENXIO; + goto fail_disable; + } + err = pci_request_region(dev, 0, ohci_driver_name); if (err) { dev_err(&dev->dev, "MMIO resource unavailable\n"); diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c index adc07102a20d..c1cdc9236666 100644 --- a/drivers/firmware/memmap.c +++ b/drivers/firmware/memmap.c @@ -98,7 +98,7 @@ static LIST_HEAD(map_entries); /** * firmware_map_add_entry() - Does the real work to add a firmware memmap entry. * @start: Start of the memory range. - * @end: End of the memory range (inclusive). + * @end: End of the memory range (exclusive). * @type: Type of the memory range. * @entry: Pre-allocated (either kmalloc() or bootmem allocator), uninitialised * entry. @@ -113,7 +113,7 @@ static int firmware_map_add_entry(u64 start, u64 end, BUG_ON(start > end); entry->start = start; - entry->end = end; + entry->end = end - 1; entry->type = type; INIT_LIST_HEAD(&entry->list); kobject_init(&entry->kobj, &memmap_ktype); @@ -148,7 +148,7 @@ static int add_sysfs_fw_map_entry(struct firmware_map_entry *entry) * firmware_map_add_hotplug() - Adds a firmware mapping entry when we do * memory hotplug. * @start: Start of the memory range. - * @end: End of the memory range (inclusive). + * @end: End of the memory range (exclusive) * @type: Type of the memory range. * * Adds a firmware mapping entry. This function is for memory hotplug, it is @@ -175,7 +175,7 @@ int __meminit firmware_map_add_hotplug(u64 start, u64 end, const char *type) /** * firmware_map_add_early() - Adds a firmware mapping entry. * @start: Start of the memory range. - * @end: End of the memory range (inclusive). + * @end: End of the memory range. * @type: Type of the memory range. * * Adds a firmware mapping entry. This function uses the bootmem allocator diff --git a/drivers/firmware/pcdp.c b/drivers/firmware/pcdp.c index 51e0e2d8fac6..a330492e06f9 100644 --- a/drivers/firmware/pcdp.c +++ b/drivers/firmware/pcdp.c @@ -95,7 +95,7 @@ efi_setup_pcdp_console(char *cmdline) if (efi.hcdp == EFI_INVALID_TABLE_ADDR) return -ENODEV; - pcdp = ioremap(efi.hcdp, 4096); + pcdp = early_ioremap(efi.hcdp, 4096); printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, efi.hcdp); if (strstr(cmdline, "console=hcdp")) { @@ -131,6 +131,6 @@ efi_setup_pcdp_console(char *cmdline) } out: - iounmap(pcdp); + early_iounmap(pcdp, 4096); return rc; } diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 502b5ea43f4f..b16c8a72a2e2 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -597,6 +597,13 @@ config GPIO_AB8500 help Select this to enable the AB8500 IC GPIO driver +config GPIO_TPS6586X + bool "TPS6586X GPIO" + depends on MFD_TPS6586X + help + Select this option to enable GPIO driver for the TPS6586X + chip family. + config GPIO_TPS65910 bool "TPS65910 GPIO" depends on MFD_TPS65910 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index d37048105a87..153caceeb053 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o +obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c new file mode 100644 index 000000000000..2526b3bb0fae --- /dev/null +++ b/drivers/gpio/gpio-tps6586x.c @@ -0,0 +1,158 @@ +/* + * TI TPS6586x GPIO driver + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Author: Laxman dewangan <ldewangan@nvidia.com> + * + * Based on tps6586x.c + * Copyright (c) 2010 CompuLab Ltd. + * Mike Rapoport <mike@compulab.co.il> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mfd/tps6586x.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +/* GPIO control registers */ +#define TPS6586X_GPIOSET1 0x5d +#define TPS6586X_GPIOSET2 0x5e + +struct tps6586x_gpio { + struct gpio_chip gpio_chip; + struct device *parent; +}; + +static inline struct tps6586x_gpio *to_tps6586x_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct tps6586x_gpio, gpio_chip); +} + +static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc); + uint8_t val; + int ret; + + ret = tps6586x_read(tps6586x_gpio->parent, TPS6586X_GPIOSET2, &val); + if (ret) + return ret; + + return !!(val & (1 << offset)); +} + +static void tps6586x_gpio_set(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc); + + tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET2, + value << offset, 1 << offset); +} + +static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc); + uint8_t val, mask; + + tps6586x_gpio_set(gc, offset, value); + + val = 0x1 << (offset * 2); + mask = 0x3 << (offset * 2); + + return tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET1, + val, mask); +} + +static int __devinit tps6586x_gpio_probe(struct platform_device *pdev) +{ + struct tps6586x_platform_data *pdata; + struct tps6586x_gpio *tps6586x_gpio; + int ret; + + pdata = dev_get_platdata(pdev->dev.parent); + tps6586x_gpio = devm_kzalloc(&pdev->dev, + sizeof(*tps6586x_gpio), GFP_KERNEL); + if (!tps6586x_gpio) { + dev_err(&pdev->dev, "Could not allocate tps6586x_gpio\n"); + return -ENOMEM; + } + + tps6586x_gpio->parent = pdev->dev.parent; + + tps6586x_gpio->gpio_chip.owner = THIS_MODULE; + tps6586x_gpio->gpio_chip.label = pdev->name; + tps6586x_gpio->gpio_chip.dev = &pdev->dev; + tps6586x_gpio->gpio_chip.ngpio = 4; + tps6586x_gpio->gpio_chip.can_sleep = 1; + + /* FIXME: add handling of GPIOs as dedicated inputs */ + tps6586x_gpio->gpio_chip.direction_output = tps6586x_gpio_output; + tps6586x_gpio->gpio_chip.set = tps6586x_gpio_set; + tps6586x_gpio->gpio_chip.get = tps6586x_gpio_get; + +#ifdef CONFIG_OF_GPIO + tps6586x_gpio->gpio_chip.of_node = pdev->dev.parent->of_node; +#endif + if (pdata && pdata->gpio_base) + tps6586x_gpio->gpio_chip.base = pdata->gpio_base; + else + tps6586x_gpio->gpio_chip.base = -1; + + ret = gpiochip_add(&tps6586x_gpio->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, tps6586x_gpio); + + return ret; +} + +static int __devexit tps6586x_gpio_remove(struct platform_device *pdev) +{ + struct tps6586x_gpio *tps6586x_gpio = platform_get_drvdata(pdev); + + return gpiochip_remove(&tps6586x_gpio->gpio_chip); +} + +static struct platform_driver tps6586x_gpio_driver = { + .driver.name = "tps6586x-gpio", + .driver.owner = THIS_MODULE, + .probe = tps6586x_gpio_probe, + .remove = __devexit_p(tps6586x_gpio_remove), +}; + +static int __init tps6586x_gpio_init(void) +{ + return platform_driver_register(&tps6586x_gpio_driver); +} +subsys_initcall(tps6586x_gpio_init); + +static void __exit tps6586x_gpio_exit(void) +{ + platform_driver_unregister(&tps6586x_gpio_driver); +} +module_exit(tps6586x_gpio_exit); + +MODULE_ALIAS("platform:tps6586x-gpio"); +MODULE_DESCRIPTION("GPIO interface for TPS6586X PMIC"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index bf791fa0e50d..d9568198c300 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -196,7 +196,8 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector, return ret; } -struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector) +static struct drm_encoder *exynos_drm_best_encoder( + struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct exynos_drm_connector *exynos_connector = diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index eaf630dc5dba..84dd099eae3b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -33,7 +33,6 @@ #include "exynos_drm_fbdev.h" static LIST_HEAD(exynos_drm_subdrv_list); -static struct drm_device *drm_dev; static int exynos_drm_subdrv_probe(struct drm_device *dev, struct exynos_drm_subdrv *subdrv) @@ -120,8 +119,6 @@ int exynos_drm_device_register(struct drm_device *dev) if (!dev) return -EINVAL; - drm_dev = dev; - list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) { subdrv->drm_dev = dev; err = exynos_drm_subdrv_probe(dev, subdrv); @@ -149,8 +146,6 @@ int exynos_drm_device_unregister(struct drm_device *dev) list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) exynos_drm_subdrv_remove(dev, subdrv); - drm_dev = NULL; - return 0; } EXPORT_SYMBOL_GPL(exynos_drm_device_unregister); diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 32a34c85899b..abb1e2f8227f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -29,21 +29,23 @@ #include "drmP.h" #include "drm_crtc_helper.h" -#include "exynos_drm_crtc.h" #include "exynos_drm_drv.h" -#include "exynos_drm_fb.h" #include "exynos_drm_encoder.h" -#include "exynos_drm_gem.h" +#include "exynos_drm_plane.h" #define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\ drm_crtc) +enum exynos_crtc_mode { + CRTC_MODE_NORMAL, /* normal mode */ + CRTC_MODE_BLANK, /* The private plane of crtc is blank */ +}; + /* * Exynos specific crtc structure. * * @drm_crtc: crtc object. - * @overlay: contain information common to display controller and hdmi and - * contents of this overlay object would be copied to sub driver size. + * @drm_plane: pointer of private plane object for this crtc * @pipe: a crtc index created at load() with a new crtc object creation * and the crtc object would be set to private->crtc array * to get a crtc object corresponding to this pipe from private->crtc @@ -52,115 +54,16 @@ * we can refer to the crtc to current hardware interrupt occured through * this pipe value. * @dpms: store the crtc dpms value + * @mode: store the crtc mode value */ struct exynos_drm_crtc { struct drm_crtc drm_crtc; - struct exynos_drm_overlay overlay; + struct drm_plane *plane; unsigned int pipe; unsigned int dpms; + enum exynos_crtc_mode mode; }; -static void exynos_drm_crtc_apply(struct drm_crtc *crtc) -{ - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct exynos_drm_overlay *overlay = &exynos_crtc->overlay; - - exynos_drm_fn_encoder(crtc, overlay, - exynos_drm_encoder_crtc_mode_set); - exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe, - exynos_drm_encoder_crtc_commit); -} - -int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, - struct drm_framebuffer *fb, - struct drm_display_mode *mode, - struct exynos_drm_crtc_pos *pos) -{ - struct exynos_drm_gem_buf *buffer; - unsigned int actual_w; - unsigned int actual_h; - int nr = exynos_drm_format_num_buffers(fb->pixel_format); - int i; - - for (i = 0; i < nr; i++) { - buffer = exynos_drm_fb_buffer(fb, i); - if (!buffer) { - DRM_LOG_KMS("buffer is null\n"); - return -EFAULT; - } - - overlay->dma_addr[i] = buffer->dma_addr; - overlay->vaddr[i] = buffer->kvaddr; - - DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n", - i, (unsigned long)overlay->vaddr[i], - (unsigned long)overlay->dma_addr[i]); - } - - actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w); - actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h); - - /* set drm framebuffer data. */ - overlay->fb_x = pos->fb_x; - overlay->fb_y = pos->fb_y; - overlay->fb_width = fb->width; - overlay->fb_height = fb->height; - overlay->src_width = pos->src_w; - overlay->src_height = pos->src_h; - overlay->bpp = fb->bits_per_pixel; - overlay->pitch = fb->pitches[0]; - overlay->pixel_format = fb->pixel_format; - - /* set overlay range to be displayed. */ - overlay->crtc_x = pos->crtc_x; - overlay->crtc_y = pos->crtc_y; - overlay->crtc_width = actual_w; - overlay->crtc_height = actual_h; - - /* set drm mode data. */ - overlay->mode_width = mode->hdisplay; - overlay->mode_height = mode->vdisplay; - overlay->refresh = mode->vrefresh; - overlay->scan_flag = mode->flags; - - DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)", - overlay->crtc_x, overlay->crtc_y, - overlay->crtc_width, overlay->crtc_height); - - return 0; -} - -static int exynos_drm_crtc_update(struct drm_crtc *crtc) -{ - struct exynos_drm_crtc *exynos_crtc; - struct exynos_drm_overlay *overlay; - struct exynos_drm_crtc_pos pos; - struct drm_display_mode *mode = &crtc->mode; - struct drm_framebuffer *fb = crtc->fb; - - if (!mode || !fb) - return -EINVAL; - - exynos_crtc = to_exynos_crtc(crtc); - overlay = &exynos_crtc->overlay; - - memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos)); - - /* it means the offset of framebuffer to be displayed. */ - pos.fb_x = crtc->x; - pos.fb_y = crtc->y; - - /* OSD position to be displayed. */ - pos.crtc_x = 0; - pos.crtc_y = 0; - pos.crtc_w = fb->width - crtc->x; - pos.crtc_h = fb->height - crtc->y; - pos.src_w = pos.crtc_w; - pos.src_h = pos.crtc_h; - - return exynos_drm_overlay_update(overlay, crtc->fb, mode, &pos); -} - static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) { struct drm_device *dev = crtc->dev; @@ -175,23 +78,8 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) mutex_lock(&dev->struct_mutex); - switch (mode) { - case DRM_MODE_DPMS_ON: - exynos_drm_fn_encoder(crtc, &mode, - exynos_drm_encoder_crtc_dpms); - exynos_crtc->dpms = mode; - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - exynos_drm_fn_encoder(crtc, &mode, - exynos_drm_encoder_crtc_dpms); - exynos_crtc->dpms = mode; - break; - default: - DRM_ERROR("unspecified mode %d\n", mode); - break; - } + exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms); + exynos_crtc->dpms = mode; mutex_unlock(&dev->struct_mutex); } @@ -209,30 +97,8 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc) DRM_DEBUG_KMS("%s\n", __FILE__); - /* - * when set_crtc is requested from user or at booting time, - * crtc->commit would be called without dpms call so if dpms is - * no power on then crtc->dpms should be called - * with DRM_MODE_DPMS_ON for the hardware power to be on. - */ - if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) { - int mode = DRM_MODE_DPMS_ON; - - /* - * enable hardware(power on) to all encoders hdmi connected - * to current crtc. - */ - exynos_drm_crtc_dpms(crtc, mode); - /* - * enable dma to all encoders connected to current crtc and - * lcd panel. - */ - exynos_drm_fn_encoder(crtc, &mode, - exynos_drm_encoder_dpms_from_crtc); - } - - exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe, - exynos_drm_encoder_crtc_commit); + exynos_plane_commit(exynos_crtc->plane); + exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON); } static bool @@ -251,31 +117,61 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb) { + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_plane *plane = exynos_crtc->plane; + unsigned int crtc_w; + unsigned int crtc_h; + int pipe = exynos_crtc->pipe; + int ret; + DRM_DEBUG_KMS("%s\n", __FILE__); + exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON); + /* * copy the mode data adjusted by mode_fixup() into crtc->mode * so that hardware can be seet to proper mode. */ memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode)); - return exynos_drm_crtc_update(crtc); + crtc_w = crtc->fb->width - x; + crtc_h = crtc->fb->height - y; + + ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h, + x, y, crtc_w, crtc_h); + if (ret) + return ret; + + plane->crtc = crtc; + plane->fb = crtc->fb; + + exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe); + + return 0; } static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_plane *plane = exynos_crtc->plane; + unsigned int crtc_w; + unsigned int crtc_h; int ret; DRM_DEBUG_KMS("%s\n", __FILE__); - ret = exynos_drm_crtc_update(crtc); + crtc_w = crtc->fb->width - x; + crtc_h = crtc->fb->height - y; + + ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h, + x, y, crtc_w, crtc_h); if (ret) return ret; - exynos_drm_crtc_apply(crtc); + exynos_drm_crtc_commit(crtc); - return ret; + return 0; } static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc) @@ -284,6 +180,16 @@ static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc) /* drm framework doesn't check NULL */ } +static void exynos_drm_crtc_disable(struct drm_crtc *crtc) +{ + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF); + exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); +} + static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { .dpms = exynos_drm_crtc_dpms, .prepare = exynos_drm_crtc_prepare, @@ -292,6 +198,7 @@ static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { .mode_set = exynos_drm_crtc_mode_set, .mode_set_base = exynos_drm_crtc_mode_set_base, .load_lut = exynos_drm_crtc_load_lut, + .disable = exynos_drm_crtc_disable, }; static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, @@ -327,7 +234,8 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, &dev_priv->pageflip_event_list); crtc->fb = fb; - ret = exynos_drm_crtc_update(crtc); + ret = exynos_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y, + NULL); if (ret) { crtc->fb = old_fb; drm_vblank_put(dev, exynos_crtc->pipe); @@ -335,14 +243,6 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, goto out; } - - /* - * the values related to a buffer of the drm framebuffer - * to be applied should be set at here. because these values - * first, are set to shadow registers and then to - * real registers at vsync front porch period. - */ - exynos_drm_crtc_apply(crtc); } out: mutex_unlock(&dev->struct_mutex); @@ -362,18 +262,73 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) kfree(exynos_crtc); } +static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, + struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = crtc->dev; + struct exynos_drm_private *dev_priv = dev->dev_private; + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + + DRM_DEBUG_KMS("%s\n", __func__); + + if (property == dev_priv->crtc_mode_property) { + enum exynos_crtc_mode mode = val; + + if (mode == exynos_crtc->mode) + return 0; + + exynos_crtc->mode = mode; + + switch (mode) { + case CRTC_MODE_NORMAL: + exynos_drm_crtc_commit(crtc); + break; + case CRTC_MODE_BLANK: + exynos_plane_dpms(exynos_crtc->plane, + DRM_MODE_DPMS_OFF); + break; + default: + break; + } + + return 0; + } + + return -EINVAL; +} + static struct drm_crtc_funcs exynos_crtc_funcs = { .set_config = drm_crtc_helper_set_config, .page_flip = exynos_drm_crtc_page_flip, .destroy = exynos_drm_crtc_destroy, + .set_property = exynos_drm_crtc_set_property, +}; + +static const struct drm_prop_enum_list mode_names[] = { + { CRTC_MODE_NORMAL, "normal" }, + { CRTC_MODE_BLANK, "blank" }, }; -struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev, - struct drm_crtc *crtc) +static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc) { - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct exynos_drm_private *dev_priv = dev->dev_private; + struct drm_property *prop; - return &exynos_crtc->overlay; + DRM_DEBUG_KMS("%s\n", __func__); + + prop = dev_priv->crtc_mode_property; + if (!prop) { + prop = drm_property_create_enum(dev, 0, "mode", mode_names, + ARRAY_SIZE(mode_names)); + if (!prop) + return; + + dev_priv->crtc_mode_property = prop; + } + + drm_object_attach_property(&crtc->base, prop, 0); } int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) @@ -392,7 +347,12 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) exynos_crtc->pipe = nr; exynos_crtc->dpms = DRM_MODE_DPMS_OFF; - exynos_crtc->overlay.zpos = DEFAULT_ZPOS; + exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true); + if (!exynos_crtc->plane) { + kfree(exynos_crtc); + return -ENOMEM; + } + crtc = &exynos_crtc->drm_crtc; private->crtc[nr] = crtc; @@ -400,6 +360,8 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) drm_crtc_init(dev, crtc, &exynos_crtc_funcs); drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs); + exynos_drm_crtc_attach_mode_property(crtc); + return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index 16b8e2195a0d..6bae8d8c250e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -29,39 +29,8 @@ #ifndef _EXYNOS_DRM_CRTC_H_ #define _EXYNOS_DRM_CRTC_H_ -struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev, - struct drm_crtc *crtc); int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr); int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc); void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc); -/* - * Exynos specific crtc postion structure. - * - * @fb_x: offset x on a framebuffer to be displyed - * - the unit is screen coordinates. - * @fb_y: offset y on a framebuffer to be displayed - * - the unit is screen coordinates. - * @src_w: width of source area to be displayed from a framebuffer. - * @src_h: height of source area to be displayed from a framebuffer. - * @crtc_x: offset x on hardware screen. - * @crtc_y: offset y on hardware screen. - * @crtc_w: width of hardware screen. - * @crtc_h: height of hardware screen. - */ -struct exynos_drm_crtc_pos { - unsigned int fb_x; - unsigned int fb_y; - unsigned int src_w; - unsigned int src_h; - unsigned int crtc_x; - unsigned int crtc_y; - unsigned int crtc_w; - unsigned int crtc_h; -}; - -int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, - struct drm_framebuffer *fb, - struct drm_display_mode *mode, - struct exynos_drm_crtc_pos *pos); #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c index 274909271c36..613bf8a5d9b2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c @@ -25,6 +25,7 @@ #include "drmP.h" #include "drm.h" +#include "exynos_drm.h" #include "exynos_drm_drv.h" #include "exynos_drm_gem.h" @@ -86,6 +87,10 @@ static struct sg_table * npages = buf->size / buf->page_size; sgt = exynos_pages_to_sg(buf->pages, npages, buf->page_size); + if (!sgt) { + DRM_DEBUG_PRIME("exynos_pages_to_sg returned NULL!\n"); + goto err_unlock; + } nents = dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir); DRM_DEBUG_PRIME("npages = %d buffer size = 0x%lx page_size = 0x%lx\n", @@ -186,7 +191,7 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, struct exynos_drm_gem_obj *exynos_gem_obj; struct exynos_drm_gem_buf *buffer; struct page *page; - int ret, i = 0; + int ret; DRM_DEBUG_PRIME("%s\n", __FILE__); @@ -210,7 +215,7 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); - if (IS_ERR(sgt)) { + if (IS_ERR_OR_NULL(sgt)) { ret = PTR_ERR(sgt); goto err_buf_detach; } @@ -236,13 +241,25 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, } sgl = sgt->sgl; - buffer->dma_addr = sg_dma_address(sgl); - while (i < sgt->nents) { - buffer->pages[i] = sg_page(sgl); - buffer->size += sg_dma_len(sgl); - sgl = sg_next(sgl); - i++; + if (sgt->nents == 1) { + buffer->dma_addr = sg_dma_address(sgt->sgl); + buffer->size = sg_dma_len(sgt->sgl); + + /* always physically continuous memory if sgt->nents is 1. */ + exynos_gem_obj->flags |= EXYNOS_BO_CONTIG; + } else { + unsigned int i = 0; + + buffer->dma_addr = sg_dma_address(sgl); + while (i < sgt->nents) { + buffer->pages[i] = sg_page(sgl); + buffer->size += sg_dma_len(sgl); + sgl = sg_next(sgl); + i++; + } + + exynos_gem_obj->flags |= EXYNOS_BO_NONCONTIG; } exynos_gem_obj->buffer = buffer; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index d6de2e07fa03..ebacec6f1e48 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -85,8 +85,11 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) } for (nr = 0; nr < MAX_PLANE; nr++) { - ret = exynos_plane_init(dev, nr); - if (ret) + struct drm_plane *plane; + unsigned int possible_crtcs = (1 << MAX_CRTC) - 1; + + plane = exynos_plane_init(dev, possible_crtcs, false); + if (!plane) goto err_crtc; } @@ -221,8 +224,6 @@ static struct drm_ioctl_desc exynos_ioctls[] = { exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, exynos_drm_gem_get_ioctl, DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, exynos_plane_set_zpos_ioctl, - DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 277653d5fda0..e22704b249d7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -59,12 +59,14 @@ enum exynos_drm_output_type { * * @mode_set: copy drm overlay info to hw specific overlay info. * @commit: apply hardware specific overlay data to registers. + * @enable: enable hardware specific overlay. * @disable: disable hardware specific overlay. */ struct exynos_drm_overlay_ops { void (*mode_set)(struct device *subdrv_dev, struct exynos_drm_overlay *overlay); void (*commit)(struct device *subdrv_dev, int zpos); + void (*enable)(struct device *subdrv_dev, int zpos); void (*disable)(struct device *subdrv_dev, int zpos); }; @@ -235,6 +237,8 @@ struct exynos_drm_private { * this array is used to be aware of which crtc did it request vblank. */ struct drm_crtc *crtc[MAX_CRTC]; + struct drm_property *plane_zpos_property; + struct drm_property *crtc_mode_property; }; /* diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index 4a13a747f5d4..2c037cd7d2d4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -30,7 +30,6 @@ #include "drm_crtc_helper.h" #include "exynos_drm_drv.h" -#include "exynos_drm_crtc.h" #include "exynos_drm_encoder.h" #define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\ @@ -136,21 +135,16 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, struct drm_connector *connector; struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); struct exynos_drm_manager_ops *manager_ops = manager->ops; - struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; - struct exynos_drm_overlay *overlay = get_exynos_drm_overlay(dev, - encoder->crtc); DRM_DEBUG_KMS("%s\n", __FILE__); + exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder == encoder) { + if (connector->encoder == encoder) if (manager_ops && manager_ops->mode_set) manager_ops->mode_set(manager->dev, adjusted_mode); - - if (overlay_ops && overlay_ops->mode_set) - overlay_ops->mode_set(manager->dev, overlay); - } } } @@ -310,8 +304,8 @@ void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data) struct exynos_drm_manager_ops *manager_ops = manager->ops; int crtc = *(int *)data; - if (manager->pipe == -1) - manager->pipe = crtc; + if (manager->pipe != crtc) + return; if (manager_ops->enable_vblank) manager_ops->enable_vblank(manager->dev); @@ -324,34 +318,41 @@ void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data) struct exynos_drm_manager_ops *manager_ops = manager->ops; int crtc = *(int *)data; - if (manager->pipe == -1) - manager->pipe = crtc; + if (manager->pipe != crtc) + return; if (manager_ops->disable_vblank) manager_ops->disable_vblank(manager->dev); } -void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder, - void *data) +void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data) { - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; - int zpos = DEFAULT_ZPOS; + struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); + struct exynos_drm_manager *manager = exynos_encoder->manager; + struct exynos_drm_manager_ops *manager_ops = manager->ops; + int mode = *(int *)data; - if (data) - zpos = *(int *)data; + DRM_DEBUG_KMS("%s\n", __FILE__); - if (overlay_ops && overlay_ops->commit) - overlay_ops->commit(manager->dev, zpos); + if (manager_ops && manager_ops->dpms) + manager_ops->dpms(manager->dev, mode); + + /* + * if this condition is ok then it means that the crtc is already + * detached from encoder and last function for detaching is properly + * done, so clear pipe from manager to prevent repeated call. + */ + if (mode > DRM_MODE_DPMS_ON) { + if (!encoder->crtc) + manager->pipe = -1; + } } -void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data) { struct exynos_drm_manager *manager = to_exynos_encoder(encoder)->manager; - int crtc = *(int *)data; - int zpos = DEFAULT_ZPOS; + int pipe = *(int *)data; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -359,76 +360,62 @@ void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data) * when crtc is detached from encoder, this pipe is used * to select manager operation */ - manager->pipe = crtc; - - exynos_drm_encoder_crtc_plane_commit(encoder, &zpos); + manager->pipe = pipe; } -void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data) { - struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); - int mode = *(int *)data; + struct exynos_drm_manager *manager = + to_exynos_encoder(encoder)->manager; + struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; + struct exynos_drm_overlay *overlay = data; DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_drm_encoder_dpms(encoder, mode); - - exynos_encoder->dpms = mode; + if (overlay_ops && overlay_ops->mode_set) + overlay_ops->mode_set(manager->dev, overlay); } -void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data) { - struct drm_device *dev = encoder->dev; - struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); - struct exynos_drm_manager *manager = exynos_encoder->manager; - struct exynos_drm_manager_ops *manager_ops = manager->ops; - struct drm_connector *connector; - int mode = *(int *)data; + struct exynos_drm_manager *manager = + to_exynos_encoder(encoder)->manager; + struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; + int zpos = DEFAULT_ZPOS; DRM_DEBUG_KMS("%s\n", __FILE__); - if (manager_ops && manager_ops->dpms) - manager_ops->dpms(manager->dev, mode); - - /* - * set current dpms mode to the connector connected to - * current encoder. connector->dpms would be checked - * at drm_helper_connector_dpms() - */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) - if (connector->encoder == encoder) - connector->dpms = mode; + if (data) + zpos = *(int *)data; - /* - * if this condition is ok then it means that the crtc is already - * detached from encoder and last function for detaching is properly - * done, so clear pipe from manager to prevent repeated call. - */ - if (mode > DRM_MODE_DPMS_ON) { - if (!encoder->crtc) - manager->pipe = -1; - } + if (overlay_ops && overlay_ops->commit) + overlay_ops->commit(manager->dev, zpos); } -void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data) { struct exynos_drm_manager *manager = to_exynos_encoder(encoder)->manager; struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; - struct exynos_drm_overlay *overlay = data; + int zpos = DEFAULT_ZPOS; - if (overlay_ops && overlay_ops->mode_set) - overlay_ops->mode_set(manager->dev, overlay); + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (data) + zpos = *(int *)data; + + if (overlay_ops && overlay_ops->enable) + overlay_ops->enable(manager->dev, zpos); } -void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data) { struct exynos_drm_manager *manager = to_exynos_encoder(encoder)->manager; struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; int zpos = DEFAULT_ZPOS; - DRM_DEBUG_KMS("\n"); + DRM_DEBUG_KMS("%s\n", __FILE__); if (data) zpos = *(int *)data; diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h index eb7d2316847e..6470d9ddf5a1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h @@ -40,13 +40,11 @@ void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data, void (*fn)(struct drm_encoder *, void *)); void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data); void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder, - void *data); -void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, - void *data); void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data); +void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data); +void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data); +void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data); +void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data); +void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data); #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 29fdbfeb43cb..a68d2b313f03 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -78,7 +78,6 @@ struct fimd_context { struct drm_crtc *crtc; struct clk *bus_clk; struct clk *lcd_clk; - struct resource *regs_res; void __iomem *regs; struct fimd_win_data win_data[WINDOWS_NR]; unsigned int clkdiv; @@ -813,7 +812,7 @@ static int __devinit fimd_probe(struct platform_device *pdev) return -EINVAL; } - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; @@ -838,33 +837,26 @@ static int __devinit fimd_probe(struct platform_device *pdev) goto err_clk; } - ctx->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(dev)); - if (!ctx->regs_res) { - dev_err(dev, "failed to claim register region\n"); - ret = -ENOENT; - goto err_clk; - } - - ctx->regs = ioremap(res->start, resource_size(res)); + ctx->regs = devm_request_and_ioremap(&pdev->dev, res); if (!ctx->regs) { dev_err(dev, "failed to map registers\n"); ret = -ENXIO; - goto err_req_region_io; + goto err_clk; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(dev, "irq request failed.\n"); - goto err_req_region_irq; + goto err_clk; } ctx->irq = res->start; - ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx); - if (ret < 0) { + ret = devm_request_irq(&pdev->dev, ctx->irq, fimd_irq_handler, + 0, "drm_fimd", ctx); + if (ret) { dev_err(dev, "irq request failed.\n"); - goto err_req_irq; + goto err_clk; } ctx->vidcon0 = pdata->vidcon0; @@ -899,14 +891,6 @@ static int __devinit fimd_probe(struct platform_device *pdev) return 0; -err_req_irq: -err_req_region_irq: - iounmap(ctx->regs); - -err_req_region_io: - release_resource(ctx->regs_res); - kfree(ctx->regs_res); - err_clk: clk_disable(ctx->lcd_clk); clk_put(ctx->lcd_clk); @@ -916,7 +900,6 @@ err_bus_clk: clk_put(ctx->bus_clk); err_clk_get: - kfree(ctx); return ret; } @@ -944,13 +927,6 @@ out: clk_put(ctx->lcd_clk); clk_put(ctx->bus_clk); - iounmap(ctx->regs); - release_resource(ctx->regs_res); - kfree(ctx->regs_res); - free_irq(ctx->irq, ctx); - - kfree(ctx); - return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 5c8b683029ea..f9efde40c097 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -99,25 +99,17 @@ out: struct page **exynos_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) { - struct inode *inode; - struct address_space *mapping; struct page *p, **pages; int i, npages; - /* This is the shared memory object that backs the GEM resource */ - inode = obj->filp->f_path.dentry->d_inode; - mapping = inode->i_mapping; - npages = obj->size >> PAGE_SHIFT; pages = drm_malloc_ab(npages, sizeof(struct page *)); if (pages == NULL) return ERR_PTR(-ENOMEM); - gfpmask |= mapping_gfp_mask(mapping); - for (i = 0; i < npages; i++) { - p = shmem_read_mapping_page_gfp(mapping, i, gfpmask); + p = alloc_page(gfpmask); if (IS_ERR(p)) goto fail; pages[i] = p; @@ -126,31 +118,22 @@ struct page **exynos_gem_get_pages(struct drm_gem_object *obj, return pages; fail: - while (i--) - page_cache_release(pages[i]); + while (--i) + __free_page(pages[i]); drm_free_large(pages); return ERR_PTR(PTR_ERR(p)); } static void exynos_gem_put_pages(struct drm_gem_object *obj, - struct page **pages, - bool dirty, bool accessed) + struct page **pages) { - int i, npages; + int npages; npages = obj->size >> PAGE_SHIFT; - for (i = 0; i < npages; i++) { - if (dirty) - set_page_dirty(pages[i]); - - if (accessed) - mark_page_accessed(pages[i]); - - /* Undo the reference we took when populating the table */ - page_cache_release(pages[i]); - } + while (--npages >= 0) + __free_page(pages[npages]); drm_free_large(pages); } @@ -189,7 +172,7 @@ static int exynos_drm_gem_get_pages(struct drm_gem_object *obj) return -EINVAL; } - pages = exynos_gem_get_pages(obj, GFP_KERNEL); + pages = exynos_gem_get_pages(obj, GFP_HIGHUSER_MOVABLE); if (IS_ERR(pages)) { DRM_ERROR("failed to get pages.\n"); return PTR_ERR(pages); @@ -230,7 +213,7 @@ err1: kfree(buf->sgt); buf->sgt = NULL; err: - exynos_gem_put_pages(obj, pages, true, false); + exynos_gem_put_pages(obj, pages); return ret; } @@ -248,7 +231,7 @@ static void exynos_drm_gem_put_pages(struct drm_gem_object *obj) kfree(buf->sgt); buf->sgt = NULL; - exynos_gem_put_pages(obj, buf->pages, true, false); + exynos_gem_put_pages(obj, buf->pages); buf->pages = NULL; /* add some codes for UNCACHED type here. TODO */ @@ -291,11 +274,21 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) if (!buf->pages) return; + /* + * do not release memory region from exporter. + * + * the region will be released by exporter + * once dmabuf's refcount becomes 0. + */ + if (obj->import_attach) + goto out; + if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) exynos_drm_gem_put_pages(obj); else exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, buf); +out: exynos_drm_fini_buf(obj->dev, buf); exynos_gem_obj->buffer = NULL; @@ -668,7 +661,7 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv, * with DRM_IOCTL_MODE_CREATE_DUMB command. */ - args->pitch = args->width * args->bpp >> 3; + args->pitch = args->width * ((args->bpp + 7) / 8); args->size = PAGE_ALIGN(args->pitch * args->height); exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 14d038b6cb02..085b2a5d5f70 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -63,7 +63,8 @@ struct exynos_drm_gem_buf { * by user request or at framebuffer creation. * continuous memory region allocated by user request * or at framebuffer creation. - * @size: total memory size to physically non-continuous memory region. + * @size: size requested from user, in bytes and this size is aligned + * in page unit. * @flags: indicate memory type to allocated buffer and cache attruibute. * * P.S. this object would be transfered to user as kms_bo.handle so diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index c4c6525d4653..b89829e5043a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -12,9 +12,12 @@ #include "drmP.h" #include "exynos_drm.h" -#include "exynos_drm_crtc.h" #include "exynos_drm_drv.h" #include "exynos_drm_encoder.h" +#include "exynos_drm_fb.h" +#include "exynos_drm_gem.h" + +#define to_exynos_plane(x) container_of(x, struct exynos_plane, base) struct exynos_plane { struct drm_plane base; @@ -30,6 +33,108 @@ static const uint32_t formats[] = { DRM_FORMAT_NV12MT, }; +int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct exynos_plane *exynos_plane = to_exynos_plane(plane); + struct exynos_drm_overlay *overlay = &exynos_plane->overlay; + unsigned int actual_w; + unsigned int actual_h; + int nr; + int i; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + nr = exynos_drm_format_num_buffers(fb->pixel_format); + for (i = 0; i < nr; i++) { + struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i); + + if (!buffer) { + DRM_LOG_KMS("buffer is null\n"); + return -EFAULT; + } + + overlay->dma_addr[i] = buffer->dma_addr; + overlay->vaddr[i] = buffer->kvaddr; + + DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n", + i, (unsigned long)overlay->vaddr[i], + (unsigned long)overlay->dma_addr[i]); + } + + actual_w = min((unsigned)(crtc->mode.hdisplay - crtc_x), crtc_w); + actual_h = min((unsigned)(crtc->mode.vdisplay - crtc_y), crtc_h); + + /* set drm framebuffer data. */ + overlay->fb_x = src_x; + overlay->fb_y = src_y; + overlay->fb_width = fb->width; + overlay->fb_height = fb->height; + overlay->src_width = src_w; + overlay->src_height = src_h; + overlay->bpp = fb->bits_per_pixel; + overlay->pitch = fb->pitches[0]; + overlay->pixel_format = fb->pixel_format; + + /* set overlay range to be displayed. */ + overlay->crtc_x = crtc_x; + overlay->crtc_y = crtc_y; + overlay->crtc_width = actual_w; + overlay->crtc_height = actual_h; + + /* set drm mode data. */ + overlay->mode_width = crtc->mode.hdisplay; + overlay->mode_height = crtc->mode.vdisplay; + overlay->refresh = crtc->mode.vrefresh; + overlay->scan_flag = crtc->mode.flags; + + DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)", + overlay->crtc_x, overlay->crtc_y, + overlay->crtc_width, overlay->crtc_height); + + exynos_drm_fn_encoder(crtc, overlay, exynos_drm_encoder_plane_mode_set); + + return 0; +} + +void exynos_plane_commit(struct drm_plane *plane) +{ + struct exynos_plane *exynos_plane = to_exynos_plane(plane); + struct exynos_drm_overlay *overlay = &exynos_plane->overlay; + + exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, + exynos_drm_encoder_plane_commit); +} + +void exynos_plane_dpms(struct drm_plane *plane, int mode) +{ + struct exynos_plane *exynos_plane = to_exynos_plane(plane); + struct exynos_drm_overlay *overlay = &exynos_plane->overlay; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + if (mode == DRM_MODE_DPMS_ON) { + if (exynos_plane->enabled) + return; + + exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, + exynos_drm_encoder_plane_enable); + + exynos_plane->enabled = true; + } else { + if (!exynos_plane->enabled) + return; + + exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, + exynos_drm_encoder_plane_disable); + + exynos_plane->enabled = false; + } +} + static int exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, @@ -37,64 +142,37 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) { - struct exynos_plane *exynos_plane = - container_of(plane, struct exynos_plane, base); - struct exynos_drm_overlay *overlay = &exynos_plane->overlay; - struct exynos_drm_crtc_pos pos; int ret; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos)); - pos.crtc_x = crtc_x; - pos.crtc_y = crtc_y; - pos.crtc_w = crtc_w; - pos.crtc_h = crtc_h; - - /* considering 16.16 fixed point of source values */ - pos.fb_x = src_x >> 16; - pos.fb_y = src_y >> 16; - pos.src_w = src_w >> 16; - pos.src_h = src_h >> 16; - - ret = exynos_drm_overlay_update(overlay, fb, &crtc->mode, &pos); + ret = exynos_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, + crtc_w, crtc_h, src_x >> 16, src_y >> 16, + src_w >> 16, src_h >> 16); if (ret < 0) return ret; - exynos_drm_fn_encoder(crtc, overlay, - exynos_drm_encoder_crtc_mode_set); - exynos_drm_fn_encoder(crtc, &overlay->zpos, - exynos_drm_encoder_crtc_plane_commit); + plane->crtc = crtc; + plane->fb = crtc->fb; - exynos_plane->enabled = true; + exynos_plane_commit(plane); + exynos_plane_dpms(plane, DRM_MODE_DPMS_ON); return 0; } static int exynos_disable_plane(struct drm_plane *plane) { - struct exynos_plane *exynos_plane = - container_of(plane, struct exynos_plane, base); - struct exynos_drm_overlay *overlay = &exynos_plane->overlay; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (!exynos_plane->enabled) - return 0; - - exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, - exynos_drm_encoder_crtc_disable); - - exynos_plane->enabled = false; - exynos_plane->overlay.zpos = DEFAULT_ZPOS; + exynos_plane_dpms(plane, DRM_MODE_DPMS_OFF); return 0; } static void exynos_plane_destroy(struct drm_plane *plane) { - struct exynos_plane *exynos_plane = - container_of(plane, struct exynos_plane, base); + struct exynos_plane *exynos_plane = to_exynos_plane(plane); DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -103,69 +181,79 @@ static void exynos_plane_destroy(struct drm_plane *plane) kfree(exynos_plane); } +static int exynos_plane_set_property(struct drm_plane *plane, + struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = plane->dev; + struct exynos_plane *exynos_plane = to_exynos_plane(plane); + struct exynos_drm_private *dev_priv = dev->dev_private; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + if (property == dev_priv->plane_zpos_property) { + exynos_plane->overlay.zpos = val; + return 0; + } + + return -EINVAL; +} + static struct drm_plane_funcs exynos_plane_funcs = { .update_plane = exynos_update_plane, .disable_plane = exynos_disable_plane, .destroy = exynos_plane_destroy, + .set_property = exynos_plane_set_property, }; -int exynos_plane_init(struct drm_device *dev, unsigned int nr) +static void exynos_plane_attach_zpos_property(struct drm_plane *plane) { - struct exynos_plane *exynos_plane; - uint32_t possible_crtcs; + struct drm_device *dev = plane->dev; + struct exynos_drm_private *dev_priv = dev->dev_private; + struct drm_property *prop; - exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL); - if (!exynos_plane) - return -ENOMEM; + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - /* all CRTCs are available */ - possible_crtcs = (1 << MAX_CRTC) - 1; + prop = dev_priv->plane_zpos_property; + if (!prop) { + prop = drm_property_create_range(dev, 0, "zpos", 0, + MAX_PLANE - 1); + if (!prop) + return; - exynos_plane->overlay.zpos = DEFAULT_ZPOS; + dev_priv->plane_zpos_property = prop; + } - return drm_plane_init(dev, &exynos_plane->base, possible_crtcs, - &exynos_plane_funcs, formats, ARRAY_SIZE(formats), - false); + drm_object_attach_property(&plane->base, prop, 0); } -int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) +struct drm_plane *exynos_plane_init(struct drm_device *dev, + unsigned int possible_crtcs, bool priv) { - struct drm_exynos_plane_set_zpos *zpos_req = data; - struct drm_mode_object *obj; - struct drm_plane *plane; struct exynos_plane *exynos_plane; - int ret = 0; + int err; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (zpos_req->zpos < 0 || zpos_req->zpos >= MAX_PLANE) { - if (zpos_req->zpos != DEFAULT_ZPOS) { - DRM_ERROR("zpos not within limits\n"); - return -EINVAL; - } + exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL); + if (!exynos_plane) { + DRM_ERROR("failed to allocate plane\n"); + return NULL; } - mutex_lock(&dev->mode_config.mutex); - - obj = drm_mode_object_find(dev, zpos_req->plane_id, - DRM_MODE_OBJECT_PLANE); - if (!obj) { - DRM_DEBUG_KMS("Unknown plane ID %d\n", - zpos_req->plane_id); - ret = -EINVAL; - goto out; + err = drm_plane_init(dev, &exynos_plane->base, possible_crtcs, + &exynos_plane_funcs, formats, ARRAY_SIZE(formats), + priv); + if (err) { + DRM_ERROR("failed to initialize plane\n"); + kfree(exynos_plane); + return NULL; } - plane = obj_to_plane(obj); - exynos_plane = container_of(plane, struct exynos_plane, base); - - exynos_plane->overlay.zpos = zpos_req->zpos; + if (priv) + exynos_plane->overlay.zpos = DEFAULT_ZPOS; + else + exynos_plane_attach_zpos_property(&exynos_plane->base); -out: - mutex_unlock(&dev->mode_config.mutex); - return ret; + return &exynos_plane->base; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.h b/drivers/gpu/drm/exynos/exynos_drm_plane.h index 16b71f8217e7..88312458580d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.h +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.h @@ -9,6 +9,12 @@ * */ -int exynos_plane_init(struct drm_device *dev, unsigned int nr); -int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); +int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h); +void exynos_plane_commit(struct drm_plane *plane); +void exynos_plane_dpms(struct drm_plane *plane, int mode); +struct drm_plane *exynos_plane_init(struct drm_device *dev, + unsigned int possible_crtcs, bool priv); diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 7b9c153dceb6..bb1550c4dd57 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -85,8 +85,6 @@ static const char fake_edid_info[] = { 0x00, 0x00, 0x00, 0x06 }; -static void vidi_fake_vblank_handler(struct work_struct *work); - static bool vidi_display_is_connected(struct device *dev) { struct vidi_context *ctx = get_vidi_context(dev); @@ -531,6 +529,16 @@ static int vidi_store_connection(struct device *dev, if (ctx->connected > 1) return -EINVAL; + /* use fake edid data for test. */ + if (!ctx->raw_edid) + ctx->raw_edid = (struct edid *)fake_edid_info; + + /* if raw_edid isn't same as fake data then it can't be tested. */ + if (ctx->raw_edid != (struct edid *)fake_edid_info) { + DRM_DEBUG_KMS("edid data is not fake data.\n"); + return -EINVAL; + } + DRM_DEBUG_KMS("requested connection.\n"); drm_helper_hpd_irq_event(ctx->subdrv.drm_dev); @@ -549,6 +557,8 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, struct exynos_drm_manager *manager; struct exynos_drm_display_ops *display_ops; struct drm_exynos_vidi_connection *vidi = data; + struct edid *raw_edid; + int edid_len; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -557,11 +567,6 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, return -EINVAL; } - if (!vidi->edid) { - DRM_DEBUG_KMS("edid data is null.\n"); - return -EINVAL; - } - if (vidi->connection > 1) { DRM_DEBUG_KMS("connection should be 0 or 1.\n"); return -EINVAL; @@ -588,8 +593,30 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, return -EINVAL; } - if (vidi->connection) - ctx->raw_edid = (struct edid *)vidi->edid; + if (vidi->connection) { + if (!vidi->edid) { + DRM_DEBUG_KMS("edid data is null.\n"); + return -EINVAL; + } + raw_edid = (struct edid *)(uint32_t)vidi->edid; + edid_len = (1 + raw_edid->extensions) * EDID_LENGTH; + ctx->raw_edid = kzalloc(edid_len, GFP_KERNEL); + if (!ctx->raw_edid) { + DRM_DEBUG_KMS("failed to allocate raw_edid.\n"); + return -ENOMEM; + } + memcpy(ctx->raw_edid, raw_edid, edid_len); + } else { + /* + * with connection = 0, free raw_edid + * only if raw edid data isn't same as fake data. + */ + if (ctx->raw_edid && ctx->raw_edid != + (struct edid *)fake_edid_info) { + kfree(ctx->raw_edid); + ctx->raw_edid = NULL; + } + } ctx->connected = vidi->connection; drm_helper_hpd_irq_event(ctx->subdrv.drm_dev); @@ -614,9 +641,6 @@ static int __devinit vidi_probe(struct platform_device *pdev) INIT_WORK(&ctx->work, vidi_fake_vblank_handler); - /* for test */ - ctx->raw_edid = (struct edid *)fake_edid_info; - subdrv = &ctx->subdrv; subdrv->dev = dev; subdrv->manager = &vidi_manager; @@ -644,6 +668,11 @@ static int __devexit vidi_remove(struct platform_device *pdev) exynos_drm_subdrv_unregister(&ctx->subdrv); + if (ctx->raw_edid != (struct edid *)fake_edid_info) { + kfree(ctx->raw_edid); + ctx->raw_edid = NULL; + } + kfree(ctx); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 066bde3f19c4..409e2ec1207c 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -63,7 +63,6 @@ struct hdmi_context { bool dvi_mode; struct mutex hdmi_mutex; - struct resource *regs_res; void __iomem *regs; unsigned int external_irq; unsigned int internal_irq; @@ -2280,16 +2279,17 @@ static int __devinit hdmi_probe(struct platform_device *pdev) return -EINVAL; } - drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL); + drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx), + GFP_KERNEL); if (!drm_hdmi_ctx) { DRM_ERROR("failed to allocate common hdmi context.\n"); return -ENOMEM; } - hdata = kzalloc(sizeof(struct hdmi_context), GFP_KERNEL); + hdata = devm_kzalloc(&pdev->dev, sizeof(struct hdmi_context), + GFP_KERNEL); if (!hdata) { DRM_ERROR("out of memory\n"); - kfree(drm_hdmi_ctx); return -ENOMEM; } @@ -2318,26 +2318,18 @@ static int __devinit hdmi_probe(struct platform_device *pdev) goto err_resource; } - hdata->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(dev)); - if (!hdata->regs_res) { - DRM_ERROR("failed to claim register region\n"); - ret = -ENOENT; - goto err_resource; - } - - hdata->regs = ioremap(res->start, resource_size(res)); + hdata->regs = devm_request_and_ioremap(&pdev->dev, res); if (!hdata->regs) { DRM_ERROR("failed to map registers\n"); ret = -ENXIO; - goto err_req_region; + goto err_resource; } /* DDC i2c driver */ if (i2c_add_driver(&ddc_driver)) { DRM_ERROR("failed to register ddc i2c driver\n"); ret = -ENOENT; - goto err_iomap; + goto err_resource; } hdata->ddc_port = hdmi_ddc; @@ -2398,16 +2390,9 @@ err_hdmiphy: i2c_del_driver(&hdmiphy_driver); err_ddc: i2c_del_driver(&ddc_driver); -err_iomap: - iounmap(hdata->regs); -err_req_region: - release_mem_region(hdata->regs_res->start, - resource_size(hdata->regs_res)); err_resource: hdmi_resources_cleanup(hdata); err_data: - kfree(hdata); - kfree(drm_hdmi_ctx); return ret; } @@ -2425,18 +2410,11 @@ static int __devexit hdmi_remove(struct platform_device *pdev) hdmi_resources_cleanup(hdata); - iounmap(hdata->regs); - - release_mem_region(hdata->regs_res->start, - resource_size(hdata->regs_res)); - /* hdmiphy i2c driver */ i2c_del_driver(&hdmiphy_driver); /* DDC i2c driver */ i2c_del_driver(&ddc_driver); - kfree(hdata); - return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index e2147a2ddcec..30fcc12f81dd 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -956,7 +956,8 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx, clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi); - mixer_res->mixer_regs = ioremap(res->start, resource_size(res)); + mixer_res->mixer_regs = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); if (mixer_res->mixer_regs == NULL) { dev_err(dev, "register mapping failed.\n"); ret = -ENXIO; @@ -967,38 +968,34 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx, if (res == NULL) { dev_err(dev, "get memory resource failed.\n"); ret = -ENXIO; - goto fail_mixer_regs; + goto fail; } - mixer_res->vp_regs = ioremap(res->start, resource_size(res)); + mixer_res->vp_regs = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); if (mixer_res->vp_regs == NULL) { dev_err(dev, "register mapping failed.\n"); ret = -ENXIO; - goto fail_mixer_regs; + goto fail; } res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq"); if (res == NULL) { dev_err(dev, "get interrupt resource failed.\n"); ret = -ENXIO; - goto fail_vp_regs; + goto fail; } - ret = request_irq(res->start, mixer_irq_handler, 0, "drm_mixer", ctx); + ret = devm_request_irq(&pdev->dev, res->start, mixer_irq_handler, + 0, "drm_mixer", ctx); if (ret) { dev_err(dev, "request interrupt failed.\n"); - goto fail_vp_regs; + goto fail; } mixer_res->irq = res->start; return 0; -fail_vp_regs: - iounmap(mixer_res->vp_regs); - -fail_mixer_regs: - iounmap(mixer_res->mixer_regs); - fail: if (!IS_ERR_OR_NULL(mixer_res->sclk_dac)) clk_put(mixer_res->sclk_dac); @@ -1013,16 +1010,6 @@ fail: return ret; } -static void mixer_resources_cleanup(struct mixer_context *ctx) -{ - struct mixer_resources *res = &ctx->mixer_res; - - free_irq(res->irq, ctx); - - iounmap(res->vp_regs); - iounmap(res->mixer_regs); -} - static int __devinit mixer_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1032,16 +1019,16 @@ static int __devinit mixer_probe(struct platform_device *pdev) dev_info(dev, "probe start\n"); - drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL); + drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx), + GFP_KERNEL); if (!drm_hdmi_ctx) { DRM_ERROR("failed to allocate common hdmi context.\n"); return -ENOMEM; } - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) { DRM_ERROR("failed to alloc mixer context.\n"); - kfree(drm_hdmi_ctx); return -ENOMEM; } @@ -1072,17 +1059,10 @@ fail: static int mixer_remove(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct exynos_drm_hdmi_context *drm_hdmi_ctx = - platform_get_drvdata(pdev); - struct mixer_context *ctx = drm_hdmi_ctx->ctx; - - dev_info(dev, "remove successful\n"); + dev_info(&pdev->dev, "remove successful\n"); pm_runtime_disable(&pdev->dev); - mixer_resources_cleanup(ctx); - return 0; } diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 563c02904ddf..23ab3c496b05 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -927,6 +927,8 @@ static int acpi_power_meter_remove(struct acpi_device *device, int type) return 0; } +#ifdef CONFIG_PM_SLEEP + static int acpi_power_meter_resume(struct device *dev) { struct acpi_power_meter_resource *resource; @@ -944,6 +946,8 @@ static int acpi_power_meter_resume(struct device *dev) return 0; } +#endif /* CONFIG_PM_SLEEP */ + static SIMPLE_DEV_PM_OPS(acpi_power_meter_pm, NULL, acpi_power_meter_resume); static struct acpi_driver acpi_power_meter_driver = { diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 4d937a18fadb..282708860517 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -55,9 +55,9 @@ /* wait up to 32 ms for a status change. */ #define APPLESMC_MIN_WAIT 0x0010 +#define APPLESMC_RETRY_WAIT 0x0100 #define APPLESMC_MAX_WAIT 0x8000 -#define APPLESMC_STATUS_MASK 0x0f #define APPLESMC_READ_CMD 0x10 #define APPLESMC_WRITE_CMD 0x11 #define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12 @@ -162,51 +162,68 @@ static unsigned int key_at_index; static struct workqueue_struct *applesmc_led_wq; /* - * __wait_status - Wait up to 32ms for the status port to get a certain value - * (masked with 0x0f), returning zero if the value is obtained. Callers must + * wait_read - Wait for a byte to appear on SMC port. Callers must * hold applesmc_lock. */ -static int __wait_status(u8 val) +static int wait_read(void) { + u8 status; int us; - - val = val & APPLESMC_STATUS_MASK; - for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { udelay(us); - if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) + status = inb(APPLESMC_CMD_PORT); + /* read: wait for smc to settle */ + if (status & 0x01) return 0; } + pr_warn("wait_read() fail: 0x%02x\n", status); return -EIO; } /* - * special treatment of command port - on newer macbooks, it seems necessary - * to resend the command byte before polling the status again. Callers must - * hold applesmc_lock. + * send_byte - Write to SMC port, retrying when necessary. Callers + * must hold applesmc_lock. */ -static int send_command(u8 cmd) +static int send_byte(u8 cmd, u16 port) { + u8 status; int us; + + outb(cmd, port); for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { - outb(cmd, APPLESMC_CMD_PORT); udelay(us); - if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c) + status = inb(APPLESMC_CMD_PORT); + /* write: wait for smc to settle */ + if (status & 0x02) + continue; + /* ready: cmd accepted, return */ + if (status & 0x04) return 0; + /* timeout: give up */ + if (us << 1 == APPLESMC_MAX_WAIT) + break; + /* busy: long wait and resend */ + udelay(APPLESMC_RETRY_WAIT); + outb(cmd, port); } + + pr_warn("send_byte(0x%02x, 0x%04x) fail: 0x%02x\n", cmd, port, status); return -EIO; } +static int send_command(u8 cmd) +{ + return send_byte(cmd, APPLESMC_CMD_PORT); +} + static int send_argument(const char *key) { int i; - for (i = 0; i < 4; i++) { - outb(key[i], APPLESMC_DATA_PORT); - if (__wait_status(0x04)) + for (i = 0; i < 4; i++) + if (send_byte(key[i], APPLESMC_DATA_PORT)) return -EIO; - } return 0; } @@ -219,11 +236,14 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) return -EIO; } - outb(len, APPLESMC_DATA_PORT); + if (send_byte(len, APPLESMC_DATA_PORT)) { + pr_warn("%.4s: read len fail\n", key); + return -EIO; + } for (i = 0; i < len; i++) { - if (__wait_status(0x05)) { - pr_warn("%.4s: read data fail\n", key); + if (wait_read()) { + pr_warn("%.4s: read data[%d] fail\n", key, i); return -EIO; } buffer[i] = inb(APPLESMC_DATA_PORT); @@ -241,14 +261,16 @@ static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len) return -EIO; } - outb(len, APPLESMC_DATA_PORT); + if (send_byte(len, APPLESMC_DATA_PORT)) { + pr_warn("%.4s: write len fail\n", key); + return -EIO; + } for (i = 0; i < len; i++) { - if (__wait_status(0x04)) { + if (send_byte(buffer[i], APPLESMC_DATA_PORT)) { pr_warn("%s: write data fail\n", key); return -EIO; } - outb(buffer[i], APPLESMC_DATA_PORT); } return 0; diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 637c51c11b44..faa16f80db9c 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -793,7 +793,7 @@ static struct notifier_block coretemp_cpu_notifier __refdata = { .notifier_call = coretemp_cpu_callback, }; -static const struct x86_cpu_id coretemp_ids[] = { +static const struct x86_cpu_id __initconst coretemp_ids[] = { { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM }, {} }; diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index e72ba5d2a824..e21e43c13156 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -57,7 +57,7 @@ static const unsigned short normal_i2c[] = { #define JC42_CFG_EVENT_LOCK (1 << 7) #define JC42_CFG_SHUTDOWN (1 << 8) #define JC42_CFG_HYST_SHIFT 9 -#define JC42_CFG_HYST_MASK 0x03 +#define JC42_CFG_HYST_MASK (0x03 << 9) /* Capabilities */ #define JC42_CAP_RANGE (1 << 2) @@ -287,8 +287,8 @@ static ssize_t show_temp_crit_hyst(struct device *dev, return PTR_ERR(data); temp = jc42_temp_from_reg(data->temp_crit); - hyst = jc42_hysteresis[(data->config >> JC42_CFG_HYST_SHIFT) - & JC42_CFG_HYST_MASK]; + hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK) + >> JC42_CFG_HYST_SHIFT]; return sprintf(buf, "%d\n", temp - hyst); } @@ -302,8 +302,8 @@ static ssize_t show_temp_max_hyst(struct device *dev, return PTR_ERR(data); temp = jc42_temp_from_reg(data->temp_max); - hyst = jc42_hysteresis[(data->config >> JC42_CFG_HYST_SHIFT) - & JC42_CFG_HYST_MASK]; + hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK) + >> JC42_CFG_HYST_SHIFT]; return sprintf(buf, "%d\n", temp - hyst); } @@ -362,8 +362,7 @@ static ssize_t set_temp_crit_hyst(struct device *dev, } mutex_lock(&data->update_lock); - data->config = (data->config - & ~(JC42_CFG_HYST_MASK << JC42_CFG_HYST_SHIFT)) + data->config = (data->config & ~JC42_CFG_HYST_MASK) | (hyst << JC42_CFG_HYST_SHIFT); err = i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, data->config); @@ -535,9 +534,16 @@ static int jc42_remove(struct i2c_client *client) struct jc42_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &jc42_group); - if (data->config != data->orig_config) - i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, - data->orig_config); + + /* Restore original configuration except hysteresis */ + if ((data->config & ~JC42_CFG_HYST_MASK) != + (data->orig_config & ~JC42_CFG_HYST_MASK)) { + int config; + + config = (data->orig_config & ~JC42_CFG_HYST_MASK) + | (data->config & JC42_CFG_HYST_MASK); + i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, config); + } return 0; } diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c index 8689664ef03c..ee4ebc198a94 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c @@ -309,7 +309,7 @@ static struct notifier_block via_cputemp_cpu_notifier __refdata = { .notifier_call = via_cputemp_cpu_callback, }; -static const struct x86_cpu_id cputemp_ids[] = { +static const struct x86_cpu_id __initconst cputemp_ids[] = { { X86_VENDOR_CENTAUR, 6, 0xa, }, /* C7 A */ { X86_VENDOR_CENTAUR, 6, 0xd, }, /* C7 D */ { X86_VENDOR_CENTAUR, 6, 0xf, }, /* Nano */ diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 2e7530a4e7b8..b4aaa1bd6728 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -462,7 +462,7 @@ config I2C_MPC config I2C_MV64XXX tristate "Marvell mv64xxx I2C Controller" - depends on (MV64X60 || PLAT_ORION) && EXPERIMENTAL + depends on (MV64X60 || PLAT_ORION) help If you say yes to this option, support will be included for the built-in I2C interface on the Marvell 64xxx line of host bridges. @@ -483,10 +483,11 @@ config I2C_MXS config I2C_NOMADIK tristate "ST-Ericsson Nomadik/Ux500 I2C Controller" - depends on PLAT_NOMADIK + depends on ARM_AMBA help If you say yes to this option, support will be included for the - I2C interface from ST-Ericsson's Nomadik and Ux500 architectures. + I2C interface from ST-Ericsson's Nomadik and Ux500 architectures, + as well as the STA2X11 PCIe I/O HUB. config I2C_NUC900 tristate "NUC900 I2C Driver" diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 1679deef9c89..e24484beef07 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -279,30 +279,31 @@ static int __devexit at91_i2c_remove(struct platform_device *pdev) /* NOTE: could save a few mA by keeping clock off outside of at91_xfer... */ -static int at91_i2c_suspend(struct platform_device *pdev, pm_message_t mesg) +static int at91_i2c_suspend(struct device *dev) { clk_disable(twi_clk); return 0; } -static int at91_i2c_resume(struct platform_device *pdev) +static int at91_i2c_resume(struct device *dev) { return clk_enable(twi_clk); } +static SIMPLE_DEV_PM_OPS(at91_i2c_pm, at91_i2c_suspend, at91_i2c_resume); +#define AT91_I2C_PM (&at91_i2c_pm) + #else -#define at91_i2c_suspend NULL -#define at91_i2c_resume NULL +#define AT91_I2C_PM NULL #endif static struct platform_driver at91_i2c_driver = { .probe = at91_i2c_probe, .remove = __devexit_p(at91_i2c_remove), - .suspend = at91_i2c_suspend, - .resume = at91_i2c_resume, .driver = { .name = "at91_i2c", .owner = THIS_MODULE, + .pm = AT91_I2C_PM, }, }; diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index cdb59e5b23f7..0cf780fd6ef1 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -25,6 +25,7 @@ #include <asm/blackfin.h> #include <asm/portmux.h> #include <asm/irq.h> +#include <asm/bfin_twi.h> /* SMBus mode*/ #define TWI_I2C_MODE_STANDARD 1 @@ -32,56 +33,6 @@ #define TWI_I2C_MODE_COMBINED 3 #define TWI_I2C_MODE_REPEAT 4 -struct bfin_twi_iface { - int irq; - spinlock_t lock; - char read_write; - u8 command; - u8 *transPtr; - int readNum; - int writeNum; - int cur_mode; - int manual_stop; - int result; - struct i2c_adapter adap; - struct completion complete; - struct i2c_msg *pmsg; - int msg_num; - int cur_msg; - u16 saved_clkdiv; - u16 saved_control; - void __iomem *regs_base; -}; - - -#define DEFINE_TWI_REG(reg, off) \ -static inline u16 read_##reg(struct bfin_twi_iface *iface) \ - { return bfin_read16(iface->regs_base + (off)); } \ -static inline void write_##reg(struct bfin_twi_iface *iface, u16 v) \ - { bfin_write16(iface->regs_base + (off), v); } - -DEFINE_TWI_REG(CLKDIV, 0x00) -DEFINE_TWI_REG(CONTROL, 0x04) -DEFINE_TWI_REG(SLAVE_CTL, 0x08) -DEFINE_TWI_REG(SLAVE_STAT, 0x0C) -DEFINE_TWI_REG(SLAVE_ADDR, 0x10) -DEFINE_TWI_REG(MASTER_CTL, 0x14) -DEFINE_TWI_REG(MASTER_STAT, 0x18) -DEFINE_TWI_REG(MASTER_ADDR, 0x1C) -DEFINE_TWI_REG(INT_STAT, 0x20) -DEFINE_TWI_REG(INT_MASK, 0x24) -DEFINE_TWI_REG(FIFO_CTL, 0x28) -DEFINE_TWI_REG(FIFO_STAT, 0x2C) -DEFINE_TWI_REG(XMT_DATA8, 0x80) -DEFINE_TWI_REG(XMT_DATA16, 0x84) -DEFINE_TWI_REG(RCV_DATA8, 0x88) -DEFINE_TWI_REG(RCV_DATA16, 0x8C) - -static const u16 pin_req[2][3] = { - {P_TWI0_SCL, P_TWI0_SDA, 0}, - {P_TWI1_SCL, P_TWI1_SDA, 0}, -}; - static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface, unsigned short twi_int_status) { @@ -99,7 +50,7 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface, */ else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) write_MASTER_CTL(iface, - read_MASTER_CTL(iface) | MDIR | RSTART); + read_MASTER_CTL(iface) | MDIR); else if (iface->manual_stop) write_MASTER_CTL(iface, read_MASTER_CTL(iface) | STOP); @@ -107,10 +58,10 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface, iface->cur_msg + 1 < iface->msg_num) { if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD) write_MASTER_CTL(iface, - read_MASTER_CTL(iface) | RSTART | MDIR); + read_MASTER_CTL(iface) | MDIR); else write_MASTER_CTL(iface, - (read_MASTER_CTL(iface) | RSTART) & ~MDIR); + read_MASTER_CTL(iface) & ~MDIR); } } if (twi_int_status & RCVSERV) { @@ -130,17 +81,25 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface, } iface->transPtr++; iface->readNum--; - } else if (iface->manual_stop) { - write_MASTER_CTL(iface, - read_MASTER_CTL(iface) | STOP); - } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && - iface->cur_msg + 1 < iface->msg_num) { - if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD) - write_MASTER_CTL(iface, - read_MASTER_CTL(iface) | RSTART | MDIR); - else + } + + if (iface->readNum == 0) { + if (iface->manual_stop) { + /* Temporary workaround to avoid possible bus stall - + * Flush FIFO before issuing the STOP condition + */ + read_RCV_DATA16(iface); write_MASTER_CTL(iface, - (read_MASTER_CTL(iface) | RSTART) & ~MDIR); + read_MASTER_CTL(iface) | STOP); + } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && + iface->cur_msg + 1 < iface->msg_num) { + if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD) + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) | MDIR); + else + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) & ~MDIR); + } } } if (twi_int_status & MERR) { @@ -193,7 +152,8 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface, return; } if (twi_int_status & MCOMP) { - if ((read_MASTER_CTL(iface) & MEN) == 0 && + if (twi_int_status & (XMTSERV | RCVSERV) && + (read_MASTER_CTL(iface) & MEN) == 0 && (iface->cur_mode == TWI_I2C_MODE_REPEAT || iface->cur_mode == TWI_I2C_MODE_COMBINED)) { iface->result = -1; @@ -221,7 +181,7 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface, write_MASTER_CTL(iface, read_MASTER_CTL(iface) & ~RSTART); } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && - iface->cur_msg+1 < iface->msg_num) { + iface->cur_msg + 1 < iface->msg_num) { iface->cur_msg++; iface->transPtr = iface->pmsg[iface->cur_msg].buf; iface->writeNum = iface->readNum = @@ -241,27 +201,29 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface, } } - if (iface->pmsg[iface->cur_msg].len <= 255) - write_MASTER_CTL(iface, + if (iface->pmsg[iface->cur_msg].len <= 255) { + write_MASTER_CTL(iface, (read_MASTER_CTL(iface) & (~(0xff << 6))) | - (iface->pmsg[iface->cur_msg].len << 6)); - else { + (iface->pmsg[iface->cur_msg].len << 6)); + iface->manual_stop = 0; + } else { write_MASTER_CTL(iface, (read_MASTER_CTL(iface) | (0xff << 6))); iface->manual_stop = 1; } - /* remove restart bit and enable master receive */ - write_MASTER_CTL(iface, - read_MASTER_CTL(iface) & ~RSTART); + /* remove restart bit before last message */ + if (iface->cur_msg + 1 == iface->msg_num) + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) & ~RSTART); } else { iface->result = 1; write_INT_MASK(iface, 0); write_MASTER_CTL(iface, 0); } + complete(&iface->complete); } - complete(&iface->complete); } /* Interrupt handler */ @@ -298,8 +260,8 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap, if (!(read_CONTROL(iface) & TWI_ENA)) return -ENXIO; - while (read_MASTER_STAT(iface) & BUSBUSY) - yield(); + if (read_MASTER_STAT(iface) & BUSBUSY) + return -EAGAIN; iface->pmsg = msgs; iface->msg_num = num; @@ -311,7 +273,8 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap, return -EINVAL; } - iface->cur_mode = TWI_I2C_MODE_REPEAT; + if (iface->msg_num > 1) + iface->cur_mode = TWI_I2C_MODE_REPEAT; iface->manual_stop = 0; iface->transPtr = pmsg->buf; iface->writeNum = iface->readNum = pmsg->len; @@ -356,6 +319,7 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap, /* Master enable */ write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | + (iface->msg_num > 1 ? RSTART : 0) | ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0)); SSYNC(); @@ -398,8 +362,8 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, if (!(read_CONTROL(iface) & TWI_ENA)) return -ENXIO; - while (read_MASTER_STAT(iface) & BUSBUSY) - yield(); + if (read_MASTER_STAT(iface) & BUSBUSY) + return -EAGAIN; iface->writeNum = 0; iface->readNum = 0; @@ -520,7 +484,7 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, else write_MASTER_CTL(iface, 0x1 << 6); /* Master enable */ - write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | + write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | RSTART | ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0)); break; default: @@ -611,9 +575,9 @@ static struct i2c_algorithm bfin_twi_algorithm = { .functionality = bfin_twi_functionality, }; -static int i2c_bfin_twi_suspend(struct platform_device *pdev, pm_message_t state) +static int i2c_bfin_twi_suspend(struct device *dev) { - struct bfin_twi_iface *iface = platform_get_drvdata(pdev); + struct bfin_twi_iface *iface = dev_get_drvdata(dev); iface->saved_clkdiv = read_CLKDIV(iface); iface->saved_control = read_CONTROL(iface); @@ -626,14 +590,14 @@ static int i2c_bfin_twi_suspend(struct platform_device *pdev, pm_message_t state return 0; } -static int i2c_bfin_twi_resume(struct platform_device *pdev) +static int i2c_bfin_twi_resume(struct device *dev) { - struct bfin_twi_iface *iface = platform_get_drvdata(pdev); + struct bfin_twi_iface *iface = dev_get_drvdata(dev); int rc = request_irq(iface->irq, bfin_twi_interrupt_entry, - 0, pdev->name, iface); + 0, to_platform_device(dev)->name, iface); if (rc) { - dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq); + dev_err(dev, "Can't get IRQ %d !\n", iface->irq); return -ENODEV; } @@ -646,6 +610,9 @@ static int i2c_bfin_twi_resume(struct platform_device *pdev) return 0; } +static SIMPLE_DEV_PM_OPS(i2c_bfin_twi_pm, + i2c_bfin_twi_suspend, i2c_bfin_twi_resume); + static int i2c_bfin_twi_probe(struct platform_device *pdev) { struct bfin_twi_iface *iface; @@ -695,7 +662,8 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev) p_adap->timeout = 5 * HZ; p_adap->retries = 3; - rc = peripheral_request_list(pin_req[pdev->id], "i2c-bfin-twi"); + rc = peripheral_request_list((unsigned short *)pdev->dev.platform_data, + "i2c-bfin-twi"); if (rc) { dev_err(&pdev->dev, "Can't setup pin mux!\n"); goto out_error_pin_mux; @@ -742,7 +710,7 @@ out_error_add_adapter: free_irq(iface->irq, iface); out_error_req_irq: out_error_no_irq: - peripheral_free_list(pin_req[pdev->id]); + peripheral_free_list((unsigned short *)pdev->dev.platform_data); out_error_pin_mux: iounmap(iface->regs_base); out_error_ioremap: @@ -760,7 +728,7 @@ static int i2c_bfin_twi_remove(struct platform_device *pdev) i2c_del_adapter(&(iface->adap)); free_irq(iface->irq, iface); - peripheral_free_list(pin_req[pdev->id]); + peripheral_free_list((unsigned short *)pdev->dev.platform_data); iounmap(iface->regs_base); kfree(iface); @@ -770,11 +738,10 @@ static int i2c_bfin_twi_remove(struct platform_device *pdev) static struct platform_driver i2c_bfin_twi_driver = { .probe = i2c_bfin_twi_probe, .remove = i2c_bfin_twi_remove, - .suspend = i2c_bfin_twi_suspend, - .resume = i2c_bfin_twi_resume, .driver = { .name = "i2c-bfin-twi", .owner = THIS_MODULE, + .pm = &i2c_bfin_twi_pm, }, }; diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 370031ac8200..0722f869465c 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -117,10 +117,8 @@ static u16 __initdata i2c_clk_div[50][2] = { struct imx_i2c_struct { struct i2c_adapter adapter; - struct resource *res; struct clk *clk; void __iomem *base; - int irq; wait_queue_head_t queue; unsigned long i2csr; unsigned int disable_delay; @@ -472,9 +470,8 @@ static int __init i2c_imx_probe(struct platform_device *pdev) struct imxi2c_platform_data *pdata = pdev->dev.platform_data; struct pinctrl *pinctrl; void __iomem *base; - resource_size_t res_size; - int irq, bitrate; - int ret; + int irq, ret; + u32 bitrate; dev_dbg(&pdev->dev, "<%s>\n", __func__); @@ -489,25 +486,15 @@ static int __init i2c_imx_probe(struct platform_device *pdev) return -ENOENT; } - res_size = resource_size(res); - - if (!request_mem_region(res->start, res_size, DRIVER_NAME)) { - dev_err(&pdev->dev, "request_mem_region failed\n"); + base = devm_request_and_ioremap(&pdev->dev, res); + if (!base) return -EBUSY; - } - - base = ioremap(res->start, res_size); - if (!base) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -EIO; - goto fail1; - } - i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL); + i2c_imx = devm_kzalloc(&pdev->dev, sizeof(struct imx_i2c_struct), + GFP_KERNEL); if (!i2c_imx) { dev_err(&pdev->dev, "can't allocate interface\n"); - ret = -ENOMEM; - goto fail2; + return -ENOMEM; } /* Setup i2c_imx driver structure */ @@ -517,29 +504,27 @@ static int __init i2c_imx_probe(struct platform_device *pdev) i2c_imx->adapter.dev.parent = &pdev->dev; i2c_imx->adapter.nr = pdev->id; i2c_imx->adapter.dev.of_node = pdev->dev.of_node; - i2c_imx->irq = irq; i2c_imx->base = base; - i2c_imx->res = res; pinctrl = devm_pinctrl_get_select_default(&pdev->dev); if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - goto fail3; + dev_err(&pdev->dev, "can't get/select pinctrl\n"); + return PTR_ERR(pinctrl); } /* Get I2C clock */ - i2c_imx->clk = clk_get(&pdev->dev, "i2c_clk"); + i2c_imx->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(i2c_imx->clk)) { - ret = PTR_ERR(i2c_imx->clk); dev_err(&pdev->dev, "can't get I2C clock\n"); - goto fail3; + return PTR_ERR(i2c_imx->clk); } /* Request IRQ */ - ret = request_irq(i2c_imx->irq, i2c_imx_isr, 0, pdev->name, i2c_imx); + ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0, + pdev->name, i2c_imx); if (ret) { - dev_err(&pdev->dev, "can't claim irq %d\n", i2c_imx->irq); - goto fail4; + dev_err(&pdev->dev, "can't claim irq %d\n", irq); + return ret; } /* Init queue */ @@ -564,7 +549,7 @@ static int __init i2c_imx_probe(struct platform_device *pdev) ret = i2c_add_numbered_adapter(&i2c_imx->adapter); if (ret < 0) { dev_err(&pdev->dev, "registration failed\n"); - goto fail5; + return ret; } of_i2c_register_devices(&i2c_imx->adapter); @@ -572,28 +557,16 @@ static int __init i2c_imx_probe(struct platform_device *pdev) /* Set up platform driver data */ platform_set_drvdata(pdev, i2c_imx); - dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", i2c_imx->irq); + dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq); dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x\n", - i2c_imx->res->start, i2c_imx->res->end); - dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x \n", - res_size, i2c_imx->res->start); + res->start, res->end); + dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x\n", + resource_size(res), res->start); dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n", i2c_imx->adapter.name); dev_dbg(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); return 0; /* Return OK */ - -fail5: - free_irq(i2c_imx->irq, i2c_imx); -fail4: - clk_put(i2c_imx->clk); -fail3: - kfree(i2c_imx); -fail2: - iounmap(base); -fail1: - release_mem_region(res->start, resource_size(res)); - return ret; /* Return error number */ } static int __exit i2c_imx_remove(struct platform_device *pdev) @@ -605,20 +578,12 @@ static int __exit i2c_imx_remove(struct platform_device *pdev) i2c_del_adapter(&i2c_imx->adapter); platform_set_drvdata(pdev, NULL); - /* free interrupt */ - free_irq(i2c_imx->irq, i2c_imx); - /* setup chip registers to defaults */ writeb(0, i2c_imx->base + IMX_I2C_IADR); writeb(0, i2c_imx->base + IMX_I2C_IFDR); writeb(0, i2c_imx->base + IMX_I2C_I2CR); writeb(0, i2c_imx->base + IMX_I2C_I2SR); - clk_put(i2c_imx->clk); - - iounmap(i2c_imx->base); - release_mem_region(i2c_imx->res->start, resource_size(i2c_imx->res)); - kfree(i2c_imx); return 0; } diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 4f44a33017b0..2e9d56719e99 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -18,6 +18,11 @@ #include <linux/mv643xx_i2c.h> #include <linux/platform_device.h> #include <linux/io.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_i2c.h> +#include <linux/clk.h> +#include <linux/err.h> /* Register defines */ #define MV64XXX_I2C_REG_SLAVE_ADDR 0x00 @@ -98,6 +103,9 @@ struct mv64xxx_i2c_data { int rc; u32 freq_m; u32 freq_n; +#if defined(CONFIG_HAVE_CLK) + struct clk *clk; +#endif wait_queue_head_t waitq; spinlock_t lock; struct i2c_msg *msg; @@ -521,6 +529,82 @@ mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data) drv_data->reg_base_p = 0; } +#ifdef CONFIG_OF +static int __devinit +mv64xxx_calc_freq(const int tclk, const int n, const int m) +{ + return tclk / (10 * (m + 1) * (2 << n)); +} + +static bool __devinit +mv64xxx_find_baud_factors(const u32 req_freq, const u32 tclk, u32 *best_n, + u32 *best_m) +{ + int freq, delta, best_delta = INT_MAX; + int m, n; + + for (n = 0; n <= 7; n++) + for (m = 0; m <= 15; m++) { + freq = mv64xxx_calc_freq(tclk, n, m); + delta = req_freq - freq; + if (delta >= 0 && delta < best_delta) { + *best_m = m; + *best_n = n; + best_delta = delta; + } + if (best_delta == 0) + return true; + } + if (best_delta == INT_MAX) + return false; + return true; +} + +static int __devinit +mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, + struct device_node *np) +{ + u32 bus_freq, tclk; + int rc = 0; + + /* CLK is mandatory when using DT to describe the i2c bus. We + * need to know tclk in order to calculate bus clock + * factors. + */ +#if !defined(CONFIG_HAVE_CLK) + /* Have OF but no CLK */ + return -ENODEV; +#else + if (IS_ERR(drv_data->clk)) { + rc = -ENODEV; + goto out; + } + tclk = clk_get_rate(drv_data->clk); + of_property_read_u32(np, "clock-frequency", &bus_freq); + if (!mv64xxx_find_baud_factors(bus_freq, tclk, + &drv_data->freq_n, &drv_data->freq_m)) { + rc = -EINVAL; + goto out; + } + drv_data->irq = irq_of_parse_and_map(np, 0); + + /* Its not yet defined how timeouts will be specified in device tree. + * So hard code the value to 1 second. + */ + drv_data->adapter.timeout = HZ; +out: + return rc; +#endif +} +#else /* CONFIG_OF */ +static int __devinit +mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, + struct device_node *np) +{ + return -ENODEV; +} +#endif /* CONFIG_OF */ + static int __devinit mv64xxx_i2c_probe(struct platform_device *pd) { @@ -528,7 +612,7 @@ mv64xxx_i2c_probe(struct platform_device *pd) struct mv64xxx_i2c_pdata *pdata = pd->dev.platform_data; int rc; - if ((pd->id != 0) || !pdata) + if ((!pdata && !pd->dev.of_node)) return -ENODEV; drv_data = kzalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL); @@ -546,19 +630,35 @@ mv64xxx_i2c_probe(struct platform_device *pd) init_waitqueue_head(&drv_data->waitq); spin_lock_init(&drv_data->lock); - drv_data->freq_m = pdata->freq_m; - drv_data->freq_n = pdata->freq_n; - drv_data->irq = platform_get_irq(pd, 0); +#if defined(CONFIG_HAVE_CLK) + /* Not all platforms have a clk */ + drv_data->clk = clk_get(&pd->dev, NULL); + if (!IS_ERR(drv_data->clk)) { + clk_prepare(drv_data->clk); + clk_enable(drv_data->clk); + } +#endif + if (pdata) { + drv_data->freq_m = pdata->freq_m; + drv_data->freq_n = pdata->freq_n; + drv_data->irq = platform_get_irq(pd, 0); + drv_data->adapter.timeout = msecs_to_jiffies(pdata->timeout); + } else if (pd->dev.of_node) { + rc = mv64xxx_of_config(drv_data, pd->dev.of_node); + if (rc) + goto exit_unmap_regs; + } if (drv_data->irq < 0) { rc = -ENXIO; goto exit_unmap_regs; } + drv_data->adapter.dev.parent = &pd->dev; drv_data->adapter.algo = &mv64xxx_i2c_algo; drv_data->adapter.owner = THIS_MODULE; drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; - drv_data->adapter.timeout = msecs_to_jiffies(pdata->timeout); drv_data->adapter.nr = pd->id; + drv_data->adapter.dev.of_node = pd->dev.of_node; platform_set_drvdata(pd, drv_data); i2c_set_adapdata(&drv_data->adapter, drv_data); @@ -577,11 +677,20 @@ mv64xxx_i2c_probe(struct platform_device *pd) goto exit_free_irq; } + of_i2c_register_devices(&drv_data->adapter); + return 0; exit_free_irq: free_irq(drv_data->irq, drv_data); exit_unmap_regs: +#if defined(CONFIG_HAVE_CLK) + /* Not all platforms have a clk */ + if (!IS_ERR(drv_data->clk)) { + clk_disable(drv_data->clk); + clk_unprepare(drv_data->clk); + } +#endif mv64xxx_i2c_unmap_regs(drv_data); exit_kfree: kfree(drv_data); @@ -597,17 +706,31 @@ mv64xxx_i2c_remove(struct platform_device *dev) rc = i2c_del_adapter(&drv_data->adapter); free_irq(drv_data->irq, drv_data); mv64xxx_i2c_unmap_regs(drv_data); +#if defined(CONFIG_HAVE_CLK) + /* Not all platforms have a clk */ + if (!IS_ERR(drv_data->clk)) { + clk_disable(drv_data->clk); + clk_unprepare(drv_data->clk); + } +#endif kfree(drv_data); return rc; } +static const struct of_device_id mv64xxx_i2c_of_match_table[] __devinitdata = { + { .compatible = "marvell,mv64xxx-i2c", }, + {} +}; +MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table); + static struct platform_driver mv64xxx_i2c_driver = { .probe = mv64xxx_i2c_probe, .remove = __devexit_p(mv64xxx_i2c_remove), .driver = { .owner = THIS_MODULE, .name = MV64XXX_I2C_CTLR_NAME, + .of_match_table = of_match_ptr(mv64xxx_i2c_of_match_table), }, }; diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 04eb441b6ce1..088c5c1ed17d 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -46,6 +46,10 @@ #define MXS_I2C_CTRL0_DIRECTION 0x00010000 #define MXS_I2C_CTRL0_XFER_COUNT(v) ((v) & 0x0000FFFF) +#define MXS_I2C_TIMING0 (0x10) +#define MXS_I2C_TIMING1 (0x20) +#define MXS_I2C_TIMING2 (0x30) + #define MXS_I2C_CTRL1 (0x40) #define MXS_I2C_CTRL1_SET (0x44) #define MXS_I2C_CTRL1_CLR (0x48) @@ -97,6 +101,35 @@ #define MXS_CMD_I2C_READ (MXS_I2C_CTRL0_SEND_NAK_ON_LAST | \ MXS_I2C_CTRL0_MASTER_MODE) +struct mxs_i2c_speed_config { + uint32_t timing0; + uint32_t timing1; + uint32_t timing2; +}; + +/* + * Timing values for the default 24MHz clock supplied into the i2c block. + * + * The bus can operate at 95kHz or at 400kHz with the following timing + * register configurations. The 100kHz mode isn't present because it's + * values are not stated in the i.MX233/i.MX28 datasheet. The 95kHz mode + * shall be close enough replacement. Therefore when the bus is configured + * for 100kHz operation, 95kHz timing settings are actually loaded. + * + * For details, see i.MX233 [25.4.2 - 25.4.4] and i.MX28 [27.5.2 - 27.5.4]. + */ +static const struct mxs_i2c_speed_config mxs_i2c_95kHz_config = { + .timing0 = 0x00780030, + .timing1 = 0x00800030, + .timing2 = 0x00300030, +}; + +static const struct mxs_i2c_speed_config mxs_i2c_400kHz_config = { + .timing0 = 0x000f0007, + .timing1 = 0x001f000f, + .timing2 = 0x00300030, +}; + /** * struct mxs_i2c_dev - per device, private MXS-I2C data * @@ -112,11 +145,17 @@ struct mxs_i2c_dev { struct completion cmd_complete; u32 cmd_err; struct i2c_adapter adapter; + const struct mxs_i2c_speed_config *speed; }; static void mxs_i2c_reset(struct mxs_i2c_dev *i2c) { stmp_reset_block(i2c->regs); + + writel(i2c->speed->timing0, i2c->regs + MXS_I2C_TIMING0); + writel(i2c->speed->timing1, i2c->regs + MXS_I2C_TIMING1); + writel(i2c->speed->timing2, i2c->regs + MXS_I2C_TIMING2); + writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET); writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE, i2c->regs + MXS_I2C_QUEUECTRL_SET); @@ -193,7 +232,7 @@ static int mxs_i2c_wait_for_data(struct mxs_i2c_dev *i2c) static int mxs_i2c_finish_read(struct mxs_i2c_dev *i2c, u8 *buf, int len) { - u32 data; + u32 uninitialized_var(data); int i; for (i = 0; i < len; i++) { @@ -319,6 +358,28 @@ static const struct i2c_algorithm mxs_i2c_algo = { .functionality = mxs_i2c_func, }; +static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c) +{ + uint32_t speed; + struct device *dev = i2c->dev; + struct device_node *node = dev->of_node; + int ret; + + if (!node) + return -EINVAL; + + i2c->speed = &mxs_i2c_95kHz_config; + ret = of_property_read_u32(node, "clock-frequency", &speed); + if (ret) + dev_warn(dev, "No I2C speed selected, using 100kHz\n"); + else if (speed == 400000) + i2c->speed = &mxs_i2c_400kHz_config; + else if (speed != 100000) + dev_warn(dev, "Unsupported I2C speed selected, using 100kHz\n"); + + return 0; +} + static int __devinit mxs_i2c_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -358,6 +419,11 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev) return err; i2c->dev = dev; + + err = mxs_i2c_get_ofdata(i2c); + if (err) + return err; + platform_set_drvdata(pdev, i2c); /* Do reset to enforce correct startup after pinmuxing */ diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index a92440dbef07..5e6f1eed4f83 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -14,7 +14,8 @@ */ #include <linux/init.h> #include <linux/module.h> -#include <linux/platform_device.h> +#include <linux/amba/bus.h> +#include <linux/atomic.h> #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/i2c.h> @@ -23,8 +24,7 @@ #include <linux/io.h> #include <linux/regulator/consumer.h> #include <linux/pm_runtime.h> - -#include <plat/i2c.h> +#include <linux/platform_data/i2c-nomadik.h> #define DRIVER_NAME "nmk-i2c" @@ -136,7 +136,7 @@ struct i2c_nmk_client { /** * struct nmk_i2c_dev - private data structure of the controller. - * @pdev: parent platform device. + * @adev: parent amba device. * @adap: corresponding I2C adapter. * @irq: interrupt line for the controller. * @virtbase: virtual io memory area. @@ -150,7 +150,7 @@ struct i2c_nmk_client { * @busy: Busy doing transfer. */ struct nmk_i2c_dev { - struct platform_device *pdev; + struct amba_device *adev; struct i2c_adapter adap; int irq; void __iomem *virtbase; @@ -217,7 +217,7 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *dev) } } - dev_err(&dev->pdev->dev, + dev_err(&dev->adev->dev, "flushing operation timed out giving up after %d attempts", LOOP_ATTEMPTS); @@ -276,15 +276,32 @@ exit: /** * load_i2c_mcr_reg() - load the MCR register * @dev: private data of controller + * @flags: message flags */ -static u32 load_i2c_mcr_reg(struct nmk_i2c_dev *dev) +static u32 load_i2c_mcr_reg(struct nmk_i2c_dev *dev, u16 flags) { u32 mcr = 0; + unsigned short slave_adr_3msb_bits; - /* 7-bit address transaction */ - mcr |= GEN_MASK(1, I2C_MCR_AM, 12); mcr |= GEN_MASK(dev->cli.slave_adr, I2C_MCR_A7, 1); + if (unlikely(flags & I2C_M_TEN)) { + /* 10-bit address transaction */ + mcr |= GEN_MASK(2, I2C_MCR_AM, 12); + /* + * Get the top 3 bits. + * EA10 represents extended address in MCR. This includes + * the extension (MSB bits) of the 7 bit address loaded + * in A7 + */ + slave_adr_3msb_bits = (dev->cli.slave_adr >> 7) & 0x7; + + mcr |= GEN_MASK(slave_adr_3msb_bits, I2C_MCR_EA10, 8); + } else { + /* 7-bit address transaction */ + mcr |= GEN_MASK(1, I2C_MCR_AM, 12); + } + /* start byte procedure not applied */ mcr |= GEN_MASK(0, I2C_MCR_SB, 11); @@ -364,7 +381,7 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev) * and high speed (up to 3.4 Mb/s) */ if (dev->cfg.sm > I2C_FREQ_MODE_FAST) { - dev_err(&dev->pdev->dev, + dev_err(&dev->adev->dev, "do not support this mode defaulting to std. mode\n"); brcr2 = i2c_clk/(100000 * 2) & 0xffff; writel((brcr1 | brcr2), dev->virtbase + I2C_BRCR); @@ -381,19 +398,20 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev) /** * read_i2c() - Read from I2C client device * @dev: private data of I2C Driver + * @flags: message flags * * This function reads from i2c client device when controller is in * master mode. There is a completion timeout. If there is no transfer * before timeout error is returned. */ -static int read_i2c(struct nmk_i2c_dev *dev) +static int read_i2c(struct nmk_i2c_dev *dev, u16 flags) { u32 status = 0; u32 mcr; u32 irq_mask = 0; int timeout; - mcr = load_i2c_mcr_reg(dev); + mcr = load_i2c_mcr_reg(dev, flags); writel(mcr, dev->virtbase + I2C_MCR); /* load the current CR value */ @@ -423,7 +441,7 @@ static int read_i2c(struct nmk_i2c_dev *dev) &dev->xfer_complete, dev->adap.timeout); if (timeout < 0) { - dev_err(&dev->pdev->dev, + dev_err(&dev->adev->dev, "wait_for_completion_timeout " "returned %d waiting for event\n", timeout); status = timeout; @@ -431,7 +449,7 @@ static int read_i2c(struct nmk_i2c_dev *dev) if (timeout == 0) { /* Controller timed out */ - dev_err(&dev->pdev->dev, "read from slave 0x%x timed out\n", + dev_err(&dev->adev->dev, "read from slave 0x%x timed out\n", dev->cli.slave_adr); status = -ETIMEDOUT; } @@ -459,17 +477,18 @@ static void fill_tx_fifo(struct nmk_i2c_dev *dev, int no_bytes) /** * write_i2c() - Write data to I2C client. * @dev: private data of I2C Driver + * @flags: message flags * * This function writes data to I2C client */ -static int write_i2c(struct nmk_i2c_dev *dev) +static int write_i2c(struct nmk_i2c_dev *dev, u16 flags) { u32 status = 0; u32 mcr; u32 irq_mask = 0; int timeout; - mcr = load_i2c_mcr_reg(dev); + mcr = load_i2c_mcr_reg(dev, flags); writel(mcr, dev->virtbase + I2C_MCR); @@ -510,7 +529,7 @@ static int write_i2c(struct nmk_i2c_dev *dev) &dev->xfer_complete, dev->adap.timeout); if (timeout < 0) { - dev_err(&dev->pdev->dev, + dev_err(&dev->adev->dev, "wait_for_completion_timeout " "returned %d waiting for event\n", timeout); status = timeout; @@ -518,7 +537,7 @@ static int write_i2c(struct nmk_i2c_dev *dev) if (timeout == 0) { /* Controller timed out */ - dev_err(&dev->pdev->dev, "write to slave 0x%x timed out\n", + dev_err(&dev->adev->dev, "write to slave 0x%x timed out\n", dev->cli.slave_adr); status = -ETIMEDOUT; } @@ -538,11 +557,11 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags) if (flags & I2C_M_RD) { /* read operation */ dev->cli.operation = I2C_READ; - status = read_i2c(dev); + status = read_i2c(dev, flags); } else { /* write operation */ dev->cli.operation = I2C_WRITE; - status = write_i2c(dev); + status = write_i2c(dev, flags); } if (status || (dev->result)) { @@ -557,7 +576,7 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags) if (((i2c_sr >> 2) & 0x3) == 0x3) { /* get the abort cause */ cause = (i2c_sr >> 4) & 0x7; - dev_err(&dev->pdev->dev, "%s\n", + dev_err(&dev->adev->dev, "%s\n", cause >= ARRAY_SIZE(abort_causes) ? "unknown reason" : abort_causes[cause]); @@ -630,7 +649,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, if (dev->regulator) regulator_enable(dev->regulator); - pm_runtime_get_sync(&dev->pdev->dev); + pm_runtime_get_sync(&dev->adev->dev); clk_enable(dev->clk); @@ -644,13 +663,6 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, setup_i2c_controller(dev); for (i = 0; i < num_msgs; i++) { - if (unlikely(msgs[i].flags & I2C_M_TEN)) { - dev_err(&dev->pdev->dev, - "10 bit addressing not supported\n"); - - status = -EINVAL; - goto out; - } dev->cli.slave_adr = msgs[i].addr; dev->cli.buffer = msgs[i].buf; dev->cli.count = msgs[i].len; @@ -667,7 +679,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, out: clk_disable(dev->clk); - pm_runtime_put_sync(&dev->pdev->dev); + pm_runtime_put_sync(&dev->adev->dev); if (dev->regulator) regulator_disable(dev->regulator); @@ -790,7 +802,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) if (dev->cli.count) { dev->result = -EIO; - dev_err(&dev->pdev->dev, + dev_err(&dev->adev->dev, "%lu bytes still remain to be xfered\n", dev->cli.count); (void) init_hw(dev); @@ -834,7 +846,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) dev->result = -EIO; (void) init_hw(dev); - dev_err(&dev->pdev->dev, "Tx Fifo Over run\n"); + dev_err(&dev->adev->dev, "Tx Fifo Over run\n"); complete(&dev->xfer_complete); break; @@ -847,10 +859,10 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) case I2C_IT_RFSE: case I2C_IT_WTSR: case I2C_IT_STD: - dev_err(&dev->pdev->dev, "unhandled Interrupt\n"); + dev_err(&dev->adev->dev, "unhandled Interrupt\n"); break; default: - dev_err(&dev->pdev->dev, "spurious Interrupt..\n"); + dev_err(&dev->adev->dev, "spurious Interrupt..\n"); break; } @@ -861,8 +873,8 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) #ifdef CONFIG_PM static int nmk_i2c_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct nmk_i2c_dev *nmk_i2c = platform_get_drvdata(pdev); + struct amba_device *adev = to_amba_device(dev); + struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev); if (nmk_i2c->busy) return -EBUSY; @@ -891,7 +903,7 @@ static const struct dev_pm_ops nmk_i2c_pm = { static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; } static const struct i2c_algorithm nmk_i2c_algo = { @@ -899,78 +911,69 @@ static const struct i2c_algorithm nmk_i2c_algo = { .functionality = nmk_i2c_functionality }; -static int __devinit nmk_i2c_probe(struct platform_device *pdev) +static atomic_t adapter_id = ATOMIC_INIT(0); + +static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; - struct resource *res; struct nmk_i2c_controller *pdata = - pdev->dev.platform_data; + adev->dev.platform_data; struct nmk_i2c_dev *dev; struct i2c_adapter *adap; + if (!pdata) { + dev_warn(&adev->dev, "no platform data\n"); + return -ENODEV; + } dev = kzalloc(sizeof(struct nmk_i2c_dev), GFP_KERNEL); if (!dev) { - dev_err(&pdev->dev, "cannot allocate memory\n"); + dev_err(&adev->dev, "cannot allocate memory\n"); ret = -ENOMEM; goto err_no_mem; } dev->busy = false; - dev->pdev = pdev; - platform_set_drvdata(pdev, dev); + dev->adev = adev; + amba_set_drvdata(adev, dev); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENOENT; - goto err_no_resource; - } - - if (request_mem_region(res->start, resource_size(res), - DRIVER_NAME "I/O region") == NULL) { - ret = -EBUSY; - goto err_no_region; - } - - dev->virtbase = ioremap(res->start, resource_size(res)); + dev->virtbase = ioremap(adev->res.start, resource_size(&adev->res)); if (!dev->virtbase) { ret = -ENOMEM; goto err_no_ioremap; } - dev->irq = platform_get_irq(pdev, 0); + dev->irq = adev->irq[0]; ret = request_irq(dev->irq, i2c_irq_handler, 0, DRIVER_NAME, dev); if (ret) { - dev_err(&pdev->dev, "cannot claim the irq %d\n", dev->irq); + dev_err(&adev->dev, "cannot claim the irq %d\n", dev->irq); goto err_irq; } - dev->regulator = regulator_get(&pdev->dev, "v-i2c"); + dev->regulator = regulator_get(&adev->dev, "v-i2c"); if (IS_ERR(dev->regulator)) { - dev_warn(&pdev->dev, "could not get i2c regulator\n"); + dev_warn(&adev->dev, "could not get i2c regulator\n"); dev->regulator = NULL; } - pm_suspend_ignore_children(&pdev->dev, true); - pm_runtime_enable(&pdev->dev); + pm_suspend_ignore_children(&adev->dev, true); - dev->clk = clk_get(&pdev->dev, NULL); + dev->clk = clk_get(&adev->dev, NULL); if (IS_ERR(dev->clk)) { - dev_err(&pdev->dev, "could not get i2c clock\n"); + dev_err(&adev->dev, "could not get i2c clock\n"); ret = PTR_ERR(dev->clk); goto err_no_clk; } adap = &dev->adap; - adap->dev.parent = &pdev->dev; + adap->dev.parent = &adev->dev; adap->owner = THIS_MODULE; adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adap->algo = &nmk_i2c_algo; adap->timeout = msecs_to_jiffies(pdata->timeout); + adap->nr = atomic_read(&adapter_id); snprintf(adap->name, sizeof(adap->name), - "Nomadik I2C%d at %lx", pdev->id, (unsigned long)res->start); - - /* fetch the controller id */ - adap->nr = pdev->id; + "Nomadik I2C%d at %pR", adap->nr, &adev->res); + atomic_inc(&adapter_id); /* fetch the controller configuration from machine */ dev->cfg.clk_freq = pdata->clk_freq; @@ -981,16 +984,18 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(adap, dev); - dev_info(&pdev->dev, + dev_info(&adev->dev, "initialize %s on virtual base %p\n", adap->name, dev->virtbase); ret = i2c_add_numbered_adapter(adap); if (ret) { - dev_err(&pdev->dev, "failed to add adapter\n"); + dev_err(&adev->dev, "failed to add adapter\n"); goto err_add_adap; } + pm_runtime_put(&adev->dev); + return 0; err_add_adap: @@ -998,25 +1003,21 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev) err_no_clk: if (dev->regulator) regulator_put(dev->regulator); - pm_runtime_disable(&pdev->dev); free_irq(dev->irq, dev); err_irq: iounmap(dev->virtbase); err_no_ioremap: - release_mem_region(res->start, resource_size(res)); - err_no_region: - platform_set_drvdata(pdev, NULL); - err_no_resource: + amba_set_drvdata(adev, NULL); kfree(dev); err_no_mem: return ret; } -static int __devexit nmk_i2c_remove(struct platform_device *pdev) +static int nmk_i2c_remove(struct amba_device *adev) { - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - struct nmk_i2c_dev *dev = platform_get_drvdata(pdev); + struct resource *res = &adev->res; + struct nmk_i2c_dev *dev = amba_get_drvdata(adev); i2c_del_adapter(&dev->adap); flush_i2c_fifo(dev); @@ -1031,31 +1032,46 @@ static int __devexit nmk_i2c_remove(struct platform_device *pdev) clk_put(dev->clk); if (dev->regulator) regulator_put(dev->regulator); - pm_runtime_disable(&pdev->dev); - platform_set_drvdata(pdev, NULL); + pm_runtime_disable(&adev->dev); + amba_set_drvdata(adev, NULL); kfree(dev); return 0; } -static struct platform_driver nmk_i2c_driver = { - .driver = { +static struct amba_id nmk_i2c_ids[] = { + { + .id = 0x00180024, + .mask = 0x00ffffff, + }, + { + .id = 0x00380024, + .mask = 0x00ffffff, + }, + {}, +}; + +MODULE_DEVICE_TABLE(amba, nmk_i2c_ids); + +static struct amba_driver nmk_i2c_driver = { + .drv = { .owner = THIS_MODULE, .name = DRIVER_NAME, .pm = &nmk_i2c_pm, }, + .id_table = nmk_i2c_ids, .probe = nmk_i2c_probe, - .remove = __devexit_p(nmk_i2c_remove), + .remove = nmk_i2c_remove, }; static int __init nmk_i2c_init(void) { - return platform_driver_register(&nmk_i2c_driver); + return amba_driver_register(&nmk_i2c_driver); } static void __exit nmk_i2c_exit(void) { - platform_driver_unregister(&nmk_i2c_driver); + amba_driver_unregister(&nmk_i2c_driver); } subsys_initcall(nmk_i2c_init); @@ -1064,4 +1080,3 @@ module_exit(nmk_i2c_exit); MODULE_AUTHOR("Sachin Verma, Srinidhi KASAGAR"); MODULE_DESCRIPTION("Nomadik/Ux500 I2C driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 75194c579b6d..bffd5501ac2d 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -10,40 +10,9 @@ */ /* - * Device tree configuration: - * - * Required properties: - * - compatible : "opencores,i2c-ocores" - * - reg : bus address start and address range size of device - * - interrupts : interrupt number - * - regstep : size of device registers in bytes - * - clock-frequency : frequency of bus clock in Hz - * - * Example: - * - * i2c0: ocores@a0000000 { - * compatible = "opencores,i2c-ocores"; - * reg = <0xa0000000 0x8>; - * interrupts = <10>; - * - * regstep = <1>; - * clock-frequency = <20000000>; - * - * -- Devices connected on this I2C bus get - * -- defined here; address- and size-cells - * -- apply to these child devices - * - * #address-cells = <1>; - * #size-cells = <0>; - * - * dummy@60 { - * compatible = "dummy"; - * reg = <60>; - * }; - * }; - * + * This driver can be used from the device tree, see + * Documentation/devicetree/bindings/i2c/ocore-i2c.txt */ - #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -56,10 +25,12 @@ #include <linux/slab.h> #include <linux/io.h> #include <linux/of_i2c.h> +#include <linux/log2.h> struct ocores_i2c { void __iomem *base; - int regstep; + u32 reg_shift; + u32 reg_io_width; wait_queue_head_t wait; struct i2c_adapter adap; struct i2c_msg *msg; @@ -102,12 +73,22 @@ struct ocores_i2c { static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value) { - iowrite8(value, i2c->base + reg * i2c->regstep); + if (i2c->reg_io_width == 4) + iowrite32(value, i2c->base + (reg << i2c->reg_shift)); + else if (i2c->reg_io_width == 2) + iowrite16(value, i2c->base + (reg << i2c->reg_shift)); + else + iowrite8(value, i2c->base + (reg << i2c->reg_shift)); } static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg) { - return ioread8(i2c->base + reg * i2c->regstep); + if (i2c->reg_io_width == 4) + return ioread32(i2c->base + (reg << i2c->reg_shift)); + else if (i2c->reg_io_width == 2) + return ioread16(i2c->base + (reg << i2c->reg_shift)); + else + return ioread8(i2c->base + (reg << i2c->reg_shift)); } static void ocores_process(struct ocores_i2c *i2c) @@ -247,26 +228,35 @@ static struct i2c_adapter ocores_adapter = { }; #ifdef CONFIG_OF -static int ocores_i2c_of_probe(struct platform_device* pdev, - struct ocores_i2c* i2c) +static int ocores_i2c_of_probe(struct platform_device *pdev, + struct ocores_i2c *i2c) { - const __be32* val; - - val = of_get_property(pdev->dev.of_node, "regstep", NULL); - if (!val) { - dev_err(&pdev->dev, "Missing required parameter 'regstep'"); - return -ENODEV; + struct device_node *np = pdev->dev.of_node; + u32 val; + + if (of_property_read_u32(np, "reg-shift", &i2c->reg_shift)) { + /* no 'reg-shift', check for deprecated 'regstep' */ + if (!of_property_read_u32(np, "regstep", &val)) { + if (!is_power_of_2(val)) { + dev_err(&pdev->dev, "invalid regstep %d\n", + val); + return -EINVAL; + } + i2c->reg_shift = ilog2(val); + dev_warn(&pdev->dev, + "regstep property deprecated, use reg-shift\n"); + } } - i2c->regstep = be32_to_cpup(val); - val = of_get_property(pdev->dev.of_node, "clock-frequency", NULL); - if (!val) { + if (of_property_read_u32(np, "clock-frequency", &val)) { dev_err(&pdev->dev, - "Missing required parameter 'clock-frequency'"); + "Missing required parameter 'clock-frequency'\n"); return -ENODEV; } - i2c->clock_khz = be32_to_cpup(val) / 1000; + i2c->clock_khz = val / 1000; + of_property_read_u32(pdev->dev.of_node, "reg-io-width", + &i2c->reg_io_width); return 0; } #else @@ -308,7 +298,8 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; if (pdata) { - i2c->regstep = pdata->regstep; + i2c->reg_shift = pdata->reg_shift; + i2c->reg_io_width = pdata->reg_io_width; i2c->clock_khz = pdata->clock_khz; } else { ret = ocores_i2c_of_probe(pdev, i2c); @@ -316,6 +307,9 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev) return ret; } + if (i2c->reg_io_width == 0) + i2c->reg_io_width = 1; /* Set to default value */ + ocores_init(i2c); init_waitqueue_head(&i2c->wait); @@ -351,7 +345,7 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev) return 0; } -static int __devexit ocores_i2c_remove(struct platform_device* pdev) +static int __devexit ocores_i2c_remove(struct platform_device *pdev) { struct ocores_i2c *i2c = platform_get_drvdata(pdev); @@ -367,9 +361,9 @@ static int __devexit ocores_i2c_remove(struct platform_device* pdev) } #ifdef CONFIG_PM -static int ocores_i2c_suspend(struct platform_device *pdev, pm_message_t state) +static int ocores_i2c_suspend(struct device *dev) { - struct ocores_i2c *i2c = platform_get_drvdata(pdev); + struct ocores_i2c *i2c = dev_get_drvdata(dev); u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); /* make sure the device is disabled */ @@ -378,17 +372,19 @@ static int ocores_i2c_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int ocores_i2c_resume(struct platform_device *pdev) +static int ocores_i2c_resume(struct device *dev) { - struct ocores_i2c *i2c = platform_get_drvdata(pdev); + struct ocores_i2c *i2c = dev_get_drvdata(dev); ocores_init(i2c); return 0; } + +static SIMPLE_DEV_PM_OPS(ocores_i2c_pm, ocores_i2c_suspend, ocores_i2c_resume); +#define OCORES_I2C_PM (&ocores_i2c_pm) #else -#define ocores_i2c_suspend NULL -#define ocores_i2c_resume NULL +#define OCORES_I2C_PM NULL #endif static struct of_device_id ocores_i2c_match[] = { @@ -400,12 +396,11 @@ MODULE_DEVICE_TABLE(of, ocores_i2c_match); static struct platform_driver ocores_i2c_driver = { .probe = ocores_i2c_probe, .remove = __devexit_p(ocores_i2c_remove), - .suspend = ocores_i2c_suspend, - .resume = ocores_i2c_resume, .driver = { .owner = THIS_MODULE, .name = "ocores-i2c", .of_match_table = ocores_i2c_match, + .pm = OCORES_I2C_PM, }, }; diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index ee139a598814..f44c83549fe5 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -2,7 +2,7 @@ * (C) Copyright 2009-2010 * Nokia Siemens Networks, michael.lawnick.ext@nsn.com * - * Portions Copyright (C) 2010 Cavium Networks, Inc. + * Portions Copyright (C) 2010, 2011 Cavium Networks, Inc. * * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors. * @@ -11,17 +11,18 @@ * warranty of any kind, whether express or implied. */ +#include <linux/platform_device.h> +#include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of_i2c.h> +#include <linux/delay.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/init.h> - -#include <linux/io.h> #include <linux/i2c.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/of.h> #include <asm/octeon/octeon.h> @@ -65,7 +66,7 @@ struct octeon_i2c { wait_queue_head_t queue; struct i2c_adapter adap; int irq; - int twsi_freq; + u32 twsi_freq; int sys_freq; resource_size_t twsi_phys; void __iomem *twsi_base; @@ -121,10 +122,8 @@ static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) */ static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) { - u64 tmp; - __raw_writeq(data, i2c->twsi_base + TWSI_INT); - tmp = __raw_readq(i2c->twsi_base + TWSI_INT); + __raw_readq(i2c->twsi_base + TWSI_INT); } /** @@ -515,7 +514,6 @@ static int __devinit octeon_i2c_probe(struct platform_device *pdev) { int irq, result = 0; struct octeon_i2c *i2c; - struct octeon_i2c_data *i2c_data; struct resource *res_mem; /* All adaptors have an irq. */ @@ -523,86 +521,90 @@ static int __devinit octeon_i2c_probe(struct platform_device *pdev) if (irq < 0) return irq; - i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) { dev_err(&pdev->dev, "kzalloc failed\n"); result = -ENOMEM; goto out; } i2c->dev = &pdev->dev; - i2c_data = pdev->dev.platform_data; res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res_mem == NULL) { dev_err(i2c->dev, "found no memory resource\n"); result = -ENXIO; - goto fail_region; + goto out; } + i2c->twsi_phys = res_mem->start; + i2c->regsize = resource_size(res_mem); - if (i2c_data == NULL) { - dev_err(i2c->dev, "no I2C frequency data\n"); + /* + * "clock-rate" is a legacy binding, the official binding is + * "clock-frequency". Try the official one first and then + * fall back if it doesn't exist. + */ + if (of_property_read_u32(pdev->dev.of_node, + "clock-frequency", &i2c->twsi_freq) && + of_property_read_u32(pdev->dev.of_node, + "clock-rate", &i2c->twsi_freq)) { + dev_err(i2c->dev, + "no I2C 'clock-rate' or 'clock-frequency' property\n"); result = -ENXIO; - goto fail_region; + goto out; } - i2c->twsi_phys = res_mem->start; - i2c->regsize = resource_size(res_mem); - i2c->twsi_freq = i2c_data->i2c_freq; - i2c->sys_freq = i2c_data->sys_freq; + i2c->sys_freq = octeon_get_io_clock_rate(); - if (!request_mem_region(i2c->twsi_phys, i2c->regsize, res_mem->name)) { + if (!devm_request_mem_region(&pdev->dev, i2c->twsi_phys, i2c->regsize, + res_mem->name)) { dev_err(i2c->dev, "request_mem_region failed\n"); - goto fail_region; + goto out; } - i2c->twsi_base = ioremap(i2c->twsi_phys, i2c->regsize); + i2c->twsi_base = devm_ioremap(&pdev->dev, i2c->twsi_phys, i2c->regsize); init_waitqueue_head(&i2c->queue); i2c->irq = irq; - result = request_irq(i2c->irq, octeon_i2c_isr, 0, DRV_NAME, i2c); + result = devm_request_irq(&pdev->dev, i2c->irq, + octeon_i2c_isr, 0, DRV_NAME, i2c); if (result < 0) { dev_err(i2c->dev, "failed to attach interrupt\n"); - goto fail_irq; + goto out; } result = octeon_i2c_initlowlevel(i2c); if (result) { dev_err(i2c->dev, "init low level failed\n"); - goto fail_add; + goto out; } result = octeon_i2c_setclock(i2c); if (result) { dev_err(i2c->dev, "clock init failed\n"); - goto fail_add; + goto out; } i2c->adap = octeon_i2c_ops; i2c->adap.dev.parent = &pdev->dev; - i2c->adap.nr = pdev->id >= 0 ? pdev->id : 0; + i2c->adap.dev.of_node = pdev->dev.of_node; i2c_set_adapdata(&i2c->adap, i2c); platform_set_drvdata(pdev, i2c); - result = i2c_add_numbered_adapter(&i2c->adap); + result = i2c_add_adapter(&i2c->adap); if (result < 0) { dev_err(i2c->dev, "failed to add adapter\n"); goto fail_add; } - dev_info(i2c->dev, "version %s\n", DRV_VERSION); - return result; + of_i2c_register_devices(&i2c->adap); + + return 0; fail_add: platform_set_drvdata(pdev, NULL); - free_irq(i2c->irq, i2c); -fail_irq: - iounmap(i2c->twsi_base); - release_mem_region(i2c->twsi_phys, i2c->regsize); -fail_region: - kfree(i2c); out: return result; }; @@ -613,19 +615,24 @@ static int __devexit octeon_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&i2c->adap); platform_set_drvdata(pdev, NULL); - free_irq(i2c->irq, i2c); - iounmap(i2c->twsi_base); - release_mem_region(i2c->twsi_phys, i2c->regsize); - kfree(i2c); return 0; }; +static struct of_device_id octeon_i2c_match[] = { + { + .compatible = "cavium,octeon-3860-twsi", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, octeon_i2c_match); + static struct platform_driver octeon_i2c_driver = { .probe = octeon_i2c_probe, .remove = __devexit_p(octeon_i2c_remove), .driver = { .owner = THIS_MODULE, .name = DRV_NAME, + .of_match_table = octeon_i2c_match, }, }; @@ -635,4 +642,3 @@ MODULE_AUTHOR("Michael Lawnick <michael.lawnick.ext@nsn.com>"); MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index c2148332de0f..6849635b268a 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -49,8 +49,8 @@ /* I2C controller revisions present on specific hardware */ #define OMAP_I2C_REV_ON_2430 0x36 -#define OMAP_I2C_REV_ON_3430 0x3C -#define OMAP_I2C_REV_ON_3530_4430 0x40 +#define OMAP_I2C_REV_ON_3430_3530 0x3C +#define OMAP_I2C_REV_ON_3630_4430 0x40 /* timeout waiting for the controller to respond */ #define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000)) @@ -173,7 +173,7 @@ enum { /* Errata definitions */ #define I2C_OMAP_ERRATA_I207 (1 << 0) -#define I2C_OMAP3_1P153 (1 << 1) +#define I2C_OMAP_ERRATA_I462 (1 << 1) struct omap_i2c_dev { struct device *dev; @@ -269,47 +269,6 @@ static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg) (i2c_dev->regs[reg] << i2c_dev->reg_shift)); } -static void omap_i2c_unidle(struct omap_i2c_dev *dev) -{ - if (dev->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) { - omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); - omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, dev->pscstate); - omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, dev->scllstate); - omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, dev->sclhstate); - omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, dev->bufstate); - omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, dev->syscstate); - omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate); - omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); - } - - /* - * Don't write to this register if the IE state is 0 as it can - * cause deadlock. - */ - if (dev->iestate) - omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); -} - -static void omap_i2c_idle(struct omap_i2c_dev *dev) -{ - u16 iv; - - dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); - if (dev->dtrev == OMAP_I2C_IP_VERSION_2) - omap_i2c_write_reg(dev, OMAP_I2C_IP_V2_IRQENABLE_CLR, 1); - else - omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0); - - if (dev->rev < OMAP_I2C_OMAP1_REV_2) { - iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); /* Read clears */ - } else { - omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, dev->iestate); - - /* Flush posted write */ - omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); - } -} - static int omap_i2c_init(struct omap_i2c_dev *dev) { u16 psc = 0, scll = 0, sclh = 0, buf = 0; @@ -346,7 +305,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, SYSC_AUTOIDLE_MASK); - } else if (dev->rev >= OMAP_I2C_REV_ON_3430) { + } else if (dev->rev >= OMAP_I2C_REV_ON_3430_3530) { dev->syscstate = SYSC_AUTOIDLE_MASK; dev->syscstate |= SYSC_ENAWAKEUP_MASK; dev->syscstate |= (SYSC_IDLEMODE_SMART << @@ -468,11 +427,6 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) /* Take the I2C module out of reset: */ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); - dev->errata = 0; - - if (dev->flags & OMAP_I2C_FLAG_APPLY_ERRATA_I207) - dev->errata |= I2C_OMAP_ERRATA_I207; - /* Enable interrupts */ dev->iestate = (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK | @@ -514,7 +468,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) { struct omap_i2c_dev *dev = i2c_get_adapdata(adap); - int r; + unsigned long timeout; u16 w; dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n", @@ -536,7 +490,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap, w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR; omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, w); - init_completion(&dev->cmd_complete); + INIT_COMPLETION(dev->cmd_complete); dev->cmd_err = 0; w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT; @@ -584,12 +538,10 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap, * REVISIT: We should abort the transfer on signals, but the bus goes * into arbitration and we're currently unable to recover from it. */ - r = wait_for_completion_timeout(&dev->cmd_complete, - OMAP_I2C_TIMEOUT); + timeout = wait_for_completion_timeout(&dev->cmd_complete, + OMAP_I2C_TIMEOUT); dev->buf_len = 0; - if (r < 0) - return r; - if (r == 0) { + if (timeout == 0) { dev_err(dev->dev, "controller timed out\n"); omap_i2c_init(dev); return -ETIMEDOUT; @@ -630,7 +582,9 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) int i; int r; - pm_runtime_get_sync(dev->dev); + r = pm_runtime_get_sync(dev->dev); + if (IS_ERR_VALUE(r)) + return r; r = omap_i2c_wait_for_bb(dev); if (r < 0) @@ -767,11 +721,11 @@ omap_i2c_omap1_isr(int this_irq, void *dev_id) #endif /* - * OMAP3430 Errata 1.153: When an XRDY/XDR is hit, wait for XUDF before writing + * OMAP3430 Errata i462: When an XRDY/XDR is hit, wait for XUDF before writing * data to DATA_REG. Otherwise some data bytes can be lost while transferring * them from the memory to the I2C interface. */ -static int errata_omap3_1p153(struct omap_i2c_dev *dev, u16 *stat, int *err) +static int errata_omap3_i462(struct omap_i2c_dev *dev, u16 *stat, int *err) { unsigned long timeout = 10000; @@ -779,7 +733,6 @@ static int errata_omap3_1p153(struct omap_i2c_dev *dev, u16 *stat, int *err) if (*stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) { omap_i2c_ack_stat(dev, *stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)); - *err |= OMAP_I2C_STAT_XUDF; return -ETIMEDOUT; } @@ -792,6 +745,7 @@ static int errata_omap3_1p153(struct omap_i2c_dev *dev, u16 *stat, int *err) return 0; } + *err |= OMAP_I2C_STAT_XUDF; return 0; } @@ -930,8 +884,8 @@ complete: break; } - if ((dev->errata & I2C_OMAP3_1P153) && - errata_omap3_1p153(dev, &stat, &err)) + if ((dev->errata & I2C_OMAP_ERRATA_I462) && + errata_omap3_i462(dev, &stat, &err)) goto complete; omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); @@ -1048,6 +1002,7 @@ omap_i2c_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, dev); + init_completion(&dev->cmd_complete); dev->reg_shift = (dev->flags >> OMAP_I2C_FLAG_BUS_SHIFT__SHIFT) & 3; @@ -1057,12 +1012,19 @@ omap_i2c_probe(struct platform_device *pdev) dev->regs = (u8 *)reg_map_ip_v1; pm_runtime_enable(dev->dev); - pm_runtime_get_sync(dev->dev); + r = pm_runtime_get_sync(dev->dev); + if (IS_ERR_VALUE(r)) + goto err_free_mem; dev->rev = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff; - if (dev->rev <= OMAP_I2C_REV_ON_3430) - dev->errata |= I2C_OMAP3_1P153; + dev->errata = 0; + + if (dev->flags & OMAP_I2C_FLAG_APPLY_ERRATA_I207) + dev->errata |= I2C_OMAP_ERRATA_I207; + + if (dev->rev <= OMAP_I2C_REV_ON_3430_3530) + dev->errata |= I2C_OMAP_ERRATA_I462; if (!(dev->flags & OMAP_I2C_FLAG_NO_FIFO)) { u16 s; @@ -1079,7 +1041,7 @@ omap_i2c_probe(struct platform_device *pdev) dev->fifo_size = (dev->fifo_size / 2); - if (dev->rev >= OMAP_I2C_REV_ON_3530_4430) + if (dev->rev >= OMAP_I2C_REV_ON_3630_4430) dev->b_hw = 0; /* Disable hardware fixes */ else dev->b_hw = 1; /* Enable hardware fixes */ @@ -1095,7 +1057,7 @@ omap_i2c_probe(struct platform_device *pdev) isr = (dev->rev < OMAP_I2C_OMAP1_REV_2) ? omap_i2c_omap1_isr : omap_i2c_isr; - r = request_irq(dev->irq, isr, 0, pdev->name, dev); + r = request_irq(dev->irq, isr, IRQF_NO_SUSPEND, pdev->name, dev); if (r) { dev_err(dev->dev, "failure requesting irq %i\n", dev->irq); @@ -1105,8 +1067,6 @@ omap_i2c_probe(struct platform_device *pdev) dev_info(dev->dev, "bus %d rev%d.%d.%d at %d kHz\n", pdev->id, dev->dtrev, dev->rev >> 4, dev->rev & 0xf, dev->speed); - pm_runtime_put(dev->dev); - adap = &dev->adapter; i2c_set_adapdata(adap, dev); adap->owner = THIS_MODULE; @@ -1126,6 +1086,8 @@ omap_i2c_probe(struct platform_device *pdev) of_i2c_register_devices(adap); + pm_runtime_put(dev->dev); + return 0; err_free_irq: @@ -1134,6 +1096,7 @@ err_unuse_clocks: omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); pm_runtime_put(dev->dev); iounmap(dev->base); + pm_runtime_disable(&pdev->dev); err_free_mem: platform_set_drvdata(pdev, NULL); kfree(dev); @@ -1143,17 +1106,23 @@ err_release_region: return r; } -static int -omap_i2c_remove(struct platform_device *pdev) +static int __devexit omap_i2c_remove(struct platform_device *pdev) { struct omap_i2c_dev *dev = platform_get_drvdata(pdev); struct resource *mem; + int ret; platform_set_drvdata(pdev, NULL); free_irq(dev->irq, dev); i2c_del_adapter(&dev->adapter); + ret = pm_runtime_get_sync(&pdev->dev); + if (IS_ERR_VALUE(ret)) + return ret; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); iounmap(dev->base); kfree(dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1161,13 +1130,26 @@ omap_i2c_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM #ifdef CONFIG_PM_RUNTIME static int omap_i2c_runtime_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct omap_i2c_dev *_dev = platform_get_drvdata(pdev); + u16 iv; + + _dev->iestate = omap_i2c_read_reg(_dev, OMAP_I2C_IE_REG); + + omap_i2c_write_reg(_dev, OMAP_I2C_IE_REG, 0); - omap_i2c_idle(_dev); + if (_dev->rev < OMAP_I2C_OMAP1_REV_2) { + iv = omap_i2c_read_reg(_dev, OMAP_I2C_IV_REG); /* Read clears */ + } else { + omap_i2c_write_reg(_dev, OMAP_I2C_STAT_REG, _dev->iestate); + + /* Flush posted write */ + omap_i2c_read_reg(_dev, OMAP_I2C_STAT_REG); + } return 0; } @@ -1177,23 +1159,40 @@ static int omap_i2c_runtime_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct omap_i2c_dev *_dev = platform_get_drvdata(pdev); - omap_i2c_unidle(_dev); + if (_dev->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) { + omap_i2c_write_reg(_dev, OMAP_I2C_CON_REG, 0); + omap_i2c_write_reg(_dev, OMAP_I2C_PSC_REG, _dev->pscstate); + omap_i2c_write_reg(_dev, OMAP_I2C_SCLL_REG, _dev->scllstate); + omap_i2c_write_reg(_dev, OMAP_I2C_SCLH_REG, _dev->sclhstate); + omap_i2c_write_reg(_dev, OMAP_I2C_BUF_REG, _dev->bufstate); + omap_i2c_write_reg(_dev, OMAP_I2C_SYSC_REG, _dev->syscstate); + omap_i2c_write_reg(_dev, OMAP_I2C_WE_REG, _dev->westate); + omap_i2c_write_reg(_dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); + } + + /* + * Don't write to this register if the IE state is 0 as it can + * cause deadlock. + */ + if (_dev->iestate) + omap_i2c_write_reg(_dev, OMAP_I2C_IE_REG, _dev->iestate); return 0; } +#endif /* CONFIG_PM_RUNTIME */ static struct dev_pm_ops omap_i2c_pm_ops = { - .runtime_suspend = omap_i2c_runtime_suspend, - .runtime_resume = omap_i2c_runtime_resume, + SET_RUNTIME_PM_OPS(omap_i2c_runtime_suspend, + omap_i2c_runtime_resume, NULL) }; #define OMAP_I2C_PM_OPS (&omap_i2c_pm_ops) #else #define OMAP_I2C_PM_OPS NULL -#endif +#endif /* CONFIG_PM */ static struct platform_driver omap_i2c_driver = { .probe = omap_i2c_probe, - .remove = omap_i2c_remove, + .remove = __devexit_p(omap_i2c_remove), .driver = { .name = "omap_i2c", .owner = THIS_MODULE, diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index 99389d2eae51..5d54416770b0 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -587,25 +587,27 @@ static struct i2c_algorithm pnx_algorithm = { }; #ifdef CONFIG_PM -static int i2c_pnx_controller_suspend(struct platform_device *pdev, - pm_message_t state) +static int i2c_pnx_controller_suspend(struct device *dev) { - struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); + struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev); clk_disable(alg_data->clk); return 0; } -static int i2c_pnx_controller_resume(struct platform_device *pdev) +static int i2c_pnx_controller_resume(struct device *dev) { - struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); + struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev); return clk_enable(alg_data->clk); } + +static SIMPLE_DEV_PM_OPS(i2c_pnx_pm, + i2c_pnx_controller_suspend, i2c_pnx_controller_resume); +#define PNX_I2C_PM (&i2c_pnx_pm) #else -#define i2c_pnx_controller_suspend NULL -#define i2c_pnx_controller_resume NULL +#define PNX_I2C_PM NULL #endif static int __devinit i2c_pnx_probe(struct platform_device *pdev) @@ -783,11 +785,10 @@ static struct platform_driver i2c_pnx_driver = { .name = "pnx-i2c", .owner = THIS_MODULE, .of_match_table = of_match_ptr(i2c_pnx_of_match), + .pm = PNX_I2C_PM, }, .probe = i2c_pnx_probe, .remove = __devexit_p(i2c_pnx_remove), - .suspend = i2c_pnx_controller_suspend, - .resume = i2c_pnx_controller_resume, }; static int __init i2c_adap_pnx_init(void) diff --git a/drivers/i2c/busses/i2c-puv3.c b/drivers/i2c/busses/i2c-puv3.c index 93709fbe30eb..d8515be00b98 100644 --- a/drivers/i2c/busses/i2c-puv3.c +++ b/drivers/i2c/busses/i2c-puv3.c @@ -254,7 +254,7 @@ static int __devexit puv3_i2c_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int puv3_i2c_suspend(struct platform_device *dev, pm_message_t state) +static int puv3_i2c_suspend(struct device *dev) { int poll_count; /* Disable the IIC */ @@ -267,23 +267,20 @@ static int puv3_i2c_suspend(struct platform_device *dev, pm_message_t state) return 0; } -static int puv3_i2c_resume(struct platform_device *dev) -{ - return 0 ; -} +static SIMPLE_DEV_PM_OPS(puv3_i2c_pm, puv3_i2c_suspend, NULL); +#define PUV3_I2C_PM (&puv3_i2c_pm) + #else -#define puv3_i2c_suspend NULL -#define puv3_i2c_resume NULL +#define PUV3_I2C_PM NULL #endif static struct platform_driver puv3_i2c_driver = { .probe = puv3_i2c_probe, .remove = __devexit_p(puv3_i2c_remove), - .suspend = puv3_i2c_suspend, - .resume = puv3_i2c_resume, .driver = { .name = "PKUnity-v3-I2C", .owner = THIS_MODULE, + .pm = PUV3_I2C_PM, } }; diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index a997c7d3f95d..1034d93fb838 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -41,13 +41,6 @@ #include <asm/irq.h> -#ifndef CONFIG_HAVE_CLK -#define clk_get(dev, id) NULL -#define clk_put(clk) do { } while (0) -#define clk_disable(clk) do { } while (0) -#define clk_enable(clk) do { } while (0) -#endif - struct pxa_reg_layout { u32 ibmr; u32 idbr; diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 01959154572d..5ae3b0236bd3 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -122,7 +122,7 @@ static inline unsigned int s3c24xx_get_device_quirks(struct platform_device *pde { if (pdev->dev.of_node) { const struct of_device_id *match; - match = of_match_node(&s3c24xx_i2c_match, pdev->dev.of_node); + match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node); return (unsigned int)match->data; } @@ -609,7 +609,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, if (ret != -EAGAIN) { clk_disable(i2c->clk); - pm_runtime_put_sync(&adap->dev); + pm_runtime_put(&adap->dev); return ret; } @@ -619,7 +619,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, } clk_disable(i2c->clk); - pm_runtime_put_sync(&adap->dev); + pm_runtime_put(&adap->dev); return -EREMOTEIO; } diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c index 4d44af181f37..580a0c04cb42 100644 --- a/drivers/i2c/busses/i2c-stu300.c +++ b/drivers/i2c/busses/i2c-stu300.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2009 ST-Ericsson AB + * Copyright (C) 2007-2012 ST-Ericsson AB * License terms: GNU General Public License (GPL) version 2 * ST DDC I2C master mode driver, used in e.g. U300 series platforms. * Author: Linus Walleij <linus.walleij@stericsson.com> @@ -139,8 +139,6 @@ module_param(scl_frequency, uint, 0644); * struct stu300_dev - the stu300 driver state holder * @pdev: parent platform device * @adapter: corresponding I2C adapter - * @phybase: location of I/O area in memory - * @physize: size of I/O area in memory * @clk: hardware block clock * @irq: assigned interrupt line * @cmd_issue_lock: this locks the following cmd_ variables @@ -155,8 +153,6 @@ module_param(scl_frequency, uint, 0644); struct stu300_dev { struct platform_device *pdev; struct i2c_adapter adapter; - resource_size_t phybase; - resource_size_t physize; void __iomem *virtbase; struct clk *clk; int irq; @@ -873,64 +869,44 @@ stu300_probe(struct platform_device *pdev) int ret = 0; char clk_name[] = "I2C0"; - dev = kzalloc(sizeof(struct stu300_dev), GFP_KERNEL); + dev = devm_kzalloc(&pdev->dev, sizeof(struct stu300_dev), GFP_KERNEL); if (!dev) { dev_err(&pdev->dev, "could not allocate device struct\n"); - ret = -ENOMEM; - goto err_no_devmem; + return -ENOMEM; } bus_nr = pdev->id; clk_name[3] += (char)bus_nr; - dev->clk = clk_get(&pdev->dev, clk_name); + dev->clk = devm_clk_get(&pdev->dev, clk_name); if (IS_ERR(dev->clk)) { - ret = PTR_ERR(dev->clk); dev_err(&pdev->dev, "could not retrieve i2c bus clock\n"); - goto err_no_clk; + return PTR_ERR(dev->clk); } dev->pdev = pdev; - platform_set_drvdata(pdev, dev); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENOENT; - goto err_no_resource; - } - - dev->phybase = res->start; - dev->physize = resource_size(res); - - if (request_mem_region(dev->phybase, dev->physize, - NAME " I/O Area") == NULL) { - ret = -EBUSY; - goto err_no_ioregion; - } + if (!res) + return -ENOENT; - dev->virtbase = ioremap(dev->phybase, dev->physize); + dev->virtbase = devm_request_and_ioremap(&pdev->dev, res); dev_dbg(&pdev->dev, "initialize bus device I2C%d on virtual " "base %p\n", bus_nr, dev->virtbase); - if (!dev->virtbase) { - ret = -ENOMEM; - goto err_no_ioremap; - } + if (!dev->virtbase) + return -ENOMEM; dev->irq = platform_get_irq(pdev, 0); - if (request_irq(dev->irq, stu300_irh, 0, - NAME, dev)) { - ret = -EIO; - goto err_no_irq; - } + ret = devm_request_irq(&pdev->dev, dev->irq, stu300_irh, 0, NAME, dev); + if (ret < 0) + return ret; dev->speed = scl_frequency; - clk_enable(dev->clk); + clk_prepare_enable(dev->clk); ret = stu300_init_hw(dev); clk_disable(dev->clk); - if (ret != 0) { dev_err(&dev->pdev->dev, "error initializing hardware.\n"); - goto err_init_hw; + return -EIO; } /* IRQ event handling initialization */ @@ -952,57 +928,43 @@ stu300_probe(struct platform_device *pdev) /* i2c device drivers may be active on return from add_adapter() */ ret = i2c_add_numbered_adapter(adap); if (ret) { - dev_err(&dev->pdev->dev, "failure adding ST Micro DDC " + dev_err(&pdev->dev, "failure adding ST Micro DDC " "I2C adapter\n"); - goto err_add_adapter; + return ret; } - return 0; - err_add_adapter: - err_init_hw: - free_irq(dev->irq, dev); - err_no_irq: - iounmap(dev->virtbase); - err_no_ioremap: - release_mem_region(dev->phybase, dev->physize); - err_no_ioregion: - platform_set_drvdata(pdev, NULL); - err_no_resource: - clk_put(dev->clk); - err_no_clk: - kfree(dev); - err_no_devmem: - dev_err(&pdev->dev, "failed to add " NAME " adapter: %d\n", - pdev->id); - return ret; + platform_set_drvdata(pdev, dev); + return 0; } #ifdef CONFIG_PM -static int stu300_suspend(struct platform_device *pdev, pm_message_t state) +static int stu300_suspend(struct device *device) { - struct stu300_dev *dev = platform_get_drvdata(pdev); + struct stu300_dev *dev = dev_get_drvdata(device); /* Turn off everything */ stu300_wr8(0x00, dev->virtbase + I2C_CR); return 0; } -static int stu300_resume(struct platform_device *pdev) +static int stu300_resume(struct device *device) { int ret = 0; - struct stu300_dev *dev = platform_get_drvdata(pdev); + struct stu300_dev *dev = dev_get_drvdata(device); clk_enable(dev->clk); ret = stu300_init_hw(dev); clk_disable(dev->clk); if (ret != 0) - dev_err(&pdev->dev, "error re-initializing hardware.\n"); + dev_err(device, "error re-initializing hardware.\n"); return ret; } + +static SIMPLE_DEV_PM_OPS(stu300_pm, stu300_suspend, stu300_resume); +#define STU300_I2C_PM (&stu300_pm) #else -#define stu300_suspend NULL -#define stu300_resume NULL +#define STU300_I2C_PM NULL #endif static int __exit @@ -1013,12 +975,7 @@ stu300_remove(struct platform_device *pdev) i2c_del_adapter(&dev->adapter); /* Turn off everything */ stu300_wr8(0x00, dev->virtbase + I2C_CR); - free_irq(dev->irq, dev); - iounmap(dev->virtbase); - release_mem_region(dev->phybase, dev->physize); - clk_put(dev->clk); platform_set_drvdata(pdev, NULL); - kfree(dev); return 0; } @@ -1026,10 +983,9 @@ static struct platform_driver stu300_i2c_driver = { .driver = { .name = NAME, .owner = THIS_MODULE, + .pm = STU300_I2C_PM, }, .remove = __exit_p(stu300_remove), - .suspend = stu300_suspend, - .resume = stu300_resume, }; diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 3da7ee3eb505..66eb53fac202 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -97,8 +97,21 @@ #define I2C_HEADER_10BIT_ADDR (1<<18) #define I2C_HEADER_IE_ENABLE (1<<17) #define I2C_HEADER_REPEAT_START (1<<16) +#define I2C_HEADER_CONTINUE_XFER (1<<15) #define I2C_HEADER_MASTER_ADDR_SHIFT 12 #define I2C_HEADER_SLAVE_ADDR_SHIFT 1 +/* + * msg_end_type: The bus control which need to be send at end of transfer. + * @MSG_END_STOP: Send stop pulse at end of transfer. + * @MSG_END_REPEAT_START: Send repeat start at end of transfer. + * @MSG_END_CONTINUE: The following on message is coming and so do not send + * stop or repeat start. + */ +enum msg_end_type { + MSG_END_STOP, + MSG_END_REPEAT_START, + MSG_END_CONTINUE, +}; /** * struct tegra_i2c_dev - per device i2c context @@ -106,7 +119,6 @@ * @adapter: core i2c layer adapter information * @clk: clock reference for i2c controller * @i2c_clk: clock reference for i2c bus - * @iomem: memory resource for registers * @base: ioremapped registers cookie * @cont_id: i2c controller id, used for for packet header * @irq: irq number of transfer complete interrupt @@ -124,7 +136,6 @@ struct tegra_i2c_dev { struct i2c_adapter adapter; struct clk *clk; struct clk *i2c_clk; - struct resource *iomem; void __iomem *base; int cont_id; int irq; @@ -165,6 +176,10 @@ static void i2c_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned long reg) { writel(val, i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg)); + + /* Read back register to make sure that register writes completed */ + if (reg != I2C_TX_FIFO) + readl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg)); } static u32 i2c_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg) @@ -449,7 +464,7 @@ err: } static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, - struct i2c_msg *msg, int stop) + struct i2c_msg *msg, enum msg_end_type end_state) { u32 packet_header; u32 int_mask; @@ -476,7 +491,9 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); packet_header = I2C_HEADER_IE_ENABLE; - if (!stop) + if (end_state == MSG_END_CONTINUE) + packet_header |= I2C_HEADER_CONTINUE_XFER; + else if (end_state == MSG_END_REPEAT_START) packet_header |= I2C_HEADER_REPEAT_START; if (msg->flags & I2C_M_TEN) { packet_header |= msg->addr; @@ -548,8 +565,14 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], clk_prepare_enable(i2c_dev->clk); for (i = 0; i < num; i++) { - int stop = (i == (num - 1)) ? 1 : 0; - ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], stop); + enum msg_end_type end_type = MSG_END_STOP; + if (i < (num - 1)) { + if (msgs[i + 1].flags & I2C_M_NOSTART) + end_type = MSG_END_CONTINUE; + else + end_type = MSG_END_REPEAT_START; + } + ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type); if (ret) break; } @@ -559,7 +582,8 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], static u32 tegra_i2c_func(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART; } static const struct i2c_algorithm tegra_i2c_algo = { @@ -572,7 +596,6 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev) struct tegra_i2c_dev *i2c_dev; struct tegra_i2c_platform_data *pdata = pdev->dev.platform_data; struct resource *res; - struct resource *iomem; struct clk *clk; struct clk *i2c_clk; const unsigned int *prop; @@ -585,50 +608,41 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev) dev_err(&pdev->dev, "no mem resource\n"); return -EINVAL; } - iomem = request_mem_region(res->start, resource_size(res), pdev->name); - if (!iomem) { - dev_err(&pdev->dev, "I2C region already claimed\n"); - return -EBUSY; - } - base = ioremap(iomem->start, resource_size(iomem)); + base = devm_request_and_ioremap(&pdev->dev, res); if (!base) { - dev_err(&pdev->dev, "Cannot ioremap I2C region\n"); - return -ENOMEM; + dev_err(&pdev->dev, "Cannot request/ioremap I2C registers\n"); + return -EADDRNOTAVAIL; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(&pdev->dev, "no irq resource\n"); - ret = -EINVAL; - goto err_iounmap; + return -EINVAL; } irq = res->start; - clk = clk_get(&pdev->dev, NULL); + clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { dev_err(&pdev->dev, "missing controller clock"); - ret = PTR_ERR(clk); - goto err_release_region; + return PTR_ERR(clk); } - i2c_clk = clk_get(&pdev->dev, "i2c"); + i2c_clk = devm_clk_get(&pdev->dev, "i2c"); if (IS_ERR(i2c_clk)) { dev_err(&pdev->dev, "missing bus clock"); - ret = PTR_ERR(i2c_clk); - goto err_clk_put; + return PTR_ERR(i2c_clk); } - i2c_dev = kzalloc(sizeof(struct tegra_i2c_dev), GFP_KERNEL); + i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); if (!i2c_dev) { - ret = -ENOMEM; - goto err_i2c_clk_put; + dev_err(&pdev->dev, "Could not allocate struct tegra_i2c_dev"); + return -ENOMEM; } i2c_dev->base = base; i2c_dev->clk = clk; i2c_dev->i2c_clk = i2c_clk; - i2c_dev->iomem = iomem; i2c_dev->adapter.algo = &tegra_i2c_algo; i2c_dev->irq = irq; i2c_dev->cont_id = pdev->id; @@ -657,13 +671,14 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev) ret = tegra_i2c_init(i2c_dev); if (ret) { dev_err(&pdev->dev, "Failed to initialize i2c controller"); - goto err_free; + return ret; } - ret = request_irq(i2c_dev->irq, tegra_i2c_isr, 0, pdev->name, i2c_dev); + ret = devm_request_irq(&pdev->dev, i2c_dev->irq, + tegra_i2c_isr, 0, pdev->name, i2c_dev); if (ret) { dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq); - goto err_free; + return ret; } clk_prepare_enable(i2c_dev->i2c_clk); @@ -681,45 +696,26 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev) ret = i2c_add_numbered_adapter(&i2c_dev->adapter); if (ret) { dev_err(&pdev->dev, "Failed to add I2C adapter\n"); - goto err_free_irq; + clk_disable_unprepare(i2c_dev->i2c_clk); + return ret; } of_i2c_register_devices(&i2c_dev->adapter); return 0; -err_free_irq: - free_irq(i2c_dev->irq, i2c_dev); -err_free: - kfree(i2c_dev); -err_i2c_clk_put: - clk_put(i2c_clk); -err_clk_put: - clk_put(clk); -err_release_region: - release_mem_region(iomem->start, resource_size(iomem)); -err_iounmap: - iounmap(base); - return ret; } static int __devexit tegra_i2c_remove(struct platform_device *pdev) { struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev); i2c_del_adapter(&i2c_dev->adapter); - free_irq(i2c_dev->irq, i2c_dev); - clk_put(i2c_dev->i2c_clk); - clk_put(i2c_dev->clk); - release_mem_region(i2c_dev->iomem->start, - resource_size(i2c_dev->iomem)); - iounmap(i2c_dev->base); - kfree(i2c_dev); return 0; } #ifdef CONFIG_PM -static int tegra_i2c_suspend(struct platform_device *pdev, pm_message_t state) +static int tegra_i2c_suspend(struct device *dev) { - struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev); i2c_lock_adapter(&i2c_dev->adapter); i2c_dev->is_suspended = true; @@ -728,9 +724,9 @@ static int tegra_i2c_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int tegra_i2c_resume(struct platform_device *pdev) +static int tegra_i2c_resume(struct device *dev) { - struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev); int ret; i2c_lock_adapter(&i2c_dev->adapter); @@ -748,6 +744,11 @@ static int tegra_i2c_resume(struct platform_device *pdev) return 0; } + +static SIMPLE_DEV_PM_OPS(tegra_i2c_pm, tegra_i2c_suspend, tegra_i2c_resume); +#define TEGRA_I2C_PM (&tegra_i2c_pm) +#else +#define TEGRA_I2C_PM NULL #endif #if defined(CONFIG_OF) @@ -758,21 +759,16 @@ static const struct of_device_id tegra_i2c_of_match[] __devinitconst = { {}, }; MODULE_DEVICE_TABLE(of, tegra_i2c_of_match); -#else -#define tegra_i2c_of_match NULL #endif static struct platform_driver tegra_i2c_driver = { .probe = tegra_i2c_probe, .remove = __devexit_p(tegra_i2c_remove), -#ifdef CONFIG_PM - .suspend = tegra_i2c_suspend, - .resume = tegra_i2c_resume, -#endif .driver = { .name = "tegra-i2c", .owner = THIS_MODULE, - .of_match_table = tegra_i2c_of_match, + .of_match_table = of_match_ptr(tegra_i2c_of_match), + .pm = TEGRA_I2C_PM, }, }; diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 26488aa893d5..2efa56c5ff2c 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1312,6 +1312,37 @@ module_exit(i2c_exit); */ /** + * __i2c_transfer - unlocked flavor of i2c_transfer + * @adap: Handle to I2C bus + * @msgs: One or more messages to execute before STOP is issued to + * terminate the operation; each message begins with a START. + * @num: Number of messages to be executed. + * + * Returns negative errno, else the number of messages executed. + * + * Adapter lock must be held when calling this function. No debug logging + * takes place. adap->algo->master_xfer existence isn't checked. + */ +int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + unsigned long orig_jiffies; + int ret, try; + + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (ret = 0, try = 0; try <= adap->retries; try++) { + ret = adap->algo->master_xfer(adap, msgs, num); + if (ret != -EAGAIN) + break; + if (time_after(jiffies, orig_jiffies + adap->timeout)) + break; + } + + return ret; +} +EXPORT_SYMBOL(__i2c_transfer); + +/** * i2c_transfer - execute a single or combined I2C message * @adap: Handle to I2C bus * @msgs: One or more messages to execute before STOP is issued to @@ -1325,8 +1356,7 @@ module_exit(i2c_exit); */ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { - unsigned long orig_jiffies; - int ret, try; + int ret; /* REVISIT the fault reporting model here is weak: * @@ -1364,15 +1394,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) i2c_lock_adapter(adap); } - /* Retry automatically on arbitration loss */ - orig_jiffies = jiffies; - for (ret = 0, try = 0; try <= adap->retries; try++) { - ret = adap->algo->master_xfer(adap, msgs, num); - if (ret != -EAGAIN) - break; - if (time_after(jiffies, orig_jiffies + adap->timeout)) - break; - } + ret = __i2c_transfer(adap, msgs, num); i2c_unlock_adapter(adap); return ret; diff --git a/drivers/input/misc/88pm80x_onkey.c b/drivers/input/misc/88pm80x_onkey.c new file mode 100644 index 000000000000..7f26e7b6c228 --- /dev/null +++ b/drivers/input/misc/88pm80x_onkey.c @@ -0,0 +1,168 @@ +/* + * Marvell 88PM80x ONKEY driver + * + * Copyright (C) 2012 Marvell International Ltd. + * Haojian Zhuang <haojian.zhuang@marvell.com> + * Qiao Zhou <zhouqiao@marvell.com> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/input.h> +#include <linux/mfd/88pm80x.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define PM800_LONG_ONKEY_EN (1 << 0) +#define PM800_LONG_KEY_DELAY (8) /* 1 .. 16 seconds */ +#define PM800_LONKEY_PRESS_TIME ((PM800_LONG_KEY_DELAY-1) << 4) +#define PM800_LONKEY_PRESS_TIME_MASK (0xF0) +#define PM800_SW_PDOWN (1 << 5) + +struct pm80x_onkey_info { + struct input_dev *idev; + struct pm80x_chip *pm80x; + struct regmap *map; + int irq; +}; + +/* 88PM80x gives us an interrupt when ONKEY is held */ +static irqreturn_t pm80x_onkey_handler(int irq, void *data) +{ + struct pm80x_onkey_info *info = data; + int ret = 0; + unsigned int val; + + ret = regmap_read(info->map, PM800_STATUS_1, &val); + if (ret < 0) { + dev_err(info->idev->dev.parent, "failed to read status: %d\n", ret); + return IRQ_NONE; + } + val &= PM800_ONKEY_STS1; + + input_report_key(info->idev, KEY_POWER, val); + input_sync(info->idev); + + return IRQ_HANDLED; +} + +static SIMPLE_DEV_PM_OPS(pm80x_onkey_pm_ops, pm80x_dev_suspend, + pm80x_dev_resume); + +static int __devinit pm80x_onkey_probe(struct platform_device *pdev) +{ + + struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm80x_onkey_info *info; + int err; + + info = kzalloc(sizeof(struct pm80x_onkey_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pm80x = chip; + + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) { + dev_err(&pdev->dev, "No IRQ resource!\n"); + err = -EINVAL; + goto out; + } + + info->map = info->pm80x->regmap; + if (!info->map) { + dev_err(&pdev->dev, "no regmap!\n"); + err = -EINVAL; + goto out; + } + + info->idev = input_allocate_device(); + if (!info->idev) { + dev_err(&pdev->dev, "Failed to allocate input dev\n"); + err = -ENOMEM; + goto out; + } + + info->idev->name = "88pm80x_on"; + info->idev->phys = "88pm80x_on/input0"; + info->idev->id.bustype = BUS_I2C; + info->idev->dev.parent = &pdev->dev; + info->idev->evbit[0] = BIT_MASK(EV_KEY); + __set_bit(KEY_POWER, info->idev->keybit); + + err = pm80x_request_irq(info->pm80x, info->irq, pm80x_onkey_handler, + IRQF_ONESHOT, "onkey", info); + if (err < 0) { + dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n", + info->irq, err); + goto out_reg; + } + + err = input_register_device(info->idev); + if (err) { + dev_err(&pdev->dev, "Can't register input device: %d\n", err); + goto out_irq; + } + + platform_set_drvdata(pdev, info); + + /* Enable long onkey detection */ + regmap_update_bits(info->map, PM800_RTC_MISC4, PM800_LONG_ONKEY_EN, + PM800_LONG_ONKEY_EN); + /* Set 8-second interval */ + regmap_update_bits(info->map, PM800_RTC_MISC3, + PM800_LONKEY_PRESS_TIME_MASK, + PM800_LONKEY_PRESS_TIME); + + device_init_wakeup(&pdev->dev, 1); + return 0; + +out_irq: + pm80x_free_irq(info->pm80x, info->irq, info); +out_reg: + input_free_device(info->idev); +out: + kfree(info); + return err; +} + +static int __devexit pm80x_onkey_remove(struct platform_device *pdev) +{ + struct pm80x_onkey_info *info = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + pm80x_free_irq(info->pm80x, info->irq, info); + input_unregister_device(info->idev); + kfree(info); + return 0; +} + +static struct platform_driver pm80x_onkey_driver = { + .driver = { + .name = "88pm80x-onkey", + .owner = THIS_MODULE, + .pm = &pm80x_onkey_pm_ops, + }, + .probe = pm80x_onkey_probe, + .remove = __devexit_p(pm80x_onkey_remove), +}; + +module_platform_driver(pm80x_onkey_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Marvell 88PM80x ONKEY driver"); +MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>"); +MODULE_ALIAS("platform:88pm80x-onkey"); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7faf4a7fcaa9..7c0f1ecfdd7a 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -22,6 +22,16 @@ config INPUT_88PM860X_ONKEY To compile this driver as a module, choose M here: the module will be called 88pm860x_onkey. +config INPUT_88PM80X_ONKEY + tristate "88PM80x ONKEY support" + depends on MFD_88PM800 + help + Support the ONKEY of Marvell 88PM80x PMICs as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the module + will be called 88pm80x_onkey. + config INPUT_AB8500_PONKEY tristate "AB8500 Pon (PowerOn) Key" depends on AB8500_CORE diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index f55cdf4916fa..83fe6f5b77d1 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -5,6 +5,7 @@ # Each configuration option enables a list of files. obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o +obj-$(CONFIG_INPUT_88PM80X_ONKEY) += 88pm80x_onkey.o obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o obj-$(CONFIG_INPUT_AD714X) += ad714x.o obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 84ec691c05aa..f06231b7cab1 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -74,8 +74,8 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) ponkey->idev = input; ponkey->ab8500 = ab8500; - ponkey->irq_dbf = irq_dbf; - ponkey->irq_dbr = irq_dbr; + ponkey->irq_dbf = ab8500_irq_get_virq(ab8500, irq_dbf); + ponkey->irq_dbr = ab8500_irq_get_virq(ab8500, irq_dbr); input->name = "AB8500 POn(PowerOn) Key"; input->dev.parent = &pdev->dev; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index d5b390f75c9a..14eaecea2b70 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -40,11 +40,27 @@ * Note that newer firmware allows querying device for maximum useable * coordinates. */ +#define XMIN 0 +#define XMAX 6143 +#define YMIN 0 +#define YMAX 6143 #define XMIN_NOMINAL 1472 #define XMAX_NOMINAL 5472 #define YMIN_NOMINAL 1408 #define YMAX_NOMINAL 4448 +/* Size in bits of absolute position values reported by the hardware */ +#define ABS_POS_BITS 13 + +/* + * Any position values from the hardware above the following limits are + * treated as "wrapped around negative" values that have been truncated to + * the 13-bit reporting range of the hardware. These are just reasonable + * guesses and can be adjusted if hardware is found that operates outside + * of these parameters. + */ +#define X_MAX_POSITIVE (((1 << ABS_POS_BITS) + XMAX) / 2) +#define Y_MAX_POSITIVE (((1 << ABS_POS_BITS) + YMAX) / 2) /***************************************************************************** * Stuff we need even when we do not want native Synaptics support @@ -588,6 +604,12 @@ static int synaptics_parse_hw_state(const unsigned char buf[], hw->right = (buf[0] & 0x02) ? 1 : 0; } + /* Convert wrap-around values to negative */ + if (hw->x > X_MAX_POSITIVE) + hw->x -= 1 << ABS_POS_BITS; + if (hw->y > Y_MAX_POSITIVE) + hw->y -= 1 << ABS_POS_BITS; + return 0; } diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 6533f44be5bd..002041975de9 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -464,7 +464,7 @@ static void wacom_intuos_general(struct wacom_wac *wacom) t = (data[6] << 2) | ((data[7] >> 6) & 3); if ((features->type >= INTUOS4S && features->type <= INTUOS4L) || (features->type >= INTUOS5S && features->type <= INTUOS5L) || - features->type == WACOM_21UX2 || features->type == WACOM_24HD) { + (features->type >= WACOM_21UX2 && features->type <= WACOM_24HD)) { t = (t << 1) | (data[1] & 1); } input_report_abs(input, ABS_PRESSURE, t); @@ -614,7 +614,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) input_report_abs(input, ABS_MISC, 0); } } else { - if (features->type == WACOM_21UX2) { + if (features->type == WACOM_21UX2 || features->type == WACOM_22HD) { input_report_key(input, BTN_0, (data[5] & 0x01)); input_report_key(input, BTN_1, (data[6] & 0x01)); input_report_key(input, BTN_2, (data[6] & 0x02)); @@ -633,6 +633,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) input_report_key(input, BTN_Z, (data[8] & 0x20)); input_report_key(input, BTN_BASE, (data[8] & 0x40)); input_report_key(input, BTN_BASE2, (data[8] & 0x80)); + + if (features->type == WACOM_22HD) { + input_report_key(input, KEY_PROG1, data[9] & 0x01); + input_report_key(input, KEY_PROG2, data[9] & 0x02); + input_report_key(input, KEY_PROG3, data[9] & 0x04); + } } else { input_report_key(input, BTN_0, (data[5] & 0x01)); input_report_key(input, BTN_1, (data[5] & 0x02)); @@ -1231,6 +1237,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) case CINTIQ: case WACOM_BEE: case WACOM_21UX2: + case WACOM_22HD: case WACOM_24HD: sync = wacom_intuos_irq(wacom_wac); break; @@ -1432,6 +1439,12 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, wacom_setup_cintiq(wacom_wac); break; + case WACOM_22HD: + __set_bit(KEY_PROG1, input_dev->keybit); + __set_bit(KEY_PROG2, input_dev->keybit); + __set_bit(KEY_PROG3, input_dev->keybit); + /* fall through */ + case WACOM_21UX2: __set_bit(BTN_A, input_dev->keybit); __set_bit(BTN_B, input_dev->keybit); @@ -1858,6 +1871,9 @@ static const struct wacom_features wacom_features_0xF0 = static const struct wacom_features wacom_features_0xCC = { "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047, 63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xFA = + { "Wacom Cintiq 22HD", WACOM_PKGLEN_INTUOS, 95840, 54260, 2047, + 63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0x90 = { "Wacom ISDv4 90", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; @@ -2075,6 +2091,7 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xEF) }, { USB_DEVICE_WACOM(0x47) }, { USB_DEVICE_WACOM(0xF4) }, + { USB_DEVICE_WACOM(0xFA) }, { USB_DEVICE_LENOVO(0x6004) }, { } }; diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index bd5d37b28714..96c185cc301e 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -73,8 +73,9 @@ enum { INTUOS5S, INTUOS5, INTUOS5L, - WACOM_24HD, WACOM_21UX2, + WACOM_22HD, + WACOM_24HD, CINTIQ, WACOM_BEE, WACOM_MO, diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 73bd2f6b82ec..1ba232cbc09d 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -472,6 +472,19 @@ config TOUCHSCREEN_PENMOUNT To compile this driver as a module, choose M here: the module will be called penmount. +config TOUCHSCREEN_EDT_FT5X06 + tristate "EDT FocalTech FT5x06 I2C Touchscreen support" + depends on I2C + help + Say Y here if you have an EDT "Polytouch" touchscreen based + on the FocalTech FT5x06 family of controllers connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called edt-ft5x06. + config TOUCHSCREEN_MIGOR tristate "Renesas MIGO-R touchscreen" depends on SH_MIGOR && I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 5920c60f999d..178eb128d90f 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o +obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c new file mode 100644 index 000000000000..9afc777a40a7 --- /dev/null +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -0,0 +1,898 @@ +/* + * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * This is a driver for the EDT "Polytouch" family of touch controllers + * based on the FocalTech FT5x06 line of chips. + * + * Development of this driver has been sponsored by Glyn: + * http://www.glyn.com/Products/Displays + */ + +#include <linux/module.h> +#include <linux/ratelimit.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/i2c.h> +#include <linux/uaccess.h> +#include <linux/delay.h> +#include <linux/debugfs.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/input/mt.h> +#include <linux/input/edt-ft5x06.h> + +#define MAX_SUPPORT_POINTS 5 + +#define WORK_REGISTER_THRESHOLD 0x00 +#define WORK_REGISTER_REPORT_RATE 0x08 +#define WORK_REGISTER_GAIN 0x30 +#define WORK_REGISTER_OFFSET 0x31 +#define WORK_REGISTER_NUM_X 0x33 +#define WORK_REGISTER_NUM_Y 0x34 + +#define WORK_REGISTER_OPMODE 0x3c +#define FACTORY_REGISTER_OPMODE 0x01 + +#define TOUCH_EVENT_DOWN 0x00 +#define TOUCH_EVENT_UP 0x01 +#define TOUCH_EVENT_ON 0x02 +#define TOUCH_EVENT_RESERVED 0x03 + +#define EDT_NAME_LEN 23 +#define EDT_SWITCH_MODE_RETRIES 10 +#define EDT_SWITCH_MODE_DELAY 5 /* msec */ +#define EDT_RAW_DATA_RETRIES 100 +#define EDT_RAW_DATA_DELAY 1 /* msec */ + +struct edt_ft5x06_ts_data { + struct i2c_client *client; + struct input_dev *input; + u16 num_x; + u16 num_y; + +#if defined(CONFIG_DEBUG_FS) + struct dentry *debug_dir; + u8 *raw_buffer; + size_t raw_bufsize; +#endif + + struct mutex mutex; + bool factory_mode; + int threshold; + int gain; + int offset; + int report_rate; + + char name[EDT_NAME_LEN]; +}; + +static int edt_ft5x06_ts_readwrite(struct i2c_client *client, + u16 wr_len, u8 *wr_buf, + u16 rd_len, u8 *rd_buf) +{ + struct i2c_msg wrmsg[2]; + int i = 0; + int ret; + + if (wr_len) { + wrmsg[i].addr = client->addr; + wrmsg[i].flags = 0; + wrmsg[i].len = wr_len; + wrmsg[i].buf = wr_buf; + i++; + } + if (rd_len) { + wrmsg[i].addr = client->addr; + wrmsg[i].flags = I2C_M_RD; + wrmsg[i].len = rd_len; + wrmsg[i].buf = rd_buf; + i++; + } + + ret = i2c_transfer(client->adapter, wrmsg, i); + if (ret < 0) + return ret; + if (ret != i) + return -EIO; + + return 0; +} + +static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, + u8 *buf, int buflen) +{ + int i; + u8 crc = 0; + + for (i = 0; i < buflen - 1; i++) + crc ^= buf[i]; + + if (crc != buf[buflen-1]) { + dev_err_ratelimited(&tsdata->client->dev, + "crc error: 0x%02x expected, got 0x%02x\n", + crc, buf[buflen-1]); + return false; + } + + return true; +} + +static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) +{ + struct edt_ft5x06_ts_data *tsdata = dev_id; + struct device *dev = &tsdata->client->dev; + u8 cmd = 0xf9; + u8 rdbuf[26]; + int i, type, x, y, id; + int error; + + memset(rdbuf, 0, sizeof(rdbuf)); + + error = edt_ft5x06_ts_readwrite(tsdata->client, + sizeof(cmd), &cmd, + sizeof(rdbuf), rdbuf); + if (error) { + dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n", + error); + goto out; + } + + if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != 26) { + dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n", + rdbuf[0], rdbuf[1], rdbuf[2]); + goto out; + } + + if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, 26)) + goto out; + + for (i = 0; i < MAX_SUPPORT_POINTS; i++) { + u8 *buf = &rdbuf[i * 4 + 5]; + bool down; + + type = buf[0] >> 6; + /* ignore Reserved events */ + if (type == TOUCH_EVENT_RESERVED) + continue; + + x = ((buf[0] << 8) | buf[1]) & 0x0fff; + y = ((buf[2] << 8) | buf[3]) & 0x0fff; + id = (buf[2] >> 4) & 0x0f; + down = (type != TOUCH_EVENT_UP); + + input_mt_slot(tsdata->input, id); + input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); + + if (!down) + continue; + + input_report_abs(tsdata->input, ABS_MT_POSITION_X, x); + input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y); + } + + input_mt_report_pointer_emulation(tsdata->input, true); + input_sync(tsdata->input); + +out: + return IRQ_HANDLED; +} + +static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, + u8 addr, u8 value) +{ + u8 wrbuf[4]; + + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[2] = value; + wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; + + return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL); +} + +static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, + u8 addr) +{ + u8 wrbuf[2], rdbuf[2]; + int error; + + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; + + error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, rdbuf); + if (error) + return error; + + if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { + dev_err(&tsdata->client->dev, + "crc error: 0x%02x expected, got 0x%02x\n", + wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], rdbuf[1]); + return -EIO; + } + + return rdbuf[0]; +} + +struct edt_ft5x06_attribute { + struct device_attribute dattr; + size_t field_offset; + u8 limit_low; + u8 limit_high; + u8 addr; +}; + +#define EDT_ATTR(_field, _mode, _addr, _limit_low, _limit_high) \ + struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \ + .dattr = __ATTR(_field, _mode, \ + edt_ft5x06_setting_show, \ + edt_ft5x06_setting_store), \ + .field_offset = \ + offsetof(struct edt_ft5x06_ts_data, _field), \ + .limit_low = _limit_low, \ + .limit_high = _limit_high, \ + .addr = _addr, \ + } + +static ssize_t edt_ft5x06_setting_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + struct edt_ft5x06_attribute *attr = + container_of(dattr, struct edt_ft5x06_attribute, dattr); + u8 *field = (u8 *)((char *)tsdata + attr->field_offset); + int val; + size_t count = 0; + int error = 0; + + mutex_lock(&tsdata->mutex); + + if (tsdata->factory_mode) { + error = -EIO; + goto out; + } + + val = edt_ft5x06_register_read(tsdata, attr->addr); + if (val < 0) { + error = val; + dev_err(&tsdata->client->dev, + "Failed to fetch attribute %s, error %d\n", + dattr->attr.name, error); + goto out; + } + + if (val != *field) { + dev_warn(&tsdata->client->dev, + "%s: read (%d) and stored value (%d) differ\n", + dattr->attr.name, val, *field); + *field = val; + } + + count = scnprintf(buf, PAGE_SIZE, "%d\n", val); +out: + mutex_unlock(&tsdata->mutex); + return error ?: count; +} + +static ssize_t edt_ft5x06_setting_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + struct edt_ft5x06_attribute *attr = + container_of(dattr, struct edt_ft5x06_attribute, dattr); + u8 *field = (u8 *)((char *)tsdata + attr->field_offset); + unsigned int val; + int error; + + mutex_lock(&tsdata->mutex); + + if (tsdata->factory_mode) { + error = -EIO; + goto out; + } + + error = kstrtouint(buf, 0, &val); + if (error) + goto out; + + if (val < attr->limit_low || val > attr->limit_high) { + error = -ERANGE; + goto out; + } + + error = edt_ft5x06_register_write(tsdata, attr->addr, val); + if (error) { + dev_err(&tsdata->client->dev, + "Failed to update attribute %s, error: %d\n", + dattr->attr.name, error); + goto out; + } + + *field = val; + +out: + mutex_unlock(&tsdata->mutex); + return error ?: count; +} + +static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, 0, 31); +static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, 0, 31); +static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, + WORK_REGISTER_THRESHOLD, 20, 80); +static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, + WORK_REGISTER_REPORT_RATE, 3, 14); + +static struct attribute *edt_ft5x06_attrs[] = { + &edt_ft5x06_attr_gain.dattr.attr, + &edt_ft5x06_attr_offset.dattr.attr, + &edt_ft5x06_attr_threshold.dattr.attr, + &edt_ft5x06_attr_report_rate.dattr.attr, + NULL +}; + +static const struct attribute_group edt_ft5x06_attr_group = { + .attrs = edt_ft5x06_attrs, +}; + +#ifdef CONFIG_DEBUG_FS +static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) +{ + struct i2c_client *client = tsdata->client; + int retries = EDT_SWITCH_MODE_RETRIES; + int ret; + int error; + + disable_irq(client->irq); + + if (!tsdata->raw_buffer) { + tsdata->raw_bufsize = tsdata->num_x * tsdata->num_y * + sizeof(u16); + tsdata->raw_buffer = kzalloc(tsdata->raw_bufsize, GFP_KERNEL); + if (!tsdata->raw_buffer) { + error = -ENOMEM; + goto err_out; + } + } + + /* mode register is 0x3c when in the work mode */ + error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03); + if (error) { + dev_err(&client->dev, + "failed to switch to factory mode, error %d\n", error); + goto err_out; + } + + tsdata->factory_mode = true; + do { + mdelay(EDT_SWITCH_MODE_DELAY); + /* mode register is 0x01 when in factory mode */ + ret = edt_ft5x06_register_read(tsdata, FACTORY_REGISTER_OPMODE); + if (ret == 0x03) + break; + } while (--retries > 0); + + if (retries == 0) { + dev_err(&client->dev, "not in factory mode after %dms.\n", + EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY); + error = -EIO; + goto err_out; + } + + return 0; + +err_out: + kfree(tsdata->raw_buffer); + tsdata->raw_buffer = NULL; + tsdata->factory_mode = false; + enable_irq(client->irq); + + return error; +} + +static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) +{ + struct i2c_client *client = tsdata->client; + int retries = EDT_SWITCH_MODE_RETRIES; + int ret; + int error; + + /* mode register is 0x01 when in the factory mode */ + error = edt_ft5x06_register_write(tsdata, FACTORY_REGISTER_OPMODE, 0x1); + if (error) { + dev_err(&client->dev, + "failed to switch to work mode, error: %d\n", error); + return error; + } + + tsdata->factory_mode = false; + + do { + mdelay(EDT_SWITCH_MODE_DELAY); + /* mode register is 0x01 when in factory mode */ + ret = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OPMODE); + if (ret == 0x01) + break; + } while (--retries > 0); + + if (retries == 0) { + dev_err(&client->dev, "not in work mode after %dms.\n", + EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY); + tsdata->factory_mode = true; + return -EIO; + } + + if (tsdata->raw_buffer) + kfree(tsdata->raw_buffer); + tsdata->raw_buffer = NULL; + + /* restore parameters */ + edt_ft5x06_register_write(tsdata, WORK_REGISTER_THRESHOLD, + tsdata->threshold); + edt_ft5x06_register_write(tsdata, WORK_REGISTER_GAIN, + tsdata->gain); + edt_ft5x06_register_write(tsdata, WORK_REGISTER_OFFSET, + tsdata->offset); + edt_ft5x06_register_write(tsdata, WORK_REGISTER_REPORT_RATE, + tsdata->report_rate); + + enable_irq(client->irq); + + return 0; +} + +static int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode) +{ + struct edt_ft5x06_ts_data *tsdata = data; + + *mode = tsdata->factory_mode; + + return 0; +}; + +static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode) +{ + struct edt_ft5x06_ts_data *tsdata = data; + int retval = 0; + + if (mode > 1) + return -ERANGE; + + mutex_lock(&tsdata->mutex); + + if (mode != tsdata->factory_mode) { + retval = mode ? edt_ft5x06_factory_mode(tsdata) : + edt_ft5x06_work_mode(tsdata); + } + + mutex_unlock(&tsdata->mutex); + + return retval; +}; + +DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get, + edt_ft5x06_debugfs_mode_set, "%llu\n"); + +static int edt_ft5x06_debugfs_raw_data_open(struct inode *inode, + struct file *file) +{ + file->private_data = inode->i_private; + + return 0; +} + +static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, + char __user *buf, size_t count, loff_t *off) +{ + struct edt_ft5x06_ts_data *tsdata = file->private_data; + struct i2c_client *client = tsdata->client; + int retries = EDT_RAW_DATA_RETRIES; + int val, i, error; + size_t read = 0; + int colbytes; + char wrbuf[3]; + u8 *rdbuf; + + if (*off < 0 || *off >= tsdata->raw_bufsize) + return 0; + + mutex_lock(&tsdata->mutex); + + if (!tsdata->factory_mode || !tsdata->raw_buffer) { + error = -EIO; + goto out; + } + + error = edt_ft5x06_register_write(tsdata, 0x08, 0x01); + if (error) { + dev_dbg(&client->dev, + "failed to write 0x08 register, error %d\n", error); + goto out; + } + + do { + msleep(EDT_RAW_DATA_DELAY); + val = edt_ft5x06_register_read(tsdata, 0x08); + if (val < 1) + break; + } while (--retries > 0); + + if (val < 0) { + error = val; + dev_dbg(&client->dev, + "failed to read 0x08 register, error %d\n", error); + goto out; + } + + if (retries == 0) { + dev_dbg(&client->dev, + "timed out waiting for register to settle\n"); + error = -ETIMEDOUT; + goto out; + } + + rdbuf = tsdata->raw_buffer; + colbytes = tsdata->num_y * sizeof(u16); + + wrbuf[0] = 0xf5; + wrbuf[1] = 0x0e; + for (i = 0; i < tsdata->num_x; i++) { + wrbuf[2] = i; /* column index */ + error = edt_ft5x06_ts_readwrite(tsdata->client, + sizeof(wrbuf), wrbuf, + colbytes, rdbuf); + if (error) + goto out; + + rdbuf += colbytes; + } + + read = min_t(size_t, count, tsdata->raw_bufsize - *off); + error = copy_to_user(buf, tsdata->raw_buffer + *off, read); + if (!error) + *off += read; +out: + mutex_unlock(&tsdata->mutex); + return error ?: read; +}; + + +static const struct file_operations debugfs_raw_data_fops = { + .open = edt_ft5x06_debugfs_raw_data_open, + .read = edt_ft5x06_debugfs_raw_data_read, +}; + +static void __devinit +edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, + const char *debugfs_name) +{ + tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL); + if (!tsdata->debug_dir) + return; + + debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x); + debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y); + + debugfs_create_file("mode", S_IRUSR | S_IWUSR, + tsdata->debug_dir, tsdata, &debugfs_mode_fops); + debugfs_create_file("raw_data", S_IRUSR, + tsdata->debug_dir, tsdata, &debugfs_raw_data_fops); +} + +static void __devexit +edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) +{ + if (tsdata->debug_dir) + debugfs_remove_recursive(tsdata->debug_dir); +} + +#else + +static inline void +edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, + const char *debugfs_name) +{ +} + +static inline void +edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) +{ +} + +#endif /* CONFIG_DEBUGFS */ + + + +static int __devinit edt_ft5x06_ts_reset(struct i2c_client *client, + int reset_pin) +{ + int error; + + if (gpio_is_valid(reset_pin)) { + /* this pulls reset down, enabling the low active reset */ + error = gpio_request_one(reset_pin, GPIOF_OUT_INIT_LOW, + "edt-ft5x06 reset"); + if (error) { + dev_err(&client->dev, + "Failed to request GPIO %d as reset pin, error %d\n", + reset_pin, error); + return error; + } + + mdelay(50); + gpio_set_value(reset_pin, 1); + mdelay(100); + } + + return 0; +} + +static int __devinit edt_ft5x06_ts_identify(struct i2c_client *client, + char *model_name, + char *fw_version) +{ + u8 rdbuf[EDT_NAME_LEN]; + char *p; + int error; + + error = edt_ft5x06_ts_readwrite(client, 1, "\xbb", + EDT_NAME_LEN - 1, rdbuf); + if (error) + return error; + + /* remove last '$' end marker */ + rdbuf[EDT_NAME_LEN - 1] = '\0'; + if (rdbuf[EDT_NAME_LEN - 2] == '$') + rdbuf[EDT_NAME_LEN - 2] = '\0'; + + /* look for Model/Version separator */ + p = strchr(rdbuf, '*'); + if (p) + *p++ = '\0'; + + strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN); + strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); + + return 0; +} + +#define EDT_ATTR_CHECKSET(name, reg) \ + if (pdata->name >= edt_ft5x06_attr_##name.limit_low && \ + pdata->name <= edt_ft5x06_attr_##name.limit_high) \ + edt_ft5x06_register_write(tsdata, reg, pdata->name) + +static void __devinit +edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata, + const struct edt_ft5x06_platform_data *pdata) +{ + if (!pdata->use_parameters) + return; + + /* pick up defaults from the platform data */ + EDT_ATTR_CHECKSET(threshold, WORK_REGISTER_THRESHOLD); + EDT_ATTR_CHECKSET(gain, WORK_REGISTER_GAIN); + EDT_ATTR_CHECKSET(offset, WORK_REGISTER_OFFSET); + EDT_ATTR_CHECKSET(report_rate, WORK_REGISTER_REPORT_RATE); +} + +static void __devinit +edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) +{ + tsdata->threshold = edt_ft5x06_register_read(tsdata, + WORK_REGISTER_THRESHOLD); + tsdata->gain = edt_ft5x06_register_read(tsdata, WORK_REGISTER_GAIN); + tsdata->offset = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OFFSET); + tsdata->report_rate = edt_ft5x06_register_read(tsdata, + WORK_REGISTER_REPORT_RATE); + tsdata->num_x = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_X); + tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y); +} + +static int __devinit edt_ft5x06_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct edt_ft5x06_platform_data *pdata = + client->dev.platform_data; + struct edt_ft5x06_ts_data *tsdata; + struct input_dev *input; + int error; + char fw_version[EDT_NAME_LEN]; + + dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); + + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + error = edt_ft5x06_ts_reset(client, pdata->reset_pin); + if (error) + return error; + + if (gpio_is_valid(pdata->irq_pin)) { + error = gpio_request_one(pdata->irq_pin, + GPIOF_IN, "edt-ft5x06 irq"); + if (error) { + dev_err(&client->dev, + "Failed to request GPIO %d, error %d\n", + pdata->irq_pin, error); + return error; + } + } + + tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL); + input = input_allocate_device(); + if (!tsdata || !input) { + dev_err(&client->dev, "failed to allocate driver data.\n"); + error = -ENOMEM; + goto err_free_mem; + } + + mutex_init(&tsdata->mutex); + tsdata->client = client; + tsdata->input = input; + tsdata->factory_mode = false; + + error = edt_ft5x06_ts_identify(client, tsdata->name, fw_version); + if (error) { + dev_err(&client->dev, "touchscreen probe failed\n"); + goto err_free_mem; + } + + edt_ft5x06_ts_get_defaults(tsdata, pdata); + edt_ft5x06_ts_get_parameters(tsdata); + + dev_dbg(&client->dev, + "Model \"%s\", Rev. \"%s\", %dx%d sensors\n", + tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); + + input->name = tsdata->name; + input->id.bustype = BUS_I2C; + input->dev.parent = &client->dev; + + __set_bit(EV_SYN, input->evbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_ABS, input->evbit); + __set_bit(BTN_TOUCH, input->keybit); + input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0); + input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_X, + 0, tsdata->num_x * 64 - 1, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, + 0, tsdata->num_y * 64 - 1, 0, 0); + error = input_mt_init_slots(input, MAX_SUPPORT_POINTS); + if (error) { + dev_err(&client->dev, "Unable to init MT slots.\n"); + goto err_free_mem; + } + + input_set_drvdata(input, tsdata); + i2c_set_clientdata(client, tsdata); + + error = request_threaded_irq(client->irq, NULL, edt_ft5x06_ts_isr, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->name, tsdata); + if (error) { + dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); + goto err_free_mem; + } + + error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group); + if (error) + goto err_free_irq; + + error = input_register_device(input); + if (error) + goto err_remove_attrs; + + edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); + device_init_wakeup(&client->dev, 1); + + dev_dbg(&client->dev, + "EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.\n", + pdata->irq_pin, pdata->reset_pin); + + return 0; + +err_remove_attrs: + sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); +err_free_irq: + free_irq(client->irq, tsdata); +err_free_mem: + input_free_device(input); + kfree(tsdata); + + if (gpio_is_valid(pdata->irq_pin)) + gpio_free(pdata->irq_pin); + + return error; +} + +static int __devexit edt_ft5x06_ts_remove(struct i2c_client *client) +{ + const struct edt_ft5x06_platform_data *pdata = + dev_get_platdata(&client->dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + edt_ft5x06_ts_teardown_debugfs(tsdata); + sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); + + free_irq(client->irq, tsdata); + input_unregister_device(tsdata->input); + + if (gpio_is_valid(pdata->irq_pin)) + gpio_free(pdata->irq_pin); + if (gpio_is_valid(pdata->reset_pin)) + gpio_free(pdata->reset_pin); + + kfree(tsdata->raw_buffer); + kfree(tsdata); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int edt_ft5x06_ts_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int edt_ft5x06_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops, + edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume); + +static const struct i2c_device_id edt_ft5x06_ts_id[] = { + { "edt-ft5x06", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); + +static struct i2c_driver edt_ft5x06_ts_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "edt_ft5x06", + .pm = &edt_ft5x06_ts_pm_ops, + }, + .id_table = edt_ft5x06_ts_id, + .probe = edt_ft5x06_ts_probe, + .remove = __devexit_p(edt_ft5x06_ts_remove), +}; + +module_i2c_driver(edt_ft5x06_ts_driver); + +MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>"); +MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 10f122a3a856..1eee45b69b71 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -260,15 +260,6 @@ config DM_DEBUG_BLOCK_STACK_TRACING If unsure, say N. -config DM_DEBUG_SPACE_MAPS - boolean "Extra validation for thin provisioning space maps" - depends on DM_THIN_PROVISIONING - ---help--- - Enable this for messages that may help debug problems with the - space maps used by thin provisioning. - - If unsure, say N. - config DM_MIRROR tristate "Mirror target" depends on BLK_DEV_DM diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 3f06df59fd82..664743d6a6cd 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -42,21 +42,21 @@ struct convert_context { unsigned int offset_out; unsigned int idx_in; unsigned int idx_out; - sector_t sector; - atomic_t pending; + sector_t cc_sector; + atomic_t cc_pending; }; /* * per bio private data */ struct dm_crypt_io { - struct dm_target *target; + struct crypt_config *cc; struct bio *base_bio; struct work_struct work; struct convert_context ctx; - atomic_t pending; + atomic_t io_pending; int error; sector_t sector; struct dm_crypt_io *base_io; @@ -109,9 +109,6 @@ enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID }; */ struct crypt_cpu { struct ablkcipher_request *req; - /* ESSIV: struct crypto_cipher *essiv_tfm */ - void *iv_private; - struct crypto_ablkcipher *tfms[0]; }; /* @@ -151,6 +148,10 @@ struct crypt_config { * per_cpu_ptr() only. */ struct crypt_cpu __percpu *cpu; + + /* ESSIV: struct crypto_cipher *essiv_tfm */ + void *iv_private; + struct crypto_ablkcipher **tfms; unsigned tfms_count; /* @@ -193,7 +194,7 @@ static struct crypt_cpu *this_crypt_config(struct crypt_config *cc) */ static struct crypto_ablkcipher *any_tfm(struct crypt_config *cc) { - return __this_cpu_ptr(cc->cpu)->tfms[0]; + return cc->tfms[0]; } /* @@ -258,7 +259,7 @@ static int crypt_iv_essiv_init(struct crypt_config *cc) struct hash_desc desc; struct scatterlist sg; struct crypto_cipher *essiv_tfm; - int err, cpu; + int err; sg_init_one(&sg, cc->key, cc->key_size); desc.tfm = essiv->hash_tfm; @@ -268,14 +269,12 @@ static int crypt_iv_essiv_init(struct crypt_config *cc) if (err) return err; - for_each_possible_cpu(cpu) { - essiv_tfm = per_cpu_ptr(cc->cpu, cpu)->iv_private, + essiv_tfm = cc->iv_private; - err = crypto_cipher_setkey(essiv_tfm, essiv->salt, - crypto_hash_digestsize(essiv->hash_tfm)); - if (err) - return err; - } + err = crypto_cipher_setkey(essiv_tfm, essiv->salt, + crypto_hash_digestsize(essiv->hash_tfm)); + if (err) + return err; return 0; } @@ -286,16 +285,14 @@ static int crypt_iv_essiv_wipe(struct crypt_config *cc) struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv; unsigned salt_size = crypto_hash_digestsize(essiv->hash_tfm); struct crypto_cipher *essiv_tfm; - int cpu, r, err = 0; + int r, err = 0; memset(essiv->salt, 0, salt_size); - for_each_possible_cpu(cpu) { - essiv_tfm = per_cpu_ptr(cc->cpu, cpu)->iv_private; - r = crypto_cipher_setkey(essiv_tfm, essiv->salt, salt_size); - if (r) - err = r; - } + essiv_tfm = cc->iv_private; + r = crypto_cipher_setkey(essiv_tfm, essiv->salt, salt_size); + if (r) + err = r; return err; } @@ -335,8 +332,6 @@ static struct crypto_cipher *setup_essiv_cpu(struct crypt_config *cc, static void crypt_iv_essiv_dtr(struct crypt_config *cc) { - int cpu; - struct crypt_cpu *cpu_cc; struct crypto_cipher *essiv_tfm; struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv; @@ -346,15 +341,12 @@ static void crypt_iv_essiv_dtr(struct crypt_config *cc) kzfree(essiv->salt); essiv->salt = NULL; - for_each_possible_cpu(cpu) { - cpu_cc = per_cpu_ptr(cc->cpu, cpu); - essiv_tfm = cpu_cc->iv_private; + essiv_tfm = cc->iv_private; - if (essiv_tfm) - crypto_free_cipher(essiv_tfm); + if (essiv_tfm) + crypto_free_cipher(essiv_tfm); - cpu_cc->iv_private = NULL; - } + cc->iv_private = NULL; } static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti, @@ -363,7 +355,7 @@ static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti, struct crypto_cipher *essiv_tfm = NULL; struct crypto_hash *hash_tfm = NULL; u8 *salt = NULL; - int err, cpu; + int err; if (!opts) { ti->error = "Digest algorithm missing for ESSIV mode"; @@ -388,15 +380,13 @@ static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti, cc->iv_gen_private.essiv.salt = salt; cc->iv_gen_private.essiv.hash_tfm = hash_tfm; - for_each_possible_cpu(cpu) { - essiv_tfm = setup_essiv_cpu(cc, ti, salt, - crypto_hash_digestsize(hash_tfm)); - if (IS_ERR(essiv_tfm)) { - crypt_iv_essiv_dtr(cc); - return PTR_ERR(essiv_tfm); - } - per_cpu_ptr(cc->cpu, cpu)->iv_private = essiv_tfm; + essiv_tfm = setup_essiv_cpu(cc, ti, salt, + crypto_hash_digestsize(hash_tfm)); + if (IS_ERR(essiv_tfm)) { + crypt_iv_essiv_dtr(cc); + return PTR_ERR(essiv_tfm); } + cc->iv_private = essiv_tfm; return 0; @@ -410,7 +400,7 @@ bad: static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv, struct dm_crypt_request *dmreq) { - struct crypto_cipher *essiv_tfm = this_crypt_config(cc)->iv_private; + struct crypto_cipher *essiv_tfm = cc->iv_private; memset(iv, 0, cc->iv_size); *(__le64 *)iv = cpu_to_le64(dmreq->iv_sector); @@ -664,7 +654,7 @@ static void crypt_convert_init(struct crypt_config *cc, ctx->offset_out = 0; ctx->idx_in = bio_in ? bio_in->bi_idx : 0; ctx->idx_out = bio_out ? bio_out->bi_idx : 0; - ctx->sector = sector + cc->iv_offset; + ctx->cc_sector = sector + cc->iv_offset; init_completion(&ctx->restart); } @@ -695,12 +685,12 @@ static int crypt_convert_block(struct crypt_config *cc, struct bio_vec *bv_out = bio_iovec_idx(ctx->bio_out, ctx->idx_out); struct dm_crypt_request *dmreq; u8 *iv; - int r = 0; + int r; dmreq = dmreq_of_req(cc, req); iv = iv_of_dmreq(cc, dmreq); - dmreq->iv_sector = ctx->sector; + dmreq->iv_sector = ctx->cc_sector; dmreq->ctx = ctx; sg_init_table(&dmreq->sg_in, 1); sg_set_page(&dmreq->sg_in, bv_in->bv_page, 1 << SECTOR_SHIFT, @@ -749,12 +739,12 @@ static void crypt_alloc_req(struct crypt_config *cc, struct convert_context *ctx) { struct crypt_cpu *this_cc = this_crypt_config(cc); - unsigned key_index = ctx->sector & (cc->tfms_count - 1); + unsigned key_index = ctx->cc_sector & (cc->tfms_count - 1); if (!this_cc->req) this_cc->req = mempool_alloc(cc->req_pool, GFP_NOIO); - ablkcipher_request_set_tfm(this_cc->req, this_cc->tfms[key_index]); + ablkcipher_request_set_tfm(this_cc->req, cc->tfms[key_index]); ablkcipher_request_set_callback(this_cc->req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, kcryptd_async_done, dmreq_of_req(cc, this_cc->req)); @@ -769,14 +759,14 @@ static int crypt_convert(struct crypt_config *cc, struct crypt_cpu *this_cc = this_crypt_config(cc); int r; - atomic_set(&ctx->pending, 1); + atomic_set(&ctx->cc_pending, 1); while(ctx->idx_in < ctx->bio_in->bi_vcnt && ctx->idx_out < ctx->bio_out->bi_vcnt) { crypt_alloc_req(cc, ctx); - atomic_inc(&ctx->pending); + atomic_inc(&ctx->cc_pending); r = crypt_convert_block(cc, ctx, this_cc->req); @@ -788,19 +778,19 @@ static int crypt_convert(struct crypt_config *cc, /* fall through*/ case -EINPROGRESS: this_cc->req = NULL; - ctx->sector++; + ctx->cc_sector++; continue; /* sync */ case 0: - atomic_dec(&ctx->pending); - ctx->sector++; + atomic_dec(&ctx->cc_pending); + ctx->cc_sector++; cond_resched(); continue; /* error */ default: - atomic_dec(&ctx->pending); + atomic_dec(&ctx->cc_pending); return r; } } @@ -811,7 +801,7 @@ static int crypt_convert(struct crypt_config *cc, static void dm_crypt_bio_destructor(struct bio *bio) { struct dm_crypt_io *io = bio->bi_private; - struct crypt_config *cc = io->target->private; + struct crypt_config *cc = io->cc; bio_free(bio, cc->bs); } @@ -825,7 +815,7 @@ static void dm_crypt_bio_destructor(struct bio *bio) static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size, unsigned *out_of_pages) { - struct crypt_config *cc = io->target->private; + struct crypt_config *cc = io->cc; struct bio *clone; unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; gfp_t gfp_mask = GFP_NOIO | __GFP_HIGHMEM; @@ -884,26 +874,25 @@ static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone) } } -static struct dm_crypt_io *crypt_io_alloc(struct dm_target *ti, +static struct dm_crypt_io *crypt_io_alloc(struct crypt_config *cc, struct bio *bio, sector_t sector) { - struct crypt_config *cc = ti->private; struct dm_crypt_io *io; io = mempool_alloc(cc->io_pool, GFP_NOIO); - io->target = ti; + io->cc = cc; io->base_bio = bio; io->sector = sector; io->error = 0; io->base_io = NULL; - atomic_set(&io->pending, 0); + atomic_set(&io->io_pending, 0); return io; } static void crypt_inc_pending(struct dm_crypt_io *io) { - atomic_inc(&io->pending); + atomic_inc(&io->io_pending); } /* @@ -913,12 +902,12 @@ static void crypt_inc_pending(struct dm_crypt_io *io) */ static void crypt_dec_pending(struct dm_crypt_io *io) { - struct crypt_config *cc = io->target->private; + struct crypt_config *cc = io->cc; struct bio *base_bio = io->base_bio; struct dm_crypt_io *base_io = io->base_io; int error = io->error; - if (!atomic_dec_and_test(&io->pending)) + if (!atomic_dec_and_test(&io->io_pending)) return; mempool_free(io, cc->io_pool); @@ -952,7 +941,7 @@ static void crypt_dec_pending(struct dm_crypt_io *io) static void crypt_endio(struct bio *clone, int error) { struct dm_crypt_io *io = clone->bi_private; - struct crypt_config *cc = io->target->private; + struct crypt_config *cc = io->cc; unsigned rw = bio_data_dir(clone); if (unlikely(!bio_flagged(clone, BIO_UPTODATE) && !error)) @@ -979,7 +968,7 @@ static void crypt_endio(struct bio *clone, int error) static void clone_init(struct dm_crypt_io *io, struct bio *clone) { - struct crypt_config *cc = io->target->private; + struct crypt_config *cc = io->cc; clone->bi_private = io; clone->bi_end_io = crypt_endio; @@ -990,7 +979,7 @@ static void clone_init(struct dm_crypt_io *io, struct bio *clone) static int kcryptd_io_read(struct dm_crypt_io *io, gfp_t gfp) { - struct crypt_config *cc = io->target->private; + struct crypt_config *cc = io->cc; struct bio *base_bio = io->base_bio; struct bio *clone; @@ -1038,7 +1027,7 @@ static void kcryptd_io(struct work_struct *work) static void kcryptd_queue_io(struct dm_crypt_io *io) { - struct crypt_config *cc = io->target->private; + struct crypt_config *cc = io->cc; INIT_WORK(&io->work, kcryptd_io); queue_work(cc->io_queue, &io->work); @@ -1047,7 +1036,7 @@ static void kcryptd_queue_io(struct dm_crypt_io *io) static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async) { struct bio *clone = io->ctx.bio_out; - struct crypt_config *cc = io->target->private; + struct crypt_config *cc = io->cc; if (unlikely(io->error < 0)) { crypt_free_buffer_pages(cc, clone); @@ -1069,7 +1058,7 @@ static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async) static void kcryptd_crypt_write_convert(struct dm_crypt_io *io) { - struct crypt_config *cc = io->target->private; + struct crypt_config *cc = io->cc; struct bio *clone; struct dm_crypt_io *new_io; int crypt_finished; @@ -1107,7 +1096,7 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io) if (r < 0) io->error = -EIO; - crypt_finished = atomic_dec_and_test(&io->ctx.pending); + crypt_finished = atomic_dec_and_test(&io->ctx.cc_pending); /* Encryption was already finished, submit io now */ if (crypt_finished) { @@ -1135,7 +1124,7 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io) * between fragments, so switch to a new dm_crypt_io structure. */ if (unlikely(!crypt_finished && remaining)) { - new_io = crypt_io_alloc(io->target, io->base_bio, + new_io = crypt_io_alloc(io->cc, io->base_bio, sector); crypt_inc_pending(new_io); crypt_convert_init(cc, &new_io->ctx, NULL, @@ -1169,7 +1158,7 @@ static void kcryptd_crypt_read_done(struct dm_crypt_io *io) static void kcryptd_crypt_read_convert(struct dm_crypt_io *io) { - struct crypt_config *cc = io->target->private; + struct crypt_config *cc = io->cc; int r = 0; crypt_inc_pending(io); @@ -1181,7 +1170,7 @@ static void kcryptd_crypt_read_convert(struct dm_crypt_io *io) if (r < 0) io->error = -EIO; - if (atomic_dec_and_test(&io->ctx.pending)) + if (atomic_dec_and_test(&io->ctx.cc_pending)) kcryptd_crypt_read_done(io); crypt_dec_pending(io); @@ -1193,7 +1182,7 @@ static void kcryptd_async_done(struct crypto_async_request *async_req, struct dm_crypt_request *dmreq = async_req->data; struct convert_context *ctx = dmreq->ctx; struct dm_crypt_io *io = container_of(ctx, struct dm_crypt_io, ctx); - struct crypt_config *cc = io->target->private; + struct crypt_config *cc = io->cc; if (error == -EINPROGRESS) { complete(&ctx->restart); @@ -1208,7 +1197,7 @@ static void kcryptd_async_done(struct crypto_async_request *async_req, mempool_free(req_of_dmreq(cc, dmreq), cc->req_pool); - if (!atomic_dec_and_test(&ctx->pending)) + if (!atomic_dec_and_test(&ctx->cc_pending)) return; if (bio_data_dir(io->base_bio) == READ) @@ -1229,7 +1218,7 @@ static void kcryptd_crypt(struct work_struct *work) static void kcryptd_queue_crypt(struct dm_crypt_io *io) { - struct crypt_config *cc = io->target->private; + struct crypt_config *cc = io->cc; INIT_WORK(&io->work, kcryptd_crypt); queue_work(cc->crypt_queue, &io->work); @@ -1241,7 +1230,6 @@ static void kcryptd_queue_crypt(struct dm_crypt_io *io) static int crypt_decode_key(u8 *key, char *hex, unsigned int size) { char buffer[3]; - char *endp; unsigned int i; buffer[2] = '\0'; @@ -1250,9 +1238,7 @@ static int crypt_decode_key(u8 *key, char *hex, unsigned int size) buffer[0] = *hex++; buffer[1] = *hex++; - key[i] = (u8)simple_strtoul(buffer, &endp, 16); - - if (endp != &buffer[2]) + if (kstrtou8(buffer, 16, &key[i])) return -EINVAL; } @@ -1276,29 +1262,38 @@ static void crypt_encode_key(char *hex, u8 *key, unsigned int size) } } -static void crypt_free_tfms(struct crypt_config *cc, int cpu) +static void crypt_free_tfms(struct crypt_config *cc) { - struct crypt_cpu *cpu_cc = per_cpu_ptr(cc->cpu, cpu); unsigned i; + if (!cc->tfms) + return; + for (i = 0; i < cc->tfms_count; i++) - if (cpu_cc->tfms[i] && !IS_ERR(cpu_cc->tfms[i])) { - crypto_free_ablkcipher(cpu_cc->tfms[i]); - cpu_cc->tfms[i] = NULL; + if (cc->tfms[i] && !IS_ERR(cc->tfms[i])) { + crypto_free_ablkcipher(cc->tfms[i]); + cc->tfms[i] = NULL; } + + kfree(cc->tfms); + cc->tfms = NULL; } -static int crypt_alloc_tfms(struct crypt_config *cc, int cpu, char *ciphermode) +static int crypt_alloc_tfms(struct crypt_config *cc, char *ciphermode) { - struct crypt_cpu *cpu_cc = per_cpu_ptr(cc->cpu, cpu); unsigned i; int err; + cc->tfms = kmalloc(cc->tfms_count * sizeof(struct crypto_ablkcipher *), + GFP_KERNEL); + if (!cc->tfms) + return -ENOMEM; + for (i = 0; i < cc->tfms_count; i++) { - cpu_cc->tfms[i] = crypto_alloc_ablkcipher(ciphermode, 0, 0); - if (IS_ERR(cpu_cc->tfms[i])) { - err = PTR_ERR(cpu_cc->tfms[i]); - crypt_free_tfms(cc, cpu); + cc->tfms[i] = crypto_alloc_ablkcipher(ciphermode, 0, 0); + if (IS_ERR(cc->tfms[i])) { + err = PTR_ERR(cc->tfms[i]); + crypt_free_tfms(cc); return err; } } @@ -1309,15 +1304,14 @@ static int crypt_alloc_tfms(struct crypt_config *cc, int cpu, char *ciphermode) static int crypt_setkey_allcpus(struct crypt_config *cc) { unsigned subkey_size = cc->key_size >> ilog2(cc->tfms_count); - int cpu, err = 0, i, r; - - for_each_possible_cpu(cpu) { - for (i = 0; i < cc->tfms_count; i++) { - r = crypto_ablkcipher_setkey(per_cpu_ptr(cc->cpu, cpu)->tfms[i], - cc->key + (i * subkey_size), subkey_size); - if (r) - err = r; - } + int err = 0, i, r; + + for (i = 0; i < cc->tfms_count; i++) { + r = crypto_ablkcipher_setkey(cc->tfms[i], + cc->key + (i * subkey_size), + subkey_size); + if (r) + err = r; } return err; @@ -1379,9 +1373,10 @@ static void crypt_dtr(struct dm_target *ti) cpu_cc = per_cpu_ptr(cc->cpu, cpu); if (cpu_cc->req) mempool_free(cpu_cc->req, cc->req_pool); - crypt_free_tfms(cc, cpu); } + crypt_free_tfms(cc); + if (cc->bs) bioset_free(cc->bs); @@ -1414,7 +1409,7 @@ static int crypt_ctr_cipher(struct dm_target *ti, struct crypt_config *cc = ti->private; char *tmp, *cipher, *chainmode, *ivmode, *ivopts, *keycount; char *cipher_api = NULL; - int cpu, ret = -EINVAL; + int ret = -EINVAL; char dummy; /* Convert to crypto api definition? */ @@ -1455,8 +1450,7 @@ static int crypt_ctr_cipher(struct dm_target *ti, if (tmp) DMWARN("Ignoring unexpected additional cipher options"); - cc->cpu = __alloc_percpu(sizeof(*(cc->cpu)) + - cc->tfms_count * sizeof(*(cc->cpu->tfms)), + cc->cpu = __alloc_percpu(sizeof(*(cc->cpu)), __alignof__(struct crypt_cpu)); if (!cc->cpu) { ti->error = "Cannot allocate per cpu state"; @@ -1489,12 +1483,10 @@ static int crypt_ctr_cipher(struct dm_target *ti, } /* Allocate cipher */ - for_each_possible_cpu(cpu) { - ret = crypt_alloc_tfms(cc, cpu, cipher_api); - if (ret < 0) { - ti->error = "Error allocating crypto tfm"; - goto bad; - } + ret = crypt_alloc_tfms(cc, cipher_api); + if (ret < 0) { + ti->error = "Error allocating crypto tfm"; + goto bad; } /* Initialize and set key */ @@ -1702,7 +1694,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) } ti->num_flush_requests = 1; - ti->discard_zeroes_data_unsupported = 1; + ti->discard_zeroes_data_unsupported = true; return 0; @@ -1715,7 +1707,7 @@ static int crypt_map(struct dm_target *ti, struct bio *bio, union map_info *map_context) { struct dm_crypt_io *io; - struct crypt_config *cc; + struct crypt_config *cc = ti->private; /* * If bio is REQ_FLUSH or REQ_DISCARD, just bypass crypt queues. @@ -1723,14 +1715,13 @@ static int crypt_map(struct dm_target *ti, struct bio *bio, * - for REQ_DISCARD caller must use flush if IO ordering matters */ if (unlikely(bio->bi_rw & (REQ_FLUSH | REQ_DISCARD))) { - cc = ti->private; bio->bi_bdev = cc->dev->bdev; if (bio_sectors(bio)) bio->bi_sector = cc->start + dm_target_offset(ti, bio->bi_sector); return DM_MAPIO_REMAPPED; } - io = crypt_io_alloc(ti, bio, dm_target_offset(ti, bio->bi_sector)); + io = crypt_io_alloc(cc, bio, dm_target_offset(ti, bio->bi_sector)); if (bio_data_dir(io->base_bio) == READ) { if (kcryptd_io_read(io, GFP_NOWAIT)) @@ -1742,7 +1733,7 @@ static int crypt_map(struct dm_target *ti, struct bio *bio, } static int crypt_status(struct dm_target *ti, status_type_t type, - char *result, unsigned int maxlen) + unsigned status_flags, char *result, unsigned maxlen) { struct crypt_config *cc = ti->private; unsigned int sz = 0; diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c index 2dc22dddb2ae..f53846f9ab50 100644 --- a/drivers/md/dm-delay.c +++ b/drivers/md/dm-delay.c @@ -295,7 +295,7 @@ static int delay_map(struct dm_target *ti, struct bio *bio, } static int delay_status(struct dm_target *ti, status_type_t type, - char *result, unsigned maxlen) + unsigned status_flags, char *result, unsigned maxlen) { struct delay_c *dc = ti->private; int sz = 0; diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c index aa70f7d43a1a..ebaa4f803eec 100644 --- a/drivers/md/dm-exception-store.c +++ b/drivers/md/dm-exception-store.c @@ -142,24 +142,19 @@ EXPORT_SYMBOL(dm_exception_store_type_unregister); static int set_chunk_size(struct dm_exception_store *store, const char *chunk_size_arg, char **error) { - unsigned long chunk_size_ulong; - char *value; + unsigned chunk_size; - chunk_size_ulong = simple_strtoul(chunk_size_arg, &value, 10); - if (*chunk_size_arg == '\0' || *value != '\0' || - chunk_size_ulong > UINT_MAX) { + if (kstrtouint(chunk_size_arg, 10, &chunk_size)) { *error = "Invalid chunk size"; return -EINVAL; } - if (!chunk_size_ulong) { + if (!chunk_size) { store->chunk_size = store->chunk_mask = store->chunk_shift = 0; return 0; } - return dm_exception_store_set_chunk_size(store, - (unsigned) chunk_size_ulong, - error); + return dm_exception_store_set_chunk_size(store, chunk_size, error); } int dm_exception_store_set_chunk_size(struct dm_exception_store *store, diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c index ac49c01f1a44..cc15543a6ad7 100644 --- a/drivers/md/dm-flakey.c +++ b/drivers/md/dm-flakey.c @@ -333,7 +333,7 @@ static int flakey_end_io(struct dm_target *ti, struct bio *bio, } static int flakey_status(struct dm_target *ti, status_type_t type, - char *result, unsigned int maxlen) + unsigned status_flags, char *result, unsigned maxlen) { unsigned sz = 0; struct flakey_c *fc = ti->private; diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index a1a3e6df17b8..afd95986d099 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1054,6 +1054,7 @@ static void retrieve_status(struct dm_table *table, char *outbuf, *outptr; status_type_t type; size_t remaining, len, used = 0; + unsigned status_flags = 0; outptr = outbuf = get_result_buffer(param, param_size, &len); @@ -1090,7 +1091,9 @@ static void retrieve_status(struct dm_table *table, /* Get the status/table string from the target driver */ if (ti->type->status) { - if (ti->type->status(ti, type, outptr, remaining)) { + if (param->flags & DM_NOFLUSH_FLAG) + status_flags |= DM_STATUS_NOFLUSH_FLAG; + if (ti->type->status(ti, type, status_flags, outptr, remaining)) { param->flags |= DM_BUFFER_FULL_FLAG; break; } diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 3639eeab6042..1bf19a93eef0 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -96,7 +96,7 @@ static int linear_map(struct dm_target *ti, struct bio *bio, } static int linear_status(struct dm_target *ti, status_type_t type, - char *result, unsigned int maxlen) + unsigned status_flags, char *result, unsigned maxlen) { struct linear_c *lc = (struct linear_c *) ti->private; diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 65ebaebf502b..627d19186d5a 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -571,16 +571,6 @@ static void disk_dtr(struct dm_dirty_log *log) destroy_log_context(lc); } -static int count_bits32(uint32_t *addr, unsigned size) -{ - int count = 0, i; - - for (i = 0; i < size; i++) { - count += hweight32(*(addr+i)); - } - return count; -} - static void fail_log_device(struct log_c *lc) { if (lc->log_dev_failed) @@ -629,7 +619,8 @@ static int disk_resume(struct dm_dirty_log *log) /* copy clean across to sync */ memcpy(lc->sync_bits, lc->clean_bits, size); - lc->sync_count = count_bits32(lc->clean_bits, lc->bitset_uint32_count); + lc->sync_count = memweight(lc->clean_bits, + lc->bitset_uint32_count * sizeof(uint32_t)); lc->sync_search = 0; /* set the correct number of regions in the header */ diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 638dae048b4f..d8abb90a6c2f 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -85,6 +85,7 @@ struct multipath { unsigned queue_io:1; /* Must we queue all I/O? */ unsigned queue_if_no_path:1; /* Queue I/O if last path fails? */ unsigned saved_queue_if_no_path:1; /* Saved state during suspension */ + unsigned retain_attached_hw_handler:1; /* If there's already a hw_handler present, don't change it. */ unsigned pg_init_retries; /* Number of times to retry pg_init */ unsigned pg_init_count; /* Number of times pg_init called */ @@ -568,6 +569,8 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps int r; struct pgpath *p; struct multipath *m = ti->private; + struct request_queue *q = NULL; + const char *attached_handler_name; /* we need at least a path arg */ if (as->argc < 1) { @@ -586,13 +589,37 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps goto bad; } - if (m->hw_handler_name) { - struct request_queue *q = bdev_get_queue(p->path.dev->bdev); + if (m->retain_attached_hw_handler || m->hw_handler_name) + q = bdev_get_queue(p->path.dev->bdev); + + if (m->retain_attached_hw_handler) { + attached_handler_name = scsi_dh_attached_handler_name(q, GFP_KERNEL); + if (attached_handler_name) { + /* + * Reset hw_handler_name to match the attached handler + * and clear any hw_handler_params associated with the + * ignored handler. + * + * NB. This modifies the table line to show the actual + * handler instead of the original table passed in. + */ + kfree(m->hw_handler_name); + m->hw_handler_name = attached_handler_name; + + kfree(m->hw_handler_params); + m->hw_handler_params = NULL; + } + } + if (m->hw_handler_name) { + /* + * Increments scsi_dh reference, even when using an + * already-attached handler. + */ r = scsi_dh_attach(q, m->hw_handler_name); if (r == -EBUSY) { /* - * Already attached to different hw_handler, + * Already attached to different hw_handler: * try to reattach with correct one. */ scsi_dh_detach(q); @@ -760,7 +787,7 @@ static int parse_features(struct dm_arg_set *as, struct multipath *m) const char *arg_name; static struct dm_arg _args[] = { - {0, 5, "invalid number of feature args"}, + {0, 6, "invalid number of feature args"}, {1, 50, "pg_init_retries must be between 1 and 50"}, {0, 60000, "pg_init_delay_msecs must be between 0 and 60000"}, }; @@ -781,6 +808,11 @@ static int parse_features(struct dm_arg_set *as, struct multipath *m) continue; } + if (!strcasecmp(arg_name, "retain_attached_hw_handler")) { + m->retain_attached_hw_handler = 1; + continue; + } + if (!strcasecmp(arg_name, "pg_init_retries") && (argc >= 1)) { r = dm_read_arg(_args + 1, as, &m->pg_init_retries, &ti->error); @@ -1346,7 +1378,7 @@ static void multipath_resume(struct dm_target *ti) * num_paths num_selector_args [path_dev [selector_args]* ]+ ]+ */ static int multipath_status(struct dm_target *ti, status_type_t type, - char *result, unsigned int maxlen) + unsigned status_flags, char *result, unsigned maxlen) { int sz = 0; unsigned long flags; @@ -1364,13 +1396,16 @@ static int multipath_status(struct dm_target *ti, status_type_t type, else { DMEMIT("%u ", m->queue_if_no_path + (m->pg_init_retries > 0) * 2 + - (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) * 2); + (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) * 2 + + m->retain_attached_hw_handler); if (m->queue_if_no_path) DMEMIT("queue_if_no_path "); if (m->pg_init_retries) DMEMIT("pg_init_retries %u ", m->pg_init_retries); if (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) DMEMIT("pg_init_delay_msecs %u ", m->pg_init_delay_msecs); + if (m->retain_attached_hw_handler) + DMEMIT("retain_attached_hw_handler "); } if (!m->hw_handler_name || type == STATUSTYPE_INFO) @@ -1656,7 +1691,7 @@ out: *---------------------------------------------------------------*/ static struct target_type multipath_target = { .name = "multipath", - .version = {1, 4, 0}, + .version = {1, 5, 0}, .module = THIS_MODULE, .ctr = multipath_ctr, .dtr = multipath_dtr, diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index 017c34d78d61..f2f29c526544 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -101,20 +101,12 @@ static struct raid_set *context_alloc(struct dm_target *ti, struct raid_type *ra { unsigned i; struct raid_set *rs; - sector_t sectors_per_dev; if (raid_devs <= raid_type->parity_devs) { ti->error = "Insufficient number of devices"; return ERR_PTR(-EINVAL); } - sectors_per_dev = ti->len; - if ((raid_type->level > 1) && - sector_div(sectors_per_dev, (raid_devs - raid_type->parity_devs))) { - ti->error = "Target length not divisible by number of data devices"; - return ERR_PTR(-EINVAL); - } - rs = kzalloc(sizeof(*rs) + raid_devs * sizeof(rs->dev[0]), GFP_KERNEL); if (!rs) { ti->error = "Cannot allocate raid context"; @@ -128,7 +120,6 @@ static struct raid_set *context_alloc(struct dm_target *ti, struct raid_type *ra rs->md.raid_disks = raid_devs; rs->md.level = raid_type->level; rs->md.new_level = rs->md.level; - rs->md.dev_sectors = sectors_per_dev; rs->md.layout = raid_type->algorithm; rs->md.new_layout = rs->md.layout; rs->md.delta_disks = 0; @@ -143,6 +134,7 @@ static struct raid_set *context_alloc(struct dm_target *ti, struct raid_type *ra * rs->md.external * rs->md.chunk_sectors * rs->md.new_chunk_sectors + * rs->md.dev_sectors */ return rs; @@ -353,6 +345,8 @@ static int parse_raid_params(struct raid_set *rs, char **argv, { unsigned i, rebuild_cnt = 0; unsigned long value, region_size = 0; + sector_t sectors_per_dev = rs->ti->len; + sector_t max_io_len; char *key; /* @@ -429,13 +423,28 @@ static int parse_raid_params(struct raid_set *rs, char **argv, if (!strcasecmp(key, "rebuild")) { rebuild_cnt++; - if (((rs->raid_type->level != 1) && - (rebuild_cnt > rs->raid_type->parity_devs)) || - ((rs->raid_type->level == 1) && - (rebuild_cnt > (rs->md.raid_disks - 1)))) { - rs->ti->error = "Too many rebuild devices specified for given RAID type"; + + switch (rs->raid_type->level) { + case 1: + if (rebuild_cnt >= rs->md.raid_disks) { + rs->ti->error = "Too many rebuild devices specified"; + return -EINVAL; + } + break; + case 4: + case 5: + case 6: + if (rebuild_cnt > rs->raid_type->parity_devs) { + rs->ti->error = "Too many rebuild devices specified for given RAID type"; + return -EINVAL; + } + break; + default: + DMERR("The rebuild parameter is not supported for %s", rs->raid_type->name); + rs->ti->error = "Rebuild not supported for this RAID type"; return -EINVAL; } + if (value > rs->md.raid_disks) { rs->ti->error = "Invalid rebuild index given"; return -EINVAL; @@ -522,14 +531,19 @@ static int parse_raid_params(struct raid_set *rs, char **argv, return -EINVAL; if (rs->md.chunk_sectors) - rs->ti->split_io = rs->md.chunk_sectors; + max_io_len = rs->md.chunk_sectors; else - rs->ti->split_io = region_size; + max_io_len = region_size; - if (rs->md.chunk_sectors) - rs->ti->split_io = rs->md.chunk_sectors; - else - rs->ti->split_io = region_size; + if (dm_set_target_max_io_len(rs->ti, max_io_len)) + return -EINVAL; + + if ((rs->raid_type->level > 1) && + sector_div(sectors_per_dev, (rs->md.raid_disks - rs->raid_type->parity_devs))) { + rs->ti->error = "Target length not divisible by number of data devices"; + return -EINVAL; + } + rs->md.dev_sectors = sectors_per_dev; /* Assume there are no metadata devices until the drives are parsed */ rs->md.persistent = 0; @@ -1067,7 +1081,7 @@ static int raid_map(struct dm_target *ti, struct bio *bio, union map_info *map_c } static int raid_status(struct dm_target *ti, status_type_t type, - char *result, unsigned maxlen) + unsigned status_flags, char *result, unsigned maxlen) { struct raid_set *rs = ti->private; unsigned raid_param_cnt = 1; /* at least 1 for chunksize */ diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index b58b7a33914a..bc5ddba8045b 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -1081,10 +1081,14 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv) } ti->private = ms; - ti->split_io = dm_rh_get_region_size(ms->rh); + + r = dm_set_target_max_io_len(ti, dm_rh_get_region_size(ms->rh)); + if (r) + goto err_free_context; + ti->num_flush_requests = 1; ti->num_discard_requests = 1; - ti->discard_zeroes_data_unsupported = 1; + ti->discard_zeroes_data_unsupported = true; ms->kmirrord_wq = alloc_workqueue("kmirrord", WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0); @@ -1363,7 +1367,7 @@ static char device_status_char(struct mirror *m) static int mirror_status(struct dm_target *ti, status_type_t type, - char *result, unsigned int maxlen) + unsigned status_flags, char *result, unsigned maxlen) { unsigned int m, sz = 0; struct mirror_set *ms = (struct mirror_set *) ti->private; diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 6f758870fc19..a143921feaf6 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -691,7 +691,7 @@ static int dm_add_exception(void *context, chunk_t old, chunk_t new) * Return a minimum chunk size of all snapshots that have the specified origin. * Return zero if the origin has no snapshots. */ -static sector_t __minimum_chunk_size(struct origin *o) +static uint32_t __minimum_chunk_size(struct origin *o) { struct dm_snapshot *snap; unsigned chunk_size = 0; @@ -701,7 +701,7 @@ static sector_t __minimum_chunk_size(struct origin *o) chunk_size = min_not_zero(chunk_size, snap->store->chunk_size); - return chunk_size; + return (uint32_t) chunk_size; } /* @@ -1172,7 +1172,10 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) ti->error = "Chunk size not set"; goto bad_read_metadata; } - ti->split_io = s->store->chunk_size; + + r = dm_set_target_max_io_len(ti, s->store->chunk_size); + if (r) + goto bad_read_metadata; return 0; @@ -1239,7 +1242,7 @@ static void __handover_exceptions(struct dm_snapshot *snap_src, snap_dest->store->snap = snap_dest; snap_src->store->snap = snap_src; - snap_dest->ti->split_io = snap_dest->store->chunk_size; + snap_dest->ti->max_io_len = snap_dest->store->chunk_size; snap_dest->valid = snap_src->valid; /* @@ -1817,9 +1820,9 @@ static void snapshot_resume(struct dm_target *ti) up_write(&s->lock); } -static sector_t get_origin_minimum_chunksize(struct block_device *bdev) +static uint32_t get_origin_minimum_chunksize(struct block_device *bdev) { - sector_t min_chunksize; + uint32_t min_chunksize; down_read(&_origins_lock); min_chunksize = __minimum_chunk_size(__lookup_origin(bdev)); @@ -1838,15 +1841,15 @@ static void snapshot_merge_resume(struct dm_target *ti) snapshot_resume(ti); /* - * snapshot-merge acts as an origin, so set ti->split_io + * snapshot-merge acts as an origin, so set ti->max_io_len */ - ti->split_io = get_origin_minimum_chunksize(s->origin->bdev); + ti->max_io_len = get_origin_minimum_chunksize(s->origin->bdev); start_merge(s); } static int snapshot_status(struct dm_target *ti, status_type_t type, - char *result, unsigned int maxlen) + unsigned status_flags, char *result, unsigned maxlen) { unsigned sz = 0; struct dm_snapshot *snap = ti->private; @@ -2073,12 +2076,12 @@ static int origin_write_extent(struct dm_snapshot *merging_snap, struct origin *o; /* - * The origin's __minimum_chunk_size() got stored in split_io + * The origin's __minimum_chunk_size() got stored in max_io_len * by snapshot_merge_resume(). */ down_read(&_origins_lock); o = __lookup_origin(merging_snap->origin->bdev); - for (n = 0; n < size; n += merging_snap->ti->split_io) + for (n = 0; n < size; n += merging_snap->ti->max_io_len) if (__origin_write(&o->snapshots, sector + n, NULL) == DM_MAPIO_SUBMITTED) must_wait = 1; @@ -2138,18 +2141,18 @@ static int origin_map(struct dm_target *ti, struct bio *bio, } /* - * Set the target "split_io" field to the minimum of all the snapshots' + * Set the target "max_io_len" field to the minimum of all the snapshots' * chunk sizes. */ static void origin_resume(struct dm_target *ti) { struct dm_dev *dev = ti->private; - ti->split_io = get_origin_minimum_chunksize(dev->bdev); + ti->max_io_len = get_origin_minimum_chunksize(dev->bdev); } -static int origin_status(struct dm_target *ti, status_type_t type, char *result, - unsigned int maxlen) +static int origin_status(struct dm_target *ti, status_type_t type, + unsigned status_flags, char *result, unsigned maxlen) { struct dm_dev *dev = ti->private; @@ -2176,7 +2179,6 @@ static int origin_merge(struct dm_target *ti, struct bvec_merge_data *bvm, return max_size; bvm->bi_bdev = dev->bdev; - bvm->bi_sector = bvm->bi_sector; return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); } diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index 35c94ff24ad5..a087bf2a8d66 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -26,14 +26,12 @@ struct stripe { struct stripe_c { uint32_t stripes; int stripes_shift; - sector_t stripes_mask; /* The size of this target / num. stripes */ sector_t stripe_width; - /* stripe chunk size */ - uint32_t chunk_shift; - sector_t chunk_mask; + uint32_t chunk_size; + int chunk_size_shift; /* Needed for handling events */ struct dm_target *ti; @@ -91,7 +89,7 @@ static int get_stripe(struct dm_target *ti, struct stripe_c *sc, /* * Construct a striped mapping. - * <number of stripes> <chunk size (2^^n)> [<dev_path> <offset>]+ + * <number of stripes> <chunk size> [<dev_path> <offset>]+ */ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv) { @@ -99,7 +97,6 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv) sector_t width; uint32_t stripes; uint32_t chunk_size; - char *end; int r; unsigned int i; @@ -108,34 +105,23 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv) return -EINVAL; } - stripes = simple_strtoul(argv[0], &end, 10); - if (!stripes || *end) { + if (kstrtouint(argv[0], 10, &stripes) || !stripes) { ti->error = "Invalid stripe count"; return -EINVAL; } - chunk_size = simple_strtoul(argv[1], &end, 10); - if (*end) { + if (kstrtouint(argv[1], 10, &chunk_size) || !chunk_size) { ti->error = "Invalid chunk_size"; return -EINVAL; } - /* - * chunk_size is a power of two - */ - if (!is_power_of_2(chunk_size) || - (chunk_size < (PAGE_SIZE >> SECTOR_SHIFT))) { - ti->error = "Invalid chunk size"; - return -EINVAL; - } - - if (ti->len & (chunk_size - 1)) { + width = ti->len; + if (sector_div(width, chunk_size)) { ti->error = "Target length not divisible by " "chunk size"; return -EINVAL; } - width = ti->len; if (sector_div(width, stripes)) { ti->error = "Target length not divisible by " "number of stripes"; @@ -167,17 +153,21 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv) if (stripes & (stripes - 1)) sc->stripes_shift = -1; - else { - sc->stripes_shift = ffs(stripes) - 1; - sc->stripes_mask = ((sector_t) stripes) - 1; - } + else + sc->stripes_shift = __ffs(stripes); + + r = dm_set_target_max_io_len(ti, chunk_size); + if (r) + return r; - ti->split_io = chunk_size; ti->num_flush_requests = stripes; ti->num_discard_requests = stripes; - sc->chunk_shift = ffs(chunk_size) - 1; - sc->chunk_mask = ((sector_t) chunk_size) - 1; + sc->chunk_size = chunk_size; + if (chunk_size & (chunk_size - 1)) + sc->chunk_size_shift = -1; + else + sc->chunk_size_shift = __ffs(chunk_size); /* * Get the stripe destinations. @@ -216,17 +206,29 @@ static void stripe_dtr(struct dm_target *ti) static void stripe_map_sector(struct stripe_c *sc, sector_t sector, uint32_t *stripe, sector_t *result) { - sector_t offset = dm_target_offset(sc->ti, sector); - sector_t chunk = offset >> sc->chunk_shift; + sector_t chunk = dm_target_offset(sc->ti, sector); + sector_t chunk_offset; + + if (sc->chunk_size_shift < 0) + chunk_offset = sector_div(chunk, sc->chunk_size); + else { + chunk_offset = chunk & (sc->chunk_size - 1); + chunk >>= sc->chunk_size_shift; + } if (sc->stripes_shift < 0) *stripe = sector_div(chunk, sc->stripes); else { - *stripe = chunk & sc->stripes_mask; + *stripe = chunk & (sc->stripes - 1); chunk >>= sc->stripes_shift; } - *result = (chunk << sc->chunk_shift) | (offset & sc->chunk_mask); + if (sc->chunk_size_shift < 0) + chunk *= sc->chunk_size; + else + chunk <<= sc->chunk_size_shift; + + *result = chunk + chunk_offset; } static void stripe_map_range_sector(struct stripe_c *sc, sector_t sector, @@ -237,9 +239,16 @@ static void stripe_map_range_sector(struct stripe_c *sc, sector_t sector, stripe_map_sector(sc, sector, &stripe, result); if (stripe == target_stripe) return; - *result &= ~sc->chunk_mask; /* round down */ + + /* round down */ + sector = *result; + if (sc->chunk_size_shift < 0) + *result -= sector_div(sector, sc->chunk_size); + else + *result = sector & ~(sector_t)(sc->chunk_size - 1); + if (target_stripe < stripe) - *result += sc->chunk_mask + 1; /* next chunk */ + *result += sc->chunk_size; /* next chunk */ } static int stripe_map_discard(struct stripe_c *sc, struct bio *bio, @@ -302,8 +311,8 @@ static int stripe_map(struct dm_target *ti, struct bio *bio, * */ -static int stripe_status(struct dm_target *ti, - status_type_t type, char *result, unsigned int maxlen) +static int stripe_status(struct dm_target *ti, status_type_t type, + unsigned status_flags, char *result, unsigned maxlen) { struct stripe_c *sc = (struct stripe_c *) ti->private; char buffer[sc->stripes + 1]; @@ -324,7 +333,7 @@ static int stripe_status(struct dm_target *ti, case STATUSTYPE_TABLE: DMEMIT("%d %llu", sc->stripes, - (unsigned long long)sc->chunk_mask + 1); + (unsigned long long)sc->chunk_size); for (i = 0; i < sc->stripes; i++) DMEMIT(" %s %llu", sc->stripe[i].dev->name, (unsigned long long)sc->stripe[i].physical_start); @@ -391,7 +400,7 @@ static void stripe_io_hints(struct dm_target *ti, struct queue_limits *limits) { struct stripe_c *sc = ti->private; - unsigned chunk_size = (sc->chunk_mask + 1) << 9; + unsigned chunk_size = sc->chunk_size << SECTOR_SHIFT; blk_limits_io_min(limits, chunk_size); blk_limits_io_opt(limits, chunk_size * sc->stripes); @@ -419,7 +428,7 @@ static int stripe_merge(struct dm_target *ti, struct bvec_merge_data *bvm, static struct target_type stripe_target = { .name = "striped", - .version = {1, 4, 0}, + .version = {1, 5, 0}, .module = THIS_MODULE, .ctr = stripe_ctr, .dtr = stripe_dtr, diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 2e227fbf1622..f90069029aae 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -1319,6 +1319,9 @@ static bool dm_table_supports_flush(struct dm_table *t, unsigned flush) if (!ti->num_flush_requests) continue; + if (ti->flush_supported) + return 1; + if (ti->type->iterate_devices && ti->type->iterate_devices(ti, device_flush_capable, &flush)) return 1; diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 3e2907f0bc46..693e149e9727 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2011-2012 Red Hat, Inc. * * This file is released under the GPL. */ @@ -80,6 +80,12 @@ #define THIN_METADATA_CACHE_SIZE 64 #define SECTOR_TO_BLOCK_SHIFT 3 +/* + * 3 for btree insert + + * 2 for btree lookup used within space map + */ +#define THIN_MAX_CONCURRENT_LOCKS 5 + /* This should be plenty */ #define SPACE_MAP_ROOT_SIZE 128 @@ -172,13 +178,20 @@ struct dm_pool_metadata { struct rw_semaphore root_lock; uint32_t time; - int need_commit; dm_block_t root; dm_block_t details_root; struct list_head thin_devices; uint64_t trans_id; unsigned long flags; sector_t data_block_size; + bool read_only:1; + + /* + * Set if a transaction has to be aborted but the attempt to roll back + * to the previous (good) transaction failed. The only pool metadata + * operation possible in this state is the closing of the device. + */ + bool fail_io:1; }; struct dm_thin_device { @@ -187,7 +200,8 @@ struct dm_thin_device { dm_thin_id id; int open_count; - int changed; + bool changed:1; + bool aborted_with_changes:1; uint64_t mapped_blocks; uint64_t transaction_id; uint32_t creation_time; @@ -338,7 +352,21 @@ static int subtree_equal(void *context, void *value1_le, void *value2_le) /*----------------------------------------------------------------*/ -static int superblock_all_zeroes(struct dm_block_manager *bm, int *result) +static int superblock_lock_zero(struct dm_pool_metadata *pmd, + struct dm_block **sblock) +{ + return dm_bm_write_lock_zero(pmd->bm, THIN_SUPERBLOCK_LOCATION, + &sb_validator, sblock); +} + +static int superblock_lock(struct dm_pool_metadata *pmd, + struct dm_block **sblock) +{ + return dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION, + &sb_validator, sblock); +} + +static int __superblock_all_zeroes(struct dm_block_manager *bm, int *result) { int r; unsigned i; @@ -365,72 +393,9 @@ static int superblock_all_zeroes(struct dm_block_manager *bm, int *result) return dm_bm_unlock(b); } -static int init_pmd(struct dm_pool_metadata *pmd, - struct dm_block_manager *bm, - dm_block_t nr_blocks, int create) +static void __setup_btree_details(struct dm_pool_metadata *pmd) { - int r; - struct dm_space_map *sm, *data_sm; - struct dm_transaction_manager *tm; - struct dm_block *sblock; - - if (create) { - r = dm_tm_create_with_sm(bm, THIN_SUPERBLOCK_LOCATION, - &sb_validator, &tm, &sm, &sblock); - if (r < 0) { - DMERR("tm_create_with_sm failed"); - return r; - } - - data_sm = dm_sm_disk_create(tm, nr_blocks); - if (IS_ERR(data_sm)) { - DMERR("sm_disk_create failed"); - dm_tm_unlock(tm, sblock); - r = PTR_ERR(data_sm); - goto bad; - } - } else { - struct thin_disk_superblock *disk_super = NULL; - size_t space_map_root_offset = - offsetof(struct thin_disk_superblock, metadata_space_map_root); - - r = dm_tm_open_with_sm(bm, THIN_SUPERBLOCK_LOCATION, - &sb_validator, space_map_root_offset, - SPACE_MAP_ROOT_SIZE, &tm, &sm, &sblock); - if (r < 0) { - DMERR("tm_open_with_sm failed"); - return r; - } - - disk_super = dm_block_data(sblock); - data_sm = dm_sm_disk_open(tm, disk_super->data_space_map_root, - sizeof(disk_super->data_space_map_root)); - if (IS_ERR(data_sm)) { - DMERR("sm_disk_open failed"); - r = PTR_ERR(data_sm); - goto bad; - } - } - - - r = dm_tm_unlock(tm, sblock); - if (r < 0) { - DMERR("couldn't unlock superblock"); - goto bad_data_sm; - } - - pmd->bm = bm; - pmd->metadata_sm = sm; - pmd->data_sm = data_sm; - pmd->tm = tm; - pmd->nb_tm = dm_tm_create_non_blocking_clone(tm); - if (!pmd->nb_tm) { - DMERR("could not create clone tm"); - r = -ENOMEM; - goto bad_data_sm; - } - - pmd->info.tm = tm; + pmd->info.tm = pmd->tm; pmd->info.levels = 2; pmd->info.value_type.context = pmd->data_sm; pmd->info.value_type.size = sizeof(__le64); @@ -441,7 +406,7 @@ static int init_pmd(struct dm_pool_metadata *pmd, memcpy(&pmd->nb_info, &pmd->info, sizeof(pmd->nb_info)); pmd->nb_info.tm = pmd->nb_tm; - pmd->tl_info.tm = tm; + pmd->tl_info.tm = pmd->tm; pmd->tl_info.levels = 1; pmd->tl_info.value_type.context = &pmd->info; pmd->tl_info.value_type.size = sizeof(__le64); @@ -449,7 +414,7 @@ static int init_pmd(struct dm_pool_metadata *pmd, pmd->tl_info.value_type.dec = subtree_dec; pmd->tl_info.value_type.equal = subtree_equal; - pmd->bl_info.tm = tm; + pmd->bl_info.tm = pmd->tm; pmd->bl_info.levels = 1; pmd->bl_info.value_type.context = pmd->data_sm; pmd->bl_info.value_type.size = sizeof(__le64); @@ -457,48 +422,266 @@ static int init_pmd(struct dm_pool_metadata *pmd, pmd->bl_info.value_type.dec = data_block_dec; pmd->bl_info.value_type.equal = data_block_equal; - pmd->details_info.tm = tm; + pmd->details_info.tm = pmd->tm; pmd->details_info.levels = 1; pmd->details_info.value_type.context = NULL; pmd->details_info.value_type.size = sizeof(struct disk_device_details); pmd->details_info.value_type.inc = NULL; pmd->details_info.value_type.dec = NULL; pmd->details_info.value_type.equal = NULL; +} - pmd->root = 0; +static int __write_initial_superblock(struct dm_pool_metadata *pmd) +{ + int r; + struct dm_block *sblock; + size_t metadata_len, data_len; + struct thin_disk_superblock *disk_super; + sector_t bdev_size = i_size_read(pmd->bdev->bd_inode) >> SECTOR_SHIFT; - init_rwsem(&pmd->root_lock); - pmd->time = 0; - pmd->need_commit = 0; - pmd->details_root = 0; - pmd->trans_id = 0; - pmd->flags = 0; - INIT_LIST_HEAD(&pmd->thin_devices); + if (bdev_size > THIN_METADATA_MAX_SECTORS) + bdev_size = THIN_METADATA_MAX_SECTORS; + + r = dm_sm_root_size(pmd->metadata_sm, &metadata_len); + if (r < 0) + return r; + + r = dm_sm_root_size(pmd->data_sm, &data_len); + if (r < 0) + return r; + + r = dm_sm_commit(pmd->data_sm); + if (r < 0) + return r; + + r = dm_tm_pre_commit(pmd->tm); + if (r < 0) + return r; + + r = superblock_lock_zero(pmd, &sblock); + if (r) + return r; + + disk_super = dm_block_data(sblock); + disk_super->flags = 0; + memset(disk_super->uuid, 0, sizeof(disk_super->uuid)); + disk_super->magic = cpu_to_le64(THIN_SUPERBLOCK_MAGIC); + disk_super->version = cpu_to_le32(THIN_VERSION); + disk_super->time = 0; + disk_super->trans_id = 0; + disk_super->held_root = 0; + + r = dm_sm_copy_root(pmd->metadata_sm, &disk_super->metadata_space_map_root, + metadata_len); + if (r < 0) + goto bad_locked; + + r = dm_sm_copy_root(pmd->data_sm, &disk_super->data_space_map_root, + data_len); + if (r < 0) + goto bad_locked; + + disk_super->data_mapping_root = cpu_to_le64(pmd->root); + disk_super->device_details_root = cpu_to_le64(pmd->details_root); + disk_super->metadata_block_size = cpu_to_le32(THIN_METADATA_BLOCK_SIZE >> SECTOR_SHIFT); + disk_super->metadata_nr_blocks = cpu_to_le64(bdev_size >> SECTOR_TO_BLOCK_SHIFT); + disk_super->data_block_size = cpu_to_le32(pmd->data_block_size); + + return dm_tm_commit(pmd->tm, sblock); + +bad_locked: + dm_bm_unlock(sblock); + return r; +} + +static int __format_metadata(struct dm_pool_metadata *pmd) +{ + int r; + + r = dm_tm_create_with_sm(pmd->bm, THIN_SUPERBLOCK_LOCATION, + &pmd->tm, &pmd->metadata_sm); + if (r < 0) { + DMERR("tm_create_with_sm failed"); + return r; + } + + pmd->data_sm = dm_sm_disk_create(pmd->tm, 0); + if (IS_ERR(pmd->data_sm)) { + DMERR("sm_disk_create failed"); + r = PTR_ERR(pmd->data_sm); + goto bad_cleanup_tm; + } + + pmd->nb_tm = dm_tm_create_non_blocking_clone(pmd->tm); + if (!pmd->nb_tm) { + DMERR("could not create non-blocking clone tm"); + r = -ENOMEM; + goto bad_cleanup_data_sm; + } + + __setup_btree_details(pmd); + + r = dm_btree_empty(&pmd->info, &pmd->root); + if (r < 0) + goto bad_cleanup_nb_tm; + + r = dm_btree_empty(&pmd->details_info, &pmd->details_root); + if (r < 0) { + DMERR("couldn't create devices root"); + goto bad_cleanup_nb_tm; + } + + r = __write_initial_superblock(pmd); + if (r) + goto bad_cleanup_nb_tm; return 0; -bad_data_sm: - dm_sm_destroy(data_sm); -bad: - dm_tm_destroy(tm); - dm_sm_destroy(sm); +bad_cleanup_nb_tm: + dm_tm_destroy(pmd->nb_tm); +bad_cleanup_data_sm: + dm_sm_destroy(pmd->data_sm); +bad_cleanup_tm: + dm_tm_destroy(pmd->tm); + dm_sm_destroy(pmd->metadata_sm); + + return r; +} + +static int __check_incompat_features(struct thin_disk_superblock *disk_super, + struct dm_pool_metadata *pmd) +{ + uint32_t features; + + features = le32_to_cpu(disk_super->incompat_flags) & ~THIN_FEATURE_INCOMPAT_SUPP; + if (features) { + DMERR("could not access metadata due to unsupported optional features (%lx).", + (unsigned long)features); + return -EINVAL; + } + + /* + * Check for read-only metadata to skip the following RDWR checks. + */ + if (get_disk_ro(pmd->bdev->bd_disk)) + return 0; + + features = le32_to_cpu(disk_super->compat_ro_flags) & ~THIN_FEATURE_COMPAT_RO_SUPP; + if (features) { + DMERR("could not access metadata RDWR due to unsupported optional features (%lx).", + (unsigned long)features); + return -EINVAL; + } + + return 0; +} + +static int __open_metadata(struct dm_pool_metadata *pmd) +{ + int r; + struct dm_block *sblock; + struct thin_disk_superblock *disk_super; + + r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION, + &sb_validator, &sblock); + if (r < 0) { + DMERR("couldn't read superblock"); + return r; + } + + disk_super = dm_block_data(sblock); + + r = __check_incompat_features(disk_super, pmd); + if (r < 0) + goto bad_unlock_sblock; + + r = dm_tm_open_with_sm(pmd->bm, THIN_SUPERBLOCK_LOCATION, + disk_super->metadata_space_map_root, + sizeof(disk_super->metadata_space_map_root), + &pmd->tm, &pmd->metadata_sm); + if (r < 0) { + DMERR("tm_open_with_sm failed"); + goto bad_unlock_sblock; + } + + pmd->data_sm = dm_sm_disk_open(pmd->tm, disk_super->data_space_map_root, + sizeof(disk_super->data_space_map_root)); + if (IS_ERR(pmd->data_sm)) { + DMERR("sm_disk_open failed"); + r = PTR_ERR(pmd->data_sm); + goto bad_cleanup_tm; + } + + pmd->nb_tm = dm_tm_create_non_blocking_clone(pmd->tm); + if (!pmd->nb_tm) { + DMERR("could not create non-blocking clone tm"); + r = -ENOMEM; + goto bad_cleanup_data_sm; + } + + __setup_btree_details(pmd); + return dm_bm_unlock(sblock); + +bad_cleanup_data_sm: + dm_sm_destroy(pmd->data_sm); +bad_cleanup_tm: + dm_tm_destroy(pmd->tm); + dm_sm_destroy(pmd->metadata_sm); +bad_unlock_sblock: + dm_bm_unlock(sblock); + + return r; +} + +static int __open_or_format_metadata(struct dm_pool_metadata *pmd, bool format_device) +{ + int r, unformatted; + + r = __superblock_all_zeroes(pmd->bm, &unformatted); + if (r) + return r; + + if (unformatted) + return format_device ? __format_metadata(pmd) : -EPERM; + + return __open_metadata(pmd); +} + +static int __create_persistent_data_objects(struct dm_pool_metadata *pmd, bool format_device) +{ + int r; + + pmd->bm = dm_block_manager_create(pmd->bdev, THIN_METADATA_BLOCK_SIZE, + THIN_METADATA_CACHE_SIZE, + THIN_MAX_CONCURRENT_LOCKS); + if (IS_ERR(pmd->bm)) { + DMERR("could not create block manager"); + return PTR_ERR(pmd->bm); + } + + r = __open_or_format_metadata(pmd, format_device); + if (r) + dm_block_manager_destroy(pmd->bm); return r; } +static void __destroy_persistent_data_objects(struct dm_pool_metadata *pmd) +{ + dm_sm_destroy(pmd->data_sm); + dm_sm_destroy(pmd->metadata_sm); + dm_tm_destroy(pmd->nb_tm); + dm_tm_destroy(pmd->tm); + dm_block_manager_destroy(pmd->bm); +} + static int __begin_transaction(struct dm_pool_metadata *pmd) { int r; - u32 features; struct thin_disk_superblock *disk_super; struct dm_block *sblock; /* - * __maybe_commit_transaction() resets these - */ - WARN_ON(pmd->need_commit); - - /* * We re-read the superblock every time. Shouldn't need to do this * really. */ @@ -515,32 +698,8 @@ static int __begin_transaction(struct dm_pool_metadata *pmd) pmd->flags = le32_to_cpu(disk_super->flags); pmd->data_block_size = le32_to_cpu(disk_super->data_block_size); - features = le32_to_cpu(disk_super->incompat_flags) & ~THIN_FEATURE_INCOMPAT_SUPP; - if (features) { - DMERR("could not access metadata due to " - "unsupported optional features (%lx).", - (unsigned long)features); - r = -EINVAL; - goto out; - } - - /* - * Check for read-only metadata to skip the following RDWR checks. - */ - if (get_disk_ro(pmd->bdev->bd_disk)) - goto out; - - features = le32_to_cpu(disk_super->compat_ro_flags) & ~THIN_FEATURE_COMPAT_RO_SUPP; - if (features) { - DMERR("could not access metadata RDWR due to " - "unsupported optional features (%lx).", - (unsigned long)features); - r = -EINVAL; - } - -out: dm_bm_unlock(sblock); - return r; + return 0; } static int __write_changed_details(struct dm_pool_metadata *pmd) @@ -573,8 +732,6 @@ static int __write_changed_details(struct dm_pool_metadata *pmd) list_del(&td->list); kfree(td); } - - pmd->need_commit = 1; } return 0; @@ -582,9 +739,6 @@ static int __write_changed_details(struct dm_pool_metadata *pmd) static int __commit_transaction(struct dm_pool_metadata *pmd) { - /* - * FIXME: Associated pool should be made read-only on failure. - */ int r; size_t metadata_len, data_len; struct thin_disk_superblock *disk_super; @@ -597,31 +751,27 @@ static int __commit_transaction(struct dm_pool_metadata *pmd) r = __write_changed_details(pmd); if (r < 0) - goto out; - - if (!pmd->need_commit) - goto out; + return r; r = dm_sm_commit(pmd->data_sm); if (r < 0) - goto out; + return r; r = dm_tm_pre_commit(pmd->tm); if (r < 0) - goto out; + return r; r = dm_sm_root_size(pmd->metadata_sm, &metadata_len); if (r < 0) - goto out; + return r; r = dm_sm_root_size(pmd->data_sm, &data_len); if (r < 0) - goto out; + return r; - r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION, - &sb_validator, &sblock); + r = superblock_lock(pmd, &sblock); if (r) - goto out; + return r; disk_super = dm_block_data(sblock); disk_super->time = cpu_to_le32(pmd->time); @@ -640,12 +790,7 @@ static int __commit_transaction(struct dm_pool_metadata *pmd) if (r < 0) goto out_locked; - r = dm_tm_commit(pmd->tm, sblock); - if (!r) - pmd->need_commit = 0; - -out: - return r; + return dm_tm_commit(pmd->tm, sblock); out_locked: dm_bm_unlock(sblock); @@ -653,15 +798,11 @@ out_locked: } struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev, - sector_t data_block_size) + sector_t data_block_size, + bool format_device) { int r; - struct thin_disk_superblock *disk_super; struct dm_pool_metadata *pmd; - sector_t bdev_size = i_size_read(bdev->bd_inode) >> SECTOR_SHIFT; - struct dm_block_manager *bm; - int create; - struct dm_block *sblock; pmd = kmalloc(sizeof(*pmd), GFP_KERNEL); if (!pmd) { @@ -669,90 +810,28 @@ struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev, return ERR_PTR(-ENOMEM); } - /* - * Max hex locks: - * 3 for btree insert + - * 2 for btree lookup used within space map - */ - bm = dm_block_manager_create(bdev, THIN_METADATA_BLOCK_SIZE, - THIN_METADATA_CACHE_SIZE, 5); - if (!bm) { - DMERR("could not create block manager"); - kfree(pmd); - return ERR_PTR(-ENOMEM); - } - - r = superblock_all_zeroes(bm, &create); - if (r) { - dm_block_manager_destroy(bm); - kfree(pmd); - return ERR_PTR(r); - } - + init_rwsem(&pmd->root_lock); + pmd->time = 0; + INIT_LIST_HEAD(&pmd->thin_devices); + pmd->read_only = false; + pmd->fail_io = false; + pmd->bdev = bdev; + pmd->data_block_size = data_block_size; - r = init_pmd(pmd, bm, 0, create); + r = __create_persistent_data_objects(pmd, format_device); if (r) { - dm_block_manager_destroy(bm); kfree(pmd); return ERR_PTR(r); } - pmd->bdev = bdev; - - if (!create) { - r = __begin_transaction(pmd); - if (r < 0) - goto bad; - return pmd; - } - - /* - * Create. - */ - r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION, - &sb_validator, &sblock); - if (r) - goto bad; - - if (bdev_size > THIN_METADATA_MAX_SECTORS) - bdev_size = THIN_METADATA_MAX_SECTORS; - - disk_super = dm_block_data(sblock); - disk_super->magic = cpu_to_le64(THIN_SUPERBLOCK_MAGIC); - disk_super->version = cpu_to_le32(THIN_VERSION); - disk_super->time = 0; - disk_super->metadata_block_size = cpu_to_le32(THIN_METADATA_BLOCK_SIZE >> SECTOR_SHIFT); - disk_super->metadata_nr_blocks = cpu_to_le64(bdev_size >> SECTOR_TO_BLOCK_SHIFT); - disk_super->data_block_size = cpu_to_le32(data_block_size); - - r = dm_bm_unlock(sblock); - if (r < 0) - goto bad; - - r = dm_btree_empty(&pmd->info, &pmd->root); - if (r < 0) - goto bad; - - r = dm_btree_empty(&pmd->details_info, &pmd->details_root); - if (r < 0) { - DMERR("couldn't create devices root"); - goto bad; - } - pmd->flags = 0; - pmd->need_commit = 1; - r = dm_pool_commit_metadata(pmd); + r = __begin_transaction(pmd); if (r < 0) { - DMERR("%s: dm_pool_commit_metadata() failed, error = %d", - __func__, r); - goto bad; + if (dm_pool_metadata_close(pmd) < 0) + DMWARN("%s: dm_pool_metadata_close() failed.", __func__); + return ERR_PTR(r); } return pmd; - -bad: - if (dm_pool_metadata_close(pmd) < 0) - DMWARN("%s: dm_pool_metadata_close() failed.", __func__); - return ERR_PTR(r); } int dm_pool_metadata_close(struct dm_pool_metadata *pmd) @@ -778,18 +857,17 @@ int dm_pool_metadata_close(struct dm_pool_metadata *pmd) return -EBUSY; } - r = __commit_transaction(pmd); - if (r < 0) - DMWARN("%s: __commit_transaction() failed, error = %d", - __func__, r); + if (!pmd->read_only && !pmd->fail_io) { + r = __commit_transaction(pmd); + if (r < 0) + DMWARN("%s: __commit_transaction() failed, error = %d", + __func__, r); + } - dm_tm_destroy(pmd->tm); - dm_tm_destroy(pmd->nb_tm); - dm_block_manager_destroy(pmd->bm); - dm_sm_destroy(pmd->metadata_sm); - dm_sm_destroy(pmd->data_sm); - kfree(pmd); + if (!pmd->fail_io) + __destroy_persistent_data_objects(pmd); + kfree(pmd); return 0; } @@ -850,6 +928,7 @@ static int __open_device(struct dm_pool_metadata *pmd, (*td)->id = dev; (*td)->open_count = 1; (*td)->changed = changed; + (*td)->aborted_with_changes = false; (*td)->mapped_blocks = le64_to_cpu(details_le.mapped_blocks); (*td)->transaction_id = le64_to_cpu(details_le.transaction_id); (*td)->creation_time = le32_to_cpu(details_le.creation_time); @@ -911,10 +990,11 @@ static int __create_thin(struct dm_pool_metadata *pmd, int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_thin_id dev) { - int r; + int r = -EINVAL; down_write(&pmd->root_lock); - r = __create_thin(pmd, dev); + if (!pmd->fail_io) + r = __create_thin(pmd, dev); up_write(&pmd->root_lock); return r; @@ -1001,10 +1081,11 @@ int dm_pool_create_snap(struct dm_pool_metadata *pmd, dm_thin_id dev, dm_thin_id origin) { - int r; + int r = -EINVAL; down_write(&pmd->root_lock); - r = __create_snap(pmd, dev, origin); + if (!pmd->fail_io) + r = __create_snap(pmd, dev, origin); up_write(&pmd->root_lock); return r; @@ -1037,18 +1118,17 @@ static int __delete_device(struct dm_pool_metadata *pmd, dm_thin_id dev) if (r) return r; - pmd->need_commit = 1; - return 0; } int dm_pool_delete_thin_device(struct dm_pool_metadata *pmd, dm_thin_id dev) { - int r; + int r = -EINVAL; down_write(&pmd->root_lock); - r = __delete_device(pmd, dev); + if (!pmd->fail_io) + r = __delete_device(pmd, dev); up_write(&pmd->root_lock); return r; @@ -1058,28 +1138,40 @@ int dm_pool_set_metadata_transaction_id(struct dm_pool_metadata *pmd, uint64_t current_id, uint64_t new_id) { + int r = -EINVAL; + down_write(&pmd->root_lock); + + if (pmd->fail_io) + goto out; + if (pmd->trans_id != current_id) { - up_write(&pmd->root_lock); DMERR("mismatched transaction id"); - return -EINVAL; + goto out; } pmd->trans_id = new_id; - pmd->need_commit = 1; + r = 0; + +out: up_write(&pmd->root_lock); - return 0; + return r; } int dm_pool_get_metadata_transaction_id(struct dm_pool_metadata *pmd, uint64_t *result) { + int r = -EINVAL; + down_read(&pmd->root_lock); - *result = pmd->trans_id; + if (!pmd->fail_io) { + *result = pmd->trans_id; + r = 0; + } up_read(&pmd->root_lock); - return 0; + return r; } static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) @@ -1108,8 +1200,6 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) dm_tm_dec(pmd->tm, held_root); dm_tm_unlock(pmd->tm, copy); - pmd->need_commit = 1; - return -EBUSY; } @@ -1131,29 +1221,25 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) /* * Write the held root into the superblock. */ - r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION, - &sb_validator, &sblock); + r = superblock_lock(pmd, &sblock); if (r) { dm_tm_dec(pmd->tm, held_root); - pmd->need_commit = 1; return r; } disk_super = dm_block_data(sblock); disk_super->held_root = cpu_to_le64(held_root); dm_bm_unlock(sblock); - - pmd->need_commit = 1; - return 0; } int dm_pool_reserve_metadata_snap(struct dm_pool_metadata *pmd) { - int r; + int r = -EINVAL; down_write(&pmd->root_lock); - r = __reserve_metadata_snap(pmd); + if (!pmd->fail_io) + r = __reserve_metadata_snap(pmd); up_write(&pmd->root_lock); return r; @@ -1166,15 +1252,13 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd) struct dm_block *sblock, *copy; dm_block_t held_root; - r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION, - &sb_validator, &sblock); + r = superblock_lock(pmd, &sblock); if (r) return r; disk_super = dm_block_data(sblock); held_root = le64_to_cpu(disk_super->held_root); disk_super->held_root = cpu_to_le64(0); - pmd->need_commit = 1; dm_bm_unlock(sblock); @@ -1197,10 +1281,11 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd) int dm_pool_release_metadata_snap(struct dm_pool_metadata *pmd) { - int r; + int r = -EINVAL; down_write(&pmd->root_lock); - r = __release_metadata_snap(pmd); + if (!pmd->fail_io) + r = __release_metadata_snap(pmd); up_write(&pmd->root_lock); return r; @@ -1227,10 +1312,11 @@ static int __get_metadata_snap(struct dm_pool_metadata *pmd, int dm_pool_get_metadata_snap(struct dm_pool_metadata *pmd, dm_block_t *result) { - int r; + int r = -EINVAL; down_read(&pmd->root_lock); - r = __get_metadata_snap(pmd, result); + if (!pmd->fail_io) + r = __get_metadata_snap(pmd, result); up_read(&pmd->root_lock); return r; @@ -1239,10 +1325,11 @@ int dm_pool_get_metadata_snap(struct dm_pool_metadata *pmd, int dm_pool_open_thin_device(struct dm_pool_metadata *pmd, dm_thin_id dev, struct dm_thin_device **td) { - int r; + int r = -EINVAL; down_write(&pmd->root_lock); - r = __open_device(pmd, dev, 0, td); + if (!pmd->fail_io) + r = __open_device(pmd, dev, 0, td); up_write(&pmd->root_lock); return r; @@ -1262,7 +1349,7 @@ dm_thin_id dm_thin_dev_id(struct dm_thin_device *td) return td->id; } -static int __snapshotted_since(struct dm_thin_device *td, uint32_t time) +static bool __snapshotted_since(struct dm_thin_device *td, uint32_t time) { return td->snapshotted_time > time; } @@ -1270,28 +1357,31 @@ static int __snapshotted_since(struct dm_thin_device *td, uint32_t time) int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block, int can_block, struct dm_thin_lookup_result *result) { - int r; + int r = -EINVAL; uint64_t block_time = 0; __le64 value; struct dm_pool_metadata *pmd = td->pmd; dm_block_t keys[2] = { td->id, block }; + struct dm_btree_info *info; if (can_block) { down_read(&pmd->root_lock); - r = dm_btree_lookup(&pmd->info, pmd->root, keys, &value); - if (!r) - block_time = le64_to_cpu(value); - up_read(&pmd->root_lock); - - } else if (down_read_trylock(&pmd->root_lock)) { - r = dm_btree_lookup(&pmd->nb_info, pmd->root, keys, &value); - if (!r) - block_time = le64_to_cpu(value); - up_read(&pmd->root_lock); - - } else + info = &pmd->info; + } else if (down_read_trylock(&pmd->root_lock)) + info = &pmd->nb_info; + else return -EWOULDBLOCK; + if (pmd->fail_io) + goto out; + + r = dm_btree_lookup(info, pmd->root, keys, &value); + if (!r) + block_time = le64_to_cpu(value); + +out: + up_read(&pmd->root_lock); + if (!r) { dm_block_t exception_block; uint32_t exception_time; @@ -1312,7 +1402,6 @@ static int __insert(struct dm_thin_device *td, dm_block_t block, struct dm_pool_metadata *pmd = td->pmd; dm_block_t keys[2] = { td->id, block }; - pmd->need_commit = 1; value = cpu_to_le64(pack_block_time(data_block, pmd->time)); __dm_bless_for_disk(&value); @@ -1321,10 +1410,9 @@ static int __insert(struct dm_thin_device *td, dm_block_t block, if (r) return r; - if (inserted) { + td->changed = 1; + if (inserted) td->mapped_blocks++; - td->changed = 1; - } return 0; } @@ -1332,10 +1420,11 @@ static int __insert(struct dm_thin_device *td, dm_block_t block, int dm_thin_insert_block(struct dm_thin_device *td, dm_block_t block, dm_block_t data_block) { - int r; + int r = -EINVAL; down_write(&td->pmd->root_lock); - r = __insert(td, block, data_block); + if (!td->pmd->fail_io) + r = __insert(td, block, data_block); up_write(&td->pmd->root_lock); return r; @@ -1353,31 +1442,51 @@ static int __remove(struct dm_thin_device *td, dm_block_t block) td->mapped_blocks--; td->changed = 1; - pmd->need_commit = 1; return 0; } int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block) { - int r; + int r = -EINVAL; down_write(&td->pmd->root_lock); - r = __remove(td, block); + if (!td->pmd->fail_io) + r = __remove(td, block); up_write(&td->pmd->root_lock); return r; } -int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result) +bool dm_thin_changed_this_transaction(struct dm_thin_device *td) { int r; - down_write(&pmd->root_lock); + down_read(&td->pmd->root_lock); + r = td->changed; + up_read(&td->pmd->root_lock); - r = dm_sm_new_block(pmd->data_sm, result); - pmd->need_commit = 1; + return r; +} + +bool dm_thin_aborted_changes(struct dm_thin_device *td) +{ + bool r; + down_read(&td->pmd->root_lock); + r = td->aborted_with_changes; + up_read(&td->pmd->root_lock); + + return r; +} + +int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result) +{ + int r = -EINVAL; + + down_write(&pmd->root_lock); + if (!pmd->fail_io) + r = dm_sm_new_block(pmd->data_sm, result); up_write(&pmd->root_lock); return r; @@ -1385,9 +1494,11 @@ int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result) int dm_pool_commit_metadata(struct dm_pool_metadata *pmd) { - int r; + int r = -EINVAL; down_write(&pmd->root_lock); + if (pmd->fail_io) + goto out; r = __commit_transaction(pmd); if (r <= 0) @@ -1402,12 +1513,41 @@ out: return r; } +static void __set_abort_with_changes_flags(struct dm_pool_metadata *pmd) +{ + struct dm_thin_device *td; + + list_for_each_entry(td, &pmd->thin_devices, list) + td->aborted_with_changes = td->changed; +} + +int dm_pool_abort_metadata(struct dm_pool_metadata *pmd) +{ + int r = -EINVAL; + + down_write(&pmd->root_lock); + if (pmd->fail_io) + goto out; + + __set_abort_with_changes_flags(pmd); + __destroy_persistent_data_objects(pmd); + r = __create_persistent_data_objects(pmd, false); + if (r) + pmd->fail_io = true; + +out: + up_write(&pmd->root_lock); + + return r; +} + int dm_pool_get_free_block_count(struct dm_pool_metadata *pmd, dm_block_t *result) { - int r; + int r = -EINVAL; down_read(&pmd->root_lock); - r = dm_sm_get_nr_free(pmd->data_sm, result); + if (!pmd->fail_io) + r = dm_sm_get_nr_free(pmd->data_sm, result); up_read(&pmd->root_lock); return r; @@ -1416,10 +1556,11 @@ int dm_pool_get_free_block_count(struct dm_pool_metadata *pmd, dm_block_t *resul int dm_pool_get_free_metadata_block_count(struct dm_pool_metadata *pmd, dm_block_t *result) { - int r; + int r = -EINVAL; down_read(&pmd->root_lock); - r = dm_sm_get_nr_free(pmd->metadata_sm, result); + if (!pmd->fail_io) + r = dm_sm_get_nr_free(pmd->metadata_sm, result); up_read(&pmd->root_lock); return r; @@ -1428,10 +1569,11 @@ int dm_pool_get_free_metadata_block_count(struct dm_pool_metadata *pmd, int dm_pool_get_metadata_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result) { - int r; + int r = -EINVAL; down_read(&pmd->root_lock); - r = dm_sm_get_nr_blocks(pmd->metadata_sm, result); + if (!pmd->fail_io) + r = dm_sm_get_nr_blocks(pmd->metadata_sm, result); up_read(&pmd->root_lock); return r; @@ -1448,10 +1590,11 @@ int dm_pool_get_data_block_size(struct dm_pool_metadata *pmd, sector_t *result) int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result) { - int r; + int r = -EINVAL; down_read(&pmd->root_lock); - r = dm_sm_get_nr_blocks(pmd->data_sm, result); + if (!pmd->fail_io) + r = dm_sm_get_nr_blocks(pmd->data_sm, result); up_read(&pmd->root_lock); return r; @@ -1459,13 +1602,17 @@ int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result) int dm_thin_get_mapped_count(struct dm_thin_device *td, dm_block_t *result) { + int r = -EINVAL; struct dm_pool_metadata *pmd = td->pmd; down_read(&pmd->root_lock); - *result = td->mapped_blocks; + if (!pmd->fail_io) { + *result = td->mapped_blocks; + r = 0; + } up_read(&pmd->root_lock); - return 0; + return r; } static int __highest_block(struct dm_thin_device *td, dm_block_t *result) @@ -1487,11 +1634,12 @@ static int __highest_block(struct dm_thin_device *td, dm_block_t *result) int dm_thin_get_highest_mapped_block(struct dm_thin_device *td, dm_block_t *result) { - int r; + int r = -EINVAL; struct dm_pool_metadata *pmd = td->pmd; down_read(&pmd->root_lock); - r = __highest_block(td, result); + if (!pmd->fail_io) + r = __highest_block(td, result); up_read(&pmd->root_lock); return r; @@ -1514,20 +1662,25 @@ static int __resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count) return -EINVAL; } - r = dm_sm_extend(pmd->data_sm, new_count - old_count); - if (!r) - pmd->need_commit = 1; - - return r; + return dm_sm_extend(pmd->data_sm, new_count - old_count); } int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count) { - int r; + int r = -EINVAL; down_write(&pmd->root_lock); - r = __resize_data_dev(pmd, new_count); + if (!pmd->fail_io) + r = __resize_data_dev(pmd, new_count); up_write(&pmd->root_lock); return r; } + +void dm_pool_metadata_read_only(struct dm_pool_metadata *pmd) +{ + down_write(&pmd->root_lock); + pmd->read_only = true; + dm_bm_set_read_only(pmd->bm); + up_write(&pmd->root_lock); +} diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h index b88918ccdaf6..0cecc3702885 100644 --- a/drivers/md/dm-thin-metadata.h +++ b/drivers/md/dm-thin-metadata.h @@ -38,7 +38,8 @@ typedef uint64_t dm_thin_id; * Reopens or creates a new, empty metadata volume. */ struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev, - sector_t data_block_size); + sector_t data_block_size, + bool format_device); int dm_pool_metadata_close(struct dm_pool_metadata *pmd); @@ -79,6 +80,16 @@ int dm_pool_delete_thin_device(struct dm_pool_metadata *pmd, int dm_pool_commit_metadata(struct dm_pool_metadata *pmd); /* + * Discards all uncommitted changes. Rereads the superblock, rolling back + * to the last good transaction. Thin devices remain open. + * dm_thin_aborted_changes() tells you if they had uncommitted changes. + * + * If this call fails it's only useful to call dm_pool_metadata_close(). + * All other methods will fail with -EINVAL. + */ +int dm_pool_abort_metadata(struct dm_pool_metadata *pmd); + +/* * Set/get userspace transaction id. */ int dm_pool_set_metadata_transaction_id(struct dm_pool_metadata *pmd, @@ -119,7 +130,7 @@ dm_thin_id dm_thin_dev_id(struct dm_thin_device *td); struct dm_thin_lookup_result { dm_block_t block; - int shared; + unsigned shared:1; }; /* @@ -147,6 +158,10 @@ int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block); /* * Queries. */ +bool dm_thin_changed_this_transaction(struct dm_thin_device *td); + +bool dm_thin_aborted_changes(struct dm_thin_device *td); + int dm_thin_get_highest_mapped_block(struct dm_thin_device *td, dm_block_t *highest_mapped); @@ -171,6 +186,12 @@ int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result); */ int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_size); +/* + * Flicks the underlying block manager into read only mode, so you know + * that nothing is changing. + */ +void dm_pool_metadata_read_only(struct dm_pool_metadata *pmd); + /*----------------------------------------------------------------*/ #endif diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 68694da0d21d..af1fc3b2c2ad 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -1,10 +1,11 @@ /* - * Copyright (C) 2011 Red Hat UK. + * Copyright (C) 2011-2012 Red Hat UK. * * This file is released under the GPL. */ #include "dm-thin-metadata.h" +#include "dm.h" #include <linux/device-mapper.h> #include <linux/dm-io.h> @@ -19,7 +20,7 @@ /* * Tunable constants */ -#define ENDIO_HOOK_POOL_SIZE 10240 +#define ENDIO_HOOK_POOL_SIZE 1024 #define DEFERRED_SET_SIZE 64 #define MAPPING_POOL_SIZE 1024 #define PRISON_CELLS 1024 @@ -496,12 +497,27 @@ static void build_virtual_key(struct dm_thin_device *td, dm_block_t b, */ struct dm_thin_new_mapping; +/* + * The pool runs in 3 modes. Ordered in degraded order for comparisons. + */ +enum pool_mode { + PM_WRITE, /* metadata may be changed */ + PM_READ_ONLY, /* metadata may not be changed */ + PM_FAIL, /* all I/O fails */ +}; + struct pool_features { + enum pool_mode mode; + unsigned zero_new_blocks:1; unsigned discard_enabled:1; unsigned discard_passdown:1; }; +struct thin_c; +typedef void (*process_bio_fn)(struct thin_c *tc, struct bio *bio); +typedef void (*process_mapping_fn)(struct dm_thin_new_mapping *m); + struct pool { struct list_head list; struct dm_target *ti; /* Only set if a pool target is bound */ @@ -510,10 +526,9 @@ struct pool { struct block_device *md_dev; struct dm_pool_metadata *pmd; - uint32_t sectors_per_block; - unsigned block_shift; - dm_block_t offset_mask; dm_block_t low_water_blocks; + uint32_t sectors_per_block; + int sectors_per_block_shift; struct pool_features pf; unsigned low_water_triggered:1; /* A dm event has been sent */ @@ -526,8 +541,8 @@ struct pool { struct work_struct worker; struct delayed_work waker; - unsigned ref_count; unsigned long last_commit_jiffies; + unsigned ref_count; spinlock_t lock; struct bio_list deferred_bios; @@ -543,8 +558,17 @@ struct pool { struct dm_thin_new_mapping *next_mapping; mempool_t *mapping_pool; mempool_t *endio_hook_pool; + + process_bio_fn process_bio; + process_bio_fn process_discard; + + process_mapping_fn process_prepared_mapping; + process_mapping_fn process_prepared_discard; }; +static enum pool_mode get_pool_mode(struct pool *pool); +static void set_pool_mode(struct pool *pool, enum pool_mode mode); + /* * Target context for a pool. */ @@ -679,16 +703,28 @@ static void requeue_io(struct thin_c *tc) static dm_block_t get_bio_block(struct thin_c *tc, struct bio *bio) { - return bio->bi_sector >> tc->pool->block_shift; + sector_t block_nr = bio->bi_sector; + + if (tc->pool->sectors_per_block_shift < 0) + (void) sector_div(block_nr, tc->pool->sectors_per_block); + else + block_nr >>= tc->pool->sectors_per_block_shift; + + return block_nr; } static void remap(struct thin_c *tc, struct bio *bio, dm_block_t block) { struct pool *pool = tc->pool; + sector_t bi_sector = bio->bi_sector; bio->bi_bdev = tc->pool_dev->bdev; - bio->bi_sector = (block << pool->block_shift) + - (bio->bi_sector & pool->offset_mask); + if (tc->pool->sectors_per_block_shift < 0) + bio->bi_sector = (block * pool->sectors_per_block) + + sector_div(bi_sector, pool->sectors_per_block); + else + bio->bi_sector = (block << pool->sectors_per_block_shift) | + (bi_sector & (pool->sectors_per_block - 1)); } static void remap_to_origin(struct thin_c *tc, struct bio *bio) @@ -696,21 +732,39 @@ static void remap_to_origin(struct thin_c *tc, struct bio *bio) bio->bi_bdev = tc->origin_dev->bdev; } +static int bio_triggers_commit(struct thin_c *tc, struct bio *bio) +{ + return (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) && + dm_thin_changed_this_transaction(tc->td); +} + static void issue(struct thin_c *tc, struct bio *bio) { struct pool *pool = tc->pool; unsigned long flags; + if (!bio_triggers_commit(tc, bio)) { + generic_make_request(bio); + return; + } + /* - * Batch together any FUA/FLUSH bios we find and then issue - * a single commit for them in process_deferred_bios(). + * Complete bio with an error if earlier I/O caused changes to + * the metadata that can't be committed e.g, due to I/O errors + * on the metadata device. */ - if (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) { - spin_lock_irqsave(&pool->lock, flags); - bio_list_add(&pool->deferred_flush_bios, bio); - spin_unlock_irqrestore(&pool->lock, flags); - } else - generic_make_request(bio); + if (dm_thin_aborted_changes(tc->td)) { + bio_io_error(bio); + return; + } + + /* + * Batch together any bios that trigger commits and then issue a + * single commit for them in process_deferred_bios(). + */ + spin_lock_irqsave(&pool->lock, flags); + bio_list_add(&pool->deferred_flush_bios, bio); + spin_unlock_irqrestore(&pool->lock, flags); } static void remap_to_origin_and_issue(struct thin_c *tc, struct bio *bio) @@ -847,6 +901,14 @@ static void cell_defer_except(struct thin_c *tc, struct dm_bio_prison_cell *cell wake_worker(pool); } +static void process_prepared_mapping_fail(struct dm_thin_new_mapping *m) +{ + if (m->bio) + m->bio->bi_end_io = m->saved_bi_end_io; + cell_error(m->cell); + list_del(&m->list); + mempool_free(m, m->tc->pool->mapping_pool); +} static void process_prepared_mapping(struct dm_thin_new_mapping *m) { struct thin_c *tc = m->tc; @@ -859,7 +921,7 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m) if (m->err) { cell_error(m->cell); - return; + goto out; } /* @@ -871,7 +933,7 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m) if (r) { DMERR("dm_thin_insert_block() failed"); cell_error(m->cell); - return; + goto out; } /* @@ -886,22 +948,25 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m) } else cell_defer(tc, m->cell, m->data_block); +out: list_del(&m->list); mempool_free(m, tc->pool->mapping_pool); } -static void process_prepared_discard(struct dm_thin_new_mapping *m) +static void process_prepared_discard_fail(struct dm_thin_new_mapping *m) { - int r; struct thin_c *tc = m->tc; - r = dm_thin_remove_block(tc->td, m->virt_block); - if (r) - DMERR("dm_thin_remove_block() failed"); + bio_io_error(m->bio); + cell_defer_except(tc, m->cell); + cell_defer_except(tc, m->cell2); + mempool_free(m, tc->pool->mapping_pool); +} + +static void process_prepared_discard_passdown(struct dm_thin_new_mapping *m) +{ + struct thin_c *tc = m->tc; - /* - * Pass the discard down to the underlying device? - */ if (m->pass_discard) remap_and_issue(tc, m->bio, m->data_block); else @@ -912,8 +977,20 @@ static void process_prepared_discard(struct dm_thin_new_mapping *m) mempool_free(m, tc->pool->mapping_pool); } +static void process_prepared_discard(struct dm_thin_new_mapping *m) +{ + int r; + struct thin_c *tc = m->tc; + + r = dm_thin_remove_block(tc->td, m->virt_block); + if (r) + DMERR("dm_thin_remove_block() failed"); + + process_prepared_discard_passdown(m); +} + static void process_prepared(struct pool *pool, struct list_head *head, - void (*fn)(struct dm_thin_new_mapping *)) + process_mapping_fn *fn) { unsigned long flags; struct list_head maps; @@ -925,7 +1002,7 @@ static void process_prepared(struct pool *pool, struct list_head *head, spin_unlock_irqrestore(&pool->lock, flags); list_for_each_entry_safe(m, tmp, &maps, list) - fn(m); + (*fn)(m); } /* @@ -933,9 +1010,7 @@ static void process_prepared(struct pool *pool, struct list_head *head, */ static int io_overlaps_block(struct pool *pool, struct bio *bio) { - return !(bio->bi_sector & pool->offset_mask) && - (bio->bi_size == (pool->sectors_per_block << SECTOR_SHIFT)); - + return bio->bi_size == (pool->sectors_per_block << SECTOR_SHIFT); } static int io_overwrites_block(struct pool *pool, struct bio *bio) @@ -1093,6 +1168,35 @@ static void schedule_zero(struct thin_c *tc, dm_block_t virt_block, } } +static int commit(struct pool *pool) +{ + int r; + + r = dm_pool_commit_metadata(pool->pmd); + if (r) + DMERR("commit failed, error = %d", r); + + return r; +} + +/* + * A non-zero return indicates read_only or fail_io mode. + * Many callers don't care about the return value. + */ +static int commit_or_fallback(struct pool *pool) +{ + int r; + + if (get_pool_mode(pool) != PM_WRITE) + return -EINVAL; + + r = commit(pool); + if (r) + set_pool_mode(pool, PM_READ_ONLY); + + return r; +} + static int alloc_data_block(struct thin_c *tc, dm_block_t *result) { int r; @@ -1121,12 +1225,7 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result) * Try to commit to see if that will free up some * more space. */ - r = dm_pool_commit_metadata(pool->pmd); - if (r) { - DMERR("%s: dm_pool_commit_metadata() failed, error = %d", - __func__, r); - return r; - } + (void) commit_or_fallback(pool); r = dm_pool_get_free_block_count(pool->pmd, &free_blocks); if (r) @@ -1218,7 +1317,7 @@ static void process_discard(struct thin_c *tc, struct bio *bio) */ m = get_next_mapping(pool); m->tc = tc; - m->pass_discard = (!lookup_result.shared) & pool->pf.discard_passdown; + m->pass_discard = (!lookup_result.shared) && pool->pf.discard_passdown; m->virt_block = block; m->data_block = lookup_result.block; m->cell = cell; @@ -1234,15 +1333,10 @@ static void process_discard(struct thin_c *tc, struct bio *bio) } } else { /* - * This path is hit if people are ignoring - * limits->discard_granularity. It ignores any - * part of the discard that is in a subsequent - * block. + * The DM core makes sure that the discard doesn't span + * a block boundary. So we submit the discard of a + * partial block appropriately. */ - sector_t offset = bio->bi_sector - (block << pool->block_shift); - unsigned remaining = (pool->sectors_per_block - offset) << 9; - bio->bi_size = min(bio->bi_size, remaining); - cell_release_singleton(cell, bio); cell_release_singleton(cell2, bio); if ((!lookup_result.shared) && pool->pf.discard_passdown) @@ -1310,7 +1404,7 @@ static void process_shared_bio(struct thin_c *tc, struct bio *bio, if (bio_detain(pool->prison, &key, bio, &cell)) return; - if (bio_data_dir(bio) == WRITE) + if (bio_data_dir(bio) == WRITE && bio->bi_size) break_sharing(tc, bio, block, &key, lookup_result, cell); else { struct dm_thin_endio_hook *h = dm_get_mapinfo(bio)->ptr; @@ -1362,6 +1456,7 @@ static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block default: DMERR("%s: alloc_data_block() failed, error = %d", __func__, r); + set_pool_mode(tc->pool, PM_READ_ONLY); cell_error(cell); break; } @@ -1419,6 +1514,49 @@ static void process_bio(struct thin_c *tc, struct bio *bio) } } +static void process_bio_read_only(struct thin_c *tc, struct bio *bio) +{ + int r; + int rw = bio_data_dir(bio); + dm_block_t block = get_bio_block(tc, bio); + struct dm_thin_lookup_result lookup_result; + + r = dm_thin_find_block(tc->td, block, 1, &lookup_result); + switch (r) { + case 0: + if (lookup_result.shared && (rw == WRITE) && bio->bi_size) + bio_io_error(bio); + else + remap_and_issue(tc, bio, lookup_result.block); + break; + + case -ENODATA: + if (rw != READ) { + bio_io_error(bio); + break; + } + + if (tc->origin_dev) { + remap_to_origin_and_issue(tc, bio); + break; + } + + zero_fill_bio(bio); + bio_endio(bio, 0); + break; + + default: + DMERR("dm_thin_find_block() failed, error = %d", r); + bio_io_error(bio); + break; + } +} + +static void process_bio_fail(struct thin_c *tc, struct bio *bio) +{ + bio_io_error(bio); +} + static int need_commit_due_to_time(struct pool *pool) { return jiffies < pool->last_commit_jiffies || @@ -1430,7 +1568,6 @@ static void process_deferred_bios(struct pool *pool) unsigned long flags; struct bio *bio; struct bio_list bios; - int r; bio_list_init(&bios); @@ -1457,9 +1594,9 @@ static void process_deferred_bios(struct pool *pool) } if (bio->bi_rw & REQ_DISCARD) - process_discard(tc, bio); + pool->process_discard(tc, bio); else - process_bio(tc, bio); + pool->process_bio(tc, bio); } /* @@ -1475,10 +1612,7 @@ static void process_deferred_bios(struct pool *pool) if (bio_list_empty(&bios) && !need_commit_due_to_time(pool)) return; - r = dm_pool_commit_metadata(pool->pmd); - if (r) { - DMERR("%s: dm_pool_commit_metadata() failed, error = %d", - __func__, r); + if (commit_or_fallback(pool)) { while ((bio = bio_list_pop(&bios))) bio_io_error(bio); return; @@ -1493,8 +1627,8 @@ static void do_worker(struct work_struct *ws) { struct pool *pool = container_of(ws, struct pool, worker); - process_prepared(pool, &pool->prepared_mappings, process_prepared_mapping); - process_prepared(pool, &pool->prepared_discards, process_prepared_discard); + process_prepared(pool, &pool->prepared_mappings, &pool->process_prepared_mapping); + process_prepared(pool, &pool->prepared_discards, &pool->process_prepared_discard); process_deferred_bios(pool); } @@ -1511,6 +1645,52 @@ static void do_waker(struct work_struct *ws) /*----------------------------------------------------------------*/ +static enum pool_mode get_pool_mode(struct pool *pool) +{ + return pool->pf.mode; +} + +static void set_pool_mode(struct pool *pool, enum pool_mode mode) +{ + int r; + + pool->pf.mode = mode; + + switch (mode) { + case PM_FAIL: + DMERR("switching pool to failure mode"); + pool->process_bio = process_bio_fail; + pool->process_discard = process_bio_fail; + pool->process_prepared_mapping = process_prepared_mapping_fail; + pool->process_prepared_discard = process_prepared_discard_fail; + break; + + case PM_READ_ONLY: + DMERR("switching pool to read-only mode"); + r = dm_pool_abort_metadata(pool->pmd); + if (r) { + DMERR("aborting transaction failed"); + set_pool_mode(pool, PM_FAIL); + } else { + dm_pool_metadata_read_only(pool->pmd); + pool->process_bio = process_bio_read_only; + pool->process_discard = process_discard; + pool->process_prepared_mapping = process_prepared_mapping_fail; + pool->process_prepared_discard = process_prepared_discard_passdown; + } + break; + + case PM_WRITE: + pool->process_bio = process_bio; + pool->process_discard = process_discard; + pool->process_prepared_mapping = process_prepared_mapping; + pool->process_prepared_discard = process_prepared_discard; + break; + } +} + +/*----------------------------------------------------------------*/ + /* * Mapping functions. */ @@ -1556,6 +1736,12 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio, struct dm_thin_lookup_result result; map_context->ptr = thin_hook_bio(tc, bio); + + if (get_pool_mode(tc->pool) == PM_FAIL) { + bio_io_error(bio); + return DM_MAPIO_SUBMITTED; + } + if (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA)) { thin_defer_bio(tc, bio); return DM_MAPIO_SUBMITTED; @@ -1592,14 +1778,35 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio, break; case -ENODATA: + if (get_pool_mode(tc->pool) == PM_READ_ONLY) { + /* + * This block isn't provisioned, and we have no way + * of doing so. Just error it. + */ + bio_io_error(bio); + r = DM_MAPIO_SUBMITTED; + break; + } + /* fall through */ + + case -EWOULDBLOCK: /* * In future, the failed dm_thin_find_block above could * provide the hint to load the metadata into cache. */ - case -EWOULDBLOCK: thin_defer_bio(tc, bio); r = DM_MAPIO_SUBMITTED; break; + + default: + /* + * Must always call bio_io_error on failure. + * dm_thin_find_block can fail with -EINVAL if the + * pool is switched to fail-io mode. + */ + bio_io_error(bio); + r = DM_MAPIO_SUBMITTED; + break; } return r; @@ -1636,15 +1843,26 @@ static int bind_control_target(struct pool *pool, struct dm_target *ti) { struct pool_c *pt = ti->private; + /* + * We want to make sure that degraded pools are never upgraded. + */ + enum pool_mode old_mode = pool->pf.mode; + enum pool_mode new_mode = pt->pf.mode; + + if (old_mode > new_mode) + new_mode = old_mode; + pool->ti = ti; pool->low_water_blocks = pt->low_water_blocks; pool->pf = pt->pf; + set_pool_mode(pool, new_mode); /* * If discard_passdown was enabled verify that the data device * supports discards. Disable discard_passdown if not; otherwise * -EOPNOTSUPP will be returned. */ + /* FIXME: pull this out into a sep fn. */ if (pt->pf.discard_passdown) { struct request_queue *q = bdev_get_queue(pt->data_dev->bdev); if (!q || !blk_queue_discard(q)) { @@ -1670,6 +1888,7 @@ static void unbind_control_target(struct pool *pool, struct dm_target *ti) /* Initialize pool features. */ static void pool_features_init(struct pool_features *pf) { + pf->mode = PM_WRITE; pf->zero_new_blocks = 1; pf->discard_enabled = 1; pf->discard_passdown = 1; @@ -1700,14 +1919,16 @@ static struct kmem_cache *_endio_hook_cache; static struct pool *pool_create(struct mapped_device *pool_md, struct block_device *metadata_dev, - unsigned long block_size, char **error) + unsigned long block_size, + int read_only, char **error) { int r; void *err_p; struct pool *pool; struct dm_pool_metadata *pmd; + bool format_device = read_only ? false : true; - pmd = dm_pool_metadata_open(metadata_dev, block_size); + pmd = dm_pool_metadata_open(metadata_dev, block_size, format_device); if (IS_ERR(pmd)) { *error = "Error creating metadata object"; return (struct pool *)pmd; @@ -1722,8 +1943,10 @@ static struct pool *pool_create(struct mapped_device *pool_md, pool->pmd = pmd; pool->sectors_per_block = block_size; - pool->block_shift = ffs(block_size) - 1; - pool->offset_mask = block_size - 1; + if (block_size & (block_size - 1)) + pool->sectors_per_block_shift = -1; + else + pool->sectors_per_block_shift = __ffs(block_size); pool->low_water_blocks = 0; pool_features_init(&pool->pf); pool->prison = prison_create(PRISON_CELLS); @@ -1822,25 +2045,29 @@ static void __pool_dec(struct pool *pool) static struct pool *__pool_find(struct mapped_device *pool_md, struct block_device *metadata_dev, - unsigned long block_size, char **error, - int *created) + unsigned long block_size, int read_only, + char **error, int *created) { struct pool *pool = __pool_table_lookup_metadata_dev(metadata_dev); if (pool) { - if (pool->pool_md != pool_md) + if (pool->pool_md != pool_md) { + *error = "metadata device already in use by a pool"; return ERR_PTR(-EBUSY); + } __pool_inc(pool); } else { pool = __pool_table_lookup(pool_md); if (pool) { - if (pool->md_dev != metadata_dev) + if (pool->md_dev != metadata_dev) { + *error = "different pool cannot replace a pool"; return ERR_PTR(-EINVAL); + } __pool_inc(pool); } else { - pool = pool_create(pool_md, metadata_dev, block_size, error); + pool = pool_create(pool_md, metadata_dev, block_size, read_only, error); *created = 1; } } @@ -1891,19 +2118,23 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf, arg_name = dm_shift_arg(as); argc--; - if (!strcasecmp(arg_name, "skip_block_zeroing")) { + if (!strcasecmp(arg_name, "skip_block_zeroing")) pf->zero_new_blocks = 0; - continue; - } else if (!strcasecmp(arg_name, "ignore_discard")) { + + else if (!strcasecmp(arg_name, "ignore_discard")) pf->discard_enabled = 0; - continue; - } else if (!strcasecmp(arg_name, "no_discard_passdown")) { + + else if (!strcasecmp(arg_name, "no_discard_passdown")) pf->discard_passdown = 0; - continue; - } - ti->error = "Unrecognised pool feature requested"; - r = -EINVAL; + else if (!strcasecmp(arg_name, "read_only")) + pf->mode = PM_READ_ONLY; + + else { + ti->error = "Unrecognised pool feature requested"; + r = -EINVAL; + break; + } } return r; @@ -1967,7 +2198,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv) if (kstrtoul(argv[2], 10, &block_size) || !block_size || block_size < DATA_DEV_BLOCK_SIZE_MIN_SECTORS || block_size > DATA_DEV_BLOCK_SIZE_MAX_SECTORS || - !is_power_of_2(block_size)) { + block_size & (DATA_DEV_BLOCK_SIZE_MIN_SECTORS - 1)) { ti->error = "Invalid block size"; r = -EINVAL; goto out; @@ -1996,7 +2227,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv) } pool = __pool_find(dm_table_get_md(ti->table), metadata_dev->bdev, - block_size, &ti->error, &pool_created); + block_size, pf.mode == PM_READ_ONLY, &ti->error, &pool_created); if (IS_ERR(pool)) { r = PTR_ERR(pool); goto out_free_pt; @@ -2014,6 +2245,15 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv) goto out_flags_changed; } + /* + * The block layer requires discard_granularity to be a power of 2. + */ + if (pf.discard_enabled && !is_power_of_2(block_size)) { + ti->error = "Discard support must be disabled when the block size is not a power of 2"; + r = -EINVAL; + goto out_flags_changed; + } + pt->pool = pool; pt->ti = ti; pt->metadata_dev = metadata_dev; @@ -2033,7 +2273,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv) * stacking of discard limits (this keeps the pool and * thin devices' discard limits consistent). */ - ti->discards_supported = 1; + ti->discards_supported = true; } ti->private = pt; @@ -2093,7 +2333,8 @@ static int pool_preresume(struct dm_target *ti) int r; struct pool_c *pt = ti->private; struct pool *pool = pt->pool; - dm_block_t data_size, sb_data_size; + sector_t data_size = ti->len; + dm_block_t sb_data_size; /* * Take control of the pool object. @@ -2102,7 +2343,8 @@ static int pool_preresume(struct dm_target *ti) if (r) return r; - data_size = ti->len >> pool->block_shift; + (void) sector_div(data_size, pool->sectors_per_block); + r = dm_pool_get_data_dev_size(pool->pmd, &sb_data_size); if (r) { DMERR("failed to retrieve data device size"); @@ -2111,22 +2353,19 @@ static int pool_preresume(struct dm_target *ti) if (data_size < sb_data_size) { DMERR("pool target too small, is %llu blocks (expected %llu)", - data_size, sb_data_size); + (unsigned long long)data_size, sb_data_size); return -EINVAL; } else if (data_size > sb_data_size) { r = dm_pool_resize_data_dev(pool->pmd, data_size); if (r) { DMERR("failed to resize data device"); + /* FIXME Stricter than necessary: Rollback transaction instead here */ + set_pool_mode(pool, PM_READ_ONLY); return r; } - r = dm_pool_commit_metadata(pool->pmd); - if (r) { - DMERR("%s: dm_pool_commit_metadata() failed, error = %d", - __func__, r); - return r; - } + (void) commit_or_fallback(pool); } return 0; @@ -2149,19 +2388,12 @@ static void pool_resume(struct dm_target *ti) static void pool_postsuspend(struct dm_target *ti) { - int r; struct pool_c *pt = ti->private; struct pool *pool = pt->pool; cancel_delayed_work(&pool->waker); flush_workqueue(pool->wq); - - r = dm_pool_commit_metadata(pool->pmd); - if (r < 0) { - DMERR("%s: dm_pool_commit_metadata() failed, error = %d", - __func__, r); - /* FIXME: invalidate device? error the next FUA or FLUSH bio ?*/ - } + (void) commit_or_fallback(pool); } static int check_arg_count(unsigned argc, unsigned args_required) @@ -2295,12 +2527,7 @@ static int process_reserve_metadata_snap_mesg(unsigned argc, char **argv, struct if (r) return r; - r = dm_pool_commit_metadata(pool->pmd); - if (r) { - DMERR("%s: dm_pool_commit_metadata() failed, error = %d", - __func__, r); - return r; - } + (void) commit_or_fallback(pool); r = dm_pool_reserve_metadata_snap(pool->pmd); if (r) @@ -2361,25 +2588,41 @@ static int pool_message(struct dm_target *ti, unsigned argc, char **argv) else DMWARN("Unrecognised thin pool target message received: %s", argv[0]); - if (!r) { - r = dm_pool_commit_metadata(pool->pmd); - if (r) - DMERR("%s message: dm_pool_commit_metadata() failed, error = %d", - argv[0], r); - } + if (!r) + (void) commit_or_fallback(pool); return r; } +static void emit_flags(struct pool_features *pf, char *result, + unsigned sz, unsigned maxlen) +{ + unsigned count = !pf->zero_new_blocks + !pf->discard_enabled + + !pf->discard_passdown + (pf->mode == PM_READ_ONLY); + DMEMIT("%u ", count); + + if (!pf->zero_new_blocks) + DMEMIT("skip_block_zeroing "); + + if (!pf->discard_enabled) + DMEMIT("ignore_discard "); + + if (!pf->discard_passdown) + DMEMIT("no_discard_passdown "); + + if (pf->mode == PM_READ_ONLY) + DMEMIT("read_only "); +} + /* * Status line is: * <transaction id> <used metadata sectors>/<total metadata sectors> * <used data sectors>/<total data sectors> <held metadata root> */ static int pool_status(struct dm_target *ti, status_type_t type, - char *result, unsigned maxlen) + unsigned status_flags, char *result, unsigned maxlen) { - int r, count; + int r; unsigned sz = 0; uint64_t transaction_id; dm_block_t nr_free_blocks_data; @@ -2394,6 +2637,15 @@ static int pool_status(struct dm_target *ti, status_type_t type, switch (type) { case STATUSTYPE_INFO: + if (get_pool_mode(pool) == PM_FAIL) { + DMEMIT("Fail"); + break; + } + + /* Commit to ensure statistics aren't out-of-date */ + if (!(status_flags & DM_STATUS_NOFLUSH_FLAG) && !dm_suspended(ti)) + (void) commit_or_fallback(pool); + r = dm_pool_get_metadata_transaction_id(pool->pmd, &transaction_id); if (r) @@ -2429,9 +2681,19 @@ static int pool_status(struct dm_target *ti, status_type_t type, (unsigned long long)nr_blocks_data); if (held_root) - DMEMIT("%llu", held_root); + DMEMIT("%llu ", held_root); + else + DMEMIT("- "); + + if (pool->pf.mode == PM_READ_ONLY) + DMEMIT("ro "); + else + DMEMIT("rw "); + + if (pool->pf.discard_enabled && pool->pf.discard_passdown) + DMEMIT("discard_passdown"); else - DMEMIT("-"); + DMEMIT("no_discard_passdown"); break; @@ -2441,20 +2703,7 @@ static int pool_status(struct dm_target *ti, status_type_t type, format_dev_t(buf2, pt->data_dev->bdev->bd_dev), (unsigned long)pool->sectors_per_block, (unsigned long long)pt->low_water_blocks); - - count = !pool->pf.zero_new_blocks + !pool->pf.discard_enabled + - !pt->pf.discard_passdown; - DMEMIT("%u ", count); - - if (!pool->pf.zero_new_blocks) - DMEMIT("skip_block_zeroing "); - - if (!pool->pf.discard_enabled) - DMEMIT("ignore_discard "); - - if (!pt->pf.discard_passdown) - DMEMIT("no_discard_passdown "); - + emit_flags(&pt->pf, result, sz, maxlen); break; } @@ -2492,7 +2741,8 @@ static void set_discard_limits(struct pool *pool, struct queue_limits *limits) /* * This is just a hint, and not enforced. We have to cope with - * bios that overlap 2 blocks. + * bios that cover a block partially. A discard that spans a block + * boundary is not sent to this target. */ limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT; limits->discard_zeroes_data = pool->pf.zero_new_blocks; @@ -2513,7 +2763,7 @@ static struct target_type pool_target = { .name = "thin-pool", .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE | DM_TARGET_IMMUTABLE, - .version = {1, 2, 0}, + .version = {1, 3, 0}, .module = THIS_MODULE, .ctr = pool_ctr, .dtr = pool_dtr, @@ -2618,20 +2868,31 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv) } __pool_inc(tc->pool); + if (get_pool_mode(tc->pool) == PM_FAIL) { + ti->error = "Couldn't open thin device, Pool is in fail mode"; + goto bad_thin_open; + } + r = dm_pool_open_thin_device(tc->pool->pmd, tc->dev_id, &tc->td); if (r) { ti->error = "Couldn't open thin internal device"; goto bad_thin_open; } - ti->split_io = tc->pool->sectors_per_block; + r = dm_set_target_max_io_len(ti, tc->pool->sectors_per_block); + if (r) + goto bad_thin_open; + ti->num_flush_requests = 1; + ti->flush_supported = true; /* In case the pool supports discards, pass them on. */ if (tc->pool->pf.discard_enabled) { - ti->discards_supported = 1; + ti->discards_supported = true; ti->num_discard_requests = 1; - ti->discard_zeroes_data_unsupported = 1; + ti->discard_zeroes_data_unsupported = true; + /* Discard requests must be split on a block boundary */ + ti->split_discard_requests = true; } dm_put(pool_md); @@ -2712,7 +2973,7 @@ static void thin_postsuspend(struct dm_target *ti) * <nr mapped sectors> <highest mapped sector> */ static int thin_status(struct dm_target *ti, status_type_t type, - char *result, unsigned maxlen) + unsigned status_flags, char *result, unsigned maxlen) { int r; ssize_t sz = 0; @@ -2720,6 +2981,11 @@ static int thin_status(struct dm_target *ti, status_type_t type, char buf[BDEVNAME_SIZE]; struct thin_c *tc = ti->private; + if (get_pool_mode(tc->pool) == PM_FAIL) { + DMEMIT("Fail"); + return 0; + } + if (!tc->td) DMEMIT("-"); else { @@ -2757,19 +3023,21 @@ static int thin_status(struct dm_target *ti, status_type_t type, static int thin_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) { - dm_block_t blocks; + sector_t blocks; struct thin_c *tc = ti->private; + struct pool *pool = tc->pool; /* * We can't call dm_pool_get_data_dev_size() since that blocks. So * we follow a more convoluted path through to the pool's target. */ - if (!tc->pool->ti) + if (!pool->ti) return 0; /* nothing is bound */ - blocks = tc->pool->ti->len >> tc->pool->block_shift; + blocks = pool->ti->len; + (void) sector_div(blocks, pool->sectors_per_block); if (blocks) - return fn(ti, tc->pool_dev, 0, tc->pool->sectors_per_block * blocks, data); + return fn(ti, tc->pool_dev, 0, pool->sectors_per_block * blocks, data); return 0; } @@ -2786,7 +3054,7 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits) static struct target_type thin_target = { .name = "thin", - .version = {1, 1, 0}, + .version = {1, 3, 0}, .module = THIS_MODULE, .ctr = thin_ctr, .dtr = thin_dtr, diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c index fa365d39b612..254d19268ad2 100644 --- a/drivers/md/dm-verity.c +++ b/drivers/md/dm-verity.c @@ -515,7 +515,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio, * Status: V (valid) or C (corruption found) */ static int verity_status(struct dm_target *ti, status_type_t type, - char *result, unsigned maxlen) + unsigned status_flags, char *result, unsigned maxlen) { struct dm_verity *v = ti->private; unsigned sz = 0; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index e24143cc2040..4e09b6ff5b49 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -968,22 +968,41 @@ static sector_t max_io_len_target_boundary(sector_t sector, struct dm_target *ti static sector_t max_io_len(sector_t sector, struct dm_target *ti) { sector_t len = max_io_len_target_boundary(sector, ti); + sector_t offset, max_len; /* - * Does the target need to split even further ? + * Does the target need to split even further? */ - if (ti->split_io) { - sector_t boundary; - sector_t offset = dm_target_offset(ti, sector); - boundary = ((offset + ti->split_io) & ~(ti->split_io - 1)) - - offset; - if (len > boundary) - len = boundary; + if (ti->max_io_len) { + offset = dm_target_offset(ti, sector); + if (unlikely(ti->max_io_len & (ti->max_io_len - 1))) + max_len = sector_div(offset, ti->max_io_len); + else + max_len = offset & (ti->max_io_len - 1); + max_len = ti->max_io_len - max_len; + + if (len > max_len) + len = max_len; } return len; } +int dm_set_target_max_io_len(struct dm_target *ti, sector_t len) +{ + if (len > UINT_MAX) { + DMERR("Specified maximum size of target IO (%llu) exceeds limit (%u)", + (unsigned long long)len, UINT_MAX); + ti->error = "Maximum size of target IO is too large"; + return -EINVAL; + } + + ti->max_io_len = (uint32_t) len; + + return 0; +} +EXPORT_SYMBOL_GPL(dm_set_target_max_io_len); + static void __map_bio(struct dm_target *ti, struct bio *clone, struct dm_target_io *tio) { @@ -1196,7 +1215,10 @@ static int __clone_and_map_discard(struct clone_info *ci) if (!ti->num_discard_requests) return -EOPNOTSUPP; - len = min(ci->sector_count, max_io_len_target_boundary(ci->sector, ti)); + if (!ti->split_discard_requests) + len = min(ci->sector_count, max_io_len_target_boundary(ci->sector, ti)); + else + len = min(ci->sector_count, max_io_len(ci->sector, ti)); __issue_target_requests(ci, ti, ti->num_discard_requests, len); diff --git a/drivers/md/dm.h b/drivers/md/dm.h index b7dacd59d8d7..52eef493d266 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -23,6 +23,11 @@ #define DM_SUSPEND_NOFLUSH_FLAG (1 << 1) /* + * Status feature flags + */ +#define DM_STATUS_NOFLUSH_FLAG (1 << 0) + +/* * Type of table and mapped_device's mempool */ #define DM_TYPE_NONE 0 diff --git a/drivers/md/persistent-data/Makefile b/drivers/md/persistent-data/Makefile index cfa95f662230..d8e7cb767c1e 100644 --- a/drivers/md/persistent-data/Makefile +++ b/drivers/md/persistent-data/Makefile @@ -1,7 +1,6 @@ obj-$(CONFIG_DM_PERSISTENT_DATA) += dm-persistent-data.o dm-persistent-data-objs := \ dm-block-manager.o \ - dm-space-map-checker.o \ dm-space-map-common.o \ dm-space-map-disk.o \ dm-space-map-metadata.o \ diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c index 0317ecdc6e53..5ba277768d99 100644 --- a/drivers/md/persistent-data/dm-block-manager.c +++ b/drivers/md/persistent-data/dm-block-manager.c @@ -325,11 +325,6 @@ static struct dm_buffer *to_buffer(struct dm_block *b) return (struct dm_buffer *) b; } -static struct dm_bufio_client *to_bufio(struct dm_block_manager *bm) -{ - return (struct dm_bufio_client *) bm; -} - dm_block_t dm_block_location(struct dm_block *b) { return dm_bufio_get_block_number(to_buffer(b)); @@ -367,34 +362,60 @@ static void dm_block_manager_write_callback(struct dm_buffer *buf) /*---------------------------------------------------------------- * Public interface *--------------------------------------------------------------*/ +struct dm_block_manager { + struct dm_bufio_client *bufio; + bool read_only:1; +}; + struct dm_block_manager *dm_block_manager_create(struct block_device *bdev, unsigned block_size, unsigned cache_size, unsigned max_held_per_thread) { - return (struct dm_block_manager *) - dm_bufio_client_create(bdev, block_size, max_held_per_thread, - sizeof(struct buffer_aux), - dm_block_manager_alloc_callback, - dm_block_manager_write_callback); + int r; + struct dm_block_manager *bm; + + bm = kmalloc(sizeof(*bm), GFP_KERNEL); + if (!bm) { + r = -ENOMEM; + goto bad; + } + + bm->bufio = dm_bufio_client_create(bdev, block_size, max_held_per_thread, + sizeof(struct buffer_aux), + dm_block_manager_alloc_callback, + dm_block_manager_write_callback); + if (IS_ERR(bm->bufio)) { + r = PTR_ERR(bm->bufio); + kfree(bm); + goto bad; + } + + bm->read_only = false; + + return bm; + +bad: + return ERR_PTR(r); } EXPORT_SYMBOL_GPL(dm_block_manager_create); void dm_block_manager_destroy(struct dm_block_manager *bm) { - return dm_bufio_client_destroy(to_bufio(bm)); + dm_bufio_client_destroy(bm->bufio); + kfree(bm); } EXPORT_SYMBOL_GPL(dm_block_manager_destroy); unsigned dm_bm_block_size(struct dm_block_manager *bm) { - return dm_bufio_get_block_size(to_bufio(bm)); + return dm_bufio_get_block_size(bm->bufio); } EXPORT_SYMBOL_GPL(dm_bm_block_size); dm_block_t dm_bm_nr_blocks(struct dm_block_manager *bm) { - return dm_bufio_get_device_size(to_bufio(bm)); + return dm_bufio_get_device_size(bm->bufio); } static int dm_bm_validate_buffer(struct dm_block_manager *bm, @@ -406,7 +427,7 @@ static int dm_bm_validate_buffer(struct dm_block_manager *bm, int r; if (!v) return 0; - r = v->check(v, (struct dm_block *) buf, dm_bufio_get_block_size(to_bufio(bm))); + r = v->check(v, (struct dm_block *) buf, dm_bufio_get_block_size(bm->bufio)); if (unlikely(r)) return r; aux->validator = v; @@ -430,7 +451,7 @@ int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b, void *p; int r; - p = dm_bufio_read(to_bufio(bm), b, (struct dm_buffer **) result); + p = dm_bufio_read(bm->bufio, b, (struct dm_buffer **) result); if (unlikely(IS_ERR(p))) return PTR_ERR(p); @@ -463,7 +484,10 @@ int dm_bm_write_lock(struct dm_block_manager *bm, void *p; int r; - p = dm_bufio_read(to_bufio(bm), b, (struct dm_buffer **) result); + if (bm->read_only) + return -EPERM; + + p = dm_bufio_read(bm->bufio, b, (struct dm_buffer **) result); if (unlikely(IS_ERR(p))) return PTR_ERR(p); @@ -496,7 +520,7 @@ int dm_bm_read_try_lock(struct dm_block_manager *bm, void *p; int r; - p = dm_bufio_get(to_bufio(bm), b, (struct dm_buffer **) result); + p = dm_bufio_get(bm->bufio, b, (struct dm_buffer **) result); if (unlikely(IS_ERR(p))) return PTR_ERR(p); if (unlikely(!p)) @@ -529,7 +553,10 @@ int dm_bm_write_lock_zero(struct dm_block_manager *bm, struct buffer_aux *aux; void *p; - p = dm_bufio_new(to_bufio(bm), b, (struct dm_buffer **) result); + if (bm->read_only) + return -EPERM; + + p = dm_bufio_new(bm->bufio, b, (struct dm_buffer **) result); if (unlikely(IS_ERR(p))) return PTR_ERR(p); @@ -547,6 +574,7 @@ int dm_bm_write_lock_zero(struct dm_block_manager *bm, return 0; } +EXPORT_SYMBOL_GPL(dm_bm_write_lock_zero); int dm_bm_unlock(struct dm_block *b) { @@ -565,45 +593,30 @@ int dm_bm_unlock(struct dm_block *b) } EXPORT_SYMBOL_GPL(dm_bm_unlock); -int dm_bm_unlock_move(struct dm_block *b, dm_block_t n) -{ - struct buffer_aux *aux; - - aux = dm_bufio_get_aux_data(to_buffer(b)); - - if (aux->write_locked) { - dm_bufio_mark_buffer_dirty(to_buffer(b)); - bl_up_write(&aux->lock); - } else - bl_up_read(&aux->lock); - - dm_bufio_release_move(to_buffer(b), n); - return 0; -} - int dm_bm_flush_and_unlock(struct dm_block_manager *bm, struct dm_block *superblock) { int r; - r = dm_bufio_write_dirty_buffers(to_bufio(bm)); - if (unlikely(r)) - return r; - r = dm_bufio_issue_flush(to_bufio(bm)); - if (unlikely(r)) + if (bm->read_only) + return -EPERM; + + r = dm_bufio_write_dirty_buffers(bm->bufio); + if (unlikely(r)) { + dm_bm_unlock(superblock); return r; + } dm_bm_unlock(superblock); - r = dm_bufio_write_dirty_buffers(to_bufio(bm)); - if (unlikely(r)) - return r; - r = dm_bufio_issue_flush(to_bufio(bm)); - if (unlikely(r)) - return r; + return dm_bufio_write_dirty_buffers(bm->bufio); +} - return 0; +void dm_bm_set_read_only(struct dm_block_manager *bm) +{ + bm->read_only = true; } +EXPORT_SYMBOL_GPL(dm_bm_set_read_only); u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor) { diff --git a/drivers/md/persistent-data/dm-block-manager.h b/drivers/md/persistent-data/dm-block-manager.h index 924833d2dfa6..be5bff61be28 100644 --- a/drivers/md/persistent-data/dm-block-manager.h +++ b/drivers/md/persistent-data/dm-block-manager.h @@ -97,14 +97,6 @@ int dm_bm_write_lock_zero(struct dm_block_manager *bm, dm_block_t b, int dm_bm_unlock(struct dm_block *b); /* - * An optimisation; we often want to copy a block's contents to a new - * block. eg, as part of the shadowing operation. It's far better for - * bufio to do this move behind the scenes than hold 2 locks and memcpy the - * data. - */ -int dm_bm_unlock_move(struct dm_block *b, dm_block_t n); - -/* * It's a common idiom to have a superblock that should be committed last. * * @superblock should be write-locked on entry. It will be unlocked during @@ -116,6 +108,19 @@ int dm_bm_unlock_move(struct dm_block *b, dm_block_t n); int dm_bm_flush_and_unlock(struct dm_block_manager *bm, struct dm_block *superblock); +/* + * Switches the bm to a read only mode. Once read-only mode + * has been entered the following functions will return -EPERM. + * + * dm_bm_write_lock + * dm_bm_write_lock_zero + * dm_bm_flush_and_unlock + * + * Additionally you should not use dm_bm_unlock_move, however no error will + * be returned if you do. + */ +void dm_bm_set_read_only(struct dm_block_manager *bm); + u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor); /*----------------------------------------------------------------*/ diff --git a/drivers/md/persistent-data/dm-space-map-checker.c b/drivers/md/persistent-data/dm-space-map-checker.c deleted file mode 100644 index fc90c11620ad..000000000000 --- a/drivers/md/persistent-data/dm-space-map-checker.c +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright (C) 2011 Red Hat, Inc. - * - * This file is released under the GPL. - */ - -#include "dm-space-map-checker.h" - -#include <linux/device-mapper.h> -#include <linux/export.h> -#include <linux/vmalloc.h> - -#ifdef CONFIG_DM_DEBUG_SPACE_MAPS - -#define DM_MSG_PREFIX "space map checker" - -/*----------------------------------------------------------------*/ - -struct count_array { - dm_block_t nr; - dm_block_t nr_free; - - uint32_t *counts; -}; - -static int ca_get_count(struct count_array *ca, dm_block_t b, uint32_t *count) -{ - if (b >= ca->nr) - return -EINVAL; - - *count = ca->counts[b]; - return 0; -} - -static int ca_count_more_than_one(struct count_array *ca, dm_block_t b, int *r) -{ - if (b >= ca->nr) - return -EINVAL; - - *r = ca->counts[b] > 1; - return 0; -} - -static int ca_set_count(struct count_array *ca, dm_block_t b, uint32_t count) -{ - uint32_t old_count; - - if (b >= ca->nr) - return -EINVAL; - - old_count = ca->counts[b]; - - if (!count && old_count) - ca->nr_free++; - - else if (count && !old_count) - ca->nr_free--; - - ca->counts[b] = count; - return 0; -} - -static int ca_inc_block(struct count_array *ca, dm_block_t b) -{ - if (b >= ca->nr) - return -EINVAL; - - ca_set_count(ca, b, ca->counts[b] + 1); - return 0; -} - -static int ca_dec_block(struct count_array *ca, dm_block_t b) -{ - if (b >= ca->nr) - return -EINVAL; - - BUG_ON(ca->counts[b] == 0); - ca_set_count(ca, b, ca->counts[b] - 1); - return 0; -} - -static int ca_create(struct count_array *ca, struct dm_space_map *sm) -{ - int r; - dm_block_t nr_blocks; - - r = dm_sm_get_nr_blocks(sm, &nr_blocks); - if (r) - return r; - - ca->nr = nr_blocks; - ca->nr_free = nr_blocks; - - if (!nr_blocks) - ca->counts = NULL; - else { - ca->counts = vzalloc(sizeof(*ca->counts) * nr_blocks); - if (!ca->counts) - return -ENOMEM; - } - - return 0; -} - -static void ca_destroy(struct count_array *ca) -{ - vfree(ca->counts); -} - -static int ca_load(struct count_array *ca, struct dm_space_map *sm) -{ - int r; - uint32_t count; - dm_block_t nr_blocks, i; - - r = dm_sm_get_nr_blocks(sm, &nr_blocks); - if (r) - return r; - - BUG_ON(ca->nr != nr_blocks); - - DMWARN("Loading debug space map from disk. This may take some time"); - for (i = 0; i < nr_blocks; i++) { - r = dm_sm_get_count(sm, i, &count); - if (r) { - DMERR("load failed"); - return r; - } - - ca_set_count(ca, i, count); - } - DMWARN("Load complete"); - - return 0; -} - -static int ca_extend(struct count_array *ca, dm_block_t extra_blocks) -{ - dm_block_t nr_blocks = ca->nr + extra_blocks; - uint32_t *counts = vzalloc(sizeof(*counts) * nr_blocks); - if (!counts) - return -ENOMEM; - - if (ca->counts) { - memcpy(counts, ca->counts, sizeof(*counts) * ca->nr); - ca_destroy(ca); - } - ca->nr = nr_blocks; - ca->nr_free += extra_blocks; - ca->counts = counts; - return 0; -} - -static int ca_commit(struct count_array *old, struct count_array *new) -{ - if (old->nr != new->nr) { - BUG_ON(old->nr > new->nr); - ca_extend(old, new->nr - old->nr); - } - - BUG_ON(old->nr != new->nr); - old->nr_free = new->nr_free; - memcpy(old->counts, new->counts, sizeof(*old->counts) * old->nr); - return 0; -} - -/*----------------------------------------------------------------*/ - -struct sm_checker { - struct dm_space_map sm; - - struct count_array old_counts; - struct count_array counts; - - struct dm_space_map *real_sm; -}; - -static void sm_checker_destroy(struct dm_space_map *sm) -{ - struct sm_checker *smc = container_of(sm, struct sm_checker, sm); - - dm_sm_destroy(smc->real_sm); - ca_destroy(&smc->old_counts); - ca_destroy(&smc->counts); - kfree(smc); -} - -static int sm_checker_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count) -{ - struct sm_checker *smc = container_of(sm, struct sm_checker, sm); - int r = dm_sm_get_nr_blocks(smc->real_sm, count); - if (!r) - BUG_ON(smc->old_counts.nr != *count); - return r; -} - -static int sm_checker_get_nr_free(struct dm_space_map *sm, dm_block_t *count) -{ - struct sm_checker *smc = container_of(sm, struct sm_checker, sm); - int r = dm_sm_get_nr_free(smc->real_sm, count); - if (!r) { - /* - * Slow, but we know it's correct. - */ - dm_block_t b, n = 0; - for (b = 0; b < smc->old_counts.nr; b++) - if (smc->old_counts.counts[b] == 0 && - smc->counts.counts[b] == 0) - n++; - - if (n != *count) - DMERR("free block counts differ, checker %u, sm-disk:%u", - (unsigned) n, (unsigned) *count); - } - return r; -} - -static int sm_checker_new_block(struct dm_space_map *sm, dm_block_t *b) -{ - struct sm_checker *smc = container_of(sm, struct sm_checker, sm); - int r = dm_sm_new_block(smc->real_sm, b); - - if (!r) { - BUG_ON(*b >= smc->old_counts.nr); - BUG_ON(smc->old_counts.counts[*b] != 0); - BUG_ON(*b >= smc->counts.nr); - BUG_ON(smc->counts.counts[*b] != 0); - ca_set_count(&smc->counts, *b, 1); - } - - return r; -} - -static int sm_checker_inc_block(struct dm_space_map *sm, dm_block_t b) -{ - struct sm_checker *smc = container_of(sm, struct sm_checker, sm); - int r = dm_sm_inc_block(smc->real_sm, b); - int r2 = ca_inc_block(&smc->counts, b); - BUG_ON(r != r2); - return r; -} - -static int sm_checker_dec_block(struct dm_space_map *sm, dm_block_t b) -{ - struct sm_checker *smc = container_of(sm, struct sm_checker, sm); - int r = dm_sm_dec_block(smc->real_sm, b); - int r2 = ca_dec_block(&smc->counts, b); - BUG_ON(r != r2); - return r; -} - -static int sm_checker_get_count(struct dm_space_map *sm, dm_block_t b, uint32_t *result) -{ - struct sm_checker *smc = container_of(sm, struct sm_checker, sm); - uint32_t result2 = 0; - int r = dm_sm_get_count(smc->real_sm, b, result); - int r2 = ca_get_count(&smc->counts, b, &result2); - - BUG_ON(r != r2); - if (!r) - BUG_ON(*result != result2); - return r; -} - -static int sm_checker_count_more_than_one(struct dm_space_map *sm, dm_block_t b, int *result) -{ - struct sm_checker *smc = container_of(sm, struct sm_checker, sm); - int result2 = 0; - int r = dm_sm_count_is_more_than_one(smc->real_sm, b, result); - int r2 = ca_count_more_than_one(&smc->counts, b, &result2); - - BUG_ON(r != r2); - if (!r) - BUG_ON(!(*result) && result2); - return r; -} - -static int sm_checker_set_count(struct dm_space_map *sm, dm_block_t b, uint32_t count) -{ - struct sm_checker *smc = container_of(sm, struct sm_checker, sm); - uint32_t old_rc; - int r = dm_sm_set_count(smc->real_sm, b, count); - int r2; - - BUG_ON(b >= smc->counts.nr); - old_rc = smc->counts.counts[b]; - r2 = ca_set_count(&smc->counts, b, count); - BUG_ON(r != r2); - - return r; -} - -static int sm_checker_commit(struct dm_space_map *sm) -{ - struct sm_checker *smc = container_of(sm, struct sm_checker, sm); - int r; - - r = dm_sm_commit(smc->real_sm); - if (r) - return r; - - r = ca_commit(&smc->old_counts, &smc->counts); - if (r) - return r; - - return 0; -} - -static int sm_checker_extend(struct dm_space_map *sm, dm_block_t extra_blocks) -{ - struct sm_checker *smc = container_of(sm, struct sm_checker, sm); - int r = dm_sm_extend(smc->real_sm, extra_blocks); - if (r) - return r; - - return ca_extend(&smc->counts, extra_blocks); -} - -static int sm_checker_root_size(struct dm_space_map *sm, size_t *result) -{ - struct sm_checker *smc = container_of(sm, struct sm_checker, sm); - return dm_sm_root_size(smc->real_sm, result); -} - -static int sm_checker_copy_root(struct dm_space_map *sm, void *copy_to_here_le, size_t len) -{ - struct sm_checker *smc = container_of(sm, struct sm_checker, sm); - return dm_sm_copy_root(smc->real_sm, copy_to_here_le, len); -} - -/*----------------------------------------------------------------*/ - -static struct dm_space_map ops_ = { - .destroy = sm_checker_destroy, - .get_nr_blocks = sm_checker_get_nr_blocks, - .get_nr_free = sm_checker_get_nr_free, - .inc_block = sm_checker_inc_block, - .dec_block = sm_checker_dec_block, - .new_block = sm_checker_new_block, - .get_count = sm_checker_get_count, - .count_is_more_than_one = sm_checker_count_more_than_one, - .set_count = sm_checker_set_count, - .commit = sm_checker_commit, - .extend = sm_checker_extend, - .root_size = sm_checker_root_size, - .copy_root = sm_checker_copy_root -}; - -struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm) -{ - int r; - struct sm_checker *smc; - - if (IS_ERR_OR_NULL(sm)) - return ERR_PTR(-EINVAL); - - smc = kmalloc(sizeof(*smc), GFP_KERNEL); - if (!smc) - return ERR_PTR(-ENOMEM); - - memcpy(&smc->sm, &ops_, sizeof(smc->sm)); - r = ca_create(&smc->old_counts, sm); - if (r) { - kfree(smc); - return ERR_PTR(r); - } - - r = ca_create(&smc->counts, sm); - if (r) { - ca_destroy(&smc->old_counts); - kfree(smc); - return ERR_PTR(r); - } - - smc->real_sm = sm; - - r = ca_load(&smc->counts, sm); - if (r) { - ca_destroy(&smc->counts); - ca_destroy(&smc->old_counts); - kfree(smc); - return ERR_PTR(r); - } - - r = ca_commit(&smc->old_counts, &smc->counts); - if (r) { - ca_destroy(&smc->counts); - ca_destroy(&smc->old_counts); - kfree(smc); - return ERR_PTR(r); - } - - return &smc->sm; -} -EXPORT_SYMBOL_GPL(dm_sm_checker_create); - -struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm) -{ - int r; - struct sm_checker *smc; - - if (IS_ERR_OR_NULL(sm)) - return ERR_PTR(-EINVAL); - - smc = kmalloc(sizeof(*smc), GFP_KERNEL); - if (!smc) - return ERR_PTR(-ENOMEM); - - memcpy(&smc->sm, &ops_, sizeof(smc->sm)); - r = ca_create(&smc->old_counts, sm); - if (r) { - kfree(smc); - return ERR_PTR(r); - } - - r = ca_create(&smc->counts, sm); - if (r) { - ca_destroy(&smc->old_counts); - kfree(smc); - return ERR_PTR(r); - } - - smc->real_sm = sm; - return &smc->sm; -} -EXPORT_SYMBOL_GPL(dm_sm_checker_create_fresh); - -/*----------------------------------------------------------------*/ - -#else - -struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm) -{ - return sm; -} -EXPORT_SYMBOL_GPL(dm_sm_checker_create); - -struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm) -{ - return sm; -} -EXPORT_SYMBOL_GPL(dm_sm_checker_create_fresh); - -/*----------------------------------------------------------------*/ - -#endif diff --git a/drivers/md/persistent-data/dm-space-map-checker.h b/drivers/md/persistent-data/dm-space-map-checker.h deleted file mode 100644 index 444dccf6688c..000000000000 --- a/drivers/md/persistent-data/dm-space-map-checker.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2011 Red Hat, Inc. - * - * This file is released under the GPL. - */ - -#ifndef SNAPSHOTS_SPACE_MAP_CHECKER_H -#define SNAPSHOTS_SPACE_MAP_CHECKER_H - -#include "dm-space-map.h" - -/*----------------------------------------------------------------*/ - -/* - * This space map wraps a real on-disk space map, and verifies all of its - * operations. It uses a lot of memory, so only use if you have a specific - * problem that you're debugging. - * - * Ownership of @sm passes. - */ -struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm); -struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm); - -/*----------------------------------------------------------------*/ - -#endif diff --git a/drivers/md/persistent-data/dm-space-map-common.c b/drivers/md/persistent-data/dm-space-map-common.c index ff3beed6ad2d..d77602d63c83 100644 --- a/drivers/md/persistent-data/dm-space-map-common.c +++ b/drivers/md/persistent-data/dm-space-map-common.c @@ -224,6 +224,7 @@ static int sm_ll_init(struct ll_disk *ll, struct dm_transaction_manager *tm) ll->nr_blocks = 0; ll->bitmap_root = 0; ll->ref_count_root = 0; + ll->bitmap_index_changed = false; return 0; } @@ -476,7 +477,15 @@ int sm_ll_dec(struct ll_disk *ll, dm_block_t b, enum allocation_event *ev) int sm_ll_commit(struct ll_disk *ll) { - return ll->commit(ll); + int r = 0; + + if (ll->bitmap_index_changed) { + r = ll->commit(ll); + if (!r) + ll->bitmap_index_changed = false; + } + + return r; } /*----------------------------------------------------------------*/ @@ -491,6 +500,7 @@ static int metadata_ll_load_ie(struct ll_disk *ll, dm_block_t index, static int metadata_ll_save_ie(struct ll_disk *ll, dm_block_t index, struct disk_index_entry *ie) { + ll->bitmap_index_changed = true; memcpy(ll->mi_le.index + index, ie, sizeof(*ie)); return 0; } diff --git a/drivers/md/persistent-data/dm-space-map-common.h b/drivers/md/persistent-data/dm-space-map-common.h index 8f220821a9a9..b3078d5eda0c 100644 --- a/drivers/md/persistent-data/dm-space-map-common.h +++ b/drivers/md/persistent-data/dm-space-map-common.h @@ -78,6 +78,7 @@ struct ll_disk { open_index_fn open_index; max_index_entries_fn max_entries; commit_fn commit; + bool bitmap_index_changed:1; }; struct disk_sm_root { diff --git a/drivers/md/persistent-data/dm-space-map-disk.c b/drivers/md/persistent-data/dm-space-map-disk.c index 3d0ed5332883..f6d29e614ab7 100644 --- a/drivers/md/persistent-data/dm-space-map-disk.c +++ b/drivers/md/persistent-data/dm-space-map-disk.c @@ -4,7 +4,6 @@ * This file is released under the GPL. */ -#include "dm-space-map-checker.h" #include "dm-space-map-common.h" #include "dm-space-map-disk.h" #include "dm-space-map.h" @@ -252,9 +251,8 @@ static struct dm_space_map ops = { .copy_root = sm_disk_copy_root }; -static struct dm_space_map *dm_sm_disk_create_real( - struct dm_transaction_manager *tm, - dm_block_t nr_blocks) +struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm, + dm_block_t nr_blocks) { int r; struct sm_disk *smd; @@ -285,27 +283,10 @@ bad: kfree(smd); return ERR_PTR(r); } - -struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm, - dm_block_t nr_blocks) -{ - struct dm_space_map *sm = dm_sm_disk_create_real(tm, nr_blocks); - struct dm_space_map *smc; - - if (IS_ERR_OR_NULL(sm)) - return sm; - - smc = dm_sm_checker_create_fresh(sm); - if (IS_ERR(smc)) - dm_sm_destroy(sm); - - return smc; -} EXPORT_SYMBOL_GPL(dm_sm_disk_create); -static struct dm_space_map *dm_sm_disk_open_real( - struct dm_transaction_manager *tm, - void *root_le, size_t len) +struct dm_space_map *dm_sm_disk_open(struct dm_transaction_manager *tm, + void *root_le, size_t len) { int r; struct sm_disk *smd; @@ -332,13 +313,6 @@ bad: kfree(smd); return ERR_PTR(r); } - -struct dm_space_map *dm_sm_disk_open(struct dm_transaction_manager *tm, - void *root_le, size_t len) -{ - return dm_sm_checker_create( - dm_sm_disk_open_real(tm, root_le, len)); -} EXPORT_SYMBOL_GPL(dm_sm_disk_open); /*----------------------------------------------------------------*/ diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c index e5604b32d91f..d247a35da3c6 100644 --- a/drivers/md/persistent-data/dm-transaction-manager.c +++ b/drivers/md/persistent-data/dm-transaction-manager.c @@ -5,7 +5,6 @@ */ #include "dm-transaction-manager.h" #include "dm-space-map.h" -#include "dm-space-map-checker.h" #include "dm-space-map-disk.h" #include "dm-space-map-metadata.h" #include "dm-persistent-data-internal.h" @@ -220,13 +219,24 @@ static int __shadow_block(struct dm_transaction_manager *tm, dm_block_t orig, if (r < 0) return r; - r = dm_bm_unlock_move(orig_block, new); - if (r < 0) { + /* + * It would be tempting to use dm_bm_unlock_move here, but some + * code, such as the space maps, keeps using the old data structures + * secure in the knowledge they won't be changed until the next + * transaction. Using unlock_move would force a synchronous read + * since the old block would no longer be in the cache. + */ + r = dm_bm_write_lock_zero(tm->bm, new, v, result); + if (r) { dm_bm_unlock(orig_block); return r; } - return dm_bm_write_lock(tm->bm, new, v, result); + memcpy(dm_block_data(*result), dm_block_data(orig_block), + dm_bm_block_size(tm->bm)); + + dm_bm_unlock(orig_block); + return r; } int dm_tm_shadow_block(struct dm_transaction_manager *tm, dm_block_t orig, @@ -311,98 +321,61 @@ struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm) static int dm_tm_create_internal(struct dm_block_manager *bm, dm_block_t sb_location, - struct dm_block_validator *sb_validator, - size_t root_offset, size_t root_max_len, struct dm_transaction_manager **tm, struct dm_space_map **sm, - struct dm_block **sblock, - int create) + int create, + void *sm_root, size_t sm_len) { int r; - struct dm_space_map *inner; - inner = dm_sm_metadata_init(); - if (IS_ERR(inner)) - return PTR_ERR(inner); + *sm = dm_sm_metadata_init(); + if (IS_ERR(*sm)) + return PTR_ERR(*sm); - *tm = dm_tm_create(bm, inner); + *tm = dm_tm_create(bm, *sm); if (IS_ERR(*tm)) { - dm_sm_destroy(inner); + dm_sm_destroy(*sm); return PTR_ERR(*tm); } if (create) { - r = dm_bm_write_lock_zero(dm_tm_get_bm(*tm), sb_location, - sb_validator, sblock); - if (r < 0) { - DMERR("couldn't lock superblock"); - goto bad1; - } - - r = dm_sm_metadata_create(inner, *tm, dm_bm_nr_blocks(bm), + r = dm_sm_metadata_create(*sm, *tm, dm_bm_nr_blocks(bm), sb_location); if (r) { DMERR("couldn't create metadata space map"); - goto bad2; - } - - *sm = dm_sm_checker_create(inner); - if (IS_ERR(*sm)) { - r = PTR_ERR(*sm); - goto bad2; + goto bad; } } else { - r = dm_bm_write_lock(dm_tm_get_bm(*tm), sb_location, - sb_validator, sblock); - if (r < 0) { - DMERR("couldn't lock superblock"); - goto bad1; - } - - r = dm_sm_metadata_open(inner, *tm, - dm_block_data(*sblock) + root_offset, - root_max_len); + r = dm_sm_metadata_open(*sm, *tm, sm_root, sm_len); if (r) { DMERR("couldn't open metadata space map"); - goto bad2; - } - - *sm = dm_sm_checker_create(inner); - if (IS_ERR(*sm)) { - r = PTR_ERR(*sm); - goto bad2; + goto bad; } } return 0; -bad2: - dm_tm_unlock(*tm, *sblock); -bad1: +bad: dm_tm_destroy(*tm); - dm_sm_destroy(inner); + dm_sm_destroy(*sm); return r; } int dm_tm_create_with_sm(struct dm_block_manager *bm, dm_block_t sb_location, - struct dm_block_validator *sb_validator, struct dm_transaction_manager **tm, - struct dm_space_map **sm, struct dm_block **sblock) + struct dm_space_map **sm) { - return dm_tm_create_internal(bm, sb_location, sb_validator, - 0, 0, tm, sm, sblock, 1); + return dm_tm_create_internal(bm, sb_location, tm, sm, 1, NULL, 0); } EXPORT_SYMBOL_GPL(dm_tm_create_with_sm); int dm_tm_open_with_sm(struct dm_block_manager *bm, dm_block_t sb_location, - struct dm_block_validator *sb_validator, - size_t root_offset, size_t root_max_len, + void *sm_root, size_t root_len, struct dm_transaction_manager **tm, - struct dm_space_map **sm, struct dm_block **sblock) + struct dm_space_map **sm) { - return dm_tm_create_internal(bm, sb_location, sb_validator, root_offset, - root_max_len, tm, sm, sblock, 0); + return dm_tm_create_internal(bm, sb_location, tm, sm, 0, sm_root, root_len); } EXPORT_SYMBOL_GPL(dm_tm_open_with_sm); diff --git a/drivers/md/persistent-data/dm-transaction-manager.h b/drivers/md/persistent-data/dm-transaction-manager.h index 6da784871db4..b5b139076ca5 100644 --- a/drivers/md/persistent-data/dm-transaction-manager.h +++ b/drivers/md/persistent-data/dm-transaction-manager.h @@ -115,16 +115,17 @@ struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm); * * Returns a tm that has an open transaction to write the new disk sm. * Caller should store the new sm root and commit. + * + * The superblock location is passed so the metadata space map knows it + * shouldn't be used. */ int dm_tm_create_with_sm(struct dm_block_manager *bm, dm_block_t sb_location, - struct dm_block_validator *sb_validator, struct dm_transaction_manager **tm, - struct dm_space_map **sm, struct dm_block **sblock); + struct dm_space_map **sm); int dm_tm_open_with_sm(struct dm_block_manager *bm, dm_block_t sb_location, - struct dm_block_validator *sb_validator, - size_t root_offset, size_t root_max_len, + void *sm_root, size_t root_len, struct dm_transaction_manager **tm, - struct dm_space_map **sm, struct dm_block **sblock); + struct dm_space_map **sm); #endif /* _LINUX_DM_TRANSACTION_MANAGER_H */ diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 9575db429df4..d941581ab921 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -6,20 +6,82 @@ menuconfig MEDIA_SUPPORT tristate "Multimedia support" depends on HAS_IOMEM help - If you want to use Video for Linux, DVB for Linux, or DAB adapters, + If you want to use Webcams, Video grabber devices and/or TV devices enable this option and other options below. + Additional info and docs are available on the web at + <http://linuxtv.org> if MEDIA_SUPPORT comment "Multimedia core support" # +# Multimedia support - automatically enable V4L2 and DVB core +# +config MEDIA_CAMERA_SUPPORT + bool "Cameras/video grabbers support" + ---help--- + Enable support for webcams and video grabbers. + + Say Y when you have a webcam or a video capture grabber board. + +config MEDIA_ANALOG_TV_SUPPORT + bool "Analog TV support" + ---help--- + Enable analog TV support. + + Say Y when you have a TV board with analog support or with a + hybrid analog/digital TV chipset. + + Note: There are several DVB cards that are based on chips that + support both analog and digital TV. Disabling this option + will disable support for them. + +config MEDIA_DIGITAL_TV_SUPPORT + bool "Digital TV support" + ---help--- + Enable digital TV support. + + Say Y when you have a board with digital support or a board with + hybrid digital TV and analog TV. + +config MEDIA_RADIO_SUPPORT + bool "AM/FM radio receivers/transmitters support" + ---help--- + Enable AM/FM radio support. + + Additional info and docs are available on the web at + <http://linuxtv.org> + + Say Y when you have a board with radio support. + + Note: There are several TV cards that are based on chips that + support radio reception. Disabling this option will + disable support for them. + +config MEDIA_RC_SUPPORT + bool "Remote Controller support" + depends on INPUT + ---help--- + Enable support for Remote Controllers on Linux. This is + needed in order to support several video capture adapters, + standalone IR receivers/transmitters, and RF receivers. + + Enable this option if you have a video capture board even + if you don't need IR, as otherwise, you may not be able to + compile the driver for your adapter. + + Say Y when you have a TV or an IR device. + +# # Media controller +# Selectable only for webcam/grabbers, as other drivers don't use it # config MEDIA_CONTROLLER bool "Media Controller API (EXPERIMENTAL)" depends on EXPERIMENTAL + depends on MEDIA_CAMERA_SUPPORT ---help--- Enable the media controller API used to query media devices internal topology and configure it dynamically. @@ -27,26 +89,15 @@ config MEDIA_CONTROLLER This API is mostly used by camera interfaces in embedded platforms. # -# V4L core and enabled API's +# Video4Linux support +# Only enables if one of the V4L2 types (ATV, webcam, radio) is selected # config VIDEO_DEV - tristate "Video For Linux" - ---help--- - V4L core support for video capture and overlay devices, webcams and - AM/FM radio cards. - - This kernel includes support for the new Video for Linux Two API, - (V4L2). - - Additional info and docs are available on the web at - <http://linuxtv.org> - - Documentation for V4L2 is also available on the web at - <http://bytesex.org/v4l/>. - - To compile this driver as a module, choose M here: the - module will be called videodev. + tristate + depends on MEDIA_SUPPORT + depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT + default y config VIDEO_V4L2_COMMON tristate @@ -64,25 +115,15 @@ config VIDEO_V4L2_SUBDEV_API # # DVB Core +# Only enables if one of DTV is selected # config DVB_CORE - tristate "DVB for Linux" + tristate + depends on MEDIA_SUPPORT + depends on MEDIA_DIGITAL_TV_SUPPORT + default y select CRC32 - help - DVB core utility functions for device handling, software fallbacks etc. - - Enable this if you own a DVB/ATSC adapter and want to use it or if - you compile Linux for a digital SetTopBox. - - Say Y when you have a DVB or an ATSC card and want to use it. - - API specs and user tools are available from <http://www.linuxtv.org/>. - - Please report problems regarding this support to the LinuxDVB - mailing list. - - If unsure say N. config DVB_NET bool "DVB Network Support" @@ -97,12 +138,7 @@ config DVB_NET You may want to disable the network support on embedded devices. If unsure say Y. -config VIDEO_MEDIA - tristate - default (DVB_CORE && (VIDEO_DEV = n)) || (VIDEO_DEV && (DVB_CORE = n)) || (DVB_CORE && VIDEO_DEV) - -comment "Multimedia drivers" - +comment "Media drivers" source "drivers/media/common/Kconfig" source "drivers/media/rc/Kconfig" diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index bbf4945149a9..94c6ff7a5da3 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -1,7 +1,8 @@ config MEDIA_ATTACH bool "Load and attach frontend and tuner driver modules as needed" - depends on VIDEO_MEDIA + depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT depends on MODULES + default y if !EXPERT help Remove the static dependency of DVB card drivers on all frontend modules for all possible card variants. Instead, @@ -19,15 +20,15 @@ config MEDIA_ATTACH config MEDIA_TUNER tristate - default VIDEO_MEDIA && I2C - depends on VIDEO_MEDIA && I2C + depends on (MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT) && I2C + default y select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE - select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMISE && EXPERIMENTAL - select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMISE && MEDIA_RADIO_SUPPORT && EXPERIMENTAL + select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMISE && MEDIA_RADIO_SUPPORT select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_TDA9887 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE @@ -47,10 +48,11 @@ config MEDIA_TUNER_CUSTOMISE menu "Customize TV tuners" visible if MEDIA_TUNER_CUSTOMISE + depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT config MEDIA_TUNER_SIMPLE tristate "Simple tuner support" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C select MEDIA_TUNER_TDA9887 default m if MEDIA_TUNER_CUSTOMISE help @@ -58,7 +60,7 @@ config MEDIA_TUNER_SIMPLE config MEDIA_TUNER_TDA8290 tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C select MEDIA_TUNER_TDA827X select MEDIA_TUNER_TDA18271 default m if MEDIA_TUNER_CUSTOMISE @@ -67,21 +69,21 @@ config MEDIA_TUNER_TDA8290 config MEDIA_TUNER_TDA827X tristate "Philips TDA827X silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help A DVB-T silicon tuner module. Say Y when you want to support this tuner. config MEDIA_TUNER_TDA18271 tristate "NXP TDA18271 silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help A silicon tuner module. Say Y when you want to support this tuner. config MEDIA_TUNER_TDA9887 tristate "TDA 9885/6/7 analog IF demodulator" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help Say Y here to include support for Philips TDA9885/6/7 @@ -89,7 +91,7 @@ config MEDIA_TUNER_TDA9887 config MEDIA_TUNER_TEA5761 tristate "TEA 5761 radio tuner (EXPERIMENTAL)" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C depends on EXPERIMENTAL default m if MEDIA_TUNER_CUSTOMISE help @@ -97,63 +99,63 @@ config MEDIA_TUNER_TEA5761 config MEDIA_TUNER_TEA5767 tristate "TEA 5767 radio tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help Say Y here to include support for the Philips TEA5767 radio tuner. config MEDIA_TUNER_MT20XX tristate "Microtune 2032 / 2050 tuners" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help Say Y here to include support for the MT2032 / MT2050 tuner. config MEDIA_TUNER_MT2060 tristate "Microtune MT2060 silicon IF tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help A driver for the silicon IF tuner MT2060 from Microtune. config MEDIA_TUNER_MT2063 tristate "Microtune MT2063 silicon IF tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help A driver for the silicon IF tuner MT2063 from Microtune. config MEDIA_TUNER_MT2266 tristate "Microtune MT2266 silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help A driver for the silicon baseband tuner MT2266 from Microtune. config MEDIA_TUNER_MT2131 tristate "Microtune MT2131 silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help A driver for the silicon baseband tuner MT2131 from Microtune. config MEDIA_TUNER_QT1010 tristate "Quantek QT1010 silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help A driver for the silicon tuner QT1010 from Quantek. config MEDIA_TUNER_XC2028 tristate "XCeive xc2028/xc3028 tuners" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help Say Y here to include support for the xc2028/xc3028 tuners. config MEDIA_TUNER_XC5000 tristate "Xceive XC5000 silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help A driver for the silicon tuner XC5000 from Xceive. @@ -162,7 +164,7 @@ config MEDIA_TUNER_XC5000 config MEDIA_TUNER_XC4000 tristate "Xceive XC4000 silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help A driver for the silicon tuner XC4000 from Xceive. @@ -171,70 +173,70 @@ config MEDIA_TUNER_XC4000 config MEDIA_TUNER_MXL5005S tristate "MaxLinear MSL5005S silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help A driver for the silicon tuner MXL5005S from MaxLinear. config MEDIA_TUNER_MXL5007T tristate "MaxLinear MxL5007T silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help A driver for the silicon tuner MxL5007T from MaxLinear. config MEDIA_TUNER_MC44S803 tristate "Freescale MC44S803 Low Power CMOS Broadband tuners" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help Say Y here to support the Freescale MC44S803 based tuners config MEDIA_TUNER_MAX2165 tristate "Maxim MAX2165 silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help A driver for the silicon tuner MAX2165 from Maxim. config MEDIA_TUNER_TDA18218 tristate "NXP TDA18218 silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help NXP TDA18218 silicon tuner driver. config MEDIA_TUNER_FC0011 tristate "Fitipower FC0011 silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help Fitipower FC0011 silicon tuner driver. config MEDIA_TUNER_FC0012 tristate "Fitipower FC0012 silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help Fitipower FC0012 silicon tuner driver. config MEDIA_TUNER_FC0013 tristate "Fitipower FC0013 silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help Fitipower FC0013 silicon tuner driver. config MEDIA_TUNER_TDA18212 tristate "NXP TDA18212 silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help NXP TDA18212 silicon tuner driver. config MEDIA_TUNER_TUA9001 tristate "Infineon TUA 9001 silicon tuner" - depends on VIDEO_MEDIA && I2C + depends on MEDIA_SUPPORT && I2C default m if MEDIA_TUNER_CUSTOMISE help Infineon TUA 9001 silicon tuner driver. diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index b5ee3ebfcfca..f88f948efee2 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -90,11 +90,22 @@ struct firmware_properties { int scode_nr; }; +enum xc2028_state { + XC2028_NO_FIRMWARE = 0, + XC2028_WAITING_FIRMWARE, + XC2028_ACTIVE, + XC2028_SLEEP, + XC2028_NODEV, +}; + struct xc2028_data { struct list_head hybrid_tuner_instance_list; struct tuner_i2c_props i2c_props; __u32 frequency; + enum xc2028_state state; + const char *fname; + struct firmware_description *firm; int firm_size; __u16 firm_version; @@ -255,6 +266,21 @@ static v4l2_std_id parse_audio_std_option(void) return 0; } +static int check_device_status(struct xc2028_data *priv) +{ + switch (priv->state) { + case XC2028_NO_FIRMWARE: + case XC2028_WAITING_FIRMWARE: + return -EAGAIN; + case XC2028_ACTIVE: + case XC2028_SLEEP: + return 0; + case XC2028_NODEV: + return -ENODEV; + } + return 0; +} + static void free_firmware(struct xc2028_data *priv) { int i; @@ -270,45 +296,28 @@ static void free_firmware(struct xc2028_data *priv) priv->firm = NULL; priv->firm_size = 0; + priv->state = XC2028_NO_FIRMWARE; memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); } -static int load_all_firmwares(struct dvb_frontend *fe) +static int load_all_firmwares(struct dvb_frontend *fe, + const struct firmware *fw) { struct xc2028_data *priv = fe->tuner_priv; - const struct firmware *fw = NULL; const unsigned char *p, *endp; int rc = 0; int n, n_array; char name[33]; - char *fname; tuner_dbg("%s called\n", __func__); - if (!firmware_name[0]) - fname = priv->ctrl.fname; - else - fname = firmware_name; - - tuner_dbg("Reading firmware %s\n", fname); - rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent); - if (rc < 0) { - if (rc == -ENOENT) - tuner_err("Error: firmware %s not found.\n", - fname); - else - tuner_err("Error %d while requesting firmware %s \n", - rc, fname); - - return rc; - } p = fw->data; endp = p + fw->size; if (fw->size < sizeof(name) - 1 + 2 + 2) { tuner_err("Error: firmware file %s has invalid size!\n", - fname); + priv->fname); goto corrupt; } @@ -323,7 +332,7 @@ static int load_all_firmwares(struct dvb_frontend *fe) p += 2; tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n", - n_array, fname, name, + n_array, priv->fname, name, priv->firm_version >> 8, priv->firm_version & 0xff); priv->firm = kcalloc(n_array, sizeof(*priv->firm), GFP_KERNEL); @@ -417,9 +426,10 @@ err: free_firmware(priv); done: - release_firmware(fw); if (rc == 0) tuner_dbg("Firmware files loaded.\n"); + else + priv->state = XC2028_NODEV; return rc; } @@ -707,22 +717,15 @@ static int check_firmware(struct dvb_frontend *fe, unsigned int type, { struct xc2028_data *priv = fe->tuner_priv; struct firmware_properties new_fw; - int rc = 0, retry_count = 0; + int rc, retry_count = 0; u16 version, hwmodel; v4l2_std_id std0; tuner_dbg("%s called\n", __func__); - if (!priv->firm) { - if (!priv->ctrl.fname) { - tuner_info("xc2028/3028 firmware name not set!\n"); - return -EINVAL; - } - - rc = load_all_firmwares(fe); - if (rc < 0) - return rc; - } + rc = check_device_status(priv); + if (rc < 0) + return rc; if (priv->ctrl.mts && !(type & FM)) type |= MTS; @@ -749,9 +752,13 @@ retry: printk("scode_nr %d\n", new_fw.scode_nr); } - /* No need to reload base firmware if it matches */ - if (((BASE | new_fw.type) & BASE_TYPES) == - (priv->cur_fw.type & BASE_TYPES)) { + /* + * No need to reload base firmware if it matches and if the tuner + * is not at sleep mode + */ + if ((priv->state = XC2028_ACTIVE) && + (((BASE | new_fw.type) & BASE_TYPES) == + (priv->cur_fw.type & BASE_TYPES))) { tuner_dbg("BASE firmware not changed.\n"); goto skip_base; } @@ -872,10 +879,13 @@ read_not_reliable: * 2. Tell whether BASE firmware was just changed the next time through. */ priv->cur_fw.type |= BASE; + priv->state = XC2028_ACTIVE; return 0; fail: + priv->state = XC2028_SLEEP; + memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); if (retry_count < 8) { msleep(50); @@ -893,28 +903,39 @@ static int xc2028_signal(struct dvb_frontend *fe, u16 *strength) { struct xc2028_data *priv = fe->tuner_priv; u16 frq_lock, signal = 0; - int rc; + int rc, i; tuner_dbg("%s called\n", __func__); + rc = check_device_status(priv); + if (rc < 0) + return rc; + mutex_lock(&priv->lock); /* Sync Lock Indicator */ - rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock); - if (rc < 0) - goto ret; + for (i = 0; i < 3; i++) { + rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock); + if (rc < 0) + goto ret; - /* Frequency is locked */ - if (frq_lock == 1) - signal = 1 << 11; + if (frq_lock) + break; + msleep(6); + } + + /* Frequency didn't lock */ + if (frq_lock == 2) + goto ret; /* Get SNR of the video signal */ rc = xc2028_get_reg(priv, XREG_SNR, &signal); if (rc < 0) goto ret; - /* Use both frq_lock and signal to generate the result */ - signal = signal || ((signal & 0x07) << 12); + /* Signal level is 3 bits only */ + + signal = ((1 << 12) - 1) | ((signal & 0x07) << 12); ret: mutex_unlock(&priv->lock); @@ -926,6 +947,49 @@ ret: return rc; } +static int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc) +{ + struct xc2028_data *priv = fe->tuner_priv; + int i, rc; + u16 frq_lock = 0; + s16 afc_reg = 0; + + rc = check_device_status(priv); + if (rc < 0) + return rc; + + mutex_lock(&priv->lock); + + /* Sync Lock Indicator */ + for (i = 0; i < 3; i++) { + rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock); + if (rc < 0) + goto ret; + + if (frq_lock) + break; + msleep(6); + } + + /* Frequency didn't lock */ + if (frq_lock == 2) + goto ret; + + /* Get AFC */ + rc = xc2028_get_reg(priv, XREG_FREQ_ERROR, &afc_reg); + if (rc < 0) + return rc; + + *afc = afc_reg * 15625; /* Hz */ + + tuner_dbg("AFC is %d Hz\n", *afc); + +ret: + mutex_unlock(&priv->lock); + + return rc; +} + #define DIV 15625 static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, @@ -1111,11 +1175,16 @@ static int xc2028_set_params(struct dvb_frontend *fe) u32 delsys = c->delivery_system; u32 bw = c->bandwidth_hz; struct xc2028_data *priv = fe->tuner_priv; - unsigned int type=0; + int rc; + unsigned int type = 0; u16 demod = 0; tuner_dbg("%s called\n", __func__); + rc = check_device_status(priv); + if (rc < 0) + return rc; + switch (delsys) { case SYS_DVBT: case SYS_DVBT2: @@ -1201,7 +1270,11 @@ static int xc2028_set_params(struct dvb_frontend *fe) static int xc2028_sleep(struct dvb_frontend *fe) { struct xc2028_data *priv = fe->tuner_priv; - int rc = 0; + int rc; + + rc = check_device_status(priv); + if (rc < 0) + return rc; /* Avoid firmware reload on slow devices or if PM disabled */ if (no_poweroff || priv->ctrl.disable_power_mgmt) @@ -1220,7 +1293,7 @@ static int xc2028_sleep(struct dvb_frontend *fe) else rc = send_seq(priv, {0x80, XREG_POWER_DOWN, 0x00, 0x00}); - priv->cur_fw.type = 0; /* need firmware reload */ + priv->state = XC2028_SLEEP; mutex_unlock(&priv->lock); @@ -1237,8 +1310,9 @@ static int xc2028_dvb_release(struct dvb_frontend *fe) /* only perform final cleanup if this is the last instance */ if (hybrid_tuner_report_instance_count(priv) == 1) { - kfree(priv->ctrl.fname); free_firmware(priv); + kfree(priv->ctrl.fname); + priv->ctrl.fname = NULL; } if (priv) @@ -1254,14 +1328,42 @@ static int xc2028_dvb_release(struct dvb_frontend *fe) static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency) { struct xc2028_data *priv = fe->tuner_priv; + int rc; tuner_dbg("%s called\n", __func__); + rc = check_device_status(priv); + if (rc < 0) + return rc; + *frequency = priv->frequency; return 0; } +static void load_firmware_cb(const struct firmware *fw, + void *context) +{ + struct dvb_frontend *fe = context; + struct xc2028_data *priv = fe->tuner_priv; + int rc; + + tuner_dbg("request_firmware_nowait(): %s\n", fw ? "OK" : "error"); + if (!fw) { + tuner_err("Could not load firmware %s.\n", priv->fname); + priv->state = XC2028_NODEV; + return; + } + + rc = load_all_firmwares(fe, fw); + + release_firmware(fw); + + if (rc < 0) + return; + priv->state = XC2028_SLEEP; +} + static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) { struct xc2028_data *priv = fe->tuner_priv; @@ -1272,21 +1374,49 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) mutex_lock(&priv->lock); + /* + * Copy the config data. + * For the firmware name, keep a local copy of the string, + * in order to avoid troubles during device release. + */ + if (priv->ctrl.fname) + kfree(priv->ctrl.fname); memcpy(&priv->ctrl, p, sizeof(priv->ctrl)); - if (priv->ctrl.max_len < 9) - priv->ctrl.max_len = 13; - if (p->fname) { - if (priv->ctrl.fname && strcmp(p->fname, priv->ctrl.fname)) { - kfree(priv->ctrl.fname); - free_firmware(priv); - } - priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL); if (priv->ctrl.fname == NULL) rc = -ENOMEM; } + /* + * If firmware name changed, frees firmware. As free_firmware will + * reset the status to NO_FIRMWARE, this forces a new request_firmware + */ + if (!firmware_name[0] && p->fname && + priv->fname && strcmp(p->fname, priv->fname)) + free_firmware(priv); + + if (priv->ctrl.max_len < 9) + priv->ctrl.max_len = 13; + + if (priv->state == XC2028_NO_FIRMWARE) { + if (!firmware_name[0]) + priv->fname = priv->ctrl.fname; + else + priv->fname = firmware_name; + + rc = request_firmware_nowait(THIS_MODULE, 1, + priv->fname, + priv->i2c_props.adap->dev.parent, + GFP_KERNEL, + fe, load_firmware_cb); + if (rc < 0) { + tuner_err("Failed to request firmware %s\n", + priv->fname); + priv->state = XC2028_NODEV; + } + priv->state = XC2028_WAITING_FIRMWARE; + } mutex_unlock(&priv->lock); return rc; @@ -1305,6 +1435,7 @@ static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = { .release = xc2028_dvb_release, .get_frequency = xc2028_get_frequency, .get_rf_strength = xc2028_signal, + .get_afc = xc2028_get_afc, .set_params = xc2028_set_params, .sleep = xc2028_sleep, }; @@ -1375,3 +1506,5 @@ MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver"); MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE); +MODULE_FIRMWARE(XC3028L_DEFAULT_FIRMWARE); diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index dcca42ca57be..bac8009e1d49 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -717,6 +717,12 @@ static int xc5000_set_params(struct dvb_frontend *fe) priv->freq_hz = freq - 1750000; priv->video_standard = DTV6; break; + case SYS_ISDBT: + /* All ISDB-T are currently for 6 MHz bw */ + if (!bw) + bw = 6000000; + /* fall to OFDM handling */ + case SYS_DMBTH: case SYS_DVBT: case SYS_DVBT2: dprintk(1, "%s() OFDM\n", __func__); diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c index 131b938e9e81..ebf3f05839d2 100644 --- a/drivers/media/dvb/ddbridge/ddbridge-core.c +++ b/drivers/media/dvb/ddbridge/ddbridge-core.c @@ -578,6 +578,7 @@ static int demod_attach_drxk(struct ddb_input *input) memset(&config, 0, sizeof(config)); config.microcode_name = "drxk_a3.mc"; + config.qam_demod_parameter_count = 4; config.adr = 0x29 + (input->nr & 1); fe = input->fe = dvb_attach(drxk_attach, &config, i2c); diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index e929d5697b87..7c64c09103a9 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -220,6 +220,7 @@ struct dvb_tuner_ops { #define TUNER_STATUS_STEREO 2 int (*get_status)(struct dvb_frontend *fe, u32 *status); int (*get_rf_strength)(struct dvb_frontend *fe, u16 *strength); + int (*get_afc)(struct dvb_frontend *fe, s32 *afc); /** These are provided separately from set_params in order to facilitate silicon * tuners which require sophisticated tuning loops, controlling each parameter separately. */ diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index a26949336b3d..c2161565023a 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -418,9 +418,12 @@ config DVB_USB_RTL28XXU tristate "Realtek RTL28xxU DVB USB support" depends on DVB_USB && EXPERIMENTAL select DVB_RTL2830 + select DVB_RTL2832 select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_FC0012 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_FC0013 if !MEDIA_TUNER_CUSTOMISE help Say Y here to support the Realtek RTL28xxU DVB USB receiver. diff --git a/drivers/media/dvb/dvb-usb/az6007.c b/drivers/media/dvb/dvb-usb/az6007.c index 4008b9c50fbd..8ffcad000ad3 100644 --- a/drivers/media/dvb/dvb-usb/az6007.c +++ b/drivers/media/dvb/dvb-usb/az6007.c @@ -593,9 +593,7 @@ static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6]) memcpy(mac, st->data, sizeof(mac)); if (ret > 0) - deb_info("%s: mac is %02x:%02x:%02x:%02x:%02x:%02x\n", - __func__, mac[0], mac[1], mac[2], - mac[3], mac[4], mac[5]); + deb_info("%s: mac is %pM\n", __func__, mac); return ret; } diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 7a6160bf54ba..26c44818a5ab 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -100,6 +100,7 @@ #define USB_PID_CONCEPTRONIC_CTVDIGRCU 0xe397 #define USB_PID_CONEXANT_D680_DMB 0x86d6 #define USB_PID_CREATIX_CTX1921 0x1921 +#define USB_PID_DELOCK_USB2_DVBT 0xb803 #define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064 #define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065 #define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8 @@ -160,6 +161,7 @@ #define USB_PID_TERRATEC_CINERGY_T_STICK 0x0093 #define USB_PID_TERRATEC_CINERGY_T_STICK_RC 0x0097 #define USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC 0x0099 +#define USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1 0x00a9 #define USB_PID_TWINHAN_VP7041_COLD 0x3201 #define USB_PID_TWINHAN_VP7041_WARM 0x3202 #define USB_PID_TWINHAN_VP7020_COLD 0x3203 @@ -245,6 +247,7 @@ #define USB_PID_TERRATEC_H7_2 0x10a3 #define USB_PID_TERRATEC_T3 0x10a0 #define USB_PID_TERRATEC_T5 0x10a1 +#define USB_PID_NOXON_DAB_STICK 0x00b3 #define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e #define USB_PID_PINNACLE_PCTV2000E 0x022c #define USB_PID_PINNACLE_PCTV_DVB_T_FLASH 0x0228 diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.c b/drivers/media/dvb/dvb-usb/rtl28xxu.c index 41e1f5537f44..6bd0bd792437 100644 --- a/drivers/media/dvb/dvb-usb/rtl28xxu.c +++ b/drivers/media/dvb/dvb-usb/rtl28xxu.c @@ -3,6 +3,7 @@ * * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2012 Thomas Mair <thomas.mair86@googlemail.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 @@ -22,10 +23,13 @@ #include "rtl28xxu.h" #include "rtl2830.h" +#include "rtl2832.h" #include "qt1010.h" #include "mt2060.h" #include "mxl5005s.h" +#include "fc0012.h" +#include "fc0013.h" /* debug */ static int dvb_usb_rtl28xxu_debug; @@ -80,7 +84,7 @@ err: return ret; } -static int rtl2831_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len) +static int rtl28xx_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len) { struct rtl28xxu_req req; @@ -116,12 +120,12 @@ static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len) return rtl28xxu_ctrl_msg(d, &req); } -static int rtl2831_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val) +static int rtl28xx_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val) { - return rtl2831_wr_regs(d, reg, &val, 1); + return rtl28xx_wr_regs(d, reg, &val, 1); } -static int rtl2831_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val) +static int rtl28xx_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val) { return rtl2831_rd_regs(d, reg, val, 1); } @@ -308,12 +312,12 @@ static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap) */ /* GPIO direction */ - ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a); + ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a); if (ret) goto err; /* enable as output GPIO0, GPIO2, GPIO4 */ - ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15); + ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15); if (ret) goto err; @@ -381,34 +385,159 @@ err: return ret; } +static struct rtl2832_config rtl28xxu_rtl2832_fc0012_config = { + .i2c_addr = 0x10, /* 0x20 */ + .xtal = 28800000, + .if_dvbt = 0, + .tuner = TUNER_RTL2832_FC0012 +}; + +static struct rtl2832_config rtl28xxu_rtl2832_fc0013_config = { + .i2c_addr = 0x10, /* 0x20 */ + .xtal = 28800000, + .if_dvbt = 0, + .tuner = TUNER_RTL2832_FC0013 +}; + +static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d, + int cmd, int arg) +{ + int ret; + u8 val; + + deb_info("%s cmd=%d arg=%d\n", __func__, cmd, arg); + switch (cmd) { + case FC_FE_CALLBACK_VHF_ENABLE: + /* set output values */ + ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val); + if (ret) + goto err; + + if (arg) + val &= 0xbf; /* set GPIO6 low */ + else + val |= 0x40; /* set GPIO6 high */ + + + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val); + if (ret) + goto err; + break; + default: + ret = -EINVAL; + goto err; + } + return 0; + +err: + err("%s: failed=%d\n", __func__, ret); + + return ret; +} + + +static int rtl2832u_fc0013_tuner_callback(struct dvb_usb_device *d, + int cmd, int arg) +{ + /* TODO implement*/ + return 0; +} + +static int rtl2832u_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) +{ + struct rtl28xxu_priv *priv = d->priv; + + switch (priv->tuner) { + case TUNER_RTL2832_FC0012: + return rtl2832u_fc0012_tuner_callback(d, cmd, arg); + + case TUNER_RTL2832_FC0013: + return rtl2832u_fc0013_tuner_callback(d, cmd, arg); + default: + break; + } + + return -ENODEV; +} + +static int rtl2832u_frontend_callback(void *adapter_priv, int component, + int cmd, int arg) +{ + struct i2c_adapter *adap = adapter_priv; + struct dvb_usb_device *d = i2c_get_adapdata(adap); + + switch (component) { + case DVB_FRONTEND_COMPONENT_TUNER: + return rtl2832u_tuner_callback(d, cmd, arg); + default: + break; + } + + return -EINVAL; +} + + + + static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) { int ret; struct rtl28xxu_priv *priv = adap->dev->priv; - u8 buf[1]; + struct rtl2832_config *rtl2832_config; + + u8 buf[2], val; /* open RTL2832U/RTL2832 I2C gate */ struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"}; /* close RTL2832U/RTL2832 I2C gate */ struct rtl28xxu_req req_gate_close = {0x0120, 0x0011, 0x0001, "\x10"}; + /* for FC0012 tuner probe */ + struct rtl28xxu_req req_fc0012 = {0x00c6, CMD_I2C_RD, 1, buf}; + /* for FC0013 tuner probe */ + struct rtl28xxu_req req_fc0013 = {0x00c6, CMD_I2C_RD, 1, buf}; + /* for MT2266 tuner probe */ + struct rtl28xxu_req req_mt2266 = {0x00c0, CMD_I2C_RD, 1, buf}; /* for FC2580 tuner probe */ struct rtl28xxu_req req_fc2580 = {0x01ac, CMD_I2C_RD, 1, buf}; + /* for MT2063 tuner probe */ + struct rtl28xxu_req req_mt2063 = {0x00c0, CMD_I2C_RD, 1, buf}; + /* for MAX3543 tuner probe */ + struct rtl28xxu_req req_max3543 = {0x00c0, CMD_I2C_RD, 1, buf}; + /* for TUA9001 tuner probe */ + struct rtl28xxu_req req_tua9001 = {0x7ec0, CMD_I2C_RD, 2, buf}; + /* for MXL5007T tuner probe */ + struct rtl28xxu_req req_mxl5007t = {0xd9c0, CMD_I2C_RD, 1, buf}; + /* for E4000 tuner probe */ + struct rtl28xxu_req req_e4000 = {0x02c8, CMD_I2C_RD, 1, buf}; + /* for TDA18272 tuner probe */ + struct rtl28xxu_req req_tda18272 = {0x00c0, CMD_I2C_RD, 2, buf}; deb_info("%s:\n", __func__); - /* GPIO direction */ - ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a); + + ret = rtl28xx_rd_reg(adap->dev, SYS_GPIO_DIR, &val); if (ret) goto err; - /* enable as output GPIO0, GPIO2, GPIO4 */ - ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15); + val &= 0xbf; + + ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_DIR, val); if (ret) goto err; - ret = rtl2831_wr_reg(adap->dev, SYS_DEMOD_CTL, 0xe8); + + /* enable as output GPIO3 and GPIO6*/ + ret = rtl28xx_rd_reg(adap->dev, SYS_GPIO_OUT_EN, &val); if (ret) goto err; + val |= 0x48; + + ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_OUT_EN, val); + if (ret) + goto err; + + + /* * Probe used tuner. We need to know used tuner before demod attach * since there is some demod params needed to set according to tuner. @@ -419,25 +548,108 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) if (ret) goto err; + priv->tuner = TUNER_NONE; + + /* check FC0012 ID register; reg=00 val=a1 */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc0012); + if (ret == 0 && buf[0] == 0xa1) { + priv->tuner = TUNER_RTL2832_FC0012; + rtl2832_config = &rtl28xxu_rtl2832_fc0012_config; + info("%s: FC0012 tuner found", __func__); + goto found; + } + + /* check FC0013 ID register; reg=00 val=a3 */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc0013); + if (ret == 0 && buf[0] == 0xa3) { + priv->tuner = TUNER_RTL2832_FC0013; + rtl2832_config = &rtl28xxu_rtl2832_fc0013_config; + info("%s: FC0013 tuner found", __func__); + goto found; + } + + /* check MT2266 ID register; reg=00 val=85 */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2266); + if (ret == 0 && buf[0] == 0x85) { + priv->tuner = TUNER_RTL2832_MT2266; + /* TODO implement tuner */ + info("%s: MT2266 tuner found", __func__); + goto unsupported; + } + /* check FC2580 ID register; reg=01 val=56 */ ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc2580); if (ret == 0 && buf[0] == 0x56) { priv->tuner = TUNER_RTL2832_FC2580; - deb_info("%s: FC2580\n", __func__); - goto found; - } else { - deb_info("%s: FC2580 probe failed=%d - %02x\n", - __func__, ret, buf[0]); + /* TODO implement tuner */ + info("%s: FC2580 tuner found", __func__); + goto unsupported; } + /* check MT2063 ID register; reg=00 val=9e || 9c */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2063); + if (ret == 0 && (buf[0] == 0x9e || buf[0] == 0x9c)) { + priv->tuner = TUNER_RTL2832_MT2063; + /* TODO implement tuner */ + info("%s: MT2063 tuner found", __func__); + goto unsupported; + } + + /* check MAX3543 ID register; reg=00 val=38 */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_max3543); + if (ret == 0 && buf[0] == 0x38) { + priv->tuner = TUNER_RTL2832_MAX3543; + /* TODO implement tuner */ + info("%s: MAX3534 tuner found", __func__); + goto unsupported; + } + + /* check TUA9001 ID register; reg=7e val=2328 */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_tua9001); + if (ret == 0 && buf[0] == 0x23 && buf[1] == 0x28) { + priv->tuner = TUNER_RTL2832_TUA9001; + /* TODO implement tuner */ + info("%s: TUA9001 tuner found", __func__); + goto unsupported; + } + + /* check MXL5007R ID register; reg=d9 val=14 */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_mxl5007t); + if (ret == 0 && buf[0] == 0x14) { + priv->tuner = TUNER_RTL2832_MXL5007T; + /* TODO implement tuner */ + info("%s: MXL5007T tuner found", __func__); + goto unsupported; + } + + /* check E4000 ID register; reg=02 val=40 */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_e4000); + if (ret == 0 && buf[0] == 0x40) { + priv->tuner = TUNER_RTL2832_E4000; + /* TODO implement tuner */ + info("%s: E4000 tuner found", __func__); + goto unsupported; + } + + /* check TDA18272 ID register; reg=00 val=c760 */ + ret = rtl28xxu_ctrl_msg(adap->dev, &req_tda18272); + if (ret == 0 && (buf[0] == 0xc7 || buf[1] == 0x60)) { + priv->tuner = TUNER_RTL2832_TDA18272; + /* TODO implement tuner */ + info("%s: TDA18272 tuner found", __func__); + goto unsupported; + } + +unsupported: /* close demod I2C gate */ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close); if (ret) goto err; /* tuner not found */ + deb_info("No compatible tuner found"); ret = -ENODEV; - goto err; + return ret; found: /* close demod I2C gate */ @@ -446,9 +658,18 @@ found: goto err; /* attach demodulator */ - /* TODO: */ + adap->fe_adap[0].fe = dvb_attach(rtl2832_attach, rtl2832_config, + &adap->dev->i2c_adap); + if (adap->fe_adap[0].fe == NULL) { + ret = -ENODEV; + goto err; + } + + /* set fe callbacks */ + adap->fe_adap[0].fe->callback = rtl2832u_frontend_callback; return ret; + err: deb_info("%s: failed=%d\n", __func__, ret); return ret; @@ -531,10 +752,24 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) deb_info("%s:\n", __func__); switch (priv->tuner) { - case TUNER_RTL2832_FC2580: - /* TODO: */ - fe = NULL; + case TUNER_RTL2832_FC0012: + fe = dvb_attach(fc0012_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ); + + /* since fc0012 includs reading the signal strength delegate + * that to the tuner driver */ + adap->fe_adap[0].fe->ops.read_signal_strength = adap->fe_adap[0]. + fe->ops.tuner_ops.get_rf_strength; + return 0; break; + case TUNER_RTL2832_FC0013: + fe = dvb_attach(fc0013_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ); + + /* fc0013 also supports signal strength reading */ + adap->fe_adap[0].fe->ops.read_signal_strength = adap->fe_adap[0] + .fe->ops.tuner_ops.get_rf_strength; + return 0; default: fe = NULL; err("unknown tuner=%d", priv->tuner); @@ -551,14 +786,14 @@ err: return ret; } -static int rtl28xxu_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff) +static int rtl2831u_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff) { int ret; u8 buf[2], gpio; deb_info("%s: onoff=%d\n", __func__, onoff); - ret = rtl2831_rd_reg(adap->dev, SYS_GPIO_OUT_VAL, &gpio); + ret = rtl28xx_rd_reg(adap->dev, SYS_GPIO_OUT_VAL, &gpio); if (ret) goto err; @@ -572,11 +807,37 @@ static int rtl28xxu_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff) gpio &= (~0x04); /* LED off */ } - ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_VAL, gpio); + ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_OUT_VAL, gpio); if (ret) goto err; - ret = rtl2831_wr_regs(adap->dev, USB_EPA_CTL, buf, 2); + ret = rtl28xx_wr_regs(adap->dev, USB_EPA_CTL, buf, 2); + if (ret) + goto err; + + return ret; +err: + deb_info("%s: failed=%d\n", __func__, ret); + return ret; +} + +static int rtl2832u_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff) +{ + int ret; + u8 buf[2]; + + deb_info("%s: onoff=%d\n", __func__, onoff); + + + if (onoff) { + buf[0] = 0x00; + buf[1] = 0x00; + } else { + buf[0] = 0x10; /* stall EPA */ + buf[1] = 0x02; /* reset EPA */ + } + + ret = rtl28xx_wr_regs(adap->dev, USB_EPA_CTL, buf, 2); if (ret) goto err; @@ -586,7 +847,7 @@ err: return ret; } -static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff) +static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff) { int ret; u8 gpio, sys0; @@ -594,12 +855,12 @@ static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff) deb_info("%s: onoff=%d\n", __func__, onoff); /* demod adc */ - ret = rtl2831_rd_reg(d, SYS_SYS0, &sys0); + ret = rtl28xx_rd_reg(d, SYS_SYS0, &sys0); if (ret) goto err; /* tuner power, read GPIOs */ - ret = rtl2831_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio); + ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio); if (ret) goto err; @@ -619,12 +880,12 @@ static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff) deb_info("%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio); /* demod adc */ - ret = rtl2831_wr_reg(d, SYS_SYS0, sys0); + ret = rtl28xx_wr_reg(d, SYS_SYS0, sys0); if (ret) goto err; /* tuner power, write GPIOs */ - ret = rtl2831_wr_reg(d, SYS_GPIO_OUT_VAL, gpio); + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, gpio); if (ret) goto err; @@ -634,6 +895,128 @@ err: return ret; } +static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + int ret; + u8 val; + + deb_info("%s: onoff=%d\n", __func__, onoff); + + if (onoff) { + /* set output values */ + ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val); + if (ret) + goto err; + + val |= 0x08; + val &= 0xef; + + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val); + if (ret) + goto err; + + /* demod_ctl_1 */ + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val); + if (ret) + goto err; + + val &= 0xef; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val); + if (ret) + goto err; + + /* demod control */ + /* PLL enable */ + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val); + if (ret) + goto err; + + /* bit 7 to 1 */ + val |= 0x80; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val); + if (ret) + goto err; + + /* demod HW reset */ + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val); + if (ret) + goto err; + /* bit 5 to 0 */ + val &= 0xdf; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val); + if (ret) + goto err; + + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val); + if (ret) + goto err; + + val |= 0x20; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val); + if (ret) + goto err; + + mdelay(5); + + /*enable ADC_Q and ADC_I */ + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val); + if (ret) + goto err; + + val |= 0x48; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val); + if (ret) + goto err; + + + } else { + /* demod_ctl_1 */ + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val); + if (ret) + goto err; + + val |= 0x0c; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val); + if (ret) + goto err; + + /* set output values */ + ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val); + if (ret) + goto err; + + val |= 0x10; + + ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val); + if (ret) + goto err; + + /* demod control */ + ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val); + if (ret) + goto err; + + val &= 0x37; + + ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val); + if (ret) + goto err; + + } + + return ret; +err: + deb_info("%s: failed=%d\n", __func__, ret); + return ret; +} + + static int rtl2831u_rc_query(struct dvb_usb_device *d) { int ret, i; @@ -660,7 +1043,7 @@ static int rtl2831u_rc_query(struct dvb_usb_device *d) /* init remote controller */ if (!priv->rc_active) { for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) { - ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg, + ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg, rc_nec_tab[i].val); if (ret) goto err; @@ -690,12 +1073,12 @@ static int rtl2831u_rc_query(struct dvb_usb_device *d) rc_keydown(d->rc_dev, rc_code, 0); - ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1); + ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1); if (ret) goto err; /* repeated intentionally to avoid extra keypress */ - ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1); + ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1); if (ret) goto err; } @@ -732,7 +1115,7 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d) /* init remote controller */ if (!priv->rc_active) { for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) { - ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg, + ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg, rc_nec_tab[i].val); if (ret) goto err; @@ -740,14 +1123,14 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d) priv->rc_active = true; } - ret = rtl2831_rd_reg(d, IR_RX_IF, &buf[0]); + ret = rtl28xx_rd_reg(d, IR_RX_IF, &buf[0]); if (ret) goto err; if (buf[0] != 0x83) goto exit; - ret = rtl2831_rd_reg(d, IR_RX_BC, &buf[0]); + ret = rtl28xx_rd_reg(d, IR_RX_BC, &buf[0]); if (ret) goto err; @@ -756,9 +1139,9 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d) /* TODO: pass raw IR to Kernel IR decoder */ - ret = rtl2831_wr_reg(d, IR_RX_IF, 0x03); - ret = rtl2831_wr_reg(d, IR_RX_BUF_CTRL, 0x80); - ret = rtl2831_wr_reg(d, IR_RX_CTRL, 0x80); + ret = rtl28xx_wr_reg(d, IR_RX_IF, 0x03); + ret = rtl28xx_wr_reg(d, IR_RX_BUF_CTRL, 0x80); + ret = rtl28xx_wr_reg(d, IR_RX_CTRL, 0x80); exit: return ret; @@ -771,6 +1154,9 @@ enum rtl28xxu_usb_table_entry { RTL2831U_0BDA_2831, RTL2831U_14AA_0160, RTL2831U_14AA_0161, + RTL2832U_0CCD_00A9, + RTL2832U_1F4D_B803, + RTL2832U_0CCD_00B3, }; static struct usb_device_id rtl28xxu_table[] = { @@ -783,6 +1169,12 @@ static struct usb_device_id rtl28xxu_table[] = { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2)}, /* RTL2832U */ + [RTL2832U_0CCD_00A9] = { + USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1)}, + [RTL2832U_1F4D_B803] = { + USB_DEVICE(USB_VID_GTEK, USB_PID_DELOCK_USB2_DVBT)}, + [RTL2832U_0CCD_00B3] = { + USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK)}, {} /* terminating entry */ }; @@ -805,7 +1197,7 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = { { .frontend_attach = rtl2831u_frontend_attach, .tuner_attach = rtl2831u_tuner_attach, - .streaming_ctrl = rtl28xxu_streaming_ctrl, + .streaming_ctrl = rtl2831u_streaming_ctrl, .stream = { .type = USB_BULK, .count = 6, @@ -821,7 +1213,7 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = { } }, - .power_ctrl = rtl28xxu_power_ctrl, + .power_ctrl = rtl2831u_power_ctrl, .rc.core = { .protocol = RC_TYPE_NEC, @@ -867,7 +1259,7 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = { { .frontend_attach = rtl2832u_frontend_attach, .tuner_attach = rtl2832u_tuner_attach, - .streaming_ctrl = rtl28xxu_streaming_ctrl, + .streaming_ctrl = rtl2832u_streaming_ctrl, .stream = { .type = USB_BULK, .count = 6, @@ -883,7 +1275,7 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = { } }, - .power_ctrl = rtl28xxu_power_ctrl, + .power_ctrl = rtl2832u_power_ctrl, .rc.core = { .protocol = RC_TYPE_NEC, @@ -896,10 +1288,25 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = { .i2c_algo = &rtl28xxu_i2c_algo, - .num_device_descs = 0, /* disabled as no support for RTL2832 */ + .num_device_descs = 3, .devices = { { - .name = "Realtek RTL2832U reference design", + .name = "Terratec Cinergy T Stick Black", + .warm_ids = { + &rtl28xxu_table[RTL2832U_0CCD_00A9], + }, + }, + { + .name = "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", + .warm_ids = { + &rtl28xxu_table[RTL2832U_1F4D_B803], + }, + }, + { + .name = "NOXON DAB/DAB+ USB dongle", + .warm_ids = { + &rtl28xxu_table[RTL2832U_0CCD_00B3], + }, }, } }, @@ -910,6 +1317,7 @@ static int rtl28xxu_probe(struct usb_interface *intf, const struct usb_device_id *id) { int ret, i; + u8 val; int properties_count = ARRAY_SIZE(rtl28xxu_properties); struct dvb_usb_device *d; struct usb_device *udev; @@ -954,16 +1362,25 @@ static int rtl28xxu_probe(struct usb_interface *intf, if (ret) goto err; + /* init USB endpoints */ - ret = rtl2831_wr_reg(d, USB_SYSCTL_0, 0x09); + ret = rtl28xx_rd_reg(d, USB_SYSCTL_0, &val); + if (ret) + goto err; + + /* enable DMA and Full Packet Mode*/ + val |= 0x09; + ret = rtl28xx_wr_reg(d, USB_SYSCTL_0, val); if (ret) goto err; - ret = rtl2831_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4); + /* set EPA maximum packet size to 0x0200 */ + ret = rtl28xx_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4); if (ret) goto err; - ret = rtl2831_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4); + /* change EPA FIFO length */ + ret = rtl28xx_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4); if (ret) goto err; @@ -1007,4 +1424,5 @@ module_exit(rtl28xxu_module_exit); MODULE_DESCRIPTION("Realtek RTL28xxU DVB USB driver"); MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_AUTHOR("Thomas Mair <thomas.mair86@googlemail.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index b98ebb264e29..a08c2152d0ee 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -1,6 +1,7 @@ config DVB_FE_CUSTOMISE bool "Customise the frontend modules to build" depends on DVB_CORE + depends on EXPERT default y if EXPERT help This allows the user to select/deselect frontend drivers for their @@ -432,6 +433,13 @@ config DVB_RTL2830 help Say Y when you want to support this frontend. +config DVB_RTL2832 + tristate "Realtek RTL2832 DVB-T" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + Say Y when you want to support this frontend. + comment "DVB-C (cable) frontends" depends on DVB_CORE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index cd1ac2fd5774..185bb8b51952 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o obj-$(CONFIG_DVB_A8293) += a8293.o obj-$(CONFIG_DVB_TDA10071) += tda10071.o obj-$(CONFIG_DVB_RTL2830) += rtl2830.o +obj-$(CONFIG_DVB_RTL2832) += rtl2832.o obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o obj-$(CONFIG_DVB_AF9033) += af9033.o diff --git a/drivers/media/dvb/frontends/a8293.c b/drivers/media/dvb/frontends/a8293.c index bb56497e940a..cff44a389b40 100644 --- a/drivers/media/dvb/frontends/a8293.c +++ b/drivers/media/dvb/frontends/a8293.c @@ -21,24 +21,6 @@ #include "dvb_frontend.h" #include "a8293.h" -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); - -#define LOG_PREFIX "a8293" - -#undef dbg -#define dbg(f, arg...) \ - if (debug) \ - printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) -#undef err -#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) -#undef info -#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) -#undef warn -#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) - - struct a8293_priv { struct i2c_adapter *i2c; const struct a8293_config *cfg; @@ -65,7 +47,8 @@ static int a8293_i2c(struct a8293_priv *priv, u8 *val, int len, bool rd) if (ret == 1) { ret = 0; } else { - warn("i2c failed=%d rd=%d", ret, rd); + dev_warn(&priv->i2c->dev, "%s: i2c failed=%d rd=%d\n", + KBUILD_MODNAME, ret, rd); ret = -EREMOTEIO; } @@ -88,7 +71,8 @@ static int a8293_set_voltage(struct dvb_frontend *fe, struct a8293_priv *priv = fe->sec_priv; int ret; - dbg("%s: fe_sec_voltage=%d", __func__, fe_sec_voltage); + dev_dbg(&priv->i2c->dev, "%s: fe_sec_voltage=%d\n", __func__, + fe_sec_voltage); switch (fe_sec_voltage) { case SEC_VOLTAGE_OFF: @@ -114,14 +98,12 @@ static int a8293_set_voltage(struct dvb_frontend *fe, return ret; err: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } static void a8293_release_sec(struct dvb_frontend *fe) { - dbg("%s:", __func__); - a8293_set_voltage(fe, SEC_VOLTAGE_OFF); kfree(fe->sec_priv); @@ -154,7 +136,7 @@ struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, /* ENB=0 */ priv->reg[0] = 0x10; - ret = a8293_wr(priv, &priv->reg[1], 1); + ret = a8293_wr(priv, &priv->reg[0], 1); if (ret) goto err; @@ -164,16 +146,17 @@ struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, if (ret) goto err; - info("Allegro A8293 SEC attached."); - fe->ops.release_sec = a8293_release_sec; /* override frontend ops */ fe->ops.set_voltage = a8293_set_voltage; + dev_info(&priv->i2c->dev, "%s: Allegro A8293 SEC attached\n", + KBUILD_MODNAME); + return fe; err: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); kfree(priv); return NULL; } diff --git a/drivers/media/dvb/frontends/drxk.h b/drivers/media/dvb/frontends/drxk.h index 9d64e4fea066..d615d7d055a2 100644 --- a/drivers/media/dvb/frontends/drxk.h +++ b/drivers/media/dvb/frontends/drxk.h @@ -20,6 +20,14 @@ * means that 1=DVBC, 0 = DVBT. Zero means the opposite. * @mpeg_out_clk_strength: DRXK Mpeg output clock drive strength. * @microcode_name: Name of the firmware file with the microcode + * @qam_demod_parameter_count: The number of parameters used for the command + * to set the demodulator parameters. All + * firmwares are using the 2-parameter commmand. + * An exception is the "drxk_a3.mc" firmware, + * which uses the 4-parameter command. + * A value of 0 (default) or lower indicates that + * the correct number of parameters will be + * automatically detected. * * On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is * UIO-3. @@ -38,7 +46,8 @@ struct drxk_config { u8 mpeg_out_clk_strength; int chunk_size; - const char *microcode_name; + const char *microcode_name; + int qam_demod_parameter_count; }; #if defined(CONFIG_DVB_DRXK) || (defined(CONFIG_DVB_DRXK_MODULE) \ diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c index 60b868faeacf..1ab8154542da 100644 --- a/drivers/media/dvb/frontends/drxk_hard.c +++ b/drivers/media/dvb/frontends/drxk_hard.c @@ -28,6 +28,7 @@ #include <linux/delay.h> #include <linux/firmware.h> #include <linux/i2c.h> +#include <linux/hardirq.h> #include <asm/div64.h> #include "dvb_frontend.h" @@ -308,16 +309,42 @@ static u32 Log10Times100(u32 x) /* I2C **********************************************************************/ /****************************************************************************/ -static int i2c_read1(struct i2c_adapter *adapter, u8 adr, u8 *val) +static int drxk_i2c_lock(struct drxk_state *state) +{ + i2c_lock_adapter(state->i2c); + state->drxk_i2c_exclusive_lock = true; + + return 0; +} + +static void drxk_i2c_unlock(struct drxk_state *state) +{ + if (!state->drxk_i2c_exclusive_lock) + return; + + i2c_unlock_adapter(state->i2c); + state->drxk_i2c_exclusive_lock = false; +} + +static int drxk_i2c_transfer(struct drxk_state *state, struct i2c_msg *msgs, + unsigned len) +{ + if (state->drxk_i2c_exclusive_lock) + return __i2c_transfer(state->i2c, msgs, len); + else + return i2c_transfer(state->i2c, msgs, len); +} + +static int i2c_read1(struct drxk_state *state, u8 adr, u8 *val) { struct i2c_msg msgs[1] = { {.addr = adr, .flags = I2C_M_RD, .buf = val, .len = 1} }; - return i2c_transfer(adapter, msgs, 1); + return drxk_i2c_transfer(state, msgs, 1); } -static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) +static int i2c_write(struct drxk_state *state, u8 adr, u8 *data, int len) { int status; struct i2c_msg msg = { @@ -330,7 +357,7 @@ static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) printk(KERN_CONT " %02x", data[i]); printk(KERN_CONT "\n"); } - status = i2c_transfer(adap, &msg, 1); + status = drxk_i2c_transfer(state, &msg, 1); if (status >= 0 && status != 1) status = -EIO; @@ -340,7 +367,7 @@ static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) return status; } -static int i2c_read(struct i2c_adapter *adap, +static int i2c_read(struct drxk_state *state, u8 adr, u8 *msg, int len, u8 *answ, int alen) { int status; @@ -351,7 +378,7 @@ static int i2c_read(struct i2c_adapter *adap, .buf = answ, .len = alen} }; - status = i2c_transfer(adap, msgs, 2); + status = drxk_i2c_transfer(state, msgs, 2); if (status != 2) { if (debug > 2) printk(KERN_CONT ": ERROR!\n"); @@ -394,7 +421,7 @@ static int read16_flags(struct drxk_state *state, u32 reg, u16 *data, u8 flags) len = 2; } dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags); - status = i2c_read(state->i2c, adr, mm1, len, mm2, 2); + status = i2c_read(state, adr, mm1, len, mm2, 2); if (status < 0) return status; if (data) @@ -428,7 +455,7 @@ static int read32_flags(struct drxk_state *state, u32 reg, u32 *data, u8 flags) len = 2; } dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags); - status = i2c_read(state->i2c, adr, mm1, len, mm2, 4); + status = i2c_read(state, adr, mm1, len, mm2, 4); if (status < 0) return status; if (data) @@ -464,7 +491,7 @@ static int write16_flags(struct drxk_state *state, u32 reg, u16 data, u8 flags) mm[len + 1] = (data >> 8) & 0xff; dprintk(2, "(0x%08x, 0x%04x, 0x%02x)\n", reg, data, flags); - return i2c_write(state->i2c, adr, mm, len + 2); + return i2c_write(state, adr, mm, len + 2); } static int write16(struct drxk_state *state, u32 reg, u16 data) @@ -495,7 +522,7 @@ static int write32_flags(struct drxk_state *state, u32 reg, u32 data, u8 flags) mm[len + 3] = (data >> 24) & 0xff; dprintk(2, "(0x%08x, 0x%08x, 0x%02x)\n", reg, data, flags); - return i2c_write(state->i2c, adr, mm, len + 4); + return i2c_write(state, adr, mm, len + 4); } static int write32(struct drxk_state *state, u32 reg, u32 data) @@ -542,7 +569,7 @@ static int write_block(struct drxk_state *state, u32 Address, printk(KERN_CONT " %02x", pBlock[i]); printk(KERN_CONT "\n"); } - status = i2c_write(state->i2c, state->demod_address, + status = i2c_write(state, state->demod_address, &state->Chunk[0], Chunk + AdrLength); if (status < 0) { printk(KERN_ERR "drxk: %s: i2c write error at addr 0x%02x\n", @@ -568,17 +595,17 @@ int PowerUpDevice(struct drxk_state *state) dprintk(1, "\n"); - status = i2c_read1(state->i2c, state->demod_address, &data); + status = i2c_read1(state, state->demod_address, &data); if (status < 0) { do { data = 0; - status = i2c_write(state->i2c, state->demod_address, + status = i2c_write(state, state->demod_address, &data, 1); msleep(10); retryCount++; if (status < 0) continue; - status = i2c_read1(state->i2c, state->demod_address, + status = i2c_read1(state, state->demod_address, &data); } while (status < 0 && (retryCount < DRXK_MAX_RETRIES_POWERUP)); @@ -932,7 +959,7 @@ static int GetDeviceCapabilities(struct drxk_state *state) if (status < 0) goto error; -printk(KERN_ERR "drxk: status = 0x%08x\n", sioTopJtagidLo); + printk(KERN_INFO "drxk: status = 0x%08x\n", sioTopJtagidLo); /* driver 0.9.0 */ switch ((sioTopJtagidLo >> 29) & 0xF) { @@ -2824,7 +2851,7 @@ static int ConfigureI2CBridge(struct drxk_state *state, bool bEnableBridge) dprintk(1, "\n"); if (state->m_DrxkState == DRXK_UNINITIALIZED) - goto error; + return 0; if (state->m_DrxkState == DRXK_POWERED_DOWN) goto error; @@ -2977,7 +3004,7 @@ static int ADCSynchronization(struct drxk_state *state) status = read16(state, IQM_AF_CLKNEG__A, &clkNeg); if (status < 0) goto error; - if ((clkNeg | IQM_AF_CLKNEG_CLKNEGDATA__M) == + if ((clkNeg & IQM_AF_CLKNEG_CLKNEGDATA__M) == IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS) { clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M)); clkNeg |= @@ -5361,7 +5388,7 @@ static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus) SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK, 0, NULL, 2, Result); if (status < 0) - printk(KERN_ERR "drxk: %s status = %08x\n", __func__, status); + printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) { /* 0x0000 NOT LOCKED */ @@ -5388,12 +5415,67 @@ static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus) #define QAM_LOCKRANGE__M 0x10 #define QAM_LOCKRANGE_NORMAL 0x10 +static int QAMDemodulatorCommand(struct drxk_state *state, + int numberOfParameters) +{ + int status; + u16 cmdResult; + u16 setParamParameters[4] = { 0, 0, 0, 0 }; + + setParamParameters[0] = state->m_Constellation; /* modulation */ + setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */ + + if (numberOfParameters == 2) { + u16 setEnvParameters[1] = { 0 }; + + if (state->m_OperationMode == OM_QAM_ITU_C) + setEnvParameters[0] = QAM_TOP_ANNEX_C; + else + setEnvParameters[0] = QAM_TOP_ANNEX_A; + + status = scu_command(state, + SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, + 1, setEnvParameters, 1, &cmdResult); + if (status < 0) + goto error; + + status = scu_command(state, + SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, + numberOfParameters, setParamParameters, + 1, &cmdResult); + } else if (numberOfParameters == 4) { + if (state->m_OperationMode == OM_QAM_ITU_C) + setParamParameters[2] = QAM_TOP_ANNEX_C; + else + setParamParameters[2] = QAM_TOP_ANNEX_A; + + setParamParameters[3] |= (QAM_MIRROR_AUTO_ON); + /* Env parameters */ + /* check for LOCKRANGE Extented */ + /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */ + + status = scu_command(state, + SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, + numberOfParameters, setParamParameters, + 1, &cmdResult); + } else { + printk(KERN_WARNING "drxk: Unknown QAM demodulator parameter " + "count %d\n", numberOfParameters); + } + +error: + if (status < 0) + printk(KERN_WARNING "drxk: Warning %d on %s\n", + status, __func__); + return status; +} + static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz, s32 tunerFreqOffset) { int status; - u16 setParamParameters[4] = { 0, 0, 0, 0 }; u16 cmdResult; + int qamDemodParamCount = state->qam_demod_parameter_count; dprintk(1, "\n"); /* @@ -5445,34 +5527,42 @@ static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz, } if (status < 0) goto error; - setParamParameters[0] = state->m_Constellation; /* modulation */ - setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */ - if (state->m_OperationMode == OM_QAM_ITU_C) - setParamParameters[2] = QAM_TOP_ANNEX_C; - else - setParamParameters[2] = QAM_TOP_ANNEX_A; - setParamParameters[3] |= (QAM_MIRROR_AUTO_ON); - /* Env parameters */ - /* check for LOCKRANGE Extented */ - /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */ - status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 4, setParamParameters, 1, &cmdResult); - if (status < 0) { - /* Fall-back to the simpler call */ - if (state->m_OperationMode == OM_QAM_ITU_C) - setParamParameters[0] = QAM_TOP_ANNEX_C; - else - setParamParameters[0] = QAM_TOP_ANNEX_A; - status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 1, setParamParameters, 1, &cmdResult); - if (status < 0) - goto error; + /* Use the 4-parameter if it's requested or we're probing for + * the correct command. */ + if (state->qam_demod_parameter_count == 4 + || !state->qam_demod_parameter_count) { + qamDemodParamCount = 4; + status = QAMDemodulatorCommand(state, qamDemodParamCount); + } - setParamParameters[0] = state->m_Constellation; /* modulation */ - setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */ - status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 2, setParamParameters, 1, &cmdResult); + /* Use the 2-parameter command if it was requested or if we're + * probing for the correct command and the 4-parameter command + * failed. */ + if (state->qam_demod_parameter_count == 2 + || (!state->qam_demod_parameter_count && status < 0)) { + qamDemodParamCount = 2; + status = QAMDemodulatorCommand(state, qamDemodParamCount); } - if (status < 0) + + if (status < 0) { + dprintk(1, "Could not set demodulator parameters. Make " + "sure qam_demod_parameter_count (%d) is correct for " + "your firmware (%s).\n", + state->qam_demod_parameter_count, + state->microcode_name); goto error; + } else if (!state->qam_demod_parameter_count) { + dprintk(1, "Auto-probing the correct QAM demodulator command " + "parameters was successful - using %d parameters.\n", + qamDemodParamCount); + + /* + * One of our commands was successful. We don't need to + * auto-probe anymore, now that we got the correct command. + */ + state->qam_demod_parameter_count = qamDemodParamCount; + } /* * STEP 3: enable the system in a mode where the ADC provides valid @@ -5968,34 +6058,15 @@ error: return status; } -static int load_microcode(struct drxk_state *state, const char *mc_name) -{ - const struct firmware *fw = NULL; - int err = 0; - - dprintk(1, "\n"); - - err = request_firmware(&fw, mc_name, state->i2c->dev.parent); - if (err < 0) { - printk(KERN_ERR - "drxk: Could not load firmware file %s.\n", mc_name); - printk(KERN_INFO - "drxk: Copy %s to your hotplug directory!\n", mc_name); - return err; - } - err = DownloadMicrocode(state, fw->data, fw->size); - release_firmware(fw); - return err; -} - static int init_drxk(struct drxk_state *state) { - int status = 0; + int status = 0, n = 0; enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM; u16 driverVersion; dprintk(1, "\n"); if ((state->m_DrxkState == DRXK_UNINITIALIZED)) { + drxk_i2c_lock(state); status = PowerUpDevice(state); if (status < 0) goto error; @@ -6073,8 +6144,12 @@ static int init_drxk(struct drxk_state *state) if (status < 0) goto error; - if (state->microcode_name) - load_microcode(state, state->microcode_name); + if (state->fw) { + status = DownloadMicrocode(state, state->fw->data, + state->fw->size); + if (status < 0) + goto error; + } /* disable token-ring bus through OFDM block for possible ucode upload */ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_OFF); @@ -6167,19 +6242,71 @@ static int init_drxk(struct drxk_state *state) state->m_DrxkState = DRXK_POWERED_DOWN; } else state->m_DrxkState = DRXK_STOPPED; + + /* Initialize the supported delivery systems */ + n = 0; + if (state->m_hasDVBC) { + state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A; + state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C; + strlcat(state->frontend.ops.info.name, " DVB-C", + sizeof(state->frontend.ops.info.name)); + } + if (state->m_hasDVBT) { + state->frontend.ops.delsys[n++] = SYS_DVBT; + strlcat(state->frontend.ops.info.name, " DVB-T", + sizeof(state->frontend.ops.info.name)); + } + drxk_i2c_unlock(state); } error: - if (status < 0) + if (status < 0) { + state->m_DrxkState = DRXK_NO_DEV; + drxk_i2c_unlock(state); printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); + } return status; } +static void load_firmware_cb(const struct firmware *fw, + void *context) +{ + struct drxk_state *state = context; + + dprintk(1, ": %s\n", fw ? "firmware loaded" : "firmware not loaded"); + if (!fw) { + printk(KERN_ERR + "drxk: Could not load firmware file %s.\n", + state->microcode_name); + printk(KERN_INFO + "drxk: Copy %s to your hotplug directory!\n", + state->microcode_name); + state->microcode_name = NULL; + + /* + * As firmware is now load asynchronous, it is not possible + * anymore to fail at frontend attach. We might silently + * return here, and hope that the driver won't crash. + * We might also change all DVB callbacks to return -ENODEV + * if the device is not initialized. + * As the DRX-K devices have their own internal firmware, + * let's just hope that it will match a firmware revision + * compatible with this driver and proceed. + */ + } + state->fw = fw; + + init_drxk(state); +} + static void drxk_release(struct dvb_frontend *fe) { struct drxk_state *state = fe->demodulator_priv; dprintk(1, "\n"); + if (state->fw) + release_firmware(state->fw); + kfree(state); } @@ -6188,6 +6315,12 @@ static int drxk_sleep(struct dvb_frontend *fe) struct drxk_state *state = fe->demodulator_priv; dprintk(1, "\n"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return 0; + ShutDown(state); return 0; } @@ -6196,7 +6329,11 @@ static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) { struct drxk_state *state = fe->demodulator_priv; - dprintk(1, "%s\n", enable ? "enable" : "disable"); + dprintk(1, ": %s\n", enable ? "enable" : "disable"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + return ConfigureI2CBridge(state, enable ? true : false); } @@ -6209,6 +6346,12 @@ static int drxk_set_parameters(struct dvb_frontend *fe) dprintk(1, "\n"); + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + if (!fe->ops.tuner_ops.get_if_frequency) { printk(KERN_ERR "drxk: Error: get_if_frequency() not defined at tuner. Can't work without it!\n"); @@ -6262,6 +6405,12 @@ static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status) u32 stat; dprintk(1, "\n"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + *status = 0; GetLockStatus(state, &stat, 0); if (stat == MPEG_LOCK) @@ -6275,8 +6424,15 @@ static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status) static int drxk_read_ber(struct dvb_frontend *fe, u32 *ber) { + struct drxk_state *state = fe->demodulator_priv; + dprintk(1, "\n"); + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + *ber = 0; return 0; } @@ -6288,6 +6444,12 @@ static int drxk_read_signal_strength(struct dvb_frontend *fe, u32 val = 0; dprintk(1, "\n"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + ReadIFAgc(state, &val); *strength = val & 0xffff; return 0; @@ -6299,6 +6461,12 @@ static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr) s32 snr2; dprintk(1, "\n"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + GetSignalToNoise(state, &snr2); *snr = snr2 & 0xffff; return 0; @@ -6310,6 +6478,12 @@ static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) u16 err; dprintk(1, "\n"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + DVBTQAMGetAccPktErr(state, &err); *ucblocks = (u32) err; return 0; @@ -6318,9 +6492,16 @@ static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) static int drxk_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *sets) { + struct drxk_state *state = fe->demodulator_priv; struct dtv_frontend_properties *p = &fe->dtv_property_cache; dprintk(1, "\n"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + switch (p->delivery_system) { case SYS_DVBC_ANNEX_A: case SYS_DVBC_ANNEX_C: @@ -6371,10 +6552,9 @@ static struct dvb_frontend_ops drxk_ops = { struct dvb_frontend *drxk_attach(const struct drxk_config *config, struct i2c_adapter *i2c) { - int n; - struct drxk_state *state = NULL; u8 adr = config->adr; + int status; dprintk(1, "\n"); state = kzalloc(sizeof(struct drxk_state), GFP_KERNEL); @@ -6385,6 +6565,7 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config, state->demod_address = adr; state->single_master = config->single_master; state->microcode_name = config->microcode_name; + state->qam_demod_parameter_count = config->qam_demod_parameter_count; state->no_i2c_bridge = config->no_i2c_bridge; state->antenna_gpio = config->antenna_gpio; state->antenna_dvbt = config->antenna_dvbt; @@ -6425,22 +6606,21 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config, state->frontend.demodulator_priv = state; init_state(state); - if (init_drxk(state) < 0) - goto error; - /* Initialize the supported delivery systems */ - n = 0; - if (state->m_hasDVBC) { - state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A; - state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C; - strlcat(state->frontend.ops.info.name, " DVB-C", - sizeof(state->frontend.ops.info.name)); - } - if (state->m_hasDVBT) { - state->frontend.ops.delsys[n++] = SYS_DVBT; - strlcat(state->frontend.ops.info.name, " DVB-T", - sizeof(state->frontend.ops.info.name)); - } + /* Load firmware and initialize DRX-K */ + if (state->microcode_name) { + status = request_firmware_nowait(THIS_MODULE, 1, + state->microcode_name, + state->i2c->dev.parent, + GFP_KERNEL, + state, load_firmware_cb); + if (status < 0) { + printk(KERN_ERR + "drxk: failed to request a firmware\n"); + return NULL; + } + } else if (init_drxk(state) < 0) + goto error; printk(KERN_INFO "drxk: frontend initialized.\n"); return &state->frontend; diff --git a/drivers/media/dvb/frontends/drxk_hard.h b/drivers/media/dvb/frontends/drxk_hard.h index 4bbf841de83a..6bb9fc4a7b96 100644 --- a/drivers/media/dvb/frontends/drxk_hard.h +++ b/drivers/media/dvb/frontends/drxk_hard.h @@ -94,7 +94,15 @@ enum DRXPowerMode { enum AGC_CTRL_MODE { DRXK_AGC_CTRL_AUTO = 0, DRXK_AGC_CTRL_USER, DRXK_AGC_CTRL_OFF }; -enum EDrxkState { DRXK_UNINITIALIZED = 0, DRXK_STOPPED, DRXK_DTV_STARTED, DRXK_ATV_STARTED, DRXK_POWERED_DOWN }; +enum EDrxkState { + DRXK_UNINITIALIZED = 0, + DRXK_STOPPED, + DRXK_DTV_STARTED, + DRXK_ATV_STARTED, + DRXK_POWERED_DOWN, + DRXK_NO_DEV /* If drxk init failed */ +}; + enum EDrxkCoefArrayIndex { DRXK_COEF_IDX_MN = 0, DRXK_COEF_IDX_FM , @@ -325,6 +333,9 @@ struct drxk_state { enum DRXPowerMode m_currentPowerMode; + /* when true, avoids other devices to use the I2C bus */ + bool drxk_i2c_exclusive_lock; + /* * Configurable parameters at the driver. They stores the values found * at struct drxk_config. @@ -338,7 +349,11 @@ struct drxk_state { bool antenna_dvbt; u16 antenna_gpio; + /* Firmware */ const char *microcode_name; + struct completion fw_wait_load; + const struct firmware *fw; + int qam_demod_parameter_count; }; #define NEVER_LOCK 0 diff --git a/drivers/media/dvb/frontends/rtl2832.c b/drivers/media/dvb/frontends/rtl2832.c new file mode 100644 index 000000000000..2da592fb38ad --- /dev/null +++ b/drivers/media/dvb/frontends/rtl2832.c @@ -0,0 +1,789 @@ +/* + * Realtek RTL2832 DVB-T demodulator driver + * + * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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 "rtl2832_priv.h" +#include <linux/bitops.h> + +int rtl2832_debug; +module_param_named(debug, rtl2832_debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +#define REG_MASK(b) (BIT(b + 1) - 1) + +static const struct rtl2832_reg_entry registers[] = { + [DVBT_SOFT_RST] = {0x1, 0x1, 2, 2}, + [DVBT_IIC_REPEAT] = {0x1, 0x1, 3, 3}, + [DVBT_TR_WAIT_MIN_8K] = {0x1, 0x88, 11, 2}, + [DVBT_RSD_BER_FAIL_VAL] = {0x1, 0x8f, 15, 0}, + [DVBT_EN_BK_TRK] = {0x1, 0xa6, 7, 7}, + [DVBT_AD_EN_REG] = {0x0, 0x8, 7, 7}, + [DVBT_AD_EN_REG1] = {0x0, 0x8, 6, 6}, + [DVBT_EN_BBIN] = {0x1, 0xb1, 0, 0}, + [DVBT_MGD_THD0] = {0x1, 0x95, 7, 0}, + [DVBT_MGD_THD1] = {0x1, 0x96, 7, 0}, + [DVBT_MGD_THD2] = {0x1, 0x97, 7, 0}, + [DVBT_MGD_THD3] = {0x1, 0x98, 7, 0}, + [DVBT_MGD_THD4] = {0x1, 0x99, 7, 0}, + [DVBT_MGD_THD5] = {0x1, 0x9a, 7, 0}, + [DVBT_MGD_THD6] = {0x1, 0x9b, 7, 0}, + [DVBT_MGD_THD7] = {0x1, 0x9c, 7, 0}, + [DVBT_EN_CACQ_NOTCH] = {0x1, 0x61, 4, 4}, + [DVBT_AD_AV_REF] = {0x0, 0x9, 6, 0}, + [DVBT_REG_PI] = {0x0, 0xa, 2, 0}, + [DVBT_PIP_ON] = {0x0, 0x21, 3, 3}, + [DVBT_SCALE1_B92] = {0x2, 0x92, 7, 0}, + [DVBT_SCALE1_B93] = {0x2, 0x93, 7, 0}, + [DVBT_SCALE1_BA7] = {0x2, 0xa7, 7, 0}, + [DVBT_SCALE1_BA9] = {0x2, 0xa9, 7, 0}, + [DVBT_SCALE1_BAA] = {0x2, 0xaa, 7, 0}, + [DVBT_SCALE1_BAB] = {0x2, 0xab, 7, 0}, + [DVBT_SCALE1_BAC] = {0x2, 0xac, 7, 0}, + [DVBT_SCALE1_BB0] = {0x2, 0xb0, 7, 0}, + [DVBT_SCALE1_BB1] = {0x2, 0xb1, 7, 0}, + [DVBT_KB_P1] = {0x1, 0x64, 3, 1}, + [DVBT_KB_P2] = {0x1, 0x64, 6, 4}, + [DVBT_KB_P3] = {0x1, 0x65, 2, 0}, + [DVBT_OPT_ADC_IQ] = {0x0, 0x6, 5, 4}, + [DVBT_AD_AVI] = {0x0, 0x9, 1, 0}, + [DVBT_AD_AVQ] = {0x0, 0x9, 3, 2}, + [DVBT_K1_CR_STEP12] = {0x2, 0xad, 9, 4}, + [DVBT_TRK_KS_P2] = {0x1, 0x6f, 2, 0}, + [DVBT_TRK_KS_I2] = {0x1, 0x70, 5, 3}, + [DVBT_TR_THD_SET2] = {0x1, 0x72, 3, 0}, + [DVBT_TRK_KC_P2] = {0x1, 0x73, 5, 3}, + [DVBT_TRK_KC_I2] = {0x1, 0x75, 2, 0}, + [DVBT_CR_THD_SET2] = {0x1, 0x76, 7, 6}, + [DVBT_PSET_IFFREQ] = {0x1, 0x19, 21, 0}, + [DVBT_SPEC_INV] = {0x1, 0x15, 0, 0}, + [DVBT_RSAMP_RATIO] = {0x1, 0x9f, 27, 2}, + [DVBT_CFREQ_OFF_RATIO] = {0x1, 0x9d, 23, 4}, + [DVBT_FSM_STAGE] = {0x3, 0x51, 6, 3}, + [DVBT_RX_CONSTEL] = {0x3, 0x3c, 3, 2}, + [DVBT_RX_HIER] = {0x3, 0x3c, 6, 4}, + [DVBT_RX_C_RATE_LP] = {0x3, 0x3d, 2, 0}, + [DVBT_RX_C_RATE_HP] = {0x3, 0x3d, 5, 3}, + [DVBT_GI_IDX] = {0x3, 0x51, 1, 0}, + [DVBT_FFT_MODE_IDX] = {0x3, 0x51, 2, 2}, + [DVBT_RSD_BER_EST] = {0x3, 0x4e, 15, 0}, + [DVBT_CE_EST_EVM] = {0x4, 0xc, 15, 0}, + [DVBT_RF_AGC_VAL] = {0x3, 0x5b, 13, 0}, + [DVBT_IF_AGC_VAL] = {0x3, 0x59, 13, 0}, + [DVBT_DAGC_VAL] = {0x3, 0x5, 7, 0}, + [DVBT_SFREQ_OFF] = {0x3, 0x18, 13, 0}, + [DVBT_CFREQ_OFF] = {0x3, 0x5f, 17, 0}, + [DVBT_POLAR_RF_AGC] = {0x0, 0xe, 1, 1}, + [DVBT_POLAR_IF_AGC] = {0x0, 0xe, 0, 0}, + [DVBT_AAGC_HOLD] = {0x1, 0x4, 5, 5}, + [DVBT_EN_RF_AGC] = {0x1, 0x4, 6, 6}, + [DVBT_EN_IF_AGC] = {0x1, 0x4, 7, 7}, + [DVBT_IF_AGC_MIN] = {0x1, 0x8, 7, 0}, + [DVBT_IF_AGC_MAX] = {0x1, 0x9, 7, 0}, + [DVBT_RF_AGC_MIN] = {0x1, 0xa, 7, 0}, + [DVBT_RF_AGC_MAX] = {0x1, 0xb, 7, 0}, + [DVBT_IF_AGC_MAN] = {0x1, 0xc, 6, 6}, + [DVBT_IF_AGC_MAN_VAL] = {0x1, 0xc, 13, 0}, + [DVBT_RF_AGC_MAN] = {0x1, 0xe, 6, 6}, + [DVBT_RF_AGC_MAN_VAL] = {0x1, 0xe, 13, 0}, + [DVBT_DAGC_TRG_VAL] = {0x1, 0x12, 7, 0}, + [DVBT_AGC_TARG_VAL_0] = {0x1, 0x2, 0, 0}, + [DVBT_AGC_TARG_VAL_8_1] = {0x1, 0x3, 7, 0}, + [DVBT_AAGC_LOOP_GAIN] = {0x1, 0xc7, 5, 1}, + [DVBT_LOOP_GAIN2_3_0] = {0x1, 0x4, 4, 1}, + [DVBT_LOOP_GAIN2_4] = {0x1, 0x5, 7, 7}, + [DVBT_LOOP_GAIN3] = {0x1, 0xc8, 4, 0}, + [DVBT_VTOP1] = {0x1, 0x6, 5, 0}, + [DVBT_VTOP2] = {0x1, 0xc9, 5, 0}, + [DVBT_VTOP3] = {0x1, 0xca, 5, 0}, + [DVBT_KRF1] = {0x1, 0xcb, 7, 0}, + [DVBT_KRF2] = {0x1, 0x7, 7, 0}, + [DVBT_KRF3] = {0x1, 0xcd, 7, 0}, + [DVBT_KRF4] = {0x1, 0xce, 7, 0}, + [DVBT_EN_GI_PGA] = {0x1, 0xe5, 0, 0}, + [DVBT_THD_LOCK_UP] = {0x1, 0xd9, 8, 0}, + [DVBT_THD_LOCK_DW] = {0x1, 0xdb, 8, 0}, + [DVBT_THD_UP1] = {0x1, 0xdd, 7, 0}, + [DVBT_THD_DW1] = {0x1, 0xde, 7, 0}, + [DVBT_INTER_CNT_LEN] = {0x1, 0xd8, 3, 0}, + [DVBT_GI_PGA_STATE] = {0x1, 0xe6, 3, 3}, + [DVBT_EN_AGC_PGA] = {0x1, 0xd7, 0, 0}, + [DVBT_CKOUTPAR] = {0x1, 0x7b, 5, 5}, + [DVBT_CKOUT_PWR] = {0x1, 0x7b, 6, 6}, + [DVBT_SYNC_DUR] = {0x1, 0x7b, 7, 7}, + [DVBT_ERR_DUR] = {0x1, 0x7c, 0, 0}, + [DVBT_SYNC_LVL] = {0x1, 0x7c, 1, 1}, + [DVBT_ERR_LVL] = {0x1, 0x7c, 2, 2}, + [DVBT_VAL_LVL] = {0x1, 0x7c, 3, 3}, + [DVBT_SERIAL] = {0x1, 0x7c, 4, 4}, + [DVBT_SER_LSB] = {0x1, 0x7c, 5, 5}, + [DVBT_CDIV_PH0] = {0x1, 0x7d, 3, 0}, + [DVBT_CDIV_PH1] = {0x1, 0x7d, 7, 4}, + [DVBT_MPEG_IO_OPT_2_2] = {0x0, 0x6, 7, 7}, + [DVBT_MPEG_IO_OPT_1_0] = {0x0, 0x7, 7, 6}, + [DVBT_CKOUTPAR_PIP] = {0x0, 0xb7, 4, 4}, + [DVBT_CKOUT_PWR_PIP] = {0x0, 0xb7, 3, 3}, + [DVBT_SYNC_LVL_PIP] = {0x0, 0xb7, 2, 2}, + [DVBT_ERR_LVL_PIP] = {0x0, 0xb7, 1, 1}, + [DVBT_VAL_LVL_PIP] = {0x0, 0xb7, 0, 0}, + [DVBT_CKOUTPAR_PID] = {0x0, 0xb9, 4, 4}, + [DVBT_CKOUT_PWR_PID] = {0x0, 0xb9, 3, 3}, + [DVBT_SYNC_LVL_PID] = {0x0, 0xb9, 2, 2}, + [DVBT_ERR_LVL_PID] = {0x0, 0xb9, 1, 1}, + [DVBT_VAL_LVL_PID] = {0x0, 0xb9, 0, 0}, + [DVBT_SM_PASS] = {0x1, 0x93, 11, 0}, + [DVBT_AD7_SETTING] = {0x0, 0x11, 15, 0}, + [DVBT_RSSI_R] = {0x3, 0x1, 6, 0}, + [DVBT_ACI_DET_IND] = {0x3, 0x12, 0, 0}, + [DVBT_REG_MON] = {0x0, 0xd, 1, 0}, + [DVBT_REG_MONSEL] = {0x0, 0xd, 2, 2}, + [DVBT_REG_GPE] = {0x0, 0xd, 7, 7}, + [DVBT_REG_GPO] = {0x0, 0x10, 0, 0}, + [DVBT_REG_4MSEL] = {0x0, 0x13, 0, 0}, +}; + +/* write multiple hardware registers */ +static int rtl2832_wr(struct rtl2832_priv *priv, u8 reg, u8 *val, int len) +{ + int ret; + u8 buf[1+len]; + struct i2c_msg msg[1] = { + { + .addr = priv->cfg.i2c_addr, + .flags = 0, + .len = 1+len, + .buf = buf, + } + }; + + buf[0] = reg; + memcpy(&buf[1], val, len); + + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len); + ret = -EREMOTEIO; + } + return ret; +} + +/* read multiple hardware registers */ +static int rtl2832_rd(struct rtl2832_priv *priv, u8 reg, u8 *val, int len) +{ + int ret; + struct i2c_msg msg[2] = { + { + .addr = priv->cfg.i2c_addr, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = priv->cfg.i2c_addr, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + ret = i2c_transfer(priv->i2c, msg, 2); + if (ret == 2) { + ret = 0; + } else { + warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len); + ret = -EREMOTEIO; +} +return ret; +} + +/* write multiple registers */ +static int rtl2832_wr_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val, + int len) +{ + int ret; + + + /* switch bank if needed */ + if (page != priv->page) { + ret = rtl2832_wr(priv, 0x00, &page, 1); + if (ret) + return ret; + + priv->page = page; +} + +return rtl2832_wr(priv, reg, val, len); +} + +/* read multiple registers */ +static int rtl2832_rd_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val, + int len) +{ + int ret; + + /* switch bank if needed */ + if (page != priv->page) { + ret = rtl2832_wr(priv, 0x00, &page, 1); + if (ret) + return ret; + + priv->page = page; + } + + return rtl2832_rd(priv, reg, val, len); +} + +#if 0 /* currently not used */ +/* write single register */ +static int rtl2832_wr_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 val) +{ + return rtl2832_wr_regs(priv, reg, page, &val, 1); +} +#endif + +/* read single register */ +static int rtl2832_rd_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val) +{ + return rtl2832_rd_regs(priv, reg, page, val, 1); +} + +int rtl2832_rd_demod_reg(struct rtl2832_priv *priv, int reg, u32 *val) +{ + int ret; + + u8 reg_start_addr; + u8 msb, lsb; + u8 page; + u8 reading[4]; + u32 reading_tmp; + int i; + + u8 len; + u32 mask; + + reg_start_addr = registers[reg].start_address; + msb = registers[reg].msb; + lsb = registers[reg].lsb; + page = registers[reg].page; + + len = (msb >> 3) + 1; + mask = REG_MASK(msb - lsb); + + ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len); + if (ret) + goto err; + + reading_tmp = 0; + for (i = 0; i < len; i++) + reading_tmp |= reading[i] << ((len - 1 - i) * 8); + + *val = (reading_tmp >> lsb) & mask; + + return ret; + +err: + dbg("%s: failed=%d", __func__, ret); + return ret; + +} + +int rtl2832_wr_demod_reg(struct rtl2832_priv *priv, int reg, u32 val) +{ + int ret, i; + u8 len; + u8 reg_start_addr; + u8 msb, lsb; + u8 page; + u32 mask; + + + u8 reading[4]; + u8 writing[4]; + u32 reading_tmp; + u32 writing_tmp; + + + reg_start_addr = registers[reg].start_address; + msb = registers[reg].msb; + lsb = registers[reg].lsb; + page = registers[reg].page; + + len = (msb >> 3) + 1; + mask = REG_MASK(msb - lsb); + + + ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len); + if (ret) + goto err; + + reading_tmp = 0; + for (i = 0; i < len; i++) + reading_tmp |= reading[i] << ((len - 1 - i) * 8); + + writing_tmp = reading_tmp & ~(mask << lsb); + writing_tmp |= ((val & mask) << lsb); + + + for (i = 0; i < len; i++) + writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff; + + ret = rtl2832_wr_regs(priv, reg_start_addr, page, &writing[0], len); + if (ret) + goto err; + + return ret; + +err: + dbg("%s: failed=%d", __func__, ret); + return ret; + +} + + +static int rtl2832_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + int ret; + struct rtl2832_priv *priv = fe->demodulator_priv; + + dbg("%s: enable=%d", __func__, enable); + + /* gate already open or close */ + if (priv->i2c_gate_state == enable) + return 0; + + ret = rtl2832_wr_demod_reg(priv, DVBT_IIC_REPEAT, (enable ? 0x1 : 0x0)); + if (ret) + goto err; + + priv->i2c_gate_state = enable; + + return ret; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + + + +static int rtl2832_init(struct dvb_frontend *fe) +{ + struct rtl2832_priv *priv = fe->demodulator_priv; + int i, ret; + + u8 en_bbin; + u64 pset_iffreq; + + /* initialization values for the demodulator registers */ + struct rtl2832_reg_value rtl2832_initial_regs[] = { + {DVBT_AD_EN_REG, 0x1}, + {DVBT_AD_EN_REG1, 0x1}, + {DVBT_RSD_BER_FAIL_VAL, 0x2800}, + {DVBT_MGD_THD0, 0x10}, + {DVBT_MGD_THD1, 0x20}, + {DVBT_MGD_THD2, 0x20}, + {DVBT_MGD_THD3, 0x40}, + {DVBT_MGD_THD4, 0x22}, + {DVBT_MGD_THD5, 0x32}, + {DVBT_MGD_THD6, 0x37}, + {DVBT_MGD_THD7, 0x39}, + {DVBT_EN_BK_TRK, 0x0}, + {DVBT_EN_CACQ_NOTCH, 0x0}, + {DVBT_AD_AV_REF, 0x2a}, + {DVBT_REG_PI, 0x6}, + {DVBT_PIP_ON, 0x0}, + {DVBT_CDIV_PH0, 0x8}, + {DVBT_CDIV_PH1, 0x8}, + {DVBT_SCALE1_B92, 0x4}, + {DVBT_SCALE1_B93, 0xb0}, + {DVBT_SCALE1_BA7, 0x78}, + {DVBT_SCALE1_BA9, 0x28}, + {DVBT_SCALE1_BAA, 0x59}, + {DVBT_SCALE1_BAB, 0x83}, + {DVBT_SCALE1_BAC, 0xd4}, + {DVBT_SCALE1_BB0, 0x65}, + {DVBT_SCALE1_BB1, 0x43}, + {DVBT_KB_P1, 0x1}, + {DVBT_KB_P2, 0x4}, + {DVBT_KB_P3, 0x7}, + {DVBT_K1_CR_STEP12, 0xa}, + {DVBT_REG_GPE, 0x1}, + {DVBT_SERIAL, 0x0}, + {DVBT_CDIV_PH0, 0x9}, + {DVBT_CDIV_PH1, 0x9}, + {DVBT_MPEG_IO_OPT_2_2, 0x0}, + {DVBT_MPEG_IO_OPT_1_0, 0x0}, + {DVBT_TRK_KS_P2, 0x4}, + {DVBT_TRK_KS_I2, 0x7}, + {DVBT_TR_THD_SET2, 0x6}, + {DVBT_TRK_KC_I2, 0x5}, + {DVBT_CR_THD_SET2, 0x1}, + {DVBT_SPEC_INV, 0x0}, + {DVBT_DAGC_TRG_VAL, 0x5a}, + {DVBT_AGC_TARG_VAL_0, 0x0}, + {DVBT_AGC_TARG_VAL_8_1, 0x5a}, + {DVBT_AAGC_LOOP_GAIN, 0x16}, + {DVBT_LOOP_GAIN2_3_0, 0x6}, + {DVBT_LOOP_GAIN2_4, 0x1}, + {DVBT_LOOP_GAIN3, 0x16}, + {DVBT_VTOP1, 0x35}, + {DVBT_VTOP2, 0x21}, + {DVBT_VTOP3, 0x21}, + {DVBT_KRF1, 0x0}, + {DVBT_KRF2, 0x40}, + {DVBT_KRF3, 0x10}, + {DVBT_KRF4, 0x10}, + {DVBT_IF_AGC_MIN, 0x80}, + {DVBT_IF_AGC_MAX, 0x7f}, + {DVBT_RF_AGC_MIN, 0x80}, + {DVBT_RF_AGC_MAX, 0x7f}, + {DVBT_POLAR_RF_AGC, 0x0}, + {DVBT_POLAR_IF_AGC, 0x0}, + {DVBT_AD7_SETTING, 0xe9bf}, + {DVBT_EN_GI_PGA, 0x0}, + {DVBT_THD_LOCK_UP, 0x0}, + {DVBT_THD_LOCK_DW, 0x0}, + {DVBT_THD_UP1, 0x11}, + {DVBT_THD_DW1, 0xef}, + {DVBT_INTER_CNT_LEN, 0xc}, + {DVBT_GI_PGA_STATE, 0x0}, + {DVBT_EN_AGC_PGA, 0x1}, + {DVBT_IF_AGC_MAN, 0x0}, + }; + + + dbg("%s", __func__); + + en_bbin = (priv->cfg.if_dvbt == 0 ? 0x1 : 0x0); + + /* + * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22) + * / CrystalFreqHz) + */ + pset_iffreq = priv->cfg.if_dvbt % priv->cfg.xtal; + pset_iffreq *= 0x400000; + pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal); + pset_iffreq = pset_iffreq & 0x3fffff; + + + + for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) { + ret = rtl2832_wr_demod_reg(priv, rtl2832_initial_regs[i].reg, + rtl2832_initial_regs[i].value); + if (ret) + goto err; + } + + /* if frequency settings */ + ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin); + if (ret) + goto err; + + ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq); + if (ret) + goto err; + + priv->sleeping = false; + + return ret; + +err: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int rtl2832_sleep(struct dvb_frontend *fe) +{ + struct rtl2832_priv *priv = fe->demodulator_priv; + + dbg("%s", __func__); + priv->sleeping = true; + return 0; +} + +int rtl2832_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + dbg("%s", __func__); + s->min_delay_ms = 1000; + s->step_size = fe->ops.info.frequency_stepsize * 2; + s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; + return 0; +} + +static int rtl2832_set_frontend(struct dvb_frontend *fe) +{ + struct rtl2832_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i, j; + u64 bw_mode, num, num2; + u32 resamp_ratio, cfreq_off_ratio; + + + static u8 bw_params[3][32] = { + /* 6 MHz bandwidth */ + { + 0xf5, 0xff, 0x15, 0x38, 0x5d, 0x6d, 0x52, 0x07, 0xfa, 0x2f, + 0x53, 0xf5, 0x3f, 0xca, 0x0b, 0x91, 0xea, 0x30, 0x63, 0xb2, + 0x13, 0xda, 0x0b, 0xc4, 0x18, 0x7e, 0x16, 0x66, 0x08, 0x67, + 0x19, 0xe0, + }, + + /* 7 MHz bandwidth */ + { + 0xe7, 0xcc, 0xb5, 0xba, 0xe8, 0x2f, 0x67, 0x61, 0x00, 0xaf, + 0x86, 0xf2, 0xbf, 0x59, 0x04, 0x11, 0xb6, 0x33, 0xa4, 0x30, + 0x15, 0x10, 0x0a, 0x42, 0x18, 0xf8, 0x17, 0xd9, 0x07, 0x22, + 0x19, 0x10, + }, + + /* 8 MHz bandwidth */ + { + 0x09, 0xf6, 0xd2, 0xa7, 0x9a, 0xc9, 0x27, 0x77, 0x06, 0xbf, + 0xec, 0xf4, 0x4f, 0x0b, 0xfc, 0x01, 0x63, 0x35, 0x54, 0xa7, + 0x16, 0x66, 0x08, 0xb4, 0x19, 0x6e, 0x19, 0x65, 0x05, 0xc8, + 0x19, 0xe0, + }, + }; + + + dbg("%s: frequency=%d bandwidth_hz=%d inversion=%d", __func__, + c->frequency, c->bandwidth_hz, c->inversion); + + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + + switch (c->bandwidth_hz) { + case 6000000: + i = 0; + bw_mode = 48000000; + break; + case 7000000: + i = 1; + bw_mode = 56000000; + break; + case 8000000: + i = 2; + bw_mode = 64000000; + break; + default: + dbg("invalid bandwidth"); + return -EINVAL; + } + + for (j = 0; j < sizeof(bw_params[j]); j++) { + ret = rtl2832_wr_regs(priv, 0x1c+j, 1, &bw_params[i][j], 1); + if (ret) + goto err; + } + + /* calculate and set resample ratio + * RSAMP_RATIO = floor(CrystalFreqHz * 7 * pow(2, 22) + * / ConstWithBandwidthMode) + */ + num = priv->cfg.xtal * 7; + num *= 0x400000; + num = div_u64(num, bw_mode); + resamp_ratio = num & 0x3ffffff; + ret = rtl2832_wr_demod_reg(priv, DVBT_RSAMP_RATIO, resamp_ratio); + if (ret) + goto err; + + /* calculate and set cfreq off ratio + * CFREQ_OFF_RATIO = - floor(ConstWithBandwidthMode * pow(2, 20) + * / (CrystalFreqHz * 7)) + */ + num = bw_mode << 20; + num2 = priv->cfg.xtal * 7; + num = div_u64(num, num2); + num = -num; + cfreq_off_ratio = num & 0xfffff; + ret = rtl2832_wr_demod_reg(priv, DVBT_CFREQ_OFF_RATIO, cfreq_off_ratio); + if (ret) + goto err; + + + /* soft reset */ + ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1); + if (ret) + goto err; + + ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0); + if (ret) + goto err; + + return ret; +err: + info("%s: failed=%d", __func__, ret); + return ret; +} + +static int rtl2832_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct rtl2832_priv *priv = fe->demodulator_priv; + int ret; + u32 tmp; + *status = 0; + + + dbg("%s", __func__); + if (priv->sleeping) + return 0; + + ret = rtl2832_rd_demod_reg(priv, DVBT_FSM_STAGE, &tmp); + if (ret) + goto err; + + if (tmp == 11) { + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } + /* TODO find out if this is also true for rtl2832? */ + /*else if (tmp == 10) { + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + }*/ + + return ret; +err: + info("%s: failed=%d", __func__, ret); + return ret; +} + +static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + *snr = 0; + return 0; +} + +static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + return 0; +} + +static int rtl2832_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + return 0; +} + + +static int rtl2832_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + *strength = 0; + return 0; +} + +static struct dvb_frontend_ops rtl2832_ops; + +static void rtl2832_release(struct dvb_frontend *fe) +{ + struct rtl2832_priv *priv = fe->demodulator_priv; + + dbg("%s", __func__); + kfree(priv); +} + +struct dvb_frontend *rtl2832_attach(const struct rtl2832_config *cfg, + struct i2c_adapter *i2c) +{ + struct rtl2832_priv *priv = NULL; + int ret = 0; + u8 tmp; + + dbg("%s", __func__); + + /* allocate memory for the internal state */ + priv = kzalloc(sizeof(struct rtl2832_priv), GFP_KERNEL); + if (priv == NULL) + goto err; + + /* setup the priv */ + priv->i2c = i2c; + priv->tuner = cfg->tuner; + memcpy(&priv->cfg, cfg, sizeof(struct rtl2832_config)); + + /* check if the demod is there */ + ret = rtl2832_rd_reg(priv, 0x00, 0x0, &tmp); + if (ret) + goto err; + + /* create dvb_frontend */ + memcpy(&priv->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops)); + priv->fe.demodulator_priv = priv; + + /* TODO implement sleep mode */ + priv->sleeping = true; + + return &priv->fe; +err: + dbg("%s: failed=%d", __func__, ret); + kfree(priv); + return NULL; +} +EXPORT_SYMBOL(rtl2832_attach); + +static struct dvb_frontend_ops rtl2832_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Realtek RTL2832 (DVB-T)", + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 166667, + .caps = 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_HIERARCHY_AUTO | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + + .release = rtl2832_release, + + .init = rtl2832_init, + .sleep = rtl2832_sleep, + + .get_tune_settings = rtl2832_get_tune_settings, + + .set_frontend = rtl2832_set_frontend, + + .read_status = rtl2832_read_status, + .read_snr = rtl2832_read_snr, + .read_ber = rtl2832_read_ber, + .read_ucblocks = rtl2832_read_ucblocks, + .read_signal_strength = rtl2832_read_signal_strength, + .i2c_gate_ctrl = rtl2832_i2c_gate_ctrl, +}; + +MODULE_AUTHOR("Thomas Mair <mair.thomas86@gmail.com>"); +MODULE_DESCRIPTION("Realtek RTL2832 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.5"); diff --git a/drivers/media/dvb/frontends/rtl2832.h b/drivers/media/dvb/frontends/rtl2832.h new file mode 100644 index 000000000000..d94dc9a3fa62 --- /dev/null +++ b/drivers/media/dvb/frontends/rtl2832.h @@ -0,0 +1,74 @@ +/* + * Realtek RTL2832 DVB-T demodulator driver + * + * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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. + */ + +#ifndef RTL2832_H +#define RTL2832_H + +#include <linux/dvb/frontend.h> + +struct rtl2832_config { + /* + * Demodulator I2C address. + */ + u8 i2c_addr; + + /* + * Xtal frequency. + * Hz + * 4000000, 16000000, 25000000, 28800000 + */ + u32 xtal; + + /* + * IFs for all used modes. + * Hz + * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000 + */ + u32 if_dvbt; + + /* + */ + u8 tuner; +}; + + +#if defined(CONFIG_DVB_RTL2832) || \ + (defined(CONFIG_DVB_RTL2832_MODULE) && defined(MODULE)) +extern struct dvb_frontend *rtl2832_attach( + const struct rtl2832_config *cfg, + struct i2c_adapter *i2c +); + +extern struct i2c_adapter *rtl2832_get_tuner_i2c_adapter( + struct dvb_frontend *fe +); +#else +static inline struct dvb_frontend *rtl2832_attach( + const struct rtl2832_config *config, + struct i2c_adapter *i2c +) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + + +#endif /* RTL2832_H */ diff --git a/drivers/media/dvb/frontends/rtl2832_priv.h b/drivers/media/dvb/frontends/rtl2832_priv.h new file mode 100644 index 000000000000..0ce9502da8ba --- /dev/null +++ b/drivers/media/dvb/frontends/rtl2832_priv.h @@ -0,0 +1,260 @@ +/* + * Realtek RTL2832 DVB-T demodulator driver + * + * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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. + */ + +#ifndef RTL2832_PRIV_H +#define RTL2832_PRIV_H + +#include "dvb_frontend.h" +#include "rtl2832.h" + +#define LOG_PREFIX "rtl2832" + +#undef dbg +#define dbg(f, arg...) \ +do { \ + if (rtl2832_debug) \ + printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg); \ +} while (0) +#undef err +#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +#undef info +#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef warn +#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) + +struct rtl2832_priv { + struct i2c_adapter *i2c; + struct dvb_frontend fe; + struct rtl2832_config cfg; + + bool i2c_gate_state; + bool sleeping; + + u8 tuner; + u8 page; /* active register page */ +}; + +struct rtl2832_reg_entry { + u8 page; + u8 start_address; + u8 msb; + u8 lsb; +}; + +struct rtl2832_reg_value { + int reg; + u32 value; +}; + + +/* Demod register bit names */ +enum DVBT_REG_BIT_NAME { + DVBT_SOFT_RST, + DVBT_IIC_REPEAT, + DVBT_TR_WAIT_MIN_8K, + DVBT_RSD_BER_FAIL_VAL, + DVBT_EN_BK_TRK, + DVBT_REG_PI, + DVBT_REG_PFREQ_1_0, + DVBT_PD_DA8, + DVBT_LOCK_TH, + DVBT_BER_PASS_SCAL, + DVBT_CE_FFSM_BYPASS, + DVBT_ALPHAIIR_N, + DVBT_ALPHAIIR_DIF, + DVBT_EN_TRK_SPAN, + DVBT_LOCK_TH_LEN, + DVBT_CCI_THRE, + DVBT_CCI_MON_SCAL, + DVBT_CCI_M0, + DVBT_CCI_M1, + DVBT_CCI_M2, + DVBT_CCI_M3, + DVBT_SPEC_INIT_0, + DVBT_SPEC_INIT_1, + DVBT_SPEC_INIT_2, + DVBT_AD_EN_REG, + DVBT_AD_EN_REG1, + DVBT_EN_BBIN, + DVBT_MGD_THD0, + DVBT_MGD_THD1, + DVBT_MGD_THD2, + DVBT_MGD_THD3, + DVBT_MGD_THD4, + DVBT_MGD_THD5, + DVBT_MGD_THD6, + DVBT_MGD_THD7, + DVBT_EN_CACQ_NOTCH, + DVBT_AD_AV_REF, + DVBT_PIP_ON, + DVBT_SCALE1_B92, + DVBT_SCALE1_B93, + DVBT_SCALE1_BA7, + DVBT_SCALE1_BA9, + DVBT_SCALE1_BAA, + DVBT_SCALE1_BAB, + DVBT_SCALE1_BAC, + DVBT_SCALE1_BB0, + DVBT_SCALE1_BB1, + DVBT_KB_P1, + DVBT_KB_P2, + DVBT_KB_P3, + DVBT_OPT_ADC_IQ, + DVBT_AD_AVI, + DVBT_AD_AVQ, + DVBT_K1_CR_STEP12, + DVBT_TRK_KS_P2, + DVBT_TRK_KS_I2, + DVBT_TR_THD_SET2, + DVBT_TRK_KC_P2, + DVBT_TRK_KC_I2, + DVBT_CR_THD_SET2, + DVBT_PSET_IFFREQ, + DVBT_SPEC_INV, + DVBT_BW_INDEX, + DVBT_RSAMP_RATIO, + DVBT_CFREQ_OFF_RATIO, + DVBT_FSM_STAGE, + DVBT_RX_CONSTEL, + DVBT_RX_HIER, + DVBT_RX_C_RATE_LP, + DVBT_RX_C_RATE_HP, + DVBT_GI_IDX, + DVBT_FFT_MODE_IDX, + DVBT_RSD_BER_EST, + DVBT_CE_EST_EVM, + DVBT_RF_AGC_VAL, + DVBT_IF_AGC_VAL, + DVBT_DAGC_VAL, + DVBT_SFREQ_OFF, + DVBT_CFREQ_OFF, + DVBT_POLAR_RF_AGC, + DVBT_POLAR_IF_AGC, + DVBT_AAGC_HOLD, + DVBT_EN_RF_AGC, + DVBT_EN_IF_AGC, + DVBT_IF_AGC_MIN, + DVBT_IF_AGC_MAX, + DVBT_RF_AGC_MIN, + DVBT_RF_AGC_MAX, + DVBT_IF_AGC_MAN, + DVBT_IF_AGC_MAN_VAL, + DVBT_RF_AGC_MAN, + DVBT_RF_AGC_MAN_VAL, + DVBT_DAGC_TRG_VAL, + DVBT_AGC_TARG_VAL, + DVBT_LOOP_GAIN_3_0, + DVBT_LOOP_GAIN_4, + DVBT_VTOP, + DVBT_KRF, + DVBT_AGC_TARG_VAL_0, + DVBT_AGC_TARG_VAL_8_1, + DVBT_AAGC_LOOP_GAIN, + DVBT_LOOP_GAIN2_3_0, + DVBT_LOOP_GAIN2_4, + DVBT_LOOP_GAIN3, + DVBT_VTOP1, + DVBT_VTOP2, + DVBT_VTOP3, + DVBT_KRF1, + DVBT_KRF2, + DVBT_KRF3, + DVBT_KRF4, + DVBT_EN_GI_PGA, + DVBT_THD_LOCK_UP, + DVBT_THD_LOCK_DW, + DVBT_THD_UP1, + DVBT_THD_DW1, + DVBT_INTER_CNT_LEN, + DVBT_GI_PGA_STATE, + DVBT_EN_AGC_PGA, + DVBT_CKOUTPAR, + DVBT_CKOUT_PWR, + DVBT_SYNC_DUR, + DVBT_ERR_DUR, + DVBT_SYNC_LVL, + DVBT_ERR_LVL, + DVBT_VAL_LVL, + DVBT_SERIAL, + DVBT_SER_LSB, + DVBT_CDIV_PH0, + DVBT_CDIV_PH1, + DVBT_MPEG_IO_OPT_2_2, + DVBT_MPEG_IO_OPT_1_0, + DVBT_CKOUTPAR_PIP, + DVBT_CKOUT_PWR_PIP, + DVBT_SYNC_LVL_PIP, + DVBT_ERR_LVL_PIP, + DVBT_VAL_LVL_PIP, + DVBT_CKOUTPAR_PID, + DVBT_CKOUT_PWR_PID, + DVBT_SYNC_LVL_PID, + DVBT_ERR_LVL_PID, + DVBT_VAL_LVL_PID, + DVBT_SM_PASS, + DVBT_UPDATE_REG_2, + DVBT_BTHD_P3, + DVBT_BTHD_D3, + DVBT_FUNC4_REG0, + DVBT_FUNC4_REG1, + DVBT_FUNC4_REG2, + DVBT_FUNC4_REG3, + DVBT_FUNC4_REG4, + DVBT_FUNC4_REG5, + DVBT_FUNC4_REG6, + DVBT_FUNC4_REG7, + DVBT_FUNC4_REG8, + DVBT_FUNC4_REG9, + DVBT_FUNC4_REG10, + DVBT_FUNC5_REG0, + DVBT_FUNC5_REG1, + DVBT_FUNC5_REG2, + DVBT_FUNC5_REG3, + DVBT_FUNC5_REG4, + DVBT_FUNC5_REG5, + DVBT_FUNC5_REG6, + DVBT_FUNC5_REG7, + DVBT_FUNC5_REG8, + DVBT_FUNC5_REG9, + DVBT_FUNC5_REG10, + DVBT_FUNC5_REG11, + DVBT_FUNC5_REG12, + DVBT_FUNC5_REG13, + DVBT_FUNC5_REG14, + DVBT_FUNC5_REG15, + DVBT_FUNC5_REG16, + DVBT_FUNC5_REG17, + DVBT_FUNC5_REG18, + DVBT_AD7_SETTING, + DVBT_RSSI_R, + DVBT_ACI_DET_IND, + DVBT_REG_MON, + DVBT_REG_MONSEL, + DVBT_REG_GPE, + DVBT_REG_GPO, + DVBT_REG_4MSEL, + DVBT_TEST_REG_1, + DVBT_TEST_REG_2, + DVBT_TEST_REG_3, + DVBT_TEST_REG_4, + DVBT_REG_BIT_NAME_ITEM_TERMINATOR, +}; + +#endif /* RTL2832_PRIV_H */ diff --git a/drivers/media/dvb/frontends/s5h1420.c b/drivers/media/dvb/frontends/s5h1420.c index 2322257c69ae..e2fec9ebf947 100644 --- a/drivers/media/dvb/frontends/s5h1420.c +++ b/drivers/media/dvb/frontends/s5h1420.c @@ -634,7 +634,6 @@ static int s5h1420_set_frontend(struct dvb_frontend *fe) struct s5h1420_state* state = fe->demodulator_priv; int frequency_delta; struct dvb_frontend_tune_settings fesettings; - uint8_t clock_setting; dprintk("enter %s\n", __func__); @@ -679,25 +678,6 @@ static int s5h1420_set_frontend(struct dvb_frontend *fe) else state->fclk = 44000000; - /* Clock */ - switch (state->fclk) { - default: - case 88000000: - clock_setting = 80; - break; - case 86000000: - clock_setting = 78; - break; - case 80000000: - clock_setting = 72; - break; - case 59000000: - clock_setting = 51; - break; - case 44000000: - clock_setting = 36; - break; - } dprintk("pll01: %d, ToneFreq: %d\n", state->fclk/1000000 - 8, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32)); s5h1420_writereg(state, PLL01, state->fclk/1000000 - 8); s5h1420_writereg(state, PLL02, 0x40); diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c index 8b0dc74a3298..5d7f8a9b451b 100644 --- a/drivers/media/dvb/frontends/stb0899_drv.c +++ b/drivers/media/dvb/frontends/stb0899_drv.c @@ -1129,7 +1129,6 @@ static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber) struct stb0899_internal *internal = &state->internal; u8 lsb, msb; - u32 i; *ber = 0; @@ -1137,14 +1136,9 @@ static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber) case SYS_DVBS: case SYS_DSS: if (internal->lock) { - /* average 5 BER values */ - for (i = 0; i < 5; i++) { - msleep(100); - lsb = stb0899_read_reg(state, STB0899_ECNT1L); - msb = stb0899_read_reg(state, STB0899_ECNT1M); - *ber += MAKEWORD16(msb, lsb); - } - *ber /= 5; + lsb = stb0899_read_reg(state, STB0899_ECNT1L); + msb = stb0899_read_reg(state, STB0899_ECNT1M); + *ber = MAKEWORD16(msb, lsb); /* Viterbi Check */ if (STB0899_GETFIELD(VSTATUS_PRFVIT, internal->v_status)) { /* Error Rate */ @@ -1157,13 +1151,9 @@ static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber) break; case SYS_DVBS2: if (internal->lock) { - /* Average 5 PER values */ - for (i = 0; i < 5; i++) { - msleep(100); - lsb = stb0899_read_reg(state, STB0899_ECNT1L); - msb = stb0899_read_reg(state, STB0899_ECNT1M); - *ber += MAKEWORD16(msb, lsb); - } + lsb = stb0899_read_reg(state, STB0899_ECNT1L); + msb = stb0899_read_reg(state, STB0899_ECNT1M); + *ber = MAKEWORD16(msb, lsb); /* ber = ber * 10 ^ 7 */ *ber *= 10000000; *ber /= (-1 + (1 << (4 + 2 * STB0899_GETFIELD(NOE, internal->err_ctrl)))); diff --git a/drivers/media/dvb/frontends/stv0367.c b/drivers/media/dvb/frontends/stv0367.c index fdd20c7737b5..2a8aaeb1112d 100644 --- a/drivers/media/dvb/frontends/stv0367.c +++ b/drivers/media/dvb/frontends/stv0367.c @@ -1584,7 +1584,7 @@ static int stv0367ter_algo(struct dvb_frontend *fe) struct stv0367ter_state *ter_state = state->ter_state; int offset = 0, tempo = 0; u8 u_var; - u8 /*constell,*/ counter, tps_rcvd[2]; + u8 /*constell,*/ counter; s8 step; s32 timing_offset = 0; u32 trl_nomrate = 0, InternalFreq = 0, temp = 0; @@ -1709,9 +1709,6 @@ static int stv0367ter_algo(struct dvb_frontend *fe) return 0; ter_state->state = FE_TER_LOCKOK; - /* update results */ - tps_rcvd[0] = stv0367_readreg(state, R367TER_TPS_RCVD2); - tps_rcvd[1] = stv0367_readreg(state, R367TER_TPS_RCVD3); ter_state->mode = stv0367_readbits(state, F367TER_SYR_MODE); ter_state->guard = stv0367_readbits(state, F367TER_SYR_GUARD); diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index d79e69f65cbb..ea86a5603e57 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -3172,7 +3172,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) enum stv090x_signal_state signal_state = STV090x_NOCARRIER; u32 reg; s32 agc1_power, power_iq = 0, i; - int lock = 0, low_sr = 0, no_signal = 0; + int lock = 0, low_sr = 0; reg = STV090x_READ_DEMOD(state, TSCFGH); STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 1); /* Stop path 1 stream merger */ @@ -3413,7 +3413,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) goto err; } else { signal_state = STV090x_NODATA; - no_signal = stv090x_chk_signal(state); + stv090x_chk_signal(state); } } return signal_state; diff --git a/drivers/media/dvb/frontends/tda10071.c b/drivers/media/dvb/frontends/tda10071.c index c21bc92d2811..703c3d05f9f4 100644 --- a/drivers/media/dvb/frontends/tda10071.c +++ b/drivers/media/dvb/frontends/tda10071.c @@ -20,10 +20,6 @@ #include "tda10071_priv.h" -int tda10071_debug; -module_param_named(debug, tda10071_debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); - static struct dvb_frontend_ops tda10071_ops; /* write multiple registers */ @@ -48,7 +44,8 @@ static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, if (ret == 1) { ret = 0; } else { - warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len); + dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \ + "len=%d\n", KBUILD_MODNAME, ret, reg, len); ret = -EREMOTEIO; } return ret; @@ -79,7 +76,8 @@ static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val, memcpy(val, buf, len); ret = 0; } else { - warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len); + dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \ + "len=%d\n", KBUILD_MODNAME, ret, reg, len); ret = -EREMOTEIO; } return ret; @@ -170,7 +168,7 @@ static int tda10071_cmd_execute(struct tda10071_priv *priv, usleep_range(200, 5000); } - dbg("%s: loop=%d", __func__, i); + dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); if (i == 0) { ret = -ETIMEDOUT; @@ -179,7 +177,7 @@ static int tda10071_cmd_execute(struct tda10071_priv *priv, return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -196,7 +194,8 @@ static int tda10071_set_tone(struct dvb_frontend *fe, goto error; } - dbg("%s: tone_mode=%d", __func__, fe_sec_tone_mode); + dev_dbg(&priv->i2c->dev, "%s: tone_mode=%d\n", __func__, + fe_sec_tone_mode); switch (fe_sec_tone_mode) { case SEC_TONE_ON: @@ -206,24 +205,25 @@ static int tda10071_set_tone(struct dvb_frontend *fe, tone = 0; break; default: - dbg("%s: invalid fe_sec_tone_mode", __func__); + dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_tone_mode\n", + __func__); ret = -EINVAL; goto error; } - cmd.args[0x00] = CMD_LNB_PCB_CONFIG; - cmd.args[0x01] = 0; - cmd.args[0x02] = 0x00; - cmd.args[0x03] = 0x00; - cmd.args[0x04] = tone; - cmd.len = 0x05; + cmd.args[0] = CMD_LNB_PCB_CONFIG; + cmd.args[1] = 0; + cmd.args[2] = 0x00; + cmd.args[3] = 0x00; + cmd.args[4] = tone; + cmd.len = 5; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -240,7 +240,7 @@ static int tda10071_set_voltage(struct dvb_frontend *fe, goto error; } - dbg("%s: voltage=%d", __func__, fe_sec_voltage); + dev_dbg(&priv->i2c->dev, "%s: voltage=%d\n", __func__, fe_sec_voltage); switch (fe_sec_voltage) { case SEC_VOLTAGE_13: @@ -253,22 +253,23 @@ static int tda10071_set_voltage(struct dvb_frontend *fe, voltage = 0; break; default: - dbg("%s: invalid fe_sec_voltage", __func__); + dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_voltage\n", + __func__); ret = -EINVAL; goto error; }; - cmd.args[0x00] = CMD_LNB_SET_DC_LEVEL; - cmd.args[0x01] = 0; - cmd.args[0x02] = voltage; - cmd.len = 0x03; + cmd.args[0] = CMD_LNB_SET_DC_LEVEL; + cmd.args[1] = 0; + cmd.args[2] = voltage; + cmd.len = 3; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -285,9 +286,10 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, goto error; } - dbg("%s: msg_len=%d", __func__, diseqc_cmd->msg_len); + dev_dbg(&priv->i2c->dev, "%s: msg_len=%d\n", __func__, + diseqc_cmd->msg_len); - if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 16) { + if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) { ret = -EINVAL; goto error; } @@ -301,7 +303,7 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, usleep_range(10000, 20000); } - dbg("%s: loop=%d", __func__, i); + dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); if (i == 0) { ret = -ETIMEDOUT; @@ -312,22 +314,22 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, if (ret) goto error; - cmd.args[0x00] = CMD_LNB_SEND_DISEQC; - cmd.args[0x01] = 0; - cmd.args[0x02] = 0; - cmd.args[0x03] = 0; - cmd.args[0x04] = 2; - cmd.args[0x05] = 0; - cmd.args[0x06] = diseqc_cmd->msg_len; - memcpy(&cmd.args[0x07], diseqc_cmd->msg, diseqc_cmd->msg_len); - cmd.len = 0x07 + diseqc_cmd->msg_len; + cmd.args[0] = CMD_LNB_SEND_DISEQC; + cmd.args[1] = 0; + cmd.args[2] = 0; + cmd.args[3] = 0; + cmd.args[4] = 2; + cmd.args[5] = 0; + cmd.args[6] = diseqc_cmd->msg_len; + memcpy(&cmd.args[7], diseqc_cmd->msg, diseqc_cmd->msg_len); + cmd.len = 7 + diseqc_cmd->msg_len; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -344,7 +346,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, goto error; } - dbg("%s:", __func__); + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); /* wait LNB RX */ for (i = 500, tmp = 0; i && !tmp; i--) { @@ -355,7 +357,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, usleep_range(10000, 20000); } - dbg("%s: loop=%d", __func__, i); + dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); if (i == 0) { ret = -ETIMEDOUT; @@ -372,9 +374,9 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, reply->msg_len = sizeof(reply->msg); /* truncate API max */ /* read reply */ - cmd.args[0x00] = CMD_LNB_UPDATE_REPLY; - cmd.args[0x01] = 0; - cmd.len = 0x02; + cmd.args[0] = CMD_LNB_UPDATE_REPLY; + cmd.args[1] = 0; + cmd.len = 2; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; @@ -385,7 +387,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -402,7 +404,8 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, goto error; } - dbg("%s: fe_sec_mini_cmd=%d", __func__, fe_sec_mini_cmd); + dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__, + fe_sec_mini_cmd); switch (fe_sec_mini_cmd) { case SEC_MINI_A: @@ -412,7 +415,8 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, burst = 1; break; default: - dbg("%s: invalid fe_sec_mini_cmd", __func__); + dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_mini_cmd\n", + __func__); ret = -EINVAL; goto error; } @@ -426,7 +430,7 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, usleep_range(10000, 20000); } - dbg("%s: loop=%d", __func__, i); + dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); if (i == 0) { ret = -ETIMEDOUT; @@ -437,17 +441,17 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, if (ret) goto error; - cmd.args[0x00] = CMD_LNB_SEND_TONEBURST; - cmd.args[0x01] = 0; - cmd.args[0x02] = burst; - cmd.len = 0x03; + cmd.args[0] = CMD_LNB_SEND_TONEBURST; + cmd.args[1] = 0; + cmd.args[2] = burst; + cmd.len = 3; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -481,7 +485,7 @@ static int tda10071_read_status(struct dvb_frontend *fe, fe_status_t *status) return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -506,7 +510,7 @@ static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr) return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -523,9 +527,9 @@ static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) goto error; } - cmd.args[0x00] = CMD_GET_AGCACC; - cmd.args[0x01] = 0; - cmd.len = 0x02; + cmd.args[0] = CMD_GET_AGCACC; + cmd.args[1] = 0; + cmd.len = 2; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; @@ -545,7 +549,7 @@ static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -583,17 +587,18 @@ static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) goto error; if (priv->meas_count[i] == tmp) { - dbg("%s: meas not ready=%02x", __func__, tmp); + dev_dbg(&priv->i2c->dev, "%s: meas not ready=%02x\n", __func__, + tmp); *ber = priv->ber; return 0; } else { priv->meas_count[i] = tmp; } - cmd.args[0x00] = CMD_BER_UPDATE_COUNTERS; - cmd.args[0x01] = 0; - cmd.args[0x02] = i; - cmd.len = 0x03; + cmd.args[0] = CMD_BER_UPDATE_COUNTERS; + cmd.args[1] = 0; + cmd.args[2] = i; + cmd.len = 3; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; @@ -612,7 +617,7 @@ static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -632,7 +637,7 @@ static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -644,10 +649,11 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) int ret, i; u8 mode, rolloff, pilot, inversion, div; - dbg("%s: delivery_system=%d modulation=%d frequency=%d " \ - "symbol_rate=%d inversion=%d pilot=%d rolloff=%d", __func__, - c->delivery_system, c->modulation, c->frequency, - c->symbol_rate, c->inversion, c->pilot, c->rolloff); + dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d modulation=%d " \ + "frequency=%d symbol_rate=%d inversion=%d pilot=%d " \ + "rolloff=%d\n", __func__, c->delivery_system, c->modulation, + c->frequency, c->symbol_rate, c->inversion, c->pilot, + c->rolloff); priv->delivery_system = SYS_UNDEFINED; @@ -669,7 +675,7 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) inversion = 3; break; default: - dbg("%s: invalid inversion", __func__); + dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n", __func__); ret = -EINVAL; goto error; } @@ -692,7 +698,8 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) break; case ROLLOFF_AUTO: default: - dbg("%s: invalid rolloff", __func__); + dev_dbg(&priv->i2c->dev, "%s: invalid rolloff\n", + __func__); ret = -EINVAL; goto error; } @@ -708,13 +715,15 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) pilot = 2; break; default: - dbg("%s: invalid pilot", __func__); + dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n", + __func__); ret = -EINVAL; goto error; } break; default: - dbg("%s: invalid delivery_system", __func__); + dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", + __func__); ret = -EINVAL; goto error; } @@ -724,13 +733,15 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) c->modulation == TDA10071_MODCOD[i].modulation && c->fec_inner == TDA10071_MODCOD[i].fec) { mode = TDA10071_MODCOD[i].val; - dbg("%s: mode found=%02x", __func__, mode); + dev_dbg(&priv->i2c->dev, "%s: mode found=%02x\n", + __func__, mode); break; } } if (mode == 0xff) { - dbg("%s: invalid parameter combination", __func__); + dev_dbg(&priv->i2c->dev, "%s: invalid parameter combination\n", + __func__); ret = -EINVAL; goto error; } @@ -748,22 +759,22 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) if (ret) goto error; - cmd.args[0x00] = CMD_CHANGE_CHANNEL; - cmd.args[0x01] = 0; - cmd.args[0x02] = mode; - cmd.args[0x03] = (c->frequency >> 16) & 0xff; - cmd.args[0x04] = (c->frequency >> 8) & 0xff; - cmd.args[0x05] = (c->frequency >> 0) & 0xff; - cmd.args[0x06] = ((c->symbol_rate / 1000) >> 8) & 0xff; - cmd.args[0x07] = ((c->symbol_rate / 1000) >> 0) & 0xff; - cmd.args[0x08] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff; - cmd.args[0x09] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff; - cmd.args[0x0a] = rolloff; - cmd.args[0x0b] = inversion; - cmd.args[0x0c] = pilot; - cmd.args[0x0d] = 0x00; - cmd.args[0x0e] = 0x00; - cmd.len = 0x0f; + cmd.args[0] = CMD_CHANGE_CHANNEL; + cmd.args[1] = 0; + cmd.args[2] = mode; + cmd.args[3] = (c->frequency >> 16) & 0xff; + cmd.args[4] = (c->frequency >> 8) & 0xff; + cmd.args[5] = (c->frequency >> 0) & 0xff; + cmd.args[6] = ((c->symbol_rate / 1000) >> 8) & 0xff; + cmd.args[7] = ((c->symbol_rate / 1000) >> 0) & 0xff; + cmd.args[8] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff; + cmd.args[9] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff; + cmd.args[10] = rolloff; + cmd.args[11] = inversion; + cmd.args[12] = pilot; + cmd.args[13] = 0x00; + cmd.args[14] = 0x00; + cmd.len = 15; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; @@ -772,7 +783,7 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -829,7 +840,7 @@ static int tda10071_get_frontend(struct dvb_frontend *fe) return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -915,10 +926,10 @@ static int tda10071_init(struct dvb_frontend *fe) goto error; } - cmd.args[0x00] = CMD_SET_SLEEP_MODE; - cmd.args[0x01] = 0; - cmd.args[0x02] = 0; - cmd.len = 0x03; + cmd.args[0] = CMD_SET_SLEEP_MODE; + cmd.args[1] = 0; + cmd.args[2] = 0; + cmd.len = 3; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; @@ -929,10 +940,11 @@ static int tda10071_init(struct dvb_frontend *fe) /* request the firmware, this will block and timeout */ ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent); if (ret) { - err("did not find the firmware file. (%s) " - "Please see linux/Documentation/dvb/ for more" \ - " details on firmware-problems. (%d)", - fw_file, ret); + dev_err(&priv->i2c->dev, "%s: did not find the " \ + "firmware file. (%s) Please see " \ + "linux/Documentation/dvb/ for more " \ + "details on firmware-problems. (%d)\n", + KBUILD_MODNAME, fw_file, ret); goto error; } @@ -961,10 +973,11 @@ static int tda10071_init(struct dvb_frontend *fe) if (ret) goto error_release_firmware; - info("found a '%s' in cold state, will try to load a firmware", - tda10071_ops.info.name); - - info("downloading firmware from file '%s'", fw_file); + dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state, " \ + "will try to load a firmware\n", KBUILD_MODNAME, + tda10071_ops.info.name); + dev_info(&priv->i2c->dev, "%s: downloading firmware from " \ + "file '%s'\n", KBUILD_MODNAME, fw_file); /* do not download last byte */ fw_size = fw->size - 1; @@ -978,7 +991,9 @@ static int tda10071_init(struct dvb_frontend *fe) ret = tda10071_wr_regs(priv, 0xfa, (u8 *) &fw->data[fw_size - remaining], len); if (ret) { - err("firmware download failed=%d", ret); + dev_err(&priv->i2c->dev, "%s: firmware " \ + "download failed=%d\n", + KBUILD_MODNAME, ret); if (ret) goto error_release_firmware; } @@ -1002,15 +1017,16 @@ static int tda10071_init(struct dvb_frontend *fe) goto error; if (tmp) { - info("firmware did not run"); + dev_info(&priv->i2c->dev, "%s: firmware did not run\n", + KBUILD_MODNAME); ret = -EFAULT; goto error; } else { priv->warm = 1; } - cmd.args[0x00] = CMD_GET_FW_VERSION; - cmd.len = 0x01; + cmd.args[0] = CMD_GET_FW_VERSION; + cmd.len = 1; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; @@ -1019,54 +1035,55 @@ static int tda10071_init(struct dvb_frontend *fe) if (ret) goto error; - info("firmware version %d.%d.%d.%d", - buf[0], buf[1], buf[2], buf[3]); - info("found a '%s' in warm state.", tda10071_ops.info.name); + dev_info(&priv->i2c->dev, "%s: firmware version %d.%d.%d.%d\n", + KBUILD_MODNAME, buf[0], buf[1], buf[2], buf[3]); + dev_info(&priv->i2c->dev, "%s: found a '%s' in warm state\n", + KBUILD_MODNAME, tda10071_ops.info.name); ret = tda10071_rd_regs(priv, 0x81, buf, 2); if (ret) goto error; - cmd.args[0x00] = CMD_DEMOD_INIT; - cmd.args[0x01] = ((priv->cfg.xtal / 1000) >> 8) & 0xff; - cmd.args[0x02] = ((priv->cfg.xtal / 1000) >> 0) & 0xff; - cmd.args[0x03] = buf[0]; - cmd.args[0x04] = buf[1]; - cmd.args[0x05] = priv->cfg.pll_multiplier; - cmd.args[0x06] = priv->cfg.spec_inv; - cmd.args[0x07] = 0x00; - cmd.len = 0x08; + cmd.args[0] = CMD_DEMOD_INIT; + cmd.args[1] = ((priv->cfg.xtal / 1000) >> 8) & 0xff; + cmd.args[2] = ((priv->cfg.xtal / 1000) >> 0) & 0xff; + cmd.args[3] = buf[0]; + cmd.args[4] = buf[1]; + cmd.args[5] = priv->cfg.pll_multiplier; + cmd.args[6] = priv->cfg.spec_inv; + cmd.args[7] = 0x00; + cmd.len = 8; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; - cmd.args[0x00] = CMD_TUNER_INIT; - cmd.args[0x01] = 0x00; - cmd.args[0x02] = 0x00; - cmd.args[0x03] = 0x00; - cmd.args[0x04] = 0x00; - cmd.args[0x05] = 0x14; - cmd.args[0x06] = 0x00; - cmd.args[0x07] = 0x03; - cmd.args[0x08] = 0x02; - cmd.args[0x09] = 0x02; - cmd.args[0x0a] = 0x00; - cmd.args[0x0b] = 0x00; - cmd.args[0x0c] = 0x00; - cmd.args[0x0d] = 0x00; - cmd.args[0x0e] = 0x00; - cmd.len = 0x0f; + cmd.args[0] = CMD_TUNER_INIT; + cmd.args[1] = 0x00; + cmd.args[2] = 0x00; + cmd.args[3] = 0x00; + cmd.args[4] = 0x00; + cmd.args[5] = 0x14; + cmd.args[6] = 0x00; + cmd.args[7] = 0x03; + cmd.args[8] = 0x02; + cmd.args[9] = 0x02; + cmd.args[10] = 0x00; + cmd.args[11] = 0x00; + cmd.args[12] = 0x00; + cmd.args[13] = 0x00; + cmd.args[14] = 0x00; + cmd.len = 15; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; - cmd.args[0x00] = CMD_MPEG_CONFIG; - cmd.args[0x01] = 0; - cmd.args[0x02] = priv->cfg.ts_mode; - cmd.args[0x03] = 0x00; - cmd.args[0x04] = 0x04; - cmd.args[0x05] = 0x00; - cmd.len = 0x06; + cmd.args[0] = CMD_MPEG_CONFIG; + cmd.args[1] = 0; + cmd.args[2] = priv->cfg.ts_mode; + cmd.args[3] = 0x00; + cmd.args[4] = 0x04; + cmd.args[5] = 0x00; + cmd.len = 6; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; @@ -1075,27 +1092,27 @@ static int tda10071_init(struct dvb_frontend *fe) if (ret) goto error; - cmd.args[0x00] = CMD_LNB_CONFIG; - cmd.args[0x01] = 0; - cmd.args[0x02] = 150; - cmd.args[0x03] = 3; - cmd.args[0x04] = 22; - cmd.args[0x05] = 1; - cmd.args[0x06] = 1; - cmd.args[0x07] = 30; - cmd.args[0x08] = 30; - cmd.args[0x09] = 30; - cmd.args[0x0a] = 30; - cmd.len = 0x0b; + cmd.args[0] = CMD_LNB_CONFIG; + cmd.args[1] = 0; + cmd.args[2] = 150; + cmd.args[3] = 3; + cmd.args[4] = 22; + cmd.args[5] = 1; + cmd.args[6] = 1; + cmd.args[7] = 30; + cmd.args[8] = 30; + cmd.args[9] = 30; + cmd.args[10] = 30; + cmd.len = 11; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; - cmd.args[0x00] = CMD_BER_CONTROL; - cmd.args[0x01] = 0; - cmd.args[0x02] = 14; - cmd.args[0x03] = 14; - cmd.len = 0x04; + cmd.args[0] = CMD_BER_CONTROL; + cmd.args[1] = 0; + cmd.args[2] = 14; + cmd.args[3] = 14; + cmd.len = 4; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; @@ -1105,7 +1122,7 @@ static int tda10071_init(struct dvb_frontend *fe) error_release_firmware: release_firmware(fw); error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -1132,10 +1149,10 @@ static int tda10071_sleep(struct dvb_frontend *fe) goto error; } - cmd.args[0x00] = CMD_SET_SLEEP_MODE; - cmd.args[0x01] = 0; - cmd.args[0x02] = 1; - cmd.len = 0x03; + cmd.args[0] = CMD_SET_SLEEP_MODE; + cmd.args[1] = 0; + cmd.args[2] = 1; + cmd.len = 3; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; @@ -1149,7 +1166,7 @@ static int tda10071_sleep(struct dvb_frontend *fe) return ret; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); return ret; } @@ -1208,7 +1225,7 @@ struct dvb_frontend *tda10071_attach(const struct tda10071_config *config, return &priv->fe; error: - dbg("%s: failed=%d", __func__, ret); + dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); kfree(priv); return NULL; } diff --git a/drivers/media/dvb/frontends/tda10071_priv.h b/drivers/media/dvb/frontends/tda10071_priv.h index 93c5e6317f07..0fa85cfa70c2 100644 --- a/drivers/media/dvb/frontends/tda10071_priv.h +++ b/drivers/media/dvb/frontends/tda10071_priv.h @@ -25,19 +25,6 @@ #include "tda10071.h" #include <linux/firmware.h> -#define LOG_PREFIX "tda10071" - -#undef dbg -#define dbg(f, arg...) \ - if (tda10071_debug) \ - printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) -#undef err -#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) -#undef info -#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) -#undef warn -#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) - struct tda10071_priv { struct i2c_adapter *i2c; struct dvb_frontend fe; @@ -112,7 +99,7 @@ struct tda10071_reg_val_mask { #define CMD_BER_UPDATE_COUNTERS 0x3f /* firmare command struct */ -#define TDA10071_ARGLEN 0x1e +#define TDA10071_ARGLEN 30 struct tda10071_cmd { u8 args[TDA10071_ARGLEN]; u8 len; diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c index 7539a5d71029..72ee8de02260 100644 --- a/drivers/media/dvb/ngene/ngene-cards.c +++ b/drivers/media/dvb/ngene/ngene-cards.c @@ -217,6 +217,7 @@ static int demod_attach_drxk(struct ngene_channel *chan, memset(&config, 0, sizeof(config)); config.microcode_name = "drxk_a3.mc"; + config.qam_demod_parameter_count = 4; config.adr = 0x29 + (chan->number ^ 2); chan->fe = dvb_attach(drxk_attach, &config, i2c); diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index c257da13d766..24ce5a47f955 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -5,6 +5,7 @@ menuconfig RADIO_ADAPTERS bool "Radio Adapters" depends on VIDEO_V4L2 + depends on MEDIA_RADIO_SUPPORT default y ---help--- Say Y here to enable selecting AM/FM radio adapters. diff --git a/drivers/media/radio/lm7000.h b/drivers/media/radio/lm7000.h new file mode 100644 index 000000000000..139cd6b68824 --- /dev/null +++ b/drivers/media/radio/lm7000.h @@ -0,0 +1,43 @@ +#ifndef __LM7000_H +#define __LM7000_H + +/* Sanyo LM7000 tuner chip control + * + * Copyright 2012 Ondrej Zary <linux@rainbow-software.org> + * based on radio-aimslab.c by M. Kirkwood + * and radio-sf16fmi.c by M. Kirkwood and Petr Vandrovec + */ + +#define LM7000_DATA (1 << 0) +#define LM7000_CLK (1 << 1) +#define LM7000_CE (1 << 2) + +#define LM7000_FM_100 (0 << 20) +#define LM7000_FM_50 (1 << 20) +#define LM7000_FM_25 (2 << 20) +#define LM7000_BIT_FM (1 << 23) + +static inline void lm7000_set_freq(u32 freq, void *handle, + void (*set_pins)(void *handle, u8 pins)) +{ + int i; + u8 data; + u32 val; + + freq += 171200; /* Add 10.7 MHz IF */ + freq /= 400; /* Convert to 25 kHz units */ + val = freq | LM7000_FM_25 | LM7000_BIT_FM; + /* write the 24-bit register, starting with LSB */ + for (i = 0; i < 24; i++) { + data = val & (1 << i) ? LM7000_DATA : 0; + set_pins(handle, data | LM7000_CE); + udelay(2); + set_pins(handle, data | LM7000_CE | LM7000_CLK); + udelay(2); + set_pins(handle, data | LM7000_CE); + udelay(2); + } + set_pins(handle, 0); +} + +#endif /* __LM7000_H */ diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c index 98e0c8c20312..12c70e876f58 100644 --- a/drivers/media/radio/radio-aimslab.c +++ b/drivers/media/radio/radio-aimslab.c @@ -37,6 +37,7 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-ctrls.h> #include "radio-isa.h" +#include "lm7000.h" MODULE_AUTHOR("M. Kirkwood"); MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card."); @@ -72,55 +73,38 @@ static struct radio_isa_card *rtrack_alloc(void) return rt ? &rt->isa : NULL; } -/* The 128+64 on these outb's is to keep the volume stable while tuning. - * Without them, the volume _will_ creep up with each frequency change - * and bit 4 (+16) is to keep the signal strength meter enabled. - */ +#define AIMS_BIT_TUN_CE (1 << 0) +#define AIMS_BIT_TUN_CLK (1 << 1) +#define AIMS_BIT_TUN_DATA (1 << 2) +#define AIMS_BIT_VOL_CE (1 << 3) +#define AIMS_BIT_TUN_STRQ (1 << 4) +/* bit 5 is not connected */ +#define AIMS_BIT_VOL_UP (1 << 6) /* active low */ +#define AIMS_BIT_VOL_DN (1 << 7) /* active low */ -static void send_0_byte(struct radio_isa_card *isa, int on) +void rtrack_set_pins(void *handle, u8 pins) { - outb_p(128+64+16+on+1, isa->io); /* wr-enable + data low */ - outb_p(128+64+16+on+2+1, isa->io); /* clock */ - msleep(1); -} + struct radio_isa_card *isa = handle; + struct rtrack *rt = container_of(isa, struct rtrack, isa); + u8 bits = AIMS_BIT_VOL_DN | AIMS_BIT_VOL_UP | AIMS_BIT_TUN_STRQ; -static void send_1_byte(struct radio_isa_card *isa, int on) -{ - outb_p(128+64+16+on+4+1, isa->io); /* wr-enable+data high */ - outb_p(128+64+16+on+4+2+1, isa->io); /* clock */ - msleep(1); + if (!v4l2_ctrl_g_ctrl(rt->isa.mute)) + bits |= AIMS_BIT_VOL_CE; + + if (pins & LM7000_DATA) + bits |= AIMS_BIT_TUN_DATA; + if (pins & LM7000_CLK) + bits |= AIMS_BIT_TUN_CLK; + if (pins & LM7000_CE) + bits |= AIMS_BIT_TUN_CE; + + outb_p(bits, rt->isa.io); } static int rtrack_s_frequency(struct radio_isa_card *isa, u32 freq) { - int on = v4l2_ctrl_g_ctrl(isa->mute) ? 0 : 8; - int i; - - freq += 171200; /* Add 10.7 MHz IF */ - freq /= 800; /* Convert to 50 kHz units */ - - send_0_byte(isa, on); /* 0: LSB of frequency */ - - for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ - if (freq & (1 << i)) - send_1_byte(isa, on); - else - send_0_byte(isa, on); - - send_0_byte(isa, on); /* 14: test bit - always 0 */ - send_0_byte(isa, on); /* 15: test bit - always 0 */ - - send_0_byte(isa, on); /* 16: band data 0 - always 0 */ - send_0_byte(isa, on); /* 17: band data 1 - always 0 */ - send_0_byte(isa, on); /* 18: band data 2 - always 0 */ - send_0_byte(isa, on); /* 19: time base - always 0 */ - - send_0_byte(isa, on); /* 20: spacing (0 = 25 kHz) */ - send_1_byte(isa, on); /* 21: spacing (1 = 25 kHz) */ - send_0_byte(isa, on); /* 22: spacing (0 = 25 kHz) */ - send_1_byte(isa, on); /* 23: AM/FM (FM = 1, always) */ + lm7000_set_freq(freq, isa, rtrack_set_pins); - outb(0xd0 + on, isa->io); /* volume steady + sigstr */ return 0; } diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index 94cb6bc690f5..3182b26d6efa 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -295,7 +295,8 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->type = V4L2_TUNER_RADIO; v->rangelow = FREQ_MIN * FREQ_MUL; v->rangehigh = FREQ_MAX * FREQ_MUL; - v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_HWSEEK_WRAP; v->rxsubchans = is_stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; v->audmode = radio->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; @@ -372,7 +373,7 @@ static int vidioc_s_hw_freq_seek(struct file *file, void *priv, timeout = jiffies + msecs_to_jiffies(30000); for (;;) { if (time_after(jiffies, timeout)) { - retval = -EAGAIN; + retval = -ENODATA; break; } if (schedule_timeout_interruptible(msecs_to_jiffies(10))) { diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index a81d723b8c77..8185d5fbfa89 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -27,6 +27,7 @@ #include <linux/io.h> /* outb, outb_p */ #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> +#include "lm7000.h" MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood"); MODULE_DESCRIPTION("A driver for the SF16-FMI, SF16-FMP and SF16-FMD radio."); @@ -54,31 +55,33 @@ static struct fmi fmi_card; static struct pnp_dev *dev; bool pnp_attached; -/* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */ -/* It is only useful to give freq in interval of 800 (=0.05Mhz), - * other bits will be truncated, e.g 92.7400016 -> 92.7, but - * 92.7400017 -> 92.75 - */ -#define RSF16_ENCODE(x) ((x) / 800 + 214) #define RSF16_MINFREQ (87 * 16000) #define RSF16_MAXFREQ (108 * 16000) -static void outbits(int bits, unsigned int data, int io) +#define FMI_BIT_TUN_CE (1 << 0) +#define FMI_BIT_TUN_CLK (1 << 1) +#define FMI_BIT_TUN_DATA (1 << 2) +#define FMI_BIT_VOL_SW (1 << 3) +#define FMI_BIT_TUN_STRQ (1 << 4) + +void fmi_set_pins(void *handle, u8 pins) { - while (bits--) { - if (data & 1) { - outb(5, io); - udelay(6); - outb(7, io); - udelay(6); - } else { - outb(1, io); - udelay(6); - outb(3, io); - udelay(6); - } - data >>= 1; - } + struct fmi *fmi = handle; + u8 bits = FMI_BIT_TUN_STRQ; + + if (!fmi->mute) + bits |= FMI_BIT_VOL_SW; + + if (pins & LM7000_DATA) + bits |= FMI_BIT_TUN_DATA; + if (pins & LM7000_CLK) + bits |= FMI_BIT_TUN_CLK; + if (pins & LM7000_CE) + bits |= FMI_BIT_TUN_CE; + + mutex_lock(&fmi->lock); + outb_p(bits, fmi->io); + mutex_unlock(&fmi->lock); } static inline void fmi_mute(struct fmi *fmi) @@ -95,20 +98,6 @@ static inline void fmi_unmute(struct fmi *fmi) mutex_unlock(&fmi->lock); } -static inline int fmi_setfreq(struct fmi *fmi, unsigned long freq) -{ - mutex_lock(&fmi->lock); - fmi->curfreq = freq; - - outbits(16, RSF16_ENCODE(freq), fmi->io); - outbits(8, 0xC0, fmi->io); - msleep(143); /* was schedule_timeout(HZ/7) */ - mutex_unlock(&fmi->lock); - if (!fmi->mute) - fmi_unmute(fmi); - return 0; -} - static inline int fmi_getsigstr(struct fmi *fmi) { int val; @@ -173,7 +162,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, return -EINVAL; /* rounding in steps of 800 to match the freq that will be used */ - fmi_setfreq(fmi, (f->frequency / 800) * 800); + lm7000_set_freq((f->frequency / 800) * 800, fmi, fmi_set_pins); return 0; } diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index f1b607099b6c..e8428f573ccd 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1514,7 +1514,8 @@ static int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv, tuner->rangehigh = WL1273_FREQ(WL1273_BAND_OTHER_HIGH); tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_RDS | - V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS_BLOCK_IO; + V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS_BLOCK_IO | + V4L2_TUNER_CAP_HWSEEK_BOUNDED | V4L2_TUNER_CAP_HWSEEK_WRAP; if (radio->stereo) tuner->audmode = V4L2_TUNER_MODE_STEREO; diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index 969cf494d85b..d485b79222fd 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -363,7 +363,7 @@ stop: /* try again, if timed out */ if (retval == 0 && timed_out) - return -EAGAIN; + return -ENODATA; return retval; } @@ -596,7 +596,9 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, strcpy(tuner->name, "FM"); tuner->type = V4L2_TUNER_RADIO; tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO; + V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO | + V4L2_TUNER_CAP_HWSEEK_BOUNDED | + V4L2_TUNER_CAP_HWSEEK_WRAP; /* range limits */ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { diff --git a/drivers/media/radio/wl128x/fmdrv_rx.c b/drivers/media/radio/wl128x/fmdrv_rx.c index 43fb72291bea..3dd9fc097c47 100644 --- a/drivers/media/radio/wl128x/fmdrv_rx.c +++ b/drivers/media/radio/wl128x/fmdrv_rx.c @@ -251,7 +251,7 @@ again: if (!timeleft) { fmerr("Timeout(%d sec),didn't get tune ended int\n", jiffies_to_msecs(FM_DRV_RX_SEEK_TIMEOUT) / 1000); - return -ETIMEDOUT; + return -ENODATA; } int_reason = fmdev->irq_info.flag & (FM_TUNE_COMPLETE | FM_BAND_LIMIT); diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c index 080b96a61f1a..49a11ec1f449 100644 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c @@ -285,7 +285,9 @@ static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv, tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO | ((fmdev->rx.rds.flag == FM_RDS_ENABLE) ? V4L2_TUNER_SUB_RDS : 0); tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | - V4L2_TUNER_CAP_LOW; + V4L2_TUNER_CAP_LOW | + V4L2_TUNER_CAP_HWSEEK_BOUNDED | + V4L2_TUNER_CAP_HWSEEK_WRAP; tuner->audmode = (stereo_mono_mode ? V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO); diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index f97eeb870455..908ef70430e9 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -1,21 +1,20 @@ -menuconfig RC_CORE - tristate "Remote Controller adapters" +config RC_CORE + tristate + depends on MEDIA_RC_SUPPORT depends on INPUT - default INPUT - ---help--- - Enable support for Remote Controllers on Linux. This is - needed in order to support several video capture adapters, - standalone IR receivers/transmitters, and RF receivers. + default y - Enable this option if you have a video capture board even - if you don't need IR, as otherwise, you may not be able to - compile the driver for your adapter. +source "drivers/media/rc/keymaps/Kconfig" -if RC_CORE +menuconfig RC_DECODERS + bool "Remote controller decoders" + depends on RC_CORE + default y +if RC_DECODERS config LIRC - tristate - default y + tristate "LIRC interface driver" + depends on RC_CORE ---help--- Enable this option to build the Linux Infrared Remote @@ -24,7 +23,16 @@ config LIRC LIRC daemon handles protocol decoding for IR reception and encoding for IR transmitting (aka "blasting"). -source "drivers/media/rc/keymaps/Kconfig" +config IR_LIRC_CODEC + tristate "Enable IR to LIRC bridge" + depends on RC_CORE + depends on LIRC + default y + + ---help--- + Enable this option to pass raw IR to and from userspace via + the LIRC interface. + config IR_NEC_DECODER tristate "Enable IR raw decoder for the NEC protocol" @@ -108,16 +116,13 @@ config IR_MCE_KBD_DECODER Enable this option if you have a Microsoft Remote Keyboard for Windows Media Center Edition, which you would like to use with a raw IR receiver in your system. +endif #RC_DECODERS -config IR_LIRC_CODEC - tristate "Enable IR to LIRC bridge" +menuconfig RC_DEVICES + bool "Remote Controller devices" depends on RC_CORE - depends on LIRC - default y - ---help--- - Enable this option to pass raw IR to and from userspace via - the LIRC interface. +if RC_DEVICES config RC_ATI_REMOTE tristate "ATI / X10 based USB RF remote controls" @@ -276,4 +281,4 @@ config IR_GPIO_CIR To compile this driver as a module, choose M here: the module will be called gpio-ir-recv. -endif #RC_CORE +endif #RC_DEVICES diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index bef5296173c9..647dd951b0e8 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1018,6 +1018,8 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) spin_lock_init(&dev->hw_lock); + dev->hw_io = pnp_port_start(pnp_dev, 0); + pnp_set_drvdata(pnp_dev, dev); dev->pnp_dev = pnp_dev; @@ -1072,7 +1074,6 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) /* claim the resources */ error = -EBUSY; - dev->hw_io = pnp_port_start(pnp_dev, 0); if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) { dev->hw_io = -1; dev->irq = -1; diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 6aabf7ae3a31..ab30c64f8124 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -23,6 +23,8 @@ * USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/pnp.h> @@ -110,30 +112,32 @@ static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset) return val; } -#define pr_reg(text, ...) \ - printk(KERN_INFO KBUILD_MODNAME ": " text, ## __VA_ARGS__) - /* dump current cir register contents */ static void cir_dump_regs(struct fintek_dev *fintek) { fintek_config_mode_enable(fintek); fintek_select_logical_dev(fintek, fintek->logical_dev_cir); - pr_reg("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME); - pr_reg(" * CR CIR BASE ADDR: 0x%x\n", - (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) | + pr_info("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME); + pr_info(" * CR CIR BASE ADDR: 0x%x\n", + (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) | fintek_cr_read(fintek, CIR_CR_BASE_ADDR_LO)); - pr_reg(" * CR CIR IRQ NUM: 0x%x\n", - fintek_cr_read(fintek, CIR_CR_IRQ_SEL)); + pr_info(" * CR CIR IRQ NUM: 0x%x\n", + fintek_cr_read(fintek, CIR_CR_IRQ_SEL)); fintek_config_mode_disable(fintek); - pr_reg("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME); - pr_reg(" * STATUS: 0x%x\n", fintek_cir_reg_read(fintek, CIR_STATUS)); - pr_reg(" * CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_CONTROL)); - pr_reg(" * RX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_RX_DATA)); - pr_reg(" * TX_CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_CONTROL)); - pr_reg(" * TX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_DATA)); + pr_info("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME); + pr_info(" * STATUS: 0x%x\n", + fintek_cir_reg_read(fintek, CIR_STATUS)); + pr_info(" * CONTROL: 0x%x\n", + fintek_cir_reg_read(fintek, CIR_CONTROL)); + pr_info(" * RX_DATA: 0x%x\n", + fintek_cir_reg_read(fintek, CIR_RX_DATA)); + pr_info(" * TX_CONTROL: 0x%x\n", + fintek_cir_reg_read(fintek, CIR_TX_CONTROL)); + pr_info(" * TX_DATA: 0x%x\n", + fintek_cir_reg_read(fintek, CIR_TX_DATA)); } /* detect hardware features */ diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index 0d875450c5ce..04cb272db16a 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -82,12 +82,21 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev) goto err_allocate_device; } + rcdev->priv = gpio_dev; rcdev->driver_type = RC_DRIVER_IR_RAW; - rcdev->allowed_protos = RC_TYPE_ALL; rcdev->input_name = GPIO_IR_DEVICE_NAME; + rcdev->input_phys = GPIO_IR_DEVICE_NAME "/input0"; rcdev->input_id.bustype = BUS_HOST; + rcdev->input_id.vendor = 0x0001; + rcdev->input_id.product = 0x0001; + rcdev->input_id.version = 0x0100; + rcdev->dev.parent = &pdev->dev; rcdev->driver_name = GPIO_IR_DRIVER_NAME; - rcdev->map_name = RC_MAP_EMPTY; + if (pdata->allowed_protos) + rcdev->allowed_protos = pdata->allowed_protos; + else + rcdev->allowed_protos = RC_TYPE_ALL; + rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY; gpio_dev->rcdev = rcdev; gpio_dev->gpio_nr = pdata->gpio_nr; @@ -188,18 +197,7 @@ static struct platform_driver gpio_ir_recv_driver = { #endif }, }; - -static int __init gpio_ir_recv_init(void) -{ - return platform_driver_register(&gpio_ir_recv_driver); -} -module_init(gpio_ir_recv_init); - -static void __exit gpio_ir_recv_exit(void) -{ - platform_driver_unregister(&gpio_ir_recv_driver); -} -module_exit(gpio_ir_recv_exit); +module_platform_driver(gpio_ir_recv_driver); MODULE_DESCRIPTION("GPIO IR Receiver driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index dc8a7dddccd4..699eef39128b 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -25,6 +25,8 @@ * USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/pnp.h> @@ -123,43 +125,40 @@ static u8 nvt_cir_wake_reg_read(struct nvt_dev *nvt, u8 offset) return val; } -#define pr_reg(text, ...) \ - printk(KERN_INFO KBUILD_MODNAME ": " text, ## __VA_ARGS__) - /* dump current cir register contents */ static void cir_dump_regs(struct nvt_dev *nvt) { nvt_efm_enable(nvt); nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR); - pr_reg("%s: Dump CIR logical device registers:\n", NVT_DRIVER_NAME); - pr_reg(" * CR CIR ACTIVE : 0x%x\n", - nvt_cr_read(nvt, CR_LOGICAL_DEV_EN)); - pr_reg(" * CR CIR BASE ADDR: 0x%x\n", - (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) | + pr_info("%s: Dump CIR logical device registers:\n", NVT_DRIVER_NAME); + pr_info(" * CR CIR ACTIVE : 0x%x\n", + nvt_cr_read(nvt, CR_LOGICAL_DEV_EN)); + pr_info(" * CR CIR BASE ADDR: 0x%x\n", + (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) | nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO)); - pr_reg(" * CR CIR IRQ NUM: 0x%x\n", - nvt_cr_read(nvt, CR_CIR_IRQ_RSRC)); + pr_info(" * CR CIR IRQ NUM: 0x%x\n", + nvt_cr_read(nvt, CR_CIR_IRQ_RSRC)); nvt_efm_disable(nvt); - pr_reg("%s: Dump CIR registers:\n", NVT_DRIVER_NAME); - pr_reg(" * IRCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRCON)); - pr_reg(" * IRSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRSTS)); - pr_reg(" * IREN: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IREN)); - pr_reg(" * RXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_RXFCONT)); - pr_reg(" * CP: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CP)); - pr_reg(" * CC: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CC)); - pr_reg(" * SLCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCH)); - pr_reg(" * SLCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCL)); - pr_reg(" * FIFOCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FIFOCON)); - pr_reg(" * IRFIFOSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFIFOSTS)); - pr_reg(" * SRXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SRXFIFO)); - pr_reg(" * TXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_TXFCONT)); - pr_reg(" * STXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_STXFIFO)); - pr_reg(" * FCCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCH)); - pr_reg(" * FCCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCL)); - pr_reg(" * IRFSM: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFSM)); + pr_info("%s: Dump CIR registers:\n", NVT_DRIVER_NAME); + pr_info(" * IRCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRCON)); + pr_info(" * IRSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRSTS)); + pr_info(" * IREN: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IREN)); + pr_info(" * RXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_RXFCONT)); + pr_info(" * CP: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CP)); + pr_info(" * CC: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CC)); + pr_info(" * SLCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCH)); + pr_info(" * SLCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCL)); + pr_info(" * FIFOCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FIFOCON)); + pr_info(" * IRFIFOSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFIFOSTS)); + pr_info(" * SRXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SRXFIFO)); + pr_info(" * TXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_TXFCONT)); + pr_info(" * STXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_STXFIFO)); + pr_info(" * FCCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCH)); + pr_info(" * FCCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCL)); + pr_info(" * IRFSM: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFSM)); } /* dump current cir wake register contents */ @@ -170,59 +169,59 @@ static void cir_wake_dump_regs(struct nvt_dev *nvt) nvt_efm_enable(nvt); nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE); - pr_reg("%s: Dump CIR WAKE logical device registers:\n", - NVT_DRIVER_NAME); - pr_reg(" * CR CIR WAKE ACTIVE : 0x%x\n", - nvt_cr_read(nvt, CR_LOGICAL_DEV_EN)); - pr_reg(" * CR CIR WAKE BASE ADDR: 0x%x\n", - (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) | + pr_info("%s: Dump CIR WAKE logical device registers:\n", + NVT_DRIVER_NAME); + pr_info(" * CR CIR WAKE ACTIVE : 0x%x\n", + nvt_cr_read(nvt, CR_LOGICAL_DEV_EN)); + pr_info(" * CR CIR WAKE BASE ADDR: 0x%x\n", + (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) | nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO)); - pr_reg(" * CR CIR WAKE IRQ NUM: 0x%x\n", - nvt_cr_read(nvt, CR_CIR_IRQ_RSRC)); + pr_info(" * CR CIR WAKE IRQ NUM: 0x%x\n", + nvt_cr_read(nvt, CR_CIR_IRQ_RSRC)); nvt_efm_disable(nvt); - pr_reg("%s: Dump CIR WAKE registers\n", NVT_DRIVER_NAME); - pr_reg(" * IRCON: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON)); - pr_reg(" * IRSTS: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS)); - pr_reg(" * IREN: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN)); - pr_reg(" * FIFO CMP DEEP: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_DEEP)); - pr_reg(" * FIFO CMP TOL: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_TOL)); - pr_reg(" * FIFO COUNT: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT)); - pr_reg(" * SLCH: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCH)); - pr_reg(" * SLCL: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCL)); - pr_reg(" * FIFOCON: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON)); - pr_reg(" * SRXFSTS: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_SRXFSTS)); - pr_reg(" * SAMPLE RX FIFO: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_SAMPLE_RX_FIFO)); - pr_reg(" * WR FIFO DATA: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_WR_FIFO_DATA)); - pr_reg(" * RD FIFO ONLY: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY)); - pr_reg(" * RD FIFO ONLY IDX: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX)); - pr_reg(" * FIFO IGNORE: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_IGNORE)); - pr_reg(" * IRFSM: 0x%x\n", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRFSM)); + pr_info("%s: Dump CIR WAKE registers\n", NVT_DRIVER_NAME); + pr_info(" * IRCON: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON)); + pr_info(" * IRSTS: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS)); + pr_info(" * IREN: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN)); + pr_info(" * FIFO CMP DEEP: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_DEEP)); + pr_info(" * FIFO CMP TOL: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_TOL)); + pr_info(" * FIFO COUNT: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT)); + pr_info(" * SLCH: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCH)); + pr_info(" * SLCL: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCL)); + pr_info(" * FIFOCON: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON)); + pr_info(" * SRXFSTS: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_SRXFSTS)); + pr_info(" * SAMPLE RX FIFO: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_SAMPLE_RX_FIFO)); + pr_info(" * WR FIFO DATA: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_WR_FIFO_DATA)); + pr_info(" * RD FIFO ONLY: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY)); + pr_info(" * RD FIFO ONLY IDX: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX)); + pr_info(" * FIFO IGNORE: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_IGNORE)); + pr_info(" * IRFSM: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRFSM)); fifo_len = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT); - pr_reg("%s: Dump CIR WAKE FIFO (len %d)\n", NVT_DRIVER_NAME, fifo_len); - pr_reg("* Contents = "); + pr_info("%s: Dump CIR WAKE FIFO (len %d)\n", NVT_DRIVER_NAME, fifo_len); + pr_info("* Contents ="); for (i = 0; i < fifo_len; i++) - printk(KERN_CONT "%02x ", - nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY)); - printk(KERN_CONT "\n"); + pr_cont(" %02x", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY)); + pr_cont("\n"); } /* detect hardware features */ diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 99937c94d7df..c128fac0ce2c 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -5,7 +5,7 @@ config VIDEO_V4L2 tristate depends on VIDEO_DEV && VIDEO_V4L2_COMMON - default VIDEO_DEV && VIDEO_V4L2_COMMON + default y config VIDEOBUF_GEN tristate @@ -73,6 +73,7 @@ config VIDEOBUF2_DMA_SG menuconfig VIDEO_CAPTURE_DRIVERS bool "Video capture adapters" depends on VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT default y ---help--- Say Y here to enable selecting the video adapters for @@ -461,6 +462,15 @@ config VIDEO_ADV7343 To compile this driver as a module, choose M here: the module will be called adv7343. +config VIDEO_ADV7393 + tristate "ADV7393 video encoder" + depends on I2C + help + Support for Analog Devices I2C bus based ADV7393 encoder. + + To compile this driver as a module, choose M here: the + module will be called adv7393. + config VIDEO_AK881X tristate "AK8813/AK8814 video encoders" depends on I2C @@ -478,6 +488,7 @@ config VIDEO_SMIAPP_PLL config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT ---help--- This is a Video4Linux2 sensor-level driver for the OmniVision OV7670 VGA camera. It currently only works with the M88ALP01 @@ -486,6 +497,7 @@ config VIDEO_OV7670 config VIDEO_VS6624 tristate "ST VS6624 sensor support" depends on VIDEO_V4L2 && I2C + depends on MEDIA_CAMERA_SUPPORT ---help--- This is a Video4Linux2 sensor-level driver for the ST VS6624 camera. @@ -496,6 +508,7 @@ config VIDEO_VS6624 config VIDEO_MT9M032 tristate "MT9M032 camera sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT select VIDEO_APTINA_PLL ---help--- This driver supports MT9M032 camera sensors from Aptina, monochrome @@ -504,6 +517,7 @@ config VIDEO_MT9M032 config VIDEO_MT9P031 tristate "Aptina MT9P031 support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT select VIDEO_APTINA_PLL ---help--- This is a Video4Linux2 sensor-level driver for the Aptina @@ -512,6 +526,7 @@ config VIDEO_MT9P031 config VIDEO_MT9T001 tristate "Aptina MT9T001 support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT ---help--- This is a Video4Linux2 sensor-level driver for the Aptina (Micron) mt0t001 3 Mpixel camera. @@ -519,6 +534,7 @@ config VIDEO_MT9T001 config VIDEO_MT9V011 tristate "Micron mt9v011 sensor support" depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT ---help--- This is a Video4Linux2 sensor-level driver for the Micron mt0v011 1.3 Mpixel camera. It currently only works with the @@ -527,6 +543,7 @@ config VIDEO_MT9V011 config VIDEO_MT9V032 tristate "Micron MT9V032 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT ---help--- This is a Video4Linux2 sensor-level driver for the Micron MT9V032 752x480 CMOS sensor. @@ -534,6 +551,7 @@ config VIDEO_MT9V032 config VIDEO_TCM825X tristate "TCM825x camera sensor support" depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT ---help--- This is a driver for the Toshiba TCM825x VGA camera sensor. It is used for example in Nokia N800. @@ -541,12 +559,14 @@ config VIDEO_TCM825X config VIDEO_SR030PC30 tristate "Siliconfile SR030PC30 sensor support" depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT ---help--- This driver supports SR030PC30 VGA camera from Siliconfile config VIDEO_NOON010PC30 tristate "Siliconfile NOON010PC30 sensor support" depends on I2C && VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT ---help--- This driver supports NOON010PC30 CIF camera from Siliconfile @@ -554,6 +574,7 @@ source "drivers/media/video/m5mols/Kconfig" config VIDEO_S5K6AA tristate "Samsung S5K6AAFX sensor support" + depends on MEDIA_CAMERA_SUPPORT depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ---help--- This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M @@ -566,6 +587,7 @@ comment "Flash devices" config VIDEO_ADP1653 tristate "ADP1653 flash support" depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on MEDIA_CAMERA_SUPPORT ---help--- This is a driver for the ADP1653 flash controller. It is used for example in Nokia N900. @@ -573,6 +595,7 @@ config VIDEO_ADP1653 config VIDEO_AS3645A tristate "AS3645A flash driver support" depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on MEDIA_CAMERA_SUPPORT ---help--- This is a driver for the AS3645A and LM3555 flash controllers. It has build in control for flash, torch and indicator LEDs. @@ -647,30 +670,14 @@ menuconfig V4L_USB_DRIVERS depends on USB default y -if V4L_USB_DRIVERS +if V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT -source "drivers/media/video/au0828/Kconfig" + comment "Webcam devices" source "drivers/media/video/uvc/Kconfig" source "drivers/media/video/gspca/Kconfig" -source "drivers/media/video/pvrusb2/Kconfig" - -source "drivers/media/video/hdpvr/Kconfig" - -source "drivers/media/video/em28xx/Kconfig" - -source "drivers/media/video/tlg2300/Kconfig" - -source "drivers/media/video/cx231xx/Kconfig" - -source "drivers/media/video/tm6000/Kconfig" - -source "drivers/media/video/usbvision/Kconfig" - -source "drivers/media/video/sn9c102/Kconfig" - source "drivers/media/video/pwc/Kconfig" source "drivers/media/video/cpia2/Kconfig" @@ -711,15 +718,46 @@ config USB_S2255 Say Y here if you want support for the Sensoray 2255 USB device. This driver can be compiled as a module, called s2255drv. +source "drivers/media/video/sn9c102/Kconfig" + +endif # V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT + +if V4L_USB_DRIVERS + + comment "Webcam and/or TV USB devices" + +source "drivers/media/video/em28xx/Kconfig" + +endif + +if V4L_USB_DRIVERS && MEDIA_ANALOG_TV_SUPPORT + + comment "TV USB devices" + +source "drivers/media/video/au0828/Kconfig" + +source "drivers/media/video/pvrusb2/Kconfig" + +source "drivers/media/video/hdpvr/Kconfig" + +source "drivers/media/video/tlg2300/Kconfig" + +source "drivers/media/video/cx231xx/Kconfig" + +source "drivers/media/video/tm6000/Kconfig" + +source "drivers/media/video/usbvision/Kconfig" + endif # V4L_USB_DRIVERS # -# PCI drivers configuration +# PCI drivers configuration - No devices here are for webcams # menuconfig V4L_PCI_DRIVERS bool "V4L PCI(e) devices" depends on PCI + depends on MEDIA_ANALOG_TV_SUPPORT default y ---help--- Say Y here to enable support for these PCI(e) drivers. @@ -814,11 +852,13 @@ endif # V4L_PCI_DRIVERS # # ISA & parallel port drivers configuration +# All devices here are webcam or grabber devices # menuconfig V4L_ISA_PARPORT_DRIVERS bool "V4L ISA and parallel port devices" depends on ISA || PARPORT + depends on MEDIA_CAMERA_SUPPORT default n ---help--- Say Y here to enable support for these ISA and parallel port drivers. @@ -871,8 +911,13 @@ config VIDEO_W9966 endif # V4L_ISA_PARPORT_DRIVERS +# +# Platform drivers +# All drivers here are currently for webcam support + menuconfig V4L_PLATFORM_DRIVERS bool "V4L platform devices" + depends on MEDIA_CAMERA_SUPPORT default n ---help--- Say Y here to enable support for platform-specific V4L drivers. diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index d209de0e0ca8..b7da9faa3b0a 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o +obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_VS6624) += vs6624.o obj-$(CONFIG_VIDEO_BT819) += bt819.o diff --git a/drivers/media/video/adv7393.c b/drivers/media/video/adv7393.c new file mode 100644 index 000000000000..3dc6098c7267 --- /dev/null +++ b/drivers/media/video/adv7393.c @@ -0,0 +1,487 @@ +/* + * adv7393 - ADV7393 Video Encoder Driver + * + * The encoder hardware does not support SECAM. + * + * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ + * Benoît Thébaudeau <benoit.thebaudeau@advansee.com> + * + * Based on ADV7343 driver, + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/ctype.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/videodev2.h> +#include <linux/uaccess.h> + +#include <media/adv7393.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> + +#include "adv7393_regs.h" + +MODULE_DESCRIPTION("ADV7393 video encoder driver"); +MODULE_LICENSE("GPL"); + +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +struct adv7393_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + u8 reg00; + u8 reg01; + u8 reg02; + u8 reg35; + u8 reg80; + u8 reg82; + u32 output; + v4l2_std_id std; +}; + +static inline struct adv7393_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7393_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7393_state, hdl)->sd; +} + +static inline int adv7393_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static const u8 adv7393_init_reg_val[] = { + ADV7393_SOFT_RESET, ADV7393_SOFT_RESET_DEFAULT, + ADV7393_POWER_MODE_REG, ADV7393_POWER_MODE_REG_DEFAULT, + + ADV7393_HD_MODE_REG1, ADV7393_HD_MODE_REG1_DEFAULT, + ADV7393_HD_MODE_REG2, ADV7393_HD_MODE_REG2_DEFAULT, + ADV7393_HD_MODE_REG3, ADV7393_HD_MODE_REG3_DEFAULT, + ADV7393_HD_MODE_REG4, ADV7393_HD_MODE_REG4_DEFAULT, + ADV7393_HD_MODE_REG5, ADV7393_HD_MODE_REG5_DEFAULT, + ADV7393_HD_MODE_REG6, ADV7393_HD_MODE_REG6_DEFAULT, + ADV7393_HD_MODE_REG7, ADV7393_HD_MODE_REG7_DEFAULT, + + ADV7393_SD_MODE_REG1, ADV7393_SD_MODE_REG1_DEFAULT, + ADV7393_SD_MODE_REG2, ADV7393_SD_MODE_REG2_DEFAULT, + ADV7393_SD_MODE_REG3, ADV7393_SD_MODE_REG3_DEFAULT, + ADV7393_SD_MODE_REG4, ADV7393_SD_MODE_REG4_DEFAULT, + ADV7393_SD_MODE_REG5, ADV7393_SD_MODE_REG5_DEFAULT, + ADV7393_SD_MODE_REG6, ADV7393_SD_MODE_REG6_DEFAULT, + ADV7393_SD_MODE_REG7, ADV7393_SD_MODE_REG7_DEFAULT, + ADV7393_SD_MODE_REG8, ADV7393_SD_MODE_REG8_DEFAULT, + + ADV7393_SD_TIMING_REG0, ADV7393_SD_TIMING_REG0_DEFAULT, + + ADV7393_SD_HUE_ADJUST, ADV7393_SD_HUE_ADJUST_DEFAULT, + ADV7393_SD_CGMS_WSS0, ADV7393_SD_CGMS_WSS0_DEFAULT, + ADV7393_SD_BRIGHTNESS_WSS, ADV7393_SD_BRIGHTNESS_WSS_DEFAULT, +}; + +/* + * 2^32 + * FSC(reg) = FSC (HZ) * -------- + * 27000000 + */ +static const struct adv7393_std_info stdinfo[] = { + { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, + }, { + /* FSC(Hz) = 3,579,545.45 Hz */ + SD_STD_NTSC, 569408542, V4L2_STD_NTSC, + }, { + /* FSC(Hz) = 3,575,611.00 Hz */ + SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, + }, { + /* FSC(Hz) = 3,582,056.00 Hz */ + SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, + }, +}; + +static int adv7393_setstd(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7393_state *state = to_state(sd); + const struct adv7393_std_info *std_info; + int num_std; + u8 reg; + u32 val; + int err = 0; + int i; + + num_std = ARRAY_SIZE(stdinfo); + + for (i = 0; i < num_std; i++) { + if (stdinfo[i].stdid & std) + break; + } + + if (i == num_std) { + v4l2_dbg(1, debug, sd, + "Invalid std or std is not supported: %llx\n", + (unsigned long long)std); + return -EINVAL; + } + + std_info = &stdinfo[i]; + + /* Set the standard */ + val = state->reg80 & ~SD_STD_MASK; + val |= std_info->standard_val3; + err = adv7393_write(sd, ADV7393_SD_MODE_REG1, val); + if (err < 0) + goto setstd_exit; + + state->reg80 = val; + + /* Configure the input mode register */ + val = state->reg01 & ~INPUT_MODE_MASK; + val |= SD_INPUT_MODE; + err = adv7393_write(sd, ADV7393_MODE_SELECT_REG, val); + if (err < 0) + goto setstd_exit; + + state->reg01 = val; + + /* Program the sub carrier frequency registers */ + val = std_info->fsc_val; + for (reg = ADV7393_FSC_REG0; reg <= ADV7393_FSC_REG3; reg++) { + err = adv7393_write(sd, reg, val); + if (err < 0) + goto setstd_exit; + val >>= 8; + } + + val = state->reg82; + + /* Pedestal settings */ + if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) + val |= SD_PEDESTAL_EN; + else + val &= SD_PEDESTAL_DI; + + err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); + if (err < 0) + goto setstd_exit; + + state->reg82 = val; + +setstd_exit: + if (err != 0) + v4l2_err(sd, "Error setting std, write failed\n"); + + return err; +} + +static int adv7393_setoutput(struct v4l2_subdev *sd, u32 output_type) +{ + struct adv7393_state *state = to_state(sd); + u8 val; + int err = 0; + + if (output_type > ADV7393_SVIDEO_ID) { + v4l2_dbg(1, debug, sd, + "Invalid output type or output type not supported:%d\n", + output_type); + return -EINVAL; + } + + /* Enable Appropriate DAC */ + val = state->reg00 & 0x03; + + if (output_type == ADV7393_COMPOSITE_ID) + val |= ADV7393_COMPOSITE_POWER_VALUE; + else if (output_type == ADV7393_COMPONENT_ID) + val |= ADV7393_COMPONENT_POWER_VALUE; + else + val |= ADV7393_SVIDEO_POWER_VALUE; + + err = adv7393_write(sd, ADV7393_POWER_MODE_REG, val); + if (err < 0) + goto setoutput_exit; + + state->reg00 = val; + + /* Enable YUV output */ + val = state->reg02 | YUV_OUTPUT_SELECT; + err = adv7393_write(sd, ADV7393_MODE_REG0, val); + if (err < 0) + goto setoutput_exit; + + state->reg02 = val; + + /* configure SD DAC Output 1 bit */ + val = state->reg82; + if (output_type == ADV7393_COMPONENT_ID) + val &= SD_DAC_OUT1_DI; + else + val |= SD_DAC_OUT1_EN; + err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); + if (err < 0) + goto setoutput_exit; + + state->reg82 = val; + + /* configure ED/HD Color DAC Swap bit to zero */ + val = state->reg35 & HD_DAC_SWAP_DI; + err = adv7393_write(sd, ADV7393_HD_MODE_REG6, val); + if (err < 0) + goto setoutput_exit; + + state->reg35 = val; + +setoutput_exit: + if (err != 0) + v4l2_err(sd, "Error setting output, write failed\n"); + + return err; +} + +static int adv7393_log_status(struct v4l2_subdev *sd) +{ + struct adv7393_state *state = to_state(sd); + + v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); + v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : + ((state->output == 1) ? "Component" : "S-Video")); + return 0; +} + +static int adv7393_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return adv7393_write(sd, ADV7393_SD_BRIGHTNESS_WSS, + ctrl->val & SD_BRIGHTNESS_VALUE_MASK); + + case V4L2_CID_HUE: + return adv7393_write(sd, ADV7393_SD_HUE_ADJUST, + ctrl->val - ADV7393_HUE_MIN); + + case V4L2_CID_GAIN: + return adv7393_write(sd, ADV7393_DAC123_OUTPUT_LEVEL, + ctrl->val); + } + return -EINVAL; +} + +static int adv7393_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7393, 0); +} + +static const struct v4l2_ctrl_ops adv7393_ctrl_ops = { + .s_ctrl = adv7393_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops adv7393_core_ops = { + .log_status = adv7393_log_status, + .g_chip_ident = adv7393_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static int adv7393_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + + if (state->std == std) + return 0; + + err = adv7393_setstd(sd, std); + if (!err) + state->std = std; + + return err; +} + +static int adv7393_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + + if (state->output == output) + return 0; + + err = adv7393_setoutput(sd, output); + if (!err) + state->output = output; + + return err; +} + +static const struct v4l2_subdev_video_ops adv7393_video_ops = { + .s_std_output = adv7393_s_std_output, + .s_routing = adv7393_s_routing, +}; + +static const struct v4l2_subdev_ops adv7393_ops = { + .core = &adv7393_core_ops, + .video = &adv7393_video_ops, +}; + +static int adv7393_initialize(struct v4l2_subdev *sd) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(adv7393_init_reg_val); i += 2) { + + err = adv7393_write(sd, adv7393_init_reg_val[i], + adv7393_init_reg_val[i+1]); + if (err) { + v4l2_err(sd, "Error initializing\n"); + return err; + } + } + + /* Configure for default video standard */ + err = adv7393_setoutput(sd, state->output); + if (err < 0) { + v4l2_err(sd, "Error setting output during init\n"); + return -EINVAL; + } + + err = adv7393_setstd(sd, state->std); + if (err < 0) { + v4l2_err(sd, "Error setting std during init\n"); + return -EINVAL; + } + + return err; +} + +static int adv7393_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7393_state *state; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct adv7393_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + state->reg00 = ADV7393_POWER_MODE_REG_DEFAULT; + state->reg01 = 0x00; + state->reg02 = 0x20; + state->reg35 = ADV7393_HD_MODE_REG6_DEFAULT; + state->reg80 = ADV7393_SD_MODE_REG1_DEFAULT; + state->reg82 = ADV7393_SD_MODE_REG2_DEFAULT; + + state->output = ADV7393_COMPOSITE_ID; + state->std = V4L2_STD_NTSC; + + v4l2_i2c_subdev_init(&state->sd, client, &adv7393_ops); + + v4l2_ctrl_handler_init(&state->hdl, 3); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_BRIGHTNESS, ADV7393_BRIGHTNESS_MIN, + ADV7393_BRIGHTNESS_MAX, 1, + ADV7393_BRIGHTNESS_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_HUE, ADV7393_HUE_MIN, + ADV7393_HUE_MAX, 1, + ADV7393_HUE_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_GAIN, ADV7393_GAIN_MIN, + ADV7393_GAIN_MAX, 1, + ADV7393_GAIN_DEF); + state->sd.ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_handler_setup(&state->hdl); + + err = adv7393_initialize(&state->sd); + if (err) { + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + } + return err; +} + +static int adv7393_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7393_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + + return 0; +} + +static const struct i2c_device_id adv7393_id[] = { + {"adv7393", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, adv7393_id); + +static struct i2c_driver adv7393_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7393", + }, + .probe = adv7393_probe, + .remove = adv7393_remove, + .id_table = adv7393_id, +}; +module_i2c_driver(adv7393_driver); diff --git a/drivers/media/video/adv7393_regs.h b/drivers/media/video/adv7393_regs.h new file mode 100644 index 000000000000..78968330f0be --- /dev/null +++ b/drivers/media/video/adv7393_regs.h @@ -0,0 +1,188 @@ +/* + * ADV7393 encoder related structure and register definitions + * + * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ + * Benoît Thébaudeau <benoit.thebaudeau@advansee.com> + * + * Based on ADV7343 driver, + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ADV7393_REGS_H +#define ADV7393_REGS_H + +struct adv7393_std_info { + u32 standard_val3; + u32 fsc_val; + v4l2_std_id stdid; +}; + +/* Register offset macros */ +#define ADV7393_POWER_MODE_REG (0x00) +#define ADV7393_MODE_SELECT_REG (0x01) +#define ADV7393_MODE_REG0 (0x02) + +#define ADV7393_DAC123_OUTPUT_LEVEL (0x0B) + +#define ADV7393_SOFT_RESET (0x17) + +#define ADV7393_HD_MODE_REG1 (0x30) +#define ADV7393_HD_MODE_REG2 (0x31) +#define ADV7393_HD_MODE_REG3 (0x32) +#define ADV7393_HD_MODE_REG4 (0x33) +#define ADV7393_HD_MODE_REG5 (0x34) +#define ADV7393_HD_MODE_REG6 (0x35) + +#define ADV7393_HD_MODE_REG7 (0x39) + +#define ADV7393_SD_MODE_REG1 (0x80) +#define ADV7393_SD_MODE_REG2 (0x82) +#define ADV7393_SD_MODE_REG3 (0x83) +#define ADV7393_SD_MODE_REG4 (0x84) +#define ADV7393_SD_MODE_REG5 (0x86) +#define ADV7393_SD_MODE_REG6 (0x87) +#define ADV7393_SD_MODE_REG7 (0x88) +#define ADV7393_SD_MODE_REG8 (0x89) + +#define ADV7393_SD_TIMING_REG0 (0x8A) + +#define ADV7393_FSC_REG0 (0x8C) +#define ADV7393_FSC_REG1 (0x8D) +#define ADV7393_FSC_REG2 (0x8E) +#define ADV7393_FSC_REG3 (0x8F) + +#define ADV7393_SD_CGMS_WSS0 (0x99) + +#define ADV7393_SD_HUE_ADJUST (0xA0) +#define ADV7393_SD_BRIGHTNESS_WSS (0xA1) + +/* Default values for the registers */ +#define ADV7393_POWER_MODE_REG_DEFAULT (0x10) +#define ADV7393_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default + 720p EAV/SAV code*/ +#define ADV7393_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data + valid */ +#define ADV7393_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ +#define ADV7393_HD_MODE_REG4_DEFAULT (0xEC) /* Changed */ +#define ADV7393_HD_MODE_REG5_DEFAULT (0x08) +#define ADV7393_HD_MODE_REG6_DEFAULT (0x00) +#define ADV7393_HD_MODE_REG7_DEFAULT (0x00) +#define ADV7393_SOFT_RESET_DEFAULT (0x02) +#define ADV7393_COMPOSITE_POWER_VALUE (0x10) +#define ADV7393_COMPONENT_POWER_VALUE (0x1C) +#define ADV7393_SVIDEO_POWER_VALUE (0x0C) +#define ADV7393_SD_HUE_ADJUST_DEFAULT (0x80) +#define ADV7393_SD_BRIGHTNESS_WSS_DEFAULT (0x00) + +#define ADV7393_SD_CGMS_WSS0_DEFAULT (0x10) + +#define ADV7393_SD_MODE_REG1_DEFAULT (0x10) +#define ADV7393_SD_MODE_REG2_DEFAULT (0xC9) +#define ADV7393_SD_MODE_REG3_DEFAULT (0x00) +#define ADV7393_SD_MODE_REG4_DEFAULT (0x00) +#define ADV7393_SD_MODE_REG5_DEFAULT (0x02) +#define ADV7393_SD_MODE_REG6_DEFAULT (0x8C) +#define ADV7393_SD_MODE_REG7_DEFAULT (0x14) +#define ADV7393_SD_MODE_REG8_DEFAULT (0x00) + +#define ADV7393_SD_TIMING_REG0_DEFAULT (0x0C) + +/* Bit masks for Mode Select Register */ +#define INPUT_MODE_MASK (0x70) +#define SD_INPUT_MODE (0x00) +#define HD_720P_INPUT_MODE (0x10) +#define HD_1080I_INPUT_MODE (0x10) + +/* Bit masks for Mode Register 0 */ +#define TEST_PATTERN_BLACK_BAR_EN (0x04) +#define YUV_OUTPUT_SELECT (0x20) +#define RGB_OUTPUT_SELECT (0xDF) + +/* Bit masks for SD brightness/WSS */ +#define SD_BRIGHTNESS_VALUE_MASK (0x7F) +#define SD_BLANK_WSS_DATA_MASK (0x80) + +/* Bit masks for soft reset register */ +#define SOFT_RESET (0x02) + +/* Bit masks for HD Mode Register 1 */ +#define OUTPUT_STD_MASK (0x03) +#define OUTPUT_STD_SHIFT (0) +#define OUTPUT_STD_EIA0_2 (0x00) +#define OUTPUT_STD_EIA0_1 (0x01) +#define OUTPUT_STD_FULL (0x02) +#define EMBEDDED_SYNC (0x04) +#define EXTERNAL_SYNC (0xFB) +#define STD_MODE_MASK (0x1F) +#define STD_MODE_SHIFT (3) +#define STD_MODE_720P (0x05) +#define STD_MODE_720P_25 (0x08) +#define STD_MODE_720P_30 (0x07) +#define STD_MODE_720P_50 (0x06) +#define STD_MODE_1080I (0x0D) +#define STD_MODE_1080I_25 (0x0E) +#define STD_MODE_1080P_24 (0x11) +#define STD_MODE_1080P_25 (0x10) +#define STD_MODE_1080P_30 (0x0F) +#define STD_MODE_525P (0x00) +#define STD_MODE_625P (0x03) + +/* Bit masks for SD Mode Register 1 */ +#define SD_STD_MASK (0x03) +#define SD_STD_NTSC (0x00) +#define SD_STD_PAL_BDGHI (0x01) +#define SD_STD_PAL_M (0x02) +#define SD_STD_PAL_N (0x03) +#define SD_LUMA_FLTR_MASK (0x07) +#define SD_LUMA_FLTR_SHIFT (2) +#define SD_CHROMA_FLTR_MASK (0x07) +#define SD_CHROMA_FLTR_SHIFT (5) + +/* Bit masks for SD Mode Register 2 */ +#define SD_PRPB_SSAF_EN (0x01) +#define SD_PRPB_SSAF_DI (0xFE) +#define SD_DAC_OUT1_EN (0x02) +#define SD_DAC_OUT1_DI (0xFD) +#define SD_PEDESTAL_EN (0x08) +#define SD_PEDESTAL_DI (0xF7) +#define SD_SQUARE_PIXEL_EN (0x10) +#define SD_SQUARE_PIXEL_DI (0xEF) +#define SD_PIXEL_DATA_VALID (0x40) +#define SD_ACTIVE_EDGE_EN (0x80) +#define SD_ACTIVE_EDGE_DI (0x7F) + +/* Bit masks for HD Mode Register 6 */ +#define HD_PRPB_SYNC_EN (0x04) +#define HD_PRPB_SYNC_DI (0xFB) +#define HD_DAC_SWAP_EN (0x08) +#define HD_DAC_SWAP_DI (0xF7) +#define HD_GAMMA_CURVE_A (0xEF) +#define HD_GAMMA_CURVE_B (0x10) +#define HD_GAMMA_EN (0x20) +#define HD_GAMMA_DI (0xDF) +#define HD_ADPT_FLTR_MODEA (0xBF) +#define HD_ADPT_FLTR_MODEB (0x40) +#define HD_ADPT_FLTR_EN (0x80) +#define HD_ADPT_FLTR_DI (0x7F) + +#define ADV7393_BRIGHTNESS_MAX (63) +#define ADV7393_BRIGHTNESS_MIN (-64) +#define ADV7393_BRIGHTNESS_DEF (0) +#define ADV7393_HUE_MAX (127) +#define ADV7393_HUE_MIN (-128) +#define ADV7393_HUE_DEF (0) +#define ADV7393_GAIN_MAX (64) +#define ADV7393_GAIN_MIN (-64) +#define ADV7393_GAIN_DEF (0) + +#endif diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 856ab962cd63..5f3a00c2c4f6 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -676,6 +676,7 @@ struct tvcard bttv_tvcards[] = { .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .has_remote = 1, + .has_radio = 1, /* not every card has radio */ }, [BTTV_BOARD_VOBIS_BOOSTAR] = { .name = "Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar", diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index ff7a589d8e0f..b58ff87db771 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -558,12 +558,6 @@ static const struct bttv_format formats[] = { .depth = 16, .flags = FORMAT_FLAGS_PACKED, },{ - .name = "4:2:2, packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, - .btformat = BT848_COLOR_FMT_YUY2, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - },{ .name = "4:2:2, packed, UYVY", .fourcc = V4L2_PIX_FMT_UYVY, .btformat = BT848_COLOR_FMT_YUY2, diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 55e92902a76c..a62a7b739991 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -932,7 +932,7 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->sequence = cam->buffers[buf->index].seq; buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer; buf->length = cam->frame_size; - buf->input = 0; + buf->reserved2 = 0; buf->reserved = 0; memset(&buf->timecode, 0, sizeof(buf->timecode)); diff --git a/drivers/media/video/cs8420.h b/drivers/media/video/cs8420.h deleted file mode 100644 index 621c0c6678ea..000000000000 --- a/drivers/media/video/cs8420.h +++ /dev/null @@ -1,50 +0,0 @@ -/* cs8420.h - cs8420 initializations - Copyright (C) 1999 Nathan Laredo (laredo@gnu.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. - - 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 __CS8420_H__ -#define __CS8420_H__ - -/* Initialization Sequence */ - -static __u8 init8420[] = { - 1, 0x01, 2, 0x02, 3, 0x00, 4, 0x46, - 5, 0x24, 6, 0x84, 18, 0x18, 19, 0x13, -}; - -#define INIT8420LEN (sizeof(init8420)/2) - -static __u8 mode8420pro[] = { /* professional output mode */ - 32, 0xa1, 33, 0x00, 34, 0x00, 35, 0x00, - 36, 0x00, 37, 0x00, 38, 0x00, 39, 0x00, - 40, 0x00, 41, 0x00, 42, 0x00, 43, 0x00, - 44, 0x00, 45, 0x00, 46, 0x00, 47, 0x00, - 48, 0x00, 49, 0x00, 50, 0x00, 51, 0x00, - 52, 0x00, 53, 0x00, 54, 0x00, 55, 0x00, -}; -#define MODE8420LEN (sizeof(mode8420pro)/2) - -static __u8 mode8420con[] = { /* consumer output mode */ - 32, 0x20, 33, 0x00, 34, 0x00, 35, 0x48, - 36, 0x00, 37, 0x00, 38, 0x00, 39, 0x00, - 40, 0x00, 41, 0x00, 42, 0x00, 43, 0x00, - 44, 0x00, 45, 0x00, 46, 0x00, 47, 0x00, - 48, 0x00, 49, 0x00, 50, 0x00, 51, 0x00, - 52, 0x00, 53, 0x00, 54, 0x00, 55, 0x00, -}; - -#endif diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 35fde4e931f5..e9912db3b496 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -1142,24 +1142,6 @@ static long cx18_default(struct file *file, void *fh, bool valid_prio, return 0; } -long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) -{ - struct video_device *vfd = video_devdata(filp); - struct cx18_open_id *id = file2id(filp); - struct cx18 *cx = id->cx; - long res; - - mutex_lock(&cx->serialize_lock); - - if (cx18_debug & CX18_DBGFLG_IOCTL) - vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; - res = video_ioctl2(filp, cmd, arg); - vfd->debug = 0; - mutex_unlock(&cx->serialize_lock); - return res; -} - static const struct v4l2_ioctl_ops cx18_ioctl_ops = { .vidioc_querycap = cx18_querycap, .vidioc_s_audio = cx18_s_audio, diff --git a/drivers/media/video/cx18/cx18-ioctl.h b/drivers/media/video/cx18/cx18-ioctl.h index dcb2559ad520..2f9dd591ee0f 100644 --- a/drivers/media/video/cx18/cx18-ioctl.h +++ b/drivers/media/video/cx18/cx18-ioctl.h @@ -29,5 +29,3 @@ void cx18_set_funcs(struct video_device *vdev); int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std); int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); int cx18_s_input(struct file *file, void *fh, unsigned int inp); -long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg); diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 4185bcb80ca3..9d598ab88615 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -40,8 +40,7 @@ static struct v4l2_file_operations cx18_v4l2_enc_fops = { .owner = THIS_MODULE, .read = cx18_v4l2_read, .open = cx18_v4l2_open, - /* FIXME change to video_ioctl2 if serialization lock can be removed */ - .unlocked_ioctl = cx18_v4l2_ioctl, + .unlocked_ioctl = video_ioctl2, .release = cx18_v4l2_close, .poll = cx18_v4l2_enc_poll, .mmap = cx18_v4l2_mmap, @@ -376,6 +375,7 @@ static int cx18_prep_dev(struct cx18 *cx, int type) s->video_dev->fops = &cx18_v4l2_enc_fops; s->video_dev->release = video_device_release; s->video_dev->tvnorms = V4L2_STD_ALL; + s->video_dev->lock = &cx->serialize_lock; set_bit(V4L2_FL_USE_FH_PRIO, &s->video_dev->flags); cx18_set_funcs(s->video_dev); return 0; diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c index b085a3c6dc04..447148eff958 100644 --- a/drivers/media/video/cx231xx/cx231xx-avcore.c +++ b/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -89,7 +89,7 @@ void initGPIO(struct cx231xx *dev) verve_read_byte(dev, 0x07, &val); cx231xx_info(" verve_read_byte address0x07=0x%x\n", val); - cx231xx_capture_start(dev, 1, 2); + cx231xx_capture_start(dev, 1, Vbi); cx231xx_mode_register(dev, EP_MODE_SET, 0x0500FE00); cx231xx_mode_register(dev, GBULK_BIT_EN, 0xFFFDFFFF); @@ -99,7 +99,7 @@ void uninitGPIO(struct cx231xx *dev) { u8 value[4] = { 0, 0, 0, 0 }; - cx231xx_capture_start(dev, 0, 2); + cx231xx_capture_start(dev, 0, Vbi); verve_write_byte(dev, 0x07, 0x14); cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, 0x68, value, 4); @@ -2516,29 +2516,29 @@ int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type) if (dev->udev->speed == USB_SPEED_HIGH) { switch (media_type) { - case 81: /* audio */ + case Audio: cx231xx_info("%s: Audio enter HANC\n", __func__); status = cx231xx_mode_register(dev, TS_MODE_REG, 0x9300); break; - case 2: /* vbi */ + case Vbi: cx231xx_info("%s: set vanc registers\n", __func__); status = cx231xx_mode_register(dev, TS_MODE_REG, 0x300); break; - case 3: /* sliced cc */ + case Sliced_cc: cx231xx_info("%s: set hanc registers\n", __func__); status = cx231xx_mode_register(dev, TS_MODE_REG, 0x1300); break; - case 0: /* video */ + case Raw_Video: cx231xx_info("%s: set video registers\n", __func__); status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100); break; - case 4: /* ts1 */ + case TS1_serial_mode: cx231xx_info("%s: set ts1 registers", __func__); if (dev->board.has_417) { @@ -2569,7 +2569,7 @@ int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type) } break; - case 6: /* ts1 parallel mode */ + case TS1_parallel_mode: cx231xx_info("%s: set ts1 parallel mode registers\n", __func__); status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100); @@ -2592,52 +2592,28 @@ int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type) /* get EP for media type */ pcb_config = (struct pcb_config *)&dev->current_pcb_config; - if (pcb_config->config_num == 1) { + if (pcb_config->config_num) { switch (media_type) { - case 0: /* Video */ + case Raw_Video: ep_mask = ENABLE_EP4; /* ep4 [00:1000] */ break; - case 1: /* Audio */ + case Audio: ep_mask = ENABLE_EP3; /* ep3 [00:0100] */ break; - case 2: /* Vbi */ + case Vbi: ep_mask = ENABLE_EP5; /* ep5 [01:0000] */ break; - case 3: /* Sliced_cc */ + case Sliced_cc: ep_mask = ENABLE_EP6; /* ep6 [10:0000] */ break; - case 4: /* ts1 */ - case 6: /* ts1 parallel mode */ + case TS1_serial_mode: + case TS1_parallel_mode: ep_mask = ENABLE_EP1; /* ep1 [00:0001] */ break; - case 5: /* ts2 */ + case TS2: ep_mask = ENABLE_EP2; /* ep2 [00:0010] */ break; } - - } else if (pcb_config->config_num > 1) { - switch (media_type) { - case 0: /* Video */ - ep_mask = ENABLE_EP4; /* ep4 [00:1000] */ - break; - case 1: /* Audio */ - ep_mask = ENABLE_EP3; /* ep3 [00:0100] */ - break; - case 2: /* Vbi */ - ep_mask = ENABLE_EP5; /* ep5 [01:0000] */ - break; - case 3: /* Sliced_cc */ - ep_mask = ENABLE_EP6; /* ep6 [10:0000] */ - break; - case 4: /* ts1 */ - case 6: /* ts1 parallel mode */ - ep_mask = ENABLE_EP1; /* ep1 [00:0001] */ - break; - case 5: /* ts2 */ - ep_mask = ENABLE_EP2; /* ep2 [00:0010] */ - break; - } - } if (start) { diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c index 8ed460d692e0..02d4d36735d3 100644 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ b/drivers/media/video/cx231xx/cx231xx-cards.c @@ -1023,7 +1023,6 @@ static int cx231xx_usb_probe(struct usb_interface *interface, int nr = 0, ifnum; int i, isoc_pipe = 0; char *speed; - char descr[255] = ""; struct usb_interface_assoc_descriptor *assoc_desc; udev = usb_get_dev(interface_to_usbdev(interface)); @@ -1098,20 +1097,10 @@ static int cx231xx_usb_probe(struct usb_interface *interface, speed = "unknown"; } - if (udev->manufacturer) - strlcpy(descr, udev->manufacturer, sizeof(descr)); - - if (udev->product) { - if (*descr) - strlcat(descr, " ", sizeof(descr)); - strlcat(descr, udev->product, sizeof(descr)); - } - if (*descr) - strlcat(descr, " ", sizeof(descr)); - - cx231xx_info("New device %s@ %s Mbps " + cx231xx_info("New device %s %s @ %s Mbps " "(%04x:%04x) with %d interfaces\n", - descr, + udev->manufacturer ? udev->manufacturer : "", + udev->product ? udev->product : "", speed, le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct), diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index 04bf6627d362..dfac6e34859f 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -585,13 +585,10 @@ static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol, { snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core = chip->core; - struct v4l2_control client_ctl; int left = value->value.integer.value[0]; int right = value->value.integer.value[1]; int v, b; - memset(&client_ctl, 0, sizeof(client_ctl)); - /* Pass volume & balance onto any WM8775 */ if (left >= right) { v = left << 10; @@ -600,13 +597,8 @@ static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol, v = right << 10; b = right ? 0xffff - (0x8000 * left) / right : 0x8000; } - client_ctl.value = v; - client_ctl.id = V4L2_CID_AUDIO_VOLUME; - call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); - - client_ctl.value = b; - client_ctl.id = V4L2_CID_AUDIO_BALANCE; - call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); + wm8775_s_ctrl(core, V4L2_CID_AUDIO_VOLUME, v); + wm8775_s_ctrl(core, V4L2_CID_AUDIO_BALANCE, b); } /* OK - TODO: test it */ @@ -687,14 +679,8 @@ static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol, cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol); /* Pass mute onto any WM8775 */ if ((core->board.audio_chip == V4L2_IDENT_WM8775) && - ((1<<6) == bit)) { - struct v4l2_control client_ctl; - - memset(&client_ctl, 0, sizeof(client_ctl)); - client_ctl.value = 0 != (vol & bit); - client_ctl.id = V4L2_CID_AUDIO_MUTE; - call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); - } + ((1<<6) == bit)) + wm8775_s_ctrl(core, V4L2_CID_AUDIO_MUTE, 0 != (vol & bit)); ret = 1; } spin_unlock_irq(&chip->reg_lock); @@ -724,13 +710,10 @@ static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol, { snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core = chip->core; - struct v4l2_control client_ctl; - - memset(&client_ctl, 0, sizeof(client_ctl)); - client_ctl.id = V4L2_CID_AUDIO_LOUDNESS; - call_hw(core, WM8775_GID, core, g_ctrl, &client_ctl); - value->value.integer.value[0] = client_ctl.value ? 1 : 0; + s32 val; + val = wm8775_g_ctrl(core, V4L2_CID_AUDIO_LOUDNESS); + value->value.integer.value[0] = val ? 1 : 0; return 0; } diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index ed7b2aa1ed83..843ffd9e533b 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -35,6 +35,7 @@ #include <linux/firmware.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include <media/cx2341x.h> #include "cx88.h" @@ -523,11 +524,10 @@ static void blackbird_codec_settings(struct cx8802_dev *dev) blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, dev->height, dev->width); - dev->params.width = dev->width; - dev->params.height = dev->height; - dev->params.is_50hz = (dev->core->tvnorm & V4L2_STD_625_50) != 0; - - cx2341x_update(dev, blackbird_mbox_func, NULL, &dev->params); + dev->cxhdl.width = dev->width; + dev->cxhdl.height = dev->height; + cx2341x_handler_set_50hz(&dev->cxhdl, dev->core->tvnorm & V4L2_STD_625_50); + cx2341x_handler_setup(&dev->cxhdl); } static int blackbird_initialize_codec(struct cx8802_dev *dev) @@ -618,6 +618,8 @@ static int blackbird_start_codec(struct file *file, void *priv) /* initialize the video input */ blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0); + cx2341x_handler_set_busy(&dev->cxhdl, 1); + /* start capturing to the host interface */ blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0, BLACKBIRD_MPEG_CAPTURE, @@ -636,6 +638,8 @@ static int blackbird_stop_codec(struct cx8802_dev *dev) BLACKBIRD_RAW_BITS_NONE ); + cx2341x_handler_set_busy(&dev->cxhdl, 0); + dev->mpeg_active = 0; return 0; } @@ -685,58 +689,15 @@ static struct videobuf_queue_ops blackbird_qops = { /* ------------------------------------------------------------------ */ -static const u32 *ctrl_classes[] = { - cx88_user_ctrls, - cx2341x_mpeg_ctrls, - NULL -}; - -static int blackbird_queryctrl(struct cx8802_dev *dev, struct v4l2_queryctrl *qctrl) -{ - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); - if (qctrl->id == 0) - return -EINVAL; - - /* Standard V4L2 controls */ - if (cx8800_ctrl_query(dev->core, qctrl) == 0) - return 0; - - /* MPEG V4L2 controls */ - if (cx2341x_ctrl_query(&dev->params, qctrl)) - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - return 0; -} - -/* ------------------------------------------------------------------ */ -/* IOCTL Handlers */ - -static int vidioc_querymenu (struct file *file, void *priv, - struct v4l2_querymenu *qmenu) -{ - struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; - struct v4l2_queryctrl qctrl; - - qctrl.id = qmenu->id; - blackbird_queryctrl(dev, &qctrl); - return v4l2_ctrl_query_menu(qmenu, &qctrl, - cx2341x_ctrl_get_menu(&dev->params, qmenu->id)); -} - -static int vidioc_querycap (struct file *file, void *priv, +static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; struct cx88_core *core = dev->core; strcpy(cap->driver, "cx88_blackbird"); - strlcpy(cap->card, core->board.name, sizeof(cap->card)); - sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - if (UNSET != core->board.tuner_type) - cap->capabilities |= V4L2_CAP_TUNER; + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); + cx88_querycap(file, core, cap); return 0; } @@ -748,6 +709,7 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, strlcpy(f->description, "MPEG", sizeof(f->description)); f->pixelformat = V4L2_PIX_FMT_MPEG; + f->flags = V4L2_FMT_FLAG_COMPRESSED; return 0; } @@ -759,12 +721,12 @@ static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */ - f->fmt.pix.colorspace = 0; + f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; f->fmt.pix.field = fh->mpegq.field; - dprintk(0,"VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", + dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", dev->width, dev->height, fh->mpegq.field ); return 0; } @@ -777,9 +739,9 @@ static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; - f->fmt.pix.colorspace = 0; - dprintk(0,"VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", + f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", dev->width, dev->height, fh->mpegq.field ); return 0; } @@ -793,15 +755,15 @@ static int vidioc_s_fmt_vid_cap (struct file *file, void *priv, f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; - f->fmt.pix.colorspace = 0; + f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; dev->width = f->fmt.pix.width; dev->height = f->fmt.pix.height; fh->mpegq.field = f->fmt.pix.field; cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, f->fmt.pix.height, f->fmt.pix.width); - dprintk(0,"VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", + dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field ); return 0; } @@ -834,60 +796,21 @@ static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p) static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct cx8802_fh *fh = priv; + struct cx8802_dev *dev = fh->dev; + + if (!dev->mpeg_active) + blackbird_start_codec(file, fh); return videobuf_streamon(&fh->mpegq); } static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { struct cx8802_fh *fh = priv; - return videobuf_streamoff(&fh->mpegq); -} - -static int vidioc_g_ext_ctrls (struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; - - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - return cx2341x_ext_ctrls(&dev->params, 0, f, VIDIOC_G_EXT_CTRLS); -} - -static int vidioc_s_ext_ctrls (struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; - struct cx2341x_mpeg_params p; - int err; - - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; + struct cx8802_dev *dev = fh->dev; if (dev->mpeg_active) blackbird_stop_codec(dev); - - p = dev->params; - err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS); - if (!err) { - err = cx2341x_update(dev, blackbird_mbox_func, &dev->params, &p); - dev->params = p; - } - return err; -} - -static int vidioc_try_ext_ctrls (struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; - struct cx2341x_mpeg_params p; - int err; - - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - p = dev->params; - err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS); - - return err; + return videobuf_streamoff(&fh->mpegq); } static int vidioc_s_frequency (struct file *file, void *priv, @@ -897,6 +820,10 @@ static int vidioc_s_frequency (struct file *file, void *priv, struct cx8802_dev *dev = fh->dev; struct cx88_core *core = dev->core; + if (unlikely(UNSET == core->board.tuner_type)) + return -EINVAL; + if (unlikely(f->tuner != 0)) + return -EINVAL; if (dev->mpeg_active) blackbird_stop_codec(dev); @@ -914,29 +841,11 @@ static int vidioc_log_status (struct file *file, void *priv) char name[32 + 2]; snprintf(name, sizeof(name), "%s/2", core->name); - printk("%s/2: ============ START LOG STATUS ============\n", - core->name); call_all(core, core, log_status); - cx2341x_log_status(&dev->params, name); - printk("%s/2: ============= END LOG STATUS =============\n", - core->name); + v4l2_ctrl_handler_log_status(&dev->cxhdl.hdl, name); return 0; } -static int vidioc_queryctrl (struct file *file, void *priv, - struct v4l2_queryctrl *qctrl) -{ - struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; - - if (blackbird_queryctrl(dev, qctrl) == 0) - return 0; - - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); - if (unlikely(qctrl->id == 0)) - return -EINVAL; - return cx8800_ctrl_query(dev->core, qctrl); -} - static int vidioc_enum_input (struct file *file, void *priv, struct v4l2_input *i) { @@ -944,22 +853,6 @@ static int vidioc_enum_input (struct file *file, void *priv, return cx88_enum_input (core,i); } -static int vidioc_g_ctrl (struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; - return - cx88_get_control(core,ctl); -} - -static int vidioc_s_ctrl (struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; - return - cx88_set_control(core,ctl); -} - static int vidioc_g_frequency (struct file *file, void *priv, struct v4l2_frequency *f) { @@ -968,8 +861,9 @@ static int vidioc_g_frequency (struct file *file, void *priv, if (unlikely(UNSET == core->board.tuner_type)) return -EINVAL; + if (unlikely(f->tuner != 0)) + return -EINVAL; - f->type = V4L2_TUNER_ANALOG_TV; f->frequency = core->freq; call_all(core, tuner, g_frequency, f); @@ -990,6 +884,8 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int i) if (i >= 4) return -EINVAL; + if (0 == INPUT(i).type) + return -EINVAL; mutex_lock(&core->lock); cx88_newstation(core); @@ -1010,9 +906,9 @@ static int vidioc_g_tuner (struct file *file, void *priv, return -EINVAL; strcpy(t->name, "Television"); - t->type = V4L2_TUNER_ANALOG_TV; t->capability = V4L2_TUNER_CAP_NORM; t->rangehigh = 0xffffffffUL; + call_all(core, tuner, g_tuner, t); cx88_get_stereo(core ,t); reg = cx_read(MO_DEVICE_STATUS); @@ -1034,6 +930,14 @@ static int vidioc_s_tuner (struct file *file, void *priv, return 0; } +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm) +{ + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + + *tvnorm = core->tvnorm; + return 0; +} + static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id) { struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; @@ -1087,6 +991,7 @@ static int mpeg_open(struct file *file) mutex_unlock(&dev->core->lock); return -ENOMEM; } + v4l2_fh_init(&fh->fh, vdev); file->private_data = fh; fh->dev = dev; @@ -1103,6 +1008,7 @@ static int mpeg_open(struct file *file) dev->core->mpeg_users++; mutex_unlock(&dev->core->lock); + v4l2_fh_add(&fh->fh); return 0; } @@ -1123,6 +1029,8 @@ static int mpeg_release(struct file *file) videobuf_mmap_free(&fh->mpegq); + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); file->private_data = NULL; kfree(fh); @@ -1155,13 +1063,14 @@ mpeg_read(struct file *file, char __user *data, size_t count, loff_t *ppos) static unsigned int mpeg_poll(struct file *file, struct poll_table_struct *wait) { + unsigned long req_events = poll_requested_events(wait); struct cx8802_fh *fh = file->private_data; struct cx8802_dev *dev = fh->dev; - if (!dev->mpeg_active) + if (!dev->mpeg_active && (req_events & (POLLIN | POLLRDNORM))) blackbird_start_codec(file, fh); - return videobuf_poll_stream(file, &fh->mpegq, wait); + return v4l2_ctrl_poll(file, wait) | videobuf_poll_stream(file, &fh->mpegq, wait); } static int @@ -1180,11 +1089,10 @@ static const struct v4l2_file_operations mpeg_fops = .read = mpeg_read, .poll = mpeg_poll, .mmap = mpeg_mmap, - .ioctl = video_ioctl2, + .unlocked_ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { - .vidioc_querymenu = vidioc_querymenu, .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, @@ -1196,21 +1104,18 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { .vidioc_dqbuf = vidioc_dqbuf, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, - .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, - .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, - .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, .vidioc_s_frequency = vidioc_s_frequency, .vidioc_log_status = vidioc_log_status, - .vidioc_queryctrl = vidioc_queryctrl, .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_std = vidioc_g_std, .vidioc_s_std = vidioc_s_std, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device cx8802_mpeg_template = { @@ -1218,7 +1123,6 @@ static struct video_device cx8802_mpeg_template = { .fops = &mpeg_fops, .ioctl_ops = &mpeg_ioctl_ops, .tvnorms = CX88_NORMS, - .current_norm = V4L2_STD_NTSC_M, }; /* ------------------------------------------------------------------ */ @@ -1286,6 +1190,7 @@ static int blackbird_register_video(struct cx8802_dev *dev) dev->mpeg_dev = cx88_vdev_init(dev->core,dev->pci, &cx8802_mpeg_template,"mpeg"); + dev->mpeg_dev->ctrl_handler = &dev->cxhdl.hdl; video_set_drvdata(dev->mpeg_dev, dev); err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1); if (err < 0) { @@ -1318,17 +1223,20 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv) goto fail_core; dev->width = 720; - dev->height = 576; - cx2341x_fill_defaults(&dev->params); - dev->params.port = CX2341X_PORT_STREAMING; - - cx8802_mpeg_template.current_norm = core->tvnorm; - if (core->tvnorm & V4L2_STD_525_60) { dev->height = 480; } else { dev->height = 576; } + dev->cxhdl.port = CX2341X_PORT_STREAMING; + dev->cxhdl.width = dev->width; + dev->cxhdl.height = dev->height; + dev->cxhdl.func = blackbird_mbox_func; + dev->cxhdl.priv = dev; + err = cx2341x_handler_init(&dev->cxhdl, 36); + if (err) + goto fail_core; + v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl); /* blackbird stuff */ printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n", @@ -1336,12 +1244,14 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv) host_setup(dev->core); blackbird_initialize_codec(dev); - blackbird_register_video(dev); /* initial device configuration: needed ? */ // init_controls(core); cx88_set_tvnorm(core,core->tvnorm); cx88_video_mux(core,0); + cx2341x_handler_set_50hz(&dev->cxhdl, dev->height == 576); + cx2341x_handler_setup(&dev->cxhdl); + blackbird_register_video(dev); return 0; @@ -1351,8 +1261,12 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv) static int cx8802_blackbird_remove(struct cx8802_driver *drv) { + struct cx88_core *core = drv->core; + struct cx8802_dev *dev = core->dvbdev; + /* blackbird */ blackbird_unregister_video(drv->core->dvbdev); + v4l2_ctrl_handler_free(&dev->cxhdl.hdl); return 0; } diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index cbd5d119a2c6..4e9d4f722960 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -3693,7 +3693,22 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) return NULL; } + if (v4l2_ctrl_handler_init(&core->video_hdl, 13)) { + v4l2_device_unregister(&core->v4l2_dev); + kfree(core); + return NULL; + } + + if (v4l2_ctrl_handler_init(&core->audio_hdl, 13)) { + v4l2_ctrl_handler_free(&core->video_hdl); + v4l2_device_unregister(&core->v4l2_dev); + kfree(core); + return NULL; + } + if (0 != cx88_get_resources(core, pci)) { + v4l2_ctrl_handler_free(&core->video_hdl); + v4l2_ctrl_handler_free(&core->audio_hdl); v4l2_device_unregister(&core->v4l2_dev); kfree(core); return NULL; @@ -3706,6 +3721,11 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) core->bmmio = (u8 __iomem *)core->lmmio; if (core->lmmio == NULL) { + release_mem_region(pci_resource_start(pci, 0), + pci_resource_len(pci, 0)); + v4l2_ctrl_handler_free(&core->video_hdl); + v4l2_ctrl_handler_free(&core->audio_hdl); + v4l2_device_unregister(&core->v4l2_dev); kfree(core); return NULL; } diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index fbfdd8067937..e81c735f012a 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -1012,6 +1012,9 @@ int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm) // tell i2c chips call_all(core, core, s_std, norm); + /* The chroma_agc control should be inaccessible if the video format is SECAM */ + v4l2_ctrl_grab(core->chroma_agc, cxiformat == VideoFormatSECAM); + // done return 0; } @@ -1030,10 +1033,10 @@ struct video_device *cx88_vdev_init(struct cx88_core *core, return NULL; *vfd = *template_; vfd->v4l2_dev = &core->v4l2_dev; - vfd->parent = &pci->dev; vfd->release = video_device_release; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", core->name, type, core->board.name); + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); return vfd; } @@ -1086,6 +1089,8 @@ void cx88_core_put(struct cx88_core *core, struct pci_dev *pci) iounmap(core->lmmio); cx88_devcount--; mutex_unlock(&devlist); + v4l2_ctrl_handler_free(&core->video_hdl); + v4l2_ctrl_handler_free(&core->audio_hdl); v4l2_device_unregister(&core->v4l2_dev); kfree(core); } diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 921c56d115d6..f6fcc7e763ab 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -40,6 +40,7 @@ #include "cx88.h" #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include <media/wm8775.h> MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); @@ -155,219 +156,147 @@ static const struct cx8800_fmt* format_by_fourcc(unsigned int fourcc) /* ------------------------------------------------------------------- */ -static const struct v4l2_queryctrl no_ctl = { - .name = "42", - .flags = V4L2_CTRL_FLAG_DISABLED, +struct cx88_ctrl { + /* control information */ + u32 id; + s32 minimum; + s32 maximum; + u32 step; + s32 default_value; + + /* control register information */ + u32 off; + u32 reg; + u32 sreg; + u32 mask; + u32 shift; }; -static const struct cx88_ctrl cx8800_ctls[] = { +static const struct cx88_ctrl cx8800_vid_ctls[] = { /* --- video --- */ { - .v = { - .id = V4L2_CID_BRIGHTNESS, - .name = "Brightness", - .minimum = 0x00, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 128, - .reg = MO_CONTR_BRIGHT, - .mask = 0x00ff, - .shift = 0, + .id = V4L2_CID_BRIGHTNESS, + .minimum = 0x00, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + .off = 128, + .reg = MO_CONTR_BRIGHT, + .mask = 0x00ff, + .shift = 0, },{ - .v = { - .id = V4L2_CID_CONTRAST, - .name = "Contrast", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x3f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 0, - .reg = MO_CONTR_BRIGHT, - .mask = 0xff00, - .shift = 8, + .id = V4L2_CID_CONTRAST, + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x3f, + .off = 0, + .reg = MO_CONTR_BRIGHT, + .mask = 0xff00, + .shift = 8, },{ - .v = { - .id = V4L2_CID_HUE, - .name = "Hue", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 128, - .reg = MO_HUE, - .mask = 0x00ff, - .shift = 0, + .id = V4L2_CID_HUE, + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + .off = 128, + .reg = MO_HUE, + .mask = 0x00ff, + .shift = 0, },{ /* strictly, this only describes only U saturation. * V saturation is handled specially through code. */ - .v = { - .id = V4L2_CID_SATURATION, - .name = "Saturation", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 0, - .reg = MO_UV_SATURATION, - .mask = 0x00ff, - .shift = 0, + .id = V4L2_CID_SATURATION, + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + .off = 0, + .reg = MO_UV_SATURATION, + .mask = 0x00ff, + .shift = 0, }, { - .v = { - .id = V4L2_CID_SHARPNESS, - .name = "Sharpness", - .minimum = 0, - .maximum = 4, - .step = 1, - .default_value = 0x0, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 0, + .id = V4L2_CID_SHARPNESS, + .minimum = 0, + .maximum = 4, + .step = 1, + .default_value = 0x0, + .off = 0, /* NOTE: the value is converted and written to both even and odd registers in the code */ - .reg = MO_FILTER_ODD, - .mask = 7 << 7, - .shift = 7, + .reg = MO_FILTER_ODD, + .mask = 7 << 7, + .shift = 7, }, { - .v = { - .id = V4L2_CID_CHROMA_AGC, - .name = "Chroma AGC", - .minimum = 0, - .maximum = 1, - .default_value = 0x1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, - .reg = MO_INPUT_FORMAT, - .mask = 1 << 10, - .shift = 10, + .id = V4L2_CID_CHROMA_AGC, + .minimum = 0, + .maximum = 1, + .default_value = 0x1, + .reg = MO_INPUT_FORMAT, + .mask = 1 << 10, + .shift = 10, }, { - .v = { - .id = V4L2_CID_COLOR_KILLER, - .name = "Color killer", - .minimum = 0, - .maximum = 1, - .default_value = 0x1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, - .reg = MO_INPUT_FORMAT, - .mask = 1 << 9, - .shift = 9, + .id = V4L2_CID_COLOR_KILLER, + .minimum = 0, + .maximum = 1, + .default_value = 0x1, + .reg = MO_INPUT_FORMAT, + .mask = 1 << 9, + .shift = 9, }, { - .v = { - .id = V4L2_CID_BAND_STOP_FILTER, - .name = "Notch filter", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0x0, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .off = 0, - .reg = MO_HTOTAL, - .mask = 3 << 11, - .shift = 11, - }, { - /* --- audio --- */ - .v = { - .id = V4L2_CID_AUDIO_MUTE, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .default_value = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, - .reg = AUD_VOL_CTL, - .sreg = SHADOW_AUD_VOL_CTL, - .mask = (1 << 6), - .shift = 6, + .id = V4L2_CID_BAND_STOP_FILTER, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0x0, + .off = 0, + .reg = MO_HTOTAL, + .mask = 3 << 11, + .shift = 11, + } +}; + +static const struct cx88_ctrl cx8800_aud_ctls[] = { + { + /* --- audio --- */ + .id = V4L2_CID_AUDIO_MUTE, + .minimum = 0, + .maximum = 1, + .default_value = 1, + .reg = AUD_VOL_CTL, + .sreg = SHADOW_AUD_VOL_CTL, + .mask = (1 << 6), + .shift = 6, },{ - .v = { - .id = V4L2_CID_AUDIO_VOLUME, - .name = "Volume", - .minimum = 0, - .maximum = 0x3f, - .step = 1, - .default_value = 0x3f, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .reg = AUD_VOL_CTL, - .sreg = SHADOW_AUD_VOL_CTL, - .mask = 0x3f, - .shift = 0, + .id = V4L2_CID_AUDIO_VOLUME, + .minimum = 0, + .maximum = 0x3f, + .step = 1, + .default_value = 0x3f, + .reg = AUD_VOL_CTL, + .sreg = SHADOW_AUD_VOL_CTL, + .mask = 0x3f, + .shift = 0, },{ - .v = { - .id = V4L2_CID_AUDIO_BALANCE, - .name = "Balance", - .minimum = 0, - .maximum = 0x7f, - .step = 1, - .default_value = 0x40, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .reg = AUD_BAL_CTL, - .sreg = SHADOW_AUD_BAL_CTL, - .mask = 0x7f, - .shift = 0, + .id = V4L2_CID_AUDIO_BALANCE, + .minimum = 0, + .maximum = 0x7f, + .step = 1, + .default_value = 0x40, + .reg = AUD_BAL_CTL, + .sreg = SHADOW_AUD_BAL_CTL, + .mask = 0x7f, + .shift = 0, } }; -enum { CX8800_CTLS = ARRAY_SIZE(cx8800_ctls) }; - -/* Must be sorted from low to high control ID! */ -const u32 cx88_user_ctrls[] = { - V4L2_CID_USER_CLASS, - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_AUDIO_VOLUME, - V4L2_CID_AUDIO_BALANCE, - V4L2_CID_AUDIO_MUTE, - V4L2_CID_SHARPNESS, - V4L2_CID_CHROMA_AGC, - V4L2_CID_COLOR_KILLER, - V4L2_CID_BAND_STOP_FILTER, - 0 -}; -EXPORT_SYMBOL(cx88_user_ctrls); -static const u32 * const ctrl_classes[] = { - cx88_user_ctrls, - NULL +enum { + CX8800_VID_CTLS = ARRAY_SIZE(cx8800_vid_ctls), + CX8800_AUD_CTLS = ARRAY_SIZE(cx8800_aud_ctls), }; -int cx8800_ctrl_query(struct cx88_core *core, struct v4l2_queryctrl *qctrl) -{ - int i; - - if (qctrl->id < V4L2_CID_BASE || - qctrl->id >= V4L2_CID_LASTP1) - return -EINVAL; - for (i = 0; i < CX8800_CTLS; i++) - if (cx8800_ctls[i].v.id == qctrl->id) - break; - if (i == CX8800_CTLS) { - *qctrl = no_ctl; - return 0; - } - *qctrl = cx8800_ctls[i].v; - /* Report chroma AGC as inactive when SECAM is selected */ - if (cx8800_ctls[i].v.id == V4L2_CID_CHROMA_AGC && - core->tvnorm & V4L2_STD_SECAM) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - - return 0; -} -EXPORT_SYMBOL(cx8800_ctrl_query); - /* ------------------------------------------------------------------- */ /* resource management */ @@ -591,8 +520,9 @@ static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) { struct cx8800_fh *fh = q->priv_data; + struct cx8800_dev *dev = fh->dev; - *size = fh->fmt->depth*fh->width*fh->height >> 3; + *size = dev->fmt->depth * dev->width * dev->height >> 3; if (0 == *count) *count = 32; if (*size * *count > vid_limit * 1024 * 1024) @@ -611,21 +541,21 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); int rc, init_buffer = 0; - BUG_ON(NULL == fh->fmt); - if (fh->width < 48 || fh->width > norm_maxw(core->tvnorm) || - fh->height < 32 || fh->height > norm_maxh(core->tvnorm)) + BUG_ON(NULL == dev->fmt); + if (dev->width < 48 || dev->width > norm_maxw(core->tvnorm) || + dev->height < 32 || dev->height > norm_maxh(core->tvnorm)) return -EINVAL; - buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; + buf->vb.size = (dev->width * dev->height * dev->fmt->depth) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; - if (buf->fmt != fh->fmt || - buf->vb.width != fh->width || - buf->vb.height != fh->height || + if (buf->fmt != dev->fmt || + buf->vb.width != dev->width || + buf->vb.height != dev->height || buf->vb.field != field) { - buf->fmt = fh->fmt; - buf->vb.width = fh->width; - buf->vb.height = fh->height; + buf->fmt = dev->fmt; + buf->vb.width = dev->width; + buf->vb.height = dev->height; buf->vb.field = field; init_buffer = 1; } @@ -675,7 +605,7 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, } dprintk(2,"[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", buf, buf->vb.i, - fh->width, fh->height, fh->fmt->depth, fh->fmt->name, + dev->width, dev->height, dev->fmt->depth, dev->fmt->name, (unsigned long)buf->risc.dma); buf->vb.state = VIDEOBUF_PREPARED; @@ -755,12 +685,15 @@ static const struct videobuf_queue_ops cx8800_video_qops = { /* ------------------------------------------------------------------ */ -static struct videobuf_queue* get_queue(struct cx8800_fh *fh) +static struct videobuf_queue *get_queue(struct file *file) { - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: + struct video_device *vdev = video_devdata(file); + struct cx8800_fh *fh = file->private_data; + + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: return &fh->vidq; - case V4L2_BUF_TYPE_VBI_CAPTURE: + case VFL_TYPE_VBI: return &fh->vbiq; default: BUG(); @@ -768,12 +701,14 @@ static struct videobuf_queue* get_queue(struct cx8800_fh *fh) } } -static int get_ressource(struct cx8800_fh *fh) +static int get_resource(struct file *file) { - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: + struct video_device *vdev = video_devdata(file); + + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: return RESOURCE_VIDEO; - case V4L2_BUF_TYPE_VBI_CAPTURE: + case VFL_TYPE_VBI: return RESOURCE_VBI; default: BUG(); @@ -810,13 +745,9 @@ static int video_open(struct file *file) if (unlikely(!fh)) return -ENOMEM; + v4l2_fh_init(&fh->fh, vdev); file->private_data = fh; fh->dev = dev; - fh->radio = radio; - fh->type = type; - fh->width = 320; - fh->height = 240; - fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); mutex_lock(&core->lock); @@ -833,7 +764,7 @@ static int video_open(struct file *file) sizeof(struct cx88_buffer), fh, NULL); - if (fh->radio) { + if (vdev->vfl_type == VFL_TYPE_RADIO) { dprintk(1,"video_open: setting radio device\n"); cx_write(MO_GP3_IO, core->board.radio.gpio3); cx_write(MO_GP0_IO, core->board.radio.gpio0); @@ -859,6 +790,7 @@ static int video_open(struct file *file) core->users++; mutex_unlock(&core->lock); + v4l2_fh_add(&fh->fh); return 0; } @@ -866,15 +798,16 @@ static int video_open(struct file *file) static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { + struct video_device *vdev = video_devdata(file); struct cx8800_fh *fh = file->private_data; - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: if (res_locked(fh->dev,RESOURCE_VIDEO)) return -EBUSY; return videobuf_read_one(&fh->vidq, data, count, ppos, file->f_flags & O_NONBLOCK); - case V4L2_BUF_TYPE_VBI_CAPTURE: + case VFL_TYPE_VBI: if (!res_get(fh->dev,fh,RESOURCE_VBI)) return -EBUSY; return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1, @@ -888,16 +821,16 @@ video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) static unsigned int video_poll(struct file *file, struct poll_table_struct *wait) { + struct video_device *vdev = video_devdata(file); struct cx8800_fh *fh = file->private_data; struct cx88_buffer *buf; - unsigned int rc = POLLERR; + unsigned int rc = v4l2_ctrl_poll(file, wait); - if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { + if (vdev->vfl_type == VFL_TYPE_VBI) { if (!res_get(fh->dev,fh,RESOURCE_VBI)) - return POLLERR; - return videobuf_poll_stream(file, &fh->vbiq, wait); + return rc | POLLERR; + return rc | videobuf_poll_stream(file, &fh->vbiq, wait); } - mutex_lock(&fh->vidq.vb_lock); if (res_check(fh,RESOURCE_VIDEO)) { /* streaming capture */ @@ -913,9 +846,7 @@ video_poll(struct file *file, struct poll_table_struct *wait) poll_wait(file, &buf->vb.done, wait); if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) - rc = POLLIN|POLLRDNORM; - else - rc = 0; + rc |= POLLIN|POLLRDNORM; done: mutex_unlock(&fh->vidq.vb_lock); return rc; @@ -952,6 +883,8 @@ static int video_release(struct file *file) videobuf_mmap_free(&fh->vbiq); mutex_lock(&dev->core->lock); + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); file->private_data = NULL; kfree(fh); @@ -966,156 +899,104 @@ static int video_release(struct file *file) static int video_mmap(struct file *file, struct vm_area_struct * vma) { - struct cx8800_fh *fh = file->private_data; - - return videobuf_mmap_mapper(get_queue(fh), vma); + return videobuf_mmap_mapper(get_queue(file), vma); } /* ------------------------------------------------------------------ */ /* VIDEO CTRL IOCTLS */ -int cx88_get_control (struct cx88_core *core, struct v4l2_control *ctl) +static int cx8800_s_vid_ctrl(struct v4l2_ctrl *ctrl) { - const struct cx88_ctrl *c = NULL; - u32 value; - int i; + struct cx88_core *core = + container_of(ctrl->handler, struct cx88_core, video_hdl); + const struct cx88_ctrl *cc = ctrl->priv; + u32 value, mask; - for (i = 0; i < CX8800_CTLS; i++) - if (cx8800_ctls[i].v.id == ctl->id) - c = &cx8800_ctls[i]; - if (unlikely(NULL == c)) - return -EINVAL; + mask = cc->mask; + switch (ctrl->id) { + case V4L2_CID_SATURATION: + /* special v_sat handling */ - value = c->sreg ? cx_sread(c->sreg) : cx_read(c->reg); - switch (ctl->id) { - case V4L2_CID_AUDIO_BALANCE: - ctl->value = ((value & 0x7f) < 0x40) ? ((value & 0x7f) + 0x40) - : (0x7f - (value & 0x7f)); - break; - case V4L2_CID_AUDIO_VOLUME: - ctl->value = 0x3f - (value & 0x3f); + value = ((ctrl->val - cc->off) << cc->shift) & cc->mask; + + if (core->tvnorm & V4L2_STD_SECAM) { + /* For SECAM, both U and V sat should be equal */ + value = value << 8 | value; + } else { + /* Keeps U Saturation proportional to V Sat */ + value = (value * 0x5a) / 0x7f << 8 | value; + } + mask = 0xffff; break; case V4L2_CID_SHARPNESS: - ctl->value = ((value & 0x0200) ? (((value & 0x0180) >> 7) + 1) - : 0); + /* 0b000, 0b100, 0b101, 0b110, or 0b111 */ + value = (ctrl->val < 1 ? 0 : ((ctrl->val + 3) << 7)); + /* needs to be set for both fields */ + cx_andor(MO_FILTER_EVEN, mask, value); + break; + case V4L2_CID_CHROMA_AGC: + value = ((ctrl->val - cc->off) << cc->shift) & cc->mask; break; default: - ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift; + value = ((ctrl->val - cc->off) << cc->shift) & cc->mask; break; } - dprintk(1,"get_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n", - ctl->id, c->v.name, ctl->value, c->reg, - value,c->mask, c->sreg ? " [shadowed]" : ""); + dprintk(1, "set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n", + ctrl->id, ctrl->name, ctrl->val, cc->reg, value, + mask, cc->sreg ? " [shadowed]" : ""); + if (cc->sreg) + cx_sandor(cc->sreg, cc->reg, mask, value); + else + cx_andor(cc->reg, mask, value); return 0; } -EXPORT_SYMBOL(cx88_get_control); -int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl) +static int cx8800_s_aud_ctrl(struct v4l2_ctrl *ctrl) { - const struct cx88_ctrl *c = NULL; + struct cx88_core *core = + container_of(ctrl->handler, struct cx88_core, audio_hdl); + const struct cx88_ctrl *cc = ctrl->priv; u32 value,mask; - int i; - - for (i = 0; i < CX8800_CTLS; i++) { - if (cx8800_ctls[i].v.id == ctl->id) { - c = &cx8800_ctls[i]; - } - } - if (unlikely(NULL == c)) - return -EINVAL; - - if (ctl->value < c->v.minimum) - ctl->value = c->v.minimum; - if (ctl->value > c->v.maximum) - ctl->value = c->v.maximum; /* Pass changes onto any WM8775 */ if (core->board.audio_chip == V4L2_IDENT_WM8775) { - struct v4l2_control client_ctl; - memset(&client_ctl, 0, sizeof(client_ctl)); - client_ctl.id = ctl->id; - - switch (ctl->id) { + switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - client_ctl.value = ctl->value; + wm8775_s_ctrl(core, ctrl->id, ctrl->val); break; case V4L2_CID_AUDIO_VOLUME: - client_ctl.value = (ctl->value) ? - (0x90 + ctl->value) << 8 : 0; + wm8775_s_ctrl(core, ctrl->id, (ctrl->val) ? + (0x90 + ctrl->val) << 8 : 0); break; case V4L2_CID_AUDIO_BALANCE: - client_ctl.value = ctl->value << 9; + wm8775_s_ctrl(core, ctrl->id, ctrl->val << 9); break; default: - client_ctl.id = 0; break; } - if (client_ctl.id) - call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); } - mask=c->mask; - switch (ctl->id) { + mask = cc->mask; + switch (ctrl->id) { case V4L2_CID_AUDIO_BALANCE: - value = (ctl->value < 0x40) ? (0x7f - ctl->value) : (ctl->value - 0x40); + value = (ctrl->val < 0x40) ? (0x7f - ctrl->val) : (ctrl->val - 0x40); break; case V4L2_CID_AUDIO_VOLUME: - value = 0x3f - (ctl->value & 0x3f); - break; - case V4L2_CID_SATURATION: - /* special v_sat handling */ - - value = ((ctl->value - c->off) << c->shift) & c->mask; - - if (core->tvnorm & V4L2_STD_SECAM) { - /* For SECAM, both U and V sat should be equal */ - value=value<<8|value; - } else { - /* Keeps U Saturation proportional to V Sat */ - value=(value*0x5a)/0x7f<<8|value; - } - mask=0xffff; - break; - case V4L2_CID_SHARPNESS: - /* 0b000, 0b100, 0b101, 0b110, or 0b111 */ - value = (ctl->value < 1 ? 0 : ((ctl->value + 3) << 7)); - /* needs to be set for both fields */ - cx_andor(MO_FILTER_EVEN, mask, value); - break; - case V4L2_CID_CHROMA_AGC: - /* Do not allow chroma AGC to be enabled for SECAM */ - value = ((ctl->value - c->off) << c->shift) & c->mask; - if (core->tvnorm & V4L2_STD_SECAM && value) - return -EINVAL; + value = 0x3f - (ctrl->val & 0x3f); break; default: - value = ((ctl->value - c->off) << c->shift) & c->mask; + value = ((ctrl->val - cc->off) << cc->shift) & cc->mask; break; } dprintk(1,"set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n", - ctl->id, c->v.name, ctl->value, c->reg, value, - mask, c->sreg ? " [shadowed]" : ""); - if (c->sreg) { - cx_sandor(c->sreg, c->reg, mask, value); - } else { - cx_andor(c->reg, mask, value); - } + ctrl->id, ctrl->name, ctrl->val, cc->reg, value, + mask, cc->sreg ? " [shadowed]" : ""); + if (cc->sreg) + cx_sandor(cc->sreg, cc->reg, mask, value); + else + cx_andor(cc->reg, mask, value); return 0; } -EXPORT_SYMBOL(cx88_set_control); - -static void init_controls(struct cx88_core *core) -{ - struct v4l2_control ctrl; - int i; - - for (i = 0; i < CX8800_CTLS; i++) { - ctrl.id=cx8800_ctls[i].v.id; - ctrl.value=cx8800_ctls[i].v.default_value; - - cx88_set_control(core, &ctrl); - } -} /* ------------------------------------------------------------------ */ /* VIDEO IOCTLS */ @@ -1124,15 +1005,17 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cx8800_fh *fh = priv; + struct cx8800_dev *dev = fh->dev; - f->fmt.pix.width = fh->width; - f->fmt.pix.height = fh->height; + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; f->fmt.pix.field = fh->vidq.field; - f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.pixelformat = dev->fmt->fourcc; f->fmt.pix.bytesperline = - (f->fmt.pix.width * fh->fmt->depth) >> 3; + (f->fmt.pix.width * dev->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } @@ -1184,33 +1067,54 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cx8800_fh *fh = priv; + struct cx8800_dev *dev = fh->dev; int err = vidioc_try_fmt_vid_cap (file,priv,f); if (0 != err) return err; - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; + dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; fh->vidq.field = f->fmt.pix.field; return 0; } -static int vidioc_querycap (struct file *file, void *priv, +void cx88_querycap(struct file *file, struct cx88_core *core, + struct v4l2_capability *cap) +{ + struct video_device *vdev = video_devdata(file); + + strlcpy(cap->card, core->board.name, sizeof(cap->card)); + cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (UNSET != core->board.tuner_type) + cap->device_caps |= V4L2_CAP_TUNER; + switch (vdev->vfl_type) { + case VFL_TYPE_RADIO: + cap->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; + break; + case VFL_TYPE_GRABBER: + cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; + break; + case VFL_TYPE_VBI: + cap->device_caps |= V4L2_CAP_VBI_CAPTURE; + break; + } + cap->capabilities = cap->device_caps | V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VBI_CAPTURE | V4L2_CAP_DEVICE_CAPS; + if (core->board.radio.type == CX88_RADIO) + cap->capabilities |= V4L2_CAP_RADIO; +} +EXPORT_SYMBOL(cx88_querycap); + +static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev; struct cx88_core *core = dev->core; strcpy(cap->driver, "cx8800"); - strlcpy(cap->card, core->board.name, sizeof(cap->card)); - sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - V4L2_CAP_VBI_CAPTURE; - if (UNSET != core->board.tuner_type) - cap->capabilities |= V4L2_CAP_TUNER; + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); + cx88_querycap(file, core, cap); return 0; } @@ -1228,69 +1132,67 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p) { - struct cx8800_fh *fh = priv; - return (videobuf_reqbufs(get_queue(fh), p)); + return videobuf_reqbufs(get_queue(file), p); } static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p) { - struct cx8800_fh *fh = priv; - return (videobuf_querybuf(get_queue(fh), p)); + return videobuf_querybuf(get_queue(file), p); } static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p) { - struct cx8800_fh *fh = priv; - return (videobuf_qbuf(get_queue(fh), p)); + return videobuf_qbuf(get_queue(file), p); } static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p) { - struct cx8800_fh *fh = priv; - return (videobuf_dqbuf(get_queue(fh), p, - file->f_flags & O_NONBLOCK)); + return videobuf_dqbuf(get_queue(file), p, + file->f_flags & O_NONBLOCK); } static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { + struct video_device *vdev = video_devdata(file); struct cx8800_fh *fh = priv; struct cx8800_dev *dev = fh->dev; - /* We should remember that this driver also supports teletext, */ - /* so we have to test if the v4l2_buf_type is VBI capture data. */ - if (unlikely((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && - (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))) - return -EINVAL; - - if (unlikely(i != fh->type)) + if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) || + (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE)) return -EINVAL; - if (unlikely(!res_get(dev,fh,get_ressource(fh)))) + if (unlikely(!res_get(dev, fh, get_resource(file)))) return -EBUSY; - return videobuf_streamon(get_queue(fh)); + return videobuf_streamon(get_queue(file)); } static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { + struct video_device *vdev = video_devdata(file); struct cx8800_fh *fh = priv; struct cx8800_dev *dev = fh->dev; int err, res; - if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && - (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) + if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) || + (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE)) return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = get_ressource(fh); - err = videobuf_streamoff(get_queue(fh)); + res = get_resource(file); + err = videobuf_streamoff(get_queue(file)); if (err < 0) return err; res_free(dev,fh,res); return 0; } +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; + + *tvnorm = core->tvnorm; + return 0; +} + static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *tvnorms) { struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; @@ -1327,8 +1229,8 @@ int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i) if ((CX88_VMUX_TELEVISION == INPUT(n).type) || (CX88_VMUX_CABLE == INPUT(n).type)) { i->type = V4L2_INPUT_TYPE_TUNER; - i->std = CX88_NORMS; } + i->std = CX88_NORMS; return 0; } EXPORT_SYMBOL(cx88_enum_input); @@ -1354,6 +1256,8 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int i) if (i >= 4) return -EINVAL; + if (0 == INPUT(i).type) + return -EINVAL; mutex_lock(&core->lock); cx88_newstation(core); @@ -1362,35 +1266,6 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int i) return 0; } - - -static int vidioc_queryctrl (struct file *file, void *priv, - struct v4l2_queryctrl *qctrl) -{ - struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); - if (unlikely(qctrl->id == 0)) - return -EINVAL; - return cx8800_ctrl_query(core, qctrl); -} - -static int vidioc_g_ctrl (struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - return - cx88_get_control(core,ctl); -} - -static int vidioc_s_ctrl (struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - return - cx88_set_control(core,ctl); -} - static int vidioc_g_tuner (struct file *file, void *priv, struct v4l2_tuner *t) { @@ -1403,9 +1278,9 @@ static int vidioc_g_tuner (struct file *file, void *priv, return -EINVAL; strcpy(t->name, "Television"); - t->type = V4L2_TUNER_ANALOG_TV; t->capability = V4L2_TUNER_CAP_NORM; t->rangehigh = 0xffffffffUL; + call_all(core, tuner, g_tuner, t); cx88_get_stereo(core ,t); reg = cx_read(MO_DEVICE_STATUS); @@ -1435,9 +1310,9 @@ static int vidioc_g_frequency (struct file *file, void *priv, if (unlikely(UNSET == core->board.tuner_type)) return -EINVAL; + if (f->tuner) + return -EINVAL; - /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */ - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = core->freq; call_all(core, tuner, g_frequency, f); @@ -1454,9 +1329,10 @@ int cx88_set_freq (struct cx88_core *core, return -EINVAL; mutex_lock(&core->lock); - core->freq = f->frequency; cx88_newstation(core); call_all(core, tuner, s_frequency, f); + call_all(core, tuner, g_frequency, f); + core->freq = f->frequency; /* When changing channels it is required to reset TVAUDIO */ msleep (10); @@ -1474,13 +1350,17 @@ static int vidioc_s_frequency (struct file *file, void *priv, struct cx8800_fh *fh = priv; struct cx88_core *core = fh->dev->core; - if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) - return -EINVAL; - if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) - return -EINVAL; + return cx88_set_freq(core, f); +} - return - cx88_set_freq (core,f); +static int vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + if (!v4l2_chip_match_host(&chip->match)) + return -EINVAL; + chip->revision = 0; + chip->ident = V4L2_IDENT_UNKNOWN; + return 0; } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -1513,19 +1393,6 @@ static int vidioc_s_register (struct file *file, void *fh, /* RADIO ESPECIFIC IOCTLS */ /* ----------------------------------------------------------- */ -static int radio_querycap (struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev; - struct cx88_core *core = dev->core; - - strcpy(cap->driver, "cx8800"); - strlcpy(cap->card, core->board.name, sizeof(cap->card)); - sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci)); - cap->capabilities = V4L2_CAP_TUNER; - return 0; -} - static int radio_g_tuner (struct file *file, void *priv, struct v4l2_tuner *t) { @@ -1535,32 +1402,11 @@ static int radio_g_tuner (struct file *file, void *priv, return -EINVAL; strcpy(t->name, "Radio"); - t->type = V4L2_TUNER_RADIO; call_all(core, tuner, g_tuner, t); return 0; } -static int radio_enum_input (struct file *file, void *priv, - struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - strcpy(i->name,"Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - - return 0; -} - -static int radio_g_audio (struct file *file, void *priv, struct v4l2_audio *a) -{ - if (unlikely(a->index)) - return -EINVAL; - - strcpy(a->name,"Radio"); - return 0; -} - /* FIXME: Should add a standard for radio */ static int radio_s_tuner (struct file *file, void *priv, @@ -1570,46 +1416,14 @@ static int radio_s_tuner (struct file *file, void *priv, if (0 != t->index) return -EINVAL; + if (t->audmode > V4L2_TUNER_MODE_STEREO) + t->audmode = V4L2_TUNER_MODE_STEREO; call_all(core, tuner, s_tuner, t); return 0; } -static int radio_s_audio (struct file *file, void *fh, - struct v4l2_audio *a) -{ - return 0; -} - -static int radio_s_input (struct file *file, void *fh, unsigned int i) -{ - return 0; -} - -static int radio_queryctrl (struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - int i; - - if (c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) - return -EINVAL; - if (c->id == V4L2_CID_AUDIO_MUTE || - c->id == V4L2_CID_AUDIO_VOLUME || - c->id == V4L2_CID_AUDIO_BALANCE) { - for (i = 0; i < CX8800_CTLS; i++) { - if (cx8800_ctls[i].v.id == c->id) - break; - } - if (i == CX8800_CTLS) - return -EINVAL; - *c = cx8800_ctls[i].v; - } else - *c = no_ctl; - return 0; -} - /* ----------------------------------------------------------- */ static void cx8800_vid_timeout(unsigned long data) @@ -1752,63 +1566,89 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt, - .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt, - .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, .vidioc_s_std = vidioc_s_std, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_g_chip_ident = vidioc_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, #endif }; -static struct video_device cx8800_vbi_template; - static const struct video_device cx8800_video_template = { .name = "cx8800-video", .fops = &video_fops, .ioctl_ops = &video_ioctl_ops, .tvnorms = CX88_NORMS, - .current_norm = V4L2_STD_NTSC_M, +}; + +static const struct v4l2_ioctl_ops vbi_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt, + .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt, + .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_chip_ident = vidioc_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +static const struct video_device cx8800_vbi_template = { + .name = "cx8800-vbi", + .fops = &video_fops, + .ioctl_ops = &vbi_ioctl_ops, + .tvnorms = CX88_NORMS, }; static const struct v4l2_file_operations radio_fops = { .owner = THIS_MODULE, .open = video_open, + .poll = v4l2_ctrl_poll, .release = video_release, .unlocked_ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = radio_querycap, + .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = radio_g_tuner, - .vidioc_enum_input = radio_enum_input, - .vidioc_g_audio = radio_g_audio, .vidioc_s_tuner = radio_s_tuner, - .vidioc_s_audio = radio_s_audio, - .vidioc_s_input = radio_s_input, - .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_g_chip_ident = vidioc_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, @@ -1821,6 +1661,14 @@ static const struct video_device cx8800_radio_template = { .ioctl_ops = &radio_ioctl_ops, }; +static const struct v4l2_ctrl_ops cx8800_ctrl_vid_ops = { + .s_ctrl = cx8800_s_vid_ctrl, +}; + +static const struct v4l2_ctrl_ops cx8800_ctrl_aud_ops = { + .s_ctrl = cx8800_s_aud_ctrl, +}; + /* ----------------------------------------------------------- */ static void cx8800_unregister_video(struct cx8800_dev *dev) @@ -1853,8 +1701,8 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, { struct cx8800_dev *dev; struct cx88_core *core; - int err; + int i; dev = kzalloc(sizeof(*dev),GFP_KERNEL); if (NULL == dev) @@ -1888,14 +1736,9 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, goto fail_core; } - /* Initialize VBI template */ - memcpy( &cx8800_vbi_template, &cx8800_video_template, - sizeof(cx8800_vbi_template) ); - strcpy(cx8800_vbi_template.name,"cx8800-vbi"); - /* initialize driver struct */ spin_lock_init(&dev->slock); - core->tvnorm = cx8800_video_template.current_norm; + core->tvnorm = V4L2_STD_NTSC_M; /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); @@ -1925,6 +1768,35 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, } cx_set(MO_PCI_INTMSK, core->pci_irqmask); + for (i = 0; i < CX8800_AUD_CTLS; i++) { + const struct cx88_ctrl *cc = &cx8800_aud_ctls[i]; + struct v4l2_ctrl *vc; + + vc = v4l2_ctrl_new_std(&core->audio_hdl, &cx8800_ctrl_aud_ops, + cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value); + if (vc == NULL) { + err = core->audio_hdl.error; + goto fail_core; + } + vc->priv = (void *)cc; + } + + for (i = 0; i < CX8800_VID_CTLS; i++) { + const struct cx88_ctrl *cc = &cx8800_vid_ctls[i]; + struct v4l2_ctrl *vc; + + vc = v4l2_ctrl_new_std(&core->video_hdl, &cx8800_ctrl_vid_ops, + cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value); + if (vc == NULL) { + err = core->video_hdl.error; + goto fail_core; + } + vc->priv = (void *)cc; + if (vc->id == V4L2_CID_CHROMA_AGC) + core->chroma_agc = vc; + } + v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl); + /* load and configure helper modules */ if (core->board.audio_chip == V4L2_IDENT_WM8775) { @@ -1942,8 +1814,10 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap, &wm8775_info, NULL); - if (sd != NULL) + if (sd != NULL) { + core->sd_wm8775 = sd; sd->grp_id = WM8775_GID; + } } if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) { @@ -1971,16 +1845,22 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, /* Sets device info at pci_dev */ pci_set_drvdata(pci_dev, dev); + dev->width = 320; + dev->height = 240; + dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); + /* initial device configuration */ mutex_lock(&core->lock); cx88_set_tvnorm(core, core->tvnorm); - init_controls(core); + v4l2_ctrl_handler_setup(&core->video_hdl); + v4l2_ctrl_handler_setup(&core->audio_hdl); cx88_video_mux(core, 0); /* register v4l devices */ dev->video_dev = cx88_vdev_init(core,dev->pci, &cx8800_video_template,"video"); video_set_drvdata(dev->video_dev, dev); + dev->video_dev->ctrl_handler = &core->video_hdl; err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, video_nr[core->nr]); if (err < 0) { @@ -2007,6 +1887,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, dev->radio_dev = cx88_vdev_init(core,dev->pci, &cx8800_radio_template,"radio"); video_set_drvdata(dev->radio_dev, dev); + dev->radio_dev->ctrl_handler = &core->audio_hdl; err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO, radio_nr[core->nr]); if (err < 0) { diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index c9659def2a78..0cae0fd9e164 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -26,6 +26,7 @@ #include <linux/kdev_t.h> #include <media/v4l2-device.h> +#include <media/v4l2-fh.h> #include <media/tuner.h> #include <media/tveeprom.h> #include <media/videobuf-dma-sg.h> @@ -115,15 +116,6 @@ struct cx8800_fmt { u32 cxformat; }; -struct cx88_ctrl { - struct v4l2_queryctrl v; - u32 off; - u32 reg; - u32 sreg; - u32 mask; - u32 shift; -}; - /* ----------------------------------------------------------- */ /* SRAM memory management data (see cx88-core.c) */ @@ -359,6 +351,10 @@ struct cx88_core { /* config info -- analog */ struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler video_hdl; + struct v4l2_ctrl *chroma_agc; + struct v4l2_ctrl_handler audio_hdl; + struct v4l2_subdev *sd_wm8775; struct i2c_client *i2c_rtc; unsigned int boardnr; struct cx88_board board; @@ -409,8 +405,6 @@ static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev) return container_of(v4l2_dev, struct cx88_core, v4l2_dev); } -#define WM8775_GID (1 << 0) - #define call_hw(core, grpid, o, f, args...) \ do { \ if (!core->i2c_rc) { \ @@ -424,6 +418,36 @@ static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev) #define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args) +#define WM8775_GID (1 << 0) + +#define wm8775_s_ctrl(core, id, val) \ + do { \ + struct v4l2_ctrl *ctrl_ = \ + v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id); \ + if (ctrl_ && !core->i2c_rc) { \ + if (core->gate_ctrl) \ + core->gate_ctrl(core, 1); \ + v4l2_ctrl_s_ctrl(ctrl_, val); \ + if (core->gate_ctrl) \ + core->gate_ctrl(core, 0); \ + } \ + } while (0) + +#define wm8775_g_ctrl(core, id) \ + ({ \ + struct v4l2_ctrl *ctrl_ = \ + v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id); \ + s32 val = 0; \ + if (ctrl_ && !core->i2c_rc) { \ + if (core->gate_ctrl) \ + core->gate_ctrl(core, 1); \ + val = v4l2_ctrl_g_ctrl(ctrl_); \ + if (core->gate_ctrl) \ + core->gate_ctrl(core, 0); \ + } \ + val; \ + }) + struct cx8800_dev; struct cx8802_dev; @@ -431,19 +455,11 @@ struct cx8802_dev; /* function 0: video stuff */ struct cx8800_fh { + struct v4l2_fh fh; struct cx8800_dev *dev; - enum v4l2_buf_type type; - int radio; unsigned int resources; - /* video overlay */ - struct v4l2_window win; - struct v4l2_clip *clips; - unsigned int nclips; - /* video capture */ - const struct cx8800_fmt *fmt; - unsigned int width,height; struct videobuf_queue vidq; /* vbi capture */ @@ -468,6 +484,8 @@ struct cx8800_dev { struct pci_dev *pci; unsigned char pci_rev,pci_lat; + const struct cx8800_fmt *fmt; + unsigned int width, height; /* capture queues */ struct cx88_dmaqueue vidq; @@ -488,6 +506,7 @@ struct cx8800_dev { /* function 2: mpeg stuff */ struct cx8802_fh { + struct v4l2_fh fh; struct cx8802_dev *dev; struct videobuf_queue mpegq; }; @@ -552,7 +571,7 @@ struct cx8802_dev { unsigned char mpeg_active; /* nonzero if mpeg encoder is active */ /* mpeg params */ - struct cx2341x_mpeg_params params; + struct cx2341x_handler cxhdl; #endif #if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) @@ -722,11 +741,8 @@ void cx8802_cancel_buffers(struct cx8802_dev *dev); /* ----------------------------------------------------------- */ /* cx88-video.c*/ -extern const u32 cx88_user_ctrls[]; -extern int cx8800_ctrl_query(struct cx88_core *core, - struct v4l2_queryctrl *qctrl); int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i); int cx88_set_freq (struct cx88_core *core,struct v4l2_frequency *f); -int cx88_get_control(struct cx88_core *core, struct v4l2_control *ctl); -int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl); int cx88_video_mux(struct cx88_core *core, unsigned int input); +void cx88_querycap(struct file *file, struct cx88_core *core, + struct v4l2_capability *cap); diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index d7e2a3dc5525..07dc594e79f0 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -42,6 +42,7 @@ #include <sound/initval.h> #include <sound/control.h> #include <sound/tlv.h> +#include <sound/ac97_codec.h> #include <media/v4l2-common.h> #include "em28xx.h" @@ -679,19 +680,19 @@ static int em28xx_audio_init(struct em28xx *dev) INIT_WORK(&dev->wq_trigger, audio_trigger); if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { - em28xx_cvol_new(card, dev, "Video", AC97_VIDEO_VOL); - em28xx_cvol_new(card, dev, "Line In", AC97_LINEIN_VOL); - em28xx_cvol_new(card, dev, "Phone", AC97_PHONE_VOL); - em28xx_cvol_new(card, dev, "Microphone", AC97_PHONE_VOL); - em28xx_cvol_new(card, dev, "CD", AC97_CD_VOL); - em28xx_cvol_new(card, dev, "AUX", AC97_AUX_VOL); - em28xx_cvol_new(card, dev, "PCM", AC97_PCM_OUT_VOL); - - em28xx_cvol_new(card, dev, "Master", AC97_MASTER_VOL); - em28xx_cvol_new(card, dev, "Line", AC97_LINE_LEVEL_VOL); - em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO_VOL); - em28xx_cvol_new(card, dev, "LFE", AC97_LFE_MASTER_VOL); - em28xx_cvol_new(card, dev, "Surround", AC97_SURR_MASTER_VOL); + em28xx_cvol_new(card, dev, "Video", AC97_VIDEO); + em28xx_cvol_new(card, dev, "Line In", AC97_LINE); + em28xx_cvol_new(card, dev, "Phone", AC97_PHONE); + em28xx_cvol_new(card, dev, "Microphone", AC97_MIC); + em28xx_cvol_new(card, dev, "CD", AC97_CD); + em28xx_cvol_new(card, dev, "AUX", AC97_AUX); + em28xx_cvol_new(card, dev, "PCM", AC97_PCM); + + em28xx_cvol_new(card, dev, "Master", AC97_MASTER); + em28xx_cvol_new(card, dev, "Line", AC97_HEADPHONE); + em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO); + em28xx_cvol_new(card, dev, "LFE", AC97_CENTER_LFE_MASTER); + em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER); } err = snd_card_register(card); diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 862c6575c557..ca62b9981380 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -975,12 +975,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Terratec Cinergy HTC Stick", .has_dvb = 1, .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, -#if 0 - .tuner_type = TUNER_PHILIPS_TDA8290, - .tuner_addr = 0x41, - .dvb_gpio = terratec_h5_digital, /* FIXME: probably wrong */ - .tuner_gpio = terratec_h5_gpio, -#endif + .tuner_type = TUNER_ABSENT, .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 5717bdee8f1b..de2cb20ad2cc 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -27,6 +27,7 @@ #include <linux/slab.h> #include <linux/usb.h> #include <linux/vmalloc.h> +#include <sound/ac97_codec.h> #include <media/v4l2-common.h> #include "em28xx.h" @@ -326,13 +327,13 @@ struct em28xx_vol_itable { }; static struct em28xx_vol_itable inputs[] = { - { EM28XX_AMUX_VIDEO, AC97_VIDEO_VOL }, - { EM28XX_AMUX_LINE_IN, AC97_LINEIN_VOL }, - { EM28XX_AMUX_PHONE, AC97_PHONE_VOL }, - { EM28XX_AMUX_MIC, AC97_MIC_VOL }, - { EM28XX_AMUX_CD, AC97_CD_VOL }, - { EM28XX_AMUX_AUX, AC97_AUX_VOL }, - { EM28XX_AMUX_PCM_OUT, AC97_PCM_OUT_VOL }, + { EM28XX_AMUX_VIDEO, AC97_VIDEO }, + { EM28XX_AMUX_LINE_IN, AC97_LINE }, + { EM28XX_AMUX_PHONE, AC97_PHONE }, + { EM28XX_AMUX_MIC, AC97_MIC }, + { EM28XX_AMUX_CD, AC97_CD }, + { EM28XX_AMUX_AUX, AC97_AUX }, + { EM28XX_AMUX_PCM_OUT, AC97_PCM }, }; static int set_ac97_input(struct em28xx *dev) @@ -415,11 +416,11 @@ struct em28xx_vol_otable { }; static const struct em28xx_vol_otable outputs[] = { - { EM28XX_AOUT_MASTER, AC97_MASTER_VOL }, - { EM28XX_AOUT_LINE, AC97_LINE_LEVEL_VOL }, - { EM28XX_AOUT_MONO, AC97_MASTER_MONO_VOL }, - { EM28XX_AOUT_LFE, AC97_LFE_MASTER_VOL }, - { EM28XX_AOUT_SURR, AC97_SURR_MASTER_VOL }, + { EM28XX_AOUT_MASTER, AC97_MASTER }, + { EM28XX_AOUT_LINE, AC97_HEADPHONE }, + { EM28XX_AOUT_MONO, AC97_MASTER_MONO }, + { EM28XX_AOUT_LFE, AC97_CENTER_LFE_MASTER }, + { EM28XX_AOUT_SURR, AC97_SURROUND_MASTER }, }; int em28xx_audio_analog_set(struct em28xx *dev) @@ -459,9 +460,9 @@ int em28xx_audio_analog_set(struct em28xx *dev) if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { int vol; - em28xx_write_ac97(dev, AC97_POWER_DOWN_CTRL, 0x4200); - em28xx_write_ac97(dev, AC97_EXT_AUD_CTRL, 0x0031); - em28xx_write_ac97(dev, AC97_PCM_IN_SRATE, 0xbb80); + em28xx_write_ac97(dev, AC97_POWERDOWN, 0x4200); + em28xx_write_ac97(dev, AC97_EXTENDED_STATUS, 0x0031); + em28xx_write_ac97(dev, AC97_PCM_LR_ADC_RATE, 0xbb80); /* LSB: left channel - both channels with the same level */ vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8); @@ -487,7 +488,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) channels */ sel |= (sel << 8); - em28xx_write_ac97(dev, AC97_RECORD_SELECT, sel); + em28xx_write_ac97(dev, AC97_REC_SEL, sel); } } diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 16410ac20092..a16531fa937a 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -310,31 +310,47 @@ static struct drxd_config em28xx_drxd = { .disable_i2c_gate_ctrl = 1, }; -struct drxk_config terratec_h5_drxk = { +static struct drxk_config terratec_h5_drxk = { .adr = 0x29, .single_master = 1, .no_i2c_bridge = 1, .microcode_name = "dvb-usb-terratec-h5-drxk.fw", + .qam_demod_parameter_count = 2, }; -struct drxk_config hauppauge_930c_drxk = { +static struct drxk_config hauppauge_930c_drxk = { .adr = 0x29, .single_master = 1, .no_i2c_bridge = 1, .microcode_name = "dvb-usb-hauppauge-hvr930c-drxk.fw", .chunk_size = 56, + .qam_demod_parameter_count = 2, }; -struct drxk_config maxmedia_ub425_tc_drxk = { +struct drxk_config terratec_htc_stick_drxk = { .adr = 0x29, .single_master = 1, .no_i2c_bridge = 1, + .microcode_name = "dvb-usb-terratec-htc-stick-drxk.fw", + .chunk_size = 54, + .qam_demod_parameter_count = 2, + /* Required for the antenna_gpio to disable LNA. */ + .antenna_dvbt = true, + /* The windows driver uses the same. This will disable LNA. */ + .antenna_gpio = 0x6, }; -struct drxk_config pctv_520e_drxk = { +static struct drxk_config maxmedia_ub425_tc_drxk = { + .adr = 0x29, + .single_master = 1, + .no_i2c_bridge = 1, +}; + +static struct drxk_config pctv_520e_drxk = { .adr = 0x29, .single_master = 1, .microcode_name = "dvb-demod-drxk-pctv.fw", + .qam_demod_parameter_count = 2, .chunk_size = 58, .antenna_dvbt = true, /* disable LNA */ .antenna_gpio = (1 << 2), /* disable LNA */ @@ -473,6 +489,57 @@ static void terratec_h5_init(struct em28xx *dev) em28xx_gpio_set(dev, terratec_h5_end); }; +static void terratec_htc_stick_init(struct em28xx *dev) +{ + int i; + + /* + * GPIO configuration: + * 0xff: unknown (does not affect DVB-T). + * 0xf6: DRX-K (demodulator). + * 0xe6: unknown (does not affect DVB-T). + * 0xb6: unknown (does not affect DVB-T). + */ + struct em28xx_reg_seq terratec_htc_stick_init[] = { + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {EM2874_R80_GPIO, 0xf6, 0xff, 100}, + {EM2874_R80_GPIO, 0xe6, 0xff, 50}, + {EM2874_R80_GPIO, 0xf6, 0xff, 100}, + { -1, -1, -1, -1}, + }; + struct em28xx_reg_seq terratec_htc_stick_end[] = { + {EM2874_R80_GPIO, 0xb6, 0xff, 100}, + {EM2874_R80_GPIO, 0xf6, 0xff, 50}, + { -1, -1, -1, -1}, + }; + + /* Init the analog decoder? */ + struct { + unsigned char r[4]; + int len; + } regs[] = { + {{ 0x06, 0x02, 0x00, 0x31 }, 4}, + {{ 0x01, 0x02 }, 2}, + {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, + {{ 0x01, 0x00 }, 2}, + {{ 0x01, 0x00, 0xff, 0xaf }, 4}, + }; + + em28xx_gpio_set(dev, terratec_htc_stick_init); + + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); + msleep(10); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); + msleep(10); + + dev->i2c_client.addr = 0x82 >> 1; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); + + em28xx_gpio_set(dev, terratec_htc_stick_end); +}; + static void pctv_520e_init(struct em28xx *dev) { /* @@ -944,7 +1011,6 @@ static int em28xx_dvb_init(struct em28xx *dev) break; } case EM2884_BOARD_TERRATEC_H5: - case EM2884_BOARD_CINERGY_HTC_STICK: terratec_h5_init(dev); dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap); @@ -1021,6 +1087,25 @@ static int em28xx_dvb_init(struct em28xx *dev) } } break; + case EM2884_BOARD_CINERGY_HTC_STICK: + terratec_htc_stick_init(dev); + + /* attach demodulator */ + dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk, + &dev->i2c_adap); + if (!dvb->fe[0]) { + result = -EINVAL; + goto out_free; + } + + /* Attach the demodulator. */ + if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, + &dev->i2c_adap, + &em28xx_cxd2820r_tda18271_config)) { + result = -EINVAL; + goto out_free; + } + break; default: em28xx_errdev("/2: The frontend of your DVB/ATSC card" " isn't supported yet\n"); diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index 185db65b766e..1683bd9d51ee 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -475,6 +475,7 @@ static struct i2c_client em28xx_client_template = { */ static char *i2c_devs[128] = { [0x4a >> 1] = "saa7113h", + [0x52 >> 1] = "drxk", [0x60 >> 1] = "remote IR sensor", [0x8e >> 1] = "remote IR sensor", [0x86 >> 1] = "tda9887", diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 5e30c4f3f248..97d36b4f19db 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -345,7 +345,7 @@ static void em28xx_ir_stop(struct rc_dev *rc) cancel_delayed_work_sync(&ir->work); } -int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type) +static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type) { int rc = 0; struct em28xx_IR *ir = rc_dev->priv; diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index 2f6268505726..6ff368297f6e 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -211,58 +211,9 @@ enum em28xx_chip_id { }; /* - * Registers used by em202 and other AC97 chips + * Registers used by em202 */ -/* Standard AC97 registers */ -#define AC97_RESET 0x00 - - /* Output volumes */ -#define AC97_MASTER_VOL 0x02 -#define AC97_LINE_LEVEL_VOL 0x04 /* Some devices use for headphones */ -#define AC97_MASTER_MONO_VOL 0x06 - - /* Input volumes */ -#define AC97_PC_BEEP_VOL 0x0a -#define AC97_PHONE_VOL 0x0c -#define AC97_MIC_VOL 0x0e -#define AC97_LINEIN_VOL 0x10 -#define AC97_CD_VOL 0x12 -#define AC97_VIDEO_VOL 0x14 -#define AC97_AUX_VOL 0x16 -#define AC97_PCM_OUT_VOL 0x18 - - /* capture registers */ -#define AC97_RECORD_SELECT 0x1a -#define AC97_RECORD_GAIN 0x1c - - /* control registers */ -#define AC97_GENERAL_PURPOSE 0x20 -#define AC97_3D_CTRL 0x22 -#define AC97_AUD_INT_AND_PAG 0x24 -#define AC97_POWER_DOWN_CTRL 0x26 -#define AC97_EXT_AUD_ID 0x28 -#define AC97_EXT_AUD_CTRL 0x2a - -/* Supported rate varies for each AC97 device - if write an unsupported value, it will return the closest one - */ -#define AC97_PCM_OUT_FRONT_SRATE 0x2c -#define AC97_PCM_OUT_SURR_SRATE 0x2e -#define AC97_PCM_OUT_LFE_SRATE 0x30 -#define AC97_PCM_IN_SRATE 0x32 - - /* For devices with more than 2 channels, extra output volumes */ -#define AC97_LFE_MASTER_VOL 0x36 -#define AC97_SURR_MASTER_VOL 0x38 - - /* Digital SPDIF output control */ -#define AC97_SPDIF_OUT_CTRL 0x3a - - /* Vendor ID identifier */ -#define AC97_VENDOR_ID1 0x7c -#define AC97_VENDOR_ID2 0x7e - /* EMP202 vendor registers */ #define EM202_EXT_MODEM_CTRL 0x3e #define EM202_GPIO_CONF 0x4c diff --git a/drivers/media/video/ibmmpeg2.h b/drivers/media/video/ibmmpeg2.h deleted file mode 100644 index 68e10387c498..000000000000 --- a/drivers/media/video/ibmmpeg2.h +++ /dev/null @@ -1,94 +0,0 @@ -/* ibmmpeg2.h - IBM MPEGCD21 definitions */ - -#ifndef __IBM_MPEG2__ -#define __IBM_MPEG2__ - -/* Define all MPEG Decoder registers */ -/* Chip Control and Status */ -#define IBM_MP2_CHIP_CONTROL 0x200*2 -#define IBM_MP2_CHIP_MODE 0x201*2 -/* Timer Control and Status */ -#define IBM_MP2_SYNC_STC2 0x202*2 -#define IBM_MP2_SYNC_STC1 0x203*2 -#define IBM_MP2_SYNC_STC0 0x204*2 -#define IBM_MP2_SYNC_PTS2 0x205*2 -#define IBM_MP2_SYNC_PTS1 0x206*2 -#define IBM_MP2_SYNC_PTS0 0x207*2 -/* Video FIFO Control */ -#define IBM_MP2_FIFO 0x208*2 -#define IBM_MP2_FIFOW 0x100*2 -#define IBM_MP2_FIFO_STAT 0x209*2 -#define IBM_MP2_RB_THRESHOLD 0x22b*2 -/* Command buffer */ -#define IBM_MP2_COMMAND 0x20a*2 -#define IBM_MP2_CMD_DATA 0x20b*2 -#define IBM_MP2_CMD_STAT 0x20c*2 -#define IBM_MP2_CMD_ADDR 0x20d*2 -/* Internal Processor Control and Status */ -#define IBM_MP2_PROC_IADDR 0x20e*2 -#define IBM_MP2_PROC_IDATA 0x20f*2 -#define IBM_MP2_WR_PROT 0x235*2 -/* DRAM Access */ -#define IBM_MP2_DRAM_ADDR 0x210*2 -#define IBM_MP2_DRAM_DATA 0x212*2 -#define IBM_MP2_DRAM_CMD_STAT 0x213*2 -#define IBM_MP2_BLOCK_SIZE 0x23b*2 -#define IBM_MP2_SRC_ADDR 0x23c*2 -/* Onscreen Display */ -#define IBM_MP2_OSD_ADDR 0x214*2 -#define IBM_MP2_OSD_DATA 0x215*2 -#define IBM_MP2_OSD_MODE 0x217*2 -#define IBM_MP2_OSD_LINK_ADDR 0x229*2 -#define IBM_MP2_OSD_SIZE 0x22a*2 -/* Interrupt Control */ -#define IBM_MP2_HOST_INT 0x218*2 -#define IBM_MP2_MASK0 0x219*2 -#define IBM_MP2_HOST_INT1 0x23e*2 -#define IBM_MP2_MASK1 0x23f*2 -/* Audio Control */ -#define IBM_MP2_AUD_IADDR 0x21a*2 -#define IBM_MP2_AUD_IDATA 0x21b*2 -#define IBM_MP2_AUD_FIFO 0x21c*2 -#define IBM_MP2_AUD_FIFOW 0x101*2 -#define IBM_MP2_AUD_CTL 0x21d*2 -#define IBM_MP2_BEEP_CTL 0x21e*2 -#define IBM_MP2_FRNT_ATTEN 0x22d*2 -/* Display Control */ -#define IBM_MP2_DISP_MODE 0x220*2 -#define IBM_MP2_DISP_DLY 0x221*2 -#define IBM_MP2_VBI_CTL 0x222*2 -#define IBM_MP2_DISP_LBOR 0x223*2 -#define IBM_MP2_DISP_TBOR 0x224*2 -/* Polarity Control */ -#define IBM_MP2_INFC_CTL 0x22c*2 - -/* control commands */ -#define IBM_MP2_PLAY 0 -#define IBM_MP2_PAUSE 1 -#define IBM_MP2_SINGLE_FRAME 2 -#define IBM_MP2_FAST_FORWARD 3 -#define IBM_MP2_SLOW_MOTION 4 -#define IBM_MP2_IMED_NORM_PLAY 5 -#define IBM_MP2_RESET_WINDOW 6 -#define IBM_MP2_FREEZE_FRAME 7 -#define IBM_MP2_RESET_VID_RATE 8 -#define IBM_MP2_CONFIG_DECODER 9 -#define IBM_MP2_CHANNEL_SWITCH 10 -#define IBM_MP2_RESET_AUD_RATE 11 -#define IBM_MP2_PRE_OP_CHN_SW 12 -#define IBM_MP2_SET_STILL_MODE 14 - -/* Define Xilinx FPGA Internal Registers */ - -/* general control register 0 */ -#define XILINX_CTL0 0x600 -/* genlock delay resister 1 */ -#define XILINX_GLDELAY 0x602 -/* send 16 bits to CS3310 port */ -#define XILINX_CS3310 0x604 -/* send 16 bits to CS3310 and complete */ -#define XILINX_CS3310_CMPLT 0x60c -/* pulse width modulator control */ -#define XILINX_PWM 0x606 - -#endif diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index f7d57b3f2842..32a591062d0b 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -1830,18 +1830,6 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio, return 0; } -long ivtv_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct video_device *vfd = video_devdata(filp); - long ret; - - if (ivtv_debug & IVTV_DBGFLG_IOCTL) - vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; - ret = video_ioctl2(filp, cmd, arg); - vfd->debug = 0; - return ret; -} - static const struct v4l2_ioctl_ops ivtv_ioctl_ops = { .vidioc_querycap = ivtv_querycap, .vidioc_s_audio = ivtv_s_audio, diff --git a/drivers/media/video/ivtv/ivtv-ioctl.h b/drivers/media/video/ivtv/ivtv-ioctl.h index 89185caeafae..7c553d16579b 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.h +++ b/drivers/media/video/ivtv/ivtv-ioctl.h @@ -31,6 +31,5 @@ void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std); void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std); int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); int ivtv_s_input(struct file *file, void *fh, unsigned int inp); -long ivtv_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); #endif diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 6738592aa35d..87990c5f0910 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -50,7 +50,7 @@ static const struct v4l2_file_operations ivtv_v4l2_enc_fops = { .read = ivtv_v4l2_read, .write = ivtv_v4l2_write, .open = ivtv_v4l2_open, - .unlocked_ioctl = ivtv_v4l2_ioctl, + .unlocked_ioctl = video_ioctl2, .release = ivtv_v4l2_close, .poll = ivtv_v4l2_enc_poll, }; @@ -60,7 +60,7 @@ static const struct v4l2_file_operations ivtv_v4l2_dec_fops = { .read = ivtv_v4l2_read, .write = ivtv_v4l2_write, .open = ivtv_v4l2_open, - .unlocked_ioctl = ivtv_v4l2_ioctl, + .unlocked_ioctl = video_ioctl2, .release = ivtv_v4l2_close, .poll = ivtv_v4l2_dec_poll, }; diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig index 302dc3d70193..dc8c2505907e 100644 --- a/drivers/media/video/m5mols/Kconfig +++ b/drivers/media/video/m5mols/Kconfig @@ -1,5 +1,6 @@ config VIDEO_M5MOLS tristate "Fujitsu M-5MOLS 8MP sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT ---help--- This driver supports Fujitsu M-5MOLS camera sensor with ISP diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c index 3945556f5733..f08cf38a496d 100644 --- a/drivers/media/video/mem2mem_testdev.c +++ b/drivers/media/video/mem2mem_testdev.c @@ -60,6 +60,10 @@ MODULE_VERSION("0.1.1"); #define MEM2MEM_COLOR_STEP (0xff >> 4) #define MEM2MEM_NUM_TILES 8 +/* Flags that indicate processing mode */ +#define MEM2MEM_HFLIP (1 << 0) +#define MEM2MEM_VFLIP (1 << 1) + #define dprintk(dev, fmt, arg...) \ v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) @@ -115,6 +119,24 @@ enum { static struct v4l2_queryctrl m2mtest_ctrls[] = { { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { .id = V4L2_CID_TRANS_TIME_MSEC, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Transaction time (msec)", @@ -181,6 +203,9 @@ struct m2mtest_ctx { /* Abort requested by m2m */ int aborting; + /* Processing mode */ + int mode; + struct v4l2_m2m_ctx *m2m_ctx; /* Source and destination queue data */ @@ -249,19 +274,84 @@ static int device_process(struct m2mtest_ctx *ctx, bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES; w = 0; - for (y = 0; y < height; ++y) { - for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { - if (w & 0x1) { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ + MEM2MEM_COLOR_STEP; - } else { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ - MEM2MEM_COLOR_STEP; + switch (ctx->mode) { + case MEM2MEM_HFLIP | MEM2MEM_VFLIP: + p_out += bytesperline * height - bytes_left; + for (y = 0; y < height; ++y) { + for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { + if (w & 0x1) { + for (x = 0; x < tile_w; ++x) + *--p_out = *p_in++ + + MEM2MEM_COLOR_STEP; + } else { + for (x = 0; x < tile_w; ++x) + *--p_out = *p_in++ - + MEM2MEM_COLOR_STEP; + } + ++w; } - ++w; + p_in += bytes_left; + p_out -= bytes_left; + } + break; + + case MEM2MEM_HFLIP: + for (y = 0; y < height; ++y) { + p_out += MEM2MEM_NUM_TILES * tile_w; + for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { + if (w & 0x01) { + for (x = 0; x < tile_w; ++x) + *--p_out = *p_in++ + + MEM2MEM_COLOR_STEP; + } else { + for (x = 0; x < tile_w; ++x) + *--p_out = *p_in++ - + MEM2MEM_COLOR_STEP; + } + ++w; + } + p_in += bytes_left; + p_out += bytesperline; + } + break; + + case MEM2MEM_VFLIP: + p_out += bytesperline * (height - 1); + for (y = 0; y < height; ++y) { + for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { + if (w & 0x1) { + for (x = 0; x < tile_w; ++x) + *p_out++ = *p_in++ + + MEM2MEM_COLOR_STEP; + } else { + for (x = 0; x < tile_w; ++x) + *p_out++ = *p_in++ - + MEM2MEM_COLOR_STEP; + } + ++w; + } + p_in += bytes_left; + p_out += bytes_left - 2 * bytesperline; + } + break; + + default: + for (y = 0; y < height; ++y) { + for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { + if (w & 0x1) { + for (x = 0; x < tile_w; ++x) + *p_out++ = *p_in++ + + MEM2MEM_COLOR_STEP; + } else { + for (x = 0; x < tile_w; ++x) + *p_out++ = *p_in++ - + MEM2MEM_COLOR_STEP; + } + ++w; + } + p_in += bytes_left; + p_out += bytes_left; } - p_in += bytes_left; - p_out += bytes_left; } return 0; @@ -648,6 +738,14 @@ static int vidioc_g_ctrl(struct file *file, void *priv, struct m2mtest_ctx *ctx = priv; switch (ctrl->id) { + case V4L2_CID_HFLIP: + ctrl->value = (ctx->mode & MEM2MEM_HFLIP) ? 1 : 0; + break; + + case V4L2_CID_VFLIP: + ctrl->value = (ctx->mode & MEM2MEM_VFLIP) ? 1 : 0; + break; + case V4L2_CID_TRANS_TIME_MSEC: ctrl->value = ctx->transtime; break; @@ -691,6 +789,20 @@ static int vidioc_s_ctrl(struct file *file, void *priv, return ret; switch (ctrl->id) { + case V4L2_CID_HFLIP: + if (ctrl->value) + ctx->mode |= MEM2MEM_HFLIP; + else + ctx->mode &= ~MEM2MEM_HFLIP; + break; + + case V4L2_CID_VFLIP: + if (ctrl->value) + ctx->mode |= MEM2MEM_VFLIP; + else + ctx->mode &= ~MEM2MEM_VFLIP; + break; + case V4L2_CID_TRANS_TIME_MSEC: ctx->transtime = ctrl->value; break; @@ -861,6 +973,7 @@ static int m2mtest_open(struct file *file) ctx->translen = MEM2MEM_DEF_TRANSLEN; ctx->transtime = MEM2MEM_DEF_TRANSTIME; ctx->num_processed = 0; + ctx->mode = 0; ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0]; ctx->q_data[V4L2_M2M_DST].fmt = &formats[0]; diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 7e648183f157..00583f5fd26b 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -22,7 +22,7 @@ /* * mt9m001 i2c address 0x5d - * The platform has to define ctruct i2c_board_info objects and link to them + * The platform has to define struct i2c_board_info objects and link to them * from struct soc_camera_link */ diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c index 3c1e626139b7..445359c96113 100644 --- a/drivers/media/video/mt9m032.c +++ b/drivers/media/video/mt9m032.c @@ -688,11 +688,17 @@ static const struct v4l2_subdev_ops mt9m032_ops = { static int mt9m032_probe(struct i2c_client *client, const struct i2c_device_id *devid) { + struct mt9m032_platform_data *pdata = client->dev.platform_data; struct i2c_adapter *adapter = client->adapter; struct mt9m032 *sensor; int chip_version; int ret; + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { dev_warn(&client->dev, "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); @@ -708,7 +714,7 @@ static int mt9m032_probe(struct i2c_client *client, mutex_init(&sensor->lock); - sensor->pdata = client->dev.platform_data; + sensor->pdata = pdata; v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops); sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -738,7 +744,7 @@ static int mt9m032_probe(struct i2c_client *client, sensor->format.field = V4L2_FIELD_NONE; sensor->format.colorspace = V4L2_COLORSPACE_SRGB; - v4l2_ctrl_handler_init(&sensor->ctrls, 4); + v4l2_ctrl_handler_init(&sensor->ctrls, 5); v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, V4L2_CID_GAIN, 0, 127, 1, 64); @@ -754,6 +760,9 @@ static int mt9m032_probe(struct i2c_client *client, V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN, MT9M032_SHUTTER_WIDTH_MAX, 1, MT9M032_SHUTTER_WIDTH_DEF); + v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, + V4L2_CID_PIXEL_RATE, pdata->pix_clock, + pdata->pix_clock, 1, pdata->pix_clock); if (sensor->ctrls.error) { ret = sensor->ctrls.error; diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index b0c529964329..863d722dda06 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -214,7 +214,6 @@ struct mt9m111 { int power_count; const struct mt9m111_datafmt *fmt; int lastpage; /* PageMap cache value */ - unsigned char datawidth; }; /* Find a data format by a pixel code */ diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c index 8f061d9ac443..3be537ef22d2 100644 --- a/drivers/media/video/mt9p031.c +++ b/drivers/media/video/mt9p031.c @@ -950,7 +950,7 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->model = did->driver_data; mt9p031->reset = -1; - v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 4); + v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 5); v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN, @@ -963,6 +963,9 @@ static int mt9p031_probe(struct i2c_client *client, V4L2_CID_HFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_PIXEL_RATE, pdata->target_freq, + pdata->target_freq, 1, pdata->target_freq); for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i) v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL); diff --git a/drivers/media/video/mt9t001.c b/drivers/media/video/mt9t001.c index 49ca3cbfc6f1..6d343adf891d 100644 --- a/drivers/media/video/mt9t001.c +++ b/drivers/media/video/mt9t001.c @@ -691,7 +691,7 @@ static int mt9t001_video_probe(struct i2c_client *client) return ret; /* Configure the pixel clock polarity */ - if (pdata && pdata->clk_pol) { + if (pdata->clk_pol) { ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK, MT9T001_PIXEL_CLOCK_INVERT); if (ret < 0) @@ -715,10 +715,16 @@ static int mt9t001_video_probe(struct i2c_client *client) static int mt9t001_probe(struct i2c_client *client, const struct i2c_device_id *did) { + struct mt9t001_platform_data *pdata = client->dev.platform_data; struct mt9t001 *mt9t001; unsigned int i; int ret; + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { dev_warn(&client->adapter->dev, @@ -735,7 +741,7 @@ static int mt9t001_probe(struct i2c_client *client, return -ENOMEM; v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) + - ARRAY_SIZE(mt9t001_gains) + 2); + ARRAY_SIZE(mt9t001_gains) + 3); v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN, @@ -743,6 +749,9 @@ static int mt9t001_probe(struct i2c_client *client, MT9T001_SHUTTER_WIDTH_DEF); v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1); + v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, + V4L2_CID_PIXEL_RATE, pdata->ext_clk, pdata->ext_clk, + 1, pdata->ext_clk); for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i) v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL); diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index bf63417adb8f..72479247522a 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -23,7 +23,7 @@ /* * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c - * The platform has to define ctruct i2c_board_info objects and link to them + * The platform has to define struct i2c_board_info objects and link to them * from struct soc_camera_link */ diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index 7e32331b60fb..f1220d3d4970 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -2014,7 +2014,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return -EINVAL; switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: sel->r.left = 0; sel->r.top = 0; sel->r.width = INT_MAX; @@ -2024,7 +2024,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ccdc_try_crop(ccdc, format, &sel->r); break; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SEL_TGT_CROP: sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); break; @@ -2052,7 +2052,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != CCDC_PAD_SOURCE_OF) return -EINVAL; @@ -2064,7 +2064,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, * pad. If the KEEP_CONFIG flag is set, just return the current crop * rectangle. */ - if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) { + if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); return 0; } diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index dd91da26f1b0..53f5a703e31a 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -1949,7 +1949,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, return -EINVAL; switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: sel->r.left = 0; sel->r.top = 0; sel->r.width = INT_MAX; @@ -1960,7 +1960,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, preview_try_crop(prev, format, &sel->r); break; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SEL_TGT_CROP: sel->r = *__preview_get_crop(prev, fh, sel->which); break; @@ -1988,7 +1988,7 @@ static int preview_set_selection(struct v4l2_subdev *sd, struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != PREV_PAD_SINK) return -EINVAL; @@ -2000,7 +2000,7 @@ static int preview_set_selection(struct v4l2_subdev *sd, * pad. If the KEEP_CONFIG flag is set, just return the current crop * rectangle. */ - if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) { + if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { sel->r = *__preview_get_crop(prev, fh, sel->which); return 0; } diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c index 14041c9c8643..ae17d917f77b 100644 --- a/drivers/media/video/omap3isp/ispresizer.c +++ b/drivers/media/video/omap3isp/ispresizer.c @@ -1249,7 +1249,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, sel->which); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: sel->r.left = 0; sel->r.top = 0; sel->r.width = INT_MAX; @@ -1259,7 +1259,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, resizer_calc_ratios(res, &sel->r, format_source, &ratio); break; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SEL_TGT_CROP: sel->r = *__resizer_get_crop(res, fh, sel->which); resizer_calc_ratios(res, &sel->r, format_source, &ratio); break; @@ -1293,7 +1293,7 @@ static int resizer_set_selection(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format_sink, *format_source; struct resizer_ratio ratio; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != RESZ_PAD_SINK) return -EINVAL; diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig index f9b6001e1dd7..25e412ecad2c 100644 --- a/drivers/media/video/pvrusb2/Kconfig +++ b/drivers/media/video/pvrusb2/Kconfig @@ -1,7 +1,6 @@ config VIDEO_PVRUSB2 tristate "Hauppauge WinTV-PVR USB2 support" depends on VIDEO_V4L2 && I2C - depends on VIDEO_MEDIA # Avoids pvrusb = Y / DVB = M select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_CX2341X diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 7bddfaeeafc3..f344aed32a93 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -226,13 +226,11 @@ static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi) struct v4l2_input tmp; unsigned int cnt; int val; - int ret; cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); memset(&tmp, 0, sizeof(tmp)); tmp.index = vi->index; - ret = 0; if (vi->index >= fh->input_cnt) return -EINVAL; val = fh->input_map[vi->index]; @@ -556,9 +554,7 @@ static int pvr2_queryctrl(struct file *file, void *priv, struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; struct pvr2_ctrl *cptr; int val; - int ret; - ret = 0; if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) { cptr = pvr2_hdw_get_ctrl_nextv4l( hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL)); @@ -705,11 +701,9 @@ static int pvr2_try_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_control *ctrl; struct pvr2_ctrl *pctl; unsigned int idx; - int ret; /* For the moment just validate that the requested control actually exists. */ - ret = 0; for (idx = 0; idx < ctls->count; idx++) { ctrl = ctls->controls + idx; pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id); @@ -770,12 +764,10 @@ static int pvr2_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - struct v4l2_cropcap cap; int ret; if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), crop->c.left); @@ -965,7 +957,7 @@ static long pvr2_v4l2_ioctl(struct file *file, long ret = -EINVAL; if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) - v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); + v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); if (!pvr2_hdw_dev_ok(hdw)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, @@ -998,7 +990,7 @@ static long pvr2_v4l2_ioctl(struct file *file, pvr2_trace(PVR2_TRACE_V4LIOCTL, "pvr2_v4l2_do_ioctl failure, ret=%ld" " command was:", ret); - v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), + v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); } } diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index ec4e2ef54e65..de7c7ba99ef4 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -136,19 +136,13 @@ static int leds[2] = { 100, 0 }; /***/ -static int pwc_video_close(struct file *file); -static ssize_t pwc_video_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos); -static unsigned int pwc_video_poll(struct file *file, poll_table *wait); -static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma); - static const struct v4l2_file_operations pwc_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, - .release = pwc_video_close, - .read = pwc_video_read, - .poll = pwc_video_poll, - .mmap = pwc_video_mmap, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, .unlocked_ioctl = video_ioctl2, }; static struct video_device pwc_template = { @@ -562,17 +556,6 @@ static const char *pwc_sensor_type_to_string(unsigned int sensor_type) /***************************************************************************/ /* Video4Linux functions */ -int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file) -{ - if (pdev->capt_file != NULL && - pdev->capt_file != file) - return -EBUSY; - - pdev->capt_file = file; - - return 0; -} - static void pwc_video_release(struct v4l2_device *v) { struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); @@ -583,113 +566,6 @@ static void pwc_video_release(struct v4l2_device *v) kfree(pdev); } -static int pwc_video_close(struct file *file) -{ - struct pwc_device *pdev = video_drvdata(file); - - /* - * If we're still streaming vb2_queue_release will call stream_stop - * so we must take both the v4l2_lock and the vb_queue_lock. - */ - if (mutex_lock_interruptible(&pdev->v4l2_lock)) - return -ERESTARTSYS; - if (mutex_lock_interruptible(&pdev->vb_queue_lock)) { - mutex_unlock(&pdev->v4l2_lock); - return -ERESTARTSYS; - } - - if (pdev->capt_file == file) { - vb2_queue_release(&pdev->vb_queue); - pdev->capt_file = NULL; - } - - mutex_unlock(&pdev->vb_queue_lock); - mutex_unlock(&pdev->v4l2_lock); - - return v4l2_fh_release(file); -} - -static ssize_t pwc_video_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct pwc_device *pdev = video_drvdata(file); - int lock_v4l2 = 0; - ssize_t ret; - - if (mutex_lock_interruptible(&pdev->vb_queue_lock)) - return -ERESTARTSYS; - - ret = pwc_test_n_set_capt_file(pdev, file); - if (ret) - goto out; - - /* stream_start will get called so we must take the v4l2_lock */ - if (pdev->vb_queue.fileio == NULL) - lock_v4l2 = 1; - - /* Use try_lock, since we're taking the locks in the *wrong* order! */ - if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock)) { - ret = -ERESTARTSYS; - goto out; - } - ret = vb2_read(&pdev->vb_queue, buf, count, ppos, - file->f_flags & O_NONBLOCK); - if (lock_v4l2) - mutex_unlock(&pdev->v4l2_lock); -out: - mutex_unlock(&pdev->vb_queue_lock); - return ret; -} - -static unsigned int pwc_video_poll(struct file *file, poll_table *wait) -{ - struct pwc_device *pdev = video_drvdata(file); - struct vb2_queue *q = &pdev->vb_queue; - unsigned long req_events = poll_requested_events(wait); - unsigned int ret = POLL_ERR; - int lock_v4l2 = 0; - - if (mutex_lock_interruptible(&pdev->vb_queue_lock)) - return POLL_ERR; - - /* Will this start fileio and thus call start_stream? */ - if ((req_events & (POLLIN | POLLRDNORM)) && - q->num_buffers == 0 && !q->streaming && q->fileio == NULL) { - if (pwc_test_n_set_capt_file(pdev, file)) - goto out; - lock_v4l2 = 1; - } - - /* Use try_lock, since we're taking the locks in the *wrong* order! */ - if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock)) - goto out; - ret = vb2_poll(&pdev->vb_queue, file, wait); - if (lock_v4l2) - mutex_unlock(&pdev->v4l2_lock); - -out: - if (!pdev->udev) - ret |= POLLHUP; - mutex_unlock(&pdev->vb_queue_lock); - return ret; -} - -static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct pwc_device *pdev = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&pdev->vb_queue_lock)) - return -ERESTARTSYS; - - ret = pwc_test_n_set_capt_file(pdev, file); - if (ret == 0) - ret = vb2_mmap(&pdev->vb_queue, vma); - - mutex_unlock(&pdev->vb_queue_lock); - return ret; -} - /***************************************************************************/ /* Videobuf2 operations */ @@ -782,6 +658,8 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) if (!pdev->udev) return -ENODEV; + if (mutex_lock_interruptible(&pdev->v4l2_lock)) + return -ERESTARTSYS; /* Turn on camera and set LEDS on */ pwc_camera_power(pdev, 1); pwc_set_leds(pdev, leds[0], leds[1]); @@ -794,6 +672,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) /* And cleanup any queued bufs!! */ pwc_cleanup_queued_bufs(pdev); } + mutex_unlock(&pdev->v4l2_lock); return r; } @@ -802,6 +681,8 @@ static int stop_streaming(struct vb2_queue *vq) { struct pwc_device *pdev = vb2_get_drv_priv(vq); + if (mutex_lock_interruptible(&pdev->v4l2_lock)) + return -ERESTARTSYS; if (pdev->udev) { pwc_set_leds(pdev, 0, 0); pwc_camera_power(pdev, 0); @@ -809,22 +690,11 @@ static int stop_streaming(struct vb2_queue *vq) } pwc_cleanup_queued_bufs(pdev); + mutex_unlock(&pdev->v4l2_lock); return 0; } -static void wait_prepare(struct vb2_queue *vq) -{ - struct pwc_device *pdev = vb2_get_drv_priv(vq); - mutex_unlock(&pdev->vb_queue_lock); -} - -static void wait_finish(struct vb2_queue *vq) -{ - struct pwc_device *pdev = vb2_get_drv_priv(vq); - mutex_lock(&pdev->vb_queue_lock); -} - static struct vb2_ops pwc_vb_queue_ops = { .queue_setup = queue_setup, .buf_init = buffer_init, @@ -834,8 +704,8 @@ static struct vb2_ops pwc_vb_queue_ops = { .buf_queue = buffer_queue, .start_streaming = start_streaming, .stop_streaming = stop_streaming, - .wait_prepare = wait_prepare, - .wait_finish = wait_finish, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; /***************************************************************************/ @@ -1136,6 +1006,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id /* Init video_device structure */ memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template)); strcpy(pdev->vdev.name, name); + pdev->vdev.queue = &pdev->vb_queue; + pdev->vdev.queue->lock = &pdev->vb_queue_lock; set_bit(V4L2_FL_USE_FH_PRIO, &pdev->vdev.flags); video_set_drvdata(&pdev->vdev, pdev); @@ -1190,15 +1062,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vdev.v4l2_dev = &pdev->v4l2_dev; pdev->vdev.lock = &pdev->v4l2_lock; - /* - * Don't take v4l2_lock for these ioctls. This improves latency if - * v4l2_lock is taken for a long time, e.g. when changing a control - * value, and a new frame is ready to be dequeued. - */ - v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_DQBUF); - v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QBUF); - v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QUERYBUF); - rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1); if (rc < 0) { PWC_ERROR("Failed to register as video device (%d).\n", rc); @@ -1253,20 +1116,18 @@ static void usb_pwc_disconnect(struct usb_interface *intf) struct v4l2_device *v = usb_get_intfdata(intf); struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); - mutex_lock(&pdev->v4l2_lock); - mutex_lock(&pdev->vb_queue_lock); + mutex_lock(&pdev->v4l2_lock); /* No need to keep the urbs around after disconnection */ if (pdev->vb_queue.streaming) pwc_isoc_cleanup(pdev); pdev->udev = NULL; pwc_cleanup_queued_bufs(pdev); - mutex_unlock(&pdev->vb_queue_lock); v4l2_device_disconnect(&pdev->v4l2_dev); video_unregister_device(&pdev->vdev); - mutex_unlock(&pdev->v4l2_lock); + mutex_unlock(pdev->vb_queue.lock); #ifdef CONFIG_USB_PWC_INPUT_EVDEV if (pdev->button_dev) diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index c691e29cc36e..545e9bbdeede 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -405,6 +405,7 @@ static void pwc_vidioc_fill_fmt(struct v4l2_format *f, f->fmt.pix.pixelformat = pixfmt; f->fmt.pix.bytesperline = f->fmt.pix.width; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.width * 3 / 2; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() " "width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n", f->fmt.pix.width, @@ -468,17 +469,8 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) if (ret < 0) return ret; - if (mutex_lock_interruptible(&pdev->vb_queue_lock)) - return -ERESTARTSYS; - - ret = pwc_test_n_set_capt_file(pdev, file); - if (ret) - goto leave; - - if (pdev->vb_queue.streaming) { - ret = -EBUSY; - goto leave; - } + if (vb2_is_busy(&pdev->vb_queue)) + return -EBUSY; pixelformat = f->fmt.pix.pixelformat; @@ -496,8 +488,6 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret); pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); -leave: - mutex_unlock(&pdev->vb_queue_lock); return ret; } @@ -508,10 +498,9 @@ static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap strcpy(cap->driver, PWC_NAME); strlcpy(cap->card, pdev->vdev.name, sizeof(cap->card)); usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -520,7 +509,8 @@ static int pwc_enum_input(struct file *file, void *fh, struct v4l2_input *i) if (i->index) /* Only one INPUT is supported */ return -EINVAL; - strcpy(i->name, "usb"); + strlcpy(i->name, "Camera", sizeof(i->name)); + i->type = V4L2_INPUT_TYPE_CAMERA; return 0; } @@ -933,104 +923,6 @@ static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format * return pwc_vidioc_try_fmt(pdev, f); } -static int pwc_reqbufs(struct file *file, void *fh, - struct v4l2_requestbuffers *rb) -{ - struct pwc_device *pdev = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&pdev->vb_queue_lock)) - return -ERESTARTSYS; - - ret = pwc_test_n_set_capt_file(pdev, file); - if (ret == 0) - ret = vb2_reqbufs(&pdev->vb_queue, rb); - - mutex_unlock(&pdev->vb_queue_lock); - return ret; -} - -static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) -{ - struct pwc_device *pdev = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&pdev->vb_queue_lock)) - return -ERESTARTSYS; - - ret = pwc_test_n_set_capt_file(pdev, file); - if (ret == 0) - ret = vb2_querybuf(&pdev->vb_queue, buf); - - mutex_unlock(&pdev->vb_queue_lock); - return ret; -} - -static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) -{ - struct pwc_device *pdev = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&pdev->vb_queue_lock)) - return -ERESTARTSYS; - - ret = pwc_test_n_set_capt_file(pdev, file); - if (ret == 0) - ret = vb2_qbuf(&pdev->vb_queue, buf); - - mutex_unlock(&pdev->vb_queue_lock); - return ret; -} - -static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) -{ - struct pwc_device *pdev = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&pdev->vb_queue_lock)) - return -ERESTARTSYS; - - ret = pwc_test_n_set_capt_file(pdev, file); - if (ret == 0) - ret = vb2_dqbuf(&pdev->vb_queue, buf, - file->f_flags & O_NONBLOCK); - - mutex_unlock(&pdev->vb_queue_lock); - return ret; -} - -static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) -{ - struct pwc_device *pdev = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&pdev->vb_queue_lock)) - return -ERESTARTSYS; - - ret = pwc_test_n_set_capt_file(pdev, file); - if (ret == 0) - ret = vb2_streamon(&pdev->vb_queue, i); - - mutex_unlock(&pdev->vb_queue_lock); - return ret; -} - -static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) -{ - struct pwc_device *pdev = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&pdev->vb_queue_lock)) - return -ERESTARTSYS; - - ret = pwc_test_n_set_capt_file(pdev, file); - if (ret == 0) - ret = vb2_streamoff(&pdev->vb_queue, i); - - mutex_unlock(&pdev->vb_queue_lock); - return ret; -} - static int pwc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { @@ -1112,32 +1004,27 @@ static int pwc_s_parm(struct file *file, void *fh, int compression = 0; int ret, fps; - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - parm->parm.capture.timeperframe.numerator == 0) + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - fps = parm->parm.capture.timeperframe.denominator / - parm->parm.capture.timeperframe.numerator; - - if (mutex_lock_interruptible(&pdev->vb_queue_lock)) - return -ERESTARTSYS; + /* If timeperframe == 0, then reset the framerate to the nominal value. + We pick a high framerate here, and let pwc_set_video_mode() figure + out the best match. */ + if (parm->parm.capture.timeperframe.numerator == 0 || + parm->parm.capture.timeperframe.denominator == 0) + fps = 30; + else + fps = parm->parm.capture.timeperframe.denominator / + parm->parm.capture.timeperframe.numerator; - ret = pwc_test_n_set_capt_file(pdev, file); - if (ret) - goto leave; - - if (pdev->vb_queue.streaming) { - ret = -EBUSY; - goto leave; - } + if (vb2_is_busy(&pdev->vb_queue)) + return -EBUSY; ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt, fps, &compression, 0); pwc_g_parm(file, fh, parm); -leave: - mutex_unlock(&pdev->vb_queue_lock); return ret; } @@ -1150,12 +1037,12 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = { .vidioc_g_fmt_vid_cap = pwc_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = pwc_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = pwc_try_fmt_vid_cap, - .vidioc_reqbufs = pwc_reqbufs, - .vidioc_querybuf = pwc_querybuf, - .vidioc_qbuf = pwc_qbuf, - .vidioc_dqbuf = pwc_dqbuf, - .vidioc_streamon = pwc_streamon, - .vidioc_streamoff = pwc_streamoff, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_enum_framesizes = pwc_enum_framesizes, .vidioc_enum_frameintervals = pwc_enum_frameintervals, diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index d6b5b216b9d6..7a6a0d39c2c6 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -239,7 +239,6 @@ struct pwc_device int features; /* feature bits */ /*** Video data ***/ - struct file *capt_file; /* file doing video capture */ int vendpoint; /* video isoc endpoint */ int vcinterface; /* video control interface */ int valternate; /* alternate interface needed */ @@ -355,8 +354,6 @@ struct pwc_device extern int pwc_trace; #endif -int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file); - /** Functions in pwc-misc.c */ /* sizes in pixels */ extern const int pwc_image_sizes[PSZ_MAX][2]; diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 725812aa0c30..6a34183564d2 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -658,7 +658,7 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, r->left = r->top = 0; return; } - if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) { + if (target == V4L2_SEL_TGT_COMPOSE) { if (ctx->rotation != 90 && ctx->rotation != 270) align_h = 1; max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3)); @@ -685,7 +685,7 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, rotate ? sink->f_height : sink->f_width); max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height); - if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) { + if (target == V4L2_SEL_TGT_COMPOSE) { min_w = min_t(u32, max_w, sink->f_width / max_sc_h); min_h = min_t(u32, max_h, sink->f_height / max_sc_v); if (rotate) { @@ -1146,9 +1146,9 @@ static int fimc_cap_g_selection(struct file *file, void *fh, s->r.height = f->o_height; return 0; - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: f = &ctx->d_frame; - case V4L2_SEL_TGT_CROP_ACTIVE: + case V4L2_SEL_TGT_CROP: s->r.left = f->offs_h; s->r.top = f->offs_v; s->r.width = f->width; @@ -1160,7 +1160,7 @@ static int fimc_cap_g_selection(struct file *file, void *fh, } /* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ -int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) +static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) { if (a->left < b->left || a->top < b->top) return 0; @@ -1184,9 +1184,9 @@ static int fimc_cap_s_selection(struct file *file, void *fh, if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; - if (s->target == V4L2_SEL_TGT_COMPOSE_ACTIVE) + if (s->target == V4L2_SEL_TGT_COMPOSE) f = &ctx->d_frame; - else if (s->target == V4L2_SEL_TGT_CROP_ACTIVE) + else if (s->target == V4L2_SEL_TGT_CROP) f = &ctx->s_frame; else return -EINVAL; @@ -1428,9 +1428,9 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, mutex_lock(&fimc->lock); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: f = &ctx->d_frame; - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: r->width = f->o_width; r->height = f->o_height; r->left = 0; @@ -1438,10 +1438,10 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, mutex_unlock(&fimc->lock); return 0; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SEL_TGT_CROP: try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SEL_TGT_COMPOSE: try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); f = &ctx->d_frame; break; @@ -1482,12 +1482,12 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, return -EINVAL; mutex_lock(&fimc->lock); - fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP_ACTIVE); + fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: f = &ctx->d_frame; - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: r->width = f->o_width; r->height = f->o_height; r->left = 0; @@ -1495,10 +1495,10 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, mutex_unlock(&fimc->lock); return 0; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SEL_TGT_CROP: try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SEL_TGT_COMPOSE: try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); f = &ctx->d_frame; break; @@ -1514,7 +1514,7 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, set_frame_crop(f, r->left, r->top, r->width, r->height); set_bit(ST_CAPT_APPLY_CFG, &fimc->state); spin_unlock_irqrestore(&fimc->slock, flags); - if (sel->target == V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL) + if (sel->target == V4L2_SEL_TGT_COMPOSE) ctx->state |= FIMC_COMPOSE; } diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index a4646ca1d56f..1a445404e73d 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -463,7 +463,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v); } -int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx) +static int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx) { struct fimc_effect *effect = &ctx->effect; diff --git a/drivers/media/video/s5p-fimc/fimc-lite-reg.c b/drivers/media/video/s5p-fimc/fimc-lite-reg.c index 419adfb7cdf9..f996e94873f6 100644 --- a/drivers/media/video/s5p-fimc/fimc-lite-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-lite-reg.c @@ -215,7 +215,7 @@ void flite_hw_set_camera_bus(struct fimc_lite *dev, flite_hw_set_camera_port(dev, s_info->mux_id); } -void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f) +static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f) { static const u32 pixcode[4][2] = { { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CIODMAFMT_YCBYCR }, diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c index 74ff310db30c..c5b57e805b68 100644 --- a/drivers/media/video/s5p-fimc/fimc-lite.c +++ b/drivers/media/video/s5p-fimc/fimc-lite.c @@ -902,7 +902,7 @@ static int fimc_lite_g_selection(struct file *file, void *fh, sel->r.height = f->f_height; return 0; - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: sel->r = f->rect; return 0; } @@ -919,7 +919,7 @@ static int fimc_lite_s_selection(struct file *file, void *fh, unsigned long flags; if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || - sel->target != V4L2_SEL_TGT_COMPOSE_ACTIVE) + sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; fimc_lite_try_compose(fimc, &rect); @@ -1117,9 +1117,9 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, struct fimc_lite *fimc = v4l2_get_subdevdata(sd); struct flite_frame *f = &fimc->inp_frame; - if ((sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL && - sel->target != V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS) || - sel->pad != FLITE_SD_PAD_SINK) + if ((sel->target != V4L2_SEL_TGT_CROP && + sel->target != V4L2_SEL_TGT_CROP_BOUNDS) || + sel->pad != FLITE_SD_PAD_SINK) return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { @@ -1128,7 +1128,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, } mutex_lock(&fimc->lock); - if (sel->target == V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL) { + if (sel->target == V4L2_SEL_TGT_CROP) { sel->r = f->rect; } else { sel->r.left = 0; @@ -1153,8 +1153,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, struct flite_frame *f = &fimc->inp_frame; int ret = 0; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || - sel->pad != FLITE_SD_PAD_SINK) + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != FLITE_SD_PAD_SINK) return -EINVAL; mutex_lock(&fimc->lock); diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 52cef4865423..e65bb283fd8a 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -180,7 +180,7 @@ EXPORT_SYMBOL_GPL(fimc_pipeline_initialize); * sensor clock. * Called with the graph mutex held. */ -int __fimc_pipeline_shutdown(struct fimc_pipeline *p) +static int __fimc_pipeline_shutdown(struct fimc_pipeline *p) { int ret = 0; @@ -1010,7 +1010,7 @@ static struct platform_driver fimc_md_driver = { } }; -int __init fimc_md_init(void) +static int __init fimc_md_init(void) { int ret; @@ -1021,7 +1021,8 @@ int __init fimc_md_init(void) return platform_driver_register(&fimc_md_driver); } -void __exit fimc_md_exit(void) + +static void __exit fimc_md_exit(void) { platform_driver_unregister(&fimc_md_driver); fimc_unregister_driver(); diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c index 28b5225d94f5..95f23024b17d 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -824,10 +824,10 @@ static int s5p_jpeg_g_selection(struct file *file, void *priv, /* For JPEG blob active == default == bounds */ switch (s->target) { - case V4L2_SEL_TGT_CROP_ACTIVE: + case V4L2_SEL_TGT_CROP: case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_DEFAULT: s->r.width = ctx->out_q.w; s->r.height = ctx->out_q.h; @@ -1503,29 +1503,7 @@ static struct platform_driver s5p_jpeg_driver = { }, }; -static int __init -s5p_jpeg_register(void) -{ - int ret; - - pr_info("S5P JPEG V4L2 Driver, (c) 2011 Samsung Electronics\n"); - - ret = platform_driver_register(&s5p_jpeg_driver); - - if (ret) - pr_err("%s: failed to register jpeg driver\n", __func__); - - return ret; -} - -static void __exit -s5p_jpeg_unregister(void) -{ - platform_driver_unregister(&s5p_jpeg_driver); -} - -module_init(s5p_jpeg_register); -module_exit(s5p_jpeg_unregister); +module_platform_driver(s5p_jpeg_driver); MODULE_AUTHOR("Andrzej Pietrasiewicz <andrzej.p@samsung.com>"); MODULE_DESCRIPTION("Samsung JPEG codec driver"); diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index 33fde2a763ec..6c74b05d1f95 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -367,7 +367,7 @@ static int mxr_g_selection(struct file *file, void *fh, return -EINVAL; switch (s->target) { - case V4L2_SEL_TGT_CROP_ACTIVE: + case V4L2_SEL_TGT_CROP: s->r.left = geo->src.x_offset; s->r.top = geo->src.y_offset; s->r.width = geo->src.width; @@ -380,7 +380,7 @@ static int mxr_g_selection(struct file *file, void *fh, s->r.width = geo->src.full_width; s->r.height = geo->src.full_height; break; - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_PADDED: s->r.left = geo->dst.x_offset; s->r.top = geo->dst.y_offset; @@ -449,11 +449,11 @@ static int mxr_s_selection(struct file *file, void *fh, res.height = geo->dst.full_height; break; - case V4L2_SEL_TGT_CROP_ACTIVE: + case V4L2_SEL_TGT_CROP: target = &geo->src; stage = MXR_GEOMETRY_CROP; break; - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_PADDED: target = &geo->dst; stage = MXR_GEOMETRY_COMPOSE; diff --git a/drivers/media/video/s5p-tv/sii9234_drv.c b/drivers/media/video/s5p-tv/sii9234_drv.c index 0f31eccd7b80..6d348f90237a 100644 --- a/drivers/media/video/s5p-tv/sii9234_drv.c +++ b/drivers/media/video/s5p-tv/sii9234_drv.c @@ -419,14 +419,4 @@ static struct i2c_driver sii9234_driver = { .id_table = sii9234_id, }; -static int __init sii9234_init(void) -{ - return i2c_add_driver(&sii9234_driver); -} -module_init(sii9234_init); - -static void __exit sii9234_exit(void) -{ - i2c_del_driver(&sii9234_driver); -} -module_exit(sii9234_exit); +module_i2c_driver(sii9234_driver); diff --git a/drivers/media/video/saa7121.h b/drivers/media/video/saa7121.h deleted file mode 100644 index 66967ae37494..000000000000 --- a/drivers/media/video/saa7121.h +++ /dev/null @@ -1,132 +0,0 @@ -/* saa7121.h - saa7121 initializations - Copyright (C) 1999 Nathan Laredo (laredo@gnu.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. - - 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 __SAA7121_H__ -#define __SAA7121_H__ - -#define NTSC_BURST_START 0x19 /* 28 */ -#define NTSC_BURST_END 0x1d /* 29 */ -#define NTSC_CHROMA_PHASE 0x67 /* 5a */ -#define NTSC_GAINU 0x76 /* 5b */ -#define NTSC_GAINV 0xa5 /* 5c */ -#define NTSC_BLACK_LEVEL 0x2a /* 5d */ -#define NTSC_BLANKING_LEVEL 0x2e /* 5e */ -#define NTSC_VBI_BLANKING 0x2e /* 5f */ -#define NTSC_DAC_CONTROL 0x11 /* 61 */ -#define NTSC_BURST_AMP 0x3f /* 62 */ -#define NTSC_SUBC3 0x1f /* 63 */ -#define NTSC_SUBC2 0x7c /* 64 */ -#define NTSC_SUBC1 0xf0 /* 65 */ -#define NTSC_SUBC0 0x21 /* 66 */ -#define NTSC_HTRIG 0x72 /* 6c */ -#define NTSC_VTRIG 0x00 /* 6c */ -#define NTSC_MULTI 0x30 /* 6e */ -#define NTSC_CCTTX 0x11 /* 6f */ -#define NTSC_FIRST_ACTIVE 0x12 /* 7a */ -#define NTSC_LAST_ACTIVE 0x02 /* 7b */ -#define NTSC_MSB_VERTICAL 0x40 /* 7c */ - -#define PAL_BURST_START 0x21 /* 28 */ -#define PAL_BURST_END 0x1d /* 29 */ -#define PAL_CHROMA_PHASE 0x3f /* 5a */ -#define PAL_GAINU 0x7d /* 5b */ -#define PAL_GAINV 0xaf /* 5c */ -#define PAL_BLACK_LEVEL 0x23 /* 5d */ -#define PAL_BLANKING_LEVEL 0x35 /* 5e */ -#define PAL_VBI_BLANKING 0x35 /* 5f */ -#define PAL_DAC_CONTROL 0x02 /* 61 */ -#define PAL_BURST_AMP 0x2f /* 62 */ -#define PAL_SUBC3 0xcb /* 63 */ -#define PAL_SUBC2 0x8a /* 64 */ -#define PAL_SUBC1 0x09 /* 65 */ -#define PAL_SUBC0 0x2a /* 66 */ -#define PAL_HTRIG 0x86 /* 6c */ -#define PAL_VTRIG 0x04 /* 6d */ -#define PAL_MULTI 0x20 /* 6e */ -#define PAL_CCTTX 0x15 /* 6f */ -#define PAL_FIRST_ACTIVE 0x16 /* 7a */ -#define PAL_LAST_ACTIVE 0x36 /* 7b */ -#define PAL_MSB_VERTICAL 0x40 /* 7c */ - -/* Initialization Sequence */ - -static __u8 init7121ntsc[] = { - 0x26, 0x0, 0x27, 0x0, - 0x28, NTSC_BURST_START, 0x29, NTSC_BURST_END, - 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, 0x2d, 0x0, - 0x2e, 0x0, 0x2f, 0x0, 0x30, 0x0, 0x31, 0x0, - 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0, - 0x36, 0x0, 0x37, 0x0, 0x38, 0x0, 0x39, 0x0, - 0x3a, 0x03, 0x3b, 0x0, 0x3c, 0x0, 0x3d, 0x0, - 0x3e, 0x0, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0, - 0x42, 0x0, 0x43, 0x0, 0x44, 0x0, 0x45, 0x0, - 0x46, 0x0, 0x47, 0x0, 0x48, 0x0, 0x49, 0x0, - 0x4a, 0x0, 0x4b, 0x0, 0x4c, 0x0, 0x4d, 0x0, - 0x4e, 0x0, 0x4f, 0x0, 0x50, 0x0, 0x51, 0x0, - 0x52, 0x0, 0x53, 0x0, 0x54, 0x0, 0x55, 0x0, - 0x56, 0x0, 0x57, 0x0, 0x58, 0x0, 0x59, 0x0, - 0x5a, NTSC_CHROMA_PHASE, 0x5b, NTSC_GAINU, - 0x5c, NTSC_GAINV, 0x5d, NTSC_BLACK_LEVEL, - 0x5e, NTSC_BLANKING_LEVEL, 0x5f, NTSC_VBI_BLANKING, - 0x60, 0x0, 0x61, NTSC_DAC_CONTROL, - 0x62, NTSC_BURST_AMP, 0x63, NTSC_SUBC3, - 0x64, NTSC_SUBC2, 0x65, NTSC_SUBC1, - 0x66, NTSC_SUBC0, 0x67, 0x80, 0x68, 0x80, - 0x69, 0x80, 0x6a, 0x80, 0x6b, 0x29, - 0x6c, NTSC_HTRIG, 0x6d, NTSC_VTRIG, - 0x6e, NTSC_MULTI, 0x6f, NTSC_CCTTX, - 0x70, 0xc9, 0x71, 0x68, 0x72, 0x60, 0x73, 0x0, - 0x74, 0x0, 0x75, 0x0, 0x76, 0x0, 0x77, 0x0, - 0x78, 0x0, 0x79, 0x0, 0x7a, NTSC_FIRST_ACTIVE, - 0x7b, NTSC_LAST_ACTIVE, 0x7c, NTSC_MSB_VERTICAL, - 0x7d, 0x0, 0x7e, 0x0, 0x7f, 0x0 -}; -#define INIT7121LEN (sizeof(init7121ntsc)/2) - -static __u8 init7121pal[] = { - 0x26, 0x0, 0x27, 0x0, - 0x28, PAL_BURST_START, 0x29, PAL_BURST_END, - 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, 0x2d, 0x0, - 0x2e, 0x0, 0x2f, 0x0, 0x30, 0x0, 0x31, 0x0, - 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0, - 0x36, 0x0, 0x37, 0x0, 0x38, 0x0, 0x39, 0x0, - 0x3a, 0x03, 0x3b, 0x0, 0x3c, 0x0, 0x3d, 0x0, - 0x3e, 0x0, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0, - 0x42, 0x0, 0x43, 0x0, 0x44, 0x0, 0x45, 0x0, - 0x46, 0x0, 0x47, 0x0, 0x48, 0x0, 0x49, 0x0, - 0x4a, 0x0, 0x4b, 0x0, 0x4c, 0x0, 0x4d, 0x0, - 0x4e, 0x0, 0x4f, 0x0, 0x50, 0x0, 0x51, 0x0, - 0x52, 0x0, 0x53, 0x0, 0x54, 0x0, 0x55, 0x0, - 0x56, 0x0, 0x57, 0x0, 0x58, 0x0, 0x59, 0x0, - 0x5a, PAL_CHROMA_PHASE, 0x5b, PAL_GAINU, - 0x5c, PAL_GAINV, 0x5d, PAL_BLACK_LEVEL, - 0x5e, PAL_BLANKING_LEVEL, 0x5f, PAL_VBI_BLANKING, - 0x60, 0x0, 0x61, PAL_DAC_CONTROL, - 0x62, PAL_BURST_AMP, 0x63, PAL_SUBC3, - 0x64, PAL_SUBC2, 0x65, PAL_SUBC1, - 0x66, PAL_SUBC0, 0x67, 0x80, 0x68, 0x80, - 0x69, 0x80, 0x6a, 0x80, 0x6b, 0x29, - 0x6c, PAL_HTRIG, 0x6d, PAL_VTRIG, - 0x6e, PAL_MULTI, 0x6f, PAL_CCTTX, - 0x70, 0xc9, 0x71, 0x68, 0x72, 0x60, 0x73, 0x0, - 0x74, 0x0, 0x75, 0x0, 0x76, 0x0, 0x77, 0x0, - 0x78, 0x0, 0x79, 0x0, 0x7a, PAL_FIRST_ACTIVE, - 0x7b, PAL_LAST_ACTIVE, 0x7c, PAL_MSB_VERTICAL, - 0x7d, 0x0, 0x7e, 0x0, 0x7f, 0x0 -}; -#endif diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 5dfd826d734e..cc7f3d6ee966 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -1282,7 +1282,7 @@ static int dvb_init(struct saa7134_dev *dev) case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: if (configure_tda827x_fe(dev, &tda827x_lifeview_config, &tda827x_cfg_0) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_PHILIPS_EUROPA: case SAA7134_BOARD_VIDEOMATE_DVBT_300: @@ -1322,7 +1322,7 @@ static int dvb_init(struct saa7134_dev *dev) case SAA7134_BOARD_KWORLD_DVBT_210: if (configure_tda827x_fe(dev, &kworld_dvb_t_210_config, &tda827x_cfg_2) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_HAUPPAUGE_HVR1120: fe0->dvb.frontend = dvb_attach(tda10048_attach, @@ -1340,17 +1340,17 @@ static int dvb_init(struct saa7134_dev *dev) case SAA7134_BOARD_PHILIPS_TIGER: if (configure_tda827x_fe(dev, &philips_tiger_config, &tda827x_cfg_0) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_PINNACLE_PCTV_310i: if (configure_tda827x_fe(dev, &pinnacle_pctv_310i_config, &tda827x_cfg_1) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: if (configure_tda827x_fe(dev, &hauppauge_hvr_1110_config, &tda827x_cfg_1) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_HAUPPAUGE_HVR1150: fe0->dvb.frontend = dvb_attach(lgdt3305_attach, @@ -1368,30 +1368,30 @@ static int dvb_init(struct saa7134_dev *dev) case SAA7134_BOARD_ASUSTeK_P7131_DUAL: if (configure_tda827x_fe(dev, &asus_p7131_dual_config, &tda827x_cfg_0) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_FLYDVBT_LR301: if (configure_tda827x_fe(dev, &tda827x_lifeview_config, &tda827x_cfg_0) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_FLYDVB_TRIO: if (!use_frontend) { /* terrestrial */ if (configure_tda827x_fe(dev, &lifeview_trio_config, &tda827x_cfg_0) < 0) - goto dettach_frontend; + goto detach_frontend; } else { /* satellite */ fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); if (fe0->dvb.frontend) { if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x63, &dev->i2c_adap, 0) == NULL) { wprintk("%s: Lifeview Trio, No tda826x found!\n", __func__); - goto dettach_frontend; + goto detach_frontend; } if (dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->i2c_adap, 0x08, 0, 0) == NULL) { wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __func__); - goto dettach_frontend; + goto detach_frontend; } } } @@ -1407,7 +1407,7 @@ static int dvb_init(struct saa7134_dev *dev) &ads_duo_cfg) == NULL) { wprintk("no tda827x tuner found at addr: %02x\n", ads_tech_duo_config.tuner_address); - goto dettach_frontend; + goto detach_frontend; } } else wprintk("failed to attach tda10046\n"); @@ -1415,13 +1415,13 @@ static int dvb_init(struct saa7134_dev *dev) case SAA7134_BOARD_TEVION_DVBT_220RF: if (configure_tda827x_fe(dev, &tevion_dvbt220rf_config, &tda827x_cfg_0) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_MEDION_MD8800_QUADRO: if (!use_frontend) { /* terrestrial */ if (configure_tda827x_fe(dev, &md8800_dvbt_config, &tda827x_cfg_0) < 0) - goto dettach_frontend; + goto detach_frontend; } else { /* satellite */ fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); @@ -1435,7 +1435,7 @@ static int dvb_init(struct saa7134_dev *dev) 0x60, &dev->i2c_adap, 0) == NULL) { wprintk("%s: Medion Quadro, no tda826x " "found !\n", __func__); - goto dettach_frontend; + goto detach_frontend; } if (dev_id != 0x08) { /* we need to open the i2c gate (we know it exists) */ @@ -1444,7 +1444,7 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap, 0x08, 0, 0) == NULL) { wprintk("%s: Medion Quadro, no ISL6405 " "found !\n", __func__); - goto dettach_frontend; + goto detach_frontend; } if (dev_id == 0x07) { /* fire up the 2nd section of the LNB supply since @@ -1503,12 +1503,12 @@ static int dvb_init(struct saa7134_dev *dev) if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60, &dev->i2c_adap, 0) == NULL) { wprintk("%s: No tda826x found!\n", __func__); - goto dettach_frontend; + goto detach_frontend; } if (dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->i2c_adap, 0x08, 0, 0) == NULL) { wprintk("%s: No ISL6421 found!\n", __func__); - goto dettach_frontend; + goto detach_frontend; } } break; @@ -1537,37 +1537,37 @@ static int dvb_init(struct saa7134_dev *dev) case SAA7134_BOARD_CINERGY_HT_PCMCIA: if (configure_tda827x_fe(dev, &cinergy_ht_config, &tda827x_cfg_0) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_CINERGY_HT_PCI: if (configure_tda827x_fe(dev, &cinergy_ht_pci_config, &tda827x_cfg_0) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_PHILIPS_TIGER_S: if (configure_tda827x_fe(dev, &philips_tiger_s_config, &tda827x_cfg_2) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_ASUS_P7131_4871: if (configure_tda827x_fe(dev, &asus_p7131_4871_config, &tda827x_cfg_2) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: if (configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config, &tda827x_cfg_2) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_AVERMEDIA_SUPER_007: if (configure_tda827x_fe(dev, &avermedia_super_007_config, &tda827x_cfg_0) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_TWINHAN_DTV_DVB_3056: if (configure_tda827x_fe(dev, &twinhan_dtv_dvb_3056_config, &tda827x_cfg_2_sw42) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_PHILIPS_SNAKE: fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, @@ -1576,24 +1576,24 @@ static int dvb_init(struct saa7134_dev *dev) if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60, &dev->i2c_adap, 0) == NULL) { wprintk("%s: No tda826x found!\n", __func__); - goto dettach_frontend; + goto detach_frontend; } if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, &dev->i2c_adap, 0, 0) == NULL) { wprintk("%s: No lnbp21 found!\n", __func__); - goto dettach_frontend; + goto detach_frontend; } } break; case SAA7134_BOARD_CREATIX_CTX953: if (configure_tda827x_fe(dev, &md8800_dvbt_config, &tda827x_cfg_0) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_MSI_TVANYWHERE_AD11: if (configure_tda827x_fe(dev, &philips_tiger_s_config, &tda827x_cfg_2) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: dprintk("AverMedia E506R dvb setup\n"); @@ -1614,7 +1614,7 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap, DVB_PLL_PHILIPS_SD1878_TDA8261) == NULL) { wprintk("%s: MD7134 DVB-S, no SD1878 " "found !\n", __func__); - goto dettach_frontend; + goto detach_frontend; } /* we need to open the i2c gate (we know it exists) */ fe = fe0->dvb.frontend; @@ -1623,7 +1623,7 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap, 0x08, 0, 0) == NULL) { wprintk("%s: MD7134 DVB-S, no ISL6405 " "found !\n", __func__); - goto dettach_frontend; + goto detach_frontend; } fe->ops.i2c_gate_ctrl(fe, 0); dev->original_set_voltage = fe->ops.set_voltage; @@ -1645,7 +1645,7 @@ static int dvb_init(struct saa7134_dev *dev) if (!use_frontend) { /* terrestrial */ if (configure_tda827x_fe(dev, &asus_tiger_3in1_config, &tda827x_cfg_2) < 0) - goto dettach_frontend; + goto detach_frontend; } else { /* satellite */ fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); @@ -1655,13 +1655,13 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap, 0) == NULL) { wprintk("%s: Asus Tiger 3in1, no " "tda826x found!\n", __func__); - goto dettach_frontend; + goto detach_frontend; } if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, &dev->i2c_adap, 0, 0) == NULL) { wprintk("%s: Asus Tiger 3in1, no lnbp21" " found!\n", __func__); - goto dettach_frontend; + goto detach_frontend; } } } @@ -1670,7 +1670,7 @@ static int dvb_init(struct saa7134_dev *dev) if (!use_frontend) { /* terrestrial */ if (configure_tda827x_fe(dev, &asus_ps3_100_config, &tda827x_cfg_2) < 0) - goto dettach_frontend; + goto detach_frontend; } else { /* satellite */ fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); @@ -1680,13 +1680,13 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap, 0) == NULL) { wprintk("%s: Asus My Cinema PS3-100, no " "tda826x found!\n", __func__); - goto dettach_frontend; + goto detach_frontend; } if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, &dev->i2c_adap, 0, 0) == NULL) { wprintk("%s: Asus My Cinema PS3-100, no lnbp21" " found!\n", __func__); - goto dettach_frontend; + goto detach_frontend; } } } @@ -1694,7 +1694,7 @@ static int dvb_init(struct saa7134_dev *dev) case SAA7134_BOARD_ASUSTeK_TIGER: if (configure_tda827x_fe(dev, &philips_tiger_config, &tda827x_cfg_0) < 0) - goto dettach_frontend; + goto detach_frontend; break; case SAA7134_BOARD_BEHOLD_H6: fe0->dvb.frontend = dvb_attach(zl10353_attach, @@ -1830,19 +1830,19 @@ static int dvb_init(struct saa7134_dev *dev) }; if (!fe0->dvb.frontend) - goto dettach_frontend; + goto detach_frontend; fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg); if (!fe) { printk(KERN_ERR "%s/2: xc3028 attach failed\n", dev->name); - goto dettach_frontend; + goto detach_frontend; } } if (NULL == fe0->dvb.frontend) { printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name); - goto dettach_frontend; + goto detach_frontend; } /* define general-purpose callback pointer */ fe0->dvb.frontend->callback = saa7134_tuner_callback; @@ -1864,7 +1864,7 @@ static int dvb_init(struct saa7134_dev *dev) } return ret; -dettach_frontend: +detach_frontend: videobuf_dvb_dealloc_frontends(&dev->frontends); return -EINVAL; } diff --git a/drivers/media/video/saa7146.h b/drivers/media/video/saa7146.h deleted file mode 100644 index 9fadb331a40b..000000000000 --- a/drivers/media/video/saa7146.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - saa7146.h - definitions philips saa7146 based cards - Copyright (C) 1999 Nathan Laredo (laredo@gnu.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. - - 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 __SAA7146__ -#define __SAA7146__ - -#define SAA7146_VERSION_CODE 0x000101 - -#include <linux/types.h> -#include <linux/wait.h> - -#ifndef O_NONCAP -#define O_NONCAP O_TRUNC -#endif - -#define MAX_GBUFFERS 2 -#define FBUF_SIZE 0x190000 - -#ifdef __KERNEL__ - -struct saa7146_window -{ - int x, y; - ushort width, height; - ushort bpp, bpl; - ushort swidth, sheight; - short cropx, cropy; - ushort cropwidth, cropheight; - unsigned long vidadr; - int color_fmt; - ushort depth; -}; - -/* Per-open data for handling multiple opens on one device */ -struct device_open -{ - int isopen; - int noncapturing; - struct saa7146 *dev; -}; -#define MAX_OPENS 3 - -struct saa7146 -{ - struct video_device video_dev; - struct video_picture picture; - struct video_audio audio_dev; - struct video_info vidinfo; - int user; - int cap; - int capuser; - int irqstate; /* irq routine is state driven */ - int writemode; - int playmode; - unsigned int nr; - unsigned long irq; /* IRQ used by SAA7146 card */ - unsigned short id; - unsigned char revision; - unsigned char boardcfg[64]; /* 64 bytes of config from eeprom */ - unsigned long saa7146_adr; /* bus address of IO mem from PCI BIOS */ - struct saa7146_window win; - unsigned char __iomem *saa7146_mem; /* pointer to mapped IO memory */ - struct device_open open_data[MAX_OPENS]; -#define MAX_MARKS 16 - /* for a/v sync */ - int endmark[MAX_MARKS], endmarkhead, endmarktail; - u32 *dmaRPS1, *pageRPS1, *dmaRPS2, *pageRPS2, *dmavid1, *dmavid2, - *dmavid3, *dmaa1in, *dmaa1out, *dmaa2in, *dmaa2out, - *pagedebi, *pagevid1, *pagevid2, *pagevid3, *pagea1in, - *pagea1out, *pagea2in, *pagea2out; - wait_queue_head_t i2cq, debiq, audq, vidq; - u8 *vidbuf, *audbuf, *osdbuf, *dmadebi; - int audhead, vidhead, osdhead, audtail, vidtail, osdtail; - spinlock_t lock; /* the device lock */ -}; -#endif - -#ifdef _ALPHA_SAA7146 -#define saawrite(dat,adr) writel((dat), saa->saa7146_adr+(adr)) -#define saaread(adr) readl(saa->saa7146_adr+(adr)) -#else -#define saawrite(dat,adr) writel((dat), saa->saa7146_mem+(adr)) -#define saaread(adr) readl(saa->saa7146_mem+(adr)) -#endif - -#define saaand(dat,adr) saawrite((dat) & saaread(adr), adr) -#define saaor(dat,adr) saawrite((dat) | saaread(adr), adr) -#define saaaor(dat,mask,adr) saawrite((dat) | ((mask) & saaread(adr)), adr) - -/* bitmask of attached hardware found */ -#define SAA7146_UNKNOWN 0x00000000 -#define SAA7146_SAA7111 0x00000001 -#define SAA7146_SAA7121 0x00000002 -#define SAA7146_IBMMPEG 0x00000004 - -#endif diff --git a/drivers/media/video/saa7146reg.h b/drivers/media/video/saa7146reg.h deleted file mode 100644 index 80ec2c146b4c..000000000000 --- a/drivers/media/video/saa7146reg.h +++ /dev/null @@ -1,283 +0,0 @@ -/* - saa7146.h - definitions philips saa7146 based cards - Copyright (C) 1999 Nathan Laredo (laredo@gnu.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. - - 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 __SAA7146_REG__ -#define __SAA7146_REG__ -#define SAA7146_BASE_ODD1 0x00 -#define SAA7146_BASE_EVEN1 0x04 -#define SAA7146_PROT_ADDR1 0x08 -#define SAA7146_PITCH1 0x0c -#define SAA7146_PAGE1 0x10 -#define SAA7146_NUM_LINE_BYTE1 0x14 -#define SAA7146_BASE_ODD2 0x18 -#define SAA7146_BASE_EVEN2 0x1c -#define SAA7146_PROT_ADDR2 0x20 -#define SAA7146_PITCH2 0x24 -#define SAA7146_PAGE2 0x28 -#define SAA7146_NUM_LINE_BYTE2 0x2c -#define SAA7146_BASE_ODD3 0x30 -#define SAA7146_BASE_EVEN3 0x34 -#define SAA7146_PROT_ADDR3 0x38 -#define SAA7146_PITCH3 0x3c -#define SAA7146_PAGE3 0x40 -#define SAA7146_NUM_LINE_BYTE3 0x44 -#define SAA7146_PCI_BT_V1 0x48 -#define SAA7146_PCI_BT_V2 0x49 -#define SAA7146_PCI_BT_V3 0x4a -#define SAA7146_PCI_BT_DEBI 0x4b -#define SAA7146_PCI_BT_A 0x4c -#define SAA7146_DD1_INIT 0x50 -#define SAA7146_DD1_STREAM_B 0x54 -#define SAA7146_DD1_STREAM_A 0x56 -#define SAA7146_BRS_CTRL 0x58 -#define SAA7146_HPS_CTRL 0x5c -#define SAA7146_HPS_V_SCALE 0x60 -#define SAA7146_HPS_V_GAIN 0x64 -#define SAA7146_HPS_H_PRESCALE 0x68 -#define SAA7146_HPS_H_SCALE 0x6c -#define SAA7146_BCS_CTRL 0x70 -#define SAA7146_CHROMA_KEY_RANGE 0x74 -#define SAA7146_CLIP_FORMAT_CTRL 0x78 -#define SAA7146_DEBI_CONFIG 0x7c -#define SAA7146_DEBI_COMMAND 0x80 -#define SAA7146_DEBI_PAGE 0x84 -#define SAA7146_DEBI_AD 0x88 -#define SAA7146_I2C_TRANSFER 0x8c -#define SAA7146_I2C_STATUS 0x90 -#define SAA7146_BASE_A1_IN 0x94 -#define SAA7146_PROT_A1_IN 0x98 -#define SAA7146_PAGE_A1_IN 0x9C -#define SAA7146_BASE_A1_OUT 0xa0 -#define SAA7146_PROT_A1_OUT 0xa4 -#define SAA7146_PAGE_A1_OUT 0xa8 -#define SAA7146_BASE_A2_IN 0xac -#define SAA7146_PROT_A2_IN 0xb0 -#define SAA7146_PAGE_A2_IN 0xb4 -#define SAA7146_BASE_A2_OUT 0xb8 -#define SAA7146_PROT_A2_OUT 0xbc -#define SAA7146_PAGE_A2_OUT 0xc0 -#define SAA7146_RPS_PAGE0 0xc4 -#define SAA7146_RPS_PAGE1 0xc8 -#define SAA7146_RPS_THRESH0 0xcc -#define SAA7146_RPS_THRESH1 0xd0 -#define SAA7146_RPS_TOV0 0xd4 -#define SAA7146_RPS_TOV1 0xd8 -#define SAA7146_IER 0xdc -#define SAA7146_GPIO_CTRL 0xe0 -#define SAA7146_EC1SSR 0xe4 -#define SAA7146_EC2SSR 0xe8 -#define SAA7146_ECT1R 0xec -#define SAA7146_ECT2R 0xf0 -#define SAA7146_ACON1 0xf4 -#define SAA7146_ACON2 0xf8 -#define SAA7146_MC1 0xfc -#define SAA7146_MC2 0x100 -#define SAA7146_RPS_ADDR0 0x104 -#define SAA7146_RPS_ADDR1 0x108 -#define SAA7146_ISR 0x10c -#define SAA7146_PSR 0x110 -#define SAA7146_SSR 0x114 -#define SAA7146_EC1R 0x118 -#define SAA7146_EC2R 0x11c -#define SAA7146_VDP1 0x120 -#define SAA7146_VDP2 0x124 -#define SAA7146_VDP3 0x128 -#define SAA7146_ADP1 0x12c -#define SAA7146_ADP2 0x130 -#define SAA7146_ADP3 0x134 -#define SAA7146_ADP4 0x138 -#define SAA7146_DDP 0x13c -#define SAA7146_LEVEL_REP 0x140 -#define SAA7146_FB_BUFFER1 0x144 -#define SAA7146_FB_BUFFER2 0x148 -#define SAA7146_A_TIME_SLOT1 0x180 -#define SAA7146_A_TIME_SLOT2 0x1C0 - -/* bitfield defines */ -#define MASK_31 0x80000000 -#define MASK_30 0x40000000 -#define MASK_29 0x20000000 -#define MASK_28 0x10000000 -#define MASK_27 0x08000000 -#define MASK_26 0x04000000 -#define MASK_25 0x02000000 -#define MASK_24 0x01000000 -#define MASK_23 0x00800000 -#define MASK_22 0x00400000 -#define MASK_21 0x00200000 -#define MASK_20 0x00100000 -#define MASK_19 0x00080000 -#define MASK_18 0x00040000 -#define MASK_17 0x00020000 -#define MASK_16 0x00010000 -#define MASK_15 0x00008000 -#define MASK_14 0x00004000 -#define MASK_13 0x00002000 -#define MASK_12 0x00001000 -#define MASK_11 0x00000800 -#define MASK_10 0x00000400 -#define MASK_09 0x00000200 -#define MASK_08 0x00000100 -#define MASK_07 0x00000080 -#define MASK_06 0x00000040 -#define MASK_05 0x00000020 -#define MASK_04 0x00000010 -#define MASK_03 0x00000008 -#define MASK_02 0x00000004 -#define MASK_01 0x00000002 -#define MASK_00 0x00000001 -#define MASK_B0 0x000000ff -#define MASK_B1 0x0000ff00 -#define MASK_B2 0x00ff0000 -#define MASK_B3 0xff000000 -#define MASK_W0 0x0000ffff -#define MASK_W1 0xffff0000 -#define MASK_PA 0xfffffffc -#define MASK_PR 0xfffffffe -#define MASK_ER 0xffffffff -#define MASK_NONE 0x00000000 - -#define SAA7146_PAGE_MAP_EN MASK_11 -/* main control register 1 */ -#define SAA7146_MC1_MRST_N MASK_15 -#define SAA7146_MC1_ERPS1 MASK_13 -#define SAA7146_MC1_ERPS0 MASK_12 -#define SAA7146_MC1_EDP MASK_11 -#define SAA7146_MC1_EVP MASK_10 -#define SAA7146_MC1_EAP MASK_09 -#define SAA7146_MC1_EI2C MASK_08 -#define SAA7146_MC1_TR_E_DEBI MASK_07 -#define SAA7146_MC1_TR_E_1 MASK_06 -#define SAA7146_MC1_TR_E_2 MASK_05 -#define SAA7146_MC1_TR_E_3 MASK_04 -#define SAA7146_MC1_TR_E_A2_OUT MASK_03 -#define SAA7146_MC1_TR_E_A2_IN MASK_02 -#define SAA7146_MC1_TR_E_A1_OUT MASK_01 -#define SAA7146_MC1_TR_E_A1_IN MASK_00 -/* main control register 2 */ -#define SAA7146_MC2_RPS_SIG4 MASK_15 -#define SAA7146_MC2_RPS_SIG3 MASK_14 -#define SAA7146_MC2_RPS_SIG2 MASK_13 -#define SAA7146_MC2_RPS_SIG1 MASK_12 -#define SAA7146_MC2_RPS_SIG0 MASK_11 -#define SAA7146_MC2_UPLD_D1_B MASK_10 -#define SAA7146_MC2_UPLD_D1_A MASK_09 -#define SAA7146_MC2_UPLD_BRS MASK_08 -#define SAA7146_MC2_UPLD_HPS_H MASK_06 -#define SAA7146_MC2_UPLD_HPS_V MASK_05 -#define SAA7146_MC2_UPLD_DMA3 MASK_04 -#define SAA7146_MC2_UPLD_DMA2 MASK_03 -#define SAA7146_MC2_UPLD_DMA1 MASK_02 -#define SAA7146_MC2_UPLD_DEBI MASK_01 -#define SAA7146_MC2_UPLD_I2C MASK_00 -/* Primary Status Register and Interrupt Enable/Status Registers */ -#define SAA7146_PSR_PPEF MASK_31 -#define SAA7146_PSR_PABO MASK_30 -#define SAA7146_PSR_PPED MASK_29 -#define SAA7146_PSR_RPS_I1 MASK_28 -#define SAA7146_PSR_RPS_I0 MASK_27 -#define SAA7146_PSR_RPS_LATE1 MASK_26 -#define SAA7146_PSR_RPS_LATE0 MASK_25 -#define SAA7146_PSR_RPS_E1 MASK_24 -#define SAA7146_PSR_RPS_E0 MASK_23 -#define SAA7146_PSR_RPS_TO1 MASK_22 -#define SAA7146_PSR_RPS_TO0 MASK_21 -#define SAA7146_PSR_UPLD MASK_20 -#define SAA7146_PSR_DEBI_S MASK_19 -#define SAA7146_PSR_DEBI_E MASK_18 -#define SAA7146_PSR_I2C_S MASK_17 -#define SAA7146_PSR_I2C_E MASK_16 -#define SAA7146_PSR_A2_IN MASK_15 -#define SAA7146_PSR_A2_OUT MASK_14 -#define SAA7146_PSR_A1_IN MASK_13 -#define SAA7146_PSR_A1_OUT MASK_12 -#define SAA7146_PSR_AFOU MASK_11 -#define SAA7146_PSR_V_PE MASK_10 -#define SAA7146_PSR_VFOU MASK_09 -#define SAA7146_PSR_FIDA MASK_08 -#define SAA7146_PSR_FIDB MASK_07 -#define SAA7146_PSR_PIN3 MASK_06 -#define SAA7146_PSR_PIN2 MASK_05 -#define SAA7146_PSR_PIN1 MASK_04 -#define SAA7146_PSR_PIN0 MASK_03 -#define SAA7146_PSR_ECS MASK_02 -#define SAA7146_PSR_EC3S MASK_01 -#define SAA7146_PSR_EC0S MASK_00 -/* Secondary Status Register */ -#define SAA7146_SSR_PRQ MASK_31 -#define SAA7146_SSR_PMA MASK_30 -#define SAA7146_SSR_RPS_RE1 MASK_29 -#define SAA7146_SSR_RPS_PE1 MASK_28 -#define SAA7146_SSR_RPS_A1 MASK_27 -#define SAA7146_SSR_RPS_RE0 MASK_26 -#define SAA7146_SSR_RPS_PE0 MASK_25 -#define SAA7146_SSR_RPS_A0 MASK_24 -#define SAA7146_SSR_DEBI_TO MASK_23 -#define SAA7146_SSR_DEBI_EF MASK_22 -#define SAA7146_SSR_I2C_EA MASK_21 -#define SAA7146_SSR_I2C_EW MASK_20 -#define SAA7146_SSR_I2C_ER MASK_19 -#define SAA7146_SSR_I2C_EL MASK_18 -#define SAA7146_SSR_I2C_EF MASK_17 -#define SAA7146_SSR_V3P MASK_16 -#define SAA7146_SSR_V2P MASK_15 -#define SAA7146_SSR_V1P MASK_14 -#define SAA7146_SSR_VF3 MASK_13 -#define SAA7146_SSR_VF2 MASK_12 -#define SAA7146_SSR_VF1 MASK_11 -#define SAA7146_SSR_AF2_IN MASK_10 -#define SAA7146_SSR_AF2_OUT MASK_09 -#define SAA7146_SSR_AF1_IN MASK_08 -#define SAA7146_SSR_AF1_OUT MASK_07 -#define SAA7146_SSR_VGT MASK_05 -#define SAA7146_SSR_LNQG MASK_04 -#define SAA7146_SSR_EC5S MASK_03 -#define SAA7146_SSR_EC4S MASK_02 -#define SAA7146_SSR_EC2S MASK_01 -#define SAA7146_SSR_EC1S MASK_00 -/* I2C status register */ -#define SAA7146_I2C_ABORT MASK_07 -#define SAA7146_I2C_SPERR MASK_06 -#define SAA7146_I2C_APERR MASK_05 -#define SAA7146_I2C_DTERR MASK_04 -#define SAA7146_I2C_DRERR MASK_03 -#define SAA7146_I2C_AL MASK_02 -#define SAA7146_I2C_ERR MASK_01 -#define SAA7146_I2C_BUSY MASK_00 -/* output formats */ -#define SAA7146_YUV422 0 -#define SAA7146_RGB16 0 -#define SAA7146_YUV444 1 -#define SAA7146_RGB24 1 -#define SAA7146_ARGB32 2 -#define SAA7146_YUV411 3 -#define SAA7146_ARGB15 3 -#define SAA7146_YUV2 4 -#define SAA7146_RGAB15 4 -#define SAA7146_Y8 6 -#define SAA7146_YUV8 7 -#define SAA7146_RGB8 7 -#define SAA7146_YUV444p 8 -#define SAA7146_YUV422p 9 -#define SAA7146_YUV420p 10 -#define SAA7146_YUV1620 11 -#define SAA7146_Y1 13 -#define SAA7146_Y2 14 -#define SAA7146_YUV1 15 -#endif diff --git a/drivers/media/video/saa7164/saa7164-api.c b/drivers/media/video/saa7164/saa7164-api.c index 8a98ab68239e..c8799fdaae67 100644 --- a/drivers/media/video/saa7164/saa7164-api.c +++ b/drivers/media/video/saa7164/saa7164-api.c @@ -1367,7 +1367,6 @@ int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg, struct saa7164_dev *dev = bus->dev; u16 len = 0; int unitid; - u32 regval; u8 buf[256]; int ret; @@ -1376,19 +1375,6 @@ int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg, if (reglen > 4) return -EIO; - if (reglen == 1) - regval = *(reg); - else - if (reglen == 2) - regval = ((*(reg) << 8) || *(reg+1)); - else - if (reglen == 3) - regval = ((*(reg) << 16) | (*(reg+1) << 8) | *(reg+2)); - else - if (reglen == 4) - regval = ((*(reg) << 24) | (*(reg+1) << 16) | - (*(reg+2) << 8) | *(reg+3)); - /* Prepare the send buffer */ /* Bytes 00-03 source register length * 04-07 source bytes to read diff --git a/drivers/media/video/smiapp/Kconfig b/drivers/media/video/smiapp/Kconfig index fb99ff18be07..3149cda1d0db 100644 --- a/drivers/media/video/smiapp/Kconfig +++ b/drivers/media/video/smiapp/Kconfig @@ -1,6 +1,7 @@ config VIDEO_SMIAPP tristate "SMIA++/SMIA sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAVE_CLK + depends on MEDIA_CAMERA_SUPPORT select VIDEO_SMIAPP_PLL ---help--- This is a generic driver for SMIA++/SMIA camera modules. diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index 9cf5bda35fbe..f466a7edcb2a 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -39,9 +39,9 @@ #include "smiapp.h" -#define SMIAPP_ALIGN_DIM(dim, flags) \ - ((flags) & V4L2_SUBDEV_SEL_FLAG_SIZE_GE \ - ? ALIGN((dim), 2) \ +#define SMIAPP_ALIGN_DIM(dim, flags) \ + ((flags) & V4L2_SEL_FLAG_GE \ + ? ALIGN((dim), 2) \ : (dim) & ~1) /* @@ -1631,7 +1631,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev, smiapp_get_crop_compose(subdev, fh, crops, &comp, which); switch (target) { - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SEL_TGT_CROP: comp->width = crops[SMIAPP_PAD_SINK]->width; comp->height = crops[SMIAPP_PAD_SINK]->height; if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { @@ -1647,7 +1647,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev, } } /* Fall through */ - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SEL_TGT_COMPOSE: *crops[SMIAPP_PAD_SRC] = *comp; break; default: @@ -1723,7 +1723,7 @@ static int smiapp_set_format(struct v4l2_subdev *subdev, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) ssd->sink_fmt = *crops[ssd->sink_pad]; smiapp_propagate(subdev, fh, fmt->which, - V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL); + V4L2_SEL_TGT_CROP); mutex_unlock(&sensor->mutex); @@ -1748,14 +1748,14 @@ static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, h &= ~1; ask_h &= ~1; - if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_GE) { + if (flags & V4L2_SEL_FLAG_GE) { if (w < ask_w) val -= SCALING_GOODNESS; if (h < ask_h) val -= SCALING_GOODNESS; } - if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_LE) { + if (flags & V4L2_SEL_FLAG_LE) { if (w > ask_w) val -= SCALING_GOODNESS; if (h > ask_h) @@ -1958,7 +1958,7 @@ static int smiapp_set_compose(struct v4l2_subdev *subdev, *comp = sel->r; smiapp_propagate(subdev, fh, sel->which, - V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL); + V4L2_SEL_TGT_COMPOSE); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) return smiapp_update_mode(sensor); @@ -1974,8 +1974,8 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, /* We only implement crop in three places. */ switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: if (ssd == sensor->pixel_array && sel->pad == SMIAPP_PA_PAD_SRC) return 0; @@ -1988,8 +1988,8 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) return 0; return -EINVAL; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: - case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: if (sel->pad == ssd->source_pad) return -EINVAL; if (ssd == sensor->binner) @@ -2051,7 +2051,7 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev, if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) smiapp_propagate(subdev, fh, sel->which, - V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL); + V4L2_SEL_TGT_CROP); return 0; } @@ -2085,7 +2085,7 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev, } switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: if (ssd == sensor->pixel_array) { sel->r.width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; @@ -2097,11 +2097,11 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev, sel->r = *comp; } break; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: - case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: sel->r = *crops[sel->pad]; break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SEL_TGT_COMPOSE: sel->r = *comp; break; } @@ -2148,10 +2148,10 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev, sel->r.height); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SEL_TGT_CROP: ret = smiapp_set_crop(subdev, fh, sel); break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SEL_TGT_COMPOSE: ret = smiapp_set_compose(subdev, fh, sel); break; default: diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h index 22ea211ab54f..2bc153e869be 100644 --- a/drivers/media/video/sn9c102/sn9c102.h +++ b/drivers/media/video/sn9c102/sn9c102.h @@ -182,7 +182,7 @@ do { \ # define V4LDBG(level, name, cmd) \ do { \ if (debug >= (level)) \ - v4l_print_ioctl(name, cmd); \ + v4l_printk_ioctl(name, cmd); \ } while (0) # define KDBG(level, fmt, args...) \ do { \ diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 1ad5ab6ce5cf..b5a819af2b8c 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -228,6 +228,16 @@ static int fe_has_signal(struct dvb_frontend *fe) return strength; } +static int fe_get_afc(struct dvb_frontend *fe) +{ + s32 afc = 0; + + if (fe->ops.tuner_ops.get_afc) + fe->ops.tuner_ops.get_afc(fe, &afc); + + return 0; +} + static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg) { struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops; @@ -247,6 +257,7 @@ static struct analog_demod_ops tuner_analog_ops = { .set_params = fe_set_params, .standby = fe_standby, .has_signal = fe_has_signal, + .get_afc = fe_get_afc, .set_config = fe_set_config, .tuner_status = tuner_status }; @@ -1178,6 +1189,8 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; if (vt->type == t->mode && analog_ops->get_afc) vt->afc = analog_ops->get_afc(&t->fe); + if (analog_ops->has_signal) + vt->signal = analog_ops->has_signal(&t->fe); if (vt->type != V4L2_TUNER_RADIO) { vt->capability |= V4L2_TUNER_CAP_NORM; vt->rangelow = tv_range[0] * 16; @@ -1197,8 +1210,6 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; } - if (analog_ops->has_signal) - vt->signal = analog_ops->has_signal(&t->fe); vt->audmode = t->audmode; } vt->capability |= V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index c5b1a7365e4f..321b3153df87 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -59,8 +59,8 @@ struct CHIPSTATE; typedef int (*getvalue)(int); typedef int (*checkit)(struct CHIPSTATE*); typedef int (*initialize)(struct CHIPSTATE*); -typedef int (*getmode)(struct CHIPSTATE*); -typedef void (*setmode)(struct CHIPSTATE*, int mode); +typedef int (*getrxsubchans)(struct CHIPSTATE *); +typedef void (*setaudmode)(struct CHIPSTATE*, int mode); /* i2c command */ typedef struct AUDIOCMD { @@ -96,8 +96,8 @@ struct CHIPDESC { getvalue volfunc,treblefunc,bassfunc; /* get/set mode */ - getmode getmode; - setmode setmode; + getrxsubchans getrxsubchans; + setaudmode setaudmode; /* input switch register + values for v4l inputs */ int inputreg; @@ -118,7 +118,7 @@ struct CHIPSTATE { audiocmd shadow; /* current settings */ - __u16 left,right,treble,bass,muted,mode; + __u16 left, right, treble, bass, muted; int prevmode; int radio; int input; @@ -126,7 +126,6 @@ struct CHIPSTATE { /* thread */ struct task_struct *thread; struct timer_list wt; - int watch_stereo; int audmode; }; @@ -288,7 +287,7 @@ static int chip_thread(void *data) struct CHIPSTATE *chip = data; struct CHIPDESC *desc = chip->desc; struct v4l2_subdev *sd = &chip->sd; - int mode; + int mode, selected; v4l2_dbg(1, debug, sd, "thread started\n"); set_freezable(); @@ -302,12 +301,12 @@ static int chip_thread(void *data) break; v4l2_dbg(1, debug, sd, "thread wakeup\n"); - /* don't do anything for radio or if mode != auto */ - if (chip->radio || chip->mode != 0) + /* don't do anything for radio */ + if (chip->radio) continue; /* have a look what's going on */ - mode = desc->getmode(chip); + mode = desc->getrxsubchans(chip); if (mode == chip->prevmode) continue; @@ -316,16 +315,32 @@ static int chip_thread(void *data) chip->prevmode = mode; - if (mode & V4L2_TUNER_MODE_STEREO) - desc->setmode(chip, V4L2_TUNER_MODE_STEREO); - if (mode & V4L2_TUNER_MODE_LANG1_LANG2) - desc->setmode(chip, V4L2_TUNER_MODE_STEREO); - else if (mode & V4L2_TUNER_MODE_LANG1) - desc->setmode(chip, V4L2_TUNER_MODE_LANG1); - else if (mode & V4L2_TUNER_MODE_LANG2) - desc->setmode(chip, V4L2_TUNER_MODE_LANG2); - else - desc->setmode(chip, V4L2_TUNER_MODE_MONO); + selected = V4L2_TUNER_MODE_MONO; + switch (chip->audmode) { + case V4L2_TUNER_MODE_MONO: + if (mode & V4L2_TUNER_SUB_LANG1) + selected = V4L2_TUNER_MODE_LANG1; + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + if (mode & V4L2_TUNER_SUB_LANG1) + selected = V4L2_TUNER_MODE_LANG1; + else if (mode & V4L2_TUNER_SUB_STEREO) + selected = V4L2_TUNER_MODE_STEREO; + break; + case V4L2_TUNER_MODE_LANG2: + if (mode & V4L2_TUNER_SUB_LANG2) + selected = V4L2_TUNER_MODE_LANG2; + else if (mode & V4L2_TUNER_SUB_STEREO) + selected = V4L2_TUNER_MODE_STEREO; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + if (mode & V4L2_TUNER_SUB_LANG2) + selected = V4L2_TUNER_MODE_LANG1_LANG2; + else if (mode & V4L2_TUNER_SUB_STEREO) + selected = V4L2_TUNER_MODE_STEREO; + } + desc->setaudmode(chip, selected); /* schedule next check */ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); @@ -358,24 +373,25 @@ static int chip_thread(void *data) #define TDA9840_TEST_INT1SN 0x1 /* Integration time 0.5s when set */ #define TDA9840_TEST_INTFU 0x02 /* Disables integrator function */ -static int tda9840_getmode(struct CHIPSTATE *chip) +static int tda9840_getrxsubchans(struct CHIPSTATE *chip) { struct v4l2_subdev *sd = &chip->sd; int val, mode; val = chip_read(chip); - mode = V4L2_TUNER_MODE_MONO; + mode = V4L2_TUNER_SUB_MONO; if (val & TDA9840_DS_DUAL) - mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; if (val & TDA9840_ST_STEREO) - mode |= V4L2_TUNER_MODE_STEREO; + mode = V4L2_TUNER_SUB_STEREO; - v4l2_dbg(1, debug, sd, "tda9840_getmode(): raw chip read: %d, return: %d\n", + v4l2_dbg(1, debug, sd, + "tda9840_getrxsubchans(): raw chip read: %d, return: %d\n", val, mode); return mode; } -static void tda9840_setmode(struct CHIPSTATE *chip, int mode) +static void tda9840_setaudmode(struct CHIPSTATE *chip, int mode) { int update = 1; int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e; @@ -393,6 +409,9 @@ static void tda9840_setmode(struct CHIPSTATE *chip, int mode) case V4L2_TUNER_MODE_LANG2: t |= TDA9840_DUALB; break; + case V4L2_TUNER_MODE_LANG1_LANG2: + t |= TDA9840_DUALAB; + break; default: update = 0; } @@ -477,6 +496,7 @@ static int tda9840_checkit(struct CHIPSTATE *chip) /* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */ /* Common to TDA9855 and TDA9850: */ #define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */ +#define TDA985x_MONOSAP 2<<6 /* Selects Mono on left, SAP on right */ #define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */ #define TDA985x_MONO 0 /* Forces Mono output */ #define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */ @@ -513,18 +533,22 @@ static int tda9855_volume(int val) { return val/0x2e8+0x27; } static int tda9855_bass(int val) { return val/0xccc+0x06; } static int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; } -static int tda985x_getmode(struct CHIPSTATE *chip) +static int tda985x_getrxsubchans(struct CHIPSTATE *chip) { - int mode; + int mode, val; - mode = ((TDA985x_STP | TDA985x_SAPP) & - chip_read(chip)) >> 4; /* Add mono mode regardless of SAP and stereo */ /* Allows forced mono */ - return mode | V4L2_TUNER_MODE_MONO; + mode = V4L2_TUNER_SUB_MONO; + val = chip_read(chip); + if (val & TDA985x_STP) + mode = V4L2_TUNER_SUB_STEREO; + if (val & TDA985x_SAPP) + mode |= V4L2_TUNER_SUB_SAP; + return mode; } -static void tda985x_setmode(struct CHIPSTATE *chip, int mode) +static void tda985x_setaudmode(struct CHIPSTATE *chip, int mode) { int update = 1; int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f; @@ -534,11 +558,15 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode) c6 |= TDA985x_MONO; break; case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: c6 |= TDA985x_STEREO; break; - case V4L2_TUNER_MODE_LANG1: + case V4L2_TUNER_MODE_SAP: c6 |= TDA985x_SAP; break; + case V4L2_TUNER_MODE_LANG1_LANG2: + c6 |= TDA985x_MONOSAP; + break; default: update = 0; } @@ -583,9 +611,10 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode) #define TDA9873_TR_MASK (7 << 2) #define TDA9873_TR_MONO 4 #define TDA9873_TR_STEREO 1 << 4 -#define TDA9873_TR_REVERSE (1 << 3) & (1 << 2) +#define TDA9873_TR_REVERSE ((1 << 3) | (1 << 2)) #define TDA9873_TR_DUALA 1 << 2 #define TDA9873_TR_DUALB 1 << 3 +#define TDA9873_TR_DUALAB 0 /* output level controls * B5: output level switch (0 = reduced gain, 1 = normal gain) @@ -653,46 +682,51 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode) #define TDA9873_MOUT_DUALA 0 #define TDA9873_MOUT_DUALB 1 << 3 #define TDA9873_MOUT_ST 1 << 4 -#define TDA9873_MOUT_EXTM (1 << 4 ) & (1 << 3) +#define TDA9873_MOUT_EXTM ((1 << 4) | (1 << 3)) #define TDA9873_MOUT_EXTL 1 << 5 -#define TDA9873_MOUT_EXTR (1 << 5 ) & (1 << 3) -#define TDA9873_MOUT_EXTLR (1 << 5 ) & (1 << 4) -#define TDA9873_MOUT_MUTE (1 << 5 ) & (1 << 4) & (1 << 3) +#define TDA9873_MOUT_EXTR ((1 << 5) | (1 << 3)) +#define TDA9873_MOUT_EXTLR ((1 << 5) | (1 << 4)) +#define TDA9873_MOUT_MUTE ((1 << 5) | (1 << 4) | (1 << 3)) /* Status bits: (chip read) */ #define TDA9873_PONR 0 /* Power-on reset detected if = 1 */ #define TDA9873_STEREO 2 /* Stereo sound is identified */ #define TDA9873_DUAL 4 /* Dual sound is identified */ -static int tda9873_getmode(struct CHIPSTATE *chip) +static int tda9873_getrxsubchans(struct CHIPSTATE *chip) { struct v4l2_subdev *sd = &chip->sd; int val,mode; val = chip_read(chip); - mode = V4L2_TUNER_MODE_MONO; + mode = V4L2_TUNER_SUB_MONO; if (val & TDA9873_STEREO) - mode |= V4L2_TUNER_MODE_STEREO; + mode = V4L2_TUNER_SUB_STEREO; if (val & TDA9873_DUAL) - mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; - v4l2_dbg(1, debug, sd, "tda9873_getmode(): raw chip read: %d, return: %d\n", + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + v4l2_dbg(1, debug, sd, + "tda9873_getrxsubchans(): raw chip read: %d, return: %d\n", val, mode); return mode; } -static void tda9873_setmode(struct CHIPSTATE *chip, int mode) +static void tda9873_setaudmode(struct CHIPSTATE *chip, int mode) { struct v4l2_subdev *sd = &chip->sd; int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK; /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */ if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) { - v4l2_dbg(1, debug, sd, "tda9873_setmode(): external input\n"); + v4l2_dbg(1, debug, sd, + "tda9873_setaudmode(): external input\n"); return; } - v4l2_dbg(1, debug, sd, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); - v4l2_dbg(1, debug, sd, "tda9873_setmode(): sw_data = %d\n", sw_data); + v4l2_dbg(1, debug, sd, + "tda9873_setaudmode(): chip->shadow.bytes[%d] = %d\n", + TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); + v4l2_dbg(1, debug, sd, "tda9873_setaudmode(): sw_data = %d\n", + sw_data); switch (mode) { case V4L2_TUNER_MODE_MONO: @@ -707,13 +741,16 @@ static void tda9873_setmode(struct CHIPSTATE *chip, int mode) case V4L2_TUNER_MODE_LANG2: sw_data |= TDA9873_TR_DUALB; break; + case V4L2_TUNER_MODE_LANG1_LANG2: + sw_data |= TDA9873_TR_DUALAB; + break; default: - chip->mode = 0; return; } chip_write(chip, TDA9873_SW, sw_data); - v4l2_dbg(1, debug, sd, "tda9873_setmode(): req. mode %d; chip_write: %d\n", + v4l2_dbg(1, debug, sd, + "tda9873_setaudmode(): req. mode %d; chip_write: %d\n", mode, sw_data); } @@ -859,13 +896,13 @@ static int tda9874a_setup(struct CHIPSTATE *chip) return 1; } -static int tda9874a_getmode(struct CHIPSTATE *chip) +static int tda9874a_getrxsubchans(struct CHIPSTATE *chip) { struct v4l2_subdev *sd = &chip->sd; int dsr,nsr,mode; int necr; /* just for debugging */ - mode = V4L2_TUNER_MODE_MONO; + mode = V4L2_TUNER_SUB_MONO; if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR))) return mode; @@ -888,22 +925,23 @@ static int tda9874a_getmode(struct CHIPSTATE *chip) * external 4052 multiplexer in audio_hook(). */ if(nsr & 0x02) /* NSR.S/MB=1 */ - mode |= V4L2_TUNER_MODE_STEREO; + mode = V4L2_TUNER_SUB_STEREO; if(nsr & 0x01) /* NSR.D/SB=1 */ - mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; } else { if(dsr & 0x02) /* DSR.IDSTE=1 */ - mode |= V4L2_TUNER_MODE_STEREO; + mode = V4L2_TUNER_SUB_STEREO; if(dsr & 0x04) /* DSR.IDDUA=1 */ - mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; } - v4l2_dbg(1, debug, sd, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", + v4l2_dbg(1, debug, sd, + "tda9874a_getrxsubchans(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", dsr, nsr, necr, mode); return mode; } -static void tda9874a_setmode(struct CHIPSTATE *chip, int mode) +static void tda9874a_setaudmode(struct CHIPSTATE *chip, int mode) { struct v4l2_subdev *sd = &chip->sd; @@ -939,14 +977,18 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode) aosr = 0xa0; /* auto-select, dual B/B */ mdacosr = (tda9874a_mode) ? 0x83:0x81; break; + case V4L2_TUNER_MODE_LANG1_LANG2: + aosr = 0x00; /* always route L to L and R to R */ + mdacosr = (tda9874a_mode) ? 0x82:0x80; + break; default: - chip->mode = 0; return; } chip_write(chip, TDA9874A_AOSR, aosr); chip_write(chip, TDA9874A_MDACOSR, mdacosr); - v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", + v4l2_dbg(1, debug, sd, + "tda9874a_setaudmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", mode, aosr, mdacosr); } else { /* dic == 0x07 */ @@ -974,14 +1016,18 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode) fmmr = 0x02; /* dual */ aosr = 0x20; /* dual B/B */ break; + case V4L2_TUNER_MODE_LANG1_LANG2: + fmmr = 0x02; /* dual */ + aosr = 0x00; /* dual A/B */ + break; default: - chip->mode = 0; return; } chip_write(chip, TDA9874A_FMMR, fmmr); chip_write(chip, TDA9874A_AOSR, aosr); - v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", + v4l2_dbg(1, debug, sd, + "tda9874a_setaudmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", mode, fmmr, aosr); } } @@ -1226,25 +1272,33 @@ static int tea6320_initialize(struct CHIPSTATE * chip) static int tda8425_shift10(int val) { return (val >> 10) | 0xc0; } static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; } -static void tda8425_setmode(struct CHIPSTATE *chip, int mode) +static void tda8425_setaudmode(struct CHIPSTATE *chip, int mode) { int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1; - if (mode & V4L2_TUNER_MODE_LANG1) { + switch (mode) { + case V4L2_TUNER_MODE_LANG1: s1 |= TDA8425_S1_ML_SOUND_A; s1 |= TDA8425_S1_STEREO_PSEUDO; - - } else if (mode & V4L2_TUNER_MODE_LANG2) { + break; + case V4L2_TUNER_MODE_LANG2: s1 |= TDA8425_S1_ML_SOUND_B; s1 |= TDA8425_S1_STEREO_PSEUDO; - - } else { + break; + case V4L2_TUNER_MODE_LANG1_LANG2: s1 |= TDA8425_S1_ML_STEREO; - - if (mode & V4L2_TUNER_MODE_MONO) - s1 |= TDA8425_S1_STEREO_MONO; - if (mode & V4L2_TUNER_MODE_STEREO) - s1 |= TDA8425_S1_STEREO_SPATIAL; + s1 |= TDA8425_S1_STEREO_LINEAR; + break; + case V4L2_TUNER_MODE_MONO: + s1 |= TDA8425_S1_ML_STEREO; + s1 |= TDA8425_S1_STEREO_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + s1 |= TDA8425_S1_ML_STEREO; + s1 |= TDA8425_S1_STEREO_SPATIAL; + break; + default: + return; } chip_write(chip,TDA8425_S1,s1); } @@ -1297,18 +1351,20 @@ static void tda8425_setmode(struct CHIPSTATE *chip, int mode) * stereo L L * BIL H L */ -static int ta8874z_getmode(struct CHIPSTATE *chip) +static int ta8874z_getrxsubchans(struct CHIPSTATE *chip) { int val, mode; val = chip_read(chip); - mode = V4L2_TUNER_MODE_MONO; + mode = V4L2_TUNER_SUB_MONO; if (val & TA8874Z_B1){ - mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; }else if (!(val & TA8874Z_B0)){ - mode |= V4L2_TUNER_MODE_STEREO; + mode = V4L2_TUNER_SUB_STEREO; } - /* v4l_dbg(1, debug, chip->c, "ta8874z_getmode(): raw chip read: 0x%02x, return: 0x%02x\n", val, mode); */ + /* v4l2_dbg(1, debug, &chip->sd, + "ta8874z_getrxsubchans(): raw chip read: 0x%02x, return: 0x%02x\n", + val, mode); */ return mode; } @@ -1316,14 +1372,15 @@ static audiocmd ta8874z_stereo = { 2, {0, TA8874Z_SEPARATION_DEFAULT}}; static audiocmd ta8874z_mono = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}; static audiocmd ta8874z_main = {2, { 0, TA8874Z_SEPARATION_DEFAULT}}; static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; +static audiocmd ta8874z_both = {2, { TA8874Z_MODE_MAIN | TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; -static void ta8874z_setmode(struct CHIPSTATE *chip, int mode) +static void ta8874z_setaudmode(struct CHIPSTATE *chip, int mode) { struct v4l2_subdev *sd = &chip->sd; int update = 1; audiocmd *t = NULL; - v4l2_dbg(1, debug, sd, "ta8874z_setmode(): mode: 0x%02x\n", mode); + v4l2_dbg(1, debug, sd, "ta8874z_setaudmode(): mode: 0x%02x\n", mode); switch(mode){ case V4L2_TUNER_MODE_MONO: @@ -1338,6 +1395,9 @@ static void ta8874z_setmode(struct CHIPSTATE *chip, int mode) case V4L2_TUNER_MODE_LANG2: t = &ta8874z_sub; break; + case V4L2_TUNER_MODE_LANG1_LANG2: + t = &ta8874z_both; + break; default: update = 0; } @@ -1394,8 +1454,8 @@ static struct CHIPDESC chiplist[] = { /* callbacks */ .checkit = tda9840_checkit, - .getmode = tda9840_getmode, - .setmode = tda9840_setmode, + .getrxsubchans = tda9840_getrxsubchans, + .setaudmode = tda9840_setaudmode, .init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN /* ,TDA9840_SW, TDA9840_MONO */} } @@ -1410,8 +1470,8 @@ static struct CHIPDESC chiplist[] = { /* callbacks */ .checkit = tda9873_checkit, - .getmode = tda9873_getmode, - .setmode = tda9873_setmode, + .getrxsubchans = tda9873_getrxsubchans, + .setaudmode = tda9873_setaudmode, .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } }, .inputreg = TDA9873_SW, @@ -1430,8 +1490,8 @@ static struct CHIPDESC chiplist[] = { /* callbacks */ .initialize = tda9874a_initialize, .checkit = tda9874a_checkit, - .getmode = tda9874a_getmode, - .setmode = tda9874a_setmode, + .getrxsubchans = tda9874a_getrxsubchans, + .setaudmode = tda9874a_setaudmode, }, { .name = "tda9875", @@ -1460,8 +1520,8 @@ static struct CHIPDESC chiplist[] = { .addr_hi = I2C_ADDR_TDA985x_H >> 1, .registers = 11, - .getmode = tda985x_getmode, - .setmode = tda985x_setmode, + .getrxsubchans = tda985x_getrxsubchans, + .setaudmode = tda985x_setaudmode, .init = { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } } }, @@ -1482,8 +1542,8 @@ static struct CHIPDESC chiplist[] = { .volfunc = tda9855_volume, .bassfunc = tda9855_bass, .treblefunc = tda9855_treble, - .getmode = tda985x_getmode, - .setmode = tda985x_setmode, + .getrxsubchans = tda985x_getrxsubchans, + .setaudmode = tda985x_setaudmode, .init = { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2, TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT, @@ -1564,7 +1624,7 @@ static struct CHIPDESC chiplist[] = { .volfunc = tda8425_shift10, .bassfunc = tda8425_shift12, .treblefunc = tda8425_shift12, - .setmode = tda8425_setmode, + .setaudmode = tda8425_setaudmode, .inputreg = TDA8425_S1, .inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 }, @@ -1593,11 +1653,10 @@ static struct CHIPDESC chiplist[] = { .addr_lo = I2C_ADDR_TDA9840 >> 1, .addr_hi = I2C_ADDR_TDA9840 >> 1, .registers = 2, - .flags = CHIP_NEED_CHECKMODE, /* callbacks */ - .getmode = ta8874z_getmode, - .setmode = ta8874z_setmode, + .getrxsubchans = ta8874z_getrxsubchans, + .setaudmode = ta8874z_setaudmode, .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}, }, @@ -1736,7 +1795,6 @@ static int tvaudio_s_radio(struct v4l2_subdev *sd) struct CHIPSTATE *chip = to_state(sd); chip->radio = 1; - chip->watch_stereo = 0; /* del_timer(&chip->wt); */ return 0; } @@ -1793,9 +1851,8 @@ static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) { struct CHIPSTATE *chip = to_state(sd); struct CHIPDESC *desc = chip->desc; - int mode = 0; - if (!desc->setmode) + if (!desc->setaudmode) return 0; if (chip->radio) return 0; @@ -1805,22 +1862,18 @@ static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) case V4L2_TUNER_MODE_STEREO: case V4L2_TUNER_MODE_LANG1: case V4L2_TUNER_MODE_LANG2: - mode = vt->audmode; - break; case V4L2_TUNER_MODE_LANG1_LANG2: - mode = V4L2_TUNER_MODE_STEREO; break; default: return -EINVAL; } chip->audmode = vt->audmode; - if (mode) { - chip->watch_stereo = 0; - /* del_timer(&chip->wt); */ - chip->mode = mode; - desc->setmode(chip, mode); - } + if (chip->thread) + wake_up_process(chip->thread); + else + desc->setaudmode(chip, vt->audmode); + return 0; } @@ -1828,30 +1881,17 @@ static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) { struct CHIPSTATE *chip = to_state(sd); struct CHIPDESC *desc = chip->desc; - int mode = V4L2_TUNER_MODE_MONO; - if (!desc->getmode) + if (!desc->getrxsubchans) return 0; if (chip->radio) return 0; vt->audmode = chip->audmode; - vt->rxsubchans = 0; + vt->rxsubchans = desc->getrxsubchans(chip); vt->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - mode = desc->getmode(chip); - - if (mode & V4L2_TUNER_MODE_MONO) - vt->rxsubchans |= V4L2_TUNER_SUB_MONO; - if (mode & V4L2_TUNER_MODE_STEREO) - vt->rxsubchans |= V4L2_TUNER_SUB_STEREO; - /* Note: for SAP it should be mono/lang2 or stereo/lang2. - When this module is converted fully to v4l2, then this - should change for those chips that can detect SAP. */ - if (mode & V4L2_TUNER_MODE_LANG1) - vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | - V4L2_TUNER_SUB_LANG2; return 0; } @@ -1868,9 +1908,7 @@ static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fr struct CHIPSTATE *chip = to_state(sd); struct CHIPDESC *desc = chip->desc; - chip->mode = 0; /* automatic */ - - /* For chips that provide getmode and setmode, and doesn't + /* For chips that provide getrxsubchans and setaudmode, and doesn't automatically follows the stereo carrier, a kthread is created to set the audio standard. In this case, when then the video channel is changed, tvaudio starts on MONO mode. @@ -1879,9 +1917,8 @@ static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fr audio carrier. */ if (chip->thread) { - desc->setmode(chip, V4L2_TUNER_MODE_MONO); - if (chip->prevmode != V4L2_TUNER_MODE_MONO) - chip->prevmode = -1; /* reset previous mode */ + desc->setaudmode(chip, V4L2_TUNER_MODE_MONO); + chip->prevmode = -1; /* reset previous mode */ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); } return 0; @@ -2023,7 +2060,7 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * chip->thread = NULL; init_timer(&chip->wt); if (desc->flags & CHIP_NEED_CHECKMODE) { - if (!desc->getmode || !desc->setmode) { + if (!desc->getrxsubchans || !desc->setaudmode) { /* This shouldn't be happen. Warn user, but keep working without kthread */ diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index b7867427e5c4..0d897cb1774a 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -61,13 +61,20 @@ static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) int rc; buffer[0] = addr; - if (1 != (rc = i2c_master_send(c, buffer, 1))) - v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc); + + rc = i2c_master_send(c, buffer, 1); + if (rc < 0) { + v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); + return rc; + } msleep(10); - if (1 != (rc = i2c_master_recv(c, buffer, 1))) - v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc); + rc = i2c_master_recv(c, buffer, 1); + if (rc < 0) { + v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); + return rc; + } v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); @@ -279,6 +286,11 @@ static inline void tvp5150_selmux(struct v4l2_subdev *sd) * For Composite and TV, it should be the reverse */ val = tvp5150_read(sd, TVP5150_MISC_CTL); + if (val < 0) { + v4l2_err(sd, "%s: failed with error = %d\n", __func__, val); + return; + } + if (decoder->input == TVP5150_SVIDEO) val = (val & ~0x40) | 0x10; else @@ -676,6 +688,7 @@ static int tvp5150_get_vbi(struct v4l2_subdev *sd, v4l2_std_id std = decoder->norm; u8 reg; int pos, type = 0; + int i, ret = 0; if (std == V4L2_STD_ALL) { v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); @@ -690,13 +703,17 @@ static int tvp5150_get_vbi(struct v4l2_subdev *sd, reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI; - pos = tvp5150_read(sd, reg) & 0x0f; - if (pos < 0x0f) - type = regs[pos].type.vbi_type; - - pos = tvp5150_read(sd, reg + 1) & 0x0f; - if (pos < 0x0f) - type |= regs[pos].type.vbi_type; + for (i = 0; i <= 1; i++) { + ret = tvp5150_read(sd, reg + i); + if (ret < 0) { + v4l2_err(sd, "%s: failed with error = %d\n", + __func__, ret); + return 0; + } + pos = ret & 0x0f; + if (pos < 0x0f) + type |= regs[pos].type.vbi_type; + } return type; } @@ -1031,13 +1048,21 @@ static int tvp5150_g_chip_ident(struct v4l2_subdev *sd, #ifdef CONFIG_VIDEO_ADV_DEBUG static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { + int res; + struct i2c_client *client = v4l2_get_subdevdata(sd); if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - reg->val = tvp5150_read(sd, reg->reg & 0xff); + res = tvp5150_read(sd, reg->reg & 0xff); + if (res < 0) { + v4l2_err(sd, "%s: failed with error = %d\n", __func__, res); + return res; + } + + reg->val = res; reg->size = 1; return 0; } @@ -1126,7 +1151,8 @@ static int tvp5150_probe(struct i2c_client *c, { struct tvp5150 *core; struct v4l2_subdev *sd; - u8 msb_id, lsb_id, msb_rom, lsb_rom; + int tvp5150_id[4]; + int i, res; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(c->adapter, @@ -1139,26 +1165,37 @@ static int tvp5150_probe(struct i2c_client *c, } sd = &core->sd; v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); + + /* + * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID, + * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER + */ + for (i = 0; i < 4; i++) { + res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i); + if (res < 0) + goto free_core; + tvp5150_id[i] = res; + } + v4l_info(c, "chip found @ 0x%02x (%s)\n", c->addr << 1, c->adapter->name); - msb_id = tvp5150_read(sd, TVP5150_MSB_DEV_ID); - lsb_id = tvp5150_read(sd, TVP5150_LSB_DEV_ID); - msb_rom = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER); - lsb_rom = tvp5150_read(sd, TVP5150_ROM_MINOR_VER); - - if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */ - v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id); + if (tvp5150_id[2] == 4 && tvp5150_id[3] == 0) { /* Is TVP5150AM1 */ + v4l2_info(sd, "tvp%02x%02xam1 detected.\n", + tvp5150_id[0], tvp5150_id[1]); /* ITU-T BT.656.4 timing */ tvp5150_write(sd, TVP5150_REV_SELECT, 0); } else { - if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */ - v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id); + /* Is TVP5150A */ + if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) { + v4l2_info(sd, "tvp%02x%02xa detected.\n", + tvp5150_id[2], tvp5150_id[3]); } else { v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n", - msb_id, lsb_id); - v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom); + tvp5150_id[2], tvp5150_id[3]); + v4l2_info(sd, "*** Rom ver is %d.%d\n", + tvp5150_id[2], tvp5150_id[3]); } } @@ -1177,11 +1214,9 @@ static int tvp5150_probe(struct i2c_client *c, V4L2_CID_HUE, -128, 127, 1, 0); sd->ctrl_handler = &core->hdl; if (core->hdl.error) { - int err = core->hdl.error; - + res = core->hdl.error; v4l2_ctrl_handler_free(&core->hdl); - kfree(core); - return err; + goto free_core; } v4l2_ctrl_handler_setup(&core->hdl); @@ -1197,6 +1232,10 @@ static int tvp5150_probe(struct i2c_client *c, if (debug > 1) tvp5150_log_status(sd); return 0; + +free_core: + kfree(core); + return res; } static int tvp5150_remove(struct i2c_client *c) diff --git a/drivers/media/video/uvc/Kconfig b/drivers/media/video/uvc/Kconfig index 6c197da531b2..541c9f1e4c6a 100644 --- a/drivers/media/video/uvc/Kconfig +++ b/drivers/media/video/uvc/Kconfig @@ -10,6 +10,7 @@ config USB_VIDEO_CLASS config USB_VIDEO_CLASS_INPUT_EVDEV bool "UVC input events device support" default y + depends on USB_VIDEO_CLASS depends on USB_VIDEO_CLASS=INPUT || INPUT=y ---help--- This option makes USB Video Class devices register an input device diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index af26bbe6f76e..f7061a5ef1d2 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -2083,7 +2083,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev) /* Walk the entities list and instantiate controls */ list_for_each_entry(entity, &dev->entities, list) { struct uvc_control *ctrl; - unsigned int bControlSize = 0, ncontrols = 0; + unsigned int bControlSize = 0, ncontrols; __u8 *bmControls = NULL; if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) { @@ -2101,8 +2101,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev) uvc_ctrl_prune_entity(dev, entity); /* Count supported controls and allocate the controls array */ - for (i = 0; i < bControlSize; ++i) - ncontrols += hweight8(bmControls[i]); + ncontrols = memweight(bmControls, bControlSize); if (ncontrols == 0) continue; diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 759bef8897e9..f00db3060e0e 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -1051,7 +1051,7 @@ static long uvc_v4l2_ioctl(struct file *file, { if (uvc_trace_param & UVC_TRACE_IOCTL) { uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl("); - v4l_printk_ioctl(cmd); + v4l_printk_ioctl(NULL, cmd); printk(")\n"); } diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index b76b0ac0958f..7ac4347ca09e 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -1188,7 +1188,11 @@ static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream, u8 *mem; int len, ret; - if (urb->actual_length == 0) + /* + * Ignore ZLPs if they're not part of a frame, otherwise process them + * to trigger the end of payload detection. + */ + if (urb->actual_length == 0 && stream->bulk.header_size == 0) return; mem = urb->transfer_buffer; @@ -1594,7 +1598,7 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) psize = le16_to_cpu(ep->desc.wMaxPacketSize); psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); if (psize >= bandwidth && psize <= best_psize) { - altsetting = i; + altsetting = alts->desc.bAlternateSetting; best_psize = psize; best_ep = ep; } diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 5327ad3a6390..ac365cfb3706 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -327,7 +327,7 @@ struct v4l2_buffer32 { compat_caddr_t planes; } m; __u32 length; - __u32 input; + __u32 reserved2; __u32 reserved; }; @@ -387,8 +387,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user get_user(kp->index, &up->index) || get_user(kp->type, &up->type) || get_user(kp->flags, &up->flags) || - get_user(kp->memory, &up->memory) || - get_user(kp->input, &up->input)) + get_user(kp->memory, &up->memory)) return -EFAULT; if (V4L2_TYPE_IS_OUTPUT(kp->type)) @@ -472,8 +471,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user put_user(kp->index, &up->index) || put_user(kp->type, &up->type) || put_user(kp->flags, &up->flags) || - put_user(kp->memory, &up->memory) || - put_user(kp->input, &up->input)) + put_user(kp->memory, &up->memory)) return -EFAULT; if (put_user(kp->bytesused, &up->bytesused) || @@ -482,6 +480,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) || copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) || put_user(kp->sequence, &up->sequence) || + put_user(kp->reserved2, &up->reserved2) || put_user(kp->reserved, &up->reserved)) return -EFAULT; diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 0cbada18f6f5..af70f931727c 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -46,6 +46,29 @@ static ssize_t show_index(struct device *cd, return sprintf(buf, "%i\n", vdev->index); } +static ssize_t show_debug(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = to_video_device(cd); + + return sprintf(buf, "%i\n", vdev->debug); +} + +static ssize_t set_debug(struct device *cd, struct device_attribute *attr, + const char *buf, size_t len) +{ + struct video_device *vdev = to_video_device(cd); + int res = 0; + u16 value; + + res = kstrtou16(buf, 0, &value); + if (res) + return res; + + vdev->debug = value; + return len; +} + static ssize_t show_name(struct device *cd, struct device_attribute *attr, char *buf) { @@ -56,6 +79,7 @@ static ssize_t show_name(struct device *cd, static struct device_attribute video_device_attrs[] = { __ATTR(name, S_IRUGO, show_name, NULL), + __ATTR(debug, 0644, show_debug, set_debug), __ATTR(index, S_IRUGO, show_index, NULL), __ATTR_NULL }; @@ -281,6 +305,9 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf, ret = vdev->fops->read(filp, buf, sz, off); if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); + if (vdev->debug) + printk(KERN_DEBUG "%s: read: %zd (%d)\n", + video_device_node_name(vdev), sz, ret); return ret; } @@ -299,6 +326,9 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf, ret = vdev->fops->write(filp, buf, sz, off); if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); + if (vdev->debug) + printk(KERN_DEBUG "%s: write: %zd (%d)\n", + video_device_node_name(vdev), sz, ret); return ret; } @@ -315,6 +345,9 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) ret = vdev->fops->poll(filp, poll); if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); + if (vdev->debug) + printk(KERN_DEBUG "%s: poll: %08x\n", + video_device_node_name(vdev), ret); return ret; } @@ -324,20 +357,14 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) int ret = -ENODEV; if (vdev->fops->unlocked_ioctl) { - bool locked = false; + struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd); - if (vdev->lock) { - /* always lock unless the cmd is marked as "don't use lock" */ - locked = !v4l2_is_known_ioctl(cmd) || - !test_bit(_IOC_NR(cmd), vdev->disable_locking); - - if (locked && mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; - } + if (lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); - if (locked) - mutex_unlock(vdev->lock); + if (lock) + mutex_unlock(lock); } else if (vdev->fops->ioctl) { /* This code path is a replacement for the BKL. It is a major * hack but it will have to do for those drivers that are not @@ -385,12 +412,17 @@ static unsigned long v4l2_get_unmapped_area(struct file *filp, unsigned long flags) { struct video_device *vdev = video_devdata(filp); + int ret; if (!vdev->fops->get_unmapped_area) return -ENOSYS; if (!video_is_registered(vdev)) return -ENODEV; - return vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags); + ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags); + if (vdev->debug) + printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n", + video_device_node_name(vdev), ret); + return ret; } #endif @@ -408,6 +440,9 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) ret = vdev->fops->mmap(filp, vm); if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); + if (vdev->debug) + printk(KERN_DEBUG "%s: mmap (%d)\n", + video_device_node_name(vdev), ret); return ret; } @@ -446,6 +481,9 @@ err: /* decrease the refcount in case of an error */ if (ret) video_put(vdev); + if (vdev->debug) + printk(KERN_DEBUG "%s: open (%d)\n", + video_device_node_name(vdev), ret); return ret; } @@ -465,6 +503,9 @@ static int v4l2_release(struct inode *inode, struct file *filp) /* decrease the refcount unconditionally since the release() return value is ignored. */ video_put(vdev); + if (vdev->debug) + printk(KERN_DEBUG "%s: release\n", + video_device_node_name(vdev)); return ret; } diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index d7fa8962d8b3..70e0efb127a6 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -27,27 +27,7 @@ #include <media/v4l2-event.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> - -#define dbgarg(cmd, fmt, arg...) \ - do { \ - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { \ - printk(KERN_DEBUG "%s: ", vfd->name); \ - v4l_printk_ioctl(cmd); \ - printk(" " fmt, ## arg); \ - } \ - } while (0) - -#define dbgarg2(fmt, arg...) \ - do { \ - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ - printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\ - } while (0) - -#define dbgarg3(fmt, arg...) \ - do { \ - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ - printk(KERN_CONT "%s: " fmt, vfd->name, ## arg);\ - } while (0) +#include <media/videobuf2-core.h> /* Zero out the end of the struct pointed to by p. Everything after, but * not including, the specified field is cleared. */ @@ -183,207 +163,507 @@ static const char *v4l2_memory_names[] = { /* ------------------------------------------------------------------ */ /* debug help functions */ -struct v4l2_ioctl_info { - unsigned int ioctl; - u16 flags; - const char * const name; -}; +static void v4l_print_querycap(const void *arg, bool write_only) +{ + const struct v4l2_capability *p = arg; -/* This control needs a priority check */ -#define INFO_FL_PRIO (1 << 0) -/* This control can be valid if the filehandle passes a control handler. */ -#define INFO_FL_CTRL (1 << 1) + pr_cont("driver=%s, card=%s, bus=%s, version=0x%08x, " + "capabilities=0x%08x, device_caps=0x%08x\n", + p->driver, p->card, p->bus_info, + p->version, p->capabilities, p->device_caps); +} -#define IOCTL_INFO(_ioctl, _flags) [_IOC_NR(_ioctl)] = { \ - .ioctl = _ioctl, \ - .flags = _flags, \ - .name = #_ioctl, \ +static void v4l_print_enuminput(const void *arg, bool write_only) +{ + const struct v4l2_input *p = arg; + + pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, tuner=%u, " + "std=0x%08Lx, status=0x%x, capabilities=0x%x\n", + p->index, p->name, p->type, p->audioset, p->tuner, + (unsigned long long)p->std, p->status, p->capabilities); } -static struct v4l2_ioctl_info v4l2_ioctls[] = { - IOCTL_INFO(VIDIOC_QUERYCAP, 0), - IOCTL_INFO(VIDIOC_ENUM_FMT, 0), - IOCTL_INFO(VIDIOC_G_FMT, 0), - IOCTL_INFO(VIDIOC_S_FMT, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_REQBUFS, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_QUERYBUF, 0), - IOCTL_INFO(VIDIOC_G_FBUF, 0), - IOCTL_INFO(VIDIOC_S_FBUF, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_OVERLAY, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_QBUF, 0), - IOCTL_INFO(VIDIOC_DQBUF, 0), - IOCTL_INFO(VIDIOC_STREAMON, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_STREAMOFF, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_G_PARM, 0), - IOCTL_INFO(VIDIOC_S_PARM, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_G_STD, 0), - IOCTL_INFO(VIDIOC_S_STD, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_ENUMSTD, 0), - IOCTL_INFO(VIDIOC_ENUMINPUT, 0), - IOCTL_INFO(VIDIOC_G_CTRL, INFO_FL_CTRL), - IOCTL_INFO(VIDIOC_S_CTRL, INFO_FL_PRIO | INFO_FL_CTRL), - IOCTL_INFO(VIDIOC_G_TUNER, 0), - IOCTL_INFO(VIDIOC_S_TUNER, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_G_AUDIO, 0), - IOCTL_INFO(VIDIOC_S_AUDIO, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_QUERYCTRL, INFO_FL_CTRL), - IOCTL_INFO(VIDIOC_QUERYMENU, INFO_FL_CTRL), - IOCTL_INFO(VIDIOC_G_INPUT, 0), - IOCTL_INFO(VIDIOC_S_INPUT, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_G_OUTPUT, 0), - IOCTL_INFO(VIDIOC_S_OUTPUT, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_ENUMOUTPUT, 0), - IOCTL_INFO(VIDIOC_G_AUDOUT, 0), - IOCTL_INFO(VIDIOC_S_AUDOUT, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_G_MODULATOR, 0), - IOCTL_INFO(VIDIOC_S_MODULATOR, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_G_FREQUENCY, 0), - IOCTL_INFO(VIDIOC_S_FREQUENCY, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_CROPCAP, 0), - IOCTL_INFO(VIDIOC_G_CROP, 0), - IOCTL_INFO(VIDIOC_S_CROP, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_G_SELECTION, 0), - IOCTL_INFO(VIDIOC_S_SELECTION, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_G_JPEGCOMP, 0), - IOCTL_INFO(VIDIOC_S_JPEGCOMP, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_QUERYSTD, 0), - IOCTL_INFO(VIDIOC_TRY_FMT, 0), - IOCTL_INFO(VIDIOC_ENUMAUDIO, 0), - IOCTL_INFO(VIDIOC_ENUMAUDOUT, 0), - IOCTL_INFO(VIDIOC_G_PRIORITY, 0), - IOCTL_INFO(VIDIOC_S_PRIORITY, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_G_SLICED_VBI_CAP, 0), - IOCTL_INFO(VIDIOC_LOG_STATUS, 0), - IOCTL_INFO(VIDIOC_G_EXT_CTRLS, INFO_FL_CTRL), - IOCTL_INFO(VIDIOC_S_EXT_CTRLS, INFO_FL_PRIO | INFO_FL_CTRL), - IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS, 0), - IOCTL_INFO(VIDIOC_ENUM_FRAMESIZES, 0), - IOCTL_INFO(VIDIOC_ENUM_FRAMEINTERVALS, 0), - IOCTL_INFO(VIDIOC_G_ENC_INDEX, 0), - IOCTL_INFO(VIDIOC_ENCODER_CMD, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_TRY_ENCODER_CMD, 0), - IOCTL_INFO(VIDIOC_DECODER_CMD, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_TRY_DECODER_CMD, 0), -#ifdef CONFIG_VIDEO_ADV_DEBUG - IOCTL_INFO(VIDIOC_DBG_S_REGISTER, 0), - IOCTL_INFO(VIDIOC_DBG_G_REGISTER, 0), -#endif - IOCTL_INFO(VIDIOC_DBG_G_CHIP_IDENT, 0), - IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_ENUM_DV_PRESETS, 0), - IOCTL_INFO(VIDIOC_S_DV_PRESET, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_G_DV_PRESET, 0), - IOCTL_INFO(VIDIOC_QUERY_DV_PRESET, 0), - IOCTL_INFO(VIDIOC_S_DV_TIMINGS, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_G_DV_TIMINGS, 0), - IOCTL_INFO(VIDIOC_DQEVENT, 0), - IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT, 0), - IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, 0), - IOCTL_INFO(VIDIOC_CREATE_BUFS, INFO_FL_PRIO), - IOCTL_INFO(VIDIOC_PREPARE_BUF, 0), - IOCTL_INFO(VIDIOC_ENUM_DV_TIMINGS, 0), - IOCTL_INFO(VIDIOC_QUERY_DV_TIMINGS, 0), - IOCTL_INFO(VIDIOC_DV_TIMINGS_CAP, 0), -}; -#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) +static void v4l_print_enumoutput(const void *arg, bool write_only) +{ + const struct v4l2_output *p = arg; -bool v4l2_is_known_ioctl(unsigned int cmd) + pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, " + "modulator=%u, std=0x%08Lx, capabilities=0x%x\n", + p->index, p->name, p->type, p->audioset, p->modulator, + (unsigned long long)p->std, p->capabilities); +} + +static void v4l_print_audio(const void *arg, bool write_only) { - if (_IOC_NR(cmd) >= V4L2_IOCTLS) - return false; - return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; + const struct v4l2_audio *p = arg; + + if (write_only) + pr_cont("index=%u, mode=0x%x\n", p->index, p->mode); + else + pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n", + p->index, p->name, p->capability, p->mode); } -/* Common ioctl debug function. This function can be used by - external ioctl messages as well as internal V4L ioctl */ -void v4l_printk_ioctl(unsigned int cmd) +static void v4l_print_audioout(const void *arg, bool write_only) { - char *dir, *type; + const struct v4l2_audioout *p = arg; - switch (_IOC_TYPE(cmd)) { - case 'd': - type = "v4l2_int"; + if (write_only) + pr_cont("index=%u\n", p->index); + else + pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n", + p->index, p->name, p->capability, p->mode); +} + +static void v4l_print_fmtdesc(const void *arg, bool write_only) +{ + const struct v4l2_fmtdesc *p = arg; + + pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%s'\n", + p->index, prt_names(p->type, v4l2_type_names), + p->flags, (p->pixelformat & 0xff), + (p->pixelformat >> 8) & 0xff, + (p->pixelformat >> 16) & 0xff, + (p->pixelformat >> 24) & 0xff, + p->description); +} + +static void v4l_print_format(const void *arg, bool write_only) +{ + const struct v4l2_format *p = arg; + const struct v4l2_pix_format *pix; + const struct v4l2_pix_format_mplane *mp; + const struct v4l2_vbi_format *vbi; + const struct v4l2_sliced_vbi_format *sliced; + const struct v4l2_window *win; + const struct v4l2_clip *clip; + unsigned i; + + pr_cont("type=%s", prt_names(p->type, v4l2_type_names)); + switch (p->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + pix = &p->fmt.pix; + pr_cont(", width=%u, height=%u, " + "pixelformat=%c%c%c%c, field=%s, " + "bytesperline=%u sizeimage=%u, colorspace=%d\n", + pix->width, pix->height, + (pix->pixelformat & 0xff), + (pix->pixelformat >> 8) & 0xff, + (pix->pixelformat >> 16) & 0xff, + (pix->pixelformat >> 24) & 0xff, + prt_names(pix->field, v4l2_field_names), + pix->bytesperline, pix->sizeimage, + pix->colorspace); break; - case 'V': - if (_IOC_NR(cmd) >= V4L2_IOCTLS) { - type = "v4l2"; - break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + mp = &p->fmt.pix_mp; + pr_cont(", width=%u, height=%u, " + "format=%c%c%c%c, field=%s, " + "colorspace=%d, num_planes=%u\n", + mp->width, mp->height, + (mp->pixelformat & 0xff), + (mp->pixelformat >> 8) & 0xff, + (mp->pixelformat >> 16) & 0xff, + (mp->pixelformat >> 24) & 0xff, + prt_names(mp->field, v4l2_field_names), + mp->colorspace, mp->num_planes); + for (i = 0; i < mp->num_planes; i++) + printk(KERN_DEBUG "plane %u: bytesperline=%u sizeimage=%u\n", i, + mp->plane_fmt[i].bytesperline, + mp->plane_fmt[i].sizeimage); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + win = &p->fmt.win; + pr_cont(", wxh=%dx%d, x,y=%d,%d, field=%s, " + "chromakey=0x%08x, bitmap=%p, " + "global_alpha=0x%02x\n", + win->w.width, win->w.height, + win->w.left, win->w.top, + prt_names(win->field, v4l2_field_names), + win->chromakey, win->bitmap, win->global_alpha); + clip = win->clips; + for (i = 0; i < win->clipcount; i++) { + printk(KERN_DEBUG "clip %u: wxh=%dx%d, x,y=%d,%d\n", + i, clip->c.width, clip->c.height, + clip->c.left, clip->c.top); + clip = clip->next; } - printk("%s", v4l2_ioctls[_IOC_NR(cmd)].name); - return; - default: - type = "unknown"; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + vbi = &p->fmt.vbi; + pr_cont(", sampling_rate=%u, offset=%u, samples_per_line=%u, " + "sample_format=%c%c%c%c, start=%u,%u, count=%u,%u\n", + vbi->sampling_rate, vbi->offset, + vbi->samples_per_line, + (vbi->sample_format & 0xff), + (vbi->sample_format >> 8) & 0xff, + (vbi->sample_format >> 16) & 0xff, + (vbi->sample_format >> 24) & 0xff, + vbi->start[0], vbi->start[1], + vbi->count[0], vbi->count[1]); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + sliced = &p->fmt.sliced; + pr_cont(", service_set=0x%08x, io_size=%d\n", + sliced->service_set, sliced->io_size); + for (i = 0; i < 24; i++) + printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i, + sliced->service_lines[0][i], + sliced->service_lines[1][i]); + break; + case V4L2_BUF_TYPE_PRIVATE: + pr_cont("\n"); + break; } +} - switch (_IOC_DIR(cmd)) { - case _IOC_NONE: dir = "--"; break; - case _IOC_READ: dir = "r-"; break; - case _IOC_WRITE: dir = "-w"; break; - case _IOC_READ | _IOC_WRITE: dir = "rw"; break; - default: dir = "*ERR*"; break; - } - printk("%s ioctl '%c', dir=%s, #%d (0x%08x)", - type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd); +static void v4l_print_framebuffer(const void *arg, bool write_only) +{ + const struct v4l2_framebuffer *p = arg; + + pr_cont("capability=0x%x, flags=0x%x, base=0x%p, width=%u, " + "height=%u, pixelformat=%c%c%c%c, " + "bytesperline=%u sizeimage=%u, colorspace=%d\n", + p->capability, p->flags, p->base, + p->fmt.width, p->fmt.height, + (p->fmt.pixelformat & 0xff), + (p->fmt.pixelformat >> 8) & 0xff, + (p->fmt.pixelformat >> 16) & 0xff, + (p->fmt.pixelformat >> 24) & 0xff, + p->fmt.bytesperline, p->fmt.sizeimage, + p->fmt.colorspace); +} + +static void v4l_print_buftype(const void *arg, bool write_only) +{ + pr_cont("type=%s\n", prt_names(*(u32 *)arg, v4l2_type_names)); +} + +static void v4l_print_modulator(const void *arg, bool write_only) +{ + const struct v4l2_modulator *p = arg; + + if (write_only) + pr_cont("index=%u, txsubchans=0x%x", p->index, p->txsubchans); + else + pr_cont("index=%u, name=%s, capability=0x%x, " + "rangelow=%u, rangehigh=%u, txsubchans=0x%x\n", + p->index, p->name, p->capability, + p->rangelow, p->rangehigh, p->txsubchans); +} + +static void v4l_print_tuner(const void *arg, bool write_only) +{ + const struct v4l2_tuner *p = arg; + + if (write_only) + pr_cont("index=%u, audmode=%u\n", p->index, p->audmode); + else + pr_cont("index=%u, name=%s, type=%u, capability=0x%x, " + "rangelow=%u, rangehigh=%u, signal=%u, afc=%d, " + "rxsubchans=0x%x, audmode=%u\n", + p->index, p->name, p->type, + p->capability, p->rangelow, + p->rangehigh, p->signal, p->afc, + p->rxsubchans, p->audmode); +} + +static void v4l_print_frequency(const void *arg, bool write_only) +{ + const struct v4l2_frequency *p = arg; + + pr_cont("tuner=%u, type=%u, frequency=%u\n", + p->tuner, p->type, p->frequency); +} + +static void v4l_print_standard(const void *arg, bool write_only) +{ + const struct v4l2_standard *p = arg; + + pr_cont("index=%u, id=0x%Lx, name=%s, fps=%u/%u, " + "framelines=%u\n", p->index, + (unsigned long long)p->id, p->name, + p->frameperiod.numerator, + p->frameperiod.denominator, + p->framelines); +} + +static void v4l_print_std(const void *arg, bool write_only) +{ + pr_cont("std=0x%08Lx\n", *(const long long unsigned *)arg); } -EXPORT_SYMBOL(v4l_printk_ioctl); -static void dbgbuf(unsigned int cmd, struct video_device *vfd, - struct v4l2_buffer *p) +static void v4l_print_hw_freq_seek(const void *arg, bool write_only) { - struct v4l2_timecode *tc = &p->timecode; - struct v4l2_plane *plane; + const struct v4l2_hw_freq_seek *p = arg; + + pr_cont("tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u\n", + p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing); +} + +static void v4l_print_requestbuffers(const void *arg, bool write_only) +{ + const struct v4l2_requestbuffers *p = arg; + + pr_cont("count=%d, type=%s, memory=%s\n", + p->count, + prt_names(p->type, v4l2_type_names), + prt_names(p->memory, v4l2_memory_names)); +} + +static void v4l_print_buffer(const void *arg, bool write_only) +{ + const struct v4l2_buffer *p = arg; + const struct v4l2_timecode *tc = &p->timecode; + const struct v4l2_plane *plane; int i; - dbgarg(cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, " - "flags=0x%08d, field=%0d, sequence=%d, memory=%s\n", + pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, " + "flags=0x%08x, field=%s, sequence=%d, memory=%s", p->timestamp.tv_sec / 3600, (int)(p->timestamp.tv_sec / 60) % 60, (int)(p->timestamp.tv_sec % 60), (long)p->timestamp.tv_usec, p->index, prt_names(p->type, v4l2_type_names), - p->flags, p->field, p->sequence, - prt_names(p->memory, v4l2_memory_names)); + p->flags, prt_names(p->field, v4l2_field_names), + p->sequence, prt_names(p->memory, v4l2_memory_names)); if (V4L2_TYPE_IS_MULTIPLANAR(p->type) && p->m.planes) { + pr_cont("\n"); for (i = 0; i < p->length; ++i) { plane = &p->m.planes[i]; - dbgarg2("plane %d: bytesused=%d, data_offset=0x%08x " - "offset/userptr=0x%08lx, length=%d\n", + printk(KERN_DEBUG + "plane %d: bytesused=%d, data_offset=0x%08x " + "offset/userptr=0x%lx, length=%d\n", i, plane->bytesused, plane->data_offset, plane->m.userptr, plane->length); } } else { - dbgarg2("bytesused=%d, offset/userptr=0x%08lx, length=%d\n", + pr_cont("bytesused=%d, offset/userptr=0x%lx, length=%d\n", p->bytesused, p->m.userptr, p->length); } - dbgarg2("timecode=%02d:%02d:%02d type=%d, " - "flags=0x%08d, frames=%d, userbits=0x%08x\n", + printk(KERN_DEBUG "timecode=%02d:%02d:%02d type=%d, " + "flags=0x%08x, frames=%d, userbits=0x%08x\n", tc->hours, tc->minutes, tc->seconds, tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits); } -static inline void dbgrect(struct video_device *vfd, char *s, - struct v4l2_rect *r) +static void v4l_print_create_buffers(const void *arg, bool write_only) { - dbgarg2("%sRect start at %dx%d, size=%dx%d\n", s, r->left, r->top, - r->width, r->height); -}; + const struct v4l2_create_buffers *p = arg; + + pr_cont("index=%d, count=%d, memory=%s, ", + p->index, p->count, + prt_names(p->memory, v4l2_memory_names)); + v4l_print_format(&p->format, write_only); +} -static void dbgtimings(struct video_device *vfd, - const struct v4l2_dv_timings *p) +static void v4l_print_streamparm(const void *arg, bool write_only) { + const struct v4l2_streamparm *p = arg; + + pr_cont("type=%s", prt_names(p->type, v4l2_type_names)); + + if (p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE || + p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + const struct v4l2_captureparm *c = &p->parm.capture; + + pr_cont(", capability=0x%x, capturemode=0x%x, timeperframe=%d/%d, " + "extendedmode=%d, readbuffers=%d\n", + c->capability, c->capturemode, + c->timeperframe.numerator, c->timeperframe.denominator, + c->extendedmode, c->readbuffers); + } else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT || + p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + const struct v4l2_outputparm *c = &p->parm.output; + + pr_cont(", capability=0x%x, outputmode=0x%x, timeperframe=%d/%d, " + "extendedmode=%d, writebuffers=%d\n", + c->capability, c->outputmode, + c->timeperframe.numerator, c->timeperframe.denominator, + c->extendedmode, c->writebuffers); + } +} + +static void v4l_print_queryctrl(const void *arg, bool write_only) +{ + const struct v4l2_queryctrl *p = arg; + + pr_cont("id=0x%x, type=%d, name=%s, min/max=%d/%d, " + "step=%d, default=%d, flags=0x%08x\n", + p->id, p->type, p->name, + p->minimum, p->maximum, + p->step, p->default_value, p->flags); +} + +static void v4l_print_querymenu(const void *arg, bool write_only) +{ + const struct v4l2_querymenu *p = arg; + + pr_cont("id=0x%x, index=%d\n", p->id, p->index); +} + +static void v4l_print_control(const void *arg, bool write_only) +{ + const struct v4l2_control *p = arg; + + pr_cont("id=0x%x, value=%d\n", p->id, p->value); +} + +static void v4l_print_ext_controls(const void *arg, bool write_only) +{ + const struct v4l2_ext_controls *p = arg; + int i; + + pr_cont("class=0x%x, count=%d, error_idx=%d", + p->ctrl_class, p->count, p->error_idx); + for (i = 0; i < p->count; i++) { + if (p->controls[i].size) + pr_cont(", id/val=0x%x/0x%x", + p->controls[i].id, p->controls[i].value); + else + pr_cont(", id/size=0x%x/%u", + p->controls[i].id, p->controls[i].size); + } + pr_cont("\n"); +} + +static void v4l_print_cropcap(const void *arg, bool write_only) +{ + const struct v4l2_cropcap *p = arg; + + pr_cont("type=%s, bounds wxh=%dx%d, x,y=%d,%d, " + "defrect wxh=%dx%d, x,y=%d,%d\n, " + "pixelaspect %d/%d\n", + prt_names(p->type, v4l2_type_names), + p->bounds.width, p->bounds.height, + p->bounds.left, p->bounds.top, + p->defrect.width, p->defrect.height, + p->defrect.left, p->defrect.top, + p->pixelaspect.numerator, p->pixelaspect.denominator); +} + +static void v4l_print_crop(const void *arg, bool write_only) +{ + const struct v4l2_crop *p = arg; + + pr_cont("type=%s, wxh=%dx%d, x,y=%d,%d\n", + prt_names(p->type, v4l2_type_names), + p->c.width, p->c.height, + p->c.left, p->c.top); +} + +static void v4l_print_selection(const void *arg, bool write_only) +{ + const struct v4l2_selection *p = arg; + + pr_cont("type=%s, target=%d, flags=0x%x, wxh=%dx%d, x,y=%d,%d\n", + prt_names(p->type, v4l2_type_names), + p->target, p->flags, + p->r.width, p->r.height, p->r.left, p->r.top); +} + +static void v4l_print_jpegcompression(const void *arg, bool write_only) +{ + const struct v4l2_jpegcompression *p = arg; + + pr_cont("quality=%d, APPn=%d, APP_len=%d, " + "COM_len=%d, jpeg_markers=0x%x\n", + p->quality, p->APPn, p->APP_len, + p->COM_len, p->jpeg_markers); +} + +static void v4l_print_enc_idx(const void *arg, bool write_only) +{ + const struct v4l2_enc_idx *p = arg; + + pr_cont("entries=%d, entries_cap=%d\n", + p->entries, p->entries_cap); +} + +static void v4l_print_encoder_cmd(const void *arg, bool write_only) +{ + const struct v4l2_encoder_cmd *p = arg; + + pr_cont("cmd=%d, flags=0x%x\n", + p->cmd, p->flags); +} + +static void v4l_print_decoder_cmd(const void *arg, bool write_only) +{ + const struct v4l2_decoder_cmd *p = arg; + + pr_cont("cmd=%d, flags=0x%x\n", p->cmd, p->flags); + + if (p->cmd == V4L2_DEC_CMD_START) + pr_info("speed=%d, format=%u\n", + p->start.speed, p->start.format); + else if (p->cmd == V4L2_DEC_CMD_STOP) + pr_info("pts=%llu\n", p->stop.pts); +} + +static void v4l_print_dbg_chip_ident(const void *arg, bool write_only) +{ + const struct v4l2_dbg_chip_ident *p = arg; + + pr_cont("type=%u, ", p->match.type); + if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER) + pr_cont("name=%s, ", p->match.name); + else + pr_cont("addr=%u, ", p->match.addr); + pr_cont("chip_ident=%u, revision=0x%x\n", + p->ident, p->revision); +} + +static void v4l_print_dbg_register(const void *arg, bool write_only) +{ + const struct v4l2_dbg_register *p = arg; + + pr_cont("type=%u, ", p->match.type); + if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER) + pr_cont("name=%s, ", p->match.name); + else + pr_cont("addr=%u, ", p->match.addr); + pr_cont("reg=0x%llx, val=0x%llx\n", + p->reg, p->val); +} + +static void v4l_print_dv_enum_presets(const void *arg, bool write_only) +{ + const struct v4l2_dv_enum_preset *p = arg; + + pr_cont("index=%u, preset=%u, name=%s, width=%u, height=%u\n", + p->index, p->preset, p->name, p->width, p->height); +} + +static void v4l_print_dv_preset(const void *arg, bool write_only) +{ + const struct v4l2_dv_preset *p = arg; + + pr_cont("preset=%u\n", p->preset); +} + +static void v4l_print_dv_timings(const void *arg, bool write_only) +{ + const struct v4l2_dv_timings *p = arg; + switch (p->type) { case V4L2_DV_BT_656_1120: - dbgarg2("bt-656/1120:interlaced=%d," - " pixelclock=%lld," - " width=%d, height=%d, polarities=%x," - " hfrontporch=%d, hsync=%d," - " hbackporch=%d, vfrontporch=%d," - " vsync=%d, vbackporch=%d," - " il_vfrontporch=%d, il_vsync=%d," - " il_vbackporch=%d, standards=%x, flags=%x\n", + pr_cont("type=bt-656/1120, interlaced=%u, " + "pixelclock=%llu, " + "width=%u, height=%u, polarities=0x%x, " + "hfrontporch=%u, hsync=%u, " + "hbackporch=%u, vfrontporch=%u, " + "vsync=%u, vbackporch=%u, " + "il_vfrontporch=%u, il_vsync=%u, " + "il_vbackporch=%u, standards=0x%x, flags=0x%x\n", p->bt.interlaced, p->bt.pixelclock, p->bt.width, p->bt.height, p->bt.polarities, p->bt.hfrontporch, @@ -394,67 +674,173 @@ static void dbgtimings(struct video_device *vfd, p->bt.standards, p->bt.flags); break; default: - dbgarg2("Unknown type %d!\n", p->type); + pr_cont("type=%d\n", p->type); break; } } -static inline void v4l_print_pix_fmt(struct video_device *vfd, - struct v4l2_pix_format *fmt) +static void v4l_print_enum_dv_timings(const void *arg, bool write_only) { - dbgarg2("width=%d, height=%d, format=%c%c%c%c, field=%s, " - "bytesperline=%d sizeimage=%d, colorspace=%d\n", - fmt->width, fmt->height, - (fmt->pixelformat & 0xff), - (fmt->pixelformat >> 8) & 0xff, - (fmt->pixelformat >> 16) & 0xff, - (fmt->pixelformat >> 24) & 0xff, - prt_names(fmt->field, v4l2_field_names), - fmt->bytesperline, fmt->sizeimage, fmt->colorspace); -}; + const struct v4l2_enum_dv_timings *p = arg; + + pr_cont("index=%u, ", p->index); + v4l_print_dv_timings(&p->timings, write_only); +} -static inline void v4l_print_pix_fmt_mplane(struct video_device *vfd, - struct v4l2_pix_format_mplane *fmt) +static void v4l_print_dv_timings_cap(const void *arg, bool write_only) { - int i; + const struct v4l2_dv_timings_cap *p = arg; + + switch (p->type) { + case V4L2_DV_BT_656_1120: + pr_cont("type=bt-656/1120, width=%u-%u, height=%u-%u, " + "pixelclock=%llu-%llu, standards=0x%x, capabilities=0x%x\n", + p->bt.min_width, p->bt.max_width, + p->bt.min_height, p->bt.max_height, + p->bt.min_pixelclock, p->bt.max_pixelclock, + p->bt.standards, p->bt.capabilities); + break; + default: + pr_cont("type=%u\n", p->type); + break; + } +} - dbgarg2("width=%d, height=%d, format=%c%c%c%c, field=%s, " - "colorspace=%d, num_planes=%d\n", - fmt->width, fmt->height, - (fmt->pixelformat & 0xff), - (fmt->pixelformat >> 8) & 0xff, - (fmt->pixelformat >> 16) & 0xff, - (fmt->pixelformat >> 24) & 0xff, - prt_names(fmt->field, v4l2_field_names), - fmt->colorspace, fmt->num_planes); +static void v4l_print_frmsizeenum(const void *arg, bool write_only) +{ + const struct v4l2_frmsizeenum *p = arg; - for (i = 0; i < fmt->num_planes; ++i) - dbgarg2("plane %d: bytesperline=%d sizeimage=%d\n", i, - fmt->plane_fmt[i].bytesperline, - fmt->plane_fmt[i].sizeimage); + pr_cont("index=%u, pixelformat=%c%c%c%c, type=%u", + p->index, + (p->pixel_format & 0xff), + (p->pixel_format >> 8) & 0xff, + (p->pixel_format >> 16) & 0xff, + (p->pixel_format >> 24) & 0xff, + p->type); + switch (p->type) { + case V4L2_FRMSIZE_TYPE_DISCRETE: + pr_cont(" wxh=%ux%u\n", + p->discrete.width, p->discrete.height); + break; + case V4L2_FRMSIZE_TYPE_STEPWISE: + pr_cont(" min=%ux%u, max=%ux%u, step=%ux%u\n", + p->stepwise.min_width, p->stepwise.min_height, + p->stepwise.step_width, p->stepwise.step_height, + p->stepwise.max_width, p->stepwise.max_height); + break; + case V4L2_FRMSIZE_TYPE_CONTINUOUS: + /* fall through */ + default: + pr_cont("\n"); + break; + } } -static inline void v4l_print_ext_ctrls(unsigned int cmd, - struct video_device *vfd, struct v4l2_ext_controls *c, int show_vals) +static void v4l_print_frmivalenum(const void *arg, bool write_only) { - __u32 i; + const struct v4l2_frmivalenum *p = arg; - if (!(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) - return; - dbgarg(cmd, ""); - printk(KERN_CONT "class=0x%x", c->ctrl_class); - for (i = 0; i < c->count; i++) { - if (show_vals && !c->controls[i].size) - printk(KERN_CONT " id/val=0x%x/0x%x", - c->controls[i].id, c->controls[i].value); + pr_cont("index=%u, pixelformat=%c%c%c%c, wxh=%ux%u, type=%u", + p->index, + (p->pixel_format & 0xff), + (p->pixel_format >> 8) & 0xff, + (p->pixel_format >> 16) & 0xff, + (p->pixel_format >> 24) & 0xff, + p->width, p->height, p->type); + switch (p->type) { + case V4L2_FRMIVAL_TYPE_DISCRETE: + pr_cont(" fps=%d/%d\n", + p->discrete.numerator, + p->discrete.denominator); + break; + case V4L2_FRMIVAL_TYPE_STEPWISE: + pr_cont(" min=%d/%d, max=%d/%d, step=%d/%d\n", + p->stepwise.min.numerator, + p->stepwise.min.denominator, + p->stepwise.max.numerator, + p->stepwise.max.denominator, + p->stepwise.step.numerator, + p->stepwise.step.denominator); + break; + case V4L2_FRMIVAL_TYPE_CONTINUOUS: + /* fall through */ + default: + pr_cont("\n"); + break; + } +} + +static void v4l_print_event(const void *arg, bool write_only) +{ + const struct v4l2_event *p = arg; + const struct v4l2_event_ctrl *c; + + pr_cont("type=0x%x, pending=%u, sequence=%u, id=%u, " + "timestamp=%lu.%9.9lu\n", + p->type, p->pending, p->sequence, p->id, + p->timestamp.tv_sec, p->timestamp.tv_nsec); + switch (p->type) { + case V4L2_EVENT_VSYNC: + printk(KERN_DEBUG "field=%s\n", + prt_names(p->u.vsync.field, v4l2_field_names)); + break; + case V4L2_EVENT_CTRL: + c = &p->u.ctrl; + printk(KERN_DEBUG "changes=0x%x, type=%u, ", + c->changes, c->type); + if (c->type == V4L2_CTRL_TYPE_INTEGER64) + pr_cont("value64=%lld, ", c->value64); else - printk(KERN_CONT " id=0x%x,size=%u", - c->controls[i].id, c->controls[i].size); + pr_cont("value=%d, ", c->value); + pr_cont("flags=0x%x, minimum=%d, maximum=%d, step=%d," + " default_value=%d\n", + c->flags, c->minimum, c->maximum, + c->step, c->default_value); + break; + case V4L2_EVENT_FRAME_SYNC: + pr_cont("frame_sequence=%u\n", + p->u.frame_sync.frame_sequence); + break; } - printk(KERN_CONT "\n"); -}; +} -static inline int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv) +static void v4l_print_event_subscription(const void *arg, bool write_only) +{ + const struct v4l2_event_subscription *p = arg; + + pr_cont("type=0x%x, id=0x%x, flags=0x%x\n", + p->type, p->id, p->flags); +} + +static void v4l_print_sliced_vbi_cap(const void *arg, bool write_only) +{ + const struct v4l2_sliced_vbi_cap *p = arg; + int i; + + pr_cont("type=%s, service_set=0x%08x\n", + prt_names(p->type, v4l2_type_names), p->service_set); + for (i = 0; i < 24; i++) + printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i, + p->service_lines[0][i], + p->service_lines[1][i]); +} + +static void v4l_print_u32(const void *arg, bool write_only) +{ + pr_cont("value=%u\n", *(const u32 *)arg); +} + +static void v4l_print_newline(const void *arg, bool write_only) +{ + pr_cont("\n"); +} + +static void v4l_print_default(const void *arg, bool write_only) +{ + pr_cont("driver-specific ioctl\n"); +} + +static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv) { __u32 i; @@ -536,1615 +922,1159 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type) return -EINVAL; } -static long __video_do_ioctl(struct file *file, - unsigned int cmd, void *arg) +static int v4l_querycap(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) { - struct video_device *vfd = video_devdata(file); - const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; - void *fh = file->private_data; - struct v4l2_fh *vfh = NULL; - int use_fh_prio = 0; - long ret = -ENOTTY; + struct v4l2_capability *cap = (struct v4l2_capability *)arg; - if (ops == NULL) { - printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n", - vfd->name); - return ret; - } - - if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { - vfh = file->private_data; - use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); - } + cap->version = LINUX_VERSION_CODE; + return ops->vidioc_querycap(file, fh, cap); +} - if (v4l2_is_known_ioctl(cmd)) { - struct v4l2_ioctl_info *info = &v4l2_ioctls[_IOC_NR(cmd)]; +static int v4l_s_input(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + return ops->vidioc_s_input(file, fh, *(unsigned int *)arg); +} - if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) && - !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler)) - return -ENOTTY; +static int v4l_s_output(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + return ops->vidioc_s_output(file, fh, *(unsigned int *)arg); +} - if (use_fh_prio && (info->flags & INFO_FL_PRIO)) { - ret = v4l2_prio_check(vfd->prio, vfh->prio); - if (ret) - return ret; - } - } +static int v4l_g_priority(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd; + u32 *p = arg; - if ((vfd->debug & V4L2_DEBUG_IOCTL) && - !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) { - v4l_print_ioctl(vfd->name, cmd); - printk(KERN_CONT "\n"); - } + if (ops->vidioc_g_priority) + return ops->vidioc_g_priority(file, fh, arg); + vfd = video_devdata(file); + *p = v4l2_prio_max(&vfd->v4l2_dev->prio); + return 0; +} - switch (cmd) { +static int v4l_s_priority(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd; + struct v4l2_fh *vfh; + u32 *p = arg; + + if (ops->vidioc_s_priority) + return ops->vidioc_s_priority(file, fh, *p); + vfd = video_devdata(file); + vfh = file->private_data; + return v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p); +} - /* --- capabilities ------------------------------------------ */ - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = (struct v4l2_capability *)arg; - - cap->version = LINUX_VERSION_CODE; - ret = ops->vidioc_querycap(file, fh, cap); - if (!ret) - dbgarg(cmd, "driver=%s, card=%s, bus=%s, " - "version=0x%08x, " - "capabilities=0x%08x, " - "device_caps=0x%08x\n", - cap->driver, cap->card, cap->bus_info, - cap->version, - cap->capabilities, - cap->device_caps); - break; - } +static int v4l_enuminput(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_input *p = arg; - /* --- priority ------------------------------------------ */ - case VIDIOC_G_PRIORITY: - { - enum v4l2_priority *p = arg; + /* + * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS & + * CAP_STD here based on ioctl handler provided by the + * driver. If the driver doesn't support these + * for a specific input, it must override these flags. + */ + if (ops->vidioc_s_std) + p->capabilities |= V4L2_IN_CAP_STD; + if (ops->vidioc_s_dv_preset) + p->capabilities |= V4L2_IN_CAP_PRESETS; + if (ops->vidioc_s_dv_timings) + p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS; + + return ops->vidioc_enum_input(file, fh, p); +} - if (ops->vidioc_g_priority) { - ret = ops->vidioc_g_priority(file, fh, p); - } else if (use_fh_prio) { - *p = v4l2_prio_max(&vfd->v4l2_dev->prio); - ret = 0; - } - if (!ret) - dbgarg(cmd, "priority is %d\n", *p); - break; - } - case VIDIOC_S_PRIORITY: - { - enum v4l2_priority *p = arg; +static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_output *p = arg; - dbgarg(cmd, "setting priority to %d\n", *p); - if (ops->vidioc_s_priority) - ret = ops->vidioc_s_priority(file, fh, *p); - else - ret = v4l2_prio_change(&vfd->v4l2_dev->prio, - &vfh->prio, *p); - break; - } + /* + * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS & + * CAP_STD here based on ioctl handler provided by the + * driver. If the driver doesn't support these + * for a specific output, it must override these flags. + */ + if (ops->vidioc_s_std) + p->capabilities |= V4L2_OUT_CAP_STD; + if (ops->vidioc_s_dv_preset) + p->capabilities |= V4L2_OUT_CAP_PRESETS; + if (ops->vidioc_s_dv_timings) + p->capabilities |= V4L2_OUT_CAP_CUSTOM_TIMINGS; + + return ops->vidioc_enum_output(file, fh, p); +} - /* --- capture ioctls ---------------------------------------- */ - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *f = arg; +static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_fmtdesc *p = arg; - ret = -EINVAL; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (likely(ops->vidioc_enum_fmt_vid_cap)) - ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f); - break; - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (likely(ops->vidioc_enum_fmt_vid_cap_mplane)) - ret = ops->vidioc_enum_fmt_vid_cap_mplane(file, - fh, f); + switch (p->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (unlikely(!ops->vidioc_enum_fmt_vid_cap)) break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (likely(ops->vidioc_enum_fmt_vid_overlay)) - ret = ops->vidioc_enum_fmt_vid_overlay(file, - fh, f); + return ops->vidioc_enum_fmt_vid_cap(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane)) break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (likely(ops->vidioc_enum_fmt_vid_out)) - ret = ops->vidioc_enum_fmt_vid_out(file, fh, f); + return ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (unlikely(!ops->vidioc_enum_fmt_vid_overlay)) break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (likely(ops->vidioc_enum_fmt_vid_out_mplane)) - ret = ops->vidioc_enum_fmt_vid_out_mplane(file, - fh, f); + return ops->vidioc_enum_fmt_vid_overlay(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (unlikely(!ops->vidioc_enum_fmt_vid_out)) break; - case V4L2_BUF_TYPE_PRIVATE: - if (likely(ops->vidioc_enum_fmt_type_private)) - ret = ops->vidioc_enum_fmt_type_private(file, - fh, f); + return ops->vidioc_enum_fmt_vid_out(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane)) break; - default: + return ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg); + case V4L2_BUF_TYPE_PRIVATE: + if (unlikely(!ops->vidioc_enum_fmt_type_private)) break; - } - if (likely(!ret)) - dbgarg(cmd, "index=%d, type=%d, flags=%d, " - "pixelformat=%c%c%c%c, description='%s'\n", - f->index, f->type, f->flags, - (f->pixelformat & 0xff), - (f->pixelformat >> 8) & 0xff, - (f->pixelformat >> 16) & 0xff, - (f->pixelformat >> 24) & 0xff, - f->description); - break; + return ops->vidioc_enum_fmt_type_private(file, fh, arg); } - case VIDIOC_G_FMT: - { - struct v4l2_format *f = (struct v4l2_format *)arg; - - /* FIXME: Should be one dump per type */ - dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); - - ret = -EINVAL; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (ops->vidioc_g_fmt_vid_cap) - ret = ops->vidioc_g_fmt_vid_cap(file, fh, f); - if (!ret) - v4l_print_pix_fmt(vfd, &f->fmt.pix); + return -EINVAL; +} + +static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_format *p = arg; + + switch (p->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (unlikely(!ops->vidioc_g_fmt_vid_cap)) break; - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (ops->vidioc_g_fmt_vid_cap_mplane) - ret = ops->vidioc_g_fmt_vid_cap_mplane(file, - fh, f); - if (!ret) - v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + return ops->vidioc_g_fmt_vid_cap(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (unlikely(!ops->vidioc_g_fmt_vid_cap_mplane)) break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (likely(ops->vidioc_g_fmt_vid_overlay)) - ret = ops->vidioc_g_fmt_vid_overlay(file, - fh, f); + return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (unlikely(!ops->vidioc_g_fmt_vid_overlay)) break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (ops->vidioc_g_fmt_vid_out) - ret = ops->vidioc_g_fmt_vid_out(file, fh, f); - if (!ret) - v4l_print_pix_fmt(vfd, &f->fmt.pix); + return ops->vidioc_g_fmt_vid_overlay(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (unlikely(!ops->vidioc_g_fmt_vid_out)) break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (ops->vidioc_g_fmt_vid_out_mplane) - ret = ops->vidioc_g_fmt_vid_out_mplane(file, - fh, f); - if (!ret) - v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + return ops->vidioc_g_fmt_vid_out(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (unlikely(!ops->vidioc_g_fmt_vid_out_mplane)) break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (likely(ops->vidioc_g_fmt_vid_out_overlay)) - ret = ops->vidioc_g_fmt_vid_out_overlay(file, - fh, f); + return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (unlikely(!ops->vidioc_g_fmt_vid_out_overlay)) break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (likely(ops->vidioc_g_fmt_vbi_cap)) - ret = ops->vidioc_g_fmt_vbi_cap(file, fh, f); + return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg); + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (unlikely(!ops->vidioc_g_fmt_vbi_cap)) break; - case V4L2_BUF_TYPE_VBI_OUTPUT: - if (likely(ops->vidioc_g_fmt_vbi_out)) - ret = ops->vidioc_g_fmt_vbi_out(file, fh, f); + return ops->vidioc_g_fmt_vbi_cap(file, fh, arg); + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (unlikely(!ops->vidioc_g_fmt_vbi_out)) break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (likely(ops->vidioc_g_fmt_sliced_vbi_cap)) - ret = ops->vidioc_g_fmt_sliced_vbi_cap(file, - fh, f); + return ops->vidioc_g_fmt_vbi_out(file, fh, arg); + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_cap)) break; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (likely(ops->vidioc_g_fmt_sliced_vbi_out)) - ret = ops->vidioc_g_fmt_sliced_vbi_out(file, - fh, f); + return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg); + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_out)) break; - case V4L2_BUF_TYPE_PRIVATE: - if (likely(ops->vidioc_g_fmt_type_private)) - ret = ops->vidioc_g_fmt_type_private(file, - fh, f); + return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg); + case V4L2_BUF_TYPE_PRIVATE: + if (unlikely(!ops->vidioc_g_fmt_type_private)) break; - } - break; + return ops->vidioc_g_fmt_type_private(file, fh, arg); } - case VIDIOC_S_FMT: - { - struct v4l2_format *f = (struct v4l2_format *)arg; - - ret = -EINVAL; + return -EINVAL; +} - /* FIXME: Should be one dump per type */ - dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); +static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_format *p = arg; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - CLEAR_AFTER_FIELD(f, fmt.pix); - v4l_print_pix_fmt(vfd, &f->fmt.pix); - if (ops->vidioc_s_fmt_vid_cap) - ret = ops->vidioc_s_fmt_vid_cap(file, fh, f); + switch (p->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (unlikely(!ops->vidioc_s_fmt_vid_cap)) break; - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - CLEAR_AFTER_FIELD(f, fmt.pix_mp); - v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); - if (ops->vidioc_s_fmt_vid_cap_mplane) - ret = ops->vidioc_s_fmt_vid_cap_mplane(file, - fh, f); + CLEAR_AFTER_FIELD(p, fmt.pix); + return ops->vidioc_s_fmt_vid_cap(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane)) break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - CLEAR_AFTER_FIELD(f, fmt.win); - if (ops->vidioc_s_fmt_vid_overlay) - ret = ops->vidioc_s_fmt_vid_overlay(file, - fh, f); + CLEAR_AFTER_FIELD(p, fmt.pix_mp); + return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (unlikely(!ops->vidioc_s_fmt_vid_overlay)) break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - CLEAR_AFTER_FIELD(f, fmt.pix); - v4l_print_pix_fmt(vfd, &f->fmt.pix); - if (ops->vidioc_s_fmt_vid_out) - ret = ops->vidioc_s_fmt_vid_out(file, fh, f); + CLEAR_AFTER_FIELD(p, fmt.win); + return ops->vidioc_s_fmt_vid_overlay(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (unlikely(!ops->vidioc_s_fmt_vid_out)) break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - CLEAR_AFTER_FIELD(f, fmt.pix_mp); - v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); - if (ops->vidioc_s_fmt_vid_out_mplane) - ret = ops->vidioc_s_fmt_vid_out_mplane(file, - fh, f); + CLEAR_AFTER_FIELD(p, fmt.pix); + return ops->vidioc_s_fmt_vid_out(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane)) break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - CLEAR_AFTER_FIELD(f, fmt.win); - if (ops->vidioc_s_fmt_vid_out_overlay) - ret = ops->vidioc_s_fmt_vid_out_overlay(file, - fh, f); + CLEAR_AFTER_FIELD(p, fmt.pix_mp); + return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay)) break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - CLEAR_AFTER_FIELD(f, fmt.vbi); - if (likely(ops->vidioc_s_fmt_vbi_cap)) - ret = ops->vidioc_s_fmt_vbi_cap(file, fh, f); + CLEAR_AFTER_FIELD(p, fmt.win); + return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg); + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (unlikely(!ops->vidioc_s_fmt_vbi_cap)) break; - case V4L2_BUF_TYPE_VBI_OUTPUT: - CLEAR_AFTER_FIELD(f, fmt.vbi); - if (likely(ops->vidioc_s_fmt_vbi_out)) - ret = ops->vidioc_s_fmt_vbi_out(file, fh, f); + CLEAR_AFTER_FIELD(p, fmt.vbi); + return ops->vidioc_s_fmt_vbi_cap(file, fh, arg); + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (unlikely(!ops->vidioc_s_fmt_vbi_out)) break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - CLEAR_AFTER_FIELD(f, fmt.sliced); - if (likely(ops->vidioc_s_fmt_sliced_vbi_cap)) - ret = ops->vidioc_s_fmt_sliced_vbi_cap(file, - fh, f); + CLEAR_AFTER_FIELD(p, fmt.vbi); + return ops->vidioc_s_fmt_vbi_out(file, fh, arg); + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap)) break; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - CLEAR_AFTER_FIELD(f, fmt.sliced); - if (likely(ops->vidioc_s_fmt_sliced_vbi_out)) - ret = ops->vidioc_s_fmt_sliced_vbi_out(file, - fh, f); - + CLEAR_AFTER_FIELD(p, fmt.sliced); + return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg); + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out)) break; - case V4L2_BUF_TYPE_PRIVATE: - /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */ - if (likely(ops->vidioc_s_fmt_type_private)) - ret = ops->vidioc_s_fmt_type_private(file, - fh, f); + CLEAR_AFTER_FIELD(p, fmt.sliced); + return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg); + case V4L2_BUF_TYPE_PRIVATE: + if (unlikely(!ops->vidioc_s_fmt_type_private)) break; - } - break; + return ops->vidioc_s_fmt_type_private(file, fh, arg); } - case VIDIOC_TRY_FMT: - { - struct v4l2_format *f = (struct v4l2_format *)arg; - - /* FIXME: Should be one dump per type */ - dbgarg(cmd, "type=%s\n", prt_names(f->type, - v4l2_type_names)); - ret = -EINVAL; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - CLEAR_AFTER_FIELD(f, fmt.pix); - if (ops->vidioc_try_fmt_vid_cap) - ret = ops->vidioc_try_fmt_vid_cap(file, fh, f); - if (!ret) - v4l_print_pix_fmt(vfd, &f->fmt.pix); + return -EINVAL; +} + +static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_format *p = arg; + + switch (p->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (unlikely(!ops->vidioc_try_fmt_vid_cap)) break; - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - CLEAR_AFTER_FIELD(f, fmt.pix_mp); - if (ops->vidioc_try_fmt_vid_cap_mplane) - ret = ops->vidioc_try_fmt_vid_cap_mplane(file, - fh, f); - if (!ret) - v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + CLEAR_AFTER_FIELD(p, fmt.pix); + return ops->vidioc_try_fmt_vid_cap(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane)) break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - CLEAR_AFTER_FIELD(f, fmt.win); - if (likely(ops->vidioc_try_fmt_vid_overlay)) - ret = ops->vidioc_try_fmt_vid_overlay(file, - fh, f); + CLEAR_AFTER_FIELD(p, fmt.pix_mp); + return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (unlikely(!ops->vidioc_try_fmt_vid_overlay)) break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - CLEAR_AFTER_FIELD(f, fmt.pix); - if (ops->vidioc_try_fmt_vid_out) - ret = ops->vidioc_try_fmt_vid_out(file, fh, f); - if (!ret) - v4l_print_pix_fmt(vfd, &f->fmt.pix); + CLEAR_AFTER_FIELD(p, fmt.win); + return ops->vidioc_try_fmt_vid_overlay(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (unlikely(!ops->vidioc_try_fmt_vid_out)) break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - CLEAR_AFTER_FIELD(f, fmt.pix_mp); - if (ops->vidioc_try_fmt_vid_out_mplane) - ret = ops->vidioc_try_fmt_vid_out_mplane(file, - fh, f); - if (!ret) - v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + CLEAR_AFTER_FIELD(p, fmt.pix); + return ops->vidioc_try_fmt_vid_out(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane)) break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - CLEAR_AFTER_FIELD(f, fmt.win); - if (likely(ops->vidioc_try_fmt_vid_out_overlay)) - ret = ops->vidioc_try_fmt_vid_out_overlay(file, - fh, f); + CLEAR_AFTER_FIELD(p, fmt.pix_mp); + return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg); + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay)) break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - CLEAR_AFTER_FIELD(f, fmt.vbi); - if (likely(ops->vidioc_try_fmt_vbi_cap)) - ret = ops->vidioc_try_fmt_vbi_cap(file, fh, f); + CLEAR_AFTER_FIELD(p, fmt.win); + return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg); + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (unlikely(!ops->vidioc_try_fmt_vbi_cap)) break; - case V4L2_BUF_TYPE_VBI_OUTPUT: - CLEAR_AFTER_FIELD(f, fmt.vbi); - if (likely(ops->vidioc_try_fmt_vbi_out)) - ret = ops->vidioc_try_fmt_vbi_out(file, fh, f); + CLEAR_AFTER_FIELD(p, fmt.vbi); + return ops->vidioc_try_fmt_vbi_cap(file, fh, arg); + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (unlikely(!ops->vidioc_try_fmt_vbi_out)) break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - CLEAR_AFTER_FIELD(f, fmt.sliced); - if (likely(ops->vidioc_try_fmt_sliced_vbi_cap)) - ret = ops->vidioc_try_fmt_sliced_vbi_cap(file, - fh, f); + CLEAR_AFTER_FIELD(p, fmt.vbi); + return ops->vidioc_try_fmt_vbi_out(file, fh, arg); + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap)) break; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - CLEAR_AFTER_FIELD(f, fmt.sliced); - if (likely(ops->vidioc_try_fmt_sliced_vbi_out)) - ret = ops->vidioc_try_fmt_sliced_vbi_out(file, - fh, f); + CLEAR_AFTER_FIELD(p, fmt.sliced); + return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg); + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out)) break; - case V4L2_BUF_TYPE_PRIVATE: - /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */ - if (likely(ops->vidioc_try_fmt_type_private)) - ret = ops->vidioc_try_fmt_type_private(file, - fh, f); + CLEAR_AFTER_FIELD(p, fmt.sliced); + return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg); + case V4L2_BUF_TYPE_PRIVATE: + if (unlikely(!ops->vidioc_try_fmt_type_private)) break; - } - break; + return ops->vidioc_try_fmt_type_private(file, fh, arg); } - /* FIXME: Those buf reqs could be handled here, - with some changes on videobuf to allow its header to be included at - videodev2.h or being merged at videodev2. - */ - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *p = arg; - - ret = check_fmt(ops, p->type); - if (ret) - break; + return -EINVAL; +} - if (p->type < V4L2_BUF_TYPE_PRIVATE) - CLEAR_AFTER_FIELD(p, memory); +static int v4l_streamon(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + return ops->vidioc_streamon(file, fh, *(unsigned int *)arg); +} - ret = ops->vidioc_reqbufs(file, fh, p); - dbgarg(cmd, "count=%d, type=%s, memory=%s\n", - p->count, - prt_names(p->type, v4l2_type_names), - prt_names(p->memory, v4l2_memory_names)); - break; - } - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *p = arg; +static int v4l_streamoff(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + return ops->vidioc_streamoff(file, fh, *(unsigned int *)arg); +} - ret = check_fmt(ops, p->type); - if (ret) - break; +static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_tuner *p = arg; - ret = ops->vidioc_querybuf(file, fh, p); - if (!ret) - dbgbuf(cmd, vfd, p); - break; - } - case VIDIOC_QBUF: - { - struct v4l2_buffer *p = arg; + p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? + V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + return ops->vidioc_g_tuner(file, fh, p); +} - ret = check_fmt(ops, p->type); - if (ret) - break; +static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_tuner *p = arg; - ret = ops->vidioc_qbuf(file, fh, p); - if (!ret) - dbgbuf(cmd, vfd, p); - break; - } - case VIDIOC_DQBUF: - { - struct v4l2_buffer *p = arg; + p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? + V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + return ops->vidioc_s_tuner(file, fh, p); +} - ret = check_fmt(ops, p->type); - if (ret) - break; +static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_frequency *p = arg; - ret = ops->vidioc_dqbuf(file, fh, p); - if (!ret) - dbgbuf(cmd, vfd, p); - break; - } - case VIDIOC_OVERLAY: - { - int *i = arg; + p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? + V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + return ops->vidioc_g_frequency(file, fh, p); +} - dbgarg(cmd, "value=%d\n", *i); - ret = ops->vidioc_overlay(file, fh, *i); - break; - } - case VIDIOC_G_FBUF: - { - struct v4l2_framebuffer *p = arg; - - ret = ops->vidioc_g_fbuf(file, fh, arg); - if (!ret) { - dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", - p->capability, p->flags, - (unsigned long)p->base); - v4l_print_pix_fmt(vfd, &p->fmt); - } - break; - } - case VIDIOC_S_FBUF: - { - struct v4l2_framebuffer *p = arg; - - dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", - p->capability, p->flags, (unsigned long)p->base); - v4l_print_pix_fmt(vfd, &p->fmt); - ret = ops->vidioc_s_fbuf(file, fh, arg); - break; - } - case VIDIOC_STREAMON: - { - enum v4l2_buf_type i = *(int *)arg; +static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_frequency *p = arg; + enum v4l2_tuner_type type; - dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); - ret = ops->vidioc_streamon(file, fh, i); - break; - } - case VIDIOC_STREAMOFF: - { - enum v4l2_buf_type i = *(int *)arg; + type = (vfd->vfl_type == VFL_TYPE_RADIO) ? + V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + if (p->type != type) + return -EINVAL; + return ops->vidioc_s_frequency(file, fh, p); +} - dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); - ret = ops->vidioc_streamoff(file, fh, i); - break; - } - /* ---------- tv norms ---------- */ - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *p = arg; - v4l2_std_id id = vfd->tvnorms, curr_id = 0; - unsigned int index = p->index, i, j = 0; - const char *descr = ""; - - if (id == 0) - break; - ret = -EINVAL; - - /* Return norm array in a canonical way */ - for (i = 0; i <= index && id; i++) { - /* last std value in the standards array is 0, so this - while always ends there since (id & 0) == 0. */ - while ((id & standards[j].std) != standards[j].std) - j++; - curr_id = standards[j].std; - descr = standards[j].descr; +static int v4l_enumstd(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_standard *p = arg; + v4l2_std_id id = vfd->tvnorms, curr_id = 0; + unsigned int index = p->index, i, j = 0; + const char *descr = ""; + + /* Return norm array in a canonical way */ + for (i = 0; i <= index && id; i++) { + /* last std value in the standards array is 0, so this + while always ends there since (id & 0) == 0. */ + while ((id & standards[j].std) != standards[j].std) j++; - if (curr_id == 0) - break; - if (curr_id != V4L2_STD_PAL && - curr_id != V4L2_STD_SECAM && - curr_id != V4L2_STD_NTSC) - id &= ~curr_id; - } - if (i <= index) + curr_id = standards[j].std; + descr = standards[j].descr; + j++; + if (curr_id == 0) break; - - v4l2_video_std_construct(p, curr_id, descr); - - dbgarg(cmd, "index=%d, id=0x%Lx, name=%s, fps=%d/%d, " - "framelines=%d\n", p->index, - (unsigned long long)p->id, p->name, - p->frameperiod.numerator, - p->frameperiod.denominator, - p->framelines); - - ret = 0; - break; - } - case VIDIOC_G_STD: - { - v4l2_std_id *id = arg; - - /* Calls the specific handler */ - if (ops->vidioc_g_std) - ret = ops->vidioc_g_std(file, fh, id); - else if (vfd->current_norm) { - ret = 0; - *id = vfd->current_norm; - } - - if (likely(!ret)) - dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id); - break; + if (curr_id != V4L2_STD_PAL && + curr_id != V4L2_STD_SECAM && + curr_id != V4L2_STD_NTSC) + id &= ~curr_id; } - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg, norm; - - dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id); - - ret = -EINVAL; - norm = (*id) & vfd->tvnorms; - if (vfd->tvnorms && !norm) /* Check if std is supported */ - break; - - /* Calls the specific handler */ - ret = ops->vidioc_s_std(file, fh, &norm); - - /* Updates standard information */ - if (ret >= 0) - vfd->current_norm = norm; - break; - } - case VIDIOC_QUERYSTD: - { - v4l2_std_id *p = arg; - - /* - * If nothing detected, it should return all supported - * Drivers just need to mask the std argument, in order - * to remove the standards that don't apply from the mask. - * This means that tuners, audio and video decoders can join - * their efforts to improve the standards detection - */ - *p = vfd->tvnorms; - ret = ops->vidioc_querystd(file, fh, arg); - if (!ret) - dbgarg(cmd, "detected std=%08Lx\n", - (unsigned long long)*p); - break; - } - /* ------ input switching ---------- */ - /* FIXME: Inputs can be handled inside videodev2 */ - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *p = arg; + if (i <= index) + return -EINVAL; - /* - * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS & - * CAP_STD here based on ioctl handler provided by the - * driver. If the driver doesn't support these - * for a specific input, it must override these flags. - */ - if (ops->vidioc_s_std) - p->capabilities |= V4L2_IN_CAP_STD; - if (ops->vidioc_s_dv_preset) - p->capabilities |= V4L2_IN_CAP_PRESETS; - if (ops->vidioc_s_dv_timings) - p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS; - - ret = ops->vidioc_enum_input(file, fh, p); - if (!ret) - dbgarg(cmd, "index=%d, name=%s, type=%d, " - "audioset=%d, " - "tuner=%d, std=%08Lx, status=%d\n", - p->index, p->name, p->type, p->audioset, - p->tuner, - (unsigned long long)p->std, - p->status); - break; - } - case VIDIOC_G_INPUT: - { - unsigned int *i = arg; + v4l2_video_std_construct(p, curr_id, descr); + return 0; +} - ret = ops->vidioc_g_input(file, fh, i); - if (!ret) - dbgarg(cmd, "value=%d\n", *i); - break; - } - case VIDIOC_S_INPUT: - { - unsigned int *i = arg; +static int v4l_g_std(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + v4l2_std_id *id = arg; - dbgarg(cmd, "value=%d\n", *i); - ret = ops->vidioc_s_input(file, fh, *i); - break; + /* Calls the specific handler */ + if (ops->vidioc_g_std) + return ops->vidioc_g_std(file, fh, arg); + if (vfd->current_norm) { + *id = vfd->current_norm; + return 0; } + return -ENOTTY; +} - /* ------ output switching ---------- */ - case VIDIOC_ENUMOUTPUT: - { - struct v4l2_output *p = arg; - - /* - * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS & - * CAP_STD here based on ioctl handler provided by the - * driver. If the driver doesn't support these - * for a specific output, it must override these flags. - */ - if (ops->vidioc_s_std) - p->capabilities |= V4L2_OUT_CAP_STD; - if (ops->vidioc_s_dv_preset) - p->capabilities |= V4L2_OUT_CAP_PRESETS; - if (ops->vidioc_s_dv_timings) - p->capabilities |= V4L2_OUT_CAP_CUSTOM_TIMINGS; - - ret = ops->vidioc_enum_output(file, fh, p); - if (!ret) - dbgarg(cmd, "index=%d, name=%s, type=%d, " - "audioset=0x%x, " - "modulator=%d, std=0x%08Lx\n", - p->index, p->name, p->type, p->audioset, - p->modulator, (unsigned long long)p->std); - break; - } - case VIDIOC_G_OUTPUT: - { - unsigned int *i = arg; +static int v4l_s_std(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + v4l2_std_id *id = arg, norm; + int ret; - ret = ops->vidioc_g_output(file, fh, i); - if (!ret) - dbgarg(cmd, "value=%d\n", *i); - break; - } - case VIDIOC_S_OUTPUT: - { - unsigned int *i = arg; + norm = (*id) & vfd->tvnorms; + if (vfd->tvnorms && !norm) /* Check if std is supported */ + return -EINVAL; - dbgarg(cmd, "value=%d\n", *i); - ret = ops->vidioc_s_output(file, fh, *i); - break; - } + /* Calls the specific handler */ + ret = ops->vidioc_s_std(file, fh, &norm); - /* --- controls ---------------------------------------------- */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *p = arg; - - if (vfh && vfh->ctrl_handler) - ret = v4l2_queryctrl(vfh->ctrl_handler, p); - else if (vfd->ctrl_handler) - ret = v4l2_queryctrl(vfd->ctrl_handler, p); - else if (ops->vidioc_queryctrl) - ret = ops->vidioc_queryctrl(file, fh, p); - else - break; - if (!ret) - dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, " - "step=%d, default=%d, flags=0x%08x\n", - p->id, p->type, p->name, - p->minimum, p->maximum, - p->step, p->default_value, p->flags); - else - dbgarg(cmd, "id=0x%x\n", p->id); - break; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *p = arg; - - if (vfh && vfh->ctrl_handler) - ret = v4l2_g_ctrl(vfh->ctrl_handler, p); - else if (vfd->ctrl_handler) - ret = v4l2_g_ctrl(vfd->ctrl_handler, p); - else if (ops->vidioc_g_ctrl) - ret = ops->vidioc_g_ctrl(file, fh, p); - else if (ops->vidioc_g_ext_ctrls) { - struct v4l2_ext_controls ctrls; - struct v4l2_ext_control ctrl; - - ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); - ctrls.count = 1; - ctrls.controls = &ctrl; - ctrl.id = p->id; - ctrl.value = p->value; - if (check_ext_ctrls(&ctrls, 1)) { - ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls); - if (ret == 0) - p->value = ctrl.value; - } - } else - break; - if (!ret) - dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value); - else - dbgarg(cmd, "id=0x%x\n", p->id); - break; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *p = arg; - struct v4l2_ext_controls ctrls; - struct v4l2_ext_control ctrl; - - if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler && - !ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls) - break; + /* Updates standard information */ + if (ret >= 0) + vfd->current_norm = norm; + return ret; +} - dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value); +static int v4l_querystd(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + v4l2_std_id *p = arg; + + /* + * If nothing detected, it should return all supported + * standard. + * Drivers just need to mask the std argument, in order + * to remove the standards that don't apply from the mask. + * This means that tuners, audio and video decoders can join + * their efforts to improve the standards detection. + */ + *p = vfd->tvnorms; + return ops->vidioc_querystd(file, fh, arg); +} - if (vfh && vfh->ctrl_handler) { - ret = v4l2_s_ctrl(vfh, vfh->ctrl_handler, p); - break; - } - if (vfd->ctrl_handler) { - ret = v4l2_s_ctrl(NULL, vfd->ctrl_handler, p); - break; - } - if (ops->vidioc_s_ctrl) { - ret = ops->vidioc_s_ctrl(file, fh, p); - break; - } - if (!ops->vidioc_s_ext_ctrls) - break; +static int v4l_s_hw_freq_seek(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_hw_freq_seek *p = arg; + enum v4l2_tuner_type type; - ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); - ctrls.count = 1; - ctrls.controls = &ctrl; - ctrl.id = p->id; - ctrl.value = p->value; - if (check_ext_ctrls(&ctrls, 1)) - ret = ops->vidioc_s_ext_ctrls(file, fh, &ctrls); - else - ret = -EINVAL; - break; - } - case VIDIOC_G_EXT_CTRLS: - { - struct v4l2_ext_controls *p = arg; - - p->error_idx = p->count; - if (vfh && vfh->ctrl_handler) - ret = v4l2_g_ext_ctrls(vfh->ctrl_handler, p); - else if (vfd->ctrl_handler) - ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p); - else if (ops->vidioc_g_ext_ctrls) - ret = check_ext_ctrls(p, 0) ? - ops->vidioc_g_ext_ctrls(file, fh, p) : - -EINVAL; - else - break; - v4l_print_ext_ctrls(cmd, vfd, p, !ret); - break; - } - case VIDIOC_S_EXT_CTRLS: - { - struct v4l2_ext_controls *p = arg; + type = (vfd->vfl_type == VFL_TYPE_RADIO) ? + V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + if (p->type != type) + return -EINVAL; + return ops->vidioc_s_hw_freq_seek(file, fh, p); +} - p->error_idx = p->count; - if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler && - !ops->vidioc_s_ext_ctrls) - break; - v4l_print_ext_ctrls(cmd, vfd, p, 1); - if (vfh && vfh->ctrl_handler) - ret = v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p); - else if (vfd->ctrl_handler) - ret = v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p); - else if (check_ext_ctrls(p, 0)) - ret = ops->vidioc_s_ext_ctrls(file, fh, p); - else - ret = -EINVAL; - break; - } - case VIDIOC_TRY_EXT_CTRLS: - { - struct v4l2_ext_controls *p = arg; +static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_requestbuffers *p = arg; + int ret = check_fmt(ops, p->type); - p->error_idx = p->count; - if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler && - !ops->vidioc_try_ext_ctrls) - break; - v4l_print_ext_ctrls(cmd, vfd, p, 1); - if (vfh && vfh->ctrl_handler) - ret = v4l2_try_ext_ctrls(vfh->ctrl_handler, p); - else if (vfd->ctrl_handler) - ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p); - else if (check_ext_ctrls(p, 0)) - ret = ops->vidioc_try_ext_ctrls(file, fh, p); - else - ret = -EINVAL; - break; - } - case VIDIOC_QUERYMENU: - { - struct v4l2_querymenu *p = arg; - - if (vfh && vfh->ctrl_handler) - ret = v4l2_querymenu(vfh->ctrl_handler, p); - else if (vfd->ctrl_handler) - ret = v4l2_querymenu(vfd->ctrl_handler, p); - else if (ops->vidioc_querymenu) - ret = ops->vidioc_querymenu(file, fh, p); - else - break; - if (!ret) - dbgarg(cmd, "id=0x%x, index=%d, name=%s\n", - p->id, p->index, p->name); - else - dbgarg(cmd, "id=0x%x, index=%d\n", - p->id, p->index); - break; - } - /* --- audio ---------------------------------------------- */ - case VIDIOC_ENUMAUDIO: - { - struct v4l2_audio *p = arg; - - ret = ops->vidioc_enumaudio(file, fh, p); - if (!ret) - dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " - "mode=0x%x\n", p->index, p->name, - p->capability, p->mode); - else - dbgarg(cmd, "index=%d\n", p->index); - break; - } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *p = arg; - - ret = ops->vidioc_g_audio(file, fh, p); - if (!ret) - dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " - "mode=0x%x\n", p->index, - p->name, p->capability, p->mode); - else - dbgarg(cmd, "index=%d\n", p->index); - break; - } - case VIDIOC_S_AUDIO: - { - struct v4l2_audio *p = arg; - - dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " - "mode=0x%x\n", p->index, p->name, - p->capability, p->mode); - ret = ops->vidioc_s_audio(file, fh, p); - break; - } - case VIDIOC_ENUMAUDOUT: - { - struct v4l2_audioout *p = arg; - - dbgarg(cmd, "Enum for index=%d\n", p->index); - ret = ops->vidioc_enumaudout(file, fh, p); - if (!ret) - dbgarg2("index=%d, name=%s, capability=%d, " - "mode=%d\n", p->index, p->name, - p->capability, p->mode); - break; - } - case VIDIOC_G_AUDOUT: - { - struct v4l2_audioout *p = arg; - - ret = ops->vidioc_g_audout(file, fh, p); - if (!ret) - dbgarg2("index=%d, name=%s, capability=%d, " - "mode=%d\n", p->index, p->name, - p->capability, p->mode); - break; - } - case VIDIOC_S_AUDOUT: - { - struct v4l2_audioout *p = arg; + if (ret) + return ret; - dbgarg(cmd, "index=%d, name=%s, capability=%d, " - "mode=%d\n", p->index, p->name, - p->capability, p->mode); + if (p->type < V4L2_BUF_TYPE_PRIVATE) + CLEAR_AFTER_FIELD(p, memory); - ret = ops->vidioc_s_audout(file, fh, p); - break; - } - case VIDIOC_G_MODULATOR: - { - struct v4l2_modulator *p = arg; - - ret = ops->vidioc_g_modulator(file, fh, p); - if (!ret) - dbgarg(cmd, "index=%d, name=%s, " - "capability=%d, rangelow=%d," - " rangehigh=%d, txsubchans=%d\n", - p->index, p->name, p->capability, - p->rangelow, p->rangehigh, - p->txsubchans); - break; - } - case VIDIOC_S_MODULATOR: - { - struct v4l2_modulator *p = arg; - - dbgarg(cmd, "index=%d, name=%s, capability=%d, " - "rangelow=%d, rangehigh=%d, txsubchans=%d\n", - p->index, p->name, p->capability, p->rangelow, - p->rangehigh, p->txsubchans); - ret = ops->vidioc_s_modulator(file, fh, p); - break; - } - case VIDIOC_G_CROP: - { - struct v4l2_crop *p = arg; + return ops->vidioc_reqbufs(file, fh, p); +} - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); +static int v4l_querybuf(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_buffer *p = arg; + int ret = check_fmt(ops, p->type); - if (ops->vidioc_g_crop) { - ret = ops->vidioc_g_crop(file, fh, p); - } else { - /* simulate capture crop using selection api */ - struct v4l2_selection s = { - .type = p->type, - }; - - /* crop means compose for output devices */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE; - else - s.target = V4L2_SEL_TGT_CROP_ACTIVE; - - ret = ops->vidioc_g_selection(file, fh, &s); - - /* copying results to old structure on success */ - if (!ret) - p->c = s.r; - } + return ret ? ret : ops->vidioc_querybuf(file, fh, p); +} - if (!ret) - dbgrect(vfd, "", &p->c); - break; - } - case VIDIOC_S_CROP: - { - struct v4l2_crop *p = arg; +static int v4l_qbuf(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_buffer *p = arg; + int ret = check_fmt(ops, p->type); - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); - dbgrect(vfd, "", &p->c); + return ret ? ret : ops->vidioc_qbuf(file, fh, p); +} - if (ops->vidioc_s_crop) { - ret = ops->vidioc_s_crop(file, fh, p); - } else { - /* simulate capture crop using selection api */ - struct v4l2_selection s = { - .type = p->type, - .r = p->c, - }; - - /* crop means compose for output devices */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE; - else - s.target = V4L2_SEL_TGT_CROP_ACTIVE; - - ret = ops->vidioc_s_selection(file, fh, &s); - } - break; - } - case VIDIOC_G_SELECTION: - { - struct v4l2_selection *p = arg; +static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_buffer *p = arg; + int ret = check_fmt(ops, p->type); - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); + return ret ? ret : ops->vidioc_dqbuf(file, fh, p); +} - ret = ops->vidioc_g_selection(file, fh, p); - if (!ret) - dbgrect(vfd, "", &p->r); - break; - } - case VIDIOC_S_SELECTION: - { - struct v4l2_selection *p = arg; +static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_create_buffers *create = arg; + int ret = check_fmt(ops, create->format.type); + return ret ? ret : ops->vidioc_create_bufs(file, fh, create); +} - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); - dbgrect(vfd, "", &p->r); +static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_buffer *b = arg; + int ret = check_fmt(ops, b->type); - ret = ops->vidioc_s_selection(file, fh, p); - break; - } - case VIDIOC_CROPCAP: - { - struct v4l2_cropcap *p = arg; - - /*FIXME: Should also show v4l2_fract pixelaspect */ - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); - if (ops->vidioc_cropcap) { - ret = ops->vidioc_cropcap(file, fh, p); - } else { - struct v4l2_selection s = { .type = p->type }; + return ret ? ret : ops->vidioc_prepare_buf(file, fh, b); +} - /* obtaining bounds */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; - else - s.target = V4L2_SEL_TGT_CROP_BOUNDS; +static int v4l_g_parm(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_streamparm *p = arg; + v4l2_std_id std; + int ret = check_fmt(ops, p->type); - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - break; - p->bounds = s.r; + if (ret) + return ret; + if (ops->vidioc_g_parm) + return ops->vidioc_g_parm(file, fh, p); + std = vfd->current_norm; + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + p->parm.capture.readbuffers = 2; + if (ops->vidioc_g_std) + ret = ops->vidioc_g_std(file, fh, &std); + if (ret == 0) + v4l2_video_std_frame_period(std, + &p->parm.capture.timeperframe); + return ret; +} - /* obtaining defrect */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT; - else - s.target = V4L2_SEL_TGT_CROP_DEFAULT; +static int v4l_s_parm(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_streamparm *p = arg; + int ret = check_fmt(ops, p->type); - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - break; - p->defrect = s.r; + return ret ? ret : ops->vidioc_s_parm(file, fh, p); +} - /* setting trivial pixelaspect */ - p->pixelaspect.numerator = 1; - p->pixelaspect.denominator = 1; - } +static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_queryctrl *p = arg; + struct v4l2_fh *vfh = fh; + + if (vfh && vfh->ctrl_handler) + return v4l2_queryctrl(vfh->ctrl_handler, p); + if (vfd->ctrl_handler) + return v4l2_queryctrl(vfd->ctrl_handler, p); + if (ops->vidioc_queryctrl) + return ops->vidioc_queryctrl(file, fh, p); + return -ENOTTY; +} - if (!ret) { - dbgrect(vfd, "bounds ", &p->bounds); - dbgrect(vfd, "defrect ", &p->defrect); - } - break; - } - case VIDIOC_G_JPEGCOMP: - { - struct v4l2_jpegcompression *p = arg; - - ret = ops->vidioc_g_jpegcomp(file, fh, p); - if (!ret) - dbgarg(cmd, "quality=%d, APPn=%d, " - "APP_len=%d, COM_len=%d, " - "jpeg_markers=%d\n", - p->quality, p->APPn, p->APP_len, - p->COM_len, p->jpeg_markers); - break; - } - case VIDIOC_S_JPEGCOMP: - { - struct v4l2_jpegcompression *p = arg; - - dbgarg(cmd, "quality=%d, APPn=%d, APP_len=%d, " - "COM_len=%d, jpeg_markers=%d\n", - p->quality, p->APPn, p->APP_len, - p->COM_len, p->jpeg_markers); - ret = ops->vidioc_s_jpegcomp(file, fh, p); - break; - } - case VIDIOC_G_ENC_INDEX: - { - struct v4l2_enc_idx *p = arg; - - ret = ops->vidioc_g_enc_index(file, fh, p); - if (!ret) - dbgarg(cmd, "entries=%d, entries_cap=%d\n", - p->entries, p->entries_cap); - break; - } - case VIDIOC_ENCODER_CMD: - { - struct v4l2_encoder_cmd *p = arg; +static int v4l_querymenu(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_querymenu *p = arg; + struct v4l2_fh *vfh = fh; + + if (vfh && vfh->ctrl_handler) + return v4l2_querymenu(vfh->ctrl_handler, p); + if (vfd->ctrl_handler) + return v4l2_querymenu(vfd->ctrl_handler, p); + if (ops->vidioc_querymenu) + return ops->vidioc_querymenu(file, fh, p); + return -ENOTTY; +} - ret = ops->vidioc_encoder_cmd(file, fh, p); - if (!ret) - dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); - break; +static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_control *p = arg; + struct v4l2_fh *vfh = fh; + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control ctrl; + + if (vfh && vfh->ctrl_handler) + return v4l2_g_ctrl(vfh->ctrl_handler, p); + if (vfd->ctrl_handler) + return v4l2_g_ctrl(vfd->ctrl_handler, p); + if (ops->vidioc_g_ctrl) + return ops->vidioc_g_ctrl(file, fh, p); + if (ops->vidioc_g_ext_ctrls == NULL) + return -ENOTTY; + + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); + ctrls.count = 1; + ctrls.controls = &ctrl; + ctrl.id = p->id; + ctrl.value = p->value; + if (check_ext_ctrls(&ctrls, 1)) { + int ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls); + + if (ret == 0) + p->value = ctrl.value; + return ret; } - case VIDIOC_TRY_ENCODER_CMD: - { - struct v4l2_encoder_cmd *p = arg; + return -EINVAL; +} - ret = ops->vidioc_try_encoder_cmd(file, fh, p); - if (!ret) - dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); - break; - } - case VIDIOC_DECODER_CMD: - { - struct v4l2_decoder_cmd *p = arg; +static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_control *p = arg; + struct v4l2_fh *vfh = fh; + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control ctrl; + + if (vfh && vfh->ctrl_handler) + return v4l2_s_ctrl(vfh, vfh->ctrl_handler, p); + if (vfd->ctrl_handler) + return v4l2_s_ctrl(NULL, vfd->ctrl_handler, p); + if (ops->vidioc_s_ctrl) + return ops->vidioc_s_ctrl(file, fh, p); + if (ops->vidioc_s_ext_ctrls == NULL) + return -ENOTTY; + + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); + ctrls.count = 1; + ctrls.controls = &ctrl; + ctrl.id = p->id; + ctrl.value = p->value; + if (check_ext_ctrls(&ctrls, 1)) + return ops->vidioc_s_ext_ctrls(file, fh, &ctrls); + return -EINVAL; +} - ret = ops->vidioc_decoder_cmd(file, fh, p); - if (!ret) - dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); - break; - } - case VIDIOC_TRY_DECODER_CMD: - { - struct v4l2_decoder_cmd *p = arg; +static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_ext_controls *p = arg; + struct v4l2_fh *vfh = fh; + + p->error_idx = p->count; + if (vfh && vfh->ctrl_handler) + return v4l2_g_ext_ctrls(vfh->ctrl_handler, p); + if (vfd->ctrl_handler) + return v4l2_g_ext_ctrls(vfd->ctrl_handler, p); + if (ops->vidioc_g_ext_ctrls == NULL) + return -ENOTTY; + return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) : + -EINVAL; +} - ret = ops->vidioc_try_decoder_cmd(file, fh, p); - if (!ret) - dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); - break; - } - case VIDIOC_G_PARM: - { - struct v4l2_streamparm *p = arg; +static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_ext_controls *p = arg; + struct v4l2_fh *vfh = fh; + + p->error_idx = p->count; + if (vfh && vfh->ctrl_handler) + return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p); + if (vfd->ctrl_handler) + return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p); + if (ops->vidioc_s_ext_ctrls == NULL) + return -ENOTTY; + return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) : + -EINVAL; +} - if (ops->vidioc_g_parm) { - ret = check_fmt(ops, p->type); - if (ret) - break; - ret = ops->vidioc_g_parm(file, fh, p); - } else { - v4l2_std_id std = vfd->current_norm; +static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_ext_controls *p = arg; + struct v4l2_fh *vfh = fh; + + p->error_idx = p->count; + if (vfh && vfh->ctrl_handler) + return v4l2_try_ext_ctrls(vfh->ctrl_handler, p); + if (vfd->ctrl_handler) + return v4l2_try_ext_ctrls(vfd->ctrl_handler, p); + if (ops->vidioc_try_ext_ctrls == NULL) + return -ENOTTY; + return check_ext_ctrls(p, 0) ? ops->vidioc_try_ext_ctrls(file, fh, p) : + -EINVAL; +} - ret = -EINVAL; - if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - break; +static int v4l_g_crop(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_crop *p = arg; + struct v4l2_selection s = { + .type = p->type, + }; + int ret; + + if (ops->vidioc_g_crop) + return ops->vidioc_g_crop(file, fh, p); + /* simulate capture crop using selection api */ + + /* crop means compose for output devices */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE; + else + s.target = V4L2_SEL_TGT_CROP_ACTIVE; + + ret = ops->vidioc_g_selection(file, fh, &s); + + /* copying results to old structure on success */ + if (!ret) + p->c = s.r; + return ret; +} - ret = 0; - p->parm.capture.readbuffers = 2; - if (ops->vidioc_g_std) - ret = ops->vidioc_g_std(file, fh, &std); - if (ret == 0) - v4l2_video_std_frame_period(std, - &p->parm.capture.timeperframe); - } +static int v4l_s_crop(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_crop *p = arg; + struct v4l2_selection s = { + .type = p->type, + .r = p->c, + }; + + if (ops->vidioc_s_crop) + return ops->vidioc_s_crop(file, fh, p); + /* simulate capture crop using selection api */ + + /* crop means compose for output devices */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE; + else + s.target = V4L2_SEL_TGT_CROP_ACTIVE; + + return ops->vidioc_s_selection(file, fh, &s); +} - dbgarg(cmd, "type=%d\n", p->type); - break; - } - case VIDIOC_S_PARM: - { - struct v4l2_streamparm *p = arg; +static int v4l_cropcap(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_cropcap *p = arg; + struct v4l2_selection s = { .type = p->type }; + int ret; - ret = check_fmt(ops, p->type); - if (ret) - break; + if (ops->vidioc_cropcap) + return ops->vidioc_cropcap(file, fh, p); - dbgarg(cmd, "type=%d\n", p->type); - ret = ops->vidioc_s_parm(file, fh, p); - break; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *p = arg; + /* obtaining bounds */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; + else + s.target = V4L2_SEL_TGT_CROP_BOUNDS; - p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - ret = ops->vidioc_g_tuner(file, fh, p); - if (!ret) - dbgarg(cmd, "index=%d, name=%s, type=%d, " - "capability=0x%x, rangelow=%d, " - "rangehigh=%d, signal=%d, afc=%d, " - "rxsubchans=0x%x, audmode=%d\n", - p->index, p->name, p->type, - p->capability, p->rangelow, - p->rangehigh, p->signal, p->afc, - p->rxsubchans, p->audmode); - break; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *p = arg; + ret = ops->vidioc_g_selection(file, fh, &s); + if (ret) + return ret; + p->bounds = s.r; - p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - dbgarg(cmd, "index=%d, name=%s, type=%d, " - "capability=0x%x, rangelow=%d, " - "rangehigh=%d, signal=%d, afc=%d, " - "rxsubchans=0x%x, audmode=%d\n", - p->index, p->name, p->type, - p->capability, p->rangelow, - p->rangehigh, p->signal, p->afc, - p->rxsubchans, p->audmode); - ret = ops->vidioc_s_tuner(file, fh, p); - break; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *p = arg; + /* obtaining defrect */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT; + else + s.target = V4L2_SEL_TGT_CROP_DEFAULT; - p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - ret = ops->vidioc_g_frequency(file, fh, p); - if (!ret) - dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n", - p->tuner, p->type, p->frequency); - break; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *p = arg; - enum v4l2_tuner_type type; + ret = ops->vidioc_g_selection(file, fh, &s); + if (ret) + return ret; + p->defrect = s.r; - type = (vfd->vfl_type == VFL_TYPE_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n", - p->tuner, p->type, p->frequency); - if (p->type != type) - ret = -EINVAL; - else - ret = ops->vidioc_s_frequency(file, fh, p); - break; - } - case VIDIOC_G_SLICED_VBI_CAP: - { - struct v4l2_sliced_vbi_cap *p = arg; + /* setting trivial pixelaspect */ + p->pixelaspect.numerator = 1; + p->pixelaspect.denominator = 1; + return 0; +} - /* Clear up to type, everything after type is zerod already */ - memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type)); +static int v4l_log_status(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + int ret; + + if (vfd->v4l2_dev) + pr_info("%s: ================= START STATUS =================\n", + vfd->v4l2_dev->name); + ret = ops->vidioc_log_status(file, fh); + if (vfd->v4l2_dev) + pr_info("%s: ================== END STATUS ==================\n", + vfd->v4l2_dev->name); + return ret; +} - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); - ret = ops->vidioc_g_sliced_vbi_cap(file, fh, p); - if (!ret) - dbgarg2("service_set=%d\n", p->service_set); - break; - } - case VIDIOC_LOG_STATUS: - { - if (vfd->v4l2_dev) - pr_info("%s: ================= START STATUS =================\n", - vfd->v4l2_dev->name); - ret = ops->vidioc_log_status(file, fh); - if (vfd->v4l2_dev) - pr_info("%s: ================== END STATUS ==================\n", - vfd->v4l2_dev->name); - break; - } - case VIDIOC_DBG_G_REGISTER: - { +static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ #ifdef CONFIG_VIDEO_ADV_DEBUG - struct v4l2_dbg_register *p = arg; + struct v4l2_dbg_register *p = arg; - if (!capable(CAP_SYS_ADMIN)) - ret = -EPERM; - else - ret = ops->vidioc_g_register(file, fh, p); + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return ops->vidioc_g_register(file, fh, p); +#else + return -ENOTTY; #endif - break; - } - case VIDIOC_DBG_S_REGISTER: - { +} + +static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ #ifdef CONFIG_VIDEO_ADV_DEBUG - struct v4l2_dbg_register *p = arg; + struct v4l2_dbg_register *p = arg; - if (!capable(CAP_SYS_ADMIN)) - ret = -EPERM; - else - ret = ops->vidioc_s_register(file, fh, p); + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return ops->vidioc_s_register(file, fh, p); +#else + return -ENOTTY; #endif - break; - } - case VIDIOC_DBG_G_CHIP_IDENT: - { - struct v4l2_dbg_chip_ident *p = arg; - - p->ident = V4L2_IDENT_NONE; - p->revision = 0; - ret = ops->vidioc_g_chip_ident(file, fh, p); - if (!ret) - dbgarg(cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision); - break; - } - case VIDIOC_S_HW_FREQ_SEEK: - { - struct v4l2_hw_freq_seek *p = arg; - enum v4l2_tuner_type type; +} - type = (vfd->vfl_type == VFL_TYPE_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - dbgarg(cmd, - "tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u\n", - p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing); - if (p->type != type) - ret = -EINVAL; - else - ret = ops->vidioc_s_hw_freq_seek(file, fh, p); - break; - } - case VIDIOC_ENUM_FRAMESIZES: - { - struct v4l2_frmsizeenum *p = arg; +static int v4l_dbg_g_chip_ident(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_dbg_chip_ident *p = arg; - ret = ops->vidioc_enum_framesizes(file, fh, p); - dbgarg(cmd, - "index=%d, pixelformat=%c%c%c%c, type=%d ", - p->index, - (p->pixel_format & 0xff), - (p->pixel_format >> 8) & 0xff, - (p->pixel_format >> 16) & 0xff, - (p->pixel_format >> 24) & 0xff, - p->type); - switch (p->type) { - case V4L2_FRMSIZE_TYPE_DISCRETE: - dbgarg3("width = %d, height=%d\n", - p->discrete.width, p->discrete.height); - break; - case V4L2_FRMSIZE_TYPE_STEPWISE: - dbgarg3("min %dx%d, max %dx%d, step %dx%d\n", - p->stepwise.min_width, p->stepwise.min_height, - p->stepwise.step_width, p->stepwise.step_height, - p->stepwise.max_width, p->stepwise.max_height); - break; - case V4L2_FRMSIZE_TYPE_CONTINUOUS: - dbgarg3("continuous\n"); - break; - default: - dbgarg3("- Unknown type!\n"); - } + p->ident = V4L2_IDENT_NONE; + p->revision = 0; + return ops->vidioc_g_chip_ident(file, fh, p); +} - break; - } - case VIDIOC_ENUM_FRAMEINTERVALS: - { - struct v4l2_frmivalenum *p = arg; - - ret = ops->vidioc_enum_frameintervals(file, fh, p); - dbgarg(cmd, - "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ", - p->index, p->pixel_format, - p->width, p->height, p->type); - switch (p->type) { - case V4L2_FRMIVAL_TYPE_DISCRETE: - dbgarg2("fps=%d/%d\n", - p->discrete.numerator, - p->discrete.denominator); - break; - case V4L2_FRMIVAL_TYPE_STEPWISE: - dbgarg2("min=%d/%d, max=%d/%d, step=%d/%d\n", - p->stepwise.min.numerator, - p->stepwise.min.denominator, - p->stepwise.max.numerator, - p->stepwise.max.denominator, - p->stepwise.step.numerator, - p->stepwise.step.denominator); - break; - case V4L2_FRMIVAL_TYPE_CONTINUOUS: - dbgarg2("continuous\n"); - break; - default: - dbgarg2("- Unknown type!\n"); - } - break; - } - case VIDIOC_ENUM_DV_PRESETS: - { - struct v4l2_dv_enum_preset *p = arg; - - ret = ops->vidioc_enum_dv_presets(file, fh, p); - if (!ret) - dbgarg(cmd, - "index=%d, preset=%d, name=%s, width=%d," - " height=%d ", - p->index, p->preset, p->name, p->width, - p->height); - break; - } - case VIDIOC_S_DV_PRESET: - { - struct v4l2_dv_preset *p = arg; +static int v4l_dqevent(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK); +} - dbgarg(cmd, "preset=%d\n", p->preset); - ret = ops->vidioc_s_dv_preset(file, fh, p); - break; - } - case VIDIOC_G_DV_PRESET: - { - struct v4l2_dv_preset *p = arg; +static int v4l_subscribe_event(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + return ops->vidioc_subscribe_event(fh, arg); +} - ret = ops->vidioc_g_dv_preset(file, fh, p); - if (!ret) - dbgarg(cmd, "preset=%d\n", p->preset); - break; - } - case VIDIOC_QUERY_DV_PRESET: - { - struct v4l2_dv_preset *p = arg; +static int v4l_unsubscribe_event(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + return ops->vidioc_unsubscribe_event(fh, arg); +} - ret = ops->vidioc_query_dv_preset(file, fh, p); - if (!ret) - dbgarg(cmd, "preset=%d\n", p->preset); - break; - } - case VIDIOC_S_DV_TIMINGS: - { - struct v4l2_dv_timings *p = arg; - - dbgtimings(vfd, p); - switch (p->type) { - case V4L2_DV_BT_656_1120: - ret = ops->vidioc_s_dv_timings(file, fh, p); - break; - default: - ret = -EINVAL; - break; - } - break; - } - case VIDIOC_G_DV_TIMINGS: - { - struct v4l2_dv_timings *p = arg; +static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_sliced_vbi_cap *p = arg; - ret = ops->vidioc_g_dv_timings(file, fh, p); - if (!ret) - dbgtimings(vfd, p); - break; - } - case VIDIOC_ENUM_DV_TIMINGS: - { - struct v4l2_enum_dv_timings *p = arg; + /* Clear up to type, everything after type is zeroed already */ + memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type)); - if (!ops->vidioc_enum_dv_timings) - break; + return ops->vidioc_g_sliced_vbi_cap(file, fh, p); +} - ret = ops->vidioc_enum_dv_timings(file, fh, p); - if (!ret) { - dbgarg(cmd, "index=%d: ", p->index); - dbgtimings(vfd, &p->timings); - } - break; +struct v4l2_ioctl_info { + unsigned int ioctl; + u32 flags; + const char * const name; + union { + u32 offset; + int (*func)(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *p); + }; + void (*debug)(const void *arg, bool write_only); +}; + +/* This control needs a priority check */ +#define INFO_FL_PRIO (1 << 0) +/* This control can be valid if the filehandle passes a control handler. */ +#define INFO_FL_CTRL (1 << 1) +/* This is a standard ioctl, no need for special code */ +#define INFO_FL_STD (1 << 2) +/* This is ioctl has its own function */ +#define INFO_FL_FUNC (1 << 3) +/* Queuing ioctl */ +#define INFO_FL_QUEUE (1 << 4) +/* Zero struct from after the field to the end */ +#define INFO_FL_CLEAR(v4l2_struct, field) \ + ((offsetof(struct v4l2_struct, field) + \ + sizeof(((struct v4l2_struct *)0)->field)) << 16) +#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16) + +#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \ + [_IOC_NR(_ioctl)] = { \ + .ioctl = _ioctl, \ + .flags = _flags | INFO_FL_STD, \ + .name = #_ioctl, \ + .offset = offsetof(struct v4l2_ioctl_ops, _vidioc), \ + .debug = _debug, \ + } + +#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags) \ + [_IOC_NR(_ioctl)] = { \ + .ioctl = _ioctl, \ + .flags = _flags | INFO_FL_FUNC, \ + .name = #_ioctl, \ + .func = _func, \ + .debug = _debug, \ } - case VIDIOC_QUERY_DV_TIMINGS: - { - struct v4l2_dv_timings *p = arg; - if (!ops->vidioc_query_dv_timings) - break; +static struct v4l2_ioctl_info v4l2_ioctls[] = { + IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0), + IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)), + IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)), + IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO), + IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)), + IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0), + IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO), + IOCTL_INFO_STD(VIDIOC_OVERLAY, vidioc_overlay, v4l_print_u32, INFO_FL_PRIO), + IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)), + IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO), + IOCTL_INFO_FNC(VIDIOC_G_STD, v4l_g_std, v4l_print_std, 0), + IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO), + IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)), + IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)), + IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)), + IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL), + IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)), + IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO), + IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0), + IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO), + IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)), + IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)), + IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0), + IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO), + IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0), + IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO), + IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)), + IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0), + IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO), + IOCTL_INFO_STD(VIDIOC_G_MODULATOR, vidioc_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)), + IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO), + IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)), + IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO), + IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)), + IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)), + IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO), + IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, 0), + IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO), + IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0), + IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO), + IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0), + IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0), + IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)), + IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)), + IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0), + IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO), + IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)), + IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0), + IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL), + IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL), + IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, 0), + IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)), + IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)), + IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0), + IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)), + IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)), + IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO), + IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0), + IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0), + IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0), + IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_IDENT, v4l_dbg_g_chip_ident, v4l_print_dbg_chip_ident, 0), + IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO), + IOCTL_INFO_STD(VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets, v4l_print_dv_enum_presets, 0), + IOCTL_INFO_STD(VIDIOC_S_DV_PRESET, vidioc_s_dv_preset, v4l_print_dv_preset, INFO_FL_PRIO), + IOCTL_INFO_STD(VIDIOC_G_DV_PRESET, vidioc_g_dv_preset, v4l_print_dv_preset, 0), + IOCTL_INFO_STD(VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset, v4l_print_dv_preset, 0), + IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO), + IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0), + IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0), + IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0), + IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0), + IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE), + IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0), + IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0), + IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, 0), +}; +#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) - ret = ops->vidioc_query_dv_timings(file, fh, p); - if (!ret) - dbgtimings(vfd, p); - break; - } - case VIDIOC_DV_TIMINGS_CAP: - { - struct v4l2_dv_timings_cap *p = arg; +bool v4l2_is_known_ioctl(unsigned int cmd) +{ + if (_IOC_NR(cmd) >= V4L2_IOCTLS) + return false; + return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; +} - if (!ops->vidioc_dv_timings_cap) - break; +struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd) +{ + if (_IOC_NR(cmd) >= V4L2_IOCTLS) + return vdev->lock; + if (test_bit(_IOC_NR(cmd), vdev->disable_locking)) + return NULL; + if (vdev->queue && vdev->queue->lock && + (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE)) + return vdev->queue->lock; + return vdev->lock; +} - ret = ops->vidioc_dv_timings_cap(file, fh, p); - if (ret) - break; - switch (p->type) { - case V4L2_DV_BT_656_1120: - dbgarg(cmd, - "type=%d, width=%u-%u, height=%u-%u, " - "pixelclock=%llu-%llu, standards=%x, capabilities=%x ", - p->type, - p->bt.min_width, p->bt.max_width, - p->bt.min_height, p->bt.max_height, - p->bt.min_pixelclock, p->bt.max_pixelclock, - p->bt.standards, p->bt.capabilities); - break; - default: - dbgarg(cmd, "unknown type "); - break; - } - break; - } - case VIDIOC_DQEVENT: - { - struct v4l2_event *ev = arg; +/* Common ioctl debug function. This function can be used by + external ioctl messages as well as internal V4L ioctl */ +void v4l_printk_ioctl(const char *prefix, unsigned int cmd) +{ + const char *dir, *type; - ret = v4l2_event_dequeue(fh, ev, file->f_flags & O_NONBLOCK); - if (ret < 0) { - dbgarg(cmd, "no pending events?"); - break; - } - dbgarg(cmd, - "pending=%d, type=0x%8.8x, sequence=%d, " - "timestamp=%lu.%9.9lu ", - ev->pending, ev->type, ev->sequence, - ev->timestamp.tv_sec, ev->timestamp.tv_nsec); - break; - } - case VIDIOC_SUBSCRIBE_EVENT: - { - struct v4l2_event_subscription *sub = arg; + if (prefix) + printk(KERN_DEBUG "%s: ", prefix); - ret = ops->vidioc_subscribe_event(fh, sub); - if (ret < 0) { - dbgarg(cmd, "failed, ret=%ld", ret); - break; - } - dbgarg(cmd, "type=0x%8.8x", sub->type); + switch (_IOC_TYPE(cmd)) { + case 'd': + type = "v4l2_int"; break; - } - case VIDIOC_UNSUBSCRIBE_EVENT: - { - struct v4l2_event_subscription *sub = arg; - - ret = ops->vidioc_unsubscribe_event(fh, sub); - if (ret < 0) { - dbgarg(cmd, "failed, ret=%ld", ret); + case 'V': + if (_IOC_NR(cmd) >= V4L2_IOCTLS) { + type = "v4l2"; break; } - dbgarg(cmd, "type=0x%8.8x", sub->type); + pr_cont("%s", v4l2_ioctls[_IOC_NR(cmd)].name); + return; + default: + type = "unknown"; break; } - case VIDIOC_CREATE_BUFS: - { - struct v4l2_create_buffers *create = arg; - ret = check_fmt(ops, create->format.type); - if (ret) - break; + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: dir = "--"; break; + case _IOC_READ: dir = "r-"; break; + case _IOC_WRITE: dir = "-w"; break; + case _IOC_READ | _IOC_WRITE: dir = "rw"; break; + default: dir = "*ERR*"; break; + } + pr_cont("%s ioctl '%c', dir=%s, #%d (0x%08x)", + type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd); +} +EXPORT_SYMBOL(v4l_printk_ioctl); - ret = ops->vidioc_create_bufs(file, fh, create); +static long __video_do_ioctl(struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vfd = video_devdata(file); + const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; + bool write_only = false; + struct v4l2_ioctl_info default_info; + const struct v4l2_ioctl_info *info; + void *fh = file->private_data; + struct v4l2_fh *vfh = NULL; + int use_fh_prio = 0; + int debug = vfd->debug; + long ret = -ENOTTY; - dbgarg(cmd, "count=%d @ %d\n", create->count, create->index); - break; + if (ops == NULL) { + pr_warn("%s: has no ioctl_ops.\n", + video_device_node_name(vfd)); + return ret; } - case VIDIOC_PREPARE_BUF: - { - struct v4l2_buffer *b = arg; - ret = check_fmt(ops, b->type); - if (ret) - break; + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { + vfh = file->private_data; + use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); + } - ret = ops->vidioc_prepare_buf(file, fh, b); + if (v4l2_is_known_ioctl(cmd)) { + info = &v4l2_ioctls[_IOC_NR(cmd)]; - dbgarg(cmd, "index=%d", b->index); - break; - } - default: - if (!ops->vidioc_default) - break; - ret = ops->vidioc_default(file, fh, use_fh_prio ? - v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0, - cmd, arg); - break; - } /* switch */ + if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) && + !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler)) + goto done; - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { - if (ret < 0) { - v4l_print_ioctl(vfd->name, cmd); - printk(KERN_CONT " error %ld\n", ret); + if (use_fh_prio && (info->flags & INFO_FL_PRIO)) { + ret = v4l2_prio_check(vfd->prio, vfh->prio); + if (ret) + goto done; + } + } else { + default_info.ioctl = cmd; + default_info.flags = 0; + default_info.debug = v4l_print_default; + info = &default_info; + } + + write_only = _IOC_DIR(cmd) == _IOC_WRITE; + if (write_only && debug > V4L2_DEBUG_IOCTL) { + v4l_printk_ioctl(video_device_node_name(vfd), cmd); + pr_cont(": "); + info->debug(arg, write_only); + } + if (info->flags & INFO_FL_STD) { + typedef int (*vidioc_op)(struct file *file, void *fh, void *p); + const void *p = vfd->ioctl_ops; + const vidioc_op *vidioc = p + info->offset; + + ret = (*vidioc)(file, fh, arg); + } else if (info->flags & INFO_FL_FUNC) { + ret = info->func(ops, file, fh, arg); + } else if (!ops->vidioc_default) { + ret = -ENOTTY; + } else { + ret = ops->vidioc_default(file, fh, + use_fh_prio ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0, + cmd, arg); + } + +done: + if (debug) { + if (write_only && debug > V4L2_DEBUG_IOCTL) { + if (ret < 0) + printk(KERN_DEBUG "%s: error %ld\n", + video_device_node_name(vfd), ret); + return ret; + } + v4l_printk_ioctl(video_device_node_name(vfd), cmd); + if (ret < 0) + pr_cont(": error %ld\n", ret); + else if (debug == V4L2_DEBUG_IOCTL) + pr_cont("\n"); + else if (_IOC_DIR(cmd) == _IOC_NONE) + info->debug(arg, write_only); + else { + pr_cont(": "); + info->debug(arg, write_only); } } return ret; } -/* In some cases, only a few fields are used as input, i.e. when the app sets - * "index" and then the driver fills in the rest of the structure for the thing - * with that index. We only need to copy up the first non-input field. */ -static unsigned long cmd_input_size(unsigned int cmd) -{ - /* Size of structure up to and including 'field' */ -#define CMDINSIZE(cmd, type, field) \ - case VIDIOC_##cmd: \ - return offsetof(struct v4l2_##type, field) + \ - sizeof(((struct v4l2_##type *)0)->field); - - switch (cmd) { - CMDINSIZE(ENUM_FMT, fmtdesc, type); - CMDINSIZE(G_FMT, format, type); - CMDINSIZE(QUERYBUF, buffer, length); - CMDINSIZE(G_PARM, streamparm, type); - CMDINSIZE(ENUMSTD, standard, index); - CMDINSIZE(ENUMINPUT, input, index); - CMDINSIZE(G_CTRL, control, id); - CMDINSIZE(G_TUNER, tuner, index); - CMDINSIZE(QUERYCTRL, queryctrl, id); - CMDINSIZE(QUERYMENU, querymenu, index); - CMDINSIZE(ENUMOUTPUT, output, index); - CMDINSIZE(G_MODULATOR, modulator, index); - CMDINSIZE(G_FREQUENCY, frequency, tuner); - CMDINSIZE(CROPCAP, cropcap, type); - CMDINSIZE(G_CROP, crop, type); - CMDINSIZE(ENUMAUDIO, audio, index); - CMDINSIZE(ENUMAUDOUT, audioout, index); - CMDINSIZE(ENCODER_CMD, encoder_cmd, flags); - CMDINSIZE(TRY_ENCODER_CMD, encoder_cmd, flags); - CMDINSIZE(G_SLICED_VBI_CAP, sliced_vbi_cap, type); - CMDINSIZE(ENUM_FRAMESIZES, frmsizeenum, pixel_format); - CMDINSIZE(ENUM_FRAMEINTERVALS, frmivalenum, height); - default: - return _IOC_SIZE(cmd); - } -} - static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, void * __user *user_ptr, void ***kernel_ptr) { @@ -2219,7 +2149,20 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, err = -EFAULT; if (_IOC_DIR(cmd) & _IOC_WRITE) { - unsigned long n = cmd_input_size(cmd); + unsigned int n = _IOC_SIZE(cmd); + + /* + * In some cases, only a few fields are used as input, + * i.e. when the app sets "index" and then the driver + * fills in the rest of the structure for the thing + * with that index. We only need to copy up the first + * non-input field. + */ + if (v4l2_is_known_ioctl(cmd)) { + u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags; + if (flags & INFO_FL_CLEAR_MASK) + n = (flags & INFO_FL_CLEAR_MASK) >> 16; + } if (copy_from_user(parg, (void __user *)arg, n)) goto out; diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index db6e859b93d4..9182f81deb5b 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -245,7 +245,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; - sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + sel.target = V4L2_SEL_TGT_CROP; rval = v4l2_subdev_call( sd, pad, get_selection, subdev_fh, &sel); @@ -274,7 +274,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; - sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + sel.target = V4L2_SEL_TGT_CROP; sel.r = crop->rect; rval = v4l2_subdev_call( diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index ffdf59cfe405..bf7a326b1cdc 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -359,11 +359,6 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, break; } - if (vb->input != UNSET) { - b->flags |= V4L2_BUF_FLAG_INPUT; - b->input = vb->input; - } - b->field = vb->field; b->timestamp = vb->ts; b->bytesused = vb->size; @@ -402,7 +397,6 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, break; q->bufs[i]->i = i; - q->bufs[i]->input = UNSET; q->bufs[i]->memory = memory; q->bufs[i]->bsize = bsize; switch (memory) { @@ -566,16 +560,6 @@ int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b) goto done; } - if (b->flags & V4L2_BUF_FLAG_INPUT) { - if (b->input >= q->inputs) { - dprintk(1, "qbuf: wrong input.\n"); - goto done; - } - buf->input = b->input; - } else { - buf->input = UNSET; - } - switch (b->memory) { case V4L2_MEMORY_MMAP: if (0 == buf->baddr) { diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index b6b5cc1a43cb..9b9a06fdd0f0 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -40,7 +40,7 @@ struct videobuf_dma_contig_memory { static int __videobuf_dc_alloc(struct device *dev, struct videobuf_dma_contig_memory *mem, - unsigned long size, unsigned long flags) + unsigned long size, gfp_t flags) { mem->size = size; if (mem->cached) { diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 9d4e9edbd2e7..4e0290ab5071 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -336,9 +336,9 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) struct vb2_queue *q = vb->vb2_queue; int ret; - /* Copy back data such as timestamp, flags, input, etc. */ + /* Copy back data such as timestamp, flags, etc. */ memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m)); - b->input = vb->v4l2_buf.input; + b->reserved2 = vb->v4l2_buf.reserved2; b->reserved = vb->v4l2_buf.reserved; if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) { @@ -454,7 +454,50 @@ static int __verify_mmap_ops(struct vb2_queue *q) } /** - * vb2_reqbufs() - Initiate streaming + * __verify_memory_type() - Check whether the memory type and buffer type + * passed to a buffer operation are compatible with the queue. + */ +static int __verify_memory_type(struct vb2_queue *q, + enum v4l2_memory memory, enum v4l2_buf_type type) +{ + if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR) { + dprintk(1, "reqbufs: unsupported memory type\n"); + return -EINVAL; + } + + if (type != q->type) { + dprintk(1, "reqbufs: requested type is incorrect\n"); + return -EINVAL; + } + + /* + * Make sure all the required memory ops for given memory type + * are available. + */ + if (memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) { + dprintk(1, "reqbufs: MMAP for current setup unsupported\n"); + return -EINVAL; + } + + if (memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) { + dprintk(1, "reqbufs: USERPTR for current setup unsupported\n"); + return -EINVAL; + } + + /* + * Place the busy tests at the end: -EBUSY can be ignored when + * create_bufs is called with count == 0, but count == 0 should still + * do the memory and type validation. + */ + if (q->fileio) { + dprintk(1, "reqbufs: file io in progress\n"); + return -EBUSY; + } + return 0; +} + +/** + * __reqbufs() - Initiate streaming * @q: videobuf2 queue * @req: struct passed from userspace to vidioc_reqbufs handler in driver * @@ -476,46 +519,16 @@ static int __verify_mmap_ops(struct vb2_queue *q) * The return values from this function are intended to be directly returned * from vidioc_reqbufs handler in driver. */ -int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) +static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) { unsigned int num_buffers, allocated_buffers, num_planes = 0; - int ret = 0; - - if (q->fileio) { - dprintk(1, "reqbufs: file io in progress\n"); - return -EBUSY; - } - - if (req->memory != V4L2_MEMORY_MMAP - && req->memory != V4L2_MEMORY_USERPTR) { - dprintk(1, "reqbufs: unsupported memory type\n"); - return -EINVAL; - } - - if (req->type != q->type) { - dprintk(1, "reqbufs: requested type is incorrect\n"); - return -EINVAL; - } + int ret; if (q->streaming) { dprintk(1, "reqbufs: streaming active\n"); return -EBUSY; } - /* - * Make sure all the required memory ops for given memory type - * are available. - */ - if (req->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) { - dprintk(1, "reqbufs: MMAP for current setup unsupported\n"); - return -EINVAL; - } - - if (req->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) { - dprintk(1, "reqbufs: USERPTR for current setup unsupported\n"); - return -EINVAL; - } - if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) { /* * We already have buffers allocated, so first check if they @@ -595,10 +608,23 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) return 0; } + +/** + * vb2_reqbufs() - Wrapper for __reqbufs() that also verifies the memory and + * type values. + * @q: videobuf2 queue + * @req: struct passed from userspace to vidioc_reqbufs handler in driver + */ +int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) +{ + int ret = __verify_memory_type(q, req->memory, req->type); + + return ret ? ret : __reqbufs(q, req); +} EXPORT_SYMBOL_GPL(vb2_reqbufs); /** - * vb2_create_bufs() - Allocate buffers and any required auxiliary structs + * __create_bufs() - Allocate buffers and any required auxiliary structs * @q: videobuf2 queue * @create: creation parameters, passed from userspace to vidioc_create_bufs * handler in driver @@ -612,40 +638,10 @@ EXPORT_SYMBOL_GPL(vb2_reqbufs); * The return values from this function are intended to be directly returned * from vidioc_create_bufs handler in driver. */ -int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) +static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) { unsigned int num_planes = 0, num_buffers, allocated_buffers; - int ret = 0; - - if (q->fileio) { - dprintk(1, "%s(): file io in progress\n", __func__); - return -EBUSY; - } - - if (create->memory != V4L2_MEMORY_MMAP - && create->memory != V4L2_MEMORY_USERPTR) { - dprintk(1, "%s(): unsupported memory type\n", __func__); - return -EINVAL; - } - - if (create->format.type != q->type) { - dprintk(1, "%s(): requested type is incorrect\n", __func__); - return -EINVAL; - } - - /* - * Make sure all the required memory ops for given memory type - * are available. - */ - if (create->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) { - dprintk(1, "%s(): MMAP for current setup unsupported\n", __func__); - return -EINVAL; - } - - if (create->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) { - dprintk(1, "%s(): USERPTR for current setup unsupported\n", __func__); - return -EINVAL; - } + int ret; if (q->num_buffers == VIDEO_MAX_FRAME) { dprintk(1, "%s(): maximum number of buffers already allocated\n", @@ -653,8 +649,6 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) return -ENOBUFS; } - create->index = q->num_buffers; - if (!q->num_buffers) { memset(q->plane_sizes, 0, sizeof(q->plane_sizes)); memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx)); @@ -675,9 +669,9 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) /* Finally, allocate buffers and video memory */ ret = __vb2_queue_alloc(q, create->memory, num_buffers, num_planes); - if (ret < 0) { - dprintk(1, "Memory allocation failed with error: %d\n", ret); - return ret; + if (ret == 0) { + dprintk(1, "Memory allocation failed\n"); + return -ENOMEM; } allocated_buffers = ret; @@ -708,7 +702,7 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) if (ret < 0) { __vb2_queue_free(q, allocated_buffers); - return ret; + return -ENOMEM; } /* @@ -719,6 +713,23 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) return 0; } + +/** + * vb2_reqbufs() - Wrapper for __reqbufs() that also verifies the memory and + * type values. + * @q: videobuf2 queue + * @create: creation parameters, passed from userspace to vidioc_create_bufs + * handler in driver + */ +int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) +{ + int ret = __verify_memory_type(q, create->memory, create->format.type); + + create->index = q->num_buffers; + if (create->count == 0) + return ret != -EBUSY ? ret : 0; + return ret ? ret : __create_bufs(q, create); +} EXPORT_SYMBOL_GPL(vb2_create_bufs); /** @@ -860,7 +871,6 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b, vb->v4l2_buf.field = b->field; vb->v4l2_buf.timestamp = b->timestamp; - vb->v4l2_buf.input = b->input; vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS; return 0; @@ -2115,6 +2125,263 @@ size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count, } EXPORT_SYMBOL_GPL(vb2_write); + +/* + * The following functions are not part of the vb2 core API, but are helper + * functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations + * and struct vb2_ops. + * They contain boilerplate code that most if not all drivers have to do + * and so they simplify the driver code. + */ + +/* The queue is busy if there is a owner and you are not that owner. */ +static inline bool vb2_queue_is_busy(struct video_device *vdev, struct file *file) +{ + return vdev->queue->owner && vdev->queue->owner != file->private_data; +} + +/* vb2 ioctl helpers */ + +int vb2_ioctl_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct video_device *vdev = video_devdata(file); + int res = __verify_memory_type(vdev->queue, p->memory, p->type); + + if (res) + return res; + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + res = __reqbufs(vdev->queue, p); + /* If count == 0, then the owner has released all buffers and he + is no longer owner of the queue. Otherwise we have a new owner. */ + if (res == 0) + vdev->queue->owner = p->count ? file->private_data : NULL; + return res; +} +EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs); + +int vb2_ioctl_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *p) +{ + struct video_device *vdev = video_devdata(file); + int res = __verify_memory_type(vdev->queue, p->memory, p->format.type); + + p->index = vdev->queue->num_buffers; + /* If count == 0, then just check if memory and type are valid. + Any -EBUSY result from __verify_memory_type can be mapped to 0. */ + if (p->count == 0) + return res != -EBUSY ? res : 0; + if (res) + return res; + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + res = __create_bufs(vdev->queue, p); + if (res == 0) + vdev->queue->owner = file->private_data; + return res; +} +EXPORT_SYMBOL_GPL(vb2_ioctl_create_bufs); + +int vb2_ioctl_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_prepare_buf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf); + +int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + /* No need to call vb2_queue_is_busy(), anyone can query buffers. */ + return vb2_querybuf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_querybuf); + +int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_qbuf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf); + +int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf); + +int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_streamon(vdev->queue, i); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_streamon); + +int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_streamoff(vdev->queue, i); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff); + +/* v4l2_file_operations helpers */ + +int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = video_devdata(file); + + return vb2_mmap(vdev->queue, vma); +} +EXPORT_SYMBOL_GPL(vb2_fop_mmap); + +int vb2_fop_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + + if (file->private_data == vdev->queue->owner) { + vb2_queue_release(vdev->queue); + vdev->queue->owner = NULL; + } + return v4l2_fh_release(file); +} +EXPORT_SYMBOL_GPL(vb2_fop_release); + +ssize_t vb2_fop_write(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + bool must_lock = !test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && lock; + int err = -EBUSY; + + if (must_lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + if (vb2_queue_is_busy(vdev, file)) + goto exit; + err = vb2_write(vdev->queue, buf, count, ppos, + file->f_flags & O_NONBLOCK); + if (err >= 0) + vdev->queue->owner = file->private_data; +exit: + if (must_lock) + mutex_unlock(lock); + return err; +} +EXPORT_SYMBOL_GPL(vb2_fop_write); + +ssize_t vb2_fop_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + bool must_lock = !test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && vdev->lock; + int err = -EBUSY; + + if (must_lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + if (vb2_queue_is_busy(vdev, file)) + goto exit; + err = vb2_read(vdev->queue, buf, count, ppos, + file->f_flags & O_NONBLOCK); + if (err >= 0) + vdev->queue->owner = file->private_data; +exit: + if (must_lock) + mutex_unlock(lock); + return err; +} +EXPORT_SYMBOL_GPL(vb2_fop_read); + +unsigned int vb2_fop_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = video_devdata(file); + struct vb2_queue *q = vdev->queue; + struct mutex *lock = q->lock ? q->lock : vdev->lock; + unsigned long req_events = poll_requested_events(wait); + unsigned res; + void *fileio; + /* Yuck. We really need to get rid of this flag asap. If it is + set, then the core took the serialization lock before calling + poll(). This is being phased out, but for now we have to handle + this case. */ + bool locked = test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags); + bool must_lock = false; + + /* Try to be smart: only lock if polling might start fileio, + otherwise locking will only introduce unwanted delays. */ + if (q->num_buffers == 0 && q->fileio == NULL) { + if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && + (req_events & (POLLIN | POLLRDNORM))) + must_lock = true; + else if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) && + (req_events & (POLLOUT | POLLWRNORM))) + must_lock = true; + } + + /* If locking is needed, but this helper doesn't know how, then you + shouldn't be using this helper but you should write your own. */ + WARN_ON(must_lock && !locked && !lock); + + if (must_lock && !locked && lock && mutex_lock_interruptible(lock)) + return POLLERR; + + fileio = q->fileio; + + res = vb2_poll(vdev->queue, file, wait); + + /* If fileio was started, then we have a new queue owner. */ + if (must_lock && !fileio && q->fileio) + q->owner = file->private_data; + if (must_lock && !locked && lock) + mutex_unlock(lock); + return res; +} +EXPORT_SYMBOL_GPL(vb2_fop_poll); + +#ifndef CONFIG_MMU +unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr, + unsigned long len, unsigned long pgoff, unsigned long flags) +{ + struct video_device *vdev = video_devdata(file); + + return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags); +} +EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area); +#endif + +/* vb2_ops helpers. Only use if vq->lock is non-NULL. */ + +void vb2_ops_wait_prepare(struct vb2_queue *vq) +{ + mutex_unlock(vq->lock); +} +EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare); + +void vb2_ops_wait_finish(struct vb2_queue *vq) +{ + mutex_lock(vq->lock); +} +EXPORT_SYMBOL_GPL(vb2_ops_wait_finish); + MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2"); MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 08c10240e70f..1e8c4f3ab602 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -188,6 +188,7 @@ struct vivi_dev { struct list_head vivi_devlist; struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler ctrl_handler; + struct video_device vdev; /* controls */ struct v4l2_ctrl *brightness; @@ -213,9 +214,6 @@ struct vivi_dev { spinlock_t slock; struct mutex mutex; - /* various device info */ - struct video_device *vfd; - struct vivi_dmaqueue vidq; /* Several counters */ @@ -769,7 +767,13 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, struct vivi_dev *dev = vb2_get_drv_priv(vq); unsigned long size; - size = dev->width * dev->height * dev->pixelsize; + if (fmt) + size = fmt->fmt.pix.sizeimage; + else + size = dev->width * dev->height * dev->pixelsize; + + if (size == 0) + return -EINVAL; if (0 == *nbuffers) *nbuffers = 32; @@ -792,27 +796,6 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, return 0; } -static int buffer_init(struct vb2_buffer *vb) -{ - struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - - BUG_ON(NULL == dev->fmt); - - /* - * This callback is called once per buffer, after its allocation. - * - * Vivi does not allow changing format during streaming, but it is - * possible to do so when streaming is paused (i.e. in streamoff state). - * Buffers however are not freed when going into streamoff and so - * buffer size verification has to be done in buffer_prepare, on each - * qbuf. - * It would be best to move verification code here to buf_init and - * s_fmt though. - */ - - return 0; -} - static int buffer_prepare(struct vb2_buffer *vb) { struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue); @@ -850,20 +833,6 @@ static int buffer_prepare(struct vb2_buffer *vb) return 0; } -static int buffer_finish(struct vb2_buffer *vb) -{ - struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - dprintk(dev, 1, "%s\n", __func__); - return 0; -} - -static void buffer_cleanup(struct vb2_buffer *vb) -{ - struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - dprintk(dev, 1, "%s\n", __func__); - -} - static void buffer_queue(struct vb2_buffer *vb) { struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue); @@ -909,10 +878,7 @@ static void vivi_unlock(struct vb2_queue *vq) static struct vb2_ops vivi_video_qops = { .queue_setup = queue_setup, - .buf_init = buffer_init, .buf_prepare = buffer_prepare, - .buf_finish = buffer_finish, - .buf_cleanup = buffer_cleanup, .buf_queue = buffer_queue, .start_streaming = start_streaming, .stop_streaming = stop_streaming, @@ -1021,7 +987,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, if (ret < 0) return ret; - if (vb2_is_streaming(q)) { + if (vb2_is_busy(q)) { dprintk(dev, 1, "%s device busy\n", __func__); return -EBUSY; } @@ -1035,48 +1001,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct vivi_dev *dev = video_drvdata(file); - return vb2_reqbufs(&dev->vb_vidq, p); -} - -static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct vivi_dev *dev = video_drvdata(file); - return vb2_querybuf(&dev->vb_vidq, p); -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct vivi_dev *dev = video_drvdata(file); - return vb2_qbuf(&dev->vb_vidq, p); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct vivi_dev *dev = video_drvdata(file); - return vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK); -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct vivi_dev *dev = video_drvdata(file); - return vb2_streamon(&dev->vb_vidq, i); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct vivi_dev *dev = video_drvdata(file); - return vb2_streamoff(&dev->vb_vidq, i); -} - -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) -{ - return 0; -} - /* only one input in this sample driver */ static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp) @@ -1085,7 +1009,6 @@ static int vidioc_enum_input(struct file *file, void *priv, return -EINVAL; inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = V4L2_STD_525_60; sprintf(inp->name, "Camera %u", inp->index); return 0; } @@ -1145,58 +1068,6 @@ static int vivi_s_ctrl(struct v4l2_ctrl *ctrl) File operations for the device ------------------------------------------------------------------*/ -static ssize_t -vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos) -{ - struct vivi_dev *dev = video_drvdata(file); - int err; - - dprintk(dev, 1, "read called\n"); - mutex_lock(&dev->mutex); - err = vb2_read(&dev->vb_vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - mutex_unlock(&dev->mutex); - return err; -} - -static unsigned int -vivi_poll(struct file *file, struct poll_table_struct *wait) -{ - struct vivi_dev *dev = video_drvdata(file); - struct vb2_queue *q = &dev->vb_vidq; - - dprintk(dev, 1, "%s\n", __func__); - return vb2_poll(q, file, wait); -} - -static int vivi_close(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct vivi_dev *dev = video_drvdata(file); - - dprintk(dev, 1, "close called (dev=%s), file %p\n", - video_device_node_name(vdev), file); - - if (v4l2_fh_is_singular_file(file)) - vb2_queue_release(&dev->vb_vidq); - return v4l2_fh_release(file); -} - -static int vivi_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct vivi_dev *dev = video_drvdata(file); - int ret; - - dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma); - - ret = vb2_mmap(&dev->vb_vidq, vma); - dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, - ret); - return ret; -} - static const struct v4l2_ctrl_ops vivi_ctrl_ops = { .g_volatile_ctrl = vivi_g_volatile_ctrl, .s_ctrl = vivi_s_ctrl, @@ -1301,11 +1172,11 @@ static const struct v4l2_ctrl_config vivi_ctrl_int_menu = { static const struct v4l2_file_operations vivi_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, - .release = vivi_close, - .read = vivi_read, - .poll = vivi_poll, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = vivi_mmap, + .mmap = vb2_fop_mmap, }; static const struct v4l2_ioctl_ops vivi_ioctl_ops = { @@ -1314,16 +1185,17 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_s_std = vidioc_s_std, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, @@ -1333,10 +1205,7 @@ static struct video_device vivi_template = { .name = "vivi", .fops = &vivi_fops, .ioctl_ops = &vivi_ioctl_ops, - .release = video_device_release, - - .tvnorms = V4L2_STD_525_60, - .current_norm = V4L2_STD_NTSC_M, + .release = video_device_release_empty, }; /* ----------------------------------------------------------------- @@ -1354,8 +1223,8 @@ static int vivi_release(void) dev = list_entry(list, struct vivi_dev, vivi_devlist); v4l2_info(&dev->v4l2_dev, "unregistering %s\n", - video_device_node_name(dev->vfd)); - video_unregister_device(dev->vfd); + video_device_node_name(&dev->vdev)); + video_unregister_device(&dev->vdev); v4l2_device_unregister(&dev->v4l2_dev); v4l2_ctrl_handler_free(&dev->ctrl_handler); kfree(dev); @@ -1440,14 +1309,11 @@ static int __init vivi_create_instance(int inst) INIT_LIST_HEAD(&dev->vidq.active); init_waitqueue_head(&dev->vidq.wq); - ret = -ENOMEM; - vfd = video_device_alloc(); - if (!vfd) - goto unreg_dev; - + vfd = &dev->vdev; *vfd = vivi_template; vfd->debug = debug; vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = q; set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); /* @@ -1455,12 +1321,11 @@ static int __init vivi_create_instance(int inst) * all fops and v4l2 ioctls. */ vfd->lock = &dev->mutex; + video_set_drvdata(vfd, dev); ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); if (ret < 0) - goto rel_vdev; - - video_set_drvdata(vfd, dev); + goto unreg_dev; /* Now that everything is fine, let's add it to device list */ list_add_tail(&dev->vivi_devlist, &vivi_devlist); @@ -1468,13 +1333,10 @@ static int __init vivi_create_instance(int inst) if (video_nr != -1) video_nr++; - dev->vfd = vfd; v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", video_device_node_name(vfd)); return 0; -rel_vdev: - video_device_release(vfd); unreg_dev: v4l2_ctrl_handler_free(hdl); v4l2_device_unregister(&dev->v4l2_dev); diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index e44cb330bbc8..9afab35878b4 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -37,6 +37,10 @@ #include <linux/highmem.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> #include <media/videobuf-vmalloc.h> @@ -120,11 +124,6 @@ static struct usb_device_id device_table[] = { MODULE_DEVICE_TABLE(usb, device_table); -struct zr364xx_mode { - u32 color; /* output video color format */ - u32 brightness; /* brightness */ -}; - /* frame structure */ struct zr364xx_framei { unsigned long ulState; /* ulState:ZR364XX_READ_IDLE, @@ -173,7 +172,10 @@ static const struct zr364xx_fmt formats[] = { struct zr364xx_camera { struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface;/* the interface for this device */ - struct video_device *vdev; /* v4l video device */ + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + struct video_device vdev; /* v4l video device */ + struct v4l2_fh *owner; /* owns the streaming */ int nb; struct zr364xx_bufferi buffer; int skip; @@ -181,12 +183,9 @@ struct zr364xx_camera { int height; int method; struct mutex lock; - struct mutex open_lock; - int users; spinlock_t slock; struct zr364xx_dmaqueue vidq; - int resources; int last_frame; int cur_frame; unsigned long frame_count; @@ -197,8 +196,7 @@ struct zr364xx_camera { const struct zr364xx_fmt *fmt; struct videobuf_queue vb_vidq; - enum v4l2_buf_type type; - struct zr364xx_mode mode; + bool was_streaming; }; /* buffer for one video frame */ @@ -230,11 +228,6 @@ static int send_control_msg(struct usb_device *udev, u8 request, u16 value, transfer_buffer, size, CTRL_TIMEOUT); kfree(transfer_buffer); - - if (status < 0) - dev_err(&udev->dev, - "Failed sending control message, error %d.\n", status); - return status; } @@ -468,6 +461,7 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count, loff_t * ppos) { struct zr364xx_camera *cam = video_drvdata(file); + int err = 0; _DBG("%s\n", __func__); @@ -477,17 +471,21 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count, if (!count) return -EINVAL; - if (cam->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - zr364xx_vidioc_streamon(file, cam, cam->type) == 0) { - DBG("%s: reading %d bytes at pos %d.\n", __func__, (int) count, - (int) *ppos); + if (mutex_lock_interruptible(&cam->lock)) + return -ERESTARTSYS; + + err = zr364xx_vidioc_streamon(file, file->private_data, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (err == 0) { + DBG("%s: reading %d bytes at pos %d.\n", __func__, + (int) count, (int) *ppos); /* NoMan Sux ! */ - return videobuf_read_one(&cam->vb_vidq, buf, count, ppos, + err = videobuf_read_one(&cam->vb_vidq, buf, count, ppos, file->f_flags & O_NONBLOCK); } - - return 0; + mutex_unlock(&cam->lock); + return err; } /* video buffer vmalloc implementation based partly on VIVI driver which is @@ -702,35 +700,6 @@ static int zr364xx_read_video_callback(struct zr364xx_camera *cam, return 0; } -static int res_get(struct zr364xx_camera *cam) -{ - /* is it free? */ - mutex_lock(&cam->lock); - if (cam->resources) { - /* no, someone else uses it */ - mutex_unlock(&cam->lock); - return 0; - } - /* it's free, grab it */ - cam->resources = 1; - _DBG("res: get\n"); - mutex_unlock(&cam->lock); - return 1; -} - -static inline int res_check(struct zr364xx_camera *cam) -{ - return cam->resources; -} - -static void res_free(struct zr364xx_camera *cam) -{ - mutex_lock(&cam->lock); - cam->resources = 0; - mutex_unlock(&cam->lock); - _DBG("res: put\n"); -} - static int zr364xx_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -740,9 +709,10 @@ static int zr364xx_vidioc_querycap(struct file *file, void *priv, strlcpy(cap->card, cam->udev->product, sizeof(cap->card)); strlcpy(cap->bus_info, dev_name(&cam->udev->dev), sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -772,50 +742,18 @@ static int zr364xx_vidioc_s_input(struct file *file, void *priv, return 0; } -static int zr364xx_vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) +static int zr364xx_s_ctrl(struct v4l2_ctrl *ctrl) { - struct zr364xx_camera *cam; - - if (file == NULL) - return -ENODEV; - cam = video_drvdata(file); - - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Brightness"); - c->minimum = 0; - c->maximum = 127; - c->step = 1; - c->default_value = cam->mode.brightness; - c->flags = 0; - break; - default: - return -EINVAL; - } - return 0; -} - -static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *c) -{ - struct zr364xx_camera *cam; + struct zr364xx_camera *cam = + container_of(ctrl->handler, struct zr364xx_camera, ctrl_handler); int temp; - if (file == NULL) - return -ENODEV; - cam = video_drvdata(file); - - switch (c->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - cam->mode.brightness = c->value; /* hardware brightness */ - mutex_lock(&cam->lock); send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); - temp = (0x60 << 8) + 127 - cam->mode.brightness; + temp = (0x60 << 8) + 127 - ctrl->val; send_control_msg(cam->udev, 1, temp, 0, NULL, 0); - mutex_unlock(&cam->lock); break; default: return -EINVAL; @@ -824,25 +762,6 @@ static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv, return 0; } -static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *c) -{ - struct zr364xx_camera *cam; - - if (file == NULL) - return -ENODEV; - cam = video_drvdata(file); - - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - c->value = cam->mode.brightness; - break; - default: - return -EINVAL; - } - return 0; -} - static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { @@ -888,7 +807,7 @@ static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.field = V4L2_FIELD_NONE; f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = 0; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; f->fmt.pix.priv = 0; DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), @@ -911,7 +830,7 @@ static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.height = cam->height; f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = 0; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; f->fmt.pix.priv = 0; return 0; } @@ -936,7 +855,7 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, goto out; } - if (res_check(cam)) { + if (cam->owner) { DBG("%s can't change format after started\n", __func__); ret = -EBUSY; goto out; @@ -944,14 +863,13 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, cam->width = f->fmt.pix.width; cam->height = f->fmt.pix.height; - dev_info(&cam->udev->dev, "%s: %dx%d mode selected\n", __func__, + DBG("%s: %dx%d mode selected\n", __func__, cam->width, cam->height); f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = 0; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; f->fmt.pix.priv = 0; cam->vb_vidq.field = f->fmt.pix.field; - cam->mode.color = V4L2_PIX_FMT_JPEG; if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120) mode = 1; @@ -1015,10 +933,11 @@ out: static int zr364xx_vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { - int rc; struct zr364xx_camera *cam = video_drvdata(file); - rc = videobuf_reqbufs(&cam->vb_vidq, p); - return rc; + + if (cam->owner && cam->owner != priv) + return -EBUSY; + return videobuf_reqbufs(&cam->vb_vidq, p); } static int zr364xx_vidioc_querybuf(struct file *file, @@ -1038,6 +957,8 @@ static int zr364xx_vidioc_qbuf(struct file *file, int rc; struct zr364xx_camera *cam = video_drvdata(file); _DBG("%s\n", __func__); + if (cam->owner && cam->owner != priv) + return -EBUSY; rc = videobuf_qbuf(&cam->vb_vidq, p); return rc; } @@ -1049,6 +970,8 @@ static int zr364xx_vidioc_dqbuf(struct file *file, int rc; struct zr364xx_camera *cam = video_drvdata(file); _DBG("%s\n", __func__); + if (cam->owner && cam->owner != priv) + return -EBUSY; rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK); return rc; } @@ -1197,29 +1120,23 @@ static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam) return 0; } -static int zr364xx_vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) +static int zr364xx_prepare(struct zr364xx_camera *cam) { - struct zr364xx_camera *cam = video_drvdata(file); - int j; int res; + int i, j; - DBG("%s\n", __func__); - - if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_err(&cam->udev->dev, "invalid fh type0\n"); - return -EINVAL; - } - if (cam->type != type) { - dev_err(&cam->udev->dev, "invalid fh type1\n"); - return -EINVAL; - } - - if (!res_get(cam)) { - dev_err(&cam->udev->dev, "stream busy\n"); - return -EBUSY; + for (i = 0; init[cam->method][i].size != -1; i++) { + res = send_control_msg(cam->udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (res < 0) { + dev_err(&cam->udev->dev, + "error during open sequence: %d\n", i); + return res; + } } + cam->skip = 2; cam->last_frame = -1; cam->cur_frame = 0; cam->frame_count = 0; @@ -1227,11 +1144,31 @@ static int zr364xx_vidioc_streamon(struct file *file, void *priv, cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; cam->buffer.frame[j].cur_size = 0; } + v4l2_ctrl_handler_setup(&cam->ctrl_handler); + return 0; +} + +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int res; + + DBG("%s\n", __func__); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->owner && cam->owner != priv) + return -EBUSY; + + res = zr364xx_prepare(cam); + if (res) + return res; res = videobuf_streamon(&cam->vb_vidq); if (res == 0) { zr364xx_start_acquire(cam); - } else { - res_free(cam); + cam->owner = file->private_data; } return res; } @@ -1239,67 +1176,32 @@ static int zr364xx_vidioc_streamon(struct file *file, void *priv, static int zr364xx_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { - int res; struct zr364xx_camera *cam = video_drvdata(file); DBG("%s\n", __func__); - if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_err(&cam->udev->dev, "invalid fh type0\n"); + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - } - if (cam->type != type) { - dev_err(&cam->udev->dev, "invalid fh type1\n"); - return -EINVAL; - } + if (cam->owner && cam->owner != priv) + return -EBUSY; zr364xx_stop_acquire(cam); - res = videobuf_streamoff(&cam->vb_vidq); - if (res < 0) - return res; - res_free(cam); - return 0; + return videobuf_streamoff(&cam->vb_vidq); } /* open the camera */ static int zr364xx_open(struct file *file) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam = video_drvdata(file); - struct usb_device *udev = cam->udev; - int i, err; + int err; DBG("%s\n", __func__); - mutex_lock(&cam->open_lock); + if (mutex_lock_interruptible(&cam->lock)) + return -ERESTARTSYS; - if (cam->users) { - err = -EBUSY; + err = v4l2_fh_open(file); + if (err) goto out; - } - - for (i = 0; init[cam->method][i].size != -1; i++) { - err = - send_control_msg(udev, 1, init[cam->method][i].value, - 0, init[cam->method][i].bytes, - init[cam->method][i].size); - if (err < 0) { - dev_err(&cam->udev->dev, - "error during open sequence: %d\n", i); - goto out; - } - } - - cam->skip = 2; - cam->users++; - file->private_data = vdev; - cam->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cam->fmt = formats; - - videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops, - NULL, &cam->slock, - cam->type, - V4L2_FIELD_NONE, - sizeof(struct zr364xx_buffer), cam, NULL); /* Added some delay here, since opening/closing the camera quickly, * like Ekiga does during its startup, can crash the webcam @@ -1308,29 +1210,20 @@ static int zr364xx_open(struct file *file) err = 0; out: - mutex_unlock(&cam->open_lock); + mutex_unlock(&cam->lock); DBG("%s: %d\n", __func__, err); return err; } -static void zr364xx_destroy(struct zr364xx_camera *cam) +static void zr364xx_release(struct v4l2_device *v4l2_dev) { + struct zr364xx_camera *cam = + container_of(v4l2_dev, struct zr364xx_camera, v4l2_dev); unsigned long i; - if (!cam) { - printk(KERN_ERR KBUILD_MODNAME ", %s: no device\n", __func__); - return; - } - mutex_lock(&cam->open_lock); - if (cam->vdev) - video_unregister_device(cam->vdev); - cam->vdev = NULL; - - /* stops the read pipe if it is running */ - if (cam->b_acquire) - zr364xx_stop_acquire(cam); + v4l2_device_unregister(&cam->v4l2_dev); - zr364xx_stop_readpipe(cam); + videobuf_mmap_free(&cam->vb_vidq); /* release sys buffers */ for (i = 0; i < FRAMES; i++) { @@ -1341,62 +1234,45 @@ static void zr364xx_destroy(struct zr364xx_camera *cam) cam->buffer.frame[i].lpvbits = NULL; } + v4l2_ctrl_handler_free(&cam->ctrl_handler); /* release transfer buffer */ kfree(cam->pipe->transfer_buffer); - cam->pipe->transfer_buffer = NULL; - mutex_unlock(&cam->open_lock); kfree(cam); - cam = NULL; } /* release the camera */ -static int zr364xx_release(struct file *file) +static int zr364xx_close(struct file *file) { struct zr364xx_camera *cam; struct usb_device *udev; - int i, err; + int i; DBG("%s\n", __func__); cam = video_drvdata(file); - if (!cam) - return -ENODEV; - - mutex_lock(&cam->open_lock); + mutex_lock(&cam->lock); udev = cam->udev; - /* turn off stream */ - if (res_check(cam)) { + if (file->private_data == cam->owner) { + /* turn off stream */ if (cam->b_acquire) zr364xx_stop_acquire(cam); videobuf_streamoff(&cam->vb_vidq); - res_free(cam); - } - - cam->users--; - file->private_data = NULL; - for (i = 0; i < 2; i++) { - err = - send_control_msg(udev, 1, init[cam->method][i].value, - 0, init[cam->method][i].bytes, - init[cam->method][i].size); - if (err < 0) { - dev_err(&udev->dev, "error during release sequence\n"); - goto out; + for (i = 0; i < 2; i++) { + send_control_msg(udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); } + cam->owner = NULL; } /* Added some delay here, since opening/closing the camera quickly, * like Ekiga does during its startup, can crash the webcam */ mdelay(100); - err = 0; - -out: - mutex_unlock(&cam->open_lock); - - return err; + mutex_unlock(&cam->lock); + return v4l2_fh_release(file); } @@ -1424,21 +1300,24 @@ static unsigned int zr364xx_poll(struct file *file, { struct zr364xx_camera *cam = video_drvdata(file); struct videobuf_queue *q = &cam->vb_vidq; - _DBG("%s\n", __func__); + unsigned res = v4l2_ctrl_poll(file, wait); - if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return POLLERR; + _DBG("%s\n", __func__); - return videobuf_poll_stream(file, q, wait); + return res | videobuf_poll_stream(file, q, wait); } +static const struct v4l2_ctrl_ops zr364xx_ctrl_ops = { + .s_ctrl = zr364xx_s_ctrl, +}; + static const struct v4l2_file_operations zr364xx_fops = { .owner = THIS_MODULE, .open = zr364xx_open, - .release = zr364xx_release, + .release = zr364xx_close, .read = zr364xx_read, .mmap = zr364xx_mmap, - .ioctl = video_ioctl2, + .unlocked_ioctl = video_ioctl2, .poll = zr364xx_poll, }; @@ -1453,20 +1332,20 @@ static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { .vidioc_s_input = zr364xx_vidioc_s_input, .vidioc_streamon = zr364xx_vidioc_streamon, .vidioc_streamoff = zr364xx_vidioc_streamoff, - .vidioc_queryctrl = zr364xx_vidioc_queryctrl, - .vidioc_g_ctrl = zr364xx_vidioc_g_ctrl, - .vidioc_s_ctrl = zr364xx_vidioc_s_ctrl, .vidioc_reqbufs = zr364xx_vidioc_reqbufs, .vidioc_querybuf = zr364xx_vidioc_querybuf, .vidioc_qbuf = zr364xx_vidioc_qbuf, .vidioc_dqbuf = zr364xx_vidioc_dqbuf, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device zr364xx_template = { .name = DRIVER_DESC, .fops = &zr364xx_fops, .ioctl_ops = &zr364xx_ioctl_ops, - .release = video_device_release, + .release = video_device_release_empty, }; @@ -1540,6 +1419,7 @@ static int zr364xx_probe(struct usb_interface *intf, struct zr364xx_camera *cam = NULL; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; + struct v4l2_ctrl_handler *hdl; int err; int i; @@ -1555,21 +1435,34 @@ static int zr364xx_probe(struct usb_interface *intf, dev_err(&udev->dev, "cam: out of memory !\n"); return -ENOMEM; } - /* save the init method used by this camera */ - cam->method = id->driver_info; - cam->vdev = video_device_alloc(); - if (cam->vdev == NULL) { - dev_err(&udev->dev, "cam->vdev: out of memory !\n"); + cam->v4l2_dev.release = zr364xx_release; + err = v4l2_device_register(&intf->dev, &cam->v4l2_dev); + if (err < 0) { + dev_err(&udev->dev, "couldn't register v4l2_device\n"); kfree(cam); - cam = NULL; - return -ENOMEM; + return err; } - memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template)); - cam->vdev->parent = &intf->dev; - video_set_drvdata(cam->vdev, cam); + hdl = &cam->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 1); + v4l2_ctrl_new_std(hdl, &zr364xx_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 127, 1, 64); + if (hdl->error) { + err = hdl->error; + dev_err(&udev->dev, "couldn't register control\n"); + goto fail; + } + /* save the init method used by this camera */ + cam->method = id->driver_info; + mutex_init(&cam->lock); + cam->vdev = zr364xx_template; + cam->vdev.lock = &cam->lock; + cam->vdev.v4l2_dev = &cam->v4l2_dev; + cam->vdev.ctrl_handler = &cam->ctrl_handler; + set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); + video_set_drvdata(&cam->vdev, cam); if (debug) - cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; + cam->vdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; cam->udev = udev; @@ -1615,11 +1508,7 @@ static int zr364xx_probe(struct usb_interface *intf, header2[439] = cam->width / 256; header2[440] = cam->width % 256; - cam->users = 0; cam->nb = 0; - cam->mode.brightness = 64; - mutex_init(&cam->lock); - mutex_init(&cam->open_lock); DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf); @@ -1635,52 +1524,100 @@ static int zr364xx_probe(struct usb_interface *intf, } if (!cam->read_endpoint) { + err = -ENOMEM; dev_err(&intf->dev, "Could not find bulk-in endpoint\n"); - video_device_release(cam->vdev); - kfree(cam); - cam = NULL; - return -ENOMEM; + goto fail; } /* v4l */ INIT_LIST_HEAD(&cam->vidq.active); cam->vidq.cam = cam; - err = video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1); - if (err) { - dev_err(&udev->dev, "video_register_device failed\n"); - video_device_release(cam->vdev); - kfree(cam); - cam = NULL; - return err; - } usb_set_intfdata(intf, cam); /* load zr364xx board specific */ err = zr364xx_board_init(cam); - if (err) { - spin_lock_init(&cam->slock); - return err; - } + if (!err) + err = v4l2_ctrl_handler_setup(hdl); + if (err) + goto fail; spin_lock_init(&cam->slock); + cam->fmt = formats; + + videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops, + NULL, &cam->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, + sizeof(struct zr364xx_buffer), cam, &cam->lock); + + err = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); + if (err) { + dev_err(&udev->dev, "video_register_device failed\n"); + goto fail; + } + dev_info(&udev->dev, DRIVER_DESC " controlling device %s\n", - video_device_node_name(cam->vdev)); + video_device_node_name(&cam->vdev)); return 0; + +fail: + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); + return err; } static void zr364xx_disconnect(struct usb_interface *intf) { struct zr364xx_camera *cam = usb_get_intfdata(intf); - videobuf_mmap_free(&cam->vb_vidq); + + mutex_lock(&cam->lock); usb_set_intfdata(intf, NULL); dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n"); - zr364xx_destroy(cam); + video_unregister_device(&cam->vdev); + v4l2_device_disconnect(&cam->v4l2_dev); + + /* stops the read pipe if it is running */ + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + + zr364xx_stop_readpipe(cam); + mutex_unlock(&cam->lock); + v4l2_device_put(&cam->v4l2_dev); } +#ifdef CONFIG_PM +static int zr364xx_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + + cam->was_streaming = cam->b_acquire; + if (!cam->was_streaming) + return 0; + zr364xx_stop_acquire(cam); + zr364xx_stop_readpipe(cam); + return 0; +} + +static int zr364xx_resume(struct usb_interface *intf) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + int res; + + if (!cam->was_streaming) + return 0; + + zr364xx_start_readpipe(cam); + res = zr364xx_prepare(cam); + if (!res) + zr364xx_start_acquire(cam); + return res; +} +#endif /**********************/ /* Module integration */ @@ -1690,6 +1627,11 @@ static struct usb_driver zr364xx_driver = { .name = "zr364xx", .probe = zr364xx_probe, .disconnect = zr364xx_disconnect, +#ifdef CONFIG_PM + .suspend = zr364xx_suspend, + .resume = zr364xx_resume, + .reset_resume = zr364xx_resume, +#endif .id_table = device_table }; diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c index 098de2b35784..9a49c243a6ac 100644 --- a/drivers/message/i2o/i2o_config.c +++ b/drivers/message/i2o/i2o_config.c @@ -188,6 +188,13 @@ static int i2o_cfg_parms(unsigned long arg, unsigned int type) if (!dev) return -ENXIO; + /* + * Stop users being able to try and allocate arbitary amounts + * of DMA space. 64K is way more than sufficient for this. + */ + if (kcmd.oplen > 65536) + return -EMSGSIZE; + ops = memdup_user(kcmd.opbuf, kcmd.oplen); if (IS_ERR(ops)) return PTR_ERR(ops); diff --git a/drivers/message/i2o/i2o_proc.c b/drivers/message/i2o/i2o_proc.c index 506c36f6e1db..8001aa6bfb48 100644 --- a/drivers/message/i2o/i2o_proc.c +++ b/drivers/message/i2o/i2o_proc.c @@ -255,9 +255,8 @@ static char *scsi_devices[] = { "Array Controller Device" }; -static char *chtostr(u8 * chars, int n) +static char *chtostr(char *tmp, u8 *chars, int n) { - char tmp[256]; tmp[0] = 0; return strncat(tmp, (char *)chars, n); } @@ -791,6 +790,7 @@ static int i2o_seq_show_ddm_table(struct seq_file *seq, void *v) } *result; i2o_exec_execute_ddm_table ddm_table; + char tmp[28 + 1]; result = kmalloc(sizeof(*result), GFP_KERNEL); if (!result) @@ -826,7 +826,7 @@ static int i2o_seq_show_ddm_table(struct seq_file *seq, void *v) seq_printf(seq, "%-#7x", ddm_table.i2o_vendor_id); seq_printf(seq, "%-#8x", ddm_table.module_id); seq_printf(seq, "%-29s", - chtostr(ddm_table.module_name_version, 28)); + chtostr(tmp, ddm_table.module_name_version, 28)); seq_printf(seq, "%9d ", ddm_table.data_size); seq_printf(seq, "%8d", ddm_table.code_size); @@ -893,6 +893,7 @@ static int i2o_seq_show_drivers_stored(struct seq_file *seq, void *v) i2o_driver_result_table *result; i2o_driver_store_table *dst; + char tmp[28 + 1]; result = kmalloc(sizeof(i2o_driver_result_table), GFP_KERNEL); if (result == NULL) @@ -927,8 +928,9 @@ static int i2o_seq_show_drivers_stored(struct seq_file *seq, void *v) seq_printf(seq, "%-#7x", dst->i2o_vendor_id); seq_printf(seq, "%-#8x", dst->module_id); - seq_printf(seq, "%-29s", chtostr(dst->module_name_version, 28)); - seq_printf(seq, "%-9s", chtostr(dst->date, 8)); + seq_printf(seq, "%-29s", + chtostr(tmp, dst->module_name_version, 28)); + seq_printf(seq, "%-9s", chtostr(tmp, dst->date, 8)); seq_printf(seq, "%8d ", dst->module_size); seq_printf(seq, "%8d ", dst->mpb_size); seq_printf(seq, "0x%04x", dst->module_flags); @@ -1248,6 +1250,7 @@ static int i2o_seq_show_dev_identity(struct seq_file *seq, void *v) // == (allow) 512d bytes (max) static u16 *work16 = (u16 *) work32; int token; + char tmp[16 + 1]; token = i2o_parm_field_get(d, 0xF100, -1, &work32, sizeof(work32)); @@ -1260,13 +1263,13 @@ static int i2o_seq_show_dev_identity(struct seq_file *seq, void *v) seq_printf(seq, "Owner TID : %0#5x\n", work16[2]); seq_printf(seq, "Parent TID : %0#5x\n", work16[3]); seq_printf(seq, "Vendor info : %s\n", - chtostr((u8 *) (work32 + 2), 16)); + chtostr(tmp, (u8 *) (work32 + 2), 16)); seq_printf(seq, "Product info : %s\n", - chtostr((u8 *) (work32 + 6), 16)); + chtostr(tmp, (u8 *) (work32 + 6), 16)); seq_printf(seq, "Description : %s\n", - chtostr((u8 *) (work32 + 10), 16)); + chtostr(tmp, (u8 *) (work32 + 10), 16)); seq_printf(seq, "Product rev. : %s\n", - chtostr((u8 *) (work32 + 14), 8)); + chtostr(tmp, (u8 *) (work32 + 14), 8)); seq_printf(seq, "Serial number : "); print_serial_number(seq, (u8 *) (work32 + 16), @@ -1303,6 +1306,8 @@ static int i2o_seq_show_ddm_identity(struct seq_file *seq, void *v) u8 pad[256]; // allow up to 256 byte (max) serial number } result; + char tmp[24 + 1]; + token = i2o_parm_field_get(d, 0xF101, -1, &result, sizeof(result)); if (token < 0) { @@ -1312,9 +1317,9 @@ static int i2o_seq_show_ddm_identity(struct seq_file *seq, void *v) seq_printf(seq, "Registering DDM TID : 0x%03x\n", result.ddm_tid); seq_printf(seq, "Module name : %s\n", - chtostr(result.module_name, 24)); + chtostr(tmp, result.module_name, 24)); seq_printf(seq, "Module revision : %s\n", - chtostr(result.module_rev, 8)); + chtostr(tmp, result.module_rev, 8)); seq_printf(seq, "Serial number : "); print_serial_number(seq, result.serial_number, sizeof(result) - 36); @@ -1338,6 +1343,8 @@ static int i2o_seq_show_uinfo(struct seq_file *seq, void *v) u8 instance_number[4]; } result; + char tmp[64 + 1]; + token = i2o_parm_field_get(d, 0xF102, -1, &result, sizeof(result)); if (token < 0) { @@ -1346,13 +1353,13 @@ static int i2o_seq_show_uinfo(struct seq_file *seq, void *v) } seq_printf(seq, "Device name : %s\n", - chtostr(result.device_name, 64)); + chtostr(tmp, result.device_name, 64)); seq_printf(seq, "Service name : %s\n", - chtostr(result.service_name, 64)); + chtostr(tmp, result.service_name, 64)); seq_printf(seq, "Physical name : %s\n", - chtostr(result.physical_location, 64)); + chtostr(tmp, result.physical_location, 64)); seq_printf(seq, "Instance number : %s\n", - chtostr(result.instance_number, 4)); + chtostr(tmp, result.instance_number, 4)); return 0; } diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c new file mode 100644 index 000000000000..b67a3018b136 --- /dev/null +++ b/drivers/mfd/88pm800.c @@ -0,0 +1,596 @@ +/* + * Base driver for Marvell 88PM800 + * + * Copyright (C) 2012 Marvell International Ltd. + * Haojian Zhuang <haojian.zhuang@marvell.com> + * Joseph(Yossi) Hanin <yhanin@marvell.com> + * Qiao Zhou <zhouqiao@marvell.com> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/mfd/core.h> +#include <linux/mfd/88pm80x.h> +#include <linux/slab.h> + +#define PM800_CHIP_ID (0x00) + +/* Interrupt Registers */ +#define PM800_INT_STATUS1 (0x05) +#define PM800_ONKEY_INT_STS1 (1 << 0) +#define PM800_EXTON_INT_STS1 (1 << 1) +#define PM800_CHG_INT_STS1 (1 << 2) +#define PM800_BAT_INT_STS1 (1 << 3) +#define PM800_RTC_INT_STS1 (1 << 4) +#define PM800_CLASSD_OC_INT_STS1 (1 << 5) + +#define PM800_INT_STATUS2 (0x06) +#define PM800_VBAT_INT_STS2 (1 << 0) +#define PM800_VSYS_INT_STS2 (1 << 1) +#define PM800_VCHG_INT_STS2 (1 << 2) +#define PM800_TINT_INT_STS2 (1 << 3) +#define PM800_GPADC0_INT_STS2 (1 << 4) +#define PM800_TBAT_INT_STS2 (1 << 5) +#define PM800_GPADC2_INT_STS2 (1 << 6) +#define PM800_GPADC3_INT_STS2 (1 << 7) + +#define PM800_INT_STATUS3 (0x07) + +#define PM800_INT_STATUS4 (0x08) +#define PM800_GPIO0_INT_STS4 (1 << 0) +#define PM800_GPIO1_INT_STS4 (1 << 1) +#define PM800_GPIO2_INT_STS4 (1 << 2) +#define PM800_GPIO3_INT_STS4 (1 << 3) +#define PM800_GPIO4_INT_STS4 (1 << 4) + +#define PM800_INT_ENA_1 (0x09) +#define PM800_ONKEY_INT_ENA1 (1 << 0) +#define PM800_EXTON_INT_ENA1 (1 << 1) +#define PM800_CHG_INT_ENA1 (1 << 2) +#define PM800_BAT_INT_ENA1 (1 << 3) +#define PM800_RTC_INT_ENA1 (1 << 4) +#define PM800_CLASSD_OC_INT_ENA1 (1 << 5) + +#define PM800_INT_ENA_2 (0x0A) +#define PM800_VBAT_INT_ENA2 (1 << 0) +#define PM800_VSYS_INT_ENA2 (1 << 1) +#define PM800_VCHG_INT_ENA2 (1 << 2) +#define PM800_TINT_INT_ENA2 (1 << 3) + +#define PM800_INT_ENA_3 (0x0B) +#define PM800_GPADC0_INT_ENA3 (1 << 0) +#define PM800_GPADC1_INT_ENA3 (1 << 1) +#define PM800_GPADC2_INT_ENA3 (1 << 2) +#define PM800_GPADC3_INT_ENA3 (1 << 3) +#define PM800_GPADC4_INT_ENA3 (1 << 4) + +#define PM800_INT_ENA_4 (0x0C) +#define PM800_GPIO0_INT_ENA4 (1 << 0) +#define PM800_GPIO1_INT_ENA4 (1 << 1) +#define PM800_GPIO2_INT_ENA4 (1 << 2) +#define PM800_GPIO3_INT_ENA4 (1 << 3) +#define PM800_GPIO4_INT_ENA4 (1 << 4) + +/* number of INT_ENA & INT_STATUS regs */ +#define PM800_INT_REG_NUM (4) + +/* Interrupt Number in 88PM800 */ +enum { + PM800_IRQ_ONKEY, /*EN1b0 *//*0 */ + PM800_IRQ_EXTON, /*EN1b1 */ + PM800_IRQ_CHG, /*EN1b2 */ + PM800_IRQ_BAT, /*EN1b3 */ + PM800_IRQ_RTC, /*EN1b4 */ + PM800_IRQ_CLASSD, /*EN1b5 *//*5 */ + PM800_IRQ_VBAT, /*EN2b0 */ + PM800_IRQ_VSYS, /*EN2b1 */ + PM800_IRQ_VCHG, /*EN2b2 */ + PM800_IRQ_TINT, /*EN2b3 */ + PM800_IRQ_GPADC0, /*EN3b0 *//*10 */ + PM800_IRQ_GPADC1, /*EN3b1 */ + PM800_IRQ_GPADC2, /*EN3b2 */ + PM800_IRQ_GPADC3, /*EN3b3 */ + PM800_IRQ_GPADC4, /*EN3b4 */ + PM800_IRQ_GPIO0, /*EN4b0 *//*15 */ + PM800_IRQ_GPIO1, /*EN4b1 */ + PM800_IRQ_GPIO2, /*EN4b2 */ + PM800_IRQ_GPIO3, /*EN4b3 */ + PM800_IRQ_GPIO4, /*EN4b4 *//*19 */ + PM800_MAX_IRQ, +}; + +enum { + /* Procida */ + PM800_CHIP_A0 = 0x60, + PM800_CHIP_A1 = 0x61, + PM800_CHIP_B0 = 0x62, + PM800_CHIP_C0 = 0x63, + PM800_CHIP_END = PM800_CHIP_C0, + + /* Make sure to update this to the last stepping */ + PM8XXX_CHIP_END = PM800_CHIP_END +}; + +static const struct i2c_device_id pm80x_id_table[] = { + {"88PM800", CHIP_PM800}, + {} /* NULL terminated */ +}; +MODULE_DEVICE_TABLE(i2c, pm80x_id_table); + +static struct resource rtc_resources[] = { + { + .name = "88pm80x-rtc", + .start = PM800_IRQ_RTC, + .end = PM800_IRQ_RTC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell rtc_devs[] = { + { + .name = "88pm80x-rtc", + .num_resources = ARRAY_SIZE(rtc_resources), + .resources = &rtc_resources[0], + .id = -1, + }, +}; + +static struct resource onkey_resources[] = { + { + .name = "88pm80x-onkey", + .start = PM800_IRQ_ONKEY, + .end = PM800_IRQ_ONKEY, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell onkey_devs[] = { + { + .name = "88pm80x-onkey", + .num_resources = 1, + .resources = &onkey_resources[0], + .id = -1, + }, +}; + +static const struct regmap_irq pm800_irqs[] = { + /* INT0 */ + [PM800_IRQ_ONKEY] = { + .mask = PM800_ONKEY_INT_ENA1, + }, + [PM800_IRQ_EXTON] = { + .mask = PM800_EXTON_INT_ENA1, + }, + [PM800_IRQ_CHG] = { + .mask = PM800_CHG_INT_ENA1, + }, + [PM800_IRQ_BAT] = { + .mask = PM800_BAT_INT_ENA1, + }, + [PM800_IRQ_RTC] = { + .mask = PM800_RTC_INT_ENA1, + }, + [PM800_IRQ_CLASSD] = { + .mask = PM800_CLASSD_OC_INT_ENA1, + }, + /* INT1 */ + [PM800_IRQ_VBAT] = { + .reg_offset = 1, + .mask = PM800_VBAT_INT_ENA2, + }, + [PM800_IRQ_VSYS] = { + .reg_offset = 1, + .mask = PM800_VSYS_INT_ENA2, + }, + [PM800_IRQ_VCHG] = { + .reg_offset = 1, + .mask = PM800_VCHG_INT_ENA2, + }, + [PM800_IRQ_TINT] = { + .reg_offset = 1, + .mask = PM800_TINT_INT_ENA2, + }, + /* INT2 */ + [PM800_IRQ_GPADC0] = { + .reg_offset = 2, + .mask = PM800_GPADC0_INT_ENA3, + }, + [PM800_IRQ_GPADC1] = { + .reg_offset = 2, + .mask = PM800_GPADC1_INT_ENA3, + }, + [PM800_IRQ_GPADC2] = { + .reg_offset = 2, + .mask = PM800_GPADC2_INT_ENA3, + }, + [PM800_IRQ_GPADC3] = { + .reg_offset = 2, + .mask = PM800_GPADC3_INT_ENA3, + }, + [PM800_IRQ_GPADC4] = { + .reg_offset = 2, + .mask = PM800_GPADC4_INT_ENA3, + }, + /* INT3 */ + [PM800_IRQ_GPIO0] = { + .reg_offset = 3, + .mask = PM800_GPIO0_INT_ENA4, + }, + [PM800_IRQ_GPIO1] = { + .reg_offset = 3, + .mask = PM800_GPIO1_INT_ENA4, + }, + [PM800_IRQ_GPIO2] = { + .reg_offset = 3, + .mask = PM800_GPIO2_INT_ENA4, + }, + [PM800_IRQ_GPIO3] = { + .reg_offset = 3, + .mask = PM800_GPIO3_INT_ENA4, + }, + [PM800_IRQ_GPIO4] = { + .reg_offset = 3, + .mask = PM800_GPIO4_INT_ENA4, + }, +}; + +static int __devinit device_gpadc_init(struct pm80x_chip *chip, + struct pm80x_platform_data *pdata) +{ + struct pm80x_subchip *subchip = chip->subchip; + struct regmap *map = subchip->regmap_gpadc; + int data = 0, mask = 0, ret = 0; + + if (!map) { + dev_warn(chip->dev, + "Warning: gpadc regmap is not available!\n"); + return -EINVAL; + } + /* + * initialize GPADC without activating it turn on GPADC + * measurments + */ + ret = regmap_update_bits(map, + PM800_GPADC_MISC_CONFIG2, + PM800_GPADC_MISC_GPFSM_EN, + PM800_GPADC_MISC_GPFSM_EN); + if (ret < 0) + goto out; + /* + * This function configures the ADC as requires for + * CP implementation.CP does not "own" the ADC configuration + * registers and relies on AP. + * Reason: enable automatic ADC measurements needed + * for CP to get VBAT and RF temperature readings. + */ + ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN1, + PM800_MEAS_EN1_VBAT, PM800_MEAS_EN1_VBAT); + if (ret < 0) + goto out; + ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN2, + (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN), + (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN)); + if (ret < 0) + goto out; + + /* + * the defult of PM800 is GPADC operates at 100Ks/s rate + * and Number of GPADC slots with active current bias prior + * to GPADC sampling = 1 slot for all GPADCs set for + * Temprature mesurmants + */ + mask = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 | + PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3); + + if (pdata && (pdata->batt_det == 0)) + data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 | + PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3); + else + data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN2 | + PM800_GPADC_GP_BIAS_EN3); + + ret = regmap_update_bits(map, PM800_GP_BIAS_ENA1, mask, data); + if (ret < 0) + goto out; + + dev_info(chip->dev, "pm800 device_gpadc_init: Done\n"); + return 0; + +out: + dev_info(chip->dev, "pm800 device_gpadc_init: Failed!\n"); + return ret; +} + +static int __devinit device_irq_init_800(struct pm80x_chip *chip) +{ + struct regmap *map = chip->regmap; + unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + int data, mask, ret = -EINVAL; + + if (!map || !chip->irq) { + dev_err(chip->dev, "incorrect parameters\n"); + return -EINVAL; + } + + /* + * irq_mode defines the way of clearing interrupt. it's read-clear by + * default. + */ + mask = + PM800_WAKEUP2_INV_INT | PM800_WAKEUP2_INT_CLEAR | + PM800_WAKEUP2_INT_MASK; + + data = PM800_WAKEUP2_INT_CLEAR; + ret = regmap_update_bits(map, PM800_WAKEUP2, mask, data); + + if (ret < 0) + goto out; + + ret = + regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1, + chip->regmap_irq_chip, &chip->irq_data); + +out: + return ret; +} + +static void device_irq_exit_800(struct pm80x_chip *chip) +{ + regmap_del_irq_chip(chip->irq, chip->irq_data); +} + +static struct regmap_irq_chip pm800_irq_chip = { + .name = "88pm800", + .irqs = pm800_irqs, + .num_irqs = ARRAY_SIZE(pm800_irqs), + + .num_regs = 4, + .status_base = PM800_INT_STATUS1, + .mask_base = PM800_INT_ENA_1, + .ack_base = PM800_INT_STATUS1, +}; + +static int pm800_pages_init(struct pm80x_chip *chip) +{ + struct pm80x_subchip *subchip; + struct i2c_client *client = chip->client; + + subchip = chip->subchip; + /* PM800 block power: i2c addr 0x31 */ + if (subchip->power_page_addr) { + subchip->power_page = + i2c_new_dummy(client->adapter, subchip->power_page_addr); + subchip->regmap_power = + devm_regmap_init_i2c(subchip->power_page, + &pm80x_regmap_config); + i2c_set_clientdata(subchip->power_page, chip); + } else + dev_info(chip->dev, + "PM800 block power 0x31: No power_page_addr\n"); + + /* PM800 block GPADC: i2c addr 0x32 */ + if (subchip->gpadc_page_addr) { + subchip->gpadc_page = i2c_new_dummy(client->adapter, + subchip->gpadc_page_addr); + subchip->regmap_gpadc = + devm_regmap_init_i2c(subchip->gpadc_page, + &pm80x_regmap_config); + i2c_set_clientdata(subchip->gpadc_page, chip); + } else + dev_info(chip->dev, + "PM800 block GPADC 0x32: No gpadc_page_addr\n"); + + return 0; +} + +static void pm800_pages_exit(struct pm80x_chip *chip) +{ + struct pm80x_subchip *subchip; + + regmap_exit(chip->regmap); + i2c_unregister_device(chip->client); + + subchip = chip->subchip; + if (subchip->power_page) { + regmap_exit(subchip->regmap_power); + i2c_unregister_device(subchip->power_page); + } + if (subchip->gpadc_page) { + regmap_exit(subchip->regmap_gpadc); + i2c_unregister_device(subchip->gpadc_page); + } +} + +static int __devinit device_800_init(struct pm80x_chip *chip, + struct pm80x_platform_data *pdata) +{ + int ret, pmic_id; + unsigned int val; + + ret = regmap_read(chip->regmap, PM800_CHIP_ID, &val); + if (ret < 0) { + dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret); + goto out; + } + + pmic_id = val & PM80X_VERSION_MASK; + + if ((pmic_id >= PM800_CHIP_A0) && (pmic_id <= PM800_CHIP_END)) { + chip->version = val; + dev_info(chip->dev, + "88PM80x:Marvell 88PM800 (ID:0x%x) detected\n", val); + } else { + dev_err(chip->dev, + "Failed to detect Marvell 88PM800:ChipID[0x%x]\n", val); + ret = -EINVAL; + goto out; + } + + /* + * alarm wake up bit will be clear in device_irq_init(), + * read before that + */ + ret = regmap_read(chip->regmap, PM800_RTC_CONTROL, &val); + if (ret < 0) { + dev_err(chip->dev, "Failed to read RTC register: %d\n", ret); + goto out; + } + if (val & PM800_ALARM_WAKEUP) { + if (pdata && pdata->rtc) + pdata->rtc->rtc_wakeup = 1; + } + + ret = device_gpadc_init(chip, pdata); + if (ret < 0) { + dev_err(chip->dev, "[%s]Failed to init gpadc\n", __func__); + goto out; + } + + chip->regmap_irq_chip = &pm800_irq_chip; + + ret = device_irq_init_800(chip); + if (ret < 0) { + dev_err(chip->dev, "[%s]Failed to init pm800 irq\n", __func__); + goto out; + } + + ret = + mfd_add_devices(chip->dev, 0, &onkey_devs[0], + ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add onkey subdev\n"); + goto out_dev; + } else + dev_info(chip->dev, "[%s]:Added mfd onkey_devs\n", __func__); + + if (pdata && pdata->rtc) { + rtc_devs[0].platform_data = pdata->rtc; + rtc_devs[0].pdata_size = sizeof(struct pm80x_rtc_pdata); + ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0], + ARRAY_SIZE(rtc_devs), NULL, 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add rtc subdev\n"); + goto out_dev; + } else + dev_info(chip->dev, + "[%s]:Added mfd rtc_devs\n", __func__); + } + + return 0; +out_dev: + mfd_remove_devices(chip->dev); + device_irq_exit_800(chip); +out: + return ret; +} + +static int __devinit pm800_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct pm80x_chip *chip; + struct pm80x_platform_data *pdata = client->dev.platform_data; + struct pm80x_subchip *subchip; + + ret = pm80x_init(client, id); + if (ret) { + dev_err(&client->dev, "pm800_init fail\n"); + goto out_init; + } + + chip = i2c_get_clientdata(client); + + /* init subchip for PM800 */ + subchip = + devm_kzalloc(&client->dev, sizeof(struct pm80x_subchip), + GFP_KERNEL); + if (!subchip) { + ret = -ENOMEM; + goto err_subchip_alloc; + } + + subchip->power_page_addr = pdata->power_page_addr; + subchip->gpadc_page_addr = pdata->gpadc_page_addr; + chip->subchip = subchip; + + ret = device_800_init(chip, pdata); + if (ret) { + dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id); + goto err_800_init; + } + + ret = pm800_pages_init(chip); + if (ret) { + dev_err(&client->dev, "pm800_pages_init failed!\n"); + goto err_page_init; + } + + if (pdata->plat_config) + pdata->plat_config(chip, pdata); + +err_page_init: + mfd_remove_devices(chip->dev); + device_irq_exit_800(chip); +err_800_init: + devm_kfree(&client->dev, subchip); +err_subchip_alloc: + pm80x_deinit(client); +out_init: + return ret; +} + +static int __devexit pm800_remove(struct i2c_client *client) +{ + struct pm80x_chip *chip = i2c_get_clientdata(client); + + mfd_remove_devices(chip->dev); + device_irq_exit_800(chip); + + pm800_pages_exit(chip); + devm_kfree(&client->dev, chip->subchip); + + pm80x_deinit(client); + + return 0; +} + +static struct i2c_driver pm800_driver = { + .driver = { + .name = "88PM80X", + .owner = THIS_MODULE, + .pm = &pm80x_pm_ops, + }, + .probe = pm800_probe, + .remove = __devexit_p(pm800_remove), + .id_table = pm80x_id_table, +}; + +static int __init pm800_i2c_init(void) +{ + return i2c_add_driver(&pm800_driver); +} +subsys_initcall(pm800_i2c_init); + +static void __exit pm800_i2c_exit(void) +{ + i2c_del_driver(&pm800_driver); +} +module_exit(pm800_i2c_exit); + +MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM800"); +MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c new file mode 100644 index 000000000000..6146583589f6 --- /dev/null +++ b/drivers/mfd/88pm805.c @@ -0,0 +1,301 @@ +/* + * Base driver for Marvell 88PM805 + * + * Copyright (C) 2012 Marvell International Ltd. + * Haojian Zhuang <haojian.zhuang@marvell.com> + * Joseph(Yossi) Hanin <yhanin@marvell.com> + * Qiao Zhou <zhouqiao@marvell.com> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/irq.h> +#include <linux/mfd/core.h> +#include <linux/mfd/88pm80x.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#define PM805_CHIP_ID (0x00) + +static const struct i2c_device_id pm80x_id_table[] = { + {"88PM805", CHIP_PM805}, + {} /* NULL terminated */ +}; +MODULE_DEVICE_TABLE(i2c, pm80x_id_table); + +/* Interrupt Number in 88PM805 */ +enum { + PM805_IRQ_LDO_OFF, /*0 */ + PM805_IRQ_SRC_DPLL_LOCK, /*1 */ + PM805_IRQ_CLIP_FAULT, + PM805_IRQ_MIC_CONFLICT, + PM805_IRQ_HP2_SHRT, + PM805_IRQ_HP1_SHRT, /*5 */ + PM805_IRQ_FINE_PLL_FAULT, + PM805_IRQ_RAW_PLL_FAULT, + PM805_IRQ_VOLP_BTN_DET, + PM805_IRQ_VOLM_BTN_DET, + PM805_IRQ_SHRT_BTN_DET, /*10 */ + PM805_IRQ_MIC_DET, /*11 */ + + PM805_MAX_IRQ, +}; + +static struct resource codec_resources[] = { + { + /* Headset microphone insertion or removal */ + .name = "micin", + .start = PM805_IRQ_MIC_DET, + .end = PM805_IRQ_MIC_DET, + .flags = IORESOURCE_IRQ, + }, + { + /* Audio short HP1 */ + .name = "audio-short1", + .start = PM805_IRQ_HP1_SHRT, + .end = PM805_IRQ_HP1_SHRT, + .flags = IORESOURCE_IRQ, + }, + { + /* Audio short HP2 */ + .name = "audio-short2", + .start = PM805_IRQ_HP2_SHRT, + .end = PM805_IRQ_HP2_SHRT, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell codec_devs[] = { + { + .name = "88pm80x-codec", + .num_resources = ARRAY_SIZE(codec_resources), + .resources = &codec_resources[0], + .id = -1, + }, +}; + +static struct regmap_irq pm805_irqs[] = { + /* INT0 */ + [PM805_IRQ_LDO_OFF] = { + .mask = PM805_INT1_HP1_SHRT, + }, + [PM805_IRQ_SRC_DPLL_LOCK] = { + .mask = PM805_INT1_HP2_SHRT, + }, + [PM805_IRQ_CLIP_FAULT] = { + .mask = PM805_INT1_MIC_CONFLICT, + }, + [PM805_IRQ_MIC_CONFLICT] = { + .mask = PM805_INT1_CLIP_FAULT, + }, + [PM805_IRQ_HP2_SHRT] = { + .mask = PM805_INT1_LDO_OFF, + }, + [PM805_IRQ_HP1_SHRT] = { + .mask = PM805_INT1_SRC_DPLL_LOCK, + }, + /* INT1 */ + [PM805_IRQ_FINE_PLL_FAULT] = { + .reg_offset = 1, + .mask = PM805_INT2_MIC_DET, + }, + [PM805_IRQ_RAW_PLL_FAULT] = { + .reg_offset = 1, + .mask = PM805_INT2_SHRT_BTN_DET, + }, + [PM805_IRQ_VOLP_BTN_DET] = { + .reg_offset = 1, + .mask = PM805_INT2_VOLM_BTN_DET, + }, + [PM805_IRQ_VOLM_BTN_DET] = { + .reg_offset = 1, + .mask = PM805_INT2_VOLP_BTN_DET, + }, + [PM805_IRQ_SHRT_BTN_DET] = { + .reg_offset = 1, + .mask = PM805_INT2_RAW_PLL_FAULT, + }, + [PM805_IRQ_MIC_DET] = { + .reg_offset = 1, + .mask = PM805_INT2_FINE_PLL_FAULT, + }, +}; + +static int __devinit device_irq_init_805(struct pm80x_chip *chip) +{ + struct regmap *map = chip->regmap; + unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + int data, mask, ret = -EINVAL; + + if (!map || !chip->irq) { + dev_err(chip->dev, "incorrect parameters\n"); + return -EINVAL; + } + + /* + * irq_mode defines the way of clearing interrupt. it's read-clear by + * default. + */ + mask = + PM805_STATUS0_INT_CLEAR | PM805_STATUS0_INV_INT | + PM800_STATUS0_INT_MASK; + + data = PM805_STATUS0_INT_CLEAR; + ret = regmap_update_bits(map, PM805_INT_STATUS0, mask, data); + /* + * PM805_INT_STATUS is under 32K clock domain, so need to + * add proper delay before the next I2C register access. + */ + msleep(1); + + if (ret < 0) + goto out; + + ret = + regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1, + chip->regmap_irq_chip, &chip->irq_data); + +out: + return ret; +} + +static void device_irq_exit_805(struct pm80x_chip *chip) +{ + regmap_del_irq_chip(chip->irq, chip->irq_data); +} + +static struct regmap_irq_chip pm805_irq_chip = { + .name = "88pm805", + .irqs = pm805_irqs, + .num_irqs = ARRAY_SIZE(pm805_irqs), + + .num_regs = 2, + .status_base = PM805_INT_STATUS1, + .mask_base = PM805_INT_MASK1, + .ack_base = PM805_INT_STATUS1, +}; + +static int __devinit device_805_init(struct pm80x_chip *chip) +{ + int ret = 0; + unsigned int val; + struct regmap *map = chip->regmap; + + if (!map) { + dev_err(chip->dev, "regmap is invalid\n"); + return -EINVAL; + } + + ret = regmap_read(map, PM805_CHIP_ID, &val); + if (ret < 0) { + dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret); + goto out_irq_init; + } + chip->version = val; + + chip->regmap_irq_chip = &pm805_irq_chip; + + ret = device_irq_init_805(chip); + if (ret < 0) { + dev_err(chip->dev, "Failed to init pm805 irq!\n"); + goto out_irq_init; + } + + ret = mfd_add_devices(chip->dev, 0, &codec_devs[0], + ARRAY_SIZE(codec_devs), &codec_resources[0], 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add codec subdev\n"); + goto out_codec; + } else + dev_info(chip->dev, "[%s]:Added mfd codec_devs\n", __func__); + + return 0; + +out_codec: + device_irq_exit_805(chip); +out_irq_init: + return ret; +} + +static int __devinit pm805_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct pm80x_chip *chip; + struct pm80x_platform_data *pdata = client->dev.platform_data; + + ret = pm80x_init(client, id); + if (ret) { + dev_err(&client->dev, "pm805_init fail!\n"); + goto out_init; + } + + chip = i2c_get_clientdata(client); + + ret = device_805_init(chip); + if (ret) { + dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id); + goto err_805_init; + } + + if (pdata->plat_config) + pdata->plat_config(chip, pdata); + +err_805_init: + pm80x_deinit(client); +out_init: + return ret; +} + +static int __devexit pm805_remove(struct i2c_client *client) +{ + struct pm80x_chip *chip = i2c_get_clientdata(client); + + mfd_remove_devices(chip->dev); + device_irq_exit_805(chip); + + pm80x_deinit(client); + + return 0; +} + +static struct i2c_driver pm805_driver = { + .driver = { + .name = "88PM80X", + .owner = THIS_MODULE, + .pm = &pm80x_pm_ops, + }, + .probe = pm805_probe, + .remove = __devexit_p(pm805_remove), + .id_table = pm80x_id_table, +}; + +static int __init pm805_i2c_init(void) +{ + return i2c_add_driver(&pm805_driver); +} +subsys_initcall(pm805_i2c_init); + +static void __exit pm805_i2c_exit(void) +{ + i2c_del_driver(&pm805_driver); +} +module_exit(pm805_i2c_exit); + +MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM805"); +MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c new file mode 100644 index 000000000000..cd0bf527d764 --- /dev/null +++ b/drivers/mfd/88pm80x.c @@ -0,0 +1,145 @@ +/* + * I2C driver for Marvell 88PM80x + * + * Copyright (C) 2012 Marvell International Ltd. + * Haojian Zhuang <haojian.zhuang@marvell.com> + * Joseph(Yossi) Hanin <yhanin@marvell.com> + * Qiao Zhou <zhouqiao@marvell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/mfd/88pm80x.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/err.h> + +/* + * workaround: some registers needed by pm805 are defined in pm800, so + * need to use this global variable to maintain the relation between + * pm800 and pm805. would remove it after HW chip fixes the issue. + */ +static struct pm80x_chip *g_pm80x_chip; + +const struct regmap_config pm80x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; +EXPORT_SYMBOL_GPL(pm80x_regmap_config); + +int __devinit pm80x_init(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pm80x_chip *chip; + struct regmap *map; + int ret = 0; + + chip = + devm_kzalloc(&client->dev, sizeof(struct pm80x_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + map = devm_regmap_init_i2c(client, &pm80x_regmap_config); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + ret); + goto err_regmap_init; + } + + chip->id = id->driver_data; + if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) { + ret = -EINVAL; + goto err_chip_id; + } + + chip->client = client; + chip->regmap = map; + + chip->irq = client->irq; + + chip->dev = &client->dev; + dev_set_drvdata(chip->dev, chip); + i2c_set_clientdata(chip->client, chip); + + device_init_wakeup(&client->dev, 1); + + /* + * workaround: set g_pm80x_chip to the first probed chip. if the + * second chip is probed, just point to the companion to each + * other so that pm805 can access those specific register. would + * remove it after HW chip fixes the issue. + */ + if (!g_pm80x_chip) + g_pm80x_chip = chip; + else { + chip->companion = g_pm80x_chip->client; + g_pm80x_chip->companion = chip->client; + } + + return 0; + +err_chip_id: + regmap_exit(map); +err_regmap_init: + devm_kfree(&client->dev, chip); + return ret; +} +EXPORT_SYMBOL_GPL(pm80x_init); + +int pm80x_deinit(struct i2c_client *client) +{ + struct pm80x_chip *chip = i2c_get_clientdata(client); + + /* + * workaround: clear the dependency between pm800 and pm805. + * would remove it after HW chip fixes the issue. + */ + if (g_pm80x_chip->companion) + g_pm80x_chip->companion = NULL; + else + g_pm80x_chip = NULL; + + regmap_exit(chip->regmap); + devm_kfree(&client->dev, chip); + + return 0; +} +EXPORT_SYMBOL_GPL(pm80x_deinit); + +#ifdef CONFIG_PM_SLEEP +static int pm80x_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct pm80x_chip *chip = i2c_get_clientdata(client); + + if (chip && chip->wu_flag) + if (device_may_wakeup(chip->dev)) + enable_irq_wake(chip->irq); + + return 0; +} + +static int pm80x_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct pm80x_chip *chip = i2c_get_clientdata(client); + + if (chip && chip->wu_flag) + if (device_may_wakeup(chip->dev)) + disable_irq_wake(chip->irq); + + return 0; +} +#endif + +SIMPLE_DEV_PM_OPS(pm80x_pm_ops, pm80x_suspend, pm80x_resume); +EXPORT_SYMBOL_GPL(pm80x_pm_ops); + +MODULE_DESCRIPTION("I2C Driver for Marvell 88PM80x"); +MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 87bd5ba38d5b..d09918cf1b15 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -90,6 +90,10 @@ static struct resource charger_resources[] __devinitdata = { {PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,}, }; +static struct resource preg_resources[] __devinitdata = { + {PM8606_ID_PREG, PM8606_ID_PREG, "preg", IORESOURCE_IO,}, +}; + static struct resource rtc_resources[] __devinitdata = { {PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,}, }; @@ -142,9 +146,19 @@ static struct mfd_cell codec_devs[] = { {"88pm860x-codec", -1,}, }; +static struct regulator_consumer_supply preg_supply[] = { + REGULATOR_SUPPLY("preg", "charger-manager"), +}; + +static struct regulator_init_data preg_init_data = { + .num_consumer_supplies = ARRAY_SIZE(preg_supply), + .consumer_supplies = &preg_supply[0], +}; + static struct mfd_cell power_devs[] = { {"88pm860x-battery", -1,}, {"88pm860x-charger", -1,}, + {"88pm860x-preg", -1,}, }; static struct mfd_cell rtc_devs[] = { @@ -768,6 +782,15 @@ static void __devinit device_power_init(struct pm860x_chip *chip, &charger_resources[0], chip->irq_base); if (ret < 0) dev_err(chip->dev, "Failed to add charger subdev\n"); + + power_devs[2].platform_data = &preg_init_data; + power_devs[2].pdata_size = sizeof(struct regulator_init_data); + power_devs[2].num_resources = ARRAY_SIZE(preg_resources); + power_devs[2].resources = &preg_resources[0], + ret = mfd_add_devices(chip->dev, 0, &power_devs[2], 1, + &preg_resources[0], chip->irq_base); + if (ret < 0) + dev_err(chip->dev, "Failed to add preg subdev\n"); } static void __devinit device_onkey_init(struct pm860x_chip *chip, diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 92144ed1ad46..d1facef28a60 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -7,6 +7,7 @@ menu "Multifunction device drivers" config MFD_CORE tristate + select IRQ_DOMAIN default n config MFD_88PM860X @@ -20,6 +21,30 @@ config MFD_88PM860X select individual components like voltage regulators, RTC and battery-charger under the corresponding menus. +config MFD_88PM800 + tristate "Support Marvell 88PM800" + depends on I2C=y && GENERIC_HARDIRQS + select REGMAP_I2C + select REGMAP_IRQ + select MFD_CORE + help + This supports for Marvell 88PM800 Power Management IC. + This includes the I2C driver and the core APIs _only_, you have to + select individual components like voltage regulators, RTC and + battery-charger under the corresponding menus. + +config MFD_88PM805 + tristate "Support Marvell 88PM805" + depends on I2C=y && GENERIC_HARDIRQS + select REGMAP_I2C + select REGMAP_IRQ + select MFD_CORE + help + This supports for Marvell 88PM805 Power Management IC. This includes + the I2C driver and the core APIs _only_, you have to select individual + components like codec device, headset/Mic device under the + corresponding menus. + config MFD_SM501 tristate "Support for Silicon Motion SM501" ---help--- @@ -173,8 +198,9 @@ config MFD_TPS65217 config MFD_TPS6586X bool "TPS6586x Power Management chips" - depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE + select REGMAP_I2C depends on REGULATOR help If you say yes here you get support for the TPS6586X series of @@ -276,6 +302,7 @@ config TWL6030_PWM tristate "TWL6030 PWM (Pulse Width Modulator) Support" depends on TWL4030_CORE select HAVE_PWM + depends on !PWM default n help Say yes here if you want support for TWL6030 PWM. @@ -423,6 +450,19 @@ config PMIC_ADP5520 individual components like LCD backlight, LEDs, GPIOs and Kepad under the corresponding menus. +config MFD_MAX77686 + bool "Maxim Semiconductor MAX77686 PMIC Support" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C + select IRQ_DOMAIN + help + Say yes here to support for Maxim Semiconductor MAX77686. + This is a Power Management IC with RTC on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. + config MFD_MAX77693 bool "Maxim Semiconductor MAX77693 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS @@ -450,6 +490,7 @@ config MFD_MAX8997 bool "Maxim Semiconductor MAX8997/8966 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE + select IRQ_DOMAIN help Say yes here to support for Maxim Semiconductor MAX8997/8966. This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic, @@ -469,17 +510,56 @@ config MFD_MAX8998 additional drivers must be enabled in order to use the functionality of the device. -config MFD_S5M_CORE - bool "SAMSUNG S5M Series Support" +config MFD_SEC_CORE + bool "SAMSUNG Electronics PMIC Series Support" depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C + select REGMAP_IRQ help - Support for the Samsung Electronics S5M MFD series. + Support for the Samsung Electronics MFD series. This driver provides common support for accessing the device, additional drivers must be enabled in order to use the functionality of the device +config MFD_ARIZONA + select REGMAP + select REGMAP_IRQ + select MFD_CORE + bool + +config MFD_ARIZONA_I2C + tristate "Support Wolfson Microelectronics Arizona platform with I2C" + select MFD_ARIZONA + select MFD_CORE + select REGMAP_I2C + depends on I2C + help + Support for the Wolfson Microelectronics Arizona platform audio SoC + core functionality controlled via I2C. + +config MFD_ARIZONA_SPI + tristate "Support Wolfson Microelectronics Arizona platform with SPI" + select MFD_ARIZONA + select MFD_CORE + select REGMAP_SPI + depends on SPI_MASTER + help + Support for the Wolfson Microelectronics Arizona platform audio SoC + core functionality controlled via I2C. + +config MFD_WM5102 + bool "Support Wolfson Microelectronics WM5102" + depends on MFD_ARIZONA + help + Support for Wolfson Microelectronics WM5102 low power audio SoC + +config MFD_WM5110 + bool "Support Wolfson Microelectronics WM5110" + depends on MFD_ARIZONA + help + Support for Wolfson Microelectronics WM5110 low power audio SoC + config MFD_WM8400 bool "Support Wolfson Microelectronics WM8400" select MFD_CORE @@ -697,6 +777,7 @@ config AB8500_CORE bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU select MFD_CORE + select IRQ_DOMAIN help Select this option to enable access to AB8500 power management chip. This connects to U8500 either on the SSP/SPI bus (deprecated @@ -704,16 +785,6 @@ config AB8500_CORE the irq_chip parts for handling the Mixed Signal chip events. This chip embeds various other multimedia funtionalities as well. -config AB8500_I2C_CORE - bool "AB8500 register access via PRCMU I2C" - depends on AB8500_CORE && MFD_DB8500_PRCMU - default y - help - This enables register access to the AB8500 chip via PRCMU I2C. - The AB8500 chip can be accessed via SPI or I2C. On DB8500 hardware - the I2C bus is connected to the Power Reset - and Mangagement Unit, PRCMU. - config AB8500_DEBUG bool "Enable debug info via debugfs" depends on AB8500_CORE && DEBUG_FS diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 75f6ed68a4b9..79dd22d1dc3d 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -4,6 +4,8 @@ 88pm860x-objs := 88pm860x-core.o 88pm860x-i2c.o obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o +obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o +obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o @@ -24,6 +26,16 @@ obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o +obj-$(CONFIG_MFD_ARIZONA) += arizona-core.o +obj-$(CONFIG_MFD_ARIZONA) += arizona-irq.o +obj-$(CONFIG_MFD_ARIZONA_I2C) += arizona-i2c.o +obj-$(CONFIG_MFD_ARIZONA_SPI) += arizona-spi.o +ifneq ($(CONFIG_MFD_WM5102),n) +obj-$(CONFIG_MFD_ARIZONA) += wm5102-tables.o +endif +ifneq ($(CONFIG_MFD_WM5110),n) +obj-$(CONFIG_MFD_ARIZONA) += wm5110-tables.o +endif obj-$(CONFIG_MFD_WM8400) += wm8400-core.o wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o wm831x-objs += wm831x-auxadc.o @@ -78,6 +90,7 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-core.o obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o +obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o @@ -116,6 +129,6 @@ obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o obj-$(CONFIG_MFD_PALMAS) += palmas.o obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o -obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o +obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c index 1efad20fb175..4276aab4f196 100644 --- a/drivers/mfd/ab3100-core.c +++ b/drivers/mfd/ab3100-core.c @@ -867,7 +867,7 @@ static int __devinit ab3100_probe(struct i2c_client *client, int err; int i; - ab3100 = kzalloc(sizeof(struct ab3100), GFP_KERNEL); + ab3100 = devm_kzalloc(&client->dev, sizeof(struct ab3100), GFP_KERNEL); if (!ab3100) { dev_err(&client->dev, "could not allocate AB3100 device\n"); return -ENOMEM; @@ -921,7 +921,7 @@ static int __devinit ab3100_probe(struct i2c_client *client, /* Attach a second dummy i2c_client to the test register address */ ab3100->testreg_client = i2c_new_dummy(client->adapter, - client->addr + 1); + client->addr + 1); if (!ab3100->testreg_client) { err = -ENOMEM; goto exit_no_testreg_client; @@ -931,13 +931,13 @@ static int __devinit ab3100_probe(struct i2c_client *client, if (err) goto exit_no_setup; - err = request_threaded_irq(client->irq, NULL, ab3100_irq_handler, - IRQF_ONESHOT, "ab3100-core", ab3100); - /* This real unpredictable IRQ is of course sampled for entropy */ - rand_initialize_irq(client->irq); - + err = devm_request_threaded_irq(&client->dev, + client->irq, NULL, ab3100_irq_handler, + IRQF_ONESHOT, "ab3100-core", ab3100); if (err) goto exit_no_irq; + /* This real unpredictable IRQ is of course sampled for entropy */ + rand_initialize_irq(client->irq); err = abx500_register_ops(&client->dev, &ab3100_ops); if (err) @@ -962,7 +962,6 @@ static int __devinit ab3100_probe(struct i2c_client *client, i2c_unregister_device(ab3100->testreg_client); exit_no_testreg_client: exit_no_detect: - kfree(ab3100); return err; } @@ -972,16 +971,8 @@ static int __devexit ab3100_remove(struct i2c_client *client) /* Unregister subdevices */ mfd_remove_devices(&client->dev); - ab3100_remove_debugfs(); i2c_unregister_device(ab3100->testreg_client); - - /* - * At this point, all subscribers should have unregistered - * their notifiers so deactivate IRQ - */ - free_irq(client->irq, ab3100); - kfree(ab3100); return 0; } diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index dac0e2998603..626b4ecaf647 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -11,6 +11,7 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/module.h> @@ -140,7 +141,7 @@ static const char ab8500_version_str[][7] = { [AB8500_VERSION_AB8540] = "AB8540", }; -static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data) +static int ab8500_prcmu_write(struct ab8500 *ab8500, u16 addr, u8 data) { int ret; @@ -150,7 +151,7 @@ static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data) return ret; } -static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask, +static int ab8500_prcmu_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask, u8 data) { int ret; @@ -162,7 +163,7 @@ static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask, return ret; } -static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr) +static int ab8500_prcmu_read(struct ab8500 *ab8500, u16 addr) { int ret; u8 data; @@ -361,7 +362,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data) static void ab8500_irq_mask(struct irq_data *data) { struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); - int offset = data->irq - ab8500->irq_base; + int offset = data->hwirq; int index = offset / 8; int mask = 1 << (offset % 8); @@ -371,7 +372,7 @@ static void ab8500_irq_mask(struct irq_data *data) static void ab8500_irq_unmask(struct irq_data *data) { struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); - int offset = data->irq - ab8500->irq_base; + int offset = data->hwirq; int index = offset / 8; int mask = 1 << (offset % 8); @@ -510,38 +511,51 @@ static irqreturn_t ab8500_irq(int irq, void *dev) return IRQ_HANDLED; } -static int ab8500_irq_init(struct ab8500 *ab8500) +/** + * ab8500_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ + * + * @ab8500: ab8500_irq controller to operate on. + * @irq: index of the interrupt requested in the chip IRQs + * + * Useful for drivers to request their own IRQs. + */ +int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq) { - int base = ab8500->irq_base; - int irq; - int num_irqs; + if (!ab8500) + return -EINVAL; - if (is_ab9540(ab8500)) - num_irqs = AB9540_NR_IRQS; - else if (is_ab8505(ab8500)) - num_irqs = AB8505_NR_IRQS; - else - num_irqs = AB8500_NR_IRQS; + return irq_create_mapping(ab8500->domain, irq); +} +EXPORT_SYMBOL_GPL(ab8500_irq_get_virq); + +static int ab8500_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hwirq) +{ + struct ab8500 *ab8500 = d->host_data; - for (irq = base; irq < base + num_irqs; irq++) { - irq_set_chip_data(irq, ab8500); - irq_set_chip_and_handler(irq, &ab8500_irq_chip, - handle_simple_irq); - irq_set_nested_thread(irq, 1); + if (!ab8500) + return -EINVAL; + + irq_set_chip_data(virq, ab8500); + irq_set_chip_and_handler(virq, &ab8500_irq_chip, + handle_simple_irq); + irq_set_nested_thread(virq, 1); #ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); + set_irq_flags(virq, IRQF_VALID); #else - irq_set_noprobe(irq); + irq_set_noprobe(virq); #endif - } return 0; } -static void ab8500_irq_remove(struct ab8500 *ab8500) +static struct irq_domain_ops ab8500_irq_ops = { + .map = ab8500_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np) { - int base = ab8500->irq_base; - int irq; int num_irqs; if (is_ab9540(ab8500)) @@ -551,13 +565,22 @@ static void ab8500_irq_remove(struct ab8500 *ab8500) else num_irqs = AB8500_NR_IRQS; - for (irq = base; irq < base + num_irqs; irq++) { -#ifdef CONFIG_ARM - set_irq_flags(irq, 0); -#endif - irq_set_chip_and_handler(irq, NULL, NULL); - irq_set_chip_data(irq, NULL); + if (ab8500->irq_base) { + ab8500->domain = irq_domain_add_legacy( + NULL, num_irqs, ab8500->irq_base, + 0, &ab8500_irq_ops, ab8500); + } + else { + ab8500->domain = irq_domain_add_linear( + np, num_irqs, &ab8500_irq_ops, ab8500); + } + + if (!ab8500->domain) { + dev_err(ab8500->dev, "Failed to create irqdomain\n"); + return -ENOSYS; } + + return 0; } int ab8500_suspend(struct ab8500 *ab8500) @@ -947,54 +970,69 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = { #ifdef CONFIG_DEBUG_FS { .name = "ab8500-debug", + .of_compatible = "stericsson,ab8500-debug", .num_resources = ARRAY_SIZE(ab8500_debug_resources), .resources = ab8500_debug_resources, }, #endif { .name = "ab8500-sysctrl", + .of_compatible = "stericsson,ab8500-sysctrl", }, { .name = "ab8500-regulator", + .of_compatible = "stericsson,ab8500-regulator", }, { .name = "ab8500-gpadc", + .of_compatible = "stericsson,ab8500-gpadc", .num_resources = ARRAY_SIZE(ab8500_gpadc_resources), .resources = ab8500_gpadc_resources, }, { .name = "ab8500-rtc", + .of_compatible = "stericsson,ab8500-rtc", .num_resources = ARRAY_SIZE(ab8500_rtc_resources), .resources = ab8500_rtc_resources, }, { .name = "ab8500-acc-det", + .of_compatible = "stericsson,ab8500-acc-det", .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources), .resources = ab8500_av_acc_detect_resources, }, { .name = "ab8500-poweron-key", + .of_compatible = "stericsson,ab8500-poweron-key", .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources), .resources = ab8500_poweronkey_db_resources, }, { .name = "ab8500-pwm", + .of_compatible = "stericsson,ab8500-pwm", .id = 1, }, { .name = "ab8500-pwm", + .of_compatible = "stericsson,ab8500-pwm", .id = 2, }, { .name = "ab8500-pwm", + .of_compatible = "stericsson,ab8500-pwm", .id = 3, }, - { .name = "ab8500-leds", }, + { + .name = "ab8500-leds", + .of_compatible = "stericsson,ab8500-leds", + }, { .name = "ab8500-denc", + .of_compatible = "stericsson,ab8500-denc", }, { .name = "ab8500-temp", + .of_compatible = "stericsson,ab8500-temp", .num_resources = ARRAY_SIZE(ab8500_temp_resources), .resources = ab8500_temp_resources, }, @@ -1026,11 +1064,13 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = { static struct mfd_cell __devinitdata ab8500_devs[] = { { .name = "ab8500-gpio", + .of_compatible = "stericsson,ab8500-gpio", .num_resources = ARRAY_SIZE(ab8500_gpio_resources), .resources = ab8500_gpio_resources, }, { .name = "ab8500-usb", + .of_compatible = "stericsson,ab8500-usb", .num_resources = ARRAY_SIZE(ab8500_usb_resources), .resources = ab8500_usb_resources, }, @@ -1207,16 +1247,17 @@ static struct attribute_group ab9540_attr_group = { .attrs = ab9540_sysfs_entries, }; -static const struct of_device_id ab8500_match[] = { - { - .compatible = "stericsson,ab8500", - .data = (void *)AB8500_VERSION_AB8500, - }, - {}, -}; - static int __devinit ab8500_probe(struct platform_device *pdev) { + static char *switch_off_status[] = { + "Swoff bit programming", + "Thermal protection activation", + "Vbat lower then BattOk falling threshold", + "Watchdog expired", + "Non presence of 32kHz clock", + "Battery level lower than power on reset threshold", + "Power on key 1 pressed longer than 10 seconds", + "DB8500 thermal shutdown"}; struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev); const struct platform_device_id *platid = platform_get_device_id(pdev); enum ab8500_version version = AB8500_VERSION_UNDEFINED; @@ -1233,14 +1274,6 @@ static int __devinit ab8500_probe(struct platform_device *pdev) if (plat) ab8500->irq_base = plat->irq_base; - else if (np) - ret = of_property_read_u32(np, "stericsson,irq-base", &ab8500->irq_base); - - if (!ab8500->irq_base) { - dev_info(&pdev->dev, "couldn't find irq-base\n"); - ret = -EINVAL; - goto out_free_ab8500; - } ab8500->dev = &pdev->dev; @@ -1252,9 +1285,9 @@ static int __devinit ab8500_probe(struct platform_device *pdev) ab8500->irq = resource->start; - ab8500->read = ab8500_i2c_read; - ab8500->write = ab8500_i2c_write; - ab8500->write_masked = ab8500_i2c_write_masked; + ab8500->read = ab8500_prcmu_read; + ab8500->write = ab8500_prcmu_write; + ab8500->write_masked = ab8500_prcmu_write_masked; mutex_init(&ab8500->lock); mutex_init(&ab8500->irq_lock); @@ -1264,9 +1297,6 @@ static int __devinit ab8500_probe(struct platform_device *pdev) if (platid) version = platid->driver_data; - else if (np) - version = (unsigned int) - of_match_device(ab8500_match, &pdev->dev)->data; if (version != AB8500_VERSION_UNDEFINED) ab8500->version = version; @@ -1323,7 +1353,20 @@ static int __devinit ab8500_probe(struct platform_device *pdev) AB8500_SWITCH_OFF_STATUS, &value); if (ret < 0) return ret; - dev_info(ab8500->dev, "switch off status: %#x", value); + dev_info(ab8500->dev, "switch off cause(s) (%#x): ", value); + + if (value) { + for (i = 0; i < ARRAY_SIZE(switch_off_status); i++) { + if (value & 1) + printk(KERN_CONT " \"%s\"", + switch_off_status[i]); + value = value >> 1; + + } + printk(KERN_CONT "\n"); + } else { + printk(KERN_CONT " None\n"); + } if (plat && plat->init) plat->init(ab8500); @@ -1352,53 +1395,50 @@ static int __devinit ab8500_probe(struct platform_device *pdev) for (i = 0; i < ab8500->mask_size; i++) ab8500->mask[i] = ab8500->oldmask[i] = 0xff; - if (ab8500->irq_base) { - ret = ab8500_irq_init(ab8500); - if (ret) - goto out_freeoldmask; + ret = ab8500_irq_init(ab8500, np); + if (ret) + goto out_freeoldmask; - /* Activate this feature only in ab9540 */ - /* till tests are done on ab8500 1p2 or later*/ - if (is_ab9540(ab8500)) - ret = request_threaded_irq(ab8500->irq, NULL, + /* Activate this feature only in ab9540 */ + /* till tests are done on ab8500 1p2 or later*/ + if (is_ab9540(ab8500)) { + ret = request_threaded_irq(ab8500->irq, NULL, ab8500_hierarchical_irq, IRQF_ONESHOT | IRQF_NO_SUSPEND, "ab8500", ab8500); - else - ret = request_threaded_irq(ab8500->irq, NULL, + } + else { + ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq, IRQF_ONESHOT | IRQF_NO_SUSPEND, "ab8500", ab8500); if (ret) - goto out_removeirq; + goto out_freeoldmask; } - if (!np) { - ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs, - ARRAY_SIZE(abx500_common_devs), NULL, - ab8500->irq_base); + ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs, + ARRAY_SIZE(abx500_common_devs), NULL, + ab8500->irq_base); + if (ret) + goto out_freeirq; - if (ret) - goto out_freeirq; - - if (is_ab9540(ab8500)) - ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs, - ARRAY_SIZE(ab9540_devs), NULL, - ab8500->irq_base); - else - ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, - ARRAY_SIZE(ab8500_devs), NULL, - ab8500->irq_base); - if (ret) - goto out_freeirq; + if (is_ab9540(ab8500)) + ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs, + ARRAY_SIZE(ab9540_devs), NULL, + ab8500->irq_base); + else + ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, + ARRAY_SIZE(ab8500_devs), NULL, + ab8500->irq_base); + if (ret) + goto out_freeirq; - if (is_ab9540(ab8500) || is_ab8505(ab8500)) - ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs, - ARRAY_SIZE(ab9540_ab8505_devs), NULL, - ab8500->irq_base); - if (ret) - goto out_freeirq; - } + if (is_ab9540(ab8500) || is_ab8505(ab8500)) + ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs, + ARRAY_SIZE(ab9540_ab8505_devs), NULL, + ab8500->irq_base); + if (ret) + goto out_freeirq; if (!no_bm) { /* Add battery management devices */ @@ -1417,15 +1457,11 @@ static int __devinit ab8500_probe(struct platform_device *pdev) &ab8500_attr_group); if (ret) dev_err(ab8500->dev, "error creating sysfs entries\n"); - else - return ret; + + return ret; out_freeirq: - if (ab8500->irq_base) - free_irq(ab8500->irq, ab8500); -out_removeirq: - if (ab8500->irq_base) - ab8500_irq_remove(ab8500); + free_irq(ab8500->irq, ab8500); out_freeoldmask: kfree(ab8500->oldmask); out_freemask: @@ -1444,11 +1480,10 @@ static int __devexit ab8500_remove(struct platform_device *pdev) sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group); else sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); + mfd_remove_devices(ab8500->dev); - if (ab8500->irq_base) { - free_irq(ab8500->irq, ab8500); - ab8500_irq_remove(ab8500); - } + free_irq(ab8500->irq, ab8500); + kfree(ab8500->oldmask); kfree(ab8500->mask); kfree(ab8500); @@ -1468,7 +1503,6 @@ static struct platform_driver ab8500_core_driver = { .driver = { .name = "ab8500-core", .owner = THIS_MODULE, - .of_match_table = ab8500_match, }, .probe = ab8500_probe, .remove = __devexit_p(ab8500_remove), @@ -1484,7 +1518,7 @@ static void __exit ab8500_core_exit(void) { platform_driver_unregister(&ab8500_core_driver); } -arch_initcall(ab8500_core_init); +core_initcall(ab8500_core_init); module_exit(ab8500_core_exit); MODULE_AUTHOR("Mattias Wallin, Srinidhi Kasagar, Rabin Vincent"); diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 50c4c89ab220..c4cb806978ac 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -31,12 +31,12 @@ struct ab8500_reg_range { }; /** - * struct ab8500_i2c_ranges + * struct ab8500_prcmu_ranges * @num_ranges: the number of ranges in the list * @bankid: bank identifier * @range: the list of register ranges */ -struct ab8500_i2c_ranges { +struct ab8500_prcmu_ranges { u8 num_ranges; u8 bankid; const struct ab8500_reg_range *range; @@ -47,7 +47,7 @@ struct ab8500_i2c_ranges { #define AB8500_REV_REG 0x80 -static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = { +static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = { [0x0] = { .num_ranges = 0, .range = 0, @@ -608,16 +608,10 @@ static int __devexit ab8500_debug_remove(struct platform_device *plf) return 0; } -static const struct of_device_id ab8500_debug_match[] = { - { .compatible = "stericsson,ab8500-debug", }, - {} -}; - static struct platform_driver ab8500_debug_driver = { .driver = { .name = "ab8500-debug", .owner = THIS_MODULE, - .of_match_table = ab8500_debug_match, }, .probe = ab8500_debug_probe, .remove = __devexit_p(ab8500_debug_remove) diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index b86fd8e1ec3f..866f95960b4b 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -599,7 +599,8 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev) /* Register interrupt - SwAdcComplete */ ret = request_threaded_irq(gpadc->irq, NULL, ab8500_bm_gpswadcconvend_handler, - IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc", gpadc); + IRQF_ONESHOT | IRQF_NO_SUSPEND | IRQF_SHARED, + "ab8500-gpadc", gpadc); if (ret < 0) { dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n", gpadc->irq); @@ -648,18 +649,12 @@ static int __devexit ab8500_gpadc_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id ab8500_gpadc_match[] = { - { .compatible = "stericsson,ab8500-gpadc", }, - {} -}; - static struct platform_driver ab8500_gpadc_driver = { .probe = ab8500_gpadc_probe, .remove = __devexit_p(ab8500_gpadc_remove), .driver = { .name = "ab8500-gpadc", .owner = THIS_MODULE, - .of_match_table = ab8500_gpadc_match, }, }; diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 5a3e51ccf258..c28d4eb1eff0 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -61,16 +61,10 @@ static int __devexit ab8500_sysctrl_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id ab8500_sysctrl_match[] = { - { .compatible = "stericsson,ab8500-sysctrl", }, - {} -}; - static struct platform_driver ab8500_sysctrl_driver = { .driver = { .name = "ab8500-sysctrl", .owner = THIS_MODULE, - .of_match_table = ab8500_sysctrl_match, }, .probe = ab8500_sysctrl_probe, .remove = __devexit_p(ab8500_sysctrl_remove), diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c index 8d816cce8322..ea8b9475731d 100644 --- a/drivers/mfd/adp5520.c +++ b/drivers/mfd/adp5520.c @@ -320,7 +320,7 @@ static int __devexit adp5520_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int adp5520_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); diff --git a/drivers/mfd/anatop-mfd.c b/drivers/mfd/anatop-mfd.c index 6da06341f6c9..5576e07576de 100644 --- a/drivers/mfd/anatop-mfd.c +++ b/drivers/mfd/anatop-mfd.c @@ -83,7 +83,7 @@ static int __devinit of_anatop_probe(struct platform_device *pdev) drvdata->ioreg = ioreg; spin_lock_init(&drvdata->reglock); platform_set_drvdata(pdev, drvdata); - of_platform_populate(np, of_anatop_match, NULL, dev); + of_platform_populate(np, NULL, NULL, dev); return 0; } diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c new file mode 100644 index 000000000000..c7983e862549 --- /dev/null +++ b/drivers/mfd/arizona-core.c @@ -0,0 +1,566 @@ +/* + * Arizona core driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/registers.h> + +#include "arizona.h" + +static const char *wm5102_core_supplies[] = { + "AVDD", + "DBVDD1", +}; + +int arizona_clk32k_enable(struct arizona *arizona) +{ + int ret = 0; + + mutex_lock(&arizona->clk_lock); + + arizona->clk32k_ref++; + + if (arizona->clk32k_ref == 1) + ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, + ARIZONA_CLK_32K_ENA, + ARIZONA_CLK_32K_ENA); + + if (ret != 0) + arizona->clk32k_ref--; + + mutex_unlock(&arizona->clk_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(arizona_clk32k_enable); + +int arizona_clk32k_disable(struct arizona *arizona) +{ + int ret = 0; + + mutex_lock(&arizona->clk_lock); + + BUG_ON(arizona->clk32k_ref <= 0); + + arizona->clk32k_ref--; + + if (arizona->clk32k_ref == 0) + regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, + ARIZONA_CLK_32K_ENA, 0); + + mutex_unlock(&arizona->clk_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(arizona_clk32k_disable); + +static irqreturn_t arizona_clkgen_err(int irq, void *data) +{ + struct arizona *arizona = data; + + dev_err(arizona->dev, "CLKGEN error\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t arizona_underclocked(int irq, void *data) +{ + struct arizona *arizona = data; + unsigned int val; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_8, + &val); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read underclock status: %d\n", + ret); + return IRQ_NONE; + } + + if (val & ARIZONA_AIF3_UNDERCLOCKED_STS) + dev_err(arizona->dev, "AIF3 underclocked\n"); + if (val & ARIZONA_AIF3_UNDERCLOCKED_STS) + dev_err(arizona->dev, "AIF3 underclocked\n"); + if (val & ARIZONA_AIF2_UNDERCLOCKED_STS) + dev_err(arizona->dev, "AIF1 underclocked\n"); + if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS) + dev_err(arizona->dev, "ISRC2 underclocked\n"); + if (val & ARIZONA_ISRC1_UNDERCLOCKED_STS) + dev_err(arizona->dev, "ISRC1 underclocked\n"); + if (val & ARIZONA_FX_UNDERCLOCKED_STS) + dev_err(arizona->dev, "FX underclocked\n"); + if (val & ARIZONA_ASRC_UNDERCLOCKED_STS) + dev_err(arizona->dev, "ASRC underclocked\n"); + if (val & ARIZONA_DAC_UNDERCLOCKED_STS) + dev_err(arizona->dev, "DAC underclocked\n"); + if (val & ARIZONA_ADC_UNDERCLOCKED_STS) + dev_err(arizona->dev, "ADC underclocked\n"); + if (val & ARIZONA_MIXER_UNDERCLOCKED_STS) + dev_err(arizona->dev, "Mixer underclocked\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t arizona_overclocked(int irq, void *data) +{ + struct arizona *arizona = data; + unsigned int val[2]; + int ret; + + ret = regmap_bulk_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_6, + &val[0], 2); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read overclock status: %d\n", + ret); + return IRQ_NONE; + } + + if (val[0] & ARIZONA_PWM_OVERCLOCKED_STS) + dev_err(arizona->dev, "PWM overclocked\n"); + if (val[0] & ARIZONA_FX_CORE_OVERCLOCKED_STS) + dev_err(arizona->dev, "FX core overclocked\n"); + if (val[0] & ARIZONA_DAC_SYS_OVERCLOCKED_STS) + dev_err(arizona->dev, "DAC SYS overclocked\n"); + if (val[0] & ARIZONA_DAC_WARP_OVERCLOCKED_STS) + dev_err(arizona->dev, "DAC WARP overclocked\n"); + if (val[0] & ARIZONA_ADC_OVERCLOCKED_STS) + dev_err(arizona->dev, "ADC overclocked\n"); + if (val[0] & ARIZONA_MIXER_OVERCLOCKED_STS) + dev_err(arizona->dev, "Mixer overclocked\n"); + if (val[0] & ARIZONA_AIF3_SYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "AIF3 overclocked\n"); + if (val[0] & ARIZONA_AIF2_SYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "AIF2 overclocked\n"); + if (val[0] & ARIZONA_AIF1_SYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "AIF1 overclocked\n"); + if (val[0] & ARIZONA_PAD_CTRL_OVERCLOCKED_STS) + dev_err(arizona->dev, "Pad control overclocked\n"); + + if (val[1] & ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS) + dev_err(arizona->dev, "Slimbus subsystem overclocked\n"); + if (val[1] & ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "Slimbus async overclocked\n"); + if (val[1] & ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "Slimbus sync overclocked\n"); + if (val[1] & ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS) + dev_err(arizona->dev, "ASRC async system overclocked\n"); + if (val[1] & ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS) + dev_err(arizona->dev, "ASRC async WARP overclocked\n"); + if (val[1] & ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS) + dev_err(arizona->dev, "ASRC sync system overclocked\n"); + if (val[1] & ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS) + dev_err(arizona->dev, "ASRC sync WARP overclocked\n"); + if (val[1] & ARIZONA_ADSP2_1_OVERCLOCKED_STS) + dev_err(arizona->dev, "DSP1 overclocked\n"); + if (val[1] & ARIZONA_ISRC2_OVERCLOCKED_STS) + dev_err(arizona->dev, "ISRC2 overclocked\n"); + if (val[1] & ARIZONA_ISRC1_OVERCLOCKED_STS) + dev_err(arizona->dev, "ISRC1 overclocked\n"); + + return IRQ_HANDLED; +} + +static int arizona_wait_for_boot(struct arizona *arizona) +{ + unsigned int reg; + int ret, i; + + /* + * We can't use an interrupt as we need to runtime resume to do so, + * we won't race with the interrupt handler as it'll be blocked on + * runtime resume. + */ + for (i = 0; i < 5; i++) { + msleep(1); + + ret = regmap_read(arizona->regmap, + ARIZONA_INTERRUPT_RAW_STATUS_5, ®); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read boot state: %d\n", + ret); + continue; + } + + if (reg & ARIZONA_BOOT_DONE_STS) + break; + } + + if (reg & ARIZONA_BOOT_DONE_STS) { + regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5, + ARIZONA_BOOT_DONE_STS); + } else { + dev_err(arizona->dev, "Device boot timed out: %x\n", reg); + return -ETIMEDOUT; + } + + pm_runtime_mark_last_busy(arizona->dev); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int arizona_runtime_resume(struct device *dev) +{ + struct arizona *arizona = dev_get_drvdata(dev); + int ret; + + dev_dbg(arizona->dev, "Leaving AoD mode\n"); + + ret = regulator_enable(arizona->dcvdd); + if (ret != 0) { + dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret); + return ret; + } + + regcache_cache_only(arizona->regmap, false); + + ret = arizona_wait_for_boot(arizona); + if (ret != 0) { + regulator_disable(arizona->dcvdd); + return ret; + } + + regcache_sync(arizona->regmap); + + return 0; +} + +static int arizona_runtime_suspend(struct device *dev) +{ + struct arizona *arizona = dev_get_drvdata(dev); + + dev_dbg(arizona->dev, "Entering AoD mode\n"); + + regulator_disable(arizona->dcvdd); + regcache_cache_only(arizona->regmap, true); + regcache_mark_dirty(arizona->regmap); + + return 0; +} +#endif + +const struct dev_pm_ops arizona_pm_ops = { + SET_RUNTIME_PM_OPS(arizona_runtime_suspend, + arizona_runtime_resume, + NULL) +}; +EXPORT_SYMBOL_GPL(arizona_pm_ops); + +static struct mfd_cell early_devs[] = { + { .name = "arizona-ldo1" }, +}; + +static struct mfd_cell wm5102_devs[] = { + { .name = "arizona-extcon" }, + { .name = "arizona-gpio" }, + { .name = "arizona-micsupp" }, + { .name = "arizona-pwm" }, + { .name = "wm5102-codec" }, +}; + +static struct mfd_cell wm5110_devs[] = { + { .name = "arizona-extcon" }, + { .name = "arizona-gpio" }, + { .name = "arizona-micsupp" }, + { .name = "arizona-pwm" }, + { .name = "wm5110-codec" }, +}; + +int __devinit arizona_dev_init(struct arizona *arizona) +{ + struct device *dev = arizona->dev; + const char *type_name; + unsigned int reg, val; + int ret, i; + + dev_set_drvdata(arizona->dev, arizona); + mutex_init(&arizona->clk_lock); + + if (dev_get_platdata(arizona->dev)) + memcpy(&arizona->pdata, dev_get_platdata(arizona->dev), + sizeof(arizona->pdata)); + + regcache_cache_only(arizona->regmap, true); + + switch (arizona->type) { + case WM5102: + case WM5110: + for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++) + arizona->core_supplies[i].supply + = wm5102_core_supplies[i]; + arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies); + break; + default: + dev_err(arizona->dev, "Unknown device type %d\n", + arizona->type); + return -EINVAL; + } + + ret = mfd_add_devices(arizona->dev, -1, early_devs, + ARRAY_SIZE(early_devs), NULL, 0); + if (ret != 0) { + dev_err(dev, "Failed to add early children: %d\n", ret); + return ret; + } + + ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies, + arizona->core_supplies); + if (ret != 0) { + dev_err(dev, "Failed to request core supplies: %d\n", + ret); + goto err_early; + } + + arizona->dcvdd = devm_regulator_get(arizona->dev, "DCVDD"); + if (IS_ERR(arizona->dcvdd)) { + ret = PTR_ERR(arizona->dcvdd); + dev_err(dev, "Failed to request DCVDD: %d\n", ret); + goto err_early; + } + + ret = regulator_bulk_enable(arizona->num_core_supplies, + arizona->core_supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable core supplies: %d\n", + ret); + goto err_early; + } + + ret = regulator_enable(arizona->dcvdd); + if (ret != 0) { + dev_err(dev, "Failed to enable DCVDD: %d\n", ret); + goto err_enable; + } + + if (arizona->pdata.reset) { + /* Start out with /RESET low to put the chip into reset */ + ret = gpio_request_one(arizona->pdata.reset, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, + "arizona /RESET"); + if (ret != 0) { + dev_err(dev, "Failed to request /RESET: %d\n", ret); + goto err_dcvdd; + } + + gpio_set_value_cansleep(arizona->pdata.reset, 1); + } + + regcache_cache_only(arizona->regmap, false); + + ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, ®); + if (ret != 0) { + dev_err(dev, "Failed to read ID register: %d\n", ret); + goto err_reset; + } + + ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION, + &arizona->rev); + if (ret != 0) { + dev_err(dev, "Failed to read revision register: %d\n", ret); + goto err_reset; + } + arizona->rev &= ARIZONA_DEVICE_REVISION_MASK; + + switch (reg) { +#ifdef CONFIG_MFD_WM5102 + case 0x5102: + type_name = "WM5102"; + if (arizona->type != WM5102) { + dev_err(arizona->dev, "WM5102 registered as %d\n", + arizona->type); + arizona->type = WM5102; + } + ret = wm5102_patch(arizona); + break; +#endif +#ifdef CONFIG_MFD_WM5110 + case 0x5110: + type_name = "WM5110"; + if (arizona->type != WM5110) { + dev_err(arizona->dev, "WM5110 registered as %d\n", + arizona->type); + arizona->type = WM5110; + } + ret = wm5110_patch(arizona); + break; +#endif + default: + dev_err(arizona->dev, "Unknown device ID %x\n", reg); + goto err_reset; + } + + dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A'); + + if (ret != 0) + dev_err(arizona->dev, "Failed to apply patch: %d\n", ret); + + /* If we have a /RESET GPIO we'll already be reset */ + if (!arizona->pdata.reset) { + ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0); + if (ret != 0) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err_reset; + } + } + + ret = arizona_wait_for_boot(arizona); + if (ret != 0) { + dev_err(arizona->dev, "Device failed initial boot: %d\n", ret); + goto err_reset; + } + + for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { + if (!arizona->pdata.gpio_defaults[i]) + continue; + + regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i, + arizona->pdata.gpio_defaults[i]); + } + + pm_runtime_set_autosuspend_delay(arizona->dev, 100); + pm_runtime_use_autosuspend(arizona->dev); + pm_runtime_enable(arizona->dev); + + /* Chip default */ + if (!arizona->pdata.clk32k_src) + arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2; + + switch (arizona->pdata.clk32k_src) { + case ARIZONA_32KZ_MCLK1: + case ARIZONA_32KZ_MCLK2: + regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, + ARIZONA_CLK_32K_SRC_MASK, + arizona->pdata.clk32k_src - 1); + break; + case ARIZONA_32KZ_NONE: + regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, + ARIZONA_CLK_32K_SRC_MASK, 2); + break; + default: + dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n", + arizona->pdata.clk32k_src); + ret = -EINVAL; + goto err_reset; + } + + for (i = 0; i < ARIZONA_MAX_INPUT; i++) { + /* Default for both is 0 so noop with defaults */ + val = arizona->pdata.dmic_ref[i] + << ARIZONA_IN1_DMIC_SUP_SHIFT; + val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT; + + regmap_update_bits(arizona->regmap, + ARIZONA_IN1L_CONTROL + (i * 8), + ARIZONA_IN1_DMIC_SUP_MASK | + ARIZONA_IN1_MODE_MASK, val); + } + + for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) { + /* Default is 0 so noop with defaults */ + if (arizona->pdata.out_mono[i]) + val = ARIZONA_OUT1_MONO; + else + val = 0; + + regmap_update_bits(arizona->regmap, + ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8), + ARIZONA_OUT1_MONO, val); + } + + for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) { + if (arizona->pdata.spk_mute[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_PDM_SPK1_CTRL_1 + (i * 2), + ARIZONA_SPK1_MUTE_ENDIAN_MASK | + ARIZONA_SPK1_MUTE_SEQ1_MASK, + arizona->pdata.spk_mute[i]); + + if (arizona->pdata.spk_fmt[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_PDM_SPK1_CTRL_2 + (i * 2), + ARIZONA_SPK1_FMT_MASK, + arizona->pdata.spk_fmt[i]); + } + + /* Set up for interrupts */ + ret = arizona_irq_init(arizona); + if (ret != 0) + goto err_reset; + + arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error", + arizona_clkgen_err, arizona); + arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked", + arizona_overclocked, arizona); + arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked", + arizona_underclocked, arizona); + + switch (arizona->type) { + case WM5102: + ret = mfd_add_devices(arizona->dev, -1, wm5102_devs, + ARRAY_SIZE(wm5102_devs), NULL, 0); + break; + case WM5110: + ret = mfd_add_devices(arizona->dev, -1, wm5110_devs, + ARRAY_SIZE(wm5102_devs), NULL, 0); + break; + } + + if (ret != 0) { + dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret); + goto err_irq; + } + +#ifdef CONFIG_PM_RUNTIME + regulator_disable(arizona->dcvdd); +#endif + + return 0; + +err_irq: + arizona_irq_exit(arizona); +err_reset: + if (arizona->pdata.reset) { + gpio_set_value_cansleep(arizona->pdata.reset, 1); + gpio_free(arizona->pdata.reset); + } +err_dcvdd: + regulator_disable(arizona->dcvdd); +err_enable: + regulator_bulk_disable(arizona->num_core_supplies, + arizona->core_supplies); +err_early: + mfd_remove_devices(dev); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_dev_init); + +int __devexit arizona_dev_exit(struct arizona *arizona) +{ + mfd_remove_devices(arizona->dev); + arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona); + arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona); + arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona); + pm_runtime_disable(arizona->dev); + arizona_irq_exit(arizona); + return 0; +} +EXPORT_SYMBOL_GPL(arizona_dev_exit); diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c new file mode 100644 index 000000000000..570c4b438086 --- /dev/null +++ b/drivers/mfd/arizona-i2c.c @@ -0,0 +1,97 @@ +/* + * Arizona-i2c.c -- Arizona I2C bus interface + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#include <linux/mfd/arizona/core.h> + +#include "arizona.h" + +static __devinit int arizona_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct arizona *arizona; + const struct regmap_config *regmap_config; + int ret; + + switch (id->driver_data) { +#ifdef CONFIG_MFD_WM5102 + case WM5102: + regmap_config = &wm5102_i2c_regmap; + break; +#endif +#ifdef CONFIG_MFD_WM5110 + case WM5110: + regmap_config = &wm5110_i2c_regmap; + break; +#endif + default: + dev_err(&i2c->dev, "Unknown device type %ld\n", + id->driver_data); + return -EINVAL; + } + + arizona = devm_kzalloc(&i2c->dev, sizeof(*arizona), GFP_KERNEL); + if (arizona == NULL) + return -ENOMEM; + + arizona->regmap = devm_regmap_init_i2c(i2c, regmap_config); + if (IS_ERR(arizona->regmap)) { + ret = PTR_ERR(arizona->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + arizona->type = id->driver_data; + arizona->dev = &i2c->dev; + arizona->irq = i2c->irq; + + return arizona_dev_init(arizona); +} + +static int __devexit arizona_i2c_remove(struct i2c_client *i2c) +{ + struct arizona *arizona = dev_get_drvdata(&i2c->dev); + arizona_dev_exit(arizona); + return 0; +} + +static const struct i2c_device_id arizona_i2c_id[] = { + { "wm5102", WM5102 }, + { "wm5110", WM5110 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, arizona_i2c_id); + +static struct i2c_driver arizona_i2c_driver = { + .driver = { + .name = "arizona", + .owner = THIS_MODULE, + .pm = &arizona_pm_ops, + }, + .probe = arizona_i2c_probe, + .remove = __devexit_p(arizona_i2c_remove), + .id_table = arizona_i2c_id, +}; + +module_i2c_driver(arizona_i2c_driver); + +MODULE_DESCRIPTION("Arizona I2C bus interface"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c new file mode 100644 index 000000000000..98ac345f468e --- /dev/null +++ b/drivers/mfd/arizona-irq.c @@ -0,0 +1,275 @@ +/* + * Arizona interrupt support + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/registers.h> + +#include "arizona.h" + +static int arizona_map_irq(struct arizona *arizona, int irq) +{ + int ret; + + ret = regmap_irq_get_virq(arizona->aod_irq_chip, irq); + if (ret < 0) + ret = regmap_irq_get_virq(arizona->irq_chip, irq); + + return ret; +} + +int arizona_request_irq(struct arizona *arizona, int irq, char *name, + irq_handler_t handler, void *data) +{ + irq = arizona_map_irq(arizona, irq); + if (irq < 0) + return irq; + + return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, + name, data); +} +EXPORT_SYMBOL_GPL(arizona_request_irq); + +void arizona_free_irq(struct arizona *arizona, int irq, void *data) +{ + irq = arizona_map_irq(arizona, irq); + if (irq < 0) + return; + + free_irq(irq, data); +} +EXPORT_SYMBOL_GPL(arizona_free_irq); + +int arizona_set_irq_wake(struct arizona *arizona, int irq, int on) +{ + irq = arizona_map_irq(arizona, irq); + if (irq < 0) + return irq; + + return irq_set_irq_wake(irq, on); +} +EXPORT_SYMBOL_GPL(arizona_set_irq_wake); + +static irqreturn_t arizona_boot_done(int irq, void *data) +{ + struct arizona *arizona = data; + + dev_dbg(arizona->dev, "Boot done\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t arizona_ctrlif_err(int irq, void *data) +{ + struct arizona *arizona = data; + + /* + * For pretty much all potential sources a register cache sync + * won't help, we've just got a software bug somewhere. + */ + dev_err(arizona->dev, "Control interface error\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t arizona_irq_thread(int irq, void *data) +{ + struct arizona *arizona = data; + int i, ret; + + ret = pm_runtime_get_sync(arizona->dev); + if (ret < 0) { + dev_err(arizona->dev, "Failed to resume device: %d\n", ret); + return IRQ_NONE; + } + + /* Check both domains */ + for (i = 0; i < 2; i++) + handle_nested_irq(irq_find_mapping(arizona->virq, i)); + + pm_runtime_mark_last_busy(arizona->dev); + pm_runtime_put_autosuspend(arizona->dev); + + return IRQ_HANDLED; +} + +static void arizona_irq_enable(struct irq_data *data) +{ +} + +static void arizona_irq_disable(struct irq_data *data) +{ +} + +static struct irq_chip arizona_irq_chip = { + .name = "arizona", + .irq_disable = arizona_irq_disable, + .irq_enable = arizona_irq_enable, +}; + +static int arizona_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct regmap_irq_chip_data *data = h->host_data; + + irq_set_chip_data(virq, data); + irq_set_chip_and_handler(virq, &arizona_irq_chip, handle_edge_irq); + irq_set_nested_thread(virq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(virq, IRQF_VALID); +#else + irq_set_noprobe(virq); +#endif + + return 0; +} + +static struct irq_domain_ops arizona_domain_ops = { + .map = arizona_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +int arizona_irq_init(struct arizona *arizona) +{ + int flags = IRQF_ONESHOT; + int ret, i; + const struct regmap_irq_chip *aod, *irq; + + switch (arizona->type) { +#ifdef CONFIG_MFD_WM5102 + case WM5102: + aod = &wm5102_aod; + irq = &wm5102_irq; + break; +#endif +#ifdef CONFIG_MFD_WM5110 + case WM5110: + aod = &wm5110_aod; + irq = &wm5110_irq; + break; +#endif + default: + BUG_ON("Unknown Arizona class device" == NULL); + return -EINVAL; + } + + if (arizona->pdata.irq_active_high) { + ret = regmap_update_bits(arizona->regmap, ARIZONA_IRQ_CTRL_1, + ARIZONA_IRQ_POL, 0); + if (ret != 0) { + dev_err(arizona->dev, "Couldn't set IRQ polarity: %d\n", + ret); + goto err; + } + + flags |= IRQF_TRIGGER_HIGH; + } else { + flags |= IRQF_TRIGGER_LOW; + } + + /* Allocate a virtual IRQ domain to distribute to the regmap domains */ + arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops, + arizona); + if (!arizona->virq) { + ret = -EINVAL; + goto err; + } + + ret = regmap_add_irq_chip(arizona->regmap, + irq_create_mapping(arizona->virq, 0), + IRQF_ONESHOT, -1, aod, + &arizona->aod_irq_chip); + if (ret != 0) { + dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret); + goto err_domain; + } + + ret = regmap_add_irq_chip(arizona->regmap, + irq_create_mapping(arizona->virq, 1), + IRQF_ONESHOT, -1, irq, + &arizona->irq_chip); + if (ret != 0) { + dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret); + goto err_aod; + } + + /* Make sure the boot done IRQ is unmasked for resumes */ + i = arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE); + ret = request_threaded_irq(i, NULL, arizona_boot_done, IRQF_ONESHOT, + "Boot done", arizona); + if (ret != 0) { + dev_err(arizona->dev, "Failed to request boot done %d: %d\n", + arizona->irq, ret); + goto err_boot_done; + } + + /* Handle control interface errors in the core */ + i = arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR); + ret = request_threaded_irq(i, NULL, arizona_ctrlif_err, IRQF_ONESHOT, + "Control interface error", arizona); + if (ret != 0) { + dev_err(arizona->dev, "Failed to request boot done %d: %d\n", + arizona->irq, ret); + goto err_ctrlif; + } + + ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread, + flags, "arizona", arizona); + + if (ret != 0) { + dev_err(arizona->dev, "Failed to request IRQ %d: %d\n", + arizona->irq, ret); + goto err_main_irq; + } + + return 0; + +err_main_irq: + free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), arizona); +err_ctrlif: + free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona); +err_boot_done: + regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1), + arizona->irq_chip); +err_aod: + regmap_del_irq_chip(irq_create_mapping(arizona->virq, 0), + arizona->aod_irq_chip); +err_domain: +err: + return ret; +} + +int arizona_irq_exit(struct arizona *arizona) +{ + free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), arizona); + free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona); + regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1), + arizona->irq_chip); + regmap_del_irq_chip(irq_create_mapping(arizona->virq, 0), + arizona->aod_irq_chip); + free_irq(arizona->irq, arizona); + + return 0; +} diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c new file mode 100644 index 000000000000..df2e5a8bee28 --- /dev/null +++ b/drivers/mfd/arizona-spi.c @@ -0,0 +1,97 @@ +/* + * arizona-spi.c -- Arizona SPI bus interface + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/err.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> + +#include <linux/mfd/arizona/core.h> + +#include "arizona.h" + +static int __devinit arizona_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct arizona *arizona; + const struct regmap_config *regmap_config; + int ret; + + switch (id->driver_data) { +#ifdef CONFIG_MFD_WM5102 + case WM5102: + regmap_config = &wm5102_spi_regmap; + break; +#endif +#ifdef CONFIG_MFD_WM5110 + case WM5110: + regmap_config = &wm5110_spi_regmap; + break; +#endif + default: + dev_err(&spi->dev, "Unknown device type %ld\n", + id->driver_data); + return -EINVAL; + } + + arizona = devm_kzalloc(&spi->dev, sizeof(*arizona), GFP_KERNEL); + if (arizona == NULL) + return -ENOMEM; + + arizona->regmap = devm_regmap_init_spi(spi, regmap_config); + if (IS_ERR(arizona->regmap)) { + ret = PTR_ERR(arizona->regmap); + dev_err(&spi->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + arizona->type = id->driver_data; + arizona->dev = &spi->dev; + arizona->irq = spi->irq; + + return arizona_dev_init(arizona); +} + +static int __devexit arizona_spi_remove(struct spi_device *spi) +{ + struct arizona *arizona = dev_get_drvdata(&spi->dev); + arizona_dev_exit(arizona); + return 0; +} + +static const struct spi_device_id arizona_spi_ids[] = { + { "wm5102", WM5102 }, + { "wm5110", WM5110 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, arizona_spi_ids); + +static struct spi_driver arizona_spi_driver = { + .driver = { + .name = "arizona", + .owner = THIS_MODULE, + .pm = &arizona_pm_ops, + }, + .probe = arizona_spi_probe, + .remove = __devexit_p(arizona_spi_remove), + .id_table = arizona_spi_ids, +}; + +module_spi_driver(arizona_spi_driver); + +MODULE_DESCRIPTION("Arizona SPI bus interface"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h new file mode 100644 index 000000000000..9798ae5da67b --- /dev/null +++ b/drivers/mfd/arizona.h @@ -0,0 +1,40 @@ +/* + * wm5102.h -- WM5102 MFD internals + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * 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. + */ + +#ifndef _WM5102_H +#define _WM5102_H + +#include <linux/regmap.h> +#include <linux/pm.h> + +struct wm_arizona; + +extern const struct regmap_config wm5102_i2c_regmap; +extern const struct regmap_config wm5102_spi_regmap; + +extern const struct regmap_config wm5110_i2c_regmap; +extern const struct regmap_config wm5110_spi_regmap; + +extern const struct dev_pm_ops arizona_pm_ops; + +extern const struct regmap_irq_chip wm5102_aod; +extern const struct regmap_irq_chip wm5102_irq; + +extern const struct regmap_irq_chip wm5110_aod; +extern const struct regmap_irq_chip wm5110_irq; + +int arizona_dev_init(struct arizona *arizona); +int arizona_dev_exit(struct arizona *arizona); +int arizona_irq_init(struct arizona *arizona); +int arizona_irq_exit(struct arizona *arizona); + +#endif diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index 1f1313c90573..2544910e1fd6 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -772,7 +772,6 @@ EXPORT_SYMBOL_GPL(da9052_regmap_config); int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) { struct da9052_pdata *pdata = da9052->dev->platform_data; - struct irq_desc *desc; int ret; mutex_init(&da9052->auxadc_lock); diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 50e83dc5dc49..7040a0081130 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -28,6 +28,7 @@ #include <linux/uaccess.h> #include <linux/mfd/core.h> #include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/abx500/ab8500.h> #include <linux/regulator/db8500-prcmu.h> #include <linux/regulator/machine.h> #include <asm/hardware/gic.h> @@ -2269,10 +2270,10 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) /** * prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem */ -void prcmu_ac_wake_req(void) +int prcmu_ac_wake_req(void) { u32 val; - u32 status; + int ret = 0; mutex_lock(&mb0_transfer.ac_wake_lock); @@ -2282,39 +2283,32 @@ void prcmu_ac_wake_req(void) atomic_set(&ac_wake_req_state, 1); -retry: - writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), PRCM_HOSTACCESS_REQ); + /* + * Force Modem Wake-up before hostaccess_req ping-pong. + * It prevents Modem to enter in Sleep while acking the hostaccess + * request. The 31us delay has been calculated by HWI. + */ + val |= PRCM_HOSTACCESS_REQ_WAKE_REQ; + writel(val, PRCM_HOSTACCESS_REQ); + + udelay(31); + + val |= PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ; + writel(val, PRCM_HOSTACCESS_REQ); if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, msecs_to_jiffies(5000))) { +#if defined(CONFIG_DBX500_PRCMU_DEBUG) + db8500_prcmu_debug_dump(__func__, true, true); +#endif pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n", __func__); - goto unlock_and_return; - } - - /* - * The modem can generate an AC_WAKE_ACK, and then still go to sleep. - * As a workaround, we wait, and then check that the modem is indeed - * awake (in terms of the value of the PRCM_MOD_AWAKE_STATUS - * register, which may not be the whole truth). - */ - udelay(400); - status = (readl(PRCM_MOD_AWAKE_STATUS) & BITS(0, 2)); - if (status != (PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE | - PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)) { - pr_err("prcmu: %s received ack, but modem not awake (0x%X).\n", - __func__, status); - udelay(1200); - writel(val, PRCM_HOSTACCESS_REQ); - if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work, - msecs_to_jiffies(5000))) - goto retry; - pr_crit("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n", - __func__); + ret = -EFAULT; } unlock_and_return: mutex_unlock(&mb0_transfer.ac_wake_lock); + return ret; } /** @@ -2945,14 +2939,31 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { }, }; +static struct resource ab8500_resources[] = { + [0] = { + .start = IRQ_DB8500_AB8500, + .end = IRQ_DB8500_AB8500, + .flags = IORESOURCE_IRQ + } +}; + static struct mfd_cell db8500_prcmu_devs[] = { { .name = "db8500-prcmu-regulators", + .of_compatible = "stericsson,db8500-prcmu-regulator", .platform_data = &db8500_regulators, .pdata_size = sizeof(db8500_regulators), }, { .name = "cpufreq-u8500", + .of_compatible = "stericsson,cpufreq-u8500", + }, + { + .name = "ab8500-core", + .of_compatible = "stericsson,ab8500", + .num_resources = ARRAY_SIZE(ab8500_resources), + .resources = ab8500_resources, + .id = AB8500_VERSION_AB8500, }, }; @@ -2962,8 +2973,9 @@ static struct mfd_cell db8500_prcmu_devs[] = { */ static int __devinit db8500_prcmu_probe(struct platform_device *pdev) { + struct ab8500_platform_data *ab8500_platdata = pdev->dev.platform_data; struct device_node *np = pdev->dev.of_node; - int irq = 0, err = 0; + int irq = 0, err = 0, i; if (ux500_is_svp()) return -ENODEV; @@ -2987,16 +2999,21 @@ static int __devinit db8500_prcmu_probe(struct platform_device *pdev) goto no_irq_return; } + for (i = 0; i < ARRAY_SIZE(db8500_prcmu_devs); i++) { + if (!strcmp(db8500_prcmu_devs[i].name, "ab8500-core")) { + db8500_prcmu_devs[i].platform_data = ab8500_platdata; + db8500_prcmu_devs[i].pdata_size = sizeof(struct ab8500_platform_data); + } + } + if (cpu_is_u8500v20_or_later()) prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET); - if (!np) { - err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs, - ARRAY_SIZE(db8500_prcmu_devs), NULL, 0); - if (err) { - pr_err("prcmu: Failed to add subdevices\n"); - return err; - } + err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs, + ARRAY_SIZE(db8500_prcmu_devs), NULL, 0); + if (err) { + pr_err("prcmu: Failed to add subdevices\n"); + return err; } pr_info("DB8500 PRCMU initialized\n"); @@ -3004,11 +3021,16 @@ static int __devinit db8500_prcmu_probe(struct platform_device *pdev) no_irq_return: return err; } +static const struct of_device_id db8500_prcmu_match[] = { + { .compatible = "stericsson,db8500-prcmu"}, + { }, +}; static struct platform_driver db8500_prcmu_driver = { .driver = { .name = "db8500-prcmu", .owner = THIS_MODULE, + .of_match_table = db8500_prcmu_match, }, .probe = db8500_prcmu_probe, }; @@ -3018,7 +3040,7 @@ static int __init db8500_prcmu_init(void) return platform_driver_register(&db8500_prcmu_driver); } -arch_initcall(db8500_prcmu_init); +core_initcall(db8500_prcmu_init); MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com>"); MODULE_DESCRIPTION("DB8500 PRCM Unit driver"); diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h index 3a0bf91d7780..23108a6e3167 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -106,6 +106,7 @@ #define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334) #define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1 +#define PRCM_HOSTACCESS_REQ_WAKE_REQ BIT(16) #define ARM_WAKEUP_MODEM 0x1 #define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C) diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c new file mode 100644 index 000000000000..cdc3280e2ec7 --- /dev/null +++ b/drivers/mfd/max77686-irq.c @@ -0,0 +1,319 @@ +/* + * max77686-irq.c - Interrupt controller support for MAX77686 + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Chiwoong Byun <woong.byun@samsung.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8997-irq.c + */ + +#include <linux/err.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/mfd/max77686.h> +#include <linux/mfd/max77686-private.h> +#include <linux/irqdomain.h> +#include <linux/regmap.h> + +enum { + MAX77686_DEBUG_IRQ_INFO = 1 << 0, + MAX77686_DEBUG_IRQ_MASK = 1 << 1, + MAX77686_DEBUG_IRQ_INT = 1 << 2, +}; + +static int debug_mask = 0; +module_param(debug_mask, int, 0); +MODULE_PARM_DESC(debug_mask, "Set debug_mask : 0x0=off 0x1=IRQ_INFO 0x2=IRQ_MASK 0x4=IRQ_INI)"); + +static const u8 max77686_mask_reg[] = { + [PMIC_INT1] = MAX77686_REG_INT1MSK, + [PMIC_INT2] = MAX77686_REG_INT2MSK, + [RTC_INT] = MAX77686_RTC_INTM, +}; + +static struct regmap *max77686_get_regmap(struct max77686_dev *max77686, + enum max77686_irq_source src) +{ + switch (src) { + case PMIC_INT1 ... PMIC_INT2: + return max77686->regmap; + case RTC_INT: + return max77686->rtc_regmap; + default: + return ERR_PTR(-EINVAL); + } +} + +struct max77686_irq_data { + int mask; + enum max77686_irq_source group; +}; + +#define DECLARE_IRQ(idx, _group, _mask) \ + [(idx)] = { .group = (_group), .mask = (_mask) } +static const struct max77686_irq_data max77686_irqs[] = { + DECLARE_IRQ(MAX77686_PMICIRQ_PWRONF, PMIC_INT1, 1 << 0), + DECLARE_IRQ(MAX77686_PMICIRQ_PWRONR, PMIC_INT1, 1 << 1), + DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBF, PMIC_INT1, 1 << 2), + DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBR, PMIC_INT1, 1 << 3), + DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBF, PMIC_INT1, 1 << 4), + DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBR, PMIC_INT1, 1 << 5), + DECLARE_IRQ(MAX77686_PMICIRQ_ONKEY1S, PMIC_INT1, 1 << 6), + DECLARE_IRQ(MAX77686_PMICIRQ_MRSTB, PMIC_INT1, 1 << 7), + DECLARE_IRQ(MAX77686_PMICIRQ_140C, PMIC_INT2, 1 << 0), + DECLARE_IRQ(MAX77686_PMICIRQ_120C, PMIC_INT2, 1 << 1), + DECLARE_IRQ(MAX77686_RTCIRQ_RTC60S, RTC_INT, 1 << 0), + DECLARE_IRQ(MAX77686_RTCIRQ_RTCA1, RTC_INT, 1 << 1), + DECLARE_IRQ(MAX77686_RTCIRQ_RTCA2, RTC_INT, 1 << 2), + DECLARE_IRQ(MAX77686_RTCIRQ_SMPL, RTC_INT, 1 << 3), + DECLARE_IRQ(MAX77686_RTCIRQ_RTC1S, RTC_INT, 1 << 4), + DECLARE_IRQ(MAX77686_RTCIRQ_WTSR, RTC_INT, 1 << 5), +}; + +static void max77686_irq_lock(struct irq_data *data) +{ + struct max77686_dev *max77686 = irq_get_chip_data(data->irq); + + if (debug_mask & MAX77686_DEBUG_IRQ_MASK) + pr_info("%s\n", __func__); + + mutex_lock(&max77686->irqlock); +} + +static void max77686_irq_sync_unlock(struct irq_data *data) +{ + struct max77686_dev *max77686 = irq_get_chip_data(data->irq); + int i; + + for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) { + u8 mask_reg = max77686_mask_reg[i]; + struct regmap *map = max77686_get_regmap(max77686, i); + + if (debug_mask & MAX77686_DEBUG_IRQ_MASK) + pr_debug("%s: mask_reg[%d]=0x%x, cur=0x%x\n", + __func__, i, mask_reg, max77686->irq_masks_cur[i]); + + if (mask_reg == MAX77686_REG_INVALID || + IS_ERR_OR_NULL(map)) + continue; + + max77686->irq_masks_cache[i] = max77686->irq_masks_cur[i]; + + regmap_write(map, max77686_mask_reg[i], + max77686->irq_masks_cur[i]); + } + + mutex_unlock(&max77686->irqlock); +} + +static const inline struct max77686_irq_data *to_max77686_irq(int irq) +{ + struct irq_data *data = irq_get_irq_data(irq); + return &max77686_irqs[data->hwirq]; +} + +static void max77686_irq_mask(struct irq_data *data) +{ + struct max77686_dev *max77686 = irq_get_chip_data(data->irq); + const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq); + + max77686->irq_masks_cur[irq_data->group] |= irq_data->mask; + + if (debug_mask & MAX77686_DEBUG_IRQ_MASK) + pr_info("%s: group=%d, cur=0x%x\n", + __func__, irq_data->group, + max77686->irq_masks_cur[irq_data->group]); +} + +static void max77686_irq_unmask(struct irq_data *data) +{ + struct max77686_dev *max77686 = irq_get_chip_data(data->irq); + const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq); + + max77686->irq_masks_cur[irq_data->group] &= ~irq_data->mask; + + if (debug_mask & MAX77686_DEBUG_IRQ_MASK) + pr_info("%s: group=%d, cur=0x%x\n", + __func__, irq_data->group, + max77686->irq_masks_cur[irq_data->group]); +} + +static struct irq_chip max77686_irq_chip = { + .name = "max77686", + .irq_bus_lock = max77686_irq_lock, + .irq_bus_sync_unlock = max77686_irq_sync_unlock, + .irq_mask = max77686_irq_mask, + .irq_unmask = max77686_irq_unmask, +}; + +static irqreturn_t max77686_irq_thread(int irq, void *data) +{ + struct max77686_dev *max77686 = data; + unsigned int irq_reg[MAX77686_IRQ_GROUP_NR] = {}; + unsigned int irq_src; + int ret; + int i, cur_irq; + + ret = regmap_read(max77686->regmap, MAX77686_REG_INTSRC, &irq_src); + if (ret < 0) { + dev_err(max77686->dev, "Failed to read interrupt source: %d\n", + ret); + return IRQ_NONE; + } + + if (debug_mask & MAX77686_DEBUG_IRQ_INT) + pr_info("%s: irq_src=0x%x\n", __func__, irq_src); + + if (irq_src == MAX77686_IRQSRC_PMIC) { + ret = regmap_bulk_read(max77686->regmap, + MAX77686_REG_INT1, irq_reg, 2); + if (ret < 0) { + dev_err(max77686->dev, "Failed to read interrupt source: %d\n", + ret); + return IRQ_NONE; + } + + if (debug_mask & MAX77686_DEBUG_IRQ_INT) + pr_info("%s: int1=0x%x, int2=0x%x\n", __func__, + irq_reg[PMIC_INT1], irq_reg[PMIC_INT2]); + } + + if (irq_src & MAX77686_IRQSRC_RTC) { + ret = regmap_read(max77686->rtc_regmap, + MAX77686_RTC_INT, &irq_reg[RTC_INT]); + if (ret < 0) { + dev_err(max77686->dev, "Failed to read interrupt source: %d\n", + ret); + return IRQ_NONE; + } + + if (debug_mask & MAX77686_DEBUG_IRQ_INT) + pr_info("%s: rtc int=0x%x\n", __func__, + irq_reg[RTC_INT]); + + } + + for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) + irq_reg[i] &= ~max77686->irq_masks_cur[i]; + + for (i = 0; i < MAX77686_IRQ_NR; i++) { + if (irq_reg[max77686_irqs[i].group] & max77686_irqs[i].mask) { + cur_irq = irq_find_mapping(max77686->irq_domain, i); + if (cur_irq) + handle_nested_irq(cur_irq); + } + } + + return IRQ_HANDLED; +} + +static int max77686_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct max77686_dev *max77686 = d->host_data; + + irq_set_chip_data(irq, max77686); + irq_set_chip_and_handler(irq, &max77686_irq_chip, handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + return 0; +} + +static struct irq_domain_ops max77686_irq_domain_ops = { + .map = max77686_irq_domain_map, +}; + +int max77686_irq_init(struct max77686_dev *max77686) +{ + struct irq_domain *domain; + int i; + int ret; + int val; + struct regmap *map; + + mutex_init(&max77686->irqlock); + + if (max77686->irq_gpio && !max77686->irq) { + max77686->irq = gpio_to_irq(max77686->irq_gpio); + + if (debug_mask & MAX77686_DEBUG_IRQ_INT) { + ret = gpio_request(max77686->irq_gpio, "pmic_irq"); + if (ret < 0) { + dev_err(max77686->dev, + "Failed to request gpio %d with ret:" + "%d\n", max77686->irq_gpio, ret); + return IRQ_NONE; + } + + gpio_direction_input(max77686->irq_gpio); + val = gpio_get_value(max77686->irq_gpio); + gpio_free(max77686->irq_gpio); + pr_info("%s: gpio_irq=%x\n", __func__, val); + } + } + + if (!max77686->irq) { + dev_err(max77686->dev, "irq is not specified\n"); + return -ENODEV; + } + + /* Mask individual interrupt sources */ + for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) { + max77686->irq_masks_cur[i] = 0xff; + max77686->irq_masks_cache[i] = 0xff; + map = max77686_get_regmap(max77686, i); + + if (IS_ERR_OR_NULL(map)) + continue; + if (max77686_mask_reg[i] == MAX77686_REG_INVALID) + continue; + + regmap_write(map, max77686_mask_reg[i], 0xff); + } + domain = irq_domain_add_linear(NULL, MAX77686_IRQ_NR, + &max77686_irq_domain_ops, max77686); + if (!domain) { + dev_err(max77686->dev, "could not create irq domain\n"); + return -ENODEV; + } + max77686->irq_domain = domain; + + ret = request_threaded_irq(max77686->irq, NULL, max77686_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "max77686-irq", max77686); + + if (ret) + dev_err(max77686->dev, "Failed to request IRQ %d: %d\n", + max77686->irq, ret); + + + if (debug_mask & MAX77686_DEBUG_IRQ_INFO) + pr_info("%s-\n", __func__); + + return 0; +} + +void max77686_irq_exit(struct max77686_dev *max77686) +{ + if (max77686->irq) + free_irq(max77686->irq, max77686); +} diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c new file mode 100644 index 000000000000..c03e12b51924 --- /dev/null +++ b/drivers/mfd/max77686.c @@ -0,0 +1,187 @@ +/* + * max77686.c - mfd core driver for the Maxim 77686 + * + * Copyright (C) 2012 Samsung Electronics + * Chiwoong Byun <woong.byun@smasung.com> + * Jonghwa Lee <jonghwa3.lee@samsung.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8997.c + */ + +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/pm_runtime.h> +#include <linux/module.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max77686.h> +#include <linux/mfd/max77686-private.h> +#include <linux/err.h> + +#define I2C_ADDR_RTC (0x0C >> 1) + +static struct mfd_cell max77686_devs[] = { + { .name = "max77686-pmic", }, + { .name = "max77686-rtc", }, +}; + +static struct regmap_config max77686_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +#ifdef CONFIG_OF +static struct of_device_id __devinitdata max77686_pmic_dt_match[] = { + {.compatible = "maxim,max77686", .data = 0}, + {}, +}; + +static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device + *dev) +{ + struct max77686_platform_data *pd; + + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) { + dev_err(dev, "could not allocate memory for pdata\n"); + return NULL; + } + + dev->platform_data = pd; + return pd; +} +#else +static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device + *dev) +{ + return 0; +} +#endif + +static int max77686_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max77686_dev *max77686 = NULL; + struct max77686_platform_data *pdata = i2c->dev.platform_data; + unsigned int data; + int ret = 0; + + if (i2c->dev.of_node) + pdata = max77686_i2c_parse_dt_pdata(&i2c->dev); + + if (!pdata) { + ret = -EIO; + dev_err(&i2c->dev, "No platform data found.\n"); + goto err; + } + + max77686 = kzalloc(sizeof(struct max77686_dev), GFP_KERNEL); + if (max77686 == NULL) + return -ENOMEM; + + max77686->regmap = regmap_init_i2c(i2c, &max77686_regmap_config); + if (IS_ERR(max77686->regmap)) { + ret = PTR_ERR(max77686->regmap); + dev_err(max77686->dev, "Failed to allocate register map: %d\n", + ret); + kfree(max77686); + return ret; + } + + i2c_set_clientdata(i2c, max77686); + max77686->dev = &i2c->dev; + max77686->i2c = i2c; + max77686->type = id->driver_data; + + max77686->wakeup = pdata->wakeup; + max77686->irq_gpio = pdata->irq_gpio; + max77686->irq = i2c->irq; + + if (regmap_read(max77686->regmap, + MAX77686_REG_DEVICE_ID, &data) < 0) { + dev_err(max77686->dev, + "device not found on this channel (this is not an error)\n"); + ret = -ENODEV; + goto err; + } else + dev_info(max77686->dev, "device found\n"); + + max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); + i2c_set_clientdata(max77686->rtc, max77686); + + max77686_irq_init(max77686); + + ret = mfd_add_devices(max77686->dev, -1, max77686_devs, + ARRAY_SIZE(max77686_devs), NULL, 0); + + if (ret < 0) + goto err_mfd; + + return ret; + +err_mfd: + mfd_remove_devices(max77686->dev); + i2c_unregister_device(max77686->rtc); +err: + kfree(max77686); + return ret; +} + +static int max77686_i2c_remove(struct i2c_client *i2c) +{ + struct max77686_dev *max77686 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max77686->dev); + i2c_unregister_device(max77686->rtc); + kfree(max77686); + + return 0; +} + +static const struct i2c_device_id max77686_i2c_id[] = { + { "max77686", TYPE_MAX77686 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max77686_i2c_id); + +static struct i2c_driver max77686_i2c_driver = { + .driver = { + .name = "max77686", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max77686_pmic_dt_match), + }, + .probe = max77686_i2c_probe, + .remove = max77686_i2c_remove, + .id_table = max77686_i2c_id, +}; + +static int __init max77686_i2c_init(void) +{ + return i2c_add_driver(&max77686_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(max77686_i2c_init); + +static void __exit max77686_i2c_exit(void) +{ + i2c_del_driver(&max77686_i2c_driver); +} +module_exit(max77686_i2c_exit); + +MODULE_DESCRIPTION("MAXIM 77686 multi-function core driver"); +MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c index e9e4278722f3..a1811cb50ec7 100644 --- a/drivers/mfd/max77693.c +++ b/drivers/mfd/max77693.c @@ -138,8 +138,6 @@ static int max77693_i2c_probe(struct i2c_client *i2c, max77693->wakeup = pdata->wakeup; - mutex_init(&max77693->iolock); - if (max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2, ®_data) < 0) { dev_err(max77693->dev, "device not found on this channel\n"); @@ -156,7 +154,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, ret = max77693_irq_init(max77693); if (ret < 0) - goto err_mfd; + goto err_irq; pm_runtime_set_active(max77693->dev); @@ -170,11 +168,11 @@ static int max77693_i2c_probe(struct i2c_client *i2c, return ret; err_mfd: + max77693_irq_exit(max77693); +err_irq: i2c_unregister_device(max77693->muic); i2c_unregister_device(max77693->haptic); err_regmap: - kfree(max77693); - return ret; } @@ -183,6 +181,7 @@ static int max77693_i2c_remove(struct i2c_client *i2c) struct max77693_dev *max77693 = i2c_get_clientdata(i2c); mfd_remove_devices(max77693->dev); + max77693_irq_exit(max77693); i2c_unregister_device(max77693->muic); i2c_unregister_device(max77693->haptic); @@ -215,7 +214,7 @@ static int max77693_resume(struct device *dev) return max77693_irq_resume(max77693); } -const struct dev_pm_ops max77693_pm = { +static const struct dev_pm_ops max77693_pm = { .suspend = max77693_suspend, .resume = max77693_resume, }; diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index ca881efedf75..825a7f06d9ba 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -75,9 +75,9 @@ static struct mfd_cell power_devs[] = { static struct resource rtc_resources[] = { { .name = "max8925-rtc", - .start = MAX8925_RTC_IRQ, - .end = MAX8925_RTC_IRQ_MASK, - .flags = IORESOURCE_IO, + .start = MAX8925_IRQ_RTC_ALARM0, + .end = MAX8925_IRQ_RTC_ALARM0, + .flags = IORESOURCE_IRQ, }, }; @@ -598,7 +598,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip, ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0], ARRAY_SIZE(rtc_devs), - &rtc_resources[0], 0); + &rtc_resources[0], chip->irq_base); if (ret < 0) { dev_err(chip->dev, "Failed to add rtc subdev\n"); goto out; diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c index 09274cf7c33b..43fa61413e93 100644 --- a/drivers/mfd/max8997-irq.c +++ b/drivers/mfd/max8997-irq.c @@ -142,7 +142,8 @@ static void max8997_irq_sync_unlock(struct irq_data *data) static const inline struct max8997_irq_data * irq_to_max8997_irq(struct max8997_dev *max8997, int irq) { - return &max8997_irqs[irq - max8997->irq_base]; + struct irq_data *data = irq_get_irq_data(irq); + return &max8997_irqs[data->hwirq]; } static void max8997_irq_mask(struct irq_data *data) @@ -182,7 +183,7 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {}; u8 irq_src; int ret; - int i; + int i, cur_irq; ret = max8997_read_reg(max8997->i2c, MAX8997_REG_INTSRC, &irq_src); if (ret < 0) { @@ -269,8 +270,11 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) /* Report */ for (i = 0; i < MAX8997_IRQ_NR; i++) { - if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) - handle_nested_irq(max8997->irq_base + i); + if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) { + cur_irq = irq_find_mapping(max8997->irq_domain, i); + if (cur_irq) + handle_nested_irq(cur_irq); + } } return IRQ_HANDLED; @@ -278,26 +282,40 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) int max8997_irq_resume(struct max8997_dev *max8997) { - if (max8997->irq && max8997->irq_base) - max8997_irq_thread(max8997->irq_base, max8997); + if (max8997->irq && max8997->irq_domain) + max8997_irq_thread(0, max8997); + return 0; +} + +static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct max8997_dev *max8997 = d->host_data; + + irq_set_chip_data(irq, max8997); + irq_set_chip_and_handler(irq, &max8997_irq_chip, handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif return 0; } +static struct irq_domain_ops max8997_irq_domain_ops = { + .map = max8997_irq_domain_map, +}; + int max8997_irq_init(struct max8997_dev *max8997) { + struct irq_domain *domain; int i; - int cur_irq; int ret; u8 val; if (!max8997->irq) { dev_warn(max8997->dev, "No interrupt specified.\n"); - max8997->irq_base = 0; - return 0; - } - - if (!max8997->irq_base) { - dev_err(max8997->dev, "No interrupt base specified.\n"); return 0; } @@ -327,19 +345,13 @@ int max8997_irq_init(struct max8997_dev *max8997) true : false; } - /* Register with genirq */ - for (i = 0; i < MAX8997_IRQ_NR; i++) { - cur_irq = i + max8997->irq_base; - irq_set_chip_data(cur_irq, max8997); - irq_set_chip_and_handler(cur_irq, &max8997_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(cur_irq, IRQF_VALID); -#else - irq_set_noprobe(cur_irq); -#endif + domain = irq_domain_add_linear(NULL, MAX8997_IRQ_NR, + &max8997_irq_domain_ops, max8997); + if (!domain) { + dev_err(max8997->dev, "could not create irq domain\n"); + return -ENODEV; } + max8997->irq_domain = domain; ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index cb83a7ab53e7..10b629c245b6 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -143,7 +143,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c, if (!pdata) goto err; - max8997->irq_base = pdata->irq_base; max8997->ono = pdata->ono; mutex_init(&max8997->iolock); @@ -206,7 +205,7 @@ static const struct i2c_device_id max8997_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, max8998_i2c_id); -u8 max8997_dumpaddr_pmic[] = { +static u8 max8997_dumpaddr_pmic[] = { MAX8997_REG_INT1MSK, MAX8997_REG_INT2MSK, MAX8997_REG_INT3MSK, @@ -331,7 +330,7 @@ u8 max8997_dumpaddr_pmic[] = { MAX8997_REG_DVSOKTIMER5, }; -u8 max8997_dumpaddr_muic[] = { +static u8 max8997_dumpaddr_muic[] = { MAX8997_MUIC_REG_INTMASK1, MAX8997_MUIC_REG_INTMASK2, MAX8997_MUIC_REG_INTMASK3, @@ -341,7 +340,7 @@ u8 max8997_dumpaddr_muic[] = { MAX8997_MUIC_REG_CONTROL3, }; -u8 max8997_dumpaddr_haptic[] = { +static u8 max8997_dumpaddr_haptic[] = { MAX8997_HAPTIC_REG_CONF1, MAX8997_HAPTIC_REG_CONF2, MAX8997_HAPTIC_REG_DRVCONF, @@ -423,7 +422,7 @@ static int max8997_resume(struct device *dev) return max8997_irq_resume(max8997); } -const struct dev_pm_ops max8997_pm = { +static const struct dev_pm_ops max8997_pm = { .suspend = max8997_suspend, .resume = max8997_resume, .freeze = max8997_freeze, diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index f0ea3b8b3e4a..b801dc72f041 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -723,10 +723,6 @@ void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx) free_irq(mc13xxx->irq, mc13xxx); mfd_remove_devices(mc13xxx->dev); - - regmap_exit(mc13xxx->regmap); - - kfree(mc13xxx); } EXPORT_SYMBOL_GPL(mc13xxx_common_cleanup); diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c index d22501dad6a6..9d18dde3cd2a 100644 --- a/drivers/mfd/mc13xxx-i2c.c +++ b/drivers/mfd/mc13xxx-i2c.c @@ -53,17 +53,11 @@ static struct regmap_config mc13xxx_regmap_i2c_config = { static int mc13xxx_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct of_device_id *of_id; - struct i2c_driver *idrv = to_i2c_driver(client->dev.driver); struct mc13xxx *mc13xxx; struct mc13xxx_platform_data *pdata = dev_get_platdata(&client->dev); int ret; - of_id = of_match_device(mc13xxx_dt_ids, &client->dev); - if (of_id) - idrv->id_table = (const struct i2c_device_id*) of_id->data; - - mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL); + mc13xxx = devm_kzalloc(&client->dev, sizeof(*mc13xxx), GFP_KERNEL); if (!mc13xxx) return -ENOMEM; @@ -72,13 +66,13 @@ static int mc13xxx_i2c_probe(struct i2c_client *client, mc13xxx->dev = &client->dev; mutex_init(&mc13xxx->lock); - mc13xxx->regmap = regmap_init_i2c(client, &mc13xxx_regmap_i2c_config); + mc13xxx->regmap = devm_regmap_init_i2c(client, + &mc13xxx_regmap_i2c_config); if (IS_ERR(mc13xxx->regmap)) { ret = PTR_ERR(mc13xxx->regmap); dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n", ret); dev_set_drvdata(&client->dev, NULL); - kfree(mc13xxx); return ret; } diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c index 03df422feb76..0bdb43a0aff0 100644 --- a/drivers/mfd/mc13xxx-spi.c +++ b/drivers/mfd/mc13xxx-spi.c @@ -119,17 +119,11 @@ static struct regmap_bus regmap_mc13xxx_bus = { static int mc13xxx_spi_probe(struct spi_device *spi) { - const struct of_device_id *of_id; - struct spi_driver *sdrv = to_spi_driver(spi->dev.driver); struct mc13xxx *mc13xxx; struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev); int ret; - of_id = of_match_device(mc13xxx_dt_ids, &spi->dev); - if (of_id) - sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data]; - - mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL); + mc13xxx = devm_kzalloc(&spi->dev, sizeof(*mc13xxx), GFP_KERNEL); if (!mc13xxx) return -ENOMEM; @@ -139,15 +133,14 @@ static int mc13xxx_spi_probe(struct spi_device *spi) mc13xxx->dev = &spi->dev; mutex_init(&mc13xxx->lock); - mc13xxx->regmap = regmap_init(&spi->dev, ®map_mc13xxx_bus, &spi->dev, - &mc13xxx_regmap_spi_config); - + mc13xxx->regmap = devm_regmap_init(&spi->dev, ®map_mc13xxx_bus, + &spi->dev, + &mc13xxx_regmap_spi_config); if (IS_ERR(mc13xxx->regmap)) { ret = PTR_ERR(mc13xxx->regmap); dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n", ret); dev_set_drvdata(&spi->dev, NULL); - kfree(mc13xxx); return ret; } diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index ffc3d48676ae..0c3a01cde2f7 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -18,6 +18,8 @@ #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/irqdomain.h> +#include <linux/of.h> int mfd_cell_enable(struct platform_device *pdev) { @@ -76,6 +78,8 @@ static int mfd_add_device(struct device *parent, int id, { struct resource *res; struct platform_device *pdev; + struct device_node *np = NULL; + struct irq_domain *domain = NULL; int ret = -ENOMEM; int r; @@ -89,6 +93,16 @@ static int mfd_add_device(struct device *parent, int id, pdev->dev.parent = parent; + if (parent->of_node && cell->of_compatible) { + for_each_child_of_node(parent->of_node, np) { + if (of_device_is_compatible(np, cell->of_compatible)) { + pdev->dev.of_node = np; + domain = irq_find_host(parent->of_node); + break; + } + } + } + if (cell->pdata_size) { ret = platform_device_add_data(pdev, cell->platform_data, cell->pdata_size); @@ -112,10 +126,18 @@ static int mfd_add_device(struct device *parent, int id, res[r].end = mem_base->start + cell->resources[r].end; } else if (cell->resources[r].flags & IORESOURCE_IRQ) { - res[r].start = irq_base + - cell->resources[r].start; - res[r].end = irq_base + - cell->resources[r].end; + if (domain) { + /* Unable to create mappings for IRQ ranges. */ + WARN_ON(cell->resources[r].start != + cell->resources[r].end); + res[r].start = res[r].end = irq_create_mapping( + domain, cell->resources[r].start); + } else { + res[r].start = irq_base + + cell->resources[r].start; + res[r].end = irq_base + + cell->resources[r].end; + } } else { res[r].parent = cell->resources[r].parent; res[r].start = cell->resources[r].start; diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index 29c122bf28ea..45ce1fb5a549 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -253,8 +253,13 @@ static int __devinit pcf50633_probe(struct i2c_client *client, } pdev->dev.parent = pcf->dev; - platform_device_add_data(pdev, &pdata->reg_init_data[i], - sizeof(pdata->reg_init_data[i])); + if (platform_device_add_data(pdev, &pdata->reg_init_data[i], + sizeof(pdata->reg_init_data[i])) < 0) { + platform_device_put(pdev); + dev_err(pcf->dev, "Out of memory for regulator parameters %d\n", + i); + continue; + } pcf->regulator_pdev[i] = pdev; platform_device_add(pdev); diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c deleted file mode 100644 index dd170307e60e..000000000000 --- a/drivers/mfd/s5m-core.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * s5m87xx.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd - * http://www.samsung.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. - * - */ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/init.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/interrupt.h> -#include <linux/pm_runtime.h> -#include <linux/mutex.h> -#include <linux/mfd/core.h> -#include <linux/mfd/s5m87xx/s5m-core.h> -#include <linux/mfd/s5m87xx/s5m-pmic.h> -#include <linux/mfd/s5m87xx/s5m-rtc.h> -#include <linux/regmap.h> - -static struct mfd_cell s5m8751_devs[] = { - { - .name = "s5m8751-pmic", - }, { - .name = "s5m-charger", - }, { - .name = "s5m8751-codec", - }, -}; - -static struct mfd_cell s5m8763_devs[] = { - { - .name = "s5m8763-pmic", - }, { - .name = "s5m-rtc", - }, { - .name = "s5m-charger", - }, -}; - -static struct mfd_cell s5m8767_devs[] = { - { - .name = "s5m8767-pmic", - }, { - .name = "s5m-rtc", - }, -}; - -int s5m_reg_read(struct s5m87xx_dev *s5m87xx, u8 reg, void *dest) -{ - return regmap_read(s5m87xx->regmap, reg, dest); -} -EXPORT_SYMBOL_GPL(s5m_reg_read); - -int s5m_bulk_read(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf) -{ - return regmap_bulk_read(s5m87xx->regmap, reg, buf, count); -} -EXPORT_SYMBOL_GPL(s5m_bulk_read); - -int s5m_reg_write(struct s5m87xx_dev *s5m87xx, u8 reg, u8 value) -{ - return regmap_write(s5m87xx->regmap, reg, value); -} -EXPORT_SYMBOL_GPL(s5m_reg_write); - -int s5m_bulk_write(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf) -{ - return regmap_raw_write(s5m87xx->regmap, reg, buf, count); -} -EXPORT_SYMBOL_GPL(s5m_bulk_write); - -int s5m_reg_update(struct s5m87xx_dev *s5m87xx, u8 reg, u8 val, u8 mask) -{ - return regmap_update_bits(s5m87xx->regmap, reg, mask, val); -} -EXPORT_SYMBOL_GPL(s5m_reg_update); - -static struct regmap_config s5m_regmap_config = { - .reg_bits = 8, - .val_bits = 8, -}; - -static int s5m87xx_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct s5m_platform_data *pdata = i2c->dev.platform_data; - struct s5m87xx_dev *s5m87xx; - int ret; - - s5m87xx = devm_kzalloc(&i2c->dev, sizeof(struct s5m87xx_dev), - GFP_KERNEL); - if (s5m87xx == NULL) - return -ENOMEM; - - i2c_set_clientdata(i2c, s5m87xx); - s5m87xx->dev = &i2c->dev; - s5m87xx->i2c = i2c; - s5m87xx->irq = i2c->irq; - s5m87xx->type = id->driver_data; - - if (pdata) { - s5m87xx->device_type = pdata->device_type; - s5m87xx->ono = pdata->ono; - s5m87xx->irq_base = pdata->irq_base; - s5m87xx->wakeup = pdata->wakeup; - } - - s5m87xx->regmap = devm_regmap_init_i2c(i2c, &s5m_regmap_config); - if (IS_ERR(s5m87xx->regmap)) { - ret = PTR_ERR(s5m87xx->regmap); - dev_err(&i2c->dev, "Failed to allocate register map: %d\n", - ret); - return ret; - } - - s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR); - i2c_set_clientdata(s5m87xx->rtc, s5m87xx); - - if (pdata && pdata->cfg_pmic_irq) - pdata->cfg_pmic_irq(); - - s5m_irq_init(s5m87xx); - - pm_runtime_set_active(s5m87xx->dev); - - switch (s5m87xx->device_type) { - case S5M8751X: - ret = mfd_add_devices(s5m87xx->dev, -1, s5m8751_devs, - ARRAY_SIZE(s5m8751_devs), NULL, 0); - break; - case S5M8763X: - ret = mfd_add_devices(s5m87xx->dev, -1, s5m8763_devs, - ARRAY_SIZE(s5m8763_devs), NULL, 0); - break; - case S5M8767X: - ret = mfd_add_devices(s5m87xx->dev, -1, s5m8767_devs, - ARRAY_SIZE(s5m8767_devs), NULL, 0); - break; - default: - /* If this happens the probe function is problem */ - BUG(); - } - - if (ret < 0) - goto err; - - return ret; - -err: - mfd_remove_devices(s5m87xx->dev); - s5m_irq_exit(s5m87xx); - i2c_unregister_device(s5m87xx->rtc); - return ret; -} - -static int s5m87xx_i2c_remove(struct i2c_client *i2c) -{ - struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c); - - mfd_remove_devices(s5m87xx->dev); - s5m_irq_exit(s5m87xx); - i2c_unregister_device(s5m87xx->rtc); - return 0; -} - -static const struct i2c_device_id s5m87xx_i2c_id[] = { - { "s5m87xx", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, s5m87xx_i2c_id); - -static struct i2c_driver s5m87xx_i2c_driver = { - .driver = { - .name = "s5m87xx", - .owner = THIS_MODULE, - }, - .probe = s5m87xx_i2c_probe, - .remove = s5m87xx_i2c_remove, - .id_table = s5m87xx_i2c_id, -}; - -static int __init s5m87xx_i2c_init(void) -{ - return i2c_add_driver(&s5m87xx_i2c_driver); -} - -subsys_initcall(s5m87xx_i2c_init); - -static void __exit s5m87xx_i2c_exit(void) -{ - i2c_del_driver(&s5m87xx_i2c_driver); -} -module_exit(s5m87xx_i2c_exit); - -MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); -MODULE_DESCRIPTION("Core support for the S5M MFD"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/s5m-irq.c b/drivers/mfd/s5m-irq.c deleted file mode 100644 index 0236676085cf..000000000000 --- a/drivers/mfd/s5m-irq.c +++ /dev/null @@ -1,495 +0,0 @@ -/* - * s5m-irq.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd - * http://www.samsung.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. - * - */ - -#include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/mfd/s5m87xx/s5m-core.h> - -struct s5m_irq_data { - int reg; - int mask; -}; - -static struct s5m_irq_data s5m8767_irqs[] = { - [S5M8767_IRQ_PWRR] = { - .reg = 1, - .mask = S5M8767_IRQ_PWRR_MASK, - }, - [S5M8767_IRQ_PWRF] = { - .reg = 1, - .mask = S5M8767_IRQ_PWRF_MASK, - }, - [S5M8767_IRQ_PWR1S] = { - .reg = 1, - .mask = S5M8767_IRQ_PWR1S_MASK, - }, - [S5M8767_IRQ_JIGR] = { - .reg = 1, - .mask = S5M8767_IRQ_JIGR_MASK, - }, - [S5M8767_IRQ_JIGF] = { - .reg = 1, - .mask = S5M8767_IRQ_JIGF_MASK, - }, - [S5M8767_IRQ_LOWBAT2] = { - .reg = 1, - .mask = S5M8767_IRQ_LOWBAT2_MASK, - }, - [S5M8767_IRQ_LOWBAT1] = { - .reg = 1, - .mask = S5M8767_IRQ_LOWBAT1_MASK, - }, - [S5M8767_IRQ_MRB] = { - .reg = 2, - .mask = S5M8767_IRQ_MRB_MASK, - }, - [S5M8767_IRQ_DVSOK2] = { - .reg = 2, - .mask = S5M8767_IRQ_DVSOK2_MASK, - }, - [S5M8767_IRQ_DVSOK3] = { - .reg = 2, - .mask = S5M8767_IRQ_DVSOK3_MASK, - }, - [S5M8767_IRQ_DVSOK4] = { - .reg = 2, - .mask = S5M8767_IRQ_DVSOK4_MASK, - }, - [S5M8767_IRQ_RTC60S] = { - .reg = 3, - .mask = S5M8767_IRQ_RTC60S_MASK, - }, - [S5M8767_IRQ_RTCA1] = { - .reg = 3, - .mask = S5M8767_IRQ_RTCA1_MASK, - }, - [S5M8767_IRQ_RTCA2] = { - .reg = 3, - .mask = S5M8767_IRQ_RTCA2_MASK, - }, - [S5M8767_IRQ_SMPL] = { - .reg = 3, - .mask = S5M8767_IRQ_SMPL_MASK, - }, - [S5M8767_IRQ_RTC1S] = { - .reg = 3, - .mask = S5M8767_IRQ_RTC1S_MASK, - }, - [S5M8767_IRQ_WTSR] = { - .reg = 3, - .mask = S5M8767_IRQ_WTSR_MASK, - }, -}; - -static struct s5m_irq_data s5m8763_irqs[] = { - [S5M8763_IRQ_DCINF] = { - .reg = 1, - .mask = S5M8763_IRQ_DCINF_MASK, - }, - [S5M8763_IRQ_DCINR] = { - .reg = 1, - .mask = S5M8763_IRQ_DCINR_MASK, - }, - [S5M8763_IRQ_JIGF] = { - .reg = 1, - .mask = S5M8763_IRQ_JIGF_MASK, - }, - [S5M8763_IRQ_JIGR] = { - .reg = 1, - .mask = S5M8763_IRQ_JIGR_MASK, - }, - [S5M8763_IRQ_PWRONF] = { - .reg = 1, - .mask = S5M8763_IRQ_PWRONF_MASK, - }, - [S5M8763_IRQ_PWRONR] = { - .reg = 1, - .mask = S5M8763_IRQ_PWRONR_MASK, - }, - [S5M8763_IRQ_WTSREVNT] = { - .reg = 2, - .mask = S5M8763_IRQ_WTSREVNT_MASK, - }, - [S5M8763_IRQ_SMPLEVNT] = { - .reg = 2, - .mask = S5M8763_IRQ_SMPLEVNT_MASK, - }, - [S5M8763_IRQ_ALARM1] = { - .reg = 2, - .mask = S5M8763_IRQ_ALARM1_MASK, - }, - [S5M8763_IRQ_ALARM0] = { - .reg = 2, - .mask = S5M8763_IRQ_ALARM0_MASK, - }, - [S5M8763_IRQ_ONKEY1S] = { - .reg = 3, - .mask = S5M8763_IRQ_ONKEY1S_MASK, - }, - [S5M8763_IRQ_TOPOFFR] = { - .reg = 3, - .mask = S5M8763_IRQ_TOPOFFR_MASK, - }, - [S5M8763_IRQ_DCINOVPR] = { - .reg = 3, - .mask = S5M8763_IRQ_DCINOVPR_MASK, - }, - [S5M8763_IRQ_CHGRSTF] = { - .reg = 3, - .mask = S5M8763_IRQ_CHGRSTF_MASK, - }, - [S5M8763_IRQ_DONER] = { - .reg = 3, - .mask = S5M8763_IRQ_DONER_MASK, - }, - [S5M8763_IRQ_CHGFAULT] = { - .reg = 3, - .mask = S5M8763_IRQ_CHGFAULT_MASK, - }, - [S5M8763_IRQ_LOBAT1] = { - .reg = 4, - .mask = S5M8763_IRQ_LOBAT1_MASK, - }, - [S5M8763_IRQ_LOBAT2] = { - .reg = 4, - .mask = S5M8763_IRQ_LOBAT2_MASK, - }, -}; - -static inline struct s5m_irq_data * -irq_to_s5m8767_irq(struct s5m87xx_dev *s5m87xx, int irq) -{ - return &s5m8767_irqs[irq - s5m87xx->irq_base]; -} - -static void s5m8767_irq_lock(struct irq_data *data) -{ - struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); - - mutex_lock(&s5m87xx->irqlock); -} - -static void s5m8767_irq_sync_unlock(struct irq_data *data) -{ - struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); - int i; - - for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) { - if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) { - s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i]; - s5m_reg_write(s5m87xx, S5M8767_REG_INT1M + i, - s5m87xx->irq_masks_cur[i]); - } - } - - mutex_unlock(&s5m87xx->irqlock); -} - -static void s5m8767_irq_unmask(struct irq_data *data) -{ - struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); - struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx, - data->irq); - - s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; -} - -static void s5m8767_irq_mask(struct irq_data *data) -{ - struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); - struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx, - data->irq); - - s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; -} - -static struct irq_chip s5m8767_irq_chip = { - .name = "s5m8767", - .irq_bus_lock = s5m8767_irq_lock, - .irq_bus_sync_unlock = s5m8767_irq_sync_unlock, - .irq_mask = s5m8767_irq_mask, - .irq_unmask = s5m8767_irq_unmask, -}; - -static inline struct s5m_irq_data * -irq_to_s5m8763_irq(struct s5m87xx_dev *s5m87xx, int irq) -{ - return &s5m8763_irqs[irq - s5m87xx->irq_base]; -} - -static void s5m8763_irq_lock(struct irq_data *data) -{ - struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); - - mutex_lock(&s5m87xx->irqlock); -} - -static void s5m8763_irq_sync_unlock(struct irq_data *data) -{ - struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); - int i; - - for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) { - if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) { - s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i]; - s5m_reg_write(s5m87xx, S5M8763_REG_IRQM1 + i, - s5m87xx->irq_masks_cur[i]); - } - } - - mutex_unlock(&s5m87xx->irqlock); -} - -static void s5m8763_irq_unmask(struct irq_data *data) -{ - struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); - struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx, - data->irq); - - s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; -} - -static void s5m8763_irq_mask(struct irq_data *data) -{ - struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); - struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx, - data->irq); - - s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; -} - -static struct irq_chip s5m8763_irq_chip = { - .name = "s5m8763", - .irq_bus_lock = s5m8763_irq_lock, - .irq_bus_sync_unlock = s5m8763_irq_sync_unlock, - .irq_mask = s5m8763_irq_mask, - .irq_unmask = s5m8763_irq_unmask, -}; - - -static irqreturn_t s5m8767_irq_thread(int irq, void *data) -{ - struct s5m87xx_dev *s5m87xx = data; - u8 irq_reg[NUM_IRQ_REGS-1]; - int ret; - int i; - - - ret = s5m_bulk_read(s5m87xx, S5M8767_REG_INT1, - NUM_IRQ_REGS - 1, irq_reg); - if (ret < 0) { - dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n", - ret); - return IRQ_NONE; - } - - for (i = 0; i < NUM_IRQ_REGS - 1; i++) - irq_reg[i] &= ~s5m87xx->irq_masks_cur[i]; - - for (i = 0; i < S5M8767_IRQ_NR; i++) { - if (irq_reg[s5m8767_irqs[i].reg - 1] & s5m8767_irqs[i].mask) - handle_nested_irq(s5m87xx->irq_base + i); - } - - return IRQ_HANDLED; -} - -static irqreturn_t s5m8763_irq_thread(int irq, void *data) -{ - struct s5m87xx_dev *s5m87xx = data; - u8 irq_reg[NUM_IRQ_REGS]; - int ret; - int i; - - ret = s5m_bulk_read(s5m87xx, S5M8763_REG_IRQ1, - NUM_IRQ_REGS, irq_reg); - if (ret < 0) { - dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n", - ret); - return IRQ_NONE; - } - - for (i = 0; i < NUM_IRQ_REGS; i++) - irq_reg[i] &= ~s5m87xx->irq_masks_cur[i]; - - for (i = 0; i < S5M8763_IRQ_NR; i++) { - if (irq_reg[s5m8763_irqs[i].reg - 1] & s5m8763_irqs[i].mask) - handle_nested_irq(s5m87xx->irq_base + i); - } - - return IRQ_HANDLED; -} - -int s5m_irq_resume(struct s5m87xx_dev *s5m87xx) -{ - if (s5m87xx->irq && s5m87xx->irq_base){ - switch (s5m87xx->device_type) { - case S5M8763X: - s5m8763_irq_thread(s5m87xx->irq_base, s5m87xx); - break; - case S5M8767X: - s5m8767_irq_thread(s5m87xx->irq_base, s5m87xx); - break; - default: - dev_err(s5m87xx->dev, - "Unknown device type %d\n", - s5m87xx->device_type); - return -EINVAL; - - } - } - return 0; -} - -int s5m_irq_init(struct s5m87xx_dev *s5m87xx) -{ - int i; - int cur_irq; - int ret = 0; - int type = s5m87xx->device_type; - - if (!s5m87xx->irq) { - dev_warn(s5m87xx->dev, - "No interrupt specified, no interrupts\n"); - s5m87xx->irq_base = 0; - return 0; - } - - if (!s5m87xx->irq_base) { - dev_err(s5m87xx->dev, - "No interrupt base specified, no interrupts\n"); - return 0; - } - - mutex_init(&s5m87xx->irqlock); - - switch (type) { - case S5M8763X: - for (i = 0; i < NUM_IRQ_REGS; i++) { - s5m87xx->irq_masks_cur[i] = 0xff; - s5m87xx->irq_masks_cache[i] = 0xff; - s5m_reg_write(s5m87xx, S5M8763_REG_IRQM1 + i, - 0xff); - } - - s5m_reg_write(s5m87xx, S5M8763_REG_STATUSM1, 0xff); - s5m_reg_write(s5m87xx, S5M8763_REG_STATUSM2, 0xff); - - for (i = 0; i < S5M8763_IRQ_NR; i++) { - cur_irq = i + s5m87xx->irq_base; - irq_set_chip_data(cur_irq, s5m87xx); - irq_set_chip_and_handler(cur_irq, &s5m8763_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(cur_irq, IRQF_VALID); -#else - irq_set_noprobe(cur_irq); -#endif - } - - ret = request_threaded_irq(s5m87xx->irq, NULL, - s5m8763_irq_thread, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "s5m87xx-irq", s5m87xx); - if (ret) { - dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n", - s5m87xx->irq, ret); - return ret; - } - break; - case S5M8767X: - for (i = 0; i < NUM_IRQ_REGS - 1; i++) { - s5m87xx->irq_masks_cur[i] = 0xff; - s5m87xx->irq_masks_cache[i] = 0xff; - s5m_reg_write(s5m87xx, S5M8767_REG_INT1M + i, - 0xff); - } - for (i = 0; i < S5M8767_IRQ_NR; i++) { - cur_irq = i + s5m87xx->irq_base; - irq_set_chip_data(cur_irq, s5m87xx); - if (ret) { - dev_err(s5m87xx->dev, - "Failed to irq_set_chip_data %d: %d\n", - s5m87xx->irq, ret); - return ret; - } - - irq_set_chip_and_handler(cur_irq, &s5m8767_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(cur_irq, IRQF_VALID); -#else - irq_set_noprobe(cur_irq); -#endif - } - - ret = request_threaded_irq(s5m87xx->irq, NULL, - s5m8767_irq_thread, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "s5m87xx-irq", s5m87xx); - if (ret) { - dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n", - s5m87xx->irq, ret); - return ret; - } - break; - default: - dev_err(s5m87xx->dev, - "Unknown device type %d\n", s5m87xx->device_type); - return -EINVAL; - } - - if (!s5m87xx->ono) - return 0; - - switch (type) { - case S5M8763X: - ret = request_threaded_irq(s5m87xx->ono, NULL, - s5m8763_irq_thread, - IRQF_TRIGGER_FALLING | - IRQF_TRIGGER_RISING | - IRQF_ONESHOT, "s5m87xx-ono", - s5m87xx); - break; - case S5M8767X: - ret = request_threaded_irq(s5m87xx->ono, NULL, - s5m8767_irq_thread, - IRQF_TRIGGER_FALLING | - IRQF_TRIGGER_RISING | - IRQF_ONESHOT, "s5m87xx-ono", s5m87xx); - break; - default: - ret = -EINVAL; - break; - } - - if (ret) { - dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n", - s5m87xx->ono, ret); - return ret; - } - - return 0; -} - -void s5m_irq_exit(struct s5m87xx_dev *s5m87xx) -{ - if (s5m87xx->ono) - free_irq(s5m87xx->ono, s5m87xx); - - if (s5m87xx->irq) - free_irq(s5m87xx->irq, s5m87xx); -} diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c new file mode 100644 index 000000000000..2988efde11eb --- /dev/null +++ b/drivers/mfd/sec-core.c @@ -0,0 +1,216 @@ +/* + * sec-core.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * http://www.samsung.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. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> +#include <linux/mutex.h> +#include <linux/mfd/core.h> +#include <linux/mfd/samsung/core.h> +#include <linux/mfd/samsung/irq.h> +#include <linux/mfd/samsung/rtc.h> +#include <linux/regmap.h> + +static struct mfd_cell s5m8751_devs[] = { + { + .name = "s5m8751-pmic", + }, { + .name = "s5m-charger", + }, { + .name = "s5m8751-codec", + }, +}; + +static struct mfd_cell s5m8763_devs[] = { + { + .name = "s5m8763-pmic", + }, { + .name = "s5m-rtc", + }, { + .name = "s5m-charger", + }, +}; + +static struct mfd_cell s5m8767_devs[] = { + { + .name = "s5m8767-pmic", + }, { + .name = "s5m-rtc", + }, +}; + +static struct mfd_cell s2mps11_devs[] = { + { + .name = "s2mps11-pmic", + }, +}; + +int sec_reg_read(struct sec_pmic_dev *sec_pmic, u8 reg, void *dest) +{ + return regmap_read(sec_pmic->regmap, reg, dest); +} +EXPORT_SYMBOL_GPL(sec_reg_read); + +int sec_bulk_read(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf) +{ + return regmap_bulk_read(sec_pmic->regmap, reg, buf, count); +} +EXPORT_SYMBOL_GPL(sec_bulk_read); + +int sec_reg_write(struct sec_pmic_dev *sec_pmic, u8 reg, u8 value) +{ + return regmap_write(sec_pmic->regmap, reg, value); +} +EXPORT_SYMBOL_GPL(sec_reg_write); + +int sec_bulk_write(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf) +{ + return regmap_raw_write(sec_pmic->regmap, reg, buf, count); +} +EXPORT_SYMBOL_GPL(sec_bulk_write); + +int sec_reg_update(struct sec_pmic_dev *sec_pmic, u8 reg, u8 val, u8 mask) +{ + return regmap_update_bits(sec_pmic->regmap, reg, mask, val); +} +EXPORT_SYMBOL_GPL(sec_reg_update); + +static struct regmap_config sec_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int sec_pmic_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct sec_platform_data *pdata = i2c->dev.platform_data; + struct sec_pmic_dev *sec_pmic; + int ret; + + sec_pmic = devm_kzalloc(&i2c->dev, sizeof(struct sec_pmic_dev), + GFP_KERNEL); + if (sec_pmic == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, sec_pmic); + sec_pmic->dev = &i2c->dev; + sec_pmic->i2c = i2c; + sec_pmic->irq = i2c->irq; + sec_pmic->type = id->driver_data; + + if (pdata) { + sec_pmic->device_type = pdata->device_type; + sec_pmic->ono = pdata->ono; + sec_pmic->irq_base = pdata->irq_base; + sec_pmic->wakeup = pdata->wakeup; + } + + sec_pmic->regmap = devm_regmap_init_i2c(i2c, &sec_regmap_config); + if (IS_ERR(sec_pmic->regmap)) { + ret = PTR_ERR(sec_pmic->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + sec_pmic->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR); + i2c_set_clientdata(sec_pmic->rtc, sec_pmic); + + if (pdata && pdata->cfg_pmic_irq) + pdata->cfg_pmic_irq(); + + sec_irq_init(sec_pmic); + + pm_runtime_set_active(sec_pmic->dev); + + switch (sec_pmic->device_type) { + case S5M8751X: + ret = mfd_add_devices(sec_pmic->dev, -1, s5m8751_devs, + ARRAY_SIZE(s5m8751_devs), NULL, 0); + break; + case S5M8763X: + ret = mfd_add_devices(sec_pmic->dev, -1, s5m8763_devs, + ARRAY_SIZE(s5m8763_devs), NULL, 0); + break; + case S5M8767X: + ret = mfd_add_devices(sec_pmic->dev, -1, s5m8767_devs, + ARRAY_SIZE(s5m8767_devs), NULL, 0); + break; + case S2MPS11X: + ret = mfd_add_devices(sec_pmic->dev, -1, s2mps11_devs, + ARRAY_SIZE(s2mps11_devs), NULL, 0); + break; + default: + /* If this happens the probe function is problem */ + BUG(); + } + + if (ret < 0) + goto err; + + return ret; + +err: + mfd_remove_devices(sec_pmic->dev); + sec_irq_exit(sec_pmic); + i2c_unregister_device(sec_pmic->rtc); + return ret; +} + +static int sec_pmic_remove(struct i2c_client *i2c) +{ + struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c); + + mfd_remove_devices(sec_pmic->dev); + sec_irq_exit(sec_pmic); + i2c_unregister_device(sec_pmic->rtc); + return 0; +} + +static const struct i2c_device_id sec_pmic_id[] = { + { "sec_pmic", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sec_pmic_id); + +static struct i2c_driver sec_pmic_driver = { + .driver = { + .name = "sec_pmic", + .owner = THIS_MODULE, + }, + .probe = sec_pmic_probe, + .remove = sec_pmic_remove, + .id_table = sec_pmic_id, +}; + +static int __init sec_pmic_init(void) +{ + return i2c_add_driver(&sec_pmic_driver); +} + +subsys_initcall(sec_pmic_init); + +static void __exit sec_pmic_exit(void) +{ + i2c_del_driver(&sec_pmic_driver); +} +module_exit(sec_pmic_exit); + +MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); +MODULE_DESCRIPTION("Core support for the S5M MFD"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c new file mode 100644 index 000000000000..c901fa50fea1 --- /dev/null +++ b/drivers/mfd/sec-irq.c @@ -0,0 +1,317 @@ +/* + * sec-irq.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * http://www.samsung.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. + * + */ + +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/regmap.h> + +#include <linux/mfd/samsung/core.h> +#include <linux/mfd/samsung/irq.h> +#include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/s5m8763.h> +#include <linux/mfd/samsung/s5m8767.h> + +static struct regmap_irq s2mps11_irqs[] = { + [S2MPS11_IRQ_PWRONF] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_PWRONF_MASK, + }, + [S2MPS11_IRQ_PWRONR] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_PWRONR_MASK, + }, + [S2MPS11_IRQ_JIGONBF] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_JIGONBF_MASK, + }, + [S2MPS11_IRQ_JIGONBR] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_JIGONBR_MASK, + }, + [S2MPS11_IRQ_ACOKBF] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_ACOKBF_MASK, + }, + [S2MPS11_IRQ_ACOKBR] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_ACOKBR_MASK, + }, + [S2MPS11_IRQ_PWRON1S] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_PWRON1S_MASK, + }, + [S2MPS11_IRQ_MRB] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_MRB_MASK, + }, + [S2MPS11_IRQ_RTC60S] = { + .reg_offset = 2, + .mask = S2MPS11_IRQ_RTC60S_MASK, + }, + [S2MPS11_IRQ_RTCA1] = { + .reg_offset = 2, + .mask = S2MPS11_IRQ_RTCA1_MASK, + }, + [S2MPS11_IRQ_RTCA2] = { + .reg_offset = 2, + .mask = S2MPS11_IRQ_RTCA2_MASK, + }, + [S2MPS11_IRQ_SMPL] = { + .reg_offset = 2, + .mask = S2MPS11_IRQ_SMPL_MASK, + }, + [S2MPS11_IRQ_RTC1S] = { + .reg_offset = 2, + .mask = S2MPS11_IRQ_RTC1S_MASK, + }, + [S2MPS11_IRQ_WTSR] = { + .reg_offset = 2, + .mask = S2MPS11_IRQ_WTSR_MASK, + }, + [S2MPS11_IRQ_INT120C] = { + .reg_offset = 3, + .mask = S2MPS11_IRQ_INT120C_MASK, + }, + [S2MPS11_IRQ_INT140C] = { + .reg_offset = 3, + .mask = S2MPS11_IRQ_INT140C_MASK, + }, +}; + + +static struct regmap_irq s5m8767_irqs[] = { + [S5M8767_IRQ_PWRR] = { + .reg_offset = 1, + .mask = S5M8767_IRQ_PWRR_MASK, + }, + [S5M8767_IRQ_PWRF] = { + .reg_offset = 1, + .mask = S5M8767_IRQ_PWRF_MASK, + }, + [S5M8767_IRQ_PWR1S] = { + .reg_offset = 1, + .mask = S5M8767_IRQ_PWR1S_MASK, + }, + [S5M8767_IRQ_JIGR] = { + .reg_offset = 1, + .mask = S5M8767_IRQ_JIGR_MASK, + }, + [S5M8767_IRQ_JIGF] = { + .reg_offset = 1, + .mask = S5M8767_IRQ_JIGF_MASK, + }, + [S5M8767_IRQ_LOWBAT2] = { + .reg_offset = 1, + .mask = S5M8767_IRQ_LOWBAT2_MASK, + }, + [S5M8767_IRQ_LOWBAT1] = { + .reg_offset = 1, + .mask = S5M8767_IRQ_LOWBAT1_MASK, + }, + [S5M8767_IRQ_MRB] = { + .reg_offset = 2, + .mask = S5M8767_IRQ_MRB_MASK, + }, + [S5M8767_IRQ_DVSOK2] = { + .reg_offset = 2, + .mask = S5M8767_IRQ_DVSOK2_MASK, + }, + [S5M8767_IRQ_DVSOK3] = { + .reg_offset = 2, + .mask = S5M8767_IRQ_DVSOK3_MASK, + }, + [S5M8767_IRQ_DVSOK4] = { + .reg_offset = 2, + .mask = S5M8767_IRQ_DVSOK4_MASK, + }, + [S5M8767_IRQ_RTC60S] = { + .reg_offset = 3, + .mask = S5M8767_IRQ_RTC60S_MASK, + }, + [S5M8767_IRQ_RTCA1] = { + .reg_offset = 3, + .mask = S5M8767_IRQ_RTCA1_MASK, + }, + [S5M8767_IRQ_RTCA2] = { + .reg_offset = 3, + .mask = S5M8767_IRQ_RTCA2_MASK, + }, + [S5M8767_IRQ_SMPL] = { + .reg_offset = 3, + .mask = S5M8767_IRQ_SMPL_MASK, + }, + [S5M8767_IRQ_RTC1S] = { + .reg_offset = 3, + .mask = S5M8767_IRQ_RTC1S_MASK, + }, + [S5M8767_IRQ_WTSR] = { + .reg_offset = 3, + .mask = S5M8767_IRQ_WTSR_MASK, + }, +}; + +static struct regmap_irq s5m8763_irqs[] = { + [S5M8763_IRQ_DCINF] = { + .reg_offset = 1, + .mask = S5M8763_IRQ_DCINF_MASK, + }, + [S5M8763_IRQ_DCINR] = { + .reg_offset = 1, + .mask = S5M8763_IRQ_DCINR_MASK, + }, + [S5M8763_IRQ_JIGF] = { + .reg_offset = 1, + .mask = S5M8763_IRQ_JIGF_MASK, + }, + [S5M8763_IRQ_JIGR] = { + .reg_offset = 1, + .mask = S5M8763_IRQ_JIGR_MASK, + }, + [S5M8763_IRQ_PWRONF] = { + .reg_offset = 1, + .mask = S5M8763_IRQ_PWRONF_MASK, + }, + [S5M8763_IRQ_PWRONR] = { + .reg_offset = 1, + .mask = S5M8763_IRQ_PWRONR_MASK, + }, + [S5M8763_IRQ_WTSREVNT] = { + .reg_offset = 2, + .mask = S5M8763_IRQ_WTSREVNT_MASK, + }, + [S5M8763_IRQ_SMPLEVNT] = { + .reg_offset = 2, + .mask = S5M8763_IRQ_SMPLEVNT_MASK, + }, + [S5M8763_IRQ_ALARM1] = { + .reg_offset = 2, + .mask = S5M8763_IRQ_ALARM1_MASK, + }, + [S5M8763_IRQ_ALARM0] = { + .reg_offset = 2, + .mask = S5M8763_IRQ_ALARM0_MASK, + }, + [S5M8763_IRQ_ONKEY1S] = { + .reg_offset = 3, + .mask = S5M8763_IRQ_ONKEY1S_MASK, + }, + [S5M8763_IRQ_TOPOFFR] = { + .reg_offset = 3, + .mask = S5M8763_IRQ_TOPOFFR_MASK, + }, + [S5M8763_IRQ_DCINOVPR] = { + .reg_offset = 3, + .mask = S5M8763_IRQ_DCINOVPR_MASK, + }, + [S5M8763_IRQ_CHGRSTF] = { + .reg_offset = 3, + .mask = S5M8763_IRQ_CHGRSTF_MASK, + }, + [S5M8763_IRQ_DONER] = { + .reg_offset = 3, + .mask = S5M8763_IRQ_DONER_MASK, + }, + [S5M8763_IRQ_CHGFAULT] = { + .reg_offset = 3, + .mask = S5M8763_IRQ_CHGFAULT_MASK, + }, + [S5M8763_IRQ_LOBAT1] = { + .reg_offset = 4, + .mask = S5M8763_IRQ_LOBAT1_MASK, + }, + [S5M8763_IRQ_LOBAT2] = { + .reg_offset = 4, + .mask = S5M8763_IRQ_LOBAT2_MASK, + }, +}; + +static struct regmap_irq_chip s2mps11_irq_chip = { + .name = "s2mps11", + .irqs = s2mps11_irqs, + .num_irqs = ARRAY_SIZE(s2mps11_irqs), + .num_regs = 3, + .status_base = S2MPS11_REG_INT1, + .mask_base = S2MPS11_REG_INT1M, + .ack_base = S2MPS11_REG_INT1, +}; + +static struct regmap_irq_chip s5m8767_irq_chip = { + .name = "s5m8767", + .irqs = s5m8767_irqs, + .num_irqs = ARRAY_SIZE(s5m8767_irqs), + .num_regs = 3, + .status_base = S5M8767_REG_INT1, + .mask_base = S5M8767_REG_INT1M, + .ack_base = S5M8767_REG_INT1, +}; + +static struct regmap_irq_chip s5m8763_irq_chip = { + .name = "s5m8763", + .irqs = s5m8763_irqs, + .num_irqs = ARRAY_SIZE(s5m8763_irqs), + .num_regs = 4, + .status_base = S5M8763_REG_IRQ1, + .mask_base = S5M8763_REG_IRQM1, + .ack_base = S5M8763_REG_IRQ1, +}; + +int sec_irq_init(struct sec_pmic_dev *sec_pmic) +{ + int ret = 0; + int type = sec_pmic->device_type; + + if (!sec_pmic->irq) { + dev_warn(sec_pmic->dev, + "No interrupt specified, no interrupts\n"); + sec_pmic->irq_base = 0; + return 0; + } + + switch (type) { + case S5M8763X: + ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + sec_pmic->irq_base, &s5m8763_irq_chip, + &sec_pmic->irq_data); + break; + case S5M8767X: + ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + sec_pmic->irq_base, &s5m8767_irq_chip, + &sec_pmic->irq_data); + break; + case S2MPS11X: + ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + sec_pmic->irq_base, &s2mps11_irq_chip, + &sec_pmic->irq_data); + break; + default: + dev_err(sec_pmic->dev, "Unknown device type %d\n", + sec_pmic->device_type); + return -EINVAL; + } + + if (ret != 0) { + dev_err(sec_pmic->dev, "Failed to register IRQ chip: %d\n", ret); + return ret; + } + + return 0; +} + +void sec_irq_exit(struct sec_pmic_dev *sec_pmic) +{ + regmap_del_irq_chip(sec_pmic->irq, sec_pmic->irq_data); +} diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index de979742c6fc..048bf0532a09 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -357,7 +357,7 @@ static int __devexit tc3589x_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int tc3589x_suspend(struct device *dev) { struct tc3589x *tc3589x = dev_get_drvdata(dev); @@ -385,11 +385,10 @@ static int tc3589x_resume(struct device *dev) return ret; } - -static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, - tc3589x_resume); #endif +static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume); + static const struct i2c_device_id tc3589x_id[] = { { "tc3589x", 24 }, { } @@ -399,9 +398,7 @@ MODULE_DEVICE_TABLE(i2c, tc3589x_id); static struct i2c_driver tc3589x_driver = { .driver.name = "tc3589x", .driver.owner = THIS_MODULE, -#ifdef CONFIG_PM .driver.pm = &tc3589x_dev_pm_ops, -#endif .probe = tc3589x_probe, .remove = __devexit_p(tc3589x_remove), .id_table = tc3589x_id, diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 0ba26fb12cf5..a447f4ec11fb 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -83,7 +83,7 @@ timberdale_xiic_platform_data = { static __devinitdata struct ocores_i2c_platform_data timberdale_ocores_platform_data = { - .regstep = 4, + .reg_shift = 2, .clock_khz = 62500, .devices = timberdale_i2c_board_info, .num_devices = ARRAY_SIZE(timberdale_i2c_board_info) diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 396b9d1b6bd6..80e24f4b47bf 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -71,10 +71,10 @@ static const struct tps65090_irq_data tps65090_irqs[] = { static struct mfd_cell tps65090s[] = { { - .name = "tps65910-pmic", + .name = "tps65090-pmic", }, { - .name = "tps65910-regulator", + .name = "tps65090-regulator", }, }; diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index c84b5506d5fb..353c34812120 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -21,17 +21,14 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> -#include <linux/gpio.h> +#include <linux/err.h> #include <linux/i2c.h> +#include <linux/regmap.h> #include <linux/regulator/of_regulator.h> #include <linux/mfd/core.h> #include <linux/mfd/tps6586x.h> -/* GPIO control registers */ -#define TPS6586X_GPIOSET1 0x5d -#define TPS6586X_GPIOSET2 0x5e - /* interrupt control registers */ #define TPS6586X_INT_ACK1 0xb5 #define TPS6586X_INT_ACK2 0xb6 @@ -48,6 +45,9 @@ /* device id */ #define TPS6586X_VERSIONCRC 0xcd +/* Maximum register */ +#define TPS6586X_MAX_REGISTER (TPS6586X_VERSIONCRC + 1) + struct tps6586x_irq_data { u8 mask_reg; u8 mask_mask; @@ -89,226 +89,96 @@ static const struct tps6586x_irq_data tps6586x_irqs[] = { [TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1), }; +static struct mfd_cell tps6586x_cell[] = { + { + .name = "tps6586x-gpio", + }, + { + .name = "tps6586x-rtc", + }, + { + .name = "tps6586x-onkey", + }, +}; + struct tps6586x { - struct mutex lock; struct device *dev; struct i2c_client *client; + struct regmap *regmap; - struct gpio_chip gpio; struct irq_chip irq_chip; struct mutex irq_lock; int irq_base; u32 irq_en; - u8 mask_cache[5]; u8 mask_reg[5]; }; -static inline int __tps6586x_read(struct i2c_client *client, - int reg, uint8_t *val) -{ - int ret; - - ret = i2c_smbus_read_byte_data(client, reg); - if (ret < 0) { - dev_err(&client->dev, "failed reading at 0x%02x\n", reg); - return ret; - } - - *val = (uint8_t)ret; - - return 0; -} - -static inline int __tps6586x_reads(struct i2c_client *client, int reg, - int len, uint8_t *val) -{ - int ret; - - ret = i2c_smbus_read_i2c_block_data(client, reg, len, val); - if (ret < 0) { - dev_err(&client->dev, "failed reading from 0x%02x\n", reg); - return ret; - } - - return 0; -} - -static inline int __tps6586x_write(struct i2c_client *client, - int reg, uint8_t val) +static inline struct tps6586x *dev_to_tps6586x(struct device *dev) { - int ret; - - ret = i2c_smbus_write_byte_data(client, reg, val); - if (ret < 0) { - dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n", - val, reg); - return ret; - } - - return 0; -} - -static inline int __tps6586x_writes(struct i2c_client *client, int reg, - int len, uint8_t *val) -{ - int ret, i; - - for (i = 0; i < len; i++) { - ret = __tps6586x_write(client, reg + i, *(val + i)); - if (ret < 0) - return ret; - } - - return 0; + return i2c_get_clientdata(to_i2c_client(dev)); } int tps6586x_write(struct device *dev, int reg, uint8_t val) { - return __tps6586x_write(to_i2c_client(dev), reg, val); + struct tps6586x *tps6586x = dev_to_tps6586x(dev); + + return regmap_write(tps6586x->regmap, reg, val); } EXPORT_SYMBOL_GPL(tps6586x_write); int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val) { - return __tps6586x_writes(to_i2c_client(dev), reg, len, val); + struct tps6586x *tps6586x = dev_to_tps6586x(dev); + + return regmap_bulk_write(tps6586x->regmap, reg, val, len); } EXPORT_SYMBOL_GPL(tps6586x_writes); int tps6586x_read(struct device *dev, int reg, uint8_t *val) { - return __tps6586x_read(to_i2c_client(dev), reg, val); + struct tps6586x *tps6586x = dev_to_tps6586x(dev); + unsigned int rval; + int ret; + + ret = regmap_read(tps6586x->regmap, reg, &rval); + if (!ret) + *val = rval; + return ret; } EXPORT_SYMBOL_GPL(tps6586x_read); int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val) { - return __tps6586x_reads(to_i2c_client(dev), reg, len, val); + struct tps6586x *tps6586x = dev_to_tps6586x(dev); + + return regmap_bulk_read(tps6586x->regmap, reg, val, len); } EXPORT_SYMBOL_GPL(tps6586x_reads); int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask) { - struct tps6586x *tps6586x = dev_get_drvdata(dev); - uint8_t reg_val; - int ret = 0; - - mutex_lock(&tps6586x->lock); + struct tps6586x *tps6586x = dev_to_tps6586x(dev); - ret = __tps6586x_read(to_i2c_client(dev), reg, ®_val); - if (ret) - goto out; - - if ((reg_val & bit_mask) != bit_mask) { - reg_val |= bit_mask; - ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val); - } -out: - mutex_unlock(&tps6586x->lock); - return ret; + return regmap_update_bits(tps6586x->regmap, reg, bit_mask, bit_mask); } EXPORT_SYMBOL_GPL(tps6586x_set_bits); int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask) { - struct tps6586x *tps6586x = dev_get_drvdata(dev); - uint8_t reg_val; - int ret = 0; - - mutex_lock(&tps6586x->lock); + struct tps6586x *tps6586x = dev_to_tps6586x(dev); - ret = __tps6586x_read(to_i2c_client(dev), reg, ®_val); - if (ret) - goto out; - - if (reg_val & bit_mask) { - reg_val &= ~bit_mask; - ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val); - } -out: - mutex_unlock(&tps6586x->lock); - return ret; + return regmap_update_bits(tps6586x->regmap, reg, bit_mask, 0); } EXPORT_SYMBOL_GPL(tps6586x_clr_bits); int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask) { - struct tps6586x *tps6586x = dev_get_drvdata(dev); - uint8_t reg_val; - int ret = 0; - - mutex_lock(&tps6586x->lock); + struct tps6586x *tps6586x = dev_to_tps6586x(dev); - ret = __tps6586x_read(tps6586x->client, reg, ®_val); - if (ret) - goto out; - - if ((reg_val & mask) != val) { - reg_val = (reg_val & ~mask) | val; - ret = __tps6586x_write(tps6586x->client, reg, reg_val); - } -out: - mutex_unlock(&tps6586x->lock); - return ret; + return regmap_update_bits(tps6586x->regmap, reg, mask, val); } EXPORT_SYMBOL_GPL(tps6586x_update); -static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset) -{ - struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio); - uint8_t val; - int ret; - - ret = __tps6586x_read(tps6586x->client, TPS6586X_GPIOSET2, &val); - if (ret) - return ret; - - return !!(val & (1 << offset)); -} - - -static void tps6586x_gpio_set(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio); - - tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET2, - value << offset, 1 << offset); -} - -static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, - int value) -{ - struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio); - uint8_t val, mask; - - tps6586x_gpio_set(gc, offset, value); - - val = 0x1 << (offset * 2); - mask = 0x3 << (offset * 2); - - return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask); -} - -static int tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base) -{ - if (!gpio_base) - return 0; - - tps6586x->gpio.owner = THIS_MODULE; - tps6586x->gpio.label = tps6586x->client->name; - tps6586x->gpio.dev = tps6586x->dev; - tps6586x->gpio.base = gpio_base; - tps6586x->gpio.ngpio = 4; - tps6586x->gpio.can_sleep = 1; - - /* FIXME: add handling of GPIOs as dedicated inputs */ - tps6586x->gpio.direction_output = tps6586x_gpio_output; - tps6586x->gpio.set = tps6586x_gpio_set; - tps6586x->gpio.get = tps6586x_gpio_get; - - return gpiochip_add(&tps6586x->gpio); -} - static int __remove_subdev(struct device *dev, void *unused) { platform_device_unregister(to_platform_device(dev)); @@ -354,12 +224,11 @@ static void tps6586x_irq_sync_unlock(struct irq_data *data) int i; for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) { - if (tps6586x->mask_reg[i] != tps6586x->mask_cache[i]) { - if (!WARN_ON(tps6586x_write(tps6586x->dev, - TPS6586X_INT_MASK1 + i, - tps6586x->mask_reg[i]))) - tps6586x->mask_cache[i] = tps6586x->mask_reg[i]; - } + int ret; + ret = tps6586x_write(tps6586x->dev, + TPS6586X_INT_MASK1 + i, + tps6586x->mask_reg[i]); + WARN_ON(ret); } mutex_unlock(&tps6586x->irq_lock); @@ -406,7 +275,6 @@ static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq, mutex_init(&tps6586x->irq_lock); for (i = 0; i < 5; i++) { - tps6586x->mask_cache[i] = 0xff; tps6586x->mask_reg[i] = 0xff; tps6586x_write(tps6586x->dev, TPS6586X_INT_MASK1 + i, 0xff); } @@ -556,6 +424,23 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien } #endif +static bool is_volatile_reg(struct device *dev, unsigned int reg) +{ + /* Cache all interrupt mask register */ + if ((reg >= TPS6586X_INT_MASK1) && (reg <= TPS6586X_INT_MASK5)) + return false; + + return true; +} + +static const struct regmap_config tps6586x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = TPS6586X_MAX_REGISTER - 1, + .volatile_reg = is_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + static int __devinit tps6586x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -579,29 +464,39 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client, dev_info(&client->dev, "VERSIONCRC is %02x\n", ret); - tps6586x = kzalloc(sizeof(struct tps6586x), GFP_KERNEL); - if (tps6586x == NULL) + tps6586x = devm_kzalloc(&client->dev, sizeof(*tps6586x), GFP_KERNEL); + if (tps6586x == NULL) { + dev_err(&client->dev, "memory for tps6586x alloc failed\n"); return -ENOMEM; + } tps6586x->client = client; tps6586x->dev = &client->dev; i2c_set_clientdata(client, tps6586x); - mutex_init(&tps6586x->lock); + tps6586x->regmap = devm_regmap_init_i2c(client, + &tps6586x_regmap_config); + if (IS_ERR(tps6586x->regmap)) { + ret = PTR_ERR(tps6586x->regmap); + dev_err(&client->dev, "regmap init failed: %d\n", ret); + return ret; + } + if (client->irq) { ret = tps6586x_irq_init(tps6586x, client->irq, pdata->irq_base); if (ret) { dev_err(&client->dev, "IRQ init failed: %d\n", ret); - goto err_irq_init; + return ret; } } - ret = tps6586x_gpio_init(tps6586x, pdata->gpio_base); - if (ret) { - dev_err(&client->dev, "GPIO registration failed: %d\n", ret); - goto err_gpio_init; + ret = mfd_add_devices(tps6586x->dev, -1, + tps6586x_cell, ARRAY_SIZE(tps6586x_cell), NULL, 0); + if (ret < 0) { + dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret); + goto err_mfd_add; } ret = tps6586x_add_subdevs(tps6586x, pdata); @@ -613,38 +508,21 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client, return 0; err_add_devs: - if (pdata->gpio_base) { - ret = gpiochip_remove(&tps6586x->gpio); - if (ret) - dev_err(&client->dev, "Can't remove gpio chip: %d\n", - ret); - } -err_gpio_init: + mfd_remove_devices(tps6586x->dev); +err_mfd_add: if (client->irq) free_irq(client->irq, tps6586x); -err_irq_init: - kfree(tps6586x); return ret; } static int __devexit tps6586x_i2c_remove(struct i2c_client *client) { struct tps6586x *tps6586x = i2c_get_clientdata(client); - struct tps6586x_platform_data *pdata = client->dev.platform_data; - int ret; + tps6586x_remove_subdevs(tps6586x); + mfd_remove_devices(tps6586x->dev); if (client->irq) free_irq(client->irq, tps6586x); - - if (pdata->gpio_base) { - ret = gpiochip_remove(&tps6586x->gpio); - if (ret) - dev_err(&client->dev, "Can't remove gpio chip: %d\n", - ret); - } - - tps6586x_remove_subdevs(tps6586x); - kfree(tps6586x); return 0; } diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index be9e07b77325..1c563792c777 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -68,6 +68,24 @@ static const struct regmap_config tps65910_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +static int __devinit tps65910_ck32k_init(struct tps65910 *tps65910, + struct tps65910_board *pmic_pdata) +{ + int ret; + + if (!pmic_pdata->en_ck32k_xtal) + return 0; + + ret = tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL, + DEVCTRL_CK32K_CTRL_MASK); + if (ret < 0) { + dev_err(tps65910->dev, "clear ck32k_ctrl failed: %d\n", ret); + return ret; + } + + return 0; +} + static int __devinit tps65910_sleepinit(struct tps65910 *tps65910, struct tps65910_board *pmic_pdata) { @@ -175,6 +193,9 @@ static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, else if (*chip_id == TPS65911) dev_warn(&client->dev, "VMBCH2-Threshold not specified"); + prop = of_property_read_bool(np, "ti,en-ck32k-xtal"); + board_info->en_ck32k_xtal = prop; + board_info->irq = client->irq; board_info->irq_base = -1; @@ -243,7 +264,7 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c, init_data->irq_base = pmic_plat_data->irq_base; tps65910_irq_init(tps65910, init_data->irq, init_data); - + tps65910_ck32k_init(tps65910, pmic_plat_data); tps65910_sleepinit(tps65910, pmic_plat_data); return ret; diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 6fc90befa79e..1c32afed28aa 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -568,7 +568,6 @@ add_numbered_child(unsigned chip, const char *name, int num, goto err; } - device_init_wakeup(&pdev->dev, can_wakeup); pdev->dev.parent = &twl->client->dev; if (pdata) { @@ -593,6 +592,8 @@ add_numbered_child(unsigned chip, const char *name, int num, } status = platform_device_add(pdev); + if (status == 0) + device_init_wakeup(&pdev->dev, can_wakeup); err: if (status < 0) { @@ -716,8 +717,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, static struct regulator_consumer_supply usb1v8 = { .supply = "usb1v8", }; - static struct regulator_consumer_supply usb3v1 = { - .supply = "usb3v1", + static struct regulator_consumer_supply usb3v1[] = { + { .supply = "usb3v1" }, + { .supply = "bci3v1" }, }; /* First add the regulators so that they can be used by transceiver */ @@ -745,7 +747,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, return PTR_ERR(child); child = add_regulator_linked(TWL4030_REG_VUSB3V1, - &usb_fixed, &usb3v1, 1, + &usb_fixed, usb3v1, 2, features); if (IS_ERR(child)) return PTR_ERR(child); @@ -766,7 +768,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, if (twl_has_regulator() && child) { usb1v5.dev_name = dev_name(child); usb1v8.dev_name = dev_name(child); - usb3v1.dev_name = dev_name(child); + usb3v1[0].dev_name = dev_name(child); } } if (twl_has_usb() && pdata->usb && twl_class_is_6030()) { diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 4ded9e7aa246..b0fad0ffca56 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -64,19 +64,15 @@ int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg) int ret; unsigned int val; - mutex_lock(&twl6040->io_mutex); /* Vibra control registers from cache */ if (unlikely(reg == TWL6040_REG_VIBCTLL || reg == TWL6040_REG_VIBCTLR)) { val = twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)]; } else { ret = regmap_read(twl6040->regmap, reg, &val); - if (ret < 0) { - mutex_unlock(&twl6040->io_mutex); + if (ret < 0) return ret; - } } - mutex_unlock(&twl6040->io_mutex); return val; } @@ -86,12 +82,10 @@ int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val) { int ret; - mutex_lock(&twl6040->io_mutex); ret = regmap_write(twl6040->regmap, reg, val); /* Cache the vibra control registers */ if (reg == TWL6040_REG_VIBCTLL || reg == TWL6040_REG_VIBCTLR) twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)] = val; - mutex_unlock(&twl6040->io_mutex); return ret; } @@ -99,23 +93,13 @@ EXPORT_SYMBOL(twl6040_reg_write); int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) { - int ret; - - mutex_lock(&twl6040->io_mutex); - ret = regmap_update_bits(twl6040->regmap, reg, mask, mask); - mutex_unlock(&twl6040->io_mutex); - return ret; + return regmap_update_bits(twl6040->regmap, reg, mask, mask); } EXPORT_SYMBOL(twl6040_set_bits); int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) { - int ret; - - mutex_lock(&twl6040->io_mutex); - ret = regmap_update_bits(twl6040->regmap, reg, mask, 0); - mutex_unlock(&twl6040->io_mutex); - return ret; + return regmap_update_bits(twl6040->regmap, reg, mask, 0); } EXPORT_SYMBOL(twl6040_clear_bits); @@ -573,7 +557,6 @@ static int __devinit twl6040_probe(struct i2c_client *client, twl6040->irq = client->irq; mutex_init(&twl6040->mutex); - mutex_init(&twl6040->io_mutex); init_completion(&twl6040->ready); twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV); @@ -696,6 +679,7 @@ static int __devexit twl6040_remove(struct i2c_client *client) static const struct i2c_device_id twl6040_i2c_id[] = { { "twl6040", 0, }, + { "twl6041", 0, }, { }, }; MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id); diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c new file mode 100644 index 000000000000..01b9255ed631 --- /dev/null +++ b/drivers/mfd/wm5102-tables.c @@ -0,0 +1,2399 @@ +/* + * wm5102-tables.c -- WM5102 data tables + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/registers.h> + +#include "arizona.h" + +#define WM5102_NUM_AOD_ISR 2 +#define WM5102_NUM_ISR 5 + +static const struct reg_default wm5102_reva_patch[] = { + { 0x80, 0x0003 }, + { 0x221, 0x0090 }, + { 0x211, 0x0014 }, + { 0x212, 0x0000 }, + { 0x214, 0x000C }, + { 0x171, 0x0002 }, + { 0x171, 0x0000 }, + { 0x461, 0x8000 }, + { 0x463, 0x50F0 }, + { 0x465, 0x4820 }, + { 0x467, 0x4040 }, + { 0x469, 0x3940 }, + { 0x46B, 0x3310 }, + { 0x46D, 0x2D80 }, + { 0x46F, 0x2890 }, + { 0x471, 0x1990 }, + { 0x473, 0x1450 }, + { 0x475, 0x1020 }, + { 0x477, 0x0CD0 }, + { 0x479, 0x0A30 }, + { 0x47B, 0x0810 }, + { 0x47D, 0x0510 }, + { 0x500, 0x000D }, + { 0x507, 0x1820 }, + { 0x508, 0x1820 }, + { 0x540, 0x000D }, + { 0x547, 0x1820 }, + { 0x548, 0x1820 }, + { 0x580, 0x000D }, + { 0x587, 0x1820 }, + { 0x588, 0x1820 }, + { 0x101, 0x8140 }, + { 0x3000, 0x2225 }, + { 0x3001, 0x3a03 }, + { 0x3002, 0x0225 }, + { 0x3003, 0x0801 }, + { 0x3004, 0x6249 }, + { 0x3005, 0x0c04 }, + { 0x3006, 0x0225 }, + { 0x3007, 0x5901 }, + { 0x3008, 0xe249 }, + { 0x3009, 0x030d }, + { 0x300a, 0x0249 }, + { 0x300b, 0x2c01 }, + { 0x300c, 0xe249 }, + { 0x300d, 0x4342 }, + { 0x300e, 0xe249 }, + { 0x300f, 0x73c0 }, + { 0x3010, 0x4249 }, + { 0x3011, 0x0c00 }, + { 0x3012, 0x0225 }, + { 0x3013, 0x1f01 }, + { 0x3014, 0x0225 }, + { 0x3015, 0x1e01 }, + { 0x3016, 0x0225 }, + { 0x3017, 0xfa00 }, + { 0x3018, 0x0000 }, + { 0x3019, 0xf000 }, + { 0x301a, 0x0000 }, + { 0x301b, 0xf000 }, + { 0x301c, 0x0000 }, + { 0x301d, 0xf000 }, + { 0x301e, 0x0000 }, + { 0x301f, 0xf000 }, + { 0x3020, 0x0000 }, + { 0x3021, 0xf000 }, + { 0x3022, 0x0000 }, + { 0x3023, 0xf000 }, + { 0x3024, 0x0000 }, + { 0x3025, 0xf000 }, + { 0x3026, 0x0000 }, + { 0x3027, 0xf000 }, + { 0x3028, 0x0000 }, + { 0x3029, 0xf000 }, + { 0x302a, 0x0000 }, + { 0x302b, 0xf000 }, + { 0x302c, 0x0000 }, + { 0x302d, 0xf000 }, + { 0x302e, 0x0000 }, + { 0x302f, 0xf000 }, + { 0x3030, 0x0225 }, + { 0x3031, 0x1a01 }, + { 0x3032, 0x0225 }, + { 0x3033, 0x1e00 }, + { 0x3034, 0x0225 }, + { 0x3035, 0x1f00 }, + { 0x3036, 0x6225 }, + { 0x3037, 0xf800 }, + { 0x3038, 0x0000 }, + { 0x3039, 0xf000 }, + { 0x303a, 0x0000 }, + { 0x303b, 0xf000 }, + { 0x303c, 0x0000 }, + { 0x303d, 0xf000 }, + { 0x303e, 0x0000 }, + { 0x303f, 0xf000 }, + { 0x3040, 0x2226 }, + { 0x3041, 0x3a03 }, + { 0x3042, 0x0226 }, + { 0x3043, 0x0801 }, + { 0x3044, 0x6249 }, + { 0x3045, 0x0c06 }, + { 0x3046, 0x0226 }, + { 0x3047, 0x5901 }, + { 0x3048, 0xe249 }, + { 0x3049, 0x030d }, + { 0x304a, 0x0249 }, + { 0x304b, 0x2c01 }, + { 0x304c, 0xe249 }, + { 0x304d, 0x4342 }, + { 0x304e, 0xe249 }, + { 0x304f, 0x73c0 }, + { 0x3050, 0x4249 }, + { 0x3051, 0x0c00 }, + { 0x3052, 0x0226 }, + { 0x3053, 0x1f01 }, + { 0x3054, 0x0226 }, + { 0x3055, 0x1e01 }, + { 0x3056, 0x0226 }, + { 0x3057, 0xfa00 }, + { 0x3058, 0x0000 }, + { 0x3059, 0xf000 }, + { 0x305a, 0x0000 }, + { 0x305b, 0xf000 }, + { 0x305c, 0x0000 }, + { 0x305d, 0xf000 }, + { 0x305e, 0x0000 }, + { 0x305f, 0xf000 }, + { 0x3060, 0x0000 }, + { 0x3061, 0xf000 }, + { 0x3062, 0x0000 }, + { 0x3063, 0xf000 }, + { 0x3064, 0x0000 }, + { 0x3065, 0xf000 }, + { 0x3066, 0x0000 }, + { 0x3067, 0xf000 }, + { 0x3068, 0x0000 }, + { 0x3069, 0xf000 }, + { 0x306a, 0x0000 }, + { 0x306b, 0xf000 }, + { 0x306c, 0x0000 }, + { 0x306d, 0xf000 }, + { 0x306e, 0x0000 }, + { 0x306f, 0xf000 }, + { 0x3070, 0x0226 }, + { 0x3071, 0x1a01 }, + { 0x3072, 0x0226 }, + { 0x3073, 0x1e00 }, + { 0x3074, 0x0226 }, + { 0x3075, 0x1f00 }, + { 0x3076, 0x6226 }, + { 0x3077, 0xf800 }, + { 0x3078, 0x0000 }, + { 0x3079, 0xf000 }, + { 0x307a, 0x0000 }, + { 0x307b, 0xf000 }, + { 0x307c, 0x0000 }, + { 0x307d, 0xf000 }, + { 0x307e, 0x0000 }, + { 0x307f, 0xf000 }, + { 0x3080, 0x2227 }, + { 0x3081, 0x3a03 }, + { 0x3082, 0x0227 }, + { 0x3083, 0x0801 }, + { 0x3084, 0x6255 }, + { 0x3085, 0x0c04 }, + { 0x3086, 0x0227 }, + { 0x3087, 0x5901 }, + { 0x3088, 0xe255 }, + { 0x3089, 0x030d }, + { 0x308a, 0x0255 }, + { 0x308b, 0x2c01 }, + { 0x308c, 0xe255 }, + { 0x308d, 0x4342 }, + { 0x308e, 0xe255 }, + { 0x308f, 0x73c0 }, + { 0x3090, 0x4255 }, + { 0x3091, 0x0c00 }, + { 0x3092, 0x0227 }, + { 0x3093, 0x1f01 }, + { 0x3094, 0x0227 }, + { 0x3095, 0x1e01 }, + { 0x3096, 0x0227 }, + { 0x3097, 0xfa00 }, + { 0x3098, 0x0000 }, + { 0x3099, 0xf000 }, + { 0x309a, 0x0000 }, + { 0x309b, 0xf000 }, + { 0x309c, 0x0000 }, + { 0x309d, 0xf000 }, + { 0x309e, 0x0000 }, + { 0x309f, 0xf000 }, + { 0x30a0, 0x0000 }, + { 0x30a1, 0xf000 }, + { 0x30a2, 0x0000 }, + { 0x30a3, 0xf000 }, + { 0x30a4, 0x0000 }, + { 0x30a5, 0xf000 }, + { 0x30a6, 0x0000 }, + { 0x30a7, 0xf000 }, + { 0x30a8, 0x0000 }, + { 0x30a9, 0xf000 }, + { 0x30aa, 0x0000 }, + { 0x30ab, 0xf000 }, + { 0x30ac, 0x0000 }, + { 0x30ad, 0xf000 }, + { 0x30ae, 0x0000 }, + { 0x30af, 0xf000 }, + { 0x30b0, 0x0227 }, + { 0x30b1, 0x1a01 }, + { 0x30b2, 0x0227 }, + { 0x30b3, 0x1e00 }, + { 0x30b4, 0x0227 }, + { 0x30b5, 0x1f00 }, + { 0x30b6, 0x6227 }, + { 0x30b7, 0xf800 }, + { 0x30b8, 0x0000 }, + { 0x30b9, 0xf000 }, + { 0x30ba, 0x0000 }, + { 0x30bb, 0xf000 }, + { 0x30bc, 0x0000 }, + { 0x30bd, 0xf000 }, + { 0x30be, 0x0000 }, + { 0x30bf, 0xf000 }, + { 0x30c0, 0x2228 }, + { 0x30c1, 0x3a03 }, + { 0x30c2, 0x0228 }, + { 0x30c3, 0x0801 }, + { 0x30c4, 0x6255 }, + { 0x30c5, 0x0c06 }, + { 0x30c6, 0x0228 }, + { 0x30c7, 0x5901 }, + { 0x30c8, 0xe255 }, + { 0x30c9, 0x030d }, + { 0x30ca, 0x0255 }, + { 0x30cb, 0x2c01 }, + { 0x30cc, 0xe255 }, + { 0x30cd, 0x4342 }, + { 0x30ce, 0xe255 }, + { 0x30cf, 0x73c0 }, + { 0x30d0, 0x4255 }, + { 0x30d1, 0x0c00 }, + { 0x30d2, 0x0228 }, + { 0x30d3, 0x1f01 }, + { 0x30d4, 0x0228 }, + { 0x30d5, 0x1e01 }, + { 0x30d6, 0x0228 }, + { 0x30d7, 0xfa00 }, + { 0x30d8, 0x0000 }, + { 0x30d9, 0xf000 }, + { 0x30da, 0x0000 }, + { 0x30db, 0xf000 }, + { 0x30dc, 0x0000 }, + { 0x30dd, 0xf000 }, + { 0x30de, 0x0000 }, + { 0x30df, 0xf000 }, + { 0x30e0, 0x0000 }, + { 0x30e1, 0xf000 }, + { 0x30e2, 0x0000 }, + { 0x30e3, 0xf000 }, + { 0x30e4, 0x0000 }, + { 0x30e5, 0xf000 }, + { 0x30e6, 0x0000 }, + { 0x30e7, 0xf000 }, + { 0x30e8, 0x0000 }, + { 0x30e9, 0xf000 }, + { 0x30ea, 0x0000 }, + { 0x30eb, 0xf000 }, + { 0x30ec, 0x0000 }, + { 0x30ed, 0xf000 }, + { 0x30ee, 0x0000 }, + { 0x30ef, 0xf000 }, + { 0x30f0, 0x0228 }, + { 0x30f1, 0x1a01 }, + { 0x30f2, 0x0228 }, + { 0x30f3, 0x1e00 }, + { 0x30f4, 0x0228 }, + { 0x30f5, 0x1f00 }, + { 0x30f6, 0x6228 }, + { 0x30f7, 0xf800 }, + { 0x30f8, 0x0000 }, + { 0x30f9, 0xf000 }, + { 0x30fa, 0x0000 }, + { 0x30fb, 0xf000 }, + { 0x30fc, 0x0000 }, + { 0x30fd, 0xf000 }, + { 0x30fe, 0x0000 }, + { 0x30ff, 0xf000 }, + { 0x3100, 0x222b }, + { 0x3101, 0x3a03 }, + { 0x3102, 0x222b }, + { 0x3103, 0x5803 }, + { 0x3104, 0xe26f }, + { 0x3105, 0x030d }, + { 0x3106, 0x626f }, + { 0x3107, 0x2c01 }, + { 0x3108, 0xe26f }, + { 0x3109, 0x4342 }, + { 0x310a, 0xe26f }, + { 0x310b, 0x73c0 }, + { 0x310c, 0x026f }, + { 0x310d, 0x0c00 }, + { 0x310e, 0x022b }, + { 0x310f, 0x1f01 }, + { 0x3110, 0x022b }, + { 0x3111, 0x1e01 }, + { 0x3112, 0x022b }, + { 0x3113, 0xfa00 }, + { 0x3114, 0x0000 }, + { 0x3115, 0xf000 }, + { 0x3116, 0x0000 }, + { 0x3117, 0xf000 }, + { 0x3118, 0x0000 }, + { 0x3119, 0xf000 }, + { 0x311a, 0x0000 }, + { 0x311b, 0xf000 }, + { 0x311c, 0x0000 }, + { 0x311d, 0xf000 }, + { 0x311e, 0x0000 }, + { 0x311f, 0xf000 }, + { 0x3120, 0x022b }, + { 0x3121, 0x0a01 }, + { 0x3122, 0x022b }, + { 0x3123, 0x1e00 }, + { 0x3124, 0x022b }, + { 0x3125, 0x1f00 }, + { 0x3126, 0x622b }, + { 0x3127, 0xf800 }, + { 0x3128, 0x0000 }, + { 0x3129, 0xf000 }, + { 0x312a, 0x0000 }, + { 0x312b, 0xf000 }, + { 0x312c, 0x0000 }, + { 0x312d, 0xf000 }, + { 0x312e, 0x0000 }, + { 0x312f, 0xf000 }, + { 0x3130, 0x0000 }, + { 0x3131, 0xf000 }, + { 0x3132, 0x0000 }, + { 0x3133, 0xf000 }, + { 0x3134, 0x0000 }, + { 0x3135, 0xf000 }, + { 0x3136, 0x0000 }, + { 0x3137, 0xf000 }, + { 0x3138, 0x0000 }, + { 0x3139, 0xf000 }, + { 0x313a, 0x0000 }, + { 0x313b, 0xf000 }, + { 0x313c, 0x0000 }, + { 0x313d, 0xf000 }, + { 0x313e, 0x0000 }, + { 0x313f, 0xf000 }, + { 0x3140, 0x0000 }, + { 0x3141, 0xf000 }, + { 0x3142, 0x0000 }, + { 0x3143, 0xf000 }, + { 0x3144, 0x0000 }, + { 0x3145, 0xf000 }, + { 0x3146, 0x0000 }, + { 0x3147, 0xf000 }, + { 0x3148, 0x0000 }, + { 0x3149, 0xf000 }, + { 0x314a, 0x0000 }, + { 0x314b, 0xf000 }, + { 0x314c, 0x0000 }, + { 0x314d, 0xf000 }, + { 0x314e, 0x0000 }, + { 0x314f, 0xf000 }, + { 0x3150, 0x0000 }, + { 0x3151, 0xf000 }, + { 0x3152, 0x0000 }, + { 0x3153, 0xf000 }, + { 0x3154, 0x0000 }, + { 0x3155, 0xf000 }, + { 0x3156, 0x0000 }, + { 0x3157, 0xf000 }, + { 0x3158, 0x0000 }, + { 0x3159, 0xf000 }, + { 0x315a, 0x0000 }, + { 0x315b, 0xf000 }, + { 0x315c, 0x0000 }, + { 0x315d, 0xf000 }, + { 0x315e, 0x0000 }, + { 0x315f, 0xf000 }, + { 0x3160, 0x0000 }, + { 0x3161, 0xf000 }, + { 0x3162, 0x0000 }, + { 0x3163, 0xf000 }, + { 0x3164, 0x0000 }, + { 0x3165, 0xf000 }, + { 0x3166, 0x0000 }, + { 0x3167, 0xf000 }, + { 0x3168, 0x0000 }, + { 0x3169, 0xf000 }, + { 0x316a, 0x0000 }, + { 0x316b, 0xf000 }, + { 0x316c, 0x0000 }, + { 0x316d, 0xf000 }, + { 0x316e, 0x0000 }, + { 0x316f, 0xf000 }, + { 0x3170, 0x0000 }, + { 0x3171, 0xf000 }, + { 0x3172, 0x0000 }, + { 0x3173, 0xf000 }, + { 0x3174, 0x0000 }, + { 0x3175, 0xf000 }, + { 0x3176, 0x0000 }, + { 0x3177, 0xf000 }, + { 0x3178, 0x0000 }, + { 0x3179, 0xf000 }, + { 0x317a, 0x0000 }, + { 0x317b, 0xf000 }, + { 0x317c, 0x0000 }, + { 0x317d, 0xf000 }, + { 0x317e, 0x0000 }, + { 0x317f, 0xf000 }, + { 0x3180, 0x2001 }, + { 0x3181, 0xf101 }, + { 0x3182, 0x0000 }, + { 0x3183, 0xf000 }, + { 0x3184, 0x0000 }, + { 0x3185, 0xf000 }, + { 0x3186, 0x0000 }, + { 0x3187, 0xf000 }, + { 0x3188, 0x0000 }, + { 0x3189, 0xf000 }, + { 0x318a, 0x0000 }, + { 0x318b, 0xf000 }, + { 0x318c, 0x0000 }, + { 0x318d, 0xf000 }, + { 0x318e, 0x0000 }, + { 0x318f, 0xf000 }, + { 0x3190, 0x0000 }, + { 0x3191, 0xf000 }, + { 0x3192, 0x0000 }, + { 0x3193, 0xf000 }, + { 0x3194, 0x0000 }, + { 0x3195, 0xf000 }, + { 0x3196, 0x0000 }, + { 0x3197, 0xf000 }, + { 0x3198, 0x0000 }, + { 0x3199, 0xf000 }, + { 0x319a, 0x0000 }, + { 0x319b, 0xf000 }, + { 0x319c, 0x0000 }, + { 0x319d, 0xf000 }, + { 0x319e, 0x0000 }, + { 0x319f, 0xf000 }, + { 0x31a0, 0x0000 }, + { 0x31a1, 0xf000 }, + { 0x31a2, 0x0000 }, + { 0x31a3, 0xf000 }, + { 0x31a4, 0x0000 }, + { 0x31a5, 0xf000 }, + { 0x31a6, 0x0000 }, + { 0x31a7, 0xf000 }, + { 0x31a8, 0x0000 }, + { 0x31a9, 0xf000 }, + { 0x31aa, 0x0000 }, + { 0x31ab, 0xf000 }, + { 0x31ac, 0x0000 }, + { 0x31ad, 0xf000 }, + { 0x31ae, 0x0000 }, + { 0x31af, 0xf000 }, + { 0x31b0, 0x0000 }, + { 0x31b1, 0xf000 }, + { 0x31b2, 0x0000 }, + { 0x31b3, 0xf000 }, + { 0x31b4, 0x0000 }, + { 0x31b5, 0xf000 }, + { 0x31b6, 0x0000 }, + { 0x31b7, 0xf000 }, + { 0x31b8, 0x0000 }, + { 0x31b9, 0xf000 }, + { 0x31ba, 0x0000 }, + { 0x31bb, 0xf000 }, + { 0x31bc, 0x0000 }, + { 0x31bd, 0xf000 }, + { 0x31be, 0x0000 }, + { 0x31bf, 0xf000 }, + { 0x31c0, 0x0000 }, + { 0x31c1, 0xf000 }, + { 0x31c2, 0x0000 }, + { 0x31c3, 0xf000 }, + { 0x31c4, 0x0000 }, + { 0x31c5, 0xf000 }, + { 0x31c6, 0x0000 }, + { 0x31c7, 0xf000 }, + { 0x31c8, 0x0000 }, + { 0x31c9, 0xf000 }, + { 0x31ca, 0x0000 }, + { 0x31cb, 0xf000 }, + { 0x31cc, 0x0000 }, + { 0x31cd, 0xf000 }, + { 0x31ce, 0x0000 }, + { 0x31cf, 0xf000 }, + { 0x31d0, 0x0000 }, + { 0x31d1, 0xf000 }, + { 0x31d2, 0x0000 }, + { 0x31d3, 0xf000 }, + { 0x31d4, 0x0000 }, + { 0x31d5, 0xf000 }, + { 0x31d6, 0x0000 }, + { 0x31d7, 0xf000 }, + { 0x31d8, 0x0000 }, + { 0x31d9, 0xf000 }, + { 0x31da, 0x0000 }, + { 0x31db, 0xf000 }, + { 0x31dc, 0x0000 }, + { 0x31dd, 0xf000 }, + { 0x31de, 0x0000 }, + { 0x31df, 0xf000 }, + { 0x31e0, 0x0000 }, + { 0x31e1, 0xf000 }, + { 0x31e2, 0x0000 }, + { 0x31e3, 0xf000 }, + { 0x31e4, 0x0000 }, + { 0x31e5, 0xf000 }, + { 0x31e6, 0x0000 }, + { 0x31e7, 0xf000 }, + { 0x31e8, 0x0000 }, + { 0x31e9, 0xf000 }, + { 0x31ea, 0x0000 }, + { 0x31eb, 0xf000 }, + { 0x31ec, 0x0000 }, + { 0x31ed, 0xf000 }, + { 0x31ee, 0x0000 }, + { 0x31ef, 0xf000 }, + { 0x31f0, 0x0000 }, + { 0x31f1, 0xf000 }, + { 0x31f2, 0x0000 }, + { 0x31f3, 0xf000 }, + { 0x31f4, 0x0000 }, + { 0x31f5, 0xf000 }, + { 0x31f6, 0x0000 }, + { 0x31f7, 0xf000 }, + { 0x31f8, 0x0000 }, + { 0x31f9, 0xf000 }, + { 0x31fa, 0x0000 }, + { 0x31fb, 0xf000 }, + { 0x31fc, 0x0000 }, + { 0x31fd, 0xf000 }, + { 0x31fe, 0x0000 }, + { 0x31ff, 0xf000 }, + { 0x024d, 0xff50 }, + { 0x0252, 0xff50 }, + { 0x0259, 0x0112 }, + { 0x025e, 0x0112 }, + { 0x101, 0x0304 }, + { 0x80, 0x0000 }, +}; + +/* We use a function so we can use ARRAY_SIZE() */ +int wm5102_patch(struct arizona *arizona) +{ + switch (arizona->rev) { + case 0: + return regmap_register_patch(arizona->regmap, + wm5102_reva_patch, + ARRAY_SIZE(wm5102_reva_patch)); + default: + return 0; + } +} + +static const struct regmap_irq wm5102_aod_irqs[ARIZONA_NUM_IRQ] = { + [ARIZONA_IRQ_GP5_FALL] = { .mask = ARIZONA_GP5_FALL_EINT1 }, + [ARIZONA_IRQ_GP5_RISE] = { .mask = ARIZONA_GP5_RISE_EINT1 }, + [ARIZONA_IRQ_JD_FALL] = { .mask = ARIZONA_JD1_FALL_EINT1 }, + [ARIZONA_IRQ_JD_RISE] = { .mask = ARIZONA_JD1_RISE_EINT1 }, +}; + +const struct regmap_irq_chip wm5102_aod = { + .name = "wm5102 AOD", + .status_base = ARIZONA_AOD_IRQ1, + .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1, + .ack_base = ARIZONA_AOD_IRQ1, + .wake_base = ARIZONA_WAKE_CONTROL, + .num_regs = 1, + .irqs = wm5102_aod_irqs, + .num_irqs = ARRAY_SIZE(wm5102_aod_irqs), +}; + +static const struct regmap_irq wm5102_irqs[ARIZONA_NUM_IRQ] = { + [ARIZONA_IRQ_GP4] = { .reg_offset = 0, .mask = ARIZONA_GP4_EINT1 }, + [ARIZONA_IRQ_GP3] = { .reg_offset = 0, .mask = ARIZONA_GP3_EINT1 }, + [ARIZONA_IRQ_GP2] = { .reg_offset = 0, .mask = ARIZONA_GP2_EINT1 }, + [ARIZONA_IRQ_GP1] = { .reg_offset = 0, .mask = ARIZONA_GP1_EINT1 }, + + [ARIZONA_IRQ_DSP1_RAM_RDY] = { + .reg_offset = 1, .mask = ARIZONA_DSP1_RAM_RDY_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ2] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ2_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ1] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ1_EINT1 + }, + + [ARIZONA_IRQ_SPK_SHUTDOWN_WARN] = { + .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_WARN_EINT1 + }, + [ARIZONA_IRQ_SPK_SHUTDOWN] = { + .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_EINT1 + }, + [ARIZONA_IRQ_HPDET] = { + .reg_offset = 2, .mask = ARIZONA_HPDET_EINT1 + }, + [ARIZONA_IRQ_MICDET] = { + .reg_offset = 2, .mask = ARIZONA_MICDET_EINT1 + }, + [ARIZONA_IRQ_WSEQ_DONE] = { + .reg_offset = 2, .mask = ARIZONA_WSEQ_DONE_EINT1 + }, + [ARIZONA_IRQ_DRC2_SIG_DET] = { + .reg_offset = 2, .mask = ARIZONA_DRC2_SIG_DET_EINT1 + }, + [ARIZONA_IRQ_DRC1_SIG_DET] = { + .reg_offset = 2, .mask = ARIZONA_DRC1_SIG_DET_EINT1 + }, + [ARIZONA_IRQ_ASRC2_LOCK] = { + .reg_offset = 2, .mask = ARIZONA_ASRC2_LOCK_EINT1 + }, + [ARIZONA_IRQ_ASRC1_LOCK] = { + .reg_offset = 2, .mask = ARIZONA_ASRC1_LOCK_EINT1 + }, + [ARIZONA_IRQ_UNDERCLOCKED] = { + .reg_offset = 2, .mask = ARIZONA_UNDERCLOCKED_EINT1 + }, + [ARIZONA_IRQ_OVERCLOCKED] = { + .reg_offset = 2, .mask = ARIZONA_OVERCLOCKED_EINT1 + }, + [ARIZONA_IRQ_FLL2_LOCK] = { + .reg_offset = 2, .mask = ARIZONA_FLL2_LOCK_EINT1 + }, + [ARIZONA_IRQ_FLL1_LOCK] = { + .reg_offset = 2, .mask = ARIZONA_FLL1_LOCK_EINT1 + }, + [ARIZONA_IRQ_CLKGEN_ERR] = { + .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_EINT1 + }, + [ARIZONA_IRQ_CLKGEN_ERR_ASYNC] = { + .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_ASYNC_EINT1 + }, + + [ARIZONA_IRQ_ASRC_CFG_ERR] = { + .reg_offset = 3, .mask = ARIZONA_ASRC_CFG_ERR_EINT1 + }, + [ARIZONA_IRQ_AIF3_ERR] = { + .reg_offset = 3, .mask = ARIZONA_AIF3_ERR_EINT1 + }, + [ARIZONA_IRQ_AIF2_ERR] = { + .reg_offset = 3, .mask = ARIZONA_AIF2_ERR_EINT1 + }, + [ARIZONA_IRQ_AIF1_ERR] = { + .reg_offset = 3, .mask = ARIZONA_AIF1_ERR_EINT1 + }, + [ARIZONA_IRQ_CTRLIF_ERR] = { + .reg_offset = 3, .mask = ARIZONA_CTRLIF_ERR_EINT1 + }, + [ARIZONA_IRQ_MIXER_DROPPED_SAMPLES] = { + .reg_offset = 3, .mask = ARIZONA_MIXER_DROPPED_SAMPLE_EINT1 + }, + [ARIZONA_IRQ_ASYNC_CLK_ENA_LOW] = { + .reg_offset = 3, .mask = ARIZONA_ASYNC_CLK_ENA_LOW_EINT1 + }, + [ARIZONA_IRQ_SYSCLK_ENA_LOW] = { + .reg_offset = 3, .mask = ARIZONA_SYSCLK_ENA_LOW_EINT1 + }, + [ARIZONA_IRQ_ISRC1_CFG_ERR] = { + .reg_offset = 3, .mask = ARIZONA_ISRC1_CFG_ERR_EINT1 + }, + [ARIZONA_IRQ_ISRC2_CFG_ERR] = { + .reg_offset = 3, .mask = ARIZONA_ISRC2_CFG_ERR_EINT1 + }, + + [ARIZONA_IRQ_BOOT_DONE] = { + .reg_offset = 4, .mask = ARIZONA_BOOT_DONE_EINT1 + }, + [ARIZONA_IRQ_DCS_DAC_DONE] = { + .reg_offset = 4, .mask = ARIZONA_DCS_DAC_DONE_EINT1 + }, + [ARIZONA_IRQ_DCS_HP_DONE] = { + .reg_offset = 4, .mask = ARIZONA_DCS_HP_DONE_EINT1 + }, + [ARIZONA_IRQ_FLL2_CLOCK_OK] = { + .reg_offset = 4, .mask = ARIZONA_FLL2_CLOCK_OK_EINT1 + }, + [ARIZONA_IRQ_FLL1_CLOCK_OK] = { + .reg_offset = 4, .mask = ARIZONA_FLL1_CLOCK_OK_EINT1 + }, +}; + +const struct regmap_irq_chip wm5102_irq = { + .name = "wm5102 IRQ", + .status_base = ARIZONA_INTERRUPT_STATUS_1, + .mask_base = ARIZONA_INTERRUPT_STATUS_1_MASK, + .ack_base = ARIZONA_INTERRUPT_STATUS_1, + .num_regs = 5, + .irqs = wm5102_irqs, + .num_irqs = ARRAY_SIZE(wm5102_irqs), +}; + +static const struct reg_default wm5102_reg_default[] = { + { 0x00000008, 0x0019 }, /* R8 - Ctrl IF SPI CFG 1 */ + { 0x00000009, 0x0001 }, /* R9 - Ctrl IF I2C1 CFG 1 */ + { 0x0000000D, 0x0000 }, /* R13 - Ctrl IF Status 1 */ + { 0x00000016, 0x0000 }, /* R22 - Write Sequencer Ctrl 0 */ + { 0x00000017, 0x0000 }, /* R23 - Write Sequencer Ctrl 1 */ + { 0x00000018, 0x0000 }, /* R24 - Write Sequencer Ctrl 2 */ + { 0x0000001A, 0x0000 }, /* R26 - Write Sequencer PROM */ + { 0x00000020, 0x0000 }, /* R32 - Tone Generator 1 */ + { 0x00000021, 0x1000 }, /* R33 - Tone Generator 2 */ + { 0x00000022, 0x0000 }, /* R34 - Tone Generator 3 */ + { 0x00000023, 0x1000 }, /* R35 - Tone Generator 4 */ + { 0x00000024, 0x0000 }, /* R36 - Tone Generator 5 */ + { 0x00000030, 0x0000 }, /* R48 - PWM Drive 1 */ + { 0x00000031, 0x0100 }, /* R49 - PWM Drive 2 */ + { 0x00000032, 0x0100 }, /* R50 - PWM Drive 3 */ + { 0x00000040, 0x0000 }, /* R64 - Wake control */ + { 0x00000041, 0x0000 }, /* R65 - Sequence control */ + { 0x00000061, 0x01FF }, /* R97 - Sample Rate Sequence Select 1 */ + { 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */ + { 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */ + { 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */ + { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */ + { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */ + { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */ + { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */ + { 0x0000006C, 0x01FF }, /* R108 - Always On Triggers Sequence Select 5 */ + { 0x0000006D, 0x01FF }, /* R109 - Always On Triggers Sequence Select 6 */ + { 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */ + { 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */ + { 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */ + { 0x00000092, 0x0000 }, /* R146 - Haptics phase 1 intensity */ + { 0x00000093, 0x0000 }, /* R147 - Haptics phase 1 duration */ + { 0x00000094, 0x0000 }, /* R148 - Haptics phase 2 intensity */ + { 0x00000095, 0x0000 }, /* R149 - Haptics phase 2 duration */ + { 0x00000096, 0x0000 }, /* R150 - Haptics phase 3 intensity */ + { 0x00000097, 0x0000 }, /* R151 - Haptics phase 3 duration */ + { 0x00000100, 0x0001 }, /* R256 - Clock 32k 1 */ + { 0x00000101, 0x0304 }, /* R257 - System Clock 1 */ + { 0x00000102, 0x0011 }, /* R258 - Sample rate 1 */ + { 0x00000103, 0x0011 }, /* R259 - Sample rate 2 */ + { 0x00000104, 0x0011 }, /* R260 - Sample rate 3 */ + { 0x00000112, 0x0305 }, /* R274 - Async clock 1 */ + { 0x00000113, 0x0011 }, /* R275 - Async sample rate 1 */ + { 0x00000149, 0x0000 }, /* R329 - Output system clock */ + { 0x0000014A, 0x0000 }, /* R330 - Output async clock */ + { 0x00000152, 0x0000 }, /* R338 - Rate Estimator 1 */ + { 0x00000153, 0x0000 }, /* R339 - Rate Estimator 2 */ + { 0x00000154, 0x0000 }, /* R340 - Rate Estimator 3 */ + { 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */ + { 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */ + { 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */ + { 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */ + { 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */ + { 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */ + { 0x00000175, 0x0004 }, /* R373 - FLL1 Control 5 */ + { 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */ + { 0x00000177, 0x0181 }, /* R375 - FLL1 Loop Filter Test 1 */ + { 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */ + { 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */ + { 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */ + { 0x00000184, 0x0000 }, /* R388 - FLL1 Synchroniser 4 */ + { 0x00000185, 0x0000 }, /* R389 - FLL1 Synchroniser 5 */ + { 0x00000186, 0x0000 }, /* R390 - FLL1 Synchroniser 6 */ + { 0x00000189, 0x0000 }, /* R393 - FLL1 Spread Spectrum */ + { 0x0000018A, 0x0004 }, /* R394 - FLL1 GPIO Clock */ + { 0x00000191, 0x0000 }, /* R401 - FLL2 Control 1 */ + { 0x00000192, 0x0008 }, /* R402 - FLL2 Control 2 */ + { 0x00000193, 0x0018 }, /* R403 - FLL2 Control 3 */ + { 0x00000194, 0x007D }, /* R404 - FLL2 Control 4 */ + { 0x00000195, 0x0004 }, /* R405 - FLL2 Control 5 */ + { 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */ + { 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */ + { 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */ + { 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */ + { 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */ + { 0x000001A4, 0x0000 }, /* R420 - FLL2 Synchroniser 4 */ + { 0x000001A5, 0x0000 }, /* R421 - FLL2 Synchroniser 5 */ + { 0x000001A6, 0x0000 }, /* R422 - FLL2 Synchroniser 6 */ + { 0x000001A9, 0x0000 }, /* R425 - FLL2 Spread Spectrum */ + { 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */ + { 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */ + { 0x00000210, 0x00D4 }, /* R528 - LDO1 Control 1 */ + { 0x00000213, 0x0344 }, /* R531 - LDO2 Control 1 */ + { 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */ + { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */ + { 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */ + { 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */ + { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */ + { 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */ + { 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */ + { 0x000002A5, 0x0000 }, /* R677 - Mic Detect 3 */ + { 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */ + { 0x000002CB, 0x0000 }, /* R715 - Isolation control */ + { 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */ + { 0x00000300, 0x0000 }, /* R768 - Input Enables */ + { 0x00000308, 0x0000 }, /* R776 - Input Rate */ + { 0x00000309, 0x0022 }, /* R777 - Input Volume Ramp */ + { 0x00000310, 0x2080 }, /* R784 - IN1L Control */ + { 0x00000311, 0x0180 }, /* R785 - ADC Digital Volume 1L */ + { 0x00000312, 0x0000 }, /* R786 - DMIC1L Control */ + { 0x00000314, 0x0080 }, /* R788 - IN1R Control */ + { 0x00000315, 0x0180 }, /* R789 - ADC Digital Volume 1R */ + { 0x00000316, 0x0000 }, /* R790 - DMIC1R Control */ + { 0x00000318, 0x2080 }, /* R792 - IN2L Control */ + { 0x00000319, 0x0180 }, /* R793 - ADC Digital Volume 2L */ + { 0x0000031A, 0x0000 }, /* R794 - DMIC2L Control */ + { 0x0000031C, 0x0080 }, /* R796 - IN2R Control */ + { 0x0000031D, 0x0180 }, /* R797 - ADC Digital Volume 2R */ + { 0x0000031E, 0x0000 }, /* R798 - DMIC2R Control */ + { 0x00000320, 0x2080 }, /* R800 - IN3L Control */ + { 0x00000321, 0x0180 }, /* R801 - ADC Digital Volume 3L */ + { 0x00000322, 0x0000 }, /* R802 - DMIC3L Control */ + { 0x00000324, 0x0080 }, /* R804 - IN3R Control */ + { 0x00000325, 0x0180 }, /* R805 - ADC Digital Volume 3R */ + { 0x00000326, 0x0000 }, /* R806 - DMIC3R Control */ + { 0x00000400, 0x0000 }, /* R1024 - Output Enables 1 */ + { 0x00000408, 0x0000 }, /* R1032 - Output Rate 1 */ + { 0x00000409, 0x0022 }, /* R1033 - Output Volume Ramp */ + { 0x00000410, 0x0080 }, /* R1040 - Output Path Config 1L */ + { 0x00000411, 0x0180 }, /* R1041 - DAC Digital Volume 1L */ + { 0x00000412, 0x0080 }, /* R1042 - DAC Volume Limit 1L */ + { 0x00000413, 0x0001 }, /* R1043 - Noise Gate Select 1L */ + { 0x00000414, 0x0080 }, /* R1044 - Output Path Config 1R */ + { 0x00000415, 0x0180 }, /* R1045 - DAC Digital Volume 1R */ + { 0x00000416, 0x0080 }, /* R1046 - DAC Volume Limit 1R */ + { 0x00000417, 0x0002 }, /* R1047 - Noise Gate Select 1R */ + { 0x00000418, 0x0080 }, /* R1048 - Output Path Config 2L */ + { 0x00000419, 0x0180 }, /* R1049 - DAC Digital Volume 2L */ + { 0x0000041A, 0x0080 }, /* R1050 - DAC Volume Limit 2L */ + { 0x0000041B, 0x0004 }, /* R1051 - Noise Gate Select 2L */ + { 0x0000041C, 0x0080 }, /* R1052 - Output Path Config 2R */ + { 0x0000041D, 0x0180 }, /* R1053 - DAC Digital Volume 2R */ + { 0x0000041E, 0x0080 }, /* R1054 - DAC Volume Limit 2R */ + { 0x0000041F, 0x0008 }, /* R1055 - Noise Gate Select 2R */ + { 0x00000420, 0x0080 }, /* R1056 - Output Path Config 3L */ + { 0x00000421, 0x0180 }, /* R1057 - DAC Digital Volume 3L */ + { 0x00000422, 0x0080 }, /* R1058 - DAC Volume Limit 3L */ + { 0x00000423, 0x0010 }, /* R1059 - Noise Gate Select 3L */ + { 0x00000424, 0x0080 }, /* R1060 - Output Path Config 3R */ + { 0x00000425, 0x0180 }, /* R1061 - DAC Digital Volume 3R */ + { 0x00000426, 0x0080 }, /* R1062 - DAC Volume Limit 3R */ + { 0x00000428, 0x0000 }, /* R1064 - Output Path Config 4L */ + { 0x00000429, 0x0180 }, /* R1065 - DAC Digital Volume 4L */ + { 0x0000042A, 0x0080 }, /* R1066 - Out Volume 4L */ + { 0x0000042B, 0x0040 }, /* R1067 - Noise Gate Select 4L */ + { 0x0000042C, 0x0000 }, /* R1068 - Output Path Config 4R */ + { 0x0000042D, 0x0180 }, /* R1069 - DAC Digital Volume 4R */ + { 0x0000042E, 0x0080 }, /* R1070 - Out Volume 4R */ + { 0x0000042F, 0x0080 }, /* R1071 - Noise Gate Select 4R */ + { 0x00000430, 0x0000 }, /* R1072 - Output Path Config 5L */ + { 0x00000431, 0x0180 }, /* R1073 - DAC Digital Volume 5L */ + { 0x00000432, 0x0080 }, /* R1074 - DAC Volume Limit 5L */ + { 0x00000433, 0x0100 }, /* R1075 - Noise Gate Select 5L */ + { 0x00000434, 0x0000 }, /* R1076 - Output Path Config 5R */ + { 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */ + { 0x00000436, 0x0080 }, /* R1078 - DAC Volume Limit 5R */ + { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */ + { 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */ + { 0x00000458, 0x0001 }, /* R1112 - Noise Gate Control */ + { 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */ + { 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */ + { 0x000004DC, 0x0000 }, /* R1244 - DAC comp 1 */ + { 0x000004DD, 0x0000 }, /* R1245 - DAC comp 2 */ + { 0x000004DE, 0x0000 }, /* R1246 - DAC comp 3 */ + { 0x000004DF, 0x0000 }, /* R1247 - DAC comp 4 */ + { 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */ + { 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */ + { 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */ + { 0x00000503, 0x0000 }, /* R1283 - AIF1 Rate Ctrl */ + { 0x00000504, 0x0000 }, /* R1284 - AIF1 Format */ + { 0x00000505, 0x0040 }, /* R1285 - AIF1 Tx BCLK Rate */ + { 0x00000506, 0x0040 }, /* R1286 - AIF1 Rx BCLK Rate */ + { 0x00000507, 0x1818 }, /* R1287 - AIF1 Frame Ctrl 1 */ + { 0x00000508, 0x1818 }, /* R1288 - AIF1 Frame Ctrl 2 */ + { 0x00000509, 0x0000 }, /* R1289 - AIF1 Frame Ctrl 3 */ + { 0x0000050A, 0x0001 }, /* R1290 - AIF1 Frame Ctrl 4 */ + { 0x0000050B, 0x0002 }, /* R1291 - AIF1 Frame Ctrl 5 */ + { 0x0000050C, 0x0003 }, /* R1292 - AIF1 Frame Ctrl 6 */ + { 0x0000050D, 0x0004 }, /* R1293 - AIF1 Frame Ctrl 7 */ + { 0x0000050E, 0x0005 }, /* R1294 - AIF1 Frame Ctrl 8 */ + { 0x0000050F, 0x0006 }, /* R1295 - AIF1 Frame Ctrl 9 */ + { 0x00000510, 0x0007 }, /* R1296 - AIF1 Frame Ctrl 10 */ + { 0x00000511, 0x0000 }, /* R1297 - AIF1 Frame Ctrl 11 */ + { 0x00000512, 0x0001 }, /* R1298 - AIF1 Frame Ctrl 12 */ + { 0x00000513, 0x0002 }, /* R1299 - AIF1 Frame Ctrl 13 */ + { 0x00000514, 0x0003 }, /* R1300 - AIF1 Frame Ctrl 14 */ + { 0x00000515, 0x0004 }, /* R1301 - AIF1 Frame Ctrl 15 */ + { 0x00000516, 0x0005 }, /* R1302 - AIF1 Frame Ctrl 16 */ + { 0x00000517, 0x0006 }, /* R1303 - AIF1 Frame Ctrl 17 */ + { 0x00000518, 0x0007 }, /* R1304 - AIF1 Frame Ctrl 18 */ + { 0x00000519, 0x0000 }, /* R1305 - AIF1 Tx Enables */ + { 0x0000051A, 0x0000 }, /* R1306 - AIF1 Rx Enables */ + { 0x0000051B, 0x0000 }, /* R1307 - AIF1 Force Write */ + { 0x00000540, 0x000C }, /* R1344 - AIF2 BCLK Ctrl */ + { 0x00000541, 0x0008 }, /* R1345 - AIF2 Tx Pin Ctrl */ + { 0x00000542, 0x0000 }, /* R1346 - AIF2 Rx Pin Ctrl */ + { 0x00000543, 0x0000 }, /* R1347 - AIF2 Rate Ctrl */ + { 0x00000544, 0x0000 }, /* R1348 - AIF2 Format */ + { 0x00000545, 0x0040 }, /* R1349 - AIF2 Tx BCLK Rate */ + { 0x00000546, 0x0040 }, /* R1350 - AIF2 Rx BCLK Rate */ + { 0x00000547, 0x1818 }, /* R1351 - AIF2 Frame Ctrl 1 */ + { 0x00000548, 0x1818 }, /* R1352 - AIF2 Frame Ctrl 2 */ + { 0x00000549, 0x0000 }, /* R1353 - AIF2 Frame Ctrl 3 */ + { 0x0000054A, 0x0001 }, /* R1354 - AIF2 Frame Ctrl 4 */ + { 0x00000551, 0x0000 }, /* R1361 - AIF2 Frame Ctrl 11 */ + { 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */ + { 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */ + { 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */ + { 0x0000055B, 0x0000 }, /* R1371 - AIF2 Force Write */ + { 0x00000580, 0x000C }, /* R1408 - AIF3 BCLK Ctrl */ + { 0x00000581, 0x0008 }, /* R1409 - AIF3 Tx Pin Ctrl */ + { 0x00000582, 0x0000 }, /* R1410 - AIF3 Rx Pin Ctrl */ + { 0x00000583, 0x0000 }, /* R1411 - AIF3 Rate Ctrl */ + { 0x00000584, 0x0000 }, /* R1412 - AIF3 Format */ + { 0x00000585, 0x0040 }, /* R1413 - AIF3 Tx BCLK Rate */ + { 0x00000586, 0x0040 }, /* R1414 - AIF3 Rx BCLK Rate */ + { 0x00000587, 0x1818 }, /* R1415 - AIF3 Frame Ctrl 1 */ + { 0x00000588, 0x1818 }, /* R1416 - AIF3 Frame Ctrl 2 */ + { 0x00000589, 0x0000 }, /* R1417 - AIF3 Frame Ctrl 3 */ + { 0x0000058A, 0x0001 }, /* R1418 - AIF3 Frame Ctrl 4 */ + { 0x00000591, 0x0000 }, /* R1425 - AIF3 Frame Ctrl 11 */ + { 0x00000592, 0x0001 }, /* R1426 - AIF3 Frame Ctrl 12 */ + { 0x00000599, 0x0000 }, /* R1433 - AIF3 Tx Enables */ + { 0x0000059A, 0x0000 }, /* R1434 - AIF3 Rx Enables */ + { 0x0000059B, 0x0000 }, /* R1435 - AIF3 Force Write */ + { 0x000005E3, 0x0004 }, /* R1507 - SLIMbus Framer Ref Gear */ + { 0x000005E5, 0x0000 }, /* R1509 - SLIMbus Rates 1 */ + { 0x000005E6, 0x0000 }, /* R1510 - SLIMbus Rates 2 */ + { 0x000005E7, 0x0000 }, /* R1511 - SLIMbus Rates 3 */ + { 0x000005E8, 0x0000 }, /* R1512 - SLIMbus Rates 4 */ + { 0x000005E9, 0x0000 }, /* R1513 - SLIMbus Rates 5 */ + { 0x000005EA, 0x0000 }, /* R1514 - SLIMbus Rates 6 */ + { 0x000005EB, 0x0000 }, /* R1515 - SLIMbus Rates 7 */ + { 0x000005EC, 0x0000 }, /* R1516 - SLIMbus Rates 8 */ + { 0x000005F5, 0x0000 }, /* R1525 - SLIMbus RX Channel Enable */ + { 0x000005F6, 0x0000 }, /* R1526 - SLIMbus TX Channel Enable */ + { 0x00000640, 0x0000 }, /* R1600 - PWM1MIX Input 1 Source */ + { 0x00000641, 0x0080 }, /* R1601 - PWM1MIX Input 1 Volume */ + { 0x00000642, 0x0000 }, /* R1602 - PWM1MIX Input 2 Source */ + { 0x00000643, 0x0080 }, /* R1603 - PWM1MIX Input 2 Volume */ + { 0x00000644, 0x0000 }, /* R1604 - PWM1MIX Input 3 Source */ + { 0x00000645, 0x0080 }, /* R1605 - PWM1MIX Input 3 Volume */ + { 0x00000646, 0x0000 }, /* R1606 - PWM1MIX Input 4 Source */ + { 0x00000647, 0x0080 }, /* R1607 - PWM1MIX Input 4 Volume */ + { 0x00000648, 0x0000 }, /* R1608 - PWM2MIX Input 1 Source */ + { 0x00000649, 0x0080 }, /* R1609 - PWM2MIX Input 1 Volume */ + { 0x0000064A, 0x0000 }, /* R1610 - PWM2MIX Input 2 Source */ + { 0x0000064B, 0x0080 }, /* R1611 - PWM2MIX Input 2 Volume */ + { 0x0000064C, 0x0000 }, /* R1612 - PWM2MIX Input 3 Source */ + { 0x0000064D, 0x0080 }, /* R1613 - PWM2MIX Input 3 Volume */ + { 0x0000064E, 0x0000 }, /* R1614 - PWM2MIX Input 4 Source */ + { 0x0000064F, 0x0080 }, /* R1615 - PWM2MIX Input 4 Volume */ + { 0x00000660, 0x0000 }, /* R1632 - MICMIX Input 1 Source */ + { 0x00000661, 0x0080 }, /* R1633 - MICMIX Input 1 Volume */ + { 0x00000662, 0x0000 }, /* R1634 - MICMIX Input 2 Source */ + { 0x00000663, 0x0080 }, /* R1635 - MICMIX Input 2 Volume */ + { 0x00000664, 0x0000 }, /* R1636 - MICMIX Input 3 Source */ + { 0x00000665, 0x0080 }, /* R1637 - MICMIX Input 3 Volume */ + { 0x00000666, 0x0000 }, /* R1638 - MICMIX Input 4 Source */ + { 0x00000667, 0x0080 }, /* R1639 - MICMIX Input 4 Volume */ + { 0x00000668, 0x0000 }, /* R1640 - NOISEMIX Input 1 Source */ + { 0x00000669, 0x0080 }, /* R1641 - NOISEMIX Input 1 Volume */ + { 0x0000066A, 0x0000 }, /* R1642 - NOISEMIX Input 2 Source */ + { 0x0000066B, 0x0080 }, /* R1643 - NOISEMIX Input 2 Volume */ + { 0x0000066C, 0x0000 }, /* R1644 - NOISEMIX Input 3 Source */ + { 0x0000066D, 0x0080 }, /* R1645 - NOISEMIX Input 3 Volume */ + { 0x0000066E, 0x0000 }, /* R1646 - NOISEMIX Input 4 Source */ + { 0x0000066F, 0x0080 }, /* R1647 - NOISEMIX Input 4 Volume */ + { 0x00000680, 0x0000 }, /* R1664 - OUT1LMIX Input 1 Source */ + { 0x00000681, 0x0080 }, /* R1665 - OUT1LMIX Input 1 Volume */ + { 0x00000682, 0x0000 }, /* R1666 - OUT1LMIX Input 2 Source */ + { 0x00000683, 0x0080 }, /* R1667 - OUT1LMIX Input 2 Volume */ + { 0x00000684, 0x0000 }, /* R1668 - OUT1LMIX Input 3 Source */ + { 0x00000685, 0x0080 }, /* R1669 - OUT1LMIX Input 3 Volume */ + { 0x00000686, 0x0000 }, /* R1670 - OUT1LMIX Input 4 Source */ + { 0x00000687, 0x0080 }, /* R1671 - OUT1LMIX Input 4 Volume */ + { 0x00000688, 0x0000 }, /* R1672 - OUT1RMIX Input 1 Source */ + { 0x00000689, 0x0080 }, /* R1673 - OUT1RMIX Input 1 Volume */ + { 0x0000068A, 0x0000 }, /* R1674 - OUT1RMIX Input 2 Source */ + { 0x0000068B, 0x0080 }, /* R1675 - OUT1RMIX Input 2 Volume */ + { 0x0000068C, 0x0000 }, /* R1676 - OUT1RMIX Input 3 Source */ + { 0x0000068D, 0x0080 }, /* R1677 - OUT1RMIX Input 3 Volume */ + { 0x0000068E, 0x0000 }, /* R1678 - OUT1RMIX Input 4 Source */ + { 0x0000068F, 0x0080 }, /* R1679 - OUT1RMIX Input 4 Volume */ + { 0x00000690, 0x0000 }, /* R1680 - OUT2LMIX Input 1 Source */ + { 0x00000691, 0x0080 }, /* R1681 - OUT2LMIX Input 1 Volume */ + { 0x00000692, 0x0000 }, /* R1682 - OUT2LMIX Input 2 Source */ + { 0x00000693, 0x0080 }, /* R1683 - OUT2LMIX Input 2 Volume */ + { 0x00000694, 0x0000 }, /* R1684 - OUT2LMIX Input 3 Source */ + { 0x00000695, 0x0080 }, /* R1685 - OUT2LMIX Input 3 Volume */ + { 0x00000696, 0x0000 }, /* R1686 - OUT2LMIX Input 4 Source */ + { 0x00000697, 0x0080 }, /* R1687 - OUT2LMIX Input 4 Volume */ + { 0x00000698, 0x0000 }, /* R1688 - OUT2RMIX Input 1 Source */ + { 0x00000699, 0x0080 }, /* R1689 - OUT2RMIX Input 1 Volume */ + { 0x0000069A, 0x0000 }, /* R1690 - OUT2RMIX Input 2 Source */ + { 0x0000069B, 0x0080 }, /* R1691 - OUT2RMIX Input 2 Volume */ + { 0x0000069C, 0x0000 }, /* R1692 - OUT2RMIX Input 3 Source */ + { 0x0000069D, 0x0080 }, /* R1693 - OUT2RMIX Input 3 Volume */ + { 0x0000069E, 0x0000 }, /* R1694 - OUT2RMIX Input 4 Source */ + { 0x0000069F, 0x0080 }, /* R1695 - OUT2RMIX Input 4 Volume */ + { 0x000006A0, 0x0000 }, /* R1696 - OUT3LMIX Input 1 Source */ + { 0x000006A1, 0x0080 }, /* R1697 - OUT3LMIX Input 1 Volume */ + { 0x000006A2, 0x0000 }, /* R1698 - OUT3LMIX Input 2 Source */ + { 0x000006A3, 0x0080 }, /* R1699 - OUT3LMIX Input 2 Volume */ + { 0x000006A4, 0x0000 }, /* R1700 - OUT3LMIX Input 3 Source */ + { 0x000006A5, 0x0080 }, /* R1701 - OUT3LMIX Input 3 Volume */ + { 0x000006A6, 0x0000 }, /* R1702 - OUT3LMIX Input 4 Source */ + { 0x000006A7, 0x0080 }, /* R1703 - OUT3LMIX Input 4 Volume */ + { 0x000006B0, 0x0000 }, /* R1712 - OUT4LMIX Input 1 Source */ + { 0x000006B1, 0x0080 }, /* R1713 - OUT4LMIX Input 1 Volume */ + { 0x000006B2, 0x0000 }, /* R1714 - OUT4LMIX Input 2 Source */ + { 0x000006B3, 0x0080 }, /* R1715 - OUT4LMIX Input 2 Volume */ + { 0x000006B4, 0x0000 }, /* R1716 - OUT4LMIX Input 3 Source */ + { 0x000006B5, 0x0080 }, /* R1717 - OUT4LMIX Input 3 Volume */ + { 0x000006B6, 0x0000 }, /* R1718 - OUT4LMIX Input 4 Source */ + { 0x000006B7, 0x0080 }, /* R1719 - OUT4LMIX Input 4 Volume */ + { 0x000006B8, 0x0000 }, /* R1720 - OUT4RMIX Input 1 Source */ + { 0x000006B9, 0x0080 }, /* R1721 - OUT4RMIX Input 1 Volume */ + { 0x000006BA, 0x0000 }, /* R1722 - OUT4RMIX Input 2 Source */ + { 0x000006BB, 0x0080 }, /* R1723 - OUT4RMIX Input 2 Volume */ + { 0x000006BC, 0x0000 }, /* R1724 - OUT4RMIX Input 3 Source */ + { 0x000006BD, 0x0080 }, /* R1725 - OUT4RMIX Input 3 Volume */ + { 0x000006BE, 0x0000 }, /* R1726 - OUT4RMIX Input 4 Source */ + { 0x000006BF, 0x0080 }, /* R1727 - OUT4RMIX Input 4 Volume */ + { 0x000006C0, 0x0000 }, /* R1728 - OUT5LMIX Input 1 Source */ + { 0x000006C1, 0x0080 }, /* R1729 - OUT5LMIX Input 1 Volume */ + { 0x000006C2, 0x0000 }, /* R1730 - OUT5LMIX Input 2 Source */ + { 0x000006C3, 0x0080 }, /* R1731 - OUT5LMIX Input 2 Volume */ + { 0x000006C4, 0x0000 }, /* R1732 - OUT5LMIX Input 3 Source */ + { 0x000006C5, 0x0080 }, /* R1733 - OUT5LMIX Input 3 Volume */ + { 0x000006C6, 0x0000 }, /* R1734 - OUT5LMIX Input 4 Source */ + { 0x000006C7, 0x0080 }, /* R1735 - OUT5LMIX Input 4 Volume */ + { 0x000006C8, 0x0000 }, /* R1736 - OUT5RMIX Input 1 Source */ + { 0x000006C9, 0x0080 }, /* R1737 - OUT5RMIX Input 1 Volume */ + { 0x000006CA, 0x0000 }, /* R1738 - OUT5RMIX Input 2 Source */ + { 0x000006CB, 0x0080 }, /* R1739 - OUT5RMIX Input 2 Volume */ + { 0x000006CC, 0x0000 }, /* R1740 - OUT5RMIX Input 3 Source */ + { 0x000006CD, 0x0080 }, /* R1741 - OUT5RMIX Input 3 Volume */ + { 0x000006CE, 0x0000 }, /* R1742 - OUT5RMIX Input 4 Source */ + { 0x000006CF, 0x0080 }, /* R1743 - OUT5RMIX Input 4 Volume */ + { 0x00000700, 0x0000 }, /* R1792 - AIF1TX1MIX Input 1 Source */ + { 0x00000701, 0x0080 }, /* R1793 - AIF1TX1MIX Input 1 Volume */ + { 0x00000702, 0x0000 }, /* R1794 - AIF1TX1MIX Input 2 Source */ + { 0x00000703, 0x0080 }, /* R1795 - AIF1TX1MIX Input 2 Volume */ + { 0x00000704, 0x0000 }, /* R1796 - AIF1TX1MIX Input 3 Source */ + { 0x00000705, 0x0080 }, /* R1797 - AIF1TX1MIX Input 3 Volume */ + { 0x00000706, 0x0000 }, /* R1798 - AIF1TX1MIX Input 4 Source */ + { 0x00000707, 0x0080 }, /* R1799 - AIF1TX1MIX Input 4 Volume */ + { 0x00000708, 0x0000 }, /* R1800 - AIF1TX2MIX Input 1 Source */ + { 0x00000709, 0x0080 }, /* R1801 - AIF1TX2MIX Input 1 Volume */ + { 0x0000070A, 0x0000 }, /* R1802 - AIF1TX2MIX Input 2 Source */ + { 0x0000070B, 0x0080 }, /* R1803 - AIF1TX2MIX Input 2 Volume */ + { 0x0000070C, 0x0000 }, /* R1804 - AIF1TX2MIX Input 3 Source */ + { 0x0000070D, 0x0080 }, /* R1805 - AIF1TX2MIX Input 3 Volume */ + { 0x0000070E, 0x0000 }, /* R1806 - AIF1TX2MIX Input 4 Source */ + { 0x0000070F, 0x0080 }, /* R1807 - AIF1TX2MIX Input 4 Volume */ + { 0x00000710, 0x0000 }, /* R1808 - AIF1TX3MIX Input 1 Source */ + { 0x00000711, 0x0080 }, /* R1809 - AIF1TX3MIX Input 1 Volume */ + { 0x00000712, 0x0000 }, /* R1810 - AIF1TX3MIX Input 2 Source */ + { 0x00000713, 0x0080 }, /* R1811 - AIF1TX3MIX Input 2 Volume */ + { 0x00000714, 0x0000 }, /* R1812 - AIF1TX3MIX Input 3 Source */ + { 0x00000715, 0x0080 }, /* R1813 - AIF1TX3MIX Input 3 Volume */ + { 0x00000716, 0x0000 }, /* R1814 - AIF1TX3MIX Input 4 Source */ + { 0x00000717, 0x0080 }, /* R1815 - AIF1TX3MIX Input 4 Volume */ + { 0x00000718, 0x0000 }, /* R1816 - AIF1TX4MIX Input 1 Source */ + { 0x00000719, 0x0080 }, /* R1817 - AIF1TX4MIX Input 1 Volume */ + { 0x0000071A, 0x0000 }, /* R1818 - AIF1TX4MIX Input 2 Source */ + { 0x0000071B, 0x0080 }, /* R1819 - AIF1TX4MIX Input 2 Volume */ + { 0x0000071C, 0x0000 }, /* R1820 - AIF1TX4MIX Input 3 Source */ + { 0x0000071D, 0x0080 }, /* R1821 - AIF1TX4MIX Input 3 Volume */ + { 0x0000071E, 0x0000 }, /* R1822 - AIF1TX4MIX Input 4 Source */ + { 0x0000071F, 0x0080 }, /* R1823 - AIF1TX4MIX Input 4 Volume */ + { 0x00000720, 0x0000 }, /* R1824 - AIF1TX5MIX Input 1 Source */ + { 0x00000721, 0x0080 }, /* R1825 - AIF1TX5MIX Input 1 Volume */ + { 0x00000722, 0x0000 }, /* R1826 - AIF1TX5MIX Input 2 Source */ + { 0x00000723, 0x0080 }, /* R1827 - AIF1TX5MIX Input 2 Volume */ + { 0x00000724, 0x0000 }, /* R1828 - AIF1TX5MIX Input 3 Source */ + { 0x00000725, 0x0080 }, /* R1829 - AIF1TX5MIX Input 3 Volume */ + { 0x00000726, 0x0000 }, /* R1830 - AIF1TX5MIX Input 4 Source */ + { 0x00000727, 0x0080 }, /* R1831 - AIF1TX5MIX Input 4 Volume */ + { 0x00000728, 0x0000 }, /* R1832 - AIF1TX6MIX Input 1 Source */ + { 0x00000729, 0x0080 }, /* R1833 - AIF1TX6MIX Input 1 Volume */ + { 0x0000072A, 0x0000 }, /* R1834 - AIF1TX6MIX Input 2 Source */ + { 0x0000072B, 0x0080 }, /* R1835 - AIF1TX6MIX Input 2 Volume */ + { 0x0000072C, 0x0000 }, /* R1836 - AIF1TX6MIX Input 3 Source */ + { 0x0000072D, 0x0080 }, /* R1837 - AIF1TX6MIX Input 3 Volume */ + { 0x0000072E, 0x0000 }, /* R1838 - AIF1TX6MIX Input 4 Source */ + { 0x0000072F, 0x0080 }, /* R1839 - AIF1TX6MIX Input 4 Volume */ + { 0x00000730, 0x0000 }, /* R1840 - AIF1TX7MIX Input 1 Source */ + { 0x00000731, 0x0080 }, /* R1841 - AIF1TX7MIX Input 1 Volume */ + { 0x00000732, 0x0000 }, /* R1842 - AIF1TX7MIX Input 2 Source */ + { 0x00000733, 0x0080 }, /* R1843 - AIF1TX7MIX Input 2 Volume */ + { 0x00000734, 0x0000 }, /* R1844 - AIF1TX7MIX Input 3 Source */ + { 0x00000735, 0x0080 }, /* R1845 - AIF1TX7MIX Input 3 Volume */ + { 0x00000736, 0x0000 }, /* R1846 - AIF1TX7MIX Input 4 Source */ + { 0x00000737, 0x0080 }, /* R1847 - AIF1TX7MIX Input 4 Volume */ + { 0x00000738, 0x0000 }, /* R1848 - AIF1TX8MIX Input 1 Source */ + { 0x00000739, 0x0080 }, /* R1849 - AIF1TX8MIX Input 1 Volume */ + { 0x0000073A, 0x0000 }, /* R1850 - AIF1TX8MIX Input 2 Source */ + { 0x0000073B, 0x0080 }, /* R1851 - AIF1TX8MIX Input 2 Volume */ + { 0x0000073C, 0x0000 }, /* R1852 - AIF1TX8MIX Input 3 Source */ + { 0x0000073D, 0x0080 }, /* R1853 - AIF1TX8MIX Input 3 Volume */ + { 0x0000073E, 0x0000 }, /* R1854 - AIF1TX8MIX Input 4 Source */ + { 0x0000073F, 0x0080 }, /* R1855 - AIF1TX8MIX Input 4 Volume */ + { 0x00000740, 0x0000 }, /* R1856 - AIF2TX1MIX Input 1 Source */ + { 0x00000741, 0x0080 }, /* R1857 - AIF2TX1MIX Input 1 Volume */ + { 0x00000742, 0x0000 }, /* R1858 - AIF2TX1MIX Input 2 Source */ + { 0x00000743, 0x0080 }, /* R1859 - AIF2TX1MIX Input 2 Volume */ + { 0x00000744, 0x0000 }, /* R1860 - AIF2TX1MIX Input 3 Source */ + { 0x00000745, 0x0080 }, /* R1861 - AIF2TX1MIX Input 3 Volume */ + { 0x00000746, 0x0000 }, /* R1862 - AIF2TX1MIX Input 4 Source */ + { 0x00000747, 0x0080 }, /* R1863 - AIF2TX1MIX Input 4 Volume */ + { 0x00000748, 0x0000 }, /* R1864 - AIF2TX2MIX Input 1 Source */ + { 0x00000749, 0x0080 }, /* R1865 - AIF2TX2MIX Input 1 Volume */ + { 0x0000074A, 0x0000 }, /* R1866 - AIF2TX2MIX Input 2 Source */ + { 0x0000074B, 0x0080 }, /* R1867 - AIF2TX2MIX Input 2 Volume */ + { 0x0000074C, 0x0000 }, /* R1868 - AIF2TX2MIX Input 3 Source */ + { 0x0000074D, 0x0080 }, /* R1869 - AIF2TX2MIX Input 3 Volume */ + { 0x0000074E, 0x0000 }, /* R1870 - AIF2TX2MIX Input 4 Source */ + { 0x0000074F, 0x0080 }, /* R1871 - AIF2TX2MIX Input 4 Volume */ + { 0x00000780, 0x0000 }, /* R1920 - AIF3TX1MIX Input 1 Source */ + { 0x00000781, 0x0080 }, /* R1921 - AIF3TX1MIX Input 1 Volume */ + { 0x00000782, 0x0000 }, /* R1922 - AIF3TX1MIX Input 2 Source */ + { 0x00000783, 0x0080 }, /* R1923 - AIF3TX1MIX Input 2 Volume */ + { 0x00000784, 0x0000 }, /* R1924 - AIF3TX1MIX Input 3 Source */ + { 0x00000785, 0x0080 }, /* R1925 - AIF3TX1MIX Input 3 Volume */ + { 0x00000786, 0x0000 }, /* R1926 - AIF3TX1MIX Input 4 Source */ + { 0x00000787, 0x0080 }, /* R1927 - AIF3TX1MIX Input 4 Volume */ + { 0x00000788, 0x0000 }, /* R1928 - AIF3TX2MIX Input 1 Source */ + { 0x00000789, 0x0080 }, /* R1929 - AIF3TX2MIX Input 1 Volume */ + { 0x0000078A, 0x0000 }, /* R1930 - AIF3TX2MIX Input 2 Source */ + { 0x0000078B, 0x0080 }, /* R1931 - AIF3TX2MIX Input 2 Volume */ + { 0x0000078C, 0x0000 }, /* R1932 - AIF3TX2MIX Input 3 Source */ + { 0x0000078D, 0x0080 }, /* R1933 - AIF3TX2MIX Input 3 Volume */ + { 0x0000078E, 0x0000 }, /* R1934 - AIF3TX2MIX Input 4 Source */ + { 0x0000078F, 0x0080 }, /* R1935 - AIF3TX2MIX Input 4 Volume */ + { 0x000007C0, 0x0000 }, /* R1984 - SLIMTX1MIX Input 1 Source */ + { 0x000007C1, 0x0080 }, /* R1985 - SLIMTX1MIX Input 1 Volume */ + { 0x000007C2, 0x0000 }, /* R1986 - SLIMTX1MIX Input 2 Source */ + { 0x000007C3, 0x0080 }, /* R1987 - SLIMTX1MIX Input 2 Volume */ + { 0x000007C4, 0x0000 }, /* R1988 - SLIMTX1MIX Input 3 Source */ + { 0x000007C5, 0x0080 }, /* R1989 - SLIMTX1MIX Input 3 Volume */ + { 0x000007C6, 0x0000 }, /* R1990 - SLIMTX1MIX Input 4 Source */ + { 0x000007C7, 0x0080 }, /* R1991 - SLIMTX1MIX Input 4 Volume */ + { 0x000007C8, 0x0000 }, /* R1992 - SLIMTX2MIX Input 1 Source */ + { 0x000007C9, 0x0080 }, /* R1993 - SLIMTX2MIX Input 1 Volume */ + { 0x000007CA, 0x0000 }, /* R1994 - SLIMTX2MIX Input 2 Source */ + { 0x000007CB, 0x0080 }, /* R1995 - SLIMTX2MIX Input 2 Volume */ + { 0x000007CC, 0x0000 }, /* R1996 - SLIMTX2MIX Input 3 Source */ + { 0x000007CD, 0x0080 }, /* R1997 - SLIMTX2MIX Input 3 Volume */ + { 0x000007CE, 0x0000 }, /* R1998 - SLIMTX2MIX Input 4 Source */ + { 0x000007CF, 0x0080 }, /* R1999 - SLIMTX2MIX Input 4 Volume */ + { 0x000007D0, 0x0000 }, /* R2000 - SLIMTX3MIX Input 1 Source */ + { 0x000007D1, 0x0080 }, /* R2001 - SLIMTX3MIX Input 1 Volume */ + { 0x000007D2, 0x0000 }, /* R2002 - SLIMTX3MIX Input 2 Source */ + { 0x000007D3, 0x0080 }, /* R2003 - SLIMTX3MIX Input 2 Volume */ + { 0x000007D4, 0x0000 }, /* R2004 - SLIMTX3MIX Input 3 Source */ + { 0x000007D5, 0x0080 }, /* R2005 - SLIMTX3MIX Input 3 Volume */ + { 0x000007D6, 0x0000 }, /* R2006 - SLIMTX3MIX Input 4 Source */ + { 0x000007D7, 0x0080 }, /* R2007 - SLIMTX3MIX Input 4 Volume */ + { 0x000007D8, 0x0000 }, /* R2008 - SLIMTX4MIX Input 1 Source */ + { 0x000007D9, 0x0080 }, /* R2009 - SLIMTX4MIX Input 1 Volume */ + { 0x000007DA, 0x0000 }, /* R2010 - SLIMTX4MIX Input 2 Source */ + { 0x000007DB, 0x0080 }, /* R2011 - SLIMTX4MIX Input 2 Volume */ + { 0x000007DC, 0x0000 }, /* R2012 - SLIMTX4MIX Input 3 Source */ + { 0x000007DD, 0x0080 }, /* R2013 - SLIMTX4MIX Input 3 Volume */ + { 0x000007DE, 0x0000 }, /* R2014 - SLIMTX4MIX Input 4 Source */ + { 0x000007DF, 0x0080 }, /* R2015 - SLIMTX4MIX Input 4 Volume */ + { 0x000007E0, 0x0000 }, /* R2016 - SLIMTX5MIX Input 1 Source */ + { 0x000007E1, 0x0080 }, /* R2017 - SLIMTX5MIX Input 1 Volume */ + { 0x000007E2, 0x0000 }, /* R2018 - SLIMTX5MIX Input 2 Source */ + { 0x000007E3, 0x0080 }, /* R2019 - SLIMTX5MIX Input 2 Volume */ + { 0x000007E4, 0x0000 }, /* R2020 - SLIMTX5MIX Input 3 Source */ + { 0x000007E5, 0x0080 }, /* R2021 - SLIMTX5MIX Input 3 Volume */ + { 0x000007E6, 0x0000 }, /* R2022 - SLIMTX5MIX Input 4 Source */ + { 0x000007E7, 0x0080 }, /* R2023 - SLIMTX5MIX Input 4 Volume */ + { 0x000007E8, 0x0000 }, /* R2024 - SLIMTX6MIX Input 1 Source */ + { 0x000007E9, 0x0080 }, /* R2025 - SLIMTX6MIX Input 1 Volume */ + { 0x000007EA, 0x0000 }, /* R2026 - SLIMTX6MIX Input 2 Source */ + { 0x000007EB, 0x0080 }, /* R2027 - SLIMTX6MIX Input 2 Volume */ + { 0x000007EC, 0x0000 }, /* R2028 - SLIMTX6MIX Input 3 Source */ + { 0x000007ED, 0x0080 }, /* R2029 - SLIMTX6MIX Input 3 Volume */ + { 0x000007EE, 0x0000 }, /* R2030 - SLIMTX6MIX Input 4 Source */ + { 0x000007EF, 0x0080 }, /* R2031 - SLIMTX6MIX Input 4 Volume */ + { 0x000007F0, 0x0000 }, /* R2032 - SLIMTX7MIX Input 1 Source */ + { 0x000007F1, 0x0080 }, /* R2033 - SLIMTX7MIX Input 1 Volume */ + { 0x000007F2, 0x0000 }, /* R2034 - SLIMTX7MIX Input 2 Source */ + { 0x000007F3, 0x0080 }, /* R2035 - SLIMTX7MIX Input 2 Volume */ + { 0x000007F4, 0x0000 }, /* R2036 - SLIMTX7MIX Input 3 Source */ + { 0x000007F5, 0x0080 }, /* R2037 - SLIMTX7MIX Input 3 Volume */ + { 0x000007F6, 0x0000 }, /* R2038 - SLIMTX7MIX Input 4 Source */ + { 0x000007F7, 0x0080 }, /* R2039 - SLIMTX7MIX Input 4 Volume */ + { 0x000007F8, 0x0000 }, /* R2040 - SLIMTX8MIX Input 1 Source */ + { 0x000007F9, 0x0080 }, /* R2041 - SLIMTX8MIX Input 1 Volume */ + { 0x000007FA, 0x0000 }, /* R2042 - SLIMTX8MIX Input 2 Source */ + { 0x000007FB, 0x0080 }, /* R2043 - SLIMTX8MIX Input 2 Volume */ + { 0x000007FC, 0x0000 }, /* R2044 - SLIMTX8MIX Input 3 Source */ + { 0x000007FD, 0x0080 }, /* R2045 - SLIMTX8MIX Input 3 Volume */ + { 0x000007FE, 0x0000 }, /* R2046 - SLIMTX8MIX Input 4 Source */ + { 0x000007FF, 0x0080 }, /* R2047 - SLIMTX8MIX Input 4 Volume */ + { 0x00000880, 0x0000 }, /* R2176 - EQ1MIX Input 1 Source */ + { 0x00000881, 0x0080 }, /* R2177 - EQ1MIX Input 1 Volume */ + { 0x00000882, 0x0000 }, /* R2178 - EQ1MIX Input 2 Source */ + { 0x00000883, 0x0080 }, /* R2179 - EQ1MIX Input 2 Volume */ + { 0x00000884, 0x0000 }, /* R2180 - EQ1MIX Input 3 Source */ + { 0x00000885, 0x0080 }, /* R2181 - EQ1MIX Input 3 Volume */ + { 0x00000886, 0x0000 }, /* R2182 - EQ1MIX Input 4 Source */ + { 0x00000887, 0x0080 }, /* R2183 - EQ1MIX Input 4 Volume */ + { 0x00000888, 0x0000 }, /* R2184 - EQ2MIX Input 1 Source */ + { 0x00000889, 0x0080 }, /* R2185 - EQ2MIX Input 1 Volume */ + { 0x0000088A, 0x0000 }, /* R2186 - EQ2MIX Input 2 Source */ + { 0x0000088B, 0x0080 }, /* R2187 - EQ2MIX Input 2 Volume */ + { 0x0000088C, 0x0000 }, /* R2188 - EQ2MIX Input 3 Source */ + { 0x0000088D, 0x0080 }, /* R2189 - EQ2MIX Input 3 Volume */ + { 0x0000088E, 0x0000 }, /* R2190 - EQ2MIX Input 4 Source */ + { 0x0000088F, 0x0080 }, /* R2191 - EQ2MIX Input 4 Volume */ + { 0x00000890, 0x0000 }, /* R2192 - EQ3MIX Input 1 Source */ + { 0x00000891, 0x0080 }, /* R2193 - EQ3MIX Input 1 Volume */ + { 0x00000892, 0x0000 }, /* R2194 - EQ3MIX Input 2 Source */ + { 0x00000893, 0x0080 }, /* R2195 - EQ3MIX Input 2 Volume */ + { 0x00000894, 0x0000 }, /* R2196 - EQ3MIX Input 3 Source */ + { 0x00000895, 0x0080 }, /* R2197 - EQ3MIX Input 3 Volume */ + { 0x00000896, 0x0000 }, /* R2198 - EQ3MIX Input 4 Source */ + { 0x00000897, 0x0080 }, /* R2199 - EQ3MIX Input 4 Volume */ + { 0x00000898, 0x0000 }, /* R2200 - EQ4MIX Input 1 Source */ + { 0x00000899, 0x0080 }, /* R2201 - EQ4MIX Input 1 Volume */ + { 0x0000089A, 0x0000 }, /* R2202 - EQ4MIX Input 2 Source */ + { 0x0000089B, 0x0080 }, /* R2203 - EQ4MIX Input 2 Volume */ + { 0x0000089C, 0x0000 }, /* R2204 - EQ4MIX Input 3 Source */ + { 0x0000089D, 0x0080 }, /* R2205 - EQ4MIX Input 3 Volume */ + { 0x0000089E, 0x0000 }, /* R2206 - EQ4MIX Input 4 Source */ + { 0x0000089F, 0x0080 }, /* R2207 - EQ4MIX Input 4 Volume */ + { 0x000008C0, 0x0000 }, /* R2240 - DRC1LMIX Input 1 Source */ + { 0x000008C1, 0x0080 }, /* R2241 - DRC1LMIX Input 1 Volume */ + { 0x000008C2, 0x0000 }, /* R2242 - DRC1LMIX Input 2 Source */ + { 0x000008C3, 0x0080 }, /* R2243 - DRC1LMIX Input 2 Volume */ + { 0x000008C4, 0x0000 }, /* R2244 - DRC1LMIX Input 3 Source */ + { 0x000008C5, 0x0080 }, /* R2245 - DRC1LMIX Input 3 Volume */ + { 0x000008C6, 0x0000 }, /* R2246 - DRC1LMIX Input 4 Source */ + { 0x000008C7, 0x0080 }, /* R2247 - DRC1LMIX Input 4 Volume */ + { 0x000008C8, 0x0000 }, /* R2248 - DRC1RMIX Input 1 Source */ + { 0x000008C9, 0x0080 }, /* R2249 - DRC1RMIX Input 1 Volume */ + { 0x000008CA, 0x0000 }, /* R2250 - DRC1RMIX Input 2 Source */ + { 0x000008CB, 0x0080 }, /* R2251 - DRC1RMIX Input 2 Volume */ + { 0x000008CC, 0x0000 }, /* R2252 - DRC1RMIX Input 3 Source */ + { 0x000008CD, 0x0080 }, /* R2253 - DRC1RMIX Input 3 Volume */ + { 0x000008CE, 0x0000 }, /* R2254 - DRC1RMIX Input 4 Source */ + { 0x000008CF, 0x0080 }, /* R2255 - DRC1RMIX Input 4 Volume */ + { 0x000008D0, 0x0000 }, /* R2256 - DRC2LMIX Input 1 Source */ + { 0x000008D1, 0x0080 }, /* R2257 - DRC2LMIX Input 1 Volume */ + { 0x000008D2, 0x0000 }, /* R2258 - DRC2LMIX Input 2 Source */ + { 0x000008D3, 0x0080 }, /* R2259 - DRC2LMIX Input 2 Volume */ + { 0x000008D4, 0x0000 }, /* R2260 - DRC2LMIX Input 3 Source */ + { 0x000008D5, 0x0080 }, /* R2261 - DRC2LMIX Input 3 Volume */ + { 0x000008D6, 0x0000 }, /* R2262 - DRC2LMIX Input 4 Source */ + { 0x000008D7, 0x0080 }, /* R2263 - DRC2LMIX Input 4 Volume */ + { 0x000008D8, 0x0000 }, /* R2264 - DRC2RMIX Input 1 Source */ + { 0x000008D9, 0x0080 }, /* R2265 - DRC2RMIX Input 1 Volume */ + { 0x000008DA, 0x0000 }, /* R2266 - DRC2RMIX Input 2 Source */ + { 0x000008DB, 0x0080 }, /* R2267 - DRC2RMIX Input 2 Volume */ + { 0x000008DC, 0x0000 }, /* R2268 - DRC2RMIX Input 3 Source */ + { 0x000008DD, 0x0080 }, /* R2269 - DRC2RMIX Input 3 Volume */ + { 0x000008DE, 0x0000 }, /* R2270 - DRC2RMIX Input 4 Source */ + { 0x000008DF, 0x0080 }, /* R2271 - DRC2RMIX Input 4 Volume */ + { 0x00000900, 0x0000 }, /* R2304 - HPLP1MIX Input 1 Source */ + { 0x00000901, 0x0080 }, /* R2305 - HPLP1MIX Input 1 Volume */ + { 0x00000902, 0x0000 }, /* R2306 - HPLP1MIX Input 2 Source */ + { 0x00000903, 0x0080 }, /* R2307 - HPLP1MIX Input 2 Volume */ + { 0x00000904, 0x0000 }, /* R2308 - HPLP1MIX Input 3 Source */ + { 0x00000905, 0x0080 }, /* R2309 - HPLP1MIX Input 3 Volume */ + { 0x00000906, 0x0000 }, /* R2310 - HPLP1MIX Input 4 Source */ + { 0x00000907, 0x0080 }, /* R2311 - HPLP1MIX Input 4 Volume */ + { 0x00000908, 0x0000 }, /* R2312 - HPLP2MIX Input 1 Source */ + { 0x00000909, 0x0080 }, /* R2313 - HPLP2MIX Input 1 Volume */ + { 0x0000090A, 0x0000 }, /* R2314 - HPLP2MIX Input 2 Source */ + { 0x0000090B, 0x0080 }, /* R2315 - HPLP2MIX Input 2 Volume */ + { 0x0000090C, 0x0000 }, /* R2316 - HPLP2MIX Input 3 Source */ + { 0x0000090D, 0x0080 }, /* R2317 - HPLP2MIX Input 3 Volume */ + { 0x0000090E, 0x0000 }, /* R2318 - HPLP2MIX Input 4 Source */ + { 0x0000090F, 0x0080 }, /* R2319 - HPLP2MIX Input 4 Volume */ + { 0x00000910, 0x0000 }, /* R2320 - HPLP3MIX Input 1 Source */ + { 0x00000911, 0x0080 }, /* R2321 - HPLP3MIX Input 1 Volume */ + { 0x00000912, 0x0000 }, /* R2322 - HPLP3MIX Input 2 Source */ + { 0x00000913, 0x0080 }, /* R2323 - HPLP3MIX Input 2 Volume */ + { 0x00000914, 0x0000 }, /* R2324 - HPLP3MIX Input 3 Source */ + { 0x00000915, 0x0080 }, /* R2325 - HPLP3MIX Input 3 Volume */ + { 0x00000916, 0x0000 }, /* R2326 - HPLP3MIX Input 4 Source */ + { 0x00000917, 0x0080 }, /* R2327 - HPLP3MIX Input 4 Volume */ + { 0x00000918, 0x0000 }, /* R2328 - HPLP4MIX Input 1 Source */ + { 0x00000919, 0x0080 }, /* R2329 - HPLP4MIX Input 1 Volume */ + { 0x0000091A, 0x0000 }, /* R2330 - HPLP4MIX Input 2 Source */ + { 0x0000091B, 0x0080 }, /* R2331 - HPLP4MIX Input 2 Volume */ + { 0x0000091C, 0x0000 }, /* R2332 - HPLP4MIX Input 3 Source */ + { 0x0000091D, 0x0080 }, /* R2333 - HPLP4MIX Input 3 Volume */ + { 0x0000091E, 0x0000 }, /* R2334 - HPLP4MIX Input 4 Source */ + { 0x0000091F, 0x0080 }, /* R2335 - HPLP4MIX Input 4 Volume */ + { 0x00000940, 0x0000 }, /* R2368 - DSP1LMIX Input 1 Source */ + { 0x00000941, 0x0080 }, /* R2369 - DSP1LMIX Input 1 Volume */ + { 0x00000942, 0x0000 }, /* R2370 - DSP1LMIX Input 2 Source */ + { 0x00000943, 0x0080 }, /* R2371 - DSP1LMIX Input 2 Volume */ + { 0x00000944, 0x0000 }, /* R2372 - DSP1LMIX Input 3 Source */ + { 0x00000945, 0x0080 }, /* R2373 - DSP1LMIX Input 3 Volume */ + { 0x00000946, 0x0000 }, /* R2374 - DSP1LMIX Input 4 Source */ + { 0x00000947, 0x0080 }, /* R2375 - DSP1LMIX Input 4 Volume */ + { 0x00000948, 0x0000 }, /* R2376 - DSP1RMIX Input 1 Source */ + { 0x00000949, 0x0080 }, /* R2377 - DSP1RMIX Input 1 Volume */ + { 0x0000094A, 0x0000 }, /* R2378 - DSP1RMIX Input 2 Source */ + { 0x0000094B, 0x0080 }, /* R2379 - DSP1RMIX Input 2 Volume */ + { 0x0000094C, 0x0000 }, /* R2380 - DSP1RMIX Input 3 Source */ + { 0x0000094D, 0x0080 }, /* R2381 - DSP1RMIX Input 3 Volume */ + { 0x0000094E, 0x0000 }, /* R2382 - DSP1RMIX Input 4 Source */ + { 0x0000094F, 0x0080 }, /* R2383 - DSP1RMIX Input 4 Volume */ + { 0x00000950, 0x0000 }, /* R2384 - DSP1AUX1MIX Input 1 Source */ + { 0x00000958, 0x0000 }, /* R2392 - DSP1AUX2MIX Input 1 Source */ + { 0x00000960, 0x0000 }, /* R2400 - DSP1AUX3MIX Input 1 Source */ + { 0x00000968, 0x0000 }, /* R2408 - DSP1AUX4MIX Input 1 Source */ + { 0x00000970, 0x0000 }, /* R2416 - DSP1AUX5MIX Input 1 Source */ + { 0x00000978, 0x0000 }, /* R2424 - DSP1AUX6MIX Input 1 Source */ + { 0x00000A80, 0x0000 }, /* R2688 - ASRC1LMIX Input 1 Source */ + { 0x00000A88, 0x0000 }, /* R2696 - ASRC1RMIX Input 1 Source */ + { 0x00000A90, 0x0000 }, /* R2704 - ASRC2LMIX Input 1 Source */ + { 0x00000A98, 0x0000 }, /* R2712 - ASRC2RMIX Input 1 Source */ + { 0x00000B00, 0x0000 }, /* R2816 - ISRC1DEC1MIX Input 1 Source */ + { 0x00000B08, 0x0000 }, /* R2824 - ISRC1DEC2MIX Input 1 Source */ + { 0x00000B20, 0x0000 }, /* R2848 - ISRC1INT1MIX Input 1 Source */ + { 0x00000B28, 0x0000 }, /* R2856 - ISRC1INT2MIX Input 1 Source */ + { 0x00000B40, 0x0000 }, /* R2880 - ISRC2DEC1MIX Input 1 Source */ + { 0x00000B48, 0x0000 }, /* R2888 - ISRC2DEC2MIX Input 1 Source */ + { 0x00000B60, 0x0000 }, /* R2912 - ISRC2INT1MIX Input 1 Source */ + { 0x00000B68, 0x0000 }, /* R2920 - ISRC2INT2MIX Input 1 Source */ + { 0x00000C00, 0xA101 }, /* R3072 - GPIO1 CTRL */ + { 0x00000C01, 0xA101 }, /* R3073 - GPIO2 CTRL */ + { 0x00000C02, 0xA101 }, /* R3074 - GPIO3 CTRL */ + { 0x00000C03, 0xA101 }, /* R3075 - GPIO4 CTRL */ + { 0x00000C04, 0xA101 }, /* R3076 - GPIO5 CTRL */ + { 0x00000C0F, 0x0400 }, /* R3087 - IRQ CTRL 1 */ + { 0x00000C10, 0x1000 }, /* R3088 - GPIO Debounce Config */ + { 0x00000C20, 0x8002 }, /* R3104 - Misc Pad Ctrl 1 */ + { 0x00000C21, 0x8001 }, /* R3105 - Misc Pad Ctrl 2 */ + { 0x00000C22, 0x0000 }, /* R3106 - Misc Pad Ctrl 3 */ + { 0x00000C23, 0x0000 }, /* R3107 - Misc Pad Ctrl 4 */ + { 0x00000C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 5 */ + { 0x00000C25, 0x0000 }, /* R3109 - Misc Pad Ctrl 6 */ + { 0x00000D08, 0xFFFF }, /* R3336 - Interrupt Status 1 Mask */ + { 0x00000D09, 0xFFFF }, /* R3337 - Interrupt Status 2 Mask */ + { 0x00000D0A, 0xFFFF }, /* R3338 - Interrupt Status 3 Mask */ + { 0x00000D0B, 0xFFFF }, /* R3339 - Interrupt Status 4 Mask */ + { 0x00000D0C, 0xFEFF }, /* R3340 - Interrupt Status 5 Mask */ + { 0x00000D0F, 0x0000 }, /* R3343 - Interrupt Control */ + { 0x00000D18, 0xFFFF }, /* R3352 - IRQ2 Status 1 Mask */ + { 0x00000D19, 0xFFFF }, /* R3353 - IRQ2 Status 2 Mask */ + { 0x00000D1A, 0xFFFF }, /* R3354 - IRQ2 Status 3 Mask */ + { 0x00000D1B, 0xFFFF }, /* R3355 - IRQ2 Status 4 Mask */ + { 0x00000D1C, 0xFFFF }, /* R3356 - IRQ2 Status 5 Mask */ + { 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */ + { 0x00000D41, 0x0000 }, /* R3393 - ADSP2 IRQ0 */ + { 0x00000D53, 0xFFFF }, /* R3411 - AOD IRQ Mask IRQ1 */ + { 0x00000D54, 0xFFFF }, /* R3412 - AOD IRQ Mask IRQ2 */ + { 0x00000D56, 0x0000 }, /* R3414 - Jack detect debounce */ + { 0x00000E00, 0x0000 }, /* R3584 - FX_Ctrl1 */ + { 0x00000E01, 0x0000 }, /* R3585 - FX_Ctrl2 */ + { 0x00000E10, 0x6318 }, /* R3600 - EQ1_1 */ + { 0x00000E11, 0x6300 }, /* R3601 - EQ1_2 */ + { 0x00000E12, 0x0FC8 }, /* R3602 - EQ1_3 */ + { 0x00000E13, 0x03FE }, /* R3603 - EQ1_4 */ + { 0x00000E14, 0x00E0 }, /* R3604 - EQ1_5 */ + { 0x00000E15, 0x1EC4 }, /* R3605 - EQ1_6 */ + { 0x00000E16, 0xF136 }, /* R3606 - EQ1_7 */ + { 0x00000E17, 0x0409 }, /* R3607 - EQ1_8 */ + { 0x00000E18, 0x04CC }, /* R3608 - EQ1_9 */ + { 0x00000E19, 0x1C9B }, /* R3609 - EQ1_10 */ + { 0x00000E1A, 0xF337 }, /* R3610 - EQ1_11 */ + { 0x00000E1B, 0x040B }, /* R3611 - EQ1_12 */ + { 0x00000E1C, 0x0CBB }, /* R3612 - EQ1_13 */ + { 0x00000E1D, 0x16F8 }, /* R3613 - EQ1_14 */ + { 0x00000E1E, 0xF7D9 }, /* R3614 - EQ1_15 */ + { 0x00000E1F, 0x040A }, /* R3615 - EQ1_16 */ + { 0x00000E20, 0x1F14 }, /* R3616 - EQ1_17 */ + { 0x00000E21, 0x058C }, /* R3617 - EQ1_18 */ + { 0x00000E22, 0x0563 }, /* R3618 - EQ1_19 */ + { 0x00000E23, 0x4000 }, /* R3619 - EQ1_20 */ + { 0x00000E24, 0x0B75 }, /* R3620 - EQ1_21 */ + { 0x00000E26, 0x6318 }, /* R3622 - EQ2_1 */ + { 0x00000E27, 0x6300 }, /* R3623 - EQ2_2 */ + { 0x00000E28, 0x0FC8 }, /* R3624 - EQ2_3 */ + { 0x00000E29, 0x03FE }, /* R3625 - EQ2_4 */ + { 0x00000E2A, 0x00E0 }, /* R3626 - EQ2_5 */ + { 0x00000E2B, 0x1EC4 }, /* R3627 - EQ2_6 */ + { 0x00000E2C, 0xF136 }, /* R3628 - EQ2_7 */ + { 0x00000E2D, 0x0409 }, /* R3629 - EQ2_8 */ + { 0x00000E2E, 0x04CC }, /* R3630 - EQ2_9 */ + { 0x00000E2F, 0x1C9B }, /* R3631 - EQ2_10 */ + { 0x00000E30, 0xF337 }, /* R3632 - EQ2_11 */ + { 0x00000E31, 0x040B }, /* R3633 - EQ2_12 */ + { 0x00000E32, 0x0CBB }, /* R3634 - EQ2_13 */ + { 0x00000E33, 0x16F8 }, /* R3635 - EQ2_14 */ + { 0x00000E34, 0xF7D9 }, /* R3636 - EQ2_15 */ + { 0x00000E35, 0x040A }, /* R3637 - EQ2_16 */ + { 0x00000E36, 0x1F14 }, /* R3638 - EQ2_17 */ + { 0x00000E37, 0x058C }, /* R3639 - EQ2_18 */ + { 0x00000E38, 0x0563 }, /* R3640 - EQ2_19 */ + { 0x00000E39, 0x4000 }, /* R3641 - EQ2_20 */ + { 0x00000E3A, 0x0B75 }, /* R3642 - EQ2_21 */ + { 0x00000E3C, 0x6318 }, /* R3644 - EQ3_1 */ + { 0x00000E3D, 0x6300 }, /* R3645 - EQ3_2 */ + { 0x00000E3E, 0x0FC8 }, /* R3646 - EQ3_3 */ + { 0x00000E3F, 0x03FE }, /* R3647 - EQ3_4 */ + { 0x00000E40, 0x00E0 }, /* R3648 - EQ3_5 */ + { 0x00000E41, 0x1EC4 }, /* R3649 - EQ3_6 */ + { 0x00000E42, 0xF136 }, /* R3650 - EQ3_7 */ + { 0x00000E43, 0x0409 }, /* R3651 - EQ3_8 */ + { 0x00000E44, 0x04CC }, /* R3652 - EQ3_9 */ + { 0x00000E45, 0x1C9B }, /* R3653 - EQ3_10 */ + { 0x00000E46, 0xF337 }, /* R3654 - EQ3_11 */ + { 0x00000E47, 0x040B }, /* R3655 - EQ3_12 */ + { 0x00000E48, 0x0CBB }, /* R3656 - EQ3_13 */ + { 0x00000E49, 0x16F8 }, /* R3657 - EQ3_14 */ + { 0x00000E4A, 0xF7D9 }, /* R3658 - EQ3_15 */ + { 0x00000E4B, 0x040A }, /* R3659 - EQ3_16 */ + { 0x00000E4C, 0x1F14 }, /* R3660 - EQ3_17 */ + { 0x00000E4D, 0x058C }, /* R3661 - EQ3_18 */ + { 0x00000E4E, 0x0563 }, /* R3662 - EQ3_19 */ + { 0x00000E4F, 0x4000 }, /* R3663 - EQ3_20 */ + { 0x00000E50, 0x0B75 }, /* R3664 - EQ3_21 */ + { 0x00000E52, 0x6318 }, /* R3666 - EQ4_1 */ + { 0x00000E53, 0x6300 }, /* R3667 - EQ4_2 */ + { 0x00000E54, 0x0FC8 }, /* R3668 - EQ4_3 */ + { 0x00000E55, 0x03FE }, /* R3669 - EQ4_4 */ + { 0x00000E56, 0x00E0 }, /* R3670 - EQ4_5 */ + { 0x00000E57, 0x1EC4 }, /* R3671 - EQ4_6 */ + { 0x00000E58, 0xF136 }, /* R3672 - EQ4_7 */ + { 0x00000E59, 0x0409 }, /* R3673 - EQ4_8 */ + { 0x00000E5A, 0x04CC }, /* R3674 - EQ4_9 */ + { 0x00000E5B, 0x1C9B }, /* R3675 - EQ4_10 */ + { 0x00000E5C, 0xF337 }, /* R3676 - EQ4_11 */ + { 0x00000E5D, 0x040B }, /* R3677 - EQ4_12 */ + { 0x00000E5E, 0x0CBB }, /* R3678 - EQ4_13 */ + { 0x00000E5F, 0x16F8 }, /* R3679 - EQ4_14 */ + { 0x00000E60, 0xF7D9 }, /* R3680 - EQ4_15 */ + { 0x00000E61, 0x040A }, /* R3681 - EQ4_16 */ + { 0x00000E62, 0x1F14 }, /* R3682 - EQ4_17 */ + { 0x00000E63, 0x058C }, /* R3683 - EQ4_18 */ + { 0x00000E64, 0x0563 }, /* R3684 - EQ4_19 */ + { 0x00000E65, 0x4000 }, /* R3685 - EQ4_20 */ + { 0x00000E66, 0x0B75 }, /* R3686 - EQ4_21 */ + { 0x00000E80, 0x0018 }, /* R3712 - DRC1 ctrl1 */ + { 0x00000E81, 0x0933 }, /* R3713 - DRC1 ctrl2 */ + { 0x00000E82, 0x0018 }, /* R3714 - DRC1 ctrl3 */ + { 0x00000E83, 0x0000 }, /* R3715 - DRC1 ctrl4 */ + { 0x00000E84, 0x0000 }, /* R3716 - DRC1 ctrl5 */ + { 0x00000E89, 0x0018 }, /* R3721 - DRC2 ctrl1 */ + { 0x00000E8A, 0x0933 }, /* R3722 - DRC2 ctrl2 */ + { 0x00000E8B, 0x0018 }, /* R3723 - DRC2 ctrl3 */ + { 0x00000E8C, 0x0000 }, /* R3724 - DRC2 ctrl4 */ + { 0x00000E8D, 0x0000 }, /* R3725 - DRC2 ctrl5 */ + { 0x00000EC0, 0x0000 }, /* R3776 - HPLPF1_1 */ + { 0x00000EC1, 0x0000 }, /* R3777 - HPLPF1_2 */ + { 0x00000EC4, 0x0000 }, /* R3780 - HPLPF2_1 */ + { 0x00000EC5, 0x0000 }, /* R3781 - HPLPF2_2 */ + { 0x00000EC8, 0x0000 }, /* R3784 - HPLPF3_1 */ + { 0x00000EC9, 0x0000 }, /* R3785 - HPLPF3_2 */ + { 0x00000ECC, 0x0000 }, /* R3788 - HPLPF4_1 */ + { 0x00000ECD, 0x0000 }, /* R3789 - HPLPF4_2 */ + { 0x00000EE0, 0x0000 }, /* R3808 - ASRC_ENABLE */ + { 0x00000EE2, 0x0000 }, /* R3810 - ASRC_RATE1 */ + { 0x00000EE3, 0x4000 }, /* R3811 - ASRC_RATE2 */ + { 0x00000EF0, 0x0000 }, /* R3824 - ISRC 1 CTRL 1 */ + { 0x00000EF1, 0x0000 }, /* R3825 - ISRC 1 CTRL 2 */ + { 0x00000EF2, 0x0000 }, /* R3826 - ISRC 1 CTRL 3 */ + { 0x00000EF3, 0x0000 }, /* R3827 - ISRC 2 CTRL 1 */ + { 0x00000EF4, 0x0000 }, /* R3828 - ISRC 2 CTRL 2 */ + { 0x00000EF5, 0x0000 }, /* R3829 - ISRC 2 CTRL 3 */ + { 0x00000EF6, 0x0000 }, /* R3830 - ISRC 3 CTRL 1 */ + { 0x00000EF7, 0x0000 }, /* R3831 - ISRC 3 CTRL 2 */ + { 0x00000EF8, 0x0000 }, /* R3832 - ISRC 3 CTRL 3 */ + { 0x00001100, 0x0010 }, /* R4352 - DSP1 Control 1 */ + { 0x00001101, 0x0000 }, /* R4353 - DSP1 Clocking 1 */ +}; + +static bool wm5102_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ARIZONA_SOFTWARE_RESET: + case ARIZONA_DEVICE_REVISION: + case ARIZONA_CTRL_IF_SPI_CFG_1: + case ARIZONA_CTRL_IF_I2C1_CFG_1: + case ARIZONA_CTRL_IF_STATUS_1: + case ARIZONA_WRITE_SEQUENCER_CTRL_0: + case ARIZONA_WRITE_SEQUENCER_CTRL_1: + case ARIZONA_WRITE_SEQUENCER_CTRL_2: + case ARIZONA_WRITE_SEQUENCER_PROM: + case ARIZONA_TONE_GENERATOR_1: + case ARIZONA_TONE_GENERATOR_2: + case ARIZONA_TONE_GENERATOR_3: + case ARIZONA_TONE_GENERATOR_4: + case ARIZONA_TONE_GENERATOR_5: + case ARIZONA_PWM_DRIVE_1: + case ARIZONA_PWM_DRIVE_2: + case ARIZONA_PWM_DRIVE_3: + case ARIZONA_WAKE_CONTROL: + case ARIZONA_SEQUENCE_CONTROL: + case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1: + case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2: + case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3: + case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4: + case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1: + case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2: + case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3: + case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4: + case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5: + case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6: + case ARIZONA_COMFORT_NOISE_GENERATOR: + case ARIZONA_HAPTICS_CONTROL_1: + case ARIZONA_HAPTICS_CONTROL_2: + case ARIZONA_HAPTICS_PHASE_1_INTENSITY: + case ARIZONA_HAPTICS_PHASE_1_DURATION: + case ARIZONA_HAPTICS_PHASE_2_INTENSITY: + case ARIZONA_HAPTICS_PHASE_2_DURATION: + case ARIZONA_HAPTICS_PHASE_3_INTENSITY: + case ARIZONA_HAPTICS_PHASE_3_DURATION: + case ARIZONA_HAPTICS_STATUS: + case ARIZONA_CLOCK_32K_1: + case ARIZONA_SYSTEM_CLOCK_1: + case ARIZONA_SAMPLE_RATE_1: + case ARIZONA_SAMPLE_RATE_2: + case ARIZONA_SAMPLE_RATE_3: + case ARIZONA_SAMPLE_RATE_1_STATUS: + case ARIZONA_SAMPLE_RATE_2_STATUS: + case ARIZONA_SAMPLE_RATE_3_STATUS: + case ARIZONA_ASYNC_CLOCK_1: + case ARIZONA_ASYNC_SAMPLE_RATE_1: + case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS: + case ARIZONA_OUTPUT_SYSTEM_CLOCK: + case ARIZONA_OUTPUT_ASYNC_CLOCK: + case ARIZONA_RATE_ESTIMATOR_1: + case ARIZONA_RATE_ESTIMATOR_2: + case ARIZONA_RATE_ESTIMATOR_3: + case ARIZONA_RATE_ESTIMATOR_4: + case ARIZONA_RATE_ESTIMATOR_5: + case ARIZONA_FLL1_CONTROL_1: + case ARIZONA_FLL1_CONTROL_2: + case ARIZONA_FLL1_CONTROL_3: + case ARIZONA_FLL1_CONTROL_4: + case ARIZONA_FLL1_CONTROL_5: + case ARIZONA_FLL1_CONTROL_6: + case ARIZONA_FLL1_LOOP_FILTER_TEST_1: + case ARIZONA_FLL1_SYNCHRONISER_1: + case ARIZONA_FLL1_SYNCHRONISER_2: + case ARIZONA_FLL1_SYNCHRONISER_3: + case ARIZONA_FLL1_SYNCHRONISER_4: + case ARIZONA_FLL1_SYNCHRONISER_5: + case ARIZONA_FLL1_SYNCHRONISER_6: + case ARIZONA_FLL1_SPREAD_SPECTRUM: + case ARIZONA_FLL1_GPIO_CLOCK: + case ARIZONA_FLL2_CONTROL_1: + case ARIZONA_FLL2_CONTROL_2: + case ARIZONA_FLL2_CONTROL_3: + case ARIZONA_FLL2_CONTROL_4: + case ARIZONA_FLL2_CONTROL_5: + case ARIZONA_FLL2_CONTROL_6: + case ARIZONA_FLL2_LOOP_FILTER_TEST_1: + case ARIZONA_FLL2_SYNCHRONISER_1: + case ARIZONA_FLL2_SYNCHRONISER_2: + case ARIZONA_FLL2_SYNCHRONISER_3: + case ARIZONA_FLL2_SYNCHRONISER_4: + case ARIZONA_FLL2_SYNCHRONISER_5: + case ARIZONA_FLL2_SYNCHRONISER_6: + case ARIZONA_FLL2_SPREAD_SPECTRUM: + case ARIZONA_FLL2_GPIO_CLOCK: + case ARIZONA_MIC_CHARGE_PUMP_1: + case ARIZONA_LDO1_CONTROL_1: + case ARIZONA_LDO2_CONTROL_1: + case ARIZONA_MIC_BIAS_CTRL_1: + case ARIZONA_MIC_BIAS_CTRL_2: + case ARIZONA_MIC_BIAS_CTRL_3: + case ARIZONA_ACCESSORY_DETECT_MODE_1: + case ARIZONA_HEADPHONE_DETECT_1: + case ARIZONA_HEADPHONE_DETECT_2: + case ARIZONA_MIC_DETECT_1: + case ARIZONA_MIC_DETECT_2: + case ARIZONA_MIC_DETECT_3: + case ARIZONA_MIC_NOISE_MIX_CONTROL_1: + case ARIZONA_ISOLATION_CONTROL: + case ARIZONA_JACK_DETECT_ANALOGUE: + case ARIZONA_INPUT_ENABLES: + case ARIZONA_INPUT_RATE: + case ARIZONA_INPUT_VOLUME_RAMP: + case ARIZONA_IN1L_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_1L: + case ARIZONA_DMIC1L_CONTROL: + case ARIZONA_IN1R_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_1R: + case ARIZONA_DMIC1R_CONTROL: + case ARIZONA_IN2L_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_2L: + case ARIZONA_DMIC2L_CONTROL: + case ARIZONA_IN2R_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_2R: + case ARIZONA_DMIC2R_CONTROL: + case ARIZONA_IN3L_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_3L: + case ARIZONA_DMIC3L_CONTROL: + case ARIZONA_IN3R_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_3R: + case ARIZONA_DMIC3R_CONTROL: + case ARIZONA_OUTPUT_ENABLES_1: + case ARIZONA_OUTPUT_STATUS_1: + case ARIZONA_OUTPUT_RATE_1: + case ARIZONA_OUTPUT_VOLUME_RAMP: + case ARIZONA_OUTPUT_PATH_CONFIG_1L: + case ARIZONA_DAC_DIGITAL_VOLUME_1L: + case ARIZONA_DAC_VOLUME_LIMIT_1L: + case ARIZONA_NOISE_GATE_SELECT_1L: + case ARIZONA_OUTPUT_PATH_CONFIG_1R: + case ARIZONA_DAC_DIGITAL_VOLUME_1R: + case ARIZONA_DAC_VOLUME_LIMIT_1R: + case ARIZONA_NOISE_GATE_SELECT_1R: + case ARIZONA_OUTPUT_PATH_CONFIG_2L: + case ARIZONA_DAC_DIGITAL_VOLUME_2L: + case ARIZONA_DAC_VOLUME_LIMIT_2L: + case ARIZONA_NOISE_GATE_SELECT_2L: + case ARIZONA_OUTPUT_PATH_CONFIG_2R: + case ARIZONA_DAC_DIGITAL_VOLUME_2R: + case ARIZONA_DAC_VOLUME_LIMIT_2R: + case ARIZONA_NOISE_GATE_SELECT_2R: + case ARIZONA_OUTPUT_PATH_CONFIG_3L: + case ARIZONA_DAC_DIGITAL_VOLUME_3L: + case ARIZONA_DAC_VOLUME_LIMIT_3L: + case ARIZONA_NOISE_GATE_SELECT_3L: + case ARIZONA_OUTPUT_PATH_CONFIG_3R: + case ARIZONA_DAC_DIGITAL_VOLUME_3R: + case ARIZONA_DAC_VOLUME_LIMIT_3R: + case ARIZONA_OUTPUT_PATH_CONFIG_4L: + case ARIZONA_DAC_DIGITAL_VOLUME_4L: + case ARIZONA_OUT_VOLUME_4L: + case ARIZONA_NOISE_GATE_SELECT_4L: + case ARIZONA_OUTPUT_PATH_CONFIG_4R: + case ARIZONA_DAC_DIGITAL_VOLUME_4R: + case ARIZONA_OUT_VOLUME_4R: + case ARIZONA_NOISE_GATE_SELECT_4R: + case ARIZONA_OUTPUT_PATH_CONFIG_5L: + case ARIZONA_DAC_DIGITAL_VOLUME_5L: + case ARIZONA_DAC_VOLUME_LIMIT_5L: + case ARIZONA_NOISE_GATE_SELECT_5L: + case ARIZONA_OUTPUT_PATH_CONFIG_5R: + case ARIZONA_DAC_DIGITAL_VOLUME_5R: + case ARIZONA_DAC_VOLUME_LIMIT_5R: + case ARIZONA_NOISE_GATE_SELECT_5R: + case ARIZONA_DAC_AEC_CONTROL_1: + case ARIZONA_NOISE_GATE_CONTROL: + case ARIZONA_PDM_SPK1_CTRL_1: + case ARIZONA_PDM_SPK1_CTRL_2: + case ARIZONA_DAC_COMP_1: + case ARIZONA_DAC_COMP_2: + case ARIZONA_DAC_COMP_3: + case ARIZONA_DAC_COMP_4: + case ARIZONA_AIF1_BCLK_CTRL: + case ARIZONA_AIF1_TX_PIN_CTRL: + case ARIZONA_AIF1_RX_PIN_CTRL: + case ARIZONA_AIF1_RATE_CTRL: + case ARIZONA_AIF1_FORMAT: + case ARIZONA_AIF1_TX_BCLK_RATE: + case ARIZONA_AIF1_RX_BCLK_RATE: + case ARIZONA_AIF1_FRAME_CTRL_1: + case ARIZONA_AIF1_FRAME_CTRL_2: + case ARIZONA_AIF1_FRAME_CTRL_3: + case ARIZONA_AIF1_FRAME_CTRL_4: + case ARIZONA_AIF1_FRAME_CTRL_5: + case ARIZONA_AIF1_FRAME_CTRL_6: + case ARIZONA_AIF1_FRAME_CTRL_7: + case ARIZONA_AIF1_FRAME_CTRL_8: + case ARIZONA_AIF1_FRAME_CTRL_9: + case ARIZONA_AIF1_FRAME_CTRL_10: + case ARIZONA_AIF1_FRAME_CTRL_11: + case ARIZONA_AIF1_FRAME_CTRL_12: + case ARIZONA_AIF1_FRAME_CTRL_13: + case ARIZONA_AIF1_FRAME_CTRL_14: + case ARIZONA_AIF1_FRAME_CTRL_15: + case ARIZONA_AIF1_FRAME_CTRL_16: + case ARIZONA_AIF1_FRAME_CTRL_17: + case ARIZONA_AIF1_FRAME_CTRL_18: + case ARIZONA_AIF1_TX_ENABLES: + case ARIZONA_AIF1_RX_ENABLES: + case ARIZONA_AIF1_FORCE_WRITE: + case ARIZONA_AIF2_BCLK_CTRL: + case ARIZONA_AIF2_TX_PIN_CTRL: + case ARIZONA_AIF2_RX_PIN_CTRL: + case ARIZONA_AIF2_RATE_CTRL: + case ARIZONA_AIF2_FORMAT: + case ARIZONA_AIF2_TX_BCLK_RATE: + case ARIZONA_AIF2_RX_BCLK_RATE: + case ARIZONA_AIF2_FRAME_CTRL_1: + case ARIZONA_AIF2_FRAME_CTRL_2: + case ARIZONA_AIF2_FRAME_CTRL_3: + case ARIZONA_AIF2_FRAME_CTRL_4: + case ARIZONA_AIF2_FRAME_CTRL_11: + case ARIZONA_AIF2_FRAME_CTRL_12: + case ARIZONA_AIF2_TX_ENABLES: + case ARIZONA_AIF2_RX_ENABLES: + case ARIZONA_AIF2_FORCE_WRITE: + case ARIZONA_AIF3_BCLK_CTRL: + case ARIZONA_AIF3_TX_PIN_CTRL: + case ARIZONA_AIF3_RX_PIN_CTRL: + case ARIZONA_AIF3_RATE_CTRL: + case ARIZONA_AIF3_FORMAT: + case ARIZONA_AIF3_TX_BCLK_RATE: + case ARIZONA_AIF3_RX_BCLK_RATE: + case ARIZONA_AIF3_FRAME_CTRL_1: + case ARIZONA_AIF3_FRAME_CTRL_2: + case ARIZONA_AIF3_FRAME_CTRL_3: + case ARIZONA_AIF3_FRAME_CTRL_4: + case ARIZONA_AIF3_FRAME_CTRL_11: + case ARIZONA_AIF3_FRAME_CTRL_12: + case ARIZONA_AIF3_TX_ENABLES: + case ARIZONA_AIF3_RX_ENABLES: + case ARIZONA_AIF3_FORCE_WRITE: + case ARIZONA_SLIMBUS_FRAMER_REF_GEAR: + case ARIZONA_SLIMBUS_RATES_1: + case ARIZONA_SLIMBUS_RATES_2: + case ARIZONA_SLIMBUS_RATES_3: + case ARIZONA_SLIMBUS_RATES_4: + case ARIZONA_SLIMBUS_RATES_5: + case ARIZONA_SLIMBUS_RATES_6: + case ARIZONA_SLIMBUS_RATES_7: + case ARIZONA_SLIMBUS_RATES_8: + case ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE: + case ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE: + case ARIZONA_SLIMBUS_RX_PORT_STATUS: + case ARIZONA_SLIMBUS_TX_PORT_STATUS: + case ARIZONA_PWM1MIX_INPUT_1_SOURCE: + case ARIZONA_PWM1MIX_INPUT_1_VOLUME: + case ARIZONA_PWM1MIX_INPUT_2_SOURCE: + case ARIZONA_PWM1MIX_INPUT_2_VOLUME: + case ARIZONA_PWM1MIX_INPUT_3_SOURCE: + case ARIZONA_PWM1MIX_INPUT_3_VOLUME: + case ARIZONA_PWM1MIX_INPUT_4_SOURCE: + case ARIZONA_PWM1MIX_INPUT_4_VOLUME: + case ARIZONA_PWM2MIX_INPUT_1_SOURCE: + case ARIZONA_PWM2MIX_INPUT_1_VOLUME: + case ARIZONA_PWM2MIX_INPUT_2_SOURCE: + case ARIZONA_PWM2MIX_INPUT_2_VOLUME: + case ARIZONA_PWM2MIX_INPUT_3_SOURCE: + case ARIZONA_PWM2MIX_INPUT_3_VOLUME: + case ARIZONA_PWM2MIX_INPUT_4_SOURCE: + case ARIZONA_PWM2MIX_INPUT_4_VOLUME: + case ARIZONA_MICMIX_INPUT_1_SOURCE: + case ARIZONA_MICMIX_INPUT_1_VOLUME: + case ARIZONA_MICMIX_INPUT_2_SOURCE: + case ARIZONA_MICMIX_INPUT_2_VOLUME: + case ARIZONA_MICMIX_INPUT_3_SOURCE: + case ARIZONA_MICMIX_INPUT_3_VOLUME: + case ARIZONA_MICMIX_INPUT_4_SOURCE: + case ARIZONA_MICMIX_INPUT_4_VOLUME: + case ARIZONA_NOISEMIX_INPUT_1_SOURCE: + case ARIZONA_NOISEMIX_INPUT_1_VOLUME: + case ARIZONA_NOISEMIX_INPUT_2_SOURCE: + case ARIZONA_NOISEMIX_INPUT_2_VOLUME: + case ARIZONA_NOISEMIX_INPUT_3_SOURCE: + case ARIZONA_NOISEMIX_INPUT_3_VOLUME: + case ARIZONA_NOISEMIX_INPUT_4_SOURCE: + case ARIZONA_NOISEMIX_INPUT_4_VOLUME: + case ARIZONA_OUT1LMIX_INPUT_1_SOURCE: + case ARIZONA_OUT1LMIX_INPUT_1_VOLUME: + case ARIZONA_OUT1LMIX_INPUT_2_SOURCE: + case ARIZONA_OUT1LMIX_INPUT_2_VOLUME: + case ARIZONA_OUT1LMIX_INPUT_3_SOURCE: + case ARIZONA_OUT1LMIX_INPUT_3_VOLUME: + case ARIZONA_OUT1LMIX_INPUT_4_SOURCE: + case ARIZONA_OUT1LMIX_INPUT_4_VOLUME: + case ARIZONA_OUT1RMIX_INPUT_1_SOURCE: + case ARIZONA_OUT1RMIX_INPUT_1_VOLUME: + case ARIZONA_OUT1RMIX_INPUT_2_SOURCE: + case ARIZONA_OUT1RMIX_INPUT_2_VOLUME: + case ARIZONA_OUT1RMIX_INPUT_3_SOURCE: + case ARIZONA_OUT1RMIX_INPUT_3_VOLUME: + case ARIZONA_OUT1RMIX_INPUT_4_SOURCE: + case ARIZONA_OUT1RMIX_INPUT_4_VOLUME: + case ARIZONA_OUT2LMIX_INPUT_1_SOURCE: + case ARIZONA_OUT2LMIX_INPUT_1_VOLUME: + case ARIZONA_OUT2LMIX_INPUT_2_SOURCE: + case ARIZONA_OUT2LMIX_INPUT_2_VOLUME: + case ARIZONA_OUT2LMIX_INPUT_3_SOURCE: + case ARIZONA_OUT2LMIX_INPUT_3_VOLUME: + case ARIZONA_OUT2LMIX_INPUT_4_SOURCE: + case ARIZONA_OUT2LMIX_INPUT_4_VOLUME: + case ARIZONA_OUT2RMIX_INPUT_1_SOURCE: + case ARIZONA_OUT2RMIX_INPUT_1_VOLUME: + case ARIZONA_OUT2RMIX_INPUT_2_SOURCE: + case ARIZONA_OUT2RMIX_INPUT_2_VOLUME: + case ARIZONA_OUT2RMIX_INPUT_3_SOURCE: + case ARIZONA_OUT2RMIX_INPUT_3_VOLUME: + case ARIZONA_OUT2RMIX_INPUT_4_SOURCE: + case ARIZONA_OUT2RMIX_INPUT_4_VOLUME: + case ARIZONA_OUT3LMIX_INPUT_1_SOURCE: + case ARIZONA_OUT3LMIX_INPUT_1_VOLUME: + case ARIZONA_OUT3LMIX_INPUT_2_SOURCE: + case ARIZONA_OUT3LMIX_INPUT_2_VOLUME: + case ARIZONA_OUT3LMIX_INPUT_3_SOURCE: + case ARIZONA_OUT3LMIX_INPUT_3_VOLUME: + case ARIZONA_OUT3LMIX_INPUT_4_SOURCE: + case ARIZONA_OUT3LMIX_INPUT_4_VOLUME: + case ARIZONA_OUT4LMIX_INPUT_1_SOURCE: + case ARIZONA_OUT4LMIX_INPUT_1_VOLUME: + case ARIZONA_OUT4LMIX_INPUT_2_SOURCE: + case ARIZONA_OUT4LMIX_INPUT_2_VOLUME: + case ARIZONA_OUT4LMIX_INPUT_3_SOURCE: + case ARIZONA_OUT4LMIX_INPUT_3_VOLUME: + case ARIZONA_OUT4LMIX_INPUT_4_SOURCE: + case ARIZONA_OUT4LMIX_INPUT_4_VOLUME: + case ARIZONA_OUT4RMIX_INPUT_1_SOURCE: + case ARIZONA_OUT4RMIX_INPUT_1_VOLUME: + case ARIZONA_OUT4RMIX_INPUT_2_SOURCE: + case ARIZONA_OUT4RMIX_INPUT_2_VOLUME: + case ARIZONA_OUT4RMIX_INPUT_3_SOURCE: + case ARIZONA_OUT4RMIX_INPUT_3_VOLUME: + case ARIZONA_OUT4RMIX_INPUT_4_SOURCE: + case ARIZONA_OUT4RMIX_INPUT_4_VOLUME: + case ARIZONA_OUT5LMIX_INPUT_1_SOURCE: + case ARIZONA_OUT5LMIX_INPUT_1_VOLUME: + case ARIZONA_OUT5LMIX_INPUT_2_SOURCE: + case ARIZONA_OUT5LMIX_INPUT_2_VOLUME: + case ARIZONA_OUT5LMIX_INPUT_3_SOURCE: + case ARIZONA_OUT5LMIX_INPUT_3_VOLUME: + case ARIZONA_OUT5LMIX_INPUT_4_SOURCE: + case ARIZONA_OUT5LMIX_INPUT_4_VOLUME: + case ARIZONA_OUT5RMIX_INPUT_1_SOURCE: + case ARIZONA_OUT5RMIX_INPUT_1_VOLUME: + case ARIZONA_OUT5RMIX_INPUT_2_SOURCE: + case ARIZONA_OUT5RMIX_INPUT_2_VOLUME: + case ARIZONA_OUT5RMIX_INPUT_3_SOURCE: + case ARIZONA_OUT5RMIX_INPUT_3_VOLUME: + case ARIZONA_OUT5RMIX_INPUT_4_SOURCE: + case ARIZONA_OUT5RMIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX1MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX1MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX1MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX1MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX1MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX1MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX1MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX2MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX2MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX2MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX2MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX2MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX2MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX2MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX3MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX3MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX3MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX3MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX3MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX3MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX3MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX4MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX4MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX4MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX4MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX4MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX4MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX4MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX5MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX5MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX5MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX5MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX5MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX5MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX5MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX6MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX6MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX6MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX6MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX6MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX6MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX6MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX7MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX7MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX7MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX7MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX7MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX7MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX7MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX8MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX8MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX8MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX8MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX8MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX8MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX8MIX_INPUT_4_VOLUME: + case ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE: + case ARIZONA_AIF2TX1MIX_INPUT_1_VOLUME: + case ARIZONA_AIF2TX1MIX_INPUT_2_SOURCE: + case ARIZONA_AIF2TX1MIX_INPUT_2_VOLUME: + case ARIZONA_AIF2TX1MIX_INPUT_3_SOURCE: + case ARIZONA_AIF2TX1MIX_INPUT_3_VOLUME: + case ARIZONA_AIF2TX1MIX_INPUT_4_SOURCE: + case ARIZONA_AIF2TX1MIX_INPUT_4_VOLUME: + case ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE: + case ARIZONA_AIF2TX2MIX_INPUT_1_VOLUME: + case ARIZONA_AIF2TX2MIX_INPUT_2_SOURCE: + case ARIZONA_AIF2TX2MIX_INPUT_2_VOLUME: + case ARIZONA_AIF2TX2MIX_INPUT_3_SOURCE: + case ARIZONA_AIF2TX2MIX_INPUT_3_VOLUME: + case ARIZONA_AIF2TX2MIX_INPUT_4_SOURCE: + case ARIZONA_AIF2TX2MIX_INPUT_4_VOLUME: + case ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE: + case ARIZONA_AIF3TX1MIX_INPUT_1_VOLUME: + case ARIZONA_AIF3TX1MIX_INPUT_2_SOURCE: + case ARIZONA_AIF3TX1MIX_INPUT_2_VOLUME: + case ARIZONA_AIF3TX1MIX_INPUT_3_SOURCE: + case ARIZONA_AIF3TX1MIX_INPUT_3_VOLUME: + case ARIZONA_AIF3TX1MIX_INPUT_4_SOURCE: + case ARIZONA_AIF3TX1MIX_INPUT_4_VOLUME: + case ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE: + case ARIZONA_AIF3TX2MIX_INPUT_1_VOLUME: + case ARIZONA_AIF3TX2MIX_INPUT_2_SOURCE: + case ARIZONA_AIF3TX2MIX_INPUT_2_VOLUME: + case ARIZONA_AIF3TX2MIX_INPUT_3_SOURCE: + case ARIZONA_AIF3TX2MIX_INPUT_3_VOLUME: + case ARIZONA_AIF3TX2MIX_INPUT_4_SOURCE: + case ARIZONA_AIF3TX2MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX1MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX1MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX1MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX1MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX1MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX1MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX1MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX2MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX2MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX2MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX2MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX2MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX2MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX2MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX3MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX3MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX3MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX3MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX3MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX3MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX3MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX4MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX4MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX4MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX4MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX4MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX4MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX4MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX5MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX5MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX5MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX5MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX5MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX5MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX5MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX6MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX6MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX6MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX6MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX6MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX6MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX6MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX7MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX7MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX7MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX7MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX7MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX7MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX7MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX8MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX8MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX8MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX8MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX8MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX8MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX8MIX_INPUT_4_VOLUME: + case ARIZONA_EQ1MIX_INPUT_1_SOURCE: + case ARIZONA_EQ1MIX_INPUT_1_VOLUME: + case ARIZONA_EQ1MIX_INPUT_2_SOURCE: + case ARIZONA_EQ1MIX_INPUT_2_VOLUME: + case ARIZONA_EQ1MIX_INPUT_3_SOURCE: + case ARIZONA_EQ1MIX_INPUT_3_VOLUME: + case ARIZONA_EQ1MIX_INPUT_4_SOURCE: + case ARIZONA_EQ1MIX_INPUT_4_VOLUME: + case ARIZONA_EQ2MIX_INPUT_1_SOURCE: + case ARIZONA_EQ2MIX_INPUT_1_VOLUME: + case ARIZONA_EQ2MIX_INPUT_2_SOURCE: + case ARIZONA_EQ2MIX_INPUT_2_VOLUME: + case ARIZONA_EQ2MIX_INPUT_3_SOURCE: + case ARIZONA_EQ2MIX_INPUT_3_VOLUME: + case ARIZONA_EQ2MIX_INPUT_4_SOURCE: + case ARIZONA_EQ2MIX_INPUT_4_VOLUME: + case ARIZONA_EQ3MIX_INPUT_1_SOURCE: + case ARIZONA_EQ3MIX_INPUT_1_VOLUME: + case ARIZONA_EQ3MIX_INPUT_2_SOURCE: + case ARIZONA_EQ3MIX_INPUT_2_VOLUME: + case ARIZONA_EQ3MIX_INPUT_3_SOURCE: + case ARIZONA_EQ3MIX_INPUT_3_VOLUME: + case ARIZONA_EQ3MIX_INPUT_4_SOURCE: + case ARIZONA_EQ3MIX_INPUT_4_VOLUME: + case ARIZONA_EQ4MIX_INPUT_1_SOURCE: + case ARIZONA_EQ4MIX_INPUT_1_VOLUME: + case ARIZONA_EQ4MIX_INPUT_2_SOURCE: + case ARIZONA_EQ4MIX_INPUT_2_VOLUME: + case ARIZONA_EQ4MIX_INPUT_3_SOURCE: + case ARIZONA_EQ4MIX_INPUT_3_VOLUME: + case ARIZONA_EQ4MIX_INPUT_4_SOURCE: + case ARIZONA_EQ4MIX_INPUT_4_VOLUME: + case ARIZONA_DRC1LMIX_INPUT_1_SOURCE: + case ARIZONA_DRC1LMIX_INPUT_1_VOLUME: + case ARIZONA_DRC1LMIX_INPUT_2_SOURCE: + case ARIZONA_DRC1LMIX_INPUT_2_VOLUME: + case ARIZONA_DRC1LMIX_INPUT_3_SOURCE: + case ARIZONA_DRC1LMIX_INPUT_3_VOLUME: + case ARIZONA_DRC1LMIX_INPUT_4_SOURCE: + case ARIZONA_DRC1LMIX_INPUT_4_VOLUME: + case ARIZONA_DRC1RMIX_INPUT_1_SOURCE: + case ARIZONA_DRC1RMIX_INPUT_1_VOLUME: + case ARIZONA_DRC1RMIX_INPUT_2_SOURCE: + case ARIZONA_DRC1RMIX_INPUT_2_VOLUME: + case ARIZONA_DRC1RMIX_INPUT_3_SOURCE: + case ARIZONA_DRC1RMIX_INPUT_3_VOLUME: + case ARIZONA_DRC1RMIX_INPUT_4_SOURCE: + case ARIZONA_DRC1RMIX_INPUT_4_VOLUME: + case ARIZONA_DRC2LMIX_INPUT_1_SOURCE: + case ARIZONA_DRC2LMIX_INPUT_1_VOLUME: + case ARIZONA_DRC2LMIX_INPUT_2_SOURCE: + case ARIZONA_DRC2LMIX_INPUT_2_VOLUME: + case ARIZONA_DRC2LMIX_INPUT_3_SOURCE: + case ARIZONA_DRC2LMIX_INPUT_3_VOLUME: + case ARIZONA_DRC2LMIX_INPUT_4_SOURCE: + case ARIZONA_DRC2LMIX_INPUT_4_VOLUME: + case ARIZONA_DRC2RMIX_INPUT_1_SOURCE: + case ARIZONA_DRC2RMIX_INPUT_1_VOLUME: + case ARIZONA_DRC2RMIX_INPUT_2_SOURCE: + case ARIZONA_DRC2RMIX_INPUT_2_VOLUME: + case ARIZONA_DRC2RMIX_INPUT_3_SOURCE: + case ARIZONA_DRC2RMIX_INPUT_3_VOLUME: + case ARIZONA_DRC2RMIX_INPUT_4_SOURCE: + case ARIZONA_DRC2RMIX_INPUT_4_VOLUME: + case ARIZONA_HPLP1MIX_INPUT_1_SOURCE: + case ARIZONA_HPLP1MIX_INPUT_1_VOLUME: + case ARIZONA_HPLP1MIX_INPUT_2_SOURCE: + case ARIZONA_HPLP1MIX_INPUT_2_VOLUME: + case ARIZONA_HPLP1MIX_INPUT_3_SOURCE: + case ARIZONA_HPLP1MIX_INPUT_3_VOLUME: + case ARIZONA_HPLP1MIX_INPUT_4_SOURCE: + case ARIZONA_HPLP1MIX_INPUT_4_VOLUME: + case ARIZONA_HPLP2MIX_INPUT_1_SOURCE: + case ARIZONA_HPLP2MIX_INPUT_1_VOLUME: + case ARIZONA_HPLP2MIX_INPUT_2_SOURCE: + case ARIZONA_HPLP2MIX_INPUT_2_VOLUME: + case ARIZONA_HPLP2MIX_INPUT_3_SOURCE: + case ARIZONA_HPLP2MIX_INPUT_3_VOLUME: + case ARIZONA_HPLP2MIX_INPUT_4_SOURCE: + case ARIZONA_HPLP2MIX_INPUT_4_VOLUME: + case ARIZONA_HPLP3MIX_INPUT_1_SOURCE: + case ARIZONA_HPLP3MIX_INPUT_1_VOLUME: + case ARIZONA_HPLP3MIX_INPUT_2_SOURCE: + case ARIZONA_HPLP3MIX_INPUT_2_VOLUME: + case ARIZONA_HPLP3MIX_INPUT_3_SOURCE: + case ARIZONA_HPLP3MIX_INPUT_3_VOLUME: + case ARIZONA_HPLP3MIX_INPUT_4_SOURCE: + case ARIZONA_HPLP3MIX_INPUT_4_VOLUME: + case ARIZONA_HPLP4MIX_INPUT_1_SOURCE: + case ARIZONA_HPLP4MIX_INPUT_1_VOLUME: + case ARIZONA_HPLP4MIX_INPUT_2_SOURCE: + case ARIZONA_HPLP4MIX_INPUT_2_VOLUME: + case ARIZONA_HPLP4MIX_INPUT_3_SOURCE: + case ARIZONA_HPLP4MIX_INPUT_3_VOLUME: + case ARIZONA_HPLP4MIX_INPUT_4_SOURCE: + case ARIZONA_HPLP4MIX_INPUT_4_VOLUME: + case ARIZONA_DSP1LMIX_INPUT_1_SOURCE: + case ARIZONA_DSP1LMIX_INPUT_1_VOLUME: + case ARIZONA_DSP1LMIX_INPUT_2_SOURCE: + case ARIZONA_DSP1LMIX_INPUT_2_VOLUME: + case ARIZONA_DSP1LMIX_INPUT_3_SOURCE: + case ARIZONA_DSP1LMIX_INPUT_3_VOLUME: + case ARIZONA_DSP1LMIX_INPUT_4_SOURCE: + case ARIZONA_DSP1LMIX_INPUT_4_VOLUME: + case ARIZONA_DSP1RMIX_INPUT_1_SOURCE: + case ARIZONA_DSP1RMIX_INPUT_1_VOLUME: + case ARIZONA_DSP1RMIX_INPUT_2_SOURCE: + case ARIZONA_DSP1RMIX_INPUT_2_VOLUME: + case ARIZONA_DSP1RMIX_INPUT_3_SOURCE: + case ARIZONA_DSP1RMIX_INPUT_3_VOLUME: + case ARIZONA_DSP1RMIX_INPUT_4_SOURCE: + case ARIZONA_DSP1RMIX_INPUT_4_VOLUME: + case ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE: + case ARIZONA_DSP1AUX2MIX_INPUT_1_SOURCE: + case ARIZONA_DSP1AUX3MIX_INPUT_1_SOURCE: + case ARIZONA_DSP1AUX4MIX_INPUT_1_SOURCE: + case ARIZONA_DSP1AUX5MIX_INPUT_1_SOURCE: + case ARIZONA_DSP1AUX6MIX_INPUT_1_SOURCE: + case ARIZONA_ASRC1LMIX_INPUT_1_SOURCE: + case ARIZONA_ASRC1RMIX_INPUT_1_SOURCE: + case ARIZONA_ASRC2LMIX_INPUT_1_SOURCE: + case ARIZONA_ASRC2RMIX_INPUT_1_SOURCE: + case ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE: + case ARIZONA_GPIO1_CTRL: + case ARIZONA_GPIO2_CTRL: + case ARIZONA_GPIO3_CTRL: + case ARIZONA_GPIO4_CTRL: + case ARIZONA_GPIO5_CTRL: + case ARIZONA_IRQ_CTRL_1: + case ARIZONA_GPIO_DEBOUNCE_CONFIG: + case ARIZONA_MISC_PAD_CTRL_1: + case ARIZONA_MISC_PAD_CTRL_2: + case ARIZONA_MISC_PAD_CTRL_3: + case ARIZONA_MISC_PAD_CTRL_4: + case ARIZONA_MISC_PAD_CTRL_5: + case ARIZONA_MISC_PAD_CTRL_6: + case ARIZONA_INTERRUPT_STATUS_1: + case ARIZONA_INTERRUPT_STATUS_2: + case ARIZONA_INTERRUPT_STATUS_3: + case ARIZONA_INTERRUPT_STATUS_4: + case ARIZONA_INTERRUPT_STATUS_5: + case ARIZONA_INTERRUPT_STATUS_1_MASK: + case ARIZONA_INTERRUPT_STATUS_2_MASK: + case ARIZONA_INTERRUPT_STATUS_3_MASK: + case ARIZONA_INTERRUPT_STATUS_4_MASK: + case ARIZONA_INTERRUPT_STATUS_5_MASK: + case ARIZONA_INTERRUPT_CONTROL: + case ARIZONA_IRQ2_STATUS_1: + case ARIZONA_IRQ2_STATUS_2: + case ARIZONA_IRQ2_STATUS_3: + case ARIZONA_IRQ2_STATUS_4: + case ARIZONA_IRQ2_STATUS_5: + case ARIZONA_IRQ2_STATUS_1_MASK: + case ARIZONA_IRQ2_STATUS_2_MASK: + case ARIZONA_IRQ2_STATUS_3_MASK: + case ARIZONA_IRQ2_STATUS_4_MASK: + case ARIZONA_IRQ2_STATUS_5_MASK: + case ARIZONA_IRQ2_CONTROL: + case ARIZONA_INTERRUPT_RAW_STATUS_2: + case ARIZONA_INTERRUPT_RAW_STATUS_3: + case ARIZONA_INTERRUPT_RAW_STATUS_4: + case ARIZONA_INTERRUPT_RAW_STATUS_5: + case ARIZONA_INTERRUPT_RAW_STATUS_6: + case ARIZONA_INTERRUPT_RAW_STATUS_7: + case ARIZONA_INTERRUPT_RAW_STATUS_8: + case ARIZONA_IRQ_PIN_STATUS: + case ARIZONA_ADSP2_IRQ0: + case ARIZONA_AOD_WKUP_AND_TRIG: + case ARIZONA_AOD_IRQ1: + case ARIZONA_AOD_IRQ2: + case ARIZONA_AOD_IRQ_MASK_IRQ1: + case ARIZONA_AOD_IRQ_MASK_IRQ2: + case ARIZONA_AOD_IRQ_RAW_STATUS: + case ARIZONA_JACK_DETECT_DEBOUNCE: + case ARIZONA_FX_CTRL1: + case ARIZONA_FX_CTRL2: + case ARIZONA_EQ1_1: + case ARIZONA_EQ1_2: + case ARIZONA_EQ1_3: + case ARIZONA_EQ1_4: + case ARIZONA_EQ1_5: + case ARIZONA_EQ1_6: + case ARIZONA_EQ1_7: + case ARIZONA_EQ1_8: + case ARIZONA_EQ1_9: + case ARIZONA_EQ1_10: + case ARIZONA_EQ1_11: + case ARIZONA_EQ1_12: + case ARIZONA_EQ1_13: + case ARIZONA_EQ1_14: + case ARIZONA_EQ1_15: + case ARIZONA_EQ1_16: + case ARIZONA_EQ1_17: + case ARIZONA_EQ1_18: + case ARIZONA_EQ1_19: + case ARIZONA_EQ1_20: + case ARIZONA_EQ1_21: + case ARIZONA_EQ2_1: + case ARIZONA_EQ2_2: + case ARIZONA_EQ2_3: + case ARIZONA_EQ2_4: + case ARIZONA_EQ2_5: + case ARIZONA_EQ2_6: + case ARIZONA_EQ2_7: + case ARIZONA_EQ2_8: + case ARIZONA_EQ2_9: + case ARIZONA_EQ2_10: + case ARIZONA_EQ2_11: + case ARIZONA_EQ2_12: + case ARIZONA_EQ2_13: + case ARIZONA_EQ2_14: + case ARIZONA_EQ2_15: + case ARIZONA_EQ2_16: + case ARIZONA_EQ2_17: + case ARIZONA_EQ2_18: + case ARIZONA_EQ2_19: + case ARIZONA_EQ2_20: + case ARIZONA_EQ2_21: + case ARIZONA_EQ3_1: + case ARIZONA_EQ3_2: + case ARIZONA_EQ3_3: + case ARIZONA_EQ3_4: + case ARIZONA_EQ3_5: + case ARIZONA_EQ3_6: + case ARIZONA_EQ3_7: + case ARIZONA_EQ3_8: + case ARIZONA_EQ3_9: + case ARIZONA_EQ3_10: + case ARIZONA_EQ3_11: + case ARIZONA_EQ3_12: + case ARIZONA_EQ3_13: + case ARIZONA_EQ3_14: + case ARIZONA_EQ3_15: + case ARIZONA_EQ3_16: + case ARIZONA_EQ3_17: + case ARIZONA_EQ3_18: + case ARIZONA_EQ3_19: + case ARIZONA_EQ3_20: + case ARIZONA_EQ3_21: + case ARIZONA_EQ4_1: + case ARIZONA_EQ4_2: + case ARIZONA_EQ4_3: + case ARIZONA_EQ4_4: + case ARIZONA_EQ4_5: + case ARIZONA_EQ4_6: + case ARIZONA_EQ4_7: + case ARIZONA_EQ4_8: + case ARIZONA_EQ4_9: + case ARIZONA_EQ4_10: + case ARIZONA_EQ4_11: + case ARIZONA_EQ4_12: + case ARIZONA_EQ4_13: + case ARIZONA_EQ4_14: + case ARIZONA_EQ4_15: + case ARIZONA_EQ4_16: + case ARIZONA_EQ4_17: + case ARIZONA_EQ4_18: + case ARIZONA_EQ4_19: + case ARIZONA_EQ4_20: + case ARIZONA_EQ4_21: + case ARIZONA_DRC1_CTRL1: + case ARIZONA_DRC1_CTRL2: + case ARIZONA_DRC1_CTRL3: + case ARIZONA_DRC1_CTRL4: + case ARIZONA_DRC1_CTRL5: + case ARIZONA_DRC2_CTRL1: + case ARIZONA_DRC2_CTRL2: + case ARIZONA_DRC2_CTRL3: + case ARIZONA_DRC2_CTRL4: + case ARIZONA_DRC2_CTRL5: + case ARIZONA_HPLPF1_1: + case ARIZONA_HPLPF1_2: + case ARIZONA_HPLPF2_1: + case ARIZONA_HPLPF2_2: + case ARIZONA_HPLPF3_1: + case ARIZONA_HPLPF3_2: + case ARIZONA_HPLPF4_1: + case ARIZONA_HPLPF4_2: + case ARIZONA_ASRC_ENABLE: + case ARIZONA_ASRC_RATE1: + case ARIZONA_ASRC_RATE2: + case ARIZONA_ISRC_1_CTRL_1: + case ARIZONA_ISRC_1_CTRL_2: + case ARIZONA_ISRC_1_CTRL_3: + case ARIZONA_ISRC_2_CTRL_1: + case ARIZONA_ISRC_2_CTRL_2: + case ARIZONA_ISRC_2_CTRL_3: + case ARIZONA_ISRC_3_CTRL_1: + case ARIZONA_ISRC_3_CTRL_2: + case ARIZONA_ISRC_3_CTRL_3: + case ARIZONA_DSP1_CONTROL_1: + case ARIZONA_DSP1_CLOCKING_1: + case ARIZONA_DSP1_STATUS_1: + case ARIZONA_DSP1_STATUS_2: + return true; + default: + return false; + } +} + +static bool wm5102_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ARIZONA_SOFTWARE_RESET: + case ARIZONA_DEVICE_REVISION: + case ARIZONA_OUTPUT_STATUS_1: + case ARIZONA_SAMPLE_RATE_1_STATUS: + case ARIZONA_SAMPLE_RATE_2_STATUS: + case ARIZONA_SAMPLE_RATE_3_STATUS: + case ARIZONA_HAPTICS_STATUS: + case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS: + case ARIZONA_FX_CTRL2: + case ARIZONA_INTERRUPT_STATUS_1: + case ARIZONA_INTERRUPT_STATUS_2: + case ARIZONA_INTERRUPT_STATUS_3: + case ARIZONA_INTERRUPT_STATUS_4: + case ARIZONA_INTERRUPT_STATUS_5: + case ARIZONA_IRQ2_STATUS_1: + case ARIZONA_IRQ2_STATUS_2: + case ARIZONA_IRQ2_STATUS_3: + case ARIZONA_IRQ2_STATUS_4: + case ARIZONA_IRQ2_STATUS_5: + case ARIZONA_INTERRUPT_RAW_STATUS_2: + case ARIZONA_INTERRUPT_RAW_STATUS_3: + case ARIZONA_INTERRUPT_RAW_STATUS_4: + case ARIZONA_INTERRUPT_RAW_STATUS_5: + case ARIZONA_INTERRUPT_RAW_STATUS_6: + case ARIZONA_INTERRUPT_RAW_STATUS_7: + case ARIZONA_INTERRUPT_RAW_STATUS_8: + case ARIZONA_IRQ_PIN_STATUS: + case ARIZONA_AOD_WKUP_AND_TRIG: + case ARIZONA_AOD_IRQ1: + case ARIZONA_AOD_IRQ2: + case ARIZONA_AOD_IRQ_RAW_STATUS: + case ARIZONA_DSP1_STATUS_1: + case ARIZONA_DSP1_STATUS_2: + case ARIZONA_HEADPHONE_DETECT_2: + case ARIZONA_MIC_DETECT_3: + return true; + default: + return false; + } +} + +const struct regmap_config wm5102_spi_regmap = { + .reg_bits = 32, + .pad_bits = 16, + .val_bits = 16, + + .max_register = ARIZONA_DSP1_STATUS_2, + .readable_reg = wm5102_readable_register, + .volatile_reg = wm5102_volatile_register, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wm5102_reg_default, + .num_reg_defaults = ARRAY_SIZE(wm5102_reg_default), +}; +EXPORT_SYMBOL_GPL(wm5102_spi_regmap); + +const struct regmap_config wm5102_i2c_regmap = { + .reg_bits = 32, + .val_bits = 16, + + .max_register = ARIZONA_DSP1_STATUS_2, + .readable_reg = wm5102_readable_register, + .volatile_reg = wm5102_volatile_register, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wm5102_reg_default, + .num_reg_defaults = ARRAY_SIZE(wm5102_reg_default), +}; +EXPORT_SYMBOL_GPL(wm5102_i2c_regmap); diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c new file mode 100644 index 000000000000..bd8782c8896b --- /dev/null +++ b/drivers/mfd/wm5110-tables.c @@ -0,0 +1,2281 @@ +/* + * wm5110-tables.c -- WM5110 data tables + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/registers.h> + +#include "arizona.h" + +#define WM5110_NUM_AOD_ISR 2 +#define WM5110_NUM_ISR 5 + +static const struct reg_default wm5110_reva_patch[] = { + { 0x80, 0x3 }, + { 0x44, 0x20 }, + { 0x45, 0x40 }, + { 0x46, 0x60 }, + { 0x47, 0x80 }, + { 0x48, 0xa0 }, + { 0x51, 0x13 }, + { 0x52, 0x33 }, + { 0x53, 0x53 }, + { 0x54, 0x73 }, + { 0x55, 0x75 }, + { 0x56, 0xb3 }, + { 0x2ef, 0x124 }, + { 0x2ef, 0x124 }, + { 0x2f0, 0x124 }, + { 0x2f0, 0x124 }, + { 0x2f1, 0x124 }, + { 0x2f1, 0x124 }, + { 0x2f2, 0x124 }, + { 0x2f2, 0x124 }, + { 0x2f3, 0x124 }, + { 0x2f3, 0x124 }, + { 0x2f4, 0x124 }, + { 0x2f4, 0x124 }, + { 0x2eb, 0x60 }, + { 0x2ec, 0x60 }, + { 0x2ed, 0x60 }, + { 0xc30, 0x3e3e }, + { 0xc30, 0x3e3e }, + { 0xc31, 0x3e }, + { 0xc32, 0x3e3e }, + { 0xc32, 0x3e3e }, + { 0xc33, 0x3e3e }, + { 0xc33, 0x3e3e }, + { 0xc34, 0x3e3e }, + { 0xc34, 0x3e3e }, + { 0xc35, 0x3e3e }, + { 0xc35, 0x3e3e }, + { 0xc36, 0x3e3e }, + { 0xc36, 0x3e3e }, + { 0xc37, 0x3e3e }, + { 0xc37, 0x3e3e }, + { 0xc38, 0x3e3e }, + { 0xc38, 0x3e3e }, + { 0xc30, 0x3e3e }, + { 0xc30, 0x3e3e }, + { 0xc39, 0x3e3e }, + { 0xc39, 0x3e3e }, + { 0xc3a, 0x3e3e }, + { 0xc3a, 0x3e3e }, + { 0xc3b, 0x3e3e }, + { 0xc3b, 0x3e3e }, + { 0xc3c, 0x3e }, + { 0x201, 0x18a5 }, + { 0x201, 0x18a5 }, + { 0x201, 0x18a5 }, + { 0x202, 0x4100 }, + { 0x460, 0xc00 }, + { 0x461, 0x8000 }, + { 0x462, 0xc01 }, + { 0x463, 0x50f0 }, + { 0x464, 0xc01 }, + { 0x465, 0x4820 }, + { 0x466, 0xc01 }, + { 0x466, 0xc01 }, + { 0x467, 0x4040 }, + { 0x468, 0xc01 }, + { 0x468, 0xc01 }, + { 0x469, 0x3940 }, + { 0x46a, 0xc01 }, + { 0x46a, 0xc01 }, + { 0x46a, 0xc01 }, + { 0x46b, 0x3310 }, + { 0x46c, 0x801 }, + { 0x46c, 0x801 }, + { 0x46d, 0x2d80 }, + { 0x46e, 0x801 }, + { 0x46e, 0x801 }, + { 0x46f, 0x2890 }, + { 0x470, 0x801 }, + { 0x470, 0x801 }, + { 0x471, 0x1990 }, + { 0x472, 0x801 }, + { 0x472, 0x801 }, + { 0x473, 0x1450 }, + { 0x474, 0x801 }, + { 0x474, 0x801 }, + { 0x474, 0x801 }, + { 0x475, 0x1020 }, + { 0x476, 0x801 }, + { 0x476, 0x801 }, + { 0x476, 0x801 }, + { 0x477, 0xcd0 }, + { 0x478, 0x806 }, + { 0x478, 0x806 }, + { 0x479, 0xa30 }, + { 0x47a, 0x806 }, + { 0x47a, 0x806 }, + { 0x47b, 0x810 }, + { 0x47c, 0x80e }, + { 0x47c, 0x80e }, + { 0x47d, 0x510 }, + { 0x47e, 0x81f }, + { 0x47e, 0x81f }, + { 0x2DB, 0x0A00 }, + { 0x2DD, 0x0023 }, + { 0x2DF, 0x0102 }, + { 0x80, 0x0 }, + { 0xC20, 0x0002 }, + { 0x209, 0x002A }, +}; + +/* We use a function so we can use ARRAY_SIZE() */ +int wm5110_patch(struct arizona *arizona) +{ + switch (arizona->rev) { + case 0: + case 1: + return regmap_register_patch(arizona->regmap, + wm5110_reva_patch, + ARRAY_SIZE(wm5110_reva_patch)); + default: + return 0; + } +} +EXPORT_SYMBOL_GPL(wm5110_patch); + +static const struct regmap_irq wm5110_aod_irqs[ARIZONA_NUM_IRQ] = { + [ARIZONA_IRQ_GP5_FALL] = { .mask = ARIZONA_GP5_FALL_EINT1 }, + [ARIZONA_IRQ_GP5_RISE] = { .mask = ARIZONA_GP5_RISE_EINT1 }, + [ARIZONA_IRQ_JD_FALL] = { .mask = ARIZONA_JD1_FALL_EINT1 }, + [ARIZONA_IRQ_JD_RISE] = { .mask = ARIZONA_JD1_RISE_EINT1 }, +}; + +const struct regmap_irq_chip wm5110_aod = { + .name = "wm5110 AOD", + .status_base = ARIZONA_AOD_IRQ1, + .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1, + .ack_base = ARIZONA_AOD_IRQ1, + .wake_base = ARIZONA_WAKE_CONTROL, + .num_regs = 1, + .irqs = wm5110_aod_irqs, + .num_irqs = ARRAY_SIZE(wm5110_aod_irqs), +}; +EXPORT_SYMBOL_GPL(wm5110_aod); + +static const struct regmap_irq wm5110_irqs[ARIZONA_NUM_IRQ] = { + [ARIZONA_IRQ_GP4] = { .reg_offset = 0, .mask = ARIZONA_GP4_EINT1 }, + [ARIZONA_IRQ_GP3] = { .reg_offset = 0, .mask = ARIZONA_GP3_EINT1 }, + [ARIZONA_IRQ_GP2] = { .reg_offset = 0, .mask = ARIZONA_GP2_EINT1 }, + [ARIZONA_IRQ_GP1] = { .reg_offset = 0, .mask = ARIZONA_GP1_EINT1 }, + + [ARIZONA_IRQ_DSP4_RAM_RDY] = { + .reg_offset = 1, .mask = ARIZONA_DSP4_RAM_RDY_EINT1 + }, + [ARIZONA_IRQ_DSP3_RAM_RDY] = { + .reg_offset = 1, .mask = ARIZONA_DSP3_RAM_RDY_EINT1 + }, + [ARIZONA_IRQ_DSP2_RAM_RDY] = { + .reg_offset = 1, .mask = ARIZONA_DSP2_RAM_RDY_EINT1 + }, + [ARIZONA_IRQ_DSP1_RAM_RDY] = { + .reg_offset = 1, .mask = ARIZONA_DSP1_RAM_RDY_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ8] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ8_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ7] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ7_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ6] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ6_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ5] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ5_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ4] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ4_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ3] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ3_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ2] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ2_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ1] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ1_EINT1 + }, + + [ARIZONA_IRQ_SPK_SHUTDOWN_WARN] = { + .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_WARN_EINT1 + }, + [ARIZONA_IRQ_SPK_SHUTDOWN] = { + .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_EINT1 + }, + [ARIZONA_IRQ_HPDET] = { + .reg_offset = 2, .mask = ARIZONA_HPDET_EINT1 + }, + [ARIZONA_IRQ_MICDET] = { + .reg_offset = 2, .mask = ARIZONA_MICDET_EINT1 + }, + [ARIZONA_IRQ_WSEQ_DONE] = { + .reg_offset = 2, .mask = ARIZONA_WSEQ_DONE_EINT1 + }, + [ARIZONA_IRQ_DRC2_SIG_DET] = { + .reg_offset = 2, .mask = ARIZONA_DRC2_SIG_DET_EINT1 + }, + [ARIZONA_IRQ_DRC1_SIG_DET] = { + .reg_offset = 2, .mask = ARIZONA_DRC1_SIG_DET_EINT1 + }, + [ARIZONA_IRQ_ASRC2_LOCK] = { + .reg_offset = 2, .mask = ARIZONA_ASRC2_LOCK_EINT1 + }, + [ARIZONA_IRQ_ASRC1_LOCK] = { + .reg_offset = 2, .mask = ARIZONA_ASRC1_LOCK_EINT1 + }, + [ARIZONA_IRQ_UNDERCLOCKED] = { + .reg_offset = 2, .mask = ARIZONA_UNDERCLOCKED_EINT1 + }, + [ARIZONA_IRQ_OVERCLOCKED] = { + .reg_offset = 2, .mask = ARIZONA_OVERCLOCKED_EINT1 + }, + [ARIZONA_IRQ_FLL2_LOCK] = { + .reg_offset = 2, .mask = ARIZONA_FLL2_LOCK_EINT1 + }, + [ARIZONA_IRQ_FLL1_LOCK] = { + .reg_offset = 2, .mask = ARIZONA_FLL1_LOCK_EINT1 + }, + [ARIZONA_IRQ_CLKGEN_ERR] = { + .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_EINT1 + }, + [ARIZONA_IRQ_CLKGEN_ERR_ASYNC] = { + .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_ASYNC_EINT1 + }, + + [ARIZONA_IRQ_ASRC_CFG_ERR] = { + .reg_offset = 3, .mask = ARIZONA_ASRC_CFG_ERR_EINT1 + }, + [ARIZONA_IRQ_AIF3_ERR] = { + .reg_offset = 3, .mask = ARIZONA_AIF3_ERR_EINT1 + }, + [ARIZONA_IRQ_AIF2_ERR] = { + .reg_offset = 3, .mask = ARIZONA_AIF2_ERR_EINT1 + }, + [ARIZONA_IRQ_AIF1_ERR] = { + .reg_offset = 3, .mask = ARIZONA_AIF1_ERR_EINT1 + }, + [ARIZONA_IRQ_CTRLIF_ERR] = { + .reg_offset = 3, .mask = ARIZONA_CTRLIF_ERR_EINT1 + }, + [ARIZONA_IRQ_MIXER_DROPPED_SAMPLES] = { + .reg_offset = 3, .mask = ARIZONA_MIXER_DROPPED_SAMPLE_EINT1 + }, + [ARIZONA_IRQ_ASYNC_CLK_ENA_LOW] = { + .reg_offset = 3, .mask = ARIZONA_ASYNC_CLK_ENA_LOW_EINT1 + }, + [ARIZONA_IRQ_SYSCLK_ENA_LOW] = { + .reg_offset = 3, .mask = ARIZONA_SYSCLK_ENA_LOW_EINT1 + }, + [ARIZONA_IRQ_ISRC1_CFG_ERR] = { + .reg_offset = 3, .mask = ARIZONA_ISRC1_CFG_ERR_EINT1 + }, + [ARIZONA_IRQ_ISRC2_CFG_ERR] = { + .reg_offset = 3, .mask = ARIZONA_ISRC2_CFG_ERR_EINT1 + }, + + [ARIZONA_IRQ_BOOT_DONE] = { + .reg_offset = 4, .mask = ARIZONA_BOOT_DONE_EINT1 + }, + [ARIZONA_IRQ_DCS_DAC_DONE] = { + .reg_offset = 4, .mask = ARIZONA_DCS_DAC_DONE_EINT1 + }, + [ARIZONA_IRQ_DCS_HP_DONE] = { + .reg_offset = 4, .mask = ARIZONA_DCS_HP_DONE_EINT1 + }, + [ARIZONA_IRQ_FLL2_CLOCK_OK] = { + .reg_offset = 4, .mask = ARIZONA_FLL2_CLOCK_OK_EINT1 + }, + [ARIZONA_IRQ_FLL1_CLOCK_OK] = { + .reg_offset = 4, .mask = ARIZONA_FLL1_CLOCK_OK_EINT1 + }, +}; + +const struct regmap_irq_chip wm5110_irq = { + .name = "wm5110 IRQ", + .status_base = ARIZONA_INTERRUPT_STATUS_1, + .mask_base = ARIZONA_INTERRUPT_STATUS_1_MASK, + .ack_base = ARIZONA_INTERRUPT_STATUS_1, + .num_regs = 5, + .irqs = wm5110_irqs, + .num_irqs = ARRAY_SIZE(wm5110_irqs), +}; +EXPORT_SYMBOL_GPL(wm5110_irq); + +static const struct reg_default wm5110_reg_default[] = { + { 0x00000008, 0x0019 }, /* R8 - Ctrl IF SPI CFG 1 */ + { 0x00000009, 0x0001 }, /* R9 - Ctrl IF I2C1 CFG 1 */ + { 0x0000000A, 0x0001 }, /* R10 - Ctrl IF I2C2 CFG 1 */ + { 0x0000000B, 0x0036 }, /* R11 - Ctrl IF I2C1 CFG 2 */ + { 0x0000000C, 0x0036 }, /* R12 - Ctrl IF I2C2 CFG 2 */ + { 0x00000016, 0x0000 }, /* R22 - Write Sequencer Ctrl 0 */ + { 0x00000017, 0x0000 }, /* R23 - Write Sequencer Ctrl 1 */ + { 0x00000018, 0x0000 }, /* R24 - Write Sequencer Ctrl 2 */ + { 0x00000020, 0x0000 }, /* R32 - Tone Generator 1 */ + { 0x00000021, 0x1000 }, /* R33 - Tone Generator 2 */ + { 0x00000022, 0x0000 }, /* R34 - Tone Generator 3 */ + { 0x00000023, 0x1000 }, /* R35 - Tone Generator 4 */ + { 0x00000024, 0x0000 }, /* R36 - Tone Generator 5 */ + { 0x00000030, 0x0000 }, /* R48 - PWM Drive 1 */ + { 0x00000031, 0x0100 }, /* R49 - PWM Drive 2 */ + { 0x00000032, 0x0100 }, /* R50 - PWM Drive 3 */ + { 0x00000040, 0x0000 }, /* R64 - Wake control */ + { 0x00000041, 0x0000 }, /* R65 - Sequence control */ + { 0x00000061, 0x01FF }, /* R97 - Sample Rate Sequence Select 1 */ + { 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */ + { 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */ + { 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */ + { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */ + { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */ + { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */ + { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */ + { 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */ + { 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */ + { 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */ + { 0x00000092, 0x0000 }, /* R146 - Haptics phase 1 intensity */ + { 0x00000093, 0x0000 }, /* R147 - Haptics phase 1 duration */ + { 0x00000094, 0x0000 }, /* R148 - Haptics phase 2 intensity */ + { 0x00000095, 0x0000 }, /* R149 - Haptics phase 2 duration */ + { 0x00000096, 0x0000 }, /* R150 - Haptics phase 3 intensity */ + { 0x00000097, 0x0000 }, /* R151 - Haptics phase 3 duration */ + { 0x00000100, 0x0001 }, /* R256 - Clock 32k 1 */ + { 0x00000101, 0x0504 }, /* R257 - System Clock 1 */ + { 0x00000102, 0x0011 }, /* R258 - Sample rate 1 */ + { 0x00000103, 0x0011 }, /* R259 - Sample rate 2 */ + { 0x00000104, 0x0011 }, /* R260 - Sample rate 3 */ + { 0x00000112, 0x0305 }, /* R274 - Async clock 1 */ + { 0x00000113, 0x0011 }, /* R275 - Async sample rate 1 */ + { 0x00000149, 0x0000 }, /* R329 - Output system clock */ + { 0x0000014A, 0x0000 }, /* R330 - Output async clock */ + { 0x00000152, 0x0000 }, /* R338 - Rate Estimator 1 */ + { 0x00000153, 0x0000 }, /* R339 - Rate Estimator 2 */ + { 0x00000154, 0x0000 }, /* R340 - Rate Estimator 3 */ + { 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */ + { 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */ + { 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */ + { 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */ + { 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */ + { 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */ + { 0x00000175, 0x0006 }, /* R373 - FLL1 Control 5 */ + { 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */ + { 0x00000177, 0x0281 }, /* R375 - FLL1 Loop Filter Test 1 */ + { 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */ + { 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */ + { 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */ + { 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */ + { 0x00000184, 0x0000 }, /* R388 - FLL1 Synchroniser 4 */ + { 0x00000185, 0x0000 }, /* R389 - FLL1 Synchroniser 5 */ + { 0x00000186, 0x0000 }, /* R390 - FLL1 Synchroniser 6 */ + { 0x00000189, 0x0000 }, /* R393 - FLL1 Spread Spectrum */ + { 0x0000018A, 0x0004 }, /* R394 - FLL1 GPIO Clock */ + { 0x00000191, 0x0000 }, /* R401 - FLL2 Control 1 */ + { 0x00000192, 0x0008 }, /* R402 - FLL2 Control 2 */ + { 0x00000193, 0x0018 }, /* R403 - FLL2 Control 3 */ + { 0x00000194, 0x007D }, /* R404 - FLL2 Control 4 */ + { 0x00000195, 0x000C }, /* R405 - FLL2 Control 5 */ + { 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */ + { 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */ + { 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */ + { 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */ + { 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */ + { 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */ + { 0x000001A4, 0x0000 }, /* R420 - FLL2 Synchroniser 4 */ + { 0x000001A5, 0x0000 }, /* R421 - FLL2 Synchroniser 5 */ + { 0x000001A6, 0x0000 }, /* R422 - FLL2 Synchroniser 6 */ + { 0x000001A9, 0x0000 }, /* R425 - FLL2 Spread Spectrum */ + { 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */ + { 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */ + { 0x00000210, 0x0184 }, /* R528 - LDO1 Control 1 */ + { 0x00000213, 0x0344 }, /* R531 - LDO2 Control 1 */ + { 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */ + { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */ + { 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */ + { 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */ + { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */ + { 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */ + { 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */ + { 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */ + { 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */ + { 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */ + { 0x00000300, 0x0000 }, /* R768 - Input Enables */ + { 0x00000308, 0x0000 }, /* R776 - Input Rate */ + { 0x00000309, 0x0022 }, /* R777 - Input Volume Ramp */ + { 0x00000310, 0x2080 }, /* R784 - IN1L Control */ + { 0x00000311, 0x0180 }, /* R785 - ADC Digital Volume 1L */ + { 0x00000312, 0x0000 }, /* R786 - DMIC1L Control */ + { 0x00000314, 0x0080 }, /* R788 - IN1R Control */ + { 0x00000315, 0x0180 }, /* R789 - ADC Digital Volume 1R */ + { 0x00000316, 0x0000 }, /* R790 - DMIC1R Control */ + { 0x00000318, 0x2080 }, /* R792 - IN2L Control */ + { 0x00000319, 0x0180 }, /* R793 - ADC Digital Volume 2L */ + { 0x0000031A, 0x0000 }, /* R794 - DMIC2L Control */ + { 0x0000031C, 0x0080 }, /* R796 - IN2R Control */ + { 0x0000031D, 0x0180 }, /* R797 - ADC Digital Volume 2R */ + { 0x0000031E, 0x0000 }, /* R798 - DMIC2R Control */ + { 0x00000320, 0x2080 }, /* R800 - IN3L Control */ + { 0x00000321, 0x0180 }, /* R801 - ADC Digital Volume 3L */ + { 0x00000322, 0x0000 }, /* R802 - DMIC3L Control */ + { 0x00000324, 0x0080 }, /* R804 - IN3R Control */ + { 0x00000325, 0x0180 }, /* R805 - ADC Digital Volume 3R */ + { 0x00000326, 0x0000 }, /* R806 - DMIC3R Control */ + { 0x00000328, 0x2000 }, /* R808 - IN4L Control */ + { 0x00000329, 0x0180 }, /* R809 - ADC Digital Volume 4L */ + { 0x0000032A, 0x0000 }, /* R810 - DMIC4L Control */ + { 0x0000032D, 0x0180 }, /* R813 - ADC Digital Volume 4R */ + { 0x0000032E, 0x0000 }, /* R814 - DMIC4R Control */ + { 0x00000400, 0x0000 }, /* R1024 - Output Enables 1 */ + { 0x00000408, 0x0000 }, /* R1032 - Output Rate 1 */ + { 0x00000409, 0x0022 }, /* R1033 - Output Volume Ramp */ + { 0x00000410, 0x0080 }, /* R1040 - Output Path Config 1L */ + { 0x00000411, 0x0180 }, /* R1041 - DAC Digital Volume 1L */ + { 0x00000412, 0x0080 }, /* R1042 - DAC Volume Limit 1L */ + { 0x00000413, 0x0001 }, /* R1043 - Noise Gate Select 1L */ + { 0x00000414, 0x0080 }, /* R1044 - Output Path Config 1R */ + { 0x00000415, 0x0180 }, /* R1045 - DAC Digital Volume 1R */ + { 0x00000416, 0x0080 }, /* R1046 - DAC Volume Limit 1R */ + { 0x00000417, 0x0002 }, /* R1047 - Noise Gate Select 1R */ + { 0x00000418, 0x0080 }, /* R1048 - Output Path Config 2L */ + { 0x00000419, 0x0180 }, /* R1049 - DAC Digital Volume 2L */ + { 0x0000041A, 0x0080 }, /* R1050 - DAC Volume Limit 2L */ + { 0x0000041B, 0x0004 }, /* R1051 - Noise Gate Select 2L */ + { 0x0000041C, 0x0080 }, /* R1052 - Output Path Config 2R */ + { 0x0000041D, 0x0180 }, /* R1053 - DAC Digital Volume 2R */ + { 0x0000041E, 0x0080 }, /* R1054 - DAC Volume Limit 2R */ + { 0x0000041F, 0x0008 }, /* R1055 - Noise Gate Select 2R */ + { 0x00000420, 0x0080 }, /* R1056 - Output Path Config 3L */ + { 0x00000421, 0x0180 }, /* R1057 - DAC Digital Volume 3L */ + { 0x00000422, 0x0080 }, /* R1058 - DAC Volume Limit 3L */ + { 0x00000423, 0x0010 }, /* R1059 - Noise Gate Select 3L */ + { 0x00000424, 0x0080 }, /* R1060 - Output Path Config 3R */ + { 0x00000425, 0x0180 }, /* R1061 - DAC Digital Volume 3R */ + { 0x00000426, 0x0080 }, /* R1062 - DAC Volume Limit 3R */ + { 0x00000427, 0x0020 }, /* R1063 - Noise Gate Select 3R */ + { 0x00000428, 0x0000 }, /* R1064 - Output Path Config 4L */ + { 0x00000429, 0x0180 }, /* R1065 - DAC Digital Volume 4L */ + { 0x0000042A, 0x0080 }, /* R1066 - Out Volume 4L */ + { 0x0000042B, 0x0040 }, /* R1067 - Noise Gate Select 4L */ + { 0x0000042C, 0x0000 }, /* R1068 - Output Path Config 4R */ + { 0x0000042D, 0x0180 }, /* R1069 - DAC Digital Volume 4R */ + { 0x0000042E, 0x0080 }, /* R1070 - Out Volume 4R */ + { 0x0000042F, 0x0080 }, /* R1071 - Noise Gate Select 4R */ + { 0x00000430, 0x0000 }, /* R1072 - Output Path Config 5L */ + { 0x00000431, 0x0180 }, /* R1073 - DAC Digital Volume 5L */ + { 0x00000432, 0x0080 }, /* R1074 - DAC Volume Limit 5L */ + { 0x00000433, 0x0100 }, /* R1075 - Noise Gate Select 5L */ + { 0x00000434, 0x0000 }, /* R1076 - Output Path Config 5R */ + { 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */ + { 0x00000436, 0x0080 }, /* R1078 - DAC Volume Limit 5R */ + { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */ + { 0x00000438, 0x0000 }, /* R1080 - Output Path Config 6L */ + { 0x00000439, 0x0180 }, /* R1081 - DAC Digital Volume 6L */ + { 0x0000043A, 0x0080 }, /* R1082 - DAC Volume Limit 6L */ + { 0x0000043B, 0x0400 }, /* R1083 - Noise Gate Select 6L */ + { 0x0000043C, 0x0000 }, /* R1084 - Output Path Config 6R */ + { 0x0000043D, 0x0180 }, /* R1085 - DAC Digital Volume 6R */ + { 0x0000043E, 0x0080 }, /* R1086 - DAC Volume Limit 6R */ + { 0x0000043F, 0x0800 }, /* R1087 - Noise Gate Select 6R */ + { 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */ + { 0x00000458, 0x0001 }, /* R1112 - Noise Gate Control */ + { 0x00000480, 0x0040 }, /* R1152 - Class W ANC Threshold 1 */ + { 0x00000481, 0x0040 }, /* R1153 - Class W ANC Threshold 2 */ + { 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */ + { 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */ + { 0x00000492, 0x0069 }, /* R1170 - PDM SPK2 CTRL 1 */ + { 0x00000493, 0x0000 }, /* R1171 - PDM SPK2 CTRL 2 */ + { 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */ + { 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */ + { 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */ + { 0x00000503, 0x0000 }, /* R1283 - AIF1 Rate Ctrl */ + { 0x00000504, 0x0000 }, /* R1284 - AIF1 Format */ + { 0x00000505, 0x0040 }, /* R1285 - AIF1 Tx BCLK Rate */ + { 0x00000506, 0x0040 }, /* R1286 - AIF1 Rx BCLK Rate */ + { 0x00000507, 0x1818 }, /* R1287 - AIF1 Frame Ctrl 1 */ + { 0x00000508, 0x1818 }, /* R1288 - AIF1 Frame Ctrl 2 */ + { 0x00000509, 0x0000 }, /* R1289 - AIF1 Frame Ctrl 3 */ + { 0x0000050A, 0x0001 }, /* R1290 - AIF1 Frame Ctrl 4 */ + { 0x0000050B, 0x0002 }, /* R1291 - AIF1 Frame Ctrl 5 */ + { 0x0000050C, 0x0003 }, /* R1292 - AIF1 Frame Ctrl 6 */ + { 0x0000050D, 0x0004 }, /* R1293 - AIF1 Frame Ctrl 7 */ + { 0x0000050E, 0x0005 }, /* R1294 - AIF1 Frame Ctrl 8 */ + { 0x0000050F, 0x0006 }, /* R1295 - AIF1 Frame Ctrl 9 */ + { 0x00000510, 0x0007 }, /* R1296 - AIF1 Frame Ctrl 10 */ + { 0x00000511, 0x0000 }, /* R1297 - AIF1 Frame Ctrl 11 */ + { 0x00000512, 0x0001 }, /* R1298 - AIF1 Frame Ctrl 12 */ + { 0x00000513, 0x0002 }, /* R1299 - AIF1 Frame Ctrl 13 */ + { 0x00000514, 0x0003 }, /* R1300 - AIF1 Frame Ctrl 14 */ + { 0x00000515, 0x0004 }, /* R1301 - AIF1 Frame Ctrl 15 */ + { 0x00000516, 0x0005 }, /* R1302 - AIF1 Frame Ctrl 16 */ + { 0x00000517, 0x0006 }, /* R1303 - AIF1 Frame Ctrl 17 */ + { 0x00000518, 0x0007 }, /* R1304 - AIF1 Frame Ctrl 18 */ + { 0x00000519, 0x0000 }, /* R1305 - AIF1 Tx Enables */ + { 0x0000051A, 0x0000 }, /* R1306 - AIF1 Rx Enables */ + { 0x00000540, 0x000C }, /* R1344 - AIF2 BCLK Ctrl */ + { 0x00000541, 0x0008 }, /* R1345 - AIF2 Tx Pin Ctrl */ + { 0x00000542, 0x0000 }, /* R1346 - AIF2 Rx Pin Ctrl */ + { 0x00000543, 0x0000 }, /* R1347 - AIF2 Rate Ctrl */ + { 0x00000544, 0x0000 }, /* R1348 - AIF2 Format */ + { 0x00000545, 0x0040 }, /* R1349 - AIF2 Tx BCLK Rate */ + { 0x00000546, 0x0040 }, /* R1350 - AIF2 Rx BCLK Rate */ + { 0x00000547, 0x1818 }, /* R1351 - AIF2 Frame Ctrl 1 */ + { 0x00000548, 0x1818 }, /* R1352 - AIF2 Frame Ctrl 2 */ + { 0x00000549, 0x0000 }, /* R1353 - AIF2 Frame Ctrl 3 */ + { 0x0000054A, 0x0001 }, /* R1354 - AIF2 Frame Ctrl 4 */ + { 0x00000551, 0x0000 }, /* R1361 - AIF2 Frame Ctrl 11 */ + { 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */ + { 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */ + { 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */ + { 0x00000580, 0x000C }, /* R1408 - AIF3 BCLK Ctrl */ + { 0x00000581, 0x0008 }, /* R1409 - AIF3 Tx Pin Ctrl */ + { 0x00000582, 0x0000 }, /* R1410 - AIF3 Rx Pin Ctrl */ + { 0x00000583, 0x0000 }, /* R1411 - AIF3 Rate Ctrl */ + { 0x00000584, 0x0000 }, /* R1412 - AIF3 Format */ + { 0x00000585, 0x0040 }, /* R1413 - AIF3 Tx BCLK Rate */ + { 0x00000586, 0x0040 }, /* R1414 - AIF3 Rx BCLK Rate */ + { 0x00000587, 0x1818 }, /* R1415 - AIF3 Frame Ctrl 1 */ + { 0x00000588, 0x1818 }, /* R1416 - AIF3 Frame Ctrl 2 */ + { 0x00000589, 0x0000 }, /* R1417 - AIF3 Frame Ctrl 3 */ + { 0x0000058A, 0x0001 }, /* R1418 - AIF3 Frame Ctrl 4 */ + { 0x00000591, 0x0000 }, /* R1425 - AIF3 Frame Ctrl 11 */ + { 0x00000592, 0x0001 }, /* R1426 - AIF3 Frame Ctrl 12 */ + { 0x00000599, 0x0000 }, /* R1433 - AIF3 Tx Enables */ + { 0x0000059A, 0x0000 }, /* R1434 - AIF3 Rx Enables */ + { 0x000005E3, 0x0004 }, /* R1507 - SLIMbus Framer Ref Gear */ + { 0x000005E5, 0x0000 }, /* R1509 - SLIMbus Rates 1 */ + { 0x000005E6, 0x0000 }, /* R1510 - SLIMbus Rates 2 */ + { 0x000005E7, 0x0000 }, /* R1511 - SLIMbus Rates 3 */ + { 0x000005E8, 0x0000 }, /* R1512 - SLIMbus Rates 4 */ + { 0x000005E9, 0x0000 }, /* R1513 - SLIMbus Rates 5 */ + { 0x000005EA, 0x0000 }, /* R1514 - SLIMbus Rates 6 */ + { 0x000005EB, 0x0000 }, /* R1515 - SLIMbus Rates 7 */ + { 0x000005EC, 0x0000 }, /* R1516 - SLIMbus Rates 8 */ + { 0x000005F5, 0x0000 }, /* R1525 - SLIMbus RX Channel Enable */ + { 0x000005F6, 0x0000 }, /* R1526 - SLIMbus TX Channel Enable */ + { 0x00000640, 0x0000 }, /* R1600 - PWM1MIX Input 1 Source */ + { 0x00000641, 0x0080 }, /* R1601 - PWM1MIX Input 1 Volume */ + { 0x00000642, 0x0000 }, /* R1602 - PWM1MIX Input 2 Source */ + { 0x00000643, 0x0080 }, /* R1603 - PWM1MIX Input 2 Volume */ + { 0x00000644, 0x0000 }, /* R1604 - PWM1MIX Input 3 Source */ + { 0x00000645, 0x0080 }, /* R1605 - PWM1MIX Input 3 Volume */ + { 0x00000646, 0x0000 }, /* R1606 - PWM1MIX Input 4 Source */ + { 0x00000647, 0x0080 }, /* R1607 - PWM1MIX Input 4 Volume */ + { 0x00000648, 0x0000 }, /* R1608 - PWM2MIX Input 1 Source */ + { 0x00000649, 0x0080 }, /* R1609 - PWM2MIX Input 1 Volume */ + { 0x0000064A, 0x0000 }, /* R1610 - PWM2MIX Input 2 Source */ + { 0x0000064B, 0x0080 }, /* R1611 - PWM2MIX Input 2 Volume */ + { 0x0000064C, 0x0000 }, /* R1612 - PWM2MIX Input 3 Source */ + { 0x0000064D, 0x0080 }, /* R1613 - PWM2MIX Input 3 Volume */ + { 0x0000064E, 0x0000 }, /* R1614 - PWM2MIX Input 4 Source */ + { 0x0000064F, 0x0080 }, /* R1615 - PWM2MIX Input 4 Volume */ + { 0x00000660, 0x0000 }, /* R1632 - MICMIX Input 1 Source */ + { 0x00000661, 0x0080 }, /* R1633 - MICMIX Input 1 Volume */ + { 0x00000662, 0x0000 }, /* R1634 - MICMIX Input 2 Source */ + { 0x00000663, 0x0080 }, /* R1635 - MICMIX Input 2 Volume */ + { 0x00000664, 0x0000 }, /* R1636 - MICMIX Input 3 Source */ + { 0x00000665, 0x0080 }, /* R1637 - MICMIX Input 3 Volume */ + { 0x00000666, 0x0000 }, /* R1638 - MICMIX Input 4 Source */ + { 0x00000667, 0x0080 }, /* R1639 - MICMIX Input 4 Volume */ + { 0x00000668, 0x0000 }, /* R1640 - NOISEMIX Input 1 Source */ + { 0x00000669, 0x0080 }, /* R1641 - NOISEMIX Input 1 Volume */ + { 0x0000066A, 0x0000 }, /* R1642 - NOISEMIX Input 2 Source */ + { 0x0000066B, 0x0080 }, /* R1643 - NOISEMIX Input 2 Volume */ + { 0x0000066C, 0x0000 }, /* R1644 - NOISEMIX Input 3 Source */ + { 0x0000066D, 0x0080 }, /* R1645 - NOISEMIX Input 3 Volume */ + { 0x0000066E, 0x0000 }, /* R1646 - NOISEMIX Input 4 Source */ + { 0x0000066F, 0x0080 }, /* R1647 - NOISEMIX Input 4 Volume */ + { 0x00000680, 0x0000 }, /* R1664 - OUT1LMIX Input 1 Source */ + { 0x00000681, 0x0080 }, /* R1665 - OUT1LMIX Input 1 Volume */ + { 0x00000682, 0x0000 }, /* R1666 - OUT1LMIX Input 2 Source */ + { 0x00000683, 0x0080 }, /* R1667 - OUT1LMIX Input 2 Volume */ + { 0x00000684, 0x0000 }, /* R1668 - OUT1LMIX Input 3 Source */ + { 0x00000685, 0x0080 }, /* R1669 - OUT1LMIX Input 3 Volume */ + { 0x00000686, 0x0000 }, /* R1670 - OUT1LMIX Input 4 Source */ + { 0x00000687, 0x0080 }, /* R1671 - OUT1LMIX Input 4 Volume */ + { 0x00000688, 0x0000 }, /* R1672 - OUT1RMIX Input 1 Source */ + { 0x00000689, 0x0080 }, /* R1673 - OUT1RMIX Input 1 Volume */ + { 0x0000068A, 0x0000 }, /* R1674 - OUT1RMIX Input 2 Source */ + { 0x0000068B, 0x0080 }, /* R1675 - OUT1RMIX Input 2 Volume */ + { 0x0000068C, 0x0000 }, /* R1676 - OUT1RMIX Input 3 Source */ + { 0x0000068D, 0x0080 }, /* R1677 - OUT1RMIX Input 3 Volume */ + { 0x0000068E, 0x0000 }, /* R1678 - OUT1RMIX Input 4 Source */ + { 0x0000068F, 0x0080 }, /* R1679 - OUT1RMIX Input 4 Volume */ + { 0x00000690, 0x0000 }, /* R1680 - OUT2LMIX Input 1 Source */ + { 0x00000691, 0x0080 }, /* R1681 - OUT2LMIX Input 1 Volume */ + { 0x00000692, 0x0000 }, /* R1682 - OUT2LMIX Input 2 Source */ + { 0x00000693, 0x0080 }, /* R1683 - OUT2LMIX Input 2 Volume */ + { 0x00000694, 0x0000 }, /* R1684 - OUT2LMIX Input 3 Source */ + { 0x00000695, 0x0080 }, /* R1685 - OUT2LMIX Input 3 Volume */ + { 0x00000696, 0x0000 }, /* R1686 - OUT2LMIX Input 4 Source */ + { 0x00000697, 0x0080 }, /* R1687 - OUT2LMIX Input 4 Volume */ + { 0x00000698, 0x0000 }, /* R1688 - OUT2RMIX Input 1 Source */ + { 0x00000699, 0x0080 }, /* R1689 - OUT2RMIX Input 1 Volume */ + { 0x0000069A, 0x0000 }, /* R1690 - OUT2RMIX Input 2 Source */ + { 0x0000069B, 0x0080 }, /* R1691 - OUT2RMIX Input 2 Volume */ + { 0x0000069C, 0x0000 }, /* R1692 - OUT2RMIX Input 3 Source */ + { 0x0000069D, 0x0080 }, /* R1693 - OUT2RMIX Input 3 Volume */ + { 0x0000069E, 0x0000 }, /* R1694 - OUT2RMIX Input 4 Source */ + { 0x0000069F, 0x0080 }, /* R1695 - OUT2RMIX Input 4 Volume */ + { 0x000006A0, 0x0000 }, /* R1696 - OUT3LMIX Input 1 Source */ + { 0x000006A1, 0x0080 }, /* R1697 - OUT3LMIX Input 1 Volume */ + { 0x000006A2, 0x0000 }, /* R1698 - OUT3LMIX Input 2 Source */ + { 0x000006A3, 0x0080 }, /* R1699 - OUT3LMIX Input 2 Volume */ + { 0x000006A4, 0x0000 }, /* R1700 - OUT3LMIX Input 3 Source */ + { 0x000006A5, 0x0080 }, /* R1701 - OUT3LMIX Input 3 Volume */ + { 0x000006A6, 0x0000 }, /* R1702 - OUT3LMIX Input 4 Source */ + { 0x000006A7, 0x0080 }, /* R1703 - OUT3LMIX Input 4 Volume */ + { 0x000006A8, 0x0000 }, /* R1704 - OUT3RMIX Input 1 Source */ + { 0x000006A9, 0x0080 }, /* R1705 - OUT3RMIX Input 1 Volume */ + { 0x000006AA, 0x0000 }, /* R1706 - OUT3RMIX Input 2 Source */ + { 0x000006AB, 0x0080 }, /* R1707 - OUT3RMIX Input 2 Volume */ + { 0x000006AC, 0x0000 }, /* R1708 - OUT3RMIX Input 3 Source */ + { 0x000006AD, 0x0080 }, /* R1709 - OUT3RMIX Input 3 Volume */ + { 0x000006AE, 0x0000 }, /* R1710 - OUT3RMIX Input 4 Source */ + { 0x000006AF, 0x0080 }, /* R1711 - OUT3RMIX Input 4 Volume */ + { 0x000006B0, 0x0000 }, /* R1712 - OUT4LMIX Input 1 Source */ + { 0x000006B1, 0x0080 }, /* R1713 - OUT4LMIX Input 1 Volume */ + { 0x000006B2, 0x0000 }, /* R1714 - OUT4LMIX Input 2 Source */ + { 0x000006B3, 0x0080 }, /* R1715 - OUT4LMIX Input 2 Volume */ + { 0x000006B4, 0x0000 }, /* R1716 - OUT4LMIX Input 3 Source */ + { 0x000006B5, 0x0080 }, /* R1717 - OUT4LMIX Input 3 Volume */ + { 0x000006B6, 0x0000 }, /* R1718 - OUT4LMIX Input 4 Source */ + { 0x000006B7, 0x0080 }, /* R1719 - OUT4LMIX Input 4 Volume */ + { 0x000006B8, 0x0000 }, /* R1720 - OUT4RMIX Input 1 Source */ + { 0x000006B9, 0x0080 }, /* R1721 - OUT4RMIX Input 1 Volume */ + { 0x000006BA, 0x0000 }, /* R1722 - OUT4RMIX Input 2 Source */ + { 0x000006BB, 0x0080 }, /* R1723 - OUT4RMIX Input 2 Volume */ + { 0x000006BC, 0x0000 }, /* R1724 - OUT4RMIX Input 3 Source */ + { 0x000006BD, 0x0080 }, /* R1725 - OUT4RMIX Input 3 Volume */ + { 0x000006BE, 0x0000 }, /* R1726 - OUT4RMIX Input 4 Source */ + { 0x000006BF, 0x0080 }, /* R1727 - OUT4RMIX Input 4 Volume */ + { 0x000006C0, 0x0000 }, /* R1728 - OUT5LMIX Input 1 Source */ + { 0x000006C1, 0x0080 }, /* R1729 - OUT5LMIX Input 1 Volume */ + { 0x000006C2, 0x0000 }, /* R1730 - OUT5LMIX Input 2 Source */ + { 0x000006C3, 0x0080 }, /* R1731 - OUT5LMIX Input 2 Volume */ + { 0x000006C4, 0x0000 }, /* R1732 - OUT5LMIX Input 3 Source */ + { 0x000006C5, 0x0080 }, /* R1733 - OUT5LMIX Input 3 Volume */ + { 0x000006C6, 0x0000 }, /* R1734 - OUT5LMIX Input 4 Source */ + { 0x000006C7, 0x0080 }, /* R1735 - OUT5LMIX Input 4 Volume */ + { 0x000006C8, 0x0000 }, /* R1736 - OUT5RMIX Input 1 Source */ + { 0x000006C9, 0x0080 }, /* R1737 - OUT5RMIX Input 1 Volume */ + { 0x000006CA, 0x0000 }, /* R1738 - OUT5RMIX Input 2 Source */ + { 0x000006CB, 0x0080 }, /* R1739 - OUT5RMIX Input 2 Volume */ + { 0x000006CC, 0x0000 }, /* R1740 - OUT5RMIX Input 3 Source */ + { 0x000006CD, 0x0080 }, /* R1741 - OUT5RMIX Input 3 Volume */ + { 0x000006CE, 0x0000 }, /* R1742 - OUT5RMIX Input 4 Source */ + { 0x000006CF, 0x0080 }, /* R1743 - OUT5RMIX Input 4 Volume */ + { 0x000006D0, 0x0000 }, /* R1744 - OUT6LMIX Input 1 Source */ + { 0x000006D1, 0x0080 }, /* R1745 - OUT6LMIX Input 1 Volume */ + { 0x000006D2, 0x0000 }, /* R1746 - OUT6LMIX Input 2 Source */ + { 0x000006D3, 0x0080 }, /* R1747 - OUT6LMIX Input 2 Volume */ + { 0x000006D4, 0x0000 }, /* R1748 - OUT6LMIX Input 3 Source */ + { 0x000006D5, 0x0080 }, /* R1749 - OUT6LMIX Input 3 Volume */ + { 0x000006D6, 0x0000 }, /* R1750 - OUT6LMIX Input 4 Source */ + { 0x000006D7, 0x0080 }, /* R1751 - OUT6LMIX Input 4 Volume */ + { 0x000006D8, 0x0000 }, /* R1752 - OUT6RMIX Input 1 Source */ + { 0x000006D9, 0x0080 }, /* R1753 - OUT6RMIX Input 1 Volume */ + { 0x000006DA, 0x0000 }, /* R1754 - OUT6RMIX Input 2 Source */ + { 0x000006DB, 0x0080 }, /* R1755 - OUT6RMIX Input 2 Volume */ + { 0x000006DC, 0x0000 }, /* R1756 - OUT6RMIX Input 3 Source */ + { 0x000006DD, 0x0080 }, /* R1757 - OUT6RMIX Input 3 Volume */ + { 0x000006DE, 0x0000 }, /* R1758 - OUT6RMIX Input 4 Source */ + { 0x000006DF, 0x0080 }, /* R1759 - OUT6RMIX Input 4 Volume */ + { 0x00000700, 0x0000 }, /* R1792 - AIF1TX1MIX Input 1 Source */ + { 0x00000701, 0x0080 }, /* R1793 - AIF1TX1MIX Input 1 Volume */ + { 0x00000702, 0x0000 }, /* R1794 - AIF1TX1MIX Input 2 Source */ + { 0x00000703, 0x0080 }, /* R1795 - AIF1TX1MIX Input 2 Volume */ + { 0x00000704, 0x0000 }, /* R1796 - AIF1TX1MIX Input 3 Source */ + { 0x00000705, 0x0080 }, /* R1797 - AIF1TX1MIX Input 3 Volume */ + { 0x00000706, 0x0000 }, /* R1798 - AIF1TX1MIX Input 4 Source */ + { 0x00000707, 0x0080 }, /* R1799 - AIF1TX1MIX Input 4 Volume */ + { 0x00000708, 0x0000 }, /* R1800 - AIF1TX2MIX Input 1 Source */ + { 0x00000709, 0x0080 }, /* R1801 - AIF1TX2MIX Input 1 Volume */ + { 0x0000070A, 0x0000 }, /* R1802 - AIF1TX2MIX Input 2 Source */ + { 0x0000070B, 0x0080 }, /* R1803 - AIF1TX2MIX Input 2 Volume */ + { 0x0000070C, 0x0000 }, /* R1804 - AIF1TX2MIX Input 3 Source */ + { 0x0000070D, 0x0080 }, /* R1805 - AIF1TX2MIX Input 3 Volume */ + { 0x0000070E, 0x0000 }, /* R1806 - AIF1TX2MIX Input 4 Source */ + { 0x0000070F, 0x0080 }, /* R1807 - AIF1TX2MIX Input 4 Volume */ + { 0x00000710, 0x0000 }, /* R1808 - AIF1TX3MIX Input 1 Source */ + { 0x00000711, 0x0080 }, /* R1809 - AIF1TX3MIX Input 1 Volume */ + { 0x00000712, 0x0000 }, /* R1810 - AIF1TX3MIX Input 2 Source */ + { 0x00000713, 0x0080 }, /* R1811 - AIF1TX3MIX Input 2 Volume */ + { 0x00000714, 0x0000 }, /* R1812 - AIF1TX3MIX Input 3 Source */ + { 0x00000715, 0x0080 }, /* R1813 - AIF1TX3MIX Input 3 Volume */ + { 0x00000716, 0x0000 }, /* R1814 - AIF1TX3MIX Input 4 Source */ + { 0x00000717, 0x0080 }, /* R1815 - AIF1TX3MIX Input 4 Volume */ + { 0x00000718, 0x0000 }, /* R1816 - AIF1TX4MIX Input 1 Source */ + { 0x00000719, 0x0080 }, /* R1817 - AIF1TX4MIX Input 1 Volume */ + { 0x0000071A, 0x0000 }, /* R1818 - AIF1TX4MIX Input 2 Source */ + { 0x0000071B, 0x0080 }, /* R1819 - AIF1TX4MIX Input 2 Volume */ + { 0x0000071C, 0x0000 }, /* R1820 - AIF1TX4MIX Input 3 Source */ + { 0x0000071D, 0x0080 }, /* R1821 - AIF1TX4MIX Input 3 Volume */ + { 0x0000071E, 0x0000 }, /* R1822 - AIF1TX4MIX Input 4 Source */ + { 0x0000071F, 0x0080 }, /* R1823 - AIF1TX4MIX Input 4 Volume */ + { 0x00000720, 0x0000 }, /* R1824 - AIF1TX5MIX Input 1 Source */ + { 0x00000721, 0x0080 }, /* R1825 - AIF1TX5MIX Input 1 Volume */ + { 0x00000722, 0x0000 }, /* R1826 - AIF1TX5MIX Input 2 Source */ + { 0x00000723, 0x0080 }, /* R1827 - AIF1TX5MIX Input 2 Volume */ + { 0x00000724, 0x0000 }, /* R1828 - AIF1TX5MIX Input 3 Source */ + { 0x00000725, 0x0080 }, /* R1829 - AIF1TX5MIX Input 3 Volume */ + { 0x00000726, 0x0000 }, /* R1830 - AIF1TX5MIX Input 4 Source */ + { 0x00000727, 0x0080 }, /* R1831 - AIF1TX5MIX Input 4 Volume */ + { 0x00000728, 0x0000 }, /* R1832 - AIF1TX6MIX Input 1 Source */ + { 0x00000729, 0x0080 }, /* R1833 - AIF1TX6MIX Input 1 Volume */ + { 0x0000072A, 0x0000 }, /* R1834 - AIF1TX6MIX Input 2 Source */ + { 0x0000072B, 0x0080 }, /* R1835 - AIF1TX6MIX Input 2 Volume */ + { 0x0000072C, 0x0000 }, /* R1836 - AIF1TX6MIX Input 3 Source */ + { 0x0000072D, 0x0080 }, /* R1837 - AIF1TX6MIX Input 3 Volume */ + { 0x0000072E, 0x0000 }, /* R1838 - AIF1TX6MIX Input 4 Source */ + { 0x0000072F, 0x0080 }, /* R1839 - AIF1TX6MIX Input 4 Volume */ + { 0x00000730, 0x0000 }, /* R1840 - AIF1TX7MIX Input 1 Source */ + { 0x00000731, 0x0080 }, /* R1841 - AIF1TX7MIX Input 1 Volume */ + { 0x00000732, 0x0000 }, /* R1842 - AIF1TX7MIX Input 2 Source */ + { 0x00000733, 0x0080 }, /* R1843 - AIF1TX7MIX Input 2 Volume */ + { 0x00000734, 0x0000 }, /* R1844 - AIF1TX7MIX Input 3 Source */ + { 0x00000735, 0x0080 }, /* R1845 - AIF1TX7MIX Input 3 Volume */ + { 0x00000736, 0x0000 }, /* R1846 - AIF1TX7MIX Input 4 Source */ + { 0x00000737, 0x0080 }, /* R1847 - AIF1TX7MIX Input 4 Volume */ + { 0x00000738, 0x0000 }, /* R1848 - AIF1TX8MIX Input 1 Source */ + { 0x00000739, 0x0080 }, /* R1849 - AIF1TX8MIX Input 1 Volume */ + { 0x0000073A, 0x0000 }, /* R1850 - AIF1TX8MIX Input 2 Source */ + { 0x0000073B, 0x0080 }, /* R1851 - AIF1TX8MIX Input 2 Volume */ + { 0x0000073C, 0x0000 }, /* R1852 - AIF1TX8MIX Input 3 Source */ + { 0x0000073D, 0x0080 }, /* R1853 - AIF1TX8MIX Input 3 Volume */ + { 0x0000073E, 0x0000 }, /* R1854 - AIF1TX8MIX Input 4 Source */ + { 0x0000073F, 0x0080 }, /* R1855 - AIF1TX8MIX Input 4 Volume */ + { 0x00000740, 0x0000 }, /* R1856 - AIF2TX1MIX Input 1 Source */ + { 0x00000741, 0x0080 }, /* R1857 - AIF2TX1MIX Input 1 Volume */ + { 0x00000742, 0x0000 }, /* R1858 - AIF2TX1MIX Input 2 Source */ + { 0x00000743, 0x0080 }, /* R1859 - AIF2TX1MIX Input 2 Volume */ + { 0x00000744, 0x0000 }, /* R1860 - AIF2TX1MIX Input 3 Source */ + { 0x00000745, 0x0080 }, /* R1861 - AIF2TX1MIX Input 3 Volume */ + { 0x00000746, 0x0000 }, /* R1862 - AIF2TX1MIX Input 4 Source */ + { 0x00000747, 0x0080 }, /* R1863 - AIF2TX1MIX Input 4 Volume */ + { 0x00000748, 0x0000 }, /* R1864 - AIF2TX2MIX Input 1 Source */ + { 0x00000749, 0x0080 }, /* R1865 - AIF2TX2MIX Input 1 Volume */ + { 0x0000074A, 0x0000 }, /* R1866 - AIF2TX2MIX Input 2 Source */ + { 0x0000074B, 0x0080 }, /* R1867 - AIF2TX2MIX Input 2 Volume */ + { 0x0000074C, 0x0000 }, /* R1868 - AIF2TX2MIX Input 3 Source */ + { 0x0000074D, 0x0080 }, /* R1869 - AIF2TX2MIX Input 3 Volume */ + { 0x0000074E, 0x0000 }, /* R1870 - AIF2TX2MIX Input 4 Source */ + { 0x0000074F, 0x0080 }, /* R1871 - AIF2TX2MIX Input 4 Volume */ + { 0x00000780, 0x0000 }, /* R1920 - AIF3TX1MIX Input 1 Source */ + { 0x00000781, 0x0080 }, /* R1921 - AIF3TX1MIX Input 1 Volume */ + { 0x00000782, 0x0000 }, /* R1922 - AIF3TX1MIX Input 2 Source */ + { 0x00000783, 0x0080 }, /* R1923 - AIF3TX1MIX Input 2 Volume */ + { 0x00000784, 0x0000 }, /* R1924 - AIF3TX1MIX Input 3 Source */ + { 0x00000785, 0x0080 }, /* R1925 - AIF3TX1MIX Input 3 Volume */ + { 0x00000786, 0x0000 }, /* R1926 - AIF3TX1MIX Input 4 Source */ + { 0x00000787, 0x0080 }, /* R1927 - AIF3TX1MIX Input 4 Volume */ + { 0x00000788, 0x0000 }, /* R1928 - AIF3TX2MIX Input 1 Source */ + { 0x00000789, 0x0080 }, /* R1929 - AIF3TX2MIX Input 1 Volume */ + { 0x0000078A, 0x0000 }, /* R1930 - AIF3TX2MIX Input 2 Source */ + { 0x0000078B, 0x0080 }, /* R1931 - AIF3TX2MIX Input 2 Volume */ + { 0x0000078C, 0x0000 }, /* R1932 - AIF3TX2MIX Input 3 Source */ + { 0x0000078D, 0x0080 }, /* R1933 - AIF3TX2MIX Input 3 Volume */ + { 0x0000078E, 0x0000 }, /* R1934 - AIF3TX2MIX Input 4 Source */ + { 0x0000078F, 0x0080 }, /* R1935 - AIF3TX2MIX Input 4 Volume */ + { 0x000007C0, 0x0000 }, /* R1984 - SLIMTX1MIX Input 1 Source */ + { 0x000007C1, 0x0080 }, /* R1985 - SLIMTX1MIX Input 1 Volume */ + { 0x000007C2, 0x0000 }, /* R1986 - SLIMTX1MIX Input 2 Source */ + { 0x000007C3, 0x0080 }, /* R1987 - SLIMTX1MIX Input 2 Volume */ + { 0x000007C4, 0x0000 }, /* R1988 - SLIMTX1MIX Input 3 Source */ + { 0x000007C5, 0x0080 }, /* R1989 - SLIMTX1MIX Input 3 Volume */ + { 0x000007C6, 0x0000 }, /* R1990 - SLIMTX1MIX Input 4 Source */ + { 0x000007C7, 0x0080 }, /* R1991 - SLIMTX1MIX Input 4 Volume */ + { 0x000007C8, 0x0000 }, /* R1992 - SLIMTX2MIX Input 1 Source */ + { 0x000007C9, 0x0080 }, /* R1993 - SLIMTX2MIX Input 1 Volume */ + { 0x000007CA, 0x0000 }, /* R1994 - SLIMTX2MIX Input 2 Source */ + { 0x000007CB, 0x0080 }, /* R1995 - SLIMTX2MIX Input 2 Volume */ + { 0x000007CC, 0x0000 }, /* R1996 - SLIMTX2MIX Input 3 Source */ + { 0x000007CD, 0x0080 }, /* R1997 - SLIMTX2MIX Input 3 Volume */ + { 0x000007CE, 0x0000 }, /* R1998 - SLIMTX2MIX Input 4 Source */ + { 0x000007CF, 0x0080 }, /* R1999 - SLIMTX2MIX Input 4 Volume */ + { 0x000007D0, 0x0000 }, /* R2000 - SLIMTX3MIX Input 1 Source */ + { 0x000007D1, 0x0080 }, /* R2001 - SLIMTX3MIX Input 1 Volume */ + { 0x000007D2, 0x0000 }, /* R2002 - SLIMTX3MIX Input 2 Source */ + { 0x000007D3, 0x0080 }, /* R2003 - SLIMTX3MIX Input 2 Volume */ + { 0x000007D4, 0x0000 }, /* R2004 - SLIMTX3MIX Input 3 Source */ + { 0x000007D5, 0x0080 }, /* R2005 - SLIMTX3MIX Input 3 Volume */ + { 0x000007D6, 0x0000 }, /* R2006 - SLIMTX3MIX Input 4 Source */ + { 0x000007D7, 0x0080 }, /* R2007 - SLIMTX3MIX Input 4 Volume */ + { 0x000007D8, 0x0000 }, /* R2008 - SLIMTX4MIX Input 1 Source */ + { 0x000007D9, 0x0080 }, /* R2009 - SLIMTX4MIX Input 1 Volume */ + { 0x000007DA, 0x0000 }, /* R2010 - SLIMTX4MIX Input 2 Source */ + { 0x000007DB, 0x0080 }, /* R2011 - SLIMTX4MIX Input 2 Volume */ + { 0x000007DC, 0x0000 }, /* R2012 - SLIMTX4MIX Input 3 Source */ + { 0x000007DD, 0x0080 }, /* R2013 - SLIMTX4MIX Input 3 Volume */ + { 0x000007DE, 0x0000 }, /* R2014 - SLIMTX4MIX Input 4 Source */ + { 0x000007DF, 0x0080 }, /* R2015 - SLIMTX4MIX Input 4 Volume */ + { 0x000007E0, 0x0000 }, /* R2016 - SLIMTX5MIX Input 1 Source */ + { 0x000007E1, 0x0080 }, /* R2017 - SLIMTX5MIX Input 1 Volume */ + { 0x000007E2, 0x0000 }, /* R2018 - SLIMTX5MIX Input 2 Source */ + { 0x000007E3, 0x0080 }, /* R2019 - SLIMTX5MIX Input 2 Volume */ + { 0x000007E4, 0x0000 }, /* R2020 - SLIMTX5MIX Input 3 Source */ + { 0x000007E5, 0x0080 }, /* R2021 - SLIMTX5MIX Input 3 Volume */ + { 0x000007E6, 0x0000 }, /* R2022 - SLIMTX5MIX Input 4 Source */ + { 0x000007E7, 0x0080 }, /* R2023 - SLIMTX5MIX Input 4 Volume */ + { 0x000007E8, 0x0000 }, /* R2024 - SLIMTX6MIX Input 1 Source */ + { 0x000007E9, 0x0080 }, /* R2025 - SLIMTX6MIX Input 1 Volume */ + { 0x000007EA, 0x0000 }, /* R2026 - SLIMTX6MIX Input 2 Source */ + { 0x000007EB, 0x0080 }, /* R2027 - SLIMTX6MIX Input 2 Volume */ + { 0x000007EC, 0x0000 }, /* R2028 - SLIMTX6MIX Input 3 Source */ + { 0x000007ED, 0x0080 }, /* R2029 - SLIMTX6MIX Input 3 Volume */ + { 0x000007EE, 0x0000 }, /* R2030 - SLIMTX6MIX Input 4 Source */ + { 0x000007EF, 0x0080 }, /* R2031 - SLIMTX6MIX Input 4 Volume */ + { 0x000007F0, 0x0000 }, /* R2032 - SLIMTX7MIX Input 1 Source */ + { 0x000007F1, 0x0080 }, /* R2033 - SLIMTX7MIX Input 1 Volume */ + { 0x000007F2, 0x0000 }, /* R2034 - SLIMTX7MIX Input 2 Source */ + { 0x000007F3, 0x0080 }, /* R2035 - SLIMTX7MIX Input 2 Volume */ + { 0x000007F4, 0x0000 }, /* R2036 - SLIMTX7MIX Input 3 Source */ + { 0x000007F5, 0x0080 }, /* R2037 - SLIMTX7MIX Input 3 Volume */ + { 0x000007F6, 0x0000 }, /* R2038 - SLIMTX7MIX Input 4 Source */ + { 0x000007F7, 0x0080 }, /* R2039 - SLIMTX7MIX Input 4 Volume */ + { 0x000007F8, 0x0000 }, /* R2040 - SLIMTX8MIX Input 1 Source */ + { 0x000007F9, 0x0080 }, /* R2041 - SLIMTX8MIX Input 1 Volume */ + { 0x000007FA, 0x0000 }, /* R2042 - SLIMTX8MIX Input 2 Source */ + { 0x000007FB, 0x0080 }, /* R2043 - SLIMTX8MIX Input 2 Volume */ + { 0x000007FC, 0x0000 }, /* R2044 - SLIMTX8MIX Input 3 Source */ + { 0x000007FD, 0x0080 }, /* R2045 - SLIMTX8MIX Input 3 Volume */ + { 0x000007FE, 0x0000 }, /* R2046 - SLIMTX8MIX Input 4 Source */ + { 0x000007FF, 0x0080 }, /* R2047 - SLIMTX8MIX Input 4 Volume */ + { 0x00000880, 0x0000 }, /* R2176 - EQ1MIX Input 1 Source */ + { 0x00000881, 0x0080 }, /* R2177 - EQ1MIX Input 1 Volume */ + { 0x00000882, 0x0000 }, /* R2178 - EQ1MIX Input 2 Source */ + { 0x00000883, 0x0080 }, /* R2179 - EQ1MIX Input 2 Volume */ + { 0x00000884, 0x0000 }, /* R2180 - EQ1MIX Input 3 Source */ + { 0x00000885, 0x0080 }, /* R2181 - EQ1MIX Input 3 Volume */ + { 0x00000886, 0x0000 }, /* R2182 - EQ1MIX Input 4 Source */ + { 0x00000887, 0x0080 }, /* R2183 - EQ1MIX Input 4 Volume */ + { 0x00000888, 0x0000 }, /* R2184 - EQ2MIX Input 1 Source */ + { 0x00000889, 0x0080 }, /* R2185 - EQ2MIX Input 1 Volume */ + { 0x0000088A, 0x0000 }, /* R2186 - EQ2MIX Input 2 Source */ + { 0x0000088B, 0x0080 }, /* R2187 - EQ2MIX Input 2 Volume */ + { 0x0000088C, 0x0000 }, /* R2188 - EQ2MIX Input 3 Source */ + { 0x0000088D, 0x0080 }, /* R2189 - EQ2MIX Input 3 Volume */ + { 0x0000088E, 0x0000 }, /* R2190 - EQ2MIX Input 4 Source */ + { 0x0000088F, 0x0080 }, /* R2191 - EQ2MIX Input 4 Volume */ + { 0x00000890, 0x0000 }, /* R2192 - EQ3MIX Input 1 Source */ + { 0x00000891, 0x0080 }, /* R2193 - EQ3MIX Input 1 Volume */ + { 0x00000892, 0x0000 }, /* R2194 - EQ3MIX Input 2 Source */ + { 0x00000893, 0x0080 }, /* R2195 - EQ3MIX Input 2 Volume */ + { 0x00000894, 0x0000 }, /* R2196 - EQ3MIX Input 3 Source */ + { 0x00000895, 0x0080 }, /* R2197 - EQ3MIX Input 3 Volume */ + { 0x00000896, 0x0000 }, /* R2198 - EQ3MIX Input 4 Source */ + { 0x00000897, 0x0080 }, /* R2199 - EQ3MIX Input 4 Volume */ + { 0x00000898, 0x0000 }, /* R2200 - EQ4MIX Input 1 Source */ + { 0x00000899, 0x0080 }, /* R2201 - EQ4MIX Input 1 Volume */ + { 0x0000089A, 0x0000 }, /* R2202 - EQ4MIX Input 2 Source */ + { 0x0000089B, 0x0080 }, /* R2203 - EQ4MIX Input 2 Volume */ + { 0x0000089C, 0x0000 }, /* R2204 - EQ4MIX Input 3 Source */ + { 0x0000089D, 0x0080 }, /* R2205 - EQ4MIX Input 3 Volume */ + { 0x0000089E, 0x0000 }, /* R2206 - EQ4MIX Input 4 Source */ + { 0x0000089F, 0x0080 }, /* R2207 - EQ4MIX Input 4 Volume */ + { 0x000008C0, 0x0000 }, /* R2240 - DRC1LMIX Input 1 Source */ + { 0x000008C1, 0x0080 }, /* R2241 - DRC1LMIX Input 1 Volume */ + { 0x000008C2, 0x0000 }, /* R2242 - DRC1LMIX Input 2 Source */ + { 0x000008C3, 0x0080 }, /* R2243 - DRC1LMIX Input 2 Volume */ + { 0x000008C4, 0x0000 }, /* R2244 - DRC1LMIX Input 3 Source */ + { 0x000008C5, 0x0080 }, /* R2245 - DRC1LMIX Input 3 Volume */ + { 0x000008C6, 0x0000 }, /* R2246 - DRC1LMIX Input 4 Source */ + { 0x000008C7, 0x0080 }, /* R2247 - DRC1LMIX Input 4 Volume */ + { 0x000008C8, 0x0000 }, /* R2248 - DRC1RMIX Input 1 Source */ + { 0x000008C9, 0x0080 }, /* R2249 - DRC1RMIX Input 1 Volume */ + { 0x000008CA, 0x0000 }, /* R2250 - DRC1RMIX Input 2 Source */ + { 0x000008CB, 0x0080 }, /* R2251 - DRC1RMIX Input 2 Volume */ + { 0x000008CC, 0x0000 }, /* R2252 - DRC1RMIX Input 3 Source */ + { 0x000008CD, 0x0080 }, /* R2253 - DRC1RMIX Input 3 Volume */ + { 0x000008CE, 0x0000 }, /* R2254 - DRC1RMIX Input 4 Source */ + { 0x000008CF, 0x0080 }, /* R2255 - DRC1RMIX Input 4 Volume */ + { 0x000008D0, 0x0000 }, /* R2256 - DRC2LMIX Input 1 Source */ + { 0x000008D1, 0x0080 }, /* R2257 - DRC2LMIX Input 1 Volume */ + { 0x000008D2, 0x0000 }, /* R2258 - DRC2LMIX Input 2 Source */ + { 0x000008D3, 0x0080 }, /* R2259 - DRC2LMIX Input 2 Volume */ + { 0x000008D4, 0x0000 }, /* R2260 - DRC2LMIX Input 3 Source */ + { 0x000008D5, 0x0080 }, /* R2261 - DRC2LMIX Input 3 Volume */ + { 0x000008D6, 0x0000 }, /* R2262 - DRC2LMIX Input 4 Source */ + { 0x000008D7, 0x0080 }, /* R2263 - DRC2LMIX Input 4 Volume */ + { 0x000008D8, 0x0000 }, /* R2264 - DRC2RMIX Input 1 Source */ + { 0x000008D9, 0x0080 }, /* R2265 - DRC2RMIX Input 1 Volume */ + { 0x000008DA, 0x0000 }, /* R2266 - DRC2RMIX Input 2 Source */ + { 0x000008DB, 0x0080 }, /* R2267 - DRC2RMIX Input 2 Volume */ + { 0x000008DC, 0x0000 }, /* R2268 - DRC2RMIX Input 3 Source */ + { 0x000008DD, 0x0080 }, /* R2269 - DRC2RMIX Input 3 Volume */ + { 0x000008DE, 0x0000 }, /* R2270 - DRC2RMIX Input 4 Source */ + { 0x000008DF, 0x0080 }, /* R2271 - DRC2RMIX Input 4 Volume */ + { 0x00000900, 0x0000 }, /* R2304 - HPLP1MIX Input 1 Source */ + { 0x00000901, 0x0080 }, /* R2305 - HPLP1MIX Input 1 Volume */ + { 0x00000902, 0x0000 }, /* R2306 - HPLP1MIX Input 2 Source */ + { 0x00000903, 0x0080 }, /* R2307 - HPLP1MIX Input 2 Volume */ + { 0x00000904, 0x0000 }, /* R2308 - HPLP1MIX Input 3 Source */ + { 0x00000905, 0x0080 }, /* R2309 - HPLP1MIX Input 3 Volume */ + { 0x00000906, 0x0000 }, /* R2310 - HPLP1MIX Input 4 Source */ + { 0x00000907, 0x0080 }, /* R2311 - HPLP1MIX Input 4 Volume */ + { 0x00000908, 0x0000 }, /* R2312 - HPLP2MIX Input 1 Source */ + { 0x00000909, 0x0080 }, /* R2313 - HPLP2MIX Input 1 Volume */ + { 0x0000090A, 0x0000 }, /* R2314 - HPLP2MIX Input 2 Source */ + { 0x0000090B, 0x0080 }, /* R2315 - HPLP2MIX Input 2 Volume */ + { 0x0000090C, 0x0000 }, /* R2316 - HPLP2MIX Input 3 Source */ + { 0x0000090D, 0x0080 }, /* R2317 - HPLP2MIX Input 3 Volume */ + { 0x0000090E, 0x0000 }, /* R2318 - HPLP2MIX Input 4 Source */ + { 0x0000090F, 0x0080 }, /* R2319 - HPLP2MIX Input 4 Volume */ + { 0x00000910, 0x0000 }, /* R2320 - HPLP3MIX Input 1 Source */ + { 0x00000911, 0x0080 }, /* R2321 - HPLP3MIX Input 1 Volume */ + { 0x00000912, 0x0000 }, /* R2322 - HPLP3MIX Input 2 Source */ + { 0x00000913, 0x0080 }, /* R2323 - HPLP3MIX Input 2 Volume */ + { 0x00000914, 0x0000 }, /* R2324 - HPLP3MIX Input 3 Source */ + { 0x00000915, 0x0080 }, /* R2325 - HPLP3MIX Input 3 Volume */ + { 0x00000916, 0x0000 }, /* R2326 - HPLP3MIX Input 4 Source */ + { 0x00000917, 0x0080 }, /* R2327 - HPLP3MIX Input 4 Volume */ + { 0x00000918, 0x0000 }, /* R2328 - HPLP4MIX Input 1 Source */ + { 0x00000919, 0x0080 }, /* R2329 - HPLP4MIX Input 1 Volume */ + { 0x0000091A, 0x0000 }, /* R2330 - HPLP4MIX Input 2 Source */ + { 0x0000091B, 0x0080 }, /* R2331 - HPLP4MIX Input 2 Volume */ + { 0x0000091C, 0x0000 }, /* R2332 - HPLP4MIX Input 3 Source */ + { 0x0000091D, 0x0080 }, /* R2333 - HPLP4MIX Input 3 Volume */ + { 0x0000091E, 0x0000 }, /* R2334 - HPLP4MIX Input 4 Source */ + { 0x0000091F, 0x0080 }, /* R2335 - HPLP4MIX Input 4 Volume */ + { 0x00000940, 0x0000 }, /* R2368 - DSP1LMIX Input 1 Source */ + { 0x00000941, 0x0080 }, /* R2369 - DSP1LMIX Input 1 Volume */ + { 0x00000942, 0x0000 }, /* R2370 - DSP1LMIX Input 2 Source */ + { 0x00000943, 0x0080 }, /* R2371 - DSP1LMIX Input 2 Volume */ + { 0x00000944, 0x0000 }, /* R2372 - DSP1LMIX Input 3 Source */ + { 0x00000945, 0x0080 }, /* R2373 - DSP1LMIX Input 3 Volume */ + { 0x00000946, 0x0000 }, /* R2374 - DSP1LMIX Input 4 Source */ + { 0x00000947, 0x0080 }, /* R2375 - DSP1LMIX Input 4 Volume */ + { 0x00000948, 0x0000 }, /* R2376 - DSP1RMIX Input 1 Source */ + { 0x00000949, 0x0080 }, /* R2377 - DSP1RMIX Input 1 Volume */ + { 0x0000094A, 0x0000 }, /* R2378 - DSP1RMIX Input 2 Source */ + { 0x0000094B, 0x0080 }, /* R2379 - DSP1RMIX Input 2 Volume */ + { 0x0000094C, 0x0000 }, /* R2380 - DSP1RMIX Input 3 Source */ + { 0x0000094D, 0x0080 }, /* R2381 - DSP1RMIX Input 3 Volume */ + { 0x0000094E, 0x0000 }, /* R2382 - DSP1RMIX Input 4 Source */ + { 0x0000094F, 0x0080 }, /* R2383 - DSP1RMIX Input 4 Volume */ + { 0x00000950, 0x0000 }, /* R2384 - DSP1AUX1MIX Input 1 Source */ + { 0x00000958, 0x0000 }, /* R2392 - DSP1AUX2MIX Input 1 Source */ + { 0x00000960, 0x0000 }, /* R2400 - DSP1AUX3MIX Input 1 Source */ + { 0x00000968, 0x0000 }, /* R2408 - DSP1AUX4MIX Input 1 Source */ + { 0x00000970, 0x0000 }, /* R2416 - DSP1AUX5MIX Input 1 Source */ + { 0x00000978, 0x0000 }, /* R2424 - DSP1AUX6MIX Input 1 Source */ + { 0x00000980, 0x0000 }, /* R2432 - DSP2LMIX Input 1 Source */ + { 0x00000981, 0x0080 }, /* R2433 - DSP2LMIX Input 1 Volume */ + { 0x00000982, 0x0000 }, /* R2434 - DSP2LMIX Input 2 Source */ + { 0x00000983, 0x0080 }, /* R2435 - DSP2LMIX Input 2 Volume */ + { 0x00000984, 0x0000 }, /* R2436 - DSP2LMIX Input 3 Source */ + { 0x00000985, 0x0080 }, /* R2437 - DSP2LMIX Input 3 Volume */ + { 0x00000986, 0x0000 }, /* R2438 - DSP2LMIX Input 4 Source */ + { 0x00000987, 0x0080 }, /* R2439 - DSP2LMIX Input 4 Volume */ + { 0x00000988, 0x0000 }, /* R2440 - DSP2RMIX Input 1 Source */ + { 0x00000989, 0x0080 }, /* R2441 - DSP2RMIX Input 1 Volume */ + { 0x0000098A, 0x0000 }, /* R2442 - DSP2RMIX Input 2 Source */ + { 0x0000098B, 0x0080 }, /* R2443 - DSP2RMIX Input 2 Volume */ + { 0x0000098C, 0x0000 }, /* R2444 - DSP2RMIX Input 3 Source */ + { 0x0000098D, 0x0080 }, /* R2445 - DSP2RMIX Input 3 Volume */ + { 0x0000098E, 0x0000 }, /* R2446 - DSP2RMIX Input 4 Source */ + { 0x0000098F, 0x0080 }, /* R2447 - DSP2RMIX Input 4 Volume */ + { 0x00000990, 0x0000 }, /* R2448 - DSP2AUX1MIX Input 1 Source */ + { 0x00000998, 0x0000 }, /* R2456 - DSP2AUX2MIX Input 1 Source */ + { 0x000009A0, 0x0000 }, /* R2464 - DSP2AUX3MIX Input 1 Source */ + { 0x000009A8, 0x0000 }, /* R2472 - DSP2AUX4MIX Input 1 Source */ + { 0x000009B0, 0x0000 }, /* R2480 - DSP2AUX5MIX Input 1 Source */ + { 0x000009B8, 0x0000 }, /* R2488 - DSP2AUX6MIX Input 1 Source */ + { 0x000009C0, 0x0000 }, /* R2496 - DSP3LMIX Input 1 Source */ + { 0x000009C1, 0x0080 }, /* R2497 - DSP3LMIX Input 1 Volume */ + { 0x000009C2, 0x0000 }, /* R2498 - DSP3LMIX Input 2 Source */ + { 0x000009C3, 0x0080 }, /* R2499 - DSP3LMIX Input 2 Volume */ + { 0x000009C4, 0x0000 }, /* R2500 - DSP3LMIX Input 3 Source */ + { 0x000009C5, 0x0080 }, /* R2501 - DSP3LMIX Input 3 Volume */ + { 0x000009C6, 0x0000 }, /* R2502 - DSP3LMIX Input 4 Source */ + { 0x000009C7, 0x0080 }, /* R2503 - DSP3LMIX Input 4 Volume */ + { 0x000009C8, 0x0000 }, /* R2504 - DSP3RMIX Input 1 Source */ + { 0x000009C9, 0x0080 }, /* R2505 - DSP3RMIX Input 1 Volume */ + { 0x000009CA, 0x0000 }, /* R2506 - DSP3RMIX Input 2 Source */ + { 0x000009CB, 0x0080 }, /* R2507 - DSP3RMIX Input 2 Volume */ + { 0x000009CC, 0x0000 }, /* R2508 - DSP3RMIX Input 3 Source */ + { 0x000009CD, 0x0080 }, /* R2509 - DSP3RMIX Input 3 Volume */ + { 0x000009CE, 0x0000 }, /* R2510 - DSP3RMIX Input 4 Source */ + { 0x000009CF, 0x0080 }, /* R2511 - DSP3RMIX Input 4 Volume */ + { 0x000009D0, 0x0000 }, /* R2512 - DSP3AUX1MIX Input 1 Source */ + { 0x000009D8, 0x0000 }, /* R2520 - DSP3AUX2MIX Input 1 Source */ + { 0x000009E0, 0x0000 }, /* R2528 - DSP3AUX3MIX Input 1 Source */ + { 0x000009E8, 0x0000 }, /* R2536 - DSP3AUX4MIX Input 1 Source */ + { 0x000009F0, 0x0000 }, /* R2544 - DSP3AUX5MIX Input 1 Source */ + { 0x000009F8, 0x0000 }, /* R2552 - DSP3AUX6MIX Input 1 Source */ + { 0x00000A00, 0x0000 }, /* R2560 - DSP4LMIX Input 1 Source */ + { 0x00000A01, 0x0080 }, /* R2561 - DSP4LMIX Input 1 Volume */ + { 0x00000A02, 0x0000 }, /* R2562 - DSP4LMIX Input 2 Source */ + { 0x00000A03, 0x0080 }, /* R2563 - DSP4LMIX Input 2 Volume */ + { 0x00000A04, 0x0000 }, /* R2564 - DSP4LMIX Input 3 Source */ + { 0x00000A05, 0x0080 }, /* R2565 - DSP4LMIX Input 3 Volume */ + { 0x00000A06, 0x0000 }, /* R2566 - DSP4LMIX Input 4 Source */ + { 0x00000A07, 0x0080 }, /* R2567 - DSP4LMIX Input 4 Volume */ + { 0x00000A08, 0x0000 }, /* R2568 - DSP4RMIX Input 1 Source */ + { 0x00000A09, 0x0080 }, /* R2569 - DSP4RMIX Input 1 Volume */ + { 0x00000A0A, 0x0000 }, /* R2570 - DSP4RMIX Input 2 Source */ + { 0x00000A0B, 0x0080 }, /* R2571 - DSP4RMIX Input 2 Volume */ + { 0x00000A0C, 0x0000 }, /* R2572 - DSP4RMIX Input 3 Source */ + { 0x00000A0D, 0x0080 }, /* R2573 - DSP4RMIX Input 3 Volume */ + { 0x00000A0E, 0x0000 }, /* R2574 - DSP4RMIX Input 4 Source */ + { 0x00000A0F, 0x0080 }, /* R2575 - DSP4RMIX Input 4 Volume */ + { 0x00000A10, 0x0000 }, /* R2576 - DSP4AUX1MIX Input 1 Source */ + { 0x00000A18, 0x0000 }, /* R2584 - DSP4AUX2MIX Input 1 Source */ + { 0x00000A20, 0x0000 }, /* R2592 - DSP4AUX3MIX Input 1 Source */ + { 0x00000A28, 0x0000 }, /* R2600 - DSP4AUX4MIX Input 1 Source */ + { 0x00000A30, 0x0000 }, /* R2608 - DSP4AUX5MIX Input 1 Source */ + { 0x00000A38, 0x0000 }, /* R2616 - DSP4AUX6MIX Input 1 Source */ + { 0x00000A80, 0x0000 }, /* R2688 - ASRC1LMIX Input 1 Source */ + { 0x00000A88, 0x0000 }, /* R2696 - ASRC1RMIX Input 1 Source */ + { 0x00000A90, 0x0000 }, /* R2704 - ASRC2LMIX Input 1 Source */ + { 0x00000A98, 0x0000 }, /* R2712 - ASRC2RMIX Input 1 Source */ + { 0x00000B00, 0x0000 }, /* R2816 - ISRC1DEC1MIX Input 1 Source */ + { 0x00000B08, 0x0000 }, /* R2824 - ISRC1DEC2MIX Input 1 Source */ + { 0x00000B10, 0x0000 }, /* R2832 - ISRC1DEC3MIX Input 1 Source */ + { 0x00000B18, 0x0000 }, /* R2840 - ISRC1DEC4MIX Input 1 Source */ + { 0x00000B20, 0x0000 }, /* R2848 - ISRC1INT1MIX Input 1 Source */ + { 0x00000B28, 0x0000 }, /* R2856 - ISRC1INT2MIX Input 1 Source */ + { 0x00000B30, 0x0000 }, /* R2864 - ISRC1INT3MIX Input 1 Source */ + { 0x00000B38, 0x0000 }, /* R2872 - ISRC1INT4MIX Input 1 Source */ + { 0x00000B40, 0x0000 }, /* R2880 - ISRC2DEC1MIX Input 1 Source */ + { 0x00000B48, 0x0000 }, /* R2888 - ISRC2DEC2MIX Input 1 Source */ + { 0x00000B50, 0x0000 }, /* R2896 - ISRC2DEC3MIX Input 1 Source */ + { 0x00000B58, 0x0000 }, /* R2904 - ISRC2DEC4MIX Input 1 Source */ + { 0x00000B60, 0x0000 }, /* R2912 - ISRC2INT1MIX Input 1 Source */ + { 0x00000B68, 0x0000 }, /* R2920 - ISRC2INT2MIX Input 1 Source */ + { 0x00000B70, 0x0000 }, /* R2928 - ISRC2INT3MIX Input 1 Source */ + { 0x00000B78, 0x0000 }, /* R2936 - ISRC2INT4MIX Input 1 Source */ + { 0x00000B80, 0x0000 }, /* R2944 - ISRC3DEC1MIX Input 1 Source */ + { 0x00000B88, 0x0000 }, /* R2952 - ISRC3DEC2MIX Input 1 Source */ + { 0x00000B90, 0x0000 }, /* R2960 - ISRC3DEC3MIX Input 1 Source */ + { 0x00000B98, 0x0000 }, /* R2968 - ISRC3DEC4MIX Input 1 Source */ + { 0x00000BA0, 0x0000 }, /* R2976 - ISRC3INT1MIX Input 1 Source */ + { 0x00000BA8, 0x0000 }, /* R2984 - ISRC3INT2MIX Input 1 Source */ + { 0x00000BB0, 0x0000 }, /* R2992 - ISRC3INT3MIX Input 1 Source */ + { 0x00000BB8, 0x0000 }, /* R3000 - ISRC3INT4MIX Input 1 Source */ + { 0x00000C00, 0xA101 }, /* R3072 - GPIO1 CTRL */ + { 0x00000C01, 0xA101 }, /* R3073 - GPIO2 CTRL */ + { 0x00000C02, 0xA101 }, /* R3074 - GPIO3 CTRL */ + { 0x00000C03, 0xA101 }, /* R3075 - GPIO4 CTRL */ + { 0x00000C04, 0xA101 }, /* R3076 - GPIO5 CTRL */ + { 0x00000C0F, 0x0400 }, /* R3087 - IRQ CTRL 1 */ + { 0x00000C10, 0x1000 }, /* R3088 - GPIO Debounce Config */ + { 0x00000C20, 0x8002 }, /* R3104 - Misc Pad Ctrl 1 */ + { 0x00000C21, 0x8001 }, /* R3105 - Misc Pad Ctrl 2 */ + { 0x00000C22, 0x0000 }, /* R3106 - Misc Pad Ctrl 3 */ + { 0x00000C23, 0x0000 }, /* R3107 - Misc Pad Ctrl 4 */ + { 0x00000C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 5 */ + { 0x00000C25, 0x0000 }, /* R3109 - Misc Pad Ctrl 6 */ + { 0x00000C30, 0x8282 }, /* R3120 - Misc Pad Ctrl 7 */ + { 0x00000C31, 0x0082 }, /* R3121 - Misc Pad Ctrl 8 */ + { 0x00000C32, 0x8282 }, /* R3122 - Misc Pad Ctrl 9 */ + { 0x00000C33, 0x8282 }, /* R3123 - Misc Pad Ctrl 10 */ + { 0x00000C34, 0x8282 }, /* R3124 - Misc Pad Ctrl 11 */ + { 0x00000C35, 0x8282 }, /* R3125 - Misc Pad Ctrl 12 */ + { 0x00000C36, 0x8282 }, /* R3126 - Misc Pad Ctrl 13 */ + { 0x00000C37, 0x8282 }, /* R3127 - Misc Pad Ctrl 14 */ + { 0x00000C38, 0x8282 }, /* R3128 - Misc Pad Ctrl 15 */ + { 0x00000C39, 0x8282 }, /* R3129 - Misc Pad Ctrl 16 */ + { 0x00000C3A, 0x8282 }, /* R3130 - Misc Pad Ctrl 17 */ + { 0x00000C3B, 0x8282 }, /* R3131 - Misc Pad Ctrl 18 */ + { 0x00000D08, 0xFFFF }, /* R3336 - Interrupt Status 1 Mask */ + { 0x00000D09, 0xFFFF }, /* R3337 - Interrupt Status 2 Mask */ + { 0x00000D0A, 0xFFFF }, /* R3338 - Interrupt Status 3 Mask */ + { 0x00000D0B, 0xFFFF }, /* R3339 - Interrupt Status 4 Mask */ + { 0x00000D0C, 0xFEFF }, /* R3340 - Interrupt Status 5 Mask */ + { 0x00000D0F, 0x0000 }, /* R3343 - Interrupt Control */ + { 0x00000D18, 0xFFFF }, /* R3352 - IRQ2 Status 1 Mask */ + { 0x00000D19, 0xFFFF }, /* R3353 - IRQ2 Status 2 Mask */ + { 0x00000D1A, 0xFFFF }, /* R3354 - IRQ2 Status 3 Mask */ + { 0x00000D1B, 0xFFFF }, /* R3355 - IRQ2 Status 4 Mask */ + { 0x00000D1C, 0xFFFF }, /* R3356 - IRQ2 Status 5 Mask */ + { 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */ + { 0x00000D50, 0x0000 }, /* R3408 - AOD wkup and trig */ + { 0x00000D53, 0xFFFF }, /* R3411 - AOD IRQ Mask IRQ1 */ + { 0x00000D54, 0xFFFF }, /* R3412 - AOD IRQ Mask IRQ2 */ + { 0x00000D56, 0x0000 }, /* R3414 - Jack detect debounce */ + { 0x00000E00, 0x0000 }, /* R3584 - FX_Ctrl1 */ + { 0x00000E01, 0x0000 }, /* R3585 - FX_Ctrl2 */ + { 0x00000E10, 0x6318 }, /* R3600 - EQ1_1 */ + { 0x00000E11, 0x6300 }, /* R3601 - EQ1_2 */ + { 0x00000E12, 0x0FC8 }, /* R3602 - EQ1_3 */ + { 0x00000E13, 0x03FE }, /* R3603 - EQ1_4 */ + { 0x00000E14, 0x00E0 }, /* R3604 - EQ1_5 */ + { 0x00000E15, 0x1EC4 }, /* R3605 - EQ1_6 */ + { 0x00000E16, 0xF136 }, /* R3606 - EQ1_7 */ + { 0x00000E17, 0x0409 }, /* R3607 - EQ1_8 */ + { 0x00000E18, 0x04CC }, /* R3608 - EQ1_9 */ + { 0x00000E19, 0x1C9B }, /* R3609 - EQ1_10 */ + { 0x00000E1A, 0xF337 }, /* R3610 - EQ1_11 */ + { 0x00000E1B, 0x040B }, /* R3611 - EQ1_12 */ + { 0x00000E1C, 0x0CBB }, /* R3612 - EQ1_13 */ + { 0x00000E1D, 0x16F8 }, /* R3613 - EQ1_14 */ + { 0x00000E1E, 0xF7D9 }, /* R3614 - EQ1_15 */ + { 0x00000E1F, 0x040A }, /* R3615 - EQ1_16 */ + { 0x00000E20, 0x1F14 }, /* R3616 - EQ1_17 */ + { 0x00000E21, 0x058C }, /* R3617 - EQ1_18 */ + { 0x00000E22, 0x0563 }, /* R3618 - EQ1_19 */ + { 0x00000E23, 0x4000 }, /* R3619 - EQ1_20 */ + { 0x00000E24, 0x0B75 }, /* R3620 - EQ1_21 */ + { 0x00000E26, 0x6318 }, /* R3622 - EQ2_1 */ + { 0x00000E27, 0x6300 }, /* R3623 - EQ2_2 */ + { 0x00000E28, 0x0FC8 }, /* R3624 - EQ2_3 */ + { 0x00000E29, 0x03FE }, /* R3625 - EQ2_4 */ + { 0x00000E2A, 0x00E0 }, /* R3626 - EQ2_5 */ + { 0x00000E2B, 0x1EC4 }, /* R3627 - EQ2_6 */ + { 0x00000E2C, 0xF136 }, /* R3628 - EQ2_7 */ + { 0x00000E2D, 0x0409 }, /* R3629 - EQ2_8 */ + { 0x00000E2E, 0x04CC }, /* R3630 - EQ2_9 */ + { 0x00000E2F, 0x1C9B }, /* R3631 - EQ2_10 */ + { 0x00000E30, 0xF337 }, /* R3632 - EQ2_11 */ + { 0x00000E31, 0x040B }, /* R3633 - EQ2_12 */ + { 0x00000E32, 0x0CBB }, /* R3634 - EQ2_13 */ + { 0x00000E33, 0x16F8 }, /* R3635 - EQ2_14 */ + { 0x00000E34, 0xF7D9 }, /* R3636 - EQ2_15 */ + { 0x00000E35, 0x040A }, /* R3637 - EQ2_16 */ + { 0x00000E36, 0x1F14 }, /* R3638 - EQ2_17 */ + { 0x00000E37, 0x058C }, /* R3639 - EQ2_18 */ + { 0x00000E38, 0x0563 }, /* R3640 - EQ2_19 */ + { 0x00000E39, 0x4000 }, /* R3641 - EQ2_20 */ + { 0x00000E3A, 0x0B75 }, /* R3642 - EQ2_21 */ + { 0x00000E3C, 0x6318 }, /* R3644 - EQ3_1 */ + { 0x00000E3D, 0x6300 }, /* R3645 - EQ3_2 */ + { 0x00000E3E, 0x0FC8 }, /* R3646 - EQ3_3 */ + { 0x00000E3F, 0x03FE }, /* R3647 - EQ3_4 */ + { 0x00000E40, 0x00E0 }, /* R3648 - EQ3_5 */ + { 0x00000E41, 0x1EC4 }, /* R3649 - EQ3_6 */ + { 0x00000E42, 0xF136 }, /* R3650 - EQ3_7 */ + { 0x00000E43, 0x0409 }, /* R3651 - EQ3_8 */ + { 0x00000E44, 0x04CC }, /* R3652 - EQ3_9 */ + { 0x00000E45, 0x1C9B }, /* R3653 - EQ3_10 */ + { 0x00000E46, 0xF337 }, /* R3654 - EQ3_11 */ + { 0x00000E47, 0x040B }, /* R3655 - EQ3_12 */ + { 0x00000E48, 0x0CBB }, /* R3656 - EQ3_13 */ + { 0x00000E49, 0x16F8 }, /* R3657 - EQ3_14 */ + { 0x00000E4A, 0xF7D9 }, /* R3658 - EQ3_15 */ + { 0x00000E4B, 0x040A }, /* R3659 - EQ3_16 */ + { 0x00000E4C, 0x1F14 }, /* R3660 - EQ3_17 */ + { 0x00000E4D, 0x058C }, /* R3661 - EQ3_18 */ + { 0x00000E4E, 0x0563 }, /* R3662 - EQ3_19 */ + { 0x00000E4F, 0x4000 }, /* R3663 - EQ3_20 */ + { 0x00000E50, 0x0B75 }, /* R3664 - EQ3_21 */ + { 0x00000E52, 0x6318 }, /* R3666 - EQ4_1 */ + { 0x00000E53, 0x6300 }, /* R3667 - EQ4_2 */ + { 0x00000E54, 0x0FC8 }, /* R3668 - EQ4_3 */ + { 0x00000E55, 0x03FE }, /* R3669 - EQ4_4 */ + { 0x00000E56, 0x00E0 }, /* R3670 - EQ4_5 */ + { 0x00000E57, 0x1EC4 }, /* R3671 - EQ4_6 */ + { 0x00000E58, 0xF136 }, /* R3672 - EQ4_7 */ + { 0x00000E59, 0x0409 }, /* R3673 - EQ4_8 */ + { 0x00000E5A, 0x04CC }, /* R3674 - EQ4_9 */ + { 0x00000E5B, 0x1C9B }, /* R3675 - EQ4_10 */ + { 0x00000E5C, 0xF337 }, /* R3676 - EQ4_11 */ + { 0x00000E5D, 0x040B }, /* R3677 - EQ4_12 */ + { 0x00000E5E, 0x0CBB }, /* R3678 - EQ4_13 */ + { 0x00000E5F, 0x16F8 }, /* R3679 - EQ4_14 */ + { 0x00000E60, 0xF7D9 }, /* R3680 - EQ4_15 */ + { 0x00000E61, 0x040A }, /* R3681 - EQ4_16 */ + { 0x00000E62, 0x1F14 }, /* R3682 - EQ4_17 */ + { 0x00000E63, 0x058C }, /* R3683 - EQ4_18 */ + { 0x00000E64, 0x0563 }, /* R3684 - EQ4_19 */ + { 0x00000E65, 0x4000 }, /* R3685 - EQ4_20 */ + { 0x00000E66, 0x0B75 }, /* R3686 - EQ4_21 */ + { 0x00000E80, 0x0018 }, /* R3712 - DRC1 ctrl1 */ + { 0x00000E81, 0x0933 }, /* R3713 - DRC1 ctrl2 */ + { 0x00000E82, 0x0018 }, /* R3714 - DRC1 ctrl3 */ + { 0x00000E83, 0x0000 }, /* R3715 - DRC1 ctrl4 */ + { 0x00000E84, 0x0000 }, /* R3716 - DRC1 ctrl5 */ + { 0x00000E89, 0x0018 }, /* R3721 - DRC2 ctrl1 */ + { 0x00000E8A, 0x0933 }, /* R3722 - DRC2 ctrl2 */ + { 0x00000E8B, 0x0018 }, /* R3723 - DRC2 ctrl3 */ + { 0x00000E8C, 0x0000 }, /* R3724 - DRC2 ctrl4 */ + { 0x00000E8D, 0x0000 }, /* R3725 - DRC2 ctrl5 */ + { 0x00000EC0, 0x0000 }, /* R3776 - HPLPF1_1 */ + { 0x00000EC1, 0x0000 }, /* R3777 - HPLPF1_2 */ + { 0x00000EC4, 0x0000 }, /* R3780 - HPLPF2_1 */ + { 0x00000EC5, 0x0000 }, /* R3781 - HPLPF2_2 */ + { 0x00000EC8, 0x0000 }, /* R3784 - HPLPF3_1 */ + { 0x00000EC9, 0x0000 }, /* R3785 - HPLPF3_2 */ + { 0x00000ECC, 0x0000 }, /* R3788 - HPLPF4_1 */ + { 0x00000ECD, 0x0000 }, /* R3789 - HPLPF4_2 */ + { 0x00000EE0, 0x0000 }, /* R3808 - ASRC_ENABLE */ + { 0x00000EE2, 0x0000 }, /* R3810 - ASRC_RATE1 */ + { 0x00000EF0, 0x0000 }, /* R3824 - ISRC 1 CTRL 1 */ + { 0x00000EF1, 0x0000 }, /* R3825 - ISRC 1 CTRL 2 */ + { 0x00000EF2, 0x0000 }, /* R3826 - ISRC 1 CTRL 3 */ + { 0x00000EF3, 0x0000 }, /* R3827 - ISRC 2 CTRL 1 */ + { 0x00000EF4, 0x0000 }, /* R3828 - ISRC 2 CTRL 2 */ + { 0x00000EF5, 0x0000 }, /* R3829 - ISRC 2 CTRL 3 */ + { 0x00000EF6, 0x0000 }, /* R3830 - ISRC 3 CTRL 1 */ + { 0x00000EF7, 0x0000 }, /* R3831 - ISRC 3 CTRL 2 */ + { 0x00000EF8, 0x0000 }, /* R3832 - ISRC 3 CTRL 3 */ + { 0x00000F00, 0x0000 }, /* R3840 - Clock Control */ + { 0x00000F01, 0x0000 }, /* R3841 - ANC_SRC */ + { 0x00001100, 0x0010 }, /* R4352 - DSP1 Control 1 */ + { 0x00001101, 0x0000 }, /* R4353 - DSP1 Clocking 1 */ + { 0x00001200, 0x0010 }, /* R4608 - DSP2 Control 1 */ + { 0x00001201, 0x0000 }, /* R4609 - DSP2 Clocking 1 */ + { 0x00001300, 0x0010 }, /* R4864 - DSP3 Control 1 */ + { 0x00001301, 0x0000 }, /* R4865 - DSP3 Clocking 1 */ + { 0x00001400, 0x0010 }, /* R5120 - DSP4 Control 1 */ + { 0x00001401, 0x0000 }, /* R5121 - DSP4 Clocking 1 */ + { 0x00001404, 0x0000 }, /* R5124 - DSP4 Status 1 */ +}; + +static bool wm5110_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ARIZONA_SOFTWARE_RESET: + case ARIZONA_DEVICE_REVISION: + case ARIZONA_CTRL_IF_SPI_CFG_1: + case ARIZONA_CTRL_IF_I2C1_CFG_1: + case ARIZONA_CTRL_IF_I2C2_CFG_1: + case ARIZONA_CTRL_IF_I2C1_CFG_2: + case ARIZONA_CTRL_IF_I2C2_CFG_2: + case ARIZONA_WRITE_SEQUENCER_CTRL_0: + case ARIZONA_WRITE_SEQUENCER_CTRL_1: + case ARIZONA_WRITE_SEQUENCER_CTRL_2: + case ARIZONA_TONE_GENERATOR_1: + case ARIZONA_TONE_GENERATOR_2: + case ARIZONA_TONE_GENERATOR_3: + case ARIZONA_TONE_GENERATOR_4: + case ARIZONA_TONE_GENERATOR_5: + case ARIZONA_PWM_DRIVE_1: + case ARIZONA_PWM_DRIVE_2: + case ARIZONA_PWM_DRIVE_3: + case ARIZONA_WAKE_CONTROL: + case ARIZONA_SEQUENCE_CONTROL: + case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1: + case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2: + case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3: + case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4: + case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1: + case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2: + case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3: + case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4: + case ARIZONA_COMFORT_NOISE_GENERATOR: + case ARIZONA_HAPTICS_CONTROL_1: + case ARIZONA_HAPTICS_CONTROL_2: + case ARIZONA_HAPTICS_PHASE_1_INTENSITY: + case ARIZONA_HAPTICS_PHASE_1_DURATION: + case ARIZONA_HAPTICS_PHASE_2_INTENSITY: + case ARIZONA_HAPTICS_PHASE_2_DURATION: + case ARIZONA_HAPTICS_PHASE_3_INTENSITY: + case ARIZONA_HAPTICS_PHASE_3_DURATION: + case ARIZONA_HAPTICS_STATUS: + case ARIZONA_CLOCK_32K_1: + case ARIZONA_SYSTEM_CLOCK_1: + case ARIZONA_SAMPLE_RATE_1: + case ARIZONA_SAMPLE_RATE_2: + case ARIZONA_SAMPLE_RATE_3: + case ARIZONA_SAMPLE_RATE_1_STATUS: + case ARIZONA_SAMPLE_RATE_2_STATUS: + case ARIZONA_SAMPLE_RATE_3_STATUS: + case ARIZONA_ASYNC_CLOCK_1: + case ARIZONA_ASYNC_SAMPLE_RATE_1: + case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS: + case ARIZONA_OUTPUT_SYSTEM_CLOCK: + case ARIZONA_OUTPUT_ASYNC_CLOCK: + case ARIZONA_RATE_ESTIMATOR_1: + case ARIZONA_RATE_ESTIMATOR_2: + case ARIZONA_RATE_ESTIMATOR_3: + case ARIZONA_RATE_ESTIMATOR_4: + case ARIZONA_RATE_ESTIMATOR_5: + case ARIZONA_FLL1_CONTROL_1: + case ARIZONA_FLL1_CONTROL_2: + case ARIZONA_FLL1_CONTROL_3: + case ARIZONA_FLL1_CONTROL_4: + case ARIZONA_FLL1_CONTROL_5: + case ARIZONA_FLL1_CONTROL_6: + case ARIZONA_FLL1_LOOP_FILTER_TEST_1: + case ARIZONA_FLL1_NCO_TEST_0: + case ARIZONA_FLL1_SYNCHRONISER_1: + case ARIZONA_FLL1_SYNCHRONISER_2: + case ARIZONA_FLL1_SYNCHRONISER_3: + case ARIZONA_FLL1_SYNCHRONISER_4: + case ARIZONA_FLL1_SYNCHRONISER_5: + case ARIZONA_FLL1_SYNCHRONISER_6: + case ARIZONA_FLL1_SPREAD_SPECTRUM: + case ARIZONA_FLL1_GPIO_CLOCK: + case ARIZONA_FLL2_CONTROL_1: + case ARIZONA_FLL2_CONTROL_2: + case ARIZONA_FLL2_CONTROL_3: + case ARIZONA_FLL2_CONTROL_4: + case ARIZONA_FLL2_CONTROL_5: + case ARIZONA_FLL2_CONTROL_6: + case ARIZONA_FLL2_LOOP_FILTER_TEST_1: + case ARIZONA_FLL2_NCO_TEST_0: + case ARIZONA_FLL2_SYNCHRONISER_1: + case ARIZONA_FLL2_SYNCHRONISER_2: + case ARIZONA_FLL2_SYNCHRONISER_3: + case ARIZONA_FLL2_SYNCHRONISER_4: + case ARIZONA_FLL2_SYNCHRONISER_5: + case ARIZONA_FLL2_SYNCHRONISER_6: + case ARIZONA_FLL2_SPREAD_SPECTRUM: + case ARIZONA_FLL2_GPIO_CLOCK: + case ARIZONA_MIC_CHARGE_PUMP_1: + case ARIZONA_LDO1_CONTROL_1: + case ARIZONA_LDO2_CONTROL_1: + case ARIZONA_MIC_BIAS_CTRL_1: + case ARIZONA_MIC_BIAS_CTRL_2: + case ARIZONA_MIC_BIAS_CTRL_3: + case ARIZONA_ACCESSORY_DETECT_MODE_1: + case ARIZONA_HEADPHONE_DETECT_1: + case ARIZONA_HEADPHONE_DETECT_2: + case ARIZONA_MIC_DETECT_1: + case ARIZONA_MIC_DETECT_2: + case ARIZONA_MIC_DETECT_3: + case ARIZONA_MIC_NOISE_MIX_CONTROL_1: + case ARIZONA_JACK_DETECT_ANALOGUE: + case ARIZONA_INPUT_ENABLES: + case ARIZONA_INPUT_ENABLES_STATUS: + case ARIZONA_INPUT_RATE: + case ARIZONA_INPUT_VOLUME_RAMP: + case ARIZONA_IN1L_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_1L: + case ARIZONA_DMIC1L_CONTROL: + case ARIZONA_IN1R_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_1R: + case ARIZONA_DMIC1R_CONTROL: + case ARIZONA_IN2L_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_2L: + case ARIZONA_DMIC2L_CONTROL: + case ARIZONA_IN2R_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_2R: + case ARIZONA_DMIC2R_CONTROL: + case ARIZONA_IN3L_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_3L: + case ARIZONA_DMIC3L_CONTROL: + case ARIZONA_IN3R_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_3R: + case ARIZONA_DMIC3R_CONTROL: + case ARIZONA_IN4L_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_4L: + case ARIZONA_DMIC4L_CONTROL: + case ARIZONA_ADC_DIGITAL_VOLUME_4R: + case ARIZONA_DMIC4R_CONTROL: + case ARIZONA_OUTPUT_ENABLES_1: + case ARIZONA_OUTPUT_STATUS_1: + case ARIZONA_RAW_OUTPUT_STATUS_1: + case ARIZONA_OUTPUT_RATE_1: + case ARIZONA_OUTPUT_VOLUME_RAMP: + case ARIZONA_OUTPUT_PATH_CONFIG_1L: + case ARIZONA_DAC_DIGITAL_VOLUME_1L: + case ARIZONA_DAC_VOLUME_LIMIT_1L: + case ARIZONA_NOISE_GATE_SELECT_1L: + case ARIZONA_OUTPUT_PATH_CONFIG_1R: + case ARIZONA_DAC_DIGITAL_VOLUME_1R: + case ARIZONA_DAC_VOLUME_LIMIT_1R: + case ARIZONA_NOISE_GATE_SELECT_1R: + case ARIZONA_OUTPUT_PATH_CONFIG_2L: + case ARIZONA_DAC_DIGITAL_VOLUME_2L: + case ARIZONA_DAC_VOLUME_LIMIT_2L: + case ARIZONA_NOISE_GATE_SELECT_2L: + case ARIZONA_OUTPUT_PATH_CONFIG_2R: + case ARIZONA_DAC_DIGITAL_VOLUME_2R: + case ARIZONA_DAC_VOLUME_LIMIT_2R: + case ARIZONA_NOISE_GATE_SELECT_2R: + case ARIZONA_OUTPUT_PATH_CONFIG_3L: + case ARIZONA_DAC_DIGITAL_VOLUME_3L: + case ARIZONA_DAC_VOLUME_LIMIT_3L: + case ARIZONA_NOISE_GATE_SELECT_3L: + case ARIZONA_OUTPUT_PATH_CONFIG_3R: + case ARIZONA_DAC_DIGITAL_VOLUME_3R: + case ARIZONA_DAC_VOLUME_LIMIT_3R: + case ARIZONA_NOISE_GATE_SELECT_3R: + case ARIZONA_OUTPUT_PATH_CONFIG_4L: + case ARIZONA_DAC_DIGITAL_VOLUME_4L: + case ARIZONA_OUT_VOLUME_4L: + case ARIZONA_NOISE_GATE_SELECT_4L: + case ARIZONA_OUTPUT_PATH_CONFIG_4R: + case ARIZONA_DAC_DIGITAL_VOLUME_4R: + case ARIZONA_OUT_VOLUME_4R: + case ARIZONA_NOISE_GATE_SELECT_4R: + case ARIZONA_OUTPUT_PATH_CONFIG_5L: + case ARIZONA_DAC_DIGITAL_VOLUME_5L: + case ARIZONA_DAC_VOLUME_LIMIT_5L: + case ARIZONA_NOISE_GATE_SELECT_5L: + case ARIZONA_OUTPUT_PATH_CONFIG_5R: + case ARIZONA_DAC_DIGITAL_VOLUME_5R: + case ARIZONA_DAC_VOLUME_LIMIT_5R: + case ARIZONA_NOISE_GATE_SELECT_5R: + case ARIZONA_OUTPUT_PATH_CONFIG_6L: + case ARIZONA_DAC_DIGITAL_VOLUME_6L: + case ARIZONA_DAC_VOLUME_LIMIT_6L: + case ARIZONA_NOISE_GATE_SELECT_6L: + case ARIZONA_OUTPUT_PATH_CONFIG_6R: + case ARIZONA_DAC_DIGITAL_VOLUME_6R: + case ARIZONA_DAC_VOLUME_LIMIT_6R: + case ARIZONA_NOISE_GATE_SELECT_6R: + case ARIZONA_DAC_AEC_CONTROL_1: + case ARIZONA_NOISE_GATE_CONTROL: + case ARIZONA_PDM_SPK1_CTRL_1: + case ARIZONA_PDM_SPK1_CTRL_2: + case ARIZONA_PDM_SPK2_CTRL_1: + case ARIZONA_PDM_SPK2_CTRL_2: + case ARIZONA_AIF1_BCLK_CTRL: + case ARIZONA_AIF1_TX_PIN_CTRL: + case ARIZONA_AIF1_RX_PIN_CTRL: + case ARIZONA_AIF1_RATE_CTRL: + case ARIZONA_AIF1_FORMAT: + case ARIZONA_AIF1_TX_BCLK_RATE: + case ARIZONA_AIF1_RX_BCLK_RATE: + case ARIZONA_AIF1_FRAME_CTRL_1: + case ARIZONA_AIF1_FRAME_CTRL_2: + case ARIZONA_AIF1_FRAME_CTRL_3: + case ARIZONA_AIF1_FRAME_CTRL_4: + case ARIZONA_AIF1_FRAME_CTRL_5: + case ARIZONA_AIF1_FRAME_CTRL_6: + case ARIZONA_AIF1_FRAME_CTRL_7: + case ARIZONA_AIF1_FRAME_CTRL_8: + case ARIZONA_AIF1_FRAME_CTRL_9: + case ARIZONA_AIF1_FRAME_CTRL_10: + case ARIZONA_AIF1_FRAME_CTRL_11: + case ARIZONA_AIF1_FRAME_CTRL_12: + case ARIZONA_AIF1_FRAME_CTRL_13: + case ARIZONA_AIF1_FRAME_CTRL_14: + case ARIZONA_AIF1_FRAME_CTRL_15: + case ARIZONA_AIF1_FRAME_CTRL_16: + case ARIZONA_AIF1_FRAME_CTRL_17: + case ARIZONA_AIF1_FRAME_CTRL_18: + case ARIZONA_AIF1_TX_ENABLES: + case ARIZONA_AIF1_RX_ENABLES: + case ARIZONA_AIF2_BCLK_CTRL: + case ARIZONA_AIF2_TX_PIN_CTRL: + case ARIZONA_AIF2_RX_PIN_CTRL: + case ARIZONA_AIF2_RATE_CTRL: + case ARIZONA_AIF2_FORMAT: + case ARIZONA_AIF2_TX_BCLK_RATE: + case ARIZONA_AIF2_RX_BCLK_RATE: + case ARIZONA_AIF2_FRAME_CTRL_1: + case ARIZONA_AIF2_FRAME_CTRL_2: + case ARIZONA_AIF2_FRAME_CTRL_3: + case ARIZONA_AIF2_FRAME_CTRL_4: + case ARIZONA_AIF2_FRAME_CTRL_11: + case ARIZONA_AIF2_FRAME_CTRL_12: + case ARIZONA_AIF2_TX_ENABLES: + case ARIZONA_AIF2_RX_ENABLES: + case ARIZONA_AIF3_BCLK_CTRL: + case ARIZONA_AIF3_TX_PIN_CTRL: + case ARIZONA_AIF3_RX_PIN_CTRL: + case ARIZONA_AIF3_RATE_CTRL: + case ARIZONA_AIF3_FORMAT: + case ARIZONA_AIF3_TX_BCLK_RATE: + case ARIZONA_AIF3_RX_BCLK_RATE: + case ARIZONA_AIF3_FRAME_CTRL_1: + case ARIZONA_AIF3_FRAME_CTRL_2: + case ARIZONA_AIF3_FRAME_CTRL_3: + case ARIZONA_AIF3_FRAME_CTRL_4: + case ARIZONA_AIF3_FRAME_CTRL_11: + case ARIZONA_AIF3_FRAME_CTRL_12: + case ARIZONA_AIF3_TX_ENABLES: + case ARIZONA_AIF3_RX_ENABLES: + case ARIZONA_SLIMBUS_FRAMER_REF_GEAR: + case ARIZONA_SLIMBUS_RATES_1: + case ARIZONA_SLIMBUS_RATES_2: + case ARIZONA_SLIMBUS_RATES_3: + case ARIZONA_SLIMBUS_RATES_4: + case ARIZONA_SLIMBUS_RATES_5: + case ARIZONA_SLIMBUS_RATES_6: + case ARIZONA_SLIMBUS_RATES_7: + case ARIZONA_SLIMBUS_RATES_8: + case ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE: + case ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE: + case ARIZONA_SLIMBUS_RX_PORT_STATUS: + case ARIZONA_SLIMBUS_TX_PORT_STATUS: + case ARIZONA_PWM1MIX_INPUT_1_SOURCE: + case ARIZONA_PWM1MIX_INPUT_1_VOLUME: + case ARIZONA_PWM1MIX_INPUT_2_SOURCE: + case ARIZONA_PWM1MIX_INPUT_2_VOLUME: + case ARIZONA_PWM1MIX_INPUT_3_SOURCE: + case ARIZONA_PWM1MIX_INPUT_3_VOLUME: + case ARIZONA_PWM1MIX_INPUT_4_SOURCE: + case ARIZONA_PWM1MIX_INPUT_4_VOLUME: + case ARIZONA_PWM2MIX_INPUT_1_SOURCE: + case ARIZONA_PWM2MIX_INPUT_1_VOLUME: + case ARIZONA_PWM2MIX_INPUT_2_SOURCE: + case ARIZONA_PWM2MIX_INPUT_2_VOLUME: + case ARIZONA_PWM2MIX_INPUT_3_SOURCE: + case ARIZONA_PWM2MIX_INPUT_3_VOLUME: + case ARIZONA_PWM2MIX_INPUT_4_SOURCE: + case ARIZONA_PWM2MIX_INPUT_4_VOLUME: + case ARIZONA_MICMIX_INPUT_1_SOURCE: + case ARIZONA_MICMIX_INPUT_1_VOLUME: + case ARIZONA_MICMIX_INPUT_2_SOURCE: + case ARIZONA_MICMIX_INPUT_2_VOLUME: + case ARIZONA_MICMIX_INPUT_3_SOURCE: + case ARIZONA_MICMIX_INPUT_3_VOLUME: + case ARIZONA_MICMIX_INPUT_4_SOURCE: + case ARIZONA_MICMIX_INPUT_4_VOLUME: + case ARIZONA_NOISEMIX_INPUT_1_SOURCE: + case ARIZONA_NOISEMIX_INPUT_1_VOLUME: + case ARIZONA_NOISEMIX_INPUT_2_SOURCE: + case ARIZONA_NOISEMIX_INPUT_2_VOLUME: + case ARIZONA_NOISEMIX_INPUT_3_SOURCE: + case ARIZONA_NOISEMIX_INPUT_3_VOLUME: + case ARIZONA_NOISEMIX_INPUT_4_SOURCE: + case ARIZONA_NOISEMIX_INPUT_4_VOLUME: + case ARIZONA_OUT1LMIX_INPUT_1_SOURCE: + case ARIZONA_OUT1LMIX_INPUT_1_VOLUME: + case ARIZONA_OUT1LMIX_INPUT_2_SOURCE: + case ARIZONA_OUT1LMIX_INPUT_2_VOLUME: + case ARIZONA_OUT1LMIX_INPUT_3_SOURCE: + case ARIZONA_OUT1LMIX_INPUT_3_VOLUME: + case ARIZONA_OUT1LMIX_INPUT_4_SOURCE: + case ARIZONA_OUT1LMIX_INPUT_4_VOLUME: + case ARIZONA_OUT1RMIX_INPUT_1_SOURCE: + case ARIZONA_OUT1RMIX_INPUT_1_VOLUME: + case ARIZONA_OUT1RMIX_INPUT_2_SOURCE: + case ARIZONA_OUT1RMIX_INPUT_2_VOLUME: + case ARIZONA_OUT1RMIX_INPUT_3_SOURCE: + case ARIZONA_OUT1RMIX_INPUT_3_VOLUME: + case ARIZONA_OUT1RMIX_INPUT_4_SOURCE: + case ARIZONA_OUT1RMIX_INPUT_4_VOLUME: + case ARIZONA_OUT2LMIX_INPUT_1_SOURCE: + case ARIZONA_OUT2LMIX_INPUT_1_VOLUME: + case ARIZONA_OUT2LMIX_INPUT_2_SOURCE: + case ARIZONA_OUT2LMIX_INPUT_2_VOLUME: + case ARIZONA_OUT2LMIX_INPUT_3_SOURCE: + case ARIZONA_OUT2LMIX_INPUT_3_VOLUME: + case ARIZONA_OUT2LMIX_INPUT_4_SOURCE: + case ARIZONA_OUT2LMIX_INPUT_4_VOLUME: + case ARIZONA_OUT2RMIX_INPUT_1_SOURCE: + case ARIZONA_OUT2RMIX_INPUT_1_VOLUME: + case ARIZONA_OUT2RMIX_INPUT_2_SOURCE: + case ARIZONA_OUT2RMIX_INPUT_2_VOLUME: + case ARIZONA_OUT2RMIX_INPUT_3_SOURCE: + case ARIZONA_OUT2RMIX_INPUT_3_VOLUME: + case ARIZONA_OUT2RMIX_INPUT_4_SOURCE: + case ARIZONA_OUT2RMIX_INPUT_4_VOLUME: + case ARIZONA_OUT3LMIX_INPUT_1_SOURCE: + case ARIZONA_OUT3LMIX_INPUT_1_VOLUME: + case ARIZONA_OUT3LMIX_INPUT_2_SOURCE: + case ARIZONA_OUT3LMIX_INPUT_2_VOLUME: + case ARIZONA_OUT3LMIX_INPUT_3_SOURCE: + case ARIZONA_OUT3LMIX_INPUT_3_VOLUME: + case ARIZONA_OUT3LMIX_INPUT_4_SOURCE: + case ARIZONA_OUT3LMIX_INPUT_4_VOLUME: + case ARIZONA_OUT3RMIX_INPUT_1_SOURCE: + case ARIZONA_OUT3RMIX_INPUT_1_VOLUME: + case ARIZONA_OUT3RMIX_INPUT_2_SOURCE: + case ARIZONA_OUT3RMIX_INPUT_2_VOLUME: + case ARIZONA_OUT3RMIX_INPUT_3_SOURCE: + case ARIZONA_OUT3RMIX_INPUT_3_VOLUME: + case ARIZONA_OUT3RMIX_INPUT_4_SOURCE: + case ARIZONA_OUT3RMIX_INPUT_4_VOLUME: + case ARIZONA_OUT4LMIX_INPUT_1_SOURCE: + case ARIZONA_OUT4LMIX_INPUT_1_VOLUME: + case ARIZONA_OUT4LMIX_INPUT_2_SOURCE: + case ARIZONA_OUT4LMIX_INPUT_2_VOLUME: + case ARIZONA_OUT4LMIX_INPUT_3_SOURCE: + case ARIZONA_OUT4LMIX_INPUT_3_VOLUME: + case ARIZONA_OUT4LMIX_INPUT_4_SOURCE: + case ARIZONA_OUT4LMIX_INPUT_4_VOLUME: + case ARIZONA_OUT4RMIX_INPUT_1_SOURCE: + case ARIZONA_OUT4RMIX_INPUT_1_VOLUME: + case ARIZONA_OUT4RMIX_INPUT_2_SOURCE: + case ARIZONA_OUT4RMIX_INPUT_2_VOLUME: + case ARIZONA_OUT4RMIX_INPUT_3_SOURCE: + case ARIZONA_OUT4RMIX_INPUT_3_VOLUME: + case ARIZONA_OUT4RMIX_INPUT_4_SOURCE: + case ARIZONA_OUT4RMIX_INPUT_4_VOLUME: + case ARIZONA_OUT5LMIX_INPUT_1_SOURCE: + case ARIZONA_OUT5LMIX_INPUT_1_VOLUME: + case ARIZONA_OUT5LMIX_INPUT_2_SOURCE: + case ARIZONA_OUT5LMIX_INPUT_2_VOLUME: + case ARIZONA_OUT5LMIX_INPUT_3_SOURCE: + case ARIZONA_OUT5LMIX_INPUT_3_VOLUME: + case ARIZONA_OUT5LMIX_INPUT_4_SOURCE: + case ARIZONA_OUT5LMIX_INPUT_4_VOLUME: + case ARIZONA_OUT5RMIX_INPUT_1_SOURCE: + case ARIZONA_OUT5RMIX_INPUT_1_VOLUME: + case ARIZONA_OUT5RMIX_INPUT_2_SOURCE: + case ARIZONA_OUT5RMIX_INPUT_2_VOLUME: + case ARIZONA_OUT5RMIX_INPUT_3_SOURCE: + case ARIZONA_OUT5RMIX_INPUT_3_VOLUME: + case ARIZONA_OUT5RMIX_INPUT_4_SOURCE: + case ARIZONA_OUT5RMIX_INPUT_4_VOLUME: + case ARIZONA_OUT6LMIX_INPUT_1_SOURCE: + case ARIZONA_OUT6LMIX_INPUT_1_VOLUME: + case ARIZONA_OUT6LMIX_INPUT_2_SOURCE: + case ARIZONA_OUT6LMIX_INPUT_2_VOLUME: + case ARIZONA_OUT6LMIX_INPUT_3_SOURCE: + case ARIZONA_OUT6LMIX_INPUT_3_VOLUME: + case ARIZONA_OUT6LMIX_INPUT_4_SOURCE: + case ARIZONA_OUT6LMIX_INPUT_4_VOLUME: + case ARIZONA_OUT6RMIX_INPUT_1_SOURCE: + case ARIZONA_OUT6RMIX_INPUT_1_VOLUME: + case ARIZONA_OUT6RMIX_INPUT_2_SOURCE: + case ARIZONA_OUT6RMIX_INPUT_2_VOLUME: + case ARIZONA_OUT6RMIX_INPUT_3_SOURCE: + case ARIZONA_OUT6RMIX_INPUT_3_VOLUME: + case ARIZONA_OUT6RMIX_INPUT_4_SOURCE: + case ARIZONA_OUT6RMIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX1MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX1MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX1MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX1MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX1MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX1MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX1MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX2MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX2MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX2MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX2MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX2MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX2MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX2MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX3MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX3MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX3MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX3MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX3MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX3MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX3MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX4MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX4MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX4MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX4MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX4MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX4MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX4MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX5MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX5MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX5MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX5MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX5MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX5MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX5MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX6MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX6MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX6MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX6MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX6MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX6MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX6MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX7MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX7MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX7MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX7MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX7MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX7MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX7MIX_INPUT_4_VOLUME: + case ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE: + case ARIZONA_AIF1TX8MIX_INPUT_1_VOLUME: + case ARIZONA_AIF1TX8MIX_INPUT_2_SOURCE: + case ARIZONA_AIF1TX8MIX_INPUT_2_VOLUME: + case ARIZONA_AIF1TX8MIX_INPUT_3_SOURCE: + case ARIZONA_AIF1TX8MIX_INPUT_3_VOLUME: + case ARIZONA_AIF1TX8MIX_INPUT_4_SOURCE: + case ARIZONA_AIF1TX8MIX_INPUT_4_VOLUME: + case ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE: + case ARIZONA_AIF2TX1MIX_INPUT_1_VOLUME: + case ARIZONA_AIF2TX1MIX_INPUT_2_SOURCE: + case ARIZONA_AIF2TX1MIX_INPUT_2_VOLUME: + case ARIZONA_AIF2TX1MIX_INPUT_3_SOURCE: + case ARIZONA_AIF2TX1MIX_INPUT_3_VOLUME: + case ARIZONA_AIF2TX1MIX_INPUT_4_SOURCE: + case ARIZONA_AIF2TX1MIX_INPUT_4_VOLUME: + case ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE: + case ARIZONA_AIF2TX2MIX_INPUT_1_VOLUME: + case ARIZONA_AIF2TX2MIX_INPUT_2_SOURCE: + case ARIZONA_AIF2TX2MIX_INPUT_2_VOLUME: + case ARIZONA_AIF2TX2MIX_INPUT_3_SOURCE: + case ARIZONA_AIF2TX2MIX_INPUT_3_VOLUME: + case ARIZONA_AIF2TX2MIX_INPUT_4_SOURCE: + case ARIZONA_AIF2TX2MIX_INPUT_4_VOLUME: + case ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE: + case ARIZONA_AIF3TX1MIX_INPUT_1_VOLUME: + case ARIZONA_AIF3TX1MIX_INPUT_2_SOURCE: + case ARIZONA_AIF3TX1MIX_INPUT_2_VOLUME: + case ARIZONA_AIF3TX1MIX_INPUT_3_SOURCE: + case ARIZONA_AIF3TX1MIX_INPUT_3_VOLUME: + case ARIZONA_AIF3TX1MIX_INPUT_4_SOURCE: + case ARIZONA_AIF3TX1MIX_INPUT_4_VOLUME: + case ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE: + case ARIZONA_AIF3TX2MIX_INPUT_1_VOLUME: + case ARIZONA_AIF3TX2MIX_INPUT_2_SOURCE: + case ARIZONA_AIF3TX2MIX_INPUT_2_VOLUME: + case ARIZONA_AIF3TX2MIX_INPUT_3_SOURCE: + case ARIZONA_AIF3TX2MIX_INPUT_3_VOLUME: + case ARIZONA_AIF3TX2MIX_INPUT_4_SOURCE: + case ARIZONA_AIF3TX2MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX1MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX1MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX1MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX1MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX1MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX1MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX1MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX2MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX2MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX2MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX2MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX2MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX2MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX2MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX3MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX3MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX3MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX3MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX3MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX3MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX3MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX4MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX4MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX4MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX4MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX4MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX4MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX4MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX5MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX5MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX5MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX5MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX5MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX5MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX5MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX6MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX6MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX6MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX6MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX6MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX6MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX6MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX7MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX7MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX7MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX7MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX7MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX7MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX7MIX_INPUT_4_VOLUME: + case ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE: + case ARIZONA_SLIMTX8MIX_INPUT_1_VOLUME: + case ARIZONA_SLIMTX8MIX_INPUT_2_SOURCE: + case ARIZONA_SLIMTX8MIX_INPUT_2_VOLUME: + case ARIZONA_SLIMTX8MIX_INPUT_3_SOURCE: + case ARIZONA_SLIMTX8MIX_INPUT_3_VOLUME: + case ARIZONA_SLIMTX8MIX_INPUT_4_SOURCE: + case ARIZONA_SLIMTX8MIX_INPUT_4_VOLUME: + case ARIZONA_EQ1MIX_INPUT_1_SOURCE: + case ARIZONA_EQ1MIX_INPUT_1_VOLUME: + case ARIZONA_EQ1MIX_INPUT_2_SOURCE: + case ARIZONA_EQ1MIX_INPUT_2_VOLUME: + case ARIZONA_EQ1MIX_INPUT_3_SOURCE: + case ARIZONA_EQ1MIX_INPUT_3_VOLUME: + case ARIZONA_EQ1MIX_INPUT_4_SOURCE: + case ARIZONA_EQ1MIX_INPUT_4_VOLUME: + case ARIZONA_EQ2MIX_INPUT_1_SOURCE: + case ARIZONA_EQ2MIX_INPUT_1_VOLUME: + case ARIZONA_EQ2MIX_INPUT_2_SOURCE: + case ARIZONA_EQ2MIX_INPUT_2_VOLUME: + case ARIZONA_EQ2MIX_INPUT_3_SOURCE: + case ARIZONA_EQ2MIX_INPUT_3_VOLUME: + case ARIZONA_EQ2MIX_INPUT_4_SOURCE: + case ARIZONA_EQ2MIX_INPUT_4_VOLUME: + case ARIZONA_EQ3MIX_INPUT_1_SOURCE: + case ARIZONA_EQ3MIX_INPUT_1_VOLUME: + case ARIZONA_EQ3MIX_INPUT_2_SOURCE: + case ARIZONA_EQ3MIX_INPUT_2_VOLUME: + case ARIZONA_EQ3MIX_INPUT_3_SOURCE: + case ARIZONA_EQ3MIX_INPUT_3_VOLUME: + case ARIZONA_EQ3MIX_INPUT_4_SOURCE: + case ARIZONA_EQ3MIX_INPUT_4_VOLUME: + case ARIZONA_EQ4MIX_INPUT_1_SOURCE: + case ARIZONA_EQ4MIX_INPUT_1_VOLUME: + case ARIZONA_EQ4MIX_INPUT_2_SOURCE: + case ARIZONA_EQ4MIX_INPUT_2_VOLUME: + case ARIZONA_EQ4MIX_INPUT_3_SOURCE: + case ARIZONA_EQ4MIX_INPUT_3_VOLUME: + case ARIZONA_EQ4MIX_INPUT_4_SOURCE: + case ARIZONA_EQ4MIX_INPUT_4_VOLUME: + case ARIZONA_DRC1LMIX_INPUT_1_SOURCE: + case ARIZONA_DRC1LMIX_INPUT_1_VOLUME: + case ARIZONA_DRC1LMIX_INPUT_2_SOURCE: + case ARIZONA_DRC1LMIX_INPUT_2_VOLUME: + case ARIZONA_DRC1LMIX_INPUT_3_SOURCE: + case ARIZONA_DRC1LMIX_INPUT_3_VOLUME: + case ARIZONA_DRC1LMIX_INPUT_4_SOURCE: + case ARIZONA_DRC1LMIX_INPUT_4_VOLUME: + case ARIZONA_DRC1RMIX_INPUT_1_SOURCE: + case ARIZONA_DRC1RMIX_INPUT_1_VOLUME: + case ARIZONA_DRC1RMIX_INPUT_2_SOURCE: + case ARIZONA_DRC1RMIX_INPUT_2_VOLUME: + case ARIZONA_DRC1RMIX_INPUT_3_SOURCE: + case ARIZONA_DRC1RMIX_INPUT_3_VOLUME: + case ARIZONA_DRC1RMIX_INPUT_4_SOURCE: + case ARIZONA_DRC1RMIX_INPUT_4_VOLUME: + case ARIZONA_DRC2LMIX_INPUT_1_SOURCE: + case ARIZONA_DRC2LMIX_INPUT_1_VOLUME: + case ARIZONA_DRC2LMIX_INPUT_2_SOURCE: + case ARIZONA_DRC2LMIX_INPUT_2_VOLUME: + case ARIZONA_DRC2LMIX_INPUT_3_SOURCE: + case ARIZONA_DRC2LMIX_INPUT_3_VOLUME: + case ARIZONA_DRC2LMIX_INPUT_4_SOURCE: + case ARIZONA_DRC2LMIX_INPUT_4_VOLUME: + case ARIZONA_DRC2RMIX_INPUT_1_SOURCE: + case ARIZONA_DRC2RMIX_INPUT_1_VOLUME: + case ARIZONA_DRC2RMIX_INPUT_2_SOURCE: + case ARIZONA_DRC2RMIX_INPUT_2_VOLUME: + case ARIZONA_DRC2RMIX_INPUT_3_SOURCE: + case ARIZONA_DRC2RMIX_INPUT_3_VOLUME: + case ARIZONA_DRC2RMIX_INPUT_4_SOURCE: + case ARIZONA_DRC2RMIX_INPUT_4_VOLUME: + case ARIZONA_HPLP1MIX_INPUT_1_SOURCE: + case ARIZONA_HPLP1MIX_INPUT_1_VOLUME: + case ARIZONA_HPLP1MIX_INPUT_2_SOURCE: + case ARIZONA_HPLP1MIX_INPUT_2_VOLUME: + case ARIZONA_HPLP1MIX_INPUT_3_SOURCE: + case ARIZONA_HPLP1MIX_INPUT_3_VOLUME: + case ARIZONA_HPLP1MIX_INPUT_4_SOURCE: + case ARIZONA_HPLP1MIX_INPUT_4_VOLUME: + case ARIZONA_HPLP2MIX_INPUT_1_SOURCE: + case ARIZONA_HPLP2MIX_INPUT_1_VOLUME: + case ARIZONA_HPLP2MIX_INPUT_2_SOURCE: + case ARIZONA_HPLP2MIX_INPUT_2_VOLUME: + case ARIZONA_HPLP2MIX_INPUT_3_SOURCE: + case ARIZONA_HPLP2MIX_INPUT_3_VOLUME: + case ARIZONA_HPLP2MIX_INPUT_4_SOURCE: + case ARIZONA_HPLP2MIX_INPUT_4_VOLUME: + case ARIZONA_HPLP3MIX_INPUT_1_SOURCE: + case ARIZONA_HPLP3MIX_INPUT_1_VOLUME: + case ARIZONA_HPLP3MIX_INPUT_2_SOURCE: + case ARIZONA_HPLP3MIX_INPUT_2_VOLUME: + case ARIZONA_HPLP3MIX_INPUT_3_SOURCE: + case ARIZONA_HPLP3MIX_INPUT_3_VOLUME: + case ARIZONA_HPLP3MIX_INPUT_4_SOURCE: + case ARIZONA_HPLP3MIX_INPUT_4_VOLUME: + case ARIZONA_HPLP4MIX_INPUT_1_SOURCE: + case ARIZONA_HPLP4MIX_INPUT_1_VOLUME: + case ARIZONA_HPLP4MIX_INPUT_2_SOURCE: + case ARIZONA_HPLP4MIX_INPUT_2_VOLUME: + case ARIZONA_HPLP4MIX_INPUT_3_SOURCE: + case ARIZONA_HPLP4MIX_INPUT_3_VOLUME: + case ARIZONA_HPLP4MIX_INPUT_4_SOURCE: + case ARIZONA_HPLP4MIX_INPUT_4_VOLUME: + case ARIZONA_DSP1LMIX_INPUT_1_SOURCE: + case ARIZONA_DSP1LMIX_INPUT_1_VOLUME: + case ARIZONA_DSP1LMIX_INPUT_2_SOURCE: + case ARIZONA_DSP1LMIX_INPUT_2_VOLUME: + case ARIZONA_DSP1LMIX_INPUT_3_SOURCE: + case ARIZONA_DSP1LMIX_INPUT_3_VOLUME: + case ARIZONA_DSP1LMIX_INPUT_4_SOURCE: + case ARIZONA_DSP1LMIX_INPUT_4_VOLUME: + case ARIZONA_DSP1RMIX_INPUT_1_SOURCE: + case ARIZONA_DSP1RMIX_INPUT_1_VOLUME: + case ARIZONA_DSP1RMIX_INPUT_2_SOURCE: + case ARIZONA_DSP1RMIX_INPUT_2_VOLUME: + case ARIZONA_DSP1RMIX_INPUT_3_SOURCE: + case ARIZONA_DSP1RMIX_INPUT_3_VOLUME: + case ARIZONA_DSP1RMIX_INPUT_4_SOURCE: + case ARIZONA_DSP1RMIX_INPUT_4_VOLUME: + case ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE: + case ARIZONA_DSP1AUX2MIX_INPUT_1_SOURCE: + case ARIZONA_DSP1AUX3MIX_INPUT_1_SOURCE: + case ARIZONA_DSP1AUX4MIX_INPUT_1_SOURCE: + case ARIZONA_DSP1AUX5MIX_INPUT_1_SOURCE: + case ARIZONA_DSP1AUX6MIX_INPUT_1_SOURCE: + case ARIZONA_DSP2LMIX_INPUT_1_SOURCE: + case ARIZONA_DSP2LMIX_INPUT_1_VOLUME: + case ARIZONA_DSP2LMIX_INPUT_2_SOURCE: + case ARIZONA_DSP2LMIX_INPUT_2_VOLUME: + case ARIZONA_DSP2LMIX_INPUT_3_SOURCE: + case ARIZONA_DSP2LMIX_INPUT_3_VOLUME: + case ARIZONA_DSP2LMIX_INPUT_4_SOURCE: + case ARIZONA_DSP2LMIX_INPUT_4_VOLUME: + case ARIZONA_DSP2RMIX_INPUT_1_SOURCE: + case ARIZONA_DSP2RMIX_INPUT_1_VOLUME: + case ARIZONA_DSP2RMIX_INPUT_2_SOURCE: + case ARIZONA_DSP2RMIX_INPUT_2_VOLUME: + case ARIZONA_DSP2RMIX_INPUT_3_SOURCE: + case ARIZONA_DSP2RMIX_INPUT_3_VOLUME: + case ARIZONA_DSP2RMIX_INPUT_4_SOURCE: + case ARIZONA_DSP2RMIX_INPUT_4_VOLUME: + case ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE: + case ARIZONA_DSP2AUX2MIX_INPUT_1_SOURCE: + case ARIZONA_DSP2AUX3MIX_INPUT_1_SOURCE: + case ARIZONA_DSP2AUX4MIX_INPUT_1_SOURCE: + case ARIZONA_DSP2AUX5MIX_INPUT_1_SOURCE: + case ARIZONA_DSP2AUX6MIX_INPUT_1_SOURCE: + case ARIZONA_DSP3LMIX_INPUT_1_SOURCE: + case ARIZONA_DSP3LMIX_INPUT_1_VOLUME: + case ARIZONA_DSP3LMIX_INPUT_2_SOURCE: + case ARIZONA_DSP3LMIX_INPUT_2_VOLUME: + case ARIZONA_DSP3LMIX_INPUT_3_SOURCE: + case ARIZONA_DSP3LMIX_INPUT_3_VOLUME: + case ARIZONA_DSP3LMIX_INPUT_4_SOURCE: + case ARIZONA_DSP3LMIX_INPUT_4_VOLUME: + case ARIZONA_DSP3RMIX_INPUT_1_SOURCE: + case ARIZONA_DSP3RMIX_INPUT_1_VOLUME: + case ARIZONA_DSP3RMIX_INPUT_2_SOURCE: + case ARIZONA_DSP3RMIX_INPUT_2_VOLUME: + case ARIZONA_DSP3RMIX_INPUT_3_SOURCE: + case ARIZONA_DSP3RMIX_INPUT_3_VOLUME: + case ARIZONA_DSP3RMIX_INPUT_4_SOURCE: + case ARIZONA_DSP3RMIX_INPUT_4_VOLUME: + case ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE: + case ARIZONA_DSP3AUX2MIX_INPUT_1_SOURCE: + case ARIZONA_DSP3AUX3MIX_INPUT_1_SOURCE: + case ARIZONA_DSP3AUX4MIX_INPUT_1_SOURCE: + case ARIZONA_DSP3AUX5MIX_INPUT_1_SOURCE: + case ARIZONA_DSP3AUX6MIX_INPUT_1_SOURCE: + case ARIZONA_DSP4LMIX_INPUT_1_SOURCE: + case ARIZONA_DSP4LMIX_INPUT_1_VOLUME: + case ARIZONA_DSP4LMIX_INPUT_2_SOURCE: + case ARIZONA_DSP4LMIX_INPUT_2_VOLUME: + case ARIZONA_DSP4LMIX_INPUT_3_SOURCE: + case ARIZONA_DSP4LMIX_INPUT_3_VOLUME: + case ARIZONA_DSP4LMIX_INPUT_4_SOURCE: + case ARIZONA_DSP4LMIX_INPUT_4_VOLUME: + case ARIZONA_DSP4RMIX_INPUT_1_SOURCE: + case ARIZONA_DSP4RMIX_INPUT_1_VOLUME: + case ARIZONA_DSP4RMIX_INPUT_2_SOURCE: + case ARIZONA_DSP4RMIX_INPUT_2_VOLUME: + case ARIZONA_DSP4RMIX_INPUT_3_SOURCE: + case ARIZONA_DSP4RMIX_INPUT_3_VOLUME: + case ARIZONA_DSP4RMIX_INPUT_4_SOURCE: + case ARIZONA_DSP4RMIX_INPUT_4_VOLUME: + case ARIZONA_DSP4AUX1MIX_INPUT_1_SOURCE: + case ARIZONA_DSP4AUX2MIX_INPUT_1_SOURCE: + case ARIZONA_DSP4AUX3MIX_INPUT_1_SOURCE: + case ARIZONA_DSP4AUX4MIX_INPUT_1_SOURCE: + case ARIZONA_DSP4AUX5MIX_INPUT_1_SOURCE: + case ARIZONA_DSP4AUX6MIX_INPUT_1_SOURCE: + case ARIZONA_ASRC1LMIX_INPUT_1_SOURCE: + case ARIZONA_ASRC1RMIX_INPUT_1_SOURCE: + case ARIZONA_ASRC2LMIX_INPUT_1_SOURCE: + case ARIZONA_ASRC2RMIX_INPUT_1_SOURCE: + case ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE: + case ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE: + case ARIZONA_GPIO1_CTRL: + case ARIZONA_GPIO2_CTRL: + case ARIZONA_GPIO3_CTRL: + case ARIZONA_GPIO4_CTRL: + case ARIZONA_GPIO5_CTRL: + case ARIZONA_IRQ_CTRL_1: + case ARIZONA_GPIO_DEBOUNCE_CONFIG: + case ARIZONA_MISC_PAD_CTRL_1: + case ARIZONA_MISC_PAD_CTRL_2: + case ARIZONA_MISC_PAD_CTRL_3: + case ARIZONA_MISC_PAD_CTRL_4: + case ARIZONA_MISC_PAD_CTRL_5: + case ARIZONA_MISC_PAD_CTRL_6: + case ARIZONA_MISC_PAD_CTRL_7: + case ARIZONA_MISC_PAD_CTRL_8: + case ARIZONA_MISC_PAD_CTRL_9: + case ARIZONA_MISC_PAD_CTRL_10: + case ARIZONA_MISC_PAD_CTRL_11: + case ARIZONA_MISC_PAD_CTRL_12: + case ARIZONA_MISC_PAD_CTRL_13: + case ARIZONA_MISC_PAD_CTRL_14: + case ARIZONA_MISC_PAD_CTRL_15: + case ARIZONA_MISC_PAD_CTRL_16: + case ARIZONA_MISC_PAD_CTRL_17: + case ARIZONA_MISC_PAD_CTRL_18: + case ARIZONA_INTERRUPT_STATUS_1: + case ARIZONA_INTERRUPT_STATUS_2: + case ARIZONA_INTERRUPT_STATUS_3: + case ARIZONA_INTERRUPT_STATUS_4: + case ARIZONA_INTERRUPT_STATUS_5: + case ARIZONA_INTERRUPT_STATUS_1_MASK: + case ARIZONA_INTERRUPT_STATUS_2_MASK: + case ARIZONA_INTERRUPT_STATUS_3_MASK: + case ARIZONA_INTERRUPT_STATUS_4_MASK: + case ARIZONA_INTERRUPT_STATUS_5_MASK: + case ARIZONA_INTERRUPT_CONTROL: + case ARIZONA_IRQ2_STATUS_1: + case ARIZONA_IRQ2_STATUS_2: + case ARIZONA_IRQ2_STATUS_3: + case ARIZONA_IRQ2_STATUS_4: + case ARIZONA_IRQ2_STATUS_5: + case ARIZONA_IRQ2_STATUS_1_MASK: + case ARIZONA_IRQ2_STATUS_2_MASK: + case ARIZONA_IRQ2_STATUS_3_MASK: + case ARIZONA_IRQ2_STATUS_4_MASK: + case ARIZONA_IRQ2_STATUS_5_MASK: + case ARIZONA_IRQ2_CONTROL: + case ARIZONA_INTERRUPT_RAW_STATUS_2: + case ARIZONA_INTERRUPT_RAW_STATUS_3: + case ARIZONA_INTERRUPT_RAW_STATUS_4: + case ARIZONA_INTERRUPT_RAW_STATUS_5: + case ARIZONA_INTERRUPT_RAW_STATUS_6: + case ARIZONA_INTERRUPT_RAW_STATUS_7: + case ARIZONA_INTERRUPT_RAW_STATUS_8: + case ARIZONA_IRQ_PIN_STATUS: + case ARIZONA_AOD_WKUP_AND_TRIG: + case ARIZONA_AOD_IRQ1: + case ARIZONA_AOD_IRQ2: + case ARIZONA_AOD_IRQ_MASK_IRQ1: + case ARIZONA_AOD_IRQ_MASK_IRQ2: + case ARIZONA_AOD_IRQ_RAW_STATUS: + case ARIZONA_JACK_DETECT_DEBOUNCE: + case ARIZONA_FX_CTRL1: + case ARIZONA_FX_CTRL2: + case ARIZONA_EQ1_1: + case ARIZONA_EQ1_2: + case ARIZONA_EQ1_3: + case ARIZONA_EQ1_4: + case ARIZONA_EQ1_5: + case ARIZONA_EQ1_6: + case ARIZONA_EQ1_7: + case ARIZONA_EQ1_8: + case ARIZONA_EQ1_9: + case ARIZONA_EQ1_10: + case ARIZONA_EQ1_11: + case ARIZONA_EQ1_12: + case ARIZONA_EQ1_13: + case ARIZONA_EQ1_14: + case ARIZONA_EQ1_15: + case ARIZONA_EQ1_16: + case ARIZONA_EQ1_17: + case ARIZONA_EQ1_18: + case ARIZONA_EQ1_19: + case ARIZONA_EQ1_20: + case ARIZONA_EQ1_21: + case ARIZONA_EQ2_1: + case ARIZONA_EQ2_2: + case ARIZONA_EQ2_3: + case ARIZONA_EQ2_4: + case ARIZONA_EQ2_5: + case ARIZONA_EQ2_6: + case ARIZONA_EQ2_7: + case ARIZONA_EQ2_8: + case ARIZONA_EQ2_9: + case ARIZONA_EQ2_10: + case ARIZONA_EQ2_11: + case ARIZONA_EQ2_12: + case ARIZONA_EQ2_13: + case ARIZONA_EQ2_14: + case ARIZONA_EQ2_15: + case ARIZONA_EQ2_16: + case ARIZONA_EQ2_17: + case ARIZONA_EQ2_18: + case ARIZONA_EQ2_19: + case ARIZONA_EQ2_20: + case ARIZONA_EQ2_21: + case ARIZONA_EQ3_1: + case ARIZONA_EQ3_2: + case ARIZONA_EQ3_3: + case ARIZONA_EQ3_4: + case ARIZONA_EQ3_5: + case ARIZONA_EQ3_6: + case ARIZONA_EQ3_7: + case ARIZONA_EQ3_8: + case ARIZONA_EQ3_9: + case ARIZONA_EQ3_10: + case ARIZONA_EQ3_11: + case ARIZONA_EQ3_12: + case ARIZONA_EQ3_13: + case ARIZONA_EQ3_14: + case ARIZONA_EQ3_15: + case ARIZONA_EQ3_16: + case ARIZONA_EQ3_17: + case ARIZONA_EQ3_18: + case ARIZONA_EQ3_19: + case ARIZONA_EQ3_20: + case ARIZONA_EQ3_21: + case ARIZONA_EQ4_1: + case ARIZONA_EQ4_2: + case ARIZONA_EQ4_3: + case ARIZONA_EQ4_4: + case ARIZONA_EQ4_5: + case ARIZONA_EQ4_6: + case ARIZONA_EQ4_7: + case ARIZONA_EQ4_8: + case ARIZONA_EQ4_9: + case ARIZONA_EQ4_10: + case ARIZONA_EQ4_11: + case ARIZONA_EQ4_12: + case ARIZONA_EQ4_13: + case ARIZONA_EQ4_14: + case ARIZONA_EQ4_15: + case ARIZONA_EQ4_16: + case ARIZONA_EQ4_17: + case ARIZONA_EQ4_18: + case ARIZONA_EQ4_19: + case ARIZONA_EQ4_20: + case ARIZONA_EQ4_21: + case ARIZONA_DRC1_CTRL1: + case ARIZONA_DRC1_CTRL2: + case ARIZONA_DRC1_CTRL3: + case ARIZONA_DRC1_CTRL4: + case ARIZONA_DRC1_CTRL5: + case ARIZONA_DRC2_CTRL1: + case ARIZONA_DRC2_CTRL2: + case ARIZONA_DRC2_CTRL3: + case ARIZONA_DRC2_CTRL4: + case ARIZONA_DRC2_CTRL5: + case ARIZONA_HPLPF1_1: + case ARIZONA_HPLPF1_2: + case ARIZONA_HPLPF2_1: + case ARIZONA_HPLPF2_2: + case ARIZONA_HPLPF3_1: + case ARIZONA_HPLPF3_2: + case ARIZONA_HPLPF4_1: + case ARIZONA_HPLPF4_2: + case ARIZONA_ASRC_ENABLE: + case ARIZONA_ASRC_STATUS: + case ARIZONA_ASRC_RATE1: + case ARIZONA_ISRC_1_CTRL_1: + case ARIZONA_ISRC_1_CTRL_2: + case ARIZONA_ISRC_1_CTRL_3: + case ARIZONA_ISRC_2_CTRL_1: + case ARIZONA_ISRC_2_CTRL_2: + case ARIZONA_ISRC_2_CTRL_3: + case ARIZONA_ISRC_3_CTRL_1: + case ARIZONA_ISRC_3_CTRL_2: + case ARIZONA_ISRC_3_CTRL_3: + case ARIZONA_CLOCK_CONTROL: + case ARIZONA_ANC_SRC: + case ARIZONA_DSP_STATUS: + case ARIZONA_DSP1_CONTROL_1: + case ARIZONA_DSP1_CLOCKING_1: + case ARIZONA_DSP1_STATUS_1: + case ARIZONA_DSP1_STATUS_2: + case ARIZONA_DSP2_CONTROL_1: + case ARIZONA_DSP2_CLOCKING_1: + case ARIZONA_DSP2_STATUS_1: + case ARIZONA_DSP2_STATUS_2: + case ARIZONA_DSP3_CONTROL_1: + case ARIZONA_DSP3_CLOCKING_1: + case ARIZONA_DSP3_STATUS_1: + case ARIZONA_DSP3_STATUS_2: + case ARIZONA_DSP4_CONTROL_1: + case ARIZONA_DSP4_CLOCKING_1: + case ARIZONA_DSP4_STATUS_1: + case ARIZONA_DSP4_STATUS_2: + return true; + default: + return false; + } +} + +static bool wm5110_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ARIZONA_SOFTWARE_RESET: + case ARIZONA_DEVICE_REVISION: + case ARIZONA_HAPTICS_STATUS: + case ARIZONA_SAMPLE_RATE_1_STATUS: + case ARIZONA_SAMPLE_RATE_2_STATUS: + case ARIZONA_SAMPLE_RATE_3_STATUS: + case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS: + case ARIZONA_MIC_DETECT_3: + case ARIZONA_HEADPHONE_DETECT_2: + case ARIZONA_INPUT_ENABLES_STATUS: + case ARIZONA_OUTPUT_STATUS_1: + case ARIZONA_RAW_OUTPUT_STATUS_1: + case ARIZONA_SLIMBUS_RX_PORT_STATUS: + case ARIZONA_SLIMBUS_TX_PORT_STATUS: + case ARIZONA_INTERRUPT_STATUS_1: + case ARIZONA_INTERRUPT_STATUS_2: + case ARIZONA_INTERRUPT_STATUS_3: + case ARIZONA_INTERRUPT_STATUS_4: + case ARIZONA_INTERRUPT_STATUS_5: + case ARIZONA_IRQ2_STATUS_1: + case ARIZONA_IRQ2_STATUS_2: + case ARIZONA_IRQ2_STATUS_3: + case ARIZONA_IRQ2_STATUS_4: + case ARIZONA_IRQ2_STATUS_5: + case ARIZONA_INTERRUPT_RAW_STATUS_2: + case ARIZONA_INTERRUPT_RAW_STATUS_3: + case ARIZONA_INTERRUPT_RAW_STATUS_4: + case ARIZONA_INTERRUPT_RAW_STATUS_5: + case ARIZONA_INTERRUPT_RAW_STATUS_6: + case ARIZONA_INTERRUPT_RAW_STATUS_7: + case ARIZONA_INTERRUPT_RAW_STATUS_8: + case ARIZONA_IRQ_PIN_STATUS: + case ARIZONA_AOD_IRQ1: + case ARIZONA_AOD_IRQ2: + case ARIZONA_ASRC_STATUS: + case ARIZONA_DSP_STATUS: + case ARIZONA_DSP1_CONTROL_1: + case ARIZONA_DSP1_CLOCKING_1: + case ARIZONA_DSP1_STATUS_1: + case ARIZONA_DSP1_STATUS_2: + case ARIZONA_DSP2_STATUS_1: + case ARIZONA_DSP2_STATUS_2: + case ARIZONA_DSP3_STATUS_1: + case ARIZONA_DSP3_STATUS_2: + case ARIZONA_DSP4_STATUS_1: + case ARIZONA_DSP4_STATUS_2: + return true; + default: + return false; + } +} + +const struct regmap_config wm5110_spi_regmap = { + .reg_bits = 32, + .pad_bits = 16, + .val_bits = 16, + + .max_register = ARIZONA_DSP1_STATUS_2, + .readable_reg = wm5110_readable_register, + .volatile_reg = wm5110_volatile_register, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wm5110_reg_default, + .num_reg_defaults = ARRAY_SIZE(wm5110_reg_default), +}; +EXPORT_SYMBOL_GPL(wm5110_spi_regmap); + +const struct regmap_config wm5110_i2c_regmap = { + .reg_bits = 32, + .val_bits = 16, + + .max_register = ARIZONA_DSP1_STATUS_2, + .readable_reg = wm5110_readable_register, + .volatile_reg = wm5110_volatile_register, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wm5110_reg_default, + .num_reg_defaults = ARRAY_SIZE(wm5110_reg_default), +}; +EXPORT_SYMBOL_GPL(wm5110_i2c_regmap); diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index 8a9b11ca076a..7c1ae24605d9 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -32,9 +32,6 @@ #include <linux/mfd/wm8350/supply.h> #include <linux/mfd/wm8350/wdt.h> -#define WM8350_UNLOCK_KEY 0x0013 -#define WM8350_LOCK_KEY 0x0000 - #define WM8350_CLOCK_CONTROL_1 0x28 #define WM8350_AIF_TEST 0x74 @@ -63,181 +60,32 @@ /* * WM8350 Device IO */ -static DEFINE_MUTEX(io_mutex); static DEFINE_MUTEX(reg_lock_mutex); -/* Perform a physical read from the device. - */ -static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs, - u16 *dest) -{ - int i, ret; - int bytes = num_regs * 2; - - dev_dbg(wm8350->dev, "volatile read\n"); - ret = regmap_raw_read(wm8350->regmap, reg, dest, bytes); - - for (i = reg; i < reg + num_regs; i++) { - /* Cache is CPU endian */ - dest[i - reg] = be16_to_cpu(dest[i - reg]); - - /* Mask out non-readable bits */ - dest[i - reg] &= wm8350_reg_io_map[i].readable; - } - - dump(num_regs, dest); - - return ret; -} - -static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest) -{ - int i; - int end = reg + num_regs; - int ret = 0; - int bytes = num_regs * 2; - - if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { - dev_err(wm8350->dev, "invalid reg %x\n", - reg + num_regs - 1); - return -EINVAL; - } - - dev_dbg(wm8350->dev, - "%s R%d(0x%2.2x) %d regs\n", __func__, reg, reg, num_regs); - -#if WM8350_BUS_DEBUG - /* we can _safely_ read any register, but warn if read not supported */ - for (i = reg; i < end; i++) { - if (!wm8350_reg_io_map[i].readable) - dev_warn(wm8350->dev, - "reg R%d is not readable\n", i); - } -#endif - - /* if any volatile registers are required, then read back all */ - for (i = reg; i < end; i++) - if (wm8350_reg_io_map[i].vol) - return wm8350_phys_read(wm8350, reg, num_regs, dest); - - /* no volatiles, then cache is good */ - dev_dbg(wm8350->dev, "cache read\n"); - memcpy(dest, &wm8350->reg_cache[reg], bytes); - dump(num_regs, dest); - return ret; -} - -static inline int is_reg_locked(struct wm8350 *wm8350, u8 reg) -{ - if (reg == WM8350_SECURITY || - wm8350->reg_cache[WM8350_SECURITY] == WM8350_UNLOCK_KEY) - return 0; - - if ((reg >= WM8350_GPIO_FUNCTION_SELECT_1 && - reg <= WM8350_GPIO_FUNCTION_SELECT_4) || - (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 && - reg <= WM8350_BATTERY_CHARGER_CONTROL_3)) - return 1; - return 0; -} - -static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src) -{ - int i; - int end = reg + num_regs; - int bytes = num_regs * 2; - - if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { - dev_err(wm8350->dev, "invalid reg %x\n", - reg + num_regs - 1); - return -EINVAL; - } - - /* it's generally not a good idea to write to RO or locked registers */ - for (i = reg; i < end; i++) { - if (!wm8350_reg_io_map[i].writable) { - dev_err(wm8350->dev, - "attempted write to read only reg R%d\n", i); - return -EINVAL; - } - - if (is_reg_locked(wm8350, i)) { - dev_err(wm8350->dev, - "attempted write to locked reg R%d\n", i); - return -EINVAL; - } - - src[i - reg] &= wm8350_reg_io_map[i].writable; - - wm8350->reg_cache[i] = - (wm8350->reg_cache[i] & ~wm8350_reg_io_map[i].writable) - | src[i - reg]; - - src[i - reg] = cpu_to_be16(src[i - reg]); - } - - /* Actually write it out */ - return regmap_raw_write(wm8350->regmap, reg, src, bytes); -} - /* * Safe read, modify, write methods */ int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask) { - u16 data; - int err; - - mutex_lock(&io_mutex); - err = wm8350_read(wm8350, reg, 1, &data); - if (err) { - dev_err(wm8350->dev, "read from reg R%d failed\n", reg); - goto out; - } - - data &= ~mask; - err = wm8350_write(wm8350, reg, 1, &data); - if (err) - dev_err(wm8350->dev, "write to reg R%d failed\n", reg); -out: - mutex_unlock(&io_mutex); - return err; + return regmap_update_bits(wm8350->regmap, reg, mask, 0); } EXPORT_SYMBOL_GPL(wm8350_clear_bits); int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask) { - u16 data; - int err; - - mutex_lock(&io_mutex); - err = wm8350_read(wm8350, reg, 1, &data); - if (err) { - dev_err(wm8350->dev, "read from reg R%d failed\n", reg); - goto out; - } - - data |= mask; - err = wm8350_write(wm8350, reg, 1, &data); - if (err) - dev_err(wm8350->dev, "write to reg R%d failed\n", reg); -out: - mutex_unlock(&io_mutex); - return err; + return regmap_update_bits(wm8350->regmap, reg, mask, mask); } EXPORT_SYMBOL_GPL(wm8350_set_bits); u16 wm8350_reg_read(struct wm8350 *wm8350, int reg) { - u16 data; + unsigned int data; int err; - mutex_lock(&io_mutex); - err = wm8350_read(wm8350, reg, 1, &data); + err = regmap_read(wm8350->regmap, reg, &data); if (err) dev_err(wm8350->dev, "read from reg R%d failed\n", reg); - mutex_unlock(&io_mutex); return data; } EXPORT_SYMBOL_GPL(wm8350_reg_read); @@ -245,13 +93,11 @@ EXPORT_SYMBOL_GPL(wm8350_reg_read); int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val) { int ret; - u16 data = val; - mutex_lock(&io_mutex); - ret = wm8350_write(wm8350, reg, 1, &data); + ret = regmap_write(wm8350->regmap, reg, val); + if (ret) dev_err(wm8350->dev, "write to reg R%d failed\n", reg); - mutex_unlock(&io_mutex); return ret; } EXPORT_SYMBOL_GPL(wm8350_reg_write); @@ -261,12 +107,11 @@ int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs, { int err = 0; - mutex_lock(&io_mutex); - err = wm8350_read(wm8350, start_reg, regs, dest); + err = regmap_bulk_read(wm8350->regmap, start_reg, dest, regs); if (err) dev_err(wm8350->dev, "block read starting from R%d failed\n", start_reg); - mutex_unlock(&io_mutex); + return err; } EXPORT_SYMBOL_GPL(wm8350_block_read); @@ -276,12 +121,11 @@ int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs, { int ret = 0; - mutex_lock(&io_mutex); - ret = wm8350_write(wm8350, start_reg, regs, src); + ret = regmap_bulk_write(wm8350->regmap, start_reg, src, regs); if (ret) dev_err(wm8350->dev, "block write starting at R%d failed\n", start_reg); - mutex_unlock(&io_mutex); + return ret; } EXPORT_SYMBOL_GPL(wm8350_block_write); @@ -295,15 +139,20 @@ EXPORT_SYMBOL_GPL(wm8350_block_write); */ int wm8350_reg_lock(struct wm8350 *wm8350) { - u16 key = WM8350_LOCK_KEY; int ret; + mutex_lock(®_lock_mutex); + ldbg(__func__); - mutex_lock(&io_mutex); - ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key); + + ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_LOCK_KEY); if (ret) dev_err(wm8350->dev, "lock failed\n"); - mutex_unlock(&io_mutex); + + wm8350->unlocked = false; + + mutex_unlock(®_lock_mutex); + return ret; } EXPORT_SYMBOL_GPL(wm8350_reg_lock); @@ -319,15 +168,20 @@ EXPORT_SYMBOL_GPL(wm8350_reg_lock); */ int wm8350_reg_unlock(struct wm8350 *wm8350) { - u16 key = WM8350_UNLOCK_KEY; int ret; + mutex_lock(®_lock_mutex); + ldbg(__func__); - mutex_lock(&io_mutex); - ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key); + + ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_UNLOCK_KEY); if (ret) dev_err(wm8350->dev, "unlock failed\n"); - mutex_unlock(&io_mutex); + + wm8350->unlocked = true; + + mutex_unlock(®_lock_mutex); + return ret; } EXPORT_SYMBOL_GPL(wm8350_reg_unlock); @@ -395,146 +249,6 @@ static irqreturn_t wm8350_auxadc_irq(int irq, void *irq_data) } /* - * Cache is always host endian. - */ -static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode) -{ - int i, ret = 0; - u16 value; - const u16 *reg_map; - - switch (type) { - case 0: - switch (mode) { -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0 - case 0: - reg_map = wm8350_mode0_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1 - case 1: - reg_map = wm8350_mode1_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2 - case 2: - reg_map = wm8350_mode2_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3 - case 3: - reg_map = wm8350_mode3_defaults; - break; -#endif - default: - dev_err(wm8350->dev, - "WM8350 configuration mode %d not supported\n", - mode); - return -EINVAL; - } - break; - - case 1: - switch (mode) { -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0 - case 0: - reg_map = wm8351_mode0_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1 - case 1: - reg_map = wm8351_mode1_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2 - case 2: - reg_map = wm8351_mode2_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3 - case 3: - reg_map = wm8351_mode3_defaults; - break; -#endif - default: - dev_err(wm8350->dev, - "WM8351 configuration mode %d not supported\n", - mode); - return -EINVAL; - } - break; - - case 2: - switch (mode) { -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0 - case 0: - reg_map = wm8352_mode0_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1 - case 1: - reg_map = wm8352_mode1_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2 - case 2: - reg_map = wm8352_mode2_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3 - case 3: - reg_map = wm8352_mode3_defaults; - break; -#endif - default: - dev_err(wm8350->dev, - "WM8352 configuration mode %d not supported\n", - mode); - return -EINVAL; - } - break; - - default: - dev_err(wm8350->dev, - "WM835x configuration mode %d not supported\n", - mode); - return -EINVAL; - } - - wm8350->reg_cache = - kmalloc(sizeof(u16) * (WM8350_MAX_REGISTER + 1), GFP_KERNEL); - if (wm8350->reg_cache == NULL) - return -ENOMEM; - - /* Read the initial cache state back from the device - this is - * a PMIC so the device many not be in a virgin state and we - * can't rely on the silicon values. - */ - ret = regmap_raw_read(wm8350->regmap, 0, wm8350->reg_cache, - sizeof(u16) * (WM8350_MAX_REGISTER + 1)); - if (ret < 0) { - dev_err(wm8350->dev, - "failed to read initial cache values\n"); - goto out; - } - - /* Mask out uncacheable/unreadable bits and the audio. */ - for (i = 0; i < WM8350_MAX_REGISTER; i++) { - if (wm8350_reg_io_map[i].readable && - (i < WM8350_CLOCK_CONTROL_1 || i > WM8350_AIF_TEST)) { - value = be16_to_cpu(wm8350->reg_cache[i]); - value &= wm8350_reg_io_map[i].readable; - wm8350->reg_cache[i] = value; - } else - wm8350->reg_cache[i] = reg_map[i]; - } - -out: - kfree(wm8350->reg_cache); - return ret; -} - -/* * Register a client device. This is non-fatal since there is no need to * fail the entire device init due to a single platform device failing. */ @@ -681,18 +395,12 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, goto err; } - ret = wm8350_create_cache(wm8350, mask_rev, mode); - if (ret < 0) { - dev_err(wm8350->dev, "Failed to create register cache\n"); - return ret; - } - mutex_init(&wm8350->auxadc_mutex); init_completion(&wm8350->auxadc_done); ret = wm8350_irq_init(wm8350, irq, pdata); if (ret < 0) - goto err_free; + goto err; if (wm8350->irq_base) { ret = request_threaded_irq(wm8350->irq_base + @@ -730,8 +438,6 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, err_irq: wm8350_irq_exit(wm8350); -err_free: - kfree(wm8350->reg_cache); err: return ret; } @@ -758,8 +464,6 @@ void wm8350_device_exit(struct wm8350 *wm8350) free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350); wm8350_irq_exit(wm8350); - - kfree(wm8350->reg_cache); } EXPORT_SYMBOL_GPL(wm8350_device_exit); diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c index a68aceb4e48c..2e57101c8d3d 100644 --- a/drivers/mfd/wm8350-i2c.c +++ b/drivers/mfd/wm8350-i2c.c @@ -23,11 +23,6 @@ #include <linux/regmap.h> #include <linux/slab.h> -static const struct regmap_config wm8350_regmap = { - .reg_bits = 8, - .val_bits = 16, -}; - static int wm8350_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c index 9fd01bf63c51..624ff90501cd 100644 --- a/drivers/mfd/wm8350-irq.c +++ b/drivers/mfd/wm8350-irq.c @@ -432,11 +432,9 @@ static void wm8350_irq_sync_unlock(struct irq_data *data) for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) { /* If there's been a change in the mask write it back * to the hardware. */ - if (wm8350->irq_masks[i] != - wm8350->reg_cache[WM8350_INT_STATUS_1_MASK + i]) - WARN_ON(wm8350_reg_write(wm8350, - WM8350_INT_STATUS_1_MASK + i, - wm8350->irq_masks[i])); + WARN_ON(regmap_update_bits(wm8350->regmap, + WM8350_INT_STATUS_1_MASK + i, + 0xffff, wm8350->irq_masks[i])); } mutex_unlock(&wm8350->irq_lock); diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c index e965139e5cd5..9efc64750fb6 100644 --- a/drivers/mfd/wm8350-regmap.c +++ b/drivers/mfd/wm8350-regmap.c @@ -14,3170 +14,18 @@ #include <linux/mfd/wm8350/core.h> -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8350_mode0_defaults[] = { - 0x17FF, /* R0 - Reset/ID */ - 0x1000, /* R1 - ID */ - 0x0000, /* R2 */ - 0x1002, /* R3 - System Control 1 */ - 0x0004, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 - Power Up Interrupt Status */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 - Power Up Interrupt Status Mask */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3B00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - LOUT1 Volume */ - 0x00E4, /* R105 - ROUT1 Volume */ - 0x00E4, /* R106 - LOUT2 Volume */ - 0x02E4, /* R107 - ROUT2 Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 - AIF Test */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x03FC, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0FFC, /* R134 - GPIO Configuration (i/o) */ - 0x0FFC, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0013, /* R140 - GPIO Function Select 1 */ - 0x0000, /* R141 - GPIO Function Select 2 */ - 0x0000, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x002D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0000, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0000, /* R186 - DCDC3 Control */ - 0x0000, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0000, /* R189 - DCDC4 Control */ - 0x0000, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0000, /* R195 - DCDC6 Control */ - 0x0000, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001B, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001B, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001B, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 */ - 0x4000, /* R220 - RAM BIST 1 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 */ - 0x0000, /* R227 */ - 0x0000, /* R228 */ - 0x0000, /* R229 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 */ - 0x0000, /* R232 */ - 0x0000, /* R233 */ - 0x0000, /* R234 */ - 0x0000, /* R235 */ - 0x0000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0000, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0000, /* R243 */ - 0x0000, /* R244 */ - 0x0000, /* R245 */ - 0x0000, /* R246 */ - 0x0000, /* R247 */ - 0x0000, /* R248 */ - 0x0000, /* R249 */ - 0x0000, /* R250 */ - 0x0000, /* R251 */ - 0x0000, /* R252 */ - 0x0000, /* R253 */ - 0x0000, /* R254 */ - 0x0000, /* R255 */ -}; -#endif - -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8350_mode1_defaults[] = { - 0x17FF, /* R0 - Reset/ID */ - 0x1000, /* R1 - ID */ - 0x0000, /* R2 */ - 0x1002, /* R3 - System Control 1 */ - 0x0014, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 - Power Up Interrupt Status */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 - Power Up Interrupt Status Mask */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3B00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - LOUT1 Volume */ - 0x00E4, /* R105 - ROUT1 Volume */ - 0x00E4, /* R106 - LOUT2 Volume */ - 0x02E4, /* R107 - ROUT2 Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 - AIF Test */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x03FC, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x00FB, /* R134 - GPIO Configuration (i/o) */ - 0x04FE, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0312, /* R140 - GPIO Function Select 1 */ - 0x1003, /* R141 - GPIO Function Select 2 */ - 0x1331, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x002D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x0062, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0026, /* R186 - DCDC3 Control */ - 0x0400, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0062, /* R189 - DCDC4 Control */ - 0x0400, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x0800, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x0006, /* R200 - LDO1 Control */ - 0x0400, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0006, /* R203 - LDO2 Control */ - 0x0400, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001B, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001B, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 */ - 0x4000, /* R220 - RAM BIST 1 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 */ - 0x0000, /* R227 */ - 0x0000, /* R228 */ - 0x0000, /* R229 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 */ - 0x0000, /* R232 */ - 0x0000, /* R233 */ - 0x0000, /* R234 */ - 0x0000, /* R235 */ - 0x0000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0000, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0000, /* R243 */ - 0x0000, /* R244 */ - 0x0000, /* R245 */ - 0x0000, /* R246 */ - 0x0000, /* R247 */ - 0x0000, /* R248 */ - 0x0000, /* R249 */ - 0x0000, /* R250 */ - 0x0000, /* R251 */ - 0x0000, /* R252 */ - 0x0000, /* R253 */ - 0x0000, /* R254 */ - 0x0000, /* R255 */ -}; -#endif - -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8350_mode2_defaults[] = { - 0x17FF, /* R0 - Reset/ID */ - 0x1000, /* R1 - ID */ - 0x0000, /* R2 */ - 0x1002, /* R3 - System Control 1 */ - 0x0014, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 - Power Up Interrupt Status */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 - Power Up Interrupt Status Mask */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3B00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - LOUT1 Volume */ - 0x00E4, /* R105 - ROUT1 Volume */ - 0x00E4, /* R106 - LOUT2 Volume */ - 0x02E4, /* R107 - ROUT2 Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 - AIF Test */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x03FC, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x08FB, /* R134 - GPIO Configuration (i/o) */ - 0x0CFE, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0312, /* R140 - GPIO Function Select 1 */ - 0x0003, /* R141 - GPIO Function Select 2 */ - 0x2331, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x002D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x002E, /* R186 - DCDC3 Control */ - 0x0800, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x000E, /* R189 - DCDC4 Control */ - 0x0800, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x0C00, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001A, /* R200 - LDO1 Control */ - 0x0800, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0010, /* R203 - LDO2 Control */ - 0x0800, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x000A, /* R206 - LDO3 Control */ - 0x0C00, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001A, /* R209 - LDO4 Control */ - 0x0800, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 */ - 0x4000, /* R220 - RAM BIST 1 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 */ - 0x0000, /* R227 */ - 0x0000, /* R228 */ - 0x0000, /* R229 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 */ - 0x0000, /* R232 */ - 0x0000, /* R233 */ - 0x0000, /* R234 */ - 0x0000, /* R235 */ - 0x0000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0000, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0000, /* R243 */ - 0x0000, /* R244 */ - 0x0000, /* R245 */ - 0x0000, /* R246 */ - 0x0000, /* R247 */ - 0x0000, /* R248 */ - 0x0000, /* R249 */ - 0x0000, /* R250 */ - 0x0000, /* R251 */ - 0x0000, /* R252 */ - 0x0000, /* R253 */ - 0x0000, /* R254 */ - 0x0000, /* R255 */ -}; -#endif - -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8350_mode3_defaults[] = { - 0x17FF, /* R0 - Reset/ID */ - 0x1000, /* R1 - ID */ - 0x0000, /* R2 */ - 0x1000, /* R3 - System Control 1 */ - 0x0004, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 - Power Up Interrupt Status */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 - Power Up Interrupt Status Mask */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3B00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - LOUT1 Volume */ - 0x00E4, /* R105 - ROUT1 Volume */ - 0x00E4, /* R106 - LOUT2 Volume */ - 0x02E4, /* R107 - ROUT2 Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 - AIF Test */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x03FC, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0A7B, /* R134 - GPIO Configuration (i/o) */ - 0x06FE, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x1312, /* R140 - GPIO Function Select 1 */ - 0x1030, /* R141 - GPIO Function Select 2 */ - 0x2231, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x002D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x000E, /* R186 - DCDC3 Control */ - 0x0400, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0026, /* R189 - DCDC4 Control */ - 0x0400, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x0400, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001C, /* R203 - LDO2 Control */ - 0x0400, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001C, /* R206 - LDO3 Control */ - 0x0400, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001F, /* R209 - LDO4 Control */ - 0x0400, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 */ - 0x4000, /* R220 - RAM BIST 1 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 */ - 0x0000, /* R227 */ - 0x0000, /* R228 */ - 0x0000, /* R229 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 */ - 0x0000, /* R232 */ - 0x0000, /* R233 */ - 0x0000, /* R234 */ - 0x0000, /* R235 */ - 0x0000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0000, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0000, /* R243 */ - 0x0000, /* R244 */ - 0x0000, /* R245 */ - 0x0000, /* R246 */ - 0x0000, /* R247 */ - 0x0000, /* R248 */ - 0x0000, /* R249 */ - 0x0000, /* R250 */ - 0x0000, /* R251 */ - 0x0000, /* R252 */ - 0x0000, /* R253 */ - 0x0000, /* R254 */ - 0x0000, /* R255 */ -}; -#endif - -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8351_mode0_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0001, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0004, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0FFC, /* R134 - GPIO Configuration (i/o) */ - 0x0FFC, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0013, /* R140 - GPIO Function Select 1 */ - 0x0000, /* R141 - GPIO Function Select 2 */ - 0x0000, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 */ - 0x0000, /* R175 */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0000, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0000, /* R186 - DCDC3 Control */ - 0x0000, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0000, /* R189 - DCDC4 Control */ - 0x0000, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 */ - 0x0000, /* R193 */ - 0x0000, /* R194 */ - 0x0000, /* R195 */ - 0x0000, /* R196 */ - 0x0006, /* R197 */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001B, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001B, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001B, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 - FLL Test 1 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x1000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8351_mode1_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0001, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0CFB, /* R134 - GPIO Configuration (i/o) */ - 0x0C1F, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0300, /* R140 - GPIO Function Select 1 */ - 0x1110, /* R141 - GPIO Function Select 2 */ - 0x0013, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 */ - 0x0000, /* R175 */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0C00, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0026, /* R186 - DCDC3 Control */ - 0x0400, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0062, /* R189 - DCDC4 Control */ - 0x0800, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 */ - 0x0000, /* R193 */ - 0x0000, /* R194 */ - 0x000A, /* R195 */ - 0x1000, /* R196 */ - 0x0006, /* R197 */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x0006, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0010, /* R203 - LDO2 Control */ - 0x0C00, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001F, /* R206 - LDO3 Control */ - 0x0800, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x000A, /* R209 - LDO4 Control */ - 0x0800, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 - FLL Test 1 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x1000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x1000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8351_mode2_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0001, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0214, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0110, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x09FA, /* R134 - GPIO Configuration (i/o) */ - 0x0DF6, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x1310, /* R140 - GPIO Function Select 1 */ - 0x0003, /* R141 - GPIO Function Select 2 */ - 0x2000, /* R142 - GPIO Function Select 3 */ - 0x0000, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 */ - 0x0000, /* R175 */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x001A, /* R180 - DCDC1 Control */ - 0x0800, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0056, /* R186 - DCDC3 Control */ - 0x0400, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0026, /* R189 - DCDC4 Control */ - 0x0C00, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 */ - 0x0000, /* R193 */ - 0x0000, /* R194 */ - 0x0026, /* R195 */ - 0x0C00, /* R196 */ - 0x0006, /* R197 */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0400, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0010, /* R203 - LDO2 Control */ - 0x0C00, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x0015, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001A, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 - FLL Test 1 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x1000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8351_mode3_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0001, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0010, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0BFB, /* R134 - GPIO Configuration (i/o) */ - 0x0FFD, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0310, /* R140 - GPIO Function Select 1 */ - 0x0001, /* R141 - GPIO Function Select 2 */ - 0x2300, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 */ - 0x0000, /* R175 */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0026, /* R186 - DCDC3 Control */ - 0x0800, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0062, /* R189 - DCDC4 Control */ - 0x1400, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 */ - 0x0000, /* R193 */ - 0x0000, /* R194 */ - 0x0026, /* R195 */ - 0x0400, /* R196 */ - 0x0006, /* R197 */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x0006, /* R200 - LDO1 Control */ - 0x0C00, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0016, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x0019, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001A, /* R209 - LDO4 Control */ - 0x1000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 - FLL Test 1 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x1000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8352_mode0_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0002, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0004, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0FFC, /* R134 - GPIO Configuration (i/o) */ - 0x0FFC, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0013, /* R140 - GPIO Function Select 1 */ - 0x0000, /* R141 - GPIO Function Select 2 */ - 0x0000, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0000, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0000, /* R186 - DCDC3 Control */ - 0x0000, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0000, /* R189 - DCDC4 Control */ - 0x0000, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0000, /* R195 - DCDC6 Control */ - 0x0000, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001B, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001B, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001B, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x5000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ - 0x5100, /* R252 */ - 0x1000, /* R253 - DCDC6 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8352_mode1_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0002, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0BFB, /* R134 - GPIO Configuration (i/o) */ - 0x0FFF, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0300, /* R140 - GPIO Function Select 1 */ - 0x0000, /* R141 - GPIO Function Select 2 */ - 0x2300, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x0062, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0006, /* R186 - DCDC3 Control */ - 0x0800, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0006, /* R189 - DCDC4 Control */ - 0x0C00, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x1000, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x0002, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001A, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001F, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001F, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x5000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ - 0x5100, /* R252 */ - 0x1000, /* R253 - DCDC6 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8352_mode2_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0002, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0110, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x09DA, /* R134 - GPIO Configuration (i/o) */ - 0x0DD6, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x1310, /* R140 - GPIO Function Select 1 */ - 0x0033, /* R141 - GPIO Function Select 2 */ - 0x2000, /* R142 - GPIO Function Select 3 */ - 0x0000, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0800, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0056, /* R186 - DCDC3 Control */ - 0x1800, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x000E, /* R189 - DCDC4 Control */ - 0x1000, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x0C00, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0006, /* R203 - LDO2 Control */ - 0x0400, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001C, /* R206 - LDO3 Control */ - 0x1400, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001A, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x5000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ - 0x5100, /* R252 */ - 0x1000, /* R253 - DCDC6 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8352_mode3_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0002, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0010, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0BFB, /* R134 - GPIO Configuration (i/o) */ - 0x0FFD, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0310, /* R140 - GPIO Function Select 1 */ - 0x0001, /* R141 - GPIO Function Select 2 */ - 0x2300, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x0006, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0050, /* R186 - DCDC3 Control */ - 0x0C00, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x000E, /* R189 - DCDC4 Control */ - 0x0400, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0029, /* R195 - DCDC6 Control */ - 0x0800, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001D, /* R200 - LDO1 Control */ - 0x1000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0017, /* R203 - LDO2 Control */ - 0x1000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x0006, /* R206 - LDO3 Control */ - 0x1000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x0010, /* R209 - LDO4 Control */ - 0x1000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x5000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ - 0x5100, /* R252 */ - 0x1000, /* R253 - DCDC6 Test Controls */ -}; -#endif - /* * Access masks. */ -const struct wm8350_reg_access wm8350_reg_io_map[] = { +static const struct wm8350_reg_access { + u16 readable; /* Mask of readable bits */ + u16 writable; /* Mask of writable bits */ + u16 vol; /* Mask of volatile bits */ +} wm8350_reg_io_map[] = { /* read write volatile */ - { 0xFFFF, 0xFFFF, 0xFFFF }, /* R0 - Reset/ID */ - { 0x7CFF, 0x0C00, 0x7FFF }, /* R1 - ID */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R0 - Reset/ID */ + { 0x7CFF, 0x0C00, 0x0000 }, /* R1 - ID */ { 0x007F, 0x0000, 0x0000 }, /* R2 - ROM Mask ID */ { 0xBE3B, 0xBE3B, 0x8000 }, /* R3 - System Control 1 */ { 0xFEF7, 0xFEF7, 0xF800 }, /* R4 - System Control 2 */ @@ -3433,3 +281,59 @@ const struct wm8350_reg_access wm8350_reg_io_map[] = { { 0x0000, 0x0000, 0x0000 }, /* R254 */ { 0x0000, 0x0000, 0x0000 }, /* R255 */ }; + +static bool wm8350_readable(struct device *dev, unsigned int reg) +{ + return wm8350_reg_io_map[reg].readable; +} + +static bool wm8350_writeable(struct device *dev, unsigned int reg) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + + if (!wm8350->unlocked) { + if ((reg >= WM8350_GPIO_FUNCTION_SELECT_1 && + reg <= WM8350_GPIO_FUNCTION_SELECT_4) || + (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 && + reg <= WM8350_BATTERY_CHARGER_CONTROL_3)) + return false; + } + + return wm8350_reg_io_map[reg].writable; +} + +static bool wm8350_volatile(struct device *dev, unsigned int reg) +{ + return wm8350_reg_io_map[reg].vol; +} + +static bool wm8350_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8350_SYSTEM_INTERRUPTS: + case WM8350_INT_STATUS_1: + case WM8350_INT_STATUS_2: + case WM8350_POWER_UP_INT_STATUS: + case WM8350_UNDER_VOLTAGE_INT_STATUS: + case WM8350_OVER_CURRENT_INT_STATUS: + case WM8350_GPIO_INT_STATUS: + case WM8350_COMPARATOR_INT_STATUS: + return true; + + default: + return false; + } +} + +const struct regmap_config wm8350_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .cache_type = REGCACHE_RBTREE, + + .max_register = WM8350_MAX_REGISTER, + .readable_reg = wm8350_readable, + .writeable_reg = wm8350_writeable, + .volatile_reg = wm8350_volatile, + .precious_reg = wm8350_precious, +}; diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 1e321d349777..eec74aa55fdf 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -283,9 +283,24 @@ static int wm8994_suspend(struct device *dev) wm8994_reg_write(wm8994, WM8994_SOFTWARE_RESET, wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET)); - regcache_cache_only(wm8994->regmap, true); regcache_mark_dirty(wm8994->regmap); + /* Restore GPIO registers to prevent problems with mismatched + * pin configurations. + */ + ret = regcache_sync_region(wm8994->regmap, WM8994_GPIO_1, + WM8994_GPIO_11); + if (ret != 0) + dev_err(dev, "Failed to restore GPIO registers: %d\n", ret); + + /* In case one of the GPIOs is used as a wake input. */ + ret = regcache_sync_region(wm8994->regmap, + WM8994_INTERRUPT_STATUS_1_MASK, + WM8994_INTERRUPT_STATUS_1_MASK); + if (ret != 0) + dev_err(dev, "Failed to restore interrupt mask: %d\n", ret); + + regcache_cache_only(wm8994->regmap, true); wm8994->suspended = true; ret = regulator_bulk_disable(wm8994->num_supplies, diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c index f1837f669755..0aac4aff17a5 100644 --- a/drivers/mfd/wm8994-irq.c +++ b/drivers/mfd/wm8994-irq.c @@ -21,6 +21,7 @@ #include <linux/regmap.h> #include <linux/mfd/wm8994/core.h> +#include <linux/mfd/wm8994/pdata.h> #include <linux/mfd/wm8994/registers.h> #include <linux/delay.h> @@ -139,6 +140,8 @@ static struct regmap_irq_chip wm8994_irq_chip = { int wm8994_irq_init(struct wm8994 *wm8994) { int ret; + unsigned long irqflags; + struct wm8994_pdata *pdata = wm8994->dev->platform_data; if (!wm8994->irq) { dev_warn(wm8994->dev, @@ -147,8 +150,13 @@ int wm8994_irq_init(struct wm8994 *wm8994) return 0; } + /* select user or default irq flags */ + irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; + if (pdata->irq_flags) + irqflags = pdata->irq_flags; + ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + irqflags, wm8994->irq_base, &wm8994_irq_chip, &wm8994->irq_data); if (ret != 0) { diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 154f3ef07631..98a442da892a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -64,6 +64,7 @@ config AB8500_PWM bool "AB8500 PWM support" depends on AB8500_CORE && ARCH_U8500 select HAVE_PWM + depends on !PWM help This driver exports functions to enable/disble/config/free Pulse Width Modulation in the Analog Baseband Chip AB8500. diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c index 042a8fe4efaa..d7a9aa14e5d5 100644 --- a/drivers/misc/ab8500-pwm.c +++ b/drivers/misc/ab8500-pwm.c @@ -142,16 +142,10 @@ static int __devexit ab8500_pwm_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id ab8500_pwm_match[] = { - { .compatible = "stericsson,ab8500-pwm", }, - {} -}; - static struct platform_driver ab8500_pwm_driver = { .driver = { .name = "ab8500-pwm", .owner = THIS_MODULE, - .of_match_table = ab8500_pwm_match, }, .probe = ab8500_pwm_probe, .remove = __devexit_p(ab8500_pwm_remove), diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 28adefe70f96..08aad69c8da4 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -477,6 +477,8 @@ static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf, int i, n, out; buf = (char *)__get_free_page(GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; n = snprintf(buf, PAGE_SIZE, "Available crash types:\n"); for (i = 0; i < ARRAY_SIZE(cp_type); i++) diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 2b62232c2c6a..acfaeeb9e01a 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -349,6 +349,11 @@ void st_int_recv(void *disc_data, st_gdata->rx_skb = alloc_skb( st_gdata->list[type]->max_frame_size, GFP_ATOMIC); + if (st_gdata->rx_skb == NULL) { + pr_err("out of memory: dropping\n"); + goto done; + } + skb_reserve(st_gdata->rx_skb, st_gdata->list[type]->reserve); /* next 2 required for BT only */ diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index a6fa884ae49b..100b6775e175 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -52,9 +52,10 @@ #define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) #define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) +#define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa -#define JZ_NAND_MEM_ADDR_OFFSET 0x10000 #define JZ_NAND_MEM_CMD_OFFSET 0x08000 +#define JZ_NAND_MEM_ADDR_OFFSET 0x10000 struct jz_nand { struct mtd_info mtd; @@ -62,8 +63,11 @@ struct jz_nand { void __iomem *base; struct resource *mem; - void __iomem *bank_base; - struct resource *bank_mem; + unsigned char banks[JZ_NAND_NUM_BANKS]; + void __iomem *bank_base[JZ_NAND_NUM_BANKS]; + struct resource *bank_mem[JZ_NAND_NUM_BANKS]; + + int selected_bank; struct jz_nand_platform_data *pdata; bool is_reading; @@ -74,26 +78,50 @@ static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) return container_of(mtd, struct jz_nand, mtd); } +static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + struct nand_chip *chip = mtd->priv; + uint32_t ctrl; + int banknr; + + ctrl = readl(nand->base + JZ_REG_NAND_CTRL); + ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK; + + if (chipnr == -1) { + banknr = -1; + } else { + banknr = nand->banks[chipnr] - 1; + chip->IO_ADDR_R = nand->bank_base[banknr]; + chip->IO_ADDR_W = nand->bank_base[banknr]; + } + writel(ctrl, nand->base + JZ_REG_NAND_CTRL); + + nand->selected_bank = banknr; +} + static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) { struct jz_nand *nand = mtd_to_jz_nand(mtd); struct nand_chip *chip = mtd->priv; uint32_t reg; + void __iomem *bank_base = nand->bank_base[nand->selected_bank]; + + BUG_ON(nand->selected_bank < 0); if (ctrl & NAND_CTRL_CHANGE) { BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); if (ctrl & NAND_ALE) - chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET; + bank_base += JZ_NAND_MEM_ADDR_OFFSET; else if (ctrl & NAND_CLE) - chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET; - else - chip->IO_ADDR_W = nand->bank_base; + bank_base += JZ_NAND_MEM_CMD_OFFSET; + chip->IO_ADDR_W = bank_base; reg = readl(nand->base + JZ_REG_NAND_CTRL); if (ctrl & NAND_NCE) - reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); + reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); else - reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); + reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); writel(reg, nand->base + JZ_REG_NAND_CTRL); } if (dat != NAND_CMD_NONE) @@ -252,7 +280,7 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, } static int jz_nand_ioremap_resource(struct platform_device *pdev, - const char *name, struct resource **res, void __iomem **base) + const char *name, struct resource **res, void *__iomem *base) { int ret; @@ -288,6 +316,90 @@ err: return ret; } +static inline void jz_nand_iounmap_resource(struct resource *res, void __iomem *base) +{ + iounmap(base); + release_mem_region(res->start, resource_size(res)); +} + +static int __devinit jz_nand_detect_bank(struct platform_device *pdev, struct jz_nand *nand, unsigned char bank, size_t chipnr, uint8_t *nand_maf_id, uint8_t *nand_dev_id) { + int ret; + int gpio; + char gpio_name[9]; + char res_name[6]; + uint32_t ctrl; + struct mtd_info *mtd = &nand->mtd; + struct nand_chip *chip = &nand->chip; + + /* Request GPIO port. */ + gpio = JZ_GPIO_MEM_CS0 + bank - 1; + sprintf(gpio_name, "NAND CS%d", bank); + ret = gpio_request(gpio, gpio_name); + if (ret) { + dev_warn(&pdev->dev, + "Failed to request %s gpio %d: %d\n", + gpio_name, gpio, ret); + goto notfound_gpio; + } + + /* Request I/O resource. */ + sprintf(res_name, "bank%d", bank); + ret = jz_nand_ioremap_resource(pdev, res_name, + &nand->bank_mem[bank - 1], + &nand->bank_base[bank - 1]); + if (ret) + goto notfound_resource; + + /* Enable chip in bank. */ + jz_gpio_set_function(gpio, JZ_GPIO_FUNC_MEM_CS0); + ctrl = readl(nand->base + JZ_REG_NAND_CTRL); + ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1); + writel(ctrl, nand->base + JZ_REG_NAND_CTRL); + + if (chipnr == 0) { + /* Detect first chip. */ + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + goto notfound_id; + + /* Retrieve the IDs from the first chip. */ + chip->select_chip(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + *nand_maf_id = chip->read_byte(mtd); + *nand_dev_id = chip->read_byte(mtd); + } else { + /* Detect additional chip. */ + chip->select_chip(mtd, chipnr); + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + if (*nand_maf_id != chip->read_byte(mtd) + || *nand_dev_id != chip->read_byte(mtd)) { + ret = -ENODEV; + goto notfound_id; + } + + /* Update size of the MTD. */ + chip->numchips++; + mtd->size += chip->chipsize; + } + + dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank); + return 0; + +notfound_id: + dev_info(&pdev->dev, "No chip found on bank %i\n", bank); + ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1)); + writel(ctrl, nand->base + JZ_REG_NAND_CTRL); + jz_gpio_set_function(gpio, JZ_GPIO_FUNC_NONE); + jz_nand_iounmap_resource(nand->bank_mem[bank - 1], + nand->bank_base[bank - 1]); +notfound_resource: + gpio_free(gpio); +notfound_gpio: + return ret; +} + static int __devinit jz_nand_probe(struct platform_device *pdev) { int ret; @@ -295,6 +407,8 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) struct nand_chip *chip; struct mtd_info *mtd; struct jz_nand_platform_data *pdata = pdev->dev.platform_data; + size_t chipnr, bank_idx; + uint8_t nand_maf_id = 0, nand_dev_id = 0; nand = kzalloc(sizeof(*nand), GFP_KERNEL); if (!nand) { @@ -305,10 +419,6 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); if (ret) goto err_free; - ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem, - &nand->bank_base); - if (ret) - goto err_iounmap_mmio; if (pdata && gpio_is_valid(pdata->busy_gpio)) { ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); @@ -316,7 +426,7 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to request busy gpio %d: %d\n", pdata->busy_gpio, ret); - goto err_iounmap_mem; + goto err_iounmap_mmio; } } @@ -339,22 +449,51 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) chip->chip_delay = 50; chip->cmd_ctrl = jz_nand_cmd_ctrl; + chip->select_chip = jz_nand_select_chip; if (pdata && gpio_is_valid(pdata->busy_gpio)) chip->dev_ready = jz_nand_dev_ready; - chip->IO_ADDR_R = nand->bank_base; - chip->IO_ADDR_W = nand->bank_base; - nand->pdata = pdata; platform_set_drvdata(pdev, nand); - writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL); - - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) { - dev_err(&pdev->dev, "Failed to scan nand\n"); - goto err_gpio_free; + /* We are going to autodetect NAND chips in the banks specified in the + * platform data. Although nand_scan_ident() can detect multiple chips, + * it requires those chips to be numbered consecuitively, which is not + * always the case for external memory banks. And a fixed chip-to-bank + * mapping is not practical either, since for example Dingoo units + * produced at different times have NAND chips in different banks. + */ + chipnr = 0; + for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) { + unsigned char bank; + + /* If there is no platform data, look for NAND in bank 1, + * which is the most likely bank since it is the only one + * that can be booted from. + */ + bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1; + if (bank == 0) + break; + if (bank > JZ_NAND_NUM_BANKS) { + dev_warn(&pdev->dev, + "Skipping non-existing bank: %d\n", bank); + continue; + } + /* The detection routine will directly or indirectly call + * jz_nand_select_chip(), so nand->banks has to contain the + * bank we're checking. + */ + nand->banks[chipnr] = bank; + if (jz_nand_detect_bank(pdev, nand, bank, chipnr, + &nand_maf_id, &nand_dev_id) == 0) + chipnr++; + else + nand->banks[chipnr] = 0; + } + if (chipnr == 0) { + dev_err(&pdev->dev, "No NAND chips found\n"); + goto err_gpio_busy; } if (pdata && pdata->ident_callback) { @@ -364,8 +503,8 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) ret = nand_scan_tail(mtd); if (ret) { - dev_err(&pdev->dev, "Failed to scan nand\n"); - goto err_gpio_free; + dev_err(&pdev->dev, "Failed to scan NAND\n"); + goto err_unclaim_banks; } ret = mtd_device_parse_register(mtd, NULL, NULL, @@ -382,14 +521,21 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) return 0; err_nand_release: - nand_release(&nand->mtd); -err_gpio_free: + nand_release(mtd); +err_unclaim_banks: + while (chipnr--) { + unsigned char bank = nand->banks[chipnr]; + gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); + jz_nand_iounmap_resource(nand->bank_mem[bank - 1], + nand->bank_base[bank - 1]); + } + writel(0, nand->base + JZ_REG_NAND_CTRL); +err_gpio_busy: + if (pdata && gpio_is_valid(pdata->busy_gpio)) + gpio_free(pdata->busy_gpio); platform_set_drvdata(pdev, NULL); - gpio_free(pdata->busy_gpio); -err_iounmap_mem: - iounmap(nand->bank_base); err_iounmap_mmio: - iounmap(nand->base); + jz_nand_iounmap_resource(nand->mem, nand->base); err_free: kfree(nand); return ret; @@ -398,16 +544,26 @@ err_free: static int __devexit jz_nand_remove(struct platform_device *pdev) { struct jz_nand *nand = platform_get_drvdata(pdev); + struct jz_nand_platform_data *pdata = pdev->dev.platform_data; + size_t i; nand_release(&nand->mtd); /* Deassert and disable all chips */ writel(0, nand->base + JZ_REG_NAND_CTRL); - iounmap(nand->bank_base); - release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem)); - iounmap(nand->base); - release_mem_region(nand->mem->start, resource_size(nand->mem)); + for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) { + unsigned char bank = nand->banks[i]; + if (bank != 0) { + jz_nand_iounmap_resource(nand->bank_mem[bank - 1], + nand->bank_base[bank - 1]); + gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); + } + } + if (pdata && gpio_is_valid(pdata->busy_gpio)) + gpio_free(pdata->busy_gpio); + + jz_nand_iounmap_resource(nand->mem, nand->base); platform_set_drvdata(pdev, NULL); kfree(nand); diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index f0921d16f0a9..6ff7ad006c30 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -74,7 +74,6 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev) const struct platform_device_id *id; struct resource *mem; int irq; -#ifdef CONFIG_HAVE_CLK struct clk *clk; /* get the appropriate clk */ @@ -84,7 +83,6 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev) ret = -ENODEV; goto exit; } -#endif /* get the platform data */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -145,10 +143,8 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev) dev->irq = irq; priv->base = addr; -#ifdef CONFIG_HAVE_CLK priv->can.clock.freq = clk_get_rate(clk); priv->priv = clk; -#endif platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); @@ -172,10 +168,8 @@ exit_iounmap: exit_release_mem: release_mem_region(mem->start, resource_size(mem)); exit_free_clk: -#ifdef CONFIG_HAVE_CLK clk_put(clk); exit: -#endif dev_err(&pdev->dev, "probe failed\n"); return ret; @@ -196,9 +190,7 @@ static int __devexit c_can_plat_remove(struct platform_device *pdev) mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(mem->start, resource_size(mem)); -#ifdef CONFIG_HAVE_CLK clk_put(priv->priv); -#endif return 0; } diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c index cd827ff4a021..c42bbb16cdae 100644 --- a/drivers/net/ethernet/octeon/octeon_mgmt.c +++ b/drivers/net/ethernet/octeon/octeon_mgmt.c @@ -6,19 +6,21 @@ * Copyright (C) 2009 Cavium Networks */ -#include <linux/capability.h> +#include <linux/platform_device.h> #include <linux/dma-mapping.h> -#include <linux/init.h> -#include <linux/module.h> +#include <linux/etherdevice.h> +#include <linux/capability.h> #include <linux/interrupt.h> -#include <linux/platform_device.h> #include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/if.h> +#include <linux/spinlock.h> #include <linux/if_vlan.h> +#include <linux/of_mdio.h> +#include <linux/module.h> +#include <linux/of_net.h> +#include <linux/init.h> #include <linux/slab.h> #include <linux/phy.h> -#include <linux/spinlock.h> +#include <linux/io.h> #include <asm/octeon/octeon.h> #include <asm/octeon/cvmx-mixx-defs.h> @@ -58,8 +60,56 @@ union mgmt_port_ring_entry { } s; }; +#define MIX_ORING1 0x0 +#define MIX_ORING2 0x8 +#define MIX_IRING1 0x10 +#define MIX_IRING2 0x18 +#define MIX_CTL 0x20 +#define MIX_IRHWM 0x28 +#define MIX_IRCNT 0x30 +#define MIX_ORHWM 0x38 +#define MIX_ORCNT 0x40 +#define MIX_ISR 0x48 +#define MIX_INTENA 0x50 +#define MIX_REMCNT 0x58 +#define MIX_BIST 0x78 + +#define AGL_GMX_PRT_CFG 0x10 +#define AGL_GMX_RX_FRM_CTL 0x18 +#define AGL_GMX_RX_FRM_MAX 0x30 +#define AGL_GMX_RX_JABBER 0x38 +#define AGL_GMX_RX_STATS_CTL 0x50 + +#define AGL_GMX_RX_STATS_PKTS_DRP 0xb0 +#define AGL_GMX_RX_STATS_OCTS_DRP 0xb8 +#define AGL_GMX_RX_STATS_PKTS_BAD 0xc0 + +#define AGL_GMX_RX_ADR_CTL 0x100 +#define AGL_GMX_RX_ADR_CAM_EN 0x108 +#define AGL_GMX_RX_ADR_CAM0 0x180 +#define AGL_GMX_RX_ADR_CAM1 0x188 +#define AGL_GMX_RX_ADR_CAM2 0x190 +#define AGL_GMX_RX_ADR_CAM3 0x198 +#define AGL_GMX_RX_ADR_CAM4 0x1a0 +#define AGL_GMX_RX_ADR_CAM5 0x1a8 + +#define AGL_GMX_TX_STATS_CTL 0x268 +#define AGL_GMX_TX_CTL 0x270 +#define AGL_GMX_TX_STAT0 0x280 +#define AGL_GMX_TX_STAT1 0x288 +#define AGL_GMX_TX_STAT2 0x290 +#define AGL_GMX_TX_STAT3 0x298 +#define AGL_GMX_TX_STAT4 0x2a0 +#define AGL_GMX_TX_STAT5 0x2a8 +#define AGL_GMX_TX_STAT6 0x2b0 +#define AGL_GMX_TX_STAT7 0x2b8 +#define AGL_GMX_TX_STAT8 0x2c0 +#define AGL_GMX_TX_STAT9 0x2c8 + struct octeon_mgmt { struct net_device *netdev; + u64 mix; + u64 agl; int port; int irq; u64 *tx_ring; @@ -85,31 +135,34 @@ struct octeon_mgmt { struct napi_struct napi; struct tasklet_struct tx_clean_tasklet; struct phy_device *phydev; + struct device_node *phy_np; + resource_size_t mix_phys; + resource_size_t mix_size; + resource_size_t agl_phys; + resource_size_t agl_size; }; static void octeon_mgmt_set_rx_irq(struct octeon_mgmt *p, int enable) { - int port = p->port; union cvmx_mixx_intena mix_intena; unsigned long flags; spin_lock_irqsave(&p->lock, flags); - mix_intena.u64 = cvmx_read_csr(CVMX_MIXX_INTENA(port)); + mix_intena.u64 = cvmx_read_csr(p->mix + MIX_INTENA); mix_intena.s.ithena = enable ? 1 : 0; - cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64); + cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64); spin_unlock_irqrestore(&p->lock, flags); } static void octeon_mgmt_set_tx_irq(struct octeon_mgmt *p, int enable) { - int port = p->port; union cvmx_mixx_intena mix_intena; unsigned long flags; spin_lock_irqsave(&p->lock, flags); - mix_intena.u64 = cvmx_read_csr(CVMX_MIXX_INTENA(port)); + mix_intena.u64 = cvmx_read_csr(p->mix + MIX_INTENA); mix_intena.s.othena = enable ? 1 : 0; - cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64); + cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64); spin_unlock_irqrestore(&p->lock, flags); } @@ -146,7 +199,6 @@ static unsigned int ring_size_to_bytes(unsigned int ring_size) static void octeon_mgmt_rx_fill_ring(struct net_device *netdev) { struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; while (p->rx_current_fill < ring_max_fill(OCTEON_MGMT_RX_RING_SIZE)) { unsigned int size; @@ -177,24 +229,23 @@ static void octeon_mgmt_rx_fill_ring(struct net_device *netdev) (p->rx_next_fill + 1) % OCTEON_MGMT_RX_RING_SIZE; p->rx_current_fill++; /* Ring the bell. */ - cvmx_write_csr(CVMX_MIXX_IRING2(port), 1); + cvmx_write_csr(p->mix + MIX_IRING2, 1); } } static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p) { - int port = p->port; union cvmx_mixx_orcnt mix_orcnt; union mgmt_port_ring_entry re; struct sk_buff *skb; int cleaned = 0; unsigned long flags; - mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port)); + mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT); while (mix_orcnt.s.orcnt) { spin_lock_irqsave(&p->tx_list.lock, flags); - mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port)); + mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT); if (mix_orcnt.s.orcnt == 0) { spin_unlock_irqrestore(&p->tx_list.lock, flags); @@ -214,7 +265,7 @@ static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p) mix_orcnt.s.orcnt = 1; /* Acknowledge to hardware that we have the buffer. */ - cvmx_write_csr(CVMX_MIXX_ORCNT(port), mix_orcnt.u64); + cvmx_write_csr(p->mix + MIX_ORCNT, mix_orcnt.u64); p->tx_current_fill--; spin_unlock_irqrestore(&p->tx_list.lock, flags); @@ -224,7 +275,7 @@ static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p) dev_kfree_skb_any(skb); cleaned++; - mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port)); + mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT); } if (cleaned && netif_queue_stopped(p->netdev)) @@ -241,13 +292,12 @@ static void octeon_mgmt_clean_tx_tasklet(unsigned long arg) static void octeon_mgmt_update_rx_stats(struct net_device *netdev) { struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; unsigned long flags; u64 drop, bad; /* These reads also clear the count registers. */ - drop = cvmx_read_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_DRP(port)); - bad = cvmx_read_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_BAD(port)); + drop = cvmx_read_csr(p->agl + AGL_GMX_RX_STATS_PKTS_DRP); + bad = cvmx_read_csr(p->agl + AGL_GMX_RX_STATS_PKTS_BAD); if (drop || bad) { /* Do an atomic update. */ @@ -261,15 +311,14 @@ static void octeon_mgmt_update_rx_stats(struct net_device *netdev) static void octeon_mgmt_update_tx_stats(struct net_device *netdev) { struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; unsigned long flags; union cvmx_agl_gmx_txx_stat0 s0; union cvmx_agl_gmx_txx_stat1 s1; /* These reads also clear the count registers. */ - s0.u64 = cvmx_read_csr(CVMX_AGL_GMX_TXX_STAT0(port)); - s1.u64 = cvmx_read_csr(CVMX_AGL_GMX_TXX_STAT1(port)); + s0.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_STAT0); + s1.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_STAT1); if (s0.s.xsdef || s0.s.xscol || s1.s.scol || s1.s.mcol) { /* Do an atomic update. */ @@ -308,7 +357,6 @@ static u64 octeon_mgmt_dequeue_rx_buffer(struct octeon_mgmt *p, static int octeon_mgmt_receive_one(struct octeon_mgmt *p) { - int port = p->port; struct net_device *netdev = p->netdev; union cvmx_mixx_ircnt mix_ircnt; union mgmt_port_ring_entry re; @@ -381,18 +429,17 @@ done: /* Tell the hardware we processed a packet. */ mix_ircnt.u64 = 0; mix_ircnt.s.ircnt = 1; - cvmx_write_csr(CVMX_MIXX_IRCNT(port), mix_ircnt.u64); + cvmx_write_csr(p->mix + MIX_IRCNT, mix_ircnt.u64); return rc; } static int octeon_mgmt_receive_packets(struct octeon_mgmt *p, int budget) { - int port = p->port; unsigned int work_done = 0; union cvmx_mixx_ircnt mix_ircnt; int rc; - mix_ircnt.u64 = cvmx_read_csr(CVMX_MIXX_IRCNT(port)); + mix_ircnt.u64 = cvmx_read_csr(p->mix + MIX_IRCNT); while (work_done < budget && mix_ircnt.s.ircnt) { rc = octeon_mgmt_receive_one(p); @@ -400,7 +447,7 @@ static int octeon_mgmt_receive_packets(struct octeon_mgmt *p, int budget) work_done++; /* Check for more packets. */ - mix_ircnt.u64 = cvmx_read_csr(CVMX_MIXX_IRCNT(port)); + mix_ircnt.u64 = cvmx_read_csr(p->mix + MIX_IRCNT); } octeon_mgmt_rx_fill_ring(p->netdev); @@ -434,16 +481,16 @@ static void octeon_mgmt_reset_hw(struct octeon_mgmt *p) union cvmx_agl_gmx_bist agl_gmx_bist; mix_ctl.u64 = 0; - cvmx_write_csr(CVMX_MIXX_CTL(p->port), mix_ctl.u64); + cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64); do { - mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(p->port)); + mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL); } while (mix_ctl.s.busy); mix_ctl.s.reset = 1; - cvmx_write_csr(CVMX_MIXX_CTL(p->port), mix_ctl.u64); - cvmx_read_csr(CVMX_MIXX_CTL(p->port)); + cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64); + cvmx_read_csr(p->mix + MIX_CTL); cvmx_wait(64); - mix_bist.u64 = cvmx_read_csr(CVMX_MIXX_BIST(p->port)); + mix_bist.u64 = cvmx_read_csr(p->mix + MIX_BIST); if (mix_bist.u64) dev_warn(p->dev, "MIX failed BIST (0x%016llx)\n", (unsigned long long)mix_bist.u64); @@ -474,7 +521,6 @@ static void octeon_mgmt_cam_state_add(struct octeon_mgmt_cam_state *cs, static void octeon_mgmt_set_rx_filtering(struct net_device *netdev) { struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; union cvmx_agl_gmx_rxx_adr_ctl adr_ctl; union cvmx_agl_gmx_prtx_cfg agl_gmx_prtx; unsigned long flags; @@ -520,29 +566,29 @@ static void octeon_mgmt_set_rx_filtering(struct net_device *netdev) spin_lock_irqsave(&p->lock, flags); /* Disable packet I/O. */ - agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); + agl_gmx_prtx.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG); prev_packet_enable = agl_gmx_prtx.s.en; agl_gmx_prtx.s.en = 0; - cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); + cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, agl_gmx_prtx.u64); adr_ctl.u64 = 0; adr_ctl.s.cam_mode = cam_mode; adr_ctl.s.mcst = multicast_mode; adr_ctl.s.bcst = 1; /* Allow broadcast */ - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port), adr_ctl.u64); + cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CTL, adr_ctl.u64); - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM0(port), cam_state.cam[0]); - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM1(port), cam_state.cam[1]); - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM2(port), cam_state.cam[2]); - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM3(port), cam_state.cam[3]); - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM4(port), cam_state.cam[4]); - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM5(port), cam_state.cam[5]); - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), cam_state.cam_mask); + cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM0, cam_state.cam[0]); + cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM1, cam_state.cam[1]); + cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM2, cam_state.cam[2]); + cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM3, cam_state.cam[3]); + cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM4, cam_state.cam[4]); + cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM5, cam_state.cam[5]); + cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM_EN, cam_state.cam_mask); /* Restore packet I/O. */ agl_gmx_prtx.s.en = prev_packet_enable; - cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); + cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, agl_gmx_prtx.u64); spin_unlock_irqrestore(&p->lock, flags); } @@ -564,7 +610,6 @@ static int octeon_mgmt_set_mac_address(struct net_device *netdev, void *addr) static int octeon_mgmt_change_mtu(struct net_device *netdev, int new_mtu) { struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; int size_without_fcs = new_mtu + OCTEON_MGMT_RX_HEADROOM; /* @@ -580,8 +625,8 @@ static int octeon_mgmt_change_mtu(struct net_device *netdev, int new_mtu) netdev->mtu = new_mtu; - cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_MAX(port), size_without_fcs); - cvmx_write_csr(CVMX_AGL_GMX_RXX_JABBER(port), + cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_MAX, size_without_fcs); + cvmx_write_csr(p->agl + AGL_GMX_RX_JABBER, (size_without_fcs + 7) & 0xfff8); return 0; @@ -591,14 +636,13 @@ static irqreturn_t octeon_mgmt_interrupt(int cpl, void *dev_id) { struct net_device *netdev = dev_id; struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; union cvmx_mixx_isr mixx_isr; - mixx_isr.u64 = cvmx_read_csr(CVMX_MIXX_ISR(port)); + mixx_isr.u64 = cvmx_read_csr(p->mix + MIX_ISR); /* Clear any pending interrupts */ - cvmx_write_csr(CVMX_MIXX_ISR(port), mixx_isr.u64); - cvmx_read_csr(CVMX_MIXX_ISR(port)); + cvmx_write_csr(p->mix + MIX_ISR, mixx_isr.u64); + cvmx_read_csr(p->mix + MIX_ISR); if (mixx_isr.s.irthresh) { octeon_mgmt_disable_rx_irq(p); @@ -629,7 +673,6 @@ static int octeon_mgmt_ioctl(struct net_device *netdev, static void octeon_mgmt_adjust_link(struct net_device *netdev) { struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; union cvmx_agl_gmx_prtx_cfg prtx_cfg; unsigned long flags; int link_changed = 0; @@ -640,11 +683,9 @@ static void octeon_mgmt_adjust_link(struct net_device *netdev) link_changed = 1; if (p->last_duplex != p->phydev->duplex) { p->last_duplex = p->phydev->duplex; - prtx_cfg.u64 = - cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); + prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG); prtx_cfg.s.duplex = p->phydev->duplex; - cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), - prtx_cfg.u64); + cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64); } } else { if (p->last_link) @@ -670,18 +711,16 @@ static void octeon_mgmt_adjust_link(struct net_device *netdev) static int octeon_mgmt_init_phy(struct net_device *netdev) { struct octeon_mgmt *p = netdev_priv(netdev); - char phy_id[MII_BUS_ID_SIZE + 3]; - if (octeon_is_simulation()) { + if (octeon_is_simulation() || p->phy_np == NULL) { /* No PHYs in the simulator. */ netif_carrier_on(netdev); return 0; } - snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "mdio-octeon-0", p->port); - - p->phydev = phy_connect(netdev, phy_id, octeon_mgmt_adjust_link, 0, - PHY_INTERFACE_MODE_MII); + p->phydev = of_phy_connect(netdev, p->phy_np, + octeon_mgmt_adjust_link, 0, + PHY_INTERFACE_MODE_MII); if (IS_ERR(p->phydev)) { p->phydev = NULL; @@ -737,14 +776,14 @@ static int octeon_mgmt_open(struct net_device *netdev) octeon_mgmt_reset_hw(p); - mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port)); + mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL); /* Bring it out of reset if needed. */ if (mix_ctl.s.reset) { mix_ctl.s.reset = 0; - cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64); + cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64); do { - mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port)); + mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL); } while (mix_ctl.s.reset); } @@ -755,17 +794,17 @@ static int octeon_mgmt_open(struct net_device *netdev) oring1.u64 = 0; oring1.s.obase = p->tx_ring_handle >> 3; oring1.s.osize = OCTEON_MGMT_TX_RING_SIZE; - cvmx_write_csr(CVMX_MIXX_ORING1(port), oring1.u64); + cvmx_write_csr(p->mix + MIX_ORING1, oring1.u64); iring1.u64 = 0; iring1.s.ibase = p->rx_ring_handle >> 3; iring1.s.isize = OCTEON_MGMT_RX_RING_SIZE; - cvmx_write_csr(CVMX_MIXX_IRING1(port), iring1.u64); + cvmx_write_csr(p->mix + MIX_IRING1, iring1.u64); /* Disable packet I/O. */ - prtx_cfg.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); + prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG); prtx_cfg.s.en = 0; - cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), prtx_cfg.u64); + cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64); memcpy(sa.sa_data, netdev->dev_addr, ETH_ALEN); octeon_mgmt_set_mac_address(netdev, &sa); @@ -782,7 +821,7 @@ static int octeon_mgmt_open(struct net_device *netdev) mix_ctl.s.nbtarb = 0; /* Arbitration mode */ /* MII CB-request FIFO programmable high watermark */ mix_ctl.s.mrq_hwm = 1; - cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64); + cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64); if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X) || OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X)) { @@ -809,16 +848,16 @@ static int octeon_mgmt_open(struct net_device *netdev) /* Clear statistics. */ /* Clear on read. */ - cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_CTL(port), 1); - cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_DRP(port), 0); - cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_BAD(port), 0); + cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_CTL, 1); + cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_PKTS_DRP, 0); + cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_PKTS_BAD, 0); - cvmx_write_csr(CVMX_AGL_GMX_TXX_STATS_CTL(port), 1); - cvmx_write_csr(CVMX_AGL_GMX_TXX_STAT0(port), 0); - cvmx_write_csr(CVMX_AGL_GMX_TXX_STAT1(port), 0); + cvmx_write_csr(p->agl + AGL_GMX_TX_STATS_CTL, 1); + cvmx_write_csr(p->agl + AGL_GMX_TX_STAT0, 0); + cvmx_write_csr(p->agl + AGL_GMX_TX_STAT1, 0); /* Clear any pending interrupts */ - cvmx_write_csr(CVMX_MIXX_ISR(port), cvmx_read_csr(CVMX_MIXX_ISR(port))); + cvmx_write_csr(p->mix + MIX_ISR, cvmx_read_csr(p->mix + MIX_ISR)); if (request_irq(p->irq, octeon_mgmt_interrupt, 0, netdev->name, netdev)) { @@ -829,18 +868,18 @@ static int octeon_mgmt_open(struct net_device *netdev) /* Interrupt every single RX packet */ mix_irhwm.u64 = 0; mix_irhwm.s.irhwm = 0; - cvmx_write_csr(CVMX_MIXX_IRHWM(port), mix_irhwm.u64); + cvmx_write_csr(p->mix + MIX_IRHWM, mix_irhwm.u64); /* Interrupt when we have 1 or more packets to clean. */ mix_orhwm.u64 = 0; mix_orhwm.s.orhwm = 1; - cvmx_write_csr(CVMX_MIXX_ORHWM(port), mix_orhwm.u64); + cvmx_write_csr(p->mix + MIX_ORHWM, mix_orhwm.u64); /* Enable receive and transmit interrupts */ mix_intena.u64 = 0; mix_intena.s.ithena = 1; mix_intena.s.othena = 1; - cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64); + cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64); /* Enable packet I/O. */ @@ -871,7 +910,7 @@ static int octeon_mgmt_open(struct net_device *netdev) * frame. GMX checks that the PREAMBLE is sent correctly. */ rxx_frm_ctl.s.pre_chk = 1; - cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_CTL(port), rxx_frm_ctl.u64); + cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64); /* Enable the AGL block */ agl_gmx_inf_mode.u64 = 0; @@ -879,13 +918,13 @@ static int octeon_mgmt_open(struct net_device *netdev) cvmx_write_csr(CVMX_AGL_GMX_INF_MODE, agl_gmx_inf_mode.u64); /* Configure the port duplex and enables */ - prtx_cfg.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); + prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG); prtx_cfg.s.tx_en = 1; prtx_cfg.s.rx_en = 1; prtx_cfg.s.en = 1; p->last_duplex = 1; prtx_cfg.s.duplex = p->last_duplex; - cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), prtx_cfg.u64); + cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64); p->last_link = 0; netif_carrier_off(netdev); @@ -949,7 +988,6 @@ static int octeon_mgmt_stop(struct net_device *netdev) static int octeon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev) { struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; union mgmt_port_ring_entry re; unsigned long flags; int rv = NETDEV_TX_BUSY; @@ -993,7 +1031,7 @@ static int octeon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev) netdev->stats.tx_bytes += skb->len; /* Ring the bell. */ - cvmx_write_csr(CVMX_MIXX_ORING2(port), 1); + cvmx_write_csr(p->mix + MIX_ORING2, 1); rv = NETDEV_TX_OK; out: @@ -1071,10 +1109,14 @@ static const struct net_device_ops octeon_mgmt_ops = { static int __devinit octeon_mgmt_probe(struct platform_device *pdev) { - struct resource *res_irq; struct net_device *netdev; struct octeon_mgmt *p; - int i; + const __be32 *data; + const u8 *mac; + struct resource *res_mix; + struct resource *res_agl; + int len; + int result; netdev = alloc_etherdev(sizeof(struct octeon_mgmt)); if (netdev == NULL) @@ -1088,14 +1130,63 @@ static int __devinit octeon_mgmt_probe(struct platform_device *pdev) p->netdev = netdev; p->dev = &pdev->dev; - p->port = pdev->id; + data = of_get_property(pdev->dev.of_node, "cell-index", &len); + if (data && len == sizeof(*data)) { + p->port = be32_to_cpup(data); + } else { + dev_err(&pdev->dev, "no 'cell-index' property\n"); + result = -ENXIO; + goto err; + } + snprintf(netdev->name, IFNAMSIZ, "mgmt%d", p->port); - res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res_irq) + result = platform_get_irq(pdev, 0); + if (result < 0) + goto err; + + p->irq = result; + + res_mix = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res_mix == NULL) { + dev_err(&pdev->dev, "no 'reg' resource\n"); + result = -ENXIO; + goto err; + } + + res_agl = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res_agl == NULL) { + dev_err(&pdev->dev, "no 'reg' resource\n"); + result = -ENXIO; + goto err; + } + + p->mix_phys = res_mix->start; + p->mix_size = resource_size(res_mix); + p->agl_phys = res_agl->start; + p->agl_size = resource_size(res_agl); + + + if (!devm_request_mem_region(&pdev->dev, p->mix_phys, p->mix_size, + res_mix->name)) { + dev_err(&pdev->dev, "request_mem_region (%s) failed\n", + res_mix->name); + result = -ENXIO; + goto err; + } + + if (!devm_request_mem_region(&pdev->dev, p->agl_phys, p->agl_size, + res_agl->name)) { + result = -ENXIO; + dev_err(&pdev->dev, "request_mem_region (%s) failed\n", + res_agl->name); goto err; + } + + + p->mix = (u64)devm_ioremap(&pdev->dev, p->mix_phys, p->mix_size); + p->agl = (u64)devm_ioremap(&pdev->dev, p->agl_phys, p->agl_size); - p->irq = res_irq->start; spin_lock_init(&p->lock); skb_queue_head_init(&p->tx_list); @@ -1108,24 +1199,26 @@ static int __devinit octeon_mgmt_probe(struct platform_device *pdev) netdev->netdev_ops = &octeon_mgmt_ops; netdev->ethtool_ops = &octeon_mgmt_ethtool_ops; - /* The mgmt ports get the first N MACs. */ - for (i = 0; i < 6; i++) - netdev->dev_addr[i] = octeon_bootinfo->mac_addr_base[i]; - netdev->dev_addr[5] += p->port; + mac = of_get_mac_address(pdev->dev.of_node); + + if (mac) + memcpy(netdev->dev_addr, mac, 6); - if (p->port >= octeon_bootinfo->mac_addr_count) - dev_err(&pdev->dev, - "Error %s: Using MAC outside of the assigned range: %pM\n", - netdev->name, netdev->dev_addr); + p->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0); - if (register_netdev(netdev)) + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64); + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + + result = register_netdev(netdev); + if (result) goto err; dev_info(&pdev->dev, "Version " DRV_VERSION "\n"); return 0; + err: free_netdev(netdev); - return -ENOENT; + return result; } static int __devexit octeon_mgmt_remove(struct platform_device *pdev) @@ -1137,10 +1230,19 @@ static int __devexit octeon_mgmt_remove(struct platform_device *pdev) return 0; } +static struct of_device_id octeon_mgmt_match[] = { + { + .compatible = "cavium,octeon-5750-mix", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, octeon_mgmt_match); + static struct platform_driver octeon_mgmt_driver = { .driver = { .name = "octeon_mgmt", .owner = THIS_MODULE, + .of_match_table = octeon_mgmt_match, }, .probe = octeon_mgmt_probe, .remove = __devexit_p(octeon_mgmt_remove), diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index ab4c376cb276..f2d3665430ad 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -82,9 +82,7 @@ struct stmmac_priv { struct stmmac_counters mmc; struct dma_features dma_cap; int hw_cap_support; -#ifdef CONFIG_HAVE_CLK struct clk *stmmac_clk; -#endif int clk_csr; int synopsys_id; struct timer_list eee_ctrl_timer; @@ -113,46 +111,6 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device, void stmmac_disable_eee_mode(struct stmmac_priv *priv); bool stmmac_eee_init(struct stmmac_priv *priv); -#ifdef CONFIG_HAVE_CLK -static inline int stmmac_clk_enable(struct stmmac_priv *priv) -{ - if (!IS_ERR(priv->stmmac_clk)) - return clk_prepare_enable(priv->stmmac_clk); - - return 0; -} - -static inline void stmmac_clk_disable(struct stmmac_priv *priv) -{ - if (IS_ERR(priv->stmmac_clk)) - return; - - clk_disable_unprepare(priv->stmmac_clk); -} -static inline int stmmac_clk_get(struct stmmac_priv *priv) -{ - priv->stmmac_clk = clk_get(priv->device, NULL); - - if (IS_ERR(priv->stmmac_clk)) - return PTR_ERR(priv->stmmac_clk); - - return 0; -} -#else -static inline int stmmac_clk_enable(struct stmmac_priv *priv) -{ - return 0; -} -static inline void stmmac_clk_disable(struct stmmac_priv *priv) -{ -} -static inline int stmmac_clk_get(struct stmmac_priv *priv) -{ - return 0; -} -#endif /* CONFIG_HAVE_CLK */ - - #ifdef CONFIG_STMMAC_PLATFORM extern struct platform_driver stmmac_pltfr_driver; static inline int stmmac_register_platform(void) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index f6b04c1a3672..fd8882f9602a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -28,6 +28,7 @@ https://bugzilla.stlinux.com/ *******************************************************************************/ +#include <linux/clk.h> #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/ip.h> @@ -173,12 +174,8 @@ static void stmmac_verify_args(void) static void stmmac_clk_csr_set(struct stmmac_priv *priv) { -#ifdef CONFIG_HAVE_CLK u32 clk_rate; - if (IS_ERR(priv->stmmac_clk)) - return; - clk_rate = clk_get_rate(priv->stmmac_clk); /* Platform provided default clk_csr would be assumed valid @@ -200,7 +197,6 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv) * we can not estimate the proper divider as it is not known * the frequency of clk_csr_i. So we do not change the default * divider. */ -#endif } #if defined(STMMAC_XMIT_DEBUG) || defined(STMMAC_RX_DEBUG) @@ -1070,7 +1066,7 @@ static int stmmac_open(struct net_device *dev) } else priv->tm->enable = 1; #endif - stmmac_clk_enable(priv); + clk_enable(priv->stmmac_clk); stmmac_check_ether_addr(priv); @@ -1192,7 +1188,7 @@ open_error: if (priv->phydev) phy_disconnect(priv->phydev); - stmmac_clk_disable(priv); + clk_disable(priv->stmmac_clk); return ret; } @@ -1250,7 +1246,7 @@ static int stmmac_release(struct net_device *dev) #ifdef CONFIG_STMMAC_DEBUG_FS stmmac_exit_fs(); #endif - stmmac_clk_disable(priv); + clk_disable(priv->stmmac_clk); return 0; } @@ -2078,11 +2074,14 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device, ret = register_netdev(ndev); if (ret) { pr_err("%s: ERROR %i registering the device\n", __func__, ret); - goto error; + goto error_netdev_register; } - if (stmmac_clk_get(priv)) + priv->stmmac_clk = clk_get(priv->device, NULL); + if (IS_ERR(priv->stmmac_clk)) { pr_warning("%s: warning: cannot get CSR clock\n", __func__); + goto error_clk_get; + } /* If a specific clk_csr value is passed from the platform * this means that the CSR Clock Range selection cannot be @@ -2100,15 +2099,17 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device, if (ret < 0) { pr_debug("%s: MDIO bus (id: %d) registration failed", __func__, priv->plat->bus_id); - goto error; + goto error_mdio_register; } return priv; -error: - netif_napi_del(&priv->napi); - +error_mdio_register: + clk_put(priv->stmmac_clk); +error_clk_get: unregister_netdev(ndev); +error_netdev_register: + netif_napi_del(&priv->napi); free_netdev(ndev); return NULL; @@ -2177,7 +2178,7 @@ int stmmac_suspend(struct net_device *ndev) else { stmmac_set_mac(priv->ioaddr, false); /* Disable clock in case of PWM is off */ - stmmac_clk_disable(priv); + clk_disable(priv->stmmac_clk); } spin_unlock_irqrestore(&priv->lock, flags); return 0; @@ -2202,7 +2203,7 @@ int stmmac_resume(struct net_device *ndev) priv->hw->mac->pmt(priv->ioaddr, 0); else /* enable the clk prevously disabled */ - stmmac_clk_enable(priv); + clk_enable(priv->stmmac_clk); netif_device_attach(ndev); diff --git a/drivers/net/phy/mdio-octeon.c b/drivers/net/phy/mdio-octeon.c index 826d961f39f7..d4015aa663e6 100644 --- a/drivers/net/phy/mdio-octeon.c +++ b/drivers/net/phy/mdio-octeon.c @@ -3,14 +3,17 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2009 Cavium Networks + * Copyright (C) 2009,2011 Cavium, Inc. */ -#include <linux/gfp.h> -#include <linux/init.h> -#include <linux/module.h> #include <linux/platform_device.h> +#include <linux/of_mdio.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/gfp.h> #include <linux/phy.h> +#include <linux/io.h> #include <asm/octeon/octeon.h> #include <asm/octeon/cvmx-smix-defs.h> @@ -18,9 +21,17 @@ #define DRV_VERSION "1.0" #define DRV_DESCRIPTION "Cavium Networks Octeon SMI/MDIO driver" +#define SMI_CMD 0x0 +#define SMI_WR_DAT 0x8 +#define SMI_RD_DAT 0x10 +#define SMI_CLK 0x18 +#define SMI_EN 0x20 + struct octeon_mdiobus { struct mii_bus *mii_bus; - int unit; + u64 register_base; + resource_size_t mdio_phys; + resource_size_t regsize; int phy_irq[PHY_MAX_ADDR]; }; @@ -35,15 +46,15 @@ static int octeon_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum) smi_cmd.s.phy_op = 1; /* MDIO_CLAUSE_22_READ */ smi_cmd.s.phy_adr = phy_id; smi_cmd.s.reg_adr = regnum; - cvmx_write_csr(CVMX_SMIX_CMD(p->unit), smi_cmd.u64); + cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64); do { /* * Wait 1000 clocks so we don't saturate the RSL bus * doing reads. */ - cvmx_wait(1000); - smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(p->unit)); + __delay(1000); + smi_rd.u64 = cvmx_read_csr(p->register_base + SMI_RD_DAT); } while (smi_rd.s.pending && --timeout); if (smi_rd.s.val) @@ -62,21 +73,21 @@ static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id, smi_wr.u64 = 0; smi_wr.s.dat = val; - cvmx_write_csr(CVMX_SMIX_WR_DAT(p->unit), smi_wr.u64); + cvmx_write_csr(p->register_base + SMI_WR_DAT, smi_wr.u64); smi_cmd.u64 = 0; smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_22_WRITE */ smi_cmd.s.phy_adr = phy_id; smi_cmd.s.reg_adr = regnum; - cvmx_write_csr(CVMX_SMIX_CMD(p->unit), smi_cmd.u64); + cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64); do { /* * Wait 1000 clocks so we don't saturate the RSL bus * doing reads. */ - cvmx_wait(1000); - smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(p->unit)); + __delay(1000); + smi_wr.u64 = cvmx_read_csr(p->register_base + SMI_WR_DAT); } while (smi_wr.s.pending && --timeout); if (timeout <= 0) @@ -88,38 +99,44 @@ static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id, static int __devinit octeon_mdiobus_probe(struct platform_device *pdev) { struct octeon_mdiobus *bus; + struct resource *res_mem; union cvmx_smix_en smi_en; - int i; int err = -ENOENT; bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); if (!bus) return -ENOMEM; - /* The platform_device id is our unit number. */ - bus->unit = pdev->id; + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (res_mem == NULL) { + dev_err(&pdev->dev, "found no memory resource\n"); + err = -ENXIO; + goto fail; + } + bus->mdio_phys = res_mem->start; + bus->regsize = resource_size(res_mem); + if (!devm_request_mem_region(&pdev->dev, bus->mdio_phys, bus->regsize, + res_mem->name)) { + dev_err(&pdev->dev, "request_mem_region failed\n"); + goto fail; + } + bus->register_base = + (u64)devm_ioremap(&pdev->dev, bus->mdio_phys, bus->regsize); bus->mii_bus = mdiobus_alloc(); if (!bus->mii_bus) - goto err; + goto fail; smi_en.u64 = 0; smi_en.s.en = 1; - cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64); - - /* - * Standard Octeon evaluation boards don't support phy - * interrupts, we need to poll. - */ - for (i = 0; i < PHY_MAX_ADDR; i++) - bus->phy_irq[i] = PHY_POLL; + cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64); bus->mii_bus->priv = bus; bus->mii_bus->irq = bus->phy_irq; bus->mii_bus->name = "mdio-octeon"; - snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", - bus->mii_bus->name, bus->unit); + snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", bus->register_base); bus->mii_bus->parent = &pdev->dev; bus->mii_bus->read = octeon_mdiobus_read; @@ -127,20 +144,18 @@ static int __devinit octeon_mdiobus_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, bus); - err = mdiobus_register(bus->mii_bus); + err = of_mdiobus_register(bus->mii_bus, pdev->dev.of_node); if (err) - goto err_register; + goto fail_register; dev_info(&pdev->dev, "Version " DRV_VERSION "\n"); return 0; -err_register: +fail_register: mdiobus_free(bus->mii_bus); - -err: - devm_kfree(&pdev->dev, bus); +fail: smi_en.u64 = 0; - cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64); + cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64); return err; } @@ -154,14 +169,23 @@ static int __devexit octeon_mdiobus_remove(struct platform_device *pdev) mdiobus_unregister(bus->mii_bus); mdiobus_free(bus->mii_bus); smi_en.u64 = 0; - cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64); + cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64); return 0; } +static struct of_device_id octeon_mdiobus_match[] = { + { + .compatible = "cavium,octeon-3860-mdio", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, octeon_mdiobus_match); + static struct platform_driver octeon_mdiobus_driver = { .driver = { .name = "mdio-octeon", .owner = THIS_MODULE, + .of_match_table = octeon_mdiobus_match, }, .probe = octeon_mdiobus_probe, .remove = __devexit_p(octeon_mdiobus_remove), diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c8f40c9c0428..3782e1cd3697 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -95,6 +95,7 @@ MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026"); enum acer_wmi_event_ids { WMID_HOTKEY_EVENT = 0x1, + WMID_ACCEL_EVENT = 0x5, }; static const struct key_entry acer_wmi_keymap[] = { @@ -130,6 +131,7 @@ static const struct key_entry acer_wmi_keymap[] = { }; static struct input_dev *acer_wmi_input_dev; +static struct input_dev *acer_wmi_accel_dev; struct event_return_value { u8 function; @@ -200,6 +202,7 @@ struct hotkey_function_type_aa { #define ACER_CAP_BLUETOOTH (1<<2) #define ACER_CAP_BRIGHTNESS (1<<3) #define ACER_CAP_THREEG (1<<4) +#define ACER_CAP_ACCEL (1<<5) #define ACER_CAP_ANY (0xFFFFFFFF) /* @@ -1399,6 +1402,60 @@ static void acer_backlight_exit(void) } /* + * Accelerometer device + */ +static acpi_handle gsensor_handle; + +static int acer_gsensor_init(void) +{ + acpi_status status; + struct acpi_buffer output; + union acpi_object out_obj; + + output.length = sizeof(out_obj); + output.pointer = &out_obj; + status = acpi_evaluate_object(gsensor_handle, "_INI", NULL, &output); + if (ACPI_FAILURE(status)) + return -1; + + return 0; +} + +static int acer_gsensor_open(struct input_dev *input) +{ + return acer_gsensor_init(); +} + +static int acer_gsensor_event(void) +{ + acpi_status status; + struct acpi_buffer output; + union acpi_object out_obj[5]; + + if (!has_cap(ACER_CAP_ACCEL)) + return -1; + + output.length = sizeof(out_obj); + output.pointer = out_obj; + + status = acpi_evaluate_object(gsensor_handle, "RDVL", NULL, &output); + if (ACPI_FAILURE(status)) + return -1; + + if (out_obj->package.count != 4) + return -1; + + input_report_abs(acer_wmi_accel_dev, ABS_X, + (s16)out_obj->package.elements[0].integer.value); + input_report_abs(acer_wmi_accel_dev, ABS_Y, + (s16)out_obj->package.elements[1].integer.value); + input_report_abs(acer_wmi_accel_dev, ABS_Z, + (s16)out_obj->package.elements[2].integer.value); + input_sync(acer_wmi_accel_dev); + return 0; +} + +/* * Rfkill devices */ static void acer_rfkill_update(struct work_struct *ignored); @@ -1673,6 +1730,9 @@ static void acer_wmi_notify(u32 value, void *context) 1, true); } break; + case WMID_ACCEL_EVENT: + acer_gsensor_event(); + break; default: pr_warn("Unknown function number - %d - %d\n", return_value.function, return_value.key_num); @@ -1758,6 +1818,73 @@ static int acer_wmi_enable_lm(void) return status; } +static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level, + void *ctx, void **retval) +{ + *(acpi_handle *)retval = ah; + return AE_OK; +} + +static int __init acer_wmi_get_handle(const char *name, const char *prop, + acpi_handle *ah) +{ + acpi_status status; + acpi_handle handle; + + BUG_ON(!name || !ah); + + handle = NULL; + status = acpi_get_devices(prop, acer_wmi_get_handle_cb, + (void *)name, &handle); + + if (ACPI_SUCCESS(status)) { + *ah = handle; + return 0; + } else { + return -ENODEV; + } +} + +static int __init acer_wmi_accel_setup(void) +{ + int err; + + err = acer_wmi_get_handle("SENR", "BST0001", &gsensor_handle); + if (err) + return err; + + interface->capability |= ACER_CAP_ACCEL; + + acer_wmi_accel_dev = input_allocate_device(); + if (!acer_wmi_accel_dev) + return -ENOMEM; + + acer_wmi_accel_dev->open = acer_gsensor_open; + + acer_wmi_accel_dev->name = "Acer BMA150 accelerometer"; + acer_wmi_accel_dev->phys = "wmi/input1"; + acer_wmi_accel_dev->id.bustype = BUS_HOST; + acer_wmi_accel_dev->evbit[0] = BIT_MASK(EV_ABS); + input_set_abs_params(acer_wmi_accel_dev, ABS_X, -16384, 16384, 0, 0); + input_set_abs_params(acer_wmi_accel_dev, ABS_Y, -16384, 16384, 0, 0); + input_set_abs_params(acer_wmi_accel_dev, ABS_Z, -16384, 16384, 0, 0); + + err = input_register_device(acer_wmi_accel_dev); + if (err) + goto err_free_dev; + + return 0; + +err_free_dev: + input_free_device(acer_wmi_accel_dev); + return err; +} + +static void acer_wmi_accel_destroy(void) +{ + input_unregister_device(acer_wmi_accel_dev); +} + static int __init acer_wmi_input_setup(void) { acpi_status status; @@ -1912,6 +2039,9 @@ static int acer_resume(struct device *dev) if (has_cap(ACER_CAP_BRIGHTNESS)) set_u32(data->brightness, ACER_CAP_BRIGHTNESS); + if (has_cap(ACER_CAP_ACCEL)) + acer_gsensor_init(); + return 0; } @@ -2060,14 +2190,16 @@ static int __init acer_wmi_init(void) set_quirks(); + if (dmi_check_system(video_vendor_dmi_table)) + acpi_video_dmi_promote_vendor(); if (acpi_video_backlight_support()) { - if (dmi_check_system(video_vendor_dmi_table)) { - acpi_video_unregister(); - } else { - interface->capability &= ~ACER_CAP_BRIGHTNESS; - pr_info("Brightness must be controlled by " - "acpi video driver\n"); - } + interface->capability &= ~ACER_CAP_BRIGHTNESS; + pr_info("Brightness must be controlled by acpi video driver\n"); + } else { +#ifdef CONFIG_ACPI_VIDEO + pr_info("Disabling ACPI video driver\n"); + acpi_video_unregister(); +#endif } if (wmi_has_guid(WMID_GUID3)) { @@ -2090,6 +2222,8 @@ static int __init acer_wmi_init(void) return err; } + acer_wmi_accel_setup(); + err = platform_driver_register(&acer_platform_driver); if (err) { pr_err("Unable to register platform driver\n"); @@ -2133,6 +2267,8 @@ error_device_alloc: error_platform_register: if (wmi_has_guid(ACERWMID_EVENT_GUID)) acer_wmi_input_destroy(); + if (has_cap(ACER_CAP_ACCEL)) + acer_wmi_accel_destroy(); return err; } @@ -2142,6 +2278,9 @@ static void __exit acer_wmi_exit(void) if (wmi_has_guid(ACERWMID_EVENT_GUID)) acer_wmi_input_destroy(); + if (has_cap(ACER_CAP_ACCEL)) + acer_wmi_accel_destroy(); + remove_sysfs(acer_platform_device); remove_debugfs(); platform_device_unregister(acer_platform_device); diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 694a15a56230..905fa01ac8df 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -193,7 +193,10 @@ static int __devinit gmux_probe(struct pnp_dev *pnp, * backlight control and supports more levels than other options. * Disable the other backlight choices. */ + acpi_video_dmi_promote_vendor(); +#ifdef CONFIG_ACPI_VIDEO acpi_video_unregister(); +#endif apple_bl_unregister(); return 0; @@ -213,7 +216,10 @@ static void __devexit gmux_remove(struct pnp_dev *pnp) release_region(gmux_data->iostart, gmux_data->iolen); kfree(gmux_data); + acpi_video_dmi_demote_vendor(); +#ifdef CONFIG_ACPI_VIDEO acpi_video_register(); +#endif apple_bl_register(); } diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 99a30b513137..6b0ebdeae916 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -26,6 +26,7 @@ #include <linux/input.h> #include <linux/input/sparse-keymap.h> #include <linux/fb.h> +#include <linux/dmi.h> #include "asus-wmi.h" @@ -48,18 +49,115 @@ MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID); * 1 | Hardware | Software * 4 | Software | Software */ -static uint wapf; +static int wapf = -1; module_param(wapf, uint, 0444); MODULE_PARM_DESC(wapf, "WAPF value"); +static struct quirk_entry *quirks; + static struct quirk_entry quirk_asus_unknown = { + .wapf = 0, +}; + +static struct quirk_entry quirk_asus_x401u = { + .wapf = 4, +}; + +static int dmi_matched(const struct dmi_system_id *dmi) +{ + quirks = dmi->driver_data; + return 1; +} + +static struct dmi_system_id asus_quirks[] = { + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X401U", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X401U"), + }, + .driver_data = &quirk_asus_x401u, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X401A1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X401A1"), + }, + .driver_data = &quirk_asus_x401u, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X501U", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X501U"), + }, + .driver_data = &quirk_asus_x401u, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X501A1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X501A1"), + }, + .driver_data = &quirk_asus_x401u, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X55A", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X55A"), + }, + .driver_data = &quirk_asus_x401u, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X55C", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X55C"), + }, + .driver_data = &quirk_asus_x401u, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X55U", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X55U"), + }, + .driver_data = &quirk_asus_x401u, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X55VD", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X55VD"), + }, + .driver_data = &quirk_asus_x401u, + }, + {}, }; static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) { - driver->quirks = &quirk_asus_unknown; - driver->quirks->wapf = wapf; + quirks = &quirk_asus_unknown; + dmi_check_system(asus_quirks); + + driver->quirks = quirks; driver->panel_power = FB_BLANK_UNBLANK; + + /* overwrite the wapf setting if the wapf paramater is specified */ + if (wapf != -1) + quirks->wapf = wapf; + else + wapf = quirks->wapf; } static const struct key_entry asus_nb_wmi_keymap[] = { @@ -94,6 +192,10 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0x8A, { KEY_PROG1 } }, { KE_KEY, 0x95, { KEY_MEDIA } }, { KE_KEY, 0x99, { KEY_PHONE } }, + { KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */ + { KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */ + { KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */ + { KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */ { KE_KEY, 0xb5, { KEY_CALC } }, { KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, { KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 77aadde5281c..c7a36f6b0580 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -47,6 +47,9 @@ #include <linux/thermal.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#ifdef CONFIG_ACPI_VIDEO +#include <acpi/video.h> +#endif #include "asus-wmi.h" @@ -136,6 +139,9 @@ MODULE_LICENSE("GPL"); /* Power */ #define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012 +/* Deep S3 / Resume on LID open */ +#define ASUS_WMI_DEVID_LID_RESUME 0x00120031 + /* DSTS masks */ #define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 #define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 @@ -1365,6 +1371,7 @@ static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf) ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD); ASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA); ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER); +ASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME); static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1390,6 +1397,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_camera.attr, &dev_attr_cardr.attr, &dev_attr_touchpad.attr, + &dev_attr_lid_resume.attr, NULL }; @@ -1408,6 +1416,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_CARDREADER; else if (attr == &dev_attr_touchpad.attr) devid = ASUS_WMI_DEVID_TOUCHPAD; + else if (attr == &dev_attr_lid_resume.attr) + devid = ASUS_WMI_DEVID_LID_RESUME; if (devid != -1) ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); @@ -1467,14 +1477,9 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) */ if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL)) asus->dsts_id = ASUS_WMI_METHODID_DSTS; - else if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS2, 0, 0, NULL)) + else asus->dsts_id = ASUS_WMI_METHODID_DSTS2; - if (!asus->dsts_id) { - pr_err("Can't find DSTS"); - return -ENODEV; - } - /* CWAP allow to define the behavior of the Fn+F2 key, * this method doesn't seems to be present on Eee PCs */ if (asus->driver->quirks->wapf >= 0) @@ -1681,7 +1686,13 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_rfkill; + if (asus->driver->quirks->wmi_backlight_power) + acpi_video_dmi_promote_vendor(); if (!acpi_video_backlight_support()) { +#ifdef CONFIG_ACPI_VIDEO + pr_info("Disabling ACPI video driver\n"); + acpi_video_unregister(); +#endif err = asus_wmi_backlight_init(asus); if (err && err != -ENODEV) goto fail_backlight; diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index d43b66742004..9c1da8b81bea 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -39,6 +39,7 @@ struct quirk_entry { bool hotplug_wireless; bool scalar_panel_brightness; bool store_backlight_power; + bool wmi_backlight_power; int wapf; }; diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index e2230a2b2f8e..2ca7dd1ab3e4 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -31,15 +31,21 @@ MODULE_LICENSE("GPL"); struct cmpc_accel { int sensitivity; + int g_select; + int inputdev_state; }; -#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 +#define CMPC_ACCEL_DEV_STATE_CLOSED 0 +#define CMPC_ACCEL_DEV_STATE_OPEN 1 +#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 +#define CMPC_ACCEL_G_SELECT_DEFAULT 0 #define CMPC_ACCEL_HID "ACCE0000" +#define CMPC_ACCEL_HID_V4 "ACCE0001" #define CMPC_TABLET_HID "TBLT0000" #define CMPC_IPML_HID "IPML200" -#define CMPC_KEYS_HID "FnBT0000" +#define CMPC_KEYS_HID "FNBT0000" /* * Generic input device code. @@ -76,7 +82,391 @@ static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi) } /* - * Accelerometer code. + * Accelerometer code for Classmate V4 + */ +static acpi_status cmpc_start_accel_v4(acpi_handle handle) +{ + union acpi_object param[4]; + struct acpi_object_list input; + acpi_status status; + + param[0].type = ACPI_TYPE_INTEGER; + param[0].integer.value = 0x3; + param[1].type = ACPI_TYPE_INTEGER; + param[1].integer.value = 0; + param[2].type = ACPI_TYPE_INTEGER; + param[2].integer.value = 0; + param[3].type = ACPI_TYPE_INTEGER; + param[3].integer.value = 0; + input.count = 4; + input.pointer = param; + status = acpi_evaluate_object(handle, "ACMD", &input, NULL); + return status; +} + +static acpi_status cmpc_stop_accel_v4(acpi_handle handle) +{ + union acpi_object param[4]; + struct acpi_object_list input; + acpi_status status; + + param[0].type = ACPI_TYPE_INTEGER; + param[0].integer.value = 0x4; + param[1].type = ACPI_TYPE_INTEGER; + param[1].integer.value = 0; + param[2].type = ACPI_TYPE_INTEGER; + param[2].integer.value = 0; + param[3].type = ACPI_TYPE_INTEGER; + param[3].integer.value = 0; + input.count = 4; + input.pointer = param; + status = acpi_evaluate_object(handle, "ACMD", &input, NULL); + return status; +} + +static acpi_status cmpc_accel_set_sensitivity_v4(acpi_handle handle, int val) +{ + union acpi_object param[4]; + struct acpi_object_list input; + + param[0].type = ACPI_TYPE_INTEGER; + param[0].integer.value = 0x02; + param[1].type = ACPI_TYPE_INTEGER; + param[1].integer.value = val; + param[2].type = ACPI_TYPE_INTEGER; + param[2].integer.value = 0; + param[3].type = ACPI_TYPE_INTEGER; + param[3].integer.value = 0; + input.count = 4; + input.pointer = param; + return acpi_evaluate_object(handle, "ACMD", &input, NULL); +} + +static acpi_status cmpc_accel_set_g_select_v4(acpi_handle handle, int val) +{ + union acpi_object param[4]; + struct acpi_object_list input; + + param[0].type = ACPI_TYPE_INTEGER; + param[0].integer.value = 0x05; + param[1].type = ACPI_TYPE_INTEGER; + param[1].integer.value = val; + param[2].type = ACPI_TYPE_INTEGER; + param[2].integer.value = 0; + param[3].type = ACPI_TYPE_INTEGER; + param[3].integer.value = 0; + input.count = 4; + input.pointer = param; + return acpi_evaluate_object(handle, "ACMD", &input, NULL); +} + +static acpi_status cmpc_get_accel_v4(acpi_handle handle, + int16_t *x, + int16_t *y, + int16_t *z) +{ + union acpi_object param[4]; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + int16_t *locs; + acpi_status status; + + param[0].type = ACPI_TYPE_INTEGER; + param[0].integer.value = 0x01; + param[1].type = ACPI_TYPE_INTEGER; + param[1].integer.value = 0; + param[2].type = ACPI_TYPE_INTEGER; + param[2].integer.value = 0; + param[3].type = ACPI_TYPE_INTEGER; + param[3].integer.value = 0; + input.count = 4; + input.pointer = param; + status = acpi_evaluate_object(handle, "ACMD", &input, &output); + if (ACPI_SUCCESS(status)) { + union acpi_object *obj; + obj = output.pointer; + locs = (int16_t *) obj->buffer.pointer; + *x = locs[0]; + *y = locs[1]; + *z = locs[2]; + kfree(output.pointer); + } + return status; +} + +static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event) +{ + if (event == 0x81) { + int16_t x, y, z; + acpi_status status; + + status = cmpc_get_accel_v4(dev->handle, &x, &y, &z); + if (ACPI_SUCCESS(status)) { + struct input_dev *inputdev = dev_get_drvdata(&dev->dev); + + input_report_abs(inputdev, ABS_X, x); + input_report_abs(inputdev, ABS_Y, y); + input_report_abs(inputdev, ABS_Z, z); + input_sync(inputdev); + } + } +} + +static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi; + struct input_dev *inputdev; + struct cmpc_accel *accel; + + acpi = to_acpi_device(dev); + inputdev = dev_get_drvdata(&acpi->dev); + accel = dev_get_drvdata(&inputdev->dev); + + return sprintf(buf, "%d\n", accel->sensitivity); +} + +static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_device *acpi; + struct input_dev *inputdev; + struct cmpc_accel *accel; + unsigned long sensitivity; + int r; + + acpi = to_acpi_device(dev); + inputdev = dev_get_drvdata(&acpi->dev); + accel = dev_get_drvdata(&inputdev->dev); + + r = kstrtoul(buf, 0, &sensitivity); + if (r) + return r; + + /* sensitivity must be between 1 and 127 */ + if (sensitivity < 1 || sensitivity > 127) + return -EINVAL; + + accel->sensitivity = sensitivity; + cmpc_accel_set_sensitivity_v4(acpi->handle, sensitivity); + + return strnlen(buf, count); +} + +static struct device_attribute cmpc_accel_sensitivity_attr_v4 = { + .attr = { .name = "sensitivity", .mode = 0660 }, + .show = cmpc_accel_sensitivity_show_v4, + .store = cmpc_accel_sensitivity_store_v4 +}; + +static ssize_t cmpc_accel_g_select_show_v4(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi; + struct input_dev *inputdev; + struct cmpc_accel *accel; + + acpi = to_acpi_device(dev); + inputdev = dev_get_drvdata(&acpi->dev); + accel = dev_get_drvdata(&inputdev->dev); + + return sprintf(buf, "%d\n", accel->g_select); +} + +static ssize_t cmpc_accel_g_select_store_v4(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_device *acpi; + struct input_dev *inputdev; + struct cmpc_accel *accel; + unsigned long g_select; + int r; + + acpi = to_acpi_device(dev); + inputdev = dev_get_drvdata(&acpi->dev); + accel = dev_get_drvdata(&inputdev->dev); + + r = kstrtoul(buf, 0, &g_select); + if (r) + return r; + + /* 0 means 1.5g, 1 means 6g, everything else is wrong */ + if (g_select != 0 && g_select != 1) + return -EINVAL; + + accel->g_select = g_select; + cmpc_accel_set_g_select_v4(acpi->handle, g_select); + + return strnlen(buf, count); +} + +static struct device_attribute cmpc_accel_g_select_attr_v4 = { + .attr = { .name = "g_select", .mode = 0660 }, + .show = cmpc_accel_g_select_show_v4, + .store = cmpc_accel_g_select_store_v4 +}; + +static int cmpc_accel_open_v4(struct input_dev *input) +{ + struct acpi_device *acpi; + struct cmpc_accel *accel; + + acpi = to_acpi_device(input->dev.parent); + accel = dev_get_drvdata(&input->dev); + + cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); + cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); + + if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) { + accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN; + return 0; + } + return -EIO; +} + +static void cmpc_accel_close_v4(struct input_dev *input) +{ + struct acpi_device *acpi; + struct cmpc_accel *accel; + + acpi = to_acpi_device(input->dev.parent); + accel = dev_get_drvdata(&input->dev); + + cmpc_stop_accel_v4(acpi->handle); + accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; +} + +static void cmpc_accel_idev_init_v4(struct input_dev *inputdev) +{ + set_bit(EV_ABS, inputdev->evbit); + input_set_abs_params(inputdev, ABS_X, -255, 255, 16, 0); + input_set_abs_params(inputdev, ABS_Y, -255, 255, 16, 0); + input_set_abs_params(inputdev, ABS_Z, -255, 255, 16, 0); + inputdev->open = cmpc_accel_open_v4; + inputdev->close = cmpc_accel_close_v4; +} + +static int cmpc_accel_suspend_v4(struct device *dev) +{ + struct input_dev *inputdev; + struct cmpc_accel *accel; + + inputdev = dev_get_drvdata(dev); + accel = dev_get_drvdata(&inputdev->dev); + + if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) + return cmpc_stop_accel_v4(to_acpi_device(dev)->handle); + + return 0; +} + +static int cmpc_accel_resume_v4(struct device *dev) +{ + struct input_dev *inputdev; + struct cmpc_accel *accel; + + inputdev = dev_get_drvdata(dev); + accel = dev_get_drvdata(&inputdev->dev); + + if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) { + cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle, + accel->sensitivity); + cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle, + accel->g_select); + + if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle))) + return -EIO; + } + + return 0; +} + +static int cmpc_accel_add_v4(struct acpi_device *acpi) +{ + int error; + struct input_dev *inputdev; + struct cmpc_accel *accel; + + accel = kmalloc(sizeof(*accel), GFP_KERNEL); + if (!accel) + return -ENOMEM; + + accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; + + accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; + cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); + + error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); + if (error) + goto failed_sensitivity; + + accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT; + cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); + + error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); + if (error) + goto failed_g_select; + + error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4", + cmpc_accel_idev_init_v4); + if (error) + goto failed_input; + + inputdev = dev_get_drvdata(&acpi->dev); + dev_set_drvdata(&inputdev->dev, accel); + + return 0; + +failed_input: + device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); +failed_g_select: + device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); +failed_sensitivity: + kfree(accel); + return error; +} + +static int cmpc_accel_remove_v4(struct acpi_device *acpi, int type) +{ + struct input_dev *inputdev; + struct cmpc_accel *accel; + + inputdev = dev_get_drvdata(&acpi->dev); + accel = dev_get_drvdata(&inputdev->dev); + + device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); + device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); + return cmpc_remove_acpi_notify_device(acpi); +} + +static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4, + cmpc_accel_resume_v4); + +static const struct acpi_device_id cmpc_accel_device_ids_v4[] = { + {CMPC_ACCEL_HID_V4, 0}, + {"", 0} +}; + +static struct acpi_driver cmpc_accel_acpi_driver_v4 = { + .owner = THIS_MODULE, + .name = "cmpc_accel_v4", + .class = "cmpc_accel_v4", + .ids = cmpc_accel_device_ids_v4, + .ops = { + .add = cmpc_accel_add_v4, + .remove = cmpc_accel_remove_v4, + .notify = cmpc_accel_handler_v4, + }, + .drv.pm = &cmpc_accel_pm, +}; + + +/* + * Accelerometer code for Classmate versions prior to V4 */ static acpi_status cmpc_start_accel(acpi_handle handle) { @@ -726,8 +1116,15 @@ static int cmpc_init(void) if (r) goto failed_accel; + r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4); + if (r) + goto failed_accel_v4; + return r; +failed_accel_v4: + acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); + failed_accel: acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); @@ -743,6 +1140,7 @@ failed_keys: static void cmpc_exit(void) { + acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4); acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); @@ -754,6 +1152,7 @@ module_exit(cmpc_exit); static const struct acpi_device_id cmpc_device_ids[] = { {CMPC_ACCEL_HID, 0}, + {CMPC_ACCEL_HID_V4, 0}, {CMPC_TABLET_HID, 0}, {CMPC_IPML_HID, 0}, {CMPC_KEYS_HID, 0}, diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 5f78aac9b163..4e96e8c0b60f 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -206,6 +206,60 @@ static struct dmi_system_id __devinitdata dell_quirks[] = { }, .driver_data = &quirk_dell_vostro_v130, }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron 5420", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5420"), + }, + .driver_data = &quirk_dell_vostro_v130, + }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron 5520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5520"), + }, + .driver_data = &quirk_dell_vostro_v130, + }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron 5720", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5720"), + }, + .driver_data = &quirk_dell_vostro_v130, + }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron 7420", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7420"), + }, + .driver_data = &quirk_dell_vostro_v130, + }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron 7520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7520"), + }, + .driver_data = &quirk_dell_vostro_v130, + }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron 7720", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7720"), + }, + .driver_data = &quirk_dell_vostro_v130, + }, { } }; diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 656761380342..5838332ea5bd 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -79,7 +79,7 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */ { KE_KEY, HOME_PRESS, { KEY_CONFIG } }, /* Home/Express gate key */ { KE_KEY, 0xe8, { KEY_SCREENLOCK } }, - { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } }, + { KE_KEY, 0xe9, { KEY_DISPLAYTOGGLE } }, { KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } }, { KE_KEY, 0xec, { KEY_CAMERA_UP } }, { KE_KEY, 0xed, { KEY_CAMERA_DOWN } }, @@ -107,6 +107,11 @@ static struct quirk_entry quirk_asus_et2012_type3 = { .store_backlight_power = true, }; +static struct quirk_entry quirk_asus_x101ch = { + /* We need this when ACPI function doesn't do this well */ + .wmi_backlight_power = true, +}; + static struct quirk_entry *quirks; static void et2012_quirks(void) @@ -157,6 +162,24 @@ static struct dmi_system_id asus_quirks[] = { }, .driver_data = &quirk_asus_unknown, }, + { + .callback = dmi_matched, + .ident = "ASUSTeK Computer INC. X101CH", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X101CH"), + }, + .driver_data = &quirk_asus_x101ch, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK Computer INC. 1015CX", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "1015CX"), + }, + .driver_data = &quirk_asus_x101ch, + }, {}, }; diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index e2a34b42ddc1..c1ca7bcebb66 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -26,7 +26,7 @@ #include <linux/seq_file.h> #include <linux/debugfs.h> #include <linux/ctype.h> -#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE) +#ifdef CONFIG_ACPI_VIDEO #include <acpi/video.h> #endif @@ -1465,6 +1465,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ }, }, + /* DMI ids for laptops with bad Chassis Type */ + { + .ident = "R40/R41", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "R40/R41"), + DMI_MATCH(DMI_BOARD_NAME, "R40/R41"), + }, + }, /* Specific DMI ids for laptop with quirks */ { .callback = samsung_dmi_matched, @@ -1506,6 +1515,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { }, .driver_data = &samsung_broken_acpi_video, }, + { + .callback = samsung_dmi_matched, + .ident = "X360", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "X360"), + DMI_MATCH(DMI_BOARD_NAME, "X360"), + }, + .driver_data = &samsung_broken_acpi_video, + }, { }, }; MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); @@ -1530,15 +1549,18 @@ static int __init samsung_init(void) samsung->quirks = quirks; -#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE) +#ifdef CONFIG_ACPI + if (samsung->quirks->broken_acpi_video) + acpi_video_dmi_promote_vendor(); + /* Don't handle backlight here if the acpi video already handle it */ if (acpi_video_backlight_support()) { - if (samsung->quirks->broken_acpi_video) { - pr_info("Disabling ACPI video driver\n"); - acpi_video_unregister(); - } else { - samsung->handle_backlight = false; - } + samsung->handle_backlight = false; + } else if (samsung->quirks->broken_acpi_video) { + pr_info("Disabling ACPI video driver\n"); +#ifdef CONFIG_ACPI_VIDEO + acpi_video_unregister(); +#endif } #endif @@ -1552,8 +1574,7 @@ static int __init samsung_init(void) #ifdef CONFIG_ACPI /* Only log that if we are really on a sabi platform */ - if (acpi_video_backlight_support() && - !samsung->quirks->broken_acpi_video) + if (acpi_video_backlight_support()) pr_info("Backlight controlled by ACPI video driver\n"); #endif diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d5fd4a1193f8..e7f73287636c 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3015,8 +3015,6 @@ static void hotkey_exit(void) if (hotkey_dev_attributes) delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); - kfree(hotkey_keycode_map); - dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY, "restoring original HKEY status and mask\n"); /* yes, there is a bitwise or below, we want the @@ -5217,6 +5215,7 @@ static void led_exit(void) led_classdev_unregister(&tpacpi_leds[i].led_classdev); } + flush_workqueue(tpacpi_wq); kfree(tpacpi_leds); } @@ -8936,6 +8935,7 @@ static void thinkpad_acpi_module_exit(void) input_unregister_device(tpacpi_inputdev); else input_free_device(tpacpi_inputdev); + kfree(hotkey_keycode_map); } if (tpacpi_hwmon) @@ -8969,6 +8969,7 @@ static void thinkpad_acpi_module_exit(void) kfree(thinkpad_id.bios_version_str); kfree(thinkpad_id.ec_version_str); kfree(thinkpad_id.model_str); + kfree(thinkpad_id.nummodel_str); } diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index aa764ecc4e60..c1892f321c46 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -268,6 +268,7 @@ config CHARGER_GPIO config CHARGER_MANAGER bool "Battery charger manager for multiple chargers" depends on REGULATOR && RTC_CLASS + select EXTCON help Say Y to enable charger-manager support, which allows multiple chargers attached to a battery and multiple batteries attached to a diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index f5d6d379f2fb..181ddece5181 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -22,6 +22,7 @@ * Datasheets: * http://focus.ti.com/docs/prod/folders/print/bq27000.html * http://focus.ti.com/docs/prod/folders/print/bq27500.html + * http://www.ti.com/product/bq27425-g1 */ #include <linux/module.h> @@ -51,6 +52,7 @@ #define BQ27x00_REG_LMD 0x12 /* Last measured discharge */ #define BQ27x00_REG_CYCT 0x2A /* Cycle count total */ #define BQ27x00_REG_AE 0x22 /* Available energy */ +#define BQ27x00_POWER_AVG 0x24 #define BQ27000_REG_RSOC 0x0B /* Relative State-of-Charge */ #define BQ27000_REG_ILMD 0x76 /* Initial last measured discharge */ @@ -66,15 +68,21 @@ #define BQ27500_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */ #define BQ27500_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */ #define BQ27500_FLAG_FC BIT(9) +#define BQ27500_FLAG_OTC BIT(15) + +/* bq27425 register addresses are same as bq27x00 addresses minus 4 */ +#define BQ27425_REG_OFFSET 0x04 +#define BQ27425_REG_SOC 0x18 /* Register address plus offset */ #define BQ27000_RS 20 /* Resistor sense */ +#define BQ27x00_POWER_CONSTANT (256 * 29200 / 1000) struct bq27x00_device_info; struct bq27x00_access_methods { int (*read)(struct bq27x00_device_info *di, u8 reg, bool single); }; -enum bq27x00_chip { BQ27000, BQ27500 }; +enum bq27x00_chip { BQ27000, BQ27500, BQ27425}; struct bq27x00_reg_cache { int temperature; @@ -86,6 +94,8 @@ struct bq27x00_reg_cache { int capacity; int energy; int flags; + int power_avg; + int health; }; struct bq27x00_device_info { @@ -123,6 +133,22 @@ static enum power_supply_property bq27x00_battery_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_POWER_AVG, + POWER_SUPPLY_PROP_HEALTH, +}; + +static enum power_supply_property bq27425_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, }; static unsigned int poll_interval = 360; @@ -137,10 +163,24 @@ MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \ static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg, bool single) { + if (di->chip == BQ27425) + return di->bus.read(di, reg - BQ27425_REG_OFFSET, single); return di->bus.read(di, reg, single); } /* + * Higher versions of the chip like BQ27425 and BQ27500 + * differ from BQ27000 and BQ27200 in calculation of certain + * parameters. Hence we need to check for the chip type. + */ +static bool bq27xxx_is_chip_version_higher(struct bq27x00_device_info *di) +{ + if (di->chip == BQ27425 || di->chip == BQ27500) + return true; + return false; +} + +/* * Return the battery Relative State-of-Charge * Or < 0 if something fails. */ @@ -150,6 +190,8 @@ static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di) if (di->chip == BQ27500) rsoc = bq27x00_read(di, BQ27500_REG_SOC, false); + else if (di->chip == BQ27425) + rsoc = bq27x00_read(di, BQ27425_REG_SOC, false); else rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true); @@ -174,7 +216,7 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg) return charge; } - if (di->chip == BQ27500) + if (bq27xxx_is_chip_version_higher(di)) charge *= 1000; else charge = charge * 3570 / BQ27000_RS; @@ -208,7 +250,7 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) { int ilmd; - if (di->chip == BQ27500) + if (bq27xxx_is_chip_version_higher(di)) ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); else ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true); @@ -218,7 +260,7 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) return ilmd; } - if (di->chip == BQ27500) + if (bq27xxx_is_chip_version_higher(di)) ilmd *= 1000; else ilmd = ilmd * 256 * 3570 / BQ27000_RS; @@ -262,7 +304,7 @@ static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di) return temp; } - if (di->chip == BQ27500) + if (bq27xxx_is_chip_version_higher(di)) temp -= 2731; else temp = ((temp * 5) - 5463) / 2; @@ -306,14 +348,70 @@ static int bq27x00_battery_read_time(struct bq27x00_device_info *di, u8 reg) return tval * 60; } +/* + * Read a power avg register. + * Return < 0 if something fails. + */ +static int bq27x00_battery_read_pwr_avg(struct bq27x00_device_info *di, u8 reg) +{ + int tval; + + tval = bq27x00_read(di, reg, false); + if (tval < 0) { + dev_err(di->dev, "error reading power avg rgister %02x: %d\n", + reg, tval); + return tval; + } + + if (di->chip == BQ27500) + return tval; + else + return (tval * BQ27x00_POWER_CONSTANT) / BQ27000_RS; +} + +/* + * Read flag register. + * Return < 0 if something fails. + */ +static int bq27x00_battery_read_health(struct bq27x00_device_info *di) +{ + int tval; + + tval = bq27x00_read(di, BQ27x00_REG_FLAGS, false); + if (tval < 0) { + dev_err(di->dev, "error reading flag register:%d\n", tval); + return tval; + } + + if ((di->chip == BQ27500)) { + if (tval & BQ27500_FLAG_SOCF) + tval = POWER_SUPPLY_HEALTH_DEAD; + else if (tval & BQ27500_FLAG_OTC) + tval = POWER_SUPPLY_HEALTH_OVERHEAT; + else + tval = POWER_SUPPLY_HEALTH_GOOD; + return tval; + } else { + if (tval & BQ27000_FLAG_EDV1) + tval = POWER_SUPPLY_HEALTH_DEAD; + else + tval = POWER_SUPPLY_HEALTH_GOOD; + return tval; + } + + return -1; +} + static void bq27x00_update(struct bq27x00_device_info *di) { struct bq27x00_reg_cache cache = {0, }; bool is_bq27500 = di->chip == BQ27500; + bool is_bq27425 = di->chip == BQ27425; cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500); if (cache.flags >= 0) { - if (!is_bq27500 && (cache.flags & BQ27000_FLAG_CI)) { + if (!is_bq27500 && !is_bq27425 + && (cache.flags & BQ27000_FLAG_CI)) { dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n"); cache.capacity = -ENODATA; cache.energy = -ENODATA; @@ -321,16 +419,30 @@ static void bq27x00_update(struct bq27x00_device_info *di) cache.time_to_empty_avg = -ENODATA; cache.time_to_full = -ENODATA; cache.charge_full = -ENODATA; + cache.health = -ENODATA; } else { cache.capacity = bq27x00_battery_read_rsoc(di); - cache.energy = bq27x00_battery_read_energy(di); - cache.time_to_empty = bq27x00_battery_read_time(di, BQ27x00_REG_TTE); - cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP); - cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF); + if (!is_bq27425) { + cache.energy = bq27x00_battery_read_energy(di); + cache.time_to_empty = + bq27x00_battery_read_time(di, + BQ27x00_REG_TTE); + cache.time_to_empty_avg = + bq27x00_battery_read_time(di, + BQ27x00_REG_TTECP); + cache.time_to_full = + bq27x00_battery_read_time(di, + BQ27x00_REG_TTF); + } cache.charge_full = bq27x00_battery_read_lmd(di); + cache.health = bq27x00_battery_read_health(di); } cache.temperature = bq27x00_battery_read_temperature(di); + if (!is_bq27425) + cache.cycle_count = bq27x00_battery_read_cyct(di); cache.cycle_count = bq27x00_battery_read_cyct(di); + cache.power_avg = + bq27x00_battery_read_pwr_avg(di, BQ27x00_POWER_AVG); /* We only have to read charge design full once */ if (di->charge_design_full <= 0) @@ -376,7 +488,7 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di, return curr; } - if (di->chip == BQ27500) { + if (bq27xxx_is_chip_version_higher(di)) { /* bq27500 returns signed value */ val->intval = (int)((s16)curr) * 1000; } else { @@ -397,7 +509,7 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di, { int status; - if (di->chip == BQ27500) { + if (bq27xxx_is_chip_version_higher(di)) { if (di->cache.flags & BQ27500_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; else if (di->cache.flags & BQ27500_FLAG_DSC) @@ -425,7 +537,7 @@ static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di, { int level; - if (di->chip == BQ27500) { + if (bq27xxx_is_chip_version_higher(di)) { if (di->cache.flags & BQ27500_FLAG_FC) level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; else if (di->cache.flags & BQ27500_FLAG_SOC1) @@ -550,6 +662,12 @@ static int bq27x00_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_ENERGY_NOW: ret = bq27x00_simple_value(di->cache.energy, val); break; + case POWER_SUPPLY_PROP_POWER_AVG: + ret = bq27x00_simple_value(di->cache.power_avg, val); + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = bq27x00_simple_value(di->cache.health, val); + break; default: return -EINVAL; } @@ -570,8 +688,14 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di) int ret; di->bat.type = POWER_SUPPLY_TYPE_BATTERY; - di->bat.properties = bq27x00_battery_props; - di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props); + di->chip = BQ27425; + if (di->chip == BQ27425) { + di->bat.properties = bq27425_battery_props; + di->bat.num_properties = ARRAY_SIZE(bq27425_battery_props); + } else { + di->bat.properties = bq27x00_battery_props; + di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props); + } di->bat.get_property = bq27x00_battery_get_property; di->bat.external_power_changed = bq27x00_external_power_changed; @@ -729,6 +853,7 @@ static int bq27x00_battery_remove(struct i2c_client *client) static const struct i2c_device_id bq27x00_id[] = { { "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */ { "bq27500", BQ27500 }, + { "bq27425", BQ27425 }, {}, }; MODULE_DEVICE_TABLE(i2c, bq27x00_id); diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 86935ec18954..526e5c931294 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -271,16 +271,13 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) if (enable) { if (cm->emergency_stop) return -EAGAIN; - err = regulator_bulk_enable(desc->num_charger_regulators, - desc->charger_regulators); + for (i = 0 ; i < desc->num_charger_regulators ; i++) + regulator_enable(desc->charger_regulators[i].consumer); } else { /* * Abnormal battery state - Stop charging forcibly, * even if charger was enabled at the other places */ - err = regulator_bulk_disable(desc->num_charger_regulators, - desc->charger_regulators); - for (i = 0; i < desc->num_charger_regulators; i++) { if (regulator_is_enabled( desc->charger_regulators[i].consumer)) { @@ -288,7 +285,7 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) desc->charger_regulators[i].consumer); dev_warn(cm->dev, "Disable regulator(%s) forcibly.\n", - desc->charger_regulators[i].supply); + desc->charger_regulators[i].regulator_name); } } } @@ -994,11 +991,92 @@ int setup_charger_manager(struct charger_global_desc *gd) } EXPORT_SYMBOL_GPL(setup_charger_manager); +/** + * charger_extcon_work - enable/diable charger according to the state + * of charger cable + * + * @work: work_struct of the function charger_extcon_work. + */ +static void charger_extcon_work(struct work_struct *work) +{ + struct charger_cable *cable = + container_of(work, struct charger_cable, wq); + int ret; + + if (cable->attached && cable->min_uA != 0 && cable->max_uA != 0) { + ret = regulator_set_current_limit(cable->charger->consumer, + cable->min_uA, cable->max_uA); + if (ret < 0) { + pr_err("Cannot set current limit of %s (%s)\n", + cable->charger->regulator_name, cable->name); + return; + } + + pr_info("Set current limit of %s : %duA ~ %duA\n", + cable->charger->regulator_name, + cable->min_uA, cable->max_uA); + } + + try_charger_enable(cable->cm, cable->attached); +} + +/** + * charger_extcon_notifier - receive the state of charger cable + * when registered cable is attached or detached. + * + * @self: the notifier block of the charger_extcon_notifier. + * @event: the cable state. + * @ptr: the data pointer of notifier block. + */ +static int charger_extcon_notifier(struct notifier_block *self, + unsigned long event, void *ptr) +{ + struct charger_cable *cable = + container_of(self, struct charger_cable, nb); + + cable->attached = event; + schedule_work(&cable->wq); + + return NOTIFY_DONE; +} + +/** + * charger_extcon_init - register external connector to use it + * as the charger cable + * + * @cm: the Charger Manager representing the battery. + * @cable: the Charger cable representing the external connector. + */ +static int charger_extcon_init(struct charger_manager *cm, + struct charger_cable *cable) +{ + int ret = 0; + + /* + * Charger manager use Extcon framework to identify + * the charger cable among various external connector + * cable (e.g., TA, USB, MHL, Dock). + */ + INIT_WORK(&cable->wq, charger_extcon_work); + cable->nb.notifier_call = charger_extcon_notifier; + ret = extcon_register_interest(&cable->extcon_dev, + cable->extcon_name, cable->name, &cable->nb); + if (ret < 0) { + pr_info("Cannot register extcon_dev for %s(cable: %s).\n", + cable->extcon_name, + cable->name); + ret = -EINVAL; + } + + return ret; +} + static int charger_manager_probe(struct platform_device *pdev) { struct charger_desc *desc = dev_get_platdata(&pdev->dev); struct charger_manager *cm; int ret = 0, i = 0; + int j = 0; union power_supply_propval val; if (g_desc && !rtc_dev && g_desc->rtc_name) { @@ -1167,11 +1245,31 @@ static int charger_manager_probe(struct platform_device *pdev) goto err_register; } - ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators, - desc->charger_regulators); - if (ret) { - dev_err(&pdev->dev, "Cannot get charger regulators.\n"); - goto err_bulk_get; + for (i = 0 ; i < desc->num_charger_regulators ; i++) { + struct charger_regulator *charger + = &desc->charger_regulators[i]; + + charger->consumer = regulator_get(&pdev->dev, + charger->regulator_name); + if (charger->consumer == NULL) { + dev_err(&pdev->dev, "Cannot find charger(%s)n", + charger->regulator_name); + ret = -EINVAL; + goto err_chg_get; + } + + for (j = 0 ; j < charger->num_cables ; j++) { + struct charger_cable *cable = &charger->cables[j]; + + ret = charger_extcon_init(cm, cable); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot find charger(%s)n", + charger->regulator_name); + goto err_extcon; + } + cable->charger = charger; + cable->cm = cm; + } } ret = try_charger_enable(cm, true); @@ -1197,9 +1295,19 @@ static int charger_manager_probe(struct platform_device *pdev) return 0; err_chg_enable: - regulator_bulk_free(desc->num_charger_regulators, - desc->charger_regulators); -err_bulk_get: +err_extcon: + for (i = 0 ; i < desc->num_charger_regulators ; i++) { + struct charger_regulator *charger + = &desc->charger_regulators[i]; + for (j = 0 ; j < charger->num_cables ; j++) { + struct charger_cable *cable = &charger->cables[j]; + extcon_unregister_interest(&cable->extcon_dev); + } + } +err_chg_get: + for (i = 0 ; i < desc->num_charger_regulators ; i++) + regulator_put(desc->charger_regulators[i].consumer); + power_supply_unregister(&cm->charger_psy); err_register: kfree(cm->charger_psy.properties); @@ -1218,6 +1326,8 @@ static int __devexit charger_manager_remove(struct platform_device *pdev) { struct charger_manager *cm = platform_get_drvdata(pdev); struct charger_desc *desc = cm->desc; + int i = 0; + int j = 0; /* Remove from the list */ mutex_lock(&cm_list_mtx); @@ -1229,8 +1339,18 @@ static int __devexit charger_manager_remove(struct platform_device *pdev) if (delayed_work_pending(&cm_monitor_work)) cancel_delayed_work_sync(&cm_monitor_work); - regulator_bulk_free(desc->num_charger_regulators, - desc->charger_regulators); + for (i = 0 ; i < desc->num_charger_regulators ; i++) { + struct charger_regulator *charger + = &desc->charger_regulators[i]; + for (j = 0 ; j < charger->num_cables ; j++) { + struct charger_cable *cable = &charger->cables[j]; + extcon_unregister_interest(&cable->extcon_dev); + } + } + + for (i = 0 ; i < desc->num_charger_regulators ; i++) + regulator_put(desc->charger_regulators[i].consumer); + power_supply_unregister(&cm->charger_psy); try_charger_enable(cm, false); diff --git a/drivers/power/ds2781_battery.c b/drivers/power/ds2781_battery.c index 5f92a4bb33f9..7a1ff4e4cf9a 100644 --- a/drivers/power/ds2781_battery.c +++ b/drivers/power/ds2781_battery.c @@ -64,7 +64,7 @@ static inline int ds2781_battery_io(struct ds2781_device_info *dev_info, return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io); } -int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf, +static int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf, int addr, size_t count) { return ds2781_battery_io(dev_info, buf, addr, count, 0); diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c index 8672c9177dd7..cb2aa3195687 100644 --- a/drivers/power/gpio-charger.c +++ b/drivers/power/gpio-charger.c @@ -54,7 +54,7 @@ static int gpio_charger_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_ONLINE: - val->intval = gpio_get_value(pdata->gpio); + val->intval = gpio_get_value_cansleep(pdata->gpio); val->intval ^= pdata->gpio_active_low; break; default: diff --git a/drivers/power/lp8727_charger.c b/drivers/power/lp8727_charger.c index d8b75780bfef..6a364f4798f7 100644 --- a/drivers/power/lp8727_charger.c +++ b/drivers/power/lp8727_charger.c @@ -15,7 +15,7 @@ #include <linux/interrupt.h> #include <linux/i2c.h> #include <linux/power_supply.h> -#include <linux/lp8727.h> +#include <linux/platform_data/lp8727.h> #define DEBOUNCE_MSEC 270 diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 140788b309f8..74abc6c755b4 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -113,6 +113,7 @@ static enum power_supply_property max17042_battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_OCV, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, @@ -201,6 +202,13 @@ static int max17042_get_property(struct power_supply *psy, val->intval = ret * 1000 / 2; break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + ret = max17042_read_reg(chip->client, MAX17042_QH); + if (ret < 0) + return ret; + + val->intval = ret * 1000 / 2; + break; case POWER_SUPPLY_PROP_TEMP: ret = max17042_read_reg(chip->client, MAX17042_TEMP); if (ret < 0) diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index 7385092f9bc8..55b10b813353 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -231,11 +231,9 @@ static int olpc_bat_get_charge_full_design(union power_supply_propval *val) case POWER_SUPPLY_TECHNOLOGY_LiFe: switch (mfr) { - case 1: /* Gold Peak */ - val->intval = 2800000; - break; + case 1: /* Gold Peak, fall through */ case 2: /* BYD */ - val->intval = 3100000; + val->intval = 2800000; break; default: return -EIO; @@ -267,6 +265,55 @@ static int olpc_bat_get_charge_now(union power_supply_propval *val) return 0; } +static int olpc_bat_get_voltage_max_design(union power_supply_propval *val) +{ + uint8_t ec_byte; + union power_supply_propval tech; + int mfr; + int ret; + + ret = olpc_bat_get_tech(&tech); + if (ret) + return ret; + + ec_byte = BAT_ADDR_MFR_TYPE; + ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); + if (ret) + return ret; + + mfr = ec_byte >> 4; + + switch (tech.intval) { + case POWER_SUPPLY_TECHNOLOGY_NiMH: + switch (mfr) { + case 1: /* Gold Peak */ + val->intval = 6000000; + break; + default: + return -EIO; + } + break; + + case POWER_SUPPLY_TECHNOLOGY_LiFe: + switch (mfr) { + case 1: /* Gold Peak */ + val->intval = 6400000; + break; + case 2: /* BYD */ + val->intval = 6500000; + break; + default: + return -EIO; + } + break; + + default: + return -EIO; + } + + return ret; +} + /********************************************************************* * Battery properties *********************************************************************/ @@ -401,6 +448,11 @@ static int olpc_bat_get_property(struct power_supply *psy, sprintf(bat_serial, "%016llx", (long long)be64_to_cpu(ser_buf)); val->strval = bat_serial; break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + ret = olpc_bat_get_voltage_max_design(val); + if (ret) + return ret; + break; default: ret = -EINVAL; break; @@ -428,6 +480,7 @@ static enum power_supply_property olpc_xo1_bat_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_SERIAL_NUMBER, POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, }; /* XO-1.5 does not have ambient temperature property */ @@ -449,6 +502,7 @@ static enum power_supply_property olpc_xo15_bat_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_SERIAL_NUMBER, POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, }; /* EEPROM reading goes completely around the power_supply API, sadly */ diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 8dbcd53c5e67..6a1ca735e935 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -134,13 +134,13 @@ static void update_charger(void) regulator_set_current_limit(ac_draw, max_uA, max_uA); if (!regulator_enabled) { dev_dbg(dev, "charger on (AC)\n"); - regulator_enable(ac_draw); + WARN_ON(regulator_enable(ac_draw)); regulator_enabled = 1; } } else { if (regulator_enabled) { dev_dbg(dev, "charger off\n"); - regulator_disable(ac_draw); + WARN_ON(regulator_disable(ac_draw)); regulator_enabled = 0; } } diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 6ad612726785..08cc8a3c15af 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -17,6 +17,7 @@ #include <linux/device.h> #include <linux/err.h> #include <linux/power_supply.h> +#include <linux/thermal.h> #include "power_supply.h" /* exported for the APM Power driver, APM emulation */ @@ -169,6 +170,63 @@ static void power_supply_dev_release(struct device *dev) kfree(dev); } +#ifdef CONFIG_THERMAL +static int power_supply_read_temp(struct thermal_zone_device *tzd, + unsigned long *temp) +{ + struct power_supply *psy; + union power_supply_propval val; + int ret; + + WARN_ON(tzd == NULL); + psy = tzd->devdata; + ret = psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &val); + + /* Convert tenths of degree Celsius to milli degree Celsius. */ + if (!ret) + *temp = val.intval * 100; + + return ret; +} + +static struct thermal_zone_device_ops psy_tzd_ops = { + .get_temp = power_supply_read_temp, +}; + +static int psy_register_thermal(struct power_supply *psy) +{ + int i; + + /* Register battery zone device psy reports temperature */ + for (i = 0; i < psy->num_properties; i++) { + if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) { + psy->tzd = thermal_zone_device_register(psy->name, 0, 0, + psy, &psy_tzd_ops, 0, 0, 0, 0); + if (IS_ERR(psy->tzd)) + return PTR_ERR(psy->tzd); + break; + } + } + return 0; +} + +static void psy_unregister_thermal(struct power_supply *psy) +{ + if (IS_ERR_OR_NULL(psy->tzd)) + return; + thermal_zone_device_unregister(psy->tzd); +} +#else +static int psy_register_thermal(struct power_supply *psy) +{ + return 0; +} + +static void psy_unregister_thermal(struct power_supply *psy) +{ +} +#endif + int power_supply_register(struct device *parent, struct power_supply *psy) { struct device *dev; @@ -197,6 +255,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy) if (rc) goto device_add_failed; + rc = psy_register_thermal(psy); + if (rc) + goto register_thermal_failed; + rc = power_supply_create_triggers(psy); if (rc) goto create_triggers_failed; @@ -206,6 +268,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy) goto success; create_triggers_failed: + psy_unregister_thermal(psy); +register_thermal_failed: device_del(dev); kobject_set_name_failed: device_add_failed: @@ -220,6 +284,7 @@ void power_supply_unregister(struct power_supply *psy) cancel_work_sync(&psy->changed_work); sysfs_remove_link(&psy->dev->kobj, "powers"); power_supply_remove_triggers(psy); + psy_unregister_thermal(psy); device_unregister(psy->dev); } EXPORT_SYMBOL_GPL(power_supply_unregister); diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 4150747f9186..1d96614a17a4 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -159,6 +159,8 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(charge_now), POWER_SUPPLY_ATTR(charge_avg), POWER_SUPPLY_ATTR(charge_counter), + POWER_SUPPLY_ATTR(constant_charge_current), + POWER_SUPPLY_ATTR(constant_charge_voltage), POWER_SUPPLY_ATTR(energy_full_design), POWER_SUPPLY_ATTR(energy_empty_design), POWER_SUPPLY_ATTR(energy_full), @@ -166,9 +168,15 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(energy_now), POWER_SUPPLY_ATTR(energy_avg), POWER_SUPPLY_ATTR(capacity), + POWER_SUPPLY_ATTR(capacity_alert_min), + POWER_SUPPLY_ATTR(capacity_alert_max), POWER_SUPPLY_ATTR(capacity_level), POWER_SUPPLY_ATTR(temp), + POWER_SUPPLY_ATTR(temp_alert_min), + POWER_SUPPLY_ATTR(temp_alert_max), POWER_SUPPLY_ATTR(temp_ambient), + POWER_SUPPLY_ATTR(temp_ambient_alert_min), + POWER_SUPPLY_ATTR(temp_ambient_alert_max), POWER_SUPPLY_ATTR(time_to_empty_now), POWER_SUPPLY_ATTR(time_to_empty_avg), POWER_SUPPLY_ATTR(time_to_full_now), diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c index a5b6849d4123..a65e8f54157e 100644 --- a/drivers/power/sbs-battery.c +++ b/drivers/power/sbs-battery.c @@ -469,7 +469,7 @@ static int sbs_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; - break; + goto done; /* don't trigger power_supply_changed()! */ case POWER_SUPPLY_PROP_ENERGY_NOW: case POWER_SUPPLY_PROP_ENERGY_FULL: diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c index f8eedd8a676f..332dd0110bda 100644 --- a/drivers/power/smb347-charger.c +++ b/drivers/power/smb347-charger.c @@ -196,6 +196,14 @@ static const unsigned int ccc_tbl[] = { 1200000, }; +/* Convert register value to current using lookup table */ +static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val) +{ + if (val >= size) + return -EINVAL; + return tbl[val]; +} + /* Convert current to register value using lookup table */ static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val) { @@ -841,22 +849,101 @@ fail: return ret; } +/* + * Returns the constant charge current programmed + * into the charger in uA. + */ +static int get_const_charge_current(struct smb347_charger *smb) +{ + int ret, intval; + unsigned int v; + + if (!smb347_is_ps_online(smb)) + return -ENODATA; + + ret = regmap_read(smb->regmap, STAT_B, &v); + if (ret < 0) + return ret; + + /* + * The current value is composition of FCC and PCC values + * and we can detect which table to use from bit 5. + */ + if (v & 0x20) { + intval = hw_to_current(fcc_tbl, ARRAY_SIZE(fcc_tbl), v & 7); + } else { + v >>= 3; + intval = hw_to_current(pcc_tbl, ARRAY_SIZE(pcc_tbl), v & 7); + } + + return intval; +} + +/* + * Returns the constant charge voltage programmed + * into the charger in uV. + */ +static int get_const_charge_voltage(struct smb347_charger *smb) +{ + int ret, intval; + unsigned int v; + + if (!smb347_is_ps_online(smb)) + return -ENODATA; + + ret = regmap_read(smb->regmap, STAT_A, &v); + if (ret < 0) + return ret; + + v &= STAT_A_FLOAT_VOLTAGE_MASK; + if (v > 0x3d) + v = 0x3d; + + intval = 3500000 + v * 20000; + + return intval; +} + static int smb347_mains_get_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { struct smb347_charger *smb = container_of(psy, struct smb347_charger, mains); + int ret; - if (prop == POWER_SUPPLY_PROP_ONLINE) { + switch (prop) { + case POWER_SUPPLY_PROP_ONLINE: val->intval = smb->mains_online; - return 0; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = get_const_charge_voltage(smb); + if (ret < 0) + return ret; + else + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = get_const_charge_current(smb); + if (ret < 0) + return ret; + else + val->intval = ret; + break; + + default: + return -EINVAL; } - return -EINVAL; + + return 0; } static enum power_supply_property smb347_mains_properties[] = { POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, }; static int smb347_usb_get_property(struct power_supply *psy, @@ -865,16 +952,40 @@ static int smb347_usb_get_property(struct power_supply *psy, { struct smb347_charger *smb = container_of(psy, struct smb347_charger, usb); + int ret; - if (prop == POWER_SUPPLY_PROP_ONLINE) { + switch (prop) { + case POWER_SUPPLY_PROP_ONLINE: val->intval = smb->usb_online; - return 0; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = get_const_charge_voltage(smb); + if (ret < 0) + return ret; + else + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = get_const_charge_current(smb); + if (ret < 0) + return ret; + else + val->intval = ret; + break; + + default: + return -EINVAL; } - return -EINVAL; + + return 0; } static enum power_supply_property smb347_usb_properties[] = { POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, }; static int smb347_battery_get_property(struct power_supply *psy, diff --git a/drivers/power/test_power.c b/drivers/power/test_power.c index b527c93bf2f3..b99a452a4fda 100644 --- a/drivers/power/test_power.c +++ b/drivers/power/test_power.c @@ -22,11 +22,13 @@ #include <linux/vermagic.h> static int ac_online = 1; +static int usb_online = 1; static int battery_status = POWER_SUPPLY_STATUS_DISCHARGING; static int battery_health = POWER_SUPPLY_HEALTH_GOOD; static int battery_present = 1; /* true */ static int battery_technology = POWER_SUPPLY_TECHNOLOGY_LION; static int battery_capacity = 50; +static int battery_voltage = 3300; static int test_power_get_ac_property(struct power_supply *psy, enum power_supply_property psp, @@ -42,6 +44,20 @@ static int test_power_get_ac_property(struct power_supply *psy, return 0; } +static int test_power_get_usb_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = usb_online; + break; + default: + return -EINVAL; + } + return 0; +} + static int test_power_get_battery_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -86,6 +102,12 @@ static int test_power_get_battery_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: val->intval = 3600; break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = 26; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = battery_voltage; + break; default: pr_info("%s: some properties deliberately report errors.\n", __func__); @@ -114,6 +136,8 @@ static enum power_supply_property test_power_battery_props[] = { POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_SERIAL_NUMBER, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_VOLTAGE_NOW, }; static char *test_power_ac_supplied_to[] = { @@ -135,6 +159,14 @@ static struct power_supply test_power_supplies[] = { .properties = test_power_battery_props, .num_properties = ARRAY_SIZE(test_power_battery_props), .get_property = test_power_get_battery_property, + }, { + .name = "test_usb", + .type = POWER_SUPPLY_TYPE_USB, + .supplied_to = test_power_ac_supplied_to, + .num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to), + .properties = test_power_ac_props, + .num_properties = ARRAY_SIZE(test_power_ac_props), + .get_property = test_power_get_usb_property, }, }; @@ -167,6 +199,7 @@ static void __exit test_power_exit(void) /* Let's see how we handle changes... */ ac_online = 0; + usb_online = 0; battery_status = POWER_SUPPLY_STATUS_DISCHARGING; for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) power_supply_changed(&test_power_supplies[i]); @@ -275,6 +308,19 @@ static int param_get_ac_online(char *buffer, const struct kernel_param *kp) return strlen(buffer); } +static int param_set_usb_online(const char *key, const struct kernel_param *kp) +{ + usb_online = map_get_value(map_ac_online, key, usb_online); + power_supply_changed(&test_power_supplies[2]); + return 0; +} + +static int param_get_usb_online(char *buffer, const struct kernel_param *kp) +{ + strcpy(buffer, map_get_key(map_ac_online, usb_online, "unknown")); + return strlen(buffer); +} + static int param_set_battery_status(const char *key, const struct kernel_param *kp) { @@ -350,13 +396,31 @@ static int param_set_battery_capacity(const char *key, #define param_get_battery_capacity param_get_int +static int param_set_battery_voltage(const char *key, + const struct kernel_param *kp) +{ + int tmp; + + if (1 != sscanf(key, "%d", &tmp)) + return -EINVAL; + + battery_voltage = tmp; + power_supply_changed(&test_power_supplies[1]); + return 0; +} +#define param_get_battery_voltage param_get_int static struct kernel_param_ops param_ops_ac_online = { .set = param_set_ac_online, .get = param_get_ac_online, }; +static struct kernel_param_ops param_ops_usb_online = { + .set = param_set_usb_online, + .get = param_get_usb_online, +}; + static struct kernel_param_ops param_ops_battery_status = { .set = param_set_battery_status, .get = param_get_battery_status, @@ -382,18 +446,27 @@ static struct kernel_param_ops param_ops_battery_capacity = { .get = param_get_battery_capacity, }; +static struct kernel_param_ops param_ops_battery_voltage = { + .set = param_set_battery_voltage, + .get = param_get_battery_voltage, +}; #define param_check_ac_online(name, p) __param_check(name, p, void); +#define param_check_usb_online(name, p) __param_check(name, p, void); #define param_check_battery_status(name, p) __param_check(name, p, void); #define param_check_battery_present(name, p) __param_check(name, p, void); #define param_check_battery_technology(name, p) __param_check(name, p, void); #define param_check_battery_health(name, p) __param_check(name, p, void); #define param_check_battery_capacity(name, p) __param_check(name, p, void); +#define param_check_battery_voltage(name, p) __param_check(name, p, void); module_param(ac_online, ac_online, 0644); MODULE_PARM_DESC(ac_online, "AC charging state <on|off>"); +module_param(usb_online, usb_online, 0644); +MODULE_PARM_DESC(usb_online, "USB charging state <on|off>"); + module_param(battery_status, battery_status, 0644); MODULE_PARM_DESC(battery_status, "battery status <charging|discharging|not-charging|full>"); @@ -413,6 +486,8 @@ MODULE_PARM_DESC(battery_health, module_param(battery_capacity, battery_capacity, 0644); MODULE_PARM_DESC(battery_capacity, "battery capacity (percentage)"); +module_param(battery_voltage, battery_voltage, 0644); +MODULE_PARM_DESC(battery_voltage, "battery voltage (millivolts)"); MODULE_DESCRIPTION("Power supply driver for testing"); MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>"); diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 7cacbaa68efe..15f4d5d8611b 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -22,6 +22,7 @@ #include <linux/power_supply.h> #include <linux/notifier.h> #include <linux/usb/otg.h> +#include <linux/regulator/machine.h> #define TWL4030_BCIMSTATEC 0x02 #define TWL4030_BCIICHG 0x08 @@ -29,6 +30,7 @@ #define TWL4030_BCIVBUS 0x0c #define TWL4030_BCIMFSTS4 0x10 #define TWL4030_BCICTL1 0x23 +#define TWL4030_BB_CFG 0x12 #define TWL4030_BCIAUTOWEN BIT(5) #define TWL4030_CONFIG_DONE BIT(4) @@ -38,6 +40,17 @@ #define TWL4030_USBFASTMCHG BIT(2) #define TWL4030_STS_VBUS BIT(7) #define TWL4030_STS_USB_ID BIT(2) +#define TWL4030_BBCHEN BIT(4) +#define TWL4030_BBSEL_MASK 0b1100 +#define TWL4030_BBSEL_2V5 0b0000 +#define TWL4030_BBSEL_3V0 0b0100 +#define TWL4030_BBSEL_3V1 0b1000 +#define TWL4030_BBSEL_3V2 0b1100 +#define TWL4030_BBISEL_MASK 0b11 +#define TWL4030_BBISEL_25uA 0b00 +#define TWL4030_BBISEL_150uA 0b01 +#define TWL4030_BBISEL_500uA 0b10 +#define TWL4030_BBISEL_1000uA 0b11 /* BCI interrupts */ #define TWL4030_WOVF BIT(0) /* Watchdog overflow */ @@ -75,6 +88,8 @@ struct twl4030_bci { struct work_struct work; int irq_chg; int irq_bci; + struct regulator *usb_reg; + int usb_enabled; unsigned long event; }; @@ -104,7 +119,7 @@ static int twl4030_bci_read(u8 reg, u8 *val) static int twl4030_clear_set_boot_bci(u8 clear, u8 set) { - return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, 0, + return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, clear, TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set, TWL4030_PM_MASTER_BOOT_BCI); } @@ -152,14 +167,14 @@ static int twl4030_bci_have_vbus(struct twl4030_bci *bci) } /* - * Enable/Disable USB Charge funtionality. + * Enable/Disable USB Charge functionality. */ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) { int ret; if (enable) { - /* Check for USB charger conneted */ + /* Check for USB charger connected */ if (!twl4030_bci_have_vbus(bci)) return -ENODEV; @@ -172,6 +187,12 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) return -EACCES; } + /* Need to keep regulator on */ + if (!bci->usb_enabled) { + regulator_enable(bci->usb_reg); + bci->usb_enabled = 1; + } + /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */ ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB); if (ret < 0) @@ -182,6 +203,10 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4); } else { ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0); + if (bci->usb_enabled) { + regulator_disable(bci->usb_reg); + bci->usb_enabled = 0; + } } return ret; @@ -203,6 +228,49 @@ static int twl4030_charger_enable_ac(bool enable) } /* + * Enable/Disable charging of Backup Battery. + */ +static int twl4030_charger_enable_backup(int uvolt, int uamp) +{ + int ret; + u8 flags; + + if (uvolt < 2500000 || + uamp < 25) { + /* disable charging of backup battery */ + ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER, + TWL4030_BBCHEN, 0, TWL4030_BB_CFG); + return ret; + } + + flags = TWL4030_BBCHEN; + if (uvolt >= 3200000) + flags |= TWL4030_BBSEL_3V2; + else if (uvolt >= 3100000) + flags |= TWL4030_BBSEL_3V1; + else if (uvolt >= 3000000) + flags |= TWL4030_BBSEL_3V0; + else + flags |= TWL4030_BBSEL_2V5; + + if (uamp >= 1000) + flags |= TWL4030_BBISEL_1000uA; + else if (uamp >= 500) + flags |= TWL4030_BBISEL_500uA; + else if (uamp >= 150) + flags |= TWL4030_BBISEL_150uA; + else + flags |= TWL4030_BBISEL_25uA; + + ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER, + TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK, + flags, + TWL4030_BB_CFG); + + return ret; +} + +/* * TWL4030 CHG_PRES (AC charger presence) events */ static irqreturn_t twl4030_charger_interrupt(int irq, void *arg) @@ -425,6 +493,7 @@ static enum power_supply_property twl4030_charger_props[] = { static int __init twl4030_bci_probe(struct platform_device *pdev) { struct twl4030_bci *bci; + struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data; int ret; u32 reg; @@ -456,6 +525,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) bci->usb.num_properties = ARRAY_SIZE(twl4030_charger_props); bci->usb.get_property = twl4030_bci_get_property; + bci->usb_reg = regulator_get(bci->dev, "bci3v1"); + ret = power_supply_register(&pdev->dev, &bci->usb); if (ret) { dev_err(&pdev->dev, "failed to register usb: %d\n", ret); @@ -504,6 +575,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) twl4030_charger_enable_ac(true); twl4030_charger_enable_usb(bci, true); + twl4030_charger_enable_backup(pdata->bb_uvolt, + pdata->bb_uamp); return 0; @@ -532,6 +605,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl4030_charger_enable_ac(false); twl4030_charger_enable_usb(bci, false); + twl4030_charger_enable_backup(0, 0); /* mask interrupts */ twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index 98fbe62694d4..e771487132f7 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c @@ -327,8 +327,10 @@ int pps_register_cdev(struct pps_device *pps) } pps->dev = device_create(pps_class, pps->info.dev, devt, pps, "pps%d", pps->id); - if (IS_ERR(pps->dev)) + if (IS_ERR(pps->dev)) { + err = PTR_ERR(pps->dev); goto del_cdev; + } pps->dev->release = pps_device_destruct; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig new file mode 100644 index 000000000000..8fc3808d7a3e --- /dev/null +++ b/drivers/pwm/Kconfig @@ -0,0 +1,108 @@ +menuconfig PWM + bool "PWM Support" + depends on !MACH_JZ4740 && !PUV3_PWM + help + This enables PWM support through the generic PWM framework. + You only need to enable this, if you also want to enable + one or more of the PWM drivers below. + + If unsure, say N. + +if PWM + +config PWM_BFIN + tristate "Blackfin PWM support" + depends on BFIN_GPTIMERS + help + Generic PWM framework driver for Blackfin. + + To compile this driver as a module, choose M here: the module + will be called pwm-bfin. + +config PWM_IMX + tristate "i.MX pwm support" + depends on ARCH_MXC + help + Generic PWM framework driver for i.MX. + + To compile this driver as a module, choose M here: the module + will be called pwm-imx. + +config PWM_LPC32XX + tristate "LPC32XX PWM support" + depends on ARCH_LPC32XX + help + Generic PWM framework driver for LPC32XX. The LPC32XX SOC has two + PWM controllers. + + To compile this driver as a module, choose M here: the module + will be called pwm-lpc32xx. + +config PWM_MXS + tristate "Freescale MXS PWM support" + depends on ARCH_MXS && OF + select STMP_DEVICE + help + Generic PWM framework driver for Freescale MXS. + + To compile this driver as a module, choose M here: the module + will be called pwm-mxs. + +config PWM_PXA + tristate "PXA PWM support" + depends on ARCH_PXA + help + Generic PWM framework driver for PXA. + + To compile this driver as a module, choose M here: the module + will be called pwm-pxa. + +config PWM_SAMSUNG + tristate "Samsung pwm support" + depends on PLAT_SAMSUNG + help + Generic PWM framework driver for Samsung. + + To compile this driver as a module, choose M here: the module + will be called pwm-samsung. + +config PWM_TEGRA + tristate "NVIDIA Tegra PWM support" + depends on ARCH_TEGRA + help + Generic PWM framework driver for the PWFM controller found on NVIDIA + Tegra SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-tegra. + +config PWM_TIECAP + tristate "ECAP PWM support" + depends on SOC_AM33XX + help + PWM driver support for the ECAP APWM controller found on AM33XX + TI SOC + + To compile this driver as a module, choose M here: the module + will be called pwm-tiecap. + +config PWM_TIEHRPWM + tristate "EHRPWM PWM support" + depends on SOC_AM33XX + help + PWM driver support for the EHRPWM controller found on AM33XX + TI SOC + + To compile this driver as a module, choose M here: the module + will be called pwm-tiehrpwm. + +config PWM_VT8500 + tristate "vt8500 pwm support" + depends on ARCH_VT8500 + help + Generic PWM framework driver for vt8500. + + To compile this driver as a module, choose M here: the module + will be called pwm-vt8500. + +endif diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile new file mode 100644 index 000000000000..e4b2c898964d --- /dev/null +++ b/drivers/pwm/Makefile @@ -0,0 +1,11 @@ +obj-$(CONFIG_PWM) += core.o +obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o +obj-$(CONFIG_PWM_IMX) += pwm-imx.o +obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o +obj-$(CONFIG_PWM_MXS) += pwm-mxs.o +obj-$(CONFIG_PWM_PXA) += pwm-pxa.o +obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o +obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o +obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o +obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o +obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c new file mode 100644 index 000000000000..ecb76909e946 --- /dev/null +++ b/drivers/pwm/core.c @@ -0,0 +1,713 @@ +/* + * Generic pwmlib implementation + * + * Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de> + * Copyright (C) 2011-2012 Avionic Design 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, 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/pwm.h> +#include <linux/radix-tree.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +#define MAX_PWMS 1024 + +static DEFINE_MUTEX(pwm_lookup_lock); +static LIST_HEAD(pwm_lookup_list); +static DEFINE_MUTEX(pwm_lock); +static LIST_HEAD(pwm_chips); +static DECLARE_BITMAP(allocated_pwms, MAX_PWMS); +static RADIX_TREE(pwm_tree, GFP_KERNEL); + +static struct pwm_device *pwm_to_device(unsigned int pwm) +{ + return radix_tree_lookup(&pwm_tree, pwm); +} + +static int alloc_pwms(int pwm, unsigned int count) +{ + unsigned int from = 0; + unsigned int start; + + if (pwm >= MAX_PWMS) + return -EINVAL; + + if (pwm >= 0) + from = pwm; + + start = bitmap_find_next_zero_area(allocated_pwms, MAX_PWMS, from, + count, 0); + + if (pwm >= 0 && start != pwm) + return -EEXIST; + + if (start + count > MAX_PWMS) + return -ENOSPC; + + return start; +} + +static void free_pwms(struct pwm_chip *chip) +{ + unsigned int i; + + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; + radix_tree_delete(&pwm_tree, pwm->pwm); + } + + bitmap_clear(allocated_pwms, chip->base, chip->npwm); + + kfree(chip->pwms); + chip->pwms = NULL; +} + +static struct pwm_chip *pwmchip_find_by_name(const char *name) +{ + struct pwm_chip *chip; + + if (!name) + return NULL; + + mutex_lock(&pwm_lock); + + list_for_each_entry(chip, &pwm_chips, list) { + const char *chip_name = dev_name(chip->dev); + + if (chip_name && strcmp(chip_name, name) == 0) { + mutex_unlock(&pwm_lock); + return chip; + } + } + + mutex_unlock(&pwm_lock); + + return NULL; +} + +static int pwm_device_request(struct pwm_device *pwm, const char *label) +{ + int err; + + if (test_bit(PWMF_REQUESTED, &pwm->flags)) + return -EBUSY; + + if (!try_module_get(pwm->chip->ops->owner)) + return -ENODEV; + + if (pwm->chip->ops->request) { + err = pwm->chip->ops->request(pwm->chip, pwm); + if (err) { + module_put(pwm->chip->ops->owner); + return err; + } + } + + set_bit(PWMF_REQUESTED, &pwm->flags); + pwm->label = label; + + return 0; +} + +static struct pwm_device *of_pwm_simple_xlate(struct pwm_chip *pc, + const struct of_phandle_args *args) +{ + struct pwm_device *pwm; + + if (pc->of_pwm_n_cells < 2) + return ERR_PTR(-EINVAL); + + if (args->args[0] >= pc->npwm) + return ERR_PTR(-EINVAL); + + pwm = pwm_request_from_chip(pc, args->args[0], NULL); + if (IS_ERR(pwm)) + return pwm; + + pwm_set_period(pwm, args->args[1]); + + return pwm; +} + +void of_pwmchip_add(struct pwm_chip *chip) +{ + if (!chip->dev || !chip->dev->of_node) + return; + + if (!chip->of_xlate) { + chip->of_xlate = of_pwm_simple_xlate; + chip->of_pwm_n_cells = 2; + } + + of_node_get(chip->dev->of_node); +} + +void of_pwmchip_remove(struct pwm_chip *chip) +{ + if (chip->dev && chip->dev->of_node) + of_node_put(chip->dev->of_node); +} + +/** + * pwm_set_chip_data() - set private chip data for a PWM + * @pwm: PWM device + * @data: pointer to chip-specific data + */ +int pwm_set_chip_data(struct pwm_device *pwm, void *data) +{ + if (!pwm) + return -EINVAL; + + pwm->chip_data = data; + + return 0; +} + +/** + * pwm_get_chip_data() - get private chip data for a PWM + * @pwm: PWM device + */ +void *pwm_get_chip_data(struct pwm_device *pwm) +{ + return pwm ? pwm->chip_data : NULL; +} + +/** + * pwmchip_add() - register a new PWM chip + * @chip: the PWM chip to add + * + * Register a new PWM chip. If chip->base < 0 then a dynamically assigned base + * will be used. + */ +int pwmchip_add(struct pwm_chip *chip) +{ + struct pwm_device *pwm; + unsigned int i; + int ret; + + if (!chip || !chip->dev || !chip->ops || !chip->ops->config || + !chip->ops->enable || !chip->ops->disable) + return -EINVAL; + + mutex_lock(&pwm_lock); + + ret = alloc_pwms(chip->base, chip->npwm); + if (ret < 0) + goto out; + + chip->pwms = kzalloc(chip->npwm * sizeof(*pwm), GFP_KERNEL); + if (!chip->pwms) { + ret = -ENOMEM; + goto out; + } + + chip->base = ret; + + for (i = 0; i < chip->npwm; i++) { + pwm = &chip->pwms[i]; + + pwm->chip = chip; + pwm->pwm = chip->base + i; + pwm->hwpwm = i; + + radix_tree_insert(&pwm_tree, pwm->pwm, pwm); + } + + bitmap_set(allocated_pwms, chip->base, chip->npwm); + + INIT_LIST_HEAD(&chip->list); + list_add(&chip->list, &pwm_chips); + + ret = 0; + + if (IS_ENABLED(CONFIG_OF)) + of_pwmchip_add(chip); + +out: + mutex_unlock(&pwm_lock); + return ret; +} +EXPORT_SYMBOL_GPL(pwmchip_add); + +/** + * pwmchip_remove() - remove a PWM chip + * @chip: the PWM chip to remove + * + * Removes a PWM chip. This function may return busy if the PWM chip provides + * a PWM device that is still requested. + */ +int pwmchip_remove(struct pwm_chip *chip) +{ + unsigned int i; + int ret = 0; + + mutex_lock(&pwm_lock); + + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; + + if (test_bit(PWMF_REQUESTED, &pwm->flags)) { + ret = -EBUSY; + goto out; + } + } + + list_del_init(&chip->list); + + if (IS_ENABLED(CONFIG_OF)) + of_pwmchip_remove(chip); + + free_pwms(chip); + +out: + mutex_unlock(&pwm_lock); + return ret; +} +EXPORT_SYMBOL_GPL(pwmchip_remove); + +/** + * pwm_request() - request a PWM device + * @pwm_id: global PWM device index + * @label: PWM device label + * + * This function is deprecated, use pwm_get() instead. + */ +struct pwm_device *pwm_request(int pwm, const char *label) +{ + struct pwm_device *dev; + int err; + + if (pwm < 0 || pwm >= MAX_PWMS) + return ERR_PTR(-EINVAL); + + mutex_lock(&pwm_lock); + + dev = pwm_to_device(pwm); + if (!dev) { + dev = ERR_PTR(-EPROBE_DEFER); + goto out; + } + + err = pwm_device_request(dev, label); + if (err < 0) + dev = ERR_PTR(err); + +out: + mutex_unlock(&pwm_lock); + + return dev; +} +EXPORT_SYMBOL_GPL(pwm_request); + +/** + * pwm_request_from_chip() - request a PWM device relative to a PWM chip + * @chip: PWM chip + * @index: per-chip index of the PWM to request + * @label: a literal description string of this PWM + * + * Returns the PWM at the given index of the given PWM chip. A negative error + * code is returned if the index is not valid for the specified PWM chip or + * if the PWM device cannot be requested. + */ +struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, + unsigned int index, + const char *label) +{ + struct pwm_device *pwm; + int err; + + if (!chip || index >= chip->npwm) + return ERR_PTR(-EINVAL); + + mutex_lock(&pwm_lock); + pwm = &chip->pwms[index]; + + err = pwm_device_request(pwm, label); + if (err < 0) + pwm = ERR_PTR(err); + + mutex_unlock(&pwm_lock); + return pwm; +} +EXPORT_SYMBOL_GPL(pwm_request_from_chip); + +/** + * pwm_free() - free a PWM device + * @pwm: PWM device + * + * This function is deprecated, use pwm_put() instead. + */ +void pwm_free(struct pwm_device *pwm) +{ + pwm_put(pwm); +} +EXPORT_SYMBOL_GPL(pwm_free); + +/** + * pwm_config() - change a PWM device configuration + * @pwm: PWM device + * @duty_ns: "on" time (in nanoseconds) + * @period_ns: duration (in nanoseconds) of one cycle + */ +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +{ + if (!pwm || period_ns == 0 || duty_ns > period_ns) + return -EINVAL; + + return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns); +} +EXPORT_SYMBOL_GPL(pwm_config); + +/** + * pwm_enable() - start a PWM output toggling + * @pwm: PWM device + */ +int pwm_enable(struct pwm_device *pwm) +{ + if (pwm && !test_and_set_bit(PWMF_ENABLED, &pwm->flags)) + return pwm->chip->ops->enable(pwm->chip, pwm); + + return pwm ? 0 : -EINVAL; +} +EXPORT_SYMBOL_GPL(pwm_enable); + +/** + * pwm_disable() - stop a PWM output toggling + * @pwm: PWM device + */ +void pwm_disable(struct pwm_device *pwm) +{ + if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags)) + pwm->chip->ops->disable(pwm->chip, pwm); +} +EXPORT_SYMBOL_GPL(pwm_disable); + +static struct pwm_chip *of_node_to_pwmchip(struct device_node *np) +{ + struct pwm_chip *chip; + + mutex_lock(&pwm_lock); + + list_for_each_entry(chip, &pwm_chips, list) + if (chip->dev && chip->dev->of_node == np) { + mutex_unlock(&pwm_lock); + return chip; + } + + mutex_unlock(&pwm_lock); + + return ERR_PTR(-EPROBE_DEFER); +} + +/** + * of_pwm_request() - request a PWM via the PWM framework + * @np: device node to get the PWM from + * @con_id: consumer name + * + * Returns the PWM device parsed from the phandle and index specified in the + * "pwms" property of a device tree node or a negative error-code on failure. + * Values parsed from the device tree are stored in the returned PWM device + * object. + * + * If con_id is NULL, the first PWM device listed in the "pwms" property will + * be requested. Otherwise the "pwm-names" property is used to do a reverse + * lookup of the PWM index. This also means that the "pwm-names" property + * becomes mandatory for devices that look up the PWM device via the con_id + * parameter. + */ +static struct pwm_device *of_pwm_request(struct device_node *np, + const char *con_id) +{ + struct pwm_device *pwm = NULL; + struct of_phandle_args args; + struct pwm_chip *pc; + int index = 0; + int err; + + if (con_id) { + index = of_property_match_string(np, "pwm-names", con_id); + if (index < 0) + return ERR_PTR(index); + } + + err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index, + &args); + if (err) { + pr_debug("%s(): can't parse \"pwms\" property\n", __func__); + return ERR_PTR(err); + } + + pc = of_node_to_pwmchip(args.np); + if (IS_ERR(pc)) { + pr_debug("%s(): PWM chip not found\n", __func__); + pwm = ERR_CAST(pc); + goto put; + } + + if (args.args_count != pc->of_pwm_n_cells) { + pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name, + args.np->full_name); + pwm = ERR_PTR(-EINVAL); + goto put; + } + + pwm = pc->of_xlate(pc, &args); + if (IS_ERR(pwm)) + goto put; + + /* + * If a consumer name was not given, try to look it up from the + * "pwm-names" property if it exists. Otherwise use the name of + * the user device node. + */ + if (!con_id) { + err = of_property_read_string_index(np, "pwm-names", index, + &con_id); + if (err < 0) + con_id = np->name; + } + + pwm->label = con_id; + +put: + of_node_put(args.np); + + return pwm; +} + +/** + * pwm_add_table() - register PWM device consumers + * @table: array of consumers to register + * @num: number of consumers in table + */ +void __init pwm_add_table(struct pwm_lookup *table, size_t num) +{ + mutex_lock(&pwm_lookup_lock); + + while (num--) { + list_add_tail(&table->list, &pwm_lookup_list); + table++; + } + + mutex_unlock(&pwm_lookup_lock); +} + +/** + * pwm_get() - look up and request a PWM device + * @dev: device for PWM consumer + * @con_id: consumer name + * + * Lookup is first attempted using DT. If the device was not instantiated from + * a device tree, a PWM chip and a relative index is looked up via a table + * supplied by board setup code (see pwm_add_table()). + * + * Once a PWM chip has been found the specified PWM device will be requested + * and is ready to be used. + */ +struct pwm_device *pwm_get(struct device *dev, const char *con_id) +{ + struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER); + const char *dev_id = dev ? dev_name(dev): NULL; + struct pwm_chip *chip = NULL; + unsigned int index = 0; + unsigned int best = 0; + struct pwm_lookup *p; + unsigned int match; + + /* look up via DT first */ + if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) + return of_pwm_request(dev->of_node, con_id); + + /* + * We look up the provider in the static table typically provided by + * board setup code. We first try to lookup the consumer device by + * name. If the consumer device was passed in as NULL or if no match + * was found, we try to find the consumer by directly looking it up + * by name. + * + * If a match is found, the provider PWM chip is looked up by name + * and a PWM device is requested using the PWM device per-chip index. + * + * The lookup algorithm was shamelessly taken from the clock + * framework: + * + * We do slightly fuzzy matching here: + * An entry with a NULL ID is assumed to be a wildcard. + * If an entry has a device ID, it must match + * If an entry has a connection ID, it must match + * Then we take the most specific entry - with the following order + * of precedence: dev+con > dev only > con only. + */ + mutex_lock(&pwm_lookup_lock); + + list_for_each_entry(p, &pwm_lookup_list, list) { + match = 0; + + if (p->dev_id) { + if (!dev_id || strcmp(p->dev_id, dev_id)) + continue; + + match += 2; + } + + if (p->con_id) { + if (!con_id || strcmp(p->con_id, con_id)) + continue; + + match += 1; + } + + if (match > best) { + chip = pwmchip_find_by_name(p->provider); + index = p->index; + + if (match != 3) + best = match; + else + break; + } + } + + if (chip) + pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id); + + mutex_unlock(&pwm_lookup_lock); + + return pwm; +} +EXPORT_SYMBOL_GPL(pwm_get); + +/** + * pwm_put() - release a PWM device + * @pwm: PWM device + */ +void pwm_put(struct pwm_device *pwm) +{ + if (!pwm) + return; + + mutex_lock(&pwm_lock); + + if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) { + pr_warning("PWM device already freed\n"); + goto out; + } + + if (pwm->chip->ops->free) + pwm->chip->ops->free(pwm->chip, pwm); + + pwm->label = NULL; + + module_put(pwm->chip->ops->owner); +out: + mutex_unlock(&pwm_lock); +} +EXPORT_SYMBOL_GPL(pwm_put); + +#ifdef CONFIG_DEBUG_FS +static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) +{ + unsigned int i; + + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; + + seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label); + + if (test_bit(PWMF_REQUESTED, &pwm->flags)) + seq_printf(s, " requested"); + + if (test_bit(PWMF_ENABLED, &pwm->flags)) + seq_printf(s, " enabled"); + + seq_printf(s, "\n"); + } +} + +static void *pwm_seq_start(struct seq_file *s, loff_t *pos) +{ + mutex_lock(&pwm_lock); + s->private = ""; + + return seq_list_start(&pwm_chips, *pos); +} + +static void *pwm_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + s->private = "\n"; + + return seq_list_next(v, &pwm_chips, pos); +} + +static void pwm_seq_stop(struct seq_file *s, void *v) +{ + mutex_unlock(&pwm_lock); +} + +static int pwm_seq_show(struct seq_file *s, void *v) +{ + struct pwm_chip *chip = list_entry(v, struct pwm_chip, list); + + seq_printf(s, "%s%s/%s, %d PWM device%s\n", (char *)s->private, + chip->dev->bus ? chip->dev->bus->name : "no-bus", + dev_name(chip->dev), chip->npwm, + (chip->npwm != 1) ? "s" : ""); + + if (chip->ops->dbg_show) + chip->ops->dbg_show(chip, s); + else + pwm_dbg_show(chip, s); + + return 0; +} + +static const struct seq_operations pwm_seq_ops = { + .start = pwm_seq_start, + .next = pwm_seq_next, + .stop = pwm_seq_stop, + .show = pwm_seq_show, +}; + +static int pwm_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &pwm_seq_ops); +} + +static const struct file_operations pwm_debugfs_ops = { + .owner = THIS_MODULE, + .open = pwm_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init pwm_debugfs_init(void) +{ + debugfs_create_file("pwm", S_IFREG | S_IRUGO, NULL, NULL, + &pwm_debugfs_ops); + + return 0; +} + +subsys_initcall(pwm_debugfs_init); +#endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/pwm/pwm-bfin.c b/drivers/pwm/pwm-bfin.c new file mode 100644 index 000000000000..d53c4e7941ef --- /dev/null +++ b/drivers/pwm/pwm-bfin.c @@ -0,0 +1,162 @@ +/* + * Blackfin Pulse Width Modulation (PWM) core + * + * Copyright (c) 2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> + +#include <asm/gptimers.h> +#include <asm/portmux.h> + +struct bfin_pwm_chip { + struct pwm_chip chip; +}; + +struct bfin_pwm { + unsigned short pin; +}; + +static const unsigned short pwm_to_gptimer_per[] = { + P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR4, P_TMR5, + P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11, +}; + +static int bfin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct bfin_pwm *priv; + int ret; + + if (pwm->hwpwm >= ARRAY_SIZE(pwm_to_gptimer_per)) + return -EINVAL; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pin = pwm_to_gptimer_per[pwm->hwpwm]; + + ret = peripheral_request(priv->pin, NULL); + if (ret) { + kfree(priv); + return ret; + } + + pwm_set_chip_data(pwm, priv); + + return 0; +} + +static void bfin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct bfin_pwm *priv = pwm_get_chip_data(pwm); + + if (priv) { + peripheral_free(priv->pin); + kfree(priv); + } +} + +static int bfin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct bfin_pwm *priv = pwm_get_chip_data(pwm); + unsigned long period, duty; + unsigned long long val; + + if (duty_ns < 0 || duty_ns > period_ns) + return -EINVAL; + + val = (unsigned long long)get_sclk() * period_ns; + do_div(val, NSEC_PER_SEC); + period = val; + + val = (unsigned long long)period * duty_ns; + do_div(val, period_ns); + duty = period - val; + + if (duty >= period) + duty = period - 1; + + set_gptimer_config(priv->pin, TIMER_MODE_PWM | TIMER_PERIOD_CNT); + set_gptimer_pwidth(priv->pin, duty); + set_gptimer_period(priv->pin, period); + + return 0; +} + +static int bfin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct bfin_pwm *priv = pwm_get_chip_data(pwm); + + enable_gptimer(priv->pin); + + return 0; +} + +static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct bfin_pwm *priv = pwm_get_chip_data(pwm); + + disable_gptimer(priv->pin); +} + +static struct pwm_ops bfin_pwm_ops = { + .request = bfin_pwm_request, + .free = bfin_pwm_free, + .config = bfin_pwm_config, + .enable = bfin_pwm_enable, + .disable = bfin_pwm_disable, + .owner = THIS_MODULE, +}; + +static int bfin_pwm_probe(struct platform_device *pdev) +{ + struct bfin_pwm_chip *pwm; + int ret; + + pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); + if (!pwm) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, pwm); + + pwm->chip.dev = &pdev->dev; + pwm->chip.ops = &bfin_pwm_ops; + pwm->chip.base = -1; + pwm->chip.npwm = 12; + + ret = pwmchip_add(&pwm->chip); + if (ret < 0) { + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int __devexit bfin_pwm_remove(struct platform_device *pdev) +{ + struct bfin_pwm_chip *pwm = platform_get_drvdata(pdev); + + return pwmchip_remove(&pwm->chip); +} + +static struct platform_driver bfin_pwm_driver = { + .driver = { + .name = "bfin-pwm", + }, + .probe = bfin_pwm_probe, + .remove = __devexit_p(bfin_pwm_remove), +}; + +module_platform_driver(bfin_pwm_driver); + +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c new file mode 100644 index 000000000000..2a0b35333972 --- /dev/null +++ b/drivers/pwm/pwm-imx.c @@ -0,0 +1,230 @@ +/* + * simple driver for PWM (Pulse Width Modulator) controller + * + * 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. + * + * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/pwm.h> +#include <mach/hardware.h> + + +/* i.MX1 and i.MX21 share the same PWM function block: */ + +#define MX1_PWMC 0x00 /* PWM Control Register */ +#define MX1_PWMS 0x04 /* PWM Sample Register */ +#define MX1_PWMP 0x08 /* PWM Period Register */ + + +/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ + +#define MX3_PWMCR 0x00 /* PWM Control Register */ +#define MX3_PWMSAR 0x0C /* PWM Sample Register */ +#define MX3_PWMPR 0x10 /* PWM Period Register */ +#define MX3_PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4) +#define MX3_PWMCR_DOZEEN (1 << 24) +#define MX3_PWMCR_WAITEN (1 << 23) +#define MX3_PWMCR_DBGEN (1 << 22) +#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) +#define MX3_PWMCR_CLKSRC_IPG (1 << 16) +#define MX3_PWMCR_EN (1 << 0) + +struct imx_chip { + struct clk *clk; + + int clk_enabled; + void __iomem *mmio_base; + + struct pwm_chip chip; +}; + +#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) + +static int imx_pwm_config(struct pwm_chip *chip, + struct pwm_device *pwm, int duty_ns, int period_ns) +{ + struct imx_chip *imx = to_imx_chip(chip); + + if (!(cpu_is_mx1() || cpu_is_mx21())) { + unsigned long long c; + unsigned long period_cycles, duty_cycles, prescale; + u32 cr; + + c = clk_get_rate(imx->clk); + c = c * period_ns; + do_div(c, 1000000000); + period_cycles = c; + + prescale = period_cycles / 0x10000 + 1; + + period_cycles /= prescale; + c = (unsigned long long)period_cycles * duty_ns; + do_div(c, period_ns); + duty_cycles = c; + + /* + * according to imx pwm RM, the real period value should be + * PERIOD value in PWMPR plus 2. + */ + if (period_cycles > 2) + period_cycles -= 2; + else + period_cycles = 0; + + writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); + writel(period_cycles, imx->mmio_base + MX3_PWMPR); + + cr = MX3_PWMCR_PRESCALER(prescale) | + MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | + MX3_PWMCR_DBGEN | MX3_PWMCR_EN; + + if (cpu_is_mx25()) + cr |= MX3_PWMCR_CLKSRC_IPG; + else + cr |= MX3_PWMCR_CLKSRC_IPG_HIGH; + + writel(cr, imx->mmio_base + MX3_PWMCR); + } else if (cpu_is_mx1() || cpu_is_mx21()) { + /* The PWM subsystem allows for exact frequencies. However, + * I cannot connect a scope on my device to the PWM line and + * thus cannot provide the program the PWM controller + * exactly. Instead, I'm relying on the fact that the + * Bootloader (u-boot or WinCE+haret) has programmed the PWM + * function group already. So I'll just modify the PWM sample + * register to follow the ratio of duty_ns vs. period_ns + * accordingly. + * + * This is good enough for programming the brightness of + * the LCD backlight. + * + * The real implementation would divide PERCLK[0] first by + * both the prescaler (/1 .. /128) and then by CLKSEL + * (/2 .. /16). + */ + u32 max = readl(imx->mmio_base + MX1_PWMP); + u32 p = max * duty_ns / period_ns; + writel(max - p, imx->mmio_base + MX1_PWMS); + } else { + BUG(); + } + + return 0; +} + +static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct imx_chip *imx = to_imx_chip(chip); + int rc = 0; + + if (!imx->clk_enabled) { + rc = clk_prepare_enable(imx->clk); + if (!rc) + imx->clk_enabled = 1; + } + return rc; +} + +static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct imx_chip *imx = to_imx_chip(chip); + + writel(0, imx->mmio_base + MX3_PWMCR); + + if (imx->clk_enabled) { + clk_disable_unprepare(imx->clk); + imx->clk_enabled = 0; + } +} + +static struct pwm_ops imx_pwm_ops = { + .enable = imx_pwm_enable, + .disable = imx_pwm_disable, + .config = imx_pwm_config, + .owner = THIS_MODULE, +}; + +static int __devinit imx_pwm_probe(struct platform_device *pdev) +{ + struct imx_chip *imx; + struct resource *r; + int ret = 0; + + imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); + if (imx == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + imx->clk = devm_clk_get(&pdev->dev, "pwm"); + + if (IS_ERR(imx->clk)) + return PTR_ERR(imx->clk); + + imx->chip.ops = &imx_pwm_ops; + imx->chip.dev = &pdev->dev; + imx->chip.base = -1; + imx->chip.npwm = 1; + + imx->clk_enabled = 0; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no memory resource defined\n"); + return -ENODEV; + } + + imx->mmio_base = devm_request_and_ioremap(&pdev->dev, r); + if (imx->mmio_base == NULL) + return -EADDRNOTAVAIL; + + ret = pwmchip_add(&imx->chip); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, imx); + return 0; +} + +static int __devexit imx_pwm_remove(struct platform_device *pdev) +{ + struct imx_chip *imx; + + imx = platform_get_drvdata(pdev); + if (imx == NULL) + return -ENODEV; + + return pwmchip_remove(&imx->chip); +} + +static struct platform_driver imx_pwm_driver = { + .driver = { + .name = "mxc_pwm", + }, + .probe = imx_pwm_probe, + .remove = __devexit_p(imx_pwm_remove), +}; + +static int __init imx_pwm_init(void) +{ + return platform_driver_register(&imx_pwm_driver); +} +arch_initcall(imx_pwm_init); + +static void __exit imx_pwm_exit(void) +{ + platform_driver_unregister(&imx_pwm_driver); +} +module_exit(imx_pwm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c new file mode 100644 index 000000000000..adb87f0c1633 --- /dev/null +++ b/drivers/pwm/pwm-lpc32xx.c @@ -0,0 +1,148 @@ +/* + * Copyright 2012 Alexandre Pereira da Silva <aletes.xgr@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2. + * + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> + +struct lpc32xx_pwm_chip { + struct pwm_chip chip; + struct clk *clk; + void __iomem *base; +}; + +#define PWM_ENABLE (1 << 31) +#define PWM_RELOADV(x) (((x) & 0xFF) << 8) +#define PWM_DUTY(x) ((x) & 0xFF) + +#define to_lpc32xx_pwm_chip(_chip) \ + container_of(_chip, struct lpc32xx_pwm_chip, chip) + +static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip); + unsigned long long c; + int period_cycles, duty_cycles; + + c = clk_get_rate(lpc32xx->clk) / 256; + c = c * period_ns; + do_div(c, NSEC_PER_SEC); + + /* Handle high and low extremes */ + if (c == 0) + c = 1; + if (c > 255) + c = 0; /* 0 set division by 256 */ + period_cycles = c; + + c = 256 * duty_ns; + do_div(c, period_ns); + duty_cycles = c; + + writel(PWM_ENABLE | PWM_RELOADV(period_cycles) | PWM_DUTY(duty_cycles), + lpc32xx->base + (pwm->hwpwm << 2)); + + return 0; +} + +static int lpc32xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip); + + return clk_enable(lpc32xx->clk); +} + +static void lpc32xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip); + + writel(0, lpc32xx->base + (pwm->hwpwm << 2)); + clk_disable(lpc32xx->clk); +} + +static const struct pwm_ops lpc32xx_pwm_ops = { + .config = lpc32xx_pwm_config, + .enable = lpc32xx_pwm_enable, + .disable = lpc32xx_pwm_disable, + .owner = THIS_MODULE, +}; + +static int lpc32xx_pwm_probe(struct platform_device *pdev) +{ + struct lpc32xx_pwm_chip *lpc32xx; + struct resource *res; + int ret; + + lpc32xx = devm_kzalloc(&pdev->dev, sizeof(*lpc32xx), GFP_KERNEL); + if (!lpc32xx) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + lpc32xx->base = devm_request_and_ioremap(&pdev->dev, res); + if (!lpc32xx->base) + return -EADDRNOTAVAIL; + + lpc32xx->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(lpc32xx->clk)) + return PTR_ERR(lpc32xx->clk); + + lpc32xx->chip.dev = &pdev->dev; + lpc32xx->chip.ops = &lpc32xx_pwm_ops; + lpc32xx->chip.npwm = 2; + + ret = pwmchip_add(&lpc32xx->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip, error %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, lpc32xx); + + return 0; +} + +static int __devexit lpc32xx_pwm_remove(struct platform_device *pdev) +{ + struct lpc32xx_pwm_chip *lpc32xx = platform_get_drvdata(pdev); + + clk_disable(lpc32xx->clk); + return pwmchip_remove(&lpc32xx->chip); +} + +static struct of_device_id lpc32xx_pwm_dt_ids[] = { + { .compatible = "nxp,lpc3220-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, lpc32xx_pwm_dt_ids); + +static struct platform_driver lpc32xx_pwm_driver = { + .driver = { + .name = "lpc32xx-pwm", + .of_match_table = of_match_ptr(lpc32xx_pwm_dt_ids), + }, + .probe = lpc32xx_pwm_probe, + .remove = __devexit_p(lpc32xx_pwm_remove), +}; +module_platform_driver(lpc32xx_pwm_driver); + +MODULE_ALIAS("platform:lpc32xx-pwm"); +MODULE_AUTHOR("Alexandre Pereira da Silva <aletes.xgr@gmail.com>"); +MODULE_DESCRIPTION("LPC32XX PWM Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c new file mode 100644 index 000000000000..e5852646f082 --- /dev/null +++ b/drivers/pwm/pwm-mxs.c @@ -0,0 +1,203 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> +#include <linux/stmp_device.h> + +#define SET 0x4 +#define CLR 0x8 +#define TOG 0xc + +#define PWM_CTRL 0x0 +#define PWM_ACTIVE0 0x10 +#define PWM_PERIOD0 0x20 +#define PERIOD_PERIOD(p) ((p) & 0xffff) +#define PERIOD_PERIOD_MAX 0x10000 +#define PERIOD_ACTIVE_HIGH (3 << 16) +#define PERIOD_INACTIVE_LOW (2 << 18) +#define PERIOD_CDIV(div) (((div) & 0x7) << 20) +#define PERIOD_CDIV_MAX 8 + +struct mxs_pwm_chip { + struct pwm_chip chip; + struct device *dev; + struct clk *clk; + void __iomem *base; +}; + +#define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip) + +static int mxs_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip); + int ret, div = 0; + unsigned int period_cycles, duty_cycles; + unsigned long rate; + unsigned long long c; + + rate = clk_get_rate(mxs->clk); + while (1) { + c = rate / (1 << div); + c = c * period_ns; + do_div(c, 1000000000); + if (c < PERIOD_PERIOD_MAX) + break; + div++; + if (div > PERIOD_CDIV_MAX) + return -EINVAL; + } + + period_cycles = c; + c *= duty_ns; + do_div(c, period_ns); + duty_cycles = c; + + /* + * If the PWM channel is disabled, make sure to turn on the clock + * before writing the register. Otherwise, keep it enabled. + */ + if (!test_bit(PWMF_ENABLED, &pwm->flags)) { + ret = clk_prepare_enable(mxs->clk); + if (ret) + return ret; + } + + writel(duty_cycles << 16, + mxs->base + PWM_ACTIVE0 + pwm->hwpwm * 0x20); + writel(PERIOD_PERIOD(period_cycles) | PERIOD_ACTIVE_HIGH | + PERIOD_INACTIVE_LOW | PERIOD_CDIV(div), + mxs->base + PWM_PERIOD0 + pwm->hwpwm * 0x20); + + /* + * If the PWM is not enabled, turn the clock off again to save power. + */ + if (!test_bit(PWMF_ENABLED, &pwm->flags)) + clk_disable_unprepare(mxs->clk); + + return 0; +} + +static int mxs_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip); + int ret; + + ret = clk_prepare_enable(mxs->clk); + if (ret) + return ret; + + writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + SET); + + return 0; +} + +static void mxs_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip); + + writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + CLR); + + clk_disable_unprepare(mxs->clk); +} + +static const struct pwm_ops mxs_pwm_ops = { + .config = mxs_pwm_config, + .enable = mxs_pwm_enable, + .disable = mxs_pwm_disable, + .owner = THIS_MODULE, +}; + +static int mxs_pwm_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct mxs_pwm_chip *mxs; + struct resource *res; + struct pinctrl *pinctrl; + int ret; + + mxs = devm_kzalloc(&pdev->dev, sizeof(*mxs), GFP_KERNEL); + if (!mxs) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mxs->base = devm_request_and_ioremap(&pdev->dev, res); + if (!mxs->base) + return -EADDRNOTAVAIL; + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + return PTR_ERR(pinctrl); + + mxs->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mxs->clk)) + return PTR_ERR(mxs->clk); + + mxs->chip.dev = &pdev->dev; + mxs->chip.ops = &mxs_pwm_ops; + mxs->chip.base = -1; + ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get pwm number: %d\n", ret); + return ret; + } + + ret = pwmchip_add(&mxs->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add pwm chip %d\n", ret); + return ret; + } + + mxs->dev = &pdev->dev; + platform_set_drvdata(pdev, mxs); + + stmp_reset_block(mxs->base); + + return 0; +} + +static int __devexit mxs_pwm_remove(struct platform_device *pdev) +{ + struct mxs_pwm_chip *mxs = platform_get_drvdata(pdev); + + return pwmchip_remove(&mxs->chip); +} + +static struct of_device_id mxs_pwm_dt_ids[] = { + { .compatible = "fsl,imx23-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_pwm_dt_ids); + +static struct platform_driver mxs_pwm_driver = { + .driver = { + .name = "mxs-pwm", + .of_match_table = of_match_ptr(mxs_pwm_dt_ids), + }, + .probe = mxs_pwm_probe, + .remove = __devexit_p(mxs_pwm_remove), +}; +module_platform_driver(mxs_pwm_driver); + +MODULE_ALIAS("platform:mxs-pwm"); +MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); +MODULE_DESCRIPTION("Freescale MXS PWM Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c new file mode 100644 index 000000000000..bd5867a1c700 --- /dev/null +++ b/drivers/pwm/pwm-pxa.c @@ -0,0 +1,218 @@ +/* + * drivers/pwm/pwm-pxa.c + * + * simple driver for PWM (Pulse Width Modulator) controller + * + * 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. + * + * 2008-02-13 initial version + * eric miao <eric.miao@marvell.com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/pwm.h> + +#include <asm/div64.h> + +#define HAS_SECONDARY_PWM 0x10 +#define PWM_ID_BASE(d) ((d) & 0xf) + +static const struct platform_device_id pwm_id_table[] = { + /* PWM has_secondary_pwm? */ + { "pxa25x-pwm", 0 }, + { "pxa27x-pwm", 0 | HAS_SECONDARY_PWM }, + { "pxa168-pwm", 1 }, + { "pxa910-pwm", 1 }, + { }, +}; +MODULE_DEVICE_TABLE(platform, pwm_id_table); + +/* PWM registers and bits definitions */ +#define PWMCR (0x00) +#define PWMDCR (0x04) +#define PWMPCR (0x08) + +#define PWMCR_SD (1 << 6) +#define PWMDCR_FD (1 << 10) + +struct pxa_pwm_chip { + struct pwm_chip chip; + struct device *dev; + + struct clk *clk; + int clk_enabled; + void __iomem *mmio_base; +}; + +static inline struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct pxa_pwm_chip, chip); +} + +/* + * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE + * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE + */ +static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip); + unsigned long long c; + unsigned long period_cycles, prescale, pv, dc; + unsigned long offset; + int rc; + + if (period_ns == 0 || duty_ns > period_ns) + return -EINVAL; + + offset = pwm->hwpwm ? 0x10 : 0; + + c = clk_get_rate(pc->clk); + c = c * period_ns; + do_div(c, 1000000000); + period_cycles = c; + + if (period_cycles < 1) + period_cycles = 1; + prescale = (period_cycles - 1) / 1024; + pv = period_cycles / (prescale + 1) - 1; + + if (prescale > 63) + return -EINVAL; + + if (duty_ns == period_ns) + dc = PWMDCR_FD; + else + dc = (pv + 1) * duty_ns / period_ns; + + /* NOTE: the clock to PWM has to be enabled first + * before writing to the registers + */ + rc = clk_prepare_enable(pc->clk); + if (rc < 0) + return rc; + + writel(prescale, pc->mmio_base + offset + PWMCR); + writel(dc, pc->mmio_base + offset + PWMDCR); + writel(pv, pc->mmio_base + offset + PWMPCR); + + clk_disable_unprepare(pc->clk); + return 0; +} + +static int pxa_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip); + int rc = 0; + + if (!pc->clk_enabled) { + rc = clk_prepare_enable(pc->clk); + if (!rc) + pc->clk_enabled++; + } + return rc; +} + +static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip); + + if (pc->clk_enabled) { + clk_disable_unprepare(pc->clk); + pc->clk_enabled--; + } +} + +static struct pwm_ops pxa_pwm_ops = { + .config = pxa_pwm_config, + .enable = pxa_pwm_enable, + .disable = pxa_pwm_disable, + .owner = THIS_MODULE, +}; + +static int __devinit pwm_probe(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + struct pxa_pwm_chip *pwm; + struct resource *r; + int ret = 0; + + pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); + if (pwm == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + pwm->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pwm->clk)) + return PTR_ERR(pwm->clk); + + pwm->clk_enabled = 0; + + pwm->chip.dev = &pdev->dev; + pwm->chip.ops = &pxa_pwm_ops; + pwm->chip.base = -1; + pwm->chip.npwm = (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no memory resource defined\n"); + return -ENODEV; + } + + pwm->mmio_base = devm_request_and_ioremap(&pdev->dev, r); + if (pwm->mmio_base == NULL) + return -EADDRNOTAVAIL; + + ret = pwmchip_add(&pwm->chip); + if (ret < 0) { + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, pwm); + return 0; +} + +static int __devexit pwm_remove(struct platform_device *pdev) +{ + struct pxa_pwm_chip *chip; + + chip = platform_get_drvdata(pdev); + if (chip == NULL) + return -ENODEV; + + return pwmchip_remove(&chip->chip); +} + +static struct platform_driver pwm_driver = { + .driver = { + .name = "pxa25x-pwm", + .owner = THIS_MODULE, + }, + .probe = pwm_probe, + .remove = __devexit_p(pwm_remove), + .id_table = pwm_id_table, +}; + +static int __init pwm_init(void) +{ + return platform_driver_register(&pwm_driver); +} +arch_initcall(pwm_init); + +static void __exit pwm_exit(void) +{ + platform_driver_unregister(&pwm_driver); +} +module_exit(pwm_exit); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c new file mode 100644 index 000000000000..d10386528c9c --- /dev/null +++ b/drivers/pwm/pwm-samsung.c @@ -0,0 +1,356 @@ +/* drivers/pwm/pwm-samsung.c + * + * Copyright (c) 2007 Ben Dooks + * Copyright (c) 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org> + * + * S3C series PWM device core + * + * 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. +*/ + +#define pr_fmt(fmt) "pwm-samsung: " fmt + +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/pwm.h> + +#include <mach/map.h> + +#include <plat/regs-timer.h> + +struct s3c_chip { + struct platform_device *pdev; + + struct clk *clk_div; + struct clk *clk; + const char *label; + + unsigned int period_ns; + unsigned int duty_ns; + + unsigned char tcon_base; + unsigned char pwm_id; + struct pwm_chip chip; +}; + +#define to_s3c_chip(chip) container_of(chip, struct s3c_chip, chip) + +#define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg) + +static struct clk *clk_scaler[2]; + +static inline int pwm_is_tdiv(struct s3c_chip *chip) +{ + return clk_get_parent(chip->clk) == chip->clk_div; +} + +#define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0)) +#define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2)) +#define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3)) +#define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1)) + +static int s3c_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct s3c_chip *s3c = to_s3c_chip(chip); + unsigned long flags; + unsigned long tcon; + + local_irq_save(flags); + + tcon = __raw_readl(S3C2410_TCON); + tcon |= pwm_tcon_start(s3c); + __raw_writel(tcon, S3C2410_TCON); + + local_irq_restore(flags); + + return 0; +} + +static void s3c_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct s3c_chip *s3c = to_s3c_chip(chip); + unsigned long flags; + unsigned long tcon; + + local_irq_save(flags); + + tcon = __raw_readl(S3C2410_TCON); + tcon &= ~pwm_tcon_start(s3c); + __raw_writel(tcon, S3C2410_TCON); + + local_irq_restore(flags); +} + +static unsigned long pwm_calc_tin(struct s3c_chip *s3c, unsigned long freq) +{ + unsigned long tin_parent_rate; + unsigned int div; + + tin_parent_rate = clk_get_rate(clk_get_parent(s3c->clk_div)); + pwm_dbg(s3c, "tin parent at %lu\n", tin_parent_rate); + + for (div = 2; div <= 16; div *= 2) { + if ((tin_parent_rate / (div << 16)) < freq) + return tin_parent_rate / div; + } + + return tin_parent_rate / 16; +} + +#define NS_IN_HZ (1000000000UL) + +static int s3c_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct s3c_chip *s3c = to_s3c_chip(chip); + unsigned long tin_rate; + unsigned long tin_ns; + unsigned long period; + unsigned long flags; + unsigned long tcon; + unsigned long tcnt; + long tcmp; + + /* We currently avoid using 64bit arithmetic by using the + * fact that anything faster than 1Hz is easily representable + * by 32bits. */ + + if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ) + return -ERANGE; + + if (duty_ns > period_ns) + return -EINVAL; + + if (period_ns == s3c->period_ns && + duty_ns == s3c->duty_ns) + return 0; + + /* The TCMP and TCNT can be read without a lock, they're not + * shared between the timers. */ + + tcmp = __raw_readl(S3C2410_TCMPB(s3c->pwm_id)); + tcnt = __raw_readl(S3C2410_TCNTB(s3c->pwm_id)); + + period = NS_IN_HZ / period_ns; + + pwm_dbg(s3c, "duty_ns=%d, period_ns=%d (%lu)\n", + duty_ns, period_ns, period); + + /* Check to see if we are changing the clock rate of the PWM */ + + if (s3c->period_ns != period_ns) { + if (pwm_is_tdiv(s3c)) { + tin_rate = pwm_calc_tin(s3c, period); + clk_set_rate(s3c->clk_div, tin_rate); + } else + tin_rate = clk_get_rate(s3c->clk); + + s3c->period_ns = period_ns; + + pwm_dbg(s3c, "tin_rate=%lu\n", tin_rate); + + tin_ns = NS_IN_HZ / tin_rate; + tcnt = period_ns / tin_ns; + } else + tin_ns = NS_IN_HZ / clk_get_rate(s3c->clk); + + /* Note, counters count down */ + + tcmp = duty_ns / tin_ns; + tcmp = tcnt - tcmp; + /* the pwm hw only checks the compare register after a decrement, + so the pin never toggles if tcmp = tcnt */ + if (tcmp == tcnt) + tcmp--; + + pwm_dbg(s3c, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt); + + if (tcmp < 0) + tcmp = 0; + + /* Update the PWM register block. */ + + local_irq_save(flags); + + __raw_writel(tcmp, S3C2410_TCMPB(s3c->pwm_id)); + __raw_writel(tcnt, S3C2410_TCNTB(s3c->pwm_id)); + + tcon = __raw_readl(S3C2410_TCON); + tcon |= pwm_tcon_manulupdate(s3c); + tcon |= pwm_tcon_autoreload(s3c); + __raw_writel(tcon, S3C2410_TCON); + + tcon &= ~pwm_tcon_manulupdate(s3c); + __raw_writel(tcon, S3C2410_TCON); + + local_irq_restore(flags); + + return 0; +} + +static struct pwm_ops s3c_pwm_ops = { + .enable = s3c_pwm_enable, + .disable = s3c_pwm_disable, + .config = s3c_pwm_config, + .owner = THIS_MODULE, +}; + +static int s3c_pwm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct s3c_chip *s3c; + unsigned long flags; + unsigned long tcon; + unsigned int id = pdev->id; + int ret; + + if (id == 4) { + dev_err(dev, "TIMER4 is currently not supported\n"); + return -ENXIO; + } + + s3c = devm_kzalloc(&pdev->dev, sizeof(*s3c), GFP_KERNEL); + if (s3c == NULL) { + dev_err(dev, "failed to allocate pwm_device\n"); + return -ENOMEM; + } + + /* calculate base of control bits in TCON */ + s3c->tcon_base = id == 0 ? 0 : (id * 4) + 4; + s3c->chip.ops = &s3c_pwm_ops; + s3c->chip.base = -1; + s3c->chip.npwm = 1; + + s3c->clk = devm_clk_get(dev, "pwm-tin"); + if (IS_ERR(s3c->clk)) { + dev_err(dev, "failed to get pwm tin clk\n"); + return PTR_ERR(s3c->clk); + } + + s3c->clk_div = devm_clk_get(dev, "pwm-tdiv"); + if (IS_ERR(s3c->clk_div)) { + dev_err(dev, "failed to get pwm tdiv clk\n"); + return PTR_ERR(s3c->clk_div); + } + + clk_enable(s3c->clk); + clk_enable(s3c->clk_div); + + local_irq_save(flags); + + tcon = __raw_readl(S3C2410_TCON); + tcon |= pwm_tcon_invert(s3c); + __raw_writel(tcon, S3C2410_TCON); + + local_irq_restore(flags); + + ret = pwmchip_add(&s3c->chip); + if (ret < 0) { + dev_err(dev, "failed to register pwm\n"); + goto err_clk_tdiv; + } + + pwm_dbg(s3c, "config bits %02x\n", + (__raw_readl(S3C2410_TCON) >> s3c->tcon_base) & 0x0f); + + dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n", + clk_get_rate(s3c->clk), + clk_get_rate(s3c->clk_div), + pwm_is_tdiv(s3c) ? "div" : "ext", s3c->tcon_base); + + platform_set_drvdata(pdev, s3c); + return 0; + + err_clk_tdiv: + clk_disable(s3c->clk_div); + clk_disable(s3c->clk); + return ret; +} + +static int __devexit s3c_pwm_remove(struct platform_device *pdev) +{ + struct s3c_chip *s3c = platform_get_drvdata(pdev); + int err; + + err = pwmchip_remove(&s3c->chip); + if (err < 0) + return err; + + clk_disable(s3c->clk_div); + clk_disable(s3c->clk); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct s3c_chip *s3c = platform_get_drvdata(pdev); + + /* No one preserve these values during suspend so reset them + * Otherwise driver leaves PWM unconfigured if same values + * passed to pwm_config + */ + s3c->period_ns = 0; + s3c->duty_ns = 0; + + return 0; +} + +static int s3c_pwm_resume(struct platform_device *pdev) +{ + struct s3c_chip *s3c = platform_get_drvdata(pdev); + unsigned long tcon; + + /* Restore invertion */ + tcon = __raw_readl(S3C2410_TCON); + tcon |= pwm_tcon_invert(s3c); + __raw_writel(tcon, S3C2410_TCON); + + return 0; +} + +#else +#define s3c_pwm_suspend NULL +#define s3c_pwm_resume NULL +#endif + +static struct platform_driver s3c_pwm_driver = { + .driver = { + .name = "s3c24xx-pwm", + .owner = THIS_MODULE, + }, + .probe = s3c_pwm_probe, + .remove = __devexit_p(s3c_pwm_remove), + .suspend = s3c_pwm_suspend, + .resume = s3c_pwm_resume, +}; + +static int __init pwm_init(void) +{ + int ret; + + clk_scaler[0] = clk_get(NULL, "pwm-scaler0"); + clk_scaler[1] = clk_get(NULL, "pwm-scaler1"); + + if (IS_ERR(clk_scaler[0]) || IS_ERR(clk_scaler[1])) { + pr_err("failed to get scaler clocks\n"); + return -EINVAL; + } + + ret = platform_driver_register(&s3c_pwm_driver); + if (ret) + pr_err("failed to add pwm driver\n"); + + return ret; +} + +arch_initcall(pwm_init); diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c new file mode 100644 index 000000000000..02ce18d5e49a --- /dev/null +++ b/drivers/pwm/pwm-tegra.c @@ -0,0 +1,261 @@ +/* + * drivers/pwm/pwm-tegra.c + * + * Tegra pulse-width-modulation controller driver + * + * Copyright (c) 2010, NVIDIA Corporation. + * Based on arch/arm/plat-mxc/pwm.c by Sascha Hauer <s.hauer@pengutronix.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; 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/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pwm.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define PWM_ENABLE (1 << 31) +#define PWM_DUTY_WIDTH 8 +#define PWM_DUTY_SHIFT 16 +#define PWM_SCALE_WIDTH 13 +#define PWM_SCALE_SHIFT 0 + +#define NUM_PWM 4 + +struct tegra_pwm_chip { + struct pwm_chip chip; + struct device *dev; + + struct clk *clk; + + void __iomem *mmio_base; +}; + +static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct tegra_pwm_chip, chip); +} + +static inline u32 pwm_readl(struct tegra_pwm_chip *chip, unsigned int num) +{ + return readl(chip->mmio_base + (num << 4)); +} + +static inline void pwm_writel(struct tegra_pwm_chip *chip, unsigned int num, + unsigned long val) +{ + writel(val, chip->mmio_base + (num << 4)); +} + +static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip); + unsigned long long c; + unsigned long rate, hz; + u32 val = 0; + int err; + + /* + * Convert from duty_ns / period_ns to a fixed number of duty ticks + * per (1 << PWM_DUTY_WIDTH) cycles and make sure to round to the + * nearest integer during division. + */ + c = duty_ns * ((1 << PWM_DUTY_WIDTH) - 1) + period_ns / 2; + do_div(c, period_ns); + + val = (u32)c << PWM_DUTY_SHIFT; + + /* + * Compute the prescaler value for which (1 << PWM_DUTY_WIDTH) + * cycles at the PWM clock rate will take period_ns nanoseconds. + */ + rate = clk_get_rate(pc->clk) >> PWM_DUTY_WIDTH; + hz = 1000000000ul / period_ns; + + rate = (rate + (hz / 2)) / hz; + + /* + * Since the actual PWM divider is the register's frequency divider + * field minus 1, we need to decrement to get the correct value to + * write to the register. + */ + if (rate > 0) + rate--; + + /* + * Make sure that the rate will fit in the register's frequency + * divider field. + */ + if (rate >> PWM_SCALE_WIDTH) + return -EINVAL; + + val |= rate << PWM_SCALE_SHIFT; + + /* + * If the PWM channel is disabled, make sure to turn on the clock + * before writing the register. Otherwise, keep it enabled. + */ + if (!test_bit(PWMF_ENABLED, &pwm->flags)) { + err = clk_prepare_enable(pc->clk); + if (err < 0) + return err; + } else + val |= PWM_ENABLE; + + pwm_writel(pc, pwm->hwpwm, val); + + /* + * If the PWM is not enabled, turn the clock off again to save power. + */ + if (!test_bit(PWMF_ENABLED, &pwm->flags)) + clk_disable_unprepare(pc->clk); + + return 0; +} + +static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip); + int rc = 0; + u32 val; + + rc = clk_prepare_enable(pc->clk); + if (rc < 0) + return rc; + + val = pwm_readl(pc, pwm->hwpwm); + val |= PWM_ENABLE; + pwm_writel(pc, pwm->hwpwm, val); + + return 0; +} + +static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip); + u32 val; + + val = pwm_readl(pc, pwm->hwpwm); + val &= ~PWM_ENABLE; + pwm_writel(pc, pwm->hwpwm, val); + + clk_disable_unprepare(pc->clk); +} + +static const struct pwm_ops tegra_pwm_ops = { + .config = tegra_pwm_config, + .enable = tegra_pwm_enable, + .disable = tegra_pwm_disable, + .owner = THIS_MODULE, +}; + +static int tegra_pwm_probe(struct platform_device *pdev) +{ + struct tegra_pwm_chip *pwm; + struct resource *r; + int ret; + + pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); + if (!pwm) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + pwm->dev = &pdev->dev; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resources defined\n"); + return -ENODEV; + } + + pwm->mmio_base = devm_request_and_ioremap(&pdev->dev, r); + if (!pwm->mmio_base) { + dev_err(&pdev->dev, "failed to ioremap() region\n"); + return -EADDRNOTAVAIL; + } + + platform_set_drvdata(pdev, pwm); + + pwm->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pwm->clk)) + return PTR_ERR(pwm->clk); + + pwm->chip.dev = &pdev->dev; + pwm->chip.ops = &tegra_pwm_ops; + pwm->chip.base = -1; + pwm->chip.npwm = NUM_PWM; + + ret = pwmchip_add(&pwm->chip); + if (ret < 0) { + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int __devexit tegra_pwm_remove(struct platform_device *pdev) +{ + struct tegra_pwm_chip *pc = platform_get_drvdata(pdev); + int i; + + if (WARN_ON(!pc)) + return -ENODEV; + + for (i = 0; i < NUM_PWM; i++) { + struct pwm_device *pwm = &pc->chip.pwms[i]; + + if (!test_bit(PWMF_ENABLED, &pwm->flags)) + if (clk_prepare_enable(pc->clk) < 0) + continue; + + pwm_writel(pc, i, 0); + + clk_disable_unprepare(pc->clk); + } + + return pwmchip_remove(&pc->chip); +} + +#ifdef CONFIG_OF +static struct of_device_id tegra_pwm_of_match[] = { + { .compatible = "nvidia,tegra20-pwm" }, + { .compatible = "nvidia,tegra30-pwm" }, + { } +}; + +MODULE_DEVICE_TABLE(of, tegra_pwm_of_match); +#endif + +static struct platform_driver tegra_pwm_driver = { + .driver = { + .name = "tegra-pwm", + .of_match_table = of_match_ptr(tegra_pwm_of_match), + }, + .probe = tegra_pwm_probe, + .remove = __devexit_p(tegra_pwm_remove), +}; + +module_platform_driver(tegra_pwm_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("NVIDIA Corporation"); +MODULE_ALIAS("platform:tegra-pwm"); diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c new file mode 100644 index 000000000000..3c2ad284ee3e --- /dev/null +++ b/drivers/pwm/pwm-tiecap.c @@ -0,0 +1,232 @@ +/* + * ECAP PWM driver + * + * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.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. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <linux/pwm.h> + +/* ECAP registers and bits definitions */ +#define CAP1 0x08 +#define CAP2 0x0C +#define CAP3 0x10 +#define CAP4 0x14 +#define ECCTL2 0x2A +#define ECCTL2_APWM_MODE BIT(9) +#define ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6)) +#define ECCTL2_TSCTR_FREERUN BIT(4) + +struct ecap_pwm_chip { + struct pwm_chip chip; + unsigned int clk_rate; + void __iomem *mmio_base; +}; + +static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct ecap_pwm_chip, chip); +} + +/* + * period_ns = 10^9 * period_cycles / PWM_CLK_RATE + * duty_ns = 10^9 * duty_cycles / PWM_CLK_RATE + */ +static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); + unsigned long long c; + unsigned long period_cycles, duty_cycles; + unsigned int reg_val; + + if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC) + return -ERANGE; + + c = pc->clk_rate; + c = c * period_ns; + do_div(c, NSEC_PER_SEC); + period_cycles = (unsigned long)c; + + if (period_cycles < 1) { + period_cycles = 1; + duty_cycles = 1; + } else { + c = pc->clk_rate; + c = c * duty_ns; + do_div(c, NSEC_PER_SEC); + duty_cycles = (unsigned long)c; + } + + pm_runtime_get_sync(pc->chip.dev); + + reg_val = readw(pc->mmio_base + ECCTL2); + + /* Configure APWM mode & disable sync option */ + reg_val |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA; + + writew(reg_val, pc->mmio_base + ECCTL2); + + if (!test_bit(PWMF_ENABLED, &pwm->flags)) { + /* Update active registers if not running */ + writel(duty_cycles, pc->mmio_base + CAP2); + writel(period_cycles, pc->mmio_base + CAP1); + } else { + /* + * Update shadow registers to configure period and + * compare values. This helps current PWM period to + * complete on reconfiguring + */ + writel(duty_cycles, pc->mmio_base + CAP4); + writel(period_cycles, pc->mmio_base + CAP3); + } + + pm_runtime_put_sync(pc->chip.dev); + return 0; +} + +static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); + unsigned int reg_val; + + /* Leave clock enabled on enabling PWM */ + pm_runtime_get_sync(pc->chip.dev); + + /* + * Enable 'Free run Time stamp counter mode' to start counter + * and 'APWM mode' to enable APWM output + */ + reg_val = readw(pc->mmio_base + ECCTL2); + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE; + writew(reg_val, pc->mmio_base + ECCTL2); + return 0; +} + +static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); + unsigned int reg_val; + + /* + * Disable 'Free run Time stamp counter mode' to stop counter + * and 'APWM mode' to put APWM output to low + */ + reg_val = readw(pc->mmio_base + ECCTL2); + reg_val &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE); + writew(reg_val, pc->mmio_base + ECCTL2); + + /* Disable clock on PWM disable */ + pm_runtime_put_sync(pc->chip.dev); +} + +static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + if (test_bit(PWMF_ENABLED, &pwm->flags)) { + dev_warn(chip->dev, "Removing PWM device without disabling\n"); + pm_runtime_put_sync(chip->dev); + } +} + +static const struct pwm_ops ecap_pwm_ops = { + .free = ecap_pwm_free, + .config = ecap_pwm_config, + .enable = ecap_pwm_enable, + .disable = ecap_pwm_disable, + .owner = THIS_MODULE, +}; + +static int __devinit ecap_pwm_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + struct clk *clk; + struct ecap_pwm_chip *pc; + + pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); + if (!pc) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + clk = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + return PTR_ERR(clk); + } + + pc->clk_rate = clk_get_rate(clk); + if (!pc->clk_rate) { + dev_err(&pdev->dev, "failed to get clock rate\n"); + return -EINVAL; + } + + pc->chip.dev = &pdev->dev; + pc->chip.ops = &ecap_pwm_ops; + pc->chip.base = -1; + pc->chip.npwm = 1; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resource defined\n"); + return -ENODEV; + } + + pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r); + if (!pc->mmio_base) { + dev_err(&pdev->dev, "failed to ioremap() registers\n"); + return -EADDRNOTAVAIL; + } + + ret = pwmchip_add(&pc->chip); + if (ret < 0) { + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + return ret; + } + + pm_runtime_enable(&pdev->dev); + platform_set_drvdata(pdev, pc); + return 0; +} + +static int __devexit ecap_pwm_remove(struct platform_device *pdev) +{ + struct ecap_pwm_chip *pc = platform_get_drvdata(pdev); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return pwmchip_remove(&pc->chip); +} + +static struct platform_driver ecap_pwm_driver = { + .driver = { + .name = "ecap", + }, + .probe = ecap_pwm_probe, + .remove = __devexit_p(ecap_pwm_remove), +}; + +module_platform_driver(ecap_pwm_driver); + +MODULE_DESCRIPTION("ECAP PWM driver"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c new file mode 100644 index 000000000000..010d232cb0c8 --- /dev/null +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -0,0 +1,411 @@ +/* + * EHRPWM PWM driver + * + * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.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. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> + +/* EHRPWM registers and bits definitions */ + +/* Time base module registers */ +#define TBCTL 0x00 +#define TBPRD 0x0A + +#define TBCTL_RUN_MASK (BIT(15) | BIT(14)) +#define TBCTL_STOP_NEXT 0 +#define TBCTL_STOP_ON_CYCLE BIT(14) +#define TBCTL_FREE_RUN (BIT(15) | BIT(14)) +#define TBCTL_PRDLD_MASK BIT(3) +#define TBCTL_PRDLD_SHDW 0 +#define TBCTL_PRDLD_IMDT BIT(3) +#define TBCTL_CLKDIV_MASK (BIT(12) | BIT(11) | BIT(10) | BIT(9) | \ + BIT(8) | BIT(7)) +#define TBCTL_CTRMODE_MASK (BIT(1) | BIT(0)) +#define TBCTL_CTRMODE_UP 0 +#define TBCTL_CTRMODE_DOWN BIT(0) +#define TBCTL_CTRMODE_UPDOWN BIT(1) +#define TBCTL_CTRMODE_FREEZE (BIT(1) | BIT(0)) + +#define TBCTL_HSPCLKDIV_SHIFT 7 +#define TBCTL_CLKDIV_SHIFT 10 + +#define CLKDIV_MAX 7 +#define HSPCLKDIV_MAX 7 +#define PERIOD_MAX 0xFFFF + +/* compare module registers */ +#define CMPA 0x12 +#define CMPB 0x14 + +/* Action qualifier module registers */ +#define AQCTLA 0x16 +#define AQCTLB 0x18 +#define AQSFRC 0x1A +#define AQCSFRC 0x1C + +#define AQCTL_CBU_MASK (BIT(9) | BIT(8)) +#define AQCTL_CBU_FRCLOW BIT(8) +#define AQCTL_CBU_FRCHIGH BIT(9) +#define AQCTL_CBU_FRCTOGGLE (BIT(9) | BIT(8)) +#define AQCTL_CAU_MASK (BIT(5) | BIT(4)) +#define AQCTL_CAU_FRCLOW BIT(4) +#define AQCTL_CAU_FRCHIGH BIT(5) +#define AQCTL_CAU_FRCTOGGLE (BIT(5) | BIT(4)) +#define AQCTL_PRD_MASK (BIT(3) | BIT(2)) +#define AQCTL_PRD_FRCLOW BIT(2) +#define AQCTL_PRD_FRCHIGH BIT(3) +#define AQCTL_PRD_FRCTOGGLE (BIT(3) | BIT(2)) +#define AQCTL_ZRO_MASK (BIT(1) | BIT(0)) +#define AQCTL_ZRO_FRCLOW BIT(0) +#define AQCTL_ZRO_FRCHIGH BIT(1) +#define AQCTL_ZRO_FRCTOGGLE (BIT(1) | BIT(0)) + +#define AQSFRC_RLDCSF_MASK (BIT(7) | BIT(6)) +#define AQSFRC_RLDCSF_ZRO 0 +#define AQSFRC_RLDCSF_PRD BIT(6) +#define AQSFRC_RLDCSF_ZROPRD BIT(7) +#define AQSFRC_RLDCSF_IMDT (BIT(7) | BIT(6)) + +#define AQCSFRC_CSFB_MASK (BIT(3) | BIT(2)) +#define AQCSFRC_CSFB_FRCDIS 0 +#define AQCSFRC_CSFB_FRCLOW BIT(2) +#define AQCSFRC_CSFB_FRCHIGH BIT(3) +#define AQCSFRC_CSFB_DISSWFRC (BIT(3) | BIT(2)) +#define AQCSFRC_CSFA_MASK (BIT(1) | BIT(0)) +#define AQCSFRC_CSFA_FRCDIS 0 +#define AQCSFRC_CSFA_FRCLOW BIT(0) +#define AQCSFRC_CSFA_FRCHIGH BIT(1) +#define AQCSFRC_CSFA_DISSWFRC (BIT(1) | BIT(0)) + +#define NUM_PWM_CHANNEL 2 /* EHRPWM channels */ + +struct ehrpwm_pwm_chip { + struct pwm_chip chip; + unsigned int clk_rate; + void __iomem *mmio_base; +}; + +static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct ehrpwm_pwm_chip, chip); +} + +static void ehrpwm_write(void *base, int offset, unsigned int val) +{ + writew(val & 0xFFFF, base + offset); +} + +static void ehrpwm_modify(void *base, int offset, + unsigned short mask, unsigned short val) +{ + unsigned short regval; + + regval = readw(base + offset); + regval &= ~mask; + regval |= val & mask; + writew(regval, base + offset); +} + +/** + * set_prescale_div - Set up the prescaler divider function + * @rqst_prescaler: prescaler value min + * @prescale_div: prescaler value set + * @tb_clk_div: Time Base Control prescaler bits + */ +static int set_prescale_div(unsigned long rqst_prescaler, + unsigned short *prescale_div, unsigned short *tb_clk_div) +{ + unsigned int clkdiv, hspclkdiv; + + for (clkdiv = 0; clkdiv <= CLKDIV_MAX; clkdiv++) { + for (hspclkdiv = 0; hspclkdiv <= HSPCLKDIV_MAX; hspclkdiv++) { + + /* + * calculations for prescaler value : + * prescale_div = HSPCLKDIVIDER * CLKDIVIDER. + * HSPCLKDIVIDER = 2 ** hspclkdiv + * CLKDIVIDER = (1), if clkdiv == 0 *OR* + * (2 * clkdiv), if clkdiv != 0 + * + * Configure prescale_div value such that period + * register value is less than 65535. + */ + + *prescale_div = (1 << clkdiv) * + (hspclkdiv ? (hspclkdiv * 2) : 1); + if (*prescale_div > rqst_prescaler) { + *tb_clk_div = (clkdiv << TBCTL_CLKDIV_SHIFT) | + (hspclkdiv << TBCTL_HSPCLKDIV_SHIFT); + return 0; + } + } + } + return 1; +} + +static void configure_chans(struct ehrpwm_pwm_chip *pc, int chan, + unsigned long duty_cycles) +{ + int cmp_reg, aqctl_reg; + unsigned short aqctl_val, aqctl_mask; + + /* + * Channels can be configured from action qualifier module. + * Channel 0 configured with compare A register and for + * up-counter mode. + * Channel 1 configured with compare B register and for + * up-counter mode. + */ + if (chan == 1) { + aqctl_reg = AQCTLB; + cmp_reg = CMPB; + /* Configure PWM Low from compare B value */ + aqctl_val = AQCTL_CBU_FRCLOW; + aqctl_mask = AQCTL_CBU_MASK; + } else { + cmp_reg = CMPA; + aqctl_reg = AQCTLA; + /* Configure PWM Low from compare A value*/ + aqctl_val = AQCTL_CAU_FRCLOW; + aqctl_mask = AQCTL_CAU_MASK; + } + + /* Configure PWM High from period value and zero value */ + aqctl_val |= AQCTL_PRD_FRCHIGH | AQCTL_ZRO_FRCHIGH; + aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK; + ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val); + + ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); +} + +/* + * period_ns = 10^9 * (ps_divval * period_cycles) / PWM_CLK_RATE + * duty_ns = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE + */ +static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); + unsigned long long c; + unsigned long period_cycles, duty_cycles; + unsigned short ps_divval, tb_divval; + + if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC) + return -ERANGE; + + c = pc->clk_rate; + c = c * period_ns; + do_div(c, NSEC_PER_SEC); + period_cycles = (unsigned long)c; + + if (period_cycles < 1) { + period_cycles = 1; + duty_cycles = 1; + } else { + c = pc->clk_rate; + c = c * duty_ns; + do_div(c, NSEC_PER_SEC); + duty_cycles = (unsigned long)c; + } + + /* Configure clock prescaler to support Low frequency PWM wave */ + if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval, + &tb_divval)) { + dev_err(chip->dev, "Unsupported values\n"); + return -EINVAL; + } + + pm_runtime_get_sync(chip->dev); + + /* Update clock prescaler values */ + ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CLKDIV_MASK, tb_divval); + + /* Update period & duty cycle with presacler division */ + period_cycles = period_cycles / ps_divval; + duty_cycles = duty_cycles / ps_divval; + + /* Configure shadow loading on Period register */ + ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_PRDLD_MASK, TBCTL_PRDLD_SHDW); + + ehrpwm_write(pc->mmio_base, TBPRD, period_cycles); + + /* Configure ehrpwm counter for up-count mode */ + ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK, + TBCTL_CTRMODE_UP); + + /* Configure the channel for duty cycle */ + configure_chans(pc, pwm->hwpwm, duty_cycles); + pm_runtime_put_sync(chip->dev); + return 0; +} + +static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); + unsigned short aqcsfrc_val, aqcsfrc_mask; + + /* Leave clock enabled on enabling PWM */ + pm_runtime_get_sync(chip->dev); + + /* Disabling Action Qualifier on PWM output */ + if (pwm->hwpwm) { + aqcsfrc_val = AQCSFRC_CSFB_FRCDIS; + aqcsfrc_mask = AQCSFRC_CSFB_MASK; + } else { + aqcsfrc_val = AQCSFRC_CSFA_FRCDIS; + aqcsfrc_mask = AQCSFRC_CSFA_MASK; + } + + /* Changes to shadow mode */ + ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK, + AQSFRC_RLDCSF_ZRO); + + ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); + + /* Enable time counter for free_run */ + ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); + return 0; +} + +static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); + unsigned short aqcsfrc_val, aqcsfrc_mask; + + /* Action Qualifier puts PWM output low forcefully */ + if (pwm->hwpwm) { + aqcsfrc_val = AQCSFRC_CSFB_FRCLOW; + aqcsfrc_mask = AQCSFRC_CSFB_MASK; + } else { + aqcsfrc_val = AQCSFRC_CSFA_FRCLOW; + aqcsfrc_mask = AQCSFRC_CSFA_MASK; + } + + /* + * Changes to immediate action on Action Qualifier. This puts + * Action Qualifier control on PWM output from next TBCLK + */ + ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK, + AQSFRC_RLDCSF_IMDT); + + ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); + + /* Stop Time base counter */ + ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT); + + /* Disable clock on PWM disable */ + pm_runtime_put_sync(chip->dev); +} + +static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + if (test_bit(PWMF_ENABLED, &pwm->flags)) { + dev_warn(chip->dev, "Removing PWM device without disabling\n"); + pm_runtime_put_sync(chip->dev); + } +} + +static const struct pwm_ops ehrpwm_pwm_ops = { + .free = ehrpwm_pwm_free, + .config = ehrpwm_pwm_config, + .enable = ehrpwm_pwm_enable, + .disable = ehrpwm_pwm_disable, + .owner = THIS_MODULE, +}; + +static int __devinit ehrpwm_pwm_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + struct clk *clk; + struct ehrpwm_pwm_chip *pc; + + pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); + if (!pc) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + clk = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + return PTR_ERR(clk); + } + + pc->clk_rate = clk_get_rate(clk); + if (!pc->clk_rate) { + dev_err(&pdev->dev, "failed to get clock rate\n"); + return -EINVAL; + } + + pc->chip.dev = &pdev->dev; + pc->chip.ops = &ehrpwm_pwm_ops; + pc->chip.base = -1; + pc->chip.npwm = NUM_PWM_CHANNEL; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resource defined\n"); + return -ENODEV; + } + + pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r); + if (!pc->mmio_base) { + dev_err(&pdev->dev, "failed to ioremap() registers\n"); + return -EADDRNOTAVAIL; + } + + ret = pwmchip_add(&pc->chip); + if (ret < 0) { + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + return ret; + } + + pm_runtime_enable(&pdev->dev); + platform_set_drvdata(pdev, pc); + return 0; +} + +static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev) +{ + struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return pwmchip_remove(&pc->chip); +} + +static struct platform_driver ehrpwm_pwm_driver = { + .driver = { + .name = "ehrpwm", + }, + .probe = ehrpwm_pwm_probe, + .remove = __devexit_p(ehrpwm_pwm_remove), +}; + +module_platform_driver(ehrpwm_pwm_driver); + +MODULE_DESCRIPTION("EHRPWM PWM driver"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c new file mode 100644 index 000000000000..548021439f0c --- /dev/null +++ b/drivers/pwm/pwm-vt8500.c @@ -0,0 +1,177 @@ +/* + * drivers/pwm/pwm-vt8500.c + * + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/pwm.h> +#include <linux/delay.h> + +#include <asm/div64.h> + +#define VT8500_NR_PWMS 4 + +struct vt8500_chip { + struct pwm_chip chip; + void __iomem *base; +}; + +#define to_vt8500_chip(chip) container_of(chip, struct vt8500_chip, chip) + +#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) +static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask) +{ + int loops = msecs_to_loops(10); + while ((readb(reg) & bitmask) && --loops) + cpu_relax(); + + if (unlikely(!loops)) + pr_warning("Waiting for status bits 0x%x to clear timed out\n", + bitmask); +} + +static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct vt8500_chip *vt8500 = to_vt8500_chip(chip); + unsigned long long c; + unsigned long period_cycles, prescale, pv, dc; + + c = 25000000/2; /* wild guess --- need to implement clocks */ + c = c * period_ns; + do_div(c, 1000000000); + period_cycles = c; + + if (period_cycles < 1) + period_cycles = 1; + prescale = (period_cycles - 1) / 4096; + pv = period_cycles / (prescale + 1) - 1; + if (pv > 4095) + pv = 4095; + + if (prescale > 1023) + return -EINVAL; + + c = (unsigned long long)pv * duty_ns; + do_div(c, period_ns); + dc = c; + + pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 1)); + writel(prescale, vt8500->base + 0x4 + (pwm->hwpwm << 4)); + + pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 2)); + writel(pv, vt8500->base + 0x8 + (pwm->hwpwm << 4)); + + pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 3)); + writel(dc, vt8500->base + 0xc + (pwm->hwpwm << 4)); + + return 0; +} + +static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct vt8500_chip *vt8500 = to_vt8500_chip(chip); + + pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); + writel(5, vt8500->base + (pwm->hwpwm << 4)); + return 0; +} + +static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct vt8500_chip *vt8500 = to_vt8500_chip(chip); + + pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); + writel(0, vt8500->base + (pwm->hwpwm << 4)); +} + +static struct pwm_ops vt8500_pwm_ops = { + .enable = vt8500_pwm_enable, + .disable = vt8500_pwm_disable, + .config = vt8500_pwm_config, + .owner = THIS_MODULE, +}; + +static int __devinit pwm_probe(struct platform_device *pdev) +{ + struct vt8500_chip *chip; + struct resource *r; + int ret; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (chip == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + chip->chip.dev = &pdev->dev; + chip->chip.ops = &vt8500_pwm_ops; + chip->chip.base = -1; + chip->chip.npwm = VT8500_NR_PWMS; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no memory resource defined\n"); + return -ENODEV; + } + + chip->base = devm_request_and_ioremap(&pdev->dev, r); + if (chip->base == NULL) + return -EADDRNOTAVAIL; + + ret = pwmchip_add(&chip->chip); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, chip); + return ret; +} + +static int __devexit pwm_remove(struct platform_device *pdev) +{ + struct vt8500_chip *chip; + + chip = platform_get_drvdata(pdev); + if (chip == NULL) + return -ENODEV; + + return pwmchip_remove(&chip->chip); +} + +static struct platform_driver pwm_driver = { + .driver = { + .name = "vt8500-pwm", + .owner = THIS_MODULE, + }, + .probe = pwm_probe, + .remove = __devexit_p(pwm_remove), +}; + +static int __init pwm_init(void) +{ + return platform_driver_register(&pwm_driver); +} +arch_initcall(pwm_init); + +static void __exit pwm_exit(void) +{ + platform_driver_unregister(&pwm_driver); +} +module_exit(pwm_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index f34c3be6c9fe..4e932cc695e9 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -272,7 +272,7 @@ config REGULATOR_S2MPS11 config REGULATOR_S5M8767 tristate "Samsung S5M8767A voltage regulator" - depends on MFD_S5M_CORE + depends on MFD_SEC_CORE help This driver supports a Samsung S5M8767A voltage output regulator via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index 13d424fc1c14..10f2f4d4d190 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -848,18 +848,12 @@ static __devexit int ab8500_regulator_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id ab8500_regulator_match[] = { - { .compatible = "stericsson,ab8500-regulator", }, - {} -}; - static struct platform_driver ab8500_regulator_driver = { .probe = ab8500_regulator_probe, .remove = __devexit_p(ab8500_regulator_remove), .driver = { .name = "ab8500-regulator", .owner = THIS_MODULE, - .of_match_table = ab8500_regulator_match, }, }; diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c index 9dbb491b6efa..359f8d18fc3f 100644 --- a/drivers/regulator/db8500-prcmu.c +++ b/drivers/regulator/db8500-prcmu.c @@ -547,16 +547,10 @@ static int __exit db8500_regulator_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id db8500_prcmu_regulator_match[] = { - { .compatible = "stericsson,db8500-prcmu-regulator", }, - {} -}; - static struct platform_driver db8500_regulator_driver = { .driver = { .name = "db8500-prcmu-regulators", .owner = THIS_MODULE, - .of_match_table = db8500_prcmu_regulator_match, }, .probe = db8500_regulator_probe, .remove = __exit_p(db8500_regulator_remove), diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 102287fa7ecb..abe64a32aedf 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -19,15 +19,15 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> -#include <linux/mfd/s5m87xx/s5m-core.h> -#include <linux/mfd/s5m87xx/s5m-pmic.h> +#include <linux/mfd/samsung/core.h> +#include <linux/mfd/samsung/s5m8767.h> struct s5m8767_info { struct device *dev; - struct s5m87xx_dev *iodev; + struct sec_pmic_dev *iodev; int num_regulators; struct regulator_dev **rdev; - struct s5m_opmode_data *opmode; + struct sec_opmode_data *opmode; int ramp_delay; bool buck2_ramp; @@ -45,43 +45,43 @@ struct s5m8767_info { int buck_gpioindex; }; -struct s5m_voltage_desc { +struct sec_voltage_desc { int max; int min; int step; }; -static const struct s5m_voltage_desc buck_voltage_val1 = { +static const struct sec_voltage_desc buck_voltage_val1 = { .max = 2225000, .min = 650000, .step = 6250, }; -static const struct s5m_voltage_desc buck_voltage_val2 = { +static const struct sec_voltage_desc buck_voltage_val2 = { .max = 1600000, .min = 600000, .step = 6250, }; -static const struct s5m_voltage_desc buck_voltage_val3 = { +static const struct sec_voltage_desc buck_voltage_val3 = { .max = 3000000, .min = 750000, .step = 12500, }; -static const struct s5m_voltage_desc ldo_voltage_val1 = { +static const struct sec_voltage_desc ldo_voltage_val1 = { .max = 3950000, .min = 800000, .step = 50000, }; -static const struct s5m_voltage_desc ldo_voltage_val2 = { +static const struct sec_voltage_desc ldo_voltage_val2 = { .max = 2375000, .min = 800000, .step = 25000, }; -static const struct s5m_voltage_desc *reg_voltage_map[] = { +static const struct sec_voltage_desc *reg_voltage_map[] = { [S5M8767_LDO1] = &ldo_voltage_val2, [S5M8767_LDO2] = &ldo_voltage_val2, [S5M8767_LDO3] = &ldo_voltage_val1, @@ -213,7 +213,7 @@ static int s5m8767_reg_is_enabled(struct regulator_dev *rdev) else if (ret) return ret; - ret = s5m_reg_read(s5m8767->iodev, reg, &val); + ret = sec_reg_read(s5m8767->iodev, reg, &val); if (ret) return ret; @@ -230,7 +230,7 @@ static int s5m8767_reg_enable(struct regulator_dev *rdev) if (ret) return ret; - return s5m_reg_update(s5m8767->iodev, reg, enable_ctrl, mask); + return sec_reg_update(s5m8767->iodev, reg, enable_ctrl, mask); } static int s5m8767_reg_disable(struct regulator_dev *rdev) @@ -243,7 +243,7 @@ static int s5m8767_reg_disable(struct regulator_dev *rdev) if (ret) return ret; - return s5m_reg_update(s5m8767->iodev, reg, ~mask, mask); + return sec_reg_update(s5m8767->iodev, reg, ~mask, mask); } static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int *_reg) @@ -305,7 +305,7 @@ static int s5m8767_get_voltage_sel(struct regulator_dev *rdev) mask = (reg_id < S5M8767_BUCK1) ? 0x3f : 0xff; - ret = s5m_reg_read(s5m8767->iodev, reg, &val); + ret = sec_reg_read(s5m8767->iodev, reg, &val); if (ret) return ret; @@ -315,7 +315,7 @@ static int s5m8767_get_voltage_sel(struct regulator_dev *rdev) } static int s5m8767_convert_voltage_to_sel( - const struct s5m_voltage_desc *desc, + const struct sec_voltage_desc *desc, int min_vol, int max_vol) { int selector = 0; @@ -407,7 +407,7 @@ static int s5m8767_set_voltage_sel(struct regulator_dev *rdev, if (ret) return ret; - return s5m_reg_update(s5m8767->iodev, reg, selector, mask); + return sec_reg_update(s5m8767->iodev, reg, selector, mask); } } @@ -416,7 +416,7 @@ static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int new_sel) { struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); - const struct s5m_voltage_desc *desc; + const struct sec_voltage_desc *desc; int reg_id = rdev_get_id(rdev); desc = reg_voltage_map[reg_id]; @@ -501,8 +501,8 @@ static struct regulator_desc regulators[] = { static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) { - struct s5m87xx_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct s5m_platform_data *pdata = dev_get_platdata(iodev->dev); + struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct sec_platform_data *pdata = dev_get_platdata(iodev->dev); struct regulator_config config = { }; struct regulator_dev **rdev; struct s5m8767_info *s5m8767; @@ -572,21 +572,21 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) pdata->buck2_init + buck_voltage_val2.step); - s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS2, buck_init); + sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS2, buck_init); buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, pdata->buck3_init, pdata->buck3_init + buck_voltage_val2.step); - s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS2, buck_init); + sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS2, buck_init); buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, pdata->buck4_init, pdata->buck4_init + buck_voltage_val2.step); - s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS2, buck_init); + sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS2, buck_init); for (i = 0; i < 8; i++) { if (s5m8767->buck2_gpiodvs) { @@ -671,13 +671,13 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || pdata->buck4_gpiodvs) { - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL, + sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL, (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL, + sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL, (pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL, + sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL, (pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); } @@ -685,61 +685,61 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) /* Initialize GPIO DVS registers */ for (i = 0; i < 8; i++) { if (s5m8767->buck2_gpiodvs) { - s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS1 + i, + sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS1 + i, s5m8767->buck2_vol[i]); } if (s5m8767->buck3_gpiodvs) { - s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS1 + i, + sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS1 + i, s5m8767->buck3_vol[i]); } if (s5m8767->buck4_gpiodvs) { - s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS1 + i, + sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS1 + i, s5m8767->buck4_vol[i]); } } if (s5m8767->buck2_ramp) - s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x08, 0x08); + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x08, 0x08); if (s5m8767->buck3_ramp) - s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x04, 0x04); + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x04, 0x04); if (s5m8767->buck4_ramp) - s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x02, 0x02); + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x02, 0x02); if (s5m8767->buck2_ramp || s5m8767->buck3_ramp || s5m8767->buck4_ramp) { switch (s5m8767->ramp_delay) { case 5: - s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x40, 0xf0); break; case 10: - s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x90, 0xf0); break; case 25: - s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0xd0, 0xf0); break; case 50: - s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0xe0, 0xf0); break; case 100: - s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0xf0, 0xf0); break; default: - s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x90, 0xf0); } } for (i = 0; i < pdata->num_regulators; i++) { - const struct s5m_voltage_desc *desc; + const struct sec_voltage_desc *desc; int id = pdata->regulators[i].id; desc = reg_voltage_map[id]; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 08cbdb900a18..fabc99a75c65 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -135,6 +135,16 @@ config RTC_DRV_88PM860X This driver can also be built as a module. If so, the module will be called rtc-88pm860x. +config RTC_DRV_88PM80X + tristate "Marvell 88PM80x" + depends on RTC_CLASS && I2C && MFD_88PM800 + help + If you say yes here you get support for RTC function in Marvell + 88PM80x chips. + + This driver can also be built as a module. If so, the module + will be called rtc-88pm80x. + config RTC_DRV_DS1307 tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025" help @@ -694,6 +704,7 @@ config RTC_DRV_AB3100 config RTC_DRV_AB8500 tristate "ST-Ericsson AB8500 RTC" depends on AB8500_CORE + select RTC_INTF_DEV_UIE_EMUL help Select this to enable the ST-Ericsson AB8500 power management IC RTC support. This chip contains a battery- and capacitor-backed RTC. diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 2973921c30d8..0d5b2b66f90d 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -16,6 +16,7 @@ rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o # Keep the list ordered. obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o +obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c new file mode 100644 index 000000000000..a2f956d90de0 --- /dev/null +++ b/drivers/rtc/rtc-88pm80x.c @@ -0,0 +1,371 @@ +/* + * Real Time Clock driver for Marvell 88PM80x PMIC + * + * Copyright (c) 2012 Marvell International Ltd. + * Wenzeng Chen<wzch@marvell.com> + * Qiao Zhou <zhouqiao@marvell.com> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/regmap.h> +#include <linux/mfd/core.h> +#include <linux/mfd/88pm80x.h> +#include <linux/rtc.h> + +#define PM800_RTC_COUNTER1 (0xD1) +#define PM800_RTC_COUNTER2 (0xD2) +#define PM800_RTC_COUNTER3 (0xD3) +#define PM800_RTC_COUNTER4 (0xD4) +#define PM800_RTC_EXPIRE1_1 (0xD5) +#define PM800_RTC_EXPIRE1_2 (0xD6) +#define PM800_RTC_EXPIRE1_3 (0xD7) +#define PM800_RTC_EXPIRE1_4 (0xD8) +#define PM800_RTC_TRIM1 (0xD9) +#define PM800_RTC_TRIM2 (0xDA) +#define PM800_RTC_TRIM3 (0xDB) +#define PM800_RTC_TRIM4 (0xDC) +#define PM800_RTC_EXPIRE2_1 (0xDD) +#define PM800_RTC_EXPIRE2_2 (0xDE) +#define PM800_RTC_EXPIRE2_3 (0xDF) +#define PM800_RTC_EXPIRE2_4 (0xE0) + +#define PM800_POWER_DOWN_LOG1 (0xE5) +#define PM800_POWER_DOWN_LOG2 (0xE6) + +struct pm80x_rtc_info { + struct pm80x_chip *chip; + struct regmap *map; + struct rtc_device *rtc_dev; + struct device *dev; + struct delayed_work calib_work; + + int irq; + int vrtc; +}; + +static irqreturn_t rtc_update_handler(int irq, void *data) +{ + struct pm80x_rtc_info *info = (struct pm80x_rtc_info *)data; + int mask; + + mask = PM800_ALARM | PM800_ALARM_WAKEUP; + regmap_update_bits(info->map, PM800_RTC_CONTROL, mask | PM800_ALARM1_EN, + mask); + rtc_update_irq(info->rtc_dev, 1, RTC_AF); + return IRQ_HANDLED; +} + +static int pm80x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct pm80x_rtc_info *info = dev_get_drvdata(dev); + + if (enabled) + regmap_update_bits(info->map, PM800_RTC_CONTROL, + PM800_ALARM1_EN, PM800_ALARM1_EN); + else + regmap_update_bits(info->map, PM800_RTC_CONTROL, + PM800_ALARM1_EN, 0); + return 0; +} + +/* + * Calculate the next alarm time given the requested alarm time mask + * and the current time. + */ +static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, + struct rtc_time *alrm) +{ + unsigned long next_time; + unsigned long now_time; + + next->tm_year = now->tm_year; + next->tm_mon = now->tm_mon; + next->tm_mday = now->tm_mday; + next->tm_hour = alrm->tm_hour; + next->tm_min = alrm->tm_min; + next->tm_sec = alrm->tm_sec; + + rtc_tm_to_time(now, &now_time); + rtc_tm_to_time(next, &next_time); + + if (next_time < now_time) { + /* Advance one day */ + next_time += 60 * 60 * 24; + rtc_time_to_tm(next_time, next); + } +} + +static int pm80x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pm80x_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[4]; + unsigned long ticks, base, data; + regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); + base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); + + /* load 32-bit read-only counter */ + regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); + data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + ticks = base + data; + dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + rtc_time_to_tm(ticks, tm); + return 0; +} + +static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pm80x_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[4]; + unsigned long ticks, base, data; + if ((tm->tm_year < 70) || (tm->tm_year > 138)) { + dev_dbg(info->dev, + "Set time %d out of range. Please set time between 1970 to 2038.\n", + 1900 + tm->tm_year); + return -EINVAL; + } + rtc_tm_to_time(tm, &ticks); + + /* load 32-bit read-only counter */ + regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); + data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + base = ticks - data; + dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + buf[0] = base & 0xFF; + buf[1] = (base >> 8) & 0xFF; + buf[2] = (base >> 16) & 0xFF; + buf[3] = (base >> 24) & 0xFF; + regmap_raw_write(info->map, PM800_RTC_EXPIRE2_1, buf, 4); + + return 0; +} + +static int pm80x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pm80x_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[4]; + unsigned long ticks, base, data; + int ret; + + regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); + base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); + + regmap_raw_read(info->map, PM800_RTC_EXPIRE1_1, buf, 4); + data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + ticks = base + data; + dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + + rtc_time_to_tm(ticks, &alrm->time); + regmap_read(info->map, PM800_RTC_CONTROL, &ret); + alrm->enabled = (ret & PM800_ALARM1_EN) ? 1 : 0; + alrm->pending = (ret & (PM800_ALARM | PM800_ALARM_WAKEUP)) ? 1 : 0; + return 0; +} + +static int pm80x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pm80x_rtc_info *info = dev_get_drvdata(dev); + struct rtc_time now_tm, alarm_tm; + unsigned long ticks, base, data; + unsigned char buf[4]; + int mask; + + regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_ALARM1_EN, 0); + + regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); + base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); + + /* load 32-bit read-only counter */ + regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); + data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + ticks = base + data; + dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + + rtc_time_to_tm(ticks, &now_tm); + dev_dbg(info->dev, "%s, now time : %lu\n", __func__, ticks); + rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time); + /* get new ticks for alarm in 24 hours */ + rtc_tm_to_time(&alarm_tm, &ticks); + dev_dbg(info->dev, "%s, alarm time: %lu\n", __func__, ticks); + data = ticks - base; + + buf[0] = data & 0xff; + buf[1] = (data >> 8) & 0xff; + buf[2] = (data >> 16) & 0xff; + buf[3] = (data >> 24) & 0xff; + regmap_raw_write(info->map, PM800_RTC_EXPIRE1_1, buf, 4); + if (alrm->enabled) { + mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN; + regmap_update_bits(info->map, PM800_RTC_CONTROL, mask, mask); + } else { + mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN; + regmap_update_bits(info->map, PM800_RTC_CONTROL, mask, + PM800_ALARM | PM800_ALARM_WAKEUP); + } + return 0; +} + +static const struct rtc_class_ops pm80x_rtc_ops = { + .read_time = pm80x_rtc_read_time, + .set_time = pm80x_rtc_set_time, + .read_alarm = pm80x_rtc_read_alarm, + .set_alarm = pm80x_rtc_set_alarm, + .alarm_irq_enable = pm80x_rtc_alarm_irq_enable, +}; + +#ifdef CONFIG_PM +static int pm80x_rtc_suspend(struct device *dev) +{ + return pm80x_dev_suspend(dev); +} + +static int pm80x_rtc_resume(struct device *dev) +{ + return pm80x_dev_resume(dev); +} +#endif + +static SIMPLE_DEV_PM_OPS(pm80x_rtc_pm_ops, pm80x_rtc_suspend, pm80x_rtc_resume); + +static int __devinit pm80x_rtc_probe(struct platform_device *pdev) +{ + struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm80x_platform_data *pm80x_pdata; + struct pm80x_rtc_pdata *pdata = NULL; + struct pm80x_rtc_info *info; + struct rtc_time tm; + unsigned long ticks = 0; + int ret; + + pdata = pdev->dev.platform_data; + if (pdata == NULL) + dev_warn(&pdev->dev, "No platform data!\n"); + + info = + devm_kzalloc(&pdev->dev, sizeof(struct pm80x_rtc_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) { + dev_err(&pdev->dev, "No IRQ resource!\n"); + ret = -EINVAL; + goto out; + } + + info->chip = chip; + info->map = chip->regmap; + if (!info->map) { + dev_err(&pdev->dev, "no regmap!\n"); + ret = -EINVAL; + goto out; + } + + info->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, info); + + ret = pm80x_request_irq(chip, info->irq, rtc_update_handler, + IRQF_ONESHOT, "rtc", info); + if (ret < 0) { + dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", + info->irq, ret); + goto out; + } + + ret = pm80x_rtc_read_time(&pdev->dev, &tm); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read initial time.\n"); + goto out_rtc; + } + if ((tm.tm_year < 70) || (tm.tm_year > 138)) { + tm.tm_year = 70; + tm.tm_mon = 0; + tm.tm_mday = 1; + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + ret = pm80x_rtc_set_time(&pdev->dev, &tm); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to set initial time.\n"); + goto out_rtc; + } + } + rtc_tm_to_time(&tm, &ticks); + + info->rtc_dev = rtc_device_register("88pm80x-rtc", &pdev->dev, + &pm80x_rtc_ops, THIS_MODULE); + ret = PTR_ERR(info->rtc_dev); + if (IS_ERR(info->rtc_dev)) { + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); + goto out_rtc; + } + /* + * enable internal XO instead of internal 3.25MHz clock since it can + * free running in PMIC power-down state. + */ + regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_RTC1_USE_XO, + PM800_RTC1_USE_XO); + + if (pdev->dev.parent->platform_data) { + pm80x_pdata = pdev->dev.parent->platform_data; + pdata = pm80x_pdata->rtc; + if (pdata) + info->rtc_dev->dev.platform_data = &pdata->rtc_wakeup; + } + + device_init_wakeup(&pdev->dev, 1); + + return 0; +out_rtc: + pm80x_free_irq(chip, info->irq, info); +out: + devm_kfree(&pdev->dev, info); + return ret; +} + +static int __devexit pm80x_rtc_remove(struct platform_device *pdev) +{ + struct pm80x_rtc_info *info = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); + rtc_device_unregister(info->rtc_dev); + pm80x_free_irq(info->chip, info->irq, info); + devm_kfree(&pdev->dev, info); + return 0; +} + +static struct platform_driver pm80x_rtc_driver = { + .driver = { + .name = "88pm80x-rtc", + .owner = THIS_MODULE, + .pm = &pm80x_rtc_pm_ops, + }, + .probe = pm80x_rtc_probe, + .remove = __devexit_p(pm80x_rtc_remove), +}; + +module_platform_driver(pm80x_rtc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Marvell 88PM80x RTC driver"); +MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>"); +MODULE_ALIAS("platform:88pm80x-rtc"); diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c index 370889d0489b..bf3c2f669c3c 100644 --- a/drivers/rtc/rtc-ab8500.c +++ b/drivers/rtc/rtc-ab8500.c @@ -89,22 +89,17 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm) if (retval < 0) return retval; - /* Early AB8500 chips will not clear the rtc read request bit */ - if (abx500_get_chip_id(dev) == 0) { - usleep_range(1000, 1000); - } else { - /* Wait for some cycles after enabling the rtc read in ab8500 */ - while (time_before(jiffies, timeout)) { - retval = abx500_get_register_interruptible(dev, - AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value); - if (retval < 0) - return retval; - - if (!(value & RTC_READ_REQUEST)) - break; - - usleep_range(1000, 5000); - } + /* Wait for some cycles after enabling the rtc read in ab8500 */ + while (time_before(jiffies, timeout)) { + retval = abx500_get_register_interruptible(dev, + AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value); + if (retval < 0) + return retval; + + if (!(value & RTC_READ_REQUEST)) + break; + + usleep_range(1000, 5000); } /* Read the Watchtime registers */ @@ -225,7 +220,8 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { int retval, i; unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)]; - unsigned long mins, secs = 0; + unsigned long mins, secs = 0, cursec = 0; + struct rtc_time curtm; if (alarm->time.tm_year < (AB8500_RTC_EPOCH - 1900)) { dev_dbg(dev, "year should be equal to or greater than %d\n", @@ -237,6 +233,18 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) rtc_tm_to_time(&alarm->time, &secs); /* + * Check whether alarm is set less than 1min. + * Since our RTC doesn't support alarm resolution less than 1min, + * return -EINVAL, so UIE EMUL can take it up, incase of UIE_ON + */ + ab8500_rtc_read_time(dev, &curtm); /* Read current time */ + rtc_tm_to_time(&curtm, &cursec); + if ((secs - cursec) < 59) { + dev_dbg(dev, "Alarm less than 1 minute not supported\r\n"); + return -EINVAL; + } + + /* * Convert it to the number of seconds since 01-01-2000 00:00:00, since * we only have a small counter in the RTC. */ diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c index a5b8a0c4ea84..76b2156d3c62 100644 --- a/drivers/rtc/rtc-coh901331.c +++ b/drivers/rtc/rtc-coh901331.c @@ -155,13 +155,10 @@ static int __exit coh901331_remove(struct platform_device *pdev) struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); if (rtap) { - free_irq(rtap->irq, rtap); rtc_device_unregister(rtap->rtc); + clk_unprepare(rtap->clk); clk_put(rtap->clk); - iounmap(rtap->virtbase); - release_mem_region(rtap->phybase, rtap->physize); platform_set_drvdata(pdev, NULL); - kfree(rtap); } return 0; @@ -174,49 +171,43 @@ static int __init coh901331_probe(struct platform_device *pdev) struct coh901331_port *rtap; struct resource *res; - rtap = kzalloc(sizeof(struct coh901331_port), GFP_KERNEL); + rtap = devm_kzalloc(&pdev->dev, + sizeof(struct coh901331_port), GFP_KERNEL); if (!rtap) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENOENT; - goto out_no_resource; - } + if (!res) + return -ENOENT; + rtap->phybase = res->start; rtap->physize = resource_size(res); - if (request_mem_region(rtap->phybase, rtap->physize, - "rtc-coh901331") == NULL) { - ret = -EBUSY; - goto out_no_memregion; - } + if (devm_request_mem_region(&pdev->dev, rtap->phybase, rtap->physize, + "rtc-coh901331") == NULL) + return -EBUSY; - rtap->virtbase = ioremap(rtap->phybase, rtap->physize); - if (!rtap->virtbase) { - ret = -ENOMEM; - goto out_no_remap; - } + rtap->virtbase = devm_ioremap(&pdev->dev, rtap->phybase, rtap->physize); + if (!rtap->virtbase) + return -ENOMEM; rtap->irq = platform_get_irq(pdev, 0); - if (request_irq(rtap->irq, coh901331_interrupt, 0, - "RTC COH 901 331 Alarm", rtap)) { - ret = -EIO; - goto out_no_irq; - } + if (devm_request_irq(&pdev->dev, rtap->irq, coh901331_interrupt, 0, + "RTC COH 901 331 Alarm", rtap)) + return -EIO; rtap->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(rtap->clk)) { ret = PTR_ERR(rtap->clk); dev_err(&pdev->dev, "could not get clock\n"); - goto out_no_clk; + return ret; } /* We enable/disable the clock only to assure it works */ - ret = clk_enable(rtap->clk); + ret = clk_prepare_enable(rtap->clk); if (ret) { dev_err(&pdev->dev, "could not enable clock\n"); - goto out_no_clk_enable; + goto out_no_clk_prepenable; } clk_disable(rtap->clk); @@ -232,18 +223,9 @@ static int __init coh901331_probe(struct platform_device *pdev) out_no_rtc: platform_set_drvdata(pdev, NULL); - out_no_clk_enable: + clk_unprepare(rtap->clk); + out_no_clk_prepenable: clk_put(rtap->clk); - out_no_clk: - free_irq(rtap->irq, rtap); - out_no_irq: - iounmap(rtap->virtbase); - out_no_remap: - platform_set_drvdata(pdev, NULL); - out_no_memregion: - release_mem_region(rtap->phybase, SZ_4K); - out_no_resource: - kfree(rtap); return ret; } @@ -265,6 +247,7 @@ static int coh901331_suspend(struct platform_device *pdev, pm_message_t state) writel(0, rtap->virtbase + COH901331_IRQ_MASK); clk_disable(rtap->clk); } + clk_unprepare(rtap->clk); return 0; } @@ -272,6 +255,7 @@ static int coh901331_resume(struct platform_device *pdev) { struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); + clk_prepare(rtap->clk); if (device_may_wakeup(&pdev->dev)) { disable_irq_wake(rtap->irq); } else { @@ -293,6 +277,7 @@ static void coh901331_shutdown(struct platform_device *pdev) clk_enable(rtap->clk); writel(0, rtap->virtbase + COH901331_IRQ_MASK); clk_disable(rtap->clk); + clk_unprepare(rtap->clk); } static struct platform_driver coh901331_driver = { diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c index da6ab5291a41..78070255bd3f 100644 --- a/drivers/rtc/rtc-da9052.c +++ b/drivers/rtc/rtc-da9052.c @@ -245,7 +245,7 @@ static int __devinit da9052_rtc_probe(struct platform_device *pdev) "ALM", rtc); if (ret != 0) { rtc_err(rtc->da9052, "irq registration failed: %d\n", ret); - goto err_mem; + return ret; } rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, @@ -259,8 +259,6 @@ static int __devinit da9052_rtc_probe(struct platform_device *pdev) err_free_irq: free_irq(rtc->irq, rtc); -err_mem: - devm_kfree(&pdev->dev, rtc); return ret; } @@ -271,7 +269,6 @@ static int __devexit da9052_rtc_remove(struct platform_device *pdev) rtc_device_unregister(rtc->rtc); free_irq(rtc->irq, rtc); platform_set_drvdata(pdev, NULL); - devm_kfree(&pdev->dev, rtc); return 0; } diff --git a/drivers/rtc/rtc-max8925.c b/drivers/rtc/rtc-max8925.c index 1459055a83aa..34e4349611db 100644 --- a/drivers/rtc/rtc-max8925.c +++ b/drivers/rtc/rtc-max8925.c @@ -69,6 +69,7 @@ struct max8925_rtc_info { struct max8925_chip *chip; struct i2c_client *rtc; struct device *dev; + int irq; }; static irqreturn_t rtc_update_handler(int irq, void *data) @@ -250,7 +251,7 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); struct max8925_rtc_info *info; - int irq, ret; + int ret; info = kzalloc(sizeof(struct max8925_rtc_info), GFP_KERNEL); if (!info) @@ -258,13 +259,13 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev) info->chip = chip; info->rtc = chip->rtc; info->dev = &pdev->dev; - irq = chip->irq_base + MAX8925_IRQ_RTC_ALARM0; + info->irq = platform_get_irq(pdev, 0); - ret = request_threaded_irq(irq, NULL, rtc_update_handler, + ret = request_threaded_irq(info->irq, NULL, rtc_update_handler, IRQF_ONESHOT, "rtc-alarm0", info); if (ret < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", - irq, ret); + info->irq, ret); goto out_irq; } @@ -285,7 +286,7 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev) return 0; out_rtc: platform_set_drvdata(pdev, NULL); - free_irq(chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info); + free_irq(info->irq, info); out_irq: kfree(info); return ret; @@ -296,7 +297,7 @@ static int __devexit max8925_rtc_remove(struct platform_device *pdev) struct max8925_rtc_info *info = platform_get_drvdata(pdev); if (info) { - free_irq(info->chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info); + free_irq(info->irq, info); rtc_device_unregister(info->rtc_dev); kfree(info); } diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c index 546f6850bffb..2643d8874925 100644 --- a/drivers/rtc/rtc-mc13xxx.c +++ b/drivers/rtc/rtc-mc13xxx.c @@ -404,9 +404,12 @@ static const struct platform_device_id mc13xxx_rtc_idtable[] = { .name = "mc13783-rtc", }, { .name = "mc13892-rtc", + }, { + .name = "mc34708-rtc", }, - { } + { /* sentinel */ } }; +MODULE_DEVICE_TABLE(platform, mc13xxx_rtc_idtable); static struct platform_driver mc13xxx_rtc_driver = { .id_table = mc13xxx_rtc_idtable, @@ -432,4 +435,3 @@ module_exit(mc13xxx_rtc_exit); MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); MODULE_DESCRIPTION("RTC driver for Freescale MC13XXX PMIC"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 97a3284bb7c6..c2fe426a6ef2 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -19,6 +19,7 @@ #include <linux/rtc.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/of.h> #define DRV_VERSION "0.4.3" @@ -285,9 +286,19 @@ static const struct i2c_device_id pcf8563_id[] = { }; MODULE_DEVICE_TABLE(i2c, pcf8563_id); +#ifdef CONFIG_OF +static const struct of_device_id pcf8563_of_match[] __devinitconst = { + { .compatible = "nxp,pcf8563" }, + {} +}; +MODULE_DEVICE_TABLE(of, pcf8563_of_match); +#endif + static struct i2c_driver pcf8563_driver = { .driver = { .name = "rtc-pcf8563", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pcf8563_of_match), }, .probe = pcf8563_probe, .remove = pcf8563_remove, diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index cc0533994f6e..08378e3cc21c 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -68,11 +68,26 @@ #define RTC_TIMER_FREQ 32768 +/** + * struct pl031_vendor_data - per-vendor variations + * @ops: the vendor-specific operations used on this silicon version + * @clockwatch: if this is an ST Microelectronics silicon version with a + * clockwatch function + * @st_weekday: if this is an ST Microelectronics silicon version that need + * the weekday fix + * @irqflags: special IRQ flags per variant + */ +struct pl031_vendor_data { + struct rtc_class_ops ops; + bool clockwatch; + bool st_weekday; + unsigned long irqflags; +}; + struct pl031_local { + struct pl031_vendor_data *vendor; struct rtc_device *rtc; void __iomem *base; - u8 hw_designer; - u8 hw_revision:4; }; static int pl031_alarm_irq_enable(struct device *dev, @@ -303,7 +318,8 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) { int ret; struct pl031_local *ldata; - struct rtc_class_ops *ops = id->data; + struct pl031_vendor_data *vendor = id->data; + struct rtc_class_ops *ops = &vendor->ops; unsigned long time; ret = amba_request_regions(adev, NULL); @@ -315,6 +331,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) ret = -ENOMEM; goto out; } + ldata->vendor = vendor; ldata->base = ioremap(adev->res.start, resource_size(&adev->res)); @@ -325,14 +342,11 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) amba_set_drvdata(adev, ldata); - ldata->hw_designer = amba_manf(adev); - ldata->hw_revision = amba_rev(adev); - - dev_dbg(&adev->dev, "designer ID = 0x%02x\n", ldata->hw_designer); - dev_dbg(&adev->dev, "revision = 0x%01x\n", ldata->hw_revision); + dev_dbg(&adev->dev, "designer ID = 0x%02x\n", amba_manf(adev)); + dev_dbg(&adev->dev, "revision = 0x%01x\n", amba_rev(adev)); /* Enable the clockwatch on ST Variants */ - if (ldata->hw_designer == AMBA_VENDOR_ST) + if (vendor->clockwatch) writel(readl(ldata->base + RTC_CR) | RTC_CR_CWEN, ldata->base + RTC_CR); @@ -340,7 +354,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) * On ST PL031 variants, the RTC reset value does not provide correct * weekday for 2000-01-01. Correct the erroneous sunday to saturday. */ - if (ldata->hw_designer == AMBA_VENDOR_ST) { + if (vendor->st_weekday) { if (readl(ldata->base + RTC_YDR) == 0x2000) { time = readl(ldata->base + RTC_DR); if ((time & @@ -361,7 +375,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) } if (request_irq(adev->irq[0], pl031_interrupt, - 0, "rtc-pl031", ldata)) { + vendor->irqflags, "rtc-pl031", ldata)) { ret = -EIO; goto out_no_irq; } @@ -383,48 +397,65 @@ err_req: } /* Operations for the original ARM version */ -static struct rtc_class_ops arm_pl031_ops = { - .read_time = pl031_read_time, - .set_time = pl031_set_time, - .read_alarm = pl031_read_alarm, - .set_alarm = pl031_set_alarm, - .alarm_irq_enable = pl031_alarm_irq_enable, +static struct pl031_vendor_data arm_pl031 = { + .ops = { + .read_time = pl031_read_time, + .set_time = pl031_set_time, + .read_alarm = pl031_read_alarm, + .set_alarm = pl031_set_alarm, + .alarm_irq_enable = pl031_alarm_irq_enable, + }, + .irqflags = IRQF_NO_SUSPEND, }; /* The First ST derivative */ -static struct rtc_class_ops stv1_pl031_ops = { - .read_time = pl031_read_time, - .set_time = pl031_set_time, - .read_alarm = pl031_read_alarm, - .set_alarm = pl031_set_alarm, - .alarm_irq_enable = pl031_alarm_irq_enable, +static struct pl031_vendor_data stv1_pl031 = { + .ops = { + .read_time = pl031_read_time, + .set_time = pl031_set_time, + .read_alarm = pl031_read_alarm, + .set_alarm = pl031_set_alarm, + .alarm_irq_enable = pl031_alarm_irq_enable, + }, + .clockwatch = true, + .st_weekday = true, + .irqflags = IRQF_NO_SUSPEND, }; /* And the second ST derivative */ -static struct rtc_class_ops stv2_pl031_ops = { - .read_time = pl031_stv2_read_time, - .set_time = pl031_stv2_set_time, - .read_alarm = pl031_stv2_read_alarm, - .set_alarm = pl031_stv2_set_alarm, - .alarm_irq_enable = pl031_alarm_irq_enable, +static struct pl031_vendor_data stv2_pl031 = { + .ops = { + .read_time = pl031_stv2_read_time, + .set_time = pl031_stv2_set_time, + .read_alarm = pl031_stv2_read_alarm, + .set_alarm = pl031_stv2_set_alarm, + .alarm_irq_enable = pl031_alarm_irq_enable, + }, + .clockwatch = true, + .st_weekday = true, + /* + * This variant shares the IRQ with another block and must not + * suspend that IRQ line. + */ + .irqflags = IRQF_SHARED | IRQF_NO_SUSPEND, }; static struct amba_id pl031_ids[] = { { .id = 0x00041031, .mask = 0x000fffff, - .data = &arm_pl031_ops, + .data = &arm_pl031, }, /* ST Micro variants */ { .id = 0x00180031, .mask = 0x00ffffff, - .data = &stv1_pl031_ops, + .data = &stv1_pl031, }, { .id = 0x00280031, .mask = 0x00ffffff, - .data = &stv2_pl031_ops, + .data = &stv2_pl031, }, {0, 0}, }; diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c index 33b6ba0afa0d..2c183ebff715 100644 --- a/drivers/rtc/rtc-r9701.c +++ b/drivers/rtc/rtc-r9701.c @@ -138,8 +138,7 @@ static int __devinit r9701_probe(struct spi_device *spi) * contain invalid values. If so, try to write a default date: * 2000/1/1 00:00:00 */ - r9701_get_datetime(&spi->dev, &dt); - if (rtc_valid_tm(&dt)) { + if (r9701_get_datetime(&spi->dev, &dt)) { dev_info(&spi->dev, "trying to repair invalid date/time\n"); dt.tm_sec = 0; dt.tm_min = 0; @@ -148,7 +147,8 @@ static int __devinit r9701_probe(struct spi_device *spi) dt.tm_mon = 0; dt.tm_year = 100; - if (r9701_set_datetime(&spi->dev, &dt)) { + if (r9701_set_datetime(&spi->dev, &dt) || + r9701_get_datetime(&spi->dev, &dt)) { dev_err(&spi->dev, "cannot repair RTC register\n"); return -ENODEV; } diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 7e6af0b22f17..bfbd92c8d1c9 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -26,10 +26,10 @@ #include <linux/log2.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/uaccess.h> +#include <linux/io.h> #include <mach/hardware.h> -#include <asm/uaccess.h> -#include <asm/io.h> #include <asm/irq.h> #include <plat/regs-rtc.h> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4cde4fb0cd6c..5f84b5563c2d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -144,6 +144,15 @@ config SPI_EP93XX This enables using the Cirrus EP93xx SPI controller in master mode. +config SPI_FALCON + tristate "Falcon SPI controller support" + depends on SOC_FALCON + help + The external bus unit (EBU) found on the FALC-ON SoC has SPI + emulation that is designed for serial flash access. This driver + has only been tested with m25p80 type chips. The hardware has no + support for other types of SPI peripherals. + config SPI_GPIO tristate "GPIO-based bitbanging SPI Master" depends on GENERIC_GPIO diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 273f50d1127a..3920dcf4c740 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o +obj-$(CONFIG_SPI_FALCON) += spi-falcon.o obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c new file mode 100644 index 000000000000..8f6aa735a24c --- /dev/null +++ b/drivers/spi/spi-falcon.c @@ -0,0 +1,469 @@ +/* + * 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. + * + * Copyright (C) 2012 Thomas Langer <thomas.langer@lantiq.com> + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/of.h> +#include <linux/of_platform.h> + +#include <lantiq_soc.h> + +#define DRV_NAME "sflash-falcon" + +#define FALCON_SPI_XFER_BEGIN (1 << 0) +#define FALCON_SPI_XFER_END (1 << 1) + +/* Bus Read Configuration Register0 */ +#define BUSRCON0 0x00000010 +/* Bus Write Configuration Register0 */ +#define BUSWCON0 0x00000018 +/* Serial Flash Configuration Register */ +#define SFCON 0x00000080 +/* Serial Flash Time Register */ +#define SFTIME 0x00000084 +/* Serial Flash Status Register */ +#define SFSTAT 0x00000088 +/* Serial Flash Command Register */ +#define SFCMD 0x0000008C +/* Serial Flash Address Register */ +#define SFADDR 0x00000090 +/* Serial Flash Data Register */ +#define SFDATA 0x00000094 +/* Serial Flash I/O Control Register */ +#define SFIO 0x00000098 +/* EBU Clock Control Register */ +#define EBUCC 0x000000C4 + +/* Dummy Phase Length */ +#define SFCMD_DUMLEN_OFFSET 16 +#define SFCMD_DUMLEN_MASK 0x000F0000 +/* Chip Select */ +#define SFCMD_CS_OFFSET 24 +#define SFCMD_CS_MASK 0x07000000 +/* field offset */ +#define SFCMD_ALEN_OFFSET 20 +#define SFCMD_ALEN_MASK 0x00700000 +/* SCK Rise-edge Position */ +#define SFTIME_SCKR_POS_OFFSET 8 +#define SFTIME_SCKR_POS_MASK 0x00000F00 +/* SCK Period */ +#define SFTIME_SCK_PER_OFFSET 0 +#define SFTIME_SCK_PER_MASK 0x0000000F +/* SCK Fall-edge Position */ +#define SFTIME_SCKF_POS_OFFSET 12 +#define SFTIME_SCKF_POS_MASK 0x0000F000 +/* Device Size */ +#define SFCON_DEV_SIZE_A23_0 0x03000000 +#define SFCON_DEV_SIZE_MASK 0x0F000000 +/* Read Data Position */ +#define SFTIME_RD_POS_MASK 0x000F0000 +/* Data Output */ +#define SFIO_UNUSED_WD_MASK 0x0000000F +/* Command Opcode mask */ +#define SFCMD_OPC_MASK 0x000000FF +/* dlen bytes of data to write */ +#define SFCMD_DIR_WRITE 0x00000100 +/* Data Length offset */ +#define SFCMD_DLEN_OFFSET 9 +/* Command Error */ +#define SFSTAT_CMD_ERR 0x20000000 +/* Access Command Pending */ +#define SFSTAT_CMD_PEND 0x00400000 +/* Frequency set to 100MHz. */ +#define EBUCC_EBUDIV_SELF100 0x00000001 +/* Serial Flash */ +#define BUSRCON0_AGEN_SERIAL_FLASH 0xF0000000 +/* 8-bit multiplexed */ +#define BUSRCON0_PORTW_8_BIT_MUX 0x00000000 +/* Serial Flash */ +#define BUSWCON0_AGEN_SERIAL_FLASH 0xF0000000 +/* Chip Select after opcode */ +#define SFCMD_KEEP_CS_KEEP_SELECTED 0x00008000 + +#define CLOCK_100M 100000000 +#define CLOCK_50M 50000000 + +struct falcon_sflash { + u32 sfcmd; /* for caching of opcode, direction, ... */ + struct spi_master *master; +}; + +int falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t, + unsigned long flags) +{ + struct device *dev = &spi->dev; + struct falcon_sflash *priv = spi_master_get_devdata(spi->master); + const u8 *txp = t->tx_buf; + u8 *rxp = t->rx_buf; + unsigned int bytelen = ((8 * t->len + 7) / 8); + unsigned int len, alen, dumlen; + u32 val; + enum { + state_init, + state_command_prepare, + state_write, + state_read, + state_disable_cs, + state_end + } state = state_init; + + do { + switch (state) { + case state_init: /* detect phase of upper layer sequence */ + { + /* initial write ? */ + if (flags & FALCON_SPI_XFER_BEGIN) { + if (!txp) { + dev_err(dev, + "BEGIN without tx data!\n"); + return -ENODATA; + } + /* + * Prepare the parts of the sfcmd register, + * which should not change during a sequence! + * Only exception are the length fields, + * especially alen and dumlen. + */ + + priv->sfcmd = ((spi->chip_select + << SFCMD_CS_OFFSET) + & SFCMD_CS_MASK); + priv->sfcmd |= SFCMD_KEEP_CS_KEEP_SELECTED; + priv->sfcmd |= *txp; + txp++; + bytelen--; + if (bytelen) { + /* + * more data: + * maybe address and/or dummy + */ + state = state_command_prepare; + break; + } else { + dev_dbg(dev, "write cmd %02X\n", + priv->sfcmd & SFCMD_OPC_MASK); + } + } + /* continued write ? */ + if (txp && bytelen) { + state = state_write; + break; + } + /* read data? */ + if (rxp && bytelen) { + state = state_read; + break; + } + /* end of sequence? */ + if (flags & FALCON_SPI_XFER_END) + state = state_disable_cs; + else + state = state_end; + break; + } + /* collect tx data for address and dummy phase */ + case state_command_prepare: + { + /* txp is valid, already checked */ + val = 0; + alen = 0; + dumlen = 0; + while (bytelen > 0) { + if (alen < 3) { + val = (val << 8) | (*txp++); + alen++; + } else if ((dumlen < 15) && (*txp == 0)) { + /* + * assume dummy bytes are set to 0 + * from upper layer + */ + dumlen++; + txp++; + } else { + break; + } + bytelen--; + } + priv->sfcmd &= ~(SFCMD_ALEN_MASK | SFCMD_DUMLEN_MASK); + priv->sfcmd |= (alen << SFCMD_ALEN_OFFSET) | + (dumlen << SFCMD_DUMLEN_OFFSET); + if (alen > 0) + ltq_ebu_w32(val, SFADDR); + + dev_dbg(dev, "wr %02X, alen=%d (addr=%06X) dlen=%d\n", + priv->sfcmd & SFCMD_OPC_MASK, + alen, val, dumlen); + + if (bytelen > 0) { + /* continue with write */ + state = state_write; + } else if (flags & FALCON_SPI_XFER_END) { + /* end of sequence? */ + state = state_disable_cs; + } else { + /* + * go to end and expect another + * call (read or write) + */ + state = state_end; + } + break; + } + case state_write: + { + /* txp still valid */ + priv->sfcmd |= SFCMD_DIR_WRITE; + len = 0; + val = 0; + do { + if (bytelen--) + val |= (*txp++) << (8 * len++); + if ((flags & FALCON_SPI_XFER_END) + && (bytelen == 0)) { + priv->sfcmd &= + ~SFCMD_KEEP_CS_KEEP_SELECTED; + } + if ((len == 4) || (bytelen == 0)) { + ltq_ebu_w32(val, SFDATA); + ltq_ebu_w32(priv->sfcmd + | (len<<SFCMD_DLEN_OFFSET), + SFCMD); + len = 0; + val = 0; + priv->sfcmd &= ~(SFCMD_ALEN_MASK + | SFCMD_DUMLEN_MASK); + } + } while (bytelen); + state = state_end; + break; + } + case state_read: + { + /* read data */ + priv->sfcmd &= ~SFCMD_DIR_WRITE; + do { + if ((flags & FALCON_SPI_XFER_END) + && (bytelen <= 4)) { + priv->sfcmd &= + ~SFCMD_KEEP_CS_KEEP_SELECTED; + } + len = (bytelen > 4) ? 4 : bytelen; + bytelen -= len; + ltq_ebu_w32(priv->sfcmd + | (len << SFCMD_DLEN_OFFSET), SFCMD); + priv->sfcmd &= ~(SFCMD_ALEN_MASK + | SFCMD_DUMLEN_MASK); + do { + val = ltq_ebu_r32(SFSTAT); + if (val & SFSTAT_CMD_ERR) { + /* reset error status */ + dev_err(dev, "SFSTAT: CMD_ERR"); + dev_err(dev, " (%x)\n", val); + ltq_ebu_w32(SFSTAT_CMD_ERR, + SFSTAT); + return -EBADE; + } + } while (val & SFSTAT_CMD_PEND); + val = ltq_ebu_r32(SFDATA); + do { + *rxp = (val & 0xFF); + rxp++; + val >>= 8; + len--; + } while (len); + } while (bytelen); + state = state_end; + break; + } + case state_disable_cs: + { + priv->sfcmd &= ~SFCMD_KEEP_CS_KEEP_SELECTED; + ltq_ebu_w32(priv->sfcmd | (0 << SFCMD_DLEN_OFFSET), + SFCMD); + val = ltq_ebu_r32(SFSTAT); + if (val & SFSTAT_CMD_ERR) { + /* reset error status */ + dev_err(dev, "SFSTAT: CMD_ERR (%x)\n", val); + ltq_ebu_w32(SFSTAT_CMD_ERR, SFSTAT); + return -EBADE; + } + state = state_end; + break; + } + case state_end: + break; + } + } while (state != state_end); + + return 0; +} + +static int falcon_sflash_setup(struct spi_device *spi) +{ + unsigned int i; + unsigned long flags; + + if (spi->chip_select > 0) + return -ENODEV; + + spin_lock_irqsave(&ebu_lock, flags); + + if (spi->max_speed_hz >= CLOCK_100M) { + /* set EBU clock to 100 MHz */ + ltq_sys1_w32_mask(0, EBUCC_EBUDIV_SELF100, EBUCC); + i = 1; /* divider */ + } else { + /* set EBU clock to 50 MHz */ + ltq_sys1_w32_mask(EBUCC_EBUDIV_SELF100, 0, EBUCC); + + /* search for suitable divider */ + for (i = 1; i < 7; i++) { + if (CLOCK_50M / i <= spi->max_speed_hz) + break; + } + } + + /* setup period of serial clock */ + ltq_ebu_w32_mask(SFTIME_SCKF_POS_MASK + | SFTIME_SCKR_POS_MASK + | SFTIME_SCK_PER_MASK, + (i << SFTIME_SCKR_POS_OFFSET) + | (i << (SFTIME_SCK_PER_OFFSET + 1)), + SFTIME); + + /* + * set some bits of unused_wd, to not trigger HOLD/WP + * signals on non QUAD flashes + */ + ltq_ebu_w32((SFIO_UNUSED_WD_MASK & (0x8 | 0x4)), SFIO); + + ltq_ebu_w32(BUSRCON0_AGEN_SERIAL_FLASH | BUSRCON0_PORTW_8_BIT_MUX, + BUSRCON0); + ltq_ebu_w32(BUSWCON0_AGEN_SERIAL_FLASH, BUSWCON0); + /* set address wrap around to maximum for 24-bit addresses */ + ltq_ebu_w32_mask(SFCON_DEV_SIZE_MASK, SFCON_DEV_SIZE_A23_0, SFCON); + + spin_unlock_irqrestore(&ebu_lock, flags); + + return 0; +} + +static int falcon_sflash_prepare_xfer(struct spi_master *master) +{ + return 0; +} + +static int falcon_sflash_unprepare_xfer(struct spi_master *master) +{ + return 0; +} + +static int falcon_sflash_xfer_one(struct spi_master *master, + struct spi_message *m) +{ + struct falcon_sflash *priv = spi_master_get_devdata(master); + struct spi_transfer *t; + unsigned long spi_flags; + unsigned long flags; + int ret = 0; + + priv->sfcmd = 0; + m->actual_length = 0; + + spi_flags = FALCON_SPI_XFER_BEGIN; + list_for_each_entry(t, &m->transfers, transfer_list) { + if (list_is_last(&t->transfer_list, &m->transfers)) + spi_flags |= FALCON_SPI_XFER_END; + + spin_lock_irqsave(&ebu_lock, flags); + ret = falcon_sflash_xfer(m->spi, t, spi_flags); + spin_unlock_irqrestore(&ebu_lock, flags); + + if (ret) + break; + + m->actual_length += t->len; + + WARN_ON(t->delay_usecs || t->cs_change); + spi_flags = 0; + } + + m->status = ret; + m->complete(m->context); + + return 0; +} + +static int __devinit falcon_sflash_probe(struct platform_device *pdev) +{ + struct falcon_sflash *priv; + struct spi_master *master; + int ret; + + if (ltq_boot_select() != BS_SPI) { + dev_err(&pdev->dev, "invalid bootstrap options\n"); + return -ENODEV; + } + + master = spi_alloc_master(&pdev->dev, sizeof(*priv)); + if (!master) + return -ENOMEM; + + priv = spi_master_get_devdata(master); + priv->master = master; + + master->mode_bits = SPI_MODE_3; + master->num_chipselect = 1; + master->bus_num = -1; + master->setup = falcon_sflash_setup; + master->prepare_transfer_hardware = falcon_sflash_prepare_xfer; + master->transfer_one_message = falcon_sflash_xfer_one; + master->unprepare_transfer_hardware = falcon_sflash_unprepare_xfer; + master->dev.of_node = pdev->dev.of_node; + + platform_set_drvdata(pdev, priv); + + ret = spi_register_master(master); + if (ret) + spi_master_put(master); + return ret; +} + +static int __devexit falcon_sflash_remove(struct platform_device *pdev) +{ + struct falcon_sflash *priv = platform_get_drvdata(pdev); + + spi_unregister_master(priv->master); + + return 0; +} + +static const struct of_device_id falcon_sflash_match[] = { + { .compatible = "lantiq,sflash-falcon" }, + {}, +}; +MODULE_DEVICE_TABLE(of, falcon_sflash_match); + +static struct platform_driver falcon_sflash_driver = { + .probe = falcon_sflash_probe, + .remove = __devexit_p(falcon_sflash_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = falcon_sflash_match, + } +}; + +module_platform_driver(falcon_sflash_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Lantiq Falcon SPI/SFLASH controller driver"); diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c index 945d9623550b..4afc3b419738 100644 --- a/drivers/staging/media/lirc/lirc_sir.c +++ b/drivers/staging/media/lirc/lirc_sir.c @@ -52,6 +52,7 @@ #include <linux/io.h> #include <asm/irq.h> #include <linux/fcntl.h> +#include <linux/platform_device.h> #ifdef LIRC_ON_SA1100 #include <asm/hardware.h> #ifdef CONFIG_SA1100_COLLIE @@ -487,9 +488,11 @@ static struct lirc_driver driver = { .owner = THIS_MODULE, }; +static struct platform_device *lirc_sir_dev; static int init_chrdev(void) { + driver.dev = &lirc_sir_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n"); @@ -1215,20 +1218,71 @@ static int init_lirc_sir(void) return 0; } +static int __devinit lirc_sir_probe(struct platform_device *dev) +{ + return 0; +} + +static int __devexit lirc_sir_remove(struct platform_device *dev) +{ + return 0; +} + +static struct platform_driver lirc_sir_driver = { + .probe = lirc_sir_probe, + .remove = __devexit_p(lirc_sir_remove), + .driver = { + .name = "lirc_sir", + .owner = THIS_MODULE, + }, +}; static int __init lirc_sir_init(void) { int retval; + retval = platform_driver_register(&lirc_sir_driver); + if (retval) { + printk(KERN_ERR LIRC_DRIVER_NAME ": Platform driver register " + "failed!\n"); + return -ENODEV; + } + + lirc_sir_dev = platform_device_alloc("lirc_dev", 0); + if (!lirc_sir_dev) { + printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device alloc " + "failed!\n"); + retval = -ENOMEM; + goto pdev_alloc_fail; + } + + retval = platform_device_add(lirc_sir_dev); + if (retval) { + printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device add " + "failed!\n"); + retval = -ENODEV; + goto pdev_add_fail; + } + retval = init_chrdev(); if (retval < 0) - return retval; + goto fail; + retval = init_lirc_sir(); if (retval) { drop_chrdev(); - return retval; + goto fail; } + return 0; + +fail: + platform_device_del(lirc_sir_dev); +pdev_add_fail: + platform_device_put(lirc_sir_dev); +pdev_alloc_fail: + platform_driver_unregister(&lirc_sir_driver); + return retval; } static void __exit lirc_sir_exit(void) @@ -1236,6 +1290,8 @@ static void __exit lirc_sir_exit(void) drop_hardware(); drop_chrdev(); drop_port(); + platform_device_unregister(lirc_sir_dev); + platform_driver_unregister(&lirc_sir_driver); printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); } diff --git a/drivers/staging/media/solo6x10/TODO b/drivers/staging/media/solo6x10/TODO index 7e6c4fa130df..539f739fe9e6 100644 --- a/drivers/staging/media/solo6x10/TODO +++ b/drivers/staging/media/solo6x10/TODO @@ -20,5 +20,5 @@ TODO (general): - implement loopback of external sound jack with incoming audio? - implement pause/resume -Plase send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc Ben Collins +Plase send patches to Mauro Carvalho Chehab <mchehab@redhat.com> and Cc Ben Collins <bcollins@bluecherry.net> diff --git a/drivers/staging/media/solo6x10/i2c.c b/drivers/staging/media/solo6x10/i2c.c index ef95a500b4da..398070a3d293 100644 --- a/drivers/staging/media/solo6x10/i2c.c +++ b/drivers/staging/media/solo6x10/i2c.c @@ -175,7 +175,7 @@ int solo_i2c_isr(struct solo_dev *solo_dev) solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_IIC); - if (status & (SOLO_IIC_STATE_TRNS & SOLO_IIC_STATE_SIG_ERR) || + if (status & (SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR) || solo_dev->i2c_id < 0) { solo_i2c_stop(solo_dev); return -ENXIO; diff --git a/drivers/staging/octeon/ethernet-mdio.c b/drivers/staging/octeon/ethernet-mdio.c index e31949c9c87e..f15b31b37ca5 100644 --- a/drivers/staging/octeon/ethernet-mdio.c +++ b/drivers/staging/octeon/ethernet-mdio.c @@ -28,6 +28,7 @@ #include <linux/ethtool.h> #include <linux/phy.h> #include <linux/ratelimit.h> +#include <linux/of_mdio.h> #include <net/dst.h> @@ -161,22 +162,23 @@ static void cvm_oct_adjust_link(struct net_device *dev) int cvm_oct_phy_setup_device(struct net_device *dev) { struct octeon_ethernet *priv = netdev_priv(dev); + struct device_node *phy_node; - int phy_addr = cvmx_helper_board_get_mii_address(priv->port); - if (phy_addr != -1) { - char phy_id[MII_BUS_ID_SIZE + 3]; + if (!priv->of_node) + return 0; - snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "mdio-octeon-0", phy_addr); + phy_node = of_parse_phandle(priv->of_node, "phy-handle", 0); + if (!phy_node) + return 0; - priv->phydev = phy_connect(dev, phy_id, cvm_oct_adjust_link, 0, - PHY_INTERFACE_MODE_GMII); + priv->phydev = of_phy_connect(dev, phy_node, cvm_oct_adjust_link, 0, + PHY_INTERFACE_MODE_GMII); + + if (priv->phydev == NULL) + return -ENODEV; + + priv->last_link = 0; + phy_start_aneg(priv->phydev); - if (IS_ERR(priv->phydev)) { - priv->phydev = NULL; - return -1; - } - priv->last_link = 0; - phy_start_aneg(priv->phydev); - } return 0; } diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 18f7a790f73d..683bedc74dde 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -24,6 +24,7 @@ * This file may also be available under a different license from Cavium. * Contact Cavium Networks for more information **********************************************************************/ +#include <linux/platform_device.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> @@ -32,6 +33,7 @@ #include <linux/phy.h> #include <linux/slab.h> #include <linux/interrupt.h> +#include <linux/of_net.h> #include <net/dst.h> @@ -113,15 +115,6 @@ int rx_napi_weight = 32; module_param(rx_napi_weight, int, 0444); MODULE_PARM_DESC(rx_napi_weight, "The NAPI WEIGHT parameter."); -/* - * The offset from mac_addr_base that should be used for the next port - * that is configured. By convention, if any mgmt ports exist on the - * chip, they get the first mac addresses, The ports controlled by - * this driver are numbered sequencially following any mgmt addresses - * that may exist. - */ -static unsigned int cvm_oct_mac_addr_offset; - /** * cvm_oct_poll_queue - Workqueue for polling operations. */ @@ -176,7 +169,7 @@ static void cvm_oct_periodic_worker(struct work_struct *work) queue_delayed_work(cvm_oct_poll_queue, &priv->port_periodic_work, HZ); } -static __init void cvm_oct_configure_common_hw(void) +static __devinit void cvm_oct_configure_common_hw(void) { /* Setup the FPA */ cvmx_fpa_enable(); @@ -396,23 +389,21 @@ static void cvm_oct_common_set_multicast_list(struct net_device *dev) * Returns Zero on success */ -static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr) +static int cvm_oct_set_mac_filter(struct net_device *dev) { struct octeon_ethernet *priv = netdev_priv(dev); union cvmx_gmxx_prtx_cfg gmx_cfg; int interface = INTERFACE(priv->port); int index = INDEX(priv->port); - memcpy(dev->dev_addr, addr + 2, 6); - if ((interface < 2) && (cvmx_helper_interface_get_mode(interface) != CVMX_HELPER_INTERFACE_MODE_SPI)) { int i; - uint8_t *ptr = addr; + uint8_t *ptr = dev->dev_addr; uint64_t mac = 0; for (i = 0; i < 6; i++) - mac = (mac << 8) | (uint64_t) (ptr[i + 2]); + mac = (mac << 8) | (uint64_t)ptr[i]; gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); @@ -421,17 +412,17 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr) cvmx_write_csr(CVMX_GMXX_SMACX(index, interface), mac); cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM0(index, interface), - ptr[2]); + ptr[0]); cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM1(index, interface), - ptr[3]); + ptr[1]); cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM2(index, interface), - ptr[4]); + ptr[2]); cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM3(index, interface), - ptr[5]); + ptr[3]); cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM4(index, interface), - ptr[6]); + ptr[4]); cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM5(index, interface), - ptr[7]); + ptr[5]); cvm_oct_common_set_multicast_list(dev); cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); @@ -439,6 +430,15 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr) return 0; } +static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr) +{ + int r = eth_mac_addr(dev, addr); + + if (r) + return r; + return cvm_oct_set_mac_filter(dev); +} + /** * cvm_oct_common_init - per network device initialization * @dev: Device to initialize @@ -448,26 +448,17 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr) int cvm_oct_common_init(struct net_device *dev) { struct octeon_ethernet *priv = netdev_priv(dev); - struct sockaddr sa; - u64 mac = ((u64)(octeon_bootinfo->mac_addr_base[0] & 0xff) << 40) | - ((u64)(octeon_bootinfo->mac_addr_base[1] & 0xff) << 32) | - ((u64)(octeon_bootinfo->mac_addr_base[2] & 0xff) << 24) | - ((u64)(octeon_bootinfo->mac_addr_base[3] & 0xff) << 16) | - ((u64)(octeon_bootinfo->mac_addr_base[4] & 0xff) << 8) | - (u64)(octeon_bootinfo->mac_addr_base[5] & 0xff); - - mac += cvm_oct_mac_addr_offset; - sa.sa_data[0] = (mac >> 40) & 0xff; - sa.sa_data[1] = (mac >> 32) & 0xff; - sa.sa_data[2] = (mac >> 24) & 0xff; - sa.sa_data[3] = (mac >> 16) & 0xff; - sa.sa_data[4] = (mac >> 8) & 0xff; - sa.sa_data[5] = mac & 0xff; - - if (cvm_oct_mac_addr_offset >= octeon_bootinfo->mac_addr_count) - printk(KERN_DEBUG "%s: Using MAC outside of the assigned range:" - " %pM\n", dev->name, sa.sa_data); - cvm_oct_mac_addr_offset++; + const u8 *mac = NULL; + + if (priv->of_node) + mac = of_get_mac_address(priv->of_node); + + if (mac && is_valid_ether_addr(mac)) { + memcpy(dev->dev_addr, mac, ETH_ALEN); + dev->addr_assign_type &= ~NET_ADDR_RANDOM; + } else { + eth_hw_addr_random(dev); + } /* * Force the interface to use the POW send if always_use_pow @@ -488,7 +479,7 @@ int cvm_oct_common_init(struct net_device *dev) SET_ETHTOOL_OPS(dev, &cvm_oct_ethtool_ops); cvm_oct_phy_setup_device(dev); - dev->netdev_ops->ndo_set_mac_address(dev, &sa); + cvm_oct_set_mac_filter(dev); dev->netdev_ops->ndo_change_mtu(dev, dev->mtu); /* @@ -595,22 +586,55 @@ static const struct net_device_ops cvm_oct_pow_netdev_ops = { extern void octeon_mdiobus_force_mod_depencency(void); -static int __init cvm_oct_init_module(void) +static struct device_node * __devinit cvm_oct_of_get_child(const struct device_node *parent, + int reg_val) +{ + struct device_node *node = NULL; + int size; + const __be32 *addr; + + for (;;) { + node = of_get_next_child(parent, node); + if (!node) + break; + addr = of_get_property(node, "reg", &size); + if (addr && (be32_to_cpu(*addr) == reg_val)) + break; + } + return node; +} + +static struct device_node * __devinit cvm_oct_node_for_port(struct device_node *pip, + int interface, int port) +{ + struct device_node *ni, *np; + + ni = cvm_oct_of_get_child(pip, interface); + if (!ni) + return NULL; + + np = cvm_oct_of_get_child(ni, port); + of_node_put(ni); + + return np; +} + +static int __devinit cvm_oct_probe(struct platform_device *pdev) { int num_interfaces; int interface; int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE; int qos; + struct device_node *pip; octeon_mdiobus_force_mod_depencency(); pr_notice("cavium-ethernet %s\n", OCTEON_ETHERNET_VERSION); - if (OCTEON_IS_MODEL(OCTEON_CN52XX)) - cvm_oct_mac_addr_offset = 2; /* First two are the mgmt ports. */ - else if (OCTEON_IS_MODEL(OCTEON_CN56XX)) - cvm_oct_mac_addr_offset = 1; /* First one is the mgmt port. */ - else - cvm_oct_mac_addr_offset = 0; + pip = pdev->dev.of_node; + if (!pip) { + pr_err("Error: No 'pip' in /aliases\n"); + return -EINVAL; + } cvm_oct_poll_queue = create_singlethread_workqueue("octeon-ethernet"); if (cvm_oct_poll_queue == NULL) { @@ -689,10 +713,11 @@ static int __init cvm_oct_init_module(void) cvmx_helper_interface_get_mode(interface); int num_ports = cvmx_helper_ports_on_interface(interface); int port; + int port_index; - for (port = cvmx_helper_get_ipd_port(interface, 0); + for (port_index = 0, port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); - port++) { + port_index++, port++) { struct octeon_ethernet *priv; struct net_device *dev = alloc_etherdev(sizeof(struct octeon_ethernet)); @@ -703,6 +728,7 @@ static int __init cvm_oct_init_module(void) /* Initialize the device private structure. */ priv = netdev_priv(dev); + priv->of_node = cvm_oct_node_for_port(pip, interface, port_index); INIT_DELAYED_WORK(&priv->port_periodic_work, cvm_oct_periodic_worker); @@ -787,7 +813,7 @@ static int __init cvm_oct_init_module(void) return 0; } -static void __exit cvm_oct_cleanup_module(void) +static int __devexit cvm_oct_remove(struct platform_device *pdev) { int port; @@ -835,10 +861,29 @@ static void __exit cvm_oct_cleanup_module(void) if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128); + return 0; } +static struct of_device_id cvm_oct_match[] = { + { + .compatible = "cavium,octeon-3860-pip", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, cvm_oct_match); + +static struct platform_driver cvm_oct_driver = { + .probe = cvm_oct_probe, + .remove = __devexit_p(cvm_oct_remove), + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .of_match_table = cvm_oct_match, + }, +}; + +module_platform_driver(cvm_oct_driver); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Cavium Networks <support@caviumnetworks.com>"); MODULE_DESCRIPTION("Cavium Networks Octeon ethernet driver."); -module_init(cvm_oct_init_module); -module_exit(cvm_oct_cleanup_module); diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h index d58192563552..9360e22e0739 100644 --- a/drivers/staging/octeon/octeon-ethernet.h +++ b/drivers/staging/octeon/octeon-ethernet.h @@ -31,6 +31,8 @@ #ifndef OCTEON_ETHERNET_H #define OCTEON_ETHERNET_H +#include <linux/of.h> + /** * This is the definition of the Ethernet driver's private * driver state stored in netdev_priv(dev). @@ -59,6 +61,7 @@ struct octeon_ethernet { void (*poll) (struct net_device *dev); struct delayed_work port_periodic_work; struct work_struct port_work; /* may be unused. */ + struct device_node *of_node; }; int cvm_oct_free_work(void *work_queue_entry); diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 2d7a9fe8f365..2ab31e4f02cc 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -1251,7 +1251,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) * longer needed. The passive cooling formula uses tc1 and tc2 as described in * section 11.1.5.1 of the ACPI specification 3.0. */ -struct thermal_zone_device *thermal_zone_device_register(char *type, +struct thermal_zone_device *thermal_zone_device_register(const char *type, int trips, int mask, void *devdata, const struct thermal_zone_device_ops *ops, int tc1, int tc2, int passive_delay, int polling_delay) diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 8981fbb5748c..cf6bd626f3fe 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1583,12 +1583,10 @@ static int __exit m66592_remove(struct platform_device *pdev) iounmap(m66592->reg); free_irq(platform_get_irq(pdev, 0), m66592); m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); -#ifdef CONFIG_HAVE_CLK if (m66592->pdata->on_chip) { clk_disable(m66592->clk); clk_put(m66592->clk); } -#endif kfree(m66592); return 0; } @@ -1602,9 +1600,7 @@ static int __init m66592_probe(struct platform_device *pdev) struct resource *res, *ires; void __iomem *reg = NULL; struct m66592 *m66592 = NULL; -#ifdef CONFIG_HAVE_CLK char clk_name[8]; -#endif int ret = 0; int i; @@ -1671,7 +1667,6 @@ static int __init m66592_probe(struct platform_device *pdev) goto clean_up; } -#ifdef CONFIG_HAVE_CLK if (m66592->pdata->on_chip) { snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id); m66592->clk = clk_get(&pdev->dev, clk_name); @@ -1683,7 +1678,7 @@ static int __init m66592_probe(struct platform_device *pdev) } clk_enable(m66592->clk); } -#endif + INIT_LIST_HEAD(&m66592->gadget.ep_list); m66592->gadget.ep0 = &m66592->ep[0].ep; INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list); @@ -1731,13 +1726,11 @@ err_add_udc: m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); clean_up3: -#ifdef CONFIG_HAVE_CLK if (m66592->pdata->on_chip) { clk_disable(m66592->clk); clk_put(m66592->clk); } clean_up2: -#endif free_irq(ires->start, m66592); clean_up: if (m66592) { diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h index 88c85b4116a2..16c7e14678b8 100644 --- a/drivers/usb/gadget/m66592-udc.h +++ b/drivers/usb/gadget/m66592-udc.h @@ -13,10 +13,7 @@ #ifndef __M66592_UDC_H__ #define __M66592_UDC_H__ -#ifdef CONFIG_HAVE_CLK #include <linux/clk.h> -#endif - #include <linux/usb/m66592.h> #define M66592_SYSCFG 0x00 @@ -468,9 +465,7 @@ struct m66592_ep { struct m66592 { spinlock_t lock; void __iomem *reg; -#ifdef CONFIG_HAVE_CLK struct clk *clk; -#endif struct m66592_platdata *pdata; unsigned long irq_trigger; diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index f3ac2a20c27c..5a80751accb7 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -1831,12 +1831,12 @@ static int __exit r8a66597_remove(struct platform_device *pdev) iounmap(r8a66597->sudmac_reg); free_irq(platform_get_irq(pdev, 0), r8a66597); r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); -#ifdef CONFIG_HAVE_CLK + if (r8a66597->pdata->on_chip) { clk_disable(r8a66597->clk); clk_put(r8a66597->clk); } -#endif + device_unregister(&r8a66597->gadget.dev); kfree(r8a66597); return 0; @@ -1868,9 +1868,7 @@ static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597, static int __init r8a66597_probe(struct platform_device *pdev) { -#ifdef CONFIG_HAVE_CLK char clk_name[8]; -#endif struct resource *res, *ires; int irq; void __iomem *reg = NULL; @@ -1934,7 +1932,6 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597->timer.data = (unsigned long)r8a66597; r8a66597->reg = reg; -#ifdef CONFIG_HAVE_CLK if (r8a66597->pdata->on_chip) { snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id); r8a66597->clk = clk_get(&pdev->dev, clk_name); @@ -1946,7 +1943,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) } clk_enable(r8a66597->clk); } -#endif + if (r8a66597->pdata->sudmac) { ret = r8a66597_sudmac_ioremap(r8a66597, pdev); if (ret < 0) @@ -2006,13 +2003,11 @@ err_add_udc: clean_up3: free_irq(irq, r8a66597); clean_up2: -#ifdef CONFIG_HAVE_CLK if (r8a66597->pdata->on_chip) { clk_disable(r8a66597->clk); clk_put(r8a66597->clk); } clean_up_dev: -#endif device_unregister(&r8a66597->gadget.dev); clean_up: if (r8a66597) { diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h index 99908c76ccd1..45c4b2df1785 100644 --- a/drivers/usb/gadget/r8a66597-udc.h +++ b/drivers/usb/gadget/r8a66597-udc.h @@ -13,10 +13,7 @@ #ifndef __R8A66597_H__ #define __R8A66597_H__ -#ifdef CONFIG_HAVE_CLK #include <linux/clk.h> -#endif - #include <linux/usb/r8a66597.h> #define R8A66597_MAX_SAMPLING 10 @@ -92,9 +89,7 @@ struct r8a66597 { void __iomem *reg; void __iomem *sudmac_reg; -#ifdef CONFIG_HAVE_CLK struct clk *clk; -#endif struct r8a66597_platdata *pdata; struct usb_gadget gadget; diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index ec21f4a4a056..bb55eb4a7d48 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -152,14 +152,14 @@ static int omap_ehci_init(struct usb_hcd *hcd) struct ehci_hcd_omap_platform_data *pdata; pdata = hcd->self.controller->platform_data; + + /* Hold PHYs in reset while initializing EHCI controller */ if (pdata->phy_reset) { if (gpio_is_valid(pdata->reset_gpio_port[0])) - gpio_request_one(pdata->reset_gpio_port[0], - GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); + gpio_set_value_cansleep(pdata->reset_gpio_port[0], 0); if (gpio_is_valid(pdata->reset_gpio_port[1])) - gpio_request_one(pdata->reset_gpio_port[1], - GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); + gpio_set_value_cansleep(pdata->reset_gpio_port[1], 0); /* Hold the PHY in RESET for enough time till DIR is high */ udelay(10); diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index c868be65e763..4c634eb56358 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -95,9 +95,7 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597) int i = 0; if (r8a66597->pdata->on_chip) { -#ifdef CONFIG_HAVE_CLK clk_enable(r8a66597->clk); -#endif do { r8a66597_write(r8a66597, SCKE, SYSCFG0); tmp = r8a66597_read(r8a66597, SYSCFG0); @@ -141,9 +139,7 @@ static void r8a66597_clock_disable(struct r8a66597 *r8a66597) udelay(1); if (r8a66597->pdata->on_chip) { -#ifdef CONFIG_HAVE_CLK clk_disable(r8a66597->clk); -#endif } else { r8a66597_bclr(r8a66597, PLLC, SYSCFG0); r8a66597_bclr(r8a66597, XCKE, SYSCFG0); @@ -2406,19 +2402,15 @@ static int __devexit r8a66597_remove(struct platform_device *pdev) del_timer_sync(&r8a66597->rh_timer); usb_remove_hcd(hcd); iounmap(r8a66597->reg); -#ifdef CONFIG_HAVE_CLK if (r8a66597->pdata->on_chip) clk_put(r8a66597->clk); -#endif usb_put_hcd(hcd); return 0; } static int __devinit r8a66597_probe(struct platform_device *pdev) { -#ifdef CONFIG_HAVE_CLK char clk_name[8]; -#endif struct resource *res = NULL, *ires; int irq = -1; void __iomem *reg = NULL; @@ -2482,7 +2474,6 @@ static int __devinit r8a66597_probe(struct platform_device *pdev) r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW; if (r8a66597->pdata->on_chip) { -#ifdef CONFIG_HAVE_CLK snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id); r8a66597->clk = clk_get(&pdev->dev, clk_name); if (IS_ERR(r8a66597->clk)) { @@ -2491,7 +2482,6 @@ static int __devinit r8a66597_probe(struct platform_device *pdev) ret = PTR_ERR(r8a66597->clk); goto clean_up2; } -#endif r8a66597->max_root_hub = 1; } else r8a66597->max_root_hub = 2; @@ -2531,11 +2521,9 @@ static int __devinit r8a66597_probe(struct platform_device *pdev) return 0; clean_up3: -#ifdef CONFIG_HAVE_CLK if (r8a66597->pdata->on_chip) clk_put(r8a66597->clk); clean_up2: -#endif usb_put_hcd(hcd); clean_up: diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h index f28782d20eef..672cea307abb 100644 --- a/drivers/usb/host/r8a66597.h +++ b/drivers/usb/host/r8a66597.h @@ -26,10 +26,7 @@ #ifndef __R8A66597_H__ #define __R8A66597_H__ -#ifdef CONFIG_HAVE_CLK #include <linux/clk.h> -#endif - #include <linux/usb/r8a66597.h> #define R8A66597_MAX_NUM_PIPE 10 @@ -113,9 +110,7 @@ struct r8a66597_root_hub { struct r8a66597 { spinlock_t lock; void __iomem *reg; -#ifdef CONFIG_HAVE_CLK struct clk *clk; -#endif struct r8a66597_platdata *pdata; struct r8a66597_device device0; struct r8a66597_root_hub root_hub[R8A66597_MAX_ROOT_HUB]; diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index dbcdeea30f09..586105b55a7c 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -81,14 +81,6 @@ struct musb_ep; #define is_peripheral_active(m) (!(m)->is_host) #define is_host_active(m) ((m)->is_host) -#ifndef CONFIG_HAVE_CLK -/* Dummy stub for clk framework */ -#define clk_get(dev, id) NULL -#define clk_put(clock) do {} while (0) -#define clk_enable(clock) do {} while (0) -#define clk_disable(clock) do {} while (0) -#endif - #ifdef CONFIG_PROC_FS #include <linux/fs.h> #define MUSB_CONFIG_PROC_FS diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 2979292650d6..cf282763a8dc 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -245,7 +245,7 @@ config BACKLIGHT_CARILLO_RANCH config BACKLIGHT_PWM tristate "Generic PWM based Backlight Driver" - depends on HAVE_PWM + depends on PWM help If you have a LCD backlight adjustable by PWM, say Y to enable this driver. diff --git a/drivers/video/backlight/atmel-pwm-bl.c b/drivers/video/backlight/atmel-pwm-bl.c index 0443a4f71858..df1cbb7ef6ca 100644 --- a/drivers/video/backlight/atmel-pwm-bl.c +++ b/drivers/video/backlight/atmel-pwm-bl.c @@ -127,7 +127,8 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev) struct atmel_pwm_bl *pwmbl; int retval; - pwmbl = kzalloc(sizeof(struct atmel_pwm_bl), GFP_KERNEL); + pwmbl = devm_kzalloc(&pdev->dev, sizeof(struct atmel_pwm_bl), + GFP_KERNEL); if (!pwmbl) return -ENOMEM; @@ -154,7 +155,8 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev) goto err_free_mem; if (pwmbl->gpio_on != -1) { - retval = gpio_request(pwmbl->gpio_on, "gpio_atmel_pwm_bl"); + retval = devm_gpio_request(&pdev->dev, pwmbl->gpio_on, + "gpio_atmel_pwm_bl"); if (retval) { pwmbl->gpio_on = -1; goto err_free_pwm; @@ -164,7 +166,7 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev) retval = gpio_direction_output(pwmbl->gpio_on, 0 ^ pdata->on_active_low); if (retval) - goto err_free_gpio; + goto err_free_pwm; } memset(&props, 0, sizeof(struct backlight_properties)); @@ -174,7 +176,7 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev) &atmel_pwm_bl_ops, &props); if (IS_ERR(bldev)) { retval = PTR_ERR(bldev); - goto err_free_gpio; + goto err_free_pwm; } pwmbl->bldev = bldev; @@ -196,13 +198,9 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev) err_free_bl_dev: platform_set_drvdata(pdev, NULL); backlight_device_unregister(bldev); -err_free_gpio: - if (pwmbl->gpio_on != -1) - gpio_free(pwmbl->gpio_on); err_free_pwm: pwm_channel_free(&pwmbl->pwmc); err_free_mem: - kfree(pwmbl); return retval; } @@ -210,15 +208,12 @@ static int __exit atmel_pwm_bl_remove(struct platform_device *pdev) { struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev); - if (pwmbl->gpio_on != -1) { + if (pwmbl->gpio_on != -1) gpio_set_value(pwmbl->gpio_on, 0); - gpio_free(pwmbl->gpio_on); - } pwm_channel_disable(&pwmbl->pwmc); pwm_channel_free(&pwmbl->pwmc); backlight_device_unregister(pwmbl->bldev); platform_set_drvdata(pdev, NULL); - kfree(pwmbl); return 0; } diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c index 23d732677ba1..c781768ba892 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -492,7 +492,8 @@ static int setup_gpio_backlight(struct corgi_lcd *lcd, lcd->gpio_backlight_cont = -1; if (gpio_is_valid(pdata->gpio_backlight_on)) { - err = gpio_request(pdata->gpio_backlight_on, "BL_ON"); + err = devm_gpio_request(&spi->dev, pdata->gpio_backlight_on, + "BL_ON"); if (err) { dev_err(&spi->dev, "failed to request GPIO%d for " "backlight_on\n", pdata->gpio_backlight_on); @@ -504,11 +505,12 @@ static int setup_gpio_backlight(struct corgi_lcd *lcd, } if (gpio_is_valid(pdata->gpio_backlight_cont)) { - err = gpio_request(pdata->gpio_backlight_cont, "BL_CONT"); + err = devm_gpio_request(&spi->dev, pdata->gpio_backlight_cont, + "BL_CONT"); if (err) { dev_err(&spi->dev, "failed to request GPIO%d for " "backlight_cont\n", pdata->gpio_backlight_cont); - goto err_free_backlight_on; + return err; } lcd->gpio_backlight_cont = pdata->gpio_backlight_cont; @@ -525,11 +527,6 @@ static int setup_gpio_backlight(struct corgi_lcd *lcd, } } return 0; - -err_free_backlight_on: - if (gpio_is_valid(lcd->gpio_backlight_on)) - gpio_free(lcd->gpio_backlight_on); - return err; } static int __devinit corgi_lcd_probe(struct spi_device *spi) @@ -602,12 +599,6 @@ static int __devexit corgi_lcd_remove(struct spi_device *spi) backlight_update_status(lcd->bl_dev); backlight_device_unregister(lcd->bl_dev); - if (gpio_is_valid(lcd->gpio_backlight_on)) - gpio_free(lcd->gpio_backlight_on); - - if (gpio_is_valid(lcd->gpio_backlight_cont)) - gpio_free(lcd->gpio_backlight_cont); - corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN); lcd_device_unregister(lcd->lcd_dev); diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c index 40f606a86093..2d90c0648aa0 100644 --- a/drivers/video/backlight/l4f00242t03.c +++ b/drivers/video/backlight/l4f00242t03.c @@ -175,28 +175,27 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi) priv->spi = spi; - ret = gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH, - "lcd l4f00242t03 reset"); + ret = devm_gpio_request_one(&spi->dev, pdata->reset_gpio, + GPIOF_OUT_INIT_HIGH, "lcd l4f00242t03 reset"); if (ret) { dev_err(&spi->dev, "Unable to get the lcd l4f00242t03 reset gpio.\n"); return ret; } - ret = gpio_request_one(pdata->data_enable_gpio, GPIOF_OUT_INIT_LOW, - "lcd l4f00242t03 data enable"); + ret = devm_gpio_request_one(&spi->dev, pdata->data_enable_gpio, + GPIOF_OUT_INIT_LOW, "lcd l4f00242t03 data enable"); if (ret) { dev_err(&spi->dev, "Unable to get the lcd l4f00242t03 data en gpio.\n"); - goto err; + return ret; } priv->io_reg = regulator_get(&spi->dev, "vdd"); if (IS_ERR(priv->io_reg)) { - ret = PTR_ERR(priv->io_reg); dev_err(&spi->dev, "%s: Unable to get the IO regulator\n", __func__); - goto err2; + return PTR_ERR(priv->io_reg); } priv->core_reg = regulator_get(&spi->dev, "vcore"); @@ -204,14 +203,14 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi) ret = PTR_ERR(priv->core_reg); dev_err(&spi->dev, "%s: Unable to get the core regulator\n", __func__); - goto err3; + goto err1; } priv->ld = lcd_device_register("l4f00242t03", &spi->dev, priv, &l4f_ops); if (IS_ERR(priv->ld)) { ret = PTR_ERR(priv->ld); - goto err4; + goto err2; } /* Init the LCD */ @@ -223,14 +222,10 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi) return 0; -err4: +err2: regulator_put(priv->core_reg); -err3: +err1: regulator_put(priv->io_reg); -err2: - gpio_free(pdata->data_enable_gpio); -err: - gpio_free(pdata->reset_gpio); return ret; } @@ -238,16 +233,12 @@ err: static int __devexit l4f00242t03_remove(struct spi_device *spi) { struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); - struct l4f00242t03_pdata *pdata = priv->spi->dev.platform_data; l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN); lcd_device_unregister(priv->ld); dev_set_drvdata(&spi->dev, NULL); - gpio_free(pdata->data_enable_gpio); - gpio_free(pdata->reset_gpio); - regulator_put(priv->io_reg); regulator_put(priv->core_reg); diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c index bebeb63607db..18dca0c29c68 100644 --- a/drivers/video/backlight/lm3533_bl.c +++ b/drivers/video/backlight/lm3533_bl.c @@ -295,7 +295,7 @@ static int __devinit lm3533_bl_probe(struct platform_device *pdev) return -EINVAL; } - bl = kzalloc(sizeof(*bl), GFP_KERNEL); + bl = devm_kzalloc(&pdev->dev, sizeof(*bl), GFP_KERNEL); if (!bl) { dev_err(&pdev->dev, "failed to allocate memory for backlight\n"); @@ -317,8 +317,7 @@ static int __devinit lm3533_bl_probe(struct platform_device *pdev) &lm3533_bl_ops, &props); if (IS_ERR(bd)) { dev_err(&pdev->dev, "failed to register backlight device\n"); - ret = PTR_ERR(bd); - goto err_free; + return PTR_ERR(bd); } bl->bd = bd; @@ -348,8 +347,6 @@ err_sysfs_remove: sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group); err_unregister: backlight_device_unregister(bd); -err_free: - kfree(bl); return ret; } @@ -367,7 +364,6 @@ static int __devexit lm3533_bl_remove(struct platform_device *pdev) lm3533_ctrlbank_disable(&bl->cb); sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group); backlight_device_unregister(bd); - kfree(bl); return 0; } diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c index a9f2c36966f1..ea43f2254196 100644 --- a/drivers/video/backlight/lms283gf05.c +++ b/drivers/video/backlight/lms283gf05.c @@ -158,29 +158,27 @@ static int __devinit lms283gf05_probe(struct spi_device *spi) int ret = 0; if (pdata != NULL) { - ret = gpio_request(pdata->reset_gpio, "LMS285GF05 RESET"); + ret = devm_gpio_request(&spi->dev, pdata->reset_gpio, + "LMS285GF05 RESET"); if (ret) return ret; ret = gpio_direction_output(pdata->reset_gpio, !pdata->reset_inverted); if (ret) - goto err; + return ret; } st = devm_kzalloc(&spi->dev, sizeof(struct lms283gf05_state), GFP_KERNEL); if (st == NULL) { dev_err(&spi->dev, "No memory for device state\n"); - ret = -ENOMEM; - goto err; + return -ENOMEM; } ld = lcd_device_register("lms283gf05", &spi->dev, st, &lms_ops); - if (IS_ERR(ld)) { - ret = PTR_ERR(ld); - goto err; - } + if (IS_ERR(ld)) + return PTR_ERR(ld); st->spi = spi; st->ld = ld; @@ -193,24 +191,14 @@ static int __devinit lms283gf05_probe(struct spi_device *spi) lms283gf05_toggle(spi, disp_initseq, ARRAY_SIZE(disp_initseq)); return 0; - -err: - if (pdata != NULL) - gpio_free(pdata->reset_gpio); - - return ret; } static int __devexit lms283gf05_remove(struct spi_device *spi) { struct lms283gf05_state *st = dev_get_drvdata(&spi->dev); - struct lms283gf05_pdata *pdata = st->spi->dev.platform_data; lcd_device_unregister(st->ld); - if (pdata != NULL) - gpio_free(pdata->reset_gpio); - return 0; } diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index 72a0e0c917cf..aa6d4f71131f 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -14,11 +14,15 @@ #include <linux/i2c.h> #include <linux/backlight.h> #include <linux/err.h> -#include <linux/lp855x.h> +#include <linux/platform_data/lp855x.h> /* Registers */ -#define BRIGHTNESS_CTRL (0x00) -#define DEVICE_CTRL (0x01) +#define BRIGHTNESS_CTRL 0x00 +#define DEVICE_CTRL 0x01 +#define EEPROM_START 0xA0 +#define EEPROM_END 0xA7 +#define EPROM_START 0xA0 +#define EPROM_END 0xAF #define BUF_SIZE 20 #define DEFAULT_BL_NAME "lcd-backlight" diff --git a/drivers/video/backlight/ot200_bl.c b/drivers/video/backlight/ot200_bl.c index f519d55a294c..469cf0f109d2 100644 --- a/drivers/video/backlight/ot200_bl.c +++ b/drivers/video/backlight/ot200_bl.c @@ -84,7 +84,8 @@ static int ot200_backlight_probe(struct platform_device *pdev) int retval = 0; /* request gpio */ - if (gpio_request(GPIO_DIMM, "ot200 backlight dimmer") < 0) { + if (devm_gpio_request(&pdev->dev, GPIO_DIMM, + "ot200 backlight dimmer") < 0) { dev_err(&pdev->dev, "failed to request GPIO %d\n", GPIO_DIMM); return -ENODEV; } @@ -93,14 +94,13 @@ static int ot200_backlight_probe(struct platform_device *pdev) pwm_timer = cs5535_mfgpt_alloc_timer(7, MFGPT_DOMAIN_ANY); if (!pwm_timer) { dev_err(&pdev->dev, "MFGPT 7 not available\n"); - retval = -ENODEV; - goto error_mfgpt_alloc; + return -ENODEV; } - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) { retval = -ENOMEM; - goto error_kzalloc; + goto error_devm_kzalloc; } /* setup gpio */ @@ -122,26 +122,21 @@ static int ot200_backlight_probe(struct platform_device *pdev) if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); retval = PTR_ERR(bl); - goto error_backlight_device_register; + goto error_devm_kzalloc; } platform_set_drvdata(pdev, bl); return 0; -error_backlight_device_register: - kfree(data); -error_kzalloc: +error_devm_kzalloc: cs5535_mfgpt_free_timer(pwm_timer); -error_mfgpt_alloc: - gpio_free(GPIO_DIMM); return retval; } static int ot200_backlight_remove(struct platform_device *pdev) { struct backlight_device *bl = platform_get_drvdata(pdev); - struct ot200_backlight_data *data = bl_get_data(bl); backlight_device_unregister(bl); @@ -152,9 +147,7 @@ static int ot200_backlight_remove(struct platform_device *pdev) MAX_COMP2 - dim_table[100]); cs5535_mfgpt_free_timer(pwm_timer); - gpio_free(GPIO_DIMM); - kfree(data); return 0; } diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 342b7d7cbb63..995f0164c9b0 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -26,11 +26,13 @@ struct pwm_bl_data { struct device *dev; unsigned int period; unsigned int lth_brightness; + unsigned int *levels; int (*notify)(struct device *, int brightness); void (*notify_after)(struct device *, int brightness); int (*check_fb)(struct device *, struct fb_info *); + void (*exit)(struct device *); }; static int pwm_backlight_update_status(struct backlight_device *bl) @@ -52,9 +54,18 @@ static int pwm_backlight_update_status(struct backlight_device *bl) pwm_config(pb->pwm, 0, pb->period); pwm_disable(pb->pwm); } else { - brightness = pb->lth_brightness + - (brightness * (pb->period - pb->lth_brightness) / max); - pwm_config(pb->pwm, brightness, pb->period); + int duty_cycle; + + if (pb->levels) { + duty_cycle = pb->levels[brightness]; + max = pb->levels[max]; + } else { + duty_cycle = brightness; + } + + duty_cycle = pb->lth_brightness + + (duty_cycle * (pb->period - pb->lth_brightness) / max); + pwm_config(pb->pwm, duty_cycle, pb->period); pwm_enable(pb->pwm); } @@ -83,17 +94,98 @@ static const struct backlight_ops pwm_backlight_ops = { .check_fb = pwm_backlight_check_fb, }; +#ifdef CONFIG_OF +static int pwm_backlight_parse_dt(struct device *dev, + struct platform_pwm_backlight_data *data) +{ + struct device_node *node = dev->of_node; + struct property *prop; + int length; + u32 value; + int ret; + + if (!node) + return -ENODEV; + + memset(data, 0, sizeof(*data)); + + /* determine the number of brightness levels */ + prop = of_find_property(node, "brightness-levels", &length); + if (!prop) + return -EINVAL; + + data->max_brightness = length / sizeof(u32); + + /* read brightness levels from DT property */ + if (data->max_brightness > 0) { + size_t size = sizeof(*data->levels) * data->max_brightness; + + data->levels = devm_kzalloc(dev, size, GFP_KERNEL); + if (!data->levels) + return -ENOMEM; + + ret = of_property_read_u32_array(node, "brightness-levels", + data->levels, + data->max_brightness); + if (ret < 0) + return ret; + + ret = of_property_read_u32(node, "default-brightness-level", + &value); + if (ret < 0) + return ret; + + if (value >= data->max_brightness) { + dev_warn(dev, "invalid default brightness level: %u, using %u\n", + value, data->max_brightness - 1); + value = data->max_brightness - 1; + } + + data->dft_brightness = value; + data->max_brightness--; + } + + /* + * TODO: Most users of this driver use a number of GPIOs to control + * backlight power. Support for specifying these needs to be + * added. + */ + + return 0; +} + +static struct of_device_id pwm_backlight_of_match[] = { + { .compatible = "pwm-backlight" }, + { } +}; + +MODULE_DEVICE_TABLE(of, pwm_backlight_of_match); +#else +static int pwm_backlight_parse_dt(struct device *dev, + struct platform_pwm_backlight_data *data) +{ + return -ENODEV; +} +#endif + static int pwm_backlight_probe(struct platform_device *pdev) { - struct backlight_properties props; struct platform_pwm_backlight_data *data = pdev->dev.platform_data; + struct platform_pwm_backlight_data defdata; + struct backlight_properties props; struct backlight_device *bl; struct pwm_bl_data *pb; + unsigned int max; int ret; if (!data) { - dev_err(&pdev->dev, "failed to find platform data\n"); - return -EINVAL; + ret = pwm_backlight_parse_dt(&pdev->dev, &defdata); + if (ret < 0) { + dev_err(&pdev->dev, "failed to find platform data\n"); + return ret; + } + + data = &defdata; } if (data->init) { @@ -109,21 +201,42 @@ static int pwm_backlight_probe(struct platform_device *pdev) goto err_alloc; } - pb->period = data->pwm_period_ns; + if (data->levels) { + max = data->levels[data->max_brightness]; + pb->levels = data->levels; + } else + max = data->max_brightness; + pb->notify = data->notify; pb->notify_after = data->notify_after; pb->check_fb = data->check_fb; - pb->lth_brightness = data->lth_brightness * - (data->pwm_period_ns / data->max_brightness); + pb->exit = data->exit; pb->dev = &pdev->dev; - pb->pwm = pwm_request(data->pwm_id, "backlight"); + pb->pwm = pwm_get(&pdev->dev, NULL); if (IS_ERR(pb->pwm)) { - dev_err(&pdev->dev, "unable to request PWM for backlight\n"); - ret = PTR_ERR(pb->pwm); - goto err_alloc; - } else - dev_dbg(&pdev->dev, "got pwm for backlight\n"); + dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n"); + + pb->pwm = pwm_request(data->pwm_id, "pwm-backlight"); + if (IS_ERR(pb->pwm)) { + dev_err(&pdev->dev, "unable to request legacy PWM\n"); + ret = PTR_ERR(pb->pwm); + goto err_alloc; + } + } + + dev_dbg(&pdev->dev, "got pwm for backlight\n"); + + /* + * The DT case will set the pwm_period_ns field to 0 and store the + * period, parsed from the DT, in the PWM device. For the non-DT case, + * set the period from platform data. + */ + if (data->pwm_period_ns > 0) + pwm_set_period(pb->pwm, data->pwm_period_ns); + + pb->period = pwm_get_period(pb->pwm); + pb->lth_brightness = data->lth_brightness * (pb->period / max); memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; @@ -143,7 +256,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) return 0; err_bl: - pwm_free(pb->pwm); + pwm_put(pb->pwm); err_alloc: if (data->exit) data->exit(&pdev->dev); @@ -152,16 +265,15 @@ err_alloc: static int pwm_backlight_remove(struct platform_device *pdev) { - struct platform_pwm_backlight_data *data = pdev->dev.platform_data; struct backlight_device *bl = platform_get_drvdata(pdev); struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); backlight_device_unregister(bl); pwm_config(pb->pwm, 0, pb->period); pwm_disable(pb->pwm); - pwm_free(pb->pwm); - if (data->exit) - data->exit(&pdev->dev); + pwm_put(pb->pwm); + if (pb->exit) + pb->exit(&pdev->dev); return 0; } @@ -195,11 +307,12 @@ static SIMPLE_DEV_PM_OPS(pwm_backlight_pm_ops, pwm_backlight_suspend, static struct platform_driver pwm_backlight_driver = { .driver = { - .name = "pwm-backlight", - .owner = THIS_MODULE, + .name = "pwm-backlight", + .owner = THIS_MODULE, #ifdef CONFIG_PM - .pm = &pwm_backlight_pm_ops, + .pm = &pwm_backlight_pm_ops, #endif + .of_match_table = of_match_ptr(pwm_backlight_of_match), }, .probe = pwm_backlight_probe, .remove = pwm_backlight_remove, diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c index 0d54e607e82d..49342e1d20be 100644 --- a/drivers/video/backlight/tosa_bl.c +++ b/drivers/video/backlight/tosa_bl.c @@ -92,14 +92,14 @@ static int __devinit tosa_bl_probe(struct i2c_client *client, data->comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj; - ret = gpio_request(TOSA_GPIO_BL_C20MA, "backlight"); + ret = devm_gpio_request(&client->dev, TOSA_GPIO_BL_C20MA, "backlight"); if (ret) { dev_dbg(&data->bl->dev, "Unable to request gpio!\n"); return ret; } ret = gpio_direction_output(TOSA_GPIO_BL_C20MA, 0); if (ret) - goto err_gpio_dir; + return ret; i2c_set_clientdata(client, data); data->i2c = client; @@ -123,8 +123,6 @@ static int __devinit tosa_bl_probe(struct i2c_client *client, err_reg: data->bl = NULL; -err_gpio_dir: - gpio_free(TOSA_GPIO_BL_C20MA); return ret; } @@ -135,8 +133,6 @@ static int __devexit tosa_bl_remove(struct i2c_client *client) backlight_device_unregister(data->bl); data->bl = NULL; - gpio_free(TOSA_GPIO_BL_C20MA); - return 0; } diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c index 47823b8efff0..33047a66cc24 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -193,7 +193,7 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi) data->spi = spi; dev_set_drvdata(&spi->dev, data); - ret = gpio_request(TOSA_GPIO_TG_ON, "tg #pwr"); + ret = devm_gpio_request(&spi->dev, TOSA_GPIO_TG_ON, "tg #pwr"); if (ret < 0) goto err_gpio_tg; @@ -201,7 +201,7 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi) ret = gpio_direction_output(TOSA_GPIO_TG_ON, 0); if (ret < 0) - goto err_gpio_dir; + goto err_gpio_tg; mdelay(60); tosa_lcd_tg_init(data); @@ -221,8 +221,6 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi) err_register: tosa_lcd_tg_off(data); -err_gpio_dir: - gpio_free(TOSA_GPIO_TG_ON); err_gpio_tg: dev_set_drvdata(&spi->dev, NULL); return ret; @@ -239,7 +237,6 @@ static int __devexit tosa_lcd_remove(struct spi_device *spi) tosa_lcd_tg_off(data); - gpio_free(TOSA_GPIO_TG_ON); dev_set_drvdata(&spi->dev, NULL); return 0; |